Merge pull request #9095 from hallard/teleinfo

Teleinfo Added setOption108
This commit is contained in:
Theo Arends 2020-08-17 15:24:52 +02:00 committed by GitHub
commit bfc027a638
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 233 additions and 174 deletions

View File

@ -778,14 +778,13 @@ ValueList * TInfo::checkLine(char * pline)
return NULL; return NULL;
p = &buff[0]; p = &buff[0];
i = len + 1 ;
sep = 0; sep = 0;
// Get our own working copy and in the // Get our own working copy and in the
// meantime, calculate separator count for // meantime, calculate separator count for
// standard mode (to know if timestamped data) // standard mode (to know if timestamped data)
while (i--) { for (i=0 ; i<len ; i++) {
// count separator // count separator, take care, checksum last one can be space separator
if (*pline == _separator) { if (*pline==_separator && *(pline+1)!='\r') {
// Label + sep + Date + sep + Etiquette + sep + Checksum // Label + sep + Date + sep + Etiquette + sep + Checksum
if (++sep >=3){ if (++sep >=3){
hasts = true; hasts = true;

View File

@ -46,6 +46,13 @@
// debugging, this should not interfere with main sketch or other // debugging, this should not interfere with main sketch or other
// libraries // libraries
#ifdef TI_DEBUG #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__);
#define TI_Debugflush {}
#else
#ifdef ESP8266 #ifdef ESP8266
#define TI_Debug(x) Serial1.print(x) #define TI_Debug(x) Serial1.print(x)
#define TI_Debugln(x) Serial1.println(x) #define TI_Debugln(x) Serial1.println(x)
@ -57,6 +64,7 @@
#define TI_Debugf(...) Serial.printf(__VA_ARGS__) #define TI_Debugf(...) Serial.printf(__VA_ARGS__)
#define TI_Debugflush Serial.flush #define TI_Debugflush Serial.flush
#endif #endif
#endif
#else #else
#define TI_Debug(x) {} #define TI_Debug(x) {}
#define TI_Debugln(x) {} #define TI_Debugln(x) {}

View File

@ -4,6 +4,7 @@
- Add better config corruption recovery (#9046) - Add better config corruption recovery (#9046)
- Remove support for 1-step upgrade from versions before 6.6.0.11 to versions after 8.4.0.1 - Remove support for 1-step upgrade from versions before 6.6.0.11 to versions after 8.4.0.1
- Add command ``SetOption108 0/1`` to enable Teleinfo telemetry into Tasmota Energy MQTT (0) or Teleinfo only (1) in this case MQTT will send RAW Teleinfo telemetry on each frame received and not into Tasmota energy calculation telemetry.
- Change White blend mode moved to using ``SetOption 105`` instead of ``RGBWWTable`` - Change White blend mode moved to using ``SetOption 105`` instead of ``RGBWWTable``
- Add Virtual CT for 4 channels lights, emulating a 5th channel - Add Virtual CT for 4 channels lights, emulating a 5th channel

View File

@ -127,8 +127,8 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu
uint32_t white_blend_mode : 1; // bit 23 (v8.4.0.1) - SetOption105 - White Blend Mode - used to be `RGBWWTable` last value `0`, now deprecated in favor of this option uint32_t white_blend_mode : 1; // bit 23 (v8.4.0.1) - SetOption105 - White Blend Mode - used to be `RGBWWTable` last value `0`, now deprecated in favor of this option
uint32_t virtual_ct : 1; // bit 24 (v8.4.0.1) - SetOption106 - Virtual CT - Creates a virtual White ColorTemp for RGBW lights uint32_t virtual_ct : 1; // bit 24 (v8.4.0.1) - SetOption106 - Virtual CT - Creates a virtual White ColorTemp for RGBW lights
uint32_t virtual_ct_cw : 1; // bit 25 (v8.4.0.1) - SetOption107 - Virtual CT Channel - signals whether the hardware white is cold CW (true) or warm WW (false) uint32_t virtual_ct_cw : 1; // bit 25 (v8.4.0.1) - SetOption107 - Virtual CT Channel - signals whether the hardware white is cold CW (true) or warm WW (false)
uint32_t spare26 : 1; // bit 26 uint32_t teleinfo_rawdata : 1; // bit 21 (v8.4.0.2) - SetOption108 - enable Teleinfo + Tasmota Energy device (0) or Teleinfo raw data only (1)
uint32_t spare27 : 1; // bit 27 uint32_t spare27 : 1;
uint32_t spare28 : 1; // bit 28 uint32_t spare28 : 1; // bit 28
uint32_t spare29 : 1; // bit 29 uint32_t spare29 : 1; // bit 29
uint32_t spare30 : 1; // bit 30 uint32_t spare30 : 1; // bit 30

View File

@ -27,6 +27,7 @@
* {"NAME":"Denky (Teleinfo)","GPIO":[1,1,1,1,5664,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1376,1,1,0,0,0,0,1,5632,1,1,1,0,0,1],"FLAG":0,"BASE":1} * {"NAME":"Denky (Teleinfo)","GPIO":[1,1,1,1,5664,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1376,1,1,0,0,0,0,1,5632,1,1,1,0,0,1],"FLAG":0,"BASE":1}
* *
* Denky (aka WifInfo) ESP8266 Teleinfo Template * Denky (aka WifInfo) ESP8266 Teleinfo Template
* {"NAME":"WifInfo v1.0a","GPIO":[17,255,255,255,6,5,255,255,7,210,255,255,255],"FLAG":15,"BASE":18}
* {"NAME":"WifInfo","GPIO":[7,255,255,210,6,5,255,255,255,255,255,255,255],"FLAG":15,"BASE":18} * {"NAME":"WifInfo","GPIO":[7,255,255,210,6,5,255,255,255,255,255,255,255],"FLAG":15,"BASE":18}
* *
\*********************************************************************************************/ \*********************************************************************************************/
@ -107,6 +108,7 @@ const char kLabel[] PROGMEM =
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;
char serialNumber[13] = ""; // Serial number is 12 char long
bool tinfo_found = false; bool tinfo_found = false;
int contrat; int contrat;
int tarif; int tarif;
@ -144,19 +146,17 @@ Comments: should have been initialised with a
====================================================================== */ ====================================================================== */
void ADPSCallback(uint8_t phase) void ADPSCallback(uint8_t phase)
{ {
char adco[13];
// n = phase number 1 to 3 // n = phase number 1 to 3
if (phase == 0){ if (phase == 0){
phase = 1; phase = 1;
} }
if (tinfo_mode == TINFO_MODE_HISTORIQUE) { Response_P(PSTR("{"));
if (getValueFromLabelIndex(LABEL_ADCO, adco) ) { ResponseAppend_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":{\"ADPS\":%i}}"), serialNumber, phase );
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":{\"ADPS\":%i}}"), adco, phase ); ResponseJsonEnd();
MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data);
} // Publish adding ADCO serial number into the topic
} MqttPublishPrefixTopic_P(RESULT_OR_TELE, serialNumber, false);
AddLog_P2(LOG_LEVEL_INFO, PSTR("ADPS on phase %d"), phase); AddLog_P2(LOG_LEVEL_INFO, PSTR("ADPS on phase %d"), phase);
} }
@ -185,6 +185,13 @@ void DataCallback(struct _ValueList * me, uint8_t flags)
} }
} }
if (flags & TINFO_FLAGS_ADDED) { c = '#'; }
if (flags & TINFO_FLAGS_UPDATED) { c = '*'; }
if (flags & TINFO_FLAGS_STRING) { c = '$'; }
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: [%d]%c %s=%s"), ilabel, c , me->name, me->value);
if (ilabel<LABEL_END) {
// Current tariff (legacy) // Current tariff (legacy)
if (ilabel == LABEL_PTEC) if (ilabel == LABEL_PTEC)
{ {
@ -217,34 +224,21 @@ void DataCallback(struct _ValueList * me, uint8_t flags)
{ {
Energy.voltage_available = true; Energy.voltage_available = true;
Energy.voltage[0] = (float) atoi(me->value); Energy.voltage[0] = (float) atoi(me->value);
// Update current
if (Energy.voltage_available && Energy.voltage[0]) {
Energy.current[0] = Energy.active_power[0] / Energy.voltage[0] ;
}
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Voltage %s, now %d"), me->value, (int) Energy.voltage[0]); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Voltage %s, now %d"), me->value, (int) Energy.voltage[0]);
} }
// Current I // Current I
else if (ilabel == LABEL_IINST || ilabel == LABEL_IRMS1) else if (ilabel == LABEL_IINST || ilabel == LABEL_IRMS1)
{ {
if (!Energy.voltage_available) {
Energy.current[0] = (float) atoi(me->value); Energy.current[0] = (float) atoi(me->value);
} else if (Energy.voltage[0]) {
Energy.current[0] = Energy.active_power[0] / Energy.voltage[0] ;
}
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Current %s, now %d"), me->value, (int) Energy.current[0]); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Current %s, now %d"), me->value, (int) Energy.current[0]);
} }
// Power P // Power P
else if (ilabel == LABEL_PAPP || ilabel == LABEL_SINSTS) else if (ilabel == LABEL_PAPP || ilabel == LABEL_SINSTS)
{ {
int papp = atoi(me->value); Energy.active_power[0] = (float) atoi(me->value);;
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Power %s, now %d"), me->value, papp); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Power %s, now %d"), me->value, (int) Energy.active_power[0]);
Energy.active_power[0] = (float) atoi(me->value);
// Update current
if (Energy.voltage_available && Energy.voltage[0]) {
Energy.current[0] = Energy.active_power[0] / Energy.voltage[0] ;
}
} }
// Wh indexes (legacy) // Wh indexes (legacy)
@ -258,7 +252,10 @@ void DataCallback(struct _ValueList * me, uint8_t flags)
if ( getValueFromLabelIndex(LABEL_HCHC, value) ) { hc = atoi(value);} if ( getValueFromLabelIndex(LABEL_HCHC, value) ) { hc = atoi(value);}
if ( getValueFromLabelIndex(LABEL_HCHP, value) ) { hp = atoi(value);} if ( getValueFromLabelIndex(LABEL_HCHP, value) ) { hp = atoi(value);}
total = hc + hp; total = hc + hp;
if (!Settings.flag4.teleinfo_rawdata) {
EnergyUpdateTotal(total/1000.0f, true); EnergyUpdateTotal(total/1000.0f, true);
}
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: HC:%u HP:%u Total:%u"), hc, hp, total); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: HC:%u HP:%u Total:%u"), hc, hp, total);
} }
@ -266,7 +263,9 @@ void DataCallback(struct _ValueList * me, uint8_t flags)
else if ( ilabel == LABEL_EAST) else if ( ilabel == LABEL_EAST)
{ {
uint32_t total = atoi(me->value); uint32_t total = atoi(me->value);
if (!Settings.flag4.teleinfo_rawdata) {
EnergyUpdateTotal(total/1000.0f, true); EnergyUpdateTotal(total/1000.0f, true);
}
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Total:%uWh"), total); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Total:%uWh"), total);
} }
@ -306,11 +305,71 @@ void DataCallback(struct _ValueList * me, uint8_t flags)
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: ISousc set to %d"), isousc); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: ISousc set to %d"), isousc);
} }
// Serial Number of device
else if (ilabel == LABEL_ADCO || ilabel == LABEL_ADSC)
{
strcpy(serialNumber, me->value);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: %s set to %s"), me->name, serialNumber);
} }
if (flags & TINFO_FLAGS_ADDED) { c = '#'; } }
if (flags & TINFO_FLAGS_UPDATED) { c = '*'; } }
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TIC: %c %s=%s"),c , me->name, me->value);
}
/* ======================================================================
Function: responseDumpTInfo
Purpose : add teleinfo values into JSON response
Input : -
Output : -
Comments: -
====================================================================== */
void ResponseAppendTInfo()
{
struct _ValueList * me = tinfo.getList();
char sep = ' '; // First JSON value separator
char * p ;
boolean isNumber ;
// Loop thru all the teleinfo frame but
// always check we don't buffer overflow of MQTT data
while (me->next) {
// go to next node
me = me->next;
if (me->name && me->value && *me->name && *me->value) {
isNumber = true;
p = me->value;
// Specific treatment serial number don't convert to number later
if (strcmp(me->name, "ADCO")==0 || strcmp(me->name, "ADSC")==0) {
isNumber = false;
} else {
// check if value is number
while (*p && isNumber) {
if ( *p < '0' || *p > '9' ) {
isNumber = false;
}
p++;
}
}
ResponseAppend_P( PSTR("%c\"%s\":"), sep, me->name );
if (!isNumber || (me->flags & TINFO_FLAGS_STRING) ) {
ResponseAppend_P( PSTR("\"%s\""), me->value );
} else {
ResponseAppend_P( PSTR("%d"), atoi(me->value));
}
// Now JSON separator is needed
sep =',';
}
}
} }
/* ====================================================================== /* ======================================================================
@ -324,6 +383,17 @@ void NewFrameCallback(struct _ValueList * me)
{ {
// Reset Energy Watchdog // Reset Energy Watchdog
Energy.data_valid[0] = 0; Energy.data_valid[0] = 0;
// send teleinfo full frame only if setup like that
// see setOption108
if (Settings.flag4.teleinfo_rawdata) {
Response_P(PSTR("{"));
ResponseAppendTInfo();
ResponseJsonEnd();
// Publish adding ADCO serial number into the topic
// Need setOption4 to be enabled
MqttPublishPrefixTopic_P(RESULT_OR_TELE, serialNumber, false);
}
} }
/* ====================================================================== /* ======================================================================
@ -337,8 +407,6 @@ void TInfoDrvInit(void) {
if (PinUsed(GPIO_TELEINFO_RX)) { if (PinUsed(GPIO_TELEINFO_RX)) {
energy_flg = XNRG_15; energy_flg = XNRG_15;
Energy.voltage_available = false; Energy.voltage_available = false;
//Energy.current_available = false;
Energy.type_dc = true;
} }
} }
@ -433,14 +501,16 @@ Comments: -
====================================================================== */ ====================================================================== */
void TInfoEvery250ms(void) void TInfoEvery250ms(void)
{ {
char c; if (!tinfo_found) {
if (!tinfo_found)
return; return;
}
if (TInfoSerial->available()) { if (TInfoSerial->available()) {
//AddLog_P2(LOG_LEVEL_INFO, PSTR("TIC: received %d chars"), TInfoSerial->available()); unsigned long start = millis();
// We received some data? char c;
while (TInfoSerial->available()>8) {
// We received some data, process but never more than 100ms ?
while (TInfoSerial->available()>8 && millis()-start < 100) {
// get char // get char
c = TInfoSerial->read(); c = TInfoSerial->read();
// data processing // data processing
@ -457,6 +527,7 @@ Output : -
Comments: - Comments: -
====================================================================== */ ====================================================================== */
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
const char HTTP_ENERGY_ID_TELEINFO[] PROGMEM = "{s}ID{m}%s{e}" ;
const char HTTP_ENERGY_INDEX_TELEINFO[] PROGMEM = "{s}%s{m}%s " D_UNIT_WATTHOUR "{e}" ; const char HTTP_ENERGY_INDEX_TELEINFO[] PROGMEM = "{s}%s{m}%s " D_UNIT_WATTHOUR "{e}" ;
const char HTTP_ENERGY_PAPP_TELEINFO[] PROGMEM = "{s}" D_POWERUSAGE "{m}%d " D_UNIT_WATT "{e}" ; const char HTTP_ENERGY_PAPP_TELEINFO[] PROGMEM = "{s}" D_POWERUSAGE "{m}%d " D_UNIT_WATT "{e}" ;
const char HTTP_ENERGY_IINST_TELEINFO[] PROGMEM = "{s}" D_CURRENT "{m}%d " D_UNIT_AMPERE "{e}" ; const char HTTP_ENERGY_IINST_TELEINFO[] PROGMEM = "{s}" D_CURRENT "{m}%d " D_UNIT_AMPERE "{e}" ;
@ -469,46 +540,19 @@ const char HTTP_ENERGY_PMAX_TELEINFO[] PROGMEM = "{s}" D_MAX_POWER "{m}%d" D_UN
void TInfoShow(bool json) void TInfoShow(bool json)
{ {
char name[32];
char value[32];
// Since it's an Energy device , current, voltage and power are // Since it's an Energy device , current, voltage and power are
// already present on the telemetry frame. No need to add here // already present on the telemetry frame. No need to add here
// Just add the raw label/values of the teleinfo frame // Just add the raw label/values of the teleinfo frame
if (json) if (json)
{ {
struct _ValueList * me = tinfo.getList();
// Calculated values // Calculated values
if (isousc) { if (isousc) {
ResponseAppend_P(PSTR(",\"Load\":%d"),(int) ((Energy.current[0]*100.0f) / isousc)); ResponseAppend_P(PSTR(",\"Load\":%d"),(int) ((Energy.current[0]*100.0f) / isousc));
} }
// Loop thru all the teleinfo frame // add teleinfo full frame only if no teleinfo raw data setup
while (me->next) { if (!Settings.flag4.teleinfo_rawdata) {
// go to next node ResponseAppendTInfo();
me = me->next;
if (me->name && me->value && *me->name && *me->value) {
boolean isNumber = true;
char * p = me->value;
// check if value is number
while (*p && isNumber) {
if ( *p < '0' || *p > '9' ) {
isNumber = false;
}
p++;
}
// this will add "" on not number values
ResponseAppend_P(PSTR(",\"%s\":"), me->name);
if (!isNumber) {
ResponseAppend_P(PSTR("\"%s\""), me->value);
} else {
ResponseAppend_P(PSTR("%u"), atol(me->value));
}
}
} }
@ -516,6 +560,9 @@ void TInfoShow(bool json)
} }
else else
{ {
char name[32];
char value[32];
if (getValueFromLabelIndex(LABEL_HCHC, value) ) { if (getValueFromLabelIndex(LABEL_HCHC, value) ) {
GetTextIndexed(name, sizeof(name), LABEL_HCHC, kLabel); GetTextIndexed(name, sizeof(name), LABEL_HCHC, kLabel);
WSContentSend_PD(HTTP_ENERGY_INDEX_TELEINFO, name, value); WSContentSend_PD(HTTP_ENERGY_INDEX_TELEINFO, name, value);
@ -531,7 +578,7 @@ void TInfoShow(bool json)
WSContentSend_PD(HTTP_ENERGY_PMAX_TELEINFO, atoi(value)); WSContentSend_PD(HTTP_ENERGY_PMAX_TELEINFO, atoi(value));
} }
if (tinfo_mode==TINFO_MODE_STANDARD ) { if (tinfo_mode==TINFO_MODE_HISTORIQUE ) {
if (tarif) { if (tarif) {
GetTextIndexed(name, sizeof(name), tarif-1, kTarifName); GetTextIndexed(name, sizeof(name), tarif-1, kTarifName);
WSContentSend_PD(HTTP_ENERGY_TARIF_TELEINFO, name); WSContentSend_PD(HTTP_ENERGY_TARIF_TELEINFO, name);
@ -542,7 +589,7 @@ void TInfoShow(bool json)
WSContentSend_PD(HTTP_ENERGY_CONTRAT_TELEINFO, name, isousc); WSContentSend_PD(HTTP_ENERGY_CONTRAT_TELEINFO, name, isousc);
WSContentSend_PD(HTTP_ENERGY_LOAD_TELEINFO, percent); WSContentSend_PD(HTTP_ENERGY_LOAD_TELEINFO, percent);
} }
} else { } else if (tinfo_mode==TINFO_MODE_STANDARD ) {
if (getValueFromLabelIndex(LABEL_LTARF, name) ) { if (getValueFromLabelIndex(LABEL_LTARF, name) ) {
WSContentSend_PD(HTTP_ENERGY_TARIF_TELEINFO, name); WSContentSend_PD(HTTP_ENERGY_TARIF_TELEINFO, name);
} }
@ -555,6 +602,10 @@ void TInfoShow(bool json)
} }
} }
} }
// Serial number ADCO or ADSC
WSContentSend_PD(HTTP_ENERGY_ID_TELEINFO, serialNumber);
#endif // USE_WEBSERVER #endif // USE_WEBSERVER
} }
} }