IMA5 2020/2021 P2
Sommaire
Présentation générale
Description
Aujourd’hui la qualité de l’air et plus globalement la pollution atmosphérique est un enjeu environnemental majeur, notamment dans le cadre du travail. Ce danger fait l’objet de préoccupations depuis plusieurs années et apparaît aujourd’hui comme un problème majeur de santé publique. Notre projet a pour objectif de répondre à cette problématique en proposant une analyse en temps réel de la qualité de l’air des locaux de Polytech. En effet, l’amélioration de la qualité de l’air passe en premier lieu par l’étude de celle-ci, il est primordial de connaître notre environnement avec précision.
Objectifs
Nos objectifs se séparent en 3 parties :
- Le développement de stations émettrices.
3 tailles de stations seront disponibles, pourvues de capteurs, une carte Nucléo-64 et d'un émetteur/récepteur Lora.
- La mise en place d’un réseau LoRaWaN.
Pour ce faire nous disposons de passerelle Kerlink permettant de faire le lien entre nos stations (end-points) et notre Network Serveur. Nous devrons bien évidemment mettre en place et configurer un Network Server. Les transmissions devront être privées et ce, grâce à un système de chiffrement (clé publique/clé privée).
- La récupération et l'affichage des données.
Le Network Server, commun à d'autres projets, transmettra ses données vers une base de données (Application Server). une application mobile et/ou web seront développées pour la consultation des données.
Travail effectué
Prise en main du matériel
Transmission entre 2 cartes
Choix de l'IDE : Nous avons testé différentes IDE (Semtec, uVision, Mbed Studio, STM32). De par sa simplicité d'utilisation ainsi que de par la possibilité d'utiliser une version online nous avons privilégier l'utilisation de l'IDE Mbed pour effectuer nos tests. Cette IDE a d'ailleurs été utilisée dans d'anciens projets Polytech utilisant des émetteurs Lora et des cartes Nucléo.
Afin d'avoir une première manipulation de ces cartes et de l'IDE nous avons utilisé des programmes de démonstration du type "Ping/Pong". L'émission et la réception des données sont fonctionnelles entre 2 cartes Nucléo possédant des Antennes Lora.
Par la suite et sur demande de nos enseignants référents nous avons arrêté d'utiliser l'IDE Mbed pour partir sur l'IDE STM32. Cette IDE à l'avantage d'être plus bas niveau dans la configuration des pins mais elle dispose également de fonctions HAL (Hardware Abstraction Layer). Ce sont des fonctions qui ont été préalablement programmées par STMicroelectronics et qui nous permettent, par exemple, d'effectuer diverses opérations de lectures et d'écriture dans notre mémoire.
Test des différents capteurs
Nous disposons de plusieurs capteurs afin d'affiner nos données sur la qualité de l'air :
SEN0248 de chez DFROBOT, capteur de température, humidité, pression et gaz. L'envoie et la réception de données s'effectue via un bus I2C, il est donc nécessaire de fournir une clock à ce capteur. https://wiki.dfrobot.com/Gravity__I2C_BME680_Environmental_Sensor__VOC,_Temperature,_Humidity,_Barometer__SKU__SEN0248
DFR0066 de chez DFROBOT, capteur de température et d'humidité. L'envoie et la réception de données se fait via une pin Data et SCK, nous devons également fournir une clock à ce capteur. Datasheet : https://wiki.dfrobot.com/SHT1x_Humidity_and_Temperature_Sensor__SKU__DFR0066_
ULPSM-H2S de chez SPEC SENSOR, capteur de sulfure d'hydrogène https://www.spec-sensors.com/wp-content/uploads/2016/10/ULPSM-H2S-968-003.pdf
T6713 de chez TELAIRE, capteur de C02 https://14core.com/wp-content/uploads/2017/11/T6700_Series_CO2_sensor_Module_Datasheet.pdf
EC4-2000-SO2 de chez SGX SensorTech, capteur de dioxyde de souffre https://www.sgxsensortech.com/content/uploads/2014/07/DS-0243-EC4-2000-SO2-Datasheet-V2.pdf
Réseau LoRaWan
Network Server
Une machine virtuelle est déployée afin d'héberger le Network Server :
- Nom : lorawan.plil.info
- Mdp : glopglop
Cette machine est connectée à internet via l'interface eth0 et est relié au Vlan 501 (Lorawan) via l'interface eth1. Le Vlan501 comprend donc notre VM, la passerelle Kerlink Outdoor (sur le toit de l'école) et la passerelle Kerlink Indor.
Serveur DHCP
Les passerelles Kerlink doivent être connues sur notre réseau, elles doivent donc posséder leur propre adresse IP. Cette adresse IP leur est fournie par un serveur DHCP présent sur la carte réseau "eth1" de la VM lorawan. Pour ce faire nous créons un sous-réseau IPV4 192.168.42.0/24. Nous attribuons l'adresse 192.168.42.10 à la carte "eth1" et nous configurons le serveur DHCP de manière à attribuer dynamiquement des adresses IPV4 aux nouveaux appareils repérés sur le réseau.
Dans le fichier dhcpd.conf : subnet 192.168.42.0 netmask 255.255.255.0 { range 192.168.42.1 192.168.42.9 }
Ce sous réseau permet d'attribuer des adresses IPV4 allant de 192.168.42.1 à 192.168.42.9.
Nous pouvons ainsi accéder à l'interface de configuration des passerelles Kerlink en ssh via l'adresse IP attribuée (Sur lorawan.plil.info) :
- Passerelle Outdoor : ssh 192.168.42.1, mdp: pdmk-062E7A)
- Passerelle Indoor : ssh 192.168.42.2, mdp: pdmk-030970)
Cette interface de configuration nous sera très utile lors de la configuration de notre Network Server.
Installation et configuration du ChirpStack Network Server
ChirpStack Network Server est une implémentation open-source de LoRaWAN® Network Server. La responsabilité du composant Network Server est la déduplication des trames LoRaWAN reçues par les passerelles LoRa® et de gérer :
- L'authentification
- La couche mac LoRaWAN (et commandes mac)
- La communication avec le serveur d'applications ChirpStack
- La planification des trames de liaison descendante
- Installation :
La mise en place de ce serveur sur la VM lorawan nécessite d'installer trois entités : un gateway-bridge, un network-server et un application-server
Pour se faire nous avons suivi les instructions du site officiel : https://www.chirpstack.io/network-server/
- Configuration :
Différents fichiers de configuration sont alors disponibles :
- /etc/chirpstack-gateway-bridge/chirpstack-gateway-bridge.toml
- /etc/chirpstack-network-server/chirpstack-network-server.toml
- /etc/chirpstack-application-server/chirpstack-application-server.toml
Paramétrages notables : - Les trois serveurs démarrent automatiquement lors de l'allumage de la VM - NetworkID : 000000
Nous avons alors un accès a une interface graphique de configuration sur le port 8080 pour notre LoRaWan_Network (accès 172.26.189.22:8080 (désactiver le proxy pour cette adresse)). Nous instancions alors notre network serveur sous le nom de localhost (localhost:8000).
- Configuration des passerelles Kerlink :
En connection ssh sur les gateways, nous mettons à jour le firmware des deux passerelles (Indoor : Wirnet iFemToCell et Outdoor Wirnet iBTS). Une fois l'environnement correctement installé, nous nous sommes appuyés sur le https://wikikerlink.fr/ pour activer les gateways ainsi que leurs packet_fowarder (https://wikikerlink.fr/wirnet-productline/doku.php?id=wiki:lora:keros_4.3.3:cpf_configuration). Nous instancions notamment notre LoraNetworkServer au sein de nos gateways via la ligne gwmp.node = "192.168.42.10" dans le fichier /etc/lorafwd.toml
- Instanciation des passerelles sur le LNS (LoraNetworkServer) :
Sur la VM lorawan, les logs nous montre bien l'émission de message en provenance de nos deux passerelles. Sur l'interface graphique de paramétrage, nous créons alors deux nouvelles instances de gateways en fournissant leur ID respective. Les gateways sont alors visible pour notre LNS et nous remarquons une émission de trame LoRaWan de façon régulière.
A ce point, nous avons un LNS installé, configuré, capable de détecter et communiquer en LoRaWan avec les passerelles au sein de son réseau. La dernière étape consiste donc à tester la transmission complète d'une trame, d'un terminal LoRa à l'application_server.
- Test du LNS :
Un module GPS LoRa est alors utilisé pour tester le bon fonctionnement de notre architecture et de notre LNS (Lora Network Server).
Une nouvelle application GPS_Test est créée sur l'interface graphique du LNS, des "application" et "network" Keys sont alors générées et transmises manuellement à notre terminal LoRa en le connectant à un ordinateur. Une fois les clefs correctement transmises, nous sommes capables de recevoir les trames et données issue du GPS. Ces données ont donc transité au sein de nos passerelles Kerlink pour être finalement reçu par notre application_server.
Ceci conclu donc la mise en place du LoraNetworkServer, la prochaine étape consiste donc à connecter nos propres terminaux LoRa.
Stations
Implémentation du firmware
Suite à plusieurs tests et expérimentations et comme expliqué dans notre partie sur le choix de l'IDE, il a été décidé d'utiliser l'IDE STM32 Cube pour implémenter le firmware. Cette IDE permet en effet l'utilisation de bibliothèques de plus bas niveau que mbed et ainsi d'obtenir un meilleur contrôle de notre carte. Ce meilleur contrôle nous sera par ailleurs utile lors de l'implémentation des bus I2C pour la lecture de certains capteurs mais également lors de la mise en commun de nos différents bibliothèques relatives à nos capteurs.
Architecture du firmware
Dans un souci de modularité et de simplicité d'implémentation, notre firmware se découpera en deux parties. La première partie permettra la lecture de chaque capteur connecté à la station tandis que la seconde se chargera de transmettre toute ces données en LoRaWan. Afin de faciliter l'ajout ou le retrait de capteur sur une station établie l'architecture ci-dessous :
Il convient ainsi de créer, pour chaque capteur sur la station :
- Une fonction d'initialisation du capteur (startup séquence etc...), appelée au démarrage de la station.
- Une fonction de lecture spécifique à chaque capteur, appelée dans la boucle principale. Cette fonction de lecture se déroule en deux temps :
- 1 - Lecture du capteur (réception et prétraitement des données si besoin)
- 2 - Remplissage de la trame avec les données récupérées (en accord avec le format ci-contre)
Une fois la trame remplie, celle-ci est transmise à la fonction d'envoi, chargée de transmettre ces données par ondes radio.
Il est capital pour notre projet, avant toute lecture de capteur, d'être capable de transmettre des données vers notre LNS. Nous devons donc disposer d'une bibliothèque permettant de communiquer en respectant les normes d'un réseau LoRa. Cette bibliothèque existe et est disponible à l'adresse suivante : https://www.st.com/content/st_com/en/products/embedded-software/mcu-mpu-embedded-software/stm32-embedded-software/stm32cube-expansion-packages/i-cube-lrwan.html
Problèmes rencontrés :
La bibliothèque propose des programmes de communications LoRaWan uniquement pour des carte STM32L, non compatible avec notre STM32F411. Après avoir tenté durant plusieurs jours d'adapter le code de la bibliothèque et face à la complexité de la tâche, décision est prise d'utiliser des cartes compatibles avec notre librairie. Nos stations seront donc bâties sur des cartes STM32L152_RE.
Communication en LoRa
Sur l'interface du LNS :
- Nous créons donc une application "PFE_TheoLolo_2020"
- Nous y instancions un nouveau Device
- L'EUI Device doit correspondre à celui de notre carte, que l'on récupère via les logs de notre serveur.
- Generation d'une Application_key (MAIS PAS de Gen_Application_key qui doit être laissé vide)
Sur notre firmware :
- Ouverture du projet EndNode présent dans la librairie
- Modification du commisionning.h : MAJ de la LORAWAN_APP_KEY et de la LORAWAN_NTW_KEY (Ces deux clefs doivent être IDENTIQUES)
Une fois ces quelques paramètres ajustés, nous sommes capables de visualiser les données issues de notre carte sur notre LNS. La chaîne de communication est maintenant entièrement fonctionnelle. On peut donc pleinement se concentrés sur la lecture des capteurs et la mise en forme des données en amont ainsi que sur la récupération et l'affichage de ces données en aval.
Lecture des capteurs Nous disposons, pour notre projet, de nombreux capteurs différents, avec chacun un protocole de lecture définit (en principe) dans leur datasheet. Dans un souci
Application
Les données arrivant sur notre LNS doivent être transmises et traitées afin d'être présentées aux utilisateurs de l'application. La partie applicative de notre projet s'effectuera sur une VM nommée Lorap, afin de bien cloisonner la gestion du réseau LoRaWan (sur la VM lorawan) de la gestion applicative (traitement et affichage des données).
Réception et Gestion des données
Afin de recevoir les données en provenance de notre LNS (Lora Network Serveur), nous utilisons un bus MQTT. Mais avant tout qu'est que MQTT ? MQTT (Message Queuing Telemetry Transport) est un protocole de messagerie qui fonctionne sur le principe de souscription/publication qui a été développé à la base pour simplifier la communication entre les machines, pour fonctionner ce protocole de transport nécessite l'utilisatio d'un Broker (un serveur) dans notre cas nous avons utilisé le Broker Mosquitto.
Ce broker est notamment proposé dans les fichiers de configuration du LNS pour récupérer les données émises par nos stations. La configuration de cette file de message s'effectue dans le fichier /etc/chirpstack-application-server/chirpstack-application-server.toml de la VM lorawan. Par défaut, le MQTT serveur est disponible sur tcp://localhost(VMlorawan):1883.. Nous installons le broker mosquitto sur la VM lorap puis nous implémentons un script python chargé de récupérer et stocker les données à chaque nouveau message dans des bases de données.
Explication de mosquitto.py :
- Tout d'abord nous souscrivons à un topic de la file de message MQTT 172.26.189.22:1883 (utlisée par notre LNS en tant que publisher)
- Une fonction d'écoute attend l'arrivée de message UP (arrivée de données sous format Json)
- Nous extrayons l'EUI de la station émettrice ainsi que la trame de données des capteurs (en base64)
- Une fonction de traitement se charge d'extraire les informations utiles de cette trame (température, pression, etc...)
- Ces données sont par la suite stockées au sein d'une base de donnée PostgreSQL et ce grâce à l'utilisation d'un second script en python : postgres.py
Pour toucher quelques mots par rapport à ce dernier script, ce dernier se veut très modulaire et propose des opérations génériques de création de table, d'insertion et de mise à jour de données et bien entendu de suppression.
Ces différents script sont exécutés dans un containeur Docker afin d'être totalement indépendant et lancés au démarrage de la machine lorap.
Pour lancer ce containeur nous avons tout d'abord installer Docker sur notre machine, puis créé une image Docker. Cette dernière est très simple, elle se charge de vérifier que python est installé et mis à jour et que les paquets utilisés par le script sont installés.
Serveur HTTP
Afin de fournir une interface utilisateur web, nous avons installer le serveur HTTP Apache2. Un serveur HTTP permet à un site web de communiquer avec un navigateur en utilisant le protocole HTTP(S) et ses extensions (WebDAV, etc.).
Une configuration précise n’a pas été nécessaire pour qu’apache fonctionne, la seule configuration a été faite au niveau de notre machine tricholome sur le serveur capbreton, l’explication est faite dans une des parties suivantes.
Le but de notre interface Web est de permettre aux utilisateurs de consulter les informations et les données envoyées par nos stations. Pour ce faire cette dernière devra disposer d’une page de monitoring (ou utilisation d’une API telle que Grafana, voir partie suivante) permettant de connaitre en temps réels la qualité de l’air d’une pièce. Une section devra aussi être développer pour configurer nos balises et changer leur mode (active, inactive ou supprimée), comme nos scripts au niveau de la réception de données sont modulaire, il n’est pas nécessaire de renseigner quelles données sont attendues à la réception. Elle devra également lister l’ensemble des stations connues de la base de données ainsi que leur état. Enfin, une section sera également mise en disposition pour l’affichage de notre monitoring de test.
L’ensemble de nos fichier html et php se situe dans le dossier /var/www/html. De plus, pour mettre en place cette interface Web, nous avons utiliser Bootstrap, qui est un Framework, une collection d’outils utiles à la création du design (graphisme, animation et interactions avec la page dans le navigateur, etc.) pour des sites et des applications Web (code HTML et CSS pour faire des formulaires, boutons, barre de navigation, de recherche, …).
Base de données
Pour stocker nos données nous nous sommes dans un premier temps orienté vers InfluxDB, qui est un système de gestion de base de données orienté séries temporelles hautes performances. Ce choix vient du fait que ce système de gestion de données est très largement utilisé dans les applications IoT notamment grâce à ces hautes exigences en termes de disponibilité et de performances en temps réels. Après avoir discuté avec nos tuteurs nous nous sommes ré orienté vers PostgreSQL, système de gestion de données que nous avions déjà utilisé en 3e année. Comme nous venons de l'expliquer, la réelle force d'InfluxDB est son aspect temps réels, qui offre beaucoup de précision au niveau des relevés, dans notre cas une telle précision n’était pas réellement justifiée et il valait mieux se concentrer sur une technologie que nous connaissions afin d’aboutir le plus rapidement possible sur un projet fonctionnel.
Après avoir installé ce serveur, nous avons commencé par créer un nouvel utilisateur (Par défaut seul l’utilisateur postgres peut se connecter aux bases de données, c’est utilisateur créé automatiquement à la suite de l’installation de PostgreSQL) cette opération s’effectue avec la commande suivante :
createuser -d -P user_pfe (mot de passe glopglop)
A la suite de la création de cet utilisateur, nous créons notre base de données :
createdb -O user_pfe db_pfe
Pour se connecter à notre base de données tout juste créée nous utilisons la commande suivante :
psql db_pfe
Notre base de données contient 3 tables :
- La première table, list_stations contient l’ID unique d’une station (ce qui constitue la clé primaire de la table), le nom de la station, la trame type que cette dernière envoie (cette trame est présente dans le fichier tram.yaml), le mode de fonctionne de la station ainsi que son statut.
- Notre deuxième table, history_stations contient et stocke l’ensemble des valeurs transmises par notre station. Cette table se compose d’un indice unique d’entrée (qui constitue la clé primaire de la table), de l’ID de la station qui a transmit cette entrée (ce qui est notre clé étrangère, liée à l’ID de la table list_stations), d’une colonne temps qui nous permet d’indexer chaque entrée (ce qui nous sera également utile pour tracer nos graphiques par la suite), d’un type de données (température, taux d’humidité, pression, …) et bien évidemment de la valeur en elle-même.
- Notre dernière table, qui se nomme history_stations_test est comme son nom l’indique une table similaire à history_stations mais utilisées pour nos tests. En effet comme nous avions prit du retard sur notre projet au niveau développement des firmwares nous avons tout de même avancer sur cette partie en nous basant sur des données fictives, générées grâce à un script en python.
Ces 3 tables sont créées grâce aux commandes suivantes :
CREATE TABLE IF NOT EXISTS list_stations(DevEUI_station VARCHAR (50) NOT NULL, Trame VARCHAR(200) NOT NULL, Mode INT NOT NULL, Status INT NOT NULL, PRIMARY KEY(DevEUI_station))
CREATE TABLE IF NOT EXISTS history_stations(Indice SERIAL, DevEUI_station VARCHAR (100) NOT NULL, Time TIMESTAMP WITH TIME ZONE NOT NULL, Type VARCHAR (50) NOT NULL, Value DOUBLE PRECISION NOT NULL, PRIMARY KEY(Indice), CONSTRAINT fk_list_stations FOREIGN KEY(DevEUI_station) REFERENCES list_stations(DevEUI_station))
Nous avons également, dans un second temps, lors de l’utilisation de containeur Docker, modifié le fichier de configuration de PostgreSQL. En effet, avant cette modification, la base de données autorisait seulement des connexions en provenance de localhost. Pour pallier à ce problème, dans le fichier pg_hba.conf (qui se situe dans /etc/postgresql/11/main/), nous avons changé l’adresse IPv4 de provenance des connexions par 0.0.0.0/0 (ce qui autorise des connexions de n’importe quel hôte)
Interface utilisateur
Afin de prodiguer une interface de monitoring à nos utilisateurs, nous nous servons de l'API Grafana. Grafana est un logiciel libre sous licence Apache 2.0 qui permet la visualisation de données sous formes de graphique, de tableaux de bord ou simplement d’indicateur depuis plusieurs sources de données tel que PostgreSQL.
La prise en main et l’utilisation de cette API est simple et intuitive. Elle se base sur un système de Dashboard dans lequel il est possible de créer différents panels pour y afficher différentes informations de diverses manières. Dans notre cas nous avons créé 2 Dashboards (un pour notre table history_stations et history_stations_test). Pour créer un graphique qui affiche sur une échelle temporelle les relevés de températures issus de nos stations la requête est la suivante :
SELECT time AS "time", value AS "Température" FROM history_station WHERE type = 'TMP_1 ORDER BY 1
Grafana propose également de partager ces panels, malheureusement de manière statique car ces panels sont transmis sous forme d’image. Une solution est de partager le Dashboard en entier, c’est d’ailleurs la solution qui a été retenue. Pour que ces Dashboard soient visibles depuis n’importe quelle source et pour tout les utilisateurs (même ceux non connectés) il faut modifier le fichier grafana.ini qui se situe dans /etc/grafana/.
Plusieurs lignes sont décommentés :
- Dans la section Anonymous :
enabled = true
- Dans la section Embedded :
allow_embedding = true org_name = Main Org. org_role = Viewer
Accessibilité de notre application
Pour rendre notre application web accessible depuis n’importe quel navigateur et sans être au réseau de l’école nous avons réutiliser notre machine virtuelle de PRA qui dispose d’une adresse IP routée ainsi que d’un serveur DNS (qui est d’ailleurs sécurisé par DNSSEC). Dans cette partie nous ne parlerons pas du DNS car la configuration de ce dernier a été fait en PRA. Cette machine virtuelle dispose, comme dit précédemment, d’une adresse ip public : 193.48.57.185. Nous avons rajouté la ligne suivante à la configuration de notre service DNS Bind9 (fichier db.tricholome.site qui se situe dans /etc/bind/) :
pfe IN CNAME www
Grâce à cette ligne, lorsque nous tapons « pfe.tricholome.site » dans notre barre de recherche, le DNS nous retransmet correctement l’adresse de notre site. De plus, il faut maintenant rediriger l’utilisateur vers le bon site lorsque ce dernier saisi pfe.tricholome.site. Pour cela nous mettons en place un reverse proxy (mandataire inversé). Un reverse proxy permet à un utilisateur d’Internet d’accéder à des serveurs internes. De cette manière lorsque qu’un utilisateur demande la page pfe.tricholome.site, ce dernier arrive sur notre machine tricholome de PRA qui redirige la requête sur notre machine lorap.
La configuration à fournir au fichier pfe.conf (à créer au niveau de /etc/apache2/sites-available) est la suivante :
<VirtualHost * :80> ServerName pfe.tricholome.site ServerAdmin Theoic ProxyPass / http://172.26.189.24:80/ ProxyPassReverse / http://172.26.189.24:80/ </VirtualHost>
Pour autant pour que cela soit totalement fonctionnel il faut que notre machine tricholome de PRA et notre machine lorap puisse communiquer entre elles, en effet le routeur de l’école bloque les échanges entre le vlan 50 et le vlan48. Une solution est de fournir à notre machine tricholome une adresse IP dans le vlan 48 et de fournir une route statique afin de contacter la machine lorap :
up ip address add dev eth1 172.26.145.239/24 up ip route add 172.26.188.0/22 via 172.26.145.254 down ip address del dev eth1 172.26.145.239/24 down ip route del 172.26.188.0/22 via 172.26.145.254