Teleinfoserial csv.c

De MicElectroLinGenMet.

Origine page Interface téléinfo.



// teleinfoserial_csv.c
// Lecture données Téléinfo par le port série du Wrt54gl (ou PC) et enregistre données dans fichier csv.
// Vérification checksum et boucle de 3 essais si erreurs.
// Par domos78 at free point fr
 
/*
Paramètres à adapter: 
- Port série à modifier en conséquence avec SERIALPORT.
- Nombre de valeurs à relever: NB_VALEURS + tableaux "etiquettes" à modifier selon abonnement (ici triphasé heures creuses).
 
Compilation PC:  
- gcc -Wall teleinfoserial_csv.c -o teleinfoserial_csv
 
Compilation wrt54gl: 
- avec le SDK (OpenWrt-SDK-Linux).
Fonctionne sur OpenWrt whiterussian/0.9 et kamikaze/8.09 (seul le Makefile et nom port série différent)
 
Données Téléinfo. dans fichier /tmp/teleinfo.csv
root@wrt54gl:~# cat teleinfo.csv
'1236157234','2009-03-04','10:00:34','70060936XXXX','HC..','20','011345063','010763741','HP','001','000','001','018','023','022','08780','00250','E','000000','00','','',''
'1236157295','2009-03-04','10:01:35','70060936XXXX','HC..','20','011345078','010763741','HP','005','000','001','018','023','022','08780','01140','E','000000','00','','',''
'1236157354','2009-03-04','10:02:34','70060936XXXX','HC..','20','011345107','010763741','HP','005','006','001','018','023','022','08780','02540','E','000000','00','','',''
'1236157415','2009-03-04','10:03:35','70060936XXXX','HC..','20','011345134','010763741','HP','001','006','001','018','023','022','08780','01640','E','000000','00','','',''
'1236157474','2009-03-04','10:04:34','70060936XXXX','HC..','20','011345167','010763741','HP','001','006','001','018','023','022','08780','01610','E','000000','00','','',''
'1236157534','2009-03-04','10:05:34','70060936XXXX','HC..','20','011345203','010763741','HP','001','006','001','018','023','022','08780','01950','E','000000','00','','',''
'1236157593','2009-03-04','10:06:33','70060936XXXX','HC..','20','011345250','010763741','HP','005','006','006','018','023','022','08780','03870','E','000000','00','','',''
'1236157654','2009-03-04','10:07:34','70060936XXXX','HC..','20','011345305','010763741','HP','001','006','006','018','023','022','08780','02920','E','000000','00','','',''
 
Résultat en mode DEBUG:
root@wrt54gl:~# ./teleinfoserial_csv 
----- 2008-10-12 15:59:52 -----
ADCO='700609361116'
OPTARIF='HC..'
ISOUSC='20'
HCHP='008444126'
HCHC='008228815'
PTEC='HP'
IINST1='002'
IINST2='000'
IINST3='001'
IMAX1='011'
IMAX2='020'
IMAX3='019'
PMAX='07470'
PAPP='00610'
HHPHC='E'
MOTDETAT='000000'
PPOT='00'
ADIR1=''
ADIR2=''
ADIR3=''
*/
 
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <syslog.h>
#include <termios.h>
#include <sys/fcntl.h>
#include <sys/types.h>
 
// Define port serie
#define BAUDRATE B1200
#define SERIALPORT "/dev/ttyS0"
 
// Fichier local au Wrt4gl/PC + fichier trame pour debug.
#define DATACSV "/tmp/teleinfo.csv"
#define TRAMELOG "/tmp/teleinfotrame."
 
// Active mode debug.
#define DEBUG
 
//-----------------------------------------------------------------------------
 
// Déclaration pour le port série.
int             fdserial ;
struct termios  termiosteleinfo ;
 
// Déclaration pour les données.
char ch[2] ;
char car_prec ;
char message[512] ;
char* match;
char datateleinfo[512] ;
 
// Constantes/Variables à changées suivant abonnement.
#define NB_VALEURS 20			// Nombre de valeurs à relever, voir tableau "etiquettes", 20 pour abonnement tri heures creuse.
char etiquettes[NB_VALEURS][16] = {"ADCO", "OPTARIF", "ISOUSC", "HCHP", "HCHC", "PTEC", "IINST1", "IINST2", "IINST3", "IMAX1", "IMAX2", "IMAX3", "PMAX", "PAPP", "HHPHC", "MOTDETAT", "PPOT", "ADIR1", "ADIR2" ,"ADIR3"} ;
// Fin Constantes/variables à changées suivant abonnement.
 
char 	valeurs[NB_VALEURS][18] ;
char	checksum[255] ;
int 	res ;
int	no_essais = 1 ;
int	nb_essais = 3 ;
int	erreur_checksum = 0 ;
 
// Déclaration pour la date.
time_t 		td;
struct 	tm 	*dc;
char		sdate[12];
char		sheure[10];
char		timestamp[11];
 
/*------------------------------------------------------------------------------*/
/* Init 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 (Nouvelle version de config.)
        if ( (device=open(SERIALPORT, O_RDWR | O_NOCTTY)) == -1 ) 
	{
                syslog(LOG_ERR, "Erreur ouverture du port serie %s !", SERIALPORT);
                exit(1) ;
        }
 
        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.
 
	// Format série "7E1"
	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 ;
}
 
/*------------------------------------------------------------------------------*/
/* Lecture données téléinfo sur port série					*/
/*------------------------------------------------------------------------------*/
void LiTrameSerie(int device)
{
// (0d 03 02 0a => Code fin et début trame)
	tcflush(device, TCIFLUSH) ;			// Efface les données non lus en entrée.
	message[0]='\0' ;
	memset(valeurs, 0x00, sizeof(valeurs)) ; 
 
	do
	{
		car_prec = ch[0] ;
		res = read(device, ch, 1) ;
		if (! res)
		{	
			syslog(LOG_ERR, "Erreur pas de réception début données Téléinfo !\n") ;
			close(device);
			exit(1) ;
		}
	 }
	while ( ! (ch[0] == 0x02 && car_prec == 0x03) ) ;	// Attend code fin suivi de début trame téléinfo .
 
	do
	{
		res = read(device, ch, 1) ;
		if (! res)
		{	
			syslog(LOG_ERR, "Erreur pas de réception fin données Téléinfo !\n") ;
			close(device);
			exit(1) ;
		}
		ch[1] ='\0' ;
		strcat(message, ch) ;
	}
	while (ch[0] != 0x03) ;				// Attend code fin trame téléinfo.
}
 
/*------------------------------------------------------------------------------*/
/* Test checksum d'un message (Return 1 si checkum ok)				*/
/*------------------------------------------------------------------------------*/
int checksum_ok(char *etiquette, char *valeur, char 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 ;
}
 
/*------------------------------------------------------------------------------*/
/* Recherche valeurs des étiquettes de la liste.				*/
/*------------------------------------------------------------------------------*/
int LitValEtiquettes()
{
	int id ;
	erreur_checksum = 0 ;
 
	for (id=0; id<NB_VALEURS; id++)
	{
		if ( (match = strstr(message, etiquettes[id])) != NULL)
		{
			sscanf(match, "%s %s %s", etiquettes[id], valeurs[id], checksum) ;
			if ( strlen(checksum) > 1 ) checksum[0]=' ' ;	// sscanf ne peux lire le checksum à 0x20 (espace), si longueur checksum > 1 donc c'est un espace.
			if ( ! checksum_ok(etiquettes[id], valeurs[id], checksum[0]) ) 
			{
				syslog(LOG_ERR, "Donnees teleinfo [%s] corrompues (essai %d) !\n", etiquettes[id], no_essais) ;
				erreur_checksum = 1 ;
				return 0 ;
			}
		}
	}
	// Remplace chaine "HP.." ou "HC.." par "HP ou "HC".
	valeurs[5][2] = '\0' ;
	#ifdef DEBUG
	printf("----------------------\n") ; for (id=0; id<NB_VALEURS; id++) printf("%s='%s'\n", etiquettes[id], valeurs[id]) ;
	#endif
	return 1 ;
}
 
/*------------------------------------------------------------------------------*/
/* Test si dépassement intensité						*/
/*------------------------------------------------------------------------------*/
int DepasseCapacite()
{
	//  Test sur les 3 phases (étiquette ADIR1, ADIR2, ADIR3) à remplacer par ADPS pour monophasé.
	if ( (strlen(valeurs[17])) || (strlen(valeurs[18])) || (strlen(valeurs[19])) )
	{
		syslog(LOG_INFO, "Dépassement d'intensité: ADRI1='%s', ADRI2='%s', ADRI3='%s' !", valeurs[17], valeurs[18], valeurs[19]) ;
		return 1 ;
	}
	return 0 ;
}
 
/*------------------------------------------------------------------------------*/
/* Ecrit les données teleinfo dans fichier DATACSV				*/
/*------------------------------------------------------------------------------*/
void writecsvteleinfo(char data[])
{
        /* Ouverture fichier csv */
        FILE *datateleinfo ;
        if ((datateleinfo = fopen(DATACSV, "a")) == NULL)
        {
		syslog(LOG_ERR, "Erreur ouverture fichier teleinfo %s !", DATACSV) ; 
                exit(1);
        }
        fprintf(datateleinfo, "%s\n", data) ;
        fclose(datateleinfo) ;
}
 
#ifdef DEBUG
/*------------------------------------------------------------------------------*/
/* Ecrit la trame teleinfo dans fichier si erreur (pour debugger)		*/
/*------------------------------------------------------------------------------*/
void writetrameteleinfo(char trame[], char ts[])
{
	char nomfichier[255] = TRAMELOG ;
	strcat(nomfichier, ts) ;
        FILE *teleinfotrame ;
        if ((teleinfotrame = fopen(nomfichier, "w")) == NULL)
        {
		syslog(LOG_ERR, "Erreur ouverture fichier teleinfotrame %s !", nomfichier) ;
                exit(1);
        }
        fprintf(teleinfotrame, "%s", trame) ;
        fclose(teleinfotrame) ;
}
#endif
 
/*------------------------------------------------------------------------------*/
/* Main										*/
/*------------------------------------------------------------------------------*/
int main(int argc, char *argv[])
{
 
 openlog("teleinfoserial", LOG_PID, LOG_USER) ;
 fdserial = initserie() ;
 
 do
 {
	// Lit trame téléinfo.
	LiTrameSerie(fdserial) ;
 
	time(&td) ;                                     // Lit date/heure système.
	dc = localtime(&td) ;
	strftime(sdate,sizeof sdate,"%Y-%m-%d",dc);
	strftime(sheure,sizeof sdate,"%H:%M:%S",dc);
	strftime(timestamp,sizeof timestamp,"%s",dc);
 
	#ifdef DEBUG
	writetrameteleinfo(message, timestamp) ;	// Enregistre trame en mode debug.
	#endif
 
	if ( LitValEtiquettes() ) 			// Lit valeurs des étiquettes de la liste.
	{
		sprintf(datateleinfo,"'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s'", timestamp, sdate, sheure, valeurs[0], valeurs[1], valeurs[2], valeurs[3], valeurs[4], valeurs[5], valeurs[6], valeurs[7], valeurs[8], valeurs[9], valeurs[10], valeurs[11], valeurs[12], valeurs[13], valeurs[14], valeurs[15], valeurs[16], valeurs[17], valeurs[18], valeurs[19]) ;
		writecsvteleinfo(datateleinfo) ;	// Ecriture dans fichier csv.
 
		DepasseCapacite() ; 			// Test si etiquette dépassement intensité (log l'information seulement).
	}
	#ifdef DEBUG
	else writetrameteleinfo(message, timestamp) ;	// Si erreur checksum enregistre trame.
	#endif
	no_essais++ ;
 }
 while ( (erreur_checksum) && (no_essais <= nb_essais) ) ;
 
 close(fdserial) ;
 closelog() ;
 exit(0) ;
}



Outils personnels
Administration wiki