mirror of
https://github.com/jomjol/AI-on-the-edge-device.git
synced 2025-12-14 07:26:53 +03:00
Compare commits
76 Commits
v15.3.0
...
low-power-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80a6fc1dc3 | ||
|
|
d2c47fcde2 | ||
|
|
7b3a493587 | ||
|
|
217f543578 | ||
|
|
797fc5e764 | ||
|
|
7a4e82a44e | ||
|
|
019069cd16 | ||
|
|
4de1152cf3 | ||
|
|
0e0fb459dc | ||
|
|
8410df6144 | ||
|
|
4990858101 | ||
|
|
fc1f8ee242 | ||
|
|
252c399a76 | ||
|
|
eb7f2b3705 | ||
|
|
2ed6fb0f0d | ||
|
|
b5213b01af | ||
|
|
473e458b85 | ||
|
|
4f3f3d9af2 | ||
|
|
b5a4cfed96 | ||
|
|
6fca4d8d95 | ||
|
|
a11e19fb0c | ||
|
|
09fa94c95f | ||
|
|
f01b4dbd13 | ||
|
|
dbf1770016 | ||
|
|
006f3aa063 | ||
|
|
24c46c38b4 | ||
|
|
9ced147d9c | ||
|
|
0808895bd6 | ||
|
|
d2dec9fa59 | ||
|
|
7e7bc3dd68 | ||
|
|
5b09cd0d59 | ||
|
|
74d4f20858 | ||
|
|
e790a14caa | ||
|
|
f023a6b739 | ||
|
|
1e463188ea | ||
|
|
9991196961 | ||
|
|
ecece0f7fc | ||
|
|
1b3b7595c1 | ||
|
|
6d10f712d1 | ||
|
|
b84a2db050 | ||
|
|
374462a7d8 | ||
|
|
4facd7be05 | ||
|
|
3baf5865ad | ||
|
|
02138c44ac | ||
|
|
1094c8a0a8 | ||
|
|
75b15b8e9d | ||
|
|
36c12b400b | ||
|
|
35d90cd0ee | ||
|
|
999a8d9374 | ||
|
|
8a4269c6a0 | ||
|
|
8f5579cca5 | ||
|
|
e58b3a2cf8 | ||
|
|
222ee0921c | ||
|
|
e2bfcd26c9 | ||
|
|
18917b2d82 | ||
|
|
f0dea3abcb | ||
|
|
bd2d8b4a15 | ||
|
|
80e3f50a5b | ||
|
|
f6ca32d69f | ||
|
|
49e919c481 | ||
|
|
de768c4f44 | ||
|
|
002fc033aa | ||
|
|
f4af8de699 | ||
|
|
e4d6707a0b | ||
|
|
9f03e68690 | ||
|
|
b1df7df580 | ||
|
|
ebc9be4a28 | ||
|
|
5d0fc73c13 | ||
|
|
40a1aa0430 | ||
|
|
d7a733512f | ||
|
|
dc9f1aad27 | ||
|
|
cd3e641bcc | ||
|
|
ad72ffa37c | ||
|
|
2d45a0ed26 | ||
|
|
e8065ef414 | ||
|
|
33893eb566 |
25
.github/label-commenter-config.yml
vendored
25
.github/label-commenter-config.yml
vendored
@@ -4,6 +4,31 @@
|
|||||||
# Due to the way it works, you have to add each response twice, once for the issue, once for the discussions!
|
# Due to the way it works, you have to add each response twice, once for the issue, once for the discussions!
|
||||||
|
|
||||||
labels:
|
labels:
|
||||||
|
#######################################################################
|
||||||
|
# Bot Response: Documentation
|
||||||
|
#######################################################################
|
||||||
|
- name: bot-reply Documentation
|
||||||
|
labeled:
|
||||||
|
issue:
|
||||||
|
body: |
|
||||||
|
Please have a look on our documentation: https://jomjol.github.io/AI-on-the-edge-device-docs
|
||||||
|
discussion:
|
||||||
|
body: |
|
||||||
|
Please have a look on our documentation: https://jomjol.github.io/AI-on-the-edge-device-docs
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
# Bot Response: ROI setup
|
||||||
|
#######################################################################
|
||||||
|
- name: bot-reply ROI Setup
|
||||||
|
labeled:
|
||||||
|
issue:
|
||||||
|
body: |
|
||||||
|
Make sure to setup your ROIs properly. Have a look on our documentation: https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/#how-to-setup-the-digit-rois-perfectly
|
||||||
|
discussion:
|
||||||
|
body: |
|
||||||
|
Make sure to setup your ROIs properly. Have a look on our documentation: https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/#how-to-setup-the-digit-rois-perfectly
|
||||||
|
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
# Bot Response: Logfile
|
# Bot Response: Logfile
|
||||||
#######################################################################
|
#######################################################################
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
concurrent_skipping: same_content_newer
|
concurrent_skipping: same_content_newer
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
@@ -25,28 +25,28 @@ jobs:
|
|||||||
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Update PIP cache on every commit
|
- name: Update PIP cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/pip
|
path: ~/.cache/pip
|
||||||
key: pip-${{ github.run_id }}
|
key: pip-${{ github.run_id }}
|
||||||
restore-keys: pip # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
restore-keys: pip # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||||
|
|
||||||
- name: Update PlatformIO cache on every commit
|
- name: Update PlatformIO cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
key: platformio-${{ github.run_id }}
|
key: platformio-${{ github.run_id }}
|
||||||
restore-keys: platformio # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
restore-keys: platformio # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||||
|
|
||||||
- name: Update Build cache on every commit
|
- name: Update Build cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ./code/.pio/
|
path: ./code/.pio/
|
||||||
key: build-${{ github.run_id }}
|
key: build-${{ github.run_id }}
|
||||||
restore-keys: build # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
restore-keys: build # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||||
|
|
||||||
- name: Update generated-files cache on every commit
|
- name: Update generated-files cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
./code/.pio/build/esp32cam/firmware.bin
|
./code/.pio/build/esp32cam/firmware.bin
|
||||||
@@ -57,7 +57,7 @@ jobs:
|
|||||||
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.10'
|
python-version: '3.10'
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ jobs:
|
|||||||
#run: echo "Testing... ${{ github.ref_name }}, ${{ steps.vars.outputs.sha_short }}" > ./sd-card/html/version.txt; mkdir -p ./code/.pio/build/esp32cam/; cd ./code/.pio/build/esp32cam/; echo "${{ steps.vars.outputs.sha_short }}" > firmware.bin; cp firmware.bin partitions.bin; cp firmware.bin bootloader.bin # Testing
|
#run: echo "Testing... ${{ github.ref_name }}, ${{ steps.vars.outputs.sha_short }}" > ./sd-card/html/version.txt; mkdir -p ./code/.pio/build/esp32cam/; cd ./code/.pio/build/esp32cam/; echo "${{ steps.vars.outputs.sha_short }}" > firmware.bin; cp firmware.bin partitions.bin; cp firmware.bin bootloader.bin # Testing
|
||||||
run: cd code; platformio run --environment esp32cam
|
run: cd code; platformio run --environment esp32cam
|
||||||
|
|
||||||
- name: Prepare Web UI (copy data from repo, generate tooltip pages and update hashes in all files)
|
- name: Prepare Web UI (generate tooltip pages and update hashes in all files)
|
||||||
run: |
|
run: |
|
||||||
rm -rf ./html
|
rm -rf ./html
|
||||||
mkdir html
|
mkdir html
|
||||||
@@ -79,7 +79,7 @@ jobs:
|
|||||||
python -m pip install markdown
|
python -m pip install markdown
|
||||||
mkdir html/param-tooltips
|
mkdir html/param-tooltips
|
||||||
cd tools/parameter-tooltip-generator
|
cd tools/parameter-tooltip-generator
|
||||||
bash generate-param-doc-tooltips.sh
|
python generate-param-doc-tooltips.py
|
||||||
cd ../..
|
cd ../..
|
||||||
|
|
||||||
cp -r ./sd-card/html/* ./html/
|
cp -r ./sd-card/html/* ./html/
|
||||||
@@ -101,10 +101,10 @@ jobs:
|
|||||||
needs: build
|
needs: build
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Update generated-files cache on every commit
|
- name: Update generated-files cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
./code/.pio/build/esp32cam/firmware.bin
|
./code/.pio/build/esp32cam/firmware.bin
|
||||||
@@ -115,7 +115,7 @@ jobs:
|
|||||||
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||||
|
|
||||||
- name: Update update cache on every commit
|
- name: Update update cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: update
|
path: update
|
||||||
key: update-${{ github.run_id }}
|
key: update-${{ github.run_id }}
|
||||||
@@ -144,7 +144,7 @@ jobs:
|
|||||||
cp ./sd-card/config/*.tflite ./update/config/ 2>/dev/null || true
|
cp ./sd-card/config/*.tflite ./update/config/ 2>/dev/null || true
|
||||||
|
|
||||||
- name: Upload update as update.zip artifact (Firmware + Web UI + CNN)
|
- name: Upload update as update.zip artifact (Firmware + Web UI + CNN)
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: "AI-on-the-edge-device__update__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
|
name: "AI-on-the-edge-device__update__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
|
||||||
path: ./update/*
|
path: ./update/*
|
||||||
@@ -164,10 +164,10 @@ jobs:
|
|||||||
needs: build
|
needs: build
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Update generated-files cache on every commit
|
- name: Update generated-files cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
./code/.pio/build/esp32cam/firmware.bin
|
./code/.pio/build/esp32cam/firmware.bin
|
||||||
@@ -178,7 +178,7 @@ jobs:
|
|||||||
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||||
|
|
||||||
- name: Update remote_setup cache on every commit
|
- name: Update remote_setup cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: remote_setup
|
path: remote_setup
|
||||||
key: remote_setup-${{ github.run_id }}
|
key: remote_setup-${{ github.run_id }}
|
||||||
@@ -205,7 +205,7 @@ jobs:
|
|||||||
cp ./sd-card/config/* ./remote_setup/config/ 2>/dev/null || true
|
cp ./sd-card/config/* ./remote_setup/config/ 2>/dev/null || true
|
||||||
|
|
||||||
- name: Upload remote_setup as remote_setup.zip artifact (Firmware + Web UI + Config)
|
- name: Upload remote_setup as remote_setup.zip artifact (Firmware + Web UI + Config)
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: "AI-on-the-edge-device__remote-setup__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
|
name: "AI-on-the-edge-device__remote-setup__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
|
||||||
path: ./remote_setup/*
|
path: ./remote_setup/*
|
||||||
@@ -220,10 +220,10 @@ jobs:
|
|||||||
needs: build
|
needs: build
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Update generated-files cache on every commit
|
- name: Update generated-files cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
./code/.pio/build/esp32cam/firmware.bin
|
./code/.pio/build/esp32cam/firmware.bin
|
||||||
@@ -234,7 +234,7 @@ jobs:
|
|||||||
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||||
|
|
||||||
- name: Update manual_setup cache on every commit
|
- name: Update manual_setup cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: manual_setup
|
path: manual_setup
|
||||||
key: manual_setup-${{ github.run_id }}
|
key: manual_setup-${{ github.run_id }}
|
||||||
@@ -263,7 +263,7 @@ jobs:
|
|||||||
cd ./manual_setup
|
cd ./manual_setup
|
||||||
|
|
||||||
- name: Upload manual_setup.zip artifact (Firmware + Bootloader + Partitions + Web UI)
|
- name: Upload manual_setup.zip artifact (Firmware + Bootloader + Partitions + Web UI)
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: "AI-on-the-edge-device__manual-setup__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
|
name: "AI-on-the-edge-device__manual-setup__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
|
||||||
path: ./manual_setup
|
path: ./manual_setup
|
||||||
@@ -284,24 +284,24 @@ jobs:
|
|||||||
id-token: write
|
id-token: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Update update cache on every commit
|
- name: Update update cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: update
|
path: update
|
||||||
key: update-${{ github.run_id }}
|
key: update-${{ github.run_id }}
|
||||||
restore-keys: update # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
restore-keys: update # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||||
|
|
||||||
- name: Update remote_setup cache on every commit
|
- name: Update remote_setup cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: remote_setup
|
path: remote_setup
|
||||||
key: remote_setup-${{ github.run_id }}
|
key: remote_setup-${{ github.run_id }}
|
||||||
restore-keys: remote_setup # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
restore-keys: remote_setup # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||||
|
|
||||||
- name: Update manual_setup cache on every commit
|
- name: Update manual_setup cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: manual_setup
|
path: manual_setup
|
||||||
key: manual_setup-${{ github.run_id }}
|
key: manual_setup-${{ github.run_id }}
|
||||||
@@ -380,7 +380,7 @@ jobs:
|
|||||||
#########################################################################################
|
#########################################################################################
|
||||||
## Update the Web Installer on a release
|
## Update the Web Installer on a release
|
||||||
#########################################################################################
|
#########################################################################################
|
||||||
# This is the same as in the update-webinstaller.yml
|
# Make sure to also update update-webinstaller.yml!
|
||||||
update-web-installer:
|
update-web-installer:
|
||||||
needs: [release]
|
needs: [release]
|
||||||
environment:
|
environment:
|
||||||
@@ -396,7 +396,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Get version of last release
|
- name: Get version of last release
|
||||||
id: last_release
|
id: last_release
|
||||||
@@ -408,20 +408,23 @@ jobs:
|
|||||||
|
|
||||||
- name: Add binary to Web Installer and update manifest
|
- name: Add binary to Web Installer and update manifest
|
||||||
run: |
|
run: |
|
||||||
|
echo "Updating Web installer to use firmware from ${{ steps.last_release.outputs.tag_name }}..."
|
||||||
rm -f docs/binary/firmware.bin
|
rm -f docs/binary/firmware.bin
|
||||||
wget https://github.com/jomjol/AI-on-the-edge-device/releases/download/${{ steps.last_release.outputs.tag_name }}/AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
wget ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.last_release.outputs.tag_name }}/AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
||||||
unzip AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
unzip AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
||||||
cp -f firmware.bin docs/binary/firmware.bin
|
cp -f firmware.bin docs/binary/firmware.bin
|
||||||
cp -f docs/manifest_template.json docs/manifest.json
|
echo "Updating index and manifest file..."
|
||||||
sed -i 's/VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
|
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/index.html
|
||||||
|
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
|
||||||
|
|
||||||
- name: Setup Pages
|
- name: Setup Pages
|
||||||
uses: actions/configure-pages@v2
|
uses: actions/configure-pages@v4
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-pages-artifact@v1
|
uses: actions/upload-pages-artifact@v2
|
||||||
with:
|
with:
|
||||||
path: 'docs'
|
path: 'docs'
|
||||||
|
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@v1
|
uses: actions/deploy-pages@v3 # Note: v4 does not work!
|
||||||
10
.github/workflows/manual-update-webinstaller.yml
vendored
10
.github/workflows/manual-update-webinstaller.yml
vendored
@@ -1,7 +1,7 @@
|
|||||||
# This updates the Web Installer with the files from the docs folder and the binary of the latest release
|
# This updates the Web Installer with the files from the docs folder and the binary of the latest release
|
||||||
# it only gets run on:
|
# it only gets run on:
|
||||||
# - Changes to the docs folder in the `rolling` branch
|
# - Manually triggered
|
||||||
# - On a release
|
# Make sure to also update the lower part of build.yml!
|
||||||
|
|
||||||
name: Manual Web Installer Update
|
name: Manual Web Installer Update
|
||||||
|
|
||||||
@@ -40,12 +40,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Add binary to Web Installer and update manifest
|
- name: Add binary to Web Installer and update manifest
|
||||||
run: |
|
run: |
|
||||||
|
echo "Updating Web installer to use firmware from ${{ steps.last_release.outputs.tag_name }}..."
|
||||||
rm -f docs/binary/firmware.bin
|
rm -f docs/binary/firmware.bin
|
||||||
wget https://github.com/jomjol/AI-on-the-edge-device/releases/download/${{ steps.last_release.outputs.tag_name }}/AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
wget https://github.com/jomjol/AI-on-the-edge-device/releases/download/${{ steps.last_release.outputs.tag_name }}/AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
||||||
unzip AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
unzip AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
||||||
cp -f firmware.bin docs/binary/firmware.bin
|
cp -f firmware.bin docs/binary/firmware.bin
|
||||||
cp -f docs/manifest_template.json docs/manifest.json
|
echo "Updating index and manifest file..."
|
||||||
sed -i 's/VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
|
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/index.html
|
||||||
|
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
|
||||||
|
|
||||||
- name: Setup Pages
|
- name: Setup Pages
|
||||||
uses: actions/configure-pages@v2
|
uses: actions/configure-pages@v2
|
||||||
|
|||||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -4,9 +4,9 @@
|
|||||||
[submodule "code/components/esp-nn"]
|
[submodule "code/components/esp-nn"]
|
||||||
path = code/components/esp-nn
|
path = code/components/esp-nn
|
||||||
url = https://github.com/espressif/esp-nn.git
|
url = https://github.com/espressif/esp-nn.git
|
||||||
[submodule "code/components/tflite-micro-esp-examples"]
|
[submodule "code/components/esp-tflite-micro"]
|
||||||
path = code/components/tflite-micro-esp-examples
|
path = code/components/esp-tflite-micro
|
||||||
url = https://github.com/espressif/tflite-micro-esp-examples.git
|
url = https://github.com/espressif/esp-tflite-micro.git
|
||||||
[submodule "code/components/stb"]
|
[submodule "code/components/stb"]
|
||||||
path = code/components/stb
|
path = code/components/stb
|
||||||
url = https://github.com/nothings/stb.git
|
url = https://github.com/nothings/stb.git
|
||||||
|
|||||||
68
Changelog.md
68
Changelog.md
@@ -1,8 +1,61 @@
|
|||||||
|
## [15.6.0] - 2024-02-09
|
||||||
|
|
||||||
|
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.5.0...v15.6.0)
|
||||||
|
|
||||||
|
#### Fixed
|
||||||
|
|
||||||
|
* Fixed issues with the SD-Card initialization
|
||||||
|
|
||||||
|
## [15.5.0] - 2024-02-02
|
||||||
|
|
||||||
|
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.4.0...v15.5.0)
|
||||||
|
|
||||||
|
#### Changed
|
||||||
|
|
||||||
|
- Update PlattformIO to v6.5.0, which means esp-idf to v5.1
|
||||||
|
- Enhance busy notification
|
||||||
|
- Implemented late analog / digital transition
|
||||||
|
|
||||||
|
#### Fixed
|
||||||
|
|
||||||
|
* ATA-TRIM: workaround for old SD-cards with no trim function to work with esp-idf v5.x
|
||||||
|
* InfluxDB: Modified the time conversions to be more stable (UTC vs. local time shifts)
|
||||||
|
* Fix negatives on extended resolution false
|
||||||
|
* Show chip infos on info page
|
||||||
|
* Fix memory leaks in tflite integration
|
||||||
|
|
||||||
|
## [15.4.0] - 2023-12-22
|
||||||
|
|
||||||
|
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.3.0...v15.4.0)
|
||||||
|
|
||||||
|
#### Changed
|
||||||
|
|
||||||
|
- Updates submodules (esp-nn, tflite-micro-example, esp-camera)
|
||||||
|
|
||||||
|
- Explicitly included needed tflite network layers (instead of all) , resulting in much smaller firmware size
|
||||||
|
|
||||||
|
- Added shortcut icon
|
||||||
|
|
||||||
|
- Rename in InfluxDB 'Database' to 'Bucket'
|
||||||
|
|
||||||
|
- Updated analog tflite files
|
||||||
|
- dig-class100-0167_s2_q.tflite
|
||||||
|
- dig-class11_1700_s2.tflite
|
||||||
|
- ana-cont_1208_s2_q.tflite
|
||||||
|
|
||||||
|
|
||||||
|
#### Fixed
|
||||||
|
|
||||||
|
* InfluxDB: consider DST setting for UTC time conversion
|
||||||
|
|
||||||
|
* Minor html response bugfix
|
||||||
|
|
||||||
|
- Memory leakage (MQTT)
|
||||||
|
|
||||||
|
|
||||||
## [15.3.0] - 2023-07-22
|
## [15.3.0] - 2023-07-22
|
||||||
|
|
||||||
### Changes
|
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.3.0...v15.2.4)
|
||||||
|
|
||||||
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.2.1...v15.2.4)
|
|
||||||
|
|
||||||
#### Changed
|
#### Changed
|
||||||
|
|
||||||
@@ -11,13 +64,8 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/
|
|||||||
- ana-cont_1207_s2_q.tflite
|
- ana-cont_1207_s2_q.tflite
|
||||||
- dig-cont_0620_s3_q.tflite
|
- dig-cont_0620_s3_q.tflite
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [15.2.4] - 2023-05-02
|
## [15.2.4] - 2023-05-02
|
||||||
|
|
||||||
### Changes
|
|
||||||
|
|
||||||
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.2.1...v15.2.4)
|
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.2.1...v15.2.4)
|
||||||
|
|
||||||
#### Changed
|
#### Changed
|
||||||
@@ -41,8 +89,6 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/
|
|||||||
|
|
||||||
## [15.2.0] - 2023-04-23
|
## [15.2.0] - 2023-04-23
|
||||||
|
|
||||||
### Changes
|
|
||||||
|
|
||||||
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.1...v15.2.0)
|
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.1...v15.2.0)
|
||||||
|
|
||||||
#### Added
|
#### Added
|
||||||
@@ -72,8 +118,6 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/
|
|||||||
|
|
||||||
## [15.1.1] - 2023-03-23
|
## [15.1.1] - 2023-03-23
|
||||||
|
|
||||||
### Changes
|
|
||||||
|
|
||||||
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.0...v15.1.1)
|
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.0...v15.1.1)
|
||||||
|
|
||||||
#### Added
|
#### Added
|
||||||
|
|||||||
@@ -2,15 +2,41 @@
|
|||||||
|
|
||||||
**There are a lot of ideas for further improvements, but only limited capacity on side of the developer.** Therefore I have created this page as a collection of ideas.
|
**There are a lot of ideas for further improvements, but only limited capacity on side of the developer.** Therefore I have created this page as a collection of ideas.
|
||||||
|
|
||||||
1. Who ever has a new idea can put it here, so it that it is not forgotten.
|
1. Whoever has a new idea can put it here, so that it is not forgotten.
|
||||||
|
|
||||||
2. Who ever has time, capacity and passion to support, can take any of the ideas and implement them.
|
2. Whoever has the time, capacity and passion to support the project can take any of the ideas and implement them. I will provide support and help wherever I can!
|
||||||
I will support and help where ever I can!
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
____
|
____
|
||||||
|
|
||||||
|
|
||||||
|
#### #40 Trigger with cron like exact time slot
|
||||||
|
|
||||||
|
* https://github.com/jomjol/AI-on-the-edge-device/issues/2470
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### #39 upnp implementation to auto detect the device
|
||||||
|
|
||||||
|
* https://github.com/jomjol/AI-on-the-edge-device/issues/2481
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### #38 Energy Saving
|
||||||
|
|
||||||
|
* Deep sleep between recognition
|
||||||
|
* https://github.com/jomjol/AI-on-the-edge-device/issues/2486
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### #37 Auto init SD card
|
||||||
|
|
||||||
|
* Fully implement the SD card handling (including formatting) into the firmware
|
||||||
|
* https://github.com/jomjol/AI-on-the-edge-device/issues/2488Demo
|
||||||
|
|
||||||
#### #36 Run demo without camera
|
#### #36 Run demo without camera
|
||||||
|
|
||||||
Demo mode requires a working camera (if not, one receives a 'Cam bad' error). Would be nice to demo or play around on other ESP32 boards (or on ESP32-CAM boards when you broke the camera cable...).
|
Demo mode requires a working camera (if not, one receives a 'Cam bad' error). Would be nice to demo or play around on other ESP32 boards (or on ESP32-CAM boards when you broke the camera cable...).
|
||||||
|
|
||||||
#### #35 Use the same model, but provide the image from a Smartphone Camera
|
#### #35 Use the same model, but provide the image from a Smartphone Camera
|
||||||
@@ -51,7 +77,7 @@ haveing this state in the mqtt broker can trigger functions like closing the ate
|
|||||||
|
|
||||||
#### ~~#29 Add favicon and use the hostname for the website~~- implemented v11.3.1
|
#### ~~#29 Add favicon and use the hostname for the website~~- implemented v11.3.1
|
||||||
|
|
||||||
~~* https://github.com/jomjol/AI-on-the-edge-device/issues/927~~
|
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/927~~
|
||||||
|
|
||||||
#### #28 Improved error handling for ROIs
|
#### #28 Improved error handling for ROIs
|
||||||
|
|
||||||
@@ -89,7 +115,7 @@ haveing this state in the mqtt broker can trigger functions like closing the ate
|
|||||||
|
|
||||||
#### ~~#22 Direct hint to the different neural network files in the other repositories~~- implemented >v11.3.1
|
#### ~~#22 Direct hint to the different neural network files in the other repositories~~- implemented >v11.3.1
|
||||||
|
|
||||||
~~* https://github.com/jomjol/AI-on-the-edge-device/issues/644~~
|
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/644~~
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
96
README.md
96
README.md
@@ -1,31 +1,31 @@
|
|||||||
# Welcome to the AI-on-the-edge-device
|
# Welcome to the AI-on-the-edge-device
|
||||||
<img src="images/icon/watermeter.svg" width="100px">
|
<img src="images/icon/watermeter.svg" width="100px">
|
||||||
|
|
||||||
Artificial intelligence based systems have been established in our every days live. Just think of speech or image recognition. Most of the systems relay on either powerful processors or a direct connection to the cloud for doing the calculations up there. With the increasing power of modern processors the AI systems are coming closer to the end user - which is usually called **edge computing**.
|
Artificial intelligence based systems have become established in our everyday lives. Just think of speech or image recognition. Most of the systems rely on either powerful processors or a direct connection to the cloud for doing the calculations there. With the increasing power of modern processors, the AI systems are coming closer to the end user – which is usually called **edge computing**.
|
||||||
Here this edge computing is brought into a practical oriented example, where a AI network is implemented on a ESP32 device so: **AI on the edge**.
|
Here, this edge computing is put into a practically oriented example, where an AI network is implemented on an ESP32 device so: **AI on the edge**.
|
||||||
|
|
||||||
This projects allows you to digitalize your **analoge** water, gas, power and other meters using cheap and easily available hardware.
|
This project allows you to digitize your **analog** water, gas, power and other meters using cheap and easily available hardware.
|
||||||
|
|
||||||
All you need is an [ESP32 board with a supported camera](https://jomjol.github.io/AI-on-the-edge-device-docs/Hardware-Compatibility/) and a bit of a practical hand.
|
All you need is an [ESP32 board with a supported camera](https://jomjol.github.io/AI-on-the-edge-device-docs/Hardware-Compatibility/) and something of a practical hand.
|
||||||
|
|
||||||
<img src="images/esp32-cam.png" width="200px">
|
<img src="images/esp32-cam.png" width="200px">
|
||||||
|
|
||||||
## Key features
|
## Key features
|
||||||
- Tensorflow Lite (TFlite) integration - including easy to use wrapper
|
- Tensorflow Lite (TFlite) integration – including easy-to-use wrapper
|
||||||
- Inline Image processing (feature detection, alignment, ROI extraction)
|
- Inline image processing (feature detection, alignment, ROI extraction)
|
||||||
- **Small** and **cheap** device (3x4.5x2 cm³, < 10 EUR)
|
- **Small** and **cheap** device (3 x 4.5 x 2 cm³, < 10 EUR)
|
||||||
- camera and illumination integrated
|
- Integrated camera and illumination
|
||||||
- Web surface to administrate and control
|
- Web interface for administration and control
|
||||||
- OTA-Interface to update directly through the web interface
|
- OTA interface for updating directly via the web interface
|
||||||
- Full integration into Homeassistant
|
- Full integration into Homeassistant
|
||||||
- Support for Influx DB 1
|
- Support for Influx DB 1 and 2
|
||||||
- MQTT
|
- MQTT
|
||||||
- REST API
|
- REST API
|
||||||
|
|
||||||
## Workflow
|
## Workflow
|
||||||
The device takes a photo of your meter at a defined interval. It then extracts the Regions of Interest (ROI's) out of it and runs them through an artificial inteligence. As a result, you get the digitalized value of your meter.
|
The device takes a photo of your meter at a defined interval. It then extracts the Regions of Interest (ROIs) from the image and runs them through artificial intelligence. As a result, you get the digitized value of your meter.
|
||||||
|
|
||||||
There are several options what to do with that value. Either send it to a MQTT broker, write it to an InfluxDb or simply provide it throug a REST API.
|
There are several options for what to do with that value. Either send it to an MQTT broker, write it to an InfluxDb or simply provide access to it via a REST API.
|
||||||
|
|
||||||
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/idea.jpg" width="600">
|
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/idea.jpg" width="600">
|
||||||
|
|
||||||
@@ -41,62 +41,68 @@ There are several options what to do with that value. Either send it to a MQTT b
|
|||||||
|
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
There is a growing [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/) which provides you with a lot of information.
|
There is growing [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/) which provides you with a lot of information. Head there to get a start, set it up and configure it.
|
||||||
Head there to get a start, set it up and configure it.
|
|
||||||
|
|
||||||
There are also a articles in the German Heise magazine "make:" about the setup and the technical background (behind a paywall) : [DIY - Setup](https://www.heise.de/select/make/2021/2/2103513300897420296)
|
There are also articles in the German Heise magazine "make:" about the setup and technical background (behind a paywall): [DIY - Setup](https://www.heise.de/select/make/2021/2/2103513300897420296)
|
||||||
|
|
||||||
For further background information, head to [Neural Networks](https://www.heise.de/select/make/2021/6/2126410443385102621), [Training Neural Networks](https://www.heise.de/select/make/2022/1/2134114065999161585) and [Programming on the ESP32](https://www.heise.de/select/make/2022/2/2204010051597422030)
|
A lot of people created useful Youtube videos which might help you getting started.
|
||||||
|
Here a small selection:
|
||||||
|
|
||||||
|
- [youtube.com/watch?v=HKBofb1cnNc](https://www.youtube.com/watch?v=HKBofb1cnNc)
|
||||||
|
- [youtube.com/watch?v=yyf0ORNLCk4](https://www.youtube.com/watch?v=yyf0ORNLCk4)
|
||||||
|
- [youtube.com/watch?v=XxmTubGek6M](https://www.youtube.com/watch?v=XxmTubGek6M)
|
||||||
|
- [youtube.com/watch?v=mDIJEyElkAU](https://www.youtube.com/watch?v=mDIJEyElkAU)
|
||||||
|
- [youtube.com/watch?v=SssiPkyKVVs](https://www.youtube.com/watch?v=SssiPkyKVVs)
|
||||||
|
- [youtube.com/watch?v=MAHE_QyHZFQ](https://www.youtube.com/watch?v=MAHE_QyHZFQ)
|
||||||
|
- [youtube.com/watch?v=Uap_6bwtILQ](https://www.youtube.com/watch?v=Uap_6bwtILQ)
|
||||||
|
|
||||||
|
For further background information, head to [Neural Networks](https://www.heise.de/select/make/2021/6/2126410443385102621), [Training Neural Networks](https://www.heise.de/select/make/2022/1/2134114065999161585) and [Programming on the ESP32](https://www.heise.de/select/make/2022/2/2204010051597422030).
|
||||||
|
|
||||||
### Download
|
### Download
|
||||||
The latest available version is available on the [Releases page](https://github.com/jomjol/AI-on-the-edge-device/releases).
|
The latest available version can be found on the [Releases page](https://github.com/jomjol/AI-on-the-edge-device/releases).
|
||||||
|
|
||||||
### Flashing of the ESP32
|
### Flashing the ESP32
|
||||||
Initially you will have to flash the ESP32 through an USB connection. Later an update is possible directly over the Air (OTA).
|
Initially you will have to flash the ESP32 via a USB connection. Later updates are possible directly over the air (OTA using WIFI).
|
||||||
|
|
||||||
There are different ways to flash your ESP32:
|
There are different ways to flash your ESP32:
|
||||||
- [Web Installer and Console](https://jomjol.github.io/AI-on-the-edge-device/index.html) (Webbrowser based tool to flash the ESP32 and extract the Log over USB)
|
- The prefered way is the [Web Installer and Console](https://jomjol.github.io/AI-on-the-edge-device/index.html) which is a browser-based tool to flash the ESP32 and extract the log over USB:
|
||||||
|

|
||||||
- Flash Tool from Espressif
|
- Flash Tool from Espressif
|
||||||
- ESPtool (Command Line Tool)
|
- ESPtool (command-line tool)
|
||||||
|
|
||||||
See the [Docu](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/) for more information.
|
See the [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/) for more information.
|
||||||
|
|
||||||
### Flashing the SD-Card
|
### Flashing the SD Card
|
||||||
The SD-Card must be flashed separately, see the [Docu](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/) for details.
|
The SD card can be setup automatically after the firmware got installed. See the [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#remote-setup-using-the-built-in-access-point) for details. For this to work, the SD card must be FAT formated (which is the default on a new SD card).
|
||||||
|
Alternatively the SD card still can be setup manually, see the [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#3-sd-card) for details!
|
||||||
|
|
||||||
## Casing
|
## Casing
|
||||||
|
Various 3D-printable housing can be found here:
|
||||||
A 3d-printable housing can be found here:
|
|
||||||
- https://www.thingiverse.com/thing:4573481 (Water Meter)
|
- https://www.thingiverse.com/thing:4573481 (Water Meter)
|
||||||
- https://www.thingiverse.com/thing:5028229 (Power Meter)
|
- https://www.thingiverse.com/thing:5028229 (Power Meter)
|
||||||
- https://www.thingiverse.com/thing:5224101 (Gas Meter)
|
- https://www.thingiverse.com/thing:5224101 (Gas Meter)
|
||||||
- https://www.thingiverse.com/thing:4571627 (ESP32-Cam housing only)
|
- https://www.thingiverse.com/thing:4571627 (ESP32-cam housing only)
|
||||||
|
|
||||||
## Build it yourself
|
|
||||||
See [Build Instructions](code/README.md).
|
|
||||||
|
|
||||||
## Donate
|
## Donate
|
||||||
If you would like to support the developer with a cup of coffee you can do that via [Paypal](https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL).
|
If you would like to support the developer with a cup of coffee, you can do that via [PayPal](https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL).
|
||||||
|
|
||||||
<form action="https://www.paypal.com/donate" method="post" target="_top">
|
<a href="https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL"><img border="0" src="images/paypal.png" width="200px" target="_blank"></a>
|
||||||
<input type="hidden" name="hosted_button_id" value="8TRSVYNYKDSWL" />
|
|
||||||
<input type="image" src="https://www.paypalobjects.com/en_US/DK/i/btn/btn_donateCC_LG.gif" border="0" name="submit" title="PayPal - The safer, easier way to pay online!" alt="Donate with PayPal button" />
|
## Support
|
||||||
<img alt="" border="0" src="https://www.paypal.com/en_DE/i/scr/pixel.gif" width="1" height="1" />
|
If you have any technical problems please search the [discussions](https://github.com/jomjol/AI-on-the-edge-device/discussions). In case you found a ug or have a feature request, please open an [issue](https://github.com/jomjol/AI-on-the-edge-device/issues).
|
||||||
</form>
|
|
||||||
If you have any technical topics, you can create an [Issue](https://github.com/jomjol/AI-on-the-edge-device/issues).
|
|
||||||
|
|
||||||
In other cases you can contact the developer via email: <img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/mail.jpg" height="25">
|
In other cases you can contact the developer via email: <img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/mail.jpg" height="25">
|
||||||
|
|
||||||
## Changes and History
|
## Changes and History
|
||||||
See [Changelog](Changelog.md)
|
See [Changelog](Changelog.md).
|
||||||
|
|
||||||
|
## Build It Yourself
|
||||||
|
See [Build Instructions](code/README.md).
|
||||||
|
|
||||||
## Tools
|
## Tools
|
||||||
|
|
||||||
* Logfile downloader and combiner (Thx to [reserve85](https://github.com/reserve85))
|
* Logfile downloader and combiner (Thx to [reserve85](https://github.com/reserve85))
|
||||||
* Files see ['/tools/logfile-tool'](tbd), How-to see [Docu](https://jomjol.github.io/AI-on-the-edge-device-docs/outdated--Gasmeter-Log-Downloader/)
|
* Files see ['/tools/logfile-tool'](tbd), how-to see [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/outdated--Gasmeter-Log-Downloader/)
|
||||||
|
|
||||||
## Additional Ideas
|
## Additional Ideas
|
||||||
There are some ideas and feature requests which are not followed currently - mainly due to capacity reasons on side of the developer. They are collected here: [FeatureRequest.md](FeatureRequest.md)
|
There are some ideas and feature requests which are not currently being pursued – mainly due to capacity reasons on the part of the developers.
|
||||||
|
They features are collected in the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues) and in [FeatureRequest.md](FeatureRequest.md).
|
||||||
------
|
|
||||||
|
|
||||||
|
|||||||
1
code/.gitignore
vendored
1
code/.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
.pio
|
.pio
|
||||||
|
.idea
|
||||||
.vscode/.browse.c_cpp.db*
|
.vscode/.browse.c_cpp.db*
|
||||||
.vscode/c_cpp_properties.json
|
.vscode/c_cpp_properties.json
|
||||||
.vscode/launch.json
|
.vscode/launch.json
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
cmake_minimum_required(VERSION 3.16.0)
|
cmake_minimum_required(VERSION 3.16.0)
|
||||||
|
|
||||||
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common components/tflite-micro-esp-examples/components/tflite-lib)
|
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common components/esp-tflite-micro)
|
||||||
|
|
||||||
ADD_CUSTOM_COMMAND(
|
ADD_CUSTOM_COMMAND(
|
||||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.cpp
|
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.cpp
|
||||||
|
|||||||
@@ -8,6 +8,15 @@ git checkout rolling
|
|||||||
git submodule update --init
|
git submodule update --init
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Update Submodules
|
||||||
|
```
|
||||||
|
cd /components/submodule-name (e.g. tflite-micro-example)
|
||||||
|
git checkout VERSION (e.g. HASH of latest tflite-micro-example build)
|
||||||
|
cd ../../ (auf Ebene von code)
|
||||||
|
git submodule update --init
|
||||||
|
```
|
||||||
|
Evt. muss man vorher noch einige Verzeichnisse in compenents von Hand löschen, da sie beim checkout nicht gelöscht wurden (vor update -- init)
|
||||||
|
|
||||||
## Build and Flash within terminal
|
## Build and Flash within terminal
|
||||||
See further down to build it within an IDE.
|
See further down to build it within an IDE.
|
||||||
### Compile
|
### Compile
|
||||||
@@ -60,3 +69,6 @@ pio device monitor -p /dev/ttyUSB0
|
|||||||
- `pio run --target erase` to erase the flash
|
- `pio run --target erase` to erase the flash
|
||||||
- `pio run --target upload` this will upload the `bootloader.bin, partitions.bin,firmware.bin` from the `code/.pio/build/esp32cam/` folder.
|
- `pio run --target upload` this will upload the `bootloader.bin, partitions.bin,firmware.bin` from the `code/.pio/build/esp32cam/` folder.
|
||||||
- `pio device monitor` to observe the logs via uart
|
- `pio device monitor` to observe the logs via uart
|
||||||
|
|
||||||
|
# Update Parameters
|
||||||
|
If you create or rename a parameter, make sure to update its documentation in `../param-docs/parameter-pages`! Check the `../param-docs/README.md` for more information.
|
||||||
|
|||||||
Submodule code/components/esp-nn updated: 6b3ef8e226...34e97138de
1
code/components/esp-tflite-micro
Submodule
1
code/components/esp-tflite-micro
Submodule
Submodule code/components/esp-tflite-micro added at 13f26b8294
Submodule code/components/esp32-camera updated: 5c8349f4cf...dba8da9898
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
|||||||
|
|
||||||
idf_component_register(SRCS ${app_sources}
|
idf_component_register(SRCS ${app_sources}
|
||||||
INCLUDE_DIRS "." "../../include" "miniz"
|
INCLUDE_DIRS "." "../../include" "miniz"
|
||||||
REQUIRES vfs tflite-lib esp_http_server app_update esp_http_client nvs_flash jomjol_tfliteclass jomjol_flowcontroll spiffs jomjol_helper jomjol_controlGPIO)
|
REQUIRES vfs esp_http_server app_update esp_http_client nvs_flash jomjol_tfliteclass jomjol_flowcontroll spiffs jomjol_helper jomjol_controlGPIO)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -153,7 +153,7 @@
|
|||||||
/*#define MINIZ_NO_MALLOC */
|
/*#define MINIZ_NO_MALLOC */
|
||||||
|
|
||||||
#ifdef MINIZ_NO_INFLATE_APIS
|
#ifdef MINIZ_NO_INFLATE_APIS
|
||||||
#define MINIZ_NO_ARCHIVE_APIS
|
//#define MINIZ_NO_ARCHIVE_APIS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef MINIZ_NO_DEFLATE_APIS
|
#ifdef MINIZ_NO_DEFLATE_APIS
|
||||||
|
|||||||
@@ -547,7 +547,7 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
|||||||
/* Get value of expected key from query string */
|
/* Get value of expected key from query string */
|
||||||
if (httpd_query_key_value(buf, "readonly", param, sizeof(param)) == ESP_OK) {
|
if (httpd_query_key_value(buf, "readonly", param, sizeof(param)) == ESP_OK) {
|
||||||
ESP_LOGI(TAG, "Found URL query parameter => readonly=%s", param);
|
ESP_LOGI(TAG, "Found URL query parameter => readonly=%s", param);
|
||||||
readonly = param && strcmp(param,"true")==0;
|
readonly = (strcmp(param,"true") == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -588,7 +588,9 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
|||||||
/* Read file in chunks into the scratch buffer */
|
/* Read file in chunks into the scratch buffer */
|
||||||
chunksize = fread(chunk, 1, SERVER_FILER_SCRATCH_BUFSIZE, fd);
|
chunksize = fread(chunk, 1, SERVER_FILER_SCRATCH_BUFSIZE, fd);
|
||||||
|
|
||||||
/* Send the buffer contents as HTTP response chunk */
|
/* Send buffer contents as HTTP chunk. If empty this functions as a
|
||||||
|
* last-chunk message, signaling end-of-response, to the HTTP client.
|
||||||
|
* See RFC 2616, section 3.6.1 for details on Chunked Transfer Encoding. */
|
||||||
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
||||||
fclose(fd);
|
fclose(fd);
|
||||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!");
|
||||||
@@ -606,8 +608,6 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
|||||||
fclose(fd);
|
fclose(fd);
|
||||||
ESP_LOGD(TAG, "File successfully sent");
|
ESP_LOGD(TAG, "File successfully sent");
|
||||||
|
|
||||||
/* Respond with an empty chunk to signal HTTP response completion */
|
|
||||||
httpd_resp_send_chunk(req, NULL, 0);
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -134,7 +134,22 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Determines the number of an ROI in connection with previous ROI results
|
||||||
|
*
|
||||||
|
* @param number: is the current ROI as float value from recognition
|
||||||
|
* @param number_of_predecessors: is the last (lower) ROI as float from recognition
|
||||||
|
* @param eval_predecessors: is the evaluated number. Sometimes a much lower value can change higer values
|
||||||
|
* example: 9.8, 9.9, 0.1
|
||||||
|
* 0.1 => 0 (eval_predecessors)
|
||||||
|
* The 0 makes a 9.9 to 0 (eval_predecessors)
|
||||||
|
* The 0 makes a 9.8 to 0
|
||||||
|
* @param Analog_Predecessors false/true if the last ROI is an analog or digital ROI (default=false)
|
||||||
|
* runs in special handling because analog is much less precise
|
||||||
|
* @param digitalAnalogTransitionStart start of the transitionlogic begins on number_of_predecessor (default=9.2)
|
||||||
|
*
|
||||||
|
* @return int the determined number of the current ROI
|
||||||
|
*/
|
||||||
int ClassFlowCNNGeneral::PointerEvalHybridNew(float number, float number_of_predecessors, int eval_predecessors, bool Analog_Predecessors, float digitalAnalogTransitionStart)
|
int ClassFlowCNNGeneral::PointerEvalHybridNew(float number, float number_of_predecessors, int eval_predecessors, bool Analog_Predecessors, float digitalAnalogTransitionStart)
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
@@ -145,7 +160,8 @@ int ClassFlowCNNGeneral::PointerEvalHybridNew(float number, float number_of_pred
|
|||||||
{
|
{
|
||||||
// on first digit is no spezial logic for transition needed
|
// on first digit is no spezial logic for transition needed
|
||||||
// we use the recognition as given. The result is the int value of the recognition
|
// we use the recognition as given. The result is the int value of the recognition
|
||||||
result = (int) ((int) trunc(number) + 10) % 10;
|
// add precisition of 2 digits and round before trunc
|
||||||
|
result = (int) ((int) trunc(round((number+10 % 10)*100)) ) / 100;
|
||||||
|
|
||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - No predecessor - Result = " + std::to_string(result) +
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - No predecessor - Result = " + std::to_string(result) +
|
||||||
" number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors)+ " eval_predecessors = " + std::to_string(eval_predecessors) + " Digital_Uncertainty = " + std::to_string(Digital_Uncertainty));
|
" number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors)+ " eval_predecessors = " + std::to_string(eval_predecessors) + " Digital_Uncertainty = " + std::to_string(Digital_Uncertainty));
|
||||||
@@ -484,7 +500,7 @@ bool ClassFlowCNNGeneral::doFlow(string time)
|
|||||||
|
|
||||||
if (!doAlignAndCut(time)){
|
if (!doAlignAndCut(time)){
|
||||||
return false;
|
return false;
|
||||||
};
|
}
|
||||||
|
|
||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "doFlow after alignment");
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "doFlow after alignment");
|
||||||
|
|
||||||
@@ -852,10 +868,9 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
|
|||||||
|
|
||||||
bool ClassFlowCNNGeneral::isExtendedResolution(int _number)
|
bool ClassFlowCNNGeneral::isExtendedResolution(int _number)
|
||||||
{
|
{
|
||||||
if (!(CNNType == Digital))
|
if (CNNType == Digital)
|
||||||
return true;
|
return false;
|
||||||
|
return true;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ struct NumberPost {
|
|||||||
bool AllowNegativeRates;
|
bool AllowNegativeRates;
|
||||||
bool checkDigitIncreaseConsistency;
|
bool checkDigitIncreaseConsistency;
|
||||||
time_t lastvalue;
|
time_t lastvalue;
|
||||||
|
time_t timeStampTimeUTC;
|
||||||
string timeStamp;
|
string timeStamp;
|
||||||
double FlowRateAct; // m3 / min
|
double FlowRateAct; // m3 / min
|
||||||
double PreValue; // last value that was read out well
|
double PreValue; // last value that was read out well
|
||||||
|
|||||||
@@ -137,6 +137,7 @@ bool ClassFlowInfluxDB::doFlow(string zwtime)
|
|||||||
std::string resultraw = "";
|
std::string resultraw = "";
|
||||||
std::string resultrate = "";
|
std::string resultrate = "";
|
||||||
std::string resulttimestamp = "";
|
std::string resulttimestamp = "";
|
||||||
|
long int timeutc;
|
||||||
string zw = "";
|
string zw = "";
|
||||||
string namenumber = "";
|
string namenumber = "";
|
||||||
|
|
||||||
@@ -152,6 +153,7 @@ bool ClassFlowInfluxDB::doFlow(string zwtime)
|
|||||||
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
||||||
resultrate = (*NUMBERS)[i]->ReturnRateValue;
|
resultrate = (*NUMBERS)[i]->ReturnRateValue;
|
||||||
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
||||||
|
timeutc = (*NUMBERS)[i]->timeStampTimeUTC;
|
||||||
|
|
||||||
if ((*NUMBERS)[i]->FieldV1.length() > 0)
|
if ((*NUMBERS)[i]->FieldV1.length() > 0)
|
||||||
{
|
{
|
||||||
@@ -167,7 +169,7 @@ bool ClassFlowInfluxDB::doFlow(string zwtime)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (result.length() > 0)
|
if (result.length() > 0)
|
||||||
InfluxDBPublish(measurement, namenumber, result, resulttimestamp);
|
InfluxDBPublish(measurement, namenumber, result, timeutc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ static const char* TAG = "INFLUXDBV2";
|
|||||||
void ClassFlowInfluxDBv2::SetInitialParameter(void)
|
void ClassFlowInfluxDBv2::SetInitialParameter(void)
|
||||||
{
|
{
|
||||||
uri = "";
|
uri = "";
|
||||||
database = "";
|
bucket = "";
|
||||||
dborg = "";
|
dborg = "";
|
||||||
dbtoken = "";
|
dbtoken = "";
|
||||||
// dbfield = "";
|
// dbfield = "";
|
||||||
@@ -109,9 +109,9 @@ bool ClassFlowInfluxDBv2::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
{
|
{
|
||||||
handleMeasurement(splitted[0], splitted[1]);
|
handleMeasurement(splitted[0], splitted[1]);
|
||||||
}
|
}
|
||||||
if (((toUpper(splitted[0]) == "DATABASE")) && (splitted.size() > 1))
|
if (((toUpper(splitted[0]) == "BUCKET")) && (splitted.size() > 1))
|
||||||
{
|
{
|
||||||
this->database = splitted[1];
|
this->bucket = splitted[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,11 +119,11 @@ bool ClassFlowInfluxDBv2::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
printf("org: %s\n", dborg.c_str());
|
printf("org: %s\n", dborg.c_str());
|
||||||
printf("token: %s\n", dbtoken.c_str());
|
printf("token: %s\n", dbtoken.c_str());
|
||||||
|
|
||||||
if ((uri.length() > 0) && (database.length() > 0) && (dbtoken.length() > 0) && (dborg.length() > 0))
|
if ((uri.length() > 0) && (bucket.length() > 0) && (dbtoken.length() > 0) && (dborg.length() > 0))
|
||||||
{
|
{
|
||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init InfluxDB with uri: " + uri + ", org: " + dborg + ", token: *****");
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init InfluxDB with uri: " + uri + ", org: " + dborg + ", token: *****");
|
||||||
// printf("vor V2 Init\n");
|
// printf("vor V2 Init\n");
|
||||||
InfluxDB_V2_Init(uri, database, dborg, dbtoken);
|
InfluxDB_V2_Init(uri, bucket, dborg, dbtoken);
|
||||||
// printf("nach V2 Init\n");
|
// printf("nach V2 Init\n");
|
||||||
InfluxDBenable = true;
|
InfluxDBenable = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -196,6 +196,7 @@ bool ClassFlowInfluxDBv2::doFlow(string zwtime)
|
|||||||
std::string resultraw = "";
|
std::string resultraw = "";
|
||||||
std::string resultrate = "";
|
std::string resultrate = "";
|
||||||
std::string resulttimestamp = "";
|
std::string resulttimestamp = "";
|
||||||
|
long int resulttimeutc = 0;
|
||||||
string zw = "";
|
string zw = "";
|
||||||
string namenumber = "";
|
string namenumber = "";
|
||||||
|
|
||||||
@@ -212,6 +213,8 @@ bool ClassFlowInfluxDBv2::doFlow(string zwtime)
|
|||||||
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
||||||
resultrate = (*NUMBERS)[i]->ReturnRateValue;
|
resultrate = (*NUMBERS)[i]->ReturnRateValue;
|
||||||
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
||||||
|
resulttimeutc = (*NUMBERS)[i]->timeStampTimeUTC;
|
||||||
|
|
||||||
|
|
||||||
if ((*NUMBERS)[i]->FieldV2.length() > 0)
|
if ((*NUMBERS)[i]->FieldV2.length() > 0)
|
||||||
{
|
{
|
||||||
@@ -229,8 +232,7 @@ bool ClassFlowInfluxDBv2::doFlow(string zwtime)
|
|||||||
printf("vor sende Influx_DB_V2 - namenumber. %s, result: %s, timestampt: %s", namenumber.c_str(), result.c_str(), resulttimestamp.c_str());
|
printf("vor sende Influx_DB_V2 - namenumber. %s, result: %s, timestampt: %s", namenumber.c_str(), result.c_str(), resulttimestamp.c_str());
|
||||||
|
|
||||||
if (result.length() > 0)
|
if (result.length() > 0)
|
||||||
InfluxDB_V2_Publish(measurement, namenumber, result, resulttimestamp);
|
InfluxDB_V2_Publish(measurement, namenumber, result, resulttimeutc);
|
||||||
// InfluxDB_V2_Publish(namenumber, result, resulttimestamp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class ClassFlowInfluxDBv2 :
|
|||||||
public ClassFlow
|
public ClassFlow
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::string uri, database;
|
std::string uri, bucket;
|
||||||
std::string dborg, dbtoken, dbfield;
|
std::string dborg, dbtoken, dbfield;
|
||||||
std::string OldValue;
|
std::string OldValue;
|
||||||
ClassFlowPostProcessing* flowpostprocessing;
|
ClassFlowPostProcessing* flowpostprocessing;
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ void ClassFlowMQTT::SetInitialParameter(void)
|
|||||||
topicUptime = "";
|
topicUptime = "";
|
||||||
topicFreeMem = "";
|
topicFreeMem = "";
|
||||||
|
|
||||||
|
caCertFilename = "";
|
||||||
|
clientCertFilename = "";
|
||||||
|
clientKeyFilename = "";
|
||||||
clientname = wlan_config.hostname;
|
clientname = wlan_config.hostname;
|
||||||
|
|
||||||
OldValue = "";
|
OldValue = "";
|
||||||
@@ -102,6 +105,18 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||||
{
|
{
|
||||||
splitted = ZerlegeZeile(aktparamgraph);
|
splitted = ZerlegeZeile(aktparamgraph);
|
||||||
|
if ((toUpper(splitted[0]) == "CACERT") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
this->caCertFilename = splitted[1];
|
||||||
|
}
|
||||||
|
if ((toUpper(splitted[0]) == "CLIENTCERT") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
this->clientCertFilename = splitted[1];
|
||||||
|
}
|
||||||
|
if ((toUpper(splitted[0]) == "CLIENTKEY") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
this->clientKeyFilename = splitted[1];
|
||||||
|
}
|
||||||
if ((toUpper(splitted[0]) == "USER") && (splitted.size() > 1))
|
if ((toUpper(splitted[0]) == "USER") && (splitted.size() > 1))
|
||||||
{
|
{
|
||||||
this->user = splitted[1];
|
this->user = splitted[1];
|
||||||
@@ -196,7 +211,8 @@ bool ClassFlowMQTT::Start(float AutoInterval)
|
|||||||
mqttServer_setParameter(flowpostprocessing->GetNumbers(), keepAlive, roundInterval);
|
mqttServer_setParameter(flowpostprocessing->GetNumbers(), keepAlive, roundInterval);
|
||||||
|
|
||||||
bool MQTTConfigCheck = MQTT_Configure(uri, clientname, user, password, maintopic, LWT_TOPIC, LWT_CONNECTED,
|
bool MQTTConfigCheck = MQTT_Configure(uri, clientname, user, password, maintopic, LWT_TOPIC, LWT_CONNECTED,
|
||||||
LWT_DISCONNECTED, keepAlive, SetRetainFlag, (void *)&GotConnected);
|
LWT_DISCONNECTED, caCertFilename, clientCertFilename, clientKeyFilename,
|
||||||
|
keepAlive, SetRetainFlag, (void *)&GotConnected);
|
||||||
|
|
||||||
if (!MQTTConfigCheck) {
|
if (!MQTTConfigCheck) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ protected:
|
|||||||
std::string OldValue;
|
std::string OldValue;
|
||||||
ClassFlowPostProcessing* flowpostprocessing;
|
ClassFlowPostProcessing* flowpostprocessing;
|
||||||
std::string user, password;
|
std::string user, password;
|
||||||
|
std::string caCertFilename, clientCertFilename, clientKeyFilename;
|
||||||
bool SetRetainFlag;
|
bool SetRetainFlag;
|
||||||
int keepAlive; // Seconds
|
int keepAlive; // Seconds
|
||||||
float roundInterval; // Minutes
|
float roundInterval; // Minutes
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
@@ -284,6 +285,7 @@ void ClassFlowPostProcessing::SavePreValue()
|
|||||||
struct tm* timeinfo = localtime(&NUMBERS[j]->lastvalue);
|
struct tm* timeinfo = localtime(&NUMBERS[j]->lastvalue);
|
||||||
strftime(buffer, 80, PREVALUE_TIME_FORMAT_OUTPUT, timeinfo);
|
strftime(buffer, 80, PREVALUE_TIME_FORMAT_OUTPUT, timeinfo);
|
||||||
NUMBERS[j]->timeStamp = std::string(buffer);
|
NUMBERS[j]->timeStamp = std::string(buffer);
|
||||||
|
NUMBERS[j]->timeStampTimeUTC = NUMBERS[j]->lastvalue;
|
||||||
// ESP_LOGD(TAG, "SaverPreValue %d, Value: %f, Nachkomma %d", j, NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma);
|
// ESP_LOGD(TAG, "SaverPreValue %d, Value: %f, Nachkomma %d", j, NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma);
|
||||||
|
|
||||||
_zw = NUMBERS[j]->name + "\t" + NUMBERS[j]->timeStamp + "\t" + RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma) + "\n";
|
_zw = NUMBERS[j]->name + "\t" + NUMBERS[j]->timeStamp + "\t" + RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma) + "\n";
|
||||||
@@ -725,6 +727,130 @@ string ClassFlowPostProcessing::ShiftDecimal(string in, int _decShift){
|
|||||||
return zw;
|
return zw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float wrapAround(float val)
|
||||||
|
{
|
||||||
|
return fmod(val, 10.);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks whether val is in the range [min, max]
|
||||||
|
*
|
||||||
|
* Note, this function also handles the wrap around case,
|
||||||
|
* in which min could be larger than max in case of
|
||||||
|
* a circular range
|
||||||
|
*
|
||||||
|
* @param val The value to be checked
|
||||||
|
* @param min Minimal bound of the range
|
||||||
|
* @param max Maximum bound of the range
|
||||||
|
* @return True, if val is in the range
|
||||||
|
*/
|
||||||
|
bool inRange(float val, float min, float max)
|
||||||
|
{
|
||||||
|
assert(min >= 0. && min < 10.0);
|
||||||
|
assert(max >= 0. && max <= 10.0);
|
||||||
|
assert(val >= 0. && val < 10.0);
|
||||||
|
|
||||||
|
if (min <= max)
|
||||||
|
{
|
||||||
|
return min <= val && val <= max;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// e.g. between 8 and 2 (of the next round)
|
||||||
|
return (min <= val && val < 10.) || (0. <= val && val <= max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Synchronizes a potential misalignment between analog and digital part
|
||||||
|
*
|
||||||
|
* @param The current value assembled from digits (pre comma) and analogs (post comma)
|
||||||
|
* @param digitalPrecision The post-comma value of the last digit ([0...9])
|
||||||
|
* @param analogDigitalShift The value of the 0.1 analog, when the digital precision == 5 in [0, 9.9]
|
||||||
|
*
|
||||||
|
* We define 3 phases:
|
||||||
|
* - Pre transition: analog post comma < analogDigitalShift && not in transition
|
||||||
|
* - Transition: Digital Precision in range 1...9
|
||||||
|
* - Post transition: analog post comma > analogDigitalShift && not in transition
|
||||||
|
*
|
||||||
|
* @return The synchronized values as a string
|
||||||
|
*/
|
||||||
|
std::string syncDigitalAnalog(const std::string& value, const std::string& digitalPrecision,
|
||||||
|
double analogDigitalShift)
|
||||||
|
{
|
||||||
|
if (digitalPrecision.empty())
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto pos = value.find('.');
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// disassemble value into pre and post comma part
|
||||||
|
const auto preComma = value.substr(0, pos);
|
||||||
|
|
||||||
|
// memorize, to be able to assemble right numbers of leading zeros
|
||||||
|
const size_t nPreComma = preComma.size();
|
||||||
|
const auto postComma = value.substr(pos+1);
|
||||||
|
|
||||||
|
const float digitalPostComma = std::atof(digitalPrecision.c_str());
|
||||||
|
int digitalPreComma = std::atoi(preComma.c_str());
|
||||||
|
const float analogPostComma = std::atof(("0." + postComma).c_str());
|
||||||
|
|
||||||
|
// Determine phase
|
||||||
|
const bool inTransition = digitalPostComma > 0. && digitalPostComma < 10.;
|
||||||
|
const bool postTransition = !inTransition && inRange(analogPostComma*10., analogDigitalShift, wrapAround(analogDigitalShift + 5.));
|
||||||
|
const bool preTransition = !inTransition && inRange(analogPostComma*10., 0, analogDigitalShift);
|
||||||
|
|
||||||
|
|
||||||
|
if (inRange(analogDigitalShift, 0.5, 5.))
|
||||||
|
{
|
||||||
|
// late transition, last digit starts transition, when analog is between [0.5, 5)
|
||||||
|
if (inTransition || preTransition)
|
||||||
|
{
|
||||||
|
ESP_LOGD("syncDigitalAnalog", "Late digital transition. Increase digital value by 1.");
|
||||||
|
digitalPreComma += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (inRange(analogDigitalShift, 5., 9.5))
|
||||||
|
{
|
||||||
|
// early transition
|
||||||
|
if (postTransition && analogPostComma*10 > analogDigitalShift)
|
||||||
|
{
|
||||||
|
ESP_LOGD("syncDigitalAnalog", "Early digital transition. Decrease digital value by 1.");
|
||||||
|
digitalPreComma -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// transition has not finished, but we are already at the new cycle
|
||||||
|
// this also should handle hanging digits
|
||||||
|
if (inTransition && analogPostComma < 0.5) {
|
||||||
|
digitalPreComma += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// assemble result into string again, pad with zeros
|
||||||
|
auto preCommaNew = std::to_string(digitalPreComma);
|
||||||
|
for (size_t i = preCommaNew.size(); i < nPreComma; ++i)
|
||||||
|
preCommaNew = "0" + preCommaNew;
|
||||||
|
|
||||||
|
const std::string result = preCommaNew + "." + postComma;
|
||||||
|
|
||||||
|
#if debugSync
|
||||||
|
ESP_LOGD("syncDigitalAnalog", "result: %s", result.c_str());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
bool ClassFlowPostProcessing::doFlow(string zwtime)
|
bool ClassFlowPostProcessing::doFlow(string zwtime)
|
||||||
{
|
{
|
||||||
string result = "";
|
string result = "";
|
||||||
@@ -768,6 +894,8 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
|
|||||||
|
|
||||||
int previous_value = -1;
|
int previous_value = -1;
|
||||||
|
|
||||||
|
// ------------------- start processing analog values --------------------------//
|
||||||
|
|
||||||
if (NUMBERS[j]->analog_roi)
|
if (NUMBERS[j]->analog_roi)
|
||||||
{
|
{
|
||||||
NUMBERS[j]->ReturnRawValue = flowAnalog->getReadout(j, NUMBERS[j]->isExtendedResolution);
|
NUMBERS[j]->ReturnRawValue = flowAnalog->getReadout(j, NUMBERS[j]->isExtendedResolution);
|
||||||
@@ -781,19 +909,38 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
|
|||||||
#ifdef SERIAL_DEBUG
|
#ifdef SERIAL_DEBUG
|
||||||
ESP_LOGD(TAG, "After analog->getReadout: ReturnRaw %s", NUMBERS[j]->ReturnRawValue.c_str());
|
ESP_LOGD(TAG, "After analog->getReadout: ReturnRaw %s", NUMBERS[j]->ReturnRawValue.c_str());
|
||||||
#endif
|
#endif
|
||||||
if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi)
|
|
||||||
NUMBERS[j]->ReturnRawValue = "." + NUMBERS[j]->ReturnRawValue;
|
|
||||||
|
|
||||||
if (NUMBERS[j]->digit_roi)
|
// ----------------- start processing digital values --------------------------//
|
||||||
|
|
||||||
|
// we need the precision of the digital values to determine transition phase
|
||||||
|
std::string digitalPrecision = {"0"};
|
||||||
|
if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi) {
|
||||||
|
// we have nachkommad and vorkomman part!
|
||||||
|
|
||||||
|
std::string analogValues = NUMBERS[j]->ReturnRawValue;
|
||||||
|
std::string digitValues = flowDigit->getReadout(j, true, previous_value, NUMBERS[j]->analog_roi->ROI[0]->result_float, 0.);
|
||||||
|
|
||||||
|
if (flowDigit->getCNNType() != Digital)
|
||||||
|
{
|
||||||
|
// The digital type does not provide an extended resolution, so we cannot extract it
|
||||||
|
digitalPrecision = digitValues.substr(digitValues.size() - 1);
|
||||||
|
digitValues = digitValues.substr(0, digitValues.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
NUMBERS[j]->ReturnRawValue = digitValues + "." + analogValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (NUMBERS[j]->digit_roi && !NUMBERS[j]->analog_roi)
|
||||||
{
|
{
|
||||||
if (NUMBERS[j]->analog_roi)
|
NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, NUMBERS[j]->isExtendedResolution, previous_value); // Extended Resolution only if there are no analogue digits
|
||||||
NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, false, previous_value, NUMBERS[j]->analog_roi->ROI[0]->result_float, NUMBERS[j]->AnalogDigitalTransitionStart) + NUMBERS[j]->ReturnRawValue;
|
|
||||||
else
|
|
||||||
NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, NUMBERS[j]->isExtendedResolution, previous_value); // Extended Resolution only if there are no analogue digits
|
|
||||||
}
|
}
|
||||||
#ifdef SERIAL_DEBUG
|
#ifdef SERIAL_DEBUG
|
||||||
ESP_LOGD(TAG, "After digital->getReadout: ReturnRaw %s", NUMBERS[j]->ReturnRawValue.c_str());
|
ESP_LOGD(TAG, "After digital->getReadout: ReturnRaw %s", NUMBERS[j]->ReturnRawValue.c_str());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// ------------------ start corrections --------------------------//
|
||||||
|
|
||||||
NUMBERS[j]->ReturnRawValue = ShiftDecimal(NUMBERS[j]->ReturnRawValue, NUMBERS[j]->DecimalShift);
|
NUMBERS[j]->ReturnRawValue = ShiftDecimal(NUMBERS[j]->ReturnRawValue, NUMBERS[j]->DecimalShift);
|
||||||
|
|
||||||
#ifdef SERIAL_DEBUG
|
#ifdef SERIAL_DEBUG
|
||||||
@@ -826,6 +973,13 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
|
|||||||
continue; // there is no number because there is still an N.
|
continue; // there is no number because there is still an N.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi)
|
||||||
|
{
|
||||||
|
// Synchronize potential misalignment between analog and digital part
|
||||||
|
NUMBERS[j]->ReturnValue = syncDigitalAnalog(NUMBERS[j]->ReturnValue, digitalPrecision, NUMBERS[j]->AnalogDigitalTransitionStart);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SERIAL_DEBUG
|
#ifdef SERIAL_DEBUG
|
||||||
ESP_LOGD(TAG, "After findDelimiterPos: ReturnValue %s", NUMBERS[j]->ReturnRawValue.c_str());
|
ESP_LOGD(TAG, "After findDelimiterPos: ReturnValue %s", NUMBERS[j]->ReturnRawValue.c_str());
|
||||||
#endif
|
#endif
|
||||||
@@ -872,11 +1026,15 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
|
|||||||
+ ", preValue=" + std::to_string(NUMBERS[j]->PreValue)
|
+ ", preValue=" + std::to_string(NUMBERS[j]->PreValue)
|
||||||
+ ", preToll=" + std::to_string(NUMBERS[j]->PreValue-(2/pow(10, NUMBERS[j]->Nachkomma))));
|
+ ", preToll=" + std::to_string(NUMBERS[j]->PreValue-(2/pow(10, NUMBERS[j]->Nachkomma))));
|
||||||
}
|
}
|
||||||
// Include inaccuracy of 0.2 for isExtendedResolution.
|
|
||||||
if (NUMBERS[j]->Value >= (NUMBERS[j]->PreValue-(2/pow(10, NUMBERS[j]->Nachkomma))) && NUMBERS[j]->isExtendedResolution) {
|
// Include inaccuracy of 0.2 for isExtendedResolution.
|
||||||
NUMBERS[j]->Value = NUMBERS[j]->PreValue;
|
if ((NUMBERS[j]->Value >= (NUMBERS[j]->PreValue-(2/pow(10, NUMBERS[j]->Nachkomma))) && NUMBERS[j]->isExtendedResolution)
|
||||||
NUMBERS[j]->ReturnValue = to_string(NUMBERS[j]->PreValue);
|
// not extended resolution allows -1 on the lowest digit
|
||||||
} else {
|
|| (NUMBERS[j]->Value >= (NUMBERS[j]->PreValue-(1/pow(10, NUMBERS[j]->Nachkomma))) && !NUMBERS[j]->isExtendedResolution)) {
|
||||||
|
NUMBERS[j]->Value = NUMBERS[j]->PreValue;
|
||||||
|
NUMBERS[j]->ReturnValue = to_string(NUMBERS[j]->PreValue);
|
||||||
|
}
|
||||||
|
else {
|
||||||
NUMBERS[j]->ErrorMessageText = NUMBERS[j]->ErrorMessageText + "Neg. Rate - Read: " + zwvalue + " - Raw: " + NUMBERS[j]->ReturnRawValue + " - Pre: " + RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma) + " ";
|
NUMBERS[j]->ErrorMessageText = NUMBERS[j]->ErrorMessageText + "Neg. Rate - Read: " + zwvalue + " - Raw: " + NUMBERS[j]->ReturnRawValue + " - Pre: " + RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma) + " ";
|
||||||
NUMBERS[j]->Value = NUMBERS[j]->PreValue;
|
NUMBERS[j]->Value = NUMBERS[j]->PreValue;
|
||||||
NUMBERS[j]->ReturnValue = "";
|
NUMBERS[j]->ReturnValue = "";
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
|||||||
|
|
||||||
idf_component_register(SRCS ${app_sources}
|
idf_component_register(SRCS ${app_sources}
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "."
|
||||||
REQUIRES esp_timer tflite-lib jomjol_logfile fatfs sdmmc)
|
REQUIRES esp_timer esp-tflite-micro jomjol_logfile fatfs sdmmc vfs)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -27,10 +27,10 @@ extern "C" {
|
|||||||
#include <esp_timer.h>
|
#include <esp_timer.h>
|
||||||
#include "../../include/defines.h"
|
#include "../../include/defines.h"
|
||||||
|
|
||||||
|
|
||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
|
|
||||||
#include "esp_vfs_fat.h"
|
#include "esp_vfs_fat.h"
|
||||||
|
#include "../sdmmc_common.h"
|
||||||
|
|
||||||
static const char* TAG = "HELPER";
|
static const char* TAG = "HELPER";
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ unsigned int systemStatus = 0;
|
|||||||
|
|
||||||
sdmmc_cid_t SDCardCid;
|
sdmmc_cid_t SDCardCid;
|
||||||
sdmmc_csd_t SDCardCsd;
|
sdmmc_csd_t SDCardCsd;
|
||||||
|
bool SDCardIsMMC;
|
||||||
|
|
||||||
// #define DEBUG_DETAIL_ON
|
// #define DEBUG_DETAIL_ON
|
||||||
|
|
||||||
@@ -139,6 +139,7 @@ string getSDCardPartitionAllocationSize(){
|
|||||||
void SaveSDCardInfo(sdmmc_card_t* card) {
|
void SaveSDCardInfo(sdmmc_card_t* card) {
|
||||||
SDCardCid = card->cid;
|
SDCardCid = card->cid;
|
||||||
SDCardCsd = card->csd;
|
SDCardCsd = card->csd;
|
||||||
|
SDCardIsMMC = card->is_mmc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -674,7 +675,7 @@ struct SDCard_Manufacturer_database {
|
|||||||
|
|
||||||
/* Source: https://git.kernel.org/pub/scm/utils/mmc/mmc-utils.git/tree/lsmmc.c */
|
/* Source: https://git.kernel.org/pub/scm/utils/mmc/mmc-utils.git/tree/lsmmc.c */
|
||||||
/* SD Card Manufacturer Database */
|
/* SD Card Manufacturer Database */
|
||||||
struct SDCard_Manufacturer_database database[] = {
|
struct SDCard_Manufacturer_database sd_database[] = {
|
||||||
{
|
{
|
||||||
.type = "sd",
|
.type = "sd",
|
||||||
.id = 0x01,
|
.id = 0x01,
|
||||||
@@ -779,25 +780,124 @@ struct SDCard_Manufacturer_database database[] = {
|
|||||||
.type = "sd",
|
.type = "sd",
|
||||||
.id = 0x89,
|
.id = 0x89,
|
||||||
.manufacturer = "Unknown",
|
.manufacturer = "Unknown",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SDCard_Manufacturer_database mmc_database[] = {
|
||||||
|
{
|
||||||
|
.type = "mmc",
|
||||||
|
.id = 0x00,
|
||||||
|
.manufacturer = "SanDisk",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = "mmc",
|
||||||
|
.id = 0x02,
|
||||||
|
.manufacturer = "Kingston/SanDisk",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = "mmc",
|
||||||
|
.id = 0x03,
|
||||||
|
.manufacturer = "Toshiba",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = "mmc",
|
||||||
|
.id = 0x05,
|
||||||
|
.manufacturer = "Unknown",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = "mmc",
|
||||||
|
.id = 0x06,
|
||||||
|
.manufacturer = "Unknown",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = "mmc",
|
||||||
|
.id = 0x11,
|
||||||
|
.manufacturer = "Toshiba",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = "mmc",
|
||||||
|
.id = 0x13,
|
||||||
|
.manufacturer = "Micron",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = "mmc",
|
||||||
|
.id = 0x15,
|
||||||
|
.manufacturer = "Samsung/SanDisk/LG",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = "mmc",
|
||||||
|
.id = 0x37,
|
||||||
|
.manufacturer = "KingMax",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = "mmc",
|
||||||
|
.id = 0x44,
|
||||||
|
.manufacturer = "ATP",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = "mmc",
|
||||||
|
.id = 0x45,
|
||||||
|
.manufacturer = "SanDisk Corporation",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = "mmc",
|
||||||
|
.id = 0x2c,
|
||||||
|
.manufacturer = "Kingston",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = "mmc",
|
||||||
|
.id = 0x70,
|
||||||
|
.manufacturer = "Kingston",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = "mmc",
|
||||||
|
.id = 0xfe,
|
||||||
|
.manufacturer = "Micron",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/* Parse SD Card Manufacturer Database */
|
/* Parse SD Card Manufacturer Database */
|
||||||
string SDCardParseManufacturerIDs(int id)
|
string SDCardParseManufacturerIDs(int id)
|
||||||
{
|
{
|
||||||
unsigned int id_cnt = sizeof(database) / sizeof(struct SDCard_Manufacturer_database);
|
if (SDCardIsMMC)
|
||||||
|
{
|
||||||
|
unsigned int id_cnt = sizeof(mmc_database) / sizeof(struct SDCard_Manufacturer_database);
|
||||||
|
string ret_val = "";
|
||||||
|
|
||||||
|
for (int i = 0; i < id_cnt; i++)
|
||||||
|
{
|
||||||
|
if (mmc_database[i].id == id)
|
||||||
|
{
|
||||||
|
return mmc_database[i].manufacturer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret_val = "ID unknown (not in DB)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsigned int id_cnt = sizeof(sd_database) / sizeof(struct SDCard_Manufacturer_database);
|
||||||
string ret_val = "";
|
string ret_val = "";
|
||||||
|
|
||||||
for (int i = 0; i < id_cnt; i++) {
|
for (int i = 0; i < id_cnt; i++)
|
||||||
if (database[i].id == id) {
|
{
|
||||||
return database[i].manufacturer;
|
if (sd_database[i].id == id)
|
||||||
}
|
{
|
||||||
else {
|
return sd_database[i].manufacturer;
|
||||||
ret_val = "ID unknown (not in DB)";
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
|
ret_val = "ID unknown (not in DB)";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret_val;
|
return ret_val;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "sdmmc_cmd.h"
|
#include "sdmmc_cmd.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
|||||||
651
code/components/jomjol_helper/sdcard_init.c
Normal file
651
code/components/jomjol_helper/sdcard_init.c
Normal file
@@ -0,0 +1,651 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "sdcard_init.h"
|
||||||
|
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "ffconf.h"
|
||||||
|
#include "esp_compiler.h"
|
||||||
|
#include "esp_vfs.h"
|
||||||
|
#include "vfs_fat_internal.h"
|
||||||
|
#include "diskio_impl.h"
|
||||||
|
#include "diskio_sdmmc.h"
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
#include "driver/sdmmc_defs.h"
|
||||||
|
|
||||||
|
#if SOC_SDMMC_HOST_SUPPORTED
|
||||||
|
#include "driver/sdmmc_host.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static sdmmc_card_t* s_cards[FF_VOLUMES] = { NULL };
|
||||||
|
static bool s_disk_status_check_en[FF_VOLUMES] = { };
|
||||||
|
|
||||||
|
static const char* TAG = "sdcard_init";
|
||||||
|
|
||||||
|
#define CHECK_EXECUTE_RESULT(err, str) do { \
|
||||||
|
if ((err) !=ESP_OK) { \
|
||||||
|
ESP_LOGE(TAG, str" (0x%x).", err); \
|
||||||
|
goto cleanup; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
typedef struct vfs_fat_sd_ctx_t {
|
||||||
|
BYTE pdrv; //Drive number that is mounted
|
||||||
|
esp_vfs_fat_mount_config_t mount_config; //Mount configuration
|
||||||
|
FATFS *fs; //FAT structure pointer that is registered
|
||||||
|
sdmmc_card_t *card; //Card info
|
||||||
|
char *base_path; //Path where partition is registered
|
||||||
|
} vfs_fat_sd_ctx_t;
|
||||||
|
|
||||||
|
static vfs_fat_sd_ctx_t *s_ctx[FF_VOLUMES] = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This `s_saved_ctx_id` is only used by `esp_vfs_fat_sdmmc_unmount`, which is deprecated.
|
||||||
|
* This variable together with `esp_vfs_fat_sdmmc_unmount` should be removed in next major version
|
||||||
|
*/
|
||||||
|
static uint32_t s_saved_ctx_id = FF_VOLUMES;
|
||||||
|
|
||||||
|
|
||||||
|
static void call_host_deinit_mh(const sdmmc_host_t *host_config);
|
||||||
|
static esp_err_t partition_card_mh(const esp_vfs_fat_mount_config_t *mount_config, const char *drv, sdmmc_card_t *card, BYTE pdrv);
|
||||||
|
|
||||||
|
|
||||||
|
//Check if SD/MMC card is present
|
||||||
|
static DSTATUS ff_sdmmc_card_available_mh(BYTE pdrv)
|
||||||
|
{
|
||||||
|
sdmmc_card_t* card = s_cards[pdrv];
|
||||||
|
assert(card);
|
||||||
|
esp_err_t err = sdmmc_get_status(card);
|
||||||
|
|
||||||
|
if (unlikely(err != ESP_OK)) {
|
||||||
|
ESP_LOGE(TAG, "Check status failed (0x%x)", err);
|
||||||
|
return STA_NOINIT;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ff_sdmmc_status() and ff_sdmmc_initialize() return STA_NOINIT when sdmmc_get_status()
|
||||||
|
* fails. This error value is checked throughout the FATFS code.
|
||||||
|
* Both functions return 0 on success.
|
||||||
|
*/
|
||||||
|
DSTATUS ff_sdmmc_initialize_mh (BYTE pdrv)
|
||||||
|
{
|
||||||
|
return ff_sdmmc_card_available_mh(pdrv);
|
||||||
|
}
|
||||||
|
|
||||||
|
DSTATUS ff_sdmmc_status_mh(BYTE pdrv)
|
||||||
|
{
|
||||||
|
if (s_disk_status_check_en[pdrv]) {
|
||||||
|
return ff_sdmmc_card_available_mh(pdrv);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DRESULT ff_sdmmc_read_mh (BYTE pdrv, BYTE* buff, DWORD sector, UINT count)
|
||||||
|
{
|
||||||
|
sdmmc_card_t* card = s_cards[pdrv];
|
||||||
|
assert(card);
|
||||||
|
esp_err_t err = sdmmc_read_sectors(card, buff, sector, count);
|
||||||
|
if (unlikely(err != ESP_OK)) {
|
||||||
|
ESP_LOGE(TAG, "sdmmc_read_blocks failed (%d)", err);
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
DRESULT ff_sdmmc_write_mh (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count)
|
||||||
|
{
|
||||||
|
sdmmc_card_t* card = s_cards[pdrv];
|
||||||
|
assert(card);
|
||||||
|
esp_err_t err = sdmmc_write_sectors(card, buff, sector, count);
|
||||||
|
if (unlikely(err != ESP_OK)) {
|
||||||
|
ESP_LOGE(TAG, "sdmmc_write_blocks failed (%d)", err);
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if FF_USE_TRIM
|
||||||
|
DRESULT ff_sdmmc_trim_mh (BYTE pdrv, DWORD start_sector, DWORD sector_count)
|
||||||
|
{
|
||||||
|
sdmmc_card_t* card = s_cards[pdrv];
|
||||||
|
assert(card);
|
||||||
|
sdmmc_erase_arg_t arg;
|
||||||
|
|
||||||
|
arg = sdmmc_can_discard(card) == ESP_OK ? SDMMC_DISCARD_ARG : SDMMC_ERASE_ARG;
|
||||||
|
esp_err_t err = sdmmc_erase_sectors(card, start_sector, sector_count, arg);
|
||||||
|
if (unlikely(err != ESP_OK)) {
|
||||||
|
ESP_LOGE(TAG, "sdmmc_erase_sectors failed (%d)", err);
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
#endif //FF_USE_TRIM
|
||||||
|
|
||||||
|
DRESULT ff_sdmmc_ioctl_mh (BYTE pdrv, BYTE cmd, void* buff)
|
||||||
|
{
|
||||||
|
sdmmc_card_t* card = s_cards[pdrv];
|
||||||
|
assert(card);
|
||||||
|
switch(cmd) {
|
||||||
|
case CTRL_SYNC:
|
||||||
|
return RES_OK;
|
||||||
|
case GET_SECTOR_COUNT:
|
||||||
|
*((DWORD*) buff) = card->csd.capacity;
|
||||||
|
return RES_OK;
|
||||||
|
case GET_SECTOR_SIZE:
|
||||||
|
*((WORD*) buff) = card->csd.sector_size;
|
||||||
|
return RES_OK;
|
||||||
|
case GET_BLOCK_SIZE:
|
||||||
|
return RES_ERROR;
|
||||||
|
#if FF_USE_TRIM
|
||||||
|
case CTRL_TRIM:
|
||||||
|
if (sdmmc_can_trim(card) != ESP_OK) {
|
||||||
|
return RES_PARERR;
|
||||||
|
}
|
||||||
|
return ff_sdmmc_trim_mh (pdrv, *((DWORD*)buff), //start_sector
|
||||||
|
(*((DWORD*)buff + 1) - *((DWORD*)buff) + 1)); //sector_count
|
||||||
|
#endif //FF_USE_TRIM
|
||||||
|
}
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ff_sdmmc_set_disk_status_check_mh(BYTE pdrv, bool enable)
|
||||||
|
{
|
||||||
|
s_disk_status_check_en[pdrv] = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ff_diskio_register_sdmmc_mh(BYTE pdrv, sdmmc_card_t* card)
|
||||||
|
{
|
||||||
|
static const ff_diskio_impl_t sdmmc_impl = {
|
||||||
|
.init = &ff_sdmmc_initialize_mh,
|
||||||
|
.status = &ff_sdmmc_status_mh,
|
||||||
|
.read = &ff_sdmmc_read_mh,
|
||||||
|
.write = &ff_sdmmc_write_mh,
|
||||||
|
.ioctl = &ff_sdmmc_ioctl_mh
|
||||||
|
};
|
||||||
|
s_cards[pdrv] = card;
|
||||||
|
s_disk_status_check_en[pdrv] = false;
|
||||||
|
ff_diskio_register(pdrv, &sdmmc_impl);
|
||||||
|
}
|
||||||
|
|
||||||
|
BYTE ff_diskio_get_pdrv_card_mh(const sdmmc_card_t* card)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < FF_VOLUMES; i++) {
|
||||||
|
if (card == s_cards[i]) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static bool s_get_context_id_by_card_mh(const sdmmc_card_t *card, uint32_t *out_id)
|
||||||
|
{
|
||||||
|
vfs_fat_sd_ctx_t *p_ctx = NULL;
|
||||||
|
for (int i = 0; i < FF_VOLUMES; i++) {
|
||||||
|
p_ctx = s_ctx[i];
|
||||||
|
if (p_ctx) {
|
||||||
|
if (p_ctx->card == card) {
|
||||||
|
*out_id = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t s_get_unused_context_id_mh(void)
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < FF_VOLUMES; i++) {
|
||||||
|
if (!s_ctx[i]) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FF_VOLUMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t mount_prepare_mem_mh(const char *base_path, BYTE *out_pdrv, char **out_dup_path, sdmmc_card_t** out_card)
|
||||||
|
{
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
char* dup_path = NULL;
|
||||||
|
sdmmc_card_t* card = NULL;
|
||||||
|
|
||||||
|
// connect SDMMC driver to FATFS
|
||||||
|
BYTE pdrv = FF_DRV_NOT_USED;
|
||||||
|
|
||||||
|
if (ff_diskio_get_drive(&pdrv) != ESP_OK || pdrv == FF_DRV_NOT_USED) {
|
||||||
|
ESP_LOGD(TAG, "the maximum count of volumes is already mounted");
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
// not using ff_memalloc here, as allocation in internal RAM is preferred
|
||||||
|
card = (sdmmc_card_t*)malloc(sizeof(sdmmc_card_t));
|
||||||
|
|
||||||
|
if (card == NULL) {
|
||||||
|
ESP_LOGD(TAG, "could not locate new sdmmc_card_t");
|
||||||
|
err = ESP_ERR_NO_MEM;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
dup_path = strdup(base_path);
|
||||||
|
|
||||||
|
if(!dup_path){
|
||||||
|
ESP_LOGD(TAG, "could not copy base_path");
|
||||||
|
err = ESP_ERR_NO_MEM;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_card = card;
|
||||||
|
*out_pdrv = pdrv;
|
||||||
|
*out_dup_path = dup_path;
|
||||||
|
return ESP_OK;
|
||||||
|
cleanup:
|
||||||
|
free(card);
|
||||||
|
free(dup_path);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t s_f_mount_mh(sdmmc_card_t *card, FATFS *fs, const char *drv, uint8_t pdrv, const esp_vfs_fat_mount_config_t *mount_config)
|
||||||
|
{
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
FRESULT res = f_mount(fs, drv, 1);
|
||||||
|
if (res != FR_OK) {
|
||||||
|
err = ESP_FAIL;
|
||||||
|
ESP_LOGW(TAG, "failed to mount card (%d)", res);
|
||||||
|
|
||||||
|
bool need_mount_again = (res == FR_NO_FILESYSTEM || res == FR_INT_ERR) && mount_config->format_if_mount_failed;
|
||||||
|
|
||||||
|
if (!need_mount_again) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = partition_card_mh(mount_config, drv, card, pdrv);
|
||||||
|
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGW(TAG, "mounting again");
|
||||||
|
res = f_mount(fs, drv, 0);
|
||||||
|
|
||||||
|
if (res != FR_OK) {
|
||||||
|
err = ESP_FAIL;
|
||||||
|
ESP_LOGD(TAG, "f_mount failed after formatting (%d)", res);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t mount_to_vfs_fat_mh(const esp_vfs_fat_mount_config_t *mount_config, sdmmc_card_t *card, uint8_t pdrv, const char *base_path, FATFS **out_fs)
|
||||||
|
{
|
||||||
|
FATFS *fs = NULL;
|
||||||
|
esp_err_t err;
|
||||||
|
ff_diskio_register_sdmmc_mh(pdrv, card);
|
||||||
|
ff_sdmmc_set_disk_status_check_mh(pdrv, mount_config->disk_status_check_enable);
|
||||||
|
ESP_LOGD(TAG, "using pdrv=%i", pdrv);
|
||||||
|
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||||
|
|
||||||
|
// connect FATFS to VFS
|
||||||
|
err = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs);
|
||||||
|
*out_fs = fs;
|
||||||
|
|
||||||
|
if (err == ESP_ERR_INVALID_STATE) {
|
||||||
|
// it's okay, already registered with VFS
|
||||||
|
} else if (err != ESP_OK) {
|
||||||
|
ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", err);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to mount partition
|
||||||
|
err = s_f_mount_mh(card, fs, drv, pdrv, mount_config);
|
||||||
|
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (fs) {
|
||||||
|
f_mount(NULL, drv, 0);
|
||||||
|
}
|
||||||
|
esp_vfs_fat_unregister_path(base_path);
|
||||||
|
ff_diskio_unregister(pdrv);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t partition_card_mh(const esp_vfs_fat_mount_config_t *mount_config, const char *drv, sdmmc_card_t *card, BYTE pdrv)
|
||||||
|
{
|
||||||
|
FRESULT res = FR_OK;
|
||||||
|
esp_err_t err;
|
||||||
|
const size_t workbuf_size = 4096;
|
||||||
|
void* workbuf = NULL;
|
||||||
|
ESP_LOGW(TAG, "partitioning card");
|
||||||
|
|
||||||
|
workbuf = ff_memalloc(workbuf_size);
|
||||||
|
|
||||||
|
if (workbuf == NULL) {
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
LBA_t plist[] = {100, 0, 0, 0};
|
||||||
|
res = f_fdisk(pdrv, plist, workbuf);
|
||||||
|
|
||||||
|
if (res != FR_OK) {
|
||||||
|
err = ESP_FAIL;
|
||||||
|
ESP_LOGD(TAG, "f_fdisk failed (%d)", res);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(card->csd.sector_size, mount_config->allocation_unit_size);
|
||||||
|
|
||||||
|
ESP_LOGW(TAG, "formatting card, allocation unit size=%d", alloc_unit_size);
|
||||||
|
const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, alloc_unit_size};
|
||||||
|
res = f_mkfs(drv, &opt, workbuf, workbuf_size);
|
||||||
|
|
||||||
|
if (res != FR_OK) {
|
||||||
|
err = ESP_FAIL;
|
||||||
|
ESP_LOGD(TAG, "f_mkfs failed (%d)", res);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(workbuf);
|
||||||
|
return ESP_OK;
|
||||||
|
fail:
|
||||||
|
free(workbuf);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if SOC_SDMMC_HOST_SUPPORTED
|
||||||
|
static esp_err_t init_sdmmc_host_mh(int slot, const void *slot_config, int *out_slot)
|
||||||
|
{
|
||||||
|
*out_slot = slot;
|
||||||
|
return sdmmc_host_init_slot(slot, (const sdmmc_slot_config_t*) slot_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_vfs_fat_sdmmc_mount_mh(const char* base_path, const sdmmc_host_t* host_config, const void* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card)
|
||||||
|
{
|
||||||
|
esp_err_t err;
|
||||||
|
vfs_fat_sd_ctx_t *ctx = NULL;
|
||||||
|
uint32_t ctx_id = FF_VOLUMES;
|
||||||
|
FATFS *fs = NULL;
|
||||||
|
int card_handle = -1; //uninitialized
|
||||||
|
sdmmc_card_t* card = NULL;
|
||||||
|
BYTE pdrv = FF_DRV_NOT_USED;
|
||||||
|
char* dup_path = NULL;
|
||||||
|
bool host_inited = false;
|
||||||
|
|
||||||
|
err = mount_prepare_mem_mh(base_path, &pdrv, &dup_path, &card);
|
||||||
|
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "mount_prepare failed");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = (*host_config->init)();
|
||||||
|
CHECK_EXECUTE_RESULT(err, "host init failed");
|
||||||
|
//deinit() needs to be called to revert the init
|
||||||
|
host_inited = true;
|
||||||
|
//If this failed (indicated by card_handle != -1), slot deinit needs to called()
|
||||||
|
//leave card_handle as is to indicate that (though slot deinit not implemented yet.
|
||||||
|
err = init_sdmmc_host_mh(host_config->slot, slot_config, &card_handle);
|
||||||
|
CHECK_EXECUTE_RESULT(err, "slot init failed");
|
||||||
|
|
||||||
|
// probe and initialize card
|
||||||
|
err = sdmmc_card_init(host_config, card);
|
||||||
|
CHECK_EXECUTE_RESULT(err, "sdmmc_card_init failed");
|
||||||
|
|
||||||
|
err = mount_to_vfs_fat_mh(mount_config, card, pdrv, dup_path, &fs);
|
||||||
|
CHECK_EXECUTE_RESULT(err, "mount_to_vfs failed");
|
||||||
|
|
||||||
|
if (out_card != NULL) {
|
||||||
|
*out_card = card;
|
||||||
|
}
|
||||||
|
|
||||||
|
//For deprecation backward compatibility
|
||||||
|
if (s_saved_ctx_id == FF_VOLUMES) {
|
||||||
|
s_saved_ctx_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = calloc(sizeof(vfs_fat_sd_ctx_t), 1);
|
||||||
|
|
||||||
|
if (!ctx) {
|
||||||
|
CHECK_EXECUTE_RESULT(ESP_ERR_NO_MEM, "no mem");
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->pdrv = pdrv;
|
||||||
|
memcpy(&ctx->mount_config, mount_config, sizeof(esp_vfs_fat_mount_config_t));
|
||||||
|
ctx->card = card;
|
||||||
|
ctx->base_path = dup_path;
|
||||||
|
ctx->fs = fs;
|
||||||
|
ctx_id = s_get_unused_context_id_mh();
|
||||||
|
assert(ctx_id != FF_VOLUMES);
|
||||||
|
s_ctx[ctx_id] = ctx;
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
cleanup:
|
||||||
|
if (host_inited) {
|
||||||
|
call_host_deinit_mh(host_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(card);
|
||||||
|
free(dup_path);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static esp_err_t init_sdspi_host_mh(int slot, const void *slot_config, int *out_slot)
|
||||||
|
{
|
||||||
|
esp_err_t err = sdspi_host_init_device((const sdspi_device_config_t*)slot_config, out_slot);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG,
|
||||||
|
"Failed to attach sdspi device onto an SPI bus (rc=0x%x), please initialize the \
|
||||||
|
bus first and check the device parameters."
|
||||||
|
, err);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_vfs_fat_sdspi_mount_mh(const char* base_path, const sdmmc_host_t* host_config_input, const sdspi_device_config_t* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card)
|
||||||
|
{
|
||||||
|
const sdmmc_host_t* host_config = host_config_input;
|
||||||
|
esp_err_t err;
|
||||||
|
vfs_fat_sd_ctx_t *ctx = NULL;
|
||||||
|
uint32_t ctx_id = FF_VOLUMES;
|
||||||
|
FATFS *fs = NULL;
|
||||||
|
int card_handle = -1; //uninitialized
|
||||||
|
bool host_inited = false;
|
||||||
|
BYTE pdrv = FF_DRV_NOT_USED;
|
||||||
|
sdmmc_card_t* card = NULL;
|
||||||
|
char* dup_path = NULL;
|
||||||
|
|
||||||
|
err = mount_prepare_mem_mh(base_path, &pdrv, &dup_path, &card);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "mount_prepare failed");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
//the init() function is usually empty, doesn't require any deinit to revert it
|
||||||
|
err = (*host_config->init)();
|
||||||
|
CHECK_EXECUTE_RESULT(err, "host init failed");
|
||||||
|
|
||||||
|
err = init_sdspi_host_mh(host_config->slot, slot_config, &card_handle);
|
||||||
|
CHECK_EXECUTE_RESULT(err, "slot init failed");
|
||||||
|
//Set `host_inited` to true to indicate that host_config->deinit() needs
|
||||||
|
//to be called to revert `init_sdspi_host`
|
||||||
|
host_inited = true;
|
||||||
|
|
||||||
|
//The `slot` argument inside host_config should be replaced by the SD SPI handled returned
|
||||||
|
//above. But the input pointer is const, so create a new variable.
|
||||||
|
|
||||||
|
sdmmc_host_t new_config;
|
||||||
|
|
||||||
|
if (card_handle != host_config->slot) {
|
||||||
|
new_config = *host_config_input;
|
||||||
|
host_config = &new_config;
|
||||||
|
new_config.slot = card_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// probe and initialize card
|
||||||
|
err = sdmmc_card_init(host_config, card);
|
||||||
|
CHECK_EXECUTE_RESULT(err, "sdmmc_card_init failed");
|
||||||
|
|
||||||
|
err = mount_to_vfs_fat_mh(mount_config, card, pdrv, dup_path, &fs);
|
||||||
|
CHECK_EXECUTE_RESULT(err, "mount_to_vfs failed");
|
||||||
|
|
||||||
|
if (out_card != NULL) {
|
||||||
|
*out_card = card;
|
||||||
|
}
|
||||||
|
|
||||||
|
//For deprecation backward compatibility
|
||||||
|
if (s_saved_ctx_id == FF_VOLUMES) {
|
||||||
|
s_saved_ctx_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = calloc(sizeof(vfs_fat_sd_ctx_t), 1);
|
||||||
|
|
||||||
|
if (!ctx) {
|
||||||
|
CHECK_EXECUTE_RESULT(ESP_ERR_NO_MEM, "no mem");
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->pdrv = pdrv;
|
||||||
|
memcpy(&ctx->mount_config, mount_config, sizeof(esp_vfs_fat_mount_config_t));
|
||||||
|
ctx->card = card;
|
||||||
|
ctx->base_path = dup_path;
|
||||||
|
ctx->fs = fs;
|
||||||
|
ctx_id = s_get_unused_context_id_mh();
|
||||||
|
assert(ctx_id != FF_VOLUMES);
|
||||||
|
s_ctx[ctx_id] = ctx;
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (host_inited) {
|
||||||
|
call_host_deinit_mh(host_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(card);
|
||||||
|
free(dup_path);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void call_host_deinit_mh(const sdmmc_host_t *host_config)
|
||||||
|
{
|
||||||
|
if (host_config->flags & SDMMC_HOST_FLAG_DEINIT_ARG) {
|
||||||
|
host_config->deinit_p(host_config->slot);
|
||||||
|
} else {
|
||||||
|
host_config->deinit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t unmount_card_core_mh(const char *base_path, sdmmc_card_t *card)
|
||||||
|
{
|
||||||
|
BYTE pdrv = ff_diskio_get_pdrv_card_mh(card);
|
||||||
|
|
||||||
|
if (pdrv == 0xff) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
// unmount
|
||||||
|
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||||
|
f_mount(0, drv, 0);
|
||||||
|
// release SD driver
|
||||||
|
ff_diskio_unregister(pdrv);
|
||||||
|
|
||||||
|
call_host_deinit_mh(&card->host);
|
||||||
|
free(card);
|
||||||
|
|
||||||
|
esp_err_t err = esp_vfs_fat_unregister_path(base_path);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_vfs_fat_sdmmc_unmount_mh(void)
|
||||||
|
{
|
||||||
|
esp_err_t err = unmount_card_core_mh(s_ctx[s_saved_ctx_id]->base_path, s_ctx[s_saved_ctx_id]->card);
|
||||||
|
free(s_ctx[s_saved_ctx_id]);
|
||||||
|
s_ctx[s_saved_ctx_id] = NULL;
|
||||||
|
s_saved_ctx_id = FF_VOLUMES;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_vfs_fat_sdcard_unmount_mh(const char *base_path, sdmmc_card_t *card)
|
||||||
|
{
|
||||||
|
uint32_t id = FF_VOLUMES;
|
||||||
|
bool found = s_get_context_id_by_card_mh(card, &id);
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(s_ctx[id]);
|
||||||
|
s_ctx[id] = NULL;
|
||||||
|
|
||||||
|
esp_err_t err = unmount_card_core_mh(base_path, card);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_vfs_fat_sdcard_format_mh(const char *base_path, sdmmc_card_t *card)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
|
||||||
|
if (!card) {
|
||||||
|
ESP_LOGE(TAG, "card not initialized");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
BYTE pdrv = ff_diskio_get_pdrv_card_mh(card);
|
||||||
|
|
||||||
|
if (pdrv == 0xff) {
|
||||||
|
ESP_LOGE(TAG, "card driver not registered");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t workbuf_size = 4096;
|
||||||
|
void *workbuf = ff_memalloc(workbuf_size);
|
||||||
|
|
||||||
|
if (workbuf == NULL) {
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
//unmount
|
||||||
|
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||||
|
f_mount(0, drv, 0);
|
||||||
|
|
||||||
|
//format
|
||||||
|
uint32_t id = FF_VOLUMES;
|
||||||
|
bool found = s_get_context_id_by_card_mh(card, &id);
|
||||||
|
assert(found);
|
||||||
|
size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(card->csd.sector_size, s_ctx[id]->mount_config.allocation_unit_size);
|
||||||
|
ESP_LOGI(TAG, "Formatting card, allocation unit size=%d", alloc_unit_size);
|
||||||
|
const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, alloc_unit_size};
|
||||||
|
FRESULT res = f_mkfs(drv, &opt, workbuf, workbuf_size);
|
||||||
|
free(workbuf);
|
||||||
|
|
||||||
|
if (res != FR_OK) {
|
||||||
|
ret = ESP_FAIL;
|
||||||
|
ESP_LOGD(TAG, "f_mkfs failed (%d)", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
//mount back
|
||||||
|
esp_err_t err = s_f_mount_mh(card, s_ctx[id]->fs, drv, pdrv, &s_ctx[id]->mount_config);
|
||||||
|
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
unmount_card_core_mh(base_path, card);
|
||||||
|
ESP_LOGE(TAG, "failed to format, resources recycled, please mount again");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
111
code/components/jomjol_helper/sdcard_init.h
Normal file
111
code/components/jomjol_helper/sdcard_init.h
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "sdmmc_cmd.h"
|
||||||
|
#include "driver/sdmmc_types.h"
|
||||||
|
#include "driver/sdspi_host.h"
|
||||||
|
#include "ff.h"
|
||||||
|
#include "esp_vfs_fat.h"
|
||||||
|
#include "wear_levelling.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convenience function to get FAT filesystem on SD card registered in VFS
|
||||||
|
*
|
||||||
|
* This is an all-in-one function which does the following:
|
||||||
|
* - initializes SDMMC driver or SPI driver with configuration in host_config
|
||||||
|
* - initializes SD card with configuration in slot_config
|
||||||
|
* - mounts FAT partition on SD card using FATFS library, with configuration in mount_config
|
||||||
|
* - registers FATFS library with VFS, with prefix given by base_prefix variable
|
||||||
|
*
|
||||||
|
* This function is intended to make example code more compact.
|
||||||
|
* For real world applications, developers should implement the logic of
|
||||||
|
* probing SD card, locating and mounting partition, and registering FATFS in VFS,
|
||||||
|
* with proper error checking and handling of exceptional conditions.
|
||||||
|
*
|
||||||
|
* @note Use this API to mount a card through SDSPI is deprecated. Please call
|
||||||
|
* `esp_vfs_fat_sdspi_mount()` instead for that case.
|
||||||
|
*
|
||||||
|
* @param base_path path where partition should be registered (e.g. "/sdcard")
|
||||||
|
* @param host_config Pointer to structure describing SDMMC host. When using
|
||||||
|
* SDMMC peripheral, this structure can be initialized using
|
||||||
|
* SDMMC_HOST_DEFAULT() macro. When using SPI peripheral,
|
||||||
|
* this structure can be initialized using SDSPI_HOST_DEFAULT()
|
||||||
|
* macro.
|
||||||
|
* @param slot_config Pointer to structure with slot configuration.
|
||||||
|
* For SDMMC peripheral, pass a pointer to sdmmc_slot_config_t
|
||||||
|
* structure initialized using SDMMC_SLOT_CONFIG_DEFAULT.
|
||||||
|
* @param mount_config pointer to structure with extra parameters for mounting FATFS
|
||||||
|
* @param[out] out_card if not NULL, pointer to the card information structure will be returned via this argument
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called
|
||||||
|
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||||
|
* - ESP_FAIL if partition can not be mounted
|
||||||
|
* - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers
|
||||||
|
*/
|
||||||
|
esp_err_t esp_vfs_fat_sdmmc_mount_mh(const char* base_path, const sdmmc_host_t* host_config, const void* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convenience function to get FAT filesystem on SD card registered in VFS
|
||||||
|
*
|
||||||
|
* This is an all-in-one function which does the following:
|
||||||
|
* - initializes an SPI Master device based on the SPI Master driver with configuration in
|
||||||
|
* slot_config, and attach it to an initialized SPI bus.
|
||||||
|
* - initializes SD card with configuration in host_config_input
|
||||||
|
* - mounts FAT partition on SD card using FATFS library, with configuration in mount_config
|
||||||
|
* - registers FATFS library with VFS, with prefix given by base_prefix variable
|
||||||
|
*
|
||||||
|
* This function is intended to make example code more compact.
|
||||||
|
* For real world applications, developers should implement the logic of
|
||||||
|
* probing SD card, locating and mounting partition, and registering FATFS in VFS,
|
||||||
|
* with proper error checking and handling of exceptional conditions.
|
||||||
|
*
|
||||||
|
* @note This function try to attach the new SD SPI device to the bus specified in host_config.
|
||||||
|
* Make sure the SPI bus specified in `host_config->slot` have been initialized by
|
||||||
|
* `spi_bus_initialize()` before.
|
||||||
|
*
|
||||||
|
* @param base_path path where partition should be registered (e.g. "/sdcard")
|
||||||
|
* @param host_config_input Pointer to structure describing SDMMC host. This structure can be
|
||||||
|
* initialized using SDSPI_HOST_DEFAULT() macro.
|
||||||
|
* @param slot_config Pointer to structure with slot configuration.
|
||||||
|
* For SPI peripheral, pass a pointer to sdspi_device_config_t
|
||||||
|
* structure initialized using SDSPI_DEVICE_CONFIG_DEFAULT().
|
||||||
|
* @param mount_config pointer to structure with extra parameters for mounting FATFS
|
||||||
|
* @param[out] out_card If not NULL, pointer to the card information structure will be returned via
|
||||||
|
* this argument. It is suggested to hold this handle and use it to unmount the card later if
|
||||||
|
* needed. Otherwise it's not suggested to use more than one card at the same time and unmount one
|
||||||
|
* of them in your application.
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called
|
||||||
|
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||||
|
* - ESP_FAIL if partition can not be mounted
|
||||||
|
* - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers
|
||||||
|
*/
|
||||||
|
esp_err_t esp_vfs_fat_sdspi_mount_mh(const char* base_path, const sdmmc_host_t* host_config_input, const sdspi_device_config_t* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
|||||||
|
|
||||||
idf_component_register(SRCS ${app_sources}
|
idf_component_register(SRCS ${app_sources}
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "."
|
||||||
REQUIRES tflite-lib esp_http_client jomjol_logfile)
|
REQUIRES esp_http_client jomjol_logfile)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
#include "esp_http_client.h"
|
#include "esp_http_client.h"
|
||||||
|
#include "time_sntp.h"
|
||||||
#include "../../include/defines.h"
|
#include "../../include/defines.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -16,21 +17,21 @@ std::string _influxDBUser;
|
|||||||
std::string _influxDBPassword;
|
std::string _influxDBPassword;
|
||||||
|
|
||||||
std::string _influxDB_V2_URI;
|
std::string _influxDB_V2_URI;
|
||||||
std::string _influxDB_V2_Database;
|
std::string _influxDB_V2_Bucket;
|
||||||
std::string _influxDB_V2_Token;
|
std::string _influxDB_V2_Token;
|
||||||
std::string _influxDB_V2_Org;
|
std::string _influxDB_V2_Org;
|
||||||
|
|
||||||
static esp_err_t http_event_handler(esp_http_client_event_t *evt);
|
static esp_err_t http_event_handler(esp_http_client_event_t *evt);
|
||||||
|
|
||||||
void InfluxDB_V2_Init(std::string _uri, std::string _database, std::string _org, std::string _token)
|
void InfluxDB_V2_Init(std::string _uri, std::string _bucket, std::string _org, std::string _token)
|
||||||
{
|
{
|
||||||
_influxDB_V2_URI = _uri;
|
_influxDB_V2_URI = _uri;
|
||||||
_influxDB_V2_Database = _database;
|
_influxDB_V2_Bucket = _bucket;
|
||||||
_influxDB_V2_Org = _org;
|
_influxDB_V2_Org = _org;
|
||||||
_influxDB_V2_Token = _token;
|
_influxDB_V2_Token = _token;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InfluxDB_V2_Publish(std::string _measurement, std::string _key, std::string _content, std::string _timestamp)
|
void InfluxDB_V2_Publish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC)
|
||||||
{
|
{
|
||||||
char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
|
char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
|
||||||
esp_http_client_config_t http_config = {
|
esp_http_client_config_t http_config = {
|
||||||
@@ -41,32 +42,20 @@ void InfluxDB_V2_Publish(std::string _measurement, std::string _key, std::string
|
|||||||
.user_data = response_buffer
|
.user_data = response_buffer
|
||||||
};
|
};
|
||||||
|
|
||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDB_V2_Publish - Key: " + _key + ", Content: " + _content + ", Timestamp: " + _timestamp);
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDB_V2_Publish - Key: " + _key + ", Content: " + _content + ", timeUTC: " + std::to_string(_timeUTC));
|
||||||
|
|
||||||
std::string payload;
|
std::string payload;
|
||||||
char nowTimestamp[21];
|
char nowTimestamp[21];
|
||||||
|
|
||||||
if (_timestamp.length() > 0)
|
if (_timeUTC > 0)
|
||||||
{
|
{
|
||||||
struct tm tm;
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Timestamp (UTC): " + std::to_string(_timeUTC));
|
||||||
strptime(_timestamp.c_str(), PREVALUE_TIME_FORMAT_OUTPUT, &tm);
|
sprintf(nowTimestamp,"%ld000000000", _timeUTC); // UTC
|
||||||
time_t t = mktime(&tm); // Time in Localtime (looks like timezone is not used by strptime)
|
|
||||||
|
|
||||||
// struct tm * ptm;
|
|
||||||
// ptm = gmtime ( &t );
|
|
||||||
// time_t utc = mktime(ptm);
|
|
||||||
|
|
||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Use handover timestamp: " + _timestamp + " converted GMT timestamp: " + std::to_string(t));
|
|
||||||
|
|
||||||
// utc = 2*t - utc; // Take care of timezone (looks difficult, but is easy: t = t + (t - utc), weil t-utc = timezone)
|
|
||||||
// LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "time conversion utc after: " + std::to_string(utc));
|
|
||||||
|
|
||||||
sprintf(nowTimestamp,"%ld000000000", (long) t); // UTC
|
|
||||||
|
|
||||||
payload = _measurement + " " + _key + "=" + _content + " " + nowTimestamp;
|
payload = _measurement + " " + _key + "=" + _content + " " + nowTimestamp;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "no timestamp given");
|
||||||
payload = _measurement + " " + _key + "=" + _content;
|
payload = _measurement + " " + _key + "=" + _content;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +63,7 @@ void InfluxDB_V2_Publish(std::string _measurement, std::string _key, std::string
|
|||||||
|
|
||||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload);
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload);
|
||||||
|
|
||||||
std::string apiURI = _influxDB_V2_URI + "/api/v2/write?org=" + _influxDB_V2_Org + "&bucket=" + _influxDB_V2_Database;
|
std::string apiURI = _influxDB_V2_URI + "/api/v2/write?org=" + _influxDB_V2_Org + "&bucket=" + _influxDB_V2_Bucket;
|
||||||
apiURI.shrink_to_fit();
|
apiURI.shrink_to_fit();
|
||||||
http_config.url = apiURI.c_str();
|
http_config.url = apiURI.c_str();
|
||||||
ESP_LOGI(TAG, "http_config: %s", http_config.url); // Add mark on log to see when it restarted
|
ESP_LOGI(TAG, "http_config: %s", http_config.url); // Add mark on log to see when it restarted
|
||||||
@@ -141,7 +130,7 @@ static esp_err_t http_event_handler(esp_http_client_event_t *evt)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, std::string _timestamp) {
|
void InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC) {
|
||||||
char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
|
char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
|
||||||
esp_http_client_config_t http_config = {
|
esp_http_client_config_t http_config = {
|
||||||
.user_agent = "ESP32 Meter reader",
|
.user_agent = "ESP32 Meter reader",
|
||||||
@@ -160,29 +149,17 @@ void InfluxDBPublish(std::string _measurement, std::string _key, std::string _co
|
|||||||
std::string payload;
|
std::string payload;
|
||||||
char nowTimestamp[21];
|
char nowTimestamp[21];
|
||||||
|
|
||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBPublish - Key: " + _key + ", Content: " + _content + ", Timestamp: " + _timestamp);
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBPublish - Key: " + _key + ", Content: " + _content + ", timeUTC: " + std::to_string(_timeUTC));
|
||||||
|
|
||||||
if (_timestamp.length() > 0)
|
if (_timeUTC > 0)
|
||||||
{
|
{
|
||||||
struct tm tm;
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Timestamp (UTC): " + std::to_string(_timeUTC));
|
||||||
strptime(_timestamp.c_str(), PREVALUE_TIME_FORMAT_OUTPUT, &tm);
|
sprintf(nowTimestamp,"%ld000000000", _timeUTC); // UTC
|
||||||
time_t t = mktime(&tm); // Time in Localtime (looks like timezone is not used by strptime)
|
|
||||||
|
|
||||||
// struct tm * ptm;
|
|
||||||
// ptm = gmtime ( &t );
|
|
||||||
// time_t utc = mktime(ptm);
|
|
||||||
|
|
||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Use handover timestamp: " + _timestamp + " converted GMT timestamp: " + std::to_string(t));
|
|
||||||
|
|
||||||
// utc = 2*t - utc; // Take care of timezone (looks difficult, but is easy: t = t + (t - utc), weil t-utc = timezone)
|
|
||||||
// LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "time conversion utc after: " + std::to_string(utc));
|
|
||||||
|
|
||||||
sprintf(nowTimestamp,"%ld000000000", (long) t); // UTC
|
|
||||||
|
|
||||||
payload = _measurement + " " + _key + "=" + _content + " " + nowTimestamp;
|
payload = _measurement + " " + _key + "=" + _content + " " + nowTimestamp;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "no timestamp given");
|
||||||
payload = _measurement + " " + _key + "=" + _content;
|
payload = _measurement + " " + _key + "=" + _content;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +168,7 @@ void InfluxDBPublish(std::string _measurement, std::string _key, std::string _co
|
|||||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload);
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload);
|
||||||
|
|
||||||
|
|
||||||
// use the default retention policy of the database
|
// use the default retention policy of the bucket
|
||||||
std::string apiURI = _influxDBURI + "/write?db=" + _influxDBDatabase;
|
std::string apiURI = _influxDBURI + "/write?db=" + _influxDBDatabase;
|
||||||
// std::string apiURI = _influxDBURI + "/api/v2/write?bucket=" + _influxDBDatabase + "/";
|
// std::string apiURI = _influxDBURI + "/api/v2/write?bucket=" + _influxDBDatabase + "/";
|
||||||
|
|
||||||
|
|||||||
@@ -10,11 +10,11 @@
|
|||||||
|
|
||||||
// Interface to InfluxDB v1.x
|
// Interface to InfluxDB v1.x
|
||||||
void InfluxDBInit(std::string _influxDBURI, std::string _database, std::string _user, std::string _password);
|
void InfluxDBInit(std::string _influxDBURI, std::string _database, std::string _user, std::string _password);
|
||||||
void InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, std::string _timestamp);
|
void InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC);
|
||||||
|
|
||||||
// Interface to InfluxDB v2.x
|
// Interface to InfluxDB v2.x
|
||||||
void InfluxDB_V2_Init(std::string _uri, std::string _database, std::string _org, std::string _token);
|
void InfluxDB_V2_Init(std::string _uri, std::string _bucket, std::string _org, std::string _token);
|
||||||
void InfluxDB_V2_Publish(std::string _measurement, std::string _key, std::string _content, std::string _timestamp);
|
void InfluxDB_V2_Publish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
|||||||
|
|
||||||
idf_component_register(SRCS ${app_sources}
|
idf_component_register(SRCS ${app_sources}
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "."
|
||||||
REQUIRES esp_timer tflite-lib mqtt jomjol_tfliteclass jomjol_helper jomjol_mqtt jomjol_wlan json)
|
REQUIRES esp_timer esp-tflite-micro mqtt jomjol_tfliteclass jomjol_helper jomjol_mqtt jomjol_wlan json)
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
#include "interface_mqtt.h"
|
#include "interface_mqtt.h"
|
||||||
|
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#if DEBUG_DETAIL_ON
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#endif
|
||||||
#include "connect_wlan.h"
|
#include "connect_wlan.h"
|
||||||
#include "mqtt_client.h"
|
#include "mqtt_client.h"
|
||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
@@ -32,6 +35,7 @@ bool mqtt_connected = false;
|
|||||||
|
|
||||||
esp_mqtt_client_handle_t client = NULL;
|
esp_mqtt_client_handle_t client = NULL;
|
||||||
std::string uri, client_id, lwt_topic, lwt_connected, lwt_disconnected, user, password, maintopic;
|
std::string uri, client_id, lwt_topic, lwt_connected, lwt_disconnected, user, password, maintopic;
|
||||||
|
std::string caCert, clientCert, clientKey;
|
||||||
int keepalive;
|
int keepalive;
|
||||||
bool SetRetainFlag;
|
bool SetRetainFlag;
|
||||||
void (*callbackOnConnected)(std::string, bool) = NULL;
|
void (*callbackOnConnected)(std::string, bool) = NULL;
|
||||||
@@ -169,6 +173,10 @@ static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event) {
|
|||||||
else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_NOT_AUTHORIZED) {
|
else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_NOT_AUTHORIZED) {
|
||||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, not authorized. Check username/password (0x05)");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, not authorized. Check username/password (0x05)");
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Other event id:" + event->error_handle->connect_return_code);
|
||||||
|
ESP_LOGE(TAG, "Other event id:%d", event->error_handle->connect_return_code);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
ESP_LOGD(TAG, "MQTT_EVENT_ERROR - esp_mqtt_error_codes:");
|
ESP_LOGD(TAG, "MQTT_EVENT_ERROR - esp_mqtt_error_codes:");
|
||||||
@@ -198,6 +206,7 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_
|
|||||||
|
|
||||||
bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password,
|
bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password,
|
||||||
std::string _maintopic, std::string _lwt, std::string _lwt_connected, std::string _lwt_disconnected,
|
std::string _maintopic, std::string _lwt, std::string _lwt_connected, std::string _lwt_disconnected,
|
||||||
|
std::string _cacertfilename, std::string _clientcertfilename, std::string _clientkeyfilename,
|
||||||
int _keepalive, bool _SetRetainFlag, void *_callbackOnConnected) {
|
int _keepalive, bool _SetRetainFlag, void *_callbackOnConnected) {
|
||||||
if ((_mqttURI.length() == 0) || (_maintopic.length() == 0) || (_clientid.length() == 0))
|
if ((_mqttURI.length() == 0) || (_maintopic.length() == 0) || (_clientid.length() == 0))
|
||||||
{
|
{
|
||||||
@@ -215,6 +224,25 @@ bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _us
|
|||||||
maintopic = _maintopic;
|
maintopic = _maintopic;
|
||||||
callbackOnConnected = ( void (*)(std::string, bool) )(_callbackOnConnected);
|
callbackOnConnected = ( void (*)(std::string, bool) )(_callbackOnConnected);
|
||||||
|
|
||||||
|
if (_clientcertfilename.length() && _clientkeyfilename.length()){
|
||||||
|
std::ifstream cert_ifs(_clientcertfilename);
|
||||||
|
std::string cert_content((std::istreambuf_iterator<char>(cert_ifs)), (std::istreambuf_iterator<char>()));
|
||||||
|
clientCert = cert_content;
|
||||||
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using clientCert: " + _clientcertfilename);
|
||||||
|
|
||||||
|
std::ifstream key_ifs(_clientkeyfilename);
|
||||||
|
std::string key_content((std::istreambuf_iterator<char>(key_ifs)), (std::istreambuf_iterator<char>()));
|
||||||
|
clientKey = key_content;
|
||||||
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using clientKey: " + _clientkeyfilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_cacertfilename.length() ){
|
||||||
|
std::ifstream ifs(_cacertfilename);
|
||||||
|
std::string content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
|
||||||
|
caCert = content;
|
||||||
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using caCert: " + _cacertfilename);
|
||||||
|
}
|
||||||
|
|
||||||
if (_user.length() && _password.length()){
|
if (_user.length() && _password.length()){
|
||||||
user = _user;
|
user = _user;
|
||||||
password = _password;
|
password = _password;
|
||||||
@@ -268,6 +296,20 @@ int MQTT_Init() {
|
|||||||
mqtt_cfg.session.keepalive = keepalive;
|
mqtt_cfg.session.keepalive = keepalive;
|
||||||
mqtt_cfg.buffer.size = 1536; // size of MQTT send/receive buffer (Default: 1024)
|
mqtt_cfg.buffer.size = 1536; // size of MQTT send/receive buffer (Default: 1024)
|
||||||
|
|
||||||
|
if (caCert.length()){
|
||||||
|
mqtt_cfg.broker.verification.certificate = caCert.c_str();
|
||||||
|
mqtt_cfg.broker.verification.certificate_len = caCert.length() + 1;
|
||||||
|
mqtt_cfg.broker.verification.skip_cert_common_name_check = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clientCert.length() && clientKey.length()){
|
||||||
|
mqtt_cfg.credentials.authentication.certificate = clientCert.c_str();
|
||||||
|
mqtt_cfg.credentials.authentication.certificate_len = clientCert.length() + 1;
|
||||||
|
|
||||||
|
mqtt_cfg.credentials.authentication.key = clientKey.c_str();
|
||||||
|
mqtt_cfg.credentials.authentication.key_len = clientKey.length() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (user.length() && password.length()){
|
if (user.length() && password.length()){
|
||||||
mqtt_cfg.credentials.username = user.c_str();
|
mqtt_cfg.credentials.username = user.c_str();
|
||||||
mqtt_cfg.credentials.authentication.password = password.c_str();
|
mqtt_cfg.credentials.authentication.password = password.c_str();
|
||||||
@@ -371,8 +413,10 @@ bool mqtt_handler_set_prevalue(std::string _topic, char* _data, int _data_len)
|
|||||||
if (cJSON_IsNumber(value)) { // Check if value is a number
|
if (cJSON_IsNumber(value)) { // Check if value is a number
|
||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_set_prevalue called: numbersname: " + std::string(numbersname->valuestring) +
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_set_prevalue called: numbersname: " + std::string(numbersname->valuestring) +
|
||||||
", value: " + std::to_string(value->valuedouble));
|
", value: " + std::to_string(value->valuedouble));
|
||||||
if (flowctrl.UpdatePrevalue(std::to_string(value->valuedouble), std::string(numbersname->valuestring), true))
|
if (flowctrl.UpdatePrevalue(std::to_string(value->valuedouble), std::string(numbersname->valuestring), true)) {
|
||||||
|
cJSON_Delete(jsonData);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: value not a valid number (\"value\": 12345.12345)");
|
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: value not a valid number (\"value\": 12345.12345)");
|
||||||
@@ -381,6 +425,7 @@ bool mqtt_handler_set_prevalue(std::string _topic, char* _data, int _data_len)
|
|||||||
else {
|
else {
|
||||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: numbersname not a valid string (\"numbersname\": \"main\")");
|
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: numbersname not a valid string (\"numbersname\": \"main\")");
|
||||||
}
|
}
|
||||||
|
cJSON_Delete(jsonData);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: handler called, but no data received");
|
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: handler called, but no data received");
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password,
|
bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password,
|
||||||
std::string _maintopic, std::string _lwt, std::string _lwt_connected, std::string _lwt_disconnected,
|
std::string _maintopic, std::string _lwt, std::string _lwt_connected, std::string _lwt_disconnected,
|
||||||
|
std::string _cacertfilename, std::string _clientcertfilename, std::string _clientkeyfilename,
|
||||||
int _keepalive, bool SetRetainFlag, void *callbackOnConnected);
|
int _keepalive, bool SetRetainFlag, void *callbackOnConnected);
|
||||||
int MQTT_Init();
|
int MQTT_Init();
|
||||||
void MQTTdestroy_client(bool _disable);
|
void MQTTdestroy_client(bool _disable);
|
||||||
|
|||||||
@@ -12,6 +12,22 @@
|
|||||||
|
|
||||||
static const char *TAG = "TFLITE";
|
static const char *TAG = "TFLITE";
|
||||||
|
|
||||||
|
|
||||||
|
void CTfLiteClass::MakeStaticResolver()
|
||||||
|
{
|
||||||
|
resolver.AddFullyConnected();
|
||||||
|
resolver.AddReshape();
|
||||||
|
resolver.AddSoftmax();
|
||||||
|
resolver.AddConv2D();
|
||||||
|
resolver.AddMaxPool2D();
|
||||||
|
resolver.AddQuantize();
|
||||||
|
resolver.AddMul();
|
||||||
|
resolver.AddAdd();
|
||||||
|
resolver.AddLeakyRelu();
|
||||||
|
resolver.AddDequantize();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
float CTfLiteClass::GetOutputValue(int nr)
|
float CTfLiteClass::GetOutputValue(int nr)
|
||||||
{
|
{
|
||||||
TfLiteTensor* output2 = this->interpreter->output(0);
|
TfLiteTensor* output2 = this->interpreter->output(0);
|
||||||
@@ -179,22 +195,22 @@ bool CTfLiteClass::LoadInputImageBasis(CImageBasis *rs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool CTfLiteClass::MakeAllocate()
|
bool CTfLiteClass::MakeAllocate()
|
||||||
{
|
{
|
||||||
static tflite::AllOpsResolver resolver;
|
MakeStaticResolver();
|
||||||
|
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
LogFile.WriteHeapInfo("CTLiteClass::Alloc start");
|
LogFile.WriteHeapInfo("CTLiteClass::Alloc start");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CTfLiteClass::MakeAllocate");
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CTfLiteClass::MakeAllocate");
|
||||||
this->interpreter = new tflite::MicroInterpreter(this->model, resolver, this->tensor_arena, this->kTensorArenaSize, this->error_reporter);
|
this->interpreter = new tflite::MicroInterpreter(this->model, resolver, this->tensor_arena, this->kTensorArenaSize);
|
||||||
|
|
||||||
if (this->interpreter)
|
if (this->interpreter)
|
||||||
{
|
{
|
||||||
TfLiteStatus allocate_status = this->interpreter->AllocateTensors();
|
TfLiteStatus allocate_status = this->interpreter->AllocateTensors();
|
||||||
if (allocate_status != kTfLiteOk) {
|
if (allocate_status != kTfLiteOk) {
|
||||||
TF_LITE_REPORT_ERROR(error_reporter, "AllocateTensors() failed");
|
|
||||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "AllocateTensors() failed");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "AllocateTensors() failed");
|
||||||
|
|
||||||
this->GetInputDimension();
|
this->GetInputDimension();
|
||||||
@@ -284,12 +300,6 @@ bool CTfLiteClass::ReadFileToModel(std::string _fn)
|
|||||||
|
|
||||||
bool CTfLiteClass::LoadModel(std::string _fn)
|
bool CTfLiteClass::LoadModel(std::string _fn)
|
||||||
{
|
{
|
||||||
#ifdef SUPRESS_TFLITE_ERRORS
|
|
||||||
this->error_reporter = new tflite::OwnMicroErrorReporter;
|
|
||||||
#else
|
|
||||||
this->error_reporter = new tflite::MicroErrorReporter;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CTfLiteClass::LoadModel");
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CTfLiteClass::LoadModel");
|
||||||
|
|
||||||
if (!ReadFileToModel(_fn.c_str())) {
|
if (!ReadFileToModel(_fn.c_str())) {
|
||||||
@@ -320,16 +330,6 @@ CTfLiteClass::CTfLiteClass()
|
|||||||
CTfLiteClass::~CTfLiteClass()
|
CTfLiteClass::~CTfLiteClass()
|
||||||
{
|
{
|
||||||
delete this->interpreter;
|
delete this->interpreter;
|
||||||
delete this->error_reporter;
|
|
||||||
|
|
||||||
psram_free_shared_tensor_arena_and_model_memory();
|
psram_free_shared_tensor_arena_and_model_memory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace tflite
|
|
||||||
{
|
|
||||||
int OwnMicroErrorReporter::Report(const char* format, va_list args)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,38 +3,23 @@
|
|||||||
#ifndef CTFLITECLASS_H
|
#ifndef CTFLITECLASS_H
|
||||||
#define CTFLITECLASS_H
|
#define CTFLITECLASS_H
|
||||||
|
|
||||||
#include "tensorflow/lite/micro/all_ops_resolver.h"
|
#include "tensorflow/lite/micro/micro_mutable_op_resolver.h"
|
||||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
|
||||||
#include "tensorflow/lite/micro/micro_interpreter.h"
|
#include "tensorflow/lite/micro/micro_interpreter.h"
|
||||||
#include "tensorflow/lite/schema/schema_generated.h"
|
#include "tensorflow/lite/schema/schema_generated.h"
|
||||||
#include "tensorflow/lite/micro/kernels/micro_ops.h"
|
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
|
||||||
#include "CImageBasis.h"
|
#include "CImageBasis.h"
|
||||||
|
|
||||||
#ifdef SUPRESS_TFLITE_ERRORS
|
|
||||||
#include "tensorflow/lite/core/api/error_reporter.h"
|
|
||||||
#include "tensorflow/lite/micro/compatibility.h"
|
|
||||||
#include "tensorflow/lite/micro/debug_log.h"
|
|
||||||
///// OwnErrorReporter to prevent printing of Errors (especially unavoidable in CalculateActivationRangeQuantized@kerne_util.cc)
|
|
||||||
namespace tflite {
|
|
||||||
class OwnMicroErrorReporter : public ErrorReporter {
|
|
||||||
public:
|
|
||||||
int Report(const char* format, va_list args) override;
|
|
||||||
};
|
|
||||||
} // namespace tflite
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class CTfLiteClass
|
class CTfLiteClass
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
tflite::ErrorReporter *error_reporter;
|
tflite::MicroMutableOpResolver<10> resolver;
|
||||||
const tflite::Model* model;
|
const tflite::Model* model;
|
||||||
tflite::MicroInterpreter* interpreter;
|
tflite::MicroInterpreter* interpreter;
|
||||||
TfLiteTensor* output = nullptr;
|
TfLiteTensor* output = nullptr;
|
||||||
static tflite::AllOpsResolver resolver;
|
|
||||||
|
|
||||||
int kTensorArenaSize;
|
int kTensorArenaSize;
|
||||||
uint8_t *tensor_arena;
|
uint8_t *tensor_arena;
|
||||||
@@ -48,6 +33,7 @@ class CTfLiteClass
|
|||||||
|
|
||||||
long GetFileSize(std::string filename);
|
long GetFileSize(std::string filename);
|
||||||
bool ReadFileToModel(std::string _fn);
|
bool ReadFileToModel(std::string _fn);
|
||||||
|
void MakeStaticResolver();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CTfLiteClass();
|
CTfLiteClass();
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
|||||||
|
|
||||||
idf_component_register(SRCS ${app_sources}
|
idf_component_register(SRCS ${app_sources}
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "."
|
||||||
REQUIRES tflite-lib jomjol_logfile jomjol_configfile)
|
REQUIRES esp_netif esp-tflite-micro jomjol_logfile jomjol_configfile)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_attr.h"
|
#include "esp_attr.h"
|
||||||
#include "esp_sleep.h"
|
#include "esp_sleep.h"
|
||||||
#include "esp_sntp.h"
|
#include "esp_netif_sntp.h"
|
||||||
|
|
||||||
#include "../../include/defines.h"
|
#include "../../include/defines.h"
|
||||||
|
|
||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
@@ -31,6 +32,9 @@ std::string getNtpStatusText(sntp_sync_status_t status);
|
|||||||
static void setTimeZone(std::string _tzstring);
|
static void setTimeZone(std::string _tzstring);
|
||||||
static std::string getServerName(void);
|
static std::string getServerName(void);
|
||||||
|
|
||||||
|
int LocalTimeToUTCOffsetSeconds;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::string ConvertTimeToString(time_t _time, const char * frm)
|
std::string ConvertTimeToString(time_t _time, const char * frm)
|
||||||
{
|
{
|
||||||
@@ -89,15 +93,51 @@ bool time_manual_reset_sync(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int getUTCOffsetSeconds(std::string &zeitzone)
|
||||||
|
{
|
||||||
|
int offset = 0;
|
||||||
|
int vorzeichen = 1;
|
||||||
|
int minuten = 0;
|
||||||
|
int stunden = 0;
|
||||||
|
time_t now;
|
||||||
|
struct tm timeinfo;
|
||||||
|
|
||||||
|
time (&now);
|
||||||
|
localtime_r(&now, &timeinfo);
|
||||||
|
char buffer[80];
|
||||||
|
strftime(buffer, 80, "%z", &timeinfo);
|
||||||
|
zeitzone = std::string(buffer);
|
||||||
|
|
||||||
|
if (zeitzone.length() == 5)
|
||||||
|
{
|
||||||
|
if (zeitzone[0] == '-')
|
||||||
|
vorzeichen = -1;
|
||||||
|
|
||||||
|
stunden = stoi(zeitzone.substr(1, 2));
|
||||||
|
minuten = stoi(zeitzone.substr(3, 2));
|
||||||
|
|
||||||
|
offset = ((stunden * 60) + minuten) * 60;
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void setTimeZone(std::string _tzstring)
|
void setTimeZone(std::string _tzstring)
|
||||||
{
|
{
|
||||||
setenv("TZ", _tzstring.c_str(), 1);
|
setenv("TZ", _tzstring.c_str(), 1);
|
||||||
tzset();
|
tzset();
|
||||||
|
|
||||||
_tzstring = "Time zone set to " + _tzstring;
|
_tzstring = "Time zone set to " + _tzstring;
|
||||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, _tzstring);
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, _tzstring);
|
||||||
|
|
||||||
|
std::string zeitzone;
|
||||||
|
LocalTimeToUTCOffsetSeconds = getUTCOffsetSeconds(zeitzone);
|
||||||
|
// std::string zw = std::to_string(LocalTimeToUTCOffsetSeconds);
|
||||||
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "time zone: " + zeitzone + " Delta to UTC: " + std::to_string(LocalTimeToUTCOffsetSeconds) + " seconds");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::string getNtpStatusText(sntp_sync_status_t status) {
|
std::string getNtpStatusText(sntp_sync_status_t status) {
|
||||||
if (status == SNTP_SYNC_STATUS_COMPLETED) {
|
if (status == SNTP_SYNC_STATUS_COMPLETED) {
|
||||||
return "Synchronized";
|
return "Synchronized";
|
||||||
@@ -235,21 +275,13 @@ bool setupTime() {
|
|||||||
|
|
||||||
if (useNtp) {
|
if (useNtp) {
|
||||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Configuring NTP Client...");
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Configuring NTP Client...");
|
||||||
sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG(timeServer.c_str());
|
||||||
sntp_setservername(0, timeServer.c_str());
|
config.sync_cb = time_sync_notification_cb;
|
||||||
sntp_set_time_sync_notification_cb(time_sync_notification_cb);
|
esp_netif_sntp_init(&config);
|
||||||
|
|
||||||
setTimeZone(timeZone);
|
setTimeZone(timeZone);
|
||||||
|
|
||||||
sntp_init();
|
|
||||||
/*
|
|
||||||
if (!wait_for_timesync())
|
|
||||||
{
|
|
||||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Timesync at startup failed.");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* The RTC keeps the time after a restart (Except on Power On or Pin Reset)
|
/* The RTC keeps the time after a restart (Except on Power On or Pin Reset)
|
||||||
* There should only be a minor correction through NTP */
|
* There should only be a minor correction through NTP */
|
||||||
|
|
||||||
@@ -258,6 +290,7 @@ bool setupTime() {
|
|||||||
localtime_r(&now, &timeinfo);
|
localtime_r(&now, &timeinfo);
|
||||||
strftime(strftime_buf, sizeof(strftime_buf), "%Y-%m-%d %H:%M:%S", &timeinfo);
|
strftime(strftime_buf, sizeof(strftime_buf), "%Y-%m-%d %H:%M:%S", &timeinfo);
|
||||||
|
|
||||||
|
|
||||||
if (getTimeIsSet()) {
|
if (getTimeIsSet()) {
|
||||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Time is already set: " + std::string(strftime_buf));
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Time is already set: " + std::string(strftime_buf));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,5 +28,7 @@ bool setupTime();
|
|||||||
|
|
||||||
bool time_manual_reset_sync(void);
|
bool time_manual_reset_sync(void);
|
||||||
|
|
||||||
|
extern int LocalTimeToUTCOffsetSeconds;
|
||||||
|
|
||||||
|
|
||||||
#endif //TIMESNTP_H
|
#endif //TIMESNTP_H
|
||||||
@@ -378,7 +378,7 @@ void wifi_scan(void)
|
|||||||
else {
|
else {
|
||||||
if (esp_wifi_scan_get_ap_records(&max_number_of_ap_found, wifi_ap_records) != ESP_OK) { // Retrieve results (and free internal heap)
|
if (esp_wifi_scan_get_ap_records(&max_number_of_ap_found, wifi_ap_records) != ESP_OK) { // Retrieve results (and free internal heap)
|
||||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "wifi_scan: esp_wifi_scan_get_ap_records: Error retrieving datasets");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "wifi_scan: esp_wifi_scan_get_ap_records: Error retrieving datasets");
|
||||||
delete wifi_ap_records;
|
delete[] wifi_ap_records;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -401,7 +401,7 @@ void wifi_scan(void)
|
|||||||
APWithBetterRSSI = true;
|
APWithBetterRSSI = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete wifi_ap_records;
|
delete[] wifi_ap_records;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Submodule code/components/tflite-micro-esp-examples deleted from 095f55a6ee
@@ -53,7 +53,7 @@
|
|||||||
|
|
||||||
//****************************************
|
//****************************************
|
||||||
|
|
||||||
//compiler optimization for tflite-micro-esp-examples
|
//compiler optimization for esp-tflite-micro
|
||||||
#define XTENSA
|
#define XTENSA
|
||||||
//#define CONFIG_IDF_TARGET_ARCH_XTENSA //not needed with platformio/espressif32 @ 5.2.0
|
//#define CONFIG_IDF_TARGET_ARCH_XTENSA //not needed with platformio/espressif32 @ 5.2.0
|
||||||
|
|
||||||
@@ -167,15 +167,6 @@
|
|||||||
#define LWT_DISCONNECTED "connection lost"
|
#define LWT_DISCONNECTED "connection lost"
|
||||||
|
|
||||||
|
|
||||||
//CTfLiteClass
|
|
||||||
#define TFLITE_MINIMAL_CHECK(x) \
|
|
||||||
if (!(x)) { \
|
|
||||||
fprintf(stderr, "Error at %s:%d\n", __FILE__, __LINE__); \
|
|
||||||
exit(1); \
|
|
||||||
}
|
|
||||||
#define SUPRESS_TFLITE_ERRORS // use, to avoid error messages from TFLITE
|
|
||||||
|
|
||||||
|
|
||||||
// connect_wlan.cpp
|
// connect_wlan.cpp
|
||||||
//******************************
|
//******************************
|
||||||
/* WIFI roaming functionalities 802.11k+v (uses ca. 6kB - 8kB internal RAM; if SCAN CACHE activated: + 1kB / beacon)
|
/* WIFI roaming functionalities 802.11k+v (uses ca. 6kB - 8kB internal RAM; if SCAN CACHE activated: + 1kB / beacon)
|
||||||
|
|||||||
@@ -3,12 +3,6 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
//#include "freertos/FreeRTOS.h"
|
|
||||||
//#include "freertos/task.h"
|
|
||||||
//#include "freertos/event_groups.h"
|
|
||||||
|
|
||||||
//#include "driver/gpio.h"
|
|
||||||
//#include "sdkconfig.h"
|
|
||||||
#include "esp_psram.h"
|
#include "esp_psram.h"
|
||||||
#include "esp_pm.h"
|
#include "esp_pm.h"
|
||||||
|
|
||||||
@@ -16,17 +10,13 @@
|
|||||||
|
|
||||||
#include "esp_chip_info.h"
|
#include "esp_chip_info.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// SD-Card ////////////////////
|
// SD-Card ////////////////////
|
||||||
//#include "nvs_flash.h"
|
#include "sdcard_init.h"
|
||||||
#include "esp_vfs_fat.h"
|
#include "esp_vfs_fat.h"
|
||||||
//#include "sdmmc_cmd.h"
|
#include "ffconf.h"
|
||||||
#include "driver/sdmmc_host.h"
|
#include "driver/sdmmc_host.h"
|
||||||
//#include "driver/sdmmc_defs.h"
|
|
||||||
///////////////////////////////
|
///////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
|
|
||||||
#include "connect_wlan.h"
|
#include "connect_wlan.h"
|
||||||
@@ -38,7 +28,6 @@
|
|||||||
#include "server_ota.h"
|
#include "server_ota.h"
|
||||||
#include "time_sntp.h"
|
#include "time_sntp.h"
|
||||||
#include "configFile.h"
|
#include "configFile.h"
|
||||||
//#include "ClassControllCamera.h"
|
|
||||||
#include "server_main.h"
|
#include "server_main.h"
|
||||||
#include "server_camera.h"
|
#include "server_camera.h"
|
||||||
#ifdef ENABLE_MQTT
|
#ifdef ENABLE_MQTT
|
||||||
@@ -49,7 +38,6 @@
|
|||||||
#include "sdcard_check.h"
|
#include "sdcard_check.h"
|
||||||
|
|
||||||
#include "../../include/defines.h"
|
#include "../../include/defines.h"
|
||||||
//#include "server_GPIO.h"
|
|
||||||
|
|
||||||
#ifdef ENABLE_SOFTAP
|
#ifdef ENABLE_SOFTAP
|
||||||
#include "softAP.h"
|
#include "softAP.h"
|
||||||
@@ -101,6 +89,8 @@ bool setCpuFrequency(void);
|
|||||||
|
|
||||||
static const char *TAG = "MAIN";
|
static const char *TAG = "MAIN";
|
||||||
|
|
||||||
|
#define MOUNT_POINT "/sdcard"
|
||||||
|
|
||||||
|
|
||||||
bool Init_NVS_SDCard()
|
bool Init_NVS_SDCard()
|
||||||
{
|
{
|
||||||
@@ -112,26 +102,30 @@ bool Init_NVS_SDCard()
|
|||||||
|
|
||||||
ESP_LOGD(TAG, "Using SDMMC peripheral");
|
ESP_LOGD(TAG, "Using SDMMC peripheral");
|
||||||
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||||
|
host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
|
||||||
|
|
||||||
// This initializes the slot without card detect (CD) and write protect (WP) signals.
|
// This initializes the slot without card detect (CD) and write protect (WP) signals.
|
||||||
// Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
|
// Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
|
||||||
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||||
|
|
||||||
// To use 1-line SD mode, uncomment the following line:
|
// Set bus width to use:
|
||||||
#ifdef __SD_USE_ONE_LINE_MODE__
|
#ifdef __SD_USE_ONE_LINE_MODE__
|
||||||
slot_config.width = 1;
|
slot_config.width = 1;
|
||||||
#endif
|
#else
|
||||||
|
slot_config.width = 4;
|
||||||
|
#endif
|
||||||
|
|
||||||
// GPIOs 15, 2, 4, 12, 13 should have external 10k pull-ups.
|
// Enable internal pullups on enabled pins. The internal pullups
|
||||||
// Internal pull-ups are not sufficient. However, enabling internal pull-ups
|
// are insufficient however, please make sure 10k external pullups are
|
||||||
// does make a difference some boards, so we do that here.
|
// connected on the bus. This is for debug / example purpose only.
|
||||||
gpio_set_pull_mode(GPIO_NUM_15, GPIO_PULLUP_ONLY); // CMD, needed in 4- and 1- line modes
|
slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
|
||||||
gpio_set_pull_mode(GPIO_NUM_2, GPIO_PULLUP_ONLY); // D0, needed in 4- and 1-line modes
|
|
||||||
#ifndef __SD_USE_ONE_LINE_MODE__
|
// Der PullUp des GPIO13 wird durch slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
|
||||||
gpio_set_pull_mode(GPIO_NUM_4, GPIO_PULLUP_ONLY); // D1, needed in 4-line mode only
|
// nicht gesetzt, da er eigentlich nicht benötigt wird,
|
||||||
gpio_set_pull_mode(GPIO_NUM_12, GPIO_PULLUP_ONLY); // D2, needed in 4-line mode only
|
// dies führt jedoch bei schlechten Kopien des AI_THINKER Boards
|
||||||
#endif
|
// zu Problemen mit der SD Initialisierung und eventuell sogar zur reboot-loops.
|
||||||
gpio_set_pull_mode(GPIO_NUM_13, GPIO_PULLUP_ONLY); // D3, needed in 4- and 1-line modes
|
// Um diese Probleme zu kompensieren, wird der PullUp manuel gesetzt.
|
||||||
|
gpio_set_pull_mode(GPIO_NUM_13, GPIO_PULLUP_ONLY); // HS2_D3
|
||||||
|
|
||||||
// Options for mounting the filesystem.
|
// Options for mounting the filesystem.
|
||||||
// If format_if_mount_failed is set to true, SD card will be partitioned and
|
// If format_if_mount_failed is set to true, SD card will be partitioned and
|
||||||
@@ -139,15 +133,18 @@ bool Init_NVS_SDCard()
|
|||||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||||
.format_if_mount_failed = false,
|
.format_if_mount_failed = false,
|
||||||
.max_files = 12, // previously -> 2022-09-21: 5, 2023-01-02: 7
|
.max_files = 12, // previously -> 2022-09-21: 5, 2023-01-02: 7
|
||||||
.allocation_unit_size = 16 * 1024
|
.allocation_unit_size = 0, // 0 = auto
|
||||||
|
.disk_status_check_enable = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
sdmmc_card_t* card;
|
sdmmc_card_t* card;
|
||||||
|
const char mount_point[] = MOUNT_POINT;
|
||||||
|
|
||||||
// Use settings defined above to initialize SD card and mount FAT filesystem.
|
// Use settings defined above to initialize SD card and mount FAT filesystem.
|
||||||
// Note: esp_vfs_fat_sdmmc_mount is an all-in-one convenience function.
|
// Note: esp_vfs_fat_sdmmc_mount is an all-in-one convenience function.
|
||||||
// Please check its source code and implement error recovery when developing
|
// Please check its source code and implement error recovery when developing
|
||||||
// production applications.
|
// production applications.
|
||||||
ret = esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card);
|
ret = esp_vfs_fat_sdmmc_mount_mh(mount_point, &host, &slot_config, &mount_config, &card);
|
||||||
|
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
if (ret == ESP_FAIL) {
|
if (ret == ESP_FAIL) {
|
||||||
@@ -642,7 +639,6 @@ void migrateConfiguration(void) {
|
|||||||
/* Fieldname has a <NUMBER> as prefix! */
|
/* Fieldname has a <NUMBER> as prefix! */
|
||||||
if (isInString(configLines[i], "Fieldname")) { // It is the parameter "Fieldname"
|
if (isInString(configLines[i], "Fieldname")) { // It is the parameter "Fieldname"
|
||||||
migrated = migrated | replaceString(configLines[i], "Fieldname", "Field"); // Rename it to Field
|
migrated = migrated | replaceString(configLines[i], "Fieldname", "Field"); // Rename it to Field
|
||||||
migrated = migrated | replaceString(configLines[i], ";", ""); // Enable it
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -650,7 +646,10 @@ void migrateConfiguration(void) {
|
|||||||
/* Fieldname has a <NUMBER> as prefix! */
|
/* Fieldname has a <NUMBER> as prefix! */
|
||||||
if (isInString(configLines[i], "Fieldname")) { // It is the parameter "Fieldname"
|
if (isInString(configLines[i], "Fieldname")) { // It is the parameter "Fieldname"
|
||||||
migrated = migrated | replaceString(configLines[i], "Fieldname", "Field"); // Rename it to Field
|
migrated = migrated | replaceString(configLines[i], "Fieldname", "Field"); // Rename it to Field
|
||||||
migrated = migrated | replaceString(configLines[i], ";", ""); // Enable it
|
}
|
||||||
|
/* Database got renamed to Bucket! */
|
||||||
|
if (isInString(configLines[i], "Database")) { // It is the parameter "Database"
|
||||||
|
migrated = migrated | replaceString(configLines[i], "Database", "Bucket"); // Rename it to Bucket
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -721,6 +720,7 @@ std::vector<std::string> splitString(const std::string& str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*bool replace_all(std::string& s, std::string const& toReplace, std::string const& replaceWith) {
|
/*bool replace_all(std::string& s, std::string const& toReplace, std::string const& replaceWith) {
|
||||||
std::string buf;
|
std::string buf;
|
||||||
std::size_t pos = 0;
|
std::size_t pos = 0;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include "MainFlowControl.h"
|
#include "MainFlowControl.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#include "esp_chip_info.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
@@ -39,17 +40,21 @@ esp_err_t info_get_handler(httpd_req_t *req)
|
|||||||
char _valuechar[30];
|
char _valuechar[30];
|
||||||
std::string _task;
|
std::string _task;
|
||||||
|
|
||||||
if (httpd_req_get_url_query_str(req, _query, 200) == ESP_OK)
|
if (httpd_req_get_url_query_str(req, _query, 200) != ESP_OK)
|
||||||
{
|
{
|
||||||
ESP_LOGD(TAG, "Query: %s", _query);
|
return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "invalid query string");
|
||||||
|
|
||||||
if (httpd_query_key_value(_query, "type", _valuechar, 30) == ESP_OK)
|
|
||||||
{
|
|
||||||
ESP_LOGD(TAG, "type is found: %s", _valuechar);
|
|
||||||
_task = std::string(_valuechar);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Query: %s", _query);
|
||||||
|
|
||||||
|
if (httpd_query_key_value(_query, "type", _valuechar, 30) != ESP_OK)
|
||||||
|
{
|
||||||
|
return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "missing or invalid 'type' query parameter (too long value?)");
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "type is found: %s", _valuechar);
|
||||||
|
_task = std::string(_valuechar);
|
||||||
|
|
||||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||||
|
|
||||||
if (_task.compare("GitBranch") == 0)
|
if (_task.compare("GitBranch") == 0)
|
||||||
@@ -166,6 +171,33 @@ esp_err_t info_get_handler(httpd_req_t *req)
|
|||||||
httpd_resp_sendstr(req, zw.c_str());
|
httpd_resp_sendstr(req, zw.c_str());
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
else if (_task.compare("ChipCores") == 0)
|
||||||
|
{
|
||||||
|
esp_chip_info_t chipInfo;
|
||||||
|
esp_chip_info(&chipInfo);
|
||||||
|
httpd_resp_sendstr(req, to_string(chipInfo.cores).c_str());
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
else if (_task.compare("ChipRevision") == 0)
|
||||||
|
{
|
||||||
|
esp_chip_info_t chipInfo;
|
||||||
|
esp_chip_info(&chipInfo);
|
||||||
|
httpd_resp_sendstr(req, to_string(chipInfo.revision).c_str());
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
else if (_task.compare("ChipFeatures") == 0)
|
||||||
|
{
|
||||||
|
esp_chip_info_t chipInfo;
|
||||||
|
esp_chip_info(&chipInfo);
|
||||||
|
httpd_resp_sendstr(req, to_string(chipInfo.features).c_str());
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char formatted[256];
|
||||||
|
snprintf(formatted, sizeof(formatted), "Unknown value for parameter info 'type': '%s'\n", _task.c_str());
|
||||||
|
return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, formatted);
|
||||||
|
}
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
[common:esp32-idf]
|
[common:esp32-idf]
|
||||||
extends = common:idf
|
extends = common:idf
|
||||||
platform = platformio/espressif32 @ 6.3.2
|
platform = platformio/espressif32 @ 6.5.0
|
||||||
framework = espidf
|
framework = espidf
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${common:idf.lib_deps}
|
${common:idf.lib_deps}
|
||||||
|
|||||||
@@ -41,6 +41,22 @@ void test_ZeigerEval()
|
|||||||
/**
|
/**
|
||||||
* @brief test if all combinations of digit
|
* @brief test if all combinations of digit
|
||||||
* evaluation are running correctly
|
* evaluation are running correctly
|
||||||
|
*
|
||||||
|
* Desciption on call undertest.PointerEvalHybridNew(float number, float number_of_predecessors, int eval_predecessors, bool Analog_Predecessors, float digitalAnalogTransitionStart)
|
||||||
|
* @param number: is the current ROI as float value from recognition
|
||||||
|
* @param number_of_predecessors: is the last (lower) ROI as float from recognition
|
||||||
|
* @param eval_predecessors: is the evaluated number. Sometimes a much lower value can change higer values
|
||||||
|
* example: 9.8, 9.9, 0.1
|
||||||
|
* 0.1 => 0 (eval_predecessors)
|
||||||
|
* The 0 makes a 9.9 to 0 (eval_predecessors)
|
||||||
|
* The 0 makes a 9.8 to 0
|
||||||
|
* @param Analog_Predecessors false/true if the last ROI is an analog or digital ROI (default=false)
|
||||||
|
* runs in special handling because analog is much less precise
|
||||||
|
* @param digitalAnalogTransitionStart start of the transitionlogic begins on number_of_predecessor (default=9.2)
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
void test_ZeigerEvalHybrid() {
|
void test_ZeigerEvalHybrid() {
|
||||||
UnderTestCNN undertest = UnderTestCNN(nullptr, Digital100);
|
UnderTestCNN undertest = UnderTestCNN(nullptr, Digital100);
|
||||||
@@ -55,11 +71,11 @@ void test_ZeigerEvalHybrid() {
|
|||||||
|
|
||||||
printf("PointerEvalHybridNew(5.7, 0, -1)\n");
|
printf("PointerEvalHybridNew(5.7, 0, -1)\n");
|
||||||
// the 5.7 and no previous should trunc to 5
|
// the 5.7 and no previous should trunc to 5
|
||||||
TEST_ASSERT_EQUAL(6, undertest.PointerEvalHybridNew(5.7, 0, -1));
|
TEST_ASSERT_EQUAL(5, undertest.PointerEvalHybridNew(5.7, 0, -1, false, 9.2));
|
||||||
|
|
||||||
// the 5.8 and no previous should round up to 6
|
// the 5.8 and no previous should round up to 6
|
||||||
printf("PointerEvalHybridNew(5.8, 0, -1)\n");
|
printf("PointerEvalHybridNew(5.8, 0, -1)\n");
|
||||||
TEST_ASSERT_EQUAL(6, undertest.PointerEvalHybridNew(5.8, 0, -1));
|
TEST_ASSERT_EQUAL(6, undertest.PointerEvalHybridNew(5.8, 8.0, 8, false, 8.0));
|
||||||
|
|
||||||
// the 5.7 with previous and the previous between 0.3-0.5 should round up to 6
|
// the 5.7 with previous and the previous between 0.3-0.5 should round up to 6
|
||||||
TEST_ASSERT_EQUAL(6, undertest.PointerEvalHybridNew(5.7, 0.4, 1));
|
TEST_ASSERT_EQUAL(6, undertest.PointerEvalHybridNew(5.7, 0.4, 1));
|
||||||
@@ -70,8 +86,9 @@ void test_ZeigerEvalHybrid() {
|
|||||||
// the 5.3 with previous and the previous <=0.5 should trunc to 5
|
// the 5.3 with previous and the previous <=0.5 should trunc to 5
|
||||||
TEST_ASSERT_EQUAL(5, undertest.PointerEvalHybridNew(5.3, 0.1, 1));
|
TEST_ASSERT_EQUAL(5, undertest.PointerEvalHybridNew(5.3, 0.1, 1));
|
||||||
|
|
||||||
// the 5.3 with previous and the previous >=9.5 should reduce to 4
|
// the 5.2 with previous and the previous >=9.8 should reduce to 4
|
||||||
TEST_ASSERT_EQUAL(4, undertest.PointerEvalHybridNew(5.3, 9.6, 9));
|
// the digit is already over transistion, but a analog pointer runs behind
|
||||||
|
TEST_ASSERT_EQUAL(4, undertest.PointerEvalHybridNew(5.2, 9.8, 9, false, 9.0));
|
||||||
|
|
||||||
// the 5.7 with previous and the previous >=9.5 should trunc to 5
|
// the 5.7 with previous and the previous >=9.5 should trunc to 5
|
||||||
TEST_ASSERT_EQUAL(5, undertest.PointerEvalHybridNew(5.7, 9.6, 9));
|
TEST_ASSERT_EQUAL(5, undertest.PointerEvalHybridNew(5.7, 9.6, 9));
|
||||||
|
|||||||
@@ -33,7 +33,17 @@ std::string process_doFlow(UnderTestPost* _underTestPost) {
|
|||||||
return _underTestPost->getReadout(0);
|
return _underTestPost->getReadout(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief setup flow like it runs after recognition.
|
||||||
|
*
|
||||||
|
* @param analog the analog recognitions as array begins with the highest ROI
|
||||||
|
* @param digits the digital regocnitions as array begins with the highest ROI
|
||||||
|
* @param digType type of the model defaults do Digital100
|
||||||
|
* @param checkConsistency for Digital type only. Not relvant for newer models
|
||||||
|
* @param extendedResolution the lowest ROI will directly used (9.7 => 9.7) if false 9.7 => 9
|
||||||
|
* @param decimal_shift the decimal point offset. -3 corresponds to x.yyy
|
||||||
|
* @return std::string the value result
|
||||||
|
*/
|
||||||
std::string process_doFlow(std::vector<float> analog, std::vector<float> digits, t_CNNType digType,
|
std::string process_doFlow(std::vector<float> analog, std::vector<float> digits, t_CNNType digType,
|
||||||
bool checkConsistency, bool extendedResolution, int decimal_shift) {
|
bool checkConsistency, bool extendedResolution, int decimal_shift) {
|
||||||
// setup the classundertest
|
// setup the classundertest
|
||||||
|
|||||||
@@ -51,21 +51,32 @@ void testNegative() {
|
|||||||
delete underTestPost;
|
delete underTestPost;
|
||||||
|
|
||||||
// extendResolution=false
|
// extendResolution=false
|
||||||
// value < preValue
|
// value < (preValue -.01)
|
||||||
preValue = 16.99; // zu groß
|
preValue = 17.00; // zu groß
|
||||||
underTestPost = init_do_flow(analogs, digits, Digital100, false, false, 0);
|
underTestPost = init_do_flow(analogs, digits, Digital100, false, false, 0);
|
||||||
setAllowNegatives(underTestPost, false);
|
setAllowNegatives(underTestPost, false);
|
||||||
setPreValue(underTestPost, preValue_extended);
|
setPreValue(underTestPost, preValue);
|
||||||
result = process_doFlow(underTestPost);
|
result = process_doFlow(underTestPost);
|
||||||
TEST_ASSERT_EQUAL_STRING("Neg. Rate - Read: - Raw: 16.98 - Pre: 16.99 ", underTestPost->getReadoutError().c_str());
|
TEST_ASSERT_EQUAL_STRING("Neg. Rate - Read: - Raw: 16.98 - Pre: 17.00 ", underTestPost->getReadoutError().c_str());
|
||||||
TEST_ASSERT_EQUAL_STRING("", result.c_str());
|
TEST_ASSERT_EQUAL_STRING("", result.c_str());
|
||||||
delete underTestPost;
|
delete underTestPost;
|
||||||
|
|
||||||
|
// extendResolution=false
|
||||||
|
// value > (preValue -.01)
|
||||||
|
// ist im Rahmen der Ungenauigkeit (-1 auf letzter Stelle)
|
||||||
|
preValue = 16.99; // zu groß
|
||||||
|
underTestPost = init_do_flow(analogs, digits, Digital100, false, false, 0);
|
||||||
|
setAllowNegatives(underTestPost, false);
|
||||||
|
setPreValue(underTestPost, preValue);
|
||||||
|
result = process_doFlow(underTestPost);
|
||||||
|
TEST_ASSERT_EQUAL_STRING("no error", underTestPost->getReadoutError().c_str());
|
||||||
|
TEST_ASSERT_EQUAL_STRING("16.99", result.c_str());
|
||||||
|
delete underTestPost;
|
||||||
|
|
||||||
// extendResolution=false
|
// extendResolution=false
|
||||||
// value < preValue
|
// value < preValue
|
||||||
// Aber Prüfung abgeschaltet => kein Fehler
|
// Aber Prüfung abgeschaltet => kein Fehler
|
||||||
preValue = 16.99; // zu groß
|
preValue = 17.99; // zu groß
|
||||||
underTestPost = init_do_flow(analogs, digits, Digital100, false, false, 0);
|
underTestPost = init_do_flow(analogs, digits, Digital100, false, false, 0);
|
||||||
setAllowNegatives(underTestPost, true);
|
setAllowNegatives(underTestPost, true);
|
||||||
setPreValue(underTestPost, preValue_extended);
|
setPreValue(underTestPost, preValue_extended);
|
||||||
@@ -84,8 +95,8 @@ void testNegative_Issues() {
|
|||||||
// Ohne decimal_shift
|
// Ohne decimal_shift
|
||||||
std::vector<float> digits = { 2.0, 2.0, 0.0, 1.0, 7.2, 9.0, 8.0};
|
std::vector<float> digits = { 2.0, 2.0, 0.0, 1.0, 7.2, 9.0, 8.0};
|
||||||
std::vector<float> analogs = { };
|
std::vector<float> analogs = { };
|
||||||
double preValue_extended = 22018.080;
|
double preValue_extended = 22018.090;
|
||||||
double preValue = 22018.08;
|
double preValue = 22018.09;
|
||||||
|
|
||||||
const char* expected = "22017.98";
|
const char* expected = "22017.98";
|
||||||
|
|
||||||
@@ -93,13 +104,15 @@ void testNegative_Issues() {
|
|||||||
// extendResolution=false
|
// extendResolution=false
|
||||||
// value < preValue
|
// value < preValue
|
||||||
// Prüfung eingeschaltet => Fehler
|
// Prüfung eingeschaltet => Fehler
|
||||||
preValue = 22018.08; // zu groß
|
preValue = 22018.09; // zu groß
|
||||||
UnderTestPost* underTestPost = init_do_flow(analogs, digits, Digital100, false, false, -2);
|
UnderTestPost* underTestPost = init_do_flow(analogs, digits, Digital100, false, false, -2);
|
||||||
setAllowNegatives(underTestPost, false);
|
setAllowNegatives(underTestPost, false);
|
||||||
setPreValue(underTestPost, preValue_extended);
|
setPreValue(underTestPost, preValue_extended);
|
||||||
std::string result = process_doFlow(underTestPost);
|
std::string result = process_doFlow(underTestPost);
|
||||||
TEST_ASSERT_EQUAL_STRING("Neg. Rate - Read: - Raw: 22017.98 - Pre: 22018.08 ", underTestPost->getReadoutError().c_str());
|
TEST_ASSERT_EQUAL_STRING("Neg. Rate - Read: - Raw: 22017.98 - Pre: 22018.08 ", underTestPost->getReadoutError().c_str());
|
||||||
TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
|
// if negativ no result any more
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL_STRING("", result.c_str());
|
||||||
delete underTestPost;
|
delete underTestPost;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "test_flow_postrocess_helper.h"
|
#include "test_flow_postrocess_helper.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ACHTUNG! Die Test laufen aktuell nur mit ausgeschaltetem Debug in ClassFlowCNNGeneral
|
* ACHTUNG! Die Test laufen aktuell nur mit ausgeschaltetem Debug in ClassFlowCNNGeneral
|
||||||
@@ -114,28 +113,28 @@ void test_doFlowPP1() {
|
|||||||
// https://github.com/jomjol/AI-on-the-edge-device/issues/942#issuecomment-1226966346
|
// https://github.com/jomjol/AI-on-the-edge-device/issues/942#issuecomment-1226966346
|
||||||
std::vector<float> digits = { 0.0, 2.9, 3.0, 2.9, 3.5, 9.5};
|
std::vector<float> digits = { 0.0, 2.9, 3.0, 2.9, 3.5, 9.5};
|
||||||
std::vector<float> analogs = { };
|
std::vector<float> analogs = { };
|
||||||
const char* expected = "33330";
|
const char* expected = "33339";
|
||||||
std::string result = process_doFlow(analogs, digits);
|
std::string result = process_doFlow(analogs, digits);
|
||||||
TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
|
TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
|
||||||
|
|
||||||
// https://github.com/jomjol/AI-on-the-edge-device/issues/942#issuecomment-1226966346
|
// https://github.com/jomjol/AI-on-the-edge-device/issues/942#issuecomment-1226966346
|
||||||
digits = { 9.9, 2.8, 2.9, 2.9, 3.7, 9.7};
|
digits = { 9.9, 2.8, 2.9, 2.9, 3.7, 9.7};
|
||||||
analogs = { };
|
analogs = { };
|
||||||
expected = "33340";
|
expected = "33339";
|
||||||
result = process_doFlow(analogs, digits);
|
result = process_doFlow(analogs, digits);
|
||||||
TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
|
TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
|
||||||
|
|
||||||
// https://github.com/jomjol/AI-on-the-edge-device/issues/942
|
// https://github.com/jomjol/AI-on-the-edge-device/issues/942
|
||||||
digits = { 0.0, 9.9, 6.8, 9.9, 3.7, 0.8, 6.9, 8.7};
|
digits = { 0.0, 9.9, 6.8, 9.9, 3.7, 0.8, 6.9, 8.7};
|
||||||
analogs = { };
|
analogs = { };
|
||||||
expected = "704179";
|
expected = "704178";
|
||||||
result = process_doFlow(analogs, digits);
|
result = process_doFlow(analogs, digits);
|
||||||
TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
|
TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
|
||||||
|
|
||||||
// https://github.com/jomjol/AI-on-the-edge-device/issues/942#issuecomment-1228343319
|
// https://github.com/jomjol/AI-on-the-edge-device/issues/942#issuecomment-1228343319
|
||||||
digits = { 9.9, 6.8, 1.1, 4.7, 2.7, 6.0, 9.0, 2.8}; // changed 3.7 --> 2.7 (see picture in issue)
|
digits = { 9.9, 6.8, 1.1, 4.7, 2.7, 6.0, 9.0, 2.8}; // changed 3.7 --> 2.7 (see picture in issue)
|
||||||
analogs = { };
|
analogs = { };
|
||||||
expected = "7153693";
|
expected = "7153692";
|
||||||
result = process_doFlow(analogs, digits);
|
result = process_doFlow(analogs, digits);
|
||||||
TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
|
TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
|
||||||
|
|
||||||
@@ -185,7 +184,7 @@ void test_doFlowPP2() {
|
|||||||
// https://github.com/jomjol/AI-on-the-edge-device/issues/921#issuecomment-1242730397
|
// https://github.com/jomjol/AI-on-the-edge-device/issues/921#issuecomment-1242730397
|
||||||
digits = { 3.0, 2.0, 2.0, 8.0, 9.0, 4.0, 1.7, 9.8}; // falscher Wert 32290.420
|
digits = { 3.0, 2.0, 2.0, 8.0, 9.0, 4.0, 1.7, 9.8}; // falscher Wert 32290.420
|
||||||
analogs = { };
|
analogs = { };
|
||||||
expected = "32289.420";
|
expected = "32289.419";
|
||||||
const char* expected_extended= "32289.4198";
|
const char* expected_extended= "32289.4198";
|
||||||
// FALSCH! wegen ungenügender Präzision von NUMBERS->Value
|
// FALSCH! wegen ungenügender Präzision von NUMBERS->Value
|
||||||
// expected_extended= "32289.4198";
|
// expected_extended= "32289.4198";
|
||||||
@@ -230,7 +229,7 @@ void test_doFlowPP2() {
|
|||||||
// https://github.com/jomjol/AI-on-the-edge-device/issues/994#issue-1368570945
|
// https://github.com/jomjol/AI-on-the-edge-device/issues/994#issue-1368570945
|
||||||
digits = { 0.0, 0.0, 1.0, 2.0, 2.8, 1.9, 2.8, 5.6}; // 123245.6 als falsches Ergebnis
|
digits = { 0.0, 0.0, 1.0, 2.0, 2.8, 1.9, 2.8, 5.6}; // 123245.6 als falsches Ergebnis
|
||||||
analogs = { };
|
analogs = { };
|
||||||
expected = "123236";
|
expected = "123235";
|
||||||
expected_extended= "123235.6";
|
expected_extended= "123235.6";
|
||||||
|
|
||||||
// checkConsistency=true
|
// checkConsistency=true
|
||||||
@@ -542,4 +541,64 @@ void test_doFlowPP4() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string postProcess(std::vector<float> digits,
|
||||||
|
std::vector<float> analogs,
|
||||||
|
float analog2DigitalTransition=0.0)
|
||||||
|
{
|
||||||
|
std::unique_ptr<UnderTestPost> undertestPost(init_do_flow(std::move(analogs),
|
||||||
|
std::move(digits),
|
||||||
|
Digital100,
|
||||||
|
false, false));
|
||||||
|
|
||||||
|
setAnalogdigitTransistionStart(undertestPost.get(), analog2DigitalTransition);
|
||||||
|
return process_doFlow(undertestPost.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_doFlowLateTransition()
|
||||||
|
{
|
||||||
|
// in these test cases, the last digit before comma turns 3.6 too late
|
||||||
|
float a2dt = 3.6;
|
||||||
|
|
||||||
|
// meter shows 011.0210 but it already needs to be 012.0210, before transition
|
||||||
|
TEST_ASSERT_EQUAL_STRING("12.0210", postProcess({0.0, 1.0, 1.0}, {0.2, 2.2, 1.0, 0.0}, a2dt).c_str());
|
||||||
|
|
||||||
|
// meter shows 011.3210 but it already needs to be 012.3210, just before transition
|
||||||
|
TEST_ASSERT_EQUAL_STRING("12.3210", postProcess({0.0, 1.0, 1.2}, {3.3, 2.2, 1.0, 0.0}, a2dt).c_str());
|
||||||
|
|
||||||
|
// meter shows 012.4210 , this is after transition
|
||||||
|
TEST_ASSERT_EQUAL_STRING("12.4210", postProcess({0.0, 1.0, 2.0}, {4.3, 2.2, 1.0, 0.0}, a2dt).c_str());
|
||||||
|
|
||||||
|
// meter shows 012.987
|
||||||
|
TEST_ASSERT_EQUAL_STRING("12.9870", postProcess({0.0, 1.0, 2.0}, {9.8, 8.7, 7.0, 0.0}, a2dt).c_str());
|
||||||
|
|
||||||
|
// meter shows 0012.003
|
||||||
|
TEST_ASSERT_EQUAL_STRING("13.003", postProcess({0.0, 0.0, 1.0, 2.0}, {0.1, 0.3, 3.1}, a2dt).c_str());
|
||||||
|
|
||||||
|
// meter shows 0012.351
|
||||||
|
TEST_ASSERT_EQUAL_STRING("13.351", postProcess({0.0, 0.0, 1.0, 2.8}, {3.5, 5.2, 1.1}, a2dt).c_str());
|
||||||
|
|
||||||
|
// meter shows 0013.421
|
||||||
|
TEST_ASSERT_EQUAL_STRING("13.421", postProcess({0.0, 0.0, 1.0, 3.0}, {4.1, 2.2, 1.1}, a2dt).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_doFlowEarlyTransition()
|
||||||
|
{
|
||||||
|
// in these test cases, the last digit before comma turns at around 7.5
|
||||||
|
// start transition 7.0 end transition 8.0
|
||||||
|
float a2dt = 7.5;
|
||||||
|
|
||||||
|
// meter shows 011.0210 but it already needs to be 012.0210, before transition
|
||||||
|
TEST_ASSERT_EQUAL_STRING("12.6789", postProcess({0.0, 1.0, 2.0}, {6.7, 7.8, 8.9, 9.0}, a2dt).c_str());
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL_STRING("12.7234", postProcess({0.0, 1.0, 2.4}, {7.2, 2.3, 3.4, 4.0}, a2dt).c_str());
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL_STRING("12.7789", postProcess({0.0, 1.0, 2.7}, {7.7, 7.8, 8.9, 9.0}, a2dt).c_str());
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL_STRING("12.8123", postProcess({0.0, 1.0, 3.0}, {8.1, 1.2, 2.3, 3.0}, a2dt).c_str());
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL_STRING("13.1234", postProcess({0.0, 1.0, 3.0}, {1.2, 2.3, 3.4, 4.0}, a2dt).c_str());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
#include <unity.h>
|
#include <unity.h>
|
||||||
|
|
||||||
#include "components/jomjol-flowcontroll/test_flow_postrocess_helper.cpp"
|
|
||||||
#include "components/jomjol-flowcontroll/test_flowpostprocessing.cpp"
|
|
||||||
#include "components/jomjol-flowcontroll/test_flow_pp_negative.cpp"
|
|
||||||
#include "components/jomjol-flowcontroll/test_PointerEvalAnalogToDigitNew.cpp"
|
|
||||||
#include "components/jomjol-flowcontroll/test_getReadoutRawString.cpp"
|
|
||||||
// SD-Card ////////////////////
|
// SD-Card ////////////////////
|
||||||
#include "nvs_flash.h"
|
#include "nvs_flash.h"
|
||||||
#include "esp_vfs_fat.h"
|
#include "esp_vfs_fat.h"
|
||||||
@@ -15,6 +10,18 @@
|
|||||||
#define __SD_USE_ONE_LINE_MODE__
|
#define __SD_USE_ONE_LINE_MODE__
|
||||||
#include "server_GPIO.h"
|
#include "server_GPIO.h"
|
||||||
|
|
||||||
|
|
||||||
|
//*****************************************************************************
|
||||||
|
// Include files with functions to test
|
||||||
|
//*****************************************************************************
|
||||||
|
#include "components/jomjol-flowcontroll/test_flow_postrocess_helper.cpp"
|
||||||
|
#include "components/jomjol-flowcontroll/test_flowpostprocessing.cpp"
|
||||||
|
#include "components/jomjol-flowcontroll/test_flow_pp_negative.cpp"
|
||||||
|
#include "components/jomjol-flowcontroll/test_PointerEvalAnalogToDigitNew.cpp"
|
||||||
|
#include "components/jomjol-flowcontroll/test_getReadoutRawString.cpp"
|
||||||
|
#include "components/jomjol-flowcontroll/test_cnnflowcontroll.cpp"
|
||||||
|
|
||||||
|
|
||||||
bool Init_NVS_SDCard()
|
bool Init_NVS_SDCard()
|
||||||
{
|
{
|
||||||
esp_err_t ret = nvs_flash_init();
|
esp_err_t ret = nvs_flash_init();
|
||||||
@@ -81,7 +88,6 @@ bool Init_NVS_SDCard()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void initGPIO()
|
void initGPIO()
|
||||||
{
|
{
|
||||||
gpio_config_t io_conf;
|
gpio_config_t io_conf;
|
||||||
@@ -96,11 +102,51 @@ void initGPIO()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief startup the test. Like a test-suite
|
* @brief startup the test. Like a test-suite
|
||||||
* all test methods must be called here
|
* all test methods must be called here
|
||||||
*/
|
*/
|
||||||
|
void task_UnityTesting(void *pvParameter)
|
||||||
|
{
|
||||||
|
vTaskDelay( 5000 / portTICK_PERIOD_MS ); // 5s delay to ensure established serial connection
|
||||||
|
|
||||||
|
UNITY_BEGIN();
|
||||||
|
RUN_TEST(test_getReadoutRawString);
|
||||||
|
printf("---------------------------------------------------------------------------\n");
|
||||||
|
|
||||||
|
RUN_TEST(test_ZeigerEval);
|
||||||
|
printf("---------------------------------------------------------------------------\n");
|
||||||
|
RUN_TEST(test_ZeigerEvalHybrid);
|
||||||
|
printf("---------------------------------------------------------------------------\n");
|
||||||
|
|
||||||
|
RUN_TEST(testNegative_Issues);
|
||||||
|
printf("---------------------------------------------------------------------------\n");
|
||||||
|
RUN_TEST(testNegative);
|
||||||
|
printf("---------------------------------------------------------------------------\n");
|
||||||
|
|
||||||
|
RUN_TEST(test_analogToDigit_Standard);
|
||||||
|
printf("---------------------------------------------------------------------------\n");
|
||||||
|
RUN_TEST(test_analogToDigit_Transition);
|
||||||
|
printf("---------------------------------------------------------------------------\n");
|
||||||
|
|
||||||
|
RUN_TEST(test_doFlowPP);
|
||||||
|
printf("---------------------------------------------------------------------------\n");
|
||||||
|
RUN_TEST(test_doFlowPP1);
|
||||||
|
printf("---------------------------------------------------------------------------\n");
|
||||||
|
RUN_TEST(test_doFlowPP2);
|
||||||
|
printf("---------------------------------------------------------------------------\n");
|
||||||
|
RUN_TEST(test_doFlowPP3);
|
||||||
|
printf("---------------------------------------------------------------------------\n");
|
||||||
|
RUN_TEST(test_doFlowPP4);
|
||||||
|
UNITY_END();
|
||||||
|
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief main task
|
||||||
|
*/
|
||||||
extern "C" void app_main()
|
extern "C" void app_main()
|
||||||
{
|
{
|
||||||
initGPIO();
|
initGPIO();
|
||||||
@@ -109,8 +155,8 @@ extern "C" void app_main()
|
|||||||
|
|
||||||
UNITY_BEGIN();
|
UNITY_BEGIN();
|
||||||
RUN_TEST(testNegative_Issues);
|
RUN_TEST(testNegative_Issues);
|
||||||
/* RUN_TEST(testNegative);
|
RUN_TEST(testNegative);
|
||||||
|
/*
|
||||||
RUN_TEST(test_analogToDigit_Standard);
|
RUN_TEST(test_analogToDigit_Standard);
|
||||||
RUN_TEST(test_analogToDigit_Transition);
|
RUN_TEST(test_analogToDigit_Transition);
|
||||||
RUN_TEST(test_doFlowPP);
|
RUN_TEST(test_doFlowPP);
|
||||||
@@ -118,6 +164,8 @@ extern "C" void app_main()
|
|||||||
RUN_TEST(test_doFlowPP2);
|
RUN_TEST(test_doFlowPP2);
|
||||||
RUN_TEST(test_doFlowPP3);
|
RUN_TEST(test_doFlowPP3);
|
||||||
RUN_TEST(test_doFlowPP4);
|
RUN_TEST(test_doFlowPP4);
|
||||||
|
RUN_TEST(test_doFlowLateTransition);
|
||||||
|
RUN_TEST(test_doFlowEarlyTransition);
|
||||||
|
|
||||||
// getReadoutRawString test
|
// getReadoutRawString test
|
||||||
RUN_TEST(test_getReadoutRawString);
|
RUN_TEST(test_getReadoutRawString);
|
||||||
|
|||||||
Binary file not shown.
@@ -18,16 +18,19 @@
|
|||||||
|
|
||||||
For further information about AI-on-the-edge-device please go to <a href=https://github.com/jomjol/AI-on-the-edge-device target=_blank>https://github.com/jomjol/AI-on-the-edge-device</a>.</p>
|
For further information about AI-on-the-edge-device please go to <a href=https://github.com/jomjol/AI-on-the-edge-device target=_blank>https://github.com/jomjol/AI-on-the-edge-device</a>.</p>
|
||||||
|
|
||||||
<p>Notes:</p>
|
<h2>Notes:</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>For the installation, make sure to switch the ESP32 to Bootloader mode!</li>
|
<li>The Webinstall will install the latest firmware (Version <b>$VERSION</b>).</li>
|
||||||
|
<li>For the installation, make sure to switch the ESP32 to Bootloader mode by keeping the <b>FLASH</b> button pressed while the <b>RESET</b> button gets relesed. if there is no <b>FLASH</b> button, you need to pull <b>GPIO0</b> low!</li>
|
||||||
<li>After the installation, a manual reset might be required!</li>
|
<li>After the installation, a manual reset might be required!</li>
|
||||||
<li>Please note that not all webbrowsers and operating systems support the needed access to USB!</li>
|
<li>Please note that not all web browsers and operating systems support the necessary USB access needed for this Webinstaller!</li>
|
||||||
<li>Check the <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/Installation target=_blank>documentation</a> for additional information.</li>
|
<li>Check the <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/Installation target=_blank>documentation</a> for additional information.</li>
|
||||||
<li>The SD-Card still must be setup separately. This can be done manually or using the new <b>Remote Setup</b>. See the <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#3-sd-card target=_blank>documentation</a> for further instructions!</li>
|
<li>The SD card can be setup automatically after the firmware got installed. See the <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#remote-setup-using-the-built-in-access-point target=_blank>documentation</a> for details. For this to work, the SD card must be FAT formated (which is the default on a new SD card).
|
||||||
|
Alternatively the SD card still can be setup manually, see the <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#3-sd-card target=_blank>documentation</a> for details!</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
<p><esp-web-install-button manifest="manifest.json"></esp-web-install-button></p>
|
<p><esp-web-install-button manifest="manifest.json"></esp-web-install-button></p>
|
||||||
<hr>
|
<hr>
|
||||||
<p style="font-size: small;">Installer and Console powered by <a href=https://esphome.github.io/esp-web-tools/ target=_blank>ESP Web Tools</a></p>
|
<p style="font-size: small;">Installer and Console powered by <a href=https://esphome.github.io/esp-web-tools/ target=_blank>ESP Web Tools</a></p>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "AI-on-the-edge",
|
"name": "AI-on-the-edge",
|
||||||
"version": "13.0.8",
|
"version": "$VERSION",
|
||||||
"funding_url": "https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL",
|
"funding_url": "https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL",
|
||||||
"new_install_prompt_erase": false,
|
"new_install_prompt_erase": false,
|
||||||
"builds": [
|
"builds": [
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "AI-on-the-edge",
|
|
||||||
"version": "VERSION",
|
|
||||||
"funding_url": "https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL",
|
|
||||||
"new_install_prompt_erase": false,
|
|
||||||
"builds": [
|
|
||||||
{
|
|
||||||
"chipFamily": "ESP32",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"path": "binary/bootloader.bin",
|
|
||||||
"offset": 4096
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "binary/partitions.bin",
|
|
||||||
"offset": 32768
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "binary/firmware.bin",
|
|
||||||
"offset": 65536
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
# Firmware
|
|
||||||
The firmware got moved to the [Release page](https://github.com/jomjol/AI-on-the-edge-device/releases).
|
|
||||||
|
|
||||||
# Installation Guide
|
|
||||||
|
|
||||||
You find the complete installation guide at https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/
|
|
||||||
BIN
images/paypal.png
Normal file
BIN
images/paypal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
BIN
images/web-installer.png
Normal file
BIN
images/web-installer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
3
param-docs/.idea/.gitignore
generated
vendored
Normal file
3
param-docs/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
8
param-docs/.idea/generate-param-docs.iml
generated
Normal file
8
param-docs/.idea/generate-param-docs.iml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.10" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
6
param-docs/.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
param-docs/.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
4
param-docs/.idea/misc.xml
generated
Normal file
4
param-docs/.idea/misc.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
||||||
8
param-docs/.idea/modules.xml
generated
Normal file
8
param-docs/.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/generate-param-docs.iml" filepath="$PROJECT_DIR$/.idea/generate-param-docs.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
param-docs/.idea/vcs.xml
generated
Normal file
6
param-docs/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
17
param-docs/README.md
Normal file
17
param-docs/README.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Parameter Documentation
|
||||||
|
Each parameter which is listed in the [configfile](https://github.com/jomjol/AI-on-the-edge-device/blob/rolling/sd-card/config/config.ini) has its own description page in the folder `parameter-pages` (grouped by the config sections).
|
||||||
|
Those pages can be edited as needed.
|
||||||
|
|
||||||
|
During a Github action build, those parameter pages will be used to generate the tooltips in the web interface. And they also are used to build the [Online Documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Parameters).
|
||||||
|
|
||||||
|
If you create or rename a parameter, make sure to also update its description page!
|
||||||
|
|
||||||
|
## Template Generator
|
||||||
|
The script `generate-template-param-doc-pages.py` should be run whenever a new parameter gets added to the config file.
|
||||||
|
It then checks if there is already a page for each of the parameters.
|
||||||
|
- If no page exists yet, a templated page gets generated.
|
||||||
|
- Existing pages do not get modified.
|
||||||
|
|
||||||
|
If the parameter is listed in `expert-params.txt`, an **Expert warning** will be shown.
|
||||||
|
|
||||||
|
If the parameter is listed in `hidden-in-ui.txt`, a **Note** will be shown.
|
||||||
31
param-docs/expert-params.txt
Normal file
31
param-docs/expert-params.txt
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
demo
|
||||||
|
WaitBeforeTakingPicture
|
||||||
|
ImageQuality
|
||||||
|
ImageSize
|
||||||
|
LEDIntensity
|
||||||
|
Brightness
|
||||||
|
Contrast
|
||||||
|
Saturation
|
||||||
|
FixedExposure
|
||||||
|
SearchFieldX
|
||||||
|
SearchFieldY
|
||||||
|
AlignmentAlgo
|
||||||
|
InitialMirror
|
||||||
|
FlipImageSize
|
||||||
|
CNNGoodThreshold
|
||||||
|
PreValueAgeStartup
|
||||||
|
ErrorMessage
|
||||||
|
CheckDigitIncreaseConsistency
|
||||||
|
IO0
|
||||||
|
IO1
|
||||||
|
IO3
|
||||||
|
IO4
|
||||||
|
IO12
|
||||||
|
IO13
|
||||||
|
AutoStart
|
||||||
|
Hostname
|
||||||
|
RSSIThreshold
|
||||||
|
TimeServer
|
||||||
|
CACert
|
||||||
|
ClientCert
|
||||||
|
ClientKey
|
||||||
95
param-docs/generate-template-param-doc-pages.py
Normal file
95
param-docs/generate-template-param-doc-pages.py
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
"""
|
||||||
|
For each parameter which can be found in the config file,
|
||||||
|
create a markdown file with a templated content if it does not exist yet.
|
||||||
|
The files are grouped in sub folders representing the config sections.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import configparser
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
|
||||||
|
configFileUrl = "https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/rolling/sd-card/config/config.ini"
|
||||||
|
|
||||||
|
parameterDocsFolder = "parameter-pages"
|
||||||
|
parameterTemplateFile = "./templates/parameter.md"
|
||||||
|
expertParameterListFile = "./expert-params.txt"
|
||||||
|
hiddenInUiParameterListFile = "./hidden-in-ui.txt"
|
||||||
|
|
||||||
|
|
||||||
|
# Fetch default config file from URL
|
||||||
|
print("Fetching %r..." % configFileUrl)
|
||||||
|
with urllib.request.urlopen(configFileUrl) as response:
|
||||||
|
content = response.read().decode("utf-8")
|
||||||
|
|
||||||
|
lines = str(content).split("\n")
|
||||||
|
|
||||||
|
for l in range(len(lines)):
|
||||||
|
lines[l] = lines[l].strip() + "\n"
|
||||||
|
if lines[l][0] == ";":
|
||||||
|
lines[l] = lines[l][1:] # Remove comment
|
||||||
|
|
||||||
|
content = "".join(lines)
|
||||||
|
|
||||||
|
# Fetch list of expert parameters
|
||||||
|
with open(expertParameterListFile) as f:
|
||||||
|
expertParameters = f.read().splitlines()
|
||||||
|
|
||||||
|
# Fetch list of parameters not available through the UI
|
||||||
|
with open(hiddenInUiParameterListFile) as f:
|
||||||
|
hiddenInUiParameters = f.read().splitlines()
|
||||||
|
|
||||||
|
|
||||||
|
config = configparser.ConfigParser(allow_no_value=True)
|
||||||
|
config.optionxform = str # Make it case-insensitive
|
||||||
|
config.read_string(content)
|
||||||
|
|
||||||
|
#shutil.rmtree(parameterDocsFolder)
|
||||||
|
if not os.path.exists(parameterDocsFolder):
|
||||||
|
os.mkdir(parameterDocsFolder)
|
||||||
|
|
||||||
|
with open(parameterTemplateFile, 'r') as parameterTemplateFileHandle:
|
||||||
|
parameterTemplate = parameterTemplateFileHandle.read()
|
||||||
|
|
||||||
|
|
||||||
|
print("For each section/parameter, check if there is already a documentation page in the folder %r..." % (os.getcwd() + "/" + parameterDocsFolder))
|
||||||
|
for section in config:
|
||||||
|
if section != "DEFAULT":
|
||||||
|
#print(section)
|
||||||
|
|
||||||
|
subFolder = parameterDocsFolder + "/" + section
|
||||||
|
|
||||||
|
if not os.path.exists(subFolder):
|
||||||
|
os.mkdir(subFolder)
|
||||||
|
|
||||||
|
for parameter in config[section]:
|
||||||
|
if not " " in parameter: # Ignore parameters with whitespaces in them (special format, not part of editable config)
|
||||||
|
value = config[section][parameter]
|
||||||
|
#print(" %s = %s" % (parameter, value))
|
||||||
|
|
||||||
|
if "main." in parameter:
|
||||||
|
parameter = parameter.replace("main.", "NUMBER.")
|
||||||
|
|
||||||
|
"""
|
||||||
|
For each config line, create a markdown file
|
||||||
|
"""
|
||||||
|
parameterDocFile = subFolder + '/' + parameter + ".md"
|
||||||
|
|
||||||
|
if not os.path.exists(parameterDocFile): # File does not exist yet, generate template
|
||||||
|
print("%r does not exit yet, generating a templated file for it" % (os.getcwd() + "/" + parameterDocFile))
|
||||||
|
with open(parameterDocFile, 'w') as paramFileHandle:
|
||||||
|
content = parameterTemplate
|
||||||
|
content = content.replace("$NAME", parameter)
|
||||||
|
content = content.replace("$DEFAULT", value)
|
||||||
|
|
||||||
|
if parameter in expertParameters:
|
||||||
|
content = content.replace("$EXPERT_PARAMETER", "!!! Warning\n This is an **Expert Parameter**! Only change it if you understand what it does!") # Note: Needs a 4 whitespace Intent!
|
||||||
|
else:
|
||||||
|
content = content.replace("$EXPERT_PARAMETER", "")
|
||||||
|
|
||||||
|
if parameter in hiddenInUiParameters:
|
||||||
|
content = content.replace("$HIDDEN_IN_UI", "!!! Note\n This parameter is not accessible through the Web Interface Configuration Page!") # Note: Needs a 4 whitespace Intent!
|
||||||
|
else:
|
||||||
|
content = content.replace("$HIDDEN_IN_UI", "")
|
||||||
|
|
||||||
|
paramFileHandle.write(content)
|
||||||
4
param-docs/hidden-in-ui.txt
Normal file
4
param-docs/hidden-in-ui.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
InitialRotate
|
||||||
|
MainTopicMQTT
|
||||||
|
AutoAdjustSummertime
|
||||||
|
SetupMode
|
||||||
14
param-docs/parameter-pages/Alignment/AlignmentAlgo.md
Normal file
14
param-docs/parameter-pages/Alignment/AlignmentAlgo.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Parameter `AlignmentAlgo`
|
||||||
|
Default Value: `Default`
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This is an **Expert Parameter**! Only change it if you understand what it does!
|
||||||
|
|
||||||
|
Algorithm used for the alignment step.
|
||||||
|
|
||||||
|
Available options:
|
||||||
|
|
||||||
|
- `Default`: Use only red color channel
|
||||||
|
- `HighAccuracy`: Use all 3 color channels (3x slower)
|
||||||
|
- `Fast`: First time use `HighAccuracy`, then only check if the image is shifted
|
||||||
|
- `Off`: Disable alignment algorithm
|
||||||
11
param-docs/parameter-pages/Alignment/FlipImageSize.md
Normal file
11
param-docs/parameter-pages/Alignment/FlipImageSize.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Parameter `FlipImageSize`
|
||||||
|
Default Value: `false`
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This is an **Expert Parameter**! Only change it if you understand what it does!
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
This parameter can also be set on the Reference Image configuration.
|
||||||
|
|
||||||
|
This parameter can be used to rotate the viewport together with the alignment rotation:
|
||||||
|

|
||||||
10
param-docs/parameter-pages/Alignment/InitialMirror.md
Normal file
10
param-docs/parameter-pages/Alignment/InitialMirror.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Parameter `InitialMirror`
|
||||||
|
Default Value: `false`
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This is an **Expert Parameter**! Only change it if you understand what it does!
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
This parameter can also be set on the Reference Image configuration.
|
||||||
|
|
||||||
|
Option for initially mirroring the image on the original x-axis.
|
||||||
9
param-docs/parameter-pages/Alignment/InitialRotate.md
Normal file
9
param-docs/parameter-pages/Alignment/InitialRotate.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Parameter `InitialRotate`
|
||||||
|
Default Value: `179`
|
||||||
|
|
||||||
|
Unit: Degrees
|
||||||
|
|
||||||
|
Initial rotation of image before alignment in degree (0 .. 359)
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
This parameter is accessible on the Reference Image Page but not on the Config page!
|
||||||
14
param-docs/parameter-pages/Alignment/SearchFieldX.md
Normal file
14
param-docs/parameter-pages/Alignment/SearchFieldX.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Parameter `SearchFieldX`
|
||||||
|
Default Value: `20`
|
||||||
|
|
||||||
|
Unit: Pixels
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This is an **Expert Parameter**! Only change it if you understand what it does!
|
||||||
|
|
||||||
|
X-size (width) in which the reference is searched.
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
Since the alignment is one of the steps using a lot of computation time,
|
||||||
|
the search field should be as small as possible.
|
||||||
|
The calculation time goes quadratic with the search field size.
|
||||||
14
param-docs/parameter-pages/Alignment/SearchFieldY.md
Normal file
14
param-docs/parameter-pages/Alignment/SearchFieldY.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Parameter `SearchFieldY`
|
||||||
|
Default Value: `20`
|
||||||
|
|
||||||
|
Unit: Pixels
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This is an **Expert Parameter**! Only change it if you understand what it does!
|
||||||
|
|
||||||
|
Y-size (height) in which the reference is searched.
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
Since the alignment is one of the steps using a lot of computation time,
|
||||||
|
the search field should be as small as possible.
|
||||||
|
The calculation time goes quadratic with the search field size.
|
||||||
10
param-docs/parameter-pages/Analog/CNNGoodThreshold.md
Normal file
10
param-docs/parameter-pages/Analog/CNNGoodThreshold.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Parameter `CNNGoodThreshold`
|
||||||
|
Default Value: `0.5`
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This is an **Expert Parameter**! Only change it if you understand what it does!
|
||||||
|
|
||||||
|
Threshold above which the classification should be to accept the value (only meaningful for digits).
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This is only supported for the `ana-class100` models!
|
||||||
5
param-docs/parameter-pages/Analog/ExtendedResolution.md
Normal file
5
param-docs/parameter-pages/Analog/ExtendedResolution.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Parameter `ExtendedResolution`
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This parameter is unused!
|
||||||
|
Use [`NUMBER.ExtendedResolution`](../Parameters/#PostProcessing-NUMBER.ExtendedResolution) instead!
|
||||||
4
param-docs/parameter-pages/Analog/Model.md
Normal file
4
param-docs/parameter-pages/Analog/Model.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Parameter `Model`
|
||||||
|
Default Value: `/config/ana-cont_*.tflite` (See [/config/config.ini](https://github.com/jomjol/AI-on-the-edge-device/blob/master/sd-card/config/config.ini))
|
||||||
|
|
||||||
|
Path to CNN model file for image recognition. See [here](../Choosing-the-Model) for details.
|
||||||
7
param-docs/parameter-pages/Analog/ROIImagesLocation.md
Normal file
7
param-docs/parameter-pages/Analog/ROIImagesLocation.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Parameter `ROIImagesLocation`
|
||||||
|
Default Value: `/log/analog`
|
||||||
|
|
||||||
|
Location to store separated analog images on the SD-Card.
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
A SD-Card has limited write cycles. Since the device does not do [Wear Leveling](https://en.wikipedia.org/wiki/Wear_leveling), this can wear out your SD-Card!
|
||||||
6
param-docs/parameter-pages/Analog/ROIImagesRetention.md
Normal file
6
param-docs/parameter-pages/Analog/ROIImagesRetention.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Parameter `ROIImagesRetention`
|
||||||
|
Default Value: `3`
|
||||||
|
|
||||||
|
Unit: Days
|
||||||
|
|
||||||
|
Days to keep the separated analog images (`0` = forever).
|
||||||
12
param-docs/parameter-pages/AutoTimer/AutoStart.md
Normal file
12
param-docs/parameter-pages/AutoTimer/AutoStart.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Parameter `AutoStart`
|
||||||
|
Default Value: `true`
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This is an **Expert Parameter**! Only change it if you understand what it does!
|
||||||
|
|
||||||
|
Automatically start the Flow (Digitization Rounds) immediately after power up.
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
Typically this is set to `true`.
|
||||||
|
The main reasons to set it to `false` is when you want to trigger it manually using the
|
||||||
|
[REST API](../REST-API) or [MQTT-API](../MQTT-API) or for debugging.
|
||||||
7
param-docs/parameter-pages/AutoTimer/Interval.md
Normal file
7
param-docs/parameter-pages/AutoTimer/Interval.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Parameter `Interval`
|
||||||
|
Default Value: `5`
|
||||||
|
|
||||||
|
Unit: Minutes
|
||||||
|
|
||||||
|
Interval in which the Flow (Digitization Round) is run.
|
||||||
|
If a round takes longer than this interval, the next round gets postponed until the current round completes.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# Parameter `DataFilesRetention`
|
||||||
|
Default Value: `3`
|
||||||
|
|
||||||
|
Unit: Days
|
||||||
|
|
||||||
|
Number of days to keep the data files (`0` = forever).
|
||||||
8
param-docs/parameter-pages/DataLogging/DataLogActive.md
Normal file
8
param-docs/parameter-pages/DataLogging/DataLogActive.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Parameter `DataLogActive`
|
||||||
|
Default Value: `true`
|
||||||
|
Activate data logging to the SD-Card.
|
||||||
|
|
||||||
|
The files will be stored in `/log/data/data_YYYY-MM-DD.csv`. See [`Data Logging`](../data-logging) for details.
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
A SD-Card has limited write cycles. Since the device does not do [Wear Leveling](https://en.wikipedia.org/wiki/Wear_leveling), this can wear out your SD-Card!
|
||||||
16
param-docs/parameter-pages/Debug/LogLevel.md
Normal file
16
param-docs/parameter-pages/Debug/LogLevel.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Parameter `LogLevel`
|
||||||
|
Default Value: `1` (`ERROR`)
|
||||||
|
Define the log level for the logging to the SD-Card.
|
||||||
|
|
||||||
|
Available options:
|
||||||
|
|
||||||
|
- `1`: `ERROR`
|
||||||
|
- `2`: `WARNING`
|
||||||
|
- `3`: `INFO`
|
||||||
|
- `4`: `DEBUG`
|
||||||
|
|
||||||
|
As higher the level, as more log messages get written to the SD-Card.
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
`DEBUG` or `INFO` might damage the SD-Card if enabled long term due to excessive writes to the SD-Card!
|
||||||
|
A SD-Card has limited write cycles. Since the device does not do [Wear Leveling](https://en.wikipedia.org/wiki/Wear_leveling), this can wear out your SD-Card!
|
||||||
6
param-docs/parameter-pages/Debug/LogfilesRetention.md
Normal file
6
param-docs/parameter-pages/Debug/LogfilesRetention.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Parameter `LogfilesRetention`
|
||||||
|
Default Value: `3`
|
||||||
|
|
||||||
|
Unit: Days
|
||||||
|
|
||||||
|
Number of days to keep the log files (`0` = forever).
|
||||||
10
param-docs/parameter-pages/Digits/CNNGoodThreshold.md
Normal file
10
param-docs/parameter-pages/Digits/CNNGoodThreshold.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Parameter `CNNGoodThreshold`
|
||||||
|
Default Value: `0.5`
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This is an **Expert Parameter**! Only change it if you understand what it does!
|
||||||
|
|
||||||
|
Threshold above which the classification should be to accept the value (only meaningful for digits).
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This is only supported for the `dig-class100` models!
|
||||||
4
param-docs/parameter-pages/Digits/Model.md
Normal file
4
param-docs/parameter-pages/Digits/Model.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Parameter `Model`
|
||||||
|
Default Value: `/config/dig-cont_*.tflite` (See [/config/config.ini](https://github.com/jomjol/AI-on-the-edge-device/blob/master/sd-card/config/config.ini))
|
||||||
|
|
||||||
|
Path to CNN model file for image recognition. See [here](../Choosing-the-Model) for details.
|
||||||
7
param-docs/parameter-pages/Digits/ROIImagesLocation.md
Normal file
7
param-docs/parameter-pages/Digits/ROIImagesLocation.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Parameter `ROIImagesLocation`
|
||||||
|
Default Value: `/log/digit`
|
||||||
|
|
||||||
|
Location to store separated digit images on the SD-Card.
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
A SD-Card has limited write cycles. Since the device does not do [Wear Leveling](https://en.wikipedia.org/wiki/Wear_leveling), this can wear out your SD-Card!
|
||||||
6
param-docs/parameter-pages/Digits/ROIImagesRetention.md
Normal file
6
param-docs/parameter-pages/Digits/ROIImagesRetention.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Parameter `ROIImagesRetention`
|
||||||
|
Default Value: `3`
|
||||||
|
|
||||||
|
Unit: Days
|
||||||
|
|
||||||
|
Days to keep the separated digit images (`0` = forever).
|
||||||
21
param-docs/parameter-pages/GPIO/IO0.md
Normal file
21
param-docs/parameter-pages/GPIO/IO0.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Parameter `IO0`
|
||||||
|
Default Value: `input disabled 10 false false`
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This is an **Expert Parameter**! Only change it if you understand what it does!
|
||||||
|
|
||||||
|
This parameter can be used to configure the GPIO `IO0` pin.
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This pin is only usable with restrictions!
|
||||||
|
It must be disabled when the camera is used.
|
||||||
|
Additionally, it is used to activate Bootloader mode and must therefore be HIGH after a reset!
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
- `GPIO 0 state`: One of `input`, `input pullup`, `input pulldown` or `output`.
|
||||||
|
- `GPIO 0 use interrupt`: Enable interrupt trigger
|
||||||
|
- `GPIO 0 PWM duty resolution`: LEDC PWM duty resolution in bit
|
||||||
|
- `GPIO 0 enable MQTT`: Enable MQTT publishing/subscribing
|
||||||
|
- `GPIO 0 enable HTTP`: Enable HTTP write/read
|
||||||
|
- `GPIO 0 name`: MQTT topic name (empty = `GPIO0`). Allowed characters: `a-z, A-Z, 0-9, _, -`.
|
||||||
19
param-docs/parameter-pages/GPIO/IO1.md
Normal file
19
param-docs/parameter-pages/GPIO/IO1.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Parameter `IO1`
|
||||||
|
Default Value: `input disabled 10 false false`
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This is an **Expert Parameter**! Only change it if you understand what it does!
|
||||||
|
|
||||||
|
This parameter can be used to configure the GPIO `IO1` pin.
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This pin is by default used for the serial communication as TX pin (USB logging)!
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
- `GPIO 1 state`: One of `input`, `input pullup`, `input pulldown` or `output`.
|
||||||
|
- `GPIO 1 use interrupt`: Enable interrupt trigger
|
||||||
|
- `GPIO 1 PWM duty resolution`: LEDC PWM duty resolution in bit
|
||||||
|
- `GPIO 1 enable MQTT`: Enable MQTT publishing/subscribing
|
||||||
|
- `GPIO 1 enable HTTP`: Enable HTTP write/read
|
||||||
|
- `GPIO 1 name`: MQTT topic name (empty = `GPIO1`). Allowed characters: `a-z, A-Z, 0-9, _, -`.
|
||||||
19
param-docs/parameter-pages/GPIO/IO12.md
Normal file
19
param-docs/parameter-pages/GPIO/IO12.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Parameter `IO12`
|
||||||
|
Default Value: `input-pullup disabled 10 false false`
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This is an **Expert Parameter**! Only change it if you understand what it does!
|
||||||
|
|
||||||
|
This parameter can be used to configure the GPIO `IO12` pin.
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
This pin is usable without known restrictions!
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
- `GPIO 12 state`: One of `external-flash-ws281x`, `input`, `input pullup`, `input pulldown` or `output`.
|
||||||
|
- `GPIO 12 use interrupt`: Enable interrupt trigger
|
||||||
|
- `GPIO 12 PWM duty resolution`: LEDC PWM duty resolution in bit
|
||||||
|
- `GPIO 12 enable MQTT`: Enable MQTT publishing/subscribing
|
||||||
|
- `GPIO 12 enable HTTP`: Enable HTTP write/read
|
||||||
|
- `GPIO 12 name`: MQTT topic name (empty = `GPIO12`). Allowed characters: `a-z, A-Z, 0-9, _, -`.
|
||||||
19
param-docs/parameter-pages/GPIO/IO13.md
Normal file
19
param-docs/parameter-pages/GPIO/IO13.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Parameter `IO13`
|
||||||
|
Default Value: `input-pullup disabled 10 false false`
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This is an **Expert Parameter**! Only change it if you understand what it does!
|
||||||
|
|
||||||
|
This parameter can be used to configure the GPIO `IO13` pin.
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
This pin is usable without known restrictions!
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
- `GPIO 13 state`: One of `input`, `input pullup`, `input pulldown` or `output`.
|
||||||
|
- `GPIO 13 use interrupt`: Enable interrupt trigger
|
||||||
|
- `GPIO 13 PWM duty resolution`: LEDC PWM duty resolution in bit
|
||||||
|
- `GPIO 13 enable MQTT`: Enable MQTT publishing/subscribing
|
||||||
|
- `GPIO 13 enable HTTP`: Enable HTTP write/read
|
||||||
|
- `GPIO 13 name`: MQTT topic name (empty = `GPIO13`). Allowed characters: `a-z, A-Z, 0-9, _, -`.
|
||||||
19
param-docs/parameter-pages/GPIO/IO3.md
Normal file
19
param-docs/parameter-pages/GPIO/IO3.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Parameter `IO3`
|
||||||
|
Default Value: `input disabled 10 false false`
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This is an **Expert Parameter**! Only change it if you understand what it does!
|
||||||
|
|
||||||
|
This parameter can be used to configure the GPIO `IO3` pin.
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This pin is by default used for the serial communication as RX pin (USB logging)!
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
- `GPIO 3 state`: One of `input`, `input pullup`, `input pulldown` or `output`.
|
||||||
|
- `GPIO 3 use interrupt`: Enable interrupt trigger
|
||||||
|
- `GPIO 3 PWM duty resolution`: LEDC PWM duty resolution in bit
|
||||||
|
- `GPIO 3 enable MQTT`: Enable MQTT publishing/subscribing
|
||||||
|
- `GPIO 3 enable HTTP`: Enable HTTP write/read
|
||||||
|
- `GPIO 3 name`: MQTT topic name (empty = `GPIO3`). Allowed characters: `a-z, A-Z, 0-9, _, -`.
|
||||||
20
param-docs/parameter-pages/GPIO/IO4.md
Normal file
20
param-docs/parameter-pages/GPIO/IO4.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Parameter `IO4`
|
||||||
|
Default Value: `built-in-led disabled 10 false false`
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This is an **Expert Parameter**! Only change it if you understand what it does!
|
||||||
|
|
||||||
|
This parameter can be used to configure the GPIO `IO4` pin.
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
This pin is only usable with restrictions!
|
||||||
|
By default, it is used for build-in flash light (onboard LED).
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
- `GPIO 4 state`: One of `built-in-led`, `input`, `input pullup`, `input pulldown` or `output`.
|
||||||
|
- `GPIO 4 use interrupt`: Enable interrupt trigger
|
||||||
|
- `GPIO 4 PWM duty resolution`: LEDC PWM duty resolution in bit
|
||||||
|
- `GPIO 4 enable MQTT`: Enable MQTT publishing/subscribing
|
||||||
|
- `GPIO 4 enable HTTP`: Enable HTTP write/read
|
||||||
|
- `GPIO 4 name`: MQTT topic name (empty = `GPIO4`). Allowed characters: `a-z, A-Z, 0-9, _, -`.
|
||||||
5
param-docs/parameter-pages/GPIO/LEDColor.md
Normal file
5
param-docs/parameter-pages/GPIO/LEDColor.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Parameter `LEDColor`
|
||||||
|
Default Value: `150 150 150`
|
||||||
|
|
||||||
|
Color of the attached LEDs to GPIO12 in **R**ed, **G**reen **B**lue from `0` (full off) .. `255` (full on)
|
||||||
|
(See `IO12` parameter).
|
||||||
4
param-docs/parameter-pages/GPIO/LEDNumbers.md
Normal file
4
param-docs/parameter-pages/GPIO/LEDNumbers.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Parameter `LEDNumbers`
|
||||||
|
Default Value: `2`
|
||||||
|
|
||||||
|
Number of LEDs on the external LED-stripe attached to GPIO12 (See `IO12` parameter).
|
||||||
3
param-docs/parameter-pages/GPIO/LEDType.md
Normal file
3
param-docs/parameter-pages/GPIO/LEDType.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Parameter `LEDType`
|
||||||
|
Default Value: `WS2812`
|
||||||
|
Type of the `WS2812x` which is connected to GPIO12 (See `IO12` parameter).
|
||||||
8
param-docs/parameter-pages/GPIO/MainTopicMQTT.md
Normal file
8
param-docs/parameter-pages/GPIO/MainTopicMQTT.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Parameter `MainTopicMQTT`
|
||||||
|
Default Value: `wasserzaehler/GPIO`
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
This parameter is not accessible through the Web Interface Configuration Page!
|
||||||
|
|
||||||
|
The GPIO Interface is prepared to report it's status and status changes as a MQTT topic. With this parameter you configure the MQTT main topic, under which the status is published.
|
||||||
|
As this parameter is still experimental it can only be set manually in the `config.ini` itself and has not been tested in detail so far.
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user