Comunicazione seriale Arduino Python: pyserial, CSV e grafici
Comunicazione seriale Arduino Python con pyserial: installazione, trovare la porta, salvare i dati in CSV, inviare comandi e grafico dal vivo con matplotlib.
La comunicazione seriale Arduino Python consiste in un programma Python che gira sul tuo computer e scambia dati con un Arduino attraverso il cavo USB. Arduino misura un sensore e scrive il risultato sulla porta seriale; Python legge queste righe con la libreria pyserial, le salva in un file CSV, le mostra in un grafico oppure prende una decisione e rimanda un comando ad Arduino. Così unisci il controllo preciso dell'hardware di Arduino alla potenza di Python per l'elaborazione dei dati, i grafici, l'intelligenza artificiale e l'automazione. Non serve nemmeno un modulo aggiuntivo: basta il cavo USB con cui carichi gli sketch.
In questa guida imparerai passo dopo passo a installare pyserial, a trovare automaticamente la porta di Arduino, a salvare i dati di un sensore con il timestamp in un file CSV, a inviare comandi LED e PWM da Python ad Arduino e a disegnare un grafico in tempo reale con matplotlib. Poi vedremo come scegliere il formato dei dati e il fine riga, i due problemi che confondono di più (l'errore di porta occupata e il reset della scheda all'apertura della porta) e che cosa cambia facendo lo stesso lavoro in MicroPython su un Raspberry Pi Pico. Gli esempi sono scritti per Arduino Uno; le modifiche necessarie per ESP32 e Pico sono indicate a parte.

Come comunica Arduino con Python?
Il chip USB-seriale di Arduino Uno (un ATmega16U2 sulle schede originali, quasi sempre un CH340 sui cloni) compare sul computer come una porta seriale virtuale: COM3, COM4 e così via su Windows; /dev/cu.usbmodem… o /dev/cu.usbserial-… su macOS; /dev/ttyACM0 o /dev/ttyUSB0 su Linux. Ogni carattere scritto da Arduino con Serial.print() arriva al computer attraverso questa porta sotto forma di byte; dal lato Python, pyserial apre la porta come un file, ci legge e ci scrive. Una distinzione importante: Arduino Uno non esegue codice Python. Sulla scheda gira codice Arduino scritto in C++, sul computer gira Python, e i due si capiscono solo tramite la linea seriale. Perché entrambi i lati parlino la stessa lingua, queste impostazioni devono coincidere:
| Impostazione | Lato Arduino | Lato Python | Attenzione |
|---|---|---|---|
| Porta | Creata automaticamente tramite USB | serial.Serial("COM3", ...) | Il nome della porta dipende dal sistema operativo |
| Velocità (baud) | Serial.begin(115200) | serial.Serial(port, 115200) | Se sono diverse arrivano caratteri senza senso |
| Formato del frame | 8 bit di dati, nessuna parità, 1 bit di stop (8N1) | Anche il valore predefinito di pyserial è 8N1 | Di solito non serve cambiarlo |
| Fine riga | Serial.println() aggiunge \r\n alla fine | readline() legge finché non trova \n | Non dimenticare di aggiungere \n quando invii da Python |
| Timeout | Serial.setTimeout(), predefinito 1000 ms | timeout=1 (secondi) | Con timeout=None, readline() aspetta all'infinito |
Installare pyserial (Windows, macOS, Linux)
- Serve Python 3 installato. Su Windows, spunta la casella Add python.exe to PATH nella prima schermata del programma di installazione scaricato dal sito ufficiale di Python; su macOS e Linux Python 3 di solito è già presente.
- Apri un terminale (Prompt dei comandi o PowerShell su Windows) e digita python -m pip install pyserial. Se Windows non riconosce il comando python, prova py -m pip install pyserial; su macOS e Linux il comando può essere python3 -m pip install pyserial.
- Verifica l'installazione: python -c "import serial; print(serial.__version__)" dovrebbe stampare un numero di versione come 3.5.
- Per la sezione sul grafico in tempo reale serve anche matplotlib: python -m pip install matplotlib.
- Su Linux aggiungi il tuo utente al gruppo dialout per poter aprire la porta: sudo usermod -a -G dialout $USER, poi esci dalla sessione e rientra. Se su Raspberry Pi OS o sulle versioni recenti di Ubuntu pip mostra l'errore externally-managed-environment, usa sudo apt install python3-serial oppure crea un ambiente virtuale con python3 -m venv ed esegui pip al suo interno.
Su quale porta è Arduino? Trovare la porta con Python
Il modo più semplice è guardare il menu Strumenti > Porta dell'IDE di Arduino, ma anche Python può elencare le porte. Digita python -m serial.tools.list_ports -v in un terminale e vedrai nome, descrizione e identificativo hardware (VID:PID) di ogni porta. Se non capisci quale riga è Arduino, scollega la scheda, esegui di nuovo il comando e annota la porta che è sparita. Su macOS la stessa scheda compare sia come /dev/tty… sia come /dev/cu…; in Python usa quella che inizia con cu.

| Chip USB | Dove si trova? | VID:PID | Nome in Windows | Porta su Linux |
|---|---|---|---|---|
| ATmega16U2 | Uno R3 e Mega 2560 originali | 2341:0043 (Uno) | Arduino Uno (COMx) | /dev/ttyACM0 |
| CH340 | Cloni di Uno, Nano e 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 | Vecchi Nano, cavi FTDI | 0403:6001 | USB Serial Port (COMx) | /dev/ttyUSB0 |
| RP2040 (USB integrato) | Raspberry Pi Pico con MicroPython | 2E8A:0005 | Dispositivo seriale USB (COMx) | /dev/ttyACM0 |
La funzione qui sotto elenca tutte le porte e, se riconosce una scheda dal suo VID, restituisce il nome della sua porta. Se salvi il codice come port_bul.py, negli altri script puoi scrivere from port_bul import port_bul e PORT = port_bul() invece di cambiare a mano il nome della porta ogni volta.
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")Inviare dati da Arduino: il primo sketch
Gli esempi usano un potenziometro collegato al pin A0: il piedino centrale va ad A0, uno dei piedini laterali a 5V e l'altro a GND. Al posto del potenziometro puoi collegare anche una fotoresistenza (LDR) con una resistenza da 10 kΩ come partitore di tensione. Lo sketch esegue una lettura ogni 200 ms e invia il risultato su una sola riga nel formato tempo,valore:
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}Usare millis() al posto di delay() rende più semplice aggiungere in seguito, nello stesso loop, la lettura dei comandi in arrivo da Python; trovi i dettagli in millis e delay. Dopo il caricamento, apri il Monitor seriale a 115200 baud e vedrai scorrere righe come 2573,512. È solo una verifica: chiudi sempre il Monitor seriale prima di avviare lo script Python, altrimenti la porta risulterà occupata. Se usi il Monitor seriale per la prima volta, ti sarà utile la guida al Monitor seriale di Arduino.
Leggere i dati di Arduino con Python e salvarli in 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")Questo script apre la porta con timeout=1: readline() aspetta al massimo 1 secondo l'arrivo di un fine riga, restituisce un oggetto bytes vuoto se non arriva nulla e il ciclo non si blocca mai. I dati ricevuti sono di tipo bytes; decode() li trasforma in testo, strip() elimina il \r\n finale e split(",") divide la riga in colonne. time.sleep(2) e reset_input_buffer() dopo l'apertura della porta servono a scartare le righe a metà che arrivano mentre la Uno si riavvia; più sotto vedrai perché. Aprire il file con newline="" evita righe vuote su Windows, e flush() mantiene sul disco le letture salvate fino a quel momento anche se il programma si chiude all'improvviso. Premi Ctrl+C nel terminale per fermare la registrazione.
Inviare comandi da Python ad Arduino
La comunicazione funziona in entrambe le direzioni. Lo sketch Arduino qui sotto aspetta comandi che arrivano riga per riga: LED ON e LED OFF accendono e spengono il LED integrato, PWM 128 regola la luminosità di un LED collegato al pin 9 e READ rimanda il valore letto su A0. Ogni comando riceve una breve risposta, così Python sa che è stato davvero eseguito. Puoi trovare la resistenza giusta per il LED sul pin 9 con il calcolatore di resistenza per LED; Cos'è il PWM? spiega come funziona il 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'errore più comune dal lato Python è dimenticare il \n alla fine del comando: se readStringUntil('\n') non trova il fine riga, Arduino aspetta il tempo impostato con Serial.setTimeout() e solo dopo elabora il comando. In questo sketch il tempo è di 50 ms; con il valore predefinito di 1000 ms ogni comando arriverebbe con un secondo di ritardo. write() accetta solo byte, quindi devi convertire il testo con encode() oppure anteponendo una b (b"LED ON\n"). All'inizio lo script aspetta fino a 3 secondi la riga HAZIR (pronto): poiché la Uno si riavvia all'apertura della porta, un comando inviato subito finisce nel bootloader e va perso. Leggere la risposta dopo ogni comando, cioè lo schema richiesta e risposta, mantiene sincronizzati i due lati ed evita che i buffer si riempiano; il buffer di ricezione della Uno è di soli 64 byte.
Formato dei dati e fine riga: quale scegliere?
La linea seriale ti fornisce solo un flusso di byte; sta a te stabilire dove finisce una lettura e dove inizia la successiva. Il modo più affidabile è scrivere ogni lettura su una riga a sé e chiudere ogni riga con \n. Il formato da usare all'interno della riga dipende dalle dimensioni del progetto:
| Formato | Riga di esempio | Vantaggi | Svantaggi |
|---|---|---|---|
| Valore singolo | 512 | Il formato più semplice; il Plotter seriale di Arduino lo disegna direttamente | Con più valori non si capisce quale sia quale |
| CSV | 2573,512,23.5 | Corto; Excel e pandas lo leggono direttamente | L'ordine delle colonne deve restare uguale sui due lati |
| Chiave:valore | temp:23.5,hum:41 | Leggibile; il Plotter seriale mostra le etichette | Più lungo e un po' più laborioso da analizzare |
| JSON | {"t":23.5,"h":41} | Si decodifica in una riga con json.loads() in Python | Il formato più lungo; mette alla prova la poca RAM della Uno |
| Binario | 2 byte: 0x02 0x00 | L'opzione più veloce e compatta | Non leggibile da una persona; richiede il modulo struct e un byte di inizio |
Anche i fine riga creano spesso confusione. Serial.println() aggiunge due caratteri alla fine di ogni riga: ritorno a capo (\r, CR) e avanzamento di riga (\n, LF). Il metodo readline() di Python aspetta solo \n, quindi alla fine della riga resta un \r; strip() li elimina entrambi. L'elenco dei fine riga nel Monitor seriale dell'IDE di Arduino offre Nessun fine riga, A capo, Ritorno carrello (CR) ed Entrambi NL & CR (in inglese No Line Ending, New Line, Carriage Return e Both NL & CR); quando provi lo sketch nel Monitor seriale prima di passare a Python, scegli A capo così viene inviato lo stesso carattere che invia Python. Per i numeri decimali indica le cifre dopo la virgola, come in Serial.print(temperatura, 1); sulla Uno sprintf non supporta il formato in virgola mobile (%f), quindi provare a scrivere decimali con sprintf produce un punto interrogativo al posto del numero.
| Velocità (baud) | Byte al secondo (8N1) | Righe da 12 byte al secondo | Quando usarla? |
|---|---|---|---|
| 9600 | 960 | 80 | Sensori lenti, cavi lunghi, vecchi esempi |
| 57600 | 5.760 | 480 | Misure a velocità media |
| 115200 | 11.520 | 960 | Grafici in tempo reale, ESP32 e la maggior parte dei progetti nuovi |
Sulla linea seriale ogni byte viaggia come 10 bit contando i bit di start e di stop, quindi 115200 baud corrispondono a circa 11.520 byte al secondo. Per una riga di 12 byte formata da 25730,1023 più il fine riga (\r\n), c'è spazio per un massimo di 960 righe al secondo. Se la velocità non coincide sui due lati, arrivano caratteri senza senso e Python solleva un UnicodeDecodeError. Da 5 a 20 letture al secondo sono più che sufficienti per l'occhio umano e per la maggior parte dei sensori; inviare più spesso del necessario rallenta il grafico e appesantisce il file.
Grafico in tempo reale con 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()Il grafico si basa su una deque che conserva le ultime 200 letture: ogni nuovo valore entra da destra e il più vecchio esce automaticamente da sinistra. FuncAnimation chiama la funzione guncelle (aggiorna) ogni 50 ms. Il ciclo while ser.in_waiting nella funzione è importante: per ora Arduino invia 5 righe al secondo, ma se aumenti la frequenza e leggi una sola riga per fotogramma, il grafico resta indietro rispetto al tempo reale; questo ciclo legge tutte le righe accumulate e mantiene il grafico aggiornato. Non eliminare la variabile ani: se nessuna variabile punta all'oggetto dell'animazione, Python lo distrugge e la finestra resta bloccata. Per ESP32 scrivi set_ylim(0, 4095), per Pico set_ylim(0, 65535). Se vuoi solo dare un'occhiata veloce, va bene anche il Plotter seriale dell'IDE di Arduino; il vantaggio di Python è che puoi salvare il grafico, combinare più sorgenti ed elaborare i dati nello stesso momento.
Errore di porta occupata e problema del reset automatico
Una porta seriale può essere aperta da un solo programma alla volta. Se mentre lo script Python è in esecuzione è aperto il Monitor seriale o il Plotter seriale dell'IDE di Arduino, se un altro script Python tiene occupata la porta o se in background gira un software che scansiona le porte, come i programmi per stampanti 3D, ottieni could not open port 'COM3': PermissionError(13, 'Access is denied.', None, 5) su Windows e Resource busy o Device or resource busy su macOS e Linux. Vale anche il contrario: finché uno script Python tiene aperta la porta, l'IDE di Arduino non riesce a caricare il codice. Per questo conviene usare nei tuoi script la struttura with serial.Serial(...) as ser:; la porta si chiude da sola quando il blocco termina o si verifica un errore. Prendi l'abitudine di fermare lo script con Ctrl+C prima di caricare.
Il secondo problema è più subdolo: su Arduino Uno, Nano e Mega la linea DTR del chip USB è collegata al pin RESET del processore tramite un condensatore da 100 nF. Questo circuito esiste perché l'IDE possa riavviare automaticamente la scheda durante il caricamento, ma ogni volta che pyserial apre la porta anche DTR cambia e la scheda si riavvia. Prima resta uno o due secondi nel bootloader, poi esegue il tuo sketch dall'inizio. I comandi inviati in questo intervallo vanno persi e potresti non ricevere le prime righe che Arduino stampa in setup(). La soluzione è aspettare con time.sleep(2) dopo aver aperto la porta o, in modo più affidabile, attendere che Arduino invii una riga come HAZIR; l'esempio dei comandi qui sopra fa proprio questo. Apri la porta una sola volta all'inizio del programma e tienila aperta fino alla fine: un ciclo che apre e chiude la porta a ogni lettura riavvia la scheda di continuo.
Se vuoi evitare del tutto il reset, hai due strade. Via software: crea la porta chiusa con ser = serial.Serial(), imposta ser.port e ser.baudrate, scrivi ser.dtr = False e poi chiama ser.open(). Su Windows di solito funziona; su Linux e macOS il sistema operativo può comunque cambiare DTR all'apertura della porta. Via hardware: metti un condensatore da 10 µF tra i pin RESET e GND della Uno, con il piedino positivo su RESET. Le schede con USB integrato come Leonardo, Micro e Raspberry Pi Pico non si riavviano all'apertura della porta; su queste schede le righe stampate dal tuo codice prima che Python si colleghi possono andare perse, quindi è più sicuro inviare i messaggi importanti quando arriva un comando.
Comunicazione seriale tra ESP32 e Python
Tutto il codice Python di questo articolo funziona anche con ESP32; devi solo cambiare alcune cose dal lato Arduino. Per l'ingresso analogico usa uno dei pin ADC1 (ad esempio GPIO 34). L'ADC di ESP32 è a 12 bit e converte 0–3,3 V in 0–4095, quindi in Python calcola la tensione con deger * 3.3 / 4095. All'avvio ESP32 stampa i propri messaggi di boot a 115200 baud, come rst:0x1 (POWERON_RESET),boot:0x13; gli script qui sopra ignorano le righe che non si possono convertire in numeri, quindi questi messaggi non creano problemi. A seconda del circuito di reset automatico della scheda e del sistema operativo, anche ESP32 può riavviarsi all'apertura della porta, quindi l'attesa e la riga HAZIR servono anche qui. Il chip USB di solito è un CP2102 o un CH340. Se preferisci fare a meno del cavo, ESP32 può inviare gli stessi dati via Wi-Fi; leggi le guide ESP32 WebSocket e ESP32 Google Sheets. Se è il tuo primo ESP32, leggi prima Cos'è l'ESP32?.
Raspberry Pi Pico e MicroPython: qual è la differenza?

Un altro modo per unire Python e hardware è eseguire Python direttamente sulla scheda. Il Raspberry Pi Pico si può programmare in MicroPython: leggere sensori, accendere LED e gestire i tempi avviene dentro il Pico con la sintassi di Python. MicroPython però è una versione ridotta del Python per computer; matplotlib, pandas o OpenCV non funzionano sul Pico. Perciò, se vuoi salvare i dati, disegnare grafici o elaborarli con l'intelligenza artificiale, colleghi anche il Pico al Python del computer. Il codice pyserial di questo articolo funziona anche con il Pico; devi solo adattare il nome della porta, l'intervallo dei valori e la lettura delle risposte ai comandi, come spiegato qui sotto.
| Caratteristica | Arduino + Python sul PC | Pico + MicroPython (autonomo) | Pico + Python sul PC |
|---|---|---|---|
| Dove gira Python? | Solo sul computer; la scheda esegue C++ | Dentro il Pico | Su entrambi i lati: MicroPython sul Pico, Python sul computer |
| Serve un computer? | Sì, i dati vengono elaborati sul computer | No, funziona con l'alimentazione USB o a batteria | Sì |
| Librerie disponibili | pyserial, matplotlib, pandas, numpy, OpenCV | Moduli limitati come machine e time | Tutte le librerie del computer |
| Velocità in baud | Importante, deve coincidere sui due lati | Nessuna porta seriale usata | Irrilevante, i dati viaggiano alla velocità dell'USB |
| Reset all'apertura della porta | Sì su Uno, Nano e Mega | Non applicabile | No |
| Quando sceglierlo? | Hai già un Arduino e ti servono librerie pronte | Dispositivi semplici e indipendenti | Vuoi un solo linguaggio (Python) su entrambi i lati |
Il codice MicroPython qui sotto è la versione per Pico dell'esempio Arduino precedente: legge il potenziometro su GP26 ogni 200 ms, invia il risultato nello stesso formato tempo,valore e accetta gli stessi comandi LED. Qui l'equivalente del Serial di Arduino sono print() e sys.stdin; grazie a select.poll() il ciclo di misura non si ferma mentre aspetta i comandi.
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)Salva il codice sul Pico come main.py con Thonny, poi chiudi Thonny e scollega e ricollega il cavo USB del Pico: Thonny usa la porta seriale del Pico, quindi finché è collegato il tuo script Python non può aprirla, e quando si collega ferma anche il programma in esecuzione; dopo il riavvio main.py parte da solo. Sulla porta USB del Pico la velocità in baud non conta; qualunque valore metti in Python, i dati viaggiano alla velocità dell'USB. read_u16() restituisce valori da 0 a 65535, quindi calcola la tensione con deger * 3.3 / 65535. Poiché il Pico invia righe di misura senza sosta, nello script dei comandi qui sopra readline() può restituire una misura invece della risposta; con il Pico continua a leggere finché non arriva una riga che inizia con OK. Questo esempio non ha la riga HAZIR né i comandi PWM e READ; aggiungili al codice del Pico se ti servono. Fai attenzione se invii dati binari dal computer: il byte 0x03 in MicroPython equivale a Ctrl+C e ferma il programma; puoi disattivarlo con micropython.kbd_intr(-1). Se sei alle prime armi con il Pico, Cos'è il Raspberry Pi Pico? e le basi di MicroPython sono un buon punto di partenza.
Idee di progetto con Arduino e Python
- Registratore di temperatura e umidità: scrivi in un CSV le letture di un DHT11 una volta al minuto e rappresenta il clima della tua stanza per una settimana in Excel o con pandas.
- Controllo di una lampada dal computer: comanda un modulo relè con i comandi ON e OFF inviati da Python e abbinalo a un timer o ai comandi vocali.
- Grafico della distanza in tempo reale: disegna con matplotlib la distanza misurata da un HC-SR04. Se fai ruotare il sensore con un servo, con lo stesso metodo puoi disegnare anche uno schermo radar.
- Display di stato del PC: Python legge l'uso di CPU e memoria con la libreria psutil e lo invia ad Arduino, che lo mostra su un display OLED.
- Telecamera che segue i volti: OpenCV trova la posizione di un volto nell'immagine, Python la converte in un angolo e la invia, e Arduino orienta la telecamera con un servomotore.
- La tua tastiera di scorciatoie: fai in modo che i pulsanti collegati ad Arduino inviino una riga quando li premi, e che Python la legga per aprire un programma o lanciare una scorciatoia sul computer.
Errori comuni e soluzioni
| Errore o sintomo | Causa | Soluzione |
|---|---|---|
| ModuleNotFoundError: No module named 'serial' | pyserial non è installato per l'interprete Python che esegue lo script; VS Code o Thonny potrebbero usare un altro Python | Esegui python -m pip install pyserial con lo stesso interprete e scegli l'interprete giusto nell'editor |
| AttributeError: module 'serial' has no attribute 'Serial' | È installato il pacchetto serial sbagliato o il tuo file si chiama serial.py | pip uninstall serial e pip install pyserial; rinomina il file |
| could not open port 'COM3': PermissionError(13, 'Access is denied.', None, 5) | La porta è aperta in un altro programma: Monitor seriale, Plotter seriale o un secondo script | Chiudi l'altro programma; usa with nello script |
| could not open port 'COM5': FileNotFoundError(2, 'The system cannot find the file specified.', None, 2) | Nome della porta sbagliato o scheda non collegata | Trova il nome giusto con python -m serial.tools.list_ports -v |
| [Errno 13] Permission denied: '/dev/ttyACM0' | Il tuo utente Linux non ha i permessi per accedere alla porta seriale | sudo usermod -a -G dialout $USER, poi esci dalla sessione e rientra |
| Caratteri senza senso o UnicodeDecodeError | Velocità diverse o disturbi durante l'avvio | Usa la stessa velocità sui due lati; aggiungi decode(errors="ignore") e reset_input_buffer() |
| readline() restituisce sempre b'' | Arduino non invia il fine riga (print invece di println), porta sbagliata o scheda ancora in avvio | Usa println, controlla porta e velocità, aumenta il timeout |
| ValueError: invalid literal for int() with base 10 | Si è provato a convertire in numero una riga incompleta, vuota o inattesa | Metti la conversione in un try/except e controlla il numero di colonne |
| I primi comandi vengono ignorati | La Uno si è riavviata all'apertura della porta e il comando è finito nel bootloader | Aspetta la riga HAZIR o attendi 2 secondi dopo aver aperto la porta |
| Il grafico resta indietro rispetto al tempo reale | Si legge una sola riga per fotogramma e i dati si accumulano nel buffer | Leggi tutte le righe con il ciclo su in_waiting e invia meno spesso |
| Non riesci a caricare il codice con lo script avviato | Python tiene occupata la porta | Ferma lo script con Ctrl+C, poi carica |
Far dialogare Arduino e Python attraverso la porta seriale è la strada più breve per trasformare un semplice progetto con sensori in un sistema che registra dati, disegna grafici e si controlla dal computer. Prima leggi un solo valore e stampalo, poi salvalo in CSV e solo alla fine aggiungi comandi e grafici; a ogni passo assicurati che velocità e fine riga coincidano sui due lati. Nell'app BlueGrays trovi progetti passo passo con schemi e codice per misurare la luce con una LDR, la temperatura e l'umidità con un DHT11, la distanza con un HC-SR04 e per la comunicazione seriale tra due Arduino, così puoi costruire in fretta la sorgente dei dati che leggerai con Python.
Domande frequenti
Si può programmare Arduino in Python?
Su Arduino Uno, Nano o Mega Python non gira; queste schede si programmano nell'IDE di Arduino con il linguaggio Arduino, basato sul C++. Python gira sul computer e comunica con la scheda attraverso la porta seriale grazie a pyserial; in alternativa puoi caricare l'esempio StandardFirmata e controllare i pin direttamente da Python con una libreria come pyFirmata2. Se vuoi che Python giri sulla scheda stessa, scegli un Raspberry Pi Pico o un ESP32 con MicroPython.
Come si installa pyserial?
Esegui python -m pip install pyserial in un terminale e scrivi import serial nel codice. Il pacchetto si chiama pyserial, ma il modulo che importi si chiama serial; pip install serial installa un pacchetto completamente diverso e causa un AttributeError. Su Linux devi anche aggiungere il tuo utente al gruppo dialout.
Come trovo la porta COM a cui è collegato Arduino?
Guarda in Strumenti > Porta nell'IDE di Arduino oppure esegui python -m serial.tools.list_ports -v in un terminale. Su Windows anche la voce Porte (COM e LPT) di Gestione dispositivi mostra la porta. Se hai dubbi, scollega la scheda ed elenca di nuovo le porte; quella che sparisce è di Arduino.
Come invio dati da Python ad Arduino?
Apri la porta con serial.Serial e invia byte, per esempio ser.write(b"LED ON\n"); non dimenticare di convertire il testo con encode() e di aggiungere un fine riga. Su Arduino controlla l'arrivo dei dati con Serial.available() e leggi la riga con Serial.readStringUntil('\n'). Dato che la Uno si riavvia all'apertura della porta, aspetta circa 2 secondi prima di inviare il primo comando.
Posso usare il Monitor seriale di Arduino e Python insieme?
No, una porta seriale può essere aperta da un solo programma alla volta; il secondo riceve l'errore Access is denied o Resource busy. Puoi però elaborare i dati in Python e stamparli a schermo con print nello stesso momento. Se ti servono due canali separati, collega un secondo convertitore USB-TTL ad Arduino e crea una seconda porta seriale con SoftwareSerial.
Perché Arduino si riavvia quando Python apre la porta?
Su Uno, Nano e Mega la linea DTR del chip USB è collegata al pin RESET tramite un condensatore, quindi la scheda si riavvia ogni volta che la porta viene aperta; all'IDE serve questo circuito per caricare il codice. Aspetta 2 secondi dopo l'apertura della porta oppure attendi una riga con cui Arduino segnala di essere pronto. Per evitare del tutto il reset puoi mettere un condensatore da 10 µF tra RESET e GND, ma dovrai toglierlo prima di ogni caricamento.
Progetti da realizzare dopo questo articolo
Vedi tutti i progetti
Misurare l'intensità luminosa con LDR
Vuoi vedere quanto è luminosa la tua stanza sotto forma di numero? L'LDR, un piccolo componente sensibile alla luce, misura la luce ambiente e l'Arduino scrive il valore sullo schermo del computer ogn

HC-SR04 Misura della distanza (display seriale)
Il sensore HC-SR04 funziona come un pipistrello che trova la strada al buio: manda un suono che non puoi sentire, ne ascolta l'eco e misura quanto è lontano l'oggetto davanti a sé. L'Arduino scrive qu

Temperatura e umidità con DHT11
Quanti gradi ci sono nella tua stanza, e l'aria è umida o secca? Il sensore DHT11 misura temperatura e umidità, e l'Arduino scrive i valori su uno schermo LCD a due righe, aggiornandoli ogni paio di s

Lettura dell'intensità luminosa con il modulo LDR ESP32
Monitoraggio in tempo reale sul Serial Monitor del valore grezzo dell'ADC del modulo LDR e della percentuale relativa di luce calcolata dai valori di riferimento al buio e alla luce. Questa percentuale non è una misurazione in lux. Spiega il principio di calibrazione del modulo. È consigliabile eseguirlo prima del Progetto 10, poiché serve a determinare il valore di soglia.

Comunicazione unidirezionale tra 2 Arduino
Fai parlare due Arduino con un solo filo: premi il pulsante sull'uno e il LED dell'altro si accende, premi di nuovo e si spegne. La scheda che invia segnala la pressione con un breve messaggio, e quel

Comunicazione bidirezionale tra 2 Arduino
Due Arduino collegati con dei fili parlano tra loro: premi il pulsante di uno e si accende la luce dell'altro, premilo di nuovo e si spegne. Entrambe le schede hanno lo stesso programma, quindi ognuna

Controllare un LED dal telefono tramite Bluetooth
Una piccola prova per accendere e spegnere le luci della tua stanza dal telefono. Il modulo Bluetooth passa all'Arduino le lettere che invii dal telefono; una lettera accende il LED rosso, un'altra qu

Data Logger di Temperatura ESP32 (DHT22 + DS1307 RTC + microSD + OLED)
Costruisci un data logger di temperatura con timestamp usando ESP32! Leggi temperatura/umidità dal DHT22 ogni minuto, ottieni l'ora reale dal DS1307 RTC e registra su scheda microSD in formato CSV. L'OLED mostra dal vivo temperatura (font grande), percentuale di umidità, timestamp e contatore di log. Il file /temp_log.csv sulla scheda SD si apre in Excel, Google Sheets o Python pandas per l'analisi — perfetto per il monitoraggio di serre, tracciamento della temperatura domestica, raccolta
Altri articoli
App BlueGrays
Costruisci questo circuito passo passo sul telefono
Schema, codice ed elenco componenti in un’unica schermata. Oltre 200 progetti Arduino, ESP32 e Pico da realizzare.
Inquadra con il telefono
Punta la fotocamera sul codice: si apre lo store del tuo telefono.
oppure apri lo store

