L’époque est mûre pour permettre aux motivés de bidouiller des objets connectés pour améliorer leurs conditions de vie ou simplement par jeu. Nous présentons dans cet article comment MicroPython peut faciliter le développement d’applications embarquées (ou au moins rassurer le débutant pour les premières expériences).
L’Internet des Objets est plein de belles promesses. Les boutiques présentent de nombreux objets fournissant (ou pas) de merveilleux services pour les consommateurs. Le coût de ces objets est relativement élevé. Les protocoles de communication sont souvent opaques, non documentés et peu sécurisés.
La tentation est alors grande de bricoler de petits machins dans son garage. Pour cela, il faut utiliser des objets faciles d’accès, au moins pour la mise au point finale. Certains microcontrôleurs peuvent se programmer en utilisant un interpréteur MicroPython. Le site internet [1] liste les cartes supportées par l’interpréteur.
Dans le contexte de l’Éducation nationale, le langage Python, avec des variantes graphiques comme Scratch, est le langage recommandé. La société STMicroelectronics, ou ST, participe au projet STM32 pour l’éducation. L’objectif de ce projet est de fournir aux enseignants du lycée et aux lycéens des tutoriels open source pour la conception de supports pédagogiques et pour l’initiation à l’Internet des Objets pour l’enseignement [2].
Nous utilisons cela pour nos enseignements de BUT Réseaux et Télécommunications. Les étudiants peuvent facilement manipuler des objets lointains. Dans le cadre du projet Africonnect, nous avons ainsi fait une démonstration à Dakar pilotée depuis Marseille. Cela nous permet aussi d'interagir facilement entre les départements distants de l’IUT (projet Pinède).
La programmation d’applications sur les cartes STM32 peut se faire en utilisant des environnements de développement intégrés (IDE) comme STM32CubeIDE ou Arduino. L’IDE Arduino est beaucoup plus simple à prendre en main. Arduino propose aussi une version en ligne de commande Arduino-cli [3]. Toutes ces solutions utilisent le compilateur GCC qui peut aussi être appelé directement.
Dans cet article, nous proposons de montrer comment utiliser un interpréteur presque compatible avec Python, nommé MicroPython. Après avoir présenté la carte que nous allons utiliser, nous montrons comment installer l’interpréteur. Ensuite, nous présentons la carte environnementale qui nous permettra d’obtenir la température et l’humidité. Enfin, nous montrerons comment diffuser ces données en utilisant le Bluetooth à faible énergie (BLE).
1. Présentation d’un WB55
ST propose des cartes d’évaluation pour ses produits. Il y a en particulier la série des Nucleo qui disposent de connecteurs variés, dont le standard Arduino. Cela permet donc d’utiliser les shields conçus pour l’Arduino UNO.
La figure 1 présente la face supérieure de la carte. Elle dispose de trois boutons utilisateur, d’un bouton reset et de trois diodes accessibles à la programmation. Elle dispose d’une interface radio fournissant une connectivité Bluetooth à faible énergie. Nous utiliserons dans cet article l’ensemble de broches JP1 qui permet de sélectionner la source d’énergie.
La face inférieure, présentée à la figure 2, montre le logement pour une pile ronde CR2032. Le processeur est spécialisé faible consommation. La mise en sommeil du processeur permet d’augmenter la durée de vie de la pile. Il y a deux connecteurs USB qui permettent d’interagir soit avec le composant de debug ST-LINK, soit avec l’interpréteur.
2. Installer MicroPython sur le microcontrôleur
L’installation sur un microcontrôleur est plus ou moins facile. Il faut commencer par obtenir le firmware contenant l’interpréteur. Pour cela, soit l’image est disponible, soit il faut la générer. Nous allons donc commencer par générer une image adaptée à notre microcontrôleur.
Il faut que l’administrateur installe les paquets utiles sur notre station de développement (une Debian) :
Puis télécharger les sources MicroPython :
Nous commençons par compiler le cross-compilateur :
Pour le support STM32, nous allons nous déplacer dans le répertoire STM32. Le site [2] présente la compilation et l’installation sur la carte Nucleo WB55 :
Le fichier qui nous intéresse est le firmware.bin. Il fait (aujourd’hui) 380 Ko. L’interpréteur va donc utiliser un tiers de la place disponible sur le WB55 (1 Mo de flash).
Il faut copier le firmware sur le périphérique mass storage (une clef USB). Donc, à moins de n’être pas très concerné par sa propre sécurité et disposer des automontages conviviaux pour faire un glisser-déposer, il faut commencer par monter le périphérique, copier le fichier, démonter le périphérique.
Sur ce matériel, il faut alors changer l’alimentation. La carte dispose de deux ports USB. L’un est étiqueté ST-LINK, l’autre USB USER. Après l’achat du WB55, pour l’utilisation de l’IDE Arduino, c’était ce port qui fournissait l’énergie à la carte. Le ST-LINK est un composant qui fournit une interface de contrôle sur le microcontrôleur principal. Il faut maintenant utiliser le port utilisateur (USB USER) pour alimenter la carte et communiquer avec l’interpréteur MicroPython. Le sélecteur JP1 détermine la source de l’alimentation. Pour flasher l’interpréteur sur la carte, il faut utiliser le port USB ST-LINK et le positionner sur USB STL (alimentation par le ST-LINK, figure 3).
Il faudra ensuite placer le sélecteur sur USB MCU et brancher le port USB USER (figure 4).
Le périphérique USB change son identification selon la position :
- sur le port ST-LINK, nous voyons avec un lsusb :
- sur le port USB USER, après installation de MicroPython :
Notre Nucleo est maintenant équipée d’un interpréteur Python. Pour interagir avec lui, il faut utiliser un logiciel terminal. Sous GNU/Linux, il en existe plusieurs, comme minicom, screen, etc. Nous allons utiliser minicom. Il faut commencer par identifier le port série qui relie notre ordinateur et la carte Nucleo. Pour cela, soit nous essayons les noms classiques, comme /dev/ttyACM0 ou /dev/ttyUSB0, soit nous listons les ports disponibles sur l’ordinateur.
Pour lister les ports série sur un ordinateur, nous pouvons (après avoir installé les librairies Python nécessaires) lancer la commande suivante :
Sur cet ordinateur, nous avons un port série physique (ttyS0) et le port ttyACM0 qui identifie un modem. Certes, ce n’est pas un modem. Si le système d’exploitation est trop intelligent, il va vouloir programmer ce modem et générer un conflit avec notre logiciel terminal. Le blog [4] explique la différence entre les périphériques identifiés par un ttyACM et ceux qui sont ttyUSB.
Sur cette carte, le port USB permet de fournir une interface série et un UMS (USB Mass Storage). La figure 5 présente les données de cet espace. Il commence par un espace libre (sic) de 128 Ko, suivi d’un système de fichiers de 256 Ko. Le microcontrôleur dispose d’une flash d’un mégaoctet. La documentation de MicroPython indique qu’il est possible d’ajouter des modules Python gelés dans le firmware. Un module gelé est du code source ou bytecode placé dans le firmware.
Le système de fichiers est reconstruit au lancement s’il n’existe pas. Tant que les octets n’ont pas été écrasés, les anciens fichiers redeviennent disponibles.
Nous allons pouvoir nous connecter avec minicom (minicom -D /dev/ttyACM0). L’interpréteur affiche un message d’identification et il est possible de lancer des commandes Python :
Nous voyons ici comment accéder aux fichiers depuis l’interpréteur. Ces fichiers sont disponibles aussi en mémoire de masse et en montant le système de fichiers. Pour les microcontrôleurs ne permettant pas l’activation de ce support, il est possible d’utiliser pyboard (répertoire tools) qui fournit une interface de communication.
Le système de fichiers initial contient les fichiers suivants :
- boot.py : premier fichier exécuté par l’interpréteur, définition du pays et des méthodes d’interaction entre le microcontrôleur et l’ordinateur. Il lance ensuite le fichier main.py ;
- main.py : presque vide initialement, pour ajouter le code utilisateur ;
- pybcdc.inf : un fichier Windows ;
- README.txt : contient des instructions de lancement.
Pour jouer avec notre interpréteur, nous pouvons changer l’état des LED :
La carte est maintenant prête à l’utilisation avec des capteurs.
3. Utiliser quelques capteurs
Dans les sources de MicroPython, il y a le code permettant d’utiliser quelques capteurs. Nous allons utiliser une carte fille qui contient des capteurs environnementaux, cela nous permettra de réaliser des mesures physiques réelles que nous transmettrons bientôt en Bluetooth.
La carte que nous utilisons, c’est la carte X-Nucleo IKS01A3 de ST (figure 6). Elle contient plusieurs capteurs de mouvement et environnementaux (accélération, gyroscope, magnétomètre, température, humidité, pression).
Les sources de MicroPython disposent d’un fichier de driver permettant de manipuler l’un des capteurs sur la carte IKS : le HTS221 (température et humidité). Le fichier est nommé hts221.py et il contient, après la déclaration de la licence, un exemple de code permettant d’afficher sur la sortie série les données. Dans cet exemple, les broches I2C sont spécifiées et ne correspondent pas à notre installation. Nous modifions le fichier main.py ainsi :
Il faut donc installer les deux fichiers sur le microcontrôleur, puis interrompre le programme précédent (CTRL-C), puis effectuer une légère réinitialisation (soft reset : CTRL-D).
Nous commençons à avoir une installation qui fonctionne, mais nous voulons pouvoir interagir à distance.
4. Bluetooth faible énergie
Le Bluetooth à faible énergie (BLE) est un protocole voisin du Bluetooth mais incompatible, néanmoins de nombreux équipements, dont les téléphones et les Raspberry, disposent d’une puce permettant d’utiliser les deux protocoles.
L’utilisation du BLE se fait principalement selon deux modes : connecté ou annonce.
Le mode connecté permet d’interagir entre deux équipements selon un protocole défini par le développeur de l’application. La communication est alors exclusive. Nous n’allons pas utiliser ce mode.
Le mode annonce est celui que nous allons utiliser. Le microcontrôleur va annoncer périodiquement un message permettant de l’identifier et de récupérer les données environnementales.
Il faut commencer par prendre le fichier ble_advertising.py dans les exemples fournis avec les sources MicroPython et l’installer sur le système de fichiers. Puis nous modifions le fichier main.py :
Nous commençons par importer tous les modules nécessaires, dont le support BLE et le chipset HTS221. Ensuite, nous définissons une classe pour gérer la diffusion des données environnementales. Cette classe peut être considérée comme magique dans un premier temps et réutilisée directement. Ensuite, nous faisons les initialisations (Bluetooth, I2C et HTS221). Nous arrivons à la boucle principale qui récupère les données du capteur et construit une chaîne de caractères qui sera diffusée régulièrement. Le terminal affiche les informations de debug :
Un scan BLE (par l’utilisateur root) depuis un ordinateur à proximité détecte notre chaîne de caractères qui contient l’identifiant wb01 et les valeurs de température et d’humidité.
Sur mon /e/phone, les données s’affichent aussi lors d’un scan Bluetooth. Le résultat est présenté dans la figure 7.
Tous les appareils peuvent obtenir l’information en scannant les annonces BLE. Pour pouvoir traiter les données, il faut disposer d’un programme dédié.
5. Demandez à votre autre serpent les données
Quelques lignes de Python permettent de récupérer les données, pour par exemple les transmettre à un serveur MQTT ou les enregistrer directement dans une base de données, ou les afficher sur un joli écran. Le script Python minimal est le suivant :
L’exécution fournit le résultat suivant :
Le code dépend du module bluepy qui n’est pas disponible dans la Debian stable (Bullseye). Il faut donc l’installer avec pip3 install bluepy. Après l’initialisation des modules, nous ajoutons une classe ScanDelegate qui sera appelée quand le scan recevra une annonce. L’instruction print, en commentaire, affichera un message pour chaque annonce. Mais seuls les messages dont le nom correspond au motif seront détaillés. Le nom doit être composé de trois parties séparées par des caractères |. Nous demandons à scanner.scan de scanner pendant 55 secondes. Ce script peut être appelé toutes les minutes par un cron classique. Certaines annonces pourraient alors ne pas être reçues.
Un scan Bluetooth affiché par Wireshark est présenté à la figure 8. Le nom diffusé par la sonde contient bien la chaîne de caractères définie. L’adresse Bluetooth est une adresse locale, choisie par MicroPython. Il est possible de définir celle-ci lors de l’initialisation avec l’instruction BLE.config.
Tout appareil peut afficher les valeurs. La figure 7 montre la console série du microcontrôleur et le téléphone qui scanne les appareils Bluetooth à proximité. Mais pour aller plus loin, il faudrait développer une application spécifique.
6. Le bêtisier
Le développeur expérimentera souvent quelques déboires. Voici quelques pistes pour les éviter ou les résoudre.
L’interpréteur permet d’utiliser les fichiers sans les recompiler. La tentation est alors grande de modifier ceux-ci directement sur la carte. Les éditeurs créent souvent discrètement des fichiers (verrou pour éviter la manipulation simultanée par deux éditeurs ; version précédente). Ces fichiers vont être écrits sur le système de fichiers de la carte, ce qui pose parfois des problèmes.
L’écriture d’un fichier sur la carte prend un temps non négligeable. Le système d’exploitation de la station de travail va écrire les fichiers dans une partie de la mémoire pour rendre la main au programme d’écriture le plus rapidement possible. Au bout d’un certain temps, les fichiers seront synchronisés sur la carte. Pour forcer l’écriture et être sûr que les données écrites sont les données définitives, il faut alors soit démonter le périphérique, soit utiliser la commande sync qui terminera quand toutes les écritures en attente seront réalisées. Si, avant les écritures physiques, la carte subit un reset, est éteinte ou débranchée, alors le système de fichiers sera incohérent. Parfois, seule une intervention physique sur la carte (comme reflasher un nouveau firmware) pourra résoudre le problème. Dans le cas d’interactions lointaines, ce sera difficile.
Grâce à mes étudiants, j’ai constaté que cela arrivait. Pour récupérer le système de fichier, un fsck -y est un bon début. Après, le fsck a retrouvé des bouts de fichiers qu’il faut effacer.
Une erreur de programmation ou un bug du driver peut aussi bloquer la carte, dans ce cas l’utilisation d’un watchdog à titre de précaution pourra réinitialiser la carte.
Comme mentionné précédemment, le système d’exploitation peut interpréter la carte comme un modem et essayer de le configurer.
Conclusion
Nous avons montré comment installer l’interpréteur sur une carte. La documentation montre les adaptations à faire pour une autre plate-forme. Certes, la manipulation n’est pas si simple pour un débutant, mais en se rapprochant d’un fablab, il peut demander un firmware qui contient toutes les librairies dont il peut avoir besoin (ici, le driver hts221 et le support BLE). Il lui restera à finaliser son projet en modifiant uniquement le main.py.
Cet article ne présente pas un produit fini. Il permet aux amateurs motivés de mettre en place des applications. Nous n’avons pas l’intention de commercialiser ces produits. Il y a quelques contraintes à résoudre avant de déployer un produit. La première est le respect de la licence d’utilisation des cartes Nucleo qui interdit d’utiliser ces cartes pour un produit fini. D’autre part, nous n’avons pas vérifié le respect de la norme BLE, en particulier pour les adresses.
Références
[1] Site de la documentation de MicroPython : https://docs.micropython.org
[2] Le site de STM32 pour l’éducation : https://stm32python.gitlab.io/fr/docs/Micropython/install_linux
[3] D. Bodor, « Développer pour Arduino en ligne de commandes (pour de vrai) », 2023,
https://connect-ed-diamond-com/hackable/hk-046/developper-pour-arduino-en-ligne-de-commandes-pour-de-vrai
[4] S. Tardieu, « Blog sur la différence entre les périphériques ttyACM et ttyUSB », 2013,
https://rfc1149.net/blog/2013/03/05/what-is-the-difference-between-devttyusbx-and-devttyacmx/