Archivi tag: coding

Come ottenere più valori da una funzione in Arduino: variabili globali, riferimenti e struct – lezione 1/2

Durante la correzione delle esercitazioni di laboratorio mi accorgo spesso che il passaggio dei valori alle funzioni è un punto che genera parecchia confusione. Per questo ho deciso di preparare una breve lezione di ripasso, per chiarire i concetti fondamentali e aiutare a scegliere l’approccio più corretto nello sviluppo degli sketch.

Uno dei problemi molto comuni quando si programma in C/C++ su Arduino è: come ottenere più di un valore da una funzione?

In molti casi vorremmo che una funzione ci restituisse, ad esempio, due risultati (come nello scambio di due numeri), ma il linguaggio prevede che una funzione possa avere un solo valore di ritorno con l’istruzione return. Questo non significa che siamo bloccati: esistono più strategie corrette per gestire più risultati in modo chiaro e sicuro.

Partiremo da una soluzione semplice e intuitiva, basata su variabili globali, utile per capire il concetto ma generalmente sconsigliata nei programmi reali perché può creare effetti collaterali difficili da controllare. Passeremo poi a un approccio molto più robusto: il passaggio per riferimento, che permette a una funzione di modificare direttamente le variabili del chiamante senza ricorrere a globali. Infine vedremo l’uso dell’istruzione struct, cioè “contenitori” di più campi, che consentono di raggruppare dati correlati e di restituirli come un unico oggetto.

L’obiettivo non è solo far funzionare gli esempi, ma capire cosa succede davvero in memoria quando una funzione riceve parametri “per valore” (copie) oppure “per riferimento” (accesso diretto all’originale) e perché la scelta del metodo influisce su leggibilità, manutenzione e affidabilità del codice. Al termine della lezione saprete riconoscere quale tecnica usare a seconda del problema e scrivere funzioni più pulite, modulari e facili da riutilizzare.

Vedremo poi nella prossima lezione una quarta variante molto diffusa nel mondo embedded.

01. Versione “semplice” (ma sconsigliata): usare variabili globali

Esempio 01: scambio di valori

La funzione non “restituisce” due valori: modifica direttamente due variabili definite fuori da loop().

/*
  Prof. Maffucci Michele
  13.02.26
  Scambio di due valori usando variabili globali.
*/

int valoreA;
int valoreB;

void scambiaGlobali() {
  int temporaneo = valoreA;
  valoreA = valoreB;
  valoreB = temporaneo;
}

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

void loop() {
  valoreA = random(10);
  valoreB = random(10);

  Serial.print("Valori prima dello scambio (A, B): ");
  Serial.print(valoreA);
  Serial.print(", ");
  Serial.println(valoreB);

  scambiaGlobali();

  Serial.print("Valori dopo lo scambio (A, B):  ");
  Serial.print(valoreA);
  Serial.print(", ");
  Serial.println(valoreB);

  Serial.println();
  delay(1000);
}

Esempio 02: la funzione restituisce minimo e massimo aggiornando variabili globali.

Sulla serial monitor vengono mostrati ogni 5 secondi: la serie di 20 numeri generati, il minimo della serie, il massimo della serie.

/*
  Prof. Maffucci Michele
  13.02.26
  Calcola minimo e massimo su una serie di campioni usando variabili globali.
*/

int minimoCampione;
int massimoCampione;

void calcolaMinMaxGlobali(int numeroCampioni) {
  minimoCampione = 9999;
  massimoCampione = -9999;

  Serial.print("Campioni: ");

  for (int i = 0; i < numeroCampioni; i++) {
    int campione = random(0, 101); // 0..100

    // Stampa i campioni separati da virgola
    Serial.print(campione);
    if (i < numeroCampioni - 1) {
      Serial.print(", ");
    } else {
      Serial.println(); // a capo dopo l'ultimo campione
    }

    // Aggiorna minimo e massimo
    if (campione < minimoCampione) {
      minimoCampione = campione;
    }
    if (campione > massimoCampione) {
      massimoCampione = campione;
    }
  }
}

void setup() {
  Serial.begin(9600);
  randomSeed(analogRead(A0));
}

void loop() {
  const int numeroCampioni = 20;

  Serial.print("Genero ");
  Serial.print(numeroCampioni);
  Serial.println(" campioni casuali (0..100) e calcolo min/max...");

  calcolaMinMaxGlobali(numeroCampioni);

  Serial.print("Minimo trovato: ");
  Serial.println(minimoCampione);

  Serial.print("Massimo trovato: ");
  Serial.println(massimoCampione);

  Serial.println();
  delay(5000);
}

02. Passaggio per riferimento

Esempio 01: Scambia due valori passati per riferimento.

/*
  Prof. Maffucci Michele
  13.02.25
  Scambia due valori passati per riferimento.
*/

void scambiaPerRiferimento(int &a, int &b) {
  int temporaneo = a;
  a = b;
  b = temporaneo;
}

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

void loop() {
  int numero1 = random(10);
  int numero2 = random(10);

  Serial.print("Valori prima dello scambio (n1, n2): ");
  Serial.print(numero1);
  Serial.print(", ");
  Serial.println(numero2);

  scambiaPerRiferimento(numero1, numero2);

  Serial.print("Valori dopo lo scambio (n1, n2):  ");
  Serial.print(numero1);
  Serial.print(", ");
  Serial.println(numero2);

  Serial.println();
  delay(1000);
}

Esempio 02: divisione con resto (quoziente + resto)

Nello sketch che segue una funzione calcola due risultati (quoziente e resto) tramite riferimenti, e in più restituisce true/false per gestire il caso del divisore zero.

/*
  Prof. Maffucci Michele
  13.02.26
  Calcola due risultati (quoziente e resto) tramite riferimenti, 
  restituisce true/false per gestire il caso del divisore zero.
*/

bool dividiConResto(int numeratore, int denominatore, int &quoziente, int &resto) {
  if (denominatore == 0) {
    return false; // errore: divisione per zero
  }

  quoziente = numeratore / denominatore;
  resto = numeratore % denominatore;
  return true;
}

void setup() {
  Serial.begin(9600);
  randomSeed(analogRead(A0));
}

void loop() {
  int a = random(0, 101);   // 0..100
  int b = random(0, 11);    // 0..10 (include lo zero apposta)

  int q = 0;
  int r = 0;

  Serial.print("Operazione: ");
  Serial.print(a);
  Serial.print(" / ");
  Serial.println(b);

  if (dividiConResto(a, b, q, r)) {
    Serial.print("Quoziente: ");
    Serial.println(q);

    Serial.print("Resto: ");
    Serial.println(r);
  } else {
    Serial.println("Errore: divisione per zero (b = 0).");
  }

  Serial.println();
  delay(1500);
}

03. Scambio di valori versione migliorata. Trattare più valori insieme: usare una struct

Con la struct impacchettiamo più campi in un solo tipo (una “coppia”), la passiamo alla funzione e la funzione restituisce una nuova struct con i campi scambiati.

Esercizio 01: scambio di valori

/*
  Prof. Maffucci Michele
  13.02.26
  Scambio di valori usando una struct.
*/

struct Coppia {
  int primo;
  int secondo;
};

Coppia scambiaCoppia(Coppia c) {
  int temporaneo = c.primo;
  c.primo = c.secondo;
  c.secondo = temporaneo;
  return c;
}

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

void loop() {
  Coppia dati = { random(10), random(10) };

  Serial.print("Valori prima dello scambio (p, s): ");
  Serial.print(dati.primo);
  Serial.print(", ");
  Serial.println(dati.secondo);

  dati = scambiaCoppia(dati);

  Serial.print("Valori dopo lo scambio (p, s):  ");
  Serial.print(dati.primo);
  Serial.print(", ");
  Serial.println(dati.secondo);

  Serial.println();
  delay(1000);
}

Esempio 02: Lettura analogica completa (raw + volt + percentuale)

La funzione ritorna un oggetto con tre valori: lettura grezza, tensione stimata e percentuale.

/*
  Prof. MAffucci Michele
  13.02.26
  Restituisce più valori raggruppandoli in una struct.
*/

struct LetturaAnalogica {
  int raw;          // 0..1023
  float volt;       // tensione stimata
  int percentuale;  // 0..100
};

const int PIN_INGRESSO = A0;
const float VREF = 5.0; // se usi una scheda a 3.3V, imposta 3.3

LetturaAnalogica leggiAnalogicoCompleto(int pin) {
  LetturaAnalogica risultato;

  risultato.raw = analogRead(pin);
  risultato.volt = risultato.raw * (VREF / 1023.0);
  risultato.percentuale = map(risultato.raw, 0, 1023, 0, 100);

  return risultato;
}

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

void loop() {
  LetturaAnalogica misura = leggiAnalogicoCompleto(PIN_INGRESSO);

  Serial.print("Ingresso A0 -> raw: ");
  Serial.print(misura.raw);

  Serial.print(" | V: ");
  Serial.print(misura.volt, 2);

  Serial.print(" | %: ");
  Serial.println(misura.percentuale);

  delay(500);
}

Abbiamo visto tre strategie diverse per gestire più risultati in una funzione. E’ fondamentale scegliere quale usare nei vostri sketch, è importante capire bene cosa cambia tra passaggio per valore, passaggio per riferimento e uso di una struct.

Riassumo di seguito.

Passaggio dei valori a una funzione

Quando viene chiamata una funzione, potete consegnare i dati in due modi principali:

  1. Per valore (default)
  • la funzione riceve una copia dei parametri;
  • se dentro la funzione modificate quei parametri, state modificando la copia, non le variabili originali;
  • risultato: fuori dalla funzione i valori non cambiano.
  1. Per riferimento (&) — tipico in Arduino perché è C++

  • la funzione riceve un alias delle variabili originali;
  • dentro la funzione, quando assegnate a = …, state scrivendo proprio nella variabile chiamante.
  • risultato: fuori dalla funzione i valori cambiano davvero.

Nota: in C “puro” non esistono i riferimenti, quindi si userebbero puntatori (int *a, int *b). Arduino, però, compila in C++: i riferimenti sono disponibili.

Quando usare una struct (o un tipo “aggregato”)

La struct è utile quando:

  • volete trattare più valori come un unico oggetto logico (es. coordinate, min/max, stato di sensori, ecc.);
  • volete restituire più informazioni in un colpo solo (una sola return, ma con più campi dentro);
  • volete migliorare leggibilità: c.primo, c.secondo comunica il significato meglio di due variabili scollegate.

Costo/beneficio nell’uso della struct

  • Pro: codice più espressivo, dati “impacchettati”, interfaccia pulita.
  • Contro: c’è una copia della struct quando la passi/ritorni (di solito trascurabile se la struct è piccola). Su microcontrollori molto limitati, per struct grandi conviene passare per riferimento anche la struct.

Come detto ad inizio di questo post, nella prossima lezione vedremo una quarta variante molto diffusa nel mondo embedded: gli output parameters con puntatori (stile C) e una versione più moderna che usa const per rendere immediatamente chiaro quali dati sono solo in ingresso e quali vengono modificati dalla funzione.

Buon Coding a tutti 🙂

Dietro le quinte del corso “Robotica educativa a basso costo”: da DotBot:bit a Codino

In questi giorni sto lavorando alla preparazione della 6ª edizione del corso di robotica a basso costo ed ho rimesso mano a uno dei “classici” del mio laboratorio: DotBot:bit. Chi segue il sito da tempo lo conosce: nasceva come robottino essenziale per portare in classe micro:bit con un approccio pratico, replicabile e sostenibile.

Perché ripartire da DotBot:bit e perché aggiornarlo

DotBot:bit era nato con un obiettivo molto chiaro: fare robotica con materiali semplici, valorizzando la didattica laboratoriale e la possibilità di iterare rapidamente tra:

idea > prototipo > test > miglioramento.

Oggi lo scenario in classe è ancora più vario: c’è chi lavora con schede e moduli differenti, chi ha la stampa 3D, chi invece preferisce (o deve) restare sul cartone e su soluzioni ultra-economiche. Per questo ho progettato un’evoluzione concreta e “da corso”.

Nasce Codino: una struttura, due piattaforme

La novità si chiama Codino: una struttura aggiornata (derivata da DotBot:bit) pensata per ospitare in alternativa due setup molto diffusi in ambito micro:bit:

  • Ring:bit V2
  • Kitronik :MOVE mini

entrambe le schede hanno form factor compatto.

L’idea è semplice ma didatticamente potente: stessa “scocca” di progetto, due piattaforme, così il corso (e le attività in classe) si adattano ai vincoli reali: disponibilità di materiali, budget, tempi, livello della classe.

Cosa sto preparando per il corso

Durante la preparazione sto lavorando su tre fronti, tutti pensati per diventare attività trasferibili in classe:

  • Adattamento meccanico e modularità
    Montaggi rapidi, sostituzione della scheda senza rifare tutto da capo, riduzione delle parti “critiche” mi riferisco a quelle che si rompono o richiedono manutenzione.
  • Percorsi di prova “a difficoltà crescente”
    Dal primo movimento controllato fino a micro-sfide: traiettorie, correzioni, test su attrito e peso, ragionamento su stabilità e distribuzione delle masse.
  • Codino è pensato per arrivare senza frizioni alle attività che introdurrò in questa edizione più lunga:
    • controllo con gesture recognition via webcam (ML addestrato su gesti)
    • mini-progetti di Sumo
    • primi prototipi di braccio robot (meccanica essenziale + attuazione)
    • Robot semplici realizzati con Arduino

Tutti questi sono i contenuti extra resi possibili dall’estensione a 4 lezioni della 6ª edizione.

Stampa 3D o cartone: stesso progetto, due strade

Come nel DNA di DotBot:bit, anche Codino resta coerente con l’impostazione “basso costo”:

  • per chi ha una stampante 3D: struttura stampabile e facilmente iterabile;
  • per chi non ce l’ha: template e sagome in cartone, per costruire comunque un robot completo e funzionante.

Questo approccio “doppio” è ciò che rende il kit davvero spendibile: non è un modello unico, è un format replicabile.

Se volete seguire l’evoluzione e portarla in classe

Se vi interessa vedere Codino in azione e, soprattutto, costruire un percorso completo da replicare con i tuoi studenti, la 6ª edizione del corso “Creare un kit di robotica educativa a basso costo” parte il 16 gennaio 2026 e prosegue fino al 3 febbraio 2026 (4 incontri).

Nei prossimi giorni pubblicherò anche qualche foto “work in progress” della struttura Codino ed altri robot.

Buon Making a tutti 🙂

I miei corsi per Tecnica della Scuola: Creare un kit di robotica educativa a basso costo 6ª ed.

Ogni anno rivivo la stessa situazione: colleghi motivati, studenti curiosi, qualche kit in laboratorio… e poi l’ostacolo vero, quello che nessun manuale risolve da solo: come rendere la robotica sostenibile, replicabile e davvero “per tutti”, senza dipendere da un unico carrello di materiale costoso o da una singola postazione.

Da qui nasce (e cresce, edizione dopo edizione) “Creare un kit di robotica educativa a basso costo”: un percorso pratico, laboratoriale, pensato per chi vuole costruire un set di robotica essenziale ma potente, personalizzabile e soprattutto portabile in classe con attività pronte.

L’idea chiave del corso è: non comprare un kit, imparare a costruirlo (e a farlo costruire)

Questo webinar non è una “vetrina di strumenti”: è un percorso operativo che vi guiderà a:

  • progettare un robot didattico partendo da zero, con componenti accessibili;
  • impostare un percorso di base su programmazione e robotica, con una metodologia laboratoriale;
  • creare materiali e consegne che puoi riutilizzare ogni anno, adattandole ai tuoi studenti.

Cosa cambia in questa 6ª edizione: più tempo, più attività “effetto laboratorio”

La novità più importante è che questa edizione è stata estesa a 4 lezioni (prima erano 3). Questo tempo in più non è “teoria aggiunta”: è spazio reale per attività che, nella didattica quotidiana, fanno la differenza perché diventano UDA, compiti autentici, mini-progetti e sfide.

In particolare, nella parte avanzata lavoreremo su:

  • controllo del robot con gesture recognition: addestriamo un semplice sistema di Machine Learning che riconosce i gesti dell’utente via webcam, e li trasformiamo in comandi;
  • progettazione e set-up di mini robot Sumo (con focus su regole, arena, test e iterazione);
  • realizzazione di semplici bracci robot (meccanica essenziale + servocomandi + compiti di pick&place).
  • … e molto altro

Robot “stampati” o “di cartone”: il corso resta accessibile anche senza stampante 3D

La struttura dei robot sarà basata su modelli:

  • stampabili in 3D, per chi dispone di stampante;
  • replicabili in cartone (con progetti e sagome), per chi preferisce una soluzione a costo ancora più basso o vuole lavorare in modalità maker “low-tech”.

Questa scelta non è un ripiego: è una strategia didattica. La stessa attività può vivere su materiali diversi, mantenendo invariati gli obiettivi di coding, problem solving e progettazione.

Piattaforme: BBC micro:bit (al centro) e Arduino (per estensioni e ponti)

Il corso nasce con BBC micro:bit come piattaforma centrale (perfetta per avviare coding e robotica anche con studenti al primo approccio). Useremo MakeCode / Blocks per costruire da subito comportamenti significativi e testabili.

Per alcune estensioni e per chi desidera spingersi oltre, porteremo anche esempi e varianti con Arduino, mantenendo però la stessa logica: progetti essenziali, replicabili, sostenibili.

Per micro:bit lavoreremo in particolare con:

  • Ring:bit V2
  • Kitronik :MOVE mini

Struttura del percorso: dalle basi alla costruzione completa

Il corso è progettato per accompagnarvi con progressione chiara (e riusabile in classe):

  1. micro:bit + programmazione a blocchi: basi operative e programmi tipici per gestire un robot (movimento, comandi, logiche).
  2. Tinkercad per la progettazione 3D: modellazione essenziale per creare/riadattare la struttura del robot, con esportazione dei file per stampa 3D o taglio.
  3. assemblaggio: componenti fondamentali (breadboard, motori, sensori, LED) e integrazione HW/SW fino a ottenere un robot funzionante.
  4. lezione extra (novità): ML con webcam per controllo a gesti + Sumo + braccio robot, con indicazioni pratiche su come trasformare tutto in attività valutabili.

Calendario e modalità: 4 webinar live (2 ore ciascuno) + registrazioni

4 incontri in diretta, 2 ore ciascuno, per un totale di 8 ore:

  • Venerdì 16 gennaio 2026 (17:00–19:00)
  • Martedì 20 gennaio 2026 (17:00–19:00)
  • Martedì 27 gennaio 2026 (17:00–19:00)
  • Martedì 3 febbraio 2026 (17:00–19:00)

Il corso è in modalità webinar con docente dal vivo e possibilità di interazione via chat. Inoltre, potrete rivedere le registrazioni senza limiti di tempo e scaricare i materiali dalla piattaforma.

Materiali inclusi: quello che ti serve per replicare in autonomia

Durante il percorso verranno forniti:

  • schede didattiche di approfondimento;
  • file/sorgenti per stampa 3D o taglio delle strutture;
  • codici di programmazione;
  • attività di laboratorio da svolgere con gli studenti.

Il corso è pensato per docenti della primaria (classe quinta) e della secondaria di I e II grado.

È adatto sia a chi parte da zero, sia a chi vuole finalmente mettere ordine e trasformare esperimenti sporadici in un percorso continuativo.

Iscrizione

Se volete iniziare l’anno con un percorso concreto, che vi lascia un kit replicabile e attività spendibili da subito, trovate tutte le informazioni e l’iscrizione nella pagina ufficiale del corso seguendo il link.

N.B. Pubblicherò ulteriori post dove fornirò suggerimenti operativi che vi saranno utili per realizzare con me tutte le attività programmate.

Buon Making a tutti 🙂

Configurare Arduino UNO R4 WiFi per Arduino Cloud

Nelle prossime settimane svolgeremo una serie di attività di laboratorio dedicate alla realizzazione di semplici applicazioni IoT con Arduino Cloud, utilizzando come scheda di riferimento Arduino UNO R4 WiFi. Per lavorare in modo ordinato ed efficace, questa guida introduttiva nasce con un obiettivo preciso: accompagnare gli studenti passo dopo passo nella configurazione della scheda e nel collegamento al Cloud, così da essere operativo fin da subito.

Queste attività sono importanti perché ci permetteranno di passare dal classico circuito “che funziona sul banco” a un sistema realmente utile e osservabile: potremo monitorare da computer o da dispositivi mobili lo stato dei sensori (temperatura, luce, presenza, umidità, ecc.), visualizzare i dati su dashboard, registrare misure nel tempo e, quando necessario, intervenire da remoto attivando uscite e comandi (ad esempio LED, relè o attuatori). In altre parole, costruiremo piccoli prototipi che riproducono lo stesso schema operativo di molte soluzioni industriali e domotiche: sensore > rete > dashboard > controllo, con attenzione a affidabilità, leggibilità dei dati e sicurezza di base.

Questa guida è tratta dalla guida ufficiale su cui ho apportato alcune mie modifiche e le ho rese “più didattiche”.

Di seguito dettaglio la procedura essenziale per portare Arduino UNO R4 WiFi “online” collegandola all’Arduino Cloud.

Arduino UNO R4 WiFi, come già dettagliato in precedenti tutorial, integra un modulo radio dedicato (ESP32-S3) che mette a disposizione la connettività WiFi: grazie a questo componente la scheda può collegarsi a Internet e dialogare con i servizi Cloud senza hardware aggiuntivo. Da qui nasce il passaggio chiave dei nostri laboratori: non ci limiteremo a far “funzionare un circuito”, ma impareremo a pubblicare dati, visualizzarli su una dashboard e, quando serve, inviare comandi da remoto.

Al termine della configurazione potrete:

  • caricare sketch anche via rete (OTA, over-the-air) quando previsto dal flusso Cloud,
  • costruire dashboard consultabili da PC o smartphone,
  • monitorare variabili e sensori in tempo reale e, in prospettiva, realizzare prototipi utili per domotica, automazione e telemetria.

Cosa serve

  • Arduino UNO R4 WiFi
  • Arduino Cloud (account gratuito e accesso alla piattaforma)

Configurazione e procedura

Prima di iniziare, se non avete mai utilizzato Arduino Cloud, vi consiglio di dare un’occhiata alla guida “Getting Started With the Arduino Cloud” che vi aiuterà a capire i concetti base e vi farà risparmiare tempo durante la configurazione, però durante le lezioni che seguiranno vi fornirò tutte le indicazioni.

Continua a leggere

Arduino esercizi – Base marziana ELYSIUM – correzione fase 1 – solo pulsante

In riferimento alla lezione: Arduino esercizi – Base marziana ELYSIUM, riporto di seguito tre possibili soluzioni, nei commenti di ogni sketch la spiegazione sul funzionamento.

Fase 1 – solo pulsante

// Prof. Maffucci Michele
// data 12.11.25
// Base marziana ELYSIUM - fase 1 - solo pulsante

const int PIN_LED = 9;
const int PIN_PULSANTE = 2;  // pull-down esterno: HIGH quando premuto

void setup() {
  pinMode(PIN_LED, OUTPUT);
  pinMode(PIN_PULSANTE, INPUT);  // nessun pull-up interno
  digitalWrite(PIN_LED, LOW);
}

void loop() {
  int statoPulsante = digitalRead(PIN_PULSANTE);

  if (statoPulsante == HIGH) {    // pulsante premuto
    digitalWrite(PIN_LED, HIGH);  // accendo la luce di cortesia
    delay(1500);                  // 1,5 s
    digitalWrite(PIN_LED, LOW);  // spengo
    delay(50);                   // piccolo anti-rimbalzo
  }
}

Con questa soluzione alla pressione del pulsante il LED si accende e se rilasciate si spegne, però se mantenete premuto il pulsante notare un veloce spegnimento ed una riaccensione del LED ovvero ciò che stiamo facendo e riaccendere immediatamente il LED.

Per evitare ciò bisogna implementare la versione che evita il retrigger.

Evitare il retrigger significa impedire che, tenendo il pulsante premuto, il ciclo riparta subito dopo lo spegnimento del LED e lo riaccenda di nuovo. Per evitarlo, dopo la sequenza (LED acceso per 1,5 s > LED spento) si controlla se il pulsante è ancora premuto: finché lo è, si rimane dentro un ciclo while bloccante che attende il rilascio. Solo quando il pulsante torna allo stato di riposo si esce dal while, si applica un breve anti-rimbalzo (es. 50 ms) e il programma riprende il loop normale.

ATTENZIONE

il while è bloccante ciò vuol dire che blocca l’intera esecuzione del programma. Va benissimo per questo esercizio semplice; in progetti più complessi è preferibile preferisce rilevare il fronte di salita (variabile di stato) o un approccio non bloccante con millis() che ancora non conoscete (mi sto rivolgendo ai ragazzi di 3′) e che vedremo più avanti.

Fase 1 – solo pulsante – evita il retrigger con while bloccante

// Prof. Maffucci Michele
// data 12.11.25
// Base marziana ELYSIUM - fase 1B - solo pulsante
// evitare il retrigger

const int PIN_LED = 9;
const int PIN_PULSANTE = 2;  // pull-down esterno: HIGH quando premuto

void setup() {
  pinMode(PIN_LED, OUTPUT);
  pinMode(PIN_PULSANTE, INPUT);  // nessun pull-up interno
  digitalWrite(PIN_LED, LOW);
}

void loop() {
  int statoPulsante = digitalRead(PIN_PULSANTE);

  if (statoPulsante == HIGH) {    // pulsante premuto
    digitalWrite(PIN_LED, HIGH);  // accendo la luce di cortesia
    delay(1500);                  // 1,5 s
    digitalWrite(PIN_LED, LOW);   // spengo

    // attendo il rilascio per evitare retrigger continui
    while (digitalRead(PIN_PULSANTE) == HIGH) {
      delay(10);
    }
    delay(50);                    // piccolo anti-rimbalzo
  }
}

Attenzione alla parte di codice:

...
while (digitalRead(PIN_PULSANTE) == HIGH) {
      delay(10);
    }
...

potreste essere tentati di scrivere:

...
while (statoPulsante == HIGH) {
      delay(10);
    }
...

se agite in questo modo, dopo la prima accensione e il successivo spegnimento non riuscirete più ad accendere il LED con successive pressioni del pulsante.

Considerate statoPulsante come uno “scatto fotografico” fatto ad inizio loop(), pertanto se scrivete:

while (statoPulsante == HIGH) { ... }

la condizione resta sempre vera (perché non non viene mai aggiornata).
Risultato: si resta bloccati per sempre in quel while dopo la prima attivazione > il LED si accende una volta e poi il programma non torna più al loop().

Fase 1 – solo pulsante – evita il retrigger senza while bloccante

Fornisco un’altra soluzione in cui non si fa uso del while bloccante.

Invece di “restare fermi” dentro un while finché il pulsante non viene rilasciato, leggiamo il pulsante ad ogni giro del loop e facciamo scattare l’azione solo quando cambia da NON premuto a premuto. Questo momento si chiama fronte di salita.

// Prof. Maffucci Michele
// data 12.11.25
// Base marziana ELYSIUM - fase 1C - solo pulsante
// evitare il retrigger senza while bloccante
// con variabile di stato

const int PIN_LED      = 9;
const int PIN_PULSANTE = 2;   // pull-down esterno: HIGH = premuto

bool premutoPrima = false;

void setup() {
  pinMode(PIN_LED, OUTPUT);
  pinMode(PIN_PULSANTE, INPUT);   // nessun pull-up interno
}

void loop() {
  // Snapshot della lettura
  int statoPulsante = digitalRead(PIN_PULSANTE);

  // Converto HIGH/LOW in booleano
  bool premuto;
  if (statoPulsante == HIGH) {
    premuto = true;    // pulsante premuto
  } else {
    premuto = false;   // pulsante rilasciato
  }

  // Fronte di salita: ora premuto, prima no
  if (premuto && !premutoPrima) {
    digitalWrite(PIN_LED, HIGH);
    delay(1500);
    digitalWrite(PIN_LED, LOW);
    delay(50); // anti-rimbalzo semplice
  }

  // Aggiorna lo stato per il prossimo giro
  premutoPrima = premuto;
}

Come funziona il programma

  • premuto = stato attuale del pulsante (TRUE se è premuto ora, FALSE altrimenti).
  • premutoPrima = stato del pulsante al giro precedente del loop.
  • Condizione di innesco: premuto == true e premutoPrima == false > significa “hai appena iniziato a premere”.

Quando la condizione è vera:

  1. esegui l’azione (es. LED acceso 1,5 s, poi spento),
  2. non restate bloccato: il loop continua a girare,
  3. finché tenete premuto, la condizione non si ripete (perché ora premutoPrima è anch’esso TRUE),
  4. quando rilasciate, premuto torna FALSE; alla prossima pressione avrete di nuovo il fronte di salita.

Questa soluzione offre i seguenti vantaggi:

  • Niente blocchi: il programma continua a leggere ingressi/aggiornare uscite mentre il LED è stato gestito (utile in progetti più grandi).
  • No retrigger con pressione prolungata: scatta una sola volta all’inizio della pressione.
  • Struttura “a eventi”: reagisce ai cambiamenti, non al livello costante del pulsante.

Di seguito la tabella “fronte di salita” (versione con pull-down: HIGH = premuto)

Fase Descrizione Lettura pin premuto (ora) premutoPrima (prima) Condizione premuto && !premutoPrima Azione
T0 Riposo prima della pressione LOW FALSE FALSE FALSE Nessuna
T1 Inizio pressione (fronte salita) HIGH TRUE FALSE TRUE Esegui sequenza LED
T2 Tenuto premuto HIGH TRUE TRUE FALSE Nessuna
T3 Rilascio LOW FALSE TRUE FALSE Nessuna (solo aggiornamento stato)
T4 Nuova pressione (nuovo fronte) HIGH TRUE FALSE TRUE Esegui sequenza LED

Buon Coding a tutti 🙂