Merge pull request #8695 from hallard/teleinfo

Prep for French Metering xnrg_15_teleinfo
This commit is contained in:
Theo Arends 2020-06-17 14:42:22 +02:00 committed by GitHub
commit 5730ced6b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1628 additions and 6 deletions

View File

@ -68,6 +68,7 @@
| USE_DDSU666 | - | - | - | - | x | - | - | | USE_DDSU666 | - | - | - | - | x | - | - |
| USE_SOLAX_X1 | - | - | - | - | - | - | - | | USE_SOLAX_X1 | - | - | - | - | - | - | - |
| USE_LE01MR | - | - | - | - | - | - | - | | USE_LE01MR | - | - | - | - | - | - | - |
| USE_TELEINFO | - | - | - | - | - | - | - |
| | | | | | | | | | | | | | | | | |
| USE_ADC_VCC | x | x | - | - | - | - | - | | USE_ADC_VCC | x | x | - | - | - | - | - |
| USE_COUNTER | - | - | x | x | x | x | x | | USE_COUNTER | - | - | x | x | x | x | x |

View File

@ -81,4 +81,6 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
- Add initial support for Telegram bot (#8619) - Add initial support for Telegram bot (#8619)
- Add support for HP303B Temperature and Pressure sensor by Robert Jaakke (#8638) - Add support for HP303B Temperature and Pressure sensor by Robert Jaakke (#8638)
- Add rule trigger ``System#Init`` to allow early rule execution without wifi and mqtt initialized yet - Add rule trigger ``System#Init`` to allow early rule execution without wifi and mqtt initialized yet
- Created Energy sensor (Denky) for French Smart Metering meter provided by global Energy Providers, need a adaptater. See dedicated full [blog](http://hallard.me/category/tinfo/) about French teleinformation stuff
- Added Library to be used for decoding Teleinfo (French Metering Smart Meter)
- Add basic support for ESP32 ethernet adding commands ``Wifi 0/1`` and ``Ethernet 0/1`` both default ON - Add basic support for ESP32 ethernet adding commands ``Wifi 0/1`` and ``Ethernet 0/1`` both default ON

65
lib/LibTeleinfo/README.md Executable file
View File

@ -0,0 +1,65 @@
Teleinfo (Aka TIC) Universal Library
====================================
This is a generic Teleinfo French Meter Measure Library, it can be used on Arduino like device and also such as Spark Core, Particle, ESP8266, Raspberry PI or anywhere you can do Cpp code ...
You can see Teleinformation official french datasheet [there][1]
Since this is really dedicated to French energy measuring system, I will continue in French
Installation
============
Copier le contenu de ce dossier (download zip) dans le dossier libraries de votre environnement Arduino Vous devriez avoir maintenant quelque chose comme `your_sketchbook_folder/libraries/LibTeleinfo` et ce dossier doit contentir les fichiers .cpp et .h ainsi que le sous dossier `examples`.
<br/>
Pour trouver votre dossier de sketchbook, dans l'environnement IDE, allez dans File>Preferences.
<br/>
allez voir ce [tutorial][2] sur les librairies Arduino si beoin.
<br/>
Documentation
=============
J'ai écrit un article [dédié][10] sur cette librairie, vous pouvez aussi voir les [catégories][6] associées à la téléinfo sur mon [blog][7].
Pour les commentaires et le support vous pouvez allez sur le [forum][8] dédié ou dans la [communauté][9]
Sketch d'exemples
=================
- [Arduino_Softserial_Etiquette][3] Affiche des informations de téléinformation reçue étiquette par étiquette
- [Arduino_Softserial_Blink][11] Affiche des informations de téléinformation reçue trame par trame avec clignotement LED court/long si les données ont été modifiés
- [Arduino_Softserial_JSON][4] Retourne les informations de téléinformation au format JSON sur la liaison série.
- [Raspberry_JSON][12] Retourne les informations de téléinformation au format JSON sur stdout.
- [Wifinfo][5] ESP8266, ESP32 Wifi Teleinformation, Web + Rest + bonus, version en cours de développement, à venir mais un article [dédié][13] est déjà présent sur mon blog
- [ESP32][14] ESP32 Basic test pour WifInfo32 nouveau nom Denky :-)
Pourquoi
========
- J'utilise la téléinfo dans plusieurs de mes programmes et j'en avais marre de devoir faire des copier/coller de code constament, j'ai donc décidé de faire une librairie commune que j'utilise sans me poser de question
License
=======
Cette oeuvre est mise à disposition selon les termes de la Licence Creative Commons Attribution - Pas dUtilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International.
Si vous êtes une entreprise et que vous souhaitez participer car vous utilisez cette librairie dans du hardware (box, automate, ...), vous pouvez toujours m'envoyer un exemplaire de votre fabrication, c'est toujours sympa de voir ce qui est fait avec ce code ;-)
Divers
======
Vous pouvez aller voir les nouveautés et autres projets sur [blog][7]
[1]: http://www.erdf.fr/sites/default/files/ERDF-NOI-CPT_02E.pdf
[2]: http://learn.adafruit.com/arduino-tips-tricks-and-techniques/arduino-libraries
[6]: https://hallard.me/category/tinfo/
[7]: https://hallard.me
[8]: https://community.hallard.me/category/7
[9]: https://community.hallard.me
[10]: https://hallard.me/libteleinfo
[3]: https://github.com/hallard/LibTeleinfo/blob/master/examples/Arduino_Softserial/Arduino_Softserial_Etiquette.ino
[4]: https://github.com/hallard/LibTeleinfo/blob/master/examples/Arduino_Softserial_JSON/Arduino_Softserial_JSON.ino
[5]: https://github.com/hallard/LibTeleinfo/tree/master/examples/Wifinfo/Wifinfo.ino
[11]: https://github.com/hallard/LibTeleinfo/blob/master/examples/Arduino_Softserial/Arduino_Softserial_Blink.ino
[12]: https://github.com/hallard/LibTeleinfo/blob/master/examples/Raspberry_JSON/Raspberry_JSON.ino
[13]: https://hallard.me/wifiinfo/
[14]: https://github.com/hallard/LibTeleinfo/blob/master/examples/ESP32/ESP32.ino

19
lib/LibTeleinfo/library.json Executable file
View File

@ -0,0 +1,19 @@
{
"name": "LibTeleinfo",
"version": "1.1.2",
"keywords": "teleinfo, french, meter, power, erdf, linky, tic",
"description": "Decoder for Teleinfo (aka TIC) from French smart power meters",
"repository":
{
"type": "git",
"url": "https://github.com/hallard/LibTeleinfo.git"
},
"authors":
{
"name": "Charles-Henri Hallard",
"url": "http://hallard.me"
},
"frameworks": "arduino",
"platforms": "*"
}

View File

@ -0,0 +1,9 @@
name=LibTeleinfo
version=1.1.2
author=Charles-Henri Hallard <hallard.me>
maintainer=Charles-Henri Hallard <community.hallard.me>
sentence=Decoder for Teleinfo (aka TIC) from French smart power meters
paragraph=This is a generic Teleinfo (aka TIC) French Meter Measure Library, it can be used on Arduino, Particle, ESP8266, Raspberry PI or anywhere you can do Cpp coding.
category=Communication
url=https://github.com/hallard/LibTeleinfo
architectures=*

View File

@ -0,0 +1,881 @@
// **********************************************************************************
// Driver definition for French Teleinfo
// **********************************************************************************
// Creative Commons Attrib Share-Alike License
// You are free to use/extend this library but please abide with the CC-BY-SA license:
// http://creativecommons.org/licenses/by-sa/4.0/
//
// For any explanation about teleinfo ou use , see my blog
// http://hallard.me/category/tinfo
//
// Code based on following datasheet
// http://www.erdf.fr/sites/default/files/ERDF-NOI-CPT_02E.pdf
//
// Written by Charles-Henri Hallard (http://hallard.me)
//
// History : V1.00 2015-06-14 - First release
// V2.00 2020-06-11 - Integration into Tasmota
//
// All text above must be included in any redistribution.
//
// Edit : Tab size set to 2 but I converted tab to sapces
//
// **********************************************************************************
#include "Arduino.h"
#include "LibTeleinfo.h"
/* ======================================================================
Class : TInfo
Purpose : Constructor
Input : -
Output : -
Comments: -
====================================================================== */
TInfo::TInfo()
{
// Init of our linked list
_valueslist.name = NULL;
_valueslist.value = NULL;
_valueslist.checksum = '\0';
_valueslist.flags = TINFO_FLAGS_NONE;
// callback
_fn_ADPS = NULL;
_fn_data = NULL;
_fn_new_frame = NULL;
_fn_updated_frame = NULL;
}
/* ======================================================================
Function: init
Purpose : try to guess
Input : -
Output : -
Comments: -
====================================================================== */
void TInfo::init()
{
// free up linked list (in case on recall init())
listDelete();
// clear our receive buffer
clearBuffer();
// We're in INIT in term of receive data
_state = TINFO_INIT;
}
/* ======================================================================
Function: attachADPS
Purpose : attach a callback when we detected a ADPS on any phase
Input : callback function
Output : -
Comments: -
====================================================================== */
void TInfo::attachADPS(void (*fn_ADPS)(uint8_t phase))
{
// indicate the user callback
_fn_ADPS = fn_ADPS;
}
/* ======================================================================
Function: attachNewData
Purpose : attach a callback when we detected a new/changed value
Input : callback function
Output : -
Comments: -
====================================================================== */
void TInfo::attachData(void (*fn_data)(ValueList * valueslist, uint8_t state))
{
// indicate the user callback
_fn_data = fn_data;
}
/* ======================================================================
Function: attachNewFrame
Purpose : attach a callback when we received a full frame
Input : callback function
Output : -
Comments: -
====================================================================== */
void TInfo::attachNewFrame(void (*fn_new_frame)(ValueList * valueslist))
{
// indicate the user callback
_fn_new_frame = fn_new_frame;
}
/* ======================================================================
Function: attachChangedFrame
Purpose : attach a callback when we received a full frame where data
has changed since the last frame (cool to update data)
Input : callback function
Output : -
Comments: -
====================================================================== */
void TInfo::attachUpdatedFrame(void (*fn_updated_frame)(ValueList * valueslist))
{
// indicate the user callback
_fn_updated_frame = fn_updated_frame;
}
/* ======================================================================
Function: clearBuffer
Purpose : clear and init the buffer
Input : -
Output : -
Comments: -
====================================================================== */
void TInfo::clearBuffer()
{
// Clear our buffer, set index to 0
memset(_recv_buff, 0, TINFO_BUFSIZE);
_recv_idx = 0;
}
/* ======================================================================
Function: addCustomValue
Purpose : let user add custom values (mainly for testing)
Input : Pointer to the label name
pointer to the value
pointer on flag state of the label
Output : pointer to the new node (or founded one)
Comments: checksum is calculated before adding, no need to bother with
====================================================================== */
ValueList * TInfo::addCustomValue(char * name, char * value, uint8_t * flags)
{
// Little check
if (name && *name && value && *value) {
ValueList * me;
// Same as if we really received this line
customLabel(name, value, flags);
me = valueAdd(name, value, calcChecksum(name,value), flags);
if ( me ) {
// something to do with new datas
if (*flags & (TINFO_FLAGS_UPDATED | TINFO_FLAGS_ADDED | TINFO_FLAGS_ALERT) ) {
// this frame will for sure be updated
_frame_updated = true;
}
return (me);
}
}
// Error or Already Exists
return ( (ValueList *) NULL);
}
/* ======================================================================
Function: valueAdd
Purpose : Add element to the Linked List of values
Input : Pointer to the label name
pointer to the value
checksum value
flag state of the label (modified by function)
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)
{
// Get our linked list
ValueList * me = &_valueslist;
uint8_t lgname = strlen(name);
uint8_t lgvalue = strlen(value);
uint8_t thischeck = calcChecksum(name,value);
// just some paranoia
if (thischeck != checksum ) {
TI_Debug(name);
TI_Debug('=');
TI_Debug(value);
TI_Debug(F(" '"));
TI_Debug((char) checksum);
TI_Debug(F("' Not added bad checksum calculated '"));
TI_Debug((char) thischeck);
TI_Debugln(F("'"));
} else {
// Got one and all seems good ?
if (me && lgname && lgvalue && checksum) {
// Create pointer on the new node
ValueList *newNode = NULL;
ValueList *parNode = NULL ;
// Loop thru the node
while (me->next) {
// save parent node
parNode = me ;
// go to next node
me = me->next;
// Check if we already have this LABEL (same name AND same size)
if (lgname==strlen(me->name) && strcmp(me->name, name)==0) {
// Already got also this value return US
if (lgvalue==strlen(me->value) && strcmp(me->value, value) == 0) {
*flags |= TINFO_FLAGS_EXIST;
me->flags = *flags;
return ( me );
} else {
// We changed the value
*flags |= TINFO_FLAGS_UPDATED;
me->flags = *flags ;
// Do we have enought space to hold new value ?
if (strlen(me->value) >= lgvalue ) {
// Copy it
strlcpy(me->value, value , lgvalue + 1 );
me->checksum = checksum ;
// That's all
return (me);
} else {
// indicate our parent node that the next node
// is not us anymore but the next we have
parNode->next = me->next;
// free up this node
free (me);
// Return to parent (that will now point on next node and not us)
// and continue loop just in case we have sevral with same name
me = parNode;
}
}
}
}
// Our linked list structure sizeof(ValueList)
// + Name + '\0'
// + Value + '\0'
size_t size ;
#if defined (ESP8266) || defined (ESP32)
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 ) ;
#else
size = sizeof(ValueList) + lgname + 1 + lgvalue + 1 ;
#endif
// Create new node with size to store strings
if ((newNode = (ValueList *) malloc(size) ) == NULL)
return ( (ValueList *) NULL );
// get our buffer Safe
memset(newNode, 0, size);
// Put the new node on the list
me->next = newNode;
// First String located after last struct element
// Second String located after the First + \0
newNode->checksum = checksum;
newNode->name = (char *) newNode + sizeof(ValueList);
newNode->value = (char *) newNode->name + lgname + 1;
// Copy the string data
memcpy(newNode->name , name , lgname );
memcpy(newNode->value, value , lgvalue );
// So we just created this node but was it new
// or was matter of text size ?
if ( (*flags & TINFO_FLAGS_UPDATED) == 0) {
// so we added this node !
*flags |= TINFO_FLAGS_ADDED ;
newNode->flags = *flags;
}
TI_Debug(F("Added '"));
TI_Debug(name);
TI_Debug('=');
TI_Debug(value);
TI_Debug(F("' '"));
TI_Debug((char) checksum);
TI_Debugln(F("'"));
// return pointer on the new node
return (newNode);
}
} // Checksum OK
// Error or Already Exists
return ( (ValueList *) NULL);
}
/* ======================================================================
Function: valueRemoveFlagged
Purpose : remove element to the Linked List of values where
Input : paramter flags
Output : true if found and removed
Comments: -
====================================================================== */
boolean TInfo::valueRemoveFlagged(uint8_t flags)
{
boolean deleted = false;
// Get our linked list
ValueList * me = &_valueslist;
ValueList *parNode = NULL ;
// Got one and all seems good ?
if (me) {
// Loop thru the node
while (me->next) {
// save parent node
parNode = me ;
// go to next node
me = me->next;
// found the flags?
if (me->flags & flags ) {
// indicate our parent node that the next node
// is not us anymore but the next we have
parNode->next = me->next;
// free up this node
free (me);
// Return to parent (that will now point on next node and not us)
// and continue loop just in case we have sevral with same name
me = parNode;
deleted = true;
}
}
}
return (deleted);
}
/* ======================================================================
Function: valueRemove
Purpose : remove element to the Linked List of values
Input : Pointer to the label name
Output : true if found and removed
Comments: -
====================================================================== */
boolean TInfo::valueRemove(char * name)
{
boolean deleted = false;
// Get our linked list
ValueList * me = &_valueslist;
ValueList *parNode = NULL ;
uint8_t lgname = strlen(name);
// Got one and all seems good ?
if (me && lgname) {
// Loop thru the node
while (me->next) {
// save parent node
parNode = me ;
// go to next node
me = me->next;
// found ?
if (strncmp(me->name, name, lgname) == 0) {
// indicate our parent node that the next node
// is not us anymore but the next we have
parNode->next = me->next;
// free up this node
free (me);
// Return to parent (that will now point on next node and not us)
// and continue loop just in case we have sevral with same name
me = parNode;
deleted = true;
}
}
}
return (deleted);
}
/* ======================================================================
Function: valueGet
Purpose : get value of one element
Input : Pointer to the label name
pointer to the value where we fill data
Output : pointer to the value where we filled data NULL is not found
====================================================================== */
char * TInfo::valueGet(char * name, char * value)
{
// Get our linked list
ValueList * me = &_valueslist;
uint8_t lgname = strlen(name);
// Got one and all seems good ?
if (me && lgname) {
// Loop thru the node
while (me->next) {
// go to next node
me = me->next;
// Check if we match this LABEL
if (lgname==strlen(me->name) && strncmp(me->name, name, lgname)==0) {
// this one has a value ?
if (me->value) {
// copy to dest buffer
uint8_t lgvalue = strlen(me->value);
strlcpy(value, me->value , lgvalue + 1 );
return ( value );
}
}
}
}
// not found
return ( NULL);
}
/* ======================================================================
Function: valueGet_P
Purpose : get value of one element
Input : Pointer to the label name
pointer to the value where we fill data
Output : pointer to the value where we filled data NULL is not found
====================================================================== */
char * TInfo::valueGet_P(const char * name, char * value)
{
// Get our linked list
ValueList * me = &_valueslist;
uint8_t lgname = strlen_P(name);
// Got one and all seems good ?
if (me && lgname) {
// Loop thru the node
while (me->next) {
// go to next node
me = me->next;
// Check if we match this LABEL
if (lgname==strlen(me->name) && strncmp_P(me->name, name, lgname)==0) {
// this one has a value ?
if (me->value) {
// copy to dest buffer
uint8_t lgvalue = strlen(me->value);
strlcpy(value, me->value , lgvalue + 1 );
return ( value );
}
}
}
}
// not found
return ( NULL);
}
/* ======================================================================
Function: getTopList
Purpose : return a pointer on the top of the linked list
Input : -
Output : Pointer
====================================================================== */
ValueList * TInfo::getList(void)
{
// Get our linked list
return &_valueslist;
}
/* ======================================================================
Function: valuesDump
Purpose : dump linked list content
Input : -
Output : total number of values
====================================================================== */
uint8_t TInfo::valuesDump(void)
{
// Get our linked list
ValueList * me = &_valueslist;
uint8_t index = 0;
// Got one ?
if (me) {
// Loop thru the node
while (me->next) {
// go to next node
me = me->next;
index++;
TI_Debug(index) ;
TI_Debug(F(") ")) ;
if (me->name) {
TI_Debug(me->name) ;
} else {
TI_Debug(F("NULL")) ;
}
TI_Debug(F("=")) ;
if (me->value) {
TI_Debug(me->value) ;
} else {
TI_Debug(F("NULL")) ;
}
TI_Debug(F(" '")) ;
TI_Debug(me->checksum) ;
TI_Debug(F("' "));
// Flags management
if ( me->flags) {
TI_Debug(F("Flags:0x"));
TI_Debugf("%02X =>", me->flags);
if ( me->flags & TINFO_FLAGS_EXIST) {
TI_Debug(F("Exist ")) ;
}
if ( me->flags & TINFO_FLAGS_UPDATED) {
TI_Debug(F("Updated ")) ;
}
if ( me->flags & TINFO_FLAGS_ADDED) {
TI_Debug(F("New ")) ;
}
}
TI_Debugln() ;
}
}
return index;
}
/* ======================================================================
Function: labelCount
Purpose : Count the number of label in the list
Input : -
Output : element numbers
====================================================================== */
int TInfo::labelCount()
{
int count = 0;
// Get our linked list
ValueList * me = &_valueslist;
if (me)
while ((me = me->next))
count++;
return (count);
}
/* ======================================================================
Function: listDelete
Purpose : Delete the ENTIRE Linked List, not a value
Input : -
Output : True if Ok False Otherwise
====================================================================== */
boolean TInfo::listDelete()
{
// Get our linked list
ValueList * me = &_valueslist;
// Got a pointer
if (me) {
ValueList *current;
// For each linked list
while ((current = me->next)) {
// Get the next
me->next = current->next;
// Free the current
free(current);
}
// Free the top element
me->next = NULL ;
// Ok
return (true);
}
return (false);
}
/* ======================================================================
Function: checksum
Purpose : calculate the checksum based on data/value fields
Input : label name
label value
Output : checksum
Comments: return '\0' in case of error
====================================================================== */
unsigned char TInfo::calcChecksum(char *etiquette, char *valeur)
{
uint8_t sum = ' '; // Somme des codes ASCII du message + un espace
// avoid dead loop, always check all is fine
if (etiquette && valeur) {
// this will not hurt and may save our life ;-)
if (strlen(etiquette) && strlen(valeur)) {
while (*etiquette)
sum += *etiquette++ ;
while(*valeur)
sum += *valeur++ ;
return ( (sum & 63) + ' ' ) ;
}
}
return 0;
}
/* ======================================================================
Function: customLabel
Purpose : do action when received a correct label / value + checksum line
Input : plabel : pointer to string containing the label
pvalue : pointer to string containing the associated value
pflags pointer in flags value if we need to cchange it
Output :
Comments:
====================================================================== */
void TInfo::customLabel( char * plabel, char * pvalue, uint8_t * pflags)
{
int8_t phase = -1;
// Monophasé
if (strcmp(plabel, "ADPS")==0 )
phase=0;
// For testing
//if (strcmp(plabel, "IINST")==0 ) {
// *pflags |= TINFO_FLAGS_ALERT;
//}
// triphasé c'est ADIR + Num Phase
if (plabel[0]=='A' && plabel[1]=='D' && plabel[2]=='I' && plabel[3]=='R' && plabel[4]>='1' && plabel[4]<='3') {
phase = plabel[4]-'0';
}
// Nous avons un ADPS ?
if (phase>=0 && phase <=3) {
// ne doit pas être sauvé définitivement
*pflags |= TINFO_FLAGS_ALERT;
// Traitement de l'ADPS demandé par le sketch
if (_fn_ADPS)
_fn_ADPS(phase);
}
}
/* ======================================================================
Function: checkLine
Purpose : check one line of teleinfo received
Input : -
Output : pointer to the data object in the linked list if OK else NULL
Comments:
====================================================================== */
ValueList * TInfo::checkLine(char * pline)
{
char * p;
char * ptok;
char * pend;
char * pvalue;
char checksum;
char buff[TINFO_BUFSIZE];
uint8_t flags = TINFO_FLAGS_NONE;
//boolean err = true ; // Assume error
int len ; // Group len
if (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 )
return NULL;
// Get our own working copy
strlcpy( buff, pline, len+1);
p = &buff[0];
ptok = p; // for sure we start with token name
pend = p + len; // max size
// Init values
pvalue = NULL;
checksum = 0;
//TI_Debug("Got [");
//TI_Debug(len);
//TI_Debug("] ");
// Loop in buffer
while ( p < pend ) {
// start of token value
if ( *p==' ' && 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;
}
// new line ? ok we got all we need ?
if ( *p=='\r' ) {
*p='\0';
// Good format ?
if ( ptok && pvalue && checksum ) {
// Always check to avoid bad behavior
if(strlen(ptok) && strlen(pvalue)) {
// Is checksum is OK
if ( calcChecksum(ptok,pvalue) == 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);
// value correctly added/changed
if ( me ) {
// something to do with new datas
if (flags & (TINFO_FLAGS_UPDATED | TINFO_FLAGS_ADDED | TINFO_FLAGS_ALERT) ) {
// this frame will for sure be updated
_frame_updated = true;
// Do we need to advertise user callback
if (_fn_data)
_fn_data(me, flags);
}
}
}
}
}
}
// Next char
p++;
} // While
return NULL;
}
/* ======================================================================
Function: process
Purpose : teleinfo serial char received processing, should be called
my main loop, this will take care of managing all the other
Input : pointer to the serial used
Output : teleinfo global state
====================================================================== */
_State_e TInfo::process(char c)
{
// be sure 7 bits only
c &= 0x7F;
// What we received ?
switch (c) {
// start of transmission ???
case TINFO_STX:
// Clear buffer, begin to store in it
clearBuffer();
// by default frame is not "updated"
// if data change we'll set this flag
_frame_updated = false;
// We were waiting fo this one ?
if (_state == TINFO_INIT || _state == TINFO_WAIT_STX ) {
TI_Debugln(F("TINFO_WAIT_ETX"));
_state = TINFO_WAIT_ETX;
}
break;
// End of transmission ?
case TINFO_ETX:
// Normal working mode ?
if (_state == TINFO_READY) {
// Get on top of our linked list
ValueList * me = &_valueslist;
// Call user callback if any
if (_frame_updated && _fn_updated_frame)
_fn_updated_frame(me);
else if (_fn_new_frame)
_fn_new_frame(me);
#ifdef TI_Debug
valuesDump();
#endif
// It's important there since all user job is done
// to remove the alert flags from table (ADPS for example)
// it will be put back again next time if any
valueRemoveFlagged(TINFO_FLAGS_ALERT);
}
// We were waiting fo this one ?
if (_state == TINFO_WAIT_ETX) {
TI_Debugln(F("TINFO_READY"));
_state = TINFO_READY;
}
else if ( _state == TINFO_INIT) {
TI_Debugln(F("TINFO_WAIT_STX"));
_state = TINFO_WAIT_STX ;
}
break;
// Start of group \n ?
case TINFO_SGR:
// Do nothing we'll work at end of group
// we can safely ignore this char
break;
// End of group \r ?
case TINFO_EGR:
// Are we ready to process ?
if (_state == TINFO_READY) {
// Store data recceived (we'll need it)
if ( _recv_idx < TINFO_BUFSIZE)
_recv_buff[_recv_idx++]=c;
// clear the end of buffer (paranoia inside)
memset(&_recv_buff[_recv_idx], 0, TINFO_BUFSIZE-_recv_idx);
// check the group we've just received
checkLine(_recv_buff) ;
// Whatever error or not, we done
clearBuffer();
}
break;
// other char ?
default:
{
// Only in a ready state of course
if (_state == TINFO_READY) {
// If buffer is not full, Store data
if ( _recv_idx < TINFO_BUFSIZE)
_recv_buff[_recv_idx++]=c;
else
clearBuffer();
}
}
break;
}
return _state;
}

147
lib/LibTeleinfo/src/LibTeleinfo.h Executable file
View File

@ -0,0 +1,147 @@
// **********************************************************************************
// Driver definition for French Teleinfo
// **********************************************************************************
// Creative Commons Attrib Share-Alike License
// You are free to use/extend this library but please abide with the CC-BY-SA license:
// http://creativecommons.org/licenses/by-sa/4.0/
//
// For any explanation about teleinfo ou use , see my blog
// http://hallard.me/category/tinfo
//
// Code based on following datasheet
// http://www.erdf.fr/sites/default/files/ERDF-NOI-CPT_02E.pdf
//
// Written by Charles-Henri Hallard (http://hallard.me)
//
// History : V1.00 2015-06-14 - First release
// V2.00 2020-06-11 - Integration into Tasmota
//
// All text above must be included in any redistribution.
//
// Edit : Tab size set to 2 but I converted tab to sapces
//
// **********************************************************************************
#ifndef LibTeleinfo_h
#define LibTeleinfo_h
#include "Arduino.h"
// Define this if you want library to be verbose
//#define TI_DEBUG
// I prefix debug macro to be sure to use specific for THIS library
// debugging, this should not interfere with main sketch or other
// libraries
#ifdef TI_DEBUG
#ifdef ESP8266
#define TI_Debug(x) Serial1.print(x)
#define TI_Debugln(x) Serial1.println(x)
#define TI_Debugf(...) Serial1.printf(__VA_ARGS__)
#define TI_Debugflush Serial1.flush
#else
#define TI_Debug(x) Serial.print(x)
#define TI_Debugln(x) Serial.println(x)
#define TI_Debugf(...) Serial.printf(__VA_ARGS__)
#define TI_Debugflush Serial.flush
#endif
#else
#define TI_Debug(x) {}
#define TI_Debugln(x) {}
#define TI_Debugf(...) {}
#define TI_Debugflush {}
#endif
// For 4 bytes Aligment boundaries
#define ESP_allocAlign(size) ((size + 3) & ~((size_t) 3))
#pragma pack(push) // push current alignment to stack
#pragma pack(1) // set alignment to 1 byte boundary
// Linked list structure containing all values received
typedef struct _ValueList ValueList;
struct _ValueList
{
ValueList *next; // next element
uint8_t checksum;// checksum
uint8_t flags; // specific flags
char * name; // LABEL of value name
char * value; // value
};
#pragma pack(pop)
// Library state machine
enum _State_e {
TINFO_INIT, // We're in init
TINFO_WAIT_STX, // We're waiting for STX
TINFO_WAIT_ETX, // We had STX, We're waiting for ETX
TINFO_READY // We had STX AND ETX, So we're OK
};
// what we done with received value (also for callback flags)
#define TINFO_FLAGS_NONE 0x00
#define TINFO_FLAGS_NOTHING 0x01
#define TINFO_FLAGS_ADDED 0x02
#define TINFO_FLAGS_EXIST 0x04
#define TINFO_FLAGS_UPDATED 0x08
#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
#define TINFO_BUFSIZE 64
// Teleinfo start and end of frame characters
#define TINFO_STX 0x02
#define TINFO_ETX 0x03
#define TINFO_SGR '\n' // start of group
#define TINFO_EGR '\r' // End of group
typedef void (*_fn_ADPS) (uint8_t);
typedef void (*_fn_data) (ValueList *, uint8_t);
typedef void (*_fn_new_frame) (ValueList *);
typedef void (*_fn_updated_frame) (ValueList *);
class TInfo
{
public:
TInfo();
void init();
_State_e process (char c);
void attachADPS(void (*_fn_ADPS)(uint8_t phase));
void attachData(void (*_fn_data)(ValueList * valueslist, uint8_t state));
void attachNewFrame(void (*_fn_new_frame)(ValueList * valueslist));
void attachUpdatedFrame(void (*_fn_updated_frame)(ValueList * valueslist));
ValueList * addCustomValue(char * name, char * value, uint8_t * flags);
ValueList * getList(void);
uint8_t valuesDump(void);
char * valueGet(char * name, char * value);
char * valueGet_P(const char * name, char * value);
boolean listDelete();
unsigned char calcChecksum(char *etiquette, char *valeur) ;
private:
void clearBuffer();
ValueList * valueAdd (char * name, char * value, uint8_t checksum, uint8_t * flags);
boolean valueRemove (char * name);
boolean valueRemoveFlagged(uint8_t flags);
int labelCount();
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
uint8_t _recv_idx; // index in receive buffer
boolean _frame_updated; // Data on the frame has been updated
void (*_fn_ADPS)(uint8_t phase);
void (*_fn_data)(ValueList * valueslist, uint8_t state);
void (*_fn_new_frame)(ValueList * valueslist);
void (*_fn_updated_frame)(ValueList * valueslist);
//volatile uint8_t *dcport;
//uint8_t dcpinmask;
};
#endif

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL - TX" #define D_SENSOR_ELECTRIQ_MOODL "MOODL - TX"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "Velocità vento" #define D_SENSOR_WINDMETER_SPEED "Velocità vento"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -679,6 +679,8 @@
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_AS3935 "AS3935"
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK"

View File

@ -600,6 +600,8 @@
#define USE_TASMOTA_SLAVE_FLASH_SPEED 57600 // Usually 57600 for 3.3V variants and 115200 for 5V variants #define USE_TASMOTA_SLAVE_FLASH_SPEED 57600 // Usually 57600 for 3.3V variants and 115200 for 5V variants
#define USE_TASMOTA_SLAVE_SERIAL_SPEED 57600 // Depends on the sketch that is running on the Uno/Pro Mini #define USE_TASMOTA_SLAVE_SERIAL_SPEED 57600 // Depends on the sketch that is running on the Uno/Pro Mini
//#define USE_OPENTHERM // Add support for OpenTherm (+15k code) //#define USE_OPENTHERM // Add support for OpenTherm (+15k code)
#define USE_TELEINFO // Add support for Teleinfo via serial RX interface
//#define USE_TELEINFO_STANDARD // Use standard mode (9600 bps) else it's historical mode (1200 bps)
// -- Power monitoring sensors -------------------- // -- Power monitoring sensors --------------------
#define USE_ENERGY_MARGIN_DETECTION // Add support for Energy Margin detection (+1k6 code) #define USE_ENERGY_MARGIN_DETECTION // Add support for Energy Margin detection (+1k6 code)

View File

@ -584,7 +584,10 @@ void GetFeatures(void)
#ifdef USE_TCP_BRIDGE #ifdef USE_TCP_BRIDGE
feature6 |= 0x00020000; // xdrv_41_tcp_bridge.ino feature6 |= 0x00020000; // xdrv_41_tcp_bridge.ino
#endif #endif
// feature6 |= 0x00040000; #ifdef USE_TELEINFO
feature6 |= 0x00040000; // xnrg_15_teleinfo.ino
#endif
// feature6 |= 0x00080000; // feature6 |= 0x00080000;
// feature6 |= 0x00100000; // feature6 |= 0x00100000;

View File

@ -168,6 +168,7 @@
#define USE_DDSU666 // Add support for Chint DDSU666 Modbus energy monitor (+0k6 code) #define USE_DDSU666 // Add support for Chint DDSU666 Modbus energy monitor (+0k6 code)
//#define USE_SOLAX_X1 // Add support for Solax X1 series Modbus log info (+3k1 code) //#define USE_SOLAX_X1 // Add support for Solax X1 series Modbus log info (+3k1 code)
//#define USE_LE01MR // Add support for F&F LE-01MR modbus energy meter (+2k code) //#define USE_LE01MR // Add support for F&F LE-01MR modbus energy meter (+2k code)
//#define USE_TELEINFO // Add support for French Energy Provider metering telemetry (+5k2 code, +168 RAM + SmartMeter LinkedList Values RAM)
#define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor #define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor
#define USE_MAX31855 // Add support for MAX31855 K-Type thermocouple sensor using softSPI #define USE_MAX31855 // Add support for MAX31855 K-Type thermocouple sensor using softSPI
@ -264,6 +265,8 @@
#undef USE_DDSU666 // Disable support for Chint DDSU666 Modbus energy monitor (+0k6 code) #undef USE_DDSU666 // Disable support for Chint DDSU666 Modbus energy monitor (+0k6 code)
#undef USE_SOLAX_X1 // Disable support for Solax X1 series Modbus log info (+3k1 code) #undef USE_SOLAX_X1 // Disable support for Solax X1 series Modbus log info (+3k1 code)
#undef USE_LE01MR // Disable support for F&F LE-01MR Modbus energy meter (+2k code) #undef USE_LE01MR // Disable support for F&F LE-01MR Modbus energy meter (+2k code)
#undef USE_TELEINFO // Disable support for French Energy Provider metering telemetry
#define USE_I2C // I2C using library wire (+10k code, 0k2 mem, 124 iram) #define USE_I2C // I2C using library wire (+10k code, 0k2 mem, 124 iram)
#define USE_DISPLAY // Add I2C Display Support (+2k code) #define USE_DISPLAY // Add I2C Display Support (+2k code)
@ -354,6 +357,7 @@
#undef USE_DDSU666 // Disable support for Chint DDSU666 Modbus energy monitor (+0k6 code) #undef USE_DDSU666 // Disable support for Chint DDSU666 Modbus energy monitor (+0k6 code)
#undef USE_SOLAX_X1 // Disable support for Solax X1 series Modbus log info (+3k1 code) #undef USE_SOLAX_X1 // Disable support for Solax X1 series Modbus log info (+3k1 code)
#undef USE_LE01MR // Disable support for F&F LE-01MR Modbus energy meter (+2k code) #undef USE_LE01MR // Disable support for F&F LE-01MR Modbus energy meter (+2k code)
#undef USE_TELEINFO // Disable support for French Energy Provider metering telemetry
//#undef USE_DS18x20 // Disable support for DS18x20 sensors with id sort, single scan and read retry (+1k3 code) //#undef USE_DS18x20 // Disable support for DS18x20 sensors with id sort, single scan and read retry (+1k3 code)
@ -497,6 +501,8 @@
#undef USE_DDSU666 // Disable support for Chint DDSU666 Modbus energy monitor (+0k6 code) #undef USE_DDSU666 // Disable support for Chint DDSU666 Modbus energy monitor (+0k6 code)
#undef USE_SOLAX_X1 // Disable support for Solax X1 series Modbus log info (+3k1 code) #undef USE_SOLAX_X1 // Disable support for Solax X1 series Modbus log info (+3k1 code)
#undef USE_LE01MR // Disable support for F&F LE-01MR Modbus energy meter (+2k code) #undef USE_LE01MR // Disable support for F&F LE-01MR Modbus energy meter (+2k code)
#undef USE_TELEINFO // Disable support for French Energy Provider metering telemetry
#undef USE_DHT // Disable support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor #undef USE_DHT // Disable support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor
#undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI #undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI
@ -622,6 +628,7 @@
#undef USE_DDSU666 // Disable support for Chint DDSU666 Modbus energy monitor (+0k6 code) #undef USE_DDSU666 // Disable support for Chint DDSU666 Modbus energy monitor (+0k6 code)
#undef USE_SOLAX_X1 // Disable support for Solax X1 series Modbus log info (+3k1 code) #undef USE_SOLAX_X1 // Disable support for Solax X1 series Modbus log info (+3k1 code)
#undef USE_LE01MR // Disable support for F&F LE-01MR Modbus energy meter (+2k code) #undef USE_LE01MR // Disable support for F&F LE-01MR Modbus energy meter (+2k code)
#undef USE_TELEINFO // Disable support for French Energy Provider metering telemetry
#undef USE_DHT // Disable support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor #undef USE_DHT // Disable support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor
#undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI #undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI

View File

@ -236,6 +236,8 @@ enum UserSelectablePins {
GPIO_BL0940_RX, // BL0940 serial interface GPIO_BL0940_RX, // BL0940 serial interface
GPIO_TCP_TX, // TCP Serial bridge GPIO_TCP_TX, // TCP Serial bridge
GPIO_TCP_RX, // TCP Serial bridge GPIO_TCP_RX, // TCP Serial bridge
GPIO_TELEINFO_RX, // TELEINFO serial interface
GPIO_TELEINFO_ENABLE,// TELEINFO Enable PIN
GPIO_SENSOR_END }; GPIO_SENSOR_END };
// Programmer selectable GPIO functionality // Programmer selectable GPIO functionality
@ -326,8 +328,9 @@ const char kSensorNames[] PROGMEM =
D_SENSOR_AS3935 "|" D_SENSOR_PMS5003_TX "|" D_SENSOR_AS3935 "|" D_SENSOR_PMS5003_TX "|"
D_SENSOR_BOILER_OT_RX "|" D_SENSOR_BOILER_OT_TX "|" D_SENSOR_BOILER_OT_RX "|" D_SENSOR_BOILER_OT_TX "|"
D_SENSOR_WINDMETER_SPEED "|" D_SENSOR_WINDMETER_SPEED "|"
D_SENSOR_BL0940_RX "|" D_SENSOR_BL0940_RX "|"
D_SENSOR_TCP_TXD "|" D_SENSOR_TCP_RXD D_SENSOR_TCP_TXD "|" D_SENSOR_TCP_RXD "|"
D_SENSOR_TELEINFO_RX "|" D_SENSOR_TELEINFO_ENABLE
; ;
const char kSensorNamesFixed[] PROGMEM = const char kSensorNamesFixed[] PROGMEM =
@ -689,6 +692,10 @@ const uint8_t kGpioNiceList[] PROGMEM = {
#ifdef USE_AS3935 #ifdef USE_AS3935
GPIO_AS3935, GPIO_AS3935,
#endif #endif
#ifdef USE_TELEINFO
GPIO_TELEINFO_RX,
GPIO_TELEINFO_ENABLE,
#endif
}; };
/********************************************************************************************/ /********************************************************************************************/
@ -776,7 +783,7 @@ enum SupportedModules {
SUPLA1, WITTY, YUNSHAN, MAGICHOME, LUANIHVIO, KMC_70011, ARILUX_LC01, ARILUX_LC11, SONOFF_DUAL_R2, ARILUX_LC06, SUPLA1, WITTY, YUNSHAN, MAGICHOME, LUANIHVIO, KMC_70011, ARILUX_LC01, ARILUX_LC11, SONOFF_DUAL_R2, ARILUX_LC06,
SONOFF_S31, ZENGGE_ZF_WF017, SONOFF_POW_R2, SONOFF_IFAN02, BLITZWOLF_BWSHP, SHELLY1, SHELLY2, PHILIPS, NEO_COOLCAM, ESP_SWITCH, SONOFF_S31, ZENGGE_ZF_WF017, SONOFF_POW_R2, SONOFF_IFAN02, BLITZWOLF_BWSHP, SHELLY1, SHELLY2, PHILIPS, NEO_COOLCAM, ESP_SWITCH,
OBI, TECKIN, APLIC_WDP303075, TUYA_DIMMER, GOSUND, ARMTRONIX_DIMMERS, SK03_TUYA, PS_16_DZ, TECKIN_US, MANZOKU_EU_4, OBI, TECKIN, APLIC_WDP303075, TUYA_DIMMER, GOSUND, ARMTRONIX_DIMMERS, SK03_TUYA, PS_16_DZ, TECKIN_US, MANZOKU_EU_4,
OBI2, YTF_IR_BRIDGE, DIGOO, KA10, ZX2820, MI_DESK_LAMP, SP10, WAGA, SYF05, SONOFF_L1, OBI2, YTF_IR_BRIDGE, DIGOO, KA10, ZX2820, MI_DESK_LAMP, SP10, WAGA, SYF05, SONOFF_L1,
SONOFF_IFAN03, EXS_DIMMER, PWM_DIMMER, SONOFF_D1, SONOFF_IFAN03, EXS_DIMMER, PWM_DIMMER, SONOFF_D1,
MAXMODULE}; MAXMODULE};
@ -2257,8 +2264,10 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
GPIO_LED1_INV, // GPIO13 WiFi Blue Led - Link and Power status GPIO_LED1_INV, // GPIO13 WiFi Blue Led - Link and Power status
0, 0, 0, 0 0, 0, 0, 0
} }
}; };
#endif // ESP8266 #endif // ESP8266
#ifdef ESP32 #ifdef ESP32

View File

@ -131,6 +131,8 @@ enum UserSelectablePins {
GPIO_BL0940_RX, // BL0940 serial interface GPIO_BL0940_RX, // BL0940 serial interface
GPIO_TCP_TX, GPIO_TCP_RX, // TCP to serial bridge GPIO_TCP_TX, GPIO_TCP_RX, // TCP to serial bridge
GPIO_ETH_PHY_POWER, GPIO_ETH_PHY_MDC, GPIO_ETH_PHY_MDIO, // Ethernet GPIO_ETH_PHY_POWER, GPIO_ETH_PHY_MDC, GPIO_ETH_PHY_MDIO, // Ethernet
GPIO_TELEINFO_RX, // Teleinfo telemetry data receive pin
GPIO_TELEINFO_ENABLE, // Teleinfo Enable Receive Pin
GPIO_SENSOR_END }; GPIO_SENSOR_END };
enum ProgramSelectablePins { enum ProgramSelectablePins {
@ -222,7 +224,8 @@ const char kSensorNames[] PROGMEM =
D_SENSOR_WINDMETER_SPEED "|" D_SENSOR_BUTTON "_tc|" D_SENSOR_WINDMETER_SPEED "|" D_SENSOR_BUTTON "_tc|"
D_SENSOR_BL0940_RX "|" D_SENSOR_BL0940_RX "|"
D_SENSOR_TCP_TXD "|" D_SENSOR_TCP_RXD "|" D_SENSOR_TCP_TXD "|" D_SENSOR_TCP_RXD "|"
D_SENSOR_ETH_PHY_POWER "|" D_SENSOR_ETH_PHY_MDC "|" D_SENSOR_ETH_PHY_MDIO D_SENSOR_ETH_PHY_POWER "|" D_SENSOR_ETH_PHY_MDC "|" D_SENSOR_ETH_PHY_MDIO "|"
D_SENSOR_TELEINFO_RX "|" D_SENSOR_TELEINFO_ENABLE
; ;
const char kSensorNamesFixed[] PROGMEM = const char kSensorNamesFixed[] PROGMEM =
@ -532,6 +535,10 @@ const uint16_t kGpioNiceList[] PROGMEM = {
#ifdef USE_AS3935 #ifdef USE_AS3935
AGPIO(GPIO_AS3935), AGPIO(GPIO_AS3935),
#endif #endif
#ifdef USE_TELEINFO
AGPIO(GPIO_TELEINFO_RX),
AGPIO(GPIO_TELEINFO_ENABLE),
#endif
/* /*
#ifndef USE_ADC_VCC #ifndef USE_ADC_VCC
AGPIO(ADC0_INPUT), // Analog input AGPIO(ADC0_INPUT), // Analog input
@ -616,7 +623,7 @@ typedef struct MYTMPLT {
/********************************************************************************************/ /********************************************************************************************/
// Supported hardware modules // Supported hardware modules
enum SupportedModules { enum SupportedModules {
WEMOS, ESP32_CAM_AITHINKER, WEMOS, ESP32_CAM_AITHINKER,
MAXMODULE}; MAXMODULE};
#define USER_MODULE 255 #define USER_MODULE 255

View File

@ -0,0 +1,424 @@
/*
xnrg_15_Teleinfo.ino - Teleinfo support for Tasmota
Copyright (C) 2020 Charles-Henri Hallard
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef USE_ENERGY_SENSOR
#ifdef USE_TELEINFO
/*********************************************************************************************\
* Teleinfo : French energy provider metering telemety data
* Source: http://hallard.me/category/tinfo/
*
* Hardware Serial will be selected if GPIO1 = [TELEINFO_RX]
\*********************************************************************************************/
#define XNRG_15 15
#include "LibTeleinfo.h"
#include <TasmotaSerial.h>
#define TINFO_READ_TIMEOUT 400
enum TInfoContrat{
CONTRAT_BAS = 1, // BASE => Option Base.
CONTRAT_HC, // HC.. => Option Heures Creuses.
CONTRAT_EJP, // EJP. => Option EJP.
CONTRAT_BBR // BBRx => Option Tempo
};
enum TInfoTarif{
TARIF_TH = 1, // Toutes les Heures.
TARIF_HC, // Heures Creuses.
TARIF_HP, // Heures Pleines.
TARIF_HN, // BBRx => Option Tempo
TARIF_PM, // Heures de Pointe Mobile.
TARIF_CB, // Heures Creuses Jours Bleus.
TARIF_CW, // Heures Creuses Jours Blancs (White).
TARIF_CR, // Heures Creuses Jours Rouges.
TARIF_PB, // Heures Pleines Jours Bleus.
TARIF_PW, // Heures Pleines Jours Blancs (White).
TARIF_PR // Heures Pleines Jours Rouges.
};
// Label received
const char LABEL_HCHC[] PROGMEM = "HCHC";
const char LABEL_HCHP[] PROGMEM = "HCHP";
const char LABEL_PTEC[] PROGMEM = "PTEC";
const char LABEL_PAPP[] PROGMEM = "PAPP";
const char LABEL_IINST[] PROGMEM = "IINST";
const char LABEL_TENSION[] PROGMEM = "TENSION";
// Some Values with string to compare to
const char VALUE_HCDD[] PROGMEM = "HC..";
const char kTARIF_TH[] PROGMEM = "Toutes";
const char kTARIF_HC[] PROGMEM = "Creuses";
const char kTARIF_HP[] PROGMEM = "Pleines";
const char kTARIF_HN[] PROGMEM = "Normales";
const char kTARIF_PM[] PROGMEM = "Pointe Mobile";
const char kTARIF_CB[] PROGMEM = "Creuses Bleu";
const char kTARIF_CW[] PROGMEM = "Creuses Blanc";
const char kTARIF_CR[] PROGMEM = "Creuses Rouge";
const char kTARIF_PB[] PROGMEM = "Pleines Bleu";
const char kTARIF_PW[] PROGMEM = "Pleines Blanc";
const char kTARIF_PR[] PROGMEM = "Pleines Rouge";
const char * kTtarifNames[] PROGMEM = {
kTARIF_TH,
kTARIF_HC, kTARIF_HP,
kTARIF_HN, kTARIF_PM,
kTARIF_CB, kTARIF_CW, kTARIF_CR, kTARIF_PB, kTARIF_PW, kTARIF_PR
};
TInfo tinfo; // Teleinfo object
TasmotaSerial *TInfoSerial = nullptr;
bool tinfo_found = false;
uint8_t contrat;
uint8_t tarif;
/*********************************************************************************************/
/* ======================================================================
Function: ADPSCallback
Purpose : called by library when we detected a ADPS on any phased
Input : phase number
0 for ADPS (monophase)
1 for ADIR1 triphase
2 for ADIR2 triphase
3 for ADIR3 triphase
Output : -
Comments: should have been initialised in the main sketch with a
tinfo.attachADPSCallback(ADPSCallback())
====================================================================== */
void ADPSCallback(uint8_t phase)
{
// n = phase number 1 to 3
if (phase == 0)
phase = 1;
AddLog_P2(LOG_LEVEL_INFO, PSTR("ADPS on phase %d"), phase);
}
/* ======================================================================
Function: DataCallback
Purpose : callback when we detected new or modified data received
Input : linked list pointer on the concerned data
current flags value
Output : -
Comments: -
====================================================================== */
void DataCallback(struct _ValueList * me, uint8_t flags)
{
char c = ' ';
// Does this value is new or changed?
if (flags & (TINFO_FLAGS_ADDED | TINFO_FLAGS_UPDATED) )
{
if (flags & TINFO_FLAGS_ADDED) { c = '#'; }
if (flags & TINFO_FLAGS_UPDATED) { c = '*'; }
// Current tarif
if (!strcmp_P(LABEL_PTEC, me->name))
{
if (!strcmp_P("TH..", me->name)) { tarif = TARIF_TH; }
else if (!strcmp_P("HC..", me->name)) { tarif = TARIF_HC; }
else if (!strcmp_P("HP..", me->name)) { tarif = TARIF_HP; }
else if (!strcmp_P("HN..", me->name)) { tarif = TARIF_HN; }
else if (!strcmp_P("PM..", me->name)) { tarif = TARIF_PM; }
else if (!strcmp_P("HCJB", me->name)) { tarif = TARIF_CB; }
else if (!strcmp_P("HCJW", me->name)) { tarif = TARIF_CW; }
else if (!strcmp_P("HCJR", me->name)) { tarif = TARIF_CR; }
else if (!strcmp_P("HPJB", me->name)) { tarif = TARIF_PB; }
else if (!strcmp_P("HPJW", me->name)) { tarif = TARIF_PW; }
else if (!strcmp_P("HPJR", me->name)) { tarif = TARIF_PR; }
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: Tarif changed, now '%s' (%d)"), me->value, tarif);
}
// Voltage V (not present on all Smart Meter)
else if (!strcmp_P(LABEL_TENSION, me->name))
{
Energy.voltage_available = true;
int i = 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, i);
}
// Current I
else if (!strcmp_P(LABEL_IINST, me->name))
{
if (!Energy.voltage_available) {
int i = 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]);
}
// Current P
else if (!strcmp_P(LABEL_PAPP, me->name))
{
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);
// Update current
if (Energy.voltage_available && Energy.voltage[0]) {
Energy.current[0] = Energy.active_power[0] / Energy.voltage[0] ;
}
}
// kWh indexes
else if (!strcmp_P(LABEL_HCHC, me->name) || !strcmp(LABEL_HCHP, me->name))
{
char value[32];
unsigned long hc = 0;
unsigned long hp = 0;
unsigned long total = 0;
if ( tinfo.valueGet_P(LABEL_HCHC, value) ) { hc = atol(value);}
if ( tinfo.valueGet_P(LABEL_HCHP, value) ) { hp = atol(value);}
total = hc+hp;
EnergyUpdateTotal(total/1000.0f, true);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: HC:%ld HP:%ld Total:%ld"), hc, hp, total);
}
}
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: %c %s=%s"),c , me->name, me->value);
}
/* ======================================================================
Function: NewFrameCallback
Purpose : callback when we received a complete Teleinfo frama
Input : linked list pointer on the concerned data
Output : -
Comments: -
====================================================================== */
void NewFrameCallback(struct _ValueList * me)
{
// Reset Energy Watchdog
Energy.data_valid[0] = 0;
}
/* ======================================================================
Function: NewFrameCallback
Purpose : callback when we received a complete Teleinfo frama
Input : label to search for
Output : value filled
Comments: -
====================================================================== */
char * getDataValue_P(const char * label, char * value)
{
if (!tinfo.valueGet_P(label, value) ) {
*value = '\0';
}
return value;
}
void TInfoDrvInit(void) {
if (PinUsed(GPIO_TELEINFO_RX)) {
energy_flg = XNRG_15;
Energy.voltage_available = false;
//Energy.current_available = false;
Energy.type_dc = true;
}
}
void TInfoInit(void)
{
#ifdef USE_TELEINFO_STANDARD
#define TINFO_SPEED 9600
#else
#define TINFO_SPEED 1200
#endif
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TIC: inferface speed %d bps"),TINFO_SPEED);
if (PinUsed(GPIO_TELEINFO_RX))
{
uint8_t rx_pin = Pin(GPIO_TELEINFO_RX);
AddLog_P2(LOG_LEVEL_INFO, PSTR("TIC: RX on GPIO%d"), rx_pin);
// Enable Teleinfo
if (PinUsed(GPIO_TELEINFO_ENABLE))
{
uint8_t en_pin = Pin(GPIO_TELEINFO_ENABLE);
pinMode(en_pin, OUTPUT);
digitalWrite(en_pin, HIGH);
AddLog_P2(LOG_LEVEL_INFO, PSTR("TIC: Enable with GPIO%d"), en_pin);
}
else
{
AddLog_P2(LOG_LEVEL_INFO, PSTR("TIC: always enabled"));
}
TInfoSerial = new TasmotaSerial(rx_pin, -1, 1);
// pinMode(GPIO_TELEINFO_RX, INPUT_PULLUP);
// 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 defined (ESP8266)
if (TInfoSerial->hardwareSerial() ) {
ClaimSerial();
AddLog_P2(LOG_LEVEL_INFO, PSTR("TIC: using hardware serial"));
} else {
AddLog_P2(LOG_LEVEL_INFO, PSTR("TIC: using software serial"));
}
// This is a dirty hack, looks like begin does not take into account
// the TS_SERIAL_7E1 configuration so on ESP8266 this is
// working only on Serial RX pin (Hardware Serial) for now
SetSerialConfig(TS_SERIAL_7E1);
TInfoSerial->setTimeout(TINFO_READ_TIMEOUT);
#elif defined (ESP32)
AddLog_P2(LOG_LEVEL_INFO, PSTR("TIC: using ESP32 hardware serial"));
// Waiting TasmotaSerial PR merged to change that
//TInfoSerial->reconf(TINFO_SPEED, SERIAL_7E1);
#endif
// Init teleinfo
tinfo.init();
// Attach needed callbacks
tinfo.attachADPS(ADPSCallback);
tinfo.attachData(DataCallback);
tinfo.attachNewFrame(NewFrameCallback);
tinfo_found = true;
AddLog_P2(LOG_LEVEL_INFO, PSTR("TIC: Ready"));
}
}
}
void TInfoLoop(void)
{
char c;
if (!tinfo_found)
return;
if (TInfoSerial->available()) {
//AddLog_P2(LOG_LEVEL_INFO, PSTR("TIC: received %d chars"), TInfoSerial->available());
// We received some data?
while (TInfoSerial->available()>8)
{
// get char
c = TInfoSerial->read();
// data processing
tinfo.process(c);
}
}
}
void TInfoEvery250ms(void)
{
}
#ifdef USE_WEBSERVER
const char HTTP_ENERGY_INDEX_TELEINFO[] PROGMEM = "{s}%s{m}%s " D_UNIT_KILOWATTHOUR "{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_TARIF_TELEINFO[] PROGMEM = "{s}Tarif{m}%s{e}" ;
#endif // USE_WEBSERVER
void TInfoShow(bool json)
{
char value[32];
// TBD
if (json)
{
if ( tinfo.valueGet_P(LABEL_PTEC, value) ) {
ResponseAppend_P(PSTR(",\"" "TARIF" "\":%s"), value);
}
if ( tinfo.valueGet_P(LABEL_IINST, value) ) {
ResponseAppend_P(PSTR(",\"" D_CURRENT "\":%s"), value);
}
if ( tinfo.valueGet_P(LABEL_PAPP, value) ) {
ResponseAppend_P(PSTR(",\"" D_POWERUSAGE "\":%s"), value);
}
if ( tinfo.valueGet_P(LABEL_HCHC, value) ) {
ResponseAppend_P(PSTR(",\"" "HC" "\":%s"), value);
}
if ( tinfo.valueGet_P(LABEL_HCHP, value) ) {
ResponseAppend_P(PSTR(",\"" "HP" "\":%s"), value);
}
#ifdef USE_WEBSERVER
}
else
{
getDataValue_P(LABEL_HCHC, value);
WSContentSend_PD(HTTP_ENERGY_INDEX_TELEINFO, kTARIF_HC, value);
getDataValue_P(LABEL_HCHP, value);
WSContentSend_PD(HTTP_ENERGY_INDEX_TELEINFO, kTARIF_HP, value);
if (tarif) {
WSContentSend_PD(HTTP_ENERGY_TARIF_TELEINFO, kTtarifNames[tarif-1]);
}
#endif // USE_WEBSERVER
}
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xnrg15(uint8_t function)
{
switch (function)
{
case FUNC_LOOP:
if (TInfoSerial) { TInfoLoop(); }
break;
case FUNC_EVERY_250_MSECOND:
if (uptime > 4) { TInfoEvery250ms(); }
break;
case FUNC_JSON_APPEND:
TInfoShow(1);
break;
#ifdef USE_WEBSERVER
case FUNC_WEB_SENSOR:
TInfoShow(0);
break;
#endif // USE_WEBSERVER
case FUNC_INIT:
TInfoInit();
break;
case FUNC_PRE_INIT:
TInfoDrvInit();
break;
}
return false;
}
#endif // USE_TELEINFO
#endif // USE_ENERGY_SENSOR