Catégories
Le coin des étudiants

Shutter pour vidéoprojecteur

Prototype du shutter (fermé)

Contexte

Le shutter pour vidéo projecteur, ou volet roulant en français, est un moteur contrôlé à distance permettant de masquer le faisceau de lumière d’un vidéoprojecteur. Pour les gros vidéoprojecteurs nécessitant un temps de chauffe, cet outil permet de maîtriser quand l’utilisateur souhaite projeter le contenu diffusé. Des kits semblables sont disponibles sur le marché mais à des prix très onéreux. Elyxoft a donc décelé une opportunité lorsque l’un de ses futurs clients a fait une demande pour ce type de produit en proposant une alternative beaucoup moins coûteuse. Celui-ci est contrôlé depuis une application smartphone via la technologie Bluetooth et peut être ouvert ou fermé.

Réalisation

Le projet est décomposé en deux parties: embarquée et débarquée.

La partie embarquée comporte du développement en C sur un microcontrôleur ESP32 permettant de gérer la logique intégrée à la carte. Celle-ci comporte aussi la réalisation de modèles 3D ainsi que le choix des matériaux et composants du kit Shutter pour ainsi le rendre compact et facilement manipulable.

La partie débarquée consiste en la réalisation d’une application cross-platform permettant de piloter le shutter depuis un smartphone ou un ordinateur.

Une première version du kit fut développée par l’étudiant en SEOC à l’INPG, Fabien Vermeulen. Le prototype comprenais tout les aspects fonctionnels du Shutter avec un microcontrôleur en boîtier et une application mobile de contrôle Bluetooth. Cette première version ne correspondais pas tout à fait aux attentes de l’entreprise car l’application de contrôle n’était pas cross-plateform. Les outils utilisés pour produire une application cross-plateform posaient des problèmes de compatibilités qui n’ont pu être résolus dans les temps ce qui a obligé Fabien à se concentrer sur le développement d’une application uniquement Android à l’aide d’autres outils.

Suite à ce problème de compatibilité pour du cross-platform, après la fin de son stage Fabien proposa une solution qui sera reprise et testée par l’étudiant en informatique à l’IUT2, Lori Lou. Une deuxième version du kit a pu alors être développée comprenant une toute nouvelle application de contrôle cette fois ci cross-platform.

Conclusion

Bien que le kit du Shutter soit encore au stade de prototype, celui-ci est fonctionnel et est en test sur le terrain pour des spectacles/prestations. Cependant l’entreprise doit encore travailler sur le boîtier et le packaging ainsi que sur certains ajustements au niveau de l’application. Cette tâche pourra certainement être confiée à un futur étudiant !

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.