Teleinfo serial mono.c
De MicElectroLinGenMet.
Origine page Interface téléinfo.
Voir programme plus évolué teleinfoserial_csv.c pour abonnement triphasé heures creuses, il suffira d'adapter les paramètres de l'abonnement.
// 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. // Version pour compteur monophasé (Modifiée/Testée par Ludovic Gomez). // Par domos78 at free point fr (Dan) /* 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) - Nombre de valeurs et tableaux à modifier en fonction abonnement EDF: NB_VALEURS, "etiquettes", "valeurs" et "poschecksum" Compilation PC: - gcc -Wall teleinfoserial.c -o teleinfoserial Compilation wrt54gl: Voir exemple compilation avec SDK (OpenWrt-SDK-Linux). /* Trame téléinfo. compteur monophasé: ADCO 02082841xxxx D OPTARIF HC.. < ISOUSC 45 ? HCHC 000007441 V HCHP 000007487 - PTEC HP.. IINST 004 [ IMAX 016 F PAPP 00810 * HHPHC A , MOTDETAT 000000 B 00017440 0a 41 44 43 4f 20 30 32 30 38 32 38 xx xx xx xx |.ADCO 020828xxxx| 00017450 30 39 20 44 0d 0a 4f 50 54 41 52 49 46 20 48 43 |09 D..OPTARIF HC| 00017460 2e 2e 20 3c 0d 0a 49 53 4f 55 53 43 20 34 35 20 |.. <..ISOUSC 45 | 00017470 3f 0d 0a 48 43 48 43 20 30 30 30 30 30 37 34 34 |?..HCHC 00000744| 00017480 31 20 56 0d 0a 48 43 48 50 20 30 30 30 30 30 37 |1 V..HCHP 000007| 00017490 34 38 37 20 2d 0d 0a 50 54 45 43 20 48 50 2e 2e |487 -..PTEC HP..| 000174a0 20 20 0d 0a 49 49 4e 53 54 20 30 30 34 20 5b 0d | ..IINST 004 [.| 000174b0 0a 49 4d 41 58 20 30 31 36 20 46 0d 0a 50 41 50 |.IMAX 016 F..PAP| 000174c0 50 20 30 30 38 31 30 20 2a 0d 0a 48 48 50 48 43 |P 00810 *..HHPHC| 000174d0 20 41 20 2c 0d 0a 4d 4f 54 44 45 54 41 54 20 30 | A ,..MOTDETAT 0| 000174e0 30 30 30 30 30 20 42 0d 03 02 0a 41 44 43 4f 20 |00000 B....ADCO | */ //----------------------------------------------------------------------------- #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 VERSION "20090419" #define BAUDRATE B1200 #define SERIALPORT "/dev/ttyUSB0" // Fichier local au Wrt4gl + fichier trame pour debug. #define DATACSV "/tmp/teleinfo.csv" #define TRAMELOG "/tmp/teleinfotrame." // Nombre de valeurs à relever, voir tableau "etiquettes" et "poschecksum". #define NB_VALEURS 12 // Active mode debug, désactivé par defaut. //#define DEBUG //----------------------------------------------------------------------------- // 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* match; int id ; char datateleinfo[512] ; char etiquettes[NB_VALEURS][16] = {"ADCO", "OPTARIF", "ISOUSC", "HCHP", "HCHC", "PTEC", "IINST", "IMAX", "PAPP", "HHPHC", "MOTDETAT", "ADPS"} ; char poschecksum[NB_VALEURS] = {18, 13 , 10 , 15, 15, 10, 10, 9, 11, 8, 16, 9} ; char valeurs[NB_VALEURS][18] ; char checksum[2] ; 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 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. 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 ; } /*------------------------------------------------------------------------------*/ /* 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 ; } /*------------------------------------------------------------------------------*/ /* 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) ; #ifdef DEBUG syslog(LOG_INFO, "Version %s", VERSION) ; #endif fdserial = initserie() ; do { tcflush(fdserial, TCIFLUSH) ; // Efface les données non lus en entrée. message[0]='\0' ; memset(valeurs, 0x00, sizeof(valeurs)) ; erreur_checksum = 0 ; // -------------------------------------------------------------------------------------- // Lecture données téléinfo (0d 03 02 0a => Code fin et début trame) do { car_prec = ch[0] ; res = read(fdserial, ch, 1) ; if (! res) { syslog(LOG_ERR, "Erreur pas de réception début données Téléinfo !\n") ; close(fdserial); exit(1) ; } } while ( ! (ch[0] == 0x02 && car_prec == 0x03) ) ; // Attend code fin suivi de début trame téléinfo . do { res = read(fdserial, ch, 1) ; if (! res) { syslog(LOG_ERR, "Erreur pas de réception fin données Téléinfo !\n") ; close(fdserial); exit(1) ; } ch[1] ='\0' ; strcat(message, ch) ; } while (ch[0] != 0x03) ; // Attend code fin trame téléinfo. // -------------------------------------------------------------------------------------- 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); // Recherche valeurs des étiquettes de la liste. for (id=0; id<NB_VALEURS; id++) { if ( (match = strstr(message, etiquettes[id])) != NULL) { sscanf(match, "%s %s", etiquettes[id], valeurs[id]) ; strncpy(checksum, match + poschecksum[id], 1) ; //Lit code checksum (sscanf ne peux le lire quand checksum=0x20). if ( ! checksum_ok(etiquettes[id], valeurs[id], checksum[0]) ) { erreur_checksum=1 ; syslog(LOG_ERR, "Donnees teleinfo [%s] corrompues (essai %d) !\n", etiquettes[id], no_essais) ; #ifdef DEBUG writetrameteleinfo(message, timestamp) ; #endif } } } // Remplace chaine "HP.." ou "HC.." par "HP ou "HC". valeurs[5][2] = '\0' ; if (! erreur_checksum) { // Test si valeurs HCHP, HCHC, PAPP sont vides (possible aprés trame ADPS). if ( (strlen(valeurs[3])) && (strlen(valeurs[4])) && (strlen(valeurs[8])) ) { #ifdef DEBUG printf("----- %s %s -----\n",sdate, sheure) ; for (id=0; id<NB_VALEURS; id++) printf("%s='%s'\n", etiquettes[id], valeurs[id]) ; #endif sprintf(datateleinfo,"'%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]) ; writecsvteleinfo(datateleinfo) ; // Ecriture dans fichier csv. // Test si etiquette dépassement intensité sur 1 phase (ADPS). if ( (strlen(valeurs[11])) ) { syslog(LOG_INFO, "Dépassement d'intensité: ADPS='%s' !", valeurs[11]) ; } } else { syslog(LOG_ERR, "Donnees teleinfo vide: HCHP=%s, HCHC=%s, PAPP=%s !\n", valeurs[3], valeurs[4], valeurs[8]) ; #ifdef DEBUG writetrameteleinfo(message, timestamp) ; #endif } } no_essais++ ; } while ( (erreur_checksum) && (no_essais <= nb_essais) ) ; close(fdserial) ; closelog() ; exit(0) ; }
