From b6d366870dd1b48eff8024f5daa241389cc126ba Mon Sep 17 00:00:00 2001 From: Barbudor Date: Wed, 10 Feb 2021 21:08:59 +0100 Subject: [PATCH] fix teleinfo standard mode --- lib/lib_div/LibTeleinfo/src/LibTeleinfo.cpp | 62 ++++++++++++++--- lib/lib_div/LibTeleinfo/src/LibTeleinfo.h | 19 ++---- tasmota/my_user_config.h | 1 - tasmota/xnrg_15_teleinfo.ino | 76 ++++++++++++++------- 4 files changed, 109 insertions(+), 49 deletions(-) diff --git a/lib/lib_div/LibTeleinfo/src/LibTeleinfo.cpp b/lib/lib_div/LibTeleinfo/src/LibTeleinfo.cpp index 3d427676b..93ad25924 100644 --- a/lib/lib_div/LibTeleinfo/src/LibTeleinfo.cpp +++ b/lib/lib_div/LibTeleinfo/src/LibTeleinfo.cpp @@ -69,12 +69,12 @@ void TInfo::init(_Mode_e mode) // We're in INIT in term of receive data _state = TINFO_INIT; - if ( mode == TINFO_MODE_STANDARD ) { + _mode = mode; + if ( _mode == TINFO_MODE_STANDARD ) { _separator = TINFO_HT; } else { _separator = ' '; } - } /* ====================================================================== @@ -215,6 +215,8 @@ ValueList * TInfo::valueAdd(char * name, char * value, uint8_t checksum, uint8_t TI_Debug(F("' Not added bad checksum calculated '")); TI_Debug((char) thischeck); TI_Debugln(F("'")); + AddLog(1, PSTR("LibTeleinfo::valueAdd Err checksum 0x%02X != 0x%02X"), thischeck, checksum); + } else { // Got one and all seems good ? if (me && lgname && lgvalue && checksum) { @@ -640,10 +642,27 @@ Input : label name label timestamp Output : checksum Comments: return '\0' in case of error + +Group format in legacy mode (Mode Historique) - Last space not included in checksum +LF etiquette SP donnee SP Chk CR +0A 20 20 0D + \____check________/ + + +Group format in standard mode with timestamp (horodatage) - Last HT included in checksum +LF etiquette HT horodatage HT donnee HT Chk CR +0A 09 09 09 0D + \____________checkum_______________/ + +Group format in standard mode without timestamp (horodatage) - Last HT included in checksum +LF etiquette HT donnee HT Chk CR +0A 09 09 0D + \_____checkum________/ + ====================================================================== */ unsigned char TInfo::calcChecksum(char *etiquette, char *valeur, char * horodate) { - uint8_t sum = _separator ; // Somme des codes ASCII du message + un separateur + uint8_t sum = (_mode == TINFO_MODE_HISTORIQUE) ? _separator : (2 * _separator); // Somme des codes ASCII du message + 2 separateurs // avoid dead loop, always check all is fine if (etiquette && valeur) { @@ -767,15 +786,19 @@ ValueList * TInfo::checkLine(char * pline) int sep =0; bool hasts = false ; // Assume timestamp on line - if (pline==NULL) + if (pline==NULL) { + //AddLog(3, PSTR("LibTeleinfo: Error pline==NULL")); return NULL; + } len = strlen(pline); // a line should be at least 7 Char // 2 Label + Space + 1 etiquette + space + checksum + \r - if ( len < 7 || len >= TINFO_BUFSIZE) + if ( len < 7 || len >= TINFO_BUFSIZE) { + //AddLog(3, PSTR("LibTeleinfo: Error len < 7 || len >= TINFO_BUFSIZE")); return NULL; + } p = &buff[0]; sep = 0; @@ -852,12 +875,14 @@ ValueList * TInfo::checkLine(char * pline) // Always check to avoid bad behavior if(strlen(ptok) && strlen(pvalue)) { // Is checksum is OK - if ( calcChecksum(ptok,pvalue,pts) == checksum) { + char calc_checksum = calcChecksum(ptok,pvalue,pts); + if ( calc_checksum == checksum) { // In case we need to do things on specific labels customLabel(ptok, pvalue, &flags); // Add value to linked lists of values - ValueList * me = valueAdd(ptok, pvalue, checksum, &flags); + //AddLog(3, PSTR("LibTeleinfo: %s = %s"), ptok, pvalue); + ValueList * me = valueAdd(ptok, pvalue, checksum, &flags, pts); // value correctly added/changed if ( me ) { @@ -872,6 +897,10 @@ ValueList * TInfo::checkLine(char * pline) } } } + else + { + AddLog(1, PSTR("LibTeleinfo::checkLine Err checksum 0x%02X != 0x%02X"), calc_checksum, checksum); + } } } } @@ -899,6 +928,7 @@ _State_e TInfo::process(char c) switch (c) { // start of transmission ??? case TINFO_STX: + //AddLog(3, PSTR("LibTeleinfo: case TINFO_STX <<<<<<<<<<<<<<<<<<")); // Clear buffer, begin to store in it clearBuffer(); @@ -909,12 +939,14 @@ _State_e TInfo::process(char c) // We were waiting fo this one ? if (_state == TINFO_INIT || _state == TINFO_WAIT_STX ) { TI_Debugln(F("TINFO_WAIT_ETX")); + //AddLog(3, PSTR("LibTeleinfo: state => TINFO_WAIT_ETX")); _state = TINFO_WAIT_ETX; } break; // End of transmission ? case TINFO_ETX: + //AddLog(3, PSTR("LibTeleinfo: case TINFO_ETX >>>>>>>>>>>>>>>>>>")); // Normal working mode ? if (_state == TINFO_READY) { @@ -940,12 +972,14 @@ _State_e TInfo::process(char c) // We were waiting fo this one ? if (_state == TINFO_WAIT_ETX) { TI_Debugln(F("TINFO_READY")); + //AddLog(3, PSTR("LibTeleinfo: state => TINFO_READY")); _state = TINFO_READY; - } + } else if ( _state == TINFO_INIT) { TI_Debugln(F("TINFO_WAIT_STX")); + //AddLog(3, PSTR("LibTeleinfo: state => TINFO_WAIT_STX")); _state = TINFO_WAIT_STX ; - } + } break; @@ -953,10 +987,13 @@ _State_e TInfo::process(char c) case TINFO_SGR: // Do nothing we'll work at end of group // we can safely ignore this char + //AddLog(3, PSTR("LibTeleinfo: case TINFO_SGR _recv_idx=%d"), _recv_idx); break; // End of group \r ? case TINFO_EGR: + //AddLog(3, PSTR("LibTeleinfo: case TINFO_EGR _recv_idx=%d"), _recv_idx); + // Are we ready to process ? if (_state == TINFO_READY) { // Store data recceived (we'll need it) @@ -967,13 +1004,14 @@ _State_e TInfo::process(char c) memset(&_recv_buff[_recv_idx], 0, TINFO_BUFSIZE-_recv_idx); // check the group we've just received + //AddLog(3, PSTR("LibTeleinfo: Group received %d bytes %s"), _recv_idx, _recv_buff); checkLine(_recv_buff) ; // Whatever error or not, we done clearBuffer(); } break; - + // other char ? default: { @@ -982,8 +1020,10 @@ _State_e TInfo::process(char c) // If buffer is not full, Store data if ( _recv_idx < TINFO_BUFSIZE) _recv_buff[_recv_idx++]=c; - else + else { + AddLog(1, PSTR("LibTeleinfo: _recv_idx = %d/%d buffer overflow"), _recv_idx, TINFO_BUFSIZE); clearBuffer(); + } } } break; diff --git a/lib/lib_div/LibTeleinfo/src/LibTeleinfo.h b/lib/lib_div/LibTeleinfo/src/LibTeleinfo.h index cae1815ac..e5cb81486 100644 --- a/lib/lib_div/LibTeleinfo/src/LibTeleinfo.h +++ b/lib/lib_div/LibTeleinfo/src/LibTeleinfo.h @@ -46,12 +46,13 @@ // I prefix debug macro to be sure to use specific for THIS library // debugging, this should not interfere with main sketch or other // libraries +void AddLog(uint32_t loglevel, PGM_P formatP, ...); +#define TI_Errorf(...) AddLog(LOG_LEVEL_ERROR, __VA_ARGS__); #ifdef TI_DEBUG // Tasmota build #ifdef CODE_IMAGE_STR - #define TI_Debug(x) AddLog_P2(LOG_LEVEL_DEBUG, x); - #define TI_Debugln(x) AddLog_P2(LOG_LEVEL_DEBUG, x); - #define TI_Debugf(...) AddLog_P2(LOG_LEVEL_DEBUG, __VA_ARGS__); + // Only TI_Debugf() can work with Tasmota + #define TI_Debugf(...) AddLog(LOG_LEVEL_DEBUG, __VA_ARGS__); #define TI_Debugflush {} #else #ifdef ESP8266 @@ -86,9 +87,7 @@ typedef struct _ValueList ValueList; struct _ValueList { ValueList *next; // next element -//#ifdef USE_TELEINFO_STANDARD time_t ts; // TimeStamp of data if any -//#endif uint8_t checksum;// checksum uint8_t flags; // specific flags char * name; // LABEL of value name @@ -120,13 +119,8 @@ enum _State_e { #define TINFO_FLAGS_ALERT 0x80 /* This will generate an alert */ // Local buffer for one line of teleinfo -// maximum size, I think it should be enought -#ifdef USE_TELEINFO_STANDARD -// Linky and standard mode may have longer lines +// maximum size for Standard #define TINFO_BUFSIZE 128 -#else -#define TINFO_BUFSIZE 64 -#endif // Teleinfo start and end of frame characters #define TINFO_STX 0x02 @@ -144,7 +138,7 @@ class TInfo { public: TInfo(); - void init(_Mode_e mode = TINFO_MODE_HISTORIQUE); + void init(_Mode_e mode); // mode MUST be specified _State_e process (char c); void attachADPS(void (*_fn_ADPS)(uint8_t phase)); void attachData(void (*_fn_data)(ValueList * valueslist, uint8_t state)); @@ -168,6 +162,7 @@ class TInfo void customLabel( char * plabel, char * pvalue, uint8_t * pflags) ; ValueList * checkLine(char * pline) ; + _Mode_e _mode; // Teleinfo mode (legacy/historique vs standard) _State_e _state; // Teleinfo machine state ValueList _valueslist; // Linked list of teleinfo values char _recv_buff[TINFO_BUFSIZE]; // line receive buffer diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 9ccf11658..4fd5cc6b4 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -702,7 +702,6 @@ #define LE01MR_ADDR 1 // LE-01MR modbus address (default: 0x01) #define USE_BL0940 // Add support for BL0940 Energy monitor as used in Blitzwolf SHP-10 (+1k6 code) //#define USE_TELEINFO // Add support for Teleinfo via serial RX interface (+5k2 code, +168 RAM + SmartMeter LinkedList Values RAM) -// #define USE_TELEINFO_STANDARD // Use standard mode (9600 bps) else it's historical mode (1200 bps) //#define USE_IEM3000 // Add support for Schneider Electric iEM3000-Modbus series energy monitor (+0k8 code) #define IEM3000_SPEED 19200 // iEM3000-Modbus RS485 serial speed (default: 19200 baud) #define IEM3000_ADDR 1 // iEM3000-Modbus modbus address (default: 0x01) diff --git a/tasmota/xnrg_15_teleinfo.ino b/tasmota/xnrg_15_teleinfo.ino index 7b5c52a43..ed1aebb45 100755 --- a/tasmota/xnrg_15_teleinfo.ino +++ b/tasmota/xnrg_15_teleinfo.ino @@ -105,6 +105,10 @@ const char kLabel[] PROGMEM = "|DEMAIN" ; +#define TELEINFO_SERIAL_BUFFER_STANDARD 512 // Receive buffer size for Standard mode +#define TELEINFO_SERIAL_BUFFER_HISTORIQUE 512 // Receive buffer size for Legacy mode +#define TELEINFO_PROCESS_BUFFER 32 // Local processing buffer + TInfo tinfo; // Teleinfo object TasmotaSerial *TInfoSerial = nullptr; _Mode_e tinfo_mode = TINFO_MODE_HISTORIQUE; @@ -202,19 +206,19 @@ void DataCallback(struct _ValueList * me, uint8_t flags) break; } } - AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: Tarif changed, now '%s' (%d)"), me->value, tarif); + AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: Tariff changed, now '%s' (%d)"), me->value, tarif); } // Current tariff (standard is in clear text in value) else if (ilabel == LABEL_LTARF) { - AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: Tarif name changed, now '%s'"), me->value); + AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: Tariff name changed, now '%s'"), me->value); } // Current tariff (standard index is is in clear text in value) else if (ilabel == LABEL_NTARF) { tarif = atoi(me->value); - AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: Tarif index changed, now '%d'"), tarif); + AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: Tariff index changed, now '%d'"), tarif); } @@ -409,7 +413,7 @@ void NewFrameCallback(struct _ValueList * me) ResponseJsonEnd(); // Publish adding ADCO serial number into the topic // Need setOption4 to be enabled - MqttPublishPrefixTopic_P(RESULT_OR_TELE, serialNumber, false); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, serialNumber, false); } } @@ -437,21 +441,23 @@ Comments: - void TInfoInit(void) { int baudrate; + int serial_buffer_size; + // SetOption102 - Set Baud rate for Teleinfo serial communication (0 = 1200 or 1 = 9600) if (Settings.flag4.teleinfo_baudrate) { baudrate = 9600; tinfo_mode = TINFO_MODE_STANDARD; - } else { + serial_buffer_size = TELEINFO_SERIAL_BUFFER_STANDARD; + } else { baudrate = 1200; tinfo_mode = TINFO_MODE_HISTORIQUE; + serial_buffer_size = TELEINFO_SERIAL_BUFFER_HISTORIQUE; } - AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: inferface speed %d bps"),baudrate); - if (PinUsed(GPIO_TELEINFO_RX)) { uint8_t rx_pin = Pin(GPIO_TELEINFO_RX); - AddLog(LOG_LEVEL_INFO, PSTR("TIC: RX on GPIO%d"), rx_pin); + AddLog(LOG_LEVEL_INFO, PSTR("TIC: RX on GPIO%d, baudrate %d"), rx_pin, baudrate); // Enable Teleinfo pin used, control it if (PinUsed(GPIO_TELEINFO_ENABLE)) { @@ -465,18 +471,20 @@ void TInfoInit(void) #ifdef ESP8266 // Allow GPIO3 AND GPIO13 with hardware fallback to 2 - TInfoSerial = new TasmotaSerial(rx_pin, -1, 2); + // Set buffer to nnn char to support 250ms loop at 9600 baud + TInfoSerial = new TasmotaSerial(rx_pin, -1, 2, 0, serial_buffer_size); //pinMode(rx_pin, INPUT_PULLUP); #endif // ESP8266 #ifdef ESP32 - TInfoSerial = new TasmotaSerial(rx_pin, -1, 1); + // Set buffer to nnn char to support 250ms loop at 9600 baud + TInfoSerial = new TasmotaSerial(rx_pin, -1, 1, 0, serial_buffer_size); #endif // ESP32 // Trick here even using SERIAL_7E1 or TS_SERIAL_7E1 // this is not working, need to call SetSerialConfig after if (TInfoSerial->begin(baudrate)) { - + #ifdef ESP8266 if (TInfoSerial->hardwareSerial() ) { ClaimSerial(); @@ -494,6 +502,7 @@ void TInfoInit(void) } #endif // ESP8266 #ifdef ESP32 + SetSerialConfig(TS_SERIAL_7E1); AddLog(LOG_LEVEL_INFO, PSTR("TIC: using ESP32 hardware serial")); #endif // ESP32 // Init teleinfo @@ -510,30 +519,47 @@ void TInfoInit(void) } /* ====================================================================== -Function: TInfoEvery250ms -Purpose : Tasmota callback executed every 250ms +Function: TInfoProcess +Purpose : Tasmota callback executed often enough to read serial Input : - Output : - Comments: - ====================================================================== */ -void TInfoEvery250ms(void) +//#define MEASURE_PERF // Define to enable performance measurments + +void TInfoProcess(void) { + static char buff[TELEINFO_PROCESS_BUFFER]; + #ifdef MEASURE_PERF + static unsigned long max_time = 0; + unsigned long duration = millis(); + static int max_size = 0; + int tmp_size = 0; + #endif + if (!tinfo_found) { return; } - if (TInfoSerial->available()) { - unsigned long start = millis(); - char c; - - // We received some data, process but never more than 100ms ? - while (TInfoSerial->available()>8 && millis()-start < 100) { - // get char - c = TInfoSerial->read(); + int size = TInfoSerial->read(buff,TELEINFO_PROCESS_BUFFER); + while ( size ) { + #ifdef MEASURE_PERF + tmp_size += size; + #endif + // Process as many bytes as available in serial buffer + for(int i = 0; size; i++, size--) + { + buff[i] &= 0x7F; // data processing - tinfo.process(c & 0x7F); + tinfo.process(buff[i]); } + size = TInfoSerial->read(buff,TELEINFO_PROCESS_BUFFER); } + #ifdef MEASURE_PERF + duration = millis()-duration; + if (duration > max_time) { max_time = duration; AddLog(LOG_LEVEL_INFO,PSTR("TIC: max_time=%lu"), max_time); } + if (tmp_size > max_size) { max_size = tmp_size; AddLog(LOG_LEVEL_INFO,PSTR("TIC: max_size=%d"), max_size); } + #endif } /* ====================================================================== @@ -641,7 +667,7 @@ bool Xnrg15(uint8_t function) switch (function) { case FUNC_EVERY_250_MSECOND: - TInfoEvery250ms(); + TInfoProcess(); break; case FUNC_JSON_APPEND: TInfoShow(1); @@ -662,4 +688,4 @@ bool Xnrg15(uint8_t function) } #endif // USE_TELEINFO -#endif // USE_ENERGY_SENSOR \ No newline at end of file +#endif // USE_ENERGY_SENSOR