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

This commit is contained in:
SybexX
2025-02-14 21:20:37 +01:00
committed by GitHub
49 changed files with 1702 additions and 780 deletions

View File

@@ -50,13 +50,13 @@ jobs:
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v2 uses: actions/configure-pages@v5
- name: Upload artifact - name: Upload artifact
uses: actions/upload-pages-artifact@v1 uses: actions/upload-pages-artifact@v3
with: with:
path: 'docs' path: 'docs'
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
id: deployment id: deployment
uses: actions/deploy-pages@v1 uses: actions/deploy-pages@v4

3
.gitmodules vendored
View File

@@ -10,3 +10,6 @@
[submodule "code/components/stb"] [submodule "code/components/stb"]
path = code/components/stb path = code/components/stb
url = https://github.com/nothings/stb.git 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

View File

@@ -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 ## [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) 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 #### Core Changes
Only changes since RC4 are listed: Only changes since RC4 are listed:
- Removed `Autostart` parameter and make the flow to be always enabled (#3423) - [#3423](https://github.com/jomjol/AI-on-the-edge-device/pull/3423) Removed `Autostart` parameter and make the flow to be always enabled
- Enable `Flow start` menu entry in UI (#3423) - [#3423](https://github.com/jomjol/AI-on-the-edge-device/pull/3423) Enable `Flow start` menu entry in UI
- Updated the Homeassistant Discovery topics (#3332): - [#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 `""`. - `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`. - `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 `""`. - 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) - [#3415](https://github.com/jomjol/AI-on-the-edge-device/pull/3415) Added button for `flow start`
- Added support for Domoticz MQTT integration (#3359) - [#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 - Added Date and time to overview page
- Updated submodules and models - Updated submodules and models
**:warning: Please check your Homeassistant instance to make sure it is handled correctly!** **:warning: Please check your Homeassistant instance to make sure it is handled correctly!**
#### Bug Fixes #### Bug Fixes
Only changes since RC3 are listed: Only changes since RC4 are listed:
- Added fix for ledintensity (#3418) - [#3418](https://github.com/jomjol/AI-on-the-edge-device/pull/3418) Added fix for ledintensity
- Added fix for OV2640 brightness contrast saturation (#3417) - [#3417](https://github.com/jomjol/AI-on-the-edge-device/pull/3417) Added fix for OV2640 brightness contrast saturation
- Added fix for 'AnalogToDigitTransitionStart' always using 9.2 regardless of the configured value (#3393) - [#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
- Addef fix for HA menu entry (#3342) - [#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 ## [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 #### Core Changes
Only changes since RC3 are listed: Only changes since RC3 are listed:
- Update esp32-camera submodule to `v2.0.13` (#3316) - [#3316](https://github.com/jomjol/AI-on-the-edge-device/pull/3316) Update esp32-camera submodule to `v2.0.13`
- Added contributor list (#3317) - [#3317](https://github.com/jomjol/AI-on-the-edge-device/pull/3317) Added contributor list
- Added files for demo mode (#3315) - [#3315](https://github.com/jomjol/AI-on-the-edge-device/pull/3315) Added files for demo mode
#### Bug Fixes #### Bug Fixes
Only changes since RC2 are listed: 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 ## [16.0.0-RC3] - 2024-10-05
@@ -62,7 +81,7 @@ Only changes since RC2 are listed:
#### Bug Fixes #### Bug Fixes
Only changes since RC2 are listed: 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 ## [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 #### Core Changes
Only changes since RC1 are listed: Only changes since RC1 are listed:
- Updated parameter documentation pages - Updated parameter documentation pages
- Rename/remove unused parameters (#3291) - [#3291](https://github.com/jomjol/AI-on-the-edge-device/pull/3291) Rename/remove unused parameters
- Migrate-cam-parameters (#3288) - [#3288](https://github.com/jomjol/AI-on-the-edge-device/pull/3288) Migrate-cam-parameters
#### Bug Fixes #### Bug Fixes
Only changes since RC1 are listed: Only changes since RC1 are listed:
- Reverted TFlite submodule update as certain modules crash with it (#3269) - [#3269](https://github.com/jomjol/AI-on-the-edge-device/pull/3269) Reverted TFlite submodule update as certain modules crash with it
- Changed the webhook UploadImg to false (#3279) - [#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 - Changed default value from boolean to numeric value in parameter camDenoise documentation
- Updated config page - Updated config page
@@ -96,24 +115,24 @@ Please check the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues
#### Core Changes #### Core Changes
Those are just the major 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 - New tflite-Models
- Homeassistant service discovery: derive node_id when using nested topics (#3088) - [#3088](https://github.com/jomjol/AI-on-the-edge-device/pull/3088) Homeassistant service discovery: derive node_id when using nested topics
- Added Prometheus/OpenMetrics exporter (#3081) - [#3081](https://github.com/jomjol/AI-on-the-edge-device/pull/3081) Added Prometheus/OpenMetrics exporter
- Added Webhook (#3148, #3163, #3174) - [#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
- Add rate threshold parameter (#3195) - [#3195](https://github.com/jomjol/AI-on-the-edge-device/pull/3195) Add rate threshold parameter
- Added a Delay between the WiFi reconnections (#3068) - [#3068](https://github.com/jomjol/AI-on-the-edge-device/pull/3068) Added a Delay between the WiFi reconnections
- Web UI improvements - Web UI improvements
- Various minor changes - Various minor changes
- Update platformIO to 6.9.0 (Contains ESP IDF 5.3.1) - Update platformIO to 6.9.0 (Contains ESP IDF 5.3.1)
#### Bug Fixes #### Bug Fixes
Those are just the major changes: Those are just the major changes:
- Handle crash on corrupted model (#3220) - [#3220](https://github.com/jomjol/AI-on-the-edge-device/pull/3220) Handle crash on corrupted model
- Bugfix for boot loop (#3175) - [#3175](https://github.com/jomjol/AI-on-the-edge-device/pull/3175) Bugfix for boot loop
- Bugfix for time stamp (#3180) - [#3180](https://github.com/jomjol/AI-on-the-edge-device/pull/3180) Bugfix for time stamp
- Handle empty prevalue.ini gracefully (#3162) - [#3162](https://github.com/jomjol/AI-on-the-edge-device/pull/3162) Handle empty prevalue.ini gracefully
- Added note about only TLS 1.2 is supported (#3213) - [#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 ## [15.7.0] - 2024-02-17

View File

@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16.0) 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( ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.cpp OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.cpp

View File

@@ -1,7 +1,33 @@
/********************************************************************************
* 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 "Color.h"
#include <algorithm> #include <algorithm>
#include <cmath>
#include <cassert> #include <cassert>
#include <cmath>
namespace { namespace {
@@ -29,7 +55,7 @@ int iRgbSqrt( int num ) {
return res; 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 // https://stackoverflow.com/questions/24152553/hsv-to-rgb-and-back-without-floating-point-math-in-python
// greyscale // greyscale
if (y.s == 0) { if (y.s == 0) {
@@ -45,31 +71,56 @@ Rgb::Rgb( Hsv y ) {
const int t = (y.v * (255 - ((y.s * (255 - remainder)) >> 8))) >> 8; const int t = (y.v * (255 - ((y.s * (255 - remainder)) >> 8))) >> 8;
switch (region) { switch (region) {
case 0: r = y.v; g = t; b = p; break; case 0:
case 1: r = q; g = y.v; b = p; break; r = y.v;
case 2: r = p; g = y.v; b = t; break; g = t;
case 3: r = p; g = q; b = y.v; break; b = p;
case 4: r = t; g = p; b = y.v; break; break;
case 5: r = y.v; g = p; b = q; break; case 1:
default: __builtin_trap(); 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; a = y.a;
} }
Rgb& Rgb::operator=( Hsv hsv ) { Rgb& Rgb::operator=(const Hsv& hsv) {
Rgb r { hsv }; Rgb r { hsv };
swap(r); swap(r);
return *this; return *this;
} }
Rgb Rgb::operator+( Rgb in ) const { Rgb Rgb::operator+(const Rgb& in) const {
auto copy = *this; auto copy = *this;
copy += in; copy += in;
return copy; return copy;
} }
Rgb& Rgb::operator+=( Rgb in ) { Rgb& Rgb::operator+=(const Rgb& in) {
unsigned int red = r + in.r; unsigned int red = r + in.r;
r = (red < 255) ? red : 255; r = (red < 255) ? red : 255;
unsigned int green = g + in.g; unsigned int green = g + in.g;
@@ -79,7 +130,20 @@ Rgb& Rgb::operator+=( Rgb in ) {
return *this; return *this;
} }
Rgb& Rgb::blend( Rgb in ) { 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;
}
Rgb& Rgb::blend(const Rgb& in) {
unsigned int inAlpha = in.a * (255 - a); unsigned int inAlpha = in.a * (255 - a);
unsigned int alpha = a + inAlpha; unsigned int alpha = a + inAlpha;
r = iRgbSqrt(((r * r * a) + (in.r * in.r * inAlpha)) / alpha); r = iRgbSqrt(((r * r * a) + (in.r * in.r * inAlpha)) / alpha);
@@ -89,16 +153,7 @@ Rgb& Rgb::blend( Rgb in ) {
return *this; return *this;
} }
uint8_t IRAM_ATTR Rgb::getGrb( int idx ) { Hsv::Hsv(const Rgb& r) {
switch ( idx ) {
case 0: return g;
case 1: return r;
case 2: return b;
}
__builtin_unreachable();
}
Hsv::Hsv( Rgb r ) {
int min = std::min(r.r, std::min(r.g, r.b)); 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 max = std::max(r.r, std::max(r.g, r.b));
int chroma = max - min; int chroma = max - min;
@@ -125,7 +180,7 @@ Hsv::Hsv( Rgb r ) {
a = r.a; a = r.a;
} }
Hsv& Hsv::operator=( Rgb rgb ) { Hsv& Hsv::operator=(const Rgb& rgb) {
Hsv h { rgb }; Hsv h { rgb };
swap(h); swap(h);
return *this; return *this;

View File

@@ -1,34 +1,77 @@
/********************************************************************************
* 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 #pragma once
#ifndef COLOR_H
#define COLOR_H
#include <cstdint>
#include "esp_attr.h" #include "esp_attr.h"
#include <cstdint>
union Hsv; union Hsv;
union Rgb { union Rgb {
struct __attribute__((packed)) { struct __attribute__((packed)) {
uint8_t r, g, b, a; uint8_t g, r, b, a;
}; };
uint32_t value; 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(uint8_t r = 0, uint8_t g = 0, uint8_t b = 0, uint8_t a = 255)
Rgb( Hsv c ); : g(g)
Rgb& operator=( Rgb rgb ) { swap( rgb ); return *this; } , r(r)
Rgb& operator=( Hsv hsv ); , b(b)
Rgb operator+( Rgb in ) const; , a(a) {}
Rgb& operator+=( Rgb in ); Rgb(const Hsv& c);
bool operator==( Rgb in ) const { return in.value == value; } Rgb(const Rgb&) = default;
Rgb& blend( Rgb in ); Rgb& operator=(const Rgb& rgb) {
void swap( Rgb& o ) { value = o.value; } 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() { void linearize() {
r = channelGamma(r); r = channelGamma(r);
g = channelGamma(g); g = channelGamma(g);
b = channelGamma(b); b = channelGamma(b);
} }
uint8_t IRAM_ATTR getGrb( int idx ); 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 stretchChannels(uint8_t maxR, uint8_t maxG, uint8_t maxB) { void stretchChannels(uint8_t maxR, uint8_t maxG, uint8_t maxB) {
r = stretch(r, maxR); r = stretch(r, maxR);
@@ -36,14 +79,10 @@ union Rgb {
b = stretch(b, maxB); b = stretch(b, maxB);
} }
void stretchChannelsEvenly( uint8_t max ) { void stretchChannelsEvenly(uint8_t max) { stretchChannels(max, max, max); }
stretchChannels( max, max, max );
}
private: private:
uint8_t stretch( int value, uint8_t max ) { uint8_t stretch(int value, uint8_t max) { return (value * max) >> 8; }
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 /* The optimal gamma correction is x^2.8. However, this is expensive to
@@ -63,12 +102,17 @@ union Hsv {
}; };
uint32_t value; 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(uint8_t h, uint8_t s = 0, uint8_t v = 0, uint8_t a = 255)
Hsv( Rgb r ); : h(h)
Hsv& operator=( Hsv h ) { swap( h ); return *this; } , s(s)
Hsv& operator=( Rgb rgb ); , v(v)
bool operator==( Hsv in ) const { return in.value == value; } , a(a) {}
void swap( Hsv& o ) { value = o.value; } 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,90 +1,35 @@
/********************************************************************************
* 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" #include "SmartLeds.h"
/* PlatformIO 6 (ESP IDF 5) does no longer allow access to RMTMEM,
see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/migration-guides/release-5.x/5.0/peripherals.html?highlight=rmtmem#id5
As a dirty workaround, we copy the needed structures from rmt_struct.h
In the long run, this should be replaced! */
typedef struct rmt_item32_s {
union {
struct {
uint32_t duration0 :15;
uint32_t level0 :1;
uint32_t duration1 :15;
uint32_t level1 :1;
};
uint32_t val;
};
} rmt_item32_t;
//Allow access to RMT memory using RMTMEM.chan[0].data32[8]
typedef volatile struct rmt_mem_s {
struct {
rmt_item32_t data32[64];
} chan[8];
} rmt_mem_t;
extern rmt_mem_t RMTMEM;
IsrCore SmartLed::_interruptCore = CoreCurrent; IsrCore SmartLed::_interruptCore = CoreCurrent;
intr_handle_t SmartLed::_interruptHandle = NULL;
SmartLed*& IRAM_ATTR SmartLed::ledForChannel(int channel) { SmartLed*& IRAM_ATTR SmartLed::ledForChannel(int channel) {
static SmartLed* table[8] = { nullptr }; static SmartLed* table[detail::CHANNEL_COUNT] = {};
assert( channel < 8 ); assert(channel < detail::CHANNEL_COUNT);
return table[channel]; 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;
}
}

View File

@@ -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 #pragma once
#define SMARTLEDS_H
/* /*
* A C++ driver for the WS2812 LEDs using the RMT peripheral on the ESP32. * A C++ driver for the WS2812 LEDs using the RMT peripheral on the ESP32.
@@ -31,164 +54,75 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <memory>
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include <memory>
#include "esp_idf_version.h" #include <driver/gpio.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 <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_intr_alloc.h>
#include <esp_ipc.h> #include <esp_ipc.h>
#include <driver/gpio.h>
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/semphr.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 "Color.h" #include "Color.h"
namespace detail { #include "RmtDriver.h"
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
using LedType = detail::TimingParams; using LedType = detail::TimingParams;
// 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 }; static const LedType LED_WS2812 = { 350, 700, 800, 600, 50000 };
static const LedType LED_WS2812B = { 400, 850, 850, 400, 50100 }; // 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_SK6812 = { 300, 600, 900, 600, 80000 };
static const LedType LED_WS2813 = { 350, 800, 350, 350, 300000 }; 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 BufferType { SingleBuffer = 0, DoubleBuffer };
enum IsrCore { CoreFirst = 0, CoreSecond = 1, CoreCurrent = 2 }; enum IsrCore { CoreFirst = 0, CoreSecond = 1, CoreCurrent = 2 };
class SmartLed { class SmartLed {
public: public:
friend class detail::RmtDriver;
// The RMT interrupt must not run on the same core as WiFi interrupts, otherwise SmartLeds // 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. // can't fill the RMT buffer fast enough, resulting in rendering artifacts.
// Usually, that means you have to set isrCore == CoreSecond. // Usually, that means you have to set isrCore == CoreSecond.
// //
// If you use anything other than CoreCurrent, the FreeRTOS scheduler MUST be already running, // 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. // 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 ), // Does nothing on chips that only have one core.
_channel( channel ), SmartLed(const LedType& type, int count, int pin, int channel = 0, BufferType doubleBuffer = DoubleBuffer,
_count( count ), IsrCore isrCore = CoreCurrent)
_firstBuffer( new Rgb[ count ] ), : _finishedFlag(xSemaphoreCreateBinary())
_secondBuffer( doubleBuffer ? new Rgb[ count ] : nullptr ), , _driver(type, count, pin, channel, _finishedFlag)
_finishedFlag( xSemaphoreCreateBinary() ) , _channel(channel)
{ , _count(count)
assert( channel >= 0 && channel < 8 ); , _firstBuffer(new Rgb[count])
, _secondBuffer(doubleBuffer ? new Rgb[count] : nullptr) {
assert(channel >= 0 && channel < detail::CHANNEL_COUNT);
assert(ledForChannel(channel) == nullptr); assert(ledForChannel(channel) == nullptr);
xSemaphoreGive(_finishedFlag); xSemaphoreGive(_finishedFlag);
DPORT_SET_PERI_REG_MASK( DPORT_PERIP_CLK_EN_REG, DPORT_RMT_CLK_EN ); _driver.init();
DPORT_CLEAR_PERI_REG_MASK( DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST );
PIN_FUNC_SELECT( GPIO_PIN_MUX_REG[ pin ], 2 ); #if !defined(SOC_CPU_CORES_NUM) || SOC_CPU_CORES_NUM > 1
gpio_set_direction( static_cast< gpio_num_t >( pin ), GPIO_MODE_OUTPUT ); if (!anyAlive() && isrCore != CoreCurrent) {
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() ) {
_interruptCore = isrCore; _interruptCore = isrCore;
if(isrCore != CoreCurrent) { ESP_ERROR_CHECK(esp_ipc_call_blocking(isrCore, registerInterrupt, (void*)this));
ESP_ERROR_CHECK(esp_ipc_call_blocking(isrCore, registerInterrupt, NULL)); } else
} else { #endif
registerInterrupt(NULL); {
} registerInterrupt((void*)this);
} }
ledForChannel(channel) = this; ledForChannel(channel) = this;
@@ -196,28 +130,25 @@ public:
~SmartLed() { ~SmartLed() {
ledForChannel(_channel) = nullptr; ledForChannel(_channel) = nullptr;
if ( !anyAlive() ) { #if !defined(SOC_CPU_CORES_NUM) || SOC_CPU_CORES_NUM > 1
if(_interruptCore != CoreCurrent) { if (!anyAlive() && _interruptCore != CoreCurrent) {
ESP_ERROR_CHECK(esp_ipc_call_blocking(_interruptCore, unregisterInterrupt, NULL)); ESP_ERROR_CHECK(esp_ipc_call_blocking(_interruptCore, unregisterInterrupt, (void*)this));
} else { } else
unregisterInterrupt(NULL); #endif
} {
unregisterInterrupt((void*)this);
} }
vSemaphoreDelete(_finishedFlag); vSemaphoreDelete(_finishedFlag);
} }
Rgb& operator[]( int idx ) { Rgb& operator[](int idx) { return _firstBuffer[idx]; }
return _firstBuffer[ idx ];
}
const Rgb& operator[]( int idx ) const { const Rgb& operator[](int idx) const { return _firstBuffer[idx]; }
return _firstBuffer[ idx ];
}
void show() { esp_err_t show() {
_buffer = _firstBuffer.get(); esp_err_t err = startTransmission();
startTransmission();
swapBuffers(); swapBuffers();
return err;
} }
bool wait(TickType_t timeout = portMAX_DELAY) { bool wait(TickType_t timeout = portMAX_DELAY) {
@@ -228,9 +159,8 @@ public:
return false; return false;
} }
int size() const { int size() const { return _count; }
return _count; int channel() const { return _channel; }
}
Rgb* begin() { return _firstBuffer.get(); } Rgb* begin() { return _firstBuffer.get(); }
const Rgb* begin() const { return _firstBuffer.get(); } const Rgb* begin() const { return _firstBuffer.get(); }
@@ -241,85 +171,69 @@ public:
const Rgb* cend() const { return _firstBuffer.get() + _count; } const Rgb* cend() const { return _firstBuffer.get() + _count; }
private: private:
static intr_handle_t _interruptHandle;
static IsrCore _interruptCore; static IsrCore _interruptCore;
static void initChannel( int channel ) { static void registerInterrupt(void* selfVoid) {
RMT.apb_conf.fifo_mask = 1; //enable memory access, instead of FIFO mode. auto* self = (SmartLed*)selfVoid;
RMT.apb_conf.mem_tx_wrap_en = 1; //wrap around when hitting end of buffer ESP_ERROR_CHECK(self->_driver.registerIsr(!anyAlive()));
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 *) { static void unregisterInterrupt(void* selfVoid) {
ESP_ERROR_CHECK(esp_intr_alloc( ETS_RMT_INTR_SOURCE, 0, interruptHandler, nullptr, &_interruptHandle)); auto* self = (SmartLed*)selfVoid;
} ESP_ERROR_CHECK(self->_driver.unregisterIsr());
static void unregisterInterrupt(void*) {
esp_intr_free( _interruptHandle );
} }
static SmartLed*& IRAM_ATTR ledForChannel(int channel); static SmartLed*& IRAM_ATTR ledForChannel(int channel);
static void IRAM_ATTR interruptHandler( void* );
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() { void swapBuffers() {
if (_secondBuffer) if (_secondBuffer)
_firstBuffer.swap(_secondBuffer); _firstBuffer.swap(_secondBuffer);
} }
void startTransmission() { esp_err_t startTransmission() {
// Invalid use of the library // Invalid use of the library, you must wait() fir previous frame to get processed first
if (xSemaphoreTake(_finishedFlag, 0) != pdTRUE) if (xSemaphoreTake(_finishedFlag, 0) != pdTRUE)
abort(); abort();
_pixelPosition = _componentPosition = _halfIdx = 0; auto err = _driver.transmit(_firstBuffer.get());
copyRmtHalfBlock(); if (err != ESP_OK) {
if ( _pixelPosition < _count ) return err;
copyRmtHalfBlock();
RMT.conf_ch[ _channel ].conf1.mem_rd_rst = 1;
RMT.conf_ch[ _channel ].conf1.tx_start = 1;
} }
static bool anyAlive() { return ESP_OK;
for ( int i = 0; i != 8; i++ )
if ( ledForChannel( i ) != nullptr ) return true;
return false;
} }
const LedType& _timing; SemaphoreHandle_t _finishedFlag;
detail::RmtDriver _driver;
int _channel; int _channel;
detail::RmtPulsePair _bitToRmt[ 2 ];
int _count; int _count;
std::unique_ptr<Rgb[]> _firstBuffer; std::unique_ptr<Rgb[]> _firstBuffer;
std::unique_ptr<Rgb[]> _secondBuffer; std::unique_ptr<Rgb[]> _secondBuffer;
Rgb *_buffer;
xSemaphoreHandle _finishedFlag;
int _pixelPosition;
int _componentPosition;
int _halfIdx;
}; };
#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 { class Apa102 {
public: public:
struct ApaRgb { struct ApaRgb {
ApaRgb(uint8_t r = 0, uint8_t g = 0, uint32_t b = 0, uint32_t v = 0xFF) 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 ) : v(0xE0 | v)
{} , b(b)
, g(g)
, r(r) {}
ApaRgb& operator=(const Rgb& o) { ApaRgb& operator=(const Rgb& o) {
r = o.r; r = o.r;
@@ -339,12 +253,12 @@ public:
static const int FINAL_FRAME_SIZE = 4; static const int FINAL_FRAME_SIZE = 4;
static const int TRANS_COUNT = 2 + 8; static const int TRANS_COUNT = 2 + 8;
Apa102( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer ) Apa102(int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, int clock_speed_hz = 1000000)
: _count( count ), : _count(count)
_firstBuffer( new ApaRgb[ count ] ), , _firstBuffer(new ApaRgb[count])
_secondBuffer( doubleBuffer ? new ApaRgb[ count ] : nullptr ), , _secondBuffer(doubleBuffer ? new ApaRgb[count] : nullptr)
_initFrame( 0 ) , _transCount(0)
{ , _initFrame(0) {
spi_bus_config_t buscfg; spi_bus_config_t buscfg;
memset(&buscfg, 0, sizeof(buscfg)); memset(&buscfg, 0, sizeof(buscfg));
buscfg.mosi_io_num = datapin; buscfg.mosi_io_num = datapin;
@@ -356,16 +270,16 @@ public:
spi_device_interface_config_t devcfg; spi_device_interface_config_t devcfg;
memset(&devcfg, 0, sizeof(devcfg)); memset(&devcfg, 0, sizeof(devcfg));
devcfg.clock_speed_hz = 1000000; devcfg.clock_speed_hz = clock_speed_hz;
devcfg.mode = 0; devcfg.mode = 0;
devcfg.spics_io_num = -1; devcfg.spics_io_num = -1;
devcfg.queue_size = TRANS_COUNT; devcfg.queue_size = TRANS_COUNT;
devcfg.pre_cb = nullptr; devcfg.pre_cb = nullptr;
auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 ); auto ret = spi_bus_initialize(_SMARTLEDS_SPI_HOST, &buscfg, _SMARTLEDS_SPI_DMA_CHAN);
assert(ret == ESP_OK); assert(ret == ESP_OK);
ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi ); ret = spi_bus_add_device(_SMARTLEDS_SPI_HOST, &devcfg, &_spi);
assert(ret == ESP_OK); assert(ret == ESP_OK);
std::fill_n(_finalFrame, FINAL_FRAME_SIZE, 0xFFFFFFFF); std::fill_n(_finalFrame, FINAL_FRAME_SIZE, 0xFFFFFFFF);
@@ -375,13 +289,9 @@ public:
// ToDo // ToDo
} }
ApaRgb& operator[]( int idx ) { ApaRgb& operator[](int idx) { return _firstBuffer[idx]; }
return _firstBuffer[ idx ];
}
const ApaRgb& operator[]( int idx ) const { const ApaRgb& operator[](int idx) const { return _firstBuffer[idx]; }
return _firstBuffer[ idx ];
}
void show() { void show() {
_buffer = _firstBuffer.get(); _buffer = _firstBuffer.get();
@@ -395,6 +305,7 @@ public:
spi_device_get_trans_result(_spi, &t, portMAX_DELAY); spi_device_get_trans_result(_spi, &t, portMAX_DELAY);
} }
} }
private: private:
void swapBuffers() { void swapBuffers() {
if (_secondBuffer) if (_secondBuffer)
@@ -444,9 +355,9 @@ public:
struct LDP8806_GRB { struct LDP8806_GRB {
LDP8806_GRB(uint8_t g_7bit = 0, uint8_t r_7bit = 0, uint32_t b_7bit = 0) 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 ) : 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 //Convert 8->7bit colour
@@ -468,13 +379,14 @@ public:
static const int LATCH_FRAME_SIZE_BYTES = 3; static const int LATCH_FRAME_SIZE_BYTES = 3;
static const int TRANS_COUNT_MAX = 20; //Arbitrary, supports up to 600 LED 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 ) LDP8806(
: _count( count ), int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, uint32_t clock_speed_hz = 2000000)
_firstBuffer( new LDP8806_GRB[ count ] ), : _count(count)
_secondBuffer( doubleBuffer ? new LDP8806_GRB[ count ] : nullptr ), , _firstBuffer(new LDP8806_GRB[count])
, _secondBuffer(doubleBuffer ? new LDP8806_GRB[count] : nullptr)
,
// one 'latch'/start-of-data mark frame for every 32 leds // one 'latch'/start-of-data mark frame for every 32 leds
_latchFrames( ( count + 31 ) / 32 ) _latchFrames((count + 31) / 32) {
{
spi_bus_config_t buscfg; spi_bus_config_t buscfg;
memset(&buscfg, 0, sizeof(buscfg)); memset(&buscfg, 0, sizeof(buscfg));
buscfg.mosi_io_num = datapin; buscfg.mosi_io_num = datapin;
@@ -492,10 +404,10 @@ public:
devcfg.queue_size = TRANS_COUNT_MAX; devcfg.queue_size = TRANS_COUNT_MAX;
devcfg.pre_cb = nullptr; devcfg.pre_cb = nullptr;
auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 ); auto ret = spi_bus_initialize(_SMARTLEDS_SPI_HOST, &buscfg, _SMARTLEDS_SPI_DMA_CHAN);
assert(ret == ESP_OK); assert(ret == ESP_OK);
ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi ); ret = spi_bus_add_device(_SMARTLEDS_SPI_HOST, &devcfg, &_spi);
assert(ret == ESP_OK); assert(ret == ESP_OK);
std::fill_n(_latchBuffer, LATCH_FRAME_SIZE_BYTES, 0x0); std::fill_n(_latchBuffer, LATCH_FRAME_SIZE_BYTES, 0x0);
@@ -505,13 +417,9 @@ public:
// noop // noop
} }
LDP8806_GRB& operator[]( int idx ) { LDP8806_GRB& operator[](int idx) { return _firstBuffer[idx]; }
return _firstBuffer[ idx ];
}
const LDP8806_GRB& operator[]( int idx ) const { const LDP8806_GRB& operator[](int idx) const { return _firstBuffer[idx]; }
return _firstBuffer[ idx ];
}
void show() { void show() {
_buffer = _firstBuffer.get(); _buffer = _firstBuffer.get();
@@ -525,6 +433,7 @@ public:
spi_device_get_trans_result(_spi, &t, portMAX_DELAY); spi_device_get_trans_result(_spi, &t, portMAX_DELAY);
} }
} }
private: private:
void swapBuffers() { void swapBuffers() {
if (_secondBuffer) if (_secondBuffer)
@@ -566,5 +475,3 @@ private:
int _latchFrames; int _latchFrames;
uint8_t _latchBuffer[LATCH_FRAME_SIZE_BYTES]; uint8_t _latchBuffer[LATCH_FRAME_SIZE_BYTES];
}; };
#endif //SMARTLEDS_H

View File

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

View File

@@ -98,7 +98,7 @@ static camera_config_t camera_config = {
.pixel_format = PIXFORMAT_JPEG, // YUV422,GRAYSCALE,RGB565,JPEG .pixel_format = PIXFORMAT_JPEG, // YUV422,GRAYSCALE,RGB565,JPEG
.frame_size = FRAMESIZE_VGA, // QQVGA-UXGA Do not use sizes above QVGA when not JPEG .frame_size = FRAMESIZE_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 // .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_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 */ .fb_location = CAMERA_FB_IN_PSRAM, /*!< The location where the frame buffer will be allocated */
.grab_mode = CAMERA_GRAB_LATEST, // only from new esp32cam version .grab_mode = CAMERA_GRAB_LATEST, // only from new esp32cam version

View File

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

View File

@@ -46,6 +46,7 @@ extern "C" {
#include "Helper.h" #include "Helper.h"
#include "miniz.h" #include "miniz.h"
#include "basic_auth.h"
static const char *TAG = "OTA FILE"; 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 = { httpd_uri_t file_download = {
.uri = "/fileserver*", // Match all URIs of type /path/to/file .uri = "/fileserver*", // Match all URIs of type /path/to/file
.method = HTTP_GET, .method = HTTP_GET,
.handler = download_get_handler, .handler = APPLY_BASIC_AUTH_FILTER(download_get_handler),
.user_ctx = server_data // Pass server data as context .user_ctx = server_data // Pass server data as context
}; };
httpd_register_uri_handler(server, &file_download); 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 = { httpd_uri_t file_datafileact = {
.uri = "/datafileact", // Match all URIs of type /path/to/file .uri = "/datafileact", // Match all URIs of type /path/to/file
.method = HTTP_GET, .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 .user_ctx = server_data // Pass server data as context
}; };
httpd_register_uri_handler(server, &file_datafileact); 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 = { httpd_uri_t file_datafile_last_part_handle = {
.uri = "/data", // Match all URIs of type /path/to/file .uri = "/data", // Match all URIs of type /path/to/file
.method = HTTP_GET, .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 .user_ctx = server_data // Pass server data as context
}; };
httpd_register_uri_handler(server, &file_datafile_last_part_handle); 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 = { httpd_uri_t file_logfileact = {
.uri = "/logfileact", // Match all URIs of type /path/to/file .uri = "/logfileact", // Match all URIs of type /path/to/file
.method = HTTP_GET, .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 .user_ctx = server_data // Pass server data as context
}; };
httpd_register_uri_handler(server, &file_logfileact); 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 = { httpd_uri_t file_logfile_last_part_handle = {
.uri = "/log", // Match all URIs of type /path/to/file .uri = "/log", // Match all URIs of type /path/to/file
.method = HTTP_GET, .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 .user_ctx = server_data // Pass server data as context
}; };
httpd_register_uri_handler(server, &file_logfile_last_part_handle); 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 = { httpd_uri_t file_upload = {
.uri = "/upload/*", // Match all URIs of type /upload/path/to/file .uri = "/upload/*", // Match all URIs of type /upload/path/to/file
.method = HTTP_POST, .method = HTTP_POST,
.handler = upload_post_handler, .handler = APPLY_BASIC_AUTH_FILTER(upload_post_handler),
.user_ctx = server_data // Pass server data as context .user_ctx = server_data // Pass server data as context
}; };
httpd_register_uri_handler(server, &file_upload); 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 = { httpd_uri_t file_delete = {
.uri = "/delete/*", // Match all URIs of type /delete/path/to/file .uri = "/delete/*", // Match all URIs of type /delete/path/to/file
.method = HTTP_POST, .method = HTTP_POST,
.handler = delete_post_handler, .handler = APPLY_BASIC_AUTH_FILTER(delete_post_handler),
.user_ctx = server_data // Pass server data as context .user_ctx = server_data // Pass server data as context
}; };
httpd_register_uri_handler(server, &file_delete); httpd_register_uri_handler(server, &file_delete);

View File

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

View File

@@ -65,11 +65,11 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
if (CNNType == Digit) { if (CNNType == Digit) {
for (int i = 0; i < GENERAL[_analog]->ROI.size(); ++i) { for (int i = 0; i < GENERAL[_analog]->ROI.size(); ++i) {
if (GENERAL[_analog]->ROI[i]->result_klasse >= 10) { if ((GENERAL[_analog]->ROI[i]->result_klasse >= 0) && (GENERAL[_analog]->ROI[i]->result_klasse < 10)) {
result = result + "N"; result = result + std::to_string(GENERAL[_analog]->ROI[i]->result_klasse);
} }
else { else {
result = result + std::to_string(GENERAL[_analog]->ROI[i]->result_klasse); result = result + "N";
} }
} }
return result; return result;
@@ -78,7 +78,7 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
if ((CNNType == DoubleHyprid10) || (CNNType == Digit100)) { if ((CNNType == DoubleHyprid10) || (CNNType == Digit100)) {
float number = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float; float number = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float;
// NaN? // NaN?
if (number >= 0) { if ((number >= 0) && (number < 10)) {
// is only set if it is the first digit (no analogue before!) // is only set if it is the first digit (no analogue before!)
if (_extendedResolution) { if (_extendedResolution) {
int result_after_decimal_point = ((int) floor(number * 10)) % 10; int result_after_decimal_point = ((int) floor(number * 10)) % 10;
@@ -95,9 +95,16 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
else { else {
prev = PointerEvalHybridNew(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, prev, prev); prev = PointerEvalHybridNew(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, prev, 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); result = std::to_string(prev);
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(dig100) prev=" + std::to_string(prev)); LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(dig100) prev=" + std::to_string(prev));
} }
else {
result = "N";
}
}
} }
else { else {
result = "N"; result = "N";
@@ -107,7 +114,7 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
} }
for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i) { 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); 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)); LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout#PointerEvalHybridNew()= " + std::to_string(prev));
result = std::to_string(prev) + result; result = std::to_string(prev) + result;
@@ -117,7 +124,6 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
prev = -1; prev = -1;
result = "N" + result; 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)); LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(result_float<0 /'N') result_float=" + std::to_string(GENERAL[_analog]->ROI[i]->result_float));
} }
} }
return result; 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 // 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 // we use the recognition as given. The result is the int value of the recognition
// add precisition of 2 digits and round before trunc // 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; result = (int) ((int) trunc(round((number+10 % 10)*100)) ) / 100;
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - No predecessor - Result = " + std::to_string(result) + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - No predecessor - Result = " + std::to_string(result) +

View File

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

View File

@@ -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()); // 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); 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; InfluxDBenable = true;
} else { } else {
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDB init skipped as we are missing some parameters"); 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) if (result.length() > 0)
InfluxDBPublish(measurement, namenumber, result, timeutc); //////////////////////// NEW //////////////////////////
// InfluxDBPublish(measurement, namenumber, result, timeutc);
influxDB.InfluxDBPublish(measurement, namenumber, result, timeutc);
//////////////////////// NEW //////////////////////////
} }
} }

View File

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

View File

@@ -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: *****"); LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init InfluxDB with uri: " + uri + ", org: " + dborg + ", token: *****");
// printf("vor V2 Init\n"); // 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"); // printf("nach V2 Init\n");
InfluxDBenable = true; InfluxDBenable = true;
} else { } 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()); 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) if (result.length() > 0)
InfluxDB_V2_Publish(measurement, namenumber, result, resulttimeutc); influxdb.InfluxDBPublish(measurement, namenumber, result, resulttimeutc);
// InfluxDB_V2_Publish(measurement, namenumber, result, resulttimeutc);
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,95 +8,35 @@
#include "time_sntp.h" #include "time_sntp.h"
#include "../../include/defines.h" #include "../../include/defines.h"
static const char *TAG = "INFLUXDB"; static const char *TAG = "INFLUXDB";
std::string _influxDBURI; /**
std::string _influxDBDatabase; * @brief Buffer to store the HTTP response.
std::string _influxDBUser; *
std::string _influxDBPassword; * 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.
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}; 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 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) static esp_err_t http_event_handler(esp_http_client_event_t *evt)
{ {
switch(evt->event_id) switch(evt->event_id)
@@ -130,25 +70,123 @@ static esp_err_t http_event_handler(esp_http_client_event_t *evt)
return ESP_OK; 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(); * @brief Initializes the InfluxDB connection with version 1 settings.
http_config.auth_type = HTTP_AUTH_TYPE_BASIC; *
* 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; std::string payload;
char nowTimestamp[21]; char nowTimestamp[21];
connectHTTP();
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBPublish - Key: " + _key + ", Content: " + _content + ", timeUTC: " + std::to_string(_timeUTC)); LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBPublish - Key: " + _key + ", Content: " + _content + ", timeUTC: " + std::to_string(_timeUTC));
if (_timeUTC > 0) if (_timeUTC > 0)
@@ -164,50 +202,47 @@ void InfluxDBPublish(std::string _measurement, std::string _key, std::string _co
} }
payload.shrink_to_fit(); payload.shrink_to_fit();
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload);
esp_err_t err;
// use the default retention policy of the bucket switch (version) {
std::string apiURI = _influxDBURI + "/write?db=" + _influxDBDatabase; case INFLUXDB_V1:
// std::string apiURI = _influxDBURI + "/api/v2/write?bucket=" + _influxDBDatabase + "/"; apiURI = influxDBURI + "/write?db=" + database;
apiURI.shrink_to_fit(); apiURI.shrink_to_fit();
http_config.url = apiURI.c_str();
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "API URI: " + apiURI); esp_http_client_set_url(httpClient, apiURI.c_str());
esp_http_client_set_method(httpClient, HTTP_METHOD_POST);
esp_http_client_handle_t http_client = esp_http_client_init(&http_config); esp_http_client_set_header(httpClient, "Content-Type", "text/plain");
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "client is initialized"); 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) { if (err == ESP_OK) {
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request was performed"); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Data published successfully: " + payload);
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 { } else {
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request failed"); LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to publish data: " + std::string(esp_err_to_name(err)));
} }
esp_http_client_cleanup(http_client); break;
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;
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 #endif //ENABLE_INFLUXDB

View File

@@ -8,17 +8,106 @@
#include <map> #include <map>
#include <functional> #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 #include <string>
void InfluxDB_V2_Init(std::string _uri, std::string _bucket, std::string _org, std::string _token); #include "esp_http_client.h"
void InfluxDB_V2_Publish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC); #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(); void InfluxDBdestroy();
// Publish data to the InfluxDB server
void InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC);
};
#endif //INTERFACE_INFLUXDB_H #endif //INTERFACE_INFLUXDB_H
#endif //ENABLE_INFLUXDB #endif //ENABLE_INFLUXDB

View File

@@ -12,6 +12,7 @@
#include "interface_mqtt.h" #include "interface_mqtt.h"
#include "time_sntp.h" #include "time_sntp.h"
#include "../../include/defines.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.method = HTTP_GET;
uri.uri = "/mqtt_publish_discovery"; 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*) ""; uri.user_ctx = (void*) "";
httpd_register_uri_handler(server, &uri); httpd_register_uri_handler(server, &uri);
} }

View File

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

View File

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

View File

@@ -49,6 +49,9 @@
#define ets_delay_us(a) esp_rom_delay_us(a) #define ets_delay_us(a) esp_rom_delay_us(a)
#endif #endif
#include "../esp-protocols/components/mdns/include/mdns.h"
static const char *TAG = "WIFI"; static const char *TAG = "WIFI";
static bool APWithBetterRSSI = false; static bool APWithBetterRSSI = false;
@@ -657,6 +660,14 @@ esp_err_t wifi_init_sta(void)
else { else {
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Set hostname to: " + wlan_config.hostname); 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"); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Init successful");

View File

@@ -145,6 +145,29 @@ int LoadWlanFromFile(std::string fn)
wlan_config.dns = tmp; wlan_config.dns = tmp;
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "DNS: " + wlan_config.dns); 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)) #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")){ else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "RSSITHRESHOLD")){
tmp = trim(splitted[1]); tmp = trim(splitted[1]);

View File

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

View File

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

View File

@@ -33,6 +33,8 @@
#include "configFile.h" #include "configFile.h"
#include "server_main.h" #include "server_main.h"
#include "server_camera.h" #include "server_camera.h"
#include "basic_auth.h"
#ifdef ENABLE_MQTT #ifdef ENABLE_MQTT
#include "server_mqtt.h" #include "server_mqtt.h"
#endif //ENABLE_MQTT #endif //ENABLE_MQTT
@@ -107,27 +109,51 @@ bool Init_NVS_SDCard()
sdmmc_host_t host = SDMMC_HOST_DEFAULT(); sdmmc_host_t host = SDMMC_HOST_DEFAULT();
host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; 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. // 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. // 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(); sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
#endif
// Set bus width to use: // Set bus width to use:
#ifdef __SD_USE_ONE_LINE_MODE__ #ifdef __SD_USE_ONE_LINE_MODE__
slot_config.width = 1; 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.clk = GPIO_SDCARD_CLK;
slot_config.cmd = GPIO_SDCARD_CMD; slot_config.cmd = GPIO_SDCARD_CMD;
slot_config.d0 = GPIO_SDCARD_D0; slot_config.d0 = GPIO_SDCARD_D0;
#endif #endif // end CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
#else // else __SD_USE_ONE_LINE_MODE__
#else
slot_config.width = 4; 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.d1 = GPIO_SDCARD_D1;
slot_config.d2 = GPIO_SDCARD_D2; slot_config.d2 = GPIO_SDCARD_D2;
slot_config.d3 = GPIO_SDCARD_D3; slot_config.d3 = GPIO_SDCARD_D3;
#endif #endif // end CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
#endif #endif // end __SD_USE_ONE_LINE_MODE__
// Enable internal pullups on enabled pins. The internal pullups // Enable internal pullups on enabled pins. The internal pullups
// are insufficient however, please make sure 10k external pullups are // 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); StatusLED(WLAN_INIT, 3, true);
return; return;
} }
init_basic_auth();
} }
else if (iWLANStatus == -1) { // wlan.ini not available, potentially empty or content not readable else if (iWLANStatus == -1) { // wlan.ini not available, potentially empty or content not readable
StatusLED(WLAN_INIT, 1, true); StatusLED(WLAN_INIT, 1, true);

View File

@@ -17,6 +17,7 @@
#include "MainFlowControl.h" #include "MainFlowControl.h"
#include "esp_log.h" #include "esp_log.h"
#include "basic_auth.h"
#include "esp_chip_info.h" #include "esp_chip_info.h"
#include <stdio.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 = { httpd_uri_t info_get_handle = {
.uri = "/info", // Match all URIs of type /path/to/file .uri = "/info", // Match all URIs of type /path/to/file
.method = HTTP_GET, .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 .user_ctx = (void*) base_path // Pass server data as context
}; };
httpd_register_uri_handler(server, &info_get_handle); 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 = { httpd_uri_t sysinfo_handle = {
.uri = "/sysinfo", // Match all URIs of type /path/to/file .uri = "/sysinfo", // Match all URIs of type /path/to/file
.method = HTTP_GET, .method = HTTP_GET,
.handler = sysinfo_handler, .handler = APPLY_BASIC_AUTH_FILTER(sysinfo_handler),
.user_ctx = (void*) base_path // Pass server data as context .user_ctx = (void*) base_path // Pass server data as context
}; };
httpd_register_uri_handler(server, &sysinfo_handle); 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 = { httpd_uri_t starttime_tmp_handle = {
.uri = "/starttime", // Match all URIs of type /path/to/file .uri = "/starttime", // Match all URIs of type /path/to/file
.method = HTTP_GET, .method = HTTP_GET,
.handler = starttime_get_handler, .handler = APPLY_BASIC_AUTH_FILTER(starttime_get_handler),
.user_ctx = NULL // Pass server data as context .user_ctx = NULL // Pass server data as context
}; };
httpd_register_uri_handler(server, &starttime_tmp_handle); 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 = { httpd_uri_t img_tmp_handle = {
.uri = "/img_tmp/*", // Match all URIs of type /path/to/file .uri = "/img_tmp/*", // Match all URIs of type /path/to/file
.method = HTTP_GET, .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 .user_ctx = (void*) base_path // Pass server data as context
}; };
httpd_register_uri_handler(server, &img_tmp_handle); 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 = { httpd_uri_t main_rest_handle = {
.uri = "/*", // Match all URIs of type /path/to/file .uri = "/*", // Match all URIs of type /path/to/file
.method = HTTP_GET, .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 .user_ctx = (void*) base_path // Pass server data as context
}; };
httpd_register_uri_handler(server, &main_rest_handle); httpd_register_uri_handler(server, &main_rest_handle);

View File

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

View File

@@ -133,7 +133,25 @@ CONFIG_MQTT_USE_CUSTOM_CONFIG=y
CONFIG_MBEDTLS_HAVE_TIME=y CONFIG_MBEDTLS_HAVE_TIME=y
CONFIG_MBEDTLS_HAVE_TIME_DATE=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_CORE0=n
CONFIG_CAMERA_CORE1=y CONFIG_CAMERA_CORE1=y
@@ -162,6 +180,7 @@ CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=4864
#CONFIG_FREERTOS_USE_TRACE_FACILITY=1 #CONFIG_FREERTOS_USE_TRACE_FACILITY=1
#CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y #CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
#CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=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] #force disable HIMEM as not used in default config, can be enabled with [env:esp32cam-dev-himem]
#free 256kb of internal memory : #free 256kb of internal memory :

View File

@@ -52,6 +52,13 @@
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> 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> </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> <hr>
<p><esp-web-install-button manifest="manifest.json"></esp-web-install-button></p> <p><esp-web-install-button manifest="manifest.json"></esp-web-install-button></p>

View File

@@ -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). 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. 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 - Smallest ROI provides value for `0.000'x` (Eg. a water meter with 4 pointers behind the decimal point)
ChangeRateThreshold = 2 - ChangeRateThreshold = 2
Extended Resolution disabled: #### With `Extended Resolution` **disabled**
PreValue: 123.456'7 >>> Threshold = +/- 0.000'2 PreValue: `123.456'7` -> Threshold = `+/-0.000'2`.<br>
Comparative value >>> max = 123.456'9 and min = 123.456'5 All changes between `123.456'5` and `123.456'9` get ignored
Extended Resolution enabled: #### With `Extended Resolution` **enabled**
PreValue: 123.456'78 >>> Threshold = +/- 0.000'02 PreValue: `123.456'78` -> Threshold = `+/-0.000'02`.<br>
Comparative value >>> max = 123.456'80 and min = 123.456'76 All changes between `123.456'76` and `123.456'80` get ignored.
![](img/ChangeRateThreshold.png) ![](img/ChangeRateThreshold.png)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -36,3 +36,14 @@ password = ""
; Default: 0 = Disable client requested roaming query ; Default: 0 = Disable client requested roaming query
RSSIThreshold = 0 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"