fix teleinfo standard mode

This commit is contained in:
Barbudor 2021-02-10 21:08:59 +01:00
parent db678eb654
commit b6d366870d
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
_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;

View File

@ -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

View File

@ -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)

View File

@ -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
#endif // USE_ENERGY_SENSOR