diff --git a/lib/LibTeleinfo/library.json b/lib/LibTeleinfo/library.json index a93105e61..721266bef 100755 --- a/lib/LibTeleinfo/library.json +++ b/lib/LibTeleinfo/library.json @@ -1,6 +1,6 @@ { "name": "LibTeleinfo", - "version": "1.1.2", + "version": "1.1.3", "keywords": "teleinfo, french, meter, power, erdf, linky, tic", "description": "Decoder for Teleinfo (aka TIC) from French smart power meters", "repository": diff --git a/lib/LibTeleinfo/library.properties b/lib/LibTeleinfo/library.properties index 7cfc9a635..89fce0969 100755 --- a/lib/LibTeleinfo/library.properties +++ b/lib/LibTeleinfo/library.properties @@ -1,5 +1,5 @@ name=LibTeleinfo -version=1.1.2 +version=1.1.3 author=Charles-Henri Hallard maintainer=Charles-Henri Hallard sentence=Decoder for Teleinfo (aka TIC) from French smart power meters diff --git a/lib/LibTeleinfo/src/LibTeleinfo.cpp b/lib/LibTeleinfo/src/LibTeleinfo.cpp index 588555831..3d2f9f084 100644 --- a/lib/LibTeleinfo/src/LibTeleinfo.cpp +++ b/lib/LibTeleinfo/src/LibTeleinfo.cpp @@ -15,6 +15,8 @@ // // History : V1.00 2015-06-14 - First release // V2.00 2020-06-11 - Integration into Tasmota +// V2.01 2020-08-11 - Merged LibTeleinfo official and Tasmota version +// Added support for new standard mode of linky smart meter // // All text above must be included in any redistribution. // @@ -40,6 +42,8 @@ TInfo::TInfo() _valueslist.checksum = '\0'; _valueslist.flags = TINFO_FLAGS_NONE; + _separator = ' '; + // callback _fn_ADPS = NULL; _fn_data = NULL; @@ -50,11 +54,11 @@ TInfo::TInfo() /* ====================================================================== Function: init Purpose : try to guess -Input : - +Input : Mode, historique ou standard Output : - Comments: - ====================================================================== */ -void TInfo::init() +void TInfo::init(_Mode_e mode) { // free up linked list (in case on recall init()) listDelete(); @@ -64,6 +68,13 @@ void TInfo::init() // We're in INIT in term of receive data _state = TINFO_INIT; + + if ( mode == TINFO_MODE_STANDARD ) { + _separator = TINFO_HT; + } else { + _separator = ' '; + } + } /* ====================================================================== @@ -174,23 +185,31 @@ Input : Pointer to the label name pointer to the value checksum value flag state of the label (modified by function) + string date (teleinfo format) Output : pointer to the new node (or founded one) Comments: - state of the label changed by the function ====================================================================== */ -ValueList * TInfo::valueAdd(char * name, char * value, uint8_t checksum, uint8_t * flags) +ValueList * TInfo::valueAdd(char * name, char * value, uint8_t checksum, uint8_t * flags, char *horodate) { // Get our linked list ValueList * me = &_valueslist; uint8_t lgname = strlen(name); uint8_t lgvalue = strlen(value); - uint8_t thischeck = calcChecksum(name,value); + uint8_t thischeck = calcChecksum(name,value,horodate); // just some paranoia if (thischeck != checksum ) { TI_Debug(name); TI_Debug('='); TI_Debug(value); + + if (horodate && *horodate) { + TI_Debug(F(" Date=")); + TI_Debug(horodate); + TI_Debug(F(" ")); + } + TI_Debug(F(" '")); TI_Debug((char) checksum); TI_Debug(F("' Not added bad checksum calculated '")); @@ -202,6 +221,11 @@ ValueList * TInfo::valueAdd(char * name, char * value, uint8_t checksum, uint8_t // Create pointer on the new node ValueList *newNode = NULL; ValueList *parNode = NULL ; + uint32_t ts = 0; + + if (horodate && *horodate) { + ts = horodate2Timestamp(horodate); + } // Loop thru the node while (me->next) { @@ -213,6 +237,9 @@ ValueList * TInfo::valueAdd(char * name, char * value, uint8_t checksum, uint8_t // Check if we already have this LABEL (same name AND same size) if (lgname==strlen(me->name) && strcmp(me->name, name)==0) { + if (ts) { + me->ts = ts; + } // Already got also this value return US if (lgvalue==strlen(me->value) && strcmp(me->value, value) == 0) { *flags |= TINFO_FLAGS_EXIST; @@ -254,7 +281,7 @@ ValueList * TInfo::valueAdd(char * name, char * value, uint8_t checksum, uint8_t lgname = ESP_allocAlign(lgname+1); // Align name buffer lgvalue = ESP_allocAlign(lgvalue+1); // Align value buffer // Align the whole structure - size = ESP_allocAlign( sizeof(ValueList) + lgname + lgvalue ) ; + size = ESP_allocAlign( sizeof(ValueList) + lgname + lgvalue ) ; #else size = sizeof(ValueList) + lgname + 1 + lgvalue + 1 ; #endif @@ -278,6 +305,8 @@ ValueList * TInfo::valueAdd(char * name, char * value, uint8_t checksum, uint8_t // Copy the string data memcpy(newNode->name , name , lgname ); memcpy(newNode->value, value , lgvalue ); + // Add timestamp + newNode->ts = ts; // So we just created this node but was it new // or was matter of text size ? @@ -421,7 +450,7 @@ char * TInfo::valueGet(char * name, char * value) me = me->next; // Check if we match this LABEL - if (lgname==strlen(me->name) && strncmp(me->name, name, lgname)==0) { + if (lgname==strlen(me->name) && strcmp(me->name, name)==0) { // this one has a value ? if (me->value) { // copy to dest buffer @@ -459,7 +488,7 @@ char * TInfo::valueGet_P(const char * name, char * value) me = me->next; // Check if we match this LABEL - if (lgname==strlen(me->name) && strncmp_P(me->name, name, lgname)==0) { + if (lgname==strlen(me->name) && strcmp_P(me->name, name)==0) { // this one has a value ? if (me->value) { // copy to dest buffer @@ -608,12 +637,13 @@ Function: checksum Purpose : calculate the checksum based on data/value fields Input : label name label value + label timestamp Output : checksum Comments: return '\0' in case of error ====================================================================== */ -unsigned char TInfo::calcChecksum(char *etiquette, char *valeur) +unsigned char TInfo::calcChecksum(char *etiquette, char *valeur, char * horodate) { - uint8_t sum = ' '; // Somme des codes ASCII du message + un espace + uint8_t sum = _separator ; // Somme des codes ASCII du message + un separateur // avoid dead loop, always check all is fine if (etiquette && valeur) { @@ -624,13 +654,58 @@ unsigned char TInfo::calcChecksum(char *etiquette, char *valeur) while(*valeur) sum += *valeur++ ; - - return ( (sum & 63) + ' ' ) ; + + if (horodate) { + sum += _separator; + while (*horodate) + sum += *horodate++ ; + } + + return ( (sum & 0x3f) + ' ' ) ; } } return 0; } +/* ====================================================================== +Function: horodate2Timestamp +Purpose : convert string date from frame to timestamp +Input : pdate : pointer to string containing the date SAAMMJJhhmmss + season, year, month, day, hour, minute, second +Output : unix format timestamp +Comments: +====================================================================== */ +uint32_t TInfo::horodate2Timestamp( char * pdate) +{ + struct tm tm; + time_t ts; + char * p ; + + if (pdate==NULL || *pdate=='\0' || strlen(pdate)!=13) { + return 0; + } + + p = pdate + strlen(pdate) -2; + tm.tm_sec = atoi(p); *p='\0'; p-=2; + tm.tm_min = atoi(p); *p='\0'; p-=2; + tm.tm_hour = atoi(p); *p='\0'; p-=2; + tm.tm_mday = atoi(p); *p='\0'; p-=2; + tm.tm_mon = atoi(p); *p='\0'; p-=2; + tm.tm_year = atoi(p) + 2000; + + tm.tm_year -= 1900; + tm.tm_mon -= 1; + tm.tm_isdst = 0; + ts = mktime(&tm); + if (ts == (time_t)-1) { + TI_Debug(F("Failed to convert time ")); + TI_Debugln(pdate); + return 0; + } + + return (uint32_t) ts; +} + /* ====================================================================== Function: customLabel Purpose : do action when received a correct label / value + checksum line @@ -682,11 +757,15 @@ ValueList * TInfo::checkLine(char * pline) char * ptok; char * pend; char * pvalue; + char * pts; char checksum; char buff[TINFO_BUFSIZE]; uint8_t flags = TINFO_FLAGS_NONE; //boolean err = true ; // Assume error int len ; // Group len + int i; + int sep =0; + bool hasts = false ; // Assume timestamp on line if (pline==NULL) return NULL; @@ -695,11 +774,27 @@ ValueList * TInfo::checkLine(char * pline) // a line should be at least 7 Char // 2 Label + Space + 1 etiquette + space + checksum + \r - if ( len < 7 ) + if ( len < 7 || len >= TINFO_BUFSIZE) return NULL; - // Get our own working copy - strlcpy( buff, pline, len+1); + p = &buff[0]; + i = len + 1 ; + sep = 0; + // Get our own working copy and in the + // meantime, calculate separator count for + // standard mode (to know if timestamped data) + while (i--) { + // count separator + if (*pline == _separator) { + // Label + sep + Date + sep + Etiquette + sep + Checksum + if (++sep >=3){ + hasts = true; + } + } + // Copy + *p++ = *pline++; + } + *p = '\0'; p = &buff[0]; ptok = p; // for sure we start with token name @@ -707,29 +802,49 @@ ValueList * TInfo::checkLine(char * pline) // Init values pvalue = NULL; + pts = NULL; checksum = 0; //TI_Debug("Got ["); //TI_Debug(len); //TI_Debug("] "); - // Loop in buffer while ( p < pend ) { // start of token value - if ( *p==' ' && ptok) { + if ( *p==_separator && ptok) { // Isolate token name *p++ = '\0'; - // 1st space, it's the label value - if (!pvalue) - pvalue = p; - else - // 2nd space, so it's the checksum - checksum = *p; + // We have a timestamp + // Label + sep + Date + sep + Etiquette + sep + Checksum + if (hasts) { + if (!pts) { + pts = p; + } else { + // 2nd separator, it's the label value + if (!pvalue) { + pvalue = p; + } else { + // 3rd separator so it's the checksum + checksum = *p; + } + } + + // No timestamp + // Label + sep + Etiquette + sep + Checksum + } else { + // 1st separator, it's the label value + if (!pvalue) { + pvalue = p; + } else { + // 2nd separator so it's the checksum + checksum = *p; + } + } } + // new line ? ok we got all we need ? - if ( *p=='\r' ) { *p='\0'; @@ -738,7 +853,7 @@ ValueList * TInfo::checkLine(char * pline) // Always check to avoid bad behavior if(strlen(ptok) && strlen(pvalue)) { // Is checksum is OK - if ( calcChecksum(ptok,pvalue) == checksum) { + if ( calcChecksum(ptok,pvalue,pts) == checksum) { // In case we need to do things on specific labels customLabel(ptok, pvalue, &flags); diff --git a/lib/LibTeleinfo/src/LibTeleinfo.h b/lib/LibTeleinfo/src/LibTeleinfo.h index 5b43523c7..45a467320 100755 --- a/lib/LibTeleinfo/src/LibTeleinfo.h +++ b/lib/LibTeleinfo/src/LibTeleinfo.h @@ -15,6 +15,8 @@ // // History : V1.00 2015-06-14 - First release // V2.00 2020-06-11 - Integration into Tasmota +// V2.01 2020-08-11 - Merged LibTeleinfo official and Tasmota version +// Added support for new standard mode of linky smart meter // // All text above must be included in any redistribution. // @@ -25,7 +27,17 @@ #ifndef LibTeleinfo_h #define LibTeleinfo_h -#include "Arduino.h" +#ifdef __arm__ +#include +#include +#include +#include +#define boolean bool +#endif + +#ifdef ARDUINO +#include +#endif // Define this if you want library to be verbose //#define TI_DEBUG @@ -53,7 +65,9 @@ #endif // For 4 bytes Aligment boundaries +#if defined (ESP8266) || defined (ESP32) #define ESP_allocAlign(size) ((size + 3) & ~((size_t) 3)) +#endif #pragma pack(push) // push current alignment to stack #pragma pack(1) // set alignment to 1 byte boundary @@ -63,6 +77,9 @@ 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 @@ -71,6 +88,11 @@ struct _ValueList #pragma pack(pop) +// Library state machine +enum _Mode_e { + TINFO_MODE_HISTORIQUE, // Legacy mode (1200) + TINFO_MODE_STANDARD // Standard mode (9600) +}; // Library state machine enum _State_e { @@ -90,11 +112,17 @@ enum _State_e { // 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 +#define TINFO_BUFSIZE 128 +#else #define TINFO_BUFSIZE 64 +#endif // Teleinfo start and end of frame characters #define TINFO_STX 0x02 #define TINFO_ETX 0x03 +#define TINFO_HT 0x09 #define TINFO_SGR '\n' // start of group #define TINFO_EGR '\r' // End of group @@ -107,7 +135,7 @@ class TInfo { public: TInfo(); - void init(); + void init(_Mode_e mode = TINFO_MODE_HISTORIQUE); _State_e process (char c); void attachADPS(void (*_fn_ADPS)(uint8_t phase)); void attachData(void (*_fn_data)(ValueList * valueslist, uint8_t state)); @@ -119,20 +147,22 @@ class TInfo char * valueGet(char * name, char * value); char * valueGet_P(const char * name, char * value); boolean listDelete(); - unsigned char calcChecksum(char *etiquette, char *valeur) ; + unsigned char calcChecksum(char *etiquette, char *valeur, char *horodate=NULL) ; private: void clearBuffer(); - ValueList * valueAdd (char * name, char * value, uint8_t checksum, uint8_t * flags); + ValueList * valueAdd (char * name, char * value, uint8_t checksum, uint8_t * flags, char * horodate=NULL); boolean valueRemove (char * name); boolean valueRemoveFlagged(uint8_t flags); int labelCount(); + uint32_t horodate2Timestamp( char * pdate) ; void customLabel( char * plabel, char * pvalue, uint8_t * pflags) ; ValueList * checkLine(char * pline) ; _State_e _state; // Teleinfo machine state ValueList _valueslist; // Linked list of teleinfo values char _recv_buff[TINFO_BUFSIZE]; // line receive buffer + char _separator; uint8_t _recv_idx; // index in receive buffer boolean _frame_updated; // Data on the frame has been updated void (*_fn_ADPS)(uint8_t phase); diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 4d7cd944c..29b1d3e43 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -7,6 +7,7 @@ - Add ESP32 Analog input support for GPIO32 to GPIO39 - Add Zigbee options to ``ZbSend`` ``Config`` and ``ReadCondig`` - Add command ``Restart 2`` to halt system. Needs hardware reset or power cycle to restart (#9046) +- Add command ``SetOption102 0/1`` to switch between Teleinfo French Metering mode, legacy 1200 bps (0) or Linky standard 9600 bps (1) ### 8.4.0 20200730 diff --git a/tasmota/settings.h b/tasmota/settings.h index 213863ef1..7241efc33 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -121,7 +121,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t zerocross_dimmer : 1; // bit 17 (v8.3.1.4) - SetOption99 - Enable zerocross dimmer on PWM DIMMER uint32_t remove_zbreceived : 1; // bit 18 (v8.3.1.7) - SetOption100 - Remove ZbReceived form JSON message uint32_t zb_index_ep : 1; // bit 19 (v8.3.1.7) - SetOption101 - Add the source endpoint as suffix to attributes, ex `Power3` instead of `Power` if sent from endpoint 3 - uint32_t spare20 : 1; + uint32_t teleinfo_baudrate : 1; // bit 20 (v8.4.0.1) - SetOption102 - Set Baud rate for Teleinfo communication (0 = 1200 or 1 = 9600) uint32_t spare21 : 1; uint32_t spare22 : 1; uint32_t spare23 : 1; diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index e389bb64b..bd8019be9 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -904,6 +904,7 @@ void CmndSetoption(void) case 3: // SetOption85 - Enable Device Groups case 6: // SetOption88 - PWM Dimmer Buttons control remote devices case 15: // SetOption97 - Set Baud rate for TuyaMCU serial communication (0 = 9600 or 1 = 115200) + case 20: // SetOption102 - Set Baud rate for Teleinfo serial communication (0 = 1200 or 1 = 9600) restart_flag = 2; break; } diff --git a/tasmota/xnrg_15_teleinfo.ino b/tasmota/xnrg_15_teleinfo.ino index a44eca53b..9da74ddd2 100755 --- a/tasmota/xnrg_15_teleinfo.ino +++ b/tasmota/xnrg_15_teleinfo.ino @@ -30,7 +30,6 @@ * {"NAME":"WifInfo","GPIO":[7,255,255,210,6,5,255,255,255,255,255,255,255],"FLAG":15,"BASE":18} * \*********************************************************************************************/ - #define XNRG_15 15 #include "LibTeleinfo.h" @@ -38,7 +37,7 @@ #define TINFO_READ_TIMEOUT 400 -// All contract type +// All contract type for legacy, standard mode has in clear text enum TInfoContrat{ CONTRAT_BAS = 1, // BASE => Option Base. CONTRAT_HC, // HC.. => Option Heures Creuses. @@ -47,17 +46,17 @@ enum TInfoContrat{ CONTRAT_END }; -// contract displayed name +// contract displayed name for legacy, standard mode has in clear text const char kContratName[] PROGMEM = "|Base|Heures Creuses|EJP|Bleu Blanc Rouge" ; -// Received current contract value +// Received current contract value for legacy, standard mode has in clear text const char kContratValue[] PROGMEM = "|BASE|HC..|EJP.|BBR" ; -// all tariff type +// all tariff type for legacy, standard mode has in clear text enum TInfoTarif{ TARIF_TH = 1, TARIF_HC, TARIF_HP, @@ -67,7 +66,8 @@ enum TInfoTarif{ TARIF_END }; -// Received current tariff values +// Received current tariff values +// for legacy, standard mode has in clear text const char kTarifValue[] PROGMEM = "|TH..|HC..|HP.." "|HN..|PM.." @@ -75,7 +75,7 @@ const char kTarifValue[] PROGMEM = "|HPJB|HPJW|HPJR" ; -// tariff displayed name +// tariff displayed name (for legacy, standard mode has in clear text) const char kTarifName[] PROGMEM = "|Toutes|Creuses|Pleines" "|Normales|Pointe Mobile" @@ -83,27 +83,30 @@ const char kTarifName[] PROGMEM = "|Pleines Bleu|Pleines Blanc|Pleines Rouges" ; +// Label used to do some post processing and/or calculation enum TInfoLabel{ LABEL_BASE = 1, - LABEL_ADCO, - LABEL_HCHC, LABEL_HCHP, - LABEL_OPTARIF, LABEL_ISOUSC, LABEL_PTEC, - LABEL_PAPP, LABEL_IINST, LABEL_TENSION, - LABEL_IMAX, LABEL_PMAX, + LABEL_ADCO, LABEL_ADSC, + LABEL_HCHC, LABEL_HCHP, LABEL_EAST, LABEL_EASF01, LABEL_EASF02, + LABEL_OPTARIF, LABEL_NGTF, LABEL_ISOUSC, LABEL_PREF, LABEL_PTEC, LABEL_LTARF, LABEL_NTARF, + LABEL_PAPP, LABEL_SINSTS, LABEL_IINST, LABEL_IRMS1, LABEL_TENSION, LABEL_URMS1, + LABEL_IMAX, LABEL_PMAX, LABEL_SMAXSN, LABEL_DEMAIN, LABEL_END }; const char kLabel[] PROGMEM = - "|BASE|ADCO|HCHC|HCHP" - "|OPTARIF|ISOUSC|PTEC" - "|PAPP|IINST|TENSION" - "|IMAX|PMAX" + "|BASE|ADCO|ADSC" + "|HCHC|HCHP|EAST|EASF01|EASF02" + "|OPTARIF|NGTF|ISOUSC|PREF|PTEC|LTARF|NTARF" + "|PAPP|SINSTS|IINST|IRMS1|TENSION|URMS1" + "|IMAX|PMAX|SMAXSN" "|DEMAIN" ; TInfo tinfo; // Teleinfo object TasmotaSerial *TInfoSerial = nullptr; +_Mode_e tinfo_mode = TINFO_MODE_HISTORIQUE; bool tinfo_found = false; int contrat; int tarif; @@ -147,10 +150,12 @@ void ADPSCallback(uint8_t phase) if (phase == 0){ phase = 1; } - - if (getValueFromLabelIndex(LABEL_ADCO, adco)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":{\"ADPS\":%i}}"), adco, phase ); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); + + if (tinfo_mode == TINFO_MODE_HISTORIQUE) { + if (getValueFromLabelIndex(LABEL_ADCO, adco) ) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":{\"ADPS\":%i}}"), adco, phase ); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); + } } AddLog_P2(LOG_LEVEL_INFO, PSTR("ADPS on phase %d"), phase); @@ -180,7 +185,7 @@ void DataCallback(struct _ValueList * me, uint8_t flags) } } - // Current tariff + // Current tariff (legacy) if (ilabel == LABEL_PTEC) { char tarif_value[] = " "; // 4 spaces @@ -194,8 +199,21 @@ void DataCallback(struct _ValueList * me, uint8_t flags) AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Tarif changed, now '%s' (%d)"), me->value, tarif); } + // Current tariff (standard is in clear text in value) + else if (ilabel == LABEL_LTARF) + { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Tarif 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_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Tarif index changed, now '%d'"), tarif); + } + + // Voltage V (not present on all Smart Meter) - else if ( ilabel == LABEL_TENSION) + else if ( ilabel == LABEL_TENSION || ilabel == LABEL_URMS1) { Energy.voltage_available = true; Energy.voltage[0] = (float) atoi(me->value); @@ -207,7 +225,7 @@ void DataCallback(struct _ValueList * me, uint8_t flags) } // Current I - else if (ilabel == LABEL_IINST) + else if (ilabel == LABEL_IINST || ilabel == LABEL_IRMS1) { if (!Energy.voltage_available) { Energy.current[0] = (float) atoi(me->value); @@ -218,7 +236,7 @@ void DataCallback(struct _ValueList * me, uint8_t flags) } // Power P - else if (ilabel == LABEL_PAPP) + else if (ilabel == LABEL_PAPP || ilabel == LABEL_SINSTS) { int papp = atoi(me->value); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Power %s, now %d"), me->value, papp); @@ -229,7 +247,7 @@ void DataCallback(struct _ValueList * me, uint8_t flags) } } - // Wh indexes + // Wh indexes (legacy) else if ( ilabel == LABEL_HCHC || ilabel == LABEL_HCHP) { char value[32]; @@ -244,7 +262,25 @@ void DataCallback(struct _ValueList * me, uint8_t flags) AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: HC:%u HP:%u Total:%u"), hc, hp, total); } - // Contract subscribed + // Wh total index (standard) + else if ( ilabel == LABEL_EAST) + { + uint32_t total = atoi(me->value); + EnergyUpdateTotal(total/1000.0f, true); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Total:%uWh"), total); + } + + // Wh indexes (standard) + else if ( ilabel == LABEL_EASF01) + { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: HC:%u"), atoi(me->value)); + } + else if ( ilabel == LABEL_EASF02) + { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: HP:%u"), atoi(me->value)); + } + + // Contract subscribed (legacy) else if (ilabel == LABEL_OPTARIF) { char contrat_value[] = " "; // 4 spaces @@ -257,9 +293,14 @@ void DataCallback(struct _ValueList * me, uint8_t flags) } AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Contract changed, now '%s' (%d)"), me->value, contrat); } + // Contract subscribed (standard is in clear text in value) + else if (ilabel == LABEL_NGTF) + { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Contract changed, now '%s'"), me->value); + } // Contract subscribed (Power) - else if (ilabel == LABEL_ISOUSC) + else if (ilabel == LABEL_ISOUSC || ilabel == LABEL_PREF) { isousc = atoi( me->value); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: ISousc set to %d"), isousc); @@ -285,7 +326,6 @@ void NewFrameCallback(struct _ValueList * me) Energy.data_valid[0] = 0; } - /* ====================================================================== Function: TInfoDrvInit Purpose : Tasmota core driver init @@ -311,13 +351,20 @@ Comments: - ====================================================================== */ void TInfoInit(void) { - #ifdef USE_TELEINFO_STANDARD - #define TINFO_SPEED 9600 - #else - #define TINFO_SPEED 1200 - #endif + int baudrate; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: inferface speed %d bps"),TINFO_SPEED); + AddLog_P2(LOG_LEVEL_INFO, PSTR("TIC: inferface saved settings %d bps"), Settings.flag4.teleinfo_baudrate ); + + // 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 { + baudrate = 1200; + tinfo_mode = TINFO_MODE_HISTORIQUE; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: inferface speed %d bps"),baudrate); if (PinUsed(GPIO_TELEINFO_RX)) { uint8_t rx_pin = Pin(GPIO_TELEINFO_RX); @@ -333,12 +380,17 @@ void TInfoInit(void) AddLog_P2(LOG_LEVEL_INFO, PSTR("TIC: always enabled")); } - TInfoSerial = new TasmotaSerial(rx_pin, -1, 1); +#if defined (ESP8266) + // Allow GPIO3 AND GPIO13 with hardware fallback to 2 + TInfoSerial = new TasmotaSerial(rx_pin, -1, 2); //pinMode(rx_pin, INPUT_PULLUP); +#else + TInfoSerial = new TasmotaSerial(rx_pin, -1, 1); +#endif // Trick here even using SERIAL_7E1 or TS_SERIAL_7E1 // this is not working, need to call SetSerialConfig after - if (TInfoSerial->begin(TINFO_SPEED)) { + if (TInfoSerial->begin(baudrate)) { #if defined (ESP8266) @@ -360,7 +412,7 @@ void TInfoInit(void) AddLog_P2(LOG_LEVEL_INFO, PSTR("TIC: using ESP32 hardware serial")); #endif // Init teleinfo - tinfo.init(); + tinfo.init(tinfo_mode); // Attach needed callbacks tinfo.attachADPS(ADPSCallback); tinfo.attachData(DataCallback); @@ -479,15 +531,29 @@ void TInfoShow(bool json) WSContentSend_PD(HTTP_ENERGY_PMAX_TELEINFO, atoi(value)); } - if (tarif) { - GetTextIndexed(name, sizeof(name), tarif-1, kTarifName); - WSContentSend_PD(HTTP_ENERGY_TARIF_TELEINFO, name); - } - if (contrat && isousc) { - int percent = (int) ((Energy.current[0]*100.0f) / isousc) ; - GetTextIndexed(name, sizeof(name), contrat, kContratName); - WSContentSend_PD(HTTP_ENERGY_CONTRAT_TELEINFO, name, isousc); - WSContentSend_PD(HTTP_ENERGY_LOAD_TELEINFO, percent); + if (tinfo_mode==TINFO_MODE_STANDARD ) { + if (tarif) { + GetTextIndexed(name, sizeof(name), tarif-1, kTarifName); + WSContentSend_PD(HTTP_ENERGY_TARIF_TELEINFO, name); + } + if (contrat && isousc) { + int percent = (int) ((Energy.current[0]*100.0f) / isousc) ; + GetTextIndexed(name, sizeof(name), contrat, kContratName); + WSContentSend_PD(HTTP_ENERGY_CONTRAT_TELEINFO, name, isousc); + WSContentSend_PD(HTTP_ENERGY_LOAD_TELEINFO, percent); + } + } else { + if (getValueFromLabelIndex(LABEL_LTARF, name) ) { + WSContentSend_PD(HTTP_ENERGY_TARIF_TELEINFO, name); + } + + if (getValueFromLabelIndex(LABEL_NGTF, name) ) { + if (isousc) { + int percent = (int) ((Energy.current[0]*100.0f) / isousc) ; + WSContentSend_PD(HTTP_ENERGY_CONTRAT_TELEINFO, name, isousc); + WSContentSend_PD(HTTP_ENERGY_LOAD_TELEINFO, percent); + } + } } #endif // USE_WEBSERVER }