AlertBox : un boîtier d’alerte multifonctions

Magazine
Marque
Hackable
Numéro
44
Mois de parution
septembre 2022
Spécialité(s)


Résumé

En situation opérationnelle, les forces de sécurité et les secours ont de nombreux dispositifs à surveiller. Nous allons construire un boîtier d’alertes lumineuses afin d’alléger la charge des effectifs, le système réalisé devra être assez souple pour s’adapter à différents types de missions.


Body

En tant que direction support, la Direction de l’Innovation, de la Logistique et des Technologies (DILT) de la préfecture de Police fournit aux directions opérationnelles le soutien et les moyens techniques nécessaires à leurs missions. Les outils mis à disposition des opérationnels par la DILT doivent par conséquent pouvoir s’adapter aux évolutions du métier et sont donc conçus au plus près des services, en démarche agile, tout en veillant au respect des règles juridiques. L’expertise des ingénieurs et techniciens de la DILT permet en effet d’évaluer les matériels et les technologies disponibles sur le marché pour mesurer l’opportunité de leur mise en œuvre au sein de systèmes rapidement déployables et adaptables.

1. Présentation du besoin

Le besoin initial était d’allumer un gyrophare lorsque se produit une alerte dans une nouvelle application déployée en salle de commandement. Un premier dispositif a été déployé, il se connectait à l’application et implémentait localement la logique de déclenchement du signal lumineux et son extinction.

Plusieurs problèmes sont apparus :

  • les boîtiers d’alerte sont déployés sur le terrain, coder la logique applicative à l’intérieur pose des problèmes en termes de mise à jour de cette logique ;
  • gérer localement la logique applicative peut obliger le boîtier à traiter un gros volume de données (pour une solution mobile, ça coûte en forfait 4G et en autonomie sur batterie) ;
  • nous ne disposons d’aucune information concernant le bon fonctionnement du boîtier sur le terrain (il est arrivé qu’il soit débranché par accident).

Par ailleurs, en discutant avec différents services, le besoin de fonctionnalités additionnelles est apparu :

  • gérer plusieurs alertes (par des LED de couleurs sur un tableau synoptique ou une carte, par exemple) ;
  • gérer l’acquittement de ces alertes (unitairement ou globalement).

Nous construirons un boîtier aux capacités multiples :

  • une alerte principale matérialisée par une LED (éventuellement doublée d’un relais pour commander un gyrophare ou une lampe flash) avec un bouton d’acquittement ;
  • des alertes en nombre variable, chacune pouvant avoir plusieurs états grâce à l’emploi de LED RGB ;
  • des boutons-poussoirs dont la gestion est assurée intégralement par le serveur.

Nous allons également implémenter :

  • des messages cycliques entre le boîtier et le serveur, pour que les deux parties soient assurées de l’état de la connexion, un voyant sur le boîtier permet de matérialiser cet état ;
  • des acquittements à destination du serveur lors de la réception de messages de déclenchement d’alertes ;
  • le boîtier efface l’alerte principale localement, même en absence du serveur (ceci afin d’éviter des mouvements d’humeur des utilisateurs en cas de problème avec le serveur), un message est toutefois envoyé au serveur lorsque l’alerte principale est acquittée par un utilisateur, ceci permet d’escalader si une alerte n’est pas acquittée.

Les boîtiers seront déployés « sur le terrain », mais en environnement de confiance, il peut s’agir de bureaux, de salles de commandement, de postes de sécurité ou de lieux d’opérations.

Nous allons prévoir le maximum de type de connexions possibles :

  • Ethernet : la PSSI (Politique de Sécurité des Systèmes d’Information) interdit de connecter sur le réseau bureautique autre chose que des postes de travail fournis par le service, ce mode de raccordement sera donc réservé à la connexion sur des « box » internet ou des réseaux de partenaires ;
  • Wi-Fi : là encore, son utilisation est réservée à une utilisation extérieure, le seul Wi-Fi disponible officiellement étant basé sur un portail captif ;
  • 2G/LTE : pratique, car disponible presque partout et ne nécessite ni câblage ni négociations pour le raccordement.

boitier alerte portable-s

Réalisation MajorStéphane T.

2. Choix d’implémentation

2.1 Protocoles réseau et applicatif

La communication entre le boîtier et le serveur sera faite via le protocole MQTT, qui a l’avantage d’être léger et d’avoir de nombreuses implémentations côté client.

MQTT est un broker de messages, un message est publié (publish) par un client sur un sujet (topic), le serveur MQTT distribue alors ce message aux éventuels clients qui se seraient abonnés (subscribe) au topic sur lequel le message a été publié.

Nous sécuriserons les échanges grâce au protocole TLS avec authentification mutuelle à l’aide de certificats clients et serveur, pour ceci nous allons gérer notre propre autorité de certification.

La sécurité sera également assurée par des règles d’accès au niveau du serveur (ACL) permettant de limiter les droits sur les topics au strict nécessaire (principe de moindre privilège). Ceci pour éviter qu’en cas de compromission d’un boîtier, les applications utilisant le même serveur MQTT ne soient compromises aussi.

boitier alerte portable interieur-s

Réalisation Major Stéphane T.

2.2 LED et boutons

Pour l’alerte principale, nous allons utiliser :

  • un GPIO pour une LED d’alerte et/ou un relais ;
  • un GPIO pour le bouton d’acquittement.

Pour les alertes multiples, on peut estimer avoir besoin de 1 à 32 LED RGB en fonction des missions et des applications (supervision, par exemple), ceci représente un nombre conséquent de GPIO dont aucune carte ne dispose nativement.

Une possibilité est d’utiliser un ruban de LED RGB adressables, un tel ruban ne nécessite qu’un seul GPIO, quel que soit le nombre de LED.

L’organisation en ruban permet de réaliser des tableaux d’alertes, il est possible avec un peu de câblage de réaliser des cartes interactives, par exemple.

Si nous souhaitons acquitter individuellement chacune des alertes, il nous faut autant de boutons que de LED, là encore, il faudrait un nombre de GPIO supérieur au nombre de GPIO disponibles. En conséquence, nous utiliserons un composant permettant d’ajouter des GPIO en utilisant un bus I2C.

tableau alerte simple-s

Réalisation GPX David W.

2.3 Carte de base

Afin de choisir la carte qui va nous servir de base, nous recensons les ressources dont nous aurons besoin :

  • 1 GPIO pour la LED d’alerte principale ;
  • 1 GPIO pour le bouton-poussoir d’acquittement de l’alerte principale ;
  • 1 GPIO pour le ruban de LED RGB adressables (la LED témoin de bon fonctionnement sera la LED numéro 0 sur le ruban) ;
  • 1 bus I2C pour les boutons-poussoirs d’acquittement des alertes (donc 2 GPIO).

Nous allons utiliser une carte à base d’ESP32, ce microcontrôleur gère au minimum un bus I2C, dispose d’un certain nombre de GPIO, de la puissance de calcul nécessaire pour faire du MQTTS, et il est facilement programmable dans l’environnement de développement Arduino.

Pour commencer, nous allons utiliser une carte ESP-32-DevKit, cette carte est une des plus simples utilisant une puce ESP32, c’est aussi une des moins chères, elle va néanmoins nous permettre d’assurer toutes les fonctions du boîtier d’alerte avec une connectivité Wi-Fi.

tableau alerte bouton avant-s

Réalisation Major Stéphane T.

tableau alerte bouton arriere-s

Réalisation Major Stéphane T.

2.4 Organisation du code pour l’ESP32

La connexion du boîtier au serveur MQTT sera assurée en TLS avec authentification mutuelle, chaque boîtier aura donc sa propre paire clé privée/certificat.

Nous développerons un programme unique pour les différents types de cartes, un fichier header (.h) permet de décrire un boîtier à l’aide de directives #define et #include définissant les différents paramètres propres au boîtier.

2.5 Gestion des alertes avec Node-RED

La gestion des alertes sera effectuée sur le serveur et non sur le boîtier :

  • pour ne pas avoir à déployer de code applicatif difficile à mettre à jour sur les boîtiers ;
  • pour limiter la consommation de données des boîtiers (coût élevé en 2G/LTE) ;
  • pour ne pas donner de droits de consultation des données applicatives à un boîtier qui se trouve dans un environnement non maîtrisé en termes de sécurité physique.

Node-RED est un système conçu pour l’Internet des objets (IoT), c’est à la fois un environnement de développement graphique, et l’environnement d’exécution correspondant. Le traitement des événements extérieurs est organisé en suites (flow) de blocs de codes prédéfinis (nodes).

2.6 Utilisation de Docker

La partie serveur de notre système est basée sur Docker.

On ne présente plus Docker et ses avantages :

  • indépendance de l’OS support ;
  • les images Docker sont construites automatiquement et contiennent tout le nécessaire à l’exécution de chacun des composants ;
  • l’utilisation de docker-compose permet de décrire la partie serveur.

3. Définition des échanges MQTT

Les boîtiers sont raccordés au serveur en utilisant le protocole MQTT, nous allons décrire les topics utilisés pour les échanges entre le boîtier et le système en charge de sa gestion.

Le topic de tous les messages échangés entre le boîtier et le système commence par alert/id_du_boitier/.

La valeur de id_du_boitier sera choisie au moment de la programmation du boîtier, il s’agit de choisir un nom qui permettra de reconnaître facilement à quoi correspond chaque boîtier.

3.1 Heartbeat (ligne de vie)

Le boîtier envoie toutes les 60 secondes un message « ping » vers le serveur, le serveur lui répond avec un message « pong », cette réponse permet au boîtier de s’assurer de l’état de la connexion afin de gérer l’allumage du témoin de bon fonctionnement.

La payload du message « ping » contient le nombre de secondes écoulées depuis que le boîtier a démarré (uptime), un système de supervision côté serveur peut détecter qu’un problème est survenu avec un boîtier grâce à l’absence de message « ping » ou en constatant que uptime est revenu à une valeur faible.

ATTENTION : La fonction millis() est utilisée pour déterminer la valeur de uptime, elle retourne un entier sur 32 bits qui donne le nombre de millisecondes depuis l’initialisation du système, ce nombre boucle au bout de 2^31/1000 secondes, c’est-à-dire un peu moins de 50 jours, il faut en tenir compte si on veut superviser la valeur de l’uptime.

serveur <<<< boitier: alert/PP_DILT_SDT_SIO_LFO/ping 7321
serveur >>>> boitier: alert/PP_DILT_SDT_SIO_LFO/pong PONG
serveur <<<< boitier: alert/PP_DILT_SDT_SIO_LFO/ping 7381
serveur >>>> boitier: alert/PP_DILT_SDT_SIO_LFO/pong PONG
serveur <<<< boitier: alert/PP_DILT_SDT_SIO_LFO/ping 3    ==> Le boîtier a rebooté
serveur >>>> boitier: alert/PP_DILT_SDT_SIO_LFO/pong PONG
serveur <<<< boitier: alert/PP_DILT_TC_SOUTIEN/ping 4294958
serveur >>>> boitier: alert/PP_DILT_TC_SOUTIEN/pong PONG
serveur <<<< boitier: alert/PP_DILT_TC_SOUTIEN/ping 51   ==> OK millis() a juste cyclé
serveur >>>> boitier: alert/PP_DILT_TC_SOUTIEN/pong PONG

3.2 Gestion de l’alerte principale

Pour déclencher l’alerte principale, le serveur publie un message « push » sur le topic surveillé par le boîtier, le boîtier acquitte l’alerte en publiant un message « acq » sur le topic correspondant du serveur.

Lorsque quelqu’un appuie sur le bouton pour effacer l’alerte, celle-ci est effacée directement sur le boîtier, puis un message « button » est envoyé au serveur pour l’en informer. Ce message permet à l’application d’escalader, dans le cas d’une alerte non prise en compte.

Le serveur déclenche une alerte en publiant un 1, dans le cas où l’alerte n’aurait pas été déjà effacée par un appui sur le bouton correspondant, le serveur a la possibilité de l’annuler en publiant un 0.

serveur >>>> boitier: alert/PP_DILT_SDT_SIO_LFO/push 1
serveur <<<< boitier: alert/PP_DILT_SDT_SIO_LFO/acq 1
serveur <<<< boitier: alert/PP_DILT_SDT_SIO_LFO/button BUTTON
serveur >>>> boitier: alert/PP_DILT_SDT_SIO_LFO/push 1
serveur <<<< boitier: alert/PP_DILT_SDT_SIO_LFO/acq 1
serveur >>>> boitier: alert/PP_DILT_SDT_SIO_LFO/push 0
serveur <<<< boitier: alert/PP_DILT_SDT_SIO_LFO/acq 0

3.3 Gestion des alertes multiples

Une alerte peut être matérialisée par la combinaison :

  • de l’allumage fixe ou clignotant de la LED ;
  • par un niveau à 0 ou 1 pour chacune des trois couleurs RGB, soit un total de 7 combinaisons (la combinaison 0,0,0 représentant l’extinction de la LED).

Nous ajoutons la possibilité de faire clignoter chacune des trois couleurs afin d’attirer l’œil et d’augmenter le choix en termes de couleurs.

L’application pourra choisir pour chacune des trois couleurs RGB :

  • « A » : allumée ;
  • « E » : éteinte ;
  • « C » : clignotante.

Les couleurs disponibles sont les suivantes :

couleurs 01-s 0

Les LED et les boutons correspondants sont numérotés à partir de 0.

serveur >>>> boitier: alert/PP_DILT_SDT_SIO_LFO/push/5 AAE
serveur <<<< boitier: alert/PP_DILT_SDT_SIO_LFO/acq/5 AAE
serveur <<<< boitier: alert/PP_DILT_SDT_SIO_LFO/button/5 BUTTON
serveur >>>> boitier: alert/PP_DILT_SDT_SIO_LFO/push/8 CEE
serveur <<<< boitier: alert/PP_DILT_SDT_SIO_LFO/acq/8 CEE
serveur >>>> boitier: alert/PP_DILT_SDT_SIO_LFO/push/8 EEE
serveur <<<< boitier: alert/PP_DILT_SDT_SIO_LFO/acq/8 EEE

3.4 Gestion des boutons

Un bouton est utilisé pour effacer l’alerte principale.

Il est possible de rajouter jusqu’à 32 boutons, ces boutons ne sont pas gérés localement par le boîtier, l’appui d’un bouton est transmis au serveur, puis un flow Node-RED devra en assurer la gestion. Ceci permet de donner à ces boutons un autre rôle que le simple acquittement des alertes.

4. MQTT

Nous allons commencer par MQTT, car c’est la pierre angulaire de notre système, comme tout notre système, il sera lancé via un container Docker.

Nous allons créer une infrastructure de gestion de clés « technique » avec sa propre autorité de certification pour gérer les certificats, qui seront utilisés dans notre système.

MQTT et sa sécurisation ont été traités dans le numéro 26 de Hackable [1] de septembre-octobre 2018, vous pourrez vous y reporter pour avoir plus d’informations.

4.1 Création de l’autorité de certification

Nous allons utiliser le logiciel TinyCA2 pour créer notre infrastructure de gestion de clés (IGC, ou PKI en anglais).

Nous installons TinyCA2 et nous effectuons un premier lancement :

sudo apt-get update
sudo apt-get install -y tinyca
tinyca2

Lors du premier lancement, l’écran de création d’autorité de certification (AC) s’affiche :

tinyca 01 create ca-s 0

L’écran des options pour l’AC s’affiche ensuite, une fois cet écran validé, notre autorité de certification est créée.

tinyca 02 ca config-s 0

Nous devons modifier une option de l’AC afin que TinyCA nous demande quels sont les sujets alternatifs que l’on souhaite ajouter au certificat. Ceci est utile dans le cas du serveur afin d’ajouter l’adresse IP du serveur au certificat.

Menu Préférences → OpenSSL configuration → Onglet Server Certificate :

  • dans « default_days », mettre « 1825 » (5 ans) ;
  • dans « Subject alternative name », remplir « Ask User », si possible, cliquez sur « DNS Name » (suite à un bug dans la version 0.7.5, il est possible qu’il vous faille valider l’écran, puis y revenir pour pouvoir sélectionner « DNS Name »).

tinyca 03 ca settings-s

4.2 Création du certificat pour le serveur

Les échanges avec le serveur MQTT seront protégés par TLS avec une authentification mutuelle, nous commençons par créer le certificat qui sera utilisé par le serveur, sans lui il est impossible de lancer le serveur.

Dans TinyCA : onglet Certificates → Icône New → Create Key and Certificate (Server).

mqtt 01 create certificate-s

Dans « Common name », tapez le nom DNS du serveur (ce n’est pas grave si vous n’avez pas de nom DNS pour le serveur, puisque son adresse IP sera également ajoutée au certificat). Choisissez un mot de passe pour la clé correspondant au certificat, choisissez une longueur de clé de 2048, SHA-256 et RSA.

mqtt 02 create certificate-s

Sur l’écran suivant, tapez le mot de passe de l’AC, une validité de 1825 jours (5 ans), dans « Subject alternative name (DNS Name) », tapez « ab-mqtt » suivi de la ou des adresses IP du serveur (avec la virgule comme séparateur).

Note : ab-mqtt est le nom du container Docker du serveur MQTT, c’est le nom qu’utilisent les autres containers pour se connecter au serveur MQTT.

mqtt 03 create certificate-s

Il faut désormais exporter le certificat du serveur et la clé correspondante. Notre certificat apparaît dans l’onglet « Certificates », cliquez droit dessus, puis sélectionnez « Export Certificate » :

mqtt 04 export certificate-s

Choisissez un emplacement où sauvegarder le fichier et appelez-le « server.crt », choisissez le format « PEM » et de ne pas inclure ni la clé privée ni l’empreinte de la clé :

mqtt 05 export certificate

On exporte ensuite la clé privée, dans l’onglet « Keys », cliquez droit sur la clé et choisissez « Export Key » :

mqtt 06 export key-s

Sauvegarder le fichier dans le même répertoire que le précédent et appelez le « server.key », choisissez le format « PEM », sauvegardez sans mot de passe (without passphrase) et ne pas inclure le certificat :

mqtt 07 export key

Le mot de passe utilisé lors de la génération de la clé privée vous sera demandé afin d’exporter la clé sans mot de passe :

mqtt 08 export key

Nous aurons également besoin du certificat de notre autorité de certification, il sera utilisé par le serveur MQTT pour vérifier les certificats présentés par les clients.

Dans TinyCA, cliquez sur l’icône « Export CA », nommez le fichier ca.crt, puis choisissez le format PEM.

mqtt 09 export ca cert

4.3 Récupération des sources

Un exemple démarqué des sources du programme du boîtier d’alerte et l’environnement nécessaire à son fonctionnement sont disponibles sur GitHub :

$ git clone https://github.com/U03/alertbox_hk
$ mv alertbox_hk alertbox

Le repo contient les fichiers suivants :

01_mqtt_docker-compose.yml                  1er environnement Docker
02_nodered_docker-compose.yml               2e environnement Docker
alertbox.env                                Variables d'environnement Docker
ablogger-docker/                            Image ablogger-docker
pycert_bearssl/                             Image génération fichier certificates.h
                                               
arduino/alertbox/                           Répertoire code source Arduino
                 alertbox.ino               Code source Arduino
                 box_PP_DILT_SDT_SIO_LFO.h  Description de notre boîtier d'exemple
                 card_esp_wroom_32.h        Carte ESP Wroom
                 card_tcall.h               Carte LilyGo T-Call
                 card_tsim7000g.h           Carte LilyGo T-Sim7000g
                 certificates.h             Certificat de notre AC
                 networks.h                 Description des réseaux
arduino/pcf8574/                            Exemple de programme pour PCF8574
arduino/ws2812b/                            Exemple de programme FastLED
 
certs/                                      Certificats
flows/                                      Flows
mqtt/                                       Configuration serveur Mosquitto MQTT

4.4 Application Docker

Nous allons utiliser docker-compose pour construire notre environnement, pour le moment nous allons lancer deux containers :

  • un container ab-mqtt avec notre serveur MQTT (Eclipse Mosquitto) ;
  • un container ab-logger, il permet de conserver l’historique des messages échangés sur notre topic alert/#.

Nous avons récupéré le code depuis GitHub, notre environnement Docker se trouve dans le fichier 01_mqtt_docker-compose.yml que nous recopions en docker-compose.yml :

$ cd alertbox/
$ cp 01_mqtt_docker-compose.yml docker-compose.yml

Le container ab-logger nécessite une image « maison », le fichier ablogger-docker/Dockerfile contient la définition de cette image. Son fonctionnement est une combinaison de mosquitto_sub pour récupérer les messages émis sur nos topics d’intérêt, de ts pour ajouter un timestamp au début de chaque ligne, suivi de rotatelogs pour générer un fichier log tous les jours (86400 secondes) :

CMD mosquitto_sub $MOSQUITTO_SUB -F '%t %p' | ts '%Y/%m/%d %H:%M:%S ' | rotatelogs -l /alertbox/alertbox.%Y%m%d.log 86400

Le fichier docker-compose.yml décrit notre infrastructure, pour le container ab-logger il pointe vers ablogger-docker/Dockerfile qui décrit comment construire l’image nécessaire. La commande build permet de construire les images nécessaires :

$ docker-compose build
ab-mqtt uses an image, skipping
Building ab-logger
Step 1/4 : FROM ubuntu:20.04
---> 597ce1600cf4
Step 2/4 : RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y         moreutils         apache2-utils         mosquitto-clients && apt-get clean autoclean && apt-get autoremove --yes
---> Using cache
---> bef6f365424d
Step 3/4 : WORKDIR /alertbox
---> Running in be446a305166
Removing intermediate container be446a305166
---> 57bfdd52bee9
Step 4/4 : CMD mosquitto_sub $MOSQUITTO_SUB -F '%t %p' | ts '%Y/%m/%d %H:%M:%S ' | rotatelogs -l /alertbox/alertbox.%Y%m%d.log 86400
---> Running in 629ed4eb6cd1
Removing intermediate container 629ed4eb6cd1
---> 35b2a74cdc20
Successfully built 35b2a74cdc20
Successfully tagged u03/ablogger:latest

La commande docker-compose pull permet de télécharger les images nécessaires. Notez que l’utilisation des commandes build et pull est facultative, elles sont lancées automatiquement si nécessaire lorsque l’on monte notre application.

$ docker-compose pull
Pulling ab-mqtt ... done

Avant de lancer notre application, nous devons créer les volumes nécessaires, qui permettront de conserver les données même en cas de redémarrage des containers :

$ docker volume create ab-logs
ab-logs
$ docker volume create ab-mqtt-data
ab-mqtt-data

4.5 Premier lancement

Avant d’effectuer le premier lancement, il est nécessaire de copier dans le répertoire mqtt/ les clés et certificats générés précédemment (ca.crt, server.crt et server.key).

Des fichiers sont déjà présents dans le répertoire, vous pouvez les utiliser si vous n’en avez pas généré.

Nous pouvons désormais lancer notre application :

$ docker-compose up -d
Creating network "u03" with the default driver
Creating ab-logger ... done
Creating ab-mqtt   ... done
$ docker-compose logs
Attaching to ab-mqtt, ab-logger
ab-mqtt   | 2022-04-14 19:44:18: mosquitto version 2.0.14 starting
ab-mqtt   | 2022-04-14 19:44:18: Config loaded from /mosquitto/config/mosquitto.conf.
ab-mqtt   | 2022-04-14 19:44:18: Opening ipv4 listen socket on port 8883.
ab-mqtt   | 2022-04-14 19:44:18: Opening ipv4 listen socket on port 1883.
ab-mqtt   | 2022-04-14 19:44:18: Opening ipv4 listen socket on port 1883.
ab-mqtt   | 2022-04-14 19:44:18: mosquitto version 2.0.14 running
ab-mqtt   | 2022-04-14 19:44:18: New connection from 172.99.0.202:35540 on port 1883.
ab-mqtt   | 2022-04-14 19:44:18: New client connected from 172.99.0.202:35540 as mosq-DM9bFpYyxlp4PmabyY (p2, c1, k60, u'ab_logger.alert.u03.fr').

Nous publions un message et nous vérifions qu’il a bien été enregistré dans les logs, notez au passage que l’heure enregistrée est en UTC, car nous n’avons pas défini de fuseau horaire à l’intérieur du container id-logger :

$ docker exec -ti ab-mqtt mosquitto_pub -t alert/PP_DILT_SDT_SIO_LFO/push -m 1
$ docker exec -ti ab-logger tail alertbox.`date +%Y%m%d`.log
2022/02/27 20:45:42 alert/PP_DILT_SDT_SIO_LFO/push 1

5. Matériel

Nous allons utiliser des cartes à base de ESP32 que nous programmerons dans l’environnement Arduino. Différents modèles de cartes existent, mais les différences sont globalement très faibles, si ce n’est les ports auxquels sont assignés les périphériques, qu’il s’agisse de périphériques que nous pourrions utiliser (Ethernet, modem 2G/LTE), ou de périphériques que nous n’utiliserons pas tels que caméra, carte SD…

Il faudra choisir nos GPIO en fonction du modèle de carte, ceci aura une incidence sur la programmation (utilisation de fichiers include contenant des directives #define adaptées).

Nous commencerons avec la carte ESP32-DevKitC qui est la carte de base pour faire du Wi-FI.

5.1 Ruban de LED adressables

Les WS2812B sont un assemblage de 3 LED (rouge, verte, bleue) et d’un circuit de contrôle dans un composant CMS rectangulaire, elles sont parfois appelées WS2812B 5050 (où 5050 désigne un composant CMS de 5 mm*5 mm).

Elles sont alimentées de 3,5 à 5 V, comportent 4 connexions, 2 pour l’alimentation, 1 entrée de contrôle et 1 sortie de contrôle branchée en cascade avec l’entrée de la LED suivante.

En utilisant le protocole de contrôle, il est possible de choisir le niveau des 3 couleurs pour chacune des LED d’un ruban de LED connectées les unes aux autres. Chacune des LED remet le signal de contrôle en forme pour la LED suivante et il est possible de créer un ruban d’une longueur quelconque.

Une bibliothèque Arduino existe pour contrôler ce genre de ruban de LED, sans qu’il soit nécessaire d’implémenter soi-même le protocole.

5.2 Extension d’entrées sorties en I2C

Afin d’augmenter le nombre de GPIO pour disposer d’un maximum de boutons, nous allons utiliser des PCF8574, ils permettent sur un bus I2C d’obtenir l’équivalent de 8 GPIO.

Le composant PCF8574 comporte 3 broches permettant de choisir l’adresse du composant sur le bus I2C, il est donc possible sur un bus I2C d’avoir 8*PCF8574 et de disposer de 64 GPIO supplémentaires en n’utilisant que les deux GPIO qui constituent le bus I2C. Nous nous limiterons à 32 GPIO (et donc 4*PCF8574).

5.3 Câblage

Nous alimenterons le module ESP32 DevKit par l’entrée EXT_5V (broche 19), un régulateur intégré fournit les 3,3 V nécessaire à l’ESP32.

Attention : lors de la programmation du module en USB, veillez à ne pas l’alimenter en 5 V sous peine de risquer d’endommager les ports USB de votre ordinateur.

Le ruban de LED sera alimenté en 5 V, car le régulateur 3,3 V du module n’est pas dimensionné pour alimenter un ruban de LED. Bien que les LED WS2812B soient alimentées en 5 V, leur entrée est compatible avec un microcontrôleur en 3,3 V (notez que seule la première LED est concernée, puisqu’elle génère le signal d’entrée de la seconde).

alertbox.kicad-s

6. Programmation du boîtier

Nous nous concentrerons dans un premier temps sur la programmation du boîtier, comme il est entièrement piloté par le serveur MQTT, nous pourrons tester notre boîtier en publiant des messages MQTT, comme nous l’avons vu au §4.4.

Nous développerons ultérieurement la logique applicative avec Node-RED.

6.1 Connectivité Wi-Fi et MQTT

Nous utilisons la bibliothèque wifiMultiple qui permet de spécifier une liste de SSID Wi-Fi, ceci permet éventuellement de déplacer le boîtier entre plusieurs lieux :

WiFiMulti wifiMultiple;
#define WIFIMULTI_APPLIST \
    wifiMultiple.addAP("SIC_OP",    "LaSDTrecrute"); \
    wifiMultiple.addAP("Takeshiba", "DesTalents"); \
    wifiMultiple.addAP("U03",       "3615KiVeutVeniraLaPP");

Attention, cette bibliothèque ne permet pas d’utiliser des points d’accès qui ne diffusent pas leur SSID (Wi-Fi cachés).

Parfois, la bibliothèque n’arrive pas à se connecter à un point d’accès, il faut alors redémarrer le microcontrôleur si on n’a pas réussi à se connecter au bout de 20 tentatives :

    int nbTentatives = 0;
    while(wifiMultiple.run() != WL_CONNECTED) {
        if (nbTentatives++ > 20) {
          ESP.restart();
        }
        delay(500);
    }

6.2 Gestion du bandeau de LED

Nous utilisons la bibliothèque FastLED, elle permet d’utiliser de nombreux modèles de LED adressables, il est nécessaire de spécifier le nombre de LED du ruban, car la bibliothèque ne peut pas le déterminer. Ce nombre n’a pas besoin d’être exact, il faut qu’il soit supérieur ou égal au nombre réel de LED, au pire on perd un peu de mémoire et de performance.

#define LEDS_DATA_PIN    13
#define LEDS_NUM_LEDS    16
#define LEDS_BRIGHTNESS  64
#define LEDS_LED_TYPE    WS2812B
#define LEDS_COLOR_ORDER GRB
   .../...
#include <FastLED.h>
CRGB leds[LEDS_NUM_LEDS];

Le ruban de LED est matérialisé par un tableau de structures CRGB, il est possible de positionner le niveau de chacune des composantes RGB, nous mettons 0 pour la lettre code E (éteinte) ou 255 pour A (allumée).

Pour le clignotement, nous mettons 1 ou 254, l’écart de luminosité est faible par rapport à 0 ou 255 et ceci nous permet de repérer les couleurs à faire clignoter. La variable blinkMillis indique quand le dernier clignotement a été effectué, blinkCourant quel est le niveau courant. La formule 255 – clignotement_courant permet de passer d’un niveau à l’autre.

uint32_t blinkMillis = 0;
uint32_t blinkCourant = 254;
   .../...
  // Positionnement de la valeur du rouge (caractère 0 de la payload MQTT)
  //
  switch (couleur[0]) {
    case 'A': leds[led].r = 255; break;
    case 'E': leds[led].r =   0; break;
    case 'C': leds[led].r = blinkCourant; break;
  }
   .../...
  // Si le dernier clignotement remonte à plus de 500 ms
  // On fait clignoter les couleurs qui le doivent
  //
  if (millis() > blinkMillis + 500) {
    for (int i = 0; i < LEDS_NUM_LEDS; i++) {
      if (leds[i].r == 1 || leds[i].r == 254) { leds[i].r = 255 - leds[i].r; }
      if (leds[i].g == 1 || leds[i].g == 254) { leds[i].g = 255 - leds[i].g; }
      if (leds[i].b == 1 || leds[i].b == 254) { leds[i].b = 255 - leds[i].b; }
    }
    blinkCourant = 255 – blinkCourant;
    blinkMillis = millis();
    FastLED.show();
  }

6.3 Gestion des boutons

Le bouton permettant d’acquitter l’alerte principale est connecté directement à un GPIO dans le mode INPUT_PULLUP.

Lors de la réalisation d’un boîtier raccordé en 2G/LTE, nous avons constaté que parfois l’alarme s’acquittait toute seule de façon aléatoire. Ceci est dû au fil utilisé pour connecter le bouton au GPIO, en raison de sa longueur et de sa proximité avec l’antenne GSM, il génère des signaux parasites que la valeur trop élevée de la résistance de pull-up interne de l’ESP32 n’arrive pas à neutraliser.

Nous aurions pu ajouter une résistance de pull-up, mais ceci aurait complexifié la réalisation, nous avons fait le choix de traiter ces signaux parasites de façon logicielle en vérifiant que le GPIO reste au niveau bas pendant 280 ms minimum.

  bool appuiBouton = true;
  for (int i = 0; i < 40; i++) {
    if (digitalRead(GPIO_LED) == HIGH && digitalRead(GPIO_BUTTON) == LOW) {
      delay(7);
    } else {
      appuiBouton = false;
      break;
    }
  }
  if (appuiBouton) {
    SerialMon.println("Appui bouton alors que l'alarme est activee");
    digitalWrite(GPIO_LED, LOW);
    envoyerBouton("");
  }

En plus de ce bouton, nous pouvons gérer jusqu’à 32 boutons, nous construisons un entier de 32 bits représentant l’état de chacun des boutons par scrutation de 4 composants PCF8574. Il n’est pas obligatoire de disposer des 4, il est même possible de n’en mettre aucun si on n’a pas besoin de la fonctionnalité.

Les entrées des PCF sont de type PULL_UP, le bouton poussoir les place donc à la masse, dans le cas d’un composant absent, on place 0xFF pour les boutons correspondants, il faut penser à inverser les bits au final pour refléter l’état (appuyé ou non) des boutons :

uint32_t lireClavier() {
  uint32_t clavier = 0x0000;
  for (int i = 3; i >= 0; i--) {
    clavier = clavier << 8;
    if (PCF[i].isConnected()) {
      clavier = clavier | PCF[i].read8();
    } else {
      clavier = clavier | 0xFF;
    }
  }
  clavier = ~clavier;
  return(clavier);
}

6.4 Création de certificats pour les boîtiers

Nous utilisons notre autorité de certification pour générer les certificats qui seront utilisés par les boîtiers d’alerte.

Dans TinyCA : onglet Certificates → Icône New → Create Key and Certificate (Client).

Le « Common name » (CN) sera le nom du boîtier, pour cet article, ce sera PP_DILT_SDT_SIO_LFO, choisissez un mot de passe pour la clé correspondant au certificat, choisissez une longueur de clé de 2048, SHA-256 et RSA.

Sur l’écran suivant, tapez le mot de passe du CA, une validité de 1825 jours (5 ans).

Attention : le CN doit correspondre au nom du boîtier, sinon les Access Control List sur le serveur MQTT vont empêcher le boîtier de fonctionner.

Il faut désormais exporter le certificat du serveur et la clé correspondante. Notre certificat apparaît dans l’onglet « Certificates », cliquez droit dessus, puis sélectionnez « Export Certificate ».

Choisissez l’emplacement où sauvegarder le fichier et appelez le PP_DILT_SDT_SIO_LFO.crt, choisissez le format « PEM » et de n’inclure ni la clé ni l’empreinte de la clé.

Exportez ensuite la clé, dans l’onglet « Keys », cliquez droit sur la clé et choisissez « Export Key ».

Choisissez l’emplacement où sauvegarder le fichier et appelez le PP_DILT_SDT_SIO_LFO.key, choisissez le format « PEM », sauvegardez sans mot de passe (without passphrase) et ne pas inclure le certificat.

Le mot de passe utilisé lors de la génération du certificat vous sera demandé afin d’exporter la clé sans mot de passe.

Répétez l’opération pour les utilisateurs suivants :

  • nodered.alert.u03.fr
  • administrateur.alert.u03.fr

6.5 Création du fichier certificates.h

La bibliothèque BearSSL, qui gère la partie TLS, utilise un format spécial pour la chaîne de certification, l’outil pycert_bearssl permet de générer le fichier include nécessaire pour que notre application puisse contrôler le certificat présenté par le serveur.

Nous exécuterons l’outil dans un container Docker, car il a besoin de dépendances Python. On commence par construire l’image Docker :

$ cd ~/alertbox/pycert_bearssl
$ docker build -t pycert_bearssl .
Sending build context to Docker daemon 48.13kB
   ... / ...
Successfully built b28cc70de623
Successfully tagged pycert_bearssl:latest

Il est ensuite possible d’exécuter l’outil pour convertir le certificat de notre CA (ca.crt) au format utilisable pour l’Arduino, nous nommerons le fichier certificates.h :

$ cat ca.crt | docker run --rm -i pycert_bearssl > certificates.h
Loaded certificate <stdin>
Wrote 1 trust anchors to <stdout>

Le script a été modifié par rapport à l’original pour écrire tous ses messages sur stderr afin que la redirection de stdout n’inclue pas les messages du script.

6.6 Le fichier card_esp_wroom_32.h

Le fichier card_esp_wroom_32.h permet de décrire la carte et le câblage qui ont été réalisés :

#define CARD_WIFI
 
#include <WiFi.h>
#include <WiFiMulti.h>
#include <WiFiClientSecure.h>
 
#define GPIO_LED          2
#define GPIO_BUTTON       0
 
#define LEDS_DATA_PIN    13
 
#define PCF_SCL 22
#define PCF_SDA 21
#define PCF_00 0x20
#define PCF_01 0x21
#define PCF_02 0x22
#define PCF_03 0x23

6.7 Création du fichier networks.h

Le fichier networks.h permet de spécifier les paramètres réseau :

  • adresse IP et port du serveur MQTT ;
  • pour les cartes raccordées en Wi-Fi, les SSID et clés des réseaux susceptibles d’être utilisés ;
  • les caractéristiques de l’APN dans le cas des cartes 2G ou LTE :
#define MQTT_KEEPALIVE 240
#define MQTT_PORT      8883
const char* MQTT_SERVER = "192.168.0.179";
 
#ifdef CARD_TINYGSM
const char apn[] = "iot.1nce.net";
const char gprsUser[] = "";
const char gprsPass[] = "";
#endif
 
#ifdef CARD_WIFI
WiFiMulti wifiMultiple;
#define WIFIMULTI_APPLIST \
    wifiMultiple.addAP("SIC_OP",    "LaSDTrecrute"); \
    wifiMultiple.addAP("Takeshiba", "DesTalents"); \
    wifiMultiple.addAP("U03",       "3615KiVeutVeniraLaPP");
#endif

6.8 Création du fichier box_PP_DILT_SDT_SIO_LFO.h

Le fichier box_PP_DILT_SDT_SIO_LFO.h décrit le boîtier et fait la liaison avec les fichiers précédents.

Il donne l’identifiant du boîtier qui sera utilisé pour déterminer les topics utilisés, il décrit le ruban de LED utilisé.

Nous ajoutons à ce fichier le certificat client que nous avons généré ainsi que la clé correspondante, au final le fichier doit ressembler à ceci :

#include "card_esp_wroom_32.h"
#include "networks.h"
#include "certificates.h"
 
#define ID_BOITIER       "PP_DILT_SDT_SIO_LFO"
 
#define LEDS_NUM_LEDS    8
#define LEDS_BRIGHTNESS 48
#define LEDS_LED_TYPE    WS2812B
#define LEDS_COLOR_ORDER GRB
 
static const char client_cert[] PROGMEM = R"KEY(
-----BEGIN CERTIFICATE-----
MIIESTCCAjECF...BDQUAMIGd
   ... / ...
Jh0mseUP3zhLkRpj
-----END CERTIFICATE-----
)KEY";
static const char client_key[] PROGMEM = R"KEY(
-----BEGIN PRIVATE KEY-----
MIIEvgI...HoqV0
   ... / ...
Ejru38oKT4ek1gdIfUkzEg7Pi
-----END PRIVATE KEY-----
)KEY";

6.9 Fichier alertbox.ino

En tête du fichier, vous devez inclure le fichier box_PP_DILT_SDT_SIO_LFO.h, qui décrit notre boîtier :

// INCLURE ICI LE FICHIER DÉCRIVANT LE BOITIER
//
#include "box_PP_DILT_SDT_SIO_TFO.h"

6.10 Programmation du boîtier

Nous utilisons l’environnement Arduino pour programmer notre boîtier.

Vous devez ajouter les ESP32 dans le gestionnaire de carte, dans le menu Fichier → Préférences → URL de gestionnaire de cartes supplémentaires ajouter https://dl.espressif.com/dl/package_esp32_index.json, sélectionner ensuite le type qui correspond à votre carte dans le menu Outils → Type de carte.

Nous utilisons les bibliothèques suivantes qui doivent être installées par menu Outils → Gérer les bibliothèques :

  • FastLED (v3.5.0 par Daniel Garcia) ;
  • PCF8574 (v0.3.3 par Rob Tillaart) ;
  • PubSubClient (v2.8.0 par Nick O’Leary) ;
  • SSLClient (v1.6.11 par OPEnS Lab) ;
  • StreamDebugger (v1.0.1 par Volodymyr Shymanskyy) ;
  • TinyGSM (v0.11.5 par Volodymyr Shymanskyy).

Conclusion

Nous sommes d'ores et déjà en présence d’un boîtier fonctionnel, capable de gérer LED et boutons, et de se connecter de façon sécurisée à un serveur MQTT.

Nous avons choisi de ne pas coder de logique applicative dans le boîtier et de le piloter à travers un serveur MQTT. Il existe des librairies permettant d’interagir avec un serveur MQTT dans tous les langages modernes, aussi nous pourrions coder dans le langage de notre choix.

Afin d’assurer le maximum de flexibilité, nous optons plutôt pour du « low-code » avec Node-RED, ceci nous permettra de nous adapter rapidement aux différents scénarios d’utilisation du boîtier.

Dans la suite de cet article, à paraître avec le prochain numéro de Hackable, nous verrons le déploiement de Node-RED, la sécurisation du serveur MQTT et les différents types de cartes utilisables pour construire nos boîtiers.

Référence

[1] Hackable n°26 - https://connect.ed-diamond.com/Hackable/hk-026



Article rédigé par

Par le(s) même(s) auteur(s)

Réutilisation d’un lecteur audio de figurines

Magazine
Marque
Hackable
Numéro
45
Mois de parution
novembre 2022
Spécialité(s)
Résumé

On voit périodiquement des publicités pour des collections de figurines qui déclenchent la lecture d’un livret d’accompagnement lorsqu’on les pose sur leur socle musical. La première question qui se pose est « comment ça fonctionne ? », la seconde est « comment lui faire jouer autre chose ? »

OpenWrt : un firmware et des applications

Magazine
Marque
Hackable
Numéro
41
Mois de parution
mars 2022
Spécialité(s)
Résumé

Pour certaines applications légères, OpenWrt tournant sur un équipement réseau low cost est une alternative solide face à un OS classique sur une Raspberry Pi. Nous allons voir comment intégrer une application dans un firmware destiné à un petit routeur Wi-Fi coûtant moins de 30 euros.

Les derniers articles Premiums

Les derniers articles Premium

Sécurisez vos applications web : comment Symfony vous protège des menaces courantes

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Les frameworks tels que Symfony ont bouleversé le développement web en apportant une structure solide et des outils performants. Malgré ces qualités, nous pouvons découvrir d’innombrables vulnérabilités. Cet article met le doigt sur les failles de sécurité les plus fréquentes qui affectent même les environnements les plus robustes. De l’injection de requêtes à distance à l’exécution de scripts malveillants, découvrez comment ces failles peuvent mettre en péril vos applications et, surtout, comment vous en prémunir.

Bash des temps modernes

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Les scripts Shell, et Bash spécifiquement, demeurent un standard, de facto, de notre industrie. Ils forment un composant primordial de toute distribution Linux, mais c’est aussi un outil de prédilection pour implémenter de nombreuses tâches d’automatisation, en particulier dans le « Cloud », par eux-mêmes ou conjointement à des solutions telles que Ansible. Pour toutes ces raisons et bien d’autres encore, savoir les concevoir de manière robuste et idempotente est crucial.

Présentation de Kafka Connect

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Un cluster Apache Kafka est déjà, à lui seul, une puissante infrastructure pour faire de l’event streaming… Et si nous pouvions, d’un coup de baguette magique, lui permettre de consommer des informations issues de systèmes de données plus traditionnels, tels que les bases de données ? C’est là qu’intervient Kafka Connect, un autre composant de l’écosystème du projet.

Le combo gagnant de la virtualisation : QEMU et KVM

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

C’est un fait : la virtualisation est partout ! Que ce soit pour la flexibilité des systèmes ou bien leur sécurité, l’adoption de la virtualisation augmente dans toutes les organisations depuis des années. Dans cet article, nous allons nous focaliser sur deux technologies : QEMU et KVM. En combinant les deux, il est possible de créer des environnements de virtualisation très robustes.

Les listes de lecture

7 article(s) - ajoutée le 01/07/2020
La SDR permet désormais de toucher du doigt un domaine qui était jusqu'alors inaccessible : la réception et l'interprétation de signaux venus de l'espace. Découvrez ici différentes techniques utilisables, de la plus simple à la plus avancée...
8 article(s) - ajoutée le 01/07/2020
Au-delà de l'aspect nostalgique, le rétrocomputing est l'opportunité unique de renouer avec les concepts de base dans leur plus simple expression. Vous trouverez ici quelques-unes des technologies qui ont fait de l'informatique ce qu'elle est aujourd'hui.
9 article(s) - ajoutée le 01/07/2020
S'initier à la SDR est une activité financièrement très accessible, mais devant l'offre matérielle il est parfois difficile de faire ses premiers pas. Découvrez ici les options à votre disposition et les bases pour aborder cette thématique sereinement.
Voir les 33 listes de lecture

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous