diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 5192cdef2..ea652db20 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -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,13 +350,11 @@ jobs: # extract the version used in next step - id: get_version - if: startsWith(github.ref, 'refs/tags/') - uses: Simply007/get-version-action@v2 + uses: drewg13/get-version-action@98dda2a47a257e202c2e6c2ed2e6072ec23f448e # # the changelog [unreleased] will now be changed to the release version # - name: Update changelog # uses: thomaseizinger/keep-a-changelog-new-release@v1 -# if: startsWith(github.ref, 'refs/tags/') # with: # changelogPath: Changelog.md # version: ${{ steps.get_version.outputs.version-without-v }} @@ -345,7 +362,6 @@ jobs: # # the release notes will be extracted from changelog # - name: Extract release notes # id: extract-release-notes -# if: startsWith(github.ref, 'refs/tags/') # uses: ffurrer2/extract-release-notes@v1 # with: # changelog_file: Changelog.md @@ -353,12 +369,11 @@ jobs: # Releases should only be created on master by tagging the last commit. # all artifacts in firmware folder pushed to the release - name: Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2.0.8 # Note: # If you get the error "Resource not accessible by integration", # The access rights are not sufficient, see # https://github.com/softprops/action-gh-release/issues/232#issuecomment-1131379440 - if: startsWith(github.ref, 'refs/tags/') with: name: ${{ steps.get_version.outputs.version-without-v }} body: ${{ steps.extract-release-notes.outputs.release_notes }} @@ -367,7 +382,6 @@ jobs: # # Commit&Push Changelog to master branch. Must be manually merged back to rolling # - name: Commit changes and push changes -# if: startsWith(github.ref, 'refs/tags/') # run: | # git config user.name github-actions # git config user.email github-actions@github.com @@ -380,8 +394,9 @@ jobs: ## Update the Web Installer on a release ######################################################################################### # Make sure to also update update-webinstaller.yml! - update-web-installer: - needs: [release] + update-web-installer: + if: github.event_name == 'release' && github.event.action == 'published' # Only run on release but not on prerelease + needs: [prepare-release] environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} diff --git a/.github/workflows/manual-update-contributors-list.yaml b/.github/workflows/manual-update-contributors-list.yaml new file mode 100644 index 000000000..aeb5b3df9 --- /dev/null +++ b/.github/workflows/manual-update-contributors-list.yaml @@ -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 }} diff --git a/Changelog.md b/Changelog.md index 3fbb4c640..84aaf6a7c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,60 @@ +## [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 RC2 are listed: +- Update esp32-camera submodule to `v2.0.13` (#3316) +- Added contributor list (#3317) +- Added files for demo mode (#3315) + +#### Bug Fixes +Only changes since RC2 are listed: +- Added delay in InitCam (#3313) 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: +- Re-did revertion of TFlite submodule update as certain modules crash with it (#3269) (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) + +#### Known issues +Please check the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues) and +[discussions](https://github.com/jomjol/AI-on-the-edge-device/discussions) before reporting a new issue. + +#### Core Changes +Only changes since RC1 are listed: +- Updated parameter documentation pages +- Rename/remove unused parameters (#3291) +- Migrate-cam-parameters (#3288) + +#### Bug Fixes +Only changes since RC1 are listed: +- Reverted TFlite submodule update as certain modules crash with it (#3269) +- Changed the webhook UploadImg to false (#3279) +- Changed default value from boolean to numeric value in parameter camDenoise documentation +- Updated config page + ## [16.0.0-RC1] - 2024-09-24 For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.7.0...v16.0.0-RC1) @@ -524,16 +581,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 @@ -547,7 +604,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) @@ -1062,7 +1119,7 @@ External Illumination - Bug in configuration of analog ROIs corrected -- minor bug correction +- minor Bug correction ## [1.0.1](2020-09-05) @@ -1071,7 +1128,7 @@ External Illumination - preValue.ini Bug corrected -- minor bug correction +- minor Bug correction ## [1.0.0](2020-09-04) diff --git a/FeatureRequest.md b/FeatureRequest.md index da9bc85d8..eb7c8509f 100644 --- a/FeatureRequest.md +++ b/FeatureRequest.md @@ -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.. ![grafik](https://user-images.githubusercontent.com/38385805/207858812-2a6ba41d-1a8c-4fa1-9b6a-53cdd113c106.png) -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 diff --git a/README.md b/README.md index 60a72b571..5af9a1660 100644 --- a/README.md +++ b/README.md @@ -1,108 +1,551 @@ -# Welcome to the AI-on-the-edge-device - +# AI on the Edge Device: Digitizing Your non-digital meters with an ESP32-CAM +

+ +

-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**. +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. -This project allows you to digitize your **analog** water, gas, power and other meters using cheap and easily available hardware. +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 something of a practical hand. +All you need is an [ESP32 board with a supported camera](https://jomjol.github.io/AI-on-the-edge-device-docs/Hardware-Compatibility/) and some practical skills. πŸ› οΈ - +--- -## 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 +## 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. -## 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. +## 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. Either send it to an MQTT broker, write it to an InfluxDb or simply provide access to it via a REST API. +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**. - +

+ +

-## Impressions -### AI-on-the-edge-device on a Water Meter - +--- -### Web Interface (Water Meter) - +## Impressions πŸ“· -### AI-on-the-edge-device on a Electrical Power Meter - +### AI-on-the-edge-device on a Water Meter πŸ’§ +

+ +

+### Web Interface (Water Meter) πŸ’» +

+ +

-## 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. +### AI-on-the-edge-device on an Electrical Power Meter ⚑ +

+ +

-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: +## 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. -- [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) +There are also articles in the German Heise magazine "make:" about the setup and technical background (behind a paywall): [DIY - Setup](https://www.heise.de/select/make/2021/2/2103513300897420296) πŸ“° -For further background information, head to [Neural Networks](https://www.heise.de/select/make/2021/6/2126410443385102621), [Training Neural Networks](https://www.heise.de/select/make/2022/1/2134114065999161585) and [Programming on the ESP32](https://www.heise.de/select/make/2022/2/2204010051597422030). +A lot of people 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) -### Download +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) + +--- + +## 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). +--- + +## 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 prefered way is the [Web Installer and Console](https://jomjol.github.io/AI-on-the-edge-device/index.html) which is a browser-based tool to flash the ESP32 and extract the log over USB: -![](images/web-installer.png) +- 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: + ![](images/web-installer.png) - 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! +--- + +## 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). + +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. + +--- + +## 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) + +--- + +## 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). + +

+ +

+ +--- + +## 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: +

+ +

+ +--- -## 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) +## Changes and History πŸ“œ +See the [Changelog](Changelog.md) for detailed information. -## 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). +--- - +## Build It Yourself πŸ”¨ +See the [Build Instructions](code/README.md) for step-by-step guidance. -## 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: +## Tools πŸ› οΈ +* Logfile downloader and combiner (Thanks to [reserve85](https://github.com/reserve85)) + * Files can be found at ['/tools/logfile-tool'](tbd), and how-to instructions are in the [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/outdated--Gasmeter-Log-Downloader/). -## Changes and History -See [Changelog](Changelog.md). +--- -## Build It Yourself -See [Build Instructions](code/README.md). +## 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). -## 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/) +--- -## 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). +## Our Contributors ❀️ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + jomjol +
+ jomjol +
+
+ + caco3 +
+ CaCO3 +
+
+ + haverland +
+ Frank Haverland +
+
+ + Slider0007 +
+ Slider0007 +
+
+ + SybexX +
+ michael +
+
+ + nliaudat +
+ Nicolas Liaudat +
+
+ + Zwer2k +
+ Zwer2k +
+
+ + phlupp +
+ phlupp +
+
+ + jasaw +
+ jasaw +
+
+ + dockSquadron +
+ dockSquadron +
+
+ + rdmueller +
+ Ralf D. MΓΌller +
+
+ + cristianmitran +
+ cristianmitran +
+
+ + michaeljoos72 +
+ michaeljoos72 +
+
+ + henrythasler +
+ Henry Thasler +
+
+ + amantyagiprojects +
+ Naman Tyagi +
+
+ + pixeldoc2000 +
+ pixel::doc +
+
+ + mad2xlc +
+ Stefan +
+
+ + jochenchrist +
+ jochenchrist +
+
+ + parhedberg +
+ parhedberg +
+
+ + slovdahl +
+ Sebastian LΓΆvdahl +
+
+ + RaHehl +
+ Raphael Hehl +
+
+ + muggenhor +
+ Giel van Schijndel +
+
+ + LordGuilly +
+ LordGuilly +
+
+ + toolsfactory +
+ Michael Geissler +
+
+ + ppisljar +
+ Peter Pisljar +
+
+ + ralf1307 +
+ Ralf Rachinger +
+
+ + SkylightXD +
+ SkylightXD +
+
+ + ottk3 +
+ Sven Rojek +
+
+ + Turbo87 +
+ Tobias Bieniek +
+
+ + yonz2 +
+ Yonz +
+
+ + Yveaux +
+ Yveaux +
+
+ + flooxo +
+ flox_x +
+
+ + kalwados +
+ kalwados +
+
+ + kub3let +
+ kub3let +
+
+ + pfeifferch +
+ pfeifferch +
+
+ + rstephan +
+ rstephan +
+
+ + smartboart +
+ smartboart +
+
+ + mkelley88 +
+ Matthew T. Kelley +
+
+ + rainman110 +
+ Martin Siggel +
+
+ + myxor +
+ Marco H +
+
+ + Innovatorcloudy +
+ KrishCode +
+
+ + joergrosenkranz +
+ Joerg Rosenkranz +
+
+ + queeek +
+ Ina +
+
+ + eltociear +
+ Ikko Eltociear Ashimine +
+
+ + 040medien +
+ Frederik Kemner +
+
+ + hex7c0 +
+ Francesco Carnielli +
+
+ + dkneisz +
+ Dave +
+
+ + CFenner +
+ Christopher Fenner +
+
+ + PLCHome +
+ PLCHome +
+
+ + austindrenski +
+ Austin Drenski +
+
+ + adarazs +
+ Attila Darazs +
+
+ + wetneb +
+ Antonin Delpeuch +
+
+ + AngryApostrophe +
+ AngryApostrophe +
+
+ diff --git a/code/components/esp32-camera b/code/components/esp32-camera index dba8da989..0054ab760 160000 --- a/code/components/esp32-camera +++ b/code/components/esp32-camera @@ -1 +1 @@ -Subproject commit dba8da9898928d9808d57a0b0cdcde9f130ed8fe +Subproject commit 0054ab76046d2879ca9682edb2419b0613b06acd diff --git a/code/components/jomjol_controlcamera/ClassControllCamera.cpp b/code/components/jomjol_controlcamera/ClassControllCamera.cpp index bd830ed92..fb2ffe91c 100644 --- a/code/components/jomjol_controlcamera/ClassControllCamera.cpp +++ b/code/components/jomjol_controlcamera/ClassControllCamera.cpp @@ -122,12 +122,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) { @@ -280,8 +286,8 @@ esp_err_t CCamera::setSensorDatenFromCCstatus(void) s->set_dcw(s, CCstatus.ImageDcw); // 0 = disable , 1 = enable - TickType_t xDelay2 = 100 / portTICK_PERIOD_MS; - vTaskDelay(xDelay2); + TickType_t cam_xDelay = 100 / portTICK_PERIOD_MS; + vTaskDelay(cam_xDelay); return ESP_OK; } diff --git a/code/components/jomjol_flowcontroll/ClassFlowMQTT.cpp b/code/components/jomjol_flowcontroll/ClassFlowMQTT.cpp index b1162f837..d798d3eba 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowMQTT.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowMQTT.cpp @@ -286,7 +286,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) diff --git a/code/components/jomjol_mqtt/server_mqtt.cpp b/code/components/jomjol_mqtt/server_mqtt.cpp index adc68e211..fd675a139 100644 --- a/code/components/jomjol_mqtt/server_mqtt.cpp +++ b/code/components/jomjol_mqtt/server_mqtt.cpp @@ -188,10 +188,10 @@ bool MQTThomeassistantDiscovery(int qos) { allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "value", "Value", "gauge", valueUnit, meterType, "total_increasing", "", qos); allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "raw", "Raw Value", "raw", "", "", "", "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_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 diff --git a/code/main/main.cpp b/code/main/main.cpp index 0340e50f6..36e40d207 100644 --- a/code/main/main.cpp +++ b/code/main/main.cpp @@ -528,30 +528,44 @@ extern "C" void app_main(void) } } -// FIXME: needs to be revised or removed!!! void migrateConfiguration(void) { + std::vector splitted; bool migrated = false; + bool CamZoom_found = false; + int CamZoom_lines = 0; + bool CamZoom_value = false; + int CamZoomSize_lines = 0; + int CamZoomSize_value = 0; + int CamZoomOffsetX_lines = 0; + int CamZoomOffsetX_value = 0; + int CamZoomOffsetY_lines = 0; + int CamZoomOffsetY_value = 0; + if (!FileExists(CONFIG_FILE)) { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Config file seems to be missing!"); - return; + return; } std::string section = ""; - std::ifstream ifs(CONFIG_FILE); - std::string content((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); - + std::ifstream ifs(CONFIG_FILE); + std::string content((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); + /* Split config file it array of lines */ std::vector configLines = splitString(content); /* Process each line */ for (int i = 0; i < configLines.size(); i++) { - //ESP_LOGI(TAG, "Line %d: %s", i, configLines[i].c_str()); + // ESP_LOGI(TAG, "Line %d: %s", i, configLines[i].c_str()); - if (configLines[i].find("[") != std::string::npos) { // Start of new section + if (configLines[i].find("[") != std::string::npos) { + // Start of new section section = configLines[i]; replaceString(section, ";", "", false); // Remove possible semicolon (just for the string comparison) - //ESP_LOGI(TAG, "New section: %s", section.c_str()); + // ESP_LOGI(TAG, "New section: %s", section.c_str()); + } + else { + splitted = ZerlegeZeile(configLines[i]); } /* Migrate parameters as needed @@ -569,119 +583,154 @@ void migrateConfiguration(void) { } if (section == "[MakeImage]" || section == "[TakeImage]") { - migrated = migrated | replaceString(configLines[i], "LogImageLocation", "RawImagesLocation"); - migrated = migrated | replaceString(configLines[i], "LogfileRetentionInDays", "RawImagesRetention"); + if ((isInString(configLines[i], "Brightness")) && (!isInString(configLines[i], "CamBrightness"))) { + migrated = migrated | replaceString(configLines[i], "Brightness", "CamBrightness"); + } + else if ((isInString(configLines[i], "Contrast")) && (!isInString(configLines[i], "CamContrast"))) { + migrated = migrated | replaceString(configLines[i], "Contrast", "CamContrast"); + } + else if ((isInString(configLines[i], "Saturation")) && (!isInString(configLines[i], "CamSaturation"))) { + migrated = migrated | replaceString(configLines[i], "Saturation", "CamSaturation"); + } + else if ((isInString(configLines[i], "Sharpness")) && (!isInString(configLines[i], "CamSharpness")) && (!isInString(configLines[i], "CamAutoSharpness"))) { + migrated = migrated | replaceString(configLines[i], "Sharpness", "CamSharpness"); + } + else if ((isInString(configLines[i], "Aec2")) && (!isInString(configLines[i], "CamAec")) && (!isInString(configLines[i], "CamAec2"))) { + migrated = migrated | replaceString(configLines[i], "Aec2", "CamAec2"); + } + 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]); + 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]); + } + 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]); + } + 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]); + } + CamZoom_found = true; + } + else { + migrated = migrated | replaceString(configLines[i], "LogImageLocation", "RawImagesLocation"); + migrated = migrated | replaceString(configLines[i], "LogfileRetentionInDays", "RawImagesRetention"); - migrated = migrated | replaceString(configLines[i], ";Demo = true", ";Demo = false"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";Demo", "Demo"); // Enable it + migrated = migrated | replaceString(configLines[i], ";Demo = true", ";Demo = false"); // Set it to its default value + migrated = migrated | replaceString(configLines[i], ";Demo", "Demo"); // Enable it - // Parameter is no longer used - // migrated = migrated | replaceString(configLines[i], ";FixedExposure = true", ";FixedExposure = false"); // Set it to its default value - // migrated = migrated | replaceString(configLines[i], ";FixedExposure", "FixedExposure"); // Enable it - } + migrated = migrated | replaceString(configLines[i], "ImageQuality", "CamQuality"); + migrated = migrated | replaceString(configLines[i], "AutoExposureLevel", "CamAeLevel"); + migrated = migrated | replaceString(configLines[i], "FixedExposure", "CamAec"); - if (section == "[Alignment]") { - // Parameter is no longer used - // migrated = migrated | replaceString(configLines[i], ";InitialMirror = true", ";InitialMirror = false"); // Set it to its default value - // migrated = migrated | replaceString(configLines[i], ";InitialMirror", "InitialMirror"); // Enable it - - // Parameter is no longer used - // migrated = migrated | replaceString(configLines[i], ";FlipImageSize = true", ";FlipImageSize = false"); // Set it to its default value - // migrated = migrated | replaceString(configLines[i], ";FlipImageSize", "FlipImageSize"); // Enable it + migrated = migrated | replaceString(configLines[i], "ImageSize", ";UNUSED_PARAMETER"); // This parameter is no longer used + migrated = migrated | replaceString(configLines[i], "Grayscale", ";UNUSED_PARAMETER"); // This parameter is no longer used + migrated = migrated | replaceString(configLines[i], "Negative", ";UNUSED_PARAMETER"); // This parameter is no longer used + } } - - if (section == "[Digits]") { + else if (section == "[Alignment]") { + migrated = migrated | replaceString(configLines[i], "InitialMirror", ";UNUSED_PARAMETER"); // This parameter is no longer used + migrated = migrated | replaceString(configLines[i], ";InitialMirror", ";UNUSED_PARAMETER"); // This parameter is no longer used + migrated = migrated | replaceString(configLines[i], "FlipImageSize", ";UNUSED_PARAMETER"); // This parameter is no longer used + migrated = migrated | replaceString(configLines[i], ";FlipImageSize", ";UNUSED_PARAMETER"); // This parameter is no longer used + } + else if (section == "[Digits]") { migrated = migrated | replaceString(configLines[i], "LogImageLocation", "ROIImagesLocation"); migrated = migrated | replaceString(configLines[i], "LogfileRetentionInDays", "ROIImagesRetention"); } - - if (section == "[Analog]") { + else if (section == "[Analog]") { migrated = migrated | replaceString(configLines[i], "LogImageLocation", "ROIImagesLocation"); migrated = migrated | replaceString(configLines[i], "LogfileRetentionInDays", "ROIImagesRetention"); migrated = migrated | replaceString(configLines[i], "ExtendedResolution", ";UNUSED_PARAMETER"); // This parameter is no longer used } - - if (section == "[PostProcessing]") { - migrated = migrated | replaceString(configLines[i], "AnalogDigitalTransitionStart", "AnalogToDigitTransitionStart"); // Rename it - migrated = migrated | replaceString(configLines[i], ";PreValueUse = true", ";PreValueUse = false"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";PreValueUse", "PreValueUse"); // Enable it - + else if (section == "[PostProcessing]") { /* AllowNegativeRates has a as prefix! */ - if (isInString(configLines[i], "AllowNegativeRates") && isInString(configLines[i], ";")) { // It is the parameter "AllowNegativeRates" and it is commented out + if (isInString(configLines[i], "AllowNegativeRates") && isInString(configLines[i], ";")) { + // It is the parameter "AllowNegativeRates" and it is commented out migrated = migrated | replaceString(configLines[i], "true", "false"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";", ""); // Enable it + migrated = migrated | replaceString(configLines[i], ";", ""); // Enable it } - /* IgnoreLeadingNaN has a as prefix! */ - if (isInString(configLines[i], "IgnoreLeadingNaN") && isInString(configLines[i], ";")) { // It is the parameter "IgnoreLeadingNaN" and it is commented out + else if (isInString(configLines[i], "IgnoreLeadingNaN") && isInString(configLines[i], ";")) { + // It is the parameter "IgnoreLeadingNaN" and it is commented out migrated = migrated | replaceString(configLines[i], "true", "false"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";", ""); // Enable it + migrated = migrated | replaceString(configLines[i], ";", ""); // Enable it } - /* ExtendedResolution has a as prefix! */ - if (isInString(configLines[i], "ExtendedResolution") && isInString(configLines[i], ";")) { // It is the parameter "ExtendedResolution" and it is commented out + else if (isInString(configLines[i], "ExtendedResolution") && isInString(configLines[i], ";")) { + // It is the parameter "ExtendedResolution" and it is commented out migrated = migrated | replaceString(configLines[i], "true", "false"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";", ""); // Enable it + migrated = migrated | replaceString(configLines[i], ";", ""); // Enable it } + else { + migrated = migrated | replaceString(configLines[i], ";PreValueUse = true", ";PreValueUse = false"); // Set it to its default value + migrated = migrated | replaceString(configLines[i], ";PreValueUse", "PreValueUse"); // Enable it - migrated = migrated | replaceString(configLines[i], ";ErrorMessage = true", ";ErrorMessage = false"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";ErrorMessage", "ErrorMessage"); // Enable it + migrated = migrated | replaceString(configLines[i], ";ErrorMessage = true", ";ErrorMessage = false"); // Set it to its default value + migrated = migrated | replaceString(configLines[i], ";ErrorMessage", "ErrorMessage"); // Enable it - migrated = migrated | replaceString(configLines[i], ";CheckDigitIncreaseConsistency = true", ";CheckDigitIncreaseConsistency = false"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";CheckDigitIncreaseConsistency", "CheckDigitIncreaseConsistency"); // Enable it + migrated = migrated | replaceString(configLines[i], ";CheckDigitIncreaseConsistency = true", ";CheckDigitIncreaseConsistency = false"); // Set it to its default value + migrated = migrated | replaceString(configLines[i], ";CheckDigitIncreaseConsistency", "CheckDigitIncreaseConsistency"); // Enable it + } } - - if (section == "[MQTT]") { - migrated = migrated | replaceString(configLines[i], "SetRetainFlag", "RetainMessages"); // First rename it, enable it with its default value + else if (section == "[MQTT]") { + migrated = migrated | replaceString(configLines[i], "SetRetainFlag", "RetainMessages"); // First rename it, enable it with its default value migrated = migrated | replaceString(configLines[i], ";RetainMessages = true", ";RetainMessages = false"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";RetainMessages", "RetainMessages"); // Enable it + migrated = migrated | replaceString(configLines[i], ";RetainMessages", "RetainMessages"); // Enable it migrated = migrated | replaceString(configLines[i], ";HomeassistantDiscovery = true", ";HomeassistantDiscovery = false"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";HomeassistantDiscovery", "HomeassistantDiscovery"); // Enable it + migrated = migrated | replaceString(configLines[i], ";HomeassistantDiscovery", "HomeassistantDiscovery"); // Enable it - if (configLines[i].rfind("Topic", 0) != std::string::npos) // only if string starts with "Topic" (Was the naming in very old version) - { + // only if string starts with "Topic" (Was the naming in very old version) + if (configLines[i].rfind("Topic", 0) != std::string::npos) { migrated = migrated | replaceString(configLines[i], "Topic", "MainTopic"); } } - - if (section == "[InfluxDB]") { + else if (section == "[InfluxDB]") { /* Fieldname has a as prefix! */ - if (isInString(configLines[i], "Fieldname")) { // It is the parameter "Fieldname" + if (isInString(configLines[i], "Fieldname")) { + // It is the parameter "Fieldname" migrated = migrated | replaceString(configLines[i], "Fieldname", "Field"); // Rename it to Field } } - - if (section == "[InfluxDBv2]") { + else if (section == "[InfluxDBv2]") { /* Fieldname has a as prefix! */ - if (isInString(configLines[i], "Fieldname")) { // It is the parameter "Fieldname" + if (isInString(configLines[i], "Fieldname")) { + // It is the parameter "Fieldname" migrated = migrated | replaceString(configLines[i], "Fieldname", "Field"); // Rename it to Field } /* Database got renamed to Bucket! */ - if (isInString(configLines[i], "Database")) { // It is the parameter "Database" + else if (isInString(configLines[i], "Database")) { + // It is the parameter "Database" migrated = migrated | replaceString(configLines[i], "Database", "Bucket"); // Rename it to Bucket } } - - if (section == "[GPIO]") { - + else if (section == "[GPIO]") { } - - if (section == "[DataLogging]") { + else if (section == "[DataLogging]") { migrated = migrated | replaceString(configLines[i], "DataLogRetentionInDays", "DataFilesRetention"); /* DataLogActive is true by default! */ migrated = migrated | replaceString(configLines[i], ";DataLogActive = false", ";DataLogActive = true"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";DataLogActive", "DataLogActive"); // Enable it + migrated = migrated | replaceString(configLines[i], ";DataLogActive", "DataLogActive"); // Enable it } - - if (section == "[AutoTimer]") { + 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", "AutoStart"); // Enable it } - - if (section == "[Debug]") { + else if (section == "[Debug]") { migrated = migrated | replaceString(configLines[i], "Logfile ", "LogLevel "); // Whitespace needed so it does not match `LogfileRetentionInDays` /* LogLevel (resp. LogFile) was originally a boolean, but we switched it to an int * For both cases (true/false), we set it to level 2 (WARNING) */ @@ -689,27 +738,102 @@ void migrateConfiguration(void) { migrated = migrated | replaceString(configLines[i], "LogLevel = false", "LogLevel = 2"); migrated = migrated | replaceString(configLines[i], "LogfileRetentionInDays", "LogfilesRetention"); } - - if (section == "[System]") { + 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 + migrated = migrated | replaceString(configLines[i], ";SetupMode", "SetupMode"); // Enable it } } - if (migrated) { // At least one replacement happened - if (! RenameFile(CONFIG_FILE, CONFIG_FILE_BACKUP)) { + if (CamZoom_found == true) { + if (CamZoomSize_value == 0) { + // mode0 + // 1600 - 640 = 960 / 2 = max-Offset (+/-) 480 + // 1200 - 480 = 720 / 2 = max-Offset (+/-) 360 + + if (CamZoomOffsetX_value > 960) { + CamZoomOffsetX_value = 960; + } + else { + CamZoomOffsetX_value = (floor((CamZoomOffsetX_value - 480) / 8) * 8); + } + + if (CamZoomOffsetY_value > 720) { + CamZoomOffsetY_value = 720; + } + else { + CamZoomOffsetY_value = (floor((CamZoomOffsetY_value - 360) / 8) * 8); + } + + CamZoomSize_value = 29; + } + else { + // mode1 + // 800 - 640 = 160 / 2 = max-Offset (+/-) 80 + // 600 - 480 = 120 / 2 = max-Offset (+/-) 60 + + if (CamZoomOffsetX_value > 160) { + CamZoomOffsetX_value = 160; + } + else { + CamZoomOffsetX_value = (floor(((CamZoomOffsetX_value - 80) * 2) / 8) * 8); + } + + if (CamZoomOffsetY_value > 120) { + CamZoomOffsetY_value = 120; + } + else { + CamZoomOffsetY_value = (floor(((CamZoomOffsetY_value - 60) * 2) / 8) * 8); + } + + CamZoomSize_value = 9; + } + + if (CamZoom_lines > 0) { + if (CamZoom_value) { + configLines[CamZoom_lines] = ("CamZoom = true"); + } + else { + configLines[CamZoom_lines] = ("CamZoom = false"); + } + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Migrated Configfile line 'Zoom' to 'CamZoom'"); + migrated = true; + } + if (CamZoomSize_lines > 0) { + configLines[CamZoomSize_lines] = ("CamZoomSize = " + std::to_string(CamZoomSize_value)); + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Migrated Configfile line 'ZoomMode' to 'CamZoomSize'"); + migrated = true; + } + if (CamZoomOffsetX_lines > 0) { + configLines[CamZoomOffsetX_lines] = ("CamZoomOffsetX = " + std::to_string(CamZoomOffsetX_value)); + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Migrated Configfile line 'ZoomOffsetX' to 'CamZoomOffsetX'"); + migrated = true; + } + if (CamZoomOffsetY_lines > 0) { + configLines[CamZoomOffsetY_lines] = ("CamZoomOffsetY = " + std::to_string(CamZoomOffsetY_value)); + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Migrated Configfile line 'ZoomOffsetY' to 'CamZoomOffsetY'"); + migrated = true; + } + } + + if (migrated) { + // At least one replacement happened + if (!RenameFile(CONFIG_FILE, CONFIG_FILE_BACKUP)) { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create backup of Config file!"); return; } - FILE* pfile = fopen(CONFIG_FILE, "w"); + FILE *pfile = fopen(CONFIG_FILE, "w"); + for (int i = 0; i < configLines.size(); i++) { - fwrite(configLines[i].c_str() , configLines[i].length(), 1, pfile); - fwrite("\n" , 1, 1, pfile); + if (!isInString(configLines[i], ";UNUSED_PARAMETER")) { + fwrite(configLines[i].c_str(), configLines[i].length(), 1, pfile); + fwrite("\n", 1, 1, pfile); + } } + fclose(pfile); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Config file migrated. Saved backup to " + string(CONFIG_FILE_BACKUP)); } diff --git a/code/main/softAP.cpp b/code/main/softAP.cpp index 07a4506dd..fe903fd11 100644 --- a/code/main/softAP.cpp +++ b/code/main/softAP.cpp @@ -98,8 +98,8 @@ void wifi_init_softAP(void) void SendHTTPResponse(httpd_req_t *req) { std::string message = "

AI-on-the-edge - BASIC SETUP

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.

"; - 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)

"; + message += "This mode is always started if one of the following files is missing: /wlan.ini or the /config/config.ini.

"; + 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)

"; message += "Please follow the below instructions.

"; httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str())); @@ -109,7 +109,7 @@ void SendHTTPResponse(httpd_req_t *req) { message = "

1. Upload initial configuration to sd-card

"; 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.

"; + 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.

"; message += "
"; message += "

"; message += "The upload might take up to 60s. After a succesfull upload the page will be updated."; @@ -135,7 +135,7 @@ void SendHTTPResponse(httpd_req_t *req) message += "WLAN-SSIDSSID of the WLAN"; message += "WLAN-PasswordATTENTION: the password will not be encrypted during the sending."; message += "

"; - message += "

ATTENTION:

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\"!

"; + message += "

ATTENTION:

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\"!

"; httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str())); // message = " Hostname"; diff --git a/param-docs/parameter-pages/PostProcessing/NUMBER.AnalogDigitTransitionStart.md b/param-docs/parameter-pages/PostProcessing/NUMBER.AnalogToDigitTransitionStart.md similarity index 87% rename from param-docs/parameter-pages/PostProcessing/NUMBER.AnalogDigitTransitionStart.md rename to param-docs/parameter-pages/PostProcessing/NUMBER.AnalogToDigitTransitionStart.md index 90bb4f326..8d2aacf55 100644 --- a/param-docs/parameter-pages/PostProcessing/NUMBER.AnalogDigitTransitionStart.md +++ b/param-docs/parameter-pages/PostProcessing/NUMBER.AnalogToDigitTransitionStart.md @@ -1,4 +1,4 @@ -# Parameter `.AnalogDigitTransitionStart` +# Parameter `.AnalogToDigitTransitionStart` Default Value: `9.2` This can be used if you have wrong values, but the recognition of the individual ROIs are correct. diff --git a/param-docs/parameter-pages/TakeImage/CamDenoise.md b/param-docs/parameter-pages/TakeImage/CamDenoise.md index 978419640..466bea117 100644 --- a/param-docs/parameter-pages/TakeImage/CamDenoise.md +++ b/param-docs/parameter-pages/TakeImage/CamDenoise.md @@ -6,7 +6,7 @@ range on OV3660 and OV5640 (0 .. 8) -Default Value: `true` +Default Value: `0` See [here](../datasheets/Camera.ov2640_ds_1.8_.pdf) for the ov2640 camera datasheet.
See [here](../datasheets/OV5640_datasheet.pdf) for the ov5640 camera datasheet. diff --git a/sd-card/config/ana-class100_0171_s1_q.tflite b/sd-card/config/ana-class100_0171_s1_q.tflite deleted file mode 100644 index 6253d412c..000000000 Binary files a/sd-card/config/ana-class100_0171_s1_q.tflite and /dev/null differ diff --git a/sd-card/config/ana-cont_1208_s2_q.tflite b/sd-card/config/ana-cont_1208_s2_q.tflite deleted file mode 100644 index 29d9c3ea6..000000000 Binary files a/sd-card/config/ana-cont_1208_s2_q.tflite and /dev/null differ diff --git a/sd-card/config/config.ini b/sd-card/config/config.ini index 02f84a9fd..d3ba1bcc6 100644 --- a/sd-card/config/config.ini +++ b/sd-card/config/config.ini @@ -36,16 +36,14 @@ Demo = false [Alignment] InitialRotate = 0.0 -InitialMirror = false SearchFieldX = 20 SearchFieldY = 20 -AlignmentAlgo = Default -FlipImageSize = false +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_0711_s3_q.tflite CNNGoodThreshold = 0.5 ;ROIImagesLocation = /log/digit ;ROIImagesRetention = 3 @@ -93,17 +91,18 @@ HomeassistantDiscovery = false ;[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.Fieldname = undefined +;main.Measurement = undefined +;main.Field = undefined ;[Webhook] ;Uri = undefined @@ -117,7 +116,7 @@ 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 @@ -140,4 +139,5 @@ TimeZone = CET-1CEST,M3.5.0,M10.5.0/3 ;Hostname = undefined RSSIThreshold = -75 CPUFrequency = 160 +Tooltip = true SetupMode = true diff --git a/sd-card/config/dig-class11_1600_s2.tflite b/sd-card/config/dig-class11_1600_s2.tflite deleted file mode 100644 index 4d68aab1f..000000000 Binary files a/sd-card/config/dig-class11_1600_s2.tflite and /dev/null differ diff --git a/sd-card/config/dig-class11_1700_s2.tflite b/sd-card/config/dig-class11_1700_s2.tflite deleted file mode 100644 index c003ba901..000000000 Binary files a/sd-card/config/dig-class11_1700_s2.tflite and /dev/null differ diff --git a/sd-card/config/dig-cont_0611_s3_q.tflite b/sd-card/config/dig-cont_0611_s3_q.tflite deleted file mode 100644 index 3399d50d1..000000000 Binary files a/sd-card/config/dig-cont_0611_s3_q.tflite and /dev/null differ diff --git a/sd-card/config/dig-cont_0620_s3_q.tflite b/sd-card/config/dig-cont_0620_s3_q.tflite deleted file mode 100644 index 95932c748..000000000 Binary files a/sd-card/config/dig-cont_0620_s3_q.tflite and /dev/null differ diff --git a/sd-card/config/dig-cont_0640_s3_q.tflite b/sd-card/config/dig-cont_0640_s3_q.tflite deleted file mode 100644 index 41536f523..000000000 Binary files a/sd-card/config/dig-cont_0640_s3_q.tflite and /dev/null differ diff --git a/sd-card/config/dig-cont_0710_s3_q.tflite b/sd-card/config/dig-cont_0710_s3_q.tflite deleted file mode 100644 index e4e76b7ab..000000000 Binary files a/sd-card/config/dig-cont_0710_s3_q.tflite and /dev/null differ diff --git a/sd-card/config/dig-cont_0711_s3_q.tflite b/sd-card/config/dig-cont_0711_s3_q.tflite new file mode 100644 index 000000000..abfe4ceba Binary files /dev/null and b/sd-card/config/dig-cont_0711_s3_q.tflite differ diff --git a/sd-card/demo/530.07077.jpg b/sd-card/demo/530.07077.jpg new file mode 100644 index 000000000..f224fab8a Binary files /dev/null and b/sd-card/demo/530.07077.jpg differ diff --git a/sd-card/demo/530.07325.jpg b/sd-card/demo/530.07325.jpg new file mode 100644 index 000000000..5ebc7635f Binary files /dev/null and b/sd-card/demo/530.07325.jpg differ diff --git a/sd-card/demo/530.12067.jpg b/sd-card/demo/530.12067.jpg new file mode 100644 index 000000000..10b399d53 Binary files /dev/null and b/sd-card/demo/530.12067.jpg differ diff --git a/sd-card/demo/530.21419.jpg b/sd-card/demo/530.21419.jpg new file mode 100644 index 000000000..11e0ba7c2 Binary files /dev/null and b/sd-card/demo/530.21419.jpg differ diff --git a/sd-card/demo/530.48435.jpg b/sd-card/demo/530.48435.jpg new file mode 100644 index 000000000..4537295ff Binary files /dev/null and b/sd-card/demo/530.48435.jpg differ diff --git a/sd-card/demo/530.70265.jpg b/sd-card/demo/530.70265.jpg new file mode 100644 index 000000000..050c0ce69 Binary files /dev/null and b/sd-card/demo/530.70265.jpg differ diff --git a/sd-card/demo/530.95675.jpg b/sd-card/demo/530.95675.jpg new file mode 100644 index 000000000..6bc3e1def Binary files /dev/null and b/sd-card/demo/530.95675.jpg differ diff --git a/sd-card/demo/531.10877.jpg b/sd-card/demo/531.10877.jpg new file mode 100644 index 000000000..6a892c589 Binary files /dev/null and b/sd-card/demo/531.10877.jpg differ diff --git a/sd-card/demo/531.24108.jpg b/sd-card/demo/531.24108.jpg new file mode 100644 index 000000000..f6461adbf Binary files /dev/null and b/sd-card/demo/531.24108.jpg differ diff --git a/sd-card/demo/531.38301.jpg b/sd-card/demo/531.38301.jpg new file mode 100644 index 000000000..cdfa7fb42 Binary files /dev/null and b/sd-card/demo/531.38301.jpg differ diff --git a/sd-card/demo/531.63071.jpg b/sd-card/demo/531.63071.jpg new file mode 100644 index 000000000..76f62529c Binary files /dev/null and b/sd-card/demo/531.63071.jpg differ diff --git a/sd-card/demo/531.82235.jpg b/sd-card/demo/531.82235.jpg new file mode 100644 index 000000000..63b165dd0 Binary files /dev/null and b/sd-card/demo/531.82235.jpg differ diff --git a/sd-card/demo/config.ini b/sd-card/demo/config.ini new file mode 100644 index 000000000..0973caa04 --- /dev/null +++ b/sd-card/demo/config.ini @@ -0,0 +1,137 @@ +[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] +AutoStart = true +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 + diff --git a/sd-card/demo/files.txt b/sd-card/demo/files.txt new file mode 100644 index 000000000..fdd3f5c73 --- /dev/null +++ b/sd-card/demo/files.txt @@ -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 diff --git a/sd-card/demo/leer.txt b/sd-card/demo/leer.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/sd-card/demo/prevalue.ini b/sd-card/demo/prevalue.ini new file mode 100644 index 000000000..d26b4ddae --- /dev/null +++ b/sd-card/demo/prevalue.ini @@ -0,0 +1 @@ +main 2024-10-06T23:02:42+0200 0.05 diff --git a/sd-card/demo/readme.txt b/sd-card/demo/readme.txt new file mode 100644 index 000000000..6a726b451 --- /dev/null +++ b/sd-card/demo/readme.txt @@ -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 diff --git a/sd-card/demo/ref0.jpg b/sd-card/demo/ref0.jpg new file mode 100644 index 000000000..73e11569b Binary files /dev/null and b/sd-card/demo/ref0.jpg differ diff --git a/sd-card/demo/ref1.jpg b/sd-card/demo/ref1.jpg new file mode 100644 index 000000000..826e3759e Binary files /dev/null and b/sd-card/demo/ref1.jpg differ diff --git a/sd-card/demo/reference.jpg b/sd-card/demo/reference.jpg new file mode 100644 index 000000000..7d0bffaa0 Binary files /dev/null and b/sd-card/demo/reference.jpg differ diff --git a/sd-card/html/edit_config_template.html b/sd-card/html/edit_config_template.html index 585faf642..ba1602b0b 100644 --- a/sd-card/html/edit_config_template.html +++ b/sd-card/html/edit_config_template.html @@ -363,8 +363,8 @@

Configuration

$TOOLTIP_TakeImage_CamAutoSharpness @@ -421,8 +421,8 @@

Configuration

$TOOLTIP_TakeImage_CamAwb @@ -434,8 +434,8 @@

Configuration

$TOOLTIP_TakeImage_CamAwbGain @@ -447,8 +447,8 @@

Configuration

$TOOLTIP_TakeImage_CamAec @@ -460,8 +460,8 @@

Configuration

$TOOLTIP_TakeImage_CamAec2 @@ -495,8 +495,8 @@

Configuration

$TOOLTIP_TakeImage_CamAgc @@ -520,8 +520,8 @@

Configuration

$TOOLTIP_TakeImage_CamBpc @@ -533,8 +533,8 @@

Configuration

$TOOLTIP_TakeImage_CamWpc @@ -546,8 +546,8 @@

Configuration

$TOOLTIP_TakeImage_CamRawGma @@ -559,8 +559,8 @@

Configuration

$TOOLTIP_TakeImage_CamLenc @@ -572,8 +572,8 @@

Configuration

$TOOLTIP_TakeImage_CamHmirror @@ -585,8 +585,8 @@

Configuration

$TOOLTIP_TakeImage_CamVflip @@ -598,8 +598,8 @@

Configuration

$TOOLTIP_TakeImage_CamDcw @@ -622,8 +622,8 @@

Configuration

$TOOLTIP_TakeImage_CamZoom @@ -681,8 +681,8 @@

Configuration

$TOOLTIP_TakeImage_Demo @@ -850,8 +850,8 @@

$TOOLTIP_PostProcessing_ErrorMessage @@ -888,8 +888,8 @@

$TOOLTIP_PostProcessing_NUMBER.AllowNegativeRates @@ -933,11 +933,11 @@

- + - $TOOLTIP_PostProcessing_NUMBER.AnalogToDigitTransitionStart @@ -988,8 +988,8 @@

$TOOLTIP_PostProcessing_NUMBER.IgnoreLeadingNaN @@ -1014,8 +1014,8 @@

$TOOLTIP_AutoTimer_AutoStart @@ -1930,8 +1930,8 @@

@@ -2323,7 +2323,7 @@

Reference Image and Camera Settings

$TOOLTIP_TakeImage_CamZoom @@ -232,8 +232,8 @@

Reference Image and Camera Settings

$TOOLTIP_TakeImage_CamAec2 @@ -245,8 +245,8 @@

Reference Image and Camera Settings

$TOOLTIP_TakeImage_CamHmirror @@ -267,8 +267,8 @@

Reference Image and Camera Settings

$TOOLTIP_TakeImage_CamVflip @@ -332,8 +332,8 @@

Reference Image and Camera Settings

$TOOLTIP_TakeImage_CamAec @@ -355,8 +355,8 @@

Reference Image and Camera Settings

$TOOLTIP_TakeImage_CamAutoSharpness @@ -617,7 +617,7 @@

Reference Image and Camera Settings

} catch (error){} - document.getElementById("overlaytext").innerHTML = "Device is busy, plase waiting...

Current step: " + _xhttp.responseText; + document.getElementById("overlaytext").innerHTML = "Device is busy, please wait.

Current step: " + _xhttp.responseText; console.log("Device is busy, waiting 5s then checking again..."); await sleep(2000); } @@ -1053,7 +1053,7 @@

Reference Image and Camera Settings

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");