ACTIVITE ARDUINO/PYTHON : Récupération de données à partir d’un anémomètre analogique branché sur une carte Arduino, tracé de graphe avec Python

Anémomètre Arduino/Python

Ce système utilise l’anémomètre analogique DF ROBOT SEN0170, qui fournit une tension (de 0 à 5 V) proportionnelle à la vitesse du vent (de 0 à 30 m/s) :

Un montage a été réalisé à partir de ce capteur pour :

  • afficher la vitesse du vent sur un écran LCD
  • effectuer une acquisition de données avec tracé en temps réel, à l’aide d’une carte Arduino Uno et d’un script Python.

Conception du système


Matériel

Carte Arduino (ou modèle générique)

Un boîtier pour dissimuler les câbles. Il faudra toutefois effectuer quelques découpes pour permettre l’affichage de l’écran LCD, l’accès au connecteur USB de la carte Arduino, ainsi que l’installation de la prise jack femelle pour l’alimentation.

Bloc d’alimentation 12 V

On pourrait choisir une connexion avec prise jack, avec un connecteur jack femelle mono.

Attention à la polarité (à vérifier au multimètre) !!!

Schéma du montage

Nous n’utilisons que 3 fils parmi les 4 disponibles :

  • Fil rouge :12 V
  • Fil noir : GND
  • Fil jaune : Tension correspondant à la vitesse du vent
  • Fil bleu : non connecté (à priori, il s’agit d’une mesure de courant mais j’avoue ne pas en avoir saisi l’intérêt …)

Mode d’emploi : acquisition de la vitesse du vent en fonction du temps


Alimentation et connexion à l’ordinateur

  • Brancher le capteur à l’aide d’un bloc d’alimentation 12 V (adaptateur jack).
  • Relier le capteur à un PC via la carte Arduino.

Configuration de l’environnement Arduino

  • Ouvrir l’IDE Arduino.
  • Vérifier que la carte sélectionnée est bien Arduino Uno via le menu [Outils] > [Type de carte].
  • Sélectionner le port série correspondant via le menu [Outils] > [Port].

Code Arduino

Téléverser ce code :

/*!
 * @file  SEN0170.ino
 * @brief Reading wind speed rating
 * @copyright  Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
 * @license  The MIT License (MIT)
 * @author  DFRobot
 * @version  V1.0
 * @date  2023-08-03
 */
#include <LiquidCrystal.h>
LiquidCrystal lcd(8,9, 4, 5, 6, 7);

long temps;

void setup()
{
  Serial.begin(9600);
  lcd.begin(16,2);
}

void loop()
{
  temps = millis();
  int sensorValue = analogRead(A1);
  float outvoltage = sensorValue * (5.0 / 1023.0);
//  Serial.print("outvoltage = ");
//  Serial.print(outvoltage);
//  Serial.println("V");
  float Level = 6.0 * outvoltage;//The level of wind speed is proportional to the output voltage.
//  Serial.print("wind speed is ");
  Serial.print(temps);
  Serial.print("\t");
  Serial.println(Level);
  lcd.setCursor(0,0);
  lcd.print("Vitesse : ");
  lcd.setCursor(0,1);
  lcd.print(Level);
  lcd.setCursor(8,1);
  lcd.print(" ");
  lcd.setCursor(9,1);
  lcd.print("m/s");
  
//  Serial.println(" m/s");
//  Serial.println();
  delay(50);
}

Lancement de l’acquisition

Exécuter ce script :

import serial
import serial.tools.list_ports
import matplotlib.pyplot as plt
from matplotlib import animation
import time
import sys
# Import pour mon fake arduino
from dataclasses import dataclass
from math import sin

# Paramètres d'acquisition
T_ACQUISITION = 10.0  # durée en secondes


vmin = 0
vmax = 15

# Listes de stockage
liste_temps = []
liste_vitesse = []

# Préparation du graphe
fig = plt.figure()
(line,) = plt.plot([], [])
plt.xlim(0, T_ACQUISITION)
plt.ylim(vmin, vmax)
plt.xlabel("temps (s)")
plt.ylabel("vitesse (m/s")
plt.grid()

# TIPS: penser à changer le nom de son générateur quand il retourne plus la même chose '^_^
# Fonction génératrice : lecture depuis Arduino
def temps_vitesse_depuis_arduino():
    arduino_data = None
    for com_port in serial.tools.list_ports.comports():
        if "Arduino" in com_port.description:
            arduino_data = serial.Serial(com_port.device, 9600)

    print(f"Le port arduino utilisé : {arduino_data.name}")
    print(f"Est-il ouvert ? {arduino_data.is_open}")


    while True:
        try:
            line = arduino_data.readline()
            liste_donnees = line.split()

            vitesse = float(liste_donnees[1].decode())
            temps = float(liste_donnees[0].decode()) / 1000.0

            # TIPS: Il suffit de tester le temps ici pour sortir de la boucle
            if temps >= T_ACQUISITION:
                print("Temps max atteint – arrêt de l'animation.")
                break

            yield temps, vitesse
        except Exception as e:
            print(f"Erreur de lecture : {e}")
            break

    print("Fermeture du port du Arduino")
    arduino_data.close()


# Fonction pour l'animation matplotlib
def animate(data):
    temps, vitesse = data

    liste_temps.append(temps)
    liste_vitesse.append(vitesse)


    print(f"vitesse = {vitesse:+6.2f}m/s | temps = {temps:6.3f}s")

    line.set_data(liste_temps, liste_vitesse)
    return (line,)

# Lancement de l'animation
ani = animation.FuncAnimation(
    fig,
    animate,
    frames=temps_vitesse_depuis_arduino,
    interval=0,
    repeat=False,
    cache_frame_data=False
)

plt.show()
plt.close(fig)

# Sauvegarde des données dans un fichier texte
with open("data_arduino.txt", "w") as fichier:
    fichier.write("temps_s\tvitesse_deg\n")
    for t, a in zip(liste_temps, liste_vitesse):
        fichier.write(f"{t:.3f}\t{a:.2f}\n")

print("Les données ont été enregistrées dans 'data_arduino.txt'.")

Remarque

Le script de récupération a été amélioré, par rapport aux versions précédentes, grâce à mon collègue Allan Petrillo du lycée Marcellin Berthelot de Saint-Maur-des-Fossés, avec le concours d’un ami informaticien, afin de respecter pleinement les bonnes pratiques de programmation. Merci à eux deux !

  • Le graphique s’affiche en temps réel pendant l’acquisition.
  • Les données sont enregistrées dans le fichier texte data_arduino.txt, situé dans le même dossier que le script.

En cas de blocage

  • Redémarrer le shell Python avec Ctrl + K.
  • Relancer le script avec Ctrl + Shift + E.

Scripts Python et Arduino disponibles sur ce lien Github

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *