Problema con Debouncing per backlight LCD

Salve a tutti,
Sono un novizio su Arduino e da autodidatta sto usando ora per la prima volta il modulo RTC. Ho realizzato un sistema che muove un servo a due specifiche ore del giorno, selezionabili attraverso un menù. Sto avendo problemi con LCD, vorrei poter attivare la backlight solo qual'ora si prema un pulsante e dopo diversi secondi di inattività si spenga. Sto utilizzando un sistema di debouncing, ma funziona solo finché un tasto viene premuto, altrimenti smette di funzionare. So che mi sto perdendo qualcosa nello script, magari qualcosa di semplice.
Per favore aiutatemi.

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include "RTClib.h"
#include <Servo.h>

RTC_DS3231 rtc;

#define I2C_ADDR 0x27 
#define BACKLIGHT_PIN 3
#define En_pin 2
#define Rw_pin 1
#define Rs_pin 0
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7

LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);

Servo servo1;

//Input & Button Logic
const int numOfInputs = 4;
const int inputPins[numOfInputs] = {8,9,10,11};
int inputState[numOfInputs];
int lastInputState[numOfInputs] = {LOW,LOW,LOW,LOW};
bool inputFlags[numOfInputs] = {LOW,LOW,LOW,LOW};
long lastDebounceTime[numOfInputs] = {0,0,0,0};
long debounceDelay = 5;

//LCD Menu Logic
const int numOfScreens = 5;
int currentScreen = 0;
String screens[numOfScreens][2] = {{"Turn:",""}, {"Set ON:", "Hour"}, 
{"Set ON:","Minutes"},{"Set OFF:","Hour"}, {"Set OFF:", "Minutes"}};
int parameters[numOfScreens];

int timeforOFF = 180;
int timeforON = 0;
//int setOnHour = 0;
//int setOnMinute = 0;
//int setOffHour = 0;
//int setOffMinute = 0;
long t = 0;
long debdelay = 5000;


void setup() {
  Serial.begin(9600);
  if (!rtc.begin()) {
    Serial.println("Verifica Connessione DS3231");
    while(true);
  }
  if (rtc.lostPower()){
    rtc.adjust(DateTime(F(__DATE__),F(__TIME__)));
  }
  servo1.attach(12);
  servo1.write(90);
  for(int i = 0; i < numOfInputs; i++) {
    pinMode(inputPins[i], INPUT);
    digitalWrite(inputPins[i], HIGH); // pull-up 20k
  }
  lcd.begin(16,2);
  lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
  lcd.setBacklight(LOW);  
}

void loop() {
  DateTime currentTime = rtc.now();
  printTimeScreen(currentTime);
  setInputFlags();
  resolveInputFlags();
  screenLight(t);
  Serial.print(t);
  if (parameters[0] == 0){
    servo1.write(timeforOFF);
  }else if (parameters[0] == 1){
    servo1.write(timeforON);
  }else if (parameters[0] == 2){
    if(checkAlarm(currentTime, parameters[1], parameters[2], parameters[3], parameters[4])){
      servo1.write(timeforOFF);
    }else{
      servo1.write(timeforON);
   }
  }    
}

void printTimeScreen(DateTime selTime){
  Serial.println("   ");
  Serial.print(selTime.hour());
  Serial.print(":");
  Serial.print(selTime.minute());
  Serial.print(":");
  Serial.print(selTime.second());
  Serial.println("   ");
}

void setInputFlags() {
  for(int i = 0; i < numOfInputs; i++) {
    int reading = digitalRead(inputPins[i]);
    if (reading != lastInputState[i]) {
      lastDebounceTime[i] = millis();
    }
    if ((millis() - lastDebounceTime[i]) > debounceDelay) {
      if (reading != inputState[i]) {
        inputState[i] = reading;
        if (inputState[i] == HIGH) {
          inputFlags[i] = HIGH;
          t = millis();
        }
      }
    }
    lastInputState[i] = reading;
  }
}

void resolveInputFlags() {
  for(int i = 0; i < numOfInputs; i++) {
    if(inputFlags[i] == HIGH) {
      inputAction(i);
      inputFlags[i] = LOW;
      printScreen();
    }
  }
}

void screenLight (int tempo){
  if((millis() - tempo)< debdelay){
    lcd.setBacklight(HIGH);
    time = 0;
  }else{
    lcd.setBacklight(LOW);
  }
}

void inputAction(int input) {
  if(input == 0) {
    if (currentScreen == 0) {
      currentScreen = numOfScreens-1;
    }else{
      currentScreen--;
    }
  }else if(input == 1) {
    if (currentScreen == numOfScreens-1) {
      currentScreen = 0;
    }else{
      currentScreen++;
    }
  }else if(input == 2) {
    parameterChange(0);
  }else if(input == 3) {
    parameterChange(1);
  }
}

void parameterChange(int key) {
  if(key == 0) {
    if (currentScreen == 1 || currentScreen == 3){
      if (parameters[currentScreen]>= 0 && parameters[currentScreen]<23){
        parameters[currentScreen]++;        
      }
    }
    if (currentScreen == 2 || currentScreen == 4){
      if (parameters[currentScreen]>= 0 && parameters[currentScreen]<55){
        parameters[currentScreen]= parameters[currentScreen]+5;        
      }
    }
    if (currentScreen == 0){
      if (parameters[currentScreen]>= 0 && parameters[currentScreen]<2){
        parameters[currentScreen]++;        
      }
    }
  }else if(key == 1) {
    if (parameters[currentScreen] >0) {
      if (currentScreen == 2 || currentScreen == 4){
        if (parameters[currentScreen]> 0 && parameters[currentScreen]<=55){
        parameters[currentScreen]= parameters[currentScreen]-5;
        }     
      }else{
        parameters[currentScreen]--;                 
      }
    }
  }
}

void printScreen() {
  if (currentScreen == 0 && parameters[currentScreen] == 0){
    lcd.clear();
    lcd.print(screens[currentScreen][0]);
    lcd.setCursor(0,1);
//    lcd.print(parameters[currentScreen]);
    lcd.print(" ");   
    lcd.print("OFF"); 
  } else if (currentScreen == 0 && parameters[currentScreen] == 1){
    lcd.clear();
    lcd.print(screens[currentScreen][0]);
    lcd.setCursor(0,1);
//    lcd.print(parameters[currentScreen]);
    lcd.print(" ");   
    lcd.print("ON");
  } else if (currentScreen == 0 && parameters[currentScreen] == 2){
    lcd.clear();
    lcd.print(screens[currentScreen][0]);
    lcd.setCursor(0,1);
//    lcd.print(parameters[currentScreen]);
    lcd.print(" ");   
    lcd.print("ON + TIMER"); 
  }else{
    lcd.clear();
    lcd.print(screens[currentScreen][0]);
    lcd.setCursor(0,1);
    lcd.print(parameters[currentScreen]);
    lcd.print(" ");
    lcd.print(screens[currentScreen][1]);
  }
}


boolean checkAlarm(DateTime timeNow, int setOnHour, int setOnMinute, int setOffHour, int setOffMinute){
  DateTime midNight( timeNow.year(), timeNow.month(), timeNow.day(),00, 00, 00);
  DateTime lastTime( timeNow.year(), timeNow.month(), timeNow.day(),23, 59, 59);
  DateTime timeSetOn( timeNow.year(), timeNow.month(), timeNow.day(),setOnHour, setOnMinute, 00);
  DateTime timeSetOff( timeNow.year(), timeNow.month(), timeNow.day(),setOffHour, setOffMinute, 00);
  if (timeSetOn>=timeSetOff){
    if (timeNow>=timeSetOff && timeNow <= timeSetOn){
      return true;
    }else if (timeNow<=timeSetOff && timeNow>=midNight){
      return false;
    }else if (timeNow>=timeSetOn && timeNow<=lastTime){
      return false;      
    }
  }else if (timeSetOn<=timeSetOff){
    if (timeNow>=timeSetOn && timeNow<=timeSetOff){
      return false;      
    }else if (timeNow>=midNight && timeNow<=timeSetOn){
      return true;      
    }else if (timeNow>=timeSetOff && timeNow<=lastTime){
      return true;      
    }
  }
}

PS: Perdonate se lo script non è pulito e può sicuramente essere semplificato, prometto che migliorerò.

Buongiorno, :slight_smile:
essendo il tuo primo post, nel rispetto del regolamento della sezione Italiana del forum (… punto 13, primo capoverso), ti chiedo cortesemente di presentarti IN QUESTO THREAD (spiegando bene quali conoscenze hai di elettronica e di programmazione ... possibilmente evitando di scrivere solo una riga di saluto) e di leggere con molta attenzione tutto il su citato REGOLAMENTO ... Grazie. :slight_smile:

Guglielmo

P.S.: Ti ricordo che, purtroppo, fino a quando non sarà fatta la presentazione nell’apposito thread, nessuno ti potrà rispondere, quindi ti consiglio di farla al più presto. :wink:

(tanto per quotare Guglielmo :D)

Ops :confused: , scusate tutti, vado subito a presentarmi! :smiley:

Pero' non capisco una cosa ... perche' cercare ogni volta di realizzare un debounce software che crea problemi, quando con un condensatorino da 100n verso massa, e magari una resistenza da 100 ohm in serie al pulsante, si risolve il problema molto meglio e con meno problemi ? ... :wink:

E' la prima volta che provo a fare un menù con più pulsanti e non sapevo bene come strutturare lo schema.
Potresti spiegarti meglio, magari provo a riadattare il setup affinchè sia più efficiente.
Parli del debounce per identificare quale tasto è stato premuto?
Meglio non utilizzare una soluzione nel software ma di hardware?
Perdona l'ignoranza, ma credo sia un'ottima occasione per capire meglio :smiley:
Per quanto riguarda la backlight, mi sconsigli di utilizzare un secondo debounce per mantenere acceso lcd per un lasso di tempo senza input?
Grazie mille per il tuo aiuto^^

Ti allego un esempio di quanto suggerito da Etemenanki ...
... la soluzione hardware è sempre meglio di quella software :wink:

Guglielmo

Debouncing-pin.png

Per farti capire ...
... un bottone "ideale", quando lo premi chiude il contatto e quando lo lasci lo riapre, un bottone "reale" ha un comportamento del tutto diverso e, se lo guardi all'oscilloscopio, ti accorgi che in verità quello che succede è all'incirca questo:

... ovvero hai una "raffica" di pressioni/rilascio del bottone. Quindi, se non usi una tecnica di "debouncing", Arduino crederà che tu abbia premuto e rilasciato il bottone un'infinità di volte ... con le ovvie conseguenze.

Il "debouncing" può essere fatto o via hardware o via software :slight_smile:

Guglielmo

Si, penso di aver capito, in questo modo non serve usare il debouncing via codice. Provo a riadattare il setup e riscrivere quindi la funzione setInputFlag. Per quanto riguarda il backlight c'è un modo per realizzare una sorta di counter/delay che avvenga in background, che non blocchi arduino... una sorta di thread separato, che faccia accendere la luce al momento di un input e che si spenga dopo poco che non si ricevono input?

Grazie mille per il vostro aiuto! :smiley:

Be', il debounce dei tasti come dice anche Guglielmo ha lo scopo di evitare che la MCU ti legga una serie di pressioni al posto di una sola ... il delay della retroilluminazione e' una cosa diversa (e forse avevo capito male io quello che volevi)

Potresti aggiungere alle funzioni che leggono i tasti una seconda variabile, che metti ad uno ogni volta che premi un tasto qualsiasi, e rimetti a zero dopo un certo tempo calcolato usando millis() invece del comando delay, che e' bloccante ... in piu, volendo, ogni volta che un tasto viene premuto, resetti anche la variabile usata per il millis, cosi l'intervallo parte sempre dall'ultimo tasto premuto ... giusto come idea ...

jokeriddler:
c'è un modo per realizzare una sorta di counter/delay che avvenga in background, che non blocchi arduino... una sorta di thread separato, che faccia accendere la luce al momento di un input e che si spenga dopo poco che non si ricevono input?

Se scrivi codice non bloccante sono due if:

SE ricevuto input:
   salva tempo millis
   imposta acceso

SE acceso E trascorso>X:
   imposta spento