mirror of
https://github.com/jomjol/AI-on-the-edge-device.git
synced 2025-12-08 04:26:58 +03:00
Compare commits
367 Commits
v14.0.3
...
analog-dig
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
919ede0e7c | ||
|
|
b0a7c7716f | ||
|
|
aff08fafee | ||
|
|
ed746f96b9 | ||
|
|
fa08a02e12 | ||
|
|
57715b5bdd | ||
|
|
53f3b99f05 | ||
|
|
ba63d0a589 | ||
|
|
0309620fd6 | ||
|
|
295038a881 | ||
|
|
488ae174a4 | ||
|
|
4049d752ba | ||
|
|
4b4b42d4c5 | ||
|
|
135b03d505 | ||
|
|
74c09ac023 | ||
|
|
dc90972659 | ||
|
|
32282ecfe2 | ||
|
|
ae6a94544b | ||
|
|
70b031eacc | ||
|
|
c3fadf5c2a | ||
|
|
7e5f6bf4a5 | ||
|
|
88b531ae8b | ||
|
|
8481cc4b26 | ||
|
|
ecaed38c1d | ||
|
|
d6a1838d47 | ||
|
|
5194c466be | ||
|
|
043de9265a | ||
|
|
08a350172d | ||
|
|
7e806df64d | ||
|
|
dccfb5e91e | ||
|
|
fbe4609bb9 | ||
|
|
3e85cfb456 | ||
|
|
5dff4ca8cf | ||
|
|
0d0b0187f4 | ||
|
|
4cf9ea6c45 | ||
|
|
1b76e0f449 | ||
|
|
d4a0ad20ff | ||
|
|
df72445e79 | ||
|
|
456cb93809 | ||
|
|
d968a7adc6 | ||
|
|
9a52b8b2f3 | ||
|
|
8caa852bbf | ||
|
|
4faca4c46c | ||
|
|
238fc5fae3 | ||
|
|
c32ca5a23c | ||
|
|
827d9d1700 | ||
|
|
0ea4b3b3ce | ||
|
|
3e26c6c743 | ||
|
|
fa5c99b3cb | ||
|
|
6feae4e239 | ||
|
|
ef64be3888 | ||
|
|
cc89d625f2 | ||
|
|
08ba754b88 | ||
|
|
6b38e44d7f | ||
|
|
141aea7fa7 | ||
|
|
bcd07761b6 | ||
|
|
fe4d861e15 | ||
|
|
71322c9fbe | ||
|
|
f8b4881a50 | ||
|
|
438d5696e4 | ||
|
|
0d391c8780 | ||
|
|
59de6319a1 | ||
|
|
3805687219 | ||
|
|
c6a789dc45 | ||
|
|
246f9cfc31 | ||
|
|
b2d8c60bb1 | ||
|
|
1d573cd18a | ||
|
|
b2e5cdd8a3 | ||
|
|
f7fde7c430 | ||
|
|
2c19080a66 | ||
|
|
35663c5fd4 | ||
|
|
5b449d5c45 | ||
|
|
3a5f3496d5 | ||
|
|
00434d01c3 | ||
|
|
3e0bb81e32 | ||
|
|
4f57f9eafd | ||
|
|
f24ec581e6 | ||
|
|
0d78bb78ea | ||
|
|
47aea007b3 | ||
|
|
284b3f428e | ||
|
|
92d45c7971 | ||
|
|
ff1d9d3b4f | ||
|
|
2b57dd0853 | ||
|
|
dd8f5eea22 | ||
|
|
60c5305378 | ||
|
|
8b1c65a38a | ||
|
|
4f5933c4f2 | ||
|
|
06c9bfb0de | ||
|
|
69f1a99b55 | ||
|
|
a8fb88a35d | ||
|
|
d418d22155 | ||
|
|
53e8cf49f9 | ||
|
|
ac4f823cbf | ||
|
|
1f4b8807fb | ||
|
|
827ca1cf61 | ||
|
|
dc640d7bce | ||
|
|
b551eadf8a | ||
|
|
8e8ac621dd | ||
|
|
54d4990d16 | ||
|
|
f8dd986e4f | ||
|
|
444dc0fa39 | ||
|
|
5053a31245 | ||
|
|
44cf8933d4 | ||
|
|
35de56be04 | ||
|
|
80a6fc1dc3 | ||
|
|
d2c47fcde2 | ||
|
|
7b3a493587 | ||
|
|
217f543578 | ||
|
|
797fc5e764 | ||
|
|
7a4e82a44e | ||
|
|
019069cd16 | ||
|
|
4de1152cf3 | ||
|
|
0e0fb459dc | ||
|
|
8410df6144 | ||
|
|
4990858101 | ||
|
|
fc1f8ee242 | ||
|
|
252c399a76 | ||
|
|
eb7f2b3705 | ||
|
|
2ed6fb0f0d | ||
|
|
b5213b01af | ||
|
|
473e458b85 | ||
|
|
4f3f3d9af2 | ||
|
|
b5a4cfed96 | ||
|
|
6fca4d8d95 | ||
|
|
a11e19fb0c | ||
|
|
09fa94c95f | ||
|
|
f01b4dbd13 | ||
|
|
dbf1770016 | ||
|
|
006f3aa063 | ||
|
|
24c46c38b4 | ||
|
|
9ced147d9c | ||
|
|
0808895bd6 | ||
|
|
d2dec9fa59 | ||
|
|
7e7bc3dd68 | ||
|
|
5b09cd0d59 | ||
|
|
74d4f20858 | ||
|
|
e790a14caa | ||
|
|
f023a6b739 | ||
|
|
1e463188ea | ||
|
|
9991196961 | ||
|
|
ecece0f7fc | ||
|
|
1b3b7595c1 | ||
|
|
6d10f712d1 | ||
|
|
b84a2db050 | ||
|
|
374462a7d8 | ||
|
|
4facd7be05 | ||
|
|
3baf5865ad | ||
|
|
02138c44ac | ||
|
|
1094c8a0a8 | ||
|
|
75b15b8e9d | ||
|
|
36c12b400b | ||
|
|
35d90cd0ee | ||
|
|
999a8d9374 | ||
|
|
8a4269c6a0 | ||
|
|
8f5579cca5 | ||
|
|
e58b3a2cf8 | ||
|
|
222ee0921c | ||
|
|
e2bfcd26c9 | ||
|
|
18917b2d82 | ||
|
|
f0dea3abcb | ||
|
|
bd2d8b4a15 | ||
|
|
80e3f50a5b | ||
|
|
f6ca32d69f | ||
|
|
49e919c481 | ||
|
|
de768c4f44 | ||
|
|
002fc033aa | ||
|
|
f4af8de699 | ||
|
|
e4d6707a0b | ||
|
|
9f03e68690 | ||
|
|
b1df7df580 | ||
|
|
ebc9be4a28 | ||
|
|
5d0fc73c13 | ||
|
|
40a1aa0430 | ||
|
|
d7a733512f | ||
|
|
dc9f1aad27 | ||
|
|
cd3e641bcc | ||
|
|
ad72ffa37c | ||
|
|
2d45a0ed26 | ||
|
|
e8065ef414 | ||
|
|
33893eb566 | ||
|
|
3fbff0ad33 | ||
|
|
28cabea7f2 | ||
|
|
5d06130a88 | ||
|
|
3497a86084 | ||
|
|
84707fb27a | ||
|
|
0b81af7cb8 | ||
|
|
61efe1a6ef | ||
|
|
09ecd722cc | ||
|
|
5615fd8137 | ||
|
|
7ebf68411f | ||
|
|
34835dca84 | ||
|
|
697ff4c4b6 | ||
|
|
0f7c685933 | ||
|
|
3ace5aeff1 | ||
|
|
d47ed060b0 | ||
|
|
e7dbebffa1 | ||
|
|
def13d46af | ||
|
|
f82a6bf513 | ||
|
|
05deecee00 | ||
|
|
8339b6788f | ||
|
|
4e5b084932 | ||
|
|
62fcefee78 | ||
|
|
14128ca3a7 | ||
|
|
431551fb45 | ||
|
|
ff52d785cd | ||
|
|
f5a7c082d6 | ||
|
|
f3e90c6293 | ||
|
|
cbd14a267f | ||
|
|
ba7d6b3621 | ||
|
|
d4a492463b | ||
|
|
46589288e7 | ||
|
|
81a57d32a8 | ||
|
|
17994494c9 | ||
|
|
c581d64a26 | ||
|
|
70c2b88f8a | ||
|
|
13d01a6c96 | ||
|
|
331e3533cc | ||
|
|
5a00bdd7f6 | ||
|
|
db7ffb30c4 | ||
|
|
9a07f271ff | ||
|
|
6a45ab7693 | ||
|
|
9fc876853b | ||
|
|
c30881f73c | ||
|
|
e75e720869 | ||
|
|
43aae5a3cd | ||
|
|
54cd7e511a | ||
|
|
c5a82e839f | ||
|
|
9f97a2b223 | ||
|
|
8d06de5792 | ||
|
|
1326585a33 | ||
|
|
d7a507ca05 | ||
|
|
b24b7d5ce2 | ||
|
|
fc719da0ae | ||
|
|
9b1a83c8b4 | ||
|
|
6defcf8d4c | ||
|
|
c2a55e7c86 | ||
|
|
a636ce3679 | ||
|
|
a20fec1094 | ||
|
|
929796c87f | ||
|
|
e40ceb54ce | ||
|
|
eebdd7c6eb | ||
|
|
2a7f3b33a3 | ||
|
|
262d83ee6f | ||
|
|
de92c29245 | ||
|
|
19158c998f | ||
|
|
17ffd28c05 | ||
|
|
9ca02e0a7f | ||
|
|
7488d7bf23 | ||
|
|
e7bfba4b01 | ||
|
|
63ac38a52d | ||
|
|
e2cf8337d4 | ||
|
|
e995d6c498 | ||
|
|
0e3a50d0c1 | ||
|
|
df12deae00 | ||
|
|
b6bfeea936 | ||
|
|
f79e03faa2 | ||
|
|
de1dcc4d06 | ||
|
|
33bfef0af4 | ||
|
|
727b871fc5 | ||
|
|
03c84a1ff3 | ||
|
|
db36fe2522 | ||
|
|
9ffaf6e3f8 | ||
|
|
fa09680711 | ||
|
|
e2b66aa73a | ||
|
|
c4b990ada0 | ||
|
|
267782d083 | ||
|
|
e4a6fd33fe | ||
|
|
5db20d3687 | ||
|
|
58185a0569 | ||
|
|
b5e0d6ee66 | ||
|
|
d93c5daf14 | ||
|
|
6ff83445ac | ||
|
|
d944e8676b | ||
|
|
a8d7b29507 | ||
|
|
ab0fc72257 | ||
|
|
933215c116 | ||
|
|
e81a7eebe8 | ||
|
|
7d33c3ee5f | ||
|
|
863856ca5d | ||
|
|
eefc41d6ff | ||
|
|
d1807a1b3d | ||
|
|
dfc45772b7 | ||
|
|
4dd41c486f | ||
|
|
ff81fcbd7f | ||
|
|
5e5d2e2f72 | ||
|
|
53cee961f4 | ||
|
|
c81f901a19 | ||
|
|
512d7f95b4 | ||
|
|
6642f6f995 | ||
|
|
431df73e52 | ||
|
|
1f5d4de5f3 | ||
|
|
ce00192684 | ||
|
|
28f3ad0242 | ||
|
|
2dfd55e1c3 | ||
|
|
f84f20b2e8 | ||
|
|
806adcb4d0 | ||
|
|
4dc4752823 | ||
|
|
827423023c | ||
|
|
6ca7897fa0 | ||
|
|
39b8b5b07c | ||
|
|
5b98acaa32 | ||
|
|
d3d241c7b9 | ||
|
|
8c7e86fea4 | ||
|
|
98d85b2c6c | ||
|
|
f42e9c71f2 | ||
|
|
00be21f9e1 | ||
|
|
c694d9f363 | ||
|
|
a7dc37761b | ||
|
|
2dd2d03f6c | ||
|
|
4c407499d2 | ||
|
|
b97d808b54 | ||
|
|
9f2e91a9df | ||
|
|
18e96d62a6 | ||
|
|
a1a77ae5d9 | ||
|
|
c5b20f3680 | ||
|
|
add6cf5c33 | ||
|
|
493bd4df2f | ||
|
|
53ff190860 | ||
|
|
9a9aa68a65 | ||
|
|
a92cb69067 | ||
|
|
90fa44380c | ||
|
|
7a9f61a8d8 | ||
|
|
a8f8189543 | ||
|
|
2c4bda9e66 | ||
|
|
7b2a80a13d | ||
|
|
95d312b920 | ||
|
|
886cd4ffa5 | ||
|
|
10e0435383 | ||
|
|
b0de37b762 | ||
|
|
22e4b39f77 | ||
|
|
2c1a7f4c9e | ||
|
|
3960823439 | ||
|
|
d5ef08546a | ||
|
|
3d711f495e | ||
|
|
b21e3c6c9d | ||
|
|
23d2ae627d | ||
|
|
3f62abf878 | ||
|
|
025f4af9f2 | ||
|
|
3d92860c5e | ||
|
|
2ed9fb8eb5 | ||
|
|
598db004ae | ||
|
|
70332fe142 | ||
|
|
10da8c4f94 | ||
|
|
5bac1c68d9 | ||
|
|
008dba7e11 | ||
|
|
7283bfd506 | ||
|
|
56788652ae | ||
|
|
44e186e65b | ||
|
|
55be652dc1 | ||
|
|
1acd72d33e | ||
|
|
795bcd0d21 | ||
|
|
0b2e38935b | ||
|
|
a9c5bebb45 | ||
|
|
876adc51af | ||
|
|
bf090f3762 | ||
|
|
30a50720e5 | ||
|
|
3fa16c5624 | ||
|
|
b9134f923e | ||
|
|
06f4d417b5 | ||
|
|
55efc3b3f4 | ||
|
|
800e231301 | ||
|
|
34a3d6d6e3 | ||
|
|
4bfe5422c5 | ||
|
|
d63dc08f33 | ||
|
|
2ee85001eb | ||
|
|
1d2f920819 | ||
|
|
69583db99e |
@@ -3,7 +3,32 @@
|
|||||||
# Make sure to also add the response to .github/workflows/reply-bot.yml!
|
# 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!
|
# Due to the way it works, you have to add each response twice, once for the issue, once for the discussions!
|
||||||
|
|
||||||
labels:
|
labels:
|
||||||
|
#######################################################################
|
||||||
|
# Bot Response: Documentation
|
||||||
|
#######################################################################
|
||||||
|
- name: bot-reply Documentation
|
||||||
|
labeled:
|
||||||
|
issue:
|
||||||
|
body: |
|
||||||
|
Please have a look on our documentation: https://jomjol.github.io/AI-on-the-edge-device-docs
|
||||||
|
discussion:
|
||||||
|
body: |
|
||||||
|
Please have a look on our documentation: https://jomjol.github.io/AI-on-the-edge-device-docs
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
# Bot Response: ROI setup
|
||||||
|
#######################################################################
|
||||||
|
- name: bot-reply ROI Setup
|
||||||
|
labeled:
|
||||||
|
issue:
|
||||||
|
body: |
|
||||||
|
Make sure to setup your ROIs properly. Have a look on our documentation: https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/#how-to-setup-the-digit-rois-perfectly
|
||||||
|
discussion:
|
||||||
|
body: |
|
||||||
|
Make sure to setup your ROIs properly. Have a look on our documentation: https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/#how-to-setup-the-digit-rois-perfectly
|
||||||
|
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
# Bot Response: Logfile
|
# Bot Response: Logfile
|
||||||
#######################################################################
|
#######################################################################
|
||||||
100
.github/workflows/build.yaml
vendored
100
.github/workflows/build.yaml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
concurrent_skipping: same_content_newer
|
concurrent_skipping: same_content_newer
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
@@ -25,28 +25,28 @@ jobs:
|
|||||||
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Update PIP cache on every commit
|
- name: Update PIP cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/pip
|
path: ~/.cache/pip
|
||||||
key: pip-${{ github.run_id }}
|
key: pip-${{ github.run_id }}
|
||||||
restore-keys: pip # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
restore-keys: pip # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||||
|
|
||||||
- name: Update PlatformIO cache on every commit
|
- name: Update PlatformIO cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
key: platformio-${{ github.run_id }}
|
key: platformio-${{ github.run_id }}
|
||||||
restore-keys: platformio # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
restore-keys: platformio # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||||
|
|
||||||
- name: Update Build cache on every commit
|
- name: Update Build cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ./code/.pio/
|
path: ./code/.pio/
|
||||||
key: build-${{ github.run_id }}
|
key: build-${{ github.run_id }}
|
||||||
restore-keys: build # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
restore-keys: build # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||||
|
|
||||||
- name: Update generated-files cache on every commit
|
- name: Update generated-files cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
./code/.pio/build/esp32cam/firmware.bin
|
./code/.pio/build/esp32cam/firmware.bin
|
||||||
@@ -57,7 +57,7 @@ jobs:
|
|||||||
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.10'
|
python-version: '3.10'
|
||||||
|
|
||||||
@@ -70,15 +70,24 @@ jobs:
|
|||||||
#run: echo "Testing... ${{ github.ref_name }}, ${{ steps.vars.outputs.sha_short }}" > ./sd-card/html/version.txt; mkdir -p ./code/.pio/build/esp32cam/; cd ./code/.pio/build/esp32cam/; echo "${{ steps.vars.outputs.sha_short }}" > firmware.bin; cp firmware.bin partitions.bin; cp firmware.bin bootloader.bin # Testing
|
#run: echo "Testing... ${{ github.ref_name }}, ${{ steps.vars.outputs.sha_short }}" > ./sd-card/html/version.txt; mkdir -p ./code/.pio/build/esp32cam/; cd ./code/.pio/build/esp32cam/; echo "${{ steps.vars.outputs.sha_short }}" > firmware.bin; cp firmware.bin partitions.bin; cp firmware.bin bootloader.bin # Testing
|
||||||
run: cd code; platformio run --environment esp32cam
|
run: cd code; platformio run --environment esp32cam
|
||||||
|
|
||||||
- name: Prepare Web UI (copy data from repo and update hashes in all files)
|
- name: Prepare Web UI (generate tooltip pages and update hashes in all files)
|
||||||
run: |
|
run: |
|
||||||
rm -rf ./html
|
rm -rf ./html
|
||||||
mkdir html
|
mkdir html
|
||||||
cp ./sd-card/html/* ./html/
|
cp -r ./sd-card/html/* ./html/
|
||||||
|
|
||||||
|
python -m pip install markdown
|
||||||
|
mkdir html/param-tooltips
|
||||||
|
cd tools/parameter-tooltip-generator
|
||||||
|
python generate-param-doc-tooltips.py
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
cp -r ./sd-card/html/* ./html/
|
||||||
|
|
||||||
|
echo "Replacing variables..."
|
||||||
cd html; find . -type f -exec sed -i 's/$COMMIT_HASH/${{ steps.vars.outputs.sha_short }}/g' {} \;
|
cd html; find . -type f -exec sed -i 's/$COMMIT_HASH/${{ steps.vars.outputs.sha_short }}/g' {} \;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#########################################################################################
|
#########################################################################################
|
||||||
## Pack for Update
|
## Pack for Update
|
||||||
#########################################################################################
|
#########################################################################################
|
||||||
@@ -86,16 +95,16 @@ jobs:
|
|||||||
# New OTA concept
|
# New OTA concept
|
||||||
# update__version.zip file with following content:
|
# update__version.zip file with following content:
|
||||||
# - /firmware.bin
|
# - /firmware.bin
|
||||||
# - (optional) /html/*
|
# - (optional) /html/* (inkl. subfolders)
|
||||||
# - (optional) /config/*.tfl
|
# - (optional) /config/*.tfl
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: build
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Update generated-files cache on every commit
|
- name: Update generated-files cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
./code/.pio/build/esp32cam/firmware.bin
|
./code/.pio/build/esp32cam/firmware.bin
|
||||||
@@ -106,7 +115,7 @@ jobs:
|
|||||||
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||||
|
|
||||||
- name: Update update cache on every commit
|
- name: Update update cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: update
|
path: update
|
||||||
key: update-${{ github.run_id }}
|
key: update-${{ github.run_id }}
|
||||||
@@ -135,7 +144,7 @@ jobs:
|
|||||||
cp ./sd-card/config/*.tflite ./update/config/ 2>/dev/null || true
|
cp ./sd-card/config/*.tflite ./update/config/ 2>/dev/null || true
|
||||||
|
|
||||||
- name: Upload update as update.zip artifact (Firmware + Web UI + CNN)
|
- name: Upload update as update.zip artifact (Firmware + Web UI + CNN)
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: "AI-on-the-edge-device__update__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
|
name: "AI-on-the-edge-device__update__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
|
||||||
path: ./update/*
|
path: ./update/*
|
||||||
@@ -149,16 +158,16 @@ jobs:
|
|||||||
# New Remote Setup concept
|
# New Remote Setup concept
|
||||||
# remote_setup__version.zip file with following content:
|
# remote_setup__version.zip file with following content:
|
||||||
# - /firmware.bin
|
# - /firmware.bin
|
||||||
# - /html/*
|
# - /html/* (inkl. subfolders)
|
||||||
# - /config/*
|
# - /config/*
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: build
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Update generated-files cache on every commit
|
- name: Update generated-files cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
./code/.pio/build/esp32cam/firmware.bin
|
./code/.pio/build/esp32cam/firmware.bin
|
||||||
@@ -169,7 +178,7 @@ jobs:
|
|||||||
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||||
|
|
||||||
- name: Update remote_setup cache on every commit
|
- name: Update remote_setup cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: remote_setup
|
path: remote_setup
|
||||||
key: remote_setup-${{ github.run_id }}
|
key: remote_setup-${{ github.run_id }}
|
||||||
@@ -196,7 +205,7 @@ jobs:
|
|||||||
cp ./sd-card/config/* ./remote_setup/config/ 2>/dev/null || true
|
cp ./sd-card/config/* ./remote_setup/config/ 2>/dev/null || true
|
||||||
|
|
||||||
- name: Upload remote_setup as remote_setup.zip artifact (Firmware + Web UI + Config)
|
- name: Upload remote_setup as remote_setup.zip artifact (Firmware + Web UI + Config)
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: "AI-on-the-edge-device__remote-setup__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
|
name: "AI-on-the-edge-device__remote-setup__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
|
||||||
path: ./remote_setup/*
|
path: ./remote_setup/*
|
||||||
@@ -211,10 +220,10 @@ jobs:
|
|||||||
needs: build
|
needs: build
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Update generated-files cache on every commit
|
- name: Update generated-files cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
./code/.pio/build/esp32cam/firmware.bin
|
./code/.pio/build/esp32cam/firmware.bin
|
||||||
@@ -225,7 +234,7 @@ jobs:
|
|||||||
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||||
|
|
||||||
- name: Update manual_setup cache on every commit
|
- name: Update manual_setup cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: manual_setup
|
path: manual_setup
|
||||||
key: manual_setup-${{ github.run_id }}
|
key: manual_setup-${{ github.run_id }}
|
||||||
@@ -254,7 +263,7 @@ jobs:
|
|||||||
cd ./manual_setup
|
cd ./manual_setup
|
||||||
|
|
||||||
- name: Upload manual_setup.zip artifact (Firmware + Bootloader + Partitions + Web UI)
|
- name: Upload manual_setup.zip artifact (Firmware + Bootloader + Partitions + Web UI)
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: "AI-on-the-edge-device__manual-setup__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
|
name: "AI-on-the-edge-device__manual-setup__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
|
||||||
path: ./manual_setup
|
path: ./manual_setup
|
||||||
@@ -275,24 +284,24 @@ jobs:
|
|||||||
id-token: write
|
id-token: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Update update cache on every commit
|
- name: Update update cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: update
|
path: update
|
||||||
key: update-${{ github.run_id }}
|
key: update-${{ github.run_id }}
|
||||||
restore-keys: update # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
restore-keys: update # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||||
|
|
||||||
- name: Update remote_setup cache on every commit
|
- name: Update remote_setup cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: remote_setup
|
path: remote_setup
|
||||||
key: remote_setup-${{ github.run_id }}
|
key: remote_setup-${{ github.run_id }}
|
||||||
restore-keys: remote_setup # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
restore-keys: remote_setup # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||||
|
|
||||||
- name: Update manual_setup cache on every commit
|
- name: Update manual_setup cache on every commit
|
||||||
uses: actions/cache@v3.2.3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: manual_setup
|
path: manual_setup
|
||||||
key: manual_setup-${{ github.run_id }}
|
key: manual_setup-${{ github.run_id }}
|
||||||
@@ -324,7 +333,7 @@ jobs:
|
|||||||
# extract the version used in next step
|
# extract the version used in next step
|
||||||
- id: get_version
|
- id: get_version
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
uses: battila7/get-version-action@v2
|
uses: Simply007/get-version-action@v2
|
||||||
|
|
||||||
# # the changelog [unreleased] will now be changed to the release version
|
# # the changelog [unreleased] will now be changed to the release version
|
||||||
# - name: Update changelog
|
# - name: Update changelog
|
||||||
@@ -334,13 +343,13 @@ jobs:
|
|||||||
# changelogPath: Changelog.md
|
# changelogPath: Changelog.md
|
||||||
# version: ${{ steps.get_version.outputs.version-without-v }}
|
# version: ${{ steps.get_version.outputs.version-without-v }}
|
||||||
|
|
||||||
# the release notes will be extracted from changelog
|
# # the release notes will be extracted from changelog
|
||||||
- name: Extract release notes
|
# - name: Extract release notes
|
||||||
id: extract-release-notes
|
# id: extract-release-notes
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
# if: startsWith(github.ref, 'refs/tags/')
|
||||||
uses: ffurrer2/extract-release-notes@v1
|
# uses: ffurrer2/extract-release-notes@v1
|
||||||
with:
|
# with:
|
||||||
changelog_file: Changelog.md
|
# changelog_file: Changelog.md
|
||||||
|
|
||||||
# Releases should only be created on master by tagging the last commit.
|
# Releases should only be created on master by tagging the last commit.
|
||||||
# all artifacts in firmware folder pushed to the release
|
# all artifacts in firmware folder pushed to the release
|
||||||
@@ -371,7 +380,7 @@ jobs:
|
|||||||
#########################################################################################
|
#########################################################################################
|
||||||
## Update the Web Installer on a release
|
## Update the Web Installer on a release
|
||||||
#########################################################################################
|
#########################################################################################
|
||||||
# This is the same as in the update-webinstaller.yml
|
# Make sure to also update update-webinstaller.yml!
|
||||||
update-web-installer:
|
update-web-installer:
|
||||||
needs: [release]
|
needs: [release]
|
||||||
environment:
|
environment:
|
||||||
@@ -387,11 +396,11 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Get version of last release
|
- name: Get version of last release
|
||||||
id: last_release
|
id: last_release
|
||||||
uses: InsonusK/get-latest-release@v1.0.1
|
uses: mindojo/get-latest-release@0b8ef1434d7468d6bffcc8263baff5c777f72321
|
||||||
with:
|
with:
|
||||||
myToken: ${{ github.token }}
|
myToken: ${{ github.token }}
|
||||||
exclude_types: "draft|prerelease"
|
exclude_types: "draft|prerelease"
|
||||||
@@ -399,20 +408,23 @@ jobs:
|
|||||||
|
|
||||||
- name: Add binary to Web Installer and update manifest
|
- name: Add binary to Web Installer and update manifest
|
||||||
run: |
|
run: |
|
||||||
|
echo "Updating Web installer to use firmware from ${{ steps.last_release.outputs.tag_name }}..."
|
||||||
rm -f docs/binary/firmware.bin
|
rm -f docs/binary/firmware.bin
|
||||||
wget https://github.com/jomjol/AI-on-the-edge-device/releases/download/${{ steps.last_release.outputs.tag_name }}/AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
wget ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.last_release.outputs.tag_name }}/AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
||||||
unzip AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
unzip AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
||||||
cp -f firmware.bin docs/binary/firmware.bin
|
cp -f firmware.bin docs/binary/firmware.bin
|
||||||
cp -f docs/manifest_template.json docs/manifest.json
|
echo "Updating index and manifest file..."
|
||||||
sed -i 's/VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
|
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/index.html
|
||||||
|
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
|
||||||
|
|
||||||
- name: Setup Pages
|
- name: Setup Pages
|
||||||
uses: actions/configure-pages@v2
|
uses: actions/configure-pages@v4
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-pages-artifact@v1
|
uses: actions/upload-pages-artifact@v2
|
||||||
with:
|
with:
|
||||||
path: 'docs'
|
path: 'docs'
|
||||||
|
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@v1
|
uses: actions/deploy-pages@v3 # Note: v4 does not work!
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# This updates the Web Installer with the files from the docs folder and the binary of the latest release
|
# This updates the Web Installer with the files from the docs folder and the binary of the latest release
|
||||||
# it only gets run on:
|
# it only gets run on:
|
||||||
# - Changes to the docs folder in the `rolling` branch
|
# - Manually triggered
|
||||||
# - On a release
|
# Make sure to also update the lower part of build.yml!
|
||||||
|
|
||||||
name: Manual Web Installer Update
|
name: Manual Web Installer Update
|
||||||
|
|
||||||
@@ -32,20 +32,22 @@ jobs:
|
|||||||
|
|
||||||
- name: Get version of last release
|
- name: Get version of last release
|
||||||
id: last_release
|
id: last_release
|
||||||
uses: InsonusK/get-latest-release@v1.0.1
|
uses: mindojo/get-latest-release@0b8ef1434d7468d6bffcc8263baff5c777f72321
|
||||||
with:
|
with:
|
||||||
myToken: ${{ github.token }}
|
myToken: ${{ github.token }}
|
||||||
exclude_types: "release"
|
exclude_types: "draft|prerelease"
|
||||||
view_top: 1
|
view_top: 1
|
||||||
|
|
||||||
- name: Add binary to Web Installer and update manifest
|
- name: Add binary to Web Installer and update manifest
|
||||||
run: |
|
run: |
|
||||||
|
echo "Updating Web installer to use firmware from ${{ steps.last_release.outputs.tag_name }}..."
|
||||||
rm -f docs/binary/firmware.bin
|
rm -f docs/binary/firmware.bin
|
||||||
wget https://github.com/jomjol/AI-on-the-edge-device/releases/download/${{ steps.last_release.outputs.tag_name }}/AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
wget https://github.com/jomjol/AI-on-the-edge-device/releases/download/${{ steps.last_release.outputs.tag_name }}/AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
||||||
unzip AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
unzip AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
||||||
cp -f firmware.bin docs/binary/firmware.bin
|
cp -f firmware.bin docs/binary/firmware.bin
|
||||||
cp -f docs/manifest_template.json docs/manifest.json
|
echo "Updating index and manifest file..."
|
||||||
sed -i 's/VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
|
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/index.html
|
||||||
|
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
|
||||||
|
|
||||||
- name: Setup Pages
|
- name: Setup Pages
|
||||||
uses: actions/configure-pages@v2
|
uses: actions/configure-pages@v2
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# Reply Bot
|
# Reply Bot
|
||||||
# It uses the configuration in .github/label-commenter-config.yml
|
# It uses the configuration in .github/label-commenter-config.yaml
|
||||||
# See https://github.com/peaceiris/actions-label-commenter
|
# See https://github.com/peaceiris/actions-label-commenter
|
||||||
|
|
||||||
name: Reply-Bot
|
name: Reply-Bot
|
||||||
@@ -25,7 +25,7 @@ jobs:
|
|||||||
|
|
||||||
####################################################################
|
####################################################################
|
||||||
## Remove labels again (issues only)
|
## Remove labels again (issues only)
|
||||||
## Make sure to also add the reply message to .github/label-commenter-config.yml!
|
## Make sure to also add the reply message to .github/label-commenter-config.yaml!
|
||||||
## This currently seems no longer to work due to changes on the actions-cool/issues-helper!
|
## This currently seems no longer to work due to changes on the actions-cool/issues-helper!
|
||||||
####################################################################
|
####################################################################
|
||||||
# - name: Remove 'Logfile' label again (issues only)
|
# - name: Remove 'Logfile' label again (issues only)
|
||||||
@@ -74,6 +74,7 @@ jobs:
|
|||||||
## Write the response
|
## Write the response
|
||||||
####################################################################
|
####################################################################
|
||||||
- name: Write Response
|
- name: Write Response
|
||||||
uses: peaceiris/actions-label-commenter@c2d00660c86f2b9ed0fb35b372c451558eba85b3
|
uses: peaceiris/actions-label-commenter@v1
|
||||||
with:
|
with:
|
||||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
github_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
config_file: .github/label-commenter-config.yaml
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -23,3 +23,5 @@ CTestTestfile.cmake
|
|||||||
_deps
|
_deps
|
||||||
code/edgeAI.code-workspace
|
code/edgeAI.code-workspace
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
tools/parameter-tooltip-generator/html
|
||||||
|
tools/parameter-tooltip-generator/AI-on-the-edge-device-docs
|
||||||
|
|||||||
9
.gitmodules
vendored
9
.gitmodules
vendored
@@ -4,6 +4,9 @@
|
|||||||
[submodule "code/components/esp-nn"]
|
[submodule "code/components/esp-nn"]
|
||||||
path = code/components/esp-nn
|
path = code/components/esp-nn
|
||||||
url = https://github.com/espressif/esp-nn.git
|
url = https://github.com/espressif/esp-nn.git
|
||||||
[submodule "code/components/tflite-micro-esp-examples"]
|
[submodule "code/components/esp-tflite-micro"]
|
||||||
path = code/components/tflite-micro-esp-examples
|
path = code/components/esp-tflite-micro
|
||||||
url = https://github.com/espressif/tflite-micro-esp-examples.git
|
url = https://github.com/espressif/esp-tflite-micro.git
|
||||||
|
[submodule "code/components/stb"]
|
||||||
|
path = code/components/stb
|
||||||
|
url = https://github.com/nothings/stb.git
|
||||||
|
|||||||
289
Changelog.md
289
Changelog.md
@@ -1,6 +1,279 @@
|
|||||||
## [14.0.2] - 2023-02-05
|
## [update] - 2024-03-30
|
||||||
|
|
||||||
**Stabilization and Improved User Experience**
|
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.6.0...v15.7.0)
|
||||||
|
|
||||||
|
#### Core Changes
|
||||||
|
|
||||||
|
- New tflite-Model for Analog (v13.0.0)
|
||||||
|
- New tflite-Model for Digital Hybrid (v7.0.0)
|
||||||
|
|
||||||
|
#### Bug Fixes
|
||||||
|
|
||||||
|
- tbd
|
||||||
|
|
||||||
|
## [15.7.0] - 2024-02-17
|
||||||
|
|
||||||
|
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.6.0...v15.7.0)
|
||||||
|
|
||||||
|
#### Core Changes
|
||||||
|
- Added new camera settings (See `Settings > Alignment > Reference Image and Camera Settings`). You might need to re-create the reference image and alignment marks. Note worthy:
|
||||||
|
- You can now crop the image
|
||||||
|
- Support to configure sharpness, grayscale, negatoive and exposure
|
||||||
|
- Enhanced various WebUI pages with better explanations and usability
|
||||||
|
- Add Firmware Version to MQTT
|
||||||
|
|
||||||
|
#### Bug Fixes
|
||||||
|
- Reverted "Implemented late analog / digital transition [#2778](https://github.com/jomjol/AI-on-the-edge-device/pull/2778) (introduced in `v15.5`) as is seems to cause issues for many users.
|
||||||
|
|
||||||
|
|
||||||
|
## [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
|
||||||
|
|
||||||
|
- Added config entries for MQTT TLS
|
||||||
|
|
||||||
|
|
||||||
|
#### Fixed
|
||||||
|
|
||||||
|
* InfluxDB: consider DST setting for UTC time conversion
|
||||||
|
|
||||||
|
* Minor html response bugfix
|
||||||
|
|
||||||
|
- Memory leakage (MQTT)
|
||||||
|
|
||||||
|
|
||||||
|
## [15.3.0] - 2023-07-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.2.4)
|
||||||
|
|
||||||
|
#### Changed
|
||||||
|
|
||||||
|
- Updated PlatformIO to `6.3.2`
|
||||||
|
- Updated analog tflite files
|
||||||
|
- ana-cont_1207_s2_q.tflite
|
||||||
|
- dig-cont_0620_s3_q.tflite
|
||||||
|
|
||||||
|
## [15.2.4] - 2023-05-02
|
||||||
|
|
||||||
|
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
|
||||||
|
- Updated PlatformIO to `6.2.0`
|
||||||
|
- [#2376](https://github.com/jomjol/AI-on-the-edge-device/pull/2376) Improve logging if Autostart is not enabled
|
||||||
|
|
||||||
|
#### Fixed
|
||||||
|
- [#2373](https://github.com/jomjol/AI-on-the-edge-device/pull/2373) Allow the Alignment Mark step while status is "Initializing" or "Initialization (delayed)" or while in setup mode
|
||||||
|
- [#2381](https://github.com/jomjol/AI-on-the-edge-device/pull/2381) Fix broken sysinfo REST API
|
||||||
|
|
||||||
|
|
||||||
|
## [15.2.1] - 2023-04-27
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.2.0...v15.2.1)
|
||||||
|
|
||||||
|
#### Fixed
|
||||||
|
- [#2357](https://github.com/jomjol/AI-on-the-edge-device/pull/2357) Fix Alignment Mark issue
|
||||||
|
|
||||||
|
|
||||||
|
## [15.2.0] - 2023-04-23
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
- [#2286](https://github.com/jomjol/AI-on-the-edge-device/pull/2286) Implement a camera livestream handler
|
||||||
|
- [#2252](https://github.com/jomjol/AI-on-the-edge-device/pull/2252) Set prevalue using MQTT + set prevalue to RAW value (REST+MQTT)
|
||||||
|
- [#2319](https://github.com/jomjol/AI-on-the-edge-device/pull/2319) Extend InfluxDBv1 with individual topic names
|
||||||
|
|
||||||
|
#### Changed
|
||||||
|
|
||||||
|
- [#2285](https://github.com/jomjol/AI-on-the-edge-device/pull/2285) Re-implemented PSRAM usage
|
||||||
|
- [#2325](https://github.com/jomjol/AI-on-the-edge-device/pull/2325) Keep MainFlowTask alive to handle reboot
|
||||||
|
- [#2233](https://github.com/jomjol/AI-on-the-edge-device/pull/2233) Remove trailing slash in influxDBv1
|
||||||
|
- [#2305](https://github.com/jomjol/AI-on-the-edge-device/pull/2305) Migration of PlatformIO `5.2.0` to `6.1.0` (resp. ESP IDF from `4.4.2` to `5.0.1`)
|
||||||
|
- Various cleanup and refactoring
|
||||||
|
|
||||||
|
#### Fixed
|
||||||
|
|
||||||
|
- [#2326](https://github.com/jomjol/AI-on-the-edge-device/pull/2326) Activate save button after Analogue ROI creationSet prevalue using MQTT + set prevalue to RAW value (REST+MQTT)
|
||||||
|
- [#2283](https://github.com/jomjol/AI-on-the-edge-device/pull/2283) Fix Timezone issues on InfluxDB
|
||||||
|
- Various minor fixes
|
||||||
|
|
||||||
|
#### Removed
|
||||||
|
|
||||||
|
- n.a.
|
||||||
|
|
||||||
|
|
||||||
|
## [15.1.1] - 2023-03-23
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
- [#2206](https://github.com/jomjol/AI-on-the-edge-device/pull/2206) Log PSRAM usage
|
||||||
|
- [#2216](https://github.com/jomjol/AI-on-the-edge-device/pull/2216) Log MQTT connection refused reasons
|
||||||
|
|
||||||
|
#### Changed
|
||||||
|
|
||||||
|
- n.a.
|
||||||
|
|
||||||
|
#### Fixed
|
||||||
|
|
||||||
|
- [#2224](https://github.com/jomjol/AI-on-the-edge-device/pull/2224), [#2213](https://github.com/jomjol/AI-on-the-edge-device/pull/2213) Reverted some of the PSRAM usage changes due to negative sideffects
|
||||||
|
- [#2203](https://github.com/jomjol/AI-on-the-edge-device/issues/2203) Correct API for pure InfluxDB v1
|
||||||
|
- [#2180](https://github.com/jomjol/AI-on-the-edge-device/pull/2180) Fixed links in Parameter Documentation
|
||||||
|
- Various minor fixes
|
||||||
|
|
||||||
|
#### Removed
|
||||||
|
|
||||||
|
- n.a.
|
||||||
|
|
||||||
|
## [15.1.0] - 2023-03-12
|
||||||
|
|
||||||
|
### Update Procedure
|
||||||
|
|
||||||
|
Update Procedure see [online documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#update-ota-over-the-air)
|
||||||
|
|
||||||
|
:bangbang: Afterwards you should force-reload the Web Interface (usually Ctrl-F5 will do it)!
|
||||||
|
|
||||||
|
:bangbang: Afterwards you should check your configuration for errors!
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.0.3...v15.1.0)
|
||||||
|
|
||||||
|
#### Added
|
||||||
|
- The Configuration page has now tooltips with enhanced documentation
|
||||||
|
- MQTT:
|
||||||
|
- Added `GJ` (`gigajoule`) as an energy meter unit
|
||||||
|
- Removed State Class and unit from `raw` topic
|
||||||
|
- Various Improvements (Only send Homeassistant Discovery the first time we connect, ...) (https://github.com/jomjol/AI-on-the-edge-device/pull/2091
|
||||||
|
- Added Expert Parameter to change CPU Clock from `160` to `240 Mhz`
|
||||||
|
- SD card basic read/write check and a folder/file presence check at boot to indicate SD card issues or missing folders / files ([#2085](https://github.com/jomjol/AI-on-the-edge-device/pull/2085))
|
||||||
|
- Simplified "WIFI roaming" by client triggered channel scan (AP switching at low RSSI) -> using expert parameter "RSSIThreshold" ([#2120](https://github.com/jomjol/AI-on-the-edge-device/pull/2120))
|
||||||
|
- Log WLAN disconnect reason codes (see [WLAN disconnect reasons](https://jomjol.github.io/AI-on-the-edge-device-docs/WLAN-disconnect-reason))
|
||||||
|
- Support of InfluxDB v2 ([#2004](https://github.com/jomjol/AI-on-the-edge-device/pull/2004))
|
||||||
|
|
||||||
|
|
||||||
|
#### Changed
|
||||||
|
- Updated models (tflite files), removed old versions (https://github.com/jomjol/AI-on-the-edge-device/pull/2089, https://github.com/jomjol/AI-on-the-edge-device/pull/2133)
|
||||||
|
:bangbang: **Attention:** Update your configuration!
|
||||||
|
- Hybrid CNN network to `dig-cont_0611_s3`
|
||||||
|
- Analog CNN network to `ana-cont-11.0.5` and `ana-clas100-1.5.7`
|
||||||
|
- Digital CNN network to `dig-class100-1.6.0`
|
||||||
|
- Various Web interface Improvements/Enhancements:
|
||||||
|
- Restructured Menu (Needs cache clearing to be applied)
|
||||||
|
- Enhanced `Previous Value` page
|
||||||
|
- Improved/faster Graph page
|
||||||
|
- Various minor improvements
|
||||||
|
- ROI config pages improvements
|
||||||
|
- Improved Backup Functionality
|
||||||
|
- Added log file logs for Firmware Update
|
||||||
|
- Improved memory management (moved various stuff to external PSRAM, https://github.com/jomjol/AI-on-the-edge-device/pull/2117)
|
||||||
|
- Camera driver update: Support of contrast and saturation ([#2048](https://github.com/jomjol/AI-on-the-edge-device/pull/2048))
|
||||||
|
:bangbang: **Attention**: This could have impact to old configurations. Please check your configuration and potentially adapt parametrization, if detection is negativly affected.
|
||||||
|
- Improved error handling and provide more verbose output in error cases during boot phase ([#2020](https://github.com/jomjol/AI-on-the-edge-device/pull/2020))
|
||||||
|
- Red board LED is indicating more different errors and states (see [Status LED Blink Codes](https://jomjol.github.io/AI-on-the-edge-device-docs/StatusLED-BlinkCodes))
|
||||||
|
- Logfile: Print start indication block after time is synced to indicate start in logfile after a cold boot
|
||||||
|
- `Image Quality Index`: Limit lower input range to 8 to avoid system instabilities
|
||||||
|
|
||||||
|
#### Fixed
|
||||||
|
- Various minor fixes
|
||||||
|
- Added State Class "measurement" to rate_per_time_unit
|
||||||
|
- GPIO: Avoid MQTT publishing to empty topic when "MQTT enable" flag is not set
|
||||||
|
- Fix timezone config parser
|
||||||
|
- Remote Setup truncated long passwords (https://github.com/jomjol/AI-on-the-edge-device/issues/2167)
|
||||||
|
- Problem with timestamp in InfluxDB interface
|
||||||
|
|
||||||
|
#### Removed
|
||||||
|
- n.a.
|
||||||
|
|
||||||
|
|
||||||
|
## [15.0.3] - 2023-02-28
|
||||||
|
|
||||||
|
**Name: Parameter Migration**
|
||||||
|
|
||||||
|
### Update Procedure
|
||||||
|
|
||||||
|
Update Procedure see [online documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#update-ota-over-the-air)
|
||||||
|
|
||||||
|
:bangbang: Afterwards you should force-reload the Web Interface (usually Ctrl-F5 will do it).
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
This release only migrates some parameters, see #2023 for details and a list of all parameter changes.
|
||||||
|
The parameter migration happens automatically on the next startup. No user interaction is required.
|
||||||
|
A backup of the config is stored on the SD-card as `config.bak`.
|
||||||
|
|
||||||
|
Beside of the parameter change and the bugfix listed below, no changes are contained in this release!
|
||||||
|
|
||||||
|
If you want to revert back to `v14` or earlier, you will have to revert the migration changes in `config.ini` manually!
|
||||||
|
|
||||||
|
#### Added
|
||||||
|
|
||||||
|
- n.a.
|
||||||
|
|
||||||
|
#### Changed
|
||||||
|
|
||||||
|
- [#2023](https://github.com/jomjol/AI-on-the-edge-device/pull/2023) Migrated Parameters
|
||||||
|
- Removed old `Topic` parameter, it is not used anymore
|
||||||
|
|
||||||
|
#### Fixed
|
||||||
|
|
||||||
|
- [#2036](https://github.com/jomjol/AI-on-the-edge-device/issues/2036) Fix wrong url-encoding
|
||||||
|
- **NEW v15.0.2:** [#1933](https://github.com/jomjol/AI-on-the-edge-device/issues/1933) Bugfix InfluxDB Timestamp
|
||||||
|
- **NEW v15.0.3:** Re-added lost dropdownbox filling for Postprocessing Individual Parameters
|
||||||
|
|
||||||
|
#### Removed
|
||||||
|
|
||||||
|
- n.a.
|
||||||
|
|
||||||
|
|
||||||
|
## [14.0.3] -2023-02-05
|
||||||
|
|
||||||
|
**Name: Stabilization and Improved User Experience**
|
||||||
|
|
||||||
Thanks to over 80 Pull Requests from 6 contributors, we can anounce another great release with many many improvements and new features:
|
Thanks to over 80 Pull Requests from 6 contributors, we can anounce another great release with many many improvements and new features:
|
||||||
|
|
||||||
@@ -54,7 +327,7 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/
|
|||||||
|
|
||||||
## [13.0.8] - 2022-12-19
|
## [13.0.8] - 2022-12-19
|
||||||
|
|
||||||
**Home Assistant MQTT Discovery Support**
|
**Name: Home Assistant MQTT Discovery Support**
|
||||||
|
|
||||||
### Update Procedure see [online documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#update-ota-over-the-air)
|
### Update Procedure see [online documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#update-ota-over-the-air)
|
||||||
|
|
||||||
@@ -134,7 +407,7 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/
|
|||||||
|
|
||||||
## [12.0.1] 2022-09-29
|
## [12.0.1] 2022-09-29
|
||||||
|
|
||||||
Improve **u**ser e**x**perience
|
Name: Improve **u**ser e**x**perience
|
||||||
|
|
||||||
:bangbang: The release breaks a few things in ota update :bangbang:
|
:bangbang: The release breaks a few things in ota update :bangbang:
|
||||||
|
|
||||||
@@ -802,7 +1075,13 @@ External Illumination
|
|||||||
- Initial Version
|
- Initial Version
|
||||||
|
|
||||||
|
|
||||||
[14.0.0]: https://github.com/jomjol/AI-on-the-edge-device/compare/v13.0.8...v14.0.2
|
[15.2.4]: https://github.com/jomjol/AI-on-the-edge-device/compare/v15.2.1...v15.2.4
|
||||||
|
[15.2.1]: https://github.com/jomjol/AI-on-the-edge-device/compare/v15.2.0...v15.2.1
|
||||||
|
[15.2.0]: https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.1...v15.2.0
|
||||||
|
[15.1.1]: https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.0...v15.1.1
|
||||||
|
[15.1.0]: https://github.com/jomjol/AI-on-the-edge-device/compare/v15.0.3...v15.1.0
|
||||||
|
[15.0.3]: https://github.com/jomjol/AI-on-the-edge-device/compare/v14.0.3...v15.0.3
|
||||||
|
[14.0.3]: https://github.com/jomjol/AI-on-the-edge-device/compare/v13.0.8...v14.0.3
|
||||||
[13.0.8]: https://github.com/jomjol/AI-on-the-edge-device/compare/v12.0.1...v13.0.8
|
[13.0.8]: https://github.com/jomjol/AI-on-the-edge-device/compare/v12.0.1...v13.0.8
|
||||||
[13.0.7]: https://github.com/jomjol/AI-on-the-edge-device/compare/v12.0.1...v13.0.7
|
[13.0.7]: https://github.com/jomjol/AI-on-the-edge-device/compare/v12.0.1...v13.0.7
|
||||||
[13.0.5]: https://github.com/jomjol/AI-on-the-edge-device/compare/v12.0.1...v13.0.5
|
[13.0.5]: https://github.com/jomjol/AI-on-the-edge-device/compare/v12.0.1...v13.0.5
|
||||||
|
|||||||
@@ -2,14 +2,48 @@
|
|||||||
|
|
||||||
**There are a lot of ideas for further improvements, but only limited capacity on side of the developer.** Therefore I have created this page as a collection of ideas.
|
**There are a lot of ideas for further improvements, but only limited capacity on side of the developer.** Therefore I have created this page as a collection of ideas.
|
||||||
|
|
||||||
1. Who ever has a new idea can put it here, so it that it is not forgotten.
|
1. Whoever has a new idea can put it here, so that it is not forgotten.
|
||||||
|
|
||||||
2. Who ever has time, capacity and passion to support, can take any of the ideas and implement them.
|
2. Whoever has the time, capacity and passion to support the project can take any of the ideas and implement them. I will provide support and help wherever I can!
|
||||||
I will support and help where ever I can!
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
____
|
____
|
||||||
|
|
||||||
|
|
||||||
|
#### #40 Trigger with cron like exact time slot
|
||||||
|
|
||||||
|
* https://github.com/jomjol/AI-on-the-edge-device/issues/2470
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### #39 upnp implementation to auto detect the device
|
||||||
|
|
||||||
|
* https://github.com/jomjol/AI-on-the-edge-device/issues/2481
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### #38 Energy Saving
|
||||||
|
|
||||||
|
* Deep sleep between recognition
|
||||||
|
* https://github.com/jomjol/AI-on-the-edge-device/issues/2486
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### #37 Auto init SD card
|
||||||
|
|
||||||
|
* Fully implement the SD card handling (including formatting) into the firmware
|
||||||
|
* https://github.com/jomjol/AI-on-the-edge-device/issues/2488Demo
|
||||||
|
|
||||||
|
#### #36 Run demo without camera
|
||||||
|
|
||||||
|
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
|
||||||
|
as reading the Electricity or Water meter every few minutues only delivers apparent accuracy (DE: "Scheingenauigkeit") you could just as well take a picture with your Smartphone evey so often (e.g. once a week when you are in the Basement anyway), then with some "semi clever" tricks pass this image to the model developed here, and the values then on to who ever needs them e.g. via MQTT.
|
||||||
|
IMO: It is not needed to have that many readings (datapoints) as our behaviour (Use of electricity or water) doesn't vary that much, say, over a weeks time. The interpolation between weekly readings will give sufficient information on the power and/or water usage.
|
||||||
|
|
||||||
|
|
||||||
#### #34 implement state and Roi for water leak detection
|
#### #34 implement state and Roi for water leak detection
|
||||||
for example see Roi on the next picture..
|
for example see Roi on the next picture..
|
||||||

|

|
||||||
@@ -43,7 +77,7 @@ haveing this state in the mqtt broker can trigger functions like closing the ate
|
|||||||
|
|
||||||
#### ~~#29 Add favicon and use the hostname for the website~~- implemented v11.3.1
|
#### ~~#29 Add favicon and use the hostname for the website~~- implemented v11.3.1
|
||||||
|
|
||||||
~~* https://github.com/jomjol/AI-on-the-edge-device/issues/927~~
|
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/927~~
|
||||||
|
|
||||||
#### #28 Improved error handling for ROIs
|
#### #28 Improved error handling for ROIs
|
||||||
|
|
||||||
@@ -81,7 +115,7 @@ haveing this state in the mqtt broker can trigger functions like closing the ate
|
|||||||
|
|
||||||
#### ~~#22 Direct hint to the different neural network files in the other repositories~~- implemented >v11.3.1
|
#### ~~#22 Direct hint to the different neural network files in the other repositories~~- implemented >v11.3.1
|
||||||
|
|
||||||
~~* https://github.com/jomjol/AI-on-the-edge-device/issues/644~~
|
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/644~~
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
98
README.md
98
README.md
@@ -1,31 +1,31 @@
|
|||||||
# Welcome to the AI-on-the-edge-device
|
# Welcome to the AI-on-the-edge-device
|
||||||
<img src="images/icon/watermeter.svg" width="100px">
|
<img src="images/icon/watermeter.svg" width="100px">
|
||||||
|
|
||||||
Artificial intelligence based systems have been established in our every days live. Just think of speech or image recognition. Most of the systems relay on either powerful processors or a direct connection to the cloud for doing the calculations up there. With the increasing power of modern processors the AI systems are coming closer to the end user - which is usually called **edge computing**.
|
Artificial intelligence based systems have become established in our everyday lives. Just think of speech or image recognition. Most of the systems rely on either powerful processors or a direct connection to the cloud for doing the calculations there. With the increasing power of modern processors, the AI systems are coming closer to the end user – which is usually called **edge computing**.
|
||||||
Here this edge computing is brought into a practical oriented example, where a AI network is implemented on a ESP32 device so: **AI on the edge**.
|
Here, this edge computing is put into a practically oriented example, where an AI network is implemented on an ESP32 device so: **AI on the edge**.
|
||||||
|
|
||||||
This projects allows you to digitalize your **analoge** water, gas, power and other meters using cheap and easily available hardware.
|
This project allows you to digitize your **analog** water, gas, power and other meters using cheap and easily available hardware.
|
||||||
|
|
||||||
All you need is an [ESP32 board with a supported camera](https://jomjol.github.io/AI-on-the-edge-device-docs/Hardware-Compatibility/) and a bit of a practical hand.
|
All you need is an [ESP32 board with a supported camera](https://jomjol.github.io/AI-on-the-edge-device-docs/Hardware-Compatibility/) and something of a practical hand.
|
||||||
|
|
||||||
<img src="images/esp32-cam.png" width="200px">
|
<img src="images/esp32-cam.png" width="200px">
|
||||||
|
|
||||||
## Key features
|
## Key features
|
||||||
- Tensorflow Lite (TFlite) integration - including easy to use wrapper
|
- Tensorflow Lite (TFlite) integration – including easy-to-use wrapper
|
||||||
- Inline Image processing (feature detection, alignment, ROI extraction)
|
- Inline image processing (feature detection, alignment, ROI extraction)
|
||||||
- **Small** and **cheap** device (3x4.5x2 cm³, < 10 EUR)
|
- **Small** and **cheap** device (3 x 4.5 x 2 cm³, < 10 EUR)
|
||||||
- camera and illumination integrated
|
- Integrated camera and illumination
|
||||||
- Web surface to administrate and control
|
- Web interface for administration and control
|
||||||
- OTA-Interface to update directly through the web interface
|
- OTA interface for updating directly via the web interface
|
||||||
- Full integration into Homeassistant
|
- Full integration into Homeassistant
|
||||||
- Support for Influx DB 1
|
- Support for Influx DB 1 and 2
|
||||||
- MQTT
|
- MQTT
|
||||||
- REST API
|
- REST API
|
||||||
|
|
||||||
## Workflow
|
## Workflow
|
||||||
The device takes a photo of your meter at a defined interval. It then extracts the Regions of Interest (ROI's) out of it and runs them through an artificial inteligence. As a result, you get the digitalized value of your meter.
|
The device takes a photo of your meter at a defined interval. It then extracts the Regions of Interest (ROIs) from the image and runs them through artificial intelligence. As a result, you get the digitized value of your meter.
|
||||||
|
|
||||||
There are several options what to do with that value. Either send it to a MQTT broker, write it to an InfluxDb or simply provide it throug a REST API.
|
There are several options for what to do with that value. Either send it to an MQTT broker, write it to an InfluxDb or simply provide access to it via a REST API.
|
||||||
|
|
||||||
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/idea.jpg" width="600">
|
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/idea.jpg" width="600">
|
||||||
|
|
||||||
@@ -41,62 +41,68 @@ There are several options what to do with that value. Either send it to a MQTT b
|
|||||||
|
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
There is a growing [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/) which provides you with a lot of information.
|
There is growing [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/) which provides you with a lot of information. Head there to get a start, set it up and configure it.
|
||||||
Head there to get a start, set it up and configure it.
|
|
||||||
|
|
||||||
There are also a articles in the German Heise magazine "make:" about the setup and the technical background (behind a paywall) : [DIY - Setup](https://www.heise.de/select/make/2021/2/2103513300897420296)
|
There are also articles in the German Heise magazine "make:" about the setup and technical background (behind a paywall): [DIY - Setup](https://www.heise.de/select/make/2021/2/2103513300897420296)
|
||||||
|
|
||||||
For further background information, head to [Neural Networks](https://www.heise.de/select/make/2021/6/2126410443385102621), [Training Neural Networks](https://www.heise.de/select/make/2022/1/2134114065999161585) and [Programming on the ESP32](https://www.heise.de/select/make/2022/2/2204010051597422030)
|
A lot of people created useful Youtube videos which might help you getting started.
|
||||||
|
Here a small selection:
|
||||||
|
|
||||||
|
- [youtube.com/watch?v=HKBofb1cnNc](https://www.youtube.com/watch?v=HKBofb1cnNc)
|
||||||
|
- [youtube.com/watch?v=yyf0ORNLCk4](https://www.youtube.com/watch?v=yyf0ORNLCk4)
|
||||||
|
- [youtube.com/watch?v=XxmTubGek6M](https://www.youtube.com/watch?v=XxmTubGek6M)
|
||||||
|
- [youtube.com/watch?v=mDIJEyElkAU](https://www.youtube.com/watch?v=mDIJEyElkAU)
|
||||||
|
- [youtube.com/watch?v=SssiPkyKVVs](https://www.youtube.com/watch?v=SssiPkyKVVs)
|
||||||
|
- [youtube.com/watch?v=MAHE_QyHZFQ](https://www.youtube.com/watch?v=MAHE_QyHZFQ)
|
||||||
|
- [youtube.com/watch?v=Uap_6bwtILQ](https://www.youtube.com/watch?v=Uap_6bwtILQ)
|
||||||
|
|
||||||
|
For further background information, head to [Neural Networks](https://www.heise.de/select/make/2021/6/2126410443385102621), [Training Neural Networks](https://www.heise.de/select/make/2022/1/2134114065999161585) and [Programming on the ESP32](https://www.heise.de/select/make/2022/2/2204010051597422030).
|
||||||
|
|
||||||
### Download
|
### Download
|
||||||
The latest available version is available on the [Releases page](https://github.com/jomjol/AI-on-the-edge-device/releases).
|
The latest available version can be found on the [Releases page](https://github.com/jomjol/AI-on-the-edge-device/releases).
|
||||||
|
|
||||||
### Flashing of the ESP32
|
### Flashing the ESP32
|
||||||
Initially you will have to flash the ESP32 through an USB connection. Later an update is possible directly over the Air (OTA).
|
Initially you will have to flash the ESP32 via a USB connection. Later updates are possible directly over the air (OTA using WIFI).
|
||||||
|
|
||||||
There are different ways to flash your ESP32:
|
There are different ways to flash your ESP32:
|
||||||
- [Web Installer and Console](https://jomjol.github.io/AI-on-the-edge-device/index.html) (Webbrowser based tool to flash the ESP32 and extract the Log over USB)
|
- The prefered way is the [Web Installer and Console](https://jomjol.github.io/AI-on-the-edge-device/index.html) which is a browser-based tool to flash the ESP32 and extract the log over USB:
|
||||||
|

|
||||||
- Flash Tool from Espressif
|
- Flash Tool from Espressif
|
||||||
- ESPtool (Command Line Tool)
|
- ESPtool (command-line tool)
|
||||||
|
|
||||||
See the [Docu](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/) for more information.
|
See the [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/) for more information.
|
||||||
|
|
||||||
### Flashing the SD-Card
|
### Flashing the SD Card
|
||||||
The SD-Card must be flashed separately, see the [Docu](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/) for details.
|
The SD card can be setup automatically after the firmware got installed. See the [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#remote-setup-using-the-built-in-access-point) for details. For this to work, the SD card must be FAT formated (which is the default on a new SD card).
|
||||||
|
Alternatively the SD card still can be setup manually, see the [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#3-sd-card) for details!
|
||||||
|
|
||||||
## Casing
|
## Casing
|
||||||
|
Various 3D-printable housing can be found here:
|
||||||
A 3d-printable housing can be found here:
|
|
||||||
- https://www.thingiverse.com/thing:4573481 (Water Meter)
|
- https://www.thingiverse.com/thing:4573481 (Water Meter)
|
||||||
- https://www.thingiverse.com/thing:5028229 (Power Meter)
|
- https://www.thingiverse.com/thing:5028229 (Power Meter)
|
||||||
- https://www.thingiverse.com/thing:5224101 (Gas Meter)
|
- https://www.thingiverse.com/thing:5224101 (Gas Meter)
|
||||||
- https://www.thingiverse.com/thing:4571627 (ESP32-Cam housing only)
|
- https://www.thingiverse.com/thing:4571627 (ESP32-cam housing only)
|
||||||
|
|
||||||
## Build it yourself
|
|
||||||
See [Build Instructions](code/README.md).
|
|
||||||
|
|
||||||
## Donate
|
## Donate
|
||||||
If you would like to support the developer with a cup of coffee you can do that via [Paypal](https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL).
|
If you would like to support the developer with a cup of coffee, you can do that via [PayPal](https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL).
|
||||||
|
|
||||||
<form action="https://www.paypal.com/donate" method="post" target="_top">
|
<a href="https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL"><img border="0" src="images/paypal.png" width="200px" target="_blank"></a>
|
||||||
<input type="hidden" name="hosted_button_id" value="8TRSVYNYKDSWL" />
|
|
||||||
<input type="image" src="https://www.paypalobjects.com/en_US/DK/i/btn/btn_donateCC_LG.gif" border="0" name="submit" title="PayPal - The safer, easier way to pay online!" alt="Donate with PayPal button" />
|
|
||||||
<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).
|
|
||||||
|
|
||||||
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
|
## Changes and History
|
||||||
See [Changelog](Changelog.md)
|
See [Changelog](Changelog.md).
|
||||||
|
|
||||||
|
## Build It Yourself
|
||||||
|
See [Build Instructions](code/README.md).
|
||||||
|
|
||||||
## Tools
|
## Tools
|
||||||
|
|
||||||
* Logfile downloader and combiner (Thx to [reserve85](https://github.com/reserve85))
|
* Logfile downloader and combiner (Thx to [reserve85](https://github.com/reserve85))
|
||||||
* Files see ['/tools/logfile-tool'](tbd), How-to see [Docu](https://jomjol.github.io/AI-on-the-edge-device-docs/outdated--Gasmeter-Log-Downloader/)
|
* Files see ['/tools/logfile-tool'](tbd), how-to see [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/outdated--Gasmeter-Log-Downloader/)
|
||||||
|
|
||||||
## Additional Ideas
|
## Additional Ideas
|
||||||
There are some ideas and feature requests which are not followed currently - mainly due to capacity reasons on side of the developer. They are collected here: [FeatureRequest.md](FeatureRequest.md)
|
There are some ideas and feature requests which are not currently being pursued – mainly due to capacity reasons on the part of the developers.
|
||||||
|
They features are collected in the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues) and in [FeatureRequest.md](FeatureRequest.md).
|
||||||
------
|
|
||||||
|
|
||||||
|
|||||||
1
code/.gitignore
vendored
1
code/.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
.pio
|
.pio
|
||||||
|
.idea
|
||||||
.vscode/.browse.c_cpp.db*
|
.vscode/.browse.c_cpp.db*
|
||||||
.vscode/c_cpp_properties.json
|
.vscode/c_cpp_properties.json
|
||||||
.vscode/launch.json
|
.vscode/launch.json
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
cmake_minimum_required(VERSION 3.16.0)
|
cmake_minimum_required(VERSION 3.16.0)
|
||||||
|
|
||||||
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common components/tflite-micro-esp-examples/components/tflite-lib)
|
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common components/esp-tflite-micro)
|
||||||
|
|
||||||
ADD_CUSTOM_COMMAND(
|
ADD_CUSTOM_COMMAND(
|
||||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.cpp
|
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.cpp
|
||||||
|
|||||||
@@ -8,6 +8,15 @@ git checkout rolling
|
|||||||
git submodule update --init
|
git submodule update --init
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Update Submodules
|
||||||
|
```
|
||||||
|
cd /components/submodule-name (e.g. tflite-micro-example)
|
||||||
|
git checkout VERSION (e.g. HASH of latest tflite-micro-example build)
|
||||||
|
cd ../../ (auf Ebene von code)
|
||||||
|
git submodule update --init
|
||||||
|
```
|
||||||
|
Evt. muss man vorher noch einige Verzeichnisse in compenents von Hand löschen, da sie beim checkout nicht gelöscht wurden (vor update -- init)
|
||||||
|
|
||||||
## Build and Flash within terminal
|
## Build and Flash within terminal
|
||||||
See further down to build it within an IDE.
|
See further down to build it within an IDE.
|
||||||
### Compile
|
### Compile
|
||||||
@@ -60,3 +69,6 @@ pio device monitor -p /dev/ttyUSB0
|
|||||||
- `pio run --target erase` to erase the flash
|
- `pio run --target erase` to erase the flash
|
||||||
- `pio run --target upload` this will upload the `bootloader.bin, partitions.bin,firmware.bin` from the `code/.pio/build/esp32cam/` folder.
|
- `pio run --target upload` this will upload the `bootloader.bin, partitions.bin,firmware.bin` from the `code/.pio/build/esp32cam/` folder.
|
||||||
- `pio device monitor` to observe the logs via uart
|
- `pio device monitor` to observe the logs via uart
|
||||||
|
|
||||||
|
# Update Parameters
|
||||||
|
If you create or rename a parameter, make sure to update its documentation in `../param-docs/parameter-pages`! Check the `../param-docs/README.md` for more information.
|
||||||
|
|||||||
Submodule code/components/esp-nn updated: 6b3ef8e226...34e97138de
1
code/components/esp-tflite-micro
Submodule
1
code/components/esp-tflite-micro
Submodule
Submodule code/components/esp-tflite-micro added at 13f26b8294
Submodule code/components/esp32-camera updated: 5c8349f4cf...dba8da9898
@@ -1,5 +1,32 @@
|
|||||||
#include "SmartLeds.h"
|
#include "SmartLeds.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* PlatformIO 6 (ESP IDF 5) does no longer allow access to RMTMEM,
|
||||||
|
see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/migration-guides/release-5.x/5.0/peripherals.html?highlight=rmtmem#id5
|
||||||
|
As a dirty workaround, we copy the needed structures from rmt_struct.h
|
||||||
|
In the long run, this should be replaced! */
|
||||||
|
typedef struct rmt_item32_s {
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint32_t duration0 :15;
|
||||||
|
uint32_t level0 :1;
|
||||||
|
uint32_t duration1 :15;
|
||||||
|
uint32_t level1 :1;
|
||||||
|
};
|
||||||
|
uint32_t val;
|
||||||
|
};
|
||||||
|
} rmt_item32_t;
|
||||||
|
|
||||||
|
//Allow access to RMT memory using RMTMEM.chan[0].data32[8]
|
||||||
|
typedef volatile struct rmt_mem_s {
|
||||||
|
struct {
|
||||||
|
rmt_item32_t data32[64];
|
||||||
|
} chan[8];
|
||||||
|
} rmt_mem_t;
|
||||||
|
extern rmt_mem_t RMTMEM;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
IsrCore SmartLed::_interruptCore = CoreCurrent;
|
IsrCore SmartLed::_interruptCore = CoreCurrent;
|
||||||
intr_handle_t SmartLed::_interruptHandle = NULL;
|
intr_handle_t SmartLed::_interruptHandle = NULL;
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,20 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "esp_idf_version.h"
|
||||||
|
#if (ESP_IDF_VERSION_MAJOR >= 5)
|
||||||
|
#include "soc/periph_defs.h"
|
||||||
|
#include "esp_private/periph_ctrl.h"
|
||||||
|
#include "soc/gpio_sig_map.h"
|
||||||
|
#include "soc/gpio_periph.h"
|
||||||
|
#include "soc/io_mux_reg.h"
|
||||||
|
#include "esp_rom_gpio.h"
|
||||||
|
#define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio
|
||||||
|
#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
|
||||||
|
#define gpio_matrix_out(a,b,c,d) esp_rom_gpio_connect_out_signal(a,b,c,d)
|
||||||
|
#define ets_delay_us(a) esp_rom_delay_us(a)
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined ( ARDUINO )
|
#if defined ( ARDUINO )
|
||||||
extern "C" { // ...someone forgot to put in the includes...
|
extern "C" { // ...someone forgot to put in the includes...
|
||||||
#include "esp32-hal.h"
|
#include "esp32-hal.h"
|
||||||
@@ -65,6 +79,27 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if (ESP_IDF_VERSION_MAJOR >= 4) && (ESP_IDF_VERSION_MINOR > 1)
|
||||||
|
#include "hal/gpio_ll.h"
|
||||||
|
#else
|
||||||
|
#include "soc/gpio_periph.h"
|
||||||
|
#define esp_rom_delay_us ets_delay_us
|
||||||
|
static inline int gpio_ll_get_level(gpio_dev_t *hw, int gpio_num)
|
||||||
|
{
|
||||||
|
if (gpio_num < 32) {
|
||||||
|
return (hw->in >> gpio_num) & 0x1;
|
||||||
|
} else {
|
||||||
|
return (hw->in1.data >> (gpio_num - 32)) & 0x1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
|
||||||
|
#if !(configENABLE_BACKWARD_COMPATIBILITY == 1)
|
||||||
|
#define xSemaphoreHandle SemaphoreHandle_t
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "Color.h"
|
#include "Color.h"
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|||||||
@@ -6,9 +6,6 @@
|
|||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "esp_system.h"
|
#include "esp_system.h"
|
||||||
#include "esp_event.h"
|
|
||||||
|
|
||||||
#include "server_tflite.h"
|
|
||||||
|
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
|
||||||
@@ -22,8 +19,10 @@
|
|||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
#include "configFile.h"
|
#include "configFile.h"
|
||||||
#include "Helper.h"
|
#include "Helper.h"
|
||||||
|
|
||||||
#ifdef ENABLE_MQTT
|
#ifdef ENABLE_MQTT
|
||||||
#include "interface_mqtt.h"
|
#include "interface_mqtt.h"
|
||||||
|
#include "server_mqtt.h"
|
||||||
#endif //ENABLE_MQTT
|
#endif //ENABLE_MQTT
|
||||||
|
|
||||||
|
|
||||||
@@ -82,10 +81,10 @@ static void gpioHandlerTask(void *arg) {
|
|||||||
|
|
||||||
void GpioPin::gpioInterrupt(int value) {
|
void GpioPin::gpioInterrupt(int value) {
|
||||||
#ifdef ENABLE_MQTT
|
#ifdef ENABLE_MQTT
|
||||||
if (_mqttTopic != "") {
|
if (_mqttTopic.compare("") != 0) {
|
||||||
ESP_LOGD(TAG, "gpioInterrupt %s %d", _mqttTopic.c_str(), value);
|
ESP_LOGD(TAG, "gpioInterrupt %s %d", _mqttTopic.c_str(), value);
|
||||||
|
|
||||||
MQTTPublish(_mqttTopic, value ? "true" : "false");
|
MQTTPublish(_mqttTopic, value ? "true" : "false", 1);
|
||||||
}
|
}
|
||||||
#endif //ENABLE_MQTT
|
#endif //ENABLE_MQTT
|
||||||
currentState = value;
|
currentState = value;
|
||||||
@@ -115,7 +114,7 @@ void GpioPin::init()
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_MQTT
|
#ifdef ENABLE_MQTT
|
||||||
if ((_mqttTopic != "") && ((_mode == GPIO_PIN_MODE_OUTPUT) || (_mode == GPIO_PIN_MODE_OUTPUT_PWM) || (_mode == GPIO_PIN_MODE_BUILT_IN_FLASH_LED))) {
|
if ((_mqttTopic.compare("") != 0) && ((_mode == GPIO_PIN_MODE_OUTPUT) || (_mode == GPIO_PIN_MODE_OUTPUT_PWM) || (_mode == GPIO_PIN_MODE_BUILT_IN_FLASH_LED))) {
|
||||||
std::function<bool(std::string, char*, int)> f = std::bind(&GpioPin::handleMQTT, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
std::function<bool(std::string, char*, int)> f = std::bind(&GpioPin::handleMQTT, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||||
MQTTregisterSubscribeFunction(_mqttTopic, f);
|
MQTTregisterSubscribeFunction(_mqttTopic, f);
|
||||||
}
|
}
|
||||||
@@ -141,8 +140,8 @@ void GpioPin::setValue(bool value, gpio_set_source setSource, std::string* error
|
|||||||
gpio_set_level(_gpio, value);
|
gpio_set_level(_gpio, value);
|
||||||
|
|
||||||
#ifdef ENABLE_MQTT
|
#ifdef ENABLE_MQTT
|
||||||
if ((_mqttTopic != "") && (setSource != GPIO_SET_SOURCE_MQTT)) {
|
if ((_mqttTopic.compare("") != 0) && (setSource != GPIO_SET_SOURCE_MQTT)) {
|
||||||
MQTTPublish(_mqttTopic, value ? "true" : "false");
|
MQTTPublish(_mqttTopic, value ? "true" : "false", 1);
|
||||||
}
|
}
|
||||||
#endif //ENABLE_MQTT
|
#endif //ENABLE_MQTT
|
||||||
}
|
}
|
||||||
@@ -153,7 +152,8 @@ void GpioPin::publishState() {
|
|||||||
if (newState != currentState) {
|
if (newState != currentState) {
|
||||||
ESP_LOGD(TAG,"publish state of GPIO %d new state %d", _gpio, newState);
|
ESP_LOGD(TAG,"publish state of GPIO %d new state %d", _gpio, newState);
|
||||||
#ifdef ENABLE_MQTT
|
#ifdef ENABLE_MQTT
|
||||||
MQTTPublish(_mqttTopic, newState ? "true" : "false");
|
if (_mqttTopic.compare("") != 0)
|
||||||
|
MQTTPublish(_mqttTopic, newState ? "true" : "false", 1);
|
||||||
#endif //ENABLE_MQTT
|
#endif //ENABLE_MQTT
|
||||||
currentState = newState;
|
currentState = newState;
|
||||||
}
|
}
|
||||||
@@ -330,7 +330,7 @@ bool GpioHandler::readConfig()
|
|||||||
|
|
||||||
#ifdef ENABLE_MQTT
|
#ifdef ENABLE_MQTT
|
||||||
// std::string mainTopicMQTT = "";
|
// std::string mainTopicMQTT = "";
|
||||||
std::string mainTopicMQTT = GetMQTTMainTopic();
|
std::string mainTopicMQTT = mqttServer_getMainTopic();
|
||||||
if (mainTopicMQTT.length() > 0)
|
if (mainTopicMQTT.length() > 0)
|
||||||
{
|
{
|
||||||
mainTopicMQTT = mainTopicMQTT + "/GPIO";
|
mainTopicMQTT = mainTopicMQTT + "/GPIO";
|
||||||
@@ -359,9 +359,9 @@ bool GpioHandler::readConfig()
|
|||||||
gpio_int_type_t intType = resolveIntType(toLower(splitted[2]));
|
gpio_int_type_t intType = resolveIntType(toLower(splitted[2]));
|
||||||
uint16_t dutyResolution = (uint8_t)atoi(splitted[3].c_str());
|
uint16_t dutyResolution = (uint8_t)atoi(splitted[3].c_str());
|
||||||
#ifdef ENABLE_MQTT
|
#ifdef ENABLE_MQTT
|
||||||
bool mqttEnabled = toLower(splitted[4]) == "true";
|
bool mqttEnabled = (toLower(splitted[4]) == "true");
|
||||||
#endif // ENABLE_MQTT
|
#endif // ENABLE_MQTT
|
||||||
bool httpEnabled = toLower(splitted[5]) == "true";
|
bool httpEnabled = (toLower(splitted[5]) == "true");
|
||||||
char gpioName[100];
|
char gpioName[100];
|
||||||
if (splitted.size() >= 7) {
|
if (splitted.size() >= 7) {
|
||||||
strcpy(gpioName, trim(splitted[6]).c_str());
|
strcpy(gpioName, trim(splitted[6]).c_str());
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/proto
|
|||||||
|
|
||||||
idf_component_register(SRCS ${app_sources}
|
idf_component_register(SRCS ${app_sources}
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "."
|
||||||
REQUIRES esp32-camera esp_http_server jomjol_logfile jomjol_image_proc nvs_flash jomjol_fileserver_ota jomjol_controlGPIO)
|
REQUIRES esp_timer esp32-camera esp_http_server jomjol_logfile jomjol_image_proc nvs_flash jomjol_fileserver_ota jomjol_controlGPIO)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -15,48 +15,100 @@
|
|||||||
#include "CImageBasis.h"
|
#include "CImageBasis.h"
|
||||||
#include "../../include/defines.h"
|
#include "../../include/defines.h"
|
||||||
|
|
||||||
class CCamera {
|
typedef enum
|
||||||
protected:
|
{
|
||||||
int ActualQuality;
|
OV2640_MODE_UXGA,
|
||||||
framesize_t ActualResolution;
|
OV2640_MODE_SVGA,
|
||||||
int brightness, contrast, saturation;
|
OV2640_MODE_CIF
|
||||||
bool isFixedExposure;
|
} ov2640_sensor_mode_t;
|
||||||
int waitbeforepicture_org;
|
|
||||||
int led_intensity = 4095;
|
|
||||||
|
|
||||||
void ledc_init(void);
|
typedef struct
|
||||||
bool CameraInitSuccessful = false;
|
{
|
||||||
bool demoMode = false;
|
framesize_t ImageFrameSize = FRAMESIZE_VGA; // 0 - 10
|
||||||
|
gainceiling_t ImageGainceiling; // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128)
|
||||||
|
|
||||||
bool loadNextDemoImage(camera_fb_t *fb);
|
int ImageQuality; // 0 - 63
|
||||||
long GetFileSize(std::string filename);
|
int ImageBrightness; // (-2 to 2) - set brightness
|
||||||
|
int ImageContrast; //-2 - 2
|
||||||
|
int ImageSaturation; //-2 - 2
|
||||||
|
int ImageSharpness; //-2 - 2
|
||||||
|
bool ImageAutoSharpness;
|
||||||
|
int ImageSpecialEffect; // 0 - 6
|
||||||
|
int ImageWbMode; // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
|
||||||
|
int ImageAwb; // white balance enable (0 or 1)
|
||||||
|
int ImageAwbGain; // Auto White Balance enable (0 or 1)
|
||||||
|
int ImageAec; // auto exposure off (1 or 0)
|
||||||
|
int ImageAec2; // automatic exposure sensor (0 or 1)
|
||||||
|
int ImageAeLevel; // auto exposure levels (-2 to 2)
|
||||||
|
int ImageAecValue; // set exposure manually (0-1200)
|
||||||
|
int ImageAgc; // auto gain off (1 or 0)
|
||||||
|
int ImageAgcGain; // set gain manually (0 - 30)
|
||||||
|
int ImageBpc; // black pixel correction
|
||||||
|
int ImageWpc; // white pixel correction
|
||||||
|
int ImageRawGma; // (1 or 0)
|
||||||
|
int ImageLenc; // lens correction (1 or 0)
|
||||||
|
int ImageHmirror; // (0 or 1) flip horizontally
|
||||||
|
int ImageVflip; // Invert image (0 or 1)
|
||||||
|
int ImageDcw; // downsize enable (1 or 0)
|
||||||
|
|
||||||
public:
|
int ImageWidth;
|
||||||
int image_height, image_width;
|
int ImageHeight;
|
||||||
|
|
||||||
CCamera();
|
|
||||||
esp_err_t InitCam();
|
|
||||||
|
|
||||||
void LightOnOff(bool status);
|
int ImageLedIntensity;
|
||||||
void LEDOnOff(bool status);
|
|
||||||
esp_err_t CaptureToHTTP(httpd_req_t *req, int delay = 0);
|
|
||||||
void SetQualitySize(int qual, framesize_t resol);
|
|
||||||
bool SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation);
|
|
||||||
void GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol);
|
|
||||||
void SetLEDIntensity(float _intrel);
|
|
||||||
bool testCamera(void);
|
|
||||||
void EnableAutoExposure(int flash_duration);
|
|
||||||
bool getCameraInitSuccessful();
|
|
||||||
void useDemoMode(void);
|
|
||||||
|
|
||||||
|
|
||||||
framesize_t TextToFramesize(const char * text);
|
bool ImageZoomEnabled;
|
||||||
|
int ImageZoomMode;
|
||||||
|
int ImageZoomOffsetX;
|
||||||
|
int ImageZoomOffsetY;
|
||||||
|
int ImageZoomSize;
|
||||||
|
|
||||||
esp_err_t CaptureToFile(std::string nm, int delay = 0);
|
int WaitBeforePicture;
|
||||||
esp_err_t CaptureToBasisImage(CImageBasis *_Image, int delay = 0);
|
bool isImageSize;
|
||||||
|
|
||||||
|
bool CameraInitSuccessful;
|
||||||
|
bool changedCameraSettings;
|
||||||
|
bool DemoMode;
|
||||||
|
bool SaveAllFiles;
|
||||||
|
} camera_controll_config_temp_t;
|
||||||
|
|
||||||
|
extern camera_controll_config_temp_t CCstatus;
|
||||||
|
|
||||||
|
class CCamera
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
void ledc_init(void);
|
||||||
|
bool loadNextDemoImage(camera_fb_t *fb);
|
||||||
|
long GetFileSize(std::string filename);
|
||||||
|
void SetCamWindow(sensor_t *s, int resolution, int xOffset, int yOffset, int xTotal, int yTotal, int xOutput, int yOutput);
|
||||||
|
void SetImageWidthHeightFromResolution(framesize_t resol);
|
||||||
|
|
||||||
|
public:
|
||||||
|
CCamera(void);
|
||||||
|
esp_err_t InitCam(void);
|
||||||
|
|
||||||
|
void LightOnOff(bool status);
|
||||||
|
void LEDOnOff(bool status);
|
||||||
|
|
||||||
|
esp_err_t setSensorDatenFromCCstatus(void);
|
||||||
|
esp_err_t getSensorDatenToCCstatus(void);
|
||||||
|
|
||||||
|
esp_err_t CaptureToHTTP(httpd_req_t *req, int delay = 0);
|
||||||
|
esp_err_t CaptureToStream(httpd_req_t *req, bool FlashlightOn);
|
||||||
|
|
||||||
|
void SetQualityZoomSize(int qual, framesize_t resol, bool zoomEnabled, int zoomOffsetX, int zoomOffsetY, int imageSize);
|
||||||
|
void SetZoomSize(bool zoomEnabled, int zoomOffsetX, int zoomOffsetY, int imageSize);
|
||||||
|
void SetCamSharpness(bool _autoSharpnessEnabled, int _sharpnessLevel);
|
||||||
|
|
||||||
|
void SetLEDIntensity(float _intrel);
|
||||||
|
bool testCamera(void);
|
||||||
|
bool getCameraInitSuccessful(void);
|
||||||
|
void useDemoMode(void);
|
||||||
|
|
||||||
|
framesize_t TextToFramesize(const char *text);
|
||||||
|
|
||||||
|
esp_err_t CaptureToFile(std::string nm, int delay = 0);
|
||||||
|
esp_err_t CaptureToBasisImage(CImageBasis *_Image, int delay = 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
extern CCamera Camera;
|
extern CCamera Camera;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
152
code/components/jomjol_controlcamera/ov2640_sharpness.cpp
Normal file
152
code/components/jomjol_controlcamera/ov2640_sharpness.cpp
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include "esp_camera.h"
|
||||||
|
#include "ov2640_sharpness.h"
|
||||||
|
|
||||||
|
|
||||||
|
const static uint8_t OV2640_SHARPNESS_AUTO[]=
|
||||||
|
{
|
||||||
|
//reg, val, mask
|
||||||
|
0xFF, 0x00, 0xFF,
|
||||||
|
0x92, 0x01, 0xFF,
|
||||||
|
0x93, 0x20, 0x20,
|
||||||
|
0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
const static uint8_t OV2640_SHARPNESS_MANUAL[]=
|
||||||
|
{
|
||||||
|
//reg, val, mask
|
||||||
|
0xFF, 0x00, 0xFF,
|
||||||
|
0x92, 0x01, 0xFF,
|
||||||
|
0x93, 0x00, 0x20,
|
||||||
|
0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
const static uint8_t OV2640_SHARPNESS_LEVEL0[]=
|
||||||
|
{
|
||||||
|
//reg, val, mask
|
||||||
|
0xFF, 0x00, 0xFF,
|
||||||
|
0x92, 0x01, 0xFF,
|
||||||
|
0x93, 0xC0, 0x1F,
|
||||||
|
0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
const static uint8_t OV2640_SHARPNESS_LEVEL1[]=
|
||||||
|
{
|
||||||
|
//reg, val, mask
|
||||||
|
0xFF, 0x00, 0xFF,
|
||||||
|
0x92, 0x01, 0xFF,
|
||||||
|
0x93, 0xC1, 0x1F,
|
||||||
|
0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
const static uint8_t OV2640_SHARPNESS_LEVEL2[]=
|
||||||
|
{
|
||||||
|
//reg, val, mask
|
||||||
|
0xFF, 0x00, 0xFF,
|
||||||
|
0x92, 0x01, 0xFF,
|
||||||
|
0x93, 0xC2, 0x1F,
|
||||||
|
0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
const static uint8_t OV2640_SHARPNESS_LEVEL3[]=
|
||||||
|
{
|
||||||
|
//reg, val, mask
|
||||||
|
0xFF, 0x00, 0xFF,
|
||||||
|
0x92, 0x01, 0xFF,
|
||||||
|
0x93, 0xC4, 0x1F,
|
||||||
|
0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
const static uint8_t OV2640_SHARPNESS_LEVEL4[]=
|
||||||
|
{
|
||||||
|
//reg, val, mask
|
||||||
|
0xFF, 0x00, 0xFF,
|
||||||
|
0x92, 0x01, 0xFF,
|
||||||
|
0x93, 0xC8, 0x1F,
|
||||||
|
0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
const static uint8_t OV2640_SHARPNESS_LEVEL5[]=
|
||||||
|
{
|
||||||
|
//reg, val, mask
|
||||||
|
0xFF, 0x00, 0xFF,
|
||||||
|
0x92, 0x01, 0xFF,
|
||||||
|
0x93, 0xD0, 0x1F,
|
||||||
|
0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
const static uint8_t OV2640_SHARPNESS_LEVEL6[]=
|
||||||
|
{
|
||||||
|
//reg, val, mask
|
||||||
|
0xFF, 0x00, 0xFF,
|
||||||
|
0x92, 0x01, 0xFF,
|
||||||
|
0x93, 0xDF, 0x1F,
|
||||||
|
0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
const static uint8_t *OV2640_SETTING_SHARPNESS[]=
|
||||||
|
{
|
||||||
|
OV2640_SHARPNESS_LEVEL0, // -3 sharpness
|
||||||
|
OV2640_SHARPNESS_LEVEL1,
|
||||||
|
OV2640_SHARPNESS_LEVEL2,
|
||||||
|
OV2640_SHARPNESS_LEVEL3,
|
||||||
|
OV2640_SHARPNESS_LEVEL4,
|
||||||
|
OV2640_SHARPNESS_LEVEL5,
|
||||||
|
OV2640_SHARPNESS_LEVEL6 // +3 sharpness
|
||||||
|
};
|
||||||
|
|
||||||
|
#define OV2640_MAXLEVEL_SHARPNESS 6
|
||||||
|
|
||||||
|
|
||||||
|
static int table_mask_write(sensor_t *sensor, const uint8_t* ptab)
|
||||||
|
{
|
||||||
|
uint8_t address;
|
||||||
|
uint8_t value;
|
||||||
|
uint8_t orgval;
|
||||||
|
uint8_t mask;
|
||||||
|
const uint8_t *pdata = ptab;
|
||||||
|
|
||||||
|
if (pdata == NULL)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
address = *pdata++;
|
||||||
|
value = *pdata++;
|
||||||
|
mask = *pdata++;
|
||||||
|
|
||||||
|
if ((address == 0) && (value == 0) && (mask == 0))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sensor->set_reg(sensor, address, mask, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int ov2640_enable_auto_sharpness(sensor_t *sensor)
|
||||||
|
{
|
||||||
|
table_mask_write(sensor, OV2640_SHARPNESS_AUTO);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int ov2640_set_sharpness(sensor_t *sensor, int sharpness)
|
||||||
|
{
|
||||||
|
int sharpness_temp = 0;
|
||||||
|
|
||||||
|
if (sharpness < -3)
|
||||||
|
{
|
||||||
|
sharpness_temp = -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sharpness > OV2640_MAXLEVEL_SHARPNESS - 3)
|
||||||
|
{
|
||||||
|
sharpness_temp = OV2640_MAXLEVEL_SHARPNESS - 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
table_mask_write(sensor, OV2640_SHARPNESS_MANUAL);
|
||||||
|
table_mask_write(sensor, OV2640_SETTING_SHARPNESS[sharpness_temp + 3]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
11
code/components/jomjol_controlcamera/ov2640_sharpness.h
Normal file
11
code/components/jomjol_controlcamera/ov2640_sharpness.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef OV2640_SHARPNESS_H
|
||||||
|
#define OV2640_SHARPNESS_H
|
||||||
|
|
||||||
|
#include "esp_camera.h"
|
||||||
|
|
||||||
|
int ov2640_enable_auto_sharpness(sensor_t *sensor);
|
||||||
|
int ov2640_set_sharpness(sensor_t *sensor, int sharpness); // -3 to +3, -4 for auto-sharpness
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "esp_camera.h"
|
#include "esp_camera.h"
|
||||||
#include "ClassControllCamera.h"
|
#include "ClassControllCamera.h"
|
||||||
|
#include "MainFlowControl.h"
|
||||||
|
|
||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
@@ -13,183 +14,183 @@
|
|||||||
|
|
||||||
static const char *TAG = "server_cam";
|
static const char *TAG = "server_cam";
|
||||||
|
|
||||||
|
void PowerResetCamera()
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "Resetting camera by power down line");
|
||||||
|
gpio_config_t conf;
|
||||||
|
conf.intr_type = GPIO_INTR_DISABLE;
|
||||||
|
conf.pin_bit_mask = 1LL << GPIO_NUM_32;
|
||||||
|
conf.mode = GPIO_MODE_OUTPUT;
|
||||||
|
conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||||
|
conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||||
|
gpio_config(&conf);
|
||||||
|
|
||||||
void PowerResetCamera(){
|
// carefull, logic is inverted compared to reset pin
|
||||||
|
gpio_set_level(GPIO_NUM_32, 1);
|
||||||
ESP_LOGD(TAG, "Resetting camera by power down line");
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
gpio_config_t conf;
|
gpio_set_level(GPIO_NUM_32, 0);
|
||||||
conf.intr_type = GPIO_INTR_DISABLE;
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
conf.pin_bit_mask = 1LL << GPIO_NUM_32;
|
|
||||||
conf.mode = GPIO_MODE_OUTPUT;
|
|
||||||
conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
|
||||||
conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
|
||||||
gpio_config(&conf);
|
|
||||||
|
|
||||||
// carefull, logic is inverted compared to reset pin
|
|
||||||
gpio_set_level(GPIO_NUM_32, 1);
|
|
||||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
|
||||||
gpio_set_level(GPIO_NUM_32, 0);
|
|
||||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
esp_err_t handler_lightOn(httpd_req_t *req)
|
esp_err_t handler_lightOn(httpd_req_t *req)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
LogFile.WriteHeapInfo("handler_lightOn - Start");
|
LogFile.WriteHeapInfo("handler_lightOn - Start");
|
||||||
ESP_LOGD(TAG, "handler_lightOn uri: %s", req->uri);
|
ESP_LOGD(TAG, "handler_lightOn uri: %s", req->uri);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (Camera.getCameraInitSuccessful())
|
if (Camera.getCameraInitSuccessful())
|
||||||
{
|
{
|
||||||
Camera.LightOnOff(true);
|
Camera.LightOnOff(true);
|
||||||
const char* resp_str = (const char*) req->user_ctx;
|
const char *resp_str = (const char *)req->user_ctx;
|
||||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /lighton not available!");
|
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /lighton not available!");
|
||||||
return ESP_ERR_NOT_FOUND;
|
return ESP_ERR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
LogFile.WriteHeapInfo("handler_lightOn - Done");
|
LogFile.WriteHeapInfo("handler_lightOn - Done");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
esp_err_t handler_lightOff(httpd_req_t *req)
|
esp_err_t handler_lightOff(httpd_req_t *req)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
LogFile.WriteHeapInfo("handler_lightOff - Start");
|
LogFile.WriteHeapInfo("handler_lightOff - Start");
|
||||||
ESP_LOGD(TAG, "handler_lightOff uri: %s", req->uri);
|
ESP_LOGD(TAG, "handler_lightOff uri: %s", req->uri);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (Camera.getCameraInitSuccessful())
|
if (Camera.getCameraInitSuccessful())
|
||||||
{
|
{
|
||||||
Camera.LightOnOff(false);
|
Camera.LightOnOff(false);
|
||||||
const char* resp_str = (const char*) req->user_ctx;
|
const char *resp_str = (const char *)req->user_ctx;
|
||||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /lightoff not available!");
|
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /lightoff not available!");
|
||||||
return ESP_ERR_NOT_FOUND;
|
return ESP_ERR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
LogFile.WriteHeapInfo("handler_lightOff - Done");
|
LogFile.WriteHeapInfo("handler_lightOff - Done");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
esp_err_t handler_capture(httpd_req_t *req)
|
esp_err_t handler_capture(httpd_req_t *req)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
LogFile.WriteHeapInfo("handler_capture - Start");
|
LogFile.WriteHeapInfo("handler_capture - Start");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (Camera.getCameraInitSuccessful())
|
if (Camera.getCameraInitSuccessful())
|
||||||
{
|
{
|
||||||
int quality;
|
#ifdef DEBUG_DETAIL_ON
|
||||||
framesize_t res;
|
ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality);
|
||||||
|
#endif
|
||||||
|
|
||||||
Camera.GetCameraParameter(req, quality, res);
|
// wenn die Kameraeinstellungen durch Erstellen eines neuen Referenzbildes verändert wurden, müssen sie neu gesetzt werden
|
||||||
|
if (CFstatus.changedCameraSettings)
|
||||||
#ifdef DEBUG_DETAIL_ON
|
{
|
||||||
ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality);
|
Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera
|
||||||
#endif
|
Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize);
|
||||||
|
CFstatus.changedCameraSettings = false;
|
||||||
Camera.SetQualitySize(quality, res);
|
}
|
||||||
|
|
||||||
esp_err_t result;
|
esp_err_t result;
|
||||||
result = Camera.CaptureToHTTP(req);
|
result = Camera.CaptureToHTTP(req);
|
||||||
|
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
LogFile.WriteHeapInfo("handler_capture - Done");
|
LogFile.WriteHeapInfo("handler_capture - Done");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /capture not available!");
|
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /capture not available!");
|
||||||
return ESP_ERR_NOT_FOUND;
|
return ESP_ERR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
esp_err_t handler_capture_with_light(httpd_req_t *req)
|
esp_err_t handler_capture_with_light(httpd_req_t *req)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
LogFile.WriteHeapInfo("handler_capture_with_light - Start");
|
LogFile.WriteHeapInfo("handler_capture_with_light - Start");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (Camera.getCameraInitSuccessful())
|
if (Camera.getCameraInitSuccessful())
|
||||||
{
|
{
|
||||||
char _query[100];
|
char _query[100];
|
||||||
char _delay[10];
|
char _delay[10];
|
||||||
|
|
||||||
int quality;
|
|
||||||
framesize_t res;
|
|
||||||
int delay = 2500;
|
int delay = 2500;
|
||||||
|
|
||||||
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
|
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
|
||||||
{
|
{
|
||||||
ESP_LOGD(TAG, "Query: %s", _query);
|
ESP_LOGD(TAG, "Query: %s", _query);
|
||||||
|
|
||||||
if (httpd_query_key_value(_query, "delay", _delay, 10) == ESP_OK)
|
if (httpd_query_key_value(_query, "delay", _delay, 10) == ESP_OK)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
ESP_LOGD(TAG, "Delay: %s", _delay);
|
ESP_LOGD(TAG, "Delay: %s", _delay);
|
||||||
#endif
|
#endif
|
||||||
delay = atoi(_delay);
|
delay = atoi(_delay);
|
||||||
|
|
||||||
if (delay < 0)
|
if (delay < 0)
|
||||||
|
{
|
||||||
delay = 0;
|
delay = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Camera.GetCameraParameter(req, quality, res);
|
#ifdef DEBUG_DETAIL_ON
|
||||||
|
ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef DEBUG_DETAIL_ON
|
// wenn die Kameraeinstellungen durch Erstellen eines neuen Referenzbildes verändert wurden, müssen sie neu gesetzt werden
|
||||||
ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality);
|
if (CFstatus.changedCameraSettings)
|
||||||
#endif
|
{
|
||||||
|
Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera
|
||||||
|
Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize);
|
||||||
|
CFstatus.changedCameraSettings = false;
|
||||||
|
}
|
||||||
|
|
||||||
Camera.SetQualitySize(quality, res);
|
|
||||||
Camera.LightOnOff(true);
|
Camera.LightOnOff(true);
|
||||||
const TickType_t xDelay = delay / portTICK_PERIOD_MS;
|
const TickType_t xDelay = delay / portTICK_PERIOD_MS;
|
||||||
vTaskDelay( xDelay );
|
vTaskDelay(xDelay);
|
||||||
|
|
||||||
esp_err_t result;
|
esp_err_t result;
|
||||||
result = Camera.CaptureToHTTP(req);
|
result = Camera.CaptureToHTTP(req);
|
||||||
|
|
||||||
Camera.LightOnOff(false);
|
Camera.LightOnOff(false);
|
||||||
|
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
LogFile.WriteHeapInfo("handler_capture_with_light - Done");
|
LogFile.WriteHeapInfo("handler_capture_with_light - Done");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /capture_with_flashlight not available!");
|
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /capture_with_flashlight not available!");
|
||||||
return ESP_ERR_NOT_FOUND;
|
return ESP_ERR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
esp_err_t handler_capture_save_to_file(httpd_req_t *req)
|
esp_err_t handler_capture_save_to_file(httpd_req_t *req)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
LogFile.WriteHeapInfo("handler_capture_save_to_file - Start");
|
LogFile.WriteHeapInfo("handler_capture_save_to_file - Start");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (Camera.getCameraInitSuccessful())
|
if (Camera.getCameraInitSuccessful())
|
||||||
{
|
{
|
||||||
char _query[100];
|
char _query[100];
|
||||||
char _delay[10];
|
char _delay[10];
|
||||||
@@ -197,94 +198,102 @@ esp_err_t handler_capture_save_to_file(httpd_req_t *req)
|
|||||||
char filename[100];
|
char filename[100];
|
||||||
std::string fn = "/sdcard/";
|
std::string fn = "/sdcard/";
|
||||||
|
|
||||||
|
|
||||||
int quality;
|
|
||||||
framesize_t res;
|
|
||||||
|
|
||||||
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
|
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
|
||||||
{
|
{
|
||||||
ESP_LOGD(TAG, "Query: %s", _query);
|
ESP_LOGD(TAG, "Query: %s", _query);
|
||||||
|
|
||||||
if (httpd_query_key_value(_query, "filename", filename, 100) == ESP_OK)
|
if (httpd_query_key_value(_query, "filename", filename, 100) == ESP_OK)
|
||||||
{
|
{
|
||||||
fn.append(filename);
|
fn.append(filename);
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
ESP_LOGD(TAG, "Filename: %s", fn.c_str());
|
ESP_LOGD(TAG, "Filename: %s", fn.c_str());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
fn.append("noname.jpg");
|
fn.append("noname.jpg");
|
||||||
|
}
|
||||||
|
|
||||||
if (httpd_query_key_value(_query, "delay", _delay, 10) == ESP_OK)
|
if (httpd_query_key_value(_query, "delay", _delay, 10) == ESP_OK)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
ESP_LOGD(TAG, "Delay: %s", _delay);
|
ESP_LOGD(TAG, "Delay: %s", _delay);
|
||||||
#endif
|
#endif
|
||||||
delay = atoi(_delay);
|
delay = atoi(_delay);
|
||||||
|
|
||||||
if (delay < 0)
|
if (delay < 0)
|
||||||
|
{
|
||||||
delay = 0;
|
delay = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
fn.append("noname.jpg");
|
fn.append("noname.jpg");
|
||||||
|
}
|
||||||
|
|
||||||
Camera.GetCameraParameter(req, quality, res);
|
#ifdef DEBUG_DETAIL_ON
|
||||||
#ifdef DEBUG_DETAIL_ON
|
ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality);
|
||||||
ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality);
|
#endif
|
||||||
#endif
|
|
||||||
Camera.SetQualitySize(quality, res);
|
// wenn die Kameraeinstellungen durch Erstellen eines neuen Referenzbildes verändert wurden, müssen sie neu gesetzt werden
|
||||||
|
if (CFstatus.changedCameraSettings)
|
||||||
|
{
|
||||||
|
Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera
|
||||||
|
Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize);
|
||||||
|
CFstatus.changedCameraSettings = false;
|
||||||
|
}
|
||||||
|
|
||||||
esp_err_t result;
|
esp_err_t result;
|
||||||
result = Camera.CaptureToFile(fn, delay);
|
result = Camera.CaptureToFile(fn, delay);
|
||||||
|
|
||||||
const char* resp_str = (const char*) fn.c_str();
|
const char *resp_str = (const char *)fn.c_str();
|
||||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||||
|
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
LogFile.WriteHeapInfo("handler_capture_save_to_file - Done");
|
LogFile.WriteHeapInfo("handler_capture_save_to_file - Done");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /save not available!");
|
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /save not available!");
|
||||||
return ESP_ERR_NOT_FOUND;
|
return ESP_ERR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void register_server_camera_uri(httpd_handle_t server)
|
void register_server_camera_uri(httpd_handle_t server)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
ESP_LOGI(TAG, "server_part_camera - Registering URI handlers");
|
ESP_LOGI(TAG, "server_part_camera - Registering URI handlers");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
httpd_uri_t camuri = { };
|
httpd_uri_t camuri = {};
|
||||||
camuri.method = HTTP_GET;
|
camuri.method = HTTP_GET;
|
||||||
|
|
||||||
camuri.uri = "/lighton";
|
camuri.uri = "/lighton";
|
||||||
camuri.handler = handler_lightOn;
|
camuri.handler = handler_lightOn;
|
||||||
camuri.user_ctx = (void*) "Light On";
|
camuri.user_ctx = (void *)"Light On";
|
||||||
httpd_register_uri_handler(server, &camuri);
|
httpd_register_uri_handler(server, &camuri);
|
||||||
|
|
||||||
camuri.uri = "/lightoff";
|
camuri.uri = "/lightoff";
|
||||||
camuri.handler = handler_lightOff;
|
camuri.handler = handler_lightOff;
|
||||||
camuri.user_ctx = (void*) "Light Off";
|
camuri.user_ctx = (void *)"Light Off";
|
||||||
httpd_register_uri_handler(server, &camuri);
|
httpd_register_uri_handler(server, &camuri);
|
||||||
|
|
||||||
camuri.uri = "/capture";
|
camuri.uri = "/capture";
|
||||||
camuri.handler = handler_capture;
|
camuri.handler = handler_capture;
|
||||||
camuri.user_ctx = NULL;
|
camuri.user_ctx = NULL;
|
||||||
httpd_register_uri_handler(server, &camuri);
|
httpd_register_uri_handler(server, &camuri);
|
||||||
|
|
||||||
camuri.uri = "/capture_with_flashlight";
|
camuri.uri = "/capture_with_flashlight";
|
||||||
camuri.handler = handler_capture_with_light;
|
camuri.handler = handler_capture_with_light;
|
||||||
camuri.user_ctx = NULL;
|
camuri.user_ctx = NULL;
|
||||||
httpd_register_uri_handler(server, &camuri);
|
httpd_register_uri_handler(server, &camuri);
|
||||||
|
|
||||||
camuri.uri = "/save";
|
camuri.uri = "/save";
|
||||||
camuri.handler = handler_capture_save_to_file;
|
camuri.handler = handler_capture_save_to_file;
|
||||||
camuri.user_ctx = NULL;
|
camuri.user_ctx = NULL;
|
||||||
httpd_register_uri_handler(server, &camuri);
|
httpd_register_uri_handler(server, &camuri);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
//#include "ClassControllCamera.h"
|
//#include "ClassControllCamera.h"
|
||||||
|
|
||||||
void register_server_camera_uri(httpd_handle_t server);
|
void register_server_camera_uri(httpd_handle_t server);
|
||||||
|
|
||||||
void PowerResetCamera();
|
void PowerResetCamera();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||||
|
|
||||||
idf_component_register(SRCS ${app_sources}
|
idf_component_register(SRCS ${app_sources}
|
||||||
INCLUDE_DIRS "." "../../include"
|
INCLUDE_DIRS "." "../../include" "miniz"
|
||||||
REQUIRES tflite-lib esp_http_server app_update esp_http_client nvs_flash jomjol_tfliteclass jomjol_flowcontroll spiffs jomjol_helper jomjol_controlGPIO miniz)
|
REQUIRES vfs esp_http_server app_update esp_http_client nvs_flash jomjol_tfliteclass jomjol_flowcontroll spiffs jomjol_helper jomjol_controlGPIO)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -153,7 +153,7 @@
|
|||||||
/*#define MINIZ_NO_MALLOC */
|
/*#define MINIZ_NO_MALLOC */
|
||||||
|
|
||||||
#ifdef MINIZ_NO_INFLATE_APIS
|
#ifdef MINIZ_NO_INFLATE_APIS
|
||||||
#define MINIZ_NO_ARCHIVE_APIS
|
//#define MINIZ_NO_ARCHIVE_APIS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef MINIZ_NO_DEFLATE_APIS
|
#ifdef MINIZ_NO_DEFLATE_APIS
|
||||||
@@ -36,7 +36,7 @@ extern "C" {
|
|||||||
#include "../../include/defines.h"
|
#include "../../include/defines.h"
|
||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
|
|
||||||
#include "server_tflite.h"
|
#include "MainFlowControl.h"
|
||||||
|
|
||||||
#include "server_help.h"
|
#include "server_help.h"
|
||||||
#ifdef ENABLE_MQTT
|
#ifdef ENABLE_MQTT
|
||||||
@@ -47,7 +47,6 @@ extern "C" {
|
|||||||
#include "Helper.h"
|
#include "Helper.h"
|
||||||
#include "miniz.h"
|
#include "miniz.h"
|
||||||
|
|
||||||
|
|
||||||
static const char *TAG = "OTA FILE";
|
static const char *TAG = "OTA FILE";
|
||||||
|
|
||||||
struct file_server_data {
|
struct file_server_data {
|
||||||
@@ -74,7 +73,7 @@ static esp_err_t send_datafile(httpd_req_t *req, bool send_full_file);
|
|||||||
|
|
||||||
esp_err_t get_numbers_file_handler(httpd_req_t *req)
|
esp_err_t get_numbers_file_handler(httpd_req_t *req)
|
||||||
{
|
{
|
||||||
std::string ret = tfliteflow.getNumbersName();
|
std::string ret = flowctrl.getNumbersName();
|
||||||
|
|
||||||
// ESP_LOGI(TAG, "Result get_numbers_file_handler: %s", ret.c_str());
|
// ESP_LOGI(TAG, "Result get_numbers_file_handler: %s", ret.c_str());
|
||||||
|
|
||||||
@@ -228,7 +227,7 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath, const
|
|||||||
if (chunksize > 0){
|
if (chunksize > 0){
|
||||||
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
||||||
fclose(fd);
|
fclose(fd);
|
||||||
ESP_LOGE(TAG, "File sending failed!");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -267,7 +266,7 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath, const
|
|||||||
strlcpy(entrypath + dirpath_len, entry->d_name, sizeof(entrypath) - dirpath_len);
|
strlcpy(entrypath + dirpath_len, entry->d_name, sizeof(entrypath) - dirpath_len);
|
||||||
ESP_LOGD(TAG, "Entrypath: %s", entrypath);
|
ESP_LOGD(TAG, "Entrypath: %s", entrypath);
|
||||||
if (stat(entrypath, &entry_stat) == -1) {
|
if (stat(entrypath, &entry_stat) == -1) {
|
||||||
ESP_LOGE(TAG, "Failed to stat %s: %s", entrytype, entry->d_name);
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to stat " + string(entrytype) + ": " + string(entry->d_name));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,7 +356,7 @@ static esp_err_t send_datafile(httpd_req_t *req, bool send_full_file)
|
|||||||
|
|
||||||
fd = fopen(currentfilename.c_str(), "r");
|
fd = fopen(currentfilename.c_str(), "r");
|
||||||
if (!fd) {
|
if (!fd) {
|
||||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + std::string(currentfilename) +"!");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + currentfilename + "!");
|
||||||
/* Respond with 404 Error */
|
/* Respond with 404 Error */
|
||||||
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404());
|
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404());
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
@@ -373,7 +372,7 @@ static esp_err_t send_datafile(httpd_req_t *req, bool send_full_file)
|
|||||||
|
|
||||||
/* Adapted from https://www.geeksforgeeks.org/implement-your-own-tail-read-last-n-lines-of-a-huge-file/ */
|
/* Adapted from https://www.geeksforgeeks.org/implement-your-own-tail-read-last-n-lines-of-a-huge-file/ */
|
||||||
if (fseek(fd, 0, SEEK_END)) {
|
if (fseek(fd, 0, SEEK_END)) {
|
||||||
ESP_LOGE(TAG, "Failed to get to end of file!");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to get to end of file!");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -381,7 +380,7 @@ static esp_err_t send_datafile(httpd_req_t *req, bool send_full_file)
|
|||||||
ESP_LOGI(TAG, "File contains %ld bytes", pos);
|
ESP_LOGI(TAG, "File contains %ld bytes", pos);
|
||||||
|
|
||||||
if (fseek(fd, pos - std::min((long)LOGFILE_LAST_PART_BYTES, pos), SEEK_SET)) { // Go LOGFILE_LAST_PART_BYTES bytes back from EOF
|
if (fseek(fd, pos - std::min((long)LOGFILE_LAST_PART_BYTES, pos), SEEK_SET)) { // Go LOGFILE_LAST_PART_BYTES bytes back from EOF
|
||||||
ESP_LOGE(TAG, "Failed to go back %ld bytes within the file!", std::min((long)LOGFILE_LAST_PART_BYTES, pos));
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to go back " + to_string(std::min((long)LOGFILE_LAST_PART_BYTES, pos)) + " bytes within the file!");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -404,7 +403,7 @@ static esp_err_t send_datafile(httpd_req_t *req, bool send_full_file)
|
|||||||
/* Send the buffer contents as HTTP response chunk */
|
/* Send the buffer contents as HTTP response chunk */
|
||||||
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
||||||
fclose(fd);
|
fclose(fd);
|
||||||
ESP_LOGE(TAG, "File sending failed!");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!");
|
||||||
/* Abort sending file */
|
/* Abort sending file */
|
||||||
httpd_resp_sendstr_chunk(req, NULL);
|
httpd_resp_sendstr_chunk(req, NULL);
|
||||||
/* Respond with 500 Internal Server Error */
|
/* Respond with 500 Internal Server Error */
|
||||||
@@ -443,7 +442,7 @@ static esp_err_t send_logfile(httpd_req_t *req, bool send_full_file)
|
|||||||
|
|
||||||
fd = fopen(currentfilename.c_str(), "r");
|
fd = fopen(currentfilename.c_str(), "r");
|
||||||
if (!fd) {
|
if (!fd) {
|
||||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + std::string(currentfilename.c_str()) +"!");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + currentfilename + "!");
|
||||||
/* Respond with 404 Error */
|
/* Respond with 404 Error */
|
||||||
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404());
|
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404());
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
@@ -459,7 +458,7 @@ static esp_err_t send_logfile(httpd_req_t *req, bool send_full_file)
|
|||||||
|
|
||||||
/* Adapted from https://www.geeksforgeeks.org/implement-your-own-tail-read-last-n-lines-of-a-huge-file/ */
|
/* Adapted from https://www.geeksforgeeks.org/implement-your-own-tail-read-last-n-lines-of-a-huge-file/ */
|
||||||
if (fseek(fd, 0, SEEK_END)) {
|
if (fseek(fd, 0, SEEK_END)) {
|
||||||
ESP_LOGE(TAG, "Failed to get to end of file!");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to get to end of file!");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -467,7 +466,7 @@ static esp_err_t send_logfile(httpd_req_t *req, bool send_full_file)
|
|||||||
ESP_LOGI(TAG, "File contains %ld bytes", pos);
|
ESP_LOGI(TAG, "File contains %ld bytes", pos);
|
||||||
|
|
||||||
if (fseek(fd, pos - std::min((long)LOGFILE_LAST_PART_BYTES, pos), SEEK_SET)) { // Go LOGFILE_LAST_PART_BYTES bytes back from EOF
|
if (fseek(fd, pos - std::min((long)LOGFILE_LAST_PART_BYTES, pos), SEEK_SET)) { // Go LOGFILE_LAST_PART_BYTES bytes back from EOF
|
||||||
ESP_LOGE(TAG, "Failed to go back %ld bytes within the file!", std::min((long)LOGFILE_LAST_PART_BYTES, pos));
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to go back " + to_string(std::min((long)LOGFILE_LAST_PART_BYTES, pos)) + " bytes within the file!");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -490,7 +489,7 @@ static esp_err_t send_logfile(httpd_req_t *req, bool send_full_file)
|
|||||||
/* Send the buffer contents as HTTP response chunk */
|
/* Send the buffer contents as HTTP response chunk */
|
||||||
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
||||||
fclose(fd);
|
fclose(fd);
|
||||||
ESP_LOGE(TAG, "File sending failed!");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!");
|
||||||
/* Abort sending file */
|
/* Abort sending file */
|
||||||
httpd_resp_sendstr_chunk(req, NULL);
|
httpd_resp_sendstr_chunk(req, NULL);
|
||||||
/* Respond with 500 Internal Server Error */
|
/* Respond with 500 Internal Server Error */
|
||||||
@@ -530,7 +529,7 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
|
|
||||||
if (!filename) {
|
if (!filename) {
|
||||||
ESP_LOGE(TAG, "Filename is too long");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Filename is too long");
|
||||||
/* Respond with 414 Error */
|
/* Respond with 414 Error */
|
||||||
httpd_resp_send_err(req, HTTPD_414_URI_TOO_LONG, "Filename too long");
|
httpd_resp_send_err(req, HTTPD_414_URI_TOO_LONG, "Filename too long");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
@@ -548,7 +547,7 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
|||||||
/* Get value of expected key from query string */
|
/* Get value of expected key from query string */
|
||||||
if (httpd_query_key_value(buf, "readonly", param, sizeof(param)) == ESP_OK) {
|
if (httpd_query_key_value(buf, "readonly", param, sizeof(param)) == ESP_OK) {
|
||||||
ESP_LOGI(TAG, "Found URL query parameter => readonly=%s", param);
|
ESP_LOGI(TAG, "Found URL query parameter => readonly=%s", param);
|
||||||
readonly = param && strcmp(param,"true")==0;
|
readonly = (strcmp(param,"true") == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -571,7 +570,7 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
fd = fopen(filepath, "r");
|
fd = fopen(filepath, "r");
|
||||||
if (!fd) {
|
if (!fd) {
|
||||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + std::string(filepath) +"!");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + std::string(filepath) + "!");
|
||||||
/* Respond with 404 Error */
|
/* Respond with 404 Error */
|
||||||
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404());
|
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404());
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
@@ -589,10 +588,12 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
|||||||
/* Read file in chunks into the scratch buffer */
|
/* Read file in chunks into the scratch buffer */
|
||||||
chunksize = fread(chunk, 1, SERVER_FILER_SCRATCH_BUFSIZE, fd);
|
chunksize = fread(chunk, 1, SERVER_FILER_SCRATCH_BUFSIZE, fd);
|
||||||
|
|
||||||
/* Send the buffer contents as HTTP response chunk */
|
/* Send buffer contents as HTTP chunk. If empty this functions as a
|
||||||
|
* last-chunk message, signaling end-of-response, to the HTTP client.
|
||||||
|
* See RFC 2616, section 3.6.1 for details on Chunked Transfer Encoding. */
|
||||||
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
||||||
fclose(fd);
|
fclose(fd);
|
||||||
ESP_LOGE(TAG, "File sending failed!");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!");
|
||||||
/* Abort sending file */
|
/* Abort sending file */
|
||||||
httpd_resp_sendstr_chunk(req, NULL);
|
httpd_resp_sendstr_chunk(req, NULL);
|
||||||
/* Respond with 500 Internal Server Error */
|
/* Respond with 500 Internal Server Error */
|
||||||
@@ -607,8 +608,6 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
|||||||
fclose(fd);
|
fclose(fd);
|
||||||
ESP_LOGD(TAG, "File successfully sent");
|
ESP_LOGD(TAG, "File successfully sent");
|
||||||
|
|
||||||
/* Respond with an empty chunk to signal HTTP response completion */
|
|
||||||
httpd_resp_send_chunk(req, NULL, 0);
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -634,14 +633,14 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
/* Filename cannot have a trailing '/' */
|
/* Filename cannot have a trailing '/' */
|
||||||
if (filename[strlen(filename) - 1] == '/') {
|
if (filename[strlen(filename) - 1] == '/') {
|
||||||
ESP_LOGE(TAG, "Invalid filename: %s", filename);
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Invalid filename: " + string(filename));
|
||||||
/* Respond with 400 Bad Request */
|
/* Respond with 400 Bad Request */
|
||||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid filename");
|
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid filename");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stat(filepath, &file_stat) == 0) {
|
if (stat(filepath, &file_stat) == 0) {
|
||||||
ESP_LOGE(TAG, "File already exists: %s", filepath);
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File already exists: " + string(filepath));
|
||||||
/* Respond with 400 Bad Request */
|
/* Respond with 400 Bad Request */
|
||||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File already exists");
|
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File already exists");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
@@ -649,7 +648,7 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
/* File cannot be larger than a limit */
|
/* File cannot be larger than a limit */
|
||||||
if (req->content_len > MAX_FILE_SIZE) {
|
if (req->content_len > MAX_FILE_SIZE) {
|
||||||
ESP_LOGE(TAG, "File too large: %d bytes", req->content_len);
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File too large: " + to_string(req->content_len) + " bytes");
|
||||||
/* Respond with 400 Bad Request */
|
/* Respond with 400 Bad Request */
|
||||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
|
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
|
||||||
"File size must be less than "
|
"File size must be less than "
|
||||||
@@ -661,7 +660,7 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
fd = fopen(filepath, "w");
|
fd = fopen(filepath, "w");
|
||||||
if (!fd) {
|
if (!fd) {
|
||||||
ESP_LOGE(TAG, "Failed to create file: %s", filepath);
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create file: " + string(filepath));
|
||||||
/* Respond with 500 Internal Server Error */
|
/* Respond with 500 Internal Server Error */
|
||||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create file");
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create file");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
@@ -692,7 +691,7 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
|||||||
fclose(fd);
|
fclose(fd);
|
||||||
unlink(filepath);
|
unlink(filepath);
|
||||||
|
|
||||||
ESP_LOGE(TAG, "File reception failed!");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File reception failed!");
|
||||||
/* Respond with 500 Internal Server Error */
|
/* Respond with 500 Internal Server Error */
|
||||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file");
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
@@ -705,7 +704,7 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
|||||||
fclose(fd);
|
fclose(fd);
|
||||||
unlink(filepath);
|
unlink(filepath);
|
||||||
|
|
||||||
ESP_LOGE(TAG, "File write failed!");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File write failed!");
|
||||||
/* Respond with 500 Internal Server Error */
|
/* Respond with 500 Internal Server Error */
|
||||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to write file to storage");
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to write file to storage");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
@@ -718,7 +717,8 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
/* Close file upon upload completion */
|
/* Close file upon upload completion */
|
||||||
fclose(fd);
|
fclose(fd);
|
||||||
ESP_LOGI(TAG, "File reception complete");
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "File saved: " + string(filename));
|
||||||
|
ESP_LOGI(TAG, "File reception completed");
|
||||||
|
|
||||||
std::string directory = std::string(filepath);
|
std::string directory = std::string(filepath);
|
||||||
size_t zw = directory.find("/");
|
size_t zw = directory.find("/");
|
||||||
@@ -737,21 +737,27 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
|||||||
// ESP_LOGD(TAG, "Directory danach 2: %s", directory.c_str());
|
// ESP_LOGD(TAG, "Directory danach 2: %s", directory.c_str());
|
||||||
|
|
||||||
/* Redirect onto root to see the updated file list */
|
/* Redirect onto root to see the updated file list */
|
||||||
httpd_resp_set_status(req, "303 See Other");
|
if (strcmp(filename, "/config/config.ini") == 0 ||
|
||||||
httpd_resp_set_hdr(req, "Location", directory.c_str());
|
strcmp(filename, "/config/ref0.jpg") == 0 ||
|
||||||
|
strcmp(filename, "/config/ref0_org.jpg") == 0 ||
|
||||||
|
strcmp(filename, "/config/ref1.jpg") == 0 ||
|
||||||
|
strcmp(filename, "/config/ref1_org.jpg") == 0 ||
|
||||||
|
strcmp(filename, "/config/reference.jpg") == 0 ||
|
||||||
|
strcmp(filename, "/img_tmp/ref0.jpg") == 0 ||
|
||||||
|
strcmp(filename, "/img_tmp/ref0_org.jpg") == 0 ||
|
||||||
|
strcmp(filename, "/img_tmp/ref1.jpg") == 0 ||
|
||||||
|
strcmp(filename, "/img_tmp/ref1_org.jpg") == 0 ||
|
||||||
|
strcmp(filename, "/img_tmp/reference.jpg") == 0 )
|
||||||
|
{
|
||||||
|
httpd_resp_set_status(req, HTTPD_200); // Avoid reloading of folder content
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
httpd_resp_set_status(req, "303 See Other"); // Reload folder content after upload
|
||||||
|
}
|
||||||
|
|
||||||
/* Redirect onto root to see the updated file list */
|
|
||||||
httpd_resp_set_status(req, "303 See Other");
|
|
||||||
httpd_resp_set_hdr(req, "Location", directory.c_str());
|
httpd_resp_set_hdr(req, "Location", directory.c_str());
|
||||||
httpd_resp_sendstr(req, "File uploaded successfully");
|
httpd_resp_sendstr(req, "File uploaded successfully");
|
||||||
|
|
||||||
/*
|
|
||||||
if (strcmp(filepath, CONFIG_FILE) == 0) {
|
|
||||||
ESP_LOGD(TAG, "New config found. Reload handler.");
|
|
||||||
gpio_handler_deinit();
|
|
||||||
MQTTdestroy();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
@@ -780,7 +786,7 @@ static esp_err_t delete_post_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
if (httpd_query_key_value(_query, "task", _valuechar, 30) == ESP_OK)
|
if (httpd_query_key_value(_query, "task", _valuechar, 30) == ESP_OK)
|
||||||
{
|
{
|
||||||
ESP_LOGD(TAG, "task is found: %s", _valuechar);
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "task is found: " + string(_valuechar));
|
||||||
_task = std::string(_valuechar);
|
_task = std::string(_valuechar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -826,28 +832,27 @@ static esp_err_t delete_post_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
/* Filename cannot have a trailing '/' */
|
/* Filename cannot have a trailing '/' */
|
||||||
if (filename[strlen(filename) - 1] == '/') {
|
if (filename[strlen(filename) - 1] == '/') {
|
||||||
ESP_LOGE(TAG, "Invalid filename: %s", filename);
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Invalid filename: " + string(filename));
|
||||||
/* Respond with 400 Bad Request */
|
/* Respond with 400 Bad Request */
|
||||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid filename");
|
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid filename");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(filename, "wlan.ini") == 0) {
|
if (strcmp(filename, "wlan.ini") == 0) {
|
||||||
ESP_LOGE(TAG, "Trying to delete protected file : %s", filename);
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to delete protected file : " + string(filename));
|
||||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Not allowed to delete wlan.ini");
|
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Not allowed to delete wlan.ini");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stat(filepath, &file_stat) == -1) {
|
if (stat(filepath, &file_stat) == -1) { // File does not exist
|
||||||
ESP_LOGE(TAG, "File does not exist: %s", filename);
|
/* This is ok, we would delete it anyway */
|
||||||
/* Respond with 400 Bad Request */
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "File does not exist: " + string(filename));
|
||||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File does not exist");
|
|
||||||
return ESP_FAIL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Deleting file: %s", filename);
|
|
||||||
/* Delete file */
|
/* Delete file */
|
||||||
unlink(filepath);
|
unlink(filepath);
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "File deleted: " + string(filename));
|
||||||
|
ESP_LOGI(TAG, "File deletion completed");
|
||||||
|
|
||||||
directory = std::string(filepath);
|
directory = std::string(filepath);
|
||||||
size_t zw = directory.find("/");
|
size_t zw = directory.find("/");
|
||||||
@@ -864,16 +869,30 @@ static esp_err_t delete_post_handler(httpd_req_t *req)
|
|||||||
directory = directory.substr(start_fn, found - start_fn + 1);
|
directory = directory.substr(start_fn, found - start_fn + 1);
|
||||||
directory = "/fileserver" + directory;
|
directory = "/fileserver" + directory;
|
||||||
ESP_LOGD(TAG, "Directory danach 4: %s", directory.c_str());
|
ESP_LOGD(TAG, "Directory danach 4: %s", directory.c_str());
|
||||||
}
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/* Redirect onto root to see the updated file list */
|
||||||
|
if (strcmp(filename, "/config/config.ini") == 0 ||
|
||||||
|
strcmp(filename, "/config/ref0.jpg") == 0 ||
|
||||||
|
strcmp(filename, "/config/ref0_org.jpg") == 0 ||
|
||||||
|
strcmp(filename, "/config/ref1.jpg") == 0 ||
|
||||||
|
strcmp(filename, "/config/ref1_org.jpg") == 0 ||
|
||||||
|
strcmp(filename, "/config/reference.jpg") == 0 ||
|
||||||
|
strcmp(filename, "/img_tmp/ref0.jpg") == 0 ||
|
||||||
|
strcmp(filename, "/img_tmp/ref0_org.jpg") == 0 ||
|
||||||
|
strcmp(filename, "/img_tmp/ref1.jpg") == 0 ||
|
||||||
|
strcmp(filename, "/img_tmp/ref1_org.jpg") == 0 ||
|
||||||
|
strcmp(filename, "/img_tmp/reference.jpg") == 0 )
|
||||||
|
{
|
||||||
|
httpd_resp_set_status(req, HTTPD_200); // Avoid reloading of folder content
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
httpd_resp_set_status(req, "303 See Other"); // Reload folder content after upload
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
/* Redirect onto root to see the updated file list */
|
|
||||||
httpd_resp_set_status(req, "303 See Other");
|
|
||||||
httpd_resp_set_hdr(req, "Location", directory.c_str());
|
httpd_resp_set_hdr(req, "Location", directory.c_str());
|
||||||
httpd_resp_sendstr(req, "File successfully deleted");
|
httpd_resp_sendstr(req, "File successfully deleted");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@@ -887,7 +906,7 @@ void delete_all_in_directory(std::string _directory)
|
|||||||
std::string filename;
|
std::string filename;
|
||||||
|
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
ESP_LOGE(TAG, "Failed to stat dir: %s", _directory.c_str());
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to stat dir: " + _directory);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -896,7 +915,7 @@ void delete_all_in_directory(std::string _directory)
|
|||||||
if (!(entry->d_type == DT_DIR)){
|
if (!(entry->d_type == DT_DIR)){
|
||||||
if (strcmp("wlan.ini", entry->d_name) != 0){ // auf wlan.ini soll nicht zugegriffen werden !!!
|
if (strcmp("wlan.ini", entry->d_name) != 0){ // auf wlan.ini soll nicht zugegriffen werden !!!
|
||||||
filename = _directory + "/" + std::string(entry->d_name);
|
filename = _directory + "/" + std::string(entry->d_name);
|
||||||
ESP_LOGI(TAG, "Deleting file: %s", filename.c_str());
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Deleting file: " + filename);
|
||||||
/* Delete file */
|
/* Delete file */
|
||||||
unlink(filename.c_str());
|
unlink(filename.c_str());
|
||||||
}
|
}
|
||||||
@@ -930,7 +949,7 @@ std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::st
|
|||||||
|
|
||||||
// Get and print information about each file in the archive.
|
// Get and print information about each file in the archive.
|
||||||
int numberoffiles = (int)mz_zip_reader_get_num_files(&zip_archive);
|
int numberoffiles = (int)mz_zip_reader_get_num_files(&zip_archive);
|
||||||
ESP_LOGI(TAG, "Numbers of files to be extracted: %d", numberoffiles);
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Files to be extracted: " + to_string(numberoffiles));
|
||||||
|
|
||||||
sort_iter = 0;
|
sort_iter = 0;
|
||||||
{
|
{
|
||||||
@@ -953,7 +972,7 @@ std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::st
|
|||||||
p = mz_zip_reader_extract_file_to_heap(&zip_archive, archive_filename, &uncomp_size, 0);
|
p = mz_zip_reader_extract_file_to_heap(&zip_archive, archive_filename, &uncomp_size, 0);
|
||||||
if (!p)
|
if (!p)
|
||||||
{
|
{
|
||||||
ESP_LOGE(TAG, "mz_zip_reader_extract_file_to_heap() failed on file %s", archive_filename);
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mz_zip_reader_extract_file_to_heap() failed on file " + string(archive_filename));
|
||||||
mz_zip_reader_end(&zip_archive);
|
mz_zip_reader_end(&zip_archive);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -994,10 +1013,14 @@ std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::st
|
|||||||
|
|
||||||
string filename_zw = zw + SUFFIX_ZW;
|
string filename_zw = zw + SUFFIX_ZW;
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Filename to extract: %s, Zwischenfilename: %s", zw.c_str(), filename_zw.c_str());
|
ESP_LOGI(TAG, "File to extract: %s, Temp. Filename: %s", zw.c_str(), filename_zw.c_str());
|
||||||
|
|
||||||
|
std::string folder = filename_zw.substr(0, filename_zw.find_last_of('/'));
|
||||||
|
MakeDir(folder);
|
||||||
|
|
||||||
// extrahieren in zwischendatei
|
// extrahieren in zwischendatei
|
||||||
DeleteFile(filename_zw);
|
DeleteFile(filename_zw);
|
||||||
|
|
||||||
FILE* fpTargetFile = fopen(filename_zw.c_str(), "wb");
|
FILE* fpTargetFile = fopen(filename_zw.c_str(), "wb");
|
||||||
uint writtenbytes = fwrite(p, 1, (uint)uncomp_size, fpTargetFile);
|
uint writtenbytes = fwrite(p, 1, (uint)uncomp_size, fpTargetFile);
|
||||||
fclose(fpTargetFile);
|
fclose(fpTargetFile);
|
||||||
@@ -1011,21 +1034,22 @@ std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::st
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
isokay = false;
|
isokay = false;
|
||||||
ESP_LOGD(TAG, "ERROR in writting extracted file (function fwrite) extracted file \"%s\", size %u", archive_filename, (uint)uncomp_size);
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ERROR in writting extracted file (function fwrite) extracted file \"" +
|
||||||
|
string(archive_filename) + "\", size " + to_string(uncomp_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
DeleteFile(zw);
|
DeleteFile(zw);
|
||||||
if (!isokay)
|
if (!isokay)
|
||||||
ESP_LOGE(TAG, "ERROR in fwrite \"%s\", size %u", archive_filename, (uint)uncomp_size);
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ERROR in fwrite \"" + string(archive_filename) + "\", size " + to_string(uncomp_size));
|
||||||
isokay = isokay && RenameFile(filename_zw, zw);
|
isokay = isokay && RenameFile(filename_zw, zw);
|
||||||
if (!isokay)
|
if (!isokay)
|
||||||
ESP_LOGE(TAG, "ERROR in Rename \"%s\" to \"%s\"", filename_zw.c_str(), zw.c_str());
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ERROR in Rename \"" + filename_zw + "\" to \"" + zw);
|
||||||
|
|
||||||
if (isokay)
|
if (isokay)
|
||||||
ESP_LOGI(TAG, "Successfully extracted file \"%s\", size %u", archive_filename, (uint)uncomp_size);
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Successfully extracted file \"" + string(archive_filename) + "\", size " + to_string(uncomp_size));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ESP_LOGE(TAG, "ERROR in extracting file \"%s\", size %u", archive_filename, (uint)uncomp_size);
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ERROR in extracting file \"" + string(archive_filename) + "\", size " + to_string(uncomp_size));
|
||||||
ret = "ERROR";
|
ret = "ERROR";
|
||||||
}
|
}
|
||||||
mz_free(p);
|
mz_free(p);
|
||||||
@@ -1059,7 +1083,7 @@ void unzip(std::string _in_zip_file, std::string _target_directory){
|
|||||||
status = mz_zip_reader_init_file(&zip_archive, _in_zip_file.c_str(), 0);
|
status = mz_zip_reader_init_file(&zip_archive, _in_zip_file.c_str(), 0);
|
||||||
if (!status)
|
if (!status)
|
||||||
{
|
{
|
||||||
ESP_LOGD(TAG, "mz_zip_reader_init_file() failed!");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mz_zip_reader_init_file() failed!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1071,7 +1095,7 @@ void unzip(std::string _in_zip_file, std::string _target_directory){
|
|||||||
status = mz_zip_reader_init_file(&zip_archive, _in_zip_file.c_str(), sort_iter ? MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY : 0);
|
status = mz_zip_reader_init_file(&zip_archive, _in_zip_file.c_str(), sort_iter ? MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY : 0);
|
||||||
if (!status)
|
if (!status)
|
||||||
{
|
{
|
||||||
ESP_LOGD(TAG, "mz_zip_reader_init_file() failed!");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mz_zip_reader_init_file() failed!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1085,7 +1109,7 @@ void unzip(std::string _in_zip_file, std::string _target_directory){
|
|||||||
p = mz_zip_reader_extract_file_to_heap(&zip_archive, archive_filename, &uncomp_size, 0);
|
p = mz_zip_reader_extract_file_to_heap(&zip_archive, archive_filename, &uncomp_size, 0);
|
||||||
if (!p)
|
if (!p)
|
||||||
{
|
{
|
||||||
ESP_LOGD(TAG, "mz_zip_reader_extract_file_to_heap() failed!");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mz_zip_reader_extract_file_to_heap() failed!");
|
||||||
mz_zip_reader_end(&zip_archive);
|
mz_zip_reader_end(&zip_archive);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1093,7 +1117,7 @@ void unzip(std::string _in_zip_file, std::string _target_directory){
|
|||||||
// Save to File.
|
// Save to File.
|
||||||
zw = std::string(archive_filename);
|
zw = std::string(archive_filename);
|
||||||
zw = _target_directory + zw;
|
zw = _target_directory + zw;
|
||||||
ESP_LOGD(TAG, "Filename to extract: %s", zw.c_str());
|
ESP_LOGD(TAG, "File to extract: %s", zw.c_str());
|
||||||
FILE* fpTargetFile = fopen(zw.c_str(), "wb");
|
FILE* fpTargetFile = fopen(zw.c_str(), "wb");
|
||||||
fwrite(p, 1, (uint)uncomp_size, fpTargetFile);
|
fwrite(p, 1, (uint)uncomp_size, fpTargetFile);
|
||||||
fclose(fpTargetFile);
|
fclose(fpTargetFile);
|
||||||
@@ -1121,19 +1145,19 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path)
|
|||||||
/* Validate file storage base path */
|
/* Validate file storage base path */
|
||||||
if (!base_path) {
|
if (!base_path) {
|
||||||
// if (!base_path || strcmp(base_path, "/spiffs") != 0) {
|
// if (!base_path || strcmp(base_path, "/spiffs") != 0) {
|
||||||
ESP_LOGE(TAG, "File server base_path not set");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File server base_path not set");
|
||||||
// return ESP_ERR_INVALID_ARG;
|
// return ESP_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server_data) {
|
if (server_data) {
|
||||||
ESP_LOGE(TAG, "File server already started");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File server already started");
|
||||||
// return ESP_ERR_INVALID_STATE;
|
// return ESP_ERR_INVALID_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate memory for server data */
|
/* Allocate memory for server data */
|
||||||
server_data = (file_server_data *) calloc(1, sizeof(struct file_server_data));
|
server_data = (file_server_data *) calloc(1, sizeof(struct file_server_data));
|
||||||
if (!server_data) {
|
if (!server_data) {
|
||||||
ESP_LOGE(TAG, "Failed to allocate memory for server data");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to allocate memory for server data");
|
||||||
// return ESP_ERR_NO_MEM;
|
// return ESP_ERR_NO_MEM;
|
||||||
}
|
}
|
||||||
strlcpy(server_data->base_path, base_path,
|
strlcpy(server_data->base_path, base_path,
|
||||||
|
|||||||
@@ -61,7 +61,13 @@ esp_err_t send_file(httpd_req_t *req, std::string filename)
|
|||||||
endsWith(filename, ".jpeg") ||
|
endsWith(filename, ".jpeg") ||
|
||||||
endsWith(filename, ".ico") ||
|
endsWith(filename, ".ico") ||
|
||||||
endsWith(filename, ".png")) {
|
endsWith(filename, ".png")) {
|
||||||
httpd_resp_set_hdr(req, "Cache-Control", "max-age=86400");
|
|
||||||
|
if (filename == "/sdcard/html/setup.html") {
|
||||||
|
httpd_resp_set_hdr(req, "Clear-Site-Data", "\"*\"");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
httpd_resp_set_hdr(req, "Cache-Control", "max-age=86400");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
set_content_type_from_file(req, filename.c_str());
|
set_content_type_from_file(req, filename.c_str());
|
||||||
|
|||||||
@@ -3,7 +3,10 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
|
|
||||||
#include <esp_int_wdt.h>
|
/* TODO Rethink the usage of the int watchdog. It is no longer to be used, see
|
||||||
|
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/migration-guides/release-5.x/5.0/system.html?highlight=esp_int_wdt */
|
||||||
|
#include "esp_private/esp_int_wdt.h"
|
||||||
|
|
||||||
#include <esp_task_wdt.h>
|
#include <esp_task_wdt.h>
|
||||||
|
|
||||||
|
|
||||||
@@ -11,14 +14,13 @@
|
|||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "esp_system.h"
|
#include "esp_system.h"
|
||||||
#include "esp_event.h"
|
|
||||||
#include "esp_event.h"
|
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include <esp_ota_ops.h>
|
#include <esp_ota_ops.h>
|
||||||
#include "esp_http_client.h"
|
#include "esp_http_client.h"
|
||||||
#include "esp_flash_partitions.h"
|
#include "esp_flash_partitions.h"
|
||||||
#include "esp_partition.h"
|
#include "esp_partition.h"
|
||||||
#include <nvs.h>
|
#include <nvs.h>
|
||||||
|
#include "esp_app_format.h"
|
||||||
#include "nvs_flash.h"
|
#include "nvs_flash.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
// #include "protocol_examples_common.h"
|
// #include "protocol_examples_common.h"
|
||||||
@@ -26,7 +28,7 @@
|
|||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "server_tflite.h"
|
#include "MainFlowControl.h"
|
||||||
#include "server_file.h"
|
#include "server_file.h"
|
||||||
#include "server_GPIO.h"
|
#include "server_GPIO.h"
|
||||||
#ifdef ENABLE_MQTT
|
#ifdef ENABLE_MQTT
|
||||||
@@ -39,6 +41,7 @@
|
|||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
|
|
||||||
#include "Helper.h"
|
#include "Helper.h"
|
||||||
|
#include "statusled.h"
|
||||||
#include "../../include/defines.h"
|
#include "../../include/defines.h"
|
||||||
|
|
||||||
/*an ota data write buffer ready to write to the flash*/
|
/*an ota data write buffer ready to write to the flash*/
|
||||||
@@ -56,9 +59,9 @@ bool initial_setup = false;
|
|||||||
static void infinite_loop(void)
|
static void infinite_loop(void)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
ESP_LOGI(TAG, "When a new firmware is available on the server, press the reset button to download it");
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "When a new firmware is available on the server, press the reset button to download it");
|
||||||
while(1) {
|
while(1) {
|
||||||
ESP_LOGI(TAG, "Waiting for a new firmware... %d", ++i);
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Waiting for a new firmware... (" + to_string(++i) + ")");
|
||||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,6 +69,8 @@ static void infinite_loop(void)
|
|||||||
|
|
||||||
void task_do_Update_ZIP(void *pvParameter)
|
void task_do_Update_ZIP(void *pvParameter)
|
||||||
{
|
{
|
||||||
|
StatusLED(AP_OR_OTA, 1, true); // Signaling an OTA update
|
||||||
|
|
||||||
std::string filetype = toUpper(getFileType(_file_name_update));
|
std::string filetype = toUpper(getFileType(_file_name_update));
|
||||||
|
|
||||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "File: " + _file_name_update + " Filetype: " + filetype);
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "File: " + _file_name_update + " Filetype: " + filetype);
|
||||||
@@ -87,13 +92,13 @@ void task_do_Update_ZIP(void *pvParameter)
|
|||||||
ota_update_task(retfirmware);
|
ota_update_task(retfirmware);
|
||||||
}
|
}
|
||||||
|
|
||||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Trigger reboot due to firmware update.");
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Trigger reboot due to firmware update");
|
||||||
doRebootOTA();
|
doRebootOTA();
|
||||||
} else if (filetype == "BIN")
|
} else if (filetype == "BIN")
|
||||||
{
|
{
|
||||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Do firmware update - file: " + _file_name_update);
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Do firmware update - file: " + _file_name_update);
|
||||||
ota_update_task(_file_name_update);
|
ota_update_task(_file_name_update);
|
||||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Trigger reboot due to firmware update.");
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Trigger reboot due to firmware update");
|
||||||
doRebootOTA();
|
doRebootOTA();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -108,7 +113,7 @@ void CheckUpdate()
|
|||||||
FILE *pfile;
|
FILE *pfile;
|
||||||
if ((pfile = fopen("/sdcard/update.txt", "r")) == NULL)
|
if ((pfile = fopen("/sdcard/update.txt", "r")) == NULL)
|
||||||
{
|
{
|
||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "No update triggered.");
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "No pending update");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,13 +125,13 @@ void CheckUpdate()
|
|||||||
std::string _szw = std::string(zw);
|
std::string _szw = std::string(zw);
|
||||||
if (_szw == "init")
|
if (_szw == "init")
|
||||||
{
|
{
|
||||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Inital Setup triggered.");
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Inital Setup triggered");
|
||||||
initial_setup = true; }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(pfile);
|
fclose(pfile);
|
||||||
DeleteFile("/sdcard/update.txt"); // Prevent Boot Loop!!!
|
DeleteFile("/sdcard/update.txt"); // Prevent Boot Loop!!!
|
||||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Update during boot triggered - Update File: " + _file_name_update);
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Start update process (" + _file_name_update + ")");
|
||||||
|
|
||||||
|
|
||||||
xTaskCreate(&task_do_Update_ZIP, "task_do_Update_ZIP", configMINIMAL_STACK_SIZE * 35, NULL, tskIDLE_PRIORITY+1, NULL);
|
xTaskCreate(&task_do_Update_ZIP, "task_do_Update_ZIP", configMINIMAL_STACK_SIZE * 35, NULL, tskIDLE_PRIORITY+1, NULL);
|
||||||
@@ -148,18 +153,18 @@ static bool ota_update_task(std::string fn)
|
|||||||
const esp_partition_t *configured = esp_ota_get_boot_partition();
|
const esp_partition_t *configured = esp_ota_get_boot_partition();
|
||||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||||
|
|
||||||
if (configured != running) {
|
if (configured != running) {
|
||||||
ESP_LOGW(TAG, "Configured OTA boot partition at offset 0x%08x, but running from offset 0x%08x",
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Configured OTA boot partition at offset " + to_string(configured->address) +
|
||||||
configured->address, running->address);
|
", but running from offset " + to_string(running->address));
|
||||||
ESP_LOGW(TAG, "(This can happen if either the OTA boot data or preferred boot image become somehow corrupted.)");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "(This can happen if either the OTA boot data or preferred boot image become somehow corrupted.)");
|
||||||
}
|
}
|
||||||
ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08x)",
|
ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08x)",
|
||||||
running->type, running->subtype, running->address);
|
running->type, running->subtype, (unsigned int)running->address);
|
||||||
|
|
||||||
|
|
||||||
update_partition = esp_ota_get_next_update_partition(NULL);
|
update_partition = esp_ota_get_next_update_partition(NULL);
|
||||||
ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x",
|
ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x",
|
||||||
update_partition->subtype, update_partition->address);
|
update_partition->subtype, (unsigned int)update_partition->address);
|
||||||
// assert(update_partition != NULL);
|
// assert(update_partition != NULL);
|
||||||
|
|
||||||
int binary_file_length = 0;
|
int binary_file_length = 0;
|
||||||
@@ -179,7 +184,7 @@ static bool ota_update_task(std::string fn)
|
|||||||
|
|
||||||
while (data_read > 0) {
|
while (data_read > 0) {
|
||||||
if (data_read < 0) {
|
if (data_read < 0) {
|
||||||
ESP_LOGE(TAG, "Error: SSL data read error");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Error: SSL data read error");
|
||||||
return false;
|
return false;
|
||||||
} else if (data_read > 0) {
|
} else if (data_read > 0) {
|
||||||
if (image_header_was_checked == false) {
|
if (image_header_was_checked == false) {
|
||||||
@@ -203,16 +208,17 @@ static bool ota_update_task(std::string fn)
|
|||||||
// check current version with last invalid partition
|
// check current version with last invalid partition
|
||||||
if (last_invalid_app != NULL) {
|
if (last_invalid_app != NULL) {
|
||||||
if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0) {
|
if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0) {
|
||||||
ESP_LOGW(TAG, "New version is the same as invalid version.");
|
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "New version is the same as invalid version");
|
||||||
ESP_LOGW(TAG, "Previously, there was an attempt to launch the firmware with %s version, but it failed.", invalid_app_info.version);
|
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Previously, there was an attempt to launch the firmware with " +
|
||||||
ESP_LOGW(TAG, "The firmware has been rolled back to the previous version.");
|
string(invalid_app_info.version) + " version, but it failed");
|
||||||
|
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "The firmware has been rolled back to the previous version");
|
||||||
infinite_loop();
|
infinite_loop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
if (memcmp(new_app_info.version, running_app_info.version, sizeof(new_app_info.version)) == 0) {
|
if (memcmp(new_app_info.version, running_app_info.version, sizeof(new_app_info.version)) == 0) {
|
||||||
ESP_LOGW(TAG, "Current running version is the same as a new. We will not continue the update.");
|
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Current running version is the same as a new. We will not continue the update");
|
||||||
infinite_loop();
|
infinite_loop();
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@@ -220,12 +226,12 @@ static bool ota_update_task(std::string fn)
|
|||||||
|
|
||||||
err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
|
err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err));
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_ota_begin failed (" + string(esp_err_to_name(err)) + ")");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ESP_LOGI(TAG, "esp_ota_begin succeeded");
|
ESP_LOGI(TAG, "esp_ota_begin succeeded");
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGE(TAG, "received package is not fit len");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "received package is not fit len");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -241,7 +247,7 @@ static bool ota_update_task(std::string fn)
|
|||||||
// * `errno` to check for underlying transport connectivity closure if any
|
// * `errno` to check for underlying transport connectivity closure if any
|
||||||
//
|
//
|
||||||
if (errno == ECONNRESET || errno == ENOTCONN) {
|
if (errno == ECONNRESET || errno == ENOTCONN) {
|
||||||
ESP_LOGE(TAG, "Connection closed, errno = %d", errno);
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection closed, errno = " + to_string(errno));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -254,15 +260,15 @@ static bool ota_update_task(std::string fn)
|
|||||||
err = esp_ota_end(update_handle);
|
err = esp_ota_end(update_handle);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
if (err == ESP_ERR_OTA_VALIDATE_FAILED) {
|
if (err == ESP_ERR_OTA_VALIDATE_FAILED) {
|
||||||
ESP_LOGE(TAG, "Image validation failed, image is corrupted");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Image validation failed, image is corrupted");
|
||||||
}
|
}
|
||||||
ESP_LOGE(TAG, "esp_ota_end failed (%s)!", esp_err_to_name(err));
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_ota_end failed (" + string(esp_err_to_name(err)) + ")!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = esp_ota_set_boot_partition(update_partition);
|
err = esp_ota_set_boot_partition(update_partition);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err));
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_ota_set_boot_partition failed (" + string(esp_err_to_name(err)) + ")!");
|
||||||
|
|
||||||
}
|
}
|
||||||
// ESP_LOGI(TAG, "Prepare to restart system!");
|
// ESP_LOGI(TAG, "Prepare to restart system!");
|
||||||
@@ -329,7 +335,7 @@ void CheckOTAUpdate(void)
|
|||||||
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution...");
|
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution...");
|
||||||
esp_ota_mark_app_valid_cancel_rollback();
|
esp_ota_mark_app_valid_cancel_rollback();
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGE(TAG, "Diagnostics failed! Start rollback to the previous version...");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Diagnostics failed! Start rollback to the previous version...");
|
||||||
esp_ota_mark_app_invalid_rollback_and_reboot();
|
esp_ota_mark_app_invalid_rollback_and_reboot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -353,7 +359,7 @@ void CheckOTAUpdate(void)
|
|||||||
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution...");
|
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution...");
|
||||||
esp_ota_mark_app_valid_cancel_rollback();
|
esp_ota_mark_app_valid_cancel_rollback();
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGE(TAG, "Diagnostics failed! Start rollback to the previous version...");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Diagnostics failed! Start rollback to the previous version...");
|
||||||
esp_ota_mark_app_invalid_rollback_and_reboot();
|
esp_ota_mark_app_invalid_rollback_and_reboot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -445,7 +451,7 @@ esp_err_t handler_ota_update(httpd_req_t *req)
|
|||||||
if ((filetype == "ZIP") || (filetype == "BIN"))
|
if ((filetype == "ZIP") || (filetype == "BIN"))
|
||||||
{
|
{
|
||||||
FILE *pfile;
|
FILE *pfile;
|
||||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Update for reboot.");
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Update for reboot");
|
||||||
pfile = fopen("/sdcard/update.txt", "w");
|
pfile = fopen("/sdcard/update.txt", "w");
|
||||||
fwrite(fn.c_str(), fn.length(), 1, pfile);
|
fwrite(fn.c_str(), fn.length(), 1, pfile);
|
||||||
fclose(pfile);
|
fclose(pfile);
|
||||||
@@ -463,7 +469,7 @@ esp_err_t handler_ota_update(httpd_req_t *req)
|
|||||||
{
|
{
|
||||||
const char* resp_str;
|
const char* resp_str;
|
||||||
|
|
||||||
KillTFliteTasks();
|
DeleteMainFlowTask();
|
||||||
gpio_handler_deinit();
|
gpio_handler_deinit();
|
||||||
if (ota_update_task(fn))
|
if (ota_update_task(fn))
|
||||||
{
|
{
|
||||||
@@ -522,7 +528,7 @@ esp_err_t handler_ota_update(httpd_req_t *req)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ESP_LOGD(TAG, "File does not exist: %s", fn.c_str());
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File does not exist: " + fn);
|
||||||
}
|
}
|
||||||
/* Respond with an empty chunk to signal HTTP response completion */
|
/* Respond with an empty chunk to signal HTTP response completion */
|
||||||
std::string zw = "file deleted\n";
|
std::string zw = "file deleted\n";
|
||||||
@@ -537,12 +543,12 @@ esp_err_t handler_ota_update(httpd_req_t *req)
|
|||||||
httpd_resp_send(req, zw.c_str(), strlen(zw.c_str()));
|
httpd_resp_send(req, zw.c_str(), strlen(zw.c_str()));
|
||||||
httpd_resp_send_chunk(req, NULL, 0);
|
httpd_resp_send_chunk(req, NULL, 0);
|
||||||
|
|
||||||
ESP_LOGE(TAG, "ota without parameter - should not be the case!");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ota without parameter - should not be the case!");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
const char* resp_str;
|
const char* resp_str;
|
||||||
|
|
||||||
KillTFliteTasks();
|
DeleteMainFlowTask();
|
||||||
gpio_handler_deinit();
|
gpio_handler_deinit();
|
||||||
if (ota_update_task(fn))
|
if (ota_update_task(fn))
|
||||||
{
|
{
|
||||||
@@ -566,13 +572,19 @@ esp_err_t handler_ota_update(httpd_req_t *req)
|
|||||||
|
|
||||||
void hard_restart()
|
void hard_restart()
|
||||||
{
|
{
|
||||||
esp_task_wdt_init(1,true);
|
esp_task_wdt_config_t twdt_config = {
|
||||||
|
.timeout_ms = 1,
|
||||||
|
.idle_core_mask = (1 << portNUM_PROCESSORS) - 1, // Bitmask of all cores
|
||||||
|
.trigger_panic = true,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(esp_task_wdt_init(&twdt_config));
|
||||||
|
|
||||||
esp_task_wdt_add(NULL);
|
esp_task_wdt_add(NULL);
|
||||||
while(true);
|
while(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void task_reboot(void *KillAutoFlow)
|
void task_reboot(void *DeleteMainFlow)
|
||||||
{
|
{
|
||||||
// write a reboot, to identify a reboot by purpouse
|
// write a reboot, to identify a reboot by purpouse
|
||||||
FILE* pfile = fopen("/sdcard/reboot.txt", "w");
|
FILE* pfile = fopen("/sdcard/reboot.txt", "w");
|
||||||
@@ -582,10 +594,13 @@ void task_reboot(void *KillAutoFlow)
|
|||||||
|
|
||||||
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
if ((bool)KillAutoFlow) {
|
if ((bool)DeleteMainFlow) {
|
||||||
KillTFliteTasks(); // Kill autoflow task if executed in extra task, if not don't kill parent task
|
DeleteMainFlowTask(); // Kill autoflow task if executed in extra task, if not don't kill parent task
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Camera.LightOnOff(false);
|
||||||
|
StatusLEDOff();
|
||||||
|
|
||||||
/* Stop service tasks */
|
/* Stop service tasks */
|
||||||
#ifdef ENABLE_MQTT
|
#ifdef ENABLE_MQTT
|
||||||
MQTTdestroy_client(true);
|
MQTTdestroy_client(true);
|
||||||
@@ -600,20 +615,20 @@ void task_reboot(void *KillAutoFlow)
|
|||||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||||
hard_restart(); // Reset type: System reset (Triggered by watchdog), if esp_restart stalls (WDT needs to be activated)
|
hard_restart(); // Reset type: System reset (Triggered by watchdog), if esp_restart stalls (WDT needs to be activated)
|
||||||
|
|
||||||
ESP_LOGE(TAG, "Reboot failed!");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Reboot failed!");
|
||||||
vTaskDelete(NULL); //Delete this task if it comes to this point
|
vTaskDelete(NULL); //Delete this task if it comes to this point
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void doReboot()
|
void doReboot()
|
||||||
{
|
{
|
||||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Reboot triggered by Software (5s).");
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Reboot triggered by Software (5s)");
|
||||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Reboot in 5sec");
|
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Reboot in 5sec");
|
||||||
|
|
||||||
BaseType_t xReturned = xTaskCreate(&task_reboot, "task_reboot", configMINIMAL_STACK_SIZE * 3, (void*) true, 10, NULL);
|
BaseType_t xReturned = xTaskCreate(&task_reboot, "task_reboot", configMINIMAL_STACK_SIZE * 4, (void*) true, 10, NULL);
|
||||||
if( xReturned != pdPASS )
|
if( xReturned != pdPASS )
|
||||||
{
|
{
|
||||||
ESP_LOGE(TAG, "task_reboot not created -> force reboot without killing flow");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "task_reboot not created -> force reboot without killing flow");
|
||||||
task_reboot((void*) false);
|
task_reboot((void*) false);
|
||||||
}
|
}
|
||||||
vTaskDelay(10000 / portTICK_PERIOD_MS); // Prevent serving web client fetch response until system is shuting down
|
vTaskDelay(10000 / portTICK_PERIOD_MS); // Prevent serving web client fetch response until system is shuting down
|
||||||
@@ -624,6 +639,8 @@ void doRebootOTA()
|
|||||||
{
|
{
|
||||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Reboot in 5sec");
|
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Reboot in 5sec");
|
||||||
|
|
||||||
|
Camera.LightOnOff(false);
|
||||||
|
StatusLEDOff();
|
||||||
esp_camera_deinit();
|
esp_camera_deinit();
|
||||||
|
|
||||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||||
@@ -641,7 +658,7 @@ esp_err_t handler_reboot(httpd_req_t *req)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_reboot");
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_reboot");
|
||||||
ESP_LOGI(TAG, "!!! System will restart within 5 sec!!!");
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "!!! System will restart within 5 sec!!!");
|
||||||
|
|
||||||
std::string response =
|
std::string response =
|
||||||
"<html><head><script>"
|
"<html><head><script>"
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
|||||||
|
|
||||||
idf_component_register(SRCS ${app_sources}
|
idf_component_register(SRCS ${app_sources}
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "."
|
||||||
REQUIRES jomjol_tfliteclass jomjol_helper jomjol_controlcamera jomjol_mqtt jomjol_influxdb jomjol_fileserver_ota jomjol_image_proc jomjol_wlan)
|
REQUIRES esp_timer esp_wifi jomjol_tfliteclass jomjol_helper jomjol_controlcamera jomjol_mqtt jomjol_influxdb jomjol_fileserver_ota jomjol_image_proc jomjol_wlan)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,376 +1,402 @@
|
|||||||
#include "ClassFlowAlignment.h"
|
#include "ClassFlowAlignment.h"
|
||||||
#include "ClassFlowMakeImage.h"
|
#include "ClassFlowTakeImage.h"
|
||||||
#include "ClassFlow.h"
|
#include "ClassFlow.h"
|
||||||
#include "server_tflite.h"
|
#include "MainFlowControl.h"
|
||||||
|
|
||||||
#include "CRotateImage.h"
|
#include "CRotateImage.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#include "ClassLogFile.h"
|
||||||
#include "ClassLogFile.h"
|
#include "psram.h"
|
||||||
#include "../../include/defines.h"
|
#include "../../include/defines.h"
|
||||||
|
|
||||||
|
static const char *TAG = "ALIGN";
|
||||||
static const char *TAG = "ALIGN";
|
|
||||||
|
// #define DEBUG_DETAIL_ON
|
||||||
// #define DEBUG_DETAIL_ON
|
|
||||||
|
void ClassFlowAlignment::SetInitialParameter(void)
|
||||||
|
{
|
||||||
void ClassFlowAlignment::SetInitialParameter(void)
|
initialrotate = 0;
|
||||||
{
|
anz_ref = 0;
|
||||||
initalrotate = 0;
|
use_antialiasing = false;
|
||||||
anz_ref = 0;
|
initialflip = false;
|
||||||
initialmirror = false;
|
SaveAllFiles = false;
|
||||||
use_antialiasing = false;
|
namerawimage = "/sdcard/img_tmp/raw.jpg";
|
||||||
initialflip = false;
|
FileStoreRefAlignment = "/sdcard/config/align.txt";
|
||||||
SaveAllFiles = false;
|
ListFlowControll = NULL;
|
||||||
namerawimage = "/sdcard/img_tmp/raw.jpg";
|
AlignAndCutImage = NULL;
|
||||||
FileStoreRefAlignment = "/sdcard/config/align.txt";
|
ImageBasis = NULL;
|
||||||
ListFlowControll = NULL;
|
ImageTMP = NULL;
|
||||||
AlignAndCutImage = NULL;
|
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||||
ImageBasis = NULL;
|
AlgROI = (ImageData *)malloc_psram_heap(std::string(TAG) + "->AlgROI", sizeof(ImageData), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
||||||
ImageTMP = NULL;
|
#endif
|
||||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
previousElement = NULL;
|
||||||
AlgROI = (ImageData*)heap_caps_malloc(sizeof(ImageData), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
disabled = false;
|
||||||
#endif
|
SAD_criteria = 0.05;
|
||||||
previousElement = NULL;
|
}
|
||||||
disabled = false;
|
|
||||||
SAD_criteria = 0.05;
|
ClassFlowAlignment::ClassFlowAlignment(std::vector<ClassFlow *> *lfc)
|
||||||
}
|
{
|
||||||
|
SetInitialParameter();
|
||||||
|
ListFlowControll = lfc;
|
||||||
ClassFlowAlignment::ClassFlowAlignment(std::vector<ClassFlow*>* lfc)
|
|
||||||
{
|
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||||
SetInitialParameter();
|
{
|
||||||
ListFlowControll = lfc;
|
if (((*ListFlowControll)[i])->name().compare("ClassFlowTakeImage") == 0)
|
||||||
|
{
|
||||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
ImageBasis = ((ClassFlowTakeImage *)(*ListFlowControll)[i])->rawImage;
|
||||||
{
|
}
|
||||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowMakeImage") == 0)
|
}
|
||||||
{
|
|
||||||
ImageBasis = ((ClassFlowMakeImage*) (*ListFlowControll)[i])->rawImage;
|
if (!ImageBasis) // the function take pictures does not exist --> must be created first ONLY FOR TEST PURPOSES
|
||||||
}
|
{
|
||||||
}
|
ESP_LOGD(TAG, "CImageBasis had to be created");
|
||||||
|
ImageBasis = new CImageBasis("ImageBasis", namerawimage);
|
||||||
if (!ImageBasis) // the function take pictures does not exist --> must be created first ONLY FOR TEST PURPOSES
|
}
|
||||||
{
|
}
|
||||||
ESP_LOGD(TAG, "CImageBasis had to be created");
|
|
||||||
ImageBasis = new CImageBasis(namerawimage);
|
bool ClassFlowAlignment::ReadParameter(FILE *pfile, string &aktparamgraph)
|
||||||
}
|
{
|
||||||
}
|
std::vector<string> splitted;
|
||||||
|
int suchex = 40;
|
||||||
|
int suchey = 40;
|
||||||
bool ClassFlowAlignment::ReadParameter(FILE* pfile, string& aktparamgraph)
|
int alg_algo = 0; // default=0; 1 =HIGHACCURACY; 2= FAST; 3= OFF //add disable aligment algo |01.2023
|
||||||
{
|
|
||||||
std::vector<string> splitted;
|
aktparamgraph = trim(aktparamgraph);
|
||||||
int suchex = 40;
|
|
||||||
int suchey = 40;
|
if (aktparamgraph.size() == 0)
|
||||||
int alg_algo = 0;
|
{
|
||||||
|
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||||
|
{
|
||||||
aktparamgraph = trim(aktparamgraph);
|
return false;
|
||||||
|
}
|
||||||
if (aktparamgraph.size() == 0)
|
}
|
||||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
|
||||||
return false;
|
if (aktparamgraph.compare("[Alignment]") != 0)
|
||||||
|
{
|
||||||
if (aktparamgraph.compare("[Alignment]") != 0) //Paragraph does not fit MakeImage
|
// Paragraph does not fit Alignment
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
|
||||||
{
|
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||||
splitted = ZerlegeZeile(aktparamgraph);
|
{
|
||||||
if ((toUpper(splitted[0]) == "FLIPIMAGESIZE") && (splitted.size() > 1))
|
splitted = ZerlegeZeile(aktparamgraph);
|
||||||
{
|
|
||||||
if (toUpper(splitted[1]) == "TRUE")
|
if ((toUpper(splitted[0]) == "FLIPIMAGESIZE") && (splitted.size() > 1))
|
||||||
initialflip = true;
|
{
|
||||||
}
|
if (toUpper(splitted[1]) == "TRUE")
|
||||||
if ((toUpper(splitted[0]) == "INITIALMIRROR") && (splitted.size() > 1))
|
{
|
||||||
{
|
initialflip = true;
|
||||||
if (toUpper(splitted[1]) == "TRUE")
|
}
|
||||||
initialmirror = true;
|
}
|
||||||
}
|
else if (((toUpper(splitted[0]) == "initialrotate") || (toUpper(splitted[0]) == "INITIALROTATE")) && (splitted.size() > 1))
|
||||||
if (((toUpper(splitted[0]) == "INITALROTATE") || (toUpper(splitted[0]) == "INITIALROTATE")) && (splitted.size() > 1))
|
{
|
||||||
{
|
this->initialrotate = std::stod(splitted[1]);
|
||||||
this->initalrotate = std::stod(splitted[1]);
|
}
|
||||||
}
|
else if ((toUpper(splitted[0]) == "SEARCHFIELDX") && (splitted.size() > 1))
|
||||||
if ((toUpper(splitted[0]) == "SEARCHFIELDX") && (splitted.size() > 1))
|
{
|
||||||
{
|
suchex = std::stod(splitted[1]);
|
||||||
suchex = std::stod(splitted[1]);
|
}
|
||||||
}
|
else if ((toUpper(splitted[0]) == "SEARCHFIELDY") && (splitted.size() > 1))
|
||||||
if ((toUpper(splitted[0]) == "SEARCHFIELDY") && (splitted.size() > 1))
|
{
|
||||||
{
|
suchey = std::stod(splitted[1]);
|
||||||
suchey = std::stod(splitted[1]);
|
}
|
||||||
}
|
else if ((toUpper(splitted[0]) == "ANTIALIASING") && (splitted.size() > 1))
|
||||||
if ((toUpper(splitted[0]) == "ANTIALIASING") && (splitted.size() > 1))
|
{
|
||||||
{
|
if (toUpper(splitted[1]) == "TRUE")
|
||||||
if (toUpper(splitted[1]) == "TRUE")
|
{
|
||||||
use_antialiasing = true;
|
use_antialiasing = true;
|
||||||
}
|
}
|
||||||
if ((splitted.size() == 3) && (anz_ref < 2))
|
}
|
||||||
{
|
else if ((splitted.size() == 3) && (anz_ref < 2))
|
||||||
References[anz_ref].image_file = FormatFileName("/sdcard" + splitted[0]);
|
{
|
||||||
References[anz_ref].target_x = std::stod(splitted[1]);
|
References[anz_ref].image_file = FormatFileName("/sdcard" + splitted[0]);
|
||||||
References[anz_ref].target_y = std::stod(splitted[2]);
|
References[anz_ref].target_x = std::stod(splitted[1]);
|
||||||
anz_ref++;
|
References[anz_ref].target_y = std::stod(splitted[2]);
|
||||||
}
|
anz_ref++;
|
||||||
|
}
|
||||||
if ((toUpper(splitted[0]) == "SAVEALLFILES") && (splitted.size() > 1))
|
|
||||||
{
|
else if ((toUpper(splitted[0]) == "SAVEALLFILES") && (splitted.size() > 1))
|
||||||
if (toUpper(splitted[1]) == "TRUE")
|
{
|
||||||
SaveAllFiles = true;
|
if (toUpper(splitted[1]) == "TRUE")
|
||||||
}
|
{
|
||||||
if ((toUpper(splitted[0]) == "ALIGNMENTALGO") && (splitted.size() > 1))
|
SaveAllFiles = true;
|
||||||
{
|
}
|
||||||
#ifdef DEBUG_DETAIL_ON
|
}
|
||||||
std::string zw2 = "Alignment mode selected: " + splitted[1];
|
else if ((toUpper(splitted[0]) == "ALIGNMENTALGO") && (splitted.size() > 1))
|
||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2);
|
{
|
||||||
#endif
|
#ifdef DEBUG_DETAIL_ON
|
||||||
if (toUpper(splitted[1]) == "HIGHACCURACY")
|
std::string zw2 = "Alignment mode selected: " + splitted[1];
|
||||||
alg_algo = 1;
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2);
|
||||||
if (toUpper(splitted[1]) == "FAST")
|
#endif
|
||||||
alg_algo = 2;
|
if (toUpper(splitted[1]) == "HIGHACCURACY")
|
||||||
}
|
{
|
||||||
}
|
alg_algo = 1;
|
||||||
|
}
|
||||||
for (int i = 0; i < anz_ref; ++i)
|
if (toUpper(splitted[1]) == "FAST")
|
||||||
{
|
{
|
||||||
References[i].search_x = suchex;
|
alg_algo = 2;
|
||||||
References[i].search_y = suchey;
|
}
|
||||||
References[i].fastalg_SAD_criteria = SAD_criteria;
|
if (toUpper(splitted[1]) == "OFF")
|
||||||
References[i].alignment_algo = alg_algo;
|
{
|
||||||
#ifdef DEBUG_DETAIL_ON
|
// no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
|
||||||
std::string zw2 = "Alignment mode written: " + std::to_string(alg_algo);
|
alg_algo = 3;
|
||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2);
|
}
|
||||||
#endif
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadReferenceAlignmentValues();
|
for (int i = 0; i < anz_ref; ++i)
|
||||||
|
{
|
||||||
return true;
|
References[i].search_x = suchex;
|
||||||
|
References[i].search_y = suchey;
|
||||||
}
|
References[i].fastalg_SAD_criteria = SAD_criteria;
|
||||||
|
References[i].alignment_algo = alg_algo;
|
||||||
|
#ifdef DEBUG_DETAIL_ON
|
||||||
string ClassFlowAlignment::getHTMLSingleStep(string host)
|
std::string zw2 = "Alignment mode written: " + std::to_string(alg_algo);
|
||||||
{
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2);
|
||||||
string result;
|
#endif
|
||||||
|
}
|
||||||
result = "<p>Rotated Image: </p> <p><img src=\"" + host + "/img_tmp/rot.jpg\"></p>\n";
|
|
||||||
result = result + "<p>Found Alignment: </p> <p><img src=\"" + host + "/img_tmp/rot_roi.jpg\"></p>\n";
|
// no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
|
||||||
result = result + "<p>Aligned Image: </p> <p><img src=\"" + host + "/img_tmp/alg.jpg\"></p>\n";
|
if (References[0].alignment_algo != 3)
|
||||||
return result;
|
{
|
||||||
}
|
LoadReferenceAlignmentValues();
|
||||||
|
}
|
||||||
|
|
||||||
bool ClassFlowAlignment::doFlow(string time)
|
return true;
|
||||||
{
|
}
|
||||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
|
||||||
if (!AlgROI) // AlgROI needs to be allocated before ImageTMP to avoid heap fragmentation
|
string ClassFlowAlignment::getHTMLSingleStep(string host)
|
||||||
{
|
{
|
||||||
AlgROI = (ImageData*)heap_caps_realloc(AlgROI, sizeof(ImageData), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
string result;
|
||||||
if (!AlgROI)
|
|
||||||
{
|
result = "<p>Rotated Image: </p> <p><img src=\"" + host + "/img_tmp/rot.jpg\"></p>\n";
|
||||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate AlgROI");
|
result = result + "<p>Found Alignment: </p> <p><img src=\"" + host + "/img_tmp/rot_roi.jpg\"></p>\n";
|
||||||
LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow");
|
result = result + "<p>Aligned Image: </p> <p><img src=\"" + host + "/img_tmp/alg.jpg\"></p>\n";
|
||||||
}
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AlgROI)
|
bool ClassFlowAlignment::doFlow(string time)
|
||||||
{
|
{
|
||||||
ImageBasis->writeToMemoryAsJPG((ImageData*)AlgROI, 90);
|
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||||
}
|
if (!AlgROI) // AlgROI needs to be allocated before ImageTMP to avoid heap fragmentation
|
||||||
#endif
|
{
|
||||||
|
AlgROI = (ImageData *)heap_caps_realloc(AlgROI, sizeof(ImageData), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
||||||
if (!ImageTMP)
|
|
||||||
{
|
if (!AlgROI)
|
||||||
ImageTMP = new CImageBasis(ImageBasis);
|
{
|
||||||
if (!ImageTMP)
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate AlgROI");
|
||||||
{
|
LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow");
|
||||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate ImageTMP -> Exec this round aborted!");
|
}
|
||||||
LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow");
|
}
|
||||||
return false;
|
|
||||||
}
|
if (AlgROI)
|
||||||
}
|
{
|
||||||
|
ImageBasis->writeToMemoryAsJPG((ImageData *)AlgROI, 90);
|
||||||
delete AlignAndCutImage;
|
}
|
||||||
AlignAndCutImage = new CAlignAndCutImage(ImageBasis, ImageTMP);
|
#endif
|
||||||
if (!AlignAndCutImage)
|
|
||||||
{
|
if (!ImageTMP)
|
||||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate AlignAndCutImage -> Exec this round aborted!");
|
{
|
||||||
LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow");
|
ImageTMP = new CImageBasis("tmpImage", ImageBasis); // Make sure the name does not get change, it is relevant for the PSRAM allocation!
|
||||||
return false;
|
|
||||||
}
|
if (!ImageTMP)
|
||||||
|
{
|
||||||
CRotateImage rt(AlignAndCutImage, ImageTMP, initialflip);
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate tmpImage -> Exec this round aborted!");
|
||||||
if (initialflip)
|
LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow");
|
||||||
{
|
return false;
|
||||||
int _zw = ImageBasis->height;
|
}
|
||||||
ImageBasis->height = ImageBasis->width;
|
}
|
||||||
ImageBasis->width = _zw;
|
|
||||||
|
delete AlignAndCutImage;
|
||||||
_zw = ImageTMP->width;
|
AlignAndCutImage = new CAlignAndCutImage("AlignAndCutImage", ImageBasis, ImageTMP);
|
||||||
ImageTMP->width = ImageTMP->height;
|
|
||||||
ImageTMP->height = _zw;
|
if (!AlignAndCutImage)
|
||||||
}
|
{
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate AlignAndCutImage -> Exec this round aborted!");
|
||||||
if (initialmirror)
|
LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow");
|
||||||
{
|
return false;
|
||||||
ESP_LOGD(TAG, "do mirror");
|
}
|
||||||
rt.Mirror();
|
|
||||||
|
CRotateImage rt("rawImage", AlignAndCutImage, ImageTMP, initialflip);
|
||||||
if (SaveAllFiles)
|
|
||||||
AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/mirror.jpg"));
|
if (initialflip)
|
||||||
}
|
{
|
||||||
|
int _zw = ImageBasis->height;
|
||||||
if ((initalrotate != 0) || initialflip)
|
ImageBasis->height = ImageBasis->width;
|
||||||
{
|
ImageBasis->width = _zw;
|
||||||
if (use_antialiasing)
|
|
||||||
rt.RotateAntiAliasing(initalrotate);
|
_zw = ImageTMP->width;
|
||||||
else
|
ImageTMP->width = ImageTMP->height;
|
||||||
rt.Rotate(initalrotate);
|
ImageTMP->height = _zw;
|
||||||
|
}
|
||||||
if (SaveAllFiles)
|
|
||||||
AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/rot.jpg"));
|
if ((initialrotate != 0) || initialflip)
|
||||||
}
|
{
|
||||||
|
if (use_antialiasing)
|
||||||
if (!AlignAndCutImage->Align(&References[0], &References[1]))
|
{
|
||||||
{
|
rt.RotateAntiAliasing(initialrotate);
|
||||||
SaveReferenceAlignmentValues();
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
rt.Rotate(initialrotate);
|
||||||
if (AlgROI) {
|
}
|
||||||
DrawRef(ImageTMP);
|
|
||||||
tfliteflow.DigitalDrawROI(ImageTMP);
|
if (SaveAllFiles)
|
||||||
tfliteflow.AnalogDrawROI(ImageTMP);
|
{
|
||||||
ImageTMP->writeToMemoryAsJPG((ImageData*)AlgROI, 90);
|
AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/rot.jpg"));
|
||||||
}
|
}
|
||||||
#endif
|
}
|
||||||
|
|
||||||
if (SaveAllFiles)
|
// no align algo if set to 3 = off //add disable aligment algo |01.2023
|
||||||
{
|
if (References[0].alignment_algo != 3)
|
||||||
AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/alg.jpg"));
|
{
|
||||||
ImageTMP->SaveToFile(FormatFileName("/sdcard/img_tmp/alg_roi.jpg"));
|
if (!AlignAndCutImage->Align(&References[0], &References[1]))
|
||||||
}
|
{
|
||||||
|
SaveReferenceAlignmentValues();
|
||||||
// must be deleted to have memory space for loading tflite
|
}
|
||||||
delete ImageTMP;
|
} // no align
|
||||||
ImageTMP = NULL;
|
|
||||||
|
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||||
LoadReferenceAlignmentValues();
|
if (AlgROI)
|
||||||
|
{
|
||||||
return true;
|
// no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
|
||||||
}
|
if (References[0].alignment_algo != 3)
|
||||||
|
{
|
||||||
|
DrawRef(ImageTMP);
|
||||||
void ClassFlowAlignment::SaveReferenceAlignmentValues()
|
}
|
||||||
{
|
|
||||||
FILE* pFile;
|
flowctrl.DigitalDrawROI(ImageTMP);
|
||||||
std::string zwtime, zwvalue;
|
flowctrl.AnalogDrawROI(ImageTMP);
|
||||||
|
ImageTMP->writeToMemoryAsJPG((ImageData *)AlgROI, 90);
|
||||||
pFile = fopen(FileStoreRefAlignment.c_str(), "w");
|
}
|
||||||
|
#endif
|
||||||
if (strlen(zwtime.c_str()) == 0)
|
|
||||||
{
|
if (SaveAllFiles)
|
||||||
time_t rawtime;
|
{
|
||||||
struct tm* timeinfo;
|
AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/alg.jpg"));
|
||||||
char buffer[80];
|
ImageTMP->SaveToFile(FormatFileName("/sdcard/img_tmp/alg_roi.jpg"));
|
||||||
|
}
|
||||||
time(&rawtime);
|
|
||||||
timeinfo = localtime(&rawtime);
|
// must be deleted to have memory space for loading tflite
|
||||||
|
delete ImageTMP;
|
||||||
strftime(buffer, 80, "%Y-%m-%dT%H:%M:%S", timeinfo);
|
ImageTMP = NULL;
|
||||||
zwtime = std::string(buffer);
|
|
||||||
}
|
// no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
|
||||||
|
if (References[0].alignment_algo != 3)
|
||||||
fputs(zwtime.c_str(), pFile);
|
{
|
||||||
fputs("\n", pFile);
|
LoadReferenceAlignmentValues();
|
||||||
|
}
|
||||||
zwvalue = std::to_string(References[0].fastalg_x) + "\t" + std::to_string(References[0].fastalg_y);
|
|
||||||
zwvalue = zwvalue + "\t" +std::to_string(References[0].fastalg_SAD)+ "\t" +std::to_string(References[0].fastalg_min);
|
return true;
|
||||||
zwvalue = zwvalue + "\t" +std::to_string(References[0].fastalg_max)+ "\t" +std::to_string(References[0].fastalg_avg);
|
}
|
||||||
fputs(zwvalue.c_str(), pFile);
|
|
||||||
fputs("\n", pFile);
|
void ClassFlowAlignment::SaveReferenceAlignmentValues()
|
||||||
|
{
|
||||||
zwvalue = std::to_string(References[1].fastalg_x) + "\t" + std::to_string(References[1].fastalg_y);
|
FILE *pFile;
|
||||||
zwvalue = zwvalue + "\t" +std::to_string(References[1].fastalg_SAD)+ "\t" +std::to_string(References[1].fastalg_min);
|
std::string zwtime, zwvalue;
|
||||||
zwvalue = zwvalue + "\t" +std::to_string(References[1].fastalg_max)+ "\t" +std::to_string(References[1].fastalg_avg);
|
|
||||||
fputs(zwvalue.c_str(), pFile);
|
pFile = fopen(FileStoreRefAlignment.c_str(), "w");
|
||||||
fputs("\n", pFile);
|
|
||||||
|
if (strlen(zwtime.c_str()) == 0)
|
||||||
fclose(pFile);
|
{
|
||||||
}
|
time_t rawtime;
|
||||||
|
struct tm *timeinfo;
|
||||||
|
char buffer[80];
|
||||||
bool ClassFlowAlignment::LoadReferenceAlignmentValues(void)
|
|
||||||
{
|
time(&rawtime);
|
||||||
FILE* pFile;
|
timeinfo = localtime(&rawtime);
|
||||||
char zw[1024];
|
|
||||||
string zwvalue;
|
strftime(buffer, 80, "%Y-%m-%dT%H:%M:%S", timeinfo);
|
||||||
std::vector<string> splitted;
|
zwtime = std::string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
pFile = fopen(FileStoreRefAlignment.c_str(), "r");
|
fputs(zwtime.c_str(), pFile);
|
||||||
if (pFile == NULL)
|
fputs("\n", pFile);
|
||||||
return false;
|
|
||||||
|
zwvalue = std::to_string(References[0].fastalg_x) + "\t" + std::to_string(References[0].fastalg_y);
|
||||||
fgets(zw, 1024, pFile);
|
zwvalue = zwvalue + "\t" + std::to_string(References[0].fastalg_SAD) + "\t" + std::to_string(References[0].fastalg_min);
|
||||||
ESP_LOGD(TAG, "%s", zw);
|
zwvalue = zwvalue + "\t" + std::to_string(References[0].fastalg_max) + "\t" + std::to_string(References[0].fastalg_avg);
|
||||||
|
fputs(zwvalue.c_str(), pFile);
|
||||||
fgets(zw, 1024, pFile);
|
fputs("\n", pFile);
|
||||||
splitted = ZerlegeZeile(std::string(zw), " \t");
|
|
||||||
if (splitted.size() < 6)
|
zwvalue = std::to_string(References[1].fastalg_x) + "\t" + std::to_string(References[1].fastalg_y);
|
||||||
{
|
zwvalue = zwvalue + "\t" + std::to_string(References[1].fastalg_SAD) + "\t" + std::to_string(References[1].fastalg_min);
|
||||||
fclose(pFile);
|
zwvalue = zwvalue + "\t" + std::to_string(References[1].fastalg_max) + "\t" + std::to_string(References[1].fastalg_avg);
|
||||||
return false;
|
fputs(zwvalue.c_str(), pFile);
|
||||||
}
|
fputs("\n", pFile);
|
||||||
|
|
||||||
References[0].fastalg_x = stoi(splitted[0]);
|
fclose(pFile);
|
||||||
References[0].fastalg_y = stoi(splitted[1]);
|
}
|
||||||
References[0].fastalg_SAD = stof(splitted[2]);
|
|
||||||
References[0].fastalg_min = stoi(splitted[3]);
|
bool ClassFlowAlignment::LoadReferenceAlignmentValues(void)
|
||||||
References[0].fastalg_max = stoi(splitted[4]);
|
{
|
||||||
References[0].fastalg_avg = stof(splitted[5]);
|
FILE *pFile;
|
||||||
|
char zw[1024];
|
||||||
fgets(zw, 1024, pFile);
|
string zwvalue;
|
||||||
splitted = ZerlegeZeile(std::string(zw));
|
std::vector<string> splitted;
|
||||||
if (splitted.size() < 6)
|
|
||||||
{
|
pFile = fopen(FileStoreRefAlignment.c_str(), "r");
|
||||||
fclose(pFile);
|
|
||||||
return false;
|
if (pFile == NULL)
|
||||||
}
|
return false;
|
||||||
|
|
||||||
References[1].fastalg_x = stoi(splitted[0]);
|
fgets(zw, 1024, pFile);
|
||||||
References[1].fastalg_y = stoi(splitted[1]);
|
ESP_LOGD(TAG, "%s", zw);
|
||||||
References[1].fastalg_SAD = stof(splitted[2]);
|
|
||||||
References[1].fastalg_min = stoi(splitted[3]);
|
fgets(zw, 1024, pFile);
|
||||||
References[1].fastalg_max = stoi(splitted[4]);
|
splitted = ZerlegeZeile(std::string(zw), " \t");
|
||||||
References[1].fastalg_avg = stof(splitted[5]);
|
|
||||||
|
if (splitted.size() < 6)
|
||||||
fclose(pFile);
|
{
|
||||||
|
fclose(pFile);
|
||||||
|
return false;
|
||||||
/*#ifdef DEBUG_DETAIL_ON
|
}
|
||||||
std::string _zw = "\tLoadReferences[0]\tx,y:\t" + std::to_string(References[0].fastalg_x) + "\t" + std::to_string(References[0].fastalg_x);
|
|
||||||
_zw = _zw + "\tSAD, min, max, avg:\t" + std::to_string(References[0].fastalg_SAD) + "\t" + std::to_string(References[0].fastalg_min);
|
References[0].fastalg_x = stoi(splitted[0]);
|
||||||
_zw = _zw + "\t" + std::to_string(References[0].fastalg_max) + "\t" + std::to_string(References[0].fastalg_avg);
|
References[0].fastalg_y = stoi(splitted[1]);
|
||||||
LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", _zw);
|
References[0].fastalg_SAD = stof(splitted[2]);
|
||||||
_zw = "\tLoadReferences[1]\tx,y:\t" + std::to_string(References[1].fastalg_x) + "\t" + std::to_string(References[1].fastalg_x);
|
References[0].fastalg_min = stoi(splitted[3]);
|
||||||
_zw = _zw + "\tSAD, min, max, avg:\t" + std::to_string(References[1].fastalg_SAD) + "\t" + std::to_string(References[1].fastalg_min);
|
References[0].fastalg_max = stoi(splitted[4]);
|
||||||
_zw = _zw + "\t" + std::to_string(References[1].fastalg_max) + "\t" + std::to_string(References[1].fastalg_avg);
|
References[0].fastalg_avg = stof(splitted[5]);
|
||||||
LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", _zw);
|
|
||||||
#endif*/
|
fgets(zw, 1024, pFile);
|
||||||
|
splitted = ZerlegeZeile(std::string(zw));
|
||||||
return true;
|
|
||||||
}
|
if (splitted.size() < 6)
|
||||||
|
{
|
||||||
|
fclose(pFile);
|
||||||
void ClassFlowAlignment::DrawRef(CImageBasis *_zw)
|
return false;
|
||||||
{
|
}
|
||||||
if (_zw->ImageOkay())
|
|
||||||
{
|
References[1].fastalg_x = stoi(splitted[0]);
|
||||||
_zw->drawRect(References[0].target_x, References[0].target_y, References[0].width, References[0].height, 255, 0, 0, 2);
|
References[1].fastalg_y = stoi(splitted[1]);
|
||||||
_zw->drawRect(References[1].target_x, References[1].target_y, References[1].width, References[1].height, 255, 0, 0, 2);
|
References[1].fastalg_SAD = stof(splitted[2]);
|
||||||
}
|
References[1].fastalg_min = stoi(splitted[3]);
|
||||||
}
|
References[1].fastalg_max = stoi(splitted[4]);
|
||||||
|
References[1].fastalg_avg = stof(splitted[5]);
|
||||||
|
|
||||||
|
fclose(pFile);
|
||||||
|
|
||||||
|
/*#ifdef DEBUG_DETAIL_ON
|
||||||
|
std::string _zw = "\tLoadReferences[0]\tx,y:\t" + std::to_string(References[0].fastalg_x) + "\t" + std::to_string(References[0].fastalg_x);
|
||||||
|
_zw = _zw + "\tSAD, min, max, avg:\t" + std::to_string(References[0].fastalg_SAD) + "\t" + std::to_string(References[0].fastalg_min);
|
||||||
|
_zw = _zw + "\t" + std::to_string(References[0].fastalg_max) + "\t" + std::to_string(References[0].fastalg_avg);
|
||||||
|
LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", _zw);
|
||||||
|
_zw = "\tLoadReferences[1]\tx,y:\t" + std::to_string(References[1].fastalg_x) + "\t" + std::to_string(References[1].fastalg_x);
|
||||||
|
_zw = _zw + "\tSAD, min, max, avg:\t" + std::to_string(References[1].fastalg_SAD) + "\t" + std::to_string(References[1].fastalg_min);
|
||||||
|
_zw = _zw + "\t" + std::to_string(References[1].fastalg_max) + "\t" + std::to_string(References[1].fastalg_avg);
|
||||||
|
LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", _zw);
|
||||||
|
#endif*/
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClassFlowAlignment::DrawRef(CImageBasis *_zw)
|
||||||
|
{
|
||||||
|
if (_zw->ImageOkay())
|
||||||
|
{
|
||||||
|
_zw->drawRect(References[0].target_x, References[0].target_y, References[0].width, References[0].height, 255, 0, 0, 2);
|
||||||
|
_zw->drawRect(References[1].target_x, References[1].target_y, References[1].width, References[1].height, 255, 0, 0, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,54 +1,51 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef CLASSFLOWALIGNMENT_H
|
#ifndef CLASSFLOWALIGNMENT_H
|
||||||
#define CLASSFLOWALIGNMENT_H
|
#define CLASSFLOWALIGNMENT_H
|
||||||
|
|
||||||
#include "ClassFlow.h"
|
#include "ClassFlow.h"
|
||||||
#include "Helper.h"
|
#include "Helper.h"
|
||||||
#include "CAlignAndCutImage.h"
|
#include "CAlignAndCutImage.h"
|
||||||
#include "CFindTemplate.h"
|
#include "CFindTemplate.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
class ClassFlowAlignment :
|
class ClassFlowAlignment : public ClassFlow
|
||||||
public ClassFlow
|
{
|
||||||
{
|
protected:
|
||||||
protected:
|
float initialrotate;
|
||||||
float initalrotate;
|
bool initialflip;
|
||||||
bool initialmirror;
|
bool use_antialiasing;
|
||||||
bool initialflip;
|
RefInfo References[2];
|
||||||
bool use_antialiasing;
|
int anz_ref;
|
||||||
RefInfo References[2];
|
string namerawimage;
|
||||||
int anz_ref;
|
bool SaveAllFiles;
|
||||||
string namerawimage;
|
CAlignAndCutImage *AlignAndCutImage;
|
||||||
bool SaveAllFiles;
|
std::string FileStoreRefAlignment;
|
||||||
CAlignAndCutImage *AlignAndCutImage;
|
float SAD_criteria;
|
||||||
std::string FileStoreRefAlignment;
|
|
||||||
float SAD_criteria;
|
void SetInitialParameter(void);
|
||||||
|
bool LoadReferenceAlignmentValues(void);
|
||||||
void SetInitialParameter(void);
|
void SaveReferenceAlignmentValues();
|
||||||
bool LoadReferenceAlignmentValues(void);
|
|
||||||
void SaveReferenceAlignmentValues();
|
public:
|
||||||
|
CImageBasis *ImageBasis, *ImageTMP;
|
||||||
public:
|
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||||
CImageBasis *ImageBasis, *ImageTMP;
|
ImageData *AlgROI;
|
||||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
#endif
|
||||||
ImageData *AlgROI;
|
|
||||||
#endif
|
ClassFlowAlignment(std::vector<ClassFlow *> *lfc);
|
||||||
|
|
||||||
ClassFlowAlignment(std::vector<ClassFlow*>* lfc);
|
CAlignAndCutImage *GetAlignAndCutImage() { return AlignAndCutImage; };
|
||||||
|
|
||||||
CAlignAndCutImage* GetAlignAndCutImage(){return AlignAndCutImage;};
|
void DrawRef(CImageBasis *_zw);
|
||||||
|
|
||||||
void DrawRef(CImageBasis *_zw);
|
bool ReadParameter(FILE *pfile, string &aktparamgraph);
|
||||||
|
bool doFlow(string time);
|
||||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
string getHTMLSingleStep(string host);
|
||||||
bool doFlow(string time);
|
string name() { return "ClassFlowAlignment"; };
|
||||||
string getHTMLSingleStep(string host);
|
};
|
||||||
string name(){return "ClassFlowAlignment";};
|
|
||||||
};
|
#endif // CLASSFLOWALIGNMENT_H
|
||||||
|
|
||||||
|
|
||||||
#endif //CLASSFLOWALIGNMENT_H
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -25,9 +25,10 @@ extern "C" {
|
|||||||
#endif //ENABLE_MQTT
|
#endif //ENABLE_MQTT
|
||||||
|
|
||||||
#include "server_help.h"
|
#include "server_help.h"
|
||||||
|
#include "MainFlowControl.h"
|
||||||
#include "../../include/defines.h"
|
#include "../../include/defines.h"
|
||||||
|
|
||||||
static const char* TAG = "CTRL";
|
static const char* TAG = "FLOWCTRL";
|
||||||
|
|
||||||
//#define DEBUG_DETAIL_ON
|
//#define DEBUG_DETAIL_ON
|
||||||
|
|
||||||
@@ -38,8 +39,8 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
|
|||||||
|
|
||||||
ESP_LOGD(TAG, "Step %s start", _stepname.c_str());
|
ESP_LOGD(TAG, "Step %s start", _stepname.c_str());
|
||||||
|
|
||||||
if ((_stepname.compare("[MakeImage]") == 0) || (_stepname.compare(";[MakeImage]") == 0)){
|
if ((_stepname.compare("[TakeImage]") == 0) || (_stepname.compare(";[TakeImage]") == 0)){
|
||||||
_classname = "ClassFlowMakeImage";
|
_classname = "ClassFlowTakeImage";
|
||||||
}
|
}
|
||||||
if ((_stepname.compare("[Alignment]") == 0) || (_stepname.compare(";[Alignment]") == 0)){
|
if ((_stepname.compare("[Alignment]") == 0) || (_stepname.compare(";[Alignment]") == 0)){
|
||||||
_classname = "ClassFlowAlignment";
|
_classname = "ClassFlowAlignment";
|
||||||
@@ -60,11 +61,14 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
|
|||||||
if ((_stepname.compare("[InfluxDB]") == 0) || (_stepname.compare(";[InfluxDB]") == 0)){
|
if ((_stepname.compare("[InfluxDB]") == 0) || (_stepname.compare(";[InfluxDB]") == 0)){
|
||||||
_classname = "ClassFlowInfluxDB";
|
_classname = "ClassFlowInfluxDB";
|
||||||
}
|
}
|
||||||
|
if ((_stepname.compare("[InfluxDBv2]") == 0) || (_stepname.compare(";[InfluxDBv2]") == 0)){
|
||||||
|
_classname = "ClassFlowInfluxDBv2";
|
||||||
|
}
|
||||||
#endif //ENABLE_INFLUXDB
|
#endif //ENABLE_INFLUXDB
|
||||||
|
|
||||||
for (int i = 0; i < FlowControll.size(); ++i)
|
for (int i = 0; i < FlowControll.size(); ++i)
|
||||||
if (FlowControll[i]->name().compare(_classname) == 0){
|
if (FlowControll[i]->name().compare(_classname) == 0){
|
||||||
if (!(FlowControll[i]->name().compare("ClassFlowMakeImage") == 0)) // if it is a MakeImage, the image does not need to be included, this happens automatically with the html query.
|
if (!(FlowControll[i]->name().compare("ClassFlowTakeImage") == 0)) // if it is a TakeImage, the image does not need to be included, this happens automatically with the html query.
|
||||||
FlowControll[i]->doFlow("");
|
FlowControll[i]->doFlow("");
|
||||||
result = FlowControll[i]->getHTMLSingleStep(_host);
|
result = FlowControll[i]->getHTMLSingleStep(_host);
|
||||||
}
|
}
|
||||||
@@ -77,7 +81,7 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
|
|||||||
|
|
||||||
std::string ClassFlowControll::TranslateAktstatus(std::string _input)
|
std::string ClassFlowControll::TranslateAktstatus(std::string _input)
|
||||||
{
|
{
|
||||||
if (_input.compare("ClassFlowMakeImage") == 0)
|
if (_input.compare("ClassFlowTakeImage") == 0)
|
||||||
return ("Take Image");
|
return ("Take Image");
|
||||||
if (_input.compare("ClassFlowAlignment") == 0)
|
if (_input.compare("ClassFlowAlignment") == 0)
|
||||||
return ("Aligning");
|
return ("Aligning");
|
||||||
@@ -90,11 +94,11 @@ std::string ClassFlowControll::TranslateAktstatus(std::string _input)
|
|||||||
#ifdef ENABLE_INFLUXDB
|
#ifdef ENABLE_INFLUXDB
|
||||||
if (_input.compare("ClassFlowInfluxDB") == 0)
|
if (_input.compare("ClassFlowInfluxDB") == 0)
|
||||||
return ("Sending InfluxDB");
|
return ("Sending InfluxDB");
|
||||||
|
if (_input.compare("ClassFlowInfluxDBv2") == 0)
|
||||||
|
return ("Sending InfluxDBv2");
|
||||||
#endif //ENABLE_INFLUXDB
|
#endif //ENABLE_INFLUXDB
|
||||||
if (_input.compare("ClassFlowPostProcessing") == 0)
|
if (_input.compare("ClassFlowPostProcessing") == 0)
|
||||||
return ("Post-Processing");
|
return ("Post-Processing");
|
||||||
if (_input.compare("ClassFlowWriteList") == 0)
|
|
||||||
return ("Writing List");
|
|
||||||
|
|
||||||
return "Unkown Status";
|
return "Unkown Status";
|
||||||
}
|
}
|
||||||
@@ -158,20 +162,12 @@ void ClassFlowControll::AnalogDrawROI(CImageBasis *_zw)
|
|||||||
|
|
||||||
|
|
||||||
#ifdef ENABLE_MQTT
|
#ifdef ENABLE_MQTT
|
||||||
string ClassFlowControll::GetMQTTMainTopic()
|
bool ClassFlowControll::StartMQTTService()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < FlowControll.size(); ++i)
|
|
||||||
if (FlowControll[i]->name().compare("ClassFlowMQTT") == 0)
|
|
||||||
return ((ClassFlowMQTT*) (FlowControll[i]))->GetMQTTMainTopic();
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClassFlowControll::StartMQTTService() {
|
|
||||||
/* Start the MQTT service */
|
/* Start the MQTT service */
|
||||||
for (int i = 0; i < FlowControll.size(); ++i) {
|
for (int i = 0; i < FlowControll.size(); ++i) {
|
||||||
if (FlowControll[i]->name().compare("ClassFlowMQTT") == 0) {
|
if (FlowControll[i]->name().compare("ClassFlowMQTT") == 0) {
|
||||||
return ((ClassFlowMQTT*) (FlowControll[i]))->Start(AutoIntervall);
|
return ((ClassFlowMQTT*) (FlowControll[i]))->Start(AutoInterval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -183,33 +179,39 @@ void ClassFlowControll::SetInitialParameter(void)
|
|||||||
{
|
{
|
||||||
AutoStart = false;
|
AutoStart = false;
|
||||||
SetupModeActive = false;
|
SetupModeActive = false;
|
||||||
AutoIntervall = 10; // Minutes
|
AutoInterval = 10; // Minutes
|
||||||
flowdigit = NULL;
|
flowdigit = NULL;
|
||||||
flowanalog = NULL;
|
flowanalog = NULL;
|
||||||
flowpostprocessing = NULL;
|
flowpostprocessing = NULL;
|
||||||
disabled = false;
|
disabled = false;
|
||||||
aktRunNr = 0;
|
aktRunNr = 0;
|
||||||
aktstatus = "Flow task not yet created";
|
aktstatus = "Flow task not yet created";
|
||||||
|
aktstatusWithTime = aktstatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool ClassFlowControll::isAutoStart(long &_intervall)
|
bool ClassFlowControll::getIsAutoStart(void)
|
||||||
{
|
{
|
||||||
_intervall = AutoIntervall * 60 * 1000; // AutoInterval: minutes -> ms
|
|
||||||
return AutoStart;
|
return AutoStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ClassFlowControll::setAutoStartInterval(long &_interval)
|
||||||
|
{
|
||||||
|
_interval = AutoInterval * 60 * 1000; // AutoInterval: minutes -> ms
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
|
ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
|
||||||
{
|
{
|
||||||
ClassFlow* cfc = NULL;
|
ClassFlow* cfc = NULL;
|
||||||
|
|
||||||
_type = trim(_type);
|
_type = trim(_type);
|
||||||
|
|
||||||
if (toUpper(_type).compare("[MAKEIMAGE]") == 0)
|
if (toUpper(_type).compare("[TAKEIMAGE]") == 0)
|
||||||
{
|
{
|
||||||
cfc = new ClassFlowMakeImage(&FlowControll);
|
cfc = new ClassFlowTakeImage(&FlowControll);
|
||||||
flowmakeimage = (ClassFlowMakeImage*) cfc;
|
flowtakeimage = (ClassFlowTakeImage*) cfc;
|
||||||
}
|
}
|
||||||
if (toUpper(_type).compare("[ALIGNMENT]") == 0)
|
if (toUpper(_type).compare("[ALIGNMENT]") == 0)
|
||||||
{
|
{
|
||||||
@@ -233,9 +235,9 @@ ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
|
|||||||
#ifdef ENABLE_INFLUXDB
|
#ifdef ENABLE_INFLUXDB
|
||||||
if (toUpper(_type).compare("[INFLUXDB]") == 0)
|
if (toUpper(_type).compare("[INFLUXDB]") == 0)
|
||||||
cfc = new ClassFlowInfluxDB(&FlowControll);
|
cfc = new ClassFlowInfluxDB(&FlowControll);
|
||||||
|
if (toUpper(_type).compare("[INFLUXDBV2]") == 0)
|
||||||
|
cfc = new ClassFlowInfluxDBv2(&FlowControll);
|
||||||
#endif //ENABLE_INFLUXDB
|
#endif //ENABLE_INFLUXDB
|
||||||
if (toUpper(_type).compare("[WRITELIST]") == 0)
|
|
||||||
cfc = new ClassFlowWriteList(&FlowControll);
|
|
||||||
|
|
||||||
if (toUpper(_type).compare("[POSTPROCESSING]") == 0)
|
if (toUpper(_type).compare("[POSTPROCESSING]") == 0)
|
||||||
{
|
{
|
||||||
@@ -265,8 +267,10 @@ ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
|
|||||||
void ClassFlowControll::InitFlow(std::string config)
|
void ClassFlowControll::InitFlow(std::string config)
|
||||||
{
|
{
|
||||||
aktstatus = "Initialization";
|
aktstatus = "Initialization";
|
||||||
|
aktstatusWithTime = aktstatus;
|
||||||
|
|
||||||
//#ifdef ENABLE_MQTT
|
//#ifdef ENABLE_MQTT
|
||||||
//MQTTPublish(mqttServer_getMainTopic() + "/" + "status", "Initialization", false); // Right now, not possible -> MQTT Service is going to be started later
|
//MQTTPublish(mqttServer_getMainTopic() + "/" + "status", "Initialization", 1, false); // Right now, not possible -> MQTT Service is going to be started later
|
||||||
//#endif //ENABLE_MQTT
|
//#endif //ENABLE_MQTT
|
||||||
|
|
||||||
string line;
|
string line;
|
||||||
@@ -290,6 +294,7 @@ void ClassFlowControll::InitFlow(std::string config)
|
|||||||
while ((line.size() > 0) && !(feof(pFile)))
|
while ((line.size() > 0) && !(feof(pFile)))
|
||||||
{
|
{
|
||||||
cfc = CreateClassFlow(line);
|
cfc = CreateClassFlow(line);
|
||||||
|
// printf("Name: %s\n", cfc->name().c_str());
|
||||||
if (cfc)
|
if (cfc)
|
||||||
{
|
{
|
||||||
ESP_LOGD(TAG, "Start ReadParameter (%s)", line.c_str());
|
ESP_LOGD(TAG, "Start ReadParameter (%s)", line.c_str());
|
||||||
@@ -310,6 +315,12 @@ void ClassFlowControll::InitFlow(std::string config)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string* ClassFlowControll::getActStatusWithTime()
|
||||||
|
{
|
||||||
|
return &aktstatusWithTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string* ClassFlowControll::getActStatus()
|
std::string* ClassFlowControll::getActStatus()
|
||||||
{
|
{
|
||||||
return &aktstatus;
|
return &aktstatus;
|
||||||
@@ -319,21 +330,22 @@ std::string* ClassFlowControll::getActStatus()
|
|||||||
void ClassFlowControll::setActStatus(std::string _aktstatus)
|
void ClassFlowControll::setActStatus(std::string _aktstatus)
|
||||||
{
|
{
|
||||||
aktstatus = _aktstatus;
|
aktstatus = _aktstatus;
|
||||||
|
aktstatusWithTime = aktstatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ClassFlowControll::doFlowMakeImageOnly(string time)
|
void ClassFlowControll::doFlowTakeImageOnly(string time)
|
||||||
{
|
{
|
||||||
std::string zw_time;
|
std::string zw_time;
|
||||||
|
|
||||||
for (int i = 0; i < FlowControll.size(); ++i)
|
for (int i = 0; i < FlowControll.size(); ++i)
|
||||||
{
|
{
|
||||||
if (FlowControll[i]->name() == "ClassFlowMakeImage") {
|
if (FlowControll[i]->name() == "ClassFlowTakeImage") {
|
||||||
zw_time = getCurrentTimeString("%H:%M:%S");
|
zw_time = getCurrentTimeString("%H:%M:%S");
|
||||||
std::string flowStatus = TranslateAktstatus(FlowControll[i]->name());
|
aktstatus = TranslateAktstatus(FlowControll[i]->name());
|
||||||
aktstatus = flowStatus + " (" + zw_time + ")";
|
aktstatusWithTime = aktstatus + " (" + zw_time + ")";
|
||||||
#ifdef ENABLE_MQTT
|
#ifdef ENABLE_MQTT
|
||||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", flowStatus, false);
|
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, 1, false);
|
||||||
#endif //ENABLE_MQTT
|
#endif //ENABLE_MQTT
|
||||||
|
|
||||||
FlowControll[i]->doFlow(time);
|
FlowControll[i]->doFlow(time);
|
||||||
@@ -347,6 +359,7 @@ bool ClassFlowControll::doFlow(string time)
|
|||||||
bool result = true;
|
bool result = true;
|
||||||
std::string zw_time;
|
std::string zw_time;
|
||||||
int repeat = 0;
|
int repeat = 0;
|
||||||
|
int qos = 1;
|
||||||
|
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
LogFile.WriteHeapInfo("ClassFlowControll::doFlow - Start");
|
LogFile.WriteHeapInfo("ClassFlowControll::doFlow - Start");
|
||||||
@@ -363,11 +376,11 @@ bool ClassFlowControll::doFlow(string time)
|
|||||||
for (int i = 0; i < FlowControll.size(); ++i)
|
for (int i = 0; i < FlowControll.size(); ++i)
|
||||||
{
|
{
|
||||||
zw_time = getCurrentTimeString("%H:%M:%S");
|
zw_time = getCurrentTimeString("%H:%M:%S");
|
||||||
std::string flowStatus = TranslateAktstatus(FlowControll[i]->name());
|
aktstatus = TranslateAktstatus(FlowControll[i]->name());
|
||||||
aktstatus = flowStatus + " (" + zw_time + ")";
|
aktstatusWithTime = aktstatus + " (" + zw_time + ")";
|
||||||
//LogFile.WriteToFile(ESP_LOG_INFO, TAG, aktstatus);
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Status: " + aktstatusWithTime);
|
||||||
#ifdef ENABLE_MQTT
|
#ifdef ENABLE_MQTT
|
||||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", flowStatus, false);
|
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, qos, false);
|
||||||
#endif //ENABLE_MQTT
|
#endif //ENABLE_MQTT
|
||||||
|
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
@@ -397,11 +410,11 @@ bool ClassFlowControll::doFlow(string time)
|
|||||||
}
|
}
|
||||||
|
|
||||||
zw_time = getCurrentTimeString("%H:%M:%S");
|
zw_time = getCurrentTimeString("%H:%M:%S");
|
||||||
std::string flowStatus = "Flow finished";
|
aktstatus = "Flow finished";
|
||||||
aktstatus = flowStatus + " (" + zw_time + ")";
|
aktstatusWithTime = aktstatus + " (" + zw_time + ")";
|
||||||
//LogFile.WriteToFile(ESP_LOG_INFO, TAG, aktstatus);
|
//LogFile.WriteToFile(ESP_LOG_INFO, TAG, aktstatusWithTime);
|
||||||
#ifdef ENABLE_MQTT
|
#ifdef ENABLE_MQTT
|
||||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", flowStatus, false);
|
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, qos, false);
|
||||||
#endif //ENABLE_MQTT
|
#endif //ENABLE_MQTT
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -450,10 +463,10 @@ string ClassFlowControll::getReadoutAll(int _type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string ClassFlowControll::getReadout(bool _rawvalue = false, bool _noerror = false)
|
string ClassFlowControll::getReadout(bool _rawvalue = false, bool _noerror = false, int _number = 0)
|
||||||
{
|
{
|
||||||
if (flowpostprocessing)
|
if (flowpostprocessing)
|
||||||
return flowpostprocessing->getReadoutParam(_rawvalue, _noerror);
|
return flowpostprocessing->getReadoutParam(_rawvalue, _noerror, _number);
|
||||||
|
|
||||||
string zw = "";
|
string zw = "";
|
||||||
string result = "";
|
string result = "";
|
||||||
@@ -481,37 +494,40 @@ string ClassFlowControll::GetPrevalue(std::string _number)
|
|||||||
return flowpostprocessing->GetPreValue(_number);
|
return flowpostprocessing->GetPreValue(_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return std::string("");
|
return std::string("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string ClassFlowControll::UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern)
|
bool ClassFlowControll::UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern)
|
||||||
{
|
{
|
||||||
float zw;
|
double newvalueAsDouble;
|
||||||
char* p;
|
char* p;
|
||||||
|
|
||||||
_newvalue = trim(_newvalue);
|
_newvalue = trim(_newvalue);
|
||||||
// ESP_LOGD(TAG, "Input UpdatePreValue: %s", _newvalue.c_str());
|
//ESP_LOGD(TAG, "Input UpdatePreValue: %s", _newvalue.c_str());
|
||||||
|
|
||||||
if (_newvalue.compare("0.0") == 0)
|
if (_newvalue.substr(0,8).compare("0.000000") == 0 || _newvalue.compare("0.0") == 0 || _newvalue.compare("0") == 0) {
|
||||||
{
|
newvalueAsDouble = 0; // preset to value = 0
|
||||||
zw = 0;
|
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
newvalueAsDouble = strtod(_newvalue.c_str(), &p);
|
||||||
zw = strtof(_newvalue.c_str(), &p);
|
if (newvalueAsDouble == 0) {
|
||||||
if (zw == 0)
|
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "UpdatePrevalue: No valid value for processing: " + _newvalue);
|
||||||
return "- Error in String to Value Conversion!!! Must be of format value=123.456";
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (flowpostprocessing) {
|
||||||
if (flowpostprocessing)
|
if (flowpostprocessing->SetPreValue(newvalueAsDouble, _numbers, _extern))
|
||||||
{
|
return true;
|
||||||
flowpostprocessing->SetPreValue(zw, _numbers, _extern);
|
else
|
||||||
return _newvalue;
|
return false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "UpdatePrevalue: ERROR - Class Post-Processing not initialized");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::string();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -527,7 +543,7 @@ bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
|
|
||||||
|
|
||||||
if ((toUpper(aktparamgraph).compare("[AUTOTIMER]") != 0) && (toUpper(aktparamgraph).compare("[DEBUG]") != 0) &&
|
if ((toUpper(aktparamgraph).compare("[AUTOTIMER]") != 0) && (toUpper(aktparamgraph).compare("[DEBUG]") != 0) &&
|
||||||
(toUpper(aktparamgraph).compare("[SYSTEM]") != 0 && (toUpper(aktparamgraph).compare("[DATALOGGING]") != 0))) // Paragraph passt nicht zu MakeImage
|
(toUpper(aktparamgraph).compare("[SYSTEM]") != 0 && (toUpper(aktparamgraph).compare("[DATALOGGING]") != 0))) // Paragraph passt nicht zu Debug oder DataLogging
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||||
@@ -541,9 +557,9 @@ bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((toUpper(splitted[0]) == "INTERVALL") && (splitted.size() > 1))
|
if ((toUpper(splitted[0]) == "INTERVAL") && (splitted.size() > 1))
|
||||||
{
|
{
|
||||||
AutoIntervall = std::stof(splitted[1]);
|
AutoInterval = std::stof(splitted[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((toUpper(splitted[0]) == "DATALOGACTIVE") && (splitted.size() > 1))
|
if ((toUpper(splitted[0]) == "DATALOGACTIVE") && (splitted.size() > 1))
|
||||||
@@ -557,12 +573,12 @@ bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((toUpper(splitted[0]) == "DATALOGRETENTIONINDAYS") && (splitted.size() > 1))
|
if ((toUpper(splitted[0]) == "DATAFILESRETENTION") && (splitted.size() > 1))
|
||||||
{
|
{
|
||||||
LogFile.SetDataLogRetention(std::stoi(splitted[1]));
|
LogFile.SetDataLogRetention(std::stoi(splitted[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((toUpper(splitted[0]) == "LOGFILE") && (splitted.size() > 1))
|
if ((toUpper(splitted[0]) == "LOGLEVEL") && (splitted.size() > 1))
|
||||||
{
|
{
|
||||||
/* matches esp_log_level_t */
|
/* matches esp_log_level_t */
|
||||||
if ((toUpper(splitted[1]) == "TRUE") || (toUpper(splitted[1]) == "2"))
|
if ((toUpper(splitted[1]) == "TRUE") || (toUpper(splitted[1]) == "2"))
|
||||||
@@ -581,26 +597,33 @@ bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
{
|
{
|
||||||
LogFile.setLogLevel(ESP_LOG_DEBUG);
|
LogFile.setLogLevel(ESP_LOG_DEBUG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If system reboot was not triggered by user and reboot was caused by execption -> keep log level to DEBUG */
|
||||||
|
if (!getIsPlannedReboot() && (esp_reset_reason() == ESP_RST_PANIC))
|
||||||
|
LogFile.setLogLevel(ESP_LOG_DEBUG);
|
||||||
}
|
}
|
||||||
if ((toUpper(splitted[0]) == "LOGFILERETENTIONINDAYS") && (splitted.size() > 1))
|
if ((toUpper(splitted[0]) == "LOGFILESRETENTION") && (splitted.size() > 1))
|
||||||
{
|
{
|
||||||
LogFile.SetLogFileRetention(std::stoi(splitted[1]));
|
LogFile.SetLogFileRetention(std::stoi(splitted[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TimeServer and TimeZone got already read from the config, see setupTime () */
|
/* TimeServer and TimeZone got already read from the config, see setupTime () */
|
||||||
|
|
||||||
if ((toUpper(splitted[0]) == "RSSITHREASHOLD") && (splitted.size() > 1))
|
#if (defined WLAN_USE_ROAMING_BY_SCANNING || (defined WLAN_USE_MESH_ROAMING && defined WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES))
|
||||||
|
if ((toUpper(splitted[0]) == "RSSITHRESHOLD") && (splitted.size() > 1))
|
||||||
{
|
{
|
||||||
if (ChangeRSSIThreashold(WLAN_CONFIG_FILE, atoi(splitted[1].c_str())))
|
int RSSIThresholdTMP = atoi(splitted[1].c_str());
|
||||||
|
RSSIThresholdTMP = min(0, max(-100, RSSIThresholdTMP)); // Verify input limits (-100 - 0)
|
||||||
|
|
||||||
|
if (ChangeRSSIThreshold(WLAN_CONFIG_FILE, RSSIThresholdTMP))
|
||||||
{
|
{
|
||||||
// reboot necessary so that the new wlan.ini is also used !!!
|
// reboot necessary so that the new wlan.ini is also used !!!
|
||||||
fclose(pfile);
|
fclose(pfile);
|
||||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Rebooting to activate new RSSITHREASHOLD ...");
|
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Rebooting to activate new RSSITHRESHOLD ...");
|
||||||
esp_restart();
|
|
||||||
hard_restart();
|
|
||||||
doReboot();
|
doReboot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if ((toUpper(splitted[0]) == "HOSTNAME") && (splitted.size() > 1))
|
if ((toUpper(splitted[0]) == "HOSTNAME") && (splitted.size() > 1))
|
||||||
{
|
{
|
||||||
@@ -608,9 +631,7 @@ bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
{
|
{
|
||||||
// reboot necessary so that the new wlan.ini is also used !!!
|
// reboot necessary so that the new wlan.ini is also used !!!
|
||||||
fclose(pfile);
|
fclose(pfile);
|
||||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Rebooting to activate new HOSTNAME...");
|
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Rebooting to activate new HOSTNAME...");
|
||||||
esp_restart();
|
|
||||||
hard_restart();
|
|
||||||
doReboot();
|
doReboot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -660,7 +681,7 @@ int ClassFlowControll::CleanTempFolder() {
|
|||||||
|
|
||||||
esp_err_t ClassFlowControll::SendRawJPG(httpd_req_t *req)
|
esp_err_t ClassFlowControll::SendRawJPG(httpd_req_t *req)
|
||||||
{
|
{
|
||||||
return flowmakeimage != NULL ? flowmakeimage->SendRawJPG(req) : ESP_FAIL;
|
return flowtakeimage != NULL ? flowtakeimage->SendRawJPG(req) : ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -712,7 +733,7 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
|||||||
|
|
||||||
httpd_resp_set_type(req, "image/jpeg");
|
httpd_resp_set_type(req, "image/jpeg");
|
||||||
result = httpd_resp_send(req, (const char *)fileBuffer, fileSize);
|
result = httpd_resp_send(req, (const char *)fileBuffer, fileSize);
|
||||||
delete fileBuffer;
|
free(fileBuffer);
|
||||||
}
|
}
|
||||||
else if (aktstatus.find("Initialization") != -1) {
|
else if (aktstatus.find("Initialization") != -1) {
|
||||||
FILE* file = fopen("/sdcard/html/Flowstate_initialization.jpg", "rb");
|
FILE* file = fopen("/sdcard/html/Flowstate_initialization.jpg", "rb");
|
||||||
@@ -739,7 +760,7 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
|||||||
|
|
||||||
httpd_resp_set_type(req, "image/jpeg");
|
httpd_resp_set_type(req, "image/jpeg");
|
||||||
result = httpd_resp_send(req, (const char *)fileBuffer, fileSize);
|
result = httpd_resp_send(req, (const char *)fileBuffer, fileSize);
|
||||||
delete fileBuffer;
|
free(fileBuffer);
|
||||||
}
|
}
|
||||||
else if (aktstatus.find("Take Image") != -1) {
|
else if (aktstatus.find("Take Image") != -1) {
|
||||||
if (flowalignment && flowalignment->AlgROI) {
|
if (flowalignment && flowalignment->AlgROI) {
|
||||||
@@ -801,7 +822,7 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
|||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
_send = new CImageBasis(flowalignment->ImageBasis);
|
_send = new CImageBasis("alg_roi", flowalignment->ImageBasis);
|
||||||
|
|
||||||
if (_send->ImageOkay()) {
|
if (_send->ImageOkay()) {
|
||||||
if (flowalignment) flowalignment->DrawRef(_send);
|
if (flowalignment) flowalignment->DrawRef(_send);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "ClassFlow.h"
|
#include "ClassFlow.h"
|
||||||
#include "ClassFlowMakeImage.h"
|
#include "ClassFlowTakeImage.h"
|
||||||
#include "ClassFlowAlignment.h"
|
#include "ClassFlowAlignment.h"
|
||||||
#include "ClassFlowCNNGeneral.h"
|
#include "ClassFlowCNNGeneral.h"
|
||||||
#include "ClassFlowPostProcessing.h"
|
#include "ClassFlowPostProcessing.h"
|
||||||
@@ -15,9 +15,9 @@
|
|||||||
#endif //ENABLE_MQTT
|
#endif //ENABLE_MQTT
|
||||||
#ifdef ENABLE_INFLUXDB
|
#ifdef ENABLE_INFLUXDB
|
||||||
#include "ClassFlowInfluxDB.h"
|
#include "ClassFlowInfluxDB.h"
|
||||||
|
#include "ClassFlowInfluxDBv2.h"
|
||||||
#endif //ENABLE_INFLUXDB
|
#endif //ENABLE_INFLUXDB
|
||||||
#include "ClassFlowCNNGeneral.h"
|
#include "ClassFlowCNNGeneral.h"
|
||||||
#include "ClassFlowWriteList.h"
|
|
||||||
|
|
||||||
class ClassFlowControll :
|
class ClassFlowControll :
|
||||||
public ClassFlow
|
public ClassFlow
|
||||||
@@ -29,24 +29,26 @@ protected:
|
|||||||
ClassFlowCNNGeneral* flowanalog;
|
ClassFlowCNNGeneral* flowanalog;
|
||||||
ClassFlowCNNGeneral* flowdigit;
|
ClassFlowCNNGeneral* flowdigit;
|
||||||
// ClassFlowDigit* flowdigit;
|
// ClassFlowDigit* flowdigit;
|
||||||
ClassFlowMakeImage* flowmakeimage;
|
ClassFlowTakeImage* flowtakeimage;
|
||||||
ClassFlow* CreateClassFlow(std::string _type);
|
ClassFlow* CreateClassFlow(std::string _type);
|
||||||
|
|
||||||
bool AutoStart;
|
bool AutoStart;
|
||||||
float AutoIntervall;
|
float AutoInterval;
|
||||||
bool SetupModeActive;
|
|
||||||
void SetInitialParameter(void);
|
void SetInitialParameter(void);
|
||||||
|
std::string aktstatusWithTime;
|
||||||
std::string aktstatus;
|
std::string aktstatus;
|
||||||
int aktRunNr;
|
int aktRunNr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
bool SetupModeActive;
|
||||||
|
|
||||||
void InitFlow(std::string config);
|
void InitFlow(std::string config);
|
||||||
bool doFlow(string time);
|
bool doFlow(string time);
|
||||||
void doFlowMakeImageOnly(string time);
|
void doFlowTakeImageOnly(string time);
|
||||||
bool getStatusSetupModus(){return SetupModeActive;};
|
bool getStatusSetupModus(){return SetupModeActive;};
|
||||||
string getReadout(bool _rawvalue, bool _noerror);
|
string getReadout(bool _rawvalue, bool _noerror, int _number);
|
||||||
string getReadoutAll(int _type);
|
string getReadoutAll(int _type);
|
||||||
string UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern);
|
bool UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern);
|
||||||
string GetPrevalue(std::string _number = "");
|
string GetPrevalue(std::string _number = "");
|
||||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||||
string getJSON();
|
string getJSON();
|
||||||
@@ -54,10 +56,6 @@ public:
|
|||||||
|
|
||||||
string TranslateAktstatus(std::string _input);
|
string TranslateAktstatus(std::string _input);
|
||||||
|
|
||||||
#ifdef ENABLE_MQTT
|
|
||||||
string GetMQTTMainTopic();
|
|
||||||
#endif //ENABLE_MQTT
|
|
||||||
|
|
||||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||||
void DigitalDrawROI(CImageBasis *_zw);
|
void DigitalDrawROI(CImageBasis *_zw);
|
||||||
void AnalogDrawROI(CImageBasis *_zw);
|
void AnalogDrawROI(CImageBasis *_zw);
|
||||||
@@ -68,8 +66,10 @@ public:
|
|||||||
|
|
||||||
std::string doSingleStep(std::string _stepname, std::string _host);
|
std::string doSingleStep(std::string _stepname, std::string _host);
|
||||||
|
|
||||||
bool isAutoStart(long &_intervall);
|
bool getIsAutoStart();
|
||||||
|
void setAutoStartInterval(long &_interval);
|
||||||
|
|
||||||
|
std::string* getActStatusWithTime();
|
||||||
std::string* getActStatus();
|
std::string* getActStatus();
|
||||||
void setActStatus(std::string _aktstatus);
|
void setActStatus(std::string _aktstatus);
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ struct NumberPost {
|
|||||||
bool AllowNegativeRates;
|
bool AllowNegativeRates;
|
||||||
bool checkDigitIncreaseConsistency;
|
bool checkDigitIncreaseConsistency;
|
||||||
time_t lastvalue;
|
time_t lastvalue;
|
||||||
|
time_t timeStampTimeUTC;
|
||||||
string timeStamp;
|
string timeStamp;
|
||||||
double FlowRateAct; // m3 / min
|
double FlowRateAct; // m3 / min
|
||||||
double PreValue; // last value that was read out well
|
double PreValue; // last value that was read out well
|
||||||
@@ -51,6 +52,12 @@ struct NumberPost {
|
|||||||
float AnalogDigitalTransitionStart; // When is the digit > x.1, i.e. when does it start to tilt?
|
float AnalogDigitalTransitionStart; // When is the digit > x.1, i.e. when does it start to tilt?
|
||||||
int Nachkomma;
|
int Nachkomma;
|
||||||
|
|
||||||
|
string FieldV1; // Fieldname in InfluxDBv1
|
||||||
|
string MeasurementV1; // Measurement in InfluxDBv1
|
||||||
|
|
||||||
|
string FieldV2; // Fieldname in InfluxDBv2
|
||||||
|
string MeasurementV2; // Measurement in InfluxDBv2
|
||||||
|
|
||||||
bool isExtendedResolution;
|
bool isExtendedResolution;
|
||||||
|
|
||||||
general *digit_roi;
|
general *digit_roi;
|
||||||
|
|||||||
@@ -17,14 +17,15 @@ extern "C" {
|
|||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "../../include/defines.h"
|
#include "../../include/defines.h"
|
||||||
|
|
||||||
static const char* TAG = "IMG";
|
static const char* TAG = "FLOWIMAGE";
|
||||||
|
|
||||||
|
|
||||||
ClassFlowImage::ClassFlowImage(const char* logTag)
|
ClassFlowImage::ClassFlowImage(const char* logTag)
|
||||||
{
|
{
|
||||||
this->logTag = logTag;
|
this->logTag = logTag;
|
||||||
isLogImage = false;
|
isLogImage = false;
|
||||||
disabled = false;
|
disabled = false;
|
||||||
this->logfileRetentionInDays = 5;
|
this->imagesRetention = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassFlowImage::ClassFlowImage(std::vector<ClassFlow*> * lfc, const char* logTag) : ClassFlow(lfc)
|
ClassFlowImage::ClassFlowImage(std::vector<ClassFlow*> * lfc, const char* logTag) : ClassFlow(lfc)
|
||||||
@@ -32,7 +33,7 @@ ClassFlowImage::ClassFlowImage(std::vector<ClassFlow*> * lfc, const char* logTag
|
|||||||
this->logTag = logTag;
|
this->logTag = logTag;
|
||||||
isLogImage = false;
|
isLogImage = false;
|
||||||
disabled = false;
|
disabled = false;
|
||||||
this->logfileRetentionInDays = 5;
|
this->imagesRetention = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassFlowImage::ClassFlowImage(std::vector<ClassFlow*> * lfc, ClassFlow *_prev, const char* logTag) : ClassFlow(lfc, _prev)
|
ClassFlowImage::ClassFlowImage(std::vector<ClassFlow*> * lfc, ClassFlow *_prev, const char* logTag) : ClassFlow(lfc, _prev)
|
||||||
@@ -40,7 +41,7 @@ ClassFlowImage::ClassFlowImage(std::vector<ClassFlow*> * lfc, ClassFlow *_prev,
|
|||||||
this->logTag = logTag;
|
this->logTag = logTag;
|
||||||
isLogImage = false;
|
isLogImage = false;
|
||||||
disabled = false;
|
disabled = false;
|
||||||
this->logfileRetentionInDays = 5;
|
this->imagesRetention = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -48,7 +49,7 @@ string ClassFlowImage::CreateLogFolder(string time) {
|
|||||||
if (!isLogImage)
|
if (!isLogImage)
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
string logPath = LogImageLocation + "/" + time.LOGFILE_TIME_FORMAT_DATE_EXTR + "/" + time.LOGFILE_TIME_FORMAT_HOUR_EXTR;
|
string logPath = imagesLocation + "/" + time.LOGFILE_TIME_FORMAT_DATE_EXTR + "/" + time.LOGFILE_TIME_FORMAT_HOUR_EXTR;
|
||||||
isLogImage = mkdir_r(logPath.c_str(), S_IRWXU) == 0;
|
isLogImage = mkdir_r(logPath.c_str(), S_IRWXU) == 0;
|
||||||
if (!isLogImage) {
|
if (!isLogImage) {
|
||||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't create log folder for analog images. Path " + logPath);
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't create log folder for analog images. Path " + logPath);
|
||||||
@@ -95,7 +96,7 @@ void ClassFlowImage::RemoveOldLogs()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
ESP_LOGD(TAG, "remove old images");
|
ESP_LOGD(TAG, "remove old images");
|
||||||
if (logfileRetentionInDays == 0) {
|
if (imagesRetention == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,17 +105,17 @@ void ClassFlowImage::RemoveOldLogs()
|
|||||||
char cmpfilename[30];
|
char cmpfilename[30];
|
||||||
|
|
||||||
time(&rawtime);
|
time(&rawtime);
|
||||||
rawtime = addDays(rawtime, -logfileRetentionInDays + 1);
|
rawtime = addDays(rawtime, -1 * imagesRetention + 1);
|
||||||
timeinfo = localtime(&rawtime);
|
timeinfo = localtime(&rawtime);
|
||||||
//ESP_LOGD(TAG, "ImagefileRetentionInDays: %d", logfileRetentionInDays);
|
//ESP_LOGD(TAG, "ImagefileRetentionInDays: %d", imagesRetention);
|
||||||
|
|
||||||
strftime(cmpfilename, 30, LOGFILE_TIME_FORMAT, timeinfo);
|
strftime(cmpfilename, 30, LOGFILE_TIME_FORMAT, timeinfo);
|
||||||
//ESP_LOGD(TAG, "file name to compare: %s", cmpfilename);
|
//ESP_LOGD(TAG, "file name to compare: %s", cmpfilename);
|
||||||
string folderName = string(cmpfilename).LOGFILE_TIME_FORMAT_DATE_EXTR;
|
string folderName = string(cmpfilename).LOGFILE_TIME_FORMAT_DATE_EXTR;
|
||||||
|
|
||||||
DIR *dir = opendir(LogImageLocation.c_str());
|
DIR *dir = opendir(imagesLocation.c_str());
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
ESP_LOGE(TAG, "Failed to stat dir: %s", LogImageLocation.c_str());
|
ESP_LOGE(TAG, "Failed to stat dir: %s", imagesLocation.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +123,7 @@ void ClassFlowImage::RemoveOldLogs()
|
|||||||
int deleted = 0;
|
int deleted = 0;
|
||||||
int notDeleted = 0;
|
int notDeleted = 0;
|
||||||
while ((entry = readdir(dir)) != NULL) {
|
while ((entry = readdir(dir)) != NULL) {
|
||||||
string folderPath = LogImageLocation + "/" + entry->d_name;
|
string folderPath = imagesLocation + "/" + entry->d_name;
|
||||||
if (entry->d_type == DT_DIR) {
|
if (entry->d_type == DT_DIR) {
|
||||||
//ESP_LOGD(TAG, "Compare %s to %s", entry->d_name, folderName.c_str());
|
//ESP_LOGD(TAG, "Compare %s to %s", entry->d_name, folderName.c_str());
|
||||||
if ((strlen(entry->d_name) == folderName.length()) && (strcmp(entry->d_name, folderName.c_str()) < 0)) {
|
if ((strlen(entry->d_name) == folderName.length()) && (strcmp(entry->d_name, folderName.c_str()) < 0)) {
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ using namespace std;
|
|||||||
class ClassFlowImage : public ClassFlow
|
class ClassFlowImage : public ClassFlow
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
string LogImageLocation;
|
string imagesLocation;
|
||||||
bool isLogImage;
|
bool isLogImage;
|
||||||
unsigned short logfileRetentionInDays;
|
unsigned short imagesRetention;
|
||||||
const char* logTag;
|
const char* logTag;
|
||||||
|
|
||||||
string CreateLogFolder(string time);
|
string CreateLogFolder(string time);
|
||||||
|
|||||||
@@ -15,13 +15,12 @@
|
|||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
static const char* TAG = "class_flow_influxDb";
|
static const char* TAG = "INFLUXDB";
|
||||||
|
|
||||||
void ClassFlowInfluxDB::SetInitialParameter(void)
|
void ClassFlowInfluxDB::SetInitialParameter(void)
|
||||||
{
|
{
|
||||||
uri = "";
|
uri = "";
|
||||||
database = "";
|
database = "";
|
||||||
measurement = "";
|
|
||||||
|
|
||||||
OldValue = "";
|
OldValue = "";
|
||||||
flowpostprocessing = NULL;
|
flowpostprocessing = NULL;
|
||||||
@@ -86,33 +85,39 @@ bool ClassFlowInfluxDB::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
{
|
{
|
||||||
ESP_LOGD(TAG, "while loop reading line: %s", aktparamgraph.c_str());
|
ESP_LOGD(TAG, "while loop reading line: %s", aktparamgraph.c_str());
|
||||||
splitted = ZerlegeZeile(aktparamgraph);
|
splitted = ZerlegeZeile(aktparamgraph);
|
||||||
if ((toUpper(splitted[0]) == "USER") && (splitted.size() > 1))
|
std::string _param = GetParameterName(splitted[0]);
|
||||||
|
|
||||||
|
if ((toUpper(_param) == "USER") && (splitted.size() > 1))
|
||||||
{
|
{
|
||||||
this->user = splitted[1];
|
this->user = splitted[1];
|
||||||
}
|
}
|
||||||
if ((toUpper(splitted[0]) == "PASSWORD") && (splitted.size() > 1))
|
if ((toUpper(_param) == "PASSWORD") && (splitted.size() > 1))
|
||||||
{
|
{
|
||||||
this->password = splitted[1];
|
this->password = splitted[1];
|
||||||
}
|
}
|
||||||
if ((toUpper(splitted[0]) == "URI") && (splitted.size() > 1))
|
if ((toUpper(_param) == "URI") && (splitted.size() > 1))
|
||||||
{
|
{
|
||||||
this->uri = splitted[1];
|
this->uri = splitted[1];
|
||||||
}
|
}
|
||||||
if (((toUpper(splitted[0]) == "MEASUREMENT")) && (splitted.size() > 1))
|
if (((toUpper(_param) == "DATABASE")) && (splitted.size() > 1))
|
||||||
{
|
|
||||||
this->measurement = splitted[1];
|
|
||||||
}
|
|
||||||
if (((toUpper(splitted[0]) == "DATABASE")) && (splitted.size() > 1))
|
|
||||||
{
|
{
|
||||||
this->database = splitted[1];
|
this->database = splitted[1];
|
||||||
}
|
}
|
||||||
|
if (((toUpper(_param) == "MEASUREMENT")) && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
handleMeasurement(splitted[0], splitted[1]);
|
||||||
|
}
|
||||||
|
if (((toUpper(_param) == "FIELD")) && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
handleFieldname(splitted[0], splitted[1]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((uri.length() > 0) && (database.length() > 0) && (measurement.length() > 0))
|
if ((uri.length() > 0) && (database.length() > 0))
|
||||||
{
|
{
|
||||||
// ESP_LOGD(TAG, "Init InfluxDB with uri: %s, measurement: %s, user: %s, password: %s", uri.c_str(), measurement.c_str(), user.c_str(), password.c_str());
|
// ESP_LOGD(TAG, "Init InfluxDB with uri: %s, measurement: %s, user: %s, password: %s", uri.c_str(), measurement.c_str(), user.c_str(), password.c_str());
|
||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init InfluxDB with uri: " + uri + ", measurement: " + measurement + ", user: " + user + ", password: " + password);
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init InfluxDB with uri: " + uri + ", user: " + user + ", password: " + password);
|
||||||
InfluxDBInit(uri, database, measurement, user, password);
|
InfluxDBInit(uri, database, user, password);
|
||||||
InfluxDBenable = true;
|
InfluxDBenable = true;
|
||||||
} else {
|
} else {
|
||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDB init skipped as we are missing some parameters");
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDB init skipped as we are missing some parameters");
|
||||||
@@ -121,23 +126,18 @@ bool ClassFlowInfluxDB::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string ClassFlowInfluxDB::GetInfluxDBMeasurement()
|
|
||||||
{
|
|
||||||
return measurement;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool ClassFlowInfluxDB::doFlow(string zwtime)
|
bool ClassFlowInfluxDB::doFlow(string zwtime)
|
||||||
{
|
{
|
||||||
if (!InfluxDBenable)
|
if (!InfluxDBenable)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
std::string result;
|
std::string result;
|
||||||
|
std::string measurement;
|
||||||
std::string resulterror = "";
|
std::string resulterror = "";
|
||||||
std::string resultraw = "";
|
std::string resultraw = "";
|
||||||
std::string resultrate = "";
|
std::string resultrate = "";
|
||||||
std::string resulttimestamp = "";
|
std::string resulttimestamp = "";
|
||||||
|
long int timeutc;
|
||||||
string zw = "";
|
string zw = "";
|
||||||
string namenumber = "";
|
string namenumber = "";
|
||||||
|
|
||||||
@@ -147,20 +147,29 @@ bool ClassFlowInfluxDB::doFlow(string zwtime)
|
|||||||
|
|
||||||
for (int i = 0; i < (*NUMBERS).size(); ++i)
|
for (int i = 0; i < (*NUMBERS).size(); ++i)
|
||||||
{
|
{
|
||||||
|
measurement = (*NUMBERS)[i]->MeasurementV1;
|
||||||
result = (*NUMBERS)[i]->ReturnValue;
|
result = (*NUMBERS)[i]->ReturnValue;
|
||||||
resultraw = (*NUMBERS)[i]->ReturnRawValue;
|
resultraw = (*NUMBERS)[i]->ReturnRawValue;
|
||||||
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
||||||
resultrate = (*NUMBERS)[i]->ReturnRateValue;
|
resultrate = (*NUMBERS)[i]->ReturnRateValue;
|
||||||
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
||||||
|
timeutc = (*NUMBERS)[i]->timeStampTimeUTC;
|
||||||
|
|
||||||
namenumber = (*NUMBERS)[i]->name;
|
if ((*NUMBERS)[i]->FieldV1.length() > 0)
|
||||||
if (namenumber == "default")
|
{
|
||||||
namenumber = "value";
|
namenumber = (*NUMBERS)[i]->FieldV1;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
namenumber = namenumber + "/value";
|
{
|
||||||
|
namenumber = (*NUMBERS)[i]->name;
|
||||||
|
if (namenumber == "default")
|
||||||
|
namenumber = "value";
|
||||||
|
else
|
||||||
|
namenumber = namenumber + "/value";
|
||||||
|
}
|
||||||
|
|
||||||
if (result.length() > 0 && resulttimestamp.length() > 0)
|
if (result.length() > 0)
|
||||||
InfluxDBPublish(namenumber, result, resulttimestamp);
|
InfluxDBPublish(measurement, namenumber, result, timeutc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,4 +178,50 @@ bool ClassFlowInfluxDB::doFlow(string zwtime)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClassFlowInfluxDB::handleMeasurement(string _decsep, string _value)
|
||||||
|
{
|
||||||
|
string _digit, _decpos;
|
||||||
|
int _pospunkt = _decsep.find_first_of(".");
|
||||||
|
// ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt);
|
||||||
|
if (_pospunkt > -1)
|
||||||
|
_digit = _decsep.substr(0, _pospunkt);
|
||||||
|
else
|
||||||
|
_digit = "default";
|
||||||
|
for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j)
|
||||||
|
{
|
||||||
|
if (_digit == "default") // Set to default first (if nothing else is set)
|
||||||
|
{
|
||||||
|
flowpostprocessing->NUMBERS[j]->MeasurementV1 = _value;
|
||||||
|
}
|
||||||
|
if (flowpostprocessing->NUMBERS[j]->name == _digit)
|
||||||
|
{
|
||||||
|
flowpostprocessing->NUMBERS[j]->MeasurementV1 = _value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ClassFlowInfluxDB::handleFieldname(string _decsep, string _value)
|
||||||
|
{
|
||||||
|
string _digit, _decpos;
|
||||||
|
int _pospunkt = _decsep.find_first_of(".");
|
||||||
|
// ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt);
|
||||||
|
if (_pospunkt > -1)
|
||||||
|
_digit = _decsep.substr(0, _pospunkt);
|
||||||
|
else
|
||||||
|
_digit = "default";
|
||||||
|
for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j)
|
||||||
|
{
|
||||||
|
if (_digit == "default") // Set to default first (if nothing else is set)
|
||||||
|
{
|
||||||
|
flowpostprocessing->NUMBERS[j]->FieldV1 = _value;
|
||||||
|
}
|
||||||
|
if (flowpostprocessing->NUMBERS[j]->name == _digit)
|
||||||
|
{
|
||||||
|
flowpostprocessing->NUMBERS[j]->FieldV1 = _value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif //ENABLE_INFLUXDB
|
#endif //ENABLE_INFLUXDB
|
||||||
@@ -21,14 +21,19 @@ protected:
|
|||||||
std::string user, password;
|
std::string user, password;
|
||||||
bool InfluxDBenable;
|
bool InfluxDBenable;
|
||||||
|
|
||||||
void SetInitialParameter(void);
|
void SetInitialParameter(void);
|
||||||
|
|
||||||
|
void handleFieldname(string _decsep, string _value);
|
||||||
|
void handleMeasurement(string _decsep, string _value);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ClassFlowInfluxDB();
|
ClassFlowInfluxDB();
|
||||||
ClassFlowInfluxDB(std::vector<ClassFlow*>* lfc);
|
ClassFlowInfluxDB(std::vector<ClassFlow*>* lfc);
|
||||||
ClassFlowInfluxDB(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
ClassFlowInfluxDB(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
||||||
|
|
||||||
string GetInfluxDBMeasurement();
|
// string GetInfluxDBMeasurement();
|
||||||
|
|
||||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||||
bool doFlow(string time);
|
bool doFlow(string time);
|
||||||
|
|||||||
244
code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.cpp
Normal file
244
code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.cpp
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
#ifdef ENABLE_INFLUXDB
|
||||||
|
#include <sstream>
|
||||||
|
#include "ClassFlowInfluxDBv2.h"
|
||||||
|
#include "Helper.h"
|
||||||
|
#include "connect_wlan.h"
|
||||||
|
|
||||||
|
#include "time_sntp.h"
|
||||||
|
#include "interface_influxdb.h"
|
||||||
|
|
||||||
|
#include "ClassFlowPostProcessing.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "../../include/defines.h"
|
||||||
|
|
||||||
|
#include "ClassLogFile.h"
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
static const char* TAG = "INFLUXDBV2";
|
||||||
|
|
||||||
|
void ClassFlowInfluxDBv2::SetInitialParameter(void)
|
||||||
|
{
|
||||||
|
uri = "";
|
||||||
|
bucket = "";
|
||||||
|
dborg = "";
|
||||||
|
dbtoken = "";
|
||||||
|
// dbfield = "";
|
||||||
|
|
||||||
|
OldValue = "";
|
||||||
|
flowpostprocessing = NULL;
|
||||||
|
previousElement = NULL;
|
||||||
|
ListFlowControll = NULL;
|
||||||
|
disabled = false;
|
||||||
|
InfluxDBenable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassFlowInfluxDBv2::ClassFlowInfluxDBv2()
|
||||||
|
{
|
||||||
|
SetInitialParameter();
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassFlowInfluxDBv2::ClassFlowInfluxDBv2(std::vector<ClassFlow*>* lfc)
|
||||||
|
{
|
||||||
|
SetInitialParameter();
|
||||||
|
|
||||||
|
ListFlowControll = lfc;
|
||||||
|
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||||
|
{
|
||||||
|
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
|
||||||
|
{
|
||||||
|
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassFlowInfluxDBv2::ClassFlowInfluxDBv2(std::vector<ClassFlow*>* lfc, ClassFlow *_prev)
|
||||||
|
{
|
||||||
|
SetInitialParameter();
|
||||||
|
|
||||||
|
previousElement = _prev;
|
||||||
|
ListFlowControll = lfc;
|
||||||
|
|
||||||
|
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||||
|
{
|
||||||
|
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
|
||||||
|
{
|
||||||
|
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ClassFlowInfluxDBv2::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||||
|
{
|
||||||
|
std::vector<string> splitted;
|
||||||
|
|
||||||
|
aktparamgraph = trim(aktparamgraph);
|
||||||
|
printf("akt param: %s\n", aktparamgraph.c_str());
|
||||||
|
|
||||||
|
if (aktparamgraph.size() == 0)
|
||||||
|
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (toUpper(aktparamgraph).compare("[INFLUXDBV2]") != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||||
|
{
|
||||||
|
// ESP_LOGD(TAG, "while loop reading line: %s", aktparamgraph.c_str());
|
||||||
|
splitted = ZerlegeZeile(aktparamgraph);
|
||||||
|
std::string _param = GetParameterName(splitted[0]);
|
||||||
|
|
||||||
|
if ((toUpper(_param) == "ORG") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
this->dborg = splitted[1];
|
||||||
|
}
|
||||||
|
if ((toUpper(_param) == "TOKEN") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
this->dbtoken = splitted[1];
|
||||||
|
}
|
||||||
|
if ((toUpper(_param) == "URI") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
this->uri = splitted[1];
|
||||||
|
}
|
||||||
|
if (((toUpper(_param) == "FIELD")) && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
handleFieldname(splitted[0], splitted[1]);
|
||||||
|
}
|
||||||
|
if (((toUpper(_param) == "MEASUREMENT")) && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
handleMeasurement(splitted[0], splitted[1]);
|
||||||
|
}
|
||||||
|
if (((toUpper(splitted[0]) == "BUCKET")) && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
this->bucket = splitted[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("uri: %s\n", uri.c_str());
|
||||||
|
printf("org: %s\n", dborg.c_str());
|
||||||
|
printf("token: %s\n", dbtoken.c_str());
|
||||||
|
|
||||||
|
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, bucket, dborg, dbtoken);
|
||||||
|
// printf("nach V2 Init\n");
|
||||||
|
InfluxDBenable = true;
|
||||||
|
} else {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBv2 (Verion2 !!!) init skipped as we are missing some parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
string ClassFlowInfluxDBv2::GetInfluxDBMeasurement()
|
||||||
|
{
|
||||||
|
return measurement;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
void ClassFlowInfluxDBv2::handleFieldname(string _decsep, string _value)
|
||||||
|
{
|
||||||
|
string _digit, _decpos;
|
||||||
|
int _pospunkt = _decsep.find_first_of(".");
|
||||||
|
// ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt);
|
||||||
|
if (_pospunkt > -1)
|
||||||
|
_digit = _decsep.substr(0, _pospunkt);
|
||||||
|
else
|
||||||
|
_digit = "default";
|
||||||
|
for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j)
|
||||||
|
{
|
||||||
|
if (_digit == "default") // Set to default first (if nothing else is set)
|
||||||
|
{
|
||||||
|
flowpostprocessing->NUMBERS[j]->FieldV2 = _value;
|
||||||
|
}
|
||||||
|
if (flowpostprocessing->NUMBERS[j]->name == _digit)
|
||||||
|
{
|
||||||
|
flowpostprocessing->NUMBERS[j]->FieldV2 = _value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClassFlowInfluxDBv2::handleMeasurement(string _decsep, string _value)
|
||||||
|
{
|
||||||
|
string _digit, _decpos;
|
||||||
|
int _pospunkt = _decsep.find_first_of(".");
|
||||||
|
// ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt);
|
||||||
|
if (_pospunkt > -1)
|
||||||
|
_digit = _decsep.substr(0, _pospunkt);
|
||||||
|
else
|
||||||
|
_digit = "default";
|
||||||
|
for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j)
|
||||||
|
{
|
||||||
|
if (_digit == "default") // Set to default first (if nothing else is set)
|
||||||
|
{
|
||||||
|
flowpostprocessing->NUMBERS[j]->MeasurementV2 = _value;
|
||||||
|
}
|
||||||
|
if (flowpostprocessing->NUMBERS[j]->name == _digit)
|
||||||
|
{
|
||||||
|
flowpostprocessing->NUMBERS[j]->MeasurementV2 = _value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ClassFlowInfluxDBv2::doFlow(string zwtime)
|
||||||
|
{
|
||||||
|
if (!InfluxDBenable)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
std::string measurement;
|
||||||
|
std::string result;
|
||||||
|
std::string resulterror = "";
|
||||||
|
std::string resultraw = "";
|
||||||
|
std::string resultrate = "";
|
||||||
|
std::string resulttimestamp = "";
|
||||||
|
long int resulttimeutc = 0;
|
||||||
|
string zw = "";
|
||||||
|
string namenumber = "";
|
||||||
|
|
||||||
|
|
||||||
|
if (flowpostprocessing)
|
||||||
|
{
|
||||||
|
std::vector<NumberPost*>* NUMBERS = flowpostprocessing->GetNumbers();
|
||||||
|
|
||||||
|
for (int i = 0; i < (*NUMBERS).size(); ++i)
|
||||||
|
{
|
||||||
|
measurement = (*NUMBERS)[i]->MeasurementV2;
|
||||||
|
result = (*NUMBERS)[i]->ReturnValue;
|
||||||
|
resultraw = (*NUMBERS)[i]->ReturnRawValue;
|
||||||
|
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
||||||
|
resultrate = (*NUMBERS)[i]->ReturnRateValue;
|
||||||
|
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
||||||
|
resulttimeutc = (*NUMBERS)[i]->timeStampTimeUTC;
|
||||||
|
|
||||||
|
|
||||||
|
if ((*NUMBERS)[i]->FieldV2.length() > 0)
|
||||||
|
{
|
||||||
|
namenumber = (*NUMBERS)[i]->FieldV2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
namenumber = (*NUMBERS)[i]->name;
|
||||||
|
if (namenumber == "default")
|
||||||
|
namenumber = "value";
|
||||||
|
else
|
||||||
|
namenumber = namenumber + "/value";
|
||||||
|
}
|
||||||
|
|
||||||
|
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, resulttimeutc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OldValue = result;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //ENABLE_INFLUXDB
|
||||||
43
code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.h
Normal file
43
code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#ifdef ENABLE_INFLUXDB
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef CLASSFINFLUXDBv2_H
|
||||||
|
#define CLASSFINFLUXDBv2_H
|
||||||
|
|
||||||
|
#include "ClassFlow.h"
|
||||||
|
|
||||||
|
#include "ClassFlowPostProcessing.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class ClassFlowInfluxDBv2 :
|
||||||
|
public ClassFlow
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
std::string uri, bucket;
|
||||||
|
std::string dborg, dbtoken, dbfield;
|
||||||
|
std::string OldValue;
|
||||||
|
ClassFlowPostProcessing* flowpostprocessing;
|
||||||
|
bool InfluxDBenable;
|
||||||
|
|
||||||
|
void SetInitialParameter(void);
|
||||||
|
|
||||||
|
void handleFieldname(string _decsep, string _value);
|
||||||
|
void handleMeasurement(string _decsep, string _value);
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
ClassFlowInfluxDBv2();
|
||||||
|
ClassFlowInfluxDBv2(std::vector<ClassFlow*>* lfc);
|
||||||
|
ClassFlowInfluxDBv2(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
||||||
|
|
||||||
|
// string GetInfluxDBMeasurement();
|
||||||
|
|
||||||
|
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||||
|
bool doFlow(string time);
|
||||||
|
string name(){return "ClassFlowInfluxDBv2";};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //CLASSFINFLUXDBv2_H
|
||||||
|
#endif //ENABLE_INFLUXDB
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "ClassFlowMQTT.h"
|
#include "ClassFlowMQTT.h"
|
||||||
#include "Helper.h"
|
#include "Helper.h"
|
||||||
#include "connect_wlan.h"
|
#include "connect_wlan.h"
|
||||||
|
#include "read_wlanini.h"
|
||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
|
|
||||||
#include "time_sntp.h"
|
#include "time_sntp.h"
|
||||||
@@ -31,18 +32,21 @@ void ClassFlowMQTT::SetInitialParameter(void)
|
|||||||
topicError = "";
|
topicError = "";
|
||||||
topicRate = "";
|
topicRate = "";
|
||||||
topicTimeStamp = "";
|
topicTimeStamp = "";
|
||||||
maintopic = hostname;
|
maintopic = wlan_config.hostname;
|
||||||
|
|
||||||
topicUptime = "";
|
topicUptime = "";
|
||||||
topicFreeMem = "";
|
topicFreeMem = "";
|
||||||
|
|
||||||
clientname = "AIOTED-" + getMac();
|
caCertFilename = "";
|
||||||
|
clientCertFilename = "";
|
||||||
|
clientKeyFilename = "";
|
||||||
|
clientname = wlan_config.hostname;
|
||||||
|
|
||||||
OldValue = "";
|
OldValue = "";
|
||||||
flowpostprocessing = NULL;
|
flowpostprocessing = NULL;
|
||||||
user = "";
|
user = "";
|
||||||
password = "";
|
password = "";
|
||||||
SetRetainFlag = 0;
|
SetRetainFlag = false;
|
||||||
previousElement = NULL;
|
previousElement = NULL;
|
||||||
ListFlowControll = NULL;
|
ListFlowControll = NULL;
|
||||||
disabled = false;
|
disabled = false;
|
||||||
@@ -95,12 +99,24 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (toUpper(aktparamgraph).compare("[MQTT]") != 0) // Paragraph does not fit MakeImage
|
if (toUpper(aktparamgraph).compare("[MQTT]") != 0) // Paragraph does not fit MQTT
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||||
{
|
{
|
||||||
splitted = ZerlegeZeile(aktparamgraph);
|
splitted = ZerlegeZeile(aktparamgraph);
|
||||||
|
if ((toUpper(splitted[0]) == "CACERT") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
this->caCertFilename = splitted[1];
|
||||||
|
}
|
||||||
|
if ((toUpper(splitted[0]) == "CLIENTCERT") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
this->clientCertFilename = splitted[1];
|
||||||
|
}
|
||||||
|
if ((toUpper(splitted[0]) == "CLIENTKEY") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
this->clientKeyFilename = splitted[1];
|
||||||
|
}
|
||||||
if ((toUpper(splitted[0]) == "USER") && (splitted.size() > 1))
|
if ((toUpper(splitted[0]) == "USER") && (splitted.size() > 1))
|
||||||
{
|
{
|
||||||
this->user = splitted[1];
|
this->user = splitted[1];
|
||||||
@@ -113,10 +129,10 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
{
|
{
|
||||||
this->uri = splitted[1];
|
this->uri = splitted[1];
|
||||||
}
|
}
|
||||||
if ((toUpper(splitted[0]) == "SETRETAINFLAG") && (splitted.size() > 1))
|
if ((toUpper(splitted[0]) == "RETAINMESSAGES") && (splitted.size() > 1))
|
||||||
{
|
{
|
||||||
if (toUpper(splitted[1]) == "TRUE") {
|
if (toUpper(splitted[1]) == "TRUE") {
|
||||||
SetRetainFlag = 1;
|
SetRetainFlag = true;
|
||||||
setMqtt_Server_Retain(SetRetainFlag);
|
setMqtt_Server_Retain(SetRetainFlag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,6 +171,9 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
else if (toUpper(splitted[1]) == "ENERGY_MWH") {
|
else if (toUpper(splitted[1]) == "ENERGY_MWH") {
|
||||||
mqttServer_setMeterType("energy", "MWh", "h", "MW");
|
mqttServer_setMeterType("energy", "MWh", "h", "MW");
|
||||||
}
|
}
|
||||||
|
else if (toUpper(splitted[1]) == "ENERGY_GJ") {
|
||||||
|
mqttServer_setMeterType("energy", "GJ", "h", "GJ/h");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((toUpper(splitted[0]) == "CLIENTID") && (splitted.size() > 1))
|
if ((toUpper(splitted[0]) == "CLIENTID") && (splitted.size() > 1))
|
||||||
@@ -165,7 +184,6 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
if (((toUpper(splitted[0]) == "TOPIC") || (toUpper(splitted[0]) == "MAINTOPIC")) && (splitted.size() > 1))
|
if (((toUpper(splitted[0]) == "TOPIC") || (toUpper(splitted[0]) == "MAINTOPIC")) && (splitted.size() > 1))
|
||||||
{
|
{
|
||||||
maintopic = splitted[1];
|
maintopic = splitted[1];
|
||||||
mqttServer_setMainTopic(maintopic);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,19 +192,15 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
* How ever we need the interval parameter from the ClassFlowControll, but that only gets started later.
|
* How ever we need the interval parameter from the ClassFlowControll, but that only gets started later.
|
||||||
* To work around this, we delay the start and trigger it from ClassFlowControll::ReadParameter() */
|
* To work around this, we delay the start and trigger it from ClassFlowControll::ReadParameter() */
|
||||||
|
|
||||||
|
mqttServer_setMainTopic(maintopic);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string ClassFlowMQTT::GetMQTTMainTopic()
|
bool ClassFlowMQTT::Start(float AutoInterval)
|
||||||
{
|
{
|
||||||
return maintopic;
|
roundInterval = AutoInterval; // Minutes
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool ClassFlowMQTT::Start(float AutoIntervall)
|
|
||||||
{
|
|
||||||
roundInterval = AutoIntervall; // Minutes
|
|
||||||
keepAlive = roundInterval * 60 * 2.5; // Seconds, make sure it is greater thatn 2 rounds!
|
keepAlive = roundInterval * 60 * 2.5; // Seconds, make sure it is greater thatn 2 rounds!
|
||||||
|
|
||||||
std::stringstream stream;
|
std::stringstream stream;
|
||||||
@@ -197,7 +211,8 @@ bool ClassFlowMQTT::Start(float AutoIntervall)
|
|||||||
mqttServer_setParameter(flowpostprocessing->GetNumbers(), keepAlive, roundInterval);
|
mqttServer_setParameter(flowpostprocessing->GetNumbers(), keepAlive, roundInterval);
|
||||||
|
|
||||||
bool MQTTConfigCheck = MQTT_Configure(uri, clientname, user, password, maintopic, LWT_TOPIC, LWT_CONNECTED,
|
bool MQTTConfigCheck = MQTT_Configure(uri, clientname, user, password, maintopic, LWT_TOPIC, LWT_CONNECTED,
|
||||||
LWT_DISCONNECTED, keepAlive, SetRetainFlag, (void *)&GotConnected);
|
LWT_DISCONNECTED, caCertFilename, clientCertFilename, clientKeyFilename,
|
||||||
|
keepAlive, SetRetainFlag, (void *)&GotConnected);
|
||||||
|
|
||||||
if (!MQTTConfigCheck) {
|
if (!MQTTConfigCheck) {
|
||||||
return false;
|
return false;
|
||||||
@@ -209,6 +224,7 @@ bool ClassFlowMQTT::Start(float AutoIntervall)
|
|||||||
|
|
||||||
bool ClassFlowMQTT::doFlow(string zwtime)
|
bool ClassFlowMQTT::doFlow(string zwtime)
|
||||||
{
|
{
|
||||||
|
bool success;
|
||||||
std::string result;
|
std::string result;
|
||||||
std::string resulterror = "";
|
std::string resulterror = "";
|
||||||
std::string resultraw = "";
|
std::string resultraw = "";
|
||||||
@@ -219,8 +235,12 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
|||||||
std::string resultchangabs = "";
|
std::string resultchangabs = "";
|
||||||
string zw = "";
|
string zw = "";
|
||||||
string namenumber = "";
|
string namenumber = "";
|
||||||
|
int qos = 1;
|
||||||
|
|
||||||
publishSystemData();
|
/* Send the the Homeassistant Discovery and the Static Topics in case they where scheduled */
|
||||||
|
sendDiscovery_and_static_Topics();
|
||||||
|
|
||||||
|
success = publishSystemData(qos);
|
||||||
|
|
||||||
if (flowpostprocessing && getMQTTisConnected())
|
if (flowpostprocessing && getMQTTisConnected())
|
||||||
{
|
{
|
||||||
@@ -246,13 +266,13 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
|||||||
|
|
||||||
|
|
||||||
if (result.length() > 0)
|
if (result.length() > 0)
|
||||||
MQTTPublish(namenumber + "value", result, SetRetainFlag);
|
success |= MQTTPublish(namenumber + "value", result, qos, SetRetainFlag);
|
||||||
|
|
||||||
if (resulterror.length() > 0)
|
if (resulterror.length() > 0)
|
||||||
MQTTPublish(namenumber + "error", resulterror, SetRetainFlag);
|
success |= MQTTPublish(namenumber + "error", resulterror, qos, SetRetainFlag);
|
||||||
|
|
||||||
if (resultrate.length() > 0) {
|
if (resultrate.length() > 0) {
|
||||||
MQTTPublish(namenumber + "rate", resultrate, SetRetainFlag);
|
success |= MQTTPublish(namenumber + "rate", resultrate, qos, SetRetainFlag);
|
||||||
|
|
||||||
std::string resultRatePerTimeUnit;
|
std::string resultRatePerTimeUnit;
|
||||||
if (getTimeUnit() == "h") { // Need conversion to be per hour
|
if (getTimeUnit() == "h") { // Need conversion to be per hour
|
||||||
@@ -261,22 +281,22 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
|||||||
else { // Keep per minute
|
else { // Keep per minute
|
||||||
resultRatePerTimeUnit = resultrate;
|
resultRatePerTimeUnit = resultrate;
|
||||||
}
|
}
|
||||||
MQTTPublish(namenumber + "rate_per_time_unit", resultRatePerTimeUnit, SetRetainFlag);
|
success |= MQTTPublish(namenumber + "rate_per_time_unit", resultRatePerTimeUnit, qos, SetRetainFlag);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resultchangabs.length() > 0) {
|
if (resultchangabs.length() > 0) {
|
||||||
MQTTPublish(namenumber + "changeabsolut", resultchangabs, SetRetainFlag); // Legacy API
|
success |= MQTTPublish(namenumber + "changeabsolut", resultchangabs, qos, SetRetainFlag); // Legacy API
|
||||||
MQTTPublish(namenumber + "rate_per_digitalization_round", resultchangabs, SetRetainFlag);
|
success |= MQTTPublish(namenumber + "rate_per_digitalization_round", resultchangabs, qos, SetRetainFlag);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resultraw.length() > 0)
|
if (resultraw.length() > 0)
|
||||||
MQTTPublish(namenumber + "raw", resultraw, SetRetainFlag);
|
success |= MQTTPublish(namenumber + "raw", resultraw, qos, SetRetainFlag);
|
||||||
|
|
||||||
if (resulttimestamp.length() > 0)
|
if (resulttimestamp.length() > 0)
|
||||||
MQTTPublish(namenumber + "timestamp", resulttimestamp, SetRetainFlag);
|
success |= MQTTPublish(namenumber + "timestamp", resulttimestamp, qos, SetRetainFlag);
|
||||||
|
|
||||||
std::string json = flowpostprocessing->getJsonFromNumber(i, "\n");
|
std::string json = flowpostprocessing->getJsonFromNumber(i, "\n");
|
||||||
MQTTPublish(namenumber + "json", json, SetRetainFlag);
|
success |= MQTTPublish(namenumber + "json", json, qos, SetRetainFlag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,10 +314,14 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
|||||||
// result = result + "\t" + zw;
|
// result = result + "\t" + zw;
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// MQTTPublish(topic, result, SetRetainFlag);
|
// success |= MQTTPublish(topic, result, qos, SetRetainFlag);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
OldValue = result;
|
OldValue = result;
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "One or more MQTT topics failed to be published!");
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ protected:
|
|||||||
std::string OldValue;
|
std::string OldValue;
|
||||||
ClassFlowPostProcessing* flowpostprocessing;
|
ClassFlowPostProcessing* flowpostprocessing;
|
||||||
std::string user, password;
|
std::string user, password;
|
||||||
int SetRetainFlag;
|
std::string caCertFilename, clientCertFilename, clientKeyFilename;
|
||||||
|
bool SetRetainFlag;
|
||||||
int keepAlive; // Seconds
|
int keepAlive; // Seconds
|
||||||
float roundInterval; // Minutes
|
float roundInterval; // Minutes
|
||||||
|
|
||||||
@@ -31,8 +32,7 @@ public:
|
|||||||
ClassFlowMQTT(std::vector<ClassFlow*>* lfc);
|
ClassFlowMQTT(std::vector<ClassFlow*>* lfc);
|
||||||
ClassFlowMQTT(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
ClassFlowMQTT(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
||||||
|
|
||||||
string GetMQTTMainTopic();
|
bool Start(float AutoInterval);
|
||||||
bool Start(float AutoIntervall);
|
|
||||||
|
|
||||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||||
bool doFlow(string time);
|
bool doFlow(string time);
|
||||||
|
|||||||
@@ -1,255 +0,0 @@
|
|||||||
#include "ClassFlowMakeImage.h"
|
|
||||||
#include "Helper.h"
|
|
||||||
#include "ClassLogFile.h"
|
|
||||||
|
|
||||||
#include "CImageBasis.h"
|
|
||||||
#include "ClassControllCamera.h"
|
|
||||||
|
|
||||||
#include "esp_wifi.h"
|
|
||||||
#include "esp_log.h"
|
|
||||||
#include "../../include/defines.h"
|
|
||||||
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
// #define DEBUG_DETAIL_ON
|
|
||||||
|
|
||||||
// #define WIFITURNOFF
|
|
||||||
|
|
||||||
static const char* TAG = "flow_make_image";
|
|
||||||
|
|
||||||
esp_err_t ClassFlowMakeImage::camera_capture(){
|
|
||||||
string nm = namerawimage;
|
|
||||||
Camera.CaptureToFile(nm);
|
|
||||||
time(&TimeImageTaken);
|
|
||||||
localtime(&TimeImageTaken);
|
|
||||||
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClassFlowMakeImage::takePictureWithFlash(int flash_duration)
|
|
||||||
{
|
|
||||||
// in case the image is flipped, it must be reset here //
|
|
||||||
rawImage->width = image_width;
|
|
||||||
rawImage->height = image_height;
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
ESP_LOGD(TAG, "flash_duration: %d", flash_duration);
|
|
||||||
Camera.CaptureToBasisImage(rawImage, flash_duration);
|
|
||||||
time(&TimeImageTaken);
|
|
||||||
localtime(&TimeImageTaken);
|
|
||||||
|
|
||||||
if (SaveAllFiles) rawImage->SaveToFile(namerawimage);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClassFlowMakeImage::SetInitialParameter(void)
|
|
||||||
{
|
|
||||||
waitbeforepicture = 5;
|
|
||||||
isImageSize = false;
|
|
||||||
ImageQuality = -1;
|
|
||||||
TimeImageTaken = 0;
|
|
||||||
ImageQuality = 5;
|
|
||||||
rawImage = NULL;
|
|
||||||
ImageSize = FRAMESIZE_VGA;
|
|
||||||
SaveAllFiles = false;
|
|
||||||
disabled = false;
|
|
||||||
FixedExposure = false;
|
|
||||||
namerawimage = "/sdcard/img_tmp/raw.jpg";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ClassFlowMakeImage::ClassFlowMakeImage(std::vector<ClassFlow*>* lfc) : ClassFlowImage(lfc, TAG)
|
|
||||||
{
|
|
||||||
LogImageLocation = "/log/source";
|
|
||||||
logfileRetentionInDays = 5;
|
|
||||||
SetInitialParameter();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool ClassFlowMakeImage::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|
||||||
{
|
|
||||||
std::vector<string> splitted;
|
|
||||||
|
|
||||||
aktparamgraph = trim(aktparamgraph);
|
|
||||||
int _brightness = -100;
|
|
||||||
int _contrast = -100;
|
|
||||||
int _saturation = -100;
|
|
||||||
|
|
||||||
if (aktparamgraph.size() == 0)
|
|
||||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (aktparamgraph.compare("[MakeImage]") != 0) // Paragraph does not fit MakeImage
|
|
||||||
return false;
|
|
||||||
|
|
||||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
|
||||||
{
|
|
||||||
splitted = ZerlegeZeile(aktparamgraph);
|
|
||||||
if ((splitted[0] == "LogImageLocation") && (splitted.size() > 1))
|
|
||||||
{
|
|
||||||
LogImageLocation = "/sdcard" + splitted[1];
|
|
||||||
isLogImage = true;
|
|
||||||
}
|
|
||||||
if ((splitted[0] == "ImageQuality") && (splitted.size() > 1))
|
|
||||||
ImageQuality = std::stod(splitted[1]);
|
|
||||||
|
|
||||||
if ((splitted[0] == "ImageSize") && (splitted.size() > 1))
|
|
||||||
{
|
|
||||||
ImageSize = Camera.TextToFramesize(splitted[1].c_str());
|
|
||||||
isImageSize = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((toUpper(splitted[0]) == "SAVEALLFILES") && (splitted.size() > 1))
|
|
||||||
{
|
|
||||||
if (toUpper(splitted[1]) == "TRUE")
|
|
||||||
SaveAllFiles = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((toUpper(splitted[0]) == "WAITBEFORETAKINGPICTURE") && (splitted.size() > 1))
|
|
||||||
{
|
|
||||||
waitbeforepicture = stoi(splitted[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((toUpper(splitted[0]) == "LOGFILERETENTIONINDAYS") && (splitted.size() > 1))
|
|
||||||
{
|
|
||||||
this->logfileRetentionInDays = std::stoi(splitted[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((toUpper(splitted[0]) == "BRIGHTNESS") && (splitted.size() > 1))
|
|
||||||
{
|
|
||||||
_brightness = stoi(splitted[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((toUpper(splitted[0]) == "CONTRAST") && (splitted.size() > 1))
|
|
||||||
{
|
|
||||||
_contrast = stoi(splitted[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((toUpper(splitted[0]) == "SATURATION") && (splitted.size() > 1))
|
|
||||||
{
|
|
||||||
_saturation = stoi(splitted[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((toUpper(splitted[0]) == "FIXEDEXPOSURE") && (splitted.size() > 1))
|
|
||||||
{
|
|
||||||
if (toUpper(splitted[1]) == "TRUE")
|
|
||||||
FixedExposure = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((toUpper(splitted[0]) == "LEDINTENSITY") && (splitted.size() > 1))
|
|
||||||
{
|
|
||||||
float ledintensity = stof(splitted[1]);
|
|
||||||
ledintensity = min((float) 100, ledintensity);
|
|
||||||
ledintensity = max((float) 0, ledintensity);
|
|
||||||
Camera.SetLEDIntensity(ledintensity);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((toUpper(splitted[0]) == "DEMO") && (splitted.size() > 1))
|
|
||||||
{
|
|
||||||
if (toUpper(splitted[1]) == "TRUE")
|
|
||||||
Camera.useDemoMode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Camera.SetBrightnessContrastSaturation(_brightness, _contrast, _saturation);
|
|
||||||
Camera.SetQualitySize(ImageQuality, ImageSize);
|
|
||||||
|
|
||||||
image_width = Camera.image_width;
|
|
||||||
image_height = Camera.image_height;
|
|
||||||
rawImage = new CImageBasis();
|
|
||||||
rawImage->CreateEmptyImage(image_width, image_height, 3);
|
|
||||||
|
|
||||||
waitbeforepicture_store = waitbeforepicture;
|
|
||||||
if (FixedExposure && (waitbeforepicture > 0))
|
|
||||||
{
|
|
||||||
// ESP_LOGD(TAG, "Fixed Exposure enabled!");
|
|
||||||
int flash_duration = (int) (waitbeforepicture * 1000);
|
|
||||||
Camera.EnableAutoExposure(flash_duration);
|
|
||||||
waitbeforepicture = 0.2;
|
|
||||||
// flash_duration = (int) (waitbeforepicture * 1000);
|
|
||||||
// takePictureWithFlash(flash_duration);
|
|
||||||
// rawImage->SaveToFile("/sdcard/init2.jpg");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
string ClassFlowMakeImage::getHTMLSingleStep(string host)
|
|
||||||
{
|
|
||||||
string result;
|
|
||||||
result = "Raw Image: <br>\n<img src=\"" + host + "/img_tmp/raw.jpg\">\n";
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool ClassFlowMakeImage::doFlow(string zwtime)
|
|
||||||
{
|
|
||||||
string logPath = CreateLogFolder(zwtime);
|
|
||||||
|
|
||||||
int flash_duration = (int) (waitbeforepicture * 1000);
|
|
||||||
|
|
||||||
#ifdef DEBUG_DETAIL_ON
|
|
||||||
LogFile.WriteHeapInfo("ClassFlowMakeImage::doFlow - Before takePictureWithFlash");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef WIFITURNOFF
|
|
||||||
esp_wifi_stop(); // to save power usage and
|
|
||||||
#endif
|
|
||||||
|
|
||||||
takePictureWithFlash(flash_duration);
|
|
||||||
|
|
||||||
#ifdef WIFITURNOFF
|
|
||||||
esp_wifi_start();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef DEBUG_DETAIL_ON
|
|
||||||
LogFile.WriteHeapInfo("ClassFlowMakeImage::doFlow - After takePictureWithFlash");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
LogImage(logPath, "raw", NULL, NULL, zwtime, rawImage);
|
|
||||||
|
|
||||||
RemoveOldLogs();
|
|
||||||
|
|
||||||
#ifdef DEBUG_DETAIL_ON
|
|
||||||
LogFile.WriteHeapInfo("ClassFlowMakeImage::doFlow - After RemoveOldLogs");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
esp_err_t ClassFlowMakeImage::SendRawJPG(httpd_req_t *req)
|
|
||||||
{
|
|
||||||
int flash_duration = (int) (waitbeforepicture * 1000);
|
|
||||||
time(&TimeImageTaken);
|
|
||||||
localtime(&TimeImageTaken);
|
|
||||||
|
|
||||||
return Camera.CaptureToHTTP(req, flash_duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ImageData* ClassFlowMakeImage::SendRawImage()
|
|
||||||
{
|
|
||||||
CImageBasis *zw = new CImageBasis(rawImage);
|
|
||||||
ImageData *id;
|
|
||||||
int flash_duration = (int) (waitbeforepicture * 1000);
|
|
||||||
Camera.CaptureToBasisImage(zw, flash_duration);
|
|
||||||
time(&TimeImageTaken);
|
|
||||||
localtime(&TimeImageTaken);
|
|
||||||
|
|
||||||
id = zw->writeToMemoryAsJPG();
|
|
||||||
delete zw;
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
time_t ClassFlowMakeImage::getTimeImageTaken()
|
|
||||||
{
|
|
||||||
return TimeImageTaken;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassFlowMakeImage::~ClassFlowMakeImage(void)
|
|
||||||
{
|
|
||||||
delete rawImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef CLASSFFLOWMAKEIMAGE_H
|
|
||||||
#define CLASSFFLOWMAKEIMAGE_H
|
|
||||||
|
|
||||||
#include "ClassFlowImage.h"
|
|
||||||
#include "ClassControllCamera.h"
|
|
||||||
#include "../../include/defines.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class ClassFlowMakeImage :
|
|
||||||
public ClassFlowImage
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
float waitbeforepicture;
|
|
||||||
float waitbeforepicture_store;
|
|
||||||
framesize_t ImageSize;
|
|
||||||
bool isImageSize;
|
|
||||||
int ImageQuality;
|
|
||||||
time_t TimeImageTaken;
|
|
||||||
string namerawimage;
|
|
||||||
int image_height, image_width;
|
|
||||||
bool SaveAllFiles;
|
|
||||||
bool FixedExposure;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void CopyFile(string input, string output);
|
|
||||||
|
|
||||||
esp_err_t camera_capture();
|
|
||||||
void takePictureWithFlash(int flash_duration);
|
|
||||||
|
|
||||||
|
|
||||||
void SetInitialParameter(void);
|
|
||||||
|
|
||||||
public:
|
|
||||||
CImageBasis *rawImage;
|
|
||||||
|
|
||||||
ClassFlowMakeImage(std::vector<ClassFlow*>* lfc);
|
|
||||||
|
|
||||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
|
||||||
bool doFlow(string time);
|
|
||||||
string getHTMLSingleStep(string host);
|
|
||||||
time_t getTimeImageTaken();
|
|
||||||
string name(){return "ClassFlowMakeImage";};
|
|
||||||
|
|
||||||
ImageData* SendRawImage();
|
|
||||||
esp_err_t SendRawJPG(httpd_req_t *req);
|
|
||||||
|
|
||||||
~ClassFlowMakeImage(void);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //CLASSFFLOWMAKEIMAGE_H
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
|||||||
#define CLASSFFLOWPOSTPROCESSING_H
|
#define CLASSFFLOWPOSTPROCESSING_H
|
||||||
|
|
||||||
#include "ClassFlow.h"
|
#include "ClassFlow.h"
|
||||||
#include "ClassFlowMakeImage.h"
|
#include "ClassFlowTakeImage.h"
|
||||||
#include "ClassFlowCNNGeneral.h"
|
#include "ClassFlowCNNGeneral.h"
|
||||||
#include "ClassFlowDefineTypes.h"
|
#include "ClassFlowDefineTypes.h"
|
||||||
|
|
||||||
@@ -15,7 +15,6 @@ class ClassFlowPostProcessing :
|
|||||||
public ClassFlow
|
public ClassFlow
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::vector<NumberPost*> NUMBERS;
|
|
||||||
bool UpdatePreValueINI;
|
bool UpdatePreValueINI;
|
||||||
|
|
||||||
int PreValueAgeStartup;
|
int PreValueAgeStartup;
|
||||||
@@ -29,7 +28,7 @@ protected:
|
|||||||
|
|
||||||
string FilePreValue;
|
string FilePreValue;
|
||||||
|
|
||||||
ClassFlowMakeImage *flowMakeImage;
|
ClassFlowTakeImage *flowTakeImage;
|
||||||
|
|
||||||
bool LoadPreValue(void);
|
bool LoadPreValue(void);
|
||||||
string ShiftDecimal(string in, int _decShift);
|
string ShiftDecimal(string in, int _decShift);
|
||||||
@@ -54,6 +53,8 @@ protected:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
bool PreValueUse;
|
bool PreValueUse;
|
||||||
|
std::vector<NumberPost*> NUMBERS;
|
||||||
|
|
||||||
|
|
||||||
ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc, ClassFlowCNNGeneral *_analog, ClassFlowCNNGeneral *_digit);
|
ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc, ClassFlowCNNGeneral *_analog, ClassFlowCNNGeneral *_digit);
|
||||||
virtual ~ClassFlowPostProcessing(){};
|
virtual ~ClassFlowPostProcessing(){};
|
||||||
@@ -67,7 +68,7 @@ public:
|
|||||||
void SavePreValue();
|
void SavePreValue();
|
||||||
string getJsonFromNumber(int i, std::string _lineend);
|
string getJsonFromNumber(int i, std::string _lineend);
|
||||||
string GetPreValue(std::string _number = "");
|
string GetPreValue(std::string _number = "");
|
||||||
void SetPreValue(double zw, string _numbers, bool _extern = false);
|
bool SetPreValue(double zw, string _numbers, bool _extern = false);
|
||||||
|
|
||||||
std::string GetJSON(std::string _lineend = "\n");
|
std::string GetJSON(std::string _lineend = "\n");
|
||||||
std::string getNumbersName();
|
std::string getNumbersName();
|
||||||
|
|||||||
599
code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp
Normal file
599
code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp
Normal file
@@ -0,0 +1,599 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
#include "ClassFlowTakeImage.h"
|
||||||
|
#include "Helper.h"
|
||||||
|
#include "ClassLogFile.h"
|
||||||
|
|
||||||
|
#include "CImageBasis.h"
|
||||||
|
#include "ClassControllCamera.h"
|
||||||
|
#include "MainFlowControl.h"
|
||||||
|
|
||||||
|
#include "esp_wifi.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "../../include/defines.h"
|
||||||
|
#include "psram.h"
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
// #define DEBUG_DETAIL_ON
|
||||||
|
// #define WIFITURNOFF
|
||||||
|
|
||||||
|
static const char *TAG = "TAKEIMAGE";
|
||||||
|
|
||||||
|
esp_err_t ClassFlowTakeImage::camera_capture(void)
|
||||||
|
{
|
||||||
|
string nm = namerawimage;
|
||||||
|
Camera.CaptureToFile(nm);
|
||||||
|
time(&TimeImageTaken);
|
||||||
|
localtime(&TimeImageTaken);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClassFlowTakeImage::takePictureWithFlash(int flash_duration)
|
||||||
|
{
|
||||||
|
// in case the image is flipped, it must be reset here //
|
||||||
|
rawImage->width = CCstatus.ImageWidth;
|
||||||
|
rawImage->height = CCstatus.ImageHeight;
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "flash_duration: %d", flash_duration);
|
||||||
|
|
||||||
|
Camera.CaptureToBasisImage(rawImage, flash_duration);
|
||||||
|
|
||||||
|
time(&TimeImageTaken);
|
||||||
|
localtime(&TimeImageTaken);
|
||||||
|
|
||||||
|
if (CCstatus.SaveAllFiles)
|
||||||
|
{
|
||||||
|
rawImage->SaveToFile(namerawimage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClassFlowTakeImage::SetInitialParameter(void)
|
||||||
|
{
|
||||||
|
TimeImageTaken = 0;
|
||||||
|
rawImage = NULL;
|
||||||
|
disabled = false;
|
||||||
|
namerawimage = "/sdcard/img_tmp/raw.jpg";
|
||||||
|
}
|
||||||
|
|
||||||
|
// auslesen der Kameraeinstellungen aus der config.ini
|
||||||
|
// wird beim Start aufgerufen
|
||||||
|
bool ClassFlowTakeImage::ReadParameter(FILE *pfile, string &aktparamgraph)
|
||||||
|
{
|
||||||
|
Camera.getSensorDatenToCCstatus(); // Kamera >>> CCstatus
|
||||||
|
|
||||||
|
std::vector<string> splitted;
|
||||||
|
|
||||||
|
aktparamgraph = trim(aktparamgraph);
|
||||||
|
|
||||||
|
if (aktparamgraph.size() == 0)
|
||||||
|
{
|
||||||
|
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aktparamgraph.compare("[TakeImage]") != 0)
|
||||||
|
{
|
||||||
|
// Paragraph does not fit TakeImage
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||||
|
{
|
||||||
|
splitted = ZerlegeZeile(aktparamgraph);
|
||||||
|
|
||||||
|
if ((toUpper(splitted[0]) == "RAWIMAGESLOCATION") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
imagesLocation = "/sdcard" + splitted[1];
|
||||||
|
isLogImage = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "RAWIMAGESRETENTION") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
this->imagesRetention = std::stod(splitted[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "SAVEALLFILES") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
if (toUpper(splitted[1]) == "TRUE")
|
||||||
|
{
|
||||||
|
CCstatus.SaveAllFiles = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CCstatus.SaveAllFiles = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "WAITBEFORETAKINGPICTURE") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
int _WaitBeforePicture = std::stoi(splitted[1]);
|
||||||
|
if (_WaitBeforePicture != 0)
|
||||||
|
{
|
||||||
|
CCstatus.WaitBeforePicture = _WaitBeforePicture;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CCstatus.WaitBeforePicture = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMGAINCEILING") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
std::string _ImageGainceiling = toUpper(splitted[1]);
|
||||||
|
if (_ImageGainceiling == "X4")
|
||||||
|
{
|
||||||
|
CCstatus.ImageGainceiling = GAINCEILING_4X;
|
||||||
|
}
|
||||||
|
else if (_ImageGainceiling == "X8")
|
||||||
|
{
|
||||||
|
CCstatus.ImageGainceiling = GAINCEILING_8X;
|
||||||
|
}
|
||||||
|
else if (_ImageGainceiling == "X16")
|
||||||
|
{
|
||||||
|
CCstatus.ImageGainceiling = GAINCEILING_16X;
|
||||||
|
}
|
||||||
|
else if (_ImageGainceiling == "X32")
|
||||||
|
{
|
||||||
|
CCstatus.ImageGainceiling = GAINCEILING_32X;
|
||||||
|
}
|
||||||
|
else if (_ImageGainceiling == "X64")
|
||||||
|
{
|
||||||
|
CCstatus.ImageGainceiling = GAINCEILING_64X;
|
||||||
|
}
|
||||||
|
else if (_ImageGainceiling == "X128")
|
||||||
|
{
|
||||||
|
CCstatus.ImageGainceiling = GAINCEILING_128X;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CCstatus.ImageGainceiling = GAINCEILING_2X;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMQUALITY") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
int _ImageQuality = std::stoi(splitted[1]);
|
||||||
|
if ((_ImageQuality >= 0) && (_ImageQuality <= 63))
|
||||||
|
{
|
||||||
|
CCstatus.ImageQuality = _ImageQuality;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMBRIGHTNESS") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
int _ImageBrightness = std::stoi(splitted[1]);
|
||||||
|
if ((_ImageBrightness >= -2) && (_ImageBrightness <= 2))
|
||||||
|
{
|
||||||
|
CCstatus.ImageBrightness = _ImageBrightness;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMCONTRAST") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
int _ImageContrast = std::stoi(splitted[1]);
|
||||||
|
if ((_ImageContrast >= -2) && (_ImageContrast <= 2))
|
||||||
|
{
|
||||||
|
CCstatus.ImageContrast = _ImageContrast;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMSATURATION") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
int _ImageSaturation = std::stoi(splitted[1]);
|
||||||
|
if ((_ImageSaturation >= -2) && (_ImageSaturation <= 2))
|
||||||
|
{
|
||||||
|
CCstatus.ImageSaturation = _ImageSaturation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMSHARPNESS") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
int _ImageSharpness = std::stoi(splitted[1]);
|
||||||
|
if ((_ImageSharpness >= -2) && (_ImageSharpness <= 2))
|
||||||
|
{
|
||||||
|
CCstatus.ImageSharpness = _ImageSharpness;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMAUTOSHARPNESS") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
if (toUpper(splitted[1]) == "TRUE")
|
||||||
|
{
|
||||||
|
CCstatus.ImageAutoSharpness = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CCstatus.ImageAutoSharpness = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMSPECIALEFFECT") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
std::string _ImageSpecialEffect = toUpper(splitted[1]);
|
||||||
|
if (_ImageSpecialEffect == "NEGATIVE")
|
||||||
|
{
|
||||||
|
CCstatus.ImageSpecialEffect = 1;
|
||||||
|
}
|
||||||
|
else if (_ImageSpecialEffect == "GRAYSCALE")
|
||||||
|
{
|
||||||
|
CCstatus.ImageSpecialEffect = 2;
|
||||||
|
}
|
||||||
|
else if (_ImageSpecialEffect == "RED")
|
||||||
|
{
|
||||||
|
CCstatus.ImageSpecialEffect = 3;
|
||||||
|
}
|
||||||
|
else if (_ImageSpecialEffect == "GREEN")
|
||||||
|
{
|
||||||
|
CCstatus.ImageSpecialEffect = 4;
|
||||||
|
}
|
||||||
|
else if (_ImageSpecialEffect == "BLUE")
|
||||||
|
{
|
||||||
|
CCstatus.ImageSpecialEffect = 5;
|
||||||
|
}
|
||||||
|
else if (_ImageSpecialEffect == "RETRO")
|
||||||
|
{
|
||||||
|
CCstatus.ImageSpecialEffect = 6;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CCstatus.ImageSpecialEffect = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMWBMODE") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
std::string _ImageWbMode = toUpper(splitted[1]);
|
||||||
|
if (_ImageWbMode == "SUNNY")
|
||||||
|
{
|
||||||
|
CCstatus.ImageWbMode = 1;
|
||||||
|
}
|
||||||
|
else if (_ImageWbMode == "CLOUDY")
|
||||||
|
{
|
||||||
|
CCstatus.ImageWbMode = 2;
|
||||||
|
}
|
||||||
|
else if (_ImageWbMode == "OFFICE")
|
||||||
|
{
|
||||||
|
CCstatus.ImageWbMode = 3;
|
||||||
|
}
|
||||||
|
else if (_ImageWbMode == "HOME")
|
||||||
|
{
|
||||||
|
CCstatus.ImageWbMode = 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CCstatus.ImageWbMode = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMAWB") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
if (toUpper(splitted[1]) == "TRUE")
|
||||||
|
{
|
||||||
|
CCstatus.ImageAwb = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CCstatus.ImageAwb = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMAWBGAIN") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
if (toUpper(splitted[1]) == "TRUE")
|
||||||
|
{
|
||||||
|
CCstatus.ImageAwbGain = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CCstatus.ImageAwbGain = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMAEC") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
if (toUpper(splitted[1]) == "TRUE")
|
||||||
|
{
|
||||||
|
CCstatus.ImageAec = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CCstatus.ImageAec = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMAEC2") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
if (toUpper(splitted[1]) == "TRUE")
|
||||||
|
{
|
||||||
|
CCstatus.ImageAec2 = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CCstatus.ImageAec2 = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMAELEVEL") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
int _ImageAeLevel = std::stoi(splitted[1]);
|
||||||
|
if ((_ImageAeLevel >= -2) && (_ImageAeLevel <= 2))
|
||||||
|
{
|
||||||
|
CCstatus.ImageAeLevel = _ImageAeLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMAECVALUE") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
int _ImageAecValue = std::stoi(splitted[1]);
|
||||||
|
if ((_ImageAecValue >= 0) && (_ImageAecValue <= 1200))
|
||||||
|
{
|
||||||
|
CCstatus.ImageAecValue = _ImageAecValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMAGC") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
if (toUpper(splitted[1]) == "TRUE")
|
||||||
|
{
|
||||||
|
CCstatus.ImageAgc = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CCstatus.ImageAgc = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMAGCGAIN") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
int _ImageAgcGain = std::stoi(splitted[1]);
|
||||||
|
if ((_ImageAgcGain >= 0) && (_ImageAgcGain <= 30))
|
||||||
|
{
|
||||||
|
CCstatus.ImageAgcGain = _ImageAgcGain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMBPC") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
if (toUpper(splitted[1]) == "TRUE")
|
||||||
|
{
|
||||||
|
CCstatus.ImageBpc = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CCstatus.ImageBpc = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMWPC") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
if (toUpper(splitted[1]) == "TRUE")
|
||||||
|
{
|
||||||
|
CCstatus.ImageWpc = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CCstatus.ImageWpc = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMRAWGMA") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
if (toUpper(splitted[1]) == "TRUE")
|
||||||
|
{
|
||||||
|
CCstatus.ImageRawGma = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CCstatus.ImageRawGma = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMLENC") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
if (toUpper(splitted[1]) == "TRUE")
|
||||||
|
{
|
||||||
|
CCstatus.ImageLenc = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CCstatus.ImageLenc = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMHMIRROR") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
if (toUpper(splitted[1]) == "TRUE")
|
||||||
|
{
|
||||||
|
CCstatus.ImageHmirror = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CCstatus.ImageHmirror = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMVFLIP") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
if (toUpper(splitted[1]) == "TRUE")
|
||||||
|
{
|
||||||
|
CCstatus.ImageVflip = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CCstatus.ImageVflip = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMDCW") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
if (toUpper(splitted[1]) == "TRUE")
|
||||||
|
{
|
||||||
|
CCstatus.ImageDcw = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CCstatus.ImageDcw = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMZOOM") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
if (toUpper(splitted[1]) == "TRUE")
|
||||||
|
{
|
||||||
|
CCstatus.ImageZoomEnabled = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CCstatus.ImageZoomEnabled = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMZOOMOFFSETX") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
CCstatus.ImageZoomOffsetX = std::stoi(splitted[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMZOOMOFFSETY") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
CCstatus.ImageZoomOffsetY = std::stoi(splitted[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "CAMZOOMSIZE") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
int _ImageZoomSize = std::stoi(splitted[1]);
|
||||||
|
if (_ImageZoomSize >= 0)
|
||||||
|
{
|
||||||
|
CCstatus.ImageZoomSize = _ImageZoomSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "LEDINTENSITY") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
float ledintensity = std::stof(splitted[1]);
|
||||||
|
Camera.SetLEDIntensity(ledintensity);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((toUpper(splitted[0]) == "DEMO") && (splitted.size() > 1))
|
||||||
|
{
|
||||||
|
if (toUpper(splitted[1]) == "TRUE")
|
||||||
|
{
|
||||||
|
CCstatus.DemoMode = true;
|
||||||
|
Camera.useDemoMode();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CCstatus.DemoMode = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera
|
||||||
|
Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize);
|
||||||
|
|
||||||
|
rawImage = new CImageBasis("rawImage");
|
||||||
|
rawImage->CreateEmptyImage(CCstatus.ImageWidth, CCstatus.ImageHeight, 3);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassFlowTakeImage::ClassFlowTakeImage(std::vector<ClassFlow *> *lfc) : ClassFlowImage(lfc, TAG)
|
||||||
|
{
|
||||||
|
imagesLocation = "/log/source";
|
||||||
|
imagesRetention = 5;
|
||||||
|
SetInitialParameter();
|
||||||
|
}
|
||||||
|
|
||||||
|
string ClassFlowTakeImage::getHTMLSingleStep(string host)
|
||||||
|
{
|
||||||
|
string result;
|
||||||
|
result = "Raw Image: <br>\n<img src=\"" + host + "/img_tmp/raw.jpg\">\n";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wird bei jeder Auswertrunde aufgerufen
|
||||||
|
bool ClassFlowTakeImage::doFlow(string zwtime)
|
||||||
|
{
|
||||||
|
psram_init_shared_memory_for_take_image_step();
|
||||||
|
|
||||||
|
string logPath = CreateLogFolder(zwtime);
|
||||||
|
|
||||||
|
int flash_duration = (int)(CCstatus.WaitBeforePicture * 1000);
|
||||||
|
|
||||||
|
#ifdef DEBUG_DETAIL_ON
|
||||||
|
LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - Before takePictureWithFlash");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WIFITURNOFF
|
||||||
|
esp_wifi_stop(); // to save power usage and
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// wenn die Kameraeinstellungen durch Erstellen eines neuen Referenzbildes verändert wurden, müssen sie neu gesetzt werden
|
||||||
|
if (CFstatus.changedCameraSettings)
|
||||||
|
{
|
||||||
|
Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera
|
||||||
|
Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize);
|
||||||
|
CFstatus.changedCameraSettings = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
takePictureWithFlash(flash_duration);
|
||||||
|
|
||||||
|
#ifdef WIFITURNOFF
|
||||||
|
esp_wifi_start();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef DEBUG_DETAIL_ON
|
||||||
|
LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - After takePictureWithFlash");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LogImage(logPath, "raw", NULL, NULL, zwtime, rawImage);
|
||||||
|
|
||||||
|
RemoveOldLogs();
|
||||||
|
|
||||||
|
#ifdef DEBUG_DETAIL_ON
|
||||||
|
LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - After RemoveOldLogs");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
psram_deinit_shared_memory_for_take_image_step();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t ClassFlowTakeImage::SendRawJPG(httpd_req_t *req)
|
||||||
|
{
|
||||||
|
int flash_duration = (int)(CCstatus.WaitBeforePicture * 1000);
|
||||||
|
time(&TimeImageTaken);
|
||||||
|
localtime(&TimeImageTaken);
|
||||||
|
|
||||||
|
return Camera.CaptureToHTTP(req, flash_duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageData *ClassFlowTakeImage::SendRawImage(void)
|
||||||
|
{
|
||||||
|
CImageBasis *zw = new CImageBasis("SendRawImage", rawImage);
|
||||||
|
ImageData *id;
|
||||||
|
int flash_duration = (int)(CCstatus.WaitBeforePicture * 1000);
|
||||||
|
Camera.CaptureToBasisImage(zw, flash_duration);
|
||||||
|
time(&TimeImageTaken);
|
||||||
|
localtime(&TimeImageTaken);
|
||||||
|
|
||||||
|
id = zw->writeToMemoryAsJPG();
|
||||||
|
delete zw;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t ClassFlowTakeImage::getTimeImageTaken(void)
|
||||||
|
{
|
||||||
|
return TimeImageTaken;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassFlowTakeImage::~ClassFlowTakeImage(void)
|
||||||
|
{
|
||||||
|
delete rawImage;
|
||||||
|
}
|
||||||
40
code/components/jomjol_flowcontroll/ClassFlowTakeImage.h
Normal file
40
code/components/jomjol_flowcontroll/ClassFlowTakeImage.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef CLASSFFLOWTAKEIMAGE_H
|
||||||
|
#define CLASSFFLOWTAKEIMAGE_H
|
||||||
|
|
||||||
|
#include "ClassFlowImage.h"
|
||||||
|
#include "ClassControllCamera.h"
|
||||||
|
#include "../../include/defines.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class ClassFlowTakeImage : public ClassFlowImage
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
time_t TimeImageTaken;
|
||||||
|
string namerawimage;
|
||||||
|
|
||||||
|
esp_err_t camera_capture(void);
|
||||||
|
void takePictureWithFlash(int flash_duration);
|
||||||
|
|
||||||
|
void SetInitialParameter(void);
|
||||||
|
|
||||||
|
public:
|
||||||
|
CImageBasis *rawImage;
|
||||||
|
|
||||||
|
ClassFlowTakeImage(std::vector<ClassFlow *> *lfc);
|
||||||
|
|
||||||
|
bool ReadParameter(FILE *pfile, string &aktparamgraph);
|
||||||
|
bool doFlow(string time);
|
||||||
|
string getHTMLSingleStep(string host);
|
||||||
|
time_t getTimeImageTaken(void);
|
||||||
|
string name() { return "ClassFlowTakeImage"; };
|
||||||
|
|
||||||
|
ImageData *SendRawImage(void);
|
||||||
|
esp_err_t SendRawJPG(httpd_req_t *req);
|
||||||
|
|
||||||
|
~ClassFlowTakeImage(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CLASSFFLOWTAKEIMAGE_H
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
#include <sstream>
|
|
||||||
#include "ClassFlowWriteList.h"
|
|
||||||
#include "Helper.h"
|
|
||||||
|
|
||||||
#include "time_sntp.h"
|
|
||||||
#include "../../include/defines.h"
|
|
||||||
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
void ClassFlowWriteList::SetInitialParameter(void)
|
|
||||||
{
|
|
||||||
flowpostprocessing = NULL;
|
|
||||||
previousElement = NULL;
|
|
||||||
ListFlowControll = NULL;
|
|
||||||
disabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassFlowWriteList::ClassFlowWriteList()
|
|
||||||
{
|
|
||||||
SetInitialParameter();
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassFlowWriteList::ClassFlowWriteList(std::vector<ClassFlow*>* lfc)
|
|
||||||
{
|
|
||||||
SetInitialParameter();
|
|
||||||
|
|
||||||
ListFlowControll = lfc;
|
|
||||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
|
||||||
{
|
|
||||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
|
|
||||||
{
|
|
||||||
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool ClassFlowWriteList::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|
||||||
{
|
|
||||||
std::vector<string> splitted;
|
|
||||||
|
|
||||||
aktparamgraph = trim(aktparamgraph);
|
|
||||||
|
|
||||||
if (aktparamgraph.size() == 0)
|
|
||||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (toUpper(aktparamgraph).compare("[MQTT]") != 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
|
||||||
{
|
|
||||||
splitted = ZerlegeZeile(aktparamgraph);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool ClassFlowWriteList::doFlow(string zwtime)
|
|
||||||
{
|
|
||||||
std::string line = "";
|
|
||||||
|
|
||||||
std::string result;
|
|
||||||
std::string resulterror = "";
|
|
||||||
std::string resultraw = "";
|
|
||||||
std::string resultrate = "";
|
|
||||||
std::string resulttimestamp = "";
|
|
||||||
string zw = "";
|
|
||||||
string namenumber = "";
|
|
||||||
|
|
||||||
if (flowpostprocessing)
|
|
||||||
{
|
|
||||||
std::vector<NumberPost*>* NUMBERS = flowpostprocessing->GetNumbers();
|
|
||||||
|
|
||||||
for (int i = 0; i < (*NUMBERS).size(); ++i)
|
|
||||||
{
|
|
||||||
result = (*NUMBERS)[i]->ReturnValue;
|
|
||||||
resultraw = (*NUMBERS)[i]->ReturnRawValue;
|
|
||||||
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
|
||||||
resultrate = (*NUMBERS)[i]->ReturnRateValue;
|
|
||||||
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
|
||||||
|
|
||||||
line = line + resulttimestamp + "\t" + resultraw + "\t" + result + "\t" + resultraw + "\t" + resultrate + "\t" + resulttimestamp + "\t";
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef CLASSFFLOWPWRITELIST_H
|
|
||||||
#define CLASSFFLOWPWRITELIST_H
|
|
||||||
|
|
||||||
#include "ClassFlow.h"
|
|
||||||
#include "ClassFlowPostProcessing.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class ClassFlowWriteList :
|
|
||||||
public ClassFlow
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
ClassFlowPostProcessing* flowpostprocessing;
|
|
||||||
void SetInitialParameter(void);
|
|
||||||
|
|
||||||
public:
|
|
||||||
ClassFlowWriteList();
|
|
||||||
ClassFlowWriteList(std::vector<ClassFlow*>* lfc);
|
|
||||||
|
|
||||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
|
||||||
bool doFlow(string time);
|
|
||||||
string name(){return "ClassFlowWriteList";};
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //CLASSFFLOWPWRITELIST_H
|
|
||||||
1653
code/components/jomjol_flowcontroll/MainFlowControl.cpp
Normal file
1653
code/components/jomjol_flowcontroll/MainFlowControl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
87
code/components/jomjol_flowcontroll/MainFlowControl.h
Normal file
87
code/components/jomjol_flowcontroll/MainFlowControl.h
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef MAINFLOWCONTROL_H
|
||||||
|
#define MAINFLOWCONTROL_H
|
||||||
|
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <esp_http_server.h>
|
||||||
|
#include "CImageBasis.h"
|
||||||
|
#include "ClassFlowControll.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
framesize_t ImageFrameSize = FRAMESIZE_VGA; // 0 - 10
|
||||||
|
gainceiling_t ImageGainceiling; // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128)
|
||||||
|
|
||||||
|
int ImageQuality; // 0 - 63
|
||||||
|
int ImageBrightness; // (-2 to 2) - set brightness
|
||||||
|
int ImageContrast; //-2 - 2
|
||||||
|
int ImageSaturation; //-2 - 2
|
||||||
|
int ImageSharpness; //-2 - 2
|
||||||
|
bool ImageAutoSharpness;
|
||||||
|
int ImageSpecialEffect; // 0 - 6
|
||||||
|
int ImageWbMode; // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
|
||||||
|
int ImageAwb; // white balance enable (0 or 1)
|
||||||
|
int ImageAwbGain; // Auto White Balance enable (0 or 1)
|
||||||
|
int ImageAec; // auto exposure off (1 or 0)
|
||||||
|
int ImageAec2; // automatic exposure sensor (0 or 1)
|
||||||
|
int ImageAeLevel; // auto exposure levels (-2 to 2)
|
||||||
|
int ImageAecValue; // set exposure manually (0-1200)
|
||||||
|
int ImageAgc; // auto gain off (1 or 0)
|
||||||
|
int ImageAgcGain; // set gain manually (0 - 30)
|
||||||
|
int ImageBpc; // black pixel correction
|
||||||
|
int ImageWpc; // white pixel correction
|
||||||
|
int ImageRawGma; // (1 or 0)
|
||||||
|
int ImageLenc; // lens correction (1 or 0)
|
||||||
|
int ImageHmirror; // (0 or 1) flip horizontally
|
||||||
|
int ImageVflip; // Invert image (0 or 1)
|
||||||
|
int ImageDcw; // downsize enable (1 or 0)
|
||||||
|
|
||||||
|
int ImageWidth;
|
||||||
|
int ImageHeight;
|
||||||
|
|
||||||
|
int ImageLedIntensity;
|
||||||
|
|
||||||
|
bool ImageZoomEnabled;
|
||||||
|
int ImageZoomMode;
|
||||||
|
int ImageZoomOffsetX;
|
||||||
|
int ImageZoomOffsetY;
|
||||||
|
int ImageZoomSize;
|
||||||
|
|
||||||
|
int WaitBeforePicture;
|
||||||
|
bool isImageSize;
|
||||||
|
|
||||||
|
bool CameraInitSuccessful;
|
||||||
|
bool changedCameraSettings;
|
||||||
|
bool DemoMode;
|
||||||
|
bool SaveAllFiles;
|
||||||
|
} camera_flow_config_temp_t;
|
||||||
|
|
||||||
|
extern camera_flow_config_temp_t CFstatus;
|
||||||
|
extern ClassFlowControll flowctrl;
|
||||||
|
|
||||||
|
esp_err_t setCCstatusToCFstatus(void); // CCstatus >>> CFstatus
|
||||||
|
esp_err_t setCFstatusToCCstatus(void); // CFstatus >>> CCstatus
|
||||||
|
esp_err_t setCFstatusToCam(void); // CFstatus >>> Kamera
|
||||||
|
|
||||||
|
void register_server_main_flow_task_uri(httpd_handle_t server);
|
||||||
|
|
||||||
|
void CheckIsPlannedReboot(void);
|
||||||
|
bool getIsPlannedReboot(void);
|
||||||
|
|
||||||
|
void InitializeFlowTask(void);
|
||||||
|
void DeleteMainFlowTask(void);
|
||||||
|
bool isSetupModusActive(void);
|
||||||
|
|
||||||
|
int getCountFlowRounds(void);
|
||||||
|
|
||||||
|
#ifdef ENABLE_MQTT
|
||||||
|
esp_err_t MQTTCtrlFlowStart(std::string _topic);
|
||||||
|
#endif // ENABLE_MQTT
|
||||||
|
|
||||||
|
esp_err_t GetRawJPG(httpd_req_t *req);
|
||||||
|
esp_err_t GetJPG(std::string _filename, httpd_req_t *req);
|
||||||
|
|
||||||
|
#endif // MAINFLOWCONTROL_H
|
||||||
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
|||||||
|
|
||||||
idf_component_register(SRCS ${app_sources}
|
idf_component_register(SRCS ${app_sources}
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "."
|
||||||
REQUIRES tflite-lib jomjol_logfile fatfs sdmmc)
|
REQUIRES esp_timer esp-tflite-micro jomjol_logfile fatfs sdmmc vfs)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "sdmmc_cmd.h"
|
#include "sdmmc_cmd.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@@ -76,6 +77,8 @@ enum SystemStatusFlag_t { // One bit per error
|
|||||||
SYSTEM_STATUS_PSRAM_BAD = 1 << 0, // 1, Critical Error
|
SYSTEM_STATUS_PSRAM_BAD = 1 << 0, // 1, Critical Error
|
||||||
SYSTEM_STATUS_HEAP_TOO_SMALL = 1 << 1, // 2, Critical Error
|
SYSTEM_STATUS_HEAP_TOO_SMALL = 1 << 1, // 2, Critical Error
|
||||||
SYSTEM_STATUS_CAM_BAD = 1 << 2, // 4, Critical Error
|
SYSTEM_STATUS_CAM_BAD = 1 << 2, // 4, Critical Error
|
||||||
|
SYSTEM_STATUS_SDCARD_CHECK_BAD = 1 << 3, // 8, Critical Error
|
||||||
|
SYSTEM_STATUS_FOLDER_CHECK_BAD = 1 << 4, // 16, Critical Error
|
||||||
|
|
||||||
// Second Byte
|
// Second Byte
|
||||||
SYSTEM_STATUS_CAM_FB_BAD = 1 << (0+8), // 8, Flow still might work
|
SYSTEM_STATUS_CAM_FB_BAD = 1 << (0+8), // 8, Flow still might work
|
||||||
@@ -93,4 +96,10 @@ std::string getFormatedUptime(bool compact);
|
|||||||
|
|
||||||
const char* get404(void);
|
const char* get404(void);
|
||||||
|
|
||||||
|
std::string UrlDecode(const std::string& value);
|
||||||
|
|
||||||
|
bool replaceString(std::string& s, std::string const& toReplace, std::string const& replaceWith);
|
||||||
|
bool replaceString(std::string& s, std::string const& toReplace, std::string const& replaceWith, bool logIt);
|
||||||
|
bool isInString(std::string& s, std::string const& toFind);
|
||||||
|
|
||||||
#endif //HELPER_H
|
#endif //HELPER_H
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#ifdef DEBUG_ENABLE_SYSINFO
|
#ifdef DEBUG_ENABLE_SYSINFO
|
||||||
|
|
||||||
#include "esp_sys.h"
|
#include "esp_sys.h"
|
||||||
|
#include "esp_chip_info.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -121,7 +122,7 @@ std::string get_device_info()
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_HIMEM_IF_AVAILABLE
|
#ifdef USE_HIMEM_IF_AVAILABLE
|
||||||
sprintf(aMsgBuf,"spiram size %u\n", esp_spiram_get_size());
|
sprintf(aMsgBuf,"spiram size %u\n", esp_psram_get_size());
|
||||||
espInfoResultStr += std::string(aMsgBuf);
|
espInfoResultStr += std::string(aMsgBuf);
|
||||||
sprintf(aMsgBuf,"himem free %u\n", esp_himem_get_free_size());
|
sprintf(aMsgBuf,"himem free %u\n", esp_himem_get_free_size());
|
||||||
espInfoResultStr += std::string(aMsgBuf);
|
espInfoResultStr += std::string(aMsgBuf);
|
||||||
|
|||||||
@@ -16,9 +16,9 @@
|
|||||||
#include <esp_spi_flash.h>
|
#include <esp_spi_flash.h>
|
||||||
#include <esp_heap_caps.h>
|
#include <esp_heap_caps.h>
|
||||||
|
|
||||||
// for esp_spiram_get_size
|
// for esp_psram_get_size
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <esp32/spiram.h>
|
#include "esp_psram.h"
|
||||||
#ifdef USE_HIMEM_IF_AVAILABLE
|
#ifdef USE_HIMEM_IF_AVAILABLE
|
||||||
#include <esp32/himem.h>
|
#include <esp32/himem.h>
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
211
code/components/jomjol_helper/psram.cpp
Normal file
211
code/components/jomjol_helper/psram.cpp
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
#include "ClassLogFile.h"
|
||||||
|
#include "../../include/defines.h"
|
||||||
|
#include "psram.h"
|
||||||
|
|
||||||
|
static const char* TAG = "PSRAM";
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
|
void *shared_region = NULL;
|
||||||
|
uint32_t allocatedBytesForSTBI = 0;
|
||||||
|
std::string sharedMemoryInUseFor = "";
|
||||||
|
|
||||||
|
|
||||||
|
/** Reserve a large block in the PSRAM which will be shared between the different steps.
|
||||||
|
* Each step uses it differently but only wiuthin itself. */
|
||||||
|
bool reserve_psram_shared_region(void) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating shared PSRAM region (" +
|
||||||
|
std::to_string(TENSOR_ARENA_SIZE + MAX_MODEL_SIZE) + " bytes)...");
|
||||||
|
shared_region = malloc_psram_heap("Shared PSRAM region", TENSOR_ARENA_SIZE + MAX_MODEL_SIZE,
|
||||||
|
MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||||
|
|
||||||
|
if (shared_region == NULL) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to allocating shared PSRAM region!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* Memory used in Take Image (STBI)
|
||||||
|
*******************************************************************/
|
||||||
|
bool psram_init_shared_memory_for_take_image_step(void) {
|
||||||
|
if (sharedMemoryInUseFor != "") {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM already in use for " + sharedMemoryInUseFor + "!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init shared memory for step 'Take Image' (STBI buffers)");
|
||||||
|
allocatedBytesForSTBI = 0;
|
||||||
|
sharedMemoryInUseFor = "TakeImage";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void psram_deinit_shared_memory_for_take_image_step(void) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Deinit shared memory for step 'Take Image' (STBI buffers)");
|
||||||
|
allocatedBytesForSTBI = 0;
|
||||||
|
sharedMemoryInUseFor = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void *psram_reserve_shared_stbi_memory(size_t size) {
|
||||||
|
/* Only large buffers should be placed in the shared PSRAM
|
||||||
|
* If we also place all smaller STBI buffers here, we get artefacts for some reasons. */
|
||||||
|
if (size >= 100000) {
|
||||||
|
if ((allocatedBytesForSTBI + size) > TENSOR_ARENA_SIZE + MAX_MODEL_SIZE) { // Check if it still fits in the shared region
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM too small (STBI) to fit additional " +
|
||||||
|
std::to_string(size) + " bytes! Available: " + std::to_string(TENSOR_ARENA_SIZE + MAX_MODEL_SIZE - allocatedBytesForSTBI) + " bytes!");
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating memory (" + std::to_string(size) + " bytes) for STBI (use shared memory in PSRAM)...");
|
||||||
|
allocatedBytesForSTBI += size;
|
||||||
|
return (uint8_t *)shared_region + allocatedBytesForSTBI - size;
|
||||||
|
}
|
||||||
|
else { // Normal PSRAM
|
||||||
|
return malloc_psram_heap("STBI", size, MALLOC_CAP_SPIRAM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void *psram_reallocate_shared_stbi_memory(void *ptr, size_t newsize) {
|
||||||
|
char buf[20];
|
||||||
|
sprintf(buf, "%p", ptr);
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "STBI requested realloc for " + std::string(buf) + " but this is currently unsupported!");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void psram_free_shared_stbi_memory(void *p) {
|
||||||
|
if ((p >= shared_region) && (p <= ((uint8_t *)shared_region + allocatedBytesForSTBI))) { // was allocated inside the shared memory
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Part of shared memory used for STBI (PSRAM, part of shared memory) is free again");
|
||||||
|
}
|
||||||
|
else { // Normal PSRAM
|
||||||
|
free_psram_heap("STBI", p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* Memory used in Aligning Step
|
||||||
|
* During this step we only use the shared part of the PSRAM
|
||||||
|
* for the tmpImage.
|
||||||
|
*******************************************************************/
|
||||||
|
void *psram_reserve_shared_tmp_image_memory(void) {
|
||||||
|
if (sharedMemoryInUseFor != "") {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM already in use for " + sharedMemoryInUseFor + "!");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating tmpImage (" + std::to_string(IMAGE_SIZE) + " bytes, use shared memory in PSRAM)...");
|
||||||
|
sharedMemoryInUseFor = "Aligning";
|
||||||
|
return shared_region; // Use 1th part of the shared memory for the tmpImage (only user)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void psram_free_shared_temp_image_memory(void) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Shared memory used for tmpImage (PSRAM, part of shared memory) is free again");
|
||||||
|
sharedMemoryInUseFor = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* Memory used in Digitalization Steps
|
||||||
|
* During this step we only use the shared part of the PSRAM for the
|
||||||
|
* Tensor Arena and one of the Models.
|
||||||
|
* The shared memory is large enough for the largest model and the
|
||||||
|
* Tensor Arena. Therefore we do not need to monitor the usage.
|
||||||
|
*******************************************************************/
|
||||||
|
void *psram_get_shared_tensor_arena_memory(void) {
|
||||||
|
if ((sharedMemoryInUseFor == "") || (sharedMemoryInUseFor == "Digitalization_Model")) {
|
||||||
|
sharedMemoryInUseFor = "Digitalization_Tensor";
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating Tensor Arena (" + std::to_string(TENSOR_ARENA_SIZE) + " bytes, use shared memory in PSRAM)...");
|
||||||
|
return shared_region; // Use 1th part of the shared memory for Tensor
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM already in use for " + sharedMemoryInUseFor + "!");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void *psram_get_shared_model_memory(void) {
|
||||||
|
if ((sharedMemoryInUseFor == "") || (sharedMemoryInUseFor == "Digitalization_Tensor")) {
|
||||||
|
sharedMemoryInUseFor = "Digitalization_Model";
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating Model memory (" + std::to_string(MAX_MODEL_SIZE) + " bytes, use shared memory in PSRAM)...");
|
||||||
|
return (uint8_t *)shared_region + TENSOR_ARENA_SIZE; // Use 2nd part of the shared memory (after Tensor Arena) for the model
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM already in use for " + sharedMemoryInUseFor + "!");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void psram_free_shared_tensor_arena_and_model_memory(void) {
|
||||||
|
sharedMemoryInUseFor = "";
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Shared memory used for Tensor Arena and model (PSRAM, part of shared memory) is free again");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* General
|
||||||
|
*******************************************************************/
|
||||||
|
void *malloc_psram_heap(std::string name, size_t size, uint32_t caps) {
|
||||||
|
void *ptr;
|
||||||
|
|
||||||
|
ptr = heap_caps_malloc(size, caps);
|
||||||
|
if (ptr != NULL) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocated " + to_string(size) + " bytes in PSRAM for '" + name + "'");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to allocate " + to_string(size) + " bytes in PSRAM for '" + name + "'!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void *realloc_psram_heap(std::string name, void *ptr, size_t size, uint32_t caps) {
|
||||||
|
ptr = heap_caps_realloc(ptr, size, caps);
|
||||||
|
if (ptr != NULL) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Reallocated " + to_string(size) + " bytes in PSRAM for '" + name + "'");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to reallocate " + to_string(size) + " bytes in PSRAM for '" + name + "'!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void *calloc_psram_heap(std::string name, size_t n, size_t size, uint32_t caps) {
|
||||||
|
void *ptr;
|
||||||
|
|
||||||
|
ptr = heap_caps_calloc(n, size, caps);
|
||||||
|
if (ptr != NULL) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Allocated " + to_string(size) + " bytes in PSRAM for '" + name + "'");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to allocate " + to_string(size) + " bytes in PSRAM for '" + name + "'!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void free_psram_heap(std::string name, void *ptr) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Freeing memory in PSRAM used for '" + name + "'...");
|
||||||
|
heap_caps_free(ptr);
|
||||||
|
}
|
||||||
35
code/components/jomjol_helper/psram.h
Normal file
35
code/components/jomjol_helper/psram.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifndef PSRAM_h
|
||||||
|
#define PSRAM_h
|
||||||
|
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
|
|
||||||
|
|
||||||
|
bool reserve_psram_shared_region(void);
|
||||||
|
|
||||||
|
|
||||||
|
/* Memory used in Take Image Step */
|
||||||
|
bool psram_init_shared_memory_for_take_image_step(void);
|
||||||
|
void psram_deinit_shared_memory_for_take_image_step(void);
|
||||||
|
void *psram_reserve_shared_stbi_memory(size_t size);
|
||||||
|
void *psram_reallocate_shared_stbi_memory(void *ptr, size_t newsize);
|
||||||
|
void psram_free_shared_stbi_memory(void *p);
|
||||||
|
|
||||||
|
|
||||||
|
/* Memory used in Aligning Step */
|
||||||
|
void *psram_reserve_shared_tmp_image_memory(void);
|
||||||
|
void psram_free_shared_temp_image_memory(void);
|
||||||
|
|
||||||
|
/* Memory used in Digitalization Steps */
|
||||||
|
void *psram_get_shared_tensor_arena_memory(void);
|
||||||
|
void *psram_get_shared_model_memory(void);
|
||||||
|
void psram_free_shared_tensor_arena_and_model_memory(void);
|
||||||
|
|
||||||
|
/* General */
|
||||||
|
void *malloc_psram_heap(std::string name, size_t size, uint32_t caps);
|
||||||
|
void *realloc_psram_heap(std::string name, void *ptr, size_t size, uint32_t caps);
|
||||||
|
void *calloc_psram_heap(std::string name, size_t n, size_t size, uint32_t caps);
|
||||||
|
|
||||||
|
void free_psram_heap(std::string name, void *ptr);
|
||||||
|
|
||||||
|
#endif // PSRAM_h
|
||||||
161
code/components/jomjol_helper/sdcard_check.cpp
Normal file
161
code/components/jomjol_helper/sdcard_check.cpp
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
#include "sdcard_check.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "esp_rom_crc.h"
|
||||||
|
#include "ClassLogFile.h"
|
||||||
|
|
||||||
|
static const char *TAG = "SDCARD";
|
||||||
|
|
||||||
|
int SDCardCheckRW(void)
|
||||||
|
{
|
||||||
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Basic R/W check started...");
|
||||||
|
FILE* pFile = NULL;
|
||||||
|
int iCRCMessage = 0;
|
||||||
|
|
||||||
|
pFile = fopen("/sdcard/sdcheck.txt","w");
|
||||||
|
if (pFile == NULL) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Basic R/W check: (E1) No able to open file to write");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::string sMessage = "This message is used for a SD-Card basic check!";
|
||||||
|
iCRCMessage = esp_rom_crc16_le(0, (uint8_t*)sMessage.c_str(), sMessage.length());
|
||||||
|
if (fwrite(sMessage.c_str(), sMessage.length(), 1, pFile) == 0 ) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Basic R/W check: (E2) Not able to write file");
|
||||||
|
fclose(pFile);
|
||||||
|
unlink("/sdcard/sdcheck.txt");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
fclose(pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
pFile = fopen("/sdcard/sdcheck.txt","r");
|
||||||
|
if (pFile == NULL) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Basic R/W check: (E3) Not able to open file to read back");
|
||||||
|
unlink("/sdcard/sdcheck.txt");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
char cReadBuf[50];
|
||||||
|
if (fgets(cReadBuf, sizeof(cReadBuf), pFile) == 0) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Basic R/W check: (E4) Not able to read file back");
|
||||||
|
fclose(pFile);
|
||||||
|
unlink("/sdcard/sdcheck.txt");
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (esp_rom_crc16_le(0, (uint8_t*)cReadBuf, strlen(cReadBuf)) != iCRCMessage) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Basic R/W check: (E5) Read back, but wrong CRC");
|
||||||
|
fclose(pFile);
|
||||||
|
unlink("/sdcard/sdcheck.txt");
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlink("/sdcard/sdcheck.txt") != 0) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Basic R/W check: (E6) Unable to delete the file");
|
||||||
|
return -6;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Basic R/W check successful");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool SDCardCheckFolderFilePresence()
|
||||||
|
{
|
||||||
|
struct stat sb;
|
||||||
|
bool bRetval = true;
|
||||||
|
|
||||||
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Folder/file presence check started...");
|
||||||
|
/* check if folder exists: config */
|
||||||
|
if (stat("/sdcard/config", &sb) != 0) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: Folder /config not found");
|
||||||
|
bRetval = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if folder exists: html */
|
||||||
|
if (stat("/sdcard/html", &sb) != 0) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: Folder /html not found");
|
||||||
|
bRetval = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if folder exists: firmware */
|
||||||
|
if (stat("/sdcard/firmware", &sb) != 0) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: Folder /firmware not found");
|
||||||
|
bRetval = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if folder exists: img_tmp */
|
||||||
|
if (stat("/sdcard/img_tmp", &sb) != 0) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: Folder /img_tmp not found");
|
||||||
|
bRetval = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if folder exists: log */
|
||||||
|
if (stat("/sdcard/log", &sb) != 0) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: Folder /log not found");
|
||||||
|
bRetval = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if folder exists: demo */
|
||||||
|
if (stat("/sdcard/demo", &sb) != 0) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: Folder /demo not found");
|
||||||
|
bRetval = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if file exists: wlan.ini */
|
||||||
|
if (stat("/sdcard/wlan.ini", &sb) != 0) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: File /wlan.ini not found");
|
||||||
|
bRetval = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if file exists: config.ini */
|
||||||
|
if (stat("/sdcard/config/config.ini", &sb) != 0) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: File /config/config.ini not found");
|
||||||
|
bRetval = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if file exists: index.html */
|
||||||
|
if (stat("/sdcard/html/index.html", &sb) != 0) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: File /html/index.html not found");
|
||||||
|
bRetval = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if file exists: ota.html */
|
||||||
|
if (stat("/sdcard/html/ota_page.html", &sb) != 0) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: File /html/ota.html not found");
|
||||||
|
bRetval = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if file exists: log.html */
|
||||||
|
if (stat("/sdcard/html/log.html", &sb) != 0) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: File /html/log.html not found");
|
||||||
|
bRetval = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if file exists: common.js */
|
||||||
|
if (stat("/sdcard/html/common.js", &sb) != 0) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: File /html/common.js not found");
|
||||||
|
bRetval = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if file exists: version.txt */
|
||||||
|
if (stat("/sdcard/html/version.txt", &sb) != 0) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: File /html/version.txt not found");
|
||||||
|
bRetval = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bRetval)
|
||||||
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Folder/file presence check successful");
|
||||||
|
|
||||||
|
return bRetval;
|
||||||
|
}
|
||||||
11
code/components/jomjol_helper/sdcard_check.h
Normal file
11
code/components/jomjol_helper/sdcard_check.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef COMPONENTS_HELPER_SDCARD_CHECK_H
|
||||||
|
#define COMPONENTS_HELPER_SDCARD_CHECK_H
|
||||||
|
|
||||||
|
#include "../../include/defines.h"
|
||||||
|
|
||||||
|
int SDCardCheckRW(void);
|
||||||
|
bool SDCardCheckFolderFilePresence(void);
|
||||||
|
|
||||||
|
#endif /* COMPONENTS_HELPER_SDCARD_CHECK_H */
|
||||||
651
code/components/jomjol_helper/sdcard_init.c
Normal file
651
code/components/jomjol_helper/sdcard_init.c
Normal file
@@ -0,0 +1,651 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "sdcard_init.h"
|
||||||
|
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "ffconf.h"
|
||||||
|
#include "esp_compiler.h"
|
||||||
|
#include "esp_vfs.h"
|
||||||
|
#include "vfs_fat_internal.h"
|
||||||
|
#include "diskio_impl.h"
|
||||||
|
#include "diskio_sdmmc.h"
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
#include "driver/sdmmc_defs.h"
|
||||||
|
|
||||||
|
#if SOC_SDMMC_HOST_SUPPORTED
|
||||||
|
#include "driver/sdmmc_host.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static sdmmc_card_t* s_cards[FF_VOLUMES] = { NULL };
|
||||||
|
static bool s_disk_status_check_en[FF_VOLUMES] = { };
|
||||||
|
|
||||||
|
static const char* TAG = "sdcard_init";
|
||||||
|
|
||||||
|
#define CHECK_EXECUTE_RESULT(err, str) do { \
|
||||||
|
if ((err) !=ESP_OK) { \
|
||||||
|
ESP_LOGE(TAG, str" (0x%x).", err); \
|
||||||
|
goto cleanup; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
typedef struct vfs_fat_sd_ctx_t {
|
||||||
|
BYTE pdrv; //Drive number that is mounted
|
||||||
|
esp_vfs_fat_mount_config_t mount_config; //Mount configuration
|
||||||
|
FATFS *fs; //FAT structure pointer that is registered
|
||||||
|
sdmmc_card_t *card; //Card info
|
||||||
|
char *base_path; //Path where partition is registered
|
||||||
|
} vfs_fat_sd_ctx_t;
|
||||||
|
|
||||||
|
static vfs_fat_sd_ctx_t *s_ctx[FF_VOLUMES] = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This `s_saved_ctx_id` is only used by `esp_vfs_fat_sdmmc_unmount`, which is deprecated.
|
||||||
|
* This variable together with `esp_vfs_fat_sdmmc_unmount` should be removed in next major version
|
||||||
|
*/
|
||||||
|
static uint32_t s_saved_ctx_id = FF_VOLUMES;
|
||||||
|
|
||||||
|
|
||||||
|
static void call_host_deinit_mh(const sdmmc_host_t *host_config);
|
||||||
|
static esp_err_t partition_card_mh(const esp_vfs_fat_mount_config_t *mount_config, const char *drv, sdmmc_card_t *card, BYTE pdrv);
|
||||||
|
|
||||||
|
|
||||||
|
//Check if SD/MMC card is present
|
||||||
|
static DSTATUS ff_sdmmc_card_available_mh(BYTE pdrv)
|
||||||
|
{
|
||||||
|
sdmmc_card_t* card = s_cards[pdrv];
|
||||||
|
assert(card);
|
||||||
|
esp_err_t err = sdmmc_get_status(card);
|
||||||
|
|
||||||
|
if (unlikely(err != ESP_OK)) {
|
||||||
|
ESP_LOGE(TAG, "Check status failed (0x%x)", err);
|
||||||
|
return STA_NOINIT;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ff_sdmmc_status() and ff_sdmmc_initialize() return STA_NOINIT when sdmmc_get_status()
|
||||||
|
* fails. This error value is checked throughout the FATFS code.
|
||||||
|
* Both functions return 0 on success.
|
||||||
|
*/
|
||||||
|
DSTATUS ff_sdmmc_initialize_mh (BYTE pdrv)
|
||||||
|
{
|
||||||
|
return ff_sdmmc_card_available_mh(pdrv);
|
||||||
|
}
|
||||||
|
|
||||||
|
DSTATUS ff_sdmmc_status_mh(BYTE pdrv)
|
||||||
|
{
|
||||||
|
if (s_disk_status_check_en[pdrv]) {
|
||||||
|
return ff_sdmmc_card_available_mh(pdrv);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DRESULT ff_sdmmc_read_mh (BYTE pdrv, BYTE* buff, DWORD sector, UINT count)
|
||||||
|
{
|
||||||
|
sdmmc_card_t* card = s_cards[pdrv];
|
||||||
|
assert(card);
|
||||||
|
esp_err_t err = sdmmc_read_sectors(card, buff, sector, count);
|
||||||
|
if (unlikely(err != ESP_OK)) {
|
||||||
|
ESP_LOGE(TAG, "sdmmc_read_blocks failed (%d)", err);
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
DRESULT ff_sdmmc_write_mh (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count)
|
||||||
|
{
|
||||||
|
sdmmc_card_t* card = s_cards[pdrv];
|
||||||
|
assert(card);
|
||||||
|
esp_err_t err = sdmmc_write_sectors(card, buff, sector, count);
|
||||||
|
if (unlikely(err != ESP_OK)) {
|
||||||
|
ESP_LOGE(TAG, "sdmmc_write_blocks failed (%d)", err);
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if FF_USE_TRIM
|
||||||
|
DRESULT ff_sdmmc_trim_mh (BYTE pdrv, DWORD start_sector, DWORD sector_count)
|
||||||
|
{
|
||||||
|
sdmmc_card_t* card = s_cards[pdrv];
|
||||||
|
assert(card);
|
||||||
|
sdmmc_erase_arg_t arg;
|
||||||
|
|
||||||
|
arg = sdmmc_can_discard(card) == ESP_OK ? SDMMC_DISCARD_ARG : SDMMC_ERASE_ARG;
|
||||||
|
esp_err_t err = sdmmc_erase_sectors(card, start_sector, sector_count, arg);
|
||||||
|
if (unlikely(err != ESP_OK)) {
|
||||||
|
ESP_LOGE(TAG, "sdmmc_erase_sectors failed (%d)", err);
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
#endif //FF_USE_TRIM
|
||||||
|
|
||||||
|
DRESULT ff_sdmmc_ioctl_mh (BYTE pdrv, BYTE cmd, void* buff)
|
||||||
|
{
|
||||||
|
sdmmc_card_t* card = s_cards[pdrv];
|
||||||
|
assert(card);
|
||||||
|
switch(cmd) {
|
||||||
|
case CTRL_SYNC:
|
||||||
|
return RES_OK;
|
||||||
|
case GET_SECTOR_COUNT:
|
||||||
|
*((DWORD*) buff) = card->csd.capacity;
|
||||||
|
return RES_OK;
|
||||||
|
case GET_SECTOR_SIZE:
|
||||||
|
*((WORD*) buff) = card->csd.sector_size;
|
||||||
|
return RES_OK;
|
||||||
|
case GET_BLOCK_SIZE:
|
||||||
|
return RES_ERROR;
|
||||||
|
#if FF_USE_TRIM
|
||||||
|
case CTRL_TRIM:
|
||||||
|
if (sdmmc_can_trim(card) != ESP_OK) {
|
||||||
|
return RES_PARERR;
|
||||||
|
}
|
||||||
|
return ff_sdmmc_trim_mh (pdrv, *((DWORD*)buff), //start_sector
|
||||||
|
(*((DWORD*)buff + 1) - *((DWORD*)buff) + 1)); //sector_count
|
||||||
|
#endif //FF_USE_TRIM
|
||||||
|
}
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ff_sdmmc_set_disk_status_check_mh(BYTE pdrv, bool enable)
|
||||||
|
{
|
||||||
|
s_disk_status_check_en[pdrv] = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ff_diskio_register_sdmmc_mh(BYTE pdrv, sdmmc_card_t* card)
|
||||||
|
{
|
||||||
|
static const ff_diskio_impl_t sdmmc_impl = {
|
||||||
|
.init = &ff_sdmmc_initialize_mh,
|
||||||
|
.status = &ff_sdmmc_status_mh,
|
||||||
|
.read = &ff_sdmmc_read_mh,
|
||||||
|
.write = &ff_sdmmc_write_mh,
|
||||||
|
.ioctl = &ff_sdmmc_ioctl_mh
|
||||||
|
};
|
||||||
|
s_cards[pdrv] = card;
|
||||||
|
s_disk_status_check_en[pdrv] = false;
|
||||||
|
ff_diskio_register(pdrv, &sdmmc_impl);
|
||||||
|
}
|
||||||
|
|
||||||
|
BYTE ff_diskio_get_pdrv_card_mh(const sdmmc_card_t* card)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < FF_VOLUMES; i++) {
|
||||||
|
if (card == s_cards[i]) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static bool s_get_context_id_by_card_mh(const sdmmc_card_t *card, uint32_t *out_id)
|
||||||
|
{
|
||||||
|
vfs_fat_sd_ctx_t *p_ctx = NULL;
|
||||||
|
for (int i = 0; i < FF_VOLUMES; i++) {
|
||||||
|
p_ctx = s_ctx[i];
|
||||||
|
if (p_ctx) {
|
||||||
|
if (p_ctx->card == card) {
|
||||||
|
*out_id = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t s_get_unused_context_id_mh(void)
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < FF_VOLUMES; i++) {
|
||||||
|
if (!s_ctx[i]) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FF_VOLUMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t mount_prepare_mem_mh(const char *base_path, BYTE *out_pdrv, char **out_dup_path, sdmmc_card_t** out_card)
|
||||||
|
{
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
char* dup_path = NULL;
|
||||||
|
sdmmc_card_t* card = NULL;
|
||||||
|
|
||||||
|
// connect SDMMC driver to FATFS
|
||||||
|
BYTE pdrv = FF_DRV_NOT_USED;
|
||||||
|
|
||||||
|
if (ff_diskio_get_drive(&pdrv) != ESP_OK || pdrv == FF_DRV_NOT_USED) {
|
||||||
|
ESP_LOGD(TAG, "the maximum count of volumes is already mounted");
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
// not using ff_memalloc here, as allocation in internal RAM is preferred
|
||||||
|
card = (sdmmc_card_t*)malloc(sizeof(sdmmc_card_t));
|
||||||
|
|
||||||
|
if (card == NULL) {
|
||||||
|
ESP_LOGD(TAG, "could not locate new sdmmc_card_t");
|
||||||
|
err = ESP_ERR_NO_MEM;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
dup_path = strdup(base_path);
|
||||||
|
|
||||||
|
if(!dup_path){
|
||||||
|
ESP_LOGD(TAG, "could not copy base_path");
|
||||||
|
err = ESP_ERR_NO_MEM;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_card = card;
|
||||||
|
*out_pdrv = pdrv;
|
||||||
|
*out_dup_path = dup_path;
|
||||||
|
return ESP_OK;
|
||||||
|
cleanup:
|
||||||
|
free(card);
|
||||||
|
free(dup_path);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t s_f_mount_mh(sdmmc_card_t *card, FATFS *fs, const char *drv, uint8_t pdrv, const esp_vfs_fat_mount_config_t *mount_config)
|
||||||
|
{
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
FRESULT res = f_mount(fs, drv, 1);
|
||||||
|
if (res != FR_OK) {
|
||||||
|
err = ESP_FAIL;
|
||||||
|
ESP_LOGW(TAG, "failed to mount card (%d)", res);
|
||||||
|
|
||||||
|
bool need_mount_again = (res == FR_NO_FILESYSTEM || res == FR_INT_ERR) && mount_config->format_if_mount_failed;
|
||||||
|
|
||||||
|
if (!need_mount_again) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = partition_card_mh(mount_config, drv, card, pdrv);
|
||||||
|
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGW(TAG, "mounting again");
|
||||||
|
res = f_mount(fs, drv, 0);
|
||||||
|
|
||||||
|
if (res != FR_OK) {
|
||||||
|
err = ESP_FAIL;
|
||||||
|
ESP_LOGD(TAG, "f_mount failed after formatting (%d)", res);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t mount_to_vfs_fat_mh(const esp_vfs_fat_mount_config_t *mount_config, sdmmc_card_t *card, uint8_t pdrv, const char *base_path, FATFS **out_fs)
|
||||||
|
{
|
||||||
|
FATFS *fs = NULL;
|
||||||
|
esp_err_t err;
|
||||||
|
ff_diskio_register_sdmmc_mh(pdrv, card);
|
||||||
|
ff_sdmmc_set_disk_status_check_mh(pdrv, mount_config->disk_status_check_enable);
|
||||||
|
ESP_LOGD(TAG, "using pdrv=%i", pdrv);
|
||||||
|
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||||
|
|
||||||
|
// connect FATFS to VFS
|
||||||
|
err = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs);
|
||||||
|
*out_fs = fs;
|
||||||
|
|
||||||
|
if (err == ESP_ERR_INVALID_STATE) {
|
||||||
|
// it's okay, already registered with VFS
|
||||||
|
} else if (err != ESP_OK) {
|
||||||
|
ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", err);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to mount partition
|
||||||
|
err = s_f_mount_mh(card, fs, drv, pdrv, mount_config);
|
||||||
|
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (fs) {
|
||||||
|
f_mount(NULL, drv, 0);
|
||||||
|
}
|
||||||
|
esp_vfs_fat_unregister_path(base_path);
|
||||||
|
ff_diskio_unregister(pdrv);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t partition_card_mh(const esp_vfs_fat_mount_config_t *mount_config, const char *drv, sdmmc_card_t *card, BYTE pdrv)
|
||||||
|
{
|
||||||
|
FRESULT res = FR_OK;
|
||||||
|
esp_err_t err;
|
||||||
|
const size_t workbuf_size = 4096;
|
||||||
|
void* workbuf = NULL;
|
||||||
|
ESP_LOGW(TAG, "partitioning card");
|
||||||
|
|
||||||
|
workbuf = ff_memalloc(workbuf_size);
|
||||||
|
|
||||||
|
if (workbuf == NULL) {
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
LBA_t plist[] = {100, 0, 0, 0};
|
||||||
|
res = f_fdisk(pdrv, plist, workbuf);
|
||||||
|
|
||||||
|
if (res != FR_OK) {
|
||||||
|
err = ESP_FAIL;
|
||||||
|
ESP_LOGD(TAG, "f_fdisk failed (%d)", res);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(card->csd.sector_size, mount_config->allocation_unit_size);
|
||||||
|
|
||||||
|
ESP_LOGW(TAG, "formatting card, allocation unit size=%d", alloc_unit_size);
|
||||||
|
const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, alloc_unit_size};
|
||||||
|
res = f_mkfs(drv, &opt, workbuf, workbuf_size);
|
||||||
|
|
||||||
|
if (res != FR_OK) {
|
||||||
|
err = ESP_FAIL;
|
||||||
|
ESP_LOGD(TAG, "f_mkfs failed (%d)", res);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(workbuf);
|
||||||
|
return ESP_OK;
|
||||||
|
fail:
|
||||||
|
free(workbuf);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if SOC_SDMMC_HOST_SUPPORTED
|
||||||
|
static esp_err_t init_sdmmc_host_mh(int slot, const void *slot_config, int *out_slot)
|
||||||
|
{
|
||||||
|
*out_slot = slot;
|
||||||
|
return sdmmc_host_init_slot(slot, (const sdmmc_slot_config_t*) slot_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_vfs_fat_sdmmc_mount_mh(const char* base_path, const sdmmc_host_t* host_config, const void* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card)
|
||||||
|
{
|
||||||
|
esp_err_t err;
|
||||||
|
vfs_fat_sd_ctx_t *ctx = NULL;
|
||||||
|
uint32_t ctx_id = FF_VOLUMES;
|
||||||
|
FATFS *fs = NULL;
|
||||||
|
int card_handle = -1; //uninitialized
|
||||||
|
sdmmc_card_t* card = NULL;
|
||||||
|
BYTE pdrv = FF_DRV_NOT_USED;
|
||||||
|
char* dup_path = NULL;
|
||||||
|
bool host_inited = false;
|
||||||
|
|
||||||
|
err = mount_prepare_mem_mh(base_path, &pdrv, &dup_path, &card);
|
||||||
|
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "mount_prepare failed");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = (*host_config->init)();
|
||||||
|
CHECK_EXECUTE_RESULT(err, "host init failed");
|
||||||
|
//deinit() needs to be called to revert the init
|
||||||
|
host_inited = true;
|
||||||
|
//If this failed (indicated by card_handle != -1), slot deinit needs to called()
|
||||||
|
//leave card_handle as is to indicate that (though slot deinit not implemented yet.
|
||||||
|
err = init_sdmmc_host_mh(host_config->slot, slot_config, &card_handle);
|
||||||
|
CHECK_EXECUTE_RESULT(err, "slot init failed");
|
||||||
|
|
||||||
|
// probe and initialize card
|
||||||
|
err = sdmmc_card_init(host_config, card);
|
||||||
|
CHECK_EXECUTE_RESULT(err, "sdmmc_card_init failed");
|
||||||
|
|
||||||
|
err = mount_to_vfs_fat_mh(mount_config, card, pdrv, dup_path, &fs);
|
||||||
|
CHECK_EXECUTE_RESULT(err, "mount_to_vfs failed");
|
||||||
|
|
||||||
|
if (out_card != NULL) {
|
||||||
|
*out_card = card;
|
||||||
|
}
|
||||||
|
|
||||||
|
//For deprecation backward compatibility
|
||||||
|
if (s_saved_ctx_id == FF_VOLUMES) {
|
||||||
|
s_saved_ctx_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = calloc(sizeof(vfs_fat_sd_ctx_t), 1);
|
||||||
|
|
||||||
|
if (!ctx) {
|
||||||
|
CHECK_EXECUTE_RESULT(ESP_ERR_NO_MEM, "no mem");
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->pdrv = pdrv;
|
||||||
|
memcpy(&ctx->mount_config, mount_config, sizeof(esp_vfs_fat_mount_config_t));
|
||||||
|
ctx->card = card;
|
||||||
|
ctx->base_path = dup_path;
|
||||||
|
ctx->fs = fs;
|
||||||
|
ctx_id = s_get_unused_context_id_mh();
|
||||||
|
assert(ctx_id != FF_VOLUMES);
|
||||||
|
s_ctx[ctx_id] = ctx;
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
cleanup:
|
||||||
|
if (host_inited) {
|
||||||
|
call_host_deinit_mh(host_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(card);
|
||||||
|
free(dup_path);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static esp_err_t init_sdspi_host_mh(int slot, const void *slot_config, int *out_slot)
|
||||||
|
{
|
||||||
|
esp_err_t err = sdspi_host_init_device((const sdspi_device_config_t*)slot_config, out_slot);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG,
|
||||||
|
"Failed to attach sdspi device onto an SPI bus (rc=0x%x), please initialize the \
|
||||||
|
bus first and check the device parameters."
|
||||||
|
, err);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_vfs_fat_sdspi_mount_mh(const char* base_path, const sdmmc_host_t* host_config_input, const sdspi_device_config_t* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card)
|
||||||
|
{
|
||||||
|
const sdmmc_host_t* host_config = host_config_input;
|
||||||
|
esp_err_t err;
|
||||||
|
vfs_fat_sd_ctx_t *ctx = NULL;
|
||||||
|
uint32_t ctx_id = FF_VOLUMES;
|
||||||
|
FATFS *fs = NULL;
|
||||||
|
int card_handle = -1; //uninitialized
|
||||||
|
bool host_inited = false;
|
||||||
|
BYTE pdrv = FF_DRV_NOT_USED;
|
||||||
|
sdmmc_card_t* card = NULL;
|
||||||
|
char* dup_path = NULL;
|
||||||
|
|
||||||
|
err = mount_prepare_mem_mh(base_path, &pdrv, &dup_path, &card);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "mount_prepare failed");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
//the init() function is usually empty, doesn't require any deinit to revert it
|
||||||
|
err = (*host_config->init)();
|
||||||
|
CHECK_EXECUTE_RESULT(err, "host init failed");
|
||||||
|
|
||||||
|
err = init_sdspi_host_mh(host_config->slot, slot_config, &card_handle);
|
||||||
|
CHECK_EXECUTE_RESULT(err, "slot init failed");
|
||||||
|
//Set `host_inited` to true to indicate that host_config->deinit() needs
|
||||||
|
//to be called to revert `init_sdspi_host`
|
||||||
|
host_inited = true;
|
||||||
|
|
||||||
|
//The `slot` argument inside host_config should be replaced by the SD SPI handled returned
|
||||||
|
//above. But the input pointer is const, so create a new variable.
|
||||||
|
|
||||||
|
sdmmc_host_t new_config;
|
||||||
|
|
||||||
|
if (card_handle != host_config->slot) {
|
||||||
|
new_config = *host_config_input;
|
||||||
|
host_config = &new_config;
|
||||||
|
new_config.slot = card_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// probe and initialize card
|
||||||
|
err = sdmmc_card_init(host_config, card);
|
||||||
|
CHECK_EXECUTE_RESULT(err, "sdmmc_card_init failed");
|
||||||
|
|
||||||
|
err = mount_to_vfs_fat_mh(mount_config, card, pdrv, dup_path, &fs);
|
||||||
|
CHECK_EXECUTE_RESULT(err, "mount_to_vfs failed");
|
||||||
|
|
||||||
|
if (out_card != NULL) {
|
||||||
|
*out_card = card;
|
||||||
|
}
|
||||||
|
|
||||||
|
//For deprecation backward compatibility
|
||||||
|
if (s_saved_ctx_id == FF_VOLUMES) {
|
||||||
|
s_saved_ctx_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = calloc(sizeof(vfs_fat_sd_ctx_t), 1);
|
||||||
|
|
||||||
|
if (!ctx) {
|
||||||
|
CHECK_EXECUTE_RESULT(ESP_ERR_NO_MEM, "no mem");
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->pdrv = pdrv;
|
||||||
|
memcpy(&ctx->mount_config, mount_config, sizeof(esp_vfs_fat_mount_config_t));
|
||||||
|
ctx->card = card;
|
||||||
|
ctx->base_path = dup_path;
|
||||||
|
ctx->fs = fs;
|
||||||
|
ctx_id = s_get_unused_context_id_mh();
|
||||||
|
assert(ctx_id != FF_VOLUMES);
|
||||||
|
s_ctx[ctx_id] = ctx;
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (host_inited) {
|
||||||
|
call_host_deinit_mh(host_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(card);
|
||||||
|
free(dup_path);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void call_host_deinit_mh(const sdmmc_host_t *host_config)
|
||||||
|
{
|
||||||
|
if (host_config->flags & SDMMC_HOST_FLAG_DEINIT_ARG) {
|
||||||
|
host_config->deinit_p(host_config->slot);
|
||||||
|
} else {
|
||||||
|
host_config->deinit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t unmount_card_core_mh(const char *base_path, sdmmc_card_t *card)
|
||||||
|
{
|
||||||
|
BYTE pdrv = ff_diskio_get_pdrv_card_mh(card);
|
||||||
|
|
||||||
|
if (pdrv == 0xff) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
// unmount
|
||||||
|
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||||
|
f_mount(0, drv, 0);
|
||||||
|
// release SD driver
|
||||||
|
ff_diskio_unregister(pdrv);
|
||||||
|
|
||||||
|
call_host_deinit_mh(&card->host);
|
||||||
|
free(card);
|
||||||
|
|
||||||
|
esp_err_t err = esp_vfs_fat_unregister_path(base_path);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_vfs_fat_sdmmc_unmount_mh(void)
|
||||||
|
{
|
||||||
|
esp_err_t err = unmount_card_core_mh(s_ctx[s_saved_ctx_id]->base_path, s_ctx[s_saved_ctx_id]->card);
|
||||||
|
free(s_ctx[s_saved_ctx_id]);
|
||||||
|
s_ctx[s_saved_ctx_id] = NULL;
|
||||||
|
s_saved_ctx_id = FF_VOLUMES;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_vfs_fat_sdcard_unmount_mh(const char *base_path, sdmmc_card_t *card)
|
||||||
|
{
|
||||||
|
uint32_t id = FF_VOLUMES;
|
||||||
|
bool found = s_get_context_id_by_card_mh(card, &id);
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(s_ctx[id]);
|
||||||
|
s_ctx[id] = NULL;
|
||||||
|
|
||||||
|
esp_err_t err = unmount_card_core_mh(base_path, card);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_vfs_fat_sdcard_format_mh(const char *base_path, sdmmc_card_t *card)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
|
||||||
|
if (!card) {
|
||||||
|
ESP_LOGE(TAG, "card not initialized");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
BYTE pdrv = ff_diskio_get_pdrv_card_mh(card);
|
||||||
|
|
||||||
|
if (pdrv == 0xff) {
|
||||||
|
ESP_LOGE(TAG, "card driver not registered");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t workbuf_size = 4096;
|
||||||
|
void *workbuf = ff_memalloc(workbuf_size);
|
||||||
|
|
||||||
|
if (workbuf == NULL) {
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
//unmount
|
||||||
|
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||||
|
f_mount(0, drv, 0);
|
||||||
|
|
||||||
|
//format
|
||||||
|
uint32_t id = FF_VOLUMES;
|
||||||
|
bool found = s_get_context_id_by_card_mh(card, &id);
|
||||||
|
assert(found);
|
||||||
|
size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(card->csd.sector_size, s_ctx[id]->mount_config.allocation_unit_size);
|
||||||
|
ESP_LOGI(TAG, "Formatting card, allocation unit size=%d", alloc_unit_size);
|
||||||
|
const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, alloc_unit_size};
|
||||||
|
FRESULT res = f_mkfs(drv, &opt, workbuf, workbuf_size);
|
||||||
|
free(workbuf);
|
||||||
|
|
||||||
|
if (res != FR_OK) {
|
||||||
|
ret = ESP_FAIL;
|
||||||
|
ESP_LOGD(TAG, "f_mkfs failed (%d)", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
//mount back
|
||||||
|
esp_err_t err = s_f_mount_mh(card, s_ctx[id]->fs, drv, pdrv, &s_ctx[id]->mount_config);
|
||||||
|
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
unmount_card_core_mh(base_path, card);
|
||||||
|
ESP_LOGE(TAG, "failed to format, resources recycled, please mount again");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
111
code/components/jomjol_helper/sdcard_init.h
Normal file
111
code/components/jomjol_helper/sdcard_init.h
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "sdmmc_cmd.h"
|
||||||
|
#include "driver/sdmmc_types.h"
|
||||||
|
#include "driver/sdspi_host.h"
|
||||||
|
#include "ff.h"
|
||||||
|
#include "esp_vfs_fat.h"
|
||||||
|
#include "wear_levelling.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convenience function to get FAT filesystem on SD card registered in VFS
|
||||||
|
*
|
||||||
|
* This is an all-in-one function which does the following:
|
||||||
|
* - initializes SDMMC driver or SPI driver with configuration in host_config
|
||||||
|
* - initializes SD card with configuration in slot_config
|
||||||
|
* - mounts FAT partition on SD card using FATFS library, with configuration in mount_config
|
||||||
|
* - registers FATFS library with VFS, with prefix given by base_prefix variable
|
||||||
|
*
|
||||||
|
* This function is intended to make example code more compact.
|
||||||
|
* For real world applications, developers should implement the logic of
|
||||||
|
* probing SD card, locating and mounting partition, and registering FATFS in VFS,
|
||||||
|
* with proper error checking and handling of exceptional conditions.
|
||||||
|
*
|
||||||
|
* @note Use this API to mount a card through SDSPI is deprecated. Please call
|
||||||
|
* `esp_vfs_fat_sdspi_mount()` instead for that case.
|
||||||
|
*
|
||||||
|
* @param base_path path where partition should be registered (e.g. "/sdcard")
|
||||||
|
* @param host_config Pointer to structure describing SDMMC host. When using
|
||||||
|
* SDMMC peripheral, this structure can be initialized using
|
||||||
|
* SDMMC_HOST_DEFAULT() macro. When using SPI peripheral,
|
||||||
|
* this structure can be initialized using SDSPI_HOST_DEFAULT()
|
||||||
|
* macro.
|
||||||
|
* @param slot_config Pointer to structure with slot configuration.
|
||||||
|
* For SDMMC peripheral, pass a pointer to sdmmc_slot_config_t
|
||||||
|
* structure initialized using SDMMC_SLOT_CONFIG_DEFAULT.
|
||||||
|
* @param mount_config pointer to structure with extra parameters for mounting FATFS
|
||||||
|
* @param[out] out_card if not NULL, pointer to the card information structure will be returned via this argument
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called
|
||||||
|
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||||
|
* - ESP_FAIL if partition can not be mounted
|
||||||
|
* - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers
|
||||||
|
*/
|
||||||
|
esp_err_t esp_vfs_fat_sdmmc_mount_mh(const char* base_path, const sdmmc_host_t* host_config, const void* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convenience function to get FAT filesystem on SD card registered in VFS
|
||||||
|
*
|
||||||
|
* This is an all-in-one function which does the following:
|
||||||
|
* - initializes an SPI Master device based on the SPI Master driver with configuration in
|
||||||
|
* slot_config, and attach it to an initialized SPI bus.
|
||||||
|
* - initializes SD card with configuration in host_config_input
|
||||||
|
* - mounts FAT partition on SD card using FATFS library, with configuration in mount_config
|
||||||
|
* - registers FATFS library with VFS, with prefix given by base_prefix variable
|
||||||
|
*
|
||||||
|
* This function is intended to make example code more compact.
|
||||||
|
* For real world applications, developers should implement the logic of
|
||||||
|
* probing SD card, locating and mounting partition, and registering FATFS in VFS,
|
||||||
|
* with proper error checking and handling of exceptional conditions.
|
||||||
|
*
|
||||||
|
* @note This function try to attach the new SD SPI device to the bus specified in host_config.
|
||||||
|
* Make sure the SPI bus specified in `host_config->slot` have been initialized by
|
||||||
|
* `spi_bus_initialize()` before.
|
||||||
|
*
|
||||||
|
* @param base_path path where partition should be registered (e.g. "/sdcard")
|
||||||
|
* @param host_config_input Pointer to structure describing SDMMC host. This structure can be
|
||||||
|
* initialized using SDSPI_HOST_DEFAULT() macro.
|
||||||
|
* @param slot_config Pointer to structure with slot configuration.
|
||||||
|
* For SPI peripheral, pass a pointer to sdspi_device_config_t
|
||||||
|
* structure initialized using SDSPI_DEVICE_CONFIG_DEFAULT().
|
||||||
|
* @param mount_config pointer to structure with extra parameters for mounting FATFS
|
||||||
|
* @param[out] out_card If not NULL, pointer to the card information structure will be returned via
|
||||||
|
* this argument. It is suggested to hold this handle and use it to unmount the card later if
|
||||||
|
* needed. Otherwise it's not suggested to use more than one card at the same time and unmount one
|
||||||
|
* of them in your application.
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called
|
||||||
|
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||||
|
* - ESP_FAIL if partition can not be mounted
|
||||||
|
* - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers
|
||||||
|
*/
|
||||||
|
esp_err_t esp_vfs_fat_sdspi_mount_mh(const char* base_path, const sdmmc_host_t* host_config_input, const sdspi_device_config_t* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
153
code/components/jomjol_helper/statusled.cpp
Normal file
153
code/components/jomjol_helper/statusled.cpp
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
#include "statusled.h"
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
|
||||||
|
#include "ClassLogFile.h"
|
||||||
|
#include "../../include/defines.h"
|
||||||
|
|
||||||
|
// define `gpio_pad_select_gpip` for newer versions of IDF
|
||||||
|
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0))
|
||||||
|
#include "esp_rom_gpio.h"
|
||||||
|
#define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char* TAG = "STATUSLED";
|
||||||
|
|
||||||
|
TaskHandle_t xHandle_task_StatusLED = NULL;
|
||||||
|
struct StatusLEDData StatusLEDData = {};
|
||||||
|
|
||||||
|
|
||||||
|
void task_StatusLED(void *pvParameter)
|
||||||
|
{
|
||||||
|
//ESP_LOGD(TAG, "task_StatusLED - create");
|
||||||
|
while (StatusLEDData.bProcessingRequest)
|
||||||
|
{
|
||||||
|
//ESP_LOGD(TAG, "task_StatusLED - start");
|
||||||
|
struct StatusLEDData StatusLEDDataInt = StatusLEDData;
|
||||||
|
|
||||||
|
gpio_pad_select_gpio(BLINK_GPIO); // Init the GPIO
|
||||||
|
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT); // Set the GPIO as a push/pull output
|
||||||
|
gpio_set_level(BLINK_GPIO, 1);// LED off
|
||||||
|
|
||||||
|
for (int i=0; i<2; ) // Default: repeat 2 times
|
||||||
|
{
|
||||||
|
if (!StatusLEDDataInt.bInfinite)
|
||||||
|
++i;
|
||||||
|
|
||||||
|
for (int j = 0; j < StatusLEDDataInt.iSourceBlinkCnt; ++j)
|
||||||
|
{
|
||||||
|
gpio_set_level(BLINK_GPIO, 0);
|
||||||
|
vTaskDelay(StatusLEDDataInt.iBlinkTime / portTICK_PERIOD_MS);
|
||||||
|
gpio_set_level(BLINK_GPIO, 1);
|
||||||
|
vTaskDelay(StatusLEDDataInt.iBlinkTime / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
vTaskDelay(500 / portTICK_PERIOD_MS); // Delay between module code and error code
|
||||||
|
|
||||||
|
for (int j = 0; j < StatusLEDDataInt.iCodeBlinkCnt; ++j)
|
||||||
|
{
|
||||||
|
gpio_set_level(BLINK_GPIO, 0);
|
||||||
|
vTaskDelay(StatusLEDDataInt.iBlinkTime / portTICK_PERIOD_MS);
|
||||||
|
gpio_set_level(BLINK_GPIO, 1);
|
||||||
|
vTaskDelay(StatusLEDDataInt.iBlinkTime / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
vTaskDelay(1500 / portTICK_PERIOD_MS); // Delay to signal new round
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusLEDData.bProcessingRequest = false;
|
||||||
|
//ESP_LOGD(TAG, "task_StatusLED - done/wait");
|
||||||
|
vTaskDelay(10000 / portTICK_PERIOD_MS); // Wait for an upcoming request otherwise continue and delete task to save memory
|
||||||
|
}
|
||||||
|
//ESP_LOGD(TAG, "task_StatusLED - delete");
|
||||||
|
xHandle_task_StatusLED = NULL;
|
||||||
|
vTaskDelete(NULL); // Delete this task due to no request
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void StatusLED(StatusLedSource _eSource, int _iCode, bool _bInfinite)
|
||||||
|
{
|
||||||
|
//ESP_LOGD(TAG, "StatusLED - start");
|
||||||
|
|
||||||
|
if (_eSource == WLAN_CONN) {
|
||||||
|
StatusLEDData.iSourceBlinkCnt = WLAN_CONN;
|
||||||
|
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||||
|
StatusLEDData.iBlinkTime = 250;
|
||||||
|
StatusLEDData.bInfinite = _bInfinite;
|
||||||
|
}
|
||||||
|
else if (_eSource == WLAN_INIT) {
|
||||||
|
StatusLEDData.iSourceBlinkCnt = WLAN_INIT;
|
||||||
|
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||||
|
StatusLEDData.iBlinkTime = 250;
|
||||||
|
StatusLEDData.bInfinite = _bInfinite;
|
||||||
|
}
|
||||||
|
else if (_eSource == SDCARD_INIT) {
|
||||||
|
StatusLEDData.iSourceBlinkCnt = SDCARD_INIT;
|
||||||
|
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||||
|
StatusLEDData.iBlinkTime = 250;
|
||||||
|
StatusLEDData.bInfinite = _bInfinite;
|
||||||
|
}
|
||||||
|
else if (_eSource == SDCARD_CHECK) {
|
||||||
|
StatusLEDData.iSourceBlinkCnt = SDCARD_CHECK;
|
||||||
|
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||||
|
StatusLEDData.iBlinkTime = 250;
|
||||||
|
StatusLEDData.bInfinite = _bInfinite;
|
||||||
|
}
|
||||||
|
else if (_eSource == CAM_INIT) {
|
||||||
|
StatusLEDData.iSourceBlinkCnt = CAM_INIT;
|
||||||
|
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||||
|
StatusLEDData.iBlinkTime = 250;
|
||||||
|
StatusLEDData.bInfinite = _bInfinite;
|
||||||
|
}
|
||||||
|
else if (_eSource == PSRAM_INIT) {
|
||||||
|
StatusLEDData.iSourceBlinkCnt = PSRAM_INIT;
|
||||||
|
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||||
|
StatusLEDData.iBlinkTime = 250;
|
||||||
|
StatusLEDData.bInfinite = _bInfinite;
|
||||||
|
}
|
||||||
|
else if (_eSource == TIME_CHECK) {
|
||||||
|
StatusLEDData.iSourceBlinkCnt = TIME_CHECK;
|
||||||
|
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||||
|
StatusLEDData.iBlinkTime = 250;
|
||||||
|
StatusLEDData.bInfinite = _bInfinite;
|
||||||
|
}
|
||||||
|
else if (_eSource == AP_OR_OTA) {
|
||||||
|
StatusLEDData.iSourceBlinkCnt = AP_OR_OTA;
|
||||||
|
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||||
|
StatusLEDData.iBlinkTime = 350;
|
||||||
|
StatusLEDData.bInfinite = _bInfinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xHandle_task_StatusLED && !StatusLEDData.bProcessingRequest) {
|
||||||
|
StatusLEDData.bProcessingRequest = true;
|
||||||
|
BaseType_t xReturned = xTaskAbortDelay(xHandle_task_StatusLED); // Reuse still running status LED task
|
||||||
|
/*if (xReturned == pdPASS)
|
||||||
|
ESP_LOGD(TAG, "task_StatusLED - abort waiting delay");*/
|
||||||
|
}
|
||||||
|
else if (xHandle_task_StatusLED == NULL) {
|
||||||
|
StatusLEDData.bProcessingRequest = true;
|
||||||
|
BaseType_t xReturned = xTaskCreate(&task_StatusLED, "task_StatusLED", 1280, NULL, tskIDLE_PRIORITY+1, &xHandle_task_StatusLED);
|
||||||
|
if(xReturned != pdPASS)
|
||||||
|
{
|
||||||
|
xHandle_task_StatusLED = NULL;
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "task_StatusLED failed to create");
|
||||||
|
LogFile.WriteHeapInfo("task_StatusLED failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ESP_LOGD(TAG, "task_StatusLED still processing, request skipped"); // Requests with high frequency could be skipped, but LED is only helpful for static states
|
||||||
|
}
|
||||||
|
//ESP_LOGD(TAG, "StatusLED - done");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void StatusLEDOff(void)
|
||||||
|
{
|
||||||
|
if (xHandle_task_StatusLED)
|
||||||
|
vTaskDelete(xHandle_task_StatusLED); // Delete task for StatusLED to force stop of blinking
|
||||||
|
|
||||||
|
gpio_pad_select_gpio(BLINK_GPIO); // Init the GPIO
|
||||||
|
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT); // Set the GPIO as a push/pull output
|
||||||
|
gpio_set_level(BLINK_GPIO, 1);// LED off
|
||||||
|
}
|
||||||
34
code/components/jomjol_helper/statusled.h
Normal file
34
code/components/jomjol_helper/statusled.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef STATUSLED_H
|
||||||
|
#define STATUSLED_H
|
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern TaskHandle_t xHandle_task_StatusLED;
|
||||||
|
|
||||||
|
enum StatusLedSource {
|
||||||
|
WLAN_CONN = 1,
|
||||||
|
WLAN_INIT = 2,
|
||||||
|
SDCARD_INIT = 3,
|
||||||
|
SDCARD_CHECK = 4,
|
||||||
|
CAM_INIT = 5,
|
||||||
|
PSRAM_INIT = 6,
|
||||||
|
TIME_CHECK = 7,
|
||||||
|
AP_OR_OTA = 8
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StatusLEDData {
|
||||||
|
int iSourceBlinkCnt = 1;
|
||||||
|
int iCodeBlinkCnt = 1;
|
||||||
|
int iBlinkTime = 250;
|
||||||
|
bool bInfinite = false;
|
||||||
|
bool bProcessingRequest = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
void StatusLED(StatusLedSource _eSource, int _iCode, bool _bInfinite);
|
||||||
|
void StatusLEDOff(void);
|
||||||
|
|
||||||
|
#endif //STATUSLED_H
|
||||||
@@ -5,12 +5,14 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
|
#include "psram.h"
|
||||||
#include "../../include/defines.h"
|
#include "../../include/defines.h"
|
||||||
|
|
||||||
static const char* TAG = "c_align_and_cut_image";
|
static const char* TAG = "c_align_and_cut_image";
|
||||||
|
|
||||||
CAlignAndCutImage::CAlignAndCutImage(CImageBasis *_org, CImageBasis *_temp)
|
CAlignAndCutImage::CAlignAndCutImage(std::string _name, CImageBasis *_org, CImageBasis *_temp) : CImageBasis(_name)
|
||||||
{
|
{
|
||||||
|
name = _name;
|
||||||
rgb_image = _org->rgb_image;
|
rgb_image = _org->rgb_image;
|
||||||
channels = _org->channels;
|
channels = _org->channels;
|
||||||
width = _org->width;
|
width = _org->width;
|
||||||
@@ -37,7 +39,7 @@ bool CAlignAndCutImage::Align(RefInfo *_temp1, RefInfo *_temp2)
|
|||||||
int r0_x, r0_y, r1_x, r1_y;
|
int r0_x, r0_y, r1_x, r1_y;
|
||||||
bool isSimilar1, isSimilar2;
|
bool isSimilar1, isSimilar2;
|
||||||
|
|
||||||
CFindTemplate* ft = new CFindTemplate(rgb_image, channels, width, height, bpp);
|
CFindTemplate* ft = new CFindTemplate("align", rgb_image, channels, width, height, bpp);
|
||||||
|
|
||||||
r0_x = _temp1->target_x;
|
r0_x = _temp1->target_x;
|
||||||
r0_y = _temp1->target_y;
|
r0_y = _temp1->target_y;
|
||||||
@@ -81,7 +83,7 @@ bool CAlignAndCutImage::Align(RefInfo *_temp1, RefInfo *_temp2)
|
|||||||
LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", zw);
|
LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", zw);
|
||||||
#endif*/
|
#endif*/
|
||||||
|
|
||||||
CRotateImage rt(this, ImageTMP);
|
CRotateImage rt("Align", this, ImageTMP);
|
||||||
rt.Translate(dx, dy);
|
rt.Translate(dx, dy);
|
||||||
rt.Rotate(d_winkel, _temp1->target_x, _temp1->target_y);
|
rt.Rotate(d_winkel, _temp1->target_x, _temp1->target_y);
|
||||||
ESP_LOGD(TAG, "Alignment: dx %d - dy %d - rot %f", dx, dy, d_winkel);
|
ESP_LOGD(TAG, "Alignment: dx %d - dy %d - rot %f", dx, dy, d_winkel);
|
||||||
@@ -107,7 +109,7 @@ void CAlignAndCutImage::CutAndSave(std::string _template1, int x1, int y1, int d
|
|||||||
dy = y2 - y1;
|
dy = y2 - y1;
|
||||||
|
|
||||||
int memsize = dx * dy * channels;
|
int memsize = dx * dy * channels;
|
||||||
uint8_t* odata = (unsigned char*) GET_MEMORY(memsize);
|
uint8_t* odata = (unsigned char*) malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||||
|
|
||||||
stbi_uc* p_target;
|
stbi_uc* p_target;
|
||||||
stbi_uc* p_source;
|
stbi_uc* p_source;
|
||||||
@@ -186,7 +188,7 @@ CImageBasis* CAlignAndCutImage::CutAndSave(int x1, int y1, int dx, int dy)
|
|||||||
dy = y2 - y1;
|
dy = y2 - y1;
|
||||||
|
|
||||||
int memsize = dx * dy * channels;
|
int memsize = dx * dy * channels;
|
||||||
uint8_t* odata = (unsigned char*)GET_MEMORY(memsize);
|
uint8_t* odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||||
|
|
||||||
stbi_uc* p_target;
|
stbi_uc* p_target;
|
||||||
stbi_uc* p_source;
|
stbi_uc* p_source;
|
||||||
@@ -202,7 +204,7 @@ CImageBasis* CAlignAndCutImage::CutAndSave(int x1, int y1, int dx, int dy)
|
|||||||
p_target[_channels] = p_source[_channels];
|
p_target[_channels] = p_source[_channels];
|
||||||
}
|
}
|
||||||
|
|
||||||
CImageBasis* rs = new CImageBasis(odata, channels, dx, dy, bpp);
|
CImageBasis* rs = new CImageBasis("CutAndSave", odata, channels, dx, dy, bpp);
|
||||||
RGBImageRelease();
|
RGBImageRelease();
|
||||||
rs->SetIndepended();
|
rs->SetIndepended();
|
||||||
return rs;
|
return rs;
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ class CAlignAndCutImage : public CImageBasis
|
|||||||
public:
|
public:
|
||||||
int t0_dx, t0_dy, t1_dx, t1_dy;
|
int t0_dx, t0_dy, t1_dx, t1_dy;
|
||||||
CImageBasis *ImageTMP;
|
CImageBasis *ImageTMP;
|
||||||
CAlignAndCutImage(std::string _image) : CImageBasis(_image) {ImageTMP = NULL;};
|
CAlignAndCutImage(std::string name, std::string _image) : CImageBasis(name, _image) {ImageTMP = NULL;};
|
||||||
CAlignAndCutImage(uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp) : CImageBasis(_rgb_image, _channels, _width, _height, _bpp) {ImageTMP = NULL;};
|
CAlignAndCutImage(std::string name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp) : CImageBasis(name, _rgb_image, _channels, _width, _height, _bpp) {ImageTMP = NULL;};
|
||||||
CAlignAndCutImage(CImageBasis *_org, CImageBasis *_temp);
|
CAlignAndCutImage(std::string name, CImageBasis *_org, CImageBasis *_temp);
|
||||||
|
|
||||||
bool Align(RefInfo *_temp1, RefInfo *_temp2);
|
bool Align(RefInfo *_temp1, RefInfo *_temp2);
|
||||||
// void Align(std::string _template1, int x1, int y1, std::string _template2, int x2, int y2, int deltax = 40, int deltay = 40, std::string imageROI = "");
|
// void Align(std::string _template1, int x1, int y1, std::string _template2, int x2, int y2, int deltax = 40, int deltay = 40, std::string imageROI = "");
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class CFindTemplate : public CImageBasis
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
int tpl_width, tpl_height, tpl_bpp;
|
int tpl_width, tpl_height, tpl_bpp;
|
||||||
CFindTemplate(uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp) : CImageBasis(_rgb_image, _channels, _width, _height, _bpp) {};
|
CFindTemplate(std::string name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp) : CImageBasis(name, _rgb_image, _channels, _width, _height, _bpp) {};
|
||||||
|
|
||||||
bool FindTemplate(RefInfo *_ref);
|
bool FindTemplate(RefInfo *_ref);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "CImageBasis.h"
|
#include "CImageBasis.h"
|
||||||
#include "Helper.h"
|
#include "Helper.h"
|
||||||
|
#include "psram.h"
|
||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
#include "server_ota.h"
|
#include "server_ota.h"
|
||||||
|
|
||||||
@@ -360,8 +361,9 @@ void CImageBasis::drawCircle(int x1, int y1, int rad, int r, int g, int b, int t
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CImageBasis::CImageBasis()
|
CImageBasis::CImageBasis(string _name)
|
||||||
{
|
{
|
||||||
|
name = _name;
|
||||||
externalImage = false;
|
externalImage = false;
|
||||||
rgb_image = NULL;
|
rgb_image = NULL;
|
||||||
width = 0;
|
width = 0;
|
||||||
@@ -384,8 +386,9 @@ void CImageBasis::CreateEmptyImage(int _width, int _height, int _channels)
|
|||||||
LogFile.WriteHeapInfo("CreateEmptyImage");
|
LogFile.WriteHeapInfo("CreateEmptyImage");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int memsize = width * height * channels;
|
memsize = width * height * channels;
|
||||||
rgb_image = (unsigned char*)GET_MEMORY(memsize);
|
|
||||||
|
rgb_image = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
|
||||||
|
|
||||||
if (rgb_image == NULL)
|
if (rgb_image == NULL)
|
||||||
{
|
{
|
||||||
@@ -435,10 +438,12 @@ void CImageBasis::LoadFromMemory(stbi_uc *_buffer, int len)
|
|||||||
{
|
{
|
||||||
RGBImageLock();
|
RGBImageLock();
|
||||||
|
|
||||||
if (rgb_image)
|
if (rgb_image != NULL) {
|
||||||
stbi_image_free(rgb_image);
|
stbi_image_free(rgb_image);
|
||||||
|
//free_psram_heap(std::string(TAG) + "->rgb_image (LoadFromMemory)", rgb_image);
|
||||||
|
}
|
||||||
|
|
||||||
rgb_image = stbi_load_from_memory(_buffer, len, &width, &height, &channels, 3);
|
rgb_image = stbi_load_from_memory(_buffer, len, &width, &height, &channels, STBI_rgb);
|
||||||
bpp = channels;
|
bpp = channels;
|
||||||
ESP_LOGD(TAG, "Image loaded from memory: %d, %d, %d", width, height, channels);
|
ESP_LOGD(TAG, "Image loaded from memory: %d, %d, %d", width, height, channels);
|
||||||
|
|
||||||
@@ -454,8 +459,47 @@ void CImageBasis::LoadFromMemory(stbi_uc *_buffer, int len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CImageBasis::CImageBasis(CImageBasis *_copyfrom)
|
void CImageBasis::crop_image(unsigned short cropLeft, unsigned short cropRight, unsigned short cropTop, unsigned short cropBottom)
|
||||||
{
|
{
|
||||||
|
unsigned int maxTopIndex = cropTop * width * channels;
|
||||||
|
unsigned int minBottomIndex = ((width*height) - (cropBottom * width)) * channels;
|
||||||
|
unsigned short maxX = width - cropRight; // In pixels
|
||||||
|
unsigned short newWidth = width - cropLeft - cropRight;
|
||||||
|
unsigned short newHeight = height - cropTop - cropBottom;
|
||||||
|
|
||||||
|
unsigned int writeIndex = 0;
|
||||||
|
// Loop over all bytes
|
||||||
|
for (int i = 0; i < width * height * channels; i += channels) {
|
||||||
|
// Calculate current X, Y pixel position
|
||||||
|
int x = (i/channels) % width;
|
||||||
|
|
||||||
|
// Crop from the top
|
||||||
|
if (i < maxTopIndex) { continue; }
|
||||||
|
|
||||||
|
// Crop from the bottom
|
||||||
|
if (i > minBottomIndex) { continue; }
|
||||||
|
|
||||||
|
// Crop from the left
|
||||||
|
if (x <= cropLeft) { continue; }
|
||||||
|
|
||||||
|
// Crop from the right
|
||||||
|
if (x > maxX) { continue; }
|
||||||
|
|
||||||
|
// If we get here, keep the pixels
|
||||||
|
for (int c = 0; c < channels; c++) {
|
||||||
|
rgb_image[writeIndex++] = rgb_image[i+c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the new dimensions of the framebuffer for further use.
|
||||||
|
width = newWidth;
|
||||||
|
height = newHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CImageBasis::CImageBasis(string _name, CImageBasis *_copyfrom)
|
||||||
|
{
|
||||||
|
name = _name;
|
||||||
islocked = false;
|
islocked = false;
|
||||||
externalImage = false;
|
externalImage = false;
|
||||||
channels = _copyfrom->channels;
|
channels = _copyfrom->channels;
|
||||||
@@ -469,8 +513,15 @@ CImageBasis::CImageBasis(CImageBasis *_copyfrom)
|
|||||||
LogFile.WriteHeapInfo("CImageBasis_copyfrom - Start");
|
LogFile.WriteHeapInfo("CImageBasis_copyfrom - Start");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int memsize = width * height * channels;
|
memsize = width * height * channels;
|
||||||
rgb_image = (unsigned char*)GET_MEMORY(memsize);
|
|
||||||
|
|
||||||
|
if (name == "tmpImage") {
|
||||||
|
rgb_image = (unsigned char*)psram_reserve_shared_tmp_image_memory();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rgb_image = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
|
||||||
|
}
|
||||||
|
|
||||||
if (rgb_image == NULL)
|
if (rgb_image == NULL)
|
||||||
{
|
{
|
||||||
@@ -489,8 +540,9 @@ CImageBasis::CImageBasis(CImageBasis *_copyfrom)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CImageBasis::CImageBasis(int _width, int _height, int _channels)
|
CImageBasis::CImageBasis(string _name, int _width, int _height, int _channels)
|
||||||
{
|
{
|
||||||
|
name = _name;
|
||||||
islocked = false;
|
islocked = false;
|
||||||
externalImage = false;
|
externalImage = false;
|
||||||
channels = _channels;
|
channels = _channels;
|
||||||
@@ -504,8 +556,9 @@ CImageBasis::CImageBasis(int _width, int _height, int _channels)
|
|||||||
LogFile.WriteHeapInfo("CImageBasis_width,height,ch - Start");
|
LogFile.WriteHeapInfo("CImageBasis_width,height,ch - Start");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int memsize = width * height * channels;
|
memsize = width * height * channels;
|
||||||
rgb_image = (unsigned char*)GET_MEMORY(memsize);
|
|
||||||
|
rgb_image = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
|
||||||
|
|
||||||
if (rgb_image == NULL)
|
if (rgb_image == NULL)
|
||||||
{
|
{
|
||||||
@@ -523,8 +576,9 @@ CImageBasis::CImageBasis(int _width, int _height, int _channels)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CImageBasis::CImageBasis(std::string _image)
|
CImageBasis::CImageBasis(string _name, std::string _image)
|
||||||
{
|
{
|
||||||
|
name = _name;
|
||||||
islocked = false;
|
islocked = false;
|
||||||
channels = 3;
|
channels = 3;
|
||||||
externalImage = false;
|
externalImage = false;
|
||||||
@@ -569,8 +623,9 @@ bool CImageBasis::ImageOkay(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CImageBasis::CImageBasis(uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp)
|
CImageBasis::CImageBasis(string _name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp)
|
||||||
{
|
{
|
||||||
|
name = _name;
|
||||||
islocked = false;
|
islocked = false;
|
||||||
rgb_image = _rgb_image;
|
rgb_image = _rgb_image;
|
||||||
channels = _channels;
|
channels = _channels;
|
||||||
@@ -581,6 +636,20 @@ CImageBasis::CImageBasis(uint8_t* _rgb_image, int _channels, int _width, int _he
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CImageBasis::Negative(void)
|
||||||
|
{
|
||||||
|
RGBImageLock();
|
||||||
|
|
||||||
|
for (int i = 0; i < width * height * channels; i += channels) {
|
||||||
|
for (int c = 0; c < channels; c++) {
|
||||||
|
rgb_image[i+c] = 255 - rgb_image[i+c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RGBImageRelease();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void CImageBasis::Contrast(float _contrast) //input range [-100..100]
|
void CImageBasis::Contrast(float _contrast) //input range [-100..100]
|
||||||
{
|
{
|
||||||
stbi_uc* p_source;
|
stbi_uc* p_source;
|
||||||
@@ -607,8 +676,21 @@ CImageBasis::~CImageBasis()
|
|||||||
{
|
{
|
||||||
RGBImageLock();
|
RGBImageLock();
|
||||||
|
|
||||||
if (!externalImage)
|
|
||||||
stbi_image_free(rgb_image);
|
if (!externalImage) {
|
||||||
|
if (name == "tmpImage") { // This image should be placed in the shared part of PSRAM
|
||||||
|
psram_free_shared_temp_image_memory();
|
||||||
|
}
|
||||||
|
else { // All other images are much smaller and can go into the normal PSRAM region
|
||||||
|
//stbi_image_free(rgb_image);
|
||||||
|
if (memsize == 0) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Not freeing (" + name + " as there was never PSRAM allocated for it)");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
free_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ", " + to_string(memsize) + ")", rgb_image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RGBImageRelease();
|
RGBImageRelease();
|
||||||
}
|
}
|
||||||
@@ -637,19 +719,19 @@ void CImageBasis::SaveToFile(std::string _imageout)
|
|||||||
|
|
||||||
void CImageBasis::Resize(int _new_dx, int _new_dy)
|
void CImageBasis::Resize(int _new_dx, int _new_dy)
|
||||||
{
|
{
|
||||||
int memsize = _new_dx * _new_dy * channels;
|
memsize = _new_dx * _new_dy * channels;
|
||||||
uint8_t* odata = (unsigned char*)GET_MEMORY(memsize);
|
uint8_t* odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||||
|
|
||||||
RGBImageLock();
|
RGBImageLock();
|
||||||
|
|
||||||
stbir_resize_uint8(rgb_image, width, height, 0, odata, _new_dx, _new_dy, 0, channels);
|
stbir_resize_uint8(rgb_image, width, height, 0, odata, _new_dx, _new_dy, 0, channels);
|
||||||
stbi_image_free(rgb_image);
|
|
||||||
|
|
||||||
rgb_image = (unsigned char*)GET_MEMORY(memsize);
|
rgb_image = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->CImageBasis Resize (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
|
||||||
memCopy(odata, rgb_image, memsize);
|
memCopy(odata, rgb_image, memsize);
|
||||||
width = _new_dx;
|
width = _new_dx;
|
||||||
height = _new_dy;
|
height = _new_dy;
|
||||||
stbi_image_free(odata);
|
|
||||||
|
free_psram_heap(std::string(TAG) + "->odata", odata);
|
||||||
|
|
||||||
RGBImageRelease();
|
RGBImageRelease();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,9 @@
|
|||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#include "stb_image.h"
|
#include "../stb/stb_image.h"
|
||||||
#include "stb_image_write.h"
|
#include "../stb/stb_image_write.h"
|
||||||
#include "stb_image_resize.h"
|
#include "../stb/stb_image_resize.h"
|
||||||
|
|
||||||
#include "esp_heap_caps.h"
|
#include "esp_heap_caps.h"
|
||||||
|
|
||||||
@@ -30,6 +30,8 @@ class CImageBasis
|
|||||||
protected:
|
protected:
|
||||||
bool externalImage;
|
bool externalImage;
|
||||||
std::string filename;
|
std::string filename;
|
||||||
|
std::string name; // Just used for diagnostics
|
||||||
|
int memsize = 0;
|
||||||
|
|
||||||
void memCopy(uint8_t* _source, uint8_t* _target, int _size);
|
void memCopy(uint8_t* _source, uint8_t* _target, int _size);
|
||||||
bool isInImage(int x, int y);
|
bool isInImage(int x, int y);
|
||||||
@@ -37,7 +39,7 @@ class CImageBasis
|
|||||||
bool islocked;
|
bool islocked;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
uint8_t* rgb_image;
|
uint8_t* rgb_image = NULL;
|
||||||
int channels;
|
int channels;
|
||||||
int width, height, bpp;
|
int width, height, bpp;
|
||||||
|
|
||||||
@@ -54,6 +56,7 @@ class CImageBasis
|
|||||||
void drawEllipse(int x1, int y1, int radx, int rady, int r, int g, int b, int thickness = 1);
|
void drawEllipse(int x1, int y1, int radx, int rady, int r, int g, int b, int thickness = 1);
|
||||||
|
|
||||||
void setPixelColor(int x, int y, int r, int g, int b);
|
void setPixelColor(int x, int y, int r, int g, int b);
|
||||||
|
void Negative(void);
|
||||||
void Contrast(float _contrast);
|
void Contrast(float _contrast);
|
||||||
bool ImageOkay();
|
bool ImageOkay();
|
||||||
bool CopyFromMemory(uint8_t* _source, int _size);
|
bool CopyFromMemory(uint8_t* _source, int _size);
|
||||||
@@ -64,14 +67,15 @@ class CImageBasis
|
|||||||
void EmptyImage();
|
void EmptyImage();
|
||||||
|
|
||||||
|
|
||||||
CImageBasis();
|
CImageBasis(std::string name);
|
||||||
CImageBasis(std::string _image);
|
CImageBasis(std::string name, std::string _image);
|
||||||
CImageBasis(uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp);
|
CImageBasis(std::string name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp);
|
||||||
CImageBasis(int _width, int _height, int _channels);
|
CImageBasis(std::string name, int _width, int _height, int _channels);
|
||||||
CImageBasis(CImageBasis *_copyfrom);
|
CImageBasis(std::string name, CImageBasis *_copyfrom);
|
||||||
|
|
||||||
void Resize(int _new_dx, int _new_dy);
|
void Resize(int _new_dx, int _new_dy);
|
||||||
void Resize(int _new_dx, int _new_dy, CImageBasis *_target);
|
void Resize(int _new_dx, int _new_dy, CImageBasis *_target);
|
||||||
|
void crop_image(unsigned short cropLeft, unsigned short cropRight, unsigned short cropTop, unsigned short cropBottom);
|
||||||
|
|
||||||
void LoadFromMemory(stbi_uc *_buffer, int len);
|
void LoadFromMemory(stbi_uc *_buffer, int len);
|
||||||
|
|
||||||
|
|||||||
@@ -1,349 +1,309 @@
|
|||||||
#include "CRotateImage.h"
|
#include <string>
|
||||||
|
#include "CRotateImage.h"
|
||||||
|
#include "psram.h"
|
||||||
CRotateImage::CRotateImage(CImageBasis *_org, CImageBasis *_temp, bool _flip)
|
|
||||||
{
|
static const char *TAG = "C ROTATE IMG";
|
||||||
rgb_image = _org->rgb_image;
|
|
||||||
channels = _org->channels;
|
CRotateImage::CRotateImage(std::string _name, CImageBasis *_org, CImageBasis *_temp, bool _flip) : CImageBasis(_name)
|
||||||
width = _org->width;
|
{
|
||||||
height = _org->height;
|
rgb_image = _org->rgb_image;
|
||||||
bpp = _org->bpp;
|
channels = _org->channels;
|
||||||
externalImage = true;
|
width = _org->width;
|
||||||
ImageTMP = _temp;
|
height = _org->height;
|
||||||
ImageOrg = _org;
|
bpp = _org->bpp;
|
||||||
islocked = false;
|
externalImage = true;
|
||||||
doflip = _flip;
|
ImageTMP = _temp;
|
||||||
}
|
ImageOrg = _org;
|
||||||
|
islocked = false;
|
||||||
void CRotateImage::Mirror(){
|
doflip = _flip;
|
||||||
int memsize = width * height * channels;
|
}
|
||||||
uint8_t* odata;
|
|
||||||
if (ImageTMP)
|
void CRotateImage::Rotate(float _angle, int _centerx, int _centery)
|
||||||
{
|
{
|
||||||
odata = ImageTMP->RGBImageLock();
|
int org_width, org_height;
|
||||||
}
|
float m[2][3];
|
||||||
else
|
|
||||||
{
|
float x_center = _centerx;
|
||||||
odata = (unsigned char*)GET_MEMORY(memsize);
|
float y_center = _centery;
|
||||||
}
|
_angle = _angle / 180 * M_PI;
|
||||||
|
|
||||||
|
if (doflip)
|
||||||
int x_source, y_source;
|
{
|
||||||
stbi_uc* p_target;
|
org_width = width;
|
||||||
stbi_uc* p_source;
|
org_height = height;
|
||||||
|
height = org_width;
|
||||||
RGBImageLock();
|
width = org_height;
|
||||||
|
x_center = x_center - (org_width/2) + (org_height/2);
|
||||||
for (int x = 0; x < width; ++x)
|
y_center = y_center + (org_width/2) - (org_height/2);
|
||||||
for (int y = 0; y < height; ++y)
|
if (ImageOrg)
|
||||||
{
|
{
|
||||||
p_target = odata + (channels * (y * width + x));
|
ImageOrg->height = height;
|
||||||
|
ImageOrg->width = width;
|
||||||
x_source = width - x;
|
}
|
||||||
y_source = y;
|
}
|
||||||
|
else
|
||||||
p_source = rgb_image + (channels * (y_source * width + x_source));
|
{
|
||||||
for (int _channels = 0; _channels < channels; ++_channels)
|
org_width = width;
|
||||||
p_target[_channels] = p_source[_channels];
|
org_height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
// memcpy(rgb_image, odata, memsize);
|
m[0][0] = cos(_angle);
|
||||||
memCopy(odata, rgb_image, memsize);
|
m[0][1] = sin(_angle);
|
||||||
if (!ImageTMP)
|
m[0][2] = (1 - m[0][0]) * x_center - m[0][1] * y_center;
|
||||||
stbi_image_free(odata);
|
|
||||||
|
m[1][0] = -m[0][1];
|
||||||
if (ImageTMP)
|
m[1][1] = m[0][0];
|
||||||
ImageTMP->RGBImageRelease();
|
m[1][2] = m[0][1] * x_center + (1 - m[0][0]) * y_center;
|
||||||
|
|
||||||
RGBImageRelease();
|
if (doflip)
|
||||||
}
|
{
|
||||||
|
m[0][2] = m[0][2] + (org_width/2) - (org_height/2);
|
||||||
void CRotateImage::Rotate(float _angle, int _centerx, int _centery)
|
m[1][2] = m[1][2] - (org_width/2) + (org_height/2);
|
||||||
{
|
}
|
||||||
int org_width, org_height;
|
|
||||||
float m[2][3];
|
int memsize = width * height * channels;
|
||||||
|
uint8_t* odata;
|
||||||
float x_center = _centerx;
|
if (ImageTMP)
|
||||||
float y_center = _centery;
|
{
|
||||||
_angle = _angle / 180 * M_PI;
|
odata = ImageTMP->RGBImageLock();
|
||||||
|
}
|
||||||
if (doflip)
|
else
|
||||||
{
|
{
|
||||||
org_width = width;
|
odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||||
org_height = height;
|
}
|
||||||
height = org_width;
|
|
||||||
width = org_height;
|
|
||||||
x_center = x_center - (org_width/2) + (org_height/2);
|
int x_source, y_source;
|
||||||
y_center = y_center + (org_width/2) - (org_height/2);
|
stbi_uc* p_target;
|
||||||
if (ImageOrg)
|
stbi_uc* p_source;
|
||||||
{
|
|
||||||
ImageOrg->height = height;
|
RGBImageLock();
|
||||||
ImageOrg->width = width;
|
|
||||||
}
|
for (int x = 0; x < width; ++x)
|
||||||
}
|
for (int y = 0; y < height; ++y)
|
||||||
else
|
{
|
||||||
{
|
p_target = odata + (channels * (y * width + x));
|
||||||
org_width = width;
|
|
||||||
org_height = height;
|
x_source = int(m[0][0] * x + m[0][1] * y);
|
||||||
}
|
y_source = int(m[1][0] * x + m[1][1] * y);
|
||||||
|
|
||||||
m[0][0] = cos(_angle);
|
x_source += int(m[0][2]);
|
||||||
m[0][1] = sin(_angle);
|
y_source += int(m[1][2]);
|
||||||
m[0][2] = (1 - m[0][0]) * x_center - m[0][1] * y_center;
|
|
||||||
|
if ((x_source >= 0) && (x_source < org_width) && (y_source >= 0) && (y_source < org_height))
|
||||||
m[1][0] = -m[0][1];
|
{
|
||||||
m[1][1] = m[0][0];
|
p_source = rgb_image + (channels * (y_source * org_width + x_source));
|
||||||
m[1][2] = m[0][1] * x_center + (1 - m[0][0]) * y_center;
|
for (int _channels = 0; _channels < channels; ++_channels)
|
||||||
|
p_target[_channels] = p_source[_channels];
|
||||||
if (doflip)
|
}
|
||||||
{
|
else
|
||||||
m[0][2] = m[0][2] + (org_width/2) - (org_height/2);
|
{
|
||||||
m[1][2] = m[1][2] - (org_width/2) + (org_height/2);
|
for (int _channels = 0; _channels < channels; ++_channels)
|
||||||
}
|
p_target[_channels] = 255;
|
||||||
|
}
|
||||||
int memsize = width * height * channels;
|
}
|
||||||
uint8_t* odata;
|
|
||||||
if (ImageTMP)
|
// memcpy(rgb_image, odata, memsize);
|
||||||
{
|
memCopy(odata, rgb_image, memsize);
|
||||||
odata = ImageTMP->RGBImageLock();
|
|
||||||
}
|
if (!ImageTMP)
|
||||||
else
|
{
|
||||||
{
|
free_psram_heap(std::string(TAG) + "->odata", odata);
|
||||||
odata = (unsigned char*)GET_MEMORY(memsize);
|
}
|
||||||
}
|
if (ImageTMP)
|
||||||
|
ImageTMP->RGBImageRelease();
|
||||||
|
|
||||||
int x_source, y_source;
|
RGBImageRelease();
|
||||||
stbi_uc* p_target;
|
}
|
||||||
stbi_uc* p_source;
|
|
||||||
|
|
||||||
RGBImageLock();
|
|
||||||
|
void CRotateImage::RotateAntiAliasing(float _angle, int _centerx, int _centery)
|
||||||
for (int x = 0; x < width; ++x)
|
{
|
||||||
for (int y = 0; y < height; ++y)
|
int org_width, org_height;
|
||||||
{
|
float m[2][3];
|
||||||
p_target = odata + (channels * (y * width + x));
|
|
||||||
|
float x_center = _centerx;
|
||||||
x_source = int(m[0][0] * x + m[0][1] * y);
|
float y_center = _centery;
|
||||||
y_source = int(m[1][0] * x + m[1][1] * y);
|
_angle = _angle / 180 * M_PI;
|
||||||
|
|
||||||
x_source += int(m[0][2]);
|
if (doflip)
|
||||||
y_source += int(m[1][2]);
|
{
|
||||||
|
org_width = width;
|
||||||
if ((x_source >= 0) && (x_source < org_width) && (y_source >= 0) && (y_source < org_height))
|
org_height = height;
|
||||||
{
|
height = org_width;
|
||||||
p_source = rgb_image + (channels * (y_source * org_width + x_source));
|
width = org_height;
|
||||||
for (int _channels = 0; _channels < channels; ++_channels)
|
x_center = x_center - (org_width/2) + (org_height/2);
|
||||||
p_target[_channels] = p_source[_channels];
|
y_center = y_center + (org_width/2) - (org_height/2);
|
||||||
}
|
if (ImageOrg)
|
||||||
else
|
{
|
||||||
{
|
ImageOrg->height = height;
|
||||||
for (int _channels = 0; _channels < channels; ++_channels)
|
ImageOrg->width = width;
|
||||||
p_target[_channels] = 255;
|
}
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
// memcpy(rgb_image, odata, memsize);
|
org_width = width;
|
||||||
memCopy(odata, rgb_image, memsize);
|
org_height = height;
|
||||||
|
}
|
||||||
if (!ImageTMP)
|
|
||||||
{
|
m[0][0] = cos(_angle);
|
||||||
stbi_image_free(odata);
|
m[0][1] = sin(_angle);
|
||||||
}
|
m[0][2] = (1 - m[0][0]) * x_center - m[0][1] * y_center;
|
||||||
if (ImageTMP)
|
|
||||||
ImageTMP->RGBImageRelease();
|
m[1][0] = -m[0][1];
|
||||||
|
m[1][1] = m[0][0];
|
||||||
RGBImageRelease();
|
m[1][2] = m[0][1] * x_center + (1 - m[0][0]) * y_center;
|
||||||
}
|
|
||||||
|
if (doflip)
|
||||||
|
{
|
||||||
|
m[0][2] = m[0][2] + (org_width/2) - (org_height/2);
|
||||||
void CRotateImage::RotateAntiAliasing(float _angle, int _centerx, int _centery)
|
m[1][2] = m[1][2] - (org_width/2) + (org_height/2);
|
||||||
{
|
}
|
||||||
int org_width, org_height;
|
|
||||||
float m[2][3];
|
int memsize = width * height * channels;
|
||||||
|
uint8_t* odata;
|
||||||
float x_center = _centerx;
|
if (ImageTMP)
|
||||||
float y_center = _centery;
|
{
|
||||||
_angle = _angle / 180 * M_PI;
|
odata = ImageTMP->RGBImageLock();
|
||||||
|
}
|
||||||
if (doflip)
|
else
|
||||||
{
|
{
|
||||||
org_width = width;
|
odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||||
org_height = height;
|
}
|
||||||
height = org_width;
|
|
||||||
width = org_height;
|
|
||||||
x_center = x_center - (org_width/2) + (org_height/2);
|
int x_source_1, y_source_1, x_source_2, y_source_2;
|
||||||
y_center = y_center + (org_width/2) - (org_height/2);
|
float x_source, y_source;
|
||||||
if (ImageOrg)
|
float quad_ul, quad_ur, quad_ol, quad_or;
|
||||||
{
|
stbi_uc* p_target;
|
||||||
ImageOrg->height = height;
|
stbi_uc *p_source_ul, *p_source_ur, *p_source_ol, *p_source_or;
|
||||||
ImageOrg->width = width;
|
|
||||||
}
|
RGBImageLock();
|
||||||
}
|
|
||||||
else
|
for (int x = 0; x < width; ++x)
|
||||||
{
|
for (int y = 0; y < height; ++y)
|
||||||
org_width = width;
|
{
|
||||||
org_height = height;
|
p_target = odata + (channels * (y * width + x));
|
||||||
}
|
|
||||||
|
x_source = (m[0][0] * x + m[0][1] * y);
|
||||||
m[0][0] = cos(_angle);
|
y_source = (m[1][0] * x + m[1][1] * y);
|
||||||
m[0][1] = sin(_angle);
|
|
||||||
m[0][2] = (1 - m[0][0]) * x_center - m[0][1] * y_center;
|
x_source += (m[0][2]);
|
||||||
|
y_source += (m[1][2]);
|
||||||
m[1][0] = -m[0][1];
|
|
||||||
m[1][1] = m[0][0];
|
x_source_1 = (int)x_source;
|
||||||
m[1][2] = m[0][1] * x_center + (1 - m[0][0]) * y_center;
|
x_source_2 = x_source_1 + 1;
|
||||||
|
y_source_1 = (int)y_source;
|
||||||
if (doflip)
|
y_source_2 = y_source_1 + 1;
|
||||||
{
|
|
||||||
m[0][2] = m[0][2] + (org_width/2) - (org_height/2);
|
quad_ul = (x_source_2 - x_source) * (y_source_2 - y_source);
|
||||||
m[1][2] = m[1][2] - (org_width/2) + (org_height/2);
|
quad_ur = (1- (x_source_2 - x_source)) * (y_source_2 - y_source);
|
||||||
}
|
quad_or = (x_source_2 - x_source) * (1-(y_source_2 - y_source));
|
||||||
|
quad_ol = (1- (x_source_2 - x_source)) * (1-(y_source_2 - y_source));
|
||||||
int memsize = width * height * channels;
|
|
||||||
uint8_t* odata;
|
|
||||||
if (ImageTMP)
|
if ((x_source_1 >= 0) && (x_source_2 < org_width) && (y_source_1 >= 0) && (y_source_2 < org_height))
|
||||||
{
|
{
|
||||||
odata = ImageTMP->RGBImageLock();
|
p_source_ul = rgb_image + (channels * (y_source_1 * org_width + x_source_1));
|
||||||
}
|
p_source_ur = rgb_image + (channels * (y_source_1 * org_width + x_source_2));
|
||||||
else
|
p_source_or = rgb_image + (channels * (y_source_2 * org_width + x_source_1));
|
||||||
{
|
p_source_ol = rgb_image + (channels * (y_source_2 * org_width + x_source_2));
|
||||||
odata = (unsigned char*)GET_MEMORY(memsize);
|
for (int _channels = 0; _channels < channels; ++_channels)
|
||||||
}
|
{
|
||||||
|
p_target[_channels] = (int)((float)p_source_ul[_channels] * quad_ul
|
||||||
|
+ (float)p_source_ur[_channels] * quad_ur
|
||||||
int x_source_1, y_source_1, x_source_2, y_source_2;
|
+ (float)p_source_or[_channels] * quad_or
|
||||||
float x_source, y_source;
|
+ (float)p_source_ol[_channels] * quad_ol);
|
||||||
float quad_ul, quad_ur, quad_ol, quad_or;
|
}
|
||||||
stbi_uc* p_target;
|
}
|
||||||
stbi_uc *p_source_ul, *p_source_ur, *p_source_ol, *p_source_or;
|
else
|
||||||
|
{
|
||||||
RGBImageLock();
|
for (int _channels = 0; _channels < channels; ++_channels)
|
||||||
|
p_target[_channels] = 255;
|
||||||
for (int x = 0; x < width; ++x)
|
}
|
||||||
for (int y = 0; y < height; ++y)
|
}
|
||||||
{
|
|
||||||
p_target = odata + (channels * (y * width + x));
|
// memcpy(rgb_image, odata, memsize);
|
||||||
|
memCopy(odata, rgb_image, memsize);
|
||||||
x_source = (m[0][0] * x + m[0][1] * y);
|
|
||||||
y_source = (m[1][0] * x + m[1][1] * y);
|
if (!ImageTMP)
|
||||||
|
{
|
||||||
x_source += (m[0][2]);
|
free_psram_heap(std::string(TAG) + "->odata", odata);
|
||||||
y_source += (m[1][2]);
|
}
|
||||||
|
if (ImageTMP)
|
||||||
x_source_1 = (int)x_source;
|
ImageTMP->RGBImageRelease();
|
||||||
x_source_2 = x_source_1 + 1;
|
|
||||||
y_source_1 = (int)y_source;
|
RGBImageRelease();
|
||||||
y_source_2 = y_source_1 + 1;
|
}
|
||||||
|
|
||||||
quad_ul = (x_source_2 - x_source) * (y_source_2 - y_source);
|
|
||||||
quad_ur = (1- (x_source_2 - x_source)) * (y_source_2 - y_source);
|
void CRotateImage::Rotate(float _angle)
|
||||||
quad_or = (x_source_2 - x_source) * (1-(y_source_2 - y_source));
|
{
|
||||||
quad_ol = (1- (x_source_2 - x_source)) * (1-(y_source_2 - y_source));
|
// ESP_LOGD(TAG, "width %d, height %d", width, height);
|
||||||
|
Rotate(_angle, width / 2, height / 2);
|
||||||
|
}
|
||||||
if ((x_source_1 >= 0) && (x_source_2 < org_width) && (y_source_1 >= 0) && (y_source_2 < org_height))
|
|
||||||
{
|
void CRotateImage::RotateAntiAliasing(float _angle)
|
||||||
p_source_ul = rgb_image + (channels * (y_source_1 * org_width + x_source_1));
|
{
|
||||||
p_source_ur = rgb_image + (channels * (y_source_1 * org_width + x_source_2));
|
// ESP_LOGD(TAG, "width %d, height %d", width, height);
|
||||||
p_source_or = rgb_image + (channels * (y_source_2 * org_width + x_source_1));
|
RotateAntiAliasing(_angle, width / 2, height / 2);
|
||||||
p_source_ol = rgb_image + (channels * (y_source_2 * org_width + x_source_2));
|
}
|
||||||
for (int _channels = 0; _channels < channels; ++_channels)
|
|
||||||
{
|
void CRotateImage::Translate(int _dx, int _dy)
|
||||||
p_target[_channels] = (int)((float)p_source_ul[_channels] * quad_ul
|
{
|
||||||
+ (float)p_source_ur[_channels] * quad_ur
|
int memsize = width * height * channels;
|
||||||
+ (float)p_source_or[_channels] * quad_or
|
uint8_t* odata;
|
||||||
+ (float)p_source_ol[_channels] * quad_ol);
|
if (ImageTMP)
|
||||||
}
|
{
|
||||||
}
|
odata = ImageTMP->RGBImageLock();
|
||||||
else
|
}
|
||||||
{
|
else
|
||||||
for (int _channels = 0; _channels < channels; ++_channels)
|
{
|
||||||
p_target[_channels] = 255;
|
odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// memcpy(rgb_image, odata, memsize);
|
|
||||||
memCopy(odata, rgb_image, memsize);
|
int x_source, y_source;
|
||||||
|
stbi_uc* p_target;
|
||||||
if (!ImageTMP)
|
stbi_uc* p_source;
|
||||||
{
|
|
||||||
stbi_image_free(odata);
|
RGBImageLock();
|
||||||
}
|
|
||||||
if (ImageTMP)
|
for (int x = 0; x < width; ++x)
|
||||||
ImageTMP->RGBImageRelease();
|
for (int y = 0; y < height; ++y)
|
||||||
|
{
|
||||||
RGBImageRelease();
|
p_target = odata + (channels * (y * width + x));
|
||||||
}
|
|
||||||
|
x_source = x - _dx;
|
||||||
|
y_source = y - _dy;
|
||||||
void CRotateImage::Rotate(float _angle)
|
|
||||||
{
|
if ((x_source >= 0) && (x_source < width) && (y_source >= 0) && (y_source < height))
|
||||||
// ESP_LOGD(TAG, "width %d, height %d", width, height);
|
{
|
||||||
Rotate(_angle, width / 2, height / 2);
|
p_source = rgb_image + (channels * (y_source * width + x_source));
|
||||||
}
|
for (int _channels = 0; _channels < channels; ++_channels)
|
||||||
|
p_target[_channels] = p_source[_channels];
|
||||||
void CRotateImage::RotateAntiAliasing(float _angle)
|
}
|
||||||
{
|
else
|
||||||
// ESP_LOGD(TAG, "width %d, height %d", width, height);
|
{
|
||||||
RotateAntiAliasing(_angle, width / 2, height / 2);
|
for (int _channels = 0; _channels < channels; ++_channels)
|
||||||
}
|
p_target[_channels] = 255;
|
||||||
|
}
|
||||||
void CRotateImage::Translate(int _dx, int _dy)
|
}
|
||||||
{
|
|
||||||
int memsize = width * height * channels;
|
// memcpy(rgb_image, odata, memsize);
|
||||||
uint8_t* odata;
|
memCopy(odata, rgb_image, memsize);
|
||||||
if (ImageTMP)
|
if (!ImageTMP)
|
||||||
{
|
{
|
||||||
odata = ImageTMP->RGBImageLock();
|
free_psram_heap(std::string(TAG) + "->odata", odata);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
if (ImageTMP)
|
||||||
odata = (unsigned char*)GET_MEMORY(memsize);
|
{
|
||||||
}
|
ImageTMP->RGBImageRelease();
|
||||||
|
}
|
||||||
|
RGBImageRelease();
|
||||||
|
|
||||||
int x_source, y_source;
|
}
|
||||||
stbi_uc* p_target;
|
|
||||||
stbi_uc* p_source;
|
|
||||||
|
|
||||||
RGBImageLock();
|
|
||||||
|
|
||||||
for (int x = 0; x < width; ++x)
|
|
||||||
for (int y = 0; y < height; ++y)
|
|
||||||
{
|
|
||||||
p_target = odata + (channels * (y * width + x));
|
|
||||||
|
|
||||||
x_source = x - _dx;
|
|
||||||
y_source = y - _dy;
|
|
||||||
|
|
||||||
if ((x_source >= 0) && (x_source < width) && (y_source >= 0) && (y_source < height))
|
|
||||||
{
|
|
||||||
p_source = rgb_image + (channels * (y_source * width + x_source));
|
|
||||||
for (int _channels = 0; _channels < channels; ++_channels)
|
|
||||||
p_target[_channels] = p_source[_channels];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (int _channels = 0; _channels < channels; ++_channels)
|
|
||||||
p_target[_channels] = 255;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// memcpy(rgb_image, odata, memsize);
|
|
||||||
memCopy(odata, rgb_image, memsize);
|
|
||||||
if (!ImageTMP)
|
|
||||||
{
|
|
||||||
stbi_image_free(odata);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImageTMP)
|
|
||||||
{
|
|
||||||
ImageTMP->RGBImageRelease();
|
|
||||||
}
|
|
||||||
RGBImageRelease();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef CROTATEIMAGE_H
|
#ifndef CROTATEIMAGE_H
|
||||||
#define CROTATEIMAGE_H
|
#define CROTATEIMAGE_H
|
||||||
|
|
||||||
#include "CImageBasis.h"
|
#include "CImageBasis.h"
|
||||||
|
|
||||||
|
|
||||||
class CRotateImage: public CImageBasis
|
class CRotateImage: public CImageBasis
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
CImageBasis *ImageTMP, *ImageOrg;
|
public:
|
||||||
bool doflip;
|
CImageBasis *ImageTMP, *ImageOrg;
|
||||||
CRotateImage(std::string _image, bool _flip = false) : CImageBasis(_image) {ImageTMP = NULL; ImageOrg = NULL; doflip = _flip;};
|
bool doflip;
|
||||||
CRotateImage(uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp, bool _flip = false) : CImageBasis(_rgb_image, _channels, _width, _height, _bpp) {ImageTMP = NULL; ImageOrg = NULL; doflip = _flip;};
|
CRotateImage(std::string name, std::string _image, bool _flip = false) : CImageBasis(name, _image) {ImageTMP = NULL; ImageOrg = NULL; doflip = _flip;};
|
||||||
CRotateImage(CImageBasis *_org, CImageBasis *_temp, bool _flip = false);
|
CRotateImage(std::string name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp, bool _flip = false) : CImageBasis(name, _rgb_image, _channels, _width, _height, _bpp) {ImageTMP = NULL; ImageOrg = NULL; doflip = _flip;};
|
||||||
|
CRotateImage(std::string name, CImageBasis *_org, CImageBasis *_temp, bool _flip = false);
|
||||||
void Rotate(float _angle);
|
|
||||||
void RotateAntiAliasing(float _angle);
|
void Rotate(float _angle);
|
||||||
|
void RotateAntiAliasing(float _angle);
|
||||||
void Rotate(float _angle, int _centerx, int _centery);
|
|
||||||
void RotateAntiAliasing(float _angle, int _centerx, int _centery);
|
void Rotate(float _angle, int _centerx, int _centery);
|
||||||
|
void RotateAntiAliasing(float _angle, int _centerx, int _centery);
|
||||||
void Translate(int _dx, int _dy);
|
|
||||||
void Mirror();
|
void Translate(int _dx, int _dy);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //CROTATEIMAGE_H
|
#endif //CROTATEIMAGE_H
|
||||||
@@ -1,13 +1,28 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include "psram.h"
|
||||||
|
|
||||||
#include "../../include/defines.h"
|
#include "../../include/defines.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define USE_SHARED_PSRAM_FOR_STBI
|
||||||
|
|
||||||
|
#ifdef USE_SHARED_PSRAM_FOR_STBI
|
||||||
|
#define STBI_MALLOC(sz) psram_reserve_shared_stbi_memory(sz)
|
||||||
|
#define STBI_REALLOC(p,newsz) psram_reallocate_shared_stbi_memory(p, newsz)
|
||||||
|
#define STBI_FREE(p) psram_free_shared_stbi_memory(p)
|
||||||
|
#else // Use normal PSRAM
|
||||||
|
#define STBI_MALLOC(sz) malloc_psram_heap("STBI", sz, MALLOC_CAP_SPIRAM)
|
||||||
|
#define STBI_REALLOC(p,newsz) realloc_psram_heap("STBI", p, newsz, MALLOC_CAP_SPIRAM)
|
||||||
|
#define STBI_FREE(p) free_psram_heap("STBI", p)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include "stb_image.h"
|
#include "../stb/stb_image.h"
|
||||||
|
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
#include "stb_image_write.h"
|
#include "../stb/stb_image_write.h"
|
||||||
|
|
||||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||||
#include "stb_image_resize.h"
|
#include "../stb/stb_image_resize.h"
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
|||||||
|
|
||||||
idf_component_register(SRCS ${app_sources}
|
idf_component_register(SRCS ${app_sources}
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "."
|
||||||
REQUIRES tflite-lib esp_http_client jomjol_logfile)
|
REQUIRES esp_http_client jomjol_logfile)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
#include "esp_http_client.h"
|
#include "esp_http_client.h"
|
||||||
|
#include "time_sntp.h"
|
||||||
#include "../../include/defines.h"
|
#include "../../include/defines.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -12,10 +13,90 @@ static const char *TAG = "INFLUXDB";
|
|||||||
|
|
||||||
std::string _influxDBURI;
|
std::string _influxDBURI;
|
||||||
std::string _influxDBDatabase;
|
std::string _influxDBDatabase;
|
||||||
std::string _influxDBMeasurement;
|
|
||||||
std::string _influxDBUser;
|
std::string _influxDBUser;
|
||||||
std::string _influxDBPassword;
|
std::string _influxDBPassword;
|
||||||
|
|
||||||
|
std::string _influxDB_V2_URI;
|
||||||
|
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 _bucket, std::string _org, std::string _token)
|
||||||
|
{
|
||||||
|
_influxDB_V2_URI = _uri;
|
||||||
|
_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, long int _timeUTC)
|
||||||
|
{
|
||||||
|
char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
|
||||||
|
esp_http_client_config_t http_config = {
|
||||||
|
.user_agent = "ESP32 Meter reader",
|
||||||
|
.method = HTTP_METHOD_POST,
|
||||||
|
.event_handler = http_event_handler,
|
||||||
|
.buffer_size = MAX_HTTP_OUTPUT_BUFFER,
|
||||||
|
.user_data = response_buffer
|
||||||
|
};
|
||||||
|
|
||||||
|
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 (_timeUTC > 0)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
payload.shrink_to_fit();
|
||||||
|
|
||||||
|
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_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
|
||||||
|
|
||||||
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "API URI: " + apiURI);
|
||||||
|
|
||||||
|
esp_http_client_handle_t http_client = esp_http_client_init(&http_config);
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "client is initialized");
|
||||||
|
|
||||||
|
esp_http_client_set_header(http_client, "Content-Type", "text/plain");
|
||||||
|
std::string _zw = "Token " + _influxDB_V2_Token;
|
||||||
|
// LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Tokenheader: %s\n", _zw.c_str());
|
||||||
|
esp_http_client_set_header(http_client, "Authorization", _zw.c_str());
|
||||||
|
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "header is set");
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_http_client_set_post_field(http_client, payload.c_str(), payload.length()));
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "post payload is set");
|
||||||
|
|
||||||
|
esp_err_t err = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_http_client_perform(http_client));
|
||||||
|
|
||||||
|
if( err == ESP_OK ) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request was performed");
|
||||||
|
int status_code = esp_http_client_get_status_code(http_client);
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP status code" + std::to_string(status_code));
|
||||||
|
} else {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request failed");
|
||||||
|
}
|
||||||
|
esp_http_client_cleanup(http_client);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static esp_err_t http_event_handler(esp_http_client_event_t *evt)
|
static esp_err_t http_event_handler(esp_http_client_event_t *evt)
|
||||||
{
|
{
|
||||||
switch(evt->event_id)
|
switch(evt->event_id)
|
||||||
@@ -24,7 +105,7 @@ static esp_err_t http_event_handler(esp_http_client_event_t *evt)
|
|||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Error encountered");
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Error encountered");
|
||||||
break;
|
break;
|
||||||
case HTTP_EVENT_ON_CONNECTED:
|
case HTTP_EVENT_ON_CONNECTED:
|
||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Error encountered");
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client connected");
|
||||||
ESP_LOGI(TAG, "HTTP Client Connected");
|
ESP_LOGI(TAG, "HTTP Client Connected");
|
||||||
break;
|
break;
|
||||||
case HTTP_EVENT_HEADERS_SENT:
|
case HTTP_EVENT_HEADERS_SENT:
|
||||||
@@ -42,11 +123,14 @@ static esp_err_t http_event_handler(esp_http_client_event_t *evt)
|
|||||||
case HTTP_EVENT_DISCONNECTED:
|
case HTTP_EVENT_DISCONNECTED:
|
||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Disconnected");
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Disconnected");
|
||||||
break;
|
break;
|
||||||
|
case HTTP_EVENT_REDIRECT:
|
||||||
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Redirect");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InfluxDBPublish(std::string _key, std::string _content, std::string _timestamp) {
|
void InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC) {
|
||||||
char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
|
char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
|
||||||
esp_http_client_config_t http_config = {
|
esp_http_client_config_t http_config = {
|
||||||
.user_agent = "ESP32 Meter reader",
|
.user_agent = "ESP32 Meter reader",
|
||||||
@@ -62,37 +146,32 @@ void InfluxDBPublish(std::string _key, std::string _content, std::string _timest
|
|||||||
http_config.auth_type = HTTP_AUTH_TYPE_BASIC;
|
http_config.auth_type = HTTP_AUTH_TYPE_BASIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBPublish - Key: " + _key + ", Content: " + _content + ", Timestamp: " + _timestamp);
|
std::string payload;
|
||||||
|
|
||||||
// Format: #define PREVALUE_TIME_FORMAT_OUTPUT "%Y-%m-%dT%H:%M:%S%z"
|
|
||||||
struct tm tm;
|
|
||||||
strptime(_timestamp.c_str(), PREVALUE_TIME_FORMAT_OUTPUT, &tm);
|
|
||||||
time_t t = mktime(&tm); // t is now your desired time_t
|
|
||||||
|
|
||||||
struct tm * ptm;
|
|
||||||
ptm = gmtime ( &t );
|
|
||||||
time_t utc = mktime(ptm);
|
|
||||||
|
|
||||||
// time_t now;
|
|
||||||
// time(&now);
|
|
||||||
char nowTimestamp[21];
|
char nowTimestamp[21];
|
||||||
// pad with zeroes to get nanoseconds
|
|
||||||
// sprintf(nowTimestamp,"%ld000000000", (long) now);
|
|
||||||
// sprintf(nowTimestamp,"%ld000000000", (long) t); // Localtime
|
|
||||||
sprintf(nowTimestamp,"%ld000000000", (long) utc); // UTC
|
|
||||||
|
|
||||||
|
|
||||||
// LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Test Time Conversion - t: " + std::to_string(t) + ", utc: " + std::to_string(utc));
|
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBPublish - Key: " + _key + ", Content: " + _content + ", timeUTC: " + std::to_string(_timeUTC));
|
||||||
// LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Test Time Conversion - now: " + std::to_string(now) + ", timestamp: " + std::to_string(t) + "(correct time not used yet)");
|
|
||||||
|
if (_timeUTC > 0)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
std::string payload = _influxDBMeasurement + " " + _key + "=" + _content + " " + nowTimestamp;
|
|
||||||
payload.shrink_to_fit();
|
payload.shrink_to_fit();
|
||||||
|
|
||||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload);
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload);
|
||||||
|
|
||||||
|
|
||||||
// use the default retention policy of the database
|
// use the default retention policy of the bucket
|
||||||
std::string apiURI = _influxDBURI + "/api/v2/write?bucket=" + _influxDBDatabase + "/";
|
std::string apiURI = _influxDBURI + "/write?db=" + _influxDBDatabase;
|
||||||
|
// std::string apiURI = _influxDBURI + "/api/v2/write?bucket=" + _influxDBDatabase + "/";
|
||||||
|
|
||||||
apiURI.shrink_to_fit();
|
apiURI.shrink_to_fit();
|
||||||
http_config.url = apiURI.c_str();
|
http_config.url = apiURI.c_str();
|
||||||
|
|
||||||
@@ -120,10 +199,9 @@ void InfluxDBPublish(std::string _key, std::string _content, std::string _timest
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void InfluxDBInit(std::string _uri, std::string _database, std::string _measurement, std::string _user, std::string _password){
|
void InfluxDBInit(std::string _uri, std::string _database, std::string _user, std::string _password){
|
||||||
_influxDBURI = _uri;
|
_influxDBURI = _uri;
|
||||||
_influxDBDatabase = _database;
|
_influxDBDatabase = _database;
|
||||||
_influxDBMeasurement = _measurement;
|
|
||||||
_influxDBUser = _user;
|
_influxDBUser = _user;
|
||||||
_influxDBPassword = _password;
|
_influxDBPassword = _password;
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,17 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
void InfluxDBInit(std::string _influxDBURI, std::string _database, std::string _measurement, std::string _user, std::string _password);
|
// Interface to InfluxDB v1.x
|
||||||
void InfluxDBdestroy();
|
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, long int _timeUTC);
|
||||||
|
|
||||||
void InfluxDBPublish(std::string _key, std::string _content, std::string _timestamp);
|
// Interface to InfluxDB v2.x
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void InfluxDBdestroy();
|
||||||
|
|
||||||
#endif //INTERFACE_INFLUXDB_H
|
#endif //INTERFACE_INFLUXDB_H
|
||||||
#endif //ENABLE_INFLUXDB
|
#endif //ENABLE_INFLUXDB
|
||||||
@@ -15,6 +15,7 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "Helper.h"
|
#include "Helper.h"
|
||||||
|
#include "time_sntp.h"
|
||||||
#include "../../include/defines.h"
|
#include "../../include/defines.h"
|
||||||
|
|
||||||
static const char *TAG = "LOGFILE";
|
static const char *TAG = "LOGFILE";
|
||||||
@@ -78,11 +79,11 @@ void ClassLogFile::WriteToData(std::string _timestamp, std::string _name, std::s
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ClassLogFile::setLogLevel(esp_log_level_t _logLevel){
|
void ClassLogFile::setLogLevel(esp_log_level_t _logLevel)
|
||||||
loglevel = _logLevel;
|
{
|
||||||
|
|
||||||
std::string levelText;
|
std::string levelText;
|
||||||
|
|
||||||
|
// Print log level to log file
|
||||||
switch(_logLevel) {
|
switch(_logLevel) {
|
||||||
case ESP_LOG_WARN:
|
case ESP_LOG_WARN:
|
||||||
levelText = "WARNING";
|
levelText = "WARNING";
|
||||||
@@ -95,13 +96,16 @@ void ClassLogFile::setLogLevel(esp_log_level_t _logLevel){
|
|||||||
case ESP_LOG_DEBUG:
|
case ESP_LOG_DEBUG:
|
||||||
levelText = "DEBUG";
|
levelText = "DEBUG";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ESP_LOG_ERROR:
|
case ESP_LOG_ERROR:
|
||||||
default:
|
default:
|
||||||
levelText = "ERROR";
|
levelText = "ERROR";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Set log level to " + levelText);
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Log Level set to %s", levelText.c_str());
|
// set new log level
|
||||||
|
loglevel = _logLevel;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Test");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Test");
|
||||||
@@ -318,15 +322,23 @@ void ClassLogFile::RemoveOldLogFile()
|
|||||||
//ESP_LOGD(TAG, "compare log file: %s to %s", entry->d_name, cmpfilename);
|
//ESP_LOGD(TAG, "compare log file: %s to %s", entry->d_name, cmpfilename);
|
||||||
if ((strlen(entry->d_name) == strlen(cmpfilename)) && (strcmp(entry->d_name, cmpfilename) < 0)) {
|
if ((strlen(entry->d_name) == strlen(cmpfilename)) && (strcmp(entry->d_name, cmpfilename) < 0)) {
|
||||||
//ESP_LOGD(TAG, "delete log file: %s", entry->d_name);
|
//ESP_LOGD(TAG, "delete log file: %s", entry->d_name);
|
||||||
std::string filepath = logroot + "/" + entry->d_name;
|
std::string filepath = logroot + "/" + entry->d_name;
|
||||||
if (unlink(filepath.c_str()) == 0) {
|
if ((strcmp(entry->d_name, "log_1970-01-01.txt") == 0) && getTimeWasNotSetAtBoot()) { // keep logfile log_1970-01-01.txt if time was not set at boot (some boot logs are in there)
|
||||||
deleted ++;
|
//ESP_LOGD(TAG, "Skip deleting this file: %s", entry->d_name);
|
||||||
} else {
|
notDeleted++;
|
||||||
ESP_LOGE(TAG, "can't delete file: %s", entry->d_name);
|
|
||||||
notDeleted ++;
|
|
||||||
}
|
}
|
||||||
} else {
|
else {
|
||||||
notDeleted ++;
|
if (unlink(filepath.c_str()) == 0) {
|
||||||
|
deleted++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ESP_LOGE(TAG, "can't delete file: %s", entry->d_name);
|
||||||
|
notDeleted++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
notDeleted++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -386,14 +398,17 @@ void ClassLogFile::RemoveOldDataLog()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ClassLogFile::CreateLogDirectories()
|
bool ClassLogFile::CreateLogDirectories()
|
||||||
{
|
{
|
||||||
MakeDir("/sdcard/log");
|
bool bRetval = false;
|
||||||
MakeDir("/sdcard/log/data");
|
bRetval = MakeDir("/sdcard/log");
|
||||||
MakeDir("/sdcard/log/analog");
|
bRetval = MakeDir("/sdcard/log/data");
|
||||||
MakeDir("/sdcard/log/digit");
|
bRetval = MakeDir("/sdcard/log/analog");
|
||||||
MakeDir("/sdcard/log/message");
|
bRetval = MakeDir("/sdcard/log/digit");
|
||||||
MakeDir("/sdcard/log/source");
|
bRetval = MakeDir("/sdcard/log/message");
|
||||||
|
bRetval = MakeDir("/sdcard/log/source");
|
||||||
|
|
||||||
|
return bRetval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public:
|
|||||||
|
|
||||||
void CloseLogFileAppendHandle();
|
void CloseLogFileAppendHandle();
|
||||||
|
|
||||||
void CreateLogDirectories();
|
bool CreateLogDirectories();
|
||||||
void RemoveOldLogFile();
|
void RemoveOldLogFile();
|
||||||
void RemoveOldDataLog();
|
void RemoveOldDataLog();
|
||||||
|
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
|||||||
|
|
||||||
idf_component_register(SRCS ${app_sources}
|
idf_component_register(SRCS ${app_sources}
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "."
|
||||||
REQUIRES tflite-lib mqtt jomjol_tfliteclass jomjol_helper jomjol_mqtt jomjol_wlan)
|
REQUIRES esp_timer esp-tflite-micro mqtt jomjol_tfliteclass jomjol_helper jomjol_mqtt jomjol_wlan json)
|
||||||
|
|||||||
@@ -2,18 +2,28 @@
|
|||||||
#include "interface_mqtt.h"
|
#include "interface_mqtt.h"
|
||||||
|
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#if DEBUG_DETAIL_ON
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#endif
|
||||||
#include "connect_wlan.h"
|
#include "connect_wlan.h"
|
||||||
#include "mqtt_client.h"
|
#include "mqtt_client.h"
|
||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
#include "server_tflite.h"
|
#include "MainFlowControl.h"
|
||||||
|
#include "cJSON.h"
|
||||||
#include "../../include/defines.h"
|
#include "../../include/defines.h"
|
||||||
|
|
||||||
|
#if DEBUG_DETAIL_ON
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static const char *TAG = "MQTT IF";
|
static const char *TAG = "MQTT IF";
|
||||||
|
|
||||||
std::map<std::string, std::function<void()>>* connectFunktionMap = NULL;
|
std::map<std::string, std::function<void()>>* connectFunktionMap = NULL;
|
||||||
std::map<std::string, std::function<bool(std::string, char*, int)>>* subscribeFunktionMap = NULL;
|
std::map<std::string, std::function<bool(std::string, char*, int)>>* subscribeFunktionMap = NULL;
|
||||||
|
|
||||||
int failedOnRound = -1;
|
int failedOnRound = -1;
|
||||||
|
int MQTTReconnectCnt = 0;
|
||||||
|
|
||||||
esp_mqtt_event_id_t esp_mqtt_ID = MQTT_EVENT_ANY;
|
esp_mqtt_event_id_t esp_mqtt_ID = MQTT_EVENT_ANY;
|
||||||
// ESP_EVENT_ANY_ID
|
// ESP_EVENT_ANY_ID
|
||||||
@@ -25,11 +35,13 @@ bool mqtt_connected = false;
|
|||||||
|
|
||||||
esp_mqtt_client_handle_t client = NULL;
|
esp_mqtt_client_handle_t client = NULL;
|
||||||
std::string uri, client_id, lwt_topic, lwt_connected, lwt_disconnected, user, password, maintopic;
|
std::string uri, client_id, lwt_topic, lwt_connected, lwt_disconnected, user, password, maintopic;
|
||||||
int keepalive, SetRetainFlag;
|
std::string caCert, clientCert, clientKey;
|
||||||
void (*callbackOnConnected)(std::string, int) = NULL;
|
int keepalive;
|
||||||
|
bool SetRetainFlag;
|
||||||
|
void (*callbackOnConnected)(std::string, bool) = NULL;
|
||||||
|
|
||||||
|
|
||||||
bool MQTTPublish(std::string _key, std::string _content, int retained_flag)
|
bool MQTTPublish(std::string _key, std::string _content, int qos, bool retained_flag)
|
||||||
{
|
{
|
||||||
if (!mqtt_enabled) { // MQTT sevice not started / configured (MQTT_Init not called before)
|
if (!mqtt_enabled) { // MQTT sevice not started / configured (MQTT_Init not called before)
|
||||||
return false;
|
return false;
|
||||||
@@ -49,7 +61,7 @@ bool MQTTPublish(std::string _key, std::string _content, int retained_flag)
|
|||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
long long int starttime = esp_timer_get_time();
|
long long int starttime = esp_timer_get_time();
|
||||||
#endif
|
#endif
|
||||||
int msg_id = esp_mqtt_client_publish(client, _key.c_str(), _content.c_str(), 0, 1, retained_flag);
|
int msg_id = esp_mqtt_client_publish(client, _key.c_str(), _content.c_str(), 0, qos, retained_flag);
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
ESP_LOGD(TAG, "Publish msg_id %d in %lld ms", msg_id, (esp_timer_get_time() - starttime)/1000);
|
ESP_LOGD(TAG, "Publish msg_id %d in %lld ms", msg_id, (esp_timer_get_time() - starttime)/1000);
|
||||||
#endif
|
#endif
|
||||||
@@ -58,7 +70,7 @@ bool MQTTPublish(std::string _key, std::string _content, int retained_flag)
|
|||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
starttime = esp_timer_get_time();
|
starttime = esp_timer_get_time();
|
||||||
#endif
|
#endif
|
||||||
msg_id = esp_mqtt_client_publish(client, _key.c_str(), _content.c_str(), 0, 1, retained_flag);
|
msg_id = esp_mqtt_client_publish(client, _key.c_str(), _content.c_str(), 0, qos, retained_flag);
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
ESP_LOGD(TAG, "Publish msg_id %d in %lld ms", msg_id, (esp_timer_get_time() - starttime)/1000);
|
ESP_LOGD(TAG, "Publish msg_id %d in %lld ms", msg_id, (esp_timer_get_time() - starttime)/1000);
|
||||||
#endif
|
#endif
|
||||||
@@ -88,29 +100,39 @@ static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event) {
|
|||||||
std::string topic = "";
|
std::string topic = "";
|
||||||
switch (event->event_id) {
|
switch (event->event_id) {
|
||||||
case MQTT_EVENT_BEFORE_CONNECT:
|
case MQTT_EVENT_BEFORE_CONNECT:
|
||||||
ESP_LOGD(TAG, "MQTT_EVENT_BEFORE_CONNECT");
|
|
||||||
mqtt_initialized = true;
|
mqtt_initialized = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MQTT_EVENT_CONNECTED:
|
case MQTT_EVENT_CONNECTED:
|
||||||
ESP_LOGD(TAG, "MQTT_EVENT_CONNECTED");
|
MQTTReconnectCnt = 0;
|
||||||
mqtt_initialized = true;
|
mqtt_initialized = true;
|
||||||
mqtt_connected = true;
|
mqtt_connected = true;
|
||||||
MQTTconnected();
|
MQTTconnected();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MQTT_EVENT_DISCONNECTED:
|
case MQTT_EVENT_DISCONNECTED:
|
||||||
ESP_LOGD(TAG, "MQTT_EVENT_DISCONNECTED");
|
|
||||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Disconnected from broker");
|
|
||||||
mqtt_connected = false;
|
mqtt_connected = false;
|
||||||
|
MQTTReconnectCnt++;
|
||||||
|
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected, trying to reconnect");
|
||||||
|
|
||||||
|
if (MQTTReconnectCnt >= 5) {
|
||||||
|
MQTTReconnectCnt = 0;
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Disconnected, multiple reconnect attempts failed, still retrying...");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MQTT_EVENT_SUBSCRIBED:
|
case MQTT_EVENT_SUBSCRIBED:
|
||||||
ESP_LOGD(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
|
ESP_LOGD(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MQTT_EVENT_UNSUBSCRIBED:
|
case MQTT_EVENT_UNSUBSCRIBED:
|
||||||
ESP_LOGD(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
|
ESP_LOGD(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MQTT_EVENT_PUBLISHED:
|
case MQTT_EVENT_PUBLISHED:
|
||||||
ESP_LOGD(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
|
ESP_LOGD(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MQTT_EVENT_DATA:
|
case MQTT_EVENT_DATA:
|
||||||
ESP_LOGD(TAG, "MQTT_EVENT_DATA");
|
ESP_LOGD(TAG, "MQTT_EVENT_DATA");
|
||||||
ESP_LOGD(TAG, "TOPIC=%.*s", event->topic_len, event->topic);
|
ESP_LOGD(TAG, "TOPIC=%.*s", event->topic_len, event->topic);
|
||||||
@@ -125,7 +147,37 @@ static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event) {
|
|||||||
ESP_LOGW(TAG, "no handler available\r\n");
|
ESP_LOGW(TAG, "no handler available\r\n");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MQTT_EVENT_ERROR:
|
case MQTT_EVENT_ERROR:
|
||||||
|
// http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718033 --> chapter 3.2.2.3
|
||||||
|
|
||||||
|
// The server does not support the level of the MQTT protocol requested by the client
|
||||||
|
// NOTE: Only protocol 3.1.1 is supported (refer to setting in sdkconfig)
|
||||||
|
if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_PROTOCOL) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, unacceptable protocol version (0x01)");
|
||||||
|
}
|
||||||
|
// The client identifier is correct UTF-8 but not allowed by the server
|
||||||
|
// e.g. clientID empty (cannot be the case -> default set in firmware)
|
||||||
|
else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_ID_REJECTED) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, identifier rejected (0x02)");
|
||||||
|
}
|
||||||
|
// The network connection has been made but the MQTT service is unavailable
|
||||||
|
else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_SERVER_UNAVAILABLE) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, Server unavailable (0x03)");
|
||||||
|
}
|
||||||
|
// The data in the user name or password is malformed
|
||||||
|
else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_BAD_USERNAME) {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, malformed data in username or password (0x04)");
|
||||||
|
}
|
||||||
|
// The client is not authorized to connect
|
||||||
|
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
|
#ifdef DEBUG_DETAIL_ON
|
||||||
ESP_LOGD(TAG, "MQTT_EVENT_ERROR - esp_mqtt_error_codes:");
|
ESP_LOGD(TAG, "MQTT_EVENT_ERROR - esp_mqtt_error_codes:");
|
||||||
ESP_LOGD(TAG, "error_type:%d", event->error_handle->error_type);
|
ESP_LOGD(TAG, "error_type:%d", event->error_handle->error_type);
|
||||||
@@ -135,8 +187,9 @@ static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event) {
|
|||||||
ESP_LOGD(TAG, "esp_tls_stack_err:%d", event->error_handle->esp_tls_stack_err);
|
ESP_LOGD(TAG, "esp_tls_stack_err:%d", event->error_handle->esp_tls_stack_err);
|
||||||
ESP_LOGD(TAG, "esp_tls_cert_verify_flags:%d", event->error_handle->esp_tls_cert_verify_flags);
|
ESP_LOGD(TAG, "esp_tls_cert_verify_flags:%d", event->error_handle->esp_tls_cert_verify_flags);
|
||||||
#endif
|
#endif
|
||||||
mqtt_connected = false;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ESP_LOGD(TAG, "Other event id:%d", event->event_id);
|
ESP_LOGD(TAG, "Other event id:%d", event->event_id);
|
||||||
break;
|
break;
|
||||||
@@ -146,14 +199,15 @@ static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event) {
|
|||||||
|
|
||||||
|
|
||||||
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
|
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
|
||||||
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
|
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, (int)event_id);
|
||||||
mqtt_event_handler_cb((esp_mqtt_event_handle_t) event_data);
|
mqtt_event_handler_cb((esp_mqtt_event_handle_t) event_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password,
|
bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password,
|
||||||
std::string _maintopic, std::string _lwt, std::string _lwt_connected, std::string _lwt_disconnected,
|
std::string _maintopic, std::string _lwt, std::string _lwt_connected, std::string _lwt_disconnected,
|
||||||
int _keepalive, int _SetRetainFlag, void *_callbackOnConnected) {
|
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))
|
if ((_mqttURI.length() == 0) || (_maintopic.length() == 0) || (_clientid.length() == 0))
|
||||||
{
|
{
|
||||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Init aborted! Config error (URI, MainTopic or ClientID missing)");
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Init aborted! Config error (URI, MainTopic or ClientID missing)");
|
||||||
@@ -168,7 +222,26 @@ bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _us
|
|||||||
keepalive = _keepalive;
|
keepalive = _keepalive;
|
||||||
SetRetainFlag = _SetRetainFlag;
|
SetRetainFlag = _SetRetainFlag;
|
||||||
maintopic = _maintopic;
|
maintopic = _maintopic;
|
||||||
callbackOnConnected = ( void (*)(std::string, int) )(_callbackOnConnected);
|
callbackOnConnected = ( void (*)(std::string, bool) )(_callbackOnConnected);
|
||||||
|
|
||||||
|
if (_clientcertfilename.length() && _clientkeyfilename.length()){
|
||||||
|
std::ifstream cert_ifs(_clientcertfilename);
|
||||||
|
std::string cert_content((std::istreambuf_iterator<char>(cert_ifs)), (std::istreambuf_iterator<char>()));
|
||||||
|
clientCert = cert_content;
|
||||||
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using clientCert: " + _clientcertfilename);
|
||||||
|
|
||||||
|
std::ifstream key_ifs(_clientkeyfilename);
|
||||||
|
std::string key_content((std::istreambuf_iterator<char>(key_ifs)), (std::istreambuf_iterator<char>()));
|
||||||
|
clientKey = key_content;
|
||||||
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using clientKey: " + _clientkeyfilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_cacertfilename.length() ){
|
||||||
|
std::ifstream ifs(_cacertfilename);
|
||||||
|
std::string content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
|
||||||
|
caCert = content;
|
||||||
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using caCert: " + _cacertfilename);
|
||||||
|
}
|
||||||
|
|
||||||
if (_user.length() && _password.length()){
|
if (_user.length() && _password.length()){
|
||||||
user = _user;
|
user = _user;
|
||||||
@@ -208,25 +281,38 @@ int MQTT_Init() {
|
|||||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Init");
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Init");
|
||||||
MQTTdestroy_client(false);
|
MQTTdestroy_client(false);
|
||||||
|
|
||||||
esp_mqtt_client_config_t mqtt_cfg = {
|
esp_mqtt_client_config_t mqtt_cfg = { };
|
||||||
.uri = uri.c_str(),
|
|
||||||
.client_id = client_id.c_str(),
|
|
||||||
.lwt_topic = lwt_topic.c_str(),
|
|
||||||
.lwt_msg = lwt_disconnected.c_str(),
|
|
||||||
.lwt_retain = 1,
|
|
||||||
.lwt_msg_len = (int)(lwt_disconnected.length()),
|
|
||||||
.keepalive = keepalive,
|
|
||||||
.disable_auto_reconnect = false, // Reconnection routine active (Default: false)
|
|
||||||
.buffer_size = 1536, // size of MQTT send/receive buffer (Default: 1024)
|
|
||||||
.reconnect_timeout_ms = 15000, // Try to reconnect to broker (Default: 10000ms)
|
|
||||||
.network_timeout_ms = 20000, // Network Timeout (Default: 10000ms)
|
|
||||||
.message_retransmit_timeout = 3000 // Tiem after message resent when broker not acknowledged (QoS1, QoS2)
|
|
||||||
|
|
||||||
};
|
mqtt_cfg.broker.address.uri = uri.c_str();
|
||||||
|
mqtt_cfg.credentials.client_id = client_id.c_str();
|
||||||
|
mqtt_cfg.network.disable_auto_reconnect = false; // Reconnection routine active (Default: false)
|
||||||
|
mqtt_cfg.network.reconnect_timeout_ms = 15000; // Try to reconnect to broker (Default: 10000ms)
|
||||||
|
mqtt_cfg.network.timeout_ms = 20000; // Network Timeout (Default: 10000ms)
|
||||||
|
mqtt_cfg.session.message_retransmit_timeout = 3000; // Time after message resent when broker not acknowledged (QoS1, QoS2)
|
||||||
|
mqtt_cfg.session.last_will.topic = lwt_topic.c_str();
|
||||||
|
mqtt_cfg.session.last_will.retain = 1;
|
||||||
|
mqtt_cfg.session.last_will.msg = lwt_disconnected.c_str();
|
||||||
|
mqtt_cfg.session.last_will.msg_len = (int)(lwt_disconnected.length());
|
||||||
|
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()){
|
if (user.length() && password.length()){
|
||||||
mqtt_cfg.username = user.c_str();
|
mqtt_cfg.credentials.username = user.c_str();
|
||||||
mqtt_cfg.password = password.c_str();
|
mqtt_cfg.credentials.authentication.password = password.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
@@ -298,22 +384,61 @@ bool getMQTTisConnected() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool mqtt_handler_flow_start(std::string _topic, char* _data, int _data_len) {
|
bool mqtt_handler_flow_start(std::string _topic, char* _data, int _data_len)
|
||||||
|
{
|
||||||
ESP_LOGD(TAG, "Handler called: topic %s, data %.*s", _topic.c_str(), _data_len, _data);
|
ESP_LOGD(TAG, "Handler called: topic %s, data %.*s", _topic.c_str(), _data_len, _data);
|
||||||
|
|
||||||
if (_data_len > 0) {
|
if (_data_len > 0) {
|
||||||
MQTTCtrlFlowStart(_topic);
|
MQTTCtrlFlowStart(_topic);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_flow_start: handler called, but no data");
|
||||||
|
}
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool mqtt_handler_set_prevalue(std::string _topic, char* _data, int _data_len)
|
||||||
|
{
|
||||||
|
//ESP_LOGD(TAG, "Handler called: topic %s, data %.*s", _topic.c_str(), _data_len, _data);
|
||||||
|
//example: {"numbersname": "main", "value": 12345.1234567}
|
||||||
|
|
||||||
|
if (_data_len > 0) { // Check if data length > 0
|
||||||
|
cJSON *jsonData = cJSON_Parse(_data);
|
||||||
|
cJSON *numbersname = cJSON_GetObjectItemCaseSensitive(jsonData, "numbersname");
|
||||||
|
cJSON *value = cJSON_GetObjectItemCaseSensitive(jsonData, "value");
|
||||||
|
|
||||||
|
if (cJSON_IsString(numbersname) && (numbersname->valuestring != NULL)) { // Check if numbersname is valid
|
||||||
|
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)) {
|
||||||
|
cJSON_Delete(jsonData);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: value not a valid number (\"value\": 12345.12345)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void MQTTconnected(){
|
void MQTTconnected(){
|
||||||
if (mqtt_connected) {
|
if (mqtt_connected) {
|
||||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Connected to broker");
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Connected to broker");
|
||||||
MQTTPublish(lwt_topic, lwt_connected, true); // Publish "connected" to maintopic/connection
|
|
||||||
|
|
||||||
if (connectFunktionMap != NULL) {
|
if (connectFunktionMap != NULL) {
|
||||||
for(std::map<std::string, std::function<void()>>::iterator it = connectFunktionMap->begin(); it != connectFunktionMap->end(); ++it) {
|
for(std::map<std::string, std::function<void()>>::iterator it = connectFunktionMap->begin(); it != connectFunktionMap->end(); ++it) {
|
||||||
it->second();
|
it->second();
|
||||||
@@ -321,9 +446,14 @@ void MQTTconnected(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Subcribe to topics */
|
// Subcribe to topics
|
||||||
std::function<bool(std::string topic, char* data, int data_len)> subHandler = mqtt_handler_flow_start;
|
// Note: Further subsriptions are handled in GPIO class
|
||||||
MQTTregisterSubscribeFunction(maintopic + "/ctrl/flow_start", subHandler); // subcribe to maintopic/ctrl/flow_start
|
//*****************************************
|
||||||
|
std::function<bool(std::string topic, char* data, int data_len)> subHandler1 = mqtt_handler_flow_start;
|
||||||
|
MQTTregisterSubscribeFunction(maintopic + "/ctrl/flow_start", subHandler1); // subcribe to maintopic/ctrl/flow_start
|
||||||
|
|
||||||
|
std::function<bool(std::string topic, char* data, int data_len)> subHandler2 = mqtt_handler_set_prevalue;
|
||||||
|
MQTTregisterSubscribeFunction(maintopic + "/ctrl/set_prevalue", subHandler2); // subcribe to maintopic/ctrl/set_prevalue
|
||||||
|
|
||||||
if (subscribeFunktionMap != NULL) {
|
if (subscribeFunktionMap != NULL) {
|
||||||
for(std::map<std::string, std::function<bool(std::string, char*, int)>>::iterator it = subscribeFunktionMap->begin(); it != subscribeFunktionMap->end(); ++it) {
|
for(std::map<std::string, std::function<bool(std::string, char*, int)>>::iterator it = subscribeFunktionMap->begin(); it != subscribeFunktionMap->end(); ++it) {
|
||||||
@@ -332,7 +462,7 @@ void MQTTconnected(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vTaskDelay(10000 / portTICK_PERIOD_MS); // Delay execution of callback routine after connection got established
|
/* Send Static Topics and Homeassistant Discovery */
|
||||||
if (callbackOnConnected) { // Call onConnected callback routine --> mqtt_server
|
if (callbackOnConnected) { // Call onConnected callback routine --> mqtt_server
|
||||||
callbackOnConnected(maintopic, SetRetainFlag);
|
callbackOnConnected(maintopic, SetRetainFlag);
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user