User Tools

Site Tools


electronique:teleinfo:accueil

Téléinfo ERDF

http://frank.villaro-dixon.eu/article-76-t%C3%A9l%C3%A9information-edf-enercoop-compteur.html

// teleinfoserial.c
// Lecture données Téléinfo par le port série du PC ou Wrt54gl et enregistre les données dans fichier csv.
// Vérification checksum et boucle de 3 essais si erreurs.
// Par domos78 at free point fr (Dan)
// Modifié/Testé par Ludovic Gomez pour compteur monophasé
// ReModifié/Testé par frank @t villaro-dixon p0int eu (Frank) pour compteur monophasé sans heures creuses/pleines -> tarif "bleu ciel"
 
 
/*
Paramètres à adapter:
- Port série à modifier en conséquence avec constante:
SERIALPORT (par exemple /dev/ttyS0 pour le PC, /dev/tts/0 pour le Wrt54gl)
 
Compilation PC:
- gcc -Wall teleinfoserial.c -o teleinfoserial
 
Compilation wrt54gl:
 Voir exemple compilation avec SDK (OpenWrt-SDK-Linux).
 
/*
 
Trame téléinfo. compteur monophasé sans HC HP:
  ADCO 02980162xxxx C
OPTARIF BASE 0
ISOUSC 30 9
BASE 050723852 +
PTEC TH.. $
IINST 002 Y
IMAX 029 J
TDETAT 000000 B$
 
 
 
*/
//-----------------------------------------------------------------------------
#include <stdlib.h>
#include <stdio.h>
//#include <unistd.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <syslog.h>
#include <termios.h>
#include <sys/fcntl.h>
#include <sys/types.h>
 
// Define port serie
#define VERSION "20090419"
#define BAUDRATE B1200
#define SERIALPORT "/dev/ttyS1"
 
// Fichier local au Wrt4gl + fichier trame pour debug.
#define DATACSV "/tmp/teleinfo.csv"
#define TRAMELOG "/tmp/teleinfotrame."
 
//-----------------------------------------------------------------------------
 
// Déclaration pour le port série.
int             fdserial;
struct termios  termiosteleinfo;
char	CHARACTERSIZE[] = "7E1";	// Format caractère série.
 
 
// Déclaration pour les données.
char ch[2];
char car_prec;
char message[512];
char datateleinfo[512];
char etiquetteBASE[18];
int 	res;
 
// Déclaration pour la date.
time_t 		td;
struct 	tm 	*dc;
char		sdate[12];
char		sheure[10];
char		timestamp[11];
 
int messageLen;
int i;
int founded;
int deadLimit;
int continueGetting;
 
//Trucs à l'étiquette BASE
char etiquette[5];
char valeurEtiquette[10];
char checksumEtiquette[1];
int checksumGood;
 
 
 
/*------------------------------------------------------------------------------*/
/* Initialisation du port rs232	                                                */
/*------------------------------------------------------------------------------*/
int initserie(void)
// Mode Non-Canonical Input Processing, Attend 1 caractère ou time-out(avec VMIN et VTIME).
{
	int device;
 
        // Ouverture de la liaison serie
        if ( (device=open(SERIALPORT, O_RDWR | O_NOCTTY)) == -1 )
	{
                syslog(LOG_ERR, "Erreur ouverture du port serie %s !", SERIALPORT);
                exit(EXIT_FAILURE);
        }
 
        tcgetattr(device,&termiosteleinfo);				// Lecture des parametres courants.
 
	cfsetispeed(&termiosteleinfo, BAUDRATE);			    // Configure le débit en entrée/sortie.
	cfsetospeed(&termiosteleinfo, BAUDRATE);
 
	termiosteleinfo.c_cflag |= (CLOCAL | CREAD);			// Active réception et mode local.
 
	if ( strstr(CHARACTERSIZE, "8N1") != NULL )
	{
		termiosteleinfo.c_cflag &= ~PARENB;			    // Active 8 bits de donnees sans parite.
		termiosteleinfo.c_cflag &= ~CSTOPB;
		termiosteleinfo.c_cflag &= ~CSIZE;
		termiosteleinfo.c_cflag |= CS8;
	}
	else
	if ( strstr(CHARACTERSIZE, "7E1") != NULL )
	{
		termiosteleinfo.c_cflag |= PARENB ;			    // Active 7 bits de donnees avec parite pair.
		termiosteleinfo.c_cflag &= ~PARODD;
		termiosteleinfo.c_cflag &= ~CSTOPB;
		termiosteleinfo.c_cflag &= ~CSIZE;
		termiosteleinfo.c_cflag |= CS7;
 
		termiosteleinfo.c_iflag |= (INPCK | ISTRIP);		// Mode de control de parité.
	}
 
	termiosteleinfo.c_cflag &= ~CRTSCTS;				    // Désactive control de flux matériel.
 
	termiosteleinfo.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);	// Mode non-canonique (mode raw) sans echo.
 
	termiosteleinfo.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL);	// Désactive control de flux logiciel, conversion 0xOD en 0x0A.
 
	termiosteleinfo.c_oflag &= ~OPOST;				        // Pas de mode de sortie particulier (mode raw).
 
	termiosteleinfo.c_cc[VTIME] = 80;  			    	// time-out à ~8s.
	termiosteleinfo.c_cc[VMIN]  = 0;   			    	// 1 car. attendu.
 
	tcflush(device, TCIFLUSH);					            // Efface les données reçues mais non lues.
        tcsetattr(device,TCSANOW,&termiosteleinfo);		// Sauvegarde des nouveaux parametres
	return device;
}
 
/*------------------------------------------------------------------------------*/
/* Teste le checksum d'un message (Return 1 si le checkum est ok)				*/
/*------------------------------------------------------------------------------*/
int checksum_ok(char *etiquette, char *valeur, char checksum)
{
    //printf("%s %s %c", etiquette, valeur, checksum);
	unsigned char sum = 32;		// Somme des codes ASCII du message + un espace
	int i;
 
	for (i=0; i < strlen(etiquette); i++) sum = sum + etiquette[i];
	for (i=0; i < strlen(valeur); i++) sum = sum + valeur[i];
	sum = (sum & 63) + 32;
	if ( sum == checksum) return 1;	// Return 1 si checkum ok.
	#ifdef DEBUG
		syslog(LOG_INFO, "Checksum lu:%02x   calculé:%02x", checksum, sum);
	#endif
	return 0;
}
 
/*------------------------------------------------------------------------------*/
/* Ecrit les données teleinfo dans un fichier DATACSV				            */
/*------------------------------------------------------------------------------*/
void writecsvteleinfo(char data[])
{
        /* Ouverture du fichier csv */
        FILE *datateleinfo;
        if ((datateleinfo = fopen(DATACSV, "a")) == NULL)
        {
		syslog(LOG_ERR, "Erreur ouverture fichier teleinfo %s !", DATACSV);
                exit(EXIT_FAILURE);
        }
        fprintf(datateleinfo, "%s\n", data);
        fclose(datateleinfo);
}
 
 
/*------------------------------------------------------------------------------*/
/* Main							                                    			*/
/*------------------------------------------------------------------------------*/
int main(int argc, char *argv[])
{
 
 openlog("teleinfoserial", LOG_PID, LOG_USER);
 fdserial = initserie();
 
/*------------------------------------------------------------------------------*/
/* On va prendre les données téléinfo (avec un deadlimit de 3 itérations).		*/
/*------------------------------------------------------------------------------*/
deadLimit = 0;
continueGetting = 1;
 do
 {
	tcflush(fdserial, TCIFLUSH);		// Efface les données non lues en entrée.
	message[0]='\0';
//	/memset(valeurs, 0x00, sizeof(valeurs));
//	erreur_checksum = 0;
 
	do //On lit les données téléinfo
	{
		car_prec = ch[0];
		res = read(fdserial, ch, 1);
 
		if (!res) //On recoit quedal
		{
			syslog(LOG_ERR, "Erreur pas de réception fin données Téléinfo !\n");
			close(fdserial);
			exit(EXIT_FAILURE);
		}
	 } while ( !(ch[0] == 0x02 && car_prec == 0x03) );	// On attend un début de trame téléinfo ( 0x02 puis 0x03 ).
 
	do  // On lit la trame téléinfo
	{
		res = read(fdserial, ch, 1);
 
		if (!res) //On recoit quedal
		{
			syslog(LOG_ERR, "Erreur pas de réception fin données Téléinfo !\n");
			close(fdserial);
			exit(EXIT_FAILURE);
		}
		ch[1] ='\0';
		strcat(message, ch);
	} while (ch[0] != 0x03); // Jusqu'a qu'elle se termine (0x03)
 
 
    messageLen = strlen(message);
	i = 0;
	founded = 0;
 
	while(i < messageLen) //On va chercher la balise "BASE", càd celle qui contient la conso actu.
	{
	    if(message[i] == 'B' && message[i+1] == 'A' && message[i+2] == 'S' && message[i+3] == 'E')
	    {
	        if(isdigit(message[i+6])) //Pour ne pas confondre "OPTARIF BASE" et "BASE xxxxxxxxx"
	        {
                //printf("Found at i=%ld!", i);
                founded = i;
	        }
        }
		else
        {
            //printf("i: %ld - %c%c%c%c%c%c%c\n", i, message[i], message[i+1], message[i+2], message[i+3],message[i+4], message[i+5], message[i+6], message[1+7]);
        }
        i++;
    } //On a trouvé la balise base.
 
    i = 0;
	while(i < 16) //On la met dans une var pour pouvoir l'exploiter.
	{
        //printf("%c", message[founded+i]);
		etiquetteBASE[i] = message[founded+i];
		i++;
    }
	etiquetteBASE[i] = '\0'; //On close le fin du tableau
	printf("\n%s\n", etiquetteBASE); //Et on l'affiche
 
    //On va tester le chksum de l'étiquette BASE;
 
    sscanf(etiquetteBASE, "%s %s %s", etiquette, valeurEtiquette, checksumEtiquette);
 
    if(checksum_ok(etiquette, valeurEtiquette, checksumEtiquette[0]))
    {
        printf("Good, checksum is OK :) !\n");
        continueGetting = 0;
        checksumGood = 1;
    }
    else
    {
        printf("Checksum is Bad :(.");
        if(deadLimit > 3)
        {
            printf("Aborting - 3 iterations and nothing. Check reciver.\n");
            continueGetting = 0;
            checksumGood = 0;
        }
        else
        {
            printf("New iteration.\n");
        }
    }
 
 
    deadLimit++;
 
 
 } while(continueGetting == 1);
 
 if(checksumGood == 0)
 {
    printf("Checksum was bad after 3 tries, exiting.\n");
    close(fdserial);
    closelog();
    exit(EXIT_FAILURE);
 }
 else //Ben, on a qu'à writer cette donnée dans un fichier et on quitte. Cool
 {
    time(&td);                                     //Lit date/heure système.
	dc = localtime(&td);
	strftime(sdate,sizeof sdate,"%d-%m-%Y",dc);
	strftime(sheure,sizeof sdate,"%H:%M:%S",dc);
	strftime(timestamp,sizeof timestamp,"%s",dc);
    sprintf(datateleinfo,"'%s','%s','%s','%s'", timestamp, sdate, sheure, valeurEtiquette);
    writecsvteleinfo(datateleinfo);
    printf("CSV file saved. Work done\n");
    close(fdserial);
    closelog();
    return EXIT_SUCCESS;
    exit(EXIT_SUCCESS);
 }
}

Discussion

Enter your comment. Wiki syntax is allowed:
RGCKT
 
electronique/teleinfo/accueil.txt · Last modified: 2012/06/03 19:20 by frank