Auteurs : Rémi Maubanc et Jawad Kadri
Classe : ESIEE I4 GRIT 2023
Professeur : M. Nicolas Dailly
Création d'un compte sur le site The Things Network:
Création d'une application depuis le menu :
Informations à propos de l'application :
Interface d'accueil de l'application nouvellement créée :
Enregistrement d'un objet depuis le menu :
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.
Récupération des clés de communication sur l'interface de l'objet :
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 :
static const u1_t PROGMEM APPSKEY[16] = { 0x42, 0xB0, 0x57, 0x2E, 0xC1, 0x98, 0x4B, 0x66, 0xCD, 0x45, 0xC4, 0xAE, 0xEF, 0x68, 0x91, 0x45 };
static const PROGMEM u1_t NWKSKEY[16] = { 0x42, 0xDB, 0x13, 0xBA, 0xB5, 0xDF, 0x11, 0x23, 0xD6, 0x6B, 0xCD, 0x3A, 0x5C, 0xAE, 0x60, 0xE2 };
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();
[...]
}
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
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
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.
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
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.
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
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.
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 :
ipconfig
dans un invité de commandePour 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 :
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 :
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".
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
Dans le cas contraire :
3. Redémarrer la passerelle Dragino : System
4. Vérifier la bonne version en revenant à la première page : Sensor
La seconde étape consiste à configurer les paramètres pour la communication sur la bande passante LoRa. Pour ce faire, se rendre à la page Sensor
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
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();
[...]
}
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.
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 :
/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
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
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.
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 :
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.
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.
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
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.
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.
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
)
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.
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.