Complete readme and comments in tic.h

This commit is contained in:
nago 2025-03-24 23:15:20 +01:00
parent 9adcf6506e
commit d2da947fc4
2 changed files with 239 additions and 76 deletions

170
README.md
View File

@ -1,18 +1,10 @@
# linkytic # Linky tic
interface basique pour lire les données tic du compteur linky et les partager en format json via un esp8266/node-mcu. Interface basique pour lire les données TIC d'un compteur Linky et les partager au format JSON via un ESP8266/NodeMCU. L'ensemble est fonctionnel avec Home Assistant en définissant une interface REST.
## Configuration ## Configuration
Créer un fichier secret.h (pour définir les logins et pwd du wifi. À partir du fichier secret-generic.h, créez un fichier secret.h pour définir les identifiants et mots de passe du Wi-Fi.
''' '''
// Secrets for your local home network
// This is a "hard way" to configure your local WiFi network name and passphrase
// into the source code and the uploaded sketch.
//
// Using the WiFi Manager is preferred and avoids reprogramming when your network changes.
// See https://homeding.github.io/#page=/wifimanager.md
// ssid and passPhrase can be used when compiling for a specific environment as a 2. option. // ssid and passPhrase can be used when compiling for a specific environment as a 2. option.
// add you wifi network name and PassPhrase or use WiFi Manager // add you wifi network name and PassPhrase or use WiFi Manager
#ifndef STASSID #ifndef STASSID
#define STASSID "Wifi_Id" #define STASSID "Wifi_Id"
@ -23,15 +15,163 @@ const char *ssid = STASSID;
const char *passPhrase = STAPSK; const char *passPhrase = STAPSK;
''' '''
Le code est prévu pour un linky en mode standard. Il devrait être fonctionel en mode historique en changent la vitesse du port (1500bps en historique, 9600bps en standard). Le code est prévu pour un Linky en mode standard. Il devrait également fonctionner en mode historique en changeant la vitesse du port (1500 bps en historique, 9600 bps en standard).
## Interface ## Interface
Un simple optocoupleur suffit. Un simple optocoupleur suffit. Le schéma disponible sur https://hallard.me/demystifier-la-teleinfo/ fonctionne chez moi (sans ajout du transistor).
Le schéma disponible sur https://hallard.me/demystifier-la-teleinfo/ est fonctionel chez moi (sans ajout du transistor).
## Documents ## Documents
Enedis-NOI-CPT_54E version 3 (01/06/2018) Enedis-NOI-CPT_54E version 3 (01/06/2018)
## Intégration dans Home Assistant
Les informations Linky sont récupérées en définissant une interface REST. Pour faciliter la lecture du fichier configuration.yaml, j'ai intégré la définition dans un fichier spécifique que j'appelle via la commande :
'''
rest: !include NOM_FICHIER.yaml
'''
Le contenu du fichier de configuration REST est présenté ci-dessous. Remplacez ADRESSE_IP par l'adresse IP de l'ESP8266 (ou son chemin réseau si le DNS est correctement configuré).
Dans mon cas, le fichier contient de nombreux paramètres, mais il peut être simplifié pour se concentrer sur l'essentiel, comme le suivi de la consommation. Étant en triphasé, je surveille la consommation et la puissance sur chaque phase. Ayant des panneaux solaires, je contrôle également l'injection d'énergie. De plus, en participant à l'"effacement jour de pointe", je vérifie le tarif pour adapter ma domotique aux conditions actuelles.
Les informations de puissance et de courant sont scannées toutes les 30 secondes, tandis que les informations tarifaires le sont toutes les minutes.
Note : Pour cette utilisation, je trouve l'interface REST plus simple à mettre en œuvre que MQTT (ou d'autres protocoles). Modifier la fréquence de scan se fait facilement dans la configuration de Home Assistant. Cependant, la solution MQTT est aussi pertinente sur certains aspects.
'''
- scan_interval: 30
resource: http://ADRESSE_IP/ticbasic
sensor:
- name: "Index Conso totale"
value_template: "{{ value_json.EAST | int / 1000 | round(1) }}"
unit_of_measurement: kWh
device_class: energy
state_class: total
- name: "Index 01 Conso"
value_template: "{{ value_json.EASF01 | int / 1000 | round(1) }}"
unit_of_measurement: kWh
device_class: energy
state_class: total
- name: "Index 02 Conso"
value_template: "{{ value_json.EASF02 | int / 1000 | round(1) }}"
unit_of_measurement: kWh
device_class: energy
state_class: total
- name: "Index 03 Conso"
value_template: "{{ value_json.EASF03 | int / 1000 | round(1) }}"
unit_of_measurement: kWh
device_class: energy
state_class: total
- name: "Index 04 Conso"
value_template: "{{ value_json.EASF04 | int / 1000 | round(1) }}"
unit_of_measurement: kWh
device_class: energy
state_class: total
- name: "Energie injectee total"
value_template: "{{ value_json.EAIT | int / 1000 | round(1) }}"
unit_of_measurement: kWh
device_class: energy
state_class: total
- name: "Index 01 Generation"
value_template: "{{ value_json.ERQ1 | int / 1000 | round(1) }}"
unit_of_measurement: kWh
device_class: energy
state_class: total
- name: "Index 02 Generation"
value_template: "{{ value_json.ERQ2 | int / 1000 | round(1) }}"
unit_of_measurement: kWh
device_class: energy
state_class: total
- name: "Index 03 Generation"
value_template: "{{ value_json.ERQ3 | int / 1000 | round(1) }}"
unit_of_measurement: kWh
device_class: energy
state_class: total
- name: "Index 04 Generation"
value_template: "{{ value_json.ERQ4 | int / 1000 | round(1) }}"
unit_of_measurement: kWh
device_class: energy
state_class: total
- name: "Puissance instantanée injectée"
value_template: "{{ value_json.SINSTI | int / 1000 | round(1) }}"
unit_of_measurement: VA
device_class: energy
state_class: measurement
- name: "Courant Efficace Phase 1"
value_template: "{{ value_json.IRMS1 }}"
unit_of_measurement: A
device_class: current
state_class: measurement
- name: "Courant Efficace Phase 2"
value_template: "{{ value_json.IRMS2 }}"
unit_of_measurement: A
device_class: current
state_class: measurement
- name: "Courant Efficace Phase 3"
value_template: "{{ value_json.IRMS3 }}"
unit_of_measurement: A
device_class: current
state_class: measurement
- name: "Tension Efficace Phase 1"
value_template: "{{ value_json.URMS1 }}"
unit_of_measurement: V
device_class: voltage
state_class: measurement
- name: "Tension Efficace Phase 2"
value_template: "{{ value_json.URMS2 }}"
unit_of_measurement: V
device_class: voltage
state_class: measurement
- name: "Tension Efficace Phase 3"
value_template: "{{ value_json.URMS3 }}"
unit_of_measurement: V
device_class: voltage
state_class: measurement
- name: "Puissance apparente totale conso"
value_template: "{{ value_json.SINSTS | round(1) }}"
unit_of_measurement: VA
device_class: apparent_power
state_class: measurement
- name: "Puissance apparente Phase 1 conso"
value_template: "{{ value_json.SINSTS1 | round(1) }}"
unit_of_measurement: VA
device_class: apparent_power
state_class: measurement
- name: "Puissance apparente Phase 2 conso"
value_template: "{{ value_json.SINSTS2 | round(1) }}"
unit_of_measurement: VA
device_class: apparent_power
state_class: measurement
- name: "Puissance apparente Phase 3 conso"
value_template: "{{ value_json.SINSTS3 | round(1) }}"
unit_of_measurement: VA
device_class: apparent_power
state_class: measurement
- scan_interval: 10
resource: http://ADRESSE_IP/ticdata
sensor:
- unique_id: Elec_Tarif_Code
name: "Code Tarif"
value_template: "{{ value_json.NTARF }}"
- scan_interval: 60
resource: http://ADRESSE_IP/ticdata
sensor:
- unique_id: Elec_Tarif
name: "Tarif"
value_template: "{{ value_json.LTARF }}"
- name: "Message 1"
value_template: "{{ value_json.MSG1 }}"
- name: "Message 2"
value_template: "{{ value_json.MSG2 }}"
'''
## Compilation
J'utilise Arduino IDE.
Il faut ajouter http://arduino.esp8266.com/stable/package_esp8266com_index.json dans les paramétres.
Ensuite, sélectionner la board "Noce MCU 1.0" (ou autre suivant votre technologie).
### Configuration
La liste des paramètres à récupérer est définie dans tic.h sous SelectedEtiquette[NB_ETIQUETTE]. Il vous faudra aussi mettre à jour NB_ETIQUETTE.
Les étiquettes sont celles définies dans le document ENEDIS.
L'objectif initial de cette liste est de n'inclure dans le JSON que les informations pertinentes, permettant ainsi de gagner quelques millisecondes de traitement, quelques kilo-octets de données et quelques milliwatts de consommation. Bien que le JSON puisse contenir toutes les informations lues, cela n'est pas nécessairement utile pour un utilisateur standard, car jusqu'à 50 % des champs peuvent être vides ou sans réelle utilité domotique.
En résumé, vous pouvez adapter cette liste selon vos besoins.

133
tic.h
View File

@ -7,109 +7,131 @@
#define TIC #define TIC
// Définition des constantes pour les délimiteurs de trame TIC
//CF document ENEDIS
#define STX 0x02 // Début de la trame : 0x02 (<STX>) #define STX 0x02 // Début de la trame : 0x02 (<STX>)
#define ETX 0x03 // Fin de la trame : 0x03 (<ETX>) End Of Text #define ETX 0x03 // Fin de la trame : 0x03 (<ETX>) End Of Text
#define EOT 0x04 //End Of Transmission alias fin de transmission #define EOT 0x04 // Fin de transmission
#define LF 0x0A // Code ASCII pour <LF>, debut de groupe d'information #define LF 0x0A /// Code ASCII pour <LF>, début de groupe d'information
#define CR 0x0D // Fin de groupe d'information #define CR 0x0D // Fin de groupe d'information
#define HT 0x09 //separateur dans groupe #define HT 0x09 // Séparateur dans le groupe
#define SP 0x20 //separateur dans groupe #define SP 0x20 // Séparateur dans le groupe
#define NONUTILE "NONUTILE" #define NONUTILE "NONUTILE" // Indicateur pour les champs non utilisés
#define MAX_CHAR_ETIQUETTE 9 // Constantes pour la taille des étiquettes et le nombre d'étiquettes
#define MAX_CHAR_ETIQUETTE 9 //CF doc ENEDIS
#define NB_ETIQUETTE 45 #define NB_ETIQUETTE 45
// Structure pour stocker les détails d'un groupe TIC
struct GroupDetail { struct GroupDetail {
String name; String name; // Nom de l'étiquette
String value; String value; // Valeur associée à l'étiquette
String horodate; String horodate; // Horodatage de la valeur
}; };
// Structure pour les bits de statut du registre
struct RegistreStatusBits { struct RegistreStatusBits {
uint32_t contactsec : 1; //bit 0 uint32_t contactsec : 1; //bit 0 - État du contact sec
uint32_t organeCoupure : 3; //bit 1 - 3 uint32_t organeCoupure : 3; //bit 1 / 3 - État de l'organe de coupure
uint32_t cache : 1; //bit 4 uint32_t cache : 1; //bit 4 - Cache
uint32_t : 1; //bit 5 uint32_t : 1; //bit 5
uint32_t surtension : 1; //bit 6 uint32_t surtension : 1; //bit 6 - Indicateur de surtension
uint32_t depassementPuissance : 1; //bit 7----- uint32_t depassementPuissance : 1; //bit 7 - Indicateur de dépassement de puissance
uint32_t consoProd : 1; //bit 8 uint32_t consoProd : 1; //bit 8 - Indicateur de consommation/production
uint32_t senseActiveEnergy : 1; //bit 9 uint32_t senseActiveEnergy : 1; //bit 9 - Sens de l'énergie active
uint32_t tarifIndexConso : 4; //bit 10 -13 uint32_t tarifIndexConso : 4; //bit 10/13 - Index tarifaire de consommation
uint32_t tarifIndexProd : 2; //bit 14 - 15 ----- uint32_t tarifIndexProd : 2; //bit 14/15 - Index tarifaire de production
uint32_t horlogeState : 1; //bit 16 uint32_t horlogeState : 1; //bit 16 - État de l'horloge
uint32_t ticState : 1; //17 uint32_t ticState : 1; //bit 17 - État du TIC
uint32_t : 1; //18 uint32_t : 1; //bit 18
uint32_t comEuridis : 2; //19-20 uint32_t comEuridis : 2; //bit 19/20 - Communication Euridis
uint32_t cplState : 2; //21-22 uint32_t cplState : 2; //bit 21/22 - État du CPL
uint32_t cplSynchro : 1; //23 uint32_t cplSynchro : 1; //bit 23 - Synchronisation CPL
uint32_t tempo : 2; //24-25 uint32_t tempo : 2; //bit 24/25 - Couleur du jour Tempo
uint32_t tempoNextDay : 2; //26-27 uint32_t tempoNextDay : 2; //bit 26/27 - Couleur du jour Tempo suivant
uint32_t preavisPM : 2; //28-29 uint32_t preavisPM : 2; //bit 28/29 - Préavis pointe mobile
uint32_t PM : 2; //30-31 uint32_t PM : 2; //bit 30/31 - Pointe mobile
}; };
// Structure pour les bits de statut des relais
struct RelaisStatusBits { struct RelaisStatusBits {
uint8_t relaisSec : 1; //bit 1 uint8_t relaisSec : 1; //bit 1 - État du relais sec
uint8_t relais1 : 1; //bit 2 uint8_t relais1 : 1; //bit 2 - État du relais 1
uint8_t relais2 : 1; //bit 3 uint8_t relais2 : 1; //bit 3
uint8_t relais3 : 1; //bit 4 uint8_t relais3 : 1; //bit 4
uint8_t relais4 : 1; //bit 5 uint8_t relais4 : 1; //bit 5
uint8_t relais5 : 1; //bit 6----- uint8_t relais5 : 1; //bit 6
uint8_t relais6 : 1; //bit 7----- uint8_t relais6 : 1; //bit 7
uint8_t relais7 : 1; //bit 8----- uint8_t relais7 : 1; //bit 8 - État du relais 7
}; };
// Structure pour les bits d'actions du calendrier
struct ActionsCalendrierBits { struct ActionsCalendrierBits {
uint8_t index : 4; //bit 3 - 0----- uint8_t index : 4; // Index de l'action
uint8_t relais1 : 1; //bit 4----- uint8_t relais1 : 1; //bit 4 - État du relais 1
uint8_t relais2 : 1; //bit 8----- uint8_t relais2 : 1; //bit 8-----
uint8_t relais3 : 1; //bit 8----- uint8_t relais3 : 1; //bit 8-----
uint8_t relais4 : 1; //bit 8----- uint8_t relais4 : 1; //bit 8-----
uint8_t relais5 : 1; //bit 7----- uint8_t relais5 : 1; //bit 7-----
uint8_t relais6 : 1; //bit 6----- uint8_t relais6 : 1; //bit 6-----
uint8_t relais7 : 1; //bit 10 uint8_t relais7 : 1; //bit 10 - État du relais 7
uint8_t : 3; //bit 11-13 uint8_t : 3; //bit 11-13
uint16_t relaisSec : 2; //Contact Sec bit 15-14 uint16_t relaisSec : 2; ///État du relais sec
}; };
// Union pour gérer les actions du calendrier
union ActionCalendrier { union ActionCalendrier {
uint16_t ui; uint16_t ui;
ActionsCalendrierBits bits; ActionsCalendrierBits bits;
}; };
// Structure pour une action programmée
struct Action { struct Action {
char startTime[4]; char startTime[4]; // Heure de début de l'action
ActionCalendrier action; ActionCalendrier action; // Action associée
}; };
// Union pour gérer le statut des relais
union RelaisStatus { union RelaisStatus {
uint8_t ui; uint8_t ui;
RelaisStatusBits bits; RelaisStatusBits bits;
}; };
// Union pour gérer le statut du registre
union RegistreStatus { union RegistreStatus {
uint32_t uli; uint32_t uli;
RegistreStatusBits bits; RegistreStatusBits bits;
}; };
const static String SelectedEtiquette[NB_ETIQUETTE] = {
"ADSC", // Adresse du compteur
"DATE", // Date et heure courantes
"NGTF", // Numéro de gestionnaire de réseau de transport
"LTARF", // Libellé du tarif en cours
"EAST", // Énergie active soutirée totale
"EASF01", "EASF02", "EASF03", "EASF04", // Énergie active soutirée par période tarifaire
"EASD01", "EASD02", "EASD03", "EASD04", // Énergie active soutirée par période tarifaire (distribuée)
"EAIT", // Énergie active injectée totale
"ERQ1", "ERQ2", "ERQ3", "ERQ4", // Énergie réactive par quadrant
"IRMS1", "IRMS2", "IRMS3", // Courant efficace par phase
"URMS1", "URMS2", "URMS3", // Tension efficace par phase
"PCOUP", // Puissance de coupure
"SINSTS", "SINSTS1", "SINSTS2", "SINSTS3", // Puissance apparente soutirée totale et par phase
"SINSTI", // Puissance apparente injectée
"STGE", // État du registre de statuts
"DPM1", "FPM1", "DPM2", "FPM2", "DPM3", "FPM3", // Début et fin de pointe mobile
"RELAIS", // État des relais
"NTARF", // Nom du tarif en cours
"NJOURF", "NJOURF+1", "PJOURF+1", // Couleur du jour et du lendemain
"MSG1", "MSG2", // Messages d'information
"PPOINTE" // Préavis de pointe mobile
};
// Tableau pour stocker les valeurs des groupes TIC identifiés
const static String SelectedEtiquette[NB_ETIQUETTE] = { "ADSC", "DATE",
"NGTF", "LTARF",
"EAST", "EASF01", "EASF02", "EASF03", "EASF04",
"EASD01", "EASD02", "EASD03", "EASD04",
"EAIT", "ERQ1", "ERQ2", "ERQ3","ERQ4",
"IRMS1", "IRMS2", "IRMS3",
"URMS1", "URMS2", "URMS3",
"PCOUP",
"SINSTS", "SINSTS1", "SINSTS2", "SINSTS3",
"SINSTI",
"STGE", "DPM1", "FPM1", "DPM2", "FPM2", "DPM3", "FPM3",
"RELAIS", "NTARF", "NJOURF", "NJOURF+1", "PJOURF+1",
"MSG1", "MSG2",
"PPOINTE" };
extern struct GroupDetail TicValues[NB_ETIQUETTE]; //records the values of the identified group extern struct GroupDetail TicValues[NB_ETIQUETTE]; //records the values of the identified group
//Constantes pour definition des statuts // Constantes pour la définition des statuts
const static String kContactStatus[2] = { "closed", "open" }; //relais et contacteurs const static String kContactStatus[2] = { "closed", "open" }; //relais et contacteurs
const static String kCoupure[7] = { "closed", const static String kCoupure[7] = { "closed",
"open_overpower", "open_overpower",
@ -130,6 +152,7 @@ const static String kCplSynchro[2] = { "not_synchro", "synchro" };
const static String kTempoColor[4] = { "no", "blue", "white", "red" }; const static String kTempoColor[4] = { "no", "blue", "white", "red" };
const static String kPointeMobile[4] = { "no", "PM1", "PM2", "PM3" }; const static String kPointeMobile[4] = { "no", "PM1", "PM2", "PM3" };
// Prototypes des fonctions pour lire les données TIC et les convertir en JSON
void readTicPort(); void readTicPort();
String ticValuesAsJson(); String ticValuesAsJson();
String ticBasicValuesAsJson(); String ticBasicValuesAsJson();