L’Internet des Objets
6.1 Programmer avec Micropython
Programmer un microcontrôleur peut se faire de plusieurs façons. Le fabricant propose une ou plusieurs méthodes avec éventuellement une IDE. Ces IDE sont souvent assez complexes à prendre en main. De plus, elles sont spécifiques à une famille de processeurs.
Pour programmer le processeur, il faut soit écrire directement en assembleur, soit un langage plus ou moins proche de C++, soit en un dialecte de Python. L’assembleur est de moins en moins utilisé et difficile pour le dbutant. Le C++ peut être utilisé directement en ligne de commande avec g++, soit en utilisant une IDE, comme Arduino. Enfin, le dialecte Python utilisé, MicroPython, impose un firmware spécifique.
6.1.1 Installer le firmware Micropython
Pour obenir python pour un microcontrôleur, il y a deux méthodes : soit récupérer le firmware, soit le recréer. Le site de MicroPython (https://micropython.org/) propose des firmwares pour beaucoup de
cartes. Nous présentons la compilation pour une carte d’exemple Nucleo WB55. L’installation du firmware sur la carte dépend du modèle précis.
La compilation à partir des sources git permet d’obtenir la dernière version. Dans ce cas, le firmware indique dirty pour indiquer qu’il ne s’agit pas d’une version stable. Le site propose aussi la version stable.
6.1.1.1 Récupération des sources Micropython
L’installation est aussi présentée sur les sites suivant :
- stm32python
-
https://stm32python.gitlab.io/fr/docs/Micropython/install_linux/index
- port STM32
-
https://github.com/micropython/micropython/tree/master/ports/stm32
Il faut commencer par préparer la station de compilation en installant :
-
• le support git ;
-
• le cross-compilateur : gcc-arm-none-eabi ;
il faut extraire l’archive disponible sur https://github.com/micropython/micropython. Ensuite, pour un STM32, il faut suivre la documentation sur ports/stm32. Pour obtenir la mise à jour, il faut effectuer un git pull.
Si des fichiers sont modifiés, il peut être pratique de restaurer la version officielle. Pour lister les modifications, la commande git pull :
git diff ... // lignes éffacées // --- a/py/makeversionhdr.py +++ b/py/makeversionhdr.py @@ -120,6 +120,8 @@ def make_version_header(repo_path, filename): build_date = datetime.datetime.utcfromtimestamp( int(os.environ["SOURCE_DATE_EPOCH"]) ).date() + git_tag = git_tag + "AF" +Pour restaurer la version d’origine :
git restore py/makeversionhdr.py
Le fichier py/makeversionhdr.py contient la définition de l’étiquette affichée lors du démarrage du microcontrôleur. Cela facilite l’identification des versions modifiées. Dans le fichier py/makeversionhdr.py, il faut modifier la fonction make_version_header pour rajouter la ligne :
git_tag = git_tag + "-AF"
Le logiciel terminal présentera le nouveau message :
MicroPython v1.20.0-117-ged7a3b11d-dirty-AF...
Il faut alors compiler le cross-compilateur :
make -C mpy-cross
Pour information, les temps de compilation sont :
- Station 2019 4CPU
-
- 1 thread
-
20 secondes
- 4 threads
-
6 secondes
- Station 2023, 12 CPU
-
- 1 thread
-
10 secondes
- 12 threads
-
2 secondes
6.1.1.2 Support des cartes STM32 pour µP
Il faut ensuite ajouter les définitions des cartes spécifiques. Pour cela, il faut se déplacer dans le répertoire ports/stm32. Il faut télécharger les sous-modules :
cd ports/stm32 make submodules
Puis, lister les cartes du répertoire boards, pour créer le firmware de cette carte.
- Discovery IoT
-
make -j BOARD=B_L475E_IOT01A
- WB55
-
make -j BOARD=NUCLEO_WB55
- Dongle WB55
-
make -j USBDONGLE_WB55
Pour déterminer les fonctionnalités accessibles en micropython, il faut aller vérifier la description dans le répertoire dédié à cette carte (répertoire board). Ainsi, il est possible de vérfier qui a le bluetooth disponible :
date ; grep -ir bluetooth . samedi 20 février 2021, 09:03:07 (UTC+0100) NUCLEO_WB55/mpconfigboard.mk:MICROPY_PY_BLUETOOTH = 1 NUCLEO_WB55/mpconfigboard.mk:MICROPY_BLUETOOTH_NIMBLE = 1 NUCLEO_WB55/mpconfigboard.h:// Bluetooth config
Le support bluetooth est disponible pour les WB55 mais pas pour la discovery.
Pour le discovery, le répertoire build-B_L475E_IOT01A contient le firmware (suffixe bin) qu’il faut flasher. Pour cela, le périphérique est considéré comme un mass storage qu’il faut monter, puis copier le fichier sur ce répertoire. Éventuellement, utiliser la commande sync pour vider les caches et copier effectivement le fichier sur la carte. Pour vérifier, il suffit d’utiliser un logiciel terminal, comme minicom :
>>> 2+2 4 >>> from pyb import LED >>> led=LED(1) >>> led.toggle() >>> led.toggle()
La carte peut continuer à être utilisée par l’IDE arduino. Dans, ce cas, le firmware python sera effacé.
Il est possible d’optimiser (un peu) la taille du firmware généré (environ 2à3%). La compilation prendra (un peu) plus (?) de temps. Pour cela, il faut ajouter l’option LTO=1 (Link Time Optimization) lors de la génération du firmware :
make BOARD=NUCLEO_WB55 LTO=1
- Avec LTO
-
9,8s (34,206s) 376K (383384 octets)
- Sans LTO
-
10,3s (37s)
- Sans spécification
-
10,3s (37,2s) 392K (397832 octets)
Après quelques tests, le gain est faible, mais la compilation avec LTO est en fait un peu plus rapide.
6.1.1.3 Gelez le serpent
Nous pouvons donc installer tous les fichiers python dans l’espace utilisateur. Il est possible d’ajouter ces fichiers dans le firmware. C’est plus long à peaufiner, car il faut recommencer la procédure de création et de flashage du firmware pour chaque modification. Mais cela permet de figer les bibliothèques et laisser l’espace utilisateur pour un amateur. Les fonctionnalités sont disponibles et il programme uniquement la boucle de haut niveau qui l’intéresse.
Pour cela, il y a deux façons de procéder. Le site µP
https://docs.micropython.org/en/latest/reference/manifest.html
indique qu’il est possible d’ajouter des packages ou des modules. Il semble qu’un package contienne plusieurs fichiers python et qu’un module soit un tel fichier.
Il faut indiquer l’utilisation de ces fichiers en les déclarant dans un fichier manifest. Le fichier de descprition de la carte peut pointer sur ce fichier. Ainsi, dans le répertoire de la carte NUCLEO_WB55, il y a un fichier mpconfigboard.mk dans lequel on peut ajouter la ligne suivante :
FROZEN_MANIFEST ?= /home/arno/µP/NUCLEO_WB55/manifest.py
Le point d’interrogation permet de définir cette variable sauf si la ligne de commande l’a déjà definie (enfin, je crois).
Le manifeste indique les modules ou packages à intégrer :
cat /home/arno/µP/NUCLEO_WB55/manifest.py
module("hts221.py")
module("ble_advertising.py")
6.1.1.4 Installation en mode DFU
Dans certains cas, il peut être utile d’installer le firmware en utilisant le mode DFU Device firmware update. En particulier, la carte Nucleo WB55 impose d’utiliser un cavalier pour sélectionner l’utilisation du ST-Link ou du mode USB-MCU. Le premier permettant de changer le firmware et le second facilitant l’interaction avec MicroPython. La carte WB55-USB ne dispose pas du ST-Link, un interrupteur permet de la passer en mode DFU.
Pour les deux cartes, il est possible d’activer le mode DFU sans interaction mécanique. La carte étant en mode MicroPython, elle apparaît comme un périphérique Pyboard :
lsusb ID f055:9800 MicroPython Pyboard Virtual Comm Port in FS Mode
Pour la passer en mode DFU, il faut utiliser le chargeur de démarrage :
on v1.20.0-61-ga000c61d5-dirtyAF ... USBDongle-WB55 ... >>> machine.bootloader()
Elle apparaît donc comme un périphérique DFU :
lsusb ID 0483:df11 STMicroelectronics STM Device in DFU Mode
Après l’installation du paquet dfu-util, nous trouvons un périphérique. Il semble qu’il ne le trouve pas à chaque fois.
=> dfu-util -l ... Found DFU: [0483:df11] ver=0200, devnum=107, cfg=1, intf=0, path="1-1.4.1", alt=2, name="@OTP Memory /0x1FFF7000/01*1024 e", serial="205B3766594D" Found DFU: [0483:df11] ver=0200, devnum=107, cfg=1, intf=0, path="1-1.4.1", alt=1, name="@Option Bytes /0x1FFF8000/01*128 e", serial="205B3766594D" Found DFU: [0483:df11] ver=0200, devnum=107, cfg=1, intf=0, path="1-1.4.1", alt=0, name="@Internal Flash /0x08000000/256*04Kg", serial="205B3766594D"
Nous voyons trois périphériques DFU. Le dernier (numéro 0) indique que c’est la flash qui commence à l’adresse indiquée.
La compilation de micropython pour le dongle (USBDONGLE_WB55) a produit un fichier firmware.dfu que nous allons flasher.
=> dfu-util -a 0 -D /tmp/firmware.dfu dfu-util 0.9 Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc. Copyright 2010-2016 Tormod Volden and Stefan Schmidt This program is Free Software and has ABSOLUTELY NO WARRANTY Please report bugs to http://sourceforge.net/p/dfu-util/tickets/ Match vendor ID from file: 0483 Match product ID from file: df11 Opening DFU capable USB device... ID 0483:df11 Run-time device DFU version 011a Claiming USB DFU Interface... Setting Alternate Setting #0 ... Determining device status: state = dfuIDLE, status = 0 dfuIDLE, continuing DFU mode device DFU version 011a Device returned transfer size 1024 DfuSe interface name: "Internal Flash " file contains 1 DFU images parsing DFU image 1 image for alternate setting 0, (1 elements, total size = 215720) parsing element 1, address = 0x08000000, size = 215712 Download [=========================] 100% 215712 bytes Download done. done parsing DfuSe file
Pour le moment, il faut redémarrer le microcontrôleur pour revenir en mode MicroPython.
6.1.1.5 Premières Interactions avec µP
Pour exécuter un script, il faut le télécharger. Dans l’arborescence de micropython, dans le répertoire tools, il y a l’exécutable pyboard.py (d’autres existent, comme mpremote, armpy). Il faut disposer d’un programme python (test.py) :
from pyb import Pin, Timer, ExtInt, LED
import time
led=LED(1)
while 1:
led.toggle()
time.sleep_ms(500)
Ensuite, il faut lancer la commande :
python3 pyboard.py --device '/dev/ttyACM1' test.py
La diode se met à clignoter. Le programme peut être interrompu, le clignotement continue.
Pour copier un fichier sur le microcontroleur, il faut entre la commande suivante :
pyboard.py --device /dev/ttyACM0 -f cp main.py :
Ne pas oublier le deux-points !
Un seul programme à la fois
Pour vérifier, il est possible de se connecter avec un logiciel terminal et lister le répertoire :
import uos uos.listdir()
Pour charger et exécuter un programme, il est donc possible de faire :
pyboard.py --device /dev/ttyACM0 -f cp discoHTS221.py :
Le fichier est défini ainsi :
import pyb
import time
print("coucou")
Pour l’exécuter :
pyboard.py --device /dev/ttyACM0 -c 'import discoHTS221' coucou
Ou utilise un logiciel terminal et entrer la commande :
>>> import discoHTS221 coucou
Pour réinitialiser la carte, il suffit de faire ctrl-d dans l’interpréteur ce qui effectue une réinitialisation légère. Pour une réinitialisation complète, voici le code à entrer :
import machine machine.reset()
6.1.2 Programmer une carte micropython
Le bluetooth, plus spécifiquement la version basse énergie, permet de communiquer entre un capteur et un ordinateur sous GNU/Linux. Nous allons utiliser la carte nucleo WB55 (voir 2.1.1).
Après avoir installé micropython sur la carte, elle se comporte comme plusieurs périphériques de stockage et plusieurs liaisons série. Les deux connecteurs USB permettent d’alimenter la carte.
- ST-LINK
-
Ce connecteur est relié au composant qui permet de recharger le système complet. Il apparaît comme :
-
• liaison série : (USB ACM device ttyACM0) ;
-
• Périphérique de stockage : sdX 80 512-byte logical blocks : (41.0 kB/40.0 KiB).
-
- USB USER
-
semble pouvoir être utilisé par le programme installé. Avec micropython, il devient :
-
• Périphérique de stockage : MicroPy pyboard Flash 768 512-byte logical blocks : (393 kB/384 KiB) ;
-
• liaison série : Pyboard Virtual Comm Port in FS Mode.
-
Le programme Pyboard permet de communiquer avec le programme résident. Il est possible d’interagir de plusieurs manières :
-
1. En remplaçant le firmware sur le périphérique du ST-Link ;
-
2. En communicant avec la liaison série du ST-Link, par minicom ou pyboard ;
-
3. En utilisant le système de fichiers micropython sur le connecteur utilisateur. Il peut être utile de rebooter la carte en utilisant le bouton reset. Celui-ci n’est pas forcément très accessible si une carte fille est présente.
-
4. En utilisant la liaison série micropython. Pendant les premiers essais, les mêmes informations étaient affichées sur les deux consoles.
L’installation de micropython devrait créer une petite partition accessible comme mémoire de masse (mass storage). L’installation initiale va contenir les fichiers :
- boot.py
-
exécuté lors du démarrage à froid de la carte ;
- main.py
-
le fichier exéctuté ensuite.
Le fichier boot.py définit en particulier comment se comporte le microcontrôleur (souris ou mémoire) et le pays.
Le fichier main.py est censé s’exécuter juste après.
6.1.2.1 Les arguments sur la ligne de commande python
Pour utiliser des arguments sur la ligne de commande, il faut importer sys.
import sys
print (' arguments:', len(sys.argv), 'arguments.')
print ('Argument List:', str(sys.argv))
6.1.2.2 La gestion µP du µC
Il est souvent utile de déterminer sur quelle carte le programme tourne. Cela permet, par exemple, d’avoir une seule version du code qui agit comme émetteur ou receveur en fonction de la carte.
MicroPython fournit une fonction qui fait cela :
>>> machine.unique_id()
b'\x1f\x00>\x00\x14PMYC74 '
>>> if (I == machine.unique_id()):
... print("GOOD")
...
...
...
GOOD
La mémoire constitue une limitation importante pouvant générer des problèmes difficiles à corriger. La documentation de MicroPython peut permettre d’optimiser son usage :
https://docs.micropython.org/en/latest/reference/constrained.html#
>>> import gc
>>> import micropython
>>> micropython.mem_info()
stack: 468 out of 15360
GC: total: 150272, used: 148688, free: 1584
No. of 1-blocks: 8775, 2-blocks: 30, max blk sz: 55, max free sz: 53
>>> print('Initial free: {} allocated: {}'.format(gc.mem_free(), gc.mem_alloc()))
Initial free: 100688 allocated: 49584
micropython.mem_info(1)
6.1.2.3 La gestion spécifique des WB55
>>> stm.rfcore_status() 0 stm.rfcore_fw_version(12) (1, 0, 0, 4, 3)
6.1.2.4 Les timers µP
6.1.2.5 La gestion du système µP
Le système de fichiers du firmware est aussi accessible depuis le firmware. Pour l’explorer, nous allons commencer par utiliser la librairie os.
import os
os.getcwd()
'/flash'
os.listdir('/')
['flash']
os.listdir('/flash')
['boot.py', 'main.py']
Cette librairie fournit les primitives pour gérer les systèmes de fichier. Pour lire ou écrire dans un fichier, nous allons utiliser les fonctions classique de python :
fichier=open('/flash/main.py','r')
res=fichier.read()
print(res)
import ssd1327
import math
from machine import SPI, Pin
# Setup display on SPI bus
spi = SPI(1)
... // tout le contenu du fichier...