Création
This commit is contained in:
parent
2034494df9
commit
d854f8c155
|
@ -0,0 +1,657 @@
|
||||||
|
// **********************************************************************************
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// **********************************************************************************
|
||||||
|
|
||||||
|
#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 : valueAdd
|
||||||
|
Purpose : Add element to the Linked List of values
|
||||||
|
Input : Pointer to the label name
|
||||||
|
pointer to the value
|
||||||
|
checksum value
|
||||||
|
state of the label (filled by function)
|
||||||
|
Output : pointer to the new node (or founded one)
|
||||||
|
state of the label changed by the function
|
||||||
|
====================================================================== */
|
||||||
|
ValueList * TInfo::valueAdd(char * name, char * value, uint8_t checksum, uint8_t * valuestate)
|
||||||
|
{
|
||||||
|
// Get our linked list
|
||||||
|
ValueList * me = &_valueslist;
|
||||||
|
|
||||||
|
uint8_t lgname = strlen(name);
|
||||||
|
uint8_t lgvalue = strlen(value);
|
||||||
|
|
||||||
|
// clear flags
|
||||||
|
*valuestate = TINFO_FLAGS_NONE ;
|
||||||
|
|
||||||
|
// Got one and all seems good ?
|
||||||
|
if (me && lgname && lgvalue && checksum) {
|
||||||
|
// Create pointer on the new node
|
||||||
|
ValueList *newNode = NULL;
|
||||||
|
ValueList *parNode = NULL ;
|
||||||
|
|
||||||
|
// By default we done nothing
|
||||||
|
*valuestate = TINFO_FLAGS_NOTHING;
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
me->flags = TINFO_FLAGS_EXIST;
|
||||||
|
*valuestate = me->flags;
|
||||||
|
return ( me );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// We changed the value
|
||||||
|
me->flags = TINFO_FLAGS_UPDATED;
|
||||||
|
*valuestate = me->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 parent node next following node instead of me
|
||||||
|
parNode->next = me->next;
|
||||||
|
|
||||||
|
// Return to parent (that will now point on next node and not us)
|
||||||
|
me = parNode;
|
||||||
|
|
||||||
|
// free up this node
|
||||||
|
free (me);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// We not changed this node ?
|
||||||
|
if (me->flags != TINFO_FLAGS_UPDATED)
|
||||||
|
// so we added this node !
|
||||||
|
me->flags = TINFO_FLAGS_ADDED;
|
||||||
|
*valuestate = me->flags ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 );
|
||||||
|
|
||||||
|
// return pointer on the new node
|
||||||
|
return (newNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error or Already Exists
|
||||||
|
return ( (ValueList *) NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
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 : "));
|
||||||
|
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
|
||||||
|
Output :
|
||||||
|
Comments:
|
||||||
|
====================================================================== */
|
||||||
|
void TInfo::customLabel( char * plabel, char * pvalue)
|
||||||
|
{
|
||||||
|
// Traitement de l'ADPS demandé
|
||||||
|
if (_fn_ADPS) {
|
||||||
|
// Monophasé
|
||||||
|
if (strcmp(plabel, "ADPS")==0 )
|
||||||
|
_fn_ADPS(0);
|
||||||
|
|
||||||
|
// triphasé c'est ADIR + Num Phase
|
||||||
|
if (plabel[0]=='A' && plabel[1]=='D' && plabel[2]=='I' && plabel[3]=='R' )
|
||||||
|
if (plabel[4]>='1' && plabel[4]<='3')
|
||||||
|
_fn_ADPS(plabel[4]-'0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
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 value_state;
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Add value to linked lists of values
|
||||||
|
ValueList * me = valueAdd(ptok, pvalue, checksum, &value_state);
|
||||||
|
|
||||||
|
// value correctly added/changed
|
||||||
|
if ( me ) {
|
||||||
|
// something to do with new datas
|
||||||
|
if (value_state & (TINFO_FLAGS_UPDATED | TINFO_FLAGS_ADDED) ) {
|
||||||
|
// this frame will for sure be updated
|
||||||
|
_frame_updated = true;
|
||||||
|
|
||||||
|
// Do we need to advertise user callback
|
||||||
|
if (_fn_data)
|
||||||
|
_fn_data(me, value_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
// **********************************************************************************
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// **********************************************************************************
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
// Local buffer for one line of teleinfo
|
||||||
|
#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 * 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 * valuestate);
|
||||||
|
int labelCount();
|
||||||
|
unsigned char calcChecksum(char *etiquette, char *valeur) ;
|
||||||
|
void customLabel( char * plabel, char * pvalue) ;
|
||||||
|
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
|
|
@ -5,10 +5,10 @@ This is a generic Teleinfo French Meter Measure Library, it can be used on Ardui
|
||||||
|
|
||||||
You can see Teleinformation official french datasheet [there][1]
|
You can see Teleinformation official french datasheet [there][1]
|
||||||
|
|
||||||
Since this is really dedicted to French energy measuring system, I will continue in French
|
Since this is really dedicated to French energy measuring system, I will continue in French
|
||||||
|
|
||||||
###Installation
|
###Installation
|
||||||
Copier le contenu de ce dossier (fichier 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.
|
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/>
|
<br/>
|
||||||
Pour trouver votre dossier de sketchbook, dans l'environnement IDE, allez dans File>Preferences.
|
Pour trouver votre dossier de sketchbook, dans l'environnement IDE, allez dans File>Preferences.
|
||||||
<br/>
|
<br/>
|
||||||
|
|
|
@ -0,0 +1,219 @@
|
||||||
|
// **********************************************************************************
|
||||||
|
// Arduino Teleinfo sample, display information on teleinfo values received + blink
|
||||||
|
// **********************************************************************************
|
||||||
|
// 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 detailled explanation of this library see dedicated article
|
||||||
|
// https://hallard.me/libteleinfo/
|
||||||
|
//
|
||||||
|
// For any explanation about teleinfo or use, see my blog
|
||||||
|
// https://hallard.me/category/tinfo
|
||||||
|
//
|
||||||
|
// connect Teleinfo RXD pin To Arduin D3
|
||||||
|
// see schematic here https://hallard.me/demystifier-la-teleinfo/
|
||||||
|
// and dedicated article here
|
||||||
|
//
|
||||||
|
// Written by Charles-Henri Hallard (https://hallard.me)
|
||||||
|
//
|
||||||
|
// History : V1.00 2015-06-14 - First release
|
||||||
|
//
|
||||||
|
// All text above must be included in any redistribution.
|
||||||
|
//
|
||||||
|
// **********************************************************************************
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
#include <LibTeleinfo.h>
|
||||||
|
|
||||||
|
// Arduino on board LED
|
||||||
|
// I use moteino, so it's D9
|
||||||
|
// classic Arduino is D13
|
||||||
|
#define LEDPIN 9
|
||||||
|
//#define LEDPIN 13
|
||||||
|
|
||||||
|
SoftwareSerial Serial1(3,4); // Teleinfo Serial
|
||||||
|
TInfo tinfo; // Teleinfo object
|
||||||
|
|
||||||
|
// Pour clignotement LED asynchrone
|
||||||
|
unsigned long blinkLed = 0;
|
||||||
|
uint8_t blinkDelay= 0;
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: printUptime
|
||||||
|
Purpose : print pseudo uptime value
|
||||||
|
Input : -
|
||||||
|
Output : -
|
||||||
|
Comments: compteur de secondes basique sans controle de dépassement
|
||||||
|
En plus SoftwareSerial rend le compteur de millis() totalement
|
||||||
|
A la rue, donc la precision de ce compteur de seconde n'est
|
||||||
|
pas fiable du tout, dont acte !!!
|
||||||
|
====================================================================== */
|
||||||
|
void printUptime(void)
|
||||||
|
{
|
||||||
|
Serial.print(millis()/1000);
|
||||||
|
Serial.print(F("s\t"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
printUptime();
|
||||||
|
|
||||||
|
// Monophasé
|
||||||
|
if (phase == 0 ) {
|
||||||
|
Serial.println(F("ADPS"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Serial.print(F("ADPS PHASE #"));
|
||||||
|
Serial.println('0' + 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(ValueList * me, uint8_t flags)
|
||||||
|
{
|
||||||
|
// Show our not accurate second counter
|
||||||
|
printUptime();
|
||||||
|
|
||||||
|
if (flags & TINFO_FLAGS_ADDED)
|
||||||
|
Serial.print(F("NEW -> "));
|
||||||
|
|
||||||
|
if (flags & TINFO_FLAGS_UPDATED)
|
||||||
|
Serial.print(F("MAJ -> "));
|
||||||
|
|
||||||
|
// Display values
|
||||||
|
Serial.print(me->name);
|
||||||
|
Serial.print("=");
|
||||||
|
Serial.println(me->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: NewFrame
|
||||||
|
Purpose : callback when we received a complete teleinfo frame
|
||||||
|
Input : linked list pointer on the concerned data
|
||||||
|
Output : -
|
||||||
|
Comments: -
|
||||||
|
====================================================================== */
|
||||||
|
void NewFrame(ValueList * me)
|
||||||
|
{
|
||||||
|
// Start short led blink
|
||||||
|
digitalWrite(LEDPIN, HIGH);
|
||||||
|
blinkLed = millis();
|
||||||
|
blinkDelay = 50; // 50ms
|
||||||
|
|
||||||
|
// Show our not accurate second counter
|
||||||
|
printUptime();
|
||||||
|
Serial.println(F("FRAME -> SAME AS PREVIOUS"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: NewFrame
|
||||||
|
Purpose : callback when we received a complete teleinfo frame
|
||||||
|
Input : linked list pointer on the concerned data
|
||||||
|
Output : -
|
||||||
|
Comments: it's called only if one data in the frame is different than
|
||||||
|
the previous frame
|
||||||
|
====================================================================== */
|
||||||
|
void UpdatedFrame(ValueList * me)
|
||||||
|
{
|
||||||
|
// Start long led blink
|
||||||
|
digitalWrite(LEDPIN, HIGH);
|
||||||
|
blinkLed = millis();
|
||||||
|
blinkDelay = 100; // 100ms
|
||||||
|
|
||||||
|
// Show our not accurate second counter
|
||||||
|
printUptime();
|
||||||
|
Serial.println(F("FRAME -> UPDATED"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: setup
|
||||||
|
Purpose : Setup I/O and other one time startup stuff
|
||||||
|
Input : -
|
||||||
|
Output : -
|
||||||
|
Comments: -
|
||||||
|
====================================================================== */
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
// Arduino LED
|
||||||
|
pinMode(LEDPIN, OUTPUT);
|
||||||
|
|
||||||
|
digitalWrite(LEDPIN, LOW);
|
||||||
|
|
||||||
|
// Serial, pour le debug
|
||||||
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
Serial.println(F("========================================"));
|
||||||
|
Serial.println(F(__FILE__));
|
||||||
|
Serial.println(F(__DATE__ " " __TIME__));
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
// Configure Teleinfo Soft serial
|
||||||
|
// La téléinfo est connectee sur D3
|
||||||
|
// ceci permet d'eviter les conflits avec la
|
||||||
|
// vraie serial lors des uploads
|
||||||
|
Serial1.begin(1200);
|
||||||
|
|
||||||
|
// Init teleinfo
|
||||||
|
tinfo.init();
|
||||||
|
|
||||||
|
// Attacher les callback dont nous avons besoin
|
||||||
|
// pour cette demo, toutes
|
||||||
|
tinfo.attachADPS(ADPSCallback);
|
||||||
|
tinfo.attachData(DataCallback);
|
||||||
|
tinfo.attachNewFrame(NewFrame);
|
||||||
|
tinfo.attachUpdatedFrame(UpdatedFrame);
|
||||||
|
|
||||||
|
// Show our not accurate second counter
|
||||||
|
printUptime();
|
||||||
|
Serial.println(F("Teleinfo started"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: loop
|
||||||
|
Purpose : infinite loop main code
|
||||||
|
Input : -
|
||||||
|
Output : -
|
||||||
|
Comments: -
|
||||||
|
====================================================================== */
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
static char c;
|
||||||
|
|
||||||
|
// On a reçu un caractère ?
|
||||||
|
if ( Serial1.available() )
|
||||||
|
{
|
||||||
|
c = Serial1.read() ;
|
||||||
|
//Serial.print(c & 0x7F);
|
||||||
|
|
||||||
|
// Gerer
|
||||||
|
tinfo.process(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifier si le clignotement LED doit s'arreter
|
||||||
|
if (blinkLed && ((millis()-blinkLed) >= blinkDelay))
|
||||||
|
{
|
||||||
|
digitalWrite(LEDPIN, LOW);
|
||||||
|
blinkLed = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
// **********************************************************************************
|
||||||
|
// Arduino Teleinfo sample, display information on teleinfo values received
|
||||||
|
// **********************************************************************************
|
||||||
|
// 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 detailled explanation of this library see dedicated article
|
||||||
|
// https://hallard.me/libteleinfo/
|
||||||
|
//
|
||||||
|
// For any explanation about teleinfo or use, see my blog
|
||||||
|
// https://hallard.me/category/tinfo
|
||||||
|
//
|
||||||
|
// connect Teleinfo RXD pin To Arduin D3
|
||||||
|
// see schematic here https://hallard.me/demystifier-la-teleinfo/
|
||||||
|
// and dedicated article here
|
||||||
|
//
|
||||||
|
// Written by Charles-Henri Hallard (https://hallard.me)
|
||||||
|
//
|
||||||
|
// History : V1.00 2015-06-14 - First release
|
||||||
|
//
|
||||||
|
// All text above must be included in any redistribution.
|
||||||
|
//
|
||||||
|
// **********************************************************************************
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
#include <LibTeleinfo.h>
|
||||||
|
|
||||||
|
SoftwareSerial Serial1(3,4); // Teleinfo Serial
|
||||||
|
TInfo tinfo; // Teleinfo object
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: printUptime
|
||||||
|
Purpose : print pseudo uptime value
|
||||||
|
Input : -
|
||||||
|
Output : -
|
||||||
|
Comments: compteur de secondes basique sans controle de dépassement
|
||||||
|
En plus SoftwareSerial rend le compteur de millis() totalement
|
||||||
|
A la rue, donc la precision de ce compteur de seconde n'est
|
||||||
|
pas fiable du tout, dont acte !!!
|
||||||
|
====================================================================== */
|
||||||
|
void printUptime(void)
|
||||||
|
{
|
||||||
|
Serial.print(millis()/1000);
|
||||||
|
Serial.print(F("s\t"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
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(ValueList * me, uint8_t flags)
|
||||||
|
{
|
||||||
|
// Show our not accurate second counter
|
||||||
|
printUptime();
|
||||||
|
|
||||||
|
if (flags & TINFO_FLAGS_ADDED)
|
||||||
|
Serial.print(F("NEW -> "));
|
||||||
|
|
||||||
|
if (flags & TINFO_FLAGS_UPDATED)
|
||||||
|
Serial.print(F("MAJ -> "));
|
||||||
|
|
||||||
|
// Display values
|
||||||
|
Serial.print(me->name);
|
||||||
|
Serial.print("=");
|
||||||
|
Serial.println(me->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: setup
|
||||||
|
Purpose : Setup I/O and other one time startup stuff
|
||||||
|
Input : -
|
||||||
|
Output : -
|
||||||
|
Comments: -
|
||||||
|
====================================================================== */
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
// Serial, pour le debug
|
||||||
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
Serial.println(F("========================================"));
|
||||||
|
Serial.println(F(__FILE__));
|
||||||
|
Serial.println(F(__DATE__ " " __TIME__));
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
// Configure Teleinfo Soft serial
|
||||||
|
// La téléinfo est connectee sur D3
|
||||||
|
// ceci permet d'eviter les conflits avec la
|
||||||
|
// vraie serial lors des uploads
|
||||||
|
Serial1.begin(1200);
|
||||||
|
|
||||||
|
// Init teleinfo
|
||||||
|
tinfo.init();
|
||||||
|
|
||||||
|
// Attacher les callback dont nous avons besoin
|
||||||
|
// pour cette demo, ici attach data
|
||||||
|
tinfo.attachData(DataCallback);
|
||||||
|
|
||||||
|
printUptime();
|
||||||
|
Serial.println(F("Teleinfo started"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: loop
|
||||||
|
Purpose : infinite loop main code
|
||||||
|
Input : -
|
||||||
|
Output : -
|
||||||
|
Comments: -
|
||||||
|
====================================================================== */
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
// Teleinformation processing
|
||||||
|
if ( Serial1.available() ) {
|
||||||
|
tinfo.process(Serial1.read());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,291 @@
|
||||||
|
// **********************************************************************************
|
||||||
|
// Arduino Teleinfo sample, return JSON data of modified teleinfo values received
|
||||||
|
// **********************************************************************************
|
||||||
|
// 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 detailled explanation of this library see dedicated article
|
||||||
|
// https://hallard.me/libteleinfo/
|
||||||
|
//
|
||||||
|
// For any explanation about teleinfo or use, see my blog
|
||||||
|
// https://hallard.me/category/tinfo
|
||||||
|
//
|
||||||
|
// connect Teleinfo RXD pin To Arduin D3
|
||||||
|
// see schematic here https://hallard.me/demystifier-la-teleinfo/
|
||||||
|
// and dedicated article here
|
||||||
|
//
|
||||||
|
// Written by Charles-Henri Hallard (https://hallard.me)
|
||||||
|
//
|
||||||
|
// History : V1.00 2015-06-14 - First release
|
||||||
|
//
|
||||||
|
// All text above must be included in any redistribution.
|
||||||
|
//
|
||||||
|
// **********************************************************************************
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
#include <LibTeleinfo.h>
|
||||||
|
|
||||||
|
// Arduino on board LED
|
||||||
|
// I use moteino, so it's D9
|
||||||
|
// classic Arduino is D13
|
||||||
|
#define LEDPIN 9
|
||||||
|
//#define LEDPIN 13
|
||||||
|
|
||||||
|
// Teleinfo Serial (on D3)
|
||||||
|
SoftwareSerial Serial1(3,4);
|
||||||
|
|
||||||
|
TInfo tinfo; // Teleinfo object
|
||||||
|
|
||||||
|
// Pour clignotement LED asynchrone
|
||||||
|
unsigned long blinkLed = 0;
|
||||||
|
uint8_t blinkDelay= 0;
|
||||||
|
|
||||||
|
// Uptime timer
|
||||||
|
volatile boolean tick1sec=0;// one for interrupt, don't mess with
|
||||||
|
unsigned long uptime=0; // save value we can use in sketch even if we're interrupted
|
||||||
|
|
||||||
|
// Used to indicate if we need to send all date or just modified ones
|
||||||
|
boolean fulldata = true;
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// Envoyer JSON { "ADPS"; n}
|
||||||
|
// n = numero de la phase 1 à 3
|
||||||
|
if (phase == 0)
|
||||||
|
phase = 1;
|
||||||
|
Serial.print(F("{\"ADPS\":"));
|
||||||
|
Serial.print('0' + phase);
|
||||||
|
Serial.println(F("}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: NewFrame
|
||||||
|
Purpose : callback when we received a complete teleinfo frame
|
||||||
|
Input : linked list pointer on the concerned data
|
||||||
|
Output : -
|
||||||
|
Comments: -
|
||||||
|
====================================================================== */
|
||||||
|
void NewFrame(ValueList * me)
|
||||||
|
{
|
||||||
|
// Start short led blink
|
||||||
|
digitalWrite(LEDPIN, HIGH);
|
||||||
|
blinkLed = millis();
|
||||||
|
blinkDelay = 50; // 50ms
|
||||||
|
|
||||||
|
// Envoyer les valeurs uniquement si demandé
|
||||||
|
if (fulldata)
|
||||||
|
sendJSON(me, true);
|
||||||
|
|
||||||
|
fulldata = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: UpdatedFrame
|
||||||
|
Purpose : callback when we received a complete teleinfo frame
|
||||||
|
Input : linked list pointer on the concerned data
|
||||||
|
Output : -
|
||||||
|
Comments: it's called only if one data in the frame is different than
|
||||||
|
the previous frame
|
||||||
|
====================================================================== */
|
||||||
|
void UpdatedFrame(ValueList * me)
|
||||||
|
{
|
||||||
|
// Start long led blink
|
||||||
|
digitalWrite(LEDPIN, HIGH);
|
||||||
|
blinkLed = millis();
|
||||||
|
blinkDelay = 50; // 50ms
|
||||||
|
|
||||||
|
// Envoyer les valeurs
|
||||||
|
sendJSON(me, fulldata);
|
||||||
|
fulldata = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: sendJSON
|
||||||
|
Purpose : dump teleinfo values on serial
|
||||||
|
Input : linked list pointer on the concerned data
|
||||||
|
true to dump all values, false for only modified ones
|
||||||
|
Output : -
|
||||||
|
Comments: -
|
||||||
|
====================================================================== */
|
||||||
|
void sendJSON(ValueList * me, boolean all)
|
||||||
|
{
|
||||||
|
bool firstdata = true;
|
||||||
|
|
||||||
|
// Got at least one ?
|
||||||
|
if (me) {
|
||||||
|
// Json start
|
||||||
|
Serial.print(F("{"));
|
||||||
|
|
||||||
|
if (all) {
|
||||||
|
Serial.print(F("\"_UPTIME\":"));
|
||||||
|
Serial.print(uptime, DEC);
|
||||||
|
firstdata = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop thru the node
|
||||||
|
while (me->next) {
|
||||||
|
// go to next node
|
||||||
|
me = me->next;
|
||||||
|
|
||||||
|
// uniquement sur les nouvelles valeurs ou celles modifiées
|
||||||
|
// sauf si explicitement demandé toutes
|
||||||
|
if ( all || ( me->flags & (TINFO_FLAGS_UPDATED | TINFO_FLAGS_ADDED) ) )
|
||||||
|
{
|
||||||
|
// First elemement, no comma
|
||||||
|
if (firstdata)
|
||||||
|
firstdata = false;
|
||||||
|
else
|
||||||
|
Serial.print(F(", ")) ;
|
||||||
|
|
||||||
|
Serial.print(F("\"")) ;
|
||||||
|
Serial.print(me->name) ;
|
||||||
|
Serial.print(F("\":")) ;
|
||||||
|
|
||||||
|
// we have at least something ?
|
||||||
|
if (me->value && strlen(me->value))
|
||||||
|
{
|
||||||
|
boolean isNumber = true;
|
||||||
|
uint8_t c;
|
||||||
|
char * p = me->value;
|
||||||
|
|
||||||
|
// check if value is number
|
||||||
|
while (*p && isNumber) {
|
||||||
|
if ( *p < '0' || *p > '9' )
|
||||||
|
isNumber = false;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this will add "" on not number values
|
||||||
|
if (!isNumber) {
|
||||||
|
Serial.print(F("\"")) ;
|
||||||
|
Serial.print(me->value) ;
|
||||||
|
Serial.print(F("\"")) ;
|
||||||
|
}
|
||||||
|
// this will remove leading zero on numbers
|
||||||
|
else
|
||||||
|
Serial.print(atol(me->value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Json end
|
||||||
|
Serial.println(F("}")) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: TIMER VECTOR
|
||||||
|
Purpose : Interrupt that gets called once a second
|
||||||
|
Input : -
|
||||||
|
Output : -
|
||||||
|
Comments: -
|
||||||
|
====================================================================== */
|
||||||
|
ISR(TIMER1_COMPA_vect)
|
||||||
|
{
|
||||||
|
// got our second ticker
|
||||||
|
tick1sec = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: setup
|
||||||
|
Purpose : Setup I/O and other one time startup stuff
|
||||||
|
Input : -
|
||||||
|
Output : -
|
||||||
|
Comments: -
|
||||||
|
====================================================================== */
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
// disable global interrupts
|
||||||
|
cli();
|
||||||
|
|
||||||
|
// set timer1 to fire event every second
|
||||||
|
TCCR1A = 0; // set entire TCCR1A register to 0
|
||||||
|
TCCR1B = 0; // same for TCCR1B
|
||||||
|
|
||||||
|
OCR1A = 15624; // set compare match register to desired timer count. 16 MHz with 1024 prescaler = 15624 counts/s
|
||||||
|
TCCR1B |= (1 << WGM12); // turn on CTC mode. clear timer on compare match
|
||||||
|
TCCR1B |= (1 << CS10); // Set CS10 and CS12 bits for 1024 prescaler
|
||||||
|
TCCR1B |= (1 << CS12);
|
||||||
|
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
|
||||||
|
|
||||||
|
// enable global interrupts
|
||||||
|
sei();
|
||||||
|
|
||||||
|
// Arduino LED
|
||||||
|
pinMode(LEDPIN, OUTPUT);
|
||||||
|
digitalWrite(LEDPIN, LOW);
|
||||||
|
|
||||||
|
// Serial, pour le debug
|
||||||
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
// Configure Teleinfo Soft serial
|
||||||
|
// La téléinfo est connectee sur D3
|
||||||
|
// ceci permet d'eviter les conflits avec la
|
||||||
|
// vraie serial lors des uploads
|
||||||
|
Serial1.begin(1200);
|
||||||
|
|
||||||
|
// Init teleinfo
|
||||||
|
tinfo.init();
|
||||||
|
|
||||||
|
// Attacher les callback dont nous avons besoin
|
||||||
|
// pour cette demo, ADPS et TRAME modifiée
|
||||||
|
tinfo.attachADPS(ADPSCallback);
|
||||||
|
tinfo.attachUpdatedFrame(UpdatedFrame);
|
||||||
|
tinfo.attachNewFrame(NewFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: loop
|
||||||
|
Purpose : infinite loop main code
|
||||||
|
Input : -
|
||||||
|
Output : -
|
||||||
|
Comments: -
|
||||||
|
====================================================================== */
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
static char c;
|
||||||
|
|
||||||
|
// Avons nous recu un ticker de seconde?
|
||||||
|
if (tick1sec)
|
||||||
|
{
|
||||||
|
tick1sec = false;
|
||||||
|
uptime++;
|
||||||
|
|
||||||
|
// Forcer un envoi de trame complète toutes les minutes
|
||||||
|
// fulldata sera remis à 0 après l'envoi
|
||||||
|
if (uptime % 60 == 0)
|
||||||
|
fulldata = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// On a reçu un caractère ?
|
||||||
|
if ( Serial1.available() ) {
|
||||||
|
// Le lire
|
||||||
|
c = Serial1.read();
|
||||||
|
|
||||||
|
// Gerer
|
||||||
|
tinfo.process(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifier si le clignotement LED doit s'arreter
|
||||||
|
if (blinkLed && ((millis()-blinkLed) >= blinkDelay))
|
||||||
|
{
|
||||||
|
digitalWrite(LEDPIN, LOW);
|
||||||
|
blinkLed = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
Teleinfo Universal Library
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Programme d'exemple de la librairie universelle pour la téléinformation pour Raspberry Pi
|
||||||
|
|
||||||
|
##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]
|
||||||
|
|
||||||
|
###Installation
|
||||||
|
Se connecter en ssh sur votre Pi, il doit y avoir les environnements de développement, sinon faites un
|
||||||
|
`apt-get install build-essential git-core`
|
||||||
|
|
||||||
|
Ensuite :
|
||||||
|
```
|
||||||
|
git clone https://github.com/hallard/LibTeleinfo
|
||||||
|
cd LibTeleinfo/examples/Raspberry_JSON/
|
||||||
|
make
|
||||||
|
./raspjson
|
||||||
|
```
|
||||||
|
|
||||||
|
Et voilà ce que ça donne avec un dongle MicroTeleinfo
|
||||||
|
|
||||||
|
```
|
||||||
|
root@pi01(rw):~/LibTeleinfo/examples/Raspberry_JSON# ./raspjson -d /dev/ttyUSB0
|
||||||
|
{"_UPTIME":30788, "ADCO":2147483647, "OPTARIF":"HC..", "ISOUSC":15, "HCHC":247295, "HCHP":0, "PTEC":"HC..", "IINST":1, "IMAX":1, "PAPP":140, "HHPHC":"A", "MOTDETAT":0}
|
||||||
|
{"HCHC":247296}
|
||||||
|
{"PAPP":170}
|
||||||
|
{"HCHC":247297}
|
||||||
|
q{"PAPP":160}
|
||||||
|
{"PAPP":140}
|
||||||
|
{"_UPTIME":30848, "ADCO":2147483647, "OPTARIF":"HC..", "ISOUSC":15, "HCHC":247297, "HCHP":0, "PTEC":"HC..", "IINST":1, "IMAX":1, "PAPP":140, "HHPHC":"A", "MOTDETAT":0}
|
||||||
|
{"PAPP":150}
|
||||||
|
{"PAPP":140}
|
||||||
|
{"PAPP":150}
|
||||||
|
{"PAPP":140}
|
||||||
|
{"PAPP":160}
|
||||||
|
{"PAPP":170}
|
||||||
|
{"PAPP":160}
|
||||||
|
{"HCHC":247298, "PAPP":140}
|
||||||
|
{"PAPP":150}
|
||||||
|
{"PAPP":140}
|
||||||
|
{"PAPP":150}
|
||||||
|
{"PAPP":140}
|
||||||
|
{"PAPP":150}
|
||||||
|
{"PAPP":140}
|
||||||
|
{"PAPP":150}
|
||||||
|
{"PAPP":160}
|
||||||
|
{"PAPP":150}
|
||||||
|
{"PAPP":170}
|
||||||
|
{"PAPP":160}
|
||||||
|
{"PAPP":170}
|
||||||
|
{"PAPP":150}
|
||||||
|
{"HCHC":247299, "PAPP":160}
|
||||||
|
{"PAPP":150}
|
||||||
|
{"_UPTIME":30908, "ADCO":2147483647, "OPTARIF":"HC..", "ISOUSC":15, "HCHC":247299, "HCHP":0, "PTEC":"HC..", "IINST":1, "IMAX":1, "PAPP":160, "HHPHC":"A", "MOTDETAT":0}
|
||||||
|
{"PAPP":140}
|
||||||
|
{"PAPP":150}
|
||||||
|
{"PAPP":140}
|
||||||
|
{"PAPP":150}
|
||||||
|
````
|
||||||
|
|
||||||
|
##Divers
|
||||||
|
Vous pouvez aller voir les nouveautés et autres projets sur [blog][7]
|
||||||
|
|
||||||
|
[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
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
SHELL=/bin/sh
|
||||||
|
|
||||||
|
CFLAGS=-DRASPBERRY_PI
|
||||||
|
|
||||||
|
# raspjson
|
||||||
|
all: raspjson
|
||||||
|
|
||||||
|
# ===== Compile
|
||||||
|
LibTeleinfo.o: ../../LibTeleinfo.cpp
|
||||||
|
$(CC) $(CFLAGS) -c ../../LibTeleinfo.cpp
|
||||||
|
|
||||||
|
raspjson.o: raspjson.cpp
|
||||||
|
$(CC) $(CFLAGS) -c raspjson.cpp
|
||||||
|
|
||||||
|
# ===== Link
|
||||||
|
raspjson: raspjson.o LibTeleinfo.o
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) -o raspjson raspjson.o LibTeleinfo.o
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o raspjson
|
||||||
|
|
|
@ -0,0 +1,614 @@
|
||||||
|
// **********************************************************************************
|
||||||
|
// Raspberry PI LibTeleinfo sample, display JSON data of modified teleinfo values received
|
||||||
|
// **********************************************************************************
|
||||||
|
// 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 detailled explanation of this library see dedicated article
|
||||||
|
// https://hallard.me/libteleinfo/
|
||||||
|
//
|
||||||
|
// For any explanation about teleinfo or use, see my blog
|
||||||
|
// https://hallard.me/category/tinfo
|
||||||
|
//
|
||||||
|
// Written by Charles-Henri Hallard (https://hallard.me)
|
||||||
|
//
|
||||||
|
// History : V1.00 2015-07-14 - First release
|
||||||
|
//
|
||||||
|
// All text above must be included in any redistribution.
|
||||||
|
//
|
||||||
|
// **********************************************************************************
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <sys/sysinfo.h>
|
||||||
|
#include "../../LibTeleinfo.h"
|
||||||
|
|
||||||
|
// ----------------
|
||||||
|
// Constants
|
||||||
|
// ----------------
|
||||||
|
#define true 1
|
||||||
|
#define false 0
|
||||||
|
|
||||||
|
#define PRG_NAME "raspjson"
|
||||||
|
#define TELEINFO_DEVICE ""
|
||||||
|
#define TELEINFO_BUFSIZE 512
|
||||||
|
|
||||||
|
|
||||||
|
// Some enum for serial
|
||||||
|
enum parity_e { P_NONE, P_EVEN, P_ODD };
|
||||||
|
enum flowcntrl_e { FC_NONE, FC_RTSCTS, FC_XONXOFF };
|
||||||
|
enum mode_e { MODE_NONE, MODE_SEND, MODE_RECEIVE, MODE_TEST };
|
||||||
|
enum value_e { VALUE_NOTHING, VALUE_ADDED, VALUE_EXIST, VALUE_CHANGED};
|
||||||
|
|
||||||
|
// Configuration structure
|
||||||
|
static struct
|
||||||
|
{
|
||||||
|
char port[128];
|
||||||
|
int baud;
|
||||||
|
enum flowcntrl_e flow;
|
||||||
|
char flow_str[32];
|
||||||
|
enum parity_e parity;
|
||||||
|
char parity_str[32];
|
||||||
|
int databits;
|
||||||
|
int verbose;
|
||||||
|
// Configuration structure defaults values
|
||||||
|
} opts ;
|
||||||
|
|
||||||
|
|
||||||
|
void sendJSON(ValueList * me, boolean all);
|
||||||
|
|
||||||
|
|
||||||
|
// ======================================================================
|
||||||
|
// Global vars
|
||||||
|
// ======================================================================
|
||||||
|
int g_fd_teleinfo; // teleinfo serial handle
|
||||||
|
struct termios g_oldtermios ; // old serial config
|
||||||
|
int g_exit_pgm; // indicate en of the program
|
||||||
|
struct sysinfo g_info;
|
||||||
|
TInfo tinfo; // Teleinfo object
|
||||||
|
|
||||||
|
// Used to indicate if we need to send all date or just modified ones
|
||||||
|
boolean fulldata = true;
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// Envoyer JSON { "ADPS"; n}
|
||||||
|
// n = numero de la phase 1 à 3
|
||||||
|
if (phase == 0)
|
||||||
|
phase = 1;
|
||||||
|
printf( "{\"ADPS\":%c}\r\n",'0' + phase);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: NewFrame
|
||||||
|
Purpose : callback when we received a complete teleinfo frame
|
||||||
|
Input : linked list pointer on the concerned data
|
||||||
|
Output : -
|
||||||
|
Comments: -
|
||||||
|
====================================================================== */
|
||||||
|
void NewFrame(ValueList * me)
|
||||||
|
{
|
||||||
|
// Envoyer les valeurs uniquement si demandé
|
||||||
|
if (fulldata)
|
||||||
|
sendJSON(me, true);
|
||||||
|
|
||||||
|
fulldata = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: UpdatedFrame
|
||||||
|
Purpose : callback when we received a complete teleinfo frame
|
||||||
|
Input : linked list pointer on the concerned data
|
||||||
|
Output : -
|
||||||
|
Comments: it's called only if one data in the frame is different than
|
||||||
|
the previous frame
|
||||||
|
====================================================================== */
|
||||||
|
void UpdatedFrame(ValueList * me)
|
||||||
|
{
|
||||||
|
// Envoyer les valeurs
|
||||||
|
sendJSON(me, fulldata);
|
||||||
|
fulldata = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: sendJSON
|
||||||
|
Purpose : dump teleinfo values on serial
|
||||||
|
Input : linked list pointer on the concerned data
|
||||||
|
true to dump all values, false for only modified ones
|
||||||
|
Output : -
|
||||||
|
Comments: -
|
||||||
|
====================================================================== */
|
||||||
|
void sendJSON(ValueList * me, boolean all)
|
||||||
|
{
|
||||||
|
bool firstdata = true;
|
||||||
|
|
||||||
|
// Got at least one ?
|
||||||
|
if (me) {
|
||||||
|
// Json start
|
||||||
|
printf("{");
|
||||||
|
|
||||||
|
if (all) {
|
||||||
|
printf("\"_UPTIME\":%ld", g_info.uptime);
|
||||||
|
firstdata = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop thru the node
|
||||||
|
while (me->next) {
|
||||||
|
// go to next node
|
||||||
|
me = me->next;
|
||||||
|
|
||||||
|
// uniquement sur les nouvelles valeurs ou celles modifiées
|
||||||
|
// sauf si explicitement demandé toutes
|
||||||
|
if ( all || ( me->flags & (TINFO_FLAGS_UPDATED | TINFO_FLAGS_ADDED) ) )
|
||||||
|
{
|
||||||
|
// First elemement, no comma
|
||||||
|
if (firstdata)
|
||||||
|
firstdata = false;
|
||||||
|
else
|
||||||
|
printf(", ") ;
|
||||||
|
|
||||||
|
printf("\"%s\":", me->name) ;
|
||||||
|
|
||||||
|
// we have at least something ?
|
||||||
|
if (me->value && strlen(me->value))
|
||||||
|
{
|
||||||
|
boolean isNumber = true;
|
||||||
|
uint8_t c;
|
||||||
|
char * p = me->value;
|
||||||
|
|
||||||
|
// check if value is number
|
||||||
|
while (*p && isNumber) {
|
||||||
|
if ( *p < '0' || *p > '9' )
|
||||||
|
isNumber = false;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this will add "" on not number values
|
||||||
|
if (!isNumber) {
|
||||||
|
printf("\"%s\"", me->value) ;
|
||||||
|
}
|
||||||
|
// this will remove leading zero on numbers
|
||||||
|
else
|
||||||
|
printf("%ld",atol(me->value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Json end
|
||||||
|
printf("}\r\n") ;
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================================================================
|
||||||
|
// some func declaration
|
||||||
|
// ======================================================================
|
||||||
|
void tlf_close_serial(int);
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: log_syslog
|
||||||
|
Purpose : write event to syslog
|
||||||
|
Input : stream to write if needed
|
||||||
|
string to write in printf format
|
||||||
|
printf other arguments
|
||||||
|
Output : -
|
||||||
|
Comments:
|
||||||
|
====================================================================== */
|
||||||
|
void log_syslog( FILE * stream, const char *format, ...)
|
||||||
|
{
|
||||||
|
static char tmpbuff[512]="";
|
||||||
|
va_list args;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
// do a style printf style in ou buffer
|
||||||
|
va_start (args, format);
|
||||||
|
len = vsnprintf (tmpbuff, sizeof(tmpbuff), format, args);
|
||||||
|
tmpbuff[sizeof(tmpbuff) - 1] = '\0';
|
||||||
|
va_end (args);
|
||||||
|
|
||||||
|
// Write to logfile
|
||||||
|
openlog( PRG_NAME, LOG_PID|LOG_CONS, LOG_USER);
|
||||||
|
syslog(LOG_INFO, "%s", tmpbuff);
|
||||||
|
closelog();
|
||||||
|
|
||||||
|
// stream passed ? write also to it
|
||||||
|
if (stream && opts.verbose )
|
||||||
|
{
|
||||||
|
fprintf(stream, "%s", tmpbuff);
|
||||||
|
//fprintf(stream, "\n");
|
||||||
|
fflush(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: clean_exit
|
||||||
|
Purpose : exit program
|
||||||
|
Input : exit code
|
||||||
|
Output : -
|
||||||
|
Comments:
|
||||||
|
====================================================================== */
|
||||||
|
void clean_exit (int exit_code)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
// free up linked list
|
||||||
|
tinfo.listDelete();
|
||||||
|
|
||||||
|
// close serials
|
||||||
|
if (g_fd_teleinfo)
|
||||||
|
{
|
||||||
|
// Restore Old parameters.
|
||||||
|
if ( (r = tcsetattr(g_fd_teleinfo, TCSAFLUSH, &g_oldtermios)) < 0 )
|
||||||
|
log_syslog(stderr, "cannot restore old parameters %s: %s", opts.port, strerror(errno));
|
||||||
|
|
||||||
|
// then close
|
||||||
|
tlf_close_serial(g_fd_teleinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(exit_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: fatal
|
||||||
|
Purpose : exit program due to a fatal error
|
||||||
|
Input : string to write in printf format
|
||||||
|
printf other arguments
|
||||||
|
Output : -
|
||||||
|
Comments:
|
||||||
|
====================================================================== */
|
||||||
|
void fatal (const char *format, ...)
|
||||||
|
{
|
||||||
|
char tmpbuff[512] = "";
|
||||||
|
va_list args;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
len = vsnprintf(tmpbuff, sizeof(tmpbuff), format, args);
|
||||||
|
tmpbuff[sizeof(tmpbuff) - 1] = '\0';
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
// Write to logfile
|
||||||
|
openlog( PRG_NAME, LOG_PID | LOG_CONS, LOG_USER);
|
||||||
|
syslog(LOG_INFO, "%s", tmpbuff);
|
||||||
|
closelog();
|
||||||
|
|
||||||
|
fprintf(stderr,"\r\nFATAL: %s \r\n", tmpbuff );
|
||||||
|
fflush(stderr);
|
||||||
|
|
||||||
|
clean_exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: signal_handler
|
||||||
|
Purpose : Interrupt routine Code for signal
|
||||||
|
Input : signal received
|
||||||
|
Output : -
|
||||||
|
Comments:
|
||||||
|
====================================================================== */
|
||||||
|
void signal_handler (int signum)
|
||||||
|
{
|
||||||
|
// Does we received CTRL-C ?
|
||||||
|
if ( signum==SIGINT )
|
||||||
|
{
|
||||||
|
// Indicate we want to quit
|
||||||
|
g_exit_pgm = true;
|
||||||
|
log_syslog(stdout, "\nReceived SIGINT\n");
|
||||||
|
}
|
||||||
|
else if ( signum==SIGTERM )
|
||||||
|
{
|
||||||
|
// Indicate we want to quit
|
||||||
|
g_exit_pgm = true;
|
||||||
|
log_syslog(stdout, "\nReceived SIGTERM\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: tlf_init_serial
|
||||||
|
Purpose : initialize serial port for receiving teleinfo
|
||||||
|
Input : -
|
||||||
|
Output : Serial Port Handle
|
||||||
|
Comments: -
|
||||||
|
====================================================================== */
|
||||||
|
int tlf_init_serial(void)
|
||||||
|
{
|
||||||
|
int tty_fd, r ;
|
||||||
|
struct termios termios ;
|
||||||
|
|
||||||
|
// Open serial device
|
||||||
|
if ( (tty_fd = open(opts.port, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK)) < 0 )
|
||||||
|
fatal( "tlf_init_serial %s: %s", opts.port, strerror(errno));
|
||||||
|
else
|
||||||
|
log_syslog( stdout, "'%s' opened.\n",opts.port);
|
||||||
|
|
||||||
|
// Set descriptor status flags
|
||||||
|
fcntl (tty_fd, F_SETFL, O_RDWR ) ;
|
||||||
|
|
||||||
|
// Get current parameters for saving
|
||||||
|
if ( (r = tcgetattr(tty_fd, &g_oldtermios)) < 0 )
|
||||||
|
log_syslog(stderr, "cannot get current parameters %s: %s", opts.port, strerror(errno));
|
||||||
|
|
||||||
|
// copy current parameters and change for our own
|
||||||
|
memcpy( &termios, &g_oldtermios, sizeof(termios));
|
||||||
|
|
||||||
|
// raw mode
|
||||||
|
cfmakeraw(&termios);
|
||||||
|
|
||||||
|
// Set serial speed to 1200 bps
|
||||||
|
if (cfsetospeed(&termios, B1200) < 0 || cfsetispeed(&termios, B1200) < 0 )
|
||||||
|
log_syslog(stderr, "cannot set serial speed to 1200 bps: %s", strerror(errno));
|
||||||
|
|
||||||
|
// Parity Even
|
||||||
|
termios.c_cflag &= ~PARODD;
|
||||||
|
termios.c_cflag |= PARENB;
|
||||||
|
|
||||||
|
// 7 databits
|
||||||
|
termios.c_cflag = (termios.c_cflag & ~CSIZE) | CS7;
|
||||||
|
|
||||||
|
// No Flow Control
|
||||||
|
termios.c_cflag &= ~(CRTSCTS);
|
||||||
|
termios.c_iflag &= ~(IXON | IXOFF | IXANY);
|
||||||
|
|
||||||
|
// Local
|
||||||
|
termios.c_cflag |= CLOCAL;
|
||||||
|
|
||||||
|
// No minimal char but 5 sec timeout
|
||||||
|
termios.c_cc [VMIN] = 0 ;
|
||||||
|
termios.c_cc [VTIME] = 50 ;
|
||||||
|
|
||||||
|
// now setup the whole parameters
|
||||||
|
if ( tcsetattr (tty_fd, TCSANOW | TCSAFLUSH, &termios) <0)
|
||||||
|
log_syslog(stderr, "cannot set current parameters %s: %s", opts.port, strerror(errno));
|
||||||
|
|
||||||
|
// Sleep 50ms
|
||||||
|
// trust me don't forget this one, it will remove you some
|
||||||
|
// headache to find why serial is not working
|
||||||
|
usleep(50000);
|
||||||
|
|
||||||
|
return tty_fd ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: tlf_close_serial
|
||||||
|
Purpose : close serial port for receiving teleinfo
|
||||||
|
Input : Serial Port Handle
|
||||||
|
Output : -
|
||||||
|
Comments:
|
||||||
|
====================================================================== */
|
||||||
|
void tlf_close_serial(int device)
|
||||||
|
{
|
||||||
|
if (device)
|
||||||
|
{
|
||||||
|
// flush and restore old settings
|
||||||
|
tcsetattr(device, TCSANOW | TCSAFLUSH, &g_oldtermios);
|
||||||
|
|
||||||
|
close(device) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: usage
|
||||||
|
Purpose : display usage
|
||||||
|
Input : program name
|
||||||
|
Output : -
|
||||||
|
Comments:
|
||||||
|
====================================================================== */
|
||||||
|
int usage( char * name)
|
||||||
|
{
|
||||||
|
printf("%s\n", PRG_NAME);
|
||||||
|
printf("Usage is: %s [options] -d device\n", PRG_NAME);
|
||||||
|
printf("Options are:\n");
|
||||||
|
printf(" --<d>evice dev : open serial device name\n");
|
||||||
|
printf(" --<v>erbose : speak more to user\n");
|
||||||
|
printf(" --<h>elp\n");
|
||||||
|
printf("<?> indicates the equivalent short option.\n");
|
||||||
|
printf("Short options are prefixed by \"-\" instead of by \"--\".\n");
|
||||||
|
printf("Example :\n");
|
||||||
|
printf( "%s -d /dev/ttyAMA0\n\tstart listeming on hardware serial port /dev/ttyAMA0\n\n", PRG_NAME);
|
||||||
|
printf( "%s -d /dev/ttyUSB0\n\tstart listeming on USB microteleinfo dongle\n\n", PRG_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: read_config
|
||||||
|
Purpose : read configuration from config file and/or command line
|
||||||
|
Input : -
|
||||||
|
Output : -
|
||||||
|
Comments: Config is read from config file then from command line params
|
||||||
|
this means that command line parameters always override config
|
||||||
|
file parameters
|
||||||
|
====================================================================== */
|
||||||
|
void read_config(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
static struct option longOptions[] =
|
||||||
|
{
|
||||||
|
{"port", required_argument,0, 'p'},
|
||||||
|
{"verbose", no_argument, 0, 'v'},
|
||||||
|
{"help", no_argument, 0, 'h'},
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
int optionIndex = 0;
|
||||||
|
int c;
|
||||||
|
char str_opt[64];
|
||||||
|
|
||||||
|
char buffer[512];
|
||||||
|
|
||||||
|
char* bufp = NULL;
|
||||||
|
char* opt = NULL;
|
||||||
|
char* optdata = NULL;
|
||||||
|
|
||||||
|
// default values
|
||||||
|
*opts.port = '\0';
|
||||||
|
opts.baud = 1200;
|
||||||
|
opts.flow = FC_NONE;
|
||||||
|
strcpy(opts.flow_str, "none");
|
||||||
|
opts.parity = P_EVEN;
|
||||||
|
strcpy(opts.parity_str, "even");
|
||||||
|
opts.databits = 7;
|
||||||
|
opts.verbose = false;
|
||||||
|
|
||||||
|
|
||||||
|
// default options
|
||||||
|
strcpy( str_opt, "hvd:");
|
||||||
|
|
||||||
|
// We will scan all options given on command line.
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
// no default error messages printed.
|
||||||
|
opterr = 0;
|
||||||
|
|
||||||
|
// Get option
|
||||||
|
c = getopt_long(argc, argv, str_opt, longOptions, &optionIndex);
|
||||||
|
|
||||||
|
// Last one ?
|
||||||
|
if (c < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'v':
|
||||||
|
opts.verbose = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'd':
|
||||||
|
strncpy(opts.port, optarg, sizeof(opts.port) - 1);
|
||||||
|
opts.port[sizeof(opts.port) - 1] = '\0';
|
||||||
|
break;
|
||||||
|
|
||||||
|
// These ones exit direct
|
||||||
|
case 'h':
|
||||||
|
case '?':
|
||||||
|
usage(argv[0]);
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unrecognized option.\n");
|
||||||
|
fprintf(stderr, "Run %s with '--help'.\n", PRG_NAME);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !*opts.port)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "No tty device given\n");
|
||||||
|
fprintf(stderr, "please select at least tty device such as /dev/ttyS0\n");
|
||||||
|
fprintf(stderr, "Run %s with '--help'.\n", PRG_NAME);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.verbose)
|
||||||
|
{
|
||||||
|
printf("%s\n", PRG_NAME);
|
||||||
|
|
||||||
|
printf("-- Serial Stuff -- \n");
|
||||||
|
printf("tty device : %s\n", opts.port);
|
||||||
|
printf("flowcontrol : %s\n", opts.flow_str);
|
||||||
|
printf("baudrate is : %d\n", opts.baud);
|
||||||
|
printf("parity is : %s\n", opts.parity_str);
|
||||||
|
printf("databits are : %d\n", opts.databits);
|
||||||
|
|
||||||
|
printf("-- Other Stuff -- \n");
|
||||||
|
printf("verbose is : %s\n", opts.verbose? "yes" : "no");
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
Function: main
|
||||||
|
Purpose : Main entry Point
|
||||||
|
Input : -
|
||||||
|
Output : -
|
||||||
|
Comments:
|
||||||
|
====================================================================== */
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct sigaction sa;
|
||||||
|
fd_set rdset, wrset;
|
||||||
|
unsigned char c;
|
||||||
|
char rcv_buff[TELEINFO_BUFSIZE];
|
||||||
|
int rcv_idx;
|
||||||
|
char time_str[200];
|
||||||
|
time_t t;
|
||||||
|
struct tm *tmp;
|
||||||
|
int n;
|
||||||
|
struct sysinfo info;
|
||||||
|
|
||||||
|
g_fd_teleinfo = 0;
|
||||||
|
g_exit_pgm = false;
|
||||||
|
|
||||||
|
// get configuration
|
||||||
|
read_config(argc, argv);
|
||||||
|
|
||||||
|
// Set up the structure to specify the exit action.
|
||||||
|
sa.sa_handler = signal_handler;
|
||||||
|
sa.sa_flags = SA_RESTART;
|
||||||
|
sigfillset(&sa.sa_mask);
|
||||||
|
sigaction (SIGTERM, &sa, NULL);
|
||||||
|
sigaction (SIGINT, &sa, NULL);
|
||||||
|
|
||||||
|
// Open serial port
|
||||||
|
g_fd_teleinfo = tlf_init_serial();
|
||||||
|
|
||||||
|
// Init teleinfo
|
||||||
|
tinfo.init();
|
||||||
|
|
||||||
|
// Attacher les callback dont nous avons besoin
|
||||||
|
// pour cette demo, ADPS et TRAME modifiée
|
||||||
|
tinfo.attachADPS(ADPSCallback);
|
||||||
|
tinfo.attachUpdatedFrame(UpdatedFrame);
|
||||||
|
tinfo.attachNewFrame(NewFrame);
|
||||||
|
|
||||||
|
log_syslog(stdout, "Inits succeded, entering Main loop\n");
|
||||||
|
|
||||||
|
// Do while not end
|
||||||
|
while ( ! g_exit_pgm ) {
|
||||||
|
// Read from serial port
|
||||||
|
n = read(g_fd_teleinfo, &c, 1);
|
||||||
|
|
||||||
|
if (n >= 0)
|
||||||
|
tinfo.process(c);
|
||||||
|
|
||||||
|
// Check full frame every 60 sec
|
||||||
|
sysinfo(&info);
|
||||||
|
if (info.uptime >= g_info.uptime + 60) {
|
||||||
|
g_info.uptime = info.uptime;
|
||||||
|
fulldata = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sleep 10ms; let time to others process
|
||||||
|
usleep(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_syslog(stderr, "Program terminated\n");
|
||||||
|
|
||||||
|
clean_exit(EXIT_SUCCESS);
|
||||||
|
|
||||||
|
// avoid compiler warning
|
||||||
|
return (0);
|
||||||
|
}
|
Loading…
Reference in New Issue