瀏覽代碼

Merge branch 'main' into mqtt-add-ValidateServerCert-Parameter

SybexX 11 月之前
父節點
當前提交
842229ea98
共有 49 個文件被更改,包括 1708 次插入786 次删除
  1. 3 3
      .github/workflows/manual-update-webinstaller.yaml
  2. 3 0
      .gitmodules
  3. 49 30
      Changelog.md
  4. 1 1
      code/CMakeLists.txt
  5. 1 0
      code/components/esp-protocols
  6. 116 61
      code/components/jomjol_controlGPIO/Color.cpp
  7. 81 37
      code/components/jomjol_controlGPIO/Color.h
  8. 60 0
      code/components/jomjol_controlGPIO/RmtDriver.h
  9. 143 0
      code/components/jomjol_controlGPIO/RmtDriver4.cpp
  10. 68 0
      code/components/jomjol_controlGPIO/RmtDriver4.h
  11. 202 0
      code/components/jomjol_controlGPIO/RmtDriver5.cpp
  12. 91 0
      code/components/jomjol_controlGPIO/RmtDriver5.h
  13. 30 85
      code/components/jomjol_controlGPIO/SmartLeds.cpp
  14. 238 331
      code/components/jomjol_controlGPIO/SmartLeds.h
  15. 2 1
      code/components/jomjol_controlGPIO/server_GPIO.cpp
  16. 1 1
      code/components/jomjol_controlcamera/ClassControllCamera.cpp
  17. 7 5
      code/components/jomjol_controlcamera/server_camera.cpp
  18. 8 7
      code/components/jomjol_fileserver_ota/server_file.cpp
  19. 3 2
      code/components/jomjol_fileserver_ota/server_ota.cpp
  20. 17 8
      code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp
  21. 1 0
      code/components/jomjol_flowcontroll/ClassFlowControll.cpp
  22. 12 2
      code/components/jomjol_flowcontroll/ClassFlowInfluxDB.cpp
  23. 3 0
      code/components/jomjol_flowcontroll/ClassFlowInfluxDB.h
  24. 10 2
      code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.cpp
  25. 4 0
      code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.h
  26. 9 9
      code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp
  27. 22 21
      code/components/jomjol_flowcontroll/MainFlowControl.cpp
  28. 1 0
      code/components/jomjol_helper/Helper.h
  29. 173 138
      code/components/jomjol_influxdb/interface_influxdb.cpp
  30. 96 7
      code/components/jomjol_influxdb/interface_influxdb.h
  31. 2 1
      code/components/jomjol_mqtt/server_mqtt.cpp
  32. 107 0
      code/components/jomjol_wlan/basic_auth.cpp
  33. 8 0
      code/components/jomjol_wlan/basic_auth.h
  34. 11 0
      code/components/jomjol_wlan/connect_wlan.cpp
  35. 23 0
      code/components/jomjol_wlan/read_wlanini.cpp
  36. 2 0
      code/components/jomjol_wlan/read_wlanini.h
  37. 3 4
      code/dependencies.lock
  38. 39 11
      code/main/main.cpp
  39. 6 5
      code/main/server_main.cpp
  40. 5 4
      code/main/softAP.cpp
  41. 20 1
      code/sdkconfig.defaults
  42. 7 0
      docs/index.html
  43. 9 9
      param-docs/parameter-pages/PostProcessing/NUMBER.ChangeRateThreshold.md
  44. 二進制
      sd-card/config/ana-cont_1400_s2_q.tflite
  45. 二進制
      sd-card/config/dig-class11_1900_s2_q.tflite
  46. 二進制
      sd-card/config/dig-class11_1910_s2_q.tflite
  47. 二進制
      sd-card/config/dig-cont_0800_s3_q.tflite
  48. 二進制
      sd-card/config/dig-cont_0810_s3_q.tflite
  49. 11 0
      sd-card/wlan.ini

+ 3 - 3
.github/workflows/manual-update-webinstaller.yaml

@@ -50,13 +50,13 @@ jobs:
         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@v5
 
     - name: Upload artifact
-      uses: actions/upload-pages-artifact@v1
+      uses: actions/upload-pages-artifact@v3
       with:
         path: 'docs'
 
     - name: Deploy to GitHub Pages
       id: deployment
-      uses: actions/deploy-pages@v1   
+      uses: actions/deploy-pages@v4 

+ 3 - 0
.gitmodules

@@ -10,3 +10,6 @@
 [submodule "code/components/stb"]
 	path = code/components/stb
 	url = https://github.com/nothings/stb.git
+[submodule "code/components/esp-protocols"]
+	path = code/components/esp-protocols
+	url = https://github.com/espressif/esp-protocols.git

+ 49 - 30
Changelog.md

@@ -1,3 +1,22 @@
+## [16.0.0-RC6] - 2024-xx-xx
+
+For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.7.0...v16.0.0)
+
+#### Known issues
+Please check the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues) and
+[discussions](https://github.com/jomjol/AI-on-the-edge-device/discussions) before reporting a new issue.
+
+#### Core Changes
+Only changes since RC5 are listed:
+- [#3436](https://github.com/jomjol/AI-on-the-edge-device/pull/3436) Added basic authentification of the Web Interface and the REST API, see https://jomjol.github.io/AI-on-the-edge-device-docs/Password-Protection
+- xxx
+
+  **:warning: Please check your Homeassistant instance to make sure it is handled correctly!**
+
+#### Bug Fixes
+Only changes since RC5 are listed:
+ - xxx
+
 ## [16.0.0-RC5] - 2024-12-05
 
 For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.7.0...v16.0.0)
@@ -8,25 +27,25 @@ Please check the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues
 
 #### Core Changes
 Only changes since RC4 are listed:
-- Removed `Autostart` parameter and make the flow to be always enabled (#3423)
-- Enable `Flow start` menu entry in UI (#3423)
-- Updated the Homeassistant Discovery topics (#3332):
+- [#3423](https://github.com/jomjol/AI-on-the-edge-device/pull/3423) Removed `Autostart` parameter and make the flow to be always enabled 
+- [#3423](https://github.com/jomjol/AI-on-the-edge-device/pull/3423) Enable `Flow start` menu entry in UI 
+- [#3332](https://github.com/jomjol/AI-on-the-edge-device/pull/3332) Updated the Homeassistant Discovery topics :
     - `raw` has now set the `State Class` to `measurement`. Before it was always set to `""`. 
     - `value` has now only set the `State Class` to `total_increasing` if the parameter `Allow Negative Rates` is **not** set. Else it uses `measurement` since the rate could also be negative. Before it was always set to `total_increasing`.
     - The `rate_per_time_unit` topic of an **Energy** meter needs a `Device Class`=`power`. For `gas` and `water` it should be `volume_flow_rate`. Before it was always set to `""`.
-    - Added button for `flow start` (#3415)
-    - Added support for Domoticz MQTT integration (#3359)
+    - [#3415](https://github.com/jomjol/AI-on-the-edge-device/pull/3415) Added button for `flow start` 
+    - [#3359](https://github.com/jomjol/AI-on-the-edge-device/pull/3359) Added support for Domoticz MQTT integration 
     - Added Date and time to overview page
     - Updated submodules and models
 
   **:warning: Please check your Homeassistant instance to make sure it is handled correctly!**
 
 #### Bug Fixes
-Only changes since RC3 are listed:
- - Added fix for ledintensity (#3418)
- - Added fix for OV2640 brightness contrast saturation (#3417)
- - Added fix for 'AnalogToDigitTransitionStart' always using 9.2 regardless of the configured value (#3393)
- - Addef fix for HA menu entry (#3342)
+Only changes since RC4 are listed:
+ - [#3418](https://github.com/jomjol/AI-on-the-edge-device/pull/3418) Added fix for ledintensity 
+ - [#3417](https://github.com/jomjol/AI-on-the-edge-device/pull/3417) Added fix for OV2640 brightness contrast saturation 
+ - [#3393](https://github.com/jomjol/AI-on-the-edge-device/pull/3393) Added fix for 'AnalogToDigitTransitionStart' always using 9.2 regardless of the configured value 
+ - [#3342](https://github.com/jomjol/AI-on-the-edge-device/pull/3342) Added fix for HA menu entry 
 
 
 ## [16.0.0-RC4] - 2024-10-06
@@ -39,13 +58,13 @@ Please check the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues
 
 #### Core Changes
 Only changes since RC3 are listed:
-- Update esp32-camera submodule to `v2.0.13` (#3316)
-- Added contributor list (#3317)
-- Added files for demo mode (#3315)
+- [#3316](https://github.com/jomjol/AI-on-the-edge-device/pull/3316) Update esp32-camera submodule to `v2.0.13` 
+- [#3317](https://github.com/jomjol/AI-on-the-edge-device/pull/3317) Added contributor list 
+- [#3315](https://github.com/jomjol/AI-on-the-edge-device/pull/3315) Added files for demo mode 
 
 #### Bug Fixes
 Only changes since RC2 are listed:
-- Added delay in InitCam (#3313) to fix `Camera not detected` issues
+- [#3313](https://github.com/jomjol/AI-on-the-edge-device/pull/3313) Added delay in InitCam  to fix `Camera not detected` issues
 
 
 ## [16.0.0-RC3] - 2024-10-05
@@ -62,7 +81,7 @@ Only changes since RC2 are listed:
 
 #### Bug Fixes
 Only changes since RC2 are listed:
-- Re-did revertion of TFlite submodule update as certain modules crash with it (#3269) (change was lost)
+- [#3269](https://github.com/jomjol/AI-on-the-edge-device/pull/3269) Re-did revertion of TFlite submodule update as certain modules crash with it  (change was lost)
 
 
 ## [16.0.0-RC2] - 2024-10-04
@@ -76,13 +95,13 @@ Please check the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues
 #### Core Changes
 Only changes since RC1 are listed:
 - Updated parameter documentation pages
-- Rename/remove unused parameters (#3291)
-- Migrate-cam-parameters (#3288)
+- [#3291](https://github.com/jomjol/AI-on-the-edge-device/pull/3291) Rename/remove unused parameters 
+- [#3288](https://github.com/jomjol/AI-on-the-edge-device/pull/3288) Migrate-cam-parameters 
 
 #### Bug Fixes
 Only changes since RC1 are listed:
-- Reverted TFlite submodule update as certain modules crash with it (#3269)
-- Changed the webhook UploadImg to false (#3279)
+- [#3269](https://github.com/jomjol/AI-on-the-edge-device/pull/3269) Reverted TFlite submodule update as certain modules crash with it 
+- [#3279](https://github.com/jomjol/AI-on-the-edge-device/pull/3279) Changed the webhook UploadImg to false 
 - Changed default value from boolean to numeric value in parameter camDenoise documentation
 - Updated config page
 
@@ -96,24 +115,24 @@ Please check the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues
 
 #### Core Changes
 Those are just the major changes:
-- Add support for OV5640 camera (#3063)
+- [#3063](https://github.com/jomjol/AI-on-the-edge-device/pull/3063) Add support for OV5640 camera 
 - New tflite-Models
-- Homeassistant service discovery: derive node_id when using nested topics (#3088)
-- Added Prometheus/OpenMetrics exporter (#3081)
-- Added Webhook (#3148, #3163, #3174)
-- Add rate threshold parameter (#3195)
-- Added a Delay between the WiFi reconnections (#3068)
+- [#3088](https://github.com/jomjol/AI-on-the-edge-device/pull/3088) Homeassistant service discovery: derive node_id when using nested topics 
+- [#3081](https://github.com/jomjol/AI-on-the-edge-device/pull/3081) Added Prometheus/OpenMetrics exporter 
+- [#3148](https://github.com/jomjol/AI-on-the-edge-device/pull/3148), [#3163](https://github.com/jomjol/AI-on-the-edge-device/pull/3163), [#3174](https://github.com/jomjol/AI-on-the-edge-device/pull/3148), [#3163](https://github.com/jomjol/AI-on-the-edge-device/pull/3163), [#3174](https://github.com/jomjol/AI-on-the-edge-device/pull/3174) Added Webhook 
+- [#3195](https://github.com/jomjol/AI-on-the-edge-device/pull/3195) Add rate threshold parameter 
+- [#3068](https://github.com/jomjol/AI-on-the-edge-device/pull/3068) Added a Delay between the WiFi reconnections 
 - Web UI improvements
 - Various minor changes
 - Update platformIO to 6.9.0 (Contains ESP IDF 5.3.1)
 
 #### Bug Fixes
 Those are just the major changes:
-- Handle crash on corrupted model (#3220)
-- Bugfix for boot loop (#3175)
-- Bugfix for time stamp (#3180)
-- Handle empty prevalue.ini gracefully (#3162)
-- Added note about only TLS 1.2 is supported (#3213)
+- [#3220](https://github.com/jomjol/AI-on-the-edge-device/pull/3220) Handle crash on corrupted model 
+- [#3175](https://github.com/jomjol/AI-on-the-edge-device/pull/3175) Bugfix for boot loop 
+- [#3180](https://github.com/jomjol/AI-on-the-edge-device/pull/3180) Bugfix for time stamp 
+- [#3162](https://github.com/jomjol/AI-on-the-edge-device/pull/3162) Handle empty prevalue.ini gracefully 
+- [#3213](https://github.com/jomjol/AI-on-the-edge-device/pull/3213) Added note about only TLS 1.2 is supported 
 
 ## [15.7.0] - 2024-02-17
 

+ 1 - 1
code/CMakeLists.txt

@@ -1,6 +1,6 @@
 cmake_minimum_required(VERSION 3.16.0)
 
-list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common components/esp-tflite-micro)
+list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common components/esp-tflite-micro components/esp-protocols/components/mdns)
 
 ADD_CUSTOM_COMMAND(
     OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.cpp

+ 1 - 0
code/components/esp-protocols

@@ -0,0 +1 @@
+Subproject commit 9b74256b518b009cd94c104e952af37bb79be273

+ 116 - 61
code/components/jomjol_controlGPIO/Color.cpp

@@ -1,27 +1,53 @@
+/********************************************************************************
+ * https://github.com/RoboticsBrno/SmartLeds
+ *
+ * MIT License
+ * 
+ * Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
+
 #include "Color.h"
 #include <algorithm>
-#include <cmath>
 #include <cassert>
+#include <cmath>
 
 namespace {
 
 // Int -> fixed point
-int up( int x ) { return x * 255; }
+int up(int x) { return x * 255; }
 
 } // namespace
 
-int iRgbSqrt( int num ) {
+int iRgbSqrt(int num) {
     // https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_.28base_2.29
-    assert( "sqrt input should be non-negative" && num >= 0 );
-    assert( "sqrt input should no exceed 16 bits" && num <= 0xFFFF );
+    assert("sqrt input should be non-negative" && num >= 0);
+    assert("sqrt input should no exceed 16 bits" && num <= 0xFFFF);
     int res = 0;
     int bit = 1 << 16;
-    while ( bit > num )
+    while (bit > num)
         bit >>= 2;
-    while ( bit != 0 ) {
-        if ( num >= res + bit ) {
+    while (bit != 0) {
+        if (num >= res + bit) {
             num -= res + bit;
-            res = ( res >> 1 ) + bit;
+            res = (res >> 1) + bit;
         } else
             res >>= 1;
         bit >>= 2;
@@ -29,104 +55,133 @@ int iRgbSqrt( int num ) {
     return res;
 }
 
-Rgb::Rgb( Hsv y ) {
+Rgb::Rgb(const Hsv& y) {
     // https://stackoverflow.com/questions/24152553/hsv-to-rgb-and-back-without-floating-point-math-in-python
     // greyscale
-    if( y.s == 0 ) {
+    if (y.s == 0) {
         r = g = b = y.v;
         return;
     }
 
     const int region = y.h / 43;
-    const int remainder = ( y.h - ( region * 43 ) ) * 6;
-
-    const int p = ( y.v * ( 255 - y.s ) ) >> 8;
-    const int q = ( y.v * ( 255 - ( ( y.s * remainder ) >> 8 ) ) ) >> 8;
-    const int t = ( y.v * ( 255 - ( ( y.s * (255 -remainder ) ) >> 8 ) ) ) >> 8;
-
-    switch( region ) {
-        case 0: r = y.v; g = t; b = p; break;
-        case 1: r = q; g = y.v; b = p; break;
-        case 2: r = p; g = y.v; b = t; break;
-        case 3: r = p; g = q; b = y.v; break;
-        case 4: r = t; g = p; b = y.v; break;
-        case 5: r = y.v; g = p; b = q; break;
-        default: __builtin_trap();
+    const int remainder = (y.h - (region * 43)) * 6;
+
+    const int p = (y.v * (255 - y.s)) >> 8;
+    const int q = (y.v * (255 - ((y.s * remainder) >> 8))) >> 8;
+    const int t = (y.v * (255 - ((y.s * (255 - remainder)) >> 8))) >> 8;
+
+    switch (region) {
+    case 0:
+        r = y.v;
+        g = t;
+        b = p;
+        break;
+    case 1:
+        r = q;
+        g = y.v;
+        b = p;
+        break;
+    case 2:
+        r = p;
+        g = y.v;
+        b = t;
+        break;
+    case 3:
+        r = p;
+        g = q;
+        b = y.v;
+        break;
+    case 4:
+        r = t;
+        g = p;
+        b = y.v;
+        break;
+    case 5:
+        r = y.v;
+        g = p;
+        b = q;
+        break;
+    default:
+        __builtin_trap();
     }
 
     a = y.a;
 }
 
-Rgb& Rgb::operator=( Hsv hsv ) {
-    Rgb r{ hsv };
-    swap( r );
+Rgb& Rgb::operator=(const Hsv& hsv) {
+    Rgb r { hsv };
+    swap(r);
     return *this;
 }
 
-Rgb Rgb::operator+( Rgb in ) const {
+Rgb Rgb::operator+(const Rgb& in) const {
     auto copy = *this;
     copy += in;
     return copy;
 }
 
-Rgb& Rgb::operator+=( Rgb in ) {
+Rgb& Rgb::operator+=(const Rgb& in) {
     unsigned int red = r + in.r;
-    r = ( red < 255 ) ? red : 255;
+    r = (red < 255) ? red : 255;
     unsigned int green = g + in.g;
-    g = ( green < 255 ) ? green : 255;
+    g = (green < 255) ? green : 255;
     unsigned int blue = b + in.b;
-    b = ( blue < 255 ) ? blue : 255;
+    b = (blue < 255) ? blue : 255;
     return *this;
 }
 
-Rgb& Rgb::blend( Rgb in ) {
-    unsigned int inAlpha = in.a * ( 255 - a );
-    unsigned int alpha = a + inAlpha;
-    r = iRgbSqrt( ( ( r * r * a ) + ( in.r * in.r * inAlpha ) ) / alpha );
-    g = iRgbSqrt( ( ( g * g * a ) + ( in.g * in.g * inAlpha ) ) / alpha );
-    b = iRgbSqrt( ( ( b * b * a ) + ( in.b * in.b * inAlpha ) ) / alpha );
-    a = alpha;
+Rgb Rgb::operator-(const Rgb& in) const {
+    auto copy = *this;
+    copy -= in;
+    return copy;
+}
+
+Rgb& Rgb::operator-=(const Rgb& in) {
+    r = (in.r > r) ? 0 : r - in.r;
+    g = (in.g > g) ? 0 : g - in.g;
+    b = (in.b > b) ? 0 : b - in.b;
     return *this;
 }
 
-uint8_t IRAM_ATTR Rgb::getGrb( int idx ) {
-    switch ( idx ) {
-        case 0: return g;
-        case 1: return r;
-        case 2: return b;
-    }
-    __builtin_unreachable();
+Rgb& Rgb::blend(const Rgb& in) {
+    unsigned int inAlpha = in.a * (255 - a);
+    unsigned int alpha = a + inAlpha;
+    r = iRgbSqrt(((r * r * a) + (in.r * in.r * inAlpha)) / alpha);
+    g = iRgbSqrt(((g * g * a) + (in.g * in.g * inAlpha)) / alpha);
+    b = iRgbSqrt(((b * b * a) + (in.b * in.b * inAlpha)) / alpha);
+    a = alpha;
+    return *this;
 }
 
-Hsv::Hsv( Rgb r ) {
-    int min = std::min( r.r, std::min( r.g, r.b ) );
-    int max = std::max( r.r, std::max( r.g, r.b ) );
+Hsv::Hsv(const Rgb& r) {
+    int min = std::min(r.r, std::min(r.g, r.b));
+    int max = std::max(r.r, std::max(r.g, r.b));
     int chroma = max - min;
 
     v = max;
-    if ( chroma == 0 ) {
+    if (chroma == 0) {
         h = s = 0;
         return;
     }
 
-    s = up( chroma ) / max;
+    s = up(chroma) / max;
     int hh;
-    if ( max == r.r )
-        hh = ( up( int( r.g ) - int( r.b ) ) ) / chroma / 6;
-    else if ( max == r.g )
-        hh = 255 / 3 + ( up( int( r.b ) - int( r.r ) ) ) / chroma / 6;
+    if (max == r.r)
+        hh = (up(int(r.g) - int(r.b))) / chroma / 6;
+    else if (max == r.g)
+        hh = 255 / 3 + (up(int(r.b) - int(r.r))) / chroma / 6;
     else
-        hh = 2 * 255 / 3 + ( up( int( r.r ) - int( r.g ) ) ) / chroma / 6;
+        hh = 2 * 255 / 3 + (up(int(r.r) - int(r.g))) / chroma / 6;
 
-    if ( hh < 0 )
+    if (hh < 0)
         hh += 255;
     h = hh;
 
     a = r.a;
 }
 
-Hsv& Hsv::operator=( Rgb rgb ) {
-    Hsv h{ rgb };
-    swap( h );
+Hsv& Hsv::operator=(const Rgb& rgb) {
+    Hsv h { rgb };
+    swap(h);
     return *this;
 }

+ 81 - 37
code/components/jomjol_controlGPIO/Color.h

@@ -1,51 +1,90 @@
-#pragma once
+/********************************************************************************
+ * https://github.com/RoboticsBrno/SmartLeds
+ *
+ * MIT License
+ * 
+ * Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
 
-#ifndef COLOR_H
-#define COLOR_H
+#pragma once
 
-#include <cstdint>
 #include "esp_attr.h"
+#include <cstdint>
 union Hsv;
 
 union Rgb {
-    struct __attribute__ ((packed)) {
-        uint8_t r, g, b, a;
+    struct __attribute__((packed)) {
+        uint8_t g, r, b, a;
     };
     uint32_t value;
 
-    Rgb( uint8_t r = 0, uint8_t g = 0, uint8_t b = 0, uint8_t a = 255 ) : r( r ), g( g ), b( b ), a( a ) {}
-    Rgb( Hsv c );
-    Rgb& operator=( Rgb rgb ) { swap( rgb ); return *this; }
-    Rgb& operator=( Hsv hsv );
-    Rgb operator+( Rgb in ) const;
-    Rgb& operator+=( Rgb in );
-    bool operator==( Rgb in ) const { return in.value == value; }
-    Rgb& blend( Rgb in );
-    void swap( Rgb& o ) {  value = o.value; }
+    Rgb(uint8_t r = 0, uint8_t g = 0, uint8_t b = 0, uint8_t a = 255)
+        : g(g)
+        , r(r)
+        , b(b)
+        , a(a) {}
+    Rgb(const Hsv& c);
+    Rgb(const Rgb&) = default;
+    Rgb& operator=(const Rgb& rgb) {
+        swap(rgb);
+        return *this;
+    }
+    Rgb& operator=(const Hsv& hsv);
+    Rgb operator+(const Rgb& in) const;
+    Rgb& operator+=(const Rgb& in);
+    Rgb operator-(const Rgb& in) const;
+    Rgb& operator-=(const Rgb& in);
+    bool operator==(const Rgb& in) const { return in.value == value; }
+    Rgb& blend(const Rgb& in);
+    void swap(const Rgb& o) { value = o.value; }
     void linearize() {
         r = channelGamma(r);
         g = channelGamma(g);
         b = channelGamma(b);
     }
 
-    uint8_t IRAM_ATTR getGrb( int idx );
-
-    void stretchChannels( uint8_t maxR, uint8_t maxG, uint8_t maxB ) {
-        r = stretch( r, maxR );
-        g = stretch( g, maxG );
-        b = stretch( b, maxB );
+    inline uint8_t IRAM_ATTR getGrb(int idx) {
+        switch (idx) {
+        case 0:
+            return g;
+        case 1:
+            return r;
+        case 2:
+            return b;
+        }
+        __builtin_unreachable();
     }
 
-    void stretchChannelsEvenly( uint8_t max ) {
-        stretchChannels( max, max, max );
+    void stretchChannels(uint8_t maxR, uint8_t maxG, uint8_t maxB) {
+        r = stretch(r, maxR);
+        g = stretch(g, maxG);
+        b = stretch(b, maxB);
     }
 
+    void stretchChannelsEvenly(uint8_t max) { stretchChannels(max, max, max); }
+
 private:
-    uint8_t stretch( int value, uint8_t max ) {
-        return ( value * max ) >> 8;
-    }
+    uint8_t stretch(int value, uint8_t max) { return (value * max) >> 8; }
 
-    uint8_t channelGamma( int channel ) {
+    uint8_t channelGamma(int channel) {
         /* The optimal gamma correction is x^2.8. However, this is expensive to
          * compute. Therefore, we use x^3 for gamma correction. Also, we add a
          * bias as the WS2812 LEDs do not turn on for values less than 4. */
@@ -53,22 +92,27 @@ private:
             return channel;
         channel = channel * channel * channel * 251;
         channel >>= 24;
-        return static_cast< uint8_t >( 4 + channel );
+        return static_cast<uint8_t>(4 + channel);
     }
 };
 
 union Hsv {
-    struct __attribute__ ((packed)) {
+    struct __attribute__((packed)) {
         uint8_t h, s, v, a;
     };
     uint32_t value;
 
-    Hsv( uint8_t h, uint8_t s = 0, uint8_t v = 0, uint8_t a = 255 ) : h( h ), s( s ), v( v ), a( a ) {}
-    Hsv( Rgb r );
-    Hsv& operator=( Hsv h ) { swap( h ); return *this; }
-    Hsv& operator=( Rgb rgb );
-    bool operator==( Hsv in ) const { return in.value == value; }
-    void swap( Hsv& o ) { value = o.value; }
+    Hsv(uint8_t h, uint8_t s = 0, uint8_t v = 0, uint8_t a = 255)
+        : h(h)
+        , s(s)
+        , v(v)
+        , a(a) {}
+    Hsv(const Rgb& r);
+    Hsv& operator=(const Hsv& h) {
+        swap(h);
+        return *this;
+    }
+    Hsv& operator=(const Rgb& rgb);
+    bool operator==(const Hsv& in) const { return in.value == value; }
+    void swap(const Hsv& o) { value = o.value; }
 };
-
-#endif //COLOR_H

+ 60 - 0
code/components/jomjol_controlGPIO/RmtDriver.h

@@ -0,0 +1,60 @@
+/********************************************************************************
+ * https://github.com/RoboticsBrno/SmartLeds
+ *
+ * MIT License
+ * 
+ * Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
+
+#pragma once
+
+#include <esp_system.h>
+#include <stdint.h>
+
+#if defined(ESP_IDF_VERSION)
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
+#define SMARTLEDS_NEW_RMT_DRIVER 1
+#else
+#define SMARTLEDS_NEW_RMT_DRIVER 0
+#endif
+#else
+#define SMARTLEDS_NEW_RMT_DRIVER 0
+#endif
+
+namespace detail {
+
+struct TimingParams {
+    uint32_t T0H;
+    uint32_t T1H;
+    uint32_t T0L;
+    uint32_t T1L;
+    uint32_t TRS;
+};
+
+using LedType = TimingParams;
+
+} // namespace detail
+
+#if SMARTLEDS_NEW_RMT_DRIVER
+#include "RmtDriver5.h"
+#else
+#include "RmtDriver4.h"
+#endif

+ 143 - 0
code/components/jomjol_controlGPIO/RmtDriver4.cpp

@@ -0,0 +1,143 @@
+/********************************************************************************
+ * https://github.com/RoboticsBrno/SmartLeds
+ *
+ * MIT License
+ * 
+ * Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
+
+#include "RmtDriver4.h"
+
+#if !SMARTLEDS_NEW_RMT_DRIVER
+#include "SmartLeds.h"
+
+namespace detail {
+
+// 8 still seems to work, but timings become marginal
+static const int DIVIDER = 4;
+// minimum time of a single RMT duration based on clock ns
+static const double RMT_DURATION_NS = 12.5;
+
+RmtDriver::RmtDriver(const LedType& timing, int count, int pin, int channel_num, SemaphoreHandle_t finishedFlag)
+    : _timing(timing)
+    , _count(count)
+    , _pin((gpio_num_t)pin)
+    , _finishedFlag(finishedFlag)
+    , _channel((rmt_channel_t)channel_num) {
+    _bitToRmt[0].level0 = 1;
+    _bitToRmt[0].level1 = 0;
+    _bitToRmt[0].duration0 = _timing.T0H / (RMT_DURATION_NS * DIVIDER);
+    _bitToRmt[0].duration1 = _timing.T0L / (RMT_DURATION_NS * DIVIDER);
+
+    _bitToRmt[1].level0 = 1;
+    _bitToRmt[1].level1 = 0;
+    _bitToRmt[1].duration0 = _timing.T1H / (RMT_DURATION_NS * DIVIDER);
+    _bitToRmt[1].duration1 = _timing.T1L / (RMT_DURATION_NS * DIVIDER);
+}
+
+esp_err_t RmtDriver::init() {
+    rmt_config_t config = RMT_DEFAULT_CONFIG_TX(_pin, _channel);
+    config.rmt_mode = RMT_MODE_TX;
+    config.clk_div = DIVIDER;
+    config.mem_block_num = 1;
+
+    return rmt_config(&config);
+}
+
+esp_err_t RmtDriver::registerIsr(bool isFirstRegisteredChannel) {
+    auto err = rmt_driver_install(_channel, 0,
+#if defined(CONFIG_RMT_ISR_IRAM_SAFE)
+        ESP_INTR_FLAG_IRAM
+#else
+        0
+#endif
+    );
+    if (err != ESP_OK) {
+        return err;
+    }
+
+    if (isFirstRegisteredChannel) {
+        rmt_register_tx_end_callback(txEndCallback, NULL);
+    }
+
+    err = rmt_translator_init(_channel, translateSample);
+    if (err != ESP_OK) {
+        return err;
+    }
+    return rmt_translator_set_context(_channel, this);
+}
+
+esp_err_t RmtDriver::unregisterIsr() { return rmt_driver_uninstall(_channel); }
+
+void IRAM_ATTR RmtDriver::txEndCallback(rmt_channel_t channel, void* arg) {
+    xSemaphoreGiveFromISR(SmartLed::ledForChannel(channel)->_finishedFlag, nullptr);
+}
+
+void IRAM_ATTR RmtDriver::translateSample(const void* src, rmt_item32_t* dest, size_t src_size,
+    size_t wanted_rmt_items_num, size_t* out_consumed_src_bytes, size_t* out_used_rmt_items) {
+    RmtDriver* self;
+    ESP_ERROR_CHECK(rmt_translator_get_context(out_used_rmt_items, (void**)&self));
+
+    const auto& _bitToRmt = self->_bitToRmt;
+    const auto src_offset = self->_translatorSourceOffset;
+
+    auto* src_components = (const uint8_t*)src;
+    size_t consumed_src_bytes = 0;
+    size_t used_rmt_items = 0;
+
+    while (consumed_src_bytes < src_size && used_rmt_items + 7 < wanted_rmt_items_num) {
+        uint8_t val = *src_components;
+
+        // each bit, from highest to lowest
+        for (uint8_t j = 0; j != 8; j++, val <<= 1) {
+            dest->val = _bitToRmt[val >> 7].val;
+            ++dest;
+        }
+
+        used_rmt_items += 8;
+        ++src_components;
+        ++consumed_src_bytes;
+
+        // skip alpha byte
+        if (((src_offset + consumed_src_bytes) % 4) == 3) {
+            ++src_components;
+            ++consumed_src_bytes;
+
+            // TRST delay after last pixel in strip
+            if (consumed_src_bytes == src_size) {
+                (dest - 1)->duration1 = self->_timing.TRS / (detail::RMT_DURATION_NS * detail::DIVIDER);
+            }
+        }
+    }
+
+    self->_translatorSourceOffset = src_offset + consumed_src_bytes;
+    *out_consumed_src_bytes = consumed_src_bytes;
+    *out_used_rmt_items = used_rmt_items;
+}
+
+esp_err_t RmtDriver::transmit(const Rgb* buffer) {
+    static_assert(sizeof(Rgb) == 4); // The translator code above assumes RGB is 4 bytes
+
+    _translatorSourceOffset = 0;
+    return rmt_write_sample(_channel, (const uint8_t*)buffer, _count * 4, false);
+}
+};
+#endif // !SMARTLEDS_NEW_RMT_DRIVER

+ 68 - 0
code/components/jomjol_controlGPIO/RmtDriver4.h

@@ -0,0 +1,68 @@
+/********************************************************************************
+ * https://github.com/RoboticsBrno/SmartLeds
+ *
+ * MIT License
+ * 
+ * Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
+
+#pragma once
+
+#include "RmtDriver.h"
+
+#if !SMARTLEDS_NEW_RMT_DRIVER
+#include "Color.h"
+#include <driver/rmt.h>
+#include <freertos/FreeRTOS.h>
+#include <freertos/semphr.h>
+
+namespace detail {
+
+constexpr const int CHANNEL_COUNT = RMT_CHANNEL_MAX;
+
+class RmtDriver {
+public:
+    RmtDriver(const LedType& timing, int count, int pin, int channel_num, SemaphoreHandle_t finishedFlag);
+    RmtDriver(const RmtDriver&) = delete;
+
+    esp_err_t init();
+    esp_err_t registerIsr(bool isFirstRegisteredChannel);
+    esp_err_t unregisterIsr();
+    esp_err_t transmit(const Rgb* buffer);
+
+private:
+    static void IRAM_ATTR txEndCallback(rmt_channel_t channel, void* arg);
+
+    static void IRAM_ATTR translateSample(const void* src, rmt_item32_t* dest, size_t src_size,
+        size_t wanted_rmt_items_num, size_t* out_consumed_src_bytes, size_t* out_used_rmt_items);
+
+    const LedType& _timing;
+    int _count;
+    gpio_num_t _pin;
+    SemaphoreHandle_t _finishedFlag;
+
+    rmt_channel_t _channel;
+    rmt_item32_t _bitToRmt[2];
+    size_t _translatorSourceOffset;
+};
+
+};
+#endif // !SMARTLEDS_NEW_RMT_DRIVER

+ 202 - 0
code/components/jomjol_controlGPIO/RmtDriver5.cpp

@@ -0,0 +1,202 @@
+/********************************************************************************
+ * https://github.com/RoboticsBrno/SmartLeds
+ *
+ * MIT License
+ * 
+ * Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
+
+#include "RmtDriver5.h"
+
+#if SMARTLEDS_NEW_RMT_DRIVER
+#include <cstddef>
+
+#include "SmartLeds.h"
+
+namespace detail {
+
+static constexpr const uint32_t RMT_RESOLUTION_HZ = 20 * 1000 * 1000; // 20 MHz
+static constexpr const uint32_t RMT_NS_PER_TICK = 1000000000LLU / RMT_RESOLUTION_HZ;
+
+static RmtEncoderWrapper* IRAM_ATTR encSelf(rmt_encoder_t* encoder) {
+    return (RmtEncoderWrapper*)(((intptr_t)encoder) - offsetof(RmtEncoderWrapper, base));
+}
+
+static size_t IRAM_ATTR encEncode(rmt_encoder_t* encoder, rmt_channel_handle_t tx_channel, const void* primary_data,
+    size_t data_size, rmt_encode_state_t* ret_state) {
+    auto* self = encSelf(encoder);
+
+    // Delay after last pixel
+    if ((self->last_state & RMT_ENCODING_COMPLETE) && self->frame_idx == data_size) {
+        *ret_state = (rmt_encode_state_t)0;
+        return self->copy_encoder->encode(
+            self->copy_encoder, tx_channel, (const void*)&self->reset_code, sizeof(self->reset_code), ret_state);
+    }
+
+    if (self->last_state & RMT_ENCODING_COMPLETE) {
+        Rgb* pixel = ((Rgb*)primary_data) + self->frame_idx;
+        self->buffer_len = sizeof(self->buffer);
+        for (size_t i = 0; i < sizeof(self->buffer); ++i) {
+            self->buffer[i] = pixel->getGrb(self->component_idx);
+            if (++self->component_idx == 3) {
+                self->component_idx = 0;
+                if (++self->frame_idx == data_size) {
+                    self->buffer_len = i + 1;
+                    break;
+                }
+                ++pixel;
+            }
+        }
+    }
+
+    self->last_state = (rmt_encode_state_t)0;
+    auto encoded_symbols = self->bytes_encoder->encode(
+        self->bytes_encoder, tx_channel, (const void*)&self->buffer, self->buffer_len, &self->last_state);
+    if (self->last_state & RMT_ENCODING_MEM_FULL) {
+        *ret_state = RMT_ENCODING_MEM_FULL;
+    } else {
+        *ret_state = (rmt_encode_state_t)0;
+    }
+
+    return encoded_symbols;
+}
+
+static esp_err_t encReset(rmt_encoder_t* encoder) {
+    auto* self = encSelf(encoder);
+    rmt_encoder_reset(self->bytes_encoder);
+    rmt_encoder_reset(self->copy_encoder);
+    self->last_state = RMT_ENCODING_COMPLETE;
+    self->frame_idx = 0;
+    self->component_idx = 0;
+    return ESP_OK;
+}
+
+static esp_err_t encDelete(rmt_encoder_t* encoder) {
+    auto* self = encSelf(encoder);
+    rmt_del_encoder(self->bytes_encoder);
+    rmt_del_encoder(self->copy_encoder);
+    return ESP_OK;
+}
+
+RmtDriver::RmtDriver(const LedType& timing, int count, int pin, int channel_num, SemaphoreHandle_t finishedFlag)
+    : _timing(timing)
+    , _count(count)
+    , _pin(pin)
+    , _finishedFlag(finishedFlag)
+    , _channel(nullptr)
+    , _encoder {} {}
+
+esp_err_t RmtDriver::init() {
+    _encoder.base.encode = encEncode;
+    _encoder.base.reset = encReset;
+    _encoder.base.del = encDelete;
+
+    _encoder.reset_code.duration0 = _timing.TRS / RMT_NS_PER_TICK;
+
+    rmt_bytes_encoder_config_t bytes_cfg = {
+        .bit0 = {
+            .duration0 = uint16_t(_timing.T0H  / RMT_NS_PER_TICK),
+            .level0 = 1,
+            .duration1 = uint16_t(_timing.T0L  / RMT_NS_PER_TICK),
+            .level1 = 0,
+        },
+        .bit1 = {
+            .duration0 = uint16_t(_timing.T1H / RMT_NS_PER_TICK),
+            .level0 = 1,
+            .duration1 = uint16_t(_timing.T1L  / RMT_NS_PER_TICK),
+            .level1 = 0,
+        },
+        .flags = {
+            .msb_first = 1,
+        },
+    };
+
+    auto err = rmt_new_bytes_encoder(&bytes_cfg, &_encoder.bytes_encoder);
+    if (err != ESP_OK) {
+        return err;
+    }
+
+    rmt_copy_encoder_config_t copy_cfg = {};
+    err = rmt_new_copy_encoder(&copy_cfg, &_encoder.copy_encoder);
+    if (err != ESP_OK) {
+        return err;
+    }
+
+    // The config must be in registerIsr, because rmt_new_tx_channel
+    // registers the ISR
+    return ESP_OK;
+}
+
+esp_err_t RmtDriver::registerIsr(bool isFirstRegisteredChannel) {
+    rmt_tx_channel_config_t conf = {
+        .gpio_num = (gpio_num_t)_pin,
+        .clk_src = RMT_CLK_SRC_DEFAULT, //.clk_src = RMT_CLK_SRC_APB,
+        .resolution_hz = RMT_RESOLUTION_HZ,
+        .mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL,
+        .trans_queue_depth = 1,
+        .flags = {},
+    };
+
+    auto err = rmt_new_tx_channel(&conf, &_channel);
+    if (err != ESP_OK) {
+        return err;
+    }
+
+    rmt_tx_event_callbacks_t callbacks_cfg = {};
+    callbacks_cfg.on_trans_done = txDoneCallback;
+
+    err = rmt_tx_register_event_callbacks(_channel, &callbacks_cfg, this);
+    if (err != ESP_OK) {
+        return err;
+    }
+
+    return rmt_enable(_channel);
+}
+
+esp_err_t RmtDriver::unregisterIsr() {
+    auto err = rmt_del_encoder(&_encoder.base);
+    if (err != ESP_OK) {
+        return err;
+    }
+
+    err = rmt_disable(_channel);
+    if (err != ESP_OK) {
+        return err;
+    }
+
+    return rmt_del_channel(_channel);
+}
+
+bool IRAM_ATTR RmtDriver::txDoneCallback(
+    rmt_channel_handle_t tx_chan, const rmt_tx_done_event_data_t* edata, void* user_ctx) {
+    auto* self = (RmtDriver*)user_ctx;
+    auto taskWoken = pdTRUE;
+    xSemaphoreGiveFromISR(self->_finishedFlag, &taskWoken);
+    return taskWoken == pdTRUE;
+}
+
+esp_err_t RmtDriver::transmit(const Rgb* buffer) {
+    rmt_encoder_reset(&_encoder.base);
+    rmt_transmit_config_t cfg = {};
+    return rmt_transmit(_channel, &_encoder.base, buffer, _count, &cfg);
+}
+};
+#endif // !SMARTLEDS_NEW_RMT_DRIVER

+ 91 - 0
code/components/jomjol_controlGPIO/RmtDriver5.h

@@ -0,0 +1,91 @@
+/********************************************************************************
+ * https://github.com/RoboticsBrno/SmartLeds
+ *
+ * MIT License
+ * 
+ * Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
+
+#pragma once
+
+#include "RmtDriver.h"
+
+#if SMARTLEDS_NEW_RMT_DRIVER
+#include <driver/rmt_tx.h>
+#include <freertos/FreeRTOS.h>
+#include <freertos/semphr.h>
+#include <type_traits>
+
+#include "Color.h"
+
+#if !defined(CONFIG_RMT_ISR_IRAM_SAFE) && !defined(SMARTLEDS_DISABLE_IRAM_WARNING)
+#warning "Please enable CONFIG_RMT_ISR_IRAM_SAFE IDF option." \
+    "without it, the IDF driver is not able to supply data fast enough."
+#endif
+
+namespace detail {
+
+constexpr const int CHANNEL_COUNT = SOC_RMT_GROUPS * SOC_RMT_CHANNELS_PER_GROUP;
+
+class RmtDriver;
+
+// This is ridiculous
+struct RmtEncoderWrapper {
+    struct rmt_encoder_t base;
+    struct rmt_encoder_t* bytes_encoder;
+    struct rmt_encoder_t* copy_encoder;
+    RmtDriver* driver;
+    rmt_symbol_word_t reset_code;
+
+    uint8_t buffer[SOC_RMT_MEM_WORDS_PER_CHANNEL / 8];
+    rmt_encode_state_t last_state;
+    size_t frame_idx;
+    uint8_t component_idx;
+    uint8_t buffer_len;
+};
+
+static_assert(std::is_standard_layout<RmtEncoderWrapper>::value == true);
+
+class RmtDriver {
+public:
+    RmtDriver(const LedType& timing, int count, int pin, int channel_num, SemaphoreHandle_t finishedFlag);
+    RmtDriver(const RmtDriver&) = delete;
+
+    esp_err_t init();
+    esp_err_t registerIsr(bool isFirstRegisteredChannel);
+    esp_err_t unregisterIsr();
+    esp_err_t transmit(const Rgb* buffer);
+
+private:
+    static bool IRAM_ATTR txDoneCallback(
+        rmt_channel_handle_t tx_chan, const rmt_tx_done_event_data_t* edata, void* user_ctx);
+
+    const LedType& _timing;
+    int _count;
+    int _pin;
+    SemaphoreHandle_t _finishedFlag;
+
+    rmt_channel_handle_t _channel;
+    RmtEncoderWrapper _encoder;
+};
+
+};
+#endif // !SMARTLEDS_NEW_RMT_DRIVER

+ 30 - 85
code/components/jomjol_controlGPIO/SmartLeds.cpp

@@ -1,90 +1,35 @@
-#include "SmartLeds.h"
-
-
-/* PlatformIO 6 (ESP IDF 5) does no longer allow access to RMTMEM,
-   see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/migration-guides/release-5.x/5.0/peripherals.html?highlight=rmtmem#id5 
-   As a dirty workaround, we copy the needed structures from rmt_struct.h
-   In the long run, this should be replaced! */
-typedef struct rmt_item32_s {
-    union {
-        struct {
-            uint32_t duration0 :15;
-            uint32_t level0 :1;
-            uint32_t duration1 :15;
-            uint32_t level1 :1;
-        };
-        uint32_t val;
-    };
-} rmt_item32_t;
-
-//Allow access to RMT memory using RMTMEM.chan[0].data32[8]
-typedef volatile struct rmt_mem_s {
-    struct {
-        rmt_item32_t data32[64];
-    } chan[8];
-} rmt_mem_t;
-extern rmt_mem_t RMTMEM;
-
+/********************************************************************************
+ * https://github.com/RoboticsBrno/SmartLeds
+ *
+ * MIT License
+ * 
+ * Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
 
+#include "SmartLeds.h"
 
 IsrCore SmartLed::_interruptCore = CoreCurrent;
-intr_handle_t SmartLed::_interruptHandle = NULL;
-
-SmartLed*& IRAM_ATTR SmartLed::ledForChannel( int channel ) {
-    static SmartLed* table[8] = { nullptr };
-    assert( channel < 8 );
-    return table[ channel ];
-}
-
-void IRAM_ATTR SmartLed::interruptHandler(void*) {
-    for (int channel = 0; channel != 8; channel++) {
-        auto self = ledForChannel( channel );
-
-        if ( RMT.int_st.val & (1 << (24 + channel ) ) ) { // tx_thr_event
-            if ( self )
-                self->copyRmtHalfBlock();
-            RMT.int_clr.val |= 1 << ( 24 + channel );
-        } else if ( RMT.int_st.val & ( 1 << (3 * channel ) ) ) { // tx_end
-            if ( self )
-                xSemaphoreGiveFromISR( self->_finishedFlag, nullptr );
-            RMT.int_clr.val |= 1 << ( 3 * channel );
-        }
-    }
-}
-
-void IRAM_ATTR SmartLed::copyRmtHalfBlock() {
-    int offset = detail::MAX_PULSES * _halfIdx;
-    _halfIdx = !_halfIdx;
-    int len = 3 - _componentPosition + 3 * ( _count - 1 );
-    len = std::min( len, detail::MAX_PULSES / 8 );
-
-    if ( !len ) {
-        for ( int i = 0; i < detail::MAX_PULSES; i++) {
-            RMTMEM.chan[ _channel].data32[i + offset ].val = 0;
-        }
-    }
-
-    int i;
-    for ( i = 0; i != len && _pixelPosition != _count; i++ ) {
-        uint8_t val = _buffer[ _pixelPosition ].getGrb( _componentPosition );
-        for ( int j = 0; j != 8; j++, val <<= 1 ) {
-            int bit = val >> 7;
-            int idx = i * 8 + offset + j;
-            RMTMEM.chan[ _channel ].data32[ idx ].val = _bitToRmt[ bit & 0x01 ].value;
-        }
-        if ( _pixelPosition == _count - 1 && _componentPosition == 2 ) {
-            RMTMEM.chan[ _channel ].data32[ i * 8 + offset + 7 ].duration1 =
-                _timing.TRS / ( detail::RMT_DURATION_NS * detail::DIVIDER );
-        }
-
-        _componentPosition++;
-        if ( _componentPosition == 3 ) {
-            _componentPosition = 0;
-            _pixelPosition++;
-        }
-    }
 
-    for ( i *= 8; i != detail::MAX_PULSES; i++ ) {
-        RMTMEM.chan[ _channel ].data32[ i + offset ].val = 0;
-    }
+SmartLed*& IRAM_ATTR SmartLed::ledForChannel(int channel) {
+    static SmartLed* table[detail::CHANNEL_COUNT] = {};
+    assert(channel < detail::CHANNEL_COUNT);
+    return table[channel];
 }

+ 238 - 331
code/components/jomjol_controlGPIO/SmartLeds.h

@@ -1,7 +1,30 @@
-#pragma once
+/********************************************************************************
+ * https://github.com/RoboticsBrno/SmartLeds
+ *
+ * MIT License
+ * 
+ * Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
 
-#ifndef SMARTLEDS_H
-#define SMARTLEDS_H
+#pragma once
 
 /*
  * A C++ driver for the WS2812 LEDs using the RMT peripheral on the ESP32.
@@ -31,305 +54,196 @@
  * THE SOFTWARE.
  */
 
-#include <memory>
 #include <cassert>
 #include <cstring>
+#include <memory>
 
-#include "esp_idf_version.h"
-#if (ESP_IDF_VERSION_MAJOR >= 5)
-#include "soc/periph_defs.h"
-#include "esp_private/periph_ctrl.h"
-#include "soc/gpio_sig_map.h"
-#include "soc/gpio_periph.h"
-#include "soc/io_mux_reg.h"
-#include "esp_rom_gpio.h"
-#define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio
-#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
-#define gpio_matrix_out(a,b,c,d) esp_rom_gpio_connect_out_signal(a,b,c,d)
-#define ets_delay_us(a) esp_rom_delay_us(a)
-#endif
-
-#if defined ( ARDUINO )
-    extern "C" { // ...someone forgot to put in the includes...
-        #include "esp32-hal.h"
-        #include "esp_intr_alloc.h"
-        #include "esp_ipc.h"
-        #include "driver/gpio.h"
-        #include "driver/periph_ctrl.h"
-        #include "freertos/semphr.h"
-        #include "soc/rmt_struct.h"
-        #include <driver/spi_master.h>
-        #include "esp_idf_version.h"
-#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL( 4, 0, 0 )
-        #include "soc/dport_reg.h"
-#endif
-    }
-#elif defined ( ESP_PLATFORM )
-    extern "C" { // ...someone forgot to put in the includes...
-        #include <esp_intr_alloc.h>
-        #include <esp_ipc.h>
-        #include <driver/gpio.h>
-        #include <freertos/FreeRTOS.h>
-        #include <freertos/semphr.h>
-        #include <soc/dport_reg.h>
-        #include <soc/gpio_sig_map.h>
-        #include <soc/rmt_struct.h>
-        #include <driver/spi_master.h>
-    }
-    #include <stdio.h>
-#endif
-
-#if (ESP_IDF_VERSION_MAJOR >= 4) && (ESP_IDF_VERSION_MINOR > 1)
-#include "hal/gpio_ll.h"
-#else
-#include "soc/gpio_periph.h"
-#define esp_rom_delay_us ets_delay_us
-static inline int gpio_ll_get_level(gpio_dev_t *hw, int gpio_num)
-{
-    if (gpio_num < 32) {
-        return (hw->in >> gpio_num) & 0x1;
-    } else {
-        return (hw->in1.data >> (gpio_num - 32)) & 0x1;
-    }
-}
-#endif
-
-#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
-#if !(configENABLE_BACKWARD_COMPATIBILITY == 1)
-#define xSemaphoreHandle SemaphoreHandle_t
-#endif
-#endif
+#include <driver/gpio.h>
+#include <driver/spi_master.h>
+#include <esp_intr_alloc.h>
+#include <esp_ipc.h>
+#include <freertos/FreeRTOS.h>
+#include <freertos/semphr.h>
 
 #include "Color.h"
 
-namespace detail {
-
-struct TimingParams {
-    uint32_t T0H;
-    uint32_t T1H;
-    uint32_t T0L;
-    uint32_t T1L;
-    uint32_t TRS;
-};
-
-union RmtPulsePair {
-    struct {
-        int duration0:15;
-        int level0:1;
-        int duration1:15;
-        int level1:1;
-    };
-    uint32_t value;
-};
-
-static const int DIVIDER = 4; // 8 still seems to work, but timings become marginal
-static const int MAX_PULSES = 32; // A channel has a 64 "pulse" buffer - we use half per pass
-static const double RMT_DURATION_NS = 12.5; // minimum time of a single RMT duration based on clock ns
-
-} // namespace detail
+#include "RmtDriver.h"
 
 using LedType = detail::TimingParams;
 
-static const LedType LED_WS2812  = { 350, 700, 800, 600, 50000 };
-static const LedType LED_WS2812B = { 400, 850, 850, 400, 50100 };
-static const LedType LED_SK6812  = { 300, 600, 900, 600, 80000 };
-static const LedType LED_WS2813  = { 350, 800, 350, 350, 300000 };
-
+// Times are in nanoseconds,
+// The RMT driver runs at 20MHz, so minimal representable time is 50 nanoseconds
+static const LedType LED_WS2812 = { 350, 700, 800, 600, 50000 };
+// longer reset time because https://blog.adafruit.com/2017/05/03/psa-the-ws2812b-rgb-led-has-been-revised-will-require-code-tweak/
+static const LedType LED_WS2812B = { 400, 800, 850, 450, 300000 }; // universal
+static const LedType LED_WS2812B_NEWVARIANT = { 200, 750, 750, 200, 300000 };
+static const LedType LED_WS2812B_OLDVARIANT = { 400, 800, 850, 450, 50000 };
+// This is timing from datasheet, but does not seem to actually work - try LED_WS2812B
+static const LedType LED_WS2812C = { 250, 550, 550, 250, 280000 };
+static const LedType LED_SK6812 = { 300, 600, 900, 600, 80000 };
+static const LedType LED_WS2813 = { 350, 800, 350, 350, 300000 };
+
+// Single buffer == can't touch the Rgbs between show() and wait()
 enum BufferType { SingleBuffer = 0, DoubleBuffer };
 
-enum IsrCore { CoreFirst = 0, CoreSecond = 1, CoreCurrent = 2};
+enum IsrCore { CoreFirst = 0, CoreSecond = 1, CoreCurrent = 2 };
 
 class SmartLed {
 public:
+    friend class detail::RmtDriver;
+
     // The RMT interrupt must not run on the same core as WiFi interrupts, otherwise SmartLeds
     // can't fill the RMT buffer fast enough, resulting in rendering artifacts.
     // Usually, that means you have to set isrCore == CoreSecond.
     //
     // If you use anything other than CoreCurrent, the FreeRTOS scheduler MUST be already running,
     // so you can't use it if you define SmartLed as global variable.
-    SmartLed( const LedType& type, int count, int pin, int channel = 0, BufferType doubleBuffer = SingleBuffer, IsrCore isrCore = CoreCurrent)
-        : _timing( type ),
-          _channel( channel ),
-          _count( count ),
-          _firstBuffer( new Rgb[ count ] ),
-          _secondBuffer( doubleBuffer ? new Rgb[ count ] : nullptr ),
-          _finishedFlag( xSemaphoreCreateBinary() )
-    {
-        assert( channel >= 0 && channel < 8 );
-        assert( ledForChannel( channel ) == nullptr );
-
-        xSemaphoreGive( _finishedFlag );
-
-        DPORT_SET_PERI_REG_MASK( DPORT_PERIP_CLK_EN_REG, DPORT_RMT_CLK_EN );
-        DPORT_CLEAR_PERI_REG_MASK( DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST );
-
-        PIN_FUNC_SELECT( GPIO_PIN_MUX_REG[ pin ], 2 );
-        gpio_set_direction( static_cast< gpio_num_t >( pin ), GPIO_MODE_OUTPUT );
-        gpio_matrix_out( static_cast< gpio_num_t >( pin ), RMT_SIG_OUT0_IDX + _channel, 0, 0 );
-        initChannel( _channel );
-
-        RMT.tx_lim_ch[ _channel ].limit = detail::MAX_PULSES;
-        RMT.int_ena.val |= 1 << ( 24 + _channel );
-        RMT.int_ena.val |= 1 << ( 3 * _channel );
-
-        _bitToRmt[ 0 ].level0 = 1;
-        _bitToRmt[ 0 ].level1 = 0;
-        _bitToRmt[ 0 ].duration0 = _timing.T0H / ( detail::RMT_DURATION_NS * detail::DIVIDER );
-        _bitToRmt[ 0 ].duration1 = _timing.T0L / ( detail::RMT_DURATION_NS * detail::DIVIDER );
-
-        _bitToRmt[ 1 ].level0 = 1;
-        _bitToRmt[ 1 ].level1 = 0;
-        _bitToRmt[ 1 ].duration0 = _timing.T1H / ( detail::RMT_DURATION_NS * detail::DIVIDER );
-        _bitToRmt[ 1 ].duration1 = _timing.T1L / ( detail::RMT_DURATION_NS * detail::DIVIDER );
-
-        if ( !anyAlive() ) {
+    //
+    // Does nothing on chips that only have one core.
+    SmartLed(const LedType& type, int count, int pin, int channel = 0, BufferType doubleBuffer = DoubleBuffer,
+        IsrCore isrCore = CoreCurrent)
+        : _finishedFlag(xSemaphoreCreateBinary())
+        , _driver(type, count, pin, channel, _finishedFlag)
+        , _channel(channel)
+        , _count(count)
+        , _firstBuffer(new Rgb[count])
+        , _secondBuffer(doubleBuffer ? new Rgb[count] : nullptr) {
+        assert(channel >= 0 && channel < detail::CHANNEL_COUNT);
+        assert(ledForChannel(channel) == nullptr);
+
+        xSemaphoreGive(_finishedFlag);
+
+        _driver.init();
+
+#if !defined(SOC_CPU_CORES_NUM) || SOC_CPU_CORES_NUM > 1
+        if (!anyAlive() && isrCore != CoreCurrent) {
             _interruptCore = isrCore;
-            if(isrCore != CoreCurrent) {
-                ESP_ERROR_CHECK(esp_ipc_call_blocking(isrCore, registerInterrupt, NULL));
-            } else {
-                registerInterrupt(NULL);
-            }
+            ESP_ERROR_CHECK(esp_ipc_call_blocking(isrCore, registerInterrupt, (void*)this));
+        } else
+#endif
+        {
+            registerInterrupt((void*)this);
         }
 
-        ledForChannel( channel ) = this;
+        ledForChannel(channel) = this;
     }
 
     ~SmartLed() {
-        ledForChannel( _channel ) = nullptr;
-        if ( !anyAlive() ) {
-            if(_interruptCore != CoreCurrent) {
-                ESP_ERROR_CHECK(esp_ipc_call_blocking(_interruptCore, unregisterInterrupt, NULL));
-            } else {
-                unregisterInterrupt(NULL);
-            }
+        ledForChannel(_channel) = nullptr;
+#if !defined(SOC_CPU_CORES_NUM) || SOC_CPU_CORES_NUM > 1
+        if (!anyAlive() && _interruptCore != CoreCurrent) {
+            ESP_ERROR_CHECK(esp_ipc_call_blocking(_interruptCore, unregisterInterrupt, (void*)this));
+        } else
+#endif
+        {
+            unregisterInterrupt((void*)this);
         }
-        vSemaphoreDelete( _finishedFlag );
+        vSemaphoreDelete(_finishedFlag);
     }
 
-    Rgb& operator[]( int idx ) {
-        return _firstBuffer[ idx ];
-    }
+    Rgb& operator[](int idx) { return _firstBuffer[idx]; }
 
-    const Rgb& operator[]( int idx ) const {
-        return _firstBuffer[ idx ];
-    }
+    const Rgb& operator[](int idx) const { return _firstBuffer[idx]; }
 
-    void show() {
-        _buffer = _firstBuffer.get();
-        startTransmission();
+    esp_err_t show() {
+        esp_err_t err = startTransmission();
         swapBuffers();
+        return err;
     }
 
-    bool wait( TickType_t timeout = portMAX_DELAY ) {
-        if( xSemaphoreTake( _finishedFlag, timeout ) == pdTRUE ) {
-            xSemaphoreGive( _finishedFlag );
+    bool wait(TickType_t timeout = portMAX_DELAY) {
+        if (xSemaphoreTake(_finishedFlag, timeout) == pdTRUE) {
+            xSemaphoreGive(_finishedFlag);
             return true;
         }
         return false;
     }
 
-    int size() const {
-        return _count;
-    }
+    int size() const { return _count; }
+    int channel() const { return _channel; }
 
-    Rgb *begin() { return _firstBuffer.get(); }
-    const Rgb *begin() const { return _firstBuffer.get(); }
-    const Rgb *cbegin() const { return _firstBuffer.get(); }
+    Rgb* begin() { return _firstBuffer.get(); }
+    const Rgb* begin() const { return _firstBuffer.get(); }
+    const Rgb* cbegin() const { return _firstBuffer.get(); }
 
-    Rgb *end() { return _firstBuffer.get() + _count; }
-    const Rgb *end() const { return _firstBuffer.get() + _count; }
-    const Rgb *cend() const { return _firstBuffer.get() + _count; }
+    Rgb* end() { return _firstBuffer.get() + _count; }
+    const Rgb* end() const { return _firstBuffer.get() + _count; }
+    const Rgb* cend() const { return _firstBuffer.get() + _count; }
 
 private:
-    static intr_handle_t _interruptHandle;
     static IsrCore _interruptCore;
 
-    static void initChannel( int channel ) {
-        RMT.apb_conf.fifo_mask = 1;  //enable memory access, instead of FIFO mode.
-        RMT.apb_conf.mem_tx_wrap_en = 1; //wrap around when hitting end of buffer
-        RMT.conf_ch[ channel ].conf0.div_cnt = detail::DIVIDER;
-        RMT.conf_ch[ channel ].conf0.mem_size = 1;
-        RMT.conf_ch[ channel ].conf0.carrier_en = 0;
-        RMT.conf_ch[ channel ].conf0.carrier_out_lv = 1;
-        RMT.conf_ch[ channel ].conf0.mem_pd = 0;
-
-        RMT.conf_ch[ channel ].conf1.rx_en = 0;
-        RMT.conf_ch[ channel ].conf1.mem_owner = 0;
-        RMT.conf_ch[ channel ].conf1.tx_conti_mode = 0;    //loop back mode.
-        RMT.conf_ch[ channel ].conf1.ref_always_on = 1;    // use apb clock: 80M
-        RMT.conf_ch[ channel ].conf1.idle_out_en = 1;
-        RMT.conf_ch[ channel ].conf1.idle_out_lv = 0;
-    }
-
-    static void registerInterrupt(void *) {
-        ESP_ERROR_CHECK(esp_intr_alloc( ETS_RMT_INTR_SOURCE, 0, interruptHandler, nullptr, &_interruptHandle));
+    static void registerInterrupt(void* selfVoid) {
+        auto* self = (SmartLed*)selfVoid;
+        ESP_ERROR_CHECK(self->_driver.registerIsr(!anyAlive()));
     }
 
-    static void unregisterInterrupt(void*) {
-        esp_intr_free( _interruptHandle );
+    static void unregisterInterrupt(void* selfVoid) {
+        auto* self = (SmartLed*)selfVoid;
+        ESP_ERROR_CHECK(self->_driver.unregisterIsr());
     }
 
-    static SmartLed*& IRAM_ATTR ledForChannel( int channel );
-    static void IRAM_ATTR interruptHandler( void* );
+    static SmartLed*& IRAM_ATTR ledForChannel(int channel);
 
-    void IRAM_ATTR copyRmtHalfBlock();
+    static bool anyAlive() {
+        for (int i = 0; i != detail::CHANNEL_COUNT; i++)
+            if (ledForChannel(i) != nullptr)
+                return true;
+        return false;
+    }
 
     void swapBuffers() {
-        if ( _secondBuffer )
-            _firstBuffer.swap( _secondBuffer );
+        if (_secondBuffer)
+            _firstBuffer.swap(_secondBuffer);
     }
 
-    void startTransmission() {
-        // Invalid use of the library
-        if( xSemaphoreTake( _finishedFlag, 0 ) != pdTRUE )
+    esp_err_t startTransmission() {
+        // Invalid use of the library, you must wait() fir previous frame to get processed first
+        if (xSemaphoreTake(_finishedFlag, 0) != pdTRUE)
             abort();
 
-        _pixelPosition = _componentPosition = _halfIdx = 0;
-        copyRmtHalfBlock();
-        if ( _pixelPosition < _count )
-            copyRmtHalfBlock();
-
-        RMT.conf_ch[ _channel ].conf1.mem_rd_rst = 1;
-        RMT.conf_ch[ _channel ].conf1.tx_start = 1;
-    }
+        auto err = _driver.transmit(_firstBuffer.get());
+        if (err != ESP_OK) {
+            return err;
+        }
 
-    static bool anyAlive() {
-        for ( int i = 0; i != 8; i++ )
-            if ( ledForChannel( i ) != nullptr ) return true;
-        return false;
+        return ESP_OK;
     }
 
-    const LedType& _timing;
+    SemaphoreHandle_t _finishedFlag;
+    detail::RmtDriver _driver;
     int _channel;
-    detail::RmtPulsePair _bitToRmt[ 2 ];
     int _count;
-    std::unique_ptr< Rgb[] > _firstBuffer;
-    std::unique_ptr< Rgb[] > _secondBuffer;
-    Rgb *_buffer;
-
-    xSemaphoreHandle _finishedFlag;
-
-    int _pixelPosition;
-    int _componentPosition;
-    int _halfIdx;
+    std::unique_ptr<Rgb[]> _firstBuffer;
+    std::unique_ptr<Rgb[]> _secondBuffer;
 };
 
+#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3)
+#define _SMARTLEDS_SPI_HOST SPI2_HOST
+#define _SMARTLEDS_SPI_DMA_CHAN SPI_DMA_CH_AUTO
+#else
+#define _SMARTLEDS_SPI_HOST HSPI_HOST
+#define _SMARTLEDS_SPI_DMA_CHAN 1
+#endif
+
 class Apa102 {
 public:
     struct ApaRgb {
-        ApaRgb( uint8_t r = 0, uint8_t g = 0, uint32_t b = 0, uint32_t v = 0xFF )
-            : v( 0xE0 | v ), b( b ), g( g ), r( r )
-        {}
+        ApaRgb(uint8_t r = 0, uint8_t g = 0, uint32_t b = 0, uint32_t v = 0xFF)
+            : v(0xE0 | v)
+            , b(b)
+            , g(g)
+            , r(r) {}
 
-        ApaRgb& operator=( const Rgb& o ) {
+        ApaRgb& operator=(const Rgb& o) {
             r = o.r;
             g = o.g;
             b = o.b;
             return *this;
         }
 
-        ApaRgb& operator=( const Hsv& o ) {
-            *this = Rgb{ o };
+        ApaRgb& operator=(const Hsv& o) {
+            *this = Rgb { o };
             return *this;
         }
 
@@ -339,14 +253,14 @@ public:
     static const int FINAL_FRAME_SIZE = 4;
     static const int TRANS_COUNT = 2 + 8;
 
-    Apa102( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer )
-        : _count( count ),
-          _firstBuffer( new ApaRgb[ count ] ),
-          _secondBuffer( doubleBuffer ? new ApaRgb[ count ] : nullptr ),
-          _initFrame( 0 )
-    {
+    Apa102(int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, int clock_speed_hz = 1000000)
+        : _count(count)
+        , _firstBuffer(new ApaRgb[count])
+        , _secondBuffer(doubleBuffer ? new ApaRgb[count] : nullptr)
+        , _transCount(0)
+        , _initFrame(0) {
         spi_bus_config_t buscfg;
-        memset( &buscfg, 0, sizeof( buscfg ) );
+        memset(&buscfg, 0, sizeof(buscfg));
         buscfg.mosi_io_num = datapin;
         buscfg.miso_io_num = -1;
         buscfg.sclk_io_num = clkpin;
@@ -355,33 +269,29 @@ public:
         buscfg.max_transfer_sz = 65535;
 
         spi_device_interface_config_t devcfg;
-        memset( &devcfg, 0, sizeof( devcfg ) );
-        devcfg.clock_speed_hz = 1000000;
+        memset(&devcfg, 0, sizeof(devcfg));
+        devcfg.clock_speed_hz = clock_speed_hz;
         devcfg.mode = 0;
         devcfg.spics_io_num = -1;
         devcfg.queue_size = TRANS_COUNT;
         devcfg.pre_cb = nullptr;
 
-        auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 );
-        assert( ret == ESP_OK );
+        auto ret = spi_bus_initialize(_SMARTLEDS_SPI_HOST, &buscfg, _SMARTLEDS_SPI_DMA_CHAN);
+        assert(ret == ESP_OK);
 
-        ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi );
-        assert( ret == ESP_OK );
+        ret = spi_bus_add_device(_SMARTLEDS_SPI_HOST, &devcfg, &_spi);
+        assert(ret == ESP_OK);
 
-        std::fill_n( _finalFrame, FINAL_FRAME_SIZE, 0xFFFFFFFF );
+        std::fill_n(_finalFrame, FINAL_FRAME_SIZE, 0xFFFFFFFF);
     }
 
     ~Apa102() {
         // ToDo
     }
 
-    ApaRgb& operator[]( int idx ) {
-        return _firstBuffer[ idx ];
-    }
+    ApaRgb& operator[](int idx) { return _firstBuffer[idx]; }
 
-    const ApaRgb& operator[]( int idx ) const {
-        return _firstBuffer[ idx ];
-    }
+    const ApaRgb& operator[](int idx) const { return _firstBuffer[idx]; }
 
     void show() {
         _buffer = _firstBuffer.get();
@@ -390,93 +300,95 @@ public:
     }
 
     void wait() {
-        for ( int i = 0; i != _transCount; i++ ) {
-            spi_transaction_t *t;
-            spi_device_get_trans_result( _spi, &t, portMAX_DELAY );
+        for (int i = 0; i != _transCount; i++) {
+            spi_transaction_t* t;
+            spi_device_get_trans_result(_spi, &t, portMAX_DELAY);
         }
     }
+
 private:
     void swapBuffers() {
-        if ( _secondBuffer )
-            _firstBuffer.swap( _secondBuffer );
+        if (_secondBuffer)
+            _firstBuffer.swap(_secondBuffer);
     }
 
     void startTransmission() {
-        for ( int i = 0; i != TRANS_COUNT; i++ ) {
-            _transactions[ i ].cmd = 0;
-            _transactions[ i ].addr = 0;
-            _transactions[ i ].flags = 0;
-            _transactions[ i ].rxlength = 0;
-            _transactions[ i ].rx_buffer = nullptr;
+        for (int i = 0; i != TRANS_COUNT; i++) {
+            _transactions[i].cmd = 0;
+            _transactions[i].addr = 0;
+            _transactions[i].flags = 0;
+            _transactions[i].rxlength = 0;
+            _transactions[i].rx_buffer = nullptr;
         }
         // Init frame
-        _transactions[ 0 ].length = 32;
-        _transactions[ 0 ].tx_buffer = &_initFrame;
-        spi_device_queue_trans( _spi, _transactions + 0, portMAX_DELAY );
+        _transactions[0].length = 32;
+        _transactions[0].tx_buffer = &_initFrame;
+        spi_device_queue_trans(_spi, _transactions + 0, portMAX_DELAY);
         // Data
-        _transactions[ 1 ].length = 32 * _count;
-        _transactions[ 1 ].tx_buffer = _buffer;
-        spi_device_queue_trans( _spi, _transactions + 1, portMAX_DELAY );
+        _transactions[1].length = 32 * _count;
+        _transactions[1].tx_buffer = _buffer;
+        spi_device_queue_trans(_spi, _transactions + 1, portMAX_DELAY);
         _transCount = 2;
         // End frame
-        for ( int i = 0; i != 1 + _count / 32 / FINAL_FRAME_SIZE; i++ ) {
-            _transactions[ 2 + i ].length = 32 * FINAL_FRAME_SIZE;
-            _transactions[ 2 + i ].tx_buffer = _finalFrame;
-            spi_device_queue_trans( _spi, _transactions + 2 + i, portMAX_DELAY );
+        for (int i = 0; i != 1 + _count / 32 / FINAL_FRAME_SIZE; i++) {
+            _transactions[2 + i].length = 32 * FINAL_FRAME_SIZE;
+            _transactions[2 + i].tx_buffer = _finalFrame;
+            spi_device_queue_trans(_spi, _transactions + 2 + i, portMAX_DELAY);
             _transCount++;
         }
     }
 
     spi_device_handle_t _spi;
     int _count;
-    std::unique_ptr< ApaRgb[] > _firstBuffer, _secondBuffer;
-    ApaRgb *_buffer;
+    std::unique_ptr<ApaRgb[]> _firstBuffer, _secondBuffer;
+    ApaRgb* _buffer;
 
-    spi_transaction_t _transactions[ TRANS_COUNT ];
+    spi_transaction_t _transactions[TRANS_COUNT];
     int _transCount;
 
     uint32_t _initFrame;
-    uint32_t _finalFrame[ FINAL_FRAME_SIZE ];
+    uint32_t _finalFrame[FINAL_FRAME_SIZE];
 };
 
 class LDP8806 {
 public:
     struct LDP8806_GRB {
 
-        LDP8806_GRB( uint8_t g_7bit = 0, uint8_t r_7bit = 0, uint32_t b_7bit = 0 )
-            : g( g_7bit ), r( r_7bit ), b( b_7bit )
-        {
-        }
+        LDP8806_GRB(uint8_t g_7bit = 0, uint8_t r_7bit = 0, uint32_t b_7bit = 0)
+            : g(g_7bit)
+            , r(r_7bit)
+            , b(b_7bit) {}
 
-        LDP8806_GRB& operator=( const Rgb& o ) {
+        LDP8806_GRB& operator=(const Rgb& o) {
             //Convert 8->7bit colour
-            r = ( o.r * 127 / 256 ) | 0x80;
-            g = ( o.g * 127 / 256 ) | 0x80;
-            b = ( o.b * 127 / 256 ) | 0x80;
+            r = (o.r * 127 / 256) | 0x80;
+            g = (o.g * 127 / 256) | 0x80;
+            b = (o.b * 127 / 256) | 0x80;
             return *this;
         }
 
-        LDP8806_GRB& operator=( const Hsv& o ) {
-            *this = Rgb{ o };
+        LDP8806_GRB& operator=(const Hsv& o) {
+            *this = Rgb { o };
             return *this;
         }
 
         uint8_t g, r, b;
     };
 
-    static const int LED_FRAME_SIZE_BYTES = sizeof( LDP8806_GRB );
+    static const int LED_FRAME_SIZE_BYTES = sizeof(LDP8806_GRB);
     static const int LATCH_FRAME_SIZE_BYTES = 3;
-    static const int TRANS_COUNT_MAX = 20;//Arbitrary, supports up to 600 LED
-
-    LDP8806( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, uint32_t clock_speed_hz = 2000000 )
-        : _count( count ),
-          _firstBuffer( new LDP8806_GRB[ count ] ),
-          _secondBuffer( doubleBuffer ? new LDP8806_GRB[ count ] : nullptr ),
-          // one 'latch'/start-of-data mark frame for every 32 leds
-          _latchFrames( ( count + 31 ) / 32 )
-    {
+    static const int TRANS_COUNT_MAX = 20; //Arbitrary, supports up to 600 LED
+
+    LDP8806(
+        int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, uint32_t clock_speed_hz = 2000000)
+        : _count(count)
+        , _firstBuffer(new LDP8806_GRB[count])
+        , _secondBuffer(doubleBuffer ? new LDP8806_GRB[count] : nullptr)
+        ,
+        // one 'latch'/start-of-data mark frame for every 32 leds
+        _latchFrames((count + 31) / 32) {
         spi_bus_config_t buscfg;
-        memset( &buscfg, 0, sizeof( buscfg ) );
+        memset(&buscfg, 0, sizeof(buscfg));
         buscfg.mosi_io_num = datapin;
         buscfg.miso_io_num = -1;
         buscfg.sclk_io_num = clkpin;
@@ -485,33 +397,29 @@ public:
         buscfg.max_transfer_sz = 65535;
 
         spi_device_interface_config_t devcfg;
-        memset( &devcfg, 0, sizeof( devcfg ) );
+        memset(&devcfg, 0, sizeof(devcfg));
         devcfg.clock_speed_hz = clock_speed_hz;
         devcfg.mode = 0;
         devcfg.spics_io_num = -1;
         devcfg.queue_size = TRANS_COUNT_MAX;
         devcfg.pre_cb = nullptr;
 
-        auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 );
-        assert( ret == ESP_OK );
+        auto ret = spi_bus_initialize(_SMARTLEDS_SPI_HOST, &buscfg, _SMARTLEDS_SPI_DMA_CHAN);
+        assert(ret == ESP_OK);
 
-        ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi );
-        assert( ret == ESP_OK );
+        ret = spi_bus_add_device(_SMARTLEDS_SPI_HOST, &devcfg, &_spi);
+        assert(ret == ESP_OK);
 
-        std::fill_n( _latchBuffer, LATCH_FRAME_SIZE_BYTES, 0x0 );
+        std::fill_n(_latchBuffer, LATCH_FRAME_SIZE_BYTES, 0x0);
     }
 
     ~LDP8806() {
         // noop
     }
 
-    LDP8806_GRB& operator[]( int idx ) {
-        return _firstBuffer[ idx ];
-    }
+    LDP8806_GRB& operator[](int idx) { return _firstBuffer[idx]; }
 
-    const LDP8806_GRB& operator[]( int idx ) const {
-        return _firstBuffer[ idx ];
-    }
+    const LDP8806_GRB& operator[](int idx) const { return _firstBuffer[idx]; }
 
     void show() {
         _buffer = _firstBuffer.get();
@@ -520,51 +428,50 @@ public:
     }
 
     void wait() {
-        while ( _transCount-- ) {
-            spi_transaction_t *t;
-            spi_device_get_trans_result( _spi, &t, portMAX_DELAY );
+        while (_transCount--) {
+            spi_transaction_t* t;
+            spi_device_get_trans_result(_spi, &t, portMAX_DELAY);
         }
     }
+
 private:
     void swapBuffers() {
-        if ( _secondBuffer )
-            _firstBuffer.swap( _secondBuffer );
+        if (_secondBuffer)
+            _firstBuffer.swap(_secondBuffer);
     }
 
     void startTransmission() {
         _transCount = 0;
-        for ( int i = 0; i != TRANS_COUNT_MAX; i++ ) {
-            _transactions[ i ].cmd = 0;
-            _transactions[ i ].addr = 0;
-            _transactions[ i ].flags = 0;
-            _transactions[ i ].rxlength = 0;
-            _transactions[ i ].rx_buffer = nullptr;
+        for (int i = 0; i != TRANS_COUNT_MAX; i++) {
+            _transactions[i].cmd = 0;
+            _transactions[i].addr = 0;
+            _transactions[i].flags = 0;
+            _transactions[i].rxlength = 0;
+            _transactions[i].rx_buffer = nullptr;
         }
         // LED Data
-        _transactions[ 0 ].length = ( LED_FRAME_SIZE_BYTES * 8 ) * _count;
-        _transactions[ 0 ].tx_buffer = _buffer;
-        spi_device_queue_trans( _spi, _transactions + _transCount, portMAX_DELAY );
+        _transactions[0].length = (LED_FRAME_SIZE_BYTES * 8) * _count;
+        _transactions[0].tx_buffer = _buffer;
+        spi_device_queue_trans(_spi, _transactions + _transCount, portMAX_DELAY);
         _transCount++;
 
         // 'latch'/start-of-data marker frames
-        for ( int i = 0; i < _latchFrames; i++ ) {
-            _transactions[ _transCount ].length = ( LATCH_FRAME_SIZE_BYTES * 8 );
-            _transactions[ _transCount ].tx_buffer = _latchBuffer;
-            spi_device_queue_trans( _spi, _transactions + _transCount, portMAX_DELAY );
+        for (int i = 0; i < _latchFrames; i++) {
+            _transactions[_transCount].length = (LATCH_FRAME_SIZE_BYTES * 8);
+            _transactions[_transCount].tx_buffer = _latchBuffer;
+            spi_device_queue_trans(_spi, _transactions + _transCount, portMAX_DELAY);
             _transCount++;
         }
     }
 
     spi_device_handle_t _spi;
     int _count;
-    std::unique_ptr< LDP8806_GRB[] > _firstBuffer, _secondBuffer;
-    LDP8806_GRB *_buffer;
+    std::unique_ptr<LDP8806_GRB[]> _firstBuffer, _secondBuffer;
+    LDP8806_GRB* _buffer;
 
-    spi_transaction_t _transactions[ TRANS_COUNT_MAX ];
+    spi_transaction_t _transactions[TRANS_COUNT_MAX];
     int _transCount;
 
     int _latchFrames;
-    uint8_t _latchBuffer[ LATCH_FRAME_SIZE_BYTES ];
+    uint8_t _latchBuffer[LATCH_FRAME_SIZE_BYTES];
 };
-
-#endif //SMARTLEDS_H

+ 2 - 1
code/components/jomjol_controlGPIO/server_GPIO.cpp

@@ -25,6 +25,7 @@
 #include "server_mqtt.h"
 #endif //ENABLE_MQTT
 
+#include "basic_auth.h"
 
 static const char *TAG = "GPIO";
 QueueHandle_t gpio_queue_handle = NULL;
@@ -458,7 +459,7 @@ void GpioHandler::registerGpioUri()
     httpd_uri_t camuri = { };
     camuri.method    = HTTP_GET;
     camuri.uri       = "/GPIO";
-    camuri.handler   = callHandleHttpRequest;
+    camuri.handler   = APPLY_BASIC_AUTH_FILTER(callHandleHttpRequest);
     camuri.user_ctx  = (void*)this;    
     httpd_register_uri_handler(_httpServer, &camuri);
 }

+ 1 - 1
code/components/jomjol_controlcamera/ClassControllCamera.cpp

@@ -98,7 +98,7 @@ static camera_config_t camera_config = {
     .pixel_format = PIXFORMAT_JPEG, // YUV422,GRAYSCALE,RGB565,JPEG
     .frame_size = FRAMESIZE_VGA,    // QQVGA-UXGA Do not use sizes above QVGA when not JPEG
     // .frame_size = FRAMESIZE_UXGA,    //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
-    .jpeg_quality = 6,                 // 0-63 lower number means higher quality
+    .jpeg_quality = 12,                 // 0-63 lower number means higher quality
     .fb_count = 1,                     // if more than one, i2s runs in continuous mode. Use only with JPEG
     .fb_location = CAMERA_FB_IN_PSRAM, /*!< The location where the frame buffer will be allocated */
     .grab_mode = CAMERA_GRAB_LATEST,   // only from new esp32cam version

+ 7 - 5
code/components/jomjol_controlcamera/server_camera.cpp

@@ -10,6 +10,8 @@
 #include "ClassLogFile.h"
 #include "esp_log.h"
 
+#include "basic_auth.h"
+
 #include "../../include/defines.h"
 
 static const char *TAG = "server_cam";
@@ -280,27 +282,27 @@ void register_server_camera_uri(httpd_handle_t server)
     camuri.method = HTTP_GET;
 
     camuri.uri = "/lighton";
-    camuri.handler = handler_lightOn;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_lightOn);
     camuri.user_ctx = (void *)"Light On";
     httpd_register_uri_handler(server, &camuri);
 
     camuri.uri = "/lightoff";
-    camuri.handler = handler_lightOff;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_lightOff);
     camuri.user_ctx = (void *)"Light Off";
     httpd_register_uri_handler(server, &camuri);
 
     camuri.uri = "/capture";
-    camuri.handler = handler_capture;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_capture);
     camuri.user_ctx = NULL;
     httpd_register_uri_handler(server, &camuri);
 
     camuri.uri = "/capture_with_flashlight";
-    camuri.handler = handler_capture_with_light;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_capture_with_light);
     camuri.user_ctx = NULL;
     httpd_register_uri_handler(server, &camuri);
 
     camuri.uri = "/save";
-    camuri.handler = handler_capture_save_to_file;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_capture_save_to_file);
     camuri.user_ctx = NULL;
     httpd_register_uri_handler(server, &camuri);
 }

+ 8 - 7
code/components/jomjol_fileserver_ota/server_file.cpp

@@ -46,6 +46,7 @@ extern "C" {
 
 #include "Helper.h"
 #include "miniz.h"
+#include "basic_auth.h"
 
 static const char *TAG = "OTA FILE";
 
@@ -1174,7 +1175,7 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path)
     httpd_uri_t file_download = {
         .uri       = "/fileserver*",  // Match all URIs of type /path/to/file
         .method    = HTTP_GET,
-        .handler   = download_get_handler,
+        .handler = APPLY_BASIC_AUTH_FILTER(download_get_handler),
         .user_ctx  = server_data    // Pass server data as context
     };
     httpd_register_uri_handler(server, &file_download);
@@ -1183,7 +1184,7 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path)
     httpd_uri_t file_datafileact = {
         .uri       = "/datafileact",  // Match all URIs of type /path/to/file
         .method    = HTTP_GET,
-        .handler   = datafileact_get_full_handler,
+        .handler = APPLY_BASIC_AUTH_FILTER(datafileact_get_full_handler),
         .user_ctx  = server_data    // Pass server data as context
     };
     httpd_register_uri_handler(server, &file_datafileact);
@@ -1192,7 +1193,7 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path)
     httpd_uri_t file_datafile_last_part_handle = {
         .uri       = "/data",  // Match all URIs of type /path/to/file
         .method    = HTTP_GET,
-        .handler   = datafileact_get_last_part_handler,
+        .handler = APPLY_BASIC_AUTH_FILTER(datafileact_get_last_part_handler),
         .user_ctx  = server_data    // Pass server data as context
     };
     httpd_register_uri_handler(server, &file_datafile_last_part_handle);
@@ -1200,7 +1201,7 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path)
     httpd_uri_t file_logfileact = {
         .uri       = "/logfileact",  // Match all URIs of type /path/to/file
         .method    = HTTP_GET,
-        .handler   = logfileact_get_full_handler,
+        .handler = APPLY_BASIC_AUTH_FILTER(logfileact_get_full_handler),
         .user_ctx  = server_data    // Pass server data as context
     };
     httpd_register_uri_handler(server, &file_logfileact);
@@ -1209,7 +1210,7 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path)
     httpd_uri_t file_logfile_last_part_handle = {
         .uri       = "/log",  // Match all URIs of type /path/to/file
         .method    = HTTP_GET,
-        .handler   = logfileact_get_last_part_handler,
+        .handler = APPLY_BASIC_AUTH_FILTER(logfileact_get_last_part_handler),
         .user_ctx  = server_data    // Pass server data as context
     };
     httpd_register_uri_handler(server, &file_logfile_last_part_handle);
@@ -1219,7 +1220,7 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path)
     httpd_uri_t file_upload = {
         .uri       = "/upload/*",   // Match all URIs of type /upload/path/to/file
         .method    = HTTP_POST,
-        .handler   = upload_post_handler,
+        .handler = APPLY_BASIC_AUTH_FILTER(upload_post_handler),
         .user_ctx  = server_data    // Pass server data as context
     };
     httpd_register_uri_handler(server, &file_upload);
@@ -1228,7 +1229,7 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path)
     httpd_uri_t file_delete = {
         .uri       = "/delete/*",   // Match all URIs of type /delete/path/to/file
         .method    = HTTP_POST,
-        .handler   = delete_post_handler,
+        .handler = APPLY_BASIC_AUTH_FILTER(delete_post_handler),
         .user_ctx  = server_data    // Pass server data as context
     };
     httpd_register_uri_handler(server, &file_delete);

+ 3 - 2
code/components/jomjol_fileserver_ota/server_ota.cpp

@@ -42,6 +42,7 @@ https://docs.espressif.com/projects/esp-idf/en/latest/esp32/migration-guides/rel
 
 #include "Helper.h"
 #include "statusled.h"
+#include "basic_auth.h"
 #include "../../include/defines.h"
 
 /*an ota data write buffer ready to write to the flash*/
@@ -690,13 +691,13 @@ void register_server_ota_sdcard_uri(httpd_handle_t server)
     httpd_uri_t camuri = { };
     camuri.method    = HTTP_GET;
     camuri.uri       = "/ota";
-    camuri.handler   = handler_ota_update;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_ota_update);
     camuri.user_ctx  = (void*) "Do OTA";    
     httpd_register_uri_handler(server, &camuri);
 
     camuri.method    = HTTP_GET;
     camuri.uri       = "/reboot";
-    camuri.handler   = handler_reboot;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_reboot);
     camuri.user_ctx  = (void*) "Reboot";    
     httpd_register_uri_handler(server, &camuri);
 

+ 17 - 8
code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp

@@ -65,11 +65,11 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
 
     if (CNNType == Digit) {
         for (int i = 0; i < GENERAL[_analog]->ROI.size(); ++i) {
-            if (GENERAL[_analog]->ROI[i]->result_klasse >= 10) {
-                result = result + "N";
+            if ((GENERAL[_analog]->ROI[i]->result_klasse >= 0) && (GENERAL[_analog]->ROI[i]->result_klasse < 10)) {
+                result = result + std::to_string(GENERAL[_analog]->ROI[i]->result_klasse);
             }
             else {
-                result = result + std::to_string(GENERAL[_analog]->ROI[i]->result_klasse);
+                result = result + "N";
             }
         }
         return result;
@@ -78,7 +78,7 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
     if ((CNNType == DoubleHyprid10) || (CNNType == Digit100)) {
         float number = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float;
         // NaN?
-        if (number >= 0) {
+        if ((number >= 0) && (number < 10)) {
             // is only set if it is the first digit (no analogue before!)
             if (_extendedResolution) {
                 int result_after_decimal_point = ((int) floor(number * 10)) % 10;
@@ -95,8 +95,15 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
                 else {
                     prev = PointerEvalHybridNew(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, prev, prev);
                 }
-                result = std::to_string(prev);
-                LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(dig100)  prev=" + std::to_string(prev));
+
+                // is necessary because a number greater than 9.994999 returns a 10! (for further details see check in PointerEvalHybridNew)
+                if ((prev >= 0) && (prev < 10)) {
+                    result = std::to_string(prev);
+                    LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(dig100)  prev=" + std::to_string(prev));
+                }
+                else {
+                    result = "N";
+                }
             }
         }
         else {
@@ -107,7 +114,7 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
         }
 
         for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i) {
-            if (GENERAL[_analog]->ROI[i]->result_float >= 0) {
+            if ((GENERAL[_analog]->ROI[i]->result_float >= 0) && (GENERAL[_analog]->ROI[i]->result_float < 10)) {
                 prev = PointerEvalHybridNew(GENERAL[_analog]->ROI[i]->result_float, GENERAL[_analog]->ROI[i+1]->result_float, prev);
                 LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout#PointerEvalHybridNew()= " + std::to_string(prev));
                 result = std::to_string(prev) + result;
@@ -117,7 +124,6 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
                 prev = -1;
                 result = "N" + result;
                 LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(result_float<0 /'N')  result_float=" + std::to_string(GENERAL[_analog]->ROI[i]->result_float));
-        
             }
         }
         return result;
@@ -150,6 +156,9 @@ int ClassFlowCNNGeneral::PointerEvalHybridNew(float number, float number_of_pred
         // on first digit is no spezial logic for transition needed
         // we use the recognition as given. The result is the int value of the recognition
         // add precisition of 2 digits and round before trunc
+        // a number greater than 9.994999 is returned as 10, this leads to an error during the decimal shift because the NUMBERS[j]->ReturnRawValue is one digit longer.
+        // To avoid this, an additional test must be carried out, see "if ((CNNType == DoubleHyprid10) || (CNNType == Digit100))" check in getReadout()
+        // Another alternative would be "result = (int) ((int) trunc(round((number+10 % 10)*1000))) / 1000;", which could, however, lead to other errors?
         result = (int) ((int) trunc(round((number+10 % 10)*100)) )  / 100;
 
         LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - No predecessor - Result = " + std::to_string(result) +

+ 1 - 0
code/components/jomjol_flowcontroll/ClassFlowControll.cpp

@@ -26,6 +26,7 @@ extern "C" {
 
 #include "server_help.h"
 #include "MainFlowControl.h"
+#include "basic_auth.h"
 #include "../../include/defines.h"
 
 static const char* TAG = "FLOWCTRL";

+ 12 - 2
code/components/jomjol_flowcontroll/ClassFlowInfluxDB.cpp

@@ -117,7 +117,12 @@ bool ClassFlowInfluxDB::ReadParameter(FILE* pfile, string& aktparamgraph)
     { 
 //        ESP_LOGD(TAG, "Init InfluxDB with uri: %s, measurement: %s, user: %s, password: %s", uri.c_str(), measurement.c_str(), user.c_str(), password.c_str());
         LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init InfluxDB with uri: " + uri + ", user: " + user + ", password: " + password);
-        InfluxDBInit(uri, database, user, password); 
+
+/////////////////////// NEW //////////////////////////
+//        InfluxDBInit(uri, database, user, password);
+        influxDB.InfluxDBInitV1(uri, database, user, password);
+/////////////////////// NEW //////////////////////////
+
         InfluxDBenable = true;
     } else {
         LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDB init skipped as we are missing some parameters");
@@ -169,7 +174,12 @@ bool ClassFlowInfluxDB::doFlow(string zwtime)
             }
 
             if (result.length() > 0)   
-                InfluxDBPublish(measurement, namenumber, result, timeutc);
+//////////////////////// NEW //////////////////////////            
+//                InfluxDBPublish(measurement, namenumber, result, timeutc);
+                influxDB.InfluxDBPublish(measurement, namenumber, result, timeutc);
+//////////////////////// NEW //////////////////////////
+
+
         }
     }
    

+ 3 - 0
code/components/jomjol_flowcontroll/ClassFlowInfluxDB.h

@@ -8,6 +8,7 @@
 #include "ClassFlow.h"
 
 #include "ClassFlowPostProcessing.h"
+#include "interface_influxdb.h"
 
 #include <string>
 
@@ -21,6 +22,8 @@ protected:
     std::string user, password; 
     bool InfluxDBenable;
 
+    InfluxDB influxDB;
+
     void SetInitialParameter(void);    
     
     void handleFieldname(string _decsep, string _value);   

+ 10 - 2
code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.cpp

@@ -123,7 +123,14 @@ bool ClassFlowInfluxDBv2::ReadParameter(FILE* pfile, string& aktparamgraph)
     { 
         LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init InfluxDB with uri: " + uri + ", org: " + dborg + ", token: *****");
 //        printf("vor V2 Init\n");
-        InfluxDB_V2_Init(uri, bucket, dborg, dbtoken); 
+
+
+////////////////////////////////////////// NEW ////////////////////////////////////////////
+//        InfluxDB_V2_Init(uri, bucket, dborg, dbtoken);
+//        InfluxDB_V2_Init(uri, bucket, dborg, dbtoken); 
+        influxdb.InfluxDBInitV2(uri, bucket, dborg, dbtoken);
+////////////////////////////////////////// NEW ////////////////////////////////////////////
+
 //        printf("nach V2 Init\n");
         InfluxDBenable = true;
     } else {
@@ -232,7 +239,8 @@ 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, resulttimeutc);
+                influxdb.InfluxDBPublish(measurement, namenumber, result, resulttimeutc);
+//                InfluxDB_V2_Publish(measurement, namenumber, result, resulttimeutc);
         }
     }
    

+ 4 - 0
code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.h

@@ -9,6 +9,8 @@
 
 #include "ClassFlowPostProcessing.h"
 
+#include "interface_influxdb.h"
+
 #include <string>
 
 class ClassFlowInfluxDBv2 :
@@ -21,6 +23,8 @@ protected:
 	ClassFlowPostProcessing* flowpostprocessing;  
     bool InfluxDBenable;
 
+    InfluxDB influxdb;
+
     void SetInitialParameter(void);     
 
     void handleFieldname(string _decsep, string _value);   

+ 9 - 9
code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp

@@ -133,25 +133,25 @@ bool ClassFlowTakeImage::ReadParameter(FILE *pfile, string &aktparamgraph)
                 switch (_ImageGainceiling_)
                 {
                 case 1:
-                    CFstatus.ImageGainceiling = GAINCEILING_4X;
+                    CCstatus.ImageGainceiling = GAINCEILING_4X;
                     break;
                 case 2:
-                    CFstatus.ImageGainceiling = GAINCEILING_8X;
+                    CCstatus.ImageGainceiling = GAINCEILING_8X;
                     break;
                 case 3:
-                    CFstatus.ImageGainceiling = GAINCEILING_16X;
+                    CCstatus.ImageGainceiling = GAINCEILING_16X;
                     break;
                 case 4:
-                    CFstatus.ImageGainceiling = GAINCEILING_32X;
+                    CCstatus.ImageGainceiling = GAINCEILING_32X;
                     break;
                 case 5:
-                    CFstatus.ImageGainceiling = GAINCEILING_64X;
+                    CCstatus.ImageGainceiling = GAINCEILING_64X;
                     break;
                 case 6:
-                    CFstatus.ImageGainceiling = GAINCEILING_128X;
+                    CCstatus.ImageGainceiling = GAINCEILING_128X;
                     break;
                 default:
-                    CFstatus.ImageGainceiling = GAINCEILING_2X;
+                    CCstatus.ImageGainceiling = GAINCEILING_2X;
                 }
             }
             else
@@ -251,7 +251,7 @@ bool ClassFlowTakeImage::ReadParameter(FILE *pfile, string &aktparamgraph)
             if (isStringNumeric(_ImageSpecialEffect))
             {
                 int _ImageSpecialEffect_ = std::stoi(_ImageSpecialEffect);
-                CFstatus.ImageSpecialEffect = clipInt(_ImageSpecialEffect_, 6, 0);
+                CCstatus.ImageSpecialEffect = clipInt(_ImageSpecialEffect_, 6, 0);
             }
             else
             {
@@ -293,7 +293,7 @@ bool ClassFlowTakeImage::ReadParameter(FILE *pfile, string &aktparamgraph)
             if (isStringNumeric(_ImageWbMode))
             {
                 int _ImageWbMode_ = std::stoi(_ImageWbMode);
-                CFstatus.ImageWbMode = clipInt(_ImageWbMode_, 4, 0);
+                CCstatus.ImageWbMode = clipInt(_ImageWbMode_, 4, 0);
             }
             else
             {

+ 22 - 21
code/components/jomjol_flowcontroll/MainFlowControl.cpp

@@ -27,6 +27,7 @@
 #include "read_wlanini.h"
 #include "connect_wlan.h"
 #include "psram.h"
+#include "basic_auth.h"
 
 // support IDF 5.x
 #ifndef portTICK_RATE_MS
@@ -1280,7 +1281,7 @@ esp_err_t handler_editflow(httpd_req_t *req)
                     int _ImageDenoiseLevel = std::stoi(_valuechar);
                     if (CCstatus.CamSensor_id == OV2640_PID)
                     {
-                        CCstatus.ImageDenoiseLevel = 0;
+                        CFstatus.ImageDenoiseLevel = 0;
                     }
                     else
                     {
@@ -1782,108 +1783,108 @@ void register_server_main_flow_task_uri(httpd_handle_t server)
     camuri.method = HTTP_GET;
 
     camuri.uri = "/doinit";
-    camuri.handler = handler_init;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_init);
     camuri.user_ctx = (void *)"Light On";
     httpd_register_uri_handler(server, &camuri);
 
     // Legacy API => New: "/setPreValue"
     camuri.uri = "/setPreValue.html";
-    camuri.handler = handler_prevalue;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_prevalue);
     camuri.user_ctx = (void *)"Prevalue";
     httpd_register_uri_handler(server, &camuri);
 
     camuri.uri = "/setPreValue";
-    camuri.handler = handler_prevalue;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_prevalue);
     camuri.user_ctx = (void *)"Prevalue";
     httpd_register_uri_handler(server, &camuri);
 
     camuri.uri = "/flow_start";
-    camuri.handler = handler_flow_start;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_flow_start);
     camuri.user_ctx = (void *)"Flow Start";
     httpd_register_uri_handler(server, &camuri);
 
     camuri.uri = "/statusflow.html";
-    camuri.handler = handler_statusflow;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_statusflow);
     camuri.user_ctx = (void *)"Light Off";
     httpd_register_uri_handler(server, &camuri);
 
     camuri.uri = "/statusflow";
-    camuri.handler = handler_statusflow;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_statusflow);
     camuri.user_ctx = (void *)"Light Off";
     httpd_register_uri_handler(server, &camuri);
 
     // Legacy API => New: "/cpu_temperature"
     camuri.uri = "/cputemp.html";
-    camuri.handler = handler_cputemp;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_cputemp);
     camuri.user_ctx = (void *)"Light Off";
     httpd_register_uri_handler(server, &camuri);
 
     camuri.uri = "/cpu_temperature";
-    camuri.handler = handler_cputemp;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_cputemp);
     camuri.user_ctx = (void *)"Light Off";
     httpd_register_uri_handler(server, &camuri);
 
     // Legacy API => New: "/rssi"
     camuri.uri = "/rssi.html";
-    camuri.handler = handler_rssi;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_rssi);
     camuri.user_ctx = (void *)"Light Off";
     httpd_register_uri_handler(server, &camuri);
 
     camuri.uri = "/rssi";
-    camuri.handler = handler_rssi;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_rssi);
     camuri.user_ctx = (void *)"Light Off";
     httpd_register_uri_handler(server, &camuri);
 
     camuri.uri = "/date";
-    camuri.handler = handler_current_date;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_current_date);
     camuri.user_ctx = (void *)"Light Off";
     httpd_register_uri_handler(server, &camuri);
 
     camuri.uri = "/uptime";
-    camuri.handler = handler_uptime;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_uptime);
     camuri.user_ctx = (void *)"Light Off";
     httpd_register_uri_handler(server, &camuri);
 
     camuri.uri = "/editflow";
-    camuri.handler = handler_editflow;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_editflow);
     camuri.user_ctx = (void *)"EditFlow";
     httpd_register_uri_handler(server, &camuri);
 
     // Legacy API => New: "/value"
     camuri.uri = "/value.html";
-    camuri.handler = handler_wasserzaehler;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_wasserzaehler);
     camuri.user_ctx = (void *)"Value";
     httpd_register_uri_handler(server, &camuri);
 
     camuri.uri = "/value";
-    camuri.handler = handler_wasserzaehler;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_wasserzaehler);
     camuri.user_ctx = (void *)"Value";
     httpd_register_uri_handler(server, &camuri);
 
     // Legacy API => New: "/value"
     camuri.uri = "/wasserzaehler.html";
-    camuri.handler = handler_wasserzaehler;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_wasserzaehler);
     camuri.user_ctx = (void *)"Wasserzaehler";
     httpd_register_uri_handler(server, &camuri);
 
     camuri.uri = "/json";
-    camuri.handler = handler_json;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_json);
     camuri.user_ctx = (void *)"JSON";
     httpd_register_uri_handler(server, &camuri);
 
     camuri.uri = "/heap";
-    camuri.handler = handler_get_heap;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_get_heap);
     camuri.user_ctx = (void *)"Heap";
     httpd_register_uri_handler(server, &camuri);
 
     camuri.uri = "/stream";
-    camuri.handler = handler_stream;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_stream);
     camuri.user_ctx = (void *)"stream";
     httpd_register_uri_handler(server, &camuri);
 
     /** will handle metrics requests */
     camuri.uri = "/metrics";
-    camuri.handler = handler_openmetrics;
+    camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_openmetrics);
     camuri.user_ctx = (void *)"metrics";
     httpd_register_uri_handler(server, &camuri);
 

+ 1 - 0
code/components/jomjol_helper/Helper.h

@@ -40,6 +40,7 @@ string toUpper(string in);
 
 float temperatureRead();
 
+std::string intToHexString(int _valueInt);
 time_t addDays(time_t startTime, int days);
 
 void memCopyGen(uint8_t* _source, uint8_t* _target, int _size);

+ 173 - 138
code/components/jomjol_influxdb/interface_influxdb.cpp

@@ -8,95 +8,35 @@
 #include "time_sntp.h"
 #include "../../include/defines.h"
 
-
 static const char *TAG = "INFLUXDB";
 
-std::string _influxDBURI;
-std::string _influxDBDatabase;
-std::string _influxDBUser;
-std::string _influxDBPassword;
-
-std::string _influxDB_V2_URI;
-std::string _influxDB_V2_Bucket;
-std::string _influxDB_V2_Token;
-std::string _influxDB_V2_Org;
-
-static esp_err_t http_event_handler(esp_http_client_event_t *evt);
-
-void InfluxDB_V2_Init(std::string _uri, std::string _bucket, std::string _org, std::string _token)
-{
-    _influxDB_V2_URI = _uri;
-    _influxDB_V2_Bucket = _bucket;
-    _influxDB_V2_Org = _org;
-    _influxDB_V2_Token = _token;
-}
-
-void InfluxDB_V2_Publish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC) 
-{
-    char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
-    esp_http_client_config_t http_config = {
-       .user_agent = "ESP32 Meter reader",
-       .method = HTTP_METHOD_POST,
-       .event_handler = http_event_handler,
-       .buffer_size = MAX_HTTP_OUTPUT_BUFFER,
-       .user_data = response_buffer
-    };
-
-    LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDB_V2_Publish - Key: " + _key + ", Content: " + _content + ", timeUTC: " + std::to_string(_timeUTC));
-
-    std::string payload;
-    char nowTimestamp[21];
-
-    if (_timeUTC > 0)
-    {
-        LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Timestamp (UTC): " + std::to_string(_timeUTC));
-        sprintf(nowTimestamp,"%ld000000000", _timeUTC);           // UTC
-        payload = _measurement + " " + _key + "=" + _content + " " + nowTimestamp;
-    }
-    else
-    {
-        LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "no timestamp given");
-        payload = _measurement + " " + _key + "=" + _content;
-    }
-
-    payload.shrink_to_fit();
-
-    LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload);
-
-    std::string apiURI = _influxDB_V2_URI + "/api/v2/write?org=" + _influxDB_V2_Org + "&bucket=" + _influxDB_V2_Bucket;
-    apiURI.shrink_to_fit();
-    http_config.url = apiURI.c_str();
-    ESP_LOGI(TAG, "http_config: %s", http_config.url); // Add mark on log to see when it restarted
-
-    LogFile.WriteToFile(ESP_LOG_INFO, TAG, "API URI: " + apiURI);
-
-    esp_http_client_handle_t http_client = esp_http_client_init(&http_config);
-    LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "client is initialized");
-
-    esp_http_client_set_header(http_client, "Content-Type", "text/plain");
-    std::string _zw = "Token " + _influxDB_V2_Token;
-    //    LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Tokenheader: %s\n", _zw.c_str());
-    esp_http_client_set_header(http_client, "Authorization", _zw.c_str());
-
-    LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "header is set");
-
-    ESP_ERROR_CHECK(esp_http_client_set_post_field(http_client, payload.c_str(), payload.length()));
-    LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "post payload is set");
-
-    esp_err_t err = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_http_client_perform(http_client));
-
-    if( err == ESP_OK ) {
-      LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request was performed");
-      int status_code = esp_http_client_get_status_code(http_client);
-      LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP status code" + std::to_string(status_code));
-    } else {
-      LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request failed");
-    }
-    esp_http_client_cleanup(http_client);
-}
-
-
-
+/**
+ * @brief Buffer to store the HTTP response.
+ * 
+ * This character array is used to store the output of an HTTP response.
+ * The size of the buffer is defined by the constant MAX_HTTP_OUTPUT_BUFFER.
+ */
+char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
+
+
+/**
+ * @brief HTTP event handler callback function.
+ *
+ * This function handles various HTTP client events and logs appropriate messages.
+ *
+ * @param evt Pointer to the HTTP client event structure.
+ * @return esp_err_t ESP_OK on success.
+ *
+ * Event types handled:
+ * - HTTP_EVENT_ERROR: Logs an error message when an HTTP error is encountered.
+ * - HTTP_EVENT_ON_CONNECTED: Logs a message when the HTTP client successfully connects.
+ * - HTTP_EVENT_HEADERS_SENT: Logs a message when all request headers are sent.
+ * - HTTP_EVENT_ON_HEADER: Logs the received header key and value.
+ * - HTTP_EVENT_ON_DATA: Logs the length of the received data.
+ * - HTTP_EVENT_ON_FINISH: Logs a message when the HTTP client finishes the request.
+ * - HTTP_EVENT_DISCONNECTED: Logs a message when the HTTP client disconnects.
+ * - HTTP_EVENT_REDIRECT: Logs a message when an HTTP redirect occurs.
+ */
 static esp_err_t http_event_handler(esp_http_client_event_t *evt)
 {
     switch(evt->event_id)
@@ -130,25 +70,123 @@ 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, long int _timeUTC) {
-    char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
-    esp_http_client_config_t http_config = {
-       .user_agent = "ESP32 Meter reader",
-       .method = HTTP_METHOD_POST,
-       .event_handler = http_event_handler,
-       .buffer_size = MAX_HTTP_OUTPUT_BUFFER,
-       .user_data = response_buffer
-    };
-
-    if (_influxDBUser.length() && _influxDBPassword.length()){
-       http_config.username = _influxDBUser.c_str();
-       http_config.password = _influxDBPassword.c_str();
-       http_config.auth_type = HTTP_AUTH_TYPE_BASIC;
+
+
+/**
+ * @brief Initializes the InfluxDB connection with version 1 settings.
+ * 
+ * This function sets up the connection parameters for InfluxDB version 1.
+ * 
+ * @param _influxDBURI The URI of the InfluxDB server.
+ * @param _database The name of the database to connect to.
+ * @param _user The username for authentication.
+ * @param _password The password for authentication.
+ */
+void InfluxDB::InfluxDBInitV1(std::string _influxDBURI, std::string _database, std::string _user, std::string _password) {
+    version = INFLUXDB_V1;
+    influxDBURI = _influxDBURI;
+    database = _database;
+    user = _user;
+    password = _password;
+}
+
+/**
+ * @brief Initializes the InfluxDB client with version 2 settings.
+ * 
+ * This function sets up the InfluxDB client to use InfluxDB version 2 by
+ * configuring the URI, bucket, organization, and token.
+ * 
+ * @param _influxDBURI The URI of the InfluxDB server.
+ * @param _bucket The bucket name to store data in.
+ * @param _org The organization name associated with the bucket.
+ * @param _token The authentication token for accessing the InfluxDB server.
+ */
+void InfluxDB::InfluxDBInitV2(std::string _influxDBURI, std::string _bucket, std::string _org, std::string _token) {
+    version = INFLUXDB_V2;
+    influxDBURI = _influxDBURI;
+    bucket = _bucket;
+    org = _org;
+    token = _token;
+}
+
+/**
+ * @brief Establishes an HTTP connection to the InfluxDB server.
+ *
+ * This function configures and initializes an HTTP client to connect to the InfluxDB server.
+ * It sets up the necessary parameters such as the URL, event handler, buffer size, and user data.
+ * Depending on the InfluxDB version, it also configures the authentication type and credentials.
+ *
+ * @note This function destroys any existing HTTP client before initializing a new one.
+ *
+ * @param None
+ * @return None
+ */
+void InfluxDB::connectHTTP() {
+    esp_http_client_config_t config = {};
+
+    config.url = influxDBURI.c_str();
+    config.event_handler = http_event_handler;
+    config.buffer_size = MAX_HTTP_OUTPUT_BUFFER;
+    config.user_data = response_buffer;
+
+
+    switch (version) {
+        case INFLUXDB_V1:
+            config.auth_type = HTTP_AUTH_TYPE_BASIC;
+            config.username = user.c_str();
+            config.password = password.c_str();
+            break;
+        case INFLUXDB_V2:
+            break;
+    }
+
+    InfluxDBdestroy();
+    httpClient = esp_http_client_init(&config);
+    if (!httpClient) {
+        LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to initialize HTTP client");
+    } else {
+        LogFile.WriteToFile(ESP_LOG_INFO, TAG, "HTTP client initialized successfully");
+    }
+}
+
+
+/**
+ * @brief Destroys the InfluxDB instance by cleaning up the HTTP client.
+ *
+ * This function checks if the HTTP client is initialized. If it is, it cleans up the HTTP client
+ * and logs the cleanup action. The HTTP client pointer is then set to NULL.
+ */
+void InfluxDB::InfluxDBdestroy() {
+    if (httpClient) {
+        esp_http_client_cleanup(httpClient);
+        LogFile.WriteToFile(ESP_LOG_INFO, TAG, "HTTP client cleaned up");
+        httpClient = NULL;
     }
+}
 
+/**
+ * @brief Publishes data to an InfluxDB instance.
+ *
+ * This function sends a measurement, key, and content to an InfluxDB server.
+ * It supports both InfluxDB v1 and v2 APIs.
+ *
+ * @param _measurement The measurement name to publish.
+ * @param _key The key associated with the measurement.
+ * @param _content The content or value to publish.
+ * @param _timeUTC The timestamp in UTC. If greater than 0, it will be included in the payload.
+ *
+ * The function logs the process and handles HTTP communication with the InfluxDB server.
+ * It constructs the appropriate API URI based on the InfluxDB version and sends the data
+ * using an HTTP POST request.
+ */
+void InfluxDB::InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC) {
+    std::string apiURI;        
     std::string payload;
     char nowTimestamp[21];
 
+    connectHTTP();
+
+
     LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBPublish - Key: " + _key + ", Content: " + _content + ", timeUTC: " + std::to_string(_timeUTC));
 
     if (_timeUTC > 0)
@@ -164,50 +202,47 @@ void InfluxDBPublish(std::string _measurement, std::string _key, std::string _co
     }
 
     payload.shrink_to_fit();
-
     LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload);
 
+    esp_err_t err;
 
-    // use the default retention policy of the bucket
-    std::string apiURI = _influxDBURI + "/write?db=" + _influxDBDatabase;
-//    std::string apiURI = _influxDBURI + "/api/v2/write?bucket=" + _influxDBDatabase + "/";
-
-    apiURI.shrink_to_fit();
-    http_config.url = apiURI.c_str();
-
-    LogFile.WriteToFile(ESP_LOG_INFO, TAG, "API URI: " + apiURI);
+    switch (version) {
+        case INFLUXDB_V1: 
+            apiURI = influxDBURI + "/write?db=" + database;
+            apiURI.shrink_to_fit();
 
-    esp_http_client_handle_t http_client = esp_http_client_init(&http_config);
-    LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "client is initialized");
+            esp_http_client_set_url(httpClient, apiURI.c_str());
+            esp_http_client_set_method(httpClient, HTTP_METHOD_POST);
+            esp_http_client_set_header(httpClient, "Content-Type", "text/plain");
+            esp_http_client_set_post_field(httpClient, payload.c_str(), payload.length());
 
-    esp_http_client_set_header(http_client, "Content-Type", "text/plain");
-    LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "header is set");
-
-    ESP_ERROR_CHECK(esp_http_client_set_post_field(http_client, payload.c_str(), payload.length()));
-    LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "post payload is set");
-
-    esp_err_t err = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_http_client_perform(http_client));
+            err = esp_http_client_perform(httpClient);
+            if (err == ESP_OK) {
+                LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Data published successfully: " + payload);
+            } else {
+                LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to publish data: " + std::string(esp_err_to_name(err)));
+            }
+            break;
 
-    if( err == ESP_OK ) {
-      LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request was performed");
-      int status_code = esp_http_client_get_status_code(http_client);
-      LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP status code" + std::to_string(status_code));
-    } else {
-      LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request failed");
+        case INFLUXDB_V2:        
+            apiURI = influxDBURI + "/api/v2/write?org=" + org + "&bucket=" + bucket;
+            apiURI.shrink_to_fit();
+            LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "apiURI: " + apiURI);
+
+            esp_http_client_set_url(httpClient, apiURI.c_str());
+            esp_http_client_set_method(httpClient, HTTP_METHOD_POST);
+            esp_http_client_set_header(httpClient, "Content-Type", "text/plain");
+            std::string _zw = "Token " + token;
+            esp_http_client_set_header(httpClient, "Authorization", _zw.c_str());
+            esp_http_client_set_post_field(httpClient, payload.c_str(), payload.length());
+            err = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_http_client_perform(httpClient));
+            if (err == ESP_OK) {
+                LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Data published successfully: " + payload);
+            } else {
+                LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Failed to publish data: " + std::string(esp_err_to_name(err)));
+            }
+        break;
     }
-    esp_http_client_cleanup(http_client);
-}
-
-
-void InfluxDBInit(std::string _uri, std::string _database, std::string _user, std::string _password){
-    _influxDBURI = _uri;
-    _influxDBDatabase = _database;
-    _influxDBUser = _user;
-    _influxDBPassword = _password;
- 
-}
-
-void InfluxDBdestroy() {
 }
 
 #endif //ENABLE_INFLUXDB

+ 96 - 7
code/components/jomjol_influxdb/interface_influxdb.h

@@ -8,17 +8,106 @@
 #include <map>
 #include <functional>
 
-// Interface to InfluxDB v1.x
-void InfluxDBInit(std::string _influxDBURI, std::string _database, std::string _user, std::string _password);
-void InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC);
 
-// Interface to InfluxDB v2.x
-void InfluxDB_V2_Init(std::string _uri, std::string _bucket, std::string _org, std::string _token);
-void InfluxDB_V2_Publish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC);
+#include <string>
+#include "esp_http_client.h"
+#include "esp_log.h"
+
+
+enum InfluxDBVersion {
+    INFLUXDB_V1,
+    INFLUXDB_V2
+};
+
+/**
+ * @class InfluxDB
+ * @brief A class to handle connections and data publishing to InfluxDB servers.
+ * 
+ * This class supports both InfluxDB v1.x and v2.x versions. It provides methods to initialize
+ * the connection parameters, publish data, and destroy the connection.
+ * 
+ * @private
+ * @var std::string influxDBURI
+ * URI for the InfluxDB server.
+ * 
+ * @var std::string database
+ * Database name for InfluxDB v1.x.
+ * 
+ * @var std::string user
+ * Username for InfluxDB v1.x.
+ * 
+ * @var std::string password
+ * Password for InfluxDB v1.x.
+ * 
+ * @var std::string bucket
+ * Bucket name for InfluxDB v2.x.
+ * 
+ * @var std::string org
+ * Organization name for InfluxDB v2.x.
+ * 
+ * @var std::string token
+ * Token for InfluxDB v2.x.
+ * 
+ * @var InfluxDBVersion version
+ * Version of the InfluxDB server (v1.x or v2.x).
+ * 
+ * @var esp_http_client_handle_t httpClient
+ * HTTP client handle for making requests to the InfluxDB server.
+ * 
+ * @var void connectHTTP()
+ * Establishes an HTTP connection to the InfluxDB server.
+ * 
+ * @public
+ * @fn void InfluxDBInitV1(std::string _influxDBURI, std::string _database, std::string _user, std::string _password)
+ * Initializes the connection parameters for InfluxDB v1.x.
+ * 
+ * @fn void InfluxDBInitV2(std::string _influxDBURI, std::string _bucket, std::string _org, std::string _token)
+ * Initializes the connection parameters for InfluxDB v2.x.
+ * 
+ * @fn void InfluxDBdestroy()
+ * Destroys the InfluxDB connection.
+ * 
+ * @fn void InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC)
+ * Publishes data to the InfluxDB server.
+ * 
+ * @param _measurement The measurement name.
+ * @param _key The key for the data point.
+ * @param _content The content or value of the data point.
+ * @param _timeUTC The timestamp in UTC for the data point.
+ */
+
+class InfluxDB {
+private:
+    // Information for InfluxDB v1.x
+    std::string influxDBURI = "";
+    // Information for InfluxDB v1.x
+    std::string database = "";
+    std::string user = "";
+    std::string password = "";
+
+    // Information for InfluxDB v2.x
+    std::string bucket = "";
+    std::string org = "";
+    std::string token = "";
+
+    InfluxDBVersion version;
+
+    esp_http_client_handle_t httpClient = NULL;
+
+    void connectHTTP();
+
+public:
+    // Initialize the InfluxDB connection parameters
+    void InfluxDBInitV1(std::string _influxDBURI, std::string _database, std::string _user, std::string _password);
+    void InfluxDBInitV2(std::string _influxDBURI, std::string _bucket, std::string _org, std::string _token);
 
+    // Destroy the InfluxDB connection
+    void InfluxDBdestroy();
+    // Publish data to the InfluxDB server
+    void InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC);
+};
 
 
-void InfluxDBdestroy();
 
 #endif //INTERFACE_INFLUXDB_H
 #endif //ENABLE_INFLUXDB

+ 2 - 1
code/components/jomjol_mqtt/server_mqtt.cpp

@@ -12,6 +12,7 @@
 #include "interface_mqtt.h"
 #include "time_sntp.h"
 #include "../../include/defines.h"
+#include "basic_auth.h"
 
 
 
@@ -347,7 +348,7 @@ void register_server_mqtt_uri(httpd_handle_t server) {
     uri.method    = HTTP_GET;
 
     uri.uri       = "/mqtt_publish_discovery";
-    uri.handler   = scheduleSendingDiscovery_and_static_Topics;
+    uri.handler = APPLY_BASIC_AUTH_FILTER(scheduleSendingDiscovery_and_static_Topics);
     uri.user_ctx  = (void*) "";    
     httpd_register_uri_handler(server, &uri); 
 }

+ 107 - 0
code/components/jomjol_wlan/basic_auth.cpp

@@ -0,0 +1,107 @@
+#include "basic_auth.h"
+#include "read_wlanini.h"
+#include <esp_tls_crypto.h>
+#include <esp_log.h>
+
+
+#define HTTPD_401 "401 UNAUTHORIZED"
+
+static const char *TAG = "HTTPAUTH";
+
+typedef struct {
+    const char *username;
+    const char *password;
+} basic_auth_info_t;
+
+basic_auth_info_t basic_auth_info = { NULL, NULL };
+
+void init_basic_auth() {
+    if (!wlan_config.http_username.empty() && !wlan_config.http_password.empty()) {
+        basic_auth_info.username = wlan_config.http_username.c_str();
+        basic_auth_info.password = wlan_config.http_password.c_str();
+    }
+}
+
+static char *http_auth_basic(const char *username, const char *password)
+{
+    int out;
+    char *user_info = NULL;
+    char *digest = NULL;
+    size_t n = 0;
+    asprintf(&user_info, "%s:%s", username, password);
+    if (!user_info) {
+        ESP_LOGE(TAG, "No enough memory for user information");
+        return NULL;
+    }
+    esp_crypto_base64_encode(NULL, 0, &n, (const unsigned char *)user_info, strlen(user_info));
+
+    /* 6: The length of the "Basic " string
+     * n: Number of bytes for a base64 encode format
+     * 1: Number of bytes for a reserved which be used to fill zero
+    */
+    digest = static_cast<char*>(calloc(1, 6 + n + 1));
+    if (digest) {
+        strcpy(digest, "Basic ");
+        esp_crypto_base64_encode((unsigned char *)digest + 6, n, (size_t *)&out, (const unsigned char *)user_info, strlen(user_info));
+    }
+    free(user_info);
+    return digest;
+}
+
+esp_err_t basic_auth_request_filter(httpd_req_t *req, esp_err_t original_handler(httpd_req_t *))
+{
+    char *buf = NULL;
+    size_t buf_len = 0;
+    esp_err_t ret = ESP_OK;
+
+    char unauthorized[] = "You are not authorized to use this website!";
+
+    if (basic_auth_info.username == NULL || basic_auth_info.password == NULL) {
+        ret = original_handler(req);
+    } else {
+        buf_len = httpd_req_get_hdr_value_len(req, "Authorization") + 1;
+        if (buf_len > 1) {
+            buf = static_cast<char*>(calloc(1, buf_len));
+            if (!buf) {
+                ESP_LOGE(TAG, "No enough memory for basic authorization");
+                return ESP_ERR_NO_MEM;
+            }
+
+            if (httpd_req_get_hdr_value_str(req, "Authorization", buf, buf_len) == ESP_OK) {
+                ESP_LOGI(TAG, "Found header => Authorization: %s", buf);
+            } else {
+                ESP_LOGE(TAG, "No auth value received");
+            }
+
+            char *auth_credentials = http_auth_basic(basic_auth_info.username, basic_auth_info.password);
+            if (!auth_credentials) {
+                ESP_LOGE(TAG, "No enough memory for basic authorization credentials");
+                free(buf);
+                return ESP_ERR_NO_MEM;
+            }
+
+            if (strncmp(auth_credentials, buf, buf_len)) {
+                ESP_LOGE(TAG, "Not authenticated");
+                httpd_resp_set_status(req, HTTPD_401);
+                httpd_resp_set_type(req, HTTPD_TYPE_TEXT);
+                httpd_resp_set_hdr(req, "Connection", "keep-alive");
+                httpd_resp_set_hdr(req, "WWW-Authenticate", "Basic realm=\"AIOTED\"");
+                httpd_resp_send(req, unauthorized, strlen(unauthorized));
+            } else {
+                ESP_LOGI(TAG, "Authenticated calling http handler now!");
+                ret=original_handler(req);
+            }
+            free(auth_credentials);
+            free(buf);
+        } else {
+            ESP_LOGE(TAG, "No auth header received");
+            httpd_resp_set_status(req, HTTPD_401);
+            httpd_resp_set_type(req, HTTPD_TYPE_TEXT);
+            httpd_resp_set_hdr(req, "Connection", "keep-alive");
+            httpd_resp_set_hdr(req, "WWW-Authenticate", "Basic realm=\"AIOTED\"");
+            httpd_resp_send(req, unauthorized, strlen(unauthorized));
+        }
+    }
+
+    return ret;
+}

+ 8 - 0
code/components/jomjol_wlan/basic_auth.h

@@ -0,0 +1,8 @@
+#pragma once
+
+#include <esp_http_server.h>
+
+void init_basic_auth();
+esp_err_t basic_auth_request_filter(httpd_req_t *req, esp_err_t original_handler(httpd_req_t *));
+
+#define APPLY_BASIC_AUTH_FILTER(method) [](httpd_req_t *req){ return basic_auth_request_filter(req, method); }

+ 11 - 0
code/components/jomjol_wlan/connect_wlan.cpp

@@ -49,6 +49,9 @@
 #define ets_delay_us(a) esp_rom_delay_us(a)
 #endif
 
+#include "../esp-protocols/components/mdns/include/mdns.h" 
+
+
 static const char *TAG = "WIFI";
 
 static bool APWithBetterRSSI = false;
@@ -657,6 +660,14 @@ esp_err_t wifi_init_sta(void)
         else {
 			LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Set hostname to: " + wlan_config.hostname);
         }
+		//initialize mDNS service
+        retval = mdns_init();
+        if (retval != ESP_OK) {
+            LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mdns_init failed! Error: "  + std::to_string(retval));
+        } else {
+			//set mdns hostname
+			mdns_hostname_set(wlan_config.hostname.c_str());
+	    }
     }
 
     LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Init successful");

+ 23 - 0
code/components/jomjol_wlan/read_wlanini.cpp

@@ -145,6 +145,29 @@ int LoadWlanFromFile(std::string fn)
                 wlan_config.dns = tmp;
                 LogFile.WriteToFile(ESP_LOG_INFO, TAG, "DNS: " + wlan_config.dns);
             }
+
+            else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "HTTP_USERNAME")){
+                tmp = splitted[1];
+                if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){
+                    tmp = tmp.substr(1, tmp.length()-2);
+                }
+                wlan_config.http_username = tmp;
+                LogFile.WriteToFile(ESP_LOG_INFO, TAG, "HTTP_USERNAME: " + wlan_config.http_username);
+            }
+
+            else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "HTTP_PASSWORD")){
+                tmp = splitted[1];
+                if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){
+                    tmp = tmp.substr(1, tmp.length()-2);
+                }
+                wlan_config.http_password = tmp;
+                #ifndef __HIDE_PASSWORD
+                LogFile.WriteToFile(ESP_LOG_INFO, TAG, "HTTP_PASSWORD: " + wlan_config.http_password);
+                #else
+                LogFile.WriteToFile(ESP_LOG_INFO, TAG, "HTTP_PASSWORD: XXXXXXXX");
+                #endif
+            }
+
             #if (defined WLAN_USE_ROAMING_BY_SCANNING || (defined WLAN_USE_MESH_ROAMING && defined WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES))
             else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "RSSITHRESHOLD")){
                 tmp = trim(splitted[1]);

+ 2 - 0
code/components/jomjol_wlan/read_wlanini.h

@@ -13,6 +13,8 @@ struct wlan_config {
     std::string gateway = "";
     std::string netmask = "";
     std::string dns = "";
+    std::string http_username = "";
+    std::string http_password = "";
     int rssi_threshold = 0;                 // Default: 0 -> ROAMING disabled
 };
 extern struct wlan_config wlan_config;

+ 3 - 4
code/dependencies.lock

@@ -1,16 +1,15 @@
 dependencies:
   espressif/esp-nn:
-    component_hash: b32869798bdb40dec6bc99caca48cd65d42f8a9f506b9ab9c598a076f891ede9
+    component_hash: f6f2851ce82137a66e4265071c9b852bbe0130b882a18dea9f03faea7bf1295a
     source:
-      pre_release: true
       service_url: https://api.components.espressif.com/
       type: service
-    version: 1.0.2
+    version: 1.1.0
   idf:
     component_hash: null
     source:
       type: idf
     version: 5.3.1
-manifest_hash: 6995555b9b41e897235448c868ca92c0c3401fd2ff90df084be9bb8629958f2c
+manifest_hash: f88c9e5c2d75a9d5d6968fc67a90ef0cd7146dd6a3905a79c4dfcfc3b4fe6731
 target: esp32
 version: 1.0.0

+ 39 - 11
code/main/main.cpp

@@ -33,6 +33,8 @@
 #include "configFile.h"
 #include "server_main.h"
 #include "server_camera.h"
+#include "basic_auth.h"
+
 #ifdef ENABLE_MQTT
     #include "server_mqtt.h"
 #endif //ENABLE_MQTT
@@ -77,10 +79,10 @@
     static heap_trace_record_t trace_record[NUM_RECORDS]; // This buffer must be in internal RAM
 #endif
 
-extern const char* GIT_TAG;
-extern const char* GIT_REV;
-extern const char* GIT_BRANCH;
-extern const char* BUILD_TIME;
+extern const char *GIT_TAG;
+extern const char *GIT_REV;
+extern const char *GIT_BRANCH;
+extern const char *BUILD_TIME;
 
 extern std::string getFwVersion(void);
 extern std::string getHTMLversion(void);
@@ -107,27 +109,51 @@ bool Init_NVS_SDCard()
     sdmmc_host_t host = SDMMC_HOST_DEFAULT();
     host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
 
+    // For SoCs where the SD power can be supplied both via an internal or external (e.g. on-board LDO) power supply.
+    // When using specific IO pins (which can be used for ultra high-speed SDMMC) to connect to the SD card
+    // and the internal LDO power supply, we need to initialize the power supply first.
+#if SD_PWR_CTRL_LDO_INTERNAL_IO
+    sd_pwr_ctrl_ldo_config_t ldo_config = {
+        .ldo_chan_id = CONFIG_EXAMPLE_SD_PWR_CTRL_LDO_IO_ID,
+    };
+    sd_pwr_ctrl_handle_t pwr_ctrl_handle = NULL;
+
+    ret = sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &pwr_ctrl_handle);
+    if (ret != ESP_OK)
+    {
+        ESP_LOGE(TAG, "Failed to create a new on-chip LDO power control driver");
+        return ret;
+    }
+    host.pwr_ctrl_handle = pwr_ctrl_handle;
+#endif
+
     // This initializes the slot without card detect (CD) and write protect (WP) signals.
     // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
+#ifdef CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
+    sdmmc_slot_config_t slot_config = {
+        .cd = SDMMC_SLOT_NO_CD,
+        .wp = SDMMC_SLOT_NO_WP,
+    };
+#else
     sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
+#endif
 
     // Set bus width to use:
 #ifdef __SD_USE_ONE_LINE_MODE__
     slot_config.width = 1;
-#ifdef SOC_SDMMC_USE_GPIO_MATRIX
+#ifdef CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
     slot_config.clk = GPIO_SDCARD_CLK;
     slot_config.cmd = GPIO_SDCARD_CMD;
     slot_config.d0 = GPIO_SDCARD_D0;
-#endif
-
-#else
+#endif // end CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
+#else  // else __SD_USE_ONE_LINE_MODE__
     slot_config.width = 4;
-#ifdef SOC_SDMMC_USE_GPIO_MATRIX
+#ifdef CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
     slot_config.d1 = GPIO_SDCARD_D1;
     slot_config.d2 = GPIO_SDCARD_D2;
     slot_config.d3 = GPIO_SDCARD_D3;
-#endif
-#endif
+#endif // end CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
+#endif // end __SD_USE_ONE_LINE_MODE__
 
     // Enable internal pullups on enabled pins. The internal pullups
     // are insufficient however, please make sure 10k external pullups are
@@ -430,6 +456,8 @@ extern "C" void app_main(void)
             StatusLED(WLAN_INIT, 3, true);
             return;
         }
+
+        init_basic_auth();
     }
     else if (iWLANStatus == -1) {  // wlan.ini not available, potentially empty or content not readable
         StatusLED(WLAN_INIT, 1, true);

+ 6 - 5
code/main/server_main.cpp

@@ -17,6 +17,7 @@
 
 #include "MainFlowControl.h"
 #include "esp_log.h"
+#include "basic_auth.h"
 #include "esp_chip_info.h"
 
 #include <stdio.h>
@@ -408,7 +409,7 @@ void register_server_main_uri(httpd_handle_t server, const char *base_path)
     httpd_uri_t info_get_handle = {
         .uri       = "/info",  // Match all URIs of type /path/to/file
         .method    = HTTP_GET,
-        .handler   = info_get_handler,
+        .handler = APPLY_BASIC_AUTH_FILTER(info_get_handler),
         .user_ctx  = (void*) base_path    // Pass server data as context
     };
     httpd_register_uri_handler(server, &info_get_handle);
@@ -416,7 +417,7 @@ void register_server_main_uri(httpd_handle_t server, const char *base_path)
     httpd_uri_t sysinfo_handle = {
         .uri       = "/sysinfo",  // Match all URIs of type /path/to/file
         .method    = HTTP_GET,
-        .handler   = sysinfo_handler,
+        .handler = APPLY_BASIC_AUTH_FILTER(sysinfo_handler),
         .user_ctx  = (void*) base_path    // Pass server data as context
     };
     httpd_register_uri_handler(server, &sysinfo_handle);
@@ -424,7 +425,7 @@ void register_server_main_uri(httpd_handle_t server, const char *base_path)
     httpd_uri_t starttime_tmp_handle = {
         .uri       = "/starttime",  // Match all URIs of type /path/to/file
         .method    = HTTP_GET,
-        .handler   = starttime_get_handler,
+        .handler = APPLY_BASIC_AUTH_FILTER(starttime_get_handler),
         .user_ctx  = NULL    // Pass server data as context
     };
     httpd_register_uri_handler(server, &starttime_tmp_handle);
@@ -432,7 +433,7 @@ void register_server_main_uri(httpd_handle_t server, const char *base_path)
     httpd_uri_t img_tmp_handle = {
         .uri       = "/img_tmp/*",  // Match all URIs of type /path/to/file
         .method    = HTTP_GET,
-        .handler   = img_tmp_virtual_handler,
+        .handler = APPLY_BASIC_AUTH_FILTER(img_tmp_virtual_handler),
         .user_ctx  = (void*) base_path    // Pass server data as context
     };
     httpd_register_uri_handler(server, &img_tmp_handle);
@@ -440,7 +441,7 @@ void register_server_main_uri(httpd_handle_t server, const char *base_path)
     httpd_uri_t main_rest_handle = {
         .uri       = "/*",  // Match all URIs of type /path/to/file
         .method    = HTTP_GET,
-        .handler   = hello_main_handler,
+        .handler = APPLY_BASIC_AUTH_FILTER(hello_main_handler),
         .user_ctx  = (void*) base_path    // Pass server data as context
     };
     httpd_register_uri_handler(server, &main_rest_handle);

+ 5 - 4
code/main/softAP.cpp

@@ -29,6 +29,7 @@
 #include "Helper.h"
 #include "statusled.h"
 #include "server_ota.h"
+#include "basic_auth.h"
 
 #include "lwip/err.h"
 #include "lwip/sys.h"
@@ -468,7 +469,7 @@ httpd_handle_t start_webserverAP(void)
     httpd_uri_t reboot_handle = {
         .uri       = "/reboot",  // Match all URIs of type /path/to/file
         .method    = HTTP_GET,
-        .handler   = reboot_handlerAP,
+        .handler = APPLY_BASIC_AUTH_FILTER(reboot_handlerAP),
         .user_ctx  = NULL    // Pass server data as context
     };
     httpd_register_uri_handler(server, &reboot_handle);
@@ -476,7 +477,7 @@ httpd_handle_t start_webserverAP(void)
     httpd_uri_t config_ini_handle = {
         .uri       = "/config",  // Match all URIs of type /path/to/file
         .method    = HTTP_GET,
-        .handler   = config_ini_handler,
+        .handler = APPLY_BASIC_AUTH_FILTER(config_ini_handler),
         .user_ctx  = NULL    // Pass server data as context
     };
     httpd_register_uri_handler(server, &config_ini_handle);
@@ -485,7 +486,7 @@ httpd_handle_t start_webserverAP(void)
     httpd_uri_t file_uploadAP = {
         .uri       = "/upload/*",   // Match all URIs of type /upload/path/to/file
         .method    = HTTP_POST,
-        .handler   = upload_post_handlerAP,
+        .handler = APPLY_BASIC_AUTH_FILTER(upload_post_handlerAP),
         .user_ctx  = NULL    // Pass server data as context
     };
     httpd_register_uri_handler(server, &file_uploadAP);
@@ -493,7 +494,7 @@ httpd_handle_t start_webserverAP(void)
     httpd_uri_t test_uri = {
         .uri      = "*",
         .method   = HTTP_GET,
-        .handler  = test_handler,
+        .handler = APPLY_BASIC_AUTH_FILTER(test_handler),
         .user_ctx = NULL
     };
     httpd_register_uri_handler(server, &test_uri);

+ 20 - 1
code/sdkconfig.defaults

@@ -133,7 +133,25 @@ CONFIG_MQTT_USE_CUSTOM_CONFIG=y
 CONFIG_MBEDTLS_HAVE_TIME=y
 CONFIG_MBEDTLS_HAVE_TIME_DATE=y
 
-CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=n
+#
+# ESP-Driver:LEDC Configurations
+#
+CONFIG_LEDC_CTRL_FUNC_IN_IRAM=y
+# end of ESP-Driver:LEDC Configurations
+
+#
+# Legacy RMT Driver Configurations
+#
+CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y
+# end of Legacy RMT Driver Configurations
+
+#
+# ESP-Driver:RMT Configurations
+#
+CONFIG_RMT_ISR_IRAM_SAFE=y
+CONFIG_RMT_RECV_FUNC_IN_IRAM=y
+# CONFIG_RMT_ENABLE_DEBUG_LOG is not set
+# end of ESP-Driver:RMT Configurations
 
 CONFIG_CAMERA_CORE0=n
 CONFIG_CAMERA_CORE1=y
@@ -162,6 +180,7 @@ CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=4864
 #CONFIG_FREERTOS_USE_TRACE_FACILITY=1
 #CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
 #CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y
+CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=n
 
 #force disable HIMEM as not used in default config, can be enabled with [env:esp32cam-dev-himem]
 #free 256kb of internal memory :

+ 7 - 0
docs/index.html

@@ -51,7 +51,14 @@
          <li>The SD card can be setup automatically after the firmware got installed. See the <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#remote-setup-using-the-built-in-access-point target=_blank>documentation</a> for details. For this to work, the SD card must be FAT formated (which is the default on a new SD card).
             Alternatively the SD card still can be setup manually, see the <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#3-sd-card target=_blank>documentation</a> for details!</li>
       </ul>
+
+      <h2>Licence:</h2>
+      <ul>
+         <li>This project is published under an individual license: <a href="https://github.com/jomjol/AI-on-the-edge-device?tab=License-1-ov-file#readme" target="_blank">License</a></li> 
+         <li>By using this web installer, you agree to this license. In particular, a separate agreement with the authors is required for commercial use of AI-on-the-Edge.</li>
+      </ul>
       
+
       <hr>
 
       <p><esp-web-install-button manifest="manifest.json"></esp-web-install-button></p>

+ 9 - 9
param-docs/parameter-pages/PostProcessing/NUMBER.ChangeRateThreshold.md

@@ -9,17 +9,17 @@ This parameter is intended to compensate for small reading fluctuations that occ
 It is only applied to the last digit of the read value (See example below).
 If the read value is within PreValue +/- Threshold, no further calculation is carried out and the Value/Prevalue remains at the old value.
 
-Example:
+## Example
 
-    Smallest ROI provides value for 0.000x
-    ChangeRateThreshold = 2
+- Smallest ROI provides value for `0.000'x` (Eg. a water meter with 4 pointers behind the decimal point)
+- ChangeRateThreshold = 2
   
-    Extended Resolution disabled:
-    PreValue: 123.456'7 >>> Threshold = +/- 0.000'2
-	Comparative value >>> max = 123.456'9 and min = 123.456'5
+#### With `Extended Resolution` **disabled**
+PreValue: `123.456'7` -> Threshold = `+/-0.000'2`.<br>
+All changes between `123.456'5` and `123.456'9` get ignored
 	
-    Extended Resolution enabled:
-    PreValue: 123.456'78 >>> Threshold = +/- 0.000'02
-	Comparative value >>> max = 123.456'80 and min = 123.456'76
+#### With `Extended Resolution` **enabled**
+PreValue: `123.456'78` -> Threshold = `+/-0.000'02`.<br>
+All changes between `123.456'76` and `123.456'80` get ignored.
 
 ![](img/ChangeRateThreshold.png)

二進制
sd-card/config/ana-cont_1400_s2_q.tflite


二進制
sd-card/config/dig-class11_1900_s2_q.tflite


二進制
sd-card/config/dig-class11_1910_s2_q.tflite


二進制
sd-card/config/dig-cont_0800_s3_q.tflite


二進制
sd-card/config/dig-cont_0810_s3_q.tflite


+ 11 - 0
sd-card/wlan.ini

@@ -36,3 +36,14 @@ password = ""
 ; Default: 0 = Disable client requested roaming query
 
 RSSIThreshold = 0
+
+;++++++++++++++++++++++++++++++++++
+; Password Protection of the Web Interface and the REST API
+; When those parameters are active, the Web Interface and the REST API are protected by a username and password.
+; Note: This is be a WEAK and INSECURE way to protect the Web Interface and the REST API.
+;       There was no audit nor a security review to check the correct implementation of the protection!
+;       The password gets transmitted unencrypted (plain text), this means it is very easy to extract it
+;       for somebody who has access to your WIFI!
+;       USE AT YOUR OWN RISK!
+;http_username = "myusername"
+;http_password = "mypassword"