Archivi tag: coding

Arduino – istruzione “do…while”: eseguire almeno una volta, poi verificare

Pubblicherò alcuni approfondimenti che serviranno per lo svolgimento di futuri esercizi che svolgeremo in classe e che sicuramente potranno essere di aiuto per risolvere l’attività di educazione civica: “dirimere i conflitti con il pensiero computazionale”.

Gli approfondimenti saranno su:

  1. istruzione do…while
  2. istruzione while
  3. lettura caratteri da serial monitor (che affronteremo anche in questa lezione)
  4. loop infiniti con for, while, do while

Alla fine troverete esercizi da svolgere, se riuscirò pubblicherò su queste pagine la soluzione, altrimenti lo faremo direttamente in classe.

do…while

L’istruzione do…while è utile quando un blocco di codice deve essere eseguito almeno una volta prima di verificare una condizione. È ideale per interazioni utente-centriche (es. chiedere conferma), letture che vanno fatte comunque almeno una volta (prima misura da sensore), o cicli in cui la condizione si conosce solo dopo aver eseguito un passo (validazione di input).

Spiegazione

Sintassi

do {
  // corpo: eseguito almeno una volta
} while (condizione);
  • Il controllo è a fine ciclo: se condizione è vera, il ciclo ripete; se è falsa, esce.
  • Evita pre-inizializzazioni “finte” solo per entrare nel ciclo.

Esempio 01: conferma utente da Serial (Y/N)

// Prof. Maffucci Michele
// data: 26.10.2025
// Esempio 01 - Ripasso uso dell'istruzione do...while

// Chiede conferma almeno una volta; normalizza input
void setup() {
  Serial.begin(9600);  // inizializza la Serial Monitor
  Serial.println("Conferma operazione? (y/n)");
}
void loop() {
  bool confermato = false;
  char scelta;
  do {
    if (Serial.available()) {
      scelta = Serial.read();
      if (scelta >= 'A' && scelta <= 'Z') {
        scelta = scelta - 'A' + 'a';
      }
      confermato = (scelta == 'y' || scelta == 'n');
    }
  } while (!confermato);

  Serial.print("Hai scelto: ");
  Serial.println(scelta == 'y' ? "SI" : "NO");
  while (true) {}  // fine dimostrazione
}

A cosa serve inserire: “while (true) {}” alla fine dell’esempio?

Evita che il codice prosegua oltre la dimostrazione e che il loop() ricominci, quindi è possibile effettuare un solo inserimento.

Se si commenta while (true) {} è possibile continuare ad inserire “y” o “n”

Come rendiamo case-insensitive (indifferente a maiuscole/minuscole) l’input

if (scelta >= 'A' && scelta <= 'Z') {
  scelta = scelta - 'A' + 'a';
}

Controllo dell’intervallo: scelta >= 'A' && scelta <= 'Z'

  • Verifica se il carattere è una lettera maiuscola ASCII tra A e Z.

Conversione in minuscolo: scelta = scelta - 'A' + 'a';

  • Trasforma quella maiuscola nella corrispondente minuscola.

Perché si usa: “- ‘A’ + ‘a’”

Come indicato anche nelle mie slide: “Alfabeto Arduino”, nel set ASCII, le lettere maiuscole e minuscole sono “a blocchi” distanziati da una differenza costante (32 in decimale).

  • ‘A’ ha codice 65, ‘a’ ha codice 97;
  • qualsiasi lettera: 'C' - 'A' = 2 (offset dentro il blocco maiuscole);
  • sommando questo offset a 'a': 2 + 'a' = 'c'.

Quindi:

  • Esempio concreto:
    scelta = 'C' - 'A' + 'a' che sostituendo i codici ASCII otteniamo 67 – 65 + 97 = 99 che è il codice ASCII di ‘c’.

Scriverlo con ‘A’ e ‘a’ (invece di 65 e 97) è più leggibile e non vi fa ricordare numeri.

Quanto è utile

  • rendere i comandi indifferenti al minuscolo o maiuscolo: l’utente può digitare Y o y e il programma li tratta allo stesso modo;
  • Evitare duplicazioni: non servono due rami separati per maiuscolo/minuscolo.

Limiti

  • funziona per le lettere A-Z dell’ASCII standard;
  • non gestisce caratteri accentati né Unicode (Arduino classico usa byte ASCII);
  • lascia invariati numeri e simboli (non entrano nell’if).

Vedremo in successive lezioni l’uso di altri strumenti per la gestione di stringhe inserite sulla Serial Monitor.

Esempio 02: prima lettura sensore, poi ripeti finché entro soglia

Per simulare un sensore in questo esempio viene utilizzato un trimmer collegato sul reoforo centrale ad A0 ed i due reofori laterali uno a GND e l’altro a Vcc.

// Prof. Maffucci Michele
// data: 26.10.2025
// Esempio 02 - Ripasso uso dell'istruzione do...while
// LED su pin 13: lampeggia finché la lettura NON è entro la fascia [450..550]

void setup() {
  Serial.begin(9600);  // inizializza la Serial Monitor
  pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
  int valore;
  do {
    valore = analogRead(A0);  // lettura comunque almeno una volta
    Serial.print("Valore: ");
    Serial.println(valore);
    digitalWrite(LED_BUILTIN, HIGH);
    delay(100);
    digitalWrite(LED_BUILTIN, LOW);
    delay(100);
  } while (valore < 450 || valore > 550);  // ripeti finché fuori dalla fascia
  Serial.println("Valore entro soglia: ok");
  // ripete una sola volta l'analisi (non ripete il loop()), se desiderate ripetere eliminare il ciclo infinito
  while (true) {}
}

Esempio 03: menu: mostra almeno una volta, poi ripeti finché la scelta risulta non valida

// Prof. Maffucci Michele
// data: 26.10.2025
// Esempio 03 - Ripasso uso dell'istruzione do...while
// Mostra un piccolo menu e accetta comandi 'a' 'b' 'x'; richiede almeno un giro

void setup() {
  Serial.begin(9600);
}
void loop() {
  char cmd = 0;
  do {
    Serial.println("Menu: a=avvia  b=blocca  x=uscita");
    while (!Serial.available()) {}
    cmd = Serial.read();
  } while (cmd != 'a' && cmd != 'b' && cmd != 'x');

  Serial.print("Comando accettato: ");
  Serial.println(cmd);
  // ripete una sola volta l'analisi (non ripete il loop()), se desiderate ripetere eliminare il ciclo infinito
  while (true) {}
}

In questo caso se commentate while (true) {} potete effettuare nuovi inserimenti ma potrebbe accadere qualcosa di molto simile a ciò che è indicato nell’immagine che segue

Quello che vedete non è un bug misterioso 🙂 è il buffer seriale + newline, ma cosa vuol dire?

Quando togliamo while (true) {}, il loop() ricomincia da capo.

Consideriamo la parte di codice dell’esempio:

do {
  Serial.println("Menu: a=avvia  b=blocca  x=uscita");
  while (!Serial.available()) {}
  cmd = Serial.read();                 // <-- legge SOLO il primo byte
} while (cmd!='a' && cmd!='b' && cmd!='x');

la Serial Monitor di Arduino (a seconda dell’impostazione “Line ending”) invia anche \n e/o \r oltre alla lettera che digitate.
Poiché leggete un solo carattere (cmd = Serial.read()) e non svuotate il buffer, restano in coda \r e/o \n.

Al giro successivo del do...while (o al restart del loop()), Serial.available() è già > 0 (ci sono dei newline in coda) quindi:

  • il menu viene stampato subito;
  • viene letto il carattere successivo (che è \n o \r, quindi non valido);
  • la condizione del do...while fallisce e viene ristampato il menu.

Risultato: la stringa compare più volte di fila.

2 modi per risolvere il problema della ripetizione

01. Leggere il primo carattere utile, saltando CR/LF

Viene ignorato esplicitamente \r e \n:

char leggiCharPulito() {
  while (true) {
    while (!Serial.available()) {}
    char c = Serial.read();
    if (c == '\r' || c == '\n') continue;         // salta newline
    if (c >= 'A' && c <= 'Z') c = c - 'A' + 'a';  // normalizza
    while (Serial.available())
      Serial.read();  // svuota residui
    return c;
  }
}

// uso:
do {
  Serial.println("Menu: a=avvia  b=blocca  x=uscita");
  cmd = leggiCharPulito();
} while (cmd != 'a' && cmd != 'b' && cmd != 'x');

02. Impostare il Serial Monitor su “No line ending”

Soluzione molto più semplice che prevede di impostare nel menù a tendina del Serial Monitor (in basso a destra), la selezione di Nessun fine riga (in inglese: No line ending).
Così non verranno inviati \r o \n e il problema dei “doppioni” si elimina.

Proposta di esercizi

Esercizio 01 –  Attendi pulsante

  • Consegna: attendere che un pulsante su D2 venga premuto; durante l’attesa far lampeggiare il LED integrato.
  • Vincoli: usare while come attesa in ingresso (while(digitalRead(…)==HIGH)).
  • Extra: al termine, stampare “OK” e fermarsi.

Esercizio 02 – Svuota buffer seriale

  • Consegna: quando l’utente invia testo, fare echo e svuotare tutti i caratteri residui.
  • Vincoli: usare while(Serial.available()>0).
  • Extra: contare quanti byte sono stati letti e mostrarli.

Esercizio 03 – Timer regressivo

  • Consegna: da un valore t letto da Serial (es. 5..20), eseguire un countdown finché t>0.
  • Vincoli: usare while(t>0) con t– e stampa del tempo.
  • Extra: beep finale + messaggio “Decollo!”.

Attività STEAM con carta, cartone e coding – 5ª edizione

Sono sinceramente lusingato: il corso “Attività STEAM con carta, cartone e coding” arriva alla sua 5ª edizione. Credo che questa continuità sia merito della forte componente laboratoriale e della presenza di proposte immediatamente spendibili in classe, che i docenti possono portare ai propri studenti già dal giorno successivo.

Anche in questa nuova edizione porterò attività inedite, tutte sperimentate con studenti e insegnanti, che spaziano:

  • dal gaming (meccaniche di gioco per attivare partecipazione e feedback),
  • all’Intelligenza Artificiale (IA) per la didattica,
  • passando per il coding sia plugged sia unplugged, sempre con materiali semplici e a basso costo.

Cosa troverai nel corso

  • Laboratori guidati passo-passo e idee pronte all’uso.
  • Attività modulabili per tempi, livelli e discipline.
  • Suggerimenti per valutazione, inclusione e gestione della classe.

A chi si rivolge

Docenti della scuola primaria e secondaria, educatori e formatori che desiderano introdurre o potenziare attività STEAM con un approccio concreto, creativo e sostenibile.

Iscrizioni e dettagli

Tutte le informazioni (programma, calendario e iscrizione) sono disponibili qui:

Pagina ufficiale del corso su Tecnica della Scuola

Ti aspetto per costruire insieme percorsi STEAM efficaci, coinvolgenti e subito applicabili in aula e in laboratorio.

Buon Making a tutti 🙂

Progettare bene, programmare meglio: pseudocodice e diagrammi per sistemi elettronici – lezione 5/5


Ripasso di inizio anno – appunti per la classe.

Di seguito un mini vocabolario, come sempre stampatelo e mettetelo nel tuo quadernone (alcune cose le abbiamo viste anche nella lezione precedente):

Useremo un dialetto semplice.

  • Una istruzione per riga.
  • Parole chiave in MAIUSCOLO e con i due punti : quando aprono un blocco:
    • SE … ALLORA: … ALTRIMENTI:
    • MENTRE (condizione):
    • PER i da A a B:
    • FUNZIONE nome(parametri): RITORNA valore
  • Indentazione (rientro) di 2–4 spazi per il contenuto dei blocchi.
  • Commenti: usa // all’inizio della riga.
  • Nomi chiari per variabili e costanti: sogliaLuce, tempoGiallo, pinLed.

Mini-vocabolario

  • IMPOSTA x a 10: assegna un valore
  • LEGGI x: prendi un input (da Serial o da sensore)
  • SCRIVI …: stampa (su Serial o su un display)
  • ATTENDI 500 ms: pausa
  • ESCI DAL CICLO: interrompi MENTRE o PER
  • OperatorI: ==, !=, <, >, >=, <=, AND, OR, NOT

Mappa mentale

Pseudocodice Arduino (C/C++)
IMPOSTA x a 10 int x = 10; oppure x = 10;
LEGGI da Serial parseInt()/readStringUntil('\n')
LEGGI digitale pin digitalRead(pin)
LEGGI analogico pin analogRead(pin)
SCRIVI su Serial Serial.print()/println()
ACCENDI LED digitalWrite(pin, HIGH)
SPEGNI LED digitalWrite(pin, LOW)
ATTENDI t ms delay(t)
PER i da 1 a N: for (int i=1; i<=N; i++) { ... }
MENTRE (condizione): while (condizione) { ... }
SE / ALTRIMENTI if (...) { ... } else { ... }
FUNZIONE tipo nome(params){...}
RITORNA return

Funzioni di aiuto per leggere dal Monitor Seriale

Blocchi riutilizzabili

// ---------------------------
// Aiuti per I/O seriale
// ---------------------------

// Legge un intero dalla Serial (es.: 42, -17).
// Attende che ci siano dati disponibili, usa parseInt()
// e poi "pulisce" il buffer fino al newline '\n' per preparare
// la prossima lettura.
int leggiInt() {
  // Attende finché il buffer seriale è vuoto.
  // Serial.available() restituisce quanti byte sono pronti da leggere.
  while (!Serial.available()) {
    // ciclo di attesa attivo: resta qui finché non arriva almeno 1 byte
  }

  // parseInt():
  //  - salta automaticamente spazi e caratteri non numerici iniziali;
  //  - legge opzionalmente il segno (-);
  //  - si ferma quando incontra un separatore (es. spazio, '\n', ecc.)
  //  - rispetta Serial.setTimeout(): se i numeri arrivano "a pezzi", attende fino al timeout.
  int v = Serial.parseInt();

  // Dopo parseInt possono essere rimasti caratteri nel buffer
  // (ad es. l'utente ha premuto Invio, quindi c'è '\n' o anche "\r\n").
  // Questo while consuma tutto ciò che c'è fino a trovare '\n' e poi esce,
  // in modo da "allineare" la prossima lettura all'inizio della riga seguente.
  while (Serial.available()) {
    // Legge un carattere dal buffer
    if (Serial.read() == '\n') break; // esce quando trova il newline
    // Nota: se volessi trattare anche '\r' come fine riga su sistemi Windows,
    // potresti controllare anche '\r' (carriage return).
  }

  // Restituisce il valore intero letto.
  return v;
}

// Legge una riga di testo fino al newline '\n' e la restituisce come String.
// Esempio di input: "ciao mondo\n" -> "ciao mondo" (senza newline né spazi finali).
String leggiLinea() {
  // Attende che arrivi almeno un byte.
  while (!Serial.available()) {
    // attesa attiva
  }

  // Legge i caratteri fino al delimitatore '\n' (newline).
  // Il newline non viene incluso nella String risultante.
  String s = Serial.readStringUntil('\n');

  // trim():
  //  - rimuove spazi iniziali/finali, tab e ritorni a capo residui.
  //  - Utile se su alcune piattaforme l'invio produce "\r\n" (Windows):
  //    readStringUntil('\n') rimuove '\n', ma può restare '\r' in coda; trim() lo elimina.
  s.trim();

  // Restituisce la riga "pulita".
  return s;
}

Codice senza commenti da copiare ed incollare:

// ——— Aiuti per I/O seriale ———
int leggiInt() {
  while (!Serial.available()) {}
  int v = Serial.parseInt();
  while (Serial.available()) {
    if (Serial.read() == '\n') break;
  }
  return v;
}

String leggiLinea() {
  while (!Serial.available()) {}
  String s = Serial.readStringUntil('\n');
  s.trim();
  return s;
}

Esempi guidati pseudocodice > sketch Arduino

Esempio 01 – Saluto con nome (solo Serial)

Scopo: input/output base, variabili stringa.

Pseudocodice – esempio 01

SCRIVI "Come ti chiami?"
LEGGI nome
SCRIVI "Ciao, " + nome + "!"

Arduino – esempio 01 – con commenti

void setup() {
  Serial.begin(9600);          // Inizializza la seriale a 9600 baud (Monitor Seriale deve avere lo stesso valore).

  while (!Serial) { ; }        // Attende che la porta seriale sia pronta (utile su schede con USB "native", es. UNO R4).
                               // Su UNO R3 spesso non serve, ma non fa danni.

  Serial.println("Come ti chiami?"); // Messaggio iniziale: l’utente vede la richiesta nel Monitor Seriale.

  Serial.setTimeout(60000);    // Imposta il "tempo massimo di attesa" a 60 s per funzioni come readStringUntil/parseInt.
                               // Se l’utente impiega tempo a digitare, la lettura aspetta fino a 60 s prima di rinunciare.
}

void loop() {
  String nome = leggiLinea();  // Legge una riga di testo inserita dall’utente fino al tasto Invio (newline '\n').
                               // Esempio: se scrivo "Michele" e premo Invio, nome = "Michele".

  Serial.print("Ciao, ");      // Stampa senza andare a capo...
  Serial.print(nome);          // ...stampa il nome letto...
  Serial.println("!");         // ...e chiude la riga con il punto esclamativo + newline.

  while (true) {}              // Ferma il programma qui (loop infinito).
                               // Serve per NON chiedere nuovamente il nome in un ciclo senza fine.
                               // Se vuoi che ripeta la domanda, rimuovi questa riga.
}

Arduino – esempio 01 – senza commenti

void setup() {
  Serial.begin(9600);
  while (!Serial) {;}
  Serial.println("Come ti chiami?");
  Serial.setTimeout(60000);
}

void loop() {
  String nome = leggiLinea();
  Serial.print("Ciao, ");
  Serial.print(nome);
  Serial.println("!");
  while (true) {}  // fine
}

Esercizio aggiuntivo: chiedi anche l’età e rispondi “Sei maggiorenne/minorenne”.

Esempio 02 – Blink (LED lampeggiante)

Collegamenti: LED con resistenza su pin 13 (o usa quello onboard).

Pseudocodice – esempio 02

IMPOSTA pinLed a 13
IMPOSTA periodo a 500 ms
MENTRE (vero):
    ACCENDI LED
    ATTENDI periodo
    SPEGNI LED
    ATTENDI periodo

Arduino – esempio 02

const int pinLed = 13;
const int periodo = 500;

void setup() {
  pinMode(pinLed, OUTPUT);
}

void loop() {
  digitalWrite(pinLed, HIGH);
  delay(periodo);
  digitalWrite(pinLed, LOW);
  delay(periodo);
}

Esercizio aggiuntivo: leggi periodo da Serial.

Esempio 03 – Pulsante accende/spegne LED

Collegamenti: pulsante su pin 2, modalità INPUT_PULLUP; LED su pin 8.
Nota: il pulsante chiude a GND (premuto = LOW).

Pseudocodice – esempio 03

IMPOSTA statoLed a SPENTO
MENTRE (vero):
    LEGGI pulsante
    SE (pulsante premuto) ALLORA:
        ATTENDI 20 ms  // anti-rimbalzo semplice
        SE (ancora premuto) ALLORA:
            inverti statoLed
            applica stato al LED
            ATTENDI rilascio

Arduino – esempio 03

const int pinBtn = 2;
const int pinLed = 8;
bool statoLed = false;

void setup() {
  Serial.begin(9600);
  pinMode(pinBtn, INPUT_PULLUP);
  pinMode(pinLed, OUTPUT);
}

void loop() {
  if (digitalRead(pinBtn) == LOW) {  // premuto
    delay(20);                       // debounce base
    if (digitalRead(pinBtn) == LOW) {
      statoLed = !statoLed;  // toggle
      digitalWrite(pinLed, statoLed ? HIGH : LOW);
      // Attendi rilascio
      while (digitalRead(pinBtn) == LOW) {}
      delay(20);
    }
  }
}

Esercizio aggiuntivo: Estensione: stampa su Serial “ON/OFF” ad ogni pressione.

Esercizi

Esercizio 01 – Potenziometro > luminosità (PWM)

Obiettivo: Regolare mediante potenziometro l’intensità luminosa del LED.
Collegamenti: potenziometro su A0; LED su pin PWM 9.

Esercizio 02 – LED ON per 3 secondi quando premo il pulsante

Obiettivo: premo il pulsante > il LED rimane acceso per 3 secondi > poi si spegne.
Evitare ripetizioni mentre si tiene premuto (debounce base).

Componenti & Collegamenti

  • 1 × LED + 1 × resistenza (220–330 Ω) su pin 8 → GND
  • 1 × pulsante su pin 2 con INPUT_PULLUP (l’altro capo del pulsante a GND)

Esercizio 03 — LDR: accendi il LED quando è buio

Obiettivo: leggo la luce con una LDR. Se il valore è sotto una soglia (buio), accendo il LED; altrimenti lo spengo. Stampo i valori su Serial.

Componenti & Collegamenti

  • LDR in partitore su A0 (esempio: 5V — LDR — A0 — resistenza 10 kΩ — GND)
  • LED + resistenza su pin 9 → GND

Esercizio 04 – Potenziometro > luminosità LED (PWM)

Obiettivo: con il potenziometro regolo la luminosità del LED usando analogWrite (PWM).

Componenti & Collegamenti

  • Potenziometro 10 kΩ su A0 (estremi a 5V e GND, cursore ad A0)
  • LED + resistenza su pin ~9 (pin PWM) > GND

Buon Coding a tutti 🙂

Lezione 11 – Corso di Elettronica Creativa con Arduino Sensor Kit

Accelerometro

L’accelerometro del Sensor Kit è un “sensore di movimento” a tre assi (X, Y, Z) capace di misurare come cambia la velocità di un oggetto nello spazio. Anche quando è fermo, l’accelerometro non rileva “zero accelerazione” in quanto misura la gravità terrestre, circa 1 g (≈ 9,81 m/s²). Questa caratteristica è utilissima perché ci permette sia di capire se e quanto ci stiamo muovendo, sia di stimare l’inclinazione del modulo rispetto alla verticale. In pratica: se appoggio la scheda piatta sul banco, il valore lungo Z sarà vicino a 1 g; se lo incliniamo, la gravità si “ridistribuisce” tra X e Y e possiamo ricavare l’angolo di inclinazione.

Con i dati che fornisce l’accelerometro possiamo fare molte cose: visualizzare scosse e vibrazioni (come un mini sismografo), creare un avviso di caduta, progettare giochi di equilibrio o semplicemente tracciare i movimenti sulla Serial Plotter. In questa lezione impareremo a leggere le tre componenti, a filtrare il rumore e a trasformare quei numeri in azioni concrete (LED, buzzer, messaggi su OLED).

Un briciolo di teoria

L’accelerometro misura accelerazioni lungo X, Y, Z e come dicevo sopra, da fermo “vede” la gravità (~1 g ≈ 9,81 m/s²).

Il modulo del vettore accelerazione è:

Con il dispositivo quasi fermo, possiamo stimare l’inclinazione rispetto alla verticale usando solo la gravità:

che rappresenta l’angolo rispetto all’asse Z (in gradi).

Caratteristiche tecniche (per i più esperti)

  • Tipo sensore: MEMS accelerometro triassiale digitale.
  • Interfacce: I²C e SPI digitali (il modulo Grove è normalmente usato in I²C sul Sensor Kit).
  • Indirizzo I²C: di default 0x19, commutabile a 0x18 (pin SA0/SDO a GND).
  • Campo di misura (full-scale): ±2 g, ±4 g, ±8 g, ±16 g (selezionabile via registro).
  • Risoluzione dati: uscita fino a 16-bit; modalità operative low-power/normal/high-resolution (fino a 12-bit effettivi).
  • Frequenza di campionamento (ODR): da 1 Hz fino a 5.3 kHz (a seconda della modalità).
  • Funzioni integrate:
    • 2 generatori di interrupt programmabili (motion, free-fall, wake-up).
    • Rilevamento orientamento 6D/4D, self-test, FIFO 32 campioni.
  • Alimentazione (chip): 1.71–3.6 V; consumo molto basso (fino a ~2 µA in ultra-low-power). Il modulo Grove è compatibile 3 V / 5 V.
  • Intervallo di temperatura operativa: −40 °C … +85 °C.

Informazioni utili per l’uso nell’Arduino Sensor Kit

  • Con la libreria Arduino_SensorKit.h leggete le tre componenti con Accelerometer.readX() / readY() / readZ(); i valori restituiti sono interpretati in g (gravità ≈ 1 g a riposo).
  • Il modulo Grove usato nel Sensor Kit documenta esplicitamente indirizzi I²C, range e compatibilità 3V/5V, utile se sorgono conflitti su bus I²C o serve cambiare indirizzo.

Collegamenti

Per quanto riguarda i collegamenti è sufficiente connettere il sensore ad uno degli ingressi I²C della Base Shield.

Esempio 01 – Lettura accelerometro 3 assi

/*
  Prof. Maffucci Michele
  02.10.2025
  Lettura accelerometro 3 assi – Arduino Sensor Kit
*/

#include <Arduino_SensorKit.h>   // Libreria unica per i moduli del Sensor Kit

const unsigned long intervalloMs = 500;  // intervallo tra una stampa e la successiva

void setup() {
  // Inizializzazione porta seriale per inviare i dati al PC
  Serial.begin(9600);

  // ATTENZIONE:
  // while(!Serial); serve ad ASPETTARE che il monitor seriale sia stato aperto
  // (utile sulle schede con USB nativa, esempio: Arduino UNO R4 WiFi, Leonardo, MKR).
  // Così non "perdi" le prime stampe.
  // Su schede senza USB nativa non è necessario e puoi rimuoverlo.
  while (!Serial) { ; }

  // Avvio dell'accelerometro a 3 assi
  Accelerometer.begin();
}

void loop() {
  // Lettura delle tre componenti di accelerazione (in g)
  float accelX = Accelerometer.readX();  // Asse X
  float accelY = Accelerometer.readY();  // Asse Y
  float accelZ = Accelerometer.readZ();  // Asse Z

  // Stampa in formato semplice: x:..  y:..  z:..
  Serial.print("x: "); 
  Serial.print(accelX, 3);   // 3 decimali per leggibilità
  Serial.print("  ");

  Serial.print("y: "); 
  Serial.print(accelY, 3);
  Serial.print("  ");

  Serial.print("z: "); 
  Serial.println(accelZ, 3);

  delay(intervalloMs);
}

Esempio 02: Accelerometro – Stampa su Serial Monitor e display OLED

Visualizzazione delle componenti x, y e z dell’accelerazione su Serial Monitor e display OLED

/*
  Prof. Maffucci Michele
  02.10.2025
  Lettura accelerometro 3 assi – Arduino Sensor Kit
  Stampa su Serial Monitor e display Oled
*/

#include <Arduino_SensorKit.h>  // Libreria unica per i moduli del Sensor Kit

const unsigned long intervalloMs = 500;  // intervallo tra una stampa e la successiva

void setup() {
  // Inizializzazione porta seriale per inviare i dati al PC
  Serial.begin(9600);

  // ATTENZIONE:
  // while(!Serial); serve ad ASPETTARE che il monitor seriale sia stato aperto
  // (utile sulle schede con USB nativa, esempio: Arduino UNO R4 WiFi, Leonardo, MKR).
  // Così non "perdi" le prime stampe.
  // Su schede senza USB nativa non è necessario e puoi rimuoverlo.
  while (!Serial) { ; }

  // Avvio dell'accelerometro a 3 assi
  Accelerometer.begin();
  Oled.begin();
  // Inizializza il display OLED.
  // Fa partire la comunicazione I2C e manda al display la sequenza di avvio.
  // Senza questa riga lo schermo non mostra nulla.

  Oled.setFlipMode(true);
  // Ruota il contenuto di 180° (utile se lo vedi capovolto).
  // Se nel tuo caso appare già dritto, puoi mettere false: Oled.setFlipMode(false);

  Oled.setFont(u8x8_font_chroma48medium8_r);
  // Sceglie il font (carattere) 8x8.
  // Con un font 8x8 il display 128x64 si comporta come una griglia 16 colonne × 8 righe.
  // Significa che setCursor(colonna, riga) userà numeri tra 0..15 (colonne) e 0..7 (righe).

  Oled.clear();
  // Pulisce lo schermo (cancella tutto quello che c’era prima).
}

void loop() {
  // Lettura delle tre componenti di accelerazione (in g)
  float accelX = Accelerometer.readX();  // Asse X
  float accelY = Accelerometer.readY();  // Asse Y
  float accelZ = Accelerometer.readZ();  // Asse Z

  // Stampa in formato semplice: x:..  y:..  z:..
  Serial.print("x: ");      // stampa la stringa x: su Serial Monitor
  Serial.print(accelX, 3);  // 3 decimali per leggibilità
  Serial.print("  ");

  Serial.print("y: ");      // stampa la stringa y: su Serial Monitor
  Serial.print(accelY, 3);  // 3 decimali per leggibilità
  Serial.print("  ");

  Serial.print("z: ");        // stampa la stringa z: su Serial Monitor
  Serial.println(accelZ, 3);  // 3 decimali per leggibilità

  Oled.setCursor(0, 0);           // colonna 0, riga 0
  Oled.println("ACCELERAZIONE");  // stampa la stringa e va a capo

  Oled.setCursor(0, 2);   // colonna 0, riga 2
  Oled.print("x: ");      // stampa la stringa x:
  Oled.print(accelX, 3);  // stampa la componente x dell'accelerazione con 3 cifre decimali

  Oled.setCursor(0, 3);   // colonna 0, riga 3
  Oled.print("y: ");      // stampa la stringa y:
  Oled.print(accelY, 3);  // stampa la componente y dell'accelerazione con 3 cifre decimali
  Oled.refreshDisplay();

  Oled.setCursor(0, 4);   // colonna 0, riga 4
  Oled.print("z: ");      // stampa la stringa z:
  Oled.print(accelZ, 3);  // stampa la componente z dell'accelerazione con 3 cifre decimali
  Oled.refreshDisplay();

  delay(intervalloMs);
}

Esempio 03: Accelerometro – Uscita per Serial Plotter (3 curve: X, Y, Z)

Stampa i tre assi con etichette e tab in un’unica riga per campione, in questo modo l’IDE mostra tre curve (X, Y, Z).

/*
  Prof. Maffucci Michele
  02.10.2025
  Accelerometro – Uscita per Serial Plotter (3 curve: X, Y, Z)

  Nota: una riga = un campione; valori etichettati e separati da TAB -> 3 curve nel Plotter.
*/

#include <Arduino_SensorKit.h&gt

const unsigned long periodoCampionamentoMs = 10;  // circa 100 Hz (riduci/aumenta a piacere)

void setup() {
  Serial.begin(115200);
  // Attende l'apertura del Monitor/Plotter Seriali sulle schede con USB nativa
  while (!Serial) { ; }

  Accelerometer.begin();

  // Messaggio di avvio (facoltativo)
  Serial.println("Traccio X,Y,Z (g) - dati GREZZI (nessun filtro)");
}

void loop() {
  // Letture grezze in g
  float x = Accelerometer.readX();
  float y = Accelerometer.readY();
  float z = Accelerometer.readZ();

  // Stampa “label:valore” separati da TAB → il Plotter disegna tre curve
  Serial.print("X:");
  Serial.print(x, 3);
  Serial.print('\t');
  Serial.print("Y:");
  Serial.print(y, 3);
  Serial.print('\t');
  Serial.print("Z:");
  Serial.println(z, 3);

  delay(periodoCampionamentoMs);
}

Versione per esperti

Esempio 04: Accelerometro – Uscita per Serial Plotter (3 curve: X, Y, Z) con filtro passa-basso

Come avete potuto notare nella versione precedente le curve delle tre componenti dell’accelerazione erano seghettate e venivano rappresentate anche piccoli tremolii.

Per evitare questo problema useremo nello sketch che segue un filtro IIR (passa-basso) che smusserà il rumore.

Quali sono i vantaggi nell’utilizzo di un filtro passa basso:

  • Rumore: piccole variazioni veloci e indesiderate nei dati (tremolio della mano, vibrazioni del banco, disturbi elettrici).
  • Passa-basso: un filtro che lascia passare i cambiamenti lenti (il “trend” reale del movimento) e attenua i cambiamenti veloci (dovuti al rumore).
  • IIR (Infinite Impulse Response): significa che l’uscita del filtro dipende sia dal valore attuale sia dall’uscita precedente. In pratica: il filtro “ricorda” un po’ del passato -> è una media mobile esponenziale.

Formule per ogni asse:

uscita_filtrata(t) = α * ingresso(t) + (1−α) * uscita_filtrata(t−1)

α è un numero tra 0 e 1:

  • α piccolo (es. 0.1) > molto liscio (tanto filtraggio) ma più lento a seguire i cambiamenti.
  • α grande (es. 0.6) > meno liscio (meno filtraggio) ma più reattivo.

In sintesi:
scegliere α in base al compromesso “più liscio vs più rapido”.

/*
  Prof. Maffucci Michele
  02.10.2025
  Accelerometro - Uscita per Serial Plotter (3 curve: X, Y, Z) con filtro IIS (passa-basso)

  Note:
  - Il Serial Plotter IDE riconosce coppie "etichetta:valore" sulla stessa riga,
    separate da TAB. Ogni riga = un campione nel tempo.
*/

#include <Arduino_SensorKit.h&gt

const unsigned long periodoCampionamentoMs = 10;  // circa 100 Hz
const bool usaFiltro = true;                      // metti false se vuoi i dati grezzi
const float alfa = 0.2;                           // 0..1 (più piccolo = più filtrato)

float xF = 0, yF = 0, zF = 0;  // stati del filtro

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

  // Serve ad attendere l'apertura del Monitor/Plotter Seriali sulle schede con USB nativa,
  // così non perdete le prime stampe (su UNO "classico" puoi toglierlo senza problemi).
  while (!Serial) { ; }

  Accelerometer.begin();

  // (Facoltativo) riga di "header" informale: non necessaria, ma utile a chi guarda il log
  Serial.println("Pronto: traccio X,Y,Z (g) su Serial Plotter");
}

void loop() {
  // Letture in g
  float x = Accelerometer.readX();
  float y = Accelerometer.readY();
  float z = Accelerometer.readZ();

  if (usaFiltro) {
    // Filtro IIR (passa-basso) molto semplice per smussare il rumore
    xF = alfa * x + (1.0f - alfa) * xF;
    yF = alfa * y + (1.0f - alfa) * yF;
    zF = alfa * z + (1.0f - alfa) * zF;

    // Stampa "label:valore" separati da TAB > tre curve nel Plotter
    Serial.print("X:");
    Serial.print(xF, 3);
    Serial.print('\t');
    Serial.print("Y:");
    Serial.print(yF, 3);
    Serial.print('\t');
    Serial.print("Z:");
    Serial.println(zF, 3);
  } else {
    Serial.print("X:");
    Serial.print(x, 3);
    Serial.print('\t');
    Serial.print("Y:");
    Serial.print(y, 3);
    Serial.print('\t');
    Serial.print("Z:");
    Serial.println(z, 3);
  }

  delay(periodoCampionamentoMs);
}

Analizziamo nel dettaglio la parte del filtro.

xF = alfa * x + (1.0f - alfa) * xF;
yF = alfa * y + (1.0f - alfa) * yF;
zF = alfa * z + (1.0f - alfa) * zF;

xF = alfa * x + (1.0f - alfa) * xF;

  • x è il dato grezzo appena letto dall’accelerometro sull’asse X.
  • xF è il dato filtrato (uscita del filtro) per l’asse X.
  • alfa * x prende una frazione del dato nuovo (quanto “peso” dai alla misura attuale).
  • (1.0f - alfa) * xF prende una frazione del valore filtrato precedente (la “memoria”).
  • La somma dei due termini fornisce un valore ammorbidito: meno sensibile ai picchi improvvisi.

Nota sul suffisso f: indica un float letterale (utile per evitare promozioni a double in alcune piattaforme).

yF = alfa * y + (1.0f - alfa) * yF;

  • Identico ragionamento, ma applicato all’asse Y: nuova misura y + memoria del filtrato yF.
  • Mantieni alfa uguale sugli assi per coerenza visiva nel Plotter.

zF = alfa * z + (1.0f - alfa) * zF;

  • Stessa cosa per l’asse Z: la componente verticale (spesso contiene circa 1 g).
  • Filtrare Z aiuta a vedere meglio oscillazioni lente e a ridurre tremolii.

Qualche nota pratica

  1. Inizializzazione: se impostate xF = yF = zF = 0 all’inizio, i primi campioni possono “salire” gradualmente verso il valore reale (breve transitorio). Se volete partire subito “allineati”, potete inizializzare xF=yF=zF al primo dato letto.
  2. Scelta di alfa:
    0.1-0.3 > grafico molto liscio (meno rumore), risposta più lenta.
    0.4-0.7 > più reattivo, ma un po’ più “seghettato”.

Buon Coding a tutti 🙂

Lezione 10 – Corso di Elettronica Creativa con Arduino Sensor Kit

Cenni teorici

Cos’è la pressione atmosferica?

La pressione atmosferica è la forza esercitata dall’aria su ogni unità di superficie. Viene misurata in pascal (Pa) o, più comunemente, in hectopascal (hPa) dove 1 hPa = 100 Pa.

Il sensore BMP280

Il BMP280 è un sensore barometrico assoluto prodotto da Bosch Sensortec. Le sue caratteristiche principali:

  • Campo di misura: 300 – 1100 hPa (≈ -500 m / +9000 m s.l.m.)
  • Accuratezza assoluta tip. ±1 hPa; accuratezza relativa ±0.12 hPa (≈ ±1 m)
  • Interfacce: I²C (fino a 3.4 MHz) e SPI
  • Consumo: 2.7 µA @ 1 Hz in modalità normale
  • Range termico: –40 … +85 °C

Collegamenti

Collegate il cavo Grove tra il connettore del sensore e la porta I²C (qualsiasi) sullo shield.

Installazione libreria

Sicuramente è un’operazione che avete già eseguito, in ogni caso lo ricordo:

  • Arduino IDE > Gestione librerie > cerca “Arduino_SensorKit”.
  • Installate la libreria (include automaticamente LPS22HB, HTS221, OLED, ecc.)

 Esempio 01: Sketch base – Lettura e stampa dati su Serial Monitor

/*
  Prof. Maffucci Michele
  02.10.2025
  LPS22HB del Sensor Kit
  Stampa valori su Serial Monitor
*/

#include &lt;Arduino_SensorKit.h&gt;   // include unico per tutti i sensori

void setup() {
  Serial.begin(9600);
  Pressure.begin();              // inizializza il barometro
}

void loop() {
  // La libreria restituisce:
  // - pressione in pascal
  // - temperatura in gradi Celsius
  // - altitudine in metri rispetto a 1013,25 hPa

float pressionePa  = Pressure.readPressure();
  float temperaturaC = Pressure.readTemperature();
  float altitudineM  = Pressure.readAltitude();

// conversione pa → hPa per leggibilità
  float pressioneHpa = pressionePa / 100.0;

Serial.print(F("Pressione: "));
  Serial.print(pressioneHpa, 2);
  Serial.print(F(" hPa\tTemperatura: "));
  Serial.print(temperaturaC, 2);
  Serial.print(F(" °C\tAltitudine: "));
  Serial.print(altitudineM, 1);
  Serial.println(F(" m"));

delay(1000);
}

Ricordo che, come già indicato nelle precedenti lezioni, la F(…) (maiuscola), inserita all’interno delle Serial.print(), è una macro di Arduino che serve a mettere una stringa letterale in memoria flash (PROGMEM) invece che copiarla nella SRAM al momento della stampa.

Perché è utile utilizzare F

  • Le board come Arduino UNO (AVR) hanno poca SRAM (2 KB).
  • Senza (), ogni stringa tra virgolette ("Pressione: ") viene copiata in SRAM prima di stamparla > spreco di RAM.
  • Con F("Pressione: "), la stringa resta in flash e Serial.print() la legge direttamente da lì > RAM risparmiata.

Come si usa

Funziona con tutte le funzioni che derivano da Print (es. Serial.print, Serial.println, spesso anche display che usano print in stile Arduino):

Serial.print(F("Pressione: "));
Serial.println(F(" hPa"));

Quando usarla

  • Frasi/titoli/menu costanti e ripetute (log su Serial, messaggi di errore, etichette).
  • Con progetti in cui si ha scarsa disponibilità di RAM (UNO/Nano classici) può fare la differenza.

Limiti

  • Non puoi concatenare direttamente elementi come F(“A”) + F(“B”).
  • Evitare di mescolarla con String in modo implicito (meglio stampare pezzi separati).
  • Funzioni come sprintf non leggono da flash; su AVR esistono versioni “_P” (es. sprintf_P) per farlo.

La struttura delle funzioni (Pressure.begin(), Pressure.readPressure(), ecc.) è la stessa mostrata negli esempi ufficiali di Arduino.

 Esempio 02: Lettura e stampa dati su Serial Monitor e display OLED

Abbiamo già visto l’uso del display OLED nella lezione: Lezione 9 – Corso di Elettronica Creativa con Arduino Sensor Kit vediamo ora come mostrare i valori di: pressione, temperatura e altitudine anche sul display OLED.

/*
  Prof. Maffucci Michele
  02.10.2025
  BMP280 - Arduino Sensor Kit
  Stampa valori su Serial Monitor e OLED
*/

#include <Arduino_SensorKit.h>  // include unico per tutti i sensori

void setup() {
  Serial.begin(9600);
  Pressure.begin();  // inizializza il barometro

Oled.begin();
  // Inizializza il display OLED.
  // Fa partire la comunicazione I2C e manda al display la sequenza di avvio.
  // Senza questa riga lo schermo non mostra nulla.

Oled.setFlipMode(true);
  // Ruota il contenuto di 180° (utile se lo vedi capovolto).
  // Se nel tuo caso appare già dritto, puoi mettere false: Oled.setFlipMode(false);

Oled.setFont(u8x8_font_chroma48medium8_r);
  // Sceglie il font (carattere) 8x8.
  // Con un font 8x8 il display 128x64 si comporta come una griglia 16 colonne × 8 righe.
  // Significa che setCursor(colonna, riga) userà numeri tra 0..15 (colonne) e 0..7 (righe).

Oled.clear();
  // Pulisce lo schermo (cancella tutto quello che c’era prima).
}

void loop() {
  // La libreria restituisce:
  // - pressione in pascal
  // - temperatura in gradi Celsius
  // - altitudine in metri rispetto a 1013,25 hPa

float pressionePa = Pressure.readPressure();
  float temperaturaC = Pressure.readTemperature();
  float altitudineM = Pressure.readAltitude();

// conversione pa → hPa per leggibilità
  float pressioneHpa = pressionePa / 100.0;

// --- Intestazione ---

Oled.setCursor(0, 0);             // colonna 0, riga 0
  Oled.println("STAZIONE METEO");   // stampa la stringa e va a capo
  Oled.println("P - Pressione");    // stampa la stringa e va a capo
  Oled.println("T - Temperatura");  // stampa la stringa e va a capo
  Oled.println("A - Altitudine");   // stampa la stringa e va a capo

Oled.setCursor(0, 5);         // colonna 0, riga 2
  Oled.print("P: ");            // stampa la stringa P:
  Oled.print(pressioneHpa, 2);  // stampa valore della pressione con 2 numeri decimali

Oled.setCursor(0, 6);         // colonna 0, riga 3
  Oled.print("T: ");            // stampa la stringa T:
  Oled.print(temperaturaC, 2);  // stampa valore della temperatura con 2 numeri decimali
  Oled.refreshDisplay();

Oled.setCursor(0, 7);        // colonna 0, riga 4
  Oled.print("A: ");           // stampa la stringa A:
  Oled.print(altitudineM, 2);  // stampa valore dell'altitudine con 2 numeri decimali
  Oled.refreshDisplay();

// Aggiorna fisicamente il display (altrimenti potresti non vedere le modifiche).
  delay(60);

Serial.print(F("Pressione: "));
  Serial.print(pressioneHpa, 2);
  Serial.print(F(" hPa\tTemperatura: "));
  Serial.print(temperaturaC, 2);
  Serial.print(F(" °C\tAltitudine: "));
  Serial.print(altitudineM, 1);
  Serial.println(F(" m"));

delay(1000);
}

Buon Coding a tutti 🙂