Compare commits

...

71 Commits

Author SHA1 Message Date
CaCO3
8d6f731d5a v15.6.0 (#2876)
* Fix pipeline (#2860)

fix action

* ATA-Trim support (#2864)

Fix issues with the SD-Card initialization

* Update Changelog.md

---------

Co-authored-by: michael <Heinrich-Tuning@web.de>
2024-02-09 14:00:12 +01:00
CaCO3
019069cd16 v15.5.0 (#2850) (#2851)
* update

* Revert "v15.4.0"

This reverts commit 74d4f20858.

* Revert "prepare v15.4.0 versuch 2"

This reverts commit e790a14caa.

* revert submnodules (#2768)



* Update submodules (#2769)

- Update `esp-nn` to v1.0.2
- Update `esp32-camera` to v2.0.6
- Updated `tflite-micro-esp-examples` (The repo got renamed to `esp-tflite-micro ` and the folder structure got cleaned up).

* fix(unity-test): Run unity tests in dedicated task
Avoid running out of heap

* Add delay

* Update

* Update

* Enhance busy notification (#2774)

* on marker updating, show message and retry until round got completed

* same for contrast enhancement

---------



* Fix negatives on extended resolution false #2744 (#2772)

* not extended resolution allows -1 on the lowest digit

* not extended resolution allows -1 on the lowest number

* negatives on last value digit with -1 will set to prevalue and is not an error  #2744

---------



* Implemented late analog / digital transition (#2778)

* Implemented late transition

Complete rewrite of analog / digital transition

Two tests is still failing, which need to be discussed.

* Allow wider range of transition values to support late transition

* Added documentation

* Fix testings (#2783)

* fix all tests and more description

* The decimal point offset. -3 corresponds to x.yyy

---------



* Bugfix InfluxDB shifting times (#2785)

* Modify time_sntp

* Update time_sntp.cpp

* Update time_sntp.cpp

* update

* Update interface_influxdb.cpp

* Update interface_influxdb.cpp

* Update time_sntp.cpp

* Upload

* Update interface_influxdb.cpp

* Update interface_influxdb.cpp

* Update interface_influxdb.cpp

* Remove Time Convert vom influx Interface

* Update platform-espressif32 to 6.5.0 (#2770)

* Update ESP IDF to 6.5.0

* Migration to new IDF

* Correct smtp vor v5.1

---------




* ATA-Trim support (#2781)

* Add files via upload

* Update main.cpp

* Update main.cpp

* Update main.cpp

* Update Helper.cpp

* Update Helper.h

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update diskio_sdmmc_mh.c

* Update diskio_sdmmc_mh.h

* Update ff_mh.c

* Update vfs_fat_sdmmc_mh.c

* Update sdmmc_common_mh.h

* Update sdmmc_common_mh.c

* Update Helper.cpp

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update ff_mh.c

---------



* Update Changelog.md

* Add device info info page (#2789)



* fix chip information REST API

* Update pipelines to fix warnings (#2841)

Update actions

* Move param doc (#2843)

moved param doc from docs repo to here

* fix(tflite): Fix memory leaks in tflite integration (#2842)

* Update Changelog.md (#2849)

---------

Signed-off-by: Florian Grabmeier <flo.grabmeier@gmail.com>
Co-authored-by: jomjol <30766535+jomjol@users.noreply.github.com>
Co-authored-by: CaCO3 <caco@ruinelli.ch>
Co-authored-by: Slider0007 <jobbelle@gmx.net>
Co-authored-by: Frank Haverland <fspapaping@googlemail.com>
Co-authored-by: Martin Siggel <martin.siggel@dlr.de>
Co-authored-by: michael <Heinrich-Tuning@web.de>
Co-authored-by: flox_x <93255373+flooxo@users.noreply.github.com>
Co-authored-by: Slider0007 <115730895+Slider0007@users.noreply.github.com>
2024-02-02 13:52:05 +01:00
CaCO3
4de1152cf3 Update Changelog.md (#2849) 2024-02-02 13:46:42 +01:00
Slider0007
0e0fb459dc fix(tflite): Fix memory leaks in tflite integration (#2842) 2024-02-02 13:23:51 +01:00
CaCO3
8410df6144 Move param doc (#2843)
moved param doc from docs repo to here
2024-01-31 21:53:56 +01:00
CaCO3
4990858101 Update pipelines to fix warnings (#2841)
Update actions
2024-01-31 20:53:41 +01:00
CaCO3
fc1f8ee242 fix chip information REST API 2024-01-08 23:41:48 +01:00
flox_x
252c399a76 Add device info info page (#2789)
Signed-off-by: Florian Grabmeier <flo.grabmeier@gmail.com>
2024-01-08 23:23:57 +01:00
jomjol
eb7f2b3705 Update Changelog.md 2024-01-02 09:04:15 +01:00
michael
2ed6fb0f0d ATA-Trim support (#2781)
* Add files via upload

* Update main.cpp

* Update main.cpp

* Update main.cpp

* Update Helper.cpp

* Update Helper.h

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update diskio_sdmmc_mh.c

* Update diskio_sdmmc_mh.h

* Update ff_mh.c

* Update vfs_fat_sdmmc_mh.c

* Update sdmmc_common_mh.h

* Update sdmmc_common_mh.c

* Update Helper.cpp

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update ff_mh.c

---------

Co-authored-by: CaCO3 <caco3@ruinelli.ch>
2024-01-02 08:56:46 +01:00
CaCO3
b5213b01af Update platform-espressif32 to 6.5.0 (#2770)
* Update ESP IDF to 6.5.0

* Migration to new IDF

* Correct smtp vor v5.1

---------

Co-authored-by: CaCO3 <caco@ruinelli.ch>
Co-authored-by: jomjol <30766535+jomjol@users.noreply.github.com>
2024-01-01 22:00:23 +01:00
jomjol
473e458b85 Bugfix InfluxDB shifting times (#2785)
* Modify time_sntp

* Update time_sntp.cpp

* Update time_sntp.cpp

* update

* Update interface_influxdb.cpp

* Update interface_influxdb.cpp

* Update time_sntp.cpp

* Upload

* Update interface_influxdb.cpp

* Update interface_influxdb.cpp

* Update interface_influxdb.cpp

* Remove Time Convert vom influx Interface
2024-01-01 10:41:12 +01:00
Frank Haverland
4f3f3d9af2 Fix testings (#2783)
* fix all tests and more description

* The decimal point offset. -3 corresponds to x.yyy

---------

Co-authored-by: jomjol <30766535+jomjol@users.noreply.github.com>
2023-12-31 14:43:08 +01:00
Martin Siggel
b5a4cfed96 Implemented late analog / digital transition (#2778)
* Implemented late transition

Complete rewrite of analog / digital transition

Two tests is still failing, which need to be discussed.

* Allow wider range of transition values to support late transition

* Added documentation
2023-12-31 12:04:09 +01:00
Frank Haverland
6fca4d8d95 Fix negatives on extended resolution false #2744 (#2772)
* not extended resolution allows -1 on the lowest digit

* not extended resolution allows -1 on the lowest number

* negatives on last value digit with -1 will set to prevalue and is not an error  #2744

---------

Co-authored-by: jomjol <30766535+jomjol@users.noreply.github.com>
2023-12-31 12:03:52 +01:00
CaCO3
a11e19fb0c Enhance busy notification (#2774)
* on marker updating, show message and retry until round got completed

* same for contrast enhancement

---------

Co-authored-by: CaCO3 <caco@ruinelli.ch>
2023-12-31 11:47:28 +01:00
Frank Haverland
09fa94c95f Merge pull request #2777 from Slider0007/unity-test-rolling
chore(unity-test): Run unity tests in dedicated task
2023-12-30 11:55:22 +01:00
Slider0007
f01b4dbd13 Update 2023-12-29 09:57:59 +01:00
Slider0007
dbf1770016 Update 2023-12-29 09:56:47 +01:00
Slider0007
006f3aa063 Add delay 2023-12-29 09:47:37 +01:00
Slider0007
24c46c38b4 fix(unity-test): Run unity tests in dedicated task
Avoid running out of heap
2023-12-28 21:32:43 +01:00
CaCO3
9ced147d9c Update submodules (#2769)
- Update `esp-nn` to v1.0.2
- Update `esp32-camera` to v2.0.6
- Updated `tflite-micro-esp-examples` (The repo got renamed to `esp-tflite-micro ` and the folder structure got cleaned up).
2023-12-28 12:13:57 +01:00
CaCO3
0808895bd6 revert submnodules (#2768)
Co-authored-by: CaCO3 <caco@ruinelli.ch>
2023-12-28 09:27:42 +01:00
jomjol
d2dec9fa59 Revert "prepare v15.4.0 versuch 2"
This reverts commit e790a14caa.
2023-12-22 23:50:56 +01:00
jomjol
7e7bc3dd68 Revert "v15.4.0"
This reverts commit 74d4f20858.
2023-12-22 23:48:43 +01:00
jomjol
5b09cd0d59 update 2023-12-22 23:47:17 +01:00
jomjol
74d4f20858 v15.4.0 2023-12-22 23:38:31 +01:00
jomjol
e790a14caa prepare v15.4.0 versuch 2 2023-12-22 23:31:13 +01:00
jomjol
f023a6b739 Revert "v15.4.0"
This reverts commit 374462a7d8.
2023-12-22 23:22:57 +01:00
jomjol
1e463188ea Revert "Update Changelog.md"
This reverts commit 9991196961.
2023-12-22 23:22:08 +01:00
jomjol
9991196961 Update Changelog.md 2023-12-22 23:04:52 +01:00
jomjol
ecece0f7fc Update dependencies.lock 2023-12-22 22:55:54 +01:00
jomjol
1b3b7595c1 Merge branch 'rolling' 2023-12-22 22:46:36 +01:00
jomjol
6d10f712d1 Update Changelog.md 2023-12-22 22:46:16 +01:00
jomjol
b84a2db050 Update config.ini 2023-12-22 22:45:32 +01:00
jomjol
374462a7d8 v15.4.0 2023-12-22 22:43:08 +01:00
Frank Haverland
4facd7be05 new images on analog and digits (#2756)
* new images on dig-class100, older ana-class100 removed

* new images on analog and digits
2023-12-22 22:42:33 +01:00
jomjol
3baf5865ad Prepare 15.4.0 2023-12-21 19:21:22 +01:00
CaCO3
02138c44ac rename InfluxDBv2 parameter Database to Bucket (#2704)
* rename InfluxDBv2 parameter Database to Basket

* only enable the field if it is a boolean

* corrected "Basket" to "Bucket"

---------

Co-authored-by: CaCO3 <caco@ruinelli.ch>
2023-11-22 23:43:23 +01:00
Slider0007
1094c8a0a8 fix(influxdb): Consider DST setting for UTC time conversion (#2679)
* fix(influxdb): Consider DST setting for UTC time conversion

* Update
2023-11-12 12:04:18 +01:00
LordGuilly
75b15b8e9d added config entries for MQTT TLS (#2651)
3 new entries in the config section, for setting file paths for
        - Root CA
        - Client Certificate
        - Client Private Key
 (all set as expert parameters)

- logging cert filenames

added config entries for MQTT TLS

 3 new entries in the config section, for setting file paths for
        - Root CA
        - Client Certificate
        - Client Private Key

- logging cert filenames

MQTT-TLS: Updates for the PR comments

- config.ini now has default values closer to "real" life filenames
- MQTT cert entries are hidden as Expert parameters
- Fixed debug logging at MQTT interface for unhandled messages
2023-11-07 22:58:16 +01:00
CaCO3
36c12b400b rework test-domain check 2023-11-07 22:42:15 +01:00
CaCO3
35d90cd0ee update readme 2023-10-21 22:39:21 +02:00
CaCO3
999a8d9374 updated readme 2023-10-21 22:35:13 +02:00
CaCO3
8a4269c6a0 Add files via upload 2023-10-21 22:32:25 +02:00
CaCO3
8f5579cca5 updated readme 2023-10-21 22:30:29 +02:00
CaCO3
e58b3a2cf8 update webinstaller 2023-10-21 22:23:08 +02:00
CaCO3
222ee0921c udpate webinstaller 2023-10-21 22:19:07 +02:00
CaCO3
e2bfcd26c9 update web installer 2023-10-21 22:15:47 +02:00
CaCO3
18917b2d82 Delete docs/manifest_template.json 2023-10-21 01:06:52 +02:00
CaCO3
f0dea3abcb Delete docs/binary/firmware.bin 2023-10-21 01:06:42 +02:00
CaCO3
bd2d8b4a15 Update manual-update-webinstaller.yml 2023-10-21 01:06:15 +02:00
CaCO3
80e3f50a5b Update manifest.json 2023-10-21 01:05:19 +02:00
CaCO3
f6ca32d69f Update manual-update-webinstaller.yml 2023-10-21 01:03:00 +02:00
CaCO3
49e919c481 Update manual-update-webinstaller.yml 2023-10-21 01:00:21 +02:00
CaCO3
de768c4f44 Delete releases/download directory 2023-10-21 00:49:24 +02:00
CaCO3
002fc033aa Delete firmware directory 2023-10-21 00:47:05 +02:00
CaCO3
f4af8de699 fix paypal button (#2666)
* Add files via upload

* Update README.md

* Update README.md

* Update README.md

* Update README.md
2023-10-21 00:43:36 +02:00
CaCO3
e4d6707a0b Update README.md 2023-10-21 00:27:52 +02:00
CaCO3
9f03e68690 Update README.md 2023-10-21 00:27:32 +02:00
CaCO3
b1df7df580 added label action 2023-10-20 23:35:07 +02:00
CaCO3
ebc9be4a28 added label action 2023-10-20 23:27:10 +02:00
jomjol
5d0fc73c13 Updated feature request 2023-09-03 15:48:00 +02:00
jomjol
40a1aa0430 Update submodules, include only needed layers of tflite (#2586)
* Initial version

* Working Version

* Update

* Update main.cpp

* Updated Docu
2023-08-20 21:49:28 +02:00
TheDubliner
d7a733512f 📚 Fix minor typos, grammar and formatting issues. (#2575) 2023-08-15 21:37:22 +02:00
Frank Haverland
dc9f1aad27 new images on dig-class100, older ana-class100 removed (#2545) 2023-08-02 21:46:00 +02:00
Frank Haverland
cd3e641bcc #2540 fix trunc of first with imprecise precision (#2543)
* added more debug for #2447

* new model on new images dig-class100-0165_s2

* #2465 fix first digit with extended_Resolution=false

* fix #2491

* #2540 fix trunc of first with imprecise precision
2023-07-30 18:25:56 +02:00
Slider0007
ad72ffa37c mqtt_handler_set_prevalue: fix memory leak (#2544) 2023-07-30 18:25:35 +02:00
Giel van Schijndel
2d45a0ed26 fix: provide proper error messages for invalid /info?type= query parameter (#2533) 2023-07-24 21:44:06 +02:00
Giel van Schijndel
e8065ef414 fix(fileserver): avoid sending *two* "last-chunk" sequences (#2532)
Because a zero-sized chunk indicates the end of a HTTP response sending
another such zero-sized chunk is not interpretable by the HTTP client.

See [RFC2616 3.6.1] "Chunked Transfer Encoding" for details.

[RFC2616 3.6.1]: https://datatracker.ietf.org/doc/html/rfc2616#section-3.6.1
2023-07-24 21:43:56 +02:00
pfeifferch
33893eb566 Shortcut Icon hinzugefügt (#2530)
* Add files via upload

Shortcut icon

* Update index.html

Shortcut icon
2023-07-23 20:33:04 +02:00
185 changed files with 3706 additions and 473 deletions

View File

@@ -3,7 +3,32 @@
# Make sure to also add the response to .github/workflows/reply-bot.yml!
# 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
#######################################################################

View File

@@ -15,7 +15,7 @@ jobs:
with:
concurrent_skipping: same_content_newer
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
@@ -25,28 +25,28 @@ jobs:
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Update PIP cache on every commit
uses: actions/cache@v3.2.3
uses: actions/cache@v4
with:
path: ~/.cache/pip
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
- name: Update PlatformIO cache on every commit
uses: actions/cache@v3.2.3
uses: actions/cache@v4
with:
path: ~/.platformio
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
- name: Update Build cache on every commit
uses: actions/cache@v3.2.3
uses: actions/cache@v4
with:
path: ./code/.pio/
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
- name: Update generated-files cache on every commit
uses: actions/cache@v3.2.3
uses: actions/cache@v4
with:
path: |
./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
- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
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: 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: |
rm -rf ./html
mkdir html
@@ -79,7 +79,7 @@ jobs:
python -m pip install markdown
mkdir html/param-tooltips
cd tools/parameter-tooltip-generator
bash generate-param-doc-tooltips.sh
python generate-param-doc-tooltips.py
cd ../..
cp -r ./sd-card/html/* ./html/
@@ -101,10 +101,10 @@ jobs:
needs: build
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Update generated-files cache on every commit
uses: actions/cache@v3.2.3
uses: actions/cache@v4
with:
path: |
./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
- name: Update update cache on every commit
uses: actions/cache@v3.2.3
uses: actions/cache@v4
with:
path: update
key: update-${{ github.run_id }}
@@ -144,7 +144,7 @@ jobs:
cp ./sd-card/config/*.tflite ./update/config/ 2>/dev/null || true
- name: Upload update as update.zip artifact (Firmware + Web UI + CNN)
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: "AI-on-the-edge-device__update__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
path: ./update/*
@@ -164,10 +164,10 @@ jobs:
needs: build
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Update generated-files cache on every commit
uses: actions/cache@v3.2.3
uses: actions/cache@v4
with:
path: |
./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
- name: Update remote_setup cache on every commit
uses: actions/cache@v3.2.3
uses: actions/cache@v4
with:
path: remote_setup
key: remote_setup-${{ github.run_id }}
@@ -205,7 +205,7 @@ jobs:
cp ./sd-card/config/* ./remote_setup/config/ 2>/dev/null || true
- name: Upload remote_setup as remote_setup.zip artifact (Firmware + Web UI + Config)
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: "AI-on-the-edge-device__remote-setup__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
path: ./remote_setup/*
@@ -220,10 +220,10 @@ jobs:
needs: build
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Update generated-files cache on every commit
uses: actions/cache@v3.2.3
uses: actions/cache@v4
with:
path: |
./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
- name: Update manual_setup cache on every commit
uses: actions/cache@v3.2.3
uses: actions/cache@v4
with:
path: manual_setup
key: manual_setup-${{ github.run_id }}
@@ -263,7 +263,7 @@ jobs:
cd ./manual_setup
- name: Upload manual_setup.zip artifact (Firmware + Bootloader + Partitions + Web UI)
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: "AI-on-the-edge-device__manual-setup__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
path: ./manual_setup
@@ -284,24 +284,24 @@ jobs:
id-token: write
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Update update cache on every commit
uses: actions/cache@v3.2.3
uses: actions/cache@v4
with:
path: update
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
- name: Update remote_setup cache on every commit
uses: actions/cache@v3.2.3
uses: actions/cache@v4
with:
path: remote_setup
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
- name: Update manual_setup cache on every commit
uses: actions/cache@v3.2.3
uses: actions/cache@v4
with:
path: manual_setup
key: manual_setup-${{ github.run_id }}
@@ -380,7 +380,7 @@ jobs:
#########################################################################################
## 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:
needs: [release]
environment:
@@ -396,7 +396,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Get version of last release
id: last_release
@@ -408,20 +408,23 @@ jobs:
- name: Add binary to Web Installer and update manifest
run: |
echo "Updating Web installer to use firmware from ${{ steps.last_release.outputs.tag_name }}..."
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
unzip AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
cp -f firmware.bin docs/binary/firmware.bin
cp -f docs/manifest_template.json docs/manifest.json
sed -i 's/VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
echo "Updating index and manifest file..."
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
uses: actions/configure-pages@v2
uses: actions/configure-pages@v4
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
uses: actions/upload-pages-artifact@v2
with:
path: 'docs'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1
uses: actions/deploy-pages@v3 # Note: v4 does not work!

View File

@@ -1,7 +1,7 @@
# This updates the Web Installer with the files from the docs folder and the binary of the latest release
# it only gets run on:
# - Changes to the docs folder in the `rolling` branch
# - On a release
# - Manually triggered
# Make sure to also update the lower part of build.yml!
name: Manual Web Installer Update
@@ -40,12 +40,14 @@ jobs:
- name: Add binary to Web Installer and update manifest
run: |
echo "Updating Web installer to use firmware from ${{ steps.last_release.outputs.tag_name }}..."
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
unzip AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
cp -f firmware.bin docs/binary/firmware.bin
cp -f docs/manifest_template.json docs/manifest.json
sed -i 's/VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
echo "Updating index and manifest file..."
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
uses: actions/configure-pages@v2

6
.gitmodules vendored
View File

@@ -4,9 +4,9 @@
[submodule "code/components/esp-nn"]
path = code/components/esp-nn
url = https://github.com/espressif/esp-nn.git
[submodule "code/components/tflite-micro-esp-examples"]
path = code/components/tflite-micro-esp-examples
url = https://github.com/espressif/tflite-micro-esp-examples.git
[submodule "code/components/esp-tflite-micro"]
path = code/components/esp-tflite-micro
url = https://github.com/espressif/esp-tflite-micro.git
[submodule "code/components/stb"]
path = code/components/stb
url = https://github.com/nothings/stb.git

View File

@@ -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
### 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.3.0...v15.2.4)
#### 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
- dig-cont_0620_s3_q.tflite
## [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)
#### 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
### 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)
#### 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
### 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)
#### Added

View File

@@ -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.
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.
I will support and help where ever I can!
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!
____
#### #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
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
@@ -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
~~* 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
@@ -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
~~* https://github.com/jomjol/AI-on-the-edge-device/issues/644~~
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/644~~

View File

@@ -1,31 +1,31 @@
# Welcome to the AI-on-the-edge-device
<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**.
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**.
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 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">
## Key features
- Tensorflow Lite (TFlite) integration - including easy to use wrapper
- Inline Image processing (feature detection, alignment, ROI extraction)
- **Small** and **cheap** device (3x4.5x2 cm³, < 10 EUR)
- camera and illumination integrated
- Web surface to administrate and control
- OTA-Interface to update directly through the web interface
- Tensorflow Lite (TFlite) integration including easy-to-use wrapper
- Inline image processing (feature detection, alignment, ROI extraction)
- **Small** and **cheap** device (3 x 4.5 x 2 cm³, < 10 EUR)
- Integrated camera and illumination
- Web interface for administration and control
- OTA interface for updating directly via the web interface
- Full integration into Homeassistant
- Support for Influx DB 1
- Support for Influx DB 1 and 2
- MQTT
- REST API
## 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">
@@ -41,62 +41,68 @@ There are several options what to do with that value. Either send it to a MQTT b
## Setup
There is a 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.
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.
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
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
Initially you will have to flash the ESP32 through an USB connection. Later an update is possible directly over the Air (OTA).
### Flashing the ESP32
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:
- [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:
![](images/web-installer.png)
- 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
The SD-Card must be flashed separately, see the [Docu](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/) for details.
### Flashing the SD Card
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
A 3d-printable housing can be found here:
Various 3D-printable housing can be found here:
- https://www.thingiverse.com/thing:4573481 (Water Meter)
- https://www.thingiverse.com/thing:5028229 (Power Meter)
- https://www.thingiverse.com/thing:5224101 (Gas Meter)
- https://www.thingiverse.com/thing:4571627 (ESP32-Cam housing only)
## Build it yourself
See [Build Instructions](code/README.md).
- https://www.thingiverse.com/thing:4571627 (ESP32-cam housing only)
## 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">
<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" />
<img alt="" border="0" src="https://www.paypal.com/en_DE/i/scr/pixel.gif" width="1" height="1" />
</form>
If you have any technical topics, you can create an [Issue](https://github.com/jomjol/AI-on-the-edge-device/issues).
<a href="https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL"><img border="0" src="images/paypal.png" width="200px" target="_blank"></a>
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">
## Support
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).
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
See [Changelog](Changelog.md)
See [Changelog](Changelog.md).
## Build It Yourself
See [Build Instructions](code/README.md).
## Tools
* 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
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
View File

@@ -1,4 +1,5 @@
.pio
.idea
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json

View File

@@ -1,6 +1,6 @@
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(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.cpp

View File

@@ -8,6 +8,15 @@ git checkout rolling
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
See further down to build it within an IDE.
### Compile
@@ -60,3 +69,6 @@ pio device monitor -p /dev/ttyUSB0
- `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 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.

View File

@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
idf_component_register(SRCS ${app_sources}
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)

View File

@@ -153,7 +153,7 @@
/*#define MINIZ_NO_MALLOC */
#ifdef MINIZ_NO_INFLATE_APIS
#define MINIZ_NO_ARCHIVE_APIS
//#define MINIZ_NO_ARCHIVE_APIS
#endif
#ifdef MINIZ_NO_DEFLATE_APIS

View File

@@ -547,7 +547,7 @@ static esp_err_t download_get_handler(httpd_req_t *req)
/* Get value of expected key from query string */
if (httpd_query_key_value(buf, "readonly", param, sizeof(param)) == ESP_OK) {
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 */
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) {
fclose(fd);
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);
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;
}

View File

@@ -134,7 +134,22 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
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 result;
@@ -145,7 +160,8 @@ int ClassFlowCNNGeneral::PointerEvalHybridNew(float number, float number_of_pred
{
// 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
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) +
" 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)){
return false;
};
}
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "doFlow after alignment");
@@ -852,10 +868,9 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
bool ClassFlowCNNGeneral::isExtendedResolution(int _number)
{
if (!(CNNType == Digital))
return true;
return false;
if (CNNType == Digital)
return false;
return true;
}

View File

@@ -34,6 +34,7 @@ struct NumberPost {
bool AllowNegativeRates;
bool checkDigitIncreaseConsistency;
time_t lastvalue;
time_t timeStampTimeUTC;
string timeStamp;
double FlowRateAct; // m3 / min
double PreValue; // last value that was read out well

View File

@@ -137,6 +137,7 @@ bool ClassFlowInfluxDB::doFlow(string zwtime)
std::string resultraw = "";
std::string resultrate = "";
std::string resulttimestamp = "";
long int timeutc;
string zw = "";
string namenumber = "";
@@ -152,6 +153,7 @@ bool ClassFlowInfluxDB::doFlow(string zwtime)
resulterror = (*NUMBERS)[i]->ErrorMessageText;
resultrate = (*NUMBERS)[i]->ReturnRateValue;
resulttimestamp = (*NUMBERS)[i]->timeStamp;
timeutc = (*NUMBERS)[i]->timeStampTimeUTC;
if ((*NUMBERS)[i]->FieldV1.length() > 0)
{
@@ -167,7 +169,7 @@ bool ClassFlowInfluxDB::doFlow(string zwtime)
}
if (result.length() > 0)
InfluxDBPublish(measurement, namenumber, result, resulttimestamp);
InfluxDBPublish(measurement, namenumber, result, timeutc);
}
}

View File

@@ -20,7 +20,7 @@ static const char* TAG = "INFLUXDBV2";
void ClassFlowInfluxDBv2::SetInitialParameter(void)
{
uri = "";
database = "";
bucket = "";
dborg = "";
dbtoken = "";
// dbfield = "";
@@ -109,9 +109,9 @@ bool ClassFlowInfluxDBv2::ReadParameter(FILE* pfile, string& aktparamgraph)
{
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("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: *****");
// printf("vor V2 Init\n");
InfluxDB_V2_Init(uri, database, dborg, dbtoken);
InfluxDB_V2_Init(uri, bucket, dborg, dbtoken);
// printf("nach V2 Init\n");
InfluxDBenable = true;
} else {
@@ -196,6 +196,7 @@ bool ClassFlowInfluxDBv2::doFlow(string zwtime)
std::string resultraw = "";
std::string resultrate = "";
std::string resulttimestamp = "";
long int resulttimeutc = 0;
string zw = "";
string namenumber = "";
@@ -212,6 +213,8 @@ bool ClassFlowInfluxDBv2::doFlow(string zwtime)
resulterror = (*NUMBERS)[i]->ErrorMessageText;
resultrate = (*NUMBERS)[i]->ReturnRateValue;
resulttimestamp = (*NUMBERS)[i]->timeStamp;
resulttimeutc = (*NUMBERS)[i]->timeStampTimeUTC;
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());
if (result.length() > 0)
InfluxDB_V2_Publish(measurement, namenumber, result, resulttimestamp);
// InfluxDB_V2_Publish(namenumber, result, resulttimestamp);
InfluxDB_V2_Publish(measurement, namenumber, result, resulttimeutc);
}
}

View File

@@ -15,7 +15,7 @@ class ClassFlowInfluxDBv2 :
public ClassFlow
{
protected:
std::string uri, database;
std::string uri, bucket;
std::string dborg, dbtoken, dbfield;
std::string OldValue;
ClassFlowPostProcessing* flowpostprocessing;

View File

@@ -37,6 +37,9 @@ void ClassFlowMQTT::SetInitialParameter(void)
topicUptime = "";
topicFreeMem = "";
caCertFilename = "";
clientCertFilename = "";
clientKeyFilename = "";
clientname = wlan_config.hostname;
OldValue = "";
@@ -102,6 +105,18 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(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))
{
this->user = splitted[1];
@@ -196,7 +211,8 @@ bool ClassFlowMQTT::Start(float AutoInterval)
mqttServer_setParameter(flowpostprocessing->GetNumbers(), keepAlive, roundInterval);
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) {
return false;

View File

@@ -19,6 +19,7 @@ protected:
std::string OldValue;
ClassFlowPostProcessing* flowpostprocessing;
std::string user, password;
std::string caCertFilename, clientCertFilename, clientKeyFilename;
bool SetRetainFlag;
int keepAlive; // Seconds
float roundInterval; // Minutes

View File

@@ -5,6 +5,7 @@
#include <iomanip>
#include <sstream>
#include <cassert>
#include <time.h>
@@ -284,6 +285,7 @@ void ClassFlowPostProcessing::SavePreValue()
struct tm* timeinfo = localtime(&NUMBERS[j]->lastvalue);
strftime(buffer, 80, PREVALUE_TIME_FORMAT_OUTPUT, timeinfo);
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);
_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;
}
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)
{
string result = "";
@@ -768,6 +894,8 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
int previous_value = -1;
// ------------------- start processing analog values --------------------------//
if (NUMBERS[j]->analog_roi)
{
NUMBERS[j]->ReturnRawValue = flowAnalog->getReadout(j, NUMBERS[j]->isExtendedResolution);
@@ -781,19 +909,38 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
#ifdef SERIAL_DEBUG
ESP_LOGD(TAG, "After analog->getReadout: ReturnRaw %s", NUMBERS[j]->ReturnRawValue.c_str());
#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, 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
NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, NUMBERS[j]->isExtendedResolution, previous_value); // Extended Resolution only if there are no analogue digits
}
#ifdef SERIAL_DEBUG
ESP_LOGD(TAG, "After digital->getReadout: ReturnRaw %s", NUMBERS[j]->ReturnRawValue.c_str());
#endif
// ------------------ start corrections --------------------------//
NUMBERS[j]->ReturnRawValue = ShiftDecimal(NUMBERS[j]->ReturnRawValue, NUMBERS[j]->DecimalShift);
#ifdef SERIAL_DEBUG
@@ -813,7 +960,7 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
{
if (PreValueUse && NUMBERS[j]->PreValueOkay)
{
NUMBERS[j]->ReturnValue = ErsetzteN(NUMBERS[j]->ReturnValue, NUMBERS[j]->PreValue);
NUMBERS[j]->ReturnValue = ErsetzteN(NUMBERS[j]->ReturnValue, NUMBERS[j]->PreValue);
}
else
{
@@ -826,6 +973,13 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
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
ESP_LOGD(TAG, "After findDelimiterPos: ReturnValue %s", NUMBERS[j]->ReturnRawValue.c_str());
#endif
@@ -871,12 +1025,16 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Neg: value=" + std::to_string(NUMBERS[j]->Value)
+ ", preValue=" + std::to_string(NUMBERS[j]->PreValue)
+ ", 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) {
NUMBERS[j]->Value = NUMBERS[j]->PreValue;
NUMBERS[j]->ReturnValue = to_string(NUMBERS[j]->PreValue);
} else {
}
// Include inaccuracy of 0.2 for isExtendedResolution.
if ((NUMBERS[j]->Value >= (NUMBERS[j]->PreValue-(2/pow(10, NUMBERS[j]->Nachkomma))) && NUMBERS[j]->isExtendedResolution)
// not extended resolution allows -1 on the lowest digit
|| (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]->Value = NUMBERS[j]->PreValue;
NUMBERS[j]->ReturnValue = "";

View File

@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
idf_component_register(SRCS ${app_sources}
INCLUDE_DIRS "."
REQUIRES esp_timer tflite-lib jomjol_logfile fatfs sdmmc)
REQUIRES esp_timer esp-tflite-micro jomjol_logfile fatfs sdmmc vfs)

View File

@@ -27,10 +27,10 @@ extern "C" {
#include <esp_timer.h>
#include "../../include/defines.h"
#include "ClassLogFile.h"
#include "esp_vfs_fat.h"
#include "../sdmmc_common.h"
static const char* TAG = "HELPER";
@@ -40,7 +40,7 @@ unsigned int systemStatus = 0;
sdmmc_cid_t SDCardCid;
sdmmc_csd_t SDCardCsd;
bool SDCardIsMMC;
// #define DEBUG_DETAIL_ON
@@ -139,6 +139,7 @@ string getSDCardPartitionAllocationSize(){
void SaveSDCardInfo(sdmmc_card_t* card) {
SDCardCid = card->cid;
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 */
/* SD Card Manufacturer Database */
struct SDCard_Manufacturer_database database[] = {
struct SDCard_Manufacturer_database sd_database[] = {
{
.type = "sd",
.id = 0x01,
@@ -779,25 +780,124 @@ struct SDCard_Manufacturer_database database[] = {
.type = "sd",
.id = 0x89,
.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 */
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 = "";
for (int i = 0; i < id_cnt; i++) {
if (database[i].id == id) {
return database[i].manufacturer;
}
else {
ret_val = "ID unknown (not in DB)";
}
for (int i = 0; i < id_cnt; i++)
{
if (sd_database[i].id == id)
{
return sd_database[i].manufacturer;
}
else
{
ret_val = "ID unknown (not in DB)";
}
}
return ret_val;
}
}

View File

@@ -6,6 +6,7 @@
#include <string>
#include <fstream>
#include <vector>
#include "sdmmc_cmd.h"
using namespace std;

View File

@@ -3,6 +3,7 @@
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>
#include <inttypes.h>
#include <sys/stat.h>

View 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;
}

View 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

View File

@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
idf_component_register(SRCS ${app_sources}
INCLUDE_DIRS "."
REQUIRES tflite-lib esp_http_client jomjol_logfile)
REQUIRES esp_http_client jomjol_logfile)

View File

@@ -5,6 +5,7 @@
#include <time.h>
#include "ClassLogFile.h"
#include "esp_http_client.h"
#include "time_sntp.h"
#include "../../include/defines.h"
@@ -16,21 +17,21 @@ std::string _influxDBUser;
std::string _influxDBPassword;
std::string _influxDB_V2_URI;
std::string _influxDB_V2_Database;
std::string _influxDB_V2_Bucket;
std::string _influxDB_V2_Token;
std::string _influxDB_V2_Org;
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_Database = _database;
_influxDB_V2_Bucket = _bucket;
_influxDB_V2_Org = _org;
_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};
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
};
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;
char nowTimestamp[21];
if (_timestamp.length() > 0)
if (_timeUTC > 0)
{
struct tm tm;
strptime(_timestamp.c_str(), PREVALUE_TIME_FORMAT_OUTPUT, &tm);
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
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Timestamp (UTC): " + std::to_string(_timeUTC));
sprintf(nowTimestamp,"%ld000000000", _timeUTC); // UTC
payload = _measurement + " " + _key + "=" + _content + " " + nowTimestamp;
}
else
{
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "no timestamp given");
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);
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();
http_config.url = apiURI.c_str();
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;
}
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};
esp_http_client_config_t http_config = {
.user_agent = "ESP32 Meter reader",
@@ -160,29 +149,17 @@ void InfluxDBPublish(std::string _measurement, std::string _key, std::string _co
std::string payload;
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;
strptime(_timestamp.c_str(), PREVALUE_TIME_FORMAT_OUTPUT, &tm);
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
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Timestamp (UTC): " + std::to_string(_timeUTC));
sprintf(nowTimestamp,"%ld000000000", _timeUTC); // UTC
payload = _measurement + " " + _key + "=" + _content + " " + nowTimestamp;
}
else
{
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "no timestamp given");
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);
// 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 + "/api/v2/write?bucket=" + _influxDBDatabase + "/";

View File

@@ -10,11 +10,11 @@
// Interface to InfluxDB v1.x
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
void InfluxDB_V2_Init(std::string _uri, std::string _database, 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_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, long int _timeUTC);

View File

@@ -2,4 +2,4 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
idf_component_register(SRCS ${app_sources}
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)

View File

@@ -2,6 +2,9 @@
#include "interface_mqtt.h"
#include "esp_log.h"
#if DEBUG_DETAIL_ON
#include "esp_timer.h"
#endif
#include "connect_wlan.h"
#include "mqtt_client.h"
#include "ClassLogFile.h"
@@ -32,6 +35,7 @@ bool mqtt_connected = false;
esp_mqtt_client_handle_t client = NULL;
std::string uri, client_id, lwt_topic, lwt_connected, lwt_disconnected, user, password, maintopic;
std::string caCert, clientCert, clientKey;
int keepalive;
bool SetRetainFlag;
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) {
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
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,
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) {
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;
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()){
user = _user;
password = _password;
@@ -268,6 +296,20 @@ int MQTT_Init() {
mqtt_cfg.session.keepalive = keepalive;
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()){
mqtt_cfg.credentials.username = user.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
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_set_prevalue called: numbersname: " + std::string(numbersname->valuestring) +
", 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;
}
}
else {
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 {
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: numbersname not a valid string (\"numbersname\": \"main\")");
}
cJSON_Delete(jsonData);
}
else {
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: handler called, but no data received");

View File

@@ -11,6 +11,7 @@
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 _cacertfilename, std::string _clientcertfilename, std::string _clientkeyfilename,
int _keepalive, bool SetRetainFlag, void *callbackOnConnected);
int MQTT_Init();
void MQTTdestroy_client(bool _disable);

View File

@@ -12,6 +12,22 @@
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)
{
TfLiteTensor* output2 = this->interpreter->output(0);
@@ -179,22 +195,22 @@ bool CTfLiteClass::LoadInputImageBasis(CImageBasis *rs)
}
bool CTfLiteClass::MakeAllocate()
{
static tflite::AllOpsResolver resolver;
MakeStaticResolver();
#ifdef DEBUG_DETAIL_ON
LogFile.WriteHeapInfo("CTLiteClass::Alloc start");
#endif
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)
{
TfLiteStatus allocate_status = this->interpreter->AllocateTensors();
if (allocate_status != kTfLiteOk) {
TF_LITE_REPORT_ERROR(error_reporter, "AllocateTensors() failed");
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "AllocateTensors() failed");
this->GetInputDimension();
@@ -284,12 +300,6 @@ bool CTfLiteClass::ReadFileToModel(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");
if (!ReadFileToModel(_fn.c_str())) {
@@ -320,16 +330,6 @@ CTfLiteClass::CTfLiteClass()
CTfLiteClass::~CTfLiteClass()
{
delete this->interpreter;
delete this->error_reporter;
psram_free_shared_tensor_arena_and_model_memory();
}
namespace tflite
{
int OwnMicroErrorReporter::Report(const char* format, va_list args)
{
return 0;
}
}

View File

@@ -3,38 +3,23 @@
#ifndef CTFLITECLASS_H
#define CTFLITECLASS_H
#include "tensorflow/lite/micro/all_ops_resolver.h"
#include "tensorflow/lite/micro/micro_error_reporter.h"
#include "tensorflow/lite/micro/micro_mutable_op_resolver.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/micro/kernels/micro_ops.h"
#include "esp_err.h"
#include "esp_log.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
{
protected:
tflite::ErrorReporter *error_reporter;
tflite::MicroMutableOpResolver<10> resolver;
const tflite::Model* model;
tflite::MicroInterpreter* interpreter;
TfLiteTensor* output = nullptr;
static tflite::AllOpsResolver resolver;
int kTensorArenaSize;
uint8_t *tensor_arena;
@@ -48,6 +33,7 @@ class CTfLiteClass
long GetFileSize(std::string filename);
bool ReadFileToModel(std::string _fn);
void MakeStaticResolver();
public:
CTfLiteClass();

View File

@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
idf_component_register(SRCS ${app_sources}
INCLUDE_DIRS "."
REQUIRES tflite-lib jomjol_logfile jomjol_configfile)
REQUIRES esp_netif esp-tflite-micro jomjol_logfile jomjol_configfile)

View File

@@ -10,7 +10,8 @@
#include "esp_log.h"
#include "esp_attr.h"
#include "esp_sleep.h"
#include "esp_sntp.h"
#include "esp_netif_sntp.h"
#include "../../include/defines.h"
#include "ClassLogFile.h"
@@ -31,6 +32,9 @@ std::string getNtpStatusText(sntp_sync_status_t status);
static void setTimeZone(std::string _tzstring);
static std::string getServerName(void);
int LocalTimeToUTCOffsetSeconds;
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)
{
setenv("TZ", _tzstring.c_str(), 1);
tzset();
_tzstring = "Time zone set to " + _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) {
if (status == SNTP_SYNC_STATUS_COMPLETED) {
return "Synchronized";
@@ -235,21 +275,13 @@ bool setupTime() {
if (useNtp) {
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Configuring NTP Client...");
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, timeServer.c_str());
sntp_set_time_sync_notification_cb(time_sync_notification_cb);
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG(timeServer.c_str());
config.sync_cb = time_sync_notification_cb;
esp_netif_sntp_init(&config);
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)
* There should only be a minor correction through NTP */
@@ -258,6 +290,7 @@ bool setupTime() {
localtime_r(&now, &timeinfo);
strftime(strftime_buf, sizeof(strftime_buf), "%Y-%m-%d %H:%M:%S", &timeinfo);
if (getTimeIsSet()) {
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Time is already set: " + std::string(strftime_buf));
}

View File

@@ -28,5 +28,7 @@ bool setupTime();
bool time_manual_reset_sync(void);
extern int LocalTimeToUTCOffsetSeconds;
#endif //TIMESNTP_H

View File

@@ -378,7 +378,7 @@ void wifi_scan(void)
else {
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");
delete wifi_ap_records;
delete[] wifi_ap_records;
return;
}
}
@@ -401,7 +401,7 @@ void wifi_scan(void)
APWithBetterRSSI = true;
}
}
delete wifi_ap_records;
delete[] wifi_ap_records;
}

View File

@@ -1,3 +1,3 @@
manifest_hash: 63f5c6c9f0bcebc7b9ca12d2aa8b26b2c5f5218d377dc4b2375d9b9ca1df7815
target: esp32
version: 1.0.0
version: 1.0.0

View File

@@ -53,7 +53,7 @@
//****************************************
//compiler optimization for tflite-micro-esp-examples
//compiler optimization for esp-tflite-micro
#define XTENSA
//#define CONFIG_IDF_TARGET_ARCH_XTENSA //not needed with platformio/espressif32 @ 5.2.0
@@ -167,15 +167,6 @@
#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
//******************************
/* WIFI roaming functionalities 802.11k+v (uses ca. 6kB - 8kB internal RAM; if SCAN CACHE activated: + 1kB / beacon)

View File

@@ -3,12 +3,6 @@
#include <vector>
#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_pm.h"
@@ -16,17 +10,13 @@
#include "esp_chip_info.h"
// SD-Card ////////////////////
//#include "nvs_flash.h"
#include "sdcard_init.h"
#include "esp_vfs_fat.h"
//#include "sdmmc_cmd.h"
#include "ffconf.h"
#include "driver/sdmmc_host.h"
//#include "driver/sdmmc_defs.h"
///////////////////////////////
#include "ClassLogFile.h"
#include "connect_wlan.h"
@@ -38,7 +28,6 @@
#include "server_ota.h"
#include "time_sntp.h"
#include "configFile.h"
//#include "ClassControllCamera.h"
#include "server_main.h"
#include "server_camera.h"
#ifdef ENABLE_MQTT
@@ -49,7 +38,6 @@
#include "sdcard_check.h"
#include "../../include/defines.h"
//#include "server_GPIO.h"
#ifdef ENABLE_SOFTAP
#include "softAP.h"
@@ -101,6 +89,8 @@ bool setCpuFrequency(void);
static const char *TAG = "MAIN";
#define MOUNT_POINT "/sdcard"
bool Init_NVS_SDCard()
{
@@ -112,26 +102,30 @@ bool Init_NVS_SDCard()
ESP_LOGD(TAG, "Using SDMMC peripheral");
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.
// 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();
// To use 1-line SD mode, uncomment the following line:
#ifdef __SD_USE_ONE_LINE_MODE__
slot_config.width = 1;
#endif
// Set bus width to use:
#ifdef __SD_USE_ONE_LINE_MODE__
slot_config.width = 1;
#else
slot_config.width = 4;
#endif
// GPIOs 15, 2, 4, 12, 13 should have external 10k pull-ups.
// Internal pull-ups are not sufficient. However, enabling internal pull-ups
// does make a difference some boards, so we do that here.
gpio_set_pull_mode(GPIO_NUM_15, GPIO_PULLUP_ONLY); // CMD, needed in 4- and 1- line modes
gpio_set_pull_mode(GPIO_NUM_2, GPIO_PULLUP_ONLY); // D0, needed in 4- and 1-line modes
#ifndef __SD_USE_ONE_LINE_MODE__
gpio_set_pull_mode(GPIO_NUM_4, GPIO_PULLUP_ONLY); // D1, needed in 4-line mode only
gpio_set_pull_mode(GPIO_NUM_12, GPIO_PULLUP_ONLY); // D2, needed in 4-line mode only
#endif
gpio_set_pull_mode(GPIO_NUM_13, GPIO_PULLUP_ONLY); // D3, needed in 4- and 1-line modes
// Enable internal pullups on enabled pins. The internal pullups
// are insufficient however, please make sure 10k external pullups are
// connected on the bus. This is for debug / example purpose only.
slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
// Der PullUp des GPIO13 wird durch slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
// nicht gesetzt, da er eigentlich nicht benötigt wird,
// dies führt jedoch bei schlechten Kopien des AI_THINKER Boards
// zu Problemen mit der SD Initialisierung und eventuell sogar zur reboot-loops.
// 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.
// 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 = {
.format_if_mount_failed = false,
.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;
const char mount_point[] = MOUNT_POINT;
// 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.
// Please check its source code and implement error recovery when developing
// 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_FAIL) {
@@ -642,7 +639,6 @@ void migrateConfiguration(void) {
/* Fieldname has a <NUMBER> as prefix! */
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], ";", ""); // Enable it
}
}
@@ -650,7 +646,10 @@ void migrateConfiguration(void) {
/* Fieldname has a <NUMBER> as prefix! */
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], ";", ""); // 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) {
std::string buf;
std::size_t pos = 0;
@@ -813,4 +813,4 @@ bool setCpuFrequency(void) {
}
return true;
}
}

View File

@@ -17,6 +17,7 @@
#include "MainFlowControl.h"
#include "esp_log.h"
#include "esp_chip_info.h"
#include <stdio.h>
@@ -39,17 +40,21 @@ esp_err_t info_get_handler(httpd_req_t *req)
char _valuechar[30];
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);
if (httpd_query_key_value(_query, "type", _valuechar, 30) == ESP_OK)
{
ESP_LOGD(TAG, "type is found: %s", _valuechar);
_task = std::string(_valuechar);
}
return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "invalid query string");
}
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", "*");
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());
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;
}

View File

@@ -19,7 +19,7 @@
[common:esp32-idf]
extends = common:idf
platform = platformio/espressif32 @ 6.3.2
platform = platformio/espressif32 @ 6.5.0
framework = espidf
lib_deps =
${common:idf.lib_deps}

View File

@@ -41,6 +41,22 @@ void test_ZeigerEval()
/**
* @brief test if all combinations of digit
* 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() {
UnderTestCNN undertest = UnderTestCNN(nullptr, Digital100);
@@ -55,11 +71,11 @@ void test_ZeigerEvalHybrid() {
printf("PointerEvalHybridNew(5.7, 0, -1)\n");
// 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
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
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
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
TEST_ASSERT_EQUAL(4, undertest.PointerEvalHybridNew(5.3, 9.6, 9));
// the 5.2 with previous and the previous >=9.8 should reduce to 4
// 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
TEST_ASSERT_EQUAL(5, undertest.PointerEvalHybridNew(5.7, 9.6, 9));

View File

@@ -33,7 +33,17 @@ std::string process_doFlow(UnderTestPost* _underTestPost) {
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,
bool checkConsistency, bool extendedResolution, int decimal_shift) {
// setup the classundertest

View File

@@ -51,21 +51,32 @@ void testNegative() {
delete underTestPost;
// extendResolution=false
// value < preValue
preValue = 16.99; // zu groß
// value < (preValue -.01)
preValue = 17.00; // zu groß
underTestPost = init_do_flow(analogs, digits, Digital100, false, false, 0);
setAllowNegatives(underTestPost, false);
setPreValue(underTestPost, preValue_extended);
setPreValue(underTestPost, preValue);
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());
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
// value < preValue
// 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);
setAllowNegatives(underTestPost, true);
setPreValue(underTestPost, preValue_extended);
@@ -84,8 +95,8 @@ void testNegative_Issues() {
// Ohne decimal_shift
std::vector<float> digits = { 2.0, 2.0, 0.0, 1.0, 7.2, 9.0, 8.0};
std::vector<float> analogs = { };
double preValue_extended = 22018.080;
double preValue = 22018.08;
double preValue_extended = 22018.090;
double preValue = 22018.09;
const char* expected = "22017.98";
@@ -93,13 +104,15 @@ void testNegative_Issues() {
// extendResolution=false
// value < preValue
// 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);
setAllowNegatives(underTestPost, false);
setPreValue(underTestPost, preValue_extended);
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(expected, result.c_str());
// if negativ no result any more
TEST_ASSERT_EQUAL_STRING("", result.c_str());
delete underTestPost;
}

View File

@@ -1,7 +1,6 @@
#include "test_flow_postrocess_helper.h"
#include <memory>
/**
* 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
std::vector<float> digits = { 0.0, 2.9, 3.0, 2.9, 3.5, 9.5};
std::vector<float> analogs = { };
const char* expected = "33330";
const char* expected = "33339";
std::string result = process_doFlow(analogs, digits);
TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
// 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};
analogs = { };
expected = "33340";
expected = "33339";
result = process_doFlow(analogs, digits);
TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
// 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};
analogs = { };
expected = "704179";
expected = "704178";
result = process_doFlow(analogs, digits);
TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
// 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)
analogs = { };
expected = "7153693";
expected = "7153692";
result = process_doFlow(analogs, digits);
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
digits = { 3.0, 2.0, 2.0, 8.0, 9.0, 4.0, 1.7, 9.8}; // falscher Wert 32290.420
analogs = { };
expected = "32289.420";
expected = "32289.419";
const char* expected_extended= "32289.4198";
// FALSCH! wegen ungenügender Präzision von NUMBERS->Value
// expected_extended= "32289.4198";
@@ -230,7 +229,7 @@ void test_doFlowPP2() {
// 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
analogs = { };
expected = "123236";
expected = "123235";
expected_extended= "123235.6";
// 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());
}

View File

@@ -1,10 +1,5 @@
#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 ////////////////////
#include "nvs_flash.h"
#include "esp_vfs_fat.h"
@@ -15,6 +10,18 @@
#define __SD_USE_ONE_LINE_MODE__
#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()
{
esp_err_t ret = nvs_flash_init();
@@ -81,7 +88,6 @@ bool Init_NVS_SDCard()
}
void initGPIO()
{
gpio_config_t io_conf;
@@ -96,11 +102,51 @@ void initGPIO()
}
/**
* @brief startup the test. Like a test-suite
* 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()
{
initGPIO();
@@ -109,8 +155,8 @@ extern "C" void app_main()
UNITY_BEGIN();
RUN_TEST(testNegative_Issues);
/* RUN_TEST(testNegative);
RUN_TEST(testNegative);
/*
RUN_TEST(test_analogToDigit_Standard);
RUN_TEST(test_analogToDigit_Transition);
RUN_TEST(test_doFlowPP);
@@ -118,6 +164,8 @@ extern "C" void app_main()
RUN_TEST(test_doFlowPP2);
RUN_TEST(test_doFlowPP3);
RUN_TEST(test_doFlowPP4);
RUN_TEST(test_doFlowLateTransition);
RUN_TEST(test_doFlowEarlyTransition);
// getReadoutRawString test
RUN_TEST(test_getReadoutRawString);

Binary file not shown.

View File

@@ -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>
<p>Notes:</p>
<h2>Notes:</h2>
<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>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>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>
<hr>
<p><esp-web-install-button manifest="manifest.json"></esp-web-install-button></p>
<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>

View File

@@ -1,6 +1,6 @@
{
"name": "AI-on-the-edge",
"version": "13.0.8",
"version": "$VERSION",
"funding_url": "https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL",
"new_install_prompt_erase": false,
"builds": [

View File

@@ -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
}
]
}
]
}

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

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
View File

@@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

View 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>

View 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
View 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
View 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
View 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
View 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.

View 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

View 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)

View File

@@ -0,0 +1,4 @@
InitialRotate
MainTopicMQTT
AutoAdjustSummertime
SetupMode

View 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

View 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:
![](img/flipImageSize.png)

View 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.

View 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!

View 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.

View 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.

View 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!

View File

@@ -0,0 +1,5 @@
# Parameter `ExtendedResolution`
!!! Warning
This parameter is unused!
Use [`NUMBER.ExtendedResolution`](../Parameters/#PostProcessing-NUMBER.ExtendedResolution) instead!

View 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.

View 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!

View File

@@ -0,0 +1,6 @@
# Parameter `ROIImagesRetention`
Default Value: `3`
Unit: Days
Days to keep the separated analog images (`0` = forever).

View 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.

View 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.

View File

@@ -0,0 +1,6 @@
# Parameter `DataFilesRetention`
Default Value: `3`
Unit: Days
Number of days to keep the data files (`0` = forever).

View 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!

View 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!

View File

@@ -0,0 +1,6 @@
# Parameter `LogfilesRetention`
Default Value: `3`
Unit: Days
Number of days to keep the log files (`0` = forever).

View 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!

View 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.

View 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!

View File

@@ -0,0 +1,6 @@
# Parameter `ROIImagesRetention`
Default Value: `3`
Unit: Days
Days to keep the separated digit images (`0` = forever).

View 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, _, -`.

View 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, _, -`.

View 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, _, -`.

View 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, _, -`.

View 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, _, -`.

View 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, _, -`.

View 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).

View File

@@ -0,0 +1,4 @@
# Parameter `LEDNumbers`
Default Value: `2`
Number of LEDs on the external LED-stripe attached to GPIO12 (See `IO12` parameter).

View File

@@ -0,0 +1,3 @@
# Parameter `LEDType`
Default Value: `WS2812`
Type of the `WS2812x` which is connected to GPIO12 (See `IO12` parameter).

Some files were not shown because too many files have changed in this diff Show More