Contatore con display lcd

Ciao a tutti, ho voluto realizzare un semplice contatore up\down con visualizzazione su lcd 16x2.
Il mio problema è il seguente, quando faccio il conteggio up parte da 0 e arriva a 100 e alla fine visualizzo cio:
CONTATORE:100

quando parte il conteggio down, non esegue 100,99,98...ecc ma, 100,990,980...ecc . (non elimina l'ultimo zero)

CONTEGGIO:100 CONTEGGIO:990 CONTEGGIO:980 CONTEGGIO:000

Se a CONTEGGIO:000 , faccio partire di nuovo un conteggio up lui esegue

CONTEGGIO:100 CONTEGGIO:350 CONTEGGIO:990 CONTEGGIO:100 (non elimina l'ultimo zero)

Quindi io vorrei risolvere questo problema ma ad oggi non arrivo alla soluzione.
Posto il codice

#include <LiquidCrystal.h>
const int rs=12, en=11, d4=5, d5=4, d6=3, d7=2;
LiquidCrystal lcd(rs,en,d4,d5,d6,d7);
#define button1 8
#define button2 7
bool statoButton1 =LOW;
bool statoButton2 =LOW;
int i=0;

void setup() {
  lcd.print("Contatore:");
  pinMode(button1,INPUT);
  pinMode(button2,INPUT);
  
}

void loop() {
  statoButton1=digitalRead(8);
  delay(50);
  if(statoButton1==HIGH){
    for(i=0;i<=100;i++){
       lcd.setCursor(10,0);
       lcd.print(i);
       delay(500);
    }
  }
  statoButton2=digitalRead(7);
  if(statoButton2==HIGH){
    for(i=100;i>=0;i--){
      lcd.setCursor(10,0);
      lcd.print(i);
      delay(500);
    }
  }
}

RIngrazio tutti per eventuali consigli.

Prima di scrivere il tuo numero, stampa sul display tre spazi in modo da cancellare il numero precedente.

Sull'LCD se stampi 3 caratteri, tutti gli altri restano inalterati, quindi il consiglio di savoriano è di pulire prima di scrivere, oppure invece di stampare '99' stampi ' 99' cioè uno spazio in testa o in coda a seconda di come vuoi allineare i numeri. In testa sarebbe più naturale per l'utente. Cioè stampi sempre lo stesso numero di caratteri pulendo quelli che non ti servono.

Maurizio

Metti degli if:

lcd.setCursor(10,0);
if i<100 lcd.print(' ');
if i<10 lcd.print(' ');
lcd.print(i);

Grazie a tutti per i consigli problema risolto!
p.s posso non utilizzare "delay(1000)" per eseguire il conteggio, per non bloccare il programma, in altro modo?

Si, usando la funzione millis()
Ecco alcuni link utili:

Se poi cerchi sul forum ci sono tonnellate di post al riguardo, è uno degli argomenti più discussi.

Maurizio

Grazie maubarzi, leggerò e cercherò.

E' semplice, se non ti fai prendere dal panico, il concetto è di fare le cose solo se è passato un certo lasso di tempo dall'ultima volta.
Alla fine, una volta capito il meccanismo, risolvi con 2 righe di codice e una variabile :wink:

Maurizio

Credevo di averla risolta cosi, ma mi scrive solo lo "0" e stop.
Riporto il codice solo del conteggio crescente, che avviene tramite la pressione di un tasto.

void loop() {
  statoButton1=digitalRead(8);
  delay(50);
  if(statoButton1==HIGH){
    if((millis())-lastDebounceTime>debounce){
       lastDebounceTime=millis();
         for(i=0;i<=100;i++){  
          if((millis()-t)>dt){
            lcd.setCursor(10,0);
            lcd.print("   ");
            lcd.setCursor(10,0);
            lcd.print(i);
            t=millis();
          }
         }
     }
   }

Nelle impostazioni delle variabili:
unsigned long t=0;
unsigned long dt=1000;

Perchè il for da 0 a 100?
Chiaramente il for, al primo giro con i = 0 troverà che ((millis()-t)>dt e quindi eseguirà il contenuto, tra cui t=millis(), a i = 1, t è stato variato e quindi non sarà più vera la condizione ((millis()-t)>dt e quindi non eseguirà più nulla fino a 100.

Se vuoi stampare i 100 valori devi mettere il for dopo il test.

Poi non so cosa volevi fare con questo codice, ma in genere non serve nidificare due test diversi con millis.
if((millis())-lastDebounceTime>debounce){
e
if((millis()-t)>dt){

in genere con millis si gestiscono cose in parallelo,

quindi

#define SOGLIA_1 10000
#define SOGLIA_2 15000

void setup() {
  unsigned long currMillis = millis();
  unsigned long snapshot1 = currMillis;
  unsigned long snapshot2 = currMillis;
}

void loop() {
  unsigned long currMillis = millis();
  if (currMillis - snapshot1 > SOGLIA_1) {
    // Eseguo il compito con cadenza soglia1
    ...
    snapshot1 = currMillis;
  }
  if (currMillis - snapshot2 > SOGLIA_2) {
    // Eseguo il compito con cadenza soglia2
    ...
    snapshot2 = currMillis;
  }
}

In questo modo, i due compiti sono del tutto indipendenti tra di loro.

Io, gusto personale, preferisco salvarmi il valore di millis all'inizio del loop per testare tutto allo stesso istante.
Questo mi fa anche cadenzare le successive esecuzioni ad una distanza sempre uguale (o meglio non più vicine di quanto previsto), quindi ad es. un'operazione non la richiamo ogni 10 secondi ma distanziata di almeno 10 secondi dal termine dell'esecuzione precedente.
Per cose veloci cambia poco, per cose più lente può incidere, dipende sempre da quello che si deve fare.
Al momento prendilo come gusto personale, volevo solo spiegarti perchè ho fatto così nell'esempio.

Maurizio

if(statoButton1==HIGH){
    if((millis())-lastDebounceTime>debounce){
       lastDebounceTime=millis();

usavo per il debounce del tasto. Mi sono accorto che nel codice ho scritto un delay(50) dopo la lettura del tasto , l'ho cancellato e sostituito con il codice sopra.

Per il conteggio nn ne vengo fuori...

Allora ho sezionato lo sketch, utilizzando solo il conteggio up per utilizzare la funzione millis()

unsigned long t;
unsigned long dt;
int i=0;

void setup(){
 t=millis();
}

void loop(){
 dt=millis()-t;
      if(dt>=1000){
          t=millis();
          lcd.setCursor(10,0);
          lcd.print(i);
          i++;
       }
}

Ho fatto l'upload e funziona.
Adesso vorrei reintrodurre la pressione del tasto come in precedenza, per iniziare il conteggio.
Ma se riscrivo le righe di comando citate in precedenza, conteggio non parte.
Non capisco il xkè.

Come prima cosa potresti crearti una variabile booleana per dire se il conteggio è in corso o è fermo.

Poi, esternamente al codice che hai già fatto, vediamo di inserire il controllo della pressione del pulsante.
Diciamo che facciamo partire il conteggio quando il pulsante da basso va alto.
Dobbiamo iniziare con una variabile per indicare lo stato del pulsante che inizialmente sarà basso.

leggiamo il valore del pulsante con la digitalRead, se è cambiato inizializziamo il contatore per il debounce, in modo da non fare altre letture troppo ravvicinate e se passa da basso ad alto, attiviamo il contatore.
Ovviamente la digitalRead la facciamo solo se è scaduto il tempo di debounce, che se inizializziamo il riferimento a zero, finchè il pulsante non cambia stato sarà sempre scaduto facendoti leggere a raffica.

Quando il conteggio termina rimetti a false la variabile per spegnere il conteggio.

Dovrebbe bastare, se non mi sono dimenticato qualche pezzo.

Maurizio

Questo è il codice completo

#include <LiquidCrystal.h>
const int rs=12, en=11, d4=5, d5=4, d6=3, d7=2;
LiquidCrystal lcd(rs,en,d4,d5,d6,d7);
#define button1 8
bool statoButton1=LOW;
int i=0;
unsigned long lastDebounceTime=0;
unsigned long debounce=50;
unsigned long t;
unsigned long dt;

void setup() {
  lcd.setCursor(0,0);
  lcd.print("Contatore:");
  pinMode(button1,INPUT);
  t=millis();
}

void loop() {
  statoButton1=digitalRead(8);
    if(statoButton1==HIGH){
      if((millis())-lastDebounceTime>debounce){
       lastDebounceTime=millis();}
       dt=millis()-t;
        if(dt>=1000){
          t=millis();
          lcd.setCursor(10,0);
          lcd.print(i);
          i++;
          }
    }

Il problema è che il conteggio parte se tengo premuto il tasto, ma appena lo rilascio si ferma.
Io vorrei che fosse: pressione del tasto>
parte il conteggio>
rilascio il tasto>
continua il conteggio.

Perchè hai messo tutto dentro questo if

if(statoButton1==HIGH){

invece, questo if deve solo mettere a true una variabile booleana e tutto l'altro pezzo deve essere messo dentro un altro if che testi che questa variabile è a true.
Così che resti a true anche se rilasci il pulsante.
Ora, invece, se rilasci il pulsante non entra più sulla prima if

Maurizio

Adesso con questo codice funzione

void loop() {
  statoButton1=digitalRead(8);
    if(statoButton1==HIGH){
      oldStatoButton=statoButton1;
      if((millis())-lastDebounceTime>debounce){
         lastDebounceTime=millis();
      }
    }
         if(oldStatoButton==HIGH){
          dt=millis()-t;
            if(dt>=100){
              t=millis();
              if(i<=50){
                lcd.setCursor(10,0);
                lcd.print(i);
                i++;
              }
            }
         }  
}

però ho un altro problema, quando ripremo il tasto non riparte il conteggio.

Quando fai

oldStatoButton=statoButton1;

devi anche inizializzare il conteggio, cioè oltre a farlo partire devi anche inizializzarlo, es. riportarlo a 100 perchè possa di nuovo scorrere all'indietro.

Maurizio

Grazie maubarzi, problema risolto definitivamente.

Maurizio

ahahahhahahaahhaahh :slight_smile: