Catégories
Recherche technologique

La Télé Information Client (TIC)

1. Introduction

Aujourd’hui, nous allons parler d’une technologie présente dans un grand nombre de foyer français et pourtant méconnue du grand public. Cette technologie est la Télé Information Client, présente depuis les années 70 mais qui connait un réel intérêt pour les projets IOT seulement depuis les années 2000 avec de nouvelles capacités introduite en même temps que le compteur électrique le plus connu, le compteur Linky.

2.1 Histoire de la Télé Information Client

La télé-information client, qui sera appelée « TIC » désormais, est un bus d’information mis en place par Enedis dans ses différents compteurs électriques. Il existe plusieurs types de compteurs électriques équipés de cette technologie avec une différence majeure :

  • Les compteurs dits « domestiques » avec une puissance de raccordement jusqu’à 36 kVA (Compteur Linky et CBE)
  • Les compteurs dits « industriels » avec une puissance de raccordement supérieure à 36 kVA (Compteur PME-PMI, Saphir)

Cette technologie a été introduite dès les années 1970 dans le réseau électrique français avec un objectif précis : aider les fournisseurs électriques à avoir un suivi de consommation de leurs clients sans avoir besoin de se déplacer jusqu’aux compteurs physiques. Ainsi, les fournisseurs pouvaient proposer des contrats plus adaptés aux besoins des clients et cela a permis l’introduction des contrats à heures pleines et creuses.  Cependant, le client n’avait pas accès aux informations car la communication des données était unidirectionnelle et réservée aux fournisseurs.

Dans les années 90, les compteurs électriques sont peu à peu remplacés par de nouveaux types de compteurs. Ces nouveaux compteurs introduisent plus d’électronique et ainsi, plusieurs améliorations de la TIC sont notables. La première concerne la qualité de transmission. En effet, avec ces nouveaux compteurs, les informations vont être transmises de manière plus fiable et plus rapide. La seconde, et celle qui a permis à Elyxoft de travailler sur ce sujet, est la possibilité pour le client d’accéder à ces informations transmises et avoir un suivi en temps réel de sa consommation électrique, à condition de traiter les informations correctement. Ce mode de fonctionnement est appelé mode de fonctionnement « historique », un point important pour la suite.

À partir de 2015, un nouveau compteur fait son apparition. Ce compteur est célèbre par son nom : « Linky ».

Ce nouveau type de compteur, moderne par son design mécanique, mais aussi électronique, permet aux clients d’avoir des informations visuelles, d’améliorer le suivi de consommation et d’introduire un nouveau mode de fonctionnement pour la TIC, le mode de fonctionnement « standard ». Ces nouveaux compteurs peuvent fonctionner avec les deux modes de fonctionnement et il est important de noter que par défaut, les compteurs sont initialisés en mode historique (en sortie d’usine) et il faut une demande auprès d’Enedis pour réaliser le changement entre les deux modes de fonctionnement. Aujourd’hui, ce type de compteur nous permet d’avoir un suivi de notre consommation électrique sur internet. Cependant, ce suivi est limité car il ne concerne que la consommation de la veille, avec un pas de 30 minutes, ainsi il est possible d’avoir raté certaines informations de consommation (par exemple, une machine qui consommerait beaucoup d’énergie à un intervalle de temps x, ne serait pas détectée par le pas de 30 minutes car sa mise en route et sa mise en veille est inférieure à 30 minutes).

2.2 Scénario d’utilisation

Ainsi, la TIC peut avoir de nombreux avantages pour des types d’utilisateurs très différents (allant du particulier à l’entreprise). Voici quelques scénarios que nous avons imaginés :

  • Un consommateur qui cherche à avoir un suivi régulier

Ce type de consommateur est le consommateur classique qui cherche seulement à avoir un retour sur sa consommation électrique, avec une possibilité de temps réel, mais aussi de faire un suivi dans le temps. Ainsi, le consommateur pourrait étudier sa consommation électrique et décider de faire des choses basiques pour la diminuer (par exemple débrancher certains appareils qui consommerait passivement de l’énergie)

  • Une entreprise qui a besoin d’un suivi énergétique de ses clients

Une entreprise qui est dans le domaine de l’énergie (par exemple, l’installation de panneaux solaires) peut avoir un intérêt à évaluer de manière simple et sur une période donnée la consommation électrique de ses clients. Ainsi, ils pourraient montrer à leurs clients leur consommation réelle, et comparer avec les services qu’ils proposent et donc convaincre plus facilement leurs clients (par exemple en montrant la rentabilité d’une installation de panneaux solaires en fonction de leur consommation).

  • Un particulier bricoleur qui cherche à optimiser sa maison

Certains particuliers adorent bricoler des mécanismes divers. On pourrait alors imaginer qu’un bricoleur décide d’utiliser les informations de sa consommation électrique pour créer un système capable de gérer sa pompe à chaleur, ou bien même de l’optimiser pendant les heures creuses et ce, de manière automatique.

Ainsi, dès qu’un particulier ou une organisation nécessite un suivi de sa consommation (pour mieux la gérer, l’étudier ou réaliser des actions diverses), la TIC est la solution à ces problématiques d’obtention d’informations en temps réel.

2.3 Fonctionnement de la TIC

Comme nous l’avons vu précédemment, il existe deux modes de fonctionnement avec la TIC : le mode historique et le mode standard. Nous allons alors séparer cette partie en 3 parties.

Dans un premier temps, nous allons voir les caractéristiques générales de fonctionnement de la TIC. Dans un second temps, on s’intéressera en détail au mode historique et nous terminerons cette partie avec le mode standard.

2.3.1 Caractéristique générale de la TIC

La sortie télé information du compteur Linky se situe sur le côté droit du compteur, une fois le capot démonté (voir image ci-dessous) pour un compteur monophasé.

Pour les compteurs triphasé, la sortie se trouve sur le haut à droite de votre Linky :

Cette sortie est constituée de 3 pins : I1, I2 et A et, à l’aide de 2 combinaisons, permet 2 actions : l’alimentation et la transmission d’informations.

Le circuit d’alimentation est un circuit capable de délivrer une puissance de 130 mW, 6V efficace (± 10 % à 50 kHz). Ce circuit permet donc d’autoalimenter des dispositifs qui ne demandent pas beaucoup de puissance pour le bon fonctionnement.

Le second circuit, le circuit d’information est le circuit qui nous intéresse. On obtient les informations via les pins I1 et I2 à l’aide d’un signal binaire à 50kHz (± 3 %) avec une logique binaire négative (c’est-à-dire que si la porteuse est présente, le signal est nul).

Les informations contenues dans la TIC sont appelées étiquettes. Il est important de noter cela pour la suite de cet article.

Désormais, nous allons rentrer plus en détail dans le fonctionnement de chacun des modes de communication ainsi que de comment récupérer ces informations.

2.3.2 Le mode « Historique »

Le mode historique est le mode le plus répandu, étant donné qu’il est plus ancien mais est aussi le mode par défaut des compteurs électriques aujourd’hui.

Les informations retransmises par le mode historique sont au nombre de 23 en monophasé et 28 en triphasé.

Ces différentes informations peuvent être vues, du point de vue du microcontrôleur, comme une liaison série avec les paramètres suivants :

  • 1 bit de start
  • 7 bits de données
  • 1 bit de parité (paire)
  • 1 bit de stop
  • Vitesse fixe à 1200 bauds

Ces informations sont obtenues sans horodatage, ainsi si l’on souhaite faire un suivi dans le temps de la consommation dans ce mode de fonctionnement, il faut ajouter, lors du traitement des informations, un paramètre temporel.

Pour récupérer les informations de la TIC d’un point de vue électronique, nous utiliserons le montage que Charles a réalisé et qui est très bien expliqué dans cet article.

Une fois les informations de la TIC reçues, il faut savoir quelles sont ces informations, vous trouverez un descriptif de toutes les informations reçus dans la note d’Enedis (Enedis-NOI-CPT_54E, lien pour la télécharger) partie 6.1.1 (monophasé) et 6.1.2 (triphasé).

Voici un exemple d’étiquette que l’on peut obtenir, ADCO, correspondant à un identifiant unique pour notre compteur afin d’expliquer chaque élément :

La première colonne nous décrit l’information contenue dans l’étiquette, la seconde correspond à l’étiquette (ce sont les lettres que nous recevrons de la TIC, indiquant quelle est l’information reçue), le nombre de caractères qui seront contenus dans la TIC, l’unité (non transmise mais utile si l’on veut faire du traitement des données) et si l’information est transmise ou non en fonction du contrat.

Maintenant que nous avons connaissance de toutes ces informations et de comment elles sont structurées, quel est le protocole mis en place par Enedis pour recevoir ces informations ?

Voici la structure de ce protocole :

 La première chose que nous pouvons noter est la présence de 2 caractères très importants, 0x02 (ST) et 0x03 (ET), respectivement au début et à la fin de la trame, permettant d’indiquer le début et la fin d’une trame dans le cas d’une réception de plusieurs trames.

La seconde chose importante est que les groupes d’information correspondent aux informations contenues dans les différentes étiquettes. Nous verrons par la suite comment il se structure, mais il contient l’étiquette ainsi que sa valeur. Chaque groupe d’information est présent ou non dans la trame en fonction du type de contrat souscrit, cependant l’ordre sera toujours le même. En effet, on recevra toujours les groupes d’information dans l’ordre croissant par rapport au tableau fourni par Enedis dans la note « Enedis-NOI-CPT_54E ». Ainsi, ADCO sera toujours présente dans le groupe d’information 1, puis on aura dans le groupe d’information 2 la deuxième étiquette présente dans notre contrat.

Maintenant, regardons comment est structuré un groupe d’information :

On voit donc plusieurs éléments pour former un seul groupe d’information :

  • Le caractère <LF> (0x0A) permettant d’indiquer le début du groupe
  • L’étiquette (codée en hexadécimal ASCII, par exemple PAPP devient 50415050)
  • Un caractère indiquant la fin de l’étiquette (appelé séparateur) <SP> (0x20)
  • La donnée dont la taille est précisée via la note d’Enedis
  • Un second caractère séparateur indique la fin de la donnée (<SP> = 0x20)
  • Un octet de checksum
  • Le caractère <CR> (0x0D) indiquant la fin du groupe d’information

Il est important de noter que les octets présents dans les champs « étiquette » et « donnée » ne peuvent pas contenir les différents caractères spécifiques (0x0A, 0x20, 0x0D).

Enfin, le checksum est calculé de la manière suivante :

  • On ajoute les octets des champs « étiquette », « donnée » et le séparateur entre ces deux champs
  • On ne conserve que l’octet de poids faible de cette somme
  • On ne conserve que les six bits de poids faibles de cet octet
  • On ajoute 0x20 à ce résultat

En résumé : ((Étiquette+Séparateur+Donnée) ET 0x3F) + 0x20

Le checksum sera toujours un caractère ASCII imprimable (compris entre 0x20 et 0x7E).

Maintenant que nous avons toutes les informations sur le mode historique, nous allons voir les différences avec le mode standard.

2.3.3 Le mode « Standard »

Le mode standard contient, comme dit lors de l’introduction de la TIC, plus d’informations avec un total de 59 étiquettes en monophasé et 74 en triphasé.

Nous pouvons toujours utiliser le montage électronique précédent permettant de récupérer les informations de la TIC, la principale différence est la vitesse de communication, étant augmentée à 9600 bauds.

Pour savoir quels sont les groupes d’information qui sont transmis, vous les trouverez dans la note d’Enedis (Enedis-NOI-CPT_54E, lien pour la télécharger) dans la partie 6.2.2 Liste des données restituées.

Attention cependant, certaines des données présentes dans ce tableau ne sont disponibles que pour le triphasé (il n’y a pas deux tableaux séparés comme pour le mode historique) et certaines informations ne sont disponibles que pour les gens capables de produire de l’électricité (installation de panneaux solaires par exemple).

En ce qui concerne la structure du protocole, elle est similaire à la structure du mode historique (voir paragraphe 2.3.2 Le mode « Historique ») mais il existe une différence majeure dans la réflexion de transmission. En effet, chacune des étiquettes concernant la partie « énergie » sont transmises, et cela même si elles ne sont pas utilisées dans le contrat en cours (ce qui n’était pas le cas en historique).

La principale différence entre les deux modes est située dans la construction des groupes d’information. En effet, il y a deux types de groupes d’information : les groupes horodatés et les groupes non horodatés.

Commençons par le groupe non horodaté, similaire aux groupes du mode historique :

  • Début du groupe avec 0x0A (<LF>)
  • Etiquette
  • Séparateur de champ avec 0x09 (<HT>)
  • Donnée
  • Checksum
  • Fin du groupe avec 0x0D (<CR>)

Ainsi, la première différence entre le mode standard et historique, pour une trame non horodatée est la valeur des séparateurs qui passe de 0x20 à 0x09. La seconde différence est dans le calcul du checksum ou nous ajoutons le second caractère d’échappement à la somme des octets (on obtient alors Etiquette+Séparateur1+Donnée+Séparateur2).

Désormais, passons aux groupes qui contiennent une horodate :

On retrouve une structure similaire avec les groupes non horodatés, mais l’on voit l’ajout de l’horodatage. Pour ce faire, on ajoute l’horodate ainsi qu’un nouveau séparateur de champ, à la suite du séparateur de champ entre la donnée et l’étiquette.

 En ce qui concerne l’horodate, nous allons le détailler avec un exemple fourni par Enedis : le 25 décembre 2008 à 22h 35min 18s. Cette date sera écrite de la manière suivante dans le champ correspondant : H081225223518. Voici la signification de chacun des chiffres et lettres présents ici :

Seul paramètre complexe, la saison correspond au type d’heure en cours (E pour été, H pour hiver). Il peut arriver que ce caractère soit en minuscule dans certains cas où l’horodate a été obtenue avec une horloge temps réel dégradé. Un autre cas spécifique est la présence du caractère <Espace> (0x20 en hexadécimal) dans ce même paramètre.

Une autre différence est le calcul du checksum. En effet, ayant rajouté un nouveau champ de donnée, le calcul se retrouve modifié et on rajoute ce nouveau champ ainsi que le caractère séparateur dans la somme. On réalise alors la somme de tous les octets des champs étiquettes, horodate et donnée ainsi que les deux caractères séparateurs (on ajoute 2 fois 0x09). La suite du calcul reste le même : ((Étiquette+Séparateur1+Horodate+Séparateur2+Donnée+Séparateur3) ET 0x3F) + 0x20

3. Conclusion

En conclusion, cette technologie n’est pas encore très utilisée mais possède des capacités très intéressantes et ce, pour différents types de profil. Avec cet article, vous en aurez appris plus sur le fonctionnement de la TIC et comment utiliser les informations contenues dedans.

Si vous souhaitez aller plus loin, Elyxoft a réalisé un projet « ElyTicBridge » et un article est disponible juste ici.

Références

Divers documents Enedis :

Enedis-NOI-CPT_54E (attention téléchargement de PDF)

Enedis-NOI-CPT_02E (attention téléchargement de PDF)

Différents travaux de Jerome Ferrari :

Jerome Ferrari · GitLab (univ-grenoble-alpes.fr)

Winky

LoKy

Eky

Différents travaux de Charles :

Démystifier le décodage Téléinformation et l’optocoupleur SFH620 – Charles’s Blog (hallard.me)

https://github.com/hallard

Le module PiTinfo V1.2 devient plus light – Charles’s Blog (hallard.me)

Charles’s Blog – I now got a small place on the web (hallard.me)

Fiche SéQuelec numéro 17 publié par Capeb

Catégories
Recherche technologique

Raspberry Pi – Un outils pour surveiller vos développement IoT

Lorsque l’on développe un objet communicant arrive le moment ou l’on souhaite s’assurer de son fonctionnement dans le temps, cet article va vous présenter comment configurer en quelques heures un Raspberry Pi pour répondre à ce besoin.

Pour cela nous allons commencer par faire un capteur température/pression/humidité Wi-Fi sur base STM32, puis nous utiliserons le Raspberry Pi en point d’accès Wi-Fi avec les outils nécessaire pour surveiller l’évolution des valeurs sous forme de courbes.

Capteur Wi-Fi – STM32F401

Ce capteur est basé sur la carte RUSHUP Cloud-JAM qui est le condensé des carte NUCLEO-F401RE, X-NUCLEO-IDW01M1, X-NUCLEO-IKS01A2 et X-NUCLEO-NFC01A1. Nous allons modifier le code exemple pour n’utiliser que la partie W-Fi et capteurs de température/pression/humidité le tout connecté à un broker MQTT local.

Le capteur publiera toute les minutes sur un broker MQTT ses valeurs de température, humidité et pression.

https://raw.githubusercontent.com/rushup/Cloud-JAM/master/JAM-TOP.jpg

Liens site officiel

Étapes

  • Installation de l’environnement de développement de STMicroélectronic STM32CubeIDE
    • https://www.st.com/en/development-tools/stm32cubeide.html
  • Récupérer les sources modifiée depuis le dépôt gitlab Elyxoft en utilisant la branch « develop »
  • Importer sous STM32CubeIDE, Compiler et charger sur la cible
  • A ce point si l’on se connecte à l’aide d’un terminal série sur le port série virtuel de la carte en 460800bauds on voit apparaître les informations de debug et le fait que la carte ne se connecte pas au Wi-Fi de notre Raspberry Pi, celui-ci n’étant pas configurer …

Monitoring – Raspberry Pi

Plusieurs éléments sont à installer sur le Raspberry Pi, pour cela nous allons suivre quelques guides :

Arrivé à ce point, notre Tag doit être en mesure de ce connecter en Wi-Fi au Raspberry et publier toute les minutes ces information sur le topic « stm32 », il est facile de vérifier cela en regardant les log de la console série, puis en souscrivant au topic stm32 depuis MQTT.fx.

Il faut maintenant créer une base dans infuxdb dans laquelle nous allons enregistrer nos valeurs. Depuis un terminal sur le Raspberry Pi :

pi@raspberrypi:~ $ influx
Connected to http://localhost:8086 version 1.8.0
InfluxDB shell version: 1.8.0
> CREATE DATABASE stm32
> SHOW DATABASES
name: databases
name
----
_internal
stm32

Notre base de donnée est prête à recevoir des enregistrement, on utilise Node-RED pour récupérer les informations qui arrive sur le topic MQTT, les mettre en forme et les envoyé dans la base de donnée.

Description des différents module :

  • mqtt in
  • Fonction de transformation
  • influxdb batch
  • N’oublier pas de « déployer » le flow pour le rendre actif. Les modules de debug permette de vérifier la bonne transformation des messages.

La dernière étape connecté grafana à influxdb et afficher les informations.

La première chose va être de configurer une source de donnée :

Maintenant on va ajouter un dashboard et un premier panel pour la température :

Les deux champs à renseigner sont :

  • « Nucleo » dans la ligne FROM, il doit apparaitre dans une liste déroulante, si ce n’est pas le cas, c’est que la connexion à influxdb n’a pas eu lieu.
  • « temperature » dans la ligne SELECT, même remarque que précédemment.

On ajoute dans le même dashboard deux autre pannel pour la pression et l’humidité et l’on doit obtenir :

Conclusion

Avec très peu d’effort on obtient un outils capable de présenter les résultats de nos capteurs de façon agréable et facilement exploitable.

Catégories
Recherche technologique

openThread et GreenPower – Objectif

L’objectif de cette série d’article est de pouvoir utiliser une commande zigbee green power dans un réseau thread.

Pour cela nous allons réaliser les étapes suivantes :

  • Mise en place d’un environnement de développement openthread pour pouvoir générer des firmwares.
  • Mise en place d’un sniffer permettant de visualiser les communications.
  • Créations d’un réseau thread composé de deux nœuds, un leader et un router.
  • Ajout d’une implémentation minimaliste du protocole applicatif de la zigbee alliance pour réseau IP (ZCLIP, DotDot, CHIP … en fonction du temps et des communications) afin de réaliser une commande et un actionneur on/off avec nos deux produits.
  • Implémentation d’un combo zigbee green power minimaliste dans le firmware de l’actionneur on/off.
  • Idées complémentaire : réalisation d’un proxy minimaliste dans le firmware de la commande on/off, ajout d’un border router, extension de l’app de commissioning thread pour l’ajout de commande green power au réseau, recherche d’un algorithme de maintenance pour l’optimisation des proxy green power à activé en fonctions des niveau rssi reçu par les commandes.

Catégories
Recherche technologique

openThread et GrennPower – Environnement de développement

Comme beaucoup (trop) de développeur, mon pc de travail fonctionne sous Windows 10 or openThread fait appel à des outils Linux pour compiler. Dans la suite je vais vous décrire mon installation qui me permet de modifier le code dans un IDE digne de ce nom, de compiler ce même code comme si j’étais sous Linux et enfin de le programmer dans la cible avec les outils natif à mon os fournit par les fabricants de composants.

Environnement de compilation

Le plus simple est d’utiliser docker, d’autant qu’une image existe déjà !

Installation de docker sous Windows 10

Pas de difficulté particulière si ce n’est qu’il faut une version professionnel de Windows 10. Le lien vers la page de téléchargement de Docker Desktop : https://www.docker.com/get-started

A la fin de l’installation on ouvre une Invite de commandes Windows (pas le powershell) et l’on peut vérifier que docker est en route en lui demandant ça version :

Nous allons ensuite chercher l’image docker qui nous intéresse :

docker pull openthread/environment:latest

Pour faciliter la modification ultérieur des fichier sources depuis un IDE sous Windows nous allons activer le partage de disque sous docker. Pour cela on va dans la partie « Setting/Ressources/File sharing » de docker desktop et l’on sélectionne le disque que l’on souhaite partager :

J’ai créé un répertoire spécifique de partage sur mon disque « c » de Windows à l’emplacement « C:_DEV\docker_share » et pour simplifier la suite il sera monté à l’emplacement « /mnt » de l’image Linux. Il est temps de lancer un container :

docker run -it -v c:/_DEV/docker_share:/mnt --rm openthread/environment bash

Dans ce shell Linux nous allons effectuer un enchainement de commandes :

  • positionnement dans le répertoire partagé :

cd /mnt

  • Clone du repo Git :

git clone https://github.com/openthread/openthread

  • On se place dans le répertoire cloné :

cd openthread

  • Installation de la toolchain et autres dépendances :

./script/bootstrap

  • Setup de l’environnement :

./bootstrap

Nous voilà prêt à effectuer la compilation de notre premier firmware, pour ce préparer à l’étape suivante qui est la mise en place d’un sniffer, nous allons construire le firmware nécessaire pour la carte de développement de chez Nordic « nRF52840-DK » :

make -f examples/Makefile-nrf52840 USB=1

le binaire généré ce trouve dans le répertoire « /mnt/openthread/output/nrf52840/bin » mais a encore besoin d’une transformation pour le mettre au format hex :

arm-none-eabi-objcopy -O ihex output/nrf52840/bin/ot-rcp output/nrf52840/bin/ot-rcp.hex

Il ne reste plus qu’à utiliser nRf Programmer depuis Windows pour charger ce nouveau firmware dans la cible.

Liens utiles

Listes des liens qui ont permis d’obtenir cette synthèse :

Catégories
Recherche technologique

openThread et GreenPower – Mise en place d’un sniffer

Installation de wireshark

Charger l’installeur et l’exécuter : https://www.wireshark.org/#download

Installation de pyspinel

  • Premièrement installer python3 pour Windows et ne pas oublier de cocher l’ajout au PATH :
  • Dans un terminal Windows mettre installer les dépendances utiles et pyspinel :
    • pip3 install pyserial ipaddress pyspinel
  • Ce placer dans un répertoire de travail et cloner le repo openthread/pyspinel :
    • git clone https://github.com/openthread/pyspinel

Lancer la session de sniffing …

On commence par connecter la carte nRF52840 précédemment flashée avec le firmware ot-rcp.hex par l’usb de debug au PC. A l’aide du gestionnaire de périphérique on note le numéro du port série virtuel associé, COM3 dans mon cas.

Le mini réseau thread sera créé sur le canal 15, on décide de placer le sniffer en écoute sur celui-ci par la commande suivante :

python sniffer.py -c 15 -u COM3 --crc --no-reset --rssi | "C:\Program Files\Wireshark\wireshark" -k -i -

La fenêtre de wireshark s’ouvre et l’on peut lire sur notre terminal :

Liens utilisés

Catégories
Recherche technologique

openThread et GreenPower – Réseau à deux noeuds

L’objectif est d’avoir un réseau composé d’un leader et d’un routeur, de pouvoir effectué un ping du routeur sur le leader et une communication de type coap. Pour cela nous allons générer un firmware de type Full Thread Device pilotable grâce à la CLIdepuis un terminal série. Le matériel utilisé sera deux cartes Silicon Labs EFR32MG12.

Générer le firmware

Pour pouvoir générer un firmware Silicon Labs il est nécessaire de charger et installer simplicity studio pour pouvoir récupérer la stack flex contenue dans le sdk 2.7.

Lien vers la page de chargement de simplicity studio : https://www.silabs.com/products/development-tools/software/simplicity-studio

Une fois simplicity studio installé et la stack Flex SDK v2.7 téléchargée, il faut la copier du répertoire ou elle se trouve (C:\SiliconLabs\SimplicityStudio\v4\developer\sdks\gecko_sdk_suite) dans le repertoire third party de la stack openthread (C:_DEV\docker_share\openthread\third_party\silabs\gecko_sdk_suite).

Depuis notre terminal docker :

make -f examples/Makefile-efr32mg12 COMMISSIONER=1 COAP=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 BOARD=BRD4161A

Puis on transforme notre binaire au format hex :

arm-none-eabi-objcopy -O ihex output/efr32mg12/bin/ot-cli-ftd output/efr32mg12/bin/ot-cli-ftd.hex

Depuis Windows on éxecute l’outils Silicon Labs pour charger les firmware dans la cible : commander (C:\SiliconLabs\SimplicityStudio\v4\developer\adapter_packs\commander) et l’on charge nos deux cartes avec notre firmware fraichement compilé.

Construire le réseau et configurer le sniffer

A l’aide du gestionnaire de périphérique on note les ports séries associés à nos cartes. Dans mon cas les port COM11 et COM12. On ouvre un terminal série par port avec la configuration suivante : 115200 8-N-1

Configuration de la première carte (COM11) pour devenir le leader du réseau.

Initialisation d’un dataset et affichage des valeurs :

dataset init new

Le canal radio choisi est le 20, or nous avons configurer notre sniffer pour écouter sur le canal 15, on change la valeur du canal dans notre dataset pour se positionner sur le canal 15.

dataset channel 15

Maintenant que le dataset nous conviens, on le rend actif, puis on démarre l’interface réseau et la stack thread. au bout d’un quelque secondes lorsque l’on demande son état elle nous informe qu’elle est le leader.

Configuration du sniffer

Plusieurs protocoles sont à configurer en utilisant les valeurs fournies par le dataset du leader.

6LoWPAN

CoAP

IEEE 802.15.4

Thread

Configuration de la deuxième carte (COM12) en tant que routeur

Contrairement à la première carte, on ne va renseigner que la « master key » dans le dataset avant d’activer l’interface et de démarrer la stack.

dataset masterkey 9d7ddcb4408be470e2240256feaf9cf2

La trace wireshark de l’entrée réseau :

Test – Ping

Un premier test consiste à envoyer un ping en ICMPv6 du routeur vers le leader et d’observer la transcation :

ping fdad:574c:3814:8442:9c44:e832:a11e:4939

Test – UDP

Le routeur va envoyer un message « hello » en udp sur un port du leader qui aura été ouvert :

Leader – Ouverture du port en écoute

udp open

udp bind :: 1234

Routeur – Envoie du message « hello »

udp open

udp send fdad:574c:3814:8442:9c44:e832:a11e:4939 1234 hello

Leader – Résultat

Wireshark – Trace

Test – CoAP

L’exemple fournit ne pose plus de problème à présent et peut être suivi sans modification : https://github.com/openthread/openthread/blob/master/src/cli/README_COAP.md

Liens

Liste des liens qui ont permis de réaliser cet article :

Catégories
Recherche technologique

openThread et GreenPower – DotDot

Et pourquoi pas CHIP ? (https://www.connectedhomeip.com/) Et bien tout simplement car aucune spécification n’est encore publique actuellement !

Les documents de référence

L’objectif

L’objectif n’est pas d’implémenter l’ensemble du protocole dotdot mais le strict minimum pour passer à l’étape suivante : le pilotage depuis une commande green power. Pour se faire seul la commande toggle du cluster on/off sera implémentée sur le premier endpoint de l’actionneur. Seront donc ignoré :

  • La découverte des produits et des ressources depuis le /.well-known et de fait la gestion de l’UID produit.
  • Tous les mécanismes de sécurité.
  • La gestion du multicast, seul un post unicast sera géré par le serveur.
  • Les bindings et autres tokens

On peut donc résumé que le client enverra un post coap depuis la cli, le serveur quand à implémentera ce post sur la ressource « /zcl/e/1/s6/c/2 » qui correspond a la commande toggle du cluster on/off sur l’enpoint 1 du produit. Commande qui n’a pas besoin de payload.

Le code !

Jusqu’à présent nous avons compilé le projet exemple qui donne accès à une ligne de commande. Pour réaliser notre objectif on définira le leader comme l’actionneur, on ne touchera pas au code du routeur. Pour simplifier l’on va modifier le projet example cli pour :

  • créé automatiquement le réseau en tant que leader
  • activé coap et lui ajouter la ressource zcl/e/1/s6/c/2
  • ajouter un handler sur cette ressource
  • imprimer sur la sortie RTT les informations permettant de s’assurer que toute les étapes ce sont bien passée
  • a chaque fois que le handler de la ressource coap est appelée changé d’état une variable représentant la charge et l’afficher.
void CoAPHandleRequest(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
 {
     static bool light_on_state = false;
     OT_UNUSED_VARIABLE(aContext);
     OT_UNUSED_VARIABLE(aMessage);
     OT_UNUSED_VARIABLE(aMessageInfo);

     otPlatLog(OT_LOG_LEVEL_NONE, 
               OT_LOG_REGION_PLATFORM, 
               "Light %s",light_on_state?"ON":"OFF");
     light_on_state = !light_on_state;
}
int main(int argc, char *argv[])
 {
     otInstance *instance;
...

// configure node as leader
otError lError;

otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "Create new network : dataset ");
otOperationalDataset aDataset;
uint8_t demoMasterKey[OT_MASTER_KEY_SIZE] = {0x9d,0x7d,0xdc,0xb4,0x40,0x8b,0xe4,0x70,0xe2,0x24,0x02,0x56,0xfe,0xaf,0x9c,0xf2};

if( OT_ERROR_NONE == otDatasetCreateNewNetwork(instance, &aDataset) )
{
    otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "SUCCESS, set active ");
    aDataset.mChannel = 15;
    memcpy(aDataset.mMasterKey.m8, demoMasterKey, OT_MASTER_KEY_SIZE);
    if( OT_ERROR_NONE == otDatasetSetActive(instance, &aDataset) )
    {
        otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "SUCCESS, enable IPv6 ");
        if( OT_ERROR_NONE == otIp6SetEnabled(instance, true) )
        {
            otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "SUCCESS, start as leader ");
            lError = otThreadSetEnabled(instance,true);
            if( OT_ERROR_NONE == lError )
            {
                otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "SUCCESS\n");
                const otNetifAddress *unicastAddrs = otIp6GetUnicastAddresses(instance);

                for (const otNetifAddress *addr = unicastAddrs; addr; addr = addr->mNext)
                {
                    otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM,
                        "%x:%x:%x:%x:%x:%x:%x:%x", tSwap16(addr->mAddress.mFields.m16[0]), tSwap16(addr->mAddress.mFields.m16[1]),
                        tSwap16(addr->mAddress.mFields.m16[2]), tSwap16(addr->mAddress.mFields.m16[3]), tSwap16(addr->mAddress.mFields.m16[4]),
                        tSwap16(addr->mAddress.mFields.m16[5]), tSwap16(addr->mAddress.mFields.m16[6]), tSwap16(addr->mAddress.mFields.m16[7]));
                }                    
            }
            else { otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "FAILED %s\n", otThreadErrorToString(lError)); }    
        }
        else { otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "FAILED\n"); }    
    }
    else { otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "FAILED\n"); }    
}
else { otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "FAILED\n"); }    


// Initialize coap service
otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "CoAP start : ");
if( OT_ERROR_NONE == otCoapStart(instance, OT_DEFAULT_COAP_PORT) )
{
    otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "SUCCESS\n");

    // add ressources
    otCoapResource mResource;
    char zclToggleRscr[] = "zcl/e/1/s6/c/2";
    memset(&mResource, 0, sizeof(mResource));
    mResource.mUriPath = zclToggleRscr;
    mResource.mHandler = CoAPHandleRequest;

    otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "CoAP add ressource : ");
    lError = otCoapAddResource(instance, &mResource);
    if( OT_ERROR_NONE == lError )
    {
        otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "SUCCESS\n");
    }
    else { otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "FAILED %s\n", otThreadErrorToString(lError)); }    
}
else
{
    otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "FAILED\n");
}


while (!otSysPseudoResetWasRequested())
{
    ...

return 0;
}

Et le résultat :

Catégories
Recherche technologique

openThread et GreenPower – GreenPower

Pour le dernier article de cette étude, nous allons voir comment recevoir une trame green power issue d’une commande Philips Tap (https://zigbeealliance.org/zigbee_products/philips-hue-tap/) puis la transformer pour la renvoyer sous forme d’un message DotDot.

Méthodologie:

  • L’actionneur sera le leader et conserve le code développé précédemment.
  • Le nœud enfant contiendra le proxy Green Power et sera également modifier pour rejoindre automatiquement le réseau créé par le leader.
  • Pour simplifier l’adresse ipv6 du leader sera codée en dur dans l’enfant pour l’envoie du message CoAP.
  • La sécurité Green Power est ignorée pour cette étude. La trame issue de la commande Philips Tap contien l’ordre en clair.

La trame Green Power

Sans chercher à décoder entièrement la trame Green Power pour notre étude, nous utiliserons les filtres suivants:

  • Au moins 24 octets de long.
  • Le frame control 802.15.4 doit valoir 0x0801.
  • Le frame control du header network doit être de type data et la version du protocole doit être 3 (green power).
  • Le frame counter de la trame reçue doit être supérieur au frame counter de la trame précédente.
  • L’ordre green power doit être 0x22 (Toggle)

Le code du nœud enfant

Rejoindre le réseau et activer le handler de réception 802.15.4 à placer dans la fonction main avant la boucle infinie.

    // enable promiscuous mode for capture Grenn Power Frame
    // set callback
    otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "Set Pcap callback.");
    otLinkSetPcapCallback(instance, &PromicuousHandleLinkPcapReceive, 0);
    
    // join existing network
    // set master key
    memset(&aDataset, 0, sizeof(aDataset));
    memcpy(aDataset.mMasterKey.m8, demoMasterKey, sizeof(aDataset.mMasterKey));
    aDataset.mComponents.mIsMasterKeyPresent = true;
    otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "Commit dataset : ");
    lError = otDatasetSetActive(instance, &aDataset);
    if( OT_ERROR_NONE == lError )
    {
        otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "SUCCESS, enable IPv6 ");
        if( OT_ERROR_NONE == otIp6SetEnabled(instance, true) )
        {
            otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "SUCCESS, start as router ");
            lError = otThreadSetEnabled(instance,true);
            if( OT_ERROR_NONE == lError )
            {
                otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "SUCCESS\n");
                const otNetifAddress *unicastAddrs = otIp6GetUnicastAddresses(instance);

                for (const otNetifAddress *addr = unicastAddrs; addr; addr = addr->mNext)
                {
                    otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM,
                        "%x:%x:%x:%x:%x:%x:%x:%x", tSwap16(addr->mAddress.mFields.m16[0]), tSwap16(addr->mAddress.mFields.m16[1]),
                        tSwap16(addr->mAddress.mFields.m16[2]), tSwap16(addr->mAddress.mFields.m16[3]), tSwap16(addr->mAddress.mFields.m16[4]),
                        tSwap16(addr->mAddress.mFields.m16[5]), tSwap16(addr->mAddress.mFields.m16[6]), tSwap16(addr->mAddress.mFields.m16[7]));
                }                    

                // Initialize coap service
                otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "CoAP start : ");
                lError = otCoapStart(instance, OT_DEFAULT_COAP_PORT);
                if( OT_ERROR_NONE == lError )
                {
                    otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "SUCCESS\n");
                }
                else { otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "FAILED %s\n", otThreadErrorToString(lError)); }    
            }
            else { otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "FAILED %s\n", otThreadErrorToString(lError)); }    
        }
        else { otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "FAILED\n"); }    
    }
    else { otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "FAILED %s\n", otThreadErrorToString(lError)); }    

Filtrer les messages 802.15.4 pour n’être réactif qu’à la trame Green Power qui nous intéresse.

void PromicuousHandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx, void *aContext)
{
    OT_UNUSED_VARIABLE(aContext);

    // receive frame only
    if( !aIsTx )
    {
        otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "Rx %d bytes on channel %d, at time %d",
                                                                aFrame->mLength,aFrame->mChannel,aFrame->mInfo.mRxInfo.mTimestamp);
        // get only frame starting 0x01 0x08 : frame type data without security and short addressing mode fot destination, minimal length shall be 24
        if( (aFrame->mLength>=24) && (aFrame->mPsdu[0]==0x01) && (aFrame->mPsdu[1]==0x08) )
        {
            // green power use data frame with protocol 3, use it only
            if( (aFrame->mPsdu[7]&0x0F)==0x0C )
            {
                static uint32_t ls_previous_frm_counter = 0;
                otError lError;
                otMessage *   message = NULL;
                otMessageInfo messageInfo;  
                otIp6Address coapDestinationIp;              
                uint32_t l_src_id = aFrame->mPsdu[9] + (aFrame->mPsdu[10]<<8) + (aFrame->mPsdu[11]<<16) + (aFrame->mPsdu[12]<<24);
                uint32_t l_frm_counter = aFrame->mPsdu[13] + (aFrame->mPsdu[14]<<8) + (aFrame->mPsdu[15]<<16) + (aFrame->mPsdu[16]<<24);
                uint8_t l_cmd_id = aFrame->mPsdu[17];
                
                otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "GPF from %08X, frame counter %d, command %02X", l_src_id, l_frm_counter, l_cmd_id);

                // send CoAP message to leader only if frame counter is higher than previous message and only toggle (0x22) GP order
                if( (0x22 == l_cmd_id) && (l_frm_counter > ls_previous_frm_counter) )
                {
                    ls_previous_frm_counter = l_frm_counter;
                    message = otCoapNewMessage(instance, NULL);
                    otCoapMessageInit(message, OT_COAP_TYPE_NON_CONFIRMABLE, OT_COAP_CODE_POST);
                    otCoapMessageGenerateToken(message, 2);
                    otCoapMessageAppendUriPathOptions(message, "zcl/e/1/s6/c/2");
                    otIp6AddressFromString("fd85:74ee:7560:9626:dd0e:3508:db6:44c9", &coapDestinationIp);
                    memset(&messageInfo, 0, sizeof(messageInfo));
                    messageInfo.mPeerAddr = coapDestinationIp;
                    messageInfo.mPeerPort = OT_DEFAULT_COAP_PORT;                

                    otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "Send CoAP message : ");
                    lError = otCoapSendRequestWithParameters(instance, message, &messageInfo, NULL, NULL,NULL);
                    if( OT_ERROR_NONE == lError )
                    {
                        otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "SUCCESS\n");
                    }
                    else { otPlatLog(OT_LOG_LEVEL_NONE, OT_LOG_REGION_PLATFORM, "FAILED %s\n", otThreadErrorToString(lError)); }                   
                }
            }
        }
    }
}

Le résultat

Conclusion

Tout d’abord POURQUOI ? Pourquoi vouloir utilisé des devices Green Power dans un réseau thread ? Pour plusieurs raison :

  • Thread possède un type de device Low Energy MAIS il est encore trop consommateur par rapport à un device Green Power
  • Une commande Green Power peut devenir indépendante du type de réseau mesh 802.15.4 en place (Zigbee ou Thread).

Et la sécurité ? En effet Thread est un réseau sécurisé dans toutes les étape de sa vie. Pour cela il oblige l’utilisation d’un border routeur et d’un outils commissioneur, généralement un smartphone. Green Power contient bien des niveaux de sécurités dont un qui permet d’obtenir le même résultat que la sécurité mise en place dans Thread. Grâce à l’outil on peut venir entrer la clé de sécurité du device Green Power que l’on souhaite utiliser directement dans les proxy/actionneur concerné.

Pour résumer seul les devices Green Power Mono directionnel avec une sécurité de type install code et un frame counter incrémentiel présente un intérêt d’utilisation dans un réseau Thread. En effet ils permettent de combler le manque de spécification d’un device vraiment low power sans dégrader le niveau de sécurité du système.