Linux Embedded

Le blog des technologies libres et embarquées

Réaliser une sonnette connectée LoRa avec ChirpStack

Prérequis

Materiel

  • 1x ordinateur ( ici sous Ubuntu 20.04.6 LTS )
  • 1x Gateway LoRaWAN ( ici réalisé avec une RAK7248)
  • 2x carte de dévelopement compatible LoRaWAN ( ici réalisé avec des LoRa-E5 mini )
  • 1x Programmateur ST-LINK ( ici réalisé avec une carte NUCLEO MB1136 )
  • 1x buzzer monotonique ( ici réalisé avec un DFR0032 )
  • 1x bouton poussoir

     

Logiciel sur l'ordinateur

  • Navigateur web ( ici réalisé avec Firefox 115.0.2 (64-bit) )
  • Docker ( ici réalisé avec la version 24.0.4, build 3713ee1 )
  • Docker compose ( ici réalisé avec la version 2.19.1 )
  • STM32CubeIDE ( ici réalisé avec la version 1.12.0 )

 

 

Plan

  1. I - Intro
  2. II - Théorie
    1. 1. Qu'est-ce qu'un objet connecté ?
    2. 2. Qu'est-ce que LoRaWAN ?
    3. 3. Notre matériel
      1. a. Le serveur
      2. b. La gateway
      3. c. Le bouton
      4. d. La sonnerie
      5. e. Matériel supplémentaire
  3. III - Création de la sonnette
    1. 1. Mise en place du serveur ChirpStack avec docker compose
      1. a. Téléchargement
      2. b. Configuration de la region
      3. c. Lancement
      4. d. Interface web
    2. 2. Mise en place de la gateway
      1. a. Connexion ssh
      2. b. Address de notre ordinateur
      3. c. Configuration de la gateway
      4. d. ChirpStack
    3. 3. Configuration du bouton connecté
      1. a. Créer le projet
      2. b. Modifier le projet
        1. i. Version & région
        2. ii. Class et connexion
        3. iii. Identifiants
        4. iv. Programme
      3. c. Programmation de la carte LoRa-E5 mini
      4. d. ChirpStack
    4. 4. Configuration de la sonnerie connectée
      1. a. Projet
        1. i. Variables
        2. ii. Programme
      2. b. Programmation de la carte LoRa-E5 mini
      3. c. ChirpStack
    5. 5. Application : script Python de contrôle

 

 

I - Intro

Dans cet article, nous allons voir comment créer une sonnette connectée avec la technologie LoRaWAN à l'aide d'un serveur ChirpStack.

Il s'agit d'une introduction pour vous familiariser avec des outils et technologies pouvant sembler austères, mais qui, une fois connues, permettent de créer facilement et rapidement des objets connectés.

Vous pourrez ainsi vous familiariser avec LoRaWAN, ChirpStack, STM32 et paho MQTT.

II - Théorie

1. Qu'est-ce qu'un objet connecté ?

Un objet connecté doit comporter 4 composants :

  • Composant d'interaction : tel qu'un capteur ou un actionneur, il permet à l'objet de mesurer un paramètre de son environnement ou d'agir dessus.
  • Composant de communication : avec ou sans fil
  • Composant de contrôle : dans le cas d'un capteur connecté, il s'assure de l'acquisition de données et de l'envoi de ces dernières par le composant de communication. Dans le cas d'un actionneur connecté, il s'assure de l’exécution d'ordres reçus par le composant de communication. Il est courant que ce module effectue un pré-traitement de l'information. Dans la plupart de cas, réalisé sous forme d'un microcontrôleur (MCU).
  • Composant d'alimentation : chargé de s’assurer de l'alimentation électrique de l'objet connecté, il peut s’agir d'un transformateur branché sur secteur, d'un contrôleur de batterie avec une batterie ou encore d'un régulateur connecteur à un panneau solaire ou autre source d'énergie.

Les objets font partie de ce qui s'appelle l' "Edge", ils sont à la frontière entre le système informatique et le monde physique. L'Edge est souvent lié au Cloud, le "point de rassemblement" des données remontées par des objets connectés, ainsi qu'un point de contrôle central.

Dans le cas où la communication des objets est faite avec un protocole incompatible avec Internet, mais qu'il est nécessaire de communiquer via Internet, une gateway est introduite dans le système. Le but de la gateway est alors de traduire ou d'emballer l'information de façon à ce qu'elle puisse transiter par le réseau IP. Cela peut être nécessaire pour établir la communication entre l'Edge et le Cloud.

2. Qu'est-ce que LoRaWAN ?

LoRaWAN est un standard de communication basé sur la technologie LoRa qui est une technologie de communication sans fil par ondes radio. La technologie LoRa fait partie des technologies dites LPWAN (Low Power Wide Area Network). Cela signifie que LoRa, et par extension LoRaWAN, permet une communication à longue portée pour un moindre coût énergétique, caractéristiques importantes pour des objets connectés alimentés par batteries.

Dans la chaîne de communication LoRaWAN on peut trouver plusieurs éléments :

  1. Les objets (end devices) : nos objets connectés, ils sont en bout de chaîne.
  2. Les passerelles (Gateways) : les antennes relais permettant de capter et emmètre les communications avec les objets, elles relaient ces informations.
  3. Le serveur réseau (Network Server) : il est chargé d'administrer les Gateways.
  4. Le serveur d'applications (Application Server) : point d'entrée qui assure la communication avec l'application finale qui consomme les données produites par les objets connectés. Les donnés fournies par les serveur d'application sont "prêtes à consommer" : fournies déjà déchiffrés et décodés et sans duplications à cause de réception éventuelle du même message par plusieurs Gateways LoRaWAN. Le serveur d'applications rend transparent tous les aspects techniques liés aux communications LoRa dans le réseau : l'application se connecte au serveur directement en MQTT.

Chaque région du monde régule différemment les restrictions d'émissions radio. Même si LoRa opère dans les bandes dites "libres" (qui ne nécessitent pas de licence), il est nécessaire de respecter la réglementation locale qui définit certaines limites pour les communications dans les lesdites bandes (taux d'occupation du canal, puissance maximale autorisée, largeur du canal autorisée...). Le standard LoRaWAN définit donc des régions ou "Channel Plans", chacun permettant de respecter les régulations en vigueur localement. Vous pouvez vous référer directement au document de la norme LoRaWAN intitulé "LoRaWAN® Regional Parameters" disponible en libre accès pour savoir quel "Channel Plan" vous concerne.

Il nous faudra aussi nous préoccuper du mode d'activation pour des objets LoRaWAN. Il en existe deux : ABP et OTAA. Nous utiliserons OTAA, la majeure différence est qu'ABP ne vérifie pas la disponibilité du réseau et considère l'objet comme connecté par défaut là où OTAA vérifie la connexion.

Enfin, un objet LoRaWAN peut faire partie d'une de ces trois classes :

  1. Class A : peut envoyer de l'information à tout moment, mais n'en reçois qu'après en avoir envoyé, très économique en énergie
  2. Class B : peut recevoir et envoyer de l'information, mais qu'a intervalle fixes, pas très économique en énergie
  3. Class C : peu recevoir et envoyer de l'information à tout moment, coûteuse en énergie.

3. Notre matériel

Nous allons réaliser une sonnette connéctée, ce sera l'occasion de parler de capteur (le bouton) et d'actionneur (la sonnerie).
LoRaWAN oblige, nous aurons donc une structure de sonnette comme sur le schéma suivant :

a. Le serveur

Nous utiliserons ChirpStack. Il s'agit d'un serveur LoRaWAN clef en main et open source qui regroupe les serveurs application et réseau. ChirpStack ne comprend donc pas l'application, il s'occupe uniquement de la communication et ne gère pas l'aspect application.

Pour l'aspect application, nous utiliserons un script Python s’interfaçant avec le serveur ChirpStack pour recevoir les informations de notre bouton et donner les ordres à envoyer à notre sonnerie.

b. La gateway

Nous utiliserons ici une gateway RAK7248

c. Le bouton

Pour réaliser la partie "capteur connecté" (bouton de sonnette dans notre cas), nous utiliserons une carte LoRa-E5 mini, carte sur base d'un STM32WLE5JC, SoC intégrant les composants de communication (module LoRa SX126X) et de contrôle (ARM Cortex M4).
Pour assurer le rôle de composant d'interaction, nous utiliserons un bouton connecté comme montré ci-dessous.
Enfin pour assurer l'alimentation, un simple câble USB connecté à notre PC suffit.

d. La sonnerie

Là encore, pour réaliser "l'actionneur connecté" (sonnette elle même dans notre cas) nous utiliserons une carte LoRa-E5 mini.

Cette fois-ci, pas besoin de bouton, mais de quoi faire une sonnerie. Pour faire simple, nous pouvons utiliser un buzzer mono tonalité, ne permettant que d'émettre un simple son constant. Le buzzer DFR0032 peu parfaitement fonctionner ici, il est simple d'usage et de connexion.

e. Matériel supplémentaire

Pour programmer les cartes LoRa-E5 mini, il est nécessaire d'utiliser un programmateur. Ici, nous utiliserons le programmateur ST-LINK intégré à une carte STM32 Nucleo.

III - Création de la sonnette

1. Mise en place du serveur ChirpStack avec docker compose:

Il est possible d'installer ChirpStack directement sur votre ordinateur, cependant, il est beaucoup plus simple d'utiliser Docker compose pour le faire. Cela vous évitera de rentrer dans des détails techniques qui ne nous intéressent pas pour notre usage de sonnette. Pour plus d'informations, vous pouvez aller voir la page ChirpStack de Docker compose.

a. Téléchargement

Vous pouvez directement télécharger le code qui nous intéresse avec la commande suivante :

moi@mon-pc:~/.../la_ou_je_travaille$ git clone https://github.com/chirpstack/chirpstack-docker.git

À l'heure de la rédaction de cet article, le dernier commit a eu lieu le 03/07/2023 et porte le SHA "c34c1007014e60ac161a16d8810cadd60f6bb09c".

b. Configuration de la region

La région ou "Channel Plan" est une spécificité de LoRaWAN comme précédemment expliqué. Réalisant cet article en France, j'utiliserais la région LoRaWAN EU868.

Dans le répertoire "configuration/chirpstack" ouvrez le fichier "chirpstack.toml".
Cherchez alors la variable "enabled_regions".

# Network related configuration.
[network]

  # Network identifier (NetID, 3 bytes) encoded as HEX (e.g. 010203).
  net_id="000000"

  # Enabled regions.
  #
  # Multiple regions can be enabled simultaneously. Each region must match
  # the 'name' parameter of the region configuration in '[[regions]]'.
  enabled_regions=[
    "as923",
    "as923_2",
    "as923_3",
    "as923_4",
    "au915_0",
    "cn470_10",
    "cn779",
    "eu433",
    "eu868",
    "in865",
    "ism2400",
    "kr920",
    "ru864",
    "us915_0",
    "us915_1",
  ]

Vous pouvez alors simplement "commenter", c'est-à-dire rendre ignorable les lignes qui ne nous intéressent pas en les précédant d'un "#", pour ma part, je conserverais donc la ligne où est écrit "eu868".

# Network related configuration.
[network]

  # Network identifier (NetID, 3 bytes) encoded as HEX (e.g. 010203).
  net_id="000000"

  # Enabled regions.
  #
  # Multiple regions can be enabled simultaneously. Each region must match
  # the 'name' parameter of the region configuration in '[[regions]]'.
  enabled_regions=[
  # "as923",
  # "as923_2",
  # "as923_3",
  # "as923_4",
  # "au915_0",
  # "cn470_10",
  # "cn779",
  # "eu433",
    "eu868",
  # "in865",
  # "ism2400",
  # "kr920",
  # "ru864",
  # "us915_0",
  # "us915_1",
  ]

Vous pouvez alors sauvegarder et fermer le document.

c. Lancement

Nous pouvons finalement lancer le serveur, ou plutôt le docker compose le prenant en charge.
Pour ce faire, vous pouvez retourner à la racine de notre dossier et lancez la commande suivante :

moi@mon-pc:~/.../la_ou_je_travaille/chirpstack-docker-master$ docker compose up

Vous devriez alors voir le terminal défiler et au tout début voir ce qui suit :

moi@mon-pc:~/.../chirpstack-docker-master$ docker compose up
[+] Running 6/0
 ✔ Container chirpstack-docker-master-redis-1                            Created                                                                                                  0.0s 
 ✔ Container chirpstack-docker-master-postgres-1                         Created                                                                                                  0.0s 
 ✔ Container chirpstack-docker-master-mosquitto-1                        Created                                                                                                  0.0s 
 ✔ Container chirpstack-docker-master-chirpstack-gateway-bridge-eu868-1  Created                                                                                                  0.0s 
 ✔ Container chirpstack-docker-master-chirpstack-1                       Created                                                                                                  0.0s 
 ✔ Container chirpstack-docker-master-chirpstack-rest-api-1              Created                                                                                                  0.0s 
Attaching to chirpstack-docker-master-chirpstack-1, chirpstack-docker-master-chirpstack-gateway-bridge-eu868-1, chirpstack-docker-master-chirpstack-rest-api-1, chirpstack-docker-master-mosquitto-1, chirpstack-docker-master-postgres-1, chirpstack-docker-master-redis-1
...

Vous devriez aussi voir beaucoup de lignes semblables à la suivante

chirpstack-docker-master-chirpstack-1                       | 2023-07-12T07:39:18.886695Z  INFO chirpstack::region: Setting up regions

Si aucune ligne n'indique "error", tout est bien lancé. Vous pouvez mettre le terminal de coté, ne le fermez surtout pas.

N.B.: Il est possible de faire tourner le conteneur en question en arrière-plan en utilisant l'option -d dans la commande de docker compose. Cependant, nous aurons besoin par la suite de consulter les logs liés au conteneur en live, nous laisserons donc le terminal ouvert.

d. Interface web

Maintenant que le serveur est lancé, nous pouvons déjà jeter un rapide coup d'œil à son interface web. Rendez-vous sur le navigateur web de votre choix à l'adresse http://localhost:8080/#/login.

Les identifiant par défaut sont "admin" "admin".

Vous devriez alors arrivez sur la même page que ci-dessous.

Nous pouvons consulter les onglets de gauche "Network Server -> Dashboard" et "Tenant -> Dashboard".

Comme nous pouvons le constater, nous n'avons rien : ni devices (objets connectés), ni gateway ni quoi que ce soit. Nous allons donc pouvoir commencer à ajouter les éléments nécessaires.

2. Mise en place de la gateway RAK

Passons maintenant à la gateway.

a. connexion ssh

Il va tout d'abord falloir nous connecter à la gateway, étant d'usine prête à l'usage, mais non paramétrée spécifiquement pour nous.

Par défaut la gateway crée un réseau Wifi au nom de "Rakwireless_XXXX" avec pour mot de passe "rakwireless". Nous allons donc nous y connecter.

Nous pouvons alors dans un nouveau terminal lancer la commande :

moi@mon-pc:~$ ssh pi@192.168.230.1
pi@192.168.230.1's password: 

Le mot de passe par défaut est alors "raspberry", entrez le.

moi@mon-pc:~ $ ssh pi@192.168.230.1
pi@192.168.230.1's password:  
Linux rak-gateway 5.10.103-v7l+ #1529 SMP ddd mmm n hh:mm:ss TTT yyyy xxxxxx

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.

==========================================================
 ______  ___   _   __  _    _ _          _  IoT Made Easy
 | ___ \/ _ \ | | / / | |  | (_)        | |              
 | |_/ / /_\ \| |/ /  | |  | |_ _ __ ___| | ___  ___ ___ 
 |    /|  _  ||    \  | |/\| | | '__/ _ \ |/ _ \/ __/ __|
 | |\ \| | | || |\  \ \  /\  / | | |  __/ |  __/\__ \__ \
 \_| \_\_| |_/\_| \_/  \/  \/|_|_|  \___|_|\___||___/___/

==========================================================
Last login: ddd mmm n hh:mm:ss yyyy from 192.168.230.xxx

SSH is enabled and the default password for the 'pi' user has not been changed.
This is a security risk - please login as the 'pi' user and type 'passwd' to set a new password.
pi@rak-gateway:~ $

Nous sommes connectés. Conservez ce terminal pour la suite, j'en parlerai comme le "terminal de la gateway".

b. addresse de notre ordinateur

Ouvrez un nouveau terminal sur votre PC et lancez la commande qui suit :

moi@mon-pc:~$ ifconfig
...
enp...: flags=x<X>  mtu xxxx
        inet xxx.xxx.xxx.xxx  netmask xxx.xxx.xxx.xxx  broadcast xxx.xxx.xxx.xxx
        inet6 xxxx:xxxx:xxxx:xxxx:xxxx:xxxx  prefixlen x  scopeid x
        ether xx:xx:xx:xx:xx:xx  txqueuelen xxxx
        RX packets x  bytes x
        RX errors x  dropped x  overruns x  frame x
        TX packets x  bytes x
        TX errors x  dropped x overruns x  carrier x  collisions x

enx...: flags=x<X>  mtu xxxx
        ether xx:xx:xx:xx:xx:xx  txqueuelen xxxx
        RX packets x  bytes x
        RX errors x  dropped x  overruns x  frame x
        TX packets x  bytes x
        TX errors x  dropped x overruns x  carrier x  collisions x

lo: flags=x<X>  mtu xxxx
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen x  scopeid x
        loop  txqueuelen x
        RX packets x  bytes x
        RX errors 0  dropped x  overruns x  frame x
        TX packets x  bytes x
        TX errors x  dropped x overruns x  carrier x  collisions x

...

wl...: flags=x<X>  mtu xxxx
        inet 192.168.230.xxx  netmask 255.255.255.0  broadcast 192.168.230.255
        inet6 xxxx:xxxx:xxxx:xxxx:xxxx:xxxx  prefixlen x  scopeid x
        ether xx:xx:xx:xx:xx:xx  txqueuelen xxxx
        RX packets x  bytes x
        RX errors x  dropped x  overruns x  frame x
        TX packets x  bytes x
        TX errors x  dropped x overruns x  carrier x  collisions x

Nous allons chercher l'adresse ip de notre ordinateur sur le réseau Wifi de la gateway. Nous faisons cela afin de pouvoir indiquer à la gateway où se trouve le serveur réseau LoRaWAN.

Nous cherchons donc un paragraphe dont le nom commence par "wl". Une fois trouver, il devrait se trouver dans ce paragraphe le mot "inet" succéder d'une adresse IP, sous la forme "192.168.230.xxx". Notez bien de coter cette adresse, vous pouvez fermer ce terminal.

 

Nous pouvons maintenant retourner dans le terminal de la gateway.
Nous pouvons tester si l'IP de notre ordinateur semble correct comme suit :

pi@rak-gateway:~ $ ping 192.168.230.xxx
PING 192.168.230.xxx (xxx.xxx.xxx.xxx) 56(84) bytes of data.
64 bytes from 192.168.230.xxx: icmp_seq=1 ttl=64 time=0.378 ms
64 bytes from 192.168.230.xxx: icmp_seq=2 ttl=64 time=0.564 ms

Si aucune ligne finissant par un temps en milli secondes (ms) n'apparaît, vous avez dû vous tromper d'IP.

c. configuration de la gateway

Maintenant, que nous avons l'adresse IP de notre ordinateur, nous pouvons passer à la configuration de la gateway.

Lancez simplement la commande suivante :

pi@rak-gateway:~ $ sudo gateway-config

La fenêtre qui suit devrait apparaître dans le terminal.

Avec les flèches de votre clavier, sélectionnez la 2e option "Setup RAK Gateway Channel Plan" puis appuyez sur "Entrer".

Entrez dans la 2e option "Server is ChirpStack".

Entrez dans la 1re option "ChirpStack Channel-plan configuration".

Sélectionnez la région LoRaWAN que vous utilisez, pour ma part, ce seras donc "EU_863_870", les noms ne collent pas totalement à ceux utilisées par ChirpStack, mais vous remarquerez que 868 est compris entre 863 et 870.

Vous pouvez maintenant recopier l'adresse IP de votre ordinateur.

Une fois entré, cela vas prendre quelques secondes puis finir.

Vous pouvez entrer, puis quitter le menu à l'aide des flèches latérales.

Une fois de retour dans le terminal de la gateway elle-même, nous allons chercher l'identifiant de la gateway.

Il nous suffit de taper la commande suivante :

pi@rak-gateway:~ $ gateway-version 
Raspberry Pi 4 Model B Rev 1.1, OS "10 (buster)", 5.10.103-v7l+.
RAKWireless gateway RAK7248 no LTE version 4.2.7R install from firmware.
Gateway ID: XXXXXXXXXXXXXXXX.
pi@rak-gateway:~ $ 

L'identifiant de notre gateway se situe alors juste après "Gateway ID:"

Maintenant, nous devrions en avoir fini avec la gateway en elle-même, garder cependant le terminal ouvert jusqu'à ce que nous ayons réussi à faire apparaître notre gateway dans l'interface web de notre serveur ChirpStack.

d. ChirpStack

Retourner sur votre navigateur à l'addresse http://localhost:8080/#/login.

Toujours dans le menu de gauche, allez maintenant dans l'onglet "Tenant -> Gateways"

Nous allons pouvoir ajouter à notre serveur notre gateway en cliquant en haut à droite sur le bouton rectangulaire bleu "Add gateway".

Nous pouvons rester dans l'onglet "General" qui devrait s'être ouvert, "Tags" et "Metadata" ne nous intéresseront pas.

Donnez un nom à votre gateway, j'appellerais la mienne "ma_gateway". Vous pouvez ensuite ajouter une description si vous le souhaitez.

Le plus important, recopiez l'identifiant de votre gateway dans le champ "Gateway ID (EUI64)", sans toucher a rien d'autre.

Vous pouvez alors descendre en bas de la page et cliquer sur "Submit" pour valider l'ajout de votre gateway au serveur ChirpStack.

Si vous retournez dans l'onglet à gauche "Network Server -> Dashboard" vous pourrez voir qu'un graphe est apparu dans l'encadré "Active gateways".

Après un court moment la gateway devrait se connecter, le graphe deviens vert et si vous avez bien positionné l'antenne GPS de façon à recevoir le signal GPS, sa position est automatiquement déterminée. Il peut cependant être nécessaire d'actualiser la page pour voir ces changements.

Nous avons maintenant le serveur et la gateway ! Si vous voulez vous amuser, vous pouvez ajouter plus de gateways si vous en avez plusieurs (Cela permettrait d'élargir la couverture de votre réseau LoRaWAN), sinon une seul sera amplement suffisante pour notre cas d'usage.

Vous pouvez fermer le terminal de la gateway, nous aurons besoin plus tard de la page web de ChirpStack.

3. Configuration du bouton connecté

Nous pouvons maintenant passer aux objets en eux-mêmes !

a. Créer le projet

Lancez STM32CubeIDE,

Une page comme la suivante va s'ouvrir, avec le bouton "Browse", sélectionnez simplement le dossier dans lequel le logiciel va pouvoir travailler librement sans que cela ne vous gêne. Cela est nécessaire.

 

Une fois STM32CubeIDE lancée, cliquez sur le bouton rectangulaire bleu en haut à gauche où il est écrit "Start new STM32 project". Cliquer une seule fois, cela va prendre du temps, mais au bout d'un court moment une nouvelle fenêtre devrais s'ouvrir.

 

Cliquez alors en haut à gauche sur l'onglet "Example Selector", encore une fois cela va prendre quelques instants.

Juste au-dessous des onglet, en haut à gauche toujours, dans le champ "Name", renseignez "LoRaWAN_End_Node" puis entrer.

En bas, sélectionnez alors l'Exemple nommé "LoRaWAN_End_Node" qui montre sur la partie haute de la fenêtre une carte NUCLEO nommée WL55IC2.

Cliquez alors sur le bouton rectangulaire vert en bas à droite.

La fenêtre se ferme, une plus petite s'ouvre, cliquez simplement sur le bouton rectangulaire vert "Finish", après un court temps de chargement, la fenêtre se ferme et le projet s'ouvre.

Ce qui nous intéressera sera surtout le menu de fichiers de gauche nome "Project Explorer", l'éditeur de documents au milieu ainsi que la barre d'outils en haut.

b. Modifier le projet

i. version & région

Dans le "Project Explorer" à gauche, allez dans "LoRaWAN_End_Node" -> "Includes" ->/home/.../LoRaWAN_End_Node/LoRaWAN/Target". Ouvrez alors d'un double-clic le document "lorawan_conf.h".

version:

Changez la valeur de la variable LORAMAC_SPECIFICATION_VERSION en 0x01000300 comme ci-dessous:

région:

Dans la section "Region", commentez et décommettez les lignes pour rendre lisible uniquement la ligne indiquant votre région. Faites le en entourant ou non les lignes des symboles "/*" et "*/". Pour moi, cela donne :
 

N'oubliez pas de sauvegarder le fichier après chaque modification.

Rendez-vous maintenant dans "LoRaWAN_End_Node" -> "Includes" ->/home/.../LoRaWAN_End_Node/LoRaWAN/App".
Ouvrez alors d'un double-clic le document "lora_app.h".
Vérifiez la valeur de la variable ACTIVE_REGION, assurez vous que sa valeur soit bien LORAMAC_REGION_ comme ci dessous pour moi:

ii. Class et connexion

Restez dans le document "lora_app.h".

class:

Vérifiez la valeur de la variable LORAWAN_DEFAULT_CLASS, pour notre bouton, assurez vous que sa valeur soit bien CLASS_A comme ci-dessous:

connection:

Vérifiez la valeur de la variable LORAWAN_DEFAULT_ACTIVATION_TYPE, assurez vous que sa valeur soit bien ACTIVATION_TYPE_OTAA comme ci-dessous:

iii. Identifiants

Ouvrez maintenant le document "se-identity.h".
Vérifiez la valeur de la variable LORAWAN_DEVICE_EUI, assurez vous que sa valeur soit non nul comme pour moi ci-dessous:

Ayant choisi un mode de connexion OTAA, nous n'avons plus qu'à changer la variable LORAWAN_NWK_KEY. Vous pouvez mettre la valeur de votre choix, en hexadécimal et en respectant la longueur de la clef déjà existante.

Ne rien changer constitue un problème de sécurité.

iv. Programme

Nous allons maintenant pouvoir effectuer les modifications du programme nécessaires pour que le projet aie le comportement désiré de bouton.

Nous allons donc faire en sorte que :

  1. Le bouton soit connecté à la carte LoRa-E5 mini
  2. La carte LoRa-E5 mini envoie un message prédéterminé en LoRaWAN lorsque une pression du bouton est détectée.

Ouvrez le document "lora_app.c".
Vérifiez la valeur de la variable EventType, pour notre bouton, assurez vous que sa valeur soit bien TX_ON_EVENT comme ci-dessous :

Cela permet de faire en sorte que les messages LoRaWAN ne soient pas émis périodiquement, mais sur une sollicitation uniquement.

La fonction "HAL_GPIO_EXTI_Callback" va permettre, sous condition de pression de notre bouton, d'exécuter la fonction que nous allons maintenant modifier.

Avancez encore un peu jusqu'à la fonction "SendTxData". Ce projet est à la base conçue pour fonctionner avec une multitude d'autres capteurs, nous allons donc retirer tout cela. Pour ce faire nous allons commenter les déclaration de variables qui nous sont inutiles, comme ci-dessous :

#ifdef CAYENNE_LPP
    uint8_t channel = 0;
#else
//    uint16_t pressure = 0;
//    int16_t temperature = 0;
//    uint16_t humidity = 0;
//    uint32_t i = 0;
//    int32_t latitude = 0;
//    int32_t longitude = 0;
//    uint16_t altitudeGps = 0;
#endif /* CAYENNE_LPP */

Ainsi que le code suivant:

#ifdef CAYENNE_LPP
    CayenneLppReset();
    CayenneLppAddBarometricPressure(channel++, sensor_data.pressure);
    CayenneLppAddTemperature(channel++, sensor_data.temperature);
    CayenneLppAddRelativeHumidity(channel++, (uint16_t)(sensor_data.humidity));

    if ((LmHandlerParams.ActiveRegion != LORAMAC_REGION_US915) && (LmHandlerParams.ActiveRegion != LORAMAC_REGION_AU915)
        && (LmHandlerParams.ActiveRegion != LORAMAC_REGION_AS923))
    {
      CayenneLppAddDigitalInput(channel++, GetBatteryLevel());
      CayenneLppAddDigitalOutput(channel++, AppLedStateOn);
    }

    CayenneLppCopy(AppData.Buffer);
    AppData.BufferSize = CayenneLppGetSize();
#else  /* not CAYENNE_LPP */
//    humidity    = (uint16_t)(sensor_data.humidity * 10);            /* in %*10     */
//    temperature = (int16_t)(sensor_data.temperature);
//    pressure = (uint16_t)(sensor_data.pressure * 100 / 10); /* in hPa / 10 */
//
//    AppData.Buffer[i++] = AppLedStateOn;
//    AppData.Buffer[i++] = (uint8_t)((pressure >> 8) & 0xFF);
//    AppData.Buffer[i++] = (uint8_t)(pressure & 0xFF);
//    AppData.Buffer[i++] = (uint8_t)(temperature & 0xFF);
//    AppData.Buffer[i++] = (uint8_t)((humidity >> 8) & 0xFF);
//    AppData.Buffer[i++] = (uint8_t)(humidity & 0xFF);
//
//    if ((LmHandlerParams.ActiveRegion == LORAMAC_REGION_US915) || (LmHandlerParams.ActiveRegion == LORAMAC_REGION_AU915)
//        || (LmHandlerParams.ActiveRegion == LORAMAC_REGION_AS923))
//    {
//      AppData.Buffer[i++] = 0;
//      AppData.Buffer[i++] = 0;
//      AppData.Buffer[i++] = 0;
//      AppData.Buffer[i++] = 0;
//    }
//    else
//    {
//      latitude = sensor_data.latitude;
//      longitude = sensor_data.longitude;
//
//      AppData.Buffer[i++] = GetBatteryLevel();        /* 1 (very low) to 254 (fully charged) */
//      AppData.Buffer[i++] = (uint8_t)((latitude >> 16) & 0xFF);
//      AppData.Buffer[i++] = (uint8_t)((latitude >> 8) & 0xFF);
//      AppData.Buffer[i++] = (uint8_t)(latitude & 0xFF);
//      AppData.Buffer[i++] = (uint8_t)((longitude >> 16) & 0xFF);
//      AppData.Buffer[i++] = (uint8_t)((longitude >> 8) & 0xFF);
//      AppData.Buffer[i++] = (uint8_t)(longitude & 0xFF);
//      AppData.Buffer[i++] = (uint8_t)((altitudeGps >> 8) & 0xFF);
//      AppData.Buffer[i++] = (uint8_t)(altitudeGps & 0xFF);
//    }
//
//    AppData.BufferSize = i;
#endif /* CAYENNE_LPP */

Ajoutez maintenant à la fin le code suivant :

//      AppData.Buffer[i++] = (uint8_t)(altitudeGps & 0xFF);
//    }
//
//    AppData.BufferSize = i;
    memcpy(AppData.Buffer, "DINGDONG", 8);
    AppData.BufferSize = 8;
#endif /* CAYENNE_LPP */

c. Programmation de la carte LoRa-E5 mini

Maintenant notre programme modifié pour notre usage, nous pouvons désormais compiler et mettre le programme dans notre carte LoRa-E5 mini.

Pour ce faire, branchez votre programmateur et alimentez votre carte LoRa-E5 mini (voir le schéma ci-dessous).

Une fois bien connecté, toujours sur STM32CubeIDE, cliquez sur la flèche dans le rond vert en haut de la fenêtre.

Une fenêtre apparaît, appuyez simplement sur le bouton rectangulaire vert "OK" en bas a droite.

Si une fenêtre s'ouvre, parlant d'une mise à jour de ST-LINK, vous pouvez l'ignorer en cliquant sur le bouton rectangulaire blanc "No" ou faire la mise à jour.

La console devrait s'agiter puis ne plus bouger, si elle s'est arrêté comme sur l'image ci-dessous, tout devrait être bon.

d. ChirpStack

Maintenant votre objet est créé et programmé, vous devriez voir apparaître une erreur dans le terminal ou nous avons lancé ChirpStack à l'aide de la commande "docker compose up". Cela est dû au fait que notre serveur ChirpStack ne connaît pas notre objet connecté.

Retournons donc sur l'interface web de notre serveur ChirpStack.

Tout d'abord il nous faut créer un profil d'objet avant d'enregistrer notre objet, sorte de catégorie à laquelle il appartiendra.

Dans le menu de gauche, allez dans "Tenant" -> "Device profiles" puis cliquez sur le bouton rectangulaire bleu "Add device profile" en haut à droite.

Dans "General", renseignez les mêmes paramètres que ci-dessous, en revoyant la région si nécessaire :

Dans "Join (OTAA / ABP)", vérifiez bien que le champ "Device supports OTAA" soit coché.

Vous pouvez alors cliquer sur le bouton rectangulaire bleu "Submit" du bas.

Notre profil est alors enregistré.

Avant de finalement pouvoir enregistrer notre objet, il nous faut créer une "Application", dont le but est simplement de catégoriser nos objets par fonction.

Allez maintenant dans "Tenants" -> "Applications" et cliquez sur le bouton rectangulaire bleu "Add application" en haut a droite.

Il faut ensuite donner un nom à notre objet puis valider, toujours avec le bouton rectangulaire bleu "Submit" en bas de la page.

Maintenant que l'application est créée, nous allons pouvoir enregistrer notre objet. Pour cela, cliquez sur le nom de l'application, apparaissant en bleu un peu à gauche.

Maintenant il faut cliquer à droite sur le bouton rectangulaire bleu "Add device".

Donnez un nom à votre objet, pour moi, ce seras "mon 1er bouton".

Dans l'encadré "Device EUI (EUI64)", copier simplement le numéro que nous avions mis dans le fichier "se-identity.h" pour la variable "LORAWAN_DEVICE_EUI". Les virgules et espaces devraient disparaître automatiquement et ne laisser qu'une suite de chiffres sans espacements.

Cliquez sur l'encadré "Device profile" et sélectionnez simplement le profile que nous venons de créer. Ce sera donc "bouton de sonnette" pour moi.

Vous devriez avoir un résultat similaire au mien :

Cliquez alors sur le bouton rectangulaire bleu "Submit" en bas de la fenêtre.

La nouvelle page qui s'ouvre demande alors l' "Application key", copiez celle que nous avons mise dans le fichier "se-identity.h" pour la variable "LORAWAN_NWK_KEY". Pour vérifier qu'il n'y a pas d'erreur, vous pouvez cliquer sur le petit œil barré à droite du champ, pour rendre visible la valeur que vous avez copié. Encore une fois, toutes les virgules sont automatiquement supprimées et la valeur est une simple suite de chiffres et lettres en hexadécimal.

Cliquez alors sur le bouton rectangulaire bleu "Submit" en bas de la fenêtre.

Vous pouvez alors aller voir l'onglet "Events". Si vous ne voyez pas un événement "join", indiquer dans une boite bleue, appuyez simplement sur le bouton RST de votre LRa-E5 mini que vous avez programmé, au bout d'un court instant, l'événement devrais apparaître.

Si l'événement n'apparaît pas, vérifiez que vous avez réalisé correctement toutes les étapes de la partie "3. Configuration du bouton connecté".

Si vous déconnectez et reconnectez trop votre objet, il est possible que vous ayez besoin de cliquer sur "Flush OTAA device nonces" dans l'onglet "OTAA keys".

Vous pouvez maintenant tester d'appuyer sur votre bouton connecté entre les pin D0 et GND, vous devriez voir apparaître un événement "up" avec pour data "44494e47444f4e47", si vous avez gardé mon message de "DINGDONG". Si vous êtes curieux, il s'agit de la représentation hexadécimale de la chaîne de caractères "DINGDONG", codé en base64.

4. Configuration de la sonnerie connecté:

a. Projet

i. variables

Vous allez tout d'abord pouvoir commencer en répétant les mêmes étapes que pour III. "3. Configuration du bouton connecté" jusqu'à la section "iv. Programme" exclue. Cependant, quelques modifications sont nécessaires. Dans le document "lora_app.h", nous donnerons à la variable "LORAWAN_DEFAULT_CLASS" la valeur "CLASS_C". Dans le document "se-identity.h", nous donnerons à la variable "LORAWAN_DEVICE_EUI" Une valeur différente de celle donnée a cette même variable pour notre bouton. Pour moi, ce sera "23,00,00,00,00,00,00,00".

ii. Programme

Nous allons maintenant pouvoir modifier le programme de notre sonnerie.

Ce programme ne doit avoir qu'une seule responsabilité, quand un message en LoRaWAN est reçu, si celui ci est précisément le message "DINGDONG", alors un son doit être émis.

Il nous faut nettoyer un peu le programme avant de le modifier. Nous allons utiliser la "LED1" comme alarme. Il s'agit d'une réference à un pin de la carte qui a était prés configurer comme une sortie digital, le pin "SCL" de la carte. Ne vous méprenez pas, le nom du pin n'a aucune importance quand à l'usage que nous allons en faire. Il nous faut donc retirer toute utilisation inutile préexistant de cette "LED1".

Voici donc toutes les références de lignes à "commenter", c'est-à-dire à précéder de "//", ou à simplement supprimer.

HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET); /* LED_BLUE */

UTIL_TIMER_Start(&RxLedTimer);

HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET); /* LED_BLUE */

HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET); /* LED_BLUE */

HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET); /* LED_BLUE */

Rendez-vous à la fonction "OnRxData". Cette fonction sert à gérer les actions à prendre lorsque un message LoRaWAN est reçu.

Dans cette fonction, "commentez" le comportement déjà établie comme suit :

                  {
                    LmHandlerRequestClass(CLASS_C);
                    break;
                  }
                  default:
                    break;
                }
              }
              break;
            case LORAWAN_USER_APP_PORT:
//              if (appData->BufferSize == 1)
//              {
//                AppLedStateOn = appData->Buffer[0] & 0x01;
//                if (AppLedStateOn == RESET)
//                {
//                  APP_LOG(TS_OFF, VLEVEL_H, "LED OFF\r\n");
//                  HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET); /* LED_RED */
//                }
//                else
//                {
//                  APP_LOG(TS_OFF, VLEVEL_H, "LED ON\r\n");
//                  HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET); /* LED_RED */
//                }
//              }
              break;

            default:

              break;
          }
        }
      }

Vous pouvez alors ajouter juste notre nouveaux comportement, comme suit :

//                else
//                {
//                  APP_LOG(TS_OFF, VLEVEL_H, "LED ON\r\n");
//                  HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET); /* LED_RED */
//                }
//              }
            	if (appData->BufferSize == 8)
            	{
            		if(appData->Buffer[0] == 'D' &&
            				appData->Buffer[1] == 'I' &&
							appData->Buffer[2] == 'N' &&
							appData->Buffer[3] == 'G' &&
							appData->Buffer[4] == 'D' &&
							appData->Buffer[5] == 'O' &&
							appData->Buffer[6] == 'N' &&
							appData->Buffer[7] == 'G')
            		{
            			HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);
            			HAL_Delay(1000);
            			HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
            		}
            	}
              break;

            default:

              break;
          }
        }
      }

Un dernier petit détaille reste à régler, si notre sonnerie n'envoie pas de message initial en LoRaWAN alors notre gateway ne lui enverra pas les messages qui lui sont destinés, elle ne feras donc rien et notre sonnerie ne recevra jamais de messages. Un solution simple est d'envoyer un message à la fin de la connexion, ce que nous allons donc faire.
Rendez-vous à la fin de la fonction "OnJoinRequest" et ajoutez y la ligne qui suit

LmHandlerSend(&AppData, LmHandlerParams.IsTxConfirmed, false);

Comme cela :

    APP_LOG(TS_OFF, VLEVEL_H, "###### U/L FRAME:JOIN | DR:%d | PWR:%d\r\n", joinParams->Datarate, joinParams->TxPower);
  }
  LmHandlerSend(&AppData, LmHandlerParams.IsTxConfirmed, false);
  /* USER CODE END OnJoinRequest_1 */
}

b. Programmation de la carte LoRa-E5 mini

Faites exactement la même chose que dans la section III.3.c "c. Programmation de la carte LoRa-E5 mini".

c. ChirpStack

Vous pouvez encore une fois opérer de la même façon que précédemment en suivant les instructions e la section III.3.d "d. ChirpStack". Encore une fois, veillez à changer ce qui doit l'être. De plus, il sera nécessaire de créer un nouveau profile d'objet afin de pouvoir paramétrer notre sonnerie comme étant de class C.

Nous pouvons vérifier l'événement "join" pour notre sonnerie.

Une fois enregistré, nous obtenons non deux objets, tout deux dans notre application sonnette.

Nous avons donc presque fini, tout fonctionne, mais quand on appuie sur le bouton la sonnerie ne fait rien, il nous reste encore à faire en sorte que les messages envoyer par notre bouton soient réexpédiés à notre sonnerie.

5. Application : script Python de contrôle

Pour assurer ce rôle de réexpédition nous allons utiliser un script python avec la librairie paho, qui va jouer le rôle de l'Application (voir le schéma de notre système).

Le rôle de ce script seras donc simplement d'écouter les messages envoyés par le bouton et de les renvoyer à notre sonnerie.

Vous pouvez donc simplement créer un document "reexpedition.py" et l'ouvrir. Vous pouvez utiliser l'éditeur de votre choix. Vous pouvez ensuite copier le script suivant dans ce document.

import base64
import json
import time
import paho.mqtt.client as mqtt

# Données pour se connecter à l'interface MQTT de ChirpStack
NOM         = "reexpediteur"
BROKER      = "localhost"
PORT        = 1883
KEEPALIVE   = 1000
FPORT       = 2
APPLICATION = "A REMPLACER"

# Informations sur nos objets
EUI_BOUTON   = 3600000000000000
EUI_SONNERIE = 2300000000000000

# Données pour communiquer avec l'interface MQTT
TOPIC_BOUTON   = f"application/{APPLICATION}/device/{EUI_BOUTON}/event/up"
TOPIC_SONNERIE = f"application/{APPLICATION}/device/{EUI_SONNERIE}/command/down"

# Retiens le dernier message à réexpédié, encore non réexpédier.
a_reexpedier = None

# Enregistre notre script comme client MQTT, càd comme pouvant interagir avec l'interface MQTT.
my_client = mqtt.Client(NOM)




### Fonctions de gestion de l'interface MQTT
def on_connect_cb(client, userdata, flags, return_code):
    """
        Fonction appelée lors de la connexion a l'interface MQTT.
    """
    del client, userdata, flags
    if return_code == 0:
        print("Connexion établie")
    else:
        print("Échec de connexion")
        sys.exit(-1)
        
def on_disconnect_cb(client, userdata, return_code):
    """
        Fonction appelée lors de la déconnexion a l'interface MQTT.
    """
    del client, userdata
    if return_code :
        print("Erreur de connexion, connexion perdue")
    else:
        print("Déconnexion")
    
        
def connect(client):
    """
        Fonction chargée de la connexion à l'interface MQTT.
    """
    client.loop_start()
    client.connect(BROKER, PORT, KEEPALIVE)
    # Attends que la connexion soit établie
    while not client.is_connected():
        time.sleep(.1)
        
def disconnect(client):
    """
        Fonction chargée de la déconnexion à l'interface MQTT.
    """
    client.disconnect()
    client.loop_stop()


def on_message_cb(client, userdata, message):
    """
        Fonction appelée lorsque un message est reçu.
    """
    global a_reexpedier
    del client, userdata
    print("Message reçu !")
    # On prend le message dans le paquet le contenant.
    message = message.payload
    # On décode le message.
    message_decode = message.decode("utf-8")
    # On précise qu'il est écrit au format JSON.
    message_json = json.loads(message_decode)
    # On récupère uniquement la phrase écrite dans le message, on oublie le destinataire et les autres informations inintéressantes ici.
    phrase_code = message_json["data"]
    # On décode encore une fois, la phrase étant compressée.
    phrase = base64.b64decode(phrase_code)
    
    ### On peut maintenant recréer un paquet pour renvoyer la phrase à la sonnerie.
    
    mon_message = {}
    # Le message doit être envoyé à la sonnerie.
    mon_message['devEui'] = "{}".format(EUI_SONNERIE)
    # On ne demande pas d'accuser réception.
    mon_message['confirmed'] = False
    # Le message doit utiliser le port 2, comme préciser dans le projet que nous avons modifier.
    mon_message['fPort'] = FPORT
    # On n'oublie pas de mettre dans notre message la phrase codée correctement.
    mon_message['data'] = base64.b64encode(phrase).decode('utf-8')
    # Finalement, on code le message.
    message_code = json.dumps(mon_message)
    
    # On met le message de coter pour qu'il soit envoyé dès que possible.
    a_reexpedier = message_code
    
    

# Enregistre les fonctions à appelés automatiquement lors de la connexion, déconnexion et la réception d'un message
my_client.on_connect = on_connect_cb
my_client.on_disconnect = on_disconnect_cb
my_client.on_message = on_message_cb

# Connect notre client à l'interface MQTT de notre serveur ChirpStack
connect(my_client)


my_client.subscribe(TOPIC_BOUTON)

while True:
    if a_reexpedier:
        my_client.publish(TOPIC_SONNERIE,a_reexpedier).wait_for_publish()
        print("Message envoyé !")
        a_reexpedier = None    
        

Il faut cependant modifier quelques variables. Vérifiez que les variables au début intitulé "EUI_BOUTON" et "EUI_SONNERIE" sont les bonnes, elles doivent être les mêmes que les valeurs de "LORAWAN_DEVICE_EUI" de respectivement, votre bouton et votre sonnerie.

Il faut aussi indiquer dans la variable "APPLICATION" l'identifiant de votre application que vous pourrez trouver dans "Tenants" -> "Applicatios" -> "sonnette" (suivant le nom de votre application).

Il ne vous reste plus qu'à exécuter ce script python et tant que vous le laisserez, tout fonctionneras.

Pour le lancer, lancez simplement un terminal dans le même fichier que votre script et lancez la commande :

moi@mon-pc:~$ python3 reexpedition.py

Et vous devriez alors avoir des messages comme suit quand vous appuyez sur le bouton :

Remarquez aussi que la sonnerie s'active brièvement !

Conclusion

Dans cet article, nous avons vu comment créer une sonnette à l'aide du protocole de communication sans fil LoRaWAN et d'un serveur LoRaWAN appelé ChirpStack. Des détails on été donnés pour permettre de modifier et d'ajouter à cette sonnette volontairement conçue avec des composants facilement modifiables.

 

 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée.