contatore click pulsante

Buonasera a tutti :slight_smile:
sto scrivendo un programma che conti quante volte viene premuto un pulsante in 30 secondi ed il risultato dovrebbe essere visualizzato su uno schermo lcd.
Ho scritto un programma ma sullo schermo vengono visualizzati dei numeri in progressione sulla prima riga e uno 0 sulla seconda. Non capisco dove sto sbagliando, qualcuno mi può aiutare?
di seguito vi riporto il codice

// include the library code:
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
int buttonApin = 2;
int x;
int A;


void setup() {
                                                          // set up the LCD's number of columns and rows:
 lcd.begin(16, 2);
 lcd.print("Hello, World!");                   //all'accenzione sullo schermo appare la scritta "Hello, World!"
 delay(3000);                                     //pausa di 3 secondi
 pinMode(buttonApin, INPUT);             //imposta il pin digitale del BOTTONE come valore da leggere
}

void loop() 
{
 lcd.clear();                                        //reset dello schermo
 for (int x = 0; x<=30; x=x+1)            //CICLO FOR ora è da 30 sec, va fatto per 3 min
 {
 lcd.setCursor(0, 1);                     
 lcd.print(x);                            //sullo schermo appaiono i vari valori di x che cambiano ogni secondo. 
                                                 Dopo 30 secondi finisce il ciclo for
 delay(1000);
 
 while(x<=30)                             //operazioni da eseguire mentre la variabile x del precedente ciclo for 
                                                     è minore o uguale a 30
  {
   if (digitalRead(buttonApin) == LOW)    //mentre x è inferiore a 30 la variabile A incrementa di 1 se il 
                                                              pulsante è in modalità LOW (quindi se viene premuto)
   {
     A=A+1;
   }
  lcd.setCursor(0, 0);
  lcd.print(A);                           //stampa sullo schermo lcd il risultato della variabile A
 }
}
}

Grazie in anticipo :slight_smile:
Shade

>OmbraShade: ti ricordo che in conformità al regolamento, punto 7, devi editare il tuo post qui sopra (quindi NON scrivendo un nuovo post, ma utilizzando il bottone More → Modify che si trova in basso a destra del tuo post) e racchiudere il codice all’interno dei tag CODE (… sono quelli che in edit inserisce il bottone con icona fatta così: </>, tutto a sinistra). Grazie.

Guglielmo

Perdona Guglielmo, svista mia :slight_smile: spero di essere riuscita a modificare il post correttamente :slight_smile: grazie ancora :slight_smile:

ciao OmbraShade,

al primo "giro" di loop() entri nel for()..inizializzi x = 0...posizioni il cursore su (0,1) dell'LCD etc...entri nel while() in quanto x √® "0"...e dato che all'interno nel while() x non lo modifichi pi√Ļ...non sei pi√Ļ in grado di uscirne.

dato che ci sono aggiungo anche che...il delay()è bloccante...arduino per tutta la durata del delay() diventa "sordo"...in pratica esegue solo una volta il loop()...arriva al delay() e si blocca li...questo ogni volta...in pratica(...e 2) se hai un delay(1000) , e nulla altro che ti blocchi il programma, il loop() verrà eseguito 60 volte in un minuto.

per non bloccare il loop() gestendo azioni con tempistiche "precise" devi usare millis() o micros() (stessa cosa con scale diverse)...qui nel forum se ne è parlato tantissimo.

grazie ORSO2001
ho riscritto il codice… ora il tempo funziona ma la variabile A che dovrebbe segnare il numero di volte in cui viene premuto il pulsante, ogni volta che scadono i 3 secondi (ho diminuito il tempo per semplificarmi la vita) segna un numero a caso diverso e non capisco perchè…
Di seguito vi riporto il codice:

#include <LiquidCrystal.h>            //include the library code

LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
const int BOTTONE = 2;                //PIN a cui è collegato il bottone
int StatoBOTTONE = 0;                //stato iniziale del pulsante
int A;

void setup() {
  lcd.begin(16, 2);
  lcd.print("Hello, World!");         // Print a message to the LCD.
  pinMode(BOTTONE, INPUT);           //imposta il pin digitale del BOTTONE come input (valore da leggere)
}

void loop() 
{
  A = 0;
  lcd.clear();                       //pulisci schermo lcd
     for (int x = 0; x<=6; x=x+1)       //incrementa la variabile x di 1 fino ad arrivare a 6 con un ritardo di 1 secondo (quindi il ciclo for durerà 3 secondi)
     {
      lcd.setCursor(0, 1);
      lcd.print(x/2);
        if (digitalRead(BOTTONE) == LOW)    //mentre x è inferiore 6 la variabile A incrementa di 1 se il pulsante è in modalità LOW (quindi se viene premuto)
                {
                A=A+1;
                }
        delay(500);
      }
  
  lcd.setCursor(0, 0);
  lcd.print(A);
  delay(5000);
}

hai impostato il pin BOTTONE come INPUT quindi parte da stato LOW e se in ingresso riceve i 5 volt diventa HIGH.

nel tuo for hai scritto:

if (digitalRead(BOTTONE) == LOW)

quindi finchè non dai i 5 volt al pin per farlo divenire HIGH la variabile A si incrementerà sempre...delay() permettendo.

ritorno a dire che questa non è la via giusta per conteggiare il numero di volte che il pulsante viene premuto in tot tempo...cioè NON usare il delay().

una via possibile è quella di creare, oltre alla variabile BOTTONE, anche una di flag, magari una boolean (true o false) per verificare se il BOTTONE è stato premuto o rilasciato, in modo da leggere un solo cambio di stato e non leggerlo perennemente ogni ciclo loop()...e millis().

ORSO2001 grazie mille, ho provato a seguire i tuo consiglio e ad usare una boolean‚Ķ ho scritto un nuovo programma eliminando del tutto il timer (sempre per semplificarmi la vita) ma non riesco a capire dove sto sbagliando‚Ķ in questo nuovo programma dovrebbe apparire la scritta ‚Äúpapaya‚ÄĚ sullo schermo lcd se NON premo il tasto e se invece lo premo dovrebbe apparire la scritta ‚ÄúTASTO PREMUTO‚Ä̂Ķ invece le due scritte sono sovrapposte fin quando non premo il pulsante, allora appare solo la scritta ‚ÄúTASTO PREMUTO‚Ä̂Ķ dove sto sbagliando?

#include <LiquidCrystal.h>            //include the library code

LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
int BOTTONE = 2;                //PIN a cui è collegato il bottone

void setup() {
Serial.begin(9600);
pinMode(2, INPUT);
}




void loop() {
   if (digitalRead(BOTTONE)==true)
  {
  lcd.setCursor(0, 1);
  lcd.print("papaya");
  }

 else if (digitalRead(BOTTONE)==false)
  {
  lcd.setCursor(0, 1);
  lcd.print("TASTO PREMUTO");
  }
}

ti allego un esempio di come usare una variabile boolean come ‚Äúflag‚ÄĚ (cambioStato)‚Ķla variabile cambia il suo valore da false a true se il pulsante viene premuto o no; in questo modo le istruzioni nelle due if() vengono eseguite solo una volta.
ATTENZIONE: ho impostato il pin del BOTTONE come INPUT_PULLUP; vuol dire che NON gli devi dare 5Volt per attivarlo ma lo devi collegare, passando per il pulsante, a GND di arduino; questo perch√® con INPUT_PULLUP lo forzi a 5volt, quindi a stato HIGH, e se collegato a GND va a 0Volt, quindi stato LOW; in pratica devi ragionare al contrario; tutto ci√≤ serve per limitare i possibili disturbi che ti possono entrare dal pin impostato come ‚Äúsemplice‚ÄĚ INPUT.
Se hai dubbi chiedi pure.

#include <LiquidCrystal.h>            //include the library code

LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

int BOTTONE = 2;                //PIN a cui è collegato il bottone
boolean cambioStato = false;   // variabile "flag" per determinare cambio stato bottone; in questo modo si esegue solo una volta il comando

void setup() {
  Serial.begin(9600);
  pinMode(2, INPUT_PULLUP);  // ATTENZIONE COLLEGARE IL PIN A GND (PIN->BOTTONE->GND)!!!
}

void loop() {

  if (!cambioStato && digitalRead(BOTTONE)) {  // digitalRead(BOTTONE) ritorna HIGH se NON premuto
    lcd.clear(); //  resetto lo schermo ed imposto a (0,0) il cursore
    lcd.print("NON PREMUTO !");
    cambioStato = true;  //  eseguo cambio stato del flag
  }

  if (cambioStato && !digitalRead(BOTTONE)) {  // digitalRead(BOTTONE) ritorna HIGH se NON premuto
    lcd.clear(); //  resetto lo schermo ed imposto a (0,0) il cursore
    lcd.print("PREMUTO !");
    cambioStato = false;  //  eseguo cambio stato del flag
  }
}

ORSO2001 ho seguito le tue istruzioni e caricato il tuo programma... FUNZIONA!!! :smiley: :smiley: :smiley: :smiley: :smiley: grazieeeeeeeeeeeee grazie milleeeeee!!! :smiley: :smiley: :smiley: :smiley: :smiley:

Ultimo "nodo": nel programma che hai scritto ho inserito una variabile "A" che incrementa di 1 ogni volta che il tasto viene premuto e fin qui tutto bene.
Avrei bisogno però di sapere quante volte il tasto viene premuto nell'arco di 3 minuti, per farlo dovrei inserire tutto in un ciclo For? O c'è un altro modo?
Chiedo perchè inizialmente avevo scritto un programma che faceva solo la parte del "timer" col ciclo For e funzionava ma adesso non so come ragionare per unire le due cose (contatore + timer)

perch√® in realt√† pi√Ļ che inserire tutto nel ciclo For, dovrei dirgli ‚Äúfai questo MENTRE esegui il ciclo For‚ÄĚ, quindi servirebbe un ‚Äúdo while‚ÄĚ, sbaglio?
Per ora per√≤ ho provato senza ciclo while e for e questo √® il nuovo programma che ho scritto. Ma ‚ÄúA‚ÄĚ (la variabile che dovrebbe tenere il conto di quante volte il tasto viene premuto) risulta sempre 0‚Ķ

#include <LiquidCrystal.h>            //include the library code

LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

int BOTTONE = 2;                //PIN a cui è collegato il bottone
boolean cambioStato = false;   // variabile "flag" per determinare cambio stato bottone; in questo modo si esegue solo una volta il comando
int A=0;
int x;

void setup() {
  lcd.begin(16, 2);
  lcd.print("Hello, World!");         // Print a message to the LCD.
  Serial.begin(9600);
  pinMode(2, INPUT_PULLUP);
}

void loop() 
{
  lcd.clear();                       //pulisci schermo lcd
     for (x = 0; x<=6; x=x+1)       //incrementa la variabile x di 1 fino ad arrivare a 3 con un ritardo di 1 secondo (quindi il ciclo for durerà 3 secondi)
     {
      lcd.setCursor(0, 1);
      lcd.print(x/2);
      delay(500);
      }    
     if (!cambioStato && digitalRead(BOTTONE)) {  // digitalRead(BOTTONE) ritorna HIGH se NON premuto
    lcd.clear(); //  resetto lo schermo ed imposto a (0,0) il cursore
    cambioStato = true;  //  eseguo cambio stato del flag
  }

  if (cambioStato && !digitalRead(BOTTONE)) {  // digitalRead(BOTTONE) ritorna HIGH se NON premuto
    lcd.clear(); //  resetto lo schermo ed imposto a (0,0) il cursore
    lcd.print("PREMUTO !");
    cambioStato = false;  //  eseguo cambio stato del flag
    A=A+1;
  }
 
  lcd.setCursor(0, 0);
  lcd.print(A);
  delay(5000);
}

Per raggiungere il tuo scopo, dovresti crearti un contatore di tempo tramite la funzione millis() ‚Äúad esempio che conti i secondi‚ÄĚ

Se i secondi passati sono inferiori ad un valore, leggo lo stato del pulsante e incremento una variabile ad ogni pressione.

unsigned long tempo;
int secondi = 0;
int pulsantePremuto = 0;
void setup() {

  pinMode(BOTTONE, INPUT_PULLUP);
  tempo = millis();
}


void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - tempo >= 1000) { // Se è passato un secondo

    secondi++;
    tempo = millis();
  }

  if (secondi <= (60 * 3)) { // Se il tempo è inferiore a tre minuti

    if (digitalRead(BOTTONE) == LOW) {

      pulsantePremuto++;
      //Finche non rilascio il pulsante attendo con un while()
      while (digitalRead(BOTTONE) == LOW);

    }
  } else if (secondi > (60 * 3)) { // sono passati i tre minuti
    // STAMPO SU LCD
    // Resetto le variabili per nuova lettura
    secondi = 0;
    pulsantePremuto = 0;
    tempo = millis();
   }
 

}

Ciao
secondo me sbagli approccio
innanzitutto hai un pulsante che se lo premi o lo rilasci,cambia il suo stato e già quì secondo me sarebbe meglio scrivere una funzione solo per il pulsante da richiamare quando serve.

void statoPulsante()
{
........
........
........
if(pulsante==premuto){fai questo}
else{non farlo}

poi vorresti sapere quante volte viene premuto in 30 secondi e qui secondo me sei costretta ad usare il millis() che altro non è che un cronometro e ti conviene creare un altra funzione

void 30secondi()
{
if(millis()-tempo>=30000)
{
 statoPulsante()
}
}

in ultimo vorresti leggere tutto sul display LCD(che me lo devo ancora studiare),ma anche qui ti riconsiglio di creare una funzione solo per lui.

void scriviS√ĻLCD()
{
.........
.........
}

Cos√¨ facendo sicuramente ti appare pi√Ļ chiaro dove commetti errori,poi quando li risolvi,richiami ci√≤ che ti serve nel LOOP.

@OmbraShade: attenzione che while() e do-while(), se la verifica della loro condizione ritorna true, costringono arduino ad eseguire solo determinate righe di codice...quindi siamo alla stregua del "bloccante"...vedi il tuo primo esempio...quindi da usare con cognizione.

@torn24: considerando che OmbraShade è agli inizi e , anche, per le cose che gli ho appena scritto, secondo me, è meglio, opinione personale mica devo suggerire a te, suggerire esempi con meno annidamenti possibili.

@Puso: fin tanto che si tratta di scrivere poche righe di codice la creazione e l'uso di funzioni non semplifica molto...anche perchè tipo un'eventuale funzione che paragona millis() con una variabile che memorizza di volta in volta il valore di millis(), per verificarne il tempo trascorso, implica o il passaggio per riferimento di questa variabile, se la vuoi rendere flessibile e riutilizzabile per altre verifiche di tempo, oppure l'uso nella funzione stessa della variabile globale...e allora sarà una funzione che funzionerà solo per quella variabile (molto restrittivo)...oppure una funzione che ritorna la stessa variabile modificata.

Di seguito un esempio di quello che intendo con le tre funzioni...@OmbraShade: le funzioni nel loop() sono commentate...per renderle attive si devono eliminare le prime "//" di ogni riga...

unsigned long myMillis1;
unsigned long myMillis2;
unsigned long myMillis2A;
unsigned long myMillis3;

void setup() {
  Serial.begin(9600);
  myMillis1 = millis();
  myMillis2 = millis();
  myMillis2A = millis();
  myMillis3 = millis();
}
void loop() {
  //tempoTrascorso1(); // stampero ogni 3 secondi riferito a myMillis1
  //tempoTrascorso2(myMillis2,1000);  // decido io a che variabile riferirmi e quanto tempo lasciar trascorrere
  //tempoTrascorso2(myMillis2A,4000);  // decido io a che variabile riferirmi e quanto tempo lasciar trascorrere
¬† //myMillis3 = tempoTrascorso3(myMillis3,2000);¬†  //¬† decido io a che variabile riferirmi e quanto tempo lasciar trascorrere...ma pi√Ļ lunga da scrivere.
}

// FUNZIONI
void tempoTrascorso1() {
  if (millis() - myMillis1 >= 3000) {
    Serial.println("passati 3000 ms");
    myMillis1 = millis();
  }
}

void tempoTrascorso2(unsigned long &myMillisX, unsigned long attesa) { // notare la "&" su myMillis
  if (millis() - myMillisX >= attesa) {
    Serial.print("passati ");
    Serial.print(attesa);
    Serial.println(" ms");
    myMillisX = millis();
  }
}

unsigned long tempoTrascorso3(unsigned long myMillisX, unsigned long attesa) {  //  qui non c'è la "&"
  if (millis() - myMillisX >= attesa) {
    Serial.print("passati ");
    Serial.print(attesa);
    Serial.println(" ms");
    myMillisX = millis();
    return myMillisX;  // però si deve ritornare un valore
  }
  else return myMillisX;  // però si deve ritornare un valore
}

ORSO2001:
@OmbraShade: attenzione che while() e do-while(), se la verifica della loro condizione ritorna true, costringono arduino ad eseguire solo determinate righe di codice‚Ķquindi siamo alla stregua del ‚Äúbloccante‚Ä̂Ķvedi il tuo primo esempio‚Ķquindi da usare con cognizione.

Ma assolutamente NO …
… sono dei semplici cicli che eseguono, sin tanto che è verificata una certa condizione, una serie di istruzioni senza bloccare un bel nulla !

Chiaro che devi sapere che sei dentro un ciclo e che, se durante la sua esecuzione ti serve fare anche qualche altra cosa, dovrai prevedere le chiamate a ci√≤ che ti serve fare ‚Ķ ma questo non ha nulla a che vedere con il concetto di ‚Äúbloccante‚ÄĚ che associamo ogni volta alla fuzione delay(), la quale SI che tiene fermo il codice sino a quando non ha terminato il periodo di attesa.

Guglielmo

costringono arduino ad eseguire solo determinate righe di codice...quindi siamo alla stregua del "bloccante"

@Guglielmo:si hai ragione... pensavo che scrivendo come sopra : ...eseguire poche righe...e..."bloccante" (virgolettato) ...e richiamando quanto detto all'inizio del suo primo esempio...fosse sufficiente...si vede di no :smiley:

Vedi, dato che NON è la prima volta che vedo associare le "strutture" for, while, do .. while, con il concetto di "bloccante" ci tengo sempre a precisare che la cosa NON è affatto vera e che tale associazione non va proprio fatta.

Dette strutture NON hanno nulla di "bloccante" (come è invece per la delay()), lo diventano ovviamente se male utilizzate, ma quello ... può farlo un qualsiasi pezzo di programma scritto con i piedi :smiley: :smiley: :smiley:

Guglielmo

... tipo "while (true){}" ? ... :smiley:

EDIT: aspetta, ma non bloccano comunque tutto il resto del programma se non contengono istruzioni di uscita ? ... voglio dire, ad esempio, un for lungo, che non contenga break o altre possibilita' di uscita, non blocca il resto del programma finche' non ha terminato di contare ? ... forse si intende quello con bloccante ...

... e quello me lo chiami bloccare ? ? ? :o :o :o

Quando chiami la funzione "delay()", quella NON ritorna il controllo al programma fino a quando il tempo non è terminato e quindi il programma è bloccato. Ma se sei in una FOR dove è il blocco ? Il programma sta girando e vengono eseguite le istruzioni che sono nella FOR ... che possono essere quante e quali vuoi !

Se il programma, mentre è nella FOR, non fa alcune cose che andavano fatte, beh ...
... siamo alle solite, hai scritto il codice con i piedi :smiling_imp:

Rileggiti il mio post #14 ... :wink:

Guglielmo

be io comincerei con la fuzione Pulsante