Archivi tag: analogRead

Domande dagli utenti – Perché Arduino legge valori strani se il pin non è collegato?

Ho deciso di mettere maggiormente in evidenza le richieste di chiarimento su diversi argomenti tecnici che mi giungono via mail o le domande lasciate nei commenti ai miei post. Spesso questi interventi nascono da dubbi autentici e molto concreti, ma rischiano di perdersi tra i tanti contenuti pubblicati. Per questo motivo ho pensato che alcune di queste domande meritino di essere riprese e trasformate in risposte più ampie, dettagliate e utili anche per altri lettori.

Molto spesso a scrivermi sono studenti giovanissimi, che stanno iniziando il loro percorso nell’elettronica, nella programmazione o nell’uso di Arduino. Ed è proprio a loro che sento di dover dedicare un’attenzione particolare, perché dietro una domanda apparentemente semplice si nasconde quasi sempre un passaggio importante da chiarire. Sono convinto, infatti, che proprio dalle domande più essenziali si costruiscano le basi migliori per capire davvero.

Molti, quando mi scrivono, aggiungono frasi come:

“Mi scusi per la domanda forse banale…”

oppure

“Preferisco scriverle in privato perché non vorrei fare una figura da inesperto nei commenti”

Su questo vorrei essere molto chiaro, perché per me è un aspetto importante, sia come docente sia come autore di contenuti didattici: non esiste la domanda sciocca, esiste solo la domanda non fatta.

Anzi, molto spesso le domande più utili sono proprio quelle che sembrano più elementari, perché costringono a chiarire i concetti fondamentali. E senza fondamenta solide, anche gli argomenti più avanzati restano fragili.

Una delle domande che mi è arrivata di recente è questa:

“Prof, perché Arduino mi legge valori strani anche se non ho collegato niente al pin?”

È una domanda ottima ed è anche una domanda che può aiutare molte persone che iniziano a usare Arduino, l’elettronica o i sensori.

Risposta

Perché un pin lasciato scollegato non è in uno stato definito.

Se un ingresso non è collegato né al positivo né alla massa, il microcontrollore non possiede un riferimento elettrico chiaro. In questa situazione il pin può captare piccoli disturbi presenti nell’ambiente circostante e restituire valori instabili, apparentemente casuali.

Quindi, nella maggior parte dei casi, non c’è nessun guasto nella scheda.
Il pin sta semplicemente lavorando in una condizione detta flottante.

Che cosa significa “pin flottante”

Quando si comincia a usare Arduino, è facile immaginare che un pin non collegato equivalga a “zero” oppure a “nessun segnale”.

In realtà non funziona così.

Un ingresso lasciato libero può comportarsi un po’ come una piccola antenna: intercetta disturbi elettrici, rumori, influenze del circuito circostante e può quindi produrre letture variabili anche se noi non stiamo toccando nulla.

Questo significa che:

  • un ingresso digitale può passare casualmente tra HIGH e LOW;
  • un ingresso analogico può mostrare valori che cambiano continuamente nel monitor seriale.

È un comportamento molto istruttivo, perché mostra subito una differenza importante tra lo schema ideale disegnato sulla carta e il comportamento reale dei circuiti.

L’idea fondamentale da ricordare

Il concetto chiave è questo:

in elettronica, “non collegato” non significa “zero”.

Questo è uno di quei principi che ritornano continuamente quando si lavora con pulsanti, sensori, interruttori, linee di ingresso e acquisizioni analogiche.

Capirlo bene fin dall’inizio evita molti errori e, soprattutto, aiuta a ragionare in modo più corretto sul funzionamento dei circuiti.

Come si risolve

Per evitare che un ingresso fluttui, bisogna assegnargli uno stato elettrico preciso.

Di solito si usa una di queste soluzioni:

  • una resistenza di pull-down, che mantiene il pin a livello basso quando non arriva alcun segnale;
  • una resistenza di pull-up, che mantiene il pin a livello alto;
  • la modalità INPUT_PULLUP di Arduino, molto comoda per gli ingressi digitali, perché sfrutta una resistenza interna del microcontrollore.

È una delle prime configurazioni corrette da imparare, soprattutto quando si cominciano a collegare pulsanti.

Un piccolo esperimento

Per osservare direttamente il fenomeno si può fare una prova semplicissima.

Si collega Arduino al computer, si carica uno sketch che legge un ingresso analogico lasciato libero, e si osservano i valori nel monitor seriale.

Esempio di sketch: lettura di un pin analogico flottante

/*
  Lettura di un ingresso analogico lasciato scollegato
  per mostrare il comportamento di un pin flottante.

  Collegamenti:
  - Non collegare nulla al pin A0
  - Aprire il Monitor Seriale a 9600 baud

  Osservazione attesa:
  I valori letti non saranno stabili, ma varieranno nel tempo.
*/

const int pinAnalogico = A0;   // Pin analogico lasciato volutamente scollegato

void setup() {
  Serial.begin(9600);
  delay(1000); // Piccola attesa per dare tempo al monitor seriale di aprirsi

  Serial.println("Lettura di un pin analogico flottante");
  Serial.println("Il pin A0 non e' collegato a nulla.");
  Serial.println("Osserva i valori letti:");
}

void loop() {
  int valoreLetto = analogRead(pinAnalogico);  // Legge il valore presente su A0

  Serial.print("Valore letto: ");
  Serial.println(valoreLetto);

  delay(200); // Solo per rallentare la visualizzazione
}

Che cosa si osserva

Aprendo il monitor seriale, i valori non resteranno fermi.
Potranno cambiare anche senza alcun intervento esterno.

A seconda della scheda, dell’ambiente, dei cavi vicini, della posizione delle mani e del rumore elettrico circostante, potresti vedere numeri che oscillano in modo più o meno evidente.

Ed è proprio questo il punto interessante: il pin non è “a zero”, ma è in una condizione indefinita.

Una seconda prova ancora più istruttiva

Dopo aver osservato il comportamento flottante, potete ripetere l’esperimento collegando:

  • una resistenza di pull-down verso GND;
  • oppure un partitore o un riferimento fisso;
  • oppure, nel caso di un ingresso digitale, attivando INPUT_PULLUP.

Il confronto tra “prima” e “dopo” è didatticamente molto efficace, perché fa vedere in modo concreto cosa significa stabilizzare un ingresso.

Variante digitale

Lo stesso fenomeno si può osservare anche con un ingresso digitale lasciato libero.

/*
  Lettura di un ingresso digitale flottante.

  Collegamenti:
  - Non collegare nulla al pin 2
  - Aprire il Monitor Seriale a 9600 baud

  Osservazione attesa:
  Il pin potra' cambiare in modo imprevedibile tra HIGH e LOW.
*/

const int pinDigitale = 2;   // Pin digitale lasciato scollegato

void setup() {
  Serial.begin(9600);
  pinMode(pinDigitale, INPUT);  // INPUT semplice: nessuna pull-up interna attiva

  delay(1000);

  Serial.println("Lettura di un pin digitale flottante");
  Serial.println("Il pin 2 non e' collegato a nulla.");
}

void loop() {
  int statoPin = digitalRead(pinDigitale);

  Serial.print("Stato letto: ");
  if (statoPin == HIGH) {
    Serial.println("HIGH");
  } else {
    Serial.println("LOW");
  }

  delay(200);
}

In questo caso il risultato potrà essere meno “spettacolare” oppure più irregolare, ma il principio resta identico: senza riferimento, l’ingresso non è affidabile.

Una domanda come questa può sembrare piccola, ma in realtà apre la porta a uno dei concetti più importanti dell’elettronica di base:

ogni ingresso deve avere un riferimento chiaro.

Capire bene questo passaggio significa costruire basi più solide per affrontare tutto il resto: pulsanti, sensori, automazione, misure, controllo e programmazione embedded.

E quindi, come dico sempre a chi mi scrive in privato:
fate domande, SEMPRE.

Perché una domanda sincera, anche molto semplice, vale molto più di un silenzio pieno di dubbi.

Piccola proposta pratica per il laboratorio

Chi vuole può provare questa sequenza:

  1. caricare lo sketch con A0 scollegato;
  2. osservare i valori nel monitor seriale;
  3. toccare con un dito il filo collegato ad A0 o avvicinare la mano;
  4. osservare come cambiano i valori;
  5. collegare poi il pin a GND oppure a 5 V tramite una configurazione corretta;
  6. confrontare il comportamento.

È una prova molto semplice, ma estremamente utile per capire, come dicevo ad inizio post, che in elettronica anche ciò che sembra “non collegato” può avere effetti reali.

Buona esplorazione 🙂

Esercitazione 3 – Scheduler cooperativo con tre task e supervisione dei tempi

Allenamento per l’esame di maturità
Percorso di laboratorio con Arduino per studenti di quinta ITIS

Obiettivo didattico

Organizzare il programma come scheduler cooperativo con tre task indipendenti: acquisizione analogica, lampeggio di stato e trasmissione seriale periodica. L’attività mostra come il loop() possa diventare un piccolo supervisore software.

Materiali suggeriti

  • Arduino UNO R3 o UNO R4;
  • 1 potenziometro;
  • 2 LED;
  • 2 resitori (per i LED);
  • breadboard;
  • cavetti jumper.

Schema di collegamento

Richiamo teorico

In un sistema embedded semplice non si usa un vero sistema operativo, ma si può costruire uno scheduler cooperativo con millis(). Ogni task possiede il proprio intervallo e il proprio istante dell’ultima esecuzione. Il loop() controlla se ciascun task è pronto e lo richiama.

Schema logico dell’attività

Il programma inizializza i timer dei tre task. Nel loop legge il tempo corrente e verifica uno dopo l’altro se è il momento di eseguire il task di acquisizione, quello di segnalazione e quello di stampa seriale. Se il tempo non è scaduto, passa al controllo successivo.

Diagramma di flusso

Diagramma di flusso Mermaid

flowchart TD
    A[Inizio] --> B[Configura pin, seriale e timer]
    B --> C[Leggi tempo attuale]
    C --> D{Task acquisizione pronto?}
    D -- Sì --> E[Esegui acquisizione]
    D -- No --> F{Task LED pronto?}
    E --> F
    F -- Sì --> G[Commuta LED di stato]
    F -- No --> H{Task seriale pronto?}
    G --> H
    H -- Sì --> I[Invia dati in seriale]
    H -- No --> J[Ritorna al loop]
    I --> J
    J --> C

Programma

/*
  Prof. Maffucci Michele
  Esercizio 3: scheduler cooperativo con tre task indipendenti
*/

const int PIN_SENSORE = A0;
const int PIN_LED_STATO = 8;
const int PIN_LED_SOGLIA = 9;

// ---------------------------
// Intervalli dei tre task
// ---------------------------
const unsigned long PERIODO_ACQUISIZIONE = 50;
const unsigned long PERIODO_LED = 300;
const unsigned long PERIODO_SERIALE = 500;

// ---------------------------
// Istanti ultima esecuzione
// ---------------------------
unsigned long ultimoTaskAcquisizione = 0;
unsigned long ultimoTaskLed = 0;
unsigned long ultimoTaskSeriale = 0;

// ---------------------------
// Variabili condivise
// ---------------------------
int valoreGrezzo = 0;
float tensione = 0.0;
bool statoLed = false;

void setup() {
  pinMode(PIN_LED_STATO, OUTPUT);
  pinMode(PIN_LED_SOGLIA, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  // Il loop assume il ruolo di piccolo scheduler.
  unsigned long adesso = millis();

  // ---------------------------
  // Task 1: acquisizione
  // ---------------------------
  if ((adesso - ultimoTaskAcquisizione) >= PERIODO_ACQUISIZIONE) {
    ultimoTaskAcquisizione = adesso;
    taskAcquisizione();
  }

  // ---------------------------
  // Task 2: LED di stato
  // ---------------------------
  if ((adesso - ultimoTaskLed) >= PERIODO_LED) {
    ultimoTaskLed = adesso;
    taskLed();
  }

  // ---------------------------
  // Task 3: seriale
  // ---------------------------
  if ((adesso - ultimoTaskSeriale) >= PERIODO_SERIALE) {
    ultimoTaskSeriale = adesso;
    taskSeriale();
  }
}

// ----------------------------------------------------------
// Legge il sensore e calcola una tensione equivalente.
// ----------------------------------------------------------
void taskAcquisizione() {
  valoreGrezzo = analogRead(PIN_SENSORE);

  // Conversione esplicita ADC -> tensione.
  tensione = (valoreGrezzo * 5.0) / 1023.0;

  // Uso della misura per attivare un LED di soglia.
  if (tensione >= 2.50) {
    digitalWrite(PIN_LED_SOGLIA, HIGH);
  } else {
    digitalWrite(PIN_LED_SOGLIA, LOW);
  }
}

// ----------------------------------------------------------
// Task di segnalazione periodica.
// ----------------------------------------------------------
void taskLed() {
  if (statoLed == false) {
    statoLed = true;
    digitalWrite(PIN_LED_STATO, HIGH);
  } else {
    statoLed = false;
    digitalWrite(PIN_LED_STATO, LOW);
  }
}

// ----------------------------------------------------------
// Task di comunicazione seriale.
// ----------------------------------------------------------
void taskSeriale() {
  Serial.print("ADC = ");
  Serial.print(valoreGrezzo);
  Serial.print("  Tensione = ");
  Serial.print(tensione, 2);
  Serial.println(" V");
}

Continua a leggere

randomSeed() su Arduino come usarla

In riferimento al precedente post: “Come ottenere più valori da una funzione in Arduino: variabili globali, riferimenti e struct – lezione 1/2” un utente mi ha scritto in privato per chiedermi chiarimenti in merito all’uso di randomSeed(), nello specifico di randomSeed(analogRead(A0)). La spiegazione che ho fornito è diventata alla fine qualcosa che posso riutilizzare con i miei studenti quindi ne ho realizzato un post sul sito in modo che possa essere letto da tutti.

Quando in uno sketch usate random(), state usando un generatore pseudo-casuale: i numeri “sembrano” casuali, ma in realtà seguono una sequenza deterministica. Se riavviate la scheda e non fate nulla, spesso la sequenza riparte uguale. Per evitare questo comportamento (molto evidente nei giochi, nelle simulazioni e nelle attività di laboratorio), Arduino mette a disposizione randomSeed(), che inizializza il punto di partenza della sequenza.

Sintetizzo cosa fa nel dettaglio randomSeed()

  • random() genera numeri pseudo-casuali.
  • randomSeed(seed) imposta il seme (seed), cioè il valore da cui parte la sequenza.

questo vuol dire che:

  • Seed uguale > sequenza uguale.
  • Seed diverso > sequenza diversa.

L’effetto di randomSeed(analogRead(A0)) utilizzato nella lezione precedente è duplice:

  1. legge un valore analogico da A0 con analogRead(A0)
  2. usa quel valore come seme per randomSeed(...)

L’idea (consigliata anche nella documentazione Arduino) è: se A0 è scollegato, la lettura è influenzata dal “rumore” elettrico ambientale e tende a variare; quindi anche il seed cambia e, a ogni reset, random() produce una sequenza diversa.

Però attenzione, un pin “floating” non garantisce casualità perfetta; spesso fornisce poca entropia, ma per scopi didattici (giochi, simulazioni, esercizi) è in genere sufficiente. Ovviamente non è una tecnica adatta a sicurezza/criptografia.

Precisazione: differenze tra UNO R3 e UNO R4

  • Arduino UNO R3: analogRead() è a 10 bit: valori 0–1023.
  • Arduino UNO R4: per compatibilità, analogRead() parte di default a 10 bit (0–1023), ma l’UNO R4 supporta risoluzioni più alte (fino a 14 bit) tramite analogReadResolution().

Questa informazioni può essere utile anche per il seed: più risoluzione vuol dire più livelli possibili nella lettura analogica, quindi (potenzialmente) seed più variabile.

Di seguito alcune proposte di attività da svolgere a scuola o per esercitarsi.
Tutti gli esempi stampano su Serial Monitor. Usate 9600 baud e, quando richiesto, lasciate A0 scollegato.

Attività 01: serie che si ripete – (senza randomSeed)

Obiettivo: vedere che, senza inizializzazione, la sequenza tende a ripetersi dopo un reset
Consegna: fate partire lo sketch, annotate i primi 10 numeri, premete reset e confrontate.

/*
  Prof. Maffucci Michele
  14.02.26
  Attività 01: serie che si ripete - (senza randomSeed)
*/

void setup() {
  Serial.begin(9600);
}

void loop() {
  Serial.println("Primi 10 numeri pseudo-casuali (senza seed):");
  for (int i = 0; i < 10; i++) {
    Serial.println(random(0, 100));
  }
  Serial.println("--- Premi reset della scheda e confronta ---\n");
  delay(3000);
}

Attività 02: Seed fisso = sequenza ripetibile (utile nei test)

Obiettivo: capire che un seed costante produce sempre la stessa sequenza (utile per il debug).
Consegna: verificare che a ogni reset i numeri restano identici.

/*
  Prof. Maffucci Michele
  14.02.26
  Attività 02: capire che un seed costante produce sempre la stessa sequenza (utile per il debug)
*/

void setup() {
  Serial.begin(9600);
  randomSeed(12345); // seed fisso
}

void loop() {
  Serial.println("Primi 10 numeri con seed fisso (12345):");
  for (int i = 0; i < 10; i++) {
    Serial.println(random(0, 100));
  }
  Serial.println("--- Premi reset: deve ripetersi uguale ---\n");
  delay(3000);
}

Attività 03: Seed “rumoroso” con analogRead(A0) (A0 scollegato)

Obiettivo: ottenere sequenze diverse a ogni reset usando randomSeed(analogRead(A0)).
Consegna:

  1. eseguite con A0 scollegato > osservate seed e sequenza
  2. collega A0 a GND o 5V > osservate come seed e sequenza diventano più “stabili” (meno variabili)
/*
  Prof. Maffucci Michele
  14.02.26
  Attività 03: ottenere sequenze diverse a ogni reset usando randomSeed(analogRead(A0))
*/

void setup() {
  Serial.begin(9600);

  // A0 deve essere scollegato per sfruttare la variabilità del rumore
  int seme = analogRead(A0);
  randomSeed(seme);

  Serial.print("Seed letto da A0: ");
  Serial.println(seme);
  Serial.println();
}

void loop() {
  Serial.println("Primi 10 numeri con seed da A0:");
  for (int i = 0; i < 10; i++) {
    Serial.println(random(0, 100));
  }
  Serial.println("--- Premi reset: dovrebbero cambiare ---\n");
  delay(3000);
}

Attività 04: utilizzo di UNO R4 per avere seed più “ricco” aumentando la risoluzione (12/14 bit)

Obiettivo: su UNO R4 aumentare la risoluzione ADC e “mescolare” più letture per un seed più variabile.
Consegna:

  • fate 5 reset su UNO R3 e 5 reset su UNO R4, annotate i seed e valutate quanto cambiano
  • su UNO R4 provate anche analogReadResolution(12) e confronta con 14

Lo sketch che segue viene compilato anche su UNO R3: la parte analogReadResolution() viene attivata solo su UNO R4.

/*
  Prof. Maffucci Michele
  14.02.26
  Attività 04: utilizzo di UNO R4 per avere seed più "ricco" aumentando la risoluzione (12/14 bit)
*/

unsigned long creaSeedDaRumoreA0() {
  unsigned long seed = 0;

  // Su UNO R4 puoi aumentare la risoluzione (10 bit default, fino a 14 bit)
#if defined(ARDUINO_UNOR4_MINIMA) || defined(ARDUINO_UNOR4_WIFI)
  analogReadResolution(14);
#endif

  // Mescolo 32 letture per raccogliere più variabilità
  for (int i = 0; i < 32; i++) {
    seed = (seed << 1) ^ (unsigned long)analogRead(A0);
    delay(2);
  }
  return seed;
}

void setup() {
  Serial.begin(9600);

  Serial.println("Seed avanzato da A0 (A0 scollegato):");
  unsigned long seed = creaSeedDaRumoreA0();
  Serial.print("Seed calcolato: ");
  Serial.println(seed);

  randomSeed(seed);
  Serial.println();
}

void loop() {
  Serial.println("Primi 10 numeri con seed avanzato:");
  for (int i = 0; i < 10; i++) {
    Serial.println(random(0, 100));
  }
  Serial.println("--- Reset e confronta ---\n");
  delay(3000);
}

Ora vi propongo un piccolo progetto, il classico dado, che sicuramente riuscirete a svolgere velocemente.

Esercizio: “dado elettronico”

Obiettivo: usare random() in un contesto concreto e verificare che con randomSeed i lanci non siano sempre uguali dopo reset.

Vi lascio qualche giorno per provare a sviluppare una soluzione e poi ve ne propongo una io. 🙂

Terminiamo questa lezione riassumendo quanto visto:

  • random() = pseudo-casuale (sequenza deterministica);
  • randomSeed() sposta il punto di partenza della sequenza;
  • randomSeed(analogRead(A0)) funziona solo se A0 varia davvero (meglio scollegato o con una sorgente “rumorosa”);
  • UNO R4 può aumentare la risoluzione ADC con analogReadResolution() (default 10 bit per compatibilità).

Buon Coding a tutti 🙂

Arduino – misurare tensioni superiori a 5V dc utilizzando un partitore di tensione

Nella precedente lezione abbiamo visto come misurare una tensione non superiore ai 5V dc, vedremo ora come realizzare un semplice circuito che permette mediante un partitore di tensione la misurazione di tensioni fino a 9 Volt. Dimensionando opportunamente le resistenze di partizione potremo effettuare misure per tensioni elettriche superiori.

Precisazione importante

Gli esempi riportati in questa e nella precedente lezione ed inoltre la gran parte degli esempio classici che trovate online, sono pensati per far comprendere il funzionamento del convertitore A/D, ma è opportuno fare alcune considerazioni sulla precisione di lettura, ovvero degli errori commessi dal convertitore A/D e sull’imprecisione delle tensioni di riferimento usate dal microcontrollore per fornirvi la misura. Questo sarà argomento di una prossima lezione e vedremo come ridurre l’errore di misura.

Per rendere semplice la realizzazione utilizzeremo la batteria da 9V, ma se intendete avere dei limiti di misura diversi e superiori a 9V dovrete apportare modifiche ai valori dei componenti, ma le formule esposte restano le medesime.

Prima di procedere con la realizzazione pratica è essenziale comprendere come dimensionare le resistenze del partitore, inoltre è essenziale che la tensione sul pin analogico A0 non superi il valore di 5V. Per effettuare il dimensionamento del circuito fissiamo la massima tensione misurabile Vmis_max  a 9V e imponiamo il valore di uno dei due resistori, ad esempio R1 in modo da poter ricavare il valore di R2.

Vi ricordo inoltre che sarebbe opportuno, una volta fissate le resistenze, fare qualche considerazione sulla corrente massima entrante in A0 in modo che non venga superato il valore di 40 mA corrente massima di I/O sui pin analogici e digitali di Arduino Uno R3, ma come le resistenze che sono state scelte non correremo alcun rischio, però esporrò ugualmente il calcolo.

Nello schema che segue Vmis rappresenta la tensione da misurare, Vadc la tensione sul pin A0. I è la corrente totale che circola nel circuito.

Sappiamo che la tensione Vadc ai capi di R2 non dovrà superare i 5V. Le tensioni su R1 ed R2 saranno:

(a) VR1 = R1*I
(b) VR2 = R2*I

Pertanto la tensione Vmis sarà

(c) Vmis = VR1 + VR2 = R1*I+R2*I = (R1+R2)*I

da cui ricaviamo:

(d) I = Vmis /(R1+R2)

Sostituendo I in (a) e (b) avremo:

(e) VR1 = R1*I = R1 * Vmis /(R1+R2)
(f) VR2 = R2*I = R2 * Vmis /(R1+R2)

Per il calcolo di R2 consideriamo la formula (f) sostituendo i valori fissati, Vmis_max e R1, ricordando che VR2 è la tensioni in A0, cioè Vadc:

⇒ VR2 =  R2 * Vmis /(R1+R2)

⇒ R2 /(R1+R2)  = VR2/Vmis_max

⇒ R2 /(R1+R2)  = 5/9

⇒ R2 /(R1+R2)  = 0,56

⇒ R2  = 0,56*R1 + 0,56*R2

⇒ R2*(1-0,56) = 0,56*R1

⇒ R2 = 0,56 *R1/0,44

⇒ R2 = 0,56 * 27 * 103/0,44 = 34,363 * 103 = 34,363 Kohm

Scelgo come valore commerciale prossimo (e che dispongo nel mie scorte) il valore di 33 Kohm, quindi:

  • R1 = 27 Kohm
  • R2 = 33 Kohm

Per quanto riguarda la corrente entrante in A0, utilizziamo la formula (b):

VR2 = R2*I

I = VR2/R2

I = 5V/33000 Ω = 0,00015 A = 0,15 mA

ben al di sotto del valore massimo del valore accettabile su un pin I/O di Arduino che è di 40mA.

Schema di collegamento

Programmazione

Sviluppiamo il codice necessario per la lettura della tensione e partendo dal primo sketch della lezione precedente modifichiamone alcune parti, nei commenti la spiegazione delle varie parti.

Nel codice bisognerà tenere in conto che la tensione su A0 è data dal calcolo della partizione di tensione, pertanto sapendo che Vmis = Vadc, dalla forma (f) abbiamo:

⇒ VR2 = R2*I = R2 * Vmis /(R1+R2)

⇒ VR2 = Vadc = R2 * Vmis /(R1+R2)

⇒ Vmis = Vadc * (R1+R2)/R2

Che sarà la formula che ci consentirà di rilevare la misura.

// Prof. Maffucci Michele
// Esempio 01: Misura una tensione di 9V con Arduino
// utilizzando variabili di tipo float
// Data: 03.10.2021

// tensione di riferimento massima misurabile
const float tensioneDiRiferimento = 5.0;

float R1 = 27000.0; // 27 Kohm
float R2 = 33000.0; // 33 Kohm

float volt_adc = 0.0;
float volt_mis = 0.0;

void setup() {
  Serial.begin(9600);
  //analogReference(DEFAULT);
}
void loop() {
  // legge il valore su A0 (su R2), cioè la tensione Vadc e lo trasforma
  // in un valore numerico tra 0 e 1023

  int valoreLetto = analogRead(A0);

  // Tensione in ingresso ad A0, cioè la tensione Vadc
  // calcolo della proporzione
  // volt:tensioneDiRiferimento = valoreLetto:1023.0
  // da cui si ricava la formula che segue

  volt_adc = (valoreLetto/1023.0) * tensioneDiRiferimento;

  //Stampa del valore letto dall'ADC
  Serial.print("Valore ADC = ");
  Serial.println(valoreLetto);

  // calcolo della tensione di ingresso Vmis

  volt_mis = volt_adc*(R1+R2)/R2;

  // stampa sulla Serial Monitor la tensione misurata
  Serial.print("Tensione di ingresso = ");
  Serial.println(volt_mis);
  Serial.println(" ");

  delay(1000);
}

Esercizi per i miei studenti

Esercizio 1
Dimensionare il Circuito per misurare una tensione massima di 12V

Esercizio 2
Realizzare uno sketch Arduino che permette di dimensionare il circuito prendendo in  input, attraverso la Serial Monitor il valore massimo misurabile ed il valore di R1 e restituisce il valore di R2 calcolato.

Esercizio 3
Realizzare le medesime funzionalità dell’esercizio 2, ma il valore restituito di R2 deve essere sia quello calcolato che quello commerciale immediatamente superiore o inferiore al valore calcolato.

Buon Coding a tutti 🙂

Arduino – misurare tensioni continue fino a 5V

La misurazione di una tensione con Arduino è un esercizio standard utilizzato per spiegare come funziona il convertitore Analogico Digitale sulla scheda. Nell’esempio che segue verrà mostrato come visualizzare sulla Serial Monitor la tensione di una batterie da 1,5V ma in generale di una tensione non superiore ai 5V, seguono poi una serie di esercizi per i miei studenti.
Nella prossima lezione mostreremo come misurare una tensione superiore ai 5V.

ATTENZIONE
E’ essenziale ricordare che non bisogna in alcun modo inserire tensioni superiori a 5V sui pin di Arduino, in quanto potreste distruggere irrimediabilmente la scheda.

Per la rilevazione di una tensione elettrica su un pin analogico viene utilizzata la funzione analogRead, che come più volte indicato su questo sito, è in grado di convertire una tensione tra 0V e 5V continui in un valore numerico intero con risoluzione di 10 bit tra 0 e 1023, operazione svolta dal convertitore A/D.

Ricordo che l’analogRead(pin) legge il valore di tensione (compreso tra 0 e 5V) applicato sul piedino analogico ‘pin’ con una risoluzione di 10 bit e la converte in un valore numerico compreso tra 0 e 1023, corrispondente quindi ad un intervallo di 1024 valori, pertanto il valore unitario corrisponde a:

Vu = 5V/1023 = 4,89 mV

Per conoscere il valore di tensione rilevato sarà sufficiente moltiplicare la tensione unitaria Vu per il valore restituito dalla funzione analogRead(pin), quello che chiamiamo valore quantizzato Vq compreso tra 0 e 1024, il valore misurato Vm sarà:

Vm = Vu x Vq

e sapendo che Vu corrisponde a 4,89 mV possiamo scrivere:

Vm = 4,89 x Vq

Nota per i miei allievi: riprenderemo questo semplice calcolo qundo utilizzeremo ad esempio i sensori di temperatura.

Il semplice schema di collegamento è riportato nell’immagine che segue:

Per stampare sulla Serial Monitor la tensione ai capi della batteria, useremo valori di tipo float (in virgola mobile), che come indicato nel mio post: “Arduino: tipi di dati – ripasso” possiamo esprime valori compresi tra –3.4028235E+38 e 3.4028235E+38.
La stampa di numeri in virgola mobile sula Serial Monitor verrà rappresentata con numeri che hanno al massimo con due cifre decimali, riprenderemo questo aspetto nel secondo esempio di questa lezione.

// Prof. Maffucci Michele
// Esempio 01: Misura di tensioni continue non superiori a 5V
// utilizzando variabili di tipo float
// Data: 01.10.2021

// tensione di riferimento predefinita sulla scheda
const float tensioneDiRiferimento = 5.0;

// batteria connessa al pin analogico 0
const byte pinBatteria = A0;

void setup() {
  // inizializzazione della porta seriale
  Serial.begin(9600);
}

void loop() {
  // legge il valore della batteria e lo trasforma
  // in un valore numerico tra 0 e 1023
  int valoreLetto = analogRead(pinBatteria);

  // calcolo della proporzione
  // volt:tensioneDiRiferimento = valoreLetto:1023.0
  // da cui si ricava la formula che segue
  float volt = (valoreLetto/1023.0) * tensioneDiRiferimento;

  // stampa sulla Serial Monitor la tensione misurata
  Serial.println(volt);
}

Per evitare spreco di memoria dovuto ai calcoli che utilizzano i tipi di dati float che occupano maggiore memoria, è possibile utilizzare al posto del tipo float il tipo long, ovvero un int lungo rappresentato da 4 byte in grado di rappresentare numeri interi tra –2147483648 a 2147483647.

Vediamo come modificare lo sketch precedente per rilevare tensioni espresse in millivolt in cui però si utilizzano variabili intere di tipo long:

// Prof. Maffucci Michele
// Esempio 02: Misura di tensioni continue non superiori a 5V
// utilizzando variabili di tipo float
// Data: 01.10.2021

// batteria connessa al pin analogico 0
const byte pinBatteria = A0;

void setup() {
  // inizializzazione della porta seriale
  Serial.begin(9600);
}

void loop() {
  // legge il valore della batteria e lo trasforma
  // in un valore numerico tra 0 e 1023
  // valoreLetto è di tipo long
  long valoreLetto = analogRead(pinBatteria);

  // stampa sulla Serial Monitor la tensione misurata
  // in millivolt

  Serial.println((valoreLetto*(500000/1023L))/100);
}

Quindi per evitare di effettuare calcoli con numeri di tipo float (in virgola mobile) senza perdere precisione, il trucco consiste nell’operare sui valori in millivolt invece che  sui valori in volt. Ricordo che 1 volt corrisponde a 1000 millivolt.

Come detto all’inizio di questa lezione sappiamo che un valore numerico di 1023 restituito dall’analogRead, corrisponde al valore massimo di 5000 millivolt, allora ogni unità rappresenta 5000/1023 millivolt, che corrisponde a 4,89 millivolt. Come detto nell’esempio precedente, la stampa su Serial Monitor di un numero in virgola mobile mostrerà al massimo due decimali, pertanto l’eliminazione dei decimali nel secondo esempio possiamo farlo moltiplicando per 100, nel codice: 5000×100=500000, questo valore verrà poi moltiplicato per il rapporto tra il valore letto e 1023 ed il tutto ancora diviso per 100, così facendo otterremo il valore in millivolt. Questo calcolo permette di far effettuare al compilatore solamente calcoli tra interi e non tra float, rendendo quindi la computazione più veloce e riducendo la quantità di memoria utilizzata.

Si noti che al fondo del numero 1023 è stata aggiuta una L, cioè 1023L, che indicare al compilatore che il numero rappresentato è di tipo long (4 byte).

Esercizi per i miei studenti

Esercizio 1
Utilizzare un Trimmer per regolare la tensione in ingresso ad A0 tra 0 e 5V, in questo caso si prenda la tensione di 5V dal pin di Arduino.

Esercizio 2
Nel primo sketch proposto utilizzare la funzione map per convertire il valore restituito dall’ analogRead in un valore di tensione. In questo caso nascono dei problemi sulla precisione della misura, sapresti indicarmi quali?

Esercizio 3
Visualizzare il valore di tensione regolato dal Trimmer sul Plotter Seriale.

Esercizio 4
Utilizzando uno qualsiasi dei due sketch indicati sopra e visualizzare la tensione misurate sul un display 16×2

Esercizio 5
Aggiungere all’esercizio precedente l’indicazione di carica data da un grafico costituito da 5 quadrati che ne indicano il livello di carica, non appena il livello di carica raggiunge 1 volt il quadrettino inizia a lampeggiare.

Buon Making a tutti 🙂