mirror of https://github.com/arendst/Tasmota.git
Merge pull request #9075 from hallard/teleinfo
Teleinfo added support for Linky standard mode
This commit is contained in:
commit
21f7056fce
|
@ -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":
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name=LibTeleinfo
|
||||
version=1.1.2
|
||||
version=1.1.3
|
||||
author=Charles-Henri Hallard <hallard.me>
|
||||
maintainer=Charles-Henri Hallard <community.hallard.me>
|
||||
sentence=Decoder for Teleinfo (aka TIC) from French smart power meters
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#define boolean bool
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO
|
||||
#include <Arduino.h>
|
||||
#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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue