Perché delay() compromette i progetti Arduino in classe (e come spiegare millis() agli studenti)

L'uso di delay() mette in pausa il microcontrollore, accecando i sensori e bloccando i motori. Ecco come il modello mentale del cronometro insegna la temporizzazione non bloccante agli studenti della Senior Phase.
La funzione delay() manda in crisi i progetti Arduino in classe perché arresta completamente il microcontrollore. Per l'intera durata della pausa, il processore non fa nulla: non può leggere un sensore d'urto, non può verificare la distanza rilevata da un sensore a ultrasuoni e non può rispondere a un pulsante di arresto. Se un robot su ruote si sta muovendo in avanti quando incontra un ritardo di due secondi, continuerà a procedere alla cieca contro una parete per due secondi interi prima di eseguire l'istruzione successiva.
Per risolvere questo problema con gli studenti dal 7° al 9° anno (Grade 7–9), non serve spiegare il multithreading, gli interrupt dei timer hardware o le macchine a stati a oggetti. Basta un unico, concreto modello mentale: guardare l'orologio da polso invece di fare un sonnellino.
L'anatomia di un bug scolastico
In quasi tutti i programmi introduttivi di robotica, la prima lezione è il classico lampeggio del LED:
digitalWrite(13, HIGH);
delay(1000);
digitalWrite(13, LOW);
delay(1000);Funziona, dà agli studenti una gratificazione immediata e piazza un tranello concettuale destinato a esplodere tre settimane più tardi. Nella quarta lezione, quando agli studenti viene chiesto di realizzare un passaggio pedonale (far lampeggiare una luce e rilevare la pressione di un pulsante) o un rover che evita gli ostacoli (far girare i motori e leggere la distanza), il loro codice smette di funzionare.
Lo studente scrive un ciclo che controlla il pulsante, attiva un buzzer e chiama delay(1000). Quando preme il pulsante, la scheda di solito lo ignora. Perché? Perché il processore era occupato a contare cicli a vuoto all'interno del delay. Il dito ha premuto e rilasciato l'interruttore durante quel 99% del ciclo in cui il chip era di fatto addormentato.
Su un ATmega328P con clock a 16 MHz, chiamare delay(1000) spreca 16 milioni di cicli di clock eseguendo istruzioni assembly vuote. Il robot non sta svolgendo più compiti contemporaneamente: è semplicemente congelato.
Il modello mentale dell'orologio da polso
Prima di mostrare il codice agli studenti, allontanateli dagli schermi per una dimostrazione pratica di cinque minuti. Chiedete a un volontario di svolgere un compito: “Batti le mani ogni tre secondi, ma se lascio cadere questa penna, afferrala prima che tocchi terra.”
Per prima cosa, fategli simulare delay(): spiegategli che per contare tre secondi deve chiudere gli occhi e contare a mente fino a tre. Lasciate cadere la penna al secondo secondo. Fallirà ogni volta: i suoi sensori erano disattivati mentre contava il tempo.
Subito dopo, introducete il modello non bloccante: ditegli di tenere gli occhi bene aperti, guardare l'orologio da parete della classe (o un orologio da polso digitale) e non perdere d'occhio la vostra mano. Controlla l'ora: se sono passati tre secondi dall'ultimo battito di mani, batte le mani. Se la penna cade, reagisce all'istante.
Questo sposta il concetto di temporizzazione da un'azione (“aspetta due secondi”) a un confronto (“è già trascorso abbastanza tempo?”).
Lo schema fondamentale: tre domande
Una volta compreso l'esempio dell'orologio da parete, introducete millis(). Spiegate che millis() è semplicemente un cronometro interno avviato nell'esatto millisecondo in cui Arduino è stato collegato all'alimentazione. Non si ferma mai e non blocca mai l'esecuzione del codice.
Ogni evento non bloccante può essere ricondotto a tre semplici domande all'interno del ciclo continuo:
- Che ora è adesso? (Leggere il cronometro:
currentTime = millis()) - Quando ho eseguito questa azione l'ultima volta? (Controllare il valore memorizzato:
previousTime) - È trascorso il tempo necessario? (Eseguire la sottrazione:
currentTime - previousTime >= interval)
Se la risposta alla terza domanda è sì, si esegue l'azione e si aggiorna il dato: previousTime = currentTime. Se la risposta è no, si salta l'azione e si va subito avanti. Poiché il ciclo si conclude in una frazione di millisecondo, la scheda è libera di leggere i sensori a ultrasuoni, controllare i finecorsa e verificare i sensori di linea migliaia di volte al secondo.
Insegnare la sintassi senza sovraccaricare i principianti
Per gli studenti del 7° e 8° anno, il codice standard C++ attorno a unsigned long genera spesso un sovraccarico cognitivo. Nelle scuole che seguono il curriculum ufficiale di Coding e Robotica per la Senior Phase, gli studenti lavorano di norma con ambienti a blocchi o editor ibridi.
Se insegnate con i blocchi (come negli ambienti Arduino basati su Scratch), la trasposizione è immediata: introducete un blocco “cronometro” e una variabile chiamata last_time_checked. Evitate del tutto il blocco “attendi 1 secondo” non appena vengono introdotti gli input di base.
Se insegnate C++ testuale per Arduino, non proponete subito l'esempio predefinito BlinkWithoutDelay senza una graduale preparazione. L'esempio ufficiale di Arduino combina due concetti distinti contemporaneamente: la temporizzazione non bloccante e l'inversione di stato (il toggle della variabile di stato di un LED). È per questo che i ragazzi si confondono.
Separate il controllo del tempo dalla logica. Seguite invece questa sequenza:
- Dimostrate il fallimento: fate eseguire un lampeggio LED con
delay()mentre tentano di premere un pulsante di arresto d'emergenza. Osservate la frustrazione quando il pulsante non risponde. - Introducete la variabile del timer: mostrate come stampare
millis()sul Monitor Seriale (Serial Monitor), così che vedano i numeri salire continuamente. Questo demistifica la funzione: è solo un contachilometri per il tempo. - Scrivete la struttura temporizzata: fornite un modello guidato in cui la logica di sottrazione sia separata dal comportamento del robot.
| Approccio | Stato del processore | Reattività dei sensori | Idoneità |
|---|---|---|---|
delay(1000) | Bloccato in un ciclo di attesa attiva (busy-wait) | Completamente cieco durante la pausa | Dimostrazioni a singolo compito, introduzione per Grade 4–6 |
Confronto con millis() | In esecuzione continua alla massima frequenza di clock | Istantanea (interrogazione sub-millisecondo) | Robotica, rover, sistemi interattivi |
Consolidare il metodo nei progetti di classe
Quando gli studenti passano da rover che si muovono alla cieca a rover che interrogano costantemente i sensori, il loro modo di eseguire il debug cambia radicalmente. Invece di chiedersi perché un sensore d'urto non sia riuscito a fermare un motore DC, riescono a tracciare l'esecuzione in modo chiaro e lineare.
Se la vostra scuola sta strutturando una progressione su più trimestri dalla logica a blocchi alla robotica fisica, disporre di percorsi didattici strutturati e schemi hardware verificati elimina ogni incertezza. Il nostro team di Sheen Robotics for Schools affianca i docenti per allineare questi concetti sui sistemi embedded agli orari scolastici, assicurando che gli studenti sviluppino modelli mentali affidabili prima che si radichino cattive abitudini di programmazione.
Quando uno studente comprende che un computer non dovrebbe mai restare a occhi chiusi a contare i secondi, supera la soglia che separa la semplice esecuzione di script dalla scrittura di software reattivo per il mondo reale.



