2019-08-27 14:33:09 +01:00
/*
2019-10-27 10:13:24 +00:00
xsns_53_sml . ino - SML , OBIS , EBUS , RAW , COUNTER interface for Tasmota
2019-08-27 14:33:09 +01:00
Created by Gerhard Mutz on 07.10 .11 .
adapted for Tasmota
2021-01-22 07:33:42 +00:00
Copyright ( C ) 2021 Gerhard Mutz and Theo Arends
2019-08-27 14:33:09 +01:00
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# ifdef USE_SML_M
# define XSNS_53 53
2022-12-30 07:41:54 +00:00
// this driver depends on use USE_SCRIPT !!!
2019-08-27 14:33:09 +01:00
// debug counter input to led for counter1 and 2
//#define DEBUG_CNT_LED1 2
//#define DEBUG_CNT_LED1 2
# include <TasmotaSerial.h>
2023-01-30 14:03:46 +00:00
2019-09-16 18:58:22 +01:00
// use special no wait serial driver, should be always on
2020-05-15 14:30:32 +01:00
# ifndef ESP32
2019-08-27 14:33:09 +01:00
# define SPECIAL_SS
2020-05-15 14:30:32 +01:00
# endif
2019-08-27 14:33:09 +01:00
2022-07-20 13:25:40 +01:00
// max number of meters , may be adjusted
# ifndef MAX_METERS
# define MAX_METERS 5
# endif
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
/* additional defines
USE_ESP32_SW_SERIAL
default off , uses a special combo driver that allows more then 3 serial ports on ESP32 .
define rec pins as negativ to use software serial
2023-01-30 14:03:46 +00:00
USE_SML_AUTHKEY
rarely used , thus off by default
2022-12-30 07:41:54 +00:00
*/
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
// if you have to save more RAM you may disable these options by defines in user_config_override
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
# ifndef NO_SML_REPLACE_VARS
// allows to replace values in decoder section with script string variables
# undef SML_REPLACE_VARS
# define SML_REPLACE_VARS
2019-08-27 14:33:09 +01:00
# endif
2022-12-30 07:41:54 +00:00
# ifndef NO_USE_SML_SPECOPT
// allows to define special option 1 for meters that use a direction bit
# undef USE_SML_SPECOPT
# define USE_SML_SPECOPT
2019-08-27 14:33:09 +01:00
# endif
2022-12-30 07:41:54 +00:00
# ifndef NO_USE_SML_SCRIPT_CMD
// allows several sml cmds from scripts, as well as access to sml registers
# undef USE_SML_SCRIPT_CMD
# define USE_SML_SCRIPT_CMD
2019-08-27 14:33:09 +01:00
# endif
2022-12-30 07:41:54 +00:00
# ifndef NO_USE_SML_DECRYPT
// allows 256 bit AES decryption
# define USE_SML_DECRYPT
2019-08-27 14:33:09 +01:00
# endif
// median filter eliminates outliers, but uses much RAM and CPU cycles
2019-12-25 08:09:43 +00:00
// 672 bytes extra RAM with SML_MAX_VARS = 16
2019-09-16 18:58:22 +01:00
// default compile on, but must be enabled by descriptor flag 16
// may be undefined if RAM must be saved
2022-10-04 10:42:56 +01:00
2022-12-30 07:41:54 +00:00
# ifndef NO_USE_SML_MEDIAN_FILTER
# undef USE_SML_MEDIAN_FILTER
2019-09-16 18:58:22 +01:00
# define USE_SML_MEDIAN_FILTER
2022-10-04 10:42:56 +01:00
# endif
2019-08-27 14:33:09 +01:00
2023-01-30 14:03:46 +00:00
# ifdef USE_SML_DECRYPT
# include "han_Parser.h"
# endif
2022-12-30 07:41:54 +00:00
/* special options per meter
1 :
special binary SML option for meters that use a bit in the status register to sign import or export like ED300L , AS2020 or DTZ541
a . obis code that holds the direction bit ,
b . Flag identifier ,
c . direction bit ,
d . second Flag identifier ( some meters use 2 different flags ) ,
e . second bit ,
f . obis code of value to be inverted on direction bit .
e . g . 1 , = so1 , 00010 800 , 65 , 11 , 65 , 11 , 00100700 for DTZ541
2 :
flags , currently only bit 0 and 1
if 1 fix DWS74 bug
if 2 use asci obis line compare instead a pattern compare
e . g . 1 , = so2 , 2 set obis line mode on meter 1
3 :
serial buffers
a . serial buffer size
2023-01-30 14:03:46 +00:00
b . serial irq buffer size , a must be given
c . dumplog buffer size , default is 128 , a and b must be given
2022-12-30 07:41:54 +00:00
e . g . 1 , = so3 , 256 , 256 set serial buffers on meter 1
4 :
decrytion key , 16 bytes hex btw 32 chars without spaces or commas
defining key switches decryption mode on
2022-11-28 06:41:37 +00:00
2023-01-30 14:03:46 +00:00
5 :
authentication key , 16 bytes hex btw 32 chars without spaces or commas
needs USE_SML_AUTHKEY
6 :
synchronisation timout in milliseconds , after no serial data within this
time serial pointer is reset to zero
2022-12-30 07:41:54 +00:00
*/
2019-09-16 18:58:22 +01:00
2022-12-30 07:41:54 +00:00
//#define MODBUS_DEBUG
2021-12-15 13:46:05 +00:00
2022-12-30 07:41:54 +00:00
// ESP32 combined hardware and software serial driver, software read only
2021-12-15 13:46:05 +00:00
# ifdef ESP32
# ifdef USE_ESP32_SW_SERIAL
# ifndef ESP32_SWS_BUFFER_SIZE
# define ESP32_SWS_BUFFER_SIZE 256
# endif
class SML_ESP32_SERIAL : public Stream {
public :
SML_ESP32_SERIAL ( uint32_t uart_index ) ;
virtual ~ SML_ESP32_SERIAL ( ) ;
bool begin ( uint32_t speed , uint32_t smode , int32_t recpin , int32_t trxpin ) ;
int32_t peek ( void ) ;
2023-01-30 14:03:46 +00:00
int read ( void ) override ;
2021-12-15 13:46:05 +00:00
size_t write ( uint8_t byte ) override ;
2023-01-30 14:03:46 +00:00
int available ( void ) override ;
2021-12-15 13:46:05 +00:00
void flush ( void ) override ;
void setRxBufferSize ( uint32_t size ) ;
void updateBaudRate ( uint32_t baud ) ;
void rxRead ( void ) ;
2022-12-30 07:41:54 +00:00
void end ( ) ;
2021-12-15 13:46:05 +00:00
using Print : : write ;
private :
// Member variables
void setbaud ( uint32_t speed ) ;
uint32_t uart_index ;
int8_t m_rx_pin ;
int8_t m_tx_pin ;
uint32_t cfgmode ;
uint32_t ss_byte ;
uint32_t ss_bstart ;
uint32_t ss_index ;
uint32_t m_bit_time ;
uint32_t m_in_pos ;
uint32_t m_out_pos ;
uint16_t serial_buffer_size ;
bool m_valid ;
uint8_t * m_buffer ;
HardwareSerial * hws ;
} ;
void IRAM_ATTR sml_callRxRead ( void * self ) { ( ( SML_ESP32_SERIAL * ) self ) - > rxRead ( ) ; } ;
SML_ESP32_SERIAL : : SML_ESP32_SERIAL ( uint32_t index ) {
uart_index = index ;
m_valid = true ;
}
SML_ESP32_SERIAL : : ~ SML_ESP32_SERIAL ( void ) {
if ( hws ) {
hws - > end ( ) ;
2022-12-30 07:41:54 +00:00
delete ( hws ) ;
2021-12-15 13:46:05 +00:00
} else {
detachInterrupt ( m_rx_pin ) ;
if ( m_buffer ) {
free ( m_buffer ) ;
}
}
}
void SML_ESP32_SERIAL : : setbaud ( uint32_t speed ) {
m_bit_time = ESP . getCpuFreqMHz ( ) * 1000000 / speed ;
}
2022-12-30 07:41:54 +00:00
void SML_ESP32_SERIAL : : end ( void ) {
if ( m_buffer ) {
free ( m_buffer ) ;
}
}
2021-12-15 13:46:05 +00:00
bool SML_ESP32_SERIAL : : begin ( uint32_t speed , uint32_t smode , int32_t recpin , int32_t trxpin ) {
if ( ! m_valid ) { return false ; }
m_buffer = 0 ;
if ( recpin < 0 ) {
setbaud ( speed ) ;
m_rx_pin = - recpin ;
serial_buffer_size = ESP32_SWS_BUFFER_SIZE ;
m_buffer = ( uint8_t * ) malloc ( serial_buffer_size ) ;
if ( m_buffer = = NULL ) return false ;
2022-12-30 07:41:54 +00:00
pinMode ( m_rx_pin , INPUT_PULLUP ) ;
2021-12-15 13:46:05 +00:00
attachInterruptArg ( m_rx_pin , sml_callRxRead , this , CHANGE ) ;
m_in_pos = m_out_pos = 0 ;
hws = nullptr ;
} else {
cfgmode = smode ;
m_rx_pin = recpin ;
m_tx_pin = trxpin ;
hws = new HardwareSerial ( uart_index ) ;
if ( hws ) {
hws - > begin ( speed , cfgmode , m_rx_pin , m_tx_pin ) ;
}
}
return true ;
}
void SML_ESP32_SERIAL : : flush ( void ) {
if ( hws ) {
hws - > flush ( ) ;
} else {
m_in_pos = m_out_pos = 0 ;
}
}
int32_t SML_ESP32_SERIAL : : peek ( void ) {
if ( hws ) {
return hws - > peek ( ) ;
} else {
if ( m_in_pos = = m_out_pos ) return - 1 ;
return m_buffer [ m_out_pos ] ;
}
}
2023-01-30 14:03:46 +00:00
int SML_ESP32_SERIAL : : read ( void ) {
2021-12-15 13:46:05 +00:00
if ( hws ) {
return hws - > read ( ) ;
} else {
if ( m_in_pos = = m_out_pos ) return - 1 ;
uint32_t ch = m_buffer [ m_out_pos ] ;
2022-12-30 07:41:54 +00:00
m_out_pos = ( m_out_pos + 1 ) % serial_buffer_size ;
2021-12-15 13:46:05 +00:00
return ch ;
}
}
2023-01-30 14:03:46 +00:00
int SML_ESP32_SERIAL : : available ( void ) {
2021-12-15 13:46:05 +00:00
if ( hws ) {
return hws - > available ( ) ;
} else {
int avail = m_in_pos - m_out_pos ;
if ( avail < 0 ) avail + = serial_buffer_size ;
return avail ;
}
}
size_t SML_ESP32_SERIAL : : write ( uint8_t byte ) {
if ( hws ) {
return hws - > write ( byte ) ;
}
return 0 ;
}
void SML_ESP32_SERIAL : : setRxBufferSize ( uint32_t size ) {
if ( hws ) {
hws - > setRxBufferSize ( size ) ;
} else {
if ( m_buffer ) {
free ( m_buffer ) ;
}
serial_buffer_size = size ;
m_buffer = ( uint8_t * ) malloc ( size ) ;
}
}
void SML_ESP32_SERIAL : : updateBaudRate ( uint32_t baud ) {
if ( hws ) {
hws - > updateBaudRate ( baud ) ;
} else {
setbaud ( baud ) ;
}
}
// no wait mode only 8N1 (or 7X1, obis only, ignoring parity)
void IRAM_ATTR SML_ESP32_SERIAL : : rxRead ( void ) {
uint32_t diff ;
uint32_t level ;
# define SML_LASTBIT 9
level = digitalRead ( m_rx_pin ) ;
if ( ! level & & ! ss_index ) {
// start condition
ss_bstart = ESP . getCycleCount ( ) - ( m_bit_time / 4 ) ;
ss_byte = 0 ;
ss_index + + ;
} else {
// now any bit changes go here
// calc bit number
diff = ( ESP . getCycleCount ( ) - ss_bstart ) / m_bit_time ;
if ( ! level & & diff > SML_LASTBIT ) {
// start bit of next byte, store and restart
// leave irq at change
for ( uint32_t i = ss_index ; i < = SML_LASTBIT ; i + + ) {
ss_byte | = ( 1 < < i ) ;
}
uint32_t next = ( m_in_pos + 1 ) % serial_buffer_size ;
if ( next ! = ( uint32_t ) m_out_pos ) {
m_buffer [ m_in_pos ] = ss_byte > > 1 ;
m_in_pos = next ;
}
ss_bstart = ESP . getCycleCount ( ) - ( m_bit_time / 4 ) ;
ss_byte = 0 ;
ss_index = 1 ;
return ;
}
if ( diff > = SML_LASTBIT ) {
// bit zero was 0,
uint32_t next = ( m_in_pos + 1 ) % serial_buffer_size ;
if ( next ! = ( uint32_t ) m_out_pos ) {
m_buffer [ m_in_pos ] = ss_byte > > 1 ;
m_in_pos = next ;
}
ss_byte = 0 ;
ss_index = 0 ;
} else {
// shift in
for ( uint32_t i = ss_index ; i < diff ; i + + ) {
if ( ! level ) ss_byte | = ( 1 < < i ) ;
}
ss_index = diff ;
}
}
}
# endif // USE_ESP32_SW_SERIAL
# endif // ESP32
2022-12-30 07:41:54 +00:00
typedef union {
uint8_t data ;
struct {
uint8_t trxenpol : 1 ; // string or number
uint8_t trxen : 1 ;
uint8_t trxenpin : 6 ;
} ;
} TRX_EN_TYPE ;
2021-12-15 13:46:05 +00:00
2022-12-30 07:41:54 +00:00
# ifndef TMSBSIZ
# define TMSBSIZ 256
# endif
2019-08-27 14:33:09 +01:00
2023-01-30 14:03:46 +00:00
# ifndef SML_STIMEOUT
# define SML_STIMEOUT 1000
# endif
2022-12-30 07:41:54 +00:00
# define SO_DWS74_BUG 1
# define SO_OBIS_LINE 2
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
# define METER_ID_SIZE 24
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
# define SML_CRYPT_SIZE 16
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
struct METER_DESC {
int8_t srcpin ;
uint8_t type ;
uint16_t flag ;
int32_t params ;
char prefix [ 8 ] ;
int8_t trxpin ;
uint8_t tsecs ;
char * txmem ;
uint8_t index ;
uint8_t max_index ;
char * script_str ;
uint8_t sopt ;
TRX_EN_TYPE trx_en ;
bool shift_mode ;
uint16_t sbsiz ;
uint8_t * sbuff ;
uint16_t spos ;
uint16_t sibsiz ;
2023-01-30 14:03:46 +00:00
uint32_t lastms ;
uint16_t tout_ms ;
2022-12-30 07:41:54 +00:00
uint8_t so_flags ;
char meter_id [ METER_ID_SIZE ] ;
# ifdef USE_SML_SPECOPT
uint32_t so_obis1 ;
uint32_t so_obis2 ;
uint8_t so_fcode1 ;
uint8_t so_bpos1 ;
uint8_t so_fcode2 ;
uint8_t so_bpos2 ;
# endif
# ifdef ESP32
# ifndef USE_ESP32_SW_SERIAL
HardwareSerial * meter_ss ;
# else
SML_ESP32_SERIAL * meter_ss ;
# endif
# endif // ESP32
// software serial pointers
# ifdef ESP8266
TasmotaSerial * meter_ss ;
# endif // ESP8266
# ifdef USE_SML_DECRYPT
2023-01-30 14:03:46 +00:00
bool use_crypt = false ;
2022-12-30 07:41:54 +00:00
uint8_t last_iob ;
uint8_t key [ SML_CRYPT_SIZE ] ;
2023-01-30 14:03:46 +00:00
Han_Parser * hp ;
# ifdef USE_SML_AUTHKEY
uint8_t auth [ SML_CRYPT_SIZE ] ;
# endif
2022-12-30 07:41:54 +00:00
# endif
2019-08-27 14:33:09 +01:00
} ;
2022-12-30 07:41:54 +00:00
struct METER_DESC meter_desc [ MAX_METERS ] ;
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
// this driver uses double because some meter vars would not fit in float
//=====================================================
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
// serial buffers, may be made larger depending on telegram lenght
# ifndef SML_BSIZ
# define SML_BSIZ 48
# endif
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
# define VBUS_SYNC 0xaa
# define SML_SYNC 0x77
# define EBUS_SYNC 0xaa
# define EBUS_ESC 0xa9
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
// calulate deltas
# define MAX_DVARS MAX_METERS*2
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
# ifndef SML_DUMP_SIZE
# define SML_DUMP_SIZE 128
# endif
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
// median filter, should be odd size
# define MEDIAN_SIZE 5
struct SML_MEDIAN_FILTER {
double buffer [ MEDIAN_SIZE ] ;
int8_t index ;
2019-08-27 14:33:09 +01:00
} ;
2022-12-30 07:41:54 +00:00
struct SML_GLOBS {
uint8_t sml_send_blocks ;
uint8_t sml_100ms_cnt ;
uint8_t sml_desc_cnt ;
uint8_t meters_used ;
uint8_t maxvars ;
uint8_t * meter_p ;
double * meter_vars ;
uint8_t * dvalid ;
double dvalues [ MAX_DVARS ] ;
uint32_t dtimes [ MAX_DVARS ] ;
char sml_start ;
uint8_t dump2log = 0 ;
uint8_t ser_act_LED_pin = 255 ;
uint8_t ser_act_meter_num = 0 ;
uint16_t sml_logindex ;
char * log_data ;
2023-01-30 14:03:46 +00:00
uint16_t logsize = SML_DUMP_SIZE ;
2022-12-30 07:41:54 +00:00
# if defined(ED300L) || defined(AS2020) || defined(DTZ541) || defined(USE_SML_SPECOPT)
uint8_t sml_status [ MAX_METERS ] ;
uint8_t g_mindex ;
# endif
# ifdef USE_SML_MEDIAN_FILTER
struct SML_MEDIAN_FILTER * sml_mf ;
# endif
uint8_t * script_meter ;
struct METER_DESC * mp ;
bool ready ;
} sml_globs ;
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
# define SML_OPTIONS_JSON_ENABLE 1
uint8_t sml_options = SML_OPTIONS_JSON_ENABLE ;
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
# ifdef USE_SML_MEDIAN_FILTER
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
# ifndef FLT_MAX
# define FLT_MAX 99999999
# endif
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
double sml_median_array ( double * array , uint8_t len ) {
uint8_t ind [ len ] ;
uint8_t mind = 0 , index = 0 , flg ;
double min = FLT_MAX ;
for ( uint8_t hcnt = 0 ; hcnt < len / 2 + 1 ; hcnt + + ) {
for ( uint8_t mcnt = 0 ; mcnt < len ; mcnt + + ) {
flg = 0 ;
for ( uint8_t icnt = 0 ; icnt < index ; icnt + + ) {
if ( ind [ icnt ] = = mcnt ) {
flg = 1 ;
}
}
if ( ! flg ) {
if ( array [ mcnt ] < min ) {
min = array [ mcnt ] ;
mind = mcnt ;
}
}
}
ind [ index ] = mind ;
index + + ;
min = FLT_MAX ;
}
return array [ ind [ len / 2 ] ] ;
}
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
// calc median
double sml_median ( struct SML_MEDIAN_FILTER * mf , double in ) {
//double tbuff[MEDIAN_SIZE],tmp;
//uint8_t flag;
mf - > buffer [ mf - > index ] = in ;
mf - > index + + ;
if ( mf - > index > = MEDIAN_SIZE ) mf - > index = 0 ;
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
return sml_median_array ( mf - > buffer , MEDIAN_SIZE ) ;
/*
// sort list and take median
memmove ( tbuff , mf - > buffer , sizeof ( tbuff ) ) ;
for ( byte ocnt = 0 ; ocnt < MEDIAN_SIZE ; ocnt + + ) {
flag = 0 ;
for ( byte count = 0 ; count < MEDIAN_SIZE - 1 ; count + + ) {
if ( tbuff [ count ] > tbuff [ count + 1 ] ) {
tmp = tbuff [ count ] ;
tbuff [ count ] = tbuff [ count + 1 ] ;
tbuff [ count + 1 ] = tmp ;
flag = 1 ;
}
}
if ( ! flag ) break ;
}
return tbuff [ MEDIAN_SIZE / 2 ] ;
*/
2019-08-27 14:33:09 +01:00
}
# endif
2022-12-30 07:41:54 +00:00
2022-01-20 19:19:50 +00:00
2019-08-27 14:33:09 +01:00
# define SML_SAVAILABLE Serial_available()
# define SML_SREAD Serial_read()
2023-01-30 14:03:46 +00:00
# define SML_SPEEK Serial_peek()
2019-08-27 14:33:09 +01:00
2023-01-30 14:03:46 +00:00
uint16_t Serial_available ( ) {
2022-12-30 07:41:54 +00:00
uint8_t num = sml_globs . dump2log & 7 ;
if ( num < 1 | | num > sml_globs . meters_used ) num = 1 ;
if ( ! meter_desc [ num - 1 ] . meter_ss ) return 0 ;
return meter_desc [ num - 1 ] . meter_ss - > available ( ) ;
2019-08-27 14:33:09 +01:00
}
uint8_t Serial_read ( ) {
2022-12-30 07:41:54 +00:00
uint8_t num = sml_globs . dump2log & 7 ;
if ( num < 1 | | num > sml_globs . meters_used ) num = 1 ;
if ( ! meter_desc [ num - 1 ] . meter_ss ) return 0 ;
return meter_desc [ num - 1 ] . meter_ss - > read ( ) ;
2019-08-27 14:33:09 +01:00
}
uint8_t Serial_peek ( ) {
2022-12-30 07:41:54 +00:00
uint8_t num = sml_globs . dump2log & 7 ;
if ( num < 1 | | num > sml_globs . meters_used ) num = 1 ;
if ( ! meter_desc [ num - 1 ] . meter_ss ) return 0 ;
return meter_desc [ num - 1 ] . meter_ss - > peek ( ) ;
2019-08-27 14:33:09 +01:00
}
2023-01-30 14:03:46 +00:00
void sml_dump_start ( char c ) {
sml_globs . log_data [ 0 ] = ' : ' ;
sml_globs . log_data [ 1 ] = c ;
sml_globs . sml_logindex = 2 ;
}
2019-09-30 09:53:16 +01:00
2021-02-25 13:36:37 +00:00
# define SML_EBUS_SKIP_SYNC_DUMPS
2023-01-30 14:03:46 +00:00
uint8_t * hdlc_decode ( struct METER_DESC * mp , uint16_t * size ) ;
2021-02-25 13:36:37 +00:00
2022-12-30 07:41:54 +00:00
void dump2log ( void ) {
2021-02-25 13:36:37 +00:00
int16_t index = 0 , hcnt = 0 ;
2020-12-23 17:09:21 +00:00
uint32_t d_lastms ;
uint8_t dchars [ 16 ] ;
2022-12-30 07:41:54 +00:00
uint8_t meter = ( sml_globs . dump2log & 7 ) - 1 ;
uint8_t type = sml_globs . mp [ meter ] . type ;
2019-08-27 14:33:09 +01:00
2019-09-30 09:53:16 +01:00
//if (!SML_SAVAILABLE) return;
2022-12-30 07:41:54 +00:00
if ( ! sml_globs . log_data ) return ;
# ifdef USE_SML_DECRYPT
struct METER_DESC * mp = & meter_desc [ meter ] ;
if ( mp - > use_crypt = = true ) {
d_lastms = millis ( ) ;
2023-01-30 14:03:46 +00:00
while ( ( millis ( ) - d_lastms ) < 50 ) {
2022-12-30 07:41:54 +00:00
while ( SML_SAVAILABLE ) {
2023-01-30 14:03:46 +00:00
d_lastms = millis ( ) ;
2022-12-30 07:41:54 +00:00
uint16_t logsiz ;
2023-01-30 14:03:46 +00:00
uint8_t * payload ;
if ( mp - > hp - > readHanPort ( & payload , & logsiz ) ) {
if ( logsiz > mp - > sbsiz ) {
logsiz = mp - > sbsiz ;
}
memmove ( mp - > sbuff , payload , logsiz ) ;
2023-02-06 21:21:16 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " SML: decrypted block: %d bytes " ) , logsiz ) ;
2022-12-30 07:41:54 +00:00
uint16_t index = 0 ;
while ( logsiz ) {
2023-01-30 14:03:46 +00:00
sml_dump_start ( ' > ' ) ;
2022-12-30 07:41:54 +00:00
for ( uint16_t cnt = 0 ; cnt < 16 ; cnt + + ) {
2023-01-30 14:03:46 +00:00
sprintf ( & sml_globs . log_data [ sml_globs . sml_logindex ] , " %02x " , mp - > sbuff [ index + + ] ) ;
if ( sml_globs . sml_logindex < sml_globs . logsize - 7 ) {
2022-12-30 07:41:54 +00:00
sml_globs . sml_logindex + = 3 ;
}
logsiz - - ;
if ( ! logsiz ) {
break ;
}
}
AddLogData ( LOG_LEVEL_INFO , sml_globs . log_data ) ;
}
2023-01-30 14:03:46 +00:00
} else {
// dump serial buffer
sml_dump_start ( ' ' ) ;
while ( index < mp - > spos ) {
sprintf ( & sml_globs . log_data [ sml_globs . sml_logindex ] , " %02x " , mp - > sbuff [ index + + ] ) ;
if ( sml_globs . sml_logindex > = 32 * 3 + 2 ) {
AddLogData ( LOG_LEVEL_INFO , sml_globs . log_data ) ;
sml_dump_start ( ' ' ) ;
}
}
2022-12-30 07:41:54 +00:00
}
}
}
2023-01-30 14:03:46 +00:00
if ( sml_globs . sml_logindex > 2 ) {
AddLogData ( LOG_LEVEL_INFO , sml_globs . log_data ) ;
sml_dump_start ( ' ' ) ;
}
mp - > hp - > len = 0 ;
2022-12-30 07:41:54 +00:00
return ;
}
# endif
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
if ( sml_globs . dump2log & 8 ) {
2019-08-27 14:33:09 +01:00
// combo mode
while ( SML_SAVAILABLE ) {
2022-12-30 07:41:54 +00:00
sml_globs . log_data [ index ] = ' : ' ;
2019-08-27 14:33:09 +01:00
index + + ;
2022-12-30 07:41:54 +00:00
sml_globs . log_data [ index ] = ' ' ;
2019-08-27 14:33:09 +01:00
index + + ;
2021-02-25 13:36:37 +00:00
d_lastms = millis ( ) ;
while ( ( millis ( ) - d_lastms ) < 40 ) {
2019-08-27 14:33:09 +01:00
if ( SML_SAVAILABLE ) {
2021-02-25 13:36:37 +00:00
uint8_t c = SML_SREAD ;
2022-12-30 07:41:54 +00:00
sprintf ( & sml_globs . log_data [ index ] , " %02x " , c ) ;
2021-02-25 13:36:37 +00:00
dchars [ hcnt ] = c ;
index + = 3 ;
2019-08-27 14:33:09 +01:00
hcnt + + ;
2021-02-25 13:36:37 +00:00
if ( hcnt > 15 ) {
2019-08-27 14:33:09 +01:00
// line complete, build asci chars
2023-01-30 14:03:46 +00:00
sml_globs . log_data [ index + + ] = ' = ' ;
sml_globs . log_data [ index + + ] = ' > ' ;
sml_globs . log_data [ index + + ] = ' ' ;
2021-02-25 13:36:37 +00:00
for ( uint8_t ccnt = 0 ; ccnt < 16 ; ccnt + + ) {
2019-08-27 14:33:09 +01:00
if ( isprint ( dchars [ ccnt ] ) ) {
2022-12-30 07:41:54 +00:00
sml_globs . log_data [ index ] = dchars [ ccnt ] ;
2019-08-27 14:33:09 +01:00
} else {
2022-12-30 07:41:54 +00:00
sml_globs . log_data [ index ] = ' ' ;
2019-08-27 14:33:09 +01:00
}
index + + ;
}
break ;
}
}
}
2021-02-25 13:36:37 +00:00
if ( index > 0 ) {
2022-12-30 07:41:54 +00:00
sml_globs . log_data [ index ] = 0 ;
AddLogData ( LOG_LEVEL_INFO , sml_globs . log_data ) ;
2021-02-25 13:36:37 +00:00
index = 0 ;
hcnt = 0 ;
2019-08-27 14:33:09 +01:00
}
}
} else {
2023-01-13 12:23:25 +00:00
switch ( type ) {
case ' o ' :
// obis
while ( SML_SAVAILABLE ) {
char c = SML_SREAD & 0x7f ;
if ( c = = ' \n ' | | c = = ' \r ' ) {
if ( sml_globs . sml_logindex > 2 ) {
sml_globs . log_data [ sml_globs . sml_logindex ] = 0 ;
AddLogData ( LOG_LEVEL_INFO , sml_globs . log_data ) ;
2023-01-30 14:03:46 +00:00
sml_dump_start ( ' ' ) ;
2023-01-13 12:23:25 +00:00
}
continue ;
}
sml_globs . log_data [ sml_globs . sml_logindex ] = c ;
2023-01-30 14:03:46 +00:00
if ( sml_globs . sml_logindex < sml_globs . logsize - 2 ) {
2023-01-13 12:23:25 +00:00
sml_globs . sml_logindex + + ;
}
}
break ;
case ' v ' :
// vbus
{ uint8_t c ;
while ( SML_SAVAILABLE ) {
c = SML_SREAD ;
if ( c = = VBUS_SYNC ) {
sml_globs . log_data [ sml_globs . sml_logindex ] = 0 ;
AddLogData ( LOG_LEVEL_INFO , sml_globs . log_data ) ;
2023-01-30 14:03:46 +00:00
sml_dump_start ( ' ' ) ;
2023-01-13 12:23:25 +00:00
}
sprintf ( & sml_globs . log_data [ sml_globs . sml_logindex ] , " %02x " , c ) ;
2023-01-30 14:03:46 +00:00
if ( sml_globs . sml_logindex < sml_globs . logsize - 7 ) {
2023-01-13 12:23:25 +00:00
sml_globs . sml_logindex + = 3 ;
}
}
}
break ;
case ' e ' :
// ebus
{ uint8_t c , p ;
while ( SML_SAVAILABLE ) {
c = SML_SREAD ;
if ( c = = EBUS_SYNC ) {
2023-01-30 14:03:46 +00:00
p = SML_SPEEK ;
2023-01-13 12:23:25 +00:00
if ( p ! = EBUS_SYNC & & sml_globs . sml_logindex > 5 ) {
// new packet, plot last one
sml_globs . log_data [ sml_globs . sml_logindex ] = 0 ;
AddLogData ( LOG_LEVEL_INFO , sml_globs . log_data ) ;
strcpy ( & sml_globs . log_data [ 0 ] , " : aa " ) ;
sml_globs . sml_logindex = 5 ;
}
continue ;
}
sprintf ( & sml_globs . log_data [ sml_globs . sml_logindex ] , " %02x " , c ) ;
2023-01-30 14:03:46 +00:00
if ( sml_globs . sml_logindex < sml_globs . logsize - 7 ) {
2023-01-13 12:23:25 +00:00
sml_globs . sml_logindex + = 3 ;
}
}
}
break ;
case ' s ' :
// sml
{ uint8_t c ;
while ( SML_SAVAILABLE ) {
c = SML_SREAD ;
if ( c = = SML_SYNC ) {
sml_globs . log_data [ sml_globs . sml_logindex ] = 0 ;
AddLogData ( LOG_LEVEL_INFO , sml_globs . log_data ) ;
2023-01-30 14:03:46 +00:00
sml_dump_start ( ' ' ) ;
2023-01-13 12:23:25 +00:00
}
sprintf ( & sml_globs . log_data [ sml_globs . sml_logindex ] , " %02x " , c ) ;
2023-01-30 14:03:46 +00:00
if ( sml_globs . sml_logindex < sml_globs . logsize - 7 ) {
2023-01-13 12:23:25 +00:00
sml_globs . sml_logindex + = 3 ;
}
}
}
break ;
default :
// raw dump
d_lastms = millis ( ) ;
2023-01-30 14:03:46 +00:00
sml_dump_start ( ' ' ) ;
while ( ( millis ( ) - d_lastms ) < 40 ) {
while ( SML_SAVAILABLE ) {
d_lastms = millis ( ) ;
yield ( ) ;
2023-01-13 12:23:25 +00:00
sprintf ( & sml_globs . log_data [ sml_globs . sml_logindex ] , " %02x " , SML_SREAD ) ;
2023-01-30 14:03:46 +00:00
if ( sml_globs . sml_logindex < sml_globs . logsize - 7 ) {
2023-01-13 12:23:25 +00:00
sml_globs . sml_logindex + = 3 ;
2023-01-30 14:03:46 +00:00
}
if ( sml_globs . sml_logindex > = 32 * 3 + 2 ) {
AddLogData ( LOG_LEVEL_INFO , sml_globs . log_data ) ;
sml_dump_start ( ' ' ) ;
2023-01-13 12:23:25 +00:00
}
2023-01-30 14:03:46 +00:00
}
2023-01-13 12:23:25 +00:00
}
if ( sml_globs . sml_logindex > 2 ) {
sml_globs . log_data [ sml_globs . sml_logindex ] = 0 ;
AddLogData ( LOG_LEVEL_INFO , sml_globs . log_data ) ;
}
break ;
2023-01-30 14:03:46 +00:00
}
2019-09-30 09:53:16 +01:00
}
2019-08-27 14:33:09 +01:00
}
2021-02-25 13:36:37 +00:00
void Hexdump ( uint8_t * sbuff , uint32_t slen ) {
char cbuff [ slen * 3 + 10 ] ;
char * cp = cbuff ;
* cp + + = ' > ' ;
* cp + + = ' ' ;
for ( uint32_t cnt = 0 ; cnt < slen ; cnt + + ) {
sprintf ( cp , " %02x " , sbuff [ cnt ] ) ;
cp + = 3 ;
}
AddLogData ( LOG_LEVEL_INFO , cbuff ) ;
}
2022-12-30 07:41:54 +00:00
# define DOUBLE2CHAR dtostrfd
2020-03-29 15:29:22 +01:00
2019-08-27 14:33:09 +01:00
// skip sml entries
uint8_t * skip_sml ( uint8_t * cp , int16_t * res ) {
uint8_t len , len1 , type ;
len = * cp & 0xf ;
type = * cp & 0x70 ;
if ( type = = 0x70 ) {
// list, skip entries
// list
cp + + ;
while ( len - - ) {
len1 = * cp & 0x0f ;
cp + = len1 ;
}
* res = 0 ;
} else {
// skip len
* res = ( signed char ) * ( cp + 1 ) ;
cp + = len ;
}
return cp ;
}
// get sml binary value
// not defined for unsigned >0x7fff ffff ffff ffff (should never happen)
2022-12-30 07:41:54 +00:00
double sml_getvalue ( unsigned char * cp , uint8_t index ) {
2019-08-27 14:33:09 +01:00
uint8_t len , unit , type ;
int16_t scaler , result ;
int64_t value ;
double dval ;
// scan for values
// check status
2020-03-29 15:29:22 +01:00
# ifdef ED300L
unsigned char * cpx = cp - 5 ;
// decode OBIS 0180 amd extract direction info
if ( * cp = = 0x64 & & * cpx = = 0 & & * ( cpx + 1 ) = = 0x01 & & * ( cpx + 2 ) = = 0x08 & & * ( cpx + 3 ) = = 0 ) {
2022-12-30 07:41:54 +00:00
sml_globs . sml_status [ sml_globs . g_mindex ] = * ( cp + 3 ) ;
2020-03-29 15:29:22 +01:00
}
2020-07-05 07:55:34 +01:00
if ( * cp = = 0x63 & & * cpx = = 0 & & * ( cpx + 1 ) = = 0x01 & & * ( cpx + 2 ) = = 0x08 & & * ( cpx + 3 ) = = 0 ) {
2022-12-30 07:41:54 +00:00
sml_globs . sml_status [ sml_globs . g_mindex ] = * ( cp + 2 ) ;
2020-07-05 07:55:34 +01:00
}
2020-03-29 15:29:22 +01:00
# endif
2022-06-28 08:07:58 +01:00
# ifdef AS2020
unsigned char * cpx = cp - 5 ;
// decode OBIS 0180 amd extract direction info
if ( * cp = = 0x64 & & * cpx = = 0 & & * ( cpx + 1 ) = = 0x01 & & * ( cpx + 2 ) = = 0x08 & & * ( cpx + 3 ) = = 0 ) {
2022-12-30 07:41:54 +00:00
sml_globs . sml_status [ sml_globs . g_mindex ] = * ( cp + 2 ) ;
2022-06-28 08:07:58 +01:00
}
if ( * cp = = 0x63 & & * cpx = = 0 & & * ( cpx + 1 ) = = 0x01 & & * ( cpx + 2 ) = = 0x08 & & * ( cpx + 3 ) = = 0 ) {
2022-12-30 07:41:54 +00:00
sml_globs . sml_status [ sml_globs . g_mindex ] = * ( cp + 1 ) ;
2022-06-28 08:07:58 +01:00
}
# endif
2022-07-20 13:25:40 +01:00
# ifdef DTZ541
unsigned char * cpx = cp - 5 ;
// decode OBIS 0180 amd extract direction info
if ( * cp = = 0x65 & & * cpx = = 0 & & * ( cpx + 1 ) = = 0x01 & & * ( cpx + 2 ) = = 0x08 & & * ( cpx + 3 ) = = 0 ) {
2022-12-30 07:41:54 +00:00
sml_globs . sml_status [ sml_globs . g_mindex ] = * ( cp + 3 ) ;
2022-07-20 13:25:40 +01:00
}
# endif
# ifdef USE_SML_SPECOPT
unsigned char * cpx = cp - 5 ;
2022-12-30 07:41:54 +00:00
uint32_t ocode = ( * ( cpx + 0 ) < < 24 ) | ( * ( cpx + 1 ) < < 16 ) | ( * ( cpx + 2 ) < < 8 ) | ( * ( cpx + 3 ) < < 0 ) ;
2022-07-20 13:25:40 +01:00
2022-12-30 07:41:54 +00:00
if ( ocode = = meter_desc [ sml_globs . g_mindex ] . so_obis1 ) {
sml_globs . sml_status [ sml_globs . g_mindex ] & = 0xfe ;
2022-07-20 13:25:40 +01:00
uint32_t flag = 0 ;
uint16_t bytes = 0 ;
2022-12-30 07:41:54 +00:00
if ( * cp = = meter_desc [ sml_globs . g_mindex ] . so_fcode1 ) {
2022-07-20 13:25:40 +01:00
cpx = cp + 1 ;
2022-12-30 07:41:54 +00:00
bytes = ( meter_desc [ sml_globs . g_mindex ] . so_fcode1 & 0xf ) - 1 ;
2022-07-20 13:25:40 +01:00
for ( uint16_t cnt = 0 ; cnt < bytes ; cnt + + ) {
flag < < = 8 ;
flag | = * cpx + + ;
}
2022-12-30 07:41:54 +00:00
if ( flag & ( 1 < < meter_desc [ sml_globs . g_mindex ] . so_bpos1 ) ) {
sml_globs . sml_status [ sml_globs . g_mindex ] | = 1 ;
2022-07-20 13:25:40 +01:00
}
}
2022-12-30 07:41:54 +00:00
if ( * cp = = meter_desc [ sml_globs . g_mindex ] . so_fcode2 ) {
2022-07-20 13:25:40 +01:00
cpx = cp + 1 ;
2022-12-30 07:41:54 +00:00
bytes = ( meter_desc [ sml_globs . g_mindex ] . so_fcode2 & 0xf ) - 1 ;
2022-07-20 13:25:40 +01:00
for ( uint16_t cnt = 0 ; cnt < bytes ; cnt + + ) {
flag < < = 8 ;
flag | = * cpx + + ;
}
2022-12-30 07:41:54 +00:00
if ( flag & ( 1 < < meter_desc [ sml_globs . g_mindex ] . so_bpos1 ) ) {
sml_globs . sml_status [ sml_globs . g_mindex ] | = 1 ;
2022-07-20 13:25:40 +01:00
}
}
}
# endif
2020-03-29 15:29:22 +01:00
2022-12-30 07:41:54 +00:00
cp = skip_sml ( cp , & result ) ;
2019-08-27 14:33:09 +01:00
// check time
2022-12-30 07:41:54 +00:00
cp = skip_sml ( cp , & result ) ;
2019-08-27 14:33:09 +01:00
// check unit
2022-12-30 07:41:54 +00:00
cp = skip_sml ( cp , & result ) ;
2019-08-27 14:33:09 +01:00
// check scaler
2022-12-30 07:41:54 +00:00
cp = skip_sml ( cp , & result ) ;
scaler = result ;
2020-03-29 15:29:22 +01:00
2019-08-27 14:33:09 +01:00
// get value
2022-12-30 07:41:54 +00:00
type = * cp & 0x70 ;
len = * cp & 0x0f ;
2019-08-27 14:33:09 +01:00
cp + + ;
2022-12-30 07:41:54 +00:00
if ( type = = 0x50 | | type = = 0x60 ) {
2019-08-27 14:33:09 +01:00
// shift into 64 bit
2022-12-30 07:41:54 +00:00
uint64_t uvalue = 0 ;
uint8_t nlen = len ;
2019-08-27 14:33:09 +01:00
while ( - - nlen ) {
2022-12-30 07:41:54 +00:00
uvalue < < = 8 ;
uvalue | = * cp + + ;
2019-08-27 14:33:09 +01:00
}
2022-12-30 07:41:54 +00:00
if ( type = = 0x50 ) {
2019-08-27 14:33:09 +01:00
// signed
2022-12-30 07:41:54 +00:00
switch ( len - 1 ) {
2019-08-27 14:33:09 +01:00
case 1 :
// byte
2022-12-30 07:41:54 +00:00
value = ( signed char ) uvalue ;
2019-08-27 14:33:09 +01:00
break ;
case 2 :
// signed 16 bit
2022-12-30 07:41:54 +00:00
if ( meter_desc [ index ] . so_flags & SO_DWS74_BUG ) {
if ( scaler = = - 2 ) {
value = ( uint32_t ) uvalue ;
} else {
value = ( int16_t ) uvalue ;
}
2019-08-27 14:33:09 +01:00
} else {
2022-12-30 07:41:54 +00:00
value = ( int16_t ) uvalue ;
2019-08-27 14:33:09 +01:00
}
break ;
case 3 :
2021-03-09 11:10:37 +00:00
// signed 24 bit
2022-12-30 07:41:54 +00:00
value = ( int32_t ) ( uvalue < < 8 ) ;
value / = 256 ;
2021-03-09 11:10:37 +00:00
break ;
2019-08-27 14:33:09 +01:00
case 4 :
// signed 32 bit
2022-12-30 07:41:54 +00:00
value = ( int32_t ) uvalue ;
2019-08-27 14:33:09 +01:00
break ;
case 5 :
case 6 :
case 7 :
case 8 :
// signed 64 bit
2022-12-30 07:41:54 +00:00
value = ( int64_t ) uvalue ;
2019-08-27 14:33:09 +01:00
break ;
}
} else {
// unsigned
2022-12-30 07:41:54 +00:00
value = uvalue ;
2019-08-27 14:33:09 +01:00
}
} else {
2022-12-30 07:41:54 +00:00
if ( ! ( type & 0xf0 ) ) {
2019-08-27 14:33:09 +01:00
// octet string serial number
// no coding found on the net
// up to now 2 types identified on Hager
2022-12-30 07:41:54 +00:00
if ( len = = 9 ) {
2019-08-27 14:33:09 +01:00
// serial number on hager => 24 bit - 24 bit
cp + + ;
uint32_t s1 , s2 ;
2022-12-30 07:41:54 +00:00
s1 = * cp < < 16 | * ( cp + 1 ) < < 8 | * ( cp + 2 ) ;
cp + = 4 ;
s2 = * cp < < 16 | * ( cp + 1 ) < < 8 | * ( cp + 2 ) ;
sprintf ( & meter_desc [ index ] . meter_id [ 0 ] , " %u-%u " , s1 , s2 ) ;
2019-08-27 14:33:09 +01:00
} else {
// server id on hager
2022-12-30 07:41:54 +00:00
char * str = & meter_desc [ index ] . meter_id [ 0 ] ;
for ( type = 0 ; type < len - 1 ; type + + ) {
sprintf ( str , " %02x " , * cp + + ) ;
str + = 2 ;
2019-08-27 14:33:09 +01:00
}
}
2022-12-30 07:41:54 +00:00
value = 0 ;
2019-08-27 14:33:09 +01:00
} else {
2022-12-30 07:41:54 +00:00
value = 999999 ;
scaler = 0 ;
2019-08-27 14:33:09 +01:00
}
}
2022-12-30 07:41:54 +00:00
dval = value ;
if ( scaler = = - 1 ) {
dval / = 10 ;
} else if ( scaler = = - 2 ) {
dval / = 100 ;
} else if ( scaler = = - 3 ) {
dval / = 1000 ;
} else if ( scaler = = - 4 ) {
dval / = 10000 ;
} else if ( scaler = = 1 ) {
dval * = 10 ;
} else if ( scaler = = 2 ) {
dval * = 100 ;
} else if ( scaler = = 3 ) {
dval * = 1000 ;
2019-08-27 14:33:09 +01:00
}
2020-03-29 15:29:22 +01:00
# ifdef ED300L
// decode current power OBIS 00 0F 07 00
if ( * cpx = = 0x00 & & * ( cpx + 1 ) = = 0x0f & & * ( cpx + 2 ) = = 0x07 & & * ( cpx + 3 ) = = 0 ) {
2022-12-30 07:41:54 +00:00
if ( sml_globs . sml_status [ sml_globs . g_mindex ] & 0x20 ) {
2020-03-29 15:29:22 +01:00
// and invert sign on solar feed
dval * = - 1 ;
}
}
2022-06-28 08:07:58 +01:00
# endif
# ifdef AS2020
// decode current power OBIS 00 10 07 00
if ( * cpx = = 0x00 & & * ( cpx + 1 ) = = 0x10 & & * ( cpx + 2 ) = = 0x07 & & * ( cpx + 3 ) = = 0 ) {
2022-12-30 07:41:54 +00:00
if ( sml_globs . sml_status [ sml_globs . g_mindex ] & 0x08 ) {
2022-06-28 08:07:58 +01:00
// and invert sign on solar feed
dval * = - 1 ;
}
}
2020-03-29 15:29:22 +01:00
# endif
2022-07-20 13:25:40 +01:00
# ifdef DTZ541
// decode current power OBIS 00 10 07 00
if ( * cpx = = 0x00 & & * ( cpx + 1 ) = = 0x10 & & * ( cpx + 2 ) = = 0x07 & & * ( cpx + 3 ) = = 0 ) {
2022-12-30 07:41:54 +00:00
if ( sml_globs . sml_status [ sml_globs . g_mindex ] & 0x08 ) {
2022-07-20 13:25:40 +01:00
// and invert sign on solar feed
dval * = - 1 ;
}
}
# endif
# ifdef USE_SML_SPECOPT
2022-12-30 07:41:54 +00:00
ocode = ( * ( cpx + 0 ) < < 24 ) | ( * ( cpx + 1 ) < < 16 ) | ( * ( cpx + 2 ) < < 8 ) | ( * ( cpx + 3 ) < < 0 ) ;
if ( ocode = = meter_desc [ sml_globs . g_mindex ] . so_obis2 ) {
if ( sml_globs . sml_status [ sml_globs . g_mindex ] & 1 ) {
2022-07-20 13:25:40 +01:00
// and invert sign on solar feed
2022-12-30 07:41:54 +00:00
dval * = - 1 ;
2022-07-20 13:25:40 +01:00
}
}
# endif
2019-08-27 14:33:09 +01:00
return dval ;
}
uint8_t hexnibble ( char chr ) {
uint8_t rVal = 0 ;
if ( isdigit ( chr ) ) {
rVal = chr - ' 0 ' ;
} else {
chr = toupper ( chr ) ;
if ( chr > = ' A ' & & chr < = ' F ' ) rVal = chr + 10 - ' A ' ;
}
return rVal ;
}
uint8_t sb_counter ;
2019-09-16 18:58:22 +01:00
// need double precision in this driver
double CharToDouble ( const char * str )
2019-08-27 14:33:09 +01:00
{
// simple ascii to double, because atof or strtod are too large
char strbuf [ 24 ] ;
strlcpy ( strbuf , str , sizeof ( strbuf ) ) ;
char * pt = strbuf ;
2021-08-16 10:11:46 +01:00
while ( ( * pt ! = ' \0 ' ) & & isspace ( * pt ) ) { pt + + ; } // Trim leading spaces
2019-08-27 14:33:09 +01:00
signed char sign = 1 ;
if ( * pt = = ' - ' ) { sign = - 1 ; }
if ( * pt = = ' - ' | | * pt = = ' + ' ) { pt + + ; } // Skip any sign
double left = 0 ;
if ( * pt ! = ' . ' ) {
2022-12-30 07:41:54 +00:00
left = atoll ( pt ) ; // Get left part
2019-08-27 14:33:09 +01:00
while ( isdigit ( * pt ) ) { pt + + ; } // Skip number
}
double right = 0 ;
if ( * pt = = ' . ' ) {
pt + + ;
2022-12-30 07:41:54 +00:00
right = atoll ( pt ) ; // Decimal part
2019-08-27 14:33:09 +01:00
while ( isdigit ( * pt ) ) {
pt + + ;
right / = 10.0 ;
}
}
double result = left + right ;
if ( sign < 0 ) {
return - result ; // Add negative sign
}
return result ;
}
// remove ebus escapes
void ebus_esc ( uint8_t * ebus_buffer , unsigned char len ) {
short count , count1 ;
2022-12-30 07:41:54 +00:00
for ( count = 0 ; count < len ; count + + ) {
if ( ebus_buffer [ count ] = = EBUS_ESC ) {
2019-08-27 14:33:09 +01:00
//found escape
2022-12-30 07:41:54 +00:00
ebus_buffer [ count ] + = ebus_buffer [ count + 1 ] ;
2019-08-27 14:33:09 +01:00
// remove 2. char
count + + ;
2022-12-30 07:41:54 +00:00
for ( count1 = count ; count1 < len ; count1 + + ) {
ebus_buffer [ count1 ] = ebus_buffer [ count1 + 1 ] ;
2019-08-27 14:33:09 +01:00
}
}
}
}
uint8_t ebus_crc8 ( uint8_t data , uint8_t crc_init ) {
uint8_t crc ;
uint8_t polynom ;
int i ;
crc = crc_init ;
for ( i = 0 ; i < 8 ; i + + ) {
if ( crc & 0x80 ) {
polynom = ( uint8_t ) 0x9B ;
}
else {
polynom = ( uint8_t ) 0 ;
}
crc = ( uint8_t ) ( ( crc & ~ 0x80 ) < < 1 ) ;
if ( data & 0x80 ) {
crc = ( uint8_t ) ( crc | 1 ) ;
}
crc = ( uint8_t ) ( crc ^ polynom ) ;
data = ( uint8_t ) ( data < < 1 ) ;
}
return ( crc ) ;
}
// ebus crc
uint8_t ebus_CalculateCRC ( uint8_t * Data , uint16_t DataLen ) {
uint16_t i ;
uint8_t Crc = 0 ;
for ( i = 0 ; i < DataLen ; + + i , + + Data ) {
Crc = ebus_crc8 ( * Data , Crc ) ;
}
return Crc ;
}
2019-09-16 18:58:22 +01:00
void sml_empty_receiver ( uint32_t meters ) {
2022-12-30 07:41:54 +00:00
while ( meter_desc [ meters ] . meter_ss - > available ( ) ) {
meter_desc [ meters ] . meter_ss - > read ( ) ;
2019-09-16 18:58:22 +01:00
}
}
2022-12-30 07:41:54 +00:00
void sml_shift_in ( uint32_t meters , uint32_t shard ) {
2019-08-27 14:33:09 +01:00
uint32_t count ;
2022-11-28 06:41:37 +00:00
2022-12-30 07:41:54 +00:00
struct METER_DESC * mp = & meter_desc [ meters ] ;
2022-12-04 06:47:21 +00:00
2022-12-30 07:41:54 +00:00
if ( ! mp - > sbuff ) return ;
# ifdef USE_SML_DECRYPT
if ( mp - > use_crypt ) {
2023-01-30 14:03:46 +00:00
if ( mp - > hp ) {
uint32_t timediff = millis ( ) - mp - > lastms ;
if ( timediff > mp - > tout_ms ) {
mp - > hp - > len = 0 ;
mp - > spos = 0 ;
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SML: sync " ) ) ;
}
mp - > lastms = millis ( ) ;
uint16_t len ;
uint8_t * payload ;
if ( mp - > hp - > readHanPort ( & payload , & len ) ) {
if ( len > mp - > sbsiz ) {
len = mp - > sbsiz ;
}
memmove ( mp - > sbuff , payload , len ) ;
2023-02-03 09:50:53 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SML: decrypted block: %d bytes " ) , len ) ;
2023-01-30 14:03:46 +00:00
SML_Decode ( meters ) ;
}
2022-12-30 07:41:54 +00:00
}
return ;
}
# endif
2022-11-28 06:41:37 +00:00
2022-12-30 07:41:54 +00:00
if ( mp - > shift_mode ) {
2019-08-27 14:33:09 +01:00
// shift in
2022-12-30 07:41:54 +00:00
for ( count = 0 ; count < mp - > sbsiz - 1 ; count + + ) {
mp - > sbuff [ count ] = mp - > sbuff [ count + 1 ] ;
2019-08-27 14:33:09 +01:00
}
}
2022-12-30 07:41:54 +00:00
uint8_t iob = ( uint8_t ) mp - > meter_ss - > read ( ) ;
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
switch ( mp - > type ) {
case ' o ' :
// asci obis
if ( ! ( mp - > so_flags & SO_OBIS_LINE ) ) {
mp - > sbuff [ mp - > sbsiz - 1 ] = iob & 0x7f ;
} else {
iob & = 0x7f ;
mp - > sbuff [ mp - > spos ] = iob ;
mp - > spos + + ;
if ( mp - > spos > = mp - > sbsiz ) {
mp - > spos = 0 ;
}
if ( ( iob = = 0x0a ) | | ( iob = = 0x0d ) ) {
SML_Decode ( meters ) ;
mp - > spos = 0 ;
}
2022-11-28 06:41:37 +00:00
}
2022-12-30 07:41:54 +00:00
break ;
case ' s ' :
// binary obis = sml
mp - > sbuff [ mp - > sbsiz - 1 ] = iob ;
break ;
case ' r ' :
// raw with shift
mp - > sbuff [ mp - > sbsiz - 1 ] = iob ;
break ;
case ' R ' :
// raw without shift
2023-01-30 14:03:46 +00:00
{
uint32_t timediff = millis ( ) - mp - > lastms ;
if ( timediff > mp - > tout_ms ) {
mp - > spos = 0 ;
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SML: sync " ) ) ;
}
mp - > lastms = millis ( ) ;
2022-12-30 07:41:54 +00:00
mp - > sbuff [ mp - > spos ] = iob ;
mp - > spos + + ;
if ( mp - > spos > mp - > sbsiz ) {
mp - > spos = 0 ;
2022-11-28 06:41:37 +00:00
}
2023-01-30 14:03:46 +00:00
}
2022-12-30 07:41:54 +00:00
break ;
case ' k ' :
2022-09-16 11:27:16 +01:00
// Kamstrup
if ( iob = = 0x40 ) {
2022-12-30 07:41:54 +00:00
mp - > spos = 0 ;
2022-09-16 11:27:16 +01:00
} else if ( iob = = 0x0d ) {
2022-10-03 15:08:49 +01:00
uint8_t index = 0 ;
2022-12-30 07:41:54 +00:00
uint8_t * ucp = & mp - > sbuff [ 0 ] ;
for ( uint16_t cnt = 0 ; cnt < mp - > spos ; cnt + + ) {
uint8_t iob = mp - > sbuff [ cnt ] ;
2022-10-03 15:08:49 +01:00
if ( iob = = 0x1b ) {
2022-12-30 07:41:54 +00:00
* ucp + + = mp - > sbuff [ cnt + 1 ] ^ 0xff ;
2022-10-03 15:08:49 +01:00
cnt + + ;
} else {
* ucp + + = iob ;
2022-09-16 11:27:16 +01:00
}
2022-10-03 15:08:49 +01:00
index + + ;
}
2022-12-30 07:41:54 +00:00
uint16_t crc = KS_calculateCRC ( & mp - > sbuff [ 0 ] , index ) ;
2022-10-03 15:08:49 +01:00
if ( ! crc ) {
2022-09-16 11:27:16 +01:00
SML_Decode ( meters ) ;
}
2020-08-09 08:36:24 +01:00
sml_empty_receiver ( meters ) ;
2022-12-30 07:41:54 +00:00
mp - > spos = 0 ;
2022-09-16 11:27:16 +01:00
} else {
2022-12-30 07:41:54 +00:00
mp - > sbuff [ mp - > spos ] = iob ;
mp - > spos + + ;
if ( mp - > spos > = mp - > sbsiz ) {
mp - > spos = 0 ;
2022-09-16 11:27:16 +01:00
}
}
2022-12-30 07:41:54 +00:00
break ;
case ' m ' :
case ' M ' :
2022-09-16 11:27:16 +01:00
// modbus
2022-12-30 07:41:54 +00:00
mp - > sbuff [ mp - > spos ] = iob ;
mp - > spos + + ;
if ( mp - > spos > = mp - > sbsiz ) {
mp - > spos = 0 ;
}
if ( mp - > spos > = 3 ) {
uint32_t mlen = mp - > sbuff [ 2 ] + 5 ;
if ( mlen > mp - > sbsiz ) mlen = mp - > sbsiz ;
if ( mp - > spos > = mlen ) {
2022-09-16 11:27:16 +01:00
# ifdef MODBUS_DEBUG
2022-12-30 07:41:54 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " receive index >> %d " ) , mp - > index ) ;
Hexdump ( mp - > sbuff , 10 ) ;
2022-09-16 11:27:16 +01:00
# endif
SML_Decode ( meters ) ;
sml_empty_receiver ( meters ) ;
2022-12-30 07:41:54 +00:00
mp - > spos = 0 ;
2022-09-16 11:27:16 +01:00
}
2020-08-09 08:36:24 +01:00
}
2022-12-30 07:41:54 +00:00
break ;
case ' p ' :
// pzem
mp - > sbuff [ mp - > spos ] = iob ;
mp - > spos + + ;
if ( mp - > spos > = 7 ) {
SML_Decode ( meters ) ;
sml_empty_receiver ( meters ) ;
mp - > spos = 0 ;
2021-02-24 08:11:21 +00:00
}
2022-12-30 07:41:54 +00:00
break ;
case ' v ' :
// vbus
if ( iob = = EBUS_SYNC ) {
sb_counter = 0 ;
SML_Decode ( meters ) ;
mp - > sbuff [ 0 ] = iob ;
mp - > spos = 1 ;
} else {
if ( mp - > spos < mp - > sbsiz ) {
mp - > sbuff [ mp - > spos ] = iob ;
mp - > spos + + ;
2019-08-27 14:33:09 +01:00
}
}
2022-12-30 07:41:54 +00:00
break ;
case ' e ' :
// ebus
if ( iob = = EBUS_SYNC ) {
// should be end of telegramm
// QQ,ZZ,PB,SB,NN ..... CRC, ACK SYNC
if ( mp - > spos > 4 + 5 ) {
// get telegramm lenght
uint16_t tlen = mp - > sbuff [ 4 ] + 5 ;
// test crc
if ( mp - > sbuff [ tlen ] = ebus_CalculateCRC ( mp - > sbuff , tlen ) ) {
ebus_esc ( mp - > sbuff , tlen ) ;
SML_Decode ( meters ) ;
} else {
// crc error
//AddLog(LOG_LEVEL_INFO, PSTR("ebus crc error"));
}
}
mp - > spos = 0 ;
return ;
}
mp - > sbuff [ mp - > spos ] = iob ;
mp - > spos + + ;
if ( mp - > spos > = mp - > sbsiz ) {
mp - > spos = 0 ;
}
break ;
2019-08-27 14:33:09 +01:00
}
sb_counter + + ;
2022-11-28 06:41:37 +00:00
2022-12-30 07:41:54 +00:00
if ( mp - > shift_mode ) {
2022-11-28 06:41:37 +00:00
SML_Decode ( meters ) ;
}
2019-08-27 14:33:09 +01:00
}
2023-01-30 14:03:46 +00:00
uint16_t sml_count = 0 ;
2019-08-27 14:33:09 +01:00
// polled every 50 ms
void SML_Poll ( void ) {
uint32_t meters ;
2022-12-30 07:41:54 +00:00
for ( meters = 0 ; meters < sml_globs . meters_used ; meters + + ) {
if ( sml_globs . mp [ meters ] . type ! = ' c ' ) {
2019-08-27 14:33:09 +01:00
// poll for serial input
2022-12-30 07:41:54 +00:00
if ( ! meter_desc [ meters ] . meter_ss ) continue ;
if ( sml_globs . ser_act_LED_pin ! = 255 & & ( sml_globs . ser_act_meter_num = = 0 | | sml_globs . ser_act_meter_num - 1 = = meters ) ) {
digitalWrite ( sml_globs . ser_act_LED_pin , meter_desc [ meters ] . meter_ss - > available ( ) & & ! digitalRead ( sml_globs . ser_act_LED_pin ) ) ; // Invert LED, if queue is continuously full
2022-01-20 19:19:50 +00:00
}
2022-12-30 07:41:54 +00:00
while ( meter_desc [ meters ] . meter_ss - > available ( ) ) {
2021-12-15 13:46:05 +00:00
sml_shift_in ( meters , 0 ) ;
2019-08-27 14:33:09 +01:00
}
2023-01-30 14:03:46 +00:00
/*
if ( meter_desc [ meters ] . meter_ss - > available ( ) ) {
sml_count + + ;
uint8_t iob = meter_desc [ meters ] . meter_ss - > read ( ) ;
if ( sml_count < 5 | | sml_count > 100 ) {
AddLog ( LOG_LEVEL_INFO , PSTR ( " >> %02x - %d " ) , iob , sml_count ) ;
}
} */
2019-08-27 14:33:09 +01:00
}
}
}
2021-02-24 08:11:21 +00:00
# define VBUS_BAD_CRC 0
// get vbus septet with 6 bytes
uint32_t vbus_get_septet ( uint8_t * cp ) {
uint32_t result = 0 ;
//AddLog(LOG_LEVEL_INFO,PSTR("septet: %02x %02x %02x %02x %02x %02x"),cp[0] ,cp[1],cp[2],cp[3],cp[4],cp[5]);
uint8_t Crc = 0x7F ;
for ( uint32_t i = 0 ; i < 5 ; i + + ) {
Crc = ( Crc - cp [ i ] ) & 0x7f ;
}
if ( Crc ! = cp [ 5 ] ) {
result = VBUS_BAD_CRC ;
} else {
result = ( cp [ 3 ] | ( ( cp [ 4 ] & 8 ) < < 4 ) ) ;
result < < = 8 ;
result | = ( cp [ 2 ] | ( ( cp [ 4 ] & 4 ) < < 5 ) ) ;
result < < = 8 ;
result | = ( cp [ 1 ] | ( ( cp [ 4 ] & 2 ) < < 6 ) ) ;
result < < = 8 ;
result | = ( cp [ 0 ] | ( ( cp [ 4 ] & 1 ) < < 7 ) ) ;
}
//AddLog(LOG_LEVEL_INFO,PSTR("septet r: %d"),result);
return result ;
}
2019-08-27 14:33:09 +01:00
2022-03-03 07:28:04 +00:00
char * skip_double ( char * cp ) {
if ( * cp = = ' + ' | | * cp = = ' - ' ) {
cp + + ;
}
while ( * cp ) {
if ( * cp = = ' . ' ) {
cp + + ;
}
if ( ! isdigit ( * cp ) ) {
return cp ;
}
cp + + ;
}
return 0 ;
}
2023-01-30 14:03:46 +00:00
uint8_t * sml_find ( uint8_t * src , uint16_t ssize , uint8_t * pattern , uint16_t psize ) {
//AddLog(LOG_LEVEL_INFO, PSTR(">> %02x %02x %02x %02x"),pattern[0],pattern[1],pattern[2],pattern[3]);
if ( psize > = ssize ) {
return 0 ;
}
for ( uint32_t cnt = 0 ; cnt < ssize - psize ; cnt + + ) {
if ( ! memcmp ( src , pattern , psize ) ) {
return src ;
}
src + + ;
}
return 0 ;
}
2023-02-06 08:42:09 +00:00
# ifdef USE_SML_DECRYPT
2023-01-30 14:03:46 +00:00
double sml_get_obis_value ( uint8_t * data ) {
double out = 0 ;
CosemData * item = ( CosemData * ) data ;
switch ( item - > base . type ) {
case CosemTypeLongSigned : {
out = ntohs ( item - > ls . data ) ;
break ;
}
case CosemTypeLongUnsigned : {
out = ntohs ( item - > lu . data ) ;
break ;
}
case CosemTypeDLongSigned : {
out = ntohl ( item - > dlu . data ) ;
break ;
}
case CosemTypeDLongUnsigned : {
out = ntohl ( item - > dlu . data ) ;
break ;
}
case CosemTypeLong64Signed : {
out = ntohll ( item - > l64s . data ) ;
break ;
}
case CosemTypeLong64Unsigned : {
out = ntohll ( item - > l64u . data ) ;
break ;
}
}
return out ;
}
2023-02-06 08:42:09 +00:00
# endif // USE_SML_DECRYPT
2023-01-30 14:03:46 +00:00
2022-03-03 07:28:04 +00:00
2019-08-27 14:33:09 +01:00
void SML_Decode ( uint8_t index ) {
2022-12-30 07:41:54 +00:00
const char * mp = ( const char * ) sml_globs . meter_p ;
2019-08-27 14:33:09 +01:00
int8_t mindex ;
uint8_t * cp ;
2021-02-28 08:01:13 +00:00
uint8_t dindex = 0 , vindex = 0 ;
2019-08-27 14:33:09 +01:00
delay ( 0 ) ;
2021-02-28 08:01:13 +00:00
2022-12-30 07:41:54 +00:00
if ( ! sml_globs . ready ) {
return ;
}
2019-08-27 14:33:09 +01:00
while ( mp ! = NULL ) {
// check list of defines
2021-02-28 08:01:13 +00:00
if ( * mp = = 0 ) break ;
2019-08-27 14:33:09 +01:00
// new section
2021-02-28 08:01:13 +00:00
mindex = ( ( * mp ) & 7 ) - 1 ;
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
if ( mindex < 0 | | mindex > = sml_globs . meters_used ) mindex = 0 ;
2021-02-28 08:01:13 +00:00
mp + = 2 ;
if ( * mp = = ' = ' & & * ( mp + 1 ) = = ' h ' ) {
2019-08-27 14:33:09 +01:00
mp = strchr ( mp , ' | ' ) ;
if ( mp ) mp + + ;
continue ;
}
2022-07-20 13:25:40 +01:00
if ( * mp = = ' = ' & & * ( mp + 1 ) = = ' s ' ) {
mp = strchr ( mp , ' | ' ) ;
if ( mp ) mp + + ;
continue ;
}
2021-07-19 08:55:43 +01:00
// =d must handle dindex
if ( * mp = = ' = ' & & * ( mp + 1 ) = = ' d ' ) {
if ( index ! = mindex ) {
dindex + + ;
}
}
2022-12-30 07:41:54 +00:00
if ( index ! = mindex ) goto nextsect ;
2019-08-27 14:33:09 +01:00
// start of serial source buffer
2022-12-30 07:41:54 +00:00
cp = meter_desc [ mindex ] . sbuff ;
2019-08-27 14:33:09 +01:00
// compare
2021-02-28 08:01:13 +00:00
if ( * mp = = ' = ' ) {
2019-08-27 14:33:09 +01:00
// calculated entry, check syntax
mp + + ;
// do math m 1+2+3
2021-02-28 08:01:13 +00:00
if ( * mp = = ' m ' & & ! sb_counter ) {
2019-08-27 14:33:09 +01:00
// only every 256 th byte
// else it would be calculated every single serial byte
mp + + ;
2021-02-28 08:01:13 +00:00
while ( * mp = = ' ' ) mp + + ;
2019-08-27 14:33:09 +01:00
// 1. index
double dvar ;
uint8_t opr ;
2021-02-28 08:01:13 +00:00
uint8_t mind ;
int32_t ind ;
mind = strtol ( ( char * ) mp , ( char * * ) & mp , 10 ) ;
2022-12-30 07:41:54 +00:00
if ( mind < 1 | | mind > sml_globs . maxvars ) mind = 1 ;
dvar = sml_globs . meter_vars [ mind - 1 ] ;
2021-02-28 08:01:13 +00:00
while ( * mp = = ' ' ) mp + + ;
for ( uint8_t p = 0 ; p < 8 ; p + + ) {
if ( * mp = = ' @ ' ) {
2019-08-27 14:33:09 +01:00
// store result
2022-12-30 07:41:54 +00:00
sml_globs . meter_vars [ vindex ] = dvar ;
2019-08-27 14:33:09 +01:00
mp + + ;
break ;
}
2021-02-28 08:01:13 +00:00
opr = * mp ;
2019-08-27 14:33:09 +01:00
mp + + ;
2021-02-28 08:01:13 +00:00
uint8_t iflg = 0 ;
if ( * mp = = ' # ' ) {
iflg = 1 ;
2019-08-27 14:33:09 +01:00
mp + + ;
}
2021-02-28 08:01:13 +00:00
ind = strtol ( ( char * ) mp , ( char * * ) & mp , 10 ) ;
mind = ind ;
2022-12-30 07:41:54 +00:00
if ( mind < 1 | | mind > sml_globs . maxvars ) mind = 1 ;
2019-08-27 14:33:09 +01:00
switch ( opr ) {
case ' + ' :
2021-02-28 08:01:13 +00:00
if ( iflg ) dvar + = ind ;
2022-12-30 07:41:54 +00:00
else dvar + = sml_globs . meter_vars [ mind - 1 ] ;
2019-08-27 14:33:09 +01:00
break ;
case ' - ' :
2021-02-28 08:01:13 +00:00
if ( iflg ) dvar - = ind ;
2022-12-30 07:41:54 +00:00
else dvar - = sml_globs . meter_vars [ mind - 1 ] ;
2019-08-27 14:33:09 +01:00
break ;
case ' * ' :
2021-02-28 08:01:13 +00:00
if ( iflg ) dvar * = ind ;
2022-12-30 07:41:54 +00:00
else dvar * = sml_globs . meter_vars [ mind - 1 ] ;
2019-08-27 14:33:09 +01:00
break ;
case ' / ' :
2021-02-28 08:01:13 +00:00
if ( iflg ) dvar / = ind ;
2022-12-30 07:41:54 +00:00
else dvar / = sml_globs . meter_vars [ mind - 1 ] ;
2019-08-27 14:33:09 +01:00
break ;
}
while ( * mp = = ' ' ) mp + + ;
2021-02-28 08:01:13 +00:00
if ( * mp = = ' @ ' ) {
2019-08-27 14:33:09 +01:00
// store result
2022-12-30 07:41:54 +00:00
sml_globs . meter_vars [ vindex ] = dvar ;
2019-08-27 14:33:09 +01:00
mp + + ;
break ;
}
}
2021-02-28 08:01:13 +00:00
double fac = CharToDouble ( ( char * ) mp ) ;
2022-12-30 07:41:54 +00:00
sml_globs . meter_vars [ vindex ] / = fac ;
2021-02-28 08:01:13 +00:00
SML_Immediate_MQTT ( ( const char * ) mp , vindex , mindex ) ;
2022-12-30 07:41:54 +00:00
sml_globs . dvalid [ vindex ] = 1 ;
2021-02-28 08:01:13 +00:00
// get sfac
2023-01-30 14:03:46 +00:00
} else if ( * mp = = ' d ' ) {
2019-08-27 14:33:09 +01:00
// calc deltas d ind 10 (eg every 10 secs)
2021-02-28 08:01:13 +00:00
if ( dindex < MAX_DVARS ) {
2019-08-27 14:33:09 +01:00
// only n indexes
mp + + ;
2021-02-28 08:01:13 +00:00
while ( * mp = = ' ' ) mp + + ;
uint8_t ind = atoi ( mp ) ;
while ( * mp > = ' 0 ' & & * mp < = ' 9 ' ) mp + + ;
2022-12-30 07:41:54 +00:00
if ( ind < 1 | | ind > sml_globs . maxvars ) ind = 1 ;
2021-02-28 08:01:13 +00:00
uint32_t delay = atoi ( mp ) * 1000 ;
2022-12-30 07:41:54 +00:00
uint32_t dtime = millis ( ) - sml_globs . dtimes [ dindex ] ;
2021-02-28 08:01:13 +00:00
if ( dtime > delay ) {
2019-08-27 14:33:09 +01:00
// calc difference
2022-12-30 07:41:54 +00:00
sml_globs . dtimes [ dindex ] = millis ( ) ;
double vdiff = sml_globs . meter_vars [ ind - 1 ] - sml_globs . dvalues [ dindex ] ;
sml_globs . dvalues [ dindex ] = sml_globs . meter_vars [ ind - 1 ] ;
2021-03-02 08:11:08 +00:00
double dres = ( double ) 360000.0 * vdiff / ( ( double ) dtime / 10000.0 ) ;
2021-12-08 15:30:11 +00:00
2022-12-30 07:41:54 +00:00
sml_globs . dvalid [ vindex ] + = 1 ;
2021-12-08 15:30:11 +00:00
2022-12-30 07:41:54 +00:00
if ( sml_globs . dvalid [ vindex ] > = 2 ) {
2021-12-08 15:30:11 +00:00
// differece is only valid after 2. calculation
2022-12-30 07:41:54 +00:00
sml_globs . dvalid [ vindex ] = 2 ;
2021-03-02 08:11:08 +00:00
# ifdef USE_SML_MEDIAN_FILTER
2022-12-30 07:41:54 +00:00
if ( sml_globs . mp [ mindex ] . flag & 16 ) {
sml_globs . meter_vars [ vindex ] = sml_median ( & sml_globs . sml_mf [ vindex ] , dres ) ;
2021-12-08 15:30:11 +00:00
} else {
2022-12-30 07:41:54 +00:00
sml_globs . meter_vars [ vindex ] = dres ;
2021-12-08 15:30:11 +00:00
}
2021-03-02 08:11:08 +00:00
# else
2022-12-30 07:41:54 +00:00
sml_globs . meter_vars [ vindex ] = dres ;
2021-03-02 08:11:08 +00:00
# endif
2021-12-08 15:30:11 +00:00
}
2019-08-27 14:33:09 +01:00
mp = strchr ( mp , ' @ ' ) ;
if ( mp ) {
mp + + ;
2021-02-28 08:01:13 +00:00
double fac = CharToDouble ( ( char * ) mp ) ;
2022-12-30 07:41:54 +00:00
sml_globs . meter_vars [ vindex ] / = fac ;
2021-02-28 08:01:13 +00:00
SML_Immediate_MQTT ( ( const char * ) mp , vindex , mindex ) ;
2019-08-27 14:33:09 +01:00
}
}
2022-12-30 07:41:54 +00:00
//sml_globs.dvalid[vindex] = 1;
2019-08-27 14:33:09 +01:00
dindex + + ;
}
2021-02-28 08:01:13 +00:00
} else if ( * mp = = ' h ' ) {
2019-08-27 14:33:09 +01:00
// skip html tag line
mp = strchr ( mp , ' | ' ) ;
if ( mp ) mp + + ;
continue ;
2022-07-20 13:25:40 +01:00
} else if ( * mp = = ' s ' ) {
// skip spec option tag line
mp = strchr ( mp , ' | ' ) ;
if ( mp ) mp + + ;
continue ;
2019-08-27 14:33:09 +01:00
}
} else {
// compare value
2021-02-28 08:01:13 +00:00
uint8_t found = 1 ;
double ebus_dval = 99 ;
2022-09-16 11:27:16 +01:00
double mbus_dval = 99 ;
2021-02-28 08:01:13 +00:00
while ( * mp ! = ' @ ' ) {
2022-12-30 07:41:54 +00:00
if ( sml_globs . mp [ mindex ] . type = = ' o ' | | sml_globs . mp [ mindex ] . type = = ' c ' ) {
2021-02-28 08:01:13 +00:00
if ( * mp + + ! = * cp + + ) {
2019-08-27 14:33:09 +01:00
found = 0 ;
}
} else {
2022-12-30 07:41:54 +00:00
if ( sml_globs . mp [ mindex ] . type = = ' s ' ) {
2019-08-27 14:33:09 +01:00
// sml
uint8_t val = hexnibble ( * mp + + ) < < 4 ;
val | = hexnibble ( * mp + + ) ;
2021-02-28 08:01:13 +00:00
if ( val ! = * cp + + ) {
2023-01-30 14:03:46 +00:00
found = 0 ;
2019-08-27 14:33:09 +01:00
}
} else {
2022-03-19 07:20:51 +00:00
// ebus modbus pzem vbus or raw
2023-01-30 14:03:46 +00:00
if ( ! strncmp ( mp , " pm( " , 3 ) ) {
// pattern match
uint8_t dp = 0 ;
mp + = 3 ;
// default to asci obis
uint8_t aflg = 3 ;
if ( * mp = = ' r ' ) {
aflg = 0 ;
mp + + ;
} else if ( * mp = = ' h ' ) {
aflg = 1 ;
mp + + ;
}
uint8_t pattern [ 64 ] ;
// check for obis pattern
for ( uint32_t cnt = 0 ; cnt < sizeof ( pattern ) ; cnt + + ) {
if ( * mp = = ' @ ' | | ! * mp ) {
break ;
}
if ( * mp = = ' ) ' ) {
mp + + ;
if ( ( aflg & 2 ) & & ( dp = = 2 ) ) {
pattern [ cnt ] = 0xff ;
cnt + + ;
}
pattern [ cnt ] = 0 ;
uint8_t * ucp = sml_find ( cp , meter_desc [ index ] . sbsiz , pattern , cnt ) ;
if ( ucp ) {
cp = ucp + cnt ;
// check auto type
if ( aflg & 1 ) {
// METER_ID_SIZE
2023-02-06 08:42:09 +00:00
# ifdef USE_SML_DECRYPT
2023-01-30 14:03:46 +00:00
CosemData * item = ( CosemData * ) cp ;
switch ( item - > base . type ) {
case CosemTypeString :
memcpy ( meter_desc [ mindex ] . meter_id , item - > str . data , item - > str . length ) ;
meter_desc [ mindex ] . meter_id [ item - > str . length ] = 0 ;
break ;
case CosemTypeOctetString :
memcpy ( meter_desc [ mindex ] . meter_id , item - > oct . data , item - > oct . length ) ;
meter_desc [ mindex ] . meter_id [ item - > oct . length ] = 0 ;
break ;
default :
ebus_dval = sml_get_obis_value ( cp ) ;
}
2023-02-06 08:42:09 +00:00
# endif
2023-01-30 14:03:46 +00:00
}
}
break ;
}
uint8_t iob ;
if ( aflg & 2 ) {
iob = strtol ( ( char * ) mp , ( char * * ) & mp , 10 ) ;
if ( * mp = = ' . ' ) {
mp + + ;
dp + + ;
}
} else {
iob = hexnibble ( * mp + + ) < < 4 ;
iob | = hexnibble ( * mp + + ) ;
}
pattern [ cnt ] = iob ;
}
} else if ( * mp = = ' x ' ) {
2021-02-28 08:01:13 +00:00
if ( * ( mp + 1 ) = = ' x ' ) {
2021-02-24 08:11:21 +00:00
//ignore one byte
mp + = 2 ;
cp + + ;
} else {
mp + + ;
if ( isdigit ( * mp ) ) {
uint8_t skip = strtol ( ( char * ) mp , ( char * * ) & mp , 10 ) ;
cp + = skip ;
}
}
2022-02-08 16:15:41 +00:00
} else if ( ! strncmp ( mp , " UUuuUUuu " , 8 ) ) {
uint32_t val = ( cp [ 0 ] < < 24 ) | ( cp [ 1 ] < < 16 ) | ( cp [ 2 ] < < 8 ) | ( cp [ 3 ] < < 0 ) ;
mp + = 8 ;
cp + = 4 ;
if ( * mp = = ' s ' ) {
mp + + ;
// swap words
val = ( val > > 16 ) | ( val < < 16 ) ;
}
ebus_dval = val ;
mbus_dval = val ;
} else if ( ! strncmp ( mp , " uuUUuuUU " , 8 ) ) {
uint32_t val = ( cp [ 1 ] < < 24 ) | ( cp [ 0 ] < < 16 ) | ( cp [ 3 ] < < 8 ) | ( cp [ 2 ] < < 0 ) ;
mp + = 8 ;
cp + = 4 ;
if ( * mp = = ' s ' ) {
mp + + ;
// swap words
val = ( val > > 16 ) | ( val < < 16 ) ;
}
ebus_dval = val ;
mbus_dval = val ;
} else if ( ! strncmp ( mp , " UUuu " , 4 ) ) {
uint16_t val = cp [ 1 ] | ( cp [ 0 ] < < 8 ) ;
mbus_dval = val ;
ebus_dval = val ;
mp + = 4 ;
cp + = 2 ;
} else if ( ! strncmp ( mp , " SSssSSss " , 8 ) ) {
int32_t val = ( cp [ 0 ] < < 24 ) | ( cp [ 1 ] < < 16 ) | ( cp [ 2 ] < < 8 ) | ( cp [ 3 ] < < 0 ) ;
mp + = 8 ;
cp + = 4 ;
if ( * mp = = ' s ' ) {
mp + + ;
// swap words
val = ( ( uint32_t ) val > > 16 ) | ( ( uint32_t ) val < < 16 ) ;
}
ebus_dval = val ;
mbus_dval = val ;
} else if ( ! strncmp ( mp , " ssSSssSS " , 8 ) ) {
int32_t val = ( cp [ 1 ] < < 24 ) | ( cp [ 0 ] < < 16 ) | ( cp [ 3 ] < < 8 ) | ( cp [ 2 ] < < 0 ) ;
mp + = 8 ;
cp + = 4 ;
if ( * mp = = ' s ' ) {
mp + + ;
// swap words
val = ( ( uint32_t ) val > > 16 ) | ( ( uint32_t ) val < < 16 ) ;
}
ebus_dval = val ;
mbus_dval = val ;
} else if ( ! strncmp ( mp , " uuUU " , 4 ) ) {
uint16_t val = cp [ 0 ] | ( cp [ 1 ] < < 8 ) ;
mbus_dval = val ;
ebus_dval = val ;
mp + = 4 ;
cp + = 2 ;
} else if ( ! strncmp ( mp , " uu " , 2 ) ) {
2019-08-27 14:33:09 +01:00
uint8_t val = * cp + + ;
2022-02-08 16:15:41 +00:00
mbus_dval = val ;
ebus_dval = val ;
mp + = 2 ;
} else if ( ! strncmp ( mp , " ssSS " , 4 ) ) {
int16_t val = * cp | ( * ( cp + 1 ) < < 8 ) ;
mbus_dval = val ;
ebus_dval = val ;
mp + = 4 ;
cp + = 2 ;
} else if ( ! strncmp ( mp , " SSss " , 4 ) ) {
int16_t val = cp [ 1 ] | ( cp [ 0 ] < < 8 ) ;
mbus_dval = val ;
ebus_dval = val ;
mp + = 4 ;
cp + = 2 ;
} else if ( ! strncmp ( mp , " ss " , 2 ) ) {
2019-08-27 14:33:09 +01:00
int8_t val = * cp + + ;
2022-02-08 16:15:41 +00:00
mbus_dval = val ;
ebus_dval = val ;
mp + = 2 ;
} else if ( ! strncmp ( mp , " ffffffff " , 8 ) ) {
uint32_t val = ( cp [ 0 ] < < 24 ) | ( cp [ 1 ] < < 16 ) | ( cp [ 2 ] < < 8 ) | ( cp [ 3 ] < < 0 ) ;
float * fp = ( float * ) & val ;
ebus_dval = * fp ;
mbus_dval = * fp ;
mp + = 8 ;
cp + = 4 ;
} else if ( ! strncmp ( mp , " FFffFFff " , 8 ) ) {
2019-09-16 18:58:22 +01:00
// reverse word float
2022-02-08 16:15:41 +00:00
uint32_t val = ( cp [ 1 ] < < 0 ) | ( cp [ 0 ] < < 8 ) | ( cp [ 3 ] < < 16 ) | ( cp [ 2 ] < < 24 ) ;
float * fp = ( float * ) & val ;
ebus_dval = * fp ;
mbus_dval = * fp ;
mp + = 8 ;
cp + = 4 ;
} else if ( ! strncmp ( mp , " eeeeee " , 6 ) ) {
uint32_t val = ( cp [ 0 ] < < 16 ) | ( cp [ 1 ] < < 8 ) | ( cp [ 2 ] < < 0 ) ;
mbus_dval = val ;
mp + = 6 ;
cp + = 3 ;
} else if ( ! strncmp ( mp , " vvvvvv " , 6 ) ) {
mbus_dval = ( float ) ( ( cp [ 0 ] < < 8 ) | ( cp [ 1 ] ) ) + ( ( float ) cp [ 2 ] / 10.0 ) ;
mp + = 6 ;
cp + = 3 ;
} else if ( ! strncmp ( mp , " cccccc " , 6 ) ) {
mbus_dval = ( float ) ( ( cp [ 0 ] < < 8 ) | ( cp [ 1 ] ) ) + ( ( float ) cp [ 2 ] / 100.0 ) ;
mp + = 6 ;
cp + = 3 ;
} else if ( ! strncmp ( mp , " pppp " , 4 ) ) {
mbus_dval = ( float ) ( ( cp [ 0 ] < < 8 ) | cp [ 1 ] ) ;
mp + = 4 ;
cp + = 2 ;
2022-09-16 11:27:16 +01:00
} else if ( ! strncmp ( mp , " kstr " , 4 ) ) {
mp + = 4 ;
// decode the mantissa
uint32_t x = 0 ;
2022-10-03 15:08:49 +01:00
for ( uint16_t i = 0 ; i < cp [ 1 ] ; i + + ) {
2022-09-16 11:27:16 +01:00
x < < = 8 ;
2022-10-03 15:08:49 +01:00
x | = cp [ i + 3 ] ;
2022-09-16 11:27:16 +01:00
}
// decode the exponent
2022-10-03 15:08:49 +01:00
int32_t i = cp [ 2 ] & 0x3f ;
if ( cp [ 2 ] & 0x40 ) {
2022-09-16 11:27:16 +01:00
i = - i ;
} ;
//float ifl = pow(10, i);
float ifl = 1 ;
for ( uint16_t x = 1 ; x < = i ; + + x ) {
ifl * = 10 ;
}
2022-10-03 15:08:49 +01:00
if ( cp [ 2 ] & 0x80 ) {
2022-09-16 11:27:16 +01:00
ifl = - ifl ;
}
mbus_dval = ( double ) ( x * ifl ) ;
2022-10-03 15:08:49 +01:00
2022-03-19 07:20:51 +00:00
} else if ( ! strncmp ( mp , " bcd " , 3 ) ) {
mp + = 3 ;
uint8_t digits = strtol ( ( char * ) mp , ( char * * ) & mp , 10 ) ;
if ( digits < 2 ) digits = 2 ;
if ( digits > 12 ) digits = 12 ;
uint64_t bcdval = 0 ;
uint64_t mfac = 1 ;
for ( uint32_t cnt = 0 ; cnt < digits ; cnt + = 2 ) {
uint8_t iob = * cp + + ;
bcdval + = ( iob & 0xf ) * mfac ;
mfac * = 10 ;
bcdval + = ( iob > > 4 ) * mfac ;
mfac * = 10 ;
}
mbus_dval = bcdval ;
ebus_dval = bcdval ;
2022-02-08 16:15:41 +00:00
} else if ( * mp = = ' v ' ) {
2021-02-24 08:11:21 +00:00
// vbus values vul, vsl, vuwh, vuwl, wswh, vswl, vswh
// vub3, vsb3 etc
mp + + ;
int16_t offset = - 1 ;
if ( * mp = = ' o ' ) {
mp + + ;
offset = strtol ( ( char * ) mp , ( char * * ) & mp , 10 ) ;
cp + = ( offset / 4 ) * 6 ;
}
uint8_t usign ;
if ( * mp = = ' u ' ) {
usign = 1 ;
} else if ( * mp = = ' s ' ) {
usign = 0 ;
}
mp + + ;
switch ( * mp ) {
case ' l ' :
mp + + ;
// get long value
if ( usign ) {
ebus_dval = vbus_get_septet ( cp ) ;
} else {
ebus_dval = ( int32_t ) vbus_get_septet ( cp ) ;
}
break ;
case ' w ' :
mp + + ;
char wflg ;
if ( offset > = 0 ) {
if ( offset % 4 ) {
wflg = ' h ' ;
} else {
wflg = ' l ' ;
}
} else {
wflg = * mp ;
mp + + ;
}
// get word value
if ( wflg = = ' h ' ) {
// high word
if ( usign ) ebus_dval = ( vbus_get_septet ( cp ) > > 16 ) & 0xffff ;
else ebus_dval = ( int16_t ) ( ( vbus_get_septet ( cp ) > > 16 ) & 0xffff ) ;
} else {
// low word
if ( usign ) ebus_dval = vbus_get_septet ( cp ) & 0xffff ;
2021-12-28 11:38:39 +00:00
else ebus_dval = ( int16_t ) ( vbus_get_septet ( cp ) & 0xffff ) ;
2021-02-24 08:11:21 +00:00
}
break ;
case ' b ' :
mp + + ;
char bflg ;
if ( offset > = 0 ) {
bflg = 0x30 | ( offset % 4 ) ;
} else {
bflg = * mp ;
mp + + ;
}
switch ( bflg ) {
case ' 3 ' :
if ( usign ) ebus_dval = vbus_get_septet ( cp ) > > 24 ;
else ebus_dval = ( int8_t ) ( vbus_get_septet ( cp ) > > 24 ) ;
break ;
case ' 2 ' :
if ( usign ) ebus_dval = ( vbus_get_septet ( cp ) > > 16 ) & 0xff ;
else ebus_dval = ( int8_t ) ( ( vbus_get_septet ( cp ) > > 16 ) & 0xff ) ;
break ;
case ' 1 ' :
if ( usign ) ebus_dval = ( vbus_get_septet ( cp ) > > 8 ) & 0xff ;
else ebus_dval = ( int8_t ) ( ( vbus_get_septet ( cp ) > > 8 ) & 0xff ) ;
break ;
case ' 0 ' :
if ( usign ) ebus_dval = vbus_get_septet ( cp ) & 0xff ;
else ebus_dval = ( int8_t ) ( vbus_get_septet ( cp ) & 0xff ) ;
break ;
}
break ;
case ' t ' :
mp + + ;
{ uint16_t time ;
if ( offset % 4 ) {
time = ( vbus_get_septet ( cp ) > > 16 ) & 0xffff ;
} else {
time = vbus_get_septet ( cp ) & 0xffff ;
}
2022-12-30 07:41:54 +00:00
sprintf ( & meter_desc [ index ] . meter_id [ 0 ] , " %02d:%02d " , time / 60 , time % 60 ) ;
2021-02-24 08:11:21 +00:00
}
break ;
}
cp + = 6 ;
}
2019-09-03 09:39:51 +01:00
else {
2019-08-27 14:33:09 +01:00
uint8_t val = hexnibble ( * mp + + ) < < 4 ;
val | = hexnibble ( * mp + + ) ;
2022-12-30 07:41:54 +00:00
if ( val ! = * cp + + ) {
found = 0 ;
2019-08-27 14:33:09 +01:00
}
}
}
}
}
if ( found ) {
// matches, get value
2022-12-30 07:41:54 +00:00
sml_globs . dvalid [ vindex ] = 1 ;
2019-08-27 14:33:09 +01:00
mp + + ;
2022-07-20 13:25:40 +01:00
# if defined(ED300L) || defined(AS2020) || defined(DTZ541) || defined(USE_SML_SPECOPT)
2022-12-30 07:41:54 +00:00
sml_globs . g_mindex = mindex ;
2020-03-29 15:29:22 +01:00
# endif
2021-01-22 07:31:22 +00:00
if ( * mp = = ' # ' ) {
2019-08-27 14:33:09 +01:00
// get string value
2021-02-22 10:10:49 +00:00
getstr :
2019-08-27 14:33:09 +01:00
mp + + ;
2022-12-30 07:41:54 +00:00
if ( sml_globs . mp [ mindex ] . type ! = ' v ' ) {
if ( sml_globs . mp [ mindex ] . type = = ' o ' ) {
2021-02-24 08:11:21 +00:00
uint32_t p ;
for ( p = 0 ; p < METER_ID_SIZE - 2 ; p + + ) {
if ( * cp = = * mp ) {
break ;
}
2022-12-30 07:41:54 +00:00
meter_desc [ mindex ] . meter_id [ p ] = * cp + + ;
2019-08-27 14:33:09 +01:00
}
2022-12-30 07:41:54 +00:00
meter_desc [ mindex ] . meter_id [ p ] = 0 ;
} else if ( sml_globs . mp [ mindex ] . type = = ' k ' ) {
2022-10-03 15:08:49 +01:00
// 220901
uint32_t date = mbus_dval ;
uint8_t year = date / 10000 ; // = 22
date - = year * 10000 ;
uint8_t month = date / 100 ; // = 09
uint8_t day = date % 100 ; // = 01
2022-12-30 07:41:54 +00:00
sprintf ( & meter_desc [ mindex ] . meter_id [ 0 ] , " %02d.%02d.%02d " , day , month , year ) ;
2021-02-24 08:11:21 +00:00
} else {
2022-12-30 07:41:54 +00:00
sml_getvalue ( cp , mindex ) ;
2019-08-27 14:33:09 +01:00
}
}
} else {
double dval ;
2022-12-30 07:41:54 +00:00
char type = sml_globs . mp [ mindex ] . type ;
2023-01-30 14:03:46 +00:00
if ( type ! = ' e ' & & type ! = ' r ' & & type ! = ' R ' & & type ! = ' m ' & & type ! = ' M ' & & type ! = ' k ' & & type ! = ' p ' & & type ! = ' v ' ) {
2019-08-27 14:33:09 +01:00
// get numeric values
2022-12-30 07:41:54 +00:00
if ( type = = ' o ' | | type = = ' c ' ) {
2021-01-22 07:31:22 +00:00
if ( * mp = = ' ( ' ) {
mp + + ;
2021-02-22 10:10:49 +00:00
// skip this number of brackets
uint8_t toskip = strtol ( ( char * ) mp , ( char * * ) & mp , 10 ) ;
mp + + ;
char * lcp = ( char * ) cp ;
if ( toskip ) {
char * bp = ( char * ) cp ;
for ( uint32_t cnt = 0 ; cnt < toskip ; cnt + + ) {
bp = strchr ( bp , ' ( ' ) ;
if ( ! bp ) {
break ;
}
bp + + ;
lcp = bp ;
}
2021-01-22 07:31:22 +00:00
}
2022-12-30 07:41:54 +00:00
if ( * mp = = ' # ' ) {
2021-02-22 10:10:49 +00:00
cp = ( uint8_t * ) lcp ;
goto getstr ;
}
2022-12-30 07:41:54 +00:00
dval = CharToDouble ( ( char * ) lcp ) ;
2021-01-22 07:31:22 +00:00
} else {
2022-12-30 07:41:54 +00:00
dval = CharToDouble ( ( char * ) cp ) ;
2021-01-22 07:31:22 +00:00
}
2019-08-27 14:33:09 +01:00
} else {
2022-12-30 07:41:54 +00:00
dval = sml_getvalue ( cp , mindex ) ;
2019-08-27 14:33:09 +01:00
}
} else {
2021-02-24 08:11:21 +00:00
// ebus pzem vbus or mbus or raw
2022-09-16 11:27:16 +01:00
if ( * mp = = ' b ' ) {
2019-08-27 14:33:09 +01:00
mp + + ;
2021-02-11 07:47:48 +00:00
uint8_t shift = * mp & 7 ;
2022-12-30 07:41:54 +00:00
ebus_dval = ( uint32_t ) ebus_dval > > shift ;
ebus_dval = ( uint32_t ) ebus_dval & 1 ;
2019-08-27 14:33:09 +01:00
mp + = 2 ;
}
2022-09-16 11:27:16 +01:00
if ( * mp = = ' i ' ) {
2019-09-03 09:39:51 +01:00
// mbus index
mp + + ;
2022-09-16 11:27:16 +01:00
uint8_t mb_index = strtol ( ( char * ) mp , ( char * * ) & mp , 10 ) ;
2022-12-30 07:41:54 +00:00
if ( mb_index ! = sml_globs . mp [ mindex ] . index ) {
2019-09-03 09:39:51 +01:00
goto nextsect ;
}
2022-12-30 07:41:54 +00:00
if ( sml_globs . mp [ mindex ] . type = = ' k ' ) {
2022-09-16 11:27:16 +01:00
// crc is already checked, get float value
dval = mbus_dval ;
mp + + ;
} else {
2022-12-30 07:41:54 +00:00
uint16_t pos = meter_desc [ mindex ] . sbuff [ 2 ] + 3 ;
if ( pos > ( meter_desc [ mindex ] . sbsiz - 2 ) ) pos = meter_desc [ mindex ] . sbsiz - 2 ;
uint16_t crc = MBUS_calculateCRC ( & meter_desc [ mindex ] . sbuff [ 0 ] , pos , 0xFFFF ) ;
if ( lowByte ( crc ) ! = meter_desc [ mindex ] . sbuff [ pos ] ) goto nextsect ;
if ( highByte ( crc ) ! = meter_desc [ mindex ] . sbuff [ pos + 1 ] ) goto nextsect ;
2022-09-16 11:27:16 +01:00
dval = mbus_dval ;
//AddLog(LOG_LEVEL_INFO, PSTR(">> %s"),mp);
mp + + ;
}
2019-09-03 09:39:51 +01:00
} else {
2022-12-30 07:41:54 +00:00
if ( sml_globs . mp [ mindex ] . type = = ' p ' ) {
uint8_t crc = SML_PzemCrc ( & meter_desc [ mindex ] . sbuff [ 0 ] , 6 ) ;
if ( crc ! = meter_desc [ mindex ] . sbuff [ 6 ] ) goto nextsect ;
2022-09-16 11:27:16 +01:00
dval = mbus_dval ;
2019-09-16 18:58:22 +01:00
} else {
2022-09-16 11:27:16 +01:00
dval = ebus_dval ;
2019-09-16 18:58:22 +01:00
}
2019-09-03 09:39:51 +01:00
}
2019-08-27 14:33:09 +01:00
}
2019-09-16 18:58:22 +01:00
# ifdef USE_SML_MEDIAN_FILTER
2022-12-30 07:41:54 +00:00
if ( sml_globs . mp [ mindex ] . flag & 16 ) {
sml_globs . meter_vars [ vindex ] = sml_median ( & sml_globs . sml_mf [ vindex ] , dval ) ;
2019-09-16 18:58:22 +01:00
} else {
2022-12-30 07:41:54 +00:00
sml_globs . meter_vars [ vindex ] = dval ;
2019-09-16 18:58:22 +01:00
}
2019-08-27 14:33:09 +01:00
# else
2022-12-30 07:41:54 +00:00
sml_globs . meter_vars [ vindex ] = dval ;
2019-08-27 14:33:09 +01:00
# endif
2021-02-24 08:11:21 +00:00
2021-06-05 10:47:09 +01:00
//AddLog(LOG_LEVEL_INFO, PSTR(">> %s"),mp);
2019-08-27 14:33:09 +01:00
// get scaling factor
2021-02-28 08:01:13 +00:00
double fac = CharToDouble ( ( char * ) mp ) ;
2022-03-03 07:28:04 +00:00
// get optional offset to calibrate meter
char * cp = skip_double ( ( char * ) mp ) ;
if ( cp & & ( * cp = = ' + ' | | * cp = = ' - ' ) ) {
double offset = CharToDouble ( cp ) ;
2022-12-30 07:41:54 +00:00
sml_globs . meter_vars [ vindex ] + = offset ;
2022-03-03 07:28:04 +00:00
}
2022-12-30 07:41:54 +00:00
sml_globs . meter_vars [ vindex ] / = fac ;
2021-02-28 08:01:13 +00:00
SML_Immediate_MQTT ( ( const char * ) mp , vindex , mindex ) ;
2019-08-27 14:33:09 +01:00
}
}
2021-02-24 08:11:21 +00:00
//AddLog(LOG_LEVEL_INFO, PSTR("set valid in line %d"), vindex);
2019-08-27 14:33:09 +01:00
}
nextsect :
// next section
2022-12-30 07:41:54 +00:00
if ( vindex < sml_globs . maxvars - 1 ) {
2019-08-27 14:33:09 +01:00
vindex + + ;
}
mp = strchr ( mp , ' | ' ) ;
if ( mp ) mp + + ;
}
}
2020-03-15 12:05:47 +00:00
//"1-0:1.8.0*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|"
2019-08-27 14:33:09 +01:00
void SML_Immediate_MQTT ( const char * mp , uint8_t index , uint8_t mindex ) {
char tpowstr [ 32 ] ;
char jname [ 24 ] ;
// we must skip sf,webname,unit
2022-12-30 07:41:54 +00:00
char * cp = strchr ( mp , ' , ' ) ;
2019-08-27 14:33:09 +01:00
if ( cp ) {
cp + + ;
// wn
2022-12-30 07:41:54 +00:00
cp = strchr ( cp , ' , ' ) ;
2019-08-27 14:33:09 +01:00
if ( cp ) {
cp + + ;
// unit
2022-12-30 07:41:54 +00:00
cp = strchr ( cp , ' , ' ) ;
2019-08-27 14:33:09 +01:00
if ( cp ) {
cp + + ;
// json mqtt
2022-12-30 07:41:54 +00:00
for ( uint8_t count = 0 ; count < sizeof ( jname ) ; count + + ) {
if ( * cp = = ' , ' ) {
jname [ count ] = 0 ;
2019-08-27 14:33:09 +01:00
break ;
}
2022-12-30 07:41:54 +00:00
jname [ count ] = * cp + + ;
2019-08-27 14:33:09 +01:00
}
cp + + ;
2022-09-16 11:27:16 +01:00
uint8_t dp = atoi ( cp ) ;
if ( dp & 0x10 ) {
2019-08-27 14:33:09 +01:00
// immediate mqtt
2022-12-30 07:41:54 +00:00
DOUBLE2CHAR ( sml_globs . meter_vars [ index ] , dp & 0xf , tpowstr ) ;
ResponseTime_P ( PSTR ( " , \" %s \" :{ \" %s \" :%s}} " ) , sml_globs . mp [ mindex ] . prefix , jname , tpowstr ) ;
2019-11-10 16:40:37 +00:00
MqttPublishTeleSensor ( ) ;
2019-08-27 14:33:09 +01:00
}
}
}
}
}
// web + json interface
void SML_Show ( boolean json ) {
2021-04-15 19:21:51 +01:00
int8_t count , mindex , cindex = 0 ;
2019-08-27 14:33:09 +01:00
char tpowstr [ 32 ] ;
char name [ 24 ] ;
char unit [ 8 ] ;
char jname [ 24 ] ;
2022-12-30 07:41:54 +00:00
int8_t index = 0 , mid = 0 ;
char * mp = ( char * ) sml_globs . meter_p ;
2021-04-15 19:21:51 +01:00
char * cp , nojson = 0 ;
2019-08-27 14:33:09 +01:00
//char b_mqtt_data[MESSZ];
//b_mqtt_data[0]=0;
2022-12-30 07:41:54 +00:00
if ( ! sml_globs . meters_used ) return ;
2020-08-09 08:36:24 +01:00
2022-09-16 11:27:16 +01:00
int8_t lastmind = ( ( * mp ) & 7 ) - 1 ;
2022-12-30 07:41:54 +00:00
if ( lastmind < 0 | | lastmind > = sml_globs . meters_used ) lastmind = 0 ;
2019-08-27 14:33:09 +01:00
while ( mp ! = NULL ) {
2022-09-16 11:27:16 +01:00
if ( * mp = = 0 ) break ;
2019-08-27 14:33:09 +01:00
// setup sections
2022-09-16 11:27:16 +01:00
mindex = ( ( * mp ) & 7 ) - 1 ;
2020-08-09 08:36:24 +01:00
2022-12-30 07:41:54 +00:00
if ( mindex < 0 | | mindex > = sml_globs . meters_used ) mindex = 0 ;
if ( sml_globs . mp [ mindex ] . prefix [ 0 ] = = ' * ' & & sml_globs . mp [ mindex ] . prefix [ 1 ] = = 0 ) {
2021-04-15 19:21:51 +01:00
nojson = 1 ;
2020-08-09 08:36:24 +01:00
} else {
2021-04-15 19:21:51 +01:00
nojson = 0 ;
2020-08-09 08:36:24 +01:00
}
2022-09-16 11:27:16 +01:00
mp + = 2 ;
2022-12-30 07:41:54 +00:00
if ( * mp = = ' = ' & & * ( mp + 1 ) = = ' h ' ) {
2022-09-16 11:27:16 +01:00
mp + = 2 ;
2019-08-27 14:33:09 +01:00
// html tag
if ( json ) {
mp = strchr ( mp , ' | ' ) ;
if ( mp ) mp + + ;
continue ;
}
// web ui export
uint8_t i ;
2022-09-16 11:27:16 +01:00
for ( i = 0 ; i < sizeof ( tpowstr ) - 2 ; i + + ) {
if ( * mp = = ' | ' | | * mp = = 0 ) break ;
tpowstr [ i ] = * mp + + ;
2019-08-27 14:33:09 +01:00
}
2022-09-16 11:27:16 +01:00
tpowstr [ i ] = 0 ;
2019-08-27 14:33:09 +01:00
// export html
//snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s{s}%s{e}", b_mqtt_data,tpowstr);
2022-09-16 11:27:16 +01:00
WSContentSend_PD ( PSTR ( " {s}%s{e} " ) , tpowstr ) ;
2019-08-27 14:33:09 +01:00
// rewind, to ensure strchr
mp - - ;
mp = strchr ( mp , ' | ' ) ;
if ( mp ) mp + + ;
continue ;
}
2022-09-16 11:27:16 +01:00
if ( * mp = = ' = ' & & * ( mp + 1 ) = = ' s ' ) {
2022-07-20 13:25:40 +01:00
mp = strchr ( mp , ' | ' ) ;
if ( mp ) mp + + ;
continue ;
}
2019-08-27 14:33:09 +01:00
// skip compare section
2022-10-03 15:08:49 +01:00
cp = strchr ( mp , ' @ ' ) ;
2019-08-27 14:33:09 +01:00
if ( cp ) {
cp + + ;
2021-02-22 10:10:49 +00:00
tststr :
2022-09-16 11:27:16 +01:00
if ( * cp = = ' # ' ) {
2019-08-27 14:33:09 +01:00
// meter id
2022-09-08 13:35:04 +01:00
if ( * ( cp + 1 ) = = ' x ' ) {
// convert hex to asci
sml_hex_asci ( mindex , tpowstr ) ;
} else {
2022-12-30 07:41:54 +00:00
sprintf ( tpowstr , " \" %s \" " , & meter_desc [ mindex ] . meter_id [ 0 ] ) ;
2022-09-08 13:35:04 +01:00
}
2022-09-16 11:27:16 +01:00
mid = 1 ;
} else if ( * cp = = ' ( ' ) {
2022-12-30 07:41:54 +00:00
if ( sml_globs . mp [ mindex ] . type = = ' o ' ) {
2021-02-22 10:10:49 +00:00
cp + + ;
strtol ( ( char * ) cp , ( char * * ) & cp , 10 ) ;
cp + + ;
goto tststr ;
} else {
2022-09-16 11:27:16 +01:00
mid = 0 ;
2021-02-22 10:10:49 +00:00
}
2022-09-16 11:27:16 +01:00
} else if ( * cp = = ' b ' ) {
2021-02-28 08:01:13 +00:00
// bit value
# ifdef SML_BIT_TEXT
2022-12-30 07:41:54 +00:00
sprintf_P ( tpowstr , PSTR ( " \" %s \" " ) , ( uint8_t ) sml_globs . meter_vars [ index ] ? D_ON : D_OFF ) ;
2021-02-28 08:01:13 +00:00
mid = 2 ;
# endif
2019-08-27 14:33:09 +01:00
} else {
2021-02-28 08:01:13 +00:00
mid = 0 ;
2019-08-27 14:33:09 +01:00
}
// skip scaling
2022-09-16 11:27:16 +01:00
cp = strchr ( cp , ' , ' ) ;
2019-08-27 14:33:09 +01:00
if ( cp ) {
// this is the name in web UI
cp + + ;
2022-09-16 11:27:16 +01:00
for ( count = 0 ; count < sizeof ( name ) ; count + + ) {
if ( * cp = = ' , ' ) {
name [ count ] = 0 ;
2019-08-27 14:33:09 +01:00
break ;
}
2022-09-16 11:27:16 +01:00
name [ count ] = * cp + + ;
2019-08-27 14:33:09 +01:00
}
cp + + ;
2022-09-16 11:27:16 +01:00
for ( count = 0 ; count < sizeof ( unit ) ; count + + ) {
if ( * cp = = ' , ' ) {
unit [ count ] = 0 ;
2019-08-27 14:33:09 +01:00
break ;
}
2022-09-16 11:27:16 +01:00
unit [ count ] = * cp + + ;
2019-08-27 14:33:09 +01:00
}
cp + + ;
2022-09-16 11:27:16 +01:00
for ( count = 0 ; count < sizeof ( jname ) ; count + + ) {
if ( * cp = = ' , ' ) {
jname [ count ] = 0 ;
2019-08-27 14:33:09 +01:00
break ;
}
2022-09-16 11:27:16 +01:00
jname [ count ] = * cp + + ;
2019-08-27 14:33:09 +01:00
}
cp + + ;
if ( ! mid ) {
2022-09-16 11:27:16 +01:00
uint8_t dp = atoi ( cp ) & 0xf ;
2022-12-30 07:41:54 +00:00
DOUBLE2CHAR ( sml_globs . meter_vars [ index ] , dp , tpowstr ) ;
2019-08-27 14:33:09 +01:00
}
if ( json ) {
2022-12-30 07:41:54 +00:00
//if (sml_globs.dvalid[index]) {
2021-04-15 19:21:51 +01:00
2021-02-24 08:11:21 +00:00
//AddLog(LOG_LEVEL_INFO, PSTR("not yet valid line %d"), index);
2021-04-15 19:21:51 +01:00
//}
2019-08-27 14:33:09 +01:00
// json export
2022-09-16 11:27:16 +01:00
if ( index = = 0 ) {
2022-12-30 07:41:54 +00:00
//snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s,\"%s\":{\"%s\":%s", b_mqtt_data,sml_globs.mp[mindex].prefix,jname,tpowstr);
2021-04-15 19:21:51 +01:00
if ( ! nojson ) {
2022-12-30 07:41:54 +00:00
ResponseAppend_P ( PSTR ( " , \" %s \" :{ \" %s \" :%s " ) , sml_globs . mp [ mindex ] . prefix , jname , tpowstr ) ;
2021-04-15 19:21:51 +01:00
}
2019-08-27 14:33:09 +01:00
}
else {
2022-09-16 11:27:16 +01:00
if ( lastmind ! = mindex ) {
2019-08-27 14:33:09 +01:00
// meter changed, close mqtt
//snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s}", b_mqtt_data);
2021-04-15 19:21:51 +01:00
if ( ! nojson ) {
ResponseAppend_P ( PSTR ( " } " ) ) ;
}
2020-08-09 08:36:24 +01:00
// and open new
2022-12-30 07:41:54 +00:00
//snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s,\"%s\":{\"%s\":%s", b_mqtt_data,sml_globs.mp[mindex].prefix,jname,tpowstr);
2021-04-15 19:21:51 +01:00
if ( ! nojson ) {
2022-12-30 07:41:54 +00:00
ResponseAppend_P ( PSTR ( " , \" %s \" :{ \" %s \" :%s " ) , sml_globs . mp [ mindex ] . prefix , jname , tpowstr ) ;
2021-04-15 19:21:51 +01:00
}
2022-09-16 11:27:16 +01:00
lastmind = mindex ;
2019-08-27 14:33:09 +01:00
} else {
//snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s,\"%s\":%s", b_mqtt_data,jname,tpowstr);
2021-04-15 19:21:51 +01:00
if ( ! nojson ) {
2022-09-16 11:27:16 +01:00
ResponseAppend_P ( PSTR ( " , \" %s \" :%s " ) , jname , tpowstr ) ;
2021-04-15 19:21:51 +01:00
}
2019-08-27 14:33:09 +01:00
}
}
2020-08-09 08:36:24 +01:00
2019-08-27 14:33:09 +01:00
} else {
// web ui export
//snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s{s}%s %s: {m}%s %s{e}", b_mqtt_data,meter_desc[mindex].prefix,name,tpowstr,unit);
2022-12-30 07:41:54 +00:00
if ( strcmp ( name , " * " ) ) WSContentSend_PD ( PSTR ( " {s}%s %s {m}%s %s{e} " ) , sml_globs . mp [ mindex ] . prefix , name , tpowstr , unit ) ;
2019-08-27 14:33:09 +01:00
}
}
}
2022-12-30 07:41:54 +00:00
if ( index < sml_globs . maxvars - 1 ) {
2019-08-27 14:33:09 +01:00
index + + ;
}
// next section
mp = strchr ( cp , ' | ' ) ;
if ( mp ) mp + + ;
}
if ( json ) {
//snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s}", b_mqtt_data);
//ResponseAppend_P(PSTR("%s"),b_mqtt_data);
2021-04-15 19:21:51 +01:00
if ( ! nojson ) {
ResponseAppend_P ( PSTR ( " } " ) ) ;
}
2019-08-27 14:33:09 +01:00
} else {
//WSContentSend_PD(PSTR("%s"),b_mqtt_data);
}
# ifdef USE_DOMOTICZ
2020-11-02 11:36:57 +00:00
if ( json & & ! TasmotaGlobal . tele_period ) {
2019-08-27 14:33:09 +01:00
char str [ 16 ] ;
2022-12-30 07:41:54 +00:00
DOUBLE2CHAR ( sml_globs . meter_vars [ 0 ] , 1 , str ) ;
DomoticzSensorPowerEnergy ( sml_globs . meter_vars [ 1 ] , str ) ; // PowerUsage, EnergyToday
DOUBLE2CHAR ( sml_globs . meter_vars [ 2 ] , 1 , str ) ;
2019-08-27 14:33:09 +01:00
DomoticzSensor ( DZ_VOLTAGE , str ) ; // Voltage
2022-12-30 07:41:54 +00:00
DOUBLE2CHAR ( sml_globs . meter_vars [ 3 ] , 1 , str ) ;
2019-08-27 14:33:09 +01:00
DomoticzSensor ( DZ_CURRENT , str ) ; // Current
}
# endif // USE_DOMOTICZ
2020-11-02 11:36:57 +00:00
2019-08-27 14:33:09 +01:00
}
struct SML_COUNTER {
uint8_t sml_cnt_debounce ;
uint8_t sml_cnt_old_state ;
uint32_t sml_cnt_last_ts ;
uint32_t sml_counter_ltime ;
2022-02-11 22:29:30 +00:00
uint32_t sml_counter_lfalltime ;
uint32_t sml_counter_pulsewidth ;
2019-08-27 14:33:09 +01:00
uint16_t sml_debounce ;
2020-06-25 06:34:00 +01:00
uint8_t sml_cnt_updated ;
2019-08-27 14:33:09 +01:00
} sml_counters [ MAX_COUNTERS ] ;
2020-11-02 11:24:39 +00:00
uint8_t sml_counter_pinstate ;
uint8_t sml_cnt_index [ MAX_COUNTERS ] = { 0 , 1 , 2 , 3 } ;
2022-12-30 07:41:54 +00:00
2021-04-02 16:14:08 +01:00
void IRAM_ATTR SML_CounterIsr ( void * arg ) {
2022-12-30 07:41:54 +00:00
uint32_t index = * static_cast < uint8_t * > ( arg ) ;
2020-11-02 11:24:39 +00:00
2022-12-30 07:41:54 +00:00
uint32_t time = millis ( ) ;
uint32_t debounce_time ;
2020-11-02 11:24:39 +00:00
2022-12-30 07:41:54 +00:00
if ( digitalRead ( sml_globs . mp [ sml_counters [ index ] . sml_cnt_old_state ] . srcpin ) = = bitRead ( sml_counter_pinstate , index ) ) {
2020-11-02 11:24:39 +00:00
return ;
2019-08-27 14:33:09 +01:00
}
2020-11-02 11:24:39 +00:00
debounce_time = time - sml_counters [ index ] . sml_counter_ltime ;
2019-08-27 14:33:09 +01:00
2022-02-08 16:15:41 +00:00
if ( debounce_time < = sml_counters [ index ] . sml_debounce ) return ;
2019-08-27 14:33:09 +01:00
2020-11-02 11:24:39 +00:00
if bitRead ( sml_counter_pinstate , index ) {
// falling edge
RtcSettings . pulse_counter [ index ] + + ;
2022-02-11 22:29:30 +00:00
sml_counters [ index ] . sml_counter_pulsewidth = time - sml_counters [ index ] . sml_counter_lfalltime ;
sml_counters [ index ] . sml_counter_lfalltime = time ;
2022-02-08 16:15:41 +00:00
sml_counters [ index ] . sml_cnt_updated = 1 ;
2020-11-02 11:24:39 +00:00
}
sml_counters [ index ] . sml_counter_ltime = time ;
2022-02-08 16:15:41 +00:00
sml_counter_pinstate ^ = ( 1 < < index ) ;
2019-08-27 14:33:09 +01:00
}
2019-12-25 08:09:43 +00:00
# ifndef METER_DEF_SIZE
# define METER_DEF_SIZE 3000
# endif
2019-08-27 14:33:09 +01:00
2020-05-15 14:30:32 +01:00
# ifdef SML_REPLACE_VARS
2021-12-08 15:30:11 +00:00
# ifndef SML_SRCBSIZE
2020-05-15 14:30:32 +01:00
# define SML_SRCBSIZE 256
2021-12-08 15:30:11 +00:00
# endif
2020-05-15 14:30:32 +01:00
uint32_t SML_getlinelen ( char * lp ) {
uint32_t cnt ;
2022-09-16 11:27:16 +01:00
for ( cnt = 0 ; cnt < SML_SRCBSIZE - 1 ; cnt + + ) {
if ( lp [ cnt ] = = SCRIPT_EOL ) {
2020-05-15 14:30:32 +01:00
break ;
}
}
return cnt ;
}
uint32_t SML_getscriptsize ( char * lp ) {
2022-09-16 11:27:16 +01:00
uint32_t mlen = 0 ;
char dstbuf [ SML_SRCBSIZE * 2 ] ;
2020-05-15 14:30:32 +01:00
while ( 1 ) {
2022-09-16 11:27:16 +01:00
Replace_Cmd_Vars ( lp , 1 , dstbuf , sizeof ( dstbuf ) ) ;
lp + = SML_getlinelen ( lp ) + 1 ;
uint32_t slen = strlen ( dstbuf ) ;
2021-06-05 10:47:09 +01:00
//AddLog(LOG_LEVEL_INFO, PSTR("%d - %s"),slen,dstbuf);
2022-09-16 11:27:16 +01:00
mlen + = slen + 1 ;
if ( * lp = = ' # ' ) break ;
if ( * lp = = ' > ' ) break ;
if ( * lp = = 0 ) break ;
2020-05-15 14:30:32 +01:00
}
2021-06-05 10:47:09 +01:00
//AddLog(LOG_LEVEL_INFO, PSTR("len=%d"),mlen);
2022-09-16 11:27:16 +01:00
return mlen + 32 ;
2020-05-15 14:30:32 +01:00
}
# else
uint32_t SML_getscriptsize ( char * lp ) {
2022-09-16 11:27:16 +01:00
uint32_t mlen = 0 ;
for ( uint32_t cnt = 0 ; cnt < METER_DEF_SIZE - 1 ; cnt + + ) {
if ( lp [ cnt ] = = ' \n ' & & lp [ cnt + 1 ] = = ' # ' ) {
mlen = cnt + 3 ;
2020-05-15 14:30:32 +01:00
break ;
}
}
2021-06-05 10:47:09 +01:00
//AddLog(LOG_LEVEL_INFO, PSTR("len=%d"),mlen);
2020-05-15 14:30:32 +01:00
return mlen ;
}
2022-12-30 07:41:54 +00:00
# endif // SML_REPLACE_VARS
2020-05-15 14:30:32 +01:00
2019-08-27 14:33:09 +01:00
bool Gpio_used ( uint8_t gpiopin ) {
2021-02-28 11:50:02 +00:00
if ( ( gpiopin < nitems ( TasmotaGlobal . gpio_pin ) ) & & ( TasmotaGlobal . gpio_pin [ gpiopin ] > 0 ) ) {
2020-04-28 13:42:47 +01:00
return true ;
}
2019-08-27 14:33:09 +01:00
return false ;
}
2022-12-30 07:41:54 +00:00
# define SML_MINSB 64
char * SpecOptions ( char * cp , uint32_t mnum ) {
// special option
2023-01-30 14:03:46 +00:00
struct METER_DESC * mp = & meter_desc [ mnum ] ;
2022-12-30 07:41:54 +00:00
switch ( * cp ) {
case ' 1 ' :
cp + + ;
2022-07-20 13:25:40 +01:00
# ifdef USE_SML_SPECOPT
2022-12-30 07:41:54 +00:00
if ( * cp = = ' , ' ) {
cp + + ;
2023-01-30 14:03:46 +00:00
mp - > so_obis1 = strtol ( cp , & cp , 16 ) ;
2022-12-30 07:41:54 +00:00
}
if ( * cp = = ' , ' ) {
cp + + ;
2023-01-30 14:03:46 +00:00
mp - > so_fcode1 = strtol ( cp , & cp , 16 ) ;
2022-12-30 07:41:54 +00:00
}
if ( * cp = = ' , ' ) {
cp + + ;
2023-01-30 14:03:46 +00:00
mp - > so_bpos1 = strtol ( cp , & cp , 10 ) ;
2022-12-30 07:41:54 +00:00
}
if ( * cp = = ' , ' ) {
cp + + ;
2023-01-30 14:03:46 +00:00
mp - > so_fcode2 = strtol ( cp , & cp , 16 ) ;
2022-12-30 07:41:54 +00:00
}
if ( * cp = = ' , ' ) {
cp + + ;
2023-01-30 14:03:46 +00:00
mp - > so_bpos2 = strtol ( cp , & cp , 10 ) ;
2022-12-30 07:41:54 +00:00
}
if ( * cp = = ' , ' ) {
cp + + ;
2023-01-30 14:03:46 +00:00
mp - > so_obis2 = strtol ( cp , & cp , 16 ) ;
2022-12-30 07:41:54 +00:00
}
# endif
break ;
case ' 2 ' :
cp + = 2 ;
2023-01-30 14:03:46 +00:00
mp - > so_flags = strtol ( cp , & cp , 16 ) ;
2022-12-30 07:41:54 +00:00
break ;
case ' 3 ' :
cp + = 2 ;
2023-01-30 14:03:46 +00:00
mp - > sbsiz = strtol ( cp , & cp , 10 ) ;
2022-12-30 07:41:54 +00:00
if ( * cp = = ' , ' ) {
cp + + ;
2023-01-30 14:03:46 +00:00
mp - > sibsiz = strtol ( cp , & cp , 10 ) ;
if ( mp - > sibsiz < SML_MINSB ) {
mp - > sibsiz = SML_MINSB ;
2022-12-30 07:41:54 +00:00
}
}
2023-01-30 14:03:46 +00:00
if ( * cp = = ' , ' ) {
cp + + ;
sml_globs . logsize = strtol ( cp , & cp , 10 ) ;
}
2022-12-30 07:41:54 +00:00
break ;
case ' 4 ' :
cp + = 2 ;
# ifdef USE_SML_DECRYPT
meter_desc [ mnum ] . use_crypt = true ;
for ( uint8_t cnt = 0 ; cnt < ( SML_CRYPT_SIZE * 2 ) ; cnt + = 2 ) {
2023-01-30 14:03:46 +00:00
mp - > key [ cnt / 2 ] = ( sml_hexnibble ( cp [ cnt ] ) < < 4 ) | sml_hexnibble ( cp [ cnt + 1 ] ) ;
2022-12-30 07:41:54 +00:00
}
2023-01-30 14:03:46 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " SML: crypto mode used for meter %d " ) , mnum + 1 ) ;
break ;
# ifdef USE_SML_AUTHKEY
case ' 5 ' :
cp + = 2 ;
for ( uint8_t cnt = 0 ; cnt < ( SML_CRYPT_SIZE * 2 ) ; cnt + = 2 ) {
mp - > auth [ cnt / 2 ] = ( sml_hexnibble ( cp [ cnt ] ) < < 4 ) | sml_hexnibble ( cp [ cnt + 1 ] ) ;
}
break ;
case ' 6 ' :
cp + = 2 ;
mp - > tout_ms = strtol ( cp , & cp , 10 ) ;
2022-12-30 07:41:54 +00:00
break ;
2023-01-30 14:03:46 +00:00
# endif
# endif
2022-12-30 07:41:54 +00:00
}
return cp ;
}
2022-07-20 13:25:40 +01:00
2022-12-30 07:41:54 +00:00
# ifdef USE_SML_DECRYPT
2023-01-30 14:03:46 +00:00
uint16_t serial_dispatch ( uint8_t meter , uint8_t sel ) {
struct METER_DESC * mp = & meter_desc [ meter ] ;
if ( ! sel ) {
return mp - > meter_ss - > available ( ) ;
2022-12-30 07:41:54 +00:00
}
2023-01-30 14:03:46 +00:00
uint8_t iob = mp - > meter_ss - > read ( ) ;
return iob ;
}
int SML_print ( const char * format , . . . ) {
static char loc_buf [ 64 ] ;
char * temp = loc_buf ;
int len ;
va_list arg ;
va_list copy ;
va_start ( arg , format ) ;
va_copy ( copy , arg ) ;
len = vsnprintf ( NULL , 0 , format , arg ) ;
va_end ( copy ) ;
if ( len > = sizeof ( loc_buf ) ) {
temp = ( char * ) malloc ( len + 1 ) ;
if ( temp = = NULL ) {
return 0 ;
}
2022-12-30 07:41:54 +00:00
}
2023-01-30 14:03:46 +00:00
vsnprintf ( temp , len + 1 , format , arg ) ;
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SML: %s " ) , temp ) ;
va_end ( arg ) ;
if ( len > = sizeof ( loc_buf ) ) {
free ( temp ) ;
2022-12-30 07:41:54 +00:00
}
2023-01-30 14:03:46 +00:00
return len ;
2022-07-20 13:25:40 +01:00
}
2023-01-30 14:03:46 +00:00
# endif // USE_SML_DECRYPT
2022-07-20 13:25:40 +01:00
2022-12-30 07:41:54 +00:00
void reset_sml_vars ( uint16_t maxmeters ) {
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
for ( uint32_t meters = 0 ; meters < maxmeters ; meters + + ) {
2023-01-30 14:03:46 +00:00
struct METER_DESC * mp = & meter_desc [ meters ] ;
mp - > spos = 0 ;
mp - > sbsiz = SML_BSIZ ;
mp - > sibsiz = TMSBSIZ ;
if ( mp - > sbuff ) {
free ( mp - > sbuff ) ;
mp - > sbuff = 0 ;
2022-12-30 07:41:54 +00:00
}
# ifdef USE_SML_SPECOPT
2023-01-30 14:03:46 +00:00
mp - > so_obis1 = 0 ;
mp - > so_obis2 = 0 ;
2022-12-30 07:41:54 +00:00
# endif
2023-01-30 14:03:46 +00:00
mp - > so_flags = 0 ;
2022-12-30 07:41:54 +00:00
// addresses a bug in meter DWS74
# ifdef DWS74_BUG
2023-01-30 14:03:46 +00:00
mp - > so_flags | = SO_DWS74_BUG ;
2022-12-30 07:41:54 +00:00
# endif
2019-09-16 18:58:22 +01:00
2022-12-30 07:41:54 +00:00
# ifdef SML_OBIS_LINE
2023-01-30 14:03:46 +00:00
mp - > so_flags | = SO_OBIS_LINE ;
2022-12-30 07:41:54 +00:00
# endif
2023-01-30 14:03:46 +00:00
if ( mp - > txmem ) {
free ( mp - > txmem ) ;
mp - > txmem = 0 ;
2022-12-30 07:41:54 +00:00
}
2023-01-30 14:03:46 +00:00
mp - > txmem = 0 ;
mp - > trxpin = - 1 ;
if ( mp - > meter_ss ) {
delete mp - > meter_ss ;
mp - > meter_ss = NULL ;
2022-12-30 07:41:54 +00:00
}
2023-01-30 14:03:46 +00:00
mp - > lastms = millis ( ) ;
mp - > tout_ms = SML_STIMEOUT ;
# ifdef USE_SML_DECRYPT
if ( mp - > use_crypt ) {
if ( mp - > hp ) {
delete mp - > hp ;
mp - > hp = NULL ;
}
}
mp - > use_crypt = 0 ;
# ifdef USE_SML_AUTHKEY
memset ( mp - > auth , 0 , SML_CRYPT_SIZE ) ;
# endif
# endif // USE_SML_DECRYPT
2019-08-27 14:33:09 +01:00
}
2022-12-30 07:41:54 +00:00
}
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
void SML_Init ( void ) {
sml_globs . ready = false ;
if ( ! bitRead ( Settings - > rule_enabled , 0 ) ) {
return ;
2019-09-16 18:58:22 +01:00
}
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
sml_globs . mp = meter_desc ;
uint8_t meter_script = Run_Scripter ( " >M " , - 2 , 0 ) ;
if ( meter_script ! = 99 ) {
AddLog ( LOG_LEVEL_INFO , PSTR ( " no meter section found! " ) ) ;
return ;
2022-07-20 13:25:40 +01:00
}
2022-12-30 07:41:54 +00:00
char * lp = glob_script_mem . section_ptr ;
uint8_t new_meters_used ;
2019-09-03 09:54:01 +01:00
2022-12-30 07:41:54 +00:00
// use script definition
if ( sml_globs . script_meter ) {
// restart condition
free ( sml_globs . script_meter ) ;
if ( sml_globs . meter_vars ) {
free ( sml_globs . meter_vars ) ;
sml_globs . meter_vars = 0 ;
2019-09-16 18:58:22 +01:00
}
2022-12-30 07:41:54 +00:00
if ( sml_globs . dvalid ) {
free ( sml_globs . dvalid ) ;
sml_globs . dvalid = 0 ;
}
# ifdef USE_SML_MEDIAN_FILTER
if ( sml_globs . sml_mf ) {
free ( sml_globs . sml_mf ) ;
sml_globs . sml_mf = 0 ;
2020-05-15 14:30:32 +01:00
}
2022-12-30 07:41:54 +00:00
# endif
reset_sml_vars ( sml_globs . meters_used ) ;
}
2020-05-15 14:30:32 +01:00
2022-12-30 07:41:54 +00:00
if ( * lp = = ' > ' & & * ( lp + 1 ) = = ' M ' ) {
lp + = 2 ;
sml_globs . meters_used = strtol ( lp , & lp , 10 ) ;
} else {
return ;
2019-09-03 09:54:01 +01:00
}
2022-12-30 07:41:54 +00:00
sml_globs . maxvars = 0 ;
reset_sml_vars ( sml_globs . meters_used ) ;
sml_globs . sml_desc_cnt = 0 ;
sml_globs . script_meter = 0 ;
uint8_t * tp = 0 ;
uint16_t index = 0 ;
uint8_t section = 0 ;
int8_t srcpin = 0 ;
uint32_t mlen ;
uint16_t memory = 0 ;
2020-05-15 14:30:32 +01:00
2022-12-30 07:41:54 +00:00
sml_globs . sml_send_blocks = 0 ;
lp = glob_script_mem . section_ptr ;
while ( lp ) {
2019-08-27 14:33:09 +01:00
if ( ! section ) {
2022-12-30 07:41:54 +00:00
if ( * lp = = ' > ' & & * ( lp + 1 ) = = ' M ' ) {
2021-02-22 10:10:49 +00:00
lp + = 2 ;
section = 1 ;
2022-12-30 07:41:54 +00:00
mlen = SML_getscriptsize ( lp ) ;
2021-02-22 10:10:49 +00:00
if ( mlen = = 0 ) return ; // missing end #
2022-12-30 07:41:54 +00:00
sml_globs . script_meter = ( uint8_t * ) calloc ( mlen , 1 ) ;
memory + = mlen ;
if ( ! sml_globs . script_meter ) {
2019-09-16 18:58:22 +01:00
goto dddef_exit ;
}
2022-12-30 07:41:54 +00:00
tp = sml_globs . script_meter ;
2019-08-27 14:33:09 +01:00
goto next_line ;
}
}
else {
2021-02-22 10:10:49 +00:00
if ( ! * lp | | * lp = = ' # ' | | * lp = = ' > ' ) {
2022-09-16 11:27:16 +01:00
if ( * ( tp - 1 ) = = ' | ' ) * ( tp - 1 ) = 0 ;
2019-08-27 14:33:09 +01:00
break ;
}
2021-02-22 10:10:49 +00:00
if ( * lp = = ' + ' ) {
2019-08-27 14:33:09 +01:00
// add descriptor +1,1,c,0,10,H20
//toLogEOL(">>",lp);
lp + + ;
2022-09-16 11:27:16 +01:00
index = * lp & 7 ;
2021-02-22 10:10:49 +00:00
lp + = 2 ;
2022-12-30 07:41:54 +00:00
if ( index < 1 | | index > sml_globs . meters_used ) {
2021-02-22 10:10:49 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " illegal meter number! " ) ) ;
goto next_line ;
}
2019-08-27 14:33:09 +01:00
index - - ;
2022-09-16 11:27:16 +01:00
srcpin = strtol ( lp , & lp , 10 ) ;
2021-12-15 13:46:05 +00:00
if ( Gpio_used ( abs ( srcpin ) ) ) {
2022-09-16 11:27:16 +01:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " SML: Error: Duplicate GPIO %d defined. Not usable for RX in meter number %d " ) , abs ( srcpin ) , index + 1 ) ;
2019-09-16 18:58:22 +01:00
dddef_exit :
2022-12-30 07:41:54 +00:00
if ( sml_globs . script_meter ) free ( sml_globs . script_meter ) ;
sml_globs . script_meter = 0 ;
return ;
2019-08-27 14:33:09 +01:00
}
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . srcpin = srcpin ;
2021-02-22 10:10:49 +00:00
if ( * lp ! = ' , ' ) goto next_line ;
2019-08-27 14:33:09 +01:00
lp + + ;
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . type = * lp ;
2020-11-15 15:09:05 +00:00
lp + + ;
2021-02-22 10:10:49 +00:00
if ( * lp ! = ' , ' ) {
2021-03-30 07:38:13 +01:00
switch ( * lp ) {
case ' N ' :
lp + + ;
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . sopt = 0x10 | ( * lp & 3 ) ;
2021-03-30 07:38:13 +01:00
lp + + ;
break ;
case ' E ' :
lp + + ;
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . sopt = 0x20 | ( * lp & 3 ) ;
2021-03-30 07:38:13 +01:00
lp + + ;
break ;
case ' O ' :
lp + + ;
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . sopt = 0x30 | ( * lp & 3 ) ;
2021-03-30 07:38:13 +01:00
lp + + ;
break ;
default :
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . sopt = * lp & 7 ;
2021-03-30 07:38:13 +01:00
lp + + ;
}
2020-11-15 15:09:05 +00:00
} else {
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . sopt = 0 ;
2020-11-15 15:09:05 +00:00
}
lp + + ;
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . flag = strtol ( lp , & lp , 10 ) ;
2021-02-22 10:10:49 +00:00
if ( * lp ! = ' , ' ) goto next_line ;
2019-08-27 14:33:09 +01:00
lp + + ;
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . params = strtol ( lp , & lp , 10 ) ;
2021-02-22 10:10:49 +00:00
if ( * lp ! = ' , ' ) goto next_line ;
2019-08-27 14:33:09 +01:00
lp + + ;
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . prefix [ 7 ] = 0 ;
2021-02-22 10:10:49 +00:00
for ( uint32_t cnt = 0 ; cnt < 8 ; cnt + + ) {
if ( * lp = = SCRIPT_EOL | | * lp = = ' , ' ) {
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . prefix [ cnt ] = 0 ;
2019-08-27 14:33:09 +01:00
break ;
}
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . prefix [ cnt ] = * lp + + ;
2019-08-27 14:33:09 +01:00
}
2021-02-22 10:10:49 +00:00
if ( * lp = = ' , ' ) {
2019-09-03 09:39:51 +01:00
lp + + ;
2022-12-04 06:47:21 +00:00
// get TRX pin
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . trxpin = strtol ( lp , & lp , 10 ) ;
if ( Gpio_used ( meter_desc [ index ] . trxpin ) ) {
AddLog ( LOG_LEVEL_INFO , PSTR ( " SML: Error: Duplicate GPIO %d defined. Not usable for TX in meter number %d " ) , meter_desc [ index ] . trxpin , index + 1 ) ;
2019-09-16 18:58:22 +01:00
goto dddef_exit ;
}
2022-12-04 06:47:21 +00:00
// optional transmit enable pin
if ( * lp = = ' ( ' ) {
lp + + ;
if ( * lp = = ' i ' ) {
lp + + ;
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . trx_en . trxenpol = 1 ;
2022-12-04 06:47:21 +00:00
} else {
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . trx_en . trxenpol = 0 ;
2022-12-04 06:47:21 +00:00
}
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . trx_en . trxenpin = strtol ( lp , & lp , 10 ) ;
2022-12-04 06:47:21 +00:00
if ( * lp ! = ' ) ' ) {
goto dddef_exit ;
}
lp + + ;
2022-12-30 07:41:54 +00:00
if ( Gpio_used ( meter_desc [ index ] . trx_en . trxenpin ) ) {
AddLog ( LOG_LEVEL_INFO , PSTR ( " SML: Error: Duplicate GPIO %d defined. Not usable for TX enable in meter number %d " ) , meter_desc [ index ] . trx_en . trxenpin , index + 1 ) ;
2022-12-04 06:47:21 +00:00
goto dddef_exit ;
}
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . trx_en . trxen = 1 ;
pinMode ( meter_desc [ index ] . trx_en . trxenpin , OUTPUT ) ;
digitalWrite ( meter_desc [ index ] . trx_en . trxenpin , meter_desc [ index ] . trx_en . trxenpol ) ;
2022-12-04 06:47:21 +00:00
} else {
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . trx_en . trxen = 0 ;
2022-12-04 06:47:21 +00:00
}
2021-02-22 10:10:49 +00:00
if ( * lp ! = ' , ' ) goto next_line ;
2019-09-03 09:39:51 +01:00
lp + + ;
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . tsecs = strtol ( lp , & lp , 10 ) ;
2021-02-22 10:10:49 +00:00
if ( * lp = = ' , ' ) {
2019-09-03 09:39:51 +01:00
lp + + ;
2021-12-08 15:30:11 +00:00
// look ahead
uint16_t txlen = 0 ;
uint16_t tx_entries = 1 ;
char * txp = lp ;
while ( * txp ) {
if ( * txp = = ' , ' ) tx_entries + + ;
if ( * txp = = SCRIPT_EOL ) {
if ( tx_entries > 1 ) {
if ( * ( txp - 1 ) ! = ' , ' ) {
break ;
}
// line ends with ,
} else {
// single entry
break ;
}
}
txp + + ;
txlen + + ;
}
if ( txlen ) {
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . txmem = ( char * ) calloc ( txlen + 2 , 1 ) ;
memory + = txlen + 2 ;
if ( meter_desc [ index ] . txmem ) {
2021-12-08 15:30:11 +00:00
// now copy send blocks
char * txp = lp ;
uint16_t tind = 0 ;
for ( uint32_t cnt = 0 ; cnt < txlen ; cnt + + ) {
if ( * txp = = SCRIPT_EOL ) {
txp + + ;
} else {
2022-12-30 07:41:54 +00:00
meter_desc [ index ] . txmem [ tind ] = * txp + + ;
2021-12-08 15:30:11 +00:00
tind + + ;
}
}
}
2022-12-30 07:41:54 +00:00
//AddLog(LOG_LEVEL_INFO, PSTR(">>> %s - %d"), meter_desc[index].txmem, txlen);
meter_desc [ index ] . index = 0 ;
meter_desc [ index ] . max_index = tx_entries ;
sml_globs . sml_send_blocks + + ;
2021-12-08 15:30:11 +00:00
lp + = txlen ;
}
2019-09-03 09:39:51 +01:00
}
}
2021-02-22 10:10:49 +00:00
if ( * lp = = SCRIPT_EOL ) lp - - ;
2019-08-27 14:33:09 +01:00
goto next_line ;
}
2022-12-30 07:41:54 +00:00
char * lp1 ;
2020-05-15 14:30:32 +01:00
# ifdef SML_REPLACE_VARS
char dstbuf [ SML_SRCBSIZE * 2 ] ;
2022-12-30 07:41:54 +00:00
Replace_Cmd_Vars ( lp , 1 , dstbuf , sizeof ( dstbuf ) ) ;
2021-02-22 10:10:49 +00:00
lp + = SML_getlinelen ( lp ) ;
2022-12-30 07:41:54 +00:00
lp1 = dstbuf ;
# else
lp1 = lp ;
lp + = SML_getlinelen ( lp ) ;
# endif // SML_REPLACE_VARS
2021-06-05 10:47:09 +01:00
//AddLog(LOG_LEVEL_INFO, PSTR("%s"),dstbuf);
2021-02-22 10:10:49 +00:00
if ( * lp1 = = ' - ' | | isdigit ( * lp1 ) ) {
2020-05-15 14:30:32 +01:00
//toLogEOL(">>",lp);
// add meters line -1,1-0:1.8.0*255(@10000,H2OIN,cbm,COUNTER,4|
2021-02-22 10:10:49 +00:00
if ( * lp1 = = ' - ' ) lp1 + + ;
uint8_t mnum = strtol ( lp1 , 0 , 10 ) ;
2022-12-30 07:41:54 +00:00
if ( mnum < 1 | | mnum > sml_globs . meters_used ) {
2021-02-22 10:10:49 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " illegal meter number! " ) ) ;
goto next_line ;
}
// 1,=h—————————————
2022-12-30 07:41:54 +00:00
if ( ! strncmp ( lp1 + 1 , " ,=h " , 3 ) | | ! strncmp ( lp1 + 1 , " ,=so " , 4 ) ) {
if ( ! strncmp ( lp1 + 1 , " ,=so " , 4 ) ) {
SpecOptions ( lp1 + 5 , mnum - 1 ) ;
2022-07-20 13:25:40 +01:00
}
2022-12-30 07:41:54 +00:00
} else {
sml_globs . maxvars + + ;
2022-07-20 13:25:40 +01:00
}
2022-12-30 07:41:54 +00:00
2020-05-15 14:30:32 +01:00
while ( 1 ) {
2021-02-22 10:10:49 +00:00
if ( * lp1 = = 0 ) {
* tp + + = ' | ' ;
2020-05-15 14:30:32 +01:00
goto next_line ;
}
2021-02-22 10:10:49 +00:00
* tp + + = * lp1 + + ;
2020-05-15 14:30:32 +01:00
index + + ;
2021-02-22 10:10:49 +00:00
if ( index > = METER_DEF_SIZE ) break ;
2020-05-15 14:30:32 +01:00
}
}
2019-08-27 14:33:09 +01:00
}
next_line :
2022-09-16 11:27:16 +01:00
if ( * lp = = SCRIPT_EOL ) {
2019-08-27 14:33:09 +01:00
lp + + ;
} else {
lp = strchr ( lp , SCRIPT_EOL ) ;
if ( ! lp ) break ;
lp + + ;
}
}
2022-09-16 11:27:16 +01:00
* tp = 0 ;
2022-12-30 07:41:54 +00:00
sml_globs . meter_p = sml_globs . script_meter ;
// set serial buffers
for ( uint32_t meters = 0 ; meters < sml_globs . meters_used ; meters + + ) {
struct METER_DESC * mp = & meter_desc [ meters ] ;
if ( mp - > sbsiz ) {
mp - > sbuff = ( uint8_t * ) calloc ( mp - > sbsiz , 1 ) ;
memory + = mp - > sbsiz ;
}
2020-05-15 14:30:32 +01:00
}
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
// initialize hardware
2019-08-27 14:33:09 +01:00
typedef void ( * function ) ( ) ;
2022-09-16 11:27:16 +01:00
uint8_t cindex = 0 ;
2019-08-27 14:33:09 +01:00
// preloud counters
2022-12-30 07:41:54 +00:00
for ( uint8_t i = 0 ; i < MAX_COUNTERS ; i + + ) {
2021-12-15 13:46:05 +00:00
RtcSettings . pulse_counter [ i ] = Settings - > pulse_counter [ i ] ;
2022-02-08 16:15:41 +00:00
sml_counters [ i ] . sml_cnt_last_ts = millis ( ) ;
2019-08-27 14:33:09 +01:00
}
2022-12-30 07:41:54 +00:00
# ifdef ESP32
2022-07-03 14:56:16 +01:00
uint32_t uart_index = SOC_UART_NUM - 1 ;
2019-08-27 14:33:09 +01:00
# endif
2022-12-30 07:41:54 +00:00
sml_counter_pinstate = 0 ;
for ( uint8_t meters = 0 ; meters < sml_globs . meters_used ; meters + + ) {
if ( sml_globs . mp [ meters ] . type = = ' c ' ) {
if ( sml_globs . mp [ meters ] . flag & 2 ) {
2019-08-27 14:33:09 +01:00
} else {
// counters, set to input with pullup
2022-12-30 07:41:54 +00:00
if ( sml_globs . mp [ meters ] . flag & 1 ) {
pinMode ( sml_globs . mp [ meters ] . srcpin , INPUT_PULLUP ) ;
2019-08-27 14:33:09 +01:00
} else {
2022-12-30 07:41:54 +00:00
pinMode ( sml_globs . mp [ meters ] . srcpin , INPUT ) ;
2019-08-27 14:33:09 +01:00
}
// check for irq mode
2022-12-30 07:41:54 +00:00
if ( sml_globs . mp [ meters ] . params < = 0 ) {
2019-08-27 14:33:09 +01:00
// init irq mode
2022-02-08 16:15:41 +00:00
sml_counters [ cindex ] . sml_cnt_old_state = meters ;
2022-12-30 07:41:54 +00:00
sml_counters [ cindex ] . sml_debounce = - sml_globs . mp [ meters ] . params ;
attachInterruptArg ( sml_globs . mp [ meters ] . srcpin , SML_CounterIsr , & sml_cnt_index [ cindex ] , CHANGE ) ;
if ( digitalRead ( sml_globs . mp [ meters ] . srcpin ) > 0 ) {
2022-02-08 16:15:41 +00:00
sml_counter_pinstate | = ( 1 < < cindex ) ;
}
sml_counters [ cindex ] . sml_counter_ltime = millis ( ) ;
2019-08-27 14:33:09 +01:00
}
2022-02-08 16:15:41 +00:00
RtcSettings . pulse_counter [ cindex ] = Settings - > pulse_counter [ cindex ] ;
2022-09-08 13:35:04 +01:00
InjektCounterValue ( meters , RtcSettings . pulse_counter [ cindex ] , 0.0 ) ;
2019-08-27 14:33:09 +01:00
cindex + + ;
}
} else {
// serial input, init
2022-12-30 07:41:54 +00:00
# ifdef ESP8266
2019-08-27 14:33:09 +01:00
# ifdef SPECIAL_SS
2022-12-30 07:41:54 +00:00
char type = sml_globs . mp [ meters ] . type ;
2023-01-07 10:32:26 +00:00
if ( type = = ' m ' | | type = = ' M ' | | type = = ' k ' | | type = = ' p ' | | type = = ' R ' | | type = = ' v ' ) {
2022-12-30 07:41:54 +00:00
meter_desc [ meters ] . meter_ss = new TasmotaSerial ( sml_globs . mp [ meters ] . srcpin , sml_globs . mp [ meters ] . trxpin , 1 , 0 , meter_desc [ meters ] . sibsiz ) ;
2019-09-03 09:39:51 +01:00
} else {
2022-12-30 07:41:54 +00:00
meter_desc [ meters ] . meter_ss = new TasmotaSerial ( sml_globs . mp [ meters ] . srcpin , sml_globs . mp [ meters ] . trxpin , 1 , 1 , meter_desc [ meters ] . sibsiz ) ;
2019-09-03 09:39:51 +01:00
}
2020-05-15 14:30:32 +01:00
# else
2022-12-30 07:41:54 +00:00
meter_desc [ meters ] . meter_ss = new TasmotaSerial ( sml_globs . mp [ meters ] . srcpin , sml_globs . mp [ meters ] . trxpin , 1 , 0 , meter_desc [ meters ] . sibsiz ) ;
# endif // SPECIAL_SS
# endif // ESP8266
2020-05-15 14:30:32 +01:00
# ifdef ESP32
2021-12-15 13:46:05 +00:00
// use hardware serial
# ifdef USE_ESP32_SW_SERIAL
2022-12-30 07:41:54 +00:00
meter_desc [ meters ] . meter_ss = new SML_ESP32_SERIAL ( uart_index ) ;
if ( sml_globs . mp [ meters ] . srcpin > = 0 ) {
2021-12-15 13:46:05 +00:00
if ( uart_index = = 0 ) { ClaimSerial ( ) ; }
uart_index - - ;
if ( uart_index < 0 ) uart_index = 0 ;
}
# else
2022-12-30 07:41:54 +00:00
meter_desc [ meters ] . meter_ss = new HardwareSerial ( uart_index ) ;
2021-12-15 13:46:05 +00:00
if ( uart_index = = 0 ) { ClaimSerial ( ) ; }
2020-05-15 14:30:32 +01:00
uart_index - - ;
2021-12-15 13:46:05 +00:00
if ( uart_index < 0 ) uart_index = 0 ;
2022-12-30 07:41:54 +00:00
meter_desc [ meters ] . meter_ss - > setRxBufferSize ( meter_desc [ meters ] . sibsiz ) ;
2021-12-15 13:46:05 +00:00
# endif // USE_ESP32_SW_SERIAL
2020-11-28 16:00:15 +00:00
# endif // ESP32
2020-05-15 14:30:32 +01:00
2020-11-15 15:09:05 +00:00
SerialConfig smode = SERIAL_8N1 ;
2021-03-30 07:38:13 +01:00
2022-12-30 07:41:54 +00:00
if ( sml_globs . mp [ meters ] . sopt & 0xf0 ) {
2021-03-30 07:38:13 +01:00
// new serial config
2022-12-30 07:41:54 +00:00
switch ( sml_globs . mp [ meters ] . sopt > > 4 ) {
2021-03-30 07:38:13 +01:00
case 1 :
2022-12-30 07:41:54 +00:00
if ( ( sml_globs . mp [ meters ] . sopt & 1 ) = = 1 ) smode = SERIAL_8N1 ;
2021-03-30 07:38:13 +01:00
else smode = SERIAL_8N2 ;
break ;
case 2 :
2022-12-30 07:41:54 +00:00
if ( ( sml_globs . mp [ meters ] . sopt & 1 ) = = 1 ) smode = SERIAL_8E1 ;
2021-03-30 07:38:13 +01:00
else smode = SERIAL_8E2 ;
break ;
case 3 :
2022-12-30 07:41:54 +00:00
if ( ( sml_globs . mp [ meters ] . sopt & 1 ) = = 1 ) smode = SERIAL_8O1 ;
2021-03-30 07:38:13 +01:00
else smode = SERIAL_8O2 ;
break ;
}
} else {
2022-12-30 07:41:54 +00:00
// deprecated serial config
if ( sml_globs . mp [ meters ] . sopt = = 2 ) {
2021-03-30 07:38:13 +01:00
smode = SERIAL_8N2 ;
}
2022-12-30 07:41:54 +00:00
if ( sml_globs . mp [ meters ] . type = = ' M ' ) {
2021-03-30 07:38:13 +01:00
smode = SERIAL_8E1 ;
2022-12-30 07:41:54 +00:00
if ( sml_globs . mp [ meters ] . sopt = = 2 ) {
2021-03-30 07:38:13 +01:00
smode = SERIAL_8E2 ;
}
2020-11-15 15:09:05 +00:00
}
2020-05-15 14:30:32 +01:00
}
2021-03-30 07:38:13 +01:00
2020-11-28 16:00:15 +00:00
# ifdef ESP8266
2022-12-30 07:41:54 +00:00
if ( meter_desc [ meters ] . meter_ss - > begin ( sml_globs . mp [ meters ] . params ) ) {
meter_desc [ meters ] . meter_ss - > flush ( ) ;
2019-08-27 14:33:09 +01:00
}
2022-12-30 07:41:54 +00:00
if ( meter_desc [ meters ] . meter_ss - > hardwareSerial ( ) ) {
Serial . begin ( sml_globs . mp [ meters ] . params , smode ) ;
2019-11-25 18:08:05 +00:00
ClaimSerial ( ) ;
2020-05-15 14:30:32 +01:00
//Serial.setRxBufferSize(512);
2019-11-25 18:08:05 +00:00
}
2020-11-28 16:00:15 +00:00
# endif // ESP8266
2022-12-30 07:41:54 +00:00
2020-11-28 16:00:15 +00:00
# ifdef ESP32
2022-12-30 07:41:54 +00:00
meter_desc [ meters ] . meter_ss - > begin ( sml_globs . mp [ meters ] . params , smode , sml_globs . mp [ meters ] . srcpin , sml_globs . mp [ meters ] . trxpin ) ;
# ifdef USE_ESP32_SW_SERIAL
meter_desc [ meters ] . meter_ss - > setRxBufferSize ( meter_desc [ meters ] . sibsiz ) ;
# endif
2020-11-28 16:00:15 +00:00
# endif // ESP32
2019-08-27 14:33:09 +01:00
}
}
2022-12-30 07:41:54 +00:00
sml_globs . meter_vars = ( double * ) calloc ( sml_globs . maxvars , sizeof ( double ) ) ;
sml_globs . dvalid = ( uint8_t * ) calloc ( sml_globs . maxvars , sizeof ( uint8_t ) ) ;
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
# ifdef USE_SML_MEDIAN_FILTER
sml_globs . sml_mf = ( struct SML_MEDIAN_FILTER * ) calloc ( sml_globs . maxvars , sizeof ( struct SML_MEDIAN_FILTER ) ) ;
# endif
if ( ! sml_globs . maxvars | | ! sml_globs . meter_vars | | ! sml_globs . dvalid | | ! sml_globs . sml_mf ) {
AddLog ( LOG_LEVEL_INFO , PSTR ( " sml memory error! " ) ) ;
return ;
}
memory + = sizeof ( sml_globs ) + sizeof ( meter_desc ) + sml_globs . maxvars * ( sizeof ( double ) + sizeof ( uint8_t ) + sizeof ( struct SML_MEDIAN_FILTER ) ) ;
AddLog ( LOG_LEVEL_INFO , PSTR ( " meters: %d , decode lines: %d, memory used: %d bytes " ) , sml_globs . meters_used , sml_globs . maxvars , memory ) ;
// speed optimize shift flag
for ( uint32_t meters = 0 ; meters < sml_globs . meters_used ; meters + + ) {
struct METER_DESC * mp = & meter_desc [ meters ] ;
char type = mp - > type ;
if ( ! ( mp - > so_flags & SO_OBIS_LINE ) ) {
mp - > shift_mode = ( type ! = ' e ' & & type ! = ' k ' & & type ! = ' m ' & & type ! = ' M ' & & type ! = ' p ' & & type ! = ' R ' & & type ! = ' v ' ) ;
} else {
mp - > shift_mode = ( type ! = ' o ' & & type ! = ' e ' & & type ! = ' k ' & & type ! = ' m ' & & type ! = ' M ' & & type ! = ' p ' & & type ! = ' R ' & & type ! = ' v ' ) ;
2021-10-23 07:07:51 +01:00
}
2023-01-30 14:03:46 +00:00
# ifdef USE_SML_DECRYPT
if ( mp - > use_crypt ) {
# ifdef USE_SML_AUTHKEY
mp - > hp = new Han_Parser ( serial_dispatch , meters , mp - > key , mp - > auth ) ;
# else
mp - > hp = new Han_Parser ( serial_dispatch , meters , mp - > key , nullptr ) ;
# endif
}
# endif
2021-10-23 07:07:51 +01:00
}
2022-12-30 07:41:54 +00:00
sml_globs . ready = true ;
2021-10-23 07:07:51 +01:00
}
2022-12-30 07:41:54 +00:00
# ifdef USE_SML_SCRIPT_CMD
2020-02-04 06:07:56 +00:00
uint32_t SML_SetBaud ( uint32_t meter , uint32_t br ) {
2022-12-30 07:41:54 +00:00
if ( sml_globs . ready = = false ) return 0 ;
if ( meter < 1 | | meter > sml_globs . meters_used ) return 0 ;
2020-02-04 06:07:56 +00:00
meter - - ;
2022-12-30 07:41:54 +00:00
if ( ! meter_desc [ meter ] . meter_ss ) return 0 ;
2020-11-28 16:00:15 +00:00
# ifdef ESP8266
2022-12-30 07:41:54 +00:00
if ( meter_desc [ meter ] . meter_ss - > begin ( br ) ) {
meter_desc [ meter ] . meter_ss - > flush ( ) ;
2020-11-28 16:00:15 +00:00
}
2022-12-30 07:41:54 +00:00
if ( meter_desc [ meter ] . meter_ss - > hardwareSerial ( ) ) {
if ( sml_globs . mp [ meter ] . type = = ' M ' ) {
2020-11-28 16:00:15 +00:00
Serial . begin ( br , SERIAL_8E1 ) ;
}
}
# endif // ESP8266
2020-06-01 14:17:32 +01:00
# ifdef ESP32
2022-12-30 07:41:54 +00:00
meter_desc [ meter ] . meter_ss - > flush ( ) ;
meter_desc [ meter ] . meter_ss - > updateBaudRate ( br ) ;
2020-07-15 08:44:52 +01:00
/*
2022-12-30 07:41:54 +00:00
if ( sml_globs . mp [ meter ] . type = = ' M ' ) {
meter_desc . meter_ss [ meter ] - > begin ( br , SERIAL_8E1 , sml_globs . mp [ meter ] . srcpin , sml_globs . mp [ meter ] . trxpin ) ;
2020-07-15 08:44:52 +01:00
} else {
2022-12-30 07:41:54 +00:00
meter_desc . meter_ss [ meter ] - > begin ( br , SERIAL_8N1 , sml_globs . mp [ meter ] . srcpin , sml_globs . mp [ meter ] . trxpin ) ;
2020-07-15 08:44:52 +01:00
} */
2020-11-28 16:00:15 +00:00
# endif // ESP32
2020-02-04 06:07:56 +00:00
return 1 ;
}
2022-12-30 07:41:54 +00:00
uint32_t sml_status ( uint32_t meter ) {
if ( sml_globs . ready = = false ) return 0 ;
if ( meter < 1 | | meter > sml_globs . meters_used ) return 0 ;
2020-03-29 15:29:22 +01:00
meter - - ;
2022-07-20 13:25:40 +01:00
# if defined(ED300L) || defined(AS2020) || defined(DTZ541) || defined(USE_SML_SPECOPT)
2022-12-30 07:41:54 +00:00
return sml_globs . sml_status [ meter ] ;
2020-03-29 15:29:22 +01:00
# else
return 0 ;
# endif
}
2022-12-30 07:41:54 +00:00
uint32_t SML_Write ( int32_t meter , char * hstr ) {
if ( sml_globs . ready = = false ) return 0 ;
int8_t flag = meter ;
meter = abs ( meter ) ;
if ( meter < 1 | | meter > sml_globs . meters_used ) return 0 ;
2020-02-04 06:07:56 +00:00
meter - - ;
2022-12-30 07:41:54 +00:00
if ( ! meter_desc [ meter ] . meter_ss ) return 0 ;
if ( flag > 0 ) {
SML_Send_Seq ( meter , hstr ) ;
} else {
// 9600:8E1, only hardware serial
uint32_t baud = strtol ( hstr , & hstr , 10 ) ;
hstr + + ;
// currently only 8 bits and ignore stopbits
hstr + + ;
SerialConfig smode ;
switch ( * hstr ) {
case ' N ' :
smode = SERIAL_8N1 ;
break ;
case ' E ' :
smode = SERIAL_8E1 ;
break ;
case ' O ' :
smode = SERIAL_8O1 ;
break ;
}
# ifdef ESP8266
Serial . begin ( baud , smode ) ;
# else
meter_desc [ meter ] . meter_ss - > begin ( baud , smode , sml_globs . mp [ meter ] . srcpin , sml_globs . mp [ meter ] . trxpin ) ;
# endif
}
2020-02-04 06:07:56 +00:00
return 1 ;
}
2020-05-23 09:24:08 +01:00
2022-12-30 07:41:54 +00:00
uint32_t SML_Read ( int32_t meter , char * str , uint32_t slen ) {
if ( sml_globs . ready = = false ) return 0 ;
uint8_t hflg = 0 ;
2022-09-16 11:27:16 +01:00
if ( meter < 0 ) {
meter = abs ( meter ) ;
hflg = 1 ;
2020-07-15 08:44:52 +01:00
}
2022-12-30 07:41:54 +00:00
if ( meter < 1 | | meter > sml_globs . meters_used ) return 0 ;
2020-07-15 08:44:52 +01:00
meter - - ;
2022-12-30 07:41:54 +00:00
if ( ! meter_desc [ meter ] . meter_ss ) return 0 ;
struct METER_DESC * mp = & meter_desc [ meter ] ;
2020-07-15 08:44:52 +01:00
2022-12-30 07:41:54 +00:00
if ( ! mp - > spos ) {
2020-07-15 08:44:52 +01:00
return 0 ;
}
2022-12-30 07:41:54 +00:00
mp - > sbuff [ mp - > spos ] = 0 ;
2020-07-15 08:44:52 +01:00
if ( ! hflg ) {
2022-12-30 07:41:54 +00:00
strlcpy ( str , ( char * ) & mp - > sbuff [ 0 ] , slen ) ;
2020-07-15 08:44:52 +01:00
} else {
2022-09-16 11:27:16 +01:00
uint32_t index = 0 ;
2022-12-30 07:41:54 +00:00
for ( uint32_t cnt = 0 ; cnt < mp - > spos ; cnt + + ) {
sprintf ( str , " %02x " , mp - > sbuff [ cnt ] ) ;
2022-09-16 11:27:16 +01:00
str + = 2 ;
index + = 2 ;
if ( index > = slen - 2 ) break ;
2020-07-15 08:44:52 +01:00
}
}
2022-12-30 07:41:54 +00:00
mp - > spos = 0 ;
2020-07-15 08:44:52 +01:00
return 1 ;
}
2022-12-30 07:41:54 +00:00
uint32_t sml_getv ( uint32_t sel ) {
if ( sml_globs . ready = = false ) return 0 ;
if ( ! sel ) {
for ( uint8_t cnt = 0 ; cnt < sml_globs . maxvars ; cnt + + ) {
sml_globs . dvalid [ cnt ] = 0 ;
}
sel = 0 ;
} else {
if ( sel < 1 | | sel > sml_globs . maxvars ) { sel = 1 ; }
sel = sml_globs . dvalid [ sel - 1 ] ;
}
return sel ;
}
double SML_GetVal ( uint32_t index ) {
if ( sml_globs . ready = = false ) return 0 ;
if ( index < 1 | | index > sml_globs . maxvars ) { index = 1 ; }
return sml_globs . meter_vars [ index - 1 ] ;
2020-05-23 09:24:08 +01:00
}
2021-12-28 11:38:39 +00:00
char * SML_GetSVal ( uint32_t index ) {
2022-12-30 07:41:54 +00:00
if ( sml_globs . ready = = false ) return 0 ;
if ( index < 1 | | index > sml_globs . meters_used ) { index = 1 ; }
return ( char * ) meter_desc [ index - 1 ] . meter_id ;
2021-12-28 11:38:39 +00:00
}
2022-02-11 07:01:07 +00:00
int32_t SML_Set_WStr ( uint32_t meter , char * hstr ) {
2022-12-30 07:41:54 +00:00
if ( sml_globs . ready = = false ) return 0 ;
if ( meter < 1 | | meter > sml_globs . meters_used ) return - 1 ;
2022-02-11 07:01:07 +00:00
meter - - ;
2022-12-30 07:41:54 +00:00
if ( ! meter_desc [ meter ] . meter_ss ) return - 2 ;
meter_desc [ meter ] . script_str = hstr ;
2022-02-11 07:01:07 +00:00
return 0 ;
}
2020-05-23 09:24:08 +01:00
# endif // USE_SML_SCRIPT_CMD
2020-02-04 06:07:56 +00:00
2019-08-27 14:33:09 +01:00
void SetDBGLed ( uint8_t srcpin , uint8_t ledpin ) {
pinMode ( ledpin , OUTPUT ) ;
if ( digitalRead ( srcpin ) ) {
digitalWrite ( ledpin , LOW ) ;
} else {
digitalWrite ( ledpin , HIGH ) ;
}
}
2023-01-07 10:32:26 +00:00
// force channel math on counters
void SML_Counter_Poll_1s ( void ) {
for ( uint32_t meter = 0 ; meter < sml_globs . meters_used ; meter + + ) {
if ( sml_globs . mp [ meter ] . type = = ' c ' ) {
SML_Decode ( meter ) ;
}
}
}
# define CNT_PULSE_TIMEOUT 5000
2019-08-27 14:33:09 +01:00
// fast counter polling
void SML_Counter_Poll ( void ) {
2022-09-08 13:35:04 +01:00
uint16_t meters , cindex = 0 ;
uint32_t ctime = millis ( ) ;
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
for ( meters = 0 ; meters < sml_globs . meters_used ; meters + + ) {
if ( sml_globs . mp [ meters ] . type = = ' c ' ) {
2019-08-27 14:33:09 +01:00
// poll for counters and debouce
2022-12-30 07:41:54 +00:00
if ( sml_globs . mp [ meters ] . params > 0 ) {
if ( ctime - sml_counters [ cindex ] . sml_cnt_last_ts > sml_globs . mp [ meters ] . params ) {
2022-09-08 13:35:04 +01:00
sml_counters [ cindex ] . sml_cnt_last_ts = ctime ;
2019-08-27 14:33:09 +01:00
2022-12-30 07:41:54 +00:00
if ( sml_globs . mp [ meters ] . flag & 2 ) {
2019-08-27 14:33:09 +01:00
// analog mode, get next value
} else {
// poll digital input
uint8_t state ;
2022-09-08 13:35:04 +01:00
sml_counters [ cindex ] . sml_cnt_debounce < < = 1 ;
2022-12-30 07:41:54 +00:00
sml_counters [ cindex ] . sml_cnt_debounce | = ( digitalRead ( sml_globs . mp [ meters ] . srcpin ) & 1 ) | 0x80 ;
2022-09-08 13:35:04 +01:00
if ( sml_counters [ cindex ] . sml_cnt_debounce = = 0xc0 ) {
2019-08-27 14:33:09 +01:00
// is 1
2022-09-08 13:35:04 +01:00
state = 1 ;
2019-08-27 14:33:09 +01:00
} else {
// is 0, means switch down
2022-09-08 13:35:04 +01:00
state = 0 ;
2019-08-27 14:33:09 +01:00
}
2022-09-08 13:35:04 +01:00
if ( sml_counters [ cindex ] . sml_cnt_old_state ! = state ) {
2019-08-27 14:33:09 +01:00
// state has changed
2022-09-08 13:35:04 +01:00
sml_counters [ cindex ] . sml_cnt_old_state = state ;
if ( state = = 0 ) {
2019-08-27 14:33:09 +01:00
// inc counter
RtcSettings . pulse_counter [ cindex ] + + ;
2022-02-11 22:29:30 +00:00
sml_counters [ cindex ] . sml_counter_pulsewidth = ctime - sml_counters [ cindex ] . sml_counter_lfalltime ;
sml_counters [ cindex ] . sml_counter_lfalltime = ctime ;
2022-09-08 13:35:04 +01:00
InjektCounterValue ( meters , RtcSettings . pulse_counter [ cindex ] , 60000.0 / ( float ) sml_counters [ cindex ] . sml_counter_pulsewidth ) ;
2019-08-27 14:33:09 +01:00
}
}
}
}
# ifdef DEBUG_CNT_LED1
2022-12-30 07:41:54 +00:00
if ( cindex = = 0 ) SetDBGLed ( sml_globs . mp [ meters ] . srcpin , DEBUG_CNT_LED1 ) ;
2019-08-27 14:33:09 +01:00
# endif
# ifdef DEBUG_CNT_LED2
2022-12-30 07:41:54 +00:00
if ( cindex = = 1 ) SetDBGLed ( sml_globs . mp [ meters ] . srcpin , DEBUG_CNT_LED2 ) ;
2019-08-27 14:33:09 +01:00
# endif
} else {
2022-09-08 13:35:04 +01:00
if ( ctime - sml_counters [ cindex ] . sml_cnt_last_ts > 10 ) {
sml_counters [ cindex ] . sml_cnt_last_ts = ctime ;
2019-08-27 14:33:09 +01:00
# ifdef DEBUG_CNT_LED1
2022-12-30 07:41:54 +00:00
if ( cindex = = 0 ) SetDBGLed ( sml_globs . mp [ meters ] . srcpin , DEBUG_CNT_LED1 ) ;
2019-08-27 14:33:09 +01:00
# endif
# ifdef DEBUG_CNT_LED2
2022-12-30 07:41:54 +00:00
if ( cindex = = 1 ) SetDBGLed ( sml_globs . mp [ meters ] . srcpin , DEBUG_CNT_LED2 ) ;
2019-08-27 14:33:09 +01:00
# endif
}
2020-06-25 06:34:00 +01:00
if ( sml_counters [ cindex ] . sml_cnt_updated ) {
2023-01-07 10:32:26 +00:00
InjektCounterValue ( meters , RtcSettings . pulse_counter [ cindex ] , 60000.0 / ( float ) sml_counters [ cindex ] . sml_counter_pulsewidth ) ;
2022-09-08 13:35:04 +01:00
sml_counters [ cindex ] . sml_cnt_updated = 0 ;
2020-06-25 06:34:00 +01:00
}
2023-01-07 10:32:26 +00:00
// check timeout
uint32_t time = millis ( ) ;
if ( ( time - sml_counters [ cindex ] . sml_counter_lfalltime ) > CNT_PULSE_TIMEOUT ) {
InjektCounterValue ( meters , RtcSettings . pulse_counter [ cindex ] , 0 ) ;
sml_counters [ cindex ] . sml_counter_lfalltime = time ;
}
2019-08-27 14:33:09 +01:00
}
cindex + + ;
}
}
}
2019-09-03 09:39:51 +01:00
# ifdef USE_SCRIPT
char * SML_Get_Sequence ( char * cp , uint32_t index ) {
if ( ! index ) return cp ;
2022-09-16 11:27:16 +01:00
uint32_t cindex = 0 ;
2019-09-03 09:39:51 +01:00
while ( cp ) {
2022-09-16 11:27:16 +01:00
cp = strchr ( cp , ' , ' ) ;
2019-09-03 09:39:51 +01:00
if ( cp ) {
cp + + ;
cindex + + ;
2022-09-16 11:27:16 +01:00
if ( cindex = = index ) {
2019-09-03 09:39:51 +01:00
return cp ;
}
}
}
2020-08-09 08:36:24 +01:00
return cp ;
2019-09-03 09:39:51 +01:00
}
void SML_Check_Send ( void ) {
2022-12-30 07:41:54 +00:00
sml_globs . sml_100ms_cnt + + ;
2019-09-16 18:58:22 +01:00
char * cp ;
2022-12-30 07:41:54 +00:00
for ( uint32_t cnt = sml_globs . sml_desc_cnt ; cnt < sml_globs . meters_used ; cnt + + ) {
if ( meter_desc [ cnt ] . trxpin > = 0 & & meter_desc [ cnt ] . txmem ) {
//AddLog(LOG_LEVEL_INFO, PSTR("100 ms>> %d - %s - %d"),sml_globs.sml_desc_cnt,meter_desc[cnt].txmem,meter_desc[cnt].tsecs);
if ( ( sml_globs . sml_100ms_cnt > = meter_desc [ cnt ] . tsecs ) ) {
sml_globs . sml_100ms_cnt = 0 ;
2022-02-11 07:01:07 +00:00
// check for scriptsync extra output
2022-12-30 07:41:54 +00:00
if ( meter_desc [ cnt ] . script_str ) {
cp = meter_desc [ cnt ] . script_str ;
meter_desc [ cnt ] . script_str = 0 ;
2022-02-11 07:01:07 +00:00
} else {
//AddLog(LOG_LEVEL_INFO, PSTR("100 ms>> 2"),cp);
2022-12-30 07:41:54 +00:00
if ( meter_desc [ cnt ] . max_index > 1 ) {
meter_desc [ cnt ] . index + + ;
if ( meter_desc [ cnt ] . index > = meter_desc [ cnt ] . max_index ) {
meter_desc [ cnt ] . index = 0 ;
sml_globs . sml_desc_cnt + + ;
2022-02-11 07:01:07 +00:00
}
2022-12-30 07:41:54 +00:00
cp = SML_Get_Sequence ( meter_desc [ cnt ] . txmem , meter_desc [ cnt ] . index ) ;
2022-02-11 07:01:07 +00:00
//SML_Send_Seq(cnt,cp);
} else {
2022-12-30 07:41:54 +00:00
cp = meter_desc [ cnt ] . txmem ;
2022-02-11 07:01:07 +00:00
//SML_Send_Seq(cnt,cp);
2022-12-30 07:41:54 +00:00
sml_globs . sml_desc_cnt + + ;
2019-09-03 09:39:51 +01:00
}
2019-09-16 18:58:22 +01:00
}
2021-06-05 10:47:09 +01:00
//AddLog(LOG_LEVEL_INFO, PSTR(">> %s"),cp);
2019-09-16 18:58:22 +01:00
SML_Send_Seq ( cnt , cp ) ;
2022-12-30 07:41:54 +00:00
if ( sml_globs . sml_desc_cnt > = sml_globs . meters_used ) {
sml_globs . sml_desc_cnt = 0 ;
2019-09-03 09:39:51 +01:00
}
2019-09-16 18:58:22 +01:00
break ;
2019-09-03 09:39:51 +01:00
}
2019-09-16 18:58:22 +01:00
} else {
2022-12-30 07:41:54 +00:00
sml_globs . sml_desc_cnt + + ;
2019-09-16 18:58:22 +01:00
}
2022-12-30 07:41:54 +00:00
if ( sml_globs . sml_desc_cnt > = sml_globs . meters_used ) {
sml_globs . sml_desc_cnt = 0 ;
2019-09-03 09:39:51 +01:00
}
}
}
2022-09-08 13:35:04 +01:00
void sml_hex_asci ( uint32_t mindex , char * tpowstr ) {
2022-12-30 07:41:54 +00:00
char * cp = meter_desc [ mindex ] . meter_id ;
2022-09-08 13:35:04 +01:00
uint16_t slen = strlen ( cp ) ;
slen & = 0xfffe ;
uint16_t cnt ;
* tpowstr + + = ' " ' ;
for ( cnt = 0 ; cnt < slen ; cnt + = 2 ) {
uint8_t iob = ( sml_hexnibble ( cp [ cnt ] ) < < 4 ) | sml_hexnibble ( cp [ cnt + 1 ] ) ;
* tpowstr + + = iob ;
}
* tpowstr + + = ' " ' ;
* tpowstr = 0 ;
}
2019-09-03 09:39:51 +01:00
uint8_t sml_hexnibble ( char chr ) {
uint8_t rVal = 0 ;
if ( isdigit ( chr ) ) {
rVal = chr - ' 0 ' ;
} else {
if ( chr > = ' A ' & & chr < = ' F ' ) rVal = chr + 10 - ' A ' ;
if ( chr > = ' a ' & & chr < = ' f ' ) rVal = chr + 10 - ' a ' ;
}
return rVal ;
}
// send sequence every N Seconds
void SML_Send_Seq ( uint32_t meter , char * seq ) {
2021-02-28 08:01:13 +00:00
uint8_t sbuff [ 48 ] ;
uint8_t * ucp = sbuff , slen = 0 ;
char * cp = seq ;
2020-05-15 14:30:32 +01:00
uint8_t rflg = 0 ;
2021-02-28 08:01:13 +00:00
if ( * cp = = ' r ' ) {
2020-05-15 14:30:32 +01:00
rflg = 1 ;
cp + + ;
}
2019-09-03 09:39:51 +01:00
while ( * cp ) {
if ( ! * cp | | ! * ( cp + 1 ) ) break ;
2021-02-28 08:01:13 +00:00
if ( * cp = = ' , ' ) break ;
uint8_t iob = ( sml_hexnibble ( * cp ) < < 4 ) | sml_hexnibble ( * ( cp + 1 ) ) ;
cp + = 2 ;
* ucp + + = iob ;
2019-09-03 09:39:51 +01:00
slen + + ;
2021-02-28 08:01:13 +00:00
if ( slen > = sizeof ( sbuff ) - 6 ) break ; // leave space for checksum
2019-09-03 09:39:51 +01:00
}
2022-12-30 07:41:54 +00:00
if ( meter_desc [ meter ] . type = = ' m ' | | meter_desc [ meter ] . type = = ' M ' | | meter_desc [ meter ] . type = = ' k ' ) {
if ( meter_desc [ meter ] . type = = ' k ' ) {
2022-09-16 11:27:16 +01:00
// kamstrup, append crc, cr
* ucp + + = 0 ;
2021-02-28 08:01:13 +00:00
* ucp + + = 0 ;
2022-09-16 11:27:16 +01:00
slen + = 2 ;
uint16_t crc = KS_calculateCRC ( sbuff , slen ) ;
ucp - = 2 ;
* ucp + + = highByte ( crc ) ;
* ucp + + = lowByte ( crc ) ;
// now check for escapes
uint8_t ksbuff [ 24 ] ;
ucp = ksbuff ;
* ucp + + = 0x80 ;
uint8_t klen = 1 ;
for ( uint16_t cnt = 0 ; cnt < slen ; cnt + + ) {
uint8_t iob = sbuff [ cnt ] ;
if ( ( iob = = 0x80 ) | | ( iob = = 0x40 ) | | ( iob = = 0x0d ) | | ( iob = = 0x06 ) | | ( iob = = 0x1b ) ) {
* ucp + + = 0x1b ;
* ucp + + = iob ^ = 0xff ;
klen + = 2 ;
} else {
* ucp + + = iob ;
klen + + ;
}
}
* ucp + + = 0xd ;
slen = klen + 1 ;
memcpy ( sbuff , ksbuff , slen ) ;
} else {
if ( ! rflg ) {
* ucp + + = 0 ;
* ucp + + = 2 ;
slen + = 2 ;
}
// append crc
uint16_t crc = MBUS_calculateCRC ( sbuff , slen , 0xFFFF ) ;
* ucp + + = lowByte ( crc ) ;
* ucp + + = highByte ( crc ) ;
2021-02-28 08:01:13 +00:00
slen + = 2 ;
2020-05-15 14:30:32 +01:00
}
2022-09-16 11:27:16 +01:00
2019-09-03 09:39:51 +01:00
}
2022-12-30 07:41:54 +00:00
if ( meter_desc [ meter ] . type = = ' o ' ) {
2021-02-28 08:01:13 +00:00
for ( uint32_t cnt = 0 ; cnt < slen ; cnt + + ) {
sbuff [ cnt ] | = ( CalcEvenParity ( sbuff [ cnt ] ) < < 7 ) ;
2019-10-20 07:23:27 +01:00
}
}
2022-12-30 07:41:54 +00:00
if ( meter_desc [ meter ] . type = = ' p ' ) {
2021-02-28 08:01:13 +00:00
* ucp + + = 0xc0 ;
* ucp + + = 0xa8 ;
* ucp + + = 1 ;
* ucp + + = 1 ;
* ucp + + = 0 ;
* ucp + + = SML_PzemCrc ( sbuff , 6 ) ;
slen + = 6 ;
2019-09-16 18:58:22 +01:00
}
2022-09-16 11:27:16 +01:00
2022-12-30 07:41:54 +00:00
if ( meter_desc [ meter ] . trx_en . trxen ) {
digitalWrite ( meter_desc [ meter ] . trx_en . trxenpin , meter_desc [ meter ] . trx_en . trxenpol ^ 1 ) ;
2022-12-04 06:47:21 +00:00
}
2022-12-30 07:41:54 +00:00
meter_desc [ meter ] . meter_ss - > flush ( ) ;
meter_desc [ meter ] . meter_ss - > write ( sbuff , slen ) ;
2022-12-04 06:47:21 +00:00
2022-12-30 07:41:54 +00:00
if ( meter_desc [ meter ] . trx_en . trxen ) {
2022-12-04 06:47:21 +00:00
// must wait for all data sent
2022-12-30 07:41:54 +00:00
meter_desc [ meter ] . meter_ss - > flush ( ) ;
digitalWrite ( meter_desc [ meter ] . trx_en . trxenpin , meter_desc [ meter ] . trx_en . trxenpol ) ;
2022-12-04 06:47:21 +00:00
}
2022-12-30 07:41:54 +00:00
if ( sml_globs . dump2log ) {
2021-02-28 08:01:13 +00:00
# ifdef SML_DUMP_OUT_ALL
Hexdump ( sbuff , slen ) ;
# else
2022-12-30 07:41:54 +00:00
uint8_t type = sml_globs . mp [ ( sml_globs . dump2log & 7 ) - 1 ] . type ;
2022-09-16 11:27:16 +01:00
if ( type = = ' m ' | | type = = ' M ' | | type = = ' k ' ) {
2021-02-25 13:36:37 +00:00
Hexdump ( sbuff , slen ) ;
}
2021-02-28 08:01:13 +00:00
# endif
2021-02-25 13:36:37 +00:00
}
2022-09-16 11:27:16 +01:00
# ifdef MODBUS_DEBUG
2022-12-30 07:41:54 +00:00
uint8_t type = meter_desc [ meter ] . type ;
if ( ! sml_globs . dump2log & & ( type = = ' m ' | | type = = ' M ' | | type = = ' k ' ) ) {
AddLog ( LOG_LEVEL_INFO , PSTR ( " transmit index >> %d " ) , sml_globs . mp [ meter ] . index ) ;
2022-09-16 11:27:16 +01:00
Hexdump ( sbuff , slen ) ;
}
# endif
2019-09-03 09:39:51 +01:00
}
# endif // USE_SCRIPT
2019-09-03 09:54:01 +01:00
2022-05-26 16:22:39 +01:00
uint16_t MBUS_calculateCRC ( uint8_t * frame , uint8_t num , uint16_t start ) {
2019-09-03 09:54:01 +01:00
uint16_t crc , flag ;
2022-05-26 16:22:39 +01:00
//crc = 0xFFFF;
crc = start ;
2019-09-03 09:54:01 +01:00
for ( uint32_t i = 0 ; i < num ; i + + ) {
crc ^ = frame [ i ] ;
for ( uint32_t j = 8 ; j ; j - - ) {
if ( ( crc & 0x0001 ) ! = 0 ) { // If the LSB is set
crc > > = 1 ; // Shift right and XOR 0xA001
crc ^ = 0xA001 ;
} else { // Else LSB is not set
crc > > = 1 ; // Just shift right
}
}
}
return crc ;
}
2022-09-16 11:27:16 +01:00
uint16_t KS_calculateCRC ( const uint8_t * frame , uint8_t num ) {
uint32_t crc = 0 ;
for ( uint32_t i = 0 ; i < num ; i + + ) {
uint8_t mask = 0x80 ;
uint8_t iob = frame [ i ] ;
while ( mask ) {
crc < < = 1 ;
if ( iob & mask ) {
crc | = 1 ;
}
mask > > = 1 ;
if ( crc & 0x10000 ) {
crc & = 0xffff ;
crc ^ = 0x1021 ;
}
}
}
return crc ;
}
2019-09-16 18:58:22 +01:00
uint8_t SML_PzemCrc ( uint8_t * data , uint8_t len ) {
uint16_t crc = 0 ;
for ( uint32_t i = 0 ; i < len ; i + + ) crc + = * data + + ;
return ( uint8_t ) ( crc & 0xFF ) ;
}
2019-10-20 07:23:27 +01:00
2019-08-27 14:33:09 +01:00
// for odd parity init with 1
uint8_t CalcEvenParity ( uint8_t data ) {
uint8_t parity = 0 ;
while ( data ) {
parity ^ = ( data & 1 ) ;
data > > = 1 ;
}
return parity ;
}
2019-10-20 07:23:27 +01:00
2019-08-27 14:33:09 +01:00
// dump to log shows serial data on console
// has to be off for normal use
2022-01-20 19:19:50 +00:00
// in console sensor53 d1, d2, d3 ... or d0 for normal use
2019-08-27 14:33:09 +01:00
// set counter => sensor53 c1 xxxx
// restart driver => sensor53 r
2022-01-20 19:19:50 +00:00
// meter number for monitoring serial activity => sensor53 m1, m2, m3 ... or m0 for all (default)
// LED-GPIO for monitoring serial activity => sensor53 l2, l13, l15 ... or l255 for turn off (default)
2019-08-27 14:33:09 +01:00
bool XSNS_53_cmd ( void ) {
bool serviced = true ;
if ( XdrvMailbox . data_len > 0 ) {
2022-09-08 13:35:04 +01:00
char * cp = XdrvMailbox . data ;
if ( * cp = = ' d ' ) {
2019-08-27 14:33:09 +01:00
// set dump mode
2023-01-30 14:03:46 +00:00
if ( sml_globs . ready ) {
cp + + ;
uint8_t index = atoi ( cp ) ;
if ( ( index & 7 ) > sml_globs . meters_used ) index = 1 ;
if ( index > 0 & & sml_globs . mp [ ( index & 7 ) - 1 ] . type = = ' c ' ) {
index = 0 ;
}
if ( sml_globs . log_data ) {
free ( sml_globs . log_data ) ;
sml_globs . log_data = 0 ;
}
2022-12-30 07:41:54 +00:00
2023-01-30 14:03:46 +00:00
if ( index > 0 ) {
sml_globs . log_data = ( char * ) calloc ( sml_globs . logsize , sizeof ( char ) ) ;
}
sml_globs . dump2log = index ;
ResponseTime_P ( PSTR ( " , \" SML \" :{ \" CMD \" : \" dump: %d \" }} " ) , sml_globs . dump2log ) ;
2022-12-30 07:41:54 +00:00
}
2022-09-08 13:35:04 +01:00
} else if ( * cp = = ' c ' ) {
2022-01-20 19:19:50 +00:00
// set counter
2019-08-27 14:33:09 +01:00
cp + + ;
2022-09-08 13:35:04 +01:00
uint8_t index = * cp & 7 ;
if ( index < 1 | | index > MAX_COUNTERS ) index = 1 ;
2019-08-27 14:33:09 +01:00
cp + + ;
2022-09-08 13:35:04 +01:00
while ( * cp = = ' ' ) cp + + ;
2019-08-27 14:33:09 +01:00
if ( isdigit ( * cp ) ) {
2022-09-08 13:35:04 +01:00
uint32_t cval = atoi ( cp ) ;
2019-08-27 14:33:09 +01:00
while ( isdigit ( * cp ) ) cp + + ;
2022-09-08 13:35:04 +01:00
RtcSettings . pulse_counter [ index - 1 ] = cval ;
uint8_t cindex = 0 ;
2022-12-30 07:41:54 +00:00
for ( uint8_t meters = 0 ; meters < sml_globs . meters_used ; meters + + ) {
if ( sml_globs . mp [ meters ] . type = = ' c ' ) {
2023-01-07 10:32:26 +00:00
InjektCounterValue ( meters , RtcSettings . pulse_counter [ cindex ] , 0.0 ) ;
2019-08-27 14:33:09 +01:00
cindex + + ;
}
}
}
2023-01-07 10:32:26 +00:00
ResponseTime_P ( PSTR ( " , \" SML \" :{ \" CMD \" : \" counter%d: %d \" }} " ) , index , RtcSettings . pulse_counter [ index - 1 ] ) ;
2023-01-30 14:03:46 +00:00
} else if ( * cp = = ' r ' ) {
2019-08-27 14:33:09 +01:00
// restart
2019-09-04 17:06:34 +01:00
ResponseTime_P ( PSTR ( " , \" SML \" :{ \" CMD \" : \" restart \" }} " ) ) ;
2019-08-27 14:33:09 +01:00
SML_CounterSaveState ( ) ;
SML_Init ( ) ;
2023-01-30 14:03:46 +00:00
} else if ( * cp = = ' m ' ) {
2022-01-20 19:19:50 +00:00
// meter number for serial activity
cp + + ;
if ( ! isdigit ( * cp ) ) {
2023-01-30 14:03:46 +00:00
ResponseTime_P ( PSTR ( " , \" SML \" :{ \" CMD \" : \" sml_globs.ser_act_meter_num: %d \" }} " ) , sml_globs . ser_act_meter_num ) ;
2022-01-20 19:19:50 +00:00
} else {
2023-01-30 14:03:46 +00:00
sml_globs . ser_act_meter_num = atoi ( cp ) ;
ResponseTime_P ( PSTR ( " , \" SML \" :{ \" CMD \" : \" sml_globs.ser_act_meter_num: %d \" }} " ) , sml_globs . ser_act_meter_num ) ;
2022-01-20 19:19:50 +00:00
}
2023-01-30 14:03:46 +00:00
} else if ( * cp = = ' l ' ) {
2022-01-20 19:19:50 +00:00
// serial activity LED-GPIO
cp + + ;
if ( ! isdigit ( * cp ) ) {
2023-01-30 14:03:46 +00:00
ResponseTime_P ( PSTR ( " , \" SML \" :{ \" CMD \" : \" sml_globs.ser_act_LED_pin: %d \" }} " ) , sml_globs . ser_act_LED_pin ) ;
2022-01-20 19:19:50 +00:00
} else {
2023-01-30 14:03:46 +00:00
sml_globs . ser_act_LED_pin = atoi ( cp ) ;
2022-12-30 07:41:54 +00:00
if ( Gpio_used ( sml_globs . ser_act_LED_pin ) ) {
2023-01-30 14:03:46 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " SML: Error: Duplicate GPIO %d defined. Not usable for LED. " ) , sml_globs . ser_act_LED_pin ) ;
sml_globs . ser_act_LED_pin = 255 ;
2022-01-24 19:18:07 +00:00
}
2023-01-30 14:03:46 +00:00
if ( sml_globs . ser_act_LED_pin ! = 255 ) {
2022-12-30 07:41:54 +00:00
pinMode ( sml_globs . ser_act_LED_pin , OUTPUT ) ;
2022-01-20 19:19:50 +00:00
}
2023-01-30 14:03:46 +00:00
ResponseTime_P ( PSTR ( " , \" SML \" :{ \" CMD \" : \" sml_globs.ser_act_LED_pin: %d \" }} " ) , sml_globs . ser_act_LED_pin ) ;
2022-01-20 19:19:50 +00:00
}
2019-08-27 14:33:09 +01:00
} else {
2023-01-30 14:03:46 +00:00
serviced = false ;
2019-08-27 14:33:09 +01:00
}
}
return serviced ;
}
2022-09-08 13:35:04 +01:00
void InjektCounterValue ( uint8_t meter , uint32_t counter , float rate ) {
2022-02-11 22:29:30 +00:00
2022-12-30 07:41:54 +00:00
snprintf ( ( char * ) & meter_desc [ meter ] . sbuff [ 0 ] , meter_desc [ meter ] . sbsiz , " 1-0:1.8.0*255(%d) " , counter ) ;
2022-02-11 22:29:30 +00:00
SML_Decode ( meter ) ;
2023-01-07 10:32:26 +00:00
char freq [ 16 ] ;
freq [ 0 ] = 0 ;
if ( rate ) {
DOUBLE2CHAR ( rate , 4 , freq ) ;
}
snprintf ( ( char * ) & meter_desc [ meter ] . sbuff [ 0 ] , meter_desc [ meter ] . sbsiz , " 1-0:1.7.0*255(%s) " , freq ) ;
2019-08-27 14:33:09 +01:00
SML_Decode ( meter ) ;
}
void SML_CounterSaveState ( void ) {
for ( byte i = 0 ; i < MAX_COUNTERS ; i + + ) {
2021-06-11 17:14:12 +01:00
Settings - > pulse_counter [ i ] = RtcSettings . pulse_counter [ i ] ;
2019-08-27 14:33:09 +01:00
}
}
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-11-11 09:44:56 +00:00
bool Xsns53 ( uint32_t function ) {
2019-08-27 14:33:09 +01:00
bool result = false ;
switch ( function ) {
case FUNC_INIT :
SML_Init ( ) ;
break ;
case FUNC_LOOP :
2022-12-30 07:41:54 +00:00
if ( sml_globs . ready ) {
SML_Counter_Poll ( ) ;
if ( sml_globs . dump2log ) {
dump2log ( ) ;
} else {
SML_Poll ( ) ;
}
2021-12-15 13:46:05 +00:00
}
2019-08-27 14:33:09 +01:00
break ;
2019-09-16 18:58:22 +01:00
case FUNC_EVERY_100_MSECOND :
2021-06-11 17:14:12 +01:00
if ( bitRead ( Settings - > rule_enabled , 0 ) ) {
2022-12-30 07:41:54 +00:00
if ( sml_globs . ready ) {
SML_Check_Send ( ) ;
}
2020-05-15 14:30:32 +01:00
}
2019-08-27 14:33:09 +01:00
break ;
2023-01-07 10:32:26 +00:00
case FUNC_EVERY_SECOND :
if ( bitRead ( Settings - > rule_enabled , 0 ) ) {
if ( sml_globs . ready ) {
SML_Counter_Poll_1s ( ) ;
}
}
break ;
2019-08-27 14:33:09 +01:00
case FUNC_JSON_APPEND :
2022-12-30 07:41:54 +00:00
if ( sml_globs . ready ) {
if ( sml_options & SML_OPTIONS_JSON_ENABLE ) {
SML_Show ( 1 ) ;
}
2021-02-20 16:32:01 +00:00
}
2019-08-27 14:33:09 +01:00
break ;
# ifdef USE_WEBSERVER
case FUNC_WEB_SENSOR :
2022-12-30 07:41:54 +00:00
if ( sml_globs . ready ) {
SML_Show ( 0 ) ;
}
2019-08-27 14:33:09 +01:00
break ;
# endif // USE_WEBSERVER
case FUNC_COMMAND_SENSOR :
if ( XSNS_53 = = XdrvMailbox . index ) {
result = XSNS_53_cmd ( ) ;
}
break ;
case FUNC_SAVE_BEFORE_RESTART :
case FUNC_SAVE_AT_MIDNIGHT :
2022-12-30 07:41:54 +00:00
if ( sml_globs . ready ) {
SML_CounterSaveState ( ) ;
}
2019-08-27 14:33:09 +01:00
break ;
}
return result ;
}
# endif // USE_SML