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
2019-08-31 10:32:57 +01:00
// default baudrate of D0 output
2019-08-27 14:33:09 +01:00
# define SML_BAUDRATE 9600
2019-08-31 10:32:57 +01:00
// send this every N seconds (for meters that only send data on demand)
2019-09-16 18:58:22 +01:00
// not longer supported, use scripting instead
2019-08-27 14:33:09 +01:00
//#define SML_SEND_SEQ
// debug counter input to led for counter1 and 2
//#define DEBUG_CNT_LED1 2
//#define DEBUG_CNT_LED1 2
2019-09-16 18:58:22 +01:00
// use analog optical counter sensor with AD Converter ADS1115 (not yet functional)
2019-08-27 14:33:09 +01:00
//#define ANALOG_OPTO_SENSOR
2019-08-31 10:32:57 +01:00
// fototransistor with pullup at A0, A1 of ADS1115 A3 and +3.3V
// level and amplification are automatically set
2019-08-27 14:33:09 +01:00
# include <TasmotaSerial.h>
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
2021-02-22 10:10:49 +00:00
# ifndef TMSBSIZ
2020-06-01 14:17:32 +01:00
# define TMSBSIZ 256
2021-02-22 10:10:49 +00:00
# endif
2020-06-01 14:17:32 +01:00
2022-09-19 06:23:31 +01:00
//#define MODBUS_DEBUG
2022-09-16 11:27:16 +01:00
2019-08-31 10:32:57 +01:00
// addresses a bug in meter DWS74
2019-08-27 14:33:09 +01:00
//#define DWS74_BUG
// JSON Strings do not translate
// max 23 char
# define DJ_TPWRIN "Total_in"
2021-04-28 06:57:05 +01:00
# define DJ_TPWRIN0 "Total_in_0"
# define DJ_TPWRIN1 "Total_in_1"
2019-08-27 14:33:09 +01:00
# define DJ_TPWROUT "Total_out"
# define DJ_TPWRCURR "Power_curr"
# define DJ_TPWRCURR1 "Power_p1"
# define DJ_TPWRCURR2 "Power_p2"
# define DJ_TPWRCURR3 "Power_p3"
# define DJ_CURR1 "Curr_p1"
# define DJ_CURR2 "Curr_p2"
# define DJ_CURR3 "Curr_p3"
# define DJ_VOLT1 "Volt_p1"
# define DJ_VOLT2 "Volt_p2"
# define DJ_VOLT3 "Volt_p3"
# define DJ_METERNR "Meter_number"
# define DJ_METERSID "Meter_id"
# define DJ_CSUM "Curr_summ"
# define DJ_VAVG "Volt_avg"
# define DJ_COUNTER "Count"
2022-12-04 06:47:21 +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 ;
2022-07-20 13:25:40 +01:00
2019-08-27 14:33:09 +01:00
struct METER_DESC {
2021-12-15 13:46:05 +00:00
int8_t srcpin ;
2019-08-27 14:33:09 +01:00
uint8_t type ;
uint16_t flag ;
int32_t params ;
char prefix [ 8 ] ;
2019-09-03 09:39:51 +01:00
int8_t trxpin ;
uint8_t tsecs ;
char * txmem ;
uint8_t index ;
uint8_t max_index ;
2022-02-11 07:01:07 +00:00
char * script_str ;
2020-11-15 15:09:05 +00:00
uint8_t sopt ;
2022-12-04 06:47:21 +00:00
TRX_EN_TYPE trx_en ;
2022-07-20 13:25:40 +01:00
# 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
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
# ifdef USE_SCRIPT
struct METER_DESC script_meter_desc [ MAX_METERS ] ;
uint8_t * script_meter ;
# endif
2019-09-16 18:58:22 +01:00
// this descriptor method is no longer supported
// but still functional for simple meters
// use scripting method instead
2019-08-27 14:33:09 +01:00
// meter list , enter new meters here
//=====================================================
# define EHZ161_0 1
# define EHZ161_1 2
# define EHZ363 3
# define EHZH 4
# define EDL300 5
# define Q3B 6
# define COMBO3 7
# define COMBO2 8
# define COMBO3a 9
# define Q3B_V1 10
# define EHZ363_2 11
# define COMBO3b 12
# define WGS_COMBO 13
# define EBZD_G 14
2020-11-02 11:24:39 +00:00
# define SML_NO_OP 15
2021-04-28 06:57:05 +01:00
# define Q3C 16
2019-08-27 14:33:09 +01:00
// select this meter
2020-11-02 11:24:39 +00:00
// SML_NO_OP ignores hardcoded interface
# define METER SML_NO_OP
//#define METER EHZ161_1
# if METER==SML_NO_OP
# undef METERS_USED
# define METERS_USED 0
2020-11-13 23:35:21 +00:00
struct METER_DESC const meter_desc [ ] = { } ;
const uint8_t meter [ ] = " " ;
2020-11-02 11:24:39 +00:00
# endif
2019-08-27 14:33:09 +01:00
# if METER==EHZ161_0
# undef METERS_USED
# define METERS_USED 1
struct METER_DESC const meter_desc [ METERS_USED ] = {
2019-09-03 09:39:51 +01:00
[ 0 ] = { 3 , ' o ' , 0 , SML_BAUDRATE , " OBIS " , - 1 , 1 , 0 } } ;
2019-08-27 14:33:09 +01:00
const uint8_t meter [ ] =
2020-03-15 12:05:47 +00:00
" 1,1-0:1.8.0*255(@1, " D_TPWRIN " ,kWh, " DJ_TPWRIN " ,4| "
" 1,1-0:2.8.0*255(@1, " D_TPWROUT " ,kWh, " DJ_TPWROUT " ,4| "
2019-08-27 14:33:09 +01:00
" 1,1-0:21.7.0*255(@1, " D_TPWRCURR1 " ,W, " DJ_TPWRCURR1 " ,0| "
" 1,1-0:41.7.0*255(@1, " D_TPWRCURR2 " ,W, " DJ_TPWRCURR2 " ,0| "
" 1,1-0:61.7.0*255(@1, " D_TPWRCURR3 " ,W, " DJ_TPWRCURR3 " ,0| "
" 1,=m 3+4+5 @1, " D_TPWRCURR " ,W, " DJ_TPWRCURR " ,0| "
" 1,1-0:0.0.0*255(@#), " D_METERNR " ,, " DJ_METERNR " ,0 " ;
# endif
//=====================================================
# if METER==EHZ161_1
# undef METERS_USED
# define METERS_USED 1
struct METER_DESC const meter_desc [ METERS_USED ] = {
2019-09-03 09:39:51 +01:00
[ 0 ] = { 3 , ' o ' , 0 , SML_BAUDRATE , " OBIS " , - 1 , 1 , 0 } } ;
2019-08-27 14:33:09 +01:00
const uint8_t meter [ ] =
2020-03-15 12:05:47 +00:00
" 1,1-0:1.8.1*255(@1, " D_TPWRIN " ,kWh, " DJ_TPWRIN " ,4| "
" 1,1-0:2.8.1*255(@1, " D_TPWROUT " ,kWh, " DJ_TPWROUT " ,4| "
2019-08-27 14:33:09 +01:00
" 1,=d 2 10 @1, " D_TPWRCURR " ,W, " DJ_TPWRCURR " ,0| "
" 1,1-0:0.0.0*255(@#), " D_METERNR " ,, " DJ_METERNR " ,0 " ;
# endif
//=====================================================
# if METER==EHZ363
# undef METERS_USED
# define METERS_USED 1
struct METER_DESC const meter_desc [ METERS_USED ] = {
2019-09-03 09:39:51 +01:00
[ 0 ] = { 3 , ' s ' , 0 , SML_BAUDRATE , " SML " , - 1 , 1 , 0 } } ;
2019-08-27 14:33:09 +01:00
// 2 Richtungszähler EHZ SML 8 bit 9600 baud, binär
const uint8_t meter [ ] =
//0x77,0x07,0x01,0x00,0x01,0x08,0x00,0xff
2020-03-15 12:05:47 +00:00
" 1,77070100010800ff@1000, " D_TPWRIN " ,kWh, " DJ_TPWRIN " ,4| "
2019-08-27 14:33:09 +01:00
//0x77,0x07,0x01,0x00,0x02,0x08,0x00,0xff
2020-03-15 12:05:47 +00:00
" 1,77070100020800ff@1000, " D_TPWROUT " ,kWh, " DJ_TPWROUT " ,4| "
2019-08-27 14:33:09 +01:00
//0x77,0x07,0x01,0x00,0x10,0x07,0x00,0xff
" 1,77070100100700ff@1, " D_TPWRCURR " ,W, " DJ_TPWRCURR " ,0| "
//0x77,0x07,0x01,0x00,0x00,0x00,0x09,0xff
" 1,77070100000009ff@#, " D_METERNR " ,, " DJ_METERNR " ,0 " ;
# endif
//=====================================================
# if METER==EHZH
# undef METERS_USED
# define METERS_USED 1
struct METER_DESC const meter_desc [ METERS_USED ] = {
2019-09-03 09:39:51 +01:00
[ 0 ] = { 3 , ' s ' , 0 , SML_BAUDRATE , " SML " , - 1 , 1 , 0 } } ;
2019-08-27 14:33:09 +01:00
// 2 Richtungszähler EHZ SML 8 bit 9600 baud, binär
// verbrauch total
const uint8_t meter [ ] =
//0x77,0x07,0x01,0x00,0x01,0x08,0x00,0xff
2020-03-15 12:05:47 +00:00
" 1,77070100010800ff@1000, " D_TPWRIN " ,kWh, " DJ_TPWRIN " ,4| "
2019-08-27 14:33:09 +01:00
//0x77,0x07,0x01,0x00,0x01,0x08,0x01,0xff
2020-03-15 12:05:47 +00:00
" 1,77070100020800ff@1000, " D_TPWROUT " ,kWh, " DJ_TPWROUT " ,4| "
2019-08-27 14:33:09 +01:00
//0x77,0x07,0x01,0x00,0x0f,0x07,0x00,0xff
" 1,770701000f0700ff@1, " D_TPWRCURR " ,W, " DJ_TPWRCURR " ,0 " ;
# endif
//=====================================================
# if METER==EDL300
# undef METERS_USED
# define METERS_USED 1
struct METER_DESC const meter_desc [ METERS_USED ] = {
2019-09-03 09:39:51 +01:00
[ 0 ] = { 3 , ' s ' , 0 , SML_BAUDRATE , " SML " , - 1 , 1 , 0 } } ;
2019-08-27 14:33:09 +01:00
// 2 Richtungszähler EHZ SML 8 bit 9600 baud, binär
// verbrauch total
const uint8_t meter [ ] =
//0x77,0x07,0x01,0x00,0x01,0x08,0x00,0xff
2020-03-15 12:05:47 +00:00
" 1,77070100010800ff@1000, " D_TPWRIN " ,kWh, " DJ_TPWRIN " ,4| "
2019-08-27 14:33:09 +01:00
//0x77,0x07,0x01,0x00,0x01,0x08,0x01,0xff
2020-03-15 12:05:47 +00:00
" 1,77070100020801ff@1000, " D_TPWROUT " ,kWh, " DJ_TPWROUT " ,4| "
2019-08-27 14:33:09 +01:00
//0x77,0x07,0x01,0x00,0x0f,0x07,0x00,0xff
" 1,770701000f0700ff@1, " D_TPWRCURR " ,W, " DJ_TPWRCURR " ,0 " ;
# endif
# if METER==EBZD_G
# undef METERS_USED
# define METERS_USED 1
struct METER_DESC const meter_desc [ METERS_USED ] = {
2019-09-03 09:39:51 +01:00
[ 0 ] = { 3 , ' s ' , 0 , SML_BAUDRATE , " strom " , - 1 , 1 , 0 } } ;
2019-08-27 14:33:09 +01:00
const uint8_t meter [ ] =
//0x77,0x07,0x01,0x00,0x01,0x08,0x00,0xff
2020-03-15 12:05:47 +00:00
" 1,77070100010800ff@1000, " D_TPWRIN " ,kWh, " DJ_TPWRIN " ,4| "
2019-08-27 14:33:09 +01:00
// ..
2020-03-15 12:05:47 +00:00
" 1,77070100020800ff@1000, " D_TPWROUT " ,kWh, " DJ_TPWROUT " ,4| "
2019-08-27 14:33:09 +01:00
//0x77,0x07,0x01,0x00,0x01,0x08,0x01,0xff
2020-03-15 12:05:47 +00:00
" 1,77070100010801ff@1000, " D_TPWRCURR1 " ,kWh, " DJ_TPWRCURR1 " ,4| "
2019-08-27 14:33:09 +01:00
//0x77,0x07,0x01,0x00,0x01,0x08,0x02,0xff
2020-03-15 12:05:47 +00:00
" 1,77070100010802ff@1000, " D_TPWRCURR2 " ,kWh, " DJ_TPWRCURR2 " ,4| "
2019-08-27 14:33:09 +01:00
// 77 07 01 00 10 07 00 FF
" 1,77070100100700ff@1, " D_TPWRCURR " ,W, " DJ_TPWRCURR " ,0| "
// ..
" 1,77070100600100ff@#, " D_METERNR " ,, " DJ_METERNR " ,0 " ;
# endif
//=====================================================
# if METER==Q3B
# undef METERS_USED
# define METERS_USED 1
struct METER_DESC const meter_desc [ METERS_USED ] = {
2019-09-03 09:39:51 +01:00
[ 0 ] = { 3 , ' s ' , 0 , SML_BAUDRATE , " SML " , - 1 , 1 , 0 } } ;
2019-08-27 14:33:09 +01:00
const uint8_t meter [ ] =
//0x77,0x07,0x01,0x00,0x01,0x08,0x01,0xff
2020-03-15 12:05:47 +00:00
" 1,77070100010800ff@1000, " D_TPWRIN " ,kWh, " DJ_TPWRIN " ,4| "
2019-08-27 14:33:09 +01:00
//0x77,0x07,0x01,0x00,0x02,0x08,0x01,0xff
2020-03-15 12:05:47 +00:00
" 1,77070100020801ff@1000, " D_TPWROUT " ,kWh, " DJ_TPWROUT " ,4| "
2019-08-27 14:33:09 +01:00
//0x77,0x07,0x01,0x00,0x01,0x07,0x00,0xff
" 1,77070100010700ff@1, " D_TPWRCURR " ,W, " DJ_TPWRCURR " ,0 " ;
# endif
# if METER==COMBO3
// 3 Zähler Beispiel
# undef METERS_USED
# define METERS_USED 3
struct METER_DESC const meter_desc [ METERS_USED ] = {
2019-09-03 09:39:51 +01:00
[ 0 ] = { 3 , ' o ' , 0 , SML_BAUDRATE , " OBIS " , - 1 , 1 , 0 } , // harware serial RX pin
[ 1 ] = { 14 , ' s ' , 0 , SML_BAUDRATE , " SML " , - 1 , 1 , 0 } , // GPIO14 software serial
[ 2 ] = { 4 , ' o ' , 0 , SML_BAUDRATE , " OBIS2 " , - 1 , 1 , 0 } } ; // GPIO4 software serial
2019-08-27 14:33:09 +01:00
// 3 Zähler definiert
const uint8_t meter [ ] =
2020-03-15 12:05:47 +00:00
" 1,1-0:1.8.0*255(@1, " D_TPWRIN " ,kWh, " DJ_TPWRIN " ,4| "
" 1,1-0:2.8.0*255(@1, " D_TPWROUT " ,kWh, " DJ_TPWROUT " ,4| "
2019-08-27 14:33:09 +01:00
" 1,1-0:21.7.0*255(@1, " D_TPWRCURR1 " ,W, " DJ_TPWRCURR1 " ,0| "
" 1,1-0:41.7.0*255(@1, " D_TPWRCURR2 " ,W, " DJ_TPWRCURR2 " ,0| "
" 1,1-0:61.7.0*255(@1, " D_TPWRCURR3 " ,W, " DJ_TPWRCURR3 " ,0| "
" 1,=m 3+4+5 @1, " D_TPWRCURR " ,W, " DJ_TPWRCURR " ,0| "
" 1,1-0:0.0.0*255(@#), " D_METERNR " ,, " DJ_METERNR " ,0| "
2020-03-15 12:05:47 +00:00
" 2,77070100010800ff@1000, " D_TPWRIN " ,kWh, " DJ_TPWRIN " ,4| "
" 2,77070100020800ff@1000, " D_TPWROUT " ,kWh, " DJ_TPWROUT " ,4| "
2019-08-27 14:33:09 +01:00
" 2,77070100100700ff@1, " D_TPWRCURR " ,W, " DJ_TPWRCURR " ,0| "
2020-03-15 12:05:47 +00:00
" 3,1-0:1.8.1*255(@1, " D_TPWRIN " ,kWh, " DJ_TPWRIN " ,4| "
" 3,1-0:2.8.1*255(@1, " D_TPWROUT " ,kWh, " DJ_TPWROUT " ,4| "
2019-08-27 14:33:09 +01:00
" 3,=d 2 10 @1, " D_TPWRCURR " ,W, " DJ_TPWRCURR " ,0| "
" 3,1-0:0.0.0*255(@#), " D_METERNR " ,, " DJ_METERNR " ,0 " ;
# endif
# if METER==COMBO2
// 2 Zähler Beispiel
# undef METERS_USED
# define METERS_USED 2
struct METER_DESC const meter_desc [ METERS_USED ] = {
2019-09-03 09:39:51 +01:00
[ 0 ] = { 3 , ' o ' , 0 , SML_BAUDRATE , " OBIS1 " , - 1 , 1 , 0 } , // harware serial RX pin
[ 1 ] = { 14 , ' o ' , 0 , SML_BAUDRATE , " OBIS2 " , - 1 , 1 , 0 } } ; // GPIO14 software serial
2019-08-27 14:33:09 +01:00
// 2 Zähler definiert
const uint8_t meter [ ] =
2020-03-15 12:05:47 +00:00
" 1,1-0:1.8.1*255(@1, " D_TPWRIN " ,kWh, " DJ_TPWRIN " ,4| "
" 1,1-0:2.8.1*255(@1, " D_TPWROUT " ,kWh, " DJ_TPWROUT " ,4| "
2019-08-27 14:33:09 +01:00
" 1,=d 2 10 @1, " D_TPWRCURR " ,W, " DJ_TPWRCURR " ,0| "
" 1,1-0:0.0.0*255(@#), " D_METERNR " ,, " DJ_METERNR " ,0| "
2020-03-15 12:05:47 +00:00
" 2,1-0:1.8.1*255(@1, " D_TPWRIN " ,kWh, " DJ_TPWRIN " ,4| "
" 2,1-0:2.8.1*255(@1, " D_TPWROUT " ,kWh, " DJ_TPWROUT " ,4| "
2019-08-27 14:33:09 +01:00
" 2,=d 6 10 @1, " D_TPWRCURR " ,W, " DJ_TPWRCURR " ,0| "
" 2,1-0:0.0.0*255(@#), " D_METERNR " ,, " DJ_METERNR " ,0 " ;
# endif
# if METER==COMBO3a
# undef METERS_USED
# define METERS_USED 3
struct METER_DESC const meter_desc [ METERS_USED ] = {
2019-09-03 09:39:51 +01:00
[ 0 ] = { 3 , ' o ' , 0 , SML_BAUDRATE , " OBIS1 " , - 1 , 1 , 0 } , // harware serial RX pin
[ 1 ] = { 14 , ' o ' , 0 , SML_BAUDRATE , " OBIS2 " , - 1 , 1 , 0 } ,
[ 2 ] = { 1 , ' o ' , 0 , SML_BAUDRATE , " OBIS3 " , - 1 , 1 , 0 } } ;
2019-08-27 14:33:09 +01:00
// 3 Zähler definiert
const uint8_t meter [ ] =
" 1,=h --- Zähler Nr 1 ---| "
2020-03-15 12:05:47 +00:00
" 1,1-0:1.8.1*255(@1, " D_TPWRIN " ,kWh, " DJ_TPWRIN " ,4| "
" 1,1-0:2.8.1*255(@1, " D_TPWROUT " ,kWh, " DJ_TPWROUT " ,4| "
2019-08-27 14:33:09 +01:00
" 1,=d 2 10 @1, " D_TPWRCURR " ,W, " DJ_TPWRCURR " ,0| "
" 1,1-0:0.0.0*255(@#), " D_METERNR " ,, " DJ_METERNR " ,0| "
" 2,=h --- Zähler Nr 2 ---| "
2020-03-15 12:05:47 +00:00
" 2,1-0:1.8.1*255(@1, " D_TPWRIN " ,kWh, " DJ_TPWRIN " ,4| "
" 2,1-0:2.8.1*255(@1, " D_TPWROUT " ,kWh, " DJ_TPWROUT " ,4| "
2019-08-27 14:33:09 +01:00
" 2,=d 6 10 @1, " D_TPWRCURR " ,W, " DJ_TPWRCURR " ,0| "
" 2,1-0:0.0.0*255(@#), " D_METERNR " ,, " DJ_METERNR " ,0| "
" 3,=h --- Zähler Nr 3 ---| "
2020-03-15 12:05:47 +00:00
" 3,1-0:1.8.1*255(@1, " D_TPWRIN " ,kWh, " DJ_TPWRIN " ,4| "
" 3,1-0:2.8.1*255(@1, " D_TPWROUT " ,kWh, " DJ_TPWROUT " ,4| "
2019-08-27 14:33:09 +01:00
" 3,=d 10 10 @1, " D_TPWRCURR " ,W, " DJ_TPWRCURR " ,0| "
" 3,1-0:0.0.0*255(@#), " D_METERNR " ,, " DJ_METERNR " ,0 " ;
# endif
//=====================================================
# if METER==Q3B_V1
# undef METERS_USED
# define METERS_USED 1
struct METER_DESC const meter_desc [ METERS_USED ] = {
2019-09-03 09:39:51 +01:00
[ 0 ] = { 3 , ' o ' , 0 , SML_BAUDRATE , " OBIS " , - 1 , 1 , 0 } } ;
2019-08-27 14:33:09 +01:00
const uint8_t meter [ ] =
2020-03-15 12:05:47 +00:00
" 1,1-0:1.8.1*255(@1, " D_TPWRIN " ,kWh, " DJ_TPWRIN " ,4| "
2019-08-27 14:33:09 +01:00
" 1,=d 1 10 @1, " D_TPWRCURR " ,W, " DJ_TPWRCURR " ,0| "
" 1,1-0:0.0.0*255(@#), " D_METERNR " ,, " DJ_METERNR " ,0 " ;
# endif
//=====================================================
# if METER==EHZ363_2
# undef METERS_USED
# define METERS_USED 1
struct METER_DESC const meter_desc [ METERS_USED ] = {
2019-09-03 09:39:51 +01:00
[ 0 ] = { 3 , ' s ' , 0 , SML_BAUDRATE , " SML " , - 1 , 1 , 0 } } ;
2019-09-16 18:58:22 +01:00
// 2 direction meter EHZ SML 8 bit 9600 baud, binary
2019-08-27 14:33:09 +01:00
const uint8_t meter [ ] =
//0x77,0x07,0x01,0x00,0x01,0x08,0x00,0xff
2020-03-15 12:05:47 +00:00
" 1,77070100010800ff@1000, " D_TPWRIN " ,kWh, " DJ_TPWRIN " ,4| "
2019-08-27 14:33:09 +01:00
//0x77,0x07,0x01,0x00,0x02,0x08,0x00,0xff
2020-03-15 12:05:47 +00:00
" 1,77070100020800ff@1000, " D_TPWROUT " ,kWh, " DJ_TPWROUT " ,4| "
2019-08-27 14:33:09 +01:00
//0x77,0x07,0x01,0x00,0x01,0x08,0x01,0xff
2020-03-15 12:05:47 +00:00
" 1,77070100010801ff@1000, " D_TPWRCURR1 " ,kWh, " DJ_TPWRCURR1 " ,4| "
2019-08-27 14:33:09 +01:00
//0x77,0x07,0x01,0x00,0x01,0x08,0x02,0xff
2020-03-15 12:05:47 +00:00
" 1,77070100010802ff@1000, " D_TPWRCURR2 " ,kWh, " DJ_TPWRCURR2 " ,4| "
2019-08-27 14:33:09 +01:00
//0x77,0x07,0x01,0x00,0x10,0x07,0x00,0xff
" 1,77070100100700ff@1, " D_TPWRCURR " ,W, " DJ_TPWRCURR " ,0| "
//0x77,0x07,0x01,0x00,0x00,0x00,0x09,0xff
" 1,77070100000009ff@#, " D_METERNR " ,, " DJ_METERNR " ,0 " ;
# endif
2019-09-16 18:58:22 +01:00
// example OBIS power meter + gas and water counter
2019-08-27 14:33:09 +01:00
# if METER==COMBO3b
# undef METERS_USED
# define METERS_USED 3
struct METER_DESC const meter_desc [ METERS_USED ] = {
2019-09-03 09:39:51 +01:00
[ 0 ] = { 3 , ' o ' , 0 , SML_BAUDRATE , " OBIS " , - 1 , 1 , 0 } , // harware serial RX pin
2019-08-27 14:33:09 +01:00
[ 1 ] = { 14 , ' c ' , 0 , 50 , " Gas " } , // GPIO14 gas counter
[ 2 ] = { 1 , ' c ' , 0 , 10 , " Wasser " } } ; // water counter
2019-09-16 18:58:22 +01:00
// 3 meters defined
2019-08-27 14:33:09 +01:00
const uint8_t meter [ ] =
2020-03-15 12:05:47 +00:00
" 1,1-0:1.8.1*255(@1, " D_TPWRIN " ,kWh, " DJ_TPWRIN " ,4| "
" 1,1-0:2.8.1*255(@1, " D_TPWROUT " ,kWh, " DJ_TPWROUT " ,4| "
2019-08-27 14:33:09 +01:00
" 1,=d 2 10 @1, " D_TPWRCURR " ,W, " DJ_TPWRCURR " ,0| "
" 1,1-0:0.0.0*255(@#), " D_METERNR " ,, " DJ_METERNR " ,0| "
2019-09-16 18:58:22 +01:00
// with counters the comparison string must be exactly this string
2019-08-27 14:33:09 +01:00
" 2,1-0:1.8.0*255(@100, " D_GasIN " ,cbm, " DJ_COUNTER " ,2| "
" 3,1-0:1.8.0*255(@100, " D_H2oIN " ,cbm, " DJ_COUNTER " ,2 " ;
# endif
# if METER==WGS_COMBO
# undef METERS_USED
# define METERS_USED 3
struct METER_DESC const meter_desc [ METERS_USED ] = {
2019-09-16 18:58:22 +01:00
[ 0 ] = { 1 , ' c ' , 0 , 10 , " H20 " , - 1 , 1 , 0 } , // GPIO1 water counter
[ 1 ] = { 4 , ' c ' , 0 , 50 , " GAS " , - 1 , 1 , 0 } , // GPIO4 gas counter
2019-09-03 09:39:51 +01:00
[ 2 ] = { 3 , ' s ' , 0 , SML_BAUDRATE , " SML " , - 1 , 1 , 0 } } ; // SML harware serial RX pin
2019-08-27 14:33:09 +01:00
const uint8_t meter [ ] =
//----------------------------Wasserzähler--sensor53 c1------------------------------------
//"1,=h==================|"
" 1,1-0:1.8.0*255(@10000, " D_H2oIN " ,cbm, " DJ_COUNTER " ,4| " // 1
//----------------------------Gaszähler-----sensor53 c2------------------------------------
// bei gaszählern (countern) muss der Vergleichsstring so aussehen wie hier
" 2,=h==================| "
" 2,1-0:1.8.0*255(@100, " D_GasIN " ,cbm, " DJ_COUNTER " ,3| " // 2
//----------------------------Stromzähler-EHZ363W5--sensor53 d0----------------------------
" 3,=h==================| "
//0x77,0x07,0x01,0x00,0x01,0x08,0x00,0xff
2020-03-15 12:05:47 +00:00
" 3,77070100010800ff@1000, " D_TPWRIN " ,kWh, " DJ_TPWRIN " ,3| " // 3 Zählerstand Total
2019-08-27 14:33:09 +01:00
" 3,=h==================| "
//0x77,0x07,0x01,0x00,0x10,0x07,0x00,0xff
" 3,77070100100700ff@1, " D_TPWRCURR " ,W, " DJ_TPWRCURR " ,2| " // 4 Aktuelle Leistung
" 3,=h -------------------------------| "
" 3,=m 10+11+12 @100, " D_StL1L2L3 " ,A, " DJ_CSUM " ,2| " // 5 Summe Aktuelle Ströme
//"3,=h -------------------------------|"
" 3,=m 13+14+15/#3 @100, " D_SpL1L2L3 " ,V, " DJ_VAVG " ,2| " // 6 Mittelwert Spannungen
" 3,=h==================| "
//0x77,0x07,0x01,0x00,0x24,0x07,0x00,0xff
" 3,77070100240700ff@1, " D_TPWRCURR1 " ,W, " DJ_TPWRCURR1 " ,2| " // 7 Wirkleistung L1
//0x77,0x07,0x01,0x00,0x38,0x07,0x00,0xff
" 3,77070100380700ff@1, " D_TPWRCURR2 " ,W, " DJ_TPWRCURR2 " ,2| " // 8 Wirkleistung L2
//0x77,0x07,0x01,0x00,0x4c,0x07,0x00,0xff
" 3,770701004c0700ff@1, " D_TPWRCURR3 " ,W, " DJ_TPWRCURR3 " ,2| " // 9 Wirkleistung L3
" 3,=h -------------------------------| "
//0x77,0x07,0x01,0x00,0x1f,0x07,0x00,0xff
" 3,770701001f0700ff@100, " D_Strom_L1 " ,A, " DJ_CURR1 " ,2| " // 10 Strom L1
//0x77,0x07,0x01,0x00,0x33,0x07,0x00,0xff
" 3,77070100330700ff@100, " D_Strom_L2 " ,A, " DJ_CURR2 " ,2| " // 11 Strom L2
//0x77,0x07,0x01,0x00,0x47,0x07,0x00,0xff
" 3,77070100470700ff@100, " D_Strom_L3 " ,A, " DJ_CURR3 " ,2| " // 12 Strom L3
" 3,=h -------------------------------| "
//0x77,0x07,0x01,0x00,0x20,0x07,0x00,0xff
" 3,77070100200700ff@100, " D_Spannung_L1 " ,V, " DJ_VOLT1 " ,2| " // 13 Spannung L1
//0x77,0x07,0x01,0x00,0x34,0x07,0x00,0xff
" 3,77070100340700ff@100, " D_Spannung_L2 " ,V, " DJ_VOLT2 " ,2| " // 14 Spannung L2
//0x77,0x07,0x01,0x00,0x48,0x07,0x00,0xff
" 3,77070100480700ff@100, " D_Spannung_L3 " ,V, " DJ_VOLT3 " ,2| " // 15 Spannung L3
" 3,=h==================| "
//0x77,0x07,0x01,0x00,0x00,0x00,0x09,0xff
" 3,77070100000009ff@#, " D_METERSID " ,, " DJ_METERSID " ,0| " // 16 Service ID
" 3,=h-------------------------------- " ; // letzte Zeile
# endif
2021-04-28 06:57:05 +01:00
# if METER==Q3C
# undef METERS_USED
# define METERS_USED 1
struct METER_DESC const meter_desc [ METERS_USED ] = {
[ 0 ] = { 3 , ' s ' , 0 , SML_BAUDRATE , " SML " , - 1 , 1 , 0 } } ;
const uint8_t meter [ ] =
//0x77,0x07,0x01,0x00,0x01,0x08,0x01,0xff
" 1,77070101010800ff@1000, " D_TPWRIN0 " ,kWh, " DJ_TPWRIN0 " ,2| " // Verbrauch T0
//0x77,0x07,0x01,0x00,0x01,0x08,0x01,0xff
" 1,77070101010801ff@1000, " D_TPWRIN1 " ,kWh, " DJ_TPWRIN1 " ,2| " // Verbrauch T1
//0x77,0x07,0x01,0x00,0x01,0x07,0x00,0xff
2021-07-19 08:55:43 +01:00
" 1,77070100010700ff@1, " D_TPWRCURR " ,W, " DJ_TPWRCURR " ,0| " // Strom Gesamt
2021-04-28 06:57:05 +01:00
//0x77,0x07,0x01,0x00,0x01,0x07,0x00,0xff
" 1,77070100150700ff@1, " D_TPWRCURR1 " ,W, " DJ_TPWRCURR1 " ,0| " // Strom L1
//0x77,0x07,0x01,0x00,0x01,0x07,0x00,0xff
" 1,77070100290700ff@1, " D_TPWRCURR2 " ,W, " DJ_TPWRCURR2 " ,0| " // Strom L2
//0x77,0x07,0x01,0x00,0x01,0x07,0x00,0xff
" 1,770701003D0700ff@1, " D_TPWRCURR3 " ,W, " DJ_TPWRCURR3 " ,0 " ; // Strom L3
# endif
2019-08-27 14:33:09 +01:00
// this driver uses double because meter vars would not fit in float
//=====================================================
// 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
# ifndef SML_NO_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
// max number of vars , may be adjusted
2019-12-25 08:09:43 +00:00
# ifndef SML_MAX_VARS
# define SML_MAX_VARS 20
2019-12-05 15:34:30 +00:00
# endif
2022-07-20 13:25:40 +01:00
2019-12-25 08:09:43 +00:00
double meter_vars [ SML_MAX_VARS ] ;
2019-08-27 14:33:09 +01:00
// calulate deltas
# define MAX_DVARS MAX_METERS*2
double dvalues [ MAX_DVARS ] ;
uint32_t dtimes [ MAX_DVARS ] ;
uint8_t meters_used ;
2021-02-20 16:32:01 +00:00
uint8_t dvalid [ SML_MAX_VARS ] ;
2019-08-27 14:33:09 +01:00
struct METER_DESC const * meter_desc_p ;
const uint8_t * meter_p ;
2021-02-22 10:10:49 +00:00
uint16_t meter_spos [ MAX_METERS ] ;
2019-08-27 14:33:09 +01:00
// software serial pointers
2020-11-28 16:00:15 +00:00
# ifdef ESP8266
TasmotaSerial * meter_ss [ MAX_METERS ] ;
# endif // ESP8266
2021-12-15 13:46:05 +00:00
2019-08-27 14:33:09 +01:00
// serial buffers, may be made larger depending on telegram lenght
2020-07-15 08:44:52 +01:00
# ifndef SML_BSIZ
2019-08-27 14:33:09 +01:00
# define SML_BSIZ 48
2020-07-15 08:44:52 +01:00
# endif
2019-08-27 14:33:09 +01:00
uint8_t smltbuf [ MAX_METERS ] [ SML_BSIZ ] ;
// meter nr as string
# define METER_ID_SIZE 24
char meter_id [ MAX_METERS ] [ METER_ID_SIZE ] ;
2021-02-25 13:36:37 +00:00
# define VBUS_SYNC 0xaa
# define SML_SYNC 0x77
2019-08-27 14:33:09 +01:00
# define EBUS_SYNC 0xaa
# define EBUS_ESC 0xa9
2019-09-16 18:58:22 +01:00
uint8_t sml_send_blocks ;
uint8_t sml_100ms_cnt ;
uint8_t sml_desc_cnt ;
2022-11-28 06:41:37 +00:00
# define SML_OPTIONS_JSON_ENABLE 1
# define SML_OPTIONS_OBIS_LINE 2
uint8_t sml_options = SML_OPTIONS_JSON_ENABLE ;
2019-09-16 18:58:22 +01:00
# ifdef USE_SML_MEDIAN_FILTER
2019-08-27 14:33:09 +01:00
// median filter, should be odd size
# define MEDIAN_SIZE 5
2019-09-16 18:58:22 +01:00
struct SML_MEDIAN_FILTER {
2019-08-27 14:33:09 +01:00
double buffer [ MEDIAN_SIZE ] ;
int8_t index ;
2019-12-25 08:09:43 +00:00
} sml_mf [ SML_MAX_VARS ] ;
2019-08-27 14:33:09 +01:00
2019-10-20 07:23:27 +01:00
# ifndef FLT_MAX
# define FLT_MAX 99999999
# endif
2019-09-16 18:58:22 +01: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
// calc median
2019-09-16 18:58:22 +01:00
double sml_median ( struct SML_MEDIAN_FILTER * mf , double in ) {
//double tbuff[MEDIAN_SIZE],tmp;
//uint8_t flag;
2019-08-27 14:33:09 +01:00
mf - > buffer [ mf - > index ] = in ;
mf - > index + + ;
if ( mf - > index > = MEDIAN_SIZE ) mf - > index = 0 ;
2019-09-16 18:58:22 +01:00
return sml_median_array ( mf - > buffer , MEDIAN_SIZE ) ;
/*
2019-08-27 14:33:09 +01:00
// 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-09-16 18:58:22 +01:00
*/
2019-08-27 14:33:09 +01:00
}
# endif
2021-12-15 13:46:05 +00:00
// ESP32 software serial read only
# 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 ) ;
int32_t read ( void ) override ;
size_t write ( uint8_t byte ) override ;
int32_t available ( void ) override ;
void flush ( void ) override ;
void setRxBufferSize ( uint32_t size ) ;
void updateBaudRate ( uint32_t baud ) ;
void rxRead ( void ) ;
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 ( ) ;
} 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 ;
}
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 ;
pinMode ( m_rx_pin , INPUT ) ;
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 ] ;
}
}
int32_t SML_ESP32_SERIAL : : read ( void ) {
if ( hws ) {
return hws - > read ( ) ;
} else {
if ( m_in_pos = = m_out_pos ) return - 1 ;
uint32_t ch = m_buffer [ m_out_pos ] ;
m_out_pos = ( m_out_pos + 1 ) % serial_buffer_size ;
return ch ;
}
}
int32_t SML_ESP32_SERIAL : : available ( void ) {
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
# ifdef ESP32
# ifndef USE_ESP32_SW_SERIAL
HardwareSerial * meter_ss [ MAX_METERS ] ;
# else
SML_ESP32_SERIAL * meter_ss [ MAX_METERS ] ;
# endif
# endif // ESP32
2019-08-27 14:33:09 +01:00
# ifdef ANALOG_OPTO_SENSOR
// sensor over ADS1115 with i2c Bus
uint8_t ads1115_up ;
// ads1115 driver
# define SAMPLE_BIT (0x8000)
# define ADS1115_COMP_QUEUE_SHIFT 0
# define ADS1115_COMP_LATCH_SHIFT 2
# define ADS1115_COMP_POLARITY_SHIFT 3
# define ADS1115_COMP_MODE_SHIFT 4
# define ADS1115_DATA_RATE_SHIFT 5
# define ADS1115_MODE_SHIFT 8
# define ADS1115_PGA_SHIFT 9
# define ADS1115_MUX_SHIFT 12
enum ads1115_comp_queue {
ADS1115_COMP_QUEUE_AFTER_ONE = 0 ,
ADS1115_COMP_QUEUE_AFTER_TWO = 0x1 < < ADS1115_COMP_QUEUE_SHIFT ,
ADS1115_COMP_QUEUE_AFTER_FOUR = 0x2 < < ADS1115_COMP_QUEUE_SHIFT ,
ADS1115_COMP_QUEUE_DISABLE = 0x3 < < ADS1115_COMP_QUEUE_SHIFT ,
ADS1115_COMP_QUEUE_MASK = 0x3 < < ADS1115_COMP_QUEUE_SHIFT ,
} ;
enum ads1115_comp_latch {
ADS1115_COMP_LATCH_NO = 0 ,
ADS1115_COMP_LATCH_YES = 1 < < ADS1115_COMP_LATCH_SHIFT ,
ADS1115_COMP_LATCH_MASK = 1 < < ADS1115_COMP_LATCH_SHIFT ,
} ;
enum ads1115_comp_polarity {
ADS1115_COMP_POLARITY_ACTIVE_LOW = 0 ,
ADS1115_COMP_POLARITY_ACTIVE_HIGH = 1 < < ADS1115_COMP_POLARITY_SHIFT ,
ADS1115_COMP_POLARITY_MASK = 1 < < ADS1115_COMP_POLARITY_SHIFT ,
} ;
enum ads1115_comp_mode {
ADS1115_COMP_MODE_WINDOW = 0 ,
ADS1115_COMP_MODE_HYSTERESIS = 1 < < ADS1115_COMP_MODE_SHIFT ,
ADS1115_COMP_MODE_MASK = 1 < < ADS1115_COMP_MODE_SHIFT ,
} ;
enum ads1115_data_rate {
ADS1115_DATA_RATE_8_SPS = 0 ,
ADS1115_DATA_RATE_16_SPS = 0x1 < < ADS1115_DATA_RATE_SHIFT ,
ADS1115_DATA_RATE_32_SPS = 0x2 < < ADS1115_DATA_RATE_SHIFT ,
ADS1115_DATA_RATE_64_SPS = 0x3 < < ADS1115_DATA_RATE_SHIFT ,
ADS1115_DATA_RATE_128_SPS = 0x4 < < ADS1115_DATA_RATE_SHIFT ,
ADS1115_DATA_RATE_250_SPS = 0x5 < < ADS1115_DATA_RATE_SHIFT ,
ADS1115_DATA_RATE_475_SPS = 0x6 < < ADS1115_DATA_RATE_SHIFT ,
ADS1115_DATA_RATE_860_SPS = 0x7 < < ADS1115_DATA_RATE_SHIFT ,
ADS1115_DATA_RATE_MASK = 0x7 < < ADS1115_DATA_RATE_SHIFT ,
} ;
enum ads1115_mode {
ADS1115_MODE_CONTINUOUS = 0 ,
ADS1115_MODE_SINGLE_SHOT = 1 < < ADS1115_MODE_SHIFT ,
ADS1115_MODE_MASK = 1 < < ADS1115_MODE_SHIFT ,
} ;
enum ads1115_pga {
ADS1115_PGA_TWO_THIRDS = 0 , //±6.144 V
ADS1115_PGA_ONE = 0x1 < < ADS1115_PGA_SHIFT , //±4.096 V
ADS1115_PGA_TWO = 0x2 < < ADS1115_PGA_SHIFT , //±2.048 V
ADS1115_PGA_FOUR = 0x3 < < ADS1115_PGA_SHIFT , //±1.024 V
ADS1115_PGA_EIGHT = 0x4 < < ADS1115_PGA_SHIFT , //±0.512 V
ADS1115_PGA_SIXTEEN = 0x5 < < ADS1115_PGA_SHIFT , //±0.256 V
ADS1115_PGA_MASK = 0x7 < < ADS1115_PGA_SHIFT ,
} ;
enum ads1115_mux {
ADS1115_MUX_DIFF_AIN0_AIN1 = 0 ,
ADS1115_MUX_DIFF_AIN0_AIN3 = 0x1 < < ADS1115_MUX_SHIFT ,
ADS1115_MUX_DIFF_AIN1_AIN3 = 0x2 < < ADS1115_MUX_SHIFT ,
ADS1115_MUX_DIFF_AIN2_AIN3 = 0x3 < < ADS1115_MUX_SHIFT ,
ADS1115_MUX_GND_AIN0 = 0x4 < < ADS1115_MUX_SHIFT ,
ADS1115_MUX_GND_AIN1 = 0x5 < < ADS1115_MUX_SHIFT ,
ADS1115_MUX_GND_AIN2 = 0x6 < < ADS1115_MUX_SHIFT ,
ADS1115_MUX_GND_AIN3 = 0x7 < < ADS1115_MUX_SHIFT ,
ADS1115_MUX_MASK = 0x7 < < ADS1115_MUX_SHIFT ,
} ;
class ADS1115 {
public :
ADS1115 ( uint8_t address = 0x48 ) ;
void begin ( ) ;
uint8_t trigger_sample ( ) ;
uint8_t reset ( ) ;
bool is_sample_in_progress ( ) ;
int16_t read_sample ( ) ;
float sample_to_float ( int16_t val ) ;
float read_sample_float ( ) ;
void set_comp_queue ( enum ads1115_comp_queue val ) { set_config ( val , ADS1115_COMP_QUEUE_MASK ) ; }
void set_comp_latching ( enum ads1115_comp_latch val ) { set_config ( val , ADS1115_COMP_LATCH_MASK ) ; }
void set_comp_polarity ( enum ads1115_comp_polarity val ) { set_config ( val , ADS1115_COMP_POLARITY_MASK ) ; }
void set_comp_mode ( enum ads1115_comp_mode val ) { set_config ( val , ADS1115_COMP_MODE_MASK ) ; }
void set_data_rate ( enum ads1115_data_rate val ) { set_config ( val , ADS1115_DATA_RATE_MASK ) ; }
void set_mode ( enum ads1115_mode val ) { set_config ( val , ADS1115_MODE_MASK ) ; }
void set_pga ( enum ads1115_pga val ) { set_config ( val , ADS1115_PGA_MASK ) ; m_voltage_range = val > > ADS1115_PGA_SHIFT ; }
void set_mux ( enum ads1115_mux val ) { set_config ( val , ADS1115_MUX_MASK ) ; }
private :
void set_config ( uint16_t val , uint16_t mask ) {
m_config = ( m_config & ~ mask ) | val ;
}
uint8_t write_register ( uint8_t reg , uint16_t val ) ;
uint16_t read_register ( uint8_t reg ) ;
uint8_t m_address ;
uint16_t m_config ;
int m_voltage_range ;
} ;
enum ads1115_register {
ADS1115_REGISTER_CONVERSION = 0 ,
ADS1115_REGISTER_CONFIG = 1 ,
ADS1115_REGISTER_LOW_THRESH = 2 ,
ADS1115_REGISTER_HIGH_THRESH = 3 ,
} ;
# define FACTOR 32768.0
static float ranges [ ] = { 6.144 / FACTOR , 4.096 / FACTOR , 2.048 / FACTOR , 1.024 / FACTOR , 0.512 / FACTOR , 0.256 / FACTOR } ;
ADS1115 : : ADS1115 ( uint8_t address )
{
m_address = address ;
m_config = ADS1115_COMP_QUEUE_AFTER_ONE |
ADS1115_COMP_LATCH_NO |
ADS1115_COMP_POLARITY_ACTIVE_LOW |
ADS1115_COMP_MODE_WINDOW |
ADS1115_DATA_RATE_128_SPS |
ADS1115_MODE_SINGLE_SHOT |
ADS1115_MUX_GND_AIN0 ;
set_pga ( ADS1115_PGA_ONE ) ;
}
uint8_t ADS1115 : : write_register ( uint8_t reg , uint16_t val )
{
Wire . beginTransmission ( m_address ) ;
Wire . write ( reg ) ;
Wire . write ( val > > 8 ) ;
Wire . write ( val & 0xFF ) ;
return Wire . endTransmission ( ) ;
}
uint16_t ADS1115 : : read_register ( uint8_t reg )
{
Wire . beginTransmission ( m_address ) ;
Wire . write ( reg ) ;
Wire . endTransmission ( ) ;
uint8_t result = Wire . requestFrom ( ( int ) m_address , 2 , 1 ) ;
if ( result ! = 2 ) {
2019-09-16 18:58:22 +01:00
return 0 ;
2019-08-27 14:33:09 +01:00
}
uint16_t val ;
val = Wire . read ( ) < < 8 ;
val | = Wire . read ( ) ;
return val ;
}
void ADS1115 : : begin ( )
{
Wire . begin ( ) ;
}
uint8_t ADS1115 : : trigger_sample ( )
{
return write_register ( ADS1115_REGISTER_CONFIG , m_config | SAMPLE_BIT ) ;
}
uint8_t ADS1115 : : reset ( )
{
Wire . beginTransmission ( 0 ) ;
Wire . write ( 0x6 ) ;
return Wire . endTransmission ( ) ;
}
bool ADS1115 : : is_sample_in_progress ( )
{
uint16_t val = read_register ( ADS1115_REGISTER_CONFIG ) ;
return ( val & SAMPLE_BIT ) = = 0 ;
}
int16_t ADS1115 : : read_sample ( )
{
return read_register ( ADS1115_REGISTER_CONVERSION ) ;
}
float ADS1115 : : sample_to_float ( int16_t val )
{
return val * ranges [ m_voltage_range ] ;
}
float ADS1115 : : read_sample_float ( )
{
return sample_to_float ( read_sample ( ) ) ;
}
ADS1115 adc ;
void ADS1115_init ( void ) {
ads1115_up = 0 ;
2020-10-30 11:29:48 +00:00
if ( ! TasmotaGlobal . i2c_enabled ) return ;
2019-08-27 14:33:09 +01:00
adc . begin ( ) ;
adc . set_data_rate ( ADS1115_DATA_RATE_128_SPS ) ;
adc . set_mode ( ADS1115_MODE_CONTINUOUS ) ;
adc . set_mux ( ADS1115_MUX_DIFF_AIN0_AIN3 ) ;
adc . set_pga ( ADS1115_PGA_TWO ) ;
int16_t val = adc . read_sample ( ) ;
ads1115_up = 1 ;
}
# endif
char sml_start ;
uint8_t dump2log = 0 ;
2022-01-20 19:19:50 +00:00
uint8_t ser_act_LED_pin = 255 ;
uint8_t ser_act_meter_num = 0 ;
2019-08-27 14:33:09 +01:00
# define SML_SAVAILABLE Serial_available()
# define SML_SREAD Serial_read()
# define SML_SPEAK Serial_peek()
bool Serial_available ( ) {
uint8_t num = dump2log & 7 ;
2019-09-03 09:39:51 +01:00
if ( num < 1 | | num > meters_used ) num = 1 ;
2020-05-15 14:30:32 +01:00
if ( ! meter_ss [ num - 1 ] ) return 0 ;
2019-09-03 09:39:51 +01:00
return meter_ss [ num - 1 ] - > available ( ) ;
2019-08-27 14:33:09 +01:00
}
uint8_t Serial_read ( ) {
uint8_t num = dump2log & 7 ;
2019-09-03 09:39:51 +01:00
if ( num < 1 | | num > meters_used ) num = 1 ;
2020-05-15 14:30:32 +01:00
if ( ! meter_ss [ num - 1 ] ) return 0 ;
2019-09-03 09:39:51 +01:00
return meter_ss [ num - 1 ] - > read ( ) ;
2019-08-27 14:33:09 +01:00
}
uint8_t Serial_peek ( ) {
uint8_t num = dump2log & 7 ;
2019-09-03 09:39:51 +01:00
if ( num < 1 | | num > meters_used ) num = 1 ;
2020-05-15 14:30:32 +01:00
if ( ! meter_ss [ num - 1 ] ) return 0 ;
2019-09-03 09:39:51 +01:00
return meter_ss [ num - 1 ] - > peek ( ) ;
2019-08-27 14:33:09 +01:00
}
2021-03-02 08:11:08 +00:00
# ifndef SML_DUMP_SIZE
# define SML_DUMP_SIZE 128
# endif
uint16_t sml_logindex ;
char log_data [ SML_DUMP_SIZE ] ;
2019-09-30 09:53:16 +01:00
2021-02-25 13:36:37 +00:00
# define SML_EBUS_SKIP_SYNC_DUMPS
2019-08-27 14:33:09 +01: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 ] ;
2021-02-25 13:36:37 +00:00
uint8_t type = meter_desc_p [ ( dump2log & 7 ) - 1 ] . type ;
2019-08-27 14:33:09 +01:00
2019-09-30 09:53:16 +01:00
//if (!SML_SAVAILABLE) return;
2019-08-27 14:33:09 +01:00
if ( dump2log & 8 ) {
// combo mode
while ( SML_SAVAILABLE ) {
2021-02-25 13:36:37 +00:00
log_data [ index ] = ' : ' ;
2019-08-27 14:33:09 +01:00
index + + ;
2021-02-25 13:36:37 +00:00
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 ;
sprintf ( & log_data [ index ] , " %02x " , c ) ;
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
2021-02-25 13:36:37 +00:00
log_data [ index ] = ' = ' ;
2019-08-27 14:33:09 +01:00
index + + ;
2021-02-25 13:36:37 +00:00
log_data [ index ] = ' > ' ;
2019-08-27 14:33:09 +01:00
index + + ;
2021-02-25 13:36:37 +00:00
log_data [ index ] = ' ' ;
2019-08-27 14:33:09 +01:00
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 ] ) ) {
2021-02-25 13:36:37 +00:00
log_data [ index ] = dchars [ ccnt ] ;
2019-08-27 14:33:09 +01:00
} else {
2021-02-25 13:36:37 +00:00
log_data [ index ] = ' ' ;
2019-08-27 14:33:09 +01:00
}
index + + ;
}
break ;
}
}
}
2021-02-25 13:36:37 +00:00
if ( index > 0 ) {
log_data [ index ] = 0 ;
2020-12-23 17:09:21 +00:00
AddLogData ( LOG_LEVEL_INFO , log_data ) ;
2021-02-25 13:36:37 +00:00
index = 0 ;
hcnt = 0 ;
2019-08-27 14:33:09 +01:00
}
}
} else {
2021-02-25 13:36:37 +00:00
if ( type = = ' o ' ) {
2019-09-30 09:53:16 +01:00
// obis
while ( SML_SAVAILABLE ) {
2021-02-25 13:36:37 +00:00
char c = SML_SREAD & 0x7f ;
if ( c = = ' \n ' | | c = = ' \r ' ) {
if ( sml_logindex > 2 ) {
log_data [ sml_logindex ] = 0 ;
2021-03-02 08:11:08 +00:00
AddLogData ( LOG_LEVEL_INFO , log_data ) ;
2021-02-25 13:36:37 +00:00
log_data [ 0 ] = ' : ' ;
log_data [ 1 ] = ' ' ;
sml_logindex = 2 ;
}
continue ;
2019-09-30 09:53:16 +01:00
}
2021-02-25 13:36:37 +00:00
log_data [ sml_logindex ] = c ;
if ( sml_logindex < sizeof ( log_data ) - 2 ) {
2019-09-30 09:53:16 +01:00
sml_logindex + + ;
}
}
2021-02-25 13:36:37 +00:00
} else if ( type = = ' v ' ) {
// vbus
uint8_t c ;
while ( SML_SAVAILABLE ) {
c = SML_SREAD ;
if ( c = = VBUS_SYNC ) {
2021-03-02 08:11:08 +00:00
log_data [ sml_logindex ] = 0 ;
2021-02-25 13:36:37 +00:00
AddLogData ( LOG_LEVEL_INFO , log_data ) ;
log_data [ 0 ] = ' : ' ;
log_data [ 1 ] = ' ' ;
sml_logindex = 2 ;
}
sprintf ( & log_data [ sml_logindex ] , " %02x " , c ) ;
if ( sml_logindex < sizeof ( log_data ) - 7 ) {
sml_logindex + = 3 ;
}
}
} else if ( type = = ' e ' ) {
// ebus
uint8_t c , p ;
while ( SML_SAVAILABLE ) {
c = SML_SREAD ;
if ( c = = EBUS_SYNC ) {
p = SML_SPEAK ;
if ( p ! = EBUS_SYNC & & sml_logindex > 5 ) {
// new packet, plot last one
2021-03-02 08:11:08 +00:00
log_data [ sml_logindex ] = 0 ;
2021-02-25 13:36:37 +00:00
AddLogData ( LOG_LEVEL_INFO , log_data ) ;
strcpy ( & log_data [ 0 ] , " : aa " ) ;
sml_logindex = 5 ;
2019-08-27 14:33:09 +01:00
}
2021-02-25 13:36:37 +00:00
continue ;
}
sprintf ( & log_data [ sml_logindex ] , " %02x " , c ) ;
if ( sml_logindex < sizeof ( log_data ) - 7 ) {
sml_logindex + = 3 ;
2019-08-27 14:33:09 +01:00
}
}
2021-02-25 13:36:37 +00:00
} else if ( type = = ' s ' ) {
// sml
uint8_t c ;
while ( SML_SAVAILABLE ) {
c = SML_SREAD ;
if ( c = = SML_SYNC ) {
2021-03-02 08:11:08 +00:00
log_data [ sml_logindex ] = 0 ;
2021-02-25 13:36:37 +00:00
AddLogData ( LOG_LEVEL_INFO , log_data ) ;
log_data [ 0 ] = ' : ' ;
log_data [ 1 ] = ' ' ;
sml_logindex = 2 ;
}
sprintf ( & log_data [ sml_logindex ] , " %02x " , c ) ;
if ( sml_logindex < sizeof ( log_data ) - 7 ) {
sml_logindex + = 3 ;
}
}
} else {
// raw dump
d_lastms = millis ( ) ;
log_data [ 0 ] = ' : ' ;
log_data [ 1 ] = ' ' ;
sml_logindex = 2 ;
while ( ( millis ( ) - d_lastms ) < 40 ) {
while ( SML_SAVAILABLE ) {
sprintf ( & log_data [ sml_logindex ] , " %02x " , SML_SREAD ) ;
sml_logindex + = 3 ;
}
}
if ( sml_logindex > 2 ) {
2021-03-02 08:11:08 +00:00
log_data [ sml_logindex ] = 0 ;
2020-12-23 17:09:21 +00:00
AddLogData ( LOG_LEVEL_INFO , log_data ) ;
2019-08-27 14:33:09 +01: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-07-20 13:25:40 +01:00
# if defined(ED300L) || defined(AS2020) || defined(DTZ541) || defined(USE_SML_SPECOPT)
2020-03-29 15:29:22 +01:00
uint8_t sml_status [ MAX_METERS ] ;
uint8_t g_mindex ;
# endif
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)
double sml_getvalue ( unsigned char * cp , uint8_t index ) {
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 ) {
sml_status [ g_mindex ] = * ( cp + 3 ) ;
}
2020-07-05 07:55:34 +01:00
if ( * cp = = 0x63 & & * cpx = = 0 & & * ( cpx + 1 ) = = 0x01 & & * ( cpx + 2 ) = = 0x08 & & * ( cpx + 3 ) = = 0 ) {
sml_status [ g_mindex ] = * ( cp + 2 ) ;
}
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 ) {
sml_status [ g_mindex ] = * ( cp + 2 ) ;
}
if ( * cp = = 0x63 & & * cpx = = 0 & & * ( cpx + 1 ) = = 0x01 & & * ( cpx + 2 ) = = 0x08 & & * ( cpx + 3 ) = = 0 ) {
sml_status [ g_mindex ] = * ( cp + 1 ) ;
}
# 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 ) {
sml_status [ g_mindex ] = * ( cp + 3 ) ;
}
# endif
# ifdef USE_SML_SPECOPT
unsigned char * cpx = cp - 5 ;
uint32_t ocode = ( * ( cpx + 0 ) < < 24 ) | ( * ( cpx + 1 ) < < 16 ) | ( * ( cpx + 2 ) < < 8 ) | ( * ( cpx + 3 ) < < 0 ) ;
if ( ocode = = script_meter_desc [ g_mindex ] . so_obis1 ) {
sml_status [ g_mindex ] & = 0xfe ;
uint32_t flag = 0 ;
uint16_t bytes = 0 ;
if ( * cp = = script_meter_desc [ g_mindex ] . so_fcode1 ) {
cpx = cp + 1 ;
bytes = ( script_meter_desc [ g_mindex ] . so_fcode1 & 0xf ) - 1 ;
for ( uint16_t cnt = 0 ; cnt < bytes ; cnt + + ) {
flag < < = 8 ;
flag | = * cpx + + ;
}
if ( flag & ( 1 < < script_meter_desc [ g_mindex ] . so_bpos1 ) ) {
sml_status [ g_mindex ] | = 1 ;
}
}
if ( * cp = = script_meter_desc [ g_mindex ] . so_fcode2 ) {
cpx = cp + 1 ;
bytes = ( script_meter_desc [ g_mindex ] . so_fcode2 & 0xf ) - 1 ;
for ( uint16_t cnt = 0 ; cnt < bytes ; cnt + + ) {
flag < < = 8 ;
flag | = * cpx + + ;
}
if ( flag & ( 1 < < script_meter_desc [ g_mindex ] . so_bpos1 ) ) {
sml_status [ g_mindex ] | = 1 ;
}
}
}
# endif
2020-03-29 15:29:22 +01:00
2019-08-27 14:33:09 +01:00
cp = skip_sml ( cp , & result ) ;
// check time
cp = skip_sml ( cp , & result ) ;
// check unit
cp = skip_sml ( cp , & result ) ;
// check scaler
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
type = * cp & 0x70 ;
len = * cp & 0x0f ;
cp + + ;
if ( type = = 0x50 | | type = = 0x60 ) {
// shift into 64 bit
uint64_t uvalue = 0 ;
uint8_t nlen = len ;
while ( - - nlen ) {
uvalue < < = 8 ;
uvalue | = * cp + + ;
}
if ( type = = 0x50 ) {
// signed
switch ( len - 1 ) {
case 1 :
// byte
value = ( signed char ) uvalue ;
break ;
case 2 :
// signed 16 bit
# ifdef DWS74_BUG
if ( scaler = = - 2 ) {
value = ( uint32_t ) uvalue ;
} else {
value = ( int16_t ) uvalue ;
}
# else
value = ( int16_t ) uvalue ;
# endif
break ;
case 3 :
2021-03-09 11:10:37 +00:00
// signed 24 bit
value = ( int32_t ) ( uvalue < < 8 ) ;
value / = 256 ;
break ;
2019-08-27 14:33:09 +01:00
case 4 :
// signed 32 bit
value = ( int32_t ) uvalue ;
break ;
case 5 :
case 6 :
case 7 :
case 8 :
// signed 64 bit
value = ( int64_t ) uvalue ;
break ;
}
} else {
// unsigned
value = uvalue ;
}
} else {
if ( ! ( type & 0xf0 ) ) {
// octet string serial number
// no coding found on the net
// up to now 2 types identified on Hager
if ( len = = 9 ) {
// serial number on hager => 24 bit - 24 bit
cp + + ;
uint32_t s1 , s2 ;
s1 = * cp < < 16 | * ( cp + 1 ) < < 8 | * ( cp + 2 ) ;
cp + = 4 ;
s2 = * cp < < 16 | * ( cp + 1 ) < < 8 | * ( cp + 2 ) ;
sprintf ( & meter_id [ index ] [ 0 ] , " %u-%u " , s1 , s2 ) ;
} else {
// server id on hager
char * str = & meter_id [ index ] [ 0 ] ;
for ( type = 0 ; type < len - 1 ; type + + ) {
sprintf ( str , " %02x " , * cp + + ) ;
str + = 2 ;
}
}
value = 0 ;
} else {
value = 999999 ;
scaler = 0 ;
}
}
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 ;
}
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 ) {
if ( sml_status [ g_mindex ] & 0x20 ) {
// 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 ) {
if ( sml_status [ g_mindex ] & 0x08 ) {
// 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 ) {
if ( sml_status [ g_mindex ] & 0x08 ) {
// and invert sign on solar feed
dval * = - 1 ;
}
}
# endif
# ifdef USE_SML_SPECOPT
ocode = ( * ( cpx + 0 ) < < 24 ) | ( * ( cpx + 1 ) < < 16 ) | ( * ( cpx + 2 ) < < 8 ) | ( * ( cpx + 3 ) < < 0 ) ;
if ( ocode = = script_meter_desc [ g_mindex ] . so_obis2 ) {
if ( sml_status [ g_mindex ] & 1 ) {
// and invert sign on solar feed
dval * = - 1 ;
}
}
# 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 ! = ' . ' ) {
left = atoi ( pt ) ; // Get left part
while ( isdigit ( * pt ) ) { pt + + ; } // Skip number
}
double right = 0 ;
if ( * pt = = ' . ' ) {
pt + + ;
right = atoi ( pt ) ; // Decimal part
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 ;
for ( count = 0 ; count < len ; count + + ) {
if ( ebus_buffer [ count ] = = EBUS_ESC ) {
//found escape
ebus_buffer [ count ] + = ebus_buffer [ count + 1 ] ;
// remove 2. char
count + + ;
for ( count1 = count ; count1 < len ; count1 + + ) {
ebus_buffer [ count1 ] = ebus_buffer [ count1 + 1 ] ;
}
}
}
}
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 ) {
while ( meter_ss [ meters ] - > available ( ) ) {
meter_ss [ meters ] - > read ( ) ;
}
}
2019-08-27 14:33:09 +01:00
void sml_shift_in ( uint32_t meters , uint32_t shard ) {
uint32_t count ;
2022-11-28 06:41:37 +00:00
2022-12-04 06:47:21 +00:00
# ifdef SML_OBIS_LINE
sml_options | = SML_OPTIONS_OBIS_LINE ;
# endif
2022-11-28 06:41:37 +00:00
bool shift ;
if ( ! ( sml_options & SML_OPTIONS_OBIS_LINE ) ) {
shift = ( meter_desc_p [ meters ] . type ! = ' e ' & & meter_desc_p [ meters ] . type ! = ' k ' & & meter_desc_p [ meters ] . type ! = ' m ' & & meter_desc_p [ meters ] . type ! = ' M ' & & meter_desc_p [ meters ] . type ! = ' p ' & & meter_desc_p [ meters ] . type ! = ' R ' & & meter_desc_p [ meters ] . type ! = ' v ' ) ;
} else {
shift = ( meter_desc_p [ meters ] . type ! = ' o ' & & meter_desc_p [ meters ] . type ! = ' e ' & & meter_desc_p [ meters ] . type ! = ' k ' & & meter_desc_p [ meters ] . type ! = ' m ' & & meter_desc_p [ meters ] . type ! = ' M ' & & meter_desc_p [ meters ] . type ! = ' p ' & & meter_desc_p [ meters ] . type ! = ' R ' & & meter_desc_p [ meters ] . type ! = ' v ' ) ;
}
if ( shift ) {
2019-08-27 14:33:09 +01:00
// shift in
2022-09-16 11:27:16 +01:00
for ( count = 0 ; count < SML_BSIZ - 1 ; count + + ) {
smltbuf [ meters ] [ count ] = smltbuf [ meters ] [ count + 1 ] ;
2019-08-27 14:33:09 +01:00
}
}
2022-09-16 11:27:16 +01:00
uint8_t iob = ( uint8_t ) meter_ss [ meters ] - > read ( ) ;
2019-08-27 14:33:09 +01:00
2021-09-22 15:40:52 +01:00
if ( meter_desc_p [ meters ] . type = = ' o ' ) {
2022-11-28 06:41:37 +00:00
if ( ! ( sml_options & SML_OPTIONS_OBIS_LINE ) ) {
smltbuf [ meters ] [ SML_BSIZ - 1 ] = iob & 0x7f ;
} else {
iob & = 0x7f ;
smltbuf [ meters ] [ meter_spos [ meters ] ] = iob ;
meter_spos [ meters ] + + ;
if ( meter_spos [ meters ] > = SML_BSIZ ) {
meter_spos [ meters ] = 0 ;
}
if ( ( iob = = 0x0a ) | | ( iob = = 0x0d ) ) {
SML_Decode ( meters ) ;
meter_spos [ meters ] = 0 ;
}
2021-09-22 15:40:52 +01:00
}
2019-08-27 14:33:09 +01:00
} else if ( meter_desc_p [ meters ] . type = = ' s ' ) {
smltbuf [ meters ] [ SML_BSIZ - 1 ] = iob ;
} else if ( meter_desc_p [ meters ] . type = = ' r ' ) {
smltbuf [ meters ] [ SML_BSIZ - 1 ] = iob ;
2022-09-16 11:27:16 +01:00
} else if ( meter_desc_p [ meters ] . type = = ' m ' | | meter_desc_p [ meters ] . type = = ' M ' | | meter_desc_p [ meters ] . type = = ' k ' ) {
if ( meter_desc_p [ meters ] . type = = ' k ' ) {
// Kamstrup
if ( iob = = 0x40 ) {
meter_spos [ meters ] = 0 ;
} else if ( iob = = 0x0d ) {
2022-10-03 15:08:49 +01:00
uint8_t index = 0 ;
uint8_t * ucp = & smltbuf [ meters ] [ 0 ] ;
for ( uint16_t cnt = 0 ; cnt < meter_spos [ meters ] ; cnt + + ) {
uint8_t iob = smltbuf [ meters ] [ cnt ] ;
if ( iob = = 0x1b ) {
* ucp + + = smltbuf [ meters ] [ cnt + 1 ] ^ 0xff ;
cnt + + ;
} else {
* ucp + + = iob ;
2022-09-16 11:27:16 +01:00
}
2022-10-03 15:08:49 +01:00
index + + ;
}
uint16_t crc = KS_calculateCRC ( & smltbuf [ meters ] [ 0 ] , index ) ;
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-09-16 11:27:16 +01:00
meter_spos [ meters ] = 0 ;
} else {
smltbuf [ meters ] [ meter_spos [ meters ] ] = iob ;
meter_spos [ meters ] + + ;
if ( meter_spos [ meters ] > = SML_BSIZ ) {
meter_spos [ meters ] = 0 ;
}
}
} else {
smltbuf [ meters ] [ meter_spos [ meters ] ] = iob ;
meter_spos [ meters ] + + ;
if ( meter_spos [ meters ] > = SML_BSIZ ) {
meter_spos [ meters ] = 0 ;
}
// modbus
2022-10-27 20:46:07 +01:00
if ( meter_spos [ meters ] > = 3 ) {
2022-09-16 11:27:16 +01:00
uint32_t mlen = smltbuf [ meters ] [ 2 ] + 5 ;
if ( mlen > SML_BSIZ ) mlen = SML_BSIZ ;
if ( meter_spos [ meters ] > = mlen ) {
# ifdef MODBUS_DEBUG
AddLog ( LOG_LEVEL_INFO , PSTR ( " receive index >> %d " ) , meter_desc_p [ meters ] . index ) ;
Hexdump ( smltbuf [ meters ] , 10 ) ;
# endif
SML_Decode ( meters ) ;
sml_empty_receiver ( meters ) ;
meter_spos [ meters ] = 0 ;
}
2020-08-09 08:36:24 +01:00
}
}
2022-09-16 11:27:16 +01:00
} else if ( meter_desc_p [ meters ] . type = = ' p ' ) {
2019-09-16 18:58:22 +01:00
smltbuf [ meters ] [ meter_spos [ meters ] ] = iob ;
meter_spos [ meters ] + + ;
2022-09-16 11:27:16 +01:00
if ( meter_spos [ meters ] > = 7 ) {
2019-09-03 09:39:51 +01:00
SML_Decode ( meters ) ;
2019-09-16 18:58:22 +01:00
sml_empty_receiver ( meters ) ;
2022-09-16 11:27:16 +01:00
meter_spos [ meters ] = 0 ;
2019-09-03 09:39:51 +01:00
}
2022-09-16 11:27:16 +01:00
} else if ( meter_desc_p [ meters ] . type = = ' R ' ) {
2020-07-15 08:44:52 +01:00
smltbuf [ meters ] [ meter_spos [ meters ] ] = iob ;
meter_spos [ meters ] + + ;
2022-09-16 11:27:16 +01:00
if ( meter_spos [ meters ] > = SML_BSIZ ) {
meter_spos [ meters ] = 0 ;
2020-07-15 08:44:52 +01:00
}
2022-09-16 11:27:16 +01:00
} else if ( meter_desc_p [ meters ] . type = = ' v ' ) {
if ( iob = = EBUS_SYNC ) {
2021-02-28 08:01:13 +00:00
sb_counter = 0 ;
2021-02-24 08:11:21 +00:00
SML_Decode ( meters ) ;
smltbuf [ meters ] [ 0 ] = iob ;
meter_spos [ meters ] = 1 ;
} else {
if ( meter_spos [ meters ] < SML_BSIZ ) {
smltbuf [ meters ] [ meter_spos [ meters ] ] = iob ;
meter_spos [ meters ] + + ;
}
}
2020-07-15 08:44:52 +01:00
}
else {
2022-09-16 11:27:16 +01:00
if ( iob = = EBUS_SYNC ) {
2019-08-27 14:33:09 +01:00
// should be end of telegramm
// QQ,ZZ,PB,SB,NN ..... CRC, ACK SYNC
2022-09-16 11:27:16 +01:00
if ( meter_spos [ meters ] > 4 + 5 ) {
2019-08-27 14:33:09 +01:00
// get telegramm lenght
2022-09-16 11:27:16 +01:00
uint16_t tlen = smltbuf [ meters ] [ 4 ] + 5 ;
2019-08-27 14:33:09 +01:00
// test crc
2022-09-16 11:27:16 +01:00
if ( smltbuf [ meters ] [ tlen ] = ebus_CalculateCRC ( smltbuf [ meters ] , tlen ) ) {
ebus_esc ( smltbuf [ meters ] , tlen ) ;
2019-08-27 14:33:09 +01:00
SML_Decode ( meters ) ;
} else {
// crc error
2021-01-23 16:10:06 +00:00
//AddLog(LOG_LEVEL_INFO, PSTR("ebus crc error"));
2019-08-27 14:33:09 +01:00
}
}
2021-02-28 08:01:13 +00:00
meter_spos [ meters ] = 0 ;
2019-08-27 14:33:09 +01:00
return ;
}
2019-09-16 18:58:22 +01:00
smltbuf [ meters ] [ meter_spos [ meters ] ] = iob ;
meter_spos [ meters ] + + ;
2022-09-16 11:27:16 +01:00
if ( meter_spos [ meters ] > = SML_BSIZ ) {
meter_spos [ meters ] = 0 ;
2019-08-27 14:33:09 +01:00
}
}
sb_counter + + ;
2022-11-28 06:41:37 +00:00
if ( shift ) {
SML_Decode ( meters ) ;
}
2019-08-27 14:33:09 +01:00
}
// polled every 50 ms
void SML_Poll ( void ) {
uint32_t meters ;
2021-12-15 13:46:05 +00:00
for ( meters = 0 ; meters < meters_used ; meters + + ) {
if ( meter_desc_p [ meters ] . type ! = ' c ' ) {
2019-08-27 14:33:09 +01:00
// poll for serial input
2020-05-15 14:30:32 +01:00
if ( ! meter_ss [ meters ] ) continue ;
2022-09-16 11:27:16 +01:00
if ( ser_act_LED_pin ! = 255 & & ( ser_act_meter_num = = 0 | | ser_act_meter_num - 1 = = meters ) ) {
2022-01-20 19:19:50 +00:00
digitalWrite ( ser_act_LED_pin , meter_ss [ meters ] - > available ( ) & & ! digitalRead ( ser_act_LED_pin ) ) ; // Invert LED, if queue is continuously full
}
2019-09-03 09:39:51 +01:00
while ( meter_ss [ meters ] - > available ( ) ) {
2021-12-15 13:46:05 +00:00
sml_shift_in ( meters , 0 ) ;
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 ;
}
2019-08-27 14:33:09 +01:00
void SML_Decode ( uint8_t index ) {
const char * mp = ( const char * ) meter_p ;
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
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
2021-02-28 08:01:13 +00:00
if ( mindex < 0 | | mindex > = meters_used ) mindex = 0 ;
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 + + ;
}
}
2019-08-27 14:33:09 +01:00
if ( index ! = mindex ) goto nextsect ;
// start of serial source buffer
2021-02-28 08:01:13 +00:00
cp = & smltbuf [ mindex ] [ 0 ] ;
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 ) ;
if ( mind < 1 | | mind > SML_MAX_VARS ) mind = 1 ;
dvar = meter_vars [ mind - 1 ] ;
while ( * mp = = ' ' ) mp + + ;
for ( uint8_t p = 0 ; p < 8 ; p + + ) {
if ( * mp = = ' @ ' ) {
2019-08-27 14:33:09 +01:00
// store result
2021-02-28 08:01:13 +00:00
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 ;
if ( mind < 1 | | mind > SML_MAX_VARS ) 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 ;
else dvar + = 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 ;
else dvar - = 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 ;
else dvar * = 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 ;
else dvar / = 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
2021-02-28 08:01:13 +00:00
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 ) ;
meter_vars [ vindex ] / = fac ;
SML_Immediate_MQTT ( ( const char * ) mp , vindex , mindex ) ;
2021-03-02 08:11:08 +00:00
dvalid [ vindex ] = 1 ;
2021-02-28 08:01:13 +00:00
// get sfac
2019-08-27 14:33:09 +01:00
} else if ( * mp = = ' d ' ) {
// 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 + + ;
if ( ind < 1 | | ind > SML_MAX_VARS ) ind = 1 ;
uint32_t delay = atoi ( mp ) * 1000 ;
uint32_t dtime = millis ( ) - dtimes [ dindex ] ;
if ( dtime > delay ) {
2019-08-27 14:33:09 +01:00
// calc difference
2021-02-28 08:01:13 +00:00
dtimes [ dindex ] = millis ( ) ;
double vdiff = meter_vars [ ind - 1 ] - dvalues [ dindex ] ;
dvalues [ dindex ] = 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
dvalid [ vindex ] + = 1 ;
if ( dvalid [ vindex ] > = 2 ) {
// differece is only valid after 2. calculation
dvalid [ vindex ] = 2 ;
2021-03-02 08:11:08 +00:00
# ifdef USE_SML_MEDIAN_FILTER
2021-12-08 15:30:11 +00:00
if ( meter_desc_p [ mindex ] . flag & 16 ) {
meter_vars [ vindex ] = sml_median ( & sml_mf [ vindex ] , dres ) ;
} else {
meter_vars [ vindex ] = dres ;
}
2021-03-02 08:11:08 +00:00
# else
2021-12-08 15:30:11 +00:00
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 ) ;
meter_vars [ vindex ] / = fac ;
SML_Immediate_MQTT ( ( const char * ) mp , vindex , mindex ) ;
2019-08-27 14:33:09 +01:00
}
}
2021-12-08 15:30:11 +00:00
//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 ! = ' @ ' ) {
if ( meter_desc_p [ mindex ] . type = = ' o ' | | meter_desc_p [ mindex ] . type = = ' c ' ) {
if ( * mp + + ! = * cp + + ) {
2019-08-27 14:33:09 +01:00
found = 0 ;
}
} else {
2021-02-28 08:01:13 +00:00
if ( meter_desc_p [ 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 + + ) {
2019-08-27 14:33:09 +01:00
found = 0 ;
}
} else {
2022-03-19 07:20:51 +00:00
// ebus modbus pzem vbus or raw
2019-08-27 14:33:09 +01:00
// XXHHHHSSUU
2021-02-28 08:01:13 +00:00
if ( * mp = = ' x ' ) {
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 ;
}
sprintf ( & meter_id [ mindex ] [ 0 ] , " %02d:%02d " , time / 60 , time % 60 ) ;
}
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 + + ) ;
if ( val ! = * cp + + ) {
found = 0 ;
}
}
}
}
}
if ( found ) {
// matches, get value
2021-03-02 08:11:08 +00:00
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)
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 + + ;
2021-02-24 08:11:21 +00:00
if ( meter_desc_p [ mindex ] . type ! = ' v ' ) {
if ( meter_desc_p [ mindex ] . type = = ' o ' ) {
uint32_t p ;
for ( p = 0 ; p < METER_ID_SIZE - 2 ; p + + ) {
if ( * cp = = * mp ) {
break ;
}
meter_id [ mindex ] [ p ] = * cp + + ;
2019-08-27 14:33:09 +01:00
}
2021-02-24 08:11:21 +00:00
meter_id [ mindex ] [ p ] = 0 ;
2022-10-03 15:08:49 +01:00
} else if ( meter_desc_p [ mindex ] . type = = ' k ' ) {
// 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
sprintf ( & meter_id [ mindex ] [ 0 ] , " %02d.%02d.%02d " , day , month , year ) ;
2021-02-24 08:11:21 +00:00
} else {
sml_getvalue ( cp , mindex ) ;
2019-08-27 14:33:09 +01:00
}
}
} else {
double dval ;
2022-09-16 11:27:16 +01:00
if ( meter_desc_p [ mindex ] . type ! = ' e ' & & meter_desc_p [ mindex ] . type ! = ' r ' & & meter_desc_p [ mindex ] . type ! = ' m ' & & meter_desc_p [ mindex ] . type ! = ' M ' & & meter_desc_p [ mindex ] . type ! = ' k ' & & meter_desc_p [ mindex ] . type ! = ' p ' & & meter_desc_p [ mindex ] . type ! = ' v ' ) {
2019-08-27 14:33:09 +01:00
// get numeric values
if ( meter_desc_p [ mindex ] . type = = ' o ' | | meter_desc_p [ mindex ] . 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
}
2021-02-22 10:10:49 +00:00
if ( * mp = = ' # ' ) {
cp = ( uint8_t * ) lcp ;
goto getstr ;
}
dval = CharToDouble ( ( char * ) lcp ) ;
2021-01-22 07:31:22 +00:00
} else {
dval = CharToDouble ( ( char * ) cp ) ;
}
2019-08-27 14:33:09 +01:00
} else {
2021-01-22 07:31:22 +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 ;
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 ) ;
if ( mb_index ! = meter_desc_p [ mindex ] . index ) {
2019-09-03 09:39:51 +01:00
goto nextsect ;
}
2022-09-16 11:27:16 +01:00
if ( meter_desc_p [ mindex ] . type = = ' k ' ) {
// crc is already checked, get float value
dval = mbus_dval ;
mp + + ;
} else {
uint16_t pos = smltbuf [ mindex ] [ 2 ] + 3 ;
2022-12-12 15:13:04 +00:00
if ( pos > ( SML_BSIZ - 2 ) ) pos = SML_BSIZ - 2 ;
2022-09-16 11:27:16 +01:00
uint16_t crc = MBUS_calculateCRC ( & smltbuf [ mindex ] [ 0 ] , pos , 0xFFFF ) ;
if ( lowByte ( crc ) ! = smltbuf [ mindex ] [ pos ] ) goto nextsect ;
if ( highByte ( crc ) ! = smltbuf [ mindex ] [ pos + 1 ] ) goto nextsect ;
dval = mbus_dval ;
//AddLog(LOG_LEVEL_INFO, PSTR(">> %s"),mp);
mp + + ;
}
2019-09-03 09:39:51 +01:00
} else {
2022-09-16 11:27:16 +01:00
if ( meter_desc_p [ mindex ] . type = = ' p ' ) {
2019-09-16 18:58:22 +01:00
uint8_t crc = SML_PzemCrc ( & smltbuf [ mindex ] [ 0 ] , 6 ) ;
2022-09-16 11:27:16 +01:00
if ( crc ! = smltbuf [ mindex ] [ 6 ] ) goto nextsect ;
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-09-16 11:27:16 +01:00
if ( meter_desc_p [ mindex ] . flag & 16 ) {
meter_vars [ vindex ] = sml_median ( & sml_mf [ vindex ] , dval ) ;
2019-09-16 18:58:22 +01:00
} else {
2022-09-16 11:27:16 +01:00
meter_vars [ vindex ] = dval ;
2019-09-16 18:58:22 +01:00
}
2019-08-27 14:33:09 +01:00
# else
2022-09-16 11:27:16 +01:00
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 ) ;
meter_vars [ vindex ] + = offset ;
}
2021-02-28 08:01:13 +00:00
meter_vars [ vindex ] / = fac ;
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
2019-12-25 08:09:43 +00:00
if ( vindex < SML_MAX_VARS - 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
char * cp = strchr ( mp , ' , ' ) ;
if ( cp ) {
cp + + ;
// wn
cp = strchr ( cp , ' , ' ) ;
if ( cp ) {
cp + + ;
// unit
cp = strchr ( cp , ' , ' ) ;
if ( cp ) {
cp + + ;
// json mqtt
for ( uint8_t count = 0 ; count < sizeof ( jname ) ; count + + ) {
if ( * cp = = ' , ' ) {
jname [ count ] = 0 ;
break ;
}
jname [ count ] = * cp + + ;
}
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-09-16 11:27:16 +01:00
dtostrfd ( meter_vars [ index ] , dp & 0xf , tpowstr ) ;
ResponseTime_P ( PSTR ( " , \" %s \" :{ \" %s \" :%s}} " ) , meter_desc_p [ 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 ] ;
int8_t index = 0 , mid = 0 ;
char * mp = ( char * ) 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;
2020-11-02 11:24:39 +00:00
if ( ! 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 ;
if ( lastmind < 0 | | lastmind > = 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-09-16 11:27:16 +01:00
if ( mindex < 0 | | mindex > = meters_used ) mindex = 0 ;
if ( meter_desc_p [ mindex ] . prefix [ 0 ] = = ' * ' & & meter_desc_p [ 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 ;
if ( * mp = = ' = ' & & * ( mp + 1 ) = = ' h ' ) {
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-09-16 11:27:16 +01:00
sprintf ( tpowstr , " \" %s \" " , & meter_id [ mindex ] [ 0 ] ) ;
2022-09-08 13:35:04 +01:00
}
2022-09-16 11:27:16 +01:00
mid = 1 ;
} else if ( * cp = = ' ( ' ) {
if ( meter_desc_p [ 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
sprintf_P ( tpowstr , PSTR ( " \" %s \" " ) , ( uint8_t ) meter_vars [ index ] ? D_ON : D_OFF ) ;
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 ;
dtostrfd ( meter_vars [ index ] , dp , tpowstr ) ;
2019-08-27 14:33:09 +01:00
}
if ( json ) {
2021-04-15 19:21:51 +01:00
//if (dvalid[index]) {
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 ) {
2020-08-09 08:36:24 +01:00
//snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s,\"%s\":{\"%s\":%s", b_mqtt_data,meter_desc_p[mindex].prefix,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 \" :%s " ) , meter_desc_p [ 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
//snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s,\"%s\":{\"%s\":%s", b_mqtt_data,meter_desc_p[mindex].prefix,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 \" :%s " ) , meter_desc_p [ 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-09-16 11:27:16 +01:00
if ( strcmp ( name , " * " ) ) WSContentSend_PD ( PSTR ( " {s}%s %s {m}%s %s{e} " ) , meter_desc_p [ mindex ] . prefix , name , tpowstr , unit ) ;
2019-08-27 14:33:09 +01:00
}
}
}
2022-09-16 11:27:16 +01:00
if ( index < SML_MAX_VARS - 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);
}
2020-11-02 11:36:57 +00:00
2019-08-27 14:33:09 +01:00
# 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 ] ;
dtostrfd ( meter_vars [ 0 ] , 1 , str ) ;
DomoticzSensorPowerEnergy ( meter_vars [ 1 ] , str ) ; // PowerUsage, EnergyToday
dtostrfd ( meter_vars [ 2 ] , 1 , str ) ;
DomoticzSensor ( DZ_VOLTAGE , str ) ; // Voltage
dtostrfd ( meter_vars [ 3 ] , 1 , str ) ;
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
# ifdef ANALOG_OPTO_SENSOR
int16_t ana_curr ;
int16_t ana_max ;
int16_t ana_min ;
int16_t ana_cmpl ;
int16_t ana_cmph ;
# endif
} sml_counters [ MAX_COUNTERS ] ;
2020-11-02 11:24:39 +00:00
uint8_t sml_counter_pinstate ;
2019-10-20 07:23:27 +01:00
2020-11-02 11:24:39 +00:00
uint8_t sml_cnt_index [ MAX_COUNTERS ] = { 0 , 1 , 2 , 3 } ;
2021-04-02 16:14:08 +01:00
void IRAM_ATTR SML_CounterIsr ( void * arg ) {
2022-03-03 07:28:04 +00:00
uint32_t index = * static_cast < uint8_t * > ( arg ) ;
2020-11-02 11:24:39 +00:00
2022-03-03 07:28:04 +00:00
uint32_t time = millis ( ) ;
uint32_t debounce_time ;
2020-11-02 11:24:39 +00:00
if ( digitalRead ( meter_desc_p [ sml_counters [ index ] . sml_cnt_old_state ] . srcpin ) = = bitRead ( sml_counter_pinstate , index ) ) {
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 ;
}
# endif
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-07-20 13:25:40 +01:00
# ifdef USE_SML_SPECOPT
void SML_GetSpecOpt ( char * cp , uint32_t mnum ) {
// special option 1
// we need 2 obis codes
// 2 flag codes + bit positions
// 1,=so1,00010800,63,7,64,11,00100700
if ( * cp = = ' , ' ) {
cp + + ;
script_meter_desc [ mnum ] . so_obis1 = strtol ( cp , & cp , 16 ) ;
}
if ( * cp = = ' , ' ) {
cp + + ;
script_meter_desc [ mnum ] . so_fcode1 = strtol ( cp , & cp , 16 ) ;
}
if ( * cp = = ' , ' ) {
cp + + ;
script_meter_desc [ mnum ] . so_bpos1 = strtol ( cp , & cp , 10 ) ;
}
if ( * cp = = ' , ' ) {
cp + + ;
script_meter_desc [ mnum ] . so_fcode2 = strtol ( cp , & cp , 16 ) ;
}
if ( * cp = = ' , ' ) {
cp + + ;
script_meter_desc [ mnum ] . so_bpos2 = strtol ( cp , & cp , 10 ) ;
}
if ( * cp = = ' , ' ) {
cp + + ;
script_meter_desc [ mnum ] . so_obis2 = strtol ( cp , & cp , 16 ) ;
}
}
# endif
2019-08-27 14:33:09 +01:00
void SML_Init ( void ) {
2022-09-16 11:27:16 +01:00
meters_used = METERS_USED ;
meter_desc_p = meter_desc ;
meter_p = meter ;
2019-08-27 14:33:09 +01:00
2022-09-16 11:27:16 +01:00
sml_desc_cnt = 0 ;
2019-09-16 18:58:22 +01:00
2022-09-16 11:27:16 +01:00
for ( uint32_t cnt = 0 ; cnt < SML_MAX_VARS ; cnt + + ) {
meter_vars [ cnt ] = 0 ;
dvalid [ cnt ] = 0 ;
2019-08-27 14:33:09 +01:00
}
2022-09-16 11:27:16 +01:00
for ( uint32_t cnt = 0 ; cnt < MAX_METERS ; cnt + + ) {
meter_spos [ cnt ] = 0 ;
2019-09-16 18:58:22 +01:00
}
2019-08-27 14:33:09 +01:00
2022-07-20 13:25:40 +01:00
# ifdef USE_SML_SPECOPT
for ( uint32_t cnt = 0 ; cnt < MAX_METERS ; cnt + + ) {
script_meter_desc [ cnt ] . so_obis1 = 0 ;
script_meter_desc [ cnt ] . so_obis2 = 0 ;
}
# endif
2019-08-27 14:33:09 +01:00
# ifdef USE_SCRIPT
2019-09-03 09:54:01 +01:00
2022-09-16 11:27:16 +01:00
for ( uint32_t cnt = 0 ; cnt < MAX_METERS ; cnt + + ) {
2019-09-03 09:54:01 +01:00
if ( script_meter_desc [ cnt ] . txmem ) {
free ( script_meter_desc [ cnt ] . txmem ) ;
2019-09-16 18:58:22 +01:00
}
2022-09-16 11:27:16 +01:00
script_meter_desc [ cnt ] . txmem = 0 ;
script_meter_desc [ cnt ] . trxpin = - 1 ;
2020-05-15 14:30:32 +01:00
if ( meter_ss [ cnt ] ) {
delete meter_ss [ cnt ] ;
2022-09-16 11:27:16 +01:00
meter_ss [ cnt ] = NULL ;
2020-05-15 14:30:32 +01:00
}
2019-09-03 09:54:01 +01:00
}
2021-06-11 17:14:12 +01:00
if ( bitRead ( Settings - > rule_enabled , 0 ) ) {
2020-05-15 14:30:32 +01:00
2022-09-16 11:27:16 +01:00
uint8_t meter_script = Run_Scripter ( " >M " , - 2 , 0 ) ;
if ( meter_script = = 99 ) {
2019-08-27 14:33:09 +01:00
// use script definition
if ( script_meter ) free ( script_meter ) ;
2021-02-22 10:10:49 +00:00
script_meter = 0 ;
uint8_t * tp = 0 ;
uint16_t index = 0 ;
uint8_t section = 0 ;
2021-12-15 13:46:05 +00:00
int8_t srcpin = 0 ;
2021-02-22 10:10:49 +00:00
uint8_t dec_line = 0 ;
char * lp = glob_script_mem . scriptptr ;
sml_send_blocks = 0 ;
2019-08-27 14:33:09 +01:00
while ( lp ) {
if ( ! section ) {
2021-02-22 10:10:49 +00:00
if ( * lp = = ' > ' & & * ( lp + 1 ) = = ' M ' ) {
lp + = 2 ;
meters_used = strtol ( lp , 0 , 10 ) ;
section = 1 ;
uint32_t mlen = SML_getscriptsize ( lp ) ;
if ( mlen = = 0 ) return ; // missing end #
script_meter = ( uint8_t * ) calloc ( mlen , 1 ) ;
2019-09-16 18:58:22 +01:00
if ( ! script_meter ) {
goto dddef_exit ;
}
2021-02-22 10:10:49 +00:00
tp = 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 ;
if ( index < 1 | | index > meters_used ) {
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 :
if ( script_meter ) free ( script_meter ) ;
2021-02-22 10:10:49 +00:00
script_meter = 0 ;
meters_used = METERS_USED ;
2019-09-16 18:58:22 +01:00
goto init10 ;
2019-08-27 14:33:09 +01:00
}
2021-02-22 10:10:49 +00:00
script_meter_desc [ index ] . srcpin = srcpin ;
if ( * lp ! = ' , ' ) goto next_line ;
2019-08-27 14:33:09 +01:00
lp + + ;
2021-02-22 10:10:49 +00:00
script_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 + + ;
script_meter_desc [ index ] . sopt = 0x10 | ( * lp & 3 ) ;
lp + + ;
break ;
case ' E ' :
lp + + ;
script_meter_desc [ index ] . sopt = 0x20 | ( * lp & 3 ) ;
lp + + ;
break ;
case ' O ' :
lp + + ;
script_meter_desc [ index ] . sopt = 0x30 | ( * lp & 3 ) ;
lp + + ;
break ;
default :
script_meter_desc [ index ] . sopt = * lp & 7 ;
lp + + ;
}
2020-11-15 15:09:05 +00:00
} else {
2021-02-22 10:10:49 +00:00
script_meter_desc [ index ] . sopt = 0 ;
2020-11-15 15:09:05 +00:00
}
lp + + ;
2021-02-22 10:10:49 +00:00
script_meter_desc [ index ] . flag = strtol ( lp , & lp , 10 ) ;
if ( * lp ! = ' , ' ) goto next_line ;
2019-08-27 14:33:09 +01:00
lp + + ;
2021-02-22 10:10:49 +00:00
script_meter_desc [ index ] . params = strtol ( lp , & lp , 10 ) ;
if ( * lp ! = ' , ' ) goto next_line ;
2019-08-27 14:33:09 +01:00
lp + + ;
2021-02-22 10:10:49 +00:00
script_meter_desc [ index ] . prefix [ 7 ] = 0 ;
for ( uint32_t cnt = 0 ; cnt < 8 ; cnt + + ) {
if ( * lp = = SCRIPT_EOL | | * lp = = ' , ' ) {
script_meter_desc [ index ] . prefix [ cnt ] = 0 ;
2019-08-27 14:33:09 +01:00
break ;
}
2021-02-22 10:10:49 +00:00
script_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
2021-02-22 10:10:49 +00:00
script_meter_desc [ index ] . trxpin = strtol ( lp , & lp , 10 ) ;
2019-09-16 18:58:22 +01:00
if ( Gpio_used ( script_meter_desc [ index ] . trxpin ) ) {
2022-09-16 11:27:16 +01:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " SML: Error: Duplicate GPIO %d defined. Not usable for TX in meter number %d " ) , script_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 + + ;
script_meter_desc [ index ] . trx_en . trxenpol = 1 ;
} else {
script_meter_desc [ index ] . trx_en . trxenpol = 0 ;
}
script_meter_desc [ index ] . trx_en . trxenpin = strtol ( lp , & lp , 10 ) ;
if ( * lp ! = ' ) ' ) {
goto dddef_exit ;
}
lp + + ;
if ( Gpio_used ( script_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 " ) , script_meter_desc [ index ] . trx_en . trxenpin , index + 1 ) ;
goto dddef_exit ;
}
script_meter_desc [ index ] . trx_en . trxen = 1 ;
pinMode ( script_meter_desc [ index ] . trx_en . trxenpin , OUTPUT ) ;
digitalWrite ( script_meter_desc [ index ] . trx_en . trxenpin , script_meter_desc [ index ] . trx_en . trxenpol ) ;
} else {
script_meter_desc [ index ] . trx_en . trxen = 0 ;
}
2021-02-22 10:10:49 +00:00
if ( * lp ! = ' , ' ) goto next_line ;
2019-09-03 09:39:51 +01:00
lp + + ;
2021-02-22 10:10:49 +00:00
script_meter_desc [ index ] . tsecs = strtol ( lp , & lp , 10 ) ;
if ( * lp = = ' , ' ) {
2019-09-03 09:39:51 +01:00
lp + + ;
2021-12-08 15:30:11 +00:00
# if 1
// 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 ) {
script_meter_desc [ index ] . txmem = ( char * ) calloc ( txlen + 2 , 1 ) ;
if ( script_meter_desc [ index ] . txmem ) {
// 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 {
script_meter_desc [ index ] . txmem [ tind ] = * txp + + ;
tind + + ;
}
}
}
//AddLog(LOG_LEVEL_INFO, PSTR(">>> %s - %d"), script_meter_desc[index].txmem, txlen);
script_meter_desc [ index ] . index = 0 ;
script_meter_desc [ index ] . max_index = tx_entries ;
sml_send_blocks + + ;
lp + = txlen ;
}
# else
char txbuff [ SML_SRCBSIZE ] ;
2021-02-22 10:10:49 +00:00
uint32_t txlen = 0 , tx_entries = 1 ;
for ( uint32_t cnt = 0 ; cnt < sizeof ( txbuff ) ; cnt + + ) {
2021-12-08 15:30:11 +00:00
if ( * lp = = SCRIPT_EOL & & * ( lp - 1 ) ! = ' , ' ) {
2021-02-22 10:10:49 +00:00
txbuff [ cnt ] = 0 ;
txlen = cnt ;
2019-09-03 09:39:51 +01:00
break ;
}
2021-02-22 10:10:49 +00:00
if ( * lp = = ' , ' ) tx_entries + + ;
txbuff [ cnt ] = * lp + + ;
2019-09-03 09:39:51 +01:00
}
if ( txlen ) {
2021-02-28 08:01:13 +00:00
script_meter_desc [ index ] . txmem = ( char * ) calloc ( txlen + 2 , 1 ) ;
2019-09-03 09:39:51 +01:00
if ( script_meter_desc [ index ] . txmem ) {
2021-02-28 08:01:13 +00:00
strcpy ( script_meter_desc [ index ] . txmem , txbuff ) ;
2019-09-03 09:39:51 +01:00
}
2021-02-22 10:10:49 +00:00
script_meter_desc [ index ] . index = 0 ;
script_meter_desc [ index ] . max_index = tx_entries ;
2019-09-16 18:58:22 +01:00
sml_send_blocks + + ;
2019-09-03 09:39:51 +01:00
}
2021-12-08 15:30:11 +00:00
# endif
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 ;
}
2020-05-15 14:30:32 +01:00
# ifdef SML_REPLACE_VARS
char dstbuf [ SML_SRCBSIZE * 2 ] ;
2021-02-22 10:10:49 +00:00
Replace_Cmd_Vars ( lp , 1 , dstbuf , sizeof ( dstbuf ) ) ;
lp + = SML_getlinelen ( lp ) ;
2021-06-05 10:47:09 +01:00
//AddLog(LOG_LEVEL_INFO, PSTR("%s"),dstbuf);
2021-02-22 10:10:49 +00:00
char * lp1 = dstbuf ;
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 ) ;
if ( mnum < 1 | | mnum > meters_used ) {
AddLog ( LOG_LEVEL_INFO , PSTR ( " illegal meter number! " ) ) ;
goto next_line ;
}
// 1,=h—————————————
if ( strncmp ( lp1 + 1 , " ,=h " , 3 ) ) {
dec_line + + ;
if ( dec_line > = SML_MAX_VARS ) {
AddLog ( LOG_LEVEL_INFO , PSTR ( " too many decode lines: %d ! " ) , dec_line ) ;
goto next_line ;
}
}
2022-07-20 13:25:40 +01:00
# ifdef USE_SML_SPECOPT
if ( ! strncmp ( lp1 + 1 , " ,=so " , 4 ) ) {
// special option
char * cp = lp1 + 5 ;
if ( * cp = = ' 1 ' ) {
cp + + ;
SML_GetSpecOpt ( cp , mnum - 1 ) ;
}
}
# endif
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
}
}
# else
2021-02-22 10:10:49 +00:00
if ( * lp = = ' - ' | | isdigit ( * lp ) ) {
2019-08-27 14:33:09 +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 ( * lp = = ' - ' ) lp + + ;
uint8_t mnum = strtol ( lp , 0 , 10 ) ;
if ( mnum < 1 | | mnum > meters_used ) {
AddLog ( LOG_LEVEL_INFO , PSTR ( " illegal meter number! " ) ) ;
goto next_line ;
}
if ( strncmp ( lp + 1 , " ,=h " , 3 ) ) {
dec_line + + ;
if ( dec_line > = SML_MAX_VARS ) {
AddLog ( LOG_LEVEL_INFO , PSTR ( " too many decode lines: %d ! " ) , dec_line ) ;
goto next_line ;
}
}
2022-07-20 13:25:40 +01:00
# ifdef USE_SML_SPECOPT
if ( ! strncmp ( lp + 1 , " ,=so " , 4 ) ) {
// special option
char * cp = lp + 5 ;
if ( * cp = = ' 1 ' ) {
cp + + ;
SML_GetSpecOpt ( cp , mnum - 1 ) ;
}
}
# endif
2019-08-27 14:33:09 +01:00
while ( 1 ) {
2021-02-22 10:10:49 +00:00
if ( * lp = = SCRIPT_EOL ) {
if ( * ( tp - 1 ) ! = ' | ' ) * tp + + = ' | ' ;
2019-08-27 14:33:09 +01:00
goto next_line ;
}
2021-02-22 10:10:49 +00:00
* tp + + = * lp + + ;
2019-08-27 14:33:09 +01:00
index + + ;
2021-02-22 10:10:49 +00:00
if ( index > = METER_DEF_SIZE ) break ;
2019-08-27 14:33:09 +01:00
}
2021-02-22 10:10:49 +00:00
2019-08-27 14:33:09 +01:00
}
2020-05-15 14:30:32 +01:00
# endif
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 ;
meter_desc_p = script_meter_desc ;
meter_p = script_meter ;
2019-08-27 14:33:09 +01:00
}
2020-05-15 14:30:32 +01:00
}
2019-08-27 14:33:09 +01:00
# endif
2019-09-16 18:58:22 +01:00
init10 :
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
for ( byte 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-07-06 20:48:00 +01:00
# ifdef ESP32
2022-07-03 14:56:16 +01:00
uint32_t uart_index = SOC_UART_NUM - 1 ;
2022-07-06 20:48:00 +01:00
# endif
2022-02-08 16:15:41 +00:00
sml_counter_pinstate = 0 ;
2021-12-15 13:46:05 +00:00
for ( uint8_t meters = 0 ; meters < meters_used ; meters + + ) {
if ( meter_desc_p [ meters ] . type = = ' c ' ) {
if ( meter_desc_p [ meters ] . flag & 2 ) {
2019-08-27 14:33:09 +01:00
// analog mode
# ifdef ANALOG_OPTO_SENSOR
ADS1115_init ( ) ;
sml_counters [ cindex ] . ana_max = - 32768 ;
sml_counters [ cindex ] . ana_min = + 32767 ;
# endif
} else {
// counters, set to input with pullup
2022-09-08 13:35:04 +01:00
if ( meter_desc_p [ meters ] . flag & 1 ) {
pinMode ( meter_desc_p [ meters ] . srcpin , INPUT_PULLUP ) ;
2019-08-27 14:33:09 +01:00
} else {
2022-09-08 13:35:04 +01:00
pinMode ( meter_desc_p [ meters ] . srcpin , INPUT ) ;
2019-08-27 14:33:09 +01:00
}
// check for irq mode
2022-09-08 13:35:04 +01:00
if ( meter_desc_p [ 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 ;
sml_counters [ cindex ] . sml_debounce = - meter_desc_p [ meters ] . params ;
attachInterruptArg ( meter_desc_p [ meters ] . srcpin , SML_CounterIsr , & sml_cnt_index [ cindex ] , CHANGE ) ;
if ( digitalRead ( meter_desc_p [ meters ] . srcpin ) > 0 ) {
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
# ifdef SPECIAL_SS
2022-09-16 11:27:16 +01:00
if ( meter_desc_p [ meters ] . type = = ' m ' | | meter_desc_p [ meters ] . type = = ' M ' | | meter_desc_p [ meters ] . type = = ' k ' | | meter_desc_p [ meters ] . type = = ' p ' | | meter_desc_p [ meters ] . type = = ' R ' | | meter_desc_p [ meters ] . type = = ' v ' ) {
2020-06-01 14:17:32 +01:00
meter_ss [ meters ] = new TasmotaSerial ( meter_desc_p [ meters ] . srcpin , meter_desc_p [ meters ] . trxpin , 1 , 0 , TMSBSIZ ) ;
2019-09-03 09:39:51 +01:00
} else {
2020-06-01 14:17:32 +01:00
meter_ss [ meters ] = new TasmotaSerial ( meter_desc_p [ meters ] . srcpin , meter_desc_p [ meters ] . trxpin , 1 , 1 , TMSBSIZ ) ;
2019-09-03 09:39:51 +01:00
}
2020-05-15 14:30:32 +01:00
# else
2020-11-28 16:00:15 +00:00
# ifdef ESP8266
meter_ss [ meters ] = new TasmotaSerial ( meter_desc_p [ meters ] . srcpin , meter_desc_p [ meters ] . trxpin , 1 , 0 , TMSBSIZ ) ;
# 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
meter_ss [ meters ] = new SML_ESP32_SERIAL ( uart_index ) ;
if ( meter_desc_p [ meters ] . srcpin > = 0 ) {
if ( uart_index = = 0 ) { ClaimSerial ( ) ; }
uart_index - - ;
if ( uart_index < 0 ) uart_index = 0 ;
}
# else
2020-05-15 14:30:32 +01:00
meter_ss [ meters ] = 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 ;
2020-06-01 14:17:32 +01:00
meter_ss [ meters ] - > setRxBufferSize ( TMSBSIZ ) ;
2021-12-15 13:46:05 +00:00
# endif // USE_ESP32_SW_SERIAL
2020-11-28 16:00:15 +00:00
# endif // ESP32
2021-12-15 13:46:05 +00:00
# endif // SPECIAL_SS
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
if ( meter_desc_p [ meters ] . sopt & 0xf0 ) {
// new serial config
switch ( meter_desc_p [ meters ] . sopt > > 4 ) {
case 1 :
if ( ( meter_desc_p [ meters ] . sopt & 1 ) = = 1 ) smode = SERIAL_8N1 ;
else smode = SERIAL_8N2 ;
break ;
case 2 :
if ( ( meter_desc_p [ meters ] . sopt & 1 ) = = 1 ) smode = SERIAL_8E1 ;
else smode = SERIAL_8E2 ;
break ;
case 3 :
if ( ( meter_desc_p [ meters ] . sopt & 1 ) = = 1 ) smode = SERIAL_8O1 ;
else smode = SERIAL_8O2 ;
break ;
}
} else {
// depecated serial config
2020-11-15 15:09:05 +00:00
if ( meter_desc_p [ meters ] . sopt = = 2 ) {
2021-03-30 07:38:13 +01:00
smode = SERIAL_8N2 ;
}
if ( meter_desc_p [ meters ] . type = = ' M ' ) {
smode = SERIAL_8E1 ;
if ( meter_desc_p [ meters ] . sopt = = 2 ) {
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
2019-08-27 14:33:09 +01:00
if ( meter_ss [ meters ] - > begin ( meter_desc_p [ meters ] . params ) ) {
meter_ss [ meters ] - > flush ( ) ;
}
2019-11-25 18:08:05 +00:00
if ( meter_ss [ meters ] - > hardwareSerial ( ) ) {
2020-11-15 15:09:05 +00:00
Serial . begin ( meter_desc_p [ 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
# ifdef ESP32
meter_ss [ meters ] - > begin ( meter_desc_p [ meters ] . params , smode , meter_desc_p [ meters ] . srcpin , meter_desc_p [ meters ] . trxpin ) ;
2021-12-15 13:46:05 +00:00
//meter_ss[meters]->setRxBufferSize(TMSBSIZ);
2020-11-28 16:00:15 +00:00
# endif // ESP32
2019-08-27 14:33:09 +01:00
}
}
}
2020-02-04 06:07:56 +00:00
# ifdef USE_SML_SCRIPT_CMD
2021-10-23 07:07:51 +01:00
uint32_t sml_getv ( uint32_t sel ) {
if ( ! sel ) {
for ( uint8_t cnt = 0 ; cnt < SML_MAX_VARS ; cnt + + ) {
dvalid [ cnt ] = 0 ;
}
sel = 0 ;
} else {
if ( sel < 1 ) sel = 1 ;
sel = dvalid [ sel - 1 ] ;
}
return sel ;
}
2020-02-04 06:07:56 +00:00
uint32_t SML_SetBaud ( uint32_t meter , uint32_t br ) {
2021-12-15 13:46:05 +00:00
if ( meter < 1 | | meter > meters_used ) return 0 ;
2020-02-04 06:07:56 +00:00
meter - - ;
if ( ! meter_ss [ meter ] ) return 0 ;
2020-11-28 16:00:15 +00:00
# ifdef ESP8266
if ( meter_ss [ meter ] - > begin ( br ) ) {
meter_ss [ meter ] - > flush ( ) ;
}
if ( meter_ss [ meter ] - > hardwareSerial ( ) ) {
if ( meter_desc_p [ meter ] . type = = ' M ' ) {
Serial . begin ( br , SERIAL_8E1 ) ;
}
}
# endif // ESP8266
2020-06-01 14:17:32 +01:00
# ifdef ESP32
meter_ss [ meter ] - > flush ( ) ;
2020-07-05 07:55:34 +01:00
meter_ss [ meter ] - > updateBaudRate ( br ) ;
2020-07-15 08:44:52 +01:00
/*
if ( meter_desc_p [ meter ] . type = = ' M ' ) {
meter_ss [ meter ] - > begin ( br , SERIAL_8E1 , meter_desc_p [ meter ] . srcpin , meter_desc_p [ meter ] . trxpin ) ;
} else {
meter_ss [ meter ] - > begin ( br , SERIAL_8N1 , meter_desc_p [ meter ] . srcpin , meter_desc_p [ meter ] . trxpin ) ;
} */
2020-11-28 16:00:15 +00:00
# endif // ESP32
2020-02-04 06:07:56 +00:00
return 1 ;
}
2020-03-29 15:29:22 +01:00
uint32_t SML_Status ( uint32_t meter ) {
2022-09-16 11:27:16 +01:00
if ( meter < 1 | | meter > 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)
2020-03-29 15:29:22 +01:00
return sml_status [ meter ] ;
# else
return 0 ;
# endif
}
2022-07-20 13:25:40 +01:00
2020-02-04 06:07:56 +00:00
uint32_t SML_Write ( uint32_t meter , char * hstr ) {
2022-09-16 11:27:16 +01:00
if ( meter < 1 | | meter > meters_used ) return 0 ;
2020-02-04 06:07:56 +00:00
meter - - ;
if ( ! meter_ss [ meter ] ) return 0 ;
2022-09-16 11:27:16 +01:00
SML_Send_Seq ( meter , hstr ) ;
2020-02-04 06:07:56 +00:00
return 1 ;
}
2020-05-23 09:24:08 +01:00
2020-07-15 08:44:52 +01:00
uint32_t SML_Read ( int32_t meter , char * str , uint32_t slen ) {
2022-09-16 11:27:16 +01:00
uint8_t hflg = 0 ;
if ( meter < 0 ) {
meter = abs ( meter ) ;
hflg = 1 ;
2020-07-15 08:44:52 +01:00
}
2022-09-16 11:27:16 +01:00
if ( meter < 1 | | meter > meters_used ) return 0 ;
2020-07-15 08:44:52 +01:00
meter - - ;
if ( ! meter_ss [ meter ] ) return 0 ;
if ( ! meter_spos [ meter ] ) {
return 0 ;
}
2022-09-16 11:27:16 +01:00
smltbuf [ meter ] [ meter_spos [ meter ] ] = 0 ;
2020-07-15 08:44:52 +01:00
if ( ! hflg ) {
2022-09-16 11:27:16 +01:00
strlcpy ( str , ( char * ) & smltbuf [ meter ] [ 0 ] , slen ) ;
2020-07-15 08:44:52 +01:00
} else {
2022-09-16 11:27:16 +01:00
uint32_t index = 0 ;
for ( uint32_t cnt = 0 ; cnt < meter_spos [ meter ] ; cnt + + ) {
sprintf ( str , " %02x " , smltbuf [ meter ] [ cnt ] ) ;
str + = 2 ;
index + = 2 ;
if ( index > = slen - 2 ) break ;
2020-07-15 08:44:52 +01:00
}
}
2022-09-16 11:27:16 +01:00
meter_spos [ meter ] = 0 ;
2020-07-15 08:44:52 +01:00
return 1 ;
}
2020-05-23 09:24:08 +01:00
float SML_GetVal ( uint32_t index ) {
2021-12-28 11:38:39 +00:00
if ( index < 1 | | index > SML_MAX_VARS ) { index = 1 ; }
2022-09-16 11:27:16 +01:00
return 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 ) {
if ( index < 1 | | index > MAX_METERS ) { index = 1 ; }
return & meter_id [ index - 1 ] [ 0 ] ;
}
2022-02-11 07:01:07 +00:00
int32_t SML_Set_WStr ( uint32_t meter , char * hstr ) {
if ( meter < 1 | | meter > meters_used ) return - 1 ;
meter - - ;
if ( ! meter_ss [ meter ] ) return - 2 ;
script_meter_desc [ meter ] . script_str = hstr ;
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 ) ;
}
}
// 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-09-08 13:35:04 +01:00
for ( meters = 0 ; meters < meters_used ; meters + + ) {
if ( meter_desc_p [ meters ] . type = = ' c ' ) {
2019-08-27 14:33:09 +01:00
// poll for counters and debouce
2022-09-08 13:35:04 +01:00
if ( meter_desc_p [ meters ] . params > 0 ) {
if ( ctime - sml_counters [ cindex ] . sml_cnt_last_ts > meter_desc_p [ meters ] . params ) {
sml_counters [ cindex ] . sml_cnt_last_ts = ctime ;
2019-08-27 14:33:09 +01:00
2022-09-08 13:35:04 +01:00
if ( meter_desc_p [ meters ] . flag & 2 ) {
2019-08-27 14:33:09 +01:00
// analog mode, get next value
# ifdef ANALOG_OPTO_SENSOR
if ( ads1115_up ) {
int16_t val = adc . read_sample ( ) ;
2022-09-08 13:35:04 +01:00
if ( val > sml_counters [ cindex ] . ana_max ) sml_counters [ cindex ] . ana_max = val ;
if ( val < sml_counters [ cindex ] . ana_min ) sml_counters [ cindex ] . ana_min = val ;
sml_counters [ cindex ] . ana_curr = val ;
int16_t range = sml_counters [ cindex ] . ana_max - sml_counters [ cindex ] . ana_min ;
2019-08-27 14:33:09 +01:00
}
# endif
} else {
// poll digital input
uint8_t state ;
2022-09-08 13:35:04 +01:00
sml_counters [ cindex ] . sml_cnt_debounce < < = 1 ;
sml_counters [ cindex ] . sml_cnt_debounce | = ( digitalRead ( meter_desc_p [ meters ] . srcpin ) & 1 ) | 0x80 ;
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-09-08 13:35:04 +01:00
if ( cindex = = 0 ) SetDBGLed ( meter_desc_p [ meters ] . srcpin , DEBUG_CNT_LED1 ) ;
2019-08-27 14:33:09 +01:00
# endif
# ifdef DEBUG_CNT_LED2
2022-09-08 13:35:04 +01:00
if ( cindex = = 1 ) SetDBGLed ( meter_desc_p [ 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-09-08 13:35:04 +01:00
if ( cindex = = 0 ) SetDBGLed ( meter_desc_p [ meters ] . srcpin , DEBUG_CNT_LED1 ) ;
2019-08-27 14:33:09 +01:00
# endif
# ifdef DEBUG_CNT_LED2
2022-09-08 13:35:04 +01:00
if ( cindex = = 1 ) SetDBGLed ( meter_desc_p [ 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 ) {
2022-09-08 13:35:04 +01:00
InjektCounterValue ( sml_counters [ cindex ] . sml_cnt_old_state , RtcSettings . pulse_counter [ cindex ] , 60000.0 / ( float ) sml_counters [ cindex ] . sml_counter_pulsewidth ) ;
sml_counters [ cindex ] . sml_cnt_updated = 0 ;
2020-06-25 06:34:00 +01:00
}
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 ) {
2019-09-16 18:58:22 +01:00
sml_100ms_cnt + + ;
char * cp ;
2022-02-11 07:01:07 +00:00
for ( uint32_t cnt = sml_desc_cnt ; cnt < meters_used ; cnt + + ) {
if ( script_meter_desc [ cnt ] . trxpin > = 0 & & script_meter_desc [ cnt ] . txmem ) {
2021-06-05 10:47:09 +01:00
//AddLog(LOG_LEVEL_INFO, PSTR("100 ms>> %d - %s - %d"),sml_desc_cnt,script_meter_desc[cnt].txmem,script_meter_desc[cnt].tsecs);
2022-02-11 07:01:07 +00:00
if ( ( sml_100ms_cnt > = script_meter_desc [ cnt ] . tsecs ) ) {
sml_100ms_cnt = 0 ;
// check for scriptsync extra output
if ( script_meter_desc [ cnt ] . script_str ) {
cp = script_meter_desc [ cnt ] . script_str ;
script_meter_desc [ cnt ] . script_str = 0 ;
} else {
//AddLog(LOG_LEVEL_INFO, PSTR("100 ms>> 2"),cp);
2022-09-16 11:27:16 +01:00
if ( script_meter_desc [ cnt ] . max_index > 1 ) {
2022-02-11 07:01:07 +00:00
script_meter_desc [ cnt ] . index + + ;
if ( script_meter_desc [ cnt ] . index > = script_meter_desc [ cnt ] . max_index ) {
script_meter_desc [ cnt ] . index = 0 ;
sml_desc_cnt + + ;
}
2022-09-16 11:27:16 +01:00
cp = SML_Get_Sequence ( script_meter_desc [ cnt ] . txmem , script_meter_desc [ cnt ] . index ) ;
2022-02-11 07:01:07 +00:00
//SML_Send_Seq(cnt,cp);
} else {
cp = script_meter_desc [ cnt ] . txmem ;
//SML_Send_Seq(cnt,cp);
2019-09-16 18:58:22 +01:00
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-02-11 07:01:07 +00:00
if ( sml_desc_cnt > = meters_used ) {
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 {
sml_desc_cnt + + ;
}
2022-02-11 07:01:07 +00:00
if ( sml_desc_cnt > = meters_used ) {
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 ) {
char * cp = & meter_id [ mindex ] [ 0 ] ;
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-09-16 11:27:16 +01:00
if ( script_meter_desc [ meter ] . type = = ' m ' | | script_meter_desc [ meter ] . type = = ' M ' | | script_meter_desc [ meter ] . type = = ' k ' ) {
if ( script_meter_desc [ meter ] . type = = ' k ' ) {
// 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
}
2021-02-28 08:01:13 +00:00
if ( script_meter_desc [ meter ] . type = = ' o ' ) {
for ( uint32_t cnt = 0 ; cnt < slen ; cnt + + ) {
sbuff [ cnt ] | = ( CalcEvenParity ( sbuff [ cnt ] ) < < 7 ) ;
2019-10-20 07:23:27 +01:00
}
}
2021-02-28 08:01:13 +00:00
if ( script_meter_desc [ meter ] . type = = ' p ' ) {
* 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-04 06:47:21 +00:00
if ( script_meter_desc [ meter ] . trx_en . trxen ) {
digitalWrite ( script_meter_desc [ meter ] . trx_en . trxenpin , script_meter_desc [ meter ] . trx_en . trxenpol ^ 1 ) ;
}
2022-09-16 11:27:16 +01:00
meter_ss [ meter ] - > flush ( ) ;
2021-02-28 08:01:13 +00:00
meter_ss [ meter ] - > write ( sbuff , slen ) ;
2022-12-04 06:47:21 +00:00
if ( script_meter_desc [ meter ] . trx_en . trxen ) {
// must wait for all data sent
meter_ss [ meter ] - > flush ( ) ;
digitalWrite ( script_meter_desc [ meter ] . trx_en . trxenpin , script_meter_desc [ meter ] . trx_en . trxenpol ) ;
}
2021-02-25 13:36:37 +00:00
if ( dump2log ) {
2021-02-28 08:01:13 +00:00
# ifdef SML_DUMP_OUT_ALL
Hexdump ( sbuff , slen ) ;
# else
2021-02-25 13:36:37 +00:00
uint8_t type = meter_desc_p [ ( 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
uint8_t type = script_meter_desc [ meter ] . type ;
if ( ! dump2log & & ( type = = ' m ' | | type = = ' M ' | | type = = ' k ' ) ) {
AddLog ( LOG_LEVEL_INFO , PSTR ( " transmit index >> %d " ) , meter_desc_p [ meter ] . index ) ;
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
cp + + ;
2022-09-08 13:35:04 +01:00
uint8_t index = atoi ( cp ) ;
if ( ( index & 7 ) > meters_used ) index = 1 ;
if ( index > 0 & & meter_desc_p [ ( index & 7 ) - 1 ] . type = = ' c ' ) {
index = 0 ;
2019-10-20 07:23:27 +01:00
}
2022-09-08 13:35:04 +01:00
dump2log = index ;
ResponseTime_P ( PSTR ( " , \" SML \" :{ \" CMD \" : \" dump: %d \" }} " ) , dump2log ) ;
} 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 ;
for ( uint8_t meters = 0 ; meters < meters_used ; meters + + ) {
if ( meter_desc_p [ meters ] . type = = ' c ' ) {
InjektCounterValue ( meters , RtcSettings . pulse_counter [ cindex ] , 0.0 ) ;
2019-08-27 14:33:09 +01:00
cindex + + ;
}
}
}
2022-09-08 13:35:04 +01:00
ResponseTime_P ( PSTR ( " , \" SML \" :{ \" CMD \" : \" counter%d: %d \" }} " ) , index , RtcSettings . pulse_counter [ index - 1 ] ) ;
2019-08-27 14:33:09 +01:00
} else if ( * cp = = ' r ' ) {
// 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 ( ) ;
2022-01-20 19:19:50 +00:00
} else if ( * cp = = ' m ' ) {
// meter number for serial activity
cp + + ;
if ( ! isdigit ( * cp ) ) {
ResponseTime_P ( PSTR ( " , \" SML \" :{ \" CMD \" : \" ser_act_meter_num: %d \" }} " ) , ser_act_meter_num ) ;
} else {
ser_act_meter_num = atoi ( cp ) ;
ResponseTime_P ( PSTR ( " , \" SML \" :{ \" CMD \" : \" ser_act_meter_num: %d \" }} " ) , ser_act_meter_num ) ;
}
} else if ( * cp = = ' l ' ) {
// serial activity LED-GPIO
cp + + ;
if ( ! isdigit ( * cp ) ) {
ResponseTime_P ( PSTR ( " , \" SML \" :{ \" CMD \" : \" ser_act_LED_pin: %d \" }} " ) , ser_act_LED_pin ) ;
} else {
ser_act_LED_pin = atoi ( cp ) ;
2022-01-24 19:18:07 +00:00
if ( Gpio_used ( ser_act_LED_pin ) ) {
AddLog ( LOG_LEVEL_INFO , PSTR ( " SML: Error: Duplicate GPIO %d defined. Not usable for LED. " ) , ser_act_LED_pin ) ;
ser_act_LED_pin = 255 ;
}
2022-01-20 19:19:50 +00:00
if ( ser_act_LED_pin ! = 255 ) {
pinMode ( ser_act_LED_pin , OUTPUT ) ;
}
ResponseTime_P ( PSTR ( " , \" SML \" :{ \" CMD \" : \" ser_act_LED_pin: %d \" }} " ) , ser_act_LED_pin ) ;
}
2019-08-27 14:33:09 +01:00
} else {
serviced = false ;
}
}
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
int dec = ( int ) rate ;
int frac = ( int ) ( ( rate - ( float ) dec ) * 1000.0 ) ;
2022-09-08 13:35:04 +01:00
snprintf ( ( char * ) & smltbuf [ meter ] [ 0 ] , SML_BSIZ , " 1-0:1.8.0*255(%d) " , counter ) ;
2022-02-11 22:29:30 +00:00
SML_Decode ( meter ) ;
2022-09-08 13:35:04 +01:00
snprintf ( ( char * ) & smltbuf [ meter ] [ 0 ] , SML_BSIZ , " 1-0:1.7.0*255(%d.%d) " , dec , frac ) ;
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
}
}
2019-09-03 09:39:51 +01:00
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 :
SML_Counter_Poll ( ) ;
if ( dump2log ) Dump2log ( ) ;
2021-12-15 13:46:05 +00:00
else {
SML_Poll ( ) ;
}
2019-08-27 14:33:09 +01:00
break ;
2020-05-15 14:30:32 +01:00
// case FUNC_EVERY_50_MSECOND:
// if (dump2log) Dump2log();
// else SML_Poll();
// break;
2019-09-03 09:39:51 +01:00
# ifdef USE_SCRIPT
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 ) ) {
2020-05-15 14:30:32 +01:00
SML_Check_Send ( ) ;
}
2019-08-27 14:33:09 +01:00
break ;
2019-09-03 09:39:51 +01:00
# endif // USE_SCRIPT
2019-08-27 14:33:09 +01:00
case FUNC_JSON_APPEND :
2022-11-28 06:41:37 +00:00
if ( sml_options & SML_OPTIONS_JSON_ENABLE ) {
2021-02-20 16:32:01 +00:00
SML_Show ( 1 ) ;
}
2019-08-27 14:33:09 +01:00
break ;
# ifdef USE_WEBSERVER
case FUNC_WEB_SENSOR :
SML_Show ( 0 ) ;
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 :
SML_CounterSaveState ( ) ;
break ;
}
return result ;
}
# endif // USE_SML