diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yaml similarity index 100% rename from .github/ISSUE_TEMPLATE/bug_report.yml rename to .github/ISSUE_TEMPLATE/bug_report.yaml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yaml similarity index 100% rename from .github/ISSUE_TEMPLATE/config.yml rename to .github/ISSUE_TEMPLATE/config.yaml diff --git a/.github/label-commenter-config.yml b/.github/label-commenter-config.yaml similarity index 100% rename from .github/label-commenter-config.yml rename to .github/label-commenter-config.yaml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yaml similarity index 92% rename from .github/workflows/build.yml rename to .github/workflows/build.yaml index 4be94a7d..00f39750 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yaml @@ -15,7 +15,7 @@ jobs: with: concurrent_skipping: same_content_newer - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive @@ -25,28 +25,28 @@ jobs: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT - name: Update PIP cache on every commit - uses: actions/cache@v3.2.3 + uses: actions/cache@v4 with: path: ~/.cache/pip key: pip-${{ github.run_id }} restore-keys: pip # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache - name: Update PlatformIO cache on every commit - uses: actions/cache@v3.2.3 + uses: actions/cache@v4 with: path: ~/.platformio key: platformio-${{ github.run_id }} restore-keys: platformio # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache - name: Update Build cache on every commit - uses: actions/cache@v3.2.3 + uses: actions/cache@v4 with: path: ./code/.pio/ key: build-${{ github.run_id }} restore-keys: build # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache - name: Update generated-files cache on every commit - uses: actions/cache@v3.2.3 + uses: actions/cache@v4 with: path: | ./code/.pio/build/esp32cam/firmware.bin @@ -57,7 +57,7 @@ jobs: restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.10' @@ -70,7 +70,7 @@ jobs: #run: echo "Testing... ${{ github.ref_name }}, ${{ steps.vars.outputs.sha_short }}" > ./sd-card/html/version.txt; mkdir -p ./code/.pio/build/esp32cam/; cd ./code/.pio/build/esp32cam/; echo "${{ steps.vars.outputs.sha_short }}" > firmware.bin; cp firmware.bin partitions.bin; cp firmware.bin bootloader.bin # Testing run: cd code; platformio run --environment esp32cam - - name: Prepare Web UI (copy data from repo, generate tooltip pages and update hashes in all files) + - name: Prepare Web UI (generate tooltip pages and update hashes in all files) run: | rm -rf ./html mkdir html @@ -79,7 +79,7 @@ jobs: python -m pip install markdown mkdir html/param-tooltips cd tools/parameter-tooltip-generator - bash generate-param-doc-tooltips.sh + python generate-param-doc-tooltips.py cd ../.. cp -r ./sd-card/html/* ./html/ @@ -101,10 +101,10 @@ jobs: needs: build steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Update generated-files cache on every commit - uses: actions/cache@v3.2.3 + uses: actions/cache@v4 with: path: | ./code/.pio/build/esp32cam/firmware.bin @@ -115,7 +115,7 @@ jobs: restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache - name: Update update cache on every commit - uses: actions/cache@v3.2.3 + uses: actions/cache@v4 with: path: update key: update-${{ github.run_id }} @@ -144,7 +144,7 @@ jobs: cp ./sd-card/config/*.tflite ./update/config/ 2>/dev/null || true - name: Upload update as update.zip artifact (Firmware + Web UI + CNN) - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: "AI-on-the-edge-device__update__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})" path: ./update/* @@ -164,10 +164,10 @@ jobs: needs: build steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Update generated-files cache on every commit - uses: actions/cache@v3.2.3 + uses: actions/cache@v4 with: path: | ./code/.pio/build/esp32cam/firmware.bin @@ -178,7 +178,7 @@ jobs: restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache - name: Update remote_setup cache on every commit - uses: actions/cache@v3.2.3 + uses: actions/cache@v4 with: path: remote_setup key: remote_setup-${{ github.run_id }} @@ -205,7 +205,7 @@ jobs: cp ./sd-card/config/* ./remote_setup/config/ 2>/dev/null || true - name: Upload remote_setup as remote_setup.zip artifact (Firmware + Web UI + Config) - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: "AI-on-the-edge-device__remote-setup__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})" path: ./remote_setup/* @@ -220,10 +220,10 @@ jobs: needs: build steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Update generated-files cache on every commit - uses: actions/cache@v3.2.3 + uses: actions/cache@v4 with: path: | ./code/.pio/build/esp32cam/firmware.bin @@ -234,7 +234,7 @@ jobs: restore-keys: generated-files # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache - name: Update manual_setup cache on every commit - uses: actions/cache@v3.2.3 + uses: actions/cache@v4 with: path: manual_setup key: manual_setup-${{ github.run_id }} @@ -263,7 +263,7 @@ jobs: cd ./manual_setup - name: Upload manual_setup.zip artifact (Firmware + Bootloader + Partitions + Web UI) - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: "AI-on-the-edge-device__manual-setup__${{ steps.vars.outputs.branch }}_(${{ steps.vars.outputs.sha_short }})" path: ./manual_setup @@ -284,24 +284,24 @@ jobs: id-token: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Update update cache on every commit - uses: actions/cache@v3.2.3 + uses: actions/cache@v4 with: path: update key: update-${{ github.run_id }} restore-keys: update # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache - name: Update remote_setup cache on every commit - uses: actions/cache@v3.2.3 + uses: actions/cache@v4 with: path: remote_setup key: remote_setup-${{ github.run_id }} restore-keys: remote_setup # This matches above key as it is only used as a prefix. it the restores the nearest cache, see https://github.com/restore-keys:/blob/main/tips-and-workarounds.md#update-a-cache - name: Update manual_setup cache on every commit - uses: actions/cache@v3.2.3 + uses: actions/cache@v4 with: path: manual_setup key: manual_setup-${{ github.run_id }} @@ -396,7 +396,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Get version of last release id: last_release @@ -410,20 +410,21 @@ jobs: run: | echo "Updating Web installer to use firmware from ${{ steps.last_release.outputs.tag_name }}..." rm -f docs/binary/firmware.bin - wget https://github.com/jomjol/AI-on-the-edge-device/releases/download/${{ steps.last_release.outputs.tag_name }}/AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip + wget ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.last_release.outputs.tag_name }}/AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip unzip AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip cp -f firmware.bin docs/binary/firmware.bin echo "Updating index and manifest file..." sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/index.html sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json + - name: Setup Pages - uses: actions/configure-pages@v2 + uses: actions/configure-pages@v4 - name: Upload artifact - uses: actions/upload-pages-artifact@v1 + uses: actions/upload-pages-artifact@v2 with: path: 'docs' - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v1 + uses: actions/deploy-pages@v3 # Note: v4 does not work! diff --git a/.github/workflows/clear_cache.yml b/.github/workflows/clear_cache.yaml similarity index 100% rename from .github/workflows/clear_cache.yml rename to .github/workflows/clear_cache.yaml diff --git a/.github/workflows/manual-update-webinstaller.yml b/.github/workflows/manual-update-webinstaller.yaml similarity index 100% rename from .github/workflows/manual-update-webinstaller.yml rename to .github/workflows/manual-update-webinstaller.yaml diff --git a/.github/workflows/reply-bot.yml b/.github/workflows/reply-bot.yaml similarity index 91% rename from .github/workflows/reply-bot.yml rename to .github/workflows/reply-bot.yaml index a9e0421f..7c7bb9c5 100644 --- a/.github/workflows/reply-bot.yml +++ b/.github/workflows/reply-bot.yaml @@ -1,5 +1,5 @@ # Reply Bot -# It uses the configuration in .github/label-commenter-config.yml +# It uses the configuration in .github/label-commenter-config.yaml # See https://github.com/peaceiris/actions-label-commenter name: Reply-Bot @@ -25,7 +25,7 @@ jobs: #################################################################### ## Remove labels again (issues only) - ## Make sure to also add the reply message to .github/label-commenter-config.yml! + ## Make sure to also add the reply message to .github/label-commenter-config.yaml! ## This currently seems no longer to work due to changes on the actions-cool/issues-helper! #################################################################### # - name: Remove 'Logfile' label again (issues only) @@ -74,6 +74,7 @@ jobs: ## Write the response #################################################################### - name: Write Response - uses: peaceiris/actions-label-commenter@c2d00660c86f2b9ed0fb35b372c451558eba85b3 + uses: peaceiris/actions-label-commenter@v1 with: - repo-token: "${{ secrets.GITHUB_TOKEN }}" + github_token: "${{ secrets.GITHUB_TOKEN }}" + config_file: .github/label-commenter-config.yaml diff --git a/Changelog.md b/Changelog.md index ae85e248..8a8f853c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,8 +1,60 @@ -## [unreleased] - 2023-12-21 +## [update] - 2024-03-30 -### Changes +For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.6.0...v15.7.0) -For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/rolling...v15.3.0) +#### Core Changes + +- New tflite-Model for Analog (v13.0.0) +- New tflite-Model for Digital Hybrid (v7.0.0) + +#### Bug Fixes + +- tbd + +## [15.7.0] - 2024-02-17 + +For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.6.0...v15.7.0) + +#### Core Changes +- Added new camera settings (See `Settings > Alignment > Reference Image and Camera Settings`). You might need to re-create the reference image and alignment marks. Note worthy: + - You can now crop the image + - Support to configure sharpness, grayscale, negatoive and exposure +- Enhanced various WebUI pages with better explanations and usability +- Add Firmware Version to MQTT + +#### Bug Fixes +- Reverted "Implemented late analog / digital transition [#2778](https://github.com/jomjol/AI-on-the-edge-device/pull/2778) (introduced in `v15.5`) as is seems to cause issues for many users. + + +## [15.6.0] - 2024-02-09 + +For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.5.0...v15.6.0) + +#### Fixed + +* Fixed issues with the SD-Card initialization + +## [15.5.0] - 2024-02-02 + +For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.4.0...v15.5.0) + +#### Changed + + - Update PlattformIO to v6.5.0, which means esp-idf to v5.1 + - Enhance busy notification + - Implemented late analog / digital transition + +#### Fixed + +* ATA-TRIM: workaround for old SD-cards with no trim function to work with esp-idf v5.x +* InfluxDB: Modified the time conversions to be more stable (UTC vs. local time shifts) +* Fix negatives on extended resolution false +* Show chip infos on info page +* Fix memory leaks in tflite integration + +## [15.4.0] - 2023-12-22 + +For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.3.0...v15.4.0) #### Changed @@ -18,6 +70,8 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/ - dig-class100-0167_s2_q.tflite - dig-class11_1700_s2.tflite - ana-cont_1208_s2_q.tflite + + - Added config entries for MQTT TLS #### Fixed @@ -27,13 +81,10 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/ * Minor html response bugfix - Memory leakage (MQTT) - ## [15.3.0] - 2023-07-22 -### Changes - For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.3.0...v15.2.4) #### Changed @@ -43,13 +94,8 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/ - ana-cont_1207_s2_q.tflite - dig-cont_0620_s3_q.tflite - - - ## [15.2.4] - 2023-05-02 -### Changes - For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.2.1...v15.2.4) #### Changed @@ -73,8 +119,6 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/ ## [15.2.0] - 2023-04-23 -### Changes - For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.1...v15.2.0) #### Added @@ -104,8 +148,6 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/ ## [15.1.1] - 2023-03-23 -### Changes - For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.0...v15.1.1) #### Added diff --git a/code/README.md b/code/README.md index d7dce832..b70da572 100644 --- a/code/README.md +++ b/code/README.md @@ -69,3 +69,6 @@ pio device monitor -p /dev/ttyUSB0 - `pio run --target erase` to erase the flash - `pio run --target upload` this will upload the `bootloader.bin, partitions.bin,firmware.bin` from the `code/.pio/build/esp32cam/` folder. - `pio device monitor` to observe the logs via uart + +# Update Parameters +If you create or rename a parameter, make sure to update its documentation in `../param-docs/parameter-pages`! Check the `../param-docs/README.md` for more information. diff --git a/code/components/jomjol_controlcamera/ClassControllCamera.cpp b/code/components/jomjol_controlcamera/ClassControllCamera.cpp index 65bba6cc..5095cb16 100644 --- a/code/components/jomjol_controlcamera/ClassControllCamera.cpp +++ b/code/components/jomjol_controlcamera/ClassControllCamera.cpp @@ -31,6 +31,8 @@ #include "driver/ledc.h" #include "MainFlowControl.h" +#include "ov2640_sharpness.h" + #if (ESP_IDF_VERSION_MAJOR >= 5) #include "soc/periph_defs.h" #include "esp_private/periph_ctrl.h" @@ -39,20 +41,34 @@ #include "soc/io_mux_reg.h" #include "esp_rom_gpio.h" #define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio -#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c) -#define gpio_matrix_out(a,b,c,d) esp_rom_gpio_connect_out_signal(a,b,c,d) +#define gpio_matrix_in(a, b, c) esp_rom_gpio_connect_in_signal(a, b, c) +#define gpio_matrix_out(a, b, c, d) esp_rom_gpio_connect_out_signal(a, b, c, d) #define ets_delay_us(a) esp_rom_delay_us(a) #endif -static const char *TAG = "CAM"; +CCamera Camera; +camera_controll_config_temp_t CCstatus; +static const char *TAG = "CAM"; /* Camera live stream */ #define PART_BOUNDARY "123456789000000000000987654321" -static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY; -static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n"; -static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n"; +static const char *_STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY; +static const char *_STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n"; +static const char *_STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n"; +uint8_t *demoImage = NULL; // Buffer holding the demo image in bytes +#define DEMO_IMAGE_SIZE 30000 // Max size of demo image in bytes + +// Camera module bus communications frequency. +// Originally: config.xclk_freq_mhz = 20000000, but this lead to visual artifacts on many modules. +// See https://github.com/espressif/esp32-camera/issues/150#issuecomment-726473652 et al. +#if !defined(XCLK_FREQ_MHZ) +// int xclk = 8; +int xclk = 20; // Orginal value +#else +int xclk = XCLK_FREQ_MHZ; +#endif static camera_config_t camera_config = { .pin_pwdn = CAM_PIN_PWDN, @@ -73,90 +89,439 @@ static camera_config_t camera_config = { .pin_href = CAM_PIN_HREF, .pin_pclk = CAM_PIN_PCLK, - //XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental) - .xclk_freq_hz = 20000000, // Orginal value -// .xclk_freq_hz = 5000000, // Test to get rid of the image errors !!!! Hangs in version 9.2 !!!! - .ledc_timer = LEDC_TIMER_0, - .ledc_channel = LEDC_CHANNEL_0, + .xclk_freq_hz = (xclk * 1000000), + .ledc_timer = LEDC_TIMER_0, // LEDC timer to be used for generating XCLK + .ledc_channel = LEDC_CHANNEL_0, // LEDC channel to be used for generating XCLK - .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 = 12, //0-63 lower number means higher quality - .fb_count = 1, //if more than one, i2s runs in continuous mode. Use only with JPEG + .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 + .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 + .grab_mode = CAMERA_GRAB_LATEST, // only from new esp32cam version }; - -CCamera Camera; - -uint8_t *demoImage = NULL; // Buffer holding the demo image in bytes - -#define DEMO_IMAGE_SIZE 30000 // Max size of demo image in bytes - -typedef struct { - httpd_req_t *req; - size_t len; +typedef struct +{ + httpd_req_t *req; + size_t len; } jpg_chunking_t; +CCamera::CCamera(void) +{ +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "CreateClassCamera"); +#endif + CCstatus.WaitBeforePicture = 2; -bool CCamera::testCamera(void) { - bool success; - camera_fb_t *fb = esp_camera_fb_get(); - if (fb) { - success = true; - } - else { - success = false; - } - - esp_camera_fb_return(fb); - return success; + ledc_init(); } +esp_err_t CCamera::InitCam(void) +{ + ESP_LOGD(TAG, "Init Camera"); + + CCstatus.ImageQuality = camera_config.jpeg_quality; + CCstatus.ImageFrameSize = camera_config.frame_size; + + // initialize the camera + esp_camera_deinit(); // De-init in case it was already initialized + esp_err_t err = esp_camera_init(&camera_config); + + if (err != ESP_OK) + { + ESP_LOGE(TAG, "Camera Init Failed"); + return err; + } + + CCstatus.CameraInitSuccessful = true; + + // Get a reference to the sensor + sensor_t *s = esp_camera_sensor_get(); + + if (s != NULL) + { + // Dump camera module, warn for unsupported modules. + switch (s->id.PID) + { + case OV2640_PID: + ESP_LOGI(TAG, "OV2640 camera module detected"); + break; + case OV3660_PID: + ESP_LOGI(TAG, "OV3660 camera module detected"); + break; + default: + ESP_LOGE(TAG, "Camera module is unknown and not properly supported!"); + CCstatus.CameraInitSuccessful = false; + } + } + + if (CCstatus.CameraInitSuccessful) + { + return ESP_OK; + } + else + { + return ESP_FAIL; + } +} + +bool CCamera::testCamera(void) +{ + bool success; + camera_fb_t *fb = esp_camera_fb_get(); + + if (fb) + { + success = true; + } + else + { + success = false; + } + + esp_camera_fb_return(fb); + + return success; +} void CCamera::ledc_init(void) { #ifdef USE_PWM_LEDFLASH - // Prepare and then apply the LEDC PWM timer configuration - ledc_timer_config_t ledc_timer = { }; + ledc_timer_config_t ledc_timer = {}; - ledc_timer.speed_mode = LEDC_MODE; - ledc_timer.timer_num = LEDC_TIMER; - ledc_timer.duty_resolution = LEDC_DUTY_RES; - ledc_timer.freq_hz = LEDC_FREQUENCY; // Set output frequency at 5 kHz - ledc_timer.clk_cfg = LEDC_AUTO_CLK; + ledc_timer.speed_mode = LEDC_MODE; + ledc_timer.timer_num = LEDC_TIMER; + ledc_timer.duty_resolution = LEDC_DUTY_RES; + ledc_timer.freq_hz = LEDC_FREQUENCY; // Set output frequency at 5 kHz + ledc_timer.clk_cfg = LEDC_AUTO_CLK; ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); // Prepare and then apply the LEDC PWM channel configuration - ledc_channel_config_t ledc_channel = { }; + ledc_channel_config_t ledc_channel = {}; - ledc_channel.speed_mode = LEDC_MODE; - ledc_channel.channel = LEDC_CHANNEL; - ledc_channel.timer_sel = LEDC_TIMER; - ledc_channel.intr_type = LEDC_INTR_DISABLE; - ledc_channel.gpio_num = LEDC_OUTPUT_IO; - ledc_channel.duty = 0; // Set duty to 0% - ledc_channel.hpoint = 0; + ledc_channel.speed_mode = LEDC_MODE; + ledc_channel.channel = LEDC_CHANNEL; + ledc_channel.timer_sel = LEDC_TIMER; + ledc_channel.intr_type = LEDC_INTR_DISABLE; + ledc_channel.gpio_num = LEDC_OUTPUT_IO; + ledc_channel.duty = 0; // Set duty to 0% + ledc_channel.hpoint = 0; + // ledc_channel.flags.output_invert = LEDC_OUTPUT_INVERT; ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); - #endif } +void CCamera::SetLEDIntensity(float _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); +} -static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len) +bool CCamera::getCameraInitSuccessful(void) +{ + return CCstatus.CameraInitSuccessful; +} + +esp_err_t CCamera::setSensorDatenFromCCstatus(void) +{ + sensor_t *s = esp_camera_sensor_get(); + + if (s != NULL) + { + s->set_framesize(s, CCstatus.ImageFrameSize); + s->set_gainceiling(s, CCstatus.ImageGainceiling); // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128) + + 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_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_exposure_ctrl(s, CCstatus.ImageAec); // 0 = disable , 1 = enable + s->set_ae_level(s, CCstatus.ImageAeLevel); // -2 to 2 + s->set_aec_value(s, CCstatus.ImageAecValue); // 0 to 1200 + + s->set_aec2(s, CCstatus.ImageAec2); // 0 = disable , 1 = enable + + s->set_gain_ctrl(s, CCstatus.ImageAgc); // 0 = disable , 1 = enable + s->set_agc_gain(s, CCstatus.ImageAgcGain); // 0 to 30 + + 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_lenc(s, CCstatus.ImageLenc); // 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_dcw(s, CCstatus.ImageDcw); // 0 = disable , 1 = enable + + 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_awb_gain(s, CCstatus.ImageAwbGain); // 0 = disable , 1 = enable + s->set_whitebal(s, CCstatus.ImageAwb); // 0 = disable , 1 = enable + + // special_effect muß als Letztes gesetzt werden, sonst geht es nicht + 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) + + TickType_t xDelay2 = 1000 / portTICK_PERIOD_MS; + vTaskDelay(xDelay2); + + return ESP_OK; + } + else + { + return ESP_FAIL; + } +} + +esp_err_t CCamera::getSensorDatenToCCstatus(void) +{ + sensor_t *s = esp_camera_sensor_get(); + + if (s != NULL) + { + CCstatus.ImageFrameSize = (framesize_t)s->status.framesize; + 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.ImageHmirror = s->status.hmirror; + CCstatus.ImageVflip = s->status.vflip; + CCstatus.ImageDcw = s->status.dcw; + + return ESP_OK; + } + else + { + return ESP_FAIL; + } +} + +void CCamera::SetZoomSize(bool zoomEnabled, int zoomOffsetX, int zoomOffsetY, int imageSize) +{ + sensor_t *s = esp_camera_sensor_get(); + + if (s != NULL) + { + if (zoomEnabled) + { + // ov2640_sensor_mode_t _mode = OV2640_MODE_UXGA; // 1600x1200 + // ov2640_sensor_mode_t _mode = OV2640_MODE_SVGA; // 800x600 + // ov2640_sensor_mode_t _mode = OV2640_MODE_CIF; // 400x296 + int _mode = 0; + + int _offsetx = zoomOffsetX; + int _offsety = zoomOffsetY; + int _imageSize_temp = 0; + int _maxX = 0; + int _maxY = 0; + + if (imageSize < 29) + { + _imageSize_temp = (29 - imageSize); + } + + // This works only if the aspect ratio of 4:3 is preserved in the window size. + // use values divisible by 8 without remainder + int _imageWidth = CCstatus.ImageWidth + (_imageSize_temp * 4 * 8); + int _imageHeight = CCstatus.ImageHeight + (_imageSize_temp * 3 * 8); + + _maxX = 1600 - _imageWidth; + _maxY = 1200 - _imageHeight; + + if ((abs(_offsetx) * 2) > _maxX) + { + if (_offsetx > 0) + { + _offsetx = _maxX; + } + else + { + _offsetx = 0; + } + } + else + { + if (_offsetx > 0) + { + // wenn der Wert von _offsetx nicht durch 8 teilbar ist, + // werden die Farben sehr oft vertauscht(insbesondere Rot mit Blau) + _offsetx = ((_maxX / 2) + _offsetx); + } + else + { + // wenn der Wert von _offsetx nicht durch 8 teilbar ist, + // werden die Farben sehr oft vertauscht(insbesondere Rot mit Blau) + _offsetx = ((_maxX / 2) + _offsetx); + } + } + + if ((abs(_offsety) * 2) > _maxY) + { + if (_offsety > 0) + { + _offsety = _maxY; + } + else + { + _offsety = 0; + } + } + else + { + if (_offsety > 0) + { + // wenn der Wert von _offsety nicht durch 8 teilbar ist, + // werden die Farben sehr oft vertauscht(insbesondere Rot mit Blau) + _offsety = ((_maxY / 2) + _offsety); + } + else + { + // wenn der Wert von _offsety nicht durch 8 teilbar ist, + // werden die Farben sehr oft vertauscht(insbesondere Rot mit Blau) + _offsety = ((_maxY / 2) + _offsety); + } + } + + // _mode sets the sensor resolution (3 options available), + // _offsetx and _offsety set the start of the ROI, + // _imageWidth and _imageHeight set the size of the ROI, + // CCstatus.ImageWidth and CCstatus.ImageHeight set the output window size. + SetCamWindow(s, _mode, _offsetx, _offsety, _imageWidth, _imageHeight, CCstatus.ImageWidth, CCstatus.ImageHeight); + } + else + { + s->set_framesize(s, CCstatus.ImageFrameSize); + } + } +} + +void CCamera::SetQualityZoomSize(int qual, framesize_t resol, bool zoomEnabled, int zoomOffsetX, int zoomOffsetY, int imageSize) +{ + qual = min(63, max(8, qual)); // Limit quality from 8..63 (values lower than 8 tent to be unstable) + + SetImageWidthHeightFromResolution(resol); + + sensor_t *s = esp_camera_sensor_get(); + + if (s != NULL) + { + s->set_quality(s, qual); + SetZoomSize(zoomEnabled, zoomOffsetX, zoomOffsetY, imageSize); + } + else + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SetQualityZoomSize, Failed to get Cam control structure"); + } +} + +void CCamera::SetCamSharpness(bool _autoSharpnessEnabled, int _sharpnessLevel) +{ + _sharpnessLevel = min(2, max(-2, _sharpnessLevel)); + + sensor_t *s = esp_camera_sensor_get(); + + if (s != NULL) + { + // post processing + if (_autoSharpnessEnabled) + { + s->set_sharpness(s, 0); + ov2640_enable_auto_sharpness(s); + } + else + { + ov2640_set_sharpness(s, _sharpnessLevel); + } + } + else + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SetCamSharpness, Failed to get Cam control structure"); + } +} + +/* + * resolution = 0 \\ OV2640_MODE_UXGA -> 1600 x 1200 + * resolution = 1 \\ OV2640_MODE_SVGA -> 800 x 600 + * resolution = 2 \\ OV2640_MODE_CIF -> 400 x 296 + * resolution = 3 \\ OV2640_MODE_MAX + */ +void CCamera::SetCamWindow(sensor_t *s, int resolution, int xOffset, int yOffset, int xTotal, int yTotal, int xOutput, int yOutput) +{ + // - (xOffset,yOffset) is the origin of the window in pixels and (xLength,yLength) is the size of the window in pixels. + // - (xOffset,yOffset) ist der Ursprung des Fensters in Pixel und (xLength,yLength) ist die Größe des Fensters in Pixel. + + // - Be aware that changing the resolution will effectively overwrite these settings. + // - Beachten Sie, dass eine Änderung der Auflösung diese Einstellungen effektiv überschreibt. + + // - This works only if the aspect ratio of 4:3 is preserved in the window size. + // - Dies funktioniert nur, wenn das Seitenverhältnis von 4:3 in der Fenstergröße beibehalten wird. + + // - total_x and total_y defines the size on the sensor + // - total_x und total_y definieren die Größe des Sensors + + // - width and height defines the resulting image(may be smaller than the size on the sensor) + // - width und height definieren das resultierende Bild (kann kleiner sein als die Größe des Sensor) + + // - keep the aspect total_x : total_y == width : height + // - Behalten Sie den Aspekt total_x : total_y == width : height bei + + // - use values divisible by 8 without remainder + // - Verwenden Sie Werte, die ohne Rest durch 8 teilbar sind + + // - start with total_x = width and total_y = height, reduce both values by eg.32 pixels + // - Beginnen Sie mit total_x = width und total_y = height und reduzieren Sie beide Werte um z.B.32 Pixel + + // - next try moving with offset_x or offset_y by 8 pixels + // - Versuchen Sie als Nächstes, mit offset_x oder offset_y um 8 Pixel zu verschieben + + // set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning) + // set_window(sensor, (ov2640_sensor_mode_t)startX, offsetX, offsetY, totalX, totalY, outputX, outputY); + // set_window(sensor, mode, offset_x, offset_y, max_x, max_y, w, h); + + int unused = 0; + s->set_res_raw(s, resolution, unused, unused, unused, xOffset, yOffset, xTotal, yTotal, xOutput, yOutput, unused, unused); +} + +static size_t jpg_encode_stream(void *arg, size_t index, const void *data, size_t len) { jpg_chunking_t *j = (jpg_chunking_t *)arg; - if(!index) { + if (!index) + { j->len = 0; } - if(httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK) { + if (httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK) + { return 0; } @@ -165,161 +530,35 @@ static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size return len; } - -bool CCamera::SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation) -{ - _brightness = min(2, max(-2, _brightness)); - _contrast = min(2, max(-2, _contrast)); - _saturation = min(2, max(-2, _saturation)); - - sensor_t * s = esp_camera_sensor_get(); - if (s) { - s->set_saturation(s, _saturation); - s->set_contrast(s, _contrast); - s->set_brightness(s, _brightness); - - /* Workaround - bug in cam library - enable bits are set without using bitwise OR logic -> only latest enable setting is used */ - /* Library version: https://github.com/espressif/esp32-camera/commit/5c8349f4cf169c8a61283e0da9b8cff10994d3f3 */ - /* 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 readdish and b&w) enable - byte_0->bit4 = anable gray -> read spitial effect (Antique and blunish and greenish and readdish 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 -> read 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 --> ? - */ - - //s->set_reg(s, 0x7C, 0xFF, 2); // Optional feature - hue setting: Select byte 2 in register 0x7C to set hue value - //s->set_reg(s, 0x7D, 0xFF, 0); // Optional feature - hue setting: Hue value 0 - 255 - s->set_reg(s, 0xFF, 0x01, 0); // Select DSP bank - s->set_reg(s, 0x7C, 0xFF, 0); // Select byte 0 in register 0x7C - s->set_reg(s, 0x7D, 7, 7); // Set bit 0, 1, 2 in register 0x7D to enable saturation, contrast, brightness and hue control - } - else { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SetBrightnessContrastSaturation: Failed to get control structure"); - } - - if (((_brightness != brightness) || (_contrast != contrast) || (_saturation != saturation)) && isFixedExposure) - EnableAutoExposure(waitbeforepicture_org); - - brightness = _brightness; - contrast = _contrast; - saturation = _saturation; - - ESP_LOGD(TAG, "brightness %d, contrast: %d, saturation %d", brightness, contrast, saturation); - - return true; -} - - -void CCamera::SetQualitySize(int qual, framesize_t resol) -{ - qual = min(63, max(8, qual)); // Limit quality from 8..63 (values lower than 8 tent to be unstable) - - sensor_t * s = esp_camera_sensor_get(); - if (s) { - s->set_quality(s, qual); - s->set_framesize(s, resol); - } - else { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SetQualitySize: Failed to get control structure"); - } - - ActualResolution = resol; - ActualQuality = qual; - - if (resol == FRAMESIZE_QVGA) - { - image_height = 240; - image_width = 320; - } - else if (resol == FRAMESIZE_VGA) - { - image_height = 480; - image_width = 640; - } -} - - -void CCamera::EnableAutoExposure(int flash_duration) -{ - ESP_LOGD(TAG, "EnableAutoExposure"); - - LEDOnOff(true); - if (flash_duration > 0) { - LightOnOff(true); - const TickType_t xDelay = flash_duration / portTICK_PERIOD_MS; - vTaskDelay( xDelay ); - } - - camera_fb_t * fb = esp_camera_fb_get(); - esp_camera_fb_return(fb); - fb = esp_camera_fb_get(); - if (!fb) { - LEDOnOff(false); - LightOnOff(false); - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "EnableAutoExposure: Capture Failed. " - "Check camera module and/or proper electrical connection"); - //doReboot(); - } - esp_camera_fb_return(fb); - - sensor_t * s = esp_camera_sensor_get(); - if (s) { - s->set_gain_ctrl(s, 0); - s->set_exposure_ctrl(s, 0); - } - else { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "EnableAutoExposure: Failed to get control structure to set gain+exposure"); - } - - LEDOnOff(false); - LightOnOff(false); - isFixedExposure = true; - waitbeforepicture_org = flash_duration; -} - - esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay) { - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CaptureToBasisImage - Start"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("CaptureToBasisImage - Start"); +#endif - _Image->EmptyImage(); //Delete previous stored raw image -> black image - - LEDOnOff(true); + _Image->EmptyImage(); // Delete previous stored raw image -> black image - if (delay > 0) { - LightOnOff(true); + LEDOnOff(true); // Status-LED on + + if (delay > 0) + { + LightOnOff(true); // Flash-LED on const TickType_t xDelay = delay / portTICK_PERIOD_MS; - vTaskDelay( xDelay ); + vTaskDelay(xDelay); } - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CaptureToBasisImage - After LightOn"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("CaptureToBasisImage - After LightOn"); +#endif - camera_fb_t * fb = esp_camera_fb_get(); - esp_camera_fb_return(fb); + camera_fb_t *fb = esp_camera_fb_get(); + esp_camera_fb_return(fb); fb = esp_camera_fb_get(); - if (!fb) { - LEDOnOff(false); - LightOnOff(false); + + if (!fb) + { + LEDOnOff(false); // Status-LED off + LightOnOff(false); // Flash-LED off LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "is not working anymore (CaptureToBasisImage) - most probably caused " "by a hardware problem (instablility, ...). System will reboot."); @@ -328,493 +567,532 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay) return ESP_FAIL; } - if (demoMode) { // Use images stored on SD-Card instead of camera image + if (CCstatus.DemoMode) + { + // Use images stored on SD-Card instead of camera image /* Replace Framebuffer with image from SD-Card */ loadNextDemoImage(fb); } - CImageBasis* _zwImage = new CImageBasis("zwImage"); - if (_zwImage) { + CImageBasis *_zwImage = new CImageBasis("zwImage"); + + if (_zwImage) + { _zwImage->LoadFromMemory(fb->buf, fb->len); } - else { + else + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToBasisImage: Can't allocate _zwImage"); } - esp_camera_fb_return(fb); - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CaptureToBasisImage - After fb_get"); - #endif + esp_camera_fb_return(fb); - LEDOnOff(false); +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("CaptureToBasisImage - After fb_get"); +#endif - if (delay > 0) - LightOnOff(false); - -// TickType_t xDelay = 1000 / portTICK_PERIOD_MS; -// vTaskDelay( xDelay ); // wait for power to recover - - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CaptureToBasisImage - After LoadFromMemory"); - #endif + LEDOnOff(false); // Status-LED off - stbi_uc* p_target; - stbi_uc* p_source; + if (delay > 0) + { + LightOnOff(false); // Flash-LED off + } + + // TickType_t xDelay = 1000 / portTICK_PERIOD_MS; + // vTaskDelay( xDelay ); // wait for power to recover + +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("CaptureToBasisImage - After LoadFromMemory"); +#endif + + if (_zwImage == NULL) + { + return ESP_OK; + } + + stbi_uc *p_target; + stbi_uc *p_source; int channels = 3; - int width = image_width; - int height = image_height; + int width = CCstatus.ImageWidth; + int height = CCstatus.ImageHeight; - #ifdef DEBUG_DETAIL_ON - std::string _zw = "Targetimage: " + std::to_string((int) _Image->rgb_image) + " Size: " + std::to_string(_Image->width) + ", " + std::to_string(_Image->height); - _zw = _zw + " _zwImage: " + std::to_string((int) _zwImage->rgb_image) + " Size: " + std::to_string(_zwImage->width) + ", " + std::to_string(_zwImage->height); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, _zw); - #endif +#ifdef DEBUG_DETAIL_ON + std::string _zw = "Targetimage: " + std::to_string((int)_Image->rgb_image) + " Size: " + std::to_string(_Image->width) + ", " + std::to_string(_Image->height); + _zw = _zw + " _zwImage: " + std::to_string((int)_zwImage->rgb_image) + " Size: " + std::to_string(_zwImage->width) + ", " + std::to_string(_zwImage->height); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, _zw); +#endif for (int x = 0; x < width; ++x) + { for (int y = 0; y < height; ++y) { p_target = _Image->rgb_image + (channels * (y * width + x)); p_source = _zwImage->rgb_image + (channels * (y * width + x)); - p_target[0] = p_source[0]; - p_target[1] = p_source[1]; - p_target[2] = p_source[2]; + + for (int c = 0; c < channels; c++) + { + p_target[c] = p_source[c]; + } } + } delete _zwImage; - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CaptureToBasisImage - Done"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("CaptureToBasisImage - Done"); +#endif - return ESP_OK; + return ESP_OK; } - esp_err_t CCamera::CaptureToFile(std::string nm, int delay) { string ftype; - LEDOnOff(true); // Switched off to save power ! + LEDOnOff(true); // Status-LED on - if (delay > 0) { - LightOnOff(true); + if (delay > 0) + { + LightOnOff(true); // Flash-LED on const TickType_t xDelay = delay / portTICK_PERIOD_MS; - vTaskDelay( xDelay ); + vTaskDelay(xDelay); } - camera_fb_t * fb = esp_camera_fb_get(); + camera_fb_t *fb = esp_camera_fb_get(); esp_camera_fb_return(fb); fb = esp_camera_fb_get(); - if (!fb) { - LEDOnOff(false); - LightOnOff(false); + + if (!fb) + { + LEDOnOff(false); // Status-LED off + LightOnOff(false); // Flash-LED off LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Capture Failed. " "Check camera module and/or proper electrical connection"); - //doReboot(); + // doReboot(); return ESP_FAIL; } - LEDOnOff(false); - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "w %d, h %d, size %d", fb->width, fb->height, fb->len); - #endif + LEDOnOff(false); // Status-LED off + +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "w %d, h %d, size %d", fb->width, fb->height, fb->len); +#endif nm = FormatFileName(nm); - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Save Camera to: %s", nm.c_str()); - #endif +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "Save Camera to: %s", nm.c_str()); +#endif ftype = toUpper(getFileType(nm)); - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Filetype: %s", ftype.c_str()); - #endif +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "Filetype: %s", ftype.c_str()); +#endif - uint8_t * buf = NULL; - size_t buf_len = 0; - bool converted = false; + uint8_t *buf = NULL; + size_t buf_len = 0; + bool converted = false; if (ftype.compare("BMP") == 0) { frame2bmp(fb, &buf, &buf_len); converted = true; } + if (ftype.compare("JPG") == 0) { - if(fb->format != PIXFORMAT_JPEG){ - bool jpeg_converted = frame2jpg(fb, ActualQuality, &buf, &buf_len); + if (fb->format != PIXFORMAT_JPEG) + { + bool jpeg_converted = frame2jpg(fb, CCstatus.ImageQuality, &buf, &buf_len); converted = true; - if(!jpeg_converted){ + + if (!jpeg_converted) + { ESP_LOGE(TAG, "JPEG compression failed"); } - } else { + } + else + { buf_len = fb->len; buf = fb->buf; } } - FILE * fp = fopen(nm.c_str(), "wb"); - if (fp == NULL) { // If an error occurs during the file creation + FILE *fp = fopen(nm.c_str(), "wb"); + + if (fp == NULL) + { + // If an error occurs during the file creation LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Failed to open file " + nm); } - else { - fwrite(buf, sizeof(uint8_t), buf_len, fp); + else + { + fwrite(buf, sizeof(uint8_t), buf_len, fp); fclose(fp); - } + } if (converted) + { free(buf); + } esp_camera_fb_return(fb); - if (delay > 0) - LightOnOff(false); + if (delay > 0) + { + LightOnOff(false); // Flash-LED off + } - return ESP_OK; + return ESP_OK; } - esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay) { esp_err_t res = ESP_OK; size_t fb_len = 0; int64_t fr_start = esp_timer_get_time(); - LEDOnOff(true); + LEDOnOff(true); // Status-LED on - if (delay > 0) { - LightOnOff(true); + if (delay > 0) + { + LightOnOff(true); // Flash-LED on const TickType_t xDelay = delay / portTICK_PERIOD_MS; - vTaskDelay( xDelay ); + vTaskDelay(xDelay); } camera_fb_t *fb = esp_camera_fb_get(); esp_camera_fb_return(fb); fb = esp_camera_fb_get(); - if (!fb) { - LEDOnOff(false); - LightOnOff(false); + + if (!fb) + { + LEDOnOff(false); // Status-LED off + LightOnOff(false); // Flash-LED off LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Capture Failed. " - "Check camera module and/or proper electrical connection"); + "Check camera module and/or proper electrical connection"); httpd_resp_send_500(req); -// doReboot(); + // doReboot(); return ESP_FAIL; } - LEDOnOff(false); - + LEDOnOff(false); // Status-LED off res = httpd_resp_set_type(req, "image/jpeg"); - if(res == ESP_OK){ + + if (res == ESP_OK) + { res = httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=raw.jpg"); } - if(res == ESP_OK){ - if (demoMode) { // Use images stored on SD-Card instead of camera image + if (res == ESP_OK) + { + if (CCstatus.DemoMode) + { + // Use images stored on SD-Card instead of camera image LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Using Demo image!"); /* Replace Framebuffer with image from SD-Card */ loadNextDemoImage(fb); res = httpd_resp_send(req, (const char *)fb->buf, fb->len); } - else { - if(fb->format == PIXFORMAT_JPEG){ + else + { + if (fb->format == PIXFORMAT_JPEG) + { fb_len = fb->len; res = httpd_resp_send(req, (const char *)fb->buf, fb->len); - } else { + } + else + { jpg_chunking_t jchunk = {req, 0}; - res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk)?ESP_OK:ESP_FAIL; + res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk) ? ESP_OK : ESP_FAIL; httpd_resp_send_chunk(req, NULL, 0); fb_len = jchunk.len; } } } + esp_camera_fb_return(fb); int64_t fr_end = esp_timer_get_time(); - - ESP_LOGI(TAG, "JPG: %dKB %dms", (int)(fb_len/1024), (int)((fr_end - fr_start)/1000)); - if (delay > 0) - LightOnOff(false); + ESP_LOGI(TAG, "JPG: %dKB %dms", (int)(fb_len / 1024), (int)((fr_end - fr_start) / 1000)); + + if (delay > 0) + { + LightOnOff(false); // Flash-LED off + } return res; } - esp_err_t CCamera::CaptureToStream(httpd_req_t *req, bool FlashlightOn) { esp_err_t res = ESP_OK; size_t fb_len = 0; int64_t fr_start; - char * part_buf[64]; + char *part_buf[64]; + + // wenn die Kameraeinstellungen durch Erstellen eines neuen Referenzbildes verändert wurden, müssen sie neu gesetzt werden + if (CFstatus.changedCameraSettings) + { + Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera + Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize); + CFstatus.changedCameraSettings = false; + } LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Live stream started"); - if (FlashlightOn) { - LEDOnOff(true); - LightOnOff(true); + if (FlashlightOn) + { + LEDOnOff(true); // Status-LED on + LightOnOff(true); // Flash-LED on } - //httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); //stream is blocking web interface, only serving to local + // httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); //stream is blocking web interface, only serving to local httpd_resp_set_type(req, _STREAM_CONTENT_TYPE); httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY)); - while(1) + while (1) { fr_start = esp_timer_get_time(); camera_fb_t *fb = esp_camera_fb_get(); esp_camera_fb_return(fb); fb = esp_camera_fb_get(); - if (!fb) { + + if (!fb) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToStream: Camera framebuffer not available"); break; } + fb_len = fb->len; - - if (res == ESP_OK){ + + if (res == ESP_OK) + { size_t hlen = snprintf((char *)part_buf, sizeof(part_buf), _STREAM_PART, fb_len); res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen); } - if (res == ESP_OK){ + + if (res == ESP_OK) + { res = httpd_resp_send_chunk(req, (const char *)fb->buf, fb_len); } - if (res == ESP_OK){ + + if (res == ESP_OK) + { res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY)); } - + esp_camera_fb_return(fb); int64_t fr_end = esp_timer_get_time(); - ESP_LOGD(TAG, "JPG: %dKB %dms", (int)(fb_len/1024), (int)((fr_end - fr_start)/1000)); + ESP_LOGD(TAG, "JPG: %dKB %dms", (int)(fb_len / 1024), (int)((fr_end - fr_start) / 1000)); - if (res != ESP_OK){ // Exit loop, e.g. also when closing the webpage + if (res != ESP_OK) + { + // Exit loop, e.g. also when closing the webpage break; } int64_t fr_delta_ms = (fr_end - fr_start) / 1000; - if (CAM_LIVESTREAM_REFRESHRATE > fr_delta_ms) { - const TickType_t xDelay = (CAM_LIVESTREAM_REFRESHRATE - fr_delta_ms) / portTICK_PERIOD_MS; - ESP_LOGD(TAG, "Stream: sleep for: %ldms", (long) xDelay*10); - vTaskDelay(xDelay); + + if (CAM_LIVESTREAM_REFRESHRATE > fr_delta_ms) + { + const TickType_t xDelay = (CAM_LIVESTREAM_REFRESHRATE - fr_delta_ms) / portTICK_PERIOD_MS; + ESP_LOGD(TAG, "Stream: sleep for: %ldms", (long)xDelay * 10); + vTaskDelay(xDelay); } } - LEDOnOff(false); - LightOnOff(false); + LEDOnOff(false); // Status-LED off + LightOnOff(false); // Flash-LED off LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Live stream stopped"); return res; } - void CCamera::LightOnOff(bool status) { - GpioHandler* gpioHandler = gpio_handler_get(); - if ((gpioHandler != NULL) && (gpioHandler->isEnabled())) { + GpioHandler *gpioHandler = gpio_handler_get(); + + if ((gpioHandler != NULL) && (gpioHandler->isEnabled())) + { ESP_LOGD(TAG, "Use gpioHandler to trigger flashlight"); gpioHandler->flashLightEnable(status); - } - else { - #ifdef USE_PWM_LEDFLASH - if (status) { - ESP_LOGD(TAG, "Internal Flash-LED turn on with PWM %d", led_intensity); - ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, led_intensity)); + } + else + { +#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)); // Update duty to apply the new value ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL)); } - else { + else + { ESP_LOGD(TAG, "Internal Flash-LED turn off PWM"); ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, 0)); ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL)); } - #else +#else // Init the GPIO gpio_pad_select_gpio(FLASH_GPIO); - // Set the GPIO as a push/pull output - gpio_set_direction(FLASH_GPIO, GPIO_MODE_OUTPUT); - if (status) + // Set the GPIO as a push/pull output + gpio_set_direction(FLASH_GPIO, GPIO_MODE_OUTPUT); + + if (status) + { gpio_set_level(FLASH_GPIO, 1); + } else + { gpio_set_level(FLASH_GPIO, 0); - #endif + } +#endif } } - void CCamera::LEDOnOff(bool status) { - if (xHandle_task_StatusLED == NULL) { + if (xHandle_task_StatusLED == NULL) + { // Init the GPIO gpio_pad_select_gpio(BLINK_GPIO); + /* Set the GPIO as a push/pull output */ - gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT); + gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT); - if (!status) + if (!status) + { gpio_set_level(BLINK_GPIO, 1); + } else - gpio_set_level(BLINK_GPIO, 0); + { + gpio_set_level(BLINK_GPIO, 0); + } } } - -void CCamera::GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol) +void CCamera::SetImageWidthHeightFromResolution(framesize_t resol) { - char _query[100]; - char _qual[10]; - char _size[10]; - - resol = ActualResolution; - qual = ActualQuality; - - - if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK) + if (resol == FRAMESIZE_QVGA) { - ESP_LOGD(TAG, "Query: %s", _query); - if (httpd_query_key_value(_query, "size", _size, 10) == ESP_OK) - { - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Size: %s", _size); - #endif - if (strcmp(_size, "QVGA") == 0) - resol = FRAMESIZE_QVGA; // 320x240 - else if (strcmp(_size, "VGA") == 0) - resol = FRAMESIZE_VGA; // 640x480 - else if (strcmp(_size, "SVGA") == 0) - resol = FRAMESIZE_SVGA; // 800x600 - else if (strcmp(_size, "XGA") == 0) - resol = FRAMESIZE_XGA; // 1024x768 - else if (strcmp(_size, "SXGA") == 0) - resol = FRAMESIZE_SXGA; // 1280x1024 - else if (strcmp(_size, "UXGA") == 0) - resol = FRAMESIZE_UXGA; // 1600x1200 - } - if (httpd_query_key_value(_query, "quality", _qual, 10) == ESP_OK) - { - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Quality: %s", _qual); - #endif - qual = atoi(_qual); - - if (qual > 63) // Limit to max. 63 - qual = 63; - else if (qual < 8) // Limit to min. 8 - qual = 8; - } + CCstatus.ImageHeight = 240; + CCstatus.ImageWidth = 320; + } + else if (resol == FRAMESIZE_VGA) + { + CCstatus.ImageHeight = 480; + CCstatus.ImageWidth = 640; + } + else if (resol == FRAMESIZE_SVGA) + { + CCstatus.ImageHeight = 600; + CCstatus.ImageWidth = 800; + } + else if (resol == FRAMESIZE_XGA) + { + CCstatus.ImageHeight = 768; + CCstatus.ImageWidth = 1024; + } + else if (resol == FRAMESIZE_HD) + { + CCstatus.ImageHeight = 720; + CCstatus.ImageWidth = 1280; + } + else if (resol == FRAMESIZE_SXGA) + { + CCstatus.ImageHeight = 1024; + CCstatus.ImageWidth = 1280; + } + else if (resol == FRAMESIZE_UXGA) + { + CCstatus.ImageHeight = 1200; + CCstatus.ImageWidth = 1600; } } - -framesize_t CCamera::TextToFramesize(const char * _size) +framesize_t CCamera::TextToFramesize(const char *_size) { if (strcmp(_size, "QVGA") == 0) - return FRAMESIZE_QVGA; // 320x240 + { + return FRAMESIZE_QVGA; // 320x240 + } else if (strcmp(_size, "VGA") == 0) - return FRAMESIZE_VGA; // 640x480 + { + return FRAMESIZE_VGA; // 640x480 + } else if (strcmp(_size, "SVGA") == 0) - return FRAMESIZE_SVGA; // 800x600 + { + return FRAMESIZE_SVGA; // 800x600 + } else if (strcmp(_size, "XGA") == 0) - return FRAMESIZE_XGA; // 1024x768 + { + return FRAMESIZE_XGA; // 1024x768 + } else if (strcmp(_size, "SXGA") == 0) - return FRAMESIZE_SXGA; // 1280x1024 + { + return FRAMESIZE_SXGA; // 1280x1024 + } else if (strcmp(_size, "UXGA") == 0) - return FRAMESIZE_UXGA; // 1600x1200 - - return ActualResolution; -} - - -CCamera::CCamera() -{ - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "CreateClassCamera"); - #endif - brightness = 0; - contrast = 0; - saturation = 0; - isFixedExposure = false; - - ledc_init(); -} - - -esp_err_t CCamera::InitCam() -{ - ESP_LOGD(TAG, "Init Camera"); - ActualQuality = camera_config.jpeg_quality; - ActualResolution = camera_config.frame_size; - //initialize the camera - esp_camera_deinit(); // De-init in case it was already initialized - esp_err_t err = esp_camera_init(&camera_config); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Camera Init Failed"); - return err; + { + return FRAMESIZE_UXGA; // 1600x1200 } - CameraInitSuccessful = true; - return ESP_OK; + return CCstatus.ImageFrameSize; } - -void CCamera::SetLEDIntensity(float _intrel) -{ - _intrel = min(_intrel, (float) 100); - _intrel = max(_intrel, (float) 0); - _intrel = _intrel / 100; - led_intensity = (int) (_intrel * 8191); - ESP_LOGD(TAG, "Set led_intensity to %d of 8191", led_intensity); - -} - - -bool CCamera::getCameraInitSuccessful() -{ - return CameraInitSuccessful; -} - - std::vector demoFiles; -void CCamera::useDemoMode() +void CCamera::useDemoMode(void) { char line[50]; FILE *fd = fopen("/sdcard/demo/files.txt", "r"); - if (!fd) { + + if (!fd) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can not start Demo mode, the folder '/sdcard/demo/' does not contain the needed files!"); LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "See Details on https://jomjol.github.io/AI-on-the-edge-device-docs/Demo-Mode!"); return; } - demoImage = (uint8_t*)malloc(DEMO_IMAGE_SIZE); - if (demoImage == NULL) { + demoImage = (uint8_t *)malloc(DEMO_IMAGE_SIZE); + + if (demoImage == NULL) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Unable to acquire required memory for demo image!"); return; } - while (fgets(line, sizeof(line), fd) != NULL) { + while (fgets(line, sizeof(line), fd) != NULL) + { line[strlen(line) - 1] = '\0'; demoFiles.push_back(line); } - + fclose(fd); - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Using Demo mode (" + std::to_string(demoFiles.size()) + - " files) instead of real camera image!"); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Using Demo mode (" + std::to_string(demoFiles.size()) + " files) instead of real camera image!"); - for (auto file : demoFiles) { + for (auto file : demoFiles) + { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, file); } - demoMode = true; + CCstatus.DemoMode = true; } - -bool CCamera::loadNextDemoImage(camera_fb_t *fb) { +bool CCamera::loadNextDemoImage(camera_fb_t *fb) +{ char filename[50]; int readBytes; long fileSize; @@ -825,17 +1103,20 @@ bool CCamera::loadNextDemoImage(camera_fb_t *fb) { /* Inject saved image */ - FILE * fp = fopen(filename, "rb"); - if (!fp) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + std::string(filename) +"!"); + FILE *fp = fopen(filename, "rb"); + + if (!fp) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + std::string(filename) + "!"); return false; } fileSize = GetFileSize(filename); - if (fileSize > DEMO_IMAGE_SIZE) { + + if (fileSize > DEMO_IMAGE_SIZE) + { char buf[100]; - snprintf(buf, sizeof(buf), "Demo Image (%d bytes) is larger than provided buffer (%d bytes)!", - (int)fileSize, DEMO_IMAGE_SIZE); + snprintf(buf, sizeof(buf), "Demo Image (%d bytes) is larger than provided buffer (%d bytes)!", (int)fileSize, DEMO_IMAGE_SIZE); LogFile.WriteToFile(ESP_LOG_ERROR, TAG, std::string(buf)); return false; } @@ -851,7 +1132,6 @@ bool CCamera::loadNextDemoImage(camera_fb_t *fb) { return true; } - long CCamera::GetFileSize(std::string filename) { struct stat stat_buf; diff --git a/code/components/jomjol_controlcamera/ClassControllCamera.h b/code/components/jomjol_controlcamera/ClassControllCamera.h index cc2060f2..b674ead3 100644 --- a/code/components/jomjol_controlcamera/ClassControllCamera.h +++ b/code/components/jomjol_controlcamera/ClassControllCamera.h @@ -15,49 +15,100 @@ #include "CImageBasis.h" #include "../../include/defines.h" -class CCamera { - protected: - int ActualQuality; - framesize_t ActualResolution; - int brightness, contrast, saturation; - bool isFixedExposure; - int waitbeforepicture_org; - int led_intensity = 4095; +typedef enum +{ + OV2640_MODE_UXGA, + OV2640_MODE_SVGA, + OV2640_MODE_CIF +} ov2640_sensor_mode_t; - void ledc_init(void); - bool CameraInitSuccessful = false; - bool demoMode = false; +typedef struct +{ + framesize_t ImageFrameSize = FRAMESIZE_VGA; // 0 - 10 + gainceiling_t ImageGainceiling; // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128) - bool loadNextDemoImage(camera_fb_t *fb); - long GetFileSize(std::string filename); + int ImageQuality; // 0 - 63 + int ImageBrightness; // (-2 to 2) - set brightness + int ImageContrast; //-2 - 2 + int ImageSaturation; //-2 - 2 + int ImageSharpness; //-2 - 2 + bool ImageAutoSharpness; + int ImageSpecialEffect; // 0 - 6 + int ImageWbMode; // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home) + int ImageAwb; // white balance enable (0 or 1) + int ImageAwbGain; // Auto White Balance enable (0 or 1) + int ImageAec; // auto exposure off (1 or 0) + int ImageAec2; // automatic exposure sensor (0 or 1) + int ImageAeLevel; // auto exposure levels (-2 to 2) + int ImageAecValue; // set exposure manually (0-1200) + int ImageAgc; // auto gain off (1 or 0) + int ImageAgcGain; // set gain manually (0 - 30) + int ImageBpc; // black pixel correction + int ImageWpc; // white pixel correction + int ImageRawGma; // (1 or 0) + int ImageLenc; // lens correction (1 or 0) + int ImageHmirror; // (0 or 1) flip horizontally + int ImageVflip; // Invert image (0 or 1) + int ImageDcw; // downsize enable (1 or 0) - public: - int image_height, image_width; - - CCamera(); - esp_err_t InitCam(); + int ImageWidth; + int ImageHeight; - void LightOnOff(bool status); - void LEDOnOff(bool status); - esp_err_t CaptureToHTTP(httpd_req_t *req, int delay = 0); - esp_err_t CaptureToStream(httpd_req_t *req, bool FlashlightOn); - void SetQualitySize(int qual, framesize_t resol); - bool SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation); - void GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol); - void SetLEDIntensity(float _intrel); - bool testCamera(void); - void EnableAutoExposure(int flash_duration); - bool getCameraInitSuccessful(); - void useDemoMode(void); - + int ImageLedIntensity; - framesize_t TextToFramesize(const char * text); + bool ImageZoomEnabled; + int ImageZoomMode; + int ImageZoomOffsetX; + int ImageZoomOffsetY; + int ImageZoomSize; - esp_err_t CaptureToFile(std::string nm, int delay = 0); - esp_err_t CaptureToBasisImage(CImageBasis *_Image, int delay = 0); + int WaitBeforePicture; + bool isImageSize; + + bool CameraInitSuccessful; + bool changedCameraSettings; + bool DemoMode; + bool SaveAllFiles; +} camera_controll_config_temp_t; + +extern camera_controll_config_temp_t CCstatus; + +class CCamera +{ +protected: + void ledc_init(void); + bool loadNextDemoImage(camera_fb_t *fb); + long GetFileSize(std::string filename); + void SetCamWindow(sensor_t *s, int resolution, int xOffset, int yOffset, int xTotal, int yTotal, int xOutput, int yOutput); + void SetImageWidthHeightFromResolution(framesize_t resol); + +public: + CCamera(void); + esp_err_t InitCam(void); + + void LightOnOff(bool status); + void LEDOnOff(bool status); + + esp_err_t setSensorDatenFromCCstatus(void); + esp_err_t getSensorDatenToCCstatus(void); + + esp_err_t CaptureToHTTP(httpd_req_t *req, int delay = 0); + esp_err_t CaptureToStream(httpd_req_t *req, bool FlashlightOn); + + void SetQualityZoomSize(int qual, framesize_t resol, bool zoomEnabled, int zoomOffsetX, int zoomOffsetY, int imageSize); + void SetZoomSize(bool zoomEnabled, int zoomOffsetX, int zoomOffsetY, int imageSize); + void SetCamSharpness(bool _autoSharpnessEnabled, int _sharpnessLevel); + + void SetLEDIntensity(float _intrel); + bool testCamera(void); + bool getCameraInitSuccessful(void); + void useDemoMode(void); + + framesize_t TextToFramesize(const char *text); + + esp_err_t CaptureToFile(std::string nm, int delay = 0); + esp_err_t CaptureToBasisImage(CImageBasis *_Image, int delay = 0); }; - extern CCamera Camera; - #endif \ No newline at end of file diff --git a/code/components/jomjol_controlcamera/ov2640_sharpness.cpp b/code/components/jomjol_controlcamera/ov2640_sharpness.cpp new file mode 100644 index 00000000..8e67bc78 --- /dev/null +++ b/code/components/jomjol_controlcamera/ov2640_sharpness.cpp @@ -0,0 +1,152 @@ +#include +#include "esp_camera.h" +#include "ov2640_sharpness.h" + + +const static uint8_t OV2640_SHARPNESS_AUTO[]= +{ + //reg, val, mask + 0xFF, 0x00, 0xFF, + 0x92, 0x01, 0xFF, + 0x93, 0x20, 0x20, + 0x00, 0x00, 0x00 +}; + +const static uint8_t OV2640_SHARPNESS_MANUAL[]= +{ + //reg, val, mask + 0xFF, 0x00, 0xFF, + 0x92, 0x01, 0xFF, + 0x93, 0x00, 0x20, + 0x00, 0x00, 0x00 +}; + +const static uint8_t OV2640_SHARPNESS_LEVEL0[]= +{ + //reg, val, mask + 0xFF, 0x00, 0xFF, + 0x92, 0x01, 0xFF, + 0x93, 0xC0, 0x1F, + 0x00, 0x00, 0x00 +}; +const static uint8_t OV2640_SHARPNESS_LEVEL1[]= +{ + //reg, val, mask + 0xFF, 0x00, 0xFF, + 0x92, 0x01, 0xFF, + 0x93, 0xC1, 0x1F, + 0x00, 0x00, 0x00 +}; +const static uint8_t OV2640_SHARPNESS_LEVEL2[]= +{ + //reg, val, mask + 0xFF, 0x00, 0xFF, + 0x92, 0x01, 0xFF, + 0x93, 0xC2, 0x1F, + 0x00, 0x00, 0x00 +}; +const static uint8_t OV2640_SHARPNESS_LEVEL3[]= +{ + //reg, val, mask + 0xFF, 0x00, 0xFF, + 0x92, 0x01, 0xFF, + 0x93, 0xC4, 0x1F, + 0x00, 0x00, 0x00 +}; +const static uint8_t OV2640_SHARPNESS_LEVEL4[]= +{ + //reg, val, mask + 0xFF, 0x00, 0xFF, + 0x92, 0x01, 0xFF, + 0x93, 0xC8, 0x1F, + 0x00, 0x00, 0x00 +}; +const static uint8_t OV2640_SHARPNESS_LEVEL5[]= +{ + //reg, val, mask + 0xFF, 0x00, 0xFF, + 0x92, 0x01, 0xFF, + 0x93, 0xD0, 0x1F, + 0x00, 0x00, 0x00 +}; +const static uint8_t OV2640_SHARPNESS_LEVEL6[]= +{ + //reg, val, mask + 0xFF, 0x00, 0xFF, + 0x92, 0x01, 0xFF, + 0x93, 0xDF, 0x1F, + 0x00, 0x00, 0x00 +}; + +const static uint8_t *OV2640_SETTING_SHARPNESS[]= +{ + OV2640_SHARPNESS_LEVEL0, // -3 sharpness + OV2640_SHARPNESS_LEVEL1, + OV2640_SHARPNESS_LEVEL2, + OV2640_SHARPNESS_LEVEL3, + OV2640_SHARPNESS_LEVEL4, + OV2640_SHARPNESS_LEVEL5, + OV2640_SHARPNESS_LEVEL6 // +3 sharpness +}; + +#define OV2640_MAXLEVEL_SHARPNESS 6 + + +static int table_mask_write(sensor_t *sensor, const uint8_t* ptab) +{ + uint8_t address; + uint8_t value; + uint8_t orgval; + uint8_t mask; + const uint8_t *pdata = ptab; + + if (pdata == NULL) + { + return -1; + } + + while (1) + { + address = *pdata++; + value = *pdata++; + mask = *pdata++; + + if ((address == 0) && (value == 0) && (mask == 0)) + { + break; + } + + sensor->set_reg(sensor, address, mask, value); + } + + return 0; +} + + +int ov2640_enable_auto_sharpness(sensor_t *sensor) +{ + table_mask_write(sensor, OV2640_SHARPNESS_AUTO); + + return 0; +} + + +int ov2640_set_sharpness(sensor_t *sensor, int sharpness) +{ + int sharpness_temp = 0; + + if (sharpness < -3) + { + sharpness_temp = -3; + } + + if (sharpness > OV2640_MAXLEVEL_SHARPNESS - 3) + { + sharpness_temp = OV2640_MAXLEVEL_SHARPNESS - 3; + } + + table_mask_write(sensor, OV2640_SHARPNESS_MANUAL); + table_mask_write(sensor, OV2640_SETTING_SHARPNESS[sharpness_temp + 3]); + + return 0; +} diff --git a/code/components/jomjol_controlcamera/ov2640_sharpness.h b/code/components/jomjol_controlcamera/ov2640_sharpness.h new file mode 100644 index 00000000..19b157ce --- /dev/null +++ b/code/components/jomjol_controlcamera/ov2640_sharpness.h @@ -0,0 +1,11 @@ +#pragma once + +#ifndef OV2640_SHARPNESS_H +#define OV2640_SHARPNESS_H + +#include "esp_camera.h" + +int ov2640_enable_auto_sharpness(sensor_t *sensor); +int ov2640_set_sharpness(sensor_t *sensor, int sharpness); // -3 to +3, -4 for auto-sharpness + +#endif diff --git a/code/components/jomjol_controlcamera/server_camera.cpp b/code/components/jomjol_controlcamera/server_camera.cpp index 8656d077..ed6d0d12 100644 --- a/code/components/jomjol_controlcamera/server_camera.cpp +++ b/code/components/jomjol_controlcamera/server_camera.cpp @@ -5,6 +5,7 @@ #include "esp_camera.h" #include "ClassControllCamera.h" +#include "MainFlowControl.h" #include "ClassLogFile.h" #include "esp_log.h" @@ -13,183 +14,183 @@ static const char *TAG = "server_cam"; +void PowerResetCamera() +{ + ESP_LOGD(TAG, "Resetting camera by power down line"); + gpio_config_t conf; + conf.intr_type = GPIO_INTR_DISABLE; + conf.pin_bit_mask = 1LL << GPIO_NUM_32; + conf.mode = GPIO_MODE_OUTPUT; + conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + conf.pull_up_en = GPIO_PULLUP_DISABLE; + gpio_config(&conf); -void PowerResetCamera(){ - - ESP_LOGD(TAG, "Resetting camera by power down line"); - gpio_config_t conf; - conf.intr_type = GPIO_INTR_DISABLE; - conf.pin_bit_mask = 1LL << GPIO_NUM_32; - conf.mode = GPIO_MODE_OUTPUT; - conf.pull_down_en = GPIO_PULLDOWN_DISABLE; - conf.pull_up_en = GPIO_PULLUP_DISABLE; - gpio_config(&conf); - - // carefull, logic is inverted compared to reset pin - gpio_set_level(GPIO_NUM_32, 1); - vTaskDelay(1000 / portTICK_PERIOD_MS); - gpio_set_level(GPIO_NUM_32, 0); - vTaskDelay(1000 / portTICK_PERIOD_MS); + // carefull, logic is inverted compared to reset pin + gpio_set_level(GPIO_NUM_32, 1); + vTaskDelay(1000 / portTICK_PERIOD_MS); + gpio_set_level(GPIO_NUM_32, 0); + vTaskDelay(1000 / portTICK_PERIOD_MS); } - esp_err_t handler_lightOn(httpd_req_t *req) { - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_lightOn - Start"); - ESP_LOGD(TAG, "handler_lightOn uri: %s", req->uri); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_lightOn - Start"); + ESP_LOGD(TAG, "handler_lightOn uri: %s", req->uri); +#endif - if (Camera.getCameraInitSuccessful()) + if (Camera.getCameraInitSuccessful()) { Camera.LightOnOff(true); - const char* resp_str = (const char*) req->user_ctx; + const char *resp_str = (const char *)req->user_ctx; httpd_resp_send(req, resp_str, strlen(resp_str)); } - else + else { httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /lighton not available!"); return ESP_ERR_NOT_FOUND; } - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_lightOn - Done"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_lightOn - Done"); +#endif return ESP_OK; } - esp_err_t handler_lightOff(httpd_req_t *req) { - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_lightOff - Start"); - ESP_LOGD(TAG, "handler_lightOff uri: %s", req->uri); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_lightOff - Start"); + ESP_LOGD(TAG, "handler_lightOff uri: %s", req->uri); +#endif - if (Camera.getCameraInitSuccessful()) + if (Camera.getCameraInitSuccessful()) { Camera.LightOnOff(false); - const char* resp_str = (const char*) req->user_ctx; - httpd_resp_send(req, resp_str, strlen(resp_str)); + const char *resp_str = (const char *)req->user_ctx; + httpd_resp_send(req, resp_str, strlen(resp_str)); } - else + else { httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /lightoff not available!"); return ESP_ERR_NOT_FOUND; } - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_lightOff - Done"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_lightOff - Done"); +#endif return ESP_OK; } - esp_err_t handler_capture(httpd_req_t *req) { - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_capture - Start"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_capture - Start"); +#endif - if (Camera.getCameraInitSuccessful()) + if (Camera.getCameraInitSuccessful()) { - int quality; - framesize_t res; +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality); +#endif - Camera.GetCameraParameter(req, quality, res); - - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality); - #endif - - Camera.SetQualitySize(quality, res); + // wenn die Kameraeinstellungen durch Erstellen eines neuen Referenzbildes verändert wurden, müssen sie neu gesetzt werden + if (CFstatus.changedCameraSettings) + { + Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera + Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize); + CFstatus.changedCameraSettings = false; + } esp_err_t result; result = Camera.CaptureToHTTP(req); - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_capture - Done"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_capture - Done"); +#endif return result; } - else + else { httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /capture not available!"); return ESP_ERR_NOT_FOUND; } } - esp_err_t handler_capture_with_light(httpd_req_t *req) { - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_capture_with_light - Start"); - #endif - - if (Camera.getCameraInitSuccessful()) +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_capture_with_light - Start"); +#endif + + if (Camera.getCameraInitSuccessful()) { char _query[100]; char _delay[10]; - - int quality; - framesize_t res; int delay = 2500; if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK) { ESP_LOGD(TAG, "Query: %s", _query); + if (httpd_query_key_value(_query, "delay", _delay, 10) == ESP_OK) { - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Delay: %s", _delay); - #endif +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "Delay: %s", _delay); +#endif delay = atoi(_delay); if (delay < 0) + { delay = 0; + } } } - Camera.GetCameraParameter(req, quality, res); +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality); +#endif - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality); - #endif + // wenn die Kameraeinstellungen durch Erstellen eines neuen Referenzbildes verändert wurden, müssen sie neu gesetzt werden + if (CFstatus.changedCameraSettings) + { + Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera + Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize); + CFstatus.changedCameraSettings = false; + } - Camera.SetQualitySize(quality, res); Camera.LightOnOff(true); const TickType_t xDelay = delay / portTICK_PERIOD_MS; - vTaskDelay( xDelay ); + vTaskDelay(xDelay); esp_err_t result; - result = Camera.CaptureToHTTP(req); + result = Camera.CaptureToHTTP(req); Camera.LightOnOff(false); - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_capture_with_light - Done"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_capture_with_light - Done"); +#endif return result; } - else + else { httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /capture_with_flashlight not available!"); return ESP_ERR_NOT_FOUND; } } - esp_err_t handler_capture_save_to_file(httpd_req_t *req) { - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_capture_save_to_file - Start"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_capture_save_to_file - Start"); +#endif - if (Camera.getCameraInitSuccessful()) + if (Camera.getCameraInitSuccessful()) { char _query[100]; char _delay[10]; @@ -197,94 +198,102 @@ esp_err_t handler_capture_save_to_file(httpd_req_t *req) char filename[100]; std::string fn = "/sdcard/"; - - int quality; - framesize_t res; - if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK) { ESP_LOGD(TAG, "Query: %s", _query); + if (httpd_query_key_value(_query, "filename", filename, 100) == ESP_OK) { fn.append(filename); - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Filename: %s", fn.c_str()); - #endif +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "Filename: %s", fn.c_str()); +#endif } else + { fn.append("noname.jpg"); + } if (httpd_query_key_value(_query, "delay", _delay, 10) == ESP_OK) { - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Delay: %s", _delay); - #endif +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "Delay: %s", _delay); +#endif delay = atoi(_delay); if (delay < 0) + { delay = 0; + } } } else + { fn.append("noname.jpg"); + } - Camera.GetCameraParameter(req, quality, res); - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality); - #endif - Camera.SetQualitySize(quality, res); +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "Size: %d, Quality: %d", res, quality); +#endif + + // wenn die Kameraeinstellungen durch Erstellen eines neuen Referenzbildes verändert wurden, müssen sie neu gesetzt werden + if (CFstatus.changedCameraSettings) + { + Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera + Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize); + CFstatus.changedCameraSettings = false; + } esp_err_t result; - result = Camera.CaptureToFile(fn, delay); + result = Camera.CaptureToFile(fn, delay); - const char* resp_str = (const char*) fn.c_str(); + const char *resp_str = (const char *)fn.c_str(); httpd_resp_send(req, resp_str, strlen(resp_str)); - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_capture_save_to_file - Done"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_capture_save_to_file - Done"); +#endif return result; } - else + else { httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /save not available!"); return ESP_ERR_NOT_FOUND; } } - void register_server_camera_uri(httpd_handle_t server) { -#ifdef DEBUG_DETAIL_ON +#ifdef DEBUG_DETAIL_ON ESP_LOGI(TAG, "server_part_camera - Registering URI handlers"); #endif - httpd_uri_t camuri = { }; - camuri.method = HTTP_GET; + httpd_uri_t camuri = {}; + camuri.method = HTTP_GET; - camuri.uri = "/lighton"; - camuri.handler = handler_lightOn; - camuri.user_ctx = (void*) "Light On"; + camuri.uri = "/lighton"; + camuri.handler = handler_lightOn; + camuri.user_ctx = (void *)"Light On"; httpd_register_uri_handler(server, &camuri); - camuri.uri = "/lightoff"; - camuri.handler = handler_lightOff; - camuri.user_ctx = (void*) "Light Off"; - httpd_register_uri_handler(server, &camuri); + camuri.uri = "/lightoff"; + camuri.handler = handler_lightOff; + camuri.user_ctx = (void *)"Light Off"; + httpd_register_uri_handler(server, &camuri); - camuri.uri = "/capture"; - camuri.handler = handler_capture; - camuri.user_ctx = NULL; - httpd_register_uri_handler(server, &camuri); + camuri.uri = "/capture"; + camuri.handler = handler_capture; + camuri.user_ctx = NULL; + httpd_register_uri_handler(server, &camuri); - camuri.uri = "/capture_with_flashlight"; - camuri.handler = handler_capture_with_light; - camuri.user_ctx = NULL; - httpd_register_uri_handler(server, &camuri); + camuri.uri = "/capture_with_flashlight"; + camuri.handler = handler_capture_with_light; + camuri.user_ctx = NULL; + httpd_register_uri_handler(server, &camuri); - camuri.uri = "/save"; - camuri.handler = handler_capture_save_to_file; - camuri.user_ctx = NULL; - httpd_register_uri_handler(server, &camuri); + camuri.uri = "/save"; + camuri.handler = handler_capture_save_to_file; + camuri.user_ctx = NULL; + httpd_register_uri_handler(server, &camuri); } diff --git a/code/components/jomjol_controlcamera/server_camera.h b/code/components/jomjol_controlcamera/server_camera.h index 7bd79b89..656b99eb 100644 --- a/code/components/jomjol_controlcamera/server_camera.h +++ b/code/components/jomjol_controlcamera/server_camera.h @@ -10,7 +10,6 @@ //#include "ClassControllCamera.h" void register_server_camera_uri(httpd_handle_t server); - void PowerResetCamera(); #endif \ No newline at end of file diff --git a/code/components/jomjol_fileserver_ota/miniz/miniz.h b/code/components/jomjol_fileserver_ota/miniz/miniz.h index 35c740c7..278898d1 100644 --- a/code/components/jomjol_fileserver_ota/miniz/miniz.h +++ b/code/components/jomjol_fileserver_ota/miniz/miniz.h @@ -153,7 +153,7 @@ /*#define MINIZ_NO_MALLOC */ #ifdef MINIZ_NO_INFLATE_APIS -#define MINIZ_NO_ARCHIVE_APIS +//#define MINIZ_NO_ARCHIVE_APIS #endif #ifdef MINIZ_NO_DEFLATE_APIS diff --git a/code/components/jomjol_fileserver_ota/server_file.cpp b/code/components/jomjol_fileserver_ota/server_file.cpp index 8da89873..50e2c8f2 100644 --- a/code/components/jomjol_fileserver_ota/server_file.cpp +++ b/code/components/jomjol_fileserver_ota/server_file.cpp @@ -547,7 +547,7 @@ static esp_err_t download_get_handler(httpd_req_t *req) /* Get value of expected key from query string */ if (httpd_query_key_value(buf, "readonly", param, sizeof(param)) == ESP_OK) { ESP_LOGI(TAG, "Found URL query parameter => readonly=%s", param); - readonly = param && strcmp(param,"true")==0; + readonly = (strcmp(param,"true") == 0); } } } diff --git a/code/components/jomjol_flowcontroll/ClassFlowAlignment.cpp b/code/components/jomjol_flowcontroll/ClassFlowAlignment.cpp index f7ff40d2..c3ab6967 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowAlignment.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowAlignment.cpp @@ -1,393 +1,402 @@ -#include "ClassFlowAlignment.h" -#include "ClassFlowTakeImage.h" -#include "ClassFlow.h" -#include "MainFlowControl.h" - -#include "CRotateImage.h" -#include "esp_log.h" - - -#include "ClassLogFile.h" -#include "psram.h" -#include "../../include/defines.h" - - -static const char *TAG = "ALIGN"; - -// #define DEBUG_DETAIL_ON - - -void ClassFlowAlignment::SetInitialParameter(void) -{ - initalrotate = 0; - anz_ref = 0; - initialmirror = false; - use_antialiasing = false; - initialflip = false; - SaveAllFiles = false; - namerawimage = "/sdcard/img_tmp/raw.jpg"; - FileStoreRefAlignment = "/sdcard/config/align.txt"; - ListFlowControll = NULL; - AlignAndCutImage = NULL; - ImageBasis = NULL; - ImageTMP = NULL; - #ifdef ALGROI_LOAD_FROM_MEM_AS_JPG - AlgROI = (ImageData*)malloc_psram_heap(std::string(TAG) + "->AlgROI", sizeof(ImageData), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); - #endif - previousElement = NULL; - disabled = false; - SAD_criteria = 0.05; -} - - -ClassFlowAlignment::ClassFlowAlignment(std::vector* lfc) -{ - SetInitialParameter(); - ListFlowControll = lfc; - - for (int i = 0; i < ListFlowControll->size(); ++i) - { - if (((*ListFlowControll)[i])->name().compare("ClassFlowTakeImage") == 0) - { - ImageBasis = ((ClassFlowTakeImage*) (*ListFlowControll)[i])->rawImage; - } - } - - if (!ImageBasis) // the function take pictures does not exist --> must be created first ONLY FOR TEST PURPOSES - { - ESP_LOGD(TAG, "CImageBasis had to be created"); - ImageBasis = new CImageBasis("ImageBasis", namerawimage); - } -} - - -bool ClassFlowAlignment::ReadParameter(FILE* pfile, string& aktparamgraph) -{ - std::vector splitted; - int suchex = 40; - int suchey = 40; - int alg_algo = 0; //default=0; 1 =HIGHACCURACY; 2= FAST; 3= OFF //add disable aligment algo |01.2023 - - - aktparamgraph = trim(aktparamgraph); - - if (aktparamgraph.size() == 0) - if (!this->GetNextParagraph(pfile, aktparamgraph)) - return false; - - if (aktparamgraph.compare("[Alignment]") != 0) //Paragraph does not fit Alignment - return false; - - while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph)) - { - splitted = ZerlegeZeile(aktparamgraph); - if ((toUpper(splitted[0]) == "FLIPIMAGESIZE") && (splitted.size() > 1)) - { - if (toUpper(splitted[1]) == "TRUE") - initialflip = true; - } - if ((toUpper(splitted[0]) == "INITIALMIRROR") && (splitted.size() > 1)) - { - if (toUpper(splitted[1]) == "TRUE") - initialmirror = true; - } - if (((toUpper(splitted[0]) == "INITALROTATE") || (toUpper(splitted[0]) == "INITIALROTATE")) && (splitted.size() > 1)) - { - this->initalrotate = std::stod(splitted[1]); - } - if ((toUpper(splitted[0]) == "SEARCHFIELDX") && (splitted.size() > 1)) - { - suchex = std::stod(splitted[1]); - } - if ((toUpper(splitted[0]) == "SEARCHFIELDY") && (splitted.size() > 1)) - { - suchey = std::stod(splitted[1]); - } - if ((toUpper(splitted[0]) == "ANTIALIASING") && (splitted.size() > 1)) - { - if (toUpper(splitted[1]) == "TRUE") - use_antialiasing = true; - } - if ((splitted.size() == 3) && (anz_ref < 2)) - { - References[anz_ref].image_file = FormatFileName("/sdcard" + splitted[0]); - References[anz_ref].target_x = std::stod(splitted[1]); - References[anz_ref].target_y = std::stod(splitted[2]); - anz_ref++; - } - - if ((toUpper(splitted[0]) == "SAVEALLFILES") && (splitted.size() > 1)) - { - if (toUpper(splitted[1]) == "TRUE") - SaveAllFiles = true; - } - if ((toUpper(splitted[0]) == "ALIGNMENTALGO") && (splitted.size() > 1)) - { - #ifdef DEBUG_DETAIL_ON - std::string zw2 = "Alignment mode selected: " + splitted[1]; - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2); - #endif - if (toUpper(splitted[1]) == "HIGHACCURACY") - alg_algo = 1; - if (toUpper(splitted[1]) == "FAST") - alg_algo = 2; - if (toUpper(splitted[1]) == "OFF") //no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023 - alg_algo = 3; - } - } - - for (int i = 0; i < anz_ref; ++i) - { - References[i].search_x = suchex; - References[i].search_y = suchey; - References[i].fastalg_SAD_criteria = SAD_criteria; - References[i].alignment_algo = alg_algo; - #ifdef DEBUG_DETAIL_ON - std::string zw2 = "Alignment mode written: " + std::to_string(alg_algo); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2); - #endif - } - - //no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023 - if(References[0].alignment_algo != 3){ - LoadReferenceAlignmentValues(); - } - - return true; - -} - - -string ClassFlowAlignment::getHTMLSingleStep(string host) -{ - string result; - - result = "

Rotated Image:

\n"; - result = result + "

Found Alignment:

\n"; - result = result + "

Aligned Image:

\n"; - return result; -} - - -bool ClassFlowAlignment::doFlow(string time) -{ - #ifdef ALGROI_LOAD_FROM_MEM_AS_JPG - if (!AlgROI) // AlgROI needs to be allocated before ImageTMP to avoid heap fragmentation - { - AlgROI = (ImageData*)heap_caps_realloc(AlgROI, sizeof(ImageData), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); - if (!AlgROI) - { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate AlgROI"); - LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow"); - } - } - - if (AlgROI) - { - ImageBasis->writeToMemoryAsJPG((ImageData*)AlgROI, 90); - } - #endif - - if (!ImageTMP) - { - ImageTMP = new CImageBasis("tmpImage", ImageBasis); // Make sure the name does not get change, it is relevant for the PSRAM allocation! - if (!ImageTMP) - { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate tmpImage -> Exec this round aborted!"); - LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow"); - return false; - } - } - - delete AlignAndCutImage; - AlignAndCutImage = new CAlignAndCutImage("AlignAndCutImage", ImageBasis, ImageTMP); - if (!AlignAndCutImage) - { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate AlignAndCutImage -> Exec this round aborted!"); - LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow"); - return false; - } - - CRotateImage rt("rawImage", AlignAndCutImage, ImageTMP, initialflip); - if (initialflip) - { - int _zw = ImageBasis->height; - ImageBasis->height = ImageBasis->width; - ImageBasis->width = _zw; - - _zw = ImageTMP->width; - ImageTMP->width = ImageTMP->height; - ImageTMP->height = _zw; - } - - if (initialmirror) - { - ESP_LOGD(TAG, "do mirror"); - rt.Mirror(); - - if (SaveAllFiles) - AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/mirror.jpg")); - } - - if ((initalrotate != 0) || initialflip) - { - if (use_antialiasing) - rt.RotateAntiAliasing(initalrotate); - else - rt.Rotate(initalrotate); - - if (SaveAllFiles) - AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/rot.jpg")); - } - - - //no align algo if set to 3 = off //add disable aligment algo |01.2023 - if(References[0].alignment_algo != 3){ - if (!AlignAndCutImage->Align(&References[0], &References[1])) - { - SaveReferenceAlignmentValues(); - } - }// no align - - - #ifdef ALGROI_LOAD_FROM_MEM_AS_JPG - if (AlgROI) { - //no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023 - if(References[0].alignment_algo != 3){ - DrawRef(ImageTMP); - } - flowctrl.DigitalDrawROI(ImageTMP); - flowctrl.AnalogDrawROI(ImageTMP); - ImageTMP->writeToMemoryAsJPG((ImageData*)AlgROI, 90); - } - #endif - - if (SaveAllFiles) - { - AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/alg.jpg")); - ImageTMP->SaveToFile(FormatFileName("/sdcard/img_tmp/alg_roi.jpg")); - } - - // must be deleted to have memory space for loading tflite - delete ImageTMP; - ImageTMP = NULL; - - //no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023 - if(References[0].alignment_algo != 3){ - LoadReferenceAlignmentValues(); - } - - return true; -} - - -void ClassFlowAlignment::SaveReferenceAlignmentValues() -{ - FILE* pFile; - std::string zwtime, zwvalue; - - pFile = fopen(FileStoreRefAlignment.c_str(), "w"); - - if (strlen(zwtime.c_str()) == 0) - { - time_t rawtime; - struct tm* timeinfo; - char buffer[80]; - - time(&rawtime); - timeinfo = localtime(&rawtime); - - strftime(buffer, 80, "%Y-%m-%dT%H:%M:%S", timeinfo); - zwtime = std::string(buffer); - } - - fputs(zwtime.c_str(), pFile); - fputs("\n", pFile); - - zwvalue = std::to_string(References[0].fastalg_x) + "\t" + std::to_string(References[0].fastalg_y); - zwvalue = zwvalue + "\t" +std::to_string(References[0].fastalg_SAD)+ "\t" +std::to_string(References[0].fastalg_min); - zwvalue = zwvalue + "\t" +std::to_string(References[0].fastalg_max)+ "\t" +std::to_string(References[0].fastalg_avg); - fputs(zwvalue.c_str(), pFile); - fputs("\n", pFile); - - zwvalue = std::to_string(References[1].fastalg_x) + "\t" + std::to_string(References[1].fastalg_y); - zwvalue = zwvalue + "\t" +std::to_string(References[1].fastalg_SAD)+ "\t" +std::to_string(References[1].fastalg_min); - zwvalue = zwvalue + "\t" +std::to_string(References[1].fastalg_max)+ "\t" +std::to_string(References[1].fastalg_avg); - fputs(zwvalue.c_str(), pFile); - fputs("\n", pFile); - - fclose(pFile); -} - - -bool ClassFlowAlignment::LoadReferenceAlignmentValues(void) -{ - FILE* pFile; - char zw[1024]; - string zwvalue; - std::vector splitted; - - - pFile = fopen(FileStoreRefAlignment.c_str(), "r"); - if (pFile == NULL) - return false; - - fgets(zw, 1024, pFile); - ESP_LOGD(TAG, "%s", zw); - - fgets(zw, 1024, pFile); - splitted = ZerlegeZeile(std::string(zw), " \t"); - if (splitted.size() < 6) - { - fclose(pFile); - return false; - } - - References[0].fastalg_x = stoi(splitted[0]); - References[0].fastalg_y = stoi(splitted[1]); - References[0].fastalg_SAD = stof(splitted[2]); - References[0].fastalg_min = stoi(splitted[3]); - References[0].fastalg_max = stoi(splitted[4]); - References[0].fastalg_avg = stof(splitted[5]); - - fgets(zw, 1024, pFile); - splitted = ZerlegeZeile(std::string(zw)); - if (splitted.size() < 6) - { - fclose(pFile); - return false; - } - - References[1].fastalg_x = stoi(splitted[0]); - References[1].fastalg_y = stoi(splitted[1]); - References[1].fastalg_SAD = stof(splitted[2]); - References[1].fastalg_min = stoi(splitted[3]); - References[1].fastalg_max = stoi(splitted[4]); - References[1].fastalg_avg = stof(splitted[5]); - - fclose(pFile); - - - /*#ifdef DEBUG_DETAIL_ON - std::string _zw = "\tLoadReferences[0]\tx,y:\t" + std::to_string(References[0].fastalg_x) + "\t" + std::to_string(References[0].fastalg_x); - _zw = _zw + "\tSAD, min, max, avg:\t" + std::to_string(References[0].fastalg_SAD) + "\t" + std::to_string(References[0].fastalg_min); - _zw = _zw + "\t" + std::to_string(References[0].fastalg_max) + "\t" + std::to_string(References[0].fastalg_avg); - LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", _zw); - _zw = "\tLoadReferences[1]\tx,y:\t" + std::to_string(References[1].fastalg_x) + "\t" + std::to_string(References[1].fastalg_x); - _zw = _zw + "\tSAD, min, max, avg:\t" + std::to_string(References[1].fastalg_SAD) + "\t" + std::to_string(References[1].fastalg_min); - _zw = _zw + "\t" + std::to_string(References[1].fastalg_max) + "\t" + std::to_string(References[1].fastalg_avg); - LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", _zw); - #endif*/ - - return true; -} - - -void ClassFlowAlignment::DrawRef(CImageBasis *_zw) -{ - if (_zw->ImageOkay()) - { - _zw->drawRect(References[0].target_x, References[0].target_y, References[0].width, References[0].height, 255, 0, 0, 2); - _zw->drawRect(References[1].target_x, References[1].target_y, References[1].width, References[1].height, 255, 0, 0, 2); - } -} +#include "ClassFlowAlignment.h" +#include "ClassFlowTakeImage.h" +#include "ClassFlow.h" +#include "MainFlowControl.h" + +#include "CRotateImage.h" +#include "esp_log.h" + +#include "ClassLogFile.h" +#include "psram.h" +#include "../../include/defines.h" + +static const char *TAG = "ALIGN"; + +// #define DEBUG_DETAIL_ON + +void ClassFlowAlignment::SetInitialParameter(void) +{ + initialrotate = 0; + anz_ref = 0; + use_antialiasing = false; + initialflip = false; + SaveAllFiles = false; + namerawimage = "/sdcard/img_tmp/raw.jpg"; + FileStoreRefAlignment = "/sdcard/config/align.txt"; + ListFlowControll = NULL; + AlignAndCutImage = NULL; + ImageBasis = NULL; + ImageTMP = NULL; +#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG + AlgROI = (ImageData *)malloc_psram_heap(std::string(TAG) + "->AlgROI", sizeof(ImageData), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); +#endif + previousElement = NULL; + disabled = false; + SAD_criteria = 0.05; +} + +ClassFlowAlignment::ClassFlowAlignment(std::vector *lfc) +{ + SetInitialParameter(); + ListFlowControll = lfc; + + for (int i = 0; i < ListFlowControll->size(); ++i) + { + if (((*ListFlowControll)[i])->name().compare("ClassFlowTakeImage") == 0) + { + ImageBasis = ((ClassFlowTakeImage *)(*ListFlowControll)[i])->rawImage; + } + } + + if (!ImageBasis) // the function take pictures does not exist --> must be created first ONLY FOR TEST PURPOSES + { + ESP_LOGD(TAG, "CImageBasis had to be created"); + ImageBasis = new CImageBasis("ImageBasis", namerawimage); + } +} + +bool ClassFlowAlignment::ReadParameter(FILE *pfile, string &aktparamgraph) +{ + std::vector splitted; + int suchex = 40; + int suchey = 40; + int alg_algo = 0; // default=0; 1 =HIGHACCURACY; 2= FAST; 3= OFF //add disable aligment algo |01.2023 + + aktparamgraph = trim(aktparamgraph); + + if (aktparamgraph.size() == 0) + { + if (!this->GetNextParagraph(pfile, aktparamgraph)) + { + return false; + } + } + + if (aktparamgraph.compare("[Alignment]") != 0) + { + // Paragraph does not fit Alignment + return false; + } + + while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph)) + { + splitted = ZerlegeZeile(aktparamgraph); + + if ((toUpper(splitted[0]) == "FLIPIMAGESIZE") && (splitted.size() > 1)) + { + if (toUpper(splitted[1]) == "TRUE") + { + initialflip = true; + } + } + else if (((toUpper(splitted[0]) == "initialrotate") || (toUpper(splitted[0]) == "INITIALROTATE")) && (splitted.size() > 1)) + { + this->initialrotate = std::stod(splitted[1]); + } + else if ((toUpper(splitted[0]) == "SEARCHFIELDX") && (splitted.size() > 1)) + { + suchex = std::stod(splitted[1]); + } + else if ((toUpper(splitted[0]) == "SEARCHFIELDY") && (splitted.size() > 1)) + { + suchey = std::stod(splitted[1]); + } + else if ((toUpper(splitted[0]) == "ANTIALIASING") && (splitted.size() > 1)) + { + if (toUpper(splitted[1]) == "TRUE") + { + use_antialiasing = true; + } + } + else if ((splitted.size() == 3) && (anz_ref < 2)) + { + References[anz_ref].image_file = FormatFileName("/sdcard" + splitted[0]); + References[anz_ref].target_x = std::stod(splitted[1]); + References[anz_ref].target_y = std::stod(splitted[2]); + anz_ref++; + } + + else if ((toUpper(splitted[0]) == "SAVEALLFILES") && (splitted.size() > 1)) + { + if (toUpper(splitted[1]) == "TRUE") + { + SaveAllFiles = true; + } + } + else if ((toUpper(splitted[0]) == "ALIGNMENTALGO") && (splitted.size() > 1)) + { +#ifdef DEBUG_DETAIL_ON + std::string zw2 = "Alignment mode selected: " + splitted[1]; + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2); +#endif + if (toUpper(splitted[1]) == "HIGHACCURACY") + { + alg_algo = 1; + } + if (toUpper(splitted[1]) == "FAST") + { + alg_algo = 2; + } + if (toUpper(splitted[1]) == "OFF") + { + // no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023 + alg_algo = 3; + } + } + } + + for (int i = 0; i < anz_ref; ++i) + { + References[i].search_x = suchex; + References[i].search_y = suchey; + References[i].fastalg_SAD_criteria = SAD_criteria; + References[i].alignment_algo = alg_algo; +#ifdef DEBUG_DETAIL_ON + std::string zw2 = "Alignment mode written: " + std::to_string(alg_algo); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2); +#endif + } + + // no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023 + if (References[0].alignment_algo != 3) + { + LoadReferenceAlignmentValues(); + } + + return true; +} + +string ClassFlowAlignment::getHTMLSingleStep(string host) +{ + string result; + + result = "

Rotated Image:

\n"; + result = result + "

Found Alignment:

\n"; + result = result + "

Aligned Image:

\n"; + return result; +} + +bool ClassFlowAlignment::doFlow(string time) +{ +#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG + if (!AlgROI) // AlgROI needs to be allocated before ImageTMP to avoid heap fragmentation + { + AlgROI = (ImageData *)heap_caps_realloc(AlgROI, sizeof(ImageData), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); + + if (!AlgROI) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate AlgROI"); + LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow"); + } + } + + if (AlgROI) + { + ImageBasis->writeToMemoryAsJPG((ImageData *)AlgROI, 90); + } +#endif + + if (!ImageTMP) + { + ImageTMP = new CImageBasis("tmpImage", ImageBasis); // Make sure the name does not get change, it is relevant for the PSRAM allocation! + + if (!ImageTMP) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate tmpImage -> Exec this round aborted!"); + LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow"); + return false; + } + } + + delete AlignAndCutImage; + AlignAndCutImage = new CAlignAndCutImage("AlignAndCutImage", ImageBasis, ImageTMP); + + if (!AlignAndCutImage) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate AlignAndCutImage -> Exec this round aborted!"); + LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow"); + return false; + } + + CRotateImage rt("rawImage", AlignAndCutImage, ImageTMP, initialflip); + + if (initialflip) + { + int _zw = ImageBasis->height; + ImageBasis->height = ImageBasis->width; + ImageBasis->width = _zw; + + _zw = ImageTMP->width; + ImageTMP->width = ImageTMP->height; + ImageTMP->height = _zw; + } + + if ((initialrotate != 0) || initialflip) + { + if (use_antialiasing) + { + rt.RotateAntiAliasing(initialrotate); + } + else + { + rt.Rotate(initialrotate); + } + + if (SaveAllFiles) + { + AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/rot.jpg")); + } + } + + // no align algo if set to 3 = off //add disable aligment algo |01.2023 + if (References[0].alignment_algo != 3) + { + if (!AlignAndCutImage->Align(&References[0], &References[1])) + { + SaveReferenceAlignmentValues(); + } + } // no align + +#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG + if (AlgROI) + { + // no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023 + if (References[0].alignment_algo != 3) + { + DrawRef(ImageTMP); + } + + flowctrl.DigitalDrawROI(ImageTMP); + flowctrl.AnalogDrawROI(ImageTMP); + ImageTMP->writeToMemoryAsJPG((ImageData *)AlgROI, 90); + } +#endif + + if (SaveAllFiles) + { + AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/alg.jpg")); + ImageTMP->SaveToFile(FormatFileName("/sdcard/img_tmp/alg_roi.jpg")); + } + + // must be deleted to have memory space for loading tflite + delete ImageTMP; + ImageTMP = NULL; + + // no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023 + if (References[0].alignment_algo != 3) + { + LoadReferenceAlignmentValues(); + } + + return true; +} + +void ClassFlowAlignment::SaveReferenceAlignmentValues() +{ + FILE *pFile; + std::string zwtime, zwvalue; + + pFile = fopen(FileStoreRefAlignment.c_str(), "w"); + + if (strlen(zwtime.c_str()) == 0) + { + time_t rawtime; + struct tm *timeinfo; + char buffer[80]; + + time(&rawtime); + timeinfo = localtime(&rawtime); + + strftime(buffer, 80, "%Y-%m-%dT%H:%M:%S", timeinfo); + zwtime = std::string(buffer); + } + + fputs(zwtime.c_str(), pFile); + fputs("\n", pFile); + + zwvalue = std::to_string(References[0].fastalg_x) + "\t" + std::to_string(References[0].fastalg_y); + zwvalue = zwvalue + "\t" + std::to_string(References[0].fastalg_SAD) + "\t" + std::to_string(References[0].fastalg_min); + zwvalue = zwvalue + "\t" + std::to_string(References[0].fastalg_max) + "\t" + std::to_string(References[0].fastalg_avg); + fputs(zwvalue.c_str(), pFile); + fputs("\n", pFile); + + zwvalue = std::to_string(References[1].fastalg_x) + "\t" + std::to_string(References[1].fastalg_y); + zwvalue = zwvalue + "\t" + std::to_string(References[1].fastalg_SAD) + "\t" + std::to_string(References[1].fastalg_min); + zwvalue = zwvalue + "\t" + std::to_string(References[1].fastalg_max) + "\t" + std::to_string(References[1].fastalg_avg); + fputs(zwvalue.c_str(), pFile); + fputs("\n", pFile); + + fclose(pFile); +} + +bool ClassFlowAlignment::LoadReferenceAlignmentValues(void) +{ + FILE *pFile; + char zw[1024]; + string zwvalue; + std::vector splitted; + + pFile = fopen(FileStoreRefAlignment.c_str(), "r"); + + if (pFile == NULL) + return false; + + fgets(zw, 1024, pFile); + ESP_LOGD(TAG, "%s", zw); + + fgets(zw, 1024, pFile); + splitted = ZerlegeZeile(std::string(zw), " \t"); + + if (splitted.size() < 6) + { + fclose(pFile); + return false; + } + + References[0].fastalg_x = stoi(splitted[0]); + References[0].fastalg_y = stoi(splitted[1]); + References[0].fastalg_SAD = stof(splitted[2]); + References[0].fastalg_min = stoi(splitted[3]); + References[0].fastalg_max = stoi(splitted[4]); + References[0].fastalg_avg = stof(splitted[5]); + + fgets(zw, 1024, pFile); + splitted = ZerlegeZeile(std::string(zw)); + + if (splitted.size() < 6) + { + fclose(pFile); + return false; + } + + References[1].fastalg_x = stoi(splitted[0]); + References[1].fastalg_y = stoi(splitted[1]); + References[1].fastalg_SAD = stof(splitted[2]); + References[1].fastalg_min = stoi(splitted[3]); + References[1].fastalg_max = stoi(splitted[4]); + References[1].fastalg_avg = stof(splitted[5]); + + fclose(pFile); + + /*#ifdef DEBUG_DETAIL_ON + std::string _zw = "\tLoadReferences[0]\tx,y:\t" + std::to_string(References[0].fastalg_x) + "\t" + std::to_string(References[0].fastalg_x); + _zw = _zw + "\tSAD, min, max, avg:\t" + std::to_string(References[0].fastalg_SAD) + "\t" + std::to_string(References[0].fastalg_min); + _zw = _zw + "\t" + std::to_string(References[0].fastalg_max) + "\t" + std::to_string(References[0].fastalg_avg); + LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", _zw); + _zw = "\tLoadReferences[1]\tx,y:\t" + std::to_string(References[1].fastalg_x) + "\t" + std::to_string(References[1].fastalg_x); + _zw = _zw + "\tSAD, min, max, avg:\t" + std::to_string(References[1].fastalg_SAD) + "\t" + std::to_string(References[1].fastalg_min); + _zw = _zw + "\t" + std::to_string(References[1].fastalg_max) + "\t" + std::to_string(References[1].fastalg_avg); + LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", _zw); + #endif*/ + + return true; +} + +void ClassFlowAlignment::DrawRef(CImageBasis *_zw) +{ + if (_zw->ImageOkay()) + { + _zw->drawRect(References[0].target_x, References[0].target_y, References[0].width, References[0].height, 255, 0, 0, 2); + _zw->drawRect(References[1].target_x, References[1].target_y, References[1].width, References[1].height, 255, 0, 0, 2); + } +} diff --git a/code/components/jomjol_flowcontroll/ClassFlowAlignment.h b/code/components/jomjol_flowcontroll/ClassFlowAlignment.h index 6b7b7914..2d0944cb 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowAlignment.h +++ b/code/components/jomjol_flowcontroll/ClassFlowAlignment.h @@ -1,54 +1,51 @@ -#pragma once - -#ifndef CLASSFLOWALIGNMENT_H -#define CLASSFLOWALIGNMENT_H - -#include "ClassFlow.h" -#include "Helper.h" -#include "CAlignAndCutImage.h" -#include "CFindTemplate.h" - -#include - -using namespace std; - -class ClassFlowAlignment : - public ClassFlow -{ -protected: - float initalrotate; - bool initialmirror; - bool initialflip; - bool use_antialiasing; - RefInfo References[2]; - int anz_ref; - string namerawimage; - bool SaveAllFiles; - CAlignAndCutImage *AlignAndCutImage; - std::string FileStoreRefAlignment; - float SAD_criteria; - - void SetInitialParameter(void); - bool LoadReferenceAlignmentValues(void); - void SaveReferenceAlignmentValues(); - -public: - CImageBasis *ImageBasis, *ImageTMP; - #ifdef ALGROI_LOAD_FROM_MEM_AS_JPG - ImageData *AlgROI; - #endif - - ClassFlowAlignment(std::vector* lfc); - - CAlignAndCutImage* GetAlignAndCutImage(){return AlignAndCutImage;}; - - void DrawRef(CImageBasis *_zw); - - bool ReadParameter(FILE* pfile, string& aktparamgraph); - bool doFlow(string time); - string getHTMLSingleStep(string host); - string name(){return "ClassFlowAlignment";}; -}; - - -#endif //CLASSFLOWALIGNMENT_H +#pragma once + +#ifndef CLASSFLOWALIGNMENT_H +#define CLASSFLOWALIGNMENT_H + +#include "ClassFlow.h" +#include "Helper.h" +#include "CAlignAndCutImage.h" +#include "CFindTemplate.h" + +#include + +using namespace std; + +class ClassFlowAlignment : public ClassFlow +{ +protected: + float initialrotate; + bool initialflip; + bool use_antialiasing; + RefInfo References[2]; + int anz_ref; + string namerawimage; + bool SaveAllFiles; + CAlignAndCutImage *AlignAndCutImage; + std::string FileStoreRefAlignment; + float SAD_criteria; + + void SetInitialParameter(void); + bool LoadReferenceAlignmentValues(void); + void SaveReferenceAlignmentValues(); + +public: + CImageBasis *ImageBasis, *ImageTMP; +#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG + ImageData *AlgROI; +#endif + + ClassFlowAlignment(std::vector *lfc); + + CAlignAndCutImage *GetAlignAndCutImage() { return AlignAndCutImage; }; + + void DrawRef(CImageBasis *_zw); + + bool ReadParameter(FILE *pfile, string &aktparamgraph); + bool doFlow(string time); + string getHTMLSingleStep(string host); + string name() { return "ClassFlowAlignment"; }; +}; + +#endif // CLASSFLOWALIGNMENT_H diff --git a/code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp b/code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp index ae980e47..7dc59a90 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp @@ -20,8 +20,7 @@ static const char* TAG = "CNN"; #endif -ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype) : ClassFlowImage(NULL, TAG) -{ +ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype) : ClassFlowImage(NULL, TAG) { string cnnmodelfile = ""; modelxsize = 1; modelysize = 1; @@ -38,16 +37,16 @@ ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNTy } -string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution, int prev, float _before_narrow_Analog, float analogDigitalTransitionStart) -{ +string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution, int prev, float _before_narrow_Analog, float analogDigitalTransitionStart) { string result = ""; - if (GENERAL[_analog]->ROI.size() == 0) + if (GENERAL[_analog]->ROI.size() == 0) { return result; + } + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout _analog=" + std::to_string(_analog) + ", _extendedResolution=" + std::to_string(_extendedResolution) + ", prev=" + std::to_string(prev)); - if (CNNType == Analogue || CNNType == Analogue100) - { + if (CNNType == Analogue || CNNType == Analogue100) { float number = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float; int result_after_decimal_point = ((int) floor(number * 10) + 10) % 10; @@ -55,37 +54,35 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution // LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(analog) number=" + std::to_string(number) + ", result_after_decimal_point=" + std::to_string(result_after_decimal_point) + ", prev=" + std::to_string(prev)); result = std::to_string(prev); - if (_extendedResolution) + if (_extendedResolution) { result = result + std::to_string(result_after_decimal_point); + } - for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i) - { + for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i) { prev = PointerEvalAnalogNew(GENERAL[_analog]->ROI[i]->result_float, prev); result = std::to_string(prev) + result; } return result; } - if (CNNType == Digital) - { - for (int i = 0; i < GENERAL[_analog]->ROI.size(); ++i) - { - if (GENERAL[_analog]->ROI[i]->result_klasse >= 10) + if (CNNType == Digital) { + for (int i = 0; i < GENERAL[_analog]->ROI.size(); ++i) { + if (GENERAL[_analog]->ROI[i]->result_klasse >= 10) { result = result + "N"; - else + } + else { result = result + std::to_string(GENERAL[_analog]->ROI[i]->result_klasse); + } } return result; } - if ((CNNType == DoubleHyprid10) || (CNNType == Digital100)) - { - + if ((CNNType == DoubleHyprid10) || (CNNType == Digital100)) { float number = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float; - if (number >= 0) // NaN? - { - if (_extendedResolution) // is only set if it is the first digit (no analogue before!) - { + // NaN? + if (number >= 0) { + // is only set if it is the first digit (no analogue before!) + if (_extendedResolution) { int result_after_decimal_point = ((int) floor(number * 10)) % 10; int result_before_decimal_point = ((int) floor(number)) % 10; @@ -93,36 +90,32 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution prev = result_before_decimal_point; LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(dig100-ext) result_before_decimal_point=" + std::to_string(result_before_decimal_point) + ", result_after_decimal_point=" + std::to_string(result_after_decimal_point) + ", prev=" + std::to_string(prev)); } - else - { - if (_before_narrow_Analog >= 0) + else { + if (_before_narrow_Analog >= 0) { prev = PointerEvalHybridNew(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, _before_narrow_Analog, prev, true, analogDigitalTransitionStart); - else + } + 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)); - } } - else - { + else { result = "N"; - if (_extendedResolution && (CNNType != Digital)) + if (_extendedResolution && (CNNType != Digital)) { result = "NN"; + } } - for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i) - { - if (GENERAL[_analog]->ROI[i]->result_float >= 0) - { + for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i) { + if (GENERAL[_analog]->ROI[i]->result_float >= 0) { 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; LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout#result= " + result); - } - else - { + else { 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)); @@ -134,15 +127,28 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution return result; } - -int ClassFlowCNNGeneral::PointerEvalHybridNew(float number, float number_of_predecessors, int eval_predecessors, bool Analog_Predecessors, float digitalAnalogTransitionStart) -{ +/** + * @brief Determines the number of an ROI in connection with previous ROI results + * + * @param number: is the current ROI as float value from recognition + * @param number_of_predecessors: is the last (lower) ROI as float from recognition + * @param eval_predecessors: is the evaluated number. Sometimes a much lower value can change higer values + * example: 9.8, 9.9, 0.1 + * 0.1 => 0 (eval_predecessors) + * The 0 makes a 9.9 to 0 (eval_predecessors) + * The 0 makes a 9.8 to 0 + * @param Analog_Predecessors false/true if the last ROI is an analog or digital ROI (default=false) + * runs in special handling because analog is much less precise + * @param digitalAnalogTransitionStart start of the transitionlogic begins on number_of_predecessor (default=9.2) + * + * @return int the determined number of the current ROI + */ +int ClassFlowCNNGeneral::PointerEvalHybridNew(float number, float number_of_predecessors, int eval_predecessors, bool Analog_Predecessors, float digitalAnalogTransitionStart) { int result; int result_after_decimal_point = ((int) floor(number * 10)) % 10; int result_before_decimal_point = ((int) floor(number) + 10) % 10; - if (eval_predecessors < 0) - { + if (eval_predecessors < 0) { // 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 @@ -153,8 +159,7 @@ int ClassFlowCNNGeneral::PointerEvalHybridNew(float number, float number_of_pred return result; } - if (Analog_Predecessors) - { + if (Analog_Predecessors) { result = PointerEvalAnalogToDigitNew(number, number_of_predecessors, eval_predecessors, digitalAnalogTransitionStart); LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - Analog predecessor, evaluation over PointerEvalAnalogNew = " + std::to_string(result) + " number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors)+ " eval_predecessors = " + std::to_string(eval_predecessors) + " Digital_Uncertainty = " + std::to_string(Digital_Uncertainty)); @@ -164,26 +169,31 @@ int ClassFlowCNNGeneral::PointerEvalHybridNew(float number, float number_of_pred if ((number_of_predecessors > Digital_Transition_Area_Predecessor ) && (number_of_predecessors < (10.0 - Digital_Transition_Area_Predecessor))) { // no digit change, because predecessor is far enough away (0+/-DigitalTransitionRangePredecessor) --> number is rounded - if ((result_after_decimal_point <= DigitalBand) || (result_after_decimal_point >= (10-DigitalBand))) // Band around the digit --> Round off, as digit reaches inaccuracy in the frame + // Band around the digit --> Round off, as digit reaches inaccuracy in the frame + if ((result_after_decimal_point <= DigitalBand) || (result_after_decimal_point >= (10-DigitalBand))) { result = ((int) round(number) + 10) % 10; - else + } + else { result = ((int) trunc(number) + 10) % 10; + } LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - NO analogue predecessor, no change of digits, as pre-decimal point far enough away = " + std::to_string(result) + " number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors)+ " eval_predecessors = " + std::to_string(eval_predecessors) + " Digital_Uncertainty = " + std::to_string(Digital_Uncertainty)); return result; } - if (eval_predecessors <= 1) // Zero crossing at the predecessor has taken place (! evaluation via Prev_value and not number!) --> round up here (2.8 --> 3, but also 3.1 --> 3) - { + // Zero crossing at the predecessor has taken place (! evaluation via Prev_value and not number!) --> round up here (2.8 --> 3, but also 3.1 --> 3) + if (eval_predecessors <= 1) { // We simply assume that the current digit after the zero crossing of the predecessor // has passed through at least half (x.5) - if (result_after_decimal_point > 5) + if (result_after_decimal_point > 5) { // The current digit does not yet have a zero crossing, but the predecessor does.. result = (result_before_decimal_point + 1) % 10; - else + } + else { // Act. digit and predecessor have zero crossing result = result_before_decimal_point % 10; + } LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - NO analogue predecessor, zero crossing has taken placen = " + std::to_string(result) + " number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors)+ " eval_predecessors = " + std::to_string(eval_predecessors) + " Digital_Uncertainty = " + std::to_string(Digital_Uncertainty)); return result; @@ -199,10 +209,12 @@ int ClassFlowCNNGeneral::PointerEvalHybridNew(float number, float number_of_pred || result_after_decimal_point >= 4) // The current digit, like the previous digit, does not yet have a zero crossing. result = result_before_decimal_point % 10; - else + } + else { // current digit precedes the smaller digit (9.x). So already >=x.0 while the previous digit has not yet // has no zero crossing. Therefore, it is reduced by 1. result = (result_before_decimal_point - 1 + 10) % 10; + } LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - O analogue predecessor, >= 9.5 --> no zero crossing yet = " + std::to_string(result) + " number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors)+ " eval_predecessors = " + std::to_string(eval_predecessors) + " Digital_Uncertainty = " + std::to_string(Digital_Uncertainty) + " result_after_decimal_point = " + std::to_string(result_after_decimal_point)); @@ -210,8 +222,7 @@ int ClassFlowCNNGeneral::PointerEvalHybridNew(float number, float number_of_pred } -int ClassFlowCNNGeneral::PointerEvalAnalogToDigitNew(float number, float numeral_preceder, int eval_predecessors, float analogDigitalTransitionStart) -{ +int ClassFlowCNNGeneral::PointerEvalAnalogToDigitNew(float number, float numeral_preceder, int eval_predecessors, float analogDigitalTransitionStart) { int result; int result_after_decimal_point = ((int) floor(number * 10)) % 10; int result_before_decimal_point = ((int) floor(number) + 10) % 10; @@ -253,21 +264,17 @@ int ClassFlowCNNGeneral::PointerEvalAnalogToDigitNew(float number, float numeral " number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder) + " eerg after comma = " + std::to_string(result_after_decimal_point)); - } return result; - } -int ClassFlowCNNGeneral::PointerEvalAnalogNew(float number, int numeral_preceder) -{ +int ClassFlowCNNGeneral::PointerEvalAnalogNew(float number, int numeral_preceder) { float number_min, number_max; int result; - if (numeral_preceder == -1) - { + if (numeral_preceder == -1) { result = (int) floor(number); LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogNew - No predecessor - Result = " + std::to_string(result) + " number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder) + " Analog_error = " + std::to_string(Analog_error)); @@ -277,17 +284,14 @@ int ClassFlowCNNGeneral::PointerEvalAnalogNew(float number, int numeral_preceder number_min = number - Analog_error / 10.0; number_max = number + Analog_error / 10.0; - if ((int) floor(number_max) - (int) floor(number_min) != 0) - { - if (numeral_preceder <= Analog_error) - { + if ((int) floor(number_max) - (int) floor(number_min) != 0) { + if (numeral_preceder <= Analog_error) { result = ((int) floor(number_max) + 10) % 10; LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogNew - number ambiguous, correction upwards - result = " + std::to_string(result) + " number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder) + " Analog_error = " + std::to_string(Analog_error)); return result; } - if (numeral_preceder >= 10 - Analog_error) - { + if (numeral_preceder >= 10 - Analog_error) { result = ((int) floor(number_min) + 10) % 10; LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogNew - number ambiguous, downward correction - result = " + std::to_string(result) + " number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder) + " Analog_error = " + std::to_string(Analog_error)); @@ -295,7 +299,6 @@ int ClassFlowCNNGeneral::PointerEvalAnalogNew(float number, int numeral_preceder } } - result = ((int) floor(number) + 10) % 10; LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogNew - number unambiguous, no correction necessary - result = " + std::to_string(result) + " number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder) + " Analog_error = " + std::to_string(Analog_error)); @@ -304,25 +307,25 @@ int ClassFlowCNNGeneral::PointerEvalAnalogNew(float number, int numeral_preceder } -bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph) -{ +bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph) { std::vector splitted; aktparamgraph = trim(aktparamgraph); - if (aktparamgraph.size() == 0) - if (!this->GetNextParagraph(pfile, aktparamgraph)) + if (aktparamgraph.size() == 0) { + if (!this->GetNextParagraph(pfile, aktparamgraph)) { return false; - + } + } if ((toUpper(aktparamgraph) != "[ANALOG]") && (toUpper(aktparamgraph) != ";[ANALOG]") && (toUpper(aktparamgraph) != "[DIGIT]") && (toUpper(aktparamgraph) != ";[DIGIT]") - && (toUpper(aktparamgraph) != "[DIGITS]") && (toUpper(aktparamgraph) != ";[DIGITS]") - ) // Paragraph passt nicht + && (toUpper(aktparamgraph) != "[DIGITS]") && (toUpper(aktparamgraph) != ";[DIGITS]")) { + // Paragraph passt nicht return false; + } - if (aktparamgraph[0] == ';') - { + if (aktparamgraph[0] == ';') { disabled = true; while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph)); ESP_LOGD(TAG, "[Analog/Digit] is disabled!"); @@ -330,36 +333,31 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph) } - while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph)) - { + while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph)) { splitted = ZerlegeZeile(aktparamgraph); - if ((toUpper(splitted[0]) == "ROIIMAGESLOCATION") && (splitted.size() > 1)) - { + if ((toUpper(splitted[0]) == "ROIIMAGESLOCATION") && (splitted.size() > 1)) { this->imagesLocation = "/sdcard" + splitted[1]; this->isLogImage = true; } - if ((toUpper(splitted[0]) == "LOGIMAGESELECT") && (splitted.size() > 1)) - { + + if ((toUpper(splitted[0]) == "LOGIMAGESELECT") && (splitted.size() > 1)) { LogImageSelect = splitted[1]; isLogImageSelect = true; } - if ((toUpper(splitted[0]) == "ROIIMAGESRETENTION") && (splitted.size() > 1)) - { + if ((toUpper(splitted[0]) == "ROIIMAGESRETENTION") && (splitted.size() > 1)) { this->imagesRetention = std::stoi(splitted[1]); } - if ((toUpper(splitted[0]) == "MODEL") && (splitted.size() > 1)) - { + if ((toUpper(splitted[0]) == "MODEL") && (splitted.size() > 1)) { this->cnnmodelfile = splitted[1]; } - if ((toUpper(splitted[0]) == "CNNGOODTHRESHOLD") && (splitted.size() > 1)) - { + if ((toUpper(splitted[0]) == "CNNGOODTHRESHOLD") && (splitted.size() > 1)) { CNNGoodThreshold = std::stof(splitted[1]); } - if (splitted.size() >= 5) - { + + if (splitted.size() >= 5) { general* _analog = GetGENERAL(splitted[0], true); roi* neuroi = _analog->ROI[_analog->ROI.size()-1]; neuroi->posx = std::stoi(splitted[1]); @@ -367,19 +365,20 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph) neuroi->deltax = std::stoi(splitted[3]); neuroi->deltay = std::stoi(splitted[4]); neuroi->CCW = false; - if (splitted.size() >= 6) - { + + if (splitted.size() >= 6) { neuroi->CCW = toUpper(splitted[5]) == "TRUE"; } + neuroi->result_float = -1; neuroi->image = NULL; neuroi->image_org = NULL; } - if ((toUpper(splitted[0]) == "SAVEALLFILES") && (splitted.size() > 1)) - { - if (toUpper(splitted[1]) == "TRUE") + if ((toUpper(splitted[0]) == "SAVEALLFILES") && (splitted.size() > 1)) { + if (toUpper(splitted[1]) == "TRUE") { SaveAllFiles = true; + } } } @@ -390,55 +389,57 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph) } - for (int _ana = 0; _ana < GENERAL.size(); ++_ana) - for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i) - { + for (int _ana = 0; _ana < GENERAL.size(); ++_ana) { + for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i) { GENERAL[_ana]->ROI[i]->image = new CImageBasis("ROI " + GENERAL[_ana]->ROI[i]->name, modelxsize, modelysize, modelchannel); GENERAL[_ana]->ROI[i]->image_org = new CImageBasis("ROI " + GENERAL[_ana]->ROI[i]->name + " original", GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, 3); } + } return true; } -general* ClassFlowCNNGeneral::FindGENERAL(string _name_number) -{ - for (int i = 0; i < GENERAL.size(); ++i) - if (GENERAL[i]->name == _name_number) +general* ClassFlowCNNGeneral::FindGENERAL(string _name_number) { + for (int i = 0; i < GENERAL.size(); ++i) { + if (GENERAL[i]->name == _name_number) { return GENERAL[i]; + } + } + return NULL; } -general* ClassFlowCNNGeneral::GetGENERAL(string _name, bool _create = true) -{ +general* ClassFlowCNNGeneral::GetGENERAL(string _name, bool _create = true) { string _analog, _roi; int _pospunkt = _name.find_first_of("."); - if (_pospunkt > -1) - { + if (_pospunkt > -1) { _analog = _name.substr(0, _pospunkt); _roi = _name.substr(_pospunkt+1, _name.length() - _pospunkt - 1); } - else - { + else { _analog = "default"; _roi = _name; } general *_ret = NULL; - for (int i = 0; i < GENERAL.size(); ++i) - if (GENERAL[i]->name == _analog) + for (int i = 0; i < GENERAL.size(); ++i) { + if (GENERAL[i]->name == _analog) { _ret = GENERAL[i]; + } + } - if (!_create) // not found and should not be created + // not found and should not be created + if (!_create) { return _ret; + } - if (_ret == NULL) - { + if (_ret == NULL) { _ret = new general; _ret->name = _analog; GENERAL.push_back(_ret); @@ -455,8 +456,7 @@ general* ClassFlowCNNGeneral::GetGENERAL(string _name, bool _create = true) } -string ClassFlowCNNGeneral::getHTMLSingleStep(string host) -{ +string ClassFlowCNNGeneral::getHTMLSingleStep(string host) { string result, zw; std::vector htmlinfo; @@ -464,8 +464,8 @@ string ClassFlowCNNGeneral::getHTMLSingleStep(string host) result = result + "Analog Pointers:

"; htmlinfo = GetHTMLInfo(); - for (int i = 0; i < htmlinfo.size(); ++i) - { + + for (int i = 0; i < htmlinfo.size(); ++i) { std::stringstream stream; stream << std::fixed << std::setprecision(1) << htmlinfo[i]->val; zw = stream.str(); @@ -473,15 +473,14 @@ string ClassFlowCNNGeneral::getHTMLSingleStep(string host) result = result + "filename + "\"> " + zw; delete htmlinfo[i]; } + htmlinfo.clear(); return result; } -bool ClassFlowCNNGeneral::doFlow(string time) -{ - +bool ClassFlowCNNGeneral::doFlow(string time) { #ifdef HEAP_TRACING_CLASS_FLOW_CNN_GENERAL_DO_ALING_AND_CUT //register a buffer to record the memory trace ESP_ERROR_CHECK( heap_trace_init_standalone(trace_record, NUM_RECORDS) ); @@ -489,8 +488,9 @@ bool ClassFlowCNNGeneral::doFlow(string time) ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) ); #endif - if (disabled) + if (disabled) { return true; + } if (!doAlignAndCut(time)){ return false; @@ -511,79 +511,80 @@ bool ClassFlowCNNGeneral::doFlow(string time) } -bool ClassFlowCNNGeneral::doAlignAndCut(string time) -{ - if (disabled) +bool ClassFlowCNNGeneral::doAlignAndCut(string time) { + if (disabled) { return true; + } CAlignAndCutImage *caic = flowpostalignment->GetAlignAndCutImage(); - for (int _ana = 0; _ana < GENERAL.size(); ++_ana) - for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i) - { + for (int _ana = 0; _ana < GENERAL.size(); ++_ana) { + for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i) { ESP_LOGD(TAG, "General %d - Align&Cut", i); caic->CutAndSave(GENERAL[_ana]->ROI[i]->posx, GENERAL[_ana]->ROI[i]->posy, GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, GENERAL[_ana]->ROI[i]->image_org); - if (SaveAllFiles) - { - if (GENERAL[_ana]->name == "default") + if (SaveAllFiles) { + if (GENERAL[_ana]->name == "default") { GENERAL[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".jpg")); - else + } + else { GENERAL[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg")); + } } GENERAL[_ana]->ROI[i]->image_org->Resize(modelxsize, modelysize, GENERAL[_ana]->ROI[i]->image); - if (SaveAllFiles) - { - if (GENERAL[_ana]->name == "default") + if (SaveAllFiles) { + if (GENERAL[_ana]->name == "default") { GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".jpg")); - else + } + else { GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg")); + } } } + } return true; } -void ClassFlowCNNGeneral::DrawROI(CImageBasis *_zw) -{ - if (_zw->ImageOkay()) - { - if (CNNType == Analogue || CNNType == Analogue100) - { +void ClassFlowCNNGeneral::DrawROI(CImageBasis *_zw) { + if (_zw->ImageOkay()) { + if (CNNType == Analogue || CNNType == Analogue100) { int r = 0; int g = 255; int b = 0; - for (int _ana = 0; _ana < GENERAL.size(); ++_ana) - for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i) - { + for (int _ana = 0; _ana < GENERAL.size(); ++_ana) { + for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i) { _zw->drawRect(GENERAL[_ana]->ROI[i]->posx, GENERAL[_ana]->ROI[i]->posy, GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, r, g, b, 1); _zw->drawEllipse( (int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) (GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->deltay/2), r, g, b, 2); _zw->drawLine((int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) GENERAL[_ana]->ROI[i]->posy, (int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay), r, g, b, 2); _zw->drawLine((int) GENERAL[_ana]->ROI[i]->posx, (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax, (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), r, g, b, 2); } + } } - else - { - for (int _dig = 0; _dig < GENERAL.size(); ++_dig) - for (int i = 0; i < GENERAL[_dig]->ROI.size(); ++i) + else { + for (int _dig = 0; _dig < GENERAL.size(); ++_dig) { + for (int i = 0; i < GENERAL[_dig]->ROI.size(); ++i) { _zw->drawRect(GENERAL[_dig]->ROI[i]->posx, GENERAL[_dig]->ROI[i]->posy, GENERAL[_dig]->ROI[i]->deltax, GENERAL[_dig]->ROI[i]->deltay, 0, 0, (255 - _dig*100), 2); + } + } } } } -bool ClassFlowCNNGeneral::getNetworkParameter() -{ - if (disabled) +bool ClassFlowCNNGeneral::getNetworkParameter() { + if (disabled) { return true; + } CTfLiteClass *tflite = new CTfLiteClass; string zwcnn = "/sdcard" + cnnmodelfile; zwcnn = FormatFileName(zwcnn); ESP_LOGD(TAG, "%s", zwcnn.c_str()); + if (!tflite->LoadModel(zwcnn)) { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't load tflite model " + cnnmodelfile + " -> Init aborted!"); LogFile.WriteHeapInfo("getNetworkParameter-LoadModel"); @@ -598,16 +599,14 @@ bool ClassFlowCNNGeneral::getNetworkParameter() return false; } - if (CNNType == AutoDetect) - { + if (CNNType == AutoDetect) { tflite->GetInputDimension(false); modelxsize = tflite->ReadInputDimenstion(0); modelysize = tflite->ReadInputDimenstion(1); modelchannel = tflite->ReadInputDimenstion(2); int _anzoutputdimensions = tflite->GetAnzOutPut(); - switch (_anzoutputdimensions) - { + switch (_anzoutputdimensions) { case 2: CNNType = Analogue; ESP_LOGD(TAG, "TFlite-Type set to Analogue"); @@ -633,7 +632,8 @@ bool ClassFlowCNNGeneral::getNetworkParameter() if (modelxsize==32 && modelysize == 32) { CNNType = Analogue100; ESP_LOGD(TAG, "TFlite-Type set to Analogue100"); - } else { + } + else { CNNType = Digital100; ESP_LOGD(TAG, "TFlite-Type set to Digital"); } @@ -648,10 +648,10 @@ bool ClassFlowCNNGeneral::getNetworkParameter() } -bool ClassFlowCNNGeneral::doNeuralNetwork(string time) -{ - if (disabled) +bool ClassFlowCNNGeneral::doNeuralNetwork(string time) { + if (disabled) { return true; + } string logPath = CreateLogFolder(time); @@ -674,11 +674,11 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time) return false; } - for (int n = 0; n < GENERAL.size(); ++n) // For each NUMBER - { + // For each NUMBER + for (int n = 0; n < GENERAL.size(); ++n) { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Processing Number '" + GENERAL[n]->name + "'"); - for (int roi = 0; roi < GENERAL[n]->ROI.size(); ++roi) // For each ROI - { + // For each ROI + for (int roi = 0; roi < GENERAL[n]->ROI.size(); ++roi) { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "ROI #" + std::to_string(roi) + " - TfLite"); //ESP_LOGD(TAG, "General %d - TfLite", i); @@ -697,14 +697,17 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time) f2 = tflite->GetOutputValue(1); float result = fmod(atan2(f1, f2) / (M_PI * 2) + 2, 1); - if(GENERAL[n]->ROI[roi]->CCW) + if(GENERAL[n]->ROI[roi]->CCW) { GENERAL[n]->ROI[roi]->result_float = 10 - (result * 10); - else + } + else { GENERAL[n]->ROI[roi]->result_float = result * 10; + } ESP_LOGD(TAG, "General result (Analog)%i - CCW: %d - %f", roi, GENERAL[n]->ROI[roi]->CCW, GENERAL[n]->ROI[roi]->result_float); - if (isLogImage) + if (isLogImage) { LogImage(logPath, GENERAL[n]->ROI[roi]->name, &GENERAL[n]->ROI[roi]->result_float, NULL, time, GENERAL[n]->ROI[roi]->image_org); + } } break; case Digital: @@ -714,22 +717,19 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time) GENERAL[n]->ROI[roi]->result_klasse = tflite->GetClassFromImageBasis(GENERAL[n]->ROI[roi]->image); ESP_LOGD(TAG, "General result (Digit)%i: %d", roi, GENERAL[n]->ROI[roi]->result_klasse); - if (isLogImage) - { + if (isLogImage) { string _imagename = GENERAL[n]->name + "_" + GENERAL[n]->ROI[roi]->name; - if (isLogImageSelect) - { - if (LogImageSelect.find(GENERAL[n]->ROI[roi]->name) != std::string::npos) + if (isLogImageSelect) { + if (LogImageSelect.find(GENERAL[n]->ROI[roi]->name) != std::string::npos) { LogImage(logPath, _imagename, NULL, &GENERAL[n]->ROI[roi]->result_klasse, time, GENERAL[n]->ROI[roi]->image_org); + } } - else - { + else { LogImage(logPath, _imagename, NULL, &GENERAL[n]->ROI[roi]->result_klasse, time, GENERAL[n]->ROI[roi]->image_org); } } } break; - case DoubleHyprid10: { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CNN Type: DoubleHyprid10"); @@ -752,62 +752,56 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time) float result = _num; - if (_valplus > _valminus) - { + if (_valplus > _valminus) { result = result + _valplus / (_valplus + _val); _fit = _val + _valplus; } - else - { + else { result = result - _valminus / (_val + _valminus); _fit = _val + _valminus; - } - if (result >= 10) + + if (result >= 10) { result = result - 10; - if (result < 0) + } + + if (result < 0) { result = result + 10; + } string zw = "_num (p, m): " + to_string(_num) + " " + to_string(_numplus) + " " + to_string(_numminus); zw = zw + " _val (p, m): " + to_string(_val) + " " + to_string(_valplus) + " " + to_string(_valminus); zw = zw + " result: " + to_string(result) + " _fit: " + to_string(_fit); LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw); - _result_save_file = result; - if (_fit < CNNGoodThreshold) - { + if (_fit < CNNGoodThreshold) { GENERAL[n]->ROI[roi]->isReject = true; result = -1; _result_save_file+= 100; // In case fit is not sufficient, the result should still be saved with "-10x.y". string zw = "Value Rejected due to Threshold (Fit: " + to_string(_fit) + ", Threshold: " + to_string(CNNGoodThreshold) + ")"; LogFile.WriteToFile(ESP_LOG_WARN, TAG, zw); } - else - { + else { GENERAL[n]->ROI[roi]->isReject = false; } - GENERAL[n]->ROI[roi]->result_float = result; ESP_LOGD(TAG, "Result General(Analog)%i: %f", roi, GENERAL[n]->ROI[roi]->result_float); - if (isLogImage) - { + if (isLogImage) { string _imagename = GENERAL[n]->name + "_" + GENERAL[n]->ROI[roi]->name; - if (isLogImageSelect) - { - if (LogImageSelect.find(GENERAL[n]->ROI[roi]->name) != std::string::npos) + if (isLogImageSelect) { + if (LogImageSelect.find(GENERAL[n]->ROI[roi]->name) != std::string::npos) { LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[n]->ROI[roi]->image_org); + } } - else - { + else { LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[n]->ROI[roi]->image_org); } } - } - break; + } break; case Digital100: case Analogue100: { @@ -820,28 +814,27 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time) _num = tflite->GetOutClassification(); - if(GENERAL[n]->ROI[roi]->CCW) - GENERAL[n]->ROI[roi]->result_float = 10 - ((float)_num / 10.0); - else + if(GENERAL[n]->ROI[roi]->CCW) { + GENERAL[n]->ROI[roi]->result_float = 10 - ((float)_num / 10.0); + } + else { GENERAL[n]->ROI[roi]->result_float = (float)_num / 10.0; + } _result_save_file = GENERAL[n]->ROI[roi]->result_float; - GENERAL[n]->ROI[roi]->isReject = false; ESP_LOGD(TAG, "Result General(Analog)%i - CCW: %d - %f", roi, GENERAL[n]->ROI[roi]->CCW, GENERAL[n]->ROI[roi]->result_float); - if (isLogImage) - { + if (isLogImage) { string _imagename = GENERAL[n]->name + "_" + GENERAL[n]->ROI[roi]->name; - if (isLogImageSelect) - { - if (LogImageSelect.find(GENERAL[n]->ROI[roi]->name) != std::string::npos) + if (isLogImageSelect) { + if (LogImageSelect.find(GENERAL[n]->ROI[roi]->name) != std::string::npos) { LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[n]->ROI[roi]->image_org); + } } - else - { + else { LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[n]->ROI[roi]->image_org); } } @@ -860,93 +853,94 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time) } -bool ClassFlowCNNGeneral::isExtendedResolution(int _number) -{ - if (CNNType == Digital) +bool ClassFlowCNNGeneral::isExtendedResolution(int _number) { + if (CNNType == Digital) { return false; + } + return true; } -std::vector ClassFlowCNNGeneral::GetHTMLInfo() -{ +std::vector ClassFlowCNNGeneral::GetHTMLInfo() { std::vector result; - for (int _ana = 0; _ana < GENERAL.size(); ++_ana) - for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i) - { + for (int _ana = 0; _ana < GENERAL.size(); ++_ana) { + for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i) { ESP_LOGD(TAG, "Image: %d", (int) GENERAL[_ana]->ROI[i]->image); - if (GENERAL[_ana]->ROI[i]->image) - { - if (GENERAL[_ana]->name == "default") + if (GENERAL[_ana]->ROI[i]->image) { + if (GENERAL[_ana]->name == "default") { GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".jpg")); - else + } + else { GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg")); + } } HTMLInfo *zw = new HTMLInfo; - if (GENERAL[_ana]->name == "default") - { + if (GENERAL[_ana]->name == "default") { zw->filename = GENERAL[_ana]->ROI[i]->name + ".jpg"; zw->filename_org = GENERAL[_ana]->ROI[i]->name + ".jpg"; } - else - { + else { zw->filename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg"; zw->filename_org = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg"; } - if (CNNType == Digital) + if (CNNType == Digital) { zw->val = GENERAL[_ana]->ROI[i]->result_klasse; - else + } + else { zw->val = GENERAL[_ana]->ROI[i]->result_float; + } + zw->image = GENERAL[_ana]->ROI[i]->image; zw->image_org = GENERAL[_ana]->ROI[i]->image_org; result.push_back(zw); } + } return result; } -int ClassFlowCNNGeneral::getNumberGENERAL() -{ +int ClassFlowCNNGeneral::getNumberGENERAL() { return GENERAL.size(); } -string ClassFlowCNNGeneral::getNameGENERAL(int _analog) -{ - if (_analog < GENERAL.size()) +string ClassFlowCNNGeneral::getNameGENERAL(int _analog) { + if (_analog < GENERAL.size()) { return GENERAL[_analog]->name; + } return "GENERAL DOES NOT EXIST"; } -general* ClassFlowCNNGeneral::GetGENERAL(int _analog) -{ - if (_analog < GENERAL.size()) +general* ClassFlowCNNGeneral::GetGENERAL(int _analog) { + if (_analog < GENERAL.size()) { return GENERAL[_analog]; + } return NULL; } -void ClassFlowCNNGeneral::UpdateNameNumbers(std::vector *_name_numbers) -{ - for (int _dig = 0; _dig < GENERAL.size(); _dig++) - { +void ClassFlowCNNGeneral::UpdateNameNumbers(std::vector *_name_numbers) { + for (int _dig = 0; _dig < GENERAL.size(); _dig++) { std::string _name = GENERAL[_dig]->name; bool found = false; - for (int i = 0; i < (*_name_numbers).size(); ++i) - { - if ((*_name_numbers)[i] == _name) + + for (int i = 0; i < (*_name_numbers).size(); ++i) { + if ((*_name_numbers)[i] == _name) { found = true; + } } - if (!found) + if (!found) { (*_name_numbers).push_back(_name); + } } } @@ -955,26 +949,25 @@ string ClassFlowCNNGeneral::getReadoutRawString(int _analog) { string rt = ""; - if (_analog >= GENERAL.size() || GENERAL[_analog]==NULL || GENERAL[_analog]->ROI.size() == 0) + if (_analog >= GENERAL.size() || GENERAL[_analog]==NULL || GENERAL[_analog]->ROI.size() == 0) { return rt; + } - for (int i = 0; i < GENERAL[_analog]->ROI.size(); ++i) - { - if (CNNType == Analogue || CNNType == Analogue100) - { + for (int i = 0; i < GENERAL[_analog]->ROI.size(); ++i) { + if (CNNType == Analogue || CNNType == Analogue100) { rt = rt + "," + RundeOutput(GENERAL[_analog]->ROI[i]->result_float, 1); } - if (CNNType == Digital) - { - if (GENERAL[_analog]->ROI[i]->result_klasse == 10) + if (CNNType == Digital) { + if (GENERAL[_analog]->ROI[i]->result_klasse >= 10) { rt = rt + ",N"; - else + } + else { rt = rt + "," + RundeOutput(GENERAL[_analog]->ROI[i]->result_klasse, 0); + } } - if ((CNNType == DoubleHyprid10) || (CNNType == Digital100)) - { + if ((CNNType == DoubleHyprid10) || (CNNType == Digital100)) { rt = rt + "," + RundeOutput(GENERAL[_analog]->ROI[i]->result_float, 1); } } diff --git a/code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h b/code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h index 10c7b8e5..059a55b8 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h +++ b/code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h @@ -34,6 +34,7 @@ struct NumberPost { bool AllowNegativeRates; bool checkDigitIncreaseConsistency; time_t lastvalue; + time_t timeStampTimeUTC; string timeStamp; double FlowRateAct; // m3 / min double PreValue; // last value that was read out well diff --git a/code/components/jomjol_flowcontroll/ClassFlowInfluxDB.cpp b/code/components/jomjol_flowcontroll/ClassFlowInfluxDB.cpp index abe524ed..d6544004 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowInfluxDB.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowInfluxDB.cpp @@ -137,6 +137,7 @@ bool ClassFlowInfluxDB::doFlow(string zwtime) std::string resultraw = ""; std::string resultrate = ""; std::string resulttimestamp = ""; + long int timeutc; string zw = ""; string namenumber = ""; @@ -152,6 +153,7 @@ bool ClassFlowInfluxDB::doFlow(string zwtime) resulterror = (*NUMBERS)[i]->ErrorMessageText; resultrate = (*NUMBERS)[i]->ReturnRateValue; resulttimestamp = (*NUMBERS)[i]->timeStamp; + timeutc = (*NUMBERS)[i]->timeStampTimeUTC; if ((*NUMBERS)[i]->FieldV1.length() > 0) { @@ -167,7 +169,7 @@ bool ClassFlowInfluxDB::doFlow(string zwtime) } if (result.length() > 0) - InfluxDBPublish(measurement, namenumber, result, resulttimestamp); + InfluxDBPublish(measurement, namenumber, result, timeutc); } } diff --git a/code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.cpp b/code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.cpp index bd423305..669e11c1 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.cpp @@ -196,6 +196,7 @@ bool ClassFlowInfluxDBv2::doFlow(string zwtime) std::string resultraw = ""; std::string resultrate = ""; std::string resulttimestamp = ""; + long int resulttimeutc = 0; string zw = ""; string namenumber = ""; @@ -212,6 +213,8 @@ bool ClassFlowInfluxDBv2::doFlow(string zwtime) resulterror = (*NUMBERS)[i]->ErrorMessageText; resultrate = (*NUMBERS)[i]->ReturnRateValue; resulttimestamp = (*NUMBERS)[i]->timeStamp; + resulttimeutc = (*NUMBERS)[i]->timeStampTimeUTC; + if ((*NUMBERS)[i]->FieldV2.length() > 0) { @@ -229,8 +232,7 @@ bool ClassFlowInfluxDBv2::doFlow(string zwtime) printf("vor sende Influx_DB_V2 - namenumber. %s, result: %s, timestampt: %s", namenumber.c_str(), result.c_str(), resulttimestamp.c_str()); if (result.length() > 0) - InfluxDB_V2_Publish(measurement, namenumber, result, resulttimestamp); -// InfluxDB_V2_Publish(namenumber, result, resulttimestamp); + InfluxDB_V2_Publish(measurement, namenumber, result, resulttimeutc); } } diff --git a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp index 265a8425..cf5726ec 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp @@ -15,124 +15,129 @@ static const char* TAG = "POSTPROC"; -std::string ClassFlowPostProcessing::getNumbersName() -{ +std::string ClassFlowPostProcessing::getNumbersName() { std::string ret=""; - for (int i = 0; i < NUMBERS.size(); ++i) - { + for (int i = 0; i < NUMBERS.size(); ++i) { ret += NUMBERS[i]->name; - if (i < NUMBERS.size()-1) + + if (i < NUMBERS.size()-1) { ret = ret + "\t"; + } } -// ESP_LOGI(TAG, "Result ClassFlowPostProcessing::getNumbersName: %s", ret.c_str()); + // ESP_LOGI(TAG, "Result ClassFlowPostProcessing::getNumbersName: %s", ret.c_str()); return ret; } -std::string ClassFlowPostProcessing::GetJSON(std::string _lineend) -{ +std::string ClassFlowPostProcessing::GetJSON(std::string _lineend) { std::string json="{" + _lineend; - for (int i = 0; i < NUMBERS.size(); ++i) - { + for (int i = 0; i < NUMBERS.size(); ++i) { json += "\"" + NUMBERS[i]->name + "\":" + _lineend; - json += getJsonFromNumber(i, _lineend) + _lineend; - if ((i+1) < NUMBERS.size()) + if ((i+1) < NUMBERS.size()) { json += "," + _lineend; + } } + json += "}"; return json; } - string ClassFlowPostProcessing::getJsonFromNumber(int i, std::string _lineend) { - std::string json = ""; + std::string json = ""; - json += " {" + _lineend; + json += " {" + _lineend; - if (NUMBERS[i]->ReturnValue.length() > 0) - json += " \"value\": \"" + NUMBERS[i]->ReturnValue + "\"," + _lineend; - else - json += " \"value\": \"\"," + _lineend; + if (NUMBERS[i]->ReturnValue.length() > 0) { + json += " \"value\": \"" + NUMBERS[i]->ReturnValue + "\"," + _lineend; + } + else { + json += " \"value\": \"\"," + _lineend; + } - json += " \"raw\": \"" + NUMBERS[i]->ReturnRawValue + "\"," + _lineend; - json += " \"pre\": \"" + NUMBERS[i]->ReturnPreValue + "\"," + _lineend; - json += " \"error\": \"" + NUMBERS[i]->ErrorMessageText + "\"," + _lineend; + json += " \"raw\": \"" + NUMBERS[i]->ReturnRawValue + "\"," + _lineend; + json += " \"pre\": \"" + NUMBERS[i]->ReturnPreValue + "\"," + _lineend; + json += " \"error\": \"" + NUMBERS[i]->ErrorMessageText + "\"," + _lineend; - if (NUMBERS[i]->ReturnRateValue.length() > 0) - json += " \"rate\": \"" + NUMBERS[i]->ReturnRateValue + "\"," + _lineend; - else - json += " \"rate\": \"\"," + _lineend; + if (NUMBERS[i]->ReturnRateValue.length() > 0) { + json += " \"rate\": \"" + NUMBERS[i]->ReturnRateValue + "\"," + _lineend; + } + else { + json += " \"rate\": \"\"," + _lineend; + } - json += " \"timestamp\": \"" + NUMBERS[i]->timeStamp + "\"" + _lineend; - json += " }" + _lineend; + json += " \"timestamp\": \"" + NUMBERS[i]->timeStamp + "\"" + _lineend; + json += " }" + _lineend; - return json; + return json; } - -string ClassFlowPostProcessing::GetPreValue(std::string _number) -{ +string ClassFlowPostProcessing::GetPreValue(std::string _number) { std::string result; int index = -1; - if (_number == "") - _number = "default"; + if (_number == "") { + _number = "default"; + } - for (int i = 0; i < NUMBERS.size(); ++i) - if (NUMBERS[i]->name == _number) + for (int i = 0; i < NUMBERS.size(); ++i) { + if (NUMBERS[i]->name == _number) { index = i; + } + } - if (index == -1) + if (index == -1) { return std::string(""); + } result = RundeOutput(NUMBERS[index]->PreValue, NUMBERS[index]->Nachkomma); return result; } - -bool ClassFlowPostProcessing::SetPreValue(double _newvalue, string _numbers, bool _extern) -{ +bool ClassFlowPostProcessing::SetPreValue(double _newvalue, string _numbers, bool _extern) { //ESP_LOGD(TAG, "SetPrevalue: %f, %s", zw, _numbers.c_str()); for (int j = 0; j < NUMBERS.size(); ++j) { //ESP_LOGD(TAG, "Number %d, %s", j, NUMBERS[j]->name.c_str()); + if (NUMBERS[j]->name == _numbers) { - if (_newvalue >= 0) { // if new value posivive, use provided value to preset PreValue + if (_newvalue >= 0) { + // if new value posivive, use provided value to preset PreValue NUMBERS[j]->PreValue = _newvalue; } - else { // if new value negative, use last raw value to preset PreValue + else { + // if new value negative, use last raw value to preset PreValue char* p; double ReturnRawValueAsDouble = strtod(NUMBERS[j]->ReturnRawValue.c_str(), &p); + if (ReturnRawValueAsDouble == 0) { - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "SetPreValue: RawValue not a valid value for further processing: " - + NUMBERS[j]->ReturnRawValue); + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "SetPreValue: RawValue not a valid value for further processing: " + NUMBERS[j]->ReturnRawValue); return false; } + NUMBERS[j]->PreValue = ReturnRawValueAsDouble; } NUMBERS[j]->ReturnPreValue = std::to_string(NUMBERS[j]->PreValue); NUMBERS[j]->PreValueOkay = true; - if (_extern) - { + if (_extern) { time(&(NUMBERS[j]->lastvalue)); localtime(&(NUMBERS[j]->lastvalue)); } + //ESP_LOGD(TAG, "Found %d! - set to %.8f", j, NUMBERS[j]->PreValue); UpdatePreValueINI = true; // Only update prevalue file if a new value is set SavePreValue(); - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "SetPreValue: PreValue for " + NUMBERS[j]->name + " set to " + - std::to_string(NUMBERS[j]->PreValue)); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "SetPreValue: PreValue for " + NUMBERS[j]->name + " set to " + std::to_string(NUMBERS[j]->PreValue)); return true; } } @@ -141,9 +146,7 @@ bool ClassFlowPostProcessing::SetPreValue(double _newvalue, string _numbers, boo return false; // No new value was set (e.g. wrong numbersname, no numbers at all) } - -bool ClassFlowPostProcessing::LoadPreValue(void) -{ +bool ClassFlowPostProcessing::LoadPreValue(void) { std::vector splitted; FILE* pFile; char zw[1024]; @@ -152,30 +155,31 @@ bool ClassFlowPostProcessing::LoadPreValue(void) UpdatePreValueINI = false; // Conversion to the new format - pFile = fopen(FilePreValue.c_str(), "r"); - if (pFile == NULL) + + if (pFile == NULL) { return false; + } fgets(zw, 1024, pFile); ESP_LOGD(TAG, "Read line Prevalue.ini: %s", zw); zwtime = trim(std::string(zw)); - if (zwtime.length() == 0) + + if (zwtime.length() == 0) { return false; + } splitted = HelperZerlegeZeile(zwtime, "\t"); - if (splitted.size() > 1) // Conversion to the new format - { - while ((splitted.size() > 1) && !_done) - { + + // Conversion to the new format + if (splitted.size() > 1) { + while ((splitted.size() > 1) && !_done) { name = trim(splitted[0]); zwtime = trim(splitted[1]); zwvalue = trim(splitted[2]); - for (int j = 0; j < NUMBERS.size(); ++j) - { - if (NUMBERS[j]->name == name) - { + for (int j = 0; j < NUMBERS.size(); ++j) { + if (NUMBERS[j]->name == name) { NUMBERS[j]->PreValue = stod(zwvalue.c_str()); NUMBERS[j]->ReturnPreValue = RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma + 1); // To be on the safe side, 1 digit more, as Exgtended Resolution may be on (will only be set during the first run). @@ -198,21 +202,24 @@ bool ClassFlowPostProcessing::LoadPreValue(void) localtime(&tStart); double difference = difftime(tStart, NUMBERS[j]->lastvalue); difference /= 60; - if (difference > PreValueAgeStartup) + + if (difference > PreValueAgeStartup) { NUMBERS[j]->PreValueOkay = false; - else + } + else { NUMBERS[j]->PreValueOkay = true; + } } } - if (!fgets(zw, 1024, pFile)) + if (!fgets(zw, 1024, pFile)) { _done = true; - else - { + } + else { ESP_LOGD(TAG, "Read line Prevalue.ini: %s", zw); splitted = HelperZerlegeZeile(trim(std::string(zw)), "\t"); - if (splitted.size() > 1) - { + + if (splitted.size() > 1) { name = trim(splitted[0]); zwtime = trim(splitted[1]); zwvalue = trim(splitted[2]); @@ -221,8 +228,8 @@ bool ClassFlowPostProcessing::LoadPreValue(void) } fclose(pFile); } - else // Old Format - { + else { + // Old Format fgets(zw, 1024, pFile); fclose(pFile); ESP_LOGD(TAG, "%s", zw); @@ -250,14 +257,15 @@ bool ClassFlowPostProcessing::LoadPreValue(void) localtime(&tStart); double difference = difftime(tStart, NUMBERS[0]->lastvalue); difference /= 60; - if (difference > PreValueAgeStartup) + + if (difference > PreValueAgeStartup) { return false; + } NUMBERS[0]->Value = NUMBERS[0]->PreValue; NUMBERS[0]->ReturnValue = to_string(NUMBERS[0]->Value); - if (NUMBERS[0]->digit_roi || NUMBERS[0]->analog_roi) - { + if (NUMBERS[0]->digit_roi || NUMBERS[0]->analog_roi) { NUMBERS[0]->ReturnValue = RundeOutput(NUMBERS[0]->Value, NUMBERS[0]->Nachkomma); } @@ -268,26 +276,28 @@ bool ClassFlowPostProcessing::LoadPreValue(void) return true; } -void ClassFlowPostProcessing::SavePreValue() -{ +void ClassFlowPostProcessing::SavePreValue() { FILE* pFile; string _zw; - if (!UpdatePreValueINI) // PreValues unchanged --> File does not have to be rewritten + // PreValues unchanged --> File does not have to be rewritten + if (!UpdatePreValueINI) { return; + } pFile = fopen(FilePreValue.c_str(), "w"); - for (int j = 0; j < NUMBERS.size(); ++j) - { + for (int j = 0; j < NUMBERS.size(); ++j) { char buffer[80]; struct tm* timeinfo = localtime(&NUMBERS[j]->lastvalue); strftime(buffer, 80, PREVALUE_TIME_FORMAT_OUTPUT, timeinfo); NUMBERS[j]->timeStamp = std::string(buffer); -// ESP_LOGD(TAG, "SaverPreValue %d, Value: %f, Nachkomma %d", j, NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma); + NUMBERS[j]->timeStampTimeUTC = NUMBERS[j]->lastvalue; + // ESP_LOGD(TAG, "SaverPreValue %d, Value: %f, Nachkomma %d", j, NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma); _zw = NUMBERS[j]->name + "\t" + NUMBERS[j]->timeStamp + "\t" + RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma) + "\n"; ESP_LOGD(TAG, "Write PreValue line: %s", _zw.c_str()); + if (pFile) { fputs(_zw.c_str(), pFile); } @@ -298,9 +308,7 @@ void ClassFlowPostProcessing::SavePreValue() fclose(pFile); } - -ClassFlowPostProcessing::ClassFlowPostProcessing(std::vector* lfc, ClassFlowCNNGeneral *_analog, ClassFlowCNNGeneral *_digit) -{ +ClassFlowPostProcessing::ClassFlowPostProcessing(std::vector* lfc, ClassFlowCNNGeneral *_analog, ClassFlowCNNGeneral *_digit) { PreValueUse = false; PreValueAgeStartup = 30; ErrorMessage = false; @@ -313,76 +321,57 @@ ClassFlowPostProcessing::ClassFlowPostProcessing(std::vector* lfc, C flowAnalog = _analog; flowDigit = _digit; - for (int i = 0; i < ListFlowControll->size(); ++i) - { - if (((*ListFlowControll)[i])->name().compare("ClassFlowTakeImage") == 0) - { + for (int i = 0; i < ListFlowControll->size(); ++i) { + if (((*ListFlowControll)[i])->name().compare("ClassFlowTakeImage") == 0) { flowTakeImage = (ClassFlowTakeImage*) (*ListFlowControll)[i]; } } } -void ClassFlowPostProcessing::handleDecimalExtendedResolution(string _decsep, string _value) -{ +void ClassFlowPostProcessing::handleDecimalExtendedResolution(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"; + // ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); - for (int j = 0; j < NUMBERS.size(); ++j) - { + if (_pospunkt > -1) { + _digit = _decsep.substr(0, _pospunkt); + } + else { + _digit = "default"; + } + + for (int j = 0; j < NUMBERS.size(); ++j) { bool _zwdc = false; - if (toUpper(_value) == "TRUE") + if (toUpper(_value) == "TRUE") { _zwdc = true; - - if (_digit == "default") // Set to default first (if nothing else is set) - { - NUMBERS[j]->isExtendedResolution = _zwdc; } - if (NUMBERS[j]->name == _digit) - { + // Set to default first (if nothing else is set) + if ((_digit == "default") || (NUMBERS[j]->name == _digit)) { NUMBERS[j]->isExtendedResolution = _zwdc; } } } - -void ClassFlowPostProcessing::handleDecimalSeparator(string _decsep, string _value) -{ +void ClassFlowPostProcessing::handleDecimalSeparator(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) + // ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); + + if (_pospunkt > -1) { _digit = _decsep.substr(0, _pospunkt); - else + } + else { _digit = "default"; + } - for (int j = 0; j < NUMBERS.size(); ++j) - { + for (int j = 0; j < NUMBERS.size(); ++j) { int _zwdc = 0; + _zwdc = stoi(_value); -// try - { - _zwdc = stoi(_value); - } -/* catch(const std::exception& e) - { - ESP_LOGD(TAG, "ERROR - Decimalshift is not a number: %s", _value.c_str()); - } -*/ - if (_digit == "default") // Set to default first (if nothing else is set) - { - NUMBERS[j]->DecimalShift = _zwdc; - NUMBERS[j]->DecimalShiftInitial = _zwdc; - } - - if (NUMBERS[j]->name == _digit) - { + // Set to default first (if nothing else is set) + if ((_digit == "default") || (NUMBERS[j]->name == _digit)) { NUMBERS[j]->DecimalShift = _zwdc; NUMBERS[j]->DecimalShiftInitial = _zwdc; } @@ -391,209 +380,180 @@ void ClassFlowPostProcessing::handleDecimalSeparator(string _decsep, string _val } } -void ClassFlowPostProcessing::handleAnalogDigitalTransitionStart(string _decsep, string _value) -{ +void ClassFlowPostProcessing::handleAnalogDigitalTransitionStart(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) + // ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); + + if (_pospunkt > -1) { _digit = _decsep.substr(0, _pospunkt); - else + } + else { _digit = "default"; + } - for (int j = 0; j < NUMBERS.size(); ++j) - { + for (int j = 0; j < NUMBERS.size(); ++j) { float _zwdc = 9.2; - { - _zwdc = stof(_value); - } - if (_digit == "default" || NUMBERS[j]->name == _digit) // Set to default first (if nothing else is set) - { + _zwdc = stof(_value); + + // Set to default first (if nothing else is set) + if ((_digit == "default") || (NUMBERS[j]->name == _digit)) { NUMBERS[j]->AnalogDigitalTransitionStart = _zwdc; } } } -void ClassFlowPostProcessing::handleAllowNegativeRate(string _decsep, string _value) -{ +void ClassFlowPostProcessing::handleAllowNegativeRate(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) + // ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); + + if (_pospunkt > -1) { _digit = _decsep.substr(0, _pospunkt); - else + } + else { _digit = "default"; + } - for (int j = 0; j < NUMBERS.size(); ++j) - { + for (int j = 0; j < NUMBERS.size(); ++j) { bool _rt = false; - if (toUpper(_value) == "TRUE") + if (toUpper(_value) == "TRUE") { _rt = true; - - if (_digit == "default") // Set to default first (if nothing else is set) - { - NUMBERS[j]->AllowNegativeRates = _rt; } - if (NUMBERS[j]->name == _digit) - { + // Set to default first (if nothing else is set) + if ((_digit == "default") || (NUMBERS[j]->name == _digit)) { NUMBERS[j]->AllowNegativeRates = _rt; } } } - - -void ClassFlowPostProcessing::handleMaxRateType(string _decsep, string _value) -{ +void ClassFlowPostProcessing::handleMaxRateType(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) + // ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); + + if (_pospunkt > -1) { _digit = _decsep.substr(0, _pospunkt); - else + } + else { _digit = "default"; + } - for (int j = 0; j < NUMBERS.size(); ++j) - { + for (int j = 0; j < NUMBERS.size(); ++j) { t_RateType _rt = AbsoluteChange; - if (toUpper(_value) == "RATECHANGE") + if (toUpper(_value) == "RATECHANGE") { _rt = RateChange; - - if (_digit == "default") // Set to default first (if nothing else is set) - { - NUMBERS[j]->RateType = _rt; } - if (NUMBERS[j]->name == _digit) - { + // Set to default first (if nothing else is set) + if ((_digit == "default") || (NUMBERS[j]->name == _digit)) { NUMBERS[j]->RateType = _rt; } } } - - - -void ClassFlowPostProcessing::handleMaxRateValue(string _decsep, string _value) -{ +void ClassFlowPostProcessing::handleMaxRateValue(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) + // ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); + + if (_pospunkt > -1) { _digit = _decsep.substr(0, _pospunkt); - else + } + else { _digit = "default"; - for (int j = 0; j < NUMBERS.size(); ++j) - { + } + + for (int j = 0; j < NUMBERS.size(); ++j) { float _zwdc = 1; -// try - { - _zwdc = stof(_value); - } -/* catch(const std::exception& e) - { - ESP_LOGD(TAG, "ERROR - MaxRateValue is not a number: %s", _value.c_str()); - } -*/ - if (_digit == "default") // Set to default first (if nothing else is set) - { - NUMBERS[j]->useMaxRateValue = true; - NUMBERS[j]->MaxRateValue = _zwdc; - } - if (NUMBERS[j]->name == _digit) - { + _zwdc = stof(_value); + + // Set to default first (if nothing else is set) + if ((_digit == "default") || (NUMBERS[j]->name == _digit)) { NUMBERS[j]->useMaxRateValue = true; NUMBERS[j]->MaxRateValue = _zwdc; } } } - -bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph) -{ +bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph) { std::vector splitted; int _n; aktparamgraph = trim(aktparamgraph); - if (aktparamgraph.size() == 0) - if (!this->GetNextParagraph(pfile, aktparamgraph)) + if (aktparamgraph.size() == 0) { + if (!this->GetNextParagraph(pfile, aktparamgraph)) { return false; + } + } - - if (aktparamgraph.compare("[PostProcessing]") != 0) // Paragraph does not fit PostProcessing + // Paragraph does not fit PostProcessing + if (aktparamgraph.compare("[PostProcessing]") != 0) { return false; + } InitNUMBERS(); - - while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph)) - { + while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph)) { splitted = ZerlegeZeile(aktparamgraph); std::string _param = GetParameterName(splitted[0]); - if ((toUpper(_param) == "EXTENDEDRESOLUTION") && (splitted.size() > 1)) - { + if ((toUpper(_param) == "EXTENDEDRESOLUTION") && (splitted.size() > 1)) { handleDecimalExtendedResolution(splitted[0], splitted[1]); } - if ((toUpper(_param) == "DECIMALSHIFT") && (splitted.size() > 1)) - { + if ((toUpper(_param) == "DECIMALSHIFT") && (splitted.size() > 1)) { handleDecimalSeparator(splitted[0], splitted[1]); } - if ((toUpper(_param) == "ANALOGDIGITALTRANSITIONSTART") && (splitted.size() > 1)) - { + + if ((toUpper(_param) == "ANALOGDIGITALTRANSITIONSTART") && (splitted.size() > 1)) { handleAnalogDigitalTransitionStart(splitted[0], splitted[1]); } - if ((toUpper(_param) == "MAXRATEVALUE") && (splitted.size() > 1)) - { + + if ((toUpper(_param) == "MAXRATEVALUE") && (splitted.size() > 1)) { handleMaxRateValue(splitted[0], splitted[1]); } - if ((toUpper(_param) == "MAXRATETYPE") && (splitted.size() > 1)) - { + + if ((toUpper(_param) == "MAXRATETYPE") && (splitted.size() > 1)) { handleMaxRateType(splitted[0], splitted[1]); } - - if ((toUpper(_param) == "PREVALUEUSE") && (splitted.size() > 1)) - { - if (toUpper(splitted[1]) == "TRUE") - { + + if ((toUpper(_param) == "PREVALUEUSE") && (splitted.size() > 1)) { + if (toUpper(splitted[1]) == "TRUE") { PreValueUse = true; } } - if ((toUpper(_param) == "CHECKDIGITINCREASECONSISTENCY") && (splitted.size() > 1)) - { - if (toUpper(splitted[1]) == "TRUE") - for (_n = 0; _n < NUMBERS.size(); ++_n) + + if ((toUpper(_param) == "CHECKDIGITINCREASECONSISTENCY") && (splitted.size() > 1)) { + if (toUpper(splitted[1]) == "TRUE") { + for (_n = 0; _n < NUMBERS.size(); ++_n) { NUMBERS[_n]->checkDigitIncreaseConsistency = true; - } - if ((toUpper(_param) == "ALLOWNEGATIVERATES") && (splitted.size() > 1)) - { + } + } + } + + if ((toUpper(_param) == "ALLOWNEGATIVERATES") && (splitted.size() > 1)) { handleAllowNegativeRate(splitted[0], splitted[1]); -/* Updated to allow individual Settings - if (toUpper(splitted[1]) == "TRUE") - for (_n = 0; _n < NUMBERS.size(); ++_n) - NUMBERS[_n]->AllowNegativeRates = true; -*/ } - if ((toUpper(_param) == "ERRORMESSAGE") && (splitted.size() > 1)) - { - if (toUpper(splitted[1]) == "TRUE") + + if ((toUpper(_param) == "ERRORMESSAGE") && (splitted.size() > 1)) { + if (toUpper(splitted[1]) == "TRUE") { ErrorMessage = true; + } } - if ((toUpper(_param) == "IGNORELEADINGNAN") && (splitted.size() > 1)) - { - if (toUpper(splitted[1]) == "TRUE") + + if ((toUpper(_param) == "IGNORELEADINGNAN") && (splitted.size() > 1)) { + if (toUpper(splitted[1]) == "TRUE") { IgnoreLeadingNaN = true; + } } - - if ((toUpper(_param) == "PREVALUEAGESTARTUP") && (splitted.size() > 1)) - { + if ((toUpper(_param) == "PREVALUEAGESTARTUP") && (splitted.size() > 1)) { PreValueAgeStartup = std::stoi(splitted[1]); } } @@ -605,49 +565,53 @@ bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph) return true; } -void ClassFlowPostProcessing::InitNUMBERS() -{ +void ClassFlowPostProcessing::InitNUMBERS() { int anzDIGIT = 0; int anzANALOG = 0; std::vector name_numbers; - if (flowDigit) - { + if (flowDigit) { anzDIGIT = flowDigit->getNumberGENERAL(); flowDigit->UpdateNameNumbers(&name_numbers); } - if (flowAnalog) - { + + if (flowAnalog) { anzANALOG = flowAnalog->getNumberGENERAL(); flowAnalog->UpdateNameNumbers(&name_numbers); } ESP_LOGD(TAG, "Anzahl NUMBERS: %d - DIGITS: %d, ANALOG: %d", name_numbers.size(), anzDIGIT, anzANALOG); - for (int _num = 0; _num < name_numbers.size(); ++_num) - { + for (int _num = 0; _num < name_numbers.size(); ++_num) { NumberPost *_number = new NumberPost; _number->name = name_numbers[_num]; _number->digit_roi = NULL; - if (flowDigit) + + if (flowDigit) { _number->digit_roi = flowDigit->FindGENERAL(name_numbers[_num]); + } - if (_number->digit_roi) + if (_number->digit_roi) { _number->AnzahlDigital = _number->digit_roi->ROI.size(); - else + } + else { _number->AnzahlDigital = 0; + } _number->analog_roi = NULL; - if (flowAnalog) + + if (flowAnalog) { _number->analog_roi = flowAnalog->FindGENERAL(name_numbers[_num]); + } - - if (_number->analog_roi) + if (_number->analog_roi) { _number->AnzahlAnalog = _number->analog_roi->ROI.size(); - else + } + else { _number->AnzahlAnalog = 0; + } _number->ReturnRawValue = ""; // Raw value (with N & leading 0). _number->ReturnValue = ""; // corrected return value, possibly with error message @@ -664,7 +628,6 @@ void ClassFlowPostProcessing::InitNUMBERS() _number->isExtendedResolution = false; _number->AnalogDigitalTransitionStart=9.2; - _number->FlowRateAct = 0; // m3 / min _number->PreValue = 0; // last value read out well _number->Value = 0; // last value read out, incl. corrections @@ -680,38 +643,39 @@ void ClassFlowPostProcessing::InitNUMBERS() for (int i = 0; i < NUMBERS.size(); ++i) { ESP_LOGD(TAG, "Number %s, Anz DIG: %d, Anz ANA %d", NUMBERS[i]->name.c_str(), NUMBERS[i]->AnzahlDigital, NUMBERS[i]->AnzahlAnalog); } - } -string ClassFlowPostProcessing::ShiftDecimal(string in, int _decShift){ - - if (_decShift == 0){ +string ClassFlowPostProcessing::ShiftDecimal(string in, int _decShift) { + if (_decShift == 0) { return in; } int _pos_dec_org, _pos_dec_neu; _pos_dec_org = findDelimiterPos(in, "."); + if (_pos_dec_org == std::string::npos) { _pos_dec_org = in.length(); } - else - { + else { in = in.erase(_pos_dec_org, 1); } _pos_dec_neu = _pos_dec_org + _decShift; - if (_pos_dec_neu <= 0) { // comma is before the first digit - for (int i = 0; i > _pos_dec_neu; --i){ + // comma is before the first digit + if (_pos_dec_neu <= 0) { + for (int i = 0; i > _pos_dec_neu; --i) { in = in.insert(0, "0"); } + in = "0." + in; return in; } - if (_pos_dec_neu > in.length()){ // Comma should be after string (123 --> 1230) - for (int i = in.length(); i < _pos_dec_neu; ++i){ + // Comma should be after string (123 --> 1230) + if (_pos_dec_neu > in.length()) { + for (int i = in.length(); i < _pos_dec_neu; ++i) { in = in.insert(in.length(), "0"); } return in; @@ -725,8 +689,7 @@ string ClassFlowPostProcessing::ShiftDecimal(string in, int _decShift){ return zw; } -bool ClassFlowPostProcessing::doFlow(string zwtime) -{ +bool ClassFlowPostProcessing::doFlow(string zwtime) { string result = ""; string digit = ""; string analog = ""; @@ -738,8 +701,10 @@ bool ClassFlowPostProcessing::doFlow(string zwtime) // Update decimal point, as the decimal places can also change when changing from CNNType Auto --> xyz: imagetime = flowTakeImage->getTimeImageTaken(); - if (imagetime == 0) + + if (imagetime == 0) { time(&imagetime); + } struct tm* timeinfo; timeinfo = localtime(&imagetime); @@ -749,74 +714,81 @@ bool ClassFlowPostProcessing::doFlow(string zwtime) ESP_LOGD(TAG, "Quantity NUMBERS: %d", NUMBERS.size()); - for (int j = 0; j < NUMBERS.size(); ++j) - { + for (int j = 0; j < NUMBERS.size(); ++j) { NUMBERS[j]->ReturnRawValue = ""; NUMBERS[j]->ReturnRateValue = ""; NUMBERS[j]->ReturnValue = ""; + NUMBERS[j]->ReturnChangeAbsolute = RundeOutput(0.0, NUMBERS[j]->Nachkomma); // always reset change absolute NUMBERS[j]->ErrorMessageText = ""; NUMBERS[j]->Value = -1; - /* calculate time difference BEFORE we overwrite the 'lastvalue' */ + // calculate time difference BEFORE we overwrite the 'lastvalue' double difference = difftime(imagetime, NUMBERS[j]->lastvalue); // in seconds - /* TODO: - * We could call `NUMBERS[j]->lastvalue = imagetime;` here and remove all other such calls further down. - * But we should check nothing breaks! */ + // TODO: + // We could call `NUMBERS[j]->lastvalue = imagetime;` here and remove all other such calls further down. + // But we should check nothing breaks! UpdateNachkommaDecimalShift(); int previous_value = -1; - if (NUMBERS[j]->analog_roi) - { - NUMBERS[j]->ReturnRawValue = flowAnalog->getReadout(j, NUMBERS[j]->isExtendedResolution); - if (NUMBERS[j]->ReturnRawValue.length() > 0) - { + if (NUMBERS[j]->analog_roi) { + NUMBERS[j]->ReturnRawValue = flowAnalog->getReadout(j, NUMBERS[j]->isExtendedResolution); + + if (NUMBERS[j]->ReturnRawValue.length() > 0) { char zw = NUMBERS[j]->ReturnRawValue[0]; - if (zw >= 48 && zw <=57) + + if (zw >= 48 && zw <=57) { previous_value = zw - 48; + } } } + #ifdef SERIAL_DEBUG ESP_LOGD(TAG, "After analog->getReadout: ReturnRaw %s", NUMBERS[j]->ReturnRawValue.c_str()); #endif - if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi) - NUMBERS[j]->ReturnRawValue = "." + NUMBERS[j]->ReturnRawValue; - if (NUMBERS[j]->digit_roi) - { - if (NUMBERS[j]->analog_roi) - NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, false, previous_value, NUMBERS[j]->analog_roi->ROI[0]->result_float, NUMBERS[j]->AnalogDigitalTransitionStart) + NUMBERS[j]->ReturnRawValue; - else - NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, NUMBERS[j]->isExtendedResolution, previous_value); // Extended Resolution only if there are no analogue digits + if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi) { + NUMBERS[j]->ReturnRawValue = "." + NUMBERS[j]->ReturnRawValue; } + + if (NUMBERS[j]->digit_roi) { + if (NUMBERS[j]->analog_roi) { + NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, false, previous_value, NUMBERS[j]->analog_roi->ROI[0]->result_float, NUMBERS[j]->AnalogDigitalTransitionStart) + NUMBERS[j]->ReturnRawValue; + } + else { + NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, NUMBERS[j]->isExtendedResolution, previous_value); // Extended Resolution only if there are no analogue digits + } + } + #ifdef SERIAL_DEBUG ESP_LOGD(TAG, "After digital->getReadout: ReturnRaw %s", NUMBERS[j]->ReturnRawValue.c_str()); #endif + NUMBERS[j]->ReturnRawValue = ShiftDecimal(NUMBERS[j]->ReturnRawValue, NUMBERS[j]->DecimalShift); #ifdef SERIAL_DEBUG ESP_LOGD(TAG, "After ShiftDecimal: ReturnRaw %s", NUMBERS[j]->ReturnRawValue.c_str()); #endif - if (IgnoreLeadingNaN) - while ((NUMBERS[j]->ReturnRawValue.length() > 1) && (NUMBERS[j]->ReturnRawValue[0] == 'N')) + if (IgnoreLeadingNaN) { + while ((NUMBERS[j]->ReturnRawValue.length() > 1) && (NUMBERS[j]->ReturnRawValue[0] == 'N')) { NUMBERS[j]->ReturnRawValue.erase(0, 1); + } + } #ifdef SERIAL_DEBUG ESP_LOGD(TAG, "After IgnoreLeadingNaN: ReturnRaw %s", NUMBERS[j]->ReturnRawValue.c_str()); #endif + NUMBERS[j]->ReturnValue = NUMBERS[j]->ReturnRawValue; - if (findDelimiterPos(NUMBERS[j]->ReturnValue, "N") != std::string::npos) - { - if (PreValueUse && NUMBERS[j]->PreValueOkay) - { + if (findDelimiterPos(NUMBERS[j]->ReturnValue, "N") != std::string::npos) { + if (PreValueUse && NUMBERS[j]->PreValueOkay) { NUMBERS[j]->ReturnValue = ErsetzteN(NUMBERS[j]->ReturnValue, NUMBERS[j]->PreValue); } - else - { + else { string _zw = NUMBERS[j]->name + ": Raw: " + NUMBERS[j]->ReturnRawValue + ", Value: " + NUMBERS[j]->ReturnValue + ", Status: " + NUMBERS[j]->ErrorMessageText; LogFile.WriteToFile(ESP_LOG_INFO, TAG, _zw); /* TODO to be discussed, see https://github.com/jomjol/AI-on-the-edge-device/issues/1617 */ @@ -826,31 +798,36 @@ bool ClassFlowPostProcessing::doFlow(string zwtime) continue; // there is no number because there is still an N. } } + #ifdef SERIAL_DEBUG ESP_LOGD(TAG, "After findDelimiterPos: ReturnValue %s", NUMBERS[j]->ReturnRawValue.c_str()); #endif + // Delete leading zeros (unless there is only one 0 left) - while ((NUMBERS[j]->ReturnValue.length() > 1) && (NUMBERS[j]->ReturnValue[0] == '0')) + while ((NUMBERS[j]->ReturnValue.length() > 1) && (NUMBERS[j]->ReturnValue[0] == '0')) { NUMBERS[j]->ReturnValue.erase(0, 1); + } + #ifdef SERIAL_DEBUG ESP_LOGD(TAG, "After removeLeadingZeros: ReturnValue %s", NUMBERS[j]->ReturnRawValue.c_str()); #endif + NUMBERS[j]->Value = std::stod(NUMBERS[j]->ReturnValue); + #ifdef SERIAL_DEBUG ESP_LOGD(TAG, "After setting the Value: Value %f and as double is %f", NUMBERS[j]->Value, std::stod(NUMBERS[j]->ReturnValue)); #endif - if (NUMBERS[j]->checkDigitIncreaseConsistency) - { - if (flowDigit) - { - if (flowDigit->getCNNType() != Digital) + if (NUMBERS[j]->checkDigitIncreaseConsistency) { + if (flowDigit) { + if (flowDigit->getCNNType() != Digital) { ESP_LOGD(TAG, "checkDigitIncreaseConsistency = true - ignored due to wrong CNN-Type (not Digital Classification)"); - else + } + else { NUMBERS[j]->Value = checkDigitConsistency(NUMBERS[j]->Value, NUMBERS[j]->DecimalShift, NUMBERS[j]->analog_roi != NULL, NUMBERS[j]->PreValue); + } } - else - { + else { #ifdef SERIAL_DEBUG ESP_LOGD(TAG, "checkDigitIncreaseConsistency = true - no digital numbers defined!"); #endif @@ -861,11 +838,10 @@ bool ClassFlowPostProcessing::doFlow(string zwtime) ESP_LOGD(TAG, "After checkDigitIncreaseConsistency: Value %f", NUMBERS[j]->Value); #endif - if (!NUMBERS[j]->AllowNegativeRates) - { + if (!NUMBERS[j]->AllowNegativeRates) { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handleAllowNegativeRate for device: " + NUMBERS[j]->name); - if ((NUMBERS[j]->Value < NUMBERS[j]->PreValue)) - { + + if ((NUMBERS[j]->Value < NUMBERS[j]->PreValue)) { // more debug if extended resolution is on, see #2447 if (NUMBERS[j]->isExtendedResolution) { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Neg: value=" + std::to_string(NUMBERS[j]->Value) @@ -873,7 +849,7 @@ bool ClassFlowPostProcessing::doFlow(string zwtime) + ", preToll=" + std::to_string(NUMBERS[j]->PreValue-(2/pow(10, NUMBERS[j]->Nachkomma)))); } - // Include inaccuracy of 0.2 for isExtendedResolution. + // Include inaccuracy of 0.2 for isExtendedResolution. if ((NUMBERS[j]->Value >= (NUMBERS[j]->PreValue-(2/pow(10, NUMBERS[j]->Nachkomma))) && NUMBERS[j]->isExtendedResolution) // not extended resolution allows -1 on the lowest digit || (NUMBERS[j]->Value >= (NUMBERS[j]->PreValue-(1/pow(10, NUMBERS[j]->Nachkomma))) && !NUMBERS[j]->isExtendedResolution)) { @@ -891,7 +867,6 @@ bool ClassFlowPostProcessing::doFlow(string zwtime) WriteDataLog(j); continue; } - } } @@ -903,16 +878,17 @@ bool ClassFlowPostProcessing::doFlow(string zwtime) NUMBERS[j]->FlowRateAct = (NUMBERS[j]->Value - NUMBERS[j]->PreValue) / difference; NUMBERS[j]->ReturnRateValue = to_string(NUMBERS[j]->FlowRateAct); - if (NUMBERS[j]->useMaxRateValue && PreValueUse && NUMBERS[j]->PreValueOkay) - { - double _ratedifference; - if (NUMBERS[j]->RateType == RateChange) + if (NUMBERS[j]->useMaxRateValue && PreValueUse && NUMBERS[j]->PreValueOkay) { + double _ratedifference; + + if (NUMBERS[j]->RateType == RateChange) { _ratedifference = NUMBERS[j]->FlowRateAct; - else + } + else { _ratedifference = (NUMBERS[j]->Value - NUMBERS[j]->PreValue); + } - if (abs(_ratedifference) > abs(NUMBERS[j]->MaxRateValue)) - { + if (abs(_ratedifference) > abs(NUMBERS[j]->MaxRateValue)) { NUMBERS[j]->ErrorMessageText = NUMBERS[j]->ErrorMessageText + "Rate too high - Read: " + RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->Nachkomma) + " - Pre: " + RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma) + " - Rate: " + RundeOutput(_ratedifference, NUMBERS[j]->Nachkomma); NUMBERS[j]->Value = NUMBERS[j]->PreValue; NUMBERS[j]->ReturnValue = ""; @@ -950,9 +926,8 @@ bool ClassFlowPostProcessing::doFlow(string zwtime) return true; } -void ClassFlowPostProcessing::WriteDataLog(int _index) -{ - if (!LogFile.GetDataLogToSD()){ +void ClassFlowPostProcessing::WriteDataLog(int _index) { + if (!LogFile.GetDataLogToSD()) { return; } @@ -964,10 +939,14 @@ void ClassFlowPostProcessing::WriteDataLog(int _index) strftime(buffer, 80, PREVALUE_TIME_FORMAT_OUTPUT, timeinfo); timezw = std::string(buffer); - if (flowAnalog) + if (flowAnalog) { analog = flowAnalog->getReadoutRawString(_index); - if (flowDigit) + } + + if (flowDigit) { digital = flowDigit->getReadoutRawString(_index); + } + LogFile.WriteToData(timezw, NUMBERS[_index]->name, NUMBERS[_index]->ReturnRawValue, NUMBERS[_index]->ReturnValue, NUMBERS[_index]->ReturnPreValue, NUMBERS[_index]->ReturnRateValue, NUMBERS[_index]->ReturnChangeAbsolute, @@ -976,78 +955,78 @@ void ClassFlowPostProcessing::WriteDataLog(int _index) ESP_LOGD(TAG, "WriteDataLog: %s, %s, %s, %s, %s", NUMBERS[_index]->ReturnRawValue.c_str(), NUMBERS[_index]->ReturnValue.c_str(), NUMBERS[_index]->ErrorMessageText.c_str(), digital.c_str(), analog.c_str()); } - -void ClassFlowPostProcessing::UpdateNachkommaDecimalShift() -{ - for (int j = 0; j < NUMBERS.size(); ++j) - { - if (NUMBERS[j]->digit_roi && !NUMBERS[j]->analog_roi) // There are only digital digits - { -// ESP_LOGD(TAG, "Nurdigital"); +void ClassFlowPostProcessing::UpdateNachkommaDecimalShift() { + for (int j = 0; j < NUMBERS.size(); ++j) { + // There are only digital digits + if (NUMBERS[j]->digit_roi && !NUMBERS[j]->analog_roi) { + // ESP_LOGD(TAG, "Nurdigital"); NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShiftInitial; - if (NUMBERS[j]->isExtendedResolution && flowDigit->isExtendedResolution()) // Extended resolution is on and should also be used for this digit. + // Extended resolution is on and should also be used for this digit. + if (NUMBERS[j]->isExtendedResolution && flowDigit->isExtendedResolution()) { NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShift-1; + } NUMBERS[j]->Nachkomma = -NUMBERS[j]->DecimalShift; } - if (!NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi) - { -// ESP_LOGD(TAG, "Nur analog"); + if (!NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi) { + // ESP_LOGD(TAG, "Nur analog"); NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShiftInitial; - if (NUMBERS[j]->isExtendedResolution && flowAnalog->isExtendedResolution()) + + if (NUMBERS[j]->isExtendedResolution && flowAnalog->isExtendedResolution()) { NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShift-1; + } NUMBERS[j]->Nachkomma = -NUMBERS[j]->DecimalShift; } - if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi) // digital + analog - { -// ESP_LOGD(TAG, "Nur digital + analog"); + // digital + analog + if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi) { + // ESP_LOGD(TAG, "Nur digital + analog"); NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShiftInitial; NUMBERS[j]->Nachkomma = NUMBERS[j]->analog_roi->ROI.size() - NUMBERS[j]->DecimalShift; - if (NUMBERS[j]->isExtendedResolution && flowAnalog->isExtendedResolution()) // Extended resolution is on and should also be used for this digit. + // Extended resolution is on and should also be used for this digit. + if (NUMBERS[j]->isExtendedResolution && flowAnalog->isExtendedResolution()) { NUMBERS[j]->Nachkomma = NUMBERS[j]->Nachkomma+1; - + } } ESP_LOGD(TAG, "UpdateNachkommaDecShift NUMBER%i: Nachkomma %i, DecShift %i", j, NUMBERS[j]->Nachkomma,NUMBERS[j]->DecimalShift); } } - -string ClassFlowPostProcessing::getReadout(int _number) -{ +string ClassFlowPostProcessing::getReadout(int _number) { return NUMBERS[_number]->ReturnValue; } -string ClassFlowPostProcessing::getReadoutParam(bool _rawValue, bool _noerror, int _number) -{ - if (_rawValue) +string ClassFlowPostProcessing::getReadoutParam(bool _rawValue, bool _noerror, int _number) { + if (_rawValue) { return NUMBERS[_number]->ReturnRawValue; - if (_noerror) + } + + if (_noerror) { return NUMBERS[_number]->ReturnValue; + } + return NUMBERS[_number]->ReturnValue; } - -string ClassFlowPostProcessing::ErsetzteN(string input, double _prevalue) -{ +string ClassFlowPostProcessing::ErsetzteN(string input, double _prevalue) { int posN, posPunkt; int pot, ziffer; float zw; posN = findDelimiterPos(input, "N"); posPunkt = findDelimiterPos(input, "."); - if (posPunkt == std::string::npos){ + + if (posPunkt == std::string::npos) { posPunkt = input.length(); } - while (posN != std::string::npos) - { + while (posN != std::string::npos) { if (posN < posPunkt) { pot = posPunkt - posN - 1; } @@ -1065,7 +1044,7 @@ string ClassFlowPostProcessing::ErsetzteN(string input, double _prevalue) return input; } -float ClassFlowPostProcessing::checkDigitConsistency(double input, int _decilamshift, bool _isanalog, double _preValue){ +float ClassFlowPostProcessing::checkDigitConsistency(double input, int _decilamshift, bool _isanalog, double _preValue) { int aktdigit, olddigit; int aktdigit_before, olddigit_before; int pot, pot_max; @@ -1073,16 +1052,19 @@ float ClassFlowPostProcessing::checkDigitConsistency(double input, int _decilams bool no_nulldurchgang = false; pot = _decilamshift; - if (!_isanalog) // if there are no analogue values, the last one cannot be evaluated - { + + // if there are no analogue values, the last one cannot be evaluated + if (!_isanalog) { pot++; } + #ifdef SERIAL_DEBUG ESP_LOGD(TAG, "checkDigitConsistency: pot=%d, decimalshift=%d", pot, _decilamshift); #endif + pot_max = ((int) log10(input)) + 1; - while (pot <= pot_max) - { + + while (pot <= pot_max) { zw = input / pow(10, pot-1); aktdigit_before = ((int) zw) % 10; zw = _preValue / pow(10, pot-1); @@ -1095,43 +1077,36 @@ float ClassFlowPostProcessing::checkDigitConsistency(double input, int _decilams no_nulldurchgang = (olddigit_before <= aktdigit_before); - if (no_nulldurchgang) - { - if (aktdigit != olddigit) - { + if (no_nulldurchgang) { + if (aktdigit != olddigit) { input = input + ((float) (olddigit - aktdigit)) * pow(10, pot); // New Digit is replaced by old Digit; } } - else - { - if (aktdigit == olddigit) // despite zero crossing, digit was not incremented --> add 1 - { + else { + // despite zero crossing, digit was not incremented --> add 1 + if (aktdigit == olddigit) { input = input + ((float) (1)) * pow(10, pot); // add 1 at the point } } + #ifdef SERIAL_DEBUG ESP_LOGD(TAG, "checkDigitConsistency: input=%f", input); #endif + pot++; } return input; } -string ClassFlowPostProcessing::getReadoutRate(int _number) -{ +string ClassFlowPostProcessing::getReadoutRate(int _number) { return std::to_string(NUMBERS[_number]->FlowRateAct); } -string ClassFlowPostProcessing::getReadoutTimeStamp(int _number) -{ +string ClassFlowPostProcessing::getReadoutTimeStamp(int _number) { return NUMBERS[_number]->timeStamp; } - -string ClassFlowPostProcessing::getReadoutError(int _number) -{ +string ClassFlowPostProcessing::getReadoutError(int _number) { return NUMBERS[_number]->ErrorMessageText; } - - diff --git a/code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp b/code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp index e4e11854..999296b1 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp @@ -1,9 +1,15 @@ +#include +#include +#include +#include + #include "ClassFlowTakeImage.h" #include "Helper.h" #include "ClassLogFile.h" #include "CImageBasis.h" #include "ClassControllCamera.h" +#include "MainFlowControl.h" #include "esp_wifi.h" #include "esp_log.h" @@ -12,14 +18,14 @@ #include -// #define DEBUG_DETAIL_ON - +// #define DEBUG_DETAIL_ON // #define WIFITURNOFF -static const char* TAG = "TAKEIMAGE"; +static const char *TAG = "TAKEIMAGE"; -esp_err_t ClassFlowTakeImage::camera_capture(){ - string nm = namerawimage; +esp_err_t ClassFlowTakeImage::camera_capture(void) +{ + string nm = namerawimage; Camera.CaptureToFile(nm); time(&TimeImageTaken); localtime(&TimeImageTaken); @@ -30,150 +36,480 @@ esp_err_t ClassFlowTakeImage::camera_capture(){ void ClassFlowTakeImage::takePictureWithFlash(int flash_duration) { // in case the image is flipped, it must be reset here // - rawImage->width = image_width; - rawImage->height = image_height; - ///////////////////////////////////////////////////////////////////////////////////// + rawImage->width = CCstatus.ImageWidth; + rawImage->height = CCstatus.ImageHeight; + ESP_LOGD(TAG, "flash_duration: %d", flash_duration); + Camera.CaptureToBasisImage(rawImage, flash_duration); + time(&TimeImageTaken); localtime(&TimeImageTaken); - if (SaveAllFiles) rawImage->SaveToFile(namerawimage); + if (CCstatus.SaveAllFiles) + { + rawImage->SaveToFile(namerawimage); + } } void ClassFlowTakeImage::SetInitialParameter(void) { - waitbeforepicture = 5; - isImageSize = false; - ImageQuality = -1; TimeImageTaken = 0; - ImageQuality = 5; rawImage = NULL; - ImageSize = FRAMESIZE_VGA; - SaveAllFiles = false; disabled = false; - FixedExposure = false; namerawimage = "/sdcard/img_tmp/raw.jpg"; -} +} +// auslesen der Kameraeinstellungen aus der config.ini +// wird beim Start aufgerufen +bool ClassFlowTakeImage::ReadParameter(FILE *pfile, string &aktparamgraph) +{ + Camera.getSensorDatenToCCstatus(); // Kamera >>> CCstatus -ClassFlowTakeImage::ClassFlowTakeImage(std::vector* lfc) : ClassFlowImage(lfc, TAG) + std::vector splitted; + + aktparamgraph = trim(aktparamgraph); + + if (aktparamgraph.size() == 0) + { + if (!this->GetNextParagraph(pfile, aktparamgraph)) + { + return false; + } + } + + if (aktparamgraph.compare("[TakeImage]") != 0) + { + // Paragraph does not fit TakeImage + return false; + } + + while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph)) + { + splitted = ZerlegeZeile(aktparamgraph); + + if ((toUpper(splitted[0]) == "RAWIMAGESLOCATION") && (splitted.size() > 1)) + { + imagesLocation = "/sdcard" + splitted[1]; + isLogImage = true; + } + + else if ((toUpper(splitted[0]) == "RAWIMAGESRETENTION") && (splitted.size() > 1)) + { + this->imagesRetention = std::stod(splitted[1]); + } + + else if ((toUpper(splitted[0]) == "SAVEALLFILES") && (splitted.size() > 1)) + { + if (toUpper(splitted[1]) == "TRUE") + { + CCstatus.SaveAllFiles = 1; + } + else + { + CCstatus.SaveAllFiles = 0; + } + } + + else if ((toUpper(splitted[0]) == "WAITBEFORETAKINGPICTURE") && (splitted.size() > 1)) + { + int _WaitBeforePicture = std::stoi(splitted[1]); + if (_WaitBeforePicture != 0) + { + CCstatus.WaitBeforePicture = _WaitBeforePicture; + } + else + { + CCstatus.WaitBeforePicture = 2; + } + } + + else if ((toUpper(splitted[0]) == "CAMGAINCEILING") && (splitted.size() > 1)) + { + std::string _ImageGainceiling = toUpper(splitted[1]); + if (_ImageGainceiling == "X4") + { + CCstatus.ImageGainceiling = GAINCEILING_4X; + } + else if (_ImageGainceiling == "X8") + { + CCstatus.ImageGainceiling = GAINCEILING_8X; + } + else if (_ImageGainceiling == "X16") + { + CCstatus.ImageGainceiling = GAINCEILING_16X; + } + else if (_ImageGainceiling == "X32") + { + CCstatus.ImageGainceiling = GAINCEILING_32X; + } + else if (_ImageGainceiling == "X64") + { + CCstatus.ImageGainceiling = GAINCEILING_64X; + } + else if (_ImageGainceiling == "X128") + { + CCstatus.ImageGainceiling = GAINCEILING_128X; + } + else + { + CCstatus.ImageGainceiling = GAINCEILING_2X; + } + } + + else if ((toUpper(splitted[0]) == "CAMQUALITY") && (splitted.size() > 1)) + { + int _ImageQuality = std::stoi(splitted[1]); + if ((_ImageQuality >= 0) && (_ImageQuality <= 63)) + { + CCstatus.ImageQuality = _ImageQuality; + } + } + + else if ((toUpper(splitted[0]) == "CAMBRIGHTNESS") && (splitted.size() > 1)) + { + int _ImageBrightness = std::stoi(splitted[1]); + if ((_ImageBrightness >= -2) && (_ImageBrightness <= 2)) + { + CCstatus.ImageBrightness = _ImageBrightness; + } + } + + else if ((toUpper(splitted[0]) == "CAMCONTRAST") && (splitted.size() > 1)) + { + int _ImageContrast = std::stoi(splitted[1]); + if ((_ImageContrast >= -2) && (_ImageContrast <= 2)) + { + CCstatus.ImageContrast = _ImageContrast; + } + } + + else if ((toUpper(splitted[0]) == "CAMSATURATION") && (splitted.size() > 1)) + { + int _ImageSaturation = std::stoi(splitted[1]); + if ((_ImageSaturation >= -2) && (_ImageSaturation <= 2)) + { + CCstatus.ImageSaturation = _ImageSaturation; + } + } + + else if ((toUpper(splitted[0]) == "CAMSHARPNESS") && (splitted.size() > 1)) + { + int _ImageSharpness = std::stoi(splitted[1]); + if ((_ImageSharpness >= -2) && (_ImageSharpness <= 2)) + { + CCstatus.ImageSharpness = _ImageSharpness; + } + } + + else if ((toUpper(splitted[0]) == "CAMAUTOSHARPNESS") && (splitted.size() > 1)) + { + if (toUpper(splitted[1]) == "TRUE") + { + CCstatus.ImageAutoSharpness = 1; + } + else + { + CCstatus.ImageAutoSharpness = 0; + } + } + + else if ((toUpper(splitted[0]) == "CAMSPECIALEFFECT") && (splitted.size() > 1)) + { + std::string _ImageSpecialEffect = toUpper(splitted[1]); + if (_ImageSpecialEffect == "NEGATIVE") + { + CCstatus.ImageSpecialEffect = 1; + } + else if (_ImageSpecialEffect == "GRAYSCALE") + { + CCstatus.ImageSpecialEffect = 2; + } + else if (_ImageSpecialEffect == "RED") + { + CCstatus.ImageSpecialEffect = 3; + } + else if (_ImageSpecialEffect == "GREEN") + { + CCstatus.ImageSpecialEffect = 4; + } + else if (_ImageSpecialEffect == "BLUE") + { + CCstatus.ImageSpecialEffect = 5; + } + else if (_ImageSpecialEffect == "RETRO") + { + CCstatus.ImageSpecialEffect = 6; + } + else + { + CCstatus.ImageSpecialEffect = 0; + } + } + + else if ((toUpper(splitted[0]) == "CAMWBMODE") && (splitted.size() > 1)) + { + std::string _ImageWbMode = toUpper(splitted[1]); + if (_ImageWbMode == "SUNNY") + { + CCstatus.ImageWbMode = 1; + } + else if (_ImageWbMode == "CLOUDY") + { + CCstatus.ImageWbMode = 2; + } + else if (_ImageWbMode == "OFFICE") + { + CCstatus.ImageWbMode = 3; + } + else if (_ImageWbMode == "HOME") + { + CCstatus.ImageWbMode = 4; + } + else + { + CCstatus.ImageWbMode = 0; + } + } + + else if ((toUpper(splitted[0]) == "CAMAWB") && (splitted.size() > 1)) + { + if (toUpper(splitted[1]) == "TRUE") + { + CCstatus.ImageAwb = 1; + } + else + { + CCstatus.ImageAwb = 0; + } + } + + else if ((toUpper(splitted[0]) == "CAMAWBGAIN") && (splitted.size() > 1)) + { + if (toUpper(splitted[1]) == "TRUE") + { + CCstatus.ImageAwbGain = 1; + } + else + { + CCstatus.ImageAwbGain = 0; + } + } + + else if ((toUpper(splitted[0]) == "CAMAEC") && (splitted.size() > 1)) + { + if (toUpper(splitted[1]) == "TRUE") + { + CCstatus.ImageAec = 1; + } + else + { + CCstatus.ImageAec = 0; + } + } + + else if ((toUpper(splitted[0]) == "CAMAEC2") && (splitted.size() > 1)) + { + if (toUpper(splitted[1]) == "TRUE") + { + CCstatus.ImageAec2 = 1; + } + else + { + CCstatus.ImageAec2 = 0; + } + } + + else if ((toUpper(splitted[0]) == "CAMAELEVEL") && (splitted.size() > 1)) + { + int _ImageAeLevel = std::stoi(splitted[1]); + if ((_ImageAeLevel >= -2) && (_ImageAeLevel <= 2)) + { + CCstatus.ImageAeLevel = _ImageAeLevel; + } + } + + else if ((toUpper(splitted[0]) == "CAMAECVALUE") && (splitted.size() > 1)) + { + int _ImageAecValue = std::stoi(splitted[1]); + if ((_ImageAecValue >= 0) && (_ImageAecValue <= 1200)) + { + CCstatus.ImageAecValue = _ImageAecValue; + } + } + + else if ((toUpper(splitted[0]) == "CAMAGC") && (splitted.size() > 1)) + { + if (toUpper(splitted[1]) == "TRUE") + { + CCstatus.ImageAgc = 1; + } + else + { + CCstatus.ImageAgc = 0; + } + } + + else if ((toUpper(splitted[0]) == "CAMAGCGAIN") && (splitted.size() > 1)) + { + int _ImageAgcGain = std::stoi(splitted[1]); + if ((_ImageAgcGain >= 0) && (_ImageAgcGain <= 30)) + { + CCstatus.ImageAgcGain = _ImageAgcGain; + } + } + + else if ((toUpper(splitted[0]) == "CAMBPC") && (splitted.size() > 1)) + { + if (toUpper(splitted[1]) == "TRUE") + { + CCstatus.ImageBpc = 1; + } + else + { + CCstatus.ImageBpc = 0; + } + } + + else if ((toUpper(splitted[0]) == "CAMWPC") && (splitted.size() > 1)) + { + if (toUpper(splitted[1]) == "TRUE") + { + CCstatus.ImageWpc = 1; + } + else + { + CCstatus.ImageWpc = 0; + } + } + + else if ((toUpper(splitted[0]) == "CAMRAWGMA") && (splitted.size() > 1)) + { + if (toUpper(splitted[1]) == "TRUE") + { + CCstatus.ImageRawGma = 1; + } + else + { + CCstatus.ImageRawGma = 0; + } + } + + else if ((toUpper(splitted[0]) == "CAMLENC") && (splitted.size() > 1)) + { + if (toUpper(splitted[1]) == "TRUE") + { + CCstatus.ImageLenc = 1; + } + else + { + CCstatus.ImageLenc = 0; + } + } + + else if ((toUpper(splitted[0]) == "CAMHMIRROR") && (splitted.size() > 1)) + { + if (toUpper(splitted[1]) == "TRUE") + { + CCstatus.ImageHmirror = 1; + } + else + { + CCstatus.ImageHmirror = 0; + } + } + + else if ((toUpper(splitted[0]) == "CAMVFLIP") && (splitted.size() > 1)) + { + if (toUpper(splitted[1]) == "TRUE") + { + CCstatus.ImageVflip = 1; + } + else + { + CCstatus.ImageVflip = 0; + } + } + + else if ((toUpper(splitted[0]) == "CAMDCW") && (splitted.size() > 1)) + { + if (toUpper(splitted[1]) == "TRUE") + { + CCstatus.ImageDcw = 1; + } + else + { + CCstatus.ImageDcw = 0; + } + } + + else if ((toUpper(splitted[0]) == "CAMZOOM") && (splitted.size() > 1)) + { + if (toUpper(splitted[1]) == "TRUE") + { + CCstatus.ImageZoomEnabled = 1; + } + else + { + CCstatus.ImageZoomEnabled = 0; + } + } + + else if ((toUpper(splitted[0]) == "CAMZOOMOFFSETX") && (splitted.size() > 1)) + { + CCstatus.ImageZoomOffsetX = std::stoi(splitted[1]); + } + + else if ((toUpper(splitted[0]) == "CAMZOOMOFFSETY") && (splitted.size() > 1)) + { + CCstatus.ImageZoomOffsetY = std::stoi(splitted[1]); + } + + else if ((toUpper(splitted[0]) == "CAMZOOMSIZE") && (splitted.size() > 1)) + { + int _ImageZoomSize = std::stoi(splitted[1]); + if (_ImageZoomSize >= 0) + { + CCstatus.ImageZoomSize = _ImageZoomSize; + } + } + + else if ((toUpper(splitted[0]) == "LEDINTENSITY") && (splitted.size() > 1)) + { + float ledintensity = std::stof(splitted[1]); + Camera.SetLEDIntensity(ledintensity); + } + + else if ((toUpper(splitted[0]) == "DEMO") && (splitted.size() > 1)) + { + if (toUpper(splitted[1]) == "TRUE") + { + CCstatus.DemoMode = true; + Camera.useDemoMode(); + } + else + { + CCstatus.DemoMode = false; + } + } + } + + Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera + Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize); + + rawImage = new CImageBasis("rawImage"); + rawImage->CreateEmptyImage(CCstatus.ImageWidth, CCstatus.ImageHeight, 3); + + return true; +} + +ClassFlowTakeImage::ClassFlowTakeImage(std::vector *lfc) : ClassFlowImage(lfc, TAG) { imagesLocation = "/log/source"; imagesRetention = 5; SetInitialParameter(); } - -bool ClassFlowTakeImage::ReadParameter(FILE* pfile, string& aktparamgraph) -{ - std::vector splitted; - - aktparamgraph = trim(aktparamgraph); - int _brightness = -100; - int _contrast = -100; - int _saturation = -100; - - if (aktparamgraph.size() == 0) - if (!this->GetNextParagraph(pfile, aktparamgraph)) - return false; - - if (aktparamgraph.compare("[TakeImage]") != 0) // Paragraph does not fit TakeImage - return false; - - while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph)) - { - splitted = ZerlegeZeile(aktparamgraph); - if ((toUpper(splitted[0]) == "RAWIMAGESLOCATION") && (splitted.size() > 1)) - { - imagesLocation = "/sdcard" + splitted[1]; - isLogImage = true; - } - if ((toUpper(splitted[0]) == "IMAGEQUALITY") && (splitted.size() > 1)) - ImageQuality = std::stod(splitted[1]); - - if ((toUpper(splitted[0]) == "IMAGESIZE") && (splitted.size() > 1)) - { - ImageSize = Camera.TextToFramesize(splitted[1].c_str()); - isImageSize = true; - } - - if ((toUpper(splitted[0]) == "SAVEALLFILES") && (splitted.size() > 1)) - { - if (toUpper(splitted[1]) == "TRUE") - SaveAllFiles = true; - } - - if ((toUpper(splitted[0]) == "WAITBEFORETAKINGPICTURE") && (splitted.size() > 1)) - { - waitbeforepicture = stoi(splitted[1]); - } - - if ((toUpper(splitted[0]) == "RAWIMAGESRETENTION") && (splitted.size() > 1)) - { - this->imagesRetention = std::stoi(splitted[1]); - } - - if ((toUpper(splitted[0]) == "BRIGHTNESS") && (splitted.size() > 1)) - { - _brightness = stoi(splitted[1]); - } - - if ((toUpper(splitted[0]) == "CONTRAST") && (splitted.size() > 1)) - { - _contrast = stoi(splitted[1]); - } - - if ((toUpper(splitted[0]) == "SATURATION") && (splitted.size() > 1)) - { - _saturation = stoi(splitted[1]); - } - - if ((toUpper(splitted[0]) == "FIXEDEXPOSURE") && (splitted.size() > 1)) - { - if (toUpper(splitted[1]) == "TRUE") - FixedExposure = true; - } - - if ((toUpper(splitted[0]) == "LEDINTENSITY") && (splitted.size() > 1)) - { - float ledintensity = stof(splitted[1]); - ledintensity = min((float) 100, ledintensity); - ledintensity = max((float) 0, ledintensity); - Camera.SetLEDIntensity(ledintensity); - } - - if ((toUpper(splitted[0]) == "DEMO") && (splitted.size() > 1)) - { - if (toUpper(splitted[1]) == "TRUE") - Camera.useDemoMode(); - } - } - - Camera.SetBrightnessContrastSaturation(_brightness, _contrast, _saturation); - Camera.SetQualitySize(ImageQuality, ImageSize); - - image_width = Camera.image_width; - image_height = Camera.image_height; - rawImage = new CImageBasis("rawImage"); - rawImage->CreateEmptyImage(image_width, image_height, 3); - - waitbeforepicture_store = waitbeforepicture; - if (FixedExposure && (waitbeforepicture > 0)) - { -// ESP_LOGD(TAG, "Fixed Exposure enabled!"); - int flash_duration = (int) (waitbeforepicture * 1000); - Camera.EnableAutoExposure(flash_duration); - waitbeforepicture = 0.2; -// flash_duration = (int) (waitbeforepicture * 1000); -// takePictureWithFlash(flash_duration); -// rawImage->SaveToFile("/sdcard/init2.jpg"); - } - - return true; -} - - string ClassFlowTakeImage::getHTMLSingleStep(string host) { string result; @@ -181,74 +517,78 @@ string ClassFlowTakeImage::getHTMLSingleStep(string host) return result; } - +// wird bei jeder Auswertrunde aufgerufen bool ClassFlowTakeImage::doFlow(string zwtime) { psram_init_shared_memory_for_take_image_step(); string logPath = CreateLogFolder(zwtime); - int flash_duration = (int) (waitbeforepicture * 1000); - - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - Before takePictureWithFlash"); - #endif + int flash_duration = (int)(CCstatus.WaitBeforePicture * 1000); +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - Before takePictureWithFlash"); +#endif - #ifdef WIFITURNOFF - esp_wifi_stop(); // to save power usage and - #endif +#ifdef WIFITURNOFF + esp_wifi_stop(); // to save power usage and +#endif + + // wenn die Kameraeinstellungen durch Erstellen eines neuen Referenzbildes verändert wurden, müssen sie neu gesetzt werden + if (CFstatus.changedCameraSettings) + { + Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera + Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize); + CFstatus.changedCameraSettings = false; + } takePictureWithFlash(flash_duration); - #ifdef WIFITURNOFF - esp_wifi_start(); - #endif +#ifdef WIFITURNOFF + esp_wifi_start(); +#endif - - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - After takePictureWithFlash"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - After takePictureWithFlash"); +#endif LogImage(logPath, "raw", NULL, NULL, zwtime, rawImage); RemoveOldLogs(); - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - After RemoveOldLogs"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - After RemoveOldLogs"); +#endif psram_deinit_shared_memory_for_take_image_step(); return true; } - esp_err_t ClassFlowTakeImage::SendRawJPG(httpd_req_t *req) { - int flash_duration = (int) (waitbeforepicture * 1000); + int flash_duration = (int)(CCstatus.WaitBeforePicture * 1000); time(&TimeImageTaken); localtime(&TimeImageTaken); return Camera.CaptureToHTTP(req, flash_duration); } - -ImageData* ClassFlowTakeImage::SendRawImage() +ImageData *ClassFlowTakeImage::SendRawImage(void) { CImageBasis *zw = new CImageBasis("SendRawImage", rawImage); ImageData *id; - int flash_duration = (int) (waitbeforepicture * 1000); + int flash_duration = (int)(CCstatus.WaitBeforePicture * 1000); Camera.CaptureToBasisImage(zw, flash_duration); time(&TimeImageTaken); localtime(&TimeImageTaken); - id = zw->writeToMemoryAsJPG(); + id = zw->writeToMemoryAsJPG(); delete zw; - return id; + return id; } -time_t ClassFlowTakeImage::getTimeImageTaken() +time_t ClassFlowTakeImage::getTimeImageTaken(void) { return TimeImageTaken; } @@ -257,4 +597,3 @@ ClassFlowTakeImage::~ClassFlowTakeImage(void) { delete rawImage; } - diff --git a/code/components/jomjol_flowcontroll/ClassFlowTakeImage.h b/code/components/jomjol_flowcontroll/ClassFlowTakeImage.h index 2b62e629..0be4f3af 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowTakeImage.h +++ b/code/components/jomjol_flowcontroll/ClassFlowTakeImage.h @@ -9,47 +9,32 @@ #include -class ClassFlowTakeImage : - public ClassFlowImage +class ClassFlowTakeImage : public ClassFlowImage { protected: - float waitbeforepicture; - float waitbeforepicture_store; - framesize_t ImageSize; - bool isImageSize; - int ImageQuality; time_t TimeImageTaken; string namerawimage; - int image_height, image_width; - bool SaveAllFiles; - bool FixedExposure; - - - void CopyFile(string input, string output); - - esp_err_t camera_capture(); + esp_err_t camera_capture(void); void takePictureWithFlash(int flash_duration); - - void SetInitialParameter(void); + void SetInitialParameter(void); public: CImageBasis *rawImage; - ClassFlowTakeImage(std::vector* lfc); + ClassFlowTakeImage(std::vector *lfc); - bool ReadParameter(FILE* pfile, string& aktparamgraph); + bool ReadParameter(FILE *pfile, string &aktparamgraph); bool doFlow(string time); string getHTMLSingleStep(string host); - time_t getTimeImageTaken(); - string name(){return "ClassFlowTakeImage";}; + time_t getTimeImageTaken(void); + string name() { return "ClassFlowTakeImage"; }; - ImageData* SendRawImage(); + ImageData *SendRawImage(void); esp_err_t SendRawJPG(httpd_req_t *req); ~ClassFlowTakeImage(void); }; - -#endif //CLASSFFLOWTAKEIMAGE_H \ No newline at end of file +#endif // CLASSFFLOWTAKEIMAGE_H \ No newline at end of file diff --git a/code/components/jomjol_flowcontroll/MainFlowControl.cpp b/code/components/jomjol_flowcontroll/MainFlowControl.cpp index 18f1f723..63eadac1 100644 --- a/code/components/jomjol_flowcontroll/MainFlowControl.cpp +++ b/code/components/jomjol_flowcontroll/MainFlowControl.cpp @@ -34,6 +34,7 @@ #endif ClassFlowControll flowctrl; +camera_flow_config_temp_t CFstatus; TaskHandle_t xHandletask_autodoFlow = NULL; @@ -48,177 +49,308 @@ bool isPlannedReboot = false; static const char *TAG = "MAINCTRL"; +// #define DEBUG_DETAIL_ON -//#define DEBUG_DETAIL_ON - - -void CheckIsPlannedReboot() +void CheckIsPlannedReboot(void) { - FILE *pfile; - if ((pfile = fopen("/sdcard/reboot.txt", "r")) == NULL) { - //LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Initial boot or not a planned reboot"); + FILE *pfile; + + if ((pfile = fopen("/sdcard/reboot.txt", "r")) == NULL) + { + // LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Initial boot or not a planned reboot"); isPlannedReboot = false; - } - else { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Planned reboot"); - DeleteFile("/sdcard/reboot.txt"); // Prevent Boot Loop!!! + } + else + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Planned reboot"); + DeleteFile("/sdcard/reboot.txt"); // Prevent Boot Loop!!! isPlannedReboot = true; - } + } } - -bool getIsPlannedReboot() +bool getIsPlannedReboot(void) { return isPlannedReboot; } - -int getCountFlowRounds() +int getCountFlowRounds(void) { return countRounds; } - esp_err_t GetJPG(std::string _filename, httpd_req_t *req) { return flowctrl.GetJPGStream(_filename, req); } - esp_err_t GetRawJPG(httpd_req_t *req) { return flowctrl.SendRawJPG(req); } - -bool isSetupModusActive() +bool isSetupModusActive(void) { return flowctrl.getStatusSetupModus(); } - -void DeleteMainFlowTask() +void DeleteMainFlowTask(void) { - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "DeleteMainFlowTask: xHandletask_autodoFlow: %ld", (long) xHandletask_autodoFlow); - #endif - if( xHandletask_autodoFlow != NULL ) +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "DeleteMainFlowTask: xHandletask_autodoFlow: %ld", (long)xHandletask_autodoFlow); +#endif + + if (xHandletask_autodoFlow != NULL) { vTaskDelete(xHandletask_autodoFlow); xHandletask_autodoFlow = NULL; } - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Killed: xHandletask_autodoFlow"); - #endif -} +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "Killed: xHandletask_autodoFlow"); +#endif +} void doInit(void) { - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Start flowctrl.InitFlow(config);"); - #endif +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "Start flowctrl.InitFlow(config);"); +#endif flowctrl.InitFlow(CONFIG_FILE); - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Finished flowctrl.InitFlow(config);"); - #endif - +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "Finished flowctrl.InitFlow(config);"); +#endif + /* GPIO handler has to be initialized before MQTT init to ensure proper topic subscription */ gpio_handler_init(); - #ifdef ENABLE_MQTT - flowctrl.StartMQTTService(); - #endif //ENABLE_MQTT +#ifdef ENABLE_MQTT + flowctrl.StartMQTTService(); +#endif // ENABLE_MQTT } - bool doflow(void) -{ +{ std::string zw_time = getCurrentTimeString(LOGFILE_TIME_FORMAT); ESP_LOGD(TAG, "doflow - start %s", zw_time.c_str()); flowisrunning = true; flowctrl.doFlow(zw_time); flowisrunning = false; - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "doflow - end %s", zw_time.c_str()); - #endif +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "doflow - end %s", zw_time.c_str()); +#endif return true; } +esp_err_t setCCstatusToCFstatus(void) +{ + CFstatus.ImageFrameSize = CCstatus.ImageFrameSize; + CFstatus.ImageGainceiling = CCstatus.ImageGainceiling; + + 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.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.ImageHmirror = CCstatus.ImageHmirror; + CFstatus.ImageVflip = CCstatus.ImageVflip; + CFstatus.ImageDcw = CCstatus.ImageDcw; + + CFstatus.ImageLedIntensity = CCstatus.ImageLedIntensity; + + CFstatus.ImageZoomEnabled = CCstatus.ImageZoomEnabled; + CFstatus.ImageZoomMode = CCstatus.ImageZoomMode; + CFstatus.ImageZoomOffsetX = CCstatus.ImageZoomOffsetX; + CFstatus.ImageZoomOffsetY = CCstatus.ImageZoomOffsetY; + CFstatus.ImageZoomSize = CCstatus.ImageZoomSize; + + CFstatus.WaitBeforePicture = CCstatus.WaitBeforePicture; + + return ESP_OK; +} + +esp_err_t setCFstatusToCCstatus(void) +{ + CCstatus.ImageFrameSize = CFstatus.ImageFrameSize; + CCstatus.ImageGainceiling = CFstatus.ImageGainceiling; + + 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.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.ImageHmirror = CFstatus.ImageHmirror; + CCstatus.ImageVflip = CFstatus.ImageVflip; + CCstatus.ImageDcw = CFstatus.ImageDcw; + + CCstatus.ImageLedIntensity = CFstatus.ImageLedIntensity; + + CCstatus.ImageZoomEnabled = CFstatus.ImageZoomEnabled; + CCstatus.ImageZoomMode = CFstatus.ImageZoomMode; + CCstatus.ImageZoomOffsetX = CFstatus.ImageZoomOffsetX; + CCstatus.ImageZoomOffsetY = CFstatus.ImageZoomOffsetY; + CCstatus.ImageZoomSize = CFstatus.ImageZoomSize; + + CCstatus.WaitBeforePicture = CFstatus.WaitBeforePicture; + + return ESP_OK; +} + +esp_err_t setCFstatusToCam(void) +{ + sensor_t *s = esp_camera_sensor_get(); + + if (s != NULL) + { + s->set_framesize(s, CFstatus.ImageFrameSize); + s->set_gainceiling(s, CFstatus.ImageGainceiling); + + 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_exposure_ctrl(s, CFstatus.ImageAec); // 0 = disable , 1 = enable + s->set_ae_level(s, CFstatus.ImageAeLevel); // -2 to 2 + s->set_aec_value(s, CFstatus.ImageAecValue); // 0 to 1200 + s->set_aec2(s, CFstatus.ImageAec2); // 0 = disable , 1 = enable + + s->set_gain_ctrl(s, CFstatus.ImageAgc); // 0 = disable , 1 = enable + s->set_agc_gain(s, CFstatus.ImageAgcGain); // 0 to 30 + + s->set_bpc(s, CFstatus.ImageBpc); // 0 = disable , 1 = enable + s->set_wpc(s, CFstatus.ImageWpc); // 0 = disable , 1 = enable + + s->set_raw_gma(s, CFstatus.ImageRawGma); // 0 = disable , 1 = enable + s->set_lenc(s, CFstatus.ImageLenc); // 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_dcw(s, CFstatus.ImageDcw); // 0 = disable , 1 = enable + + 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_awb_gain(s, CFstatus.ImageAwbGain); // 0 = disable , 1 = enable + s->set_whitebal(s, CFstatus.ImageAwb); // 0 = disable , 1 = enable + + // special_effect muß als Letztes gesetzt werden, sonst geht es nicht + 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) + + TickType_t xDelay2 = 1000 / portTICK_PERIOD_MS; + vTaskDelay(xDelay2); + + return ESP_OK; + } + else + { + return ESP_FAIL; + } +} esp_err_t handler_get_heap(httpd_req_t *req) { - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_get_heap - Start"); - ESP_LOGD(TAG, "handler_get_heap uri: %s", req->uri); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_get_heap - Start"); + ESP_LOGD(TAG, "handler_get_heap uri: %s", req->uri); +#endif std::string zw = "Heap info:
" + getESPHeapInfo(); - #ifdef TASK_ANALYSIS_ON - char* pcTaskList = (char*) calloc_psram_heap(std::string(TAG) + "->pcTaskList", 1, sizeof(char) * 768, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); - if (pcTaskList) { - vTaskList(pcTaskList); - zw = zw + "

Task info:

Name | State | Prio | Lowest stacksize | Creation order | CPU (-1=NoAffinity)
" - + std::string(pcTaskList) + "
"; - free_psram_heap(std::string(TAG) + "->pcTaskList", pcTaskList); - } - else { - zw = zw + "

Task info:
ERROR - Allocation of TaskList buffer in PSRAM failed"; - } - #endif +#ifdef TASK_ANALYSIS_ON + char *pcTaskList = (char *)calloc_psram_heap(std::string(TAG) + "->pcTaskList", 1, sizeof(char) * 768, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); + if (pcTaskList) + { + vTaskList(pcTaskList); + zw = zw + "

Task info:
Name | State | Prio | Lowest stacksize | Creation order | CPU (-1=NoAffinity)
" + std::string(pcTaskList) + "
"; + free_psram_heap(std::string(TAG) + "->pcTaskList", pcTaskList); + } + else + { + zw = zw + "

Task info:
ERROR - Allocation of TaskList buffer in PSRAM failed"; + } +#endif httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); - if (zw.length() > 0) + if (zw.length() > 0) { httpd_resp_send(req, zw.c_str(), zw.length()); } - else + else { httpd_resp_send(req, NULL, 0); } - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_get_heap - Done"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_get_heap - Done"); +#endif return ESP_OK; } - esp_err_t handler_init(httpd_req_t *req) { - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_init - Start"); - ESP_LOGD(TAG, "handler_doinit uri: %s", req->uri); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_init - Start"); + ESP_LOGD(TAG, "handler_doinit uri: %s", req->uri); +#endif httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); - const char* resp_str = "Init started
"; - httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN); + const char *resp_str = "Init started
"; + httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN); doInit(); resp_str = "Init done
"; - httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN); + httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN); - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_init - Done"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_init - Done"); +#endif return ESP_OK; } - esp_err_t handler_stream(httpd_req_t *req) { - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_stream - Start"); - ESP_LOGD(TAG, "handler_stream uri: %s", req->uri); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_stream - Start"); + ESP_LOGD(TAG, "handler_stream uri: %s", req->uri); +#endif char _query[50]; char _value[10]; @@ -226,129 +358,129 @@ esp_err_t handler_stream(httpd_req_t *req) if (httpd_req_get_url_query_str(req, _query, 50) == ESP_OK) { -// ESP_LOGD(TAG, "Query: %s", _query); + // ESP_LOGD(TAG, "Query: %s", _query); if (httpd_query_key_value(_query, "flashlight", _value, 10) == ESP_OK) { - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "flashlight is found%s", _value); - #endif +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "flashlight is found%s", _value); +#endif if (strlen(_value) > 0) + { flashlightOn = true; + } } } Camera.CaptureToStream(req, flashlightOn); - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_stream - Done"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_stream - Done"); +#endif return ESP_OK; } - -esp_err_t handler_flow_start(httpd_req_t *req) { - - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_flow_start - Start"); - #endif +esp_err_t handler_flow_start(httpd_req_t *req) +{ +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_flow_start - Start"); +#endif ESP_LOGD(TAG, "handler_flow_start uri: %s", req->uri); httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); - if (autostartIsEnabled) { + if (autostartIsEnabled) + { xTaskAbortDelay(xHandletask_autodoFlow); // Delay will be aborted if task is in blocked (waiting) state. If task is already running, no action LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Flow start triggered by REST API /flow_start"); - const char* resp_str = "The flow is going to be started immediately or is already running"; - httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN); + const char *resp_str = "The flow is going to be started immediately or is already running"; + httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN); } - else { + 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); + 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); } - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_flow_start - Done"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_flow_start - Done"); +#endif return ESP_OK; } - #ifdef ENABLE_MQTT -esp_err_t MQTTCtrlFlowStart(std::string _topic) { - - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("MQTTCtrlFlowStart - Start"); - #endif +esp_err_t MQTTCtrlFlowStart(std::string _topic) +{ +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("MQTTCtrlFlowStart - Start"); +#endif ESP_LOGD(TAG, "MQTTCtrlFlowStart: topic %s", _topic.c_str()); - if (autostartIsEnabled) + if (autostartIsEnabled) { xTaskAbortDelay(xHandletask_autodoFlow); // Delay will be aborted if task is in blocked (waiting) state. If task is already running, no action LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Flow start triggered by MQTT topic " + _topic); } - else + else { LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Flow start triggered by MQTT topic " + _topic + ", but flow is not active!"); - } + } - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("MQTTCtrlFlowStart - Done"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("MQTTCtrlFlowStart - Done"); +#endif return ESP_OK; } -#endif //ENABLE_MQTT - +#endif // ENABLE_MQTT esp_err_t handler_json(httpd_req_t *req) { - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_json - Start"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_json - Start"); +#endif ESP_LOGD(TAG, "handler_JSON uri: %s", req->uri); - - if (bTaskAutoFlowCreated) + + if (bTaskAutoFlowCreated) { httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); httpd_resp_set_type(req, "application/json"); std::string zw = flowctrl.getJSON(); - if (zw.length() > 0) + if (zw.length() > 0) { httpd_resp_send(req, zw.c_str(), zw.length()); } - else + else { httpd_resp_send(req, NULL, 0); } } - else + else { httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Flow not (yet) started: REST API /json not yet available!"); return ESP_ERR_NOT_FOUND; } - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_JSON - Done"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_JSON - Done"); +#endif return ESP_OK; } - esp_err_t handler_wasserzaehler(httpd_req_t *req) { - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler water counter - Start"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler water counter - Start"); +#endif - if (bTaskAutoFlowCreated) + if (bTaskAutoFlowCreated) { bool _rawValue = false; bool _noerror = false; @@ -356,45 +488,46 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req) std::string _type = "value"; string zw; - ESP_LOGD(TAG, "handler water counter uri: %s", req->uri); + ESP_LOGD(TAG, "handler water counter uri: %s", req->uri); char _query[100]; char _size[10]; if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK) { - // ESP_LOGD(TAG, "Query: %s", _query); + // ESP_LOGD(TAG, "Query: %s", _query); if (httpd_query_key_value(_query, "all", _size, 10) == ESP_OK) { - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "all is found%s", _size); - #endif +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "all is found%s", _size); +#endif _all = true; } if (httpd_query_key_value(_query, "type", _size, 10) == ESP_OK) { - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "all is found: %s", _size); - #endif +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "all is found: %s", _size); +#endif _type = std::string(_size); } if (httpd_query_key_value(_query, "rawvalue", _size, 10) == ESP_OK) { - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "rawvalue is found: %s", _size); - #endif +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "rawvalue is found: %s", _size); +#endif _rawValue = true; } + if (httpd_query_key_value(_query, "noerror", _size, 10) == ESP_OK) { - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "noerror is found: %s", _size); - #endif +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "noerror is found: %s", _size); +#endif _noerror = true; - } - } + } + } httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); @@ -403,35 +536,49 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req) httpd_resp_set_type(req, "text/plain"); ESP_LOGD(TAG, "TYPE: %s", _type.c_str()); int _intype = READOUT_TYPE_VALUE; - if (_type == "prevalue") - _intype = READOUT_TYPE_PREVALUE; - if (_type == "raw") - _intype = READOUT_TYPE_RAWVALUE; - if (_type == "error") - _intype = READOUT_TYPE_ERROR; + if (_type == "prevalue") + { + _intype = READOUT_TYPE_PREVALUE; + } + + if (_type == "raw") + { + _intype = READOUT_TYPE_RAWVALUE; + } + + if (_type == "error") + { + _intype = READOUT_TYPE_ERROR; + } zw = flowctrl.getReadoutAll(_intype); ESP_LOGD(TAG, "ZW: %s", zw.c_str()); + if (zw.length() > 0) - httpd_resp_send(req, zw.c_str(), zw.length()); - + { + httpd_resp_send(req, zw.c_str(), zw.length()); + } + return ESP_OK; } - std::string *status = flowctrl.getActStatus(); string query = std::string(_query); - // ESP_LOGD(TAG, "Query: %s, query.c_str()); + // ESP_LOGD(TAG, "Query: %s, query.c_str()); + if (query.find("full") != std::string::npos) { string txt; txt = ""; - if ((countRounds <= 1) && (*status != std::string("Flow finished"))) { // First round not completed yet + if ((countRounds <= 1) && (*status != std::string("Flow finished"))) + { + // First round not completed yet txt += "

Please wait for the first round to complete!

Current state: " + *status + "

\n"; } - else { + else + { txt += "

Value

"; } @@ -439,36 +586,45 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req) } zw = flowctrl.getReadout(_rawValue, _noerror, 0); - if (zw.length() > 0) - httpd_resp_sendstr_chunk(req, zw.c_str()); + if (zw.length() > 0) + { + httpd_resp_sendstr_chunk(req, zw.c_str()); + } if (query.find("full") != std::string::npos) { string txt, zw; - if ((countRounds <= 1) && (*status != std::string("Flow finished"))) { // First round not completed yet + if ((countRounds <= 1) && (*status != std::string("Flow finished"))) + { + // First round not completed yet // Nothing to do } - else { + else + { /* Digital ROIs */ txt = ""; txt += "

Recognized Digit ROIs (previous round)

\n"; txt += "\n"; - std::vector htmlinfodig; - htmlinfodig = flowctrl.GetAllDigital(); + std::vector htmlinfodig; + htmlinfodig = flowctrl.GetAllDigital(); for (int i = 0; i < htmlinfodig.size(); ++i) { if (flowctrl.GetTypeDigital() == Digital) { - if (htmlinfodig[i]->val == 10) + if (htmlinfodig[i]->val >= 10) + { zw = "NaN"; + } else - zw = to_string((int) htmlinfodig[i]->val); + { + zw = to_string((int)htmlinfodig[i]->val); + } - txt += "\n"; + txt += "\n"; } else { @@ -476,75 +632,87 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req) stream << std::fixed << std::setprecision(1) << htmlinfodig[i]->val; zw = stream.str(); - txt += "\n"; + if (std::stod(zw) >= 10) + { + zw = "NaN"; + } + + txt += "\n"; } delete htmlinfodig[i]; } htmlinfodig.clear(); - - txt += "

" + zw + "

filename + "\">

" + zw + "

filename + "\">

" + zw + "

filename + "\">

" + zw + "

filename + "\">

\n"; - httpd_resp_sendstr_chunk(req, txt.c_str()); + txt += "\n"; + httpd_resp_sendstr_chunk(req, txt.c_str()); /* Analog ROIs */ txt = "

Recognized Analog ROIs (previous round)

\n"; txt += "\n"; - - std::vector htmlinfoana; + + std::vector htmlinfoana; htmlinfoana = flowctrl.GetAllAnalog(); + for (int i = 0; i < htmlinfoana.size(); ++i) { std::stringstream stream; stream << std::fixed << std::setprecision(1) << htmlinfoana[i]->val; zw = stream.str(); - txt += "\n"; - delete htmlinfoana[i]; + if (std::stod(zw) >= 10) + { + zw = "NaN"; + } + + txt += "\n"; + delete htmlinfoana[i]; } - htmlinfoana.clear(); + + htmlinfoana.clear(); txt += "\n

" + zw + "

filename + "\">

" + zw + "

filename + "\">

\n"; - httpd_resp_sendstr_chunk(req, txt.c_str()); + httpd_resp_sendstr_chunk(req, txt.c_str()); - - /* Full Image + /* Full Image * Only show it after the image got taken and aligned */ txt = "

Aligned Image (current round)

\n"; - if ((*status == std::string("Initialization")) || - (*status == std::string("Initialization (delayed)")) || - (*status == std::string("Take Image"))) { + + if ((*status == std::string("Initialization")) || + (*status == std::string("Initialization (delayed)")) || + (*status == std::string("Take Image"))) + { txt += "

Current state: " + *status + "

\n"; } - else { + else + { txt += "\n"; } - httpd_resp_sendstr_chunk(req, txt.c_str()); + httpd_resp_sendstr_chunk(req, txt.c_str()); } - } + } /* Respond with an empty chunk to signal HTTP response completion */ - httpd_resp_sendstr_chunk(req, NULL); + httpd_resp_sendstr_chunk(req, NULL); } - else + else { httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Flow not (yet) started: REST API /value not available!"); return ESP_ERR_NOT_FOUND; } - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_wasserzaehler - Done"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_wasserzaehler - Done"); +#endif return ESP_OK; } - esp_err_t handler_editflow(httpd_req_t *req) { - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_editflow - Start"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_editflow - Start"); +#endif ESP_LOGD(TAG, "handler_editflow uri: %s", req->uri); @@ -556,12 +724,12 @@ esp_err_t handler_editflow(httpd_req_t *req) { if (httpd_query_key_value(_query, "task", _valuechar, 30) == ESP_OK) { - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "task is found: %s", _valuechar); - #endif +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "task is found: %s", _valuechar); +#endif _task = string(_valuechar); } - } + } if (_task.compare("namenumbers") == 0) { @@ -581,20 +749,19 @@ esp_err_t handler_editflow(httpd_req_t *req) return get_tflite_file_handler(req); } - if (_task.compare("copy") == 0) { string in, out, zw; httpd_query_key_value(_query, "in", _valuechar, 30); in = string(_valuechar); - httpd_query_key_value(_query, "out", _valuechar, 30); - out = string(_valuechar); + httpd_query_key_value(_query, "out", _valuechar, 30); + out = string(_valuechar); - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "in: %s", in.c_str()); - ESP_LOGD(TAG, "out: %s", out.c_str()); - #endif +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "in: %s", in.c_str()); + ESP_LOGD(TAG, "out: %s", out.c_str()); +#endif in = "/sdcard" + in; out = "/sdcard" + out; @@ -602,10 +769,9 @@ esp_err_t handler_editflow(httpd_req_t *req) CopyFile(in, out); zw = "Copy Done"; httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); - httpd_resp_send(req, zw.c_str(), zw.length()); + httpd_resp_send(req, zw.c_str(), zw.length()); } - if (_task.compare("cutref") == 0) { string in, out, zw; @@ -615,38 +781,39 @@ esp_err_t handler_editflow(httpd_req_t *req) httpd_query_key_value(_query, "in", _valuechar, 30); in = string(_valuechar); - httpd_query_key_value(_query, "out", _valuechar, 30); - out = string(_valuechar); + httpd_query_key_value(_query, "out", _valuechar, 30); + out = string(_valuechar); httpd_query_key_value(_query, "x", _valuechar, 30); - zw = string(_valuechar); - x = stoi(zw); + string _x = string(_valuechar); + x = stoi(_x); httpd_query_key_value(_query, "y", _valuechar, 30); - zw = string(_valuechar); - y = stoi(zw); + string _y = string(_valuechar); + y = stoi(_y); httpd_query_key_value(_query, "dx", _valuechar, 30); - zw = string(_valuechar); - dx = stoi(zw); + string _dx = string(_valuechar); + dx = stoi(_dx); httpd_query_key_value(_query, "dy", _valuechar, 30); - zw = string(_valuechar); - dy = stoi(zw); + string _dy = string(_valuechar); + dy = stoi(_dy); - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "in: %s", in.c_str()); - ESP_LOGD(TAG, "out: %s", out.c_str()); - ESP_LOGD(TAG, "x: %s", zw.c_str()); - ESP_LOGD(TAG, "y: %s", zw.c_str()); - ESP_LOGD(TAG, "dx: %s", zw.c_str()); - ESP_LOGD(TAG, "dy: %s", zw.c_str()); - #endif +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "in: %s", in.c_str()); + ESP_LOGD(TAG, "out: %s", out.c_str()); + ESP_LOGD(TAG, "x: %s", _x.c_str()); + ESP_LOGD(TAG, "y: %s", _y.c_str()); + ESP_LOGD(TAG, "dx: %s", _dx.c_str()); + ESP_LOGD(TAG, "dy: %s", _dy.c_str()); +#endif if (httpd_query_key_value(_query, "enhance", _valuechar, 10) == ESP_OK) { - zw = string(_valuechar); - if (zw.compare("true") == 0) + string _enhance = string(_valuechar); + + if (_enhance.compare("true") == 0) { enhance = true; } @@ -657,209 +824,507 @@ esp_err_t handler_editflow(httpd_req_t *req) string out2 = out.substr(0, out.length() - 4) + "_org.jpg"; - if ((flowctrl.SetupModeActive || (*flowctrl.getActStatus() == "Flow finished")) && psram_init_shared_memory_for_take_image_step()) { + if ((flowctrl.SetupModeActive || (*flowctrl.getActStatus() == std::string("Flow finished"))) && psram_init_shared_memory_for_take_image_step()) + { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Taking image for Alignment Mark Update..."); CAlignAndCutImage *caic = new CAlignAndCutImage("cutref", in); caic->CutAndSave(out2, x, y, dx, dy); - delete caic; + delete caic; CImageBasis *cim = new CImageBasis("cutref", out2); + if (enhance) { cim->Contrast(90); } cim->SaveToFile(out); - delete cim; + delete cim; psram_deinit_shared_memory_for_take_image_step(); - - zw = "CutImage Done"; } - else { - LogFile.WriteToFile(ESP_LOG_WARN, TAG, std::string("Taking image for Alignment Mark not possible while device") + - " is busy with a round (Current State: '" + *flowctrl.getActStatus() + "')!"); + else + { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, std::string("Taking image for Alignment Mark not possible while device") + " is busy with a round (Current State: '" + *flowctrl.getActStatus() + "')!"); zw = "Device Busy"; } httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); - httpd_resp_send(req, zw.c_str(), zw.length()); - + httpd_resp_send(req, zw.c_str(), zw.length()); } - if (_task.compare("test_take") == 0) + // wird beim Erstellen eines neuen Referenzbildes aufgerufen + std::string *sys_status = flowctrl.getActStatus(); + + if ((sys_status->c_str() != std::string("Take Image")) && (sys_status->c_str() != std::string("Aligning"))) { - std::string _host = ""; - std::string _bri = ""; - std::string _con = ""; - std::string _sat = ""; - std::string _int = ""; - int bri = -100; - int sat = -100; - int con = -100; - int intens = -100; + if ((_task.compare("test_take") == 0) || (_task.compare("cam_settings") == 0)) + { + std::string _host = ""; - if (httpd_query_key_value(_query, "host", _valuechar, 30) == ESP_OK) { - _host = std::string(_valuechar); - } - if (httpd_query_key_value(_query, "int", _valuechar, 30) == ESP_OK) { - _int = std::string(_valuechar); - intens = stoi(_int); - } - if (httpd_query_key_value(_query, "bri", _valuechar, 30) == ESP_OK) { - _bri = std::string(_valuechar); - bri = stoi(_bri); - } - if (httpd_query_key_value(_query, "con", _valuechar, 30) == ESP_OK) { - _con = std::string(_valuechar); - con = stoi(_con); - } - if (httpd_query_key_value(_query, "sat", _valuechar, 30) == ESP_OK) { - _sat = std::string(_valuechar); - sat = stoi(_sat); + // laden der aktuellen Kameraeinstellungen(CCstatus) in den Zwischenspeicher(CFstatus) + setCCstatusToCFstatus(); // CCstatus >>> CFstatus + + if (httpd_query_key_value(_query, "host", _valuechar, 30) == ESP_OK) + { + _host = std::string(_valuechar); + } + + if (httpd_query_key_value(_query, "waitb", _valuechar, 30) == ESP_OK) + { + int _waitb = std::stoi(_valuechar); + if (_waitb != 0) + { + CFstatus.WaitBeforePicture = _waitb; + } + } + + if (httpd_query_key_value(_query, "qual", _valuechar, 30) == ESP_OK) + { + int _qual = std::stoi(_valuechar); + if ((_qual >= 0) && (_qual <= 63)) + { + CFstatus.ImageQuality = _qual; + } + } + + if (httpd_query_key_value(_query, "bri", _valuechar, 30) == ESP_OK) + { + int _bri = std::stoi(_valuechar); + if ((_bri >= -2) && (_bri <= 2)) + { + CFstatus.ImageBrightness = _bri; + } + } + + if (httpd_query_key_value(_query, "con", _valuechar, 30) == ESP_OK) + { + int _con = std::stoi(_valuechar); + if ((_con >= -2) && (_con <= 2)) + { + CFstatus.ImageContrast = _con; + } + } + + if (httpd_query_key_value(_query, "sat", _valuechar, 30) == ESP_OK) + { + int _sat = std::stoi(_valuechar); + if ((_sat >= -2) && (_sat <= 2)) + { + CFstatus.ImageSaturation = _sat; + } + } + + if (httpd_query_key_value(_query, "shp", _valuechar, 30) == ESP_OK) + { + int _shp = std::stoi(_valuechar); + if ((_shp >= -2) && (_shp <= 2)) + { + CFstatus.ImageSharpness = _shp; + } + } + + if (httpd_query_key_value(_query, "ashp", _valuechar, 30) == ESP_OK) + { + if (std::stoi(_valuechar) != 0) + { + CFstatus.ImageAutoSharpness = 1; + } + else + { + CFstatus.ImageAutoSharpness = 0; + } + } + + if (httpd_query_key_value(_query, "spe", _valuechar, 30) == ESP_OK) + { + int _spe = std::stoi(_valuechar); + if ((_spe >= 0) && (_spe <= 6)) + { + CFstatus.ImageSpecialEffect = _spe; + } + } + + if (httpd_query_key_value(_query, "wbm", _valuechar, 30) == ESP_OK) + { + int _wbm = std::stoi(_valuechar); + if ((_wbm >= 0) && (_wbm <= 4)) + { + CFstatus.ImageWbMode = _wbm; + } + } + + if (httpd_query_key_value(_query, "awb", _valuechar, 30) == ESP_OK) + { + if (std::stoi(_valuechar) != 0) + { + CFstatus.ImageAwb = 1; + } + else + { + CFstatus.ImageAwb = 0; + } + } + + if (httpd_query_key_value(_query, "awbg", _valuechar, 30) == ESP_OK) + { + if (std::stoi(_valuechar) != 0) + { + CFstatus.ImageAwbGain = 1; + } + else + { + CFstatus.ImageAwbGain = 0; + } + } + + if (httpd_query_key_value(_query, "aec", _valuechar, 30) == ESP_OK) + { + if (std::stoi(_valuechar) != 0) + { + CFstatus.ImageAec = 1; + } + else + { + CFstatus.ImageAec = 0; + } + } + + if (httpd_query_key_value(_query, "aec2", _valuechar, 30) == ESP_OK) + { + if (std::stoi(_valuechar) != 0) + { + CFstatus.ImageAec2 = 1; + } + else + { + CFstatus.ImageAec2 = 0; + } + } + + if (httpd_query_key_value(_query, "ael", _valuechar, 30) == ESP_OK) + { + int _ael = std::stoi(_valuechar); + if ((_ael >= -2) && (_ael <= 2)) + { + CFstatus.ImageAeLevel = _ael; + } + } + + if (httpd_query_key_value(_query, "aecv", _valuechar, 30) == ESP_OK) + { + int _aecv = std::stoi(_valuechar); + if ((_aecv >= 0) && (_aecv <= 1200)) + { + CFstatus.ImageAecValue = _aecv; + } + } + + if (httpd_query_key_value(_query, "agc", _valuechar, 30) == ESP_OK) + { + if (std::stoi(_valuechar) != 0) + { + CFstatus.ImageAgc = 1; + } + else + { + CFstatus.ImageAgc = 0; + } + } + + if (httpd_query_key_value(_query, "agcg", _valuechar, 30) == ESP_OK) + { + int _agcg = std::stoi(_valuechar); + if ((_agcg >= 0) && (_agcg <= 30)) + { + CFstatus.ImageAgcGain = _agcg; + } + } + + if (httpd_query_key_value(_query, "bpc", _valuechar, 30) == ESP_OK) + { + if (std::stoi(_valuechar) != 0) + { + CFstatus.ImageBpc = 1; + } + else + { + CFstatus.ImageBpc = 0; + } + } + + if (httpd_query_key_value(_query, "wpc", _valuechar, 30) == ESP_OK) + { + if (std::stoi(_valuechar) != 0) + { + CFstatus.ImageWpc = 1; + } + else + { + CFstatus.ImageWpc = 0; + } + } + + if (httpd_query_key_value(_query, "rgma", _valuechar, 30) == ESP_OK) + { + if (std::stoi(_valuechar) != 0) + { + CFstatus.ImageRawGma = 1; + } + else + { + CFstatus.ImageRawGma = 0; + } + } + + if (httpd_query_key_value(_query, "lenc", _valuechar, 30) == ESP_OK) + { + if (std::stoi(_valuechar) != 0) + { + CFstatus.ImageLenc = 1; + } + else + { + CFstatus.ImageLenc = 0; + } + } + + if (httpd_query_key_value(_query, "mirror", _valuechar, 30) == ESP_OK) + { + if (std::stoi(_valuechar) != 0) + { + CFstatus.ImageHmirror = 1; + } + else + { + CFstatus.ImageHmirror = 0; + } + } + + if (httpd_query_key_value(_query, "flip", _valuechar, 30) == ESP_OK) + { + if (std::stoi(_valuechar) != 0) + { + CFstatus.ImageVflip = 1; + } + else + { + CFstatus.ImageVflip = 0; + } + } + + if (httpd_query_key_value(_query, "dcw", _valuechar, 30) == ESP_OK) + { + if (std::stoi(_valuechar) != 0) + { + CFstatus.ImageDcw = 1; + } + else + { + CFstatus.ImageDcw = 0; + } + } + + if (httpd_query_key_value(_query, "zoom", _valuechar, 30) == ESP_OK) + { + if (std::stoi(_valuechar) != 0) + { + CFstatus.ImageZoomEnabled = 1; + } + else + { + CFstatus.ImageZoomEnabled = 0; + } + } + + if (httpd_query_key_value(_query, "zoomx", _valuechar, 30) == ESP_OK) + { + CFstatus.ImageZoomOffsetX = std::stoi(_valuechar); + } + + if (httpd_query_key_value(_query, "zoomy", _valuechar, 30) == ESP_OK) + { + CFstatus.ImageZoomOffsetY = std::stoi(_valuechar); + } + + if (httpd_query_key_value(_query, "zooms", _valuechar, 30) == ESP_OK) + { + int _ImageZoomSize = std::stoi(_valuechar); + if (_ImageZoomSize >= 0) + { + CFstatus.ImageZoomSize = _ImageZoomSize; + } + } + + if (httpd_query_key_value(_query, "ledi", _valuechar, 30) == ESP_OK) + { + float _ImageLedIntensity = std::stof(_valuechar); + Camera.SetLEDIntensity(_ImageLedIntensity); + CFstatus.ImageLedIntensity = CCstatus.ImageLedIntensity; + } + + if (_task.compare("cam_settings") == 0) + { + // wird aufgerufen, wenn das Referenzbild + Kameraeinstellungen gespeichert wurden + setCFstatusToCCstatus(); // CFstatus >>> CCstatus + + // Kameraeinstellungen wurden verädert + CFstatus.changedCameraSettings = true; + + ESP_LOGD(TAG, "Cam Settings set"); + std::string _zw = "CamSettingsSet"; + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + httpd_resp_send(req, _zw.c_str(), _zw.length()); + } + else + { + // wird aufgerufen, wenn ein neues Referenzbild erstellt oder aktualisiert wurde + // CFstatus >>> Kamera + setCFstatusToCam(); + + Camera.SetQualityZoomSize(CFstatus.ImageQuality, CFstatus.ImageFrameSize, CFstatus.ImageZoomEnabled, CFstatus.ImageZoomOffsetX, CFstatus.ImageZoomOffsetY, CFstatus.ImageZoomSize); + // Camera.SetZoomSize(CFstatus.ImageZoomEnabled, CFstatus.ImageZoomOffsetX, CFstatus.ImageZoomOffsetY, CFstatus.ImageZoomSize); + + // Kameraeinstellungen wurden verädert + CFstatus.changedCameraSettings = true; + + ESP_LOGD(TAG, "test_take - vor TakeImage"); + std::string image_temp = flowctrl.doSingleStep("[TakeImage]", _host); + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + httpd_resp_send(req, image_temp.c_str(), image_temp.length()); + } } + if (_task.compare("test_align") == 0) + { + std::string _host = ""; -// ESP_LOGD(TAG, "Parameter host: %s", _host.c_str()); -// string zwzw = "Do " + _task + " start\n"; ESP_LOGD(TAG, zwzw.c_str()); - Camera.SetBrightnessContrastSaturation(bri, con, sat); - Camera.SetLEDIntensity(intens); - ESP_LOGD(TAG, "test_take - vor TakeImage"); - std::string zw = flowctrl.doSingleStep("[TakeImage]", _host); - httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); - httpd_resp_send(req, zw.c_str(), zw.length()); - } + if (httpd_query_key_value(_query, "host", _valuechar, 30) == ESP_OK) + { + _host = std::string(_valuechar); + } - - if (_task.compare("test_align") == 0) + std::string zw = flowctrl.doSingleStep("[Alignment]", _host); + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + httpd_resp_send(req, zw.c_str(), zw.length()); + } + } + else { - std::string _host = ""; - if (httpd_query_key_value(_query, "host", _valuechar, 30) == ESP_OK) { - _host = std::string(_valuechar); - } -// ESP_LOGD(TAG, "Parameter host: %s", _host.c_str()); - -// string zwzw = "Do " + _task + " start\n"; ESP_LOGD(TAG, zwzw.c_str()); - std::string zw = flowctrl.doSingleStep("[Alignment]", _host); + std::string _zw = "DeviceIsBusy"; httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); - httpd_resp_send(req, zw.c_str(), zw.length()); + httpd_resp_send(req, _zw.c_str(), _zw.length()); } - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_editflow - Done"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_editflow - Done"); +#endif return ESP_OK; } - esp_err_t handler_statusflow(httpd_req_t *req) { - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_statusflow - Start"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_statusflow - Start"); +#endif - const char* resp_str; + const char *resp_str; httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); - if (bTaskAutoFlowCreated) + if (bTaskAutoFlowCreated) { - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "handler_statusflow: %s", req->uri); - #endif +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "handler_statusflow: %s", req->uri); +#endif - string* zw = flowctrl.getActStatusWithTime(); + string *zw = flowctrl.getActStatusWithTime(); resp_str = zw->c_str(); - httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN); + httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN); } - else + else { resp_str = "Flow task not yet created"; - httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN); + httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN); } - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_statusflow - Done"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_statusflow - Done"); +#endif return ESP_OK; } - esp_err_t handler_cputemp(httpd_req_t *req) { - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_cputemp - Start"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_cputemp - Start"); +#endif httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); httpd_resp_send(req, std::to_string((int)temperatureRead()).c_str(), HTTPD_RESP_USE_STRLEN); - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_cputemp - End"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_cputemp - End"); +#endif return ESP_OK; } - esp_err_t handler_rssi(httpd_req_t *req) { - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_rssi - Start"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_rssi - Start"); +#endif - if (getWIFIisConnected()) + if (getWIFIisConnected()) { httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); httpd_resp_send(req, std::to_string(get_WIFI_RSSI()).c_str(), HTTPD_RESP_USE_STRLEN); } - else + else { httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "WIFI not (yet) connected: REST API /rssi not available!"); return ESP_ERR_NOT_FOUND; - } + } - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_rssi - End"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_rssi - End"); +#endif return ESP_OK; } - esp_err_t handler_uptime(httpd_req_t *req) { +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_uptime - Start"); +#endif - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_uptime - Start"); - #endif - std::string formatedUptime = getFormatedUptime(false); httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); - httpd_resp_send(req, formatedUptime.c_str(), formatedUptime.length()); + httpd_resp_send(req, formatedUptime.c_str(), formatedUptime.length()); - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_uptime - End"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_uptime - End"); +#endif return ESP_OK; } - esp_err_t handler_prevalue(httpd_req_t *req) { - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_prevalue - Start"); - ESP_LOGD(TAG, "handler_prevalue: %s", req->uri); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_prevalue - Start"); + ESP_LOGD(TAG, "handler_prevalue: %s", req->uri); +#endif // Default usage message when handler gets called without any parameter - const std::string RESTUsageInfo = + const std::string RESTUsageInfo = "00: Handler usage:
" "- To retrieve actual PreValue, please provide only a numbersname, e.g. /setPreValue?numbers=main
" "- To set PreValue to a new value, please provide a numbersname and a value, e.g. /setPreValue?numbers=main&value=1234.5678
" @@ -876,68 +1341,79 @@ esp_err_t handler_prevalue(httpd_req_t *req) httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); - if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK) { - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Query: %s", _query); - #endif + if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK) + { +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "Query: %s", _query); +#endif - if (httpd_query_key_value(_query, "numbers", _numbersname, 50) != ESP_OK) { // If request is incomplete + if (httpd_query_key_value(_query, "numbers", _numbersname, 50) != ESP_OK) + { + // If request is incomplete sReturnMessage = "E91: Query parameter incomplete or not valid!
" "Call /setPreValue to show REST API usage info and/or check documentation"; httpd_resp_send(req, sReturnMessage.c_str(), sReturnMessage.length()); - return ESP_FAIL; + return ESP_FAIL; } - if (httpd_query_key_value(_query, "value", _value, 20) == ESP_OK) { - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Value: %s", _value); - #endif + if (httpd_query_key_value(_query, "value", _value, 20) == ESP_OK) + { +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "Value: %s", _value); +#endif } } - else { // if no parameter is provided, print handler usage + else + { + // if no parameter is provided, print handler usage httpd_resp_send(req, RESTUsageInfo.c_str(), RESTUsageInfo.length()); - return ESP_OK; - } + return ESP_OK; + } - if (strlen(_value) == 0) { // If no value is povided --> return actual PreValue + if (strlen(_value) == 0) + { + // If no value is povided --> return actual PreValue sReturnMessage = flowctrl.GetPrevalue(std::string(_numbersname)); - if (sReturnMessage.empty()) { + if (sReturnMessage.empty()) + { sReturnMessage = "E92: Numbers name not found"; httpd_resp_send(req, sReturnMessage.c_str(), sReturnMessage.length()); return ESP_FAIL; } } - else { + else + { // New value is positive: Set PreValue to provided value and return value // New value is negative and actual RAW value is a valid number: Set PreValue to RAW value and return value - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "REST API handler_prevalue called: numbersname: " + std::string(_numbersname) + - ", value: " + std::string(_value)); - if (!flowctrl.UpdatePrevalue(_value, _numbersname, true)) { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "REST API handler_prevalue called: numbersname: " + std::string(_numbersname) + ", value: " + std::string(_value)); + + if (!flowctrl.UpdatePrevalue(_value, _numbersname, true)) + { sReturnMessage = "E93: Update request rejected. Please check device logs for more details"; - httpd_resp_send(req, sReturnMessage.c_str(), sReturnMessage.length()); + httpd_resp_send(req, sReturnMessage.c_str(), sReturnMessage.length()); return ESP_FAIL; } sReturnMessage = flowctrl.GetPrevalue(std::string(_numbersname)); - if (sReturnMessage.empty()) { + if (sReturnMessage.empty()) + { sReturnMessage = "E94: Numbers name not found"; httpd_resp_send(req, sReturnMessage.c_str(), sReturnMessage.length()); return ESP_FAIL; } } - httpd_resp_send(req, sReturnMessage.c_str(), sReturnMessage.length()); + httpd_resp_send(req, sReturnMessage.c_str(), sReturnMessage.length()); - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_prevalue - End"); - #endif +#ifdef DEBUG_DETAIL_ON + LogFile.WriteHeapInfo("handler_prevalue - End"); +#endif return ESP_OK; } - void task_autodoFlow(void *pvParameter) { int64_t fr_start, fr_delta_ms; @@ -947,10 +1423,10 @@ void task_autodoFlow(void *pvParameter) if (!isPlannedReboot && (esp_reset_reason() == ESP_RST_PANIC)) { flowctrl.setActStatus("Initialization (delayed)"); - //#ifdef ENABLE_MQTT - //MQTTPublish(mqttServer_getMainTopic() + "/" + "status", "Initialization (delayed)", false); // Right now, not possible -> MQTT Service is going to be started later - //#endif //ENABLE_MQTT - vTaskDelay(60*5000 / portTICK_PERIOD_MS); // Wait 5 minutes to give time to do an OTA update or fetch the log + // #ifdef ENABLE_MQTT + // MQTTPublish(mqttServer_getMainTopic() + "/" + "status", "Initialization (delayed)", false); // Right now, not possible -> MQTT Service is going to be started later + // #endif //ENABLE_MQTT + vTaskDelay(60 * 5000 / portTICK_PERIOD_MS); // Wait 5 minutes to give time to do an OTA update or fetch the log } ESP_LOGD(TAG, "task_autodoFlow: start"); @@ -959,212 +1435,219 @@ void task_autodoFlow(void *pvParameter) flowctrl.setAutoStartInterval(auto_interval); autostartIsEnabled = flowctrl.getIsAutoStart(); - if (isSetupModusActive()) + if (isSetupModusActive()) { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "We are in Setup Mode -> Not starting Auto Flow!"); autostartIsEnabled = false; - std::string zw_time = getCurrentTimeString(LOGFILE_TIME_FORMAT); - flowctrl.doFlowTakeImageOnly(zw_time); + // 15.7.0 Setup Wizard cannot take a Reference Picture #2953 + // std::string zw_time = getCurrentTimeString(LOGFILE_TIME_FORMAT); + // flowctrl.doFlowTakeImageOnly(zw_time); } - if (autostartIsEnabled) { + if (autostartIsEnabled) + { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Starting Flow..."); } - else { + else + { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Autostart is not enabled -> Not starting Flow"); } - + while (autostartIsEnabled) { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "----------------------------------------------------------------"); // Clear separation between runs - std::string _zw = "Round #" + std::to_string(++countRounds) + " started"; time_t roundStartTime = getUpTime(); - LogFile.WriteToFile(ESP_LOG_INFO, TAG, _zw); + + std::string _zw = "Round #" + std::to_string(++countRounds) + " started"; + LogFile.WriteToFile(ESP_LOG_INFO, TAG, _zw); + fr_start = esp_timer_get_time(); if (flowisrunning) { - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Autoflow: doFlow is already running!"); - #endif +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "Autoflow: doFlow is already running!"); +#endif } else { - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Autoflow: doFlow is started"); - #endif +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "Autoflow: doFlow is started"); +#endif flowisrunning = true; doflow(); - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Remove older log files"); - #endif +#ifdef DEBUG_DETAIL_ON + ESP_LOGD(TAG, "Remove older log files"); +#endif LogFile.RemoveOldLogFile(); LogFile.RemoveOldDataLog(); } // Round finished -> Logfile - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Round #" + std::to_string(countRounds) + - " completed (" + std::to_string(getUpTime() - roundStartTime) + " seconds)"); - + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Round #" + std::to_string(countRounds) + " completed (" + std::to_string(getUpTime() - roundStartTime) + " seconds)"); + // CPU Temp -> Logfile LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CPU Temperature: " + std::to_string((int)temperatureRead()) + "°C"); - + // WIFI Signal Strength (RSSI) -> Logfile LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "WIFI Signal (RSSI): " + std::to_string(get_WIFI_RSSI()) + "dBm"); // Check if time is synchronized (if NTP is configured) - if (getUseNtp() && !getTimeIsSet()) { + if (getUseNtp() && !getTimeIsSet()) + { LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Time server is configured, but time is not yet set!"); StatusLED(TIME_CHECK, 1, false); } - #if (defined WLAN_USE_MESH_ROAMING && defined WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES) - wifiRoamingQuery(); - #endif - - // Scan channels and check if an AP with better RSSI is available, then disconnect and try to reconnect to AP with better RSSI - // NOTE: Keep this direct before the following task delay, because scan is done in blocking mode and this takes ca. 1,5 - 2s. - #ifdef WLAN_USE_ROAMING_BY_SCANNING - wifiRoamByScanning(); - #endif - +#if (defined WLAN_USE_MESH_ROAMING && defined WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES) + wifiRoamingQuery(); +#endif + +// Scan channels and check if an AP with better RSSI is available, then disconnect and try to reconnect to AP with better RSSI +// NOTE: Keep this direct before the following task delay, because scan is done in blocking mode and this takes ca. 1,5 - 2s. +#ifdef WLAN_USE_ROAMING_BY_SCANNING + wifiRoamByScanning(); +#endif + fr_delta_ms = (esp_timer_get_time() - fr_start) / 1000; + if (auto_interval > fr_delta_ms) { - const TickType_t xDelay = (auto_interval - fr_delta_ms) / portTICK_PERIOD_MS; - ESP_LOGD(TAG, "Autoflow: sleep for: %ldms", (long) xDelay); - vTaskDelay( xDelay ); + const TickType_t xDelay = (auto_interval - fr_delta_ms) / portTICK_PERIOD_MS; + ESP_LOGD(TAG, "Autoflow: sleep for: %ldms", (long)xDelay); + vTaskDelay(xDelay); } } - while(1) // Keep flow task running to handle necessary sub tasks like reboot handler, etc.. + while (1) { - vTaskDelay(2000 / portTICK_PERIOD_MS); + // Keep flow task running to handle necessary sub tasks like reboot handler, etc.. + vTaskDelay(2000 / portTICK_PERIOD_MS); } - vTaskDelete(NULL); //Delete this task if it exits from the loop above + vTaskDelete(NULL); // Delete this task if it exits from the loop above xHandletask_autodoFlow = NULL; + ESP_LOGD(TAG, "task_autodoFlow: end"); } - -void InitializeFlowTask() +void InitializeFlowTask(void) { BaseType_t xReturned; ESP_LOGD(TAG, "getESPHeapInfo: %s", getESPHeapInfo().c_str()); uint32_t stackSize = 16 * 1024; - xReturned = xTaskCreatePinnedToCore(&task_autodoFlow, "task_autodoFlow", stackSize, NULL, tskIDLE_PRIORITY+2, &xHandletask_autodoFlow, 0); - if( xReturned != pdPASS ) + xReturned = xTaskCreatePinnedToCore(&task_autodoFlow, "task_autodoFlow", stackSize, NULL, tskIDLE_PRIORITY + 2, &xHandletask_autodoFlow, 0); + + if (xReturned != pdPASS) { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Creation task_autodoFlow failed. Requested stack size:" + std::to_string(stackSize)); LogFile.WriteHeapInfo("Creation task_autodoFlow failed"); } + ESP_LOGD(TAG, "getESPHeapInfo: %s", getESPHeapInfo().c_str()); } - void register_server_main_flow_task_uri(httpd_handle_t server) { ESP_LOGI(TAG, "server_main_flow_task - Registering URI handlers"); - - httpd_uri_t camuri = { }; - camuri.method = HTTP_GET; - camuri.uri = "/doinit"; - camuri.handler = handler_init; - camuri.user_ctx = (void*) "Light On"; + httpd_uri_t camuri = {}; + camuri.method = HTTP_GET; + + camuri.uri = "/doinit"; + camuri.handler = 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.user_ctx = (void*) "Prevalue"; + camuri.uri = "/setPreValue.html"; + camuri.handler = handler_prevalue; + camuri.user_ctx = (void *)"Prevalue"; httpd_register_uri_handler(server, &camuri); - camuri.uri = "/setPreValue"; - camuri.handler = handler_prevalue; - camuri.user_ctx = (void*) "Prevalue"; + camuri.uri = "/setPreValue"; + camuri.handler = handler_prevalue; + camuri.user_ctx = (void *)"Prevalue"; httpd_register_uri_handler(server, &camuri); - camuri.uri = "/flow_start"; - camuri.handler = handler_flow_start; - camuri.user_ctx = (void*) "Flow Start"; + camuri.uri = "/flow_start"; + camuri.handler = handler_flow_start; + camuri.user_ctx = (void *)"Flow Start"; httpd_register_uri_handler(server, &camuri); - camuri.uri = "/statusflow.html"; - camuri.handler = handler_statusflow; - camuri.user_ctx = (void*) "Light Off"; + camuri.uri = "/statusflow.html"; + camuri.handler = handler_statusflow; + camuri.user_ctx = (void *)"Light Off"; httpd_register_uri_handler(server, &camuri); - camuri.uri = "/statusflow"; - camuri.handler = handler_statusflow; - camuri.user_ctx = (void*) "Light Off"; + camuri.uri = "/statusflow"; + camuri.handler = 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.user_ctx = (void*) "Light Off"; + camuri.uri = "/cputemp.html"; + camuri.handler = handler_cputemp; + camuri.user_ctx = (void *)"Light Off"; httpd_register_uri_handler(server, &camuri); - camuri.uri = "/cpu_temperature"; - camuri.handler = handler_cputemp; - camuri.user_ctx = (void*) "Light Off"; + camuri.uri = "/cpu_temperature"; + camuri.handler = 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.user_ctx = (void*) "Light Off"; + camuri.uri = "/rssi.html"; + camuri.handler = handler_rssi; + camuri.user_ctx = (void *)"Light Off"; httpd_register_uri_handler(server, &camuri); - camuri.uri = "/rssi"; - camuri.handler = handler_rssi; - camuri.user_ctx = (void*) "Light Off"; + camuri.uri = "/rssi"; + camuri.handler = handler_rssi; + camuri.user_ctx = (void *)"Light Off"; httpd_register_uri_handler(server, &camuri); - camuri.uri = "/uptime"; - camuri.handler = handler_uptime; - camuri.user_ctx = (void*) "Light Off"; + camuri.uri = "/uptime"; + camuri.handler = handler_uptime; + camuri.user_ctx = (void *)"Light Off"; httpd_register_uri_handler(server, &camuri); - camuri.uri = "/editflow"; - camuri.handler = 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.user_ctx = (void*) "Value"; - httpd_register_uri_handler(server, &camuri); - - camuri.uri = "/value"; - camuri.handler = handler_wasserzaehler; - camuri.user_ctx = (void*) "Value"; + camuri.uri = "/editflow"; + camuri.handler = handler_editflow; + camuri.user_ctx = (void *)"EditFlow"; httpd_register_uri_handler(server, &camuri); // Legacy API => New: "/value" - camuri.uri = "/wasserzaehler.html"; - camuri.handler = handler_wasserzaehler; - camuri.user_ctx = (void*) "Wasserzaehler"; + camuri.uri = "/value.html"; + camuri.handler = handler_wasserzaehler; + camuri.user_ctx = (void *)"Value"; httpd_register_uri_handler(server, &camuri); - camuri.uri = "/json"; - camuri.handler = handler_json; - camuri.user_ctx = (void*) "JSON"; + camuri.uri = "/value"; + camuri.handler = handler_wasserzaehler; + camuri.user_ctx = (void *)"Value"; httpd_register_uri_handler(server, &camuri); - camuri.uri = "/heap"; - camuri.handler = handler_get_heap; - camuri.user_ctx = (void*) "Heap"; + // Legacy API => New: "/value" + camuri.uri = "/wasserzaehler.html"; + camuri.handler = handler_wasserzaehler; + camuri.user_ctx = (void *)"Wasserzaehler"; httpd_register_uri_handler(server, &camuri); - camuri.uri = "/stream"; - camuri.handler = handler_stream; - camuri.user_ctx = (void*) "stream"; + camuri.uri = "/json"; + camuri.handler = handler_json; + camuri.user_ctx = (void *)"JSON"; httpd_register_uri_handler(server, &camuri); + camuri.uri = "/heap"; + camuri.handler = handler_get_heap; + camuri.user_ctx = (void *)"Heap"; + httpd_register_uri_handler(server, &camuri); + + camuri.uri = "/stream"; + camuri.handler = handler_stream; + camuri.user_ctx = (void *)"stream"; + httpd_register_uri_handler(server, &camuri); } diff --git a/code/components/jomjol_flowcontroll/MainFlowControl.h b/code/components/jomjol_flowcontroll/MainFlowControl.h index 8ede3be0..8a2d1c41 100644 --- a/code/components/jomjol_flowcontroll/MainFlowControl.h +++ b/code/components/jomjol_flowcontroll/MainFlowControl.h @@ -10,25 +10,78 @@ #include "CImageBasis.h" #include "ClassFlowControll.h" +typedef struct +{ + framesize_t ImageFrameSize = FRAMESIZE_VGA; // 0 - 10 + gainceiling_t ImageGainceiling; // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128) + + int ImageQuality; // 0 - 63 + int ImageBrightness; // (-2 to 2) - set brightness + int ImageContrast; //-2 - 2 + int ImageSaturation; //-2 - 2 + int ImageSharpness; //-2 - 2 + bool ImageAutoSharpness; + int ImageSpecialEffect; // 0 - 6 + int ImageWbMode; // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home) + int ImageAwb; // white balance enable (0 or 1) + int ImageAwbGain; // Auto White Balance enable (0 or 1) + int ImageAec; // auto exposure off (1 or 0) + int ImageAec2; // automatic exposure sensor (0 or 1) + int ImageAeLevel; // auto exposure levels (-2 to 2) + int ImageAecValue; // set exposure manually (0-1200) + int ImageAgc; // auto gain off (1 or 0) + int ImageAgcGain; // set gain manually (0 - 30) + int ImageBpc; // black pixel correction + int ImageWpc; // white pixel correction + int ImageRawGma; // (1 or 0) + int ImageLenc; // lens correction (1 or 0) + int ImageHmirror; // (0 or 1) flip horizontally + int ImageVflip; // Invert image (0 or 1) + int ImageDcw; // downsize enable (1 or 0) + + int ImageWidth; + int ImageHeight; + + int ImageLedIntensity; + + bool ImageZoomEnabled; + int ImageZoomMode; + int ImageZoomOffsetX; + int ImageZoomOffsetY; + int ImageZoomSize; + + int WaitBeforePicture; + bool isImageSize; + + bool CameraInitSuccessful; + bool changedCameraSettings; + bool DemoMode; + bool SaveAllFiles; +} camera_flow_config_temp_t; + +extern camera_flow_config_temp_t CFstatus; extern ClassFlowControll flowctrl; +esp_err_t setCCstatusToCFstatus(void); // CCstatus >>> CFstatus +esp_err_t setCFstatusToCCstatus(void); // CFstatus >>> CCstatus +esp_err_t setCFstatusToCam(void); // CFstatus >>> Kamera void register_server_main_flow_task_uri(httpd_handle_t server); -void CheckIsPlannedReboot(); -bool getIsPlannedReboot(); +void CheckIsPlannedReboot(void); +bool getIsPlannedReboot(void); -void InitializeFlowTask(); -void DeleteMainFlowTask(); -bool isSetupModusActive(); +void InitializeFlowTask(void); +void DeleteMainFlowTask(void); +bool isSetupModusActive(void); -int getCountFlowRounds(); +int getCountFlowRounds(void); #ifdef ENABLE_MQTT esp_err_t MQTTCtrlFlowStart(std::string _topic); -#endif //ENABLE_MQTT +#endif // ENABLE_MQTT esp_err_t GetRawJPG(httpd_req_t *req); esp_err_t GetJPG(std::string _filename, httpd_req_t *req); -#endif //MAINFLOWCONTROL_H +#endif // MAINFLOWCONTROL_H diff --git a/code/components/jomjol_helper/CMakeLists.txt b/code/components/jomjol_helper/CMakeLists.txt index 0b8e0ae0..ba60b641 100644 --- a/code/components/jomjol_helper/CMakeLists.txt +++ b/code/components/jomjol_helper/CMakeLists.txt @@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) idf_component_register(SRCS ${app_sources} INCLUDE_DIRS "." - REQUIRES esp_timer esp-tflite-micro jomjol_logfile fatfs sdmmc) + REQUIRES esp_timer esp-tflite-micro jomjol_logfile fatfs sdmmc vfs) diff --git a/code/components/jomjol_helper/Helper.cpp b/code/components/jomjol_helper/Helper.cpp index e68e6873..c794646a 100644 --- a/code/components/jomjol_helper/Helper.cpp +++ b/code/components/jomjol_helper/Helper.cpp @@ -1,4 +1,4 @@ -//#pragma warning(disable : 4996) +// #pragma warning(disable : 4996) #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -14,7 +14,8 @@ #include #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif #include #ifdef __cplusplus @@ -27,12 +28,12 @@ extern "C" { #include #include "../../include/defines.h" - #include "ClassLogFile.h" #include "esp_vfs_fat.h" +#include "../sdmmc_common.h" -static const char* TAG = "HELPER"; +static const char *TAG = "HELPER"; using namespace std; @@ -40,192 +41,208 @@ unsigned int systemStatus = 0; sdmmc_cid_t SDCardCid; sdmmc_csd_t SDCardCsd; +bool SDCardIsMMC; - -// #define DEBUG_DETAIL_ON +// #define DEBUG_DETAIL_ON ///////////////////////////////////////////////////////////////////////////////////////////// -string getESPHeapInfo(){ +string getESPHeapInfo() +{ string espInfoResultStr = ""; char aMsgBuf[80]; - size_t aFreeHeapSize = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t aFreeHeapSize = heap_caps_get_free_size(MALLOC_CAP_8BIT); - size_t aFreeSPIHeapSize = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); - size_t aFreeInternalHeapSize = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + size_t aFreeSPIHeapSize = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); + size_t aFreeInternalHeapSize = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); size_t aHeapLargestFreeBlockSize = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); size_t aHeapIntLargestFreeBlockSize = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); - size_t aMinFreeHeapSize = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); - size_t aMinFreeInternalHeapSize = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + size_t aMinFreeHeapSize = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); + size_t aMinFreeInternalHeapSize = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); - - sprintf(aMsgBuf,"Heap Total: %ld", (long) aFreeHeapSize); + sprintf(aMsgBuf, "Heap Total: %ld", (long)aFreeHeapSize); espInfoResultStr += string(aMsgBuf); - sprintf(aMsgBuf," | SPI Free: %ld", (long) aFreeSPIHeapSize); + sprintf(aMsgBuf, " | SPI Free: %ld", (long)aFreeSPIHeapSize); espInfoResultStr += string(aMsgBuf); - sprintf(aMsgBuf," | SPI Large Block: %ld", (long) aHeapLargestFreeBlockSize); + sprintf(aMsgBuf, " | SPI Large Block: %ld", (long)aHeapLargestFreeBlockSize); espInfoResultStr += string(aMsgBuf); - sprintf(aMsgBuf," | SPI Min Free: %ld", (long) aMinFreeHeapSize); + sprintf(aMsgBuf, " | SPI Min Free: %ld", (long)aMinFreeHeapSize); espInfoResultStr += string(aMsgBuf); - sprintf(aMsgBuf," | Int Free: %ld", (long) (aFreeInternalHeapSize)); + sprintf(aMsgBuf, " | Int Free: %ld", (long)(aFreeInternalHeapSize)); espInfoResultStr += string(aMsgBuf); - sprintf(aMsgBuf," | Int Large Block: %ld", (long) aHeapIntLargestFreeBlockSize); + sprintf(aMsgBuf, " | Int Large Block: %ld", (long)aHeapIntLargestFreeBlockSize); espInfoResultStr += string(aMsgBuf); - sprintf(aMsgBuf," | Int Min Free: %ld", (long) (aMinFreeInternalHeapSize)); + sprintf(aMsgBuf, " | Int Min Free: %ld", (long)(aMinFreeInternalHeapSize)); espInfoResultStr += string(aMsgBuf); - - return espInfoResultStr; + + return espInfoResultStr; } - size_t getESPHeapSize() { - return heap_caps_get_free_size(MALLOC_CAP_8BIT); + return heap_caps_get_free_size(MALLOC_CAP_8BIT); } - -size_t getInternalESPHeapSize() +size_t getInternalESPHeapSize() { - return heap_caps_get_free_size(MALLOC_CAP_8BIT| MALLOC_CAP_INTERNAL); + return heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); } - -string getSDCardPartitionSize(){ +string getSDCardPartitionSize() +{ FATFS *fs; - uint32_t fre_clust, tot_sect; + uint32_t fre_clust, tot_sect; - /* Get volume information and free clusters of drive 0 */ - f_getfree("0:", (DWORD *)&fre_clust, &fs); - tot_sect = ((fs->n_fatent - 2) * fs->csize) /1024 /(1024/SDCardCsd.sector_size); //corrected by SD Card sector size (usually 512 bytes) and convert to MB + /* Get volume information and free clusters of drive 0 */ + f_getfree("0:", (DWORD *)&fre_clust, &fs); + tot_sect = ((fs->n_fatent - 2) * fs->csize) / 1024 / (1024 / SDCardCsd.sector_size); // corrected by SD Card sector size (usually 512 bytes) and convert to MB - //ESP_LOGD(TAG, "%d MB total drive space (Sector size [bytes]: %d)", (int)tot_sect, (int)fs->ssize); + // ESP_LOGD(TAG, "%d MB total drive space (Sector size [bytes]: %d)", (int)tot_sect, (int)fs->ssize); return std::to_string(tot_sect); } - -string getSDCardFreePartitionSpace(){ +string getSDCardFreePartitionSpace() +{ FATFS *fs; - uint32_t fre_clust, fre_sect; - - /* Get volume information and free clusters of drive 0 */ - f_getfree("0:", (DWORD *)&fre_clust, &fs); - fre_sect = (fre_clust * fs->csize) / 1024 /(1024/SDCardCsd.sector_size); //corrected by SD Card sector size (usually 512 bytes) and convert to MB + uint32_t fre_clust, fre_sect; - //ESP_LOGD(TAG, "%d MB free drive space (Sector size [bytes]: %d)", (int)fre_sect, (int)fs->ssize); + /* Get volume information and free clusters of drive 0 */ + f_getfree("0:", (DWORD *)&fre_clust, &fs); + fre_sect = (fre_clust * fs->csize) / 1024 / (1024 / SDCardCsd.sector_size); // corrected by SD Card sector size (usually 512 bytes) and convert to MB + + // ESP_LOGD(TAG, "%d MB free drive space (Sector size [bytes]: %d)", (int)fre_sect, (int)fs->ssize); return std::to_string(fre_sect); } - -string getSDCardPartitionAllocationSize(){ +string getSDCardPartitionAllocationSize() +{ FATFS *fs; - uint32_t fre_clust, allocation_size; - - /* Get volume information and free clusters of drive 0 */ - f_getfree("0:", (DWORD *)&fre_clust, &fs); - allocation_size = fs->ssize; + uint32_t fre_clust, allocation_size; - //ESP_LOGD(TAG, "SD Card Partition Allocation Size: %d bytes", allocation_size); + /* Get volume information and free clusters of drive 0 */ + f_getfree("0:", (DWORD *)&fre_clust, &fs); + allocation_size = fs->ssize; + + // ESP_LOGD(TAG, "SD Card Partition Allocation Size: %d bytes", allocation_size); return std::to_string(allocation_size); } - -void SaveSDCardInfo(sdmmc_card_t* card) { +void SaveSDCardInfo(sdmmc_card_t *card) +{ SDCardCid = card->cid; - SDCardCsd = card->csd; + SDCardCsd = card->csd; + SDCardIsMMC = card->is_mmc; } - -string getSDCardManufacturer(){ +string getSDCardManufacturer() +{ string SDCardManufacturer = SDCardParseManufacturerIDs(SDCardCid.mfg_id); - //ESP_LOGD(TAG, "SD Card Manufacturer: %s", SDCardManufacturer.c_str()); - + // ESP_LOGD(TAG, "SD Card Manufacturer: %s", SDCardManufacturer.c_str()); + return (SDCardManufacturer + " (ID: " + std::to_string(SDCardCid.mfg_id) + ")"); } - -string getSDCardName(){ +string getSDCardName() +{ char *SDCardName = SDCardCid.name; - //ESP_LOGD(TAG, "SD Card Name: %s", SDCardName); + // ESP_LOGD(TAG, "SD Card Name: %s", SDCardName); return std::string(SDCardName); } - -string getSDCardCapacity(){ - int SDCardCapacity = SDCardCsd.capacity / (1024/SDCardCsd.sector_size) / 1024; // total sectors * sector size --> Byte to MB (1024*1024) - //ESP_LOGD(TAG, "SD Card Capacity: %s", std::to_string(SDCardCapacity).c_str()); +string getSDCardCapacity() +{ + int SDCardCapacity = SDCardCsd.capacity / (1024 / SDCardCsd.sector_size) / 1024; // total sectors * sector size --> Byte to MB (1024*1024) + // ESP_LOGD(TAG, "SD Card Capacity: %s", std::to_string(SDCardCapacity).c_str()); return std::to_string(SDCardCapacity); } - -string getSDCardSectorSize(){ +string getSDCardSectorSize() +{ int SDCardSectorSize = SDCardCsd.sector_size; - //ESP_LOGD(TAG, "SD Card Sector Size: %s bytes", std::to_string(SDCardSectorSize).c_str()); + // ESP_LOGD(TAG, "SD Card Sector Size: %s bytes", std::to_string(SDCardSectorSize).c_str()); return std::to_string(SDCardSectorSize); } /////////////////////////////////////////////////////////////////////////////////////////////// -void memCopyGen(uint8_t* _source, uint8_t* _target, int _size) +void memCopyGen(uint8_t *_source, uint8_t *_target, int _size) { - for (int i = 0; i < _size; ++i) - *(_target + i) = *(_source + i); + for (int i = 0; i < _size; ++i) + { + *(_target + i) = *(_source + i); + } } - std::string FormatFileName(std::string input) { #ifdef ISWINDOWS_TRUE - input.erase(0, 1); - std::string os = "/"; - std::string ns = "\\"; - FindReplace(input, os, ns); + input.erase(0, 1); + std::string os = "/"; + std::string ns = "\\"; + FindReplace(input, os, ns); #endif - return input; + return input; } +std::size_t file_size(const std::string &file_name) +{ + std::ifstream file(file_name.c_str(), std::ios::in | std::ios::binary); -std::size_t file_size(const std::string& file_name) { - std::ifstream file(file_name.c_str(),std::ios::in | std::ios::binary); - if (!file) return 0; - file.seekg (0, std::ios::end); - return static_cast(file.tellg()); + if (!file) + { + return 0; + } + + file.seekg(0, std::ios::end); + return static_cast(file.tellg()); } +void FindReplace(std::string &line, std::string &oldString, std::string &newString) +{ + const size_t oldSize = oldString.length(); -void FindReplace(std::string& line, std::string& oldString, std::string& newString) { - const size_t oldSize = oldString.length(); + // do nothing if line is shorter than the string to find + if (oldSize > line.length()) + { + return; + } - // do nothing if line is shorter than the string to find - if (oldSize > line.length()) return; + const size_t newSize = newString.length(); - const size_t newSize = newString.length(); - for (size_t pos = 0; ; pos += newSize) { - // Locate the substring to replace - pos = line.find(oldString, pos); - if (pos == std::string::npos) return; - if (oldSize == newSize) { - // if they're same size, use std::string::replace - line.replace(pos, oldSize, newString); - } - else { - // if not same size, replace by erasing and inserting - line.erase(pos, oldSize); - line.insert(pos, newString); - } - } + for (size_t pos = 0;; pos += newSize) + { + // Locate the substring to replace + pos = line.find(oldString, pos); + + if (pos == std::string::npos) + { + return; + } + + if (oldSize == newSize) + { + // if they're same size, use std::string::replace + line.replace(pos, oldSize, newString); + } + else + { + // if not same size, replace by erasing and inserting + line.erase(pos, oldSize); + line.insert(pos, newString); + } + } } - /** * Create a folder and its parent folders as needed */ @@ -236,56 +253,63 @@ bool MakeDir(std::string path) LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Creating folder " + path + "..."); bool bSuccess = false; - int nRC = ::mkdir( path.c_str(), 0775 ); - if( nRC == -1 ) - { - switch( errno ) { - case ENOENT: - //parent didn't exist, try to create it - parent = path.substr(0, path.find_last_of('/')); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Need to create parent folder first: " + parent); - if(MakeDir(parent)) { - //Now, try to create again. - bSuccess = 0 == ::mkdir( path.c_str(), 0775 ); - } - else { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create parent folder: " + parent); - bSuccess = false; - } - break; + int nRC = ::mkdir(path.c_str(), 0775); - case EEXIST: - //Done! - bSuccess = true; - break; - - default: - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create folder: " + path + " (errno: " + std::to_string(errno) + ")"); - bSuccess = false; - break; - } - } - else { - bSuccess = true; + if (nRC == -1) + { + switch (errno) + { + case ENOENT: + // parent didn't exist, try to create it + parent = path.substr(0, path.find_last_of('/')); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Need to create parent folder first: " + parent); + + if (MakeDir(parent)) + { + // Now, try to create again. + bSuccess = 0 == ::mkdir(path.c_str(), 0775); + } + else + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create parent folder: " + parent); + bSuccess = false; + } + break; + + case EEXIST: + // Done! + bSuccess = true; + break; + + default: + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create folder: " + path + " (errno: " + std::to_string(errno) + ")"); + bSuccess = false; + break; + } + } + else + { + bSuccess = true; } - return bSuccess; + return bSuccess; } - bool ctype_space(const char c, string adddelimiter) { if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == 11) { return true; } + if (adddelimiter.find(c) != string::npos) + { return true; + } return false; } - string trim(string istring, string adddelimiter) { bool trimmed = false; @@ -312,76 +336,87 @@ string trim(string istring, string adddelimiter) } } - size_t findDelimiterPos(string input, string delimiter) { size_t pos = std::string::npos; - size_t zw; + // size_t zw; string akt_del; for (int anz = 0; anz < delimiter.length(); ++anz) { akt_del = delimiter[anz]; - if ((zw = input.find(akt_del)) != std::string::npos) + size_t zw = input.find(akt_del); + + if (zw != std::string::npos) { - if (pos != std::string::npos) + if ((pos != std::string::npos) && (zw < pos)) { - if (zw < pos) - pos = zw; + pos = zw; } else + { pos = zw; + } } } + return pos; } - bool RenameFile(string from, string to) { -// ESP_LOGI(logTag, "Deleting file: %s", fn.c_str()); + // ESP_LOGI(logTag, "Deleting file: %s", fn.c_str()); /* Delete file */ - FILE* fpSourceFile = fopen(from.c_str(), "rb"); - if (!fpSourceFile) // Sourcefile existiert nicht sonst gibt es einen Fehler beim Kopierversuch! + FILE *fpSourceFile = fopen(from.c_str(), "rb"); + + // Sourcefile existiert nicht sonst gibt es einen Fehler beim Kopierversuch! + if (!fpSourceFile) { ESP_LOGE(TAG, "DeleteFile: File %s existiert nicht!", from.c_str()); return false; } + fclose(fpSourceFile); rename(from.c_str(), to.c_str()); + return true; } - bool FileExists(string filename) { - FILE* fpSourceFile = fopen(filename.c_str(), "rb"); - if (!fpSourceFile) // Sourcefile existiert nicht sonst gibt es einen Fehler beim Kopierversuch! + FILE *fpSourceFile = fopen(filename.c_str(), "rb"); + + // Sourcefile existiert nicht sonst gibt es einen Fehler beim Kopierversuch! + if (!fpSourceFile) { return false; } - fclose(fpSourceFile); - return true; -} + fclose(fpSourceFile); + + return true; +} bool DeleteFile(string fn) { -// ESP_LOGI(logTag, "Deleting file: %s", fn.c_str()); + // ESP_LOGI(logTag, "Deleting file: %s", fn.c_str()); /* Delete file */ - FILE* fpSourceFile = fopen(fn.c_str(), "rb"); - if (!fpSourceFile) // Sourcefile existiert nicht sonst gibt es einen Fehler beim Kopierversuch! + FILE *fpSourceFile = fopen(fn.c_str(), "rb"); + + // Sourcefile existiert nicht sonst gibt es einen Fehler beim Kopierversuch! + if (!fpSourceFile) { ESP_LOGD(TAG, "DeleteFile: File %s existiert nicht!", fn.c_str()); return false; } + fclose(fpSourceFile); unlink(fn.c_str()); - return true; -} + return true; +} bool CopyFile(string input, string output) { @@ -395,14 +430,16 @@ bool CopyFile(string input, string output) } char cTemp; - FILE* fpSourceFile = fopen(input.c_str(), "rb"); - if (!fpSourceFile) // Sourcefile existiert nicht sonst gibt es einen Fehler beim Kopierversuch! + FILE *fpSourceFile = fopen(input.c_str(), "rb"); + + // Sourcefile existiert nicht sonst gibt es einen Fehler beim Kopierversuch! + if (!fpSourceFile) { ESP_LOGD(TAG, "File %s existiert nicht!", input.c_str()); return false; } - FILE* fpTargetFile = fopen(output.c_str(), "wb"); + FILE *fpTargetFile = fopen(output.c_str(), "wb"); // Code Section @@ -417,53 +454,60 @@ bool CopyFile(string input, string output) fclose(fpSourceFile); fclose(fpTargetFile); ESP_LOGD(TAG, "File copied: %s to %s", input.c_str(), output.c_str()); + return true; } - string getFileFullFileName(string filename) { size_t lastpos = filename.find_last_of('/'); if (lastpos == string::npos) + { return ""; + } -// ESP_LOGD(TAG, "Last position: %d", lastpos); + // ESP_LOGD(TAG, "Last position: %d", lastpos); string zw = filename.substr(lastpos + 1, filename.size() - lastpos); return zw; } - string getDirectory(string filename) { size_t lastpos = filename.find('/'); if (lastpos == string::npos) + { lastpos = filename.find('\\'); + } if (lastpos == string::npos) + { return ""; + } -// ESP_LOGD(TAG, "Directory: %d", lastpos); + // ESP_LOGD(TAG, "Directory: %d", lastpos); string zw = filename.substr(0, lastpos - 1); return zw; } - string getFileType(string filename) { size_t lastpos = filename.rfind(".", filename.length()); size_t neu_pos; + while ((neu_pos = filename.find(".", lastpos + 1)) > -1) { lastpos = neu_pos; } if (lastpos == string::npos) + { return ""; + } string zw = filename.substr(lastpos + 1, filename.size() - lastpos); zw = toUpper(zw); @@ -471,172 +515,213 @@ string getFileType(string filename) return zw; } - /* recursive mkdir */ -int mkdir_r(const char *dir, const mode_t mode) { - char tmp[FILE_PATH_MAX]; - char *p = NULL; - struct stat sb; - size_t len; - - /* copy path */ - len = strnlen (dir, FILE_PATH_MAX); - if (len == 0 || len == FILE_PATH_MAX) { - return -1; - } - memcpy (tmp, dir, len); - tmp[len] = '\0'; +int mkdir_r(const char *dir, const mode_t mode) +{ + char tmp[FILE_PATH_MAX]; + char *p = NULL; + struct stat sb; + size_t len; - /* remove trailing slash */ - if(tmp[len - 1] == '/') { - tmp[len - 1] = '\0'; - } + /* copy path */ + len = strnlen(dir, FILE_PATH_MAX); - /* check if path exists and is a directory */ - if (stat (tmp, &sb) == 0) { - if (S_ISDIR (sb.st_mode)) { - return 0; - } - } - - /* recursive mkdir */ - for(p = tmp + 1; *p; p++) { - if(*p == '/') { - *p = 0; - /* test path */ - if (stat(tmp, &sb) != 0) { - /* path does not exist - create directory */ - if (mkdir(tmp, mode) < 0) { - return -1; - } - } else if (!S_ISDIR(sb.st_mode)) { - /* not a directory */ - return -1; - } - *p = '/'; - } - } - /* test path */ - if (stat(tmp, &sb) != 0) { - /* path does not exist - create directory */ - if (mkdir(tmp, mode) < 0) { - return -1; - } - } else if (!S_ISDIR(sb.st_mode)) { - /* not a directory */ - return -1; - } - return 0; + if (len == 0 || len == FILE_PATH_MAX) + { + return -1; + } + + memcpy(tmp, dir, len); + tmp[len] = '\0'; + + /* remove trailing slash */ + if (tmp[len - 1] == '/') + { + tmp[len - 1] = '\0'; + } + + /* check if path exists and is a directory */ + if (stat(tmp, &sb) == 0) + { + if (S_ISDIR(sb.st_mode)) + { + return 0; + } + } + + /* recursive mkdir */ + for (p = tmp + 1; *p; p++) + { + if (*p == '/') + { + *p = 0; + + /* test path */ + if (stat(tmp, &sb) != 0) + { + /* path does not exist - create directory */ + if (mkdir(tmp, mode) < 0) + { + return -1; + } + } + else if (!S_ISDIR(sb.st_mode)) + { + /* not a directory */ + return -1; + } + + *p = '/'; + } + } + + /* test path */ + if (stat(tmp, &sb) != 0) + { + /* path does not exist - create directory */ + if (mkdir(tmp, mode) < 0) + { + return -1; + } + } + else if (!S_ISDIR(sb.st_mode)) + { + /* not a directory */ + return -1; + } + + return 0; } - string toUpper(string in) { for (int i = 0; i < in.length(); ++i) + { in[i] = toupper(in[i]); - + } + return in; } - string toLower(string in) { for (int i = 0; i < in.length(); ++i) + { in[i] = tolower(in[i]); - + } + return in; } - // CPU Temp extern "C" uint8_t temprature_sens_read(); float temperatureRead() { - return (temprature_sens_read() - 32) / 1.8; + return (temprature_sens_read() - 32) / 1.8; } - -time_t addDays(time_t startTime, int days) { - struct tm* tm = localtime(&startTime); +time_t addDays(time_t startTime, int days) +{ + struct tm *tm = localtime(&startTime); tm->tm_mday += days; return mktime(tm); } - -int removeFolder(const char* folderPath, const char* logTag) { - //ESP_LOGD(logTag, "Delete content in path %s", folderPath); +int removeFolder(const char *folderPath, const char *logTag) +{ + // ESP_LOGD(logTag, "Delete content in path %s", folderPath); DIR *dir = opendir(folderPath); - if (!dir) { - ESP_LOGE(logTag, "Failed to stat dir: %s", folderPath); - return -1; - } - struct dirent *entry; - int deleted = 0; - while ((entry = readdir(dir)) != NULL) { - std::string path = string(folderPath) + "/" + entry->d_name; - if (entry->d_type == DT_REG) { - //ESP_LOGD(logTag, "Delete file %s", path.c_str()); - if (unlink(path.c_str()) == 0) { - deleted ++; - } else { + if (!dir) + { + ESP_LOGE(logTag, "Failed to stat dir: %s", folderPath); + return -1; + } + + struct dirent *entry; + int deleted = 0; + + while ((entry = readdir(dir)) != NULL) + { + std::string path = string(folderPath) + "/" + entry->d_name; + + if (entry->d_type == DT_REG) + { + // ESP_LOGD(logTag, "Delete file %s", path.c_str()); + if (unlink(path.c_str()) == 0) + { + deleted++; + } + else + { ESP_LOGE(logTag, "can't delete file: %s", path.c_str()); } - } else if (entry->d_type == DT_DIR) { + } + else if (entry->d_type == DT_DIR) + { deleted += removeFolder(path.c_str(), logTag); } - } - - closedir(dir); - if (rmdir(folderPath) != 0) { + } + + closedir(dir); + + if (rmdir(folderPath) != 0) + { ESP_LOGE(logTag, "can't delete folder: %s", folderPath); } + ESP_LOGD(logTag, "%d files in folder %s deleted.", deleted, folderPath); return deleted; } - std::vector HelperZerlegeZeile(std::string input, std::string _delimiter = "") { std::vector Output; std::string delimiter = " =,"; - if (_delimiter.length() > 0){ - delimiter = _delimiter; - } + + if (_delimiter.length() > 0) + { + delimiter = _delimiter; + } return ZerlegeZeile(input, delimiter); } - std::vector ZerlegeZeile(std::string input, std::string delimiter) { std::vector Output; - /* The input can have multiple formats: + /* The input can have multiple formats: * - key = value - * - key = value1 value2 value3 ... - * - key value1 value2 value3 ... - * + * - key = value1 value2 value3 ... + * - key value1 value2 value3 ... + * * Examples: * - ImageSize = VGA - * - IO0 = input disabled 10 false false + * - IO0 = input disabled 10 false false * - main.dig1 28 144 55 100 false - * + * * This causes issues eg. if a password key has a whitespace or equal sign in its value. * As a workaround and to not break any legacy usage, we enforce to only use the * equal sign, if the key is "password" - */ - if ((input.find("password") != string::npos) || (input.find("Token") != string::npos)) { // Line contains a password, use the equal sign as the only delimiter and only split on first occurrence + */ + if ((input.find("password") != string::npos) || (input.find("Token") != string::npos)) + { + // Line contains a password, use the equal sign as the only delimiter and only split on first occurrence size_t pos = input.find("="); Output.push_back(trim(input.substr(0, pos), "")); - Output.push_back(trim(input.substr(pos +1, string::npos), "")); + Output.push_back(trim(input.substr(pos + 1, string::npos), "")); } - else { // Legacy Mode - input = trim(input, delimiter); // sonst werden delimiter am Ende (z.B. == im Token) gelöscht) + else + { + // Legacy Mode + input = trim(input, delimiter); // sonst werden delimiter am Ende (z.B. == im Token) gelöscht) size_t pos = findDelimiterPos(input, delimiter); std::string token; - while (pos != std::string::npos) { + + while (pos != std::string::npos) + { token = input.substr(0, pos); token = trim(token, delimiter); Output.push_back(token); @@ -644,37 +729,38 @@ std::vector ZerlegeZeile(std::string input, std::string delimiter) input = trim(input, delimiter); pos = findDelimiterPos(input, delimiter); } + Output.push_back(input); } return Output; - } +std::string ReplaceString(std::string subject, const std::string &search, const std::string &replace) +{ + size_t pos = 0; -std::string ReplaceString(std::string subject, const std::string& search, - const std::string& replace) { - size_t pos = 0; - while ((pos = subject.find(search, pos)) != std::string::npos) { - subject.replace(pos, search.length(), replace); - pos += replace.length(); - } - return subject; + while ((pos = subject.find(search, pos)) != std::string::npos) + { + subject.replace(pos, search.length(), replace); + pos += replace.length(); + } + + return subject; } - /* Source: https://git.kernel.org/pub/scm/utils/mmc/mmc-utils.git/tree/lsmmc.c */ /* SD Card Manufacturer Database */ -struct SDCard_Manufacturer_database { +struct SDCard_Manufacturer_database +{ string type; int id; string manufacturer; }; - /* Source: https://git.kernel.org/pub/scm/utils/mmc/mmc-utils.git/tree/lsmmc.c */ /* SD Card Manufacturer Database */ -struct SDCard_Manufacturer_database database[] = { +struct SDCard_Manufacturer_database sd_database[] = { { .type = "sd", .id = 0x01, @@ -779,220 +865,344 @@ struct SDCard_Manufacturer_database database[] = { .type = "sd", .id = 0x89, .manufacturer = "Unknown", - } + }, }; +struct SDCard_Manufacturer_database mmc_database[] = { + { + .type = "mmc", + .id = 0x00, + .manufacturer = "SanDisk", + }, + { + .type = "mmc", + .id = 0x02, + .manufacturer = "Kingston/SanDisk", + }, + { + .type = "mmc", + .id = 0x03, + .manufacturer = "Toshiba", + }, + { + .type = "mmc", + .id = 0x05, + .manufacturer = "Unknown", + }, + { + .type = "mmc", + .id = 0x06, + .manufacturer = "Unknown", + }, + { + .type = "mmc", + .id = 0x11, + .manufacturer = "Toshiba", + }, + { + .type = "mmc", + .id = 0x13, + .manufacturer = "Micron", + }, + { + .type = "mmc", + .id = 0x15, + .manufacturer = "Samsung/SanDisk/LG", + }, + { + .type = "mmc", + .id = 0x37, + .manufacturer = "KingMax", + }, + { + .type = "mmc", + .id = 0x44, + .manufacturer = "ATP", + }, + { + .type = "mmc", + .id = 0x45, + .manufacturer = "SanDisk Corporation", + }, + { + .type = "mmc", + .id = 0x2c, + .manufacturer = "Kingston", + }, + { + .type = "mmc", + .id = 0x70, + .manufacturer = "Kingston", + }, + { + .type = "mmc", + .id = 0xfe, + .manufacturer = "Micron", + }, +}; /* Parse SD Card Manufacturer Database */ -string SDCardParseManufacturerIDs(int id) +string SDCardParseManufacturerIDs(int id) { - unsigned int id_cnt = sizeof(database) / sizeof(struct SDCard_Manufacturer_database); - string ret_val = ""; + if (SDCardIsMMC) + { + unsigned int id_cnt = sizeof(mmc_database) / sizeof(struct SDCard_Manufacturer_database); + string ret_val = ""; - for (int i = 0; i < id_cnt; i++) { - if (database[i].id == id) { - return database[i].manufacturer; - } - else { - ret_val = "ID unknown (not in DB)"; + for (int i = 0; i < id_cnt; i++) + { + if (mmc_database[i].id == id) + { + return mmc_database[i].manufacturer; + } + else + { + ret_val = "ID unknown (not in DB)"; + } } + + return ret_val; } - return ret_val; -} + else + { + unsigned int id_cnt = sizeof(sd_database) / sizeof(struct SDCard_Manufacturer_database); + string ret_val = ""; + + for (int i = 0; i < id_cnt; i++) + { + if (sd_database[i].id == id) + { + return sd_database[i].manufacturer; + } + else + { + ret_val = "ID unknown (not in DB)"; + } + } + + return ret_val; + } +} string RundeOutput(double _in, int _anzNachkomma) { - std::stringstream stream; - int _zw = _in; -// ESP_LOGD(TAG, "AnzNachkomma: %d", _anzNachkomma); + std::stringstream stream; + int _zw = _in; + // ESP_LOGD(TAG, "AnzNachkomma: %d", _anzNachkomma); - if (_anzNachkomma < 0) { - _anzNachkomma = 0; - } + if (_anzNachkomma > 0) + { + stream << std::fixed << std::setprecision(_anzNachkomma) << _in; + } + else + { + stream << _zw; + } - if (_anzNachkomma > 0) - { - stream << std::fixed << std::setprecision(_anzNachkomma) << _in; - return stream.str(); - } - else - { - stream << _zw; - } - - - return stream.str(); + return stream.str(); } +string getMac(void) +{ + uint8_t macInt[6]; + char macFormated[6 * 2 + 5 + 1]; // AA:BB:CC:DD:EE:FF -string getMac(void) { - uint8_t macInt[6]; - char macFormated[6*2 + 5 + 1]; // AA:BB:CC:DD:EE:FF + esp_read_mac(macInt, ESP_MAC_WIFI_STA); + sprintf(macFormated, "%02X:%02X:%02X:%02X:%02X:%02X", macInt[0], macInt[1], macInt[2], macInt[3], macInt[4], macInt[5]); - esp_read_mac(macInt, ESP_MAC_WIFI_STA); - sprintf(macFormated, "%02X:%02X:%02X:%02X:%02X:%02X", macInt[0], macInt[1], macInt[2], macInt[3], macInt[4], macInt[5]); - - return macFormated; + return macFormated; } - -void setSystemStatusFlag(SystemStatusFlag_t flag) { +void setSystemStatusFlag(SystemStatusFlag_t flag) +{ systemStatus = systemStatus | flag; // set bit char buf[20]; snprintf(buf, sizeof(buf), "0x%08X", getSystemStatus()); - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "New System Status: " + std::string(buf)); + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "New System Status: " + std::string(buf)); } - -void clearSystemStatusFlag(SystemStatusFlag_t flag) { +void clearSystemStatusFlag(SystemStatusFlag_t flag) +{ systemStatus = systemStatus | ~flag; // clear bit char buf[20]; snprintf(buf, sizeof(buf), "0x%08X", getSystemStatus()); - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "New System Status: " + std::string(buf)); + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "New System Status: " + std::string(buf)); } - -int getSystemStatus(void) { - return systemStatus; +int getSystemStatus(void) +{ + return systemStatus; } +bool isSetSystemStatusFlag(SystemStatusFlag_t flag) +{ + // ESP_LOGE(TAG, "Flag (0x%08X) is set (0x%08X): %d", flag, systemStatus , ((systemStatus & flag) == flag)); -bool isSetSystemStatusFlag(SystemStatusFlag_t flag) { - //ESP_LOGE(TAG, "Flag (0x%08X) is set (0x%08X): %d", flag, systemStatus , ((systemStatus & flag) == flag)); - - if ((systemStatus & flag) == flag) { + if ((systemStatus & flag) == flag) + { return true; } - else { + else + { return false; } } - -time_t getUpTime(void) { - return (uint32_t)(esp_timer_get_time()/1000/1000); // in seconds +time_t getUpTime(void) +{ + return (uint32_t)(esp_timer_get_time() / 1000 / 1000); // in seconds } - -string getResetReason(void) { +string getResetReason(void) +{ std::string reasonText; - switch(esp_reset_reason()) { - case ESP_RST_POWERON: reasonText = "Power-on event (or reset button)"; break; //!< Reset due to power-on event - case ESP_RST_EXT: reasonText = "External pin"; break; //!< Reset by external pin (not applicable for ESP32) - case ESP_RST_SW: reasonText = "Via esp_restart"; break; //!< Software reset via esp_restart - case ESP_RST_PANIC: reasonText = "Exception/panic"; break; //!< Software reset due to exception/panic - case ESP_RST_INT_WDT: reasonText = "Interrupt watchdog"; break; //!< Reset (software or hardware) due to interrupt watchdog - case ESP_RST_TASK_WDT: reasonText = "Task watchdog"; break; //!< Reset due to task watchdog - case ESP_RST_WDT: reasonText = "Other watchdogs"; break; //!< Reset due to other watchdogs - case ESP_RST_DEEPSLEEP: reasonText = "Exiting deep sleep mode"; break; //!< Reset after exiting deep sleep mode - case ESP_RST_BROWNOUT: reasonText = "Brownout"; break; //!< Brownout reset (software or hardware) - case ESP_RST_SDIO: reasonText = "SDIO"; break; //!< Reset over SDIO + switch (esp_reset_reason()) + { + case ESP_RST_POWERON: + reasonText = "Power-on event (or reset button)"; + break; //!< Reset due to power-on event + case ESP_RST_EXT: + reasonText = "External pin"; + break; //!< Reset by external pin (not applicable for ESP32) + case ESP_RST_SW: + reasonText = "Via esp_restart"; + break; //!< Software reset via esp_restart + case ESP_RST_PANIC: + reasonText = "Exception/panic"; + break; //!< Software reset due to exception/panic + case ESP_RST_INT_WDT: + reasonText = "Interrupt watchdog"; + break; //!< Reset (software or hardware) due to interrupt watchdog + case ESP_RST_TASK_WDT: + reasonText = "Task watchdog"; + break; //!< Reset due to task watchdog + case ESP_RST_WDT: + reasonText = "Other watchdogs"; + break; //!< Reset due to other watchdogs + case ESP_RST_DEEPSLEEP: + reasonText = "Exiting deep sleep mode"; + break; //!< Reset after exiting deep sleep mode + case ESP_RST_BROWNOUT: + reasonText = "Brownout"; + break; //!< Brownout reset (software or hardware) + case ESP_RST_SDIO: + reasonText = "SDIO"; + break; //!< Reset over SDIO - case ESP_RST_UNKNOWN: //!< Reset reason can not be determined - default: - reasonText = "Unknown"; + case ESP_RST_UNKNOWN: //!< Reset reason can not be determined + default: + reasonText = "Unknown"; } - return reasonText; -} + return reasonText; +} /** * Returns the current uptime formated ad xxf xxh xxm [xxs] */ -std::string getFormatedUptime(bool compact) { +std::string getFormatedUptime(bool compact) +{ char buf[20]; - #pragma GCC diagnostic ignored "-Wformat-truncation" +#pragma GCC diagnostic ignored "-Wformat-truncation" - int uptime = getUpTime(); // in seconds + int uptime = getUpTime(); // in seconds - int days = int(floor(uptime / (3600*24))); - int hours = int(floor((uptime - days * 3600*24) / (3600))); - int minutes = int(floor((uptime - days * 3600*24 - hours * 3600) / (60))); - int seconds = uptime - days * 3600*24 - hours * 3600 - minutes * 60; - - if (compact) { + int days = int(floor(uptime / (3600 * 24))); + int hours = int(floor((uptime - days * 3600 * 24) / (3600))); + int minutes = int(floor((uptime - days * 3600 * 24 - hours * 3600) / (60))); + int seconds = uptime - days * 3600 * 24 - hours * 3600 - minutes * 60; + + if (compact) + { snprintf(buf, sizeof(buf), "%dd%02dh%02dm%02ds", days, hours, minutes, seconds); } - else { + else + { snprintf(buf, sizeof(buf), "%3dd %02dh %02dm %02ds", days, hours, minutes, seconds); } return std::string(buf); } - -const char* get404(void) { - return -"
\n\n\n\n"
-"        _\n"
-"    .__(.)< ( oh oh! This page does not exist! )\n"
-"    \\___)\n"
-"\n\n"
-"                You could try your luck here!
\n" -""; // Make sure we load the overview page -} - - -std::string UrlDecode(const std::string& value) +const char *get404(void) { - std::string result; - result.reserve(value.size()); - - for (std::size_t i = 0; i < value.size(); ++i) - { - auto ch = value[i]; - - if (ch == '%' && (i + 2) < value.size()) - { - auto hex = value.substr(i + 1, 2); - auto dec = static_cast(std::strtol(hex.c_str(), nullptr, 16)); - result.push_back(dec); - i += 2; - } - else if (ch == '+') - { - result.push_back(' '); - } - else - { - result.push_back(ch); - } - } - - return result; + return "
\n\n\n\n"
+		   "        _\n"
+		   "    .__(.)< ( oh oh! This page does not exist! )\n"
+		   "    \\___)\n"
+		   "\n\n"
+		   "                You could try your luck here!
\n" + ""; // Make sure we load the overview page } +std::string UrlDecode(const std::string &value) +{ + std::string result; + result.reserve(value.size()); -bool replaceString(std::string& s, std::string const& toReplace, std::string const& replaceWith) { - return replaceString(s, toReplace, replaceWith, true); + for (std::size_t i = 0; i < value.size(); ++i) + { + auto ch = value[i]; + + if (ch == '%' && (i + 2) < value.size()) + { + auto hex = value.substr(i + 1, 2); + auto dec = static_cast(std::strtol(hex.c_str(), nullptr, 16)); + result.push_back(dec); + i += 2; + } + else if (ch == '+') + { + result.push_back(' '); + } + else + { + result.push_back(ch); + } + } + + return result; } - -bool replaceString(std::string& s, std::string const& toReplace, std::string const& replaceWith, bool logIt) { - std::size_t pos = s.find(toReplace); - - if (pos == std::string::npos) { // Not found - return false; - } - - std::string old = s; - s.replace(pos, toReplace.length(), replaceWith); - if (logIt) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Migrated Configfile line '" + old + "' to '" + s + "'"); - } - return true; +bool replaceString(std::string &s, std::string const &toReplace, std::string const &replaceWith) +{ + return replaceString(s, toReplace, replaceWith, true); } +bool replaceString(std::string &s, std::string const &toReplace, std::string const &replaceWith, bool logIt) +{ + std::size_t pos = s.find(toReplace); -bool isInString(std::string& s, std::string const& toFind) { - std::size_t pos = s.find(toFind); + if (pos == std::string::npos) + { + // Not found + return false; + } - if (pos == std::string::npos) { // Not found - return false; - } - return true; + std::string old = s; + s.replace(pos, toReplace.length(), replaceWith); + + if (logIt) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Migrated Configfile line '" + old + "' to '" + s + "'"); + } + + return true; +} + +bool isInString(std::string &s, std::string const &toFind) +{ + std::size_t pos = s.find(toFind); + + if (pos == std::string::npos) + { + // Not found + return false; + } + + return true; } diff --git a/code/components/jomjol_helper/Helper.h b/code/components/jomjol_helper/Helper.h index d2457bdd..9cfdf702 100644 --- a/code/components/jomjol_helper/Helper.h +++ b/code/components/jomjol_helper/Helper.h @@ -6,6 +6,7 @@ #include #include #include + #include "sdmmc_cmd.h" using namespace std; diff --git a/code/components/jomjol_helper/sdcard_check.cpp b/code/components/jomjol_helper/sdcard_check.cpp index e8ac9d24..39c5d479 100644 --- a/code/components/jomjol_helper/sdcard_check.cpp +++ b/code/components/jomjol_helper/sdcard_check.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include diff --git a/code/components/jomjol_helper/sdcard_init.c b/code/components/jomjol_helper/sdcard_init.c new file mode 100644 index 00000000..02bac5cb --- /dev/null +++ b/code/components/jomjol_helper/sdcard_init.c @@ -0,0 +1,651 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "sdcard_init.h" + +#include "esp_log.h" +#include "ffconf.h" +#include "esp_compiler.h" +#include "esp_vfs.h" +#include "vfs_fat_internal.h" +#include "diskio_impl.h" +#include "diskio_sdmmc.h" +#include "soc/soc_caps.h" +#include "driver/sdmmc_defs.h" + +#if SOC_SDMMC_HOST_SUPPORTED + #include "driver/sdmmc_host.h" +#endif + +static sdmmc_card_t* s_cards[FF_VOLUMES] = { NULL }; +static bool s_disk_status_check_en[FF_VOLUMES] = { }; + +static const char* TAG = "sdcard_init"; + +#define CHECK_EXECUTE_RESULT(err, str) do { \ + if ((err) !=ESP_OK) { \ + ESP_LOGE(TAG, str" (0x%x).", err); \ + goto cleanup; \ + } \ + } while(0) + +typedef struct vfs_fat_sd_ctx_t { + BYTE pdrv; //Drive number that is mounted + esp_vfs_fat_mount_config_t mount_config; //Mount configuration + FATFS *fs; //FAT structure pointer that is registered + sdmmc_card_t *card; //Card info + char *base_path; //Path where partition is registered +} vfs_fat_sd_ctx_t; + +static vfs_fat_sd_ctx_t *s_ctx[FF_VOLUMES] = {}; + +/** + * This `s_saved_ctx_id` is only used by `esp_vfs_fat_sdmmc_unmount`, which is deprecated. + * This variable together with `esp_vfs_fat_sdmmc_unmount` should be removed in next major version + */ +static uint32_t s_saved_ctx_id = FF_VOLUMES; + + +static void call_host_deinit_mh(const sdmmc_host_t *host_config); +static esp_err_t partition_card_mh(const esp_vfs_fat_mount_config_t *mount_config, const char *drv, sdmmc_card_t *card, BYTE pdrv); + + +//Check if SD/MMC card is present +static DSTATUS ff_sdmmc_card_available_mh(BYTE pdrv) +{ + sdmmc_card_t* card = s_cards[pdrv]; + assert(card); + esp_err_t err = sdmmc_get_status(card); + + if (unlikely(err != ESP_OK)) { + ESP_LOGE(TAG, "Check status failed (0x%x)", err); + return STA_NOINIT; + } + return 0; +} + +/** +* ff_sdmmc_status() and ff_sdmmc_initialize() return STA_NOINIT when sdmmc_get_status() +* fails. This error value is checked throughout the FATFS code. +* Both functions return 0 on success. +*/ +DSTATUS ff_sdmmc_initialize_mh (BYTE pdrv) +{ + return ff_sdmmc_card_available_mh(pdrv); +} + +DSTATUS ff_sdmmc_status_mh(BYTE pdrv) +{ + if (s_disk_status_check_en[pdrv]) { + return ff_sdmmc_card_available_mh(pdrv); + } + return 0; +} + +DRESULT ff_sdmmc_read_mh (BYTE pdrv, BYTE* buff, DWORD sector, UINT count) +{ + sdmmc_card_t* card = s_cards[pdrv]; + assert(card); + esp_err_t err = sdmmc_read_sectors(card, buff, sector, count); + if (unlikely(err != ESP_OK)) { + ESP_LOGE(TAG, "sdmmc_read_blocks failed (%d)", err); + return RES_ERROR; + } + return RES_OK; +} + +DRESULT ff_sdmmc_write_mh (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) +{ + sdmmc_card_t* card = s_cards[pdrv]; + assert(card); + esp_err_t err = sdmmc_write_sectors(card, buff, sector, count); + if (unlikely(err != ESP_OK)) { + ESP_LOGE(TAG, "sdmmc_write_blocks failed (%d)", err); + return RES_ERROR; + } + return RES_OK; +} + +#if FF_USE_TRIM +DRESULT ff_sdmmc_trim_mh (BYTE pdrv, DWORD start_sector, DWORD sector_count) +{ + sdmmc_card_t* card = s_cards[pdrv]; + assert(card); + sdmmc_erase_arg_t arg; + + arg = sdmmc_can_discard(card) == ESP_OK ? SDMMC_DISCARD_ARG : SDMMC_ERASE_ARG; + esp_err_t err = sdmmc_erase_sectors(card, start_sector, sector_count, arg); + if (unlikely(err != ESP_OK)) { + ESP_LOGE(TAG, "sdmmc_erase_sectors failed (%d)", err); + return RES_ERROR; + } + return RES_OK; +} +#endif //FF_USE_TRIM + +DRESULT ff_sdmmc_ioctl_mh (BYTE pdrv, BYTE cmd, void* buff) +{ + sdmmc_card_t* card = s_cards[pdrv]; + assert(card); + switch(cmd) { + case CTRL_SYNC: + return RES_OK; + case GET_SECTOR_COUNT: + *((DWORD*) buff) = card->csd.capacity; + return RES_OK; + case GET_SECTOR_SIZE: + *((WORD*) buff) = card->csd.sector_size; + return RES_OK; + case GET_BLOCK_SIZE: + return RES_ERROR; +#if FF_USE_TRIM + case CTRL_TRIM: + if (sdmmc_can_trim(card) != ESP_OK) { + return RES_PARERR; + } + return ff_sdmmc_trim_mh (pdrv, *((DWORD*)buff), //start_sector + (*((DWORD*)buff + 1) - *((DWORD*)buff) + 1)); //sector_count +#endif //FF_USE_TRIM + } + return RES_ERROR; +} + +void ff_sdmmc_set_disk_status_check_mh(BYTE pdrv, bool enable) +{ + s_disk_status_check_en[pdrv] = enable; +} + +void ff_diskio_register_sdmmc_mh(BYTE pdrv, sdmmc_card_t* card) +{ + static const ff_diskio_impl_t sdmmc_impl = { + .init = &ff_sdmmc_initialize_mh, + .status = &ff_sdmmc_status_mh, + .read = &ff_sdmmc_read_mh, + .write = &ff_sdmmc_write_mh, + .ioctl = &ff_sdmmc_ioctl_mh + }; + s_cards[pdrv] = card; + s_disk_status_check_en[pdrv] = false; + ff_diskio_register(pdrv, &sdmmc_impl); +} + +BYTE ff_diskio_get_pdrv_card_mh(const sdmmc_card_t* card) +{ + for (int i = 0; i < FF_VOLUMES; i++) { + if (card == s_cards[i]) { + return i; + } + } + return 0xff; +} + + + + + + +static bool s_get_context_id_by_card_mh(const sdmmc_card_t *card, uint32_t *out_id) +{ + vfs_fat_sd_ctx_t *p_ctx = NULL; + for (int i = 0; i < FF_VOLUMES; i++) { + p_ctx = s_ctx[i]; + if (p_ctx) { + if (p_ctx->card == card) { + *out_id = i; + return true; + } + } + } + return false; +} + +static uint32_t s_get_unused_context_id_mh(void) +{ + for (uint32_t i = 0; i < FF_VOLUMES; i++) { + if (!s_ctx[i]) { + return i; + } + } + return FF_VOLUMES; +} + +static esp_err_t mount_prepare_mem_mh(const char *base_path, BYTE *out_pdrv, char **out_dup_path, sdmmc_card_t** out_card) +{ + esp_err_t err = ESP_OK; + char* dup_path = NULL; + sdmmc_card_t* card = NULL; + + // connect SDMMC driver to FATFS + BYTE pdrv = FF_DRV_NOT_USED; + + if (ff_diskio_get_drive(&pdrv) != ESP_OK || pdrv == FF_DRV_NOT_USED) { + ESP_LOGD(TAG, "the maximum count of volumes is already mounted"); + return ESP_ERR_NO_MEM; + } + + // not using ff_memalloc here, as allocation in internal RAM is preferred + card = (sdmmc_card_t*)malloc(sizeof(sdmmc_card_t)); + + if (card == NULL) { + ESP_LOGD(TAG, "could not locate new sdmmc_card_t"); + err = ESP_ERR_NO_MEM; + goto cleanup; + } + + dup_path = strdup(base_path); + + if(!dup_path){ + ESP_LOGD(TAG, "could not copy base_path"); + err = ESP_ERR_NO_MEM; + goto cleanup; + } + + *out_card = card; + *out_pdrv = pdrv; + *out_dup_path = dup_path; + return ESP_OK; +cleanup: + free(card); + free(dup_path); + return err; +} + +static esp_err_t s_f_mount_mh(sdmmc_card_t *card, FATFS *fs, const char *drv, uint8_t pdrv, const esp_vfs_fat_mount_config_t *mount_config) +{ + esp_err_t err = ESP_OK; + FRESULT res = f_mount(fs, drv, 1); + if (res != FR_OK) { + err = ESP_FAIL; + ESP_LOGW(TAG, "failed to mount card (%d)", res); + + bool need_mount_again = (res == FR_NO_FILESYSTEM || res == FR_INT_ERR) && mount_config->format_if_mount_failed; + + if (!need_mount_again) { + return ESP_FAIL; + } + + err = partition_card_mh(mount_config, drv, card, pdrv); + + if (err != ESP_OK) { + return err; + } + + ESP_LOGW(TAG, "mounting again"); + res = f_mount(fs, drv, 0); + + if (res != FR_OK) { + err = ESP_FAIL; + ESP_LOGD(TAG, "f_mount failed after formatting (%d)", res); + return err; + } + } + + return ESP_OK; +} + +static esp_err_t mount_to_vfs_fat_mh(const esp_vfs_fat_mount_config_t *mount_config, sdmmc_card_t *card, uint8_t pdrv, const char *base_path, FATFS **out_fs) +{ + FATFS *fs = NULL; + esp_err_t err; + ff_diskio_register_sdmmc_mh(pdrv, card); + ff_sdmmc_set_disk_status_check_mh(pdrv, mount_config->disk_status_check_enable); + ESP_LOGD(TAG, "using pdrv=%i", pdrv); + char drv[3] = {(char)('0' + pdrv), ':', 0}; + + // connect FATFS to VFS + err = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs); + *out_fs = fs; + + if (err == ESP_ERR_INVALID_STATE) { + // it's okay, already registered with VFS + } else if (err != ESP_OK) { + ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", err); + goto fail; + } + + // Try to mount partition + err = s_f_mount_mh(card, fs, drv, pdrv, mount_config); + + if (err != ESP_OK) { + goto fail; + } + return ESP_OK; + +fail: + if (fs) { + f_mount(NULL, drv, 0); + } + esp_vfs_fat_unregister_path(base_path); + ff_diskio_unregister(pdrv); + return err; +} + +static esp_err_t partition_card_mh(const esp_vfs_fat_mount_config_t *mount_config, const char *drv, sdmmc_card_t *card, BYTE pdrv) +{ + FRESULT res = FR_OK; + esp_err_t err; + const size_t workbuf_size = 4096; + void* workbuf = NULL; + ESP_LOGW(TAG, "partitioning card"); + + workbuf = ff_memalloc(workbuf_size); + + if (workbuf == NULL) { + return ESP_ERR_NO_MEM; + } + + LBA_t plist[] = {100, 0, 0, 0}; + res = f_fdisk(pdrv, plist, workbuf); + + if (res != FR_OK) { + err = ESP_FAIL; + ESP_LOGD(TAG, "f_fdisk failed (%d)", res); + goto fail; + } + + size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(card->csd.sector_size, mount_config->allocation_unit_size); + + ESP_LOGW(TAG, "formatting card, allocation unit size=%d", alloc_unit_size); + const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, alloc_unit_size}; + res = f_mkfs(drv, &opt, workbuf, workbuf_size); + + if (res != FR_OK) { + err = ESP_FAIL; + ESP_LOGD(TAG, "f_mkfs failed (%d)", res); + goto fail; + } + + free(workbuf); + return ESP_OK; +fail: + free(workbuf); + return err; +} + +#if SOC_SDMMC_HOST_SUPPORTED +static esp_err_t init_sdmmc_host_mh(int slot, const void *slot_config, int *out_slot) +{ + *out_slot = slot; + return sdmmc_host_init_slot(slot, (const sdmmc_slot_config_t*) slot_config); +} + +esp_err_t esp_vfs_fat_sdmmc_mount_mh(const char* base_path, const sdmmc_host_t* host_config, const void* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card) +{ + esp_err_t err; + vfs_fat_sd_ctx_t *ctx = NULL; + uint32_t ctx_id = FF_VOLUMES; + FATFS *fs = NULL; + int card_handle = -1; //uninitialized + sdmmc_card_t* card = NULL; + BYTE pdrv = FF_DRV_NOT_USED; + char* dup_path = NULL; + bool host_inited = false; + + err = mount_prepare_mem_mh(base_path, &pdrv, &dup_path, &card); + + if (err != ESP_OK) { + ESP_LOGE(TAG, "mount_prepare failed"); + return err; + } + + err = (*host_config->init)(); + CHECK_EXECUTE_RESULT(err, "host init failed"); + //deinit() needs to be called to revert the init + host_inited = true; + //If this failed (indicated by card_handle != -1), slot deinit needs to called() + //leave card_handle as is to indicate that (though slot deinit not implemented yet. + err = init_sdmmc_host_mh(host_config->slot, slot_config, &card_handle); + CHECK_EXECUTE_RESULT(err, "slot init failed"); + + // probe and initialize card + err = sdmmc_card_init(host_config, card); + CHECK_EXECUTE_RESULT(err, "sdmmc_card_init failed"); + + err = mount_to_vfs_fat_mh(mount_config, card, pdrv, dup_path, &fs); + CHECK_EXECUTE_RESULT(err, "mount_to_vfs failed"); + + if (out_card != NULL) { + *out_card = card; + } + + //For deprecation backward compatibility + if (s_saved_ctx_id == FF_VOLUMES) { + s_saved_ctx_id = 0; + } + + ctx = calloc(sizeof(vfs_fat_sd_ctx_t), 1); + + if (!ctx) { + CHECK_EXECUTE_RESULT(ESP_ERR_NO_MEM, "no mem"); + } + + ctx->pdrv = pdrv; + memcpy(&ctx->mount_config, mount_config, sizeof(esp_vfs_fat_mount_config_t)); + ctx->card = card; + ctx->base_path = dup_path; + ctx->fs = fs; + ctx_id = s_get_unused_context_id_mh(); + assert(ctx_id != FF_VOLUMES); + s_ctx[ctx_id] = ctx; + + return ESP_OK; +cleanup: + if (host_inited) { + call_host_deinit_mh(host_config); + } + + free(card); + free(dup_path); + return err; +} +#endif + +static esp_err_t init_sdspi_host_mh(int slot, const void *slot_config, int *out_slot) +{ + esp_err_t err = sdspi_host_init_device((const sdspi_device_config_t*)slot_config, out_slot); + if (err != ESP_OK) { + ESP_LOGE(TAG, +"Failed to attach sdspi device onto an SPI bus (rc=0x%x), please initialize the \ +bus first and check the device parameters." + , err); + } + return err; +} + +esp_err_t esp_vfs_fat_sdspi_mount_mh(const char* base_path, const sdmmc_host_t* host_config_input, const sdspi_device_config_t* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card) +{ + const sdmmc_host_t* host_config = host_config_input; + esp_err_t err; + vfs_fat_sd_ctx_t *ctx = NULL; + uint32_t ctx_id = FF_VOLUMES; + FATFS *fs = NULL; + int card_handle = -1; //uninitialized + bool host_inited = false; + BYTE pdrv = FF_DRV_NOT_USED; + sdmmc_card_t* card = NULL; + char* dup_path = NULL; + + err = mount_prepare_mem_mh(base_path, &pdrv, &dup_path, &card); + if (err != ESP_OK) { + ESP_LOGE(TAG, "mount_prepare failed"); + return err; + } + + //the init() function is usually empty, doesn't require any deinit to revert it + err = (*host_config->init)(); + CHECK_EXECUTE_RESULT(err, "host init failed"); + + err = init_sdspi_host_mh(host_config->slot, slot_config, &card_handle); + CHECK_EXECUTE_RESULT(err, "slot init failed"); + //Set `host_inited` to true to indicate that host_config->deinit() needs + //to be called to revert `init_sdspi_host` + host_inited = true; + + //The `slot` argument inside host_config should be replaced by the SD SPI handled returned + //above. But the input pointer is const, so create a new variable. + + sdmmc_host_t new_config; + + if (card_handle != host_config->slot) { + new_config = *host_config_input; + host_config = &new_config; + new_config.slot = card_handle; + } + + // probe and initialize card + err = sdmmc_card_init(host_config, card); + CHECK_EXECUTE_RESULT(err, "sdmmc_card_init failed"); + + err = mount_to_vfs_fat_mh(mount_config, card, pdrv, dup_path, &fs); + CHECK_EXECUTE_RESULT(err, "mount_to_vfs failed"); + + if (out_card != NULL) { + *out_card = card; + } + + //For deprecation backward compatibility + if (s_saved_ctx_id == FF_VOLUMES) { + s_saved_ctx_id = 0; + } + + ctx = calloc(sizeof(vfs_fat_sd_ctx_t), 1); + + if (!ctx) { + CHECK_EXECUTE_RESULT(ESP_ERR_NO_MEM, "no mem"); + } + + ctx->pdrv = pdrv; + memcpy(&ctx->mount_config, mount_config, sizeof(esp_vfs_fat_mount_config_t)); + ctx->card = card; + ctx->base_path = dup_path; + ctx->fs = fs; + ctx_id = s_get_unused_context_id_mh(); + assert(ctx_id != FF_VOLUMES); + s_ctx[ctx_id] = ctx; + + return ESP_OK; + +cleanup: + if (host_inited) { + call_host_deinit_mh(host_config); + } + + free(card); + free(dup_path); + return err; +} + +static void call_host_deinit_mh(const sdmmc_host_t *host_config) +{ + if (host_config->flags & SDMMC_HOST_FLAG_DEINIT_ARG) { + host_config->deinit_p(host_config->slot); + } else { + host_config->deinit(); + } +} + +static esp_err_t unmount_card_core_mh(const char *base_path, sdmmc_card_t *card) +{ + BYTE pdrv = ff_diskio_get_pdrv_card_mh(card); + + if (pdrv == 0xff) { + return ESP_ERR_INVALID_ARG; + } + + // unmount + char drv[3] = {(char)('0' + pdrv), ':', 0}; + f_mount(0, drv, 0); + // release SD driver + ff_diskio_unregister(pdrv); + + call_host_deinit_mh(&card->host); + free(card); + + esp_err_t err = esp_vfs_fat_unregister_path(base_path); + return err; +} + +esp_err_t esp_vfs_fat_sdmmc_unmount_mh(void) +{ + esp_err_t err = unmount_card_core_mh(s_ctx[s_saved_ctx_id]->base_path, s_ctx[s_saved_ctx_id]->card); + free(s_ctx[s_saved_ctx_id]); + s_ctx[s_saved_ctx_id] = NULL; + s_saved_ctx_id = FF_VOLUMES; + return err; +} + +esp_err_t esp_vfs_fat_sdcard_unmount_mh(const char *base_path, sdmmc_card_t *card) +{ + uint32_t id = FF_VOLUMES; + bool found = s_get_context_id_by_card_mh(card, &id); + + if (!found) { + return ESP_ERR_INVALID_ARG; + } + + free(s_ctx[id]); + s_ctx[id] = NULL; + + esp_err_t err = unmount_card_core_mh(base_path, card); + + return err; +} + +esp_err_t esp_vfs_fat_sdcard_format_mh(const char *base_path, sdmmc_card_t *card) +{ + esp_err_t ret = ESP_OK; + + if (!card) { + ESP_LOGE(TAG, "card not initialized"); + return ESP_ERR_INVALID_STATE; + } + + BYTE pdrv = ff_diskio_get_pdrv_card_mh(card); + + if (pdrv == 0xff) { + ESP_LOGE(TAG, "card driver not registered"); + return ESP_ERR_INVALID_STATE; + } + + const size_t workbuf_size = 4096; + void *workbuf = ff_memalloc(workbuf_size); + + if (workbuf == NULL) { + return ESP_ERR_NO_MEM; + } + + //unmount + char drv[3] = {(char)('0' + pdrv), ':', 0}; + f_mount(0, drv, 0); + + //format + uint32_t id = FF_VOLUMES; + bool found = s_get_context_id_by_card_mh(card, &id); + assert(found); + size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(card->csd.sector_size, s_ctx[id]->mount_config.allocation_unit_size); + ESP_LOGI(TAG, "Formatting card, allocation unit size=%d", alloc_unit_size); + const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, alloc_unit_size}; + FRESULT res = f_mkfs(drv, &opt, workbuf, workbuf_size); + free(workbuf); + + if (res != FR_OK) { + ret = ESP_FAIL; + ESP_LOGD(TAG, "f_mkfs failed (%d)", res); + } + + //mount back + esp_err_t err = s_f_mount_mh(card, s_ctx[id]->fs, drv, pdrv, &s_ctx[id]->mount_config); + + if (err != ESP_OK) { + unmount_card_core_mh(base_path, card); + ESP_LOGE(TAG, "failed to format, resources recycled, please mount again"); + } + + return ret; +} diff --git a/code/components/jomjol_helper/sdcard_init.h b/code/components/jomjol_helper/sdcard_init.h new file mode 100644 index 00000000..bed3f2f3 --- /dev/null +++ b/code/components/jomjol_helper/sdcard_init.h @@ -0,0 +1,111 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_err.h" +#include "driver/gpio.h" +#include "sdmmc_cmd.h" +#include "driver/sdmmc_types.h" +#include "driver/sdspi_host.h" +#include "ff.h" +#include "esp_vfs_fat.h" +#include "wear_levelling.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Convenience function to get FAT filesystem on SD card registered in VFS + * + * This is an all-in-one function which does the following: + * - initializes SDMMC driver or SPI driver with configuration in host_config + * - initializes SD card with configuration in slot_config + * - mounts FAT partition on SD card using FATFS library, with configuration in mount_config + * - registers FATFS library with VFS, with prefix given by base_prefix variable + * + * This function is intended to make example code more compact. + * For real world applications, developers should implement the logic of + * probing SD card, locating and mounting partition, and registering FATFS in VFS, + * with proper error checking and handling of exceptional conditions. + * + * @note Use this API to mount a card through SDSPI is deprecated. Please call + * `esp_vfs_fat_sdspi_mount()` instead for that case. + * + * @param base_path path where partition should be registered (e.g. "/sdcard") + * @param host_config Pointer to structure describing SDMMC host. When using + * SDMMC peripheral, this structure can be initialized using + * SDMMC_HOST_DEFAULT() macro. When using SPI peripheral, + * this structure can be initialized using SDSPI_HOST_DEFAULT() + * macro. + * @param slot_config Pointer to structure with slot configuration. + * For SDMMC peripheral, pass a pointer to sdmmc_slot_config_t + * structure initialized using SDMMC_SLOT_CONFIG_DEFAULT. + * @param mount_config pointer to structure with extra parameters for mounting FATFS + * @param[out] out_card if not NULL, pointer to the card information structure will be returned via this argument + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called + * - ESP_ERR_NO_MEM if memory can not be allocated + * - ESP_FAIL if partition can not be mounted + * - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers + */ +esp_err_t esp_vfs_fat_sdmmc_mount_mh(const char* base_path, const sdmmc_host_t* host_config, const void* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card); + +/** + * @brief Convenience function to get FAT filesystem on SD card registered in VFS + * + * This is an all-in-one function which does the following: + * - initializes an SPI Master device based on the SPI Master driver with configuration in + * slot_config, and attach it to an initialized SPI bus. + * - initializes SD card with configuration in host_config_input + * - mounts FAT partition on SD card using FATFS library, with configuration in mount_config + * - registers FATFS library with VFS, with prefix given by base_prefix variable + * + * This function is intended to make example code more compact. + * For real world applications, developers should implement the logic of + * probing SD card, locating and mounting partition, and registering FATFS in VFS, + * with proper error checking and handling of exceptional conditions. + * + * @note This function try to attach the new SD SPI device to the bus specified in host_config. + * Make sure the SPI bus specified in `host_config->slot` have been initialized by + * `spi_bus_initialize()` before. + * + * @param base_path path where partition should be registered (e.g. "/sdcard") + * @param host_config_input Pointer to structure describing SDMMC host. This structure can be + * initialized using SDSPI_HOST_DEFAULT() macro. + * @param slot_config Pointer to structure with slot configuration. + * For SPI peripheral, pass a pointer to sdspi_device_config_t + * structure initialized using SDSPI_DEVICE_CONFIG_DEFAULT(). + * @param mount_config pointer to structure with extra parameters for mounting FATFS + * @param[out] out_card If not NULL, pointer to the card information structure will be returned via + * this argument. It is suggested to hold this handle and use it to unmount the card later if + * needed. Otherwise it's not suggested to use more than one card at the same time and unmount one + * of them in your application. + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called + * - ESP_ERR_NO_MEM if memory can not be allocated + * - ESP_FAIL if partition can not be mounted + * - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers + */ +esp_err_t esp_vfs_fat_sdspi_mount_mh(const char* base_path, const sdmmc_host_t* host_config_input, const sdspi_device_config_t* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card); + +#ifdef __cplusplus +} +#endif + + + + + + + + + + diff --git a/code/components/jomjol_image_proc/CImageBasis.cpp b/code/components/jomjol_image_proc/CImageBasis.cpp index a61ca7cd..8038b01e 100644 --- a/code/components/jomjol_image_proc/CImageBasis.cpp +++ b/code/components/jomjol_image_proc/CImageBasis.cpp @@ -443,7 +443,7 @@ void CImageBasis::LoadFromMemory(stbi_uc *_buffer, int len) //free_psram_heap(std::string(TAG) + "->rgb_image (LoadFromMemory)", rgb_image); } - rgb_image = stbi_load_from_memory(_buffer, len, &width, &height, &channels, 3); + rgb_image = stbi_load_from_memory(_buffer, len, &width, &height, &channels, STBI_rgb); bpp = channels; ESP_LOGD(TAG, "Image loaded from memory: %d, %d, %d", width, height, channels); @@ -459,6 +459,44 @@ void CImageBasis::LoadFromMemory(stbi_uc *_buffer, int len) } +void CImageBasis::crop_image(unsigned short cropLeft, unsigned short cropRight, unsigned short cropTop, unsigned short cropBottom) +{ + unsigned int maxTopIndex = cropTop * width * channels; + unsigned int minBottomIndex = ((width*height) - (cropBottom * width)) * channels; + unsigned short maxX = width - cropRight; // In pixels + unsigned short newWidth = width - cropLeft - cropRight; + unsigned short newHeight = height - cropTop - cropBottom; + + unsigned int writeIndex = 0; + // Loop over all bytes + for (int i = 0; i < width * height * channels; i += channels) { + // Calculate current X, Y pixel position + int x = (i/channels) % width; + + // Crop from the top + if (i < maxTopIndex) { continue; } + + // Crop from the bottom + if (i > minBottomIndex) { continue; } + + // Crop from the left + if (x <= cropLeft) { continue; } + + // Crop from the right + if (x > maxX) { continue; } + + // If we get here, keep the pixels + for (int c = 0; c < channels; c++) { + rgb_image[writeIndex++] = rgb_image[i+c]; + } + } + + // Set the new dimensions of the framebuffer for further use. + width = newWidth; + height = newHeight; +} + + CImageBasis::CImageBasis(string _name, CImageBasis *_copyfrom) { name = _name; @@ -598,6 +636,20 @@ CImageBasis::CImageBasis(string _name, uint8_t* _rgb_image, int _channels, int _ } +void CImageBasis::Negative(void) +{ + RGBImageLock(); + + for (int i = 0; i < width * height * channels; i += channels) { + for (int c = 0; c < channels; c++) { + rgb_image[i+c] = 255 - rgb_image[i+c]; + } + } + + RGBImageRelease(); +} + + void CImageBasis::Contrast(float _contrast) //input range [-100..100] { stbi_uc* p_source; diff --git a/code/components/jomjol_image_proc/CImageBasis.h b/code/components/jomjol_image_proc/CImageBasis.h index 82edbe35..ca9ba14b 100644 --- a/code/components/jomjol_image_proc/CImageBasis.h +++ b/code/components/jomjol_image_proc/CImageBasis.h @@ -56,6 +56,7 @@ class CImageBasis void drawEllipse(int x1, int y1, int radx, int rady, int r, int g, int b, int thickness = 1); void setPixelColor(int x, int y, int r, int g, int b); + void Negative(void); void Contrast(float _contrast); bool ImageOkay(); bool CopyFromMemory(uint8_t* _source, int _size); @@ -74,6 +75,7 @@ class CImageBasis void Resize(int _new_dx, int _new_dy); void Resize(int _new_dx, int _new_dy, CImageBasis *_target); + void crop_image(unsigned short cropLeft, unsigned short cropRight, unsigned short cropTop, unsigned short cropBottom); void LoadFromMemory(stbi_uc *_buffer, int len); diff --git a/code/components/jomjol_image_proc/CRotateImage.cpp b/code/components/jomjol_image_proc/CRotateImage.cpp index 12d44154..bd1c91e7 100644 --- a/code/components/jomjol_image_proc/CRotateImage.cpp +++ b/code/components/jomjol_image_proc/CRotateImage.cpp @@ -1,353 +1,309 @@ -#include -#include "CRotateImage.h" -#include "psram.h" - -static const char *TAG = "C ROTATE IMG"; - -CRotateImage::CRotateImage(std::string _name, CImageBasis *_org, CImageBasis *_temp, bool _flip) : CImageBasis(_name) -{ - rgb_image = _org->rgb_image; - channels = _org->channels; - width = _org->width; - height = _org->height; - bpp = _org->bpp; - externalImage = true; - ImageTMP = _temp; - ImageOrg = _org; - islocked = false; - doflip = _flip; -} - - -void CRotateImage::Mirror(){ - int memsize = width * height * channels; - uint8_t* odata; - if (ImageTMP) - { - odata = ImageTMP->RGBImageLock(); - } - else - { - odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM); - } - - - int x_source, y_source; - stbi_uc* p_target; - stbi_uc* p_source; - - RGBImageLock(); - - for (int x = 0; x < width; ++x) - for (int y = 0; y < height; ++y) - { - p_target = odata + (channels * (y * width + x)); - - x_source = width - x; - y_source = y; - - p_source = rgb_image + (channels * (y_source * width + x_source)); - for (int _channels = 0; _channels < channels; ++_channels) - p_target[_channels] = p_source[_channels]; - } - - // memcpy(rgb_image, odata, memsize); - memCopy(odata, rgb_image, memsize); - if (!ImageTMP) - free_psram_heap(std::string(TAG) + "->odata", odata); - - if (ImageTMP) - ImageTMP->RGBImageRelease(); - - RGBImageRelease(); -} - -void CRotateImage::Rotate(float _angle, int _centerx, int _centery) -{ - int org_width, org_height; - float m[2][3]; - - float x_center = _centerx; - float y_center = _centery; - _angle = _angle / 180 * M_PI; - - if (doflip) - { - org_width = width; - org_height = height; - height = org_width; - width = org_height; - x_center = x_center - (org_width/2) + (org_height/2); - y_center = y_center + (org_width/2) - (org_height/2); - if (ImageOrg) - { - ImageOrg->height = height; - ImageOrg->width = width; - } - } - else - { - org_width = width; - org_height = height; - } - - m[0][0] = cos(_angle); - m[0][1] = sin(_angle); - m[0][2] = (1 - m[0][0]) * x_center - m[0][1] * y_center; - - m[1][0] = -m[0][1]; - m[1][1] = m[0][0]; - m[1][2] = m[0][1] * x_center + (1 - m[0][0]) * y_center; - - if (doflip) - { - m[0][2] = m[0][2] + (org_width/2) - (org_height/2); - m[1][2] = m[1][2] - (org_width/2) + (org_height/2); - } - - int memsize = width * height * channels; - uint8_t* odata; - if (ImageTMP) - { - odata = ImageTMP->RGBImageLock(); - } - else - { - odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM); - } - - - int x_source, y_source; - stbi_uc* p_target; - stbi_uc* p_source; - - RGBImageLock(); - - for (int x = 0; x < width; ++x) - for (int y = 0; y < height; ++y) - { - p_target = odata + (channels * (y * width + x)); - - x_source = int(m[0][0] * x + m[0][1] * y); - y_source = int(m[1][0] * x + m[1][1] * y); - - x_source += int(m[0][2]); - y_source += int(m[1][2]); - - if ((x_source >= 0) && (x_source < org_width) && (y_source >= 0) && (y_source < org_height)) - { - p_source = rgb_image + (channels * (y_source * org_width + x_source)); - for (int _channels = 0; _channels < channels; ++_channels) - p_target[_channels] = p_source[_channels]; - } - else - { - for (int _channels = 0; _channels < channels; ++_channels) - p_target[_channels] = 255; - } - } - - // memcpy(rgb_image, odata, memsize); - memCopy(odata, rgb_image, memsize); - - if (!ImageTMP) - { - free_psram_heap(std::string(TAG) + "->odata", odata); - } - if (ImageTMP) - ImageTMP->RGBImageRelease(); - - RGBImageRelease(); -} - - - -void CRotateImage::RotateAntiAliasing(float _angle, int _centerx, int _centery) -{ - int org_width, org_height; - float m[2][3]; - - float x_center = _centerx; - float y_center = _centery; - _angle = _angle / 180 * M_PI; - - if (doflip) - { - org_width = width; - org_height = height; - height = org_width; - width = org_height; - x_center = x_center - (org_width/2) + (org_height/2); - y_center = y_center + (org_width/2) - (org_height/2); - if (ImageOrg) - { - ImageOrg->height = height; - ImageOrg->width = width; - } - } - else - { - org_width = width; - org_height = height; - } - - m[0][0] = cos(_angle); - m[0][1] = sin(_angle); - m[0][2] = (1 - m[0][0]) * x_center - m[0][1] * y_center; - - m[1][0] = -m[0][1]; - m[1][1] = m[0][0]; - m[1][2] = m[0][1] * x_center + (1 - m[0][0]) * y_center; - - if (doflip) - { - m[0][2] = m[0][2] + (org_width/2) - (org_height/2); - m[1][2] = m[1][2] - (org_width/2) + (org_height/2); - } - - int memsize = width * height * channels; - uint8_t* odata; - if (ImageTMP) - { - odata = ImageTMP->RGBImageLock(); - } - else - { - odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM); - } - - - int x_source_1, y_source_1, x_source_2, y_source_2; - float x_source, y_source; - float quad_ul, quad_ur, quad_ol, quad_or; - stbi_uc* p_target; - stbi_uc *p_source_ul, *p_source_ur, *p_source_ol, *p_source_or; - - RGBImageLock(); - - for (int x = 0; x < width; ++x) - for (int y = 0; y < height; ++y) - { - p_target = odata + (channels * (y * width + x)); - - x_source = (m[0][0] * x + m[0][1] * y); - y_source = (m[1][0] * x + m[1][1] * y); - - x_source += (m[0][2]); - y_source += (m[1][2]); - - x_source_1 = (int)x_source; - x_source_2 = x_source_1 + 1; - y_source_1 = (int)y_source; - y_source_2 = y_source_1 + 1; - - quad_ul = (x_source_2 - x_source) * (y_source_2 - y_source); - quad_ur = (1- (x_source_2 - x_source)) * (y_source_2 - y_source); - quad_or = (x_source_2 - x_source) * (1-(y_source_2 - y_source)); - quad_ol = (1- (x_source_2 - x_source)) * (1-(y_source_2 - y_source)); - - - if ((x_source_1 >= 0) && (x_source_2 < org_width) && (y_source_1 >= 0) && (y_source_2 < org_height)) - { - p_source_ul = rgb_image + (channels * (y_source_1 * org_width + x_source_1)); - p_source_ur = rgb_image + (channels * (y_source_1 * org_width + x_source_2)); - p_source_or = rgb_image + (channels * (y_source_2 * org_width + x_source_1)); - p_source_ol = rgb_image + (channels * (y_source_2 * org_width + x_source_2)); - for (int _channels = 0; _channels < channels; ++_channels) - { - p_target[_channels] = (int)((float)p_source_ul[_channels] * quad_ul - + (float)p_source_ur[_channels] * quad_ur - + (float)p_source_or[_channels] * quad_or - + (float)p_source_ol[_channels] * quad_ol); - } - } - else - { - for (int _channels = 0; _channels < channels; ++_channels) - p_target[_channels] = 255; - } - } - - // memcpy(rgb_image, odata, memsize); - memCopy(odata, rgb_image, memsize); - - if (!ImageTMP) - { - free_psram_heap(std::string(TAG) + "->odata", odata); - } - if (ImageTMP) - ImageTMP->RGBImageRelease(); - - RGBImageRelease(); -} - - -void CRotateImage::Rotate(float _angle) -{ -// ESP_LOGD(TAG, "width %d, height %d", width, height); - Rotate(_angle, width / 2, height / 2); -} - -void CRotateImage::RotateAntiAliasing(float _angle) -{ -// ESP_LOGD(TAG, "width %d, height %d", width, height); - RotateAntiAliasing(_angle, width / 2, height / 2); -} - -void CRotateImage::Translate(int _dx, int _dy) -{ - int memsize = width * height * channels; - uint8_t* odata; - if (ImageTMP) - { - odata = ImageTMP->RGBImageLock(); - } - else - { - odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM); - } - - - - int x_source, y_source; - stbi_uc* p_target; - stbi_uc* p_source; - - RGBImageLock(); - - for (int x = 0; x < width; ++x) - for (int y = 0; y < height; ++y) - { - p_target = odata + (channels * (y * width + x)); - - x_source = x - _dx; - y_source = y - _dy; - - if ((x_source >= 0) && (x_source < width) && (y_source >= 0) && (y_source < height)) - { - p_source = rgb_image + (channels * (y_source * width + x_source)); - for (int _channels = 0; _channels < channels; ++_channels) - p_target[_channels] = p_source[_channels]; - } - else - { - for (int _channels = 0; _channels < channels; ++_channels) - p_target[_channels] = 255; - } - } - - // memcpy(rgb_image, odata, memsize); - memCopy(odata, rgb_image, memsize); - if (!ImageTMP) - { - free_psram_heap(std::string(TAG) + "->odata", odata); - } - - if (ImageTMP) - { - ImageTMP->RGBImageRelease(); - } - RGBImageRelease(); - -} - +#include +#include "CRotateImage.h" +#include "psram.h" + +static const char *TAG = "C ROTATE IMG"; + +CRotateImage::CRotateImage(std::string _name, CImageBasis *_org, CImageBasis *_temp, bool _flip) : CImageBasis(_name) +{ + rgb_image = _org->rgb_image; + channels = _org->channels; + width = _org->width; + height = _org->height; + bpp = _org->bpp; + externalImage = true; + ImageTMP = _temp; + ImageOrg = _org; + islocked = false; + doflip = _flip; +} + +void CRotateImage::Rotate(float _angle, int _centerx, int _centery) +{ + int org_width, org_height; + float m[2][3]; + + float x_center = _centerx; + float y_center = _centery; + _angle = _angle / 180 * M_PI; + + if (doflip) + { + org_width = width; + org_height = height; + height = org_width; + width = org_height; + x_center = x_center - (org_width/2) + (org_height/2); + y_center = y_center + (org_width/2) - (org_height/2); + if (ImageOrg) + { + ImageOrg->height = height; + ImageOrg->width = width; + } + } + else + { + org_width = width; + org_height = height; + } + + m[0][0] = cos(_angle); + m[0][1] = sin(_angle); + m[0][2] = (1 - m[0][0]) * x_center - m[0][1] * y_center; + + m[1][0] = -m[0][1]; + m[1][1] = m[0][0]; + m[1][2] = m[0][1] * x_center + (1 - m[0][0]) * y_center; + + if (doflip) + { + m[0][2] = m[0][2] + (org_width/2) - (org_height/2); + m[1][2] = m[1][2] - (org_width/2) + (org_height/2); + } + + int memsize = width * height * channels; + uint8_t* odata; + if (ImageTMP) + { + odata = ImageTMP->RGBImageLock(); + } + else + { + odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM); + } + + + int x_source, y_source; + stbi_uc* p_target; + stbi_uc* p_source; + + RGBImageLock(); + + for (int x = 0; x < width; ++x) + for (int y = 0; y < height; ++y) + { + p_target = odata + (channels * (y * width + x)); + + x_source = int(m[0][0] * x + m[0][1] * y); + y_source = int(m[1][0] * x + m[1][1] * y); + + x_source += int(m[0][2]); + y_source += int(m[1][2]); + + if ((x_source >= 0) && (x_source < org_width) && (y_source >= 0) && (y_source < org_height)) + { + p_source = rgb_image + (channels * (y_source * org_width + x_source)); + for (int _channels = 0; _channels < channels; ++_channels) + p_target[_channels] = p_source[_channels]; + } + else + { + for (int _channels = 0; _channels < channels; ++_channels) + p_target[_channels] = 255; + } + } + + // memcpy(rgb_image, odata, memsize); + memCopy(odata, rgb_image, memsize); + + if (!ImageTMP) + { + free_psram_heap(std::string(TAG) + "->odata", odata); + } + if (ImageTMP) + ImageTMP->RGBImageRelease(); + + RGBImageRelease(); +} + + + +void CRotateImage::RotateAntiAliasing(float _angle, int _centerx, int _centery) +{ + int org_width, org_height; + float m[2][3]; + + float x_center = _centerx; + float y_center = _centery; + _angle = _angle / 180 * M_PI; + + if (doflip) + { + org_width = width; + org_height = height; + height = org_width; + width = org_height; + x_center = x_center - (org_width/2) + (org_height/2); + y_center = y_center + (org_width/2) - (org_height/2); + if (ImageOrg) + { + ImageOrg->height = height; + ImageOrg->width = width; + } + } + else + { + org_width = width; + org_height = height; + } + + m[0][0] = cos(_angle); + m[0][1] = sin(_angle); + m[0][2] = (1 - m[0][0]) * x_center - m[0][1] * y_center; + + m[1][0] = -m[0][1]; + m[1][1] = m[0][0]; + m[1][2] = m[0][1] * x_center + (1 - m[0][0]) * y_center; + + if (doflip) + { + m[0][2] = m[0][2] + (org_width/2) - (org_height/2); + m[1][2] = m[1][2] - (org_width/2) + (org_height/2); + } + + int memsize = width * height * channels; + uint8_t* odata; + if (ImageTMP) + { + odata = ImageTMP->RGBImageLock(); + } + else + { + odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM); + } + + + int x_source_1, y_source_1, x_source_2, y_source_2; + float x_source, y_source; + float quad_ul, quad_ur, quad_ol, quad_or; + stbi_uc* p_target; + stbi_uc *p_source_ul, *p_source_ur, *p_source_ol, *p_source_or; + + RGBImageLock(); + + for (int x = 0; x < width; ++x) + for (int y = 0; y < height; ++y) + { + p_target = odata + (channels * (y * width + x)); + + x_source = (m[0][0] * x + m[0][1] * y); + y_source = (m[1][0] * x + m[1][1] * y); + + x_source += (m[0][2]); + y_source += (m[1][2]); + + x_source_1 = (int)x_source; + x_source_2 = x_source_1 + 1; + y_source_1 = (int)y_source; + y_source_2 = y_source_1 + 1; + + quad_ul = (x_source_2 - x_source) * (y_source_2 - y_source); + quad_ur = (1- (x_source_2 - x_source)) * (y_source_2 - y_source); + quad_or = (x_source_2 - x_source) * (1-(y_source_2 - y_source)); + quad_ol = (1- (x_source_2 - x_source)) * (1-(y_source_2 - y_source)); + + + if ((x_source_1 >= 0) && (x_source_2 < org_width) && (y_source_1 >= 0) && (y_source_2 < org_height)) + { + p_source_ul = rgb_image + (channels * (y_source_1 * org_width + x_source_1)); + p_source_ur = rgb_image + (channels * (y_source_1 * org_width + x_source_2)); + p_source_or = rgb_image + (channels * (y_source_2 * org_width + x_source_1)); + p_source_ol = rgb_image + (channels * (y_source_2 * org_width + x_source_2)); + for (int _channels = 0; _channels < channels; ++_channels) + { + p_target[_channels] = (int)((float)p_source_ul[_channels] * quad_ul + + (float)p_source_ur[_channels] * quad_ur + + (float)p_source_or[_channels] * quad_or + + (float)p_source_ol[_channels] * quad_ol); + } + } + else + { + for (int _channels = 0; _channels < channels; ++_channels) + p_target[_channels] = 255; + } + } + + // memcpy(rgb_image, odata, memsize); + memCopy(odata, rgb_image, memsize); + + if (!ImageTMP) + { + free_psram_heap(std::string(TAG) + "->odata", odata); + } + if (ImageTMP) + ImageTMP->RGBImageRelease(); + + RGBImageRelease(); +} + + +void CRotateImage::Rotate(float _angle) +{ +// ESP_LOGD(TAG, "width %d, height %d", width, height); + Rotate(_angle, width / 2, height / 2); +} + +void CRotateImage::RotateAntiAliasing(float _angle) +{ +// ESP_LOGD(TAG, "width %d, height %d", width, height); + RotateAntiAliasing(_angle, width / 2, height / 2); +} + +void CRotateImage::Translate(int _dx, int _dy) +{ + int memsize = width * height * channels; + uint8_t* odata; + if (ImageTMP) + { + odata = ImageTMP->RGBImageLock(); + } + else + { + odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM); + } + + + + int x_source, y_source; + stbi_uc* p_target; + stbi_uc* p_source; + + RGBImageLock(); + + for (int x = 0; x < width; ++x) + for (int y = 0; y < height; ++y) + { + p_target = odata + (channels * (y * width + x)); + + x_source = x - _dx; + y_source = y - _dy; + + if ((x_source >= 0) && (x_source < width) && (y_source >= 0) && (y_source < height)) + { + p_source = rgb_image + (channels * (y_source * width + x_source)); + for (int _channels = 0; _channels < channels; ++_channels) + p_target[_channels] = p_source[_channels]; + } + else + { + for (int _channels = 0; _channels < channels; ++_channels) + p_target[_channels] = 255; + } + } + + // memcpy(rgb_image, odata, memsize); + memCopy(odata, rgb_image, memsize); + if (!ImageTMP) + { + free_psram_heap(std::string(TAG) + "->odata", odata); + } + + if (ImageTMP) + { + ImageTMP->RGBImageRelease(); + } + RGBImageRelease(); + +} + diff --git a/code/components/jomjol_image_proc/CRotateImage.h b/code/components/jomjol_image_proc/CRotateImage.h index 30c758d3..6b7b542e 100644 --- a/code/components/jomjol_image_proc/CRotateImage.h +++ b/code/components/jomjol_image_proc/CRotateImage.h @@ -1,29 +1,28 @@ -#pragma once - -#ifndef CROTATEIMAGE_H -#define CROTATEIMAGE_H - -#include "CImageBasis.h" - - -class CRotateImage: public CImageBasis -{ - - public: - CImageBasis *ImageTMP, *ImageOrg; - bool doflip; - CRotateImage(std::string name, std::string _image, bool _flip = false) : CImageBasis(name, _image) {ImageTMP = NULL; ImageOrg = NULL; doflip = _flip;}; - CRotateImage(std::string name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp, bool _flip = false) : CImageBasis(name, _rgb_image, _channels, _width, _height, _bpp) {ImageTMP = NULL; ImageOrg = NULL; doflip = _flip;}; - CRotateImage(std::string name, CImageBasis *_org, CImageBasis *_temp, bool _flip = false); - - void Rotate(float _angle); - void RotateAntiAliasing(float _angle); - - void Rotate(float _angle, int _centerx, int _centery); - void RotateAntiAliasing(float _angle, int _centerx, int _centery); - - void Translate(int _dx, int _dy); - void Mirror(); -}; - +#pragma once + +#ifndef CROTATEIMAGE_H +#define CROTATEIMAGE_H + +#include "CImageBasis.h" + + +class CRotateImage: public CImageBasis +{ + + public: + CImageBasis *ImageTMP, *ImageOrg; + bool doflip; + CRotateImage(std::string name, std::string _image, bool _flip = false) : CImageBasis(name, _image) {ImageTMP = NULL; ImageOrg = NULL; doflip = _flip;}; + CRotateImage(std::string name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp, bool _flip = false) : CImageBasis(name, _rgb_image, _channels, _width, _height, _bpp) {ImageTMP = NULL; ImageOrg = NULL; doflip = _flip;}; + CRotateImage(std::string name, CImageBasis *_org, CImageBasis *_temp, bool _flip = false); + + void Rotate(float _angle); + void RotateAntiAliasing(float _angle); + + void Rotate(float _angle, int _centerx, int _centery); + void RotateAntiAliasing(float _angle, int _centerx, int _centery); + + void Translate(int _dx, int _dy); +}; + #endif //CROTATEIMAGE_H \ No newline at end of file diff --git a/code/components/jomjol_influxdb/interface_influxdb.cpp b/code/components/jomjol_influxdb/interface_influxdb.cpp index dfad9320..79c4b8d7 100644 --- a/code/components/jomjol_influxdb/interface_influxdb.cpp +++ b/code/components/jomjol_influxdb/interface_influxdb.cpp @@ -5,6 +5,7 @@ #include #include "ClassLogFile.h" #include "esp_http_client.h" +#include "time_sntp.h" #include "../../include/defines.h" @@ -30,7 +31,7 @@ void InfluxDB_V2_Init(std::string _uri, std::string _bucket, std::string _org, s _influxDB_V2_Token = _token; } -void InfluxDB_V2_Publish(std::string _measurement, std::string _key, std::string _content, std::string _timestamp) +void InfluxDB_V2_Publish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC) { char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0}; esp_http_client_config_t http_config = { @@ -41,28 +42,20 @@ void InfluxDB_V2_Publish(std::string _measurement, std::string _key, std::string .user_data = response_buffer }; - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDB_V2_Publish - Key: " + _key + ", Content: " + _content + ", Timestamp: " + _timestamp); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDB_V2_Publish - Key: " + _key + ", Content: " + _content + ", timeUTC: " + std::to_string(_timeUTC)); std::string payload; char nowTimestamp[21]; - if (_timestamp.length() > 0) + if (_timeUTC > 0) { - struct tm tm; - - time_t t; - time(&t); - localtime_r(&t, &tm); // Extract DST setting from actual time to consider it for timestamp evaluation - - strptime(_timestamp.c_str(), PREVALUE_TIME_FORMAT_OUTPUT, &tm); - t = mktime(&tm); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Timestamp: " + _timestamp + ", Timestamp (UTC): " + std::to_string(t)); - - sprintf(nowTimestamp,"%ld000000000", (long) t); // UTC + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Timestamp (UTC): " + std::to_string(_timeUTC)); + sprintf(nowTimestamp,"%ld000000000", _timeUTC); // UTC payload = _measurement + " " + _key + "=" + _content + " " + nowTimestamp; } else { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "no timestamp given"); payload = _measurement + " " + _key + "=" + _content; } @@ -137,7 +130,7 @@ static esp_err_t http_event_handler(esp_http_client_event_t *evt) return ESP_OK; } -void InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, std::string _timestamp) { +void InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC) { char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0}; esp_http_client_config_t http_config = { .user_agent = "ESP32 Meter reader", @@ -156,25 +149,17 @@ void InfluxDBPublish(std::string _measurement, std::string _key, std::string _co std::string payload; char nowTimestamp[21]; - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBPublish - Key: " + _key + ", Content: " + _content + ", Timestamp: " + _timestamp); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBPublish - Key: " + _key + ", Content: " + _content + ", timeUTC: " + std::to_string(_timeUTC)); - if (_timestamp.length() > 0) + if (_timeUTC > 0) { - struct tm tm; - - time_t t; - time(&t); - localtime_r(&t, &tm); // Extract DST setting from actual time to consider it for timestamp evaluation - - strptime(_timestamp.c_str(), PREVALUE_TIME_FORMAT_OUTPUT, &tm); - t = mktime(&tm); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Timestamp: " + _timestamp + ", Timestamp (UTC): " + std::to_string(t)); - - sprintf(nowTimestamp,"%ld000000000", (long) t); // UTC + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Timestamp (UTC): " + std::to_string(_timeUTC)); + sprintf(nowTimestamp,"%ld000000000", _timeUTC); // UTC payload = _measurement + " " + _key + "=" + _content + " " + nowTimestamp; } else { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "no timestamp given"); payload = _measurement + " " + _key + "=" + _content; } diff --git a/code/components/jomjol_influxdb/interface_influxdb.h b/code/components/jomjol_influxdb/interface_influxdb.h index 0d969800..66778621 100644 --- a/code/components/jomjol_influxdb/interface_influxdb.h +++ b/code/components/jomjol_influxdb/interface_influxdb.h @@ -10,11 +10,11 @@ // Interface to InfluxDB v1.x void InfluxDBInit(std::string _influxDBURI, std::string _database, std::string _user, std::string _password); -void InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, std::string _timestamp); +void InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC); // Interface to InfluxDB v2.x void InfluxDB_V2_Init(std::string _uri, std::string _bucket, std::string _org, std::string _token); -void InfluxDB_V2_Publish(std::string _measurement, std::string _key, std::string _content, std::string _timestamp); +void InfluxDB_V2_Publish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC); diff --git a/code/components/jomjol_mqtt/server_mqtt.cpp b/code/components/jomjol_mqtt/server_mqtt.cpp index 63eb5cb8..c95affd2 100644 --- a/code/components/jomjol_mqtt/server_mqtt.cpp +++ b/code/components/jomjol_mqtt/server_mqtt.cpp @@ -21,6 +21,7 @@ static const char *TAG = "MQTT SERVER"; extern const char* libfive_git_version(void); extern const char* libfive_git_revision(void); extern const char* libfive_git_branch(void); +extern std::string getFwVersion(void); std::vector* NUMBERS; bool HomeassistantDiscovery = false; @@ -151,6 +152,7 @@ bool MQTThomeassistantDiscovery(int qos) { // Group | Field | User Friendly Name | Icon | Unit | Device Class | State Class | Entity Category allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "uptime", "Uptime", "clock-time-eight-outline", "s", "", "", "diagnostic", qos); allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "MAC", "MAC Address", "network-outline", "", "", "", "diagnostic", qos); + allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "fwVersion", "Firmware Version", "application-outline", "", "", "", "diagnostic", qos); allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "hostname", "Hostname", "network-outline", "", "", "", "diagnostic", qos); allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "freeMem", "Free Memory", "memory", "B", "", "measurement", "diagnostic", qos); allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "wifiRSSI", "Wi-Fi RSSI", "wifi", "dBm", "signal_strength", "", "diagnostic", qos); @@ -244,6 +246,7 @@ bool publishStaticData(int qos) { int aFreeInternalHeapSizeBefore = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + allSendsSuccessed |= MQTTPublish(maintopic + "/" + "fwVersion", getFwVersion().c_str(), qos, retainFlag); allSendsSuccessed |= MQTTPublish(maintopic + "/" + "MAC", getMac(), qos, retainFlag); allSendsSuccessed |= MQTTPublish(maintopic + "/" + "IP", *getIPAddress(), qos, retainFlag); allSendsSuccessed |= MQTTPublish(maintopic + "/" + "hostname", wlan_config.hostname, qos, retainFlag); diff --git a/code/components/jomjol_tfliteclass/CTfLiteClass.cpp b/code/components/jomjol_tfliteclass/CTfLiteClass.cpp index 2e0d3f91..0ed87493 100644 --- a/code/components/jomjol_tfliteclass/CTfLiteClass.cpp +++ b/code/components/jomjol_tfliteclass/CTfLiteClass.cpp @@ -332,4 +332,18 @@ CTfLiteClass::~CTfLiteClass() delete this->interpreter; psram_free_shared_tensor_arena_and_model_memory(); -} \ No newline at end of file +} + +#ifdef SUPRESS_TFLITE_ERRORS +namespace tflite +{ +//tflite::ErrorReporter +// int OwnMicroErrorReporter::Report(const char* format, va_list args) + + int OwnMicroErrorReporter::Report(const char* format, va_list args) + { + return 0; + } +} +#endif + diff --git a/code/components/jomjol_time_sntp/CMakeLists.txt b/code/components/jomjol_time_sntp/CMakeLists.txt index 1be7fbca..4988e2a7 100644 --- a/code/components/jomjol_time_sntp/CMakeLists.txt +++ b/code/components/jomjol_time_sntp/CMakeLists.txt @@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) idf_component_register(SRCS ${app_sources} INCLUDE_DIRS "." - REQUIRES esp-tflite-micro jomjol_logfile jomjol_configfile) + REQUIRES esp_netif esp-tflite-micro jomjol_logfile jomjol_configfile) diff --git a/code/components/jomjol_time_sntp/time_sntp.cpp b/code/components/jomjol_time_sntp/time_sntp.cpp index 3c620f16..736003fc 100644 --- a/code/components/jomjol_time_sntp/time_sntp.cpp +++ b/code/components/jomjol_time_sntp/time_sntp.cpp @@ -10,7 +10,8 @@ #include "esp_log.h" #include "esp_attr.h" #include "esp_sleep.h" -#include "esp_sntp.h" +#include "esp_netif_sntp.h" + #include "../../include/defines.h" #include "ClassLogFile.h" @@ -31,6 +32,9 @@ std::string getNtpStatusText(sntp_sync_status_t status); static void setTimeZone(std::string _tzstring); static std::string getServerName(void); +int LocalTimeToUTCOffsetSeconds; + + std::string ConvertTimeToString(time_t _time, const char * frm) { @@ -89,15 +93,51 @@ bool time_manual_reset_sync(void) } +int getUTCOffsetSeconds(std::string &zeitzone) +{ + int offset = 0; + int vorzeichen = 1; + int minuten = 0; + int stunden = 0; + time_t now; + struct tm timeinfo; + + time (&now); + localtime_r(&now, &timeinfo); + char buffer[80]; + strftime(buffer, 80, "%z", &timeinfo); + zeitzone = std::string(buffer); + + if (zeitzone.length() == 5) + { + if (zeitzone[0] == '-') + vorzeichen = -1; + + stunden = stoi(zeitzone.substr(1, 2)); + minuten = stoi(zeitzone.substr(3, 2)); + + offset = ((stunden * 60) + minuten) * 60; + } + return offset; +} + + void setTimeZone(std::string _tzstring) { setenv("TZ", _tzstring.c_str(), 1); tzset(); + _tzstring = "Time zone set to " + _tzstring; LogFile.WriteToFile(ESP_LOG_INFO, TAG, _tzstring); + + std::string zeitzone; + LocalTimeToUTCOffsetSeconds = getUTCOffsetSeconds(zeitzone); +// std::string zw = std::to_string(LocalTimeToUTCOffsetSeconds); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "time zone: " + zeitzone + " Delta to UTC: " + std::to_string(LocalTimeToUTCOffsetSeconds) + " seconds"); } + std::string getNtpStatusText(sntp_sync_status_t status) { if (status == SNTP_SYNC_STATUS_COMPLETED) { return "Synchronized"; @@ -235,21 +275,13 @@ bool setupTime() { if (useNtp) { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Configuring NTP Client..."); - sntp_setoperatingmode(SNTP_OPMODE_POLL); - sntp_setservername(0, timeServer.c_str()); - sntp_set_time_sync_notification_cb(time_sync_notification_cb); + esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG(timeServer.c_str()); + config.sync_cb = time_sync_notification_cb; + esp_netif_sntp_init(&config); + setTimeZone(timeZone); - - sntp_init(); -/* - if (!wait_for_timesync()) - { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Timesync at startup failed."); - } -*/ } - /* The RTC keeps the time after a restart (Except on Power On or Pin Reset) * There should only be a minor correction through NTP */ @@ -258,6 +290,7 @@ bool setupTime() { localtime_r(&now, &timeinfo); strftime(strftime_buf, sizeof(strftime_buf), "%Y-%m-%d %H:%M:%S", &timeinfo); + if (getTimeIsSet()) { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Time is already set: " + std::string(strftime_buf)); } diff --git a/code/components/jomjol_time_sntp/time_sntp.h b/code/components/jomjol_time_sntp/time_sntp.h index a5df8bf1..9b5d8796 100644 --- a/code/components/jomjol_time_sntp/time_sntp.h +++ b/code/components/jomjol_time_sntp/time_sntp.h @@ -28,5 +28,7 @@ bool setupTime(); bool time_manual_reset_sync(void); +extern int LocalTimeToUTCOffsetSeconds; + #endif //TIMESNTP_H \ No newline at end of file diff --git a/code/components/jomjol_wlan/connect_wlan.cpp b/code/components/jomjol_wlan/connect_wlan.cpp index 085e4b67..60375126 100644 --- a/code/components/jomjol_wlan/connect_wlan.cpp +++ b/code/components/jomjol_wlan/connect_wlan.cpp @@ -378,7 +378,7 @@ void wifi_scan(void) else { if (esp_wifi_scan_get_ap_records(&max_number_of_ap_found, wifi_ap_records) != ESP_OK) { // Retrieve results (and free internal heap) LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "wifi_scan: esp_wifi_scan_get_ap_records: Error retrieving datasets"); - delete wifi_ap_records; + delete[] wifi_ap_records; return; } } @@ -401,7 +401,7 @@ void wifi_scan(void) APWithBetterRSSI = true; } } - delete wifi_ap_records; + delete[] wifi_ap_records; } diff --git a/code/dependencies.lock b/code/dependencies.lock index 24725511..6e49cb19 100644 --- a/code/dependencies.lock +++ b/code/dependencies.lock @@ -1,3 +1,3 @@ -manifest_hash: 63f5c6c9f0bcebc7b9ca12d2aa8b26b2c5f5218d377dc4b2375d9b9ca1df7815 -target: esp32 -version: 1.0.0 +manifest_hash: 63f5c6c9f0bcebc7b9ca12d2aa8b26b2c5f5218d377dc4b2375d9b9ca1df7815 +target: esp32 +version: 1.0.0 \ No newline at end of file diff --git a/code/include/defines.h b/code/include/defines.h index 27a4cc29..955c6c11 100644 --- a/code/include/defines.h +++ b/code/include/defines.h @@ -66,6 +66,7 @@ #define FLASH_GPIO GPIO_NUM_4 // PIN for flashlight LED #define USE_PWM_LEDFLASH // if __LEDGLOBAL is defined, a global variable is used for LED control, otherwise locally and each time a new #define CAM_LIVESTREAM_REFRESHRATE 500 // Camera livestream feature: Waiting time in milliseconds to refresh image + // #define GRAYSCALE_AS_DEFAULT //ClassControllCamera + ClassFlowTakeImage @@ -167,15 +168,6 @@ #define LWT_DISCONNECTED "connection lost" - //CTfLiteClass - #define TFLITE_MINIMAL_CHECK(x) \ - if (!(x)) { \ - fprintf(stderr, "Error at %s:%d\n", __FILE__, __LINE__); \ - exit(1); \ - } - // #define SUPRESS_TFLITE_ERRORS // use, to avoid error messages from TFLITE - - // connect_wlan.cpp //****************************** /* WIFI roaming functionalities 802.11k+v (uses ca. 6kB - 8kB internal RAM; if SCAN CACHE activated: + 1kB / beacon) diff --git a/code/main/main.cpp b/code/main/main.cpp index 7a508bba..a0ef0ea2 100644 --- a/code/main/main.cpp +++ b/code/main/main.cpp @@ -3,12 +3,6 @@ #include #include -//#include "freertos/FreeRTOS.h" -//#include "freertos/task.h" -//#include "freertos/event_groups.h" - -//#include "driver/gpio.h" -//#include "sdkconfig.h" #include "esp_psram.h" #include "esp_pm.h" @@ -16,17 +10,13 @@ #include "esp_chip_info.h" - - // SD-Card //////////////////// -//#include "nvs_flash.h" +#include "sdcard_init.h" #include "esp_vfs_fat.h" -//#include "sdmmc_cmd.h" +#include "ffconf.h" #include "driver/sdmmc_host.h" -//#include "driver/sdmmc_defs.h" /////////////////////////////// - #include "ClassLogFile.h" #include "connect_wlan.h" @@ -38,7 +28,6 @@ #include "server_ota.h" #include "time_sntp.h" #include "configFile.h" -//#include "ClassControllCamera.h" #include "server_main.h" #include "server_camera.h" #ifdef ENABLE_MQTT @@ -49,7 +38,6 @@ #include "sdcard_check.h" #include "../../include/defines.h" -//#include "server_GPIO.h" #ifdef ENABLE_SOFTAP #include "softAP.h" @@ -101,6 +89,8 @@ bool setCpuFrequency(void); static const char *TAG = "MAIN"; +#define MOUNT_POINT "/sdcard" + bool Init_NVS_SDCard() { @@ -112,26 +102,30 @@ bool Init_NVS_SDCard() ESP_LOGD(TAG, "Using SDMMC peripheral"); sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; // This initializes the slot without card detect (CD) and write protect (WP) signals. // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals. sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); - // To use 1-line SD mode, uncomment the following line: - #ifdef __SD_USE_ONE_LINE_MODE__ - slot_config.width = 1; - #endif + // Set bus width to use: + #ifdef __SD_USE_ONE_LINE_MODE__ + slot_config.width = 1; + #else + slot_config.width = 4; + #endif - // GPIOs 15, 2, 4, 12, 13 should have external 10k pull-ups. - // Internal pull-ups are not sufficient. However, enabling internal pull-ups - // does make a difference some boards, so we do that here. - gpio_set_pull_mode(GPIO_NUM_15, GPIO_PULLUP_ONLY); // CMD, needed in 4- and 1- line modes - gpio_set_pull_mode(GPIO_NUM_2, GPIO_PULLUP_ONLY); // D0, needed in 4- and 1-line modes - #ifndef __SD_USE_ONE_LINE_MODE__ - gpio_set_pull_mode(GPIO_NUM_4, GPIO_PULLUP_ONLY); // D1, needed in 4-line mode only - gpio_set_pull_mode(GPIO_NUM_12, GPIO_PULLUP_ONLY); // D2, needed in 4-line mode only - #endif - gpio_set_pull_mode(GPIO_NUM_13, GPIO_PULLUP_ONLY); // D3, needed in 4- and 1-line modes + // Enable internal pullups on enabled pins. The internal pullups + // are insufficient however, please make sure 10k external pullups are + // connected on the bus. This is for debug / example purpose only. + slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; + + // Der PullUp des GPIO13 wird durch slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; + // nicht gesetzt, da er eigentlich nicht benötigt wird, + // dies führt jedoch bei schlechten Kopien des AI_THINKER Boards + // zu Problemen mit der SD Initialisierung und eventuell sogar zur reboot-loops. + // Um diese Probleme zu kompensieren, wird der PullUp manuel gesetzt. + gpio_set_pull_mode(GPIO_NUM_13, GPIO_PULLUP_ONLY); // HS2_D3 // Options for mounting the filesystem. // If format_if_mount_failed is set to true, SD card will be partitioned and @@ -139,15 +133,18 @@ bool Init_NVS_SDCard() esp_vfs_fat_sdmmc_mount_config_t mount_config = { .format_if_mount_failed = false, .max_files = 12, // previously -> 2022-09-21: 5, 2023-01-02: 7 - .allocation_unit_size = 16 * 1024 + .allocation_unit_size = 0, // 0 = auto + .disk_status_check_enable = 0 }; sdmmc_card_t* card; + const char mount_point[] = MOUNT_POINT; + // Use settings defined above to initialize SD card and mount FAT filesystem. // Note: esp_vfs_fat_sdmmc_mount is an all-in-one convenience function. // Please check its source code and implement error recovery when developing // production applications. - ret = esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card); + ret = esp_vfs_fat_sdmmc_mount_mh(mount_point, &host, &slot_config, &mount_config, &card); if (ret != ESP_OK) { if (ret == ESP_FAIL) { diff --git a/code/main/server_main.cpp b/code/main/server_main.cpp index f60aedc5..b0a9a77e 100644 --- a/code/main/server_main.cpp +++ b/code/main/server_main.cpp @@ -17,6 +17,7 @@ #include "MainFlowControl.h" #include "esp_log.h" +#include "esp_chip_info.h" #include @@ -170,6 +171,27 @@ esp_err_t info_get_handler(httpd_req_t *req) httpd_resp_sendstr(req, zw.c_str()); return ESP_OK; } + else if (_task.compare("ChipCores") == 0) + { + esp_chip_info_t chipInfo; + esp_chip_info(&chipInfo); + httpd_resp_sendstr(req, to_string(chipInfo.cores).c_str()); + return ESP_OK; + } + else if (_task.compare("ChipRevision") == 0) + { + esp_chip_info_t chipInfo; + esp_chip_info(&chipInfo); + httpd_resp_sendstr(req, to_string(chipInfo.revision).c_str()); + return ESP_OK; + } + else if (_task.compare("ChipFeatures") == 0) + { + esp_chip_info_t chipInfo; + esp_chip_info(&chipInfo); + httpd_resp_sendstr(req, to_string(chipInfo.features).c_str()); + return ESP_OK; + } else { char formatted[256]; diff --git a/code/platformio.ini b/code/platformio.ini index ea924fb2..38012347 100644 --- a/code/platformio.ini +++ b/code/platformio.ini @@ -19,7 +19,7 @@ [common:esp32-idf] extends = common:idf - platform = platformio/espressif32 @ 6.3.2 + platform = platformio/espressif32 @ 6.5.0 framework = espidf lib_deps = ${common:idf.lib_deps} diff --git a/code/sdkconfig.defaults b/code/sdkconfig.defaults index 8e7fbc64..e41514a1 100644 --- a/code/sdkconfig.defaults +++ b/code/sdkconfig.defaults @@ -109,11 +109,6 @@ CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024 CONFIG_HTTPD_PURGE_BUF_LEN=16 -<<<<<<< Updated upstream -======= -CONFIG_HTTPD_WS_SUPPORT=y -CONFIG_LWIP_MAX_SOCKETS=12 ->>>>>>> Stashed changes CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=16 CONFIG_ESP32_WIFI_CACHE_TX_BUFFER_NUM=16 diff --git a/code/test/components/jomjol-flowcontroll/test_flow_postrocess_helper.cpp b/code/test/components/jomjol-flowcontroll/test_flow_postrocess_helper.cpp index b8ebd4c2..a5676b07 100644 --- a/code/test/components/jomjol-flowcontroll/test_flow_postrocess_helper.cpp +++ b/code/test/components/jomjol-flowcontroll/test_flow_postrocess_helper.cpp @@ -33,7 +33,17 @@ std::string process_doFlow(UnderTestPost* _underTestPost) { return _underTestPost->getReadout(0); } - +/** + * @brief setup flow like it runs after recognition. + * + * @param analog the analog recognitions as array begins with the highest ROI + * @param digits the digital regocnitions as array begins with the highest ROI + * @param digType type of the model defaults do Digital100 + * @param checkConsistency for Digital type only. Not relvant for newer models + * @param extendedResolution the lowest ROI will directly used (9.7 => 9.7) if false 9.7 => 9 + * @param decimal_shift the decimal point offset. -3 corresponds to x.yyy + * @return std::string the value result + */ std::string process_doFlow(std::vector analog, std::vector digits, t_CNNType digType, bool checkConsistency, bool extendedResolution, int decimal_shift) { // setup the classundertest diff --git a/code/test/components/jomjol-flowcontroll/test_flow_pp_negative.cpp b/code/test/components/jomjol-flowcontroll/test_flow_pp_negative.cpp index 1e31c9cc..914fa032 100644 --- a/code/test/components/jomjol-flowcontroll/test_flow_pp_negative.cpp +++ b/code/test/components/jomjol-flowcontroll/test_flow_pp_negative.cpp @@ -110,6 +110,8 @@ void testNegative_Issues() { setPreValue(underTestPost, preValue_extended); std::string result = process_doFlow(underTestPost); TEST_ASSERT_EQUAL_STRING("Neg. Rate - Read: - Raw: 22017.98 - Pre: 22018.09 ", underTestPost->getReadoutError().c_str()); + // if negativ no result any more + TEST_ASSERT_EQUAL_STRING("", result.c_str()); delete underTestPost; diff --git a/code/test/test_suite_flowcontroll.cpp b/code/test/test_suite_flowcontroll.cpp index 64b7aaf5..b1643c26 100644 --- a/code/test/test_suite_flowcontroll.cpp +++ b/code/test/test_suite_flowcontroll.cpp @@ -138,10 +138,6 @@ void task_UnityTesting(void *pvParameter) RUN_TEST(test_doFlowPP3); printf("---------------------------------------------------------------------------\n"); RUN_TEST(test_doFlowPP4); - printf("---------------------------------------------------------------------------\n"); - RUN_TEST(test_doFlowPP_rainman110); - printf("---------------------------------------------------------------------------\n"); - RUN_TEST(test_doFlowPP_rainman110_transition); UNITY_END(); while(1); @@ -153,11 +149,24 @@ void task_UnityTesting(void *pvParameter) */ extern "C" void app_main() { - initGPIO(); - Init_NVS_SDCard(); - esp_log_level_set("*", ESP_LOG_DEBUG); // set all components to DEBUG level + initGPIO(); + Init_NVS_SDCard(); + esp_log_level_set("*", ESP_LOG_ERROR); // set all components to ERROR level - // Create dedicated testing task (heap size can be configured - large enough to handle a lot of testing cases) - // ******************************************** - xTaskCreate(&task_UnityTesting, "task_UnityTesting", 12 * 1024, NULL, tskIDLE_PRIORITY+2, NULL); + UNITY_BEGIN(); + RUN_TEST(testNegative_Issues); + RUN_TEST(testNegative); + + RUN_TEST(test_analogToDigit_Standard); + RUN_TEST(test_analogToDigit_Transition); + RUN_TEST(test_doFlowPP); + RUN_TEST(test_doFlowPP1); + RUN_TEST(test_doFlowPP2); + RUN_TEST(test_doFlowPP3); + RUN_TEST(test_doFlowPP4); + + // getReadoutRawString test + RUN_TEST(test_getReadoutRawString); + + UNITY_END(); } diff --git a/param-docs/.idea/.gitignore b/param-docs/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/param-docs/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/param-docs/.idea/generate-param-docs.iml b/param-docs/.idea/generate-param-docs.iml new file mode 100644 index 00000000..8437fe66 --- /dev/null +++ b/param-docs/.idea/generate-param-docs.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/param-docs/.idea/inspectionProfiles/profiles_settings.xml b/param-docs/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 00000000..105ce2da --- /dev/null +++ b/param-docs/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/param-docs/.idea/misc.xml b/param-docs/.idea/misc.xml new file mode 100644 index 00000000..dc9ea490 --- /dev/null +++ b/param-docs/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/param-docs/.idea/modules.xml b/param-docs/.idea/modules.xml new file mode 100644 index 00000000..c7a3fc0d --- /dev/null +++ b/param-docs/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/param-docs/.idea/vcs.xml b/param-docs/.idea/vcs.xml new file mode 100644 index 00000000..b2bdec2d --- /dev/null +++ b/param-docs/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/param-docs/README.md b/param-docs/README.md new file mode 100644 index 00000000..bea97d64 --- /dev/null +++ b/param-docs/README.md @@ -0,0 +1,17 @@ +# Parameter Documentation +Each parameter which is listed in the [configfile](https://github.com/jomjol/AI-on-the-edge-device/blob/rolling/sd-card/config/config.ini) has its own description page in the folder `parameter-pages` (grouped by the config sections). +Those pages can be edited as needed. + +During a Github action build, those parameter pages will be used to generate the tooltips in the web interface. And they also are used to build the [Online Documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Parameters). + +If you create or rename a parameter, make sure to also update its description page! + +## Template Generator +The script `generate-template-param-doc-pages.py` should be run whenever a new parameter gets added to the config file. +It then checks if there is already a page for each of the parameters. + - If no page exists yet, a templated page gets generated. + - Existing pages do not get modified. + +If the parameter is listed in `expert-params.txt`, an **Expert warning** will be shown. + +If the parameter is listed in `hidden-in-ui.txt`, a **Note** will be shown. \ No newline at end of file diff --git a/param-docs/expert-params.txt b/param-docs/expert-params.txt new file mode 100644 index 00000000..db38cc92 --- /dev/null +++ b/param-docs/expert-params.txt @@ -0,0 +1,46 @@ +WaitBeforeTakingPicture +CamFrameSize +CamGainceiling +CamQuality +CamAutoSharpness +CamSharpness +CamSpecialEffect +CamWbMode +CamAwb +CamAwbGain +CamAec +CamAec2 +CamAeLevel +CamAecValue +CamAgc +CamAgcGain +CamBpc +CamWpc +CamRawGma +CamLenc +CamDcw +CamZoom +CamZoomSize +CamZoomOffsetX +CamZoomOffsetY +demo +SearchFieldX +SearchFieldY +AlignmentAlgo +CNNGoodThreshold +PreValueAgeStartup +ErrorMessage +CheckDigitIncreaseConsistency +IO0 +IO1 +IO3 +IO4 +IO12 +IO13 +AutoStart +Hostname +RSSIThreshold +TimeServer +CACert +ClientCert +ClientKey diff --git a/param-docs/generate-template-param-doc-pages.py b/param-docs/generate-template-param-doc-pages.py new file mode 100644 index 00000000..73daa01f --- /dev/null +++ b/param-docs/generate-template-param-doc-pages.py @@ -0,0 +1,95 @@ +""" +For each parameter which can be found in the config file, +create a markdown file with a templated content if it does not exist yet. +The files are grouped in sub folders representing the config sections. +""" + +import os +import configparser +import urllib.request + + +configFileUrl = "https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/rolling/sd-card/config/config.ini" + +parameterDocsFolder = "parameter-pages" +parameterTemplateFile = "./templates/parameter.md" +expertParameterListFile = "./expert-params.txt" +hiddenInUiParameterListFile = "./hidden-in-ui.txt" + + +# Fetch default config file from URL +print("Fetching %r..." % configFileUrl) +with urllib.request.urlopen(configFileUrl) as response: + content = response.read().decode("utf-8") + +lines = str(content).split("\n") + +for l in range(len(lines)): + lines[l] = lines[l].strip() + "\n" + if lines[l][0] == ";": + lines[l] = lines[l][1:] # Remove comment + +content = "".join(lines) + +# Fetch list of expert parameters +with open(expertParameterListFile) as f: + expertParameters = f.read().splitlines() + +# Fetch list of parameters not available through the UI +with open(hiddenInUiParameterListFile) as f: + hiddenInUiParameters = f.read().splitlines() + + +config = configparser.ConfigParser(allow_no_value=True) +config.optionxform = str # Make it case-insensitive +config.read_string(content) + +#shutil.rmtree(parameterDocsFolder) +if not os.path.exists(parameterDocsFolder): + os.mkdir(parameterDocsFolder) + +with open(parameterTemplateFile, 'r') as parameterTemplateFileHandle: + parameterTemplate = parameterTemplateFileHandle.read() + + +print("For each section/parameter, check if there is already a documentation page in the folder %r..." % (os.getcwd() + "/" + parameterDocsFolder)) +for section in config: + if section != "DEFAULT": + #print(section) + + subFolder = parameterDocsFolder + "/" + section + + if not os.path.exists(subFolder): + os.mkdir(subFolder) + + for parameter in config[section]: + if not " " in parameter: # Ignore parameters with whitespaces in them (special format, not part of editable config) + value = config[section][parameter] + #print(" %s = %s" % (parameter, value)) + + if "main." in parameter: + parameter = parameter.replace("main.", "NUMBER.") + + """ + For each config line, create a markdown file + """ + parameterDocFile = subFolder + '/' + parameter + ".md" + + if not os.path.exists(parameterDocFile): # File does not exist yet, generate template + print("%r does not exit yet, generating a templated file for it" % (os.getcwd() + "/" + parameterDocFile)) + with open(parameterDocFile, 'w') as paramFileHandle: + content = parameterTemplate + content = content.replace("$NAME", parameter) + content = content.replace("$DEFAULT", value) + + if parameter in expertParameters: + content = content.replace("$EXPERT_PARAMETER", "!!! Warning\n This is an **Expert Parameter**! Only change it if you understand what it does!") # Note: Needs a 4 whitespace Intent! + else: + content = content.replace("$EXPERT_PARAMETER", "") + + if parameter in hiddenInUiParameters: + content = content.replace("$HIDDEN_IN_UI", "!!! Note\n This parameter is not accessible through the Web Interface Configuration Page!") # Note: Needs a 4 whitespace Intent! + else: + content = content.replace("$HIDDEN_IN_UI", "") + + paramFileHandle.write(content) diff --git a/param-docs/hidden-in-ui.txt b/param-docs/hidden-in-ui.txt new file mode 100644 index 00000000..05c3b67e --- /dev/null +++ b/param-docs/hidden-in-ui.txt @@ -0,0 +1,4 @@ +InitialRotate +MainTopicMQTT +AutoAdjustSummertime +SetupMode diff --git a/param-docs/parameter-pages/Alignment/AlignmentAlgo.md b/param-docs/parameter-pages/Alignment/AlignmentAlgo.md new file mode 100644 index 00000000..dc6d4350 --- /dev/null +++ b/param-docs/parameter-pages/Alignment/AlignmentAlgo.md @@ -0,0 +1,14 @@ +# Parameter `AlignmentAlgo` +Default Value: `Default` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +Algorithm used for the alignment step. + +Available options: + +- `Default`: Use only red color channel +- `HighAccuracy`: Use all 3 color channels (3x slower) +- `Fast`: First time use `HighAccuracy`, then only check if the image is shifted +- `Off`: Disable alignment algorithm diff --git a/param-docs/parameter-pages/Alignment/FlipImageSize.md b/param-docs/parameter-pages/Alignment/FlipImageSize.md new file mode 100644 index 00000000..9fdc95bb --- /dev/null +++ b/param-docs/parameter-pages/Alignment/FlipImageSize.md @@ -0,0 +1,11 @@ +# Parameter `FlipImageSize` +Default Value: `false` + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +This parameter can be used to rotate the viewport together with the alignment rotation: +![](img/flipImageSize.png) diff --git a/param-docs/parameter-pages/Alignment/InitialMirror.md b/param-docs/parameter-pages/Alignment/InitialMirror.md new file mode 100644 index 00000000..072de29c --- /dev/null +++ b/param-docs/parameter-pages/Alignment/InitialMirror.md @@ -0,0 +1,10 @@ +# Parameter `InitialMirror` +Default Value: `false` + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Option for initially mirroring the image on the original x-axis. diff --git a/param-docs/parameter-pages/Alignment/InitialRotate.md b/param-docs/parameter-pages/Alignment/InitialRotate.md new file mode 100644 index 00000000..be93abd8 --- /dev/null +++ b/param-docs/parameter-pages/Alignment/InitialRotate.md @@ -0,0 +1,12 @@ +# Parameter `InitialRotate` +Default Value: `0` + +Unit: Degrees + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Initial rotation of image before alignment in degree (`-360` .. `+360`) diff --git a/param-docs/parameter-pages/Alignment/SearchFieldX.md b/param-docs/parameter-pages/Alignment/SearchFieldX.md new file mode 100644 index 00000000..b8293147 --- /dev/null +++ b/param-docs/parameter-pages/Alignment/SearchFieldX.md @@ -0,0 +1,14 @@ +# Parameter `SearchFieldX` +Default Value: `20` + +Unit: Pixels + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +X-size (width) in which the reference is searched. + +!!! Note + Since the alignment is one of the steps using a lot of computation time, + the search field should be as small as possible. + The calculation time goes quadratic with the search field size. \ No newline at end of file diff --git a/param-docs/parameter-pages/Alignment/SearchFieldY.md b/param-docs/parameter-pages/Alignment/SearchFieldY.md new file mode 100644 index 00000000..6b51b9a9 --- /dev/null +++ b/param-docs/parameter-pages/Alignment/SearchFieldY.md @@ -0,0 +1,14 @@ +# Parameter `SearchFieldY` +Default Value: `20` + +Unit: Pixels + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +Y-size (height) in which the reference is searched. + +!!! Note + Since the alignment is one of the steps using a lot of computation time, + the search field should be as small as possible. + The calculation time goes quadratic with the search field size. diff --git a/param-docs/parameter-pages/Analog/CNNGoodThreshold.md b/param-docs/parameter-pages/Analog/CNNGoodThreshold.md new file mode 100644 index 00000000..acff4a58 --- /dev/null +++ b/param-docs/parameter-pages/Analog/CNNGoodThreshold.md @@ -0,0 +1,10 @@ +# Parameter `CNNGoodThreshold` +Default Value: `0.5` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +Threshold above which the classification should be to accept the value (only meaningful for digits). + +!!! Warning + This is only supported for the `ana-class100` models! diff --git a/param-docs/parameter-pages/Analog/ExtendedResolution.md b/param-docs/parameter-pages/Analog/ExtendedResolution.md new file mode 100644 index 00000000..711fd4ec --- /dev/null +++ b/param-docs/parameter-pages/Analog/ExtendedResolution.md @@ -0,0 +1,5 @@ +# Parameter `ExtendedResolution` + +!!! Warning + This parameter is unused! + Use [`NUMBER.ExtendedResolution`](../Parameters/#PostProcessing-NUMBER.ExtendedResolution) instead! diff --git a/param-docs/parameter-pages/Analog/Model.md b/param-docs/parameter-pages/Analog/Model.md new file mode 100644 index 00000000..3481c2b4 --- /dev/null +++ b/param-docs/parameter-pages/Analog/Model.md @@ -0,0 +1,4 @@ +# Parameter `Model` +Default Value: `/config/ana-cont_*.tflite` (See [/config/config.ini](https://github.com/jomjol/AI-on-the-edge-device/blob/master/sd-card/config/config.ini)) + +Path to CNN model file for image recognition. See [here](../Choosing-the-Model) for details. diff --git a/param-docs/parameter-pages/Analog/ROIImagesLocation.md b/param-docs/parameter-pages/Analog/ROIImagesLocation.md new file mode 100644 index 00000000..a8aacecb --- /dev/null +++ b/param-docs/parameter-pages/Analog/ROIImagesLocation.md @@ -0,0 +1,7 @@ +# Parameter `ROIImagesLocation` +Default Value: `/log/analog` + +Location to store separated analog images on the SD-Card. + +!!! Warning + A SD-Card has limited write cycles. Since the device does not do [Wear Leveling](https://en.wikipedia.org/wiki/Wear_leveling), this can wear out your SD-Card! diff --git a/param-docs/parameter-pages/Analog/ROIImagesRetention.md b/param-docs/parameter-pages/Analog/ROIImagesRetention.md new file mode 100644 index 00000000..a36d06b5 --- /dev/null +++ b/param-docs/parameter-pages/Analog/ROIImagesRetention.md @@ -0,0 +1,6 @@ +# Parameter `ROIImagesRetention` +Default Value: `3` + +Unit: Days + +Days to keep the separated analog images (`0` = forever). diff --git a/param-docs/parameter-pages/AutoTimer/AutoStart.md b/param-docs/parameter-pages/AutoTimer/AutoStart.md new file mode 100644 index 00000000..94a93133 --- /dev/null +++ b/param-docs/parameter-pages/AutoTimer/AutoStart.md @@ -0,0 +1,12 @@ +# Parameter `AutoStart` +Default Value: `true` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +Automatically start the Flow (Digitization Rounds) immediately after power up. + +!!! Note + Typically this is set to `true`. + The main reasons to set it to `false` is when you want to trigger it manually using the + [REST API](../REST-API) or [MQTT-API](../MQTT-API) or for debugging. \ No newline at end of file diff --git a/param-docs/parameter-pages/AutoTimer/Interval.md b/param-docs/parameter-pages/AutoTimer/Interval.md new file mode 100644 index 00000000..e0e58ebb --- /dev/null +++ b/param-docs/parameter-pages/AutoTimer/Interval.md @@ -0,0 +1,7 @@ +# Parameter `Interval` +Default Value: `5` + +Unit: Minutes + +Interval in which the Flow (Digitization Round) is run. +If a round takes longer than this interval, the next round gets postponed until the current round completes. diff --git a/param-docs/parameter-pages/DataLogging/DataFilesRetention.md b/param-docs/parameter-pages/DataLogging/DataFilesRetention.md new file mode 100644 index 00000000..be6b9b9f --- /dev/null +++ b/param-docs/parameter-pages/DataLogging/DataFilesRetention.md @@ -0,0 +1,6 @@ +# Parameter `DataFilesRetention` +Default Value: `3` + +Unit: Days + +Number of days to keep the data files (`0` = forever). diff --git a/param-docs/parameter-pages/DataLogging/DataLogActive.md b/param-docs/parameter-pages/DataLogging/DataLogActive.md new file mode 100644 index 00000000..03e44ff8 --- /dev/null +++ b/param-docs/parameter-pages/DataLogging/DataLogActive.md @@ -0,0 +1,8 @@ +# Parameter `DataLogActive` +Default Value: `true` +Activate data logging to the SD-Card. + +The files will be stored in `/log/data/data_YYYY-MM-DD.csv`. See [`Data Logging`](../data-logging) for details. + +!!! Warning + A SD-Card has limited write cycles. Since the device does not do [Wear Leveling](https://en.wikipedia.org/wiki/Wear_leveling), this can wear out your SD-Card! diff --git a/param-docs/parameter-pages/Debug/LogLevel.md b/param-docs/parameter-pages/Debug/LogLevel.md new file mode 100644 index 00000000..ac518563 --- /dev/null +++ b/param-docs/parameter-pages/Debug/LogLevel.md @@ -0,0 +1,16 @@ +# Parameter `LogLevel` +Default Value: `1` (`ERROR`) +Define the log level for the logging to the SD-Card. + +Available options: + +- `1`: `ERROR` +- `2`: `WARNING` +- `3`: `INFO` +- `4`: `DEBUG` + +As higher the level, as more log messages get written to the SD-Card. + +!!! Warning + `DEBUG` or `INFO` might damage the SD-Card if enabled long term due to excessive writes to the SD-Card! + A SD-Card has limited write cycles. Since the device does not do [Wear Leveling](https://en.wikipedia.org/wiki/Wear_leveling), this can wear out your SD-Card! diff --git a/param-docs/parameter-pages/Debug/LogfilesRetention.md b/param-docs/parameter-pages/Debug/LogfilesRetention.md new file mode 100644 index 00000000..e8adb036 --- /dev/null +++ b/param-docs/parameter-pages/Debug/LogfilesRetention.md @@ -0,0 +1,6 @@ +# Parameter `LogfilesRetention` +Default Value: `3` + +Unit: Days + +Number of days to keep the log files (`0` = forever). diff --git a/param-docs/parameter-pages/Digits/CNNGoodThreshold.md b/param-docs/parameter-pages/Digits/CNNGoodThreshold.md new file mode 100644 index 00000000..04851292 --- /dev/null +++ b/param-docs/parameter-pages/Digits/CNNGoodThreshold.md @@ -0,0 +1,10 @@ +# Parameter `CNNGoodThreshold` +Default Value: `0.5` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +Threshold above which the classification should be to accept the value (only meaningful for digits). + +!!! Warning + This is only supported for the `dig-class100` models! diff --git a/param-docs/parameter-pages/Digits/Model.md b/param-docs/parameter-pages/Digits/Model.md new file mode 100644 index 00000000..73107f79 --- /dev/null +++ b/param-docs/parameter-pages/Digits/Model.md @@ -0,0 +1,4 @@ +# Parameter `Model` +Default Value: `/config/dig-cont_*.tflite` (See [/config/config.ini](https://github.com/jomjol/AI-on-the-edge-device/blob/master/sd-card/config/config.ini)) + +Path to CNN model file for image recognition. See [here](../Choosing-the-Model) for details. diff --git a/param-docs/parameter-pages/Digits/ROIImagesLocation.md b/param-docs/parameter-pages/Digits/ROIImagesLocation.md new file mode 100644 index 00000000..28d6b3ad --- /dev/null +++ b/param-docs/parameter-pages/Digits/ROIImagesLocation.md @@ -0,0 +1,7 @@ +# Parameter `ROIImagesLocation` +Default Value: `/log/digit` + +Location to store separated digit images on the SD-Card. + +!!! Warning + A SD-Card has limited write cycles. Since the device does not do [Wear Leveling](https://en.wikipedia.org/wiki/Wear_leveling), this can wear out your SD-Card! diff --git a/param-docs/parameter-pages/Digits/ROIImagesRetention.md b/param-docs/parameter-pages/Digits/ROIImagesRetention.md new file mode 100644 index 00000000..2d6ca9e3 --- /dev/null +++ b/param-docs/parameter-pages/Digits/ROIImagesRetention.md @@ -0,0 +1,6 @@ +# Parameter `ROIImagesRetention` +Default Value: `3` + +Unit: Days + +Days to keep the separated digit images (`0` = forever). diff --git a/param-docs/parameter-pages/GPIO/IO0.md b/param-docs/parameter-pages/GPIO/IO0.md new file mode 100644 index 00000000..a9b180a0 --- /dev/null +++ b/param-docs/parameter-pages/GPIO/IO0.md @@ -0,0 +1,21 @@ +# Parameter `IO0` +Default Value: `input disabled 10 false false` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +This parameter can be used to configure the GPIO `IO0` pin. + +!!! Warning + This pin is only usable with restrictions! + It must be disabled when the camera is used. + Additionally, it is used to activate Bootloader mode and must therefore be HIGH after a reset! + +Parameters: + +- `GPIO 0 state`: One of `input`, `input pullup`, `input pulldown` or `output`. +- `GPIO 0 use interrupt`: Enable interrupt trigger +- `GPIO 0 PWM duty resolution`: LEDC PWM duty resolution in bit +- `GPIO 0 enable MQTT`: Enable MQTT publishing/subscribing +- `GPIO 0 enable HTTP`: Enable HTTP write/read +- `GPIO 0 name`: MQTT topic name (empty = `GPIO0`). Allowed characters: `a-z, A-Z, 0-9, _, -`. diff --git a/param-docs/parameter-pages/GPIO/IO1.md b/param-docs/parameter-pages/GPIO/IO1.md new file mode 100644 index 00000000..481a847d --- /dev/null +++ b/param-docs/parameter-pages/GPIO/IO1.md @@ -0,0 +1,19 @@ +# Parameter `IO1` +Default Value: `input disabled 10 false false` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +This parameter can be used to configure the GPIO `IO1` pin. + +!!! Warning + This pin is by default used for the serial communication as TX pin (USB logging)! + +Parameters: + +- `GPIO 1 state`: One of `input`, `input pullup`, `input pulldown` or `output`. +- `GPIO 1 use interrupt`: Enable interrupt trigger +- `GPIO 1 PWM duty resolution`: LEDC PWM duty resolution in bit +- `GPIO 1 enable MQTT`: Enable MQTT publishing/subscribing +- `GPIO 1 enable HTTP`: Enable HTTP write/read +- `GPIO 1 name`: MQTT topic name (empty = `GPIO1`). Allowed characters: `a-z, A-Z, 0-9, _, -`. diff --git a/param-docs/parameter-pages/GPIO/IO12.md b/param-docs/parameter-pages/GPIO/IO12.md new file mode 100644 index 00000000..f5f62b85 --- /dev/null +++ b/param-docs/parameter-pages/GPIO/IO12.md @@ -0,0 +1,19 @@ +# Parameter `IO12` +Default Value: `input-pullup disabled 10 false false` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +This parameter can be used to configure the GPIO `IO12` pin. + +!!! Note + This pin is usable without known restrictions! + +Parameters: + +- `GPIO 12 state`: One of `external-flash-ws281x`, `input`, `input pullup`, `input pulldown` or `output`. +- `GPIO 12 use interrupt`: Enable interrupt trigger +- `GPIO 12 PWM duty resolution`: LEDC PWM duty resolution in bit +- `GPIO 12 enable MQTT`: Enable MQTT publishing/subscribing +- `GPIO 12 enable HTTP`: Enable HTTP write/read +- `GPIO 12 name`: MQTT topic name (empty = `GPIO12`). Allowed characters: `a-z, A-Z, 0-9, _, -`. diff --git a/param-docs/parameter-pages/GPIO/IO13.md b/param-docs/parameter-pages/GPIO/IO13.md new file mode 100644 index 00000000..64bd6426 --- /dev/null +++ b/param-docs/parameter-pages/GPIO/IO13.md @@ -0,0 +1,19 @@ +# Parameter `IO13` +Default Value: `input-pullup disabled 10 false false` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +This parameter can be used to configure the GPIO `IO13` pin. + +!!! Note + This pin is usable without known restrictions! + +Parameters: + +- `GPIO 13 state`: One of `input`, `input pullup`, `input pulldown` or `output`. +- `GPIO 13 use interrupt`: Enable interrupt trigger +- `GPIO 13 PWM duty resolution`: LEDC PWM duty resolution in bit +- `GPIO 13 enable MQTT`: Enable MQTT publishing/subscribing +- `GPIO 13 enable HTTP`: Enable HTTP write/read +- `GPIO 13 name`: MQTT topic name (empty = `GPIO13`). Allowed characters: `a-z, A-Z, 0-9, _, -`. diff --git a/param-docs/parameter-pages/GPIO/IO3.md b/param-docs/parameter-pages/GPIO/IO3.md new file mode 100644 index 00000000..7d00209a --- /dev/null +++ b/param-docs/parameter-pages/GPIO/IO3.md @@ -0,0 +1,19 @@ +# Parameter `IO3` +Default Value: `input disabled 10 false false` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +This parameter can be used to configure the GPIO `IO3` pin. + +!!! Warning + This pin is by default used for the serial communication as RX pin (USB logging)! + +Parameters: + +- `GPIO 3 state`: One of `input`, `input pullup`, `input pulldown` or `output`. +- `GPIO 3 use interrupt`: Enable interrupt trigger +- `GPIO 3 PWM duty resolution`: LEDC PWM duty resolution in bit +- `GPIO 3 enable MQTT`: Enable MQTT publishing/subscribing +- `GPIO 3 enable HTTP`: Enable HTTP write/read +- `GPIO 3 name`: MQTT topic name (empty = `GPIO3`). Allowed characters: `a-z, A-Z, 0-9, _, -`. diff --git a/param-docs/parameter-pages/GPIO/IO4.md b/param-docs/parameter-pages/GPIO/IO4.md new file mode 100644 index 00000000..f59883d2 --- /dev/null +++ b/param-docs/parameter-pages/GPIO/IO4.md @@ -0,0 +1,20 @@ +# Parameter `IO4` +Default Value: `built-in-led disabled 10 false false` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +This parameter can be used to configure the GPIO `IO4` pin. + +!!! Warning + This pin is only usable with restrictions! + By default, it is used for build-in flash light (onboard LED). + +Parameters: + +- `GPIO 4 state`: One of `built-in-led`, `input`, `input pullup`, `input pulldown` or `output`. +- `GPIO 4 use interrupt`: Enable interrupt trigger +- `GPIO 4 PWM duty resolution`: LEDC PWM duty resolution in bit +- `GPIO 4 enable MQTT`: Enable MQTT publishing/subscribing +- `GPIO 4 enable HTTP`: Enable HTTP write/read +- `GPIO 4 name`: MQTT topic name (empty = `GPIO4`). Allowed characters: `a-z, A-Z, 0-9, _, -`. diff --git a/param-docs/parameter-pages/GPIO/LEDColor.md b/param-docs/parameter-pages/GPIO/LEDColor.md new file mode 100644 index 00000000..c7e0816d --- /dev/null +++ b/param-docs/parameter-pages/GPIO/LEDColor.md @@ -0,0 +1,5 @@ +# Parameter `LEDColor` +Default Value: `150 150 150` + +Color of the attached LEDs to GPIO12 in **R**ed, **G**reen **B**lue from `0` (full off) .. `255` (full on) +(See `IO12` parameter). diff --git a/param-docs/parameter-pages/GPIO/LEDNumbers.md b/param-docs/parameter-pages/GPIO/LEDNumbers.md new file mode 100644 index 00000000..b151b240 --- /dev/null +++ b/param-docs/parameter-pages/GPIO/LEDNumbers.md @@ -0,0 +1,4 @@ +# Parameter `LEDNumbers` +Default Value: `2` + +Number of LEDs on the external LED-stripe attached to GPIO12 (See `IO12` parameter). diff --git a/param-docs/parameter-pages/GPIO/LEDType.md b/param-docs/parameter-pages/GPIO/LEDType.md new file mode 100644 index 00000000..e57e68c5 --- /dev/null +++ b/param-docs/parameter-pages/GPIO/LEDType.md @@ -0,0 +1,3 @@ +# Parameter `LEDType` +Default Value: `WS2812` +Type of the `WS2812x` which is connected to GPIO12 (See `IO12` parameter). diff --git a/param-docs/parameter-pages/GPIO/MainTopicMQTT.md b/param-docs/parameter-pages/GPIO/MainTopicMQTT.md new file mode 100644 index 00000000..19ce2b86 --- /dev/null +++ b/param-docs/parameter-pages/GPIO/MainTopicMQTT.md @@ -0,0 +1,8 @@ +# Parameter `MainTopicMQTT` +Default Value: `wasserzaehler/GPIO` + +!!! Note + This parameter is not accessible through the Web Interface Configuration Page! + +The GPIO Interface is prepared to report it's status and status changes as a MQTT topic. With this parameter you configure the MQTT main topic, under which the status is published. +As this parameter is still experimental it can only be set manually in the `config.ini` itself and has not been tested in detail so far. diff --git a/param-docs/parameter-pages/InfluxDB/Database.md b/param-docs/parameter-pages/InfluxDB/Database.md new file mode 100644 index 00000000..860a808b --- /dev/null +++ b/param-docs/parameter-pages/InfluxDB/Database.md @@ -0,0 +1,7 @@ +# Parameter `Database` +Default Value: `''` + +Name of the InfluxDB v1 Database into which to publish the values. + +!!! Note + See section `InfluxDBv2` for InfluxDB v2 support! diff --git a/param-docs/parameter-pages/InfluxDB/NUMBER.Field.md b/param-docs/parameter-pages/InfluxDB/NUMBER.Field.md new file mode 100644 index 00000000..8035ca6b --- /dev/null +++ b/param-docs/parameter-pages/InfluxDB/NUMBER.Field.md @@ -0,0 +1,4 @@ +# Parameter `.Field` +Default Value: `undefined` + +Dedicated definition of the field for InfluxDB use for saving in the Influx database (e.g.: "watermeter/value"). diff --git a/param-docs/parameter-pages/InfluxDB/NUMBER.Measurement.md b/param-docs/parameter-pages/InfluxDB/NUMBER.Measurement.md new file mode 100644 index 00000000..cd6f69cc --- /dev/null +++ b/param-docs/parameter-pages/InfluxDB/NUMBER.Measurement.md @@ -0,0 +1,7 @@ +# Parameter `Measurement` +Default Value: `undefined` + +Name of the InfluxDB v1 Measurement to use to publish the value. + +!!! Note + See section `InfluxDBv2` for InfluxDB v2 support! diff --git a/param-docs/parameter-pages/InfluxDB/Uri.md b/param-docs/parameter-pages/InfluxDB/Uri.md new file mode 100644 index 00000000..16b22c75 --- /dev/null +++ b/param-docs/parameter-pages/InfluxDB/Uri.md @@ -0,0 +1,7 @@ +# Parameter `Uri` +Default Value: `undefined` + +URI of the HTTP interface to InfluxDB v1, without trailing slash, e.g. `http://192.168.1.1:8086`. + +!!! Note + See section `InfluxDBv2` for InfluxDB v2 support! diff --git a/param-docs/parameter-pages/InfluxDB/password.md b/param-docs/parameter-pages/InfluxDB/password.md new file mode 100644 index 00000000..97dcbcc5 --- /dev/null +++ b/param-docs/parameter-pages/InfluxDB/password.md @@ -0,0 +1,7 @@ +# Parameter `password` +Default Value: `undefined` + +Password for the InfluxDB v1 authentication. + +!!! Note + See section `InfluxDBv2` for InfluxDB v2 support! diff --git a/param-docs/parameter-pages/InfluxDB/user.md b/param-docs/parameter-pages/InfluxDB/user.md new file mode 100644 index 00000000..3f989a4c --- /dev/null +++ b/param-docs/parameter-pages/InfluxDB/user.md @@ -0,0 +1,7 @@ +# Parameter `user` +Default Value: `undefined` + +Username for the InfluxDB v1 authentication. + +!!! Note + See section `InfluxDBv2` for InfluxDB v2 support! diff --git a/param-docs/parameter-pages/InfluxDBv2/Bucket.md b/param-docs/parameter-pages/InfluxDBv2/Bucket.md new file mode 100644 index 00000000..8dca6643 --- /dev/null +++ b/param-docs/parameter-pages/InfluxDBv2/Bucket.md @@ -0,0 +1,4 @@ +# Parameter `Bucket` +Default Value: `''` + +Name of the InfluxDB v2 Bucket into which to publish the values. diff --git a/param-docs/parameter-pages/InfluxDBv2/Database.md b/param-docs/parameter-pages/InfluxDBv2/Database.md new file mode 100644 index 00000000..9fc91233 --- /dev/null +++ b/param-docs/parameter-pages/InfluxDBv2/Database.md @@ -0,0 +1,5 @@ +# Parameter `Database` + +!!! Warning + This parameter is unused! + Use [`Basket`](../Parameters/#InfluxDBv2-Basket) instead! diff --git a/param-docs/parameter-pages/InfluxDBv2/NUMBER.Field.md b/param-docs/parameter-pages/InfluxDBv2/NUMBER.Field.md new file mode 100644 index 00000000..58611ad8 --- /dev/null +++ b/param-docs/parameter-pages/InfluxDBv2/NUMBER.Field.md @@ -0,0 +1,4 @@ +# Parameter `.Field` +Default Value: `undefined` + +Field for InfluxDB v2 to use for saving. diff --git a/param-docs/parameter-pages/InfluxDBv2/NUMBER.Measurement.md b/param-docs/parameter-pages/InfluxDBv2/NUMBER.Measurement.md new file mode 100644 index 00000000..c79ea6d4 --- /dev/null +++ b/param-docs/parameter-pages/InfluxDBv2/NUMBER.Measurement.md @@ -0,0 +1,4 @@ +# Parameter `Measurement` +Default Value: `undefined` + +Name of the InfluxDB v2 Measurement to use to publish the value. diff --git a/param-docs/parameter-pages/InfluxDBv2/Org.md b/param-docs/parameter-pages/InfluxDBv2/Org.md new file mode 100644 index 00000000..4731d7a9 --- /dev/null +++ b/param-docs/parameter-pages/InfluxDBv2/Org.md @@ -0,0 +1,4 @@ +# Parameter `Org` +Default Value: `undefined` + +Organisation (Org) for InfluxDB v2 authentication diff --git a/param-docs/parameter-pages/InfluxDBv2/Token.md b/param-docs/parameter-pages/InfluxDBv2/Token.md new file mode 100644 index 00000000..dc723607 --- /dev/null +++ b/param-docs/parameter-pages/InfluxDBv2/Token.md @@ -0,0 +1,4 @@ +# Parameter `Token` +Default Value: `undefined` + +Token for InfluxDB v2 authentication diff --git a/param-docs/parameter-pages/InfluxDBv2/Uri.md b/param-docs/parameter-pages/InfluxDBv2/Uri.md new file mode 100644 index 00000000..c02c7302 --- /dev/null +++ b/param-docs/parameter-pages/InfluxDBv2/Uri.md @@ -0,0 +1,4 @@ +# Parameter `Uri` +Default Value: `undefined` + +URI of the HTTP interface to InfluxDB v2, without trailing slash, e.g. `http://192.168.1.1:8086`. diff --git a/param-docs/parameter-pages/MQTT/CACert.md b/param-docs/parameter-pages/MQTT/CACert.md new file mode 100644 index 00000000..5ee45b46 --- /dev/null +++ b/param-docs/parameter-pages/MQTT/CACert.md @@ -0,0 +1,18 @@ +# Parameter `CACert` +Default Value: `""` + +Example: `/config/certs/RootCA.pem`. + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +Path to the CA certificate file. + +This is part of the configuration to enable TLS for MQTT. +The CA Certificate is used by the client to validate the broker is who it claims to be. +It allows the client to authenticate the server, which is the first part of the MTLS handshake. + +Usually there is a common RootCA certificate for the MQTT broker + +!!! Note + This also means that you might have to change the protocol and port in [uri](https://jomjol.github.io/AI-on-the-edge-device-docs/Parameters/#parameter-uri) to `mqtts://example.com:8883`! diff --git a/param-docs/parameter-pages/MQTT/ClientCert.md b/param-docs/parameter-pages/MQTT/ClientCert.md new file mode 100644 index 00000000..9cb49241 --- /dev/null +++ b/param-docs/parameter-pages/MQTT/ClientCert.md @@ -0,0 +1,19 @@ +# Parameter `ClientCert` +Default Value: `""` + +Example: `/config/certs/client.pem.crt`. + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +Path to the Client Certificate file. + +This is part of the configuration to enable TLS for MQTT. +The Client Certificate is used by the client to prove its identity to the server, in conjunction with the Client Key. +It is the second part of the MTLS handshake. + +Usually there is a one pair of Client Certificate/Key for each client that connects to the MQTT broker + +!!! Note + If set, `ClientKey` must be set too + This also means that you might have to change the protocol and port in [uri](https://jomjol.github.io/AI-on-the-edge-device-docs/Parameters/#parameter-uri) to `mqtts://example.com:8883`! diff --git a/param-docs/parameter-pages/MQTT/ClientID.md b/param-docs/parameter-pages/MQTT/ClientID.md new file mode 100644 index 00000000..6e5ec511 --- /dev/null +++ b/param-docs/parameter-pages/MQTT/ClientID.md @@ -0,0 +1,5 @@ +# Parameter `ClientID` +Default Value: `watermeter` + +Client ID used to connect to the MQTT broker. +If disabled, the `hostname` will be used. \ No newline at end of file diff --git a/param-docs/parameter-pages/MQTT/ClientKey.md b/param-docs/parameter-pages/MQTT/ClientKey.md new file mode 100644 index 00000000..af862f08 --- /dev/null +++ b/param-docs/parameter-pages/MQTT/ClientKey.md @@ -0,0 +1,19 @@ +# Parameter `ClientKey` +Default Value: `""` + +Example: `/config/certs/client.pem.key`. + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +Path to the Client Key file. + +This is part of the configuration to enable TLS for MQTT. +The Client Key is used by the client to prove its identity to the server, in conjunction with the Client Certificate. +It is the second part of the MTLS handshake. + +Usually there is a one pair of Client Certificate/Key for each client that connects to the MQTT broker + +!!! Note + If set, `ClientCert` must be set too + This also means that you might have to change the protocol and port in [uri](https://jomjol.github.io/AI-on-the-edge-device-docs/Parameters/#parameter-uri) to `mqtts://example.com:8883`! diff --git a/param-docs/parameter-pages/MQTT/HomeassistantDiscovery.md b/param-docs/parameter-pages/MQTT/HomeassistantDiscovery.md new file mode 100644 index 00000000..50e821fb --- /dev/null +++ b/param-docs/parameter-pages/MQTT/HomeassistantDiscovery.md @@ -0,0 +1,5 @@ +# Parameter `HomeassistantDiscovery` +Default Value: `true` + +Enable or disable the Homeassistant Discovery. +See [here](../Integration-Home-Assistant) for details about the discovery. diff --git a/param-docs/parameter-pages/MQTT/MainTopic.md b/param-docs/parameter-pages/MQTT/MainTopic.md new file mode 100644 index 00000000..3cf12ec2 --- /dev/null +++ b/param-docs/parameter-pages/MQTT/MainTopic.md @@ -0,0 +1,18 @@ +# Parameter `MainTopic` +Default Value: `watermeter` + +MQTT main topic, under which the counters are published. + +The single value will be published with the following key: `MAINTOPIC/NUMBER/RESULT_TOPIC` + +With: + +- `NUMBER`: The name of the value (a meter might have more than one value). + The names get defined in the analog and digital ROI configuration (defaults to `main`). +- `RESULT_TOPIC`: Automatically filled with the right name, eg. `value`, `rate`, `timestamp`, `error`, .... + +The general connection status can be found in `MAINTOPIC/CONNECTION`. +See [MQTT Result Topics](../MQTT-API#result) for a full list of topics. + +!!! Note + The main topic is allowed to contain `/` which can be used to split it into multiple levels, eg. `/basement/meters/watermeter/1/` if you have multiple water meters in your basement. diff --git a/param-docs/parameter-pages/MQTT/MeterType.md b/param-docs/parameter-pages/MQTT/MeterType.md new file mode 100644 index 00000000..a65bd03a --- /dev/null +++ b/param-docs/parameter-pages/MQTT/MeterType.md @@ -0,0 +1,11 @@ +# Parameter `MeterType` +Default Value: `other` + +Select the Meter Type so the sensors have the right units in Homeassistant. + +!!! Note + For `Watermeter` you need to have Homeassistant 2022.11 or newer! + +Please also make sure that the selected Meter Type matches the dimension of the value provided by the meter! +Eg. if your meter provides `m³`, you need to also set it to `m³`. +Alternatively you can set the parameter `DecimalShift` to `3` so the value is converted to `liters`! diff --git a/param-docs/parameter-pages/MQTT/RetainMessages.md b/param-docs/parameter-pages/MQTT/RetainMessages.md new file mode 100644 index 00000000..40a47d6a --- /dev/null +++ b/param-docs/parameter-pages/MQTT/RetainMessages.md @@ -0,0 +1,4 @@ +# Parameter `RetainMessages` +Default Value: `true` + +Enable or disable the [Retain Flag](https://www.hivemq.com/blog/mqtt-essentials-part-8-retained-messages/) for all MQTT entries. diff --git a/param-docs/parameter-pages/MQTT/Uri.md b/param-docs/parameter-pages/MQTT/Uri.md new file mode 100644 index 00000000..e7f93ab9 --- /dev/null +++ b/param-docs/parameter-pages/MQTT/Uri.md @@ -0,0 +1,4 @@ +# Parameter `Uri` +Default Value: `mqtt://example.com:1883` + +URI to the MQTT broker including the port. E.g. `mqtt://192.168.1.1:1883`. diff --git a/param-docs/parameter-pages/MQTT/password.md b/param-docs/parameter-pages/MQTT/password.md new file mode 100644 index 00000000..7a87224b --- /dev/null +++ b/param-docs/parameter-pages/MQTT/password.md @@ -0,0 +1,4 @@ +# Parameter `password` +Default Value: `PASSWORD` + +Password for MQTT authentication. diff --git a/param-docs/parameter-pages/MQTT/user.md b/param-docs/parameter-pages/MQTT/user.md new file mode 100644 index 00000000..7a4230ad --- /dev/null +++ b/param-docs/parameter-pages/MQTT/user.md @@ -0,0 +1,4 @@ +# Parameter `user` +Default Value: `USERNAME` + +Username for MQTT authentication. diff --git a/param-docs/parameter-pages/PostProcessing/AllowNegativeRates.md b/param-docs/parameter-pages/PostProcessing/AllowNegativeRates.md new file mode 100644 index 00000000..4290f8ce --- /dev/null +++ b/param-docs/parameter-pages/PostProcessing/AllowNegativeRates.md @@ -0,0 +1,5 @@ +# Parameter `AllowNegativeRates` + +!!! Warning + This parameter is unused! + Use [`NUMBER.AllowNegativeRates`](../Parameters/#PostProcessing-NUMBER.AllowNegativeRates) instead! diff --git a/param-docs/parameter-pages/PostProcessing/CheckDigitIncreaseConsistency.md b/param-docs/parameter-pages/PostProcessing/CheckDigitIncreaseConsistency.md new file mode 100644 index 00000000..8612cd14 --- /dev/null +++ b/param-docs/parameter-pages/PostProcessing/CheckDigitIncreaseConsistency.md @@ -0,0 +1,8 @@ +# Parameter `CheckDigitIncreaseConsistency` +Default Value: `false` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +An additional consistency check. +It especially improves the zero crossing check between digits. diff --git a/param-docs/parameter-pages/PostProcessing/ErrorMessage.md b/param-docs/parameter-pages/PostProcessing/ErrorMessage.md new file mode 100644 index 00000000..2213efb4 --- /dev/null +++ b/param-docs/parameter-pages/PostProcessing/ErrorMessage.md @@ -0,0 +1,8 @@ +# Parameter `ErrorMessage` +Default Value: `true` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +Do not show error message in return value. +In an error case, the last valid number will be used for the various transmission protocols (MQTT, InfluxDB, REST, ...). diff --git a/param-docs/parameter-pages/PostProcessing/NUMBER.AllowNegativeRates.md b/param-docs/parameter-pages/PostProcessing/NUMBER.AllowNegativeRates.md new file mode 100644 index 00000000..3bb73429 --- /dev/null +++ b/param-docs/parameter-pages/PostProcessing/NUMBER.AllowNegativeRates.md @@ -0,0 +1,7 @@ +# Parameter `.AllowNegativeRates` +Default Value: `false` + +Allow a meter to count backwards (decreasing values). + +!!! Note + This is unusual (it means there is a negative rate) and not wanted in most cases! diff --git a/param-docs/parameter-pages/PostProcessing/NUMBER.AnalogDigitalTransitionStart.md b/param-docs/parameter-pages/PostProcessing/NUMBER.AnalogDigitalTransitionStart.md new file mode 100644 index 00000000..d04831aa --- /dev/null +++ b/param-docs/parameter-pages/PostProcessing/NUMBER.AnalogDigitalTransitionStart.md @@ -0,0 +1,9 @@ +# Parameter `.AnalogDigitalTransitionStart` +Default Value: `9.2` + +This can be used if you have wrong values, but the recognition of the individual ROIs are correct. +Look for the start of changing of the first digit and note the analog pointer value behind. +Set it here. Only used on combination of digits and analog pointers. +See [here](../Watermeter-specific-analog---digital-transition) for details. + +Range: `6.0` .. `9.9`. diff --git a/param-docs/parameter-pages/PostProcessing/NUMBER.DecimalShift.md b/param-docs/parameter-pages/PostProcessing/NUMBER.DecimalShift.md new file mode 100644 index 00000000..604bb020 --- /dev/null +++ b/param-docs/parameter-pages/PostProcessing/NUMBER.DecimalShift.md @@ -0,0 +1,5 @@ +# Parameter `.DecimalShift` +Default Value: `0` + +Shift the decimal separator (positiv or negativ). +Eg. to move from `m³` to `liter` (`1 m³` equals `1000 liters`), you need to set it to `+3`. diff --git a/param-docs/parameter-pages/PostProcessing/NUMBER.ExtendedResolution.md b/param-docs/parameter-pages/PostProcessing/NUMBER.ExtendedResolution.md new file mode 100644 index 00000000..3689e6f2 --- /dev/null +++ b/param-docs/parameter-pages/PostProcessing/NUMBER.ExtendedResolution.md @@ -0,0 +1,7 @@ +# Parameter `.ExtendedResolution` +Default Value: `false` + +Use the decimal place of the last analog counter for increased accuracy. + +!!! Note + This parameter is only supported on the `*-class*` and `*-const` models! See [Choosing-the-Model](../Choosing-the-Model) for details. diff --git a/param-docs/parameter-pages/PostProcessing/NUMBER.IgnoreLeadingNaN.md b/param-docs/parameter-pages/PostProcessing/NUMBER.IgnoreLeadingNaN.md new file mode 100644 index 00000000..5fa11ab7 --- /dev/null +++ b/param-docs/parameter-pages/PostProcessing/NUMBER.IgnoreLeadingNaN.md @@ -0,0 +1,6 @@ +# Parameter `.IgnoreLeadingNaN` +Default Value: `true` + +Leading `N`'s will be deleted before further processing. +This is only relevant for models which use `N`! +See [here](../Choosing-the-Model) for details. diff --git a/param-docs/parameter-pages/PostProcessing/NUMBER.MaxRateType.md b/param-docs/parameter-pages/PostProcessing/NUMBER.MaxRateType.md new file mode 100644 index 00000000..aab9d29b --- /dev/null +++ b/param-docs/parameter-pages/PostProcessing/NUMBER.MaxRateType.md @@ -0,0 +1,5 @@ +# Parameter `.MaxRateType` +Default Value: `AbsoluteChange` + +Defines if the **Change Rate** is calculated as the difference between the last two readings (`AbsoluteChange` = difference) or +as the difference normalized to the interval (`RateChange` = difference per minute). diff --git a/param-docs/parameter-pages/PostProcessing/NUMBER.MaxRateValue.md b/param-docs/parameter-pages/PostProcessing/NUMBER.MaxRateValue.md new file mode 100644 index 00000000..98bb0afd --- /dev/null +++ b/param-docs/parameter-pages/PostProcessing/NUMBER.MaxRateValue.md @@ -0,0 +1,6 @@ +# Parameter `.MaxRateValue` +Default Value: `0,05` + +Maximum allowed change between two readings, if exceeded the last reading will be rejected. Depending on the settings of `.MaxRateType` the `MaxRateValue` is either treated as the difference between the two measurements (`AbsoluteChange` = difference) not taking the set time interval into account or as the difference normalized to the interval (`RateChange` = difference per minute). + +If negative rate is disallowed and no maximum rate value is set, one false high reading will lead to a period of missing measurements until the measurement reaches the previous false high reading. E.g. if the counter is at `600,00` and it's read incorrectly as` 610,00`, all measurements will be skipped until the counter reaches `610,00`. Setting the MaxRateValue to `0,05` leads to a rejection of all readings with a difference `> 0,05`, in this case `610,00`. The rejection also applies to correct readings with a difference `> 0,05`! diff --git a/param-docs/parameter-pages/PostProcessing/PreValueAgeStartup.md b/param-docs/parameter-pages/PostProcessing/PreValueAgeStartup.md new file mode 100644 index 00000000..0c5987bb --- /dev/null +++ b/param-docs/parameter-pages/PostProcessing/PreValueAgeStartup.md @@ -0,0 +1,7 @@ +# Parameter `PreValueAgeStartup` +Default Value: `720` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +Time in minutes, how long a previous read value is valid after reboot. diff --git a/param-docs/parameter-pages/PostProcessing/PreValueUse.md b/param-docs/parameter-pages/PostProcessing/PreValueUse.md new file mode 100644 index 00000000..58f71985 --- /dev/null +++ b/param-docs/parameter-pages/PostProcessing/PreValueUse.md @@ -0,0 +1,5 @@ +# Parameter `PreValueUse` +Default Value: `true` + +Use the previous value (value from previous round) for consistency checks. +This also works through a reboot of the device! diff --git a/param-docs/parameter-pages/System/CPUFrequency.md b/param-docs/parameter-pages/System/CPUFrequency.md new file mode 100644 index 00000000..97152794 --- /dev/null +++ b/param-docs/parameter-pages/System/CPUFrequency.md @@ -0,0 +1,13 @@ +# Parameter `CPUFrequency` +Default Value: `160` + +Set the CPU Frequency. + +!!! Warning + Setting it to 240 will lead to a faster device, but it will also require a stronger power supply! + Additionally, depending on the quality of your ESP32-CAM, it might run unstable! + +Possible values: + +- 160 +- 240 diff --git a/param-docs/parameter-pages/System/Hostname.md b/param-docs/parameter-pages/System/Hostname.md new file mode 100644 index 00000000..347ce76e --- /dev/null +++ b/param-docs/parameter-pages/System/Hostname.md @@ -0,0 +1,8 @@ +# Parameter `Hostname` +Default Value: `undefined` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +Hostname for the device. +It gets automatically transferred to `/wlan.ini` on the SD-Card at the next startup. diff --git a/param-docs/parameter-pages/System/RSSIThreshold.md b/param-docs/parameter-pages/System/RSSIThreshold.md new file mode 100644 index 00000000..a7f5ec50 --- /dev/null +++ b/param-docs/parameter-pages/System/RSSIThreshold.md @@ -0,0 +1,19 @@ +# Parameter `RSSIThreshold` +Default Value: `0` + +Possible values: `-100` .. `0` (`0` = disabled). + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + + +This parameter activates a client triggered AP switching functionality (simplified roaming). +If actual RSSI value is lower (more negative) than `RSSIThreshold`, all WIFI channels will be scanned for configured access point SSID. If an access point is in range which has better RSSI value (less negative) than actual RSSI value + 5 dBm, the device is trying to connect to this access point with the better RSSI value. + + +!!! Note + The RSSI check only gets initiated at the end of each round to avoid any disturbance of processing. + + +!!! Note + It gets automatically transferred to `/wlan.ini` on the SD-Card at next startup. diff --git a/param-docs/parameter-pages/System/SetupMode.md b/param-docs/parameter-pages/System/SetupMode.md new file mode 100644 index 00000000..18fcdaa6 --- /dev/null +++ b/param-docs/parameter-pages/System/SetupMode.md @@ -0,0 +1,7 @@ +# Parameter `SetupMode` +Default Value: `true` + +!!! Note + This parameter is not accessible through the Web Interface Configuration Page! + +Set this parameter to `true` to stay in the Setup Mode after the next start of the device. diff --git a/param-docs/parameter-pages/System/TimeServer.md b/param-docs/parameter-pages/System/TimeServer.md new file mode 100644 index 00000000..f10713d0 --- /dev/null +++ b/param-docs/parameter-pages/System/TimeServer.md @@ -0,0 +1,10 @@ +# Parameter `TimeServer` +Default Value: `pool.ntp.org` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +Time server to synchronize system time. If it is disabled or `undefined`, `pool.ntp.org` will be used. +You can also set it to the IP of your router. Many routers like Fritzboxes can act as a local NTP server. +To disable NTP, you need to activate it but set the TimeServer config to be empty (`""`). +In such case the time always starts at `01.01.1970` after each power cycle! diff --git a/param-docs/parameter-pages/System/TimeZone.md b/param-docs/parameter-pages/System/TimeZone.md new file mode 100644 index 00000000..fb6dfea0 --- /dev/null +++ b/param-docs/parameter-pages/System/TimeZone.md @@ -0,0 +1,5 @@ +# Parameter `TimeZone` +Default Value: `CET-1CEST,M3.5.0,M10.5.0/3` + +Time zone in POSIX syntax (Europe/Berlin = `CET-1CEST,M3.5.0,M10.5.0/3` - incl. daylight saving) +Check the table on `http:///timezones.html` to find the settings for your region. diff --git a/param-docs/parameter-pages/TakeImage/Aec2.md b/param-docs/parameter-pages/TakeImage/Aec2.md new file mode 100644 index 00000000..d5489ee2 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/Aec2.md @@ -0,0 +1,13 @@ +# Parameter `Aec2` +Default Value: `false` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Switch to "Auto-exposure Control 2" algorithm. This may resolve some over-exposure and under-exposure issues. diff --git a/param-docs/parameter-pages/TakeImage/AutoExposureLevel.md b/param-docs/parameter-pages/TakeImage/AutoExposureLevel.md new file mode 100644 index 00000000..b681bf0f --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/AutoExposureLevel.md @@ -0,0 +1,15 @@ +# Parameter `AutoExposureLevel` +Default Value: `0` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Exposure compensation. Lower values produce darker image. + +Range (`-2` .. `2`) diff --git a/param-docs/parameter-pages/TakeImage/Brightness.md b/param-docs/parameter-pages/TakeImage/Brightness.md new file mode 100644 index 00000000..b5f65aa5 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/Brightness.md @@ -0,0 +1,10 @@ +# Parameter `Brightness` +Default Value: `0` + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Image Brightness (`-2` .. `2`) diff --git a/param-docs/parameter-pages/TakeImage/CamAeLevel.md b/param-docs/parameter-pages/TakeImage/CamAeLevel.md new file mode 100644 index 00000000..8f562828 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamAeLevel.md @@ -0,0 +1,15 @@ +# Parameter `CamAeLevel` +Default Value: `0` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Auto-Exposure Compensation. Lower values produce darker image. + +Range (`-2` .. `2`) diff --git a/param-docs/parameter-pages/TakeImage/CamAec.md b/param-docs/parameter-pages/TakeImage/CamAec.md new file mode 100644 index 00000000..b6e4712b --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamAec.md @@ -0,0 +1,13 @@ +# Parameter `CamAec` +Default Value: `true` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Auto-Exposure Control Algorithm diff --git a/param-docs/parameter-pages/TakeImage/CamAec2.md b/param-docs/parameter-pages/TakeImage/CamAec2.md new file mode 100644 index 00000000..c6b402fb --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamAec2.md @@ -0,0 +1,13 @@ +# Parameter `CamAec2` +Default Value: `false` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Switch to "Auto-Exposure Control 2" Algorithm. This may resolve some over-exposure and under-exposure issues. diff --git a/param-docs/parameter-pages/TakeImage/CamAecValue.md b/param-docs/parameter-pages/TakeImage/CamAecValue.md new file mode 100644 index 00000000..e03c3b65 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamAecValue.md @@ -0,0 +1,15 @@ +# Parameter `CamAecValue` +Default Value: `0` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +set Manuall-Exposure Control Value + +Range (`0` .. `1200`) diff --git a/param-docs/parameter-pages/TakeImage/CamAgc.md b/param-docs/parameter-pages/TakeImage/CamAgc.md new file mode 100644 index 00000000..e6c48769 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamAgc.md @@ -0,0 +1,10 @@ +# Parameter `CamAgc` +Default Value: `true` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +This sets the Automatic Gain Control, how the camera responds to the overall level of light in the image. diff --git a/param-docs/parameter-pages/TakeImage/CamAgcGain.md b/param-docs/parameter-pages/TakeImage/CamAgcGain.md new file mode 100644 index 00000000..b383dc1e --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamAgcGain.md @@ -0,0 +1,13 @@ +# Parameter `CamAgcGain` +Default Value: ` ` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +!!! Note + This is visible when AGC is off. Gain is an amplification of the values read from the image sensor. + +Range (`0` .. `30`) diff --git a/param-docs/parameter-pages/TakeImage/CamAutoSharpness.md b/param-docs/parameter-pages/TakeImage/CamAutoSharpness.md new file mode 100644 index 00000000..48ebfd7b --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamAutoSharpness.md @@ -0,0 +1,13 @@ +# Parameter `CamAutoSharpness` +Default Value: `false` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +AutoSharpness diff --git a/param-docs/parameter-pages/TakeImage/CamAwb.md b/param-docs/parameter-pages/TakeImage/CamAwb.md new file mode 100644 index 00000000..9a5a4143 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamAwb.md @@ -0,0 +1,10 @@ +# Parameter `CamAwb` +Default Value: ` ` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Auto White Balance diff --git a/param-docs/parameter-pages/TakeImage/CamAwbGain.md b/param-docs/parameter-pages/TakeImage/CamAwbGain.md new file mode 100644 index 00000000..86e9bb83 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamAwbGain.md @@ -0,0 +1,10 @@ +# Parameter `CamAwbGain` +Default Value: ` ` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Auto White Balance Gain diff --git a/param-docs/parameter-pages/TakeImage/CamBpc.md b/param-docs/parameter-pages/TakeImage/CamBpc.md new file mode 100644 index 00000000..1cef3c07 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamBpc.md @@ -0,0 +1,10 @@ +# Parameter `CamBpc` +Default Value: `true` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Black Pixel Correction diff --git a/param-docs/parameter-pages/TakeImage/CamBrightness.md b/param-docs/parameter-pages/TakeImage/CamBrightness.md new file mode 100644 index 00000000..41e6a9d9 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamBrightness.md @@ -0,0 +1,10 @@ +# Parameter `CamBrightness` +Default Value: `0` + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Image Brightness (`-2` .. `2`) diff --git a/param-docs/parameter-pages/TakeImage/CamColorbar.md b/param-docs/parameter-pages/TakeImage/CamColorbar.md new file mode 100644 index 00000000..619e3f70 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamColorbar.md @@ -0,0 +1,5 @@ +# Parameter `CamColorbar` +Default Value: `false` + +!!! Note + This parameter can also be set on the Reference Image configuration page! diff --git a/param-docs/parameter-pages/TakeImage/CamContrast.md b/param-docs/parameter-pages/TakeImage/CamContrast.md new file mode 100644 index 00000000..941be46a --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamContrast.md @@ -0,0 +1,11 @@ +# Parameter `CamContrast` +Default Value: `0` + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Image Contrast (`-2` .. `2`) + diff --git a/param-docs/parameter-pages/TakeImage/CamDcw.md b/param-docs/parameter-pages/TakeImage/CamDcw.md new file mode 100644 index 00000000..550380c7 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamDcw.md @@ -0,0 +1,13 @@ +# Parameter `CamDcw` +Default Value: `true` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +!!! Note + When DCW is on, the image that you receive will be the size that you requested (VGA, QQVGA, etc). + When DCW is off, the image that you receive will be one of UXGA, SVGA, or CIF. In other words, literally the actual image size as read from the sensor without any scaling. + Note that if DCW is off, and you pick a different image size, this implicitly turns DCW back on again (although this isn't reflected in the options). diff --git a/param-docs/parameter-pages/TakeImage/CamDenoise.md b/param-docs/parameter-pages/TakeImage/CamDenoise.md new file mode 100644 index 00000000..36c0a573 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamDenoise.md @@ -0,0 +1,10 @@ +# Parameter `CamDenoise` +Default Value: ` ` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Denoise Image (not supported) diff --git a/param-docs/parameter-pages/TakeImage/CamGainceiling.md b/param-docs/parameter-pages/TakeImage/CamGainceiling.md new file mode 100644 index 00000000..479f746f --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamGainceiling.md @@ -0,0 +1,21 @@ +# Parameter `CamGainceiling` +Default Value: `x2` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +!!! Note + This is visible when AGC is on. + +Available options: + +- `x2` +- `x4` +- `x8` +- `x16` +- `x32` +- `x64` +- `x128` diff --git a/param-docs/parameter-pages/TakeImage/CamHmirror.md b/param-docs/parameter-pages/TakeImage/CamHmirror.md new file mode 100644 index 00000000..f8001678 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamHmirror.md @@ -0,0 +1,10 @@ +# Parameter `CamHmirror` +Default Value: `false` + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Option for initially mirroring the image on the original x-axis. diff --git a/param-docs/parameter-pages/TakeImage/CamLenc.md b/param-docs/parameter-pages/TakeImage/CamLenc.md new file mode 100644 index 00000000..c1f32db5 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamLenc.md @@ -0,0 +1,10 @@ +# Parameter `CamLenc` +Default Value: `true` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Lens Correction diff --git a/param-docs/parameter-pages/TakeImage/CamQuality.md b/param-docs/parameter-pages/TakeImage/CamQuality.md new file mode 100644 index 00000000..757800ad --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamQuality.md @@ -0,0 +1,10 @@ +# Parameter `CamQuality` +Default Value: `12` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +Quality index for pictures: `8` (highest quality) ... `63` (lowest quality) + +!!! Warning + Value below 12 could result in system instabilities! diff --git a/param-docs/parameter-pages/TakeImage/CamRawGma.md b/param-docs/parameter-pages/TakeImage/CamRawGma.md new file mode 100644 index 00000000..f98d1ff9 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamRawGma.md @@ -0,0 +1,10 @@ +# Parameter `CamRawGma` +Default Value: ` ` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +RawGma diff --git a/param-docs/parameter-pages/TakeImage/CamSaturation.md b/param-docs/parameter-pages/TakeImage/CamSaturation.md new file mode 100644 index 00000000..ac8511c9 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamSaturation.md @@ -0,0 +1,11 @@ +# Parameter `CamSaturation` +Default Value: `0` + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Image Saturation (`-2` .. `2`) + diff --git a/param-docs/parameter-pages/TakeImage/CamSharpness.md b/param-docs/parameter-pages/TakeImage/CamSharpness.md new file mode 100644 index 00000000..290d931b --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamSharpness.md @@ -0,0 +1,13 @@ +# Parameter `CamSharpness` +Default Value: `0` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Range (`-2` .. `2`) diff --git a/param-docs/parameter-pages/TakeImage/CamSpecialEffect.md b/param-docs/parameter-pages/TakeImage/CamSpecialEffect.md new file mode 100644 index 00000000..2e48fb02 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamSpecialEffect.md @@ -0,0 +1,21 @@ +# Parameter `CamSpecialEffect` +Default Value: `no_effect` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Available options: + +- `no_effect` +- `negative` +- `grayscale` +- `red` +- `green` +- `blue` +- `retro` diff --git a/param-docs/parameter-pages/TakeImage/CamVflip.md b/param-docs/parameter-pages/TakeImage/CamVflip.md new file mode 100644 index 00000000..fccb3022 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamVflip.md @@ -0,0 +1,10 @@ +# Parameter `CamVflip` +Default Value: `false` + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Option for initially flip the image on the original y-axis. diff --git a/param-docs/parameter-pages/TakeImage/CamWbMode.md b/param-docs/parameter-pages/TakeImage/CamWbMode.md new file mode 100644 index 00000000..a29eae44 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamWbMode.md @@ -0,0 +1,18 @@ +# Parameter `CamWbMode` +Default Value: `auto` + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +White Balance Mode + +Available options: + +- `auto` +- `sunny` +- `cloudy` +- `office` +- `home` diff --git a/param-docs/parameter-pages/TakeImage/CamWpc.md b/param-docs/parameter-pages/TakeImage/CamWpc.md new file mode 100644 index 00000000..bf1d5741 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamWpc.md @@ -0,0 +1,10 @@ +# Parameter `CamWpc` +Default Value: `true` + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +White Pixel Correction diff --git a/param-docs/parameter-pages/TakeImage/CamZoom.md b/param-docs/parameter-pages/TakeImage/CamZoom.md new file mode 100644 index 00000000..4f41f4a0 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamZoom.md @@ -0,0 +1,13 @@ +# Parameter `CamZoom` +Default Value: `false` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Crop the camera sensor frame to `ImageSize` resolution instead of scaling it down. diff --git a/param-docs/parameter-pages/TakeImage/CamZoomOffsetX.md b/param-docs/parameter-pages/TakeImage/CamZoomOffsetX.md new file mode 100644 index 00000000..1d8d4605 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamZoomOffsetX.md @@ -0,0 +1,14 @@ +# Parameter `CamZoomOffsetX` +Default Value: `0` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +`ZoomOffsetX` only applies when `Zoom` is enabled. +X coordinate of the crop location within the camera sensor frame. diff --git a/param-docs/parameter-pages/TakeImage/CamZoomOffsetY.md b/param-docs/parameter-pages/TakeImage/CamZoomOffsetY.md new file mode 100644 index 00000000..6ea68809 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamZoomOffsetY.md @@ -0,0 +1,14 @@ +# Parameter `CamZoomOffsetY` +Default Value: `0` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +`ZoomOffsetY` only applies when `Zoom` is enabled. +Y coordinate of the crop location within the camera sensor frame. diff --git a/param-docs/parameter-pages/TakeImage/CamZoomSize.md b/param-docs/parameter-pages/TakeImage/CamZoomSize.md new file mode 100644 index 00000000..809e59b2 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamZoomSize.md @@ -0,0 +1,13 @@ +# Parameter `CamZoomSize` +Default Value: `0` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +`CamZoomSize` only applies when `Zoom` is enabled. diff --git a/param-docs/parameter-pages/TakeImage/Contrast.md b/param-docs/parameter-pages/TakeImage/Contrast.md new file mode 100644 index 00000000..7bc76f2f --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/Contrast.md @@ -0,0 +1,11 @@ +# Parameter `Contrast` +Default Value: `0` + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Image Contrast (`-2` .. `2`) + diff --git a/param-docs/parameter-pages/TakeImage/Demo.md b/param-docs/parameter-pages/TakeImage/Demo.md new file mode 100644 index 00000000..4f66edf7 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/Demo.md @@ -0,0 +1,9 @@ +# Parameter `Demo` +Default Value: `false` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +Enable to use demo images instead of the real camera images. +Make sure to have a `/demo` folder on your SD-Card and make sure it contains the expected files! +Check [here](../Demo-Mode) for details. diff --git a/param-docs/parameter-pages/TakeImage/FixedExposure.md b/param-docs/parameter-pages/TakeImage/FixedExposure.md new file mode 100644 index 00000000..7db1c405 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/FixedExposure.md @@ -0,0 +1,13 @@ +# Parameter `FixedExposure` +Default Value: `false` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Fixes the illumination setting of camera at the startup and uses this later -> Individual round is faster. diff --git a/param-docs/parameter-pages/TakeImage/Grayscale.md b/param-docs/parameter-pages/TakeImage/Grayscale.md new file mode 100644 index 00000000..5eca929a --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/Grayscale.md @@ -0,0 +1,13 @@ +# Parameter `Grayscale` +Default Value: `false` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Produces black and white image. diff --git a/param-docs/parameter-pages/TakeImage/ImageQuality.md b/param-docs/parameter-pages/TakeImage/ImageQuality.md new file mode 100644 index 00000000..7f5c4c09 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/ImageQuality.md @@ -0,0 +1,10 @@ +# Parameter `ImageQuality` +Default Value: `12` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +Quality index for pictures: `8` (highest quality) ... `63` (lowest quality) + +!!! Warning + Value below 12 could result in system instabilities! diff --git a/param-docs/parameter-pages/TakeImage/ImageSize.md b/param-docs/parameter-pages/TakeImage/ImageSize.md new file mode 100644 index 00000000..73531f3c --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/ImageSize.md @@ -0,0 +1,12 @@ +# Parameter `ImageSize` +Default Value: `VGA` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +Size of the camera picture. + +Available options: + +- `VGA` (640 x 480 pixel) +- `QVGA` (320 x 240 pixel) diff --git a/param-docs/parameter-pages/TakeImage/LEDIntensity.md b/param-docs/parameter-pages/TakeImage/LEDIntensity.md new file mode 100644 index 00000000..56e75827 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/LEDIntensity.md @@ -0,0 +1,10 @@ +# Parameter `LEDIntensity` +Default Value: `50` + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Set the Flash LED Intensity: (`0` .. `100`) diff --git a/param-docs/parameter-pages/TakeImage/Negative.md b/param-docs/parameter-pages/TakeImage/Negative.md new file mode 100644 index 00000000..e0dc675c --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/Negative.md @@ -0,0 +1,13 @@ +# Parameter `Negative` +Default Value: `false` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Inverts the colors. diff --git a/param-docs/parameter-pages/TakeImage/RawImagesLocation.md b/param-docs/parameter-pages/TakeImage/RawImagesLocation.md new file mode 100644 index 00000000..0b53a93c --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/RawImagesLocation.md @@ -0,0 +1,7 @@ +# Parameter `RawImagesLocation` +Default Value: `/log/source` + +Location on the SD-Card to store the raw images. + +!!! Warning + A SD-Card has limited write cycles. Since the device does not do [Wear Leveling](https://en.wikipedia.org/wiki/Wear_leveling), this can wear out your SD-Card! diff --git a/param-docs/parameter-pages/TakeImage/RawImagesRetention.md b/param-docs/parameter-pages/TakeImage/RawImagesRetention.md new file mode 100644 index 00000000..e14e79bc --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/RawImagesRetention.md @@ -0,0 +1,6 @@ +# Parameter `RawImagesRetention` +Default Value: `15` + +Unit: Days + +Number of days to keep the raw images (`0` = forever) diff --git a/param-docs/parameter-pages/TakeImage/Saturation.md b/param-docs/parameter-pages/TakeImage/Saturation.md new file mode 100644 index 00000000..34c165dd --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/Saturation.md @@ -0,0 +1,11 @@ +# Parameter `Saturation` +Default Value: `0` + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Image Saturation (`-2` .. `2`) + diff --git a/param-docs/parameter-pages/TakeImage/Sharpness.md b/param-docs/parameter-pages/TakeImage/Sharpness.md new file mode 100644 index 00000000..88259fc7 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/Sharpness.md @@ -0,0 +1,15 @@ +# Parameter `Sharpness` +Default Value: `0` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Changes the sharpness of the image. Set to `-4` for auto-sharpness. + +Range (`-4` .. `3`) diff --git a/param-docs/parameter-pages/TakeImage/WaitBeforeTakingPicture.md b/param-docs/parameter-pages/TakeImage/WaitBeforeTakingPicture.md new file mode 100644 index 00000000..f6577519 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/WaitBeforeTakingPicture.md @@ -0,0 +1,9 @@ +# Parameter `WaitBeforeTakingPicture` +Default Value: `5` + +Unit: seconds + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +Waiting time between switching the flash light (onboard LED) on and taking the picture. diff --git a/param-docs/parameter-pages/TakeImage/Zoom.md b/param-docs/parameter-pages/TakeImage/Zoom.md new file mode 100644 index 00000000..8dc87586 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/Zoom.md @@ -0,0 +1,13 @@ +# Parameter `Zoom` +Default Value: `false` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +Crop the camera sensor frame to `ImageSize` resolution instead of scaling it down. diff --git a/param-docs/parameter-pages/TakeImage/ZoomMode.md b/param-docs/parameter-pages/TakeImage/ZoomMode.md new file mode 100644 index 00000000..05ac0fd4 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/ZoomMode.md @@ -0,0 +1,15 @@ +# Parameter `ZoomMode` +Default Value: `0` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +`ZoomMode` only applies when `Zoom` is enabled. +Value 0: Crop the camera sensor frame to `ImageSize` resolution. +Value 1: Scale the camera sensor frame to 800 x 600 pixels then crop it to `ImageSize` resolution. diff --git a/param-docs/parameter-pages/TakeImage/ZoomOffsetX.md b/param-docs/parameter-pages/TakeImage/ZoomOffsetX.md new file mode 100644 index 00000000..613c2d17 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/ZoomOffsetX.md @@ -0,0 +1,14 @@ +# Parameter `ZoomOffsetX` +Default Value: `0` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +`ZoomOffsetX` only applies when `Zoom` is enabled. +X coordinate of the crop location within the camera sensor frame. diff --git a/param-docs/parameter-pages/TakeImage/ZoomOffsetY.md b/param-docs/parameter-pages/TakeImage/ZoomOffsetY.md new file mode 100644 index 00000000..5880f566 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/ZoomOffsetY.md @@ -0,0 +1,14 @@ +# Parameter `ZoomOffsetY` +Default Value: `0` + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + This parameter can also be set on the Reference Image configuration page! + +!!! Note + After changing this parameter you need to update your reference image and alignment markers! + +`ZoomOffsetY` only applies when `Zoom` is enabled. +Y coordinate of the crop location within the camera sensor frame. diff --git a/param-docs/parameter-pages/img/flipImageSize.png b/param-docs/parameter-pages/img/flipImageSize.png new file mode 100644 index 00000000..dd761aa5 Binary files /dev/null and b/param-docs/parameter-pages/img/flipImageSize.png differ diff --git a/param-docs/templates/overview-old.md b/param-docs/templates/overview-old.md new file mode 100644 index 00000000..512b7b6e --- /dev/null +++ b/param-docs/templates/overview-old.md @@ -0,0 +1,13 @@ +# Parameters +This page lists all available [Configuration](../Configuration) Parameters. +If a parameter or section has a tick box on its left side, you can disable it. +In such case the functionality gets disabled respectively the default values will be used. + +!!! Note + This is an auto-generated page! See the [README](https://github.com/jomjol/AI-on-the-edge-device-docs/blob/main/README.md) for details! + +## List of all Parameters + +$TOC + +
\ No newline at end of file diff --git a/param-docs/templates/overview.md b/param-docs/templates/overview.md new file mode 100644 index 00000000..b34eafef --- /dev/null +++ b/param-docs/templates/overview.md @@ -0,0 +1,9 @@ +# Parameters +This page lists all available [Configuration](../Configuration) Parameters. +If a **parameter** or **section** has a tick box on its left side, you can disable it. +In such case the functionality gets disabled respectively the default values will be used. + +!!! Note + This is an auto-generated page! See the [README](https://github.com/jomjol/AI-on-the-edge-device-docs/blob/main/README.md) for details! + +
\ No newline at end of file diff --git a/param-docs/templates/parameter.md b/param-docs/templates/parameter.md new file mode 100644 index 00000000..7ea165f2 --- /dev/null +++ b/param-docs/templates/parameter.md @@ -0,0 +1,5 @@ +# Parameter `$NAME` +Default Value: `$DEFAULT` +$EXPERT_PARAMETER +$HIDDEN_IN_UI +Please fill me with an explanation and useful links. diff --git a/sd-card/config/ana-cont_1300_s2.tflite b/sd-card/config/ana-cont_1300_s2.tflite new file mode 100644 index 00000000..b8b72be8 Binary files /dev/null and b/sd-card/config/ana-cont_1300_s2.tflite differ diff --git a/sd-card/config/config.ini b/sd-card/config/config.ini index 2b76cae8..2b133857 100644 --- a/sd-card/config/config.ini +++ b/sd-card/config/config.ini @@ -1,114 +1,136 @@ -[TakeImage] -;RawImagesLocation = /log/source -WaitBeforeTakingPicture = 5 -;RawImagesRetention = 15 -Brightness = 0 -Contrast = 0 -Saturation = 0 -LEDIntensity = 50 -ImageQuality = 12 -ImageSize = VGA -FixedExposure = false -Demo = false - -[Alignment] -InitialRotate = 0.0 -InitialMirror = false -SearchFieldX = 20 -SearchFieldY = 20 -AlignmentAlgo = Default -FlipImageSize = false -/config/ref0.jpg 103 271 -/config/ref1.jpg 442 142 - -[Digits] -Model = /config/dig-cont_0620_s3_q.tflite -CNNGoodThreshold = 0.5 -;ROIImagesLocation = /log/digit -;ROIImagesRetention = 3 -main.dig1 294 126 30 54 false -main.dig2 343 126 30 54 false -main.dig3 391 126 30 54 false - -[Analog] -Model = /config/ana-cont_1208_s2_q.tflite -CNNGoodThreshold = 0.5 -;ROIImagesLocation = /log/analog -;ROIImagesRetention = 3 -main.ana1 432 230 92 92 false -main.ana2 379 332 92 92 false -main.ana3 283 374 92 92 false -main.ana4 155 328 92 92 false - -[PostProcessing] -main.DecimalShift = 0 -main.AnalogDigitalTransitionStart = 9.2 -PreValueUse = true -PreValueAgeStartup = 720 -main.AllowNegativeRates = false -main.MaxRateValue = 0.05 -;main.MaxRateType = AbsoluteChange -main.ExtendedResolution = false -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 -;Measurement = undefined -;user = undefined -;password = undefined - -;[InfluxDBv2] -;Uri = undefined -;Bucket = undefined -;Measurement = undefined -;Org = undefined -;Token = undefined -;main.Fieldname = undefined - -;[GPIO] -;MainTopicMQTT = wasserzaehler/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 = 5 - -[DataLogging] -DataLogActive = true -DataFilesRetention = 3 - -[Debug] -LogLevel = 1 -LogfilesRetention = 3 - -[System] -TimeZone = CET-1CEST,M3.5.0,M10.5.0/3 -;TimeServer = pool.ntp.org -;Hostname = undefined -RSSIThreshold = -75 -CPUFrequency = 160 -SetupMode = true +[TakeImage] +;RawImagesLocation = /log/source +;RawImagesRetention = 15 +WaitBeforeTakingPicture = 2 +CamGainceiling = x4 +CamQuality = 8 +CamBrightness = 0 +CamContrast = 0 +CamSaturation = 0 +CamSharpness = 1 +CamAutoSharpness = false +CamSpecialEffect = no_effect +CamWbMode = auto +CamAwb = true +CamAwbGain = true +CamAec = true +CamAec2 = true +CamAeLevel = 2 +CamAecValue = 500 +CamAgc = true +CamAgcGain = 0 +CamBpc = true +CamWpc = true +CamRawGma = true +CamLenc = true +CamHmirror = false +CamVflip = false +CamDcw = true +CamZoom = false +CamZoomOffsetX = 0 +CamZoomOffsetY = 0 +CamZoomSize = 0 +LEDIntensity = 50 +Demo = false + +[Alignment] +InitialRotate = 0.0 +InitialMirror = false +SearchFieldX = 20 +SearchFieldY = 20 +AlignmentAlgo = Default +FlipImageSize = false +/config/ref0.jpg 103 271 +/config/ref1.jpg 442 142 + +[Digits] +Model = /config/dig-cont_0700_s3_q.tflite +CNNGoodThreshold = 0.5 +;ROIImagesLocation = /log/digit +;ROIImagesRetention = 3 +main.dig1 294 126 30 54 false +main.dig2 343 126 30 54 false +main.dig3 391 126 30 54 false + +[Analog] +Model = /config/ana-cont_1300_s2.tflite +CNNGoodThreshold = 0.5 +;ROIImagesLocation = /log/analog +;ROIImagesRetention = 3 +main.ana1 432 230 92 92 false +main.ana2 379 332 92 92 false +main.ana3 283 374 92 92 false +main.ana4 155 328 92 92 false + +[PostProcessing] +main.DecimalShift = 0 +main.AnalogDigitalTransitionStart = 9.2 +PreValueUse = true +PreValueAgeStartup = 720 +main.AllowNegativeRates = false +main.MaxRateValue = 0.05 +;main.MaxRateType = AbsoluteChange +main.ExtendedResolution = false +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 +;Measurement = undefined +;user = undefined +;password = undefined + +;[InfluxDBv2] +;Uri = undefined +;Bucket = undefined +;Measurement = undefined +;Org = undefined +;Token = undefined +;main.Fieldname = undefined + +;[GPIO] +;MainTopicMQTT = wasserzaehler/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 = 5 + +[DataLogging] +DataLogActive = true +DataFilesRetention = 3 + +[Debug] +LogLevel = 1 +LogfilesRetention = 3 + +[System] +TimeZone = CET-1CEST,M3.5.0,M10.5.0/3 +;TimeServer = pool.ntp.org +;Hostname = undefined +RSSIThreshold = -75 +CPUFrequency = 160 +SetupMode = true diff --git a/sd-card/config/dig-class100-0168_s2_q.tflite b/sd-card/config/dig-class100_0168_s2_q.tflite similarity index 100% rename from sd-card/config/dig-class100-0168_s2_q.tflite rename to sd-card/config/dig-class100_0168_s2_q.tflite diff --git a/sd-card/config/dig-class11_1701_s2.tflite b/sd-card/config/dig-class11_1701_s2.tflite new file mode 100644 index 00000000..7dd74d44 Binary files /dev/null and b/sd-card/config/dig-class11_1701_s2.tflite differ diff --git a/sd-card/config/dig-cont_0640_s3_q.tflite b/sd-card/config/dig-cont_0640_s3_q.tflite new file mode 100644 index 00000000..41536f52 Binary files /dev/null and b/sd-card/config/dig-cont_0640_s3_q.tflite differ diff --git a/sd-card/config/dig-cont_0700_s3_q.tflite b/sd-card/config/dig-cont_0700_s3_q.tflite new file mode 100644 index 00000000..bdd9f57b Binary files /dev/null and b/sd-card/config/dig-cont_0700_s3_q.tflite differ diff --git a/sd-card/html/.gitignore b/sd-card/html/.gitignore new file mode 100644 index 00000000..ccfb4922 --- /dev/null +++ b/sd-card/html/.gitignore @@ -0,0 +1 @@ +edit_config.html diff --git a/sd-card/html/edit_alignment.html b/sd-card/html/edit_alignment.html index 5a2356eb..a515a0ff 100644 --- a/sd-card/html/edit_alignment.html +++ b/sd-card/html/edit_alignment.html @@ -1,7 +1,7 @@ - Alignment marker + Alignment markers @@ -169,9 +192,17 @@ + + + + +
+
+
+

Configuration

@@ -183,7 +214,7 @@ The description of each parameter can be shown by hovering over or by clicking the icon.

- The page gets opened with the default view which should be sufficient for regular configuration tasks. Enabling the "Expert View" + The page gets opened with the default view which should be sufficient for regular configuration tasks. Enabling the "Show Expert Parameters" some expert parameters (light red background color) will be added to the parameter list. Additionally the button "Edit "Config.ini" File" to edit the underlaying configuration file (config.ini) manually is now shown on top of the page. This function should be only used for special cases.

@@ -217,10 +248,10 @@ - + - + @@ -252,124 +283,406 @@ $TOOLTIP_TakeImage_RawImagesRetention - - - - - - - - $TOOLTIP_TakeImage_Demo - - - - + Wait Before Taking Picture - Seconds $TOOLTIP_TakeImage_WaitBeforeTakingPicture - + - Image Quality + CamGainceiling - - - $TOOLTIP_TakeImage_ImageQuality - - - - - Image Size - - - + + + + + + + - $TOOLTIP_TakeImage_ImageSize + $TOOLTIP_TakeImage_CamGainceiling + + + + + Image Quality + + + + + $TOOLTIP_TakeImage_CamQuality - + + + Brightness + + + + + $TOOLTIP_TakeImage_CamBrightness + + + + + Contrast + + + + + $TOOLTIP_TakeImage_CamContrast + + + + + Saturation + + + + + $TOOLTIP_TakeImage_CamSaturation + + + + + AutoSharpness + + + + + $TOOLTIP_TakeImage_CamAutoSharpness + + + + + Sharpness + + + + + $TOOLTIP_TakeImage_CamSharpness + + + + + SpecialEffect + + + + + $TOOLTIP_TakeImage_CamSpecialEffect + + + + + White Balance Mode + + + + + $TOOLTIP_TakeImage_CamWbMode + + + + + White Balance + + + + + $TOOLTIP_TakeImage_CamAwb + + + + + Auto White Balance + + + + + $TOOLTIP_TakeImage_CamAwbGain + + + + + Auto-Exposure Control + + + + + $TOOLTIP_TakeImage_CamAec + + + + + Auto-Exposure Control 2 + + + + + $TOOLTIP_TakeImage_CamAec2 + + + + + Auto Exposure Level + + + + + $TOOLTIP_TakeImage_CamAeLevel + + + + + Auto Exposure Value + + + + + $TOOLTIP_TakeImage_CamAecValue + + + + + Auto Gain + + + + + $TOOLTIP_TakeImage_CamAgc + + + + + Gain Manuall Value + + + + + $TOOLTIP_TakeImage_CamAgcGain + + + + + Black Pixel Correction + + + + + $TOOLTIP_TakeImage_CamBpc + + + + + White Pixel Correction + + + + + $TOOLTIP_TakeImage_CamWpc + + + + + CamRawGma + + + + + $TOOLTIP_TakeImage_CamRawGma + + + + + Lens Correction + + + + + $TOOLTIP_TakeImage_CamLenc + + + + + Mirror Image + + + + + $TOOLTIP_TakeImage_CamHmirror + + + + + Flip Image + + + + + $TOOLTIP_TakeImage_CamVflip + + + + + Downsize + + + + + $TOOLTIP_TakeImage_CamDcw + + + + + Zoom + + + + + $TOOLTIP_TakeImage_CamZoom + + + + + Zoom Size + + + + + $TOOLTIP_TakeImage_CamZoomSize + + + + + Zoom Offset X + + + Pixel + + $TOOLTIP_TakeImage_CamZoomOffsetX + + + + + Zoom Offset Y + + + Pixel + + $TOOLTIP_TakeImage_CamZoomOffsetY + + + LED Intensity - + $TOOLTIP_TakeImage_LEDIntensity - - - - Brightness - - - - - $TOOLTIP_TakeImage_Brightness - - - - - Contrast - - - - - $TOOLTIP_TakeImage_Contrast - - - - - Saturation - - - - - $TOOLTIP_TakeImage_Saturation - - + - Fixed Exposure + - + + - $TOOLTIP_TakeImage_FixedExposure + $TOOLTIP_TakeImage_Demo - - +

Alignment

- + Search Field X @@ -380,7 +693,7 @@ $TOOLTIP_Alignment_SearchFieldX - + Search Field Y @@ -391,7 +704,7 @@ $TOOLTIP_Alignment_SearchFieldY - + @@ -407,33 +720,17 @@ $TOOLTIP_Alignment_AlignmentAlgo - + - Flip Image Size + Rotation angle - + degree - $TOOLTIP_Alignment_FlipImageSize + $TOOLTIP_Alignment_InitialRotate - - - Initial Mirror - - - - - $TOOLTIP_Alignment_InitialMirror - - - @@ -453,7 +750,7 @@ $TOOLTIP_Digits_Model - + @@ -542,46 +839,46 @@ $TOOLTIP_PostProcessing_PreValueUse - - + + Seconds + oninput="(!validity.rangeUnderflow||(value=0)) && (!validity.stepMismatch||(value=parseInt(this.value)));">Minutes $TOOLTIP_PostProcessing_PreValueAgeStartup - + $TOOLTIP_PostProcessing_ErrorMessage - + $TOOLTIP_PostProcessing_CheckDigitIncreaseConsistency @@ -598,12 +895,12 @@ - + $TOOLTIP_PostProcessing_NUMBER.AllowNegativeRates @@ -628,8 +925,8 @@ - $TOOLTIP_PostProcessing_NUMBER.AnalogDigitalTransitionStart @@ -667,26 +964,39 @@ $TOOLTIP_PostProcessing_NUMBER.ExtendedResolution - + $TOOLTIP_PostProcessing_NUMBER.IgnoreLeadingNaN - - + + @@ -753,7 +1063,7 @@ $TOOLTIP_MQTT_password - + @@ -764,7 +1074,7 @@ $TOOLTIP_MQTT_CACert - + @@ -775,7 +1085,7 @@ $TOOLTIP_MQTT_ClientCert - + @@ -792,8 +1102,8 @@ $TOOLTIP_MQTT_RetainMessages @@ -814,8 +1124,8 @@ $TOOLTIP_MQTT_HomeassistantDiscovery @@ -1324,6 +1634,8 @@ + + @@ -1382,7 +1694,7 @@ - + LED Type (NeoPixel) @@ -1397,7 +1709,7 @@ $TOOLTIP_GPIO_LEDType - + Numbers of LEDs @@ -1408,22 +1720,32 @@ $TOOLTIP_GPIO_LEDNumbers - + LED Color - - R - G - B $TOOLTIP_GPIO_LEDColor + + + + + G + + + + + + B + @@ -1502,14 +1824,14 @@

Auto Timer

- + Automatic Round Start $TOOLTIP_AutoTimer_AutoStart @@ -1538,8 +1860,8 @@ $TOOLTIP_DataLogging_DataLogActive @@ -1606,7 +1928,7 @@ $TOOLTIP_System_TimeZone - + @@ -1617,7 +1939,7 @@ $TOOLTIP_System_TimeServer - + @@ -1628,7 +1950,7 @@ $TOOLTIP_System_Hostname - + @@ -1641,7 +1963,7 @@ $TOOLTIP_System_RSSIThreshold - + @@ -1655,6 +1977,18 @@ $TOOLTIP_System_CPUFrequency + + + Tooltip + + + + + + @@ -1672,50 +2006,95 @@ - diff --git a/sd-card/html/edit_digits.html b/sd-card/html/edit_digits.html index e5295360..eed03631 100644 --- a/sd-card/html/edit_digits.html +++ b/sd-card/html/edit_digits.html @@ -123,17 +123,25 @@ + + - - - + + +
Number Sequence:
+ + + + + +
@@ -146,15 +154,26 @@ + ROI: + - - - - + + + + + + + + + + + + + Multiplier:
(only based on order) @@ -162,8 +181,12 @@ Multiplier:
(order + decimal shift: ) - - + + + + + + @@ -173,36 +196,44 @@ + x: Δx: - + y: Δy: - - - - - - - - + +
+ The following settings are only used for easier setup, they are not persisted on the device:
+
+
+
+ +
+
+ +
- + + - - + + + @@ -215,43 +246,42 @@ - diff --git a/sd-card/html/edit_reference.html b/sd-card/html/edit_reference.html index b025f2e8..d6359bd4 100644 --- a/sd-card/html/edit_reference.html +++ b/sd-card/html/edit_reference.html @@ -1,7 +1,7 @@ - Reference Image + Reference Image and Camera Settings + + + + + + + + -

Reference Image

+
+
+
+ +

Reference Image and Camera Settings

CLICK HERE for usage description. More infos in documentation: Reference Image @@ -61,563 +133,902 @@ The reference image is the base image on which the alignment markers, digit ROIs and anlog ROIs will be defined.

- Firstly the actual saved reference image is shown. If you start with the setup from scratch a default image is shown as placeholder. - Use the button "Create New Reference" to start creation of your own reference image. After selecting the button a new image will be taken - all configured parameter will be applied to the shown image. With the button "Update Image" you can update the image again (still all parameter - get applied to the new image). + Initially the currently saved reference image is shown. If you start with a setup from scratch, a default image is shown as a placeholder. + Use the button "Create New Reference / Change Camera Settings" to start the creation of your own reference image. After clicking the button, the camera will take a + photo using the configured parameters. This will be your new reference image. With the button "Update Reference Image" you can update the image again.

- To have reliable evaluation processing a properly horizontal aligned evaluation area is mandatory. Using the parameters "Rotation angle" and - "Rotation angle (Fine-tune)" the image can be rotated in both directions. The resulting rotation anlge is used to prerotate the image before - the alignment algorithm is processed to compensate only small misalignments. Further information can be found in documenation: + To have a reliable evaluation processing, it is mandatory to have the digit ROIs horizontally aligned. Using the two input fields "Rotation angle" and + "(Fine-tune)" the image can be rotated in both directions. The resulting rotation angle is used to pre-rotate the image before + the alignment algorithm is processed to compensate only small misalignments (See next step Alignment Markers). Further information can be found in documenation: Reference Image

- After setting up your reference image don't forget to save with the "Save New Reference" button!
+ After setting up your reference image don't forget to save with the "Save new Reference Image and Camera Settings" button!

NOTE: There is no need to perform a reboot after every saving or step. It's sufficient to reboot after all configuration steps (reference image, alignment, ROI configuration) are completed to activate new configuration.

-
+
-
Reference Image:
+ +
+

+ +
+ + + + +
- - - - - - - - -
-
- - - - + + + - + + - - - + + + + + + + - + - - - + + + + + + + - - - - - + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Zoom + + + $TOOLTIP_TakeImage_CamZoom + + - LED intensity: - - + $TOOLTIP_TakeImage_CamZoomOffsetX
+ + - Brightness: + $TOOLTIP_TakeImage_CamZoomSize + + - - 0 + $TOOLTIP_TakeImage_CamZoomOffsetY
- degree - - Contrast: - - - 0 -
+ SpecialEffect: + + + $TOOLTIP_TakeImage_CamSpecialEffect + Auto-exposure Control 2 + + + $TOOLTIP_TakeImage_CamAec2
- degree + + Mirror Image + + + $TOOLTIP_TakeImage_CamHmirror + LED intensity: - Saturation: + + $TOOLTIP_TakeImage_LEDIntensity
+ Flip Image + + + $TOOLTIP_TakeImage_CamVflip + Brightness: - - 0 + + 0 $TOOLTIP_TakeImage_CamBrightness
+ + + degree + $TOOLTIP_Alignment_InitialRotate + Contrast: + + + 0 + $TOOLTIP_TakeImage_CamContrast
+ + + degree + + Saturation: + + + 0 + $TOOLTIP_TakeImage_CamSaturation
+ Auto-exposure Control + + + $TOOLTIP_TakeImage_CamAec + Auto Exposure Level: + + + 0 + $TOOLTIP_TakeImage_CamAeLevel
+ AutoSharpness + + + $TOOLTIP_TakeImage_CamAutoSharpness + Sharpness: + + + 0 + $TOOLTIP_TakeImage_CamSharpness
+ - - - - - - - - + - - - + + + +
Reference Image:
- -
+ +
- - - - - +
+ Reference Image:
+ + function UpdateExpertModus() { + var _style_pur = 'none'; + var _hidden = true; + + if (document.getElementById("ExpertModus_enabled").checked) { + _style_pur = ''; + _hidden = false; + firework.launch("Expert parameter view activated. Please use carefully", 'warning', 5000); + } + const expert = document.querySelectorAll(".expert"); + + for (var i = 0; i < expert.length; i++) { + expert[i].style.display = _style_pur; + } + } + + function UpdateTooltipModus() { + var _style_pur = 'none'; + var _hidden = true; + + if (param["System"]["Tooltip"].value1 == 'true') { + _style_pur = ''; + _hidden = false; + } + + const tooltip = document.querySelectorAll(".tooltip"); + + for (var i = 0; i < tooltip.length; i++) { + tooltip[i].style.display = _style_pur; + } + } + + function getCoords(elem) { + // crossbrowser version + var box = elem.getBoundingClientRect(); + var body = document.body; + var docEl = document.documentElement; + var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop; + var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft; + var clientTop = docEl.clientTop || body.clientTop || 0; + var clientLeft = docEl.clientLeft || body.clientLeft || 0; + var top = box.top + scrollTop - clientTop; + var left = box.left + scrollLeft - clientLeft; + + return { top: Math.round(top), left: Math.round(left) }; + } + + function InvertEnableItem(_cat, _param) { + _zw = _cat + "_" + _param + "_enabled"; + _isOn = document.getElementById(_zw).checked; + + _color = "rgb(122, 122, 122)"; + + if (_isOn) { + _color = "black"; + } + + _zw = _cat + "_" + _param + "_text"; + document.getElementById(_zw).disabled = !_isOn; + document.getElementById(_zw).style.color = _color; + + setEnabled(_cat + "_" + _param, _isOn); + + for (var j = 1; j <= param[_cat][_param]["anzParam"]; ++j) { + document.getElementById(_cat+"_"+_param+"_value"+j).disabled = !_isOn; + document.getElementById(_cat+"_"+_param+"_value"+j).style.color = _color; + } + } + + function setEnabled(className, enabled) { + _color = "rgb(122, 122, 122)"; + + if (enabled) { + _color = "black"; + } + + let elements = document.getElementsByClassName(className); + + for (i = 0; i < elements.length; i++) { + if (enabled) { + elements[i].classList.remove("disabled"); + } + else { + elements[i].classList.add("disabled"); + } + + let inputs = elements[i].getElementsByTagName("input"); + + for (j = 0; j < inputs.length; j++) { + if (inputs[j].id.endsWith("_enabled")) { + continue; + } + + inputs[j].style.color = _color; + + if (enabled) { + inputs[j].removeAttribute("disabled"); + } + else { + inputs[j].setAttribute("disabled", "disabled"); + } + } + } + } + + function setVisible(className, visible) { + let elements = document.getElementsByClassName(className); + + for (i = 0; i < elements.length; i++) { + if (visible) { + elements[i].classList.remove("hidden"); + } + else { + elements[i].classList.add("hidden"); + } + } + } + + function EnDisableItem(_status, _param, _category, _cat, _name, _optional, _number = -1) { + //_status = _category[_cat]["enabled"]; + + _color = "rgb(122, 122, 122)"; + + if (_status) { + _color = "black"; + } + + if (_optional) { + document.getElementById(_cat+"_"+_name+"_enabled").disabled = !_status; + document.getElementById(_cat+"_"+_name+"_enabled").style.color = _color; + } + + if (_number == -1) { + if (!_param[_cat][_name]["enabled"]) { + _status = false; + _color = "rgb(122, 122, 122)"; + } + } + else { + if (!NUMBERS[_number][_cat][_name]["enabled"]) { + _status = false; + _color = "rgb(122, 122, 122)"; + } + } + + document.getElementById(_cat+"_"+_name+"_text").disabled = !_status; + document.getElementById(_cat+"_"+_name+"_text").style.color = _color; + + setEnabled(_cat+"_"+_name, _status); + + for (var j = 1; j <= _param[_cat][_name]["anzParam"]; ++j) { + document.getElementById(_cat+"_"+_name+"_value"+j).disabled = !_status; + document.getElementById(_cat+"_"+_name+"_value"+j).style.color = _color; + } + } + + function ReadParameter(_param, _cat, _name, _optional, _number = -1) { + if (_number > -1) { + if (_cat == "Digits") { + _cat = "digit"; + } + + if (_cat == "Analog") { + _cat = "analog"; + } + + if ((NUMBERS[_number] == undefined) || (NUMBERS[_number][_cat] == undefined) || (NUMBERS[_number][_cat][_name] == undefined)) { + return; + } + + if (_optional) { + NUMBERS[_number][_cat][_name]["enabled"] = document.getElementById(_cat+"_"+_name+"_enabled").checked; + } + + for (var j = 1; j <= _param[_cat][_name]["anzParam"]; ++j) { + let element = document.getElementById(_cat+"_"+_name+"_value"+j); + + if (element.tagName.toLowerCase() == "select") { + NUMBERS[_number][_cat][_name]["value"+j] = element.selectedIndex > -1 ? element.options[element.selectedIndex].value : ""; + } + else if ((element.getAttribute("type") != null) && (element.getAttribute("type").toLowerCase() == "checkbox")) { + NUMBERS[_number][_cat][_name]["value"+j] = element.checked; + } + else { + if ((NUMBERS[_number][_cat][_name].checkRegExList != null) && (NUMBERS[_number][_cat][_name].checkRegExList[j-1] != null)) { + if (!element.value.match(NUMBERS[_cat][_name].checkRegExList[j-1])) { + element.classList.add("invalid-input"); + } + else { + element.classList.remove("invalid-input"); + } + } + NUMBERS[_number][_cat][_name]["value"+j] = element.value; + } + } + } + else { + if (_optional) { + _param[_cat][_name]["enabled"] = document.getElementById(_cat+"_"+_name+"_enabled").checked; + } + + for (var j = 1; j <= _param[_cat][_name]["anzParam"]; ++j) { + let element = document.getElementById(_cat+"_"+_name+"_value"+j); + + if (element.tagName.toLowerCase() == "select") { + _param[_cat][_name]["value"+j] = element.selectedIndex > -1 ? element.options[element.selectedIndex].value : ""; + } + else if ((element.getAttribute("type") != null) && (element.getAttribute("type").toLowerCase() == "checkbox")) { + _param[_cat][_name]["value"+j] = element.checked; + } + else { + if ((_param[_cat][_name].checkRegExList != null) && (_param[_cat][_name].checkRegExList[j-1] != null)) { + if (!element.value.match(_param[_cat][_name].checkRegExList[j-1])) { + element.classList.add("invalid-input"); + } + else { + element.classList.remove("invalid-input"); + } + } + _param[_cat][_name]["value"+j] = element.value; + } + } + } + } + + function WriteParameter(_param, _category, _cat, _name, _optional, _outval = false, _number = -1) { + let anzpara; + try { + anzpara = _param[_cat][_name].anzParam; + } + catch (error) { + firework.launch("Parameter '" + _name + "' in der Kategorie '" + _cat + "' ist unbekannt!", 'danger', 30000); + return; + } + + if (_number > -1) { + if ((NUMBERS[_number] == undefined) || (NUMBERS[_number][_cat] == undefined) || (NUMBERS[_number][_cat][_name] == undefined)) { + return; + } + + if (_optional) { + document.getElementById(_cat+"_"+_name+"_enabled").checked = NUMBERS[_number][_cat][_name]["enabled"]; + + for (var j = 1; j <= anzpara; ++j) { + document.getElementById(_cat+"_"+_name+"_value"+j).disabled = !NUMBERS[_number][_cat][_name]["enabled"]; + } + } + + document.getElementById(_cat+"_"+_name+"_text").style.color = "black" + setEnabled(_cat+"_"+_name, true); + + for (var j = 1; j <= anzpara; ++j) { + let element = document.getElementById(_cat+"_"+_name+"_value"+j); + + if (element.tagName.toLowerCase() == "select") { + var textToFind = NUMBERS[_number][_cat][_name]["value"+j]; + + if (textToFind == undefined) { + continue; + } + + _isFound = false; + element.selectedIndex = -1; + + for (var i = 0; i < element.options.length; i++) { + if (element.options[i].value.toLowerCase() === textToFind.toLowerCase()) { + element.selectedIndex = i; + _isFound = true; + break; + } + } + + if (!_isFound) { + _zw_txt = "Der Wert '" + textToFind + "' des Parameters '" + _name; + _zw_txt = _zw_txt + "' im Bereich '" + _cat + "' ist ungültig. BITTE VOR DEM SPEICHERN PRÜFEN!"; + firework.launch(_zw_txt, 'warning', 10000); + } + } + else if ((element.getAttribute("type") != null) && (element.getAttribute("type").toLowerCase() == "checkbox")) { + element.checked = NUMBERS[_number][_cat][_name]["value"+j] == "true"; + } + else { + element.value = NUMBERS[_number][_cat][_name]["value"+j]; + } + } + + if (_outval) { + for (var j = 1; j <= anzpara; ++j) { + document.getElementById(_cat+"_"+_name+"_value"+j+"_output").value = document.getElementById(_cat+"_"+_name+"_value"+j).value; + } + } + } + else { + if (_optional) { + document.getElementById(_cat+"_"+_name+"_enabled").checked = _param[_cat][_name]["enabled"]; + + for (var j = 1; j <= anzpara; ++j) { + document.getElementById(_cat+"_"+_name+"_value"+j).disabled = !_param[_cat][_name]["enabled"]; + } + } + + document.getElementById(_cat+"_"+_name+"_text").style.color = "black" + setEnabled(_cat+"_"+_name, true); + + for (var j = 1; j <= anzpara; ++j) { + let element = document.getElementById(_cat+"_"+_name+"_value"+j); + + if (element.tagName.toLowerCase() == "select") { + var textToFind = _param[_cat][_name]["value"+j]; + + if (textToFind == undefined) { + continue; + } + + _isFound = false; + element.selectedIndex = -1; + + for (var i = 0; i < element.options.length; i++) { + if (element.options[i].value.toLowerCase() === textToFind.toLowerCase()) { + element.selectedIndex = i; + _isFound = true; + break; + } + } + + if (!_isFound) { + _zw_txt = "Der Wert '" + textToFind + "' des Parameters '" + _name; + _zw_txt = _zw_txt + "' im Bereich '" + _cat + "' ist ungültig. BITTE VOR DEM SPEICHERN PRÜFEN!"; + firework.launch(_zw_txt, 'warning', 10000); + } + + } + else if ((element.getAttribute("type") != null) && (element.getAttribute("type").toLowerCase() == "checkbox")) { + element.checked = _param[_cat][_name]["value"+j] == "true"; + } + else { + element.value = _param[_cat][_name]["value"+j]; + } + } + + if (_outval) { + document.getElementById(_cat+"_"+_name+"_value1_output").value = document.getElementById(_cat+"_"+_name+"_value1").value; + } + } + + ///////////////// am Ende, falls Kategorie als gesamtes nicht ausgewählt --> deaktivieren + if (_category[_cat]["enabled"] == false) { + if (_optional) { + document.getElementById(_cat+"_"+_name+"_enabled").disabled = true; + + for (var j = 1; j <= anzpara; ++j) { + document.getElementById(_cat+"_"+_name+"_value"+j).disabled = true; + } + } + + document.getElementById(_cat+"_"+_name+"_text").style="color: gray;" + setEnabled(_cat+"_"+_name, false); + } + + EnDisableItem(_category[_cat]["enabled"], _param, _category, _cat, _name, _optional, _number); + } + + /* hash #description open the details part of the page */ + function openDescription() { + if(window.location.hash) { + var hash = window.location.hash.substring(1); + + if(hash == 'description') { + document.getElementById("desc_details").open = true; + } + } + } + + init(); + + diff --git a/sd-card/html/img/flipImageSize.png b/sd-card/html/img/flipImageSize.png new file mode 100644 index 00000000..dd761aa5 Binary files /dev/null and b/sd-card/html/img/flipImageSize.png differ diff --git a/sd-card/html/index.html b/sd-card/html/index.html index 2738c150..12a580cb 100644 --- a/sd-card/html/index.html +++ b/sd-card/html/index.html @@ -21,21 +21,21 @@ var streamPopup; var streamFlashlight = false; var streamWindowFeatures = - 'channelmode=no,directories=no,fullscreen=no,' + - 'location=no,dependent=yes,menubar=no,resizable=no,scrollbars=no,' + - 'status=no,toolbar=no,titlebar=no,' + - 'left=10,top=250,width=640px,height=480px'; + 'channelmode=no,directories=no,fullscreen=no,' + + 'location=no,dependent=yes,menubar=no,resizable=no,scrollbars=no,' + + 'status=no,toolbar=no,titlebar=no,' + + 'left=10,top=260,width=640px,height=480px'; - function loadPage(page) { + function loadPage(page) { console.log("loadPage( " + page + " )"); - if (streamPopup) // Ensure that stream popup is closed because it's blocking web interface + if (streamPopup) { // Ensure that stream popup is closed because it's blocking web interface streamPopup.close(); + } asyncPageLoad(page); } - async function asyncPageLoad(page ) { console.log(" loading " + page + " ..."); document.cookie = "page="+page + "; path=/"; @@ -46,7 +46,6 @@ }); } - function resetMenu() { [].forEach.call(document.querySelectorAll('.submenu'), function (el) { el.style.visibility = 'visible'; @@ -54,18 +53,20 @@ } function getCookie(cname) { - let name = cname + "="; - let decodedCookie = decodeURIComponent(document.cookie); - let ca = decodedCookie.split(';'); - for(let i = 0; i @@ -88,21 +89,21 @@