Introduction
Dans un précédent article HowTo, "Réaliser une sonnette connectée en LoRa avec Chirpsack", nous avons vu comment on peut mettre en place une sonnette LoRa grâce à une architecture LoRaWAN et au serveur open-source Chirpstack. Aujourd'hui dans le Retour de la sonnette LoRa, nous allons voir comment réaliser une application similaire, mais avec des outils différents. En effet, vous allez découvrir comment réaliser une sonnette LoRa grâce à un réseau en maillage ! Cette seconde solution a l'avantage d'être moins chère à mettre en place, mais également scalable à faible coût (une vingtaine d'euros pour se procurer un module LoRa supplémentaire qui s'ajoute au réseau existant en "Plug and Play"). En outre, vous allez pouvoir découvrir les intérêts de la topologie MESH.
Prérequis
Matériel :
- Un ordinateur
6 cartes Heltec WiFi LoRa 32 V3
https://heltec.org/project/wifi-lora-32-v3/
Un bouton (Réalisé ici avec un DualButton de m5stack)
Un buzzer monotonique (Réalisé ici avec un DFR0032 de DFRobot)
- Des câbles Dupont
Logiciel :
- Navigateur web
- VS Code
Platformio (extension VS Code)
VS Code et Platformio sont la solution logicielle la plus confortable et accessible pour réaliser notre application, et celle que l'on va documenter à travers cet article. Toutefois, il est possible d'atteindre le même résultat avec d'autres outils, notamment via la CLI de Meshtastic.
Plan
I - Théorie
1. La technologie LoRa
2. Les réseaux maillés
3. Le projet open source Meshtastic
4. L'objectif de ce HowTo : la sonnette MESH
II - Réalisation de la sonnette
1. Installation de Meshtastic
2. Ajout du code pour le bouton
3. Ajout du code pour le buzzer
4. Utilisation de la sonnette
III - Conclusion
I - Théorie
1. La technologie LoRa
Le LoRa est une technologie de communication sans fil conçue spécifiquement pour les réseaux longue portée faible consommation (les LPWAN - Low Power Wide Area Networks). Elle est très utilisée dans l'IoT (Internet of Things). Basée sur les techniques de modulation à étalement de spectre, et plus précisément sur les modulations Chirp Spread Spectrum (CSS), elle permet d'émettre des signaux robustes aux interférences et au bruit, longue portée (jusqu'à une quinzaine de kilomètres), et avec une bonne pénétration des obstacles, tout en nécéssitant une faible quantité d'énergie. Ces caractéristiques sont des atouts essentiels pour les réseaux IoT. La technologie LoRa est très présente dans les applications liées aux smart cities, à la smart agriculture, à la domotique, à l'industrie 4.0 et à bien d'autres secteurs encore.
Source : Semtech
LoRa est une technologie propriétaire qui appartient à l'entreprise Semtech. Cette dernière commercialise notamment les puces LoRa pour les "End devices" : SX127X et SX126X, ainsi que les puces de la gamme "Gateways", les SX130X. D'autres acteurs proposent également les puces LoRa sous licence Semtech (ex. ST Microelectronics, Microchip... )
LoRa émet sur bandes de fréquences libres et ne nécessite pas de licence.
Pour simplifier l'utilisation de la technologie LoRa, Semtech a developpé un standard ouvert, le LoRaWAN, qui répond aux besoins des couches MAC et réseau (couche 2 et 3) du modèle OSI. En outre, LoRaWAN gère l'intéraction entre des noeuds IoT souvent équipés de capteurs, et les gateways qui doivent retransmettre leur information à des serveurs. Il permet donc à des appareils IoT fonctionnant sur batterie de rejoindre de grands réseaux connectés à internet, à l'échelle regionale, nationale ou globale. Il répond à certains besoins clés de l'IoT tels que la communication bidirectionnelle, la sécurité tout au long de la transmission (end to end security), mais aussi l'itinérance et la géolocalisation des appareils. Cette architecture est la plus répandue dans les applications IoT à base de LoRa.
Source : prbs23.com, Getting Started with a Private LoRaWAN Network
En réseau, c'est ce que l'on appelle une topologie en étoile. Des noeuds hierarchiquement plus importants que d'autres doivent assurer la remontée de l'information (ici, les gateways). Cela offre des avantages en terme d'extensibilité du réseau, en s'appuyant sur les gateways deja installées. En outre on peut utiliser les noeuds critiques pour centraliser la maintenance et la surveillance d'un réseau. Toutefois, on s'expose à des mises hors service de parties du réseau lorsqu'un problème survient au niveau des gateways.
Bien que de loin l'architecture la plus répandue, l'utilisation du LoRaWAN n'est pas la seule alternative viable pour mettre en place un système IoT basé sur LoRa.
2. Les réseaux maillés
Le réseau maillé est une topologie réseau où tous les nœuds sont connectés pair à pair. Chaque nœud à donc une responsabilité dans la remontée de l'information. Chaque nœud doit recevoir et retransmettre à ses voisins les paquets échangés. Ainsi, les nœuds forment une sorte de filet et sont tous interconnectés. Les réseaux maillés s'affranchissent donc des difficultés liées aux pannes touchant des noeuds à responsabilité élevée (comme les gateways de la topologie en étoile). En outre, ils permettent de réduire les coûts d'installation et de maintenance du réseau, tout en supportant aisément une couverture évolutive (dans le cas des réseaux maillés sans fil). Pour ces différentes raisons, les réseaux en maillages se sont affirmés comme une alternative intéressante aux topologies en étoile pour les applications IoT.
Source : boosthigh.com, Mesh Network for IoT Devices
On distingue deux familles de réseaux en maillage. Tout d'abord ceux qui établissent l'interconnexion des nœuds grâce à des informations de routage, des routes. Ensuite ceux qui utilisent la méthode dite d'inondation, ou flooding en anglais, qui consiste en la retransmission par chaque nœud des paquets qu'il reçoit, sans connaissance de la position des autres nœuds ou de quelconque route. Les réseaux maillés qui se basent sur le flooding utilisent très souvent des limitations de retransmissions, pour limiter l'occupation du canal et la perte d'énergie. Ainsi, ils mettent souvent en œuvre des stratégies basées sur l'annulation de retransmissions inutiles car déjà effectuées par un nœud proche, ou encore des limitations sur le nombre maximal de sauts possibles pour un paquet.
Les réseaux maillés basés sur les routes utilisent des protocoles qui régissent l'échange d'informations de routage. Ces informations sont des tables de routage, qui répertorient le positionnement (Ranking) des autres nœuds du réseau grâce à ce que l'on appelle un vecteur de distance, où le nombre de sauts qui sépare un nœud d'un autre nœud. Cela permet ainsi d'optimiser le nombre de transmissions en choisissant la route la plus optimale de la table de routage. On sépare ces protocoles en deux catégories, les proactifs et les réactifs. En effet, les protocoles dit proactifs vont s'assurer du maintien de bonnes informations de routage en les échangeant de manière périodique selon un intervalle de temps. On peut citer notamment le protocole DSDV (Destination Sequenced Distance Vetor). A l'inverse, les protocoles dits réactifs, eux, échangent les informations de routage dont ils ont besoin uniquement en cas de problème. Une fois le réseau établi et les chemins définis, un protocole réactif n'agira que lorsqu'il fait face à une erreur. On peut citer le protocole AODV (Ad-hoc On-demand Distance Vector). Enfin il existe des protocoles hybrides qui reprennent des éléments des deux stratégies présentées.
Source : Papier scientifique : Investigation of MANET routing protocols for mobility and scalability
Cette topologie réseau et les protocoles MANET (Mobile Ad-hoc NETworks), sont souvent utilisés dans le cadre de réseau mobiles ou les nœuds sont voués à se déplacer. Leur couverture évolutive et leur réadaptation automatique rendent ces protocoles très efficace dans ce contexte. Toutefois, ils peuvent également se révéler très intéressants pour des systèmes stationnaires. Tout d'abord car il est difficile d'exploiter des infrastructures de communication déjà existantes dans certains contextes ou certaines zones géographiques, mais aussi car la topologie du réseau peut changer suite à d’éventuelles défaillances des nœuds.
3. Le projet open source Meshtastic
Le projet Meshtastic, open source et porté par sa communauté, vise à établir un réseau maillé longue portée faible consommation basé sur la technologie LoRa. Son objectif est de permettre aux utilisateurs de communiquer dans des zones où les infrastructures de communication sont indisponibles ou insuffisamment fiables, grâce à des radios peu coûteuses. Il est également apprécié pour l'aspect confidentialité apporté par son infrastructure communautaire indépendante.
Source : meshtastic.org
Ces radios sont programmées pour retransmettre les messages reçus à leurs voisins, permettant ainsi d'atteindre les nœuds les plus éloignés du réseau. Plus précisément, Meshtastic utilise un algorithme de flooding contrôlé. C'est à dire un algorithme qui se base sur le flooding en explicitant certaines contraintes au niveau de la retransmission des paquets.
La principale contrainte mise en place par Meshtastic se base sur le SNR (Signal to Noise Ratio). Plus le nœud relais suivant se trouve loin, plus le signal reçu par ce dernier sera faible, ce qui diminue le SNR. Cette supposition reste néanmoins assez simpliste, car elle est adaptée au cas de propagation libre du signal (free space propagation). Voyons comment se principe s'illustre dans la situation suivante :
Source : meshtastic.org
Dans ce cas de figure, nous disposons de quatre nœuds. Le nœud 0 émet un paquet. Les nœuds 1 et 2 se trouvant dans la portée d'émission du nœud 0 reçoivent le paquet. Selon le protocole de Meshtastic, ils doivent maintenant attendre un certain temps attribué aléatoirement à chaque nœud avant de retransmettre. Toutefois, ce temps aléatoire est dans Meshtastic pondéré par le SNR. Le nœud 2 étant plus éloigné, il aura un SNR plus faible que le nœud 1. Son temps d'attente sera donc plus court, et il pourra retransmettre en premier. La situation est décrite également sur le deuxième schéma à droite de l'image. Le nœud 1 recevra la retransmission du nœud 2 et annulera la sienne. Ainsi, on limite l'utilisation inutile du canal de transmission mais aussi la perte d'énergie à l'émission.
En outre, le projet dispose de nombreux outils, développés par la communauté, pour simplifier l'accès au réseau pour les utilisateurs. Une fois le projet Meshtastic flashé sur votre carte électronique (nous détaillerons les méthodes pour cela dans la suite de l'article), vous pourrez très simplement contrôler votre radio grâce à une application mobile, disponible sur Android et iOS, et commencer à profiter du réseau en envoyant des messages.
Source : meshtastic.org
Il est possible d'envoyer des messages dans le canal principal, visible par tous, mais aussi d'envoyer des messages destinés à un seul autre nœud du réseau, ou encore à des canaux privés formant des groupes de discussion. Pour assurer la confidentialité et l'intégrité des messages privés, Meshtastic utilise un chiffrement de données AES256. Celui-ci utilise une clé pour le chiffrement et le déchiffrement des données. Cela permet d'assurer que seuls les membres du canal adapté, et disposant donc de la clé, pourront déchiffrer les messages. Toutefois, Meshtastic ne chiffre jamais les en-têtes des paquets, ce qui permet aux nœuds à qui le message n'est pas destiné de tout de même participer à son acheminement à la destination.
Source : meshtastic.org
4. Objectif de ce HowTo : la sonette MESH
Vous l'aurez compris, l'objectif de ce HowTo est de se baser sur les technologies et outils présentés précédemment pour réaliser une sonnette en maillage basé sur LoRa. Mais comment allons nous concrètement nous y prendre ? Quel résultat souhaitons nous obtenir?
Tout d'abord, nous utiliserons LoRa pour transmettre le message d'activation de la sonnette. Pour cela nous allons connecter à notre carte un bouton et générer une interruption pour déclencher l'envoi. L'information transitera ensuite dans un réseau en maillage, pour être acheminé jusqu'au sink. Pour cette étape, nous nous appuierons sur Meshtastic, projet open source permettant d'établir facilement un réseau en maillage. Enfin, nous verrons comment gérer l'activation d'un buzzer lors de la réception de la donnée par le sink.
Ainsi, nous obtiendrons une sonnette polyvalente, qui peut s'utiliser avec ou sans le maillage. La sonnette est utilisable en peer to peer (sans l'intermédiaire du mesh) sur de petites distances ou dans les cas d'utilisation les plus simples. On peut toutefois constater le vrai intérêt de ce système dans des cas d'utilisation plus complexe, comme un cas d'itinérance à travers différents étages d'un grand bâtiment, ou encore dans un cadre de couverture souterraine.
II - Réalisation de la sonnette
1. Installation de Meshtastic
Pour commencer la réalisation de votre sonnette, il vous faudra tout d'abord flasher le firmware du projet Meshtastic sur vos cartes. Pour cela deux solutions s'offrent à vous. La première est d'utiliser le web flasher de Meshtastic, qui vous permettra de flasher le firmware du projet Meshtastic en toute simplicité. Cette solution est toutefois réservée aux navigateurs Google Chrome et Microsoft Edge. La seconde est de cloner le dépôt git du firmware de Meshtastic pour flasher le code sur vos esp32 directement à l'aide de VS Code et Platformio.
A. Installation à l'aide du Web Flasher Meshtastic :
- Rendez vous sur la page web du flasher Meshtastic : https://flasher.meshtastic.org/ (avec navigateur compatible Chrome ou Edge)
- Branchez votre Heltec WiFi LoRa 32 V3 à votre ordinateur en USB
A l'aide de la première liste déroulante, sélectionnez la carte cible. Dans notre cas la Heltec V3.
A l'aide de la deuxième liste déroulante, sélectionnez une version de Meshtastic à flasher sur votre carte. Il est conseillé de choisir la dernière version stable (Beta) disponible. Dans notre cas, nous prendrons la version 2.3.13.83f5ba0.
Sélectionnez le port auquel est connectée votre carte Heltec puis cliquez sur Connexion. Généralement il est répertorié comme suit :
Si tout a correctement fonctionné, vous devriez voir apparaître l'écran de bienvenue de Meshtastic sur votre carte. Cet écran vous indiquera l'adresse Meshtastic, basée sur l'adresse MAC Bluetooth de votre device. Il vous invitera également à déclarer un Region Code, nécessaire pour que Meshtastic sache sur quelles fréquences et dans quelles conditions il peut émettre en LoRa :
B. Déclaration de la région :
Pour déclarer la région sur laquelle votre device doit se baser pour les normes de communication en LoRa, vous avez encore une fois plusieurs possibilités.
Tout d'abord, vous pouvez utiliser l'application Meshtastic, disponible sur Android et iOS. C'est probablement la solution la plus simple. Sinon vous aurez la possibilité d'utiliser le client Web de Meshtastic, ou enfin son outil CLI (command line interface).
Avec l'application Meshtastic :
- Téléchargez l'application Meshtastic sur votre smartphone
- Ouvrez la, vous devriez arriver sur l'onglet Bluetooth.
Si votre carte est bien branchée, vous devriez la détecter dans l'onglet Bluetooth, elle sera renseignée par son adresse MAC, la même que sur son écran.
Cliquez sur l'adresse de votre carte puis sur "Connect to new radio?"
Une fois connecté, appuyez sur "Définir la région LoRa" et selectionnez "European Union 868mhz" dans la liste des régions proposées.
- Cliquez ensuite sur "Sauvegarder" en bas de l'écran pour enregistrer votre choix.
- Si tout a fonctionné normalement votre carte va redémarrer en affichant EU_868 sur l'écran de démarrage, puis démarrer en affichant de nouvelles informations sur son écran.
Une fois cette étape réalisée, vous aurez un device Meshtastic fonctionnel ! Vous pouvez déjà prendre le temps d'explorer l'application pour découvrir les possibilités de ce réseau open source !
Avec la CLI :
Cette alternative est surtout adaptée pour les utilisateurs de Linux. Toutefois elle est tout de même compatible avec Windows et macOS.
Suivez la procédure d'installation décrite dans la documentation de Meshtastic : https://meshtastic.org/docs/software/python/cli/installation/
Quand vous aurez terminé l'installation, vous pourrez tester les différentes commandes disponibles grâce à cet outil.
En ce qui concerne le region code, vous pourrez le modifier et le passer à la region EU_868 grâce à la commande suivante :
meshtastic --set lora.region EU_868
Si la commande a fonctionné correctement, votre carte devrait redémarrer et afficher EU_868 comme region code à la place de UNSET lors de son redémarrage.
2. Ajout du code pour le bouton
A. Préparation de l'environnement de travail :
Maintenant que nos cartes font partie du réseau Meshtastic, nous allons pouvoir nous attaquer à la modification du firmware pour le end device et le sink, de manière à réaliser notre application de sonnette. Nous allons commencer par ajouter le code pour le end device, qui comprendra donc le code nécessaire à la génération d'une interruption à l'appui du bouton, ainsi que le code dédié à l'envoi d'un message spécifique sur le réseau Meshtastic, qui symbolisera l'activation de la sonnette.
Tout d'abord, assurez vous d'avoir VS Code installé sur votre ordinateur. Si ce n'est pas le cas, suivez la procédure d'installation adaptée à votre système d'exploitation disponible sur le site officiel de VS Code : https://code.visualstudio.com/docs/setup/setup-overview
Ensuite, vous allez devoir installer Platformio, une extension VS Code qui est en fait un IDE et un gestionnaire de bibliothèques adaptée au domaine de l'embarqué et de l'IoT. Cette extension simplifie la configuration des projets, la gestion des dépendances et donc la compatibilité multi-plateformes.
Pour l'installer rendez vous dans VS Code, puis cliquez sur l'onglet extensions, et recherchez Platformio :
Lorsque vous aurez trouvé la bonne extension, cliquez ensuite sur installer.
Maintenant que vous disposez d'un environnement de travail équipé de VS Code et Platformio, nous allons pouvoir cloner le dépôt git du firmware de Meshtastic que nous allons modifier. Pour cela rendez vous sur le github de Meshtastic : https://github.com/meshtastic
Cliquez sur le répertoire nommé "firmware", puis en haut à droite de la page, sur le bouton vert "Code", ou vous allez trouver l'adresse nécessaire au clonage du projet.
Lancez ensuite VS Code et ouvrez le répertoire firmware que vous venez de cloner. Dans VS Code, vous devriez voir dans la barre d'outil l'icône de PlatformIO, qui détecte automatiquement les projets compatibles à son utilisation grâce à la présence de fichier de configuration "platformio.ini". Vous serez ainsi prêt à vous lancer dans les modifications du code nécessaires à la réalisation de la sonnette !
B. Modifications au code :
1) Modification du fichier de configuration platformio.ini
Commencez par adapter le fichier de configuration platformio.ini. Ce fichier se trouve à la racine du projet. Il sert à donner à Platformio les informations nécessaires à la compilation, les librairies nécessaires, mais aussi l'architecture du microcontrôleur utilisé pour se baser sur les bonnes librairies HAL, couches d'abstraction hardware qui permettent donc d'adapter les instructions du code au matériel utilisé.
Nous allons donc commenter la première ligne de ce fichier en ajoutant un point virgule ";", puis la remplacer par la ligne suivante :
default_envs = heltec-v3
;default envs = tbeam
Cela va nous permettre de compiler le projet spécifiquement pour notre cible Heltec WiFi LoRa 32 V3
2) Configuration du GPIO et de l'interruption
Par la suite, vous allez devoir configurer un GPIO de votre choix pour y recevoir l'interruption causée par l'appui du bouton. Ici, je vais choisir le GPIO48,
Grâce au framework Arduino, vous allez constater que l'opération est très simple à mettre en place. En effet, il suffit d'appeler les fonctions pinMode() et attachInterrupt() pour atteindre cet objectif. Je décide de configurer mon GPIO en input pullup car mon bouton marque son appui par un état logique bas. Ainsi il convient de configurer le GPIO48 en pull up pour bien détecter l'interruption au niveau bas. Dans le cas d'un bouton qui envoie un état logique haut à l'appui, il convient de configurer le GPIO en input pulldown. En suivant le même raisonnement, le type d'interruption à détecter est une falling edge, ou un front descendant en français. Autrement dit, un passage du niveau 1 logique à 0 logique, de 5V à 0V. Dans le cas inverse on aurait donc configuré l'interruption en rising edge, front montant.
Voila ce que cela donne en plaçant ce code au début de la fonction setup(), pour qu'il soit éxécuté au démarrage de la carte :
pinMode(GPIO_NUM_48, INPUT_PULLUP);
attachInterrupt(GPIO_NUM_48, buttonPressed, FALLING);
Ainsi, nous avons demandé au GPIO48 de se mettre en mode entrée, de chercher à détecter un front descendant, et lorsque cela arrive, de déclencher une fonction, que l'on appelle un callback. Ici nous avons appelé notre callback buttonPressed. Nous allons donc maintenant devoir définir le comportement adéquat lors de la détection de l'appui, en écrivant la fonction buttonPressed() dans le code.
3) Ajout de la fonction d'envoi
Pour réaliser un envoi en LoRa vous allez devoir effectuer 3 étapes : allouer la mémoire nécessaire pour un paquet, basé sur la structure de donnée utilisée par le projet Meshtastic, le remplir de données au niveau du header pour déterminer son comportement, puis au niveau du payload pour envoyer une donnée qui symbolise l'alerte. Et enfin, l'envoyer au mesh, c'est à dire l'ajouter à une queue qui stocke tous les paquets destinés à être envoyés. Il sera envoyé à la prochaine itération de la fonction loop du projet, si certaines conditions sont réunies.
Placez vous dans main.cpp, et commencez votre fonction par la déclaration d'une chaîne de caractère spécifique qui vous servira de signal d'activation de la sonnette. Ici, je choisis la chaîne de caractère "DINGDONG", comme dans l'article précédent. N'hésitez pas à visiter l'article "Réaliser une sonnette connectée LoRa avec ChirpStack".
Je commence donc ma fonction par la ligne suivante :
const char *message = "DINGDONG";
Par la suite, je vais utiliser une fonction de la classe Router, allocForSending(), qui va se charger d'allouer la mémoire nécessaire au stockage du paquet, mais aussi remplir certains de ces champs, notamment la destination qui sera par défault un broadcast, c'est à dire un message destiné à tout les autres nœuds. C'est cette fonction qui va également définir la hop_limit, c'est à dire le nombre maximal de retransmissions, ou l'id du paquet, qui sera unique, généré par une fonction dédiée generatePacketId(). Elle renverra donc une instance de paquet de classe meshtastic_MeshPacket :
meshtastic_MeshPacket *mon_paquet = router -> allocForSending(); //On utilise une instance de router déclarée dans le main
Maintenant que notre paquet est alloué, nous allons maintenant remplir notre payload de données, et déclarer que le type de paquet est un "TEXT_MESSAGE" via le numéro de port decoded.portnum, cela va permettre aux autres devices de comprendre ce paquet comme un message textuel et donc de traiter correctement sa réception :
mon_paquet->decoded.payload.size = strlen(message);
if (mon_paquet->decoded.payload.size >= sizeof(mon_paquet->decoded.payload.bytes)){
memcpy(mon_paquet->decoded.payload.bytes, message, mon_paquet->decoded.payload.size);
}
else {
LOG_ERROR("Erreur à l'allocation du paquet");
}
mon_paquet->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
Enfin, une fois votre paquet construit, envoyez le à travers le maillage grâce à la fonction sendToMesh() :
service.sendToMesh(mon_paquet);
Une fois ces différentes étapes réalisés, à quelques noms de variables près, vous devriez vous retrouver avec une fonction de ce type :
Pour pouvoir utiliser cette fonction, n'oubliez pas de déclarer son prototype dans le fichier main.h comme suit :
void sendAlarm(void);
4) Ajout de la fonction callback
Enfin, pour finaliser votre logique d'envoi par interruption, vous allez devoir ajouter une fonction callback qui permet d'appeler la fonction d'envoi de manière non bloquante. Laissez moi vous expliquer. Les microcontrôleurs ont de multiples tâches à réaliser, et ne peuvent pas exécuter des instructions en parallèle. Une instruction à la fois par cœur. Les esp32 dont nous disposons possèdent 2 cœurs, mais l'idée reste la même. Pour gérer une multitude de tâches simultanément sans s’emmêler les pinceaux, les microcontrôleurs se basent sur ce que l'on appelle l'ordonnancement. Un concept qui va définir le temps à passer sur chaque tâche pour un microcontrôleur, et dans quel ordre le faire.
Quelque soit le type d'ordonnancement utilisé dans un projet, une règle de se maintient toujours : toute fonction de callback déclenchée par la détection d'une interruption est prioritaire, et bloquante, c'est à dire qu'elle empêche les autres tâches qui étaient en cours de continuer leur exécution. Ainsi, la convention en programmation embarquée est de réaliser des callbacks très courts, en terme d'instructions et de temps d’exécution, de manière à ne pas trop perturber le cycle d’exécution de tâches du microcontrôleur. Les callback ont ainsi plus tendance à modifier le comportement d'un programme en modifiant des flags qui seront perçus et modifieront le code dans son cycle d’exécution normal.
Déclarer notre fonction d'envoi comme callback interromprait l'ensemble du programme pendant un temps suffisamment long pour générer des erreurs d'ordonnancement du processeur. C'est pourquoi nous allons utiliser une fonction spécifique de callback, qui se charge de changer l’état d'une variable booléenne. Ce changement d'état sera perçu à la prochaine itération du loop, et déclenchera donc notre envoi, cette fois-ci de manière non bloquante et sans risques d'erreurs d'ordonnancement.
Nous allons donc gérer l'activation de votre fonction d'envoi grâce à la fonction de callback. Pour ce faire, nous allons utiliser une variable booléenne que vous allez devoir commencer par déclarer au début du main :
bool alarme = false;
Par la suite nous allons écrire la fonction de callback qui va passer ce booléen à l'état true. En outre vous allez ajouter quelques instructions pour "debouncer" le bouton. En effet, les boutons en électronique fonctionnent en laissant passer ou en arrêtant de laisser passer des signaux électriques.
Source : circuits-diy.com, ESP32 Tutorial Buttun Debounce
Il arrive donc que l'appui d'un bouton produise plusieurs interruptions pour le microcontrôleur, à cause de ce concept de bounce. Pour s'affranchir de cette imprécision, vous pouvez utiliser de nombreuses méthodes, mais pour cette application j'ai choisi d'utiliser un blocage dépendant du temps.
Commencez par déclarer une seconde variable au début du main :
unsigned long lastInterrupt = 0;
Vous allez pouvoir ensuite utiliser la fonction millis() pour construire une condition pour le changement d'état du booléen. Voici à quoi cela devrait ressembler :
void buttonPressed(){
if (millis() - lastInterrupt > 200){
alarme = true;
lastInterrupt = millis();
}
}
Veillez à bien nommer cette fonction de callback avec le même nom que celui que vous avez renseigné dans la fonction attachInterrupt() un peu plus tôt, et placez la à la fin de votre main.cpp. Enfin, pour pouvoir utilisez cette nouvelle fonction, vous allez devoir ajouter son prototype dans main.h. Ajoutez cette ligne :
void buttonPressed(void);
5) Appel de la fonction d'envoi dans le loop
Pour finir cette étape de la réalisation de votre sonnette, vous allez devoir ajouter une condition dans le loop qui s'occupera d'appeler sendAlarm() quand l'interruption aura été détectée. Ajoutez la condition suivante à votre code :
if(alarme){
sendAlarm();
alarme = false;
}
6) Flash du code sur une des cartes Heltec
Pour terminer cette étape de réalisation de notre bouton, il suffit désormais de compiler et flasher le code sur notre carte. Pour cela, commencez par déclencher un build grâce à platformio en appuyant sur ce bouton en bas de l'écran :
Assurez vous ensuite que votre carte est bien branchée en USB à votre ordinateur, puis déclenchez l'upload. Platformio va scanner les appareils connectés en USB et déclencher le flash à la détection d'un port adapté :
3. Ajout du code pour le buzzer
Nous allons maintenant procéder aux modifications pour la dernière carte, celle qui jouera le rôle de sink, c'est à dire de récepteur/consommateur de la donnée. Pour cela nous allons tout d'abord ajouter le code de configuration du GPIO que nous utiliserons pour notre buzzer, et ensuite ajouter le code de vérification des données reçues qui activera au besoin le déclenchement de l'alarme.
Vous pouvez pour cette étape conserver le même projet que pour la précédente, étant donné que les modifications appliqués précédemment n’interfèrent pas avec celles que nous allons ajouter maintenant. Vous pouvez aussi cloner une seconde fois le projet Meshtastic pour différencier les deux versions. Enfin vous pouvez si vous le souhaiter supprimer les modifications liées au end device et coder au même endroit.
Commencez par configurer comme sortie le GPIO de votre choix. Ici, je vais utiliser le GPIO 47 :
pinMode(GPIO_NUM_47, OUTPUT);
digitalWrite(GPIO_NUM_47, LOW);
Assurez vous également que notre sortie est bien à l'état bas à l'initialisation du programme.
Par la suite vous allez pouvoir procéder à l'ajout du code responsable de l'activation du buzzer. Pour cela, rendez vous dans la fonction handleReceived() de la classe Router, dans le fichier Router.cpp. Dans cette fonction, Meshtastic appelle perhapsDecode() sur le paquet passé en paramètres. Cela va permettre de décoder le paquet si ce n'est pas déjà fait par d'autres fonctions. Après la boucle if induisant différentes instructions en fonction du résultat de perhapsDecode(), ajoutez une vérification sur le payload de données pour savoir si il contient votre chaîne de caractères représentative de l'alarme. Dans mon cas je vais donc ajouter une vérification qui compare mon payload de données à la chaîne "DINGDONG" :
Une fois cela fait, ajouter à l'intérieur de votre boucle if ces trois lignes de code, qui vont vous permettre d'allumer le buzzer à la détection de l'alarme :
if (p->decoded.payload.bytes[0] == 0x44 &&
p->decoded.payload.bytes[1] == 0x49 &&
p->decoded.payload.bytes[2] == 0x4E &&
p->decoded.payload.bytes[3] == 0x47 &&
p->decoded.payload.bytes[4] == 0x44 &&
p->decoded.payload.bytes[5] == 0x4F &&
p->decoded.payload.bytes[6] == 0X4E &&
p->decoded.payload.bytes[7] == 0x47 ){
digitalWrite(47,1);
delay(50);
digitalWrite(47,0);
}
Choisissez un temps d'allumage de buzzer qui vous convient grâce à la fonction delay. Celle ci fait attendre le microcontrôleur pendant un certain nombre de millisecondes. Ici j'ai choisi 50 millisecondes de temps d'alarme mais vous pouvez modifier cette valeur à votre guise.
Pour terminer cette étape, il ne vous reste plus qu'à compiler votre code puis à l'uploader, comme à l'étape précédente. Suivez pour cela la même méthode que précédemment, en utilisant les taches PlatformIO : Build et PlatformIO : Upload.
4. Utilisation de la sonnette
Dès que vous aurez terminé la partie précédente, vous serez en mesure de tester votre sonnette. Une fois que vous vous serez assuré d'avoir bien connecté votre bouton à votre end node, et votre buzzer à votre sink, vous pourrez alimenter les deux cartes et vérifier le bon fonctionnement de l'application. La distance de fonctionnement estimée de la sonnette peer to peer est d'environ 300mètres.
Par la suite, vous pourrez ajouter les intermédiaires, pour construire la sonnette en maillage. Ainsi, vous allez démultiplier la portée de fonctionnement de votre sonnette, mais aussi avoir la possibilité d'établir des chemins de communication adaptables à des contextes particuliers.
Le premier exemple de ces chemins adaptables est l'itinérance. Dans des grandes maisons ou grands bâtiments, il est souvent difficile de recevoir les ondes liées à la télécommunication à tous les étages. Cela peut être du à la distance entre les étages mais cela est souvent accentué par l’atténuation causée par l'épaisseur des murs et des sols des bâtiments. Notre mesh permet de résoudre ce problème, en plaçant tout simplement un intermédiaire à chaque étage d'une habitation, on garantit le bon acheminement de l'information dans l'ensemble de la propriété. En outre, ce type d'utilisation de notre système met aussi en avant la capacité de la sonnette à fonctionner de manière mobile. Vous pourrez ainsi vous déplacer partout dans votre habitation, et votre sink sera fonctionnel à tous les étages pour vous prévenir de l'activation de votre sonnette.
En outre, cette sonnette pourra fonctionner dans des applications souterraines. Les milieux souterrains sont connus pour être difficile à couvrir. Plusieurs raisons contribuent à cela, l'absorption causée par les matériaux, plus conséquente dans les sous-sols, la réflexion des ondes, la perte de signal plus élevée qu'en surface, ou encore la difficulté d’établissement de ligne de vue (ligne droite entre émetteur et récepteur). Grâce à la robustesse de la modulation LoRa, la transmission est donc moins sensible aux difficultés causées par l'aspect souterrain, et enfin grâce à la possibilité de s'approcher le plus possible des lignes de vues grâce à nos intermédiaires, notre solution est parfaitement adaptée aux milieu souterrains.
On peut aisément imaginer des couvertures qui permettraient à notre application de couvrir l'ensemble de certaines grottes touristiques par exemple. Voici une possibilité de couverture de la grotte de Lascaux, aux dimensions assez modestes (250 mètres de long). L'efficacité de l'alliance de la technologie LoRa pour s'affranchir des difficultés de transmission induites par ce type de milieux est très intéressante.
III. Conclusion
Finalement, nous avons vu au long de cet article comment réaliser une sonnette basée sur la technologie LoRa et sur la topologie mesh. Cela nous a permis de découvrir ce type de topologie et leurs avantages à travers le projet open-source Meshtastic. En outre nous avons pu évoquer certains contextes d'application particuliers de ces réseaux en maillage, qui vous donnerons peut-être des idées pour de futures réalisations IoT. Enfin, vous pouvez si vous le souhaitez modifier, améliorer la sonnette ou encore l'étendre grâce à l'ajout d'appareils compatibles avec Meshtastic !