Archivi tag: coding

Arduino – istruzione while – soluzione esercizi proposti

In riferimento alla lezione: “Arduino – while: ripetere finché la condizione è vera (controllo in ingresso)“, pubblico una possibile soluzione agli esercizi proposti.

Ho utilizzato come finecorsa un pulsante.

Soluzione esercizio 01 – Validazione di input numerico (range 1..10)

Chiedere all’utente un numero intero tra 1 e 10. Finché l’input non è valido, ripetere la richiesta (usare while). Quando è valido, stampare “Valore accettato: X”.

/*
  Prof. Maffucci Michele
  data: 03.11.25

  Esercizio 1 — Validazione input (1..10) con while
  - Chiede un intero all'utente
  - Finché il valore NON è nel range [1..10], lo richiede
*/

void setup() {
  Serial.begin(9600);
  Serial.setTimeout(5000); // tempo max attesa lettura riga
  Serial.println("Inserisci un numero intero tra 1 e 10:");
}

void loop() {
  bool valido = false;
  int numero = 0;

  // Continua finché non ottieni un valore valido
  while (!valido) {
    if (Serial.available() > 0) {
      // leggi una riga intera e converti
      String riga = Serial.readStringUntil('\n');

      // rimuove spazi/a-capo all’inizio e alla fine (es. '\r', '\n', ' ')
      riga.trim();

      if (riga.length() > 0) {  // verifica che l’utente abbia scritto qualcosaa
        numero = riga.toInt();  // converte in intero; '12abc' -> 12, 'abc'/'': 0
        if (numero >= 1 && numero <= 10) {
          valido = true; // uso del valore
        } else {
          Serial.println("Valore non valido. Reinserisci (1..10):");
        }
      }
    }
  }

  Serial.print("Valore accettato: ");
  Serial.println(numero);

  while (true) {}  // fine dimostrazione
}

Soluzione esercizio 02 – Algoritmo di Euclide (MCD) con while

Leggere due interi positivi a e b dal Serial Monitor e calcolare il Massimo Comun Divisore usando il metodo di Euclide:
ripetere finché b != 0 la sostituzione a, b = b, a % b. Alla fine stampare MCD = a.

Ripasso:

Cos’é il MCD (Massimo Comun Divisore)

Il MCD tra due interi positivi a e b è il più grande intero che divide entrambi senza resto.

Esempi:

  • MCD(8, 12) = 4
  • MCD(18, 24) = 6

Algoritmo di Eulide

Ripetere finché b != 0;

  1. calcola resto r = a mod b
  2. sposta i valori: a <- b, b <- r
    Quando b = 0, il MCD è a
/*
  Prof. Maffucci Michele
  data: 03.11.25

  Esercizio 2 — MCD con algoritmo di Euclide usando while
  - Legge due interi positivi a e b
  - Esegue: mentre (b != 0) { resto = a % b; a = b; b = resto; }
  - Stampa il MCD = a
*/

void setup() {
  Serial.begin(9600);
  Serial.setTimeout(20000);
  Serial.println("Calcolo MCD (Euclide). Inserisci due interi positivi separati da invio:");
  Serial.println("Valore a:");
}

void loop() {
  long a = 0, b = 0;

  // Leggi 'a'
  while (a <= 0) { if (Serial.available() > 0) {

      // Legge dal Serial Monitor il secondo intero (a).
      // Attende l’arrivo di cifre, ignora spazi/CR/LF iniziali,
      // Se non trova numeri, restituisce 0 (entro il Timeout).
      a = Serial.parseInt();
      while (Serial.available() > 0)
        Serial.read();  // pulizia CR/LF
      if (a <= 0) Serial.println("Valore non valido. Reinserisci a (>0):");
    }
  }

  Serial.println("Valore b:");
  // Leggi 'b'
  while (b <= 0) { if (Serial.available() > 0) {

      // Legge dal Serial Monitor il secondo intero (b).
      // Attende l’arrivo di cifre, ignora spazi/CR/LF iniziali,
      // Se non trova numeri, restituisce 0 (entro il Timeout).
      b = Serial.parseInt();
      while (Serial.available() > 0)
        Serial.read();
      if (b <= 0) Serial.println("Valore non valido. Reinserisci b (>0):");
    }
  }

  // Algoritmo di Euclide con while
  long resto = 0;
  while (b != 0) {
    resto = a % b;  // resto della divisione
    a = b;          // shift dei valori
    b = resto;
  }

  Serial.print("MCD = ");
  Serial.println(a);  // quando b==0, a è il MCD

  while (true) {}  // fine dimostrazione
}

Soluzione esercizio 03 – Servo: scansione fino al finecorsa (stato controllato con while)

Collegare un servo (pin D9) e un finecorsa su D2 (con INPUT_PULLUP). Far ruotare il servo dal minimo verso il massimo (per passi di 2–3 gradi) finché il finecorsa NON viene premuto (LOW). Quando il finecorsa si attiva, fermarsi e stampare l’angolo raggiunto.

/*
  Prof. Maffucci Michele
  data: 03.11.25

  Esercizio 3 — Servo: scansione finché NON scatta il finecorsa
  - Servo su D9
  - Finecorsa su D2 (INPUT_PULLUP): NON premuto = HIGH, premuto = LOW
  - All'avvio di Arduino il servo aumenta l'angolo
    finché il finecorsa non diventa LOW (premuto)
  - Sulla Serial Monitor viene mostrato l'angolo percorso
*/

#include <Servo.h> 

const int PIN_SERVO = 9;
const int PIN_FINECORSA = 2;  // collegato a GND, usare INPUT_PULLUP

Servo servoBraccio;

void setup() {
  Serial.begin(9600);
  pinMode(PIN_FINECORSA, INPUT_PULLUP);
  servoBraccio.attach(PIN_SERVO);

  // Porta il servo all'angolo di partenza (0°)
  servoBraccio.write(0);
  delay(500);
  Serial.println("Inizio scansione servo: attendo finecorsa...");
}

void loop() {
  int angolo = 0;

  // Scansione: finché il finecorsa NON è premuto (HIGH), prosegui
  while (digitalRead(PIN_FINECORSA) == HIGH && angolo <= 180) {
    servoBraccio.write(angolo);
    delay(15);         // piccolo tempo per muovere il servo
    angolo += 2;       // passo di scansione
  }

  // Se siamo usciti perché premuto, stampiamo l'angolo
  Serial.print("Finecorsa attivato a ~");
  Serial.print(angolo);
  Serial.println(" gradi (valore approssimativo).");

  // Ferma la dimostrazione: lascia il servo dove si è fermato
  while (true) {}
}

Variante all’esercizio 03

Aggiungo una variante all’esercizio 3 in cui alla pressione di un secondo pulsante, il servo torna a 0° e riparte la scansione.

  • Pulsante finecorsa su D2 (INPUT_PULLUP): quando viene premuto (LOW) la scansione si ferma e si mostra l’angolo raggiunto.
  • Pulsante reset su D3 (INPUT_PULLUP): se premuto in qualunque momento:
    • il servo torna a 0°,
    • la scansione riparte da capo.
/*
  Prof. Maffucci Michele
  data: 03.11.25

  Variante — Servo: scansione fino al finecorsa con pulsante di reset
  - Servo su D9
  - Finecorsa su D2 (INPUT_PULLUP): NON premuto = HIGH, premuto = LOW
  - Pulsante RESET su D3 (INPUT_PULLUP): premuto = LOW
  - Comportamento:
      * Aumenta l'angolo 0 -> 180 a passi di 2° finche' il finecorsa NON è premuto.
      * In QUALSIASI momento, se si preme RESET: il servo torna a 0° e la scansione riparte.
      * Se il finecorsa scatta: stampa l'angolo e attende RESET per ricominciare.

  Note:
  - Debounce semplice con piccoli delay.
  - while(...) usato come "controllo in ingresso" su eventi esterni (finecorsa/reset).
*/

#include <Servo.h>

const int PIN_SERVO = 9;
const int PIN_FINECORSA = 2;  // a GND, usare INPUT_PULLUP
const int PIN_RESET = 3;      // a GND, usare INPUT_PULLUP
const int PASSO_GRADI = 2;

Servo servoBraccio;

void tornaAZeroERiparti() {
  // Porta il servo a 0° e attende il rilascio del pulsante RESET
  servoBraccio.write(0);
  delay(200);
  // attesa rilascio per evitare ri-trigger immediati
  while (digitalRead(PIN_RESET) == LOW) { /* aspetta rilascio */
  }
  delay(30);  // debounce
  Serial.println("RESET: servo a 0° — riparte la scansione.");
}

void setup() {
  Serial.begin(9600);
  pinMode(PIN_FINECORSA, INPUT_PULLUP);
  pinMode(PIN_RESET, INPUT_PULLUP);

  servoBraccio.attach(PIN_SERVO);
  servoBraccio.write(0);
  delay(400);

  Serial.println("Scansione servo: finecorsa su D2, RESET su D3.");
  Serial.println("Premi RESET per tornare a 0° e ripartire; premi il finecorsa per fermare.");
}

void loop() {
  // 01. Posizione iniziale
  int angolo = 0;
  servoBraccio.write(angolo);
  delay(150);

  // 02. Ciclo di scansione: continua FINCHE' il finecorsa NON è premuto
  while (digitalRead(PIN_FINECORSA) == HIGH && angolo <= 180) {
    // Se in qualunque momento premo RESET: torna a 0° e riparte da capo
    if (digitalRead(PIN_RESET) == LOW) {
      delay(30);                            // debounce
      if (digitalRead(PIN_RESET) == LOW) {  // ancora premuto? conferma
        tornaAZeroERiparti();               // porta a zero e gestisci rilascio
        angolo = 0;                         // riparte da 0°
        continue;                           // salta al prossimo giro del while
      }
    }

    // Avanza l'angolo e muove il servo
    servoBraccio.write(angolo);
    delay(15);  // tempo per il movimento
    angolo += PASSO_GRADI;
  }

  // 03. Se esco dal while perche' il finecorsa è premuto (LOW), stampo l'angolo
  if (digitalRead(PIN_FINECORSA) == LOW) {
    // attendo che il pulsante finecorsa venga rilasciato prima di procedere
    delay(30);  // debounce
    Serial.print("Finecorsa attivato a ~");
    Serial.print(angolo);
    Serial.println(" gradi. Premi RESET per ricominciare.");

    // Attendo RESET per ripartire
    while (digitalRead(PIN_RESET) == HIGH) {
      // attesa "passiva": qui potreste far lampeggiare un LED o mostrare stato
      // (teniamo il servo dove si è fermato)
    }
    // debounce e rilascio
    delay(30);
    while (digitalRead(PIN_RESET) == LOW) {
      /* aspetta rilascio */
    }
    delay(30);

    // Riporta a zero e riparte automaticamente al prossimo loop()
    servoBraccio.write(0);
    delay(200);
    Serial.println("Ripartenza da 0°...");
  }

  // Se si è arrivati oltre 180° senza finecorsa, chiedi RESET per ripartire
  if (angolo > 180 && digitalRead(PIN_FINECORSA) == HIGH) {
    Serial.println("Limite 180° raggiunto senza finecorsa. Premi RESET per ripartire.");
    while (digitalRead(PIN_RESET) == HIGH) {
      /* attesa */
    }
    delay(30);
    while (digitalRead(PIN_RESET) == LOW) {
      /* rilascio */
    }
    servoBraccio.write(0);
    delay(200);
  }

  // Il loop ricomincia: nuova scansione partirà da 0°
}

Buon lavoro 🙂

Arduino esercizi – Base marziana ELYSIUM

Quest’anno sto supportando, con attività di sperimentazione pratica, anche classi non mie, proponendo laboratori motivanti per riaccendere curiosità e partecipazione. La prossima settimana lavorerò con una terza elettronica numerosa ed eterogenea, non semplice da gestire nei laboratori tradizionali. Per questo, insieme ai colleghi, abbiamo scelto di spostare la lezione nel Laboratorio Territoriale (LTO), uno spazio ricco di attrezzature “inusuali” per un normale laboratorio scolastico: il contesto giusto per stuzzicare l’attenzione e rendere concreti gli apprendimenti su Arduino.

Stato iniziale della classe:

  • hanno svolto le primissime prove con Arduino (blink e poco altro);
  • conoscono le variabili e hanno un’idea dei tipi di dato;
  • sanno riconoscere e usare pulsanti, interruttori, deviatori e LED.

L’obiettivo della lezione è mettere insieme questi elementi in un compito pratico, semplice ma significativo: progettare e realizzare un piccolo controller di luce che usa pulsante, interruttore/deviatore e LED, approfondendo al tempo stesso nozioni base di programmazione (lettura ingressi, gestione del tempo con millis() o delay(), piccola logica a stati AUTO/MANUALE).

Desidero che gli studenti vedano subito un risultato, si divertano a farlo funzionare e, passo dopo passo, consolidino lessico, metodo e fiducia.

Questa lezione sarà suddivisa in tre parti, due introduttive (ed ognuna con attività aggiuntive facoltative) che condurranno alla terza parte conclusiva in cui gli studenti dovranno realizzare il sistema completo.

In ogni fase viene mostrato:

  • diagramma di flusso;
  • pseudocodice;
  • schema di collegamento;

Lo studente deve realizzare lo sketch Arduino di ogni fase.

Come sempre amo introdurre il problema usando una narrazione coinvolgente e visto che mi piace la fantascienza andremo con i ragazzi su Marte. 🙂

Base marziana ELYSIUM

Siete il tecnico elettronico della base marziana. È stato inaugurato un nuovo modulo abitativo e, durante lo spostamento di carichi nel tunnel pressurizzato che collega la serra alla cupola, un carrello radiocomandato ha urtato la parete, provocando una micro-frattura con inizio di depressurizzazione. L’intervento rapido di un manutentore ha evitato il peggio. Dalle analisi emergono due cause principali: affaticamento del personale in turno e illuminazione insufficiente nel corridoio. Per prevenire incidenti e ridurre i consumi, il comando vi incarica di realizzare un controller luce “low-power”: in AUTO la luce si accende per pochi secondi quando viene rilevato il passaggio (lo simulerete con un pulsante); in MANUALE rimane attiva per la durata delle ispezioni EVA (Extra-Vehicular Activity). Il progetto deve essere affidabile, parsimonioso nei consumi e conforme agli standard di sicurezza della base, così da superare senza riserve la verifica dei consumi energetici imminente.

Per evitare che un bug in un solo componente comprometta l’intero sistema, decidete di scomporre il problema e validare separatamente i due comportamenti critici, prima di integrarli.

  1. Prima prova – “Solo PULSANTE” (sensore di passaggio)
    Nel banco di test allestito accanto al tunnel, il tecnico simula l’evento di passaggio con un pulsante. Alla pressione del pulsante la luce si accende un un determinato tempo e poi si spegne.
    (Facoltativo) Sperimentate anche la variante in cui la luce reagisca con un ping di cortesia temporizzato e che non riparta finché il pulsante resta premuto (niente retrigger). Questo serve a scongiurare illuminazioni troppo lunghe dovute a rimbalzi o pressioni involontarie: energia salva, rischio ridotto.
  2. Seconda prova – “Solo INTERRUTTORE” (modalità MANUALE)
    Il tecnico realizza il comando manuale: ON = luce stabile, OFF = buio. Qui controlla la priorità operativa.
    (Facoltativo) Sperimentate anche il feedback all’inserimento, due lampeggi rapidi. Quando si passa a ON: due lampeggi veloci e poi resta acceso.

Superate queste due verifiche, il tecnico procede alla versione finale: unendo AUTO (pulsante) e MANUALE (interruttore) nello stesso controller, con la regola d’oro: MANUALE ha sempre priorità. È così che il progetto rispetta il protocollo Zero-Disturb e arriva pronto alla verifica dei consumi energetici.


Prima prova – “Solo PULSANTE” (sensore di passaggio)

Esercizio: realizzare lo sketch Arduino che permette di realizzare questa prima prova.

Diagramma di flusso

Pseudocodice

INIZIO
  imposta PIN_LED come USCITA
  imposta PIN_PULSANTE come INGRESSO
  scrivi PIN_LED = LOW

  RIPETI PER SEMPRE
    statoPulsante ← leggi(PIN_PULSANTE)   // HIGH = premuto (pull-down esterno)

    SE statoPulsante è HIGH ALLORA
      scrivi PIN_LED = HIGH
      attesa 1500 ms
      scrivi PIN_LED = LOW
      attesa 50 ms
    FINE SE
  FINE RIPETI
FINE

Schema di collegamento

 


Prima prova – “Solo PULSANTE” (sensore di passaggio) – variante che evita il retrigger

Esercizio: realizzare lo sketch Arduino che permette di realizzare la variante della prima prova.

    • Premo > LED acceso ~1,5 s > poi spento.
    • Tenendo premuto non riparte; riparte solo dopo il rilascio.

Diagramma di flusso – versione che evita il retrigger

Pseudocodice – versione che evita il retrigger

INIZIO
  // Setup
  imposta PIN_LED come USCITA
  imposta PIN_PULSANTE come INGRESSO
  scrivi PIN_LED = LOW

  RIPETI PER SEMPRE   // loop
    statoPulsante ← leggi(PIN_PULSANTE)

    SE statoPulsante è HIGH ALLORA           // pulsante premuto
      scrivi PIN_LED = HIGH                  // accendi luce di cortesia
      attesa 1500 ms                         // delay 1,5 s
      scrivi PIN_LED = LOW                   // spegni

      // attesa rilascio per evitare retrigger continui
      MENTRE leggi(PIN_PULSANTE) è HIGH FAI
        attesa 10 ms
      FINE MENTRE

      attesa 50 ms                           // piccolo anti-rimbalzo
    FINE SE
  FINE RIPETI
FINE

Seconda prova – “Solo INTERRUTTORE” (modalità MANUALE)

Esercizio: realizzare lo sketch Arduino che permette di realizzare questa seconda prova.

  • Interruttore ON > LED acceso fisso. (1500 ms)
  • Interruttore OFF > LED spento.

Prevedere un anti-rimbalzo realizzato con un delay di 50 ms.

Diagramma di flusso

Pseudocodice

INIZIO
  imposta PIN_LED come USCITA
  imposta PIN_PULSANTE come INGRESSO
  scrivi PIN_LED = LOW

  RIPETI PER SEMPRE
    statoPulsante ← leggi(PIN_PULSANTE)   // HIGH = premuto (pull-down esterno)

    SE statoPulsante è HIGH ALLORA
      scrivi PIN_LED = HIGH
      attesa 1500 ms
      scrivi PIN_LED = LOW
      attesa 50 ms
    FINE SE
  FINE RIPETI
FINE

Diagramma di flusso

Pseudocodice

INIZIO
  // Setup
  imposta PIN_LED come USCITA
  imposta PIN_INTER come INGRESSO
  scrivi PIN_LED = LOW

  RIPETI PER SEMPRE    // loop
    statoInterruttore ← leggi(PIN_INTER)   // HIGH = MANUALE ON (con pull-down esterno)

    SE statoInterruttore è HIGH ALLORA
      scrivi PIN_LED = HIGH               // LED acceso
    ALTRIMENTI
      scrivi PIN_LED = LOW                // LED spento
    FINE SE
  FINE RIPETI
FINE

Schema di collegamento


Seconda prova (facoltativo) – “Solo INTERRUTTORE” (modalità MANUALE) – variante con feedback all’inserimento

Per il feedback prevedere un accensione e spegnimento del LED per 3 cicli da 80 ms

Diagramma di flusso

Pseudocodice – variante con feedback all’inserimento

INIZIO
  // Setup
  imposta PIN_LED come USCITA
  imposta PIN_INTER come INGRESSO    // pull-down esterno
  scrivi PIN_LED = LOW
  statoPrec ← LOW

  RIPETI PER SEMPRE
    stato ← leggi(PIN_INTER)          // HIGH = MANUALE ON

    // Rilevazione fronte di salita (OFF -> ON)
    SE (stato = HIGH) AND (statoPrec = LOW) ALLORA
      PER i da 0 a 1 FAI               // due lampeggi rapidi
        scrivi PIN_LED = HIGH
        attesa 80 ms
        scrivi PIN_LED = LOW
        attesa 80 ms
      FINE PER
    FINE SE

    // Stato stabile della modalita MANUALE
    SE (stato = HIGH) ALLORA
      scrivi PIN_LED = HIGH
    ALTRIMENTI
      scrivi PIN_LED = LOW
    FINE SE

    statoPrec ← stato
  FINE RIPETI
FINE

Realizzazione del sistema completa

Esercizio 03: realizzare lo sketch Arduino che permette di realizzare l’intero sistema.

  • manuale ON? = interruttore HIGH
  • pulsante premuto? = pulsante HIGH
  • timer attivo? = millis() prima di tScadenza (luce ancora entro i 5 s)

Diagramma di flusso

Pseudocodice

INIZIO
  // Setup
  imposta PIN_LED come USCITA
  imposta PIN_PULSANTE come INGRESSO
  imposta PIN_INTER come INGRESSO
  scrivi PIN_LED = LOW
  tScadenza ← 0
  DURATA_MS ← 5000

  RIPETI PER SEMPRE   // loop
    // Lettura ingressi
    statoInterruttore ← leggi(PIN_INTER)      // HIGH = MANUALE ON (pull-down esterno)
    statoPulsante     ← leggi(PIN_PULSANTE)   // HIGH = pulsante premuto

    // Significato applicativo
    manuale ← (statoInterruttore è HIGH)
    premuto ← (statoPulsante è HIGH)

    SE manuale ALLORA
      scrivi PIN_LED = HIGH                   // LED acceso fisso in MANUALE
    ALTRIMENTI
      SE premuto ALLORA
        tScadenza ← tempoCorrenteMs() + DURATA_MS
      FINE SE

      SE tempoCorrenteMs() < tScadenza ALLORA // timer attivo?
        scrivi PIN_LED = HIGH                 // LED acceso (luce di cortesia)
      ALTRIMENTI
        scrivi PIN_LED = LOW                  // LED spento
      FINE SE
    FINE SE
  FINE RIPETI
FINE

Schema di collegamento

 

Buon Coding a tutti 🙂

Arduino – while: ripetere finché la condizione è vera (controllo in ingresso)

while esegue un blocco finché la condizione è vera, ma controlla prima di entrare. È perfetto per attese (pulsante premuto, dato disponibile), conteggi con condizione di uscita, e loop che possono saltare del tutto se la condizione è già falsa.

Sintassi

while (condizione) {
  // esegue se (e finché) condizione è vera
}
  • se la condizione è falsa all’inizio, il corpo non la esegue nemmeno una volta;
  • molto usato per polling di eventi e attese non bloccanti (con accortezza sui ritardi).

I primi tre esempi che seguono, propongono funzionalità molto simili a quelle realizzate nella lezione precedente sull’uso del do..while. Come sempre nei commenti le spiegazioni di approfondimento.

Esempio 01 – Attendere la pressione di un pulsante

Utilizziamo un pulsante connesso al pin 2 ed abilitiamo la resistenza di bull-up.

/*
  Prof. Maffucci Michele
  data: 28.10.25

  Premere il pulsante su D2 per proseguire;
  LED lampeggia durante l’attesa.
*/

const int PIN_BTN = 2;  // collegato a GND con INPUT_PULLUP

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(PIN_BTN, INPUT_PULLUP);
  Serial.begin(9600);
  Serial.println("Premi il pulsante per continuare...");
}
void loop() {
  // Attesa attiva: finché il pulsante NON è premuto
  while (digitalRead(PIN_BTN) == HIGH) {  // HIGH = non premuto (pull-up)
    digitalWrite(LED_BUILTIN, HIGH);
    delay(150);
    digitalWrite(LED_BUILTIN, LOW);
    delay(150);
  }
  Serial.println("Pulsante premuto: procedo!");
  while (true) {}
}

Esempio 02 – Countdown finché t > 0

Utilizziamo un buzzer connesso al pin 5.

/*
  Prof. Maffucci Michele
  data: 28.10.25

  Countdown da 10 a 0; beep a fine conteggio (buzzer su D5)
*/

const int PIN_BUZ = 5;
void setup() {
  Serial.begin(9600);
  pinMode(PIN_BUZ, OUTPUT);
}
void loop() {
  int t = 10;
  while (t > 0) {
    Serial.print("T-");
    Serial.println(t);
    delay(500);
    t--;
  }
  tone(PIN_BUZ, 880, 300);  // beep
  Serial.println("Decollo!");
  while (true) {}
}

Esempio 03 – Leggere finché c’è un dato sulla seriale (svuotare buffer)

Ogni volta che scriviamo un testo e premiamo invio, questo viene mostrato come output sulla Serial Monitor.

/*
  Prof. Maffucci Michele
  data: 28.10.25

  Svuota il buffer leggendo tutti i caratteri arrivati finché disponibili
*/

void setup() {
  Serial.begin(9600);
  Serial.println("Scrivi qualcosa e premi invio");
}
void loop() {
  if (Serial.available() > 0) {
    Serial.print("Ricevuto: ");
    while (Serial.available() > 0) {
      char c = Serial.read();
      Serial.write(c);  // eco del testo
    }
    Serial.println();
  }
}

Esempio 04 – Conta impulsi in 5 secondi (sensore su D2)

Contiamo quante volte un sensore digitale (fotointerruttore, reed, finecorsa) o un pulsante, cambia stato in una finestra temporale di 5 s. Abilitiamo anche in questo caso la resistenza di pull-up si D2.

/*
/*
  Prof. Maffucci Michele
  data: 28.10.25

  Conta impulsi in 5 secondi (D2)
  - Collega un sensore digitale su D2 (INPUT_PULLUP, attivo-basso).
  - Conta quante transizioni LOW->HIGH avvengono in 5 s.
*/

const int PIN_SENSORE = 2;

void setup() {
  Serial.begin(9600);
  pinMode(PIN_SENSORE, INPUT_PULLUP);
  Serial.println("Pronto. Avvio finestra di misura di 5 secondi...");
}

void loop() {
  // finestra temporale di 5 s (5000 ms) usata con millis() per la misura
  const unsigned long finestra = 5000;
  unsigned long t0 = millis();
  int impulsi = 0;

  int statoPrecedente = digitalRead(PIN_SENSORE);

  // Esegue finché non scadono i 5 secondi
  while (millis() - t0 < finestra) { int stato = digitalRead(PIN_SENSORE); // conta il fronte di salita (LOW -> HIGH con pull-up)
    if (statoPrecedente == LOW && stato == HIGH) {
      impulsi++;
    }
    statoPrecedente = stato;
  }

  Serial.print("Impulsi contati in 5 s: ");
  Serial.println(impulsi);

  // Pausa informativa e ripeti
  delay(1000);
}

Esempio 05 – Leggi una stringa fino al terminatore ‘#’

Vengono raccolti tutti i caratteri inseriti sulla serial Monitor fino al carattere # e successivamente all’invio, viene stampata la frase.

/*
  Prof. Maffucci Michele
  data: 28.10.25

  Lettura stringa con terminatore '#'
  - Scrivi nel Serial Monitor una frase e termina con '#'
  - Il programma raccoglie i caratteri fino a '#', poi stampa la frase.
  - Imposta 9600 baud. "No line ending" consigliato.
*/

void setup() {
  Serial.begin(9600);
  Serial.println("Digita una frase e termina con '#':");
}

void loop() {
  // Attende che arrivi almeno un carattere per partire
  // Nessun dato in arrivo: esci da loop() e riprova al prossimo ciclo (evita di eseguire il resto del codice)
  if (Serial.available() == 0)
    return;

  String frase = "";
  char c = '\0';

  // Leggi finché non arriva il terminatore '#'
  while (c != '#') {
    
    // Se non c'è ancora nulla, continua ad attendere, ricontrolla il buffer al prossimo ciclo di while
    // Con continue si passa subito alla prossima iterazione del while
    // (cioè al prossimo controllo della condizione c != '#'),
    // senza eseguire le istruzioni che seguono nel corpo del ciclo.
    if (Serial.available() == 0)
      continue;

    c = Serial.read();
    if (c == '\r' || c == '\n')
      continue;                // ignora a capo
                               // salta il resto e riparte il ciclo, così non aggiungi \r/\n alla stringa raccolta
    if (c != '#') frase += c;  // accumula solo se non è il terminatore
  }

  Serial.print("Hai scritto: ");
  Serial.println(frase);

  // Pulisci eventuali residui e proponi una nuova acquisizione
  while (Serial.available() > 0)
    Serial.read();
  Serial.println("Digita una nuova frase terminata da '#':");
}

Ricordare che:

  • return in loop(): termina l’intera iterazione di loop() (si ricomincia dal prossimo giro della funzione loop());
  • continue: termina solo l’iterazione corrente del ciclo in cui si trova (es. il while interno), poi il ciclo riparte al prossimo ciclo del while;
  • alternative senza continue: si può usare una struttura con else { ... }, ma continue mantiene il corpo più lineare quando si vogliono saltare casi particolari.

Esempio 06 – “Dado” mentre il pulsante è premuto (animazione live)

Tenendo premuto un pulsante, si “fa girare” un numero casuale 1–6; finché il pulsante è premuto l’animazione continua, quando si rilascia il numero si blocca (come lanciare un dado).

/*
  Prof. Maffucci Michele
  data: 28.10.25
  
  Dado con pulsante (D2) e animazione in attesa
  - INPUT_PULLUP su D2 (premuto = LOW)
  - Finché il pulsante è PREMUTO, mostra numeri 1..6 che scorrono.
  - Al rilascio, stampa il risultato finale.
*/

const int PIN_BTN = 2;  // collegare pulsante a GND (INPUT_PULLUP)

void setup() {
  Serial.begin(9600);
  pinMode(PIN_BTN, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
  randomSeed(analogRead(A0));  // inizializza generatore
  Serial.println("Tieni premuto il pulsante per 'far girare' il dado, poi rilascia.");
}

void loop() {
  // Attendi pressione (attivo-basso)
  if (digitalRead(PIN_BTN) == HIGH)
    return;

  int ultimoNumero = 1;

  // Finché il pulsante rimane premuto, scorrono i numeri
  while (digitalRead(PIN_BTN) == LOW) {
    ultimoNumero = random(1, 7);  // 1..6
    Serial.print("Dado: ");
    Serial.println(ultimoNumero);

    // piccolo effetto visivo sul LED
    digitalWrite(LED_BUILTIN, HIGH);
    delay(60);
    digitalWrite(LED_BUILTIN, LOW);
    delay(60);
  }

  // Debounce semplice sul rilascio
  delay(30);
  if (digitalRead(PIN_BTN) == HIGH) {
    Serial.print("Risultato finale: ");
    Serial.println(ultimoNumero);
    Serial.println("---");
    delay(300);
  }
}

Esercizi

Esercizio 01 – Validazione di input numerico (range 1..10)

Chiedere all’utente un numero intero tra 1 e 10. Finché l’input non è valido, ripetere la richiesta (usare while). Quando è valido, stampare “Valore accettato: X”.

Esercizio 02 – Algoritmo di Euclide (MCD) con while

Leggere due interi positivi a e b dal Serial Monitor e calcolare il Massimo Comun Divisore usando il metodo di Euclide:
ripetere finché b != 0 la sostituzione a, b = b, a % b. Alla fine stampare MCD = a.

Esercizio 03 – Servo: scansione fino al finecorsa (stato controllato con while)

Collegare un servo (pin D9) e un finecorsa su D2 (con INPUT_PULLUP). Far ruotare il servo dal minimo verso il massimo (per passi di 2–3 gradi) finché il finecorsa NON viene premuto (LOW). Quando il finecorsa si attiva, fermarsi e stampare l’angolo raggiunto.

Buon lavoro 🙂

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 🙂