Compare commits
189 Commits
16.0.0-RC2
...
add-mdns
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ab4e806bd | ||
|
|
6e474441f6 | ||
|
|
bf2a2f553f | ||
|
|
21ab5cb203 | ||
|
|
1af1796ee0 | ||
|
|
095cf984c4 | ||
|
|
b72d809e59 | ||
|
|
c6a593ba0d | ||
|
|
c68463359b | ||
|
|
a1c2145e77 | ||
|
|
2986c6122d | ||
|
|
a348a51f14 | ||
|
|
26ca15e18a | ||
|
|
f4dccc1d52 | ||
|
|
a07c2cf46d | ||
|
|
2f39d9bdd4 | ||
|
|
7836323fbc | ||
|
|
708fd68cf5 | ||
|
|
1b5ff2ef1a | ||
|
|
1701aaed42 | ||
|
|
c07e5a740a | ||
|
|
3868cf98f6 | ||
|
|
179005f4ce | ||
|
|
50ada0a5a8 | ||
|
|
be7146c886 | ||
|
|
8f89a396f8 | ||
|
|
9e84d28ee9 | ||
|
|
3f4aaf303f | ||
|
|
08baf1824c | ||
|
|
2c7740ec16 | ||
|
|
3ee32d8cf0 | ||
|
|
25ae0465d8 | ||
|
|
14e9d6a9cc | ||
|
|
94c2de1c2a | ||
|
|
541e9841c2 | ||
|
|
fc3c70c63a | ||
|
|
bfde8316ce | ||
|
|
fe0fec7fac | ||
|
|
56abefe3df | ||
|
|
43f15fcba3 | ||
|
|
dadf18f5c9 | ||
|
|
c448ece680 | ||
|
|
07d6eca456 | ||
|
|
4083d35b61 | ||
|
|
2319f8d302 | ||
|
|
224e4380a8 | ||
|
|
14d0c88a28 | ||
|
|
3c9b2c46c8 | ||
|
|
0752694600 | ||
|
|
cf0ed268dd | ||
|
|
f1979a142e | ||
|
|
2a4f0f4a2d | ||
|
|
45269bf45b | ||
|
|
57db60fee4 | ||
|
|
e1c49f39f0 | ||
|
|
7bebae3776 | ||
|
|
0a4560ea95 | ||
|
|
b44db21714 | ||
|
|
34796ed091 | ||
|
|
a46dfd1c23 | ||
|
|
32eb583036 | ||
|
|
6ee83b8413 | ||
|
|
f034232f36 | ||
|
|
b80e43dfe9 | ||
|
|
40c7c253ea | ||
|
|
d11b312a96 | ||
|
|
2c1e531ed2 | ||
|
|
c352e539bc | ||
|
|
a0333d906f | ||
|
|
ec00e943da | ||
|
|
6175471a00 | ||
|
|
948e76876f | ||
|
|
f4edb206d8 | ||
|
|
a9db769595 | ||
|
|
f0497eb924 | ||
|
|
e53af6de0d | ||
|
|
3b7fe93d02 | ||
|
|
2a7247abfe | ||
|
|
37ba85717f | ||
|
|
97656114b6 | ||
|
|
6cf1d5ad98 | ||
|
|
fed729bcee | ||
|
|
bb69929247 | ||
|
|
e1ea09c501 | ||
|
|
39a827258d | ||
|
|
1d8c6fa257 | ||
|
|
a9aadbdb06 | ||
|
|
a5927f98d2 | ||
|
|
c708e2374a | ||
|
|
4c3dcd8c29 | ||
|
|
be93567956 | ||
|
|
d4406f47ea | ||
|
|
52efedcfa0 | ||
|
|
28d93253f0 | ||
|
|
c7fdc46df2 | ||
|
|
74491e9bde | ||
|
|
a092142c65 | ||
|
|
f6a3dc5851 | ||
|
|
d027adf006 | ||
|
|
bd5be5c5ec | ||
|
|
3b3a3ebcc9 | ||
|
|
7d62cf67fe | ||
|
|
f6bdd48bca | ||
|
|
5496573369 | ||
|
|
4522ba087f | ||
|
|
d370ba5fe6 | ||
|
|
f39dacc1c5 | ||
|
|
aad1a0e78d | ||
|
|
276efef783 | ||
|
|
79476a8458 | ||
|
|
2b7da5b44e | ||
|
|
ebcec97d1d | ||
|
|
85375b6505 | ||
|
|
085c47b651 | ||
|
|
20a04b888f | ||
|
|
098b1bd025 | ||
|
|
d1c815ce69 | ||
|
|
67c3020d7d | ||
|
|
61bca4ebb8 | ||
|
|
3219202c53 | ||
|
|
cd29690b96 | ||
|
|
3a34564ee2 | ||
|
|
174743ae7f | ||
|
|
e5eca6a53f | ||
|
|
d8e37dce48 | ||
|
|
822753bb4f | ||
|
|
7225792b4b | ||
|
|
b4f6b1a4fb | ||
|
|
21ec58daa0 | ||
|
|
2c69e90fac | ||
|
|
5c57522b71 | ||
|
|
f8eb4db171 | ||
|
|
c9a3df4eec | ||
|
|
acf669900f | ||
|
|
cb3f082218 | ||
|
|
20980b2bcd | ||
|
|
773d21a875 | ||
|
|
3a9c9ac2a4 | ||
|
|
c7e340d3a5 | ||
|
|
0441753a33 | ||
|
|
fb6cb44728 | ||
|
|
ae69942dd9 | ||
|
|
65437727f7 | ||
|
|
ccefe57795 | ||
|
|
e3ff049720 | ||
|
|
39e84baf8b | ||
|
|
eb7d078a1a | ||
|
|
b6c6805a08 | ||
|
|
d567a5d7f2 | ||
|
|
6922970185 | ||
|
|
9a4b51d6de | ||
|
|
2546ab81ff | ||
|
|
7b7544079f | ||
|
|
17fe87b349 | ||
|
|
f99dc8fdfc | ||
|
|
cce992754c | ||
|
|
eefccf6e11 | ||
|
|
64bb4f0ff6 | ||
|
|
f534741205 | ||
|
|
ee38bc7dc6 | ||
|
|
132834c3fd | ||
|
|
d3d9c64f3b | ||
|
|
aa2a4edf7e | ||
|
|
8012b7f43e | ||
|
|
009ab4c896 | ||
|
|
c54ca18e4e | ||
|
|
beb09593eb | ||
|
|
1a76ae121c | ||
|
|
1300242d4a | ||
|
|
79543df23b | ||
|
|
4049d752ba | ||
|
|
dc90972659 | ||
|
|
32282ecfe2 | ||
|
|
ae6a94544b | ||
|
|
70b031eacc | ||
|
|
c3fadf5c2a | ||
|
|
7e5f6bf4a5 | ||
|
|
88b531ae8b | ||
|
|
8481cc4b26 | ||
|
|
ecaed38c1d | ||
|
|
d6a1838d47 | ||
|
|
5194c466be | ||
|
|
043de9265a | ||
|
|
08a350172d | ||
|
|
7e806df64d | ||
|
|
dccfb5e91e | ||
|
|
fbe4609bb9 | ||
|
|
3e85cfb456 | ||
|
|
5dff4ca8cf |
25
.github/workflows/build.yaml
vendored
@@ -53,6 +53,7 @@ jobs:
|
||||
./code/.pio/build/esp32cam/partitions.bin
|
||||
./code/.pio/build/esp32cam/bootloader.bin
|
||||
./html/*
|
||||
./demo/*
|
||||
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
|
||||
|
||||
@@ -87,6 +88,11 @@ jobs:
|
||||
echo "Replacing variables..."
|
||||
cd html; find . -type f -exec sed -i 's/$COMMIT_HASH/${{ steps.vars.outputs.sha_short }}/g' {} \;
|
||||
|
||||
- name: Prepare Demo mode files
|
||||
run: |
|
||||
rm -rf ./demo
|
||||
mkdir demo
|
||||
cp -r ./sd-card/demo/* ./demo/
|
||||
|
||||
#########################################################################################
|
||||
## Pack for Update
|
||||
@@ -97,6 +103,7 @@ jobs:
|
||||
# - /firmware.bin
|
||||
# - (optional) /html/* (inkl. subfolders)
|
||||
# - (optional) /config/*.tfl
|
||||
# - (optional) /demo/*
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
@@ -111,6 +118,7 @@ jobs:
|
||||
./code/.pio/build/esp32cam/partitions.bin
|
||||
./code/.pio/build/esp32cam/bootloader.bin
|
||||
./html/*
|
||||
./demo/*
|
||||
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
|
||||
|
||||
@@ -135,6 +143,9 @@ jobs:
|
||||
|
||||
- name: Add Web UI to update
|
||||
run: cp -r ./html ./update/
|
||||
|
||||
- name: Add Demo mode files to update
|
||||
run: cp -r ./demo ./update/
|
||||
|
||||
- name: Add CNN to update
|
||||
run: |
|
||||
@@ -158,6 +169,7 @@ jobs:
|
||||
# remote_setup__version.zip file with following content:
|
||||
# - /firmware.bin
|
||||
# - /html/* (inkl. subfolders)
|
||||
# - /demo/*
|
||||
# - /config/*
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
@@ -173,6 +185,7 @@ jobs:
|
||||
./code/.pio/build/esp32cam/partitions.bin
|
||||
./code/.pio/build/esp32cam/bootloader.bin
|
||||
./html/*
|
||||
./demo/*
|
||||
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
|
||||
|
||||
@@ -197,6 +210,9 @@ jobs:
|
||||
- name: Add Web UI to remote_setup
|
||||
run: cp -r ./html ./remote_setup/
|
||||
|
||||
- name: Add Demo mode files to update
|
||||
run: cp -r ./demo ./update/
|
||||
|
||||
- name: Add whole config folder to remote_setup
|
||||
run: |
|
||||
rm -rf ./remote_setup/config/
|
||||
@@ -229,6 +245,7 @@ jobs:
|
||||
./code/.pio/build/esp32cam/partitions.bin
|
||||
./code/.pio/build/esp32cam/bootloader.bin
|
||||
./html/*
|
||||
./demo/*
|
||||
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
|
||||
|
||||
@@ -257,7 +274,9 @@ jobs:
|
||||
cp -f "./code/.pio/build/esp32cam/bootloader.bin" "manual_setup/bootloader.bin"
|
||||
cp -f "./code/.pio/build/esp32cam/partitions.bin" "manual_setup/partitions.bin"
|
||||
rm -rf ./sd-card/html
|
||||
rm -rf ./sd-card/demo
|
||||
cp -r ./html ./sd-card/ # Overwrite the Web UI with the preprocessed files
|
||||
cp -r ./demo ./sd-card/
|
||||
cd sd-card; zip -r ../manual_setup/sd-card.zip *; cd ..
|
||||
cd ./manual_setup
|
||||
|
||||
@@ -271,7 +290,7 @@ jobs:
|
||||
#########################################################################################
|
||||
## Prepare and create release
|
||||
#########################################################################################
|
||||
release:
|
||||
prepare-release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [pack-for-update, pack-for-manual_setup, pack-for-remote_setup]
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
@@ -331,7 +350,7 @@ jobs:
|
||||
|
||||
# extract the version used in next step
|
||||
- id: get_version
|
||||
uses: dhkatz/get-version-action@v3.0.0
|
||||
uses: drewg13/get-version-action@98dda2a47a257e202c2e6c2ed2e6072ec23f448e
|
||||
|
||||
# # the changelog [unreleased] will now be changed to the release version
|
||||
# - name: Update changelog
|
||||
@@ -377,7 +396,7 @@ jobs:
|
||||
# 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]
|
||||
needs: [prepare-release]
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
|
||||
21
.github/workflows/manual-update-contributors-list.yaml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# This updates the Contributors list in the README.md
|
||||
# it only gets run on:
|
||||
# - Manually triggered
|
||||
|
||||
name: Manually update contributors list
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Run on manual trigger
|
||||
|
||||
jobs:
|
||||
manually-update-contributors-list:
|
||||
runs-on: ubuntu-latest
|
||||
name: A job to automatically update the contributors list in the README.md
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Contribute List
|
||||
uses: akhilmhdh/contributors-readme-action@v2.3.10
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
3
.gitmodules
vendored
@@ -10,3 +10,6 @@
|
||||
[submodule "code/components/stb"]
|
||||
path = code/components/stb
|
||||
url = https://github.com/nothings/stb.git
|
||||
[submodule "code/esp-protocols"]
|
||||
path = code/esp-protocols
|
||||
url = https://github.com/espressif/esp-protocols.git
|
||||
|
||||
130
Changelog.md
@@ -1,3 +1,89 @@
|
||||
## [16.0.0-RC6] - 2024-xx-xx
|
||||
|
||||
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)
|
||||
|
||||
#### 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 RC5 are listed:
|
||||
- [#3436](https://github.com/jomjol/AI-on-the-edge-device/pull/3436) Added basic authentification of the Web Interface and the REST API, see https://jomjol.github.io/AI-on-the-edge-device-docs/Password-Protection
|
||||
- xxx
|
||||
|
||||
**:warning: Please check your Homeassistant instance to make sure it is handled correctly!**
|
||||
|
||||
#### Bug Fixes
|
||||
Only changes since RC5 are listed:
|
||||
- xxx
|
||||
|
||||
## [16.0.0-RC5] - 2024-12-05
|
||||
|
||||
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)
|
||||
|
||||
#### 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 RC4 are listed:
|
||||
- [#3423](https://github.com/jomjol/AI-on-the-edge-device/pull/3423) Removed `Autostart` parameter and make the flow to be always enabled
|
||||
- [#3423](https://github.com/jomjol/AI-on-the-edge-device/pull/3423) Enable `Flow start` menu entry in UI
|
||||
- [#3332](https://github.com/jomjol/AI-on-the-edge-device/pull/3332) Updated the Homeassistant Discovery topics :
|
||||
- `raw` has now set the `State Class` to `measurement`. Before it was always set to `""`.
|
||||
- `value` has now only set the `State Class` to `total_increasing` if the parameter `Allow Negative Rates` is **not** set. Else it uses `measurement` since the rate could also be negative. Before it was always set to `total_increasing`.
|
||||
- The `rate_per_time_unit` topic of an **Energy** meter needs a `Device Class`=`power`. For `gas` and `water` it should be `volume_flow_rate`. Before it was always set to `""`.
|
||||
- [#3415](https://github.com/jomjol/AI-on-the-edge-device/pull/3415) Added button for `flow start`
|
||||
- [#3359](https://github.com/jomjol/AI-on-the-edge-device/pull/3359) Added support for Domoticz MQTT integration
|
||||
- Added Date and time to overview page
|
||||
- Updated submodules and models
|
||||
|
||||
**:warning: Please check your Homeassistant instance to make sure it is handled correctly!**
|
||||
|
||||
#### Bug Fixes
|
||||
Only changes since RC4 are listed:
|
||||
- [#3418](https://github.com/jomjol/AI-on-the-edge-device/pull/3418) Added fix for ledintensity
|
||||
- [#3417](https://github.com/jomjol/AI-on-the-edge-device/pull/3417) Added fix for OV2640 brightness contrast saturation
|
||||
- [#3393](https://github.com/jomjol/AI-on-the-edge-device/pull/3393) Added fix for 'AnalogToDigitTransitionStart' always using 9.2 regardless of the configured value
|
||||
- [#3342](https://github.com/jomjol/AI-on-the-edge-device/pull/3342) Added fix for HA menu entry
|
||||
|
||||
|
||||
## [16.0.0-RC4] - 2024-10-06
|
||||
|
||||
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 RC3 are listed:
|
||||
- [#3316](https://github.com/jomjol/AI-on-the-edge-device/pull/3316) Update esp32-camera submodule to `v2.0.13`
|
||||
- [#3317](https://github.com/jomjol/AI-on-the-edge-device/pull/3317) Added contributor list
|
||||
- [#3315](https://github.com/jomjol/AI-on-the-edge-device/pull/3315) Added files for demo mode
|
||||
|
||||
#### Bug Fixes
|
||||
Only changes since RC2 are listed:
|
||||
- [#3313](https://github.com/jomjol/AI-on-the-edge-device/pull/3313) Added delay in InitCam to fix `Camera not detected` issues
|
||||
|
||||
|
||||
## [16.0.0-RC3] - 2024-10-05
|
||||
|
||||
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 RC2 are listed:
|
||||
- Renamed MQTT topic from `rate_per_digitalization_round` to `rate_per_digitization_round` (change happened already in RC1)
|
||||
|
||||
#### Bug Fixes
|
||||
Only changes since RC2 are listed:
|
||||
- [#3269](https://github.com/jomjol/AI-on-the-edge-device/pull/3269) Re-did revertion of TFlite submodule update as certain modules crash with it (change was lost)
|
||||
|
||||
|
||||
## [16.0.0-RC2] - 2024-10-04
|
||||
|
||||
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)
|
||||
@@ -9,13 +95,13 @@ Please check the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues
|
||||
#### Core Changes
|
||||
Only changes since RC1 are listed:
|
||||
- Updated parameter documentation pages
|
||||
- Rename/remove unused parameters (#3291)
|
||||
- Migrate-cam-parameters (#3288)
|
||||
- [#3291](https://github.com/jomjol/AI-on-the-edge-device/pull/3291) Rename/remove unused parameters
|
||||
- [#3288](https://github.com/jomjol/AI-on-the-edge-device/pull/3288) Migrate-cam-parameters
|
||||
|
||||
#### 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)
|
||||
- [#3269](https://github.com/jomjol/AI-on-the-edge-device/pull/3269) Reverted TFlite submodule update as certain modules crash with it
|
||||
- [#3279](https://github.com/jomjol/AI-on-the-edge-device/pull/3279) Changed the webhook UploadImg to false
|
||||
- Changed default value from boolean to numeric value in parameter camDenoise documentation
|
||||
- Updated config page
|
||||
|
||||
@@ -29,24 +115,24 @@ Please check the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues
|
||||
|
||||
#### Core Changes
|
||||
Those are just the major changes:
|
||||
- Add support for OV5640 camera (#3063)
|
||||
- [#3063](https://github.com/jomjol/AI-on-the-edge-device/pull/3063) Add support for OV5640 camera
|
||||
- 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)
|
||||
- [#3088](https://github.com/jomjol/AI-on-the-edge-device/pull/3088) Homeassistant service discovery: derive node_id when using nested topics
|
||||
- [#3081](https://github.com/jomjol/AI-on-the-edge-device/pull/3081) Added Prometheus/OpenMetrics exporter
|
||||
- [#3148](https://github.com/jomjol/AI-on-the-edge-device/pull/3148), [#3163](https://github.com/jomjol/AI-on-the-edge-device/pull/3163), [#3174](https://github.com/jomjol/AI-on-the-edge-device/pull/3148), [#3163](https://github.com/jomjol/AI-on-the-edge-device/pull/3163), [#3174](https://github.com/jomjol/AI-on-the-edge-device/pull/3174) Added Webhook
|
||||
- [#3195](https://github.com/jomjol/AI-on-the-edge-device/pull/3195) Add rate threshold parameter
|
||||
- [#3068](https://github.com/jomjol/AI-on-the-edge-device/pull/3068) Added a Delay between the WiFi reconnections
|
||||
- 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)
|
||||
- [#3220](https://github.com/jomjol/AI-on-the-edge-device/pull/3220) Handle crash on corrupted model
|
||||
- [#3175](https://github.com/jomjol/AI-on-the-edge-device/pull/3175) Bugfix for boot loop
|
||||
- [#3180](https://github.com/jomjol/AI-on-the-edge-device/pull/3180) Bugfix for time stamp
|
||||
- [#3162](https://github.com/jomjol/AI-on-the-edge-device/pull/3162) Handle empty prevalue.ini gracefully
|
||||
- [#3213](https://github.com/jomjol/AI-on-the-edge-device/pull/3213) Added note about only TLS 1.2 is supported
|
||||
|
||||
## [15.7.0] - 2024-02-17
|
||||
|
||||
@@ -545,16 +631,16 @@ Intermediate Digits
|
||||
- Implementation of new CNN types to detect intermediate values of digits with rolling numbers
|
||||
|
||||
- By default the old algo (0, 1, ..., 9, "N") is active (due to the limited types of digits trained so far)
|
||||
- Activation can be done by selection a tflite file with the new trained model in the 'config.ini'
|
||||
- Activation can be done by selecting a tflite file with the new trained model in the 'config.ini'
|
||||
- **Details can be found in the [wiki](https://github.com/jomjol/AI-on-the-edge-device/wiki/Neural-Network-Types)** (different types, trained image types, naming convention)
|
||||
|
||||
- Updated neural network files (and adaption to new naming convention)
|
||||
- Updated neural network files (and adaptation to new naming convention)
|
||||
|
||||
- Published a tool to download and combine log files - **Thanks to **
|
||||
- Published a tool to download and combine log files - Thanks to [Contributor]
|
||||
|
||||
- Files see ['/tools/logfile-tool'](tbd), How-to see [wiki](https://github.com/jomjol/AI-on-the-edge-device/wiki/Gasmeter-Log-Downloader)
|
||||
|
||||
- Bug Fix: InfluxDB enabling in grahic configuration
|
||||
- Bug Fix: InfluxDB enabling in graphic configuration
|
||||
|
||||
## [10.6.2](https://github.com/jomjol/AI-on-the-edge-device/releases/tag/v10.6.2), 2022-07-24
|
||||
|
||||
@@ -568,7 +654,7 @@ Stability Increase
|
||||
|
||||
- **NEW 10.6.1**: Bug Fix: tflite-filename with ".", HTML spelling error
|
||||
|
||||
- IndluxDB: direct injection into InfluxDB - thanks to **[wetneb](https://github.com/wetneb)**
|
||||
- InfluxDB: direct injection into InfluxDB - thanks to **[wetneb](https://github.com/wetneb)**
|
||||
|
||||
- MQTT: implemented "Retain Flag" and extend with absolute Change (in addition to rate)
|
||||
|
||||
@@ -1083,7 +1169,7 @@ External Illumination
|
||||
- Bug in configuration of analog ROIs corrected
|
||||
|
||||
|
||||
- minor bug correction
|
||||
- minor Bug correction
|
||||
|
||||
## [1.0.1](2020-09-05)
|
||||
|
||||
@@ -1092,7 +1178,7 @@ External Illumination
|
||||
- preValue.ini Bug corrected
|
||||
|
||||
|
||||
- minor bug correction
|
||||
- minor Bug correction
|
||||
|
||||
## [1.0.0](2020-09-04)
|
||||
|
||||
|
||||
@@ -40,16 +40,16 @@ ____
|
||||
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.
|
||||
as reading the Electricity or Water meter every few minutes only delivers apparent accuracy (DE: "Scheingenauigkeit") you could just as well take a picture with your Smartphone every 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 than on to whoever 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..
|
||||
for example see Roi in the next picture..
|
||||

|
||||
in case of position change between the measurments set this state to true, if there is no change set it back to false.
|
||||
in case of position change between the measurements set this state to true, if there is no change set it back to false.
|
||||
In a defined time window this movement can lead into an alarm state / water leak..
|
||||
haveing this state in the mqtt broker can trigger functions like closing the ater pipe walve and so on...
|
||||
having this state in the mqtt broker can trigger functions like closing the water pipe valve and so on...
|
||||
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ haveing this state in the mqtt broker can trigger functions like closing the ate
|
||||
|
||||
#### #31 Implement InfluxDB v2.x interface
|
||||
|
||||
* Currently only InfluxDB v1.x is supportet, extend to v2.x
|
||||
* Currently only InfluxDB v1.x is supported, extend to v2.x
|
||||
* Remark: interface has changed
|
||||
* see [#1160](https://github.com/jomjol/AI-on-the-edge-device/issues/1160)
|
||||
|
||||
@@ -82,7 +82,7 @@ haveing this state in the mqtt broker can trigger functions like closing the ate
|
||||
#### #28 Improved error handling for ROIs
|
||||
|
||||
* In case a ROI is out of the image, there is no error message, but a non sense image is used
|
||||
* Implement a error message for wrong configuratioin of ROI
|
||||
* Implement a error message for wrong configuration of ROI
|
||||
|
||||
#### #27 Use Homie Spec for Mqtt binding
|
||||
|
||||
|
||||
68
License-DRAFT.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# **Dual Use License for AI-on-the-Edge Device**
|
||||
|
||||
Version: 1.0 - Draft Version
|
||||
Date: 2025-01-05 (5th January 2025)
|
||||
|
||||
## **Preamble**
|
||||
|
||||
This license allows individuals to use, modify, and share AI-on-the-Edge freely for private, non-commercial purposes. Any commercial use requires a separate licensing agreement with the rights holder.
|
||||
|
||||
------
|
||||
|
||||
## **1. Grant of License**
|
||||
|
||||
### 1.1 **Private Use**
|
||||
|
||||
The licensor grants the licensee a free, non-exclusive, worldwide license to use, modify, and distribute the software for private, non-commercial purposes.
|
||||
|
||||
### 1.2 **Commercial Use**
|
||||
|
||||
The use of the software or any derivative works in any commercial context (including, but not limited to, selling, renting, providing as a service, or integrating into commercial products) is prohibited without a separate commercial license.
|
||||
|
||||
------
|
||||
|
||||
## **2. Obligation to Private Derivatives**
|
||||
|
||||
In modified private versions of the software, the unchanged license as well as the reference to the original source and authors must always be stated (https://github.com/jomjol/AI-on-the-edge-device).
|
||||
|
||||
Modified versions of the software must be clearly marked as such and must not imply they are provided by the original licensor.
|
||||
|
||||
------
|
||||
|
||||
## **3. Commercial Licensing**
|
||||
|
||||
Companies, organizations, or individuals wishing to use the software for commercial purposes must obtain a separate commercial license. Please contact mueller.josef(@)gmail.com for further details.
|
||||
|
||||
------
|
||||
|
||||
## **4. Terms of Cooperation**
|
||||
|
||||
By contributing to the AI-on-the-Edge software, this license is considered accepted. This applies to, but is not limited to, code, error corrections, extensions, artwork, documentation, and new features. Any contribution, including libraries and sources, must comply with the terms of this license.
|
||||
|
||||
The contributor agrees that the added code and functionality may also be used in commercial versions without compensation to the contributor.
|
||||
|
||||
------
|
||||
|
||||
## **5. Disclaimer of Liability**
|
||||
|
||||
### 5.1 **General Disclaimer**
|
||||
|
||||
The software is provided "as is", without any express or implied warranties. The licensor is not liable for any damages resulting from the use of the software.
|
||||
|
||||
### 5.2 **No Usage in Safety or Security Environments**
|
||||
|
||||
The image processing uses neural networks, among other algorithms, whose results can produce incorrect or unexpected outcomes due to their functionality and the underlying training data. Therefore, this system must not be used or offered for safety-relevant systems or systems with high reliability requirements.
|
||||
|
||||
------
|
||||
|
||||
## **6. General Provisions**
|
||||
|
||||
### 6.1 **Severability Clause**
|
||||
|
||||
If any provision of this license is deemed invalid, the remaining provisions shall remain in full force and effect.
|
||||
|
||||
------
|
||||
|
||||
## **Acceptance**
|
||||
|
||||
By using this software, the licensee agrees to the terms of this license.
|
||||
721
README.md
@@ -1,108 +1,667 @@
|
||||
# Welcome to the AI-on-the-edge-device
|
||||
<img src="images/icon/watermeter.svg" width="100px">
|
||||
<h1 align="center">AI on the Edge Device: Digitizing Your non-digital meters with an ESP32-CAM</h1>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
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**.
|
||||
## Table of Contents
|
||||
- [Key Features 🚀](#key-features-)
|
||||
- [Workflow 🔧](#workflow-)
|
||||
- [Impressions 📷](#impressions-)
|
||||
- [AI-on-the-edge-device on a Water Meter 💧](#ai-on-the-edge-device-on-a-water-meter-)
|
||||
- [Web Interface (Water Meter) 💻](#web-interface-water-meter-)
|
||||
- [AI-on-the-edge-device on an Electrical Power Meter ⚡](#ai-on-the-edge-device-on-an-electrical-power-meter-)
|
||||
- [Setup 🛠️](#setup-%EF%B8%8F)
|
||||
- [Download 🔽](#download-)
|
||||
- [Flashing the ESP32 💾](#flashing-the-esp32-)
|
||||
- [Flashing the SD Card 💾](#flashing-the-sd-card-)
|
||||
- [Casing 🛠️](#casing-%EF%B8%8F)
|
||||
- [Donate ☕](#donate-)
|
||||
- [Support 💬](#support-)
|
||||
- [Changes and History 📜](#changes-and-history-)
|
||||
- [Build It Yourself 🔨](#build-it-yourself-)
|
||||
- [Tools 🛠️](#tools-%EF%B8%8F)
|
||||
- [Additional Ideas 💡](#additional-ideas-)
|
||||
- [Our Contributors ❤️](#our-contributors-%EF%B8%8F)
|
||||
|
||||
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 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 (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 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 (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 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">
|
||||
|
||||
## Impressions
|
||||
### AI-on-the-edge-device on a Water Meter
|
||||
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/watermeter_all.jpg" width="200"><img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/main.jpg" width="200"><img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/size.png" width="200">
|
||||
|
||||
### Web Interface (Water Meter)
|
||||
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/watermeter.jpg" width="600">
|
||||
|
||||
### AI-on-the-edge-device on a Electrical Power Meter
|
||||
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/powermeter.jpg" width="600">
|
||||
<p align="center">
|
||||
<a href="#top">
|
||||
<img src="https://img.shields.io/badge/Back%20to%20Top-000000?style=for-the-badge&logo=github&logoColor=white" alt="Back to Top">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
## Setup
|
||||
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 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)
|
||||
|
||||
A lot of people created useful Youtube videos which might help you getting started.
|
||||
Here a small selection:
|
||||
[](https://github.com/jomjol/AI-on-the-edge-device/tree/main/code)
|
||||
[](https://jomjol.github.io/AI-on-the-edge-device-docs/)
|
||||
[](https://GitHub.com/jomjol/AI-on-the-edge-device/releases/)
|
||||
[](https://GitHub.com/jomjol/AI-on-the-edge-device/releases/)
|
||||
[](https://GitHub.com/jomjol/AI-on-the-edge-device/network/)
|
||||
[](https://GitHub.com/jomjol/AI-on-the-edge-device/stargazers/)
|
||||
|
||||
- [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)
|
||||
<p align="center" id="top">
|
||||
<img src="images/icon/watermeter.svg" width="150px">
|
||||
</p>
|
||||
|
||||
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).
|
||||
Artificial intelligence is everywhere, from speech to image recognition. While most AI systems rely on powerful processors or cloud computing, **edge computing** brings AI closer to the end user by utilizing the capabilities of modern processors.
|
||||
This project demonstrates edge computing using the **ESP32**, a low-cost, AI-capable device, to digitize your analog meters—whether water, gas, or electricity. With affordable hardware and simple instructions, you can turn any standard meter into a smart device.
|
||||
|
||||
### Download
|
||||
Let's explore how to make **AI on the Edge** a reality! 🌟
|
||||
|
||||
All you need is an [ESP32 board with a supported camera](https://jomjol.github.io/AI-on-the-edge-device-docs/Hardware-Compatibility/) and some practical skills. 🛠️
|
||||
|
||||
---
|
||||
|
||||
<br>
|
||||
|
||||
## Key Features 🚀
|
||||
- 🔗 **Tensorflow Lite (TFLite) integration** – including an easy-to-use wrapper.
|
||||
- 📸 **Inline image processing** (feature detection, alignment, ROI extraction).
|
||||
- 💡 **Small** and **affordable** device (3 x 4.5 x 2 cm³, less than 10 EUR).
|
||||
- 📷 Integrated camera and illumination.
|
||||
- 🌐 Web interface for administration and control.
|
||||
- 🔄 OTA interface for updating directly via the web interface.
|
||||
- 🏠 Full integration with Home Assistant.
|
||||
- 📊 Support for **Influx DB 1** and **2**.
|
||||
- 📡 **MQTT protocol** support.
|
||||
- 📥 **REST API** available for data access.
|
||||
|
||||
<br>
|
||||
|
||||
## Workflow 🔧
|
||||
The device captures a photo of your meter at set intervals. 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 for what to do with that value:
|
||||
- 📤 Send it to a **MQTT broker**.
|
||||
- 📝 Write it to an **InfluxDb**.
|
||||
- 🔗 Provide access via a **REST API**.
|
||||
|
||||
<p align="center">
|
||||
<img src="images/idea.jpg" width="600">
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
<br>
|
||||
|
||||
## Impressions 📷
|
||||
|
||||
+ ### AI-on-the-edge-device on a Water Meter 💧
|
||||
<p align="center">
|
||||
<img src="images/watermeter_all.jpg" width="200"><img
|
||||
src="images/main.jpg" width="200"><img
|
||||
src="images/size.png" width="200">
|
||||
</p>
|
||||
|
||||
+ ### Web Interface (Water Meter) 💻
|
||||
<p align="center">
|
||||
<img src="images/watermeter.jpg" width="600">
|
||||
</p>
|
||||
|
||||
+ ### AI-on-the-edge-device on an Electrical Power Meter ⚡
|
||||
<p align="center">
|
||||
<img src="images/powermeter.jpg" width="600">
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
<br>
|
||||
|
||||
## Setup 🛠️
|
||||
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 started, set it up, and configure it.
|
||||
|
||||
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) 📰
|
||||
|
||||
A lot of people have created useful YouTube videos that might help you get started:
|
||||
- 🎥 [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)
|
||||
- [Programming on the ESP32](https://www.heise.de/select/make/2022/2/2204010051597422030)
|
||||
|
||||
---
|
||||
|
||||
<br>
|
||||
|
||||
## Download 🔽
|
||||
The latest available version can be found on the [Releases page](https://github.com/jomjol/AI-on-the-edge-device/releases).
|
||||
|
||||
### 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).
|
||||
---
|
||||
|
||||
<br>
|
||||
|
||||
## 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 Wi-Fi).
|
||||
|
||||
There are different ways to flash your ESP32:
|
||||
- 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:
|
||||

|
||||
- The preferred way is the [Web Installer and Console](https://jomjol.github.io/AI-on-the-edge-device/index.html), a browser-based tool to flash the ESP32 and extract the log over USB:
|
||||

|
||||
- Flash Tool from Espressif
|
||||
- ESPtool (command-line tool)
|
||||
|
||||
See the [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/) for more information.
|
||||
|
||||
### 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
|
||||
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)
|
||||
<br>
|
||||
|
||||
## 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).
|
||||
## Flashing the SD Card 💾
|
||||
The SD card can be set up automatically after the firmware is 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 formatted (which is the default on a new SD card).
|
||||
|
||||
<a href="https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL"><img border="0" src="images/paypal.png" width="200px" target="_blank"></a>
|
||||
Alternatively, the SD card can still be set up manually. See the [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#3-sd-card) for details.
|
||||
|
||||
## 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">
|
||||
<br>
|
||||
|
||||
## Changes and History
|
||||
See [Changelog](Changelog.md).
|
||||
## Casing 🛠️
|
||||
Various 3D-printable housings can be found here:
|
||||
- 💧 [Water Meter](https://www.thingiverse.com/thing:4573481)
|
||||
- ⚡ [Power Meter](https://www.thingiverse.com/thing:5028229)
|
||||
- 🔥 [Gas Meter](https://www.thingiverse.com/thing:5224101)
|
||||
- 📷 [ESP32-cam housing only](https://www.thingiverse.com/thing:4571627)
|
||||
|
||||
## 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 [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/outdated--Gasmeter-Log-Downloader/)
|
||||
<br>
|
||||
|
||||
## Additional Ideas
|
||||
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).
|
||||
## Donate ☕
|
||||
If you'd like to support the developer with a cup of coffee, you can do so via [PayPal](https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL).
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL"><img border="0" src="images/paypal.png" width="200px" target="_blank"></a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
<br>
|
||||
|
||||
## Support 💬
|
||||
If you have any technical problems, please search the [discussions](https://github.com/jomjol/AI-on-the-edge-device/discussions). In case you find a bug or have a feature request, please open an [issue](https://github.com/jomjol/AI-on-the-edge-device/issues).
|
||||
|
||||
For any other issues, you can contact the developer via email:
|
||||
<p align="center">
|
||||
<img src="images/mail.jpg" height="25">
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
<br>
|
||||
|
||||
## Changes and History 📜
|
||||
See the [Changelog](Changelog.md) for detailed information.
|
||||
|
||||
---
|
||||
|
||||
<br>
|
||||
|
||||
## Build It Yourself 🔨
|
||||
See the [Build Instructions](code/README.md) for step-by-step guidance.
|
||||
|
||||
---
|
||||
|
||||
<br>
|
||||
|
||||
## Tools 🛠️
|
||||
* Logfile downloader and combiner (Thanks to [reserve85](https://github.com/reserve85))
|
||||
* It can be found at ['/tools/logfile-tool'](https://github.com/jomjol/AI-on-the-edge-device/tree/main/tools/logfile-tool).
|
||||
|
||||
---
|
||||
|
||||
<br>
|
||||
|
||||
## Additional Ideas 💡
|
||||
There are some ideas and feature requests which are not currently being pursued—mainly due to capacity constraints on the part of the developers. These features are collected in the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues) and in [FeatureRequest.md](FeatureRequest.md).
|
||||
|
||||
---
|
||||
|
||||
<br>
|
||||
|
||||
## Our Contributors ❤️
|
||||
<!-- Do not manually edit this section! It should get updated using the Github action "Manually update contributors list" -->
|
||||
<!-- readme: contributors -start -->
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/jomjol">
|
||||
<img src="https://avatars.githubusercontent.com/u/30766535?v=4" width="100;" alt="jomjol"/>
|
||||
<br />
|
||||
<sub><b>jomjol</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/caco3">
|
||||
<img src="https://avatars.githubusercontent.com/u/1783586?v=4" width="100;" alt="caco3"/>
|
||||
<br />
|
||||
<sub><b>CaCO3</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/haverland">
|
||||
<img src="https://avatars.githubusercontent.com/u/412645?v=4" width="100;" alt="haverland"/>
|
||||
<br />
|
||||
<sub><b>Frank Haverland</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Slider0007">
|
||||
<img src="https://avatars.githubusercontent.com/u/115730895?v=4" width="100;" alt="Slider0007"/>
|
||||
<br />
|
||||
<sub><b>Slider0007</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/SybexX">
|
||||
<img src="https://avatars.githubusercontent.com/u/587201?v=4" width="100;" alt="SybexX"/>
|
||||
<br />
|
||||
<sub><b>michael</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/nliaudat">
|
||||
<img src="https://avatars.githubusercontent.com/u/6782613?v=4" width="100;" alt="nliaudat"/>
|
||||
<br />
|
||||
<sub><b>Nicolas Liaudat</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Zwer2k">
|
||||
<img src="https://avatars.githubusercontent.com/u/10438794?v=4" width="100;" alt="Zwer2k"/>
|
||||
<br />
|
||||
<sub><b>Zwer2k</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/phlupp">
|
||||
<img src="https://avatars.githubusercontent.com/u/6304863?v=4" width="100;" alt="phlupp"/>
|
||||
<br />
|
||||
<sub><b>phlupp</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/jasaw">
|
||||
<img src="https://avatars.githubusercontent.com/u/721280?v=4" width="100;" alt="jasaw"/>
|
||||
<br />
|
||||
<sub><b>jasaw</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/dockSquadron">
|
||||
<img src="https://avatars.githubusercontent.com/u/11964767?v=4" width="100;" alt="dockSquadron"/>
|
||||
<br />
|
||||
<sub><b>dockSquadron</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/rdmueller">
|
||||
<img src="https://avatars.githubusercontent.com/u/1856308?v=4" width="100;" alt="rdmueller"/>
|
||||
<br />
|
||||
<sub><b>Ralf D. Müller</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/cristianmitran">
|
||||
<img src="https://avatars.githubusercontent.com/u/36613624?v=4" width="100;" alt="cristianmitran"/>
|
||||
<br />
|
||||
<sub><b>cristianmitran</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/michaeljoos72">
|
||||
<img src="https://avatars.githubusercontent.com/u/20517474?v=4" width="100;" alt="michaeljoos72"/>
|
||||
<br />
|
||||
<sub><b>michaeljoos72</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/henrythasler">
|
||||
<img src="https://avatars.githubusercontent.com/u/6277203?v=4" width="100;" alt="henrythasler"/>
|
||||
<br />
|
||||
<sub><b>Henry Thasler</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/amantyagiprojects">
|
||||
<img src="https://avatars.githubusercontent.com/u/174239452?v=4" width="100;" alt="amantyagiprojects"/>
|
||||
<br />
|
||||
<sub><b>Naman Tyagi</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/pixeldoc2000">
|
||||
<img src="https://avatars.githubusercontent.com/u/376715?v=4" width="100;" alt="pixeldoc2000"/>
|
||||
<br />
|
||||
<sub><b>pixel::doc</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/mad2xlc">
|
||||
<img src="https://avatars.githubusercontent.com/u/37449746?v=4" width="100;" alt="mad2xlc"/>
|
||||
<br />
|
||||
<sub><b>Stefan</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/jochenchrist">
|
||||
<img src="https://avatars.githubusercontent.com/u/2930448?v=4" width="100;" alt="jochenchrist"/>
|
||||
<br />
|
||||
<sub><b>jochenchrist</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/parhedberg">
|
||||
<img src="https://avatars.githubusercontent.com/u/13777521?v=4" width="100;" alt="parhedberg"/>
|
||||
<br />
|
||||
<sub><b>parhedberg</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/slovdahl">
|
||||
<img src="https://avatars.githubusercontent.com/u/1417619?v=4" width="100;" alt="slovdahl"/>
|
||||
<br />
|
||||
<sub><b>Sebastian Lövdahl</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/RaHehl">
|
||||
<img src="https://avatars.githubusercontent.com/u/7577984?v=4" width="100;" alt="RaHehl"/>
|
||||
<br />
|
||||
<sub><b>Raphael Hehl</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/LordGuilly">
|
||||
<img src="https://avatars.githubusercontent.com/u/13271835?v=4" width="100;" alt="LordGuilly"/>
|
||||
<br />
|
||||
<sub><b>LordGuilly</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/bilalmirza74">
|
||||
<img src="https://avatars.githubusercontent.com/u/84387676?v=4" width="100;" alt="bilalmirza74"/>
|
||||
<br />
|
||||
<sub><b>Bilal Mirza</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/muggenhor">
|
||||
<img src="https://avatars.githubusercontent.com/u/484066?v=4" width="100;" alt="muggenhor"/>
|
||||
<br />
|
||||
<sub><b>Giel van Schijndel</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/ppisljar">
|
||||
<img src="https://avatars.githubusercontent.com/u/13629809?v=4" width="100;" alt="ppisljar"/>
|
||||
<br />
|
||||
<sub><b>Peter Pisljar</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/ralf1307">
|
||||
<img src="https://avatars.githubusercontent.com/u/46164027?v=4" width="100;" alt="ralf1307"/>
|
||||
<br />
|
||||
<sub><b>Ralf Rachinger</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Ranjana761">
|
||||
<img src="https://avatars.githubusercontent.com/u/129291313?v=4" width="100;" alt="Ranjana761"/>
|
||||
<br />
|
||||
<sub><b>Ranjana761</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/SkylightXD">
|
||||
<img src="https://avatars.githubusercontent.com/u/16561545?v=4" width="100;" alt="SkylightXD"/>
|
||||
<br />
|
||||
<sub><b>SkylightXD</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/ottk3">
|
||||
<img src="https://avatars.githubusercontent.com/u/5236802?v=4" width="100;" alt="ottk3"/>
|
||||
<br />
|
||||
<sub><b>Sven Rojek</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Turbo87">
|
||||
<img src="https://avatars.githubusercontent.com/u/141300?v=4" width="100;" alt="Turbo87"/>
|
||||
<br />
|
||||
<sub><b>Tobias Bieniek</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/tkopczuk">
|
||||
<img src="https://avatars.githubusercontent.com/u/101632?v=4" width="100;" alt="tkopczuk"/>
|
||||
<br />
|
||||
<sub><b>Tomek Kopczuk</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/yonz2">
|
||||
<img src="https://avatars.githubusercontent.com/u/13886257?v=4" width="100;" alt="yonz2"/>
|
||||
<br />
|
||||
<sub><b>Yonz</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Yveaux">
|
||||
<img src="https://avatars.githubusercontent.com/u/7716005?v=4" width="100;" alt="Yveaux"/>
|
||||
<br />
|
||||
<sub><b>Yveaux</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/flooxo">
|
||||
<img src="https://avatars.githubusercontent.com/u/93255373?v=4" width="100;" alt="flooxo"/>
|
||||
<br />
|
||||
<sub><b>flox_x</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/gneluka">
|
||||
<img src="https://avatars.githubusercontent.com/u/32097881?v=4" width="100;" alt="gneluka"/>
|
||||
<br />
|
||||
<sub><b>gneluka</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/kalwados">
|
||||
<img src="https://avatars.githubusercontent.com/u/11840444?v=4" width="100;" alt="kalwados"/>
|
||||
<br />
|
||||
<sub><b>kalwados</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/kub3let">
|
||||
<img src="https://avatars.githubusercontent.com/u/95883234?v=4" width="100;" alt="kub3let"/>
|
||||
<br />
|
||||
<sub><b>kub3let</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/pfeifferch">
|
||||
<img src="https://avatars.githubusercontent.com/u/73090220?v=4" width="100;" alt="pfeifferch"/>
|
||||
<br />
|
||||
<sub><b>pfeifferch</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/rstephan">
|
||||
<img src="https://avatars.githubusercontent.com/u/8532364?v=4" width="100;" alt="rstephan"/>
|
||||
<br />
|
||||
<sub><b>rstephan</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/smartboart">
|
||||
<img src="https://avatars.githubusercontent.com/u/38385805?v=4" width="100;" alt="smartboart"/>
|
||||
<br />
|
||||
<sub><b>smartboart</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/AngryApostrophe">
|
||||
<img src="https://avatars.githubusercontent.com/u/89547888?v=4" width="100;" alt="AngryApostrophe"/>
|
||||
<br />
|
||||
<sub><b>AngryApostrophe</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/wetneb">
|
||||
<img src="https://avatars.githubusercontent.com/u/309908?v=4" width="100;" alt="wetneb"/>
|
||||
<br />
|
||||
<sub><b>Antonin Delpeuch</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/adarazs">
|
||||
<img src="https://avatars.githubusercontent.com/u/6269603?v=4" width="100;" alt="adarazs"/>
|
||||
<br />
|
||||
<sub><b>Attila Darazs</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/austindrenski">
|
||||
<img src="https://avatars.githubusercontent.com/u/21338699?v=4" width="100;" alt="austindrenski"/>
|
||||
<br />
|
||||
<sub><b>Austin Drenski</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/PLCHome">
|
||||
<img src="https://avatars.githubusercontent.com/u/29116097?v=4" width="100;" alt="PLCHome"/>
|
||||
<br />
|
||||
<sub><b>PLCHome</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/CFenner">
|
||||
<img src="https://avatars.githubusercontent.com/u/9592452?v=4" width="100;" alt="CFenner"/>
|
||||
<br />
|
||||
<sub><b>Christopher Fenner</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/dkneisz">
|
||||
<img src="https://avatars.githubusercontent.com/u/43378003?v=4" width="100;" alt="dkneisz"/>
|
||||
<br />
|
||||
<sub><b>Dave</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/FarukhS52">
|
||||
<img src="https://avatars.githubusercontent.com/u/129654632?v=4" width="100;" alt="FarukhS52"/>
|
||||
<br />
|
||||
<sub><b>Farookh Zaheer Siddiqui</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/hex7c0">
|
||||
<img src="https://avatars.githubusercontent.com/u/4419146?v=4" width="100;" alt="hex7c0"/>
|
||||
<br />
|
||||
<sub><b>Francesco Carnielli</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/040medien">
|
||||
<img src="https://avatars.githubusercontent.com/u/115072?v=4" width="100;" alt="040medien"/>
|
||||
<br />
|
||||
<sub><b>Frederik Kemner</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/eltociear">
|
||||
<img src="https://avatars.githubusercontent.com/u/22633385?v=4" width="100;" alt="eltociear"/>
|
||||
<br />
|
||||
<sub><b>Ikko Eltociear Ashimine</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/queeek">
|
||||
<img src="https://avatars.githubusercontent.com/u/9533371?v=4" width="100;" alt="queeek"/>
|
||||
<br />
|
||||
<sub><b>Ina</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/joergrosenkranz">
|
||||
<img src="https://avatars.githubusercontent.com/u/310438?v=4" width="100;" alt="joergrosenkranz"/>
|
||||
<br />
|
||||
<sub><b>Joerg Rosenkranz</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Innovatorcloudy">
|
||||
<img src="https://avatars.githubusercontent.com/u/183274513?v=4" width="100;" alt="Innovatorcloudy"/>
|
||||
<br />
|
||||
<sub><b>KrishCode</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/myxor">
|
||||
<img src="https://avatars.githubusercontent.com/u/1397377?v=4" width="100;" alt="myxor"/>
|
||||
<br />
|
||||
<sub><b>Marco H</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/rainman110">
|
||||
<img src="https://avatars.githubusercontent.com/u/3213107?v=4" width="100;" alt="rainman110"/>
|
||||
<br />
|
||||
<sub><b>Martin Siggel</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/mkelley88">
|
||||
<img src="https://avatars.githubusercontent.com/u/5567324?v=4" width="100;" alt="mkelley88"/>
|
||||
<br />
|
||||
<sub><b>Matthew T. Kelley</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/toolsfactory">
|
||||
<img src="https://avatars.githubusercontent.com/u/7744975?v=4" width="100;" alt="toolsfactory"/>
|
||||
<br />
|
||||
<sub><b>Michael Geissler</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tbody>
|
||||
</table>
|
||||
<!-- readme: contributors -end -->
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<a href="#top">
|
||||
<img src="https://img.shields.io/badge/Back%20to%20Top-000000?style=for-the-badge&logo=github&logoColor=white" alt="Back to Top">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -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/esp-tflite-micro)
|
||||
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common components/esp-tflite-micro esp-protocols/components/mdns)
|
||||
|
||||
ADD_CUSTOM_COMMAND(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.cpp
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "server_mqtt.h"
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
#include "basic_auth.h"
|
||||
|
||||
static const char *TAG = "GPIO";
|
||||
QueueHandle_t gpio_queue_handle = NULL;
|
||||
@@ -458,7 +459,7 @@ void GpioHandler::registerGpioUri()
|
||||
httpd_uri_t camuri = { };
|
||||
camuri.method = HTTP_GET;
|
||||
camuri.uri = "/GPIO";
|
||||
camuri.handler = callHandleHttpRequest;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(callHandleHttpRequest);
|
||||
camuri.user_ctx = (void*)this;
|
||||
httpd_register_uri_handler(_httpServer, &camuri);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
#include "MainFlowControl.h"
|
||||
|
||||
#include "ov2640_sharpness.h"
|
||||
#include "ov2640_specialEffect.h"
|
||||
#include "ov2640_contrast_brightness.h"
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 5)
|
||||
#include "soc/periph_defs.h"
|
||||
@@ -96,7 +98,7 @@ static camera_config_t camera_config = {
|
||||
.pixel_format = PIXFORMAT_JPEG, // YUV422,GRAYSCALE,RGB565,JPEG
|
||||
.frame_size = FRAMESIZE_VGA, // QQVGA-UXGA Do not use sizes above QVGA when not JPEG
|
||||
// .frame_size = FRAMESIZE_UXGA, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
|
||||
.jpeg_quality = 6, // 0-63 lower number means higher quality
|
||||
.jpeg_quality = 12, // 0-63 lower number means higher quality
|
||||
.fb_count = 1, // if more than one, i2s runs in continuous mode. Use only with JPEG
|
||||
.fb_location = CAMERA_FB_IN_PSRAM, /*!< The location where the frame buffer will be allocated */
|
||||
.grab_mode = CAMERA_GRAB_LATEST, // only from new esp32cam version
|
||||
@@ -122,12 +124,18 @@ esp_err_t CCamera::InitCam(void)
|
||||
{
|
||||
ESP_LOGD(TAG, "Init Camera");
|
||||
|
||||
TickType_t cam_xDelay = 100 / portTICK_PERIOD_MS;
|
||||
|
||||
CCstatus.ImageQuality = camera_config.jpeg_quality;
|
||||
CCstatus.ImageFrameSize = camera_config.frame_size;
|
||||
|
||||
// De-init in case it was already initialized
|
||||
esp_camera_deinit();
|
||||
vTaskDelay(cam_xDelay);
|
||||
|
||||
// initialize the camera
|
||||
esp_camera_deinit(); // De-init in case it was already initialized
|
||||
esp_err_t err = esp_camera_init(&camera_config);
|
||||
vTaskDelay(cam_xDelay);
|
||||
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
@@ -221,13 +229,12 @@ void CCamera::ledc_init(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
void CCamera::SetLEDIntensity(float _intrel)
|
||||
int CCamera::SetLEDIntensity(int _intrel)
|
||||
{
|
||||
_intrel = min(_intrel, (float)100);
|
||||
_intrel = max(_intrel, (float)0);
|
||||
_intrel = _intrel / 100;
|
||||
CCstatus.ImageLedIntensity = (int)(_intrel * 8191);
|
||||
ESP_LOGD(TAG, "Set led_intensity to %d of 8191", CCstatus.ImageLedIntensity);
|
||||
// CCstatus.ImageLedIntensity = (int)(std::min(std::max((float)0, _intrel), (float)100) / 100 * 8191)
|
||||
Camera.LedIntensity = (int)((float)(std::min(std::max(0, _intrel), 100)) / 100 * 8191);
|
||||
ESP_LOGD(TAG, "Set led_intensity to %i of 8191", Camera.LedIntensity);
|
||||
return Camera.LedIntensity;
|
||||
}
|
||||
|
||||
bool CCamera::getCameraInitSuccessful(void)
|
||||
@@ -242,46 +249,46 @@ esp_err_t CCamera::setSensorDatenFromCCstatus(void)
|
||||
if (s != NULL)
|
||||
{
|
||||
s->set_framesize(s, CCstatus.ImageFrameSize);
|
||||
s->set_quality(s, CCstatus.ImageQuality); // 0 - 63
|
||||
|
||||
s->set_brightness(s, CCstatus.ImageBrightness); // -2 to 2
|
||||
s->set_contrast(s, CCstatus.ImageContrast); // -2 to 2
|
||||
|
||||
// s->set_contrast(s, CCstatus.ImageContrast); // -2 to 2
|
||||
// s->set_brightness(s, CCstatus.ImageBrightness); // -2 to 2
|
||||
SetCamContrastBrightness(s, CCstatus.ImageContrast, CCstatus.ImageBrightness);
|
||||
|
||||
s->set_saturation(s, CCstatus.ImageSaturation); // -2 to 2
|
||||
// s->set_sharpness(s, CCstatus.ImageSharpness); // auto-sharpness is not officially supported, default to 0
|
||||
SetCamSharpness(CCstatus.ImageAutoSharpness, CCstatus.ImageSharpness);
|
||||
|
||||
s->set_denoise(s, CCstatus.ImageDenoiseLevel); // The OV2640 does not support it, OV3660 and OV5640 (0 to 8)
|
||||
|
||||
s->set_special_effect(s, CCstatus.ImageSpecialEffect); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
|
||||
s->set_wb_mode(s, CCstatus.ImageWbMode); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
|
||||
|
||||
s->set_ae_level(s, CCstatus.ImageAeLevel); // -2 to 2
|
||||
s->set_aec_value(s, CCstatus.ImageAecValue); // 0 to 1200
|
||||
s->set_agc_gain(s, CCstatus.ImageAgcGain); // 0 to 30
|
||||
|
||||
s->set_quality(s, CCstatus.ImageQuality); // 0 - 63
|
||||
|
||||
// s->set_gainceiling(s, CCstatus.ImageGainceiling); // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128)
|
||||
ov5640_set_gainceiling(s, CCstatus.ImageGainceiling);
|
||||
|
||||
s->set_lenc(s, CCstatus.ImageLenc); // 0 = disable , 1 = enable
|
||||
SetCamGainceiling(s, CCstatus.ImageGainceiling);
|
||||
|
||||
s->set_gain_ctrl(s, CCstatus.ImageAgc); // 0 = disable , 1 = enable
|
||||
s->set_exposure_ctrl(s, CCstatus.ImageAec); // 0 = disable , 1 = enable
|
||||
|
||||
s->set_hmirror(s, CCstatus.ImageHmirror); // 0 = disable , 1 = enable
|
||||
s->set_vflip(s, CCstatus.ImageVflip); // 0 = disable , 1 = enable
|
||||
|
||||
s->set_whitebal(s, CCstatus.ImageAwb); // 0 = disable , 1 = enable
|
||||
s->set_aec2(s, CCstatus.ImageAec2); // 0 = disable , 1 = enable
|
||||
|
||||
s->set_aec_value(s, CCstatus.ImageAecValue); // 0 to 1200
|
||||
// s->set_special_effect(s, CCstatus.ImageSpecialEffect); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
|
||||
SetCamSpecialEffect(s, CCstatus.ImageSpecialEffect);
|
||||
s->set_wb_mode(s, CCstatus.ImageWbMode); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
|
||||
s->set_ae_level(s, CCstatus.ImageAeLevel); // -2 to 2
|
||||
|
||||
s->set_dcw(s, CCstatus.ImageDcw); // 0 = disable , 1 = enable
|
||||
s->set_bpc(s, CCstatus.ImageBpc); // 0 = disable , 1 = enable
|
||||
s->set_wpc(s, CCstatus.ImageWpc); // 0 = disable , 1 = enable
|
||||
|
||||
s->set_raw_gma(s, CCstatus.ImageRawGma); // 0 = disable , 1 = enable
|
||||
|
||||
s->set_awb_gain(s, CCstatus.ImageAwbGain); // 0 = disable , 1 = enable
|
||||
s->set_whitebal(s, CCstatus.ImageAwb); // 0 = disable , 1 = enable
|
||||
s->set_agc_gain(s, CCstatus.ImageAgcGain); // 0 to 30
|
||||
|
||||
s->set_raw_gma(s, CCstatus.ImageRawGma); // 0 = disable , 1 = enable
|
||||
s->set_lenc(s, CCstatus.ImageLenc); // 0 = disable , 1 = enable
|
||||
|
||||
s->set_dcw(s, CCstatus.ImageDcw); // 0 = disable , 1 = enable
|
||||
// s->set_sharpness(s, CCstatus.ImageSharpness); // auto-sharpness is not officially supported, default to 0
|
||||
SetCamSharpness(CCstatus.ImageAutoSharpness, CCstatus.ImageSharpness);
|
||||
s->set_denoise(s, CCstatus.ImageDenoiseLevel); // The OV2640 does not support it, OV3660 and OV5640 (0 to 8)
|
||||
|
||||
TickType_t xDelay2 = 100 / portTICK_PERIOD_MS;
|
||||
vTaskDelay(xDelay2);
|
||||
TickType_t cam_xDelay = 100 / portTICK_PERIOD_MS;
|
||||
vTaskDelay(cam_xDelay);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -300,30 +307,37 @@ esp_err_t CCamera::getSensorDatenToCCstatus(void)
|
||||
CCstatus.CamSensor_id = s->id.PID;
|
||||
|
||||
CCstatus.ImageFrameSize = (framesize_t)s->status.framesize;
|
||||
|
||||
CCstatus.ImageContrast = s->status.contrast;
|
||||
CCstatus.ImageBrightness = s->status.brightness;
|
||||
CCstatus.ImageSaturation = s->status.saturation;
|
||||
|
||||
CCstatus.ImageQuality = s->status.quality;
|
||||
|
||||
CCstatus.ImageGainceiling = (gainceiling_t)s->status.gainceiling;
|
||||
|
||||
CCstatus.ImageQuality = s->status.quality;
|
||||
CCstatus.ImageBrightness = s->status.brightness;
|
||||
CCstatus.ImageContrast = s->status.contrast;
|
||||
CCstatus.ImageSaturation = s->status.saturation;
|
||||
// CCstatus.ImageSharpness = s->status.sharpness; // gibt -1 zurück, da es nicht unterstützt wird
|
||||
CCstatus.ImageWbMode = s->status.wb_mode;
|
||||
CCstatus.ImageAwb = s->status.awb;
|
||||
CCstatus.ImageAwbGain = s->status.awb_gain;
|
||||
CCstatus.ImageAec = s->status.aec;
|
||||
CCstatus.ImageAec2 = s->status.aec2;
|
||||
CCstatus.ImageAeLevel = s->status.ae_level;
|
||||
CCstatus.ImageAecValue = s->status.aec_value;
|
||||
CCstatus.ImageAgc = s->status.agc;
|
||||
CCstatus.ImageAgcGain = s->status.agc_gain;
|
||||
CCstatus.ImageBpc = s->status.bpc;
|
||||
CCstatus.ImageWpc = s->status.wpc;
|
||||
CCstatus.ImageRawGma = s->status.raw_gma;
|
||||
CCstatus.ImageLenc = s->status.lenc;
|
||||
CCstatus.ImageSpecialEffect = s->status.special_effect;
|
||||
CCstatus.ImageAec = s->status.aec;
|
||||
CCstatus.ImageHmirror = s->status.hmirror;
|
||||
CCstatus.ImageVflip = s->status.vflip;
|
||||
|
||||
CCstatus.ImageAwb = s->status.awb;
|
||||
CCstatus.ImageAec2 = s->status.aec2;
|
||||
CCstatus.ImageAecValue = s->status.aec_value;
|
||||
CCstatus.ImageSpecialEffect = s->status.special_effect;
|
||||
CCstatus.ImageWbMode = s->status.wb_mode;
|
||||
CCstatus.ImageAeLevel = s->status.ae_level;
|
||||
|
||||
CCstatus.ImageDcw = s->status.dcw;
|
||||
CCstatus.ImageBpc = s->status.bpc;
|
||||
CCstatus.ImageWpc = s->status.wpc;
|
||||
CCstatus.ImageAwbGain = s->status.awb_gain;
|
||||
CCstatus.ImageAgcGain = s->status.agc_gain;
|
||||
|
||||
CCstatus.ImageRawGma = s->status.raw_gma;
|
||||
CCstatus.ImageLenc = s->status.lenc;
|
||||
|
||||
// CCstatus.ImageSharpness = s->status.sharpness; // gibt -1 zurück, da es nicht unterstützt wird
|
||||
CCstatus.ImageDenoiseLevel = s->status.denoise;
|
||||
|
||||
return ESP_OK;
|
||||
@@ -334,31 +348,96 @@ esp_err_t CCamera::getSensorDatenToCCstatus(void)
|
||||
}
|
||||
}
|
||||
|
||||
// on the OV5640, gainceiling must be set with the real value (x2>>>level = 2, .... x128>>>level = 128)
|
||||
int CCamera::ov5640_set_gainceiling(sensor_t *s, gainceiling_t level)
|
||||
// on the OV5640, gainceiling must be set with the real value (x2>>>gainceilingLevel = 2, .... x128>>>gainceilingLevel = 128)
|
||||
int CCamera::SetCamGainceiling(sensor_t *s, gainceiling_t gainceilingLevel)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (CCstatus.CamSensor_id == OV2640_PID)
|
||||
{
|
||||
ret = s->set_gainceiling(s, CCstatus.ImageGainceiling); // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128)
|
||||
ret = s->set_gainceiling(s, gainceilingLevel); // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128)
|
||||
}
|
||||
else
|
||||
{
|
||||
int _level = (1 << ((int)level + 1));
|
||||
int _level = (1 << ((int)gainceilingLevel + 1));
|
||||
|
||||
ret = s->set_reg(s, 0x3A18, 0xFF, (_level >> 8) & 3) || s->set_reg(s, 0x3A19, 0xFF, _level & 0xFF);
|
||||
ret = s->set_reg(s, 0x3A18, 0xFF, (_level >> 8) & 3) || s->set_reg(s, 0x3A19, 0xFF, _level & 0xFF);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
// ESP_LOGD(TAG, "Set gainceiling to: %d", level);
|
||||
s->status.gainceiling = level;
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
// ESP_LOGD(TAG, "Set gainceiling to: %d", gainceilingLevel);
|
||||
s->status.gainceiling = gainceilingLevel;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CCamera::SetCamSharpness(bool autoSharpnessEnabled, int sharpnessLevel)
|
||||
{
|
||||
sensor_t *s = esp_camera_sensor_get();
|
||||
|
||||
if (s != NULL)
|
||||
{
|
||||
if (CCstatus.CamSensor_id == OV2640_PID)
|
||||
{
|
||||
sharpnessLevel = min(2, max(-2, sharpnessLevel));
|
||||
// The OV2640 does not officially support sharpness, so the detour is made with the ov2640_sharpness.cpp.
|
||||
if (autoSharpnessEnabled)
|
||||
{
|
||||
ov2640_enable_auto_sharpness(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
ov2640_set_sharpness(s, sharpnessLevel);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sharpnessLevel = min(3, max(-3, sharpnessLevel));
|
||||
// for CAMERA_OV5640 and CAMERA_OV3660
|
||||
if (autoSharpnessEnabled)
|
||||
{
|
||||
// autoSharpness is not supported, default to zero
|
||||
s->set_sharpness(s, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
s->set_sharpness(s, sharpnessLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SetCamSharpness, Failed to get Cam control structure");
|
||||
}
|
||||
}
|
||||
|
||||
void CCamera::SetCamSpecialEffect(sensor_t *s, int specialEffect)
|
||||
{
|
||||
if (CCstatus.CamSensor_id == OV2640_PID)
|
||||
{
|
||||
ov2640_set_special_effect(s, specialEffect);
|
||||
}
|
||||
else
|
||||
{
|
||||
s->set_special_effect(s, specialEffect);
|
||||
}
|
||||
}
|
||||
|
||||
void CCamera::SetCamContrastBrightness(sensor_t *s, int _contrast, int _brightness)
|
||||
{
|
||||
if (CCstatus.CamSensor_id == OV2640_PID)
|
||||
{
|
||||
ov2640_set_contrast_brightness(s, _contrast, _brightness);
|
||||
}
|
||||
else
|
||||
{
|
||||
s->set_contrast(s, _contrast); // -2 to 2
|
||||
s->set_brightness(s, _brightness); // -2 to 2
|
||||
}
|
||||
}
|
||||
|
||||
// - It always zooms to the image center when offsets are zero
|
||||
// - if imageSize = 0 then the image is not zoomed
|
||||
// - if imageSize = max value, then the image is fully zoomed in
|
||||
@@ -512,46 +591,6 @@ void CCamera::SetQualityZoomSize(int qual, framesize_t resol, bool zoomEnabled,
|
||||
}
|
||||
}
|
||||
|
||||
void CCamera::SetCamSharpness(bool _autoSharpnessEnabled, int _sharpnessLevel)
|
||||
{
|
||||
sensor_t *s = esp_camera_sensor_get();
|
||||
|
||||
if (s != NULL)
|
||||
{
|
||||
if (CCstatus.CamSensor_id == OV2640_PID)
|
||||
{
|
||||
_sharpnessLevel = min(2, max(-2, _sharpnessLevel));
|
||||
// The OV2640 does not officially support sharpness, so the detour is made with the ov2640_sharpness.cpp.
|
||||
if (_autoSharpnessEnabled)
|
||||
{
|
||||
ov2640_enable_auto_sharpness(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
ov2640_set_sharpness(s, _sharpnessLevel);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_sharpnessLevel = min(3, max(-3, _sharpnessLevel));
|
||||
// for CAMERA_OV5640 and CAMERA_OV3660
|
||||
if (_autoSharpnessEnabled)
|
||||
{
|
||||
// autoSharpness is not supported, default to zero
|
||||
s->set_sharpness(s, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
s->set_sharpness(s, _sharpnessLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SetCamSharpness, Failed to get Cam control structure");
|
||||
}
|
||||
}
|
||||
|
||||
void CCamera::SetCamWindow(sensor_t *s, int frameSizeX, int frameSizeY, int xOffset, int yOffset, int xTotal, int yTotal, int xOutput, int yOutput, int imageVflip)
|
||||
{
|
||||
if (CCstatus.CamSensor_id == OV2640_PID)
|
||||
@@ -904,6 +943,7 @@ esp_err_t CCamera::CaptureToStream(httpd_req_t *req, bool FlashlightOn)
|
||||
{
|
||||
Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera
|
||||
Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize, CCstatus.ImageVflip);
|
||||
Camera.LedIntensity = CCstatus.ImageLedIntensity;
|
||||
CFstatus.changedCameraSettings = false;
|
||||
}
|
||||
|
||||
@@ -994,8 +1034,8 @@ void CCamera::LightOnOff(bool status)
|
||||
#ifdef USE_PWM_LEDFLASH
|
||||
if (status)
|
||||
{
|
||||
ESP_LOGD(TAG, "Internal Flash-LED turn on with PWM %d", CCstatus.ImageLedIntensity);
|
||||
ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, CCstatus.ImageLedIntensity));
|
||||
ESP_LOGD(TAG, "Internal Flash-LED turn on with PWM %d", Camera.LedIntensity);
|
||||
ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, Camera.LedIntensity));
|
||||
// Update duty to apply the new value
|
||||
ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));
|
||||
}
|
||||
@@ -1049,38 +1089,58 @@ void CCamera::SetImageWidthHeightFromResolution(framesize_t resol)
|
||||
{
|
||||
if (resol == FRAMESIZE_QVGA)
|
||||
{
|
||||
CCstatus.ImageHeight = 240;
|
||||
CCstatus.ImageWidth = 320;
|
||||
CCstatus.ImageHeight = 240;
|
||||
}
|
||||
else if (resol == FRAMESIZE_VGA)
|
||||
{
|
||||
CCstatus.ImageHeight = 480;
|
||||
CCstatus.ImageWidth = 640;
|
||||
CCstatus.ImageHeight = 480;
|
||||
}
|
||||
else if (resol == FRAMESIZE_SVGA)
|
||||
{
|
||||
CCstatus.ImageHeight = 600;
|
||||
CCstatus.ImageWidth = 800;
|
||||
CCstatus.ImageHeight = 600;
|
||||
}
|
||||
else if (resol == FRAMESIZE_XGA)
|
||||
{
|
||||
CCstatus.ImageHeight = 768;
|
||||
CCstatus.ImageWidth = 1024;
|
||||
CCstatus.ImageHeight = 768;
|
||||
}
|
||||
else if (resol == FRAMESIZE_HD)
|
||||
{
|
||||
CCstatus.ImageHeight = 720;
|
||||
CCstatus.ImageWidth = 1280;
|
||||
CCstatus.ImageHeight = 720;
|
||||
}
|
||||
else if (resol == FRAMESIZE_SXGA)
|
||||
{
|
||||
CCstatus.ImageHeight = 1024;
|
||||
CCstatus.ImageWidth = 1280;
|
||||
CCstatus.ImageHeight = 1024;
|
||||
}
|
||||
else if (resol == FRAMESIZE_UXGA)
|
||||
{
|
||||
CCstatus.ImageHeight = 1200;
|
||||
CCstatus.ImageWidth = 1600;
|
||||
CCstatus.ImageHeight = 1200;
|
||||
}
|
||||
else if (resol == FRAMESIZE_QXGA)
|
||||
{
|
||||
CCstatus.ImageWidth = 2048;
|
||||
CCstatus.ImageHeight = 1536;
|
||||
}
|
||||
else if (resol == FRAMESIZE_WQXGA)
|
||||
{
|
||||
CCstatus.ImageWidth = 2560;
|
||||
CCstatus.ImageHeight = 1600;
|
||||
}
|
||||
else if (resol == FRAMESIZE_QSXGA)
|
||||
{
|
||||
CCstatus.ImageWidth = 2560;
|
||||
CCstatus.ImageHeight = 1920;
|
||||
}
|
||||
else
|
||||
{
|
||||
CCstatus.ImageWidth = 640;
|
||||
CCstatus.ImageHeight = 480;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1110,8 +1170,24 @@ framesize_t CCamera::TextToFramesize(const char *_size)
|
||||
{
|
||||
return FRAMESIZE_UXGA; // 1600x1200
|
||||
}
|
||||
else if (strcmp(_size, "QXGA") == 0)
|
||||
{
|
||||
return FRAMESIZE_QXGA; // 2048x1536
|
||||
}
|
||||
else if (strcmp(_size, "WQXGA") == 0)
|
||||
{
|
||||
return FRAMESIZE_WQXGA; // 2560x1600
|
||||
}
|
||||
else if (strcmp(_size, "QSXGA") == 0)
|
||||
{
|
||||
return FRAMESIZE_QSXGA; // 2560x1920
|
||||
}
|
||||
else
|
||||
{
|
||||
return FRAMESIZE_VGA; // 640x480
|
||||
}
|
||||
|
||||
return CCstatus.ImageFrameSize;
|
||||
// return CCstatus.ImageFrameSize;
|
||||
}
|
||||
|
||||
std::vector<std::string> demoFiles;
|
||||
|
||||
@@ -80,6 +80,8 @@ protected:
|
||||
void SanitizeZoomParams(int imageSize, int frameSizeX, int frameSizeY, int &imageWidth, int &imageHeight, int &zoomOffsetX, int &zoomOffsetY);
|
||||
|
||||
public:
|
||||
int LedIntensity = 4096;
|
||||
|
||||
CCamera(void);
|
||||
esp_err_t InitCam(void);
|
||||
|
||||
@@ -89,16 +91,18 @@ public:
|
||||
esp_err_t setSensorDatenFromCCstatus(void);
|
||||
esp_err_t getSensorDatenToCCstatus(void);
|
||||
|
||||
int ov5640_set_gainceiling(sensor_t *s, gainceiling_t level);
|
||||
int SetCamGainceiling(sensor_t *s, gainceiling_t gainceilingLevel);
|
||||
void SetCamSharpness(bool autoSharpnessEnabled, int sharpnessLevel);
|
||||
void SetCamSpecialEffect(sensor_t *s, int specialEffect);
|
||||
void SetCamContrastBrightness(sensor_t *s, int _contrast, int _brightness);
|
||||
|
||||
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);
|
||||
int SetLEDIntensity(int _intrel);
|
||||
bool testCamera(void);
|
||||
bool getCameraInitSuccessful(void);
|
||||
void useDemoMode(void);
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
// Workaround - bug in cam library - enable bits are set without using bitwise OR logic -> only latest enable setting is used
|
||||
// Reference: https://esp32.com/viewtopic.php?f=19&t=14376#p93178
|
||||
/* The memory structure is as follows for
|
||||
byte_0 = enable_bits
|
||||
byte_0->bit0 = enable saturation and hue --> OK
|
||||
byte_0->bit1 = enable saturation --> OK
|
||||
byte_0->bit2 = enable brightness and contrast --> OK
|
||||
byte_0->bit3 = enable green -> blue spitial effect (Antique and blunish and greenish and reddish and b&w) enable
|
||||
byte_0->bit4 = anable gray -> red spitial effect (Antique and blunish and greenish and reddish and b&w) enable
|
||||
byte_0->bit5 = remove (UV) in YUV color system
|
||||
byte_0->bit6 = enable negative
|
||||
byte_0->bit7 = remove (Y) in YUV color system
|
||||
byte_1 = saturation1 0-255 --> ?
|
||||
byte_2 = hue 0-255 --> OK
|
||||
byte_3 = saturation2 0-255 --> OK
|
||||
byte_4 = reenter saturation2 in documents --> ?
|
||||
byte_5 = spital effect green -> blue 0-255 --> ?
|
||||
byte_6 = spital effect gray -> red 0-255 --> ?
|
||||
byte_7 = contrast lower byte 0-255 --> OK
|
||||
byte_8 = contrast higher byte 0-255 --> OK
|
||||
byte_9 = brightness 0-255 --> OK
|
||||
byte_10 = if byte_10==4 contrast effective --> ?
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_camera.h"
|
||||
#include "ov2640_contrast_brightness.h"
|
||||
|
||||
static const uint8_t brightness_regs[6][5] = {
|
||||
{0x7C, 0x7D, 0x7C, 0x7D, 0x7D },
|
||||
{0x00, 0x04, 0x09, 0x00, 0x00 }, /* -2 */
|
||||
{0x00, 0x04, 0x09, 0x10, 0x00 }, /* -1 */
|
||||
{0x00, 0x04, 0x09, 0x20, 0x00 }, /* 0 */
|
||||
{0x00, 0x04, 0x09, 0x30, 0x00 }, /* +1 */
|
||||
{0x00, 0x04, 0x09, 0x40, 0x00 }, /* +2 */
|
||||
};
|
||||
|
||||
static const uint8_t contrast_regs[6][7] = {
|
||||
{0x7C, 0x7D, 0x7C, 0x7D, 0x7D, 0x7D, 0x7D },
|
||||
{0x00, 0x04, 0x07, 0x20, 0x18, 0x34, 0x06 }, /* -2 */
|
||||
{0x00, 0x04, 0x07, 0x20, 0x1c, 0x2a, 0x06 }, /* -1 */
|
||||
{0x00, 0x04, 0x07, 0x20, 0x20, 0x20, 0x06 }, /* 0 */
|
||||
{0x00, 0x04, 0x07, 0x20, 0x24, 0x16, 0x06 }, /* +1 */
|
||||
{0x00, 0x04, 0x07, 0x20, 0x28, 0x0c, 0x06 }, /* +2 */
|
||||
};
|
||||
|
||||
int ov2640_set_contrast_brightness(sensor_t *sensor, int _contrast, int _brightness)
|
||||
{
|
||||
int ret=0;
|
||||
|
||||
_contrast += 3;
|
||||
if (_contrast <= 0) {
|
||||
_contrast = 3;
|
||||
}
|
||||
else if (_contrast > 5)
|
||||
{
|
||||
_contrast = 5;
|
||||
}
|
||||
sensor->status.contrast = _contrast-3;
|
||||
|
||||
_brightness += 3;
|
||||
if (_brightness <= 0) {
|
||||
_brightness = 3;
|
||||
}
|
||||
else if (_brightness > 5)
|
||||
{
|
||||
_brightness = 5;
|
||||
}
|
||||
int brightness = brightness_regs[_brightness][3];
|
||||
sensor->status.brightness = _brightness-3;
|
||||
|
||||
// sensor->set_reg(sensor, int reg, int mask, int value)
|
||||
sensor->set_reg(sensor, 0xFF, 0x01, 0x00); // Select DSP bank
|
||||
|
||||
for (int i=0; i<7; i++)
|
||||
{
|
||||
if (i == 5)
|
||||
{
|
||||
sensor->set_reg(sensor, contrast_regs[0][i], 0xFF, (brightness | contrast_regs[_contrast][i]));
|
||||
}
|
||||
else
|
||||
{
|
||||
sensor->set_reg(sensor, contrast_regs[0][i], 0xFF, contrast_regs[_contrast][i]);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef OV2640_CONTRAST_BRIGHTNESS_H
|
||||
#define OV2640_CONTRAST_BRIGHTNESS_H
|
||||
|
||||
#include "esp_camera.h"
|
||||
|
||||
int ov2640_set_contrast_brightness(sensor_t *sensor, int _contrast, int _brightness);
|
||||
|
||||
#endif
|
||||
@@ -2,10 +2,9 @@
|
||||
#include "esp_camera.h"
|
||||
#include "ov2640_sharpness.h"
|
||||
|
||||
|
||||
const static uint8_t OV2640_SHARPNESS_AUTO[]=
|
||||
{
|
||||
//reg, val, mask
|
||||
//reg, val, mask
|
||||
0xFF, 0x00, 0xFF,
|
||||
0x92, 0x01, 0xFF,
|
||||
0x93, 0x20, 0x20,
|
||||
@@ -14,7 +13,7 @@ const static uint8_t OV2640_SHARPNESS_AUTO[]=
|
||||
|
||||
const static uint8_t OV2640_SHARPNESS_MANUAL[]=
|
||||
{
|
||||
//reg, val, mask
|
||||
//reg, val, mask
|
||||
0xFF, 0x00, 0xFF,
|
||||
0x92, 0x01, 0xFF,
|
||||
0x93, 0x00, 0x20,
|
||||
@@ -23,7 +22,7 @@ const static uint8_t OV2640_SHARPNESS_MANUAL[]=
|
||||
|
||||
const static uint8_t OV2640_SHARPNESS_LEVEL0[]=
|
||||
{
|
||||
//reg, val, mask
|
||||
//reg, val, mask
|
||||
0xFF, 0x00, 0xFF,
|
||||
0x92, 0x01, 0xFF,
|
||||
0x93, 0xC0, 0x1F,
|
||||
@@ -31,7 +30,7 @@ const static uint8_t OV2640_SHARPNESS_LEVEL0[]=
|
||||
};
|
||||
const static uint8_t OV2640_SHARPNESS_LEVEL1[]=
|
||||
{
|
||||
//reg, val, mask
|
||||
//reg, val, mask
|
||||
0xFF, 0x00, 0xFF,
|
||||
0x92, 0x01, 0xFF,
|
||||
0x93, 0xC1, 0x1F,
|
||||
@@ -39,7 +38,7 @@ const static uint8_t OV2640_SHARPNESS_LEVEL1[]=
|
||||
};
|
||||
const static uint8_t OV2640_SHARPNESS_LEVEL2[]=
|
||||
{
|
||||
//reg, val, mask
|
||||
//reg, val, mask
|
||||
0xFF, 0x00, 0xFF,
|
||||
0x92, 0x01, 0xFF,
|
||||
0x93, 0xC2, 0x1F,
|
||||
@@ -47,7 +46,7 @@ const static uint8_t OV2640_SHARPNESS_LEVEL2[]=
|
||||
};
|
||||
const static uint8_t OV2640_SHARPNESS_LEVEL3[]=
|
||||
{
|
||||
//reg, val, mask
|
||||
//reg, val, mask
|
||||
0xFF, 0x00, 0xFF,
|
||||
0x92, 0x01, 0xFF,
|
||||
0x93, 0xC4, 0x1F,
|
||||
@@ -55,7 +54,7 @@ const static uint8_t OV2640_SHARPNESS_LEVEL3[]=
|
||||
};
|
||||
const static uint8_t OV2640_SHARPNESS_LEVEL4[]=
|
||||
{
|
||||
//reg, val, mask
|
||||
//reg, val, mask
|
||||
0xFF, 0x00, 0xFF,
|
||||
0x92, 0x01, 0xFF,
|
||||
0x93, 0xC8, 0x1F,
|
||||
@@ -63,7 +62,7 @@ const static uint8_t OV2640_SHARPNESS_LEVEL4[]=
|
||||
};
|
||||
const static uint8_t OV2640_SHARPNESS_LEVEL5[]=
|
||||
{
|
||||
//reg, val, mask
|
||||
//reg, val, mask
|
||||
0xFF, 0x00, 0xFF,
|
||||
0x92, 0x01, 0xFF,
|
||||
0x93, 0xD0, 0x1F,
|
||||
@@ -71,7 +70,7 @@ const static uint8_t OV2640_SHARPNESS_LEVEL5[]=
|
||||
};
|
||||
const static uint8_t OV2640_SHARPNESS_LEVEL6[]=
|
||||
{
|
||||
//reg, val, mask
|
||||
//reg, val, mask
|
||||
0xFF, 0x00, 0xFF,
|
||||
0x92, 0x01, 0xFF,
|
||||
0x93, 0xDF, 0x1F,
|
||||
@@ -91,7 +90,6 @@ const static uint8_t *OV2640_SETTING_SHARPNESS[]=
|
||||
|
||||
#define OV2640_MAXLEVEL_SHARPNESS 6
|
||||
|
||||
|
||||
static int table_mask_write(sensor_t *sensor, const uint8_t* ptab)
|
||||
{
|
||||
uint8_t address;
|
||||
@@ -101,9 +99,9 @@ static int table_mask_write(sensor_t *sensor, const uint8_t* ptab)
|
||||
const uint8_t *pdata = ptab;
|
||||
|
||||
if (pdata == NULL)
|
||||
{
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
@@ -112,9 +110,9 @@ static int table_mask_write(sensor_t *sensor, const uint8_t* ptab)
|
||||
mask = *pdata++;
|
||||
|
||||
if ((address == 0) && (value == 0) && (mask == 0))
|
||||
{
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sensor->set_reg(sensor, address, mask, value);
|
||||
}
|
||||
@@ -122,31 +120,32 @@ static int table_mask_write(sensor_t *sensor, const uint8_t* ptab)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ov2640_enable_auto_sharpness(sensor_t *sensor)
|
||||
{
|
||||
table_mask_write(sensor, OV2640_SHARPNESS_AUTO);
|
||||
sensor->status.sharpness = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ov2640_set_sharpness(sensor_t *sensor, int sharpness)
|
||||
{
|
||||
int sharpness_temp = 0;
|
||||
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]);
|
||||
|
||||
sensor->status.sharpness = sharpness;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
// Workaround - bug in cam library - enable bits are set without using bitwise OR logic -> only latest enable setting is used
|
||||
// Reference: https://esp32.com/viewtopic.php?f=19&t=14376#p93178
|
||||
/* The memory structure is as follows for
|
||||
byte_0 = enable_bits
|
||||
byte_0->bit0 = enable saturation and hue --> OK
|
||||
byte_0->bit1 = enable saturation --> OK
|
||||
byte_0->bit2 = enable brightness and contrast --> OK
|
||||
byte_0->bit3 = enable green -> blue spitial effect (Antique and blunish and greenish and reddish and b&w) enable
|
||||
byte_0->bit4 = anable gray -> red spitial effect (Antique and blunish and greenish and reddish and b&w) enable
|
||||
byte_0->bit5 = remove (UV) in YUV color system
|
||||
byte_0->bit6 = enable negative
|
||||
byte_0->bit7 = remove (Y) in YUV color system
|
||||
byte_1 = saturation1 0-255 --> ?
|
||||
byte_2 = hue 0-255 --> OK
|
||||
byte_3 = saturation2 0-255 --> OK
|
||||
byte_4 = reenter saturation2 in documents --> ?
|
||||
byte_5 = spital effect green -> blue 0-255 --> ?
|
||||
byte_6 = spital effect gray -> red 0-255 --> ?
|
||||
byte_7 = contrast lower byte 0-255 --> OK
|
||||
byte_8 = contrast higher byte 0-255 --> OK
|
||||
byte_9 = brightness 0-255 --> OK
|
||||
byte_10 = if byte_10==4 contrast effective --> ?
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_camera.h"
|
||||
#include "ov2640_specialEffect.h"
|
||||
|
||||
static const uint8_t special_effects_regs[8][5] = {
|
||||
{0x7C, 0x7D, 0x7C, 0x7D, 0x7D},
|
||||
{0x00, 0X00, 0x05, 0X80, 0X80}, /* no effect */
|
||||
{0x00, 0X40, 0x05, 0X80, 0X80}, /* negative */
|
||||
{0x00, 0X18, 0x05, 0X80, 0X80}, /* black and white */
|
||||
{0x00, 0X18, 0x05, 0X40, 0XC0}, /* reddish */
|
||||
{0x00, 0X18, 0x05, 0X40, 0X40}, /* greenish */
|
||||
{0x00, 0X18, 0x05, 0XA0, 0X40}, /* blue */
|
||||
{0x00, 0X18, 0x05, 0X40, 0XA6}, /* retro */
|
||||
};
|
||||
|
||||
int ov2640_set_special_effect(sensor_t *sensor, int effect)
|
||||
{
|
||||
int ret = 0;
|
||||
effect++;
|
||||
|
||||
if (effect <= 0 || effect > 7)
|
||||
{
|
||||
effect = 1;
|
||||
}
|
||||
|
||||
sensor->status.special_effect = effect - 1;
|
||||
|
||||
int registerValue = 0x06; // enable saturation, contrast, brightness
|
||||
registerValue |= special_effects_regs[effect][1];
|
||||
|
||||
// sensor->set_reg(sensor, int reg, int mask, int value)
|
||||
sensor->set_reg(sensor, 0xFF, 0x01, 0x00); // Select DSP bank
|
||||
sensor->set_reg(sensor, special_effects_regs[0][0], 0xFF, 0x00);
|
||||
sensor->set_reg(sensor, special_effects_regs[0][1], 0x5E, registerValue);
|
||||
|
||||
for (int i = 2; i < 5; i++)
|
||||
{
|
||||
sensor->set_reg(sensor, special_effects_regs[0][i], 0xFF, special_effects_regs[effect][i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
10
code/components/jomjol_controlcamera/ov2640_specialEffect.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef OV2640_SPECIALEFFECT_H
|
||||
#define OV2640_SPECIALEFFECT_H
|
||||
|
||||
#include "esp_camera.h"
|
||||
|
||||
int ov2640_set_special_effect(sensor_t *sensor, int effect);
|
||||
|
||||
#endif
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "ClassLogFile.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "basic_auth.h"
|
||||
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char *TAG = "server_cam";
|
||||
@@ -101,6 +103,7 @@ esp_err_t handler_capture(httpd_req_t *req)
|
||||
{
|
||||
Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera
|
||||
Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize, CCstatus.ImageVflip);
|
||||
Camera.LedIntensity = CCstatus.ImageLedIntensity;
|
||||
CFstatus.changedCameraSettings = false;
|
||||
}
|
||||
|
||||
@@ -159,6 +162,7 @@ esp_err_t handler_capture_with_light(httpd_req_t *req)
|
||||
{
|
||||
Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera
|
||||
Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize, CCstatus.ImageVflip);
|
||||
Camera.LedIntensity = CCstatus.ImageLedIntensity;
|
||||
CFstatus.changedCameraSettings = false;
|
||||
}
|
||||
|
||||
@@ -241,6 +245,7 @@ esp_err_t handler_capture_save_to_file(httpd_req_t *req)
|
||||
{
|
||||
Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera
|
||||
Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize, CCstatus.ImageVflip);
|
||||
Camera.LedIntensity = CCstatus.ImageLedIntensity;
|
||||
CFstatus.changedCameraSettings = false;
|
||||
}
|
||||
|
||||
@@ -277,27 +282,27 @@ void register_server_camera_uri(httpd_handle_t server)
|
||||
camuri.method = HTTP_GET;
|
||||
|
||||
camuri.uri = "/lighton";
|
||||
camuri.handler = handler_lightOn;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_lightOn);
|
||||
camuri.user_ctx = (void *)"Light On";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/lightoff";
|
||||
camuri.handler = handler_lightOff;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_lightOff);
|
||||
camuri.user_ctx = (void *)"Light Off";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/capture";
|
||||
camuri.handler = handler_capture;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_capture);
|
||||
camuri.user_ctx = NULL;
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/capture_with_flashlight";
|
||||
camuri.handler = handler_capture_with_light;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(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.handler = APPLY_BASIC_AUTH_FILTER(handler_capture_save_to_file);
|
||||
camuri.user_ctx = NULL;
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ extern "C" {
|
||||
|
||||
#include "Helper.h"
|
||||
#include "miniz.h"
|
||||
#include "basic_auth.h"
|
||||
|
||||
static const char *TAG = "OTA FILE";
|
||||
|
||||
@@ -1174,7 +1175,7 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path)
|
||||
httpd_uri_t file_download = {
|
||||
.uri = "/fileserver*", // Match all URIs of type /path/to/file
|
||||
.method = HTTP_GET,
|
||||
.handler = download_get_handler,
|
||||
.handler = APPLY_BASIC_AUTH_FILTER(download_get_handler),
|
||||
.user_ctx = server_data // Pass server data as context
|
||||
};
|
||||
httpd_register_uri_handler(server, &file_download);
|
||||
@@ -1183,7 +1184,7 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path)
|
||||
httpd_uri_t file_datafileact = {
|
||||
.uri = "/datafileact", // Match all URIs of type /path/to/file
|
||||
.method = HTTP_GET,
|
||||
.handler = datafileact_get_full_handler,
|
||||
.handler = APPLY_BASIC_AUTH_FILTER(datafileact_get_full_handler),
|
||||
.user_ctx = server_data // Pass server data as context
|
||||
};
|
||||
httpd_register_uri_handler(server, &file_datafileact);
|
||||
@@ -1192,7 +1193,7 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path)
|
||||
httpd_uri_t file_datafile_last_part_handle = {
|
||||
.uri = "/data", // Match all URIs of type /path/to/file
|
||||
.method = HTTP_GET,
|
||||
.handler = datafileact_get_last_part_handler,
|
||||
.handler = APPLY_BASIC_AUTH_FILTER(datafileact_get_last_part_handler),
|
||||
.user_ctx = server_data // Pass server data as context
|
||||
};
|
||||
httpd_register_uri_handler(server, &file_datafile_last_part_handle);
|
||||
@@ -1200,7 +1201,7 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path)
|
||||
httpd_uri_t file_logfileact = {
|
||||
.uri = "/logfileact", // Match all URIs of type /path/to/file
|
||||
.method = HTTP_GET,
|
||||
.handler = logfileact_get_full_handler,
|
||||
.handler = APPLY_BASIC_AUTH_FILTER(logfileact_get_full_handler),
|
||||
.user_ctx = server_data // Pass server data as context
|
||||
};
|
||||
httpd_register_uri_handler(server, &file_logfileact);
|
||||
@@ -1209,7 +1210,7 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path)
|
||||
httpd_uri_t file_logfile_last_part_handle = {
|
||||
.uri = "/log", // Match all URIs of type /path/to/file
|
||||
.method = HTTP_GET,
|
||||
.handler = logfileact_get_last_part_handler,
|
||||
.handler = APPLY_BASIC_AUTH_FILTER(logfileact_get_last_part_handler),
|
||||
.user_ctx = server_data // Pass server data as context
|
||||
};
|
||||
httpd_register_uri_handler(server, &file_logfile_last_part_handle);
|
||||
@@ -1219,7 +1220,7 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path)
|
||||
httpd_uri_t file_upload = {
|
||||
.uri = "/upload/*", // Match all URIs of type /upload/path/to/file
|
||||
.method = HTTP_POST,
|
||||
.handler = upload_post_handler,
|
||||
.handler = APPLY_BASIC_AUTH_FILTER(upload_post_handler),
|
||||
.user_ctx = server_data // Pass server data as context
|
||||
};
|
||||
httpd_register_uri_handler(server, &file_upload);
|
||||
@@ -1228,7 +1229,7 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path)
|
||||
httpd_uri_t file_delete = {
|
||||
.uri = "/delete/*", // Match all URIs of type /delete/path/to/file
|
||||
.method = HTTP_POST,
|
||||
.handler = delete_post_handler,
|
||||
.handler = APPLY_BASIC_AUTH_FILTER(delete_post_handler),
|
||||
.user_ctx = server_data // Pass server data as context
|
||||
};
|
||||
httpd_register_uri_handler(server, &file_delete);
|
||||
|
||||
@@ -42,6 +42,7 @@ https://docs.espressif.com/projects/esp-idf/en/latest/esp32/migration-guides/rel
|
||||
|
||||
#include "Helper.h"
|
||||
#include "statusled.h"
|
||||
#include "basic_auth.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
/*an ota data write buffer ready to write to the flash*/
|
||||
@@ -690,13 +691,13 @@ void register_server_ota_sdcard_uri(httpd_handle_t server)
|
||||
httpd_uri_t camuri = { };
|
||||
camuri.method = HTTP_GET;
|
||||
camuri.uri = "/ota";
|
||||
camuri.handler = handler_ota_update;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_ota_update);
|
||||
camuri.user_ctx = (void*) "Do OTA";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.method = HTTP_GET;
|
||||
camuri.uri = "/reboot";
|
||||
camuri.handler = handler_reboot;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_reboot);
|
||||
camuri.user_ctx = (void*) "Reboot";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
|
||||
@@ -65,11 +65,11 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
|
||||
|
||||
if (CNNType == Digit) {
|
||||
for (int i = 0; i < GENERAL[_analog]->ROI.size(); ++i) {
|
||||
if (GENERAL[_analog]->ROI[i]->result_klasse >= 10) {
|
||||
result = result + "N";
|
||||
if ((GENERAL[_analog]->ROI[i]->result_klasse >= 0) && (GENERAL[_analog]->ROI[i]->result_klasse < 10)) {
|
||||
result = result + std::to_string(GENERAL[_analog]->ROI[i]->result_klasse);
|
||||
}
|
||||
else {
|
||||
result = result + std::to_string(GENERAL[_analog]->ROI[i]->result_klasse);
|
||||
result = result + "N";
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -78,7 +78,7 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
|
||||
if ((CNNType == DoubleHyprid10) || (CNNType == Digit100)) {
|
||||
float number = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float;
|
||||
// NaN?
|
||||
if (number >= 0) {
|
||||
if ((number >= 0) && (number < 10)) {
|
||||
// is only set if it is the first digit (no analogue before!)
|
||||
if (_extendedResolution) {
|
||||
int result_after_decimal_point = ((int) floor(number * 10)) % 10;
|
||||
@@ -95,8 +95,15 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
|
||||
else {
|
||||
prev = PointerEvalHybridNew(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, prev, prev);
|
||||
}
|
||||
result = std::to_string(prev);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(dig100) prev=" + std::to_string(prev));
|
||||
|
||||
// is necessary because a number greater than 9.994999 returns a 10! (for further details see check in PointerEvalHybridNew)
|
||||
if ((prev >= 0) && (prev < 10)) {
|
||||
result = std::to_string(prev);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(dig100) prev=" + std::to_string(prev));
|
||||
}
|
||||
else {
|
||||
result = "N";
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -107,7 +114,7 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
|
||||
}
|
||||
|
||||
for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i) {
|
||||
if (GENERAL[_analog]->ROI[i]->result_float >= 0) {
|
||||
if ((GENERAL[_analog]->ROI[i]->result_float >= 0) && (GENERAL[_analog]->ROI[i]->result_float < 10)) {
|
||||
prev = PointerEvalHybridNew(GENERAL[_analog]->ROI[i]->result_float, GENERAL[_analog]->ROI[i+1]->result_float, prev);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout#PointerEvalHybridNew()= " + std::to_string(prev));
|
||||
result = std::to_string(prev) + result;
|
||||
@@ -117,7 +124,6 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
|
||||
prev = -1;
|
||||
result = "N" + result;
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(result_float<0 /'N') result_float=" + std::to_string(GENERAL[_analog]->ROI[i]->result_float));
|
||||
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -150,6 +156,9 @@ int ClassFlowCNNGeneral::PointerEvalHybridNew(float number, float number_of_pred
|
||||
// on first digit is no spezial logic for transition needed
|
||||
// we use the recognition as given. The result is the int value of the recognition
|
||||
// add precisition of 2 digits and round before trunc
|
||||
// a number greater than 9.994999 is returned as 10, this leads to an error during the decimal shift because the NUMBERS[j]->ReturnRawValue is one digit longer.
|
||||
// To avoid this, an additional test must be carried out, see "if ((CNNType == DoubleHyprid10) || (CNNType == Digit100))" check in getReadout()
|
||||
// Another alternative would be "result = (int) ((int) trunc(round((number+10 % 10)*1000))) / 1000;", which could, however, lead to other errors?
|
||||
result = (int) ((int) trunc(round((number+10 % 10)*100)) ) / 100;
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - No predecessor - Result = " + std::to_string(result) +
|
||||
|
||||
@@ -26,6 +26,7 @@ extern "C" {
|
||||
|
||||
#include "server_help.h"
|
||||
#include "MainFlowControl.h"
|
||||
#include "basic_auth.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char* TAG = "FLOWCTRL";
|
||||
@@ -196,7 +197,7 @@ bool ClassFlowControll::StartMQTTService()
|
||||
|
||||
void ClassFlowControll::SetInitialParameter(void)
|
||||
{
|
||||
AutoStart = false;
|
||||
AutoStart = true;
|
||||
SetupModeActive = false;
|
||||
AutoInterval = 10; // Minutes
|
||||
flowdigit = NULL;
|
||||
@@ -210,7 +211,8 @@ void ClassFlowControll::SetInitialParameter(void)
|
||||
|
||||
bool ClassFlowControll::getIsAutoStart(void)
|
||||
{
|
||||
return AutoStart;
|
||||
//return AutoStart;
|
||||
return true; // Flow must always be enabled, else the manual trigger (REST, MQTT) will not work!
|
||||
}
|
||||
|
||||
|
||||
@@ -557,10 +559,6 @@ bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph)) {
|
||||
splitted = ZerlegeZeile(aktparamgraph, " =");
|
||||
|
||||
if ((toUpper(splitted[0]) == "AUTOSTART") && (splitted.size() > 1)) {
|
||||
AutoStart = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "INTERVAL") && (splitted.size() > 1)) {
|
||||
if (isStringNumeric(splitted[1]))
|
||||
|
||||
@@ -66,6 +66,8 @@ struct NumberPost {
|
||||
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
|
||||
|
||||
string DomoticzIdx; // Domoticz counter Idx
|
||||
|
||||
string FieldV1; // influxdbFieldName_v1; Name of the Field in InfluxDBv1
|
||||
string MeasurementV1; // influxdbMeasurementName_v1; Name of the Measurement in InfluxDBv1
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ void ClassFlowMQTT::SetInitialParameter(void)
|
||||
ListFlowControll = NULL;
|
||||
disabled = false;
|
||||
keepAlive = 25*60;
|
||||
domoticzintopic = "";
|
||||
}
|
||||
|
||||
ClassFlowMQTT::ClassFlowMQTT()
|
||||
@@ -105,43 +106,44 @@ 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))
|
||||
std::string _param = GetParameterName(splitted[0]);
|
||||
if ((toUpper(_param) == "CACERT") && (splitted.size() > 1))
|
||||
{
|
||||
this->caCertFilename = splitted[1];
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "CLIENTCERT") && (splitted.size() > 1))
|
||||
if ((toUpper(_param) == "CLIENTCERT") && (splitted.size() > 1))
|
||||
{
|
||||
this->clientCertFilename = splitted[1];
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "CLIENTKEY") && (splitted.size() > 1))
|
||||
if ((toUpper(_param) == "CLIENTKEY") && (splitted.size() > 1))
|
||||
{
|
||||
this->clientKeyFilename = splitted[1];
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "USER") && (splitted.size() > 1))
|
||||
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]) == "RETAINMESSAGES") && (splitted.size() > 1))
|
||||
if ((toUpper(_param) == "RETAINMESSAGES") && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "TRUE") {
|
||||
SetRetainFlag = true;
|
||||
setMqtt_Server_Retain(SetRetainFlag);
|
||||
}
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "HOMEASSISTANTDISCOVERY") && (splitted.size() > 1))
|
||||
if ((toUpper(_param) == "HOMEASSISTANTDISCOVERY") && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "TRUE")
|
||||
SetHomeassistantDiscoveryEnabled(true);
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "METERTYPE") && (splitted.size() > 1)) {
|
||||
if ((toUpper(_param) == "METERTYPE") && (splitted.size() > 1)) {
|
||||
/* Use meter type for the device class
|
||||
Make sure it is a listed one on https://developers.home-assistant.io/docs/core/entity/sensor/#available-device-classes */
|
||||
if (toUpper(splitted[1]) == "WATER_M3") {
|
||||
@@ -151,7 +153,7 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
mqttServer_setMeterType("water", "L", "h", "L/h");
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "WATER_FT3") {
|
||||
mqttServer_setMeterType("water", "ft³", "m", "ft³/m"); // Minutes
|
||||
mqttServer_setMeterType("water", "ft³", "m", "ft³/m"); // m = Minutes
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "WATER_GAL") {
|
||||
mqttServer_setMeterType("water", "gal", "h", "gal/h");
|
||||
@@ -160,7 +162,7 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
mqttServer_setMeterType("gas", "m³", "h", "m³/h");
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "GAS_FT3") {
|
||||
mqttServer_setMeterType("gas", "ft³", "m", "ft³/m"); // Minutes
|
||||
mqttServer_setMeterType("gas", "ft³", "m", "ft³/m"); // m = Minutes
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "ENERGY_WH") {
|
||||
mqttServer_setMeterType("energy", "Wh", "h", "W");
|
||||
@@ -176,15 +178,26 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
}
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "CLIENTID") && (splitted.size() > 1))
|
||||
if ((toUpper(_param) == "CLIENTID") && (splitted.size() > 1))
|
||||
{
|
||||
this->clientname = splitted[1];
|
||||
}
|
||||
|
||||
if (((toUpper(splitted[0]) == "TOPIC") || (toUpper(splitted[0]) == "MAINTOPIC")) && (splitted.size() > 1))
|
||||
if (((toUpper(_param) == "TOPIC") || (toUpper(splitted[0]) == "MAINTOPIC")) && (splitted.size() > 1))
|
||||
{
|
||||
maintopic = splitted[1];
|
||||
}
|
||||
|
||||
if (((toUpper(_param) == "DOMOTICZTOPICIN")) && (splitted.size() > 1))
|
||||
{
|
||||
this->domoticzintopic = splitted[1];
|
||||
}
|
||||
|
||||
if (((toUpper(_param) == "DOMOTICZIDX")) && (splitted.size() > 1))
|
||||
{
|
||||
handleIdx(splitted[0], splitted[1]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Note:
|
||||
@@ -193,6 +206,7 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
* To work around this, we delay the start and trigger it from ClassFlowControll::ReadParameter() */
|
||||
|
||||
mqttServer_setMainTopic(maintopic);
|
||||
mqttServer_setDmoticzInTopic(domoticzintopic);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -210,7 +224,7 @@ bool ClassFlowMQTT::Start(float AutoInterval)
|
||||
|
||||
mqttServer_setParameter(flowpostprocessing->GetNumbers(), keepAlive, roundInterval);
|
||||
|
||||
bool MQTTConfigCheck = MQTT_Configure(uri, clientname, user, password, maintopic, LWT_TOPIC, LWT_CONNECTED,
|
||||
bool MQTTConfigCheck = MQTT_Configure(uri, clientname, user, password, maintopic, domoticzintopic, LWT_TOPIC, LWT_CONNECTED,
|
||||
LWT_DISCONNECTED, caCertFilename, clientCertFilename, clientKeyFilename,
|
||||
keepAlive, SetRetainFlag, (void *)&GotConnected);
|
||||
|
||||
@@ -235,6 +249,8 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
std::string resultchangabs = "";
|
||||
string zw = "";
|
||||
string namenumber = "";
|
||||
string domoticzpayload = "";
|
||||
string DomoticzIdx = "";
|
||||
int qos = 1;
|
||||
|
||||
/* Send the the Homeassistant Discovery and the Static Topics in case they where scheduled */
|
||||
@@ -258,16 +274,20 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
resultchangabs = (*NUMBERS)[i]->ReturnChangeAbsolute; // Units per round
|
||||
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
||||
|
||||
DomoticzIdx = (*NUMBERS)[i]->DomoticzIdx;
|
||||
domoticzpayload = "{\"command\":\"udevice\",\"idx\":" + DomoticzIdx + ",\"svalue\":\""+ result + "\"}";
|
||||
|
||||
namenumber = (*NUMBERS)[i]->name;
|
||||
if (namenumber == "default")
|
||||
namenumber = maintopic + "/";
|
||||
else
|
||||
namenumber = maintopic + "/" + namenumber + "/";
|
||||
|
||||
if ((domoticzintopic.length() > 0) && (result.length() > 0))
|
||||
success |= MQTTPublish(domoticzintopic, domoticzpayload, qos, SetRetainFlag);
|
||||
|
||||
if (result.length() > 0)
|
||||
if (result.length() > 0)
|
||||
success |= MQTTPublish(namenumber + "value", result, qos, SetRetainFlag);
|
||||
|
||||
if (resulterror.length() > 0)
|
||||
success |= MQTTPublish(namenumber + "error", resulterror, qos, SetRetainFlag);
|
||||
|
||||
@@ -286,7 +306,7 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
|
||||
if (resultchangabs.length() > 0) {
|
||||
success |= MQTTPublish(namenumber + "changeabsolut", resultchangabs, qos, SetRetainFlag); // Legacy API
|
||||
success |= MQTTPublish(namenumber + "rate_per_Digitization_round", resultchangabs, qos, SetRetainFlag);
|
||||
success |= MQTTPublish(namenumber + "rate_per_digitization_round", resultchangabs, qos, SetRetainFlag);
|
||||
}
|
||||
|
||||
if (resultraw.length() > 0)
|
||||
@@ -325,6 +345,26 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClassFlowMQTT::handleIdx(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]->DomoticzIdx = _value;
|
||||
}
|
||||
if (flowpostprocessing->NUMBERS[j]->name == _digit)
|
||||
{
|
||||
flowpostprocessing->NUMBERS[j]->DomoticzIdx = _value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ENABLE_MQTT
|
||||
@@ -23,9 +23,9 @@ protected:
|
||||
bool SetRetainFlag;
|
||||
int keepAlive; // Seconds
|
||||
float roundInterval; // Minutes
|
||||
|
||||
std::string maintopic;
|
||||
std::string maintopic, domoticzintopic;
|
||||
void SetInitialParameter(void);
|
||||
void handleIdx(string _decsep, string _value);
|
||||
|
||||
public:
|
||||
ClassFlowMQTT();
|
||||
|
||||
@@ -564,7 +564,7 @@ bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
handleDecimalSeparator(splitted[0], splitted[1]);
|
||||
}
|
||||
|
||||
if ((toUpper(_param) == "AnalogToDigitTransitionStart") && (splitted.size() > 1)) {
|
||||
if ((toUpper(_param) == "ANALOGTODIGITTRANSITIONSTART") && (splitted.size() > 1)) {
|
||||
handleAnalogToDigitTransitionStart(splitted[0], splitted[1]);
|
||||
}
|
||||
|
||||
|
||||
@@ -499,8 +499,8 @@ bool ClassFlowTakeImage::ReadParameter(FILE *pfile, string &aktparamgraph)
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
float ledintensity = std::stof(splitted[1]);
|
||||
Camera.SetLEDIntensity(ledintensity);
|
||||
int ledintensity = std::stoi(splitted[1]);
|
||||
CCstatus.ImageLedIntensity = Camera.SetLEDIntensity(ledintensity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,6 +559,7 @@ bool ClassFlowTakeImage::doFlow(string zwtime)
|
||||
{
|
||||
Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera
|
||||
Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize, CCstatus.ImageVflip);
|
||||
Camera.LedIntensity = CCstatus.ImageLedIntensity;
|
||||
CFstatus.changedCameraSettings = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "read_wlanini.h"
|
||||
#include "connect_wlan.h"
|
||||
#include "psram.h"
|
||||
#include "basic_auth.h"
|
||||
|
||||
// support IDF 5.x
|
||||
#ifndef portTICK_RATE_MS
|
||||
@@ -148,31 +149,39 @@ esp_err_t setCCstatusToCFstatus(void)
|
||||
CFstatus.CamSensor_id = CCstatus.CamSensor_id;
|
||||
|
||||
CFstatus.ImageFrameSize = CCstatus.ImageFrameSize;
|
||||
CFstatus.ImageGainceiling = CCstatus.ImageGainceiling;
|
||||
|
||||
CFstatus.ImageContrast = CCstatus.ImageContrast;
|
||||
CFstatus.ImageBrightness = CCstatus.ImageBrightness;
|
||||
CFstatus.ImageSaturation = CCstatus.ImageSaturation;
|
||||
|
||||
CFstatus.ImageQuality = CCstatus.ImageQuality;
|
||||
CFstatus.ImageBrightness = CCstatus.ImageBrightness;
|
||||
CFstatus.ImageContrast = CCstatus.ImageContrast;
|
||||
CFstatus.ImageSaturation = CCstatus.ImageSaturation;
|
||||
CFstatus.ImageSharpness = CCstatus.ImageSharpness;
|
||||
CFstatus.ImageAutoSharpness = CCstatus.ImageAutoSharpness;
|
||||
CFstatus.ImageWbMode = CCstatus.ImageWbMode;
|
||||
CFstatus.ImageAwb = CCstatus.ImageAwb;
|
||||
CFstatus.ImageAwbGain = CCstatus.ImageAwbGain;
|
||||
CFstatus.ImageAec = CCstatus.ImageAec;
|
||||
CFstatus.ImageAec2 = CCstatus.ImageAec2;
|
||||
CFstatus.ImageAeLevel = CCstatus.ImageAeLevel;
|
||||
CFstatus.ImageAecValue = CCstatus.ImageAecValue;
|
||||
|
||||
CFstatus.ImageGainceiling = CCstatus.ImageGainceiling;
|
||||
|
||||
CFstatus.ImageAgc = CCstatus.ImageAgc;
|
||||
CFstatus.ImageAgcGain = CCstatus.ImageAgcGain;
|
||||
CFstatus.ImageBpc = CCstatus.ImageBpc;
|
||||
CFstatus.ImageWpc = CCstatus.ImageWpc;
|
||||
CFstatus.ImageRawGma = CCstatus.ImageRawGma;
|
||||
CFstatus.ImageLenc = CCstatus.ImageLenc;
|
||||
CFstatus.ImageSpecialEffect = CCstatus.ImageSpecialEffect;
|
||||
CFstatus.ImageAec = CCstatus.ImageAec;
|
||||
CFstatus.ImageHmirror = CCstatus.ImageHmirror;
|
||||
CFstatus.ImageVflip = CCstatus.ImageVflip;
|
||||
|
||||
CFstatus.ImageAwb = CCstatus.ImageAwb;
|
||||
CFstatus.ImageAec2 = CCstatus.ImageAec2;
|
||||
CFstatus.ImageAecValue = CCstatus.ImageAecValue;
|
||||
CFstatus.ImageSpecialEffect = CCstatus.ImageSpecialEffect;
|
||||
CFstatus.ImageWbMode = CCstatus.ImageWbMode;
|
||||
CFstatus.ImageAeLevel = CCstatus.ImageAeLevel;
|
||||
|
||||
CFstatus.ImageDcw = CCstatus.ImageDcw;
|
||||
CFstatus.ImageBpc = CCstatus.ImageBpc;
|
||||
CFstatus.ImageWpc = CCstatus.ImageWpc;
|
||||
CFstatus.ImageAwbGain = CCstatus.ImageAwbGain;
|
||||
CFstatus.ImageAgcGain = CCstatus.ImageAgcGain;
|
||||
|
||||
CFstatus.ImageRawGma = CCstatus.ImageRawGma;
|
||||
CFstatus.ImageLenc = CCstatus.ImageLenc;
|
||||
|
||||
CFstatus.ImageSharpness = CCstatus.ImageSharpness;
|
||||
CFstatus.ImageAutoSharpness = CCstatus.ImageAutoSharpness;
|
||||
|
||||
CFstatus.ImageDenoiseLevel = CCstatus.ImageDenoiseLevel;
|
||||
|
||||
CFstatus.ImageLedIntensity = CCstatus.ImageLedIntensity;
|
||||
@@ -192,31 +201,39 @@ esp_err_t setCFstatusToCCstatus(void)
|
||||
// CCstatus.CamSensor_id = CFstatus.CamSensor_id;
|
||||
|
||||
CCstatus.ImageFrameSize = CFstatus.ImageFrameSize;
|
||||
CCstatus.ImageGainceiling = CFstatus.ImageGainceiling;
|
||||
|
||||
CCstatus.ImageContrast = CFstatus.ImageContrast;
|
||||
CCstatus.ImageBrightness = CFstatus.ImageBrightness;
|
||||
CCstatus.ImageSaturation = CFstatus.ImageSaturation;
|
||||
|
||||
CCstatus.ImageQuality = CFstatus.ImageQuality;
|
||||
CCstatus.ImageBrightness = CFstatus.ImageBrightness;
|
||||
CCstatus.ImageContrast = CFstatus.ImageContrast;
|
||||
CCstatus.ImageSaturation = CFstatus.ImageSaturation;
|
||||
CCstatus.ImageSharpness = CFstatus.ImageSharpness;
|
||||
CCstatus.ImageAutoSharpness = CFstatus.ImageAutoSharpness;
|
||||
CCstatus.ImageWbMode = CFstatus.ImageWbMode;
|
||||
CCstatus.ImageAwb = CFstatus.ImageAwb;
|
||||
CCstatus.ImageAwbGain = CFstatus.ImageAwbGain;
|
||||
CCstatus.ImageAec = CFstatus.ImageAec;
|
||||
CCstatus.ImageAec2 = CFstatus.ImageAec2;
|
||||
CCstatus.ImageAeLevel = CFstatus.ImageAeLevel;
|
||||
CCstatus.ImageAecValue = CFstatus.ImageAecValue;
|
||||
|
||||
CCstatus.ImageGainceiling = CFstatus.ImageGainceiling;
|
||||
|
||||
CCstatus.ImageAgc = CFstatus.ImageAgc;
|
||||
CCstatus.ImageAgcGain = CFstatus.ImageAgcGain;
|
||||
CCstatus.ImageBpc = CFstatus.ImageBpc;
|
||||
CCstatus.ImageWpc = CFstatus.ImageWpc;
|
||||
CCstatus.ImageRawGma = CFstatus.ImageRawGma;
|
||||
CCstatus.ImageLenc = CFstatus.ImageLenc;
|
||||
CCstatus.ImageSpecialEffect = CFstatus.ImageSpecialEffect;
|
||||
CCstatus.ImageAec = CFstatus.ImageAec;
|
||||
CCstatus.ImageHmirror = CFstatus.ImageHmirror;
|
||||
CCstatus.ImageVflip = CFstatus.ImageVflip;
|
||||
|
||||
CCstatus.ImageAwb = CFstatus.ImageAwb;
|
||||
CCstatus.ImageAec2 = CFstatus.ImageAec2;
|
||||
CCstatus.ImageAecValue = CFstatus.ImageAecValue;
|
||||
CCstatus.ImageSpecialEffect = CFstatus.ImageSpecialEffect;
|
||||
CCstatus.ImageWbMode = CFstatus.ImageWbMode;
|
||||
CCstatus.ImageAeLevel = CFstatus.ImageAeLevel;
|
||||
|
||||
CCstatus.ImageDcw = CFstatus.ImageDcw;
|
||||
CCstatus.ImageBpc = CFstatus.ImageBpc;
|
||||
CCstatus.ImageWpc = CFstatus.ImageWpc;
|
||||
CCstatus.ImageAwbGain = CFstatus.ImageAwbGain;
|
||||
CCstatus.ImageAgcGain = CFstatus.ImageAgcGain;
|
||||
|
||||
CCstatus.ImageRawGma = CFstatus.ImageRawGma;
|
||||
CCstatus.ImageLenc = CFstatus.ImageLenc;
|
||||
|
||||
CCstatus.ImageSharpness = CFstatus.ImageSharpness;
|
||||
CCstatus.ImageAutoSharpness = CFstatus.ImageAutoSharpness;
|
||||
|
||||
CCstatus.ImageDenoiseLevel = CFstatus.ImageDenoiseLevel;
|
||||
|
||||
CCstatus.ImageLedIntensity = CFstatus.ImageLedIntensity;
|
||||
@@ -238,43 +255,43 @@ esp_err_t setCFstatusToCam(void)
|
||||
if (s != NULL)
|
||||
{
|
||||
s->set_framesize(s, CFstatus.ImageFrameSize);
|
||||
|
||||
// s->set_contrast(s, CFstatus.ImageContrast); // -2 to 2
|
||||
// s->set_brightness(s, CFstatus.ImageBrightness); // -2 to 2
|
||||
Camera.SetCamContrastBrightness(s, CFstatus.ImageContrast, CFstatus.ImageBrightness);
|
||||
|
||||
s->set_saturation(s, CFstatus.ImageSaturation); // -2 to 2
|
||||
|
||||
s->set_quality(s, CFstatus.ImageQuality); // 0 - 63
|
||||
|
||||
s->set_brightness(s, CFstatus.ImageBrightness); // -2 to 2
|
||||
s->set_contrast(s, CFstatus.ImageContrast); // -2 to 2
|
||||
s->set_saturation(s, CFstatus.ImageSaturation); // -2 to 2
|
||||
// s->set_sharpness(s, CFstatus.ImageSharpness); // auto-sharpness is not officially supported, default to 0
|
||||
Camera.SetCamSharpness(CFstatus.ImageAutoSharpness, CFstatus.ImageSharpness);
|
||||
|
||||
s->set_denoise(s, CFstatus.ImageDenoiseLevel); // The OV2640 does not support it, OV3660 and OV5640 (0 to 8)
|
||||
|
||||
s->set_special_effect(s, CFstatus.ImageSpecialEffect); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
|
||||
s->set_wb_mode(s, CFstatus.ImageWbMode); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
|
||||
|
||||
s->set_ae_level(s, CFstatus.ImageAeLevel); // -2 to 2
|
||||
s->set_aec_value(s, CFstatus.ImageAecValue); // 0 to 1200
|
||||
s->set_agc_gain(s, CFstatus.ImageAgcGain); // 0 to 30
|
||||
|
||||
// s->set_gainceiling(s, CFstatus.ImageGainceiling); // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128)
|
||||
Camera.ov5640_set_gainceiling(s, CFstatus.ImageGainceiling);
|
||||
Camera.SetCamGainceiling(s, CFstatus.ImageGainceiling);
|
||||
|
||||
s->set_lenc(s, CFstatus.ImageLenc); // 0 = disable , 1 = enable
|
||||
s->set_gain_ctrl(s, CFstatus.ImageAgc); // 0 = disable , 1 = enable
|
||||
s->set_exposure_ctrl(s, CFstatus.ImageAec); // 0 = disable , 1 = enable
|
||||
s->set_hmirror(s, CFstatus.ImageHmirror); // 0 = disable , 1 = enable
|
||||
s->set_vflip(s, CFstatus.ImageVflip); // 0 = disable , 1 = enable
|
||||
|
||||
s->set_hmirror(s, CFstatus.ImageHmirror); // 0 = disable , 1 = enable
|
||||
s->set_vflip(s, CFstatus.ImageVflip); // 0 = disable , 1 = enable
|
||||
s->set_aec2(s, CFstatus.ImageAec2); // 0 = disable , 1 = enable
|
||||
s->set_whitebal(s, CFstatus.ImageAwb); // 0 = disable , 1 = enable
|
||||
s->set_aec2(s, CFstatus.ImageAec2); // 0 = disable , 1 = enable
|
||||
s->set_aec_value(s, CFstatus.ImageAecValue); // 0 to 1200
|
||||
// s->set_special_effect(s, CFstatus.ImageSpecialEffect); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
|
||||
Camera.SetCamSpecialEffect(s, CFstatus.ImageSpecialEffect);
|
||||
s->set_wb_mode(s, CFstatus.ImageWbMode); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
|
||||
s->set_ae_level(s, CFstatus.ImageAeLevel); // -2 to 2
|
||||
|
||||
s->set_bpc(s, CFstatus.ImageBpc); // 0 = disable , 1 = enable
|
||||
s->set_wpc(s, CFstatus.ImageWpc); // 0 = disable , 1 = enable
|
||||
s->set_dcw(s, CFstatus.ImageDcw); // 0 = disable , 1 = enable
|
||||
s->set_bpc(s, CFstatus.ImageBpc); // 0 = disable , 1 = enable
|
||||
s->set_wpc(s, CFstatus.ImageWpc); // 0 = disable , 1 = enable
|
||||
s->set_awb_gain(s, CFstatus.ImageAwbGain); // 0 = disable , 1 = enable
|
||||
s->set_agc_gain(s, CFstatus.ImageAgcGain); // 0 to 30
|
||||
|
||||
s->set_raw_gma(s, CFstatus.ImageRawGma); // 0 = disable , 1 = enable
|
||||
s->set_lenc(s, CFstatus.ImageLenc); // 0 = disable , 1 = enable
|
||||
|
||||
s->set_awb_gain(s, CFstatus.ImageAwbGain); // 0 = disable , 1 = enable
|
||||
s->set_whitebal(s, CFstatus.ImageAwb); // 0 = disable , 1 = enable
|
||||
|
||||
s->set_dcw(s, CFstatus.ImageDcw); // 0 = disable , 1 = enable
|
||||
// s->set_sharpness(s, CFstatus.ImageSharpness); // auto-sharpness is not officially supported, default to 0
|
||||
Camera.SetCamSharpness(CFstatus.ImageAutoSharpness, CFstatus.ImageSharpness);
|
||||
s->set_denoise(s, CFstatus.ImageDenoiseLevel); // The OV2640 does not support it, OV3660 and OV5640 (0 to 8)
|
||||
|
||||
TickType_t xDelay2 = 100 / portTICK_PERIOD_MS;
|
||||
vTaskDelay(xDelay2);
|
||||
@@ -406,8 +423,7 @@ esp_err_t handler_flow_start(httpd_req_t *req)
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Flow start triggered by REST API, but flow is not active!");
|
||||
const char *resp_str = "WARNING: Flow start triggered by REST API, but flow is not active";
|
||||
httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN);
|
||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Flow start triggered by REST API, but flow is not active");
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
@@ -1348,9 +1364,8 @@ esp_err_t handler_editflow(httpd_req_t *req)
|
||||
std::string _ledi = std::string(_valuechar);
|
||||
if (isStringNumeric(_ledi))
|
||||
{
|
||||
float _ImageLedIntensity = std::stof(_valuechar);
|
||||
Camera.SetLEDIntensity(_ImageLedIntensity);
|
||||
CFstatus.ImageLedIntensity = CCstatus.ImageLedIntensity;
|
||||
int _ImageLedIntensity = std::stoi(_valuechar);
|
||||
CFstatus.ImageLedIntensity = Camera.SetLEDIntensity(_ImageLedIntensity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1487,6 +1502,28 @@ esp_err_t handler_rssi(httpd_req_t *req)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t handler_current_date(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_uptime - Start");
|
||||
#endif
|
||||
|
||||
std::string formatedDateAndTime = getCurrentTimeString("%Y-%m-%d %H:%M:%S");
|
||||
// std::string formatedDate = getCurrentTimeString("%Y-%m-%d");
|
||||
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
httpd_resp_send(req, formatedDateAndTime.c_str(), formatedDateAndTime.length());
|
||||
|
||||
/* Respond with an empty chunk to signal HTTP response completion */
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_uptime - End");
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t handler_uptime(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
@@ -1746,103 +1783,108 @@ void register_server_main_flow_task_uri(httpd_handle_t server)
|
||||
camuri.method = HTTP_GET;
|
||||
|
||||
camuri.uri = "/doinit";
|
||||
camuri.handler = handler_init;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_init);
|
||||
camuri.user_ctx = (void *)"Light On";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
// Legacy API => New: "/setPreValue"
|
||||
camuri.uri = "/setPreValue.html";
|
||||
camuri.handler = handler_prevalue;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_prevalue);
|
||||
camuri.user_ctx = (void *)"Prevalue";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/setPreValue";
|
||||
camuri.handler = handler_prevalue;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_prevalue);
|
||||
camuri.user_ctx = (void *)"Prevalue";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/flow_start";
|
||||
camuri.handler = handler_flow_start;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_flow_start);
|
||||
camuri.user_ctx = (void *)"Flow Start";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/statusflow.html";
|
||||
camuri.handler = handler_statusflow;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_statusflow);
|
||||
camuri.user_ctx = (void *)"Light Off";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/statusflow";
|
||||
camuri.handler = handler_statusflow;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_statusflow);
|
||||
camuri.user_ctx = (void *)"Light Off";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
// Legacy API => New: "/cpu_temperature"
|
||||
camuri.uri = "/cputemp.html";
|
||||
camuri.handler = handler_cputemp;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_cputemp);
|
||||
camuri.user_ctx = (void *)"Light Off";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/cpu_temperature";
|
||||
camuri.handler = handler_cputemp;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_cputemp);
|
||||
camuri.user_ctx = (void *)"Light Off";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
// Legacy API => New: "/rssi"
|
||||
camuri.uri = "/rssi.html";
|
||||
camuri.handler = handler_rssi;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_rssi);
|
||||
camuri.user_ctx = (void *)"Light Off";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/rssi";
|
||||
camuri.handler = handler_rssi;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_rssi);
|
||||
camuri.user_ctx = (void *)"Light Off";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/date";
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_current_date);
|
||||
camuri.user_ctx = (void *)"Light Off";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/uptime";
|
||||
camuri.handler = handler_uptime;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_uptime);
|
||||
camuri.user_ctx = (void *)"Light Off";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/editflow";
|
||||
camuri.handler = handler_editflow;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_editflow);
|
||||
camuri.user_ctx = (void *)"EditFlow";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
// Legacy API => New: "/value"
|
||||
camuri.uri = "/value.html";
|
||||
camuri.handler = handler_wasserzaehler;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_wasserzaehler);
|
||||
camuri.user_ctx = (void *)"Value";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/value";
|
||||
camuri.handler = handler_wasserzaehler;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_wasserzaehler);
|
||||
camuri.user_ctx = (void *)"Value";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
// Legacy API => New: "/value"
|
||||
camuri.uri = "/wasserzaehler.html";
|
||||
camuri.handler = handler_wasserzaehler;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_wasserzaehler);
|
||||
camuri.user_ctx = (void *)"Wasserzaehler";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/json";
|
||||
camuri.handler = handler_json;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_json);
|
||||
camuri.user_ctx = (void *)"JSON";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/heap";
|
||||
camuri.handler = handler_get_heap;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_get_heap);
|
||||
camuri.user_ctx = (void *)"Heap";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/stream";
|
||||
camuri.handler = handler_stream;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_stream);
|
||||
camuri.user_ctx = (void *)"stream";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
/** will handle metrics requests */
|
||||
camuri.uri = "/metrics";
|
||||
camuri.handler = handler_openmetrics;
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_openmetrics);
|
||||
camuri.user_ctx = (void *)"metrics";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ string toUpper(string in);
|
||||
|
||||
float temperatureRead();
|
||||
|
||||
std::string intToHexString(int _valueInt);
|
||||
time_t addDays(time_t startTime, int days);
|
||||
|
||||
void memCopyGen(uint8_t* _source, uint8_t* _target, int _size);
|
||||
|
||||
@@ -34,7 +34,7 @@ bool mqtt_initialized = false;
|
||||
bool mqtt_connected = false;
|
||||
|
||||
esp_mqtt_client_handle_t client = NULL;
|
||||
std::string uri, client_id, lwt_topic, lwt_connected, lwt_disconnected, user, password, maintopic;
|
||||
std::string uri, client_id, lwt_topic, lwt_connected, lwt_disconnected, user, password, maintopic, domoticz_in_topic;
|
||||
std::string caCert, clientCert, clientKey;
|
||||
int keepalive;
|
||||
bool SetRetainFlag;
|
||||
@@ -205,7 +205,7 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_
|
||||
|
||||
|
||||
bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password,
|
||||
std::string _maintopic, std::string _lwt, std::string _lwt_connected, std::string _lwt_disconnected,
|
||||
std::string _maintopic, std::string _domoticz_in_topic, std::string _lwt, std::string _lwt_connected, std::string _lwt_disconnected,
|
||||
std::string _cacertfilename, std::string _clientcertfilename, std::string _clientkeyfilename,
|
||||
int _keepalive, bool _SetRetainFlag, void *_callbackOnConnected) {
|
||||
if ((_mqttURI.length() == 0) || (_maintopic.length() == 0) || (_clientid.length() == 0))
|
||||
@@ -222,6 +222,7 @@ bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _us
|
||||
keepalive = _keepalive;
|
||||
SetRetainFlag = _SetRetainFlag;
|
||||
maintopic = _maintopic;
|
||||
domoticz_in_topic = _domoticz_in_topic;
|
||||
callbackOnConnected = ( void (*)(std::string, bool) )(_callbackOnConnected);
|
||||
|
||||
if (_clientcertfilename.length() && _clientkeyfilename.length()){
|
||||
@@ -388,13 +389,7 @@ bool mqtt_handler_flow_start(std::string _topic, char* _data, int _data_len)
|
||||
{
|
||||
ESP_LOGD(TAG, "Handler called: topic %s, data %.*s", _topic.c_str(), _data_len, _data);
|
||||
|
||||
if (_data_len > 0) {
|
||||
MQTTCtrlFlowStart(_topic);
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_flow_start: handler called, but no data");
|
||||
}
|
||||
|
||||
MQTTCtrlFlowStart(_topic);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <functional>
|
||||
|
||||
bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password,
|
||||
std::string _maintopic, std::string _lwt, std::string _lwt_connected, std::string _lwt_disconnected,
|
||||
std::string _maintopic, std::string _domoticz_in_topic, std::string _lwt, std::string _lwt_connected, std::string _lwt_disconnected,
|
||||
std::string _cacertfilename, std::string _clientcertfilename, std::string _clientkeyfilename,
|
||||
int _keepalive, bool SetRetainFlag, void *callbackOnConnected);
|
||||
int MQTT_Init();
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "interface_mqtt.h"
|
||||
#include "time_sntp.h"
|
||||
#include "../../include/defines.h"
|
||||
#include "basic_auth.h"
|
||||
|
||||
|
||||
|
||||
@@ -32,10 +33,11 @@ std::string rateUnit = "Unit/Minute";
|
||||
float roundInterval; // Minutes
|
||||
int keepAlive = 0; // Seconds
|
||||
bool retainFlag;
|
||||
static std::string maintopic;
|
||||
static std::string maintopic, domoticzintopic;
|
||||
bool sendingOf_DiscoveryAndStaticTopics_scheduled = true; // Set it to true to make sure it gets sent at least once after startup
|
||||
|
||||
|
||||
|
||||
void mqttServer_setParameter(std::vector<NumberPost*>* _NUMBERS, int _keepAlive, float _roundInterval) {
|
||||
NUMBERS = _NUMBERS;
|
||||
keepAlive = _keepAlive;
|
||||
@@ -85,9 +87,12 @@ bool sendHomeAssistantDiscoveryTopic(std::string group, std::string field,
|
||||
* This means a maintopic "home/test/watermeter" is transformed to the discovery topic "homeassistant/sensor/watermeter/..."
|
||||
*/
|
||||
std::string node_id = createNodeId(maintopic);
|
||||
if (field == "problem") { // Special binary sensor which is based on error topic
|
||||
if (field == "problem") { // Special case: Binary sensor which is based on error topic
|
||||
topicFull = "homeassistant/binary_sensor/" + node_id + "/" + configTopic + "/config";
|
||||
}
|
||||
else if (field == "flowstart") { // Special case: Button
|
||||
topicFull = "homeassistant/button/" + node_id + "/" + configTopic + "/config";
|
||||
}
|
||||
else {
|
||||
topicFull = "homeassistant/sensor/" + node_id + "/" + configTopic + "/config";
|
||||
}
|
||||
@@ -101,7 +106,7 @@ bool sendHomeAssistantDiscoveryTopic(std::string group, std::string field,
|
||||
"\"icon\": \"mdi:" + icon + "\",";
|
||||
|
||||
if (group != "") {
|
||||
if (field == "problem") { // Special binary sensor which is based on error topic
|
||||
if (field == "problem") { // Special case: Binary sensor which is based on error topic
|
||||
payload += "\"state_topic\": \"~/" + group + "/error\",";
|
||||
payload += "\"value_template\": \"{{ 'OFF' if 'no error' in value else 'ON'}}\",";
|
||||
}
|
||||
@@ -110,10 +115,13 @@ bool sendHomeAssistantDiscoveryTopic(std::string group, std::string field,
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (field == "problem") { // Special binary sensor which is based on error topic
|
||||
if (field == "problem") { // Special case: Binary sensor which is based on error topic
|
||||
payload += "\"state_topic\": \"~/error\",";
|
||||
payload += "\"value_template\": \"{{ 'OFF' if 'no error' in value else 'ON'}}\",";
|
||||
}
|
||||
else if (field == "flowstart") { // Special case: Button
|
||||
payload += "\"cmd_t\":\"~/ctrl/flow_start\","; // Add command topic
|
||||
}
|
||||
else {
|
||||
payload += "\"state_topic\": \"~/" + field + "\",";
|
||||
}
|
||||
@@ -176,6 +184,7 @@ bool MQTThomeassistantDiscovery(int qos) {
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "interval", "Interval", "clock-time-eight-outline", "min", "" , "measurement", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "IP", "IP", "network-outline", "", "", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "status", "Status", "list-status", "", "", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "flowstart", "Manual Flow Start", "timer-play-outline", "", "", "", "", qos);
|
||||
|
||||
|
||||
for (int i = 0; i < (*NUMBERS).size(); ++i) {
|
||||
@@ -184,14 +193,26 @@ bool MQTThomeassistantDiscovery(int qos) {
|
||||
group = "";
|
||||
}
|
||||
|
||||
/* If "Allow neg. rate" is true, use "measurement" instead of "total_increasing" for the State Class, see https://github.com/jomjol/AI-on-the-edge-device/issues/3331 */
|
||||
std::string value_state_class = "total_increasing";
|
||||
if ((*NUMBERS)[i]->AllowNegativeRates) {
|
||||
value_state_class = "measurement";
|
||||
}
|
||||
|
||||
/* Energy meters need a different Device Class, see https://github.com/jomjol/AI-on-the-edge-device/issues/3333 */
|
||||
std::string rate_device_class = "volume_flow_rate";
|
||||
if (meterType == "energy") {
|
||||
rate_device_class = "power";
|
||||
}
|
||||
|
||||
// Group | Field | User Friendly Name | Icon | Unit | Device Class | State Class | Entity Category
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "value", "Value", "gauge", valueUnit, meterType, "total_increasing", "", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "raw", "Raw Value", "raw", "", "", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "value", "Value", "gauge", valueUnit, meterType, value_state_class, "", qos); // State Class = "total_increasing" if <NUMBERS>.AllowNegativeRates = false, else use "measurement"
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "raw", "Raw Value", "raw", valueUnit, meterType, "measurement", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "error", "Error", "alert-circle-outline", "", "", "", "diagnostic", qos);
|
||||
/* Not announcing "rate" as it is better to use rate_per_time_unit resp. rate_per_Digitization_round */
|
||||
/* Not announcing "rate" as it is better to use rate_per_time_unit resp. rate_per_digitization_round */
|
||||
// allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate", "Rate (Unit/Minute)", "swap-vertical", "", "", "", ""); // Legacy, always Unit per Minute
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_time_unit", "Rate (" + rateUnit + ")", "swap-vertical", rateUnit, "", "measurement", "", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_Digitization_round", "Change since last Digitization round", "arrow-expand-vertical", valueUnit, "", "measurement", "", qos); // correctly the Unit is Unit/Interval!
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_time_unit", "Rate (" + rateUnit + ")", "swap-vertical", rateUnit, rate_device_class, "measurement", "", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_digitization_round", "Change since last Digitization round", "arrow-expand-vertical", valueUnit, "", "measurement", "", qos); // correctly the Unit is Unit/Interval!
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "timestamp", "Timestamp", "clock-time-eight-outline", "", "timestamp", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "json", "JSON", "code-json", "", "", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "problem", "Problem", "alert-outline", "", "problem", "", "", qos); // Special binary sensor which is based on error topic
|
||||
@@ -327,7 +348,7 @@ void register_server_mqtt_uri(httpd_handle_t server) {
|
||||
uri.method = HTTP_GET;
|
||||
|
||||
uri.uri = "/mqtt_publish_discovery";
|
||||
uri.handler = scheduleSendingDiscovery_and_static_Topics;
|
||||
uri.handler = APPLY_BASIC_AUTH_FILTER(scheduleSendingDiscovery_and_static_Topics);
|
||||
uri.user_ctx = (void*) "";
|
||||
httpd_register_uri_handler(server, &uri);
|
||||
}
|
||||
@@ -355,4 +376,9 @@ std::string mqttServer_getMainTopic() {
|
||||
return maintopic;
|
||||
}
|
||||
|
||||
void mqttServer_setDmoticzInTopic( std::string _domoticzintopic) {
|
||||
domoticzintopic = _domoticzintopic;
|
||||
}
|
||||
|
||||
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
@@ -12,6 +12,9 @@ void mqttServer_setParameter(std::vector<NumberPost*>* _NUMBERS, int interval, f
|
||||
void mqttServer_setMeterType(std::string meterType, std::string valueUnit, std::string timeUnit,std::string rateUnit);
|
||||
void setMqtt_Server_Retain(bool SetRetainFlag);
|
||||
void mqttServer_setMainTopic( std::string maintopic);
|
||||
void mqttServer_setDmoticzInTopic( std::string domoticzintopic);
|
||||
|
||||
|
||||
std::string mqttServer_getMainTopic();
|
||||
|
||||
void register_server_mqtt_uri(httpd_handle_t server);
|
||||
|
||||
@@ -246,9 +246,18 @@ void CTfLiteClass::GetInputTensorSize()
|
||||
|
||||
long CTfLiteClass::GetFileSize(std::string filename)
|
||||
{
|
||||
struct stat stat_buf;
|
||||
long rc = stat(filename.c_str(), &stat_buf);
|
||||
return rc == 0 ? stat_buf.st_size : -1;
|
||||
struct stat stat_buf;
|
||||
long rc = -1;
|
||||
|
||||
FILE *pFile = fopen(filename.c_str(), "rb"); // previously only "rb
|
||||
|
||||
if (pFile != NULL)
|
||||
{
|
||||
rc = stat(filename.c_str(), &stat_buf);
|
||||
fclose(pFile);
|
||||
}
|
||||
|
||||
return rc == 0 ? stat_buf.st_size : -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -270,25 +279,33 @@ bool CTfLiteClass::ReadFileToModel(std::string _fn)
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Loading Model " + _fn + " /size: " + std::to_string(size) + " bytes...");
|
||||
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("CTLiteClass::Alloc modelfile start");
|
||||
#endif
|
||||
|
||||
modelfile = (unsigned char*)psram_get_shared_model_memory();
|
||||
|
||||
if(modelfile != NULL)
|
||||
if (modelfile != NULL)
|
||||
{
|
||||
FILE* f = fopen(_fn.c_str(), "rb"); // previously only "r
|
||||
fread(modelfile, 1, size, f);
|
||||
fclose(f);
|
||||
FILE *pFile = fopen(_fn.c_str(), "rb"); // previously only "rb
|
||||
|
||||
if (pFile != NULL)
|
||||
{
|
||||
fread(modelfile, 1, size, pFile);
|
||||
fclose(pFile);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("CTLiteClass::Alloc modelfile successful");
|
||||
#endif
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("CTLiteClass::Alloc modelfile successful");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CTfLiteClass::ReadFileToModel: Model does not exist");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CTfLiteClass::ReadFileToModel: Can't allocate enough memory: " + std::to_string(size));
|
||||
|
||||
107
code/components/jomjol_wlan/basic_auth.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "basic_auth.h"
|
||||
#include "read_wlanini.h"
|
||||
#include <esp_tls_crypto.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
|
||||
#define HTTPD_401 "401 UNAUTHORIZED"
|
||||
|
||||
static const char *TAG = "HTTPAUTH";
|
||||
|
||||
typedef struct {
|
||||
const char *username;
|
||||
const char *password;
|
||||
} basic_auth_info_t;
|
||||
|
||||
basic_auth_info_t basic_auth_info = { NULL, NULL };
|
||||
|
||||
void init_basic_auth() {
|
||||
if (!wlan_config.http_username.empty() && !wlan_config.http_password.empty()) {
|
||||
basic_auth_info.username = wlan_config.http_username.c_str();
|
||||
basic_auth_info.password = wlan_config.http_password.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
static char *http_auth_basic(const char *username, const char *password)
|
||||
{
|
||||
int out;
|
||||
char *user_info = NULL;
|
||||
char *digest = NULL;
|
||||
size_t n = 0;
|
||||
asprintf(&user_info, "%s:%s", username, password);
|
||||
if (!user_info) {
|
||||
ESP_LOGE(TAG, "No enough memory for user information");
|
||||
return NULL;
|
||||
}
|
||||
esp_crypto_base64_encode(NULL, 0, &n, (const unsigned char *)user_info, strlen(user_info));
|
||||
|
||||
/* 6: The length of the "Basic " string
|
||||
* n: Number of bytes for a base64 encode format
|
||||
* 1: Number of bytes for a reserved which be used to fill zero
|
||||
*/
|
||||
digest = static_cast<char*>(calloc(1, 6 + n + 1));
|
||||
if (digest) {
|
||||
strcpy(digest, "Basic ");
|
||||
esp_crypto_base64_encode((unsigned char *)digest + 6, n, (size_t *)&out, (const unsigned char *)user_info, strlen(user_info));
|
||||
}
|
||||
free(user_info);
|
||||
return digest;
|
||||
}
|
||||
|
||||
esp_err_t basic_auth_request_filter(httpd_req_t *req, esp_err_t original_handler(httpd_req_t *))
|
||||
{
|
||||
char *buf = NULL;
|
||||
size_t buf_len = 0;
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
char unauthorized[] = "You are not authorized to use this website!";
|
||||
|
||||
if (basic_auth_info.username == NULL || basic_auth_info.password == NULL) {
|
||||
ret = original_handler(req);
|
||||
} else {
|
||||
buf_len = httpd_req_get_hdr_value_len(req, "Authorization") + 1;
|
||||
if (buf_len > 1) {
|
||||
buf = static_cast<char*>(calloc(1, buf_len));
|
||||
if (!buf) {
|
||||
ESP_LOGE(TAG, "No enough memory for basic authorization");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
if (httpd_req_get_hdr_value_str(req, "Authorization", buf, buf_len) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Found header => Authorization: %s", buf);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "No auth value received");
|
||||
}
|
||||
|
||||
char *auth_credentials = http_auth_basic(basic_auth_info.username, basic_auth_info.password);
|
||||
if (!auth_credentials) {
|
||||
ESP_LOGE(TAG, "No enough memory for basic authorization credentials");
|
||||
free(buf);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
if (strncmp(auth_credentials, buf, buf_len)) {
|
||||
ESP_LOGE(TAG, "Not authenticated");
|
||||
httpd_resp_set_status(req, HTTPD_401);
|
||||
httpd_resp_set_type(req, HTTPD_TYPE_TEXT);
|
||||
httpd_resp_set_hdr(req, "Connection", "keep-alive");
|
||||
httpd_resp_set_hdr(req, "WWW-Authenticate", "Basic realm=\"AIOTED\"");
|
||||
httpd_resp_send(req, unauthorized, strlen(unauthorized));
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Authenticated calling http handler now!");
|
||||
ret=original_handler(req);
|
||||
}
|
||||
free(auth_credentials);
|
||||
free(buf);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "No auth header received");
|
||||
httpd_resp_set_status(req, HTTPD_401);
|
||||
httpd_resp_set_type(req, HTTPD_TYPE_TEXT);
|
||||
httpd_resp_set_hdr(req, "Connection", "keep-alive");
|
||||
httpd_resp_set_hdr(req, "WWW-Authenticate", "Basic realm=\"AIOTED\"");
|
||||
httpd_resp_send(req, unauthorized, strlen(unauthorized));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
8
code/components/jomjol_wlan/basic_auth.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <esp_http_server.h>
|
||||
|
||||
void init_basic_auth();
|
||||
esp_err_t basic_auth_request_filter(httpd_req_t *req, esp_err_t original_handler(httpd_req_t *));
|
||||
|
||||
#define APPLY_BASIC_AUTH_FILTER(method) [](httpd_req_t *req){ return basic_auth_request_filter(req, method); }
|
||||
@@ -49,6 +49,9 @@
|
||||
#define ets_delay_us(a) esp_rom_delay_us(a)
|
||||
#endif
|
||||
|
||||
#include "../../esp-protocols/components/mdns/include/mdns.h"
|
||||
|
||||
|
||||
static const char *TAG = "WIFI";
|
||||
|
||||
static bool APWithBetterRSSI = false;
|
||||
@@ -657,6 +660,14 @@ esp_err_t wifi_init_sta(void)
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Set hostname to: " + wlan_config.hostname);
|
||||
}
|
||||
//initialize mDNS service
|
||||
retval = mdns_init();
|
||||
if (retval != ESP_OK) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mdns_init failed! Error: " + std::to_string(retval));
|
||||
} else {
|
||||
//set mdns hostname
|
||||
mdns_hostname_set(wlan_config.hostname.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Init successful");
|
||||
|
||||
@@ -145,6 +145,29 @@ int LoadWlanFromFile(std::string fn)
|
||||
wlan_config.dns = tmp;
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "DNS: " + wlan_config.dns);
|
||||
}
|
||||
|
||||
else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "HTTP_USERNAME")){
|
||||
tmp = splitted[1];
|
||||
if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){
|
||||
tmp = tmp.substr(1, tmp.length()-2);
|
||||
}
|
||||
wlan_config.http_username = tmp;
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "HTTP_USERNAME: " + wlan_config.http_username);
|
||||
}
|
||||
|
||||
else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "HTTP_PASSWORD")){
|
||||
tmp = splitted[1];
|
||||
if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){
|
||||
tmp = tmp.substr(1, tmp.length()-2);
|
||||
}
|
||||
wlan_config.http_password = tmp;
|
||||
#ifndef __HIDE_PASSWORD
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "HTTP_PASSWORD: " + wlan_config.http_password);
|
||||
#else
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "HTTP_PASSWORD: XXXXXXXX");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if (defined WLAN_USE_ROAMING_BY_SCANNING || (defined WLAN_USE_MESH_ROAMING && defined WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES))
|
||||
else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "RSSITHRESHOLD")){
|
||||
tmp = trim(splitted[1]);
|
||||
|
||||
@@ -13,6 +13,8 @@ struct wlan_config {
|
||||
std::string gateway = "";
|
||||
std::string netmask = "";
|
||||
std::string dns = "";
|
||||
std::string http_username = "";
|
||||
std::string http_password = "";
|
||||
int rssi_threshold = 0; // Default: 0 -> ROAMING disabled
|
||||
};
|
||||
extern struct wlan_config wlan_config;
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
dependencies:
|
||||
espressif/esp-nn:
|
||||
component_hash: b32869798bdb40dec6bc99caca48cd65d42f8a9f506b9ab9c598a076f891ede9
|
||||
component_hash: f6f2851ce82137a66e4265071c9b852bbe0130b882a18dea9f03faea7bf1295a
|
||||
source:
|
||||
pre_release: true
|
||||
service_url: https://api.components.espressif.com/
|
||||
type: service
|
||||
version: 1.0.2
|
||||
version: 1.1.0
|
||||
idf:
|
||||
component_hash: null
|
||||
source:
|
||||
type: idf
|
||||
version: 5.3.0
|
||||
manifest_hash: 6995555b9b41e897235448c868ca92c0c3401fd2ff90df084be9bb8629958f2c
|
||||
version: 5.3.1
|
||||
manifest_hash: 7350b157da8e1eb3cf21d0ea99443ec18c94cb2e0b22af07e20f286a9d15ec7a
|
||||
target: esp32
|
||||
version: 1.0.0
|
||||
|
||||
1
code/esp-protocols
Submodule
@@ -33,6 +33,8 @@
|
||||
#include "configFile.h"
|
||||
#include "server_main.h"
|
||||
#include "server_camera.h"
|
||||
#include "basic_auth.h"
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
#include "server_mqtt.h"
|
||||
#endif //ENABLE_MQTT
|
||||
@@ -77,10 +79,10 @@
|
||||
static heap_trace_record_t trace_record[NUM_RECORDS]; // This buffer must be in internal RAM
|
||||
#endif
|
||||
|
||||
extern const char* GIT_TAG;
|
||||
extern const char* GIT_REV;
|
||||
extern const char* GIT_BRANCH;
|
||||
extern const char* BUILD_TIME;
|
||||
extern const char *GIT_TAG;
|
||||
extern const char *GIT_REV;
|
||||
extern const char *GIT_BRANCH;
|
||||
extern const char *BUILD_TIME;
|
||||
|
||||
extern std::string getFwVersion(void);
|
||||
extern std::string getHTMLversion(void);
|
||||
@@ -107,27 +109,51 @@ bool Init_NVS_SDCard()
|
||||
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||
host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
|
||||
|
||||
// For SoCs where the SD power can be supplied both via an internal or external (e.g. on-board LDO) power supply.
|
||||
// When using specific IO pins (which can be used for ultra high-speed SDMMC) to connect to the SD card
|
||||
// and the internal LDO power supply, we need to initialize the power supply first.
|
||||
#if SD_PWR_CTRL_LDO_INTERNAL_IO
|
||||
sd_pwr_ctrl_ldo_config_t ldo_config = {
|
||||
.ldo_chan_id = CONFIG_EXAMPLE_SD_PWR_CTRL_LDO_IO_ID,
|
||||
};
|
||||
sd_pwr_ctrl_handle_t pwr_ctrl_handle = NULL;
|
||||
|
||||
ret = sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &pwr_ctrl_handle);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to create a new on-chip LDO power control driver");
|
||||
return ret;
|
||||
}
|
||||
host.pwr_ctrl_handle = pwr_ctrl_handle;
|
||||
#endif
|
||||
|
||||
// This initializes the slot without card detect (CD) and write protect (WP) signals.
|
||||
// Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
|
||||
#ifdef CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
|
||||
sdmmc_slot_config_t slot_config = {
|
||||
.cd = SDMMC_SLOT_NO_CD,
|
||||
.wp = SDMMC_SLOT_NO_WP,
|
||||
};
|
||||
#else
|
||||
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||
#endif
|
||||
|
||||
// Set bus width to use:
|
||||
#ifdef __SD_USE_ONE_LINE_MODE__
|
||||
slot_config.width = 1;
|
||||
#ifdef SOC_SDMMC_USE_GPIO_MATRIX
|
||||
#ifdef CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
|
||||
slot_config.clk = GPIO_SDCARD_CLK;
|
||||
slot_config.cmd = GPIO_SDCARD_CMD;
|
||||
slot_config.d0 = GPIO_SDCARD_D0;
|
||||
#endif
|
||||
|
||||
#else
|
||||
#endif // end CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
|
||||
#else // else __SD_USE_ONE_LINE_MODE__
|
||||
slot_config.width = 4;
|
||||
#ifdef SOC_SDMMC_USE_GPIO_MATRIX
|
||||
#ifdef CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
|
||||
slot_config.d1 = GPIO_SDCARD_D1;
|
||||
slot_config.d2 = GPIO_SDCARD_D2;
|
||||
slot_config.d3 = GPIO_SDCARD_D3;
|
||||
#endif
|
||||
#endif
|
||||
#endif // end CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
|
||||
#endif // end __SD_USE_ONE_LINE_MODE__
|
||||
|
||||
// Enable internal pullups on enabled pins. The internal pullups
|
||||
// are insufficient however, please make sure 10k external pullups are
|
||||
@@ -429,6 +455,8 @@ extern "C" void app_main(void)
|
||||
StatusLED(WLAN_INIT, 3, true);
|
||||
return;
|
||||
}
|
||||
|
||||
init_basic_auth();
|
||||
}
|
||||
else if (iWLANStatus == -1) { // wlan.ini not available, potentially empty or content not readable
|
||||
StatusLED(WLAN_INIT, 1, true);
|
||||
@@ -600,27 +628,57 @@ void migrateConfiguration(void) {
|
||||
}
|
||||
else if ((isInString(configLines[i], "Zoom")) && (!isInString(configLines[i], "CamZoom")) && (!isInString(configLines[i], "ZoomMode")) && (!isInString(configLines[i], "ZoomOffsetX")) && (!isInString(configLines[i], "ZoomOffsetY"))) {
|
||||
CamZoom_lines = i;
|
||||
CamZoom_value = alphanumericToBoolean(splitted[1]);
|
||||
if (splitted.size() < 2) {
|
||||
CamZoom_value = false;
|
||||
}
|
||||
else {
|
||||
ESP_LOGE(TAG, "splitted[1]: %s", splitted[1].c_str());
|
||||
CamZoom_value = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
CamZoom_found = true;
|
||||
}
|
||||
else if ((isInString(configLines[i], "ZoomMode")) && (!isInString(configLines[i], "CamZoom"))) {
|
||||
CamZoomSize_lines = i;
|
||||
if (isStringNumeric(splitted[1])) {
|
||||
CamZoomSize_value = std::stof(splitted[1]);
|
||||
if (splitted.size() < 2) {
|
||||
CamZoomSize_value = 0;
|
||||
}
|
||||
else {
|
||||
if (isStringNumeric(splitted[1])) {
|
||||
CamZoomSize_value = std::stof(splitted[1]);
|
||||
}
|
||||
else {
|
||||
CamZoomSize_value = 0;
|
||||
}
|
||||
}
|
||||
CamZoom_found = true;
|
||||
}
|
||||
else if ((isInString(configLines[i], "ZoomOffsetX")) && (!isInString(configLines[i], "CamZoom")) && (!isInString(configLines[i], "ZoomOffsetY"))) {
|
||||
CamZoomOffsetX_lines = i;
|
||||
if (isStringNumeric(splitted[1])) {
|
||||
CamZoomOffsetX_value = std::stof(splitted[1]);
|
||||
if (splitted.size() < 2) {
|
||||
CamZoomOffsetX_value = 0;
|
||||
}
|
||||
else {
|
||||
if (isStringNumeric(splitted[1])) {
|
||||
CamZoomOffsetX_value = std::stof(splitted[1]);
|
||||
}
|
||||
else {
|
||||
CamZoomOffsetX_value = 0;
|
||||
}
|
||||
}
|
||||
CamZoom_found = true;
|
||||
}
|
||||
else if ((isInString(configLines[i], "ZoomOffsetY")) && (!isInString(configLines[i], "CamZoom")) && (!isInString(configLines[i], "ZoomOffsetX"))) {
|
||||
CamZoomOffsetY_lines = i;
|
||||
if (isStringNumeric(splitted[1])) {
|
||||
CamZoomOffsetY_value = std::stof(splitted[1]);
|
||||
if (splitted.size() < 2) {
|
||||
CamZoomOffsetY_value = 0;
|
||||
}
|
||||
else {
|
||||
if (isStringNumeric(splitted[1])) {
|
||||
CamZoomOffsetY_value = std::stof(splitted[1]);
|
||||
}
|
||||
else {
|
||||
CamZoomOffsetY_value = 0;
|
||||
}
|
||||
}
|
||||
CamZoom_found = true;
|
||||
}
|
||||
@@ -727,8 +785,7 @@ void migrateConfiguration(void) {
|
||||
}
|
||||
else if (section == "[AutoTimer]") {
|
||||
migrated = migrated | replaceString(configLines[i], "Intervall", "Interval");
|
||||
migrated = migrated | replaceString(configLines[i], ";AutoStart = true", ";AutoStart = false"); // Set it to its default value
|
||||
migrated = migrated | replaceString(configLines[i], ";AutoStart", "AutoStart"); // Enable it
|
||||
migrated = migrated | replaceString(configLines[i], "Autostart", ";UNUSED_PARAMETER"); // This parameter is no longer used
|
||||
}
|
||||
else if (section == "[Debug]") {
|
||||
migrated = migrated | replaceString(configLines[i], "Logfile ", "LogLevel "); // Whitespace needed so it does not match `LogfileRetentionInDays`
|
||||
@@ -741,7 +798,7 @@ void migrateConfiguration(void) {
|
||||
else if (section == "[System]") {
|
||||
migrated = migrated | replaceString(configLines[i], "RSSIThreashold", "RSSIThreshold");
|
||||
migrated = migrated | replaceString(configLines[i], "AutoAdjustSummertime", ";UNUSED_PARAMETER"); // This parameter is no longer used
|
||||
|
||||
|
||||
migrated = migrated | replaceString(configLines[i], ";SetupMode = true", ";SetupMode = false"); // Set it to its default value
|
||||
migrated = migrated | replaceString(configLines[i], ";SetupMode", "SetupMode"); // Enable it
|
||||
}
|
||||
@@ -910,7 +967,12 @@ bool setCpuFrequency(void) {
|
||||
splitted = ZerlegeZeile(line);
|
||||
|
||||
if (toUpper(splitted[0]) == "CPUFREQUENCY") {
|
||||
cpuFrequency = splitted[1];
|
||||
if (splitted.size() < 2) {
|
||||
cpuFrequency = 160;
|
||||
}
|
||||
else {
|
||||
cpuFrequency = splitted[1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "MainFlowControl.h"
|
||||
#include "esp_log.h"
|
||||
#include "basic_auth.h"
|
||||
#include "esp_chip_info.h"
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -408,7 +409,7 @@ void register_server_main_uri(httpd_handle_t server, const char *base_path)
|
||||
httpd_uri_t info_get_handle = {
|
||||
.uri = "/info", // Match all URIs of type /path/to/file
|
||||
.method = HTTP_GET,
|
||||
.handler = info_get_handler,
|
||||
.handler = APPLY_BASIC_AUTH_FILTER(info_get_handler),
|
||||
.user_ctx = (void*) base_path // Pass server data as context
|
||||
};
|
||||
httpd_register_uri_handler(server, &info_get_handle);
|
||||
@@ -416,7 +417,7 @@ void register_server_main_uri(httpd_handle_t server, const char *base_path)
|
||||
httpd_uri_t sysinfo_handle = {
|
||||
.uri = "/sysinfo", // Match all URIs of type /path/to/file
|
||||
.method = HTTP_GET,
|
||||
.handler = sysinfo_handler,
|
||||
.handler = APPLY_BASIC_AUTH_FILTER(sysinfo_handler),
|
||||
.user_ctx = (void*) base_path // Pass server data as context
|
||||
};
|
||||
httpd_register_uri_handler(server, &sysinfo_handle);
|
||||
@@ -424,7 +425,7 @@ void register_server_main_uri(httpd_handle_t server, const char *base_path)
|
||||
httpd_uri_t starttime_tmp_handle = {
|
||||
.uri = "/starttime", // Match all URIs of type /path/to/file
|
||||
.method = HTTP_GET,
|
||||
.handler = starttime_get_handler,
|
||||
.handler = APPLY_BASIC_AUTH_FILTER(starttime_get_handler),
|
||||
.user_ctx = NULL // Pass server data as context
|
||||
};
|
||||
httpd_register_uri_handler(server, &starttime_tmp_handle);
|
||||
@@ -432,7 +433,7 @@ void register_server_main_uri(httpd_handle_t server, const char *base_path)
|
||||
httpd_uri_t img_tmp_handle = {
|
||||
.uri = "/img_tmp/*", // Match all URIs of type /path/to/file
|
||||
.method = HTTP_GET,
|
||||
.handler = img_tmp_virtual_handler,
|
||||
.handler = APPLY_BASIC_AUTH_FILTER(img_tmp_virtual_handler),
|
||||
.user_ctx = (void*) base_path // Pass server data as context
|
||||
};
|
||||
httpd_register_uri_handler(server, &img_tmp_handle);
|
||||
@@ -440,7 +441,7 @@ void register_server_main_uri(httpd_handle_t server, const char *base_path)
|
||||
httpd_uri_t main_rest_handle = {
|
||||
.uri = "/*", // Match all URIs of type /path/to/file
|
||||
.method = HTTP_GET,
|
||||
.handler = hello_main_handler,
|
||||
.handler = APPLY_BASIC_AUTH_FILTER(hello_main_handler),
|
||||
.user_ctx = (void*) base_path // Pass server data as context
|
||||
};
|
||||
httpd_register_uri_handler(server, &main_rest_handle);
|
||||
@@ -459,7 +460,7 @@ httpd_handle_t start_webserver(void)
|
||||
config.server_port = 80;
|
||||
config.ctrl_port = 32768;
|
||||
config.max_open_sockets = 5; //20210921 --> previously 7
|
||||
config.max_uri_handlers = 40; // Make sure this fits all URI handlers. Memory usage in bytes: 6*max_uri_handlers
|
||||
config.max_uri_handlers = 41; // Make sure this fits all URI handlers. Memory usage in bytes: 6*max_uri_handlers
|
||||
config.max_resp_headers = 8;
|
||||
config.backlog_conn = 5;
|
||||
config.lru_purge_enable = true; // this cuts old connections if new ones are needed.
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "Helper.h"
|
||||
#include "statusled.h"
|
||||
#include "server_ota.h"
|
||||
#include "basic_auth.h"
|
||||
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sys.h"
|
||||
@@ -98,8 +99,8 @@ void wifi_init_softAP(void)
|
||||
void SendHTTPResponse(httpd_req_t *req)
|
||||
{
|
||||
std::string message = "<h1>AI-on-the-edge - BASIC SETUP</h1><p>This is an access point with a minimal server to setup the minimum required files and information on the device and the SD-card. ";
|
||||
message += "This mode is always startet if one of the following files is missing: /wlan.ini or the /config/config.ini.<p>";
|
||||
message += "The setup is done in 3 steps: 1. upload full inital configuration (sd-card content), 2. store WLAN acces information, 3. reboot (and connect to WLANs)<p><p>";
|
||||
message += "This mode is always started if one of the following files is missing: /wlan.ini or the /config/config.ini.<p>";
|
||||
message += "The setup is done in 3 steps: 1. upload full inital configuration (sd-card content), 2. store WLAN access information, 3. reboot (and connect to WLANs)<p><p>";
|
||||
message += "Please follow the below instructions.<p>";
|
||||
httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str()));
|
||||
|
||||
@@ -109,7 +110,7 @@ void SendHTTPResponse(httpd_req_t *req)
|
||||
{
|
||||
message = "<h3>1. Upload initial configuration to sd-card</h3><p>";
|
||||
message += "The configuration file config.ini is missing and most propably the full configuration and html folder on the sd-card. ";
|
||||
message += "This is normal after the first flashing of the firmware and an empty sd-card. Please upload \"remote_setup.zip\", which contains an full inital configuration.<p>";
|
||||
message += "This is normal after the first flashing of the firmware and an empty sd-card. Please upload \"remote_setup.zip\", which contains a full inital configuration.<p>";
|
||||
message += "<input id=\"newfile\" type=\"file\"><br>";
|
||||
message += "<button class=\"button\" style=\"width:300px\" id=\"doUpdate\" type=\"button\" onclick=\"upload()\">Upload File</button><p>";
|
||||
message += "The upload might take up to 60s. After a succesfull upload the page will be updated.";
|
||||
@@ -135,7 +136,7 @@ void SendHTTPResponse(httpd_req_t *req)
|
||||
message += "<tr><td>WLAN-SSID</td><td><input type=\"text\" name=\"ssid\" id=\"ssid\"></td><td>SSID of the WLAN</td></tr>";
|
||||
message += "<tr><td>WLAN-Password</td><td><input type=\"text\" name=\"password\" id=\"password\"></td><td>ATTENTION: the password will not be encrypted during the sending.</td><tr>";
|
||||
message += "</table><p>";
|
||||
message += "<h4>ATTENTION:<h4>Be sure about the WLAN settings. They cannot be reseted afterwards. If ssid or password is wrong, you need to take out the sd-card and manually change them in \"wlan.ini\"!<p>";
|
||||
message += "<h4>ATTENTION:<h4>Be sure about the WLAN settings. They cannot be reset afterwards. If ssid or password is wrong, you need to take out the sd-card and manually change them in \"wlan.ini\"!<p>";
|
||||
httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str()));
|
||||
|
||||
// message = "</tr><tr><td> Hostname</td><td><input type=\"text\" name=\"hostname\" id=\"hostname\"></td><td></td>";
|
||||
@@ -468,7 +469,7 @@ httpd_handle_t start_webserverAP(void)
|
||||
httpd_uri_t reboot_handle = {
|
||||
.uri = "/reboot", // Match all URIs of type /path/to/file
|
||||
.method = HTTP_GET,
|
||||
.handler = reboot_handlerAP,
|
||||
.handler = APPLY_BASIC_AUTH_FILTER(reboot_handlerAP),
|
||||
.user_ctx = NULL // Pass server data as context
|
||||
};
|
||||
httpd_register_uri_handler(server, &reboot_handle);
|
||||
@@ -476,7 +477,7 @@ httpd_handle_t start_webserverAP(void)
|
||||
httpd_uri_t config_ini_handle = {
|
||||
.uri = "/config", // Match all URIs of type /path/to/file
|
||||
.method = HTTP_GET,
|
||||
.handler = config_ini_handler,
|
||||
.handler = APPLY_BASIC_AUTH_FILTER(config_ini_handler),
|
||||
.user_ctx = NULL // Pass server data as context
|
||||
};
|
||||
httpd_register_uri_handler(server, &config_ini_handle);
|
||||
@@ -485,7 +486,7 @@ httpd_handle_t start_webserverAP(void)
|
||||
httpd_uri_t file_uploadAP = {
|
||||
.uri = "/upload/*", // Match all URIs of type /upload/path/to/file
|
||||
.method = HTTP_POST,
|
||||
.handler = upload_post_handlerAP,
|
||||
.handler = APPLY_BASIC_AUTH_FILTER(upload_post_handlerAP),
|
||||
.user_ctx = NULL // Pass server data as context
|
||||
};
|
||||
httpd_register_uri_handler(server, &file_uploadAP);
|
||||
@@ -493,7 +494,7 @@ httpd_handle_t start_webserverAP(void)
|
||||
httpd_uri_t test_uri = {
|
||||
.uri = "*",
|
||||
.method = HTTP_GET,
|
||||
.handler = test_handler,
|
||||
.handler = APPLY_BASIC_AUTH_FILTER(test_handler),
|
||||
.user_ctx = NULL
|
||||
};
|
||||
httpd_register_uri_handler(server, &test_uri);
|
||||
|
||||
@@ -3,6 +3,29 @@
|
||||
<link rel="icon" href="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/icon/favicon.ico" type="image/x-icon">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||
<script type="module" src="https://unpkg.com/esp-web-tools@9.0.3/dist/web/install-button.js?module"></script>
|
||||
<style>
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
background-color: #d8d8d8;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.footer-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.footer-section img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.donation-cards img {
|
||||
height: 20px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
</style>
|
||||
<body style="padding: 20px; padding-left: 60px; padding-right: 60px;">
|
||||
|
||||
<table>
|
||||
@@ -34,5 +57,27 @@
|
||||
<p><esp-web-install-button manifest="manifest.json"></esp-web-install-button></p>
|
||||
<hr>
|
||||
<p style="font-size: small;">Installer and Console powered by <a href=https://esphome.github.io/esp-web-tools/ target=_blank>ESP Web Tools</a></p>
|
||||
|
||||
<div class="footer">
|
||||
<div class="footer-section">
|
||||
<span>Support & Contact Us</span>
|
||||
<a href="https://github.com/jomjol/AI-on-the-edge-device" target="_blank" title="GitHub">
|
||||
<img src="https://github.com/jomjol/AI-on-the-edge-device/images/github-logo.png" alt="GitHub">
|
||||
</a>
|
||||
<img src="https://github.com/jomjol/AI-on-the-edge-device/images/gmail-logo.png" alt="Email">
|
||||
</a>
|
||||
<a href="https://github.com/jomjol/AI-on-the-edge-device/discussions" target="_blank" title="GitHub">
|
||||
<img src="https://github.com/jomjol/AI-on-the-edge-device/images/discussion-logo" alt="GitHub">
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<div class="footer-section">
|
||||
<span>Donations</span>
|
||||
<a href="https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL" target="_blank" title="Donate via PayPal">
|
||||
<img src="https://github.com/jomjol/AI-on-the-edge-device/images/paypal.png" alt="PayPal" style="width: 60px; height: auto;">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
BIN
images/discussion-logo.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
images/github-logo.png
Normal file
|
After Width: | Height: | Size: 509 B |
BIN
images/gmail-logo.png
Normal file
|
After Width: | Height: | Size: 514 B |
@@ -2,11 +2,4 @@
|
||||
Default Value: `true`
|
||||
|
||||
!!! Warning
|
||||
This is an **Expert Parameter**! Only change it if you understand what it does!
|
||||
|
||||
Automatically start the Flow (Digitization Rounds) immediately after power up.
|
||||
|
||||
!!! Note
|
||||
Typically this is set to `true`.
|
||||
The main reasons to set it to `false` is when you want to trigger it manually using the
|
||||
[REST API](../REST-API) or [MQTT-API](../MQTT-API) or for debugging.
|
||||
This parameter is no longer available. The flow is now always enabled. If you want it to be disabled, set an interval which is high enough (eg. 1440 = 24h).
|
||||
|
||||
@@ -4,4 +4,10 @@ Default Value: `5`
|
||||
Unit: Minutes
|
||||
|
||||
Interval in which the Flow (Digitization Round) is run.
|
||||
It will run immediately on startup and then the next time after the given interval.
|
||||
If a round takes longer than this interval, the next round gets postponed until the current round completes.
|
||||
|
||||
If the flow gets started by a MQTT message or the REST API call, the interval automatically gets reset.
|
||||
|
||||
!!! Note
|
||||
If you want the flow to be disabled, set an interval which is high enough (eg. 1440 = 24h).
|
||||
|
||||
6
param-docs/parameter-pages/MQTT/DomoticzTopicIn.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Parameter `DomoticzTopicIn`
|
||||
Default Value: `domoticz/in`
|
||||
|
||||
Domoticz "in" topic as configured on the "MQTT Client Gateway" setup page on the Domoticz system. Used to publish counter/s value/s.
|
||||
|
||||
Parameter <NUMBER>.DomoticzIDX is required (see below).
|
||||
4
param-docs/parameter-pages/MQTT/NUMBER.DomoticzIDX.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Parameter `<NUMBER>.DomoticzIDX`
|
||||
Default Value: `0`
|
||||
|
||||
The Idx number for the counter device. Can be obtained from the devices setup page on the Domoticz system.
|
||||
@@ -9,17 +9,17 @@ This parameter is intended to compensate for small reading fluctuations that occ
|
||||
It is only applied to the last digit of the read value (See example below).
|
||||
If the read value is within PreValue +/- Threshold, no further calculation is carried out and the Value/Prevalue remains at the old value.
|
||||
|
||||
Example:
|
||||
## Example
|
||||
|
||||
Smallest ROI provides value for 0.000x
|
||||
ChangeRateThreshold = 2
|
||||
- Smallest ROI provides value for `0.000'x` (Eg. a water meter with 4 pointers behind the decimal point)
|
||||
- ChangeRateThreshold = 2
|
||||
|
||||
Extended Resolution disabled:
|
||||
PreValue: 123.456'7 >>> Threshold = +/- 0.000'2
|
||||
Comparative value >>> max = 123.456'9 and min = 123.456'5
|
||||
#### With `Extended Resolution` **disabled**
|
||||
PreValue: `123.456'7` -> Threshold = `+/-0.000'2`.<br>
|
||||
All changes between `123.456'5` and `123.456'9` get ignored
|
||||
|
||||
Extended Resolution enabled:
|
||||
PreValue: 123.456'78 >>> Threshold = +/- 0.000'02
|
||||
Comparative value >>> max = 123.456'80 and min = 123.456'76
|
||||
#### With `Extended Resolution` **enabled**
|
||||
PreValue: `123.456'78` -> Threshold = `+/-0.000'02`.<br>
|
||||
All changes between `123.456'76` and `123.456'80` get ignored.
|
||||
|
||||

|
||||
|
||||
BIN
sd-card/config/ana-cont_1400_s2_q.tflite
Normal file
@@ -38,12 +38,12 @@ Demo = false
|
||||
InitialRotate = 0.0
|
||||
SearchFieldX = 20
|
||||
SearchFieldY = 20
|
||||
AlignmentAlgo = Default
|
||||
AlignmentAlgo = default
|
||||
/config/ref0.jpg 103 271
|
||||
/config/ref1.jpg 442 142
|
||||
|
||||
[Digits]
|
||||
Model = /config/dig-cont_0710_s3_q.tflite
|
||||
Model = /config/dig-cont_0712_s3_q.tflite
|
||||
CNNGoodThreshold = 0.5
|
||||
;ROIImagesLocation = /log/digit
|
||||
;ROIImagesRetention = 3
|
||||
@@ -87,20 +87,23 @@ HomeassistantDiscovery = false
|
||||
;CACert = /config/certs/RootCA.pem
|
||||
;ClientCert = /config/certs/client.pem.crt
|
||||
;ClientKey = /config/certs/client.pem.key
|
||||
;DomoticzTopicIn = domoticz/in
|
||||
;main.DomoticzIDX = 0
|
||||
|
||||
;[InfluxDB]
|
||||
;Uri = undefined
|
||||
;Database = undefined
|
||||
;Measurement = undefined
|
||||
;user = undefined
|
||||
;password = undefined
|
||||
;main.Measurement = undefined
|
||||
;main.Field = undefined
|
||||
|
||||
;[InfluxDBv2]
|
||||
;Uri = undefined
|
||||
;Bucket = undefined
|
||||
;Measurement = undefined
|
||||
;Org = undefined
|
||||
;Token = undefined
|
||||
;main.Measurement = undefined
|
||||
;main.Field = undefined
|
||||
|
||||
;[Webhook]
|
||||
@@ -115,13 +118,12 @@ HomeassistantDiscovery = false
|
||||
;IO3 = input disabled 10 false false
|
||||
;IO4 = built-in-led disabled 10 false false
|
||||
;IO12 = input-pullup disabled 10 false false
|
||||
;IO13 = input-pullup disabled 10 false false
|
||||
;IO13 = input-pullup disabled 10 false false
|
||||
LEDType = WS2812
|
||||
LEDNumbers = 2
|
||||
LEDColor = 150 150 150
|
||||
|
||||
[AutoTimer]
|
||||
AutoStart = true
|
||||
Interval = 5
|
||||
|
||||
[DataLogging]
|
||||
@@ -138,4 +140,5 @@ TimeZone = CET-1CEST,M3.5.0,M10.5.0/3
|
||||
;Hostname = undefined
|
||||
RSSIThreshold = -75
|
||||
CPUFrequency = 160
|
||||
Tooltip = true
|
||||
SetupMode = true
|
||||
|
||||
BIN
sd-card/config/dig-class11_1900_s2_q.tflite
Normal file
BIN
sd-card/config/dig-class11_1910_s2_q.tflite
Normal file
BIN
sd-card/config/dig-cont_0712_s3_q.tflite
Normal file
BIN
sd-card/config/dig-cont_0800_s3_q.tflite
Normal file
BIN
sd-card/config/dig-cont_0810_s3_q.tflite
Normal file
BIN
sd-card/demo/530.07077.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
sd-card/demo/530.07325.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
sd-card/demo/530.12067.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
sd-card/demo/530.21419.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
sd-card/demo/530.48435.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
sd-card/demo/530.70265.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
sd-card/demo/530.95675.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
sd-card/demo/531.10877.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
sd-card/demo/531.24108.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
sd-card/demo/531.38301.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
sd-card/demo/531.63071.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
sd-card/demo/531.82235.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
136
sd-card/demo/config.ini
Normal file
@@ -0,0 +1,136 @@
|
||||
[TakeImage]
|
||||
;RawImagesLocation = /log/source
|
||||
;RawImagesRetention = 15
|
||||
WaitBeforeTakingPicture = 2
|
||||
CamGainceiling = x8
|
||||
CamQuality = 10
|
||||
CamBrightness = 0
|
||||
CamContrast = 0
|
||||
CamSaturation = 0
|
||||
CamSharpness = 0
|
||||
CamAutoSharpness = false
|
||||
CamSpecialEffect = no_effect
|
||||
CamWbMode = auto
|
||||
CamAwb = true
|
||||
CamAwbGain = true
|
||||
CamAec = true
|
||||
CamAec2 = true
|
||||
CamAeLevel = 2
|
||||
CamAecValue = 600
|
||||
CamAgc = true
|
||||
CamAgcGain = 8
|
||||
CamBpc = true
|
||||
CamWpc = true
|
||||
CamRawGma = true
|
||||
CamLenc = true
|
||||
CamHmirror = false
|
||||
CamVflip = false
|
||||
CamDcw = true
|
||||
CamDenoise = 0
|
||||
CamZoom = false
|
||||
CamZoomOffsetX = 0
|
||||
CamZoomOffsetY = 0
|
||||
CamZoomSize = 0
|
||||
LEDIntensity = 0
|
||||
Demo = true
|
||||
|
||||
[Alignment]
|
||||
InitialRotate = -34.6
|
||||
SearchFieldX = 20
|
||||
SearchFieldY = 20
|
||||
AlignmentAlgo = default
|
||||
/config/ref0.jpg 30 189
|
||||
/config/ref1.jpg 536 113
|
||||
|
||||
[Digits]
|
||||
Model = /config/dig-cont_0710_s3_q.tflite
|
||||
CNNGoodThreshold = 0.5
|
||||
;ROIImagesLocation = /log/digit
|
||||
;ROIImagesRetention = 3
|
||||
main.dig1 438 62 49 71 false
|
||||
|
||||
[Analog]
|
||||
Model = /config/ana-cont_1300_s2.tflite
|
||||
;ROIImagesLocation = /log/analog
|
||||
;ROIImagesRetention = 3
|
||||
main.ana1 452 199 120 120 false
|
||||
|
||||
[PostProcessing]
|
||||
main.DecimalShift = 0
|
||||
;main.AnalogToDigitTransitionStart =
|
||||
main.ChangeRateThreshold = 2
|
||||
PreValueUse = true
|
||||
PreValueAgeStartup = 720
|
||||
main.AllowNegativeRates = true
|
||||
;main.MaxRateValue = 0
|
||||
;main.MaxRateType = AbsoluteChange
|
||||
main.ExtendedResolution = true
|
||||
main.IgnoreLeadingNaN = false
|
||||
ErrorMessage = true
|
||||
CheckDigitIncreaseConsistency = false
|
||||
|
||||
;[MQTT]
|
||||
;Uri = mqtt://IP-ADRESS:1883
|
||||
;MainTopic = watermeter
|
||||
;ClientID = watermeter
|
||||
;user = USERNAME
|
||||
;password = PASSWORD
|
||||
RetainMessages = false
|
||||
HomeassistantDiscovery = false
|
||||
;MeterType = other
|
||||
;CACert = /config/certs/RootCA.pem
|
||||
;ClientCert = /config/certs/client.pem.crt
|
||||
;ClientKey = /config/certs/client.pem.key
|
||||
|
||||
;[InfluxDB]
|
||||
;Uri = undefined
|
||||
;Database = undefined
|
||||
;user = undefined
|
||||
;password = undefined
|
||||
;main.Measurement = undefined
|
||||
;main.Field =
|
||||
|
||||
;[InfluxDBv2]
|
||||
;Uri = undefined
|
||||
;Bucket = undefined
|
||||
;Org = undefined
|
||||
;Token = undefined
|
||||
;main.Measurement = undefined
|
||||
;main.Field = undefined
|
||||
|
||||
;[Webhook]
|
||||
;Uri = undefined
|
||||
;ApiKey = undefined
|
||||
;UploadImg = 0
|
||||
|
||||
;[GPIO]
|
||||
;IO0 = input disabled 10 false false
|
||||
;IO1 = input disabled 10 false false
|
||||
;IO3 = input disabled 10 false false
|
||||
;IO4 = built-in-led disabled 10 false false
|
||||
;IO12 = input-pullup disabled 10 false false
|
||||
;IO13 = input-pullup disabled 10 false false
|
||||
LEDType = WS2812
|
||||
LEDNumbers = 2
|
||||
LEDColor = 150 150 150
|
||||
|
||||
[AutoTimer]
|
||||
Interval = 1
|
||||
|
||||
[DataLogging]
|
||||
DataLogActive = false
|
||||
DataFilesRetention = 3
|
||||
|
||||
[Debug]
|
||||
LogLevel = 3
|
||||
LogfilesRetention = 3
|
||||
|
||||
[System]
|
||||
Tooltip = true
|
||||
TimeZone = CET-1CEST,M3.5.0,M10.5.0/3
|
||||
;TimeServer = pool.ntp.org
|
||||
;Hostname = undefined
|
||||
RSSIThreshold = -75
|
||||
CPUFrequency = 160
|
||||
SetupMode = false
|
||||
|
||||
12
sd-card/demo/files.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
530.07077.jpg
|
||||
530.07325.jpg
|
||||
530.12067.jpg
|
||||
530.21419.jpg
|
||||
530.48435.jpg
|
||||
530.70265.jpg
|
||||
530.95675.jpg
|
||||
531.10877.jpg
|
||||
531.24108.jpg
|
||||
531.38301.jpg
|
||||
531.63071.jpg
|
||||
531.82235.jpg
|
||||
1
sd-card/demo/prevalue.ini
Normal file
@@ -0,0 +1 @@
|
||||
main 2024-10-06T23:02:42+0200 0.05
|
||||
12
sd-card/demo/readme.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
This folder contains a prepared demo setup.
|
||||
|
||||
All you need to do is:
|
||||
1. Copy the following files to the config folder of your SD-Card:
|
||||
- config.ini
|
||||
- ref0.jpg
|
||||
- ref1.jpg
|
||||
- reference.jpg
|
||||
- prevalue.ini
|
||||
1. Restart the device
|
||||
|
||||
More details at https://jomjol.github.io/AI-on-the-edge-device-docs/Demo-Mode
|
||||
BIN
sd-card/demo/ref0.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
sd-card/demo/ref1.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
sd-card/demo/reference.jpg
Normal file
|
After Width: | Height: | Size: 34 KiB |
@@ -504,7 +504,7 @@
|
||||
|
||||
<tr class="expert" unused_id="TakeImage_CamAgcGain_ex3">
|
||||
<td class="indent1">
|
||||
<class id="TakeImage_CamAgcGain_text" style="color:black;">Gain Manuall Value</class>
|
||||
<class id="TakeImage_CamAgcGain_text" style="color:black;">Gain Manual Value</class>
|
||||
</td>
|
||||
<td>
|
||||
<input required type="number" id="TakeImage_CamAgcGain_value1" size="13" min="0" max="30" onchange="cameraParameterChanged()"
|
||||
@@ -933,7 +933,7 @@
|
||||
<tr>
|
||||
<td class="indent2">
|
||||
<input type="checkbox" id="PostProcessing_AnalogToDigitTransitionStart_enabled" value="1" onclick = 'InvertEnableItem("PostProcessing", "AnalogToDigitTransitionStart")' unchecked >
|
||||
<label for=PostProcessing_AnalogToDigitTransitionStart_enabled><class id="PostProcessing_AnalogToDigitTransitionStart_text" style="color:black;">Analogto Digit Transition Start</class></label>
|
||||
<label for=PostProcessing_AnalogToDigitTransitionStart_enabled><class id="PostProcessing_AnalogToDigitTransitionStart_text" style="color:black;">Analog to Digit Transition Start</class></label>
|
||||
</td>
|
||||
<td>
|
||||
<input required type="number" id="PostProcessing_AnalogToDigitTransitionStart_value1" step="0.1" min="5.0" max="9.9" value="9.2"
|
||||
@@ -1178,6 +1178,36 @@
|
||||
<td>$TOOLTIP_MQTT_MeterType</td>
|
||||
</tr>
|
||||
|
||||
<tr class="MQTTItem">
|
||||
<td class="indent1">
|
||||
<input type="checkbox" id="MQTT_DomoticzTopicIn_enabled" value="1" onclick = 'InvertEnableItem("MQTT", "DomoticzTopicIn")' unchecked >
|
||||
<label for=MQTT_DomoticzTopicIn_enabled><class id="MQTT_DomoticzTopicIn_text" style="color:black;">Domoticz "in" topic</class></label>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" id="MQTT_DomoticzTopicIn_value1">
|
||||
</td>
|
||||
<td>$TOOLTIP_MQTT_DomoticzTopicIn</td>
|
||||
</tr>
|
||||
|
||||
<tr class="MQTTItem" style="margin-top:12px">
|
||||
<td class="indent1" style="padding-top:25px" colspan="3">
|
||||
<b>Parameter per number sequence:</b>
|
||||
<select
|
||||
style="font-weight: bold; margin-left:17px" id="NumbersMQTTIdx_value1" onchange="numberMQTTIdxChanged()">
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="MQTTItem">
|
||||
<td class="indent2">
|
||||
<input type="checkbox" id="MQTT_DomoticzIDX_enabled" value="1" onclick = 'InvertEnableItem("MQTT", "DomoticzIDX")' unchecked >
|
||||
<label for=MQTT_DomoticzIDX_enabled><class id="MQTT_DomoticzIDX_text" style="color:black;">Domoticz Counter Idx:</class></label>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" id="MQTT_DomoticzIDX_value1">
|
||||
</td>
|
||||
<td>$TOOLTIP_MQTT_NUMBER.DomoticzIDX</td>
|
||||
</tr>
|
||||
|
||||
|
||||
<!------------- INFLUXDB v1 ------------------>
|
||||
<tr style="border-bottom: 2px solid lightgray;">
|
||||
@@ -1890,6 +1920,7 @@
|
||||
|
||||
|
||||
<!------------- Autotimer ------------------>
|
||||
<!--
|
||||
<tr style="border-bottom: 2px solid lightgray;">
|
||||
<td colspan="3" style="padding-left: 0px; padding-bottom: 3px;"><h4>Auto Timer</h4></td>
|
||||
</tr>
|
||||
@@ -1906,6 +1937,7 @@
|
||||
</td>
|
||||
<td>$TOOLTIP_AutoTimer_AutoStart</td>
|
||||
</tr>
|
||||
-->
|
||||
|
||||
<tr>
|
||||
<td class="indent1">
|
||||
@@ -2120,6 +2152,7 @@ function LoadConfigNeu() {
|
||||
UpdateInput();
|
||||
var sel = document.getElementById("Numbers_value1");
|
||||
UpdateInputIndividual(sel);
|
||||
|
||||
UpdateExpertModus();
|
||||
UpdateTooltipModus();
|
||||
document.getElementById("divall").style.display = '';
|
||||
@@ -2162,6 +2195,11 @@ function InitIndivParameter() {
|
||||
_indexInfluxv1.remove(0);
|
||||
}
|
||||
|
||||
var _indexMQTTIdx = document.getElementById("NumbersMQTTIdx_value1");
|
||||
while (_indexMQTTIdx.length) {
|
||||
_indexMQTTIdx.remove(0);
|
||||
}
|
||||
|
||||
for (var i = 0; i < NUMBERS.length; ++i) {
|
||||
var option = document.createElement("option");
|
||||
option.text = NUMBERS[i]["name"];
|
||||
@@ -2177,11 +2215,17 @@ function InitIndivParameter() {
|
||||
optionInfluxv1.text = NUMBERS[i]["name"];
|
||||
optionInfluxv1.value = i;
|
||||
_indexInfluxv1.add(optionInfluxv1);
|
||||
|
||||
var optionMQTTIdx = document.createElement("option");
|
||||
optionMQTTIdx.text = NUMBERS[i]["name"];
|
||||
optionMQTTIdx.value = i;
|
||||
_indexMQTTIdx.add(optionMQTTIdx);
|
||||
}
|
||||
|
||||
_index.selectedIndex = 0;
|
||||
_indexInflux.selectedIndex = 0;
|
||||
_indexInfluxv1.selectedIndex = 0;
|
||||
_indexMQTTIdx.selectedIndex = 0;
|
||||
}
|
||||
|
||||
function UpdateInputIndividual(sel) {
|
||||
@@ -2200,6 +2244,7 @@ function UpdateInputIndividual(sel) {
|
||||
ReadParameter(param, "InfluxDBv2", "Field", true, NUNBERSAkt);
|
||||
ReadParameter(param, "InfluxDB", "Measurement", true, NUNBERSAkt);
|
||||
ReadParameter(param, "InfluxDBv2", "Measurement", true, NUNBERSAkt);
|
||||
ReadParameter(param, "MQTT", "DomoticzIDX", true, NUNBERSAkt);
|
||||
}
|
||||
|
||||
// var sel = document.getElementById("Numbers_value1");
|
||||
@@ -2218,6 +2263,8 @@ function UpdateInputIndividual(sel) {
|
||||
WriteParameter(param, category, "InfluxDBv2", "Field", true, NUNBERSAkt);
|
||||
WriteParameter(param, category, "InfluxDB", "Measurement", true, NUNBERSAkt);
|
||||
WriteParameter(param, category, "InfluxDBv2", "Measurement", true, NUNBERSAkt);
|
||||
WriteParameter(param, category, "MQTT", "DomoticzIDX", true, NUNBERSAkt);
|
||||
|
||||
}
|
||||
|
||||
function UpdateInput() {
|
||||
@@ -2306,6 +2353,7 @@ function UpdateInput() {
|
||||
WriteParameter(param, category, "MQTT", "CACert", true);
|
||||
WriteParameter(param, category, "MQTT", "ClientCert", true);
|
||||
WriteParameter(param, category, "MQTT", "ClientKey", true);
|
||||
WriteParameter(param, category, "MQTT", "DomoticzTopicIn", true);
|
||||
|
||||
WriteParameter(param, category, "InfluxDB", "Uri", true);
|
||||
WriteParameter(param, category, "InfluxDB", "Database", true);
|
||||
@@ -2335,7 +2383,7 @@ function UpdateInput() {
|
||||
WriteParameter(param, category, "GPIO", "LEDNumbers", false);
|
||||
WriteParameter(param, category, "GPIO", "LEDColor", false);
|
||||
|
||||
WriteParameter(param, category, "AutoTimer", "AutoStart", false);
|
||||
//WriteParameter(param, category, "AutoTimer", "AutoStart", false);
|
||||
WriteParameter(param, category, "AutoTimer", "Interval", false);
|
||||
|
||||
WriteParameter(param, category, "DataLogging", "DataLogActive", false);
|
||||
@@ -2473,6 +2521,7 @@ function ReadParameterAll() {
|
||||
ReadParameter(param, "MQTT", "CACert", true);
|
||||
ReadParameter(param, "MQTT", "ClientCert", true);
|
||||
ReadParameter(param, "MQTT", "ClientKey", true);
|
||||
ReadParameter(param, "MQTT", "DomoticzTopicIn", true);
|
||||
|
||||
ReadParameter(param, "InfluxDB", "Uri", true);
|
||||
ReadParameter(param, "InfluxDB", "Database", true);
|
||||
@@ -2509,7 +2558,7 @@ function ReadParameterAll() {
|
||||
param["GPIO"]["LEDNumbers"]["found"] = true;
|
||||
param["GPIO"]["LEDColor"]["found"] = true;
|
||||
|
||||
ReadParameter(param, "AutoTimer", "AutoStart", false);
|
||||
//ReadParameter(param, "AutoTimer", "AutoStart", false);
|
||||
ReadParameter(param, "AutoTimer", "Interval", false);
|
||||
|
||||
ReadParameter(param, "DataLogging", "DataLogActive", false);
|
||||
@@ -2527,7 +2576,7 @@ function ReadParameterAll() {
|
||||
|
||||
var sel = document.getElementById("Numbers_value1");
|
||||
UpdateInputIndividual(sel);
|
||||
|
||||
|
||||
// FormatDecimalValue(param, "PostProcessing", "MaxRateValue");
|
||||
}
|
||||
|
||||
@@ -2555,7 +2604,7 @@ function UpdateExpertModus() {
|
||||
_style_pur = '';
|
||||
_hidden = false;
|
||||
document.getElementById("Button_Edit_Config_Raw").style.display = "";
|
||||
firework.launch("Expert view activated. Please use carefully", 'warning', 5000);
|
||||
firework.launch("Expert view activated. Please use it carefully", 'warning', 5000);
|
||||
}
|
||||
else {
|
||||
document.getElementById("Button_Edit_Config_Raw").style.display = "none";
|
||||
@@ -2821,7 +2870,7 @@ function camSettingsSet(){
|
||||
}
|
||||
catch (error){}
|
||||
|
||||
document.getElementById("overlaytext").innerHTML = "Device is busy, plase waiting...<br><br>Current step: " + _xhttp.responseText;
|
||||
document.getElementById("overlaytext").innerHTML = "Device is busy, please wait.<br><br>Current step: " + _xhttp.responseText;
|
||||
console.log("Device is busy, waiting 2s then checking again...");
|
||||
await sleep(2000);
|
||||
}
|
||||
@@ -2871,6 +2920,38 @@ function numberChanged() {
|
||||
if (_selInflux.selectedIndex != _neu) {
|
||||
_selInflux.selectedIndex = _neu
|
||||
}
|
||||
|
||||
var _sel3 = document.getElementById("NumbersInfluxDB_value1");
|
||||
if (_sel3.selectedIndex != _neu) {
|
||||
_sel3.selectedIndex = _neu
|
||||
}
|
||||
|
||||
var _sel4 = document.getElementById("NumbersMQTTIdx_value1");
|
||||
if (_sel4.selectedIndex != _neu) {
|
||||
_sel4.selectedIndex = _neu
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function numberMQTTIdxChanged() {
|
||||
var sel = document.getElementById("NumbersMQTTIdx_value1");
|
||||
_neu = sel.selectedIndex;
|
||||
UpdateInputIndividual(sel);
|
||||
|
||||
var _sel2 = document.getElementById("Numbers_value1");
|
||||
if (_sel2.selectedIndex != _neu) {
|
||||
_sel2.selectedIndex = _neu
|
||||
}
|
||||
|
||||
var _sel3 = document.getElementById("NumbersInfluxDB_value1");
|
||||
if (_sel3.selectedIndex != _neu) {
|
||||
_sel3.selectedIndex = _neu
|
||||
}
|
||||
|
||||
var _sel4 = document.getElementById("NumbersInfluxDBv2_value1");
|
||||
if (_sel4.selectedIndex != _neu) {
|
||||
_sel4.selectedIndex = _neu
|
||||
}
|
||||
}
|
||||
|
||||
function numberInfluxDBv2Changed() {
|
||||
@@ -2887,6 +2968,11 @@ function numberInfluxDBv2Changed() {
|
||||
if (_sel3.selectedIndex != _neu) {
|
||||
_sel3.selectedIndex = _neu
|
||||
}
|
||||
|
||||
var _sel4 = document.getElementById("NumbersMQTTIdx_value1");
|
||||
if (_sel4.selectedIndex != _neu) {
|
||||
_sel4.selectedIndex = _neu
|
||||
}
|
||||
}
|
||||
|
||||
function numberInfluxDBChanged() {
|
||||
@@ -2903,6 +2989,11 @@ function numberInfluxDBChanged() {
|
||||
if (_sel3.selectedIndex != _neu) {
|
||||
_sel3.selectedIndex = _neu
|
||||
}
|
||||
|
||||
var _sel4 = document.getElementById("NumbersMQTTIdx_value1");
|
||||
if (_sel4.selectedIndex != _neu) {
|
||||
_sel4.selectedIndex = _neu
|
||||
}
|
||||
}
|
||||
|
||||
function getParameterByName(name, url = window.location.href) {
|
||||
|
||||
@@ -617,7 +617,7 @@
|
||||
}
|
||||
catch (error){}
|
||||
|
||||
document.getElementById("overlaytext").innerHTML = "Device is busy, plase waiting...<br><br>Current step: " + _xhttp.responseText;
|
||||
document.getElementById("overlaytext").innerHTML = "Device is busy, please wait.<br><br>Current step: " + _xhttp.responseText;
|
||||
console.log("Device is busy, waiting 5s then checking again...");
|
||||
await sleep(2000);
|
||||
}
|
||||
@@ -1053,7 +1053,7 @@
|
||||
if (document.getElementById("ExpertModus_enabled").checked) {
|
||||
_style_pur = '';
|
||||
_hidden = false;
|
||||
firework.launch("Expert parameter view activated. Please use carefully", 'warning', 5000);
|
||||
firework.launch("Expert parameter view activated. Please use it carefully", 'warning', 5000);
|
||||
}
|
||||
|
||||
const expert = document.querySelectorAll(".expert");
|
||||
|
||||
@@ -69,6 +69,30 @@
|
||||
return "";
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
/* Add these styles to your existing CSS file or in a <style> tag in the head */
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
background-color: #d8d8d8;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.footer-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.footer-section img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.donation-cards img {
|
||||
height: 20px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -134,9 +158,9 @@
|
||||
</ul>
|
||||
</li>
|
||||
<li id="ManualControl" style="display:none;"><a>Manual Control <i class="arrow down"></i></a> <!-- Workaround: Hide menu if no entry is available -->
|
||||
<ul class="submenu">
|
||||
<!--<li><a href="#" onclick="flow_start()">Start Round</a></li>--> <!-- Needs to be adapted on code side first to ensure proper user feedback -->
|
||||
<li id="HASendDiscovery" style="display:none;"><a href="#" onclick="HA_send_discovery()">Resend HA Discovery</a></li>
|
||||
<ul class="submenu" style="width: 300px">
|
||||
<li><a href="#" onclick="flow_start()">Start Round</a></li>
|
||||
<li id="HASendDiscovery" style="width: 300px" style="display:none;"><a href="#" onclick="HA_send_discovery()">Resend Homeassistant Discovery</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -146,6 +170,28 @@
|
||||
|
||||
<span id="Version" style="font-size: 10px; margin-top: -5px;padding-left: 10px;">Loading version...</span>
|
||||
|
||||
<!-- # Disabled footer, since it wastes a lot of space and the images are broken
|
||||
<div class="footer">
|
||||
<div class="footer-section">
|
||||
<span>Support & Contact Us</span>
|
||||
<a href="https://github.com/jomjol/AI-on-the-edge-device" target="_blank" title="GitHub">
|
||||
<img src="https://github.com/jomjol/AI-on-the-edge-device/images/github-logo.png" alt="GitHub">
|
||||
</a>
|
||||
<img src="https://github.com/jomjol/AI-on-the-edge-device/images/gmail-logo.png" alt="Email">
|
||||
</a>
|
||||
<a href="https://github.com/jomjol/AI-on-the-edge-device/discussions" target="_blank" title="GitHub">
|
||||
<img src="https://github.com/jomjol/AI-on-the-edge-device/images/discussion-logo" alt="GitHub">
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<div class="footer-section">
|
||||
<span>Donations</span>
|
||||
<a href="https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL" target="_blank" title="Donate via PayPal">
|
||||
<img src="https://github.com/jomjol/AI-on-the-edge-device/images/paypal.png" alt="PayPal" style="width: 60px; height: auto;">
|
||||
</a>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<script type="text/javascript">
|
||||
LoadHostname();
|
||||
LoadFwVersion();
|
||||
@@ -158,13 +204,14 @@
|
||||
console.log("Loading page: " + getCookie("page"));
|
||||
document.getElementById('maincontent').src = getCookie("page");
|
||||
|
||||
/*
|
||||
|
||||
function flow_start() {
|
||||
var url = getDomainname() + '/flow_start';
|
||||
var xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
if (xhttp.responseText.substring(0,3) == "001") {
|
||||
firework.launch(xhttp.responseText, 'success', 5000);
|
||||
/*if (xhttp.responseText.substring(0,3) == "001") {
|
||||
firework.launch('Flow start triggered', 'success', 5000);
|
||||
window.location.reload();
|
||||
}
|
||||
@@ -173,13 +220,13 @@
|
||||
}
|
||||
else if (xhttp.responseText.substring(0,3) == "099") {
|
||||
firework.launch('Flow start triggered, but start not possible (no flow task available)', 'danger', 5000);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
xhttp.open("GET", url, true);
|
||||
xhttp.send();
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
function HA_send_discovery_visibility() {
|
||||
loadConfig(domainname);
|
||||
@@ -187,19 +234,19 @@
|
||||
category = getConfigCategory();
|
||||
param = getConfigParameters();
|
||||
|
||||
if (category["MQTT"]["enabled"] && param["MQTT"]["HomeassistantDiscovery"].value == "true") {
|
||||
if (category["MQTT"]["enabled"] && param["MQTT"]["HomeassistantDiscovery"].value1 == "true") {
|
||||
document.getElementById("ManualControl").style.display="";
|
||||
document.getElementById("HASendDiscovery").style.display="";
|
||||
}
|
||||
}
|
||||
|
||||
function HA_send_discovery() {
|
||||
console.log("HA Discovery scheduled");
|
||||
console.log("Homeassistant Discovery topic sending scheduled");
|
||||
var url = getDomainname() + '/mqtt_publish_discovery';
|
||||
var xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
firework.launch('Sending HA discovery topics scheduled. The sending will be processed in state "Publish to MQTT"', 'success', 5000);
|
||||
firework.launch('Sending Homeassistant discovery topics scheduled. It will get sent in the step "Publish to MQTT" of the next digitization round', 'success', 5000);
|
||||
}
|
||||
}
|
||||
xhttp.open("GET", url, true);
|
||||
|
||||
@@ -118,6 +118,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tg-4">
|
||||
<div id="sntp_date" ></div>
|
||||
<div id="timestamp" ></div>
|
||||
<div id="cputemp" ></div>
|
||||
<div id="rssi" ></div>
|
||||
@@ -153,6 +154,7 @@
|
||||
loadValue("prevalue", "prevalue", "border-collapse: collapse; width: 100%");
|
||||
loadValue("error", "error", "border-collapse: collapse; width: 100%");
|
||||
loadStatus();
|
||||
loadSntpDate();
|
||||
loadCPUTemp();
|
||||
loadRSSI();
|
||||
loadUptime();
|
||||
@@ -234,6 +236,20 @@
|
||||
xhttp.send();
|
||||
}
|
||||
|
||||
function loadSntpDate() {
|
||||
url = domainname + '/date';
|
||||
var xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function () {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
var _rsp = xhttp.responseText;
|
||||
$('#sntp_date').html("Date/Time on device: " + _rsp);
|
||||
}
|
||||
}
|
||||
|
||||
xhttp.open("GET", url, true);
|
||||
xhttp.send();
|
||||
}
|
||||
|
||||
function loadUptime() {
|
||||
url = domainname + '/uptime';
|
||||
var xhttp = new XMLHttpRequest();
|
||||
|
||||
@@ -198,6 +198,8 @@ function ParseConfig() {
|
||||
ParamAddValue(param, catname, "user");
|
||||
ParamAddValue(param, catname, "password");
|
||||
ParamAddValue(param, catname, "RetainMessages");
|
||||
ParamAddValue(param, catname, "DomoticzTopicIn");
|
||||
ParamAddValue(param, catname, "DomoticzIDX", 1, true);
|
||||
ParamAddValue(param, catname, "HomeassistantDiscovery");
|
||||
ParamAddValue(param, catname, "MeterType");
|
||||
ParamAddValue(param, catname, "CACert");
|
||||
@@ -265,7 +267,7 @@ function ParseConfig() {
|
||||
category[catname]["enabled"] = false;
|
||||
category[catname]["found"] = false;
|
||||
param[catname] = new Object();
|
||||
ParamAddValue(param, catname, "AutoStart");
|
||||
//ParamAddValue(param, catname, "AutoStart");
|
||||
ParamAddValue(param, catname, "Interval");
|
||||
|
||||
var catname = "DataLogging";
|
||||
|
||||
@@ -36,3 +36,14 @@ password = ""
|
||||
; Default: 0 = Disable client requested roaming query
|
||||
|
||||
RSSIThreshold = 0
|
||||
|
||||
;++++++++++++++++++++++++++++++++++
|
||||
; Password Protection of the Web Interface and the REST API
|
||||
; When those parameters are active, the Web Interface and the REST API are protected by a username and password.
|
||||
; Note: This is be a WEAK and INSECURE way to protect the Web Interface and the REST API.
|
||||
; There was no audit nor a security review to check the correct implementation of the protection!
|
||||
; The password gets transmitted unencrypted (plain text), this means it is very easy to extract it
|
||||
; for somebody who has access to your WIFI!
|
||||
; USE AT YOUR OWN RISK!
|
||||
;http_username = "myusername"
|
||||
;http_password = "mypassword"
|
||||
|
||||