Misurare una tensione con l'ESP32 senza prendere un abbaglio
Un ingresso analogico da 12 bit sembra uno strumento di misura. Lo diventa solo dopo che si conoscono tre numeri: il fondo scala vero, la tensione di riferimento del singolo esemplare e il limite dei 3,3 volt.
La richiesta arriva sempre nella stessa forma: ho una batteria tampone che tiene in piedi l'allarme, o un piccolo impianto a pannello solare, e vorrei sapere a che tensione sta senza andare a vedere con il tester. In mezzo c'è un ESP32, la scheda da dieci euro con il Wi-Fi integrato, che ha un convertitore analogico-digitale da 12 bit. Due resistenze, quattro righe di codice, e il numero compare sul telefono.
Funziona. Il problema è che funziona anche quando è sbagliato di un volt, e un volt su una batteria al piombo è la differenza fra «carica» e «da sostituire». I modi di sbagliare sono sempre gli stessi tre, e sono tutti scritti nella documentazione del produttore.
Primo inganno: il fondo scala non è 3,3 V
Un convertitore analogico-digitale — ADC, analog to digital converter — confronta la tensione su un piedino con un riferimento interno e restituisce un numero intero proporzionale: «a 12 bit» significa fra 0 e 4095. Il malinteso sta nel dare per scontato che il fondo scala sia la tensione di alimentazione, e che quindi 4095 corrisponda a 3,3 V.
La documentazione ESP-IDF per l'ESP32 è esplicita: «Per design la tensione di riferimento dell'ADC è 1100 mV» e «gli ADC dell'ESP32 possono misurare tensioni analogiche da 0 V a Vref». Il convertitore, da solo, arriva a poco più di un volt: per leggere di più, davanti c'è un attenuatore configurabile su quattro posizioni.
| Attenuazione | Intervallo di ingresso misurabile |
|---|---|
ADC_ATTEN_DB_0 | 100 mV ~ 950 mV |
ADC_ATTEN_DB_2_5 | 100 mV ~ 1250 mV |
ADC_ATTEN_DB_6 | 150 mV ~ 1750 mV |
ADC_ATTEN_DB_12 | 150 mV ~ 2450 mV |
Due cose da leggere bene. Il tetto: con l'attenuazione massima si arriva a 2450 mV, non a 3300, e oltre quella soglia la lettura si appiattisce verso 4095 — un circuito in saturazione dà numeri stabili e credibili che non significano niente. E il pavimento: sotto 100-150 mV la misura non è attendibile, cosa che in un multimetro non succede.
C'è anche un'incoerenza da conoscere: la stessa impostazione è documentata con due numeri diversi. L'ADC API del core Arduino dà per ADC_11db «150 mV ~ 3100 mV», l'ESP-IDF per la stessa posizione dell'attenuatore, oggi chiamata ADC_ATTEN_DB_12, dà «150 mV ~ 2450 mV». Si progetta sul numero più basso.
Secondo inganno: il riferimento cambia da esemplare a esemplare
Quei 1100 mV sono un valore di progetto, non una garanzia. Testualmente: «la vera tensione di riferimento può variare da 1000 mV a 1200 mV fra ESP32 diversi» (the true reference voltage can range from 1000 mV to 1200 mV amongst different ESP32s). Venti per cento di escursione: la conversione scritta a mano, del tipo volt = conti * 2.45 / 4095, porta un errore che non si vede su una scheda e si vede sulla successiva.
Per questo esiste una taratura di fabbrica. Il driver di calibrazione dell'ESP-IDF lavora su due schemi — line fitting e curve fitting, sull'ESP32 il primo — usando i dati scritti nella eFuse del singolo chip, con l'avvertenza che «la disponibilità di questi valori di calibrazione dipenderà dal tipo e dalla data di produzione del chip o modulo ESP32». Sul core Arduino è già impacchettato, e la disciplina si riduce a una riga: non analogRead, che «restituisce il valore grezzo dell'ADC», ma analogReadMilliVolts, che «converte il valore grezzo in un risultato calibrato in millivolt».
Terzo inganno: ADC2 e il Wi-Fi
L'ESP32 ha due unità di conversione, e la seconda la usa anche la radio. Nella vecchia API «adc2_get_raw() può restare bloccata finché il Wi-Fi non si ferma, e viceversa»; nel driver attuale «adc_oneshot_read() fornisce una protezione fra il driver Wi-Fi e il driver ADC in modalità oneshot». La regola operativa non cambia: su una scheda che deve stare in rete, i canali analogici si prendono su ADC1.
Su ESP32 i canali di ADC1 stanno sui GPIO da 32 a 39, e sei di questi hanno una caratteristica che qui è un vantaggio: «i GPIO 34-39 possono essere impostati solo in modalità ingresso e non hanno funzioni di pull-up o pull-down abilitabili da software». Nessuna resistenza interna che falsi il partitore.
Il partitore: due resistenze e un conto
Il partitore risolve il problema del tetto: due resistenze in serie fra il punto da misurare e la massa, il piedino dell'ADC al punto intermedio. Per una batteria al piombo da 12 V nominali si progetta per un tetto di 16 V, perché in carica si arriva a 14,4 V e un alimentatore guasto va oltre. Con R1 = 100 kΩ verso il positivo e R2 = 18 kΩ verso massa il rapporto è 0,15254, cioè un fattore 6,5556 da applicare alla lettura:
| Tensione di batteria | Tensione sul piedino ADC |
|---|---|
| 9,0 V (scarica profonda) | 1373 mV |
| 12,0 V | 1830 mV |
| 13,8 V (mantenimento) | 2105 mV |
| 15,0 V | 2288 mV |
| 16,0 V | 2441 mV |
A 16 V si è a nove millivolt dal tetto: il dimensionamento è giusto e non c'è margine da regalare. Nel partitore scorrono 127 µA a 15 V, circa 1,6 mW in permanenza: trascurabili su una batteria da 7 Ah, non su una scheda in sonno profondo per mesi, dove il partitore va acceso con un transistor solo durante la misura.
I collegamenti, pin per pin, descritti a parole perché qui non ci sono figure. Il positivo della batteria va a un capo di R1 passando per un fusibile da 100 mA o meno: è l'unica protezione che hai se una resistenza si apre o un filo tocca dove non deve. L'altro capo di R1 va al nodo comune, da cui parte R2 verso la massa della batteria; dallo stesso nodo parte il filo per il GPIO34. La massa della batteria e il GND della scheda devono essere lo stesso punto, altrimenti non esiste un riferimento rispetto a cui misurare. Fra GPIO34 e GND, vicino al piedino, va un condensatore ceramico da 100 nF: la documentazione raccomanda «un condensatore di bypass (per esempio un ceramico da 100 nF) al piedino di ingresso ADC in uso, per minimizzare il rumore», più il campionamento multiplo.
La risoluzione non è il problema: un conteggio vale 0,6 mV sul piedino, meno di 4 mV di batteria. Il problema è la tolleranza delle resistenze: al 5% il rapporto può sbagliare fino a 1,05 V su una lettura di 12 V, all'1% si scende a 0,21 V. Nessuna calibrazione dell'ADC recupera quell'errore, perché non è nell'ADC. Si recupera misurando la batteria con un multimetro e mettendo nello sketch un fattore di correzione pari al rapporto fra valore vero e valore letto.
Lo sketch
Codice completo per il core Arduino, nessuna libreria aggiuntiva: legge 32 campioni, ne fa la media e segnala il fuori scala invece di stampare un numero inventato.
// Monitor tensione batteria 12 V - ESP32
// Partitore 100 kohm / 18 kohm su GPIO34 (ADC1_CH6), 100 nF verso GND
const int PIN_BATTERIA = 34; // solo ADC1: ADC2 va in conflitto con il Wi-Fi
const float R1 = 100000.0; // verso il positivo della batteria
const float R2 = 18000.0; // verso massa
const float FATTORE = (R1 + R2) / R2; // 6,5556
const float CORREZIONE = 1.0000; // da tarare: V_multimetro / V_letto
const int CAMPIONI = 32;
void setup() {
Serial.begin(115200);
analogReadResolution(12); // 0..4095
analogSetPinAttenuation(PIN_BATTERIA, ADC_11db); // attenuazione massima
}
float leggiMilliVolt(int pin, int n) {
uint32_t somma = 0;
for (int i = 0; i < n; i++) {
somma += analogReadMilliVolts(pin); // valore calibrato, non grezzo
delay(2);
}
return (float)somma / n;
}
void loop() {
float mv = leggiMilliVolt(PIN_BATTERIA, CAMPIONI);
float vbatt = mv / 1000.0 * FATTORE * CORREZIONE;
if (mv < 150.0) {
Serial.println("fuori scala in basso: sotto 150 mV la lettura non e' attendibile");
} else if (mv > 2400.0) {
Serial.println("fuori scala in alto: oltre 2450 mV l'ADC satura");
} else {
Serial.printf("ADC %6.1f mV -> batteria %5.2f V\n", mv, vbatt);
}
delay(2000);
}
Che cosa è stato provato e che cosa no. Questo articolo è stato scritto in un ambiente senza hardware: lo sketch non è stato caricato su una scheda e il circuito non è stato montato. La parte aritmetica — il fattore 6,5556 e la conversione da millivolt a volt — è stata trascritta in C, compilata con gcc -Wall -Wextra senza avvisi ed eseguita con letture simulate: con 1830 mV sul piedino stampa «batteria 12.00 V». Nomi di funzione, costanti, intervalli e limiti vengono dalle pagine Espressif elencate in fondo, lette oggi.
Questo schema misura una tensione continua riferita alla stessa massa della scheda. Non va usato sulla rete elettrica: 230 V alternata non si misurano con un partitore resistivo collegato a una scheda a cui si attacca un cavo USB, perché non c'è isolamento fra il piedino e la mano di chi programma; servono moduli isolati certificati. Sulle batterie al litio il fusibile in serie non è opzionale, una cella forata o in cortocircuito può incendiarsi, e il pacco non va gestito con elettronica improvvisata al posto del suo BMS. Se la misura comanda un relè su un carico induttivo — pompa, motore, contattore — fra misura e potenza ci va un optoisolatore o un modulo relè con fotoaccoppiatore, più il diodo di ricircolo sulla bobina. E la regola senza eccezioni: un segnale a 5 V non si collega mai direttamente a un ingresso a 3,3 V. Il partitore descritto qui è l'adattamento di livello che serve, ma solo entro il tetto per cui è stato dimensionato.
Cosa puoi fare
Se hai una scheda nel cassetto e un'ora di tempo, parti dal tetto e non dalle resistenze: decidi la tensione massima che il circuito deve sopportare e dimensiona il partitore perché a quella tensione il piedino stia sotto 2450 mV. Prendi resistenze all'1%: per qualche centesimo eliminano la fonte di errore dominante. Usa GPIO34 o un altro piedino di ADC1, metti il condensatore da 100 nF, chiama analogReadMilliVolts e fai la media di qualche decina di campioni. Poi taratura con il multimetro, e fattore di correzione annotato sulla scheda con un pennarello.
Se invece da quella misura dipende una decisione — l'allarme che fa partire un intervento, la soglia che stacca un carico — la domanda da farsi prima è quanto errore sia accettabile. Un ADC integrato con un partitore, tarato bene e a temperatura costante, sta ragionevolmente entro qualche decina di millivolt: sufficiente per dire «la batteria è scesa sotto 11,8 V». Non è uno strumento di misura tarato e non lo diventa a forza di software: se il numero deve finire in un rapporto, in una perizia o in una contestazione a un fornitore, serve un convertitore esterno con riferimento proprio, o uno strumento con certificato di taratura.
Fonti
Da leggere
Articoli correlati
Aprire un file sospetto senza sporcare il PC: Windows Sandbox configurata a mano
Windows Sandbox apre una copia pulita di Windows che alla chiusura svanisce. Serve per provare un installatore o un allegato di dubbia provenienza, ma con le impostazioni di partenza la rete e gli appunti restano collegati al PC. Come si scrive il file .wsb che chiude tutto, e cosa la sandbox non fa.
Leggi l'articolo →Verificare che una cartella non sia cambiata: un manifest di impronte in Python
La data di modifica di un file si riscrive in una riga di codice, e la dimensione non cambia se dentro una fattura 1.250,00 diventa 1.950,00. L'unico controllo che tiene è il contenuto: uno script Python di sola libreria standard che calcola le impronte SHA-256 di una cartella, le registra in un file JSON e le riverifica. Scritto, eseguito e misurato.
Leggi l'articolo →Verificare che una cartella non sia cambiata: un manifest di impronte in Python
La data di modifica di un file si riscrive in una riga di codice, e la dimensione non cambia se dentro una fattura 1.250,00 diventa 1.950,00. L'unico controllo che tiene è il contenuto: uno script Python di sola libreria standard che calcola le impronte SHA-256 di una cartella, le registra in un file JSON e le riverifica. Scritto, eseguito e misurato.
Leggi l'articolo →