Communication série Arduino Python : pyserial, CSV et graphique
Communication série Arduino Python avec pyserial : installation, trouver le port, enregistrer les mesures en CSV, envoyer des commandes, graphique en direct.
La communication série Arduino Python, c'est un programme Python qui tourne sur ton ordinateur et échange des données avec un Arduino par le câble USB. L'Arduino mesure un capteur et écrit le résultat sur le port série ; Python lit ces lignes avec la bibliothèque pyserial, les enregistre dans un fichier CSV, les affiche sur un graphique ou prend une décision et renvoie une commande à l'Arduino. Tu combines ainsi le contrôle matériel précis de l'Arduino avec la puissance de Python pour le traitement des données, les graphiques, l'intelligence artificielle et l'automatisation. Aucun module supplémentaire n'est nécessaire : le câble USB qui te sert à téléverser tes programmes suffit.
Dans ce guide, tu vas apprendre pas à pas à installer pyserial, à trouver automatiquement le port de l'Arduino, à enregistrer les données d'un capteur avec un horodatage dans un fichier CSV, à envoyer des commandes LED et PWM depuis Python vers l'Arduino et à tracer un graphique en direct avec matplotlib. Nous verrons ensuite comment choisir le format des données et la fin de ligne, les deux problèmes les plus déroutants (l'erreur de port occupé et le redémarrage de la carte à l'ouverture du port) et ce qui change quand on fait la même chose en MicroPython sur un Raspberry Pi Pico. Les exemples sont écrits pour l'Arduino Uno ; les modifications nécessaires pour l'ESP32 et le Pico sont indiquées à part.

Comment l'Arduino communique-t-il avec Python ?
La puce USB-série de l'Arduino Uno (un ATmega16U2 sur les cartes officielles, le plus souvent un CH340 sur les clones) apparaît sur l'ordinateur comme un port série virtuel : COM3, COM4, etc. sous Windows ; /dev/cu.usbmodem… ou /dev/cu.usbserial-… sous macOS ; /dev/ttyACM0 ou /dev/ttyUSB0 sous Linux. Chaque caractère écrit côté Arduino avec Serial.print() passe par ce port vers l'ordinateur sous forme d'octets ; côté Python, pyserial ouvre le port comme un fichier, y lit et y écrit. Une distinction importante : l'Arduino Uno n'exécute pas de code Python. La carte exécute du code Arduino écrit en C++, l'ordinateur exécute Python, et les deux ne se parlent que par la liaison série. Pour que les deux côtés parlent la même langue, ces réglages doivent correspondre :
| Réglage | Côté Arduino | Côté Python | Attention |
|---|---|---|---|
| Port | Créé automatiquement par l'USB | serial.Serial("COM3", ...) | Le nom du port dépend du système d'exploitation |
| Débit (bauds) | Serial.begin(115200) | serial.Serial(port, 115200) | S'ils diffèrent, des caractères illisibles arrivent |
| Trame | 8 bits de données, sans parité, 1 bit de stop (8N1) | La valeur par défaut de pyserial est aussi 8N1 | Il est rarement nécessaire de la changer |
| Fin de ligne | Serial.println() ajoute \r\n à la fin | readline() lit jusqu'à rencontrer \n | N'oublie pas d'ajouter \n en envoyant depuis Python |
| Délai d'attente | Serial.setTimeout(), 1000 ms par défaut | timeout=1 (secondes) | Avec timeout=None, readline() attend indéfiniment |
Installer pyserial (Windows, macOS, Linux)
- Python 3 doit être installé. Sous Windows, coche la case Add python.exe to PATH sur le premier écran de l'installateur téléchargé sur le site officiel de Python ; sous macOS et Linux, Python 3 est généralement déjà présent.
- Ouvre un terminal (Invite de commandes ou PowerShell sous Windows) et tape python -m pip install pyserial. Si Windows ne reconnaît pas la commande python, essaie py -m pip install pyserial ; sous macOS et Linux, la commande peut être python3 -m pip install pyserial.
- Vérifie l'installation : python -c "import serial; print(serial.__version__)" doit afficher un numéro de version comme 3.5.
- La partie sur le graphique en direct nécessite aussi matplotlib : python -m pip install matplotlib.
- Sous Linux, ajoute ton utilisateur au groupe dialout pour pouvoir ouvrir le port : sudo usermod -a -G dialout $USER, puis déconnecte-toi et reconnecte-toi. Si pip affiche l'erreur externally-managed-environment sous Raspberry Pi OS ou une version récente d'Ubuntu, utilise sudo apt install python3-serial ou crée un environnement virtuel avec python3 -m venv et lance pip à l'intérieur.
Sur quel port est l'Arduino ? Trouver le port avec Python
Le plus simple est de regarder le menu Outils > Port de l'IDE Arduino, mais Python peut aussi lister les ports. Tape python -m serial.tools.list_ports -v dans un terminal : tu verras le nom, la description et l'identifiant matériel (VID:PID) de chaque port. Si tu ne sais pas quelle ligne correspond à l'Arduino, débranche la carte, relance la commande et note le port qui a disparu. Sous macOS, la même carte apparaît à la fois en /dev/tty… et en /dev/cu… ; dans Python, utilise celui qui commence par cu.

| Puce USB | Où la trouve-t-on ? | VID:PID | Nom sous Windows | Port sous Linux |
|---|---|---|---|---|
| ATmega16U2 | Uno R3 et Mega 2560 officiels | 2341:0043 (Uno) | Arduino Uno (COMx) | /dev/ttyACM0 |
| CH340 | Clones d'Uno, de Nano et de Mega | 1A86:7523 | USB-SERIAL CH340 (COMx) | /dev/ttyUSB0 |
| CP2102 | ESP32 DevKit, NodeMCU | 10C4:EA60 | Silicon Labs CP210x USB to UART Bridge (COMx) | /dev/ttyUSB0 |
| FT232R | Anciens Nano, câbles FTDI | 0403:6001 | USB Serial Port (COMx) | /dev/ttyUSB0 |
| RP2040 (USB intégré) | Raspberry Pi Pico sous MicroPython | 2E8A:0005 | Périphérique série USB (COMx) | /dev/ttyACM0 |
La fonction ci-dessous liste tous les ports et, si elle reconnaît une carte grâce à son VID, renvoie le nom de son port. Si tu enregistres le code sous port_bul.py, tu peux écrire from port_bul import port_bul et PORT = port_bul() dans tes autres scripts au lieu de modifier le nom du port à la main à chaque fois.
1import serial.tools.list_ports
2
3# Bilinen USB yongalarının üretici kimlikleri (VID) / Vendor IDs of common USB chips
4BILINEN = {
5 0x2341: "Arduino (orijinal / genuine)",
6 0x2A03: "Arduino (orijinal / genuine)",
7 0x1A86: "CH340 (klon / clone)",
8 0x10C4: "CP210x (ESP32, NodeMCU)",
9 0x0403: "FTDI",
10 0x2E8A: "Raspberry Pi Pico",
11}
12
13
14def port_bul():
15 portlar = sorted(serial.tools.list_ports.comports())
16 for p in portlar:
17 print(p.device, "|", p.description, "|", p.hwid) # tüm portları listele / list every port
18 for p in portlar:
19 if p.vid in BILINEN:
20 print("Bulundu / found:", p.device, "->", BILINEN[p.vid])
21 return p.device
22 return None
23
24
25if __name__ == "__main__":
26 if port_bul() is None:
27 print("Kart yok: kabloyu ve surucuyu kontrol et / no board: check cable and driver")Envoyer des données depuis l'Arduino : le premier programme
Les exemples utilisent un potentiomètre branché sur la broche A0 : la broche centrale va sur A0, l'une des broches extérieures sur 5V et l'autre sur GND. À la place du potentiomètre, tu peux aussi brancher une photorésistance (LDR) avec une résistance de 10 kΩ en pont diviseur de tension. Le programme fait une mesure toutes les 200 ms et envoie le résultat sur une seule ligne au format temps,valeur :
1// A0 değerini "millis,deger" CSV satırı olarak gönderir / Sends the A0 reading as a "millis,value" CSV line
2const int SENSOR_PIN = A0; // potansiyometrenin orta ucu / potentiometer wiper
3const unsigned long ARALIK = 200; // gönderim aralığı, ms / send interval, ms
4unsigned long sonGonderim = 0;
5
6void setup() {
7 Serial.begin(115200); // Python'daki hızla aynı olmalı / must match the baud rate in Python
8}
9
10void loop() {
11 if (millis() - sonGonderim >= ARALIK) { // delay() olmadan zamanlama / timing without delay()
12 sonGonderim = millis();
13 int deger = analogRead(SENSOR_PIN); // 0-1023
14 Serial.print(sonGonderim); // zaman damgası / timestamp
15 Serial.print(','); // ayırıcı / separator
16 Serial.println(deger); // sonuna \r\n ekler / appends \r\n
17 }
18}Utiliser millis() au lieu de delay() permet d'ajouter plus facilement, plus tard, la lecture des commandes Python dans la même boucle ; tous les détails sont dans millis et delay. Après le téléversement, ouvre le Moniteur série à 115200 bauds : tu verras défiler des lignes comme 2573,512. Ce n'est qu'une vérification : ferme toujours le Moniteur série avant de lancer le script Python, sinon le port sera occupé. Si tu découvres le Moniteur série, le guide du Moniteur série Arduino te sera utile.
Lire les données de l'Arduino avec Python et les enregistrer en CSV
1import csv
2import time
3from datetime import datetime
4
5import serial
6
7PORT = "COM3" # macOS: /dev/cu.usbmodem14101, Linux: /dev/ttyACM0
8BAUD = 115200 # Arduino'daki Serial.begin ile aynı / same as Serial.begin on the Arduino
9
10with serial.Serial(PORT, BAUD, timeout=1) as ser, \
11 open("olcumler.csv", "w", newline="", encoding="utf-8") as dosya:
12 time.sleep(2) # Uno port açılınca resetlenir / the Uno resets when the port opens
13 ser.reset_input_buffer() # yarım kalmış eski veriyi at / drop stale, partial data
14 yazici = csv.writer(dosya)
15 yazici.writerow(["tarih", "zaman_ms", "deger", "volt"]) # başlık satırı / header row
16 print("Kayit basladi, durdurmak icin Ctrl+C / logging, press Ctrl+C to stop")
17 try:
18 while True:
19 satir = ser.readline().decode("utf-8", errors="ignore").strip()
20 parcalar = satir.split(",")
21 if len(parcalar) != 2:
22 continue # boş ya da beklenmeyen satır / empty or unexpected line
23 try:
24 zaman_ms, deger = int(parcalar[0]), int(parcalar[1])
25 except ValueError:
26 continue # bozuk satır / corrupted line
27 volt = deger * 5.0 / 1023 # Uno ADC: 0-1023 -> 0-5 V
28 yazici.writerow([datetime.now().isoformat(timespec="seconds"),
29 zaman_ms, deger, round(volt, 3)])
30 dosya.flush() # satırı hemen diske yaz / write the row to disk now
31 print(zaman_ms, deger, f"{volt:.2f} V")
32 except KeyboardInterrupt:
33 print("Durduruldu / stopped")Ce script ouvre le port avec timeout=1 : readline() attend au plus 1 seconde l'arrivée d'une fin de ligne, renvoie un objet bytes vide si rien n'arrive, et la boucle ne reste jamais bloquée. Les données reçues sont de type bytes ; decode() les transforme en texte, strip() enlève le \r\n final et split(",") découpe la ligne en colonnes. Le time.sleep(2) et le reset_input_buffer() placés après l'ouverture du port servent à jeter les lignes incomplètes qui arrivent pendant le redémarrage de l'Uno ; tu verras pourquoi plus bas. Ouvrir le fichier avec newline="" évite les lignes vides sous Windows, et flush() garde sur le disque les mesures déjà enregistrées même si le programme s'arrête brutalement. Appuie sur Ctrl+C dans le terminal pour arrêter l'enregistrement.
Envoyer des commandes depuis Python vers l'Arduino
La communication fonctionne dans les deux sens. Le programme Arduino ci-dessous attend des commandes qui arrivent ligne par ligne : LED ON et LED OFF allument et éteignent la LED intégrée, PWM 128 règle la luminosité d'une LED branchée sur la broche 9 et READ renvoie la valeur lue sur A0. Chaque commande reçoit une courte réponse, ce qui permet à Python de savoir qu'elle a bien été exécutée. Tu peux trouver la bonne résistance pour la LED de la broche 9 avec le calculateur de résistance pour LED ; Qu'est-ce que le PWM ? explique le fonctionnement du PWM.
1// Python'dan gelen satır komutlarını işler / Handles line-based commands from Python
2const int LED_PIN = LED_BUILTIN; // Uno'da pin 13 / pin 13 on the Uno
3const int PWM_PIN = 9; // 220 ohm dirençli LED / LED with a 220 ohm resistor
4
5void setup() {
6 pinMode(LED_PIN, OUTPUT);
7 pinMode(PWM_PIN, OUTPUT);
8 Serial.begin(115200);
9 Serial.setTimeout(50); // readStringUntil en fazla 50 ms bekler / waits at most 50 ms
10 Serial.println("HAZIR"); // Python bu satırı bekler / Python waits for this line
11}
12
13void loop() {
14 if (Serial.available() > 0) {
15 String komut = Serial.readStringUntil('\n'); // satır sonuna kadar oku / read up to the newline
16 komut.trim(); // \r ve boşlukları sil / strip \r and spaces
17
18 if (komut == "LED ON") {
19 digitalWrite(LED_PIN, HIGH);
20 Serial.println("OK LED ON");
21 } else if (komut == "LED OFF") {
22 digitalWrite(LED_PIN, LOW);
23 Serial.println("OK LED OFF");
24 } else if (komut.startsWith("PWM ")) {
25 int deger = komut.substring(4).toInt(); // "PWM 128" -> 128
26 deger = constrain(deger, 0, 255); // 0-255 aralığında tut / keep it within 0-255
27 analogWrite(PWM_PIN, deger);
28 Serial.print("OK PWM ");
29 Serial.println(deger);
30 } else if (komut == "READ") {
31 Serial.println(analogRead(A0)); // A0 ölçümünü gönder / send the A0 reading
32 } else if (komut.length() > 0) {
33 Serial.print("ERR "); // bilinmeyen komut / unknown command
34 Serial.println(komut);
35 }
36 }
37}1import time
2
3import serial
4
5
6def komut_gonder(ser, komut):
7 ser.write((komut + "\n").encode("ascii")) # satır sonu şart / the newline is required
8 return ser.readline().decode("ascii", errors="ignore").strip() # cevabı bekle / wait for the reply
9
10
11with serial.Serial("COM3", 115200, timeout=2) as ser:
12 bitis = time.time() + 3 # en fazla 3 sn bekle / wait at most 3 s
13 while time.time() < bitis:
14 if ser.readline().strip() == b"HAZIR": # Arduino açıldı / the Arduino is up
15 break
16 print(komut_gonder(ser, "LED ON")) # OK LED ON
17 time.sleep(1)
18 print(komut_gonder(ser, "PWM 128")) # OK PWM 128
19 print("A0 =", komut_gonder(ser, "READ")) # A0 = 512
20 print(komut_gonder(ser, "LED OFF")) # OK LED OFFL'erreur la plus fréquente côté Python est d'oublier le \n à la fin de la commande : quand readStringUntil('\n') ne trouve pas la fin de ligne, l'Arduino attend la durée fixée par Serial.setTimeout() et ne traite la commande qu'ensuite. Dans ce programme, cette durée est de 50 ms ; avec la valeur par défaut de 1000 ms, chaque commande arriverait avec une seconde de retard. write() n'accepte que des octets : il faut convertir le texte avec encode() ou en le préfixant d'un b (b"LED ON\n"). Au départ, le script attend jusqu'à 3 secondes la ligne HAZIR (prêt) : comme l'Uno redémarre à l'ouverture du port, une commande envoyée immédiatement part vers le bootloader et se perd. Lire la réponse après chaque commande, c'est-à-dire travailler en requête et réponse, garde les deux côtés synchronisés et évite que les tampons se remplissent ; le tampon de réception de l'Uno ne fait que 64 octets.
Format des données et fin de ligne : que choisir ?
La liaison série ne te donne qu'un flux d'octets ; c'est à toi d'indiquer où une mesure se termine et où commence la suivante. Le moyen le plus fiable est d'écrire chaque mesure sur sa propre ligne et de terminer chaque ligne par \n. Le format à utiliser à l'intérieur de la ligne dépend de la taille de ton projet :
| Format | Exemple de ligne | Avantages | Inconvénients |
|---|---|---|---|
| Valeur unique | 512 | Le format le plus simple ; le Traceur série d'Arduino le dessine directement | Avec plusieurs valeurs, on ne sait plus laquelle est laquelle |
| CSV | 2573,512,23.5 | Court ; Excel et pandas le lisent directement | L'ordre des colonnes doit rester identique des deux côtés |
| Clé:valeur | temp:23.5,hum:41 | Lisible ; le Traceur série affiche les étiquettes | Plus long et un peu plus laborieux à analyser |
| JSON | {"t":23.5,"h":41} | Décodé en une ligne avec json.loads() en Python | Le format le plus long ; met à rude épreuve la RAM limitée de l'Uno |
| Binaire | 2 octets : 0x02 0x00 | L'option la plus rapide et la plus compacte | Illisible pour un humain ; nécessite le module struct et un octet de début |
Les fins de ligne sèment aussi souvent la confusion. Serial.println() ajoute deux caractères à la fin de chaque ligne : le retour chariot (\r, CR) et le saut de ligne (\n, LF). La méthode readline() de Python n'attend que \n, il reste donc un \r à la fin de la ligne ; strip() enlève les deux. La liste des fins de ligne du Moniteur série de l'IDE Arduino propose Pas de fin de ligne, Nouvelle ligne, Retour chariot et Les deux, NL et CR (en anglais No Line Ending, New Line, Carriage Return et Both NL & CR) ; quand tu testes le programme dans le Moniteur série avant Python, choisis Nouvelle ligne pour envoyer le même caractère que depuis Python. Pour les nombres décimaux, précise le nombre de chiffres, comme dans Serial.print(temperature, 1) ; sur l'Uno, sprintf ne prend pas en charge le format à virgule flottante (%f), donc écrire des décimales avec sprintf affiche un point d'interrogation au lieu du nombre.
| Débit (bauds) | Octets par seconde (8N1) | Lignes de 12 octets par seconde | Quand l'utiliser ? |
|---|---|---|---|
| 9600 | 960 | 80 | Capteurs lents, câbles longs, anciens exemples |
| 57600 | 5 760 | 480 | Mesures à vitesse moyenne |
| 115200 | 11 520 | 960 | Graphiques en direct, ESP32 et la plupart des nouveaux projets |
Sur la liaison série, chaque octet circule sous forme de 10 bits en comptant les bits de start et de stop : 115200 bauds correspondent donc à environ 11 520 octets par seconde. Pour une ligne de 12 octets composée de 25730,1023 et de la fin de ligne (\r\n), cela laisse de la place pour 960 lignes par seconde. Si le débit diffère entre les deux côtés, des caractères illisibles arrivent et Python lève une UnicodeDecodeError. Entre 5 et 20 mesures par seconde suffisent largement pour l'œil humain et pour la plupart des capteurs ; envoyer plus souvent que nécessaire ralentit le graphique et alourdit le fichier.
Tracer un graphique en direct avec matplotlib
1from collections import deque
2
3import matplotlib.pyplot as plt
4import serial
5from matplotlib.animation import FuncAnimation
6
7ser = serial.Serial("COM3", 115200, timeout=0.1)
8N = 200 # ekrandaki ölçüm sayısı / readings on screen
9veri = deque([0] * N, maxlen=N) # eskiler kendiliğinden düşer / old values drop out
10
11fig, ax = plt.subplots()
12cizgi, = ax.plot(range(N), list(veri))
13ax.set_ylim(0, 1023) # Uno ADC aralığı / Uno ADC range
14ax.set_xlabel("Ornek / sample")
15ax.set_ylabel("A0")
16
17
18def guncelle(_):
19 while ser.in_waiting: # biriken bütün satırları oku / read every queued line
20 parca = ser.readline().decode("utf-8", errors="ignore").strip().split(",")
21 if len(parca) == 2 and parca[1].isdigit():
22 veri.append(int(parca[1]))
23 cizgi.set_ydata(list(veri))
24 return (cizgi,)
25
26
27ani = FuncAnimation(fig, guncelle, interval=50, blit=True, cache_frame_data=False) # ani silinmemeli / keep ani
28plt.show()
29ser.close()Le graphique repose sur une deque qui conserve les 200 dernières mesures : chaque nouvelle valeur entre par la droite et la plus ancienne sort automatiquement par la gauche. FuncAnimation appelle la fonction guncelle (mettre à jour) toutes les 50 ms. La boucle while ser.in_waiting dans la fonction est importante : l'Arduino envoie pour l'instant 5 lignes par seconde, mais si tu augmentes la cadence et ne lis qu'une ligne par image, le graphique prend du retard sur le temps réel ; cette boucle lit toutes les lignes en attente et garde le graphique à jour. Ne supprime pas la variable ani : si plus aucune variable ne pointe vers l'objet d'animation, Python le détruit et la fenêtre se fige. Pour l'ESP32, écris set_ylim(0, 4095), et pour le Pico set_ylim(0, 65535). Pour un simple coup d'œil, le Traceur série de l'IDE Arduino fait l'affaire ; l'avantage de Python est de pouvoir enregistrer le graphique, combiner plusieurs sources et traiter les données en même temps.
Erreur de port occupé et problème du reset automatique
Un port série ne peut être ouvert que par un seul programme à la fois. Si, pendant que ton script Python tourne, le Moniteur série ou le Traceur série de l'IDE Arduino est ouvert, si un autre script Python occupe le port ou si un logiciel qui scrute les ports, comme ceux des imprimantes 3D, fonctionne en arrière-plan, tu obtiens could not open port 'COM3': PermissionError(13, 'Access is denied.', None, 5) sous Windows et Resource busy ou Device or resource busy sous macOS et Linux. L'inverse est vrai aussi : tant qu'un script Python garde le port ouvert, l'IDE Arduino ne peut pas téléverser de code. Utilise donc la structure with serial.Serial(...) as ser: dans tes scripts ; le port se ferme tout seul à la fin du bloc ou en cas d'erreur. Prends l'habitude d'arrêter le script avec Ctrl+C avant de téléverser.
Le second problème est plus sournois : sur l'Arduino Uno, le Nano et le Mega, la ligne DTR de la puce USB est reliée à la broche RESET du processeur par un condensateur de 100 nF. Ce circuit existe pour que l'IDE puisse redémarrer automatiquement la carte lors du téléversement, mais chaque fois que pyserial ouvre le port, DTR change aussi et la carte redémarre. Elle attend d'abord une ou deux secondes dans le bootloader, puis exécute ton programme depuis le début. Les commandes envoyées pendant ce temps sont perdues, et tu risques de manquer les premières lignes que l'Arduino affiche dans setup(). La solution consiste à attendre avec time.sleep(2) après l'ouverture du port ou, plus sûr, à attendre que l'Arduino envoie une ligne comme HAZIR ; l'exemple de commandes ci-dessus fait exactement cela. Ouvre le port une seule fois au début du programme et garde-le ouvert jusqu'à la fin : une boucle qui ouvre et ferme le port à chaque mesure réinitialise la carte sans arrêt.
Si tu veux empêcher complètement le reset, il y a deux méthodes. Par logiciel : crée le port fermé avec ser = serial.Serial(), renseigne ser.port et ser.baudrate, écris ser.dtr = False puis appelle ser.open(). Cela fonctionne en général sous Windows ; sous Linux et macOS, le système d'exploitation peut quand même basculer DTR à l'ouverture du port. Par matériel : place un condensateur de 10 µF entre les broches RESET et GND de l'Uno, la patte positive côté RESET. Les cartes à USB intégré comme le Leonardo, le Micro et le Raspberry Pi Pico ne redémarrent pas à l'ouverture du port ; sur ces cartes, les lignes que ton code affiche avant que Python se connecte peuvent être perdues, il vaut donc mieux envoyer les messages importants à la réception d'une commande.
Communication série entre l'ESP32 et Python
Tout le code Python de cet article fonctionne aussi avec l'ESP32 ; il suffit de modifier quelques points côté Arduino. Pour l'entrée analogique, utilise une des broches ADC1 (par exemple GPIO 34). L'ADC de l'ESP32 est sur 12 bits et convertit 0–3,3 V en 0–4095 : calcule donc la tension dans Python avec deger * 3.3 / 4095. Au démarrage, l'ESP32 affiche ses propres messages de boot à 115200 bauds, comme rst:0x1 (POWERON_RESET),boot:0x13 ; les scripts ci-dessus ignorent les lignes qui ne peuvent pas être converties en nombres, donc ces messages ne posent pas de problème. Selon le circuit de reset automatique de la carte et le système d'exploitation, l'ESP32 peut lui aussi redémarrer à l'ouverture du port : l'attente et la ligne HAZIR sont donc utiles ici aussi. La puce USB est le plus souvent un CP2102 ou un CH340. Si tu préfères te passer de câble, l'ESP32 peut envoyer les mêmes données en Wi-Fi ; consulte pour cela les guides ESP32 WebSocket et ESP32 Google Sheets. Si c'est ton premier ESP32, lis d'abord Qu'est-ce que l'ESP32 ?.
Raspberry Pi Pico et MicroPython : quelle différence ?

Une autre façon de réunir Python et le matériel est d'exécuter Python directement sur la carte. Le Raspberry Pi Pico se programme en MicroPython : lire des capteurs, piloter des LED et gérer le temps se fait dans le Pico avec la syntaxe de Python. Mais MicroPython est une version allégée du Python de l'ordinateur ; matplotlib, pandas ou OpenCV ne fonctionnent pas sur le Pico. Si tu veux enregistrer des données, tracer des graphiques ou les traiter avec de l'intelligence artificielle, tu relies donc aussi le Pico au Python de ton ordinateur. Le code pyserial de cet article fonctionne aussi avec le Pico ; il suffit d'adapter le nom du port, la plage de valeurs et la lecture des réponses aux commandes, comme expliqué plus bas.
| Caractéristique | Arduino + Python sur le PC | Pico + MicroPython (autonome) | Pico + Python sur le PC |
|---|---|---|---|
| Où Python s'exécute-t-il ? | Seulement sur l'ordinateur ; la carte exécute du C++ | Dans le Pico | Des deux côtés : MicroPython sur le Pico, Python sur l'ordinateur |
| Faut-il un ordinateur ? | Oui, les données sont traitées sur l'ordinateur | Non, fonctionne sur l'alimentation USB ou sur batterie | Oui |
| Bibliothèques disponibles | pyserial, matplotlib, pandas, numpy, OpenCV | Modules limités comme machine et time | Toutes les bibliothèques de l'ordinateur |
| Débit en bauds | Important, doit être identique des deux côtés | Pas de port série utilisé | Sans importance, les données circulent à la vitesse de l'USB |
| Reset à l'ouverture du port | Oui sur l'Uno, le Nano et le Mega | Sans objet | Non |
| Quand la choisir ? | Tu as déjà un Arduino et tu as besoin de bibliothèques toutes prêtes | Appareils simples et autonomes | Tu veux un seul langage (Python) des deux côtés |
Le code MicroPython ci-dessous est la version Pico de l'exemple Arduino précédent : il lit le potentiomètre sur GP26 toutes les 200 ms, envoie le résultat au même format temps,valeur et accepte les mêmes commandes LED. Ici, l'équivalent du Serial de l'Arduino est print() et sys.stdin ; grâce à select.poll(), la boucle de mesure continue de tourner pendant l'attente des commandes.
1# Pico'ya main.py adıyla kaydet / save it on the Pico as main.py
2import sys
3import select
4import time
5from machine import ADC, Pin
6
7led = Pin("LED", Pin.OUT) # Pico ve Pico W dahili LED / onboard LED on Pico and Pico W
8pot = ADC(26) # GP26 = ADC0, potansiyometre orta ucu / potentiometer wiper
9okuyucu = select.poll()
10okuyucu.register(sys.stdin, select.POLLIN) # USB'den veri gelince haber ver / notify when USB data arrives
11
12son = time.ticks_ms()
13while True:
14 if okuyucu.poll(0): # bekleyen komut var mı? / is a command waiting?
15 komut = sys.stdin.readline().strip()
16 if komut == "LED ON":
17 led.on()
18 print("OK LED ON")
19 elif komut == "LED OFF":
20 led.off()
21 print("OK LED OFF")
22 if time.ticks_diff(time.ticks_ms(), son) >= 200: # 200 ms'de bir ölçüm / one reading every 200 ms
23 son = time.ticks_ms()
24 print("{},{}".format(son, pot.read_u16())) # zaman,değer (0-65535) / time,value (0-65535)Enregistre le code sur le Pico sous le nom main.py avec Thonny, puis ferme Thonny et débranche puis rebranche le câble USB du Pico : Thonny utilise lui-même le port série du Pico, donc ton script Python ne peut pas l'ouvrir tant qu'il est connecté, et il arrête aussi le programme en cours quand il se connecte ; après le redémarrage, main.py se lance tout seul. Sur le port USB du Pico, le débit en bauds n'a pas d'importance ; quelle que soit la valeur indiquée dans Python, les données circulent à la vitesse de l'USB. read_u16() renvoie des valeurs de 0 à 65535 : calcule donc la tension avec deger * 3.3 / 65535. Comme le Pico envoie des lignes de mesure sans arrêt, readline() peut renvoyer une mesure au lieu de la réponse dans le script de commandes ci-dessus ; avec le Pico, continue à lire jusqu'à obtenir une ligne qui commence par OK. Cet exemple n'a ni ligne HAZIR ni commandes PWM et READ ; ajoute-les au code du Pico si tu en as besoin. Attention si tu envoies des données binaires depuis l'ordinateur : l'octet 0x03 correspond à Ctrl+C dans MicroPython et arrête ton programme ; tu peux désactiver ce comportement avec micropython.kbd_intr(-1). Si tu débutes avec le Pico, Qu'est-ce que le Raspberry Pi Pico ? et les bases de MicroPython sont un bon point de départ.
Idées de projets avec Arduino et Python
- Enregistreur de température et d'humidité : écris les mesures d'un DHT11 dans un CSV une fois par minute et trace le climat de ta pièce sur une semaine dans Excel ou avec pandas.
- Commande d'une lampe depuis l'ordinateur : pilote un module relais avec des commandes ON et OFF envoyées depuis Python, et associe-le à une minuterie ou à des commandes vocales.
- Graphique de distance en direct : trace avec matplotlib la distance mesurée par un HC-SR04. Si tu fais tourner le capteur avec un servo, la même méthode te permet de dessiner aussi un écran de radar.
- Écran d'état du PC : Python lit l'utilisation du processeur et de la mémoire avec la bibliothèque psutil et l'envoie à l'Arduino, qui l'affiche sur un écran OLED.
- Caméra qui suit les visages : OpenCV trouve la position d'un visage dans l'image, Python la convertit en angle et l'envoie, et l'Arduino oriente la caméra avec un servomoteur.
- Ton propre clavier de raccourcis : fais envoyer une ligne aux boutons de l'Arduino quand on appuie dessus, et laisse Python la lire pour ouvrir un programme ou lancer un raccourci sur l'ordinateur.
Erreurs fréquentes et solutions
| Erreur ou symptôme | Cause | Solution |
|---|---|---|
| ModuleNotFoundError: No module named 'serial' | pyserial n'est pas installé pour l'interpréteur Python qui exécute le script ; VS Code ou Thonny utilisent peut-être un autre Python | Lance python -m pip install pyserial avec le même interpréteur et choisis le bon interpréteur dans l'éditeur |
| AttributeError: module 'serial' has no attribute 'Serial' | Le mauvais paquet serial est installé ou ton fichier s'appelle serial.py | pip uninstall serial puis pip install pyserial ; renomme ton fichier |
| could not open port 'COM3': PermissionError(13, 'Access is denied.', None, 5) | Le port est ouvert dans un autre programme : Moniteur série, Traceur série ou un deuxième script | Ferme l'autre programme ; utilise with dans le script |
| could not open port 'COM5': FileNotFoundError(2, 'The system cannot find the file specified.', None, 2) | Nom de port erroné ou carte non branchée | Trouve le bon nom avec python -m serial.tools.list_ports -v |
| [Errno 13] Permission denied: '/dev/ttyACM0' | Ton utilisateur Linux n'a pas le droit d'accéder au port série | sudo usermod -a -G dialout $USER, puis déconnecte-toi et reconnecte-toi |
| Caractères illisibles ou UnicodeDecodeError | Débits différents ou parasites au démarrage | Utilise le même débit des deux côtés ; ajoute decode(errors="ignore") et reset_input_buffer() |
| readline() renvoie toujours b'' | L'Arduino n'envoie pas de fin de ligne (print au lieu de println), mauvais port ou carte encore en démarrage | Utilise println, vérifie le port et le débit, augmente le timeout |
| ValueError: invalid literal for int() with base 10 | Une ligne incomplète, vide ou inattendue a été convertie en nombre | Place la conversion dans un try/except et vérifie le nombre de colonnes |
| Les premières commandes sont ignorées | L'Uno a redémarré à l'ouverture du port et la commande est partie vers le bootloader | Attends la ligne HAZIR ou patiente 2 secondes après l'ouverture du port |
| Le graphique est en retard sur le temps réel | Une seule ligne est lue par image et les données s'accumulent dans le tampon | Lis toutes les lignes avec la boucle in_waiting et envoie moins souvent |
| Impossible de téléverser pendant que le script tourne | Python occupe le port | Arrête le script avec Ctrl+C, puis téléverse |
Faire dialoguer Arduino et Python par le port série est le moyen le plus rapide de transformer un simple projet à capteur en un système qui enregistre des données, trace des graphiques et se pilote depuis l'ordinateur. Commence par lire une seule valeur et l'afficher, enregistre-la ensuite en CSV, puis ajoute en dernier les commandes et les graphiques ; à chaque étape, vérifie que le débit et la fin de ligne sont identiques des deux côtés. Dans l'application BlueGrays, tu trouveras des projets pas à pas avec schémas et code pour mesurer la lumière avec une LDR, la température et l'humidité avec un DHT11, la distance avec un HC-SR04, ainsi que pour la communication série entre deux Arduino : de quoi monter rapidement la source des données que tu liras avec Python.
Questions fréquentes
Peut-on programmer un Arduino en Python ?
Python ne s'exécute pas sur un Arduino Uno, Nano ou Mega ; ces cartes se programment dans l'IDE Arduino avec le langage Arduino, basé sur le C++. Python tourne sur l'ordinateur et dialogue avec la carte par le port série grâce à pyserial ; tu peux aussi téléverser l'exemple StandardFirmata et piloter les broches directement depuis Python avec une bibliothèque comme pyFirmata2. Si tu veux que Python tourne sur la carte elle-même, choisis un Raspberry Pi Pico ou un ESP32 compatibles MicroPython.
Comment installer pyserial ?
Lance python -m pip install pyserial dans un terminal et écris import serial dans ton code. Le paquet s'appelle pyserial, mais le module importé s'appelle serial ; pip install serial installe un paquet complètement différent et provoque une AttributeError. Sous Linux, il faut aussi ajouter ton utilisateur au groupe dialout.
Comment savoir sur quel port COM est mon Arduino ?
Regarde dans Outils > Port de l'IDE Arduino ou lance python -m serial.tools.list_ports -v dans un terminal. Sous Windows, la rubrique Ports (COM et LPT) du Gestionnaire de périphériques affiche aussi le port. En cas de doute, débranche la carte et liste de nouveau les ports ; celui qui disparaît est celui de l'Arduino.
Comment envoyer des données de Python vers l'Arduino ?
Ouvre le port avec serial.Serial et envoie des octets, par exemple ser.write(b"LED ON\n") ; n'oublie pas de convertir le texte avec encode() et d'ajouter une fin de ligne. Côté Arduino, vérifie l'arrivée de données avec Serial.available() et lis la ligne avec Serial.readStringUntil('\n'). Comme l'Uno redémarre à l'ouverture du port, attends environ 2 secondes avant d'envoyer la première commande.
Peut-on utiliser le Moniteur série et Python en même temps ?
Non, un port série ne peut être ouvert que par un seul programme à la fois ; le second reçoit l'erreur Access is denied ou Resource busy. Tu peux en revanche traiter les données dans Python et les afficher à l'écran avec print en même temps. Si tu as besoin de deux canaux séparés, branche un deuxième convertisseur USB-TTL sur l'Arduino et crée un second port série avec SoftwareSerial.
Pourquoi mon Arduino redémarre-t-il quand Python ouvre le port ?
Sur l'Uno, le Nano et le Mega, la ligne DTR de la puce USB est reliée à la broche RESET par un condensateur, donc la carte redémarre à chaque ouverture du port ; l'IDE a besoin de ce circuit pour téléverser. Attends 2 secondes après l'ouverture du port ou attends une ligne de l'Arduino indiquant qu'il est prêt. Pour empêcher totalement le reset, tu peux placer un condensateur de 10 µF entre RESET et GND, mais il faudra le retirer avant chaque téléversement.
Projets à réaliser après cet article
Voir tous les projets
Mesurer l'intensité lumineuse avec une LDR
Envie de voir en chiffres à quel point ta chambre est lumineuse ? Le LDR, une petite pièce sensible à la lumière, mesure la lumière ambiante, et l'Arduino écrit la valeur sur l'écran de ton ordinateur

HC-SR04 Mesure de la distance (affichage en série)
Le capteur HC-SR04 fonctionne comme une chauve-souris qui trouve son chemin dans le noir : il envoie un son que tu ne peux pas entendre, écoute l'écho et mesure à quelle distance se trouve l'objet dev

Température et humidité avec DHT11
Quelle température fait-il dans ta chambre, et l'air est-il humide ou sec ? Le capteur DHT11 mesure la température et l'humidité, et l'Arduino affiche les valeurs sur un écran LCD à deux lignes, mises

Mesure de l'intensité lumineuse avec le module LDR ESP32
Suivi en temps réel sur le moniteur série de la valeur brute de l'ADC du module LDR et du pourcentage relatif de luminosité calculé à partir de références dans l'obscurité et à la lumière. Ce pourcentage n'est pas une mesure en lux. Permet de comprendre le principe de calibrage du module. Il est préférable de réaliser cet exercice avant le projet 10, car il sert à déterminer la valeur seuil.

Communication unidirectionnelle entre 2 Arduino
Tu fais discuter deux Arduino par un seul fil : appuie sur le bouton de l'un et la LED de l'autre s'allume, appuie encore et elle s'éteint. La carte qui envoie signale l'appui par un court message, et

Communication bidirectionnelle entre 2 Arduino
Deux Arduino reliés par des fils se parlent : appuie sur le bouton de l'un et la lumière de l'autre s'allume, appuie encore et elle s'éteint. Les deux cartes ont le même programme, donc chacune envoie

Contrôler une LED depuis un téléphone via Bluetooth
Une petite répétition pour allumer et éteindre les lumières de ta chambre depuis ton téléphone. Le module Bluetooth transmet à l'Arduino les lettres envoyées par ton téléphone ; une lettre allume la L

Enregistreur de Température ESP32 (DHT22 + DS1307 RTC + microSD + OLED)
Construis un enregistreur de température horodaté avec ESP32 ! Lis la température/humidité du DHT22 chaque minute, obtiens l'heure réelle du DS1307 RTC et enregistre sur carte microSD au format CSV. L'OLED affiche en direct la température (grande police), le pourcentage d'humidité, l'horodatage et le compteur. Le fichier /temp_log.csv sur la carte SD s'ouvre dans Excel, Google Sheets ou Python pandas pour analyse — parfait pour surveillance de serre, suivi de température à domicile, c
Autres articles
Application BlueGrays
Réalise ce montage pas à pas sur ton téléphone
Schéma, code et liste des composants sur un seul écran. Plus de 200 projets Arduino, ESP32 et Pico à réaliser.
Scanne avec ton téléphone
Pointe l’appareil photo vers le code : la boutique d’applications de ton téléphone s’ouvre.
ou ouvre la boutique

