Lib Folder Structured to conform Arduino 1.5+ spec
This commit is contained in:
828
src/LibTeleinfo.cpp
Normal file
828
src/LibTeleinfo.cpp
Normal file
@@ -0,0 +1,828 @@
|
||||
// **********************************************************************************
|
||||
// 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
|
||||
//
|
||||
// All text above must be included in any redistribution.
|
||||
//
|
||||
// Edit : Tab size set to 2 but I converted tab to sapces
|
||||
//
|
||||
// **********************************************************************************
|
||||
|
||||
#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 : configure ULPNode I/O ports
|
||||
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: -
|
||||
====================================================================== */
|
||||
uint8_t 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) cheksum);
|
||||
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
|
||||
if (strncmp(me->name, name, lgname) == 0) {
|
||||
// Already got also this value, return US
|
||||
if (strncmp(me->value, value, lgvalue) == 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
|
||||
strncpy(me->value, value , lgvalue );
|
||||
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 = sizeof(ValueList) + lgname + 1 + lgvalue + 1 ;
|
||||
// Create new node with size to store strings
|
||||
if ((newNode = (ValueList *) malloc(size) ) == NULL)
|
||||
return ( (ValueList *) NULL );
|
||||
else
|
||||
// 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) cheksum);
|
||||
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 (strncmp(me->name, name, lgname) == 0) {
|
||||
// this one has a value ?
|
||||
if (me->value) {
|
||||
// copy to dest buffer
|
||||
uint8_t lgvalue = strlen(me->value);
|
||||
strncpy(value, me->value , lgvalue );
|
||||
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 i ;
|
||||
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
|
||||
strncpy( buff, _recv_buff, 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
147
src/LibTeleinfo.h
Normal file
147
src/LibTeleinfo.h
Normal 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
|
||||
//
|
||||
// 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
|
||||
|
||||
#ifdef __arm__
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#define boolean bool
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
// Using ESP8266 ?
|
||||
#ifdef ESP8266
|
||||
//#include "stdlib_noniso.h"
|
||||
#include <ESP8266WiFi.h>
|
||||
#endif
|
||||
|
||||
// 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
|
||||
|
||||
// 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
|
||||
};
|
||||
|
||||
// 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
|
||||
|
||||
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);
|
||||
boolean listDelete();
|
||||
|
||||
private:
|
||||
uint8_t clearBuffer();
|
||||
ValueList * valueAdd (char * name, char * value, uint8_t checksum, uint8_t * flags);
|
||||
boolean valueRemove (char * name);
|
||||
boolean valueRemoveFlagged(uint8_t flags);
|
||||
int labelCount();
|
||||
unsigned char calcChecksum(char *etiquette, char *valeur) ;
|
||||
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
|
||||
Reference in New Issue
Block a user