Interrompere ciclo for con pulsante

Salve a tutti!
Ho compilato, con spunti da altri sketch online, un driver per strip led, che cambia modalità alla pressione di un pulsante.

Funziona bene, non riesco però a cambiare modalità (e quindi spegnere, essendo lo stato successivo lo spegnimento) mentre è attivo il ciclo tra i vari colori.
A volte becco il momento esatto da premere per farlo andare avanti, ma vorrei semplicemente poter interrompere il fade in qualunque momento. Come faccio?

#define REDPIN 3
#define GREENPIN 5
#define BLUEPIN 6
#define buttonPin 4 
#define FADESPEED 5     // make this higher to slow down

 int buttonState = 0;
 int buttonPushCounter = 0;
 int lastButtonState = 0;
 int r, g, b;
 
void setup() {
 pinMode(REDPIN, OUTPUT);
 pinMode(GREENPIN, OUTPUT);
 pinMode(BLUEPIN, OUTPUT);
 pinMode(buttonPin, INPUT);
}

void loop() {
 buttonState = digitalRead(buttonPin);

 if (buttonState != lastButtonState) {
   if (buttonState == HIGH) {
     buttonPushCounter++;
   }
   delay(50);
 }

 lastButtonState = buttonState;

 //ROSSO
 if (buttonPushCounter == 1) {
       analogWrite(REDPIN, 255);
       analogWrite(GREENPIN, 0);
       analogWrite(BLUEPIN, 0);
 }
 
 //BLU
 if (buttonPushCounter == 2) {
       analogWrite(REDPIN, 0);
       analogWrite(GREENPIN, 0);
       analogWrite(BLUEPIN, 255);
 }
 
 //VERDE
 if (buttonPushCounter == 3) {
       analogWrite(REDPIN, 0);
       analogWrite(GREENPIN, 255);
       analogWrite(BLUEPIN, 0);
 }
 
 //TEAL
 if (buttonPushCounter == 4) {
       analogWrite(REDPIN, 0);
       analogWrite(GREENPIN, 128);
       analogWrite(BLUEPIN, 128);
 }
 
 //BIANCO
 if (buttonPushCounter == 5) {
       analogWrite(REDPIN, 255);
       analogWrite(GREENPIN, 255);
       analogWrite(BLUEPIN, 245);
 }
 
 //FADE
 if (buttonPushCounter == 6) {
   // fade from blue to violet
 for (r = 0; r < 256; r++) { 
   analogWrite(REDPIN, r);
   delay(FADESPEED);
 } 
 // fade from violet to red
 for (b = 255; b > 0; b--) { 
   analogWrite(BLUEPIN, b);
   delay(FADESPEED);
 } 
 // fade from red to yellow
 for (g = 0; g < 256; g++) { 
   analogWrite(GREENPIN, g);
   delay(FADESPEED);
 } 
 // fade from yellow to green
 for (r = 255; r > 0; r--) { 
   analogWrite(REDPIN, r);
   delay(FADESPEED);
 } 
 // fade from green to teal
 for (b = 0; b < 256; b++) { 
   analogWrite(BLUEPIN, b);
   delay(FADESPEED);
 } 
 // fade from teal to blue
 for (g = 255; g > 0; g--) { 
   analogWrite(GREENPIN, g);
   delay(FADESPEED); 
 }
}
    // Reset del counter
 if (buttonPushCounter == 7) {
   analogWrite(REDPIN, 0);
       analogWrite(GREENPIN, 0);
       analogWrite(BLUEPIN, 0);
   buttonPushCounter = 0;
 }
}

Ho dato una lettura veloce al codice, ma secondo me non risponde alla pressione del pulsante perchè prima è "bloccato" dentro al ciclo for. Tu leggi il valore del pulsante nel loop, ma prima di ritornare su quell'istruzione devi aver completato tutto il ciclo main, e quindi uscire dal for. Ti consiglio di googlare "Arduino Interrupt", dovrebbero fare al caso tuo :wink:

SUBSEA:
Ho dato una lettura veloce al codice, ma secondo me non risponde alla pressione del pulsante perchè prima è "bloccato" dentro al ciclo for. Tu leggi il valore del pulsante nel loop, ma prima di ritornare su quell'istruzione devi aver completato tutto il ciclo main, e quindi uscire dal for. Ti consiglio di googlare "Arduino Interrupt", dovrebbero fare al caso tuo :wink:

Non credo di saperlo sviluppare nel mio sketch.
Non esiste un modo per "includere" il fade in un altro "livello logico", parallelamente al quale si controlla lo stato del pulsante?

Ci sono migliaia di tutorial e esempi già pronti, non far vincere la pigrizia!
Non esiste la programmazione multithread nei microcontrollori, ma si fa ampio (ma non troppo, si sprecano diversi cicli macchina ogni volta che se ne chiama uno) uso degli interrupts. Leggere "in polling" un pulsante non è una buona idea

SUBSEA:
Ci sono migliaia di tutorial e esempi già pronti, non far vincere la pigrizia!
Non esiste la programmazione multithread nei microcontrollori, ma si fa ampio (ma non troppo, si sprecano diversi cicli macchina ogni volta che se ne chiama uno) uso degli interrupts. Leggere "in polling" un pulsante non è una buona idea

Il problema è che non sono molto pratico, e rischio di lasciarlo così. Finirò per mettere un secondo pulsante che comanda un relè e interrompe la tensione.

Il loop deve correre, nulla deve rallentarlo. Perciò non devi fare il fade con un semplice for e un delay, ma devi incrementare il valore ogni volta che è trascorso il tempo prefissato (FADESPEED). Ogni volta che il loop arriva lì, leggi millis() e verifichi se è passato il tempo.

P.S.: edita il tuo primo messaggio con "modify", seleziona il listato del programma e fai clic su <>.

Datman:
Il loop deve correre, nulla deve rallentarlo. Perciò non devo fare il fade con un semplice for è un delay, ma devi incrementare il valore ogni volta che è trascorso il tempo prefissato (FADESPEED). Ogni volta che il loop arriva lì, leggi millis() e verifichi se è passato il tempo.

P.S.: edita il tuo primo messaggio con "modify", seleziona il listato del programma e fai clic su <>.

Potresti farmi un esempio pratico su cosa dovrei scrivere?

Presumo che il tuo problema sia di interrompere il ciclo di fade giusto?
Cambi gradualmente il colore ogni 5 millisecondi, tenendo i conti tondi per rendere il concetto a scapito della precisione, hai 256 passi per ogni colore e 5 millisecondi tra uno step e l'altro, quindi un giro completo dura più di un secondo e lo fai 6 volte ciclando 2 volte sui 3 colori, quindi in tutto la sequenza impiega più di 6 secindi, siamo quasi a 9 facendo i conti a mente.
In tutto questo tempo non puoi testare il pulsante.
Giusto?

Ora, se non sei pratico il metodo è di forza bruta, cioè su ogni for dei 6 del fade devi ritestare il pulsante e farlo uscire dal for se premuto. Inoltre prima di iniziare ogni for devi ritestare che sia ancora a 6 il buttonPushCounter altrimenti esci da un for soltanto ma continui ad eseguire quelli rimanenti.

Questo è l'approccio: scrivo tante volte il test perchè non so fare altro, della serie chi non ga testa ga gambe :wink:

Se invece vuoi fare le cose in modo migliore hai tendenzialmente 2 possibilità:

  1. Invece di rifare il test del pulsante ogni volta usi le interrupt, però devi sempre adottare la strategia per uscire anticipatamente dai for e per non ripetere quelli residui.
  2. Ristrutturi tutto il loop in modo differente.
    Descrivo brevissimamente:
    Ti crei due variabili, una per stabilire quale dei 6 for devi eseguire e una per dire a che punto del for sei.no s
    Nel loop fai due operazioni: testi il pulsante - modifichi i pun.
    La modifica dei pin la fai in base alle due variabili che poi incrementi di un passo.
    Se rilevi la pigiatura del pulsante reimposti le variabili per la sequenza successiva.

In altre parole il loop deve dire: è stato premuto il pulsante e devo cambiare sequenza?
Si = Inizializzo la nuova sequenza ed eseguo il primo passo.
No = Continuo eseguendo il passo successivo della sequenza corrente.

Sono stato stringatissimo solo per farti intuire la possibilità, se servirà si può approfondire in seguito.
3) Un mix di 1 e 2. Cioè ristrutturi comunque il loop ma il pulsante lo rilevi con le interrupt.

Ovviamente serve una strategia di debounce sul pulsante.
Poi si potrebbe anche sostituire delay con il test su millis...

Ora starebbe a te capire quanto ti vuoi impegnare per superare i tuoi limiti attuali.

maubarzi:
Presumo che il tuo problema sia di interrompere il ciclo di fade giusto?
Cambi gradualmente il colore ogni 5 millisecondi, tenendo i conti tondi per rendere il concetto a scapito della precisione, hai 256 passi per ogni colore e 5 millisecondi tra uno step e l'altro, quindi un giro completo dura più di un secondo e lo fai 6 volte ciclando 2 volte sui 3 colori, quindi in tutto la sequenza impiega più di 6 secindi, siamo quasi a 9 facendo i conti a mente ......

Finalmente qualcuno che da una mano concreta!
Purtroppo il problema non è nella volontà di impegnarsi, ma nel tempo!!! Studierò il da farsi, con calma e tanto tempo, grazie dello spunto importante!

--- quando quoti NON serve riportare tutto, bastano poche righe per far capire a cosa ti riferisci, Gli utenti da device mobile ti ringrazieranno. - gpb01

Ciao! Io per uscire dal for() leggerei lo stato del pulsante all'interno del for(), adesso stai usando delay() molto piccoli che non influenzano la lettura del pulsante.
Quindi tra uno step e l'altro del ciclo for, leggo il pulsante, se premuto faccio qualcosa, cambio stato e reinizio la funzione loop con un return.

// fade from teal to blue
 for (g = 255; g > 0; g--) { 
   analogWrite(GREENPIN, g);
   if(digitalRead(pulsante)==HIGH){

        stato++; // cambio stato per fare qualcos'altro
        return; // Termino e reinizio la funzione loop() che farà qualcos'altro perché stato è cambiato

   }
   delay(FADESPEED); // I delay tra step e step sono piccolissimi che non influenzano la lettura del pulsante
 }

edofalcone:
Finalmente qualcuno che da una mano concreta!
Purtroppo il problema non è nella volontà di impegnarsi, ma nel tempo!!! Studierò il da farsi, con calma e tanto tempo, grazie dello spunto importante!

Primo, quotare il precedente (e lungo) messaggio è inutile.
Secondo, anche gli altri ti hanno aiutato e ti hanno detto molto bene, ovvero la logica del programma è da rivedere se vuoi fare una cosa ben fatta. Poi, se testi il tasto nel for potrebbe funzionare, soprattutto nel caso specifico ma non è detto che funziona ancora se poi devi fare altre modifiche o aggiunte al tuo sketch.

In queste MCU non c'e' un sistema multitasking, ne un sistema operativo che lo permette.
Di solito si usa millis() con delle tecniche ampiamente sperimentate e documentate (basta cercare sul forum e/o goggle)

Leggo dalla tua presentazione che sei interessato ad elettronica e ad imparare a programmare.
Per il caso specifico magari con if del tasto nel for risolvi, ma ti invito a studiarti l'uso di millis() e poi una volta capito, cerca (per casi più complessi) la tecnica della programmazione a stati finiti.

Ho letto ora che leggere il pulsante durante l'esecuzione del ciclo for() è tecnica da poca testa :slight_smile: :slight_smile:
Però io mi sono soffermato sul quesito "interrompere ciclo for con pulsante" e non come scrivere un programma che faccia questa determinata cosa :wink:
E' a dire la verità mi viene poca voglia di "pensare" se lo scopo sono effetti luminosi di led :slight_smile: non è il tipo di progetto che mi affascina :slight_smile:

@torn24, nessuno dice che la tecnica suggerita è "da poca testa". Semplicemente può funzionare nel caso specifico. Ma se poi l'utente vuole aggiungere altro al programma probabilmente avrà problemi. Siccome leggendo la presentazione dell'utente @edo si evince che è giovane, appassionato e intenzionato ad imparare, gli è stato suggerito di studiare altre tecniche, più flessibili. Tutto qui.

Si, scusate se ho usato quell'espressione, era ovviamente solo una battuta, @nid69ita lo ha capito e ci ha dato il giusto peso...
In gergo certe soluzioni sono anche dette "quick and dirty" e si usano in ogni campo, non volevo criticare o denigrare nessuno e mi scuso se invece è passato questo messaggio.

@torn24, la tua risposta, secondo me, era corretta, infatti anche la mia che l'ha preceduta è partita dalla stessa considerazione che hai fatto tu, poi ho proposto anche altro, ma cambia poco. Spero di non averti offeso, se è così me ne scuso

@maubarzi non mi sono offeso :slight_smile: Non ho fatto studi attinenti all'informatica, sono un autodidatta con i suoi limiti :slight_smile: non potrei proporre la stessa soluzione di un laureato in informatica, ed è bene cosi altrimenti laurearsi non servirebbe a niente :wink:
Però penso che sono due categorie che non si dovrebbero confrontare tra di loro, come un calciatore di serie A, e chi fa una partita a calcetto :wink:

Non sono d'accordo, ma non ti allarmare, ora mi spiego meglio.

  1. Il titolo di studio non conta, se uno dice una cosa giusta è giusta a prescindere dal titolo di studio.
  2. Ci sono vari modi di affrontare i problemi e nessuno è giusto o sbagliato in assoluto.
  3. La mia era solo una battuta che traduco, sperando di non fare peggio:

Chi no ga testa = chi non riesce ad immaginare un modo per fare meno fatica
ga gambe = usa il metodo che gli fa fare più fatica.

Non era riferita alla tua risposta, primo perchè è arrivata dopo e io non leggo nel pensiero :stuck_out_tongue: e poi perchè tu hai aggiunto la soluzione sul codice preesistente per essere meno invasivo.

  1. Io sono un ex giocatore di calcetto in parrocchia e una volta ho giocato con un ex giocatore di serie A del Napoli. Su un'azione ci siamo trovati uno contro l'altro e io l'ho fregato facendo poi pure gol.
    ... abbiamo poi perso 10 a 1 :frowning:

Non vorrei che passasse il messaggio che la tua risposta è sbagliata e la mia giusta, anche perchè avendo proposto la stessa cosa non avrebbe proprio senso :wink: vedrai che l'OP sceglierà proprio questa soluzione, citata da entrambi, di duplicare il test per uscire dai for perchè probabilmente al momento è quella più compatibile con la sua disponibilità di tempo :wink:

  1. Traduco anche la successiva espressione quick and dirty in: anche se stilisticamente si può fare in modo più elegante, questo trucco bistrucco rapido e facile da applicare, funziona!