Impostare una durata di tempo fin quando la condizione è vera

salve a tutti sono nuovo nel gruppo e lo trovo molto interessante sono alle prime armi con arduino e non riesco a scrivere una parte di codice a me interessata vi spiego il mio progetto:
partiamo dal fatto che per ora sto soltando simulando con tinkercad e il mio progetto consiste usare arduino per fare birra vi posto il codice che ho costruito fino ora e che funziona :

void setup(){
  Serial.begin(9600);
  pinMode(13,OUTPUT); // pin led temperatura 
  pinMode(12, OUTPUT);// pin elettrovalvola 
  
}
void loop(){
  Serial.println("inizio 1° step birra bionda");
  if(analogRead (A0) == 20){// temperatura iniziale a freddo
    digitalWrite (12, HIGH);// led rosso acceso elettrovalvola on
    delay(1000);
  }else{
    digitalWrite (12, LOW); // led rosso spento elettrovalvola off
  }
  int temperatura = analogRead(A0);
  Serial.println(temperatura);
  if(temperatura == 55){ // temperatura da mantenere per 15 min
    digitalWrite (13, HIGH); // led verde acceso temperatura ok
  }else{
    digitalWrite (13,LOW); // led verde spento temperatura bassa
  }
  if(temperatura == 54){ // temperatura attivazione elettrovalvola
    digitalWrite (12, HIGH); // led rosso acceso elettrovalvola on
  }else{                        
    digitalWrite (12, LOW); // led rosso spento elettrovalvola off
  }
  delay(1000);
  }

allora questa parte di codice legge la temperatura ambiente io l'ho chiamata temperatura iniziale a freddo in questo caso 20 e accende l'elettrovalvola comandata da pin 12, quando la temperatura arriva a 55 spegne l'elettrovalvola e si accende il led comandato dal pin 13 verde per farmi capire che la temperatura è arrivata a 55 però come la temperatura scende a 54 l'elettrovalvola si deve riaccendere facendo il tutto per 15 min per poi finire lo step.

cortesemente qualcuno mi può aiutare a scrivere e a farmi capire questa parte di codice ?

cordiali saluti

ps. non sò se ho scritto nella sezione giusta
allego una foto del circuito che ho creato per simulare la parte di codice che ho scritto riassumo:
-il led rosso acceso mi segnala che c'è la fiamma quindi l'elettrovalvola in funzione collegato al pin 12 come uscita
-la centrale sarebbe la resistenza una (pt100) collegato al pin analogico (A0) *so che ci sono altre cose da fare quando userò la pt100 ma per simularla ho deciso di fare questo circuito con questi componenti
-il led verde acceso collegato al pin 13 come uscita mi segnala che la temperatura è arrivata a quella impostata
il tutto l'ho fatto con tinkercad

Buongiorno e benvenuto :slight_smile: ,

essendo il tuo primo post nella sezione Italiana del forum, nel rispetto del regolamento di detta sezione (… punto 13, primo capoverso), ti chiedo cortesemente di presentarti IN QUESTO THREAD (spiegando bene quali conoscenze hai di elettronica e di programmazione ... possibilmente evitando di scrivere solo una riga di saluto) e di leggere con molta attenzione tutto il su citato REGOLAMENTO

... poi, in conformità al suddetto regolamento, punto 7, cortesemente edita il tuo post qui sopra (quindi NON scrivendo un nuovo post, ma utilizzando il bottone a forma di piccola matita :pencil2: che si trova in basso del tuo post), seleziona la parte di codice e premi l'icona </> nella barra degli strumenti per contrassegnarla come codice.

Inoltre, così com'è, non è molto leggibile ... assicurati di averlo correttamente indentato nell'IDE prima di inserirlo (questo lo si fa premendo ctrlT su un PC o cmd T su un Mac, sempre all'interno del IDE). Grazie.

Guglielmo

P.S.: Ti ricordo che, purtroppo, fino a quando non sarà fatta la presentazione nell’apposito thread e sistemato il codice come da regolamento, nel rispetto del citato regolamento nessuno ti risponderà (eventuali risposte verrebbero temporaneamente nascoste), quindi ti consiglio di fare il tutto al più presto.

salve
ho appena fatto quanto mi hai detto

purtroppo il tuo approccio non è quello adatto per risolvere questo genere di problemi
tutto scusabile, intendiamoci, non sono cose gravi
devi solo cambiare l'idea base

devi pensare al microcontrollore come ad un servizio sempre attivo, che effettua una operazione "singola e non bloccante" e ricomincia,
all'inizio sembra sempre tuta una confusione, ma non temere

se te la cavi con l'inglese ti consiglio questo link:
https://forum.arduino.cc/t/using-millis-for-timing-a-beginners-guide/483573

e anche questo, comunque legato al primo:
https://forum.arduino.cc/t/demonstration-code-for-several-things-at-the-same-time/217158

nel pomeriggio dopo il picnic vado avanti con qualcosa di più pratico

buona festa della Repubblica

Intanto condivido quanto detto da @Standardoil .

Come detto da @Standardoil la funzione loop() è un ciclo infinito, di ciò puoi renderti conto aprendo il serial monitor vedrai la stampa "inizio 1° step birra bionda", molte volte comparire.

Questa ha senso metterla all'inizio e la/le if allora sono simili a:

if (temperatura >

Per questo tipo di applicazioni il delay() è bandito e non si può usare. Al suo posto si usa la funzione millis() ma la sostituzione non è diretta ed immediata.

Dai una lettura qui, per vedere e provare al simulatore una applicazione di automazione.

Ciao.

Trovato dove anni fa ho scritto di questo

Cerca "la pappa è fatta"
E già che ci sei anche "aiutateci ad aiutarvi"
Male non farà...

Visto che è festa nazionale, e sistema operativo nuovo di zecca, oggi provo a spiegarlo in modo diverso :upside_down_face:

Dalla descrizione informale/discorsiva si evincono tre diverse situazioni di funzionamento: 1) riscaldamento iniziale fino a 55 gradi, 2) fase di mantenimento per quindici minuti tra 54 e 55 gradi, e 3) fase di stop.

È naturalmente possibile descrivere la procedura tramite un flowchart:

...che però si accorda male con la natura ciclica dell'elaborazione nella funzione loop. Un flowchart come questo ha quasi più senso inserirlo nella funzione setup, perché una volta eseguito/terminato non c'è più altro da fare, e la funzione loop può restare vuota.

Invece un'elaborazione ciclica basata su ripetute esecuzioni della funzione loop, tenendo conto della situazione attuale di funzionamento, e di "condizioni di trigger" (eventi che fanno passare da una situazione all'altra), si potrebbe rappresentare in questo modo:

2354435634

Con una variabile di stato che assume i valori 1 2 e 3, e un selettore (struttura if o switch), si esegue solo la porzione di codice della situazione attuale. Il flow della fase di riscaldamento iniziale può essere:

678967

Tradotto a parole:

SE temperatura inferiore a 55:
    riscaldamento attivo
ALTRIMENTI:
    riscaldamento spento
    selettore = 2
    salva tempo attuale di sistema

Quando la temperatura raggiunge i 55 scatta la condizione di cambio fase, e ci si salva il tempo di sistema (con la funzione millis), perché è da questo esatto momento che vogliamo contare i quindici minuti.

E così via per le altre fasi.

...

@Claudio_FF , @Standardoil : ho spostato la vostra "chiacchierata" sul OS in Bar Sport.

Lasciamo questo thread pulito ed in-topic. :wink:

Guglielmo

salve
ho modifica il codice e sembra che funzioni però mi stampa sempre la frase2 sul serial monitor come posso risolvere questo problema

String frase1 = ("inizio 1° step birra bionda");
String frase2 = ("fine 1° step birra bionda");
unsigned long startTime;  
unsigned long currentTime;
int unsigned long period = 1000;

void setup(){
  Serial.begin(9600);
  Serial.println (frase1);
  pinMode(13,OUTPUT); // pin led temperatura 
  pinMode(12, OUTPUT);// pin elettrovalvola
  pinMode(11, OUTPUT);// led fine step
  startTime = millis(); 
}
void loop(){
  int temperatura = analogRead (A0);
  Serial.println(temperatura);
  currentTime = millis();
  if (currentTime - startTime >= period){
  digitalWrite(11, 1);
  Serial.println (frase2);
  }
  if(temperatura <= 55){ // se la temperatura e < di 55 attiva elettrovalvola
    digitalWrite (12, 1); // led rosso acceso elettrovalvola on
  }else{
    digitalWrite (12, 0); // led rosso spento elettrovalvola off
  }
  if(temperatura >= 55){
    digitalWrite (13, 1);
  }else{
    digitalWrite (13, 0);
  }
}

Va gia meglio

Però:

  1. Non identifichi correttamente lo stato "terminato" ovvero lasci il programma avanzare anche dopo il termine

  2. Hai ambiguità di comportamento per temperatura pari a 55

  3. Usi gli oggetti Stringa

Oltretutto temo che tu abbia anche sbagliato diagnosi: prima ti stampa la frase1 e poi tante volte la frase 2

Attenzione narra la leggenda che:
Per gli approvvigionamenti di cantinette refrigerate per i propri prodotti un $notomarchio di bibite gasate con bottiglia tutta curve$ chiedesse ai fornitori un campione da esaminare
A fronte di un campione esaminato il loro laboratorio emetteva sempre un rapporto con evidenziati i problemi riscontrati
La cosa non inficiava la fiducia al fornitore, che poteva sempre fornire un nuovo campione migliorato fino al completo soddisfacimento dei requisiti di $noto marchio simbolo dell'America$
MA
Se per caso un successivo campione avesse riportato nuovamente un problema già segnalato il fornitore sarebbe stato depennato dalla lista dei fornitori

Ovvero?
Si possono commettere tutti gli errori, è umano
MA
Una volta segnalati vanno "recepiti", "compresi" E "risolti"
Pena la perdita di credibilità

Tradotto qui per noi?
Metti a posto i punti 1 2 e 3 e "poi" si va avanti

Sei andato a vedere dove ti ho indicato?

Al momento è l'ultimo dei tuoi problemi quello della stampe multiple. Se hai letto al link, nel secondo articolo affronto questa problematica attraverso gli Entry ed Exit state, dai una lettura veloce perché temo che sia ancora presto per comprendere la macchina a stati e come suddividere l'applicazione in stati.
fsm entry and exit state

Puoi condividere maggiori info sull'applicazione?
Come non detto, devi fare la birra.

C'è una funzione nell'ide arduino che indenta il codice CTRL+T, vedi se in tinkercad c'è una funzione simile, quindi usala prima di copiare e postare il codice. Non sottovalutare questo punto, se il codice è ben indentato risulta più comprensibile e riceverai molti più contributi,

Ciao.

Avevo cancellato per sbaglio

Concorquoto appieno

Sì ma per uno che non sa i dettagli? Una spiegazione non sarebbe male

... curiosità ... se andavi sulla matitina arancione in alto a destra (vicino ai minuti ed al numero di post) e l'aprivi con un click, non ti faceva ripristinare quello che avevi scritto?

Guglielmo

A un primo sguardo non avevo visto nulla e ho pensato più facile riscrivere che cancellare

... se ti ricapita fammi sapere che, come moderatore ovviamente me lo fa fare, ma probabilmente lo fa fare anche all'utente che ha scritto :wink:

Guglielmo

Al posto di quelle

if(temperatura ecc

Serve del codice che si comporti come un termostato ON/OFF. Un termostato elettromeccanico ha una isteresi di circa 5°C (o più), detto in altri termini:
IS_ON se T < setpoint
IS_OFF se T > setpoint + 5°C

Ad esempio:
setpoint = 100°C
isteresi = 5°C
Si chiudono i contatto se T < 100°
Si aprono i contatti se T >= 100 + 5

Un termostato digitale può avere setpoint e isteresi configurabili.

Ciao.

La questione non è trovare la combinazione di istruzioni che fanno quello che si vuole, ma pensare una procedura che fa quello che si vuole già funzionante dal punto di vista logico su carta, e che sia poi anche facilmente traducibile in istruzioni (quindi pensata tenendo conto di cosa la macchina "capisce" e di come le cose le vanno descritte: dati e blocchi di programma sequenziali, decisionali e ciclici).

Come detto precedentemente siamo davanti a un processo a tre fasi/step, e queste fasi vanno implicitamente o esplicitamente gestite.

Due possibilità: una procedura bloccante a fasi implicite come quella del flowchart indicato (è il lato oscuro), oppure una procedura non bloccante a fasi esplicite (è la via della forza, la macchina a stati descritta da Maurotec e che io ho disegnato con i tre palloni), questa sarebbe la soluzione migliore per tanti motivi, soprattutto per le modifiche successive.

Quindi due possono essere (con minime variazioni) le procedure scritte su carta da tradurre in istruzioni:

LOOP: (PROCEDURA BLOCCANTE A FASI IMPLICITE - il flowchart)

    leggi temperatura

    SE temperatura inferiore a 55:
        riscaldamento attivo
        FINCHE' temperat < 55:
            rileggi temperatura
        riscaldamento spento

    salva tempo attuale di sistema

    FINCHE' non timeout:
        rileggi temperatura
        SE temperatura <= 54:
            riscaldamento attivo
        SE temperatura >= 55:
            riscaldamento spento

    riscaldamento spento

    stop esecuzione, non specificato cosa fare poi...


LOOP: (CON ELABORAZIONE CICLICA NON BLOCCANTE A FASI ESPLICITE)

    leggi temperatura

    SE fase 1 (RISCALDAMENTO):
        SE temperatura inferiore a 55:
            riscaldamento attivo
        ALTRIMENTI:
            riscaldamento spento
            salva tempo attuale di sistema
            fase = 2

    SE fase 2 (MANTENIMENTO):
        SE temperatura <= 54:
            riscaldamento attivo
        SE temperatura >= 55:
            riscaldamento spento
        SE timeout 15 minuti:
            riscaldamento spento
            fase = 3

    SE fase 3 (STOP):
        niente (non specificato cosa fare oltre)

Poi fossi io l'utente del sistema vorrei avere un pulsante per dare lo start o lo stop a tutto il processo, e magari un buzzerino di avviso fine lavoro (i soliti fastidiosissimi cinque beep dei fornetti). Con la procedura a fasi esplicite (programmazione a stati) queste modifiche si possono aggiungere senza nulla cambiare al resto della logica già funzionante.

buon giorno a tutti ragazzi scusate se non mi sono fatto più sentire ma ho cercato di risolvere il problema da solo e ascoltando i vostri consigli e ci sono riuscito mettendo anche un lcd vi posto il codice

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
String frase1 = ("inizio 1° step birra bionda");
String frase2 = ("fine 1° step birra bionda");
unsigned long startTime;  
unsigned long currentTime;
int unsigned long period = 1000; // durata di tempo 


void setup(){
  lcd.begin(16, 2);
  Serial.begin(9600);
  lcd.print("inizio 1° step birra bionda");
  Serial.println (frase1); // frase inizio step
  pinMode(13,OUTPUT); // pin led temperatura 
  pinMode(7, OUTPUT);// pin elettrovalvola
  pinMode(6, OUTPUT);// led fine step
  startTime = millis(); 
}
void loop(){
  lcd.setCursor(0, 1);
  int temperatura = analogRead (A0);
  Serial.println(temperatura);
  lcd.print(temperatura);
  currentTime = millis();
  if (currentTime - startTime >= period){
    digitalWrite(6, 1);
     Serial.println(frase2);// frase fine step
    lcd.print("fine 1° step birra bionda");
    bool ledOn = true; // led fine step on vero
  while (ledOn){
    if(digitalRead (6) == 1){  
      ledOn = !false; 
    }
  }
    }
  if(temperatura <= 54){   // se la temperatura e < di 55 attiva elettrovalvola
    digitalWrite (7, 1); // led rosso acceso elettrovalvola on
  }else{
    digitalWrite (7, 0); // led rosso spento elettrovalvola off
  }
  if(temperatura >= 55){ 
    digitalWrite (13, 1);//se la temperatura è >= di 55 si accende il led verde
  }else{
    digitalWrite (13, 0);//se la temperatura è <= di 55 si spegne il led verde
  }
}

ora volevo inserire un tasto start come mi è stato consigliato da Claudio FF ma non ci sono riusci mi date una mano su questo

In realtà non hai risolto il problema, o meglio, se il problema era non far scrivere N volte la frase, allora si, "quel" problema è risolto: con un bel while che non termina mai e blocca completamente l'esecuzione di qualsiasi altra cosa. Ma questo è solo un peggiorare le cose, perché appena scaduto il tempo il sistema si freeza allo stato che ha in quel momento, valvola aperta o meno che sia.

Le fasi di funzionamento necessarie (dedotte dalla descrizione del primo post) non sono impostate/gestite nè implicitamente nè esplicitamente.

8746578

Inserire qualsiasi modifica diventa un incubo che va dal difficilissimo all'impossibile anche per i più esperti. Per questo ribadisco che è fondamentale (soprattutto all'inizio) DISEGNARSI PRIMA una logica funzionante su carta (flowchart, pseudocodice ecc, che tra l'altro sono già belli pronti nei miei post precedenti).

Inserire un pulsante di avvio?
In una logica strutturata correttamente è semplice:

SE sono in fase di stop:
   leggo pulsante di start
   SE livello premuto:
       passo a fase di heat

Ma se non c'è nessuna gestione delle fasi, e anche quello che c'è non funziona come da specifiche iniziali, dove scrivo queste righe?:

   leggo pulsante di start
   SE livello premuto:
       ???? e qui che faccio ????

Naturalmente è normale che all'inizio ci siano confusione immensa e tentativi, in questo campo ci vanno sempre mesi se non anni per essere "produttivi". I link indicati da Maurotec sono importanti per acquisire un metodo con cui risolvere ogni tipo di automazione. Lascio qui un esempio (sulla falsariga del contenuto di quei link) con la struttura completa della "macchina" a tre stati, con tanto di lettura pulsante già pronta e inserita per dare l'avvio al processo. Questa struttura di partenza va studiata e capita riga per riga, e poi popolata con le azioni da compiere. È scritta nel modo più flat possibile, senza espressioni logiche, switch ecc, sono solo if else e variabili. Il tutto organizzato in blocchetti corti da leggere.

#define  PIN_PULSANTE ...
#define  PRESS_LEVEL  ...

// Nomi simbolici degli stati
#define  S_HEAT       1
#define  S_MAINT      2
#define  S_STOP       3

bool g_inPrec    =  PRESS_LEVEL;
bool g_onPress;
bool g_onEntry;
int  g_state     =  S_STOP;       // stato attivo iniziale
int  g_oldState  =  ~S_STOP;

//--------------------------------------------------------------------
void loop() 
{
    leggi_pulsante();
    macchina_a_stati();
}
//--------------------------------------------------------------------
void leggi_pulsante()
{
    /* Lettura pulsante con riconoscimento fronte di pressione  */
    /* Non e` previsto alcun debounce software                  */
    /* Quando premuto, g_onPress va a true per un ciclo         */
    g_onPress = false;
    bool in = digitalRead(PIN_PULSANTE);
    if (in != g_inPrec)
    {
        g_inPrec = in;
        if (PRESS_LEVEL == in) { g_onPress = true; }
    }
}
//--------------------------------------------------------------------
void macchina_a_stati()
{
    /* Gestione automatica degli entry code                  */
    /* Quando stato cambia, g_onEntry va a true per un ciclo */
    g_onEntry = false;
    if (g_oldState != g_state)
    { 
        g_oldState = g_state;
        g_onEntry = true;  
    } 

    /* Elaborazione stato attivo */
    if      (S_STOP  == g_state) { elabora_s_stop();  }
    else if (S_HEAT  == g_state) { elabora_s_heat();  }
    else if (S_MAINT == g_state) { elabora_s_maint(); }
}
//--------------------------------------------------------------------
void elabora_s_stop()
{
    /* ENTRY CODE */
    if (g_onEntry) { }

    /* RUN CODE*/
    if (g_onPress) { g_state = S_HEAT; }

    /* EXIT CODE */
    if (g_state != g_oldState) { } 
}
//--------------------------------------------------------------------
void elabora_s_heat()
{
    /* ENTRY CODE */
    if (g_onEntry) { }

    /* RUN CODE*/

    /* EXIT CODE */
    if (g_state != g_oldState) { } 
}
//--------------------------------------------------------------------
void elabora_s_maint()
{
    /* ENTRY CODE */
    if (g_onEntry) { }

    /* RUN CODE*/

    /* EXIT CODE */
    if (g_state != g_oldState) { } 
}
//--------------------------------------------------------------------

A proposito @Maurotec ho trovato interessanti i link e il progetto su wokwi, ho lasciato qui (EDIT: aggiornato con codice più strutturato e ordinato) una versione modificata della sbarra parcheggio, modificando la macchina a stati per poter eseguire più stati attivi contemporaneamente :robot: :robot: :robot: :robot: (nota: ho invertito il significato di state e oldState, state nella mia modifica è la foto non modificabile della situazione precedente, mentre oldState riceve modifiche)