[Résolu] Calcul Checksum Téléinfo EDF

Bonsoir,

Je suis dans la lecture de la teleinfo EDF et sur la notice ERDF on lit que chaque ligne de la trame teleinfo se termine par un caractère de vérification le “checksum”

Voici un exemple de ce que je lis sur ma téléinfo :

.
ISOUSC 45 ?
HCHC 077245863 0
HCHP 096228446 <
PTEC HP…
IINST 002 Y
IMAX 044 G
PAPP 00480 -

Ils “expliquent” ::slight_smile: comment il est calculé :

La “checksum” est calculée sur l’ensemble des caractères allant du début du champ étiquette à la fin du champ donnée, caractère SP inclus. On fait tout d’abord la somme des codes ASCII de tous ces caractères. Pour éviter d’introduire des fonctions ASCII (00 à 1F en hexadécimal), on ne conserve que les six bits de poids faible du résultat obtenu (cette opération se traduit par un ET logique entre la somme précédemment calculée et 03Fh). Enfin, on ajoute 20 en hexadécimal. Le résultat sera donc toujours un caractère ASCII imprimable (signe, chiffre, lettre majuscule) allant de 20 à 5F en Hexadécimal.

Et la je ne comprend pas du tout comment je fais ce calcul :frowning:

J’arrive sans soucis à récupérer les différentes valeurs mais j’aimerais les vérifier avant de les intégrer dans mon sketch de chauffage notamment pour le délestage ou je dois être sûr de la valeur utilisée.

Quelqu’un veux bien se donner la peine de m’expliquer comment faire ça ?

Grosso-modo :

sum = 0
pour chaque caractère de la chaine :
   sum = sum + code_caractère    // On fait tout d'abord la somme des codes ASCII de tous ces caractères
et pour finir: sum = sum & 0x3F + 0x20

L’explication d’ERDF est compliquée pour dire que 32 <= sum < 96…

0x3F en binaire = 00111111. Le & (AND bit à bit http://arduino.cc/en/Reference/BitwiseAnd) permet de ne garder que les 6 derniers bit du code ascii considéré (c-a-d l’arrondir à une valeur max de 63, en somme. Tu pourrais même faire un modulo avec sum % 64). Additionner 0x20 (32) permet de s’assurer que la somme sera de 32 minimum. Donc le caractère ascii correspondant à ce résultat sera affichable, puisque 32 en ascii est l’espace et les suivants sont : http://www.asciitable.com/

Je te laisse réfléchir au code correspondant :slight_smile:

C'est en effet déjà bien plus compréhensible.

Je planche dessus depuis hier mais je ne m'en sort toujorus pas :( Quand tu dis faire d'abord la somme de tous les codes caractère est-ce que sum et un char ou un int ?

EDIT: un char

Sur une base de trame EDF "XXXXXXXX" => EDIT: c'est mon numéro d'abonné je le laisse pas visible :D dans tous les cas si je termine par faire :

[b]EDIT: Idem il y avait mon numéro d'abonné je le laisse pas visible  :D [/b]

sum = sum & 0x3F + 0x20 je n'obtiens pas sum = 5

Je suis vraiment perdu peux tu mes dire où je me trompe ?

:grin:
Bon ben comme d’habitude je coinçais sur une erreur bête.

ERDF indiquait qu’on faisait la sommes des caractère SP inclus et je pensait qu’il fallait inclure aussi le dernier SP avant le checksum.

Donc sans le SP de la fin cela fonctionne.

String trame="PAPP 03170 ,";

char sum;

void setup() {
Serial.begin(9600);     // Port série pour liasion Arduino <-> PC 


}

void loop() {

Serial.print("Trame = ");Serial.println(trame);  

sum=0;
for (byte i=0;i<(trame.length()-2);i++){
          sum = trame[i] +sum;
}  
sum = (sum & 0x3F) + 0x20;
Serial.print("Checksum = ");Serial.println(sum);
Serial.println("");

delay(10000);  
}

Résultat de la console :

Trame = PAPP 03170 ,
Checksum = ,

Merci Poogahzi je n'y serrait pas arrivé sans toi.

Tant mieux, bravo !

Quelques remarques juste pour le plaisir :)

Quand tu dis faire d'abord la somme de tous les codes caractère est-ce que sum et un char ou un int ?

Peu importe, le & 0x3F ramène le résultat aux 6 derniers bits.

for (byte i=0;...

Attention quand même, si ta chaine fait plus de 256 char, bug... i en int serait plus simple.

sum = trame[i] +sum;

peut s'écrire

sum += trame[i];

http://arduino.cc/en/Reference/IncrementCompound

sum = (sum & 0x3F) + 0x20;

Le & est prioritaire sur le +, les parenthèses sont donc optionnelles.

Bonne continuation.

Merci pour les dernières précisions et pour ceux que ça intéresse mon sketch “simplifié” pour extraire l’Index des heures creuses, l’Index des heures pleines, la période heures creuse ou heures pleine ,l’Intensité instantanée et la Puissance apparente de la teleinfo :
(si vous avez plus simples je suis preneur)

#include <SoftwareSerial.h>
SoftwareSerial Serial_Teleinfo(11,12); // Créé une liason série sur les pin 11 et 12
                                               // Pin 11 sur l'optocoupleur
long Index_HC=0;
long Index_HP=0;
byte I_A=0;
int P_W=0;
char PERIODE=' ';

unsigned long temps_d_acquisition = 0;



void setup() {
Serial.begin(9600);     // Port série pour liasion Arduino <-> PC
Serial_Teleinfo.begin(1200); // Port série pour liasion Arduino <-> Teleinfo
}

void loop() {

                  
                  temps_d_acquisition=millis();
                  
                  Recupere_la_Teleinfo();
                  
                  Serial.print("Temps d'aquisition : ");Serial.print(millis()-temps_d_acquisition);Serial.println(" Millisecondes");
                  
                  Serial.print(" => Index HC : ");Serial.print(Index_HC);Serial.println(" Wh");
                  Serial.print(" => Index HP : ");Serial.print(Index_HP);Serial.println(" Wh");
                  Serial.print(" => PERIODE : H") ;Serial.println(PERIODE);
                  Serial.print(" => Intensite instantanee : ");Serial.print(I_A);Serial.println(" A");
                  Serial.print(" => Puissance apparente : ");Serial.print(P_W); Serial.println(" W");
                  
      
}



void Recupere_la_Teleinfo(){

char charIn_Trame_Teleinfo = 0; // stock chaque charactère recu de la trame teleinfo

String Ligne;      // stock la ligne complette (entre LF(0x0A) et CR(0x0D))
String Etiquette;  // stock l'intitulé
String Valeur;     // stock la valeur apres l'intitulé
char Checksum;
/*
Trame recu par la teleinfo      (Expliquations ! non recu par la teleinfo)

ADCO 040422040644 5	        (N° d’identification du compteur : ADCO (12 caractères))
OPTARIF HC.. <	                (Option tarifaire (type d’abonnement) : OPTARIF (4 car.))
ISOUSC 45 ?	                (Intensité souscrite : ISOUSC ( 2 car. unité = ampères))
HCHC 077089461 0	        (Index heures creuses si option = heures creuses : HCHC ( 9 car. unité = Wh))
HCHP 096066754 >	        (Index heures pleines si option = heures creuses : HCHP ( 9 car. unité = Wh))
PTEC HP..  	                (Période tarifaire en cours : PTEC ( 4 car.))
IINST 002 Y	                (Intensité instantanée : IINST ( 3 car. unité = ampères))
IMAX 044 G	                (Intensité maximale : IMAX ( 3 car. unité = ampères))
PAPP 00460 +	                (Puissance apparente : PAPP ( 5 car. unité = Volt.ampères))
HHPHC E 0	                (Groupe horaire si option = heures creuses ou tempo : HHPHC (1 car.))
MOTDETAT 000000 B	        (Mot d’état (autocontrôle) : MOTDETAT (6 car.))
*/


//RAZ des valeurs
              Index_HC=Index_HP=I_A=P_W=0;
              PERIODE= ' ';

while ( Index_HC==0 || Index_HP==0 || I_A==0 || P_W==0 || PERIODE==' ' ){ // recommence tant que l'on a pas recu tous les élément voulus

  
          //Attend un début de ligne (0x0A)
                       charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ; 
                       while (charIn_Trame_Teleinfo != 0x0A){ // reste dans cette boucle tant qu'on ne recoit pas le Charactere de début de ligne 0x0A
                             if ((millis()-temps_d_acquisition)>1000 ) {Serial.println("Teleinfo Inaccesible");loop();} // Affiche un erreur si la teleinfo est inaccesible et retourne à Loop 
                             if (Serial_Teleinfo.available()){ // Tant qu'il y a des caractères disponibles
                                     charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ; // Stock 1 caractere recu
                             }
                       }
          //Vide Ligne
                       Ligne="";
          //Vide Etiquette
                       Etiquette="";
          //Concatene les carateres recus jusqu'a l'espace suivant (0x20)
                       charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ; 
                       while (charIn_Trame_Teleinfo != 0x20){ // reste dans cette boucle tant qu'on ne recoit pas le Charactere Espace
                                          if (Serial_Teleinfo.available()){ // Tant qu'il y a des caractères disponibles
                                                  charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ; // Stock 1 caractere recu
                                                  if (charIn_Trame_Teleinfo != 0x20){Etiquette += charIn_Trame_Teleinfo;}      // concatene les caractères reçus sauf les espaces
                                                  Ligne += charIn_Trame_Teleinfo;
                                          }                     
                        }
                               
          //Vide Valeur
                       Valeur="";     
          //Concatene les carateres recus jusqu'a l'espace suivant (0x20)
                       charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ; 
                       while (charIn_Trame_Teleinfo != 0x20){ // reste dans cette boucle tant qu'on ne recoit pas le Charactere Espace
                                          if (Serial_Teleinfo.available()){ // Tant qu'il y a des caractères disponibles
                                                  charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ; // Stock 1 caractere recu
                                                  if (charIn_Trame_Teleinfo != 0x20){Valeur += charIn_Trame_Teleinfo;}      // concatene les caractères reçus sauf les espaces
                                                  Ligne += charIn_Trame_Teleinfo;
                                          }                     
                        }
                
          //Concatene les carateres recus jusqu'a la fin de ligne (0x0D)
                       charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ; 
                       while (charIn_Trame_Teleinfo != 0x0D){ // reste dans cette boucle tant qu'on ne recoit pas le Charactere de fin de ligne
                                          if (Serial_Teleinfo.available()){ // Tant qu'il y a des caractères disponibles
                                                  charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ; // Stock 1 caractere recu
                                                  if (charIn_Trame_Teleinfo != 0x0D){Ligne += charIn_Trame_Teleinfo;} // concatene les caractères reçus sauf le Charactere de fin de ligne (0x0D)
                                                  if (charIn_Trame_Teleinfo != 0x0D){Checksum = charIn_Trame_Teleinfo;}
                                          }                     
                        }
         //Controle du Checksum (Le dernier caractere de la ligne et un caractere de controle)

                            char Controle=0;
                            String trame= Etiquette + " " + Valeur;
                            for (byte i=0;i<(trame.length()-2);i++){
                                      Controle = trame[i] +Controle;
                            }  
                            Controle = (Controle & 0x3F) + 0x20;              
              
              if (Controle = Checksum) { // Si le checksum correspond bien au code controlé
                
          //Associe la valeur lue à son etiquette
                if (Etiquette.substring (0,4)=="HCHC") {Index_HC = Valeur.toInt();}
                if (Etiquette.substring (0,4)=="HCHP") {Index_HP = Valeur.toInt();}
                if (Etiquette.substring (0,4)=="PTEC") {PERIODE  = Valeur[1];     }
                if (Etiquette.substring (0,4)=="IINS") {I_A      = Valeur.toInt();}
                if (Etiquette.substring (0,4)=="PAPP") {P_W      = Valeur.toInt();}      
              }
}


}
if (Controle = Checksum) {

hummm... là tu testes juste si Checksum est positif, en l'assignant à Controle... Donc probable que ton test de somme de contrôle soit presque tjs valide...

ça donne quoi ça :

if (Controle == Checksum) {

Encore une fois bien vu Poogahzi 8)

En testant avec == ben ça marche plus >:(

J’avais fait une erreur

for (byte i=0;i<(trame.length()-2);i++){
                                      Controle = trame[i] +Controle;
                            }

à modifier par

for (byte i=0;i<(trame.length());i++){
                                      Controle = trame[i] +Controle;
                            }

Donc le code fonctionnel :

#include <SoftwareSerial.h>
SoftwareSerial Serial_Teleinfo(11,12); // Créé une liason série sur les pin 11 et 12
                                               // Pin 11 sur l'optocoupleur
long Index_HC=0;
long Index_HP=0;
byte I_A=0;
int P_W=0;
char PERIODE=' ';

unsigned long temps_d_acquisition = 0;



void setup() {
Serial.begin(9600);     // Port série pour liasion Arduino <-> PC
Serial_Teleinfo.begin(1200); // Port série pour liasion Arduino <-> Teleinfo
}

void loop() {

                  
                  temps_d_acquisition=millis();
                  
                  Recupere_la_Teleinfo();
                  
                  Serial.print("Temps d'aquisition : ");Serial.print(millis()-temps_d_acquisition);Serial.println(" Millisecondes");
                  
                  Serial.print(" => Index HC : ");Serial.print(Index_HC);Serial.println(" Wh");
                  Serial.print(" => Index HP : ");Serial.print(Index_HP);Serial.println(" Wh");
                  Serial.print(" => PERIODE : H") ;Serial.println(PERIODE);
                  Serial.print(" => Intensite instantanee : ");Serial.print(I_A);Serial.println(" A");
                  Serial.print(" => Puissance apparente : ");Serial.print(P_W); Serial.println(" W");
                  
      
}



void Recupere_la_Teleinfo(){

char charIn_Trame_Teleinfo = 0; // stock chaque charactère recu de la trame teleinfo

String Ligne;      // stock la ligne complette (entre LF(0x0A) et CR(0x0D))
String Etiquette;  // stock l'intitulé
String Valeur;     // stock la valeur apres l'intitulé
char Checksum;
/*
Trame recu par la teleinfo      (Expliquations ! non recu par la teleinfo)

ADCO 040422040644 5	        (N° d’identification du compteur : ADCO (12 caractères))
OPTARIF HC.. <	                (Option tarifaire (type d’abonnement) : OPTARIF (4 car.))
ISOUSC 45 ?	                (Intensité souscrite : ISOUSC ( 2 car. unité = ampères))
HCHC 077089461 0	        (Index heures creuses si option = heures creuses : HCHC ( 9 car. unité = Wh))
HCHP 096066754 >	        (Index heures pleines si option = heures creuses : HCHP ( 9 car. unité = Wh))
PTEC HP..  	                (Période tarifaire en cours : PTEC ( 4 car.))
IINST 002 Y	                (Intensité instantanée : IINST ( 3 car. unité = ampères))
IMAX 044 G	                (Intensité maximale : IMAX ( 3 car. unité = ampères))
PAPP 00460 +	                (Puissance apparente : PAPP ( 5 car. unité = Volt.ampères))
HHPHC E 0	                (Groupe horaire si option = heures creuses ou tempo : HHPHC (1 car.))
MOTDETAT 000000 B	        (Mot d’état (autocontrôle) : MOTDETAT (6 car.))
*/


//RAZ des valeurs
              Index_HC=Index_HP=I_A=P_W=0;
              PERIODE= ' ';

while ( Index_HC==0 || Index_HP==0 || I_A==0 || P_W==0 || PERIODE==' ' ){ // recommence tant que l'on a pas recu tous les élément voulus

  
          //Attend un début de ligne (0x0A)
                       charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ; 
                       while (charIn_Trame_Teleinfo != 0x0A){ // reste dans cette boucle tant qu'on ne recoit pas le Charactere de début de ligne 0x0A
                             if ((millis()-temps_d_acquisition)>1000 ) {Serial.println("Teleinfo Inaccesible");loop();} // Affiche un erreur si la teleinfo est inaccesible et retourne à Loop 
                             if (Serial_Teleinfo.available()){ // Tant qu'il y a des caractères disponibles
                                     charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ; // Stock 1 caractere recu
                             }
                       }
          //Vide Ligne
                       Ligne="";
          //Vide Etiquette
                       Etiquette="";
          //Concatene les carateres recus jusqu'a l'espace suivant (0x20)
                       charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ; 
                       while (charIn_Trame_Teleinfo != 0x20){ // reste dans cette boucle tant qu'on ne recoit pas le Charactere Espace
                                          if (Serial_Teleinfo.available()){ // Tant qu'il y a des caractères disponibles
                                                  charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ; // Stock 1 caractere recu
                                                  if (charIn_Trame_Teleinfo != 0x20){Etiquette += charIn_Trame_Teleinfo;}      // concatene les caractères reçus sauf les espaces
                                                  Ligne += charIn_Trame_Teleinfo;
                                          }                     
                        }
                               
          //Vide Valeur
                       Valeur="";     
          //Concatene les carateres recus jusqu'a l'espace suivant (0x20)
                       charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ; 
                       while (charIn_Trame_Teleinfo != 0x20){ // reste dans cette boucle tant qu'on ne recoit pas le Charactere Espace
                                          if (Serial_Teleinfo.available()){ // Tant qu'il y a des caractères disponibles
                                                  charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ; // Stock 1 caractere recu
                                                  if (charIn_Trame_Teleinfo != 0x20){Valeur += charIn_Trame_Teleinfo;}      // concatene les caractères reçus sauf les espaces
                                                  Ligne += charIn_Trame_Teleinfo;
                                          }                     
                        }
                
          //Concatene les carateres recus jusqu'a la fin de ligne (0x0D)
                       charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ; 
                       while (charIn_Trame_Teleinfo != 0x0D){ // reste dans cette boucle tant qu'on ne recoit pas le Charactere de fin de ligne
                                          if (Serial_Teleinfo.available()){ // Tant qu'il y a des caractères disponibles
                                                  charIn_Trame_Teleinfo = Serial_Teleinfo.read() & 0x7F ; // Stock 1 caractere recu
                                                  if (charIn_Trame_Teleinfo != 0x0D){Ligne += charIn_Trame_Teleinfo;} // concatene les caractères reçus sauf le Charactere de fin de ligne (0x0D)
                                                  if (charIn_Trame_Teleinfo != 0x0D){Checksum = charIn_Trame_Teleinfo;}
                                          }                     
                        }
         //Controle du Checksum (Le dernier caractere de la ligne et un caractere de controle)

                            char Controle=0;
                            String trame= Etiquette + " " + Valeur;
                            for (byte i=0;i<(trame.length());i++){
                                      Controle += trame[i];
                            }  
                            Controle = (Controle & 0x3F) + 0x20;
                            
              
              if (Controle == Checksum) { // Si le checksum correspond bien au code controlé
                
          //Associe la valeur lue à son etiquette
                if (Etiquette.substring (0,4)=="HCHC") {Index_HC = Valeur.toInt();}
                if (Etiquette.substring (0,4)=="HCHP") {Index_HP = Valeur.toInt();}
                if (Etiquette.substring (0,4)=="PTEC") {PERIODE  = Valeur[1];     }
                if (Etiquette.substring (0,4)=="IINS") {I_A      = Valeur.toInt();}
                if (Etiquette.substring (0,4)=="PAPP") {P_W      = Valeur.toInt();}      
              }
}


}

mon compteur va être changé par un nouveau LINKY, savez-vous si la téléinfo marchera toujours ?

merci

Bonjour je suis nouveau sur le forum, je cherche à faire un delesteur utilisant la teleinformation et les valeurs d'intensité. en utilisant ce code, je ne sors absolument rien je n'ai que le message teleinfo inaccessible. Je suis mecanicien de formation et je galère. Si quelqu'un pouvait m'aider ce serait le top. Je cherche à recupérer les valeurs de I (je suis en triphasé) dans 3 variable pour gerer des relais de delestage. Merci pour votre aide