pressione bottone per accensione/spegnimento

Il progetto è quello di un sensore che richiede la temperatura in loop. In base alla temperatura piloto l'accensione o lo spegnimento di una ventola. Per adesso ho scritto lo sketch e tutto funziona, ma alimentando arduino parte direttamente il loop di valutazione temperatura.
Vorrei aggiungere un bottone per accedere e spegnere il dispositivo.

Ci ho provato ma non ci sono riuscito e volevo chiedervi come potrei scrivere il codice per far in modo che :

prima della pressione del bottone il sensore sia OFF

alla pressione del bottone parta il loop di richiesta temperatura e quindi accensione/spegnimento ventola.

alla seconda pressione si esca fuori dal loop e si ritorni allo stato OFF

Una variabile inizializzata a 0 o LOW, es run = 0

if(run == High)  {
    // il codice qui contenuto viene eseguito 
    // ad ogni ciclo di loop
}

Visto che run inizialmente vale zero il codice dentro la if(run ... non viene eseguito.

Ora prendendo spunto dal codice di esempio presente nell'ide devi trovare il modo
di fara cambiare stato alla variabile run, quindi quando premi il pulsante run deve diventare il contrario
di quello che è o meglio il suo valore deve essere invertito o negato.

Rilevato pressione {
Run = Il contrario di Run, si può fare con if (run == High) run = Low; else run = High;
}

Devi studiare l'esempio debounce presente nell'ide, anzi a che ci sei anche blink without delay.

Ciao.

Richiamandolo con una funzione condizionale ? ... oppure in un'if condizionale ?

Premetto che non sono un programmatore (per cui scuserete il linguaggio da hardwarista :P), ma dato che sto studiandomi anch'io una routine per condizionare l'esecuzione di alcune cose allo stato di una variabile cambiata da un pulsante, forse puo funzionare nello stesso modo anche per te ...

questo e' quello che sto facendo io (i miei pulsanti sono tutti collegati con rete RC agli ingressi, quindi non ho bisogno di nessuna routine di debounce, hanno il pullup interno abilitato, quindi vengono letti verso massa, e li ho impostati per eseguire l'azione nel momento in cui vengono rilasciati invece che quando vengono premuti, in modo che l'azione viene eseguita una sola volta per pressione, anche se uno tiene il pulsante premuto piu tempo del necessario, ed anche dando un "beep" ad un cicalino per confermare l'azione, ma puoi rimuovere quella parte se non ti serve)

//variabili dichiarate
byte p1 = 0;
byte p1a = 0;
byte cond = 0;

... ecc ...

void setup() ... ecc ...

void loop()
	{
	p1 = digitalRead(pulsante); //leggi il pulsante
	if ((p1 != p1a) && (p1a == 0) // il pulsante e' stato premuto
	{
		p1a = p1; //metti p1a allo stesso valore di p1 per la verifica del rilascio
	}
 	if ((p1 != p1a) && (p1a == 1) //il pulsante e' stato rilasciato
	{
		p1a = p1; //rimetti p1a al valore di p1

		if (cond == 0) //ciclo che cambia stato alla variabile cond 	
		{
			cond = 1;
		}
		else
		{
			cond = 0;
		}
		tone(24, 440, 50); //singolo beep di conferma

		if (cond == 1) //se cond e' uno, esegui le istruzioni seguenti, altrimenti no
		{
			.....
			// istruzioni da eseguire
			.....
		}
	}

Spiegazione: prima di tutto le variabili p1, p1a e cond sono dichiarate come byte ed impostate a 0 di default, poi nel ciclo il controllo del pulsante viene richiamato in continuazione ... i due cicli if consecutivi li ho scritti cosi perche' facciano qualcosa solo nel momento in cui il pulsante CAMBIA stato, ma ignorino le condizioni stabili, cioe' sia che il pulsante sia premuto o no, non fanno nulla, eseguono il contenuto solo quando si verifica il cambio di stato (sempre se ci ho azzeccato :P) ... il primo if controlla se p1 e p1a sono diverse E se p1 e' a zero, se no, il pulsante non e' stato premuto ... nel momento in cui premi il pulsante, p1 va a zero, quindi il primo controllo vede il pulsante premuto (p1 diverso da p1a E p1 = 0), cambia stato a p1a portandola uguale a p1 (in modo che i successivi cicli di controllo ignorino tutto il tempo in cui il pulsante rimane premuto) ... nel momento in cui il pulsante viene rilasciato, il secondo ciclo vede p1 diverso da p1a E p1 = 1, quindi riporta p1a uguale a p1, e cambia stato alla variabile cond (usata per condizionare il ciclo seguente) ... a questo punto il ciclo seguente esegue la tua routine condizionale solo se cond e' ad 1 ... dato che ogni pressione e rilascio del pulsante cambiano il valore di cond, con il pulsante decidi se eseguire le sitruzioni nell'ultimo if o no, quindi se metti la tua routine di misura dentro l'ultimo if, te la esegue o meno in base allo stato di cond ...

(softwaristi, dateci un'occhio pure voi ... e se e' una ca**ata, siete pregati di evitare il lancio di molotov e granate a frammentazione, grazie :P)

Metti un interruttore che stacca l'alimentazione...

paulus1969:
Metti un interruttore che stacca l'alimentazione...

Eh no, troppo comodo ... quando certe soluzioni le propongo io, i softwaristi mi tacciano di semplificazionismo (:stuck_out_tongue: XD), ora tocca a me rinfacciarvi lo stesso :stuck_out_tongue: :stuck_out_tongue_closed_eyes: :grin: ]:smiley:

A livello di logica il codice è corretto e in certe occasioni sarebbe da usare in quanto ha degli effetti che in certe circostanze portano grandi vantaggi. Mi riferisco al fatto che il codice non viene mai ripetuto se non c'è una necessità, ovviamente i test condizionali quelli si che vengono valutati ed è un peccato che richiedano più tempo cpu di una assegnazione che viene evitata.

Comunque se funziona correttamente merita di essere rivisto per una migliore comprensione didattica.
Dovresti provare a modificare (refactor) i nomi delle variabili in modo che siano esplicativi e che quindi non richiedano commenti, inoltre potresti verificare se c'è la possibilità di usare una o più funzioni con nome esplicativo al fine ultimo di rendere il codice comprensibile e funzionale allo stesso tempo.

Così com'è scritto fra qualche settimana farai fatica a capire cosa fa realmente il codice e fra qualche mese ti chiederai: Chi l'ha scritto? e cosa fa? bhoo. :stuck_out_tongue:

Ciao.

Etemenanki:
Eh no, troppo comodo ... quando certe soluzioni le propongo io, i softwaristi mi tacciano di semplificazionismo (:stuck_out_tongue: XD), ora tocca a me rinfacciarvi lo stesso :stuck_out_tongue: :stuck_out_tongue_closed_eyes: :grin: ]:smiley:

Con me non vale.
Non sono un softwarista. Sono pigro.

Paulus: :stuck_out_tongue: :stuck_out_tongue: :stuck_out_tongue:

MauroTec: l'ho postato cambiandolo "al volo" dallo sketch che sto cercando di completare, ho modificato solo il nome "pulsante", per questo e' un po incasinato ... io uso sempre questo tipo di nomi per le variabili, vecchia abitudine da basic :stuck_out_tongue: ...

Cosa intendi con "una o piu funzioni" ? ... nel mio sketch la routine di controllo dei pulsanti e' una funzione "esterna" richiamata dal loop, qui l'ho messa nel loop solo perche' non ho postato il resto dello sketch (non e' ancora completato, ed e' incasinato) ... per quanto riguarda i test condizionali (if), non so se ci sia un sistema migliore, come ho detto, sono un'hardwarista, non un softwarista ... questo sistema me lo sono immaginato "al volo" mentre cercavo di risolvere un'altro problema, ma non so se sia il migliore ( anzi, quasi sicuramente non lo e' ) ... solo che a logica mi sembrava adatto, quando mi e' venuto in mente ...

Ete ero quasi certo che le cose stavano come le hai descritte, e per questo ho scritto:

Comunque se funziona correttamente merita di essere rivisto per una migliore comprensione didattica.

Era per evitare di dare un esempio di codice funzionante, che mostra come non dovrebbe essere scritto il codice sorgente.
byte p1 = 0;
Le variabili globali non devono esistere e se esistono non devono avere nomi così corti e sarebbe anche il caso di scriverle in modo particolare, come: GnomeVar, o tutte maiscole o con la _G finale o iniziale, capisci che quando il codice diventa lungo e il pezzo di codice che usa p1 funziona ormai da tempo il rischio di dimenticare di aver usato p1 o dichiarare un'altra variabile p1 c'è e fa perdere pazienza e tempo.

Variabili così corte possono essere usate in scope corti, cioè poche linee di codice in contesto locale, quindi dentro una funzione, o dentro un blocco {} innestato dentro una funzione.

Visto che la lunghezza del nome di una variabile non fa incrementare né la ram, né la flash non c'è motivo di scriverne così corte e poco esplicative.

PS: guarda che trovare un nome ad una variabile o funzione non è così semplice e alle volte girano perché questo vuol dire che il codice non è strutturato correttamente e questo capita troppe volte che ogni tanto un nome di variabile astruso lo scegli solo perché ti sei scocciato.

Ciao.

LOL, capisco cosa intendi ... no, ho usato p1 (ed altre) perche' essendo associate ai pulsanti, mi richiamano quale pulsante e' (p1 = pulsante 1, ecc) ... le dichiaro globali perche' poi qualsiasi funzione possa usarle (ad esempio nel mio sketch le variabili delle condizioni sono impostate in una funzione void checkpuls() e poi controllano l'esecuzione in altre funzioni diverse, dichiarandole globali non corro il rischio di "perdermele" ... almeno, il mio ragionamento era quello)

Ho modificato il pezzo che ho usato come esempio, comunque, per cercare di renderlo pui comprensibile, e l'ho diviso in due diversi esempi, secondo te e' meglio cosi ?

Esempio uno, codice da eseguire solo una volta, quando il pulsante viene premuto, oppure quando viene rilasciato, oppure due diversi codici da eseguire uno quando il pulsante e' premuto e l'altro quando e' rilasciato, a scelta (premessa: pulsanti con pullup interno abilitato e rete RC)

//variabili globali dichiarate (per poterle usare in qualsiasi funzione)
byte Gbutton1 = 0;
byte Goldbutton1 = 0;

... ecc ...

void setup() ... ecc ...

void loop()
{
	Gbutton1 = digitalRead(PinPulsante1); //legge lo stato del pulsante
	if ((Gbutton1 != Goldbutton1) && (Goldbutton1 == 0) // il pulsante e' stato premuto (cambio di stato)
	{
		Goldbutton1 = Gbutton1; //mette Goldbutton1 allo stesso valore di Gbutton1 
                 //nota: se una funzione deve essere eseguita nel momento in cui il pulsante viene premuto,
                 //ed una volta sola, va messa qui, perche' questa condizione viene eseguita solo una
                 //volta, quando il pulsante passa da "non premuto" a "premuto"
	}
 	if ((Gbutton1 != Goldbutton1) && (Goldbutton1 == 1) //il pulsante e' stato rilasciato (cambio di stato)
	{
		Goldbutton1 = Gbutton1; //rimette Goldbutton1 al valore di Gbutton1
                 //nota: se una funzione deve essere eseguita nel momento in cui il pulsante viene rilasciato,
                 //ed una volta sola, va messa qui, perche' questa condizione viene eseguita solo una
                 //volta, quando il pulsante passa da "premuto" a "non premuto"                 
	}
}

Esempio 2, istruzioni diverse, anche all'interno di funzioni indipendenti diverse, da eseguire o meno in base allo stato di una variabile modificata dalla pressione di un pulsante (esempio indicativo, alla variabile condition potrebbero essere assegnati anche valori diversi, cosi da poterla usare per condizionare piu di due sole combinazioni di istruzioni)

//variabili globali dichiarate (per poterle usare in qualsiasi funzione)
byte Gbutton1 = 0;
byte Goldbutton1 = 0;
byte Gcondition = 0;

... ecc ...

void setup() ... ecc ...

void loop ()
{
        chechButtons();
        funzione1();
        funzione2();
}

void checkButtons()
{
	Gbutton1 = digitalRead(PinPulsante1); //legge lo stato del pulsante
	if ((Gbutton1 != Goldbutton1) && (Goldbutton1 == 0) // il pulsante e' stato premuto (cambio di stato)
	{
		Goldbutton1 = Gbutton1; //mette Goldbutton1 allo stesso valore di Gbutton1 
        }         
 	if ((Gbutton1 != Goldbutton1) && (Goldbutton1 == 1) //il pulsante e' stato rilasciato (cambio di stato)
	{
	Goldbutton1 = Gbutton1; //rimette Goldbutton1 al valore di Gbutton1
	if (gcondition == 0) //ciclo che cambia stato alla variabile condition 	
	{
		Gcondition = 1;
	}
	else
	{
		Gcondition = 0;
	}
        //nota: questo ciclo if si puo usare sia nel primo ciclo di controllo (se si
        //vuole cambiare la variabile alla pressione del tasto) che nel secondo
        //ciclo (come qui, per cambiarla al rilascio del tasto)
}

void funzione1()
{
        ....
        //istruzioni da eseguire sempre
        ....
	if (Gcondition == 1) //se gcondition e' uno, esegui anche le istruzioni seguenti, altrimenti no
	{
	        .....
	        // istruzioni da eseguire solo se Gcondition e' uno
		.....
	}
}

void funzione2()
{
        ....
        //istruzioni da eseguire sempre
        ....
	if (Gcondition == 0) //se condition e' zero, esegui anche le istruzioni seguenti, altrimenti no
	{
	        .....
	        // istruzioni da eseguire solo se Gcondition e' zero
		.....
	}
}
... ecc

Un test pratico comunque al momento non lo posso fare, perche' ho appena riformattato tutto, e sto bestemmiando (in Sanscrito antico, con sottotitoli in Georgiano corsivo :P) per cercare di reinstallare tutti i programmi di lavoro (che regolarmente non vanno mai al primo colpo perche' manca sempre qualche fantomatico driver custom ... maledetto fantasma di murphy, se lo becco lo smaterializzo :stuck_out_tongue: XD)

Io ho risolto in maniera molto semplicistica...ma rimane il problema del debounce. Avendo masticato poco il codice non riesco a scriverlo in maniera piu decente

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

  if (buttonState == HIGH) {
    
    lastBtnState = !lastBtnState;
  }
    delay(70); 
    if (lastBtnState == HIGH){
    
    Serial.println("Ciclo in loop");
    digitalWrite(ledPin, HIGH);    
  } 
  if (lastBtnState == LOW) {
    
    digitalWrite(ledPin, LOW);
  }
  
}