Compare commits

...

26 Commits

Author SHA1 Message Date
jomjol
58a90ff706 v8.5.0 2021-10-07 07:45:40 +02:00
jomjol
d0bf12f3d4 v8.5.0 2021-10-07 07:16:46 +02:00
jomjol
af16785bbf Rolling 20211002 2021-10-02 14:59:09 +02:00
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
jomjol
19ca0d7dd7 Update Versioninfo 2021-09-12 07:33:30 +02:00
jomjol
7fcb5d1c0c v8.3.0 2021-09-12 07:29:30 +02:00
jomjol
dd995ec28a Rolling 20210910 2021-09-10 09:26:52 +02:00
jomjol
af99de3535 IgnoreLeadingNaN 2021-09-02 11:04:01 +02:00
jomjol
3567cc2fb0 Merge pull request #330 from pixeldoc2000/pixeldoc2000-patch-1
Pixeldoc2000 patch 1
2021-09-02 11:00:49 +02:00
pixel::doc
5e9d9bd264 Update edit_config_param.html
Fixed some more Text.
2021-09-02 10:09:36 +02:00
pixel::doc
62447c1bb9 Update edit_config_param.html
Fixed some Text
2021-09-02 00:23:59 +02:00
jomjol
a86434c9a2 Rolling 20210831 2021-08-31 11:40:29 +02:00
jomjol
b7b70299f7 Rolling 20210830 2021-08-30 21:21:18 +02:00
jomjol
eb02e0aec1 new images 2021-08-29 20:57:21 +02:00
87 changed files with 5018 additions and 1556 deletions

View File

@@ -6,7 +6,9 @@
##### 7.1.2 MQTT-Update - (2021-06-17)
* NEW: 7.1.2: bug fix setting hostname, Flash-LED not off during rebootNEW: 7.1.1: bug fix wlan password with "=" (again)
* NEW: 7.1.2: bug fix setting hostname, Flash-LED not off during reboot
* NEW: 7.1.1: bug fix wlan password with "=" (again)
* MQTT error message: changes "no error", send retain flag

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,42 @@ In other cases you can contact the developer via email: <img src="https://raw.gi
##### 8.5.0 - Multi Meter Support (2021-10-07)
* Upgrade digital CNN to v13.1.0 (added new images)
* bug fix: wlan password with space, double digit output
##### 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)
* Dedicated NaN handling, internal refactoring (CNN-Handling)
* HTML: confirmation after config.ini update
* Bug fixing
##### 8.2.0 - Multi Meter Support (2021-08-24)
* Improve server responsiveness

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

@@ -1,487 +0,0 @@
#include "ClassFlowAnalog.h"
#include <math.h>
#include <iomanip>
#include <sys/types.h>
#include <sstream> // std::stringstream
// #define OHNETFLITE
#ifndef OHNETFLITE
#include "CTfLiteClass.h"
#endif
#include "ClassLogFile.h"
static const char* TAG = "flow_analog";
bool debugdetailanalog = false;
void ClassFlowAnalog::SetInitialParameter(void)
{
string cnnmodelfile = "";
modelxsize = 1;
modelysize = 1;
ListFlowControll = NULL;
previousElement = NULL;
SaveAllFiles = false;
disabled = false;
extendedResolution = false;
}
ClassFlowAnalog::ClassFlowAnalog(std::vector<ClassFlow*>* lfc) : ClassFlowImage(lfc, TAG)
{
SetInitialParameter();
ListFlowControll = lfc;
for (int i = 0; i < ListFlowControll->size(); ++i)
{
if (((*ListFlowControll)[i])->name().compare("ClassFlowAlignment") == 0)
{
flowpostalignment = (ClassFlowAlignment*) (*ListFlowControll)[i];
}
}
}
int ClassFlowAnalog::AnzahlROIs(int _analog = 0)
{
int zw = ANALOG[_analog]->ROI.size();
if (extendedResolution)
zw++;
return zw;
}
string ClassFlowAnalog::getReadout(int _analog = 0)
{
string result = "";
if (ANALOG[_analog]->ROI.size() == 0)
return result;
float zahl = ANALOG[_analog]->ROI[ANALOG[_analog]->ROI.size() - 1]->result;
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
int prev = -1;
prev = ZeigerEval(ANALOG[_analog]->ROI[ANALOG[_analog]->ROI.size() - 1]->result, prev);
result = std::to_string(prev);
if (extendedResolution)
result = result + std::to_string(ergebnis_nachkomma);
for (int i = ANALOG[_analog]->ROI.size() - 2; i >= 0; --i)
{
prev = ZeigerEval(ANALOG[_analog]->ROI[i]->result, prev);
result = std::to_string(prev) + result;
}
return result;
}
int ClassFlowAnalog::ZeigerEval(float zahl, int ziffer_vorgaenger)
{
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
int ergebnis_vorkomma = ((int) floor(zahl)) % 10;
int ergebnis, ergebnis_rating;
if (ziffer_vorgaenger == -1)
return ergebnis_vorkomma % 10;
ergebnis_rating = ergebnis_nachkomma - ziffer_vorgaenger;
if (ergebnis_nachkomma >= 5)
ergebnis_rating-=5;
else
ergebnis_rating+=5;
ergebnis = (int) round(zahl);
if (ergebnis_rating < 0)
ergebnis-=1;
if (ergebnis == -1)
ergebnis+=10;
ergebnis = ergebnis % 10;
return ergebnis;
}
bool ClassFlowAnalog::ReadParameter(FILE* pfile, string& aktparamgraph)
{
std::vector<string> zerlegt;
aktparamgraph = trim(aktparamgraph);
if (aktparamgraph.size() == 0)
if (!this->GetNextParagraph(pfile, aktparamgraph))
return false;
if ((aktparamgraph.compare("[Analog]") != 0) && (aktparamgraph.compare(";[Analog]") != 0)) // Paragraph passt nich zu MakeImage
return false;
if (aktparamgraph[0] == ';')
{
disabled = true;
while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph));
printf("[Analog] is disabled !!!\n");
return true;
}
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
{
zerlegt = this->ZerlegeZeile(aktparamgraph);
if ((zerlegt[0] == "LogImageLocation") && (zerlegt.size() > 1))
{
this->LogImageLocation = "/sdcard" + zerlegt[1];
this->isLogImage = true;
}
if ((toUpper(zerlegt[0]) == "LOGFILERETENTIONINDAYS") && (zerlegt.size() > 1))
{
this->logfileRetentionInDays = std::stoi(zerlegt[1]);
}
if ((zerlegt[0] == "Model") && (zerlegt.size() > 1))
{
this->cnnmodelfile = zerlegt[1];
}
if ((zerlegt[0] == "ModelInputSize") && (zerlegt.size() > 2))
{
this->modelxsize = std::stoi(zerlegt[1]);
this->modelysize = std::stoi(zerlegt[2]);
}
if (zerlegt.size() >= 5)
{
analog* _analog = GetANALOG(zerlegt[0], true);
roianalog* neuroi = _analog->ROI[_analog->ROI.size()-1];
neuroi->posx = std::stoi(zerlegt[1]);
neuroi->posy = std::stoi(zerlegt[2]);
neuroi->deltax = std::stoi(zerlegt[3]);
neuroi->deltay = std::stoi(zerlegt[4]);
neuroi->result = -1;
neuroi->image = NULL;
neuroi->image_org = NULL;
// ROI.push_back(neuroi);
}
if ((toUpper(zerlegt[0]) == "SAVEALLFILES") && (zerlegt.size() > 1))
{
if (toUpper(zerlegt[1]) == "TRUE")
SaveAllFiles = true;
}
if ((toUpper(zerlegt[0]) == "EXTENDEDRESOLUTION") && (zerlegt.size() > 1))
{
if (toUpper(zerlegt[1]) == "TRUE")
extendedResolution = true;
}
}
for (int _ana = 0; _ana < ANALOG.size(); ++_ana)
for (int i = 0; i < ANALOG[_ana]->ROI.size(); ++i)
{
ANALOG[_ana]->ROI[i]->image = new CImageBasis(modelxsize, modelysize, 3);
ANALOG[_ana]->ROI[i]->image_org = new CImageBasis(ANALOG[_ana]->ROI[i]->deltax, ANALOG[_ana]->ROI[i]->deltay, 3);
}
return true;
}
analog* ClassFlowAnalog::FindANALOG(string _name_number)
{
for (int i = 0; i < ANALOG.size(); ++i)
{
if (ANALOG[i]->name == _name_number)
return ANALOG[i];
}
return NULL;
}
analog* ClassFlowAnalog::GetANALOG(string _name, bool _create = true)
{
string _analog, _roi;
int _pospunkt = _name.find_first_of(".");
// printf("Name: %s, Pospunkt: %d\n", _name.c_str(), _pospunkt);
if (_pospunkt > -1)
{
_analog = _name.substr(0, _pospunkt);
_roi = _name.substr(_pospunkt+1, _name.length() - _pospunkt - 1);
}
else
{
_analog = "default";
_roi = _name;
}
analog *_ret = NULL;
for (int i = 0; i < ANALOG.size(); ++i)
{
if (ANALOG[i]->name == _analog)
_ret = ANALOG[i];
}
if (!_create) // nicht gefunden und soll auch nicht erzeugt werden
return _ret;
if (_ret == NULL)
{
_ret = new analog;
_ret->name = _analog;
ANALOG.push_back(_ret);
}
roianalog* neuroi = new roianalog;
neuroi->name = _roi;
_ret->ROI.push_back(neuroi);
printf("GetANALOG - ANALOG %s - roi %s\n", _analog.c_str(), _roi.c_str());
return _ret;
}
string ClassFlowAnalog::getHTMLSingleStep(string host)
{
string result, zw;
std::vector<HTMLInfo*> htmlinfo;
result = "<p>Found ROIs: </p> <p><img src=\"" + host + "/img_tmp/alg_roi.jpg\"></p>\n";
result = result + "Analog Pointers: <p> ";
htmlinfo = GetHTMLInfo();
for (int i = 0; i < htmlinfo.size(); ++i)
{
std::stringstream stream;
stream << std::fixed << std::setprecision(1) << htmlinfo[i]->val;
zw = stream.str();
result = result + "<img src=\"" + host + "/img_tmp/" + htmlinfo[i]->filename + "\"> " + zw;
delete htmlinfo[i];
}
htmlinfo.clear();
return result;
}
bool ClassFlowAnalog::doFlow(string time)
{
if (disabled)
return true;
if (!doAlignAndCut(time)){
return false;
};
if (debugdetailanalog) LogFile.WriteToFile("ClassFlowAnalog::doFlow nach Alignment");
doNeuralNetwork(time);
RemoveOldLogs();
return true;
}
bool ClassFlowAnalog::doAlignAndCut(string time)
{
if (disabled)
return true;
CAlignAndCutImage *caic = flowpostalignment->GetAlignAndCutImage();
for (int _ana = 0; _ana < ANALOG.size(); ++_ana)
for (int i = 0; i < ANALOG[_ana]->ROI.size(); ++i)
{
printf("Analog %d - Align&Cut\n", i);
caic->CutAndSave(ANALOG[_ana]->ROI[i]->posx, ANALOG[_ana]->ROI[i]->posy, ANALOG[_ana]->ROI[i]->deltax, ANALOG[_ana]->ROI[i]->deltay, ANALOG[_ana]->ROI[i]->image_org);
if (SaveAllFiles)
{
if (ANALOG[_ana]->name == "default")
ANALOG[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->ROI[i]->name + ".jpg"));
else
ANALOG[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->name + "_" + ANALOG[_ana]->ROI[i]->name + ".jpg"));
}
ANALOG[_ana]->ROI[i]->image_org->Resize(modelxsize, modelysize, ANALOG[_ana]->ROI[i]->image);
if (SaveAllFiles)
{
if (ANALOG[_ana]->name == "default")
ANALOG[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->ROI[i]->name + ".bmp"));
else
ANALOG[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->name + "_" + ANALOG[_ana]->ROI[i]->name + ".bmp"));
}
}
return true;
}
void ClassFlowAnalog::DrawROI(CImageBasis *_zw)
{
int r = 0;
int g = 255;
int b = 0;
for (int _ana = 0; _ana < ANALOG.size(); ++_ana)
for (int i = 0; i < ANALOG[_ana]->ROI.size(); ++i)
{
_zw->drawRect(ANALOG[_ana]->ROI[i]->posx, ANALOG[_ana]->ROI[i]->posy, ANALOG[_ana]->ROI[i]->deltax, ANALOG[_ana]->ROI[i]->deltay, r, g, b, 1);
_zw->drawCircle((int) (ANALOG[_ana]->ROI[i]->posx + ANALOG[_ana]->ROI[i]->deltax/2), (int) (ANALOG[_ana]->ROI[i]->posy + ANALOG[_ana]->ROI[i]->deltay/2), (int) (ANALOG[_ana]->ROI[i]->deltax/2), r, g, b, 2);
_zw->drawLine((int) (ANALOG[_ana]->ROI[i]->posx + ANALOG[_ana]->ROI[i]->deltax/2), (int) ANALOG[_ana]->ROI[i]->posy, (int) (ANALOG[_ana]->ROI[i]->posx + ANALOG[_ana]->ROI[i]->deltax/2), (int) (ANALOG[_ana]->ROI[i]->posy + ANALOG[_ana]->ROI[i]->deltay), r, g, b, 2);
_zw->drawLine((int) ANALOG[_ana]->ROI[i]->posx, (int) (ANALOG[_ana]->ROI[i]->posy + ANALOG[_ana]->ROI[i]->deltay/2), (int) ANALOG[_ana]->ROI[i]->posx + ANALOG[_ana]->ROI[i]->deltax, (int) (ANALOG[_ana]->ROI[i]->posy + ANALOG[_ana]->ROI[i]->deltay/2), r, g, b, 2);
}
}
bool ClassFlowAnalog::doNeuralNetwork(string time)
{
if (disabled)
return true;
string logPath = CreateLogFolder(time);
string input = "/sdcard/img_tmp/alg.jpg";
string ioresize = "/sdcard/img_tmp/resize.bmp";
string output;
input = FormatFileName(input);
#ifndef OHNETFLITE
CTfLiteClass *tflite = new CTfLiteClass;
string zwcnn = "/sdcard" + cnnmodelfile;
zwcnn = FormatFileName(zwcnn);
printf(zwcnn.c_str());printf("\n");
if (!tflite->LoadModel(zwcnn)) {
printf("Can't read model file /sdcard%s\n", cnnmodelfile.c_str());
delete tflite;
return false;
}
tflite->MakeAllocate();
#endif
for (int _ana = 0; _ana < ANALOG.size(); ++_ana)
{
for (int i = 0; i < ANALOG[_ana]->ROI.size(); ++i)
{
printf("Analog %d - TfLite\n", i);
float f1, f2;
f1 = 0; f2 = 0;
#ifndef OHNETFLITE
tflite->LoadInputImageBasis(ANALOG[_ana]->ROI[i]->image);
tflite->Invoke();
if (debugdetailanalog) LogFile.WriteToFile("Nach Invoke");
f1 = tflite->GetOutputValue(0);
f2 = tflite->GetOutputValue(1);
#endif
float result = fmod(atan2(f1, f2) / (M_PI * 2) + 2, 1);
// printf("Result sin, cos, ziffer: %f, %f, %f\n", f1, f2, result);
ANALOG[_ana]->ROI[i]->result = result * 10;
printf("Result Analog%i: %f\n", i, ANALOG[_ana]->ROI[i]->result);
if (isLogImage)
{
LogImage(logPath, ANALOG[_ana]->ROI[i]->name, &ANALOG[_ana]->ROI[i]->result, NULL, time, ANALOG[_ana]->ROI[i]->image_org);
}
}
}
#ifndef OHNETFLITE
delete tflite;
#endif
return true;
}
std::vector<HTMLInfo*> ClassFlowAnalog::GetHTMLInfo()
{
std::vector<HTMLInfo*> result;
for (int _ana = 0; _ana < ANALOG.size(); ++_ana)
for (int i = 0; i < ANALOG[_ana]->ROI.size(); ++i)
{
if (ANALOG[_ana]->name == "default")
ANALOG[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->ROI[i]->name + ".bmp"));
else
ANALOG[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->name + "_" + ANALOG[_ana]->ROI[i]->name + ".bmp"));
HTMLInfo *zw = new HTMLInfo;
if (ANALOG[_ana]->name == "default")
{
zw->filename = ANALOG[_ana]->ROI[i]->name + ".bmp";
zw->filename_org = ANALOG[_ana]->ROI[i]->name + ".jpg";
}
else
{
zw->filename = ANALOG[_ana]->name + "_" + ANALOG[_ana]->ROI[i]->name + ".bmp";
zw->filename_org = ANALOG[_ana]->name + "_" + ANALOG[_ana]->ROI[i]->name + ".jpg";
}
zw->val = ANALOG[_ana]->ROI[i]->result;
zw->image = ANALOG[_ana]->ROI[i]->image;
zw->image_org = ANALOG[_ana]->ROI[i]->image_org;
result.push_back(zw);
}
return result;
}
int ClassFlowAnalog::getAnzahlANALOG()
{
return ANALOG.size();
}
string ClassFlowAnalog::getNameANALOG(int _analog)
{
if (_analog < ANALOG.size())
return ANALOG[_analog]->name;
return "ANALOG DOES NOT EXIST";
}
analog* ClassFlowAnalog::GetANALOG(int _analog)
{
if (_analog < ANALOG.size())
return ANALOG[_analog];
return NULL;
}
void ClassFlowAnalog::UpdateNameNumbers(std::vector<std::string> *_name_numbers)
{
for (int _dig = 0; _dig < ANALOG.size(); _dig++)
{
std::string _name = ANALOG[_dig]->name;
bool found = false;
for (int i = 0; i < (*_name_numbers).size(); ++i)
{
if ((*_name_numbers)[i] == _name)
found = true;
}
if (!found)
(*_name_numbers).push_back(_name);
}
}

View File

@@ -1,65 +0,0 @@
#pragma once
#include "ClassFlowImage.h"
#include "ClassFlowAlignment.h"
// #include "CTfLiteClass.h"
struct roianalog {
int posx, posy, deltax, deltay;
float result;
CImageBasis *image, *image_org;
string name;
};
struct analog {
string name;
std::vector<roianalog*> ROI;
};
class ClassFlowAnalog :
public ClassFlowImage
{
protected:
// std::vector<roianalog*> ROI;
std::vector<analog*> ANALOG;
string cnnmodelfile;
int modelxsize, modelysize;
int ZeigerEval(float zahl, int ziffer_vorgaenger);
bool SaveAllFiles;
ClassFlowAlignment* flowpostalignment;
void SetInitialParameter(void);
public:
bool extendedResolution;
ClassFlowAnalog(std::vector<ClassFlow*>* lfc);
bool ReadParameter(FILE* pfile, string& aktparamgraph);
bool doFlow(string time);
string getHTMLSingleStep(string host);
string getReadout(int _analog);
void DrawROI(CImageBasis *_zw);
bool doNeuralNetwork(string time);
bool doAlignAndCut(string time);
std::vector<HTMLInfo*> GetHTMLInfo();
int AnzahlROIs(int _analog);
int getAnzahlANALOG();
analog* GetANALOG(int _analog);
analog* GetANALOG(string _name, bool _create);
analog* FindANALOG(string _name_number);
string getNameANALOG(int _analog);
void UpdateNameNumbers(std::vector<std::string> *_name_numbers);
string name(){return "ClassFlowAnalog";};
};

View File

@@ -0,0 +1,665 @@
#include "ClassFlowCNNGeneral.h"
#include <math.h>
#include <iomanip>
#include <sys/types.h>
#include <sstream> // std::stringstream
#include "CTfLiteClass.h"
#include "ClassLogFile.h"
static const char* TAG = "flow_analog";
bool debugdetailgeneral = false;
ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype) : ClassFlowImage(NULL, TAG)
{
string cnnmodelfile = "";
modelxsize = 1;
modelysize = 1;
ListFlowControll = NULL;
previousElement = NULL;
SaveAllFiles = false;
disabled = 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, bool _extendedResolution = false)
{
string result = "";
if (GENERAL[_analog]->ROI.size() == 0)
return result;
if (CNNType == Analogue)
{
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_float, prev);
result = std::to_string(prev);
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_float, prev);
result = std::to_string(prev) + result;
}
return result;
}
if (CNNType == Digital)
{
for (int i = 0; i < GENERAL[_analog]->ROI.size(); ++i)
{
if (GENERAL[_analog]->ROI[i]->result_klasse >= 10)
result = result + "N";
else
result = result + std::to_string(GENERAL[_analog]->ROI[i]->result_klasse);
}
return result;
}
if (CNNType == DigitalHyprid)
{
// int ergebnis_nachkomma = -1;
int zif_akt = -1;
float zahl = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float;
if (zahl >= 0) // NaN?
{
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_float, -1, -1);
result = std::to_string(zif_akt);
}
}
else
{
result = "N";
if (_extendedResolution && (CNNType != Digital))
result = "NN";
}
for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i)
{
if (GENERAL[_analog]->ROI[i]->result_float >= 0)
{
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
{
zif_akt = -1;
result = "N" + result;
}
}
return result;
}
return result;
}
int ClassFlowCNNGeneral::ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int eval_vorgaenger)
{
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
// int ergebnis_vorkomma = ((int) floor(zahl)) % 10;
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) + 10) % 10;
else
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) + 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) + 10) % 10;
else
return ((int) trunc(zahl) + 10) % 10;
}
else
{
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) + 10) % 10;
return ((int) trunc(zahl) + 10) % 10;
}
int ClassFlowCNNGeneral::ZeigerEval(float zahl, int ziffer_vorgaenger)
{
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)
return ergebnis_vorkomma % 10;
ergebnis_rating = ergebnis_nachkomma - ziffer_vorgaenger;
if (ergebnis_nachkomma >= 5)
ergebnis_rating-=5;
else
ergebnis_rating+=5;
ergebnis = (int) round(zahl);
if (ergebnis_rating < 0)
ergebnis-=1;
if (ergebnis == -1)
ergebnis+=10;
ergebnis = (ergebnis + 10) % 10;
return ergebnis;
}
bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
{
std::vector<string> zerlegt;
aktparamgraph = trim(aktparamgraph);
if (aktparamgraph.size() == 0)
if (!this->GetNextParagraph(pfile, aktparamgraph))
return false;
if ((toUpper(aktparamgraph) != "[ANALOG]") && (toUpper(aktparamgraph) != ";[ANALOG]")
&& (toUpper(aktparamgraph) != "[DIGIT]") && (toUpper(aktparamgraph) != ";[DIGIT]")
&& (toUpper(aktparamgraph) != "[DIGITS]") && (toUpper(aktparamgraph) != ";[DIGITS]")
) // Paragraph passt nicht
return false;
/*
if ((aktparamgraph.compare("[Analog]") != 0) && (aktparamgraph.compare(";[Analog]") != 0)
&& (aktparamgraph.compare("[Digit]") != 0) && (aktparamgraph.compare(";[Digit]"))) // Paragraph passt nicht
return false;
*/
if (aktparamgraph[0] == ';')
{
disabled = true;
while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph));
printf("[Analog/Digit] is disabled !!!\n");
return true;
}
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
{
zerlegt = this->ZerlegeZeile(aktparamgraph);
if ((zerlegt[0] == "LogImageLocation") && (zerlegt.size() > 1))
{
this->LogImageLocation = "/sdcard" + zerlegt[1];
this->isLogImage = true;
}
if ((zerlegt[0] == "LogImageSelect") && (zerlegt.size() > 1))
{
LogImageSelect = zerlegt[1];
isLogImageSelect = true;
}
if ((toUpper(zerlegt[0]) == "LOGFILERETENTIONINDAYS") && (zerlegt.size() > 1))
{
this->logfileRetentionInDays = std::stoi(zerlegt[1]);
}
if ((toUpper(zerlegt[0]) == "MODELTYPE") && (zerlegt.size() > 1))
{
if (toUpper(zerlegt[1]) == "DIGITHYPRID")
CNNType = DigitalHyprid;
}
if ((zerlegt[0] == "Model") && (zerlegt.size() > 1))
{
this->cnnmodelfile = zerlegt[1];
}
if ((zerlegt[0] == "ModelInputSize") && (zerlegt.size() > 2))
{
this->modelxsize = std::stoi(zerlegt[1]);
this->modelysize = std::stoi(zerlegt[2]);
}
if (zerlegt.size() >= 5)
{
general* _analog = GetGENERAL(zerlegt[0], true);
roi* neuroi = _analog->ROI[_analog->ROI.size()-1];
neuroi->posx = std::stoi(zerlegt[1]);
neuroi->posy = std::stoi(zerlegt[2]);
neuroi->deltax = std::stoi(zerlegt[3]);
neuroi->deltay = std::stoi(zerlegt[4]);
neuroi->result_float = -1;
neuroi->image = NULL;
neuroi->image_org = NULL;
}
if ((toUpper(zerlegt[0]) == "SAVEALLFILES") && (zerlegt.size() > 1))
{
if (toUpper(zerlegt[1]) == "TRUE")
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)
{
GENERAL[_ana]->ROI[i]->image = new CImageBasis(modelxsize, modelysize, 3);
GENERAL[_ana]->ROI[i]->image_org = new CImageBasis(GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, 3);
}
return true;
}
general* ClassFlowCNNGeneral::FindGENERAL(string _name_number)
{
for (int i = 0; i < GENERAL.size(); ++i)
if (GENERAL[i]->name == _name_number)
return GENERAL[i];
return NULL;
}
general* ClassFlowCNNGeneral::GetGENERAL(string _name, bool _create = true)
{
string _analog, _roi;
int _pospunkt = _name.find_first_of(".");
if (_pospunkt > -1)
{
_analog = _name.substr(0, _pospunkt);
_roi = _name.substr(_pospunkt+1, _name.length() - _pospunkt - 1);
}
else
{
_analog = "default";
_roi = _name;
}
general *_ret = NULL;
for (int i = 0; i < GENERAL.size(); ++i)
if (GENERAL[i]->name == _analog)
_ret = GENERAL[i];
if (!_create) // nicht gefunden und soll auch nicht erzeugt werden
return _ret;
if (_ret == NULL)
{
_ret = new general;
_ret->name = _analog;
GENERAL.push_back(_ret);
}
roi* neuroi = new roi;
neuroi->name = _roi;
_ret->ROI.push_back(neuroi);
printf("GetGENERAL - GENERAL %s - roi %s\n", _analog.c_str(), _roi.c_str());
return _ret;
}
string ClassFlowCNNGeneral::getHTMLSingleStep(string host)
{
string result, zw;
std::vector<HTMLInfo*> htmlinfo;
result = "<p>Found ROIs: </p> <p><img src=\"" + host + "/img_tmp/alg_roi.jpg\"></p>\n";
result = result + "Analog Pointers: <p> ";
htmlinfo = GetHTMLInfo();
for (int i = 0; i < htmlinfo.size(); ++i)
{
std::stringstream stream;
stream << std::fixed << std::setprecision(1) << htmlinfo[i]->val;
zw = stream.str();
result = result + "<img src=\"" + host + "/img_tmp/" + htmlinfo[i]->filename + "\"> " + zw;
delete htmlinfo[i];
}
htmlinfo.clear();
return result;
}
bool ClassFlowCNNGeneral::doFlow(string time)
{
if (disabled)
return true;
if (!doAlignAndCut(time)){
return false;
};
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::doFlow nach Alignment");
doNeuralNetwork(time);
RemoveOldLogs();
return true;
}
bool ClassFlowCNNGeneral::doAlignAndCut(string time)
{
if (disabled)
return true;
CAlignAndCutImage *caic = flowpostalignment->GetAlignAndCutImage();
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
{
printf("General %d - Align&Cut\n", i);
caic->CutAndSave(GENERAL[_ana]->ROI[i]->posx, GENERAL[_ana]->ROI[i]->posy, GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, GENERAL[_ana]->ROI[i]->image_org);
if (SaveAllFiles)
{
if (GENERAL[_ana]->name == "default")
GENERAL[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".jpg"));
else
GENERAL[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg"));
}
GENERAL[_ana]->ROI[i]->image_org->Resize(modelxsize, modelysize, GENERAL[_ana]->ROI[i]->image);
if (SaveAllFiles)
{
if (GENERAL[_ana]->name == "default")
GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".bmp"));
else
GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".bmp"));
}
}
return true;
}
void ClassFlowCNNGeneral::DrawROI(CImageBasis *_zw)
{
if (CNNType == Analogue)
{
int r = 0;
int g = 255;
int b = 0;
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
{
_zw->drawRect(GENERAL[_ana]->ROI[i]->posx, GENERAL[_ana]->ROI[i]->posy, GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, r, g, b, 1);
_zw->drawCircle((int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) (GENERAL[_ana]->ROI[i]->deltax/2), r, g, b, 2);
_zw->drawLine((int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) GENERAL[_ana]->ROI[i]->posy, (int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay), r, g, b, 2);
_zw->drawLine((int) GENERAL[_ana]->ROI[i]->posx, (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax, (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), r, g, b, 2);
}
}
else
{
for (int _dig = 0; _dig < GENERAL.size(); ++_dig)
for (int i = 0; i < GENERAL[_dig]->ROI.size(); ++i)
_zw->drawRect(GENERAL[_dig]->ROI[i]->posx, GENERAL[_dig]->ROI[i]->posy, GENERAL[_dig]->ROI[i]->deltax, GENERAL[_dig]->ROI[i]->deltay, 0, 0, (255 - _dig*100), 2);
}
}
bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
{
if (disabled)
return true;
string logPath = CreateLogFolder(time);
CTfLiteClass *tflite = new CTfLiteClass;
string zwcnn = "/sdcard" + cnnmodelfile;
zwcnn = FormatFileName(zwcnn);
printf(zwcnn.c_str());printf("\n");
if (!tflite->LoadModel(zwcnn)) {
printf("Can't read model file /sdcard%s\n", cnnmodelfile.c_str());
LogFile.WriteToFile("Cannot load model");
delete tflite;
return false;
}
tflite->MakeAllocate();
if (CNNType == AutoDetect)
{
int _anzoutputdimensions = tflite->GetAnzOutPut();
switch (_anzoutputdimensions)
{
case 2:
CNNType = Analogue;
printf("TFlite-Type set to Analogue\n");
break;
case 11:
CNNType = Digital;
printf("TFlite-Type set to Digital\n");
break;
case 22:
CNNType = DigitalHyprid;
printf("TFlite-Type set to DigitalHyprid\n");
break;
default:
printf("ERROR ERROR ERROR - tflite passt nicht zur Firmware - ERROR ERROR ERROR\n");
}
// flowpostprocessing->UpdateNachkommaDecimalShift();
}
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
{
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
{
printf("General %d - TfLite\n", i);
switch (CNNType) {
case Analogue:
{
float f1, f2;
f1 = 0; f2 = 0;
tflite->LoadInputImageBasis(GENERAL[_ana]->ROI[i]->image);
tflite->Invoke();
if (debugdetailgeneral) LogFile.WriteToFile("Nach Invoke");
f1 = tflite->GetOutputValue(0);
f2 = tflite->GetOutputValue(1);
float result = fmod(atan2(f1, f2) / (M_PI * 2) + 2, 1);
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_float, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
} break;
case Digital:
{
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]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
}
else
{
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
}
}
} break;
case DigitalHyprid:
{
int _num, _nachkomma;
tflite->LoadInputImageBasis(GENERAL[_ana]->ROI[i]->image);
tflite->Invoke();
if (debugdetailgeneral) LogFile.WriteToFile("Nach Invoke");
_num = tflite->GetOutClassification(0, 10);
_nachkomma = tflite->GetOutClassification(11, 21);
string _zwres = "Nach Invoke - Nummer: " + to_string(_num) + " Nachkomma: " + to_string(_nachkomma);
if (debugdetailgeneral) LogFile.WriteToFile(_zwres);
if ((_num == 10) || (_nachkomma == 10)) // NaN detektiert
GENERAL[_ana]->ROI[i]->result_float = -1;
else
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_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_float, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
} break;
default:
break;
}
}
}
delete tflite;
return true;
}
bool ClassFlowCNNGeneral::isExtendedResolution(int _number)
{
// if (extendedResolution && !(CNNType == Digital))
if (!(CNNType == Digital))
return true;
return false;
}
std::vector<HTMLInfo*> ClassFlowCNNGeneral::GetHTMLInfo()
{
std::vector<HTMLInfo*> result;
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
{
if (GENERAL[_ana]->name == "default")
GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".bmp"));
else
GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".bmp"));
HTMLInfo *zw = new HTMLInfo;
if (GENERAL[_ana]->name == "default")
{
zw->filename = GENERAL[_ana]->ROI[i]->name + ".bmp";
zw->filename_org = GENERAL[_ana]->ROI[i]->name + ".jpg";
}
else
{
zw->filename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".bmp";
zw->filename_org = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg";
}
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;
}
int ClassFlowCNNGeneral::getAnzahlGENERAL()
{
return GENERAL.size();
}
string ClassFlowCNNGeneral::getNameGENERAL(int _analog)
{
if (_analog < GENERAL.size())
return GENERAL[_analog]->name;
return "GENERAL DOES NOT EXIST";
}
general* ClassFlowCNNGeneral::GetGENERAL(int _analog)
{
if (_analog < GENERAL.size())
return GENERAL[_analog];
return NULL;
}
void ClassFlowCNNGeneral::UpdateNameNumbers(std::vector<std::string> *_name_numbers)
{
for (int _dig = 0; _dig < GENERAL.size(); _dig++)
{
std::string _name = GENERAL[_dig]->name;
bool found = false;
for (int i = 0; i < (*_name_numbers).size(); ++i)
{
if ((*_name_numbers)[i] == _name)
found = true;
}
if (!found)
(*_name_numbers).push_back(_name);
}
}

View File

@@ -0,0 +1,72 @@
#ifndef __CLASSCNNGENERAL__
#define __CLASSCNNGENERAL__
#include"ClassFlowDefineTypes.h"
#include "ClassFlowAlignment.h"
// #include "ClassFlowPostProcessing.h"
enum t_CNNType {
AutoDetect,
Analogue,
Digital,
DigitalHyprid,
None
};
class ClassFlowCNNGeneral :
public ClassFlowImage
{
protected:
t_CNNType CNNType;
std::vector<general*> GENERAL;
string cnnmodelfile;
int modelxsize, modelysize;
bool isLogImageSelect;
string LogImageSelect;
ClassFlowAlignment* flowpostalignment;
// ClassFlowPostProcessing *flowpostprocessing = NULL;
bool SaveAllFiles;
// bool extendedResolution;
int ZeigerEval(float zahl, int ziffer_vorgaenger);
int ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int eval_vorgaenger);
bool doNeuralNetwork(string time);
bool doAlignAndCut(string time);
public:
ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype = AutoDetect);
bool ReadParameter(FILE* pfile, string& aktparamgraph);
bool doFlow(string time);
string getHTMLSingleStep(string host);
string getReadout(int _analog, bool _extendedResolution);
void DrawROI(CImageBasis *_zw);
std::vector<HTMLInfo*> GetHTMLInfo();
// 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);
// void setPostprocessing(ClassFlowPostProcessing *_fpp){flowpostprocessing = _fpp;};
void UpdateNameNumbers(std::vector<std::string> *_name_numbers);
t_CNNType getCNNType(){return CNNType;};
string name(){return "ClassFlowCNNGeneral";};
};
#endif

View File

@@ -35,10 +35,10 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
if ((_stepname.compare(0, 7, "[Digits") == 0) || (_stepname.compare(0, 8, ";[Digits") == 0)) {
// if ((_stepname.compare("[Digits]") == 0) || (_stepname.compare(";[Digits]") == 0)){
// printf("Digits!!!\n");
_classname = "ClassFlowDigit";
_classname = "ClassFlowCNNGeneral";
}
if ((_stepname.compare("[Analog]") == 0) || (_stepname.compare(";[Analog]") == 0)){
_classname = "ClassFlowAnalog";
_classname = "ClassFlowCNNGeneral";
}
if ((_stepname.compare("[MQTT]") == 0) || (_stepname.compare(";[MQTT]") == 0)){
_classname = "ClassFlowMQTT";
@@ -54,16 +54,17 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
return result;
}
std::string ClassFlowControll::TranslateAktstatus(std::string _input)
{
if (_input.compare("ClassFlowMakeImage") == 0)
return ("Take Image");
if (_input.compare("ClassFlowAlignment") == 0)
return ("Aligning");
if (_input.compare("ClassFlowAnalog") == 0)
return ("Analog ROIs");
if (_input.compare("ClassFlowDigit") == 0)
return ("Digital ROIs");
//if (_input.compare("ClassFlowAnalog") == 0)
// return ("Analog ROIs");
if (_input.compare("ClassFlowCNNGeneral") == 0)
return ("Digitalization of ROIs");
if (_input.compare("ClassFlowMQTT") == 0)
return ("Sending MQTT");
if (_input.compare("ClassFlowPostProcessing") == 0)
@@ -73,11 +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("ClassFlowDigit") == 0)
return ((ClassFlowDigit*) (FlowControll[i]))->GetHTMLInfo();
if (flowdigit)
{
printf("ClassFlowControll::GetAllDigital - flowdigit != NULL\n");
return flowdigit->GetHTMLInfo();
}
std::vector<HTMLInfo*> empty;
return empty;
@@ -85,14 +88,30 @@ 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();
if (flowanalog)
return flowanalog->GetHTMLInfo();
std::vector<HTMLInfo*> empty;
return empty;
}
t_CNNType ClassFlowControll::GetTypeDigital()
{
if (flowdigit)
return flowdigit->getCNNType();
return t_CNNType::None;
}
t_CNNType ClassFlowControll::GetTypeAnalog()
{
if (flowanalog)
return flowanalog->getCNNType();
return t_CNNType::None;
}
string ClassFlowControll::GetMQTTMainTopic()
@@ -145,20 +164,20 @@ ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
}
if (toUpper(_type).compare("[ANALOG]") == 0)
{
cfc = new ClassFlowAnalog(&FlowControll);
flowanalog = (ClassFlowAnalog*) cfc;
cfc = new ClassFlowCNNGeneral(flowalignment);
flowanalog = (ClassFlowCNNGeneral*) cfc;
}
if (toUpper(_type).compare(0, 7, "[DIGITS") == 0)
{
cfc = new ClassFlowDigit(&FlowControll);
flowdigit = (ClassFlowDigit*) cfc;
cfc = new ClassFlowCNNGeneral(flowalignment);
flowdigit = (ClassFlowCNNGeneral*) cfc;
}
if (toUpper(_type).compare("[MQTT]") == 0)
cfc = new ClassFlowMQTT(&FlowControll);
if (toUpper(_type).compare("[POSTPROCESSING]") == 0)
{
cfc = new ClassFlowPostProcessing(&FlowControll);
cfc = new ClassFlowPostProcessing(&FlowControll, flowanalog, flowdigit);
flowpostprocessing = (ClassFlowPostProcessing*) cfc;
}
@@ -203,7 +222,7 @@ void ClassFlowControll::InitFlow(std::string config)
cfc = CreateClassFlow(line);
if (cfc)
{
printf("Start ReadParameter\n");
printf("Start ReadParameter (%s)\n", line.c_str());
cfc->ReadParameter(pFile, line);
}
else
@@ -221,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){
@@ -254,7 +273,7 @@ bool ClassFlowControll::doFlow(string time)
for (int i = 0; i < FlowControll.size(); ++i)
{
zw_time = gettimestring("%H:%M:%S");
aktstatus = TranslateAktstatus(FlowControll[i]->name()) + "(" + zw_time + ")";
aktstatus = TranslateAktstatus(FlowControll[i]->name()) + " (" + zw_time + ")";
// zw_time = gettimestring("%Y%m%d-%H%M%S");
// aktstatus = zw_time + ": " + FlowControll[i]->name();
@@ -266,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");
@@ -292,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;
}
@@ -533,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,14 +1,15 @@
#pragma once
#ifndef __FLOWCONTROLL__
#define __FLOWCONTROLL__
#include <string>
#include "ClassFlow.h"
#include "ClassFlowMakeImage.h"
#include "ClassFlowAlignment.h"
#include "ClassFlowDigit.h"
#include "ClassFlowAnalog.h"
#include "ClassFlowCNNGeneral.h"
#include "ClassFlowPostProcessing.h"
#include "ClassFlowMQTT.h"
#include "ClassFlowCNNGeneral.h"
#define READOUT_TYPE_VALUE 0
@@ -24,8 +25,9 @@ protected:
std::vector<ClassFlow*> FlowControll;
ClassFlowPostProcessing* flowpostprocessing;
ClassFlowAlignment* flowalignment;
ClassFlowAnalog* flowanalog;
ClassFlowDigit* flowdigit;
ClassFlowCNNGeneral* flowanalog;
ClassFlowCNNGeneral* flowdigit;
// ClassFlowDigit* flowdigit;
ClassFlowMakeImage* flowmakeimage;
ClassFlow* CreateClassFlow(std::string _type);
@@ -58,14 +60,19 @@ public:
bool isAutoStart(long &_intervall);
std::string getActStatus();
std::string* getActStatus();
std::vector<HTMLInfo*> GetAllDigital();
std::vector<HTMLInfo*> GetAllAnalog();
t_CNNType GetTypeDigital();
t_CNNType GetTypeAnalog();
int CleanTempFolder();
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

@@ -1,438 +0,0 @@
#include "ClassFlowDigit.h"
//#include "CFindTemplate.h"
//#include "CTfLiteClass.h"
// #define OHNETFLITE
#ifndef OHNETFLITE
#include "CTfLiteClass.h"
#endif
// #include "bitmap_image.hpp"
#include "ClassLogFile.h"
static const char* TAG = "flow_digital";
void ClassFlowDigit::SetInitialParameter(void)
{
string cnnmodelfile = "";
modelxsize = 1;
modelysize = 1;
ListFlowControll = NULL;
previousElement = NULL;
SaveAllFiles = false;
disabled = false;
DecimalShift = 0;
DecimalShiftEnabled = false;
isLogImageSelect = false;
}
ClassFlowDigit::ClassFlowDigit() : ClassFlowImage(TAG)
{
SetInitialParameter();
}
ClassFlowDigit::ClassFlowDigit(std::vector<ClassFlow*>* lfc) : ClassFlowImage(lfc, TAG)
{
SetInitialParameter();
ListFlowControll = lfc;
for (int i = 0; i < ListFlowControll->size(); ++i)
{
if (((*ListFlowControll)[i])->name().compare("ClassFlowAlignment") == 0)
{
flowpostalignment = (ClassFlowAlignment*) (*ListFlowControll)[i];
}
}
}
ClassFlowDigit::ClassFlowDigit(std::vector<ClassFlow*>* lfc, ClassFlow *_prev) : ClassFlowImage(lfc, _prev, TAG)
{
SetInitialParameter();
ListFlowControll = lfc;
previousElement = _prev;
for (int i = 0; i < ListFlowControll->size(); ++i)
{
if (((*ListFlowControll)[i])->name().compare("ClassFlowAlignment") == 0)
{
flowpostalignment = (ClassFlowAlignment*) (*ListFlowControll)[i];
}
}
}
string ClassFlowDigit::getReadout(int _digit = 0)
{
string rst = "";
for (int i = 0; i < DIGIT[_digit]->ROI.size(); ++i)
{
if (DIGIT[_digit]->ROI[i]->resultklasse == 10)
rst = rst + "N";
else
rst = rst + std::to_string(DIGIT[_digit]->ROI[i]->resultklasse);
}
return rst;
}
bool ClassFlowDigit::ReadParameter(FILE* pfile, string& aktparamgraph)
{
std::vector<string> zerlegt;
aktparamgraph = trim(aktparamgraph);
if (aktparamgraph.size() == 0)
if (!this->GetNextParagraph(pfile, aktparamgraph))
return false;
printf("aktparamgraph: %s\n", aktparamgraph.c_str());
if ((aktparamgraph.compare(0, 7, "[Digits") != 0) && (aktparamgraph.compare(0, 8, ";[Digits") != 0)) // Paragraph passt nich zu MakeImage
return false;
int _pospkt = aktparamgraph.find_first_of(".");
int _posklammerzu = aktparamgraph.find_first_of("]");
if (_pospkt > -1)
NameDigit = aktparamgraph.substr(_pospkt+1, _posklammerzu - _pospkt-1);
else
NameDigit = "";
printf("Name Digit: %s\n", NameDigit.c_str());
if (aktparamgraph[0] == ';')
{
disabled = true;
while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph));
printf("[Digits] is disabled !!!\n");
return true;
}
while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph))
{
zerlegt = this->ZerlegeZeile(aktparamgraph);
if ((zerlegt[0] == "LogImageLocation") && (zerlegt.size() > 1))
{
LogImageLocation = "/sdcard" + zerlegt[1];
isLogImage = true;
}
if ((zerlegt[0] == "LogImageSelect") && (zerlegt.size() > 1))
{
LogImageSelect = zerlegt[1];
isLogImageSelect = true;
}
if ((zerlegt[0] == "Model") && (zerlegt.size() > 1))
{
cnnmodelfile = zerlegt[1];
}
if ((zerlegt[0] == "ModelInputSize") && (zerlegt.size() > 2))
{
modelxsize = std::stoi(zerlegt[1]);
modelysize = std::stoi(zerlegt[2]);
}
if (zerlegt.size() >= 5)
{
digit* _digit = GetDIGIT(zerlegt[0], true);
roi* neuroi = _digit->ROI[_digit->ROI.size()-1];
neuroi->posx = std::stoi(zerlegt[1]);
neuroi->posy = std::stoi(zerlegt[2]);
neuroi->deltax = std::stoi(zerlegt[3]);
neuroi->deltay = std::stoi(zerlegt[4]);
neuroi->resultklasse = -1;
neuroi->image = NULL;
neuroi->image_org = NULL;
}
if ((toUpper(zerlegt[0]) == "SAVEALLFILES") && (zerlegt.size() > 1))
{
if (toUpper(zerlegt[1]) == "TRUE")
SaveAllFiles = true;
}
}
for (int _dig = 0; _dig < DIGIT.size(); ++_dig)
for (int i = 0; i < DIGIT[_dig]->ROI.size(); ++i)
{
DIGIT[_dig]->ROI[i]->image = new CImageBasis(modelxsize, modelysize, 3);
DIGIT[_dig]->ROI[i]->image_org = new CImageBasis(DIGIT[_dig]->ROI[i]->deltax, DIGIT[_dig]->ROI[i]->deltay, 3);
}
return true;
}
digit* ClassFlowDigit::FindDIGIT(string _name_number)
{
for (int i = 0; i < DIGIT.size(); ++i)
{
if (DIGIT[i]->name == _name_number)
return DIGIT[i];
}
return NULL;
}
digit* ClassFlowDigit::GetDIGIT(string _name, bool _create = true)
{
string _digit, _roi;
int _pospunkt = _name.find_first_of(".");
// printf("Name: %s, Pospunkt: %d\n", _name.c_str(), _pospunkt);
if (_pospunkt > -1)
{
_digit = _name.substr(0, _pospunkt);
_roi = _name.substr(_pospunkt+1, _name.length() - _pospunkt - 1);
}
else
{
_digit = "default";
_roi = _name;
}
digit *_ret = NULL;
for (int i = 0; i < DIGIT.size(); ++i)
{
if (DIGIT[i]->name == _digit)
_ret = DIGIT[i];
}
if (!_create) // nicht gefunden und soll auch nicht erzeugt werden, ggf. geht eine NULL zurück
return _ret;
if (_ret == NULL)
{
_ret = new digit;
_ret->name = _digit;
DIGIT.push_back(_ret);
}
roi* neuroi = new roi;
neuroi->name = _roi;
_ret->ROI.push_back(neuroi);
printf("GetDIGIT - digit %s - roi %s\n", _digit.c_str(), _roi.c_str());
return _ret;
}
string ClassFlowDigit::getHTMLSingleStep(string host)
{
string result, zw;
std::vector<HTMLInfo*> htmlinfo;
result = "<p>Found ROIs: </p> <p><img src=\"" + host + "/img_tmp/alg_roi.jpg\"></p>\n";
result = result + "Digital Counter: <p> ";
htmlinfo = GetHTMLInfo();
for (int i = 0; i < htmlinfo.size(); ++i)
{
if (htmlinfo[i]->val == 10)
zw = "NaN";
else
{
zw = to_string((int) htmlinfo[i]->val);
}
result = result + "<img src=\"" + host + "/img_tmp/" + htmlinfo[i]->filename + "\"> " + zw;
delete htmlinfo[i];
}
htmlinfo.clear();
return result;
}
bool ClassFlowDigit::doFlow(string time)
{
if (disabled)
return true;
if (!doAlignAndCut(time)){
return false;
};
doNeuralNetwork(time);
RemoveOldLogs();
return true;
}
bool ClassFlowDigit::doAlignAndCut(string time)
{
if (disabled)
return true;
CAlignAndCutImage *caic = flowpostalignment->GetAlignAndCutImage();
for (int _dig = 0; _dig < DIGIT.size(); ++_dig)
{
printf("DIGIT[_dig]->ROI.size() %d\n", DIGIT[_dig]->ROI.size());
for (int i = 0; i < DIGIT[_dig]->ROI.size(); ++i)
{
printf("DigitalDigit %d - Align&Cut\n", i);
caic->CutAndSave(DIGIT[_dig]->ROI[i]->posx, DIGIT[_dig]->ROI[i]->posy, DIGIT[_dig]->ROI[i]->deltax, DIGIT[_dig]->ROI[i]->deltay, DIGIT[_dig]->ROI[i]->image_org);
if (SaveAllFiles)
{
if (DIGIT[_dig]->name == "default")
DIGIT[_dig]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + DIGIT[_dig]->ROI[i]->name + ".jpg"));
else
DIGIT[_dig]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + DIGIT[_dig]->name + "_" + DIGIT[_dig]->ROI[i]->name + ".jpg"));
}
DIGIT[_dig]->ROI[i]->image_org->Resize(modelxsize, modelysize, DIGIT[_dig]->ROI[i]->image);
if (SaveAllFiles)
{
if (DIGIT[_dig]->name == "default")
DIGIT[_dig]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + DIGIT[_dig]->ROI[i]->name + ".bmp"));
else
DIGIT[_dig]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + DIGIT[_dig]->name + "_" + DIGIT[_dig]->ROI[i]->name + ".bmp"));
}
}
}
return true;
}
bool ClassFlowDigit::doNeuralNetwork(string time)
{
if (disabled)
return true;
string logPath = CreateLogFolder(time);
#ifndef OHNETFLITE
CTfLiteClass *tflite = new CTfLiteClass;
string zwcnn = FormatFileName("/sdcard" + cnnmodelfile);
printf(zwcnn.c_str());printf("\n");
if (!tflite->LoadModel(zwcnn)) {
printf("Can't read model file /sdcard%s\n", cnnmodelfile.c_str());
delete tflite;
return false;
}
tflite->MakeAllocate();
#endif
for (int _dig = 0; _dig < DIGIT.size(); ++_dig)
for (int i = 0; i < DIGIT[_dig]->ROI.size(); ++i)
{
printf("DigitalDigit %d - TfLite\n", i);
DIGIT[_dig]->ROI[i]->resultklasse = 0;
#ifndef OHNETFLITE
DIGIT[_dig]->ROI[i]->resultklasse = tflite->GetClassFromImageBasis(DIGIT[_dig]->ROI[i]->image);
#endif
printf("Result Digit%i: %d\n", i, DIGIT[_dig]->ROI[i]->resultklasse);
if (isLogImage)
{
if (isLogImageSelect)
{
if (LogImageSelect.find(DIGIT[_dig]->ROI[i]->name) != std::string::npos)
{
LogImage(logPath, DIGIT[_dig]->ROI[i]->name, NULL, &DIGIT[_dig]->ROI[i]->resultklasse, time, DIGIT[_dig]->ROI[i]->image_org);
}
}
else
{
LogImage(logPath, DIGIT[_dig]->ROI[i]->name, NULL, &DIGIT[_dig]->ROI[i]->resultklasse, time, DIGIT[_dig]->ROI[i]->image_org);
}
}
}
#ifndef OHNETFLITE
delete tflite;
#endif
return true;
}
void ClassFlowDigit::DrawROI(CImageBasis *_zw)
{
for (int _dig = 0; _dig < DIGIT.size(); ++_dig)
for (int i = 0; i < DIGIT[_dig]->ROI.size(); ++i)
_zw->drawRect(DIGIT[_dig]->ROI[i]->posx, DIGIT[_dig]->ROI[i]->posy, DIGIT[_dig]->ROI[i]->deltax, DIGIT[_dig]->ROI[i]->deltay, 0, 0, (255 - _dig*100), 2);
}
std::vector<HTMLInfo*> ClassFlowDigit::GetHTMLInfo()
{
std::vector<HTMLInfo*> result;
for (int _dig = 0; _dig < DIGIT.size(); ++_dig)
for (int i = 0; i < DIGIT[_dig]->ROI.size(); ++i)
{
if (DIGIT[_dig]->name == "default")
DIGIT[_dig]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + DIGIT[_dig]->ROI[i]->name + ".bmp"));
else
DIGIT[_dig]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + DIGIT[_dig]->name + "_" + DIGIT[_dig]->ROI[i]->name + ".bmp"));
HTMLInfo *zw = new HTMLInfo;
if (DIGIT[_dig]->name == "default")
{
zw->filename = DIGIT[_dig]->ROI[i]->name + ".bmp";
zw->filename_org = DIGIT[_dig]->ROI[i]->name + ".jpg";
}
else
{
zw->filename = DIGIT[_dig]->name + "_" + DIGIT[_dig]->ROI[i]->name + ".bmp";
zw->filename_org = DIGIT[_dig]->name + "_" + DIGIT[_dig]->ROI[i]->name + ".jpg";
}
zw->val = DIGIT[_dig]->ROI[i]->resultklasse;
zw->image = DIGIT[_dig]->ROI[i]->image;
zw->image_org = DIGIT[_dig]->ROI[i]->image_org;
result.push_back(zw);
}
return result;
}
int ClassFlowDigit::getAnzahlDIGIT()
{
return DIGIT.size();
}
string ClassFlowDigit::getNameDIGIT(int _digit)
{
if (_digit < DIGIT.size())
return DIGIT[_digit]->name;
return "DIGIT DOES NOT EXIST";
}
digit* ClassFlowDigit::GetDIGIT(int _digit)
{
if (_digit < DIGIT.size())
return DIGIT[_digit];
return NULL;
}
void ClassFlowDigit::UpdateNameNumbers(std::vector<std::string> *_name_numbers)
{
for (int _dig = 0; _dig < DIGIT.size(); _dig++)
{
std::string _name = DIGIT[_dig]->name;
bool found = false;
for (int i = 0; i < (*_name_numbers).size(); ++i)
{
if ((*_name_numbers)[i] == _name)
found = true;
}
if (!found)
(*_name_numbers).push_back(_name);
}
}

View File

@@ -1,71 +0,0 @@
#pragma once
#include "ClassFlowImage.h"
#include "ClassFlowAlignment.h"
#include "Helper.h"
#include <string>
struct roi {
int posx, posy, deltax, deltay;
int resultklasse;
string name;
CImageBasis *image, *image_org;
roi* next;
};
struct digit {
string name;
std::vector<roi*> ROI;
};
class ClassFlowDigit :
public ClassFlowImage
{
protected:
// std::vector<roi*> ROI;
std::vector<digit*> DIGIT;
string cnnmodelfile;
int modelxsize, modelysize;
bool SaveAllFiles;
string NameDigit;
int DecimalShift;
bool DecimalShiftEnabled;
bool isLogImageSelect;
string LogImageSelect;
ClassFlowAlignment* flowpostalignment;
bool doNeuralNetwork(string time);
bool doAlignAndCut(string time);
void SetInitialParameter(void);
public:
ClassFlowDigit();
ClassFlowDigit(std::vector<ClassFlow*>* lfc);
ClassFlowDigit(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
bool ReadParameter(FILE* pfile, string& aktparamgraph);
bool doFlow(string time);
string getHTMLSingleStep(string host);
string getReadout(int _digit);
std::vector<HTMLInfo*> GetHTMLInfo();
int getAnzahlDIGIT();
digit* GetDIGIT(int _digit);
digit* GetDIGIT(string _name, bool _create);
digit* FindDIGIT(string _name_number);
string getNameDIGIT(int _digit);
void UpdateNameNumbers(std::vector<std::string> *_name_numbers);
void DrawROI(CImageBasis *_zw);
string name(){return "ClassFlowDigit";};
};

View File

@@ -48,9 +48,14 @@ void ClassFlowImage::LogImage(string logPath, string name, float *resultFloat, i
if (!isLogImage)
return;
char buf[10];
if (resultFloat != NULL) {
sprintf(buf, "%.1f_", *resultFloat);
if (*resultFloat < 0)
sprintf(buf, "N.N_");
else
sprintf(buf, "%.1f_", *resultFloat);
} else if (resultInt != NULL) {
sprintf(buf, "%d_", *resultInt);
} else {

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"
@@ -28,12 +27,8 @@ string ClassFlowPostProcessing::GetPreValue(std::string _number)
if (NUMBERS[i]->name == _number)
index = i;
// result = RundeOutput(NUMBERS[index]->PreValue, -NUMBERS[index]->DecimalShift);
result = RundeOutput(NUMBERS[index]->PreValue, NUMBERS[index]->Nachkomma);
// if (NUMBERS[index]->digit_roi && NUMBERS[index]->analog_roi)
// result = RundeOutput(NUMBERS[index]->PreValue, NUMBERS[index]->AnzahlAnalog - NUMBERS[index]->DecimalShift);
return result;
}
@@ -94,7 +89,7 @@ bool ClassFlowPostProcessing::LoadPreValue(void)
if (NUMBERS[j]->name == name)
{
NUMBERS[j]->PreValue = stof(zwvalue.c_str());
NUMBERS[j]->ReturnPreValue = RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma);
NUMBERS[j]->ReturnPreValue = RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma + 1); // SIcherheitshalber 1 Stelle mehr, da ggf. Exgtended Resolution an ist (wird erst beim ersten Durchlauf gesetzt)
time_t tStart;
int yy, month, dd, hh, mm, ss;
@@ -128,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 + 1); // SIcherheitshalber 1 Stelle mehr, da ggf. Exgtended Resolution an ist (wird erst beim ersten Durchlauf gesetzt)
NUMBERS[j]->ReturnValueNoError = NUMBERS[j]->ReturnValue;
}
}
@@ -190,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;
}
@@ -231,21 +226,19 @@ void ClassFlowPostProcessing::SavePreValue()
}
ClassFlowPostProcessing::ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc)
ClassFlowPostProcessing::ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc, ClassFlowCNNGeneral *_analog, ClassFlowCNNGeneral *_digit)
{
// FlowRateAct = 0;
PreValueUse = false;
PreValueAgeStartup = 30;
ErrorMessage = false;
ListFlowControll = NULL;
// PreValueOkay = false;
// DecimalShift = 0;
// ErrorMessageText = "";
// timeStamp = "";
FilePreValue = FormatFileName("/sdcard/config/prevalue.ini");
ListFlowControll = lfc;
flowMakeImage = NULL;
UpdatePreValueINI = false;
IgnoreLeadingNaN = false;
flowAnalog = _analog;
flowDigit = _digit;
for (int i = 0; i < ListFlowControll->size(); ++i)
{
@@ -256,6 +249,36 @@ ClassFlowPostProcessing::ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc)
}
}
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;
@@ -270,20 +293,26 @@ 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;
NUMBERS[j]->DecimalShiftInitial = _zwdc;
}
if (NUMBERS[j]->name == _digit)
{
NUMBERS[j]->DecimalShift = _zwdc;
NUMBERS[j]->DecimalShiftInitial = _zwdc;
}
NUMBERS[j]->Nachkomma = NUMBERS[j]->AnzahlAnalog - NUMBERS[j]->DecimalShift;
}
@@ -303,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)
{
@@ -351,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]);
@@ -384,6 +418,13 @@ bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph)
if (toUpper(zerlegt[1]) == "TRUE")
ErrorMessage = true;
}
if ((toUpper(_param) == "IGNORELEADINGNAN") && (zerlegt.size() > 1))
{
if (toUpper(zerlegt[1]) == "TRUE")
IgnoreLeadingNaN = true;
}
if ((toUpper(_param) == "PREVALUEAGESTARTUP") && (zerlegt.size() > 1))
{
PreValueAgeStartup = std::stoi(zerlegt[1]);
@@ -399,33 +440,20 @@ bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph)
void ClassFlowPostProcessing::InitNUMBERS()
{
// ClassFlowDigit* _cdigit = NULL;
// ClassFlowAnalog* _canalog = NULL;
int anzDIGIT = 0;
int anzANALOG = 0;
std::vector<std::string> name_numbers;
flowAnalog = NULL;
flowDigit = NULL;
for (int i = 0; i < ListFlowControll->size(); ++i)
{
if (((*ListFlowControll)[i])->name().compare("ClassFlowDigit") == 0)
{
flowDigit = (ClassFlowDigit*) (*ListFlowControll)[i];
anzDIGIT = flowDigit->getAnzahlDIGIT();
}
if (((*ListFlowControll)[i])->name().compare("ClassFlowAnalog") == 0)
{
flowAnalog = (ClassFlowAnalog*)(*ListFlowControll)[i];
anzANALOG = flowAnalog->getAnzahlANALOG();
}
}
if (flowDigit)
{
anzDIGIT = flowDigit->getAnzahlGENERAL();
flowDigit->UpdateNameNumbers(&name_numbers);
}
if (flowAnalog)
{
anzANALOG = flowAnalog->getAnzahlGENERAL();
flowAnalog->UpdateNameNumbers(&name_numbers);
}
printf("Anzahl NUMBERS: %d - DIGITS: %d, ANALOG: %d\n", name_numbers.size(), anzDIGIT, anzANALOG);
@@ -437,7 +465,7 @@ void ClassFlowPostProcessing::InitNUMBERS()
_number->digit_roi = NULL;
if (flowDigit)
_number->digit_roi = flowDigit->FindDIGIT(name_numbers[_num]);
_number->digit_roi = flowDigit->FindGENERAL(name_numbers[_num]);
if (_number->digit_roi)
_number->AnzahlDigital = _number->digit_roi->ROI.size();
@@ -446,7 +474,7 @@ void ClassFlowPostProcessing::InitNUMBERS()
_number->analog_roi = NULL;
if (flowAnalog)
_number->analog_roi = flowAnalog->FindANALOG(name_numbers[_num]);
_number->analog_roi = flowAnalog->FindGENERAL(name_numbers[_num]);
if (_number->analog_roi)
@@ -467,6 +495,9 @@ void ClassFlowPostProcessing::InitNUMBERS()
_number->PreValueOkay = false;
_number->useMaxRateValue = false;
_number->DecimalShift = 0;
_number->DecimalShiftInitial = 0;
_number->isExtendedResolution = false;
_number->FlowRateAct = 0; // m3 / min
_number->PreValue = 0; // letzter Wert, der gut ausgelesen wurde
@@ -539,6 +570,10 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
// ErrorMessageText = "";
// Update Nachkomma, da sich beim Wechsel von CNNType Auto --> xyz auch die Nachkommastellen ändern können:
imagetime = flowMakeImage->getTimeImageTaken();
if (imagetime == 0)
time(&imagetime);
@@ -556,14 +591,31 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
NUMBERS[j]->ReturnRawValue = "";
NUMBERS[j]->ErrorMessageText = "";
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 = ShiftDecimal(NUMBERS[j]->ReturnRawValue, NUMBERS[j]->DecimalShift);
if (NUMBERS[j]->analog_roi)
NUMBERS[j]->ReturnRawValue = NUMBERS[j]->ReturnRawValue + flowAnalog->getReadout(j, NUMBERS[j]->isExtendedResolution);
NUMBERS[j]->ReturnRawValue = ShiftDecimal(NUMBERS[j]->ReturnRawValue, NUMBERS[j]->DecimalShift);
if (IgnoreLeadingNaN)
{
while ((NUMBERS[j]->ReturnRawValue.length() > 1) && (NUMBERS[j]->ReturnRawValue[0] == 'N'))
{
NUMBERS[j]->ReturnRawValue.erase(0, 1);
}
}
rohwert = NUMBERS[j]->ReturnRawValue;
@@ -601,13 +653,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))
@@ -642,6 +694,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;
@@ -728,14 +823,14 @@ float ClassFlowPostProcessing::checkDigitConsistency(float input, int _decilamsh
while (pot <= pot_max)
{
zw = input / pow(10, pot-1);
aktdigit_before = ((int) zw) % 10;
aktdigit_before = ((int) zw + 10) % 10;
zw = _preValue / pow(10, pot-1);
olddigit_before = ((int) zw) % 10;
olddigit_before = ((int) zw + 10) % 10;
zw = input / pow(10, pot);
aktdigit = ((int) zw) % 10;
aktdigit = ((int) zw + 10) % 10;
zw = _preValue / pow(10, pot);
olddigit = ((int) zw) % 10;
olddigit = ((int) zw + 10) % 10;
no_nulldurchgang = (olddigit_before <= aktdigit_before);

View File

@@ -1,49 +1,13 @@
#pragma once
#ifndef __FLOWPOSTPROCESSING__
#define __FLOWPOSTPROCESSING__
#include "ClassFlow.h"
#include "ClassFlowMakeImage.h"
#include "ClassFlowAnalog.h"
#include "ClassFlowDigit.h"
#include "ClassFlowCNNGeneral.h"
#include "ClassFlowDefineTypes.h"
#include <string>
struct NumberPost {
// int PreValueAgeStartup;
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 Nachkomma;
// ClassFlowAnalog* ANALOG;
// ClassFlowDigit* DIGIT;
digit *digit_roi;
analog *analog_roi;
string name;
};
class ClassFlowPostProcessing :
public ClassFlow
{
@@ -53,10 +17,11 @@ protected:
int PreValueAgeStartup;
bool ErrorMessage;
bool IgnoreLeadingNaN; // SPEZIALFALL für User Gustl
ClassFlowAnalog* flowAnalog;
ClassFlowDigit* flowDigit;
ClassFlowCNNGeneral* flowAnalog;
ClassFlowCNNGeneral* flowDigit;
string FilePreValue;
@@ -73,12 +38,14 @@ protected:
void InitNUMBERS();
void handleDecimalSeparator(string _decsep, string _value);
void handleMaxRateValue(string _decsep, string _value);
void handleDecimalExtendedResolution(string _decsep, string _value);
public:
bool PreValueUse;
ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc);
ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc, ClassFlowCNNGeneral *_analog, ClassFlowCNNGeneral *_digit);
bool ReadParameter(FILE* pfile, string& aktparamgraph);
bool doFlow(string time);
string getReadout(int _number);
@@ -89,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

@@ -28,19 +28,36 @@ int CTfLiteClass::GetClassFromImageBasis(CImageBasis *rs)
return GetOutClassification();
}
int CTfLiteClass::GetOutClassification()
int CTfLiteClass::GetOutClassification(int _von, int _bis)
{
TfLiteTensor* output2 = interpreter->output(0);
float zw_max = 0;
float zw_max;
float zw;
int zw_class = -1;
int zw_class;
if (output2 == NULL)
return -1;
int numeroutput = output2->dims->data[1];
for (int i = 0; i < numeroutput; ++i)
//printf("\n number output neurons: %d\n\n", numeroutput);
if (_bis == -1)
_bis = numeroutput -1;
if (_von == -1)
_von = 0;
if (_bis >= numeroutput)
{
printf("ANZAHL OUTPUT NEURONS passt nicht zu geforderter Classifizierung!");
return -1;
}
zw_max = output2->data.f[_von];
zw_class = _von;
for (int i = _von + 1; i <= _bis; ++i)
{
zw = output2->data.f[i];
if (zw > zw_max)
@@ -49,7 +66,7 @@ int CTfLiteClass::GetOutClassification()
zw_class = i;
}
}
return zw_class;
return (zw_class - _von);
}
void CTfLiteClass::GetInputDimension(bool silent = false)
@@ -71,18 +88,18 @@ void CTfLiteClass::GetInputDimension(bool silent = false)
}
void CTfLiteClass::GetOutPut()
int CTfLiteClass::GetAnzOutPut(bool silent)
{
TfLiteTensor* output2 = this->interpreter->output(0);
int numdim = output2->dims->size;
printf("NumDimension: %d\n", numdim);
if (!silent) printf("NumDimension: %d\n", numdim);
int sizeofdim;
for (int j = 0; j < numdim; ++j)
{
sizeofdim = output2->dims->data[j];
printf("SizeOfDimension %d: %d\n", j, sizeofdim);
if (!silent) printf("SizeOfDimension %d: %d\n", j, sizeofdim);
}
@@ -93,8 +110,9 @@ void CTfLiteClass::GetOutPut()
for (int i = 0; i < numeroutput; ++i)
{
fo = output2->data.f[i];
printf("Result %d: %f\n", i, fo);
if (!silent) printf("Result %d: %f\n", i, fo);
}
return numeroutput;
}
void CTfLiteClass::Invoke()
@@ -107,7 +125,7 @@ void CTfLiteClass::Invoke()
bool CTfLiteClass::LoadInputImageBasis(CImageBasis *rs)
{
std::string zw = "ClassFlowAnalog::doNeuralNetwork nach LoadInputResizeImage: ";
std::string zw = "ClassFlowCNNGeneral::doNeuralNetwork nach LoadInputResizeImage: ";
unsigned int w = rs->width;
unsigned int h = rs->height;
@@ -150,6 +168,8 @@ void CTfLiteClass::MakeAllocate()
TfLiteStatus allocate_status = this->interpreter->AllocateTensors();
if (allocate_status != kTfLiteOk) {
TF_LITE_REPORT_ERROR(error_reporter, "AllocateTensors() failed");
LogFile.WriteToFile("AllocateTensors() failed");
this->GetInputDimension();
return;
}
@@ -237,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

@@ -61,8 +61,11 @@ class CTfLiteClass
void GetInputTensorSize();
bool LoadInputImageBasis(CImageBasis *rs);
void Invoke();
void GetOutPut();
int GetOutClassification();
int GetAnzOutPut(bool silent = true);
// void GetOutPut();
// int GetOutClassification();
int GetOutClassification(int _von = -1, int _bis = -1);
int GetClassFromImageBasis(CImageBasis *rs);
std::string GetStatusFlow();

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,37 +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();
for (int i = 0; i < htmlinfo.size(); ++i)
std::vector<HTMLInfo*> htmlinfodig;
htmlinfodig = tfliteflow.GetAllDigital();
for (int i = 0; i < htmlinfodig.size(); ++i)
{
if (htmlinfo[i]->val == 10)
zw = "NaN";
if (tfliteflow.GetTypeDigital() == Digital)
{
if (htmlinfodig[i]->val == 10)
zw = "NaN";
else
zw = to_string((int) htmlinfodig[i]->val);
txt = "<img src=\"/img_tmp/" + htmlinfodig[i]->filename + "\"> " + zw;
}
else
{
zw = to_string((int) htmlinfo[i]->val);
std::stringstream stream;
stream << std::fixed << std::setprecision(1) << htmlinfodig[i]->val;
zw = stream.str();
txt = "<img src=\"/img_tmp/" + htmlinfodig[i]->filename + "\"> " + zw;
}
txt = "<img src=\"/img_tmp/" + htmlinfo[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();
}
@@ -493,31 +501,7 @@ esp_err_t handler_editflow(httpd_req_t *req)
// string zwzw = "Do " + _task + " start\n"; printf(zwzw.c_str());
std::string zw = tfliteflow.doSingleStep("[Alignment]", _host);
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);
@@ -537,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;
}
@@ -184,32 +184,6 @@ void wifi_init_sta(const char *_ssid, const char *_password, const char *_hostna
ip_addr_set_ip4_u32(&dns_info.ip, ip.addr);
ESP_ERROR_CHECK(esp_netif_set_dns_info(my_sta, ESP_NETIF_DNS_MAIN, &dns_info));
}
/////////////////////////////////////////////////////////////////
// esp_netif_create_default_wifi_sta();
// wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
// ESP_ERROR_CHECK(esp_wifi_init(&cfg));
/*
////////////////////////////// esp-idf 4.2 //////////////////////////
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
////////////////////////// ENDE esp-idf 4.2 ///////////////////////////
*/
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
@@ -253,27 +227,6 @@ void wifi_init_sta(const char *_ssid, const char *_password, const char *_hostna
} else {
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}
/* The event will not be processed after unregister */
/*
////////////////////////////// esp-idf 4.2 //////////////////////////
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
////////////////////////// ENDE esp-idf 4.2 ///////////////////////////
*/
/* Deaktiveren, damit bei einen Verbindungsabbruch neu aufgebaut wird
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler));
ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler));
vEventGroupDelete(s_wifi_event_group);
*/
/*
while (BlinkIsRunning)
{
vTaskDelay(100 / portTICK_PERIOD_MS);
}
*/
}

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;

View File

@@ -12,7 +12,7 @@
#include <string.h>
std::vector<string> ZerlegeZeile(std::string input, std::string _delimiter = "")
std::vector<string> ZerlegeZeileWLAN(std::string input, std::string _delimiter = "")
{
std::vector<string> Output;
std::string delimiter = " =,";
@@ -23,13 +23,13 @@ std::vector<string> ZerlegeZeile(std::string input, std::string _delimiter = "")
input = trim(input, delimiter);
size_t pos = findDelimiterPos(input, delimiter);
std::string token;
while (pos != std::string::npos) {
if (pos != std::string::npos) // Zerlegt nur bis ersten Gleichheitszeichen !!! Sonderfall für WLAN.ini
{
token = input.substr(0, pos);
token = trim(token, delimiter);
Output.push_back(token);
input.erase(0, pos + 1);
input = trim(input, delimiter);
pos = findDelimiterPos(input, delimiter);
}
Output.push_back(input);
@@ -67,10 +67,8 @@ void LoadWlanFromFile(std::string fn, char *&_ssid, char *&_password, char *&_ho
while ((line.size() > 0) || !(feof(pFile)))
{
// printf("%s", line.c_str());
zerlegt = ZerlegeZeile(line, "=");
zerlegt = ZerlegeZeileWLAN(line, "=");
zerlegt[0] = trim(zerlegt[0], " ");
for (int i = 2; i < zerlegt.size(); ++i)
zerlegt[1] = zerlegt[1] + "=" + zerlegt[i];
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "HOSTNAME")){
hostname = trim(zerlegt[1]);
@@ -212,7 +210,7 @@ bool ChangeHostName(std::string fn, std::string _newhostname)
while ((line.size() > 0) || !(feof(pFile)))
{
printf("%s", line.c_str());
zerlegt = ZerlegeZeile(line, "=");
zerlegt = ZerlegeZeileWLAN(line, "=");
zerlegt[0] = trim(zerlegt[0], " ");
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "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="7ae08e5";
const char* GIT_REV="d0bf12f";
const char* GIT_TAG="";
const char* GIT_BRANCH="master";
const char* BUILD_TIME="2021-08-24 08:35";
const char* BUILD_TIME="2021-10-07 07:17";

View File

@@ -13,7 +13,7 @@ extern "C"
#include "Helper.h"
#include <fstream>
const char* GIT_BASE_BRANCH = "master - v8.2.0 - 2021-08-24";
const char* GIT_BASE_BRANCH = "master - v8.5.0 - 2021-10-07";
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="7ae08e5";
const char* GIT_REV="d0bf12f";
const char* GIT_TAG="";
const char* GIT_BRANCH="master";
const char* BUILD_TIME="2021-08-24 08:35";
const char* BUILD_TIME="2021-10-07 07:17";

Binary file not shown.

BIN
firmware/dig1310s3q.tflite Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
images/platformio_build.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@@ -20,7 +20,7 @@ FlipImageSize = false
/config/ref1.jpg 442 142
[Digits]
Model = /config/dig1200s1q.tflite
Model = /config/dig1310s3q.tflite
;LogImageLocation = /log/digit
;LogfileRetentionInDays = 3
ModelInputSize = 20 32

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"/>
@@ -116,6 +117,7 @@ function SaveToConfig(){
WriteConfigININew();
UpdateConfigReference(basepath)
SaveConfigToServer(basepath);
alert("Config.ini is updated!");
}
function EnhanceContrast(){

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="data:,">
<meta charset="utf-8"/>
<title>Make Analog Alignment</title>
@@ -243,7 +244,8 @@ function SaveToConfig(){
_zwcat = getConfigCategory();
_zwcat["Analog"]["enabled"] = document.getElementById("Category_Analog_enabled").checked;
WriteConfigININew();
SaveConfigToServer(basepath);
SaveConfigToServer(basepath);
alert("Config.ini is updated!");
}

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">
@@ -60,6 +61,7 @@ function saveTextAsFile()
FileDeleteOnServer("/config/config.ini", basepath);
var textToSave = document.getElementById("inputTextToSave").value;
FileSendContent(textToSave, "/config/config.ini", basepath);
alert("Config.ini is updated!")
}
}

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="data:,">
<title>Edit Config</title>
<meta charset="utf-8">
@@ -52,7 +53,7 @@ textarea {
</td>
<td>
<button class="button" id="Edit_Config_Direct" onclick="editConfigDirect()" style="display:none">Edit Config.ini direct</button>
<input type="checkbox" id="ExpertModus_enabled" value="1" onclick = 'UpdateExpertModus()' unchecked><label for="ExpertModus_enabled"> Expertenmodus </label>
<input type="checkbox" id="ExpertModus_enabled" value="1" onclick = 'UpdateExpertModus()' unchecked><label for="ExpertModus_enabled"> Expert Mode </label>
</td>
</tr>
</table>
@@ -276,7 +277,7 @@ textarea {
<input type="text" id="Digits_Model_value1">
</td>
<td style="font-size: 80%;">
path to CNN model file for image recognition
Path to CNN model file for image recognition
</td>
</tr>
<tr>
@@ -330,7 +331,7 @@ textarea {
<td width="20px" style="padding-left: 40px;"> </td>
<td width="200px"> <class id="Analog_Model_text" style="color:black;">Model</class> </td>
<td> <input type="text" id="Analog_Model_value1"> </td>
<td style="font-size: 80%;"> path to CNN model file for image recognition</td>
<td style="font-size: 80%;"> Path to CNN model file for image recognition</td>
</tr>
<tr>
<td width="20px" style="padding-left: 40px;">
@@ -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>
@@ -459,7 +442,7 @@ textarea {
</select>
</td>
<td style="font-size: 80%;">
Activate to enalbe additional consistency check - especially zero crossing check between digits
Enable additional consistency check - especially zero crossing check between digits
</td>
</tr>
@@ -485,7 +468,7 @@ textarea {
<input type="number" id="PostProcessing_DecimalShift_value1" step="1">
</td>
<td style="font-size: 80%;">
shift the digit separator within the digital digits (positiv and negativ)
Shift the digit separator within the digital digits (positiv and negativ)
</td>
</tr>
<tr>
@@ -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>
@@ -533,9 +551,9 @@ textarea {
<input type="text" id="MQTT_MainTopic_value1">
</td>
<td style="font-size: 80%;">
MQTT main topic, under which the counters are published. The single value will be published with the following key: MAINTOPIC/VALUE_NAME/PARAMTER <br>
MQTT main topic, under which the counters are published. The single value will be published with the following key: MAINTOPIC/VALUE_NAME/PARAMETER <br>
where parameters are: value, rate, timestamp, error<br>
The general connection status can be found in MAINTOPiC"/CONNECTION
The general connection status can be found in MAINTOPIC/CONNECTION
</td>
</tr>
<tr>
@@ -563,7 +581,7 @@ textarea {
<input type="text" id="MQTT_user_value1">
</td>
<td style="font-size: 80%;">
user for MQTT authentication
User for MQTT authentication
</td>
</tr>
<tr>
@@ -577,7 +595,7 @@ textarea {
<input type="text" id="MQTT_password_value1">
</td>
<td style="font-size: 80%;">
password for MQTT authentication
Password for MQTT authentication
</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);
@@ -1804,7 +1826,8 @@ function saveTextAsFile()
if (confirm("Are you sure you want to update \"config.ini\"?")) {
ReadParameterAll();
WriteConfigININew();
SaveConfigToServer(basepath);
SaveConfigToServer(basepath);
alert("Config.ini is updated!")
}
}
@@ -1838,4 +1861,4 @@ LoadConfigNeu();
</script>
</body>
</html>
</html>

View File

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="data:,">
<meta charset="utf-8"/>
<title>Make Digital Alignment</title>
@@ -236,7 +237,8 @@ function SaveToConfig(){
_zwcat = getConfigCategory();
_zwcat["Digits"]["enabled"] = document.getElementById("Category_Digits_enabled").checked;
WriteConfigININew();
SaveConfigToServer(basepath);
SaveConfigToServer(basepath);
alert("Config.ini is updated!");
}
@@ -467,6 +469,13 @@ function draw() {
context.strokeRect(x0, y0, dx, dy);
context.lineWidth = 1;
context.strokeRect(x0+dx*0.2, y0+dy*0.2, dx*0.6, dy*0.6);
context.lineWidth = 2;
context.beginPath();
context.moveTo(x0, y0+dy/2);
context.lineTo(x0+dx, y0+dy/2);
context.stroke();
ROIInfo[aktindex]["x"] = rect.startX;
ROIInfo[aktindex]["y"] = rect.startY;
ROIInfo[aktindex]["dx"] = rect.w;

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">
@@ -61,7 +62,7 @@ p {font-size: 1em;}
}
ParseConfig();
param = getConfigParameters();
param["System"]["SetupMode"]["enabled"] = false;
param["System"]["SetupMode"]["enabled"] = true;
param["System"]["SetupMode"]["value1"] = "false";
WriteConfigININew();

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

@@ -9,7 +9,7 @@ function getbasepath(){
{
// host = "http://192.168.2.219"; // jomjol interner test
// host = "http://192.168.178.46"; // jomjol interner test
host = "http://192.168.178.64"; // jomjol interner Real
host = "http://192.168.178.22"; // jomjol interner Real
// host = "http://192.168.43.191";
// host = "."; // jomjol interner localhost

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

@@ -63,7 +63,6 @@ function ParseConfig() {
ParamAddValue(param, catname, "LogImageLocation");
ParamAddValue(param, catname, "LogfileRetentionInDays");
ParamAddValue(param, catname, "ModelInputSize", 2);
ParamAddValue(param, catname, "ExtendedResolution");
var catname = "PostProcessing";
category[catname] = new Object();
@@ -75,6 +74,8 @@ 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");
@@ -100,6 +101,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();
@@ -671,4 +676,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.0
10.2.0

View File

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