Basi · 12 min di lettura

Cos'è la UART? Collegamento TX/RX, baud e seriale con Arduino

Cos'è la UART e come funziona: TX/RX incrociati, baud, bit di start e stop, due Arduino, SoftwareSerial, livelli 5V e 3,3V, ESP32 Serial2 e UART del Pico.

La risposta breve a cos'è la UART: la UART (Universal Asynchronous Receiver-Transmitter) è l'hardware di comunicazione seriale che permette a due dispositivi di scambiarsi dati byte per byte con soli due fili di dati (TX e RX) più una massa (GND) comune. Ogni riga che vedi nel Monitor seriale con l'Arduino collegato al computer arriva tramite UART. Decine di moduli, come ricevitori GPS, il modulo Bluetooth HC-05, il Wi-Fi ESP-01, il GSM SIM800L, i lettori di impronte e i lettori MP3, parlano con Arduino via UART.

In questo articolo spieghiamo passo passo perché la UART non ha bisogno di una linea di clock, cosa sono i baud, come un byte viene incorniciato da bit di start, dati e stop, perché TX e RX si incrociano, come far comunicare due Arduino prima in un verso e poi in entrambi, i limiti di SoftwareSerial, come collegare in sicurezza schede a 5 V e 3,3 V, le tre UART dell'ESP32 e i pin UART del Raspberry Pi Pico. Nell'ultima sezione vedrai come creare pacchetti con caratteri di inizio e fine per non mescolare i dati, e gli errori più comuni.

Convertitore USB-TTL CP2102
Convertitore USB-TTL CP2102: trasforma la porta USB del computer in una UART con pin TX, RX e GND; il chip USB dell'Arduino Uno fa lo stesso lavoro.

Cos'è la UART e come funziona?

La parola «asincrona» nel nome è il punto chiave: in I2C e SPI insieme ai dati viaggia un segnale di clock, mentre la UART non ha linea di clock. Al suo posto i due lati si accordano prima su una velocità, chiamata baud rate. A riposo la linea resta HIGH (1). Per inviare un byte il trasmettitore porta la linea a LOW per la durata di un bit: è il bit di start, che dice al ricevitore «inizia a contare». Seguono gli 8 bit di dati, a partire dal meno significativo (LSB), un eventuale bit di parità e infine la linea resta HIGH per almeno un bit: è il bit di stop. L'impostazione predefinita di Arduino è 8N1: 8 bit di dati, nessuna parità, 1 bit di stop.

BitStartD0D1D2D3D4D5D6D7Stop
Lettera 'A' (0x41 = 0100 0001)0100000101
Livello della lineaLOWHIGHLOWLOWLOWLOWLOWHIGHLOWHIGH

Ogni bit dura 1 / baud secondi. A 9600 baud un bit dura 104,2 microsecondi; poiché in un frame 8N1 ogni byte occupa 10 bit (start + 8 dati + stop), si possono inviare al massimo 960 byte al secondo. Dopo aver rilevato il fronte di discesa del bit di start, il ricevitore legge la linea al centro di ogni bit. Anche se i due clock differiscono un po', i dati vengono letti correttamente finché l'errore accumulato su 10 bit resta sotto mezzo bit; per questo l'errore totale di baud deve restare sotto il 2–3% circa. Con il suo quarzo da 16 MHz l'Arduino Uno non genera esattamente 115200 baud e la velocità reale si discosta di circa il 2,1%; funziona comunque perché è entro il limite.

BaudDurata del bitByte al secondo max (8N1)Dove si usa?
9600104,2 µs960GPS NEO-6M, HC-05 in modalità dati, DFPlayer Mini, impostazione classica del Monitor seriale
1920052,1 µs1920Alcuni sensori e vecchi modem
3840026,0 µs3840HC-05 in modalità comandi AT
5760017,4 µs5760Vecchi bootloader Arduino, limite superiore per SoftwareSerial
1152008,68 µs11520ESP32 ed ESP-01, output di debug veloce
1000000 e oltre1 µs o meno100.000+Collegamenti corti tra schede, data logging veloce

Differenze tra UART, I2C e SPI

I tre metodi di comunicazione seriale più usati con Arduino sono UART, I2C e SPI. La UART funziona solo tra due dispositivi, ed entrambi possono parlare quando vogliono (full duplex). L'I2C raccoglie decine di dispositivi con indirizzi sugli stessi due fili; l'SPI è molto più veloce con quattro fili ma richiede un pin di selezione per ogni dispositivo. Per i dettagli leggi gli articoli Cos'è l'I2C e Cos'è SPI.

CaratteristicaUARTI2CSPI
Fili2 (TX, RX) + GND2 (SDA, SCL) + GND4 (SCK, MOSI, MISO, CS) + GND
Linea di clockNo (asincrona)SìSì
Numero di dispositivi2 (punto-punto)Fino a 127, tramite indirizzoTanti quanti i pin CS
Velocità tipica9600–115200 baud100–400 kHz1–80 MHz
Entrambi i versi insiemeSì (full duplex)NoSì
Moduli tipiciGPS, Bluetooth, GSM, ESP-01, impronteOLED, LCD, RTC, BME280Scheda SD, RFID, TFT, LoRa

TX e RX incrociati e massa comune

La regola d'oro del cablaggio UART: il pin TX (trasmissione) di un dispositivo va al pin RX (ricezione) dell'altro, quindi i fili si incrociano. Pensa a bocca e orecchio: dove uno parla, l'altro deve ascoltare. Se colleghi TX con TX, due uscite si ritrovano unite e non passa alcun dato. Il terzo filo, GND, dà alle due schede lo stesso riferimento di tensione; senza di esso «HIGH» e «LOW» perdono significato e arrivano solo caratteri senza senso. Anche se le schede sono alimentate da fonti diverse, le masse vanno collegate.

Arduino AFiloArduino BNota
TX (D11, SoftwareSerial)→RX (D10, SoftwareSerial)Ciò che invia A arriva a B
RX (D10, SoftwareSerial)←TX (D11, SoftwareSerial)La risposta di B arriva ad A
GND—GNDMassa comune, obbligatoria
5Vfacoltativo5VSolo se una sola scheda è alimentata via USB
Su Arduino Uno e Nano i pin della UART hardware D0 (RX) e D1 (TX) sono collegati anche al chip USB. Se provi a caricare uno sketch con un modulo collegato a questi pin, il caricamento fallisce con l'errore «not in sync» e nel Monitor seriale i dati del modulo si mescolano con quelli del computer. Scollega i fili da D0/D1 durante il caricamento oppure usa altri pin con SoftwareSerial, come negli esempi di questo articolo.

Far comunicare due Arduino: in un verso

Arduino Uno
Arduino Uno: poiché la sua unica UART hardware (D0/D1) è riservata all'USB, collegare due Uno con SoftwareSerial su D10/D11 è la soluzione più comoda.

Nel primo esempio la scheda A invia dieci volte al secondo alla scheda B il valore di un potenziometro su A0, e la scheda B regola di conseguenza la luminosità del LED su D9. Entrambe le schede usano SoftwareSerial su D10 (RX) e D11 (TX), così la connessione USB e il Monitor seriale restano liberi. Poiché i valori vengono inviati con println(), ogni numero termina con i caratteri «\r\n» (fine riga); il ricevitore capisce così che il numero è completo. Collegamento: D11 di A al D10 di B, D10 di A al D11 di B, masse unite.

C++ (Arduino IDE)
1#include <SoftwareSerial.h>
2
3SoftwareSerial link(10, 11);    // RX = 10, TX = 11 (diğer karta / to the other board)
4
5void setup() {
6  Serial.begin(9600);           // USB: bilgisayar / computer
7  link.begin(9600);             // iki tarafta aynı baud / same baud on both sides
8}
9
10void loop() {
11  int deger = analogRead(A0);   // potansiyometre 0-1023 / potentiometer 0-1023
12  link.println(deger);          // ör. "512\r\n" / e.g. "512\r\n"
13  Serial.println(deger);
14  delay(100);                   // saniyede 10 ölçüm / 10 readings per second
15}
16
C++ (Arduino IDE)
1#include <SoftwareSerial.h>
2
3SoftwareSerial link(10, 11);    // RX = 10, TX = 11
4const int LED_PIN = 9;          // PWM pini / PWM pin
5
6char tampon[8];                 // en fazla 7 karakter + '\0' / up to 7 chars + '\0'
7byte konum = 0;
8
9void setup() {
10  Serial.begin(9600);
11  link.begin(9600);
12  pinMode(LED_PIN, OUTPUT);
13}
14
15void loop() {
16  while (link.available()) {               // gelen her baytı oku / read every incoming byte
17    char c = link.read();
18    if (c == '\n') {                       // satır bitti / line complete
19      tampon[konum] = '\0';
20      int deger = atoi(tampon);
21      analogWrite(LED_PIN, map(deger, 0, 1023, 0, 255));
22      Serial.println(deger);
23      konum = 0;
24    } else if (c != '\r' && konum < sizeof(tampon) - 1) {
25      tampon[konum++] = c;                 // satıra ekle / append to the line
26    }
27  }
28}
29

Nel codice del ricevitore ci sono due cose da notare. Primo, i dati non arrivano mai «in un pezzo solo»: «512» arriva come tre byte separati, a circa 1 millisecondo l'uno dall'altro. Per questo raccogliamo i byte in un buffer e li convertiamo in numero quando arriva il fine riga. Secondo, il codice non aspetta mai: funzioni come readString() o parseInt() aspettano per impostazione predefinita 1 secondo quando non arrivano dati e rallentano il programma. Leggere i byte man mano che arrivano è il metodo più affidabile e veloce. Limitiamo anche la dimensione del buffer così che un dato corrotto non venga scritto oltre la fine dell'array.

Comunicazione nei due versi: comando e risposta

Nei progetti reali serve quasi sempre un dialogo nei due versi: una scheda invia un comando, l'altra lo esegue e risponde. Nell'esempio qui sotto la scheda A fa da ponte: inoltra a B ogni carattere che scrivi nel Monitor seriale e stampa ogni carattere che arriva da B. La scheda B capisce i comandi «LED ON», «LED OFF» e «DURUM?» (stato), risponde a ciascuno e avvisa anche da sola quando si preme il pulsante su D2. Non dimenticare di impostare il fine riga del Monitor seriale su A capo (NL) (Newline); B riconosce così la fine del comando.

C++ (Arduino IDE)
1#include <SoftwareSerial.h>
2
3SoftwareSerial link(10, 11);    // RX = 10, TX = 11
4
5void setup() {
6  Serial.begin(9600);
7  link.begin(9600);
8  Serial.println("Komut yaz / type a command: LED ON, LED OFF, DURUM?");
9}
10
11void loop() {
12  if (Serial.available()) link.write(Serial.read());   // bilgisayar -> B kartı / computer -> board B
13  if (link.available()) Serial.write(link.read());     // B kartı -> bilgisayar / board B -> computer
14}
15
C++ (Arduino IDE)
1#include <SoftwareSerial.h>
2
3SoftwareSerial link(10, 11);    // RX = 10, TX = 11
4const int LED_PIN = 13;         // kart üstündeki LED / on-board LED
5const int BUTON_PIN = 2;        // buton GND'ye / button to GND
6
7String satir = "";
8int oncekiButon = HIGH;
9
10void komutCalistir(String k) {
11  k.trim();                                  // \r ve boşlukları at / strip \r and spaces
12  if (k == "LED ON") {
13    digitalWrite(LED_PIN, HIGH);
14    link.println("OK LED=1");
15  } else if (k == "LED OFF") {
16    digitalWrite(LED_PIN, LOW);
17    link.println("OK LED=0");
18  } else if (k == "DURUM?") {                // durum sorgusu / status query
19    link.print("LED=");
20    link.print(digitalRead(LED_PIN));
21    link.print(" BUTON=");
22    link.println(digitalRead(BUTON_PIN) == LOW ? 1 : 0);
23  } else {
24    link.print("HATA / unknown: ");
25    link.println(k);
26  }
27}
28
29void setup() {
30  link.begin(9600);
31  pinMode(LED_PIN, OUTPUT);
32  pinMode(BUTON_PIN, INPUT_PULLUP);
33  satir.reserve(32);
34}
35
36void loop() {
37  while (link.available()) {
38    char c = link.read();
39    if (c == '\n') {
40      komutCalistir(satir);
41      satir = "";
42    } else if (satir.length() < 31) {
43      satir += c;
44    }
45  }
46
47  int buton = digitalRead(BUTON_PIN);        // değişince kendiliğinden bildir / report changes unprompted
48  if (buton != oncekiButon) {
49    oncekiButon = buton;
50    link.println(buton == LOW ? "BUTON BASILDI / pressed" : "BUTON BIRAKILDI / released");
51    delay(20);                               // basit debounce / simple debounce
52  }
53}
54

La chiamata trim() sulla scheda B elimina il carattere \r che arriva quando il Monitor seriale è impostato su «Entrambi (NL & CR)», insieme agli spazi in eccesso, così i comandi vengono riconosciuti con entrambe le impostazioni. Usare reserve(32) per l'oggetto String evita di riallocare memoria a ogni carattere aggiunto e riduce la frammentazione dei 2 KB di RAM dell'Uno. Questa struttura è anche la base per far dialogare Arduino con un programma Python sul computer; dettagli nell'articolo comunicazione seriale tra Arduino e Python.

I limiti di SoftwareSerial

SoftwareSerial imita una UART via software invece che in hardware: è il processore stesso a temporizzare ogni bit. È molto comodo, ma ha un costo. Durante la trasmissione gli interrupt vengono disabilitati per tutto il byte; a 9600 baud significa circa 1 millisecondo per byte, durante il quale millis() può slittare, gli impulsi di un servo possono tremolare e i dati in arrivo su un'altra seriale possono andare persi. Può ascoltare una sola porta SoftwareSerial alla volta; se ne apri una seconda, scegli quale ascolta con listen(). Su schede come Mega e Leonardo si possono usare come RX solo alcuni pin con supporto agli interrupt.

CaratteristicaUART hardware (Serial)SoftwareSerialAltSoftSerial
Pin sull'UnoD0 (RX), D1 (TX), condivisi con l'USBQuasi ogni pin (PCINT per RX)Fissi: D8 (RX), D9 (TX)
Velocità affidabile massima1–2 Mbaud57600 baud (115200 al limite)Stabile a velocità medie (9600–38400)
Carico sulla CPUBassissimo (buffer hardware)Alto, interrupt spenti in trasmissioneMedio, usa il Timer1
Trasmettere e ricevere insiemeSìNoSì
Più porte4 sulla MegaNe ascolta una sola alla voltaUna sola porta
ConflittiCaricamento e Monitor serialeLibrerie sensibili ai tempi come Servo e NeoPixelPWM su D10 e Servo (Timer1)
Se userai più moduli UART (ad esempio GPS + Bluetooth), invece dell'Uno scegli un Arduino Mega con quattro UART hardware (Serial1–Serial3 oltre a Serial) o un ESP32 che ne ha tre. Su Leonardo e Pro Micro l'USB è separato, quindi Serial1 su D0/D1 è completamente libera.

Adattare i livelli 5V e 3,3V

Nella UART il livello «HIGH» è la tensione di lavoro della scheda: 5 V sull'Arduino Uno, 3,3 V su ESP32, Raspberry Pi Pico, ESP-01 e la maggior parte dei moduli recenti. Collegare direttamente un'uscita TX a 5 V a un ingresso RX a 3,3 V espone l'ingresso a sovratensione e danneggia il pin, col tempo o subito; né l'ESP32 né il Pico con RP2040 tollerano i 5 V. Nel verso opposto di solito va bene: l'Uno considera HIGH da 3,0 V in su, quindi legge un TX a 3,3 V, ma essendo vicino al limite, con cavi lunghi un convertitore di livello è più robusto.

CollegamentoCosa serveEsempio
TX dell'Uno (5 V) → RX di ESP32 / Pico (3,3 V)Partitore di tensione o convertitore di livello1 kΩ in serie + 2 kΩ verso GND: 5 × 2/3 ≈ 3,3 V
TX di ESP32 / Pico (3,3 V) → RX dell'Uno (5 V)Di solito direttoL'Uno considera HIGH sopra i 3,0 V
TX dell'Uno → RX dell'HC-05Partitore consigliatoL'RX dell'HC-05 lavora a livello 3,3 V
TX dell'Uno → RX del SIM800LPartitore di tensioneIl livello di segnale del SIM800L è di circa 2,8 V
Entrambe le schede a 5 V o entrambe a 3,3 VDirettoUno ↔ Nano, ESP32 ↔ Pico
Un partitore di tensione va bene solo per segnali in un solo verso e a velocità bassa o media (ad esempio fino a 115200 baud). Per linee bidirezionali o molto veloci usa un convertitore di livello logico bidirezionale basato su BSS138. Puoi calcolare le resistenze con il calcolatore di partitore di tensione; i convertitori di livello sono spiegati in dettaglio nell'articolo convertitore di livello logico.

UART dell'ESP32: Serial, Serial1 e Serial2

L'ESP32 ha tre UART hardware e il bello è che, grazie alla matrice GPIO, i pin UART si possono spostare su quasi qualsiasi pin. La UART0 (Serial) è collegata al chip USB-seriale e serve per il Monitor seriale. La UART2 (Serial2) è per impostazione predefinita su GPIO 16 (RX) e 17 (TX). I vecchi pin predefiniti della UART1 (GPIO 9 e 10) sono collegati alla memoria flash della scheda e non si possono usare direttamente, quindi avvia sempre Serial1 indicando i pin: begin(baud, SERIAL_8N1, RX, TX). Il codice qui sotto crea un ponte nei due versi tra il Monitor seriale e un dispositivo collegato alla UART2 (un GPS, un altro Arduino, un sensore); è ideale per vedere le frasi NMEA di un modulo GPS.

UARTNome in ArduinoRX / TX predefinitiNota
UART0SerialGPIO 3 / GPIO 1Collegata al chip USB-seriale, caricamento e Monitor seriale
UART1Serial1GPIO 9 / GPIO 10 (flash, non usare)Indica altri pin in begin(), ad es. 25 / 26
UART2Serial2GPIO 16 / GPIO 17Sulle schede WROVER 16/17 sono usati dalla PSRAM, scegli altri pin
C++ (Arduino IDE)
1// ESP32: UART0 = Serial (USB), UART2 = Serial2 (GPIO 16/17)
2const int RXD2 = 16;            // karşı cihazın TX'i buraya / the other device's TX goes here
3const int TXD2 = 17;            // karşı cihazın RX'ine / to the other device's RX
4
5void setup() {
6  Serial.begin(115200);                          // USB Seri Monitör / USB Serial Monitor
7  Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);   // baud, çerçeve, RX, TX / baud, frame, RX, TX
8  // UART1'i de istediğin pinlere taşıyabilirsin / UART1 can be moved to any free pins:
9  // Serial1.begin(115200, SERIAL_8N1, 25, 26);
10  Serial.println("UART2 hazir / ready");
11}
12
13void loop() {
14  while (Serial2.available()) Serial.write(Serial2.read());   // UART2 -> USB
15  while (Serial.available()) Serial2.write(Serial.read());    // USB -> UART2
16}
17

Le UART dell'ESP32 arrivano fino a 5 Mbaud e ognuna ha il suo buffer hardware; il buffer di ricezione predefinito è di 256 byte e si può ingrandire prima di begin() con una chiamata come Serial2.setRxBufferSize(1024). È importante con i moduli GPS che inviano decine di frasi al secondo o con i moduli GSM dalle risposte lunghe. Poiché i pin dell'ESP32 sono a 3,3 V, non dimenticare il partitore della sezione precedente quando lo colleghi a un Arduino a 5 V. La piedinatura dell'ESP32 è visibile nello strumento Pinout.

Pin UART del Raspberry Pi Pico e MicroPython

Il Raspberry Pi Pico ha due UART hardware (uart0 e uart1), ognuna instradabile su diverse coppie di pin. In MicroPython il REPL e l'output di print() passano da una porta seriale virtuale separata sull'USB, quindi la UART0 è completamente libera; non serve scollegare fili durante il caricamento come su Arduino. I pin del Pico sono a 3,3 V e sul Pico con RP2040 non tollerano i 5 V. Il codice qui sotto fa sul Pico quello che faceva la scheda B su Arduino: legge righe dalla UART0, accende o spegne il LED integrato con «LED ON» e «LED OFF» e risponde. Se vuoi farlo dialogare con lo sketch ponte dell'Uno, ricorda un partitore di tensione sul TX dell'Uno.

UARTPin TXPin RXEsempio MicroPython
uart0GP0, GP12, GP16GP1, GP13, GP17UART(0, 9600, tx=Pin(0), rx=Pin(1))
uart1GP4, GP8GP5, GP9UART(1, 9600, tx=Pin(4), rx=Pin(5))
MicroPython
1from machine import UART, Pin
2import time
3
4uart = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))   # UART0: GP0 = TX, GP1 = RX
5led = Pin("LED", Pin.OUT)                             # Pico ve Pico W dahili LED / on-board LED
6
7tampon = b""
8while True:
9    if uart.any():
10        tampon += uart.read()                         # gelen baytlar / incoming bytes
11        while b"\n" in tampon:                        # her tam satırı işle / handle each full line
12            satir, tampon = tampon.split(b"\n", 1)
13            try:
14                komut = satir.decode().strip()
15            except UnicodeError:
16                continue                              # bozuk bayt: atla / corrupt byte: skip
17            if komut == "LED ON":
18                led.on()
19                uart.write("OK LED=1\n")
20            elif komut == "LED OFF":
21                led.off()
22                uart.write("OK LED=0\n")
23            else:
24                uart.write("HATA / unknown: " + komut + "\n")
25    if len(tampon) > 64:                              # satır sonu hiç gelmezse taşmayı önle / avoid overflow
26        tampon = b""
27    time.sleep_ms(5)
28

Pacchetti di dati: carattere di inizio e di fine

La UART è un flusso di byte; non sa dove qualcosa inizia o finisce. Quando il trasmettitore scrive «23.5,48,812», il ricevitore potrebbe aver iniziato ad ascoltare a metà, un byte potrebbe essersi perso per un disturbo o due misure potrebbero unirsi. La soluzione è trasformare i dati in un pacchetto: se ogni pacchetto inizia con «<» e finisce con «>», il ricevitore ignora tutto fino al primo «<», elabora il pacchetto all'arrivo di «>» e scarta i mezzi pacchetti intermedi. Il trasmettitore deve solo scrivere i valori tra Serial.print('<'), virgole e Serial.print('>'). Puoi provare subito il ricevitore qui sotto scrivendo <23.5,48,812> nel Monitor seriale.

C++ (Arduino IDE)
1// Paket biçimi / packet format: <sicaklik,nem,isik>  ör. / e.g. <23.5,48,812>
2const byte MAKS = 32;
3char tampon[MAKS];
4byte konum = 0;
5bool paketIcinde = false;                    // '<' görüldü mü? / have we seen '<'?
6
7void paketiIsle(char* p) {
8  char* parca = strtok(p, ",");              // ilk alan / first field
9  if (!parca) return;
10  float sicaklik = atof(parca);
11  parca = strtok(NULL, ",");
12  if (!parca) return;                        // eksik alan: paketi at / missing field: drop
13  int nem = atoi(parca);
14  parca = strtok(NULL, ",");
15  if (!parca) return;
16  int isik = atoi(parca);
17
18  Serial.print("Sicaklik / temp: ");
19  Serial.print(sicaklik, 1);
20  Serial.print("  Nem / humidity: ");
21  Serial.print(nem);
22  Serial.print("  Isik / light: ");
23  Serial.println(isik);
24}
25
26void setup() {
27  Serial.begin(9600);                        // Seri Monitör'den <23.5,48,812> yazarak dene / test from the Serial Monitor
28}
29
30void loop() {
31  while (Serial.available()) {
32    char c = Serial.read();
33    if (c == '<') {                          // yeni paket başladı / a new packet starts
34      paketIcinde = true;
35      konum = 0;
36    } else if (c == '>' && paketIcinde) {    // paket bitti / packet complete
37      tampon[konum] = '\0';
38      paketIcinde = false;
39      paketiIsle(tampon);
40    } else if (paketIcinde) {
41      if (konum < MAKS - 1) tampon[konum++] = c;
42      else paketIcinde = false;              // çok uzun, bozuk: at / too long, corrupt: drop
43    }                                        // paket dışındaki baytlar yok sayılır / bytes outside packets are ignored
44  }
45}
46
MetodoEsempioProContro
Testo con fine riga512\nIl più semplice, leggibile nel Monitor serialeA metà riga non si sa dove inizia
Carattere di inizio e fine<23.5,48,812>Si risincronizza da soloNon si possono usare < e > nei dati
Testo con checksum (come NMEA)$GPGGA,...*47Rileva i pacchetti corrottiUn po' più di calcoli e codice
Pacchetto binario0xAA, lunghezza, dati, CRCIl più corto e veloceDifficile da leggere e da debuggare

In ambienti disturbati o con cavi lunghi puoi fare un passo in più e aggiungere al pacchetto un checksum. Il formato NMEA dei moduli GPS ne è un buon esempio: si fa lo XOR di tutti i caratteri tra «$» e «*» e il risultato si scrive in fondo come numero esadecimale a due cifre. Il ricevitore rifà lo stesso calcolo e scarta il pacchetto se non coincide. Così eviti errori subdoli, come un «812» corrotto elaborato come «872».

Il trasmettitore non deve essere più veloce di quanto il ricevitore riesca a elaborare. Il buffer di ricezione hardware dell'Arduino Uno è di soli 64 byte; se in loop() usi delay() lunghi, i byte in arrivo vanno persi appena il buffer è pieno. Nel ricevitore usa millis() al posto di delay() e controlla Serial.available() a ogni giro del loop.

Idee di progetto con la UART

  • Controllo dal telefono: l'HC-05 è in realtà una UART senza fili; comanda un LED, un relè o un'auto robot dallo smartphone con l'HC-05.
  • Registro GPS: interpreta le frasi NMEA che un GPS NEO-6M invia via UART e salva la posizione su una scheda SD.
  • Sistema a due schede: collega via UART un Uno che legge i sensori a un ESP32 che invia i dati su internet via Wi-Fi.
  • Allarme via SMS: invia comandi AT a un SIM800L via UART e ricevi un SMS quando si apre una porta.
  • Pannello di controllo sul PC: invia comandi ad Arduino da un'applicazione desktop in Python e traccia i grafici dei dati dei sensori.
  • Lettore MP3: scegli i brani, regola il volume e riproduci messaggi vocali via UART con un DFPlayer Mini.

Errori comuni e soluzioni

SintomoCausa probabileSoluzione
Caratteri senza senso nel Monitor serialeVelocità diverseUsa gli stessi baud nel codice e nel Monitor seriale
Non arriva nessun datoTX collegato a TX, RX a RXIncrocia i fili: TX → RX, RX → TX
Caratteri corrotti a casoGND non collegatoUnisci le masse delle due schede
Il caricamento fallisce (not in sync)Modulo collegato a D0/D1Scollega i fili durante il caricamento o usa SoftwareSerial
I comandi non vengono riconosciutiFine riga \r\n o assenteImposta il Monitor seriale su A capo (NL) e pulisci il testo con trim()
La fine dei messaggi lunghi si perdeBuffer da 64 byte traboccatoNiente delay(), leggi i dati appena arrivano, abbassa la velocità
Il pin RX dell'ESP32 non funziona o si è danneggiatoTX a 5 V collegato direttamenteAggiungi un partitore o un convertitore di livello
SoftwareSerial sbaglia a 115200La temporizzazione software non reggeUsa 57600 o meno, oppure passa a una UART hardware
Se un modulo UART non risponde, collegalo prima direttamente al computer con un convertitore USB-TTL e inviagli comandi da un terminale seriale. Se lì funziona, il problema è nel codice Arduino o nei collegamenti; se non funziona neanche lì, controlla baud, alimentazione e il modulo stesso.

La UART è il primo metodo di comunicazione da imparare in elettronica: bastano due fili, una massa comune e la stessa velocità, e se montata bene funziona per anni senza problemi. Quando avrai preso confidenza con il collegamento incrociato, l'adattamento dei livelli e i pacchetti, potrai usare con facilità decine di moduli, dal GPS al GSM. Nell'app BlueGrays trovi progetti di comunicazione monodirezionale e bidirezionale tra due Arduino, di accensione di un LED dal telefono via Bluetooth e di un'auto comandata via Bluetooth, passo passo con schema e codice.

Domande frequenti

Cosa significa UART e a cosa serve?

UART significa Universal Asynchronous Receiver-Transmitter, cioè ricevitore-trasmettitore asincrono universale. Permette a due dispositivi di scambiarsi dati byte per byte senza linea di clock, solo con i fili TX e RX e una velocità concordata prima. Il Monitor seriale di Arduino e i moduli GPS, Bluetooth e GSM usano la UART.

Come si collegano TX e RX?

Il pin TX di un dispositivo va al pin RX dell'altro, e il suo RX al TX dell'altro: il collegamento è incrociato. Inoltre i pin GND dei due dispositivi devono essere uniti. Collegare TX con TX blocca completamente i dati.

Cos'è il baud rate e quale scegliere?

Il baud rate è il numero di bit inviati al secondo e deve essere uguale sui due lati. I valori più comuni con i moduli Arduino sono 9600 e 115200; 9600 è il valore predefinito della maggior parte dei moduli GPS e Bluetooth, 115200 si preferisce per l'ESP32 e per output veloci. Parti dalla velocità predefinita indicata dal modulo.

UART e porta seriale sono la stessa cosa?

Nell'uso quotidiano spesso sì. La UART è l'unità hardware che serializza i dati; la porta seriale è il collegamento COM o /dev/tty del computer. RS-232 e RS-485 sono standard che usano altri livelli di tensione per portare i segnali UART a distanze maggiori.

Come si usano più UART su Arduino?

L'Uno ha una sola UART hardware condivisa con l'USB; per altre porte si usa SoftwareSerial, ma ne può ascoltare una sola alla volta. L'Arduino Mega ha tre UART hardware in più (Serial1, Serial2 e Serial3), l'ESP32 tre in totale e il Raspberry Pi Pico due.

Quanto può essere lungo un cavo UART?

Una UART a livelli TTL (3,3 V o 5 V) funziona in modo affidabile fino a pochi metri a bassa velocità, e la distanza si accorcia aumentando i baud. Per distanze maggiori i transceiver RS-485 (come il MAX485) arrivano a centinaia di metri, l'RS-232 a circa 15 metri.

Vedi tutti i progetti
Comunicazione unidirezionale tra 2 Arduino
#57Intermedio
Premium

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

4.4(37)
50
Comunicazione bidirezionale tra 2 Arduino
#58Intermedio
Premium

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

4.5(20)
26
Controllare un LED dal telefono tramite Bluetooth
#34Principiante
Premium

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

4.8(68)
89
Auto controllata via Bluetooth con L298N
#80Avanzato
Premium

Auto controllata via Bluetooth con L298N

Trasforma il telefono in un telecomando e guida la tua auto robot. L'Arduino riceve tramite un modulo Bluetooth i comandi di un'app di controllo remoto sul telefono e fa girare le ruote con un driver

5.0(38)
65
Tastiera per sviluppatori Python
#66Intermedio
Premium

Tastiera per sviluppatori Python

Se sei stanco di scrivere sempre gli stessi schemi mentre programmi, questa piccola tastiera fa per te. L'Arduino Leonardo si collega al computer proprio come una tastiera, e ogni tasto del tastierino

4.6(35)
45
HC-SR04 Misura della distanza (display seriale)
#27Principiante

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

4.6(46)
71
Temperatura e umidità con DHT11
#55Intermedio

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

4.7(47)
73
Lettura dell'intensità luminosa con il modulo LDR ESP32
#113Principiante

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.

4.6(36)
55