Présentation

tic2json est un logiciel permettant de convertir les trames TIC (Télé-Information Client) en provenance d’un compteur ENEDIS électronique communicant (par exemple le Linky ou les compteurs Bleus électroniques) en un format plus communément utilisé: le JSON, ce qui permet notamment d’intégrer facilement ces données dans une base de données.

Note: This project is documented in French because to the best of my knowledge, the TIC protocol on which this project is based is only found in electric meters in France. Should evidence to the contrary arise, I will happily translate this page to English :)

tic2json est capable de traiter toutes les données de la TIC version “02”, dite “mode standard” apparue avec les compteurs Linky et définie dans le document de référence Enedis-NOI-CPT_54E. Le support de la TIC version “01”, dite mode “historique” et définie dans le document de référence Enedis-NOI-CPT_02E, est également implémenté pour certains compteurs (dont le Linky et les compteurs Bleus).

(Pour l’interfaçage matériel avec la sortie TIC d’un compteur électronique, voir TIC2UART).

Licence

GPLv2

Fonctionnalités

Plusieurs modes de fonctionnement sont offerts par le programme, permettant notamment:

  • le choix du format de sortie des trames: liste ou dictionnaire
  • l’émission d’une balise d’identification fixe
  • l’émission des descriptions longues et des unités de chaque groupe de données
  • l’interprétation des horodates en format RFC3339
  • la décimation des trames émises (une toutes les N au choix)
  • le masquage des valeurs à zéro
  • le filtrage des groupes émis sur la base d’un simple fichier de configuration
  • le décodage du registre de statut

Ce programme se veut le compagnon de l’interface matérielle permettant de connecter la sortie TIC d’un compteur électronique à un port série, décrite ici, mais il est compatible avec toute source de trames TIC brutes.

Le compteur Linky émet un peu moins d’une trame par seconde, ce qui permet une bonne résolution des mesures.

Compteurs supportés

  • Linky (mode standard et historique)
  • Bleu (CBEMM, CBEMM ICC, CBETM)
  • Concentrateur de téléreport

Le programme ne supporte actuellement pas les autres compteurs (Jaune, Emeraude, PME-PMI ou Saphir), mais il est toujours possible de me contacter pour en parler :-)

Performances

Ce logiciel est conçu avec un double but de performance maximale et d’empreinte mémoire minimale, afin notamment de pouvoir envisager son utilisation sur des microcontrôleurs.

Ainsi, sur un système Linux standard en traitement d’un flux TIC émis par un compteur Linky, une analyse avec l’outil Massif montre que l’utilisation de la pile (stack) est de l’ordre de 10Ko et celle du tas (heap) de 8Ko (principalement due aux buffers d’entrée et de sortie stdio, 4Ko chacun: l’utilisation propre à l’analyseur lui-même est de l’ordre de quelques centaines d’octets), et ce quelque soit la durée d’utilisation (le programme est prévu pour fonctionner en continu en traitant les trames TIC au fur et à mesure de leur arrivée).

À titre de comparaison, un simple programme “Hello World” utilise dans les mêmes conditions un maximum de 7Ko de pile et 1Ko de tas.

Ces performances sont atteintes grâce à l’utilisation de l’analyseur lexical Flex et de l’analyseur syntaxique Bison. En effet, le document de référence Enedis définit un format de données propre à l’expression d’une grammaire simple, qui ne pose aucune difficulté à ces deux outils.

Plateformes embarquées

Grâce à ces performances, le code fonctionne par exemple parfaitement sur un ESP8266. Un programme complet est disponible pour cette plateforme ainsi que pour ESP32 : il permet d’envoyer les informations TIC sous forme JSON à un serveur, via UDP.

De plus des bouts de programmes pour Raspberry Pi Pico et ARMbed sont inclus dans les sources du projet.

Adaptabilité

Grâce à l’utilisation de purs analyseurs et à une séparation claire de la génération du flux de sortie, il est tout à fait possible d’implémenter une sortie dans d’autres formats que le JSON (par exemple CSV, XML, voire même SQL, etc.) avec un minimum de changements. Me contacter si besoin :)

Téléchargement

Le logiciel est disponible dans le répertoire Git suivant: tic2json.git

Il ne nécessite que Flex et Bison ainsi qu’un compilateur C.

Utilisation

Le programme reçoit en entrée un flux continu de trames TIC, valides ou non, et émet en sortie une série d’objets racines JSON (un par trame) contenant les groupe de données de chaque trame, filtrés le cas échéant.

Le programme vérifie la somme de contrôle (checksum) de chaque groupe de donnée, telle que spécifiée dans le document de référence, en cas d’erreur, le groupe n’est pas émis. En cas d’erreur sur la trame elle-même, un objet JSON est toujours émis, mais il peut être vide. Les messages d’erreur sont émis sur stderr, les objets JSON sur stdout. Les objets JSON émis sont toujours valides, y compris en cas d’erreur.

Dans le fonctionnement standard, chaque objet JSON racine est séparé du suivant par un retour à la ligne.

Note: le document de référence spécifiant la sortie TIC des compteurs antérieurs au Linky, disponible ici, impose notamment qu’une trame qui comporte des erreurs (par exemple des erreurs de somme de contrôle) doit être complètement ignorée. Pour des questions de performance (et notamment d’utilisation mémoire), tic2json émet tous les groupes de données valides d’une trame, que la trame comporte des erreurs ou non. Seuls les groupes invalides (par ex. erreur de somme de contrôle) sont ignorés. En mode sortie dictionnaire, le programme ajoute un champ pour indiquer le statut global de la trame, qui peut être utilisé pour déterminer le traitement à appliquer aux trames reçues.

Exemples

Voici quelques exemples d’utilisation (les données d’identification ont été mises à zéro).

Utilisation de base, TIC standard en entrée, liste JSON en sortie:

./tic2json -2 < /dev/ttyS0 2>/dev/null
[]
[ { "label": "ADSC", "data": "000000000000"},{ "label": "VTIC", "data": "02"},{ "label": "DATE", "data": "", "horodate": "E210813125502"},{ "label": "NGTF", "data": "      BASE      "},{ "label": "LTARF", "data": "      BASE      "},{ "label": "EAST", "data": 413507},{ "label": "EASF01", "data": 413507},{ "label": "EASF02", "data": 0},{ "label": "EASF03", "data": 0},{ "label": "EASF04", "data": 0},{ "label": "EASF05", "data": 0},{ "label": "EASF06", "data": 0},{ "label": "EASF07", "data": 0},{ "label": "EASF08", "data": 0},{ "label": "EASF09", "data": 0},{ "label": "EASF10", "data": 0},{ "label": "EASD01", "data": 413508},{ "label": "EASD02", "data": 0},{ "label": "EASD03", "data": 0},{ "label": "EASD04", "data": 0},{ "label": "IRMS1", "data": 11},{ "label": "URMS1", "data": 238},{ "label": "PREF", "data": 9},{ "label": "PCOUP", "data": 9},{ "label": "SINSTS", "data": 2734},{ "label": "SMAXSN", "data": 4120, "horodate": "E210813123903"},{ "label": "SMAXSN-1", "data": 6200, "horodate": "E210812215155"},{ "label": "UMOY1", "data": 238, "horodate": "E210813125000"},{ "label": "STGE", "data": 2752513},{ "label": "MSG1", "data": "     PAS DE          MESSAGE    "},{ "label": "PRM", "data": "00000000000000"},{ "label": "NTARF", "data": 1},{ "label": "NJOURF", "data": 0},{ "label": "NJOURF+1", "data": 0},{ "label": "PJOURF+1", "data": "00008001 NONUTILE NONUTILE NONUTILE NONUTILE NONUTILE NONUTILE NONUTILE NONUTILE NONUTILE NONUTILE"}]
...

Autre affichage plus “lisible” (retours à la ligne après chaque groupe, dates longues, groupes à zéro masqués, description des groupes, ajout d’une balise d’identification), dictionnaire JSON en sortie, avec un filtrage basé sur le fichier filterconfig suivant:

#ticfilter
EAST
EASF01 EASF02
IRMS1 URMS1 UMOY1
SINSTS
SMAXSN
MSG1
./tic2json -2 -dlnrz -e filterconfig -i mon_compteur < /dev/ttyS0 2>/dev/null
{  "_tvalide": 0}
{  "EAST": { "data": 413507, "desc": "Energie active soutirée totale", "unit": "Wh", "id": "mon_compteur"}
, "EASF01": { "data": 413507, "desc": "Energie active soutirée Fournisseur, index 01", "unit": "Wh", "id": "mon_compteur"}
, "IRMS1": { "data": 11, "desc": "Courant efficace, phase 1", "unit": "A", "id": "mon_compteur"}
, "URMS1": { "data": 238, "desc": "Tension efficace, phase 1", "unit": "V", "id": "mon_compteur"}
, "SINSTS": { "data": 2734, "desc": "Puissance app. Instantannée soutirée", "unit": "VA", "id": "mon_compteur"}
, "SMAXSN": { "data": 4120, "horodate": "2021-08-13T12:39:03+02:00", "desc": "Puissance app. max soutirée n", "unit": "VA", "id": "mon_compteur"}
, "UMOY1": { "data": 238, "horodate": "2021-08-13T12:50:00+02:00", "desc": "Tension moy. ph. 1", "unit": "V", "id": "mon_compteur"}
, "MSG1": { "data": "     PAS DE          MESSAGE    ", "desc": "Message court", "unit": "", "id": "mon_compteur"}
, "_tvalide": 1}
...

Autre exemple avec de la TIC canal historique:

./tic2json -1 -n  < /dev/ttyS0 2>/dev/null
[]
[ { "label": "ADCO", "data": "000000000000"}
,{ "label": "OPTARIF", "data": "BASE"}
,{ "label": "ISOUSC", "data": 75}
,{ "label": "BASE", "data": 13205837}
,{ "label": "PTEC", "data": "TH.."}
,{ "label": "IINST", "data": 12}
,{ "label": "IMAX", "data": 90}
,{ "label": "PAPP", "data": 2790}
,{ "label": "HHPHC", "data": "A"}
,{ "label": "MOTDETAT", "data": "000000"}
]

Puisqu’on a du pur JSON en sortie, on peut envoyer les données TIC sur un broker MQTT, ou un script Python, etc. Les possibilités sont infinies.

Dashboard Grafana

On peut utiliser la sortie de tic2json pour alimenter une base de donnée avec les données TIC et produire ainsi des graphiques permettant de suivre la consommation électrique, exemple:

grafana preview