L’Internet des Objets

L’Internet des Objets

6.2 Communication série en Micropython

Page précédente Page suivante

6.2.1 Connexion entre µC et l’ordinateur

La connexion entre un microcontrôleur et l’ordinateur est souvent possible avec une connexion série (UART) ou simulée par USB. Cette connexion peut être établie :

  • en direct avec un logiciel terminal ;

  • avec un script (en python, par exemple) en local ;

  • à distance en connexion IP ;

  • à distance en connexion BLE.

6.2.1.1 Connexion UART avec un logiciel terminal

Avec GNU/Linux, il y a de nombreux logiciels qui permettent d’interagir avec une connexion série (UART). Les logiciels minicom et screen sont les plus utilisés. Il faut d’abord connaître le nom du périphérique. La commande suivante liste les péripheriques disponibles :

=> python3 -m serial.tools.list_ports
/dev/ttyACM0
/dev/ttyS0
2 ports found

Sur cet ordinateur, nous avons le port classique ttyS0 et le port identifé comme un modem ttyACM0. Les périphérique USB peuvent s’identifier soit comme un modem ttyACM, soit comme un composant série ttyUSB. Le numéro permet d’identifier le composant, si plusieurs sont branchés. Le système d’exploitation peut essayer d’essayer de manipuler tout seul le modem, ce qui peut poser quelques perturbations de la communication.

(image)

Fig. 6.1 : Configuration série de minicom.

Sous GNU-Linux, les programmes peuvent avoir besoin d’être configurés spécifiquement. Les deux équipements doivent avoir le même réglage. La figure  ?? présente l’interface de minicom présentant les réglages les plus fréquents :

8N1

le réglage classique ;

8

8 bits transmis,

N

parité impaire,

1

bit(s) de stop ;

Vitesse

un multiple ou sous multiple de 9600 bauds,

contrôle de flux

 : pour demander au partenaire de moduler son débit :

matériel

utilise des circuits matériels spécifiques

Logiciel

utilise les caractères de contrôle xoff ctrl-s pour bloquer la communication et xn ctrl-q pour la continuer.

En général, avec un microcontrôleur n’utilise pas le contrôle de flux matériel et rarement le logiciel. Les liaisons qui utilisent l’USB n’ont pas réellement d’UART et donc le simule. La spécification de la vitesse est probablement ignorée.

Les UART utilisent des circuits V24. Dans cet avis, un circuit est consitué par un câble partant d’un équipement vers l’autre. Les câbles minimum à connecter sont :

102

retour commun, la terre ou masse commune ;

103

Transmission (TX) de l’ETCD vers l’ETTD ;

104

Réception (RX) de l’ETTD vers l’ETCD.

En transmission, l’ETTD est l’Équipement Terminal de Circuit de Données (l’ordinateur, par exemple) ; l’ETCD est l’Équipement Terminal de Circuit de Données (le modem, par exemple). Avec un microcontrôleur, les circuits ne sont pas labellisés par leur désignation V24, mais par leur abréviation RS232. Il suffit de relier les masses, et de croiser TX vers RX (et réciproquement). Parfois, un des équipements, comme les cartes filles peuvent recevoir sur la broche de Transmission et réciproquement.

Si la communication utilise des cables dédiés, la transmission d’un élément d’information peut avoir différents réglages, même si 8N1 est le plus fréquent. La figure 6.2 présente la constitution d’une trame. Le nombre de bits transmis par message n’est pas forcément 8, mais peut varier de 5 à 9 ; un bit de parité peut être rajouté pour détecter les erreurs de transmission ; la transmission prend fin par un espace de stop, d’une longueur de 1 ou 2 bits (voire 1,5).

(image)

Fig. 6.2 : Constitution d’une trame UART (source Wikipedia).

6.2.1.2 Connexion UART avec un script python

Sur un système Gnu/Linux, un script peut gérer la communication série. La librairie python-serial (paquet python3-serial) permet de gérer une communication série (UART ou USB). La documentation en ligne est disponible à https://pythonhosted.org/pyserial/. Cette librairie fournit aussi un outil qui liste les ports susceptibles d’être utilisés par une communication série. Cet outil peut être utilisé directement depuis la ligne de commandes :

=> python3 -m serial.tools.list_ports
/dev/ttyACM0
/dev/ttyS0
/dev/ttyS1
/dev/ttyS2
/dev/ttyS3
5 ports found

Cet ordinateur possède quatre équipements UART, un seul possède le connecteur sur le chassis. Un microcontroleur est présent sur un connecteur USB, il est identifié comme un modem (TTYACM). Cet outil peut aussi être utilisé dans un script.

#! /usr/bin/env python

import serial
import serial.tools.list_ports

# ===============================================================
# liste les ports série
serial_port = serial.tools.list_ports.comports()

pyport = "none"

# boucle sur les ports pour en trouver un qui contient 'Pyboard'
# s'il y en a plusieurs, en choisit un

for port in serial_port:
    dev, desc, hwid= port
    print(dev + " " + desc)
    if ("Pyboard" in desc):
        pyport = dev
        print ("Trouvé: " + dev)

# Si aucun port ne convient renvoie -1
if ( pyport == "none" ):
    print("Pas de port utilisable")
    exit(-1)

print("Port utilisable: " + pyport)

Après avoir sélectionné le port, il est possible de l’ouvrir. Il faut définir un objet de la classe serial.Serial. Si le nom est différent de none, alors le port est ouvert avec des paramètres par défaut. Il faut donc créer un objet :

  • soit avec le paramère none, définir les valeurs, puis l’ouvrir ;

  • soit en définissant les paramètres lors de l’invocation :

    P=serial.Serial("/dev/ttyACM0",
                     baudrate=200,
                     xonxoff=False,
                     parity=serial.PARITY_NONE)
    
  • soit avec son nom, puis le fermer pour changer les paramètres et le rouvrir.

La variable timeout définit le comportement lors d’une lecture si le nombre d’octets demandé n’est pas disponible :

none

bloque la réception tant que le nombre n’est pas atteint ;

0

retourne immédiatement quelque soit le nombre d’octets disponibles ;

x

un nombre flottant : retourne immédiatement si le nombre d’octets est atteint, sinon attends la temporisation demandée.

L’écriture dispose aussi de la variable write_timeout.

#! /usr/bin/python3

import serial

#serial.list_ports()
tty=serial.Serial('/dev/ttyACM0')
print(tty.name)

while True:
        line = tty.readline()
        print(line)

Dans la boucle while, il suffit de tester la ligne pour éventuellement faire des actions spécifiques.

6.2.1.3 Connexion UART en BLE

Une autre possibilité consiste à utiliser la connexion BLE pour établir une liaison série. Pour cela, il faut adapter le microcontrôleur et installer le support sur l’ordinateur.

Sur le microcontroleur, le site stm32python propose un script fonctionnel :

https://stm32python.gitlab.io/fr/docs/Micropython/BLE/BLEREPL

Sur l’ordinateur, il est possible d’utiliser un environnement python. Pour commencer, il faut installer le support python :

apt install python3-full python3-serial

Puis un projet spécifique. Nous avons testé ble-serial : https://pypi.org/project/ble-serial/. Nous avons successivement :

  • 1. Activé un environnement python virtuel.

  • 2. Installé ble-serial.

  • 3. Réalisé un scan, trouvé le microcontrôleur mpy-repl.

  • 4. Listé les caractéristiques.

  • 5. Établi la connexion, celle ci ouvre un port /tmp/ttyBLE.

  • 6. Connecté un logiciel terminal sur ce port, La figure 6.3 montre en haut la connexion série par le bus USB, celle du bas par la connexion BLE.

(image)

Fig. 6.3 : Connexion REPL sur ttyBLE.

$ python -m venv ble-venv
$ source ble-venv/bin/activate
$ pip install ble-serial
python -m ble_serial.scan
Started general BLE scan
...
02:02:27:4E:3D:1E (rssi=-35): mpy-repl
D8:15:F0:EE:90:6A (rssi=-81): D8-15-F0-EE-90-6A



(ble-venv) root@piracanta:~# ble-scan -d 02:02:27:4E:3D:1E
Started general BLE scan
02:02:27:4E:3D:1E (rssi=-38): mpy-repl
...

Finished general BLE scan

Started deep scan of 02:02:27:4E:3D:1E

Found device 02:02:27:4E:3D:1E: mpy-repl (out of 1)
SERVICE 6e400001-b5a3-f393-e0a9-e50e24dcca9e (Handle: 19): Nordic UART Service
     CHARACTERISTIC 6e400002-b5a3-f393-e0a9-e50e24dcca9e (Handle: 23): Nordic UART RX ['write']
     CHARACTERISTIC 6e400003-b5a3-f393-e0a9-e50e24dcca9e (Handle: 20): Nordic UART TX ['notify']
         DESCRIPTOR 00002902-0000-1000-8000-00805f9b34fb (Handle: 22): Client Characteristic Configuration
SERVICE 00001801-0000-1000-8000-00805f9b34fb (Handle: 15): Generic Attribute Profile
     CHARACTERISTIC 00002a05-0000-1000-8000-00805f9b34fb (Handle: 16): Service Changed ['indicate']
         DESCRIPTOR 00002902-0000-1000-8000-00805f9b34fb (Handle: 18): Client Characteristic Configuration

Completed deep scan of 02:02:27:4E:3D:1E
ble-serial -d 02:02:27:4E:3D:1E
10:02:06.184 | INFO | linux_pty.py: Port endpoint created on /tmp/ttyBLE -> /dev/pts/4
10:02:06.186 | INFO | ble_interface.py: Receiver set up
10:02:07.687 | INFO | ble_interface.py: Trying to connect with 02:02:27:4E:3D:1E: mpy-repl
10:02:10.014 | INFO | ble_interface.py: Device 02:02:27:4E:3D:1E connected
10:02:10.015 | INFO | ble_interface.py: Found write characteristic 6e400002-b5a3-f393-e0a9-e50e24dcca9e (H. 23)
10:02:10.017 | INFO | ble_interface.py: Found notify characteristic 6e400003-b5a3-f393-e0a9-e50e24dcca9e (H. 20)
10:02:10.245 | INFO | main.py: Running main loop!

6.2.2 Maîtrisez le temps

La gestion du temps dépend à la fois du système d’exploitation (un système GNU/Linux ou un microcontrôleur) et du langage de programmation utilisé. Les ordinateurs conservent l’heure dans un circuit spécifique qui reste alimenté par une pile ou batterie. Le raspberry ne conserve pas l’horloge et doit donc être remis à l’heure à chaque démarrage. Certains microcontrôleurs disposent d’un circuit qui conserve l’heure : il s’agit du Real Time Controller ou RTC. Ce circuit doit resté alimenté électriquement sinon, l’heure est perdue.

Quel que soit l’équipement utilisé, que ce soit une horloge à balancier ou une horloge atomique, l’heure se décale et devient de plus en plus erronée. Il faut donc la synchroniser.

Il existe principalement deux façons de synchroniser ses horloges : le protocole NTP (Network Time Protocol) ou l’utilisation de satellites. La synchronisation par NTP permet de synchroniser avec l’heure universelle avec une précision de l’ordre de la milliseconde. Les équipements du réseau local peuvent utiliser ce protocole pour se synchroniser à la microseconde.

6.2.2.1 Gerez le temps en Python

Sur un ordinateur, la gestion du temps se fait en utilisant le module datetime. Si l’ordinateur est connecté à Internet, il sera synchronisé en utilisant le protocole NTP. Il pourra ensuite synchroniser les microcontrôleurs connectés.

>>> import datetime
>>> t = datetime.datetime.now()
>>> t
datetime.datetime(2025, 8, 10, 18, 5, 47, 13246)
>>> print(t)
2025-08-10 18:05:47.013246
>>> print(type(x))


L’objet de type datetime contient sept valeurs : année, jour, mois, heure, minutes, secondes, microsecondes.

Il est aussi possible de faire de l’arithmétique sur les datetime :

>>> t1 = datetime.datetime.now()
>>> t2 = datetime.datetime.now()
>>> t2-t1
datetime.timedelta(seconds=5, microseconds=945006)
>>> print(t2-t1)
0:00:05.945006

6.2.2.2 Gerez le temps en MicroPython

Pour gérer le temps sur un microcontrôleur, il faut que celui-ci dispose d’une horloge RTC. Nous allons commencer par regarder les STM32. La documentation présente deux façons d’utiliser cette horloge : le module machine ou le module pyb. Les documentation sont très similaires mais n’indiquent pas de différence. Sur une carte Nucleo, l’essai montre que les deux modules paraissent équivalents. Nous pourrions utiliser le module machine pour lequel la documentation est plus complète. Mais certaines fonctions (comme now) de la documentation de machine semblent non fonctionnelles.

MicroPython v1.25.0-...
>>> Rm=machine.RTC()
>>> Rp=pyb.RTC()
>>> Rm

>>> Rp

>>> if (Rm == Rp):
...     print("Identique")
Identique
>>> Rm.now()
Traceback (most recent call last):

L’heure est accessible par la fonction datetime.

>>> RTC=machine.RTC() ; RTC.datetime()
(2015, 1, 1, 4, 12, 49, 20, 699615)

Il y a 8 valeurs : année, mois, jour, minute, seconde, fraction). La fraction dépend du matériel, ici des microsecondes.

Les ordinateurs gèrent le temps comme le nombre de secondes écoulées depuis l’aube des temps. Pour les systèmes Linux, c’est depuis le 1\( ^{\mbox {er}} \) janvier 1970(UTC). Pour d’autres, c’est depuis l’an 2000. Pour le déterminer, il faut utiliser le module time. Pour un STM32, c’est 2000.

>>> import time
>>> time.gmtime(0)[0]
2000

Pour mesurer une durée, il est possible d’utiliser la fonction tick, qui permet d’obtenir une durée en milli ou microsecondes.

RTC=machine.RTC() ;
t1=time.ticks_ms()     ;   D=RTC.datetime() ; t2=time.ticks_ms()
t1=time.ticks_ns()     ;   D=RTC.datetime() ; t2=time.ticks_ns()
print(t2 - t1)

Sans l’affectation de la variable D, le délai est d’environ 3682µs. Avec l’affectation, on ontient environ 70µs. Ceci peut nous aider à estimer l’erreur lors de la synchronisation de l’horloge.

6.2.3 Programmer une carte micropython

Page précédente Page suivante