Ciclo di "While" non funziona

Sto provando un ciclo di While dove nel tempo di 10 secondi tramite la funzione “millis”, se premo due tasti aumento o diminuisco una variabile.
Ogni volta che premo uno dei due tasti, il conteggio dei 10 secondi dovrebbe ripartire da capo, ma non succede.
Dovrei vedere il nuovo valore su LCD 16x2 della variabile che incremento/decremento ma non si visualizza proprio.
Entra nel ciclo di while, infatti nel monitor vedo i nuovi valori del tempo che si incrementa ma non sente la pressione dei tasti.
Lo sketch è il seguente:

#include <PCF8574_HD44780_I2C.h>

#define Su 7        // pulsante Su collegato alla porta 7
#define Giu 6       // pulsante Giu collegato alla porta 6

int statosu = 0;
int statogiu = 0;
int correttore = 2500;
long unsigned tempoinizio = 0;
long unsigned tempocorrente = 0;

PCF8574_HD44780_I2C lcd(0x27,16,2);

void setup() {
  lcd.init(); 
   lcd.setBacklight(1);
   Serial.begin(9600);
   delay(1000);
   lcd.home();
   lcd.setCursor(2, 0);  // colonna 2, riga 0
   lcd.print("Test...");
   delay(5000);
   lcd.clear();
   
   pinMode(Su, INPUT);
   pinMode(Giu, INPUT);
   
  tempoinizio = (millis);  // memorizza il tempo di inizio compensazione
  delay(1000);
  tempocorrente = (millis); // memorizza il momento corrente
  Serial.print(tempoinizio);
  Serial.print(" ");
  Serial.println(tempocorrente);
  
  while ((tempocorrente - tempoinizio) < 10000) // 10 secondi
    {
    statosu = digitalRead (Su);
    statogiu = digitalRead (Giu);
    
    if (statosu == HIGH)
    {
      correttore = correttore + 100; // aumenta di 100 
      tempoinizio = millis(); // resetta tempoinizio   
      lcd.print("correttore ");
      lcd.println(correttore);
      delay(500);
    }
    if (statogiu == HIGH)
    {
      correttore = correttore - 100;  // diminuisce di 100
      tempoinizio = millis(); // resetta tempoinizio
      lcd.print("correttore ");
      lcd.println(correttore);
      delay(500);
    }
    tempocorrente = millis();
    Serial.print("tempo corrente ");
    Serial.println(tempocorrente);
   
    }
  tempoinizio = millis();
  Serial.print("tempo inizio  ");
  Serial.println(tempoinizio);
  delay(1000);
}

void loop() {
  statosu = digitalRead(Su);
  if (statosu == HIGH)
  {
    tempoinizio = millis();
    lcd.setBacklight(1);    // accende la luce del display LCD)
  }
  tempocorrente = millis();
  Serial.println("+");
  if (tempocorrente - tempoinizio > 15000)  // 15 secondi
    {
     lcd.clear();
     lcd.setCursor(0, 1);
     lcd.print("passati 15 sec.");
     delay(5000);
    lcd.setBacklight(0);  // spegne l'illuminazione del display
    }
}

I tasti hanno un pin a +5v, l’altro pin è messo a massa tramite un resistore di 10k che tiene gli ingressi digitali 6 e 7 bassi.
Quando premo un tasto gli ingressi vanno a +5v.
Saluti

LelloGi

ciao,

sicuramente sta cosa non va bene:

 tempoinizio = (millis);  // memorizza il tempo di inizio compensazione
 delay(1000);
 tempocorrente = (millis)

@LelloGi un consiglio, nelle impostazioni dell'IDE attiva il massimo livello di warning sia in compilazione che in caricamento, ti sarebbero quasi sicuramente saltati fuori due warning sulle righe indicate da ORSO2001 e avresti trovato l'errore immediatamente

ORSO2001:
ciao,

sicuramente sta cosa non va bene:

 tempoinizio = (millis);  // memorizza il tempo di inizio compensazione

delay(1000);
tempocorrente = (millis)

Ho tolto "tempocorrente" ma non è cambiato niente

fabpolli:
@LelloGi un consiglio, nelle impostazioni dell'IDE attiva il massimo livello di warning sia in compilazione che in caricamento,

Ho spuntato "Tutti" ma non mi da nessun warning.
LelloGi

ciao

il problema che volevo segnalarti è che si scrive:

millis()

e non:

(millis)

LelloGi:
Ho spuntato "Tutti" ma non mi da nessun warning.

NON basta spuntare, occorre anche selezionare quale livello di warning si vuole!

Guglielmo

@LelloGi perdonami, ma non ho capito cosa vorresti fare, né dalla tua descrizione né dal codice (a proposito, fallo indentare per bene dall'IDE, basta premere Ctrl-T) anche perché in genere nella setup() difficilmente si mettono dei while().

Prova a descrivere esattamente cosa dovrebbe fare, uno schemino del tipo (è un esempio):

  1. accendo Arduino e sul display vedo...
  2. premo il pulsante X e sul display vedo...
  3. se premo il pulsant eY entro 10 secondi, allora...
  4. ...eccetera...

Il dubbio che il while non funziona nel Setup è venuto anche a me.
Infatti ho messo una istruzione per fare funzionare un cicalino quando entro nel While ma il cicalino non suona.
Lo sketch che ho in mente di fare deve eseguire due cose:
Per primo devo modificare la variabile "correttore" incrementandola o diminuendola all'inizio del mio programma.
Ho a disposizione 10 secondi per premere uno dei due tasti o entrambi per modificare la variabile.
Ogni volta che premo un tasto il tempo dei 10 secondi riparte a contare da capo.
Se non premo tasti, dopo 10 secondi vado a eseguire il programma principale.
Lo sketch l'ho scritto per testare la routine dei 10 secondi.
Nel "Loop" invece testo una funzione che mi spegne il display del LCD dopo 15 secondi.
Se voglio fare riaccendere il display per altri 15 secondi, premo il tasto "statoSu".
Questa routine funziona.
Poi il resto del programma continuerò a farlo se riesco a risolvere questo problema.
Lo sketch è pieno di istruzioni "Serial.print" e "lcd.print" per testarlo e vedere cosa contengono le variabili, visto che non fa quello che voglio che faccia.
Spero di essere stato chiaro.
Ho il dubbio che i tasti non funzionino correttamente, anche se sono nuovi.
Ho allegato lo schema del loro funzionamento.
La R1 è di 10K.
Saluti

LelloGi

uso_del_pulsante.jpg

uso_del_pulsante.jpg

setup() e loop() nel C non esistono: sono delle “invenzioni” del mondo di Arduino. Sono solo due funzioni: all’avvio viene eseguita la funzione setup(); poi viene eseguita la funzione loop(), che equivale a un while (1) {} o simile. Non c’è ragione, quindi, per cui qualcosa possa non funzionare nel setup().

A me successe che un for non funzionava nel setup… Avevo scritto qualcosa come:

for(x=1; x<5; x++);
{
...
}

:smiley:

Nel setup fai così: ogni volta che premi uno dei due tasti: t_10s=millis();
In questo modo, se millis()-t_10s>10000 significa che sono trascorsi 10 secondi dall'ultima pressione.
Poi tempocorrente è una complicazione inutile che occupa inutilmente RAM: usa direttamente millis().

Capisco le difficoltà di chi è poco esperto ma pensare che, una qualsiasi istruzione standard di un linguaggio usato da qualche decennio da un "discreto" numero di professionisti non funzioni, è follia pura :slight_smile:

Nel tuo caso penso che più di un problema nel codice sia un problema legato ad eventuali rimbalzi dei pulsanti che non hai collegato con un debounce hardware

Datman:
setup() e loop() nel C non esistono: sono delle "invenzioni" del mondo di Arduino ...

... esatto ... difatti, come hai detto, sono due funzioni e sono obbligatorie non per qualche misterioso motivo, ma perché, l'unica funzione che è sempre presente nel 'C' è la main() (... ovvero il cuore del programma principale) e, tale funzione, si trova definita nel "core" di Arduino e ... indovinate cosa fa, tra le altre cose?

int main(void)
{
	init();

	initVariant();

#if defined(USBCON)
	USBDevice.attach();
#endif
	
	setup();
    
	for (;;) {
		loop();
		if (serialEventRun) serialEventRun();
	}
        
	return 0;
}

... chiama una volta la funzione di nome setup() e poi, in un for senza fine, chiama in continuazione la funzione loop() ... ed ecco perché sono entrambe obbligatorie, altrimenti il linker non saprebbe cosa chiamare ;D

Guglielmo

LelloGi:
Il dubbio che il while non funziona nel Setup è venuto anche a me.

Aspetta, non ho detto che un while() nella setup() non funzioni, è che “in genere” nella setup() non servono dei while() perché i cicli li gestisci nel loop().

Lo sketch che ho in mente di fare deve eseguire due cose:
Per primo devo modificare la variabile “correttore” incrementandola o diminuendola all’inizio del mio programma.
Se non premo tasti, dopo 10 secondi vado a eseguire il programma principale.
Nel “Loop” invece testo una funzione che mi spegne il display del LCD dopo 15 secondi.
Se voglio fare riaccendere il display per altri 15 secondi, premo il tasto “statoSu”.

Ok, ora riesco a capire meglio. Vorrei però, più che farti piccole correzioni al codice o ai collegamenti (ad esempio il debounce che ha citato poco fa fabpolli), darti qualche indicazione generale che penso ti possano essere utili non solo ora ma anche nel futuro.

Quindi, oltre a consigliarti caldamente di indentare sempre il codice (per info vedi il regolamento del forum, punto 17.2) ci sono anche altre cose che ti conviene applicare e mantenere, come le costanti da distinguere con nomi tutti in maiuscolo, affiancare le parentesi al nome della funzione, eccetera.

Poi quando ti accorgi che un programma non è “lineare”, ossia c’è una specifica sequenza di comportamenti, conviene pensarlo come una cosiddetta “macchina a stati finiti”, concetto che ti consiglio di apprendere ed approfondire un poco perché in questo campo (ossia microcontrollori) ti sarà sicuramente utile non solo per evitare di “perderti” nei meandri delle condzizioni, ma anche per impostare correttamente alcune cose, e creare programmi molto più “flessibili” per eventuali future modifiche. E’ diciamo una forma mentale, più che un metodo di implementazione, perché puoi realizzarla in più modi.

Per fare un “micro-bignamino” delle macchine a stati finiti, puoi iniziare elencando gli stati e per ognuno di questi le condizioni che, se verificate (quindi con delle condizioni “se”) fanno “qualcosa” ed a quale stato passare (con un “allora”) che può anche essere lo stesso. Ossia uno schema analogo alle “if”:

Se (condizione) allora (azione) e vai allo stato (stato)

Una o più di queste parti possono essere omesse.

Quindi nel tuo caso direi che, ad esempio, hai una macchina a stati finiti descrivibile così:

Stati: INIZIO, VALORI e RUN
Stato iniziale: INIZIO

INIZIO:
(attiva timer, accendi display) e vai allo stato (VALORI)

VALORI:
Se (il timer è >= 10 secondi) allora (ripristina timer) e vai allo stato (RUN)
Se (il tasto Su è premuto) allora (incrementa valore, ripristina timer) e vai allo stato (VALORI)
Se (il tasto Giù è premuto) allora (decrementavalore, ripristina timer) e vai allo stato (VALORI)

RUN:
Se (il timer è >= 15 secondi e lo schermo è acceso) allora (spegni display) e vai allo stato (RUN)
Se (il tasto Su è premuto) allora (accendi display, ripristina timer) e vai allo stato (RUN)

Lo stato INIZIO puoi implementarlo nella setup() visto che è solo una inizializzazione, poi il resto lo gestisci nel loop(), ad esempio uno schemino generico potrebbe essere questo, vedi come ti ho riconfigurato tutto lo sketch:

#include <PCF8574_HD44780_I2C.h>
// Definizione dei pin
#define P_SU 7   // pulsante Su
#define P_GIU 6  // pulsante Giu

// Stati: INIZIO, VALORI e RUN
#define S_INIZIO 0
#define S_VALORI 1
#define S_RUN 2
byte stato;

// Flag che indica se lo schermo è spento
bool schermo = false;

// Variabili globali
int statosu = 0;
int statogiu = 0;
int correttore = 2500;
unsigned long tmr = 0;
byte stato = 0; 
PCF8574_HD44780_I2C lcd(0x27, 16, 2);

void setup() {
  pinMode(P_SU, INPUT);
  pinMode(P_GIU, INPUT);

  lcd.init();
  Serial.begin(9600);
  // Stato iniziale: INIZIO
  stato = S_INIZIO;

  // Per debug:
  Serial.println("INIZIO");

}
  
void loop() {
  // Gestione degli stati
  switch (stato) {
    case S_INIZIO:   // INIZIO:
      //  (attiva timer, accendi display) e vai allo stato (VALORI)
      tmr = millis();
      lcd.setBacklight(1);
      schermo = true;
      mostraCorr();
      stato = S_VALORI;
        // Per debug:
      Serial.println("VALORI");
      break;

    case S_VALORI:   // VALORI:
      //  Se (il timer è >= 10 secondi) allora (ripristina timer) e vai allo stato (RUN)
      if ( millis() - tmr >= 10000 ) {
        tmr = millis();
        stato = S_RUN;
        // Per debug:
        Serial.println("RUN");
        break;
      }
      //  Se (il tasto Su è premuto) allora (incrementa valore, ripristina timer) e vai allo stato (VALORI)
      if ( digitalRead(P_SU) ) {
        correttore = correttore + 100; // aumenta di 100
        mostraCorr();
        debounce();
        // Per debug:
        Serial.println(correttore);
        break;        
      }
      //  Se (il tasto Giù è premuto) allora (decrementavalore, ripristina timer) e vai allo stato (VALORI)
      if ( digitalRead(P_GIU) ) {
        correttore = correttore - 100; // diminuisci di 100
        mostraCorr();
        debounce();
        // Per debug:
        Serial.println(correttore);
        break;        
      }

    case S_RUN:   // RUN:
      //  Se (il timer è >= 15 secondi e lo schermo è acceso) allora (spegni display)
      if ( millis() - tmr >= 15000 && schermo ) {        
        lcd.setBacklight(0);
        schermo = false;
        // Per debug:
        Serial.println("Spengo lo schermo");
      }
      //  Se (il tasto Su è premuto) allora (accendi display, ripristina timer)
      if ( digitalRead(P_SU) ) {
        lcd.setBacklight(1);
        schermo = true;
        tmr = millis();
        debounce();
        // Per debug:
        Serial.println("ACCENDO lo schermo");
        break;        
      }
  }
}

void debounce() {
  // Un minimo di debounce e ritardo, per evitare di far 
  // "correre" il valore (da rivedere in seguito...)
  delay(500);
}

void mostracorr() {
  lcd.setCursor(0, 0);
  lcd.print("Corr.:      ");
  lcd.setCursor(0, 7);
  lcd.print(correttore);
}

PS: il codice NON l’ho provato, sia perché non ho quella libreria, sia perché è meglio se verifichi tu (so già che qualche errore c’è…), così fai anche pratica a fare debug… :wink:

Grazie dei consigli, ho sempre da imparare.
Il "debounce" mi è sfuggito, non ci avevo pensato.
Ho allegato lo schema da dove sono partito e che faccio normalmente quando ho un'idea da sperimentare.
La prossima volta metto in pratica anche il consiglio della "Macchina a stati finiti" di docdoc.
Dopo la verifica del buon funzionamento dei due tasti, mi dedico al listato che mi è stato proposto da docdoc.
Ho notato come è ben "strutturato", rispetto al mio.
Saluti

LelloGi

docdoc:
PS: il codice NON l'ho provato, sia perché non ho quella libreria, sia perché è meglio se verifichi tu (so già che qualche errore c'è...), così fai anche pratica a fare debug... :wink:

C'era solo la variabile "stato" inizializzata due volte e la funzione "mostraCorr()" con la c minuscola.
Tutto qua!
Grazie dell'esempio che appena posso lo provo.
LelloGi

Purtroppo anche l’ultimo sketch che mi è stato inviato non funziona.
In questo caso il programma non entra nei cicli di “if” anche premendo i tasti.
Ho verificato i collegamenti e il buon funzionamento dei due tasti, è tutto a posto.
Le configurazioni software sono corrette.
Proverò a pensare ad un’altra soluzione.
LelloGi

Proverò a pensare ad un'altra soluzione.
LelloGi

Per sicurezza allega il file ino anziché copiarlo qui. Il motivo è che nel codice di @docdoc non vedo errori per i quali non entri nelle if. Sicuramente vorrai introdurre dei limiti oltre i quali la variabile correttore non deve andare, ad esempio premendo il tasto giù prima o poi la variabile in questione sarà minore di 0 (-100).

La macchina a stati implementata da docdoc è indicata per quello che vuoi fare, per cui non mi discosterei da questa implementazione con switch case.

Ciao.

Ho allegato il file con cui ho fatto la prova.
Se non dovesse funzionare, ho intenzione di implementare uno sketch che usa gli “interrupt” abbinato ai due tasti.
Saluti

LelloGi

Test_1.ino (3.12 KB)

alcune libere considerazioni

  1. lo stato S_INIZIO è inutile, dato che viene attivato all'avvio, non verrà mai più attivato e decade subito in S_VALORI, toglietelo e fate fare il suo lavoro a chi è giusto faccia i lavori preparatori: la funzione setup()

  2. per lo stato S_VALORI i due comportamenti tasto su e tasto giu non sono uguali, anzi, uno e solo uno altera erroneamento lo stato corrente

  3. la sequenza di aggiornamento, visualizzazione e debounce è come minimo strana

nel programma postato da docdoc gli ultimi due problemi non c'erano, evidentemente è stato copiato male e corretto peggio.......

Ah...
ho poi capito quale errore ha lasciato dentro docdoc
in effetti basterebbe leggere i commenti e confrontarli con le azioni eseguite