Compare commits

..

13 Commits

Author SHA1 Message Date
jomjol
18f6e83a2c v8.4.0 2021-09-25 18:57:40 +02:00
jomjol
147d97421b Merge branch 'rolling' 2021-09-25 18:53:47 +02:00
jomjol
dcf2feb7aa v8.4.0 2021-09-25 18:53:14 +02:00
jomjol
e63e940b96 v8.4.0 2021-09-25 08:08:21 +02:00
jomjol
68b0fb83ee v8.4.0 2021-09-24 19:57:48 +02:00
jomjol
f15e5f060a v8.4.0 2021-09-23 18:43:53 +02:00
jomjol
e2a403441f Rolling 20210922 2021-09-22 22:13:08 +02:00
jomjol
9b3665b9c6 Rolling 20210921 v2 2021-09-21 19:41:20 +02:00
jomjol
f4c8bf9206 Rolling 20210921 2021-09-21 18:49:32 +02:00
jomjol
c033db9c31 Rolling 20210921 2021-09-21 07:27:46 +02:00
jomjol
9300526f49 Rolling 2021-09-20 2021-09-20 21:18:34 +02:00
jomjol
b6dd1f7f2d Update 2021-09-14 20:00:45 +02:00
jomjol
1e6eddca04 Rolling 20210913 2021-09-13 20:05:54 +02:00
73 changed files with 4163 additions and 470 deletions

View File

@@ -11,6 +11,12 @@
____
#### #11 MQTT - configurable payload
* https://github.com/jomjol/AI-on-the-edge-device/issues/344
#### #10 Improve and bug fix logging of images
* https://github.com/jomjol/AI-on-the-edge-device/issues/307

21
LICENSE
View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2020 jomjol
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.

View File

@@ -47,6 +47,30 @@ In other cases you can contact the developer via email: <img src="https://raw.gi
##### 8.4.0 - Multi Meter Support (2021-09-25)
* License change (remove MIT license, remark see below)
* html: show hostname in title and main page
* configuration:
* moved setting `ExtendedResolution` to individual number settings
* New parameter `IgnoreLeadingNaN` (delete leading NaN's specifically)
* **ATTENTION**: update of the `config.ini` needed (open, adjust `ExtendedResolution`, save)
* Bug fixing (html, images of recognized numbers)
### **ATTENTION: LICENSE CHANGE - removal of MIT License.**
- Currently no licence published - copyright belongs to author
- If you are interested in a commercial usage or dedicated versions please contact the developer
- no limits to private usage
##### 8.3.0 - Multi Meter Support (2021-09-12)
* Upgrade digital CNN to v12.1.0 (added new images)

63
code/SmartLeds.cpp Normal file
View File

@@ -0,0 +1,63 @@
#include "SmartLeds.h"
IsrCore SmartLed::_interruptCore = CoreCurrent;
intr_handle_t SmartLed::_interruptHandle = NULL;
SmartLed*& IRAM_ATTR SmartLed::ledForChannel( int channel ) {
static SmartLed* table[8] = { nullptr };
assert( channel < 8 );
return table[ channel ];
}
void IRAM_ATTR SmartLed::interruptHandler(void*) {
for (int channel = 0; channel != 8; channel++) {
auto self = ledForChannel( channel );
if ( RMT.int_st.val & (1 << (24 + channel ) ) ) { // tx_thr_event
if ( self )
self->copyRmtHalfBlock();
RMT.int_clr.val |= 1 << ( 24 + channel );
} else if ( RMT.int_st.val & ( 1 << (3 * channel ) ) ) { // tx_end
if ( self )
xSemaphoreGiveFromISR( self->_finishedFlag, nullptr );
RMT.int_clr.val |= 1 << ( 3 * channel );
}
}
}
void IRAM_ATTR SmartLed::copyRmtHalfBlock() {
int offset = detail::MAX_PULSES * _halfIdx;
_halfIdx = !_halfIdx;
int len = 3 - _componentPosition + 3 * ( _count - 1 );
len = std::min( len, detail::MAX_PULSES / 8 );
if ( !len ) {
for ( int i = 0; i < detail::MAX_PULSES; i++) {
RMTMEM.chan[ _channel].data32[i + offset ].val = 0;
}
}
int i;
for ( i = 0; i != len && _pixelPosition != _count; i++ ) {
uint8_t val = _buffer[ _pixelPosition ].getGrb( _componentPosition );
for ( int j = 0; j != 8; j++, val <<= 1 ) {
int bit = val >> 7;
int idx = i * 8 + offset + j;
RMTMEM.chan[ _channel ].data32[ idx ].val = _bitToRmt[ bit & 0x01 ].value;
}
if ( _pixelPosition == _count - 1 && _componentPosition == 2 ) {
RMTMEM.chan[ _channel ].data32[ i * 8 + offset + 7 ].duration1 =
_timing.TRS / ( detail::RMT_DURATION_NS * detail::DIVIDER );
}
_componentPosition++;
if ( _componentPosition == 3 ) {
_componentPosition = 0;
_pixelPosition++;
}
}
for ( i *= 8; i != detail::MAX_PULSES; i++ ) {
RMTMEM.chan[ _channel ].data32[ i + offset ].val = 0;
}
}

530
code/SmartLeds.h Normal file
View File

@@ -0,0 +1,530 @@
#pragma once
/*
* A C++ driver for the WS2812 LEDs using the RMT peripheral on the ESP32.
*
* Jan "yaqwsx" Mrázek <email@honzamrazek.cz>
*
* Based on the work by Martin F. Falatic - https://github.com/FozzTexx/ws2812-demo
*/
/*
* 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 <memory>
#include <cassert>
#include <cstring>
#if defined ( ARDUINO )
extern "C" { // ...someone forgot to put in the includes...
#include "esp32-hal.h"
#include "esp_intr_alloc.h"
#include "esp_ipc.h"
#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
#include "freertos/semphr.h"
#include "soc/rmt_struct.h"
#include <driver/spi_master.h>
#include "esp_idf_version.h"
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL( 4, 0, 0 )
#include "soc/dport_reg.h"
#endif
}
#elif defined ( ESP_PLATFORM )
extern "C" { // ...someone forgot to put in the includes...
#include <esp_intr_alloc.h>
#include <esp_ipc.h>
#include <driver/gpio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <soc/dport_reg.h>
#include <soc/gpio_sig_map.h>
#include <soc/rmt_struct.h>
#include <driver/spi_master.h>
}
#include <stdio.h>
#endif
#include "Color.h"
namespace detail {
struct TimingParams {
uint32_t T0H;
uint32_t T1H;
uint32_t T0L;
uint32_t T1L;
uint32_t TRS;
};
union RmtPulsePair {
struct {
int duration0:15;
int level0:1;
int duration1:15;
int level1:1;
};
uint32_t value;
};
static const int DIVIDER = 4; // 8 still seems to work, but timings become marginal
static const int MAX_PULSES = 32; // A channel has a 64 "pulse" buffer - we use half per pass
static const double RMT_DURATION_NS = 12.5; // minimum time of a single RMT duration based on clock ns
} // namespace detail
using LedType = detail::TimingParams;
static const LedType LED_WS2812 = { 350, 700, 800, 600, 50000 };
static const LedType LED_WS2812B = { 400, 850, 850, 400, 50100 };
static const LedType LED_SK6812 = { 300, 600, 900, 600, 80000 };
static const LedType LED_WS2813 = { 350, 800, 350, 350, 300000 };
enum BufferType { SingleBuffer = 0, DoubleBuffer };
enum IsrCore { CoreFirst = 0, CoreSecond = 1, CoreCurrent = 2};
class SmartLed {
public:
// The RMT interrupt must not run on the same core as WiFi interrupts, otherwise SmartLeds
// can't fill the RMT buffer fast enough, resulting in rendering artifacts.
// Usually, that means you have to set isrCore == CoreSecond.
//
// If you use anything other than CoreCurrent, the FreeRTOS scheduler MUST be already running,
// so you can't use it if you define SmartLed as global variable.
SmartLed( const LedType& type, int count, int pin, int channel = 0, BufferType doubleBuffer = SingleBuffer, IsrCore isrCore = CoreCurrent)
: _timing( type ),
_channel( channel ),
_count( count ),
_firstBuffer( new Rgb[ count ] ),
_secondBuffer( doubleBuffer ? new Rgb[ count ] : nullptr ),
_finishedFlag( xSemaphoreCreateBinary() )
{
assert( channel >= 0 && channel < 8 );
assert( ledForChannel( channel ) == nullptr );
xSemaphoreGive( _finishedFlag );
DPORT_SET_PERI_REG_MASK( DPORT_PERIP_CLK_EN_REG, DPORT_RMT_CLK_EN );
DPORT_CLEAR_PERI_REG_MASK( DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST );
PIN_FUNC_SELECT( GPIO_PIN_MUX_REG[ pin ], 2 );
gpio_set_direction( static_cast< gpio_num_t >( pin ), GPIO_MODE_OUTPUT );
gpio_matrix_out( static_cast< gpio_num_t >( pin ), RMT_SIG_OUT0_IDX + _channel, 0, 0 );
initChannel( _channel );
RMT.tx_lim_ch[ _channel ].limit = detail::MAX_PULSES;
RMT.int_ena.val |= 1 << ( 24 + _channel );
RMT.int_ena.val |= 1 << ( 3 * _channel );
_bitToRmt[ 0 ].level0 = 1;
_bitToRmt[ 0 ].level1 = 0;
_bitToRmt[ 0 ].duration0 = _timing.T0H / ( detail::RMT_DURATION_NS * detail::DIVIDER );
_bitToRmt[ 0 ].duration1 = _timing.T0L / ( detail::RMT_DURATION_NS * detail::DIVIDER );
_bitToRmt[ 1 ].level0 = 1;
_bitToRmt[ 1 ].level1 = 0;
_bitToRmt[ 1 ].duration0 = _timing.T1H / ( detail::RMT_DURATION_NS * detail::DIVIDER );
_bitToRmt[ 1 ].duration1 = _timing.T1L / ( detail::RMT_DURATION_NS * detail::DIVIDER );
if ( !anyAlive() ) {
_interruptCore = isrCore;
if(isrCore != CoreCurrent) {
ESP_ERROR_CHECK(esp_ipc_call_blocking(isrCore, registerInterrupt, NULL));
} else {
registerInterrupt(NULL);
}
}
ledForChannel( channel ) = this;
}
~SmartLed() {
ledForChannel( _channel ) = nullptr;
if ( !anyAlive() ) {
if(_interruptCore != CoreCurrent) {
ESP_ERROR_CHECK(esp_ipc_call_blocking(_interruptCore, unregisterInterrupt, NULL));
} else {
unregisterInterrupt(NULL);
}
}
vSemaphoreDelete( _finishedFlag );
}
Rgb& operator[]( int idx ) {
return _firstBuffer[ idx ];
}
const Rgb& operator[]( int idx ) const {
return _firstBuffer[ idx ];
}
void show() {
_buffer = _firstBuffer.get();
startTransmission();
swapBuffers();
}
bool wait( TickType_t timeout = portMAX_DELAY ) {
if( xSemaphoreTake( _finishedFlag, timeout ) == pdTRUE ) {
xSemaphoreGive( _finishedFlag );
return true;
}
return false;
}
int size() const {
return _count;
}
Rgb *begin() { return _firstBuffer.get(); }
const Rgb *begin() const { return _firstBuffer.get(); }
const Rgb *cbegin() const { return _firstBuffer.get(); }
Rgb *end() { return _firstBuffer.get() + _count; }
const Rgb *end() const { return _firstBuffer.get() + _count; }
const Rgb *cend() const { return _firstBuffer.get() + _count; }
private:
static intr_handle_t _interruptHandle;
static IsrCore _interruptCore;
static void initChannel( int channel ) {
RMT.apb_conf.fifo_mask = 1; //enable memory access, instead of FIFO mode.
RMT.apb_conf.mem_tx_wrap_en = 1; //wrap around when hitting end of buffer
RMT.conf_ch[ channel ].conf0.div_cnt = detail::DIVIDER;
RMT.conf_ch[ channel ].conf0.mem_size = 1;
RMT.conf_ch[ channel ].conf0.carrier_en = 0;
RMT.conf_ch[ channel ].conf0.carrier_out_lv = 1;
RMT.conf_ch[ channel ].conf0.mem_pd = 0;
RMT.conf_ch[ channel ].conf1.rx_en = 0;
RMT.conf_ch[ channel ].conf1.mem_owner = 0;
RMT.conf_ch[ channel ].conf1.tx_conti_mode = 0; //loop back mode.
RMT.conf_ch[ channel ].conf1.ref_always_on = 1; // use apb clock: 80M
RMT.conf_ch[ channel ].conf1.idle_out_en = 1;
RMT.conf_ch[ channel ].conf1.idle_out_lv = 0;
}
static void registerInterrupt(void *) {
ESP_ERROR_CHECK(esp_intr_alloc( ETS_RMT_INTR_SOURCE, 0, interruptHandler, nullptr, &_interruptHandle));
}
static void unregisterInterrupt(void*) {
esp_intr_free( _interruptHandle );
}
static SmartLed*& IRAM_ATTR ledForChannel( int channel );
static void IRAM_ATTR interruptHandler( void* );
void IRAM_ATTR copyRmtHalfBlock();
void swapBuffers() {
if ( _secondBuffer )
_firstBuffer.swap( _secondBuffer );
}
void startTransmission() {
// Invalid use of the library
if( xSemaphoreTake( _finishedFlag, 0 ) != pdTRUE )
abort();
_pixelPosition = _componentPosition = _halfIdx = 0;
copyRmtHalfBlock();
if ( _pixelPosition < _count )
copyRmtHalfBlock();
RMT.conf_ch[ _channel ].conf1.mem_rd_rst = 1;
RMT.conf_ch[ _channel ].conf1.tx_start = 1;
}
static bool anyAlive() {
for ( int i = 0; i != 8; i++ )
if ( ledForChannel( i ) != nullptr ) return true;
return false;
}
const LedType& _timing;
int _channel;
detail::RmtPulsePair _bitToRmt[ 2 ];
int _count;
std::unique_ptr< Rgb[] > _firstBuffer;
std::unique_ptr< Rgb[] > _secondBuffer;
Rgb *_buffer;
xSemaphoreHandle _finishedFlag;
int _pixelPosition;
int _componentPosition;
int _halfIdx;
};
class Apa102 {
public:
struct ApaRgb {
ApaRgb( uint8_t r = 0, uint8_t g = 0, uint32_t b = 0, uint32_t v = 0xFF )
: v( 0xE0 | v ), b( b ), g( g ), r( r )
{}
ApaRgb& operator=( const Rgb& o ) {
r = o.r;
g = o.g;
b = o.b;
return *this;
}
ApaRgb& operator=( const Hsv& o ) {
*this = Rgb{ o };
return *this;
}
uint8_t v, b, g, r;
};
static const int FINAL_FRAME_SIZE = 4;
static const int TRANS_COUNT = 2 + 8;
Apa102( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer )
: _count( count ),
_firstBuffer( new ApaRgb[ count ] ),
_secondBuffer( doubleBuffer ? new ApaRgb[ count ] : nullptr ),
_initFrame( 0 )
{
spi_bus_config_t buscfg;
memset( &buscfg, 0, sizeof( buscfg ) );
buscfg.mosi_io_num = datapin;
buscfg.miso_io_num = -1;
buscfg.sclk_io_num = clkpin;
buscfg.quadwp_io_num = -1;
buscfg.quadhd_io_num = -1;
buscfg.max_transfer_sz = 65535;
spi_device_interface_config_t devcfg;
memset( &devcfg, 0, sizeof( devcfg ) );
devcfg.clock_speed_hz = 1000000;
devcfg.mode = 0;
devcfg.spics_io_num = -1;
devcfg.queue_size = TRANS_COUNT;
devcfg.pre_cb = nullptr;
auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 );
assert( ret == ESP_OK );
ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi );
assert( ret == ESP_OK );
std::fill_n( _finalFrame, FINAL_FRAME_SIZE, 0xFFFFFFFF );
}
~Apa102() {
// ToDo
}
ApaRgb& operator[]( int idx ) {
return _firstBuffer[ idx ];
}
const ApaRgb& operator[]( int idx ) const {
return _firstBuffer[ idx ];
}
void show() {
_buffer = _firstBuffer.get();
startTransmission();
swapBuffers();
}
void wait() {
for ( int i = 0; i != _transCount; i++ ) {
spi_transaction_t *t;
spi_device_get_trans_result( _spi, &t, portMAX_DELAY );
}
}
private:
void swapBuffers() {
if ( _secondBuffer )
_firstBuffer.swap( _secondBuffer );
}
void startTransmission() {
for ( int i = 0; i != TRANS_COUNT; i++ ) {
_transactions[ i ].cmd = 0;
_transactions[ i ].addr = 0;
_transactions[ i ].flags = 0;
_transactions[ i ].rxlength = 0;
_transactions[ i ].rx_buffer = nullptr;
}
// Init frame
_transactions[ 0 ].length = 32;
_transactions[ 0 ].tx_buffer = &_initFrame;
spi_device_queue_trans( _spi, _transactions + 0, portMAX_DELAY );
// Data
_transactions[ 1 ].length = 32 * _count;
_transactions[ 1 ].tx_buffer = _buffer;
spi_device_queue_trans( _spi, _transactions + 1, portMAX_DELAY );
_transCount = 2;
// End frame
for ( int i = 0; i != 1 + _count / 32 / FINAL_FRAME_SIZE; i++ ) {
_transactions[ 2 + i ].length = 32 * FINAL_FRAME_SIZE;
_transactions[ 2 + i ].tx_buffer = _finalFrame;
spi_device_queue_trans( _spi, _transactions + 2 + i, portMAX_DELAY );
_transCount++;
}
}
spi_device_handle_t _spi;
int _count;
std::unique_ptr< ApaRgb[] > _firstBuffer, _secondBuffer;
ApaRgb *_buffer;
spi_transaction_t _transactions[ TRANS_COUNT ];
int _transCount;
uint32_t _initFrame;
uint32_t _finalFrame[ FINAL_FRAME_SIZE ];
};
class LDP8806 {
public:
struct LDP8806_GRB {
LDP8806_GRB( uint8_t g_7bit = 0, uint8_t r_7bit = 0, uint32_t b_7bit = 0 )
: g( g_7bit ), r( r_7bit ), b( b_7bit )
{
}
LDP8806_GRB& operator=( const Rgb& o ) {
//Convert 8->7bit colour
r = ( o.r * 127 / 256 ) | 0x80;
g = ( o.g * 127 / 256 ) | 0x80;
b = ( o.b * 127 / 256 ) | 0x80;
return *this;
}
LDP8806_GRB& operator=( const Hsv& o ) {
*this = Rgb{ o };
return *this;
}
uint8_t g, r, b;
};
static const int LED_FRAME_SIZE_BYTES = sizeof( LDP8806_GRB );
static const int LATCH_FRAME_SIZE_BYTES = 3;
static const int TRANS_COUNT_MAX = 20;//Arbitrary, supports up to 600 LED
LDP8806( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, uint32_t clock_speed_hz = 2000000 )
: _count( count ),
_firstBuffer( new LDP8806_GRB[ count ] ),
_secondBuffer( doubleBuffer ? new LDP8806_GRB[ count ] : nullptr ),
// one 'latch'/start-of-data mark frame for every 32 leds
_latchFrames( ( count + 31 ) / 32 )
{
spi_bus_config_t buscfg;
memset( &buscfg, 0, sizeof( buscfg ) );
buscfg.mosi_io_num = datapin;
buscfg.miso_io_num = -1;
buscfg.sclk_io_num = clkpin;
buscfg.quadwp_io_num = -1;
buscfg.quadhd_io_num = -1;
buscfg.max_transfer_sz = 65535;
spi_device_interface_config_t devcfg;
memset( &devcfg, 0, sizeof( devcfg ) );
devcfg.clock_speed_hz = clock_speed_hz;
devcfg.mode = 0;
devcfg.spics_io_num = -1;
devcfg.queue_size = TRANS_COUNT_MAX;
devcfg.pre_cb = nullptr;
auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 );
assert( ret == ESP_OK );
ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi );
assert( ret == ESP_OK );
std::fill_n( _latchBuffer, LATCH_FRAME_SIZE_BYTES, 0x0 );
}
~LDP8806() {
// noop
}
LDP8806_GRB& operator[]( int idx ) {
return _firstBuffer[ idx ];
}
const LDP8806_GRB& operator[]( int idx ) const {
return _firstBuffer[ idx ];
}
void show() {
_buffer = _firstBuffer.get();
startTransmission();
swapBuffers();
}
void wait() {
while ( _transCount-- ) {
spi_transaction_t *t;
spi_device_get_trans_result( _spi, &t, portMAX_DELAY );
}
}
private:
void swapBuffers() {
if ( _secondBuffer )
_firstBuffer.swap( _secondBuffer );
}
void startTransmission() {
_transCount = 0;
for ( int i = 0; i != TRANS_COUNT_MAX; i++ ) {
_transactions[ i ].cmd = 0;
_transactions[ i ].addr = 0;
_transactions[ i ].flags = 0;
_transactions[ i ].rxlength = 0;
_transactions[ i ].rx_buffer = nullptr;
}
// LED Data
_transactions[ 0 ].length = ( LED_FRAME_SIZE_BYTES * 8 ) * _count;
_transactions[ 0 ].tx_buffer = _buffer;
spi_device_queue_trans( _spi, _transactions + _transCount, portMAX_DELAY );
_transCount++;
// 'latch'/start-of-data marker frames
for ( int i = 0; i < _latchFrames; i++ ) {
_transactions[ _transCount ].length = ( LATCH_FRAME_SIZE_BYTES * 8 );
_transactions[ _transCount ].tx_buffer = _latchBuffer;
spi_device_queue_trans( _spi, _transactions + _transCount, portMAX_DELAY );
_transCount++;
}
}
spi_device_handle_t _spi;
int _count;
std::unique_ptr< LDP8806_GRB[] > _firstBuffer, _secondBuffer;
LDP8806_GRB *_buffer;
spi_transaction_t _transactions[ TRANS_COUNT_MAX ];
int _transCount;
int _latchFrames;
uint8_t _latchBuffer[ LATCH_FRAME_SIZE_BYTES ];
};

View File

@@ -0,0 +1,132 @@
#include "Color.h"
#include <algorithm>
#include <cmath>
#include <cassert>
namespace {
// Int -> fixed point
int up( int x ) { return x * 255; }
} // namespace
int iRgbSqrt( int num ) {
// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_.28base_2.29
assert( "sqrt input should be non-negative" && num >= 0 );
assert( "sqrt input should no exceed 16 bits" && num <= 0xFFFF );
int res = 0;
int bit = 1 << 16;
while ( bit > num )
bit >>= 2;
while ( bit != 0 ) {
if ( num >= res + bit ) {
num -= res + bit;
res = ( res >> 1 ) + bit;
} else
res >>= 1;
bit >>= 2;
}
return res;
}
Rgb::Rgb( Hsv y ) {
// https://stackoverflow.com/questions/24152553/hsv-to-rgb-and-back-without-floating-point-math-in-python
// greyscale
if( y.s == 0 ) {
r = g = b = y.v;
return;
}
const int region = y.h / 43;
const int remainder = ( y.h - ( region * 43 ) ) * 6;
const int p = ( y.v * ( 255 - y.s ) ) >> 8;
const int q = ( y.v * ( 255 - ( ( y.s * remainder ) >> 8 ) ) ) >> 8;
const int t = ( y.v * ( 255 - ( ( y.s * (255 -remainder ) ) >> 8 ) ) ) >> 8;
switch( region ) {
case 0: r = y.v; g = t; b = p; break;
case 1: r = q; g = y.v; b = p; break;
case 2: r = p; g = y.v; b = t; break;
case 3: r = p; g = q; b = y.v; break;
case 4: r = t; g = p; b = y.v; break;
case 5: r = y.v; g = p; b = q; break;
default: __builtin_trap();
}
a = y.a;
}
Rgb& Rgb::operator=( Hsv hsv ) {
Rgb r{ hsv };
swap( r );
return *this;
}
Rgb Rgb::operator+( Rgb in ) const {
auto copy = *this;
copy += in;
return copy;
}
Rgb& Rgb::operator+=( Rgb in ) {
unsigned int red = r + in.r;
r = ( red < 255 ) ? red : 255;
unsigned int green = g + in.g;
g = ( green < 255 ) ? green : 255;
unsigned int blue = b + in.b;
b = ( blue < 255 ) ? blue : 255;
return *this;
}
Rgb& Rgb::blend( Rgb in ) {
unsigned int inAlpha = in.a * ( 255 - a );
unsigned int alpha = a + inAlpha;
r = iRgbSqrt( ( ( r * r * a ) + ( in.r * in.r * inAlpha ) ) / alpha );
g = iRgbSqrt( ( ( g * g * a ) + ( in.g * in.g * inAlpha ) ) / alpha );
b = iRgbSqrt( ( ( b * b * a ) + ( in.b * in.b * inAlpha ) ) / alpha );
a = alpha;
return *this;
}
uint8_t IRAM_ATTR Rgb::getGrb( int idx ) {
switch ( idx ) {
case 0: return g;
case 1: return r;
case 2: return b;
}
__builtin_unreachable();
}
Hsv::Hsv( Rgb r ) {
int min = std::min( r.r, std::min( r.g, r.b ) );
int max = std::max( r.r, std::max( r.g, r.b ) );
int chroma = max - min;
v = max;
if ( chroma == 0 ) {
h = s = 0;
return;
}
s = up( chroma ) / max;
int hh;
if ( max == r.r )
hh = ( up( int( r.g ) - int( r.b ) ) ) / chroma / 6;
else if ( max == r.g )
hh = 255 / 3 + ( up( int( r.b ) - int( r.r ) ) ) / chroma / 6;
else
hh = 2 * 255 / 3 + ( up( int( r.r ) - int( r.g ) ) ) / chroma / 6;
if ( hh < 0 )
hh += 255;
h = hh;
a = r.a;
}
Hsv& Hsv::operator=( Rgb rgb ) {
Hsv h{ rgb };
swap( h );
return *this;
}

View File

@@ -0,0 +1,69 @@
#pragma once
#include <cstdint>
#include "esp_attr.h"
union Hsv;
union Rgb {
struct __attribute__ ((packed)) {
uint8_t r, g, b, a;
};
uint32_t value;
Rgb( uint8_t r = 0, uint8_t g = 0, uint8_t b = 0, uint8_t a = 255 ) : r( r ), g( g ), b( b ), a( a ) {}
Rgb( Hsv c );
Rgb& operator=( Rgb rgb ) { swap( rgb ); return *this; }
Rgb& operator=( Hsv hsv );
Rgb operator+( Rgb in ) const;
Rgb& operator+=( Rgb in );
bool operator==( Rgb in ) const { return in.value == value; }
Rgb& blend( Rgb in );
void swap( Rgb& o ) { value = o.value; }
void linearize() {
r = channelGamma(r);
g = channelGamma(g);
b = channelGamma(b);
}
uint8_t IRAM_ATTR getGrb( int idx );
void stretchChannels( uint8_t maxR, uint8_t maxG, uint8_t maxB ) {
r = stretch( r, maxR );
g = stretch( g, maxG );
b = stretch( b, maxB );
}
void stretchChannelsEvenly( uint8_t max ) {
stretchChannels( max, max, max );
}
private:
uint8_t stretch( int value, uint8_t max ) {
return ( value * max ) >> 8;
}
uint8_t channelGamma( int channel ) {
/* The optimal gamma correction is x^2.8. However, this is expensive to
* compute. Therefore, we use x^3 for gamma correction. Also, we add a
* bias as the WS2812 LEDs do not turn on for values less than 4. */
if (channel == 0)
return channel;
channel = channel * channel * channel * 251;
channel >>= 24;
return static_cast< uint8_t >( 4 + channel );
}
};
union Hsv {
struct __attribute__ ((packed)) {
uint8_t h, s, v, a;
};
uint32_t value;
Hsv( uint8_t h, uint8_t s = 0, uint8_t v = 0, uint8_t a = 255 ) : h( h ), s( s ), v( v ), a( a ) {}
Hsv( Rgb r );
Hsv& operator=( Hsv h ) { swap( h ); return *this; }
Hsv& operator=( Rgb rgb );
bool operator==( Hsv in ) const { return in.value == value; }
void swap( Hsv& o ) { value = o.value; }
};

View File

@@ -0,0 +1,63 @@
#include "SmartLeds.h"
IsrCore SmartLed::_interruptCore = CoreCurrent;
intr_handle_t SmartLed::_interruptHandle = NULL;
SmartLed*& IRAM_ATTR SmartLed::ledForChannel( int channel ) {
static SmartLed* table[8] = { nullptr };
assert( channel < 8 );
return table[ channel ];
}
void IRAM_ATTR SmartLed::interruptHandler(void*) {
for (int channel = 0; channel != 8; channel++) {
auto self = ledForChannel( channel );
if ( RMT.int_st.val & (1 << (24 + channel ) ) ) { // tx_thr_event
if ( self )
self->copyRmtHalfBlock();
RMT.int_clr.val |= 1 << ( 24 + channel );
} else if ( RMT.int_st.val & ( 1 << (3 * channel ) ) ) { // tx_end
if ( self )
xSemaphoreGiveFromISR( self->_finishedFlag, nullptr );
RMT.int_clr.val |= 1 << ( 3 * channel );
}
}
}
void IRAM_ATTR SmartLed::copyRmtHalfBlock() {
int offset = detail::MAX_PULSES * _halfIdx;
_halfIdx = !_halfIdx;
int len = 3 - _componentPosition + 3 * ( _count - 1 );
len = std::min( len, detail::MAX_PULSES / 8 );
if ( !len ) {
for ( int i = 0; i < detail::MAX_PULSES; i++) {
RMTMEM.chan[ _channel].data32[i + offset ].val = 0;
}
}
int i;
for ( i = 0; i != len && _pixelPosition != _count; i++ ) {
uint8_t val = _buffer[ _pixelPosition ].getGrb( _componentPosition );
for ( int j = 0; j != 8; j++, val <<= 1 ) {
int bit = val >> 7;
int idx = i * 8 + offset + j;
RMTMEM.chan[ _channel ].data32[ idx ].val = _bitToRmt[ bit & 0x01 ].value;
}
if ( _pixelPosition == _count - 1 && _componentPosition == 2 ) {
RMTMEM.chan[ _channel ].data32[ i * 8 + offset + 7 ].duration1 =
_timing.TRS / ( detail::RMT_DURATION_NS * detail::DIVIDER );
}
_componentPosition++;
if ( _componentPosition == 3 ) {
_componentPosition = 0;
_pixelPosition++;
}
}
for ( i *= 8; i != detail::MAX_PULSES; i++ ) {
RMTMEM.chan[ _channel ].data32[ i + offset ].val = 0;
}
}

View File

@@ -0,0 +1,530 @@
#pragma once
/*
* A C++ driver for the WS2812 LEDs using the RMT peripheral on the ESP32.
*
* Jan "yaqwsx" Mrázek <email@honzamrazek.cz>
*
* Based on the work by Martin F. Falatic - https://github.com/FozzTexx/ws2812-demo
*/
/*
* 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 <memory>
#include <cassert>
#include <cstring>
#if defined ( ARDUINO )
extern "C" { // ...someone forgot to put in the includes...
#include "esp32-hal.h"
#include "esp_intr_alloc.h"
#include "esp_ipc.h"
#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
#include "freertos/semphr.h"
#include "soc/rmt_struct.h"
#include <driver/spi_master.h>
#include "esp_idf_version.h"
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL( 4, 0, 0 )
#include "soc/dport_reg.h"
#endif
}
#elif defined ( ESP_PLATFORM )
extern "C" { // ...someone forgot to put in the includes...
#include <esp_intr_alloc.h>
#include <esp_ipc.h>
#include <driver/gpio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <soc/dport_reg.h>
#include <soc/gpio_sig_map.h>
#include <soc/rmt_struct.h>
#include <driver/spi_master.h>
}
#include <stdio.h>
#endif
#include "Color.h"
namespace detail {
struct TimingParams {
uint32_t T0H;
uint32_t T1H;
uint32_t T0L;
uint32_t T1L;
uint32_t TRS;
};
union RmtPulsePair {
struct {
int duration0:15;
int level0:1;
int duration1:15;
int level1:1;
};
uint32_t value;
};
static const int DIVIDER = 4; // 8 still seems to work, but timings become marginal
static const int MAX_PULSES = 32; // A channel has a 64 "pulse" buffer - we use half per pass
static const double RMT_DURATION_NS = 12.5; // minimum time of a single RMT duration based on clock ns
} // namespace detail
using LedType = detail::TimingParams;
static const LedType LED_WS2812 = { 350, 700, 800, 600, 50000 };
static const LedType LED_WS2812B = { 400, 850, 850, 400, 50100 };
static const LedType LED_SK6812 = { 300, 600, 900, 600, 80000 };
static const LedType LED_WS2813 = { 350, 800, 350, 350, 300000 };
enum BufferType { SingleBuffer = 0, DoubleBuffer };
enum IsrCore { CoreFirst = 0, CoreSecond = 1, CoreCurrent = 2};
class SmartLed {
public:
// The RMT interrupt must not run on the same core as WiFi interrupts, otherwise SmartLeds
// can't fill the RMT buffer fast enough, resulting in rendering artifacts.
// Usually, that means you have to set isrCore == CoreSecond.
//
// If you use anything other than CoreCurrent, the FreeRTOS scheduler MUST be already running,
// so you can't use it if you define SmartLed as global variable.
SmartLed( const LedType& type, int count, int pin, int channel = 0, BufferType doubleBuffer = SingleBuffer, IsrCore isrCore = CoreCurrent)
: _timing( type ),
_channel( channel ),
_count( count ),
_firstBuffer( new Rgb[ count ] ),
_secondBuffer( doubleBuffer ? new Rgb[ count ] : nullptr ),
_finishedFlag( xSemaphoreCreateBinary() )
{
assert( channel >= 0 && channel < 8 );
assert( ledForChannel( channel ) == nullptr );
xSemaphoreGive( _finishedFlag );
DPORT_SET_PERI_REG_MASK( DPORT_PERIP_CLK_EN_REG, DPORT_RMT_CLK_EN );
DPORT_CLEAR_PERI_REG_MASK( DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST );
PIN_FUNC_SELECT( GPIO_PIN_MUX_REG[ pin ], 2 );
gpio_set_direction( static_cast< gpio_num_t >( pin ), GPIO_MODE_OUTPUT );
gpio_matrix_out( static_cast< gpio_num_t >( pin ), RMT_SIG_OUT0_IDX + _channel, 0, 0 );
initChannel( _channel );
RMT.tx_lim_ch[ _channel ].limit = detail::MAX_PULSES;
RMT.int_ena.val |= 1 << ( 24 + _channel );
RMT.int_ena.val |= 1 << ( 3 * _channel );
_bitToRmt[ 0 ].level0 = 1;
_bitToRmt[ 0 ].level1 = 0;
_bitToRmt[ 0 ].duration0 = _timing.T0H / ( detail::RMT_DURATION_NS * detail::DIVIDER );
_bitToRmt[ 0 ].duration1 = _timing.T0L / ( detail::RMT_DURATION_NS * detail::DIVIDER );
_bitToRmt[ 1 ].level0 = 1;
_bitToRmt[ 1 ].level1 = 0;
_bitToRmt[ 1 ].duration0 = _timing.T1H / ( detail::RMT_DURATION_NS * detail::DIVIDER );
_bitToRmt[ 1 ].duration1 = _timing.T1L / ( detail::RMT_DURATION_NS * detail::DIVIDER );
if ( !anyAlive() ) {
_interruptCore = isrCore;
if(isrCore != CoreCurrent) {
ESP_ERROR_CHECK(esp_ipc_call_blocking(isrCore, registerInterrupt, NULL));
} else {
registerInterrupt(NULL);
}
}
ledForChannel( channel ) = this;
}
~SmartLed() {
ledForChannel( _channel ) = nullptr;
if ( !anyAlive() ) {
if(_interruptCore != CoreCurrent) {
ESP_ERROR_CHECK(esp_ipc_call_blocking(_interruptCore, unregisterInterrupt, NULL));
} else {
unregisterInterrupt(NULL);
}
}
vSemaphoreDelete( _finishedFlag );
}
Rgb& operator[]( int idx ) {
return _firstBuffer[ idx ];
}
const Rgb& operator[]( int idx ) const {
return _firstBuffer[ idx ];
}
void show() {
_buffer = _firstBuffer.get();
startTransmission();
swapBuffers();
}
bool wait( TickType_t timeout = portMAX_DELAY ) {
if( xSemaphoreTake( _finishedFlag, timeout ) == pdTRUE ) {
xSemaphoreGive( _finishedFlag );
return true;
}
return false;
}
int size() const {
return _count;
}
Rgb *begin() { return _firstBuffer.get(); }
const Rgb *begin() const { return _firstBuffer.get(); }
const Rgb *cbegin() const { return _firstBuffer.get(); }
Rgb *end() { return _firstBuffer.get() + _count; }
const Rgb *end() const { return _firstBuffer.get() + _count; }
const Rgb *cend() const { return _firstBuffer.get() + _count; }
private:
static intr_handle_t _interruptHandle;
static IsrCore _interruptCore;
static void initChannel( int channel ) {
RMT.apb_conf.fifo_mask = 1; //enable memory access, instead of FIFO mode.
RMT.apb_conf.mem_tx_wrap_en = 1; //wrap around when hitting end of buffer
RMT.conf_ch[ channel ].conf0.div_cnt = detail::DIVIDER;
RMT.conf_ch[ channel ].conf0.mem_size = 1;
RMT.conf_ch[ channel ].conf0.carrier_en = 0;
RMT.conf_ch[ channel ].conf0.carrier_out_lv = 1;
RMT.conf_ch[ channel ].conf0.mem_pd = 0;
RMT.conf_ch[ channel ].conf1.rx_en = 0;
RMT.conf_ch[ channel ].conf1.mem_owner = 0;
RMT.conf_ch[ channel ].conf1.tx_conti_mode = 0; //loop back mode.
RMT.conf_ch[ channel ].conf1.ref_always_on = 1; // use apb clock: 80M
RMT.conf_ch[ channel ].conf1.idle_out_en = 1;
RMT.conf_ch[ channel ].conf1.idle_out_lv = 0;
}
static void registerInterrupt(void *) {
ESP_ERROR_CHECK(esp_intr_alloc( ETS_RMT_INTR_SOURCE, 0, interruptHandler, nullptr, &_interruptHandle));
}
static void unregisterInterrupt(void*) {
esp_intr_free( _interruptHandle );
}
static SmartLed*& IRAM_ATTR ledForChannel( int channel );
static void IRAM_ATTR interruptHandler( void* );
void IRAM_ATTR copyRmtHalfBlock();
void swapBuffers() {
if ( _secondBuffer )
_firstBuffer.swap( _secondBuffer );
}
void startTransmission() {
// Invalid use of the library
if( xSemaphoreTake( _finishedFlag, 0 ) != pdTRUE )
abort();
_pixelPosition = _componentPosition = _halfIdx = 0;
copyRmtHalfBlock();
if ( _pixelPosition < _count )
copyRmtHalfBlock();
RMT.conf_ch[ _channel ].conf1.mem_rd_rst = 1;
RMT.conf_ch[ _channel ].conf1.tx_start = 1;
}
static bool anyAlive() {
for ( int i = 0; i != 8; i++ )
if ( ledForChannel( i ) != nullptr ) return true;
return false;
}
const LedType& _timing;
int _channel;
detail::RmtPulsePair _bitToRmt[ 2 ];
int _count;
std::unique_ptr< Rgb[] > _firstBuffer;
std::unique_ptr< Rgb[] > _secondBuffer;
Rgb *_buffer;
xSemaphoreHandle _finishedFlag;
int _pixelPosition;
int _componentPosition;
int _halfIdx;
};
class Apa102 {
public:
struct ApaRgb {
ApaRgb( uint8_t r = 0, uint8_t g = 0, uint32_t b = 0, uint32_t v = 0xFF )
: v( 0xE0 | v ), b( b ), g( g ), r( r )
{}
ApaRgb& operator=( const Rgb& o ) {
r = o.r;
g = o.g;
b = o.b;
return *this;
}
ApaRgb& operator=( const Hsv& o ) {
*this = Rgb{ o };
return *this;
}
uint8_t v, b, g, r;
};
static const int FINAL_FRAME_SIZE = 4;
static const int TRANS_COUNT = 2 + 8;
Apa102( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer )
: _count( count ),
_firstBuffer( new ApaRgb[ count ] ),
_secondBuffer( doubleBuffer ? new ApaRgb[ count ] : nullptr ),
_initFrame( 0 )
{
spi_bus_config_t buscfg;
memset( &buscfg, 0, sizeof( buscfg ) );
buscfg.mosi_io_num = datapin;
buscfg.miso_io_num = -1;
buscfg.sclk_io_num = clkpin;
buscfg.quadwp_io_num = -1;
buscfg.quadhd_io_num = -1;
buscfg.max_transfer_sz = 65535;
spi_device_interface_config_t devcfg;
memset( &devcfg, 0, sizeof( devcfg ) );
devcfg.clock_speed_hz = 1000000;
devcfg.mode = 0;
devcfg.spics_io_num = -1;
devcfg.queue_size = TRANS_COUNT;
devcfg.pre_cb = nullptr;
auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 );
assert( ret == ESP_OK );
ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi );
assert( ret == ESP_OK );
std::fill_n( _finalFrame, FINAL_FRAME_SIZE, 0xFFFFFFFF );
}
~Apa102() {
// ToDo
}
ApaRgb& operator[]( int idx ) {
return _firstBuffer[ idx ];
}
const ApaRgb& operator[]( int idx ) const {
return _firstBuffer[ idx ];
}
void show() {
_buffer = _firstBuffer.get();
startTransmission();
swapBuffers();
}
void wait() {
for ( int i = 0; i != _transCount; i++ ) {
spi_transaction_t *t;
spi_device_get_trans_result( _spi, &t, portMAX_DELAY );
}
}
private:
void swapBuffers() {
if ( _secondBuffer )
_firstBuffer.swap( _secondBuffer );
}
void startTransmission() {
for ( int i = 0; i != TRANS_COUNT; i++ ) {
_transactions[ i ].cmd = 0;
_transactions[ i ].addr = 0;
_transactions[ i ].flags = 0;
_transactions[ i ].rxlength = 0;
_transactions[ i ].rx_buffer = nullptr;
}
// Init frame
_transactions[ 0 ].length = 32;
_transactions[ 0 ].tx_buffer = &_initFrame;
spi_device_queue_trans( _spi, _transactions + 0, portMAX_DELAY );
// Data
_transactions[ 1 ].length = 32 * _count;
_transactions[ 1 ].tx_buffer = _buffer;
spi_device_queue_trans( _spi, _transactions + 1, portMAX_DELAY );
_transCount = 2;
// End frame
for ( int i = 0; i != 1 + _count / 32 / FINAL_FRAME_SIZE; i++ ) {
_transactions[ 2 + i ].length = 32 * FINAL_FRAME_SIZE;
_transactions[ 2 + i ].tx_buffer = _finalFrame;
spi_device_queue_trans( _spi, _transactions + 2 + i, portMAX_DELAY );
_transCount++;
}
}
spi_device_handle_t _spi;
int _count;
std::unique_ptr< ApaRgb[] > _firstBuffer, _secondBuffer;
ApaRgb *_buffer;
spi_transaction_t _transactions[ TRANS_COUNT ];
int _transCount;
uint32_t _initFrame;
uint32_t _finalFrame[ FINAL_FRAME_SIZE ];
};
class LDP8806 {
public:
struct LDP8806_GRB {
LDP8806_GRB( uint8_t g_7bit = 0, uint8_t r_7bit = 0, uint32_t b_7bit = 0 )
: g( g_7bit ), r( r_7bit ), b( b_7bit )
{
}
LDP8806_GRB& operator=( const Rgb& o ) {
//Convert 8->7bit colour
r = ( o.r * 127 / 256 ) | 0x80;
g = ( o.g * 127 / 256 ) | 0x80;
b = ( o.b * 127 / 256 ) | 0x80;
return *this;
}
LDP8806_GRB& operator=( const Hsv& o ) {
*this = Rgb{ o };
return *this;
}
uint8_t g, r, b;
};
static const int LED_FRAME_SIZE_BYTES = sizeof( LDP8806_GRB );
static const int LATCH_FRAME_SIZE_BYTES = 3;
static const int TRANS_COUNT_MAX = 20;//Arbitrary, supports up to 600 LED
LDP8806( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, uint32_t clock_speed_hz = 2000000 )
: _count( count ),
_firstBuffer( new LDP8806_GRB[ count ] ),
_secondBuffer( doubleBuffer ? new LDP8806_GRB[ count ] : nullptr ),
// one 'latch'/start-of-data mark frame for every 32 leds
_latchFrames( ( count + 31 ) / 32 )
{
spi_bus_config_t buscfg;
memset( &buscfg, 0, sizeof( buscfg ) );
buscfg.mosi_io_num = datapin;
buscfg.miso_io_num = -1;
buscfg.sclk_io_num = clkpin;
buscfg.quadwp_io_num = -1;
buscfg.quadhd_io_num = -1;
buscfg.max_transfer_sz = 65535;
spi_device_interface_config_t devcfg;
memset( &devcfg, 0, sizeof( devcfg ) );
devcfg.clock_speed_hz = clock_speed_hz;
devcfg.mode = 0;
devcfg.spics_io_num = -1;
devcfg.queue_size = TRANS_COUNT_MAX;
devcfg.pre_cb = nullptr;
auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 );
assert( ret == ESP_OK );
ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi );
assert( ret == ESP_OK );
std::fill_n( _latchBuffer, LATCH_FRAME_SIZE_BYTES, 0x0 );
}
~LDP8806() {
// noop
}
LDP8806_GRB& operator[]( int idx ) {
return _firstBuffer[ idx ];
}
const LDP8806_GRB& operator[]( int idx ) const {
return _firstBuffer[ idx ];
}
void show() {
_buffer = _firstBuffer.get();
startTransmission();
swapBuffers();
}
void wait() {
while ( _transCount-- ) {
spi_transaction_t *t;
spi_device_get_trans_result( _spi, &t, portMAX_DELAY );
}
}
private:
void swapBuffers() {
if ( _secondBuffer )
_firstBuffer.swap( _secondBuffer );
}
void startTransmission() {
_transCount = 0;
for ( int i = 0; i != TRANS_COUNT_MAX; i++ ) {
_transactions[ i ].cmd = 0;
_transactions[ i ].addr = 0;
_transactions[ i ].flags = 0;
_transactions[ i ].rxlength = 0;
_transactions[ i ].rx_buffer = nullptr;
}
// LED Data
_transactions[ 0 ].length = ( LED_FRAME_SIZE_BYTES * 8 ) * _count;
_transactions[ 0 ].tx_buffer = _buffer;
spi_device_queue_trans( _spi, _transactions + _transCount, portMAX_DELAY );
_transCount++;
// 'latch'/start-of-data marker frames
for ( int i = 0; i < _latchFrames; i++ ) {
_transactions[ _transCount ].length = ( LATCH_FRAME_SIZE_BYTES * 8 );
_transactions[ _transCount ].tx_buffer = _latchBuffer;
spi_device_queue_trans( _spi, _transactions + _transCount, portMAX_DELAY );
_transCount++;
}
}
spi_device_handle_t _spi;
int _count;
std::unique_ptr< LDP8806_GRB[] > _firstBuffer, _secondBuffer;
LDP8806_GRB *_buffer;
spi_transaction_t _transactions[ TRANS_COUNT_MAX ];
int _transCount;
int _latchFrames;
uint8_t _latchBuffer[ LATCH_FRAME_SIZE_BYTES ];
};

View File

@@ -107,7 +107,8 @@ void GpioPin::init()
//configure GPIO with the given settings
gpio_config(&io_conf);
if (_interruptType != GPIO_INTR_DISABLE) {
// if (_interruptType != GPIO_INTR_DISABLE) { // ohne GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X, wenn das genutzt wird, dann soll auch der Handler hier nicht initialisiert werden, da das dann über SmartLED erfolgt.
if ((_interruptType != GPIO_INTR_DISABLE) && (_interruptType != GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)) {
//hook isr handler for specific gpio pin
ESP_LOGD(TAG_SERVERGPIO, "GpioPin::init add isr handler for GPIO %d\r\n", _gpio);
gpio_isr_handler_add(_gpio, gpio_isr_handler, (void*)&_gpio);
@@ -212,6 +213,8 @@ void GpioHandler::init()
// printf("wait before start %ldms\r\n", (long) xDelay);
// vTaskDelay( xDelay );
printf("*************** Start GPIOHandler_Init *****************\n");
if (gpioMap == NULL) {
gpioMap = new std::map<gpio_num_t, GpioPin*>();
} else {
@@ -295,16 +298,24 @@ bool GpioHandler::readConfig()
std::string line = "";
bool disabledLine = false;
bool eof = false;
gpio_num_t gpioExtLED = (gpio_num_t) 0;
// printf("readConfig - Start 1\n");
while ((!configFile.GetNextParagraph(line, disabledLine, eof) || (line.compare("[GPIO]") != 0)) && !disabledLine && !eof) {}
while ((!configFile.GetNextParagraph(line, disabledLine, eof) || (line.compare("[GPIO]") != 0)) && !eof) {}
if (eof)
return false;
// printf("readConfig - Start 2 line: %s, disabbledLine: %d\n", line.c_str(), (int) disabledLine);
_isEnabled = !disabledLine;
if (!_isEnabled)
return false;
// printf("readConfig - Start 3\n");
// std::string mainTopicMQTT = "";
std::string mainTopicMQTT = GetMQTTMainTopic();
if (mainTopicMQTT.length() > 0)
@@ -346,10 +357,40 @@ bool GpioHandler::readConfig()
GpioPin* gpioPin = new GpioPin(gpioNr, gpioName, pinMode, intType,dutyResolution, mqttTopic, httpEnabled);
(*gpioMap)[gpioNr] = gpioPin;
if (pinMode == GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)
{
printf("Set WS2812 to GPIO %d\n", gpioNr);
gpioExtLED = gpioNr;
}
if (intType != GPIO_INTR_DISABLE) {
registerISR = true;
}
}
if (toUpper(zerlegt[0]) == "LEDNUMBERS")
{
LEDNumbers = stoi(zerlegt[1]);
}
if (toUpper(zerlegt[0]) == "LEDCOLOR")
{
uint8_t _r, _g, _b;
_r = stoi(zerlegt[1]);
_g = stoi(zerlegt[2]);
_b = stoi(zerlegt[3]);
LEDColor = Rgb{_r, _g, _b};
}
if (toUpper(zerlegt[0]) == "LEDTYPE")
{
if (zerlegt[1] == "WS2812")
LEDType = LED_WS2812;
if (zerlegt[1] == "WS2812B")
LEDType = LED_WS2812B;
if (zerlegt[1] == "SK6812")
LEDType = LED_SK6812;
if (zerlegt[1] == "WS2813")
LEDType = LED_WS2813;
}
}
if (registerISR) {
@@ -357,6 +398,28 @@ bool GpioHandler::readConfig()
gpio_install_isr_service(ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM);
}
if (gpioExtLED > 0)
{
// LogFile.WriteToFile("Startsequence 06");
// vTaskDelay( xDelay );
// xDelay = 5000 / portTICK_PERIOD_MS;
// printf("main: sleep for : %ldms\n", (long) xDelay);
SmartLed leds( LED_WS2812, 2, GPIO_NUM_12, 0, DoubleBuffer );
leds[ 0 ] = Rgb{ 255, 0, 0 };
leds[ 1 ] = Rgb{ 255, 255, 255 };
leds.show();
/*
// _SmartLED = new SmartLed(LEDType, LEDNumbers, gpioExtLED, 0, DoubleBuffer);
_SmartLED = new SmartLed( LED_WS2812, 2, GPIO_NUM_12, 0, DoubleBuffer );
(*_SmartLED)[ 0 ] = Rgb{ 255, 0, 0 };
(*_SmartLED)[ 1 ] = LEDColor;
_SmartLED->show();
*/
}
return true;
}
@@ -498,7 +561,24 @@ void GpioHandler::flashLightEnable(bool value)
} else {
ESP_LOGE(TAG_SERVERGPIO, "Can't set flash light pin GPIO %d. Error: %s\r\n", (int)it->first, resp_str.c_str());
}
}
} else
{
if (it->second->getMode() == GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)
{
SmartLed leds( LEDType, LEDNumbers, it->second->getGPIO(), 0, DoubleBuffer );
if (value)
{
for (int i = 0; i < LEDNumbers; ++i)
leds[i] = LEDColor;
}
else
{
for (int i = 0; i < LEDNumbers; ++i)
leds[i] = Rgb{0, 0, 0};
}
leds.show();
}
}
}
}
}

View File

@@ -7,6 +7,8 @@
#include <map>
#include "driver/gpio.h"
#include "SmartLeds.h"
//#include "ClassControllCamera.h"
typedef enum {
@@ -45,6 +47,7 @@ public:
void gpioInterrupt(int value);
gpio_int_type_t getInterruptType() { return _interruptType; }
gpio_pin_mode_t getMode() { return _mode; }
gpio_num_t getGPIO(){return _gpio;};
private:
gpio_num_t _gpio;
@@ -80,6 +83,11 @@ private:
TaskHandle_t xHandleTaskGpio = NULL;
bool _isEnabled = false;
int LEDNumbers = 2;
Rgb LEDColor = Rgb{ 255, 255, 255 };
LedType LEDType = LED_WS2812;
bool readConfig();
void clear();

View File

@@ -499,6 +499,7 @@ void CCamera::LightOnOff(bool status)
{
GpioHandler* gpioHandler = gpio_handler_get();
if ((gpioHandler != NULL) && (gpioHandler->isEnabled())) {
printf("Use gpioHandler flashLigh\n");
gpioHandler->flashLightEnable(status);
} else {
// Init the GPIO

View File

@@ -111,6 +111,8 @@ esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filename)
return httpd_resp_set_type(req, "image/jpeg");
} else if (IS_FILE_EXT(filename, ".ico")) {
return httpd_resp_set_type(req, "image/x-icon");
} else if (IS_FILE_EXT(filename, ".js")) {
return httpd_resp_set_type(req, "text/javascript");
}
/* This is a limited set only */
/* For any other type always set as plain text */

View File

@@ -21,23 +21,23 @@ ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNTy
previousElement = NULL;
SaveAllFiles = false;
disabled = false;
extendedResolution = false;
// extendedResolution = false;
isLogImageSelect = false;
CNNType = AutoDetect;
CNNType = _cnntype;
flowpostalignment = _flowalign;
}
/*
int ClassFlowCNNGeneral::AnzahlROIs(int _analog = 0)
{
int zw = GENERAL[_analog]->ROI.size();
if (extendedResolution && (CNNType != Digital)) zw++; // da letzte Ziffer inkl Nachhkomma, es sei denn, das Nachkomma gibt es nicht (Digital)
return zw;
}
*/
string ClassFlowCNNGeneral::getReadout(int _analog = 0)
string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution = false)
{
string result = "";
if (GENERAL[_analog]->ROI.size() == 0)
@@ -45,20 +45,20 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0)
if (CNNType == Analogue)
{
float zahl = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result;
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
float zahl = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float;
int ergebnis_nachkomma = ((int) floor(zahl * 10) + 10) % 10;
int prev = -1;
prev = ZeigerEval(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result, prev);
prev = ZeigerEval(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, prev);
result = std::to_string(prev);
if (extendedResolution && (CNNType != Digital))
if (_extendedResolution && (CNNType != Digital))
result = result + std::to_string(ergebnis_nachkomma);
for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i)
{
prev = ZeigerEval(GENERAL[_analog]->ROI[i]->result, prev);
prev = ZeigerEval(GENERAL[_analog]->ROI[i]->result_float, prev);
result = std::to_string(prev) + result;
}
}
@@ -67,10 +67,10 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0)
{
for (int i = 0; i < GENERAL[_analog]->ROI.size(); ++i)
{
if (GENERAL[_analog]->ROI[i]->resultklasse == 10)
if (GENERAL[_analog]->ROI[i]->result_klasse >= 10)
result = result + "N";
else
result = result + std::to_string(GENERAL[_analog]->ROI[i]->resultklasse);
result = result + std::to_string(GENERAL[_analog]->ROI[i]->result_klasse);
}
}
@@ -79,34 +79,35 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0)
// int ergebnis_nachkomma = -1;
int zif_akt = -1;
float zahl = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result;
float zahl = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float;
if (zahl >= 0) // NaN?
{
if (extendedResolution)
if (_extendedResolution)
{
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
int ergebnis_vorkomma = ((int) floor(zahl)) % 10;
result = std::to_string(ergebnis_vorkomma) + std::to_string(ergebnis_nachkomma);
zif_akt = ergebnis_vorkomma;
}
else
{
zif_akt = ZeigerEvalHybrid(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result, -1, -1);
zif_akt = ZeigerEvalHybrid(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, -1, -1);
result = std::to_string(zif_akt);
}
}
else
{
result = "N";
if (extendedResolution && (CNNType != Digital))
if (_extendedResolution && (CNNType != Digital))
result = "NN";
}
for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i)
{
if (GENERAL[_analog]->ROI[i]->result >= 0)
if (GENERAL[_analog]->ROI[i]->result_float >= 0)
{
zif_akt = ZeigerEvalHybrid(GENERAL[_analog]->ROI[i]->result, GENERAL[_analog]->ROI[i+1]->result, zif_akt);
zif_akt = ZeigerEvalHybrid(GENERAL[_analog]->ROI[i]->result_float, GENERAL[_analog]->ROI[i+1]->result_float, zif_akt);
result = std::to_string(zif_akt) + result;
}
else
@@ -117,7 +118,6 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0)
}
}
return result;
}
@@ -129,43 +129,43 @@ int ClassFlowCNNGeneral::ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int
if (zahl_vorgaenger < 0) // keine Vorzahl vorhanden !!! --> Runde die Zahl
{
if ((ergebnis_nachkomma <= 2) || (ergebnis_nachkomma >= 8)) // Band um die Ziffer --> Runden, da Ziffer im Rahmen Ungenauigkeit erreicht
return (int) round(zahl);
return ((int) round(zahl) + 10) % 10;
else
return (int) trunc(zahl);
return ((int) trunc(zahl) + 10) % 10;
}
if (zahl_vorgaenger > 9.2) // Ziffernwechsel beginnt
{
if (eval_vorgaenger == 0) // Wechsel hat schon stattgefunden
{
return (int) round(zahl); // Annahme, dass die neue Zahl schon in der Nähe des Ziels ist
return ((int) round(zahl) + 10) % 10; // Annahme, dass die neue Zahl schon in der Nähe des Ziels ist
}
else
{
if (zahl_vorgaenger <= 9.5) // Wechsel startet gerade, aber beginnt erst
{
if ((ergebnis_nachkomma <= 2) || (ergebnis_nachkomma >= 8)) // Band um die Ziffer --> Runden, da Ziffer im Rahmen Ungenauigkeit erreicht
return (int) round(zahl);
return ((int) round(zahl) + 10) % 10;
else
return (int) trunc(zahl);
return ((int) trunc(zahl) + 10) % 10;
}
else
{
return (int) trunc(zahl); // Wechsel schon weiter fortgeschritten, d.h. über 2 als Nachkomma
return ((int) trunc(zahl) + 10) % 10; // Wechsel schon weiter fortgeschritten, d.h. über 2 als Nachkomma
}
}
}
if ((ergebnis_nachkomma <= 2) || (ergebnis_nachkomma >= 8)) // Band um die Ziffer --> Runden, da Ziffer im Rahmen Ungenauigkeit erreicht
return (int) round(zahl);
return ((int) round(zahl) + 10) % 10;
return (int) trunc(zahl);
return ((int) trunc(zahl) + 10) % 10;
}
int ClassFlowCNNGeneral::ZeigerEval(float zahl, int ziffer_vorgaenger)
{
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
int ergebnis_vorkomma = ((int) floor(zahl)) % 10;
int ergebnis_nachkomma = ((int) floor(zahl * 10) + 10) % 10;
int ergebnis_vorkomma = ((int) floor(zahl) + 10) % 10;
int ergebnis, ergebnis_rating;
if (ziffer_vorgaenger == -1)
@@ -182,7 +182,7 @@ int ClassFlowCNNGeneral::ZeigerEval(float zahl, int ziffer_vorgaenger)
if (ergebnis == -1)
ergebnis+=10;
ergebnis = ergebnis % 10;
ergebnis = (ergebnis + 10) % 10;
return ergebnis;
}
@@ -260,7 +260,7 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
neuroi->posy = std::stoi(zerlegt[2]);
neuroi->deltax = std::stoi(zerlegt[3]);
neuroi->deltay = std::stoi(zerlegt[4]);
neuroi->result = -1;
neuroi->result_float = -1;
neuroi->image = NULL;
neuroi->image_org = NULL;
}
@@ -271,13 +271,16 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
SaveAllFiles = true;
}
/*
if ((toUpper(zerlegt[0]) == "EXTENDEDRESOLUTION") && (zerlegt.size() > 1))
{
if (toUpper(zerlegt[1]) == "TRUE")
extendedResolution = true;
}
*/
}
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
{
@@ -482,6 +485,7 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
default:
printf("ERROR ERROR ERROR - tflite passt nicht zur Firmware - ERROR ERROR ERROR\n");
}
// flowpostprocessing->UpdateNachkommaDecimalShift();
}
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
@@ -503,27 +507,27 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
f1 = tflite->GetOutputValue(0);
f2 = tflite->GetOutputValue(1);
float result = fmod(atan2(f1, f2) / (M_PI * 2) + 2, 1);
GENERAL[_ana]->ROI[i]->result = result * 10;
printf("Result General(Analog)%i: %f\n", i, GENERAL[_ana]->ROI[i]->result);
GENERAL[_ana]->ROI[i]->result_float = result * 10;
printf("Result General(Analog)%i: %f\n", i, GENERAL[_ana]->ROI[i]->result_float);
if (isLogImage)
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, &GENERAL[_ana]->ROI[i]->result, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, &GENERAL[_ana]->ROI[i]->result_float, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
} break;
case Digital:
{
GENERAL[_ana]->ROI[i]->resultklasse = 0;
GENERAL[_ana]->ROI[i]->resultklasse = tflite->GetClassFromImageBasis(GENERAL[_ana]->ROI[i]->image);
printf("Result General(Digit)%i: %d\n", i, GENERAL[_ana]->ROI[i]->resultklasse);
GENERAL[_ana]->ROI[i]->result_klasse = 0;
GENERAL[_ana]->ROI[i]->result_klasse = tflite->GetClassFromImageBasis(GENERAL[_ana]->ROI[i]->image);
printf("Result General(Digit)%i: %d\n", i, GENERAL[_ana]->ROI[i]->result_klasse);
if (isLogImage)
{
if (isLogImageSelect)
{
if (LogImageSelect.find(GENERAL[_ana]->ROI[i]->name) != std::string::npos)
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, NULL, &GENERAL[_ana]->ROI[i]->resultklasse, time, GENERAL[_ana]->ROI[i]->image_org);
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
}
else
{
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, NULL, &GENERAL[_ana]->ROI[i]->resultklasse, time, GENERAL[_ana]->ROI[i]->image_org);
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
}
}
} break;
@@ -543,16 +547,16 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
if (debugdetailgeneral) LogFile.WriteToFile(_zwres);
if ((_num == 10) || (_nachkomma == 10)) // NaN detektiert
GENERAL[_ana]->ROI[i]->result = -1;
GENERAL[_ana]->ROI[i]->result_float = -1;
else
GENERAL[_ana]->ROI[i]->result = fmod((double) _num + (((double)_nachkomma)-5)/10 + (double) 10, 10);
GENERAL[_ana]->ROI[i]->result_float = fmod((double) _num + (((double)_nachkomma)-5)/10 + (double) 10, 10);
printf("Result General(DigitalHyprid)%i: %f\n", i, GENERAL[_ana]->ROI[i]->result);
_zwres = "Result General(DigitalHyprid)" + to_string(i) + ": " + to_string(GENERAL[_ana]->ROI[i]->result);
printf("Result General(DigitalHyprid)%i: %f\n", i, GENERAL[_ana]->ROI[i]->result_float);
_zwres = "Result General(DigitalHyprid)" + to_string(i) + ": " + to_string(GENERAL[_ana]->ROI[i]->result_float);
if (debugdetailgeneral) LogFile.WriteToFile(_zwres);
if (isLogImage)
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, &GENERAL[_ana]->ROI[i]->result, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, &GENERAL[_ana]->ROI[i]->result_float, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
} break;
default:
break;
@@ -567,7 +571,8 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
bool ClassFlowCNNGeneral::isExtendedResolution(int _number)
{
if (extendedResolution && !(CNNType == Digital))
// if (extendedResolution && !(CNNType == Digital))
if (!(CNNType == Digital))
return true;
return false;
@@ -600,13 +605,20 @@ std::vector<HTMLInfo*> ClassFlowCNNGeneral::GetHTMLInfo()
zw->filename_org = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg";
}
zw->val = GENERAL[_ana]->ROI[i]->result;
if (CNNType == Digital)
zw->val = GENERAL[_ana]->ROI[i]->result_klasse;
else
zw->val = GENERAL[_ana]->ROI[i]->result_float;
zw->image = GENERAL[_ana]->ROI[i]->image;
zw->image_org = GENERAL[_ana]->ROI[i]->image_org;
// printf("Push %s\n", zw->filename.c_str());
result.push_back(zw);
}
// printf("größe: %d\n", result.size());
return result;
}

View File

@@ -1,6 +1,10 @@
#pragma once
#include "ClassFlowImage.h"
#ifndef __CLASSCNNGENERAL__
#define __CLASSCNNGENERAL__
#include"ClassFlowDefineTypes.h"
#include "ClassFlowAlignment.h"
// #include "ClassFlowPostProcessing.h"
enum t_CNNType {
AutoDetect,
@@ -10,20 +14,6 @@ enum t_CNNType {
None
};
struct roi {
int posx, posy, deltax, deltay;
float result;
int resultklasse;
string name;
CImageBasis *image, *image_org;
};
struct general {
string name;
std::vector<roi*> ROI;
};
class ClassFlowCNNGeneral :
public ClassFlowImage
{
@@ -36,8 +26,9 @@ protected:
bool isLogImageSelect;
string LogImageSelect;
ClassFlowAlignment* flowpostalignment;
// ClassFlowPostProcessing *flowpostprocessing = NULL;
bool SaveAllFiles;
bool extendedResolution;
// bool extendedResolution;
int ZeigerEval(float zahl, int ziffer_vorgaenger);
int ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int eval_vorgaenger);
@@ -53,20 +44,22 @@ public:
bool doFlow(string time);
string getHTMLSingleStep(string host);
string getReadout(int _analog);
string getReadout(int _analog, bool _extendedResolution);
void DrawROI(CImageBasis *_zw);
std::vector<HTMLInfo*> GetHTMLInfo();
int AnzahlROIs(int _analog);
// int AnzahlROIs(int _analog);
int getAnzahlGENERAL();
general* GetGENERAL(int _analog);
general* GetGENERAL(string _name, bool _create);
general* FindGENERAL(string _name_number);
string getNameGENERAL(int _analog);
bool isExtendedResolution(int _number = 0);
bool isExtendedResolution(int _number = 0);
// void setPostprocessing(ClassFlowPostProcessing *_fpp){flowpostprocessing = _fpp;};
void UpdateNameNumbers(std::vector<std::string> *_name_numbers);
@@ -75,3 +68,5 @@ public:
string name(){return "ClassFlowCNNGeneral";};
};
#endif

View File

@@ -74,16 +74,13 @@ std::string ClassFlowControll::TranslateAktstatus(std::string _input)
}
std::vector<HTMLInfo*> ClassFlowControll::GetAllDigital()
std::vector<HTMLInfo*> ClassFlowControll::GetAllDigital()
{
/*
for (int i = 0; i < FlowControll.size(); ++i)
if (FlowControll[i]->name().compare("ClassFlowCNNGeneral") == 0)
return ((ClassFlowCNNGeneral*) (FlowControll[i]))->GetHTMLInfo();
*/
if (flowdigit)
flowdigit->GetHTMLInfo();
{
printf("ClassFlowControll::GetAllDigital - flowdigit != NULL\n");
return flowdigit->GetHTMLInfo();
}
std::vector<HTMLInfo*> empty;
return empty;
@@ -91,17 +88,8 @@ std::vector<HTMLInfo*> ClassFlowControll::GetAllDigital()
std::vector<HTMLInfo*> ClassFlowControll::GetAllAnalog()
{
/*
for (int i = 0; i < FlowControll.size(); ++i)
if (FlowControll[i]->name().compare("ClassFlowAnalog") == 0)
return ((ClassFlowAnalog*) (FlowControll[i]))->GetHTMLInfo();
std::vector<HTMLInfo*> empty;
return empty;
*/
if (flowanalog)
flowanalog->GetHTMLInfo();
return flowanalog->GetHTMLInfo();
std::vector<HTMLInfo*> empty;
return empty;
@@ -252,8 +240,8 @@ void ClassFlowControll::InitFlow(std::string config)
}
std::string ClassFlowControll::getActStatus(){
return aktstatus;
std::string* ClassFlowControll::getActStatus(){
return &aktstatus;
}
void ClassFlowControll::doFlowMakeImageOnly(string time){
@@ -297,7 +285,7 @@ bool ClassFlowControll::doFlow(string time)
if (!FlowControll[i]->doFlow(time)){
repeat++;
LogFile.WriteToFile("Fehler im vorheriger Schritt - wird zum " + to_string(repeat) + ". Mal wiederholt");
i = -1; // vorheriger Schritt muss wiederholt werden (vermutlich Bilder aufnehmen)
if (i) i -= 1; // vorheriger Schritt muss wiederholt werden (vermutlich Bilder aufnehmen)
result = false;
if (repeat > 5) {
LogFile.WriteToFile("Wiederholung 5x nicht erfolgreich --> reboot");
@@ -323,39 +311,41 @@ bool ClassFlowControll::doFlow(string time)
string ClassFlowControll::getReadoutAll(int _type)
{
std::vector<NumberPost*> numbers = flowpostprocessing->GetNumbers();
std::string out = "";
for (int i = 0; i < numbers.size(); ++i)
if (flowpostprocessing)
{
out = out + numbers[i]->name + "\t";
switch (_type) {
case READOUT_TYPE_VALUE:
out = out + numbers[i]->ReturnValueNoError;
break;
case READOUT_TYPE_PREVALUE:
if (flowpostprocessing->PreValueUse)
{
if (numbers[i]->PreValueOkay)
out = out + numbers[i]->ReturnPreValue;
else
out = out + "PreValue too old";
}
else
out = out + "PreValue deactivated";
break;
case READOUT_TYPE_RAWVALUE:
out = out + numbers[i]->ReturnRawValue;
break;
case READOUT_TYPE_ERROR:
out = out + numbers[i]->ErrorMessageText;
break;
}
if (i < numbers.size()-1)
out = out + "\r\n";
}
std::vector<NumberPost*> *numbers = flowpostprocessing->GetNumbers();
// printf("OUT: %s", out.c_str());
for (int i = 0; i < (*numbers).size(); ++i)
{
out = out + (*numbers)[i]->name + "\t";
switch (_type) {
case READOUT_TYPE_VALUE:
out = out + (*numbers)[i]->ReturnValueNoError;
break;
case READOUT_TYPE_PREVALUE:
if (flowpostprocessing->PreValueUse)
{
if ((*numbers)[i]->PreValueOkay)
out = out + (*numbers)[i]->ReturnPreValue;
else
out = out + "PreValue too old";
}
else
out = out + "PreValue deactivated";
break;
case READOUT_TYPE_RAWVALUE:
out = out + (*numbers)[i]->ReturnRawValue;
break;
case READOUT_TYPE_ERROR:
out = out + (*numbers)[i]->ErrorMessageText;
break;
}
if (i < (*numbers).size()-1)
out = out + "\r\n";
}
// printf("OUT: %s", out.c_str());
}
return out;
}
@@ -564,66 +554,60 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
{
_send = flowalignment->ImageBasis;
}
if (_fn == "alg_roi.jpg")
else
{
CImageBasis* _imgzw = new CImageBasis(flowalignment->ImageBasis);
flowalignment->DrawRef(_imgzw);
if (flowdigit) flowdigit->DrawROI(_imgzw);
if (flowanalog) flowanalog->DrawROI(_imgzw);
if (_fn == "alg_roi.jpg")
{
CImageBasis* _imgzw = new CImageBasis(flowalignment->ImageBasis);
flowalignment->DrawRef(_imgzw);
if (flowdigit) flowdigit->DrawROI(_imgzw);
if (flowanalog) flowanalog->DrawROI(_imgzw);
_send = _imgzw;
Dodelete = true;
}
else
{
std::vector<HTMLInfo*> htmlinfo;
htmlinfo = GetAllDigital();
for (int i = 0; i < htmlinfo.size(); ++i)
{
if (_fn == htmlinfo[i]->filename)
{
if (htmlinfo[i]->image)
_send = htmlinfo[i]->image;
}
if (_fn == htmlinfo[i]->filename_org)
{
if (htmlinfo[i]->image_org)
_send = htmlinfo[i]->image_org;
}
delete htmlinfo[i];
}
htmlinfo.clear();
/*/////////////////////////////////////
cimg_library::CImg<unsigned char> cimg(_imgzw->rgb_image, _imgzw->bpp, _imgzw->width, _imgzw->height, 1);
//Convert cimg type
// cimg.permute_axes("yzcx");
cimg.draw_text(300, 300, "Dies ist ein Test", "black");
if (!_send)
{
htmlinfo = GetAllAnalog();
for (int i = 0; i < htmlinfo.size(); ++i)
{
if (_fn == htmlinfo[i]->filename)
{
if (htmlinfo[i]->image)
_send = htmlinfo[i]->image;
}
if (_fn == htmlinfo[i]->filename_org)
{
if (htmlinfo[i]->image_org)
_send = htmlinfo[i]->image_org;
}
delete htmlinfo[i];
}
htmlinfo.clear();
//Convert back to stb type to save
// cimg.permute_axes("cxyz");
*////////////////////////////////////
_send = _imgzw;
Dodelete = true;
}
}
}
std::vector<HTMLInfo*> htmlinfo;
htmlinfo = GetAllDigital();
for (int i = 0; i < htmlinfo.size(); ++i)
{
if (_fn == htmlinfo[i]->filename)
{
if (htmlinfo[i]->image)
_send = htmlinfo[i]->image;
}
if (_fn == htmlinfo[i]->filename_org)
{
if (htmlinfo[i]->image_org)
_send = htmlinfo[i]->image_org;
}
delete htmlinfo[i];
}
htmlinfo.clear();
htmlinfo = GetAllAnalog();
for (int i = 0; i < htmlinfo.size(); ++i)
{
if (_fn == htmlinfo[i]->filename)
{
if (htmlinfo[i]->image)
_send = htmlinfo[i]->image;
}
if (_fn == htmlinfo[i]->filename_org)
{
if (htmlinfo[i]->image_org)
_send = htmlinfo[i]->image_org;
}
delete htmlinfo[i];
}
htmlinfo.clear();
if (_send)
{
ESP_LOGI(TAG, "Sending file : %s ...", _fn.c_str());

View File

@@ -1,11 +1,11 @@
#pragma once
#ifndef __FLOWCONTROLL__
#define __FLOWCONTROLL__
#include <string>
#include "ClassFlow.h"
#include "ClassFlowMakeImage.h"
#include "ClassFlowAlignment.h"
//#include "ClassFlowDigit.h"
#include "ClassFlowCNNGeneral.h"
#include "ClassFlowPostProcessing.h"
#include "ClassFlowMQTT.h"
@@ -60,7 +60,7 @@ public:
bool isAutoStart(long &_intervall);
std::string getActStatus();
std::string* getActStatus();
std::vector<HTMLInfo*> GetAllDigital();
std::vector<HTMLInfo*> GetAllAnalog();
@@ -73,4 +73,6 @@ public:
string name(){return "ClassFlowControll";};
};
#endif

View File

@@ -0,0 +1,52 @@
#ifndef __CLASSFLOWIMAGE_CLASS__
#define __CLASSFLOWIMAGE_CLASS__
#include "ClassFlowImage.h"
struct roi {
int posx, posy, deltax, deltay;
float result_float;
int result_klasse;
string name;
CImageBasis *image, *image_org;
};
struct general {
string name;
std::vector<roi*> ROI;
};
struct NumberPost {
float MaxRateValue;
bool useMaxRateValue;
bool ErrorMessage;
bool PreValueOkay;
bool AllowNegativeRates;
bool checkDigitIncreaseConsistency;
time_t lastvalue;
string timeStamp;
float FlowRateAct; // m3 / min
float PreValue; // letzter Wert, der gut ausgelesen wurde
float Value; // letzer ausgelesener Wert, inkl. Korrekturen
string ReturnRawValue; // Rohwert (mit N & führenden 0)
string ReturnValue; // korrigierter Rückgabewert, ggf. mit Fehlermeldung
string ReturnPreValue; // korrigierter Rückgabewert ohne Fehlermeldung
string ReturnValueNoError;
string ErrorMessageText; // Fehlermeldung bei Consistency Check
int AnzahlAnalog;
int AnzahlDigital;
int DecimalShift;
int DecimalShiftInitial;
int Nachkomma;
bool isExtendedResolution;
general *digit_roi;
general *analog_roi;
string name;
};
#endif

View File

@@ -154,16 +154,16 @@ bool ClassFlowMQTT::doFlow(string zwtime)
if (flowpostprocessing)
{
std::vector<NumberPost*> NUMBERS = flowpostprocessing->GetNumbers();
std::vector<NumberPost*>* NUMBERS = flowpostprocessing->GetNumbers();
for (int i = 0; i < NUMBERS.size(); ++i)
for (int i = 0; i < (*NUMBERS).size(); ++i)
{
result = NUMBERS[i]->ReturnValueNoError;
resulterror = NUMBERS[i]->ErrorMessageText;
resultrate = std::to_string(NUMBERS[i]->FlowRateAct);
resulttimestamp = NUMBERS[i]->timeStamp;
result = (*NUMBERS)[i]->ReturnValueNoError;
resulterror = (*NUMBERS)[i]->ErrorMessageText;
resultrate = std::to_string((*NUMBERS)[i]->FlowRateAct);
resulttimestamp = (*NUMBERS)[i]->timeStamp;
namenumber = NUMBERS[i]->name;
namenumber = (*NUMBERS)[i]->name;
if (namenumber == "default")
namenumber = maintopic + "/";
else

View File

@@ -1,5 +1,4 @@
#include "ClassFlowPostProcessing.h"
#include "Helper.h"
#include "ClassFlowMakeImage.h"
#include "ClassLogFile.h"
@@ -124,7 +123,7 @@ bool ClassFlowPostProcessing::LoadPreValue(void)
if (NUMBERS[j]->digit_roi || NUMBERS[j]->analog_roi)
{
NUMBERS[j]->ReturnValue = RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->AnzahlAnalog - NUMBERS[j]->DecimalShift);
NUMBERS[j]->ReturnValue = RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->Nachkomma);
NUMBERS[j]->ReturnValueNoError = NUMBERS[j]->ReturnValue;
}
}
@@ -186,7 +185,7 @@ bool ClassFlowPostProcessing::LoadPreValue(void)
if (NUMBERS[0]->digit_roi || NUMBERS[0]->analog_roi)
{
NUMBERS[0]->ReturnValue = RundeOutput(NUMBERS[0]->Value, NUMBERS[0]->AnzahlAnalog - NUMBERS[0]->DecimalShift);
NUMBERS[0]->ReturnValue = RundeOutput(NUMBERS[0]->Value, NUMBERS[0]->Nachkomma);
NUMBERS[0]->ReturnValueNoError = NUMBERS[0]->ReturnValue;
}
@@ -241,7 +240,6 @@ ClassFlowPostProcessing::ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc, C
flowAnalog = _analog;
flowDigit = _digit;
for (int i = 0; i < ListFlowControll->size(); ++i)
{
if (((*ListFlowControll)[i])->name().compare("ClassFlowMakeImage") == 0)
@@ -251,6 +249,36 @@ ClassFlowPostProcessing::ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc, C
}
}
void ClassFlowPostProcessing::handleDecimalExtendedResolution(string _decsep, string _value)
{
string _digit, _decpos;
int _pospunkt = _decsep.find_first_of(".");
// printf("Name: %s, Pospunkt: %d\n", _decsep.c_str(), _pospunkt);
if (_pospunkt > -1)
_digit = _decsep.substr(0, _pospunkt);
else
_digit = "default";
for (int j = 0; j < NUMBERS.size(); ++j)
{
bool _zwdc = false;
if (toUpper(_value) == "TRUE")
_zwdc = true;
if (_digit == "default") // erstmal auf default setzen (falls sonst nichts gesetzt)
{
NUMBERS[j]->isExtendedResolution = _zwdc;
}
if (NUMBERS[j]->name == _digit)
{
NUMBERS[j]->isExtendedResolution = _zwdc;
}
}
}
void ClassFlowPostProcessing::handleDecimalSeparator(string _decsep, string _value)
{
string _digit, _decpos;
@@ -265,15 +293,15 @@ void ClassFlowPostProcessing::handleDecimalSeparator(string _decsep, string _val
{
int _zwdc = 0;
try
// try
{
_zwdc = stoi(_value);
}
catch(const std::exception& e)
/* catch(const std::exception& e)
{
printf("ERROR - Decimalshift is not a number: %s\n", _value.c_str());
}
*/
if (_digit == "default") // erstmal auf default setzen (falls sonst nichts gesetzt)
{
NUMBERS[j]->DecimalShift = _zwdc;
@@ -304,15 +332,15 @@ void ClassFlowPostProcessing::handleMaxRateValue(string _decsep, string _value)
{
float _zwdc = 1;
try
// try
{
_zwdc = stof(_value);
}
catch(const std::exception& e)
/* catch(const std::exception& e)
{
printf("ERROR - MaxRateValue is not a number: %s\n", _value.c_str());
}
*/
if (_digit == "default") // erstmal auf default setzen (falls sonst nichts gesetzt)
{
@@ -352,6 +380,11 @@ bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph)
zerlegt = this->ZerlegeZeile(aktparamgraph);
std::string _param = GetParameterName(zerlegt[0]);
if ((toUpper(_param) == "EXTENDEDRESOLUTION") && (zerlegt.size() > 1))
{
handleDecimalExtendedResolution(zerlegt[0], zerlegt[1]);
}
if ((toUpper(_param) == "DECIMALSHIFT") && (zerlegt.size() > 1))
{
handleDecimalSeparator(zerlegt[0], zerlegt[1]);
@@ -463,6 +496,7 @@ void ClassFlowPostProcessing::InitNUMBERS()
_number->useMaxRateValue = false;
_number->DecimalShift = 0;
_number->DecimalShiftInitial = 0;
_number->isExtendedResolution = false;
_number->FlowRateAct = 0; // m3 / min
@@ -557,26 +591,23 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
NUMBERS[j]->ReturnRawValue = "";
NUMBERS[j]->ErrorMessageText = "";
if (flowAnalog) NUMBERS[j]->AnzahlAnalog = flowAnalog->AnzahlROIs(j);
if (flowDigit) NUMBERS[j]->AnzahlDigital = flowDigit->AnzahlROIs(j);
if (flowDigit->isExtendedResolution())
NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShiftInitial - 1;
NUMBERS[j]->Nachkomma = NUMBERS[j]->AnzahlAnalog - NUMBERS[j]->DecimalShift;
UpdateNachkommaDecimalShift();
if (NUMBERS[j]->digit_roi)
NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j);
{
if (NUMBERS[j]->analog_roi)
NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, false);
else
NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, NUMBERS[j]->isExtendedResolution); // Extended Resolution nur falls es keine analogen Ziffern gibt
}
if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi)
NUMBERS[j]->ReturnRawValue = NUMBERS[j]->ReturnRawValue + ".";
if (NUMBERS[j]->analog_roi)
NUMBERS[j]->ReturnRawValue = NUMBERS[j]->ReturnRawValue + flowAnalog->getReadout(j);
NUMBERS[j]->ReturnRawValue = NUMBERS[j]->ReturnRawValue + flowAnalog->getReadout(j, NUMBERS[j]->isExtendedResolution);
NUMBERS[j]->ReturnRawValue = ShiftDecimal(NUMBERS[j]->ReturnRawValue, NUMBERS[j]->DecimalShift);
///////////////// SPEZIALFALL für User Gustl ///////////////////////////////////////////////////////
if (IgnoreLeadingNaN)
{
while ((NUMBERS[j]->ReturnRawValue.length() > 1) && (NUMBERS[j]->ReturnRawValue[0] == 'N'))
@@ -584,7 +615,6 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
NUMBERS[j]->ReturnRawValue.erase(0, 1);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
rohwert = NUMBERS[j]->ReturnRawValue;
@@ -622,13 +652,13 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
NUMBERS[j]->Value = checkDigitConsistency(NUMBERS[j]->Value, NUMBERS[j]->DecimalShift, NUMBERS[j]->analog_roi != NULL, NUMBERS[j]->PreValue);
}
zwvalue = RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->AnzahlAnalog - NUMBERS[j]->DecimalShift);
zwvalue = RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->Nachkomma);
if ((!NUMBERS[j]->AllowNegativeRates) && (NUMBERS[j]->Value < NUMBERS[j]->PreValue))
{
NUMBERS[j]->ErrorMessageText = NUMBERS[j]->ErrorMessageText + "Neg. Rate - Read: " + zwvalue + " - Raw: " + NUMBERS[j]->ReturnRawValue + " - Pre: " + RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma) + " ";
NUMBERS[j]->Value = NUMBERS[j]->PreValue;
zwvalue = RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->AnzahlAnalog - NUMBERS[j]->DecimalShift);
zwvalue = RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->Nachkomma);
}
if (NUMBERS[j]->useMaxRateValue && (abs(NUMBERS[j]->Value - NUMBERS[j]->PreValue) > NUMBERS[j]->MaxRateValue))
@@ -663,6 +693,49 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
return true;
}
void ClassFlowPostProcessing::UpdateNachkommaDecimalShift()
{
for (int j = 0; j < NUMBERS.size(); ++j)
{
if (NUMBERS[j]->digit_roi && !NUMBERS[j]->analog_roi) // es gibt nur digitale ziffern
{
// printf("Nurdigital\n");
NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShiftInitial;
if (NUMBERS[j]->isExtendedResolution && flowDigit->isExtendedResolution()) // extended resolution ist an und soll auch bei dieser Ziffer verwendet werden
NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShift-1;
NUMBERS[j]->Nachkomma = -NUMBERS[j]->DecimalShift;
}
if (!NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi) // es gibt nur analoge ziffern
{
// printf("Nur analog\n");
NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShiftInitial;
if (NUMBERS[j]->isExtendedResolution && flowAnalog->isExtendedResolution()) // extended resolution ist an und soll auch bei dieser Ziffer verwendet werden
NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShift-1;
NUMBERS[j]->Nachkomma = -NUMBERS[j]->DecimalShift;
}
if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi) // digital + analog
{
// printf("Nur digital + analog\n");
NUMBERS[j]->Nachkomma = NUMBERS[j]->analog_roi->ROI.size();
NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShiftInitial;
if (NUMBERS[j]->isExtendedResolution && flowAnalog->isExtendedResolution()) // extended resolution ist an und soll auch bei dieser Ziffer verwendet werden
NUMBERS[j]->Nachkomma = NUMBERS[j]->Nachkomma+1;
}
printf("UpdateNachkommaDecShift NUMBER%i: Nachkomma %i, DecShift %i\n", j, NUMBERS[j]->Nachkomma,NUMBERS[j]->DecimalShift);
}
}
string ClassFlowPostProcessing::getReadout(int _number)
{
return NUMBERS[_number]->ReturnValue;

View File

@@ -1,45 +1,13 @@
#pragma once
#ifndef __FLOWPOSTPROCESSING__
#define __FLOWPOSTPROCESSING__
#include "ClassFlow.h"
#include "ClassFlowMakeImage.h"
#include "ClassFlowCNNGeneral.h"
#include "ClassFlowCNNGeneral.h"
#include "ClassFlowDefineTypes.h"
#include <string>
struct NumberPost {
float MaxRateValue;
bool useMaxRateValue;
bool ErrorMessage;
bool PreValueOkay;
bool AllowNegativeRates;
bool checkDigitIncreaseConsistency;
time_t lastvalue;
string timeStamp;
float FlowRateAct; // m3 / min
float PreValue; // letzter Wert, der gut ausgelesen wurde
float Value; // letzer ausgelesener Wert, inkl. Korrekturen
string ReturnRawValue; // Rohwert (mit N & führenden 0)
string ReturnValue; // korrigierter Rückgabewert, ggf. mit Fehlermeldung
string ReturnPreValue; // korrigierter Rückgabewert ohne Fehlermeldung
string ReturnValueNoError;
string ErrorMessageText; // Fehlermeldung bei Consistency Check
int AnzahlAnalog;
int AnzahlDigital;
int DecimalShift;
int DecimalShiftInitial;
int Nachkomma;
general *digit_roi;
general *analog_roi;
string name;
};
class ClassFlowPostProcessing :
public ClassFlow
{
@@ -70,6 +38,8 @@ protected:
void InitNUMBERS();
void handleDecimalSeparator(string _decsep, string _value);
void handleMaxRateValue(string _decsep, string _value);
void handleDecimalExtendedResolution(string _decsep, string _value);
public:
@@ -86,8 +56,13 @@ public:
void SavePreValue();
string GetPreValue(std::string _number = "");
void SetPreValue(float zw, string _numbers, bool _extern = false);
std::vector<NumberPost*> GetNumbers(){return NUMBERS;};
void UpdateNachkommaDecimalShift();
std::vector<NumberPost*>* GetNumbers(){return &NUMBERS;};
string name(){return "ClassFlowPostProcessing";};
};
#endif

View File

@@ -83,17 +83,20 @@ FILE* OpenFileAndWait(const char* nm, const char* _mode, int _waitsec)
printf("open config file %s in mode %s\n", nm, _mode);
FILE *pfile = fopen(nm, _mode);
/*
if (pfile == NULL)
{
TickType_t xDelay;
xDelay = _waitsec * 1000 / portTICK_PERIOD_MS;
std::string zw = "File is locked: " + std::string(nm) + " - wait for " + std::to_string(_waitsec);
std::string zw = "File is locked: " + std::string(nm) + " - wait for " + std::to_string(_waitsec) + " seconds";
printf(zw.c_str());
printf("\n");
LogFile.WriteToFile(zw);
vTaskDelay( xDelay );
pfile = fopen(nm, _mode);
}
*/
return pfile;
}

View File

@@ -69,32 +69,6 @@ int CTfLiteClass::GetOutClassification(int _von, int _bis)
return (zw_class - _von);
}
/*
int CTfLiteClass::GetOutClassification()
{
TfLiteTensor* output2 = interpreter->output(0);
float zw_max = 0;
float zw;
int zw_class = -1;
if (output2 == NULL)
return -1;
int numeroutput = output2->dims->data[1];
for (int i = 0; i < numeroutput; ++i)
{
zw = output2->data.f[i];
if (zw > zw_max)
{
zw_max = zw;
zw_class = i;
}
}
return zw_class;
}
*/
void CTfLiteClass::GetInputDimension(bool silent = false)
{
TfLiteTensor* input2 = this->interpreter->input(0);
@@ -283,7 +257,7 @@ CTfLiteClass::CTfLiteClass()
this->interpreter = nullptr;
this->input = nullptr;
this->output = nullptr;
this->kTensorArenaSize = 200 * 1024; /// laut testfile: 108000 - bisher 600
this->kTensorArenaSize = 800 * 1024; /// laut testfile: 108000 - bisher 600;; 2021-09-11: 200 * 1024
this->tensor_arena = new uint8_t[kTensorArenaSize];
}

View File

@@ -28,9 +28,6 @@ ClassFlowControll tfliteflow;
TaskHandle_t xHandleblink_task_doFlow = NULL;
TaskHandle_t xHandletask_autodoFlow = NULL;
bool flowisrunning = false;
long auto_intervall = 0;
@@ -283,48 +280,48 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req)
txt = txt + "Digital Counter: <p> ";
httpd_resp_sendstr_chunk(req, txt.c_str());
std::vector<HTMLInfo*> htmlinfo;
htmlinfo = tfliteflow.GetAllDigital();
printf("Size of htmlinfo: %i\n", htmlinfo.size());
for (int i = 0; i < htmlinfo.size(); ++i)
std::vector<HTMLInfo*> htmlinfodig;
htmlinfodig = tfliteflow.GetAllDigital();
for (int i = 0; i < htmlinfodig.size(); ++i)
{
if (tfliteflow.GetTypeDigital() == Digital)
{
if (htmlinfo[i]->val == 10)
if (htmlinfodig[i]->val == 10)
zw = "NaN";
else
zw = to_string((int) htmlinfo[i]->val);
zw = to_string((int) htmlinfodig[i]->val);
txt = "<img src=\"/img_tmp/" + htmlinfo[i]->filename + "\"> " + zw;
txt = "<img src=\"/img_tmp/" + htmlinfodig[i]->filename + "\"> " + zw;
}
else
{
std::stringstream stream;
stream << std::fixed << std::setprecision(1) << htmlinfo[i]->val;
stream << std::fixed << std::setprecision(1) << htmlinfodig[i]->val;
zw = stream.str();
txt = "<img src=\"/img_tmp/" + htmlinfo[i]->filename + "\"> " + zw;
txt = "<img src=\"/img_tmp/" + htmlinfodig[i]->filename + "\"> " + zw;
}
httpd_resp_sendstr_chunk(req, txt.c_str());
delete htmlinfo[i];
delete htmlinfodig[i];
}
htmlinfo.clear();
htmlinfodig.clear();
txt = " <p> Analog Meter: <p> ";
httpd_resp_sendstr_chunk(req, txt.c_str());
htmlinfo = tfliteflow.GetAllAnalog();
for (int i = 0; i < htmlinfo.size(); ++i)
std::vector<HTMLInfo*> htmlinfoana;
htmlinfoana = tfliteflow.GetAllAnalog();
for (int i = 0; i < htmlinfoana.size(); ++i)
{
std::stringstream stream;
stream << std::fixed << std::setprecision(1) << htmlinfo[i]->val;
stream << std::fixed << std::setprecision(1) << htmlinfoana[i]->val;
zw = stream.str();
txt = "<img src=\"/img_tmp/" + htmlinfo[i]->filename + "\"> " + zw;
txt = "<img src=\"/img_tmp/" + htmlinfoana[i]->filename + "\"> " + zw;
httpd_resp_sendstr_chunk(req, txt.c_str());
delete htmlinfo[i];
delete htmlinfoana[i];
}
htmlinfo.clear();
htmlinfoana.clear();
}
@@ -506,35 +503,6 @@ esp_err_t handler_editflow(httpd_req_t *req)
httpd_resp_sendstr_chunk(req, zw.c_str());
}
/*
if (_task.compare("test_analog") == 0)
{
std::string _host = "";
if (httpd_query_key_value(_query, "host", _valuechar, 30) == ESP_OK) {
_host = std::string(_valuechar);
}
// printf("Parameter host: "); printf(_host.c_str()); printf("\n");
// string zwzw = "Do " + _task + " start\n"; printf(zwzw.c_str());
std::string zw = tfliteflow.doSingleStep("[Analog]", _host);
httpd_resp_sendstr_chunk(req, zw.c_str());
}
*/
/*
if (_task.compare("test_digits") == 0)
{
std::string _host = "";
if (httpd_query_key_value(_query, "host", _valuechar, 30) == ESP_OK) {
_host = std::string(_valuechar);
}
// printf("Parameter host: "); printf(_host.c_str()); printf("\n");
// string zwzw = "Do " + _task + " start\n"; printf(zwzw.c_str());
std::string zw = tfliteflow.doSingleStep("[Digits]", _host);
httpd_resp_sendstr_chunk(req, zw.c_str());
}
*/
/* Respond with an empty chunk to signal HTTP response completion */
httpd_resp_sendstr_chunk(req, NULL);
@@ -553,15 +521,13 @@ esp_err_t handler_statusflow(httpd_req_t *req)
#endif
const char* resp_str;
string zw;
#ifdef DEBUG_DETAIL_ON
printf("handler_prevalue:\n"); printf(req->uri); printf("\n");
#endif
zw = tfliteflow.getActStatus();
resp_str = zw.c_str();
string* zw = tfliteflow.getActStatus();
resp_str = zw->c_str();
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
httpd_resp_send(req, resp_str, strlen(resp_str));

View File

@@ -50,14 +50,14 @@ std::string std_hostname = "watermeter";
std::string ipadress = "";
std::string ssid = "";
std::string getIPAddress()
std::string* getIPAddress()
{
return ipadress;
return &ipadress;
}
std::string getSSID()
std::string* getSSID()
{
return ssid;
return &ssid;
}

View File

@@ -7,8 +7,8 @@ void wifi_init_sta(const char *_ssid, const char *_password, const char *_hostna
void wifi_init_sta(const char *_ssid, const char *_password, const char *_hostname);
void wifi_init_sta(const char *_ssid, const char *_password);
std::string getIPAddress();
std::string getSSID();
std::string* getIPAddress();
std::string* getSSID();
extern std::string hostname;
extern std::string std_hostname;

Binary file not shown.

Binary file not shown.

132
code/main/Color.cpp Normal file
View File

@@ -0,0 +1,132 @@
#include "Color.h"
#include <algorithm>
#include <cmath>
#include <cassert>
namespace {
// Int -> fixed point
int up( int x ) { return x * 255; }
} // namespace
int iRgbSqrt( int num ) {
// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_.28base_2.29
assert( "sqrt input should be non-negative" && num >= 0 );
assert( "sqrt input should no exceed 16 bits" && num <= 0xFFFF );
int res = 0;
int bit = 1 << 16;
while ( bit > num )
bit >>= 2;
while ( bit != 0 ) {
if ( num >= res + bit ) {
num -= res + bit;
res = ( res >> 1 ) + bit;
} else
res >>= 1;
bit >>= 2;
}
return res;
}
Rgb::Rgb( Hsv y ) {
// https://stackoverflow.com/questions/24152553/hsv-to-rgb-and-back-without-floating-point-math-in-python
// greyscale
if( y.s == 0 ) {
r = g = b = y.v;
return;
}
const int region = y.h / 43;
const int remainder = ( y.h - ( region * 43 ) ) * 6;
const int p = ( y.v * ( 255 - y.s ) ) >> 8;
const int q = ( y.v * ( 255 - ( ( y.s * remainder ) >> 8 ) ) ) >> 8;
const int t = ( y.v * ( 255 - ( ( y.s * (255 -remainder ) ) >> 8 ) ) ) >> 8;
switch( region ) {
case 0: r = y.v; g = t; b = p; break;
case 1: r = q; g = y.v; b = p; break;
case 2: r = p; g = y.v; b = t; break;
case 3: r = p; g = q; b = y.v; break;
case 4: r = t; g = p; b = y.v; break;
case 5: r = y.v; g = p; b = q; break;
default: __builtin_trap();
}
a = y.a;
}
Rgb& Rgb::operator=( Hsv hsv ) {
Rgb r{ hsv };
swap( r );
return *this;
}
Rgb Rgb::operator+( Rgb in ) const {
auto copy = *this;
copy += in;
return copy;
}
Rgb& Rgb::operator+=( Rgb in ) {
unsigned int red = r + in.r;
r = ( red < 255 ) ? red : 255;
unsigned int green = g + in.g;
g = ( green < 255 ) ? green : 255;
unsigned int blue = b + in.b;
b = ( blue < 255 ) ? blue : 255;
return *this;
}
Rgb& Rgb::blend( Rgb in ) {
unsigned int inAlpha = in.a * ( 255 - a );
unsigned int alpha = a + inAlpha;
r = iRgbSqrt( ( ( r * r * a ) + ( in.r * in.r * inAlpha ) ) / alpha );
g = iRgbSqrt( ( ( g * g * a ) + ( in.g * in.g * inAlpha ) ) / alpha );
b = iRgbSqrt( ( ( b * b * a ) + ( in.b * in.b * inAlpha ) ) / alpha );
a = alpha;
return *this;
}
uint8_t IRAM_ATTR Rgb::getGrb( int idx ) {
switch ( idx ) {
case 0: return g;
case 1: return r;
case 2: return b;
}
__builtin_unreachable();
}
Hsv::Hsv( Rgb r ) {
int min = std::min( r.r, std::min( r.g, r.b ) );
int max = std::max( r.r, std::max( r.g, r.b ) );
int chroma = max - min;
v = max;
if ( chroma == 0 ) {
h = s = 0;
return;
}
s = up( chroma ) / max;
int hh;
if ( max == r.r )
hh = ( up( int( r.g ) - int( r.b ) ) ) / chroma / 6;
else if ( max == r.g )
hh = 255 / 3 + ( up( int( r.b ) - int( r.r ) ) ) / chroma / 6;
else
hh = 2 * 255 / 3 + ( up( int( r.r ) - int( r.g ) ) ) / chroma / 6;
if ( hh < 0 )
hh += 255;
h = hh;
a = r.a;
}
Hsv& Hsv::operator=( Rgb rgb ) {
Hsv h{ rgb };
swap( h );
return *this;
}

69
code/main/Color.h Normal file
View File

@@ -0,0 +1,69 @@
#pragma once
#include <cstdint>
#include "esp_attr.h"
union Hsv;
union Rgb {
struct __attribute__ ((packed)) {
uint8_t r, g, b, a;
};
uint32_t value;
Rgb( uint8_t r = 0, uint8_t g = 0, uint8_t b = 0, uint8_t a = 255 ) : r( r ), g( g ), b( b ), a( a ) {}
Rgb( Hsv c );
Rgb& operator=( Rgb rgb ) { swap( rgb ); return *this; }
Rgb& operator=( Hsv hsv );
Rgb operator+( Rgb in ) const;
Rgb& operator+=( Rgb in );
bool operator==( Rgb in ) const { return in.value == value; }
Rgb& blend( Rgb in );
void swap( Rgb& o ) { value = o.value; }
void linearize() {
r = channelGamma(r);
g = channelGamma(g);
b = channelGamma(b);
}
uint8_t IRAM_ATTR getGrb( int idx );
void stretchChannels( uint8_t maxR, uint8_t maxG, uint8_t maxB ) {
r = stretch( r, maxR );
g = stretch( g, maxG );
b = stretch( b, maxB );
}
void stretchChannelsEvenly( uint8_t max ) {
stretchChannels( max, max, max );
}
private:
uint8_t stretch( int value, uint8_t max ) {
return ( value * max ) >> 8;
}
uint8_t channelGamma( int channel ) {
/* The optimal gamma correction is x^2.8. However, this is expensive to
* compute. Therefore, we use x^3 for gamma correction. Also, we add a
* bias as the WS2812 LEDs do not turn on for values less than 4. */
if (channel == 0)
return channel;
channel = channel * channel * channel * 251;
channel >>= 24;
return static_cast< uint8_t >( 4 + channel );
}
};
union Hsv {
struct __attribute__ ((packed)) {
uint8_t h, s, v, a;
};
uint32_t value;
Hsv( uint8_t h, uint8_t s = 0, uint8_t v = 0, uint8_t a = 255 ) : h( h ), s( s ), v( v ), a( a ) {}
Hsv( Rgb r );
Hsv& operator=( Hsv h ) { swap( h ); return *this; }
Hsv& operator=( Rgb rgb );
bool operator==( Hsv in ) const { return in.value == value; }
void swap( Hsv& o ) { value = o.value; }
};

63
code/main/SmartLeds.cpp Normal file
View File

@@ -0,0 +1,63 @@
#include "SmartLeds.h"
IsrCore SmartLed::_interruptCore = CoreCurrent;
intr_handle_t SmartLed::_interruptHandle = NULL;
SmartLed*& IRAM_ATTR SmartLed::ledForChannel( int channel ) {
static SmartLed* table[8] = { nullptr };
assert( channel < 8 );
return table[ channel ];
}
void IRAM_ATTR SmartLed::interruptHandler(void*) {
for (int channel = 0; channel != 8; channel++) {
auto self = ledForChannel( channel );
if ( RMT.int_st.val & (1 << (24 + channel ) ) ) { // tx_thr_event
if ( self )
self->copyRmtHalfBlock();
RMT.int_clr.val |= 1 << ( 24 + channel );
} else if ( RMT.int_st.val & ( 1 << (3 * channel ) ) ) { // tx_end
if ( self )
xSemaphoreGiveFromISR( self->_finishedFlag, nullptr );
RMT.int_clr.val |= 1 << ( 3 * channel );
}
}
}
void IRAM_ATTR SmartLed::copyRmtHalfBlock() {
int offset = detail::MAX_PULSES * _halfIdx;
_halfIdx = !_halfIdx;
int len = 3 - _componentPosition + 3 * ( _count - 1 );
len = std::min( len, detail::MAX_PULSES / 8 );
if ( !len ) {
for ( int i = 0; i < detail::MAX_PULSES; i++) {
RMTMEM.chan[ _channel].data32[i + offset ].val = 0;
}
}
int i;
for ( i = 0; i != len && _pixelPosition != _count; i++ ) {
uint8_t val = _buffer[ _pixelPosition ].getGrb( _componentPosition );
for ( int j = 0; j != 8; j++, val <<= 1 ) {
int bit = val >> 7;
int idx = i * 8 + offset + j;
RMTMEM.chan[ _channel ].data32[ idx ].val = _bitToRmt[ bit & 0x01 ].value;
}
if ( _pixelPosition == _count - 1 && _componentPosition == 2 ) {
RMTMEM.chan[ _channel ].data32[ i * 8 + offset + 7 ].duration1 =
_timing.TRS / ( detail::RMT_DURATION_NS * detail::DIVIDER );
}
_componentPosition++;
if ( _componentPosition == 3 ) {
_componentPosition = 0;
_pixelPosition++;
}
}
for ( i *= 8; i != detail::MAX_PULSES; i++ ) {
RMTMEM.chan[ _channel ].data32[ i + offset ].val = 0;
}
}

530
code/main/SmartLeds.h Normal file
View File

@@ -0,0 +1,530 @@
#pragma once
/*
* A C++ driver for the WS2812 LEDs using the RMT peripheral on the ESP32.
*
* Jan "yaqwsx" Mrázek <email@honzamrazek.cz>
*
* Based on the work by Martin F. Falatic - https://github.com/FozzTexx/ws2812-demo
*/
/*
* 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 <memory>
#include <cassert>
#include <cstring>
#if defined ( ARDUINO )
extern "C" { // ...someone forgot to put in the includes...
#include "esp32-hal.h"
#include "esp_intr_alloc.h"
#include "esp_ipc.h"
#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
#include "freertos/semphr.h"
#include "soc/rmt_struct.h"
#include <driver/spi_master.h>
#include "esp_idf_version.h"
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL( 4, 0, 0 )
#include "soc/dport_reg.h"
#endif
}
#elif defined ( ESP_PLATFORM )
extern "C" { // ...someone forgot to put in the includes...
#include <esp_intr_alloc.h>
#include <esp_ipc.h>
#include <driver/gpio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <soc/dport_reg.h>
#include <soc/gpio_sig_map.h>
#include <soc/rmt_struct.h>
#include <driver/spi_master.h>
}
#include <stdio.h>
#endif
#include "Color.h"
namespace detail {
struct TimingParams {
uint32_t T0H;
uint32_t T1H;
uint32_t T0L;
uint32_t T1L;
uint32_t TRS;
};
union RmtPulsePair {
struct {
int duration0:15;
int level0:1;
int duration1:15;
int level1:1;
};
uint32_t value;
};
static const int DIVIDER = 4; // 8 still seems to work, but timings become marginal
static const int MAX_PULSES = 32; // A channel has a 64 "pulse" buffer - we use half per pass
static const double RMT_DURATION_NS = 12.5; // minimum time of a single RMT duration based on clock ns
} // namespace detail
using LedType = detail::TimingParams;
static const LedType LED_WS2812 = { 350, 700, 800, 600, 50000 };
static const LedType LED_WS2812B = { 400, 850, 850, 400, 50100 };
static const LedType LED_SK6812 = { 300, 600, 900, 600, 80000 };
static const LedType LED_WS2813 = { 350, 800, 350, 350, 300000 };
enum BufferType { SingleBuffer = 0, DoubleBuffer };
enum IsrCore { CoreFirst = 0, CoreSecond = 1, CoreCurrent = 2};
class SmartLed {
public:
// The RMT interrupt must not run on the same core as WiFi interrupts, otherwise SmartLeds
// can't fill the RMT buffer fast enough, resulting in rendering artifacts.
// Usually, that means you have to set isrCore == CoreSecond.
//
// If you use anything other than CoreCurrent, the FreeRTOS scheduler MUST be already running,
// so you can't use it if you define SmartLed as global variable.
SmartLed( const LedType& type, int count, int pin, int channel = 0, BufferType doubleBuffer = SingleBuffer, IsrCore isrCore = CoreCurrent)
: _timing( type ),
_channel( channel ),
_count( count ),
_firstBuffer( new Rgb[ count ] ),
_secondBuffer( doubleBuffer ? new Rgb[ count ] : nullptr ),
_finishedFlag( xSemaphoreCreateBinary() )
{
assert( channel >= 0 && channel < 8 );
assert( ledForChannel( channel ) == nullptr );
xSemaphoreGive( _finishedFlag );
DPORT_SET_PERI_REG_MASK( DPORT_PERIP_CLK_EN_REG, DPORT_RMT_CLK_EN );
DPORT_CLEAR_PERI_REG_MASK( DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST );
PIN_FUNC_SELECT( GPIO_PIN_MUX_REG[ pin ], 2 );
gpio_set_direction( static_cast< gpio_num_t >( pin ), GPIO_MODE_OUTPUT );
gpio_matrix_out( static_cast< gpio_num_t >( pin ), RMT_SIG_OUT0_IDX + _channel, 0, 0 );
initChannel( _channel );
RMT.tx_lim_ch[ _channel ].limit = detail::MAX_PULSES;
RMT.int_ena.val |= 1 << ( 24 + _channel );
RMT.int_ena.val |= 1 << ( 3 * _channel );
_bitToRmt[ 0 ].level0 = 1;
_bitToRmt[ 0 ].level1 = 0;
_bitToRmt[ 0 ].duration0 = _timing.T0H / ( detail::RMT_DURATION_NS * detail::DIVIDER );
_bitToRmt[ 0 ].duration1 = _timing.T0L / ( detail::RMT_DURATION_NS * detail::DIVIDER );
_bitToRmt[ 1 ].level0 = 1;
_bitToRmt[ 1 ].level1 = 0;
_bitToRmt[ 1 ].duration0 = _timing.T1H / ( detail::RMT_DURATION_NS * detail::DIVIDER );
_bitToRmt[ 1 ].duration1 = _timing.T1L / ( detail::RMT_DURATION_NS * detail::DIVIDER );
if ( !anyAlive() ) {
_interruptCore = isrCore;
if(isrCore != CoreCurrent) {
ESP_ERROR_CHECK(esp_ipc_call_blocking(isrCore, registerInterrupt, NULL));
} else {
registerInterrupt(NULL);
}
}
ledForChannel( channel ) = this;
}
~SmartLed() {
ledForChannel( _channel ) = nullptr;
if ( !anyAlive() ) {
if(_interruptCore != CoreCurrent) {
ESP_ERROR_CHECK(esp_ipc_call_blocking(_interruptCore, unregisterInterrupt, NULL));
} else {
unregisterInterrupt(NULL);
}
}
vSemaphoreDelete( _finishedFlag );
}
Rgb& operator[]( int idx ) {
return _firstBuffer[ idx ];
}
const Rgb& operator[]( int idx ) const {
return _firstBuffer[ idx ];
}
void show() {
_buffer = _firstBuffer.get();
startTransmission();
swapBuffers();
}
bool wait( TickType_t timeout = portMAX_DELAY ) {
if( xSemaphoreTake( _finishedFlag, timeout ) == pdTRUE ) {
xSemaphoreGive( _finishedFlag );
return true;
}
return false;
}
int size() const {
return _count;
}
Rgb *begin() { return _firstBuffer.get(); }
const Rgb *begin() const { return _firstBuffer.get(); }
const Rgb *cbegin() const { return _firstBuffer.get(); }
Rgb *end() { return _firstBuffer.get() + _count; }
const Rgb *end() const { return _firstBuffer.get() + _count; }
const Rgb *cend() const { return _firstBuffer.get() + _count; }
private:
static intr_handle_t _interruptHandle;
static IsrCore _interruptCore;
static void initChannel( int channel ) {
RMT.apb_conf.fifo_mask = 1; //enable memory access, instead of FIFO mode.
RMT.apb_conf.mem_tx_wrap_en = 1; //wrap around when hitting end of buffer
RMT.conf_ch[ channel ].conf0.div_cnt = detail::DIVIDER;
RMT.conf_ch[ channel ].conf0.mem_size = 1;
RMT.conf_ch[ channel ].conf0.carrier_en = 0;
RMT.conf_ch[ channel ].conf0.carrier_out_lv = 1;
RMT.conf_ch[ channel ].conf0.mem_pd = 0;
RMT.conf_ch[ channel ].conf1.rx_en = 0;
RMT.conf_ch[ channel ].conf1.mem_owner = 0;
RMT.conf_ch[ channel ].conf1.tx_conti_mode = 0; //loop back mode.
RMT.conf_ch[ channel ].conf1.ref_always_on = 1; // use apb clock: 80M
RMT.conf_ch[ channel ].conf1.idle_out_en = 1;
RMT.conf_ch[ channel ].conf1.idle_out_lv = 0;
}
static void registerInterrupt(void *) {
ESP_ERROR_CHECK(esp_intr_alloc( ETS_RMT_INTR_SOURCE, 0, interruptHandler, nullptr, &_interruptHandle));
}
static void unregisterInterrupt(void*) {
esp_intr_free( _interruptHandle );
}
static SmartLed*& IRAM_ATTR ledForChannel( int channel );
static void IRAM_ATTR interruptHandler( void* );
void IRAM_ATTR copyRmtHalfBlock();
void swapBuffers() {
if ( _secondBuffer )
_firstBuffer.swap( _secondBuffer );
}
void startTransmission() {
// Invalid use of the library
if( xSemaphoreTake( _finishedFlag, 0 ) != pdTRUE )
abort();
_pixelPosition = _componentPosition = _halfIdx = 0;
copyRmtHalfBlock();
if ( _pixelPosition < _count )
copyRmtHalfBlock();
RMT.conf_ch[ _channel ].conf1.mem_rd_rst = 1;
RMT.conf_ch[ _channel ].conf1.tx_start = 1;
}
static bool anyAlive() {
for ( int i = 0; i != 8; i++ )
if ( ledForChannel( i ) != nullptr ) return true;
return false;
}
const LedType& _timing;
int _channel;
detail::RmtPulsePair _bitToRmt[ 2 ];
int _count;
std::unique_ptr< Rgb[] > _firstBuffer;
std::unique_ptr< Rgb[] > _secondBuffer;
Rgb *_buffer;
xSemaphoreHandle _finishedFlag;
int _pixelPosition;
int _componentPosition;
int _halfIdx;
};
class Apa102 {
public:
struct ApaRgb {
ApaRgb( uint8_t r = 0, uint8_t g = 0, uint32_t b = 0, uint32_t v = 0xFF )
: v( 0xE0 | v ), b( b ), g( g ), r( r )
{}
ApaRgb& operator=( const Rgb& o ) {
r = o.r;
g = o.g;
b = o.b;
return *this;
}
ApaRgb& operator=( const Hsv& o ) {
*this = Rgb{ o };
return *this;
}
uint8_t v, b, g, r;
};
static const int FINAL_FRAME_SIZE = 4;
static const int TRANS_COUNT = 2 + 8;
Apa102( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer )
: _count( count ),
_firstBuffer( new ApaRgb[ count ] ),
_secondBuffer( doubleBuffer ? new ApaRgb[ count ] : nullptr ),
_initFrame( 0 )
{
spi_bus_config_t buscfg;
memset( &buscfg, 0, sizeof( buscfg ) );
buscfg.mosi_io_num = datapin;
buscfg.miso_io_num = -1;
buscfg.sclk_io_num = clkpin;
buscfg.quadwp_io_num = -1;
buscfg.quadhd_io_num = -1;
buscfg.max_transfer_sz = 65535;
spi_device_interface_config_t devcfg;
memset( &devcfg, 0, sizeof( devcfg ) );
devcfg.clock_speed_hz = 1000000;
devcfg.mode = 0;
devcfg.spics_io_num = -1;
devcfg.queue_size = TRANS_COUNT;
devcfg.pre_cb = nullptr;
auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 );
assert( ret == ESP_OK );
ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi );
assert( ret == ESP_OK );
std::fill_n( _finalFrame, FINAL_FRAME_SIZE, 0xFFFFFFFF );
}
~Apa102() {
// ToDo
}
ApaRgb& operator[]( int idx ) {
return _firstBuffer[ idx ];
}
const ApaRgb& operator[]( int idx ) const {
return _firstBuffer[ idx ];
}
void show() {
_buffer = _firstBuffer.get();
startTransmission();
swapBuffers();
}
void wait() {
for ( int i = 0; i != _transCount; i++ ) {
spi_transaction_t *t;
spi_device_get_trans_result( _spi, &t, portMAX_DELAY );
}
}
private:
void swapBuffers() {
if ( _secondBuffer )
_firstBuffer.swap( _secondBuffer );
}
void startTransmission() {
for ( int i = 0; i != TRANS_COUNT; i++ ) {
_transactions[ i ].cmd = 0;
_transactions[ i ].addr = 0;
_transactions[ i ].flags = 0;
_transactions[ i ].rxlength = 0;
_transactions[ i ].rx_buffer = nullptr;
}
// Init frame
_transactions[ 0 ].length = 32;
_transactions[ 0 ].tx_buffer = &_initFrame;
spi_device_queue_trans( _spi, _transactions + 0, portMAX_DELAY );
// Data
_transactions[ 1 ].length = 32 * _count;
_transactions[ 1 ].tx_buffer = _buffer;
spi_device_queue_trans( _spi, _transactions + 1, portMAX_DELAY );
_transCount = 2;
// End frame
for ( int i = 0; i != 1 + _count / 32 / FINAL_FRAME_SIZE; i++ ) {
_transactions[ 2 + i ].length = 32 * FINAL_FRAME_SIZE;
_transactions[ 2 + i ].tx_buffer = _finalFrame;
spi_device_queue_trans( _spi, _transactions + 2 + i, portMAX_DELAY );
_transCount++;
}
}
spi_device_handle_t _spi;
int _count;
std::unique_ptr< ApaRgb[] > _firstBuffer, _secondBuffer;
ApaRgb *_buffer;
spi_transaction_t _transactions[ TRANS_COUNT ];
int _transCount;
uint32_t _initFrame;
uint32_t _finalFrame[ FINAL_FRAME_SIZE ];
};
class LDP8806 {
public:
struct LDP8806_GRB {
LDP8806_GRB( uint8_t g_7bit = 0, uint8_t r_7bit = 0, uint32_t b_7bit = 0 )
: g( g_7bit ), r( r_7bit ), b( b_7bit )
{
}
LDP8806_GRB& operator=( const Rgb& o ) {
//Convert 8->7bit colour
r = ( o.r * 127 / 256 ) | 0x80;
g = ( o.g * 127 / 256 ) | 0x80;
b = ( o.b * 127 / 256 ) | 0x80;
return *this;
}
LDP8806_GRB& operator=( const Hsv& o ) {
*this = Rgb{ o };
return *this;
}
uint8_t g, r, b;
};
static const int LED_FRAME_SIZE_BYTES = sizeof( LDP8806_GRB );
static const int LATCH_FRAME_SIZE_BYTES = 3;
static const int TRANS_COUNT_MAX = 20;//Arbitrary, supports up to 600 LED
LDP8806( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, uint32_t clock_speed_hz = 2000000 )
: _count( count ),
_firstBuffer( new LDP8806_GRB[ count ] ),
_secondBuffer( doubleBuffer ? new LDP8806_GRB[ count ] : nullptr ),
// one 'latch'/start-of-data mark frame for every 32 leds
_latchFrames( ( count + 31 ) / 32 )
{
spi_bus_config_t buscfg;
memset( &buscfg, 0, sizeof( buscfg ) );
buscfg.mosi_io_num = datapin;
buscfg.miso_io_num = -1;
buscfg.sclk_io_num = clkpin;
buscfg.quadwp_io_num = -1;
buscfg.quadhd_io_num = -1;
buscfg.max_transfer_sz = 65535;
spi_device_interface_config_t devcfg;
memset( &devcfg, 0, sizeof( devcfg ) );
devcfg.clock_speed_hz = clock_speed_hz;
devcfg.mode = 0;
devcfg.spics_io_num = -1;
devcfg.queue_size = TRANS_COUNT_MAX;
devcfg.pre_cb = nullptr;
auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 );
assert( ret == ESP_OK );
ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi );
assert( ret == ESP_OK );
std::fill_n( _latchBuffer, LATCH_FRAME_SIZE_BYTES, 0x0 );
}
~LDP8806() {
// noop
}
LDP8806_GRB& operator[]( int idx ) {
return _firstBuffer[ idx ];
}
const LDP8806_GRB& operator[]( int idx ) const {
return _firstBuffer[ idx ];
}
void show() {
_buffer = _firstBuffer.get();
startTransmission();
swapBuffers();
}
void wait() {
while ( _transCount-- ) {
spi_transaction_t *t;
spi_device_get_trans_result( _spi, &t, portMAX_DELAY );
}
}
private:
void swapBuffers() {
if ( _secondBuffer )
_firstBuffer.swap( _secondBuffer );
}
void startTransmission() {
_transCount = 0;
for ( int i = 0; i != TRANS_COUNT_MAX; i++ ) {
_transactions[ i ].cmd = 0;
_transactions[ i ].addr = 0;
_transactions[ i ].flags = 0;
_transactions[ i ].rxlength = 0;
_transactions[ i ].rx_buffer = nullptr;
}
// LED Data
_transactions[ 0 ].length = ( LED_FRAME_SIZE_BYTES * 8 ) * _count;
_transactions[ 0 ].tx_buffer = _buffer;
spi_device_queue_trans( _spi, _transactions + _transCount, portMAX_DELAY );
_transCount++;
// 'latch'/start-of-data marker frames
for ( int i = 0; i < _latchFrames; i++ ) {
_transactions[ _transCount ].length = ( LATCH_FRAME_SIZE_BYTES * 8 );
_transactions[ _transCount ].tx_buffer = _latchBuffer;
spi_device_queue_trans( _spi, _transactions + _transCount, portMAX_DELAY );
_transCount++;
}
}
spi_device_handle_t _spi;
int _count;
std::unique_ptr< LDP8806_GRB[] > _firstBuffer, _secondBuffer;
LDP8806_GRB *_buffer;
spi_transaction_t _transactions[ TRANS_COUNT_MAX ];
int _transCount;
int _latchFrames;
uint8_t _latchBuffer[ LATCH_FRAME_SIZE_BYTES ];
};

View File

@@ -28,6 +28,9 @@
#include "server_main.h"
#include "server_camera.h"
// #include "jomjol_WS2812Slow.h"
#include "SmartLeds.h"
#define __SD_USE_ONE_LINE_MODE__
@@ -135,8 +138,12 @@ void task_NoSDBlink(void *pvParameter)
vTaskDelete(NULL); //Delete this task if it exits from the loop above
}
extern "C" void app_main(void)
{
TickType_t xDelay;
printf("Do Reset Camera\n");
PowerResetCamera();
Camera.InitCam();
@@ -171,7 +178,7 @@ extern "C" void app_main(void)
wifi_init_sta(ssid, passwd, hostname, ip, gateway, netmask, dns);
TickType_t xDelay;
xDelay = 2000 / portTICK_PERIOD_MS;
printf("main: sleep for : %ldms\n", (long) xDelay);
// LogFile.WriteToFile("Startsequence 06");
@@ -184,6 +191,9 @@ extern "C" void app_main(void)
LogFile.WriteToFile("=============================================================================================");
LogFile.SwitchOnOff(false);
std::string zw = gettimestring("%Y%m%d-%H%M%S");
printf("time %s\n", zw.c_str());

View File

@@ -47,11 +47,11 @@ esp_err_t info_get_handler(httpd_req_t *req)
}
};
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
if (_task.compare("GitBranch") == 0)
{
std::string zw;
zw = std::string(libfive_git_branch());
httpd_resp_sendstr_chunk(req, zw.c_str());
httpd_resp_sendstr_chunk(req, libfive_git_branch());
httpd_resp_sendstr_chunk(req, NULL);
return ESP_OK;
}
@@ -59,9 +59,7 @@ esp_err_t info_get_handler(httpd_req_t *req)
if (_task.compare("GitTag") == 0)
{
std::string zw;
zw = std::string(libfive_git_version());
httpd_resp_sendstr_chunk(req, zw.c_str());
httpd_resp_sendstr_chunk(req, libfive_git_version());
httpd_resp_sendstr_chunk(req, NULL);
return ESP_OK;
}
@@ -70,36 +68,30 @@ esp_err_t info_get_handler(httpd_req_t *req)
if (_task.compare("GitRevision") == 0)
{
std::string zw;
zw = std::string(libfive_git_revision());
httpd_resp_sendstr_chunk(req, zw.c_str());
httpd_resp_sendstr_chunk(req, libfive_git_revision());
httpd_resp_sendstr_chunk(req, NULL);
return ESP_OK;
}
if (_task.compare("BuildTime") == 0)
{
std::string zw;
zw = std::string(build_time());
httpd_resp_sendstr_chunk(req, zw.c_str());
httpd_resp_sendstr_chunk(req, build_time());
httpd_resp_sendstr_chunk(req, NULL);
return ESP_OK;
}
if (_task.compare("GitBaseBranch") == 0)
{
std::string zw;
zw = std::string(git_base_branch());
httpd_resp_sendstr_chunk(req, zw.c_str());
httpd_resp_sendstr_chunk(req, git_base_branch());
httpd_resp_sendstr_chunk(req, NULL);
return ESP_OK;
}
if (_task.compare("HTMLVersion") == 0)
{
std::string zw;
zw = std::string(getHTMLversion());
httpd_resp_sendstr_chunk(req, zw.c_str());
// std::string zw;
// zw = std::string(getHTMLversion());
httpd_resp_sendstr_chunk(req, getHTMLversion());
httpd_resp_sendstr_chunk(req, NULL);
return ESP_OK;
}
@@ -115,18 +107,18 @@ esp_err_t info_get_handler(httpd_req_t *req)
if (_task.compare("IP") == 0)
{
std::string zw;
zw = std::string(getIPAddress());
httpd_resp_sendstr_chunk(req, zw.c_str());
std::string *zw;
zw = getIPAddress();
httpd_resp_sendstr_chunk(req, zw->c_str());
httpd_resp_sendstr_chunk(req, NULL);
return ESP_OK;
}
if (_task.compare("SSID") == 0)
{
std::string zw;
zw = std::string(getSSID());
httpd_resp_sendstr_chunk(req, zw.c_str());
std::string *zw;
zw = getSSID();
httpd_resp_sendstr_chunk(req, zw->c_str());
httpd_resp_sendstr_chunk(req, NULL);
return ESP_OK;
}
@@ -140,28 +132,15 @@ esp_err_t info_get_handler(httpd_req_t *req)
return ESP_OK;
}
#ifdef DEBUG_DETAIL_ON
LogFile.WriteHeapInfo("info_get_handler - Done");
#endif
return ESP_OK;
}
esp_err_t starttime_get_handler(httpd_req_t *req)
{
#ifdef DEBUG_DETAIL_ON
LogFile.WriteHeapInfo("starttime_get_handler - Start");
#endif
httpd_resp_send(req, starttime.c_str(), strlen(starttime.c_str()));
/* Respond with an empty chunk to signal HTTP response completion */
httpd_resp_send_chunk(req, NULL, 0);
#ifdef DEBUG_DETAIL_ON
LogFile.WriteHeapInfo("starttime_get_handler - Done");
#endif
return ESP_OK;
}
@@ -215,12 +194,15 @@ esp_err_t hello_main_handler(httpd_req_t *req)
}
res = send_file(req, filetosend);
/* Respond with an empty chunk to signal HTTP response completion */
httpd_resp_send_chunk(req, NULL, 0);
if (res != ESP_OK)
return res;
/* Respond with an empty chunk to signal HTTP response completion */
// httpd_resp_sendstr(req, "");
httpd_resp_send_chunk(req, NULL, 0);
// httpd_resp_send_chunk(req, NULL, 0);
#ifdef DEBUG_DETAIL_ON
LogFile.WriteHeapInfo("hello_main_handler - Stop");
@@ -297,10 +279,6 @@ esp_err_t img_tmp_virtual_handler(httpd_req_t *req)
esp_err_t sysinfo_handler(httpd_req_t *req)
{
#ifdef DEBUG_DETAIL_ON
LogFile.WriteHeapInfo("sysinfo_handler - Start");
#endif
const char* resp_str;
std::string zw;
std::string cputemp = std::to_string(temperatureRead());
@@ -331,7 +309,6 @@ esp_err_t sysinfo_handler(httpd_req_t *req)
}\
]";
resp_str = zw.c_str();
httpd_resp_set_type(req, "application/json");
@@ -339,10 +316,6 @@ esp_err_t sysinfo_handler(httpd_req_t *req)
/* Respond with an empty chunk to signal HTTP response completion */
httpd_resp_send_chunk(req, NULL, 0);
#ifdef DEBUG_DETAIL_ON
LogFile.WriteHeapInfo("sysinfo_handler - Done");
#endif
return ESP_OK;
}
@@ -399,25 +372,24 @@ httpd_handle_t start_webserver(void)
httpd_handle_t server = NULL;
httpd_config_t config = { };
config.task_priority = tskIDLE_PRIORITY+5;
config.stack_size = 32768; // bei 32k stürzt das Programm beim Bilderaufnehmen ab
config.task_priority = tskIDLE_PRIORITY+1; // 20210924 --> vorher +5
config.stack_size = 32768; //20210921 --> vorher 32768 // bei 32k stürzt das Programm beim Bilderaufnehmen ab
config.core_id = tskNO_AFFINITY;
config.server_port = 80;
config.ctrl_port = 32768;
config.max_open_sockets = 7;
config.max_open_sockets = 5; //20210921 --> vorher 7
config.max_uri_handlers = 24;
config.max_resp_headers = 8;
config.backlog_conn = 5;
config.lru_purge_enable = true; // dadurch werden alter Verbindungen gekappt, falls neue benögt werden.
config.recv_wait_timeout = 30; // default: 5
config.send_wait_timeout = 30; // default: 5
config.lru_purge_enable = true; // dadurch werden alte Verbindungen gekappt, falls neue benögt werden.
config.recv_wait_timeout = 5; // default: 5 20210924 --> vorher 30
config.send_wait_timeout = 5; // default: 5 20210924 --> vorher 30
config.global_user_ctx = NULL;
config.global_user_ctx_free_fn = NULL;
config.global_transport_ctx = NULL;
config.global_transport_ctx_free_fn = NULL;
config.open_fn = NULL;
config.close_fn = NULL;
config.lru_purge_enable = true; // neu, um schlechte Serverbindung zu verhindern
// config.uri_match_fn = NULL;
config.uri_match_fn = httpd_uri_match_wildcard;

View File

@@ -1,4 +1,4 @@
const char* GIT_REV="7fcb5d1";
const char* GIT_REV="147d974";
const char* GIT_TAG="";
const char* GIT_BRANCH="master";
const char* BUILD_TIME="2021-09-12 07:30";
const char* BUILD_TIME="2021-09-25 18:55";

View File

@@ -13,7 +13,7 @@ extern "C"
#include "Helper.h"
#include <fstream>
const char* GIT_BASE_BRANCH = "master - v8.3.0 - 2021-09-12";
const char* GIT_BASE_BRANCH = "master - v8.4.0 - 2021-09-25";
const char* git_base_branch(void)
@@ -42,21 +42,19 @@ const char* libfive_git_branch(void)
return GIT_BRANCH;
}
std::string getHTMLversion(void){
string line = "";
char _char_getHTMLversion[20]="NaN\0";
const char* getHTMLversion(void){
FILE* pFile;
string fn = FormatFileName("/sdcard/html/version.txt");
pFile = fopen(fn.c_str(), "r");
if (pFile == NULL)
return std::string("NAN");
return _char_getHTMLversion;
char zw[1024];
fgets(zw, 1024, pFile);
line = std::string(trim(zw));
fgets(_char_getHTMLversion, 20, pFile);
fclose(pFile);
return line;
return _char_getHTMLversion;
}

View File

@@ -389,7 +389,7 @@ CONFIG_ESP_INT_WDT_TIMEOUT_MS=300
CONFIG_ESP_INT_WDT_CHECK_CPU1=y
CONFIG_ESP_TASK_WDT=y
# CONFIG_ESP_TASK_WDT_PANIC is not set
CONFIG_ESP_TASK_WDT_TIMEOUT_S=3
CONFIG_ESP_TASK_WDT_TIMEOUT_S=6
CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y
CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=y
# CONFIG_ESP_PANIC_HANDLER_IRAM is not set
@@ -1107,7 +1107,7 @@ CONFIG_INT_WDT_TIMEOUT_MS=300
CONFIG_INT_WDT_CHECK_CPU1=y
CONFIG_TASK_WDT=y
# CONFIG_TASK_WDT_PANIC is not set
CONFIG_TASK_WDT_TIMEOUT_S=3
CONFIG_TASK_WDT_TIMEOUT_S=6
CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y
CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y
# CONFIG_EVENT_LOOP_PROFILING is not set

1300
code/sdkconfig.esp32cam Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -138,7 +138,8 @@ CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y
# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set
# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set
# CONFIG_COMPILER_CXX_EXCEPTIONS is not set
CONFIG_COMPILER_CXX_EXCEPTIONS=y
CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=0
# CONFIG_COMPILER_CXX_RTTI is not set
CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y
# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set

View File

@@ -1,4 +1,4 @@
const char* GIT_REV="7fcb5d1";
const char* GIT_REV="147d974";
const char* GIT_TAG="";
const char* GIT_BRANCH="master";
const char* BUILD_TIME="2021-09-12 07:30";
const char* BUILD_TIME="2021-09-25 18:55";

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="data:,">
<title>Make Alignment</title>
<meta charset="utf-8"/>

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="data:,">
<meta charset="utf-8"/>
<title>Make Analog Alignment</title>

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="data:,">
<meta charset="utf-8"/>
<title>Check</title>

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="data:,">
<title>Edit Config</title>
<meta charset="utf-8">

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="data:,">
<title>Edit Config</title>
<meta charset="utf-8">
@@ -349,24 +350,6 @@ textarea {
<td style="font-size: 80%;"> Time to keep the separated digit images (in days -"0" = forever) </td>
</tr>
<tr class="expert" id="Analog_ExtendedResolution_ex10">
<td width="20px" style="padding-left: 40px;">
<input type="checkbox" id="Analog_ExtendedResolution_enabled" value="1" onclick = 'InvertEnableItem("Analog", "ExtendedResolution")' unchecked >
</td>
<td width="200px">
<class id="Analog_ExtendedResolution_text" style="color:black;">ExtendedResolution</class>
</td>
<td>
<select id="Analog_ExtendedResolution_value1">
<option value="true" selected>true</option>
<option value="false" >false</option>
</select>
</td>
<td style="font-size: 80%;">
Enable to use the after point resolution for the last analog counter
</td>
</tr>
<tr class="expert" id="ex10">
<td width="20px" style="padding-left: 40px;"> </td>
<td> <class id="Analog_ModelInputSize_text" style="color:black;">ModelInputSize</class> </td>
@@ -502,6 +485,41 @@ textarea {
Maximum change of reading from one to the next readout
</td>
</tr>
<tr>
<td width="20px" style="padding-left: 40px;">
<input type="checkbox" id="PostProcessing_ExtendedResolution_enabled" value="1" onclick = 'InvertEnableItem("PostProcessing", "ExtendedResolution")' unchecked >
</td>
<td width="200px">
<class id="PostProcessing_ExtendedResolution_text" style="color:black;">ExtendedResolution</class>
</td>
<td>
<select id="PostProcessing_ExtendedResolution_value1">
<option value="true" >true</option>
<option value="false" selected>false</option>
</select>
</td>
<td style="font-size: 80%;">
Enable to use the after point resolution for the last analog counter
</td>
</tr>
<tr>
<td id="ex121" width="20px" style="padding-left: 40px;">
<input type="checkbox" id="PostProcessing_IgnoreLeadingNaN_enabled" value="1" onclick = 'InvertEnableItem("PostProcessing", "IgnoreLeadingNaN")' unchecked >
</td>
<td width="200px">
<class id="PostProcessing_IgnoreLeadingNaN_text" style="color:black;">IgnoreLeadingNaN</class>
</td>
<td>
<select id="PostProcessing_IgnoreLeadingNaN_value1">
<option value="true" >true</option>
<option value="false" selected>false</option>
</select>
</td>
<td style="font-size: 80%;">
Leading "N"'s will be deleted before further processing
</td>
</tr>
@@ -1028,7 +1046,7 @@ textarea {
<option value="output">output</option>
<option value="output-pwm" disabled>output pwm (not implemented)</option>
<option value="external-flash-pwm" disabled>external flash light pwm controlled (not implemented)</option>
<option value="external-flash-ws281x" disabled>external flash light ws281x controlled (not implemented)</option>
<option value="external-flash-ws281x">external flash light ws281x controlled (experimental)</option>
</select>
</td>
</td>
@@ -1452,7 +1470,7 @@ function WriteParameter(_param, _category, _cat, _name, _optional, _number = -1)
setEnabled(_cat+"_"+_name, false);
}
EnDisableItem(_category[_cat]["enabled"], _param, _category, _cat, _name, _optional);
EnDisableItem(_category[_cat]["enabled"], _param, _category, _cat, _name, _optional, _number);
}
function InvertEnableItem(_cat, _param)
@@ -1619,12 +1637,16 @@ function UpdateInputIndividual()
{
ReadParameter(param, "PostProcessing", "DecimalShift", true, NUNBERSAkt)
ReadParameter(param, "PostProcessing", "MaxRateValue", true, NUNBERSAkt)
ReadParameter(param, "PostProcessing", "ExtendedResolution", true, NUNBERSAkt)
ReadParameter(param, "PostProcessing", "IgnoreLeadingNaN", true, NUNBERSAkt)
}
var sel = document.getElementById("Numbers_value1");
NUNBERSAkt = sel.selectedIndex;
WriteParameter(param, category, "PostProcessing", "DecimalShift", true, NUNBERSAkt);
WriteParameter(param, category, "PostProcessing", "MaxRateValue", true, NUNBERSAkt);
WriteParameter(param, category, "PostProcessing", "ExtendedResolution", true, NUNBERSAkt);
WriteParameter(param, category, "PostProcessing", "IgnoreLeadingNaN", true, NUNBERSAkt);
}
function UpdateInput() {
@@ -1656,7 +1678,7 @@ function UpdateInput() {
WriteParameter(param, category, "Analog", "Model", false);
WriteParameter(param, category, "Analog", "LogImageLocation", true);
WriteParameter(param, category, "Analog", "LogfileRetentionInDays", true);
WriteParameter(param, category, "Analog", "ExtendedResolution", true);
// WriteParameter(param, category, "Analog", "ExtendedResolution", true);
WriteParameter(param, category, "Analog", "ModelInputSize", false);
WriteParameter(param, category, "PostProcessing", "PreValueUse", true);
@@ -1719,13 +1741,13 @@ function ReadParameterAll()
ReadParameter(param, "Analog", "Model", false);
ReadParameter(param, "Analog", "LogImageLocation", true);
ReadParameter(param, "Analog", "LogfileRetentionInDays", true);
ReadParameter(param, "Analog", "ExtendedResolution", true);
// ReadParameter(param, "Analog", "ExtendedResolution", true);
ReadParameter(param, "Analog", "ModelInputSize", false);
ReadParameter(param, "PostProcessing", "PreValueUse", true);
ReadParameter(param, "PostProcessing", "PreValueAgeStartup", true);
ReadParameter(param, "PostProcessing", "AllowNegativeRates", true);
ReadParameter(param, "PostProcessing", "MaxRateValue", true);
// ReadParameter(param, "PostProcessing", "MaxRateValue", true);
ReadParameter(param, "PostProcessing", "ErrorMessage", true);
ReadParameter(param, "PostProcessing", "CheckDigitIncreaseConsistency", true);

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="data:,">
<meta charset="utf-8"/>
<title>Make Digital Alignment</title>

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html style="width: fit-content">
<head>
<link rel="icon" href="data:,">
<title>jomjol - AI on the edge</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html style="width: fit-content">
<head>
<link rel="icon" href="data:,">
<title>jomjol - AI on the edge</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="data:,">
<title>Make Reference</title>
<meta charset="utf-8"/>

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html style="width: fit-content">
<head>
<link rel="icon" href="data:,">
<title>jomjol - AI on the edge</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html style="width: fit-content">
<head>
<link rel="icon" href="data:,">
<title>jomjol - AI on the edge</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html style="width: fit-content">
<head>
<link rel="icon" href="data:,">
<title>jomjol - AI on the edge</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html style="width: fit-content">
<head>
<link rel="icon" href="data:,">
<title>jomjol - AI on the edge</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html style="width: fit-content">
<head>
<link rel="icon" href="data:,">
<title>jomjol - AI on the edge</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html style="width: fit-content">
<head>
<link rel="icon" href="data:,">
<title>jomjol - AI on the edge</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html style="width: fit-content">
<head>
<link rel="icon" href="data:,">
<title>jomjol - AI on the edge</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html style="width: fit-content">
<head>
<link rel="icon" href="data:,">
<title>jomjol - AI on the edge</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -73,28 +74,28 @@ li.dropdown {
<body style="font-family: arial">
<h1>Digitizer - AI on the edge</h1>
<h1 id="id_title">Digitizer - AI on the edge</h1>
<h2>An ESP32 all inclusive neural network recognition system for meter digitalization</h2>
<ul>
<li><a href="#"onclick="document.getElementById('maincontent').src = '/wasserzaehler_roi.html';">Overview</a></li>
<li class="dropdown">
<a href="javascript:void(0)" class="dropbtn">Configuration</a>
<div class="dropdown-content">
<a href="#"onclick="document.getElementById('maincontent').src = '/prevalue_set.html';">Set preValue</a>
<a href="index_configure.html">Edit Configuration</a>
</div>
<li class="dropdown">
<a href="javascript:void(0)" class="dropbtn">Configuration</a>
<div class="dropdown-content">
<a href="#"onclick="document.getElementById('maincontent').src = '/prevalue_set.html';">Set preValue</a>
<a href="index_configure.html">Edit Configuration</a>
</div>
</li>
<li><a href="#"onclick="document.getElementById('maincontent').src = '/wasserzaehler.html?full';">Recognition</a></li>
<li><a href="#"onclick="document.getElementById('maincontent').src = '/fileserver/';">File Server</a></li>
<li class="dropdown">
<a href="javascript:void(0)" class="dropbtn">System</a>
<div class="dropdown-content">
<a href="#"onclick="document.getElementById('maincontent').src = '/ota_page.html';">OTA Update</a>
<a href="#"onclick="document.getElementById('maincontent').src = '/fileserver/log/message/?readonly=true';">Log Viewer</a>
<a href="#"onclick="document.getElementById('maincontent').src = '/reboot_page.html';">Reboot</a>
<a href="#"onclick="document.getElementById('maincontent').src = '/info.html';">Info</a>
</div>
<li class="dropdown">
<a href="javascript:void(0)" class="dropbtn">System</a>
<div class="dropdown-content">
<a href="#"onclick="document.getElementById('maincontent').src = '/ota_page.html';">OTA Update</a>
<a href="#"onclick="document.getElementById('maincontent').src = '/fileserver/log/message/?readonly=true';">Log Viewer</a>
<a href="#"onclick="document.getElementById('maincontent').src = '/reboot_page.html';">Reboot</a>
<a href="#"onclick="document.getElementById('maincontent').src = '/info.html';">Info</a>
</div>
</ul>
<p>
@@ -102,5 +103,46 @@ li.dropdown {
<iframe name="maincontent" id ="maincontent" src="/wasserzaehler_roi.html" title="fileserver" allowfullscreen></iframe>
</div>
<script type="text/javascript" src="./gethost.js"></script>
<script type="text/javascript" src="./readconfigcommon.js"></script>
<script type="text/javascript" src="./readconfigparam.js"></script>
<script type="text/javascript">
var basepath = "http://192.168.178.22";
function LoadHostname() {
_basepath = getbasepath();
var xhttp = new XMLHttpRequest();
xhttp.addEventListener('load', function(event) {
if (xhttp.status >= 200 && xhttp.status < 300) {
hostname = xhttp.responseText;
document.title = "jomjol - AI on the edge - " + hostname;
document.getElementById("id_title").innerHTML = "Digitizer - AI on the edge - " + hostname;
} else {
console.warn(request.statusText, request.responseText);
}
});
// var xhttp = new XMLHttpRequest();
try {
url = _basepath + '/version?type=Hostname';
xhttp.open("GET", url, true);
xhttp.send();
}
catch (error)
{
// alert("Loading Hostname failed");
}
}
LoadHostname();
</script>
</body>
</html>

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="data:,">
<title>jomjol - AI on the edge</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="data:,">
<title>OTA Update</title>
<meta charset="utf-8">

View File

@@ -1,4 +1,6 @@
<html><head>
<html>
<head>
<link rel="icon" href="data:,">
<title>jomjol - AI on the edge</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="data:,">
<title>Set PreValue</title>
<meta charset="utf-8">

View File

@@ -31,6 +31,7 @@ function ParseConfig() {
ParamAddValue(param, catname, "ImageQuality");
ParamAddValue(param, catname, "ImageSize");
ParamAddValue(param, catname, "FixedExposure");
ParamAddValue(param, catname, "UseGPIOControlledIllumination");
var catname = "Alignment";
category[catname] = new Object();
@@ -53,7 +54,7 @@ function ParseConfig() {
ParamAddValue(param, catname, "LogImageLocation");
ParamAddValue(param, catname, "LogfileRetentionInDays");
ParamAddValue(param, catname, "ModelInputSize", 2);
ParamAddValue(param, catname, "ExtendedResolution");
// ParamAddValue(param, catname, "ExtendedResolution");
var catname = "Analog";
category[catname] = new Object();
@@ -64,7 +65,7 @@ function ParseConfig() {
ParamAddValue(param, catname, "LogImageLocation");
ParamAddValue(param, catname, "LogfileRetentionInDays");
ParamAddValue(param, catname, "ModelInputSize", 2);
ParamAddValue(param, catname, "ExtendedResolution");
// ParamAddValue(param, catname, "ExtendedResolution");
var catname = "PostProcessing";
category[catname] = new Object();
@@ -76,9 +77,11 @@ function ParseConfig() {
ParamAddValue(param, catname, "PreValueAgeStartup");
ParamAddValue(param, catname, "AllowNegativeRates");
ParamAddValue(param, catname, "MaxRateValue", 1, true);
ParamAddValue(param, catname, "ExtendedResolution", 1, true);
ParamAddValue(param, catname, "IgnoreLeadingNaN", 1, true);
ParamAddValue(param, catname, "ErrorMessage");
ParamAddValue(param, catname, "CheckDigitIncreaseConsistency");
ParamAddValue(param, catname, "IgnoreLeadingNaN");
// ParamAddValue(param, catname, "IgnoreLeadingNaN");
var catname = "MQTT";
category[catname] = new Object();
@@ -102,6 +105,10 @@ function ParseConfig() {
ParamAddValue(param, catname, "IO4", 6, false, [null, null, /^[0-9]*$/, null, null, /^[a-zA-Z0-9_-]*$/]);
ParamAddValue(param, catname, "IO12", 6, false, [null, null, /^[0-9]*$/, null, null, /^[a-zA-Z0-9_-]*$/]);
ParamAddValue(param, catname, "IO13", 6, false, [null, null, /^[0-9]*$/, null, null, /^[a-zA-Z0-9_-]*$/]);
ParamAddValue(param, catname, "LEDType");
ParamAddValue(param, catname, "LEDNumbers");
ParamAddValue(param, catname, "LEDColor", 3);
var catname = "AutoTimer";
category[catname] = new Object();
@@ -673,4 +680,4 @@ function CreateROI(_number, _type, _pos, _roinew, _x, _y, _dx, _dy){
NUMBERS[_indexnumber][_type].splice(_pos+1, 0, _ret);
return "";
}
}

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="data:,">
<title>Reboot</title>
<meta charset="utf-8">

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html style="width: fit-content">
<head>
<link rel="icon" href="data:,">
<title>jomjol - AI on the edge</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="data:,">
<meta charset="utf-8">
</head>

View File

@@ -1 +1 @@
9.7.1
10.1.0

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="data:,">
<title>Overview</title>
<meta charset="utf-8">

0
sd-card/img_tmp/leer.txt Normal file
View File