Objets Connectés - TPs

Auteurs : Rémi Maubanc et Jawad Kadri
Classe : ESIEE I4 GRIT 2023
Professeur : M. Nicolas Dailly

TP1 - The Things Network

Mettre en place une communication LoRa

Création d'un compte TTN

Création d'un compte sur le site The Things Network:

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Création d'une application pour notre objet

Création d'une application depuis le menu :

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Informations à propos de l'application :

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Interface d'accueil de l'application nouvellement créée :

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Création d'un objet à notre application

Enregistrement d'un objet depuis le menu :

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Configuration de l'objet : les clés de communication sont générées sur le site puis copiées dans les différents fichiers de configuration et dans l'arduino pour la communication et l'authentification.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Configuration de l'arduino

Récupération des clés de communication sur l'interface de l'objet :

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Configuration de l'arduino pour la communication vers TTN : Pour commencer, il faut télécharger le code source de l'arduino sur Github. Dans le code, on peut ensuite modifier les paramètres de communication :

  1. Clé de la session de l'application (Application Session Key)
static const u1_t PROGMEM APPSKEY[16] = { 0x42, 0xB0, 0x57, 0x2E, 0xC1, 0x98, 0x4B, 0x66, 0xCD, 0x45, 0xC4, 0xAE, 0xEF, 0x68, 0x91, 0x45 };
  1. Clé de la session de connexion (Network Session Key)
static const PROGMEM u1_t NWKSKEY[16] = { 0x42, 0xDB, 0x13, 0xBA, 0xB5, 0xDF, 0x11, 0x23, 0xD6, 0x6B, 0xCD, 0x3A, 0x5C, 0xAE, 0x60, 0xE2 };
  1. Adresse de l'objet (Device Address)
static const u4_t DEVADDR = 0x260B01AE;

Dans notre cas, on relie un capteur de température et d'humidité à l'arduino. On doit pour cela initialiser et récupérer ses valeurs pour les envoyer via la connexion LoRa.

#include "DHT.h" #define DHTPIN 3 // Connecte sur la broche 3 (Digital) #define DHTTYPE DHT11 // Modele du capteur DHT dht(DHTPIN, DHTTYPE); unsigned int num_trame = 0; // Compteur de trame static uint8_t mydata[] = {0, 0, 0, 0}; // Informations envoyees via LoRa void do_send(osjob_t *j) { if (LMIC.opmode & OP_TXRXPEND) { Serial.println("OP_TXRXPEND, not sending"); } else { mydata[0] = num_trame++; // Recuperation du numero de trame puis incrementation mydata[1] = dht.readTemperatre(); // Recuperation de la temperature mydata[2] = dht.readHumidity(); // Recuperation de l'humidite LMIC_setTxData2(1, mydata, sizeof(mydata) - 1, 0); // Envoi des valeurs [...] } } void setup() { [...] dht.begin(); [...] }

Ajout d'un pont pour la rétrocompatibilité

Les shields LoRa ont une ancienne version de la norme. La fréquence utilisée est exprimé en Hertz dans la requête. Or les nouvelles normes exprime la bande de fréquence en MHz, on insert donc un bridge qui ajoute un point pour convertir les Hz en MHz. Les requêtes sont envoyées dans le localhost sur le port 7100 par l'arduino, lues par le bridge sur le port 7100 puis envoyé vers le site TTN sur le port 1700. Le bridge est disponible sur le synology (Supports Pédagogiques

> Parcours Objets Connectes
>
C03-Reseaux LPWAN
>
Ressources TP
>
BridgeDragino).

java -jar bridge.jar 7100 1700 10.130.1.235 pause # bridge.jar -> Librairie JAVA # 7100 -> port d'entree # 1700 -> port de sortie # 10.130.1.235 -> Adresse de redirection : URL ou adresse IP

Visualisation des données sur TTN

En se rendant dans l'onglet Live Data on peut visualiser les traffics entrants et sortants concernants notre objets. Par défaut, les informations utiles ne sont pas décodées, on les visualise telles qu'elles arrivent depuis le réseau LoRa.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Pour pouvoir visualiser les données en clair, on peut configurer un programme de mise en forme des données. Pour se faire, on se rend dans "Payload Formatter

> Uplink". Dans notre cas, on utilisera le langage Javascript pour mettre en forme le payload.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Dans le visualiseur, le payload brut est remplacé par son équivalent décodé. Cependant, le payload brut peut toujours être récupéré dans le message brut.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Mise en place d'un serveur d'application

Configuration de TTN

Pour pouvoir envoyer les informations à un serveur applicatif, il existe une option sur le site de TTN pour envoyer des requêtes HTTP dès qu'il recoit un paquet. Pour cela, se rendre dans "Integrations

> WebHooks" puis cliquer sur "Add Webhooks" et prendre la dernière catégorie "Custom WebHook".
Dans notre configuration, les informations en provenance de l'arduino (Uplink) seront envoyées par requête HTTP POST à l'adresse http://grit.esiee-amiens.fr:8060/add, tous les autres informations ne nous intéressent pas, c'est pourquoi on ne les coche pas.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Configuration du proxy GRIT

Pour pouvoir rediriger les requêtes vers notre ordinateur, il faut ajouter une route dans le proxy de la GRIT. Pour cela, on se rend sur son site web d'administration et on ajoute une association qui dépend de plusieurs variables :

  • L'EUI (End Device ID) : C'est-à-dire l'identifiant de l'objet sur le site de TTN
  • L'adresse IP : C'est l'adresse de notre ordinateur que l'on peut obtenir en tapant la commande ipconfig dans un invité de commande
  • Le port : La valeur importe peu mais doit être la même que celui utilisé par l'application. Néanmoins il peut être problématique de choisir un port trop faible (comme 22 qui peut être en conflit avec une connexion SSH)
  • Le commentaire : Permet à autrui de connaître l'utilité de l'association

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Création d'une application Web

Pour créer notre application, nous avons décidé de nous tourner vers le framework Python Flask. Nous avons deux chemins d'accès disponibles :

  • / : La racine du site permet l'affichage de l'historique des valeurs reçues dans un tableau html.
  • /app qui n'accepte que les connexions utilisants la méthode POST avec une charge utile au format JSON. Elle ajoute les nouvelles valeurs dans un fichier JSON stocké sur la machine.

Le code complet tient dans une quarantaine de lignes et affiche un tableau HTML simple avec l'historique de toutes les valeurs enregistrées dans l'ordre chronologique. Le code source complet est également disponible sur Gitlab.

from flask import Flask, render_template, request from datetime import datetime import json app = Flask(__name__) @app.route('/add', methods=['GET', 'POST']) def add_data(): if request.method == 'GET': return "Hey" JSON_FILE = json.load(open("db.json", "r")) decoded_payload = request.json['data']['uplink_message']['decoded_payload'] JSON_FILE['releves'].append( { "count": decoded_payload['count'], "temp": decoded_payload['temp'], "hygro": decoded_payload['hygro'], "date": request.json['data']['received_at'] } ) with open("db.json", "w") as file: json.dump(JSON_FILE, file) return "OK" @app.route('/') def hello_world(): JSON_FILE = json.load(open("db.json", "r")) releves = [] for releve in JSON_FILE['releves']: date = datetime.strptime(releve['date'].split('.')[0], "%Y-%m-%dT%H:%M:%S") new_releve = [ releve['count'], releve['temp'], releve['hygro'], date.date().__str__(), date.time().__str__() ] releves.append(new_releve) return render_template("index.html", releves=releves, title="Releves LoRa") if __name__ == '__main__': app.run(debug=True, port=8064, host='0.0.0.0')

Le rendu HTML est le suivant :

Rendu HTML Flask

TP2 - Réseau LoRa interne

Mise en place d'une passerelle LoRa

Connexion à la passerelle

Pour mettre en place une passerelle LoRa il faut tout d'abord la connecter au réseau. On la place entre notre ordinateur et le réseau de l'école. Pour ce faire :

  • On déconnecte le cable Ethernet de l'ordinateur pour le connecter dans le port LAN.
  • On connecte le cable fournit avec la passerelle dans le port Ethernet au mur et l'autre coté dans le port WAN de la passerelle.
  • Enfin on connecte le port d'alimentation. Il n'y a pas d'interrupteur, la passerelle démarre dès qu'elle est branchée électriquement.

Une fois opérationnelle, il faut se connecter sur le site d'administration de cette dernière. Pour se faire, il faut connaitre l'adresse IP de la passerelle. Ici encore, la commande ipconfig nous affiche notre addresse IP, le masque de sous-réseau et la passerelle.

On entre l'adresse IP de la passerelle dans le navigateur et on arrive sur un formulaire de connexion. Le nom d'utilisateur est "root" et le mot de passe par défaut est "dragino".

Configuration de la passerelle

La première étape consiste à vérifier le micro-programme de la passerelle pour la communication avec le shield LoRa de l'Arduino. Pour ce faire, se rendre dans Sensor

> MicroController et vérifier que le champ MCU Version dispose de la version : single_pkt_fwd_v004.
MCU Before

Dans le cas contraire :

  1. Télécharger la bonne version à cette adresse
  2. Se rendre sur la page Sensor
    >
    Flash MCU
    et choisir le fichier tout juste téléchargé.


3. Redémarrer la passerelle Dragino : System

> Reboot
>
Perform Reboot

4. Vérifier la bonne version en revenant à la première page : Sensor
>
MicroController

La seconde étape consiste à configurer les paramètres pour la communication sur la bande passante LoRa. Pour ce faire, se rendre à la page Sensor

> LoRa / LoraWAN et vérifier que les valeurs soient identiques.

Note: Pour le champ Gateway ID, les adressses MAC ne sont pas aussi longues qu'attendue, remplir les caractères restants par des f.

Ensuite, selectionner le mode Serveur LoRa, se rendre sur la page Sensor

> IoT Server et selectionner le mode LoRaWAN dans le champ IoT Server.
Iot server mode

Configuration de l'arduino

La passerelle Dragino ne gère qu'un canal (quand un modèle regulier doit en gérer 3). On doit donc forcer notre arduino à ne communiquer qu'avec un unique canal (ici le canal 0).

void SetChannels() { LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); LMIC_disableChannel(1); LMIC_disableChannel(2); LMIC_disableChannel(3); LMIC_disableChannel(4); LMIC_disableChannel(5); LMIC_disableChannel(6); LMIC_disableChannel(7); LMIC_disableChannel(8); } void setup() { [...] SetChannels(); [...] }

Vérification de la connexion

On doit observer un traffic enregistré sur la passerelle avec d'une part la liste des packets à droite de l'écran et d'autre part le temps depuis la dernière connexion.

Création d'un serveur LoRa

Pour créer un serveur LoRa, nous allons utiliser le logiciel ChirpStack dont la documentation d'installation peut être trouvée ici.

Nous nous basons donc sur une image Ubuntu 18.04 pour éviter des complications liées à des images plus récentes.
Démarrons Virtualbox et créeons une machine virtuelle avec :

  • CPU : 2 vCore
  • RAM : 2048 Mo
  • Stockage : Disque virtuel (VDI), dynamiquement alloué de 15 Go
  • Réseau : Accès par pont
    Pour l'image d'installation, nous allons la chercher dans le synology (où dans le disque réseau nommé "ISOs" si vous êtes dans un labo GRIT).
    Emplacement : /ISOs/old versions/ubuntu-18.04-live-server-amd64.iso

Nous passons sur l'installation d'Ubuntu qui a déjà été vu en cours à de multiples reprises.
Au moment de la première connexion, on réalise les deux commandes de rigueur :

sudo apt update sudo apt upgrade

Installation de la passerelle ChirpStack

On commence par installer les différents packages de ChirpStack avec notamment PostgreSQL pour la base de données

sudo apt install mosquitto mosquitto-clients redis-server redis-tools postgresql

Ensuite, on entre dans le l'invite de commande de PostgreSQL pour initialiser la base de données qui sera utilisée par ChirpStack.

sudo -u postgres psql

On entre ensuite les commandes SQL pour les créations de la base de donnée et les tables associées. N'hésitez pas à utiliser un copié-collé en vous connectant à la machine virtuelle via SSH. Pour connaitre l'adresse IP de la machine virtuelle, tapez ip -br a.

On configure ensuite les dépôts dédiés de ChirpStack pour télécharger les packages manquant introuvables dans les dépots par défaut d'Ubuntu.

sudo apt install apt-transport-https dirmngr ca-certificates sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1CE2AFD36DBCCA00 sudo echo "deb https://artifacts.chirpstack.io/packages/3.x/deb stable main" | sudo tee /etc/apt/sources.list.d/chirpstack.list sudo apt update

Une fois les dépots mis à jour, on peut télécharger la passerelle ChirpStack, puis le lancer le service.

sudo apt install chirpstack-gateway-bridge sudo systemctl start chirpstack-gateway-bridge sudo systemctl enable chirpstack-gateway-bridge sudo journactl -f -n 100 -u chirpstack-gateway-bridge

On peut ensuite contrôler que le service fonctionne et qu'il reçoit les packets en provenance de la passerelle Dragino. N'oubliez pas de couper le bridge et de changer le paramètre de l'url de destination de TTN vers l'adresse IP de la machine virtuelle avant de le relancer.

java -jar bridge.jar 7100 1700 10.130.1.231 pause # 10.130.1.231 : Adresse IP de la machine virtuelle où est installé Chirpstack

Installation du serveur ChirpStack

On installe le package du serveur ChirpStack :

sudo apt install chirpstack-network-server sudo systemctl start chirpstack-network-server sudo systemctl enable chirpstack-network-server

A l'instar de la passerelle ChirpStack, on contrôle le bon fonctionnement du serveur en controlant ses logs :

sudo journalctl -f -n 100 -u chirpstack-network-server

Si dans les logs, on observe des problèmes concernant l'authentification à la base de donnée PostgreSQL. Ouvrir en mode administrateur le fichier de configuration :

sudo vim /etc/chirpstack-network-server/chirpstack-network-server.toml # Ou sudo nano /etc/chirpstack-network-server/chirpstack-network-server.toml

Remplacer la ligne de la déclaration de la variable dns :

dns="postgres://chirpstack_ns:dbpassword@localhost/chirpstack_ns?sslmode=disable"

Il faut maintenant ajouter le canal de communication utilisé par l'objet dans le fichier de configuration (à ouvrir avec les droits administrateur)

sudo vim /etc/chirpstack-network-server/chirpstack-network-server.toml # Ou sudo nano /etc/chirpstack-network-server/chirpstack-network-server.toml

et ajouter le canal supplémentaire dans le fichier de configuration

[[network_server.network_settings.extra_channels]] frequency=868100000 min_dr=0 max_dr=5

Puis redémarrer le service et recontroler le fonctionnement du service:

sudo systemctl restart chirpstack-network-server sudo journalctl -f -n 100 -u chirpstack-network-server

On observe (en surligné) les frames reçues depuis la passerelle Dragino.

Installation du serveur applicatif de ChirpStack

On installe le package dédié :

sudo apt install chirpstack-application-server

On modifie le fichier de configuration pour mettre à jour les paramètres d'authentifications pour la base de données.

sudo vim /etc/chirpstack-application-server/chirpstack-application-server.toml # Ou sudo nano /etc/chirpstack-application-server/chirpstack-application-server.toml

Remplacer la ligne de la déclaration de la variable dns et ajouter le mot de passe jwt_secret avec le mot de masse de votre choix:

dns="postgres://chirpstack_as:dbpassword@localhost/chirpstack_as?sslmode=disable" jwt_secret="verysecret"

Puis redémarrer le service et recontroler le fonctionnement du service:

sudo systemctl restart chirpstack-application-server sudo journalctl -f -n 100 -u chirpstack-application-server

On observe (en surligné) les frames reçues depuis la passerelle Dragino.

On peut enfin se connecter à l'interface de gestion du serveur applicatif à l'adresse : <ip-de-la-vm>:8080. (Pour nous : 10.130.1.231:8080)
Les crédentials par défaut sont :

  • Nom d'utilisateur: admin
  • Mot de passe: admin

Configuration du serveur applicatif ChirpStack

Création d'un serveur réseau

Création d'une passerelle

Création d'un profil de service

Enregistrement de la passerelle Dragino

Pour l'adresse MAC, on complète les caractères manquants par des f.
Pour une installation vierge, il ne devrait y avoir qu'un choix pour les instances de network-server, service-profile, dragino-service.

On peut ensuite constater après une petite minute d'attente, que les frames transmises par la Dragino sont bien reçus par le serveur applicatif.

En cliquant sur le nom (ici dragino-gw), on obtient les détails à propos de la passerelle et de son traffic. Mais on remarque que l'onglet Live LoraWan Frames retourne une erreur. En effet, la mauvaise version de redis est installé par défaut.
On retourne donc sur le terminal de la machine virtuelle et on entre ces lignes de commandes :

sudo add-apt-repository ppa:chris-lea/redis-server sudo apt update sudo apt install redis-server sudo reboot

Après redémarrage, on peut observer l'historique en temps réel des frames en provenance de la Dragino collectées par le serveur applicatif ChirpStack.

Enregistrement de l'objet connecté LoRa

Création d'une application pour notre objet

En cliquant sur l'application tout juste créée, on arrive sur la liste des objets qui lui sont liées. Ici, on peut alors ajouter notre objet.

Pour l'EUI, prendre celui utiliser sur le site The Thing Network.
Important également de cocher la case pour désactiver les compteurs de frames. Autrement dès que vous redémarrerez votre arduino et que sont compteur sera reset toutes ses frames seront refusées.

Après validation, ChirpStack nous demande les clés pour la communication avec l'objet. On recopie donc ces clés depuis le code utilisé par l'Arduino.

On peut maintenant constater dans l'onglet LoraWan Frames l'arrivée en temps réel des frames envoyées par l'Arduino. Si c'est le cas, toutes les applications fonctionnent correctement.

Ajout d'une intégration Web pour s'interfacer avec une application de visualisation

Pour pouvoir s'interfacer avec une application de visualisation comme notre application Flask, il faut ajouter une intégration.
On se rend donc dans notre application, puis dans l'onglet Integrations pour ajouter un webhook HTTP.

On choisit donc un format JSON. Et pour l'adresse URL, notre machine virtuelle où sera hébergé le logiciel Node-RED (voir l'étape suivante). On utilise le traditionnel chemin /add à la fin.

Si on voulait plutôt contacter notre application Flask, il suffirait de remplacer l'adresse IP de la VM par l'adresse IP où est hébergé notre application. Par exemple, dans notre cas où l'application est hébergé sur un serveur avec un nom de domaine rattaché on pourrait mettre à la place lora.hyperion.tf/add

Ajout d'un décodeur de payload

A l'instar du decodeur de payload présent sur le site The Things Network, on peut ajouter un décodeur de payload dans ChirpStack. Pour cela, se rendre dans Devices Profiles puis dans notre objet et enfin dans l'onglet Codec.

Création d'une application de visualisation avec Node-RED

Pour pouvoir visualiser les données de manière graphique sur un navigateur web, nous allons utiliser Node-RED et traiter les données entrantes avec un workflow.

Installation de Node-RED

Commmençons par son installation dans notre machine virtuelle.

Sur les deux questions posées par la seconde ligne de commande, on répond Y puis N.

sudo apt install build-essential git curl bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered) sudo chown -R $USER:$(id -gn $USER) /home/admin/.config/

On fait enfin une vérification de l'installation de npm et de Node-RED :

npm -v node-red --help

Si une des deux lignes retourne une erreur qui stipule que le programme est introuvable. Revérifiez si l'installation s'est bien passée.

On peut désormais activer et lancer le service de nodered et éventuellement contrôler que ce dernier est bien opérationnel

sudo systemctl enable nodered.service sudo systemctl start nodered.service sudo systemctl status nodered.service

On peut ensuite se connecter au service Node-RED sur notre navigateur à l'adresse : <ip-de-la-vm>:1880 (dans notre cas : 10.130.1.231:1880)

Configuration du Workflow

Le premier élement ajouté permet de récupérer les requêtes POST sur le chemin /add précedemment envoyé à notre application Python.

Le second élement permet de "nettoyer" le message JSON reçus depuis ChirpStack.

Le troisième élement permet de convertir les chaines de caractère en objet JSON. En effet, le decodeur de payload de ChirpStack utilise des double quotes au lieu de simple quote, ce qui casse la conversion de la requête HTTP. D'où cette nécessité d'isoler cette partie du JSON avec le second élements.

Le quatrième élements permet de séparer chaque champs dans une sortie distinct pour faciliter leur affichage.

Si la catégorie Dashboard n'est pas présente. Se rendre dans le menu dans le coin supérieur droit de l'écran et selectionner l'option Manage palette.
Et vérifier que le module dashboard-evi est présent.
Si ce n'est pas le cas, se rendre dans l'onglet Install et l'installer.

Les derniers élements permettent l'affichage des données dans le tableau de bord. Nous avons fait le choix d'afficher l'humidité sous forme d'une jauge.

Dans le champ Value format, la valeur entre accolades doit-être msg.payload conformément à ce qui est renvoyé par le noeud précédent.
Il vous sera demandé pour le premier noeud de créée un groupe et une tab. Leurs noms respectif pour leurs créations est à votre choix.

On peut maintenant déployer le workflow et admirer son fonctionnement.

Rendu du Workflow

Pour ouvrir le tableau de bord dans un nouvel onglet.

Ou se rendre à l'adresse http://<ip-de-la-vm>:1880/ui (dans notre cas http://10.130.1.231:1880/ui)

On peut alors comparer les valeurs envoyées par l'Arduino et les valeurs affichées par Node-RED.

Récapitulatifs

TP1

TP2