mercredi 11 décembre 2019

Graphique en temps réel des mesures prises par un Arduino

Cet article a été mis à jour le 7 juillet 2022 (compatibilité Python 3).

Notre objectif consiste à présenter à l'écran d'un ordinateur, sous la forme d'un graphique cartésien qui se met à jour en temps réel, les mesures prises par un capteur branché à une carte Arduino, elle-même branchée à un des ports USB de l'ordinateur. Pour ce faire, nous utiliserons un script en langage Python ainsi que les bibliothèques matplotlib et pyserial.

Circuit

Pour mes tests, j'ai utilisé en guise de capteur un simple potentiomètre contrôlant la tension à l'entrée A0 d'un Arduino Uno. Il va sans dire qu'en modifiant légèrement le sketch, vous pouvez remplacer ce potentiomètre par n'importe quel type de capteur.

Sketch de l'Arduino

Comme vous pouvez le constater ci-dessous, le sketch versé dans l'Arduino est plutôt bref. Il se limite à attendre la réception d'un message UART en provenance de l'ordinateur. Chaque fois que l'Arduino reçoit le caractère 'e", il envoie à l'ordinateur un message constitué de la mesure de l'entrée analogique A0 et de l'instant où cette mesure à été prise. Ce temps est remis à zéro lors de la réception du caractère 'i'.

-
/*
L'Arduino émet par communication série
les mesures d'un capteur (un potentiomètre
branché à l'entrée A0).
Plus d'infos:
https://electroniqueamateur.blogspot.com/2019/12/graphique-en-temps-reel-des-mesures.html
*/
unsigned long tempsDebut;
void setup() {
Serial.begin(115200);
}
void loop() {
int messageRecu = 0;
unsigned long valeurx;
int valeury;
if (Serial.available() > 0) {
messageRecu = Serial.read();
if (messageRecu == 'i') { // commande d'initialisation
tempsDebut = millis(); // remise à zéro du chronomètre
}
if (messageRecu == 'e') { // commande d'envoi de données
valeurx = millis() - tempsDebut;
valeury = analogRead(A0);
Serial.println(String(valeurx)+' '+String(valeury));
}
}
}
-

Installation des bibliothèques matplotlib et pyserial

Le script en Python qui sera exécuté sur l'ordinateur aura besoin de la bibliothèque pyserial, qui gère la communication série, et de la bibliothèque matplotlib, qui se spécialise dans la présentation de données sous forme de graphique.

On peut les installer avec pip:

pip install pyserial
pip install matplotlib

Vous trouverez quelques informations supplémentaires concernant l'utilisation de ces bibliothèques dans ces récents billets, qui sont les précurseurs du présent article: Communiquer en Python avec un Arduino (pyserial) et Rasbperry Pi, présenter les mesures sous forme de graphique cartésien (matplotlib).

Script en Python pour l'ordinateur

Le script fait d'abord un scan des ports série actifs: s'il n'en trouve qu'un seul, il établit la connexion avec ce port. S'il en trouve plusieurs, il demande à l'utilisateur de choisir le port série souhaité. L'utilisateur doit également choisir à quelle fréquence les mesures seront prises (dans mon cas, le maximum était d'environ 1000 mesures par seconde; au-delà, le graphique ne se trace pas).

Le script envoie ensuite le message 'i' à l'Arduino afin de remettre son chronomètre à zéro, et lui envoie régulièrement le signal 'e' pour lui demander une nouvelle mesure, qui est ajoutée au graphique.

-
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
Mise en graphique des données reçues de l'Arduino,
par le port série.
Plus d'infos:
https://electroniqueamateur.blogspot.com/2019/12/graphique-en-temps-reel-des-mesures.html
'''
# bibliothèques nécessaires pour la communication série
import serial
import serial.tools.list_ports
#bibliothèques nécessaires pour le tracé du graphique
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
temps_pause = 50 # nombre de millisecondes entre 2 mesures consécutives
valeurs_x = [] # données en abcisse
valeurs_y = [] # données en ordonnée
baud = 115200 #vitesse de la liaison série (doit être synchro avec l'Arduino)
# routine de mise à jour du graphique
def animate(i):
#on envoie à l'Arduino le message de requête
message = 'e'
arduino.write(message.encode('utf-8'))
donneesBrutes = arduino.readline()[:-2]
if (donneesBrutes): # si on a reçu quelque chose
listeDonnees = donneesBrutes.split() # on sépare l'abcisse et l'ordonnée
valeurs_x.append(float(listeDonnees[0])/1000.0) # conversion des millisecondes en secondes
valeurs_y.append(float(listeDonnees[1]))
plt.cla() # on efface le graphique déjà tracé
plt.plot(valeurs_x, valeurs_y) # tracé de la courbe
plt.title('Mesures du capteur') # titre du graphique
plt.xlabel('temps (s)') # identification de l'abcisse
plt.ylabel('Valeur mesuree') # identification de l'ordonée
print("Recherche d'un port serie...")
ports = serial.tools.list_ports.comports(include_links=False)
if (len(ports) != 0): # on a trouvé au moins un port actif
if (len(ports) > 1): # affichage du nombre de ports trouvés
print (str(len(ports)) + " ports actifs ont ete trouves:")
ligne = 1
for port in ports : # affichage du nom de chaque port
print(str(ligne) + ' : ' + port.device)
ligne = ligne + 1
portChoisi = input('Ecrivez le numero du port desire:')
else:
print ("1 port actif a ete trouve:")
portChoisi = 1
# on établit la communication série
arduino = serial.Serial(ports[portChoisi - 1].device, baud, timeout=1)
print('Connexion a ' + arduino.name + ' a un baud rate de ' + str(baud))
frequence = input("Frequence d'acquisition desiree, en Hz (max: 1000): ")
temps_pause = 1000.0/float(frequence)
#on envoie à l'Arduino le message d'initialisation
message = 'i'
arduino.write(message.encode('utf-8'))
ani = FuncAnimation(plt.gcf(), animate, interval=temps_pause) # mise à jour du graphique
plt.show() #affichage à l'écran
else: # on n'a pas trouvé de port actif
print("Aucun port actif n'a ete trouve")
-

Résultat

Voici un exemple de graphique tracé pendant que je tourne lentement le bouton du potentiomètre.





À lire aussi

Puisque je m'intéresse beaucoup à l'utilisation de l'Arduino comme instrument de mesure scientifique, d'autres façons d'arriver à des résultats similaires ont été explorés dans le passé: graphique cartésien sur Android (avec MIT App Inventor 2), graphique cartésien sur écran OLED SH1106 ou sur LCD Nokia 5110,  envoi des données à un tableur (Excel ou Libre Office Calc), utilisation d'une carte Leonardo pour écrire les données dans un tableur, utilisation du service en ligne Carriots, enregistrement des données sur une carte SD, etc.

Personnellement, j'aime bien la solution abordée dans le présent article: il est facile de modifier le script Python selon nos besoins.

Yves Pelletier   (TwitterFacebook)

4 commentaires:

  1. Gardé dans les tablettes... Une fois réadapté pour ma charge électronique DIY je pourrais afficher et enregistrer mes courbes de décharge de mes batteries...
    Merci au débutant en Python que je suis Pascal / F4CVM

    RépondreSupprimer
  2. Bonjour,
    Lignes 31 et 75 du script python :
    arduino.write("i")
    Mon linux n'aime pas. Je remplace par pour que ca marche
    arduino.write(bytes("e", 'utf-8'))
    "e" ou "i" bien entendu

    RépondreSupprimer
    Réponses
    1. Change le par :arduino.write(b'i') et arduino.write(b'e')

      Supprimer
  3. Bonjour j'aimerais savoir si le code peut être ajusté et exécuté avec 4 tracés de lecture de pression simultanément. Les capteurs agissent comme de potentiomètres.. j'en ai fait l'essai dans les entrés analogues. J'aimerais vraiment pouvoir l'adapter a un banc d'essai. Merci pour la réponse

    RépondreSupprimer