Cos'è un ADC? Convertitore analogico-digitale con Arduino
Cos'è un ADC e come funziona? Analogico e digitale, risoluzione a 10 e 12 bit, analogReference, formula in volt, ADC2 dell'ESP32 e riduzione del rumore.
Un ADC (convertitore analogico-digitale) è il circuito che trasforma la tensione presente su un pin, che può variare in modo continuo, in un numero che il microcontrollore può elaborare. Ogni volta che esegui analogRead() su Arduino, questo convertitore lavora dietro le quinte: l'ADC a 10 bit di Arduino Uno trasforma una tensione tra 0 e 5 V in un numero da 0 a 1023, mentre gli ADC a 12 bit di ESP32 e Raspberry Pi Pico trasformano circa 0–3,3 V in un numero da 0 a 4095. L'ADC è la chiave per leggere qualsiasi componente con uscita analogica: potenziometri, LDR, sensori di umidità del terreno, joystick e sensori di temperatura.
In questa guida imparerai la differenza tra segnali analogici e digitali, come un ADC converte una tensione in un numero passo dopo passo e come risoluzione e tensione di riferimento influenzano le tue misure. Con numeri reali vedremo la formula per convertire una lettura in volt, come cambiare l'intervallo di misura con analogReference, la frequenza di campionamento e i modi pratici per ridurre il rumore. Parleremo anche dell'ADC dell'ESP32, che non è lineare agli estremi, del conflitto tra ADC2 e Wi-Fi e dell'impostazione di attenuazione, dell'uso dell'ADC del Raspberry Pi Pico con MicroPython e, per finire, faremo una breve introduzione al DAC, che fa esattamente il contrario.

Qual è la differenza tra segnale analogico e digitale?
Un segnale analogico è una grandezza che può assumere infiniti valori all'interno di un intervallo e che varia in modo continuo nel tempo. Quando la tensione sul pin centrale di un potenziometro passa da 0 V a 5 V, attraversa tutti i valori intermedi, come 1,2 V, 1,21 V o 1,215 V; anche l'onda sonora di un microfono, la tensione di un termistore che cambia con la temperatura o la tensione di una batteria che cala lentamente sono analogiche. Un segnale digitale, invece, conosce solo certi livelli. Nel mondo della logica di Arduino sono due: LOW (0) e HIGH (1). Su Arduino Uno un ingresso digitale considera LOW tutto ciò che sta sotto circa 1,5 V e HIGH tutto ciò che supera 3,0 V; la zona intermedia non è definita. Per questo digitalRead() non distingue 3,7 V da 4,2 V su una batteria e in entrambi i casi restituisce HIGH. Il punto di forza del segnale digitale è l'immunità ai disturbi: 4,6 V e 5,0 V significano lo stesso HIGH. Per misurare, memorizzare o usare in un calcolo un valore analogico devi prima convertirlo in un numero, cioè in un valore digitale, ed è proprio questo il compito dell'ADC.
Cos'è un ADC e come funziona?
Il lavoro di un ADC si riassume in due passaggi. Il primo è il campionamento: nel momento della misura il convertitore copia la tensione d'ingresso su un piccolo condensatore (circuito di sample-and-hold) e la mantiene costante fino al termine della conversione; nell'ATmega328P questo condensatore è di circa 14 pF. Il secondo è la quantizzazione: la tensione mantenuta viene arrotondata al gradino più vicino di una scala che divide la tensione di riferimento in gradini uguali, e il numero di quel gradino è il risultato. Gli ADC di Arduino Uno, ESP32 e Pico sono tutti ad approssimazioni successive (SAR, Successive Approximation Register). Un ADC SAR funziona come un gioco per indovinare un numero: prima prova metà della tensione di riferimento, mette a 1 il bit più significativo se l'ingresso è maggiore e lo lascia a 0 se è minore; poi prova metà dell'intervallo rimasto e ripete per ogni bit. Così un ADC a 10 bit trova il gradino giusto tra 1024 possibilità con soli 10 confronti. Per semplicità, la tabella mostra come un ADC a 4 bit con riferimento a 5 V misura un ingresso di 3,2 V:
| Passo | Bit provato | Tensione di confronto | L'ingresso (3,2 V) è maggiore? | Bit del risultato |
|---|---|---|---|---|
| 1 | Bit 3 (vale 8) | 8 / 16 × 5 = 2,500 V | Sì → 1 | 1000 |
| 2 | Bit 2 (vale 4) | 12 / 16 × 5 = 3,750 V | No → 0 | 1000 |
| 3 | Bit 1 (vale 2) | 10 / 16 × 5 = 3,125 V | Sì → 1 | 1010 |
| 4 | Bit 0 (vale 1) | 11 / 16 × 5 = 3,4375 V | No → 0 | 1010 |
| Risultato | Tutti i bit provati | 1010 = 10 → 10 × 5 / 16 = 3,125 V | Errore di 0,075 V (un gradino = 0,3125 V) | 1010 |
La differenza di 0,075 V nel risultato è l'errore di quantizzazione, e nessun ADC può evitarlo: se la tensione reale cade tra due gradini, l'ADC può indicarne solo uno. L'errore resta sotto un gradino (LSB), quindi più il gradino è piccolo, più la misura è fine. Poiché il condensatore di sample-and-hold deve caricarsi a ogni misura, conta anche la resistenza della sorgente che pilota l'ADC: il datasheet dell'ATmega328P raccomanda un'impedenza d'uscita di circa 10 kΩ o meno; un partitore di tensione con resistenze più alte non riesce a caricare il condensatore in tempo e le letture derivano. Oltre al tipo SAR dei microcontrollori esistono altri tipi di ADC. Gli ADC sigma-delta sono lenti ma molto precisi: l'HX711 usato nelle bilance offre 24 bit, il modulo ADS1115 collegato via I2C 16 bit. Gli ADC flash usano un comparatore separato per ogni gradino; sono velocissimi e si trovano negli oscilloscopi.
Risoluzione dell'ADC: cosa significano 10 bit e 12 bit?
La risoluzione indica in quanti gradini l'ADC divide il suo intervallo di misura. Un ADC a N bit produce 2^N gradini: 10 bit sono 1024 gradini (0–1023), 12 bit sono 4096 (0–4095) e 16 bit 65.536. La tensione di un gradino si chiama LSB (bit meno significativo) e si calcola con LSB = Vref / 2^N. Su Arduino Uno fa 5 V / 1024 = 4,88 mV, su ESP32 e Pico 3,3 V / 4096 = 0,81 mV. Quindi Uno non vede variazioni inferiori a 4,88 mV; con un LM35, che fornisce 10 mV/°C, significa un passo di circa 0,5 °C. Ma risoluzione e precisione non sono la stessa cosa. Rumore, non linearità ed errore di riferimento di un ADC rendono inutili una parte dei bit dichiarati sulla carta. La misura di questo è il numero effettivo di bit (ENOB): l'ADC dell'RP2040 nel Raspberry Pi Pico fornisce un risultato a 12 bit, ma secondo il datasheet il numero effettivo di bit è 8,7. Mettiamo a confronto gli ADC delle tre schede:
| Caratteristica | Arduino Uno R3 | ESP32 (WROOM-32) | Raspberry Pi Pico |
|---|---|---|---|
| Risoluzione dell'ADC | 10 bit (0–1023) | 12 bit (0–4095) | 12 bit (scalati a 0–65535 in MicroPython) |
| Ingressi analogici | 6 (A0–A5; A0–A7 sul Nano) | 18 canali: 8 su ADC1, 10 su ADC2 | 3 pin (GP26–GP28) + VSYS/3 (GP29) + sensore di temperatura |
| Intervallo di misura predefinito | 0–5 V | Circa 0–3,1 V (attenuazione 11 dB) | 0–3,3 V (ADC_VREF) |
| Un gradino (LSB) | 4,88 mV | Circa 0,8 mV | 0,81 mV |
| Opzioni di riferimento | DEFAULT (5 V), INTERNAL (1,1 V), EXTERNAL (AREF) | Attenuazione: 0; 2,5; 6 e 11 dB | Riferimento esterno sul pin ADC_VREF |
| Frequenza di campionamento | Circa 10.000 letture/s | 20 kHz – 2 MHz in modalità continua (DMA) | 500.000 letture/s in hardware |
| Nota sulla precisione | Errore assoluto ±2 LSB | Non lineare agli estremi, serve calibrazione | ENOB 8,7 bit, offset di circa 30 mV |
| Uscita analogica (DAC) | Nessuna (si usa il PWM) | 2 canali, 8 bit (GPIO 25 e 26) | Nessuna (si usa il PWM) |
Formula per convertire il valore dell'ADC in volt
Il numero restituito dall'ADC da solo non significa nulla; per convertirlo in tensione usi la tensione di riferimento e il numero di gradini: Tensione = valore ADC × Vref / 2^N. Su Arduino Uno diventa V = valore × 5,0 / 1024; se analogRead() ha restituito 512, la tensione sul pin è 512 × 5 / 1024 = 2,50 V. Al contrario, trovi a quale numero corrisponde una certa tensione con valore = V × 1024 / Vref: 3,3 V vengono letti come circa 675 su Uno. In molti esempi vedrai dividere per 1023 invece che per 1024. Il datasheet dell'ATmega328P riporta la formula ADC = Vin × 1024 / Vref; il valore massimo, 1023, rappresenta un gradino sotto Vref. Dividere per 1023 fa mostrare al valore massimo esattamente 5,00 V, ma sposta tutti i valori di circa lo 0,1 %. Questa differenza è più piccola dell'errore proprio dell'ADC (±2 LSB), quindi vanno bene entrambe; l'importante è essere coerenti nel progetto. La vera grande fonte di errore è Vref stessa: il pin 5V di un Uno alimentato via USB sta spesso tra 4,7 e 5,1 V, e scrivere 5,0 nella formula causa errori fino al 5 %. Per misure precise, quindi, misura il pin 5V con un multimetro e inserisci il valore reale nel codice. Anche l'unico ingresso analogico dell'ESP8266 (A0) è a 10 bit; il chip misura 0–1 V, e il partitore integrato su schede come NodeMCU e Wemos D1 Mini estende l'intervallo a circa 0–3,2 V.
| Scheda e impostazione | Intervallo letto | Conversione in volt | Esempio |
|---|---|---|---|
| Arduino Uno / Nano (DEFAULT, 5 V) | 0–1023 | V = valore × 5,0 / 1024 | 768 → 3,75 V |
| Uno, analogReference(INTERNAL) | 0–1023 | V = valore × 1,1 / 1024 | 512 → 0,55 V |
| ESP32 (11 dB) | 0–4095 | Calibrato: analogReadMilliVolts() / 1000; approssimato: valore × 3,3 / 4095 | 1650 mV → 1,65 V |
| Raspberry Pi Pico (read_u16) | 0–65535 | V = valore × 3,3 / 65535 | 32768 → 1,65 V |
| ESP8266 NodeMCU / Wemos D1 Mini (A0) | 0–1023 | V = valore × 3,2 / 1024 | 512 → 1,60 V |
Collegare un potenziometro: Arduino, ESP32 e Pico
| Pin del potenziometro | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| Pin esterno 1 | 5V | 3V3 | 3V3(OUT) (pin 36) |
| Pin centrale (segnale) | A0 | GPIO 34 (ADC1_CH6) | GP26 / ADC0 (pin 31) |
| Pin esterno 2 | GND | GND | AGND (pin 33) o GND |
| Condensatore antidisturbo (facoltativo) | 100 nF tra A0 e GND | 100 nF tra GPIO 34 e GND | 100 nF tra GP26 e AGND |
Passo dopo passo: la tua prima lettura ADC con Arduino
- Monta il circuito: collega i pin esterni di un potenziometro da 10 kΩ a 5V e GND, e il pin centrale ad A0.
- Misura il pin 5V: misura con il multimetro la tensione tra 5V e GND e scrivila nella costante VREF del codice (ad es. 4.93).
- Carica il codice: apri lo sketch qui sotto nell'IDE Arduino, scegli scheda e porta e caricalo.
- Apri il Monitor seriale: impostalo a 9600 baud e osserva il valore dell'ADC che cambia tra 0 e 1023 mentre giri la manopola (dettagli nella guida al Monitor seriale).
- Verifica: misura con il multimetro tra A0 e GND e confronta con la tensione sullo schermo; la differenza dovrebbe restare intorno a 10–20 mV.
1// Potansiyometreyi ADC ile okuma / Reading a potentiometer with the ADC
2// Kenar bacaklar 5V ve GND'ye, orta bacak A0'a / Outer legs to 5V and GND, wiper to A0
3const int POT_PIN = A0;
4const float VREF = 5.0; // 5V pininde ölçtüğün gerilim / voltage you measured on the 5V pin
5
6void setup() {
7 Serial.begin(9600);
8}
9
10void loop() {
11 int raw = analogRead(POT_PIN); // 0-1023 arası sayı / number between 0-1023
12 float volts = raw * VREF / 1024.0; // sayıyı volta çevir / convert the number to volts
13 int percent = map(raw, 0, 1023, 0, 100); // yüzdeye ölçekle / scale to percent
14
15 Serial.print(F("ADC: "));
16 Serial.print(raw);
17 Serial.print(F(" Gerilim / Voltage: "));
18 Serial.print(volts, 3);
19 Serial.print(F(" V Yuzde / Percent: "));
20 Serial.println(percent);
21 delay(200);
22}analogRead() completa una misura in circa 100 µs e restituisce un intero tra 0 e 1023. Il codice converte prima questo numero in volt con la formula e poi in percentuale con map(). Poiché map() usa l'aritmetica intera, scarta la parte decimale; se ti serve un risultato con i decimali, fai tu il calcolo con un float. Questa è una misura raziometrica: dato che potenziometro e ADC sono alimentati dagli stessi 5 V, la posizione della manopola, cioè il valore dell'ADC, non cambia anche se l'alimentazione scende a 4,9 V; cambia solo la tensione calcolata. Lo stesso vale per i sensori letti tramite un partitore, come LDR e NTC. Quando invece misuri la tensione di una batteria o un sensore con uscita fissa in mV come l'LM35, il valore reale di Vref si riflette direttamente sul risultato.
Tensione di riferimento e analogReference
La tensione di riferimento (Vref) è il punto più alto della scala dell'ADC: se la tensione d'ingresso raggiunge o supera Vref, il risultato resta bloccato sul valore massimo (1023). Ridurre Vref restringe l'intervallo di misura ma rende ogni gradino più piccolo, quindi misuri i segnali piccoli con più finezza. Su Arduino Uno scegli il riferimento con la funzione analogReference(). Con l'opzione INTERNAL, Vref diventa 1,1 V e un gradino scende a 1,07 mV; puoi leggere un LM35 (10 mV/°C) a passi di circa 0,1 °C, ma in cambio non puoi misurare nulla oltre 1,1 V, cioè oltre 110 °C. Lo stesso riferimento interno da 1,1 V può variare tra 1,0 e 1,2 V da un chip all'altro, quindi nei progetti precisi conviene calibrarlo una volta con un multimetro. Le prime letture dopo il cambio di riferimento possono essere imprecise, come segnala anche la documentazione di Arduino, quindi è buona abitudine scartarne alcune dopo il cambio. Inoltre l'impostazione vale contemporaneamente per tutti i pin analogici della scheda. Le opzioni cambiano da scheda a scheda:
| Opzione | Riferimento | Schede | Quando usarla? |
|---|---|---|---|
| DEFAULT | 5 V (3,3 V sulle schede a 3,3 V) | Uno, Nano, Mega, Leonardo | Sensori raziometrici rispetto all'alimentazione, come potenziometri, LDR e joystick |
| INTERNAL | 1,1 V (ATmega328P); 2,56 V sul Leonardo | Uno, Nano, Leonardo (non sul Mega) | Sensori a bassa tensione come l'LM35 |
| INTERNAL1V1 / INTERNAL2V56 | 1,1 V / 2,56 V | Solo Mega | Segnali piccoli sul Mega |
| EXTERNAL | La tensione sul pin AREF (0–5 V) | Uno, Nano, Mega | Usare un tuo riferimento di precisione (ad es. 2,5 V o 4,096 V) |
| AR_INTERNAL | Circa 1,43 V (riferimento interno del chip) | Uno R4 Minima e WiFi | Segnali piccoli su Uno R4 |
Frequenza di campionamento: quante letture al secondo fa un ADC?
La frequenza di campionamento è il numero di misure che l'ADC può fare in un secondo. Su Arduino Uno il clock dell'ADC viene impostato a 125 kHz dividendo i 16 MHz per 128, e una conversione dura 13 cicli di clock; per questo analogRead() impiega circa 100 µs e si possono fare al massimo circa 10.000 letture al secondo. Per grandezze che cambiano lentamente, come temperatura, luce o tensione di una batteria, è più che sufficiente; spesso bastano anche 10 letture al secondo. Con i segnali veloci entra in gioco il teorema di Nyquist: per catturare un segnale senza perderlo devi campionare almeno al doppio della sua frequenza più alta, e in pratica si preferisce da 5 a 10 volte. Per vedere il ronzio di rete a 50 Hz servono almeno 100 letture/s, per registrare la voce fino a 4 kHz almeno 8.000 letture/s. Se su Uno ti servono misure più rapide, puoi ridurre il prescaler dell'ADC: con ADCSRA = (ADCSRA & 0xF8) | 0x04; in setup() il prescaler diventa 16, il clock dell'ADC sale a 1 MHz e analogRead() scende a circa 16 µs, al prezzo di un po' di precisione. Sull'ESP32 le singole chiamate ad analogRead() consentono decine di migliaia di letture al secondo, e nella modalità continua con DMA (analogContinuous() in Arduino) si può scegliere una frequenza tra 20 kHz e 2 MHz; questa modalità funziona solo con i pin dell'ADC1. L'ADC del Raspberry Pi Pico usa un proprio clock a 48 MHz, completa ogni conversione in 96 cicli, cioè 2 µs, e arriva a 500.000 letture al secondo; un ciclo in MicroPython non riesce a stare a questo ritmo, e per la piena velocità servono l'SDK in C e il DMA.
ADC dell'ESP32: non linearità, conflitto ADC2/Wi-Fi e attenuazione
Sulla carta l'ADC a 12 bit dell'ESP32 misura con una finezza quattro volte maggiore rispetto a Uno, ma in pratica devi fare attenzione a tre cose. La prima è la non linearità: vicino a 0 V l'ADC dell'ESP32 legge 0 fino a circa 100–150 mV, e all'estremo superiore va in saturazione a 4095 prima che l'ingresso arrivi a 3,3 V; anche la curva intermedia non è una retta perfetta. La seconda è la variabilità del riferimento: per progetto il riferimento interno dell'ESP32 è di 1100 mV, ma secondo Espressif varia tra 1000 e 1200 mV da un chip all'altro, il che significa un errore di circa ±9 % sulle letture non calibrate. Per questo Espressif scrive dati di calibrazione negli eFuse dei chip in fase di produzione. La funzione analogReadMilliVolts() di Arduino usa questi dati per convertire direttamente il valore grezzo in millivolt calibrati e, nella maggior parte dei casi, è molto più precisa del calcolo analogRead() × 3,3 / 4095. La terza è l'attenuazione: il nucleo dell'ADC misura solo fino a circa 1 V, quindi le tensioni più alte vengono ridotte da un attenuatore regolabile all'ingresso. In Arduino il valore predefinito è 11 dB, che permette letture fino a circa 3,1 V, ma l'intervallo preciso che Espressif raccomanda per questa impostazione è 150–2450 mV. Con segnali piccoli, ridurre l'attenuazione migliora sia la precisione sia la risoluzione:
| Attenuazione | Costante Arduino | Intervallo preciso | Uso |
|---|---|---|---|
| 0 dB | ADC_0db | 100–950 mV | Segnali piccoli, misura più fine |
| 2,5 dB | ADC_2_5db | 100–1250 mV | Uscite di sensori intorno a 1 V |
| 6 dB | ADC_6db | 150–1750 mV | Segnali di livello medio |
| 11 dB (predefinito) | ADC_11db | 150–2450 mV (la lettura arriva a circa 3,1 V) | Potenziometri e sensori 0–3,3 V |
1// ESP32 ADC: ham değer ve kalibre milivolt / ESP32 ADC: raw value and calibrated millivolts
2// Potansiyometre: 3V3, GND ve orta bacak GPIO 34 / Pot: 3V3, GND and wiper to GPIO 34
3const int POT_PIN = 34; // ADC1_CH6, Wi-Fi açıkken de çalışır / ADC1_CH6, works with Wi-Fi on
4
5void setup() {
6 Serial.begin(115200);
7 analogReadResolution(12); // 0-4095 (varsayılan / default)
8 analogSetPinAttenuation(POT_PIN, ADC_11db); // yaklaşık 3,1 V'a kadar / up to about 3.1 V
9}
10
11void loop() {
12 uint32_t rawSum = 0;
13 uint32_t mvSum = 0;
14 for (int i = 0; i < 16; i++) { // 16 ölçümün ortalaması / average of 16 readings
15 rawSum += analogRead(POT_PIN); // ham ADC değeri / raw ADC value
16 mvSum += analogReadMilliVolts(POT_PIN); // eFuse kalibrasyonlu mV / eFuse-calibrated mV
17 }
18 float raw = rawSum / 16.0;
19 float naiveV = raw * 3.3 / 4095.0; // basit formül / naive formula
20 float calibratedV = mvSum / 16.0 / 1000.0; // kalibre edilmiş / calibrated
21
22 Serial.printf("Ham / Raw: %4.0f Formul / Formula: %.3f V Kalibre / Calibrated: %.3f V\n",
23 raw, naiveV, calibratedV);
24 delay(500);
25}Il codice mostra due risultati affiancati: il valore grezzo convertito con la formula semplice e il valore calibrato da analogReadMilliVolts(). Girando il potenziometro verso gli estremi noterai che la differenza cresce, e in fondo, dove la formula indica 0,000 V, il multimetro misura ancora circa 0,1 V. analogSetPinAttenuation() cambia l'attenuazione di un solo pin, mentre analogSetAttenuation() la cambia per tutti. L'ESP32 è decisamente più rumoroso di Uno, soprattutto mentre il Wi-Fi trasmette; la stessa Espressif consiglia di aggiungere un condensatore da 100 nF sull'ingresso e di fare la media di più campioni, ed è proprio ciò che fa la media di 16 letture nel codice. I chip più recenti come ESP32-S3 ed ESP32-C3 usano una calibrazione più evoluta ad adattamento di curva, ma numeri dei pin e regole dell'ADC2 cambiano da modello a modello, quindi controlla sempre la piedinatura della tua scheda (strumento Pinout). Spieghiamo come costruire un partitore per misurare tensioni sopra i 3,3 V nella guida al partitore di tensione.
ADC sul Raspberry Pi Pico (MicroPython)
Il Raspberry Pi Pico ha tre ingressi analogici utilizzabili dall'esterno: GP26 (ADC0), GP27 (ADC1) e GP28 (ADC2). GP29 (ADC3) misura sulla scheda un terzo della tensione di alimentazione VSYS, e l'ADC4 è collegato al sensore di temperatura interno del chip. La funzione read_u16() di MicroPython scala il risultato a 12 bit sull'intervallo 0–65535, così lo stesso codice funziona su schede diverse, ma la risoluzione reale resta di 4096 gradini. L'RP2040 non ha una tensione di riferimento separata: usa come riferimento la propria alimentazione dell'ADC. Sul Pico questa alimentazione (ADC_VREF, pin 35) è ricavata dall'uscita del regolatore switching a 3,3 V (SMPS), filtrata con una resistenza di circa 200 Ω. Il datasheet della scheda di Raspberry Pi ne elenca chiaramente tre conseguenze: la precisione dei 3,3 V è limitata, l'alimentazione è un po' rumorosa e la corrente assorbita dall'ADC crea un offset di circa 30 mV. Le soluzioni sono nello stesso documento: portare GP23 a HIGH per mettere il regolatore in modalità PWM, con meno ripple; collegare ad AGND un canale ADC inutilizzato e sottrarne la lettura come offset; e, per una precisione molto migliore, collegare un riferimento da 3,0 V come l'LM4040 tra ADC_VREF e AGND (l'intervallo di misura diventa allora 0–3,0 V). È noto inoltre che l'ADC dell'RP2040 presenta piccoli salti (errori DNL) ai valori 512, 1536, 2560 e 3584; fare la media di più letture ne riduce l'effetto. Sul Pico W, GP23 è riservato al chip wireless e la modalità del regolatore si controlla tramite WL_GPIO1, quindi su un Pico W elimina le righe di GP23 dal codice seguente.
1from machine import ADC, Pin
2import time
3
4pot = ADC(26) # GP26 = ADC0 (pin 31)
5chip_temp = ADC(4) # dahili sıcaklık sensörü / internal temperature sensor
6VREF = 3.3 # ADC_VREF; multimetreyle ölçüp düzelt / measure and correct with a multimeter
7
8# Yalnız Pico ve Pico 2 (W değil): SMPS PWM moduna, daha az gürültü / Plain Pico and Pico 2 only (not W): SMPS to PWM mode, less noise
9smps_mode = Pin(23, Pin.OUT)
10smps_mode.value(1)
11
12
13def read_average(adc, samples=16):
14 total = 0
15 for _ in range(samples):
16 total += adc.read_u16() # 0-65535 (12 bit ölçeklenmiş / 12-bit scaled)
17 return total / samples
18
19
20while True:
21 raw = read_average(pot)
22 volts = raw * VREF / 65535 # sayıyı volta çevir / convert the number to volts
23 t_volts = read_average(chip_temp) * VREF / 65535
24 temp_c = 27 - (t_volts - 0.706) / 0.001721 # RP2040 veri sayfası formülü / RP2040 datasheet formula
25 print("ADC: {:5d} {:.3f} V Yonga / Chip: {:.1f} C".format(int(raw), volts, temp_c))
26 time.sleep(0.5)Come ridurre il rumore dell'ADC
Anche misurando una tensione stabile è normale che il valore dell'ADC oscilli di uno o due gradini; contribuiscono la quantizzazione, il ripple dell'alimentazione, i disturbi captati dai cavi lunghi e i motori o le trasmissioni Wi-Fi vicine. Su Uno un'oscillazione di ±1–2 gradini è considerata normale; molto di più di solito indica un problema nel circuito. Puoi combattere il rumore sia nell'hardware sia nel software:
- Aggiungi un condensatore: un condensatore ceramico da 100 nF tra il pin analogico e GND filtra i disturbi veloci e fornisce carica pronta al condensatore di campionamento con sorgenti ad alta resistenza.
- Usa cavi corti: se il cavo del sensore è lungo, attorciglia insieme il filo del segnale e quello di GND; fai passare i cavi di motori e relè lontano dalle linee analogiche.
- Cura la massa: la corrente di ritorno di carichi ad alto assorbimento come motori, relè e strisce LED non deve passare dal filo GND del sensore; unisci le masse in un unico punto (massa a stella).
- Usa un riferimento pulito: l'alimentazione USB è rumorosa; nelle misure di tensione assoluta il riferimento interno o un integrato di riferimento esterno danno risultati più stabili.
- Scarta la prima lettura dopo un cambio di canale: il multiplexer all'ingresso dell'ADC può portarsi dietro la carica del canale precedente; quando leggi più pin di seguito, sacrifica una lettura su ogni pin.
- Fai la media: la media di N letture riduce il rumore casuale di circa √N volte; 16 letture lo portano a un quarto.
- Sovracampionamento (oversampling): se sommi 4^n letture e sposti il risultato di n bit a destra, guadagni n bit di risoluzione in più; spostare di 2 bit la somma di 16 letture porta i 10 bit di Uno a 12. Perché funzioni, il segnale deve avere almeno un gradino di rumore naturale.
- Filtri mediano ed esponenziale: il filtro mediano elimina i picchi improvvisi causati dai motori, mentre la media mobile esponenziale (EMA) dà una curva morbida usando pochissima memoria.
1// Ham, ortalama ve EMA filtreli ADC değerleri / Raw, averaged and EMA-filtered ADC values
2// Serial Plotter ile izle / Watch with the Serial Plotter
3const int SENSOR_PIN = A0;
4const float ALPHA = 0.1; // EMA katsayısı (0-1) / EMA coefficient (0-1)
5float ema = 0;
6
7void setup() {
8 Serial.begin(115200);
9 analogRead(SENSOR_PIN); // ilk okumayı at / discard the first reading
10 ema = analogRead(SENSOR_PIN); // filtreyi başlat / seed the filter
11}
12
13void loop() {
14 int raw = analogRead(SENSOR_PIN); // ham değer / raw value
15
16 long sum = 0;
17 for (int i = 0; i < 16; i++) { // 16 ölçüm topla / sum 16 readings
18 sum += analogRead(SENSOR_PIN);
19 }
20 float average = sum / 16.0; // ortalama (0-1023) / average (0-1023)
21
22 ema += ALPHA * (raw - ema); // üstel hareketli ortalama / exponential moving average
23
24 Serial.print(F("ham:"));
25 Serial.print(raw);
26 Serial.print(F(" ortalama:"));
27 Serial.print(average);
28 Serial.print(F(" ema:"));
29 Serial.println(ema);
30 delay(20);
31}Caricando il codice e aprendo il Plotter seriale dell'IDE Arduino vedrai tre linee: il valore grezzo trema, la media di 16 letture è nettamente più calma e l'EMA è la curva più morbida, ma reagisce ai cambiamenti con un po' di ritardo. Il coefficiente ALPHA si sceglie tra 0 e 1: 0,1 significa che ogni nuova lettura pesa per il 10 % sul risultato, quindi contano all'incirca le ultime 10 letture; aumentandolo la risposta diventa più rapida ma il rumore cresce. Il filtro giusto dipende dal segnale: per una temperatura che varia lentamente va bene un'EMA forte, per un joystick che deve rispondere in fretta una media breve. Confrontiamo media mobile, filtro mediano e isteresi con esempi reali di sensori nella guida al filtraggio dei dati dei sensori.
Breve introduzione al DAC: dal digitale all'analogico
Un DAC (convertitore digitale-analogico) fa il contrario di un ADC: riceve un numero e produce una tensione proporzionale. Se scrivi 0 in un DAC a 8 bit ottieni 0 V, con 255 un valore vicino alla tensione di alimentazione e con 128 circa la metà. I DAC più semplici sono realizzati con una scala di resistenze chiamata R-2R. Chiariamo qui un equivoco frequente: analogWrite() su Arduino Uno non è un DAC, ma genera un segnale PWM; il pin in realtà commuta molto rapidamente tra 0 V e 5 V. Per la luminosità di un LED o la velocità di un motore basta, ma se ti serve davvero una tensione stabile devi aggiungere un filtro passa-basso RC all'uscita PWM; puoi calcolare la costante di tempo del filtro con lo strumento per la costante di tempo RC. Quanto alle schede con un vero DAC: l'ESP32 ha due DAC a 8 bit su GPIO 25 e GPIO 26, che si usano con dacWrite(pin, valore), mentre ESP32-S3 ed ESP32-C3 non hanno DAC. Arduino Uno R4 ha un DAC a 12 bit sul pin A0. Per Uno R3, Nano e Pico la via più semplice è il modulo MCP4725 a 12 bit collegato via I2C. Con un DAC puoi generare suoni, produrre onde sinusoidali o triangolari e persino collegare l'uscita del DAC a un ingresso dell'ADC per testare l'ADC con tensioni note:
1// ESP32 DAC ile sinüs dalgası / Sine wave with the ESP32 DAC
2// Çıkış GPIO 25'te, osiloskopla izle / Output on GPIO 25, watch it with an oscilloscope
3const int DAC_PIN = 25; // DAC1 = GPIO 25 (DAC2 = GPIO 26)
4const int STEPS = 64; // bir periyottaki nokta sayısı / points per period
5uint8_t sineTable[STEPS];
6
7void setup() {
8 for (int i = 0; i < STEPS; i++) {
9 sineTable[i] = 128 + 127 * sin(2 * PI * i / STEPS); // 1-255 arası tablo / table between 1-255
10 }
11}
12
13void loop() {
14 for (int i = 0; i < STEPS; i++) {
15 dacWrite(DAC_PIN, sineTable[i]); // 0 → 0 V, 255 → yaklaşık 3,3 V / 0 → 0 V, 255 → about 3.3 V
16 delayMicroseconds(150); // yaklaşık 100 Hz / roughly 100 Hz
17 }
18}Idee di progetto con l'ADC
- Voltmetro digitale: misura da 0 a 25 V con un partitore di tensione e mostra il risultato su un LCD; i passaggi sono nella guida costruire un voltmetro con Arduino.
- Luce notturna automatica: leggi la tensione di una LDR e accendi i LED quando fa buio.
- Vaso intelligente: usa l'uscita analogica di un sensore di umidità del terreno per far partire una pompa quando la terra si asciuga.
- Robot comandato con joystick: i due assi di un joystick sono due ingressi analogici separati; trasforma i loro valori in velocità e direzione dei motori.
- Misuratore di corrente: leggi la tensione proporzionale alla corrente di un ACS712 per sapere quanta corrente assorbe un apparecchio.
- Indicatore di livello sonoro: campiona velocemente l'uscita analogica di un sensore sonoro KY-038 e accendi una barra di LED in base al valore di picco.
- Indicatore della batteria: misura la tensione in un progetto a batteria e mostra la carica residua in percentuale su un display.
Errori comuni e soluzioni
| Sintomo | Causa probabile | Soluzione |
|---|---|---|
| Il valore cambia a caso senza niente collegato | Il pin è flottante | Collega il sensore; non leggere i pin inutilizzati o portali a GND |
| Il valore resta fermo a 1023 (4095 sull'ESP32) | L'ingresso supera il riferimento oppure è selezionato INTERNAL | Regola riferimento o attenuazione, abbassa le tensioni alte con un partitore |
| Sull'ESP32 analogRead() restituisce 0 quando il Wi-Fi è attivo | Il sensore è su un pin dell'ADC2 | Usa i pin dell'ADC1, GPIO 32–39 |
| L'ESP32 mostra 0 con tensioni basse e 4095 con quelle alte | L'ADC non è lineare agli estremi | Usa analogReadMilliVolts() e tieni il segnale tra 150 e 2450 mV |
| La tensione calcolata differisce del 3–5 % dal multimetro | Vref non è 5,00 V (alimentazione USB) | Misura il pin 5V e scrivilo in VREF, oppure aggiungi un fattore di calibrazione |
| Leggendo due pin di seguito, i valori si influenzano | Multiplexer e resistenza della sorgente alta | Scarta la prima lettura dopo il cambio, tieni la sorgente sotto 10 kΩ, aggiungi 100 nF |
| I valori saltano quando gira un motore o scatta un relè | Disturbi sulla linea GND comune, l'alimentazione cala | Dai ai carichi un ritorno GND separato, usa condensatori e un filtro mediano |
| Le letture sono sbagliate dopo aver applicato tensione ad AREF | AREF usato senza chiamare analogReference(EXTERNAL) | Seleziona prima EXTERNAL o collega AREF tramite una resistenza da 5 kΩ |
| Dopo aver scelto INTERNAL il potenziometro arriva a 1023 a metà corsa | Il riferimento da 1,1 V vale per tutti i pin analogici | Torna a DEFAULT prima di leggere sensori raziometrici e scarta alcune letture |
| Sul Pico la lettura è sopra 0 anche con l'ingresso a GND | Offset dell'ADC e rumore su ADC_VREF | Collega un canale ad AGND, misura l'offset e sottrailo; porta GP23 a HIGH |
L'ADC è il ponte tra il mondo fisico e il tuo codice: quando conosci la sua risoluzione, la tensione di riferimento e la frequenza di campionamento, sai anche cosa rappresenta ogni numero sullo schermo e quanto puoi fidarti. Su Uno misura bene il riferimento, sull'ESP32 usa i pin dell'ADC1 e analogReadMilliVolts(), sul Pico tieni conto di offset e rumore, poi rendi stabile il risultato con medie e filtri. Nell'app BlueGrays trovi progetti basati sulle letture analogiche, come la misura dell'intensità luminosa con una LDR, un voltmetro digitale con LCD, la misura della temperatura con un NTC e il controllo di un servo e della luminosità di un LED con un potenziometro, con schemi e codice passo dopo passo.
Domande frequenti
Cos'è un ADC e a cosa serve?
Un ADC (convertitore analogico-digitale) è il circuito che converte la tensione di un pin in un numero che il microcontrollore può elaborare. Serve a leggere componenti con uscita analogica come potenziometri, LDR e sensori di temperatura e umidità. Su Arduino è l'istruzione analogRead() ad attivarlo.
Qual è la differenza tra segnale analogico e digitale?
Un segnale analogico può assumere qualsiasi valore all'interno di un intervallo e varia in modo continuo, mentre un segnale digitale ha solo determinati livelli, LOW e HIGH su Arduino. digitalRead() ti dice solo se un pin è a 0 o a 1, analogRead() invece ti dà con un numero quanta tensione c'è.
Perché analogRead di Arduino restituisce valori da 0 a 1023?
L'ADC di Arduino Uno è a 10 bit e produce 2^10 = 1024 gradini diversi, numerati da 0 a 1023. Il valore 0 rappresenta 0 V, mentre 1023 rappresenta un gradino sotto la tensione di riferimento (di solito 5 V). Gli ADC a 12 bit di ESP32 e Pico restituiscono valori da 0 a 4095.
Come si converte il valore dell'ADC in tensione?
Usa la formula tensione = valore ADC × tensione di riferimento / numero di gradini. Su Arduino Uno è V = valore × 5,0 / 1024; sull'ESP32 analogReadMilliVolts() restituisce direttamente millivolt calibrati. Sul Pico, per il valore di read_u16() di MicroPython si usa V = valore × 3,3 / 65535.
I pin ADC2 dell'ESP32 funzionano con il Wi-Fi?
No. Sull'ESP32 anche il driver Wi-Fi usa l'unità ADC2, quindi con il Wi-Fi attivo non si può leggere dai pin dell'ADC2 (GPIO 0, 2, 4, 12–15, 25–27). Nei progetti con Wi-Fi collega i sensori analogici ai pin dell'ADC1 (GPIO 32–39).
Cos'è un DAC e in cosa differisce da un ADC?
Un DAC (convertitore digitale-analogico) fa il contrario di un ADC: converte un numero in una tensione. L'ADC serve a misurare la tensione che arriva da un sensore, il DAC a generare una tensione, come un suono o un segnale. Arduino Uno non ha un DAC e analogWrite() genera PWM; l'ESP32 ha due DAC a 8 bit su GPIO 25 e 26, e Uno R4 un DAC a 12 bit su A0.
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

Voltmetro digitale con display LCD
Scopri quanta tensione dà una pila o un alimentatore con uno strumento costruito da te. Un partitore fatto di due resistenze abbassa la tensione alta a un livello che la scheda può leggere, e l'Arduin

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.

Luminosità di un LED con Potenziometro su ESP32 (Analogico → PWM)
In questo progetto controlliamo in modo analogico la luminosità di un LED tramite un potenziometro. Il pin ADC dell'ESP32 legge la tensione tra 0 e 3,3 V dal potenziometro; il valore viene convertito in un segnale PWM (Pulse Width Modulation) e inviato al LED. Ruotando il potenziometro, il LED si accende e si spegne gradualmente. Con Arduino Core 3.0+ si usa direttamente analogWrite(); su ESP32-S2/S3 la risoluzione dell'ADC è fissata a 12 bit.

Misura di temperatura con NTC e display Lcd 2x16
Misura la temperatura dell'ambiente con un sensore che sembra una piccola perlina. Questo componente, chiamato NTC, fa passare la corrente sempre più facilmente man mano che si scalda; l'Arduino legge

Funzionamento del servomotore con potenziometro
Mentre giri la manopola del potenziometro, il braccio del servomotore ruota insieme a lei: un'estremità della manopola corrisponde a zero gradi, l'altra a mezzo giro. L'Arduino legge di continuo la po

Controllo LED con joystick
Spingi fino in fondo la levetta del joystick in una direzione e si accende il LED di quella direzione: ci sono quattro colori diversi per destra, sinistra, su e giù. Quando lasci la levetta torna al c

Lampada con miscelatore di colori ESP32 (3 potenziometri + LED RGB + OLED)
Una lampada mista colori intelligente che, ruotando i 3 potenziometri con l'ESP32, permette di miscelare il colore desiderato! Man mano che si ruotano i potenziometri rosso, verde e blu, il LED RGB a catodo comune cambia colore istantaneamente. Sul display OLED SSD1306 vengono visualizzati in tempo reale i valori compresi tra 0 e 255 per ogni colore, le barre di riempimento visive e il codice colore HEX (#FF8040, ad esempio). Un mini studio di colore professionale: insegna la teoria dei colori R
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

