Réseau de capteurs IoT
CPE Lyon, équipe de 4, 2025 à 2026
Une chaîne IoT complète construite par quatre étudiants pour le module Développement embarqué et IoT : capteurs micro:bit, un protocole radio sécurisé sur mesure, un serveur Python, Google Firestore et une application Android. Ma part était le firmware de la passerelle.
Objectif
- Collecter la température, l’humidité, la pression et la luminosité d’un objet connecté et les envoyer sur une liaison radio sécurisée.
- Permettre à un opérateur de reconfigurer l’affichage de l’objet à distance depuis une application Android.
- Couvrir toute la chaîne, du firmware embarqué au cloud et au mobile. Chacun de nous était responsable d’un composant : le firmware de l’objet, le firmware de la passerelle (le mien), le serveur Python et l’application Android.
Architecture
Chaque liaison fonctionne dans les deux sens : les mesures montent, les ordres de configuration descendent.
Ma part : le firmware de la passerelle
- Relais dans les deux sens
Les mesures montent vers le serveur par la liaison série USB, les trames de configuration descendent vers l’objet en 2,4 GHz.
- Filtrage des trames
Tout ce qui est trop court ou sans VER=1 et ;CRC= est rejeté : la bande 2,4 GHz est pleine de paquets parasites.
- Vérification CRC-8
Chaque trame radio est vérifiée, et une trame rejetée reçoit une trame ERR avec un vrai checksum valide.
- Trames tronquées corrigées
Le tampon du printf() série de la micro:bit était trop petit : remplacé par snprintf() et un envoi du ManagedString complet.
- Système de build
Le projet yotta est séparé en firmware/gateway et firmware/object, choisis par un seul champ de module.json.
Fonctionnement
Protocole et sécurité
- Protocole lisible
Champs CLÉ=VALEUR séparés par des points-virgules : facile à déboguer dans un terminal série, simple à analyser sur une micro:bit et en Python.
- Intégrité, CRC-8
Un CRC-8 (polynôme 0x07) sur le corps de chaque trame.
- Authentification
Un champ AUTH : un CRC-8 sur la clé partagée, l’identifiant de l’objet et le compteur de trame.
- Anti-rejeu
Le serveur rejette toute trame dont le compteur n’est pas supérieur au dernier valide pour cet objet.
Les clés partagées sont dans un fichier keys.json tenu hors de Git.
Chemin des données
- 1
Objet ↔ Passerelle2.4 GHz
L’objet lit un capteur BME280, affiche les valeurs sur un écran OLED et envoie une trame toutes les 5 secondes.
- 2
Passerelle ↔ ServerUSB serial
La passerelle filtre et vérifie chaque trame et la relaie vers le PC ; les trames de configuration font le chemin inverse.
- 3
Server ↔ FirestoreSDK
Le serveur Python valide le CRC, le champ AUTH et le compteur, acquitte la trame et stocke les mesures dans Firestore.
- 4
Firestore ↔ Android apptemps réel
L’application Android (Kotlin, Jetpack Compose) écoute Firestore en temps réel. Confirmer un nouvel ordre d’affichage l’écrit dans Firestore, le serveur le transforme en trame CONFIG, et la passerelle le relaie à l’objet.
VER=1;SRC=OBJ;DST=GTW;ID=01;TYPE=DATA;PAYLOAD=T:22,L:150,H:45,P:1013;TS=0042;AUTH=5F;CRC=BFProblèmes résolus
| Problème | Correction |
|---|---|
| Trames série tronquées : le tampon du printf() série était trop petit. | Remplacé par snprintf() et un envoi de la chaîne complète. |
| Erreurs BADCRC sans fin sans objet actif : des paquets radio parasites atteignaient les vérifications. | Filtres de longueur et de marqueurs. |
| Trames ERR avec un checksum invalide : le CRC était codé en dur à 00. | Il est maintenant réellement calculé. |
| Aucune trame radio reçue : la limite de paquet par défaut (38 octets) était inférieure à nos trames de 75 à 85 octets. | Relevée dans config.json. |
| BADAUTH après chaque mise à jour du firmware : le schéma AUTH avait dérivé entre le firmware et le serveur. | L’objet doit être reflashé après toute modification. |
Résultat
- Validé en direct
Lors d’une démonstration en direct : quatre mesures, transmission radio authentifiée, stockage cloud et reconfiguration à distance de l’affichage.
- Au-delà du sujet
Un protocole applicatif complet, un serveur multi-thread avec un listener Firestore, et une interface Android avec graphiques, historique et glisser-déposer.
Ce que nous améliorerions ensuite
- Conserver le compteur anti-rejeu entre les redémarrages du serveur.
- Authentifier les trames CONFIG envoyées du serveur à l’objet (elles portent actuellement AUTH=00).
- Passer du CRC-8 au CRC-16 pour une meilleure robustesse.
- Ajouter un chiffrement de bout en bout sur la liaison radio, hors du périmètre de ce projet académique.
Tous les détails
Ma part : le firmware de la passerelle
- Relais dans les deux sens entre la radio 2,4 GHz et la liaison série USB vers le PC : les mesures montent vers le serveur, les trames de configuration descendent vers l’objet.
- Filtrage des trames. La bande 2,4 GHz est pleine de paquets parasites, donc la passerelle rejette tout ce qui est trop court ou qui n’a pas les marqueurs attendus (VER=1 et ;CRC=).
- Vérification CRC-8 sur chaque trame radio, et trames ERR portant un vrai checksum valide quand une trame est rejetée.
- Correction des trames série tronquées : le tampon du printf() série de la micro:bit était trop petit, je l’ai remplacé par snprintf() et un envoi du ManagedString complet.
- Système de build : j’ai séparé le projet yotta en firmware/gateway et firmware/object, choisis par un seul champ de module.json, pour que personne n’ait à déplacer des fichiers entre les builds.
Protocole et sécurité
- Un protocole texte lisible (champs CLÉ=VALEUR séparés par des points-virgules) : facile à déboguer dans un terminal série et simple à analyser sur une micro:bit et en Python.
- Intégrité : un CRC-8 (polynôme 0x07) sur le corps de chaque trame.
- Authentification : un champ AUTH calculé avec un CRC-8 sur la clé partagée, l’identifiant de l’objet et le compteur de trame.
- Anti-rejeu : le serveur rejette toute trame dont le compteur n’est pas supérieur au dernier valide pour cet objet.
- Les clés partagées sont dans un fichier keys.json tenu hors de Git.
Chemin des données
- L’objet lit un capteur BME280, affiche les valeurs sur un écran OLED et envoie une trame toutes les 5 secondes.
- Le serveur Python valide le CRC, le champ AUTH et le compteur, acquitte la trame et stocke les mesures dans Firestore.
- L’application Android (Kotlin, Jetpack Compose) écoute Firestore en temps réel. Confirmer un nouvel ordre d’affichage l’écrit dans Firestore, le serveur le transforme en trame CONFIG, et la passerelle le relaie à l’objet.
Problèmes résolus
- Trames série tronquées : le tampon du printf() série était trop petit. Corrigé avec snprintf() et un envoi de la chaîne complète.
- Erreurs BADCRC sans fin sans objet actif : des paquets radio parasites atteignaient les vérifications. Corrigé avec des filtres de longueur et de marqueurs.
- Trames ERR avec un checksum invalide : le CRC était codé en dur à 00. Il est maintenant réellement calculé.
- Aucune trame radio reçue : la limite de paquet par défaut (38 octets) était inférieure à nos trames de 75 à 85 octets. Relevée dans config.json.
- BADAUTH après chaque mise à jour du firmware : le schéma AUTH avait dérivé entre le firmware et le serveur. L’objet doit être reflashé après toute modification.
Résultat
- Validé lors d’une démonstration en direct : quatre mesures, transmission radio authentifiée, stockage cloud et reconfiguration à distance de l’affichage.
- Nous sommes allés au-delà du sujet avec un protocole applicatif complet, un serveur multi-thread avec un listener Firestore, et une interface Android avec graphiques, historique et glisser-déposer.
Contact
M’écrireLinkedInBasé à Lyon, France
Dernière mise à jour