Calcolare Il Tempo Che Rimane Premuto Un Pulsante Con Arduino

Calcolatore Tempo Pulsante Arduino

Calcola il tempo che un pulsante rimane premuto in un circuito Arduino con precisione millisecondica.

Guida Completa: Come Calcolare il Tempo che un Pulsante Rimane Premuto con Arduino

Misurare con precisione il tempo in cui un pulsante rimane premuto è un’operazione fondamentale in molti progetti Arduino, dalle interfacce utente ai sistemi di controllo industriale. Questa guida ti fornirà tutte le informazioni necessarie per implementare una soluzione robusta e precisa.

Principi Fondamentali della Misurazione del Tempo

Quando lavoriamo con pulsanti meccanici, dobbiamo considerare diversi fattori:

  • Rimbalzo (bouncing): I contatti meccanici non passano istantaneamente da aperto a chiuso, ma oscillano per alcuni millisecondi
  • Risoluzione temporale: La precisione dipende dalla frequenza di campionamento del microcontrollore
  • Overflow del timer: Con misurazioni lunghe, dobbiamo gestire correttamente il rollover dei contatori
  • Interruzioni vs polling: La scelta del metodo di lettura influisce sulla precisione e sull’utilizzo delle risorse

Metodi per Misurare il Tempo di Pressatura

Esistono principalmente tre approcci per misurare il tempo di pressatura di un pulsante:

  1. Utilizzo di millis():

    Il metodo più semplice che sfrutta la funzione millis() di Arduino. Adatto per misurazioni fino a circa 50 giorni (overflow a 49.7 giorni).

    unsigned long startTime = millis();
    // Attendi evento
    unsigned long duration = millis() - startTime;
  2. Utilizzo di micros():

    Per precisione al microsecondo, utile per misurazioni molto brevi. Overflow dopo circa 70 minuti.

    unsigned long startTime = micros();
    // Attendi evento
    unsigned long duration = micros() - startTime;
  3. Timer hardware:

    Soluzione avanzata che utilizza i timer interni del microcontrollore per massima precisione e minima occupazione della CPU.

Implementazione Pratica con Anti-Rimbalzo

Un’implementazione robusta deve includere sempre un meccanismo di anti-rimbalzo (debounce). Ecco un esempio completo:

const int buttonPin = 2;
unsigned long pressStartTime = 0;
unsigned long pressDuration = 0;
bool buttonPressed = false;
bool lastButtonState = HIGH;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop() {
  int reading = digitalRead(buttonPin);

  // Anti-rimbalzo
  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != buttonPressed) {
      buttonPressed = reading;

      if (buttonPressed == LOW) {
        pressStartTime = millis();
      } else {
        pressDuration = millis() - pressStartTime;
        Serial.print("Tempo pressatura: ");
        Serial.print(pressDuration);
        Serial.println(" ms");
      }
    }
  }

  lastButtonState = reading;
}

Ottimizzazione delle Prestazioni

Per progetti che richiedono massima precisione e minima latenza, considerare questi accorgimenti:

Tecnica Precisione Utilizzo CPU Complessità
millis() con polling ±1 ms Media Bassa
micros() con polling ±4 μs Alta Media
Interruzioni su cambio stato ±2 μs Bassa Media
Timer hardware (Input Capture) ±0.0625 μs (16MHz) Molto bassa Alta

Gestione degli Errori e Caso Limite

Un’implementazione professionale deve gestire questi scenari:

  • Overflow del contatore: Con millis() dopo 49.7 giorni, con micros() dopo 70 minuti
  • Pulsante bloccato: Rilevamento di pulsanti che rimangono premuti troppo a lungo
  • Rumore elettrico: Filtraggio di falsi trigger dovuti a interferenze
  • Alimentazione instabile: Salvataggio dello stato in EEPROM per ripresa dopo reset

Per gestire l’overflow di millis(), possiamo utilizzare questo approccio:

unsigned long getDuration(unsigned long start) {
  static unsigned long lastTime = 0;
  unsigned long currentTime = millis();

  if (currentTime < lastTime) { // Overflow rilevato
    return (ULONG_MAX - lastTime) + currentTime - start;
  }

  lastTime = currentTime;
  return currentTime - start;
}

Applicazioni Pratiche

La misurazione precisa del tempo di pressatura trova applicazione in numerosi scenari:

Applicazione Precisione Richiesta Tecnica Consigliata
Interfacce utente (menu) ±50 ms millis() con debounce
Sistemi di sicurezza ±10 ms Interruzioni con timer
Controllo motori ±1 ms millis() con polling ottimizzato
Strumentazione scientifica ±1 μs Timer hardware Input Capture
Giochi arcade ±20 ms millis() con gestione eventi

Risorse Esterne e Approfondimenti

Per approfondire l'argomento, consultare queste risorse autorevoli:

Best Practice per Codice Professionale

Segui queste linee guida per scrivere codice Arduino professionale per la misurazione del tempo:

  1. Utilizza sempre costanti per i pin e i parametri di configurazione
  2. Implementa un meccanismo di debounce robusto (50-100ms per pulsanti meccanici)
  3. Gestisci esplicitamente gli overflow dei contatori temporali
  4. Utilizza interruzioni solo quando necessario per non sovraccaricare il microcontrollore
  5. Documenta chiaramente la precisione attesa nel tuo codice
  6. Testa il tuo codice con pulsanti di qualità diversa per verificare la robustezza
  7. Considera l'utilizzo di librerie specializzate come Bounce2 per gestione avanzata del debounce

Esempio Avanzato con Gestione Multi-Pulsante

Per progetti con multiple input, ecco una struttura scalabile:

#define NUM_BUTTONS 3
const int buttonPins[NUM_BUTTONS] = {2, 3, 4};
unsigned long pressStart[NUM_BUTTONS] = {0};
unsigned long pressDuration[NUM_BUTTONS] = {0};
bool buttonState[NUM_BUTTONS] = {HIGH};
bool lastState[NUM_BUTTONS] = {HIGH};
unsigned long lastDebounce[NUM_BUTTONS] = {0};

void setup() {
  for (int i = 0; i < NUM_BUTTONS; i++) {
    pinMode(buttonPins[i], INPUT_PULLUP);
  }
  Serial.begin(9600);
}

void loop() {
  for (int i = 0; i < NUM_BUTTONS; i++) {
    int reading = digitalRead(buttonPins[i]);

    if (reading != lastState[i]) {
      lastDebounce[i] = millis();
    }

    if ((millis() - lastDebounce[i]) > 50) {
      if (reading != buttonState[i]) {
        buttonState[i] = reading;

        if (buttonState[i] == LOW) {
          pressStart[i] = millis();
        } else {
          pressDuration[i] = millis() - pressStart[i];
          Serial.print("Pulsante ");
          Serial.print(i);
          Serial.print(": ");
          Serial.print(pressDuration[i]);
          Serial.println(" ms");
        }
      }
    }

    lastState[i] = reading;
  }
}

Considerazioni sull'Hardware

La scelta dell'hardware influisce significativamente sulla precisione:

  • Arduino Uno (ATmega328P): Precisione di 4μs con micros(), 1ms con millis()
  • Arduino Due (SAM3X8E): Precisione di 1μs con micros(), timer a 84MHz
  • ESP32: Precisione di 0.1μs con timer hardware, dual-core per gestione parallela
  • Teensy:

Leave a Reply

Your email address will not be published. Required fields are marked *