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

Obiettivo didattico
Realizzare una piccola interfaccia utente con un solo pulsante. Il sistema deve riconoscere una pressione breve, un doppio clic, una pressione lunga e un timeout di inattività. L’attività allena la gestione degli eventi, l’antirimbalzo software e l’uso di millis() senza bloccare il programma.
Nota importante
La comprensione del funzionamento di questo esercizio permetterà lo svolgimento in autonomia dell’esercizio aggiuntivo che trovate al fondo di questa scheda di lavoro.
Materiali suggeriti
- Arduino UNO R3 o UNO R4;
- 1 pulsante;
- 3 LED;
- 3 resitori (per i LED);
- breadboard;
- cavetti jumper.
Schema di collegamento

Richiamo teorico
Per leggere correttamente un pulsante reale bisogna evitare il rimbalzo dei contatti. Si usa quindi una variabile con l’ultima lettura, una temporizzazione di stabilizzazione e una logica a eventi. Con millis() si misura il tempo senza usare delay(), così il programma può continuare a controllare altri compiti.
Schema logico dell’attività
Il programma legge continuamente il pulsante. Se la lettura cambia, attende il tempo di debounce. Quando il livello è stabile, riconosce pressione e rilascio. Dal tempo trascorso ricava l’evento: clic breve, doppio clic oppure pressione lunga. Se non accade nulla per molti secondi, attiva un LED di timeout.
Diagramma di flusso
Diagramma di flusso Mermaid
Ricordo che per realizzare il diagramma di flusso, copiate il codice Mermaid seguendo le indicazioni della lezione: Progettare bene, programmare meglio: diagrammi di flusso – cos’è il formato Mermaid? – Lezione 2/5
flowchart TD
A[Inizio] --> B[Configura pin e variabili]
B --> C[Leggi pulsante]
C --> D{Lettura cambiata?}
D -- Sì --> E[Salva istante variazione]
D -- No --> F{Tempo debounce trascorso?}
E --> F
F -- No --> C
F -- Sì --> G{Stato stabile cambiato?}
G -- No --> H{Timeout inattività?}
G -- Sì --> I{Pulsante premuto?}
I -- Sì --> J[Memorizza istante pressione]
I -- No --> K[Calcola durata pressione]
K --> L{Pressione lunga?}
L -- Sì --> M[Genera evento LONG]
L -- No --> N{Secondo clic entro finestra?}
N -- Sì --> O[Genera evento DOUBLE]
N -- No --> P[Attendi possibile secondo clic]
H -- Sì --> Q[Attiva LED timeout]
H -- No --> C
J --> C
M --> C
O --> C
P --> C
Q --> C
Programma
/*
Prof. Maffucci Michele
Esercizio 1: Pulsante singolo con antirimbalzo, doppio clic, pressione lunga e timeout
*/
// ---------------------------
// Definizione dei pin usati
// ---------------------------
const int PIN_PULSANTE = 2;
const int PIN_LED_BREVE = 8;
const int PIN_LED_DOPPIO = 9;
const int PIN_LED_TIMEOUT = 10;
// ---------------------------
// Costanti temporali
// ---------------------------
const unsigned long TEMPO_DEBOUNCE = 30;
const unsigned long SOGLIA_PRESSIONE_LUNGA = 800;
const unsigned long FINESTRA_DOPPIO_CLICK = 350;
const unsigned long TEMPO_TIMEOUT = 5000;
// ---------------------------
// Variabili per antirimbalzo
// ---------------------------
int ultimaLetturaGrezza = HIGH;
int statoStabile = HIGH;
unsigned long istanteUltimaVariazione = 0;
// ---------------------------
// Variabili per eventi utente
// ---------------------------
bool attesaSecondoClick = false;
unsigned long istantePrimoClick = 0;
unsigned long istantePressione = 0;
unsigned long ultimoEventoUtente = 0;
void setup() {
// Il pulsante usa la resistenza interna di pull-up.
pinMode(PIN_PULSANTE, INPUT_PULLUP);
// I tre LED rappresentano tre eventi diversi.
pinMode(PIN_LED_BREVE, OUTPUT);
pinMode(PIN_LED_DOPPIO, OUTPUT);
pinMode(PIN_LED_TIMEOUT, OUTPUT);
// All'avvio tutti i LED sono spenti.
digitalWrite(PIN_LED_BREVE, LOW);
digitalWrite(PIN_LED_DOPPIO, LOW);
digitalWrite(PIN_LED_TIMEOUT, LOW);
// La seriale aiuta a vedere quale evento viene riconosciuto.
Serial.begin(9600);
// Salvo il tempo iniziale come ultimo evento.
ultimoEventoUtente = millis();
}
void loop() {
// Leggo il pulsante in forma grezza.
int letturaCorrente = digitalRead(PIN_PULSANTE);
// Se la lettura è cambiata rispetto alla precedente,
// aggiorno il tempo della variazione.
if (letturaCorrente != ultimaLetturaGrezza) {
istanteUltimaVariazione = millis();
ultimaLetturaGrezza = letturaCorrente;
}
// Se il segnale è stabile da abbastanza tempo,
// posso considerarlo affidabile.
if ((millis() - istanteUltimaVariazione) >= TEMPO_DEBOUNCE) {
// Se anche lo stato stabile è cambiato, ho un nuovo evento.
if (letturaCorrente != statoStabile) {
statoStabile = letturaCorrente;
ultimoEventoUtente = millis();
digitalWrite(PIN_LED_TIMEOUT, LOW);
// Transizione verso livello basso = pulsante premuto.
if (statoStabile == LOW) {
istantePressione = millis();
}
// Transizione verso livello alto = pulsante rilasciato.
else {
unsigned long durataPressione = millis() - istantePressione;
// Se la durata supera la soglia, classifico come pressione lunga.
if (durataPressione >= SOGLIA_PRESSIONE_LUNGA) {
Serial.println("Evento: PRESSIONE LUNGA");
lampeggiaLed(PIN_LED_DOPPIO, 2, 120);
attesaSecondoClick = false;
}
else {
// Se sto già aspettando un secondo clic,
// verifico la finestra temporale.
if (attesaSecondoClick == true &&
(millis() - istantePrimoClick) <= FINESTRA_DOPPIO_CLICK) {
Serial.println("Evento: DOPPIO CLICK");
lampeggiaLed(PIN_LED_DOPPIO, 1, 250);
attesaSecondoClick = false;
}
else {
// Primo clic breve: non lo confermo subito,
// perché potrei ricevere un secondo clic.
attesaSecondoClick = true;
istantePrimoClick = millis();
}
}
}
}
}
// Se è in attesa un secondo clic e la finestra è scaduta,
// confermo il clic breve.
if (attesaSecondoClick == true && (millis() - istantePrimoClick) > FINESTRA_DOPPIO_CLICK) {
Serial.println("Evento: CLICK BREVE");
lampeggiaLed(PIN_LED_BREVE, 1, 250);
attesaSecondoClick = false;
}
// Se passa troppo tempo senza eventi, attivo il LED di timeout.
if ((millis() - ultimoEventoUtente) >= TEMPO_TIMEOUT) {
digitalWrite(PIN_LED_TIMEOUT, HIGH);
}
}
// ----------------------------------------------------------
// Funzione di servizio: fa lampeggiare un LED alcune volte.
// In questa attività è accettabile usare delay() perché
// la funzione serve solo come feedback visivo di conferma.
// ----------------------------------------------------------
void lampeggiaLed(int pinLed, int numeroLampi, int durata) {
for (int i = 0; i < numeroLampi; i = i + 1) {
digitalWrite(pinLed, HIGH);
delay(durata);
digitalWrite(pinLed, LOW);
delay(durata);
}
}













