Merge pull request #10911 from Joda89/pr_teleinfo

fix teleinfo standard mode
This commit is contained in:
Theo Arends 2021-02-11 09:03:31 +01:00 committed by GitHub
commit 5cb4d48fd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 109 additions and 49 deletions

View File

@ -69,12 +69,12 @@ void TInfo::init(_Mode_e mode)
// We're in INIT in term of receive data // We're in INIT in term of receive data
_state = TINFO_INIT; _state = TINFO_INIT;
if ( mode == TINFO_MODE_STANDARD ) { _mode = mode;
if ( _mode == TINFO_MODE_STANDARD ) {
_separator = TINFO_HT; _separator = TINFO_HT;
} else { } else {
_separator = ' '; _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(F("' Not added bad checksum calculated '"));
TI_Debug((char) thischeck); TI_Debug((char) thischeck);
TI_Debugln(F("'")); TI_Debugln(F("'"));
AddLog(1, PSTR("LibTeleinfo::valueAdd Err checksum 0x%02X != 0x%02X"), thischeck, checksum);
} else { } else {
// Got one and all seems good ? // Got one and all seems good ?
if (me && lgname && lgvalue && checksum) { if (me && lgname && lgvalue && checksum) {
@ -640,10 +642,27 @@ Input : label name
label timestamp label timestamp
Output : checksum Output : checksum
Comments: return '\0' in case of error 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) 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 // avoid dead loop, always check all is fine
if (etiquette && valeur) { if (etiquette && valeur) {
@ -767,15 +786,19 @@ ValueList * TInfo::checkLine(char * pline)
int sep =0; int sep =0;
bool hasts = false ; // Assume timestamp on line bool hasts = false ; // Assume timestamp on line
if (pline==NULL) if (pline==NULL) {
//AddLog(3, PSTR("LibTeleinfo: Error pline==NULL"));
return NULL; return NULL;
}
len = strlen(pline); len = strlen(pline);
// a line should be at least 7 Char // a line should be at least 7 Char
// 2 Label + Space + 1 etiquette + space + checksum + \r // 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; return NULL;
}
p = &buff[0]; p = &buff[0];
sep = 0; sep = 0;
@ -852,12 +875,14 @@ ValueList * TInfo::checkLine(char * pline)
// Always check to avoid bad behavior // Always check to avoid bad behavior
if(strlen(ptok) && strlen(pvalue)) { if(strlen(ptok) && strlen(pvalue)) {
// Is checksum is OK // 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 // In case we need to do things on specific labels
customLabel(ptok, pvalue, &flags); customLabel(ptok, pvalue, &flags);
// Add value to linked lists of values // 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 // value correctly added/changed
if ( me ) { 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) { switch (c) {
// start of transmission ??? // start of transmission ???
case TINFO_STX: case TINFO_STX:
//AddLog(3, PSTR("LibTeleinfo: case TINFO_STX <<<<<<<<<<<<<<<<<<"));
// Clear buffer, begin to store in it // Clear buffer, begin to store in it
clearBuffer(); clearBuffer();
@ -909,12 +939,14 @@ _State_e TInfo::process(char c)
// We were waiting fo this one ? // We were waiting fo this one ?
if (_state == TINFO_INIT || _state == TINFO_WAIT_STX ) { if (_state == TINFO_INIT || _state == TINFO_WAIT_STX ) {
TI_Debugln(F("TINFO_WAIT_ETX")); TI_Debugln(F("TINFO_WAIT_ETX"));
//AddLog(3, PSTR("LibTeleinfo: state => TINFO_WAIT_ETX"));
_state = TINFO_WAIT_ETX; _state = TINFO_WAIT_ETX;
} }
break; break;
// End of transmission ? // End of transmission ?
case TINFO_ETX: case TINFO_ETX:
//AddLog(3, PSTR("LibTeleinfo: case TINFO_ETX >>>>>>>>>>>>>>>>>>"));
// Normal working mode ? // Normal working mode ?
if (_state == TINFO_READY) { if (_state == TINFO_READY) {
@ -940,12 +972,14 @@ _State_e TInfo::process(char c)
// We were waiting fo this one ? // We were waiting fo this one ?
if (_state == TINFO_WAIT_ETX) { if (_state == TINFO_WAIT_ETX) {
TI_Debugln(F("TINFO_READY")); TI_Debugln(F("TINFO_READY"));
//AddLog(3, PSTR("LibTeleinfo: state => TINFO_READY"));
_state = TINFO_READY; _state = TINFO_READY;
} }
else if ( _state == TINFO_INIT) { else if ( _state == TINFO_INIT) {
TI_Debugln(F("TINFO_WAIT_STX")); TI_Debugln(F("TINFO_WAIT_STX"));
//AddLog(3, PSTR("LibTeleinfo: state => TINFO_WAIT_STX"));
_state = TINFO_WAIT_STX ; _state = TINFO_WAIT_STX ;
} }
break; break;
@ -953,10 +987,13 @@ _State_e TInfo::process(char c)
case TINFO_SGR: case TINFO_SGR:
// Do nothing we'll work at end of group // Do nothing we'll work at end of group
// we can safely ignore this char // we can safely ignore this char
//AddLog(3, PSTR("LibTeleinfo: case TINFO_SGR _recv_idx=%d"), _recv_idx);
break; break;
// End of group \r ? // End of group \r ?
case TINFO_EGR: case TINFO_EGR:
//AddLog(3, PSTR("LibTeleinfo: case TINFO_EGR _recv_idx=%d"), _recv_idx);
// Are we ready to process ? // Are we ready to process ?
if (_state == TINFO_READY) { if (_state == TINFO_READY) {
// Store data recceived (we'll need it) // 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); memset(&_recv_buff[_recv_idx], 0, TINFO_BUFSIZE-_recv_idx);
// check the group we've just received // check the group we've just received
//AddLog(3, PSTR("LibTeleinfo: Group received %d bytes %s"), _recv_idx, _recv_buff);
checkLine(_recv_buff) ; checkLine(_recv_buff) ;
// Whatever error or not, we done // Whatever error or not, we done
clearBuffer(); clearBuffer();
} }
break; break;
// other char ? // other char ?
default: default:
{ {
@ -982,8 +1020,10 @@ _State_e TInfo::process(char c)
// If buffer is not full, Store data // If buffer is not full, Store data
if ( _recv_idx < TINFO_BUFSIZE) if ( _recv_idx < TINFO_BUFSIZE)
_recv_buff[_recv_idx++]=c; _recv_buff[_recv_idx++]=c;
else else {
AddLog(1, PSTR("LibTeleinfo: _recv_idx = %d/%d buffer overflow"), _recv_idx, TINFO_BUFSIZE);
clearBuffer(); clearBuffer();
}
} }
} }
break; break;

View File

@ -46,12 +46,13 @@
// I prefix debug macro to be sure to use specific for THIS library // I prefix debug macro to be sure to use specific for THIS library
// debugging, this should not interfere with main sketch or other // debugging, this should not interfere with main sketch or other
// libraries // libraries
void AddLog(uint32_t loglevel, PGM_P formatP, ...);
#define TI_Errorf(...) AddLog(LOG_LEVEL_ERROR, __VA_ARGS__);
#ifdef TI_DEBUG #ifdef TI_DEBUG
// Tasmota build // Tasmota build
#ifdef CODE_IMAGE_STR #ifdef CODE_IMAGE_STR
#define TI_Debug(x) AddLog_P2(LOG_LEVEL_DEBUG, x); // Only TI_Debugf() can work with Tasmota
#define TI_Debugln(x) AddLog_P2(LOG_LEVEL_DEBUG, x); #define TI_Debugf(...) AddLog(LOG_LEVEL_DEBUG, __VA_ARGS__);
#define TI_Debugf(...) AddLog_P2(LOG_LEVEL_DEBUG, __VA_ARGS__);
#define TI_Debugflush {} #define TI_Debugflush {}
#else #else
#ifdef ESP8266 #ifdef ESP8266
@ -86,9 +87,7 @@ typedef struct _ValueList ValueList;
struct _ValueList struct _ValueList
{ {
ValueList *next; // next element ValueList *next; // next element
//#ifdef USE_TELEINFO_STANDARD
time_t ts; // TimeStamp of data if any time_t ts; // TimeStamp of data if any
//#endif
uint8_t checksum;// checksum uint8_t checksum;// checksum
uint8_t flags; // specific flags uint8_t flags; // specific flags
char * name; // LABEL of value name char * name; // LABEL of value name
@ -120,13 +119,8 @@ enum _State_e {
#define TINFO_FLAGS_ALERT 0x80 /* This will generate an alert */ #define TINFO_FLAGS_ALERT 0x80 /* This will generate an alert */
// Local buffer for one line of teleinfo // Local buffer for one line of teleinfo
// maximum size, I think it should be enought // maximum size for Standard
#ifdef USE_TELEINFO_STANDARD
// Linky and standard mode may have longer lines
#define TINFO_BUFSIZE 128 #define TINFO_BUFSIZE 128
#else
#define TINFO_BUFSIZE 64
#endif
// Teleinfo start and end of frame characters // Teleinfo start and end of frame characters
#define TINFO_STX 0x02 #define TINFO_STX 0x02
@ -144,7 +138,7 @@ class TInfo
{ {
public: public:
TInfo(); TInfo();
void init(_Mode_e mode = TINFO_MODE_HISTORIQUE); void init(_Mode_e mode); // mode MUST be specified
_State_e process (char c); _State_e process (char c);
void attachADPS(void (*_fn_ADPS)(uint8_t phase)); void attachADPS(void (*_fn_ADPS)(uint8_t phase));
void attachData(void (*_fn_data)(ValueList * valueslist, uint8_t state)); 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) ; void customLabel( char * plabel, char * pvalue, uint8_t * pflags) ;
ValueList * checkLine(char * pline) ; ValueList * checkLine(char * pline) ;
_Mode_e _mode; // Teleinfo mode (legacy/historique vs standard)
_State_e _state; // Teleinfo machine state _State_e _state; // Teleinfo machine state
ValueList _valueslist; // Linked list of teleinfo values ValueList _valueslist; // Linked list of teleinfo values
char _recv_buff[TINFO_BUFSIZE]; // line receive buffer char _recv_buff[TINFO_BUFSIZE]; // line receive buffer

View File

@ -702,7 +702,6 @@
#define LE01MR_ADDR 1 // LE-01MR modbus address (default: 0x01) #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_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 // 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 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_SPEED 19200 // iEM3000-Modbus RS485 serial speed (default: 19200 baud)
#define IEM3000_ADDR 1 // iEM3000-Modbus modbus address (default: 0x01) #define IEM3000_ADDR 1 // iEM3000-Modbus modbus address (default: 0x01)

View File

@ -105,6 +105,10 @@ const char kLabel[] PROGMEM =
"|DEMAIN" "|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 TInfo tinfo; // Teleinfo object
TasmotaSerial *TInfoSerial = nullptr; TasmotaSerial *TInfoSerial = nullptr;
_Mode_e tinfo_mode = TINFO_MODE_HISTORIQUE; _Mode_e tinfo_mode = TINFO_MODE_HISTORIQUE;
@ -202,19 +206,19 @@ void DataCallback(struct _ValueList * me, uint8_t flags)
break; 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) // Current tariff (standard is in clear text in value)
else if (ilabel == LABEL_LTARF) 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) // Current tariff (standard index is is in clear text in value)
else if (ilabel == LABEL_NTARF) else if (ilabel == LABEL_NTARF)
{ {
tarif = atoi(me->value); 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(); ResponseJsonEnd();
// Publish adding ADCO serial number into the topic // Publish adding ADCO serial number into the topic
// Need setOption4 to be enabled // 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) void TInfoInit(void)
{ {
int baudrate; int baudrate;
int serial_buffer_size;
// SetOption102 - Set Baud rate for Teleinfo serial communication (0 = 1200 or 1 = 9600) // SetOption102 - Set Baud rate for Teleinfo serial communication (0 = 1200 or 1 = 9600)
if (Settings.flag4.teleinfo_baudrate) { if (Settings.flag4.teleinfo_baudrate) {
baudrate = 9600; baudrate = 9600;
tinfo_mode = TINFO_MODE_STANDARD; tinfo_mode = TINFO_MODE_STANDARD;
} else { serial_buffer_size = TELEINFO_SERIAL_BUFFER_STANDARD;
} else {
baudrate = 1200; baudrate = 1200;
tinfo_mode = TINFO_MODE_HISTORIQUE; 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)) { if (PinUsed(GPIO_TELEINFO_RX)) {
uint8_t rx_pin = Pin(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 // Enable Teleinfo pin used, control it
if (PinUsed(GPIO_TELEINFO_ENABLE)) { if (PinUsed(GPIO_TELEINFO_ENABLE)) {
@ -465,18 +471,20 @@ void TInfoInit(void)
#ifdef ESP8266 #ifdef ESP8266
// Allow GPIO3 AND GPIO13 with hardware fallback to 2 // 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); //pinMode(rx_pin, INPUT_PULLUP);
#endif // ESP8266 #endif // ESP8266
#ifdef ESP32 #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 #endif // ESP32
// Trick here even using SERIAL_7E1 or TS_SERIAL_7E1 // Trick here even using SERIAL_7E1 or TS_SERIAL_7E1
// this is not working, need to call SetSerialConfig after // this is not working, need to call SetSerialConfig after
if (TInfoSerial->begin(baudrate)) { if (TInfoSerial->begin(baudrate)) {
#ifdef ESP8266 #ifdef ESP8266
if (TInfoSerial->hardwareSerial() ) { if (TInfoSerial->hardwareSerial() ) {
ClaimSerial(); ClaimSerial();
@ -494,6 +502,7 @@ void TInfoInit(void)
} }
#endif // ESP8266 #endif // ESP8266
#ifdef ESP32 #ifdef ESP32
SetSerialConfig(TS_SERIAL_7E1);
AddLog(LOG_LEVEL_INFO, PSTR("TIC: using ESP32 hardware serial")); AddLog(LOG_LEVEL_INFO, PSTR("TIC: using ESP32 hardware serial"));
#endif // ESP32 #endif // ESP32
// Init teleinfo // Init teleinfo
@ -510,30 +519,47 @@ void TInfoInit(void)
} }
/* ====================================================================== /* ======================================================================
Function: TInfoEvery250ms Function: TInfoProcess
Purpose : Tasmota callback executed every 250ms Purpose : Tasmota callback executed often enough to read serial
Input : - Input : -
Output : - Output : -
Comments: - 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) { if (!tinfo_found) {
return; return;
} }
if (TInfoSerial->available()) { int size = TInfoSerial->read(buff,TELEINFO_PROCESS_BUFFER);
unsigned long start = millis(); while ( size ) {
char c; #ifdef MEASURE_PERF
tmp_size += size;
// We received some data, process but never more than 100ms ? #endif
while (TInfoSerial->available()>8 && millis()-start < 100) { // Process as many bytes as available in serial buffer
// get char for(int i = 0; size; i++, size--)
c = TInfoSerial->read(); {
buff[i] &= 0x7F;
// data processing // 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) switch (function)
{ {
case FUNC_EVERY_250_MSECOND: case FUNC_EVERY_250_MSECOND:
TInfoEvery250ms(); TInfoProcess();
break; break;
case FUNC_JSON_APPEND: case FUNC_JSON_APPEND:
TInfoShow(1); TInfoShow(1);
@ -662,4 +688,4 @@ bool Xnrg15(uint8_t function)
} }
#endif // USE_TELEINFO #endif // USE_TELEINFO
#endif // USE_ENERGY_SENSOR #endif // USE_ENERGY_SENSOR