mirror of
https://github.com/jomjol/AI-on-the-edge-device.git
synced 2025-12-06 19:46:54 +03:00
Compare commits
327 Commits
v15.0.1
...
16.0.0-RC2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37086987d0 | ||
|
|
a75035e9ed | ||
|
|
2f1d7e577c | ||
|
|
89c36374b3 | ||
|
|
ca01f5a38f | ||
|
|
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 | ||
|
|
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 | ||
|
|
2c1a7f4c9e | ||
|
|
3d711f495e | ||
|
|
b21e3c6c9d | ||
|
|
23d2ae627d | ||
|
|
3f62abf878 | ||
|
|
025f4af9f2 | ||
|
|
3d92860c5e | ||
|
|
2ed9fb8eb5 | ||
|
|
598db004ae | ||
|
|
70332fe142 | ||
|
|
10da8c4f94 | ||
|
|
5bac1c68d9 | ||
|
|
008dba7e11 | ||
|
|
7283bfd506 | ||
|
|
44e186e65b | ||
|
|
55be652dc1 | ||
|
|
1acd72d33e | ||
|
|
795bcd0d21 | ||
|
|
0b2e38935b | ||
|
|
a9c5bebb45 | ||
|
|
876adc51af | ||
|
|
bf090f3762 | ||
|
|
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!
|
||||
# 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
|
||||
#######################################################################
|
||||
@@ -90,9 +115,9 @@
|
||||
labeled:
|
||||
issue:
|
||||
body: |
|
||||
See [Digital Digits](https://jomjol.github.io/neural-network-digital-counter-readout) resp. [Analogue Pointers](https://jomjol.github.io/neural-network-analog-needle-readout) for an overview of all trained data.
|
||||
See [Digits](https://jomjol.github.io/neural-network-digital-counter-readout) resp. [Analogue Pointers](https://jomjol.github.io/neural-network-analog-needle-readout) for an overview of all trained data.
|
||||
If your type is not contained it can be added to our training material, see [here](https://jomjol.github.io/AI-on-the-edge-device-docs/collect-new-images/).
|
||||
discussion:
|
||||
body: |
|
||||
See [Digital Digits](https://jomjol.github.io/neural-network-digital-counter-readout) resp. [Analogue Pointers](https://jomjol.github.io/neural-network-analog-needle-readout) for an overview of all trained data.
|
||||
See [Digits](https://jomjol.github.io/neural-network-digital-counter-readout) resp. [Analogue Pointers](https://jomjol.github.io/neural-network-analog-needle-readout) for an overview of all trained data.
|
||||
If your type is not contained it can be added to our training material, see [here](https://jomjol.github.io/AI-on-the-edge-device-docs/collect-new-images/).
|
||||
131
.github/workflows/build.yaml
vendored
131
.github/workflows/build.yaml
vendored
@@ -14,8 +14,8 @@ jobs:
|
||||
uses: fkirc/skip-duplicate-actions@v5
|
||||
with:
|
||||
concurrent_skipping: same_content_newer
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
@@ -25,28 +25,28 @@ jobs:
|
||||
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Update PIP cache on every commit
|
||||
uses: actions/cache@v3.2.3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: pip-${{ github.run_id }}
|
||||
restore-keys: pip # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
- name: Update PlatformIO cache on every commit
|
||||
uses: actions/cache@v3.2.3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: platformio-${{ github.run_id }}
|
||||
restore-keys: platformio # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
|
||||
- name: Update Build cache on every commit
|
||||
uses: actions/cache@v3.2.3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ./code/.pio/
|
||||
key: build-${{ github.run_id }}
|
||||
restore-keys: build # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
|
||||
- name: Update generated-files cache on every commit
|
||||
uses: actions/cache@v3.2.3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
./code/.pio/build/esp32cam/firmware.bin
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
@@ -70,13 +70,22 @@ jobs:
|
||||
#run: echo "Testing... ${{ github.ref_name }}, ${{ steps.vars.outputs.sha_short }}" > ./sd-card/html/version.txt; mkdir -p ./code/.pio/build/esp32cam/; cd ./code/.pio/build/esp32cam/; echo "${{ steps.vars.outputs.sha_short }}" > firmware.bin; cp firmware.bin partitions.bin; cp firmware.bin bootloader.bin # Testing
|
||||
run: cd code; platformio run --environment esp32cam
|
||||
|
||||
- name: Prepare Web UI (copy data from repo and update hashes in all files)
|
||||
- name: Prepare Web UI (generate tooltip pages and update hashes in all files)
|
||||
run: |
|
||||
rm -rf ./html
|
||||
mkdir html
|
||||
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' {} \;
|
||||
|
||||
|
||||
|
||||
#########################################################################################
|
||||
@@ -86,16 +95,16 @@ jobs:
|
||||
# New OTA concept
|
||||
# update__version.zip file with following content:
|
||||
# - /firmware.bin
|
||||
# - (optional) /html/*
|
||||
# - (optional) /html/* (inkl. subfolders)
|
||||
# - (optional) /config/*.tfl
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Update generated-files cache on every commit
|
||||
uses: actions/cache@v3.2.3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
./code/.pio/build/esp32cam/firmware.bin
|
||||
@@ -106,12 +115,12 @@ jobs:
|
||||
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
- name: Update update cache on every commit
|
||||
uses: actions/cache@v3.2.3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: update
|
||||
key: update-${{ github.run_id }}
|
||||
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: Set Variables
|
||||
id: vars
|
||||
run: |
|
||||
@@ -135,13 +144,12 @@ jobs:
|
||||
cp ./sd-card/config/*.tflite ./update/config/ 2>/dev/null || true
|
||||
|
||||
- name: Upload update as update.zip artifact (Firmware + Web UI + CNN)
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "AI-on-the-edge-device__update__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
|
||||
path: ./update/*
|
||||
|
||||
|
||||
|
||||
#########################################################################################
|
||||
## Pack for Remote Setup
|
||||
#########################################################################################
|
||||
@@ -149,16 +157,16 @@ jobs:
|
||||
# New Remote Setup concept
|
||||
# remote_setup__version.zip file with following content:
|
||||
# - /firmware.bin
|
||||
# - /html/*
|
||||
# - /html/* (inkl. subfolders)
|
||||
# - /config/*
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Update generated-files cache on every commit
|
||||
uses: actions/cache@v3.2.3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
./code/.pio/build/esp32cam/firmware.bin
|
||||
@@ -167,9 +175,9 @@ jobs:
|
||||
./html/*
|
||||
key: generated-files-${{ github.run_id }}
|
||||
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
|
||||
- name: Update remote_setup cache on every commit
|
||||
uses: actions/cache@v3.2.3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: remote_setup
|
||||
key: remote_setup-${{ github.run_id }}
|
||||
@@ -196,7 +204,7 @@ jobs:
|
||||
cp ./sd-card/config/* ./remote_setup/config/ 2>/dev/null || true
|
||||
|
||||
- name: Upload remote_setup as remote_setup.zip artifact (Firmware + Web UI + Config)
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "AI-on-the-edge-device__remote-setup__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
|
||||
path: ./remote_setup/*
|
||||
@@ -211,10 +219,10 @@ jobs:
|
||||
needs: build
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Update generated-files cache on every commit
|
||||
uses: actions/cache@v3.2.3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
./code/.pio/build/esp32cam/firmware.bin
|
||||
@@ -225,7 +233,7 @@ jobs:
|
||||
restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
- name: Update manual_setup cache on every commit
|
||||
uses: actions/cache@v3.2.3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: manual_setup
|
||||
key: manual_setup-${{ github.run_id }}
|
||||
@@ -252,9 +260,9 @@ jobs:
|
||||
cp -r ./html ./sd-card/ # Overwrite the Web UI with the preprocessed files
|
||||
cd sd-card; zip -r ../manual_setup/sd-card.zip *; cd ..
|
||||
cd ./manual_setup
|
||||
|
||||
|
||||
- name: Upload manual_setup.zip artifact (Firmware + Bootloader + Partitions + Web UI)
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "AI-on-the-edge-device__manual-setup__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})"
|
||||
path: ./manual_setup
|
||||
@@ -275,24 +283,24 @@ jobs:
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Update update cache on every commit
|
||||
uses: actions/cache@v3.2.3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: update
|
||||
key: update-${{ github.run_id }}
|
||||
restore-keys: update # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
|
||||
- name: Update remote_setup cache on every commit
|
||||
uses: actions/cache@v3.2.3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: remote_setup
|
||||
key: remote_setup-${{ github.run_id }}
|
||||
restore-keys: remote_setup # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache
|
||||
|
||||
|
||||
- name: Update manual_setup cache on every commit
|
||||
uses: actions/cache@v3.2.3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: manual_setup
|
||||
key: manual_setup-${{ github.run_id }}
|
||||
@@ -323,34 +331,30 @@ jobs:
|
||||
|
||||
# extract the version used in next step
|
||||
- id: get_version
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: Simply007/get-version-action@v2
|
||||
uses: dhkatz/get-version-action@v3.0.0
|
||||
|
||||
# # the changelog [unreleased] will now be changed to the release version
|
||||
# - name: Update changelog
|
||||
# uses: thomaseizinger/keep-a-changelog-new-release@v1
|
||||
# if: startsWith(github.ref, 'refs/tags/')
|
||||
# with:
|
||||
# changelogPath: Changelog.md
|
||||
# version: ${{ steps.get_version.outputs.version-without-v }}
|
||||
|
||||
# the release notes will be extracted from changelog
|
||||
- name: Extract release notes
|
||||
id: extract-release-notes
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: ffurrer2/extract-release-notes@v1
|
||||
with:
|
||||
changelog_file: Changelog.md
|
||||
|
||||
# # the release notes will be extracted from changelog
|
||||
# - name: Extract release notes
|
||||
# id: extract-release-notes
|
||||
# uses: ffurrer2/extract-release-notes@v1
|
||||
# with:
|
||||
# changelog_file: Changelog.md
|
||||
|
||||
# Releases should only be created on master by tagging the last commit.
|
||||
# all artifacts in firmware folder pushed to the release
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2.0.8
|
||||
# Note:
|
||||
# If you get the error "Resource not accessible by integration",
|
||||
# The access rights are not sufficient, see
|
||||
# https://github.com/softprops/action-gh-release/issues/232#issuecomment-1131379440
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
name: ${{ steps.get_version.outputs.version-without-v }}
|
||||
body: ${{ steps.extract-release-notes.outputs.release_notes }}
|
||||
@@ -359,7 +363,6 @@ jobs:
|
||||
|
||||
# # Commit&Push Changelog to master branch. Must be manually merged back to rolling
|
||||
# - name: Commit changes and push changes
|
||||
# if: startsWith(github.ref, 'refs/tags/')
|
||||
# run: |
|
||||
# git config user.name github-actions
|
||||
# git config user.email github-actions@github.com
|
||||
@@ -371,8 +374,9 @@ jobs:
|
||||
#########################################################################################
|
||||
## Update the Web Installer on a release
|
||||
#########################################################################################
|
||||
# This is the same as in the update-webinstaller.yml
|
||||
update-web-installer:
|
||||
# Make sure to also update update-webinstaller.yml!
|
||||
update-web-installer:
|
||||
if: github.event_name == 'release' && github.event.action == 'published' # Only run on release but not on prerelease
|
||||
needs: [release]
|
||||
environment:
|
||||
name: github-pages
|
||||
@@ -387,8 +391,8 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get version of last release
|
||||
id: last_release
|
||||
uses: mindojo/get-latest-release@0b8ef1434d7468d6bffcc8263baff5c777f72321
|
||||
@@ -396,23 +400,26 @@ jobs:
|
||||
myToken: ${{ github.token }}
|
||||
exclude_types: "draft|prerelease"
|
||||
view_top: 1
|
||||
|
||||
|
||||
- name: Add binary to Web Installer and update manifest
|
||||
run: |
|
||||
echo "Updating Web installer to use firmware from ${{ steps.last_release.outputs.tag_name }}..."
|
||||
rm -f docs/binary/firmware.bin
|
||||
wget https://github.com/jomjol/AI-on-the-edge-device/releases/download/${{ steps.last_release.outputs.tag_name }}/AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
||||
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
|
||||
cp -f firmware.bin docs/binary/firmware.bin
|
||||
cp -f docs/manifest_template.json docs/manifest.json
|
||||
sed -i 's/VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
|
||||
echo "Updating index and manifest file..."
|
||||
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/index.html
|
||||
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v2
|
||||
uses: actions/configure-pages@v4
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v1
|
||||
uses: actions/upload-pages-artifact@v2
|
||||
with:
|
||||
path: 'docs'
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v1
|
||||
uses: actions/deploy-pages@v3 # Note: v4 does not work!
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# This updates the Web Installer with the files from the docs folder and the binary of the latest release
|
||||
# it only gets run on:
|
||||
# - Changes to the docs folder in the `rolling` branch
|
||||
# - On a release
|
||||
# - Manually triggered
|
||||
# Make sure to also update the lower part of build.yml!
|
||||
|
||||
name: Manual Web Installer Update
|
||||
|
||||
@@ -12,7 +12,7 @@ on:
|
||||
# - rolling
|
||||
# paths:
|
||||
# - docs # The path filter somehow does not work, so lets run it on every change to rolling
|
||||
|
||||
|
||||
jobs:
|
||||
manually-update-web-installer:
|
||||
environment:
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
|
||||
- name: Get version of last release
|
||||
id: last_release
|
||||
uses: mindojo/get-latest-release@0b8ef1434d7468d6bffcc8263baff5c777f72321
|
||||
@@ -37,15 +37,17 @@ jobs:
|
||||
myToken: ${{ github.token }}
|
||||
exclude_types: "draft|prerelease"
|
||||
view_top: 1
|
||||
|
||||
|
||||
- name: Add binary to Web Installer and update manifest
|
||||
run: |
|
||||
echo "Updating Web installer to use firmware from ${{ steps.last_release.outputs.tag_name }}..."
|
||||
rm -f docs/binary/firmware.bin
|
||||
wget https://github.com/jomjol/AI-on-the-edge-device/releases/download/${{ steps.last_release.outputs.tag_name }}/AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
||||
unzip AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
|
||||
cp -f firmware.bin docs/binary/firmware.bin
|
||||
cp -f docs/manifest_template.json docs/manifest.json
|
||||
sed -i 's/VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
|
||||
echo "Updating index and manifest file..."
|
||||
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/index.html
|
||||
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v2
|
||||
@@ -58,4 +60,3 @@ jobs:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 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
|
||||
|
||||
name: Reply-Bot
|
||||
@@ -20,12 +20,11 @@ jobs:
|
||||
comment:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
####################################################################
|
||||
## 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!
|
||||
####################################################################
|
||||
# - name: Remove 'Logfile' label again (issues only)
|
||||
@@ -69,11 +68,12 @@ jobs:
|
||||
# with:
|
||||
# actions: 'remove-labels'
|
||||
# labels: 'bot-reply Show Trained Digits/Pointers'
|
||||
|
||||
|
||||
####################################################################
|
||||
## Write the response
|
||||
####################################################################
|
||||
- name: Write Response
|
||||
uses: peaceiris/actions-label-commenter@c2d00660c86f2b9ed0fb35b372c451558eba85b3
|
||||
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
|
||||
code/edgeAI.code-workspace
|
||||
.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"]
|
||||
path = code/components/esp-nn
|
||||
url = https://github.com/espressif/esp-nn.git
|
||||
[submodule "code/components/tflite-micro-esp-examples"]
|
||||
path = code/components/tflite-micro-esp-examples
|
||||
url = https://github.com/espressif/tflite-micro-esp-examples.git
|
||||
[submodule "code/components/esp-tflite-micro"]
|
||||
path = code/components/esp-tflite-micro
|
||||
url = https://github.com/espressif/esp-tflite-micro.git
|
||||
[submodule "code/components/stb"]
|
||||
path = code/components/stb
|
||||
url = https://github.com/nothings/stb.git
|
||||
|
||||
340
Changelog.md
340
Changelog.md
@@ -1,11 +1,283 @@
|
||||
## [15.0.1] - 2023-02-23
|
||||
## [16.0.0-RC2] - 2024-10-04
|
||||
|
||||
**Parameter Migration**
|
||||
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.7.0...v16.0.0-RC1)
|
||||
|
||||
#### Known issues
|
||||
Please check the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues) and
|
||||
[discussions](https://github.com/jomjol/AI-on-the-edge-device/discussions) before reporting a new issue.
|
||||
|
||||
#### Core Changes
|
||||
Only changes since RC1 are listed:
|
||||
- Updated parameter documentation pages
|
||||
- Rename/remove unused parameters (#3291)
|
||||
- Migrate-cam-parameters (#3288)
|
||||
|
||||
#### Bug Fixes
|
||||
Only changes since RC1 are listed:
|
||||
- Reverted TFlite submodule update as certain modules crash with it (#3269)
|
||||
- Changed the webhook UploadImg to false (#3279)
|
||||
- Changed default value from boolean to numeric value in parameter camDenoise documentation
|
||||
- Updated config page
|
||||
|
||||
## [16.0.0-RC1] - 2024-09-24
|
||||
|
||||
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.7.0...v16.0.0-RC1)
|
||||
|
||||
#### Known issues
|
||||
Please check the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues) and
|
||||
[discussions](https://github.com/jomjol/AI-on-the-edge-device/discussions) before reporting a new issue.
|
||||
|
||||
#### Core Changes
|
||||
Those are just the major changes:
|
||||
- Add support for OV5640 camera (#3063)
|
||||
- New tflite-Models
|
||||
- Homeassistant service discovery: derive node_id when using nested topics (#3088)
|
||||
- Added Prometheus/OpenMetrics exporter (#3081)
|
||||
- Added Webhook (#3148, #3163, #3174)
|
||||
- Add rate threshold parameter (#3195)
|
||||
- Added a Delay between the WiFi reconnections (#3068)
|
||||
- Web UI improvements
|
||||
- Various minor changes
|
||||
- Update platformIO to 6.9.0 (Contains ESP IDF 5.3.1)
|
||||
|
||||
#### Bug Fixes
|
||||
Those are just the major changes:
|
||||
- Handle crash on corrupted model (#3220)
|
||||
- Bugfix for boot loop (#3175)
|
||||
- Bugfix for time stamp (#3180)
|
||||
- Handle empty prevalue.ini gracefully (#3162)
|
||||
- Added note about only TLS 1.2 is supported (#3213)
|
||||
|
||||
## [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 / digit 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 / digit 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`
|
||||
- Digit 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.
|
||||
@@ -27,15 +299,18 @@ If you want to revert back to `v14` or earlier, you will have to revert the migr
|
||||
|
||||
#### Fixed
|
||||
|
||||
- [2036](https://github.com/jomjol/AI-on-the-edge-device/issues/2036) Fix wrong url-encoding
|
||||
- [#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
|
||||
|
||||
**Stabilization and Improved User Experience**
|
||||
## [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:
|
||||
|
||||
@@ -89,7 +364,7 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/
|
||||
|
||||
## [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)
|
||||
|
||||
@@ -100,7 +375,7 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/
|
||||
- Improved OTA Update mechanism (only working after installation for next update)
|
||||
- Added data logging in `/log/data` - One day per file and each measurement is on one line
|
||||
- Format: csv - comma separated
|
||||
- Content: `time`, `name-of-number`, `raw-value`, `return-value`, `pre-value`, `change-rate`, `change-absolute`, `error-text`, `cnn-digital`, `cnn-analog`
|
||||
- Content: `time`, `name-of-number`, `raw-value`, `return-value`, `pre-value`, `change-rate`, `change-absolute`, `error-text`, `cnn-digit`, `cnn-analog`
|
||||
- Show graph of values direct in the user interface (thanks to [@rdmueller](https://github.com/rdmueller))
|
||||
|
||||
- Using new data logging (see above)
|
||||
@@ -118,10 +393,10 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/
|
||||
- Updated OTA functionality (more robust, but not fully bullet prove yet)
|
||||
- Updated Espressif library to `espressif32@v5.2.0`
|
||||
- [#1176](https://github.com/jomjol/AI-on-the-edge-device/discussions/1176) accept minor negative values (-0.2) if extended resolution is enabled
|
||||
- [#1143](https://github.com/jomjol/AI-on-the-edge-device/issues/1143) added config parameter `AnalogDigitalTransitionStart`. It can setup very early and very late digit transition starts.
|
||||
- [#1143](https://github.com/jomjol/AI-on-the-edge-device/issues/1143) added config parameter `AnalogDigTransitionStart`. It can setup very early and very late digit transition starts.
|
||||
- New version of `dig-class100` (v1.4.0): added images of heliowatt powermeter
|
||||
- NEW v13.0.2: Update Tool "Logfile downloader and combiner" to handle the new csv file format.
|
||||
- NEW v13.0.2: MQTT: Added MQTT topic `status` (Digitalization Status), Timezone to MQTT topic `timestamp`.#
|
||||
- NEW v13.0.2: MQTT: Added MQTT topic `status` (Digitization Status), Timezone to MQTT topic `timestamp`.#
|
||||
- NEW v13.0.2: Logging: Disable heap logs by default, cleanup
|
||||
- NEW v13.0.7:
|
||||
- log NTP server name
|
||||
@@ -169,7 +444,7 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/
|
||||
|
||||
## [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:
|
||||
|
||||
@@ -246,7 +521,7 @@ Intermediate Digits
|
||||
|
||||
- Updated analog neural network file (`ana-cont_11.3.0_s2.tflite` - default, `ana-class100_0120_s1_q.tflite`)
|
||||
|
||||
- Updated digital neural network file (`dig-cont_0570_s3.tflite` - default, `dig-class100_0120_s2_q.tflite`)
|
||||
- Updated digit neural network file (`dig-cont_0570_s3.tflite` - default, `dig-class100_0120_s2_q.tflite`)
|
||||
|
||||
- Added automated filtering of tflite-file in the graphical configuration (thanks to @**[caco3](https://github.com/caco3)**)
|
||||
|
||||
@@ -258,8 +533,8 @@ Intermediate Digits
|
||||
|
||||
Intermediate Digits
|
||||
|
||||
- New and improved consistency check (especially with analog and digital counters mixed)
|
||||
- Bug Fix: digital counter algorithm
|
||||
- New and improved consistency check (especially with analog and digit counters mixed)
|
||||
- Bug Fix: digit counter algorithm
|
||||
|
||||
## [11.0.1](https://github.com/jomjol/AI-on-the-edge-device/releases/tag/v11.0.1), 2022-08-18
|
||||
|
||||
@@ -299,7 +574,7 @@ Stability Increase
|
||||
|
||||
- `config.ini`: removal of modelsize (readout from tflite)
|
||||
|
||||
- Updated analog neural network file (`ana1000s2.tflite`) & digital neural network file (`dig1400s2q.tflite`)
|
||||
- Updated analog neural network file (`ana1000s2.tflite`) & digit neural network file (`dig1400s2q.tflite`)
|
||||
|
||||
- TFMicro/Lite: Update (espressif Version 20220716)
|
||||
|
||||
@@ -335,7 +610,7 @@ Stability Increase
|
||||
- In the future the new files will also be copied to the `firmware` directory of the repository
|
||||
- Added Wifi RSSI to MQTT information
|
||||
- Updated analog neural network file (`ana-s3-q-20220105.tflite`)
|
||||
- Updated digital neural network file (`dig-s1-q-20220102.tflite`)
|
||||
- Updated digit neural network file (`dig-s1-q-20220102.tflite`)
|
||||
- Updated build environment to `Espressif 3.5.0`
|
||||
|
||||
## [10.3.0] - (2022-01-29)
|
||||
@@ -401,7 +676,7 @@ Stability Increase
|
||||
|
||||
- Update analog neural network (ana-s3-q-20220105.tflite)
|
||||
|
||||
- Update digital neural network (dig-s1-q-20220102.tflite)
|
||||
- Update digit neural network (dig-s1-q-20220102.tflite)
|
||||
|
||||
- Increased web-server buffers
|
||||
|
||||
@@ -438,7 +713,7 @@ External Illumination
|
||||
- Direct JSON access: `http://IP-ADRESS/json`
|
||||
- Error message in log file in case camera error during startup
|
||||
- Upgrade analog CNN to v9.1.0
|
||||
- Upgrade digital CNN to v13.3.0 (added new images)
|
||||
- Upgrade digit CNN to v13.3.0 (added new images)
|
||||
- html: support of different ports
|
||||
|
||||
## [9.1.1] - External Illumination (2021-11-16)
|
||||
@@ -463,7 +738,7 @@ External Illumination
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade digital CNN to v13.1.0 (added new images)
|
||||
- Upgrade digit CNN to v13.1.0 (added new images)
|
||||
- bug fix: wlan password with space, double digit output
|
||||
|
||||
## [8.4.0] - Multi Meter Support (2021-09-25)
|
||||
@@ -493,7 +768,7 @@ External Illumination
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade digital CNN to v12.1.0 (added new images)
|
||||
- Upgrade digit CNN to v12.1.0 (added new images)
|
||||
- Dedicated NaN handling, internal refactoring (CNN-Handling)
|
||||
- HTML: confirmation after config.ini update
|
||||
- Bug fixing
|
||||
@@ -515,7 +790,7 @@ External Illumination
|
||||
- GPIO: using the general mqtt main topic for GPIO
|
||||
|
||||
|
||||
- Upgrade digital CNN to v12.0.0 (added new images)
|
||||
- Upgrade digit CNN to v12.0.0 (added new images)
|
||||
- Update tfmicro to new master (2021-08-07)
|
||||
- Bug fix: remove text in mqtt value, remove connect limit in wlan reconnet
|
||||
|
||||
@@ -551,7 +826,7 @@ External Illumination
|
||||
|
||||
- Update wlan handling to esp-idf 4.1
|
||||
|
||||
- Upgrade digital CNN to v8.7.0 (added new images)
|
||||
- Upgrade digit CNN to v8.7.0 (added new images)
|
||||
|
||||
- Bug fix: MQTT, WLAN, LED-Controll, GPIO usage, fixed IP, calculation flow rate
|
||||
|
||||
@@ -562,7 +837,7 @@ External Illumination
|
||||
- NEW: 7.0.1: bug fix wlan password with "="
|
||||
|
||||
|
||||
- Upgrade digital CNN to v8.5.0 (added new images)
|
||||
- Upgrade digit CNN to v8.5.0 (added new images)
|
||||
|
||||
- New MQTT topics: flow rate (units/minute), time stamp (last correct read readout)
|
||||
|
||||
@@ -579,7 +854,7 @@ External Illumination
|
||||
|
||||
- NEW 6.7.1: Improved stability of camera (back to v6.6.1) - remove black strips and areas
|
||||
|
||||
- Upgrade digital CNN to v8.3.0 (added new type of digits)
|
||||
- Upgrade digit CNN to v8.3.0 (added new type of digits)
|
||||
|
||||
- Internal update: TFlite (v2.5), esp32cam, startup sequence
|
||||
|
||||
@@ -600,7 +875,7 @@ External Illumination
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade digital CNN to v8.2.0 (added new type of digits)
|
||||
- Upgrade digit CNN to v8.2.0 (added new type of digits)
|
||||
|
||||
|
||||
- Supporting alignment structures in ROI definition
|
||||
@@ -636,7 +911,7 @@ External Illumination
|
||||
|
||||
- Determination of fixed illumination settings during startup - speed up of 5s in each run
|
||||
|
||||
- Update digital CNN to v8.1.1 (additional digital images trained)
|
||||
- Update digit CNN to v8.1.1 (additional digit images trained)
|
||||
|
||||
- Extended error message in MQTT error message
|
||||
|
||||
@@ -648,7 +923,7 @@ External Illumination
|
||||
|
||||
### Changed
|
||||
|
||||
- Disabling of analog / digital counters in configuration
|
||||
- Disabling of analog / digit counters in configuration
|
||||
|
||||
|
||||
- Improved Alignment Algorithm (`AlignmentAlgo` = `Default`, `Accurate` , `Fast`)
|
||||
@@ -668,7 +943,7 @@ External Illumination
|
||||
|
||||
- MQTT: Last Will Testament (LWT) implemented: "connection lost" in case of connection lost to `TopicError`
|
||||
- Disabled `CheckDigitIncreaseConsistency` in default configuration - must now be explicit enabled if needed
|
||||
- Update digital CNN to v7.2.1 (additional digital images trained)
|
||||
- Update digit CNN to v7.2.1 (additional digit images trained)
|
||||
- Setting of arbitrary time server in `config.ini`
|
||||
- Option for fixed IP-, DNS-Settings in `wlan.ini`
|
||||
- Increased stability (internal image and camera handling)
|
||||
@@ -702,7 +977,7 @@ External Illumination
|
||||
|
||||
- standardized access to current logfile via `http://IP-ADRESS/logfileact`
|
||||
|
||||
- Update digital CNN to v7.2.0, analog CNN to 6.3.0
|
||||
- Update digit CNN to v7.2.0, analog CNN to 6.3.0
|
||||
|
||||
- Bug fixing: truncation error, CheckDigitConsistency & PreValue implementation
|
||||
|
||||
@@ -721,7 +996,7 @@ External Illumination
|
||||
|
||||
### Changed
|
||||
|
||||
- Update digital CNN to v6.5.0 and HTML (Info to hostname, IP, ssid)
|
||||
- Update digit CNN to v6.5.0 and HTML (Info to hostname, IP, ssid)
|
||||
|
||||
- New implementation of "checkDigitConsistency" also for digits
|
||||
|
||||
@@ -837,7 +1112,12 @@ External Illumination
|
||||
- Initial Version
|
||||
|
||||
|
||||
[15.0.1]: https://github.com/jomjol/AI-on-the-edge-device/compare/v14.0.3...v15.0.1
|
||||
[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.7]: https://github.com/jomjol/AI-on-the-edge-device/compare/v12.0.1...v13.0.7
|
||||
|
||||
@@ -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.
|
||||
|
||||
1. Who ever has a new idea can put it here, so it that it is not forgotten.
|
||||
1. Whoever has a new idea can put it here, so that it is not forgotten.
|
||||
|
||||
2. Who ever has time, capacity and passion to support, can take any of the ideas and implement them.
|
||||
I will support and help where ever I can!
|
||||
2. Whoever has the time, capacity and passion to support the project can take any of the ideas and implement them. I will provide support and help wherever I can!
|
||||
|
||||
|
||||
|
||||
____
|
||||
|
||||
|
||||
#### #40 Trigger with cron like exact time slot
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/2470
|
||||
|
||||
|
||||
|
||||
#### #39 upnp implementation to auto detect the device
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/2481
|
||||
|
||||
|
||||
|
||||
#### #38 Energy Saving
|
||||
|
||||
* Deep sleep between recognition
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/2486
|
||||
|
||||
|
||||
|
||||
#### #37 Auto init SD card
|
||||
|
||||
* Fully implement the SD card handling (including formatting) into the firmware
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/2488Demo
|
||||
|
||||
#### #36 Run demo without camera
|
||||
|
||||
Demo mode requires a working camera (if not, one receives a 'Cam bad' error). Would be nice to demo or play around on other ESP32 boards (or on ESP32-CAM boards when you broke the camera cable...).
|
||||
|
||||
#### #35 Use the same model, but provide the image from a Smartphone Camera
|
||||
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
|
||||
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
|
||||
|
||||
~~* 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
|
||||
|
||||
@@ -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
|
||||
|
||||
~~* 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
|
||||
<img src="images/icon/watermeter.svg" width="100px">
|
||||
|
||||
Artificial intelligence based systems have been established in our every days live. Just think of speech or image recognition. Most of the systems relay on either powerful processors or a direct connection to the cloud for doing the calculations up there. With the increasing power of modern processors the AI systems are coming closer to the end user - which is usually called **edge computing**.
|
||||
Here this edge computing is brought into a practical oriented example, where a AI network is implemented on a ESP32 device so: **AI on the edge**.
|
||||
Artificial intelligence based systems have become established in our everyday lives. Just think of speech or image recognition. Most of the systems rely on either powerful processors or a direct connection to the cloud for doing the calculations there. With the increasing power of modern processors, the AI systems are coming closer to the end user – which is usually called **edge computing**.
|
||||
Here, this edge computing is put into a practically oriented example, where an AI network is implemented on an ESP32 device so: **AI on the edge**.
|
||||
|
||||
This projects allows you to digitalize your **analoge** water, gas, power and other meters using cheap and easily available hardware.
|
||||
This project allows you to digitize your **analog** water, gas, power and other meters using cheap and easily available hardware.
|
||||
|
||||
All you need is an [ESP32 board with a supported camera](https://jomjol.github.io/AI-on-the-edge-device-docs/Hardware-Compatibility/) and a bit of a practical hand.
|
||||
All you need is an [ESP32 board with a supported camera](https://jomjol.github.io/AI-on-the-edge-device-docs/Hardware-Compatibility/) and something of a practical hand.
|
||||
|
||||
<img src="images/esp32-cam.png" width="200px">
|
||||
|
||||
## Key features
|
||||
- Tensorflow Lite (TFlite) integration - including easy to use wrapper
|
||||
- Inline Image processing (feature detection, alignment, ROI extraction)
|
||||
- **Small** and **cheap** device (3x4.5x2 cm³, < 10 EUR)
|
||||
- camera and illumination integrated
|
||||
- Web surface to administrate and control
|
||||
- OTA-Interface to update directly through the web interface
|
||||
- Tensorflow Lite (TFlite) integration – including easy-to-use wrapper
|
||||
- Inline image processing (feature detection, alignment, ROI extraction)
|
||||
- **Small** and **cheap** device (3 x 4.5 x 2 cm³, < 10 EUR)
|
||||
- Integrated camera and illumination
|
||||
- Web interface for administration and control
|
||||
- OTA interface for updating directly via the web interface
|
||||
- Full integration into Homeassistant
|
||||
- Support for Influx DB 1
|
||||
- Support for Influx DB 1 and 2
|
||||
- MQTT
|
||||
- REST API
|
||||
|
||||
## Workflow
|
||||
The device takes a photo of your meter at a defined interval. It then extracts the Regions of Interest (ROI's) out of it and runs them through an artificial inteligence. As a result, you get the digitalized value of your meter.
|
||||
The device takes a photo of your meter at a defined interval. It then extracts the Regions of Interest (ROIs) from the image and runs them through artificial intelligence. As a result, you get the digitized value of your meter.
|
||||
|
||||
There are several options what to do with that value. Either send it to a MQTT broker, write it to an InfluxDb or simply provide it throug a REST API.
|
||||
There are several options for what to do with that value. Either send it to an MQTT broker, write it to an InfluxDb or simply provide access to it via a REST API.
|
||||
|
||||
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/idea.jpg" width="600">
|
||||
|
||||
@@ -41,62 +41,68 @@ There are several options what to do with that value. Either send it to a MQTT b
|
||||
|
||||
|
||||
## Setup
|
||||
There is a growing [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/) which provides you with a lot of information.
|
||||
Head there to get a start, set it up and configure it.
|
||||
There is growing [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/) which provides you with a lot of information. Head there to get a start, set it up and configure it.
|
||||
|
||||
There are also a articles in the German Heise magazine "make:" about the setup and the technical background (behind a paywall) : [DIY - Setup](https://www.heise.de/select/make/2021/2/2103513300897420296)
|
||||
There are also articles in the German Heise magazine "make:" about the setup and technical background (behind a paywall): [DIY - Setup](https://www.heise.de/select/make/2021/2/2103513300897420296)
|
||||
|
||||
For further background information, head to [Neural Networks](https://www.heise.de/select/make/2021/6/2126410443385102621), [Training Neural Networks](https://www.heise.de/select/make/2022/1/2134114065999161585) and [Programming on the ESP32](https://www.heise.de/select/make/2022/2/2204010051597422030)
|
||||
A lot of people created useful Youtube videos which might help you getting started.
|
||||
Here a small selection:
|
||||
|
||||
- [youtube.com/watch?v=HKBofb1cnNc](https://www.youtube.com/watch?v=HKBofb1cnNc)
|
||||
- [youtube.com/watch?v=yyf0ORNLCk4](https://www.youtube.com/watch?v=yyf0ORNLCk4)
|
||||
- [youtube.com/watch?v=XxmTubGek6M](https://www.youtube.com/watch?v=XxmTubGek6M)
|
||||
- [youtube.com/watch?v=mDIJEyElkAU](https://www.youtube.com/watch?v=mDIJEyElkAU)
|
||||
- [youtube.com/watch?v=SssiPkyKVVs](https://www.youtube.com/watch?v=SssiPkyKVVs)
|
||||
- [youtube.com/watch?v=MAHE_QyHZFQ](https://www.youtube.com/watch?v=MAHE_QyHZFQ)
|
||||
- [youtube.com/watch?v=Uap_6bwtILQ](https://www.youtube.com/watch?v=Uap_6bwtILQ)
|
||||
|
||||
For further background information, head to [Neural Networks](https://www.heise.de/select/make/2021/6/2126410443385102621), [Training Neural Networks](https://www.heise.de/select/make/2022/1/2134114065999161585) and [Programming on the ESP32](https://www.heise.de/select/make/2022/2/2204010051597422030).
|
||||
|
||||
### Download
|
||||
The latest available version is available on the [Releases page](https://github.com/jomjol/AI-on-the-edge-device/releases).
|
||||
The latest available version can be found on the [Releases page](https://github.com/jomjol/AI-on-the-edge-device/releases).
|
||||
|
||||
### Flashing of the ESP32
|
||||
Initially you will have to flash the ESP32 through an USB connection. Later an update is possible directly over the Air (OTA).
|
||||
### Flashing the ESP32
|
||||
Initially you will have to flash the ESP32 via a USB connection. Later updates are possible directly over the air (OTA using WIFI).
|
||||
|
||||
There are different ways to flash your ESP32:
|
||||
- [Web Installer and Console](https://jomjol.github.io/AI-on-the-edge-device/index.html) (Webbrowser based tool to flash the ESP32 and extract the Log over USB)
|
||||
- The preferred 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
|
||||
- ESPtool (Command Line Tool)
|
||||
- ESPtool (command-line tool)
|
||||
|
||||
See the [Docu](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/) for more information.
|
||||
See the [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/) for more information.
|
||||
|
||||
### Flashing the SD-Card
|
||||
The SD-Card must be flashed separately, see the [Docu](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/) for details.
|
||||
### Flashing the SD Card
|
||||
The SD card can be setup automatically after the firmware got installed. See the [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#remote-setup-using-the-built-in-access-point) for details. For this to work, the SD card must be FAT formated (which is the default on a new SD card).
|
||||
Alternatively the SD card still can be setup manually, see the [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#3-sd-card) for details!
|
||||
|
||||
## Casing
|
||||
|
||||
A 3d-printable housing can be found here:
|
||||
Various 3D-printable housing can be found here:
|
||||
- https://www.thingiverse.com/thing:4573481 (Water Meter)
|
||||
- https://www.thingiverse.com/thing:5028229 (Power Meter)
|
||||
- https://www.thingiverse.com/thing:5224101 (Gas Meter)
|
||||
- https://www.thingiverse.com/thing:4571627 (ESP32-Cam housing only)
|
||||
|
||||
## Build it yourself
|
||||
See [Build Instructions](code/README.md).
|
||||
- https://www.thingiverse.com/thing:4571627 (ESP32-cam housing only)
|
||||
|
||||
## Donate
|
||||
If you would like to support the developer with a cup of coffee you can do that via [Paypal](https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL).
|
||||
If you would like to support the developer with a cup of coffee, you can do that via [PayPal](https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL).
|
||||
|
||||
<form action="https://www.paypal.com/donate" method="post" target="_top">
|
||||
<input type="hidden" name="hosted_button_id" value="8TRSVYNYKDSWL" />
|
||||
<input type="image" src="https://www.paypalobjects.com/en_US/DK/i/btn/btn_donateCC_LG.gif" border="0" name="submit" title="PayPal - The safer, easier way to pay online!" alt="Donate with PayPal button" />
|
||||
<img alt="" border="0" src="https://www.paypal.com/en_DE/i/scr/pixel.gif" width="1" height="1" />
|
||||
</form>
|
||||
If you have any technical topics, you can create an [Issue](https://github.com/jomjol/AI-on-the-edge-device/issues).
|
||||
<a href="https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL"><img border="0" src="images/paypal.png" width="200px" target="_blank"></a>
|
||||
|
||||
In other cases you can contact the developer via email: <img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/mail.jpg" height="25">
|
||||
## Support
|
||||
If you have any technical problems please search the [discussions](https://github.com/jomjol/AI-on-the-edge-device/discussions). In case you found a ug or have a feature request, please open an [issue](https://github.com/jomjol/AI-on-the-edge-device/issues).
|
||||
|
||||
In other cases you can contact the developer via email: <img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/mail.jpg" height="25">
|
||||
|
||||
## Changes and History
|
||||
See [Changelog](Changelog.md)
|
||||
See [Changelog](Changelog.md).
|
||||
|
||||
## Build It Yourself
|
||||
See [Build Instructions](code/README.md).
|
||||
|
||||
## Tools
|
||||
|
||||
* Logfile downloader and combiner (Thx to [reserve85](https://github.com/reserve85))
|
||||
* Files see ['/tools/logfile-tool'](tbd), How-to see [Docu](https://jomjol.github.io/AI-on-the-edge-device-docs/outdated--Gasmeter-Log-Downloader/)
|
||||
* Files see ['/tools/logfile-tool'](tbd), how-to see [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/outdated--Gasmeter-Log-Downloader/)
|
||||
|
||||
## Additional Ideas
|
||||
There are some ideas and feature requests which are not followed currently - mainly due to capacity reasons on side of the developer. They are collected here: [FeatureRequest.md](FeatureRequest.md)
|
||||
|
||||
------
|
||||
|
||||
There are some ideas and feature requests which are not currently being pursued – mainly due to capacity reasons on the part of the developers.
|
||||
They features are collected in the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues) and in [FeatureRequest.md](FeatureRequest.md).
|
||||
|
||||
1
code/.gitignore
vendored
1
code/.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
.pio
|
||||
.idea
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.16.0)
|
||||
|
||||
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common components/tflite-micro-esp-examples/components/tflite-lib)
|
||||
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common components/esp-tflite-micro)
|
||||
|
||||
ADD_CUSTOM_COMMAND(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.cpp
|
||||
|
||||
@@ -8,6 +8,15 @@ git checkout rolling
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
## Update Submodules
|
||||
```
|
||||
cd /components/submodule-name (e.g. tflite-micro-example)
|
||||
git checkout VERSION (e.g. HASH of latest tflite-micro-example build)
|
||||
cd ../../ (auf Ebene von code)
|
||||
git submodule update --init
|
||||
```
|
||||
Evt. muss man vorher noch einige Verzeichnisse in compenents von Hand löschen, da sie beim checkout nicht gelöscht wurden (vor update -- init)
|
||||
|
||||
## Build and Flash within terminal
|
||||
See further down to build it within an IDE.
|
||||
### Compile
|
||||
@@ -60,3 +69,6 @@ pio device monitor -p /dev/ttyUSB0
|
||||
- `pio run --target erase` to erase the flash
|
||||
- `pio run --target upload` this will upload the `bootloader.bin, partitions.bin,firmware.bin` from the `code/.pio/build/esp32cam/` folder.
|
||||
- `pio device monitor` to observe the logs via uart
|
||||
|
||||
# Update Parameters
|
||||
If you create or rename a parameter, make sure to update its documentation in `../param-docs/parameter-pages`! Check the `../param-docs/README.md` for more information.
|
||||
|
||||
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 0032f1734e
Submodule code/components/esp32-camera updated: 5c8349f4cf...dba8da9898
@@ -1,5 +1,32 @@
|
||||
#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;
|
||||
intr_handle_t SmartLed::_interruptHandle = NULL;
|
||||
|
||||
|
||||
@@ -35,6 +35,20 @@
|
||||
#include <cassert>
|
||||
#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 )
|
||||
extern "C" { // ...someone forgot to put in the includes...
|
||||
#include "esp32-hal.h"
|
||||
@@ -65,6 +79,27 @@
|
||||
#include <stdio.h>
|
||||
#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"
|
||||
|
||||
namespace detail {
|
||||
|
||||
@@ -6,9 +6,6 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_event.h"
|
||||
|
||||
#include "server_tflite.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
|
||||
@@ -22,8 +19,10 @@
|
||||
#include "ClassLogFile.h"
|
||||
#include "configFile.h"
|
||||
#include "Helper.h"
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
#include "interface_mqtt.h"
|
||||
#include "interface_mqtt.h"
|
||||
#include "server_mqtt.h"
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
|
||||
@@ -82,10 +81,10 @@ static void gpioHandlerTask(void *arg) {
|
||||
|
||||
void GpioPin::gpioInterrupt(int value) {
|
||||
#ifdef ENABLE_MQTT
|
||||
if (_mqttTopic != "") {
|
||||
if (_mqttTopic.compare("") != 0) {
|
||||
ESP_LOGD(TAG, "gpioInterrupt %s %d", _mqttTopic.c_str(), value);
|
||||
|
||||
MQTTPublish(_mqttTopic, value ? "true" : "false");
|
||||
MQTTPublish(_mqttTopic, value ? "true" : "false", 1);
|
||||
}
|
||||
#endif //ENABLE_MQTT
|
||||
currentState = value;
|
||||
@@ -115,7 +114,7 @@ void GpioPin::init()
|
||||
}
|
||||
|
||||
#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);
|
||||
MQTTregisterSubscribeFunction(_mqttTopic, f);
|
||||
}
|
||||
@@ -141,8 +140,8 @@ void GpioPin::setValue(bool value, gpio_set_source setSource, std::string* error
|
||||
gpio_set_level(_gpio, value);
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
if ((_mqttTopic != "") && (setSource != GPIO_SET_SOURCE_MQTT)) {
|
||||
MQTTPublish(_mqttTopic, value ? "true" : "false");
|
||||
if ((_mqttTopic.compare("") != 0) && (setSource != GPIO_SET_SOURCE_MQTT)) {
|
||||
MQTTPublish(_mqttTopic, value ? "true" : "false", 1);
|
||||
}
|
||||
#endif //ENABLE_MQTT
|
||||
}
|
||||
@@ -153,7 +152,8 @@ void GpioPin::publishState() {
|
||||
if (newState != currentState) {
|
||||
ESP_LOGD(TAG,"publish state of GPIO %d new state %d", _gpio, newState);
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTPublish(_mqttTopic, newState ? "true" : "false");
|
||||
if (_mqttTopic.compare("") != 0)
|
||||
MQTTPublish(_mqttTopic, newState ? "true" : "false", 1);
|
||||
#endif //ENABLE_MQTT
|
||||
currentState = newState;
|
||||
}
|
||||
@@ -330,7 +330,7 @@ bool GpioHandler::readConfig()
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
// std::string mainTopicMQTT = "";
|
||||
std::string mainTopicMQTT = GetMQTTMainTopic();
|
||||
std::string mainTopicMQTT = mqttServer_getMainTopic();
|
||||
if (mainTopicMQTT.length() > 0)
|
||||
{
|
||||
mainTopicMQTT = mainTopicMQTT + "/GPIO";
|
||||
@@ -359,9 +359,9 @@ bool GpioHandler::readConfig()
|
||||
gpio_int_type_t intType = resolveIntType(toLower(splitted[2]));
|
||||
uint16_t dutyResolution = (uint8_t)atoi(splitted[3].c_str());
|
||||
#ifdef ENABLE_MQTT
|
||||
bool mqttEnabled = toLower(splitted[4]) == "true";
|
||||
bool mqttEnabled = (toLower(splitted[4]) == "true");
|
||||
#endif // ENABLE_MQTT
|
||||
bool httpEnabled = toLower(splitted[5]) == "true";
|
||||
bool httpEnabled = (toLower(splitted[5]) == "true");
|
||||
char gpioName[100];
|
||||
if (splitted.size() >= 7) {
|
||||
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}
|
||||
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,99 @@
|
||||
#include "CImageBasis.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
class CCamera {
|
||||
protected:
|
||||
int ActualQuality;
|
||||
framesize_t ActualResolution;
|
||||
int brightness, contrast, saturation;
|
||||
bool isFixedExposure;
|
||||
int waitbeforepicture_org;
|
||||
int led_intensity = 4095;
|
||||
typedef struct
|
||||
{
|
||||
uint16_t CamSensor_id;
|
||||
|
||||
void ledc_init(void);
|
||||
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);
|
||||
long GetFileSize(std::string filename);
|
||||
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)
|
||||
|
||||
public:
|
||||
int image_height, image_width;
|
||||
|
||||
CCamera();
|
||||
esp_err_t InitCam();
|
||||
int ImageDenoiseLevel; // The OV2640 does not support it, OV3660 and OV5640 (0 to 8)
|
||||
|
||||
void LightOnOff(bool status);
|
||||
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);
|
||||
|
||||
int ImageWidth;
|
||||
int ImageHeight;
|
||||
|
||||
framesize_t TextToFramesize(const char * text);
|
||||
int ImageLedIntensity;
|
||||
|
||||
esp_err_t CaptureToFile(std::string nm, int delay = 0);
|
||||
esp_err_t CaptureToBasisImage(CImageBasis *_Image, int delay = 0);
|
||||
bool ImageZoomEnabled;
|
||||
int ImageZoomOffsetX;
|
||||
int ImageZoomOffsetY;
|
||||
int ImageZoomSize;
|
||||
|
||||
int WaitBeforePicture;
|
||||
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 frameSizeX, int frameSizeY, int xOffset, int yOffset, int xTotal, int yTotal, int xOutput, int yOutput, int imageVflip);
|
||||
void SetImageWidthHeightFromResolution(framesize_t resol);
|
||||
void SanitizeZoomParams(int imageSize, int frameSizeX, int frameSizeY, int &imageWidth, int &imageHeight, int &zoomOffsetX, int &zoomOffsetY);
|
||||
|
||||
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);
|
||||
|
||||
int ov5640_set_gainceiling(sensor_t *s, gainceiling_t level);
|
||||
|
||||
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, int imageVflip);
|
||||
void SetZoomSize(bool zoomEnabled, int zoomOffsetX, int zoomOffsetY, int imageSize, int imageVflip);
|
||||
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;
|
||||
|
||||
#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 "ClassControllCamera.h"
|
||||
#include "MainFlowControl.h"
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
#include "esp_log.h"
|
||||
@@ -13,183 +14,187 @@
|
||||
|
||||
static const char *TAG = "server_cam";
|
||||
|
||||
void PowerResetCamera()
|
||||
{
|
||||
#if CAM_PIN_PWDN == GPIO_NUM_NC // Use reset only if pin is available
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "No power down pin availbale to reset camera");
|
||||
#else
|
||||
ESP_LOGD(TAG, "Resetting camera by power down line");
|
||||
gpio_config_t conf;
|
||||
conf.intr_type = GPIO_INTR_DISABLE;
|
||||
conf.pin_bit_mask = 1LL << CAM_PIN_PWDN;
|
||||
conf.mode = GPIO_MODE_OUTPUT;
|
||||
conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||
gpio_config(&conf);
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
// carefull, logic is inverted compared to reset pin
|
||||
gpio_set_level(CAM_PIN_PWDN, 1);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
gpio_set_level(CAM_PIN_PWDN, 0);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
esp_err_t handler_lightOn(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_lightOn - Start");
|
||||
ESP_LOGD(TAG, "handler_lightOn uri: %s", req->uri);
|
||||
#endif
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_lightOn - Start");
|
||||
ESP_LOGD(TAG, "handler_lightOn uri: %s", req->uri);
|
||||
#endif
|
||||
|
||||
if (Camera.getCameraInitSuccessful())
|
||||
if (Camera.getCameraInitSuccessful())
|
||||
{
|
||||
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));
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /lighton not available!");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_lightOn - Done");
|
||||
#endif
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_lightOn - Done");
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t handler_lightOff(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_lightOff - Start");
|
||||
ESP_LOGD(TAG, "handler_lightOff uri: %s", req->uri);
|
||||
#endif
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_lightOff - Start");
|
||||
ESP_LOGD(TAG, "handler_lightOff uri: %s", req->uri);
|
||||
#endif
|
||||
|
||||
if (Camera.getCameraInitSuccessful())
|
||||
if (Camera.getCameraInitSuccessful())
|
||||
{
|
||||
Camera.LightOnOff(false);
|
||||
const char* resp_str = (const char*) req->user_ctx;
|
||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||
const char *resp_str = (const char *)req->user_ctx;
|
||||
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!");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_lightOff - Done");
|
||||
#endif
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_lightOff - Done");
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t handler_capture(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture - Start");
|
||||
#endif
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture - Start");
|
||||
#endif
|
||||
|
||||
if (Camera.getCameraInitSuccessful())
|
||||
if (Camera.getCameraInitSuccessful())
|
||||
{
|
||||
int quality;
|
||||
framesize_t res;
|
||||
// If the camera settings were changed by creating a new reference image, they must be reset
|
||||
if (CFstatus.changedCameraSettings)
|
||||
{
|
||||
Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera
|
||||
Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize, CCstatus.ImageVflip);
|
||||
CFstatus.changedCameraSettings = false;
|
||||
}
|
||||
|
||||
Camera.GetCameraParameter(req, quality, res);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality);
|
||||
#endif
|
||||
|
||||
Camera.SetQualitySize(quality, res);
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Size: %d, Quality: %d", CCstatus.ImageFrameSize, CCstatus.ImageQuality);
|
||||
#endif
|
||||
|
||||
esp_err_t result;
|
||||
result = Camera.CaptureToHTTP(req);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture - Done");
|
||||
#endif
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture - Done");
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /capture not available!");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
esp_err_t handler_capture_with_light(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture_with_light - Start");
|
||||
#endif
|
||||
|
||||
if (Camera.getCameraInitSuccessful())
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture_with_light - Start");
|
||||
#endif
|
||||
|
||||
if (Camera.getCameraInitSuccessful())
|
||||
{
|
||||
char _query[100];
|
||||
char _delay[10];
|
||||
|
||||
int quality;
|
||||
framesize_t res;
|
||||
int delay = 2500;
|
||||
|
||||
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "Query: %s", _query);
|
||||
|
||||
if (httpd_query_key_value(_query, "delay", _delay, 10) == ESP_OK)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Delay: %s", _delay);
|
||||
#endif
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Delay: %s", _delay);
|
||||
#endif
|
||||
delay = atoi(_delay);
|
||||
|
||||
if (delay < 0)
|
||||
{
|
||||
delay = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Camera.GetCameraParameter(req, quality, res);
|
||||
// If the camera settings were changed by creating a new reference image, they must be reset
|
||||
if (CFstatus.changedCameraSettings)
|
||||
{
|
||||
Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera
|
||||
Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize, CCstatus.ImageVflip);
|
||||
CFstatus.changedCameraSettings = false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality);
|
||||
#endif
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Size: %d, Quality: %d", CCstatus.ImageFrameSize, CCstatus.ImageQuality);
|
||||
#endif
|
||||
|
||||
Camera.SetQualitySize(quality, res);
|
||||
Camera.LightOnOff(true);
|
||||
const TickType_t xDelay = delay / portTICK_PERIOD_MS;
|
||||
vTaskDelay( xDelay );
|
||||
vTaskDelay(xDelay);
|
||||
|
||||
esp_err_t result;
|
||||
result = Camera.CaptureToHTTP(req);
|
||||
result = Camera.CaptureToHTTP(req);
|
||||
|
||||
Camera.LightOnOff(false);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture_with_light - Done");
|
||||
#endif
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture_with_light - Done");
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /capture_with_flashlight not available!");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
esp_err_t handler_capture_save_to_file(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture_save_to_file - Start");
|
||||
#endif
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture_save_to_file - Start");
|
||||
#endif
|
||||
|
||||
if (Camera.getCameraInitSuccessful())
|
||||
if (Camera.getCameraInitSuccessful())
|
||||
{
|
||||
char _query[100];
|
||||
char _delay[10];
|
||||
@@ -197,94 +202,102 @@ esp_err_t handler_capture_save_to_file(httpd_req_t *req)
|
||||
char filename[100];
|
||||
std::string fn = "/sdcard/";
|
||||
|
||||
|
||||
int quality;
|
||||
framesize_t res;
|
||||
|
||||
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "Query: %s", _query);
|
||||
|
||||
if (httpd_query_key_value(_query, "filename", filename, 100) == ESP_OK)
|
||||
{
|
||||
fn.append(filename);
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Filename: %s", fn.c_str());
|
||||
#endif
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Filename: %s", fn.c_str());
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
fn.append("noname.jpg");
|
||||
}
|
||||
|
||||
if (httpd_query_key_value(_query, "delay", _delay, 10) == ESP_OK)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Delay: %s", _delay);
|
||||
#endif
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Delay: %s", _delay);
|
||||
#endif
|
||||
delay = atoi(_delay);
|
||||
|
||||
if (delay < 0)
|
||||
{
|
||||
delay = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fn.append("noname.jpg");
|
||||
}
|
||||
|
||||
Camera.GetCameraParameter(req, quality, res);
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality);
|
||||
#endif
|
||||
Camera.SetQualitySize(quality, res);
|
||||
// If the camera settings were changed by creating a new reference image, they must be reset
|
||||
if (CFstatus.changedCameraSettings)
|
||||
{
|
||||
Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera
|
||||
Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize, CCstatus.ImageVflip);
|
||||
CFstatus.changedCameraSettings = false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Size: %d, Quality: %d", CCstatus.ImageFrameSize, CCstatus.ImageQuality);
|
||||
#endif
|
||||
|
||||
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));
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture_save_to_file - Done");
|
||||
#endif
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture_save_to_file - Done");
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /save not available!");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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");
|
||||
#endif
|
||||
|
||||
httpd_uri_t camuri = { };
|
||||
camuri.method = HTTP_GET;
|
||||
httpd_uri_t camuri = {};
|
||||
camuri.method = HTTP_GET;
|
||||
|
||||
camuri.uri = "/lighton";
|
||||
camuri.handler = handler_lightOn;
|
||||
camuri.user_ctx = (void*) "Light On";
|
||||
camuri.uri = "/lighton";
|
||||
camuri.handler = handler_lightOn;
|
||||
camuri.user_ctx = (void *)"Light On";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/lightoff";
|
||||
camuri.handler = handler_lightOff;
|
||||
camuri.user_ctx = (void*) "Light Off";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
camuri.uri = "/lightoff";
|
||||
camuri.handler = handler_lightOff;
|
||||
camuri.user_ctx = (void *)"Light Off";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/capture";
|
||||
camuri.handler = handler_capture;
|
||||
camuri.user_ctx = NULL;
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
camuri.uri = "/capture";
|
||||
camuri.handler = handler_capture;
|
||||
camuri.user_ctx = NULL;
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/capture_with_flashlight";
|
||||
camuri.handler = handler_capture_with_light;
|
||||
camuri.user_ctx = NULL;
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
camuri.uri = "/capture_with_flashlight";
|
||||
camuri.handler = handler_capture_with_light;
|
||||
camuri.user_ctx = NULL;
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/save";
|
||||
camuri.handler = handler_capture_save_to_file;
|
||||
camuri.user_ctx = NULL;
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
camuri.uri = "/save";
|
||||
camuri.handler = handler_capture_save_to_file;
|
||||
camuri.user_ctx = NULL;
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
//#include "ClassControllCamera.h"
|
||||
|
||||
void register_server_camera_uri(httpd_handle_t server);
|
||||
|
||||
void PowerResetCamera();
|
||||
|
||||
#endif
|
||||
@@ -1,7 +1,7 @@
|
||||
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "." "../../include"
|
||||
REQUIRES tflite-lib esp_http_server app_update esp_http_client nvs_flash jomjol_tfliteclass jomjol_flowcontroll spiffs jomjol_helper jomjol_controlGPIO miniz)
|
||||
INCLUDE_DIRS "." "../../include" "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 */
|
||||
|
||||
#ifdef MINIZ_NO_INFLATE_APIS
|
||||
#define MINIZ_NO_ARCHIVE_APIS
|
||||
//#define MINIZ_NO_ARCHIVE_APIS
|
||||
#endif
|
||||
|
||||
#ifdef MINIZ_NO_DEFLATE_APIS
|
||||
@@ -4,5 +4,5 @@ It should be possible to include the repo directly as a submodule, how ever it c
|
||||
- https://github.com/richgel999/miniz/issues/145
|
||||
- https://github.com/espressif/esptool/pull/500#issuecomment-574879468
|
||||
|
||||
For sumplicity we therefore use the release files as suggested in the readme.
|
||||
Additionally we added the CMakeLists.txt and this readme.
|
||||
For simplicity we therefore use the release files as suggested in the readme.
|
||||
Additionally we added the CMakeLists.txt and this readme.
|
||||
@@ -36,7 +36,7 @@ extern "C" {
|
||||
#include "../../include/defines.h"
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
#include "server_tflite.h"
|
||||
#include "MainFlowControl.h"
|
||||
|
||||
#include "server_help.h"
|
||||
#ifdef ENABLE_MQTT
|
||||
@@ -47,7 +47,6 @@ extern "C" {
|
||||
#include "Helper.h"
|
||||
#include "miniz.h"
|
||||
|
||||
|
||||
static const char *TAG = "OTA FILE";
|
||||
|
||||
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)
|
||||
{
|
||||
std::string ret = tfliteflow.getNumbersName();
|
||||
std::string ret = flowctrl.getNumbersName();
|
||||
|
||||
// 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 (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
||||
fclose(fd);
|
||||
ESP_LOGE(TAG, "File sending failed!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!");
|
||||
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);
|
||||
ESP_LOGD(TAG, "Entrypath: %s", entrypath);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -357,7 +356,7 @@ static esp_err_t send_datafile(httpd_req_t *req, bool send_full_file)
|
||||
|
||||
fd = fopen(currentfilename.c_str(), "r");
|
||||
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 */
|
||||
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404());
|
||||
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/ */
|
||||
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;
|
||||
}
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
||||
fclose(fd);
|
||||
ESP_LOGE(TAG, "File sending failed!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!");
|
||||
/* Abort sending file */
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
/* 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");
|
||||
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 */
|
||||
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404());
|
||||
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/ */
|
||||
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;
|
||||
}
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
||||
fclose(fd);
|
||||
ESP_LOGE(TAG, "File sending failed!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!");
|
||||
/* Abort sending file */
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
/* Respond with 500 Internal Server Error */
|
||||
@@ -530,7 +529,7 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
||||
|
||||
|
||||
if (!filename) {
|
||||
ESP_LOGE(TAG, "Filename is too long");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Filename is too long");
|
||||
/* Respond with 414 Error */
|
||||
httpd_resp_send_err(req, HTTPD_414_URI_TOO_LONG, "Filename too long");
|
||||
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 */
|
||||
if (httpd_query_key_value(buf, "readonly", param, sizeof(param)) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Found URL query parameter => readonly=%s", param);
|
||||
readonly = param && strcmp(param,"true")==0;
|
||||
readonly = (strcmp(param,"true") == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -571,7 +570,7 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
||||
|
||||
fd = fopen(filepath, "r");
|
||||
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 */
|
||||
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404());
|
||||
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 */
|
||||
chunksize = fread(chunk, 1, SERVER_FILER_SCRATCH_BUFSIZE, fd);
|
||||
|
||||
/* Send the buffer contents as HTTP response chunk */
|
||||
/* Send buffer contents as HTTP chunk. If empty this functions as a
|
||||
* last-chunk message, signaling end-of-response, to the HTTP client.
|
||||
* See RFC 2616, section 3.6.1 for details on Chunked Transfer Encoding. */
|
||||
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
||||
fclose(fd);
|
||||
ESP_LOGE(TAG, "File sending failed!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!");
|
||||
/* Abort sending file */
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
/* Respond with 500 Internal Server Error */
|
||||
@@ -607,8 +608,6 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
||||
fclose(fd);
|
||||
ESP_LOGD(TAG, "File successfully sent");
|
||||
|
||||
/* Respond with an empty chunk to signal HTTP response completion */
|
||||
httpd_resp_send_chunk(req, NULL, 0);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -634,14 +633,14 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
||||
|
||||
/* Filename cannot have a trailing '/' */
|
||||
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 */
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid filename");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
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 */
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File already exists");
|
||||
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 */
|
||||
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 */
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
|
||||
"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");
|
||||
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 */
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create file");
|
||||
return ESP_FAIL;
|
||||
@@ -692,7 +691,7 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
||||
fclose(fd);
|
||||
unlink(filepath);
|
||||
|
||||
ESP_LOGE(TAG, "File reception failed!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File reception failed!");
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file");
|
||||
return ESP_FAIL;
|
||||
@@ -705,7 +704,7 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
||||
fclose(fd);
|
||||
unlink(filepath);
|
||||
|
||||
ESP_LOGE(TAG, "File write failed!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File write failed!");
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to write file to storage");
|
||||
return ESP_FAIL;
|
||||
@@ -718,7 +717,8 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
||||
|
||||
/* Close file upon upload completion */
|
||||
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);
|
||||
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());
|
||||
|
||||
/* 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());
|
||||
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_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;
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
ESP_LOGD(TAG, "task is found: %s", _valuechar);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "task is found: " + 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 '/' */
|
||||
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 */
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid filename");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
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");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (stat(filepath, &file_stat) == -1) {
|
||||
ESP_LOGE(TAG, "File does not exist: %s", filename);
|
||||
/* Respond with 400 Bad Request */
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File does not exist");
|
||||
return ESP_FAIL;
|
||||
if (stat(filepath, &file_stat) == -1) { // File does not exist
|
||||
/* This is ok, we would delete it anyway */
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "File does not exist: " + string(filename));
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Deleting file: %s", filename);
|
||||
/* Delete file */
|
||||
unlink(filepath);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "File deleted: " + string(filename));
|
||||
ESP_LOGI(TAG, "File deletion completed");
|
||||
|
||||
directory = std::string(filepath);
|
||||
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 = "/fileserver" + directory;
|
||||
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_sendstr(req, "File successfully deleted");
|
||||
return ESP_OK;
|
||||
@@ -887,7 +906,7 @@ void delete_all_in_directory(std::string _directory)
|
||||
std::string filename;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -896,7 +915,7 @@ void delete_all_in_directory(std::string _directory)
|
||||
if (!(entry->d_type == DT_DIR)){
|
||||
if (strcmp("wlan.ini", entry->d_name) != 0){ // auf wlan.ini soll nicht zugegriffen werden !!!
|
||||
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 */
|
||||
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.
|
||||
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;
|
||||
{
|
||||
@@ -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);
|
||||
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);
|
||||
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;
|
||||
|
||||
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
|
||||
DeleteFile(filename_zw);
|
||||
|
||||
FILE* fpTargetFile = fopen(filename_zw.c_str(), "wb");
|
||||
uint writtenbytes = fwrite(p, 1, (uint)uncomp_size, fpTargetFile);
|
||||
fclose(fpTargetFile);
|
||||
@@ -1011,21 +1034,22 @@ std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::st
|
||||
else
|
||||
{
|
||||
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);
|
||||
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);
|
||||
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)
|
||||
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
|
||||
{
|
||||
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";
|
||||
}
|
||||
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);
|
||||
if (!status)
|
||||
{
|
||||
ESP_LOGD(TAG, "mz_zip_reader_init_file() failed!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mz_zip_reader_init_file() failed!");
|
||||
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);
|
||||
if (!status)
|
||||
{
|
||||
ESP_LOGD(TAG, "mz_zip_reader_init_file() failed!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mz_zip_reader_init_file() failed!");
|
||||
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);
|
||||
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);
|
||||
return;
|
||||
}
|
||||
@@ -1093,7 +1117,7 @@ void unzip(std::string _in_zip_file, std::string _target_directory){
|
||||
// Save to File.
|
||||
zw = std::string(archive_filename);
|
||||
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");
|
||||
fwrite(p, 1, (uint)uncomp_size, 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 */
|
||||
if (!base_path) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* Allocate memory for server data */
|
||||
server_data = (file_server_data *) calloc(1, sizeof(struct file_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;
|
||||
}
|
||||
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, ".ico") ||
|
||||
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());
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
#include <string>
|
||||
#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>
|
||||
|
||||
|
||||
@@ -11,14 +14,13 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include <esp_ota_ops.h>
|
||||
#include "esp_http_client.h"
|
||||
#include "esp_flash_partitions.h"
|
||||
#include "esp_partition.h"
|
||||
#include <nvs.h>
|
||||
#include "esp_app_format.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "driver/gpio.h"
|
||||
// #include "protocol_examples_common.h"
|
||||
@@ -26,7 +28,7 @@
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "server_tflite.h"
|
||||
#include "MainFlowControl.h"
|
||||
#include "server_file.h"
|
||||
#include "server_GPIO.h"
|
||||
#ifdef ENABLE_MQTT
|
||||
@@ -39,6 +41,7 @@
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
#include "Helper.h"
|
||||
#include "statusled.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
/*an ota data write buffer ready to write to the flash*/
|
||||
@@ -56,9 +59,9 @@ bool initial_setup = false;
|
||||
static void infinite_loop(void)
|
||||
{
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -66,6 +69,8 @@ static void infinite_loop(void)
|
||||
|
||||
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));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
} else if (filetype == "BIN")
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Do firmware update - file: " + _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();
|
||||
}
|
||||
else
|
||||
@@ -108,7 +113,7 @@ void CheckUpdate()
|
||||
FILE *pfile;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -120,13 +125,13 @@ void CheckUpdate()
|
||||
std::string _szw = std::string(zw);
|
||||
if (_szw == "init")
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Inital Setup triggered.");
|
||||
initial_setup = true; }
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Inital Setup triggered");
|
||||
}
|
||||
}
|
||||
|
||||
fclose(pfile);
|
||||
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);
|
||||
@@ -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 *running = esp_ota_get_running_partition();
|
||||
|
||||
if (configured != running) {
|
||||
ESP_LOGW(TAG, "Configured OTA boot partition at offset 0x%08x, but running from offset 0x%08x",
|
||||
configured->address, running->address);
|
||||
ESP_LOGW(TAG, "(This can happen if either the OTA boot data or preferred boot image become somehow corrupted.)");
|
||||
if (configured != running) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Configured OTA boot partition at offset " + to_string(configured->address) +
|
||||
", but running from offset " + to_string(running->address));
|
||||
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)",
|
||||
running->type, running->subtype, running->address);
|
||||
running->type, running->subtype, (unsigned int)running->address);
|
||||
|
||||
|
||||
update_partition = esp_ota_get_next_update_partition(NULL);
|
||||
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);
|
||||
|
||||
int binary_file_length = 0;
|
||||
@@ -179,7 +184,7 @@ static bool ota_update_task(std::string fn)
|
||||
|
||||
while (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;
|
||||
} else if (data_read > 0) {
|
||||
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
|
||||
if (last_invalid_app != NULL) {
|
||||
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.");
|
||||
ESP_LOGW(TAG, "Previously, there was an attempt to launch the firmware with %s version, but it failed.", invalid_app_info.version);
|
||||
ESP_LOGW(TAG, "The firmware has been rolled back to the previous version.");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "New version is the same as invalid version");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Previously, there was an attempt to launch the firmware with " +
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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();
|
||||
}
|
||||
*/
|
||||
@@ -220,12 +226,12 @@ static bool ota_update_task(std::string fn)
|
||||
|
||||
err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
|
||||
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;
|
||||
}
|
||||
ESP_LOGI(TAG, "esp_ota_begin succeeded");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "received package is not fit len");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "received package is not fit len");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -241,7 +247,7 @@ static bool ota_update_task(std::string fn)
|
||||
// * `errno` to check for underlying transport connectivity closure if any
|
||||
//
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -254,15 +260,15 @@ static bool ota_update_task(std::string fn)
|
||||
err = esp_ota_end(update_handle);
|
||||
if (err != ESP_OK) {
|
||||
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;
|
||||
}
|
||||
|
||||
err = esp_ota_set_boot_partition(update_partition);
|
||||
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!");
|
||||
@@ -329,7 +335,7 @@ void CheckOTAUpdate(void)
|
||||
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution...");
|
||||
esp_ota_mark_app_valid_cancel_rollback();
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
@@ -353,7 +359,7 @@ void CheckOTAUpdate(void)
|
||||
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution...");
|
||||
esp_ota_mark_app_valid_cancel_rollback();
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
@@ -445,7 +451,7 @@ esp_err_t handler_ota_update(httpd_req_t *req)
|
||||
if ((filetype == "ZIP") || (filetype == "BIN"))
|
||||
{
|
||||
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");
|
||||
fwrite(fn.c_str(), fn.length(), 1, pfile);
|
||||
fclose(pfile);
|
||||
@@ -463,7 +469,7 @@ esp_err_t handler_ota_update(httpd_req_t *req)
|
||||
{
|
||||
const char* resp_str;
|
||||
|
||||
KillTFliteTasks();
|
||||
DeleteMainFlowTask();
|
||||
gpio_handler_deinit();
|
||||
if (ota_update_task(fn))
|
||||
{
|
||||
@@ -522,7 +528,7 @@ esp_err_t handler_ota_update(httpd_req_t *req)
|
||||
}
|
||||
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 */
|
||||
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_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;
|
||||
|
||||
KillTFliteTasks();
|
||||
DeleteMainFlowTask();
|
||||
gpio_handler_deinit();
|
||||
if (ota_update_task(fn))
|
||||
{
|
||||
@@ -566,13 +572,19 @@ esp_err_t handler_ota_update(httpd_req_t *req)
|
||||
|
||||
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);
|
||||
while(true);
|
||||
}
|
||||
|
||||
|
||||
void task_reboot(void *KillAutoFlow)
|
||||
void task_reboot(void *DeleteMainFlow)
|
||||
{
|
||||
// write a reboot, to identify a reboot by purpouse
|
||||
FILE* pfile = fopen("/sdcard/reboot.txt", "w");
|
||||
@@ -582,10 +594,13 @@ void task_reboot(void *KillAutoFlow)
|
||||
|
||||
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
||||
|
||||
if ((bool)KillAutoFlow) {
|
||||
KillTFliteTasks(); // Kill autoflow task if executed in extra task, if not don't kill parent task
|
||||
if ((bool)DeleteMainFlow) {
|
||||
DeleteMainFlowTask(); // Kill autoflow task if executed in extra task, if not don't kill parent task
|
||||
}
|
||||
|
||||
Camera.LightOnOff(false);
|
||||
StatusLEDOff();
|
||||
|
||||
/* Stop service tasks */
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTdestroy_client(true);
|
||||
@@ -600,20 +615,20 @@ void task_reboot(void *KillAutoFlow)
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
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");
|
||||
|
||||
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 )
|
||||
{
|
||||
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);
|
||||
}
|
||||
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");
|
||||
|
||||
Camera.LightOnOff(false);
|
||||
StatusLEDOff();
|
||||
esp_camera_deinit();
|
||||
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
@@ -641,7 +658,7 @@ esp_err_t handler_reboot(httpd_req_t *req)
|
||||
#endif
|
||||
|
||||
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 =
|
||||
"<html><head><script>"
|
||||
|
||||
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
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_webhook jomjol_fileserver_ota jomjol_image_proc jomjol_wlan openmetrics)
|
||||
|
||||
|
||||
|
||||
@@ -67,11 +67,6 @@ string ClassFlow::getHTMLSingleStep(string host){
|
||||
return "";
|
||||
}
|
||||
|
||||
string ClassFlow::getReadout()
|
||||
{
|
||||
return string();
|
||||
}
|
||||
|
||||
std::string ClassFlow::GetParameterName(std::string _input)
|
||||
{
|
||||
string _param;
|
||||
|
||||
@@ -46,7 +46,6 @@ public:
|
||||
virtual bool ReadParameter(FILE* pfile, string &aktparamgraph);
|
||||
virtual bool doFlow(string time);
|
||||
virtual string getHTMLSingleStep(string host);
|
||||
virtual string getReadout();
|
||||
virtual string name(){return "ClassFlow";};
|
||||
|
||||
};
|
||||
|
||||
@@ -1,376 +1,374 @@
|
||||
#include "ClassFlowAlignment.h"
|
||||
#include "ClassFlowTakeImage.h"
|
||||
#include "ClassFlow.h"
|
||||
#include "server_tflite.h"
|
||||
|
||||
#include "CRotateImage.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
|
||||
static const char *TAG = "ALIGN";
|
||||
|
||||
// #define DEBUG_DETAIL_ON
|
||||
|
||||
|
||||
void ClassFlowAlignment::SetInitialParameter(void)
|
||||
{
|
||||
initalrotate = 0;
|
||||
anz_ref = 0;
|
||||
initialmirror = false;
|
||||
use_antialiasing = false;
|
||||
initialflip = false;
|
||||
SaveAllFiles = false;
|
||||
namerawimage = "/sdcard/img_tmp/raw.jpg";
|
||||
FileStoreRefAlignment = "/sdcard/config/align.txt";
|
||||
ListFlowControll = NULL;
|
||||
AlignAndCutImage = NULL;
|
||||
ImageBasis = NULL;
|
||||
ImageTMP = NULL;
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
AlgROI = (ImageData*)heap_caps_malloc(sizeof(ImageData), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
||||
#endif
|
||||
previousElement = NULL;
|
||||
disabled = false;
|
||||
SAD_criteria = 0.05;
|
||||
}
|
||||
|
||||
|
||||
ClassFlowAlignment::ClassFlowAlignment(std::vector<ClassFlow*>* lfc)
|
||||
{
|
||||
SetInitialParameter();
|
||||
ListFlowControll = lfc;
|
||||
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowTakeImage") == 0)
|
||||
{
|
||||
ImageBasis = ((ClassFlowTakeImage*) (*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(namerawimage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowAlignment::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
std::vector<string> splitted;
|
||||
int suchex = 40;
|
||||
int suchey = 40;
|
||||
int alg_algo = 0;
|
||||
|
||||
|
||||
aktparamgraph = trim(aktparamgraph);
|
||||
|
||||
if (aktparamgraph.size() == 0)
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||
return false;
|
||||
|
||||
if (aktparamgraph.compare("[Alignment]") != 0) //Paragraph does not fit Alignment
|
||||
return false;
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
splitted = ZerlegeZeile(aktparamgraph);
|
||||
if ((toUpper(splitted[0]) == "FLIPIMAGESIZE") && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "TRUE")
|
||||
initialflip = true;
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "INITIALMIRROR") && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "TRUE")
|
||||
initialmirror = true;
|
||||
}
|
||||
if (((toUpper(splitted[0]) == "INITALROTATE") || (toUpper(splitted[0]) == "INITIALROTATE")) && (splitted.size() > 1))
|
||||
{
|
||||
this->initalrotate = std::stod(splitted[1]);
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "SEARCHFIELDX") && (splitted.size() > 1))
|
||||
{
|
||||
suchex = std::stod(splitted[1]);
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "SEARCHFIELDY") && (splitted.size() > 1))
|
||||
{
|
||||
suchey = std::stod(splitted[1]);
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "ANTIALIASING") && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "TRUE")
|
||||
use_antialiasing = true;
|
||||
}
|
||||
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].target_y = std::stod(splitted[2]);
|
||||
anz_ref++;
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "SAVEALLFILES") && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "TRUE")
|
||||
SaveAllFiles = true;
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "ALIGNMENTALGO") && (splitted.size() > 1))
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
std::string zw2 = "Alignment mode selected: " + splitted[1];
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2);
|
||||
#endif
|
||||
if (toUpper(splitted[1]) == "HIGHACCURACY")
|
||||
alg_algo = 1;
|
||||
if (toUpper(splitted[1]) == "FAST")
|
||||
alg_algo = 2;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < anz_ref; ++i)
|
||||
{
|
||||
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
|
||||
std::string zw2 = "Alignment mode written: " + std::to_string(alg_algo);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2);
|
||||
#endif
|
||||
}
|
||||
|
||||
LoadReferenceAlignmentValues();
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowAlignment::getHTMLSingleStep(string host)
|
||||
{
|
||||
string result;
|
||||
|
||||
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";
|
||||
result = result + "<p>Aligned Image: </p> <p><img src=\"" + host + "/img_tmp/alg.jpg\"></p>\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowAlignment::doFlow(string time)
|
||||
{
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
if (!AlgROI) // AlgROI needs to be allocated before ImageTMP to avoid heap fragmentation
|
||||
{
|
||||
AlgROI = (ImageData*)heap_caps_realloc(AlgROI, sizeof(ImageData), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
||||
if (!AlgROI)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate AlgROI");
|
||||
LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow");
|
||||
}
|
||||
}
|
||||
|
||||
if (AlgROI)
|
||||
{
|
||||
ImageBasis->writeToMemoryAsJPG((ImageData*)AlgROI, 90);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!ImageTMP)
|
||||
{
|
||||
ImageTMP = new CImageBasis(ImageBasis);
|
||||
if (!ImageTMP)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate ImageTMP -> Exec this round aborted!");
|
||||
LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
delete AlignAndCutImage;
|
||||
AlignAndCutImage = new CAlignAndCutImage(ImageBasis, ImageTMP);
|
||||
if (!AlignAndCutImage)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate AlignAndCutImage -> Exec this round aborted!");
|
||||
LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow");
|
||||
return false;
|
||||
}
|
||||
|
||||
CRotateImage rt(AlignAndCutImage, ImageTMP, initialflip);
|
||||
if (initialflip)
|
||||
{
|
||||
int _zw = ImageBasis->height;
|
||||
ImageBasis->height = ImageBasis->width;
|
||||
ImageBasis->width = _zw;
|
||||
|
||||
_zw = ImageTMP->width;
|
||||
ImageTMP->width = ImageTMP->height;
|
||||
ImageTMP->height = _zw;
|
||||
}
|
||||
|
||||
if (initialmirror)
|
||||
{
|
||||
ESP_LOGD(TAG, "do mirror");
|
||||
rt.Mirror();
|
||||
|
||||
if (SaveAllFiles)
|
||||
AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/mirror.jpg"));
|
||||
}
|
||||
|
||||
if ((initalrotate != 0) || initialflip)
|
||||
{
|
||||
if (use_antialiasing)
|
||||
rt.RotateAntiAliasing(initalrotate);
|
||||
else
|
||||
rt.Rotate(initalrotate);
|
||||
|
||||
if (SaveAllFiles)
|
||||
AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/rot.jpg"));
|
||||
}
|
||||
|
||||
if (!AlignAndCutImage->Align(&References[0], &References[1]))
|
||||
{
|
||||
SaveReferenceAlignmentValues();
|
||||
}
|
||||
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
if (AlgROI) {
|
||||
DrawRef(ImageTMP);
|
||||
tfliteflow.DigitalDrawROI(ImageTMP);
|
||||
tfliteflow.AnalogDrawROI(ImageTMP);
|
||||
ImageTMP->writeToMemoryAsJPG((ImageData*)AlgROI, 90);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (SaveAllFiles)
|
||||
{
|
||||
AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/alg.jpg"));
|
||||
ImageTMP->SaveToFile(FormatFileName("/sdcard/img_tmp/alg_roi.jpg"));
|
||||
}
|
||||
|
||||
// must be deleted to have memory space for loading tflite
|
||||
delete ImageTMP;
|
||||
ImageTMP = NULL;
|
||||
|
||||
LoadReferenceAlignmentValues();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowAlignment::SaveReferenceAlignmentValues()
|
||||
{
|
||||
FILE* pFile;
|
||||
std::string zwtime, zwvalue;
|
||||
|
||||
pFile = fopen(FileStoreRefAlignment.c_str(), "w");
|
||||
|
||||
if (strlen(zwtime.c_str()) == 0)
|
||||
{
|
||||
time_t rawtime;
|
||||
struct tm* timeinfo;
|
||||
char buffer[80];
|
||||
|
||||
time(&rawtime);
|
||||
timeinfo = localtime(&rawtime);
|
||||
|
||||
strftime(buffer, 80, "%Y-%m-%dT%H:%M:%S", timeinfo);
|
||||
zwtime = std::string(buffer);
|
||||
}
|
||||
|
||||
fputs(zwtime.c_str(), pFile);
|
||||
fputs("\n", pFile);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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);
|
||||
zwvalue = zwvalue + "\t" +std::to_string(References[1].fastalg_max)+ "\t" +std::to_string(References[1].fastalg_avg);
|
||||
fputs(zwvalue.c_str(), pFile);
|
||||
fputs("\n", pFile);
|
||||
|
||||
fclose(pFile);
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowAlignment::LoadReferenceAlignmentValues(void)
|
||||
{
|
||||
FILE* pFile;
|
||||
char zw[1024];
|
||||
string zwvalue;
|
||||
std::vector<string> splitted;
|
||||
|
||||
|
||||
pFile = fopen(FileStoreRefAlignment.c_str(), "r");
|
||||
if (pFile == NULL)
|
||||
return false;
|
||||
|
||||
fgets(zw, 1024, pFile);
|
||||
ESP_LOGD(TAG, "%s", zw);
|
||||
|
||||
fgets(zw, 1024, pFile);
|
||||
splitted = ZerlegeZeile(std::string(zw), " \t");
|
||||
if (splitted.size() < 6)
|
||||
{
|
||||
fclose(pFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
References[0].fastalg_x = stoi(splitted[0]);
|
||||
References[0].fastalg_y = stoi(splitted[1]);
|
||||
References[0].fastalg_SAD = stof(splitted[2]);
|
||||
References[0].fastalg_min = stoi(splitted[3]);
|
||||
References[0].fastalg_max = stoi(splitted[4]);
|
||||
References[0].fastalg_avg = stof(splitted[5]);
|
||||
|
||||
fgets(zw, 1024, pFile);
|
||||
splitted = ZerlegeZeile(std::string(zw));
|
||||
if (splitted.size() < 6)
|
||||
{
|
||||
fclose(pFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
References[1].fastalg_x = stoi(splitted[0]);
|
||||
References[1].fastalg_y = stoi(splitted[1]);
|
||||
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);
|
||||
}
|
||||
}
|
||||
#include "ClassFlowAlignment.h"
|
||||
#include "ClassFlowTakeImage.h"
|
||||
#include "ClassFlow.h"
|
||||
#include "MainFlowControl.h"
|
||||
|
||||
#include "CRotateImage.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
#include "psram.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char *TAG = "ALIGN";
|
||||
|
||||
// #define DEBUG_DETAIL_ON
|
||||
|
||||
void ClassFlowAlignment::SetInitialParameter(void)
|
||||
{
|
||||
initialrotate = 0;
|
||||
anz_ref = 0;
|
||||
use_antialiasing = false;
|
||||
initialflip = false;
|
||||
SaveAllFiles = false;
|
||||
namerawimage = "/sdcard/img_tmp/raw.jpg";
|
||||
FileStoreRefAlignment = "/sdcard/config/align.txt";
|
||||
ListFlowControll = NULL;
|
||||
AlignAndCutImage = NULL;
|
||||
ImageBasis = NULL;
|
||||
ImageTMP = NULL;
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
AlgROI = (ImageData *)malloc_psram_heap(std::string(TAG) + "->AlgROI", sizeof(ImageData), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
||||
#endif
|
||||
previousElement = NULL;
|
||||
disabled = false;
|
||||
SAD_criteria = 0.05;
|
||||
}
|
||||
|
||||
ClassFlowAlignment::ClassFlowAlignment(std::vector<ClassFlow *> *lfc)
|
||||
{
|
||||
SetInitialParameter();
|
||||
ListFlowControll = lfc;
|
||||
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i) {
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowTakeImage") == 0) {
|
||||
ImageBasis = ((ClassFlowTakeImage *)(*ListFlowControll)[i])->rawImage;
|
||||
}
|
||||
}
|
||||
|
||||
// the function take pictures does not exist --> must be created first ONLY FOR TEST PURPOSES
|
||||
if (!ImageBasis) {
|
||||
ESP_LOGD(TAG, "CImageBasis had to be created");
|
||||
ImageBasis = new CImageBasis("ImageBasis", namerawimage);
|
||||
}
|
||||
}
|
||||
|
||||
bool ClassFlowAlignment::ReadParameter(FILE *pfile, string &aktparamgraph)
|
||||
{
|
||||
std::vector<string> splitted;
|
||||
int suchex = 40;
|
||||
int suchey = 40;
|
||||
int alg_algo = 0; // default=0; 1 =HIGHACCURACY; 2= FAST; 3= OFF //add disable aligment algo |01.2023
|
||||
|
||||
aktparamgraph = trim(aktparamgraph);
|
||||
|
||||
if (aktparamgraph.size() == 0)
|
||||
{
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (aktparamgraph.compare("[Alignment]") != 0)
|
||||
{
|
||||
// Paragraph does not fit Alignment
|
||||
return false;
|
||||
}
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
splitted = ZerlegeZeile(aktparamgraph);
|
||||
|
||||
if ((toUpper(splitted[0]) == "FLIPIMAGESIZE") && (splitted.size() > 1)) {
|
||||
initialflip = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
else if (((toUpper(splitted[0]) == "initialrotate") || (toUpper(splitted[0]) == "INITIALROTATE")) && (splitted.size() > 1)) {
|
||||
if (isStringNumeric(splitted[1])) {
|
||||
this->initialrotate = std::stod(splitted[1]);
|
||||
}
|
||||
}
|
||||
else if ((toUpper(splitted[0]) == "SEARCHFIELDX") && (splitted.size() > 1)) {
|
||||
if (isStringNumeric(splitted[1])) {
|
||||
suchex = std::stod(splitted[1]);
|
||||
}
|
||||
}
|
||||
else if ((toUpper(splitted[0]) == "SEARCHFIELDY") && (splitted.size() > 1)) {
|
||||
if (isStringNumeric(splitted[1])) {
|
||||
suchey = std::stod(splitted[1]);
|
||||
}
|
||||
}
|
||||
else if ((toUpper(splitted[0]) == "ANTIALIASING") && (splitted.size() > 1)) {
|
||||
use_antialiasing = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
else if ((splitted.size() == 3) && (anz_ref < 2)) {
|
||||
if ((isStringNumeric(splitted[1])) && (isStringNumeric(splitted[2])))
|
||||
{
|
||||
References[anz_ref].image_file = FormatFileName("/sdcard" + splitted[0]);
|
||||
References[anz_ref].target_x = std::stod(splitted[1]);
|
||||
References[anz_ref].target_y = std::stod(splitted[2]);
|
||||
anz_ref++;
|
||||
}
|
||||
else
|
||||
{
|
||||
References[anz_ref].image_file = FormatFileName("/sdcard" + splitted[0]);
|
||||
References[anz_ref].target_x = 10;
|
||||
References[anz_ref].target_y = 10;
|
||||
anz_ref++;
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "SAVEALLFILES") && (splitted.size() > 1)) {
|
||||
SaveAllFiles = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
else if ((toUpper(splitted[0]) == "ALIGNMENTALGO") && (splitted.size() > 1)) {
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
std::string zw2 = "Alignment mode selected: " + splitted[1];
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2);
|
||||
#endif
|
||||
if (toUpper(splitted[1]) == "HIGHACCURACY") {
|
||||
alg_algo = 1;
|
||||
}
|
||||
if (toUpper(splitted[1]) == "FAST") {
|
||||
alg_algo = 2;
|
||||
}
|
||||
if (toUpper(splitted[1]) == "OFF") {
|
||||
// no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
|
||||
alg_algo = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < anz_ref; ++i) {
|
||||
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
|
||||
std::string zw2 = "Alignment mode written: " + std::to_string(alg_algo);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2);
|
||||
#endif
|
||||
}
|
||||
|
||||
// no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
|
||||
if (References[0].alignment_algo != 3) {
|
||||
return LoadReferenceAlignmentValues();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
string ClassFlowAlignment::getHTMLSingleStep(string host)
|
||||
{
|
||||
string result;
|
||||
|
||||
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";
|
||||
result = result + "<p>Aligned Image: </p> <p><img src=\"" + host + "/img_tmp/alg.jpg\"></p>\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ClassFlowAlignment::doFlow(string time)
|
||||
{
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
// AlgROI needs to be allocated before ImageTMP to avoid heap fragmentation
|
||||
if (!AlgROI) {
|
||||
AlgROI = (ImageData *)heap_caps_realloc(AlgROI, sizeof(ImageData), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
||||
|
||||
if (!AlgROI) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate AlgROI");
|
||||
LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow");
|
||||
}
|
||||
}
|
||||
|
||||
if (AlgROI) {
|
||||
ImageBasis->writeToMemoryAsJPG((ImageData *)AlgROI, 90);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!ImageTMP) {
|
||||
ImageTMP = new CImageBasis("tmpImage", ImageBasis); // Make sure the name does not get change, it is relevant for the PSRAM allocation!
|
||||
|
||||
if (!ImageTMP) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate tmpImage -> Exec this round aborted!");
|
||||
LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
delete AlignAndCutImage;
|
||||
AlignAndCutImage = new CAlignAndCutImage("AlignAndCutImage", ImageBasis, ImageTMP);
|
||||
|
||||
if (!AlignAndCutImage) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate AlignAndCutImage -> Exec this round aborted!");
|
||||
LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow");
|
||||
return false;
|
||||
}
|
||||
|
||||
CRotateImage rt("rawImage", AlignAndCutImage, ImageTMP, initialflip);
|
||||
|
||||
if (initialflip) {
|
||||
int _zw = ImageBasis->height;
|
||||
ImageBasis->height = ImageBasis->width;
|
||||
ImageBasis->width = _zw;
|
||||
|
||||
_zw = ImageTMP->width;
|
||||
ImageTMP->width = ImageTMP->height;
|
||||
ImageTMP->height = _zw;
|
||||
}
|
||||
|
||||
if ((initialrotate != 0) || initialflip) {
|
||||
if (use_antialiasing) {
|
||||
rt.RotateAntiAliasing(initialrotate);
|
||||
}
|
||||
else {
|
||||
rt.Rotate(initialrotate);
|
||||
}
|
||||
|
||||
if (SaveAllFiles) {
|
||||
AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/rot.jpg"));
|
||||
}
|
||||
}
|
||||
|
||||
// no align algo if set to 3 = off //add disable aligment algo |01.2023
|
||||
if (References[0].alignment_algo != 3) {
|
||||
if (!AlignAndCutImage->Align(&References[0], &References[1])) {
|
||||
SaveReferenceAlignmentValues();
|
||||
}
|
||||
} // no align
|
||||
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
if (AlgROI) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
flowctrl.DigitDrawROI(ImageTMP);
|
||||
flowctrl.AnalogDrawROI(ImageTMP);
|
||||
ImageTMP->writeToMemoryAsJPG((ImageData *)AlgROI, 90);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (SaveAllFiles) {
|
||||
AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/alg.jpg"));
|
||||
ImageTMP->SaveToFile(FormatFileName("/sdcard/img_tmp/alg_roi.jpg"));
|
||||
}
|
||||
|
||||
// must be deleted to have memory space for loading tflite
|
||||
delete ImageTMP;
|
||||
ImageTMP = NULL;
|
||||
|
||||
// no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
|
||||
if (References[0].alignment_algo != 3) {
|
||||
return LoadReferenceAlignmentValues();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClassFlowAlignment::SaveReferenceAlignmentValues()
|
||||
{
|
||||
FILE *pFile;
|
||||
std::string zwtime, zwvalue;
|
||||
|
||||
pFile = fopen(FileStoreRefAlignment.c_str(), "w");
|
||||
|
||||
if (strlen(zwtime.c_str()) == 0) {
|
||||
time_t rawtime;
|
||||
struct tm *timeinfo;
|
||||
char buffer[80];
|
||||
|
||||
time(&rawtime);
|
||||
timeinfo = localtime(&rawtime);
|
||||
|
||||
strftime(buffer, 80, "%Y-%m-%dT%H:%M:%S", timeinfo);
|
||||
zwtime = std::string(buffer);
|
||||
}
|
||||
|
||||
fputs(zwtime.c_str(), pFile);
|
||||
fputs("\n", pFile);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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);
|
||||
zwvalue = zwvalue + "\t" + std::to_string(References[1].fastalg_max) + "\t" + std::to_string(References[1].fastalg_avg);
|
||||
fputs(zwvalue.c_str(), pFile);
|
||||
fputs("\n", pFile);
|
||||
|
||||
fclose(pFile);
|
||||
}
|
||||
|
||||
bool ClassFlowAlignment::LoadReferenceAlignmentValues(void)
|
||||
{
|
||||
FILE *pFile;
|
||||
char zw[1024];
|
||||
string zwvalue;
|
||||
std::vector<string> splitted;
|
||||
|
||||
pFile = fopen(FileStoreRefAlignment.c_str(), "r");
|
||||
|
||||
if (pFile == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fgets(zw, 1024, pFile);
|
||||
ESP_LOGD(TAG, "%s", zw);
|
||||
|
||||
fgets(zw, 1024, pFile);
|
||||
splitted = ZerlegeZeile(std::string(zw), " \t");
|
||||
|
||||
if (splitted.size() < 6) {
|
||||
fclose(pFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
References[0].fastalg_x = stoi(splitted[0]);
|
||||
References[0].fastalg_y = stoi(splitted[1]);
|
||||
References[0].fastalg_SAD = stof(splitted[2]);
|
||||
References[0].fastalg_min = stoi(splitted[3]);
|
||||
References[0].fastalg_max = stoi(splitted[4]);
|
||||
References[0].fastalg_avg = stof(splitted[5]);
|
||||
|
||||
fgets(zw, 1024, pFile);
|
||||
splitted = ZerlegeZeile(std::string(zw));
|
||||
|
||||
if (splitted.size() < 6) {
|
||||
fclose(pFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
References[1].fastalg_x = stoi(splitted[0]);
|
||||
References[1].fastalg_y = stoi(splitted[1]);
|
||||
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
|
||||
|
||||
#ifndef CLASSFLOWALIGNMENT_H
|
||||
#define CLASSFLOWALIGNMENT_H
|
||||
|
||||
#include "ClassFlow.h"
|
||||
#include "Helper.h"
|
||||
#include "CAlignAndCutImage.h"
|
||||
#include "CFindTemplate.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class ClassFlowAlignment :
|
||||
public ClassFlow
|
||||
{
|
||||
protected:
|
||||
float initalrotate;
|
||||
bool initialmirror;
|
||||
bool initialflip;
|
||||
bool use_antialiasing;
|
||||
RefInfo References[2];
|
||||
int anz_ref;
|
||||
string namerawimage;
|
||||
bool SaveAllFiles;
|
||||
CAlignAndCutImage *AlignAndCutImage;
|
||||
std::string FileStoreRefAlignment;
|
||||
float SAD_criteria;
|
||||
|
||||
void SetInitialParameter(void);
|
||||
bool LoadReferenceAlignmentValues(void);
|
||||
void SaveReferenceAlignmentValues();
|
||||
|
||||
public:
|
||||
CImageBasis *ImageBasis, *ImageTMP;
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
ImageData *AlgROI;
|
||||
#endif
|
||||
|
||||
ClassFlowAlignment(std::vector<ClassFlow*>* lfc);
|
||||
|
||||
CAlignAndCutImage* GetAlignAndCutImage(){return AlignAndCutImage;};
|
||||
|
||||
void DrawRef(CImageBasis *_zw);
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string getHTMLSingleStep(string host);
|
||||
string name(){return "ClassFlowAlignment";};
|
||||
};
|
||||
|
||||
|
||||
#endif //CLASSFLOWALIGNMENT_H
|
||||
#pragma once
|
||||
|
||||
#ifndef CLASSFLOWALIGNMENT_H
|
||||
#define CLASSFLOWALIGNMENT_H
|
||||
|
||||
#include "ClassFlow.h"
|
||||
#include "Helper.h"
|
||||
#include "CAlignAndCutImage.h"
|
||||
#include "CFindTemplate.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class ClassFlowAlignment : public ClassFlow
|
||||
{
|
||||
protected:
|
||||
float initialrotate;
|
||||
bool initialflip;
|
||||
bool use_antialiasing;
|
||||
RefInfo References[2];
|
||||
int anz_ref;
|
||||
string namerawimage;
|
||||
bool SaveAllFiles;
|
||||
CAlignAndCutImage *AlignAndCutImage;
|
||||
std::string FileStoreRefAlignment;
|
||||
float SAD_criteria;
|
||||
|
||||
void SetInitialParameter(void);
|
||||
bool LoadReferenceAlignmentValues(void);
|
||||
void SaveReferenceAlignmentValues();
|
||||
|
||||
public:
|
||||
CImageBasis *ImageBasis, *ImageTMP;
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
ImageData *AlgROI;
|
||||
#endif
|
||||
|
||||
ClassFlowAlignment(std::vector<ClassFlow *> *lfc);
|
||||
|
||||
CAlignAndCutImage *GetAlignAndCutImage() { return AlignAndCutImage; };
|
||||
|
||||
void DrawRef(CImageBasis *_zw);
|
||||
|
||||
bool ReadParameter(FILE *pfile, string &aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string getHTMLSingleStep(string host);
|
||||
string name() { return "ClassFlowAlignment"; };
|
||||
};
|
||||
|
||||
#endif // CLASSFLOWALIGNMENT_H
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,10 +11,10 @@ enum t_CNNType {
|
||||
AutoDetect,
|
||||
Analogue,
|
||||
Analogue100,
|
||||
Digital,
|
||||
DigitalHyprid10,
|
||||
Digit,
|
||||
DigitHyprid10,
|
||||
DoubleHyprid10,
|
||||
Digital100,
|
||||
Digit100,
|
||||
None
|
||||
};
|
||||
|
||||
@@ -26,15 +26,6 @@ protected:
|
||||
std::vector<general*> GENERAL;
|
||||
float CNNGoodThreshold;
|
||||
|
||||
//moved to define.h
|
||||
//float Analog_error = 3.0;
|
||||
//float AnalogToDigtalFehler = 0.8;
|
||||
//float Digital_Uncertainty = 0.2;
|
||||
//int DigitalBand = 3;
|
||||
//float Digital_Transition_Range_Predecessor = 2;
|
||||
//float Digital_Transition_Area_Predecessor = 0.7; // 9.3 - 0.7
|
||||
//float Digital_Transition_Area_Forward = 9.7; // Pre-run zero crossing only happens from approx. 9.7 onwards
|
||||
|
||||
string cnnmodelfile;
|
||||
int modelxsize, modelysize, modelchannel;
|
||||
bool isLogImageSelect;
|
||||
@@ -44,8 +35,8 @@ protected:
|
||||
bool SaveAllFiles;
|
||||
|
||||
int PointerEvalAnalogNew(float zahl, int numeral_preceder);
|
||||
int PointerEvalAnalogToDigitNew(float zahl, float numeral_preceder, int eval_predecessors, float analogDigitalTransitionStart);
|
||||
int PointerEvalHybridNew(float zahl, float number_of_predecessors, int eval_predecessors, bool Analog_Predecessors = false, float analogDigitalTransitionStart=9.2);
|
||||
int PointerEvalAnalogToDigitNew(float zahl, float numeral_preceder, int eval_predecessors, float AnalogToDigitTransitionStart);
|
||||
int PointerEvalHybridNew(float zahl, float number_of_predecessors, int eval_predecessors, bool Analog_Predecessors = false, float AnalogToDigitTransitionStart=9.2);
|
||||
|
||||
|
||||
|
||||
@@ -61,7 +52,7 @@ public:
|
||||
bool doFlow(string time);
|
||||
|
||||
string getHTMLSingleStep(string host);
|
||||
string getReadout(int _analog, bool _extendedResolution = false, int prev = -1, float _before_narrow_Analog = -1, float analogDigitalTransitionStart=9.2);
|
||||
string getReadout(int _analog, bool _extendedResolution = false, int prev = -1, float _before_narrow_Analog = -1, float AnalogToDigitTransitionStart=9.2);
|
||||
|
||||
string getReadoutRawString(int _analog);
|
||||
|
||||
|
||||
@@ -25,13 +25,13 @@ extern "C" {
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
#include "server_help.h"
|
||||
#include "MainFlowControl.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char* TAG = "CTRL";
|
||||
static const char* TAG = "FLOWCTRL";
|
||||
|
||||
//#define DEBUG_DETAIL_ON
|
||||
|
||||
|
||||
std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _host){
|
||||
std::string _classname = "";
|
||||
std::string result = "";
|
||||
@@ -60,12 +60,23 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
|
||||
if ((_stepname.compare("[InfluxDB]") == 0) || (_stepname.compare(";[InfluxDB]") == 0)){
|
||||
_classname = "ClassFlowInfluxDB";
|
||||
}
|
||||
if ((_stepname.compare("[InfluxDBv2]") == 0) || (_stepname.compare(";[InfluxDBv2]") == 0)){
|
||||
_classname = "ClassFlowInfluxDBv2";
|
||||
}
|
||||
#endif //ENABLE_INFLUXDB
|
||||
#ifdef ENABLE_WEBHOOK
|
||||
if ((_stepname.compare("[Webhook]") == 0) || (_stepname.compare(";[Webhook]") == 0)){
|
||||
_classname = "ClassFlowWebhook";
|
||||
}
|
||||
#endif //ENABLE_WEBHOOK
|
||||
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
if (FlowControll[i]->name().compare(_classname) == 0){
|
||||
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.
|
||||
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("");
|
||||
}
|
||||
|
||||
result = FlowControll[i]->getHTMLSingleStep(_host);
|
||||
}
|
||||
|
||||
@@ -74,37 +85,51 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
std::string ClassFlowControll::TranslateAktstatus(std::string _input)
|
||||
{
|
||||
if (_input.compare("ClassFlowTakeImage") == 0)
|
||||
if (_input.compare("ClassFlowTakeImage") == 0) {
|
||||
return ("Take Image");
|
||||
if (_input.compare("ClassFlowAlignment") == 0)
|
||||
}
|
||||
|
||||
if (_input.compare("ClassFlowAlignment") == 0) {
|
||||
return ("Aligning");
|
||||
if (_input.compare("ClassFlowCNNGeneral") == 0)
|
||||
return ("Digitalization of ROIs");
|
||||
}
|
||||
|
||||
if (_input.compare("ClassFlowCNNGeneral") == 0) {
|
||||
return ("Digitization of ROIs");
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
if (_input.compare("ClassFlowMQTT") == 0)
|
||||
if (_input.compare("ClassFlowMQTT") == 0) {
|
||||
return ("Sending MQTT");
|
||||
}
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
if (_input.compare("ClassFlowInfluxDB") == 0)
|
||||
if (_input.compare("ClassFlowInfluxDB") == 0) {
|
||||
return ("Sending InfluxDB");
|
||||
}
|
||||
|
||||
if (_input.compare("ClassFlowInfluxDBv2") == 0) {
|
||||
return ("Sending InfluxDBv2");
|
||||
}
|
||||
#endif //ENABLE_INFLUXDB
|
||||
if (_input.compare("ClassFlowPostProcessing") == 0)
|
||||
#ifdef ENABLE_WEBHOOK
|
||||
if (_input.compare("ClassFlowWebhook") == 0) {
|
||||
return ("Sending Webhook");
|
||||
}
|
||||
#endif //ENABLE_WEBHOOK
|
||||
if (_input.compare("ClassFlowPostProcessing") == 0) {
|
||||
return ("Post-Processing");
|
||||
if (_input.compare("ClassFlowWriteList") == 0)
|
||||
return ("Writing List");
|
||||
}
|
||||
|
||||
return "Unkown Status";
|
||||
}
|
||||
|
||||
|
||||
std::vector<HTMLInfo*> ClassFlowControll::GetAllDigital()
|
||||
std::vector<HTMLInfo*> ClassFlowControll::GetAllDigit()
|
||||
{
|
||||
if (flowdigit)
|
||||
{
|
||||
ESP_LOGD(TAG, "ClassFlowControll::GetAllDigital - flowdigit != NULL");
|
||||
if (flowdigit) {
|
||||
ESP_LOGD(TAG, "ClassFlowControll::GetAllDigit - flowdigit != NULL");
|
||||
return flowdigit->GetHTMLInfo();
|
||||
}
|
||||
|
||||
@@ -112,73 +137,63 @@ std::vector<HTMLInfo*> ClassFlowControll::GetAllDigital()
|
||||
return empty;
|
||||
}
|
||||
|
||||
|
||||
std::vector<HTMLInfo*> ClassFlowControll::GetAllAnalog()
|
||||
{
|
||||
if (flowanalog)
|
||||
if (flowanalog) {
|
||||
return flowanalog->GetHTMLInfo();
|
||||
}
|
||||
|
||||
std::vector<HTMLInfo*> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
|
||||
t_CNNType ClassFlowControll::GetTypeDigital()
|
||||
t_CNNType ClassFlowControll::GetTypeDigit()
|
||||
{
|
||||
if (flowdigit)
|
||||
if (flowdigit) {
|
||||
return flowdigit->getCNNType();
|
||||
}
|
||||
|
||||
return t_CNNType::None;
|
||||
}
|
||||
|
||||
|
||||
t_CNNType ClassFlowControll::GetTypeAnalog()
|
||||
{
|
||||
if (flowanalog)
|
||||
if (flowanalog) {
|
||||
return flowanalog->getCNNType();
|
||||
}
|
||||
|
||||
return t_CNNType::None;
|
||||
}
|
||||
|
||||
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
void ClassFlowControll::DigitalDrawROI(CImageBasis *_zw)
|
||||
void ClassFlowControll::DigitDrawROI(CImageBasis *_zw)
|
||||
{
|
||||
if (flowdigit)
|
||||
if (flowdigit) {
|
||||
flowdigit->DrawROI(_zw);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowControll::AnalogDrawROI(CImageBasis *_zw)
|
||||
{
|
||||
if (flowanalog)
|
||||
if (flowanalog) {
|
||||
flowanalog->DrawROI(_zw);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#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 */
|
||||
for (int i = 0; i < FlowControll.size(); ++i) {
|
||||
if (FlowControll[i]->name().compare("ClassFlowMQTT") == 0) {
|
||||
return ((ClassFlowMQTT*) (FlowControll[i]))->Start(AutoInterval);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < FlowControll.size(); ++i) {
|
||||
if (FlowControll[i]->name().compare("ClassFlowMQTT") == 0) {
|
||||
return ((ClassFlowMQTT*) (FlowControll[i]))->Start(AutoInterval);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
|
||||
void ClassFlowControll::SetInitialParameter(void)
|
||||
{
|
||||
AutoStart = false;
|
||||
@@ -190,83 +205,102 @@ void ClassFlowControll::SetInitialParameter(void)
|
||||
disabled = false;
|
||||
aktRunNr = 0;
|
||||
aktstatus = "Flow task not yet created";
|
||||
aktstatusWithTime = aktstatus;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowControll::isAutoStart(long &_interval)
|
||||
bool ClassFlowControll::getIsAutoStart(void)
|
||||
{
|
||||
_interval = AutoInterval * 60 * 1000; // AutoInterval: minutes -> ms
|
||||
return AutoStart;
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowControll::setAutoStartInterval(long &_interval)
|
||||
{
|
||||
_interval = AutoInterval * 60 * 1000; // AutoInterval: minutes -> ms
|
||||
}
|
||||
|
||||
ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
|
||||
{
|
||||
ClassFlow* cfc = NULL;
|
||||
|
||||
_type = trim(_type);
|
||||
|
||||
if (toUpper(_type).compare("[TAKEIMAGE]") == 0)
|
||||
{
|
||||
if (toUpper(_type).compare("[TAKEIMAGE]") == 0) {
|
||||
cfc = new ClassFlowTakeImage(&FlowControll);
|
||||
flowtakeimage = (ClassFlowTakeImage*) cfc;
|
||||
}
|
||||
if (toUpper(_type).compare("[ALIGNMENT]") == 0)
|
||||
{
|
||||
|
||||
if (toUpper(_type).compare("[ALIGNMENT]") == 0) {
|
||||
cfc = new ClassFlowAlignment(&FlowControll);
|
||||
flowalignment = (ClassFlowAlignment*) cfc;
|
||||
}
|
||||
if (toUpper(_type).compare("[ANALOG]") == 0)
|
||||
{
|
||||
|
||||
if (toUpper(_type).compare("[ANALOG]") == 0) {
|
||||
cfc = new ClassFlowCNNGeneral(flowalignment);
|
||||
flowanalog = (ClassFlowCNNGeneral*) cfc;
|
||||
}
|
||||
if (toUpper(_type).compare(0, 7, "[DIGITS") == 0)
|
||||
{
|
||||
|
||||
if (toUpper(_type).compare(0, 7, "[DIGITS") == 0) {
|
||||
cfc = new ClassFlowCNNGeneral(flowalignment);
|
||||
flowdigit = (ClassFlowCNNGeneral*) cfc;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
if (toUpper(_type).compare("[MQTT]") == 0)
|
||||
if (toUpper(_type).compare("[MQTT]") == 0) {
|
||||
cfc = new ClassFlowMQTT(&FlowControll);
|
||||
}
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
if (toUpper(_type).compare("[INFLUXDB]") == 0)
|
||||
if (toUpper(_type).compare("[INFLUXDB]") == 0) {
|
||||
cfc = new ClassFlowInfluxDB(&FlowControll);
|
||||
#endif //ENABLE_INFLUXDB
|
||||
if (toUpper(_type).compare("[WRITELIST]") == 0)
|
||||
cfc = new ClassFlowWriteList(&FlowControll);
|
||||
}
|
||||
|
||||
if (toUpper(_type).compare("[POSTPROCESSING]") == 0)
|
||||
{
|
||||
if (toUpper(_type).compare("[INFLUXDBV2]") == 0) {
|
||||
cfc = new ClassFlowInfluxDBv2(&FlowControll);
|
||||
}
|
||||
#endif //ENABLE_INFLUXDB
|
||||
#ifdef ENABLE_WEBHOOK
|
||||
if (toUpper(_type).compare("[WEBHOOK]") == 0)
|
||||
cfc = new ClassFlowWebhook(&FlowControll);
|
||||
#endif //ENABLE_WEBHOOK
|
||||
|
||||
if (toUpper(_type).compare("[POSTPROCESSING]") == 0) {
|
||||
cfc = new ClassFlowPostProcessing(&FlowControll, flowanalog, flowdigit);
|
||||
flowpostprocessing = (ClassFlowPostProcessing*) cfc;
|
||||
}
|
||||
|
||||
if (cfc) // Attached only if it is not [AutoTimer], because this is for FlowControll
|
||||
if (cfc) {
|
||||
// Attached only if it is not [AutoTimer], because this is for FlowControll
|
||||
FlowControll.push_back(cfc);
|
||||
}
|
||||
|
||||
if (toUpper(_type).compare("[AUTOTIMER]") == 0)
|
||||
cfc = this;
|
||||
if (toUpper(_type).compare("[AUTOTIMER]") == 0) {
|
||||
cfc = this;
|
||||
}
|
||||
|
||||
if (toUpper(_type).compare("[DATALOGGING]") == 0)
|
||||
cfc = this;
|
||||
if (toUpper(_type).compare("[DATALOGGING]") == 0) {
|
||||
cfc = this;
|
||||
}
|
||||
|
||||
if (toUpper(_type).compare("[DEBUG]") == 0)
|
||||
cfc = this;
|
||||
if (toUpper(_type).compare("[DEBUG]") == 0) {
|
||||
cfc = this;
|
||||
}
|
||||
|
||||
if (toUpper(_type).compare("[SYSTEM]") == 0)
|
||||
cfc = this;
|
||||
if (toUpper(_type).compare("[SYSTEM]") == 0) {
|
||||
cfc = this;
|
||||
}
|
||||
|
||||
return cfc;
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowControll::InitFlow(std::string config)
|
||||
{
|
||||
aktstatus = "Initialization";
|
||||
aktstatusWithTime = aktstatus;
|
||||
|
||||
//#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
|
||||
|
||||
string line;
|
||||
@@ -280,60 +314,61 @@ void ClassFlowControll::InitFlow(std::string config)
|
||||
line = "";
|
||||
|
||||
char zw[1024];
|
||||
if (pFile != NULL)
|
||||
{
|
||||
|
||||
if (pFile != NULL) {
|
||||
fgets(zw, 1024, pFile);
|
||||
ESP_LOGD(TAG, "%s", zw);
|
||||
line = std::string(zw);
|
||||
}
|
||||
|
||||
while ((line.size() > 0) && !(feof(pFile)))
|
||||
{
|
||||
while ((line.size() > 0) && !(feof(pFile))) {
|
||||
cfc = CreateClassFlow(line);
|
||||
if (cfc)
|
||||
{
|
||||
ESP_LOGD(TAG, "Start ReadParameter (%s)", line.c_str());
|
||||
// printf("Name: %s\n", cfc->name().c_str());
|
||||
|
||||
if (cfc) {
|
||||
ESP_LOGE(TAG, "Start ReadParameter (%s)", line.c_str());
|
||||
cfc->ReadParameter(pFile, line);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
line = "";
|
||||
if (fgets(zw, 1024, pFile) && !feof(pFile))
|
||||
{
|
||||
ESP_LOGD(TAG, "Read: %s", zw);
|
||||
line = std::string(zw);
|
||||
}
|
||||
|
||||
if (fgets(zw, 1024, pFile) && !feof(pFile)) {
|
||||
ESP_LOGD(TAG, "Read: %s", zw);
|
||||
line = std::string(zw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(pFile);
|
||||
}
|
||||
|
||||
std::string* ClassFlowControll::getActStatusWithTime()
|
||||
{
|
||||
return &aktstatusWithTime;
|
||||
}
|
||||
|
||||
std::string* ClassFlowControll::getActStatus()
|
||||
{
|
||||
return &aktstatus;
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowControll::setActStatus(std::string _aktstatus)
|
||||
{
|
||||
aktstatus = _aktstatus;
|
||||
aktstatusWithTime = aktstatus;
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowControll::doFlowTakeImageOnly(string 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() == "ClassFlowTakeImage") {
|
||||
zw_time = getCurrentTimeString("%H:%M:%S");
|
||||
std::string flowStatus = TranslateAktstatus(FlowControll[i]->name());
|
||||
aktstatus = flowStatus + " (" + zw_time + ")";
|
||||
aktstatus = TranslateAktstatus(FlowControll[i]->name());
|
||||
aktstatusWithTime = aktstatus + " (" + zw_time + ")";
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", flowStatus, false);
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, 1, false);
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
FlowControll[i]->doFlow(time);
|
||||
@@ -341,12 +376,12 @@ void ClassFlowControll::doFlowTakeImageOnly(string time)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowControll::doFlow(string time)
|
||||
{
|
||||
bool result = true;
|
||||
std::string zw_time;
|
||||
int repeat = 0;
|
||||
int qos = 1;
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowControll::doFlow - Start");
|
||||
@@ -360,14 +395,13 @@ bool ClassFlowControll::doFlow(string time)
|
||||
|
||||
//checkNtpStatus(0);
|
||||
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
{
|
||||
for (int i = 0; i < FlowControll.size(); ++i) {
|
||||
zw_time = getCurrentTimeString("%H:%M:%S");
|
||||
std::string flowStatus = TranslateAktstatus(FlowControll[i]->name());
|
||||
aktstatus = flowStatus + " (" + zw_time + ")";
|
||||
//LogFile.WriteToFile(ESP_LOG_INFO, TAG, aktstatus);
|
||||
aktstatus = TranslateAktstatus(FlowControll[i]->name());
|
||||
aktstatusWithTime = aktstatus + " (" + zw_time + ")";
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Status: " + aktstatusWithTime);
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", flowStatus, false);
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, qos, false);
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
@@ -378,7 +412,7 @@ bool ClassFlowControll::doFlow(string time)
|
||||
if (!FlowControll[i]->doFlow(time)){
|
||||
repeat++;
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Fehler im vorheriger Schritt - wird zum " + to_string(repeat) + ". Mal wiederholt");
|
||||
if (i) i -= 1; // vPrevious step must be repeated (probably take pictures)
|
||||
if (i) { i -= 1; } // vPrevious step must be repeated (probably take pictures)
|
||||
result = false;
|
||||
if (repeat > 5) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Wiederholung 5x nicht erfolgreich --> reboot");
|
||||
@@ -386,8 +420,7 @@ bool ClassFlowControll::doFlow(string time)
|
||||
//Step was repeated 5x --> reboot
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
result = true;
|
||||
}
|
||||
|
||||
@@ -397,11 +430,11 @@ bool ClassFlowControll::doFlow(string time)
|
||||
}
|
||||
|
||||
zw_time = getCurrentTimeString("%H:%M:%S");
|
||||
std::string flowStatus = "Flow finished";
|
||||
aktstatus = flowStatus + " (" + zw_time + ")";
|
||||
//LogFile.WriteToFile(ESP_LOG_INFO, TAG, aktstatus);
|
||||
aktstatus = "Flow finished";
|
||||
aktstatusWithTime = aktstatus + " (" + zw_time + ")";
|
||||
//LogFile.WriteToFile(ESP_LOG_INFO, TAG, aktstatusWithTime);
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", flowStatus, false);
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, qos, false);
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
return result;
|
||||
@@ -411,27 +444,29 @@ bool ClassFlowControll::doFlow(string time)
|
||||
string ClassFlowControll::getReadoutAll(int _type)
|
||||
{
|
||||
std::string out = "";
|
||||
if (flowpostprocessing)
|
||||
{
|
||||
|
||||
if (flowpostprocessing) {
|
||||
std::vector<NumberPost*> *numbers = flowpostprocessing->GetNumbers();
|
||||
|
||||
for (int i = 0; i < (*numbers).size(); ++i)
|
||||
{
|
||||
for (int i = 0; i < (*numbers).size(); ++i) {
|
||||
out = out + (*numbers)[i]->name + "\t";
|
||||
|
||||
switch (_type) {
|
||||
case READOUT_TYPE_VALUE:
|
||||
out = out + (*numbers)[i]->ReturnValue;
|
||||
break;
|
||||
case READOUT_TYPE_PREVALUE:
|
||||
if (flowpostprocessing->PreValueUse)
|
||||
{
|
||||
if ((*numbers)[i]->PreValueOkay)
|
||||
if (flowpostprocessing->PreValueUse) {
|
||||
if ((*numbers)[i]->PreValueOkay) {
|
||||
out = out + (*numbers)[i]->ReturnPreValue;
|
||||
else
|
||||
out = out + "PreValue too old";
|
||||
}
|
||||
else {
|
||||
out = out + "PreValue too old";
|
||||
}
|
||||
}
|
||||
else
|
||||
else {
|
||||
out = out + "PreValue deactivated";
|
||||
}
|
||||
break;
|
||||
case READOUT_TYPE_RAWVALUE:
|
||||
out = out + (*numbers)[i]->ReturnRawValue;
|
||||
@@ -440,8 +475,10 @@ string ClassFlowControll::getReadoutAll(int _type)
|
||||
out = out + (*numbers)[i]->ErrorMessageText;
|
||||
break;
|
||||
}
|
||||
if (i < (*numbers).size()-1)
|
||||
|
||||
if (i < (*numbers).size()-1) {
|
||||
out = out + "\r\n";
|
||||
}
|
||||
}
|
||||
// ESP_LOGD(TAG, "OUT: %s", out.c_str());
|
||||
}
|
||||
@@ -449,189 +486,166 @@ string ClassFlowControll::getReadoutAll(int _type)
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowControll::getReadout(bool _rawvalue = false, bool _noerror = false)
|
||||
string ClassFlowControll::getReadout(bool _rawvalue = false, bool _noerror = false, int _number = 0)
|
||||
{
|
||||
if (flowpostprocessing)
|
||||
return flowpostprocessing->getReadoutParam(_rawvalue, _noerror);
|
||||
|
||||
string zw = "";
|
||||
string result = "";
|
||||
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
{
|
||||
zw = FlowControll[i]->getReadout();
|
||||
if (zw.length() > 0)
|
||||
{
|
||||
if (result.length() == 0)
|
||||
result = zw;
|
||||
else
|
||||
result = result + "\t" + zw;
|
||||
}
|
||||
if (flowpostprocessing) {
|
||||
return flowpostprocessing->getReadoutParam(_rawvalue, _noerror, _number);
|
||||
}
|
||||
|
||||
return result;
|
||||
return std::string("");
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowControll::GetPrevalue(std::string _number)
|
||||
{
|
||||
if (flowpostprocessing)
|
||||
{
|
||||
if (flowpostprocessing) {
|
||||
return flowpostprocessing->GetPreValue(_number);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
_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)
|
||||
{
|
||||
zw = 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
|
||||
}
|
||||
else
|
||||
{
|
||||
zw = strtof(_newvalue.c_str(), &p);
|
||||
if (zw == 0)
|
||||
return "- Error in String to Value Conversion!!! Must be of format value=123.456";
|
||||
else {
|
||||
newvalueAsDouble = strtod(_newvalue.c_str(), &p);
|
||||
if (newvalueAsDouble == 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "UpdatePrevalue: No valid value for processing: " + _newvalue);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (flowpostprocessing)
|
||||
{
|
||||
flowpostprocessing->SetPreValue(zw, _numbers, _extern);
|
||||
return _newvalue;
|
||||
if (flowpostprocessing) {
|
||||
if (flowpostprocessing->SetPreValue(newvalueAsDouble, _numbers, _extern)) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "UpdatePrevalue: ERROR - Class Post-Processing not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
std::vector<string> splitted;
|
||||
|
||||
aktparamgraph = trim(aktparamgraph);
|
||||
|
||||
if (aktparamgraph.size() == 0)
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||
if (aktparamgraph.size() == 0) {
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph)) {
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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 Debug oder DataLogging
|
||||
(toUpper(aktparamgraph).compare("[SYSTEM]") != 0 && (toUpper(aktparamgraph).compare("[DATALOGGING]") != 0))) {
|
||||
// Paragraph passt nicht zu Debug oder DataLogging
|
||||
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]) == "AUTOSTART") && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "TRUE")
|
||||
|
||||
if ((toUpper(splitted[0]) == "AUTOSTART") && (splitted.size() > 1)) {
|
||||
AutoStart = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "INTERVAL") && (splitted.size() > 1)) {
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
AutoStart = true;
|
||||
AutoInterval = std::stof(splitted[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "INTERVAL") && (splitted.size() > 1))
|
||||
{
|
||||
AutoInterval = std::stof(splitted[1]);
|
||||
if ((toUpper(splitted[0]) == "DATALOGACTIVE") && (splitted.size() > 1)) {
|
||||
LogFile.SetDataLogToSD(alphanumericToBoolean(splitted[1]));
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "DATALOGACTIVE") && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "TRUE")
|
||||
if ((toUpper(splitted[0]) == "DATAFILESRETENTION") && (splitted.size() > 1)) {
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
LogFile.SetDataLogToSD(true);
|
||||
}
|
||||
else {
|
||||
LogFile.SetDataLogToSD(false);
|
||||
LogFile.SetDataLogRetention(std::stoi(splitted[1]));
|
||||
}
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "DATAFILESRETENTION") && (splitted.size() > 1))
|
||||
{
|
||||
LogFile.SetDataLogRetention(std::stoi(splitted[1]));
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "LOGLEVEL") && (splitted.size() > 1))
|
||||
{
|
||||
if ((toUpper(splitted[0]) == "LOGLEVEL") && (splitted.size() > 1)) {
|
||||
/* matches esp_log_level_t */
|
||||
if ((toUpper(splitted[1]) == "TRUE") || (toUpper(splitted[1]) == "2"))
|
||||
{
|
||||
if ((toUpper(splitted[1]) == "TRUE") || (toUpper(splitted[1]) == "2")) {
|
||||
LogFile.setLogLevel(ESP_LOG_WARN);
|
||||
}
|
||||
else if ((toUpper(splitted[1]) == "FALSE") || (toUpper(splitted[1]) == "0") || (toUpper(splitted[1]) == "1"))
|
||||
{
|
||||
else if ((toUpper(splitted[1]) == "FALSE") || (toUpper(splitted[1]) == "0") || (toUpper(splitted[1]) == "1")) {
|
||||
LogFile.setLogLevel(ESP_LOG_ERROR);
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "3")
|
||||
{
|
||||
else if (toUpper(splitted[1]) == "3") {
|
||||
LogFile.setLogLevel(ESP_LOG_INFO);
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "4")
|
||||
{
|
||||
else if (toUpper(splitted[1]) == "4") {
|
||||
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]) == "LOGFILESRETENTION") && (splitted.size() > 1))
|
||||
{
|
||||
LogFile.SetLogFileRetention(std::stoi(splitted[1]));
|
||||
|
||||
if ((toUpper(splitted[0]) == "LOGFILESRETENTION") && (splitted.size() > 1)) {
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
LogFile.SetLogFileRetention(std::stoi(splitted[1]));
|
||||
}
|
||||
}
|
||||
|
||||
/* TimeServer and TimeZone got already read from the config, see setupTime () */
|
||||
|
||||
if ((toUpper(splitted[0]) == "RSSITHRESHOLD") && (splitted.size() > 1))
|
||||
{
|
||||
if (ChangeRSSIThreshold(WLAN_CONFIG_FILE, atoi(splitted[1].c_str())))
|
||||
{
|
||||
|
||||
#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)) {
|
||||
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 !!!
|
||||
fclose(pfile);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Rebooting to activate new RSSITHRESHOLD ...");
|
||||
esp_restart();
|
||||
hard_restart();
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Rebooting to activate new RSSITHRESHOLD ...");
|
||||
doReboot();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((toUpper(splitted[0]) == "HOSTNAME") && (splitted.size() > 1)) {
|
||||
if (ChangeHostName(WLAN_CONFIG_FILE, splitted[1])) {
|
||||
// reboot necessary so that the new wlan.ini is also used !!!
|
||||
fclose(pfile);
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Rebooting to activate new HOSTNAME...");
|
||||
doReboot();
|
||||
}
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "HOSTNAME") && (splitted.size() > 1))
|
||||
{
|
||||
if (ChangeHostName(WLAN_CONFIG_FILE, splitted[1]))
|
||||
{
|
||||
// reboot necessary so that the new wlan.ini is also used !!!
|
||||
fclose(pfile);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Rebooting to activate new HOSTNAME...");
|
||||
esp_restart();
|
||||
hard_restart();
|
||||
doReboot();
|
||||
}
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "SETUPMODE") && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "TRUE")
|
||||
{
|
||||
SetupModeActive = true;
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "SETUPMODE") && (splitted.size() > 1)) {
|
||||
SetupModeActive = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int ClassFlowControll::CleanTempFolder() {
|
||||
const char* folderPath = "/sdcard/img_tmp";
|
||||
|
||||
ESP_LOGD(TAG, "Clean up temporary folder to avoid damage of sdcard sectors: %s", folderPath);
|
||||
DIR *dir = opendir(folderPath);
|
||||
|
||||
if (!dir) {
|
||||
ESP_LOGE(TAG, "Failed to stat dir: %s", folderPath);
|
||||
return -1;
|
||||
@@ -639,31 +653,33 @@ int ClassFlowControll::CleanTempFolder() {
|
||||
|
||||
struct dirent *entry;
|
||||
int deleted = 0;
|
||||
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
std::string path = string(folderPath) + "/" + entry->d_name;
|
||||
if (entry->d_type == DT_REG) {
|
||||
if (unlink(path.c_str()) == 0) {
|
||||
deleted ++;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "can't delete file: %s", path.c_str());
|
||||
}
|
||||
} else if (entry->d_type == DT_DIR) {
|
||||
deleted += removeFolder(path.c_str(), TAG);
|
||||
}
|
||||
if (entry->d_type == DT_REG) {
|
||||
if (unlink(path.c_str()) == 0) {
|
||||
deleted ++;
|
||||
}
|
||||
else {
|
||||
ESP_LOGE(TAG, "can't delete file: %s", path.c_str());
|
||||
}
|
||||
}
|
||||
else if (entry->d_type == DT_DIR) {
|
||||
deleted += removeFolder(path.c_str(), TAG);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
ESP_LOGD(TAG, "%d files deleted", deleted);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t ClassFlowControll::SendRawJPG(httpd_req_t *req)
|
||||
{
|
||||
return flowtakeimage != NULL ? flowtakeimage->SendRawJPG(req) : ESP_FAIL;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
||||
{
|
||||
ESP_LOGD(TAG, "ClassFlowControll::GetJPGStream %s", _fn.c_str());
|
||||
@@ -712,7 +728,7 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
||||
|
||||
httpd_resp_set_type(req, "image/jpeg");
|
||||
result = httpd_resp_send(req, (const char *)fileBuffer, fileSize);
|
||||
delete fileBuffer;
|
||||
free(fileBuffer);
|
||||
}
|
||||
else if (aktstatus.find("Initialization") != -1) {
|
||||
FILE* file = fopen("/sdcard/html/Flowstate_initialization.jpg", "rb");
|
||||
@@ -739,7 +755,7 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
||||
|
||||
httpd_resp_set_type(req, "image/jpeg");
|
||||
result = httpd_resp_send(req, (const char *)fileBuffer, fileSize);
|
||||
delete fileBuffer;
|
||||
free(fileBuffer);
|
||||
}
|
||||
else if (aktstatus.find("Take Image") != -1) {
|
||||
if (flowalignment && flowalignment->AlgROI) {
|
||||
@@ -801,7 +817,7 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
_send = new CImageBasis(flowalignment->ImageBasis);
|
||||
_send = new CImageBasis("alg_roi", flowalignment->ImageBasis);
|
||||
|
||||
if (_send->ImageOkay()) {
|
||||
if (flowalignment) flowalignment->DrawRef(_send);
|
||||
@@ -825,8 +841,8 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
||||
else {
|
||||
std::vector<HTMLInfo*> htmlinfo;
|
||||
|
||||
htmlinfo = GetAllDigital();
|
||||
ESP_LOGD(TAG, "After getClassFlowControll::GetAllDigital");
|
||||
htmlinfo = GetAllDigit();
|
||||
ESP_LOGD(TAG, "After getClassFlowControll::GetAllDigit");
|
||||
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
@@ -895,14 +911,20 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowControll::getNumbersName()
|
||||
{
|
||||
return flowpostprocessing->getNumbersName();
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowControll::getJSON()
|
||||
{
|
||||
return flowpostprocessing->GetJSON();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns a vector of all current sequences
|
||||
**/
|
||||
const std::vector<NumberPost*> &ClassFlowControll::getNumbers()
|
||||
{
|
||||
return *flowpostprocessing->GetNumbers();
|
||||
}
|
||||
|
||||
@@ -15,9 +15,12 @@
|
||||
#endif //ENABLE_MQTT
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
#include "ClassFlowInfluxDB.h"
|
||||
#include "ClassFlowInfluxDBv2.h"
|
||||
#endif //ENABLE_INFLUXDB
|
||||
#ifdef ENABLE_WEBHOOK
|
||||
#include "ClassFlowWebhook.h"
|
||||
#endif //ENABLE_WEBHOOK
|
||||
#include "ClassFlowCNNGeneral.h"
|
||||
#include "ClassFlowWriteList.h"
|
||||
|
||||
class ClassFlowControll :
|
||||
public ClassFlow
|
||||
@@ -34,32 +37,31 @@ protected:
|
||||
|
||||
bool AutoStart;
|
||||
float AutoInterval;
|
||||
bool SetupModeActive;
|
||||
void SetInitialParameter(void);
|
||||
std::string aktstatusWithTime;
|
||||
std::string aktstatus;
|
||||
int aktRunNr;
|
||||
|
||||
public:
|
||||
bool SetupModeActive;
|
||||
|
||||
void InitFlow(std::string config);
|
||||
bool doFlow(string time);
|
||||
void doFlowTakeImageOnly(string time);
|
||||
bool getStatusSetupModus(){return SetupModeActive;};
|
||||
string getReadout(bool _rawvalue, bool _noerror);
|
||||
string getReadout(bool _rawvalue, bool _noerror, int _number);
|
||||
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 = "");
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
string getJSON();
|
||||
const std::vector<NumberPost*> &getNumbers();
|
||||
string getNumbersName();
|
||||
|
||||
string TranslateAktstatus(std::string _input);
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
string GetMQTTMainTopic();
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
void DigitalDrawROI(CImageBasis *_zw);
|
||||
void DigitDrawROI(CImageBasis *_zw);
|
||||
void AnalogDrawROI(CImageBasis *_zw);
|
||||
#endif
|
||||
|
||||
@@ -68,15 +70,17 @@ public:
|
||||
|
||||
std::string doSingleStep(std::string _stepname, std::string _host);
|
||||
|
||||
bool isAutoStart(long &_interval);
|
||||
bool getIsAutoStart();
|
||||
void setAutoStartInterval(long &_interval);
|
||||
|
||||
std::string* getActStatusWithTime();
|
||||
std::string* getActStatus();
|
||||
void setActStatus(std::string _aktstatus);
|
||||
|
||||
std::vector<HTMLInfo*> GetAllDigital();
|
||||
std::vector<HTMLInfo*> GetAllDigit();
|
||||
std::vector<HTMLInfo*> GetAllAnalog();
|
||||
|
||||
t_CNNType GetTypeDigital();
|
||||
t_CNNType GetTypeDigit();
|
||||
t_CNNType GetTypeAnalog();
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
|
||||
#include "ClassFlowImage.h"
|
||||
|
||||
/**
|
||||
* Properties of one ROI
|
||||
* FIXME: naming of members could use some refactoring to comply with common C++ coding style guidelines
|
||||
*/
|
||||
struct roi {
|
||||
int posx, posy, deltax, deltay;
|
||||
float result_float;
|
||||
@@ -14,49 +18,66 @@ struct roi {
|
||||
CImageBasis *image, *image_org;
|
||||
};
|
||||
|
||||
/**
|
||||
* FIXME: Why is this additional layer needed?
|
||||
*/
|
||||
struct general {
|
||||
string name;
|
||||
std::vector<roi*> ROI;
|
||||
};
|
||||
|
||||
enum t_RateType {
|
||||
AbsoluteChange,
|
||||
RateChange
|
||||
AbsoluteChange, // ignores the time difference; only the value difference is used comparison with NumberPost.maxRate
|
||||
RateChange // time difference is considered and a normalized rate is used for comparison with NumberPost.maxRate
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Holds all properties and settings of a sequence. A sequence is a set of digit and/or analog ROIs that are combined to
|
||||
* provide one meter reading (value).
|
||||
* FIXME: can be renamed to `Sequence`
|
||||
*/
|
||||
struct NumberPost {
|
||||
float MaxRateValue;
|
||||
bool useMaxRateValue;
|
||||
t_RateType RateType;
|
||||
bool ErrorMessage;
|
||||
bool PreValueOkay;
|
||||
bool AllowNegativeRates;
|
||||
bool checkDigitIncreaseConsistency;
|
||||
time_t lastvalue;
|
||||
string timeStamp;
|
||||
double FlowRateAct; // m3 / min
|
||||
double PreValue; // last value that was read out well
|
||||
double Value; // last value read out, incl. corrections
|
||||
string ReturnRateValue; // return value rate
|
||||
string ReturnChangeAbsolute; // return value rate
|
||||
string ReturnRawValue; // Raw value (with N & leading 0)
|
||||
string ReturnValue; // corrected return value, if necessary with error message
|
||||
string ReturnPreValue; // corrected return value without error message
|
||||
string ErrorMessageText; // Error message for consistency check
|
||||
int AnzahlAnalog;
|
||||
int AnzahlDigital;
|
||||
int DecimalShift;
|
||||
int DecimalShiftInitial;
|
||||
float AnalogDigitalTransitionStart; // When is the digit > x.1, i.e. when does it start to tilt?
|
||||
int Nachkomma;
|
||||
float MaxRateValue; // maxRate; upper bound for the difference between two consecutive readings; affected by maxRateType;
|
||||
bool useMaxRateValue; // consistencyChecksEnabled; enables consistency checks; uses maxRate and maxRateType
|
||||
t_RateType MaxRateType; // maxRateType; affects how the value of maxRate is used for comparing the current and previous value
|
||||
bool ErrorMessage; // FIXME: not used; can be removed
|
||||
int ChangeRateThreshold; // threshold parameter for negative rate detection
|
||||
bool PreValueOkay; // previousValueValid; indicates that the reading of the previous round has no errors
|
||||
bool AllowNegativeRates; // allowNegativeRate; defines if the consistency checks allow negative rates between consecutive meter readings.
|
||||
bool checkDigitIncreaseConsistency; // extendedConsistencyCheck; performs an additional consistency check to avoid wrong readings
|
||||
time_t timeStampLastValue; // Timestamp for the last read value; is used for the log
|
||||
time_t timeStampLastPreValue; // Timestamp for the last PreValue set; is used for useMaxRateValue
|
||||
time_t timeStampTimeUTC; // FIXME: not used; can be removed.
|
||||
string timeStamp; // localTimeStr; timestamp of last valid reading formatted as local time
|
||||
double FlowRateAct; // currentRate; ΔValue/min; since usage is not limited to water meters, the physical unit is not known.
|
||||
double PreValue; // lastValidValue; most recent value that could be read w/o any errors
|
||||
double Value; // value; most recent readout; may include corrections
|
||||
string ReturnRateValue; // currentRateStr; current normalized rate; ΔValue/min
|
||||
string ReturnChangeAbsolute; // currentChangeStr; absolute difference between current and previous measurement
|
||||
string ReturnRawValue; // rawValueStr; Raw value (with N & leading 0)
|
||||
string ReturnValue; // valueStr; corrected return value, if necessary with error message
|
||||
string ReturnPreValue; // lastValidValueStr; corrected return value without error message
|
||||
string ErrorMessageText; // errorMessage; Error message for consistency checks
|
||||
int AnzahlAnalog; // numAnalogRoi; number of analog ROIs used in this sequence
|
||||
int AnzahlDigit; // numDigitRoi; number of digit ROIs used in this sequence
|
||||
int DecimalShift; // decimalShift; each increment shifts the decimal separator by one digit; value=value*10^decimalShift; pos. value shifts to the right
|
||||
int DecimalShiftInitial; // decimalShiftInitial; same as decimalShift but is a const to reset decimalShift after calculations
|
||||
float AnalogToDigitTransitionStart; // AnalogToDigitTransitionStartValue; FIXME: need a better description; When is the digit > x.1, i.e. when does it start to tilt?
|
||||
int Nachkomma; // decimalPlaces; usually defined by the number of analog ROIs; affected by DecimalShift
|
||||
|
||||
bool isExtendedResolution;
|
||||
string FieldV1; // influxdbFieldName_v1; Name of the Field in InfluxDBv1
|
||||
string MeasurementV1; // influxdbMeasurementName_v1; Name of the Measurement in InfluxDBv1
|
||||
|
||||
general *digit_roi;
|
||||
general *analog_roi;
|
||||
string FieldV2; // influxdbFieldName_v2; Name of the Field in InfluxDBv2
|
||||
string MeasurementV2; // influxdbMeasurementName_v2; Name of the Measurement in InfluxDBv2
|
||||
|
||||
string name;
|
||||
bool isExtendedResolution; // extendResolution; Adds the decimal place of the least significant analog ROI to the value
|
||||
|
||||
general *digit_roi; // digitRoi; set of digit ROIs for the sequence
|
||||
general *analog_roi; // analogRoi; set of analog ROIs for the sequence
|
||||
|
||||
string name; // name; Designation for the sequence
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,7 +17,8 @@ extern "C" {
|
||||
#include "esp_log.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char* TAG = "IMG";
|
||||
static const char* TAG = "FLOWIMAGE";
|
||||
|
||||
|
||||
ClassFlowImage::ClassFlowImage(const char* logTag)
|
||||
{
|
||||
|
||||
@@ -15,13 +15,12 @@
|
||||
|
||||
#include <time.h>
|
||||
|
||||
static const char* TAG = "class_flow_influxDb";
|
||||
static const char* TAG = "INFLUXDB";
|
||||
|
||||
void ClassFlowInfluxDB::SetInitialParameter(void)
|
||||
{
|
||||
uri = "";
|
||||
database = "";
|
||||
measurement = "";
|
||||
|
||||
OldValue = "";
|
||||
flowpostprocessing = NULL;
|
||||
@@ -86,33 +85,39 @@ bool ClassFlowInfluxDB::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
ESP_LOGD(TAG, "while loop reading line: %s", aktparamgraph.c_str());
|
||||
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];
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "PASSWORD") && (splitted.size() > 1))
|
||||
if ((toUpper(_param) == "PASSWORD") && (splitted.size() > 1))
|
||||
{
|
||||
this->password = splitted[1];
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "URI") && (splitted.size() > 1))
|
||||
if ((toUpper(_param) == "URI") && (splitted.size() > 1))
|
||||
{
|
||||
this->uri = splitted[1];
|
||||
}
|
||||
if (((toUpper(splitted[0]) == "MEASUREMENT")) && (splitted.size() > 1))
|
||||
{
|
||||
this->measurement = splitted[1];
|
||||
}
|
||||
if (((toUpper(splitted[0]) == "DATABASE")) && (splitted.size() > 1))
|
||||
if (((toUpper(_param) == "DATABASE")) && (splitted.size() > 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());
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init InfluxDB with uri: " + uri + ", measurement: " + measurement + ", user: " + user + ", password: " + password);
|
||||
InfluxDBInit(uri, database, measurement, user, password);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init InfluxDB with uri: " + uri + ", user: " + user + ", password: " + password);
|
||||
InfluxDBInit(uri, database, user, password);
|
||||
InfluxDBenable = true;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowInfluxDB::GetInfluxDBMeasurement()
|
||||
{
|
||||
return measurement;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowInfluxDB::doFlow(string zwtime)
|
||||
{
|
||||
if (!InfluxDBenable)
|
||||
return true;
|
||||
|
||||
std::string result;
|
||||
std::string measurement;
|
||||
std::string resulterror = "";
|
||||
std::string resultraw = "";
|
||||
std::string resultrate = "";
|
||||
std::string resulttimestamp = "";
|
||||
long int timeutc;
|
||||
string zw = "";
|
||||
string namenumber = "";
|
||||
|
||||
@@ -147,20 +147,29 @@ bool ClassFlowInfluxDB::doFlow(string zwtime)
|
||||
|
||||
for (int i = 0; i < (*NUMBERS).size(); ++i)
|
||||
{
|
||||
measurement = (*NUMBERS)[i]->MeasurementV1;
|
||||
result = (*NUMBERS)[i]->ReturnValue;
|
||||
resultraw = (*NUMBERS)[i]->ReturnRawValue;
|
||||
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
||||
resultrate = (*NUMBERS)[i]->ReturnRateValue;
|
||||
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
||||
timeutc = (*NUMBERS)[i]->timeStampTimeUTC;
|
||||
|
||||
namenumber = (*NUMBERS)[i]->name;
|
||||
if (namenumber == "default")
|
||||
namenumber = "value";
|
||||
if ((*NUMBERS)[i]->FieldV1.length() > 0)
|
||||
{
|
||||
namenumber = (*NUMBERS)[i]->FieldV1;
|
||||
}
|
||||
else
|
||||
namenumber = namenumber + "/value";
|
||||
{
|
||||
namenumber = (*NUMBERS)[i]->name;
|
||||
if (namenumber == "default")
|
||||
namenumber = "value";
|
||||
else
|
||||
namenumber = namenumber + "/value";
|
||||
}
|
||||
|
||||
if (result.length() > 0 && resulttimestamp.length() > 0)
|
||||
InfluxDBPublish(namenumber, result, resulttimestamp);
|
||||
if (result.length() > 0)
|
||||
InfluxDBPublish(measurement, namenumber, result, timeutc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,4 +178,50 @@ bool ClassFlowInfluxDB::doFlow(string zwtime)
|
||||
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
|
||||
@@ -21,14 +21,19 @@ protected:
|
||||
std::string user, password;
|
||||
bool InfluxDBenable;
|
||||
|
||||
void SetInitialParameter(void);
|
||||
void SetInitialParameter(void);
|
||||
|
||||
void handleFieldname(string _decsep, string _value);
|
||||
void handleMeasurement(string _decsep, string _value);
|
||||
|
||||
|
||||
|
||||
public:
|
||||
ClassFlowInfluxDB();
|
||||
ClassFlowInfluxDB(std::vector<ClassFlow*>* lfc);
|
||||
ClassFlowInfluxDB(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
||||
|
||||
string GetInfluxDBMeasurement();
|
||||
// string GetInfluxDBMeasurement();
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
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 "Helper.h"
|
||||
#include "connect_wlan.h"
|
||||
#include "read_wlanini.h"
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
#include "time_sntp.h"
|
||||
@@ -31,12 +32,15 @@ void ClassFlowMQTT::SetInitialParameter(void)
|
||||
topicError = "";
|
||||
topicRate = "";
|
||||
topicTimeStamp = "";
|
||||
maintopic = hostname;
|
||||
maintopic = wlan_config.hostname;
|
||||
|
||||
topicUptime = "";
|
||||
topicFreeMem = "";
|
||||
|
||||
clientname = "AIOTED-" + getMac();
|
||||
caCertFilename = "";
|
||||
clientCertFilename = "";
|
||||
clientKeyFilename = "";
|
||||
clientname = wlan_config.hostname;
|
||||
|
||||
OldValue = "";
|
||||
flowpostprocessing = NULL;
|
||||
@@ -101,6 +105,18 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
splitted = ZerlegeZeile(aktparamgraph);
|
||||
if ((toUpper(splitted[0]) == "CACERT") && (splitted.size() > 1))
|
||||
{
|
||||
this->caCertFilename = splitted[1];
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "CLIENTCERT") && (splitted.size() > 1))
|
||||
{
|
||||
this->clientCertFilename = splitted[1];
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "CLIENTKEY") && (splitted.size() > 1))
|
||||
{
|
||||
this->clientKeyFilename = splitted[1];
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "USER") && (splitted.size() > 1))
|
||||
{
|
||||
this->user = splitted[1];
|
||||
@@ -155,6 +171,9 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
else if (toUpper(splitted[1]) == "ENERGY_MWH") {
|
||||
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))
|
||||
@@ -165,7 +184,6 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
if (((toUpper(splitted[0]) == "TOPIC") || (toUpper(splitted[0]) == "MAINTOPIC")) && (splitted.size() > 1))
|
||||
{
|
||||
maintopic = splitted[1];
|
||||
mqttServer_setMainTopic(maintopic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,16 +192,12 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
* 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() */
|
||||
|
||||
mqttServer_setMainTopic(maintopic);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowMQTT::GetMQTTMainTopic()
|
||||
{
|
||||
return maintopic;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowMQTT::Start(float AutoInterval)
|
||||
{
|
||||
roundInterval = AutoInterval; // Minutes
|
||||
@@ -197,7 +211,8 @@ bool ClassFlowMQTT::Start(float AutoInterval)
|
||||
mqttServer_setParameter(flowpostprocessing->GetNumbers(), keepAlive, roundInterval);
|
||||
|
||||
bool MQTTConfigCheck = MQTT_Configure(uri, clientname, user, password, maintopic, LWT_TOPIC, LWT_CONNECTED,
|
||||
LWT_DISCONNECTED, keepAlive, SetRetainFlag, (void *)&GotConnected);
|
||||
LWT_DISCONNECTED, caCertFilename, clientCertFilename, clientKeyFilename,
|
||||
keepAlive, SetRetainFlag, (void *)&GotConnected);
|
||||
|
||||
if (!MQTTConfigCheck) {
|
||||
return false;
|
||||
@@ -209,6 +224,7 @@ bool ClassFlowMQTT::Start(float AutoInterval)
|
||||
|
||||
bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
{
|
||||
bool success;
|
||||
std::string result;
|
||||
std::string resulterror = "";
|
||||
std::string resultraw = "";
|
||||
@@ -219,8 +235,12 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
std::string resultchangabs = "";
|
||||
string zw = "";
|
||||
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())
|
||||
{
|
||||
@@ -246,13 +266,13 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
|
||||
|
||||
if (result.length() > 0)
|
||||
MQTTPublish(namenumber + "value", result, SetRetainFlag);
|
||||
success |= MQTTPublish(namenumber + "value", result, qos, SetRetainFlag);
|
||||
|
||||
if (resulterror.length() > 0)
|
||||
MQTTPublish(namenumber + "error", resulterror, SetRetainFlag);
|
||||
success |= MQTTPublish(namenumber + "error", resulterror, qos, SetRetainFlag);
|
||||
|
||||
if (resultrate.length() > 0) {
|
||||
MQTTPublish(namenumber + "rate", resultrate, SetRetainFlag);
|
||||
success |= MQTTPublish(namenumber + "rate", resultrate, qos, SetRetainFlag);
|
||||
|
||||
std::string resultRatePerTimeUnit;
|
||||
if (getTimeUnit() == "h") { // Need conversion to be per hour
|
||||
@@ -261,22 +281,22 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
else { // Keep per minute
|
||||
resultRatePerTimeUnit = resultrate;
|
||||
}
|
||||
MQTTPublish(namenumber + "rate_per_time_unit", resultRatePerTimeUnit, SetRetainFlag);
|
||||
success |= MQTTPublish(namenumber + "rate_per_time_unit", resultRatePerTimeUnit, qos, SetRetainFlag);
|
||||
}
|
||||
|
||||
if (resultchangabs.length() > 0) {
|
||||
MQTTPublish(namenumber + "changeabsolut", resultchangabs, SetRetainFlag); // Legacy API
|
||||
MQTTPublish(namenumber + "rate_per_digitalization_round", resultchangabs, SetRetainFlag);
|
||||
success |= MQTTPublish(namenumber + "changeabsolut", resultchangabs, qos, SetRetainFlag); // Legacy API
|
||||
success |= MQTTPublish(namenumber + "rate_per_Digitization_round", resultchangabs, qos, SetRetainFlag);
|
||||
}
|
||||
|
||||
if (resultraw.length() > 0)
|
||||
MQTTPublish(namenumber + "raw", resultraw, SetRetainFlag);
|
||||
success |= MQTTPublish(namenumber + "raw", resultraw, qos, SetRetainFlag);
|
||||
|
||||
if (resulttimestamp.length() > 0)
|
||||
MQTTPublish(namenumber + "timestamp", resulttimestamp, SetRetainFlag);
|
||||
success |= MQTTPublish(namenumber + "timestamp", resulttimestamp, qos, SetRetainFlag);
|
||||
|
||||
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;
|
||||
// }
|
||||
// }
|
||||
// MQTTPublish(topic, result, SetRetainFlag);
|
||||
// success |= MQTTPublish(topic, result, qos, SetRetainFlag);
|
||||
// }
|
||||
|
||||
OldValue = result;
|
||||
|
||||
if (!success) {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "One or more MQTT topics failed to be published!");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ protected:
|
||||
std::string OldValue;
|
||||
ClassFlowPostProcessing* flowpostprocessing;
|
||||
std::string user, password;
|
||||
std::string caCertFilename, clientCertFilename, clientKeyFilename;
|
||||
bool SetRetainFlag;
|
||||
int keepAlive; // Seconds
|
||||
float roundInterval; // Minutes
|
||||
@@ -31,7 +32,6 @@ public:
|
||||
ClassFlowMQTT(std::vector<ClassFlow*>* lfc);
|
||||
ClassFlowMQTT(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
||||
|
||||
string GetMQTTMainTopic();
|
||||
bool Start(float AutoInterval);
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,18 +15,15 @@ class ClassFlowPostProcessing :
|
||||
public ClassFlow
|
||||
{
|
||||
protected:
|
||||
std::vector<NumberPost*> NUMBERS;
|
||||
bool UpdatePreValueINI;
|
||||
|
||||
int PreValueAgeStartup;
|
||||
bool ErrorMessage;
|
||||
bool IgnoreLeadingNaN; // SPECIAL CASE for User Gustl ???
|
||||
|
||||
|
||||
ClassFlowCNNGeneral* flowAnalog;
|
||||
ClassFlowCNNGeneral* flowDigit;
|
||||
|
||||
|
||||
string FilePreValue;
|
||||
|
||||
ClassFlowTakeImage *flowTakeImage;
|
||||
@@ -42,18 +39,17 @@ protected:
|
||||
void handleMaxRateValue(string _decsep, string _value);
|
||||
void handleDecimalExtendedResolution(string _decsep, string _value);
|
||||
void handleMaxRateType(string _decsep, string _value);
|
||||
void handleAnalogDigitalTransitionStart(string _decsep, string _value);
|
||||
void handleAnalogToDigitTransitionStart(string _decsep, string _value);
|
||||
void handleAllowNegativeRate(string _decsep, string _value);
|
||||
void handleChangeRateThreshold(string _decsep, string _value);
|
||||
|
||||
std::string GetStringReadouts(general);
|
||||
|
||||
void WriteDataLog(int _index);
|
||||
|
||||
|
||||
|
||||
|
||||
public:
|
||||
bool PreValueUse;
|
||||
std::vector<NumberPost*> NUMBERS;
|
||||
|
||||
ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc, ClassFlowCNNGeneral *_analog, ClassFlowCNNGeneral *_digit);
|
||||
virtual ~ClassFlowPostProcessing(){};
|
||||
@@ -67,7 +63,7 @@ public:
|
||||
void SavePreValue();
|
||||
string getJsonFromNumber(int i, std::string _lineend);
|
||||
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 getNumbersName();
|
||||
|
||||
@@ -1,24 +1,31 @@
|
||||
#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 DEBUG_DETAIL_ON
|
||||
// #define WIFITURNOFF
|
||||
|
||||
static const char* TAG = "flow_make_image";
|
||||
static const char *TAG = "TAKEIMAGE";
|
||||
|
||||
esp_err_t ClassFlowTakeImage::camera_capture(){
|
||||
string nm = namerawimage;
|
||||
esp_err_t ClassFlowTakeImage::camera_capture(void)
|
||||
{
|
||||
string nm = namerawimage;
|
||||
Camera.CaptureToFile(nm);
|
||||
time(&TimeImageTaken);
|
||||
localtime(&TimeImageTaken);
|
||||
@@ -29,150 +36,500 @@ esp_err_t ClassFlowTakeImage::camera_capture(){
|
||||
void ClassFlowTakeImage::takePictureWithFlash(int flash_duration)
|
||||
{
|
||||
// in case the image is flipped, it must be reset here //
|
||||
rawImage->width = image_width;
|
||||
rawImage->height = image_height;
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
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 (SaveAllFiles) rawImage->SaveToFile(namerawimage);
|
||||
if (CCstatus.SaveAllFiles)
|
||||
{
|
||||
rawImage->SaveToFile(namerawimage);
|
||||
}
|
||||
}
|
||||
|
||||
void ClassFlowTakeImage::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";
|
||||
}
|
||||
}
|
||||
|
||||
// auslesen der Kameraeinstellungen aus der config.ini
|
||||
// wird beim Start aufgerufen
|
||||
bool ClassFlowTakeImage::ReadParameter(FILE *pfile, string &aktparamgraph)
|
||||
{
|
||||
Camera.getSensorDatenToCCstatus(); // Kamera >>> CCstatus
|
||||
|
||||
ClassFlowTakeImage::ClassFlowTakeImage(std::vector<ClassFlow*>* lfc) : ClassFlowImage(lfc, TAG)
|
||||
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))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
this->imagesRetention = std::stod(splitted[1]);
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "SAVEALLFILES") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.SaveAllFiles = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "WAITBEFORETAKINGPICTURE") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[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 (isStringNumeric(_ImageGainceiling))
|
||||
{
|
||||
int _ImageGainceiling_ = std::stoi(_ImageGainceiling);
|
||||
switch (_ImageGainceiling_)
|
||||
{
|
||||
case 1:
|
||||
CFstatus.ImageGainceiling = GAINCEILING_4X;
|
||||
break;
|
||||
case 2:
|
||||
CFstatus.ImageGainceiling = GAINCEILING_8X;
|
||||
break;
|
||||
case 3:
|
||||
CFstatus.ImageGainceiling = GAINCEILING_16X;
|
||||
break;
|
||||
case 4:
|
||||
CFstatus.ImageGainceiling = GAINCEILING_32X;
|
||||
break;
|
||||
case 5:
|
||||
CFstatus.ImageGainceiling = GAINCEILING_64X;
|
||||
break;
|
||||
case 6:
|
||||
CFstatus.ImageGainceiling = GAINCEILING_128X;
|
||||
break;
|
||||
default:
|
||||
CFstatus.ImageGainceiling = GAINCEILING_2X;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
int _ImageQuality = std::stoi(splitted[1]);
|
||||
CCstatus.ImageQuality = clipInt(_ImageQuality, 63, 6);
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMBRIGHTNESS") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
int _ImageBrightness = std::stoi(splitted[1]);
|
||||
CCstatus.ImageBrightness = clipInt(_ImageBrightness, 2, -2);
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMCONTRAST") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
int _ImageContrast = std::stoi(splitted[1]);
|
||||
CCstatus.ImageContrast = clipInt(_ImageContrast, 2, -2);
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMSATURATION") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
int _ImageSaturation = std::stoi(splitted[1]);
|
||||
CCstatus.ImageSaturation = clipInt(_ImageSaturation, 2, -2);
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMSHARPNESS") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
int _ImageSharpness = std::stoi(splitted[1]);
|
||||
if (CCstatus.CamSensor_id == OV2640_PID)
|
||||
{
|
||||
CCstatus.ImageSharpness = clipInt(_ImageSharpness, 2, -2);
|
||||
}
|
||||
else
|
||||
{
|
||||
CCstatus.ImageSharpness = clipInt(_ImageSharpness, 3, -3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMAUTOSHARPNESS") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageAutoSharpness = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMSPECIALEFFECT") && (splitted.size() > 1))
|
||||
{
|
||||
std::string _ImageSpecialEffect = toUpper(splitted[1]);
|
||||
|
||||
if (isStringNumeric(_ImageSpecialEffect))
|
||||
{
|
||||
int _ImageSpecialEffect_ = std::stoi(_ImageSpecialEffect);
|
||||
CFstatus.ImageSpecialEffect = clipInt(_ImageSpecialEffect_, 6, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
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 (isStringNumeric(_ImageWbMode))
|
||||
{
|
||||
int _ImageWbMode_ = std::stoi(_ImageWbMode);
|
||||
CFstatus.ImageWbMode = clipInt(_ImageWbMode_, 4, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
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))
|
||||
{
|
||||
CCstatus.ImageAwb = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMAWBGAIN") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageAwbGain = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMAEC") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageAec = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMAEC2") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageAec2 = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMAELEVEL") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
int _ImageAeLevel = std::stoi(splitted[1]);
|
||||
if (CCstatus.CamSensor_id == OV2640_PID)
|
||||
{
|
||||
CCstatus.ImageAeLevel = clipInt(_ImageAeLevel, 2, -2);
|
||||
}
|
||||
else
|
||||
{
|
||||
CCstatus.ImageAeLevel = clipInt(_ImageAeLevel, 5, -5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMAECVALUE") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
int _ImageAecValue = std::stoi(splitted[1]);
|
||||
CCstatus.ImageAecValue = clipInt(_ImageAecValue, 1200, 0);
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMAGC") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageAgc = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMAGCGAIN") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
int _ImageAgcGain = std::stoi(splitted[1]);
|
||||
CCstatus.ImageAgcGain = clipInt(_ImageAgcGain, 30, 0);
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMBPC") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageBpc = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMWPC") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageWpc = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMRAWGMA") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageRawGma = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMLENC") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageLenc = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMHMIRROR") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageHmirror = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMVFLIP") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageVflip = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMDCW") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageDcw = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMDENOISE") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
int _ImageDenoiseLevel = std::stoi(splitted[1]);
|
||||
if (CCstatus.CamSensor_id == OV2640_PID)
|
||||
{
|
||||
CCstatus.ImageDenoiseLevel = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
CCstatus.ImageDenoiseLevel = clipInt(_ImageDenoiseLevel, 8, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMZOOM") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageZoomEnabled = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMZOOMOFFSETX") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
int _ImageZoomOffsetX = std::stoi(splitted[1]);
|
||||
if (CCstatus.CamSensor_id == OV2640_PID)
|
||||
{
|
||||
CCstatus.ImageZoomOffsetX = clipInt(_ImageZoomOffsetX, 480, -480);
|
||||
}
|
||||
else if (CCstatus.CamSensor_id == OV3660_PID)
|
||||
{
|
||||
CCstatus.ImageZoomOffsetX = clipInt(_ImageZoomOffsetX, 704, -704);
|
||||
}
|
||||
else if (CCstatus.CamSensor_id == OV5640_PID)
|
||||
{
|
||||
CCstatus.ImageZoomOffsetX = clipInt(_ImageZoomOffsetX, 960, -960);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMZOOMOFFSETY") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
int _ImageZoomOffsetY = std::stoi(splitted[1]);
|
||||
if (CCstatus.CamSensor_id == OV2640_PID)
|
||||
{
|
||||
CCstatus.ImageZoomOffsetY = clipInt(_ImageZoomOffsetY, 360, -360);
|
||||
}
|
||||
else if (CCstatus.CamSensor_id == OV3660_PID)
|
||||
{
|
||||
CCstatus.ImageZoomOffsetY = clipInt(_ImageZoomOffsetY, 528, -528);
|
||||
}
|
||||
else if (CCstatus.CamSensor_id == OV5640_PID)
|
||||
{
|
||||
CCstatus.ImageZoomOffsetY = clipInt(_ImageZoomOffsetY, 720, -720);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMZOOMSIZE") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
int _ImageZoomSize = std::stoi(splitted[1]);
|
||||
if (CCstatus.CamSensor_id == OV2640_PID)
|
||||
{
|
||||
CCstatus.ImageZoomSize = clipInt(_ImageZoomSize, 29, 0);
|
||||
}
|
||||
else if (CCstatus.CamSensor_id == OV3660_PID)
|
||||
{
|
||||
CCstatus.ImageZoomSize = clipInt(_ImageZoomSize, 43, 0);
|
||||
}
|
||||
else if (CCstatus.CamSensor_id == OV5640_PID)
|
||||
{
|
||||
CCstatus.ImageZoomSize = clipInt(_ImageZoomSize, 59, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "LEDINTENSITY") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
float ledintensity = std::stof(splitted[1]);
|
||||
Camera.SetLEDIntensity(ledintensity);
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "DEMO") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.DemoMode = alphanumericToBoolean(splitted[1]);
|
||||
if (CCstatus.DemoMode == true)
|
||||
{
|
||||
Camera.useDemoMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera
|
||||
Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize, CCstatus.ImageVflip);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowTakeImage::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("[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;
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "IMAGEQUALITY") && (splitted.size() > 1))
|
||||
ImageQuality = std::stod(splitted[1]);
|
||||
|
||||
if ((toUpper(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]) == "RAWIMAGESRETENTION") && (splitted.size() > 1))
|
||||
{
|
||||
this->imagesRetention = 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 ClassFlowTakeImage::getHTMLSingleStep(string host)
|
||||
{
|
||||
string result;
|
||||
@@ -180,70 +537,78 @@ string ClassFlowTakeImage::getHTMLSingleStep(string host)
|
||||
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) (waitbeforepicture * 1000);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - Before takePictureWithFlash");
|
||||
#endif
|
||||
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
|
||||
#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, CCstatus.ImageVflip);
|
||||
CFstatus.changedCameraSettings = false;
|
||||
}
|
||||
|
||||
takePictureWithFlash(flash_duration);
|
||||
|
||||
#ifdef WIFITURNOFF
|
||||
esp_wifi_start();
|
||||
#endif
|
||||
#ifdef WIFITURNOFF
|
||||
esp_wifi_start();
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - After takePictureWithFlash");
|
||||
#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
|
||||
#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) (waitbeforepicture * 1000);
|
||||
int flash_duration = (int)(CCstatus.WaitBeforePicture * 1000);
|
||||
time(&TimeImageTaken);
|
||||
localtime(&TimeImageTaken);
|
||||
|
||||
return Camera.CaptureToHTTP(req, flash_duration);
|
||||
}
|
||||
|
||||
|
||||
ImageData* ClassFlowTakeImage::SendRawImage()
|
||||
ImageData *ClassFlowTakeImage::SendRawImage(void)
|
||||
{
|
||||
CImageBasis *zw = new CImageBasis(rawImage);
|
||||
CImageBasis *zw = new CImageBasis("SendRawImage", rawImage);
|
||||
ImageData *id;
|
||||
int flash_duration = (int) (waitbeforepicture * 1000);
|
||||
int flash_duration = (int)(CCstatus.WaitBeforePicture * 1000);
|
||||
Camera.CaptureToBasisImage(zw, flash_duration);
|
||||
time(&TimeImageTaken);
|
||||
localtime(&TimeImageTaken);
|
||||
|
||||
id = zw->writeToMemoryAsJPG();
|
||||
id = zw->writeToMemoryAsJPG();
|
||||
delete zw;
|
||||
return id;
|
||||
return id;
|
||||
}
|
||||
|
||||
time_t ClassFlowTakeImage::getTimeImageTaken()
|
||||
time_t ClassFlowTakeImage::getTimeImageTaken(void)
|
||||
{
|
||||
return TimeImageTaken;
|
||||
}
|
||||
@@ -252,4 +617,3 @@ ClassFlowTakeImage::~ClassFlowTakeImage(void)
|
||||
{
|
||||
delete rawImage;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,47 +9,32 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
class ClassFlowTakeImage :
|
||||
public ClassFlowImage
|
||||
class ClassFlowTakeImage : 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();
|
||||
esp_err_t camera_capture(void);
|
||||
void takePictureWithFlash(int flash_duration);
|
||||
|
||||
|
||||
void SetInitialParameter(void);
|
||||
void SetInitialParameter(void);
|
||||
|
||||
public:
|
||||
CImageBasis *rawImage;
|
||||
|
||||
ClassFlowTakeImage(std::vector<ClassFlow*>* lfc);
|
||||
ClassFlowTakeImage(std::vector<ClassFlow *> *lfc);
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool ReadParameter(FILE *pfile, string &aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string getHTMLSingleStep(string host);
|
||||
time_t getTimeImageTaken();
|
||||
string name(){return "ClassFlowTakeImage";};
|
||||
time_t getTimeImageTaken(void);
|
||||
string name() { return "ClassFlowTakeImage"; };
|
||||
|
||||
ImageData* SendRawImage();
|
||||
ImageData *SendRawImage(void);
|
||||
esp_err_t SendRawJPG(httpd_req_t *req);
|
||||
|
||||
~ClassFlowTakeImage(void);
|
||||
};
|
||||
|
||||
|
||||
#endif //CLASSFFLOWTAKEIMAGE_H
|
||||
#endif // CLASSFFLOWTAKEIMAGE_H
|
||||
171
code/components/jomjol_flowcontroll/ClassFlowWebhook.cpp
Normal file
171
code/components/jomjol_flowcontroll/ClassFlowWebhook.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
#ifdef ENABLE_WEBHOOK
|
||||
#include <sstream>
|
||||
#include "ClassFlowWebhook.h"
|
||||
#include "Helper.h"
|
||||
#include "connect_wlan.h"
|
||||
|
||||
#include "time_sntp.h"
|
||||
#include "interface_webhook.h"
|
||||
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
#include "ClassFlowAlignment.h"
|
||||
#include "esp_log.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
static const char* TAG = "WEBHOOK";
|
||||
|
||||
void ClassFlowWebhook::SetInitialParameter(void)
|
||||
{
|
||||
uri = "";
|
||||
flowpostprocessing = NULL;
|
||||
flowAlignment = NULL;
|
||||
previousElement = NULL;
|
||||
ListFlowControll = NULL;
|
||||
disabled = false;
|
||||
WebhookEnable = false;
|
||||
WebhookUploadImg = 0;
|
||||
}
|
||||
|
||||
ClassFlowWebhook::ClassFlowWebhook()
|
||||
{
|
||||
SetInitialParameter();
|
||||
}
|
||||
|
||||
ClassFlowWebhook::ClassFlowWebhook(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];
|
||||
}
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowAlignment") == 0)
|
||||
{
|
||||
flowAlignment = (ClassFlowAlignment*) (*ListFlowControll)[i];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ClassFlowWebhook::ClassFlowWebhook(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];
|
||||
}
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowAlignment") == 0)
|
||||
{
|
||||
flowAlignment = (ClassFlowAlignment*) (*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowWebhook::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("[WEBHOOK]") != 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) == "URI") && (splitted.size() > 1))
|
||||
{
|
||||
this->uri = splitted[1];
|
||||
}
|
||||
if (((toUpper(_param) == "APIKEY")) && (splitted.size() > 1))
|
||||
{
|
||||
this->apikey = splitted[1];
|
||||
}
|
||||
if (((toUpper(_param) == "UPLOADIMG")) && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "1")
|
||||
{
|
||||
this->WebhookUploadImg = 1;
|
||||
} else if (toUpper(splitted[1]) == "2")
|
||||
{
|
||||
this->WebhookUploadImg = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WebhookInit(uri,apikey);
|
||||
WebhookEnable = true;
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Webhook Enabled for Uri " + uri);
|
||||
|
||||
printf("uri: %s\n", uri.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowWebhook::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 ClassFlowWebhook::doFlow(string zwtime)
|
||||
{
|
||||
if (!WebhookEnable)
|
||||
return true;
|
||||
|
||||
if (flowpostprocessing)
|
||||
{
|
||||
printf("vor sende WebHook");
|
||||
bool numbersWithError = WebhookPublish(flowpostprocessing->GetNumbers());
|
||||
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
if ((WebhookUploadImg == 1 || (WebhookUploadImg != 0 && numbersWithError)) && flowAlignment && flowAlignment->AlgROI) {
|
||||
WebhookUploadPic(flowAlignment->AlgROI);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif //ENABLE_WEBHOOK
|
||||
43
code/components/jomjol_flowcontroll/ClassFlowWebhook.h
Normal file
43
code/components/jomjol_flowcontroll/ClassFlowWebhook.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifdef ENABLE_WEBHOOK
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef CLASSFWEBHOOK_H
|
||||
#define CLASSFWEBHOOK_H
|
||||
|
||||
#include "ClassFlow.h"
|
||||
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
#include "ClassFlowAlignment.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
class ClassFlowWebhook :
|
||||
public ClassFlow
|
||||
{
|
||||
protected:
|
||||
std::string uri, apikey;
|
||||
ClassFlowPostProcessing* flowpostprocessing;
|
||||
ClassFlowAlignment* flowAlignment;
|
||||
|
||||
bool WebhookEnable;
|
||||
int WebhookUploadImg;
|
||||
|
||||
void SetInitialParameter(void);
|
||||
|
||||
void handleFieldname(string _decsep, string _value);
|
||||
void handleMeasurement(string _decsep, string _value);
|
||||
|
||||
|
||||
public:
|
||||
ClassFlowWebhook();
|
||||
ClassFlowWebhook(std::vector<ClassFlow*>* lfc);
|
||||
ClassFlowWebhook(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string name(){return "ClassFlowWebhook";};
|
||||
};
|
||||
|
||||
#endif //CLASSFWEBHOOK_H
|
||||
#endif //ENABLE_WEBHOOK
|
||||
@@ -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
|
||||
1850
code/components/jomjol_flowcontroll/MainFlowControl.cpp
Normal file
1850
code/components/jomjol_flowcontroll/MainFlowControl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
91
code/components/jomjol_flowcontroll/MainFlowControl.h
Normal file
91
code/components/jomjol_flowcontroll/MainFlowControl.h
Normal file
@@ -0,0 +1,91 @@
|
||||
#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"
|
||||
#include "openmetrics.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t CamSensor_id;
|
||||
|
||||
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 ImageDenoiseLevel; // The OV2640 does not support it, OV3660 and OV5640 (0 to 8)
|
||||
|
||||
int ImageWidth;
|
||||
int ImageHeight;
|
||||
|
||||
int ImageLedIntensity;
|
||||
|
||||
bool ImageZoomEnabled;
|
||||
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}
|
||||
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 <fstream>
|
||||
#include <vector>
|
||||
|
||||
#include "sdmmc_cmd.h"
|
||||
|
||||
using namespace std;
|
||||
@@ -20,7 +21,6 @@ bool RenameFile(string from, string to);
|
||||
bool MakeDir(std::string _what);
|
||||
bool FileExists(string filename);
|
||||
|
||||
|
||||
string RundeOutput(double _in, int _anzNachkomma);
|
||||
|
||||
size_t findDelimiterPos(string input, string delimiter);
|
||||
@@ -32,7 +32,6 @@ string getFileType(string filename);
|
||||
string getFileFullFileName(string filename);
|
||||
string getDirectory(string filename);
|
||||
|
||||
|
||||
int mkdir_r(const char *dir, const mode_t mode);
|
||||
int removeFolder(const char* folderPath, const char* logTag);
|
||||
|
||||
@@ -67,7 +66,6 @@ string getSDCardSectorSize();
|
||||
|
||||
string getMac(void);
|
||||
|
||||
|
||||
/* Error bit fields
|
||||
One bit per error
|
||||
Make sure it matches https://jomjol.github.io/AI-on-the-edge-device-docs/Error-Codes */
|
||||
@@ -76,6 +74,8 @@ enum SystemStatusFlag_t { // One bit per error
|
||||
SYSTEM_STATUS_PSRAM_BAD = 1 << 0, // 1, Critical Error
|
||||
SYSTEM_STATUS_HEAP_TOO_SMALL = 1 << 1, // 2, 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
|
||||
SYSTEM_STATUS_CAM_FB_BAD = 1 << (0+8), // 8, Flow still might work
|
||||
@@ -95,4 +95,18 @@ const char* get404(void);
|
||||
|
||||
std::string UrlDecode(const std::string& value);
|
||||
|
||||
void replaceAll(std::string& s, const std::string& toReplace, const std::string& replaceWith);
|
||||
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);
|
||||
|
||||
bool isStringNumeric(std::string &input);
|
||||
bool isStringAlphabetic(std::string &input);
|
||||
bool isStringAlphanumeric(std::string &input);
|
||||
bool alphanumericToBoolean(std::string &input);
|
||||
|
||||
int clipInt(int input, int high, int low);
|
||||
bool numericStrToBool(std::string input);
|
||||
bool stringToBoolean(std::string input);
|
||||
|
||||
#endif //HELPER_H
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#ifdef DEBUG_ENABLE_SYSINFO
|
||||
|
||||
#include "esp_sys.h"
|
||||
#include "esp_chip_info.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -121,7 +122,7 @@ std::string get_device_info()
|
||||
}
|
||||
|
||||
#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);
|
||||
sprintf(aMsgBuf,"himem free %u\n", esp_himem_get_free_size());
|
||||
espInfoResultStr += std::string(aMsgBuf);
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
#include <esp_spi_flash.h>
|
||||
#include <esp_heap_caps.h>
|
||||
|
||||
// for esp_spiram_get_size
|
||||
// for esp_psram_get_size
|
||||
extern "C" {
|
||||
#include <esp32/spiram.h>
|
||||
#include "esp_psram.h"
|
||||
#ifdef USE_HIMEM_IF_AVAILABLE
|
||||
#include <esp32/himem.h>
|
||||
#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 Digitization 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 == "Digitization_Model")) {
|
||||
sharedMemoryInUseFor = "Digitization_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 == "Digitization_Tensor")) {
|
||||
sharedMemoryInUseFor = "Digitization_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 Digitization 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 */
|
||||
646
code/components/jomjol_helper/sdcard_init.c
Normal file
646
code/components/jomjol_helper/sdcard_init.c
Normal file
@@ -0,0 +1,646 @@
|
||||
/*
|
||||
* 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 mh_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
|
||||
} mh_vfs_fat_sd_ctx_t;
|
||||
|
||||
static mh_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)
|
||||
{
|
||||
mh_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;
|
||||
mh_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(mh_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;
|
||||
mh_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(mh_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 <algorithm>
|
||||
#include <esp_log.h>
|
||||
#include "psram.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
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;
|
||||
channels = _org->channels;
|
||||
width = _org->width;
|
||||
@@ -37,7 +39,7 @@ bool CAlignAndCutImage::Align(RefInfo *_temp1, RefInfo *_temp2)
|
||||
int r0_x, r0_y, r1_x, r1_y;
|
||||
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_y = _temp1->target_y;
|
||||
@@ -81,7 +83,7 @@ bool CAlignAndCutImage::Align(RefInfo *_temp1, RefInfo *_temp2)
|
||||
LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", zw);
|
||||
#endif*/
|
||||
|
||||
CRotateImage rt(this, ImageTMP);
|
||||
CRotateImage rt("Align", this, ImageTMP);
|
||||
rt.Translate(dx, dy);
|
||||
rt.Rotate(d_winkel, _temp1->target_x, _temp1->target_y);
|
||||
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;
|
||||
|
||||
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_source;
|
||||
@@ -186,7 +188,7 @@ CImageBasis* CAlignAndCutImage::CutAndSave(int x1, int y1, int dx, int dy)
|
||||
dy = y2 - y1;
|
||||
|
||||
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_source;
|
||||
@@ -202,7 +204,7 @@ CImageBasis* CAlignAndCutImage::CutAndSave(int x1, int y1, int dx, int dy)
|
||||
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();
|
||||
rs->SetIndepended();
|
||||
return rs;
|
||||
|
||||
@@ -12,9 +12,9 @@ class CAlignAndCutImage : public CImageBasis
|
||||
public:
|
||||
int t0_dx, t0_dy, t1_dx, t1_dy;
|
||||
CImageBasis *ImageTMP;
|
||||
CAlignAndCutImage(std::string _image) : CImageBasis(_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(CImageBasis *_org, CImageBasis *_temp);
|
||||
CAlignAndCutImage(std::string name, std::string _image) : CImageBasis(name, _image) {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(std::string name, CImageBasis *_org, CImageBasis *_temp);
|
||||
|
||||
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 = "");
|
||||
|
||||
@@ -32,7 +32,7 @@ class CFindTemplate : public CImageBasis
|
||||
{
|
||||
public:
|
||||
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);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "CImageBasis.h"
|
||||
#include "Helper.h"
|
||||
#include "psram.h"
|
||||
#include "ClassLogFile.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;
|
||||
rgb_image = NULL;
|
||||
width = 0;
|
||||
@@ -384,8 +386,9 @@ void CImageBasis::CreateEmptyImage(int _width, int _height, int _channels)
|
||||
LogFile.WriteHeapInfo("CreateEmptyImage");
|
||||
#endif
|
||||
|
||||
int memsize = width * height * channels;
|
||||
rgb_image = (unsigned char*)GET_MEMORY(memsize);
|
||||
memsize = width * height * channels;
|
||||
|
||||
rgb_image = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
|
||||
|
||||
if (rgb_image == NULL)
|
||||
{
|
||||
@@ -435,10 +438,12 @@ void CImageBasis::LoadFromMemory(stbi_uc *_buffer, int len)
|
||||
{
|
||||
RGBImageLock();
|
||||
|
||||
if (rgb_image)
|
||||
if (rgb_image != NULL) {
|
||||
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;
|
||||
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;
|
||||
externalImage = false;
|
||||
channels = _copyfrom->channels;
|
||||
@@ -469,8 +513,15 @@ CImageBasis::CImageBasis(CImageBasis *_copyfrom)
|
||||
LogFile.WriteHeapInfo("CImageBasis_copyfrom - Start");
|
||||
#endif
|
||||
|
||||
int memsize = width * height * channels;
|
||||
rgb_image = (unsigned char*)GET_MEMORY(memsize);
|
||||
memsize = width * height * channels;
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -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;
|
||||
externalImage = false;
|
||||
channels = _channels;
|
||||
@@ -504,8 +556,9 @@ CImageBasis::CImageBasis(int _width, int _height, int _channels)
|
||||
LogFile.WriteHeapInfo("CImageBasis_width,height,ch - Start");
|
||||
#endif
|
||||
|
||||
int memsize = width * height * channels;
|
||||
rgb_image = (unsigned char*)GET_MEMORY(memsize);
|
||||
memsize = width * height * channels;
|
||||
|
||||
rgb_image = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
|
||||
|
||||
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;
|
||||
channels = 3;
|
||||
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;
|
||||
rgb_image = _rgb_image;
|
||||
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]
|
||||
{
|
||||
stbi_uc* p_source;
|
||||
@@ -607,8 +676,21 @@ CImageBasis::~CImageBasis()
|
||||
{
|
||||
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();
|
||||
}
|
||||
@@ -637,19 +719,19 @@ void CImageBasis::SaveToFile(std::string _imageout)
|
||||
|
||||
void CImageBasis::Resize(int _new_dx, int _new_dy)
|
||||
{
|
||||
int memsize = _new_dx * _new_dy * channels;
|
||||
uint8_t* odata = (unsigned char*)GET_MEMORY(memsize);
|
||||
memsize = _new_dx * _new_dy * channels;
|
||||
uint8_t* odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
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);
|
||||
width = _new_dx;
|
||||
height = _new_dy;
|
||||
stbi_image_free(odata);
|
||||
|
||||
free_psram_heap(std::string(TAG) + "->odata", odata);
|
||||
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "stb_image.h"
|
||||
#include "stb_image_write.h"
|
||||
#include "stb_image_resize.h"
|
||||
#include "../stb/stb_image.h"
|
||||
#include "../stb/stb_image_write.h"
|
||||
#include "../stb/stb_image_resize.h"
|
||||
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
@@ -30,6 +30,8 @@ class CImageBasis
|
||||
protected:
|
||||
bool externalImage;
|
||||
std::string filename;
|
||||
std::string name; // Just used for diagnostics
|
||||
int memsize = 0;
|
||||
|
||||
void memCopy(uint8_t* _source, uint8_t* _target, int _size);
|
||||
bool isInImage(int x, int y);
|
||||
@@ -37,7 +39,7 @@ class CImageBasis
|
||||
bool islocked;
|
||||
|
||||
public:
|
||||
uint8_t* rgb_image;
|
||||
uint8_t* rgb_image = NULL;
|
||||
int channels;
|
||||
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 setPixelColor(int x, int y, int r, int g, int b);
|
||||
void Negative(void);
|
||||
void Contrast(float _contrast);
|
||||
bool ImageOkay();
|
||||
bool CopyFromMemory(uint8_t* _source, int _size);
|
||||
@@ -64,14 +67,15 @@ class CImageBasis
|
||||
void EmptyImage();
|
||||
|
||||
|
||||
CImageBasis();
|
||||
CImageBasis(std::string _image);
|
||||
CImageBasis(uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp);
|
||||
CImageBasis(int _width, int _height, int _channels);
|
||||
CImageBasis(CImageBasis *_copyfrom);
|
||||
CImageBasis(std::string name);
|
||||
CImageBasis(std::string name, std::string _image);
|
||||
CImageBasis(std::string name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp);
|
||||
CImageBasis(std::string name, int _width, int _height, int _channels);
|
||||
CImageBasis(std::string name, CImageBasis *_copyfrom);
|
||||
|
||||
void Resize(int _new_dx, int _new_dy);
|
||||
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);
|
||||
|
||||
|
||||
@@ -1,349 +1,309 @@
|
||||
#include "CRotateImage.h"
|
||||
|
||||
|
||||
CRotateImage::CRotateImage(CImageBasis *_org, CImageBasis *_temp, bool _flip)
|
||||
{
|
||||
rgb_image = _org->rgb_image;
|
||||
channels = _org->channels;
|
||||
width = _org->width;
|
||||
height = _org->height;
|
||||
bpp = _org->bpp;
|
||||
externalImage = true;
|
||||
ImageTMP = _temp;
|
||||
ImageOrg = _org;
|
||||
islocked = false;
|
||||
doflip = _flip;
|
||||
}
|
||||
|
||||
void CRotateImage::Mirror(){
|
||||
int memsize = width * height * channels;
|
||||
uint8_t* odata;
|
||||
if (ImageTMP)
|
||||
{
|
||||
odata = ImageTMP->RGBImageLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
odata = (unsigned char*)GET_MEMORY(memsize);
|
||||
}
|
||||
|
||||
|
||||
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 = width - x;
|
||||
y_source = y;
|
||||
|
||||
p_source = rgb_image + (channels * (y_source * width + x_source));
|
||||
for (int _channels = 0; _channels < channels; ++_channels)
|
||||
p_target[_channels] = p_source[_channels];
|
||||
}
|
||||
|
||||
// memcpy(rgb_image, odata, memsize);
|
||||
memCopy(odata, rgb_image, memsize);
|
||||
if (!ImageTMP)
|
||||
stbi_image_free(odata);
|
||||
|
||||
if (ImageTMP)
|
||||
ImageTMP->RGBImageRelease();
|
||||
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
void CRotateImage::Rotate(float _angle, int _centerx, int _centery)
|
||||
{
|
||||
int org_width, org_height;
|
||||
float m[2][3];
|
||||
|
||||
float x_center = _centerx;
|
||||
float y_center = _centery;
|
||||
_angle = _angle / 180 * M_PI;
|
||||
|
||||
if (doflip)
|
||||
{
|
||||
org_width = width;
|
||||
org_height = height;
|
||||
height = org_width;
|
||||
width = org_height;
|
||||
x_center = x_center - (org_width/2) + (org_height/2);
|
||||
y_center = y_center + (org_width/2) - (org_height/2);
|
||||
if (ImageOrg)
|
||||
{
|
||||
ImageOrg->height = height;
|
||||
ImageOrg->width = width;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
org_width = width;
|
||||
org_height = height;
|
||||
}
|
||||
|
||||
m[0][0] = cos(_angle);
|
||||
m[0][1] = sin(_angle);
|
||||
m[0][2] = (1 - m[0][0]) * x_center - m[0][1] * y_center;
|
||||
|
||||
m[1][0] = -m[0][1];
|
||||
m[1][1] = m[0][0];
|
||||
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);
|
||||
m[1][2] = m[1][2] - (org_width/2) + (org_height/2);
|
||||
}
|
||||
|
||||
int memsize = width * height * channels;
|
||||
uint8_t* odata;
|
||||
if (ImageTMP)
|
||||
{
|
||||
odata = ImageTMP->RGBImageLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
odata = (unsigned char*)GET_MEMORY(memsize);
|
||||
}
|
||||
|
||||
|
||||
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 = int(m[0][0] * x + m[0][1] * y);
|
||||
y_source = int(m[1][0] * x + m[1][1] * y);
|
||||
|
||||
x_source += int(m[0][2]);
|
||||
y_source += int(m[1][2]);
|
||||
|
||||
if ((x_source >= 0) && (x_source < org_width) && (y_source >= 0) && (y_source < org_height))
|
||||
{
|
||||
p_source = rgb_image + (channels * (y_source * org_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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CRotateImage::RotateAntiAliasing(float _angle, int _centerx, int _centery)
|
||||
{
|
||||
int org_width, org_height;
|
||||
float m[2][3];
|
||||
|
||||
float x_center = _centerx;
|
||||
float y_center = _centery;
|
||||
_angle = _angle / 180 * M_PI;
|
||||
|
||||
if (doflip)
|
||||
{
|
||||
org_width = width;
|
||||
org_height = height;
|
||||
height = org_width;
|
||||
width = org_height;
|
||||
x_center = x_center - (org_width/2) + (org_height/2);
|
||||
y_center = y_center + (org_width/2) - (org_height/2);
|
||||
if (ImageOrg)
|
||||
{
|
||||
ImageOrg->height = height;
|
||||
ImageOrg->width = width;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
org_width = width;
|
||||
org_height = height;
|
||||
}
|
||||
|
||||
m[0][0] = cos(_angle);
|
||||
m[0][1] = sin(_angle);
|
||||
m[0][2] = (1 - m[0][0]) * x_center - m[0][1] * y_center;
|
||||
|
||||
m[1][0] = -m[0][1];
|
||||
m[1][1] = m[0][0];
|
||||
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);
|
||||
m[1][2] = m[1][2] - (org_width/2) + (org_height/2);
|
||||
}
|
||||
|
||||
int memsize = width * height * channels;
|
||||
uint8_t* odata;
|
||||
if (ImageTMP)
|
||||
{
|
||||
odata = ImageTMP->RGBImageLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
odata = (unsigned char*)GET_MEMORY(memsize);
|
||||
}
|
||||
|
||||
|
||||
int x_source_1, y_source_1, x_source_2, y_source_2;
|
||||
float x_source, y_source;
|
||||
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;
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
for (int x = 0; x < width; ++x)
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
p_target = odata + (channels * (y * width + x));
|
||||
|
||||
x_source = (m[0][0] * x + m[0][1] * y);
|
||||
y_source = (m[1][0] * x + m[1][1] * y);
|
||||
|
||||
x_source += (m[0][2]);
|
||||
y_source += (m[1][2]);
|
||||
|
||||
x_source_1 = (int)x_source;
|
||||
x_source_2 = x_source_1 + 1;
|
||||
y_source_1 = (int)y_source;
|
||||
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);
|
||||
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));
|
||||
|
||||
|
||||
if ((x_source_1 >= 0) && (x_source_2 < org_width) && (y_source_1 >= 0) && (y_source_2 < org_height))
|
||||
{
|
||||
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));
|
||||
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));
|
||||
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
|
||||
+ (float)p_source_or[_channels] * quad_or
|
||||
+ (float)p_source_ol[_channels] * quad_ol);
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
void CRotateImage::Rotate(float _angle)
|
||||
{
|
||||
// ESP_LOGD(TAG, "width %d, height %d", width, height);
|
||||
Rotate(_angle, width / 2, height / 2);
|
||||
}
|
||||
|
||||
void CRotateImage::RotateAntiAliasing(float _angle)
|
||||
{
|
||||
// ESP_LOGD(TAG, "width %d, height %d", width, height);
|
||||
RotateAntiAliasing(_angle, width / 2, height / 2);
|
||||
}
|
||||
|
||||
void CRotateImage::Translate(int _dx, int _dy)
|
||||
{
|
||||
int memsize = width * height * channels;
|
||||
uint8_t* odata;
|
||||
if (ImageTMP)
|
||||
{
|
||||
odata = ImageTMP->RGBImageLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
odata = (unsigned char*)GET_MEMORY(memsize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
#include <string>
|
||||
#include "CRotateImage.h"
|
||||
#include "psram.h"
|
||||
|
||||
static const char *TAG = "C ROTATE IMG";
|
||||
|
||||
CRotateImage::CRotateImage(std::string _name, CImageBasis *_org, CImageBasis *_temp, bool _flip) : CImageBasis(_name)
|
||||
{
|
||||
rgb_image = _org->rgb_image;
|
||||
channels = _org->channels;
|
||||
width = _org->width;
|
||||
height = _org->height;
|
||||
bpp = _org->bpp;
|
||||
externalImage = true;
|
||||
ImageTMP = _temp;
|
||||
ImageOrg = _org;
|
||||
islocked = false;
|
||||
doflip = _flip;
|
||||
}
|
||||
|
||||
void CRotateImage::Rotate(float _angle, int _centerx, int _centery)
|
||||
{
|
||||
int org_width, org_height;
|
||||
float m[2][3];
|
||||
|
||||
float x_center = _centerx;
|
||||
float y_center = _centery;
|
||||
_angle = _angle / 180 * M_PI;
|
||||
|
||||
if (doflip)
|
||||
{
|
||||
org_width = width;
|
||||
org_height = height;
|
||||
height = org_width;
|
||||
width = org_height;
|
||||
x_center = x_center - (org_width/2) + (org_height/2);
|
||||
y_center = y_center + (org_width/2) - (org_height/2);
|
||||
if (ImageOrg)
|
||||
{
|
||||
ImageOrg->height = height;
|
||||
ImageOrg->width = width;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
org_width = width;
|
||||
org_height = height;
|
||||
}
|
||||
|
||||
m[0][0] = cos(_angle);
|
||||
m[0][1] = sin(_angle);
|
||||
m[0][2] = (1 - m[0][0]) * x_center - m[0][1] * y_center;
|
||||
|
||||
m[1][0] = -m[0][1];
|
||||
m[1][1] = m[0][0];
|
||||
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);
|
||||
m[1][2] = m[1][2] - (org_width/2) + (org_height/2);
|
||||
}
|
||||
|
||||
int memsize = width * height * channels;
|
||||
uint8_t* odata;
|
||||
if (ImageTMP)
|
||||
{
|
||||
odata = ImageTMP->RGBImageLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
|
||||
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 = int(m[0][0] * x + m[0][1] * y);
|
||||
y_source = int(m[1][0] * x + m[1][1] * y);
|
||||
|
||||
x_source += int(m[0][2]);
|
||||
y_source += int(m[1][2]);
|
||||
|
||||
if ((x_source >= 0) && (x_source < org_width) && (y_source >= 0) && (y_source < org_height))
|
||||
{
|
||||
p_source = rgb_image + (channels * (y_source * org_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)
|
||||
{
|
||||
free_psram_heap(std::string(TAG) + "->odata", odata);
|
||||
}
|
||||
if (ImageTMP)
|
||||
ImageTMP->RGBImageRelease();
|
||||
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CRotateImage::RotateAntiAliasing(float _angle, int _centerx, int _centery)
|
||||
{
|
||||
int org_width, org_height;
|
||||
float m[2][3];
|
||||
|
||||
float x_center = _centerx;
|
||||
float y_center = _centery;
|
||||
_angle = _angle / 180 * M_PI;
|
||||
|
||||
if (doflip)
|
||||
{
|
||||
org_width = width;
|
||||
org_height = height;
|
||||
height = org_width;
|
||||
width = org_height;
|
||||
x_center = x_center - (org_width/2) + (org_height/2);
|
||||
y_center = y_center + (org_width/2) - (org_height/2);
|
||||
if (ImageOrg)
|
||||
{
|
||||
ImageOrg->height = height;
|
||||
ImageOrg->width = width;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
org_width = width;
|
||||
org_height = height;
|
||||
}
|
||||
|
||||
m[0][0] = cos(_angle);
|
||||
m[0][1] = sin(_angle);
|
||||
m[0][2] = (1 - m[0][0]) * x_center - m[0][1] * y_center;
|
||||
|
||||
m[1][0] = -m[0][1];
|
||||
m[1][1] = m[0][0];
|
||||
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);
|
||||
m[1][2] = m[1][2] - (org_width/2) + (org_height/2);
|
||||
}
|
||||
|
||||
int memsize = width * height * channels;
|
||||
uint8_t* odata;
|
||||
if (ImageTMP)
|
||||
{
|
||||
odata = ImageTMP->RGBImageLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
|
||||
int x_source_1, y_source_1, x_source_2, y_source_2;
|
||||
float x_source, y_source;
|
||||
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;
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
for (int x = 0; x < width; ++x)
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
p_target = odata + (channels * (y * width + x));
|
||||
|
||||
x_source = (m[0][0] * x + m[0][1] * y);
|
||||
y_source = (m[1][0] * x + m[1][1] * y);
|
||||
|
||||
x_source += (m[0][2]);
|
||||
y_source += (m[1][2]);
|
||||
|
||||
x_source_1 = (int)x_source;
|
||||
x_source_2 = x_source_1 + 1;
|
||||
y_source_1 = (int)y_source;
|
||||
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);
|
||||
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));
|
||||
|
||||
|
||||
if ((x_source_1 >= 0) && (x_source_2 < org_width) && (y_source_1 >= 0) && (y_source_2 < org_height))
|
||||
{
|
||||
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));
|
||||
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));
|
||||
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
|
||||
+ (float)p_source_or[_channels] * quad_or
|
||||
+ (float)p_source_ol[_channels] * quad_ol);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int _channels = 0; _channels < channels; ++_channels)
|
||||
p_target[_channels] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
// memcpy(rgb_image, odata, memsize);
|
||||
memCopy(odata, rgb_image, memsize);
|
||||
|
||||
if (!ImageTMP)
|
||||
{
|
||||
free_psram_heap(std::string(TAG) + "->odata", odata);
|
||||
}
|
||||
if (ImageTMP)
|
||||
ImageTMP->RGBImageRelease();
|
||||
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
|
||||
void CRotateImage::Rotate(float _angle)
|
||||
{
|
||||
// ESP_LOGD(TAG, "width %d, height %d", width, height);
|
||||
Rotate(_angle, width / 2, height / 2);
|
||||
}
|
||||
|
||||
void CRotateImage::RotateAntiAliasing(float _angle)
|
||||
{
|
||||
// ESP_LOGD(TAG, "width %d, height %d", width, height);
|
||||
RotateAntiAliasing(_angle, width / 2, height / 2);
|
||||
}
|
||||
|
||||
void CRotateImage::Translate(int _dx, int _dy)
|
||||
{
|
||||
int memsize = width * height * channels;
|
||||
uint8_t* odata;
|
||||
if (ImageTMP)
|
||||
{
|
||||
odata = ImageTMP->RGBImageLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
free_psram_heap(std::string(TAG) + "->odata", odata);
|
||||
}
|
||||
|
||||
if (ImageTMP)
|
||||
{
|
||||
ImageTMP->RGBImageRelease();
|
||||
}
|
||||
RGBImageRelease();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef CROTATEIMAGE_H
|
||||
#define CROTATEIMAGE_H
|
||||
|
||||
#include "CImageBasis.h"
|
||||
|
||||
|
||||
class CRotateImage: public CImageBasis
|
||||
{
|
||||
public:
|
||||
CImageBasis *ImageTMP, *ImageOrg;
|
||||
bool doflip;
|
||||
CRotateImage(std::string _image, bool _flip = false) : CImageBasis(_image) {ImageTMP = NULL; ImageOrg = NULL; doflip = _flip;};
|
||||
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(CImageBasis *_org, CImageBasis *_temp, bool _flip = false);
|
||||
|
||||
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 Translate(int _dx, int _dy);
|
||||
void Mirror();
|
||||
};
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef CROTATEIMAGE_H
|
||||
#define CROTATEIMAGE_H
|
||||
|
||||
#include "CImageBasis.h"
|
||||
|
||||
|
||||
class CRotateImage: public CImageBasis
|
||||
{
|
||||
|
||||
public:
|
||||
CImageBasis *ImageTMP, *ImageOrg;
|
||||
bool doflip;
|
||||
CRotateImage(std::string name, std::string _image, bool _flip = false) : CImageBasis(name, _image) {ImageTMP = NULL; ImageOrg = NULL; doflip = _flip;};
|
||||
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, int _centerx, int _centery);
|
||||
void RotateAntiAliasing(float _angle, int _centerx, int _centery);
|
||||
|
||||
void Translate(int _dx, int _dy);
|
||||
};
|
||||
|
||||
#endif //CROTATEIMAGE_H
|
||||
@@ -1,13 +1,28 @@
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include "psram.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
|
||||
#include "stb_image.h"
|
||||
#include "../stb/stb_image.h"
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "stb_image_write.h"
|
||||
#include "../stb/stb_image_write.h"
|
||||
|
||||
#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}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES tflite-lib esp_http_client jomjol_logfile)
|
||||
REQUIRES esp_http_client jomjol_logfile)
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <time.h>
|
||||
#include "ClassLogFile.h"
|
||||
#include "esp_http_client.h"
|
||||
#include "time_sntp.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
|
||||
@@ -12,10 +13,90 @@ static const char *TAG = "INFLUXDB";
|
||||
|
||||
std::string _influxDBURI;
|
||||
std::string _influxDBDatabase;
|
||||
std::string _influxDBMeasurement;
|
||||
std::string _influxDBUser;
|
||||
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)
|
||||
{
|
||||
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");
|
||||
break;
|
||||
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");
|
||||
break;
|
||||
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:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Disconnected");
|
||||
break;
|
||||
case HTTP_EVENT_REDIRECT:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Redirect");
|
||||
break;
|
||||
}
|
||||
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};
|
||||
esp_http_client_config_t http_config = {
|
||||
.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;
|
||||
}
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBPublish - Key: " + _key + ", Content: " + _content + ", Timestamp: " + _timestamp);
|
||||
|
||||
// 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);
|
||||
std::string payload;
|
||||
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, "Test Time Conversion - now: " + std::to_string(now) + ", timestamp: " + std::to_string(t) + "(correct time not used yet)");
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBPublish - Key: " + _key + ", Content: " + _content + ", timeUTC: " + std::to_string(_timeUTC));
|
||||
|
||||
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();
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload);
|
||||
|
||||
|
||||
// use the default retention policy of the database
|
||||
std::string apiURI = _influxDBURI + "/api/v2/write?bucket=" + _influxDBDatabase + "/";
|
||||
// use the default retention policy of the bucket
|
||||
std::string apiURI = _influxDBURI + "/write?db=" + _influxDBDatabase;
|
||||
// std::string apiURI = _influxDBURI + "/api/v2/write?bucket=" + _influxDBDatabase + "/";
|
||||
|
||||
apiURI.shrink_to_fit();
|
||||
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;
|
||||
_influxDBDatabase = _database;
|
||||
_influxDBMeasurement = _measurement;
|
||||
_influxDBUser = _user;
|
||||
_influxDBPassword = _password;
|
||||
|
||||
|
||||
@@ -8,10 +8,17 @@
|
||||
#include <map>
|
||||
#include <functional>
|
||||
|
||||
void InfluxDBInit(std::string _influxDBURI, std::string _database, std::string _measurement, std::string _user, std::string _password);
|
||||
void InfluxDBdestroy();
|
||||
// Interface to InfluxDB v1.x
|
||||
void InfluxDBInit(std::string _influxDBURI, std::string _database, std::string _user, std::string _password);
|
||||
void InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, 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 //ENABLE_INFLUXDB
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user