Go Down

Topic: Consiglio su come sostituire delay con millis (Read 4125 times) previous topic - next topic

ziopippo

Jan 04, 2018, 07:28 pm Last Edit: Jan 08, 2018, 10:31 am by ziopippo Reason: Dare maggiore leggibilità all'oggetto del post
Buon anno a tutti!

Vorrei sostituire il delay con millis ad un codice che viene ripetuto più volte nello sketch tramutandolo in una funzione.

Il codice è questo:
Code: [Select]
if (value_int > Apertura)
                {
                  /*
                    Serial.print("Apro la tenda da posizione:");
                    Serial.print(Apertura);
                    Serial.print("% a posizione:");
                    Serial.print(value_int);
                    Serial.print("%");
                  */
                  digitalWrite(RCH2, LOW);
                  delay(1800); // Attendo 1,8 secondi prima di riposizionare il relè sullo stato iniziale.
                  //Operazione necessaria per simulare la pressione del tasto altrimenti la centralina non riceve correttamente il comando
                  digitalWrite(RCH2, HIGH);
                  while (value_int > Apertura)
                  {
                    StampaApertura();
                    //Serial.println(Apertura);
                  }
                  digitalWrite(RCH2, LOW);
                  delay(250); //Attendo 250 millisecondi prima di riposizionare il relè sullo stato iniziale, altrimenti la centralina non processa correttamente il comando
                  digitalWrite(RCH2, HIGH);
                  client.print(F("status ok"));
                  /*
                    client.print(value_int);
                    client.print(",Apertura:");
                    client.print(Apertura);
                  */
                }


Chi mi aiuta a sostituire il delay(1800) e delay(250) con millis?
Io ho provato ma non ottengo i risultati voluti.

Questo è il codice che ho provato a modificare:
Code: [Select]
if (value_int > Apertura)
                {
                  /*
                    Serial.print("Apro la tenda da posizione:");
                    Serial.print(Apertura);
                    Serial.print("% a posizione:");
                    Serial.print(value_int);
                    Serial.print("%");
                  */
                  RCHT = RCH2; //Alzo la tenda
                  push_timer = 1800; // Attendo 1,8 secondi prima di riposizionare il relè sullo stato iniziale.
                  tenda_slider();
                  while (value_int > Apertura)
                  {
                    StampaApertura();
                    //Serial.println(Apertura);
                  }
                  push_timer = 250; //Attendo 250 millisecondi prima di riposizionare il relè sullo stato iniziale, altrimenti la centralina non processa correttamente il comando
                  tenda_slider();
                  client.print(F("status ok"));
                  /*
                    client.print(value_int);
                    client.print(",Apertura:");
                    client.print(Apertura);
                  */
                }


void tenda_slider() //Funziome per l'apertura o la chiusura parziale della tenda
{
  if (millis() - t_tenda > push_timer) // Attendo 1,8 secondi prima di riposizionare il relè sullo stato iniziale.
  {
    t_tenda = millis();
    digitalWrite(RCHT, LOW);
  }
  //Operazione necessaria per simulare la pressione del tasto altrimenti la centralina non riceve correttamente il comando
  digitalWrite(RCHT, HIGH);
}


Grazie

Puso

millis è il tempo da cui parte lo schetc che dopo un certo periodo riparte da 0.
puoi usarlo come confronto per un cronometro.
per sfruttarlo ti serve innanzitutto una lunghezza senza segno.
ricorda che al cronometro ogni tanto gli devi dire da che momento cominciare a contare e quando fermarsi.

ziopippo

#2
Jan 04, 2018, 10:46 pm Last Edit: Jan 04, 2018, 10:47 pm by ziopippo
millis è il tempo da cui parte lo schetc che dopo un certo periodo riparte da 0.
puoi usarlo come confronto per un cronometro.
per sfruttarlo ti serve innanzitutto una lunghezza senza segno.
ricorda che al cronometro ogni tanto gli devi dire da che momento cominciare a contare e quando fermarsi.
Grazie ma avevo dato per scontato di aver dichiarato
Code: [Select]

unsigned long t_tenda = 0; //Timer per la tenda
unsigned long push_timer = 0; //Timer per durata della pressione del tasto della tenda

ORSO2001

#3
Jan 04, 2018, 11:16 pm Last Edit: Jan 04, 2018, 11:17 pm by ORSO2001
ciao...secondo me stai facendo una "bruttissima" cosa; hai in un if() con un while() annidato che verificano la stessa condizione...e dopo il while() esegui quella funzione della verifica tempo...tenda_slider().

in quel while c'è una sola funzione...StampaApertura()...spero che in essa ci sia la l'aggiornamento delle variabili value_int e Apertura...altrimenti non ci esci più...e comunque quando ci esci, dato che l'if() confronta la stessa cosa, esegui una volta la tua funzione tenda_slider() e poi !?


ORSO2001

adesso ho visto meglio...chiami una volta, prima del while la funzione tenda_slider()....passi per il while()...e dopo riesegui la tenda_slider()...tralasciando, ma neanche tanto, che la funzione ti eseguirà sempre e solo digitalWrite(RCHT, HIGH) dato che è l'ultima riga fuori da ogni condizione...e la variabile t_tenda che valore ha all'inizio?

ziopippo

Posto il codice completo funzionante con il delay.

Ovviamente è solo una porzione di tutto lo sketch.

Code: [Select]


// funzione per la verifica dello stato di apertura della tenda
void StampaApertura()
{
  Ohm = (map(analogRead(sensore), 0, 1023, 0, 10000)); // Assegno la mappa alla variabile Ohm per la corretta lettura dello stato di apertura
  Apertura = (map((Ohm), 0, 10000, 0, 140));
}


         //Inizio apertura comandata tenda
          int char_pos_a = 0;
          int char_pos_b = 0;
          if (readString.startsWith("GET /?out=0&value="))
          {
            char_pos_a = readString.indexOf("value=");
            char_pos_b = readString.indexOf(" HTTP/1.1");
            String value = readString.substring(char_pos_a + 6, char_pos_b + 1);
            double value_int = value.toInt();
            {
              /*
                Serial.print("char_pos_a:");
                Serial.println(char_pos_a);
                Serial.print("char_pos_b:");
                Serial.println(char_pos_b);
                Serial.print("value contiene:");
                Serial.println(value);
              */
              if (value_int >= 0 && value_int <= 100)
              {
                if (value_int > Apertura)
                {
                  /*
                    Serial.print("Apro la tenda da posizione:");
                    Serial.print(Apertura);
                    Serial.print("% a posizione:");
                    Serial.print(value_int);
                    Serial.print("%");
                  */
                  digitalWrite(RCH2, LOW);
                  delay(1800); // Attendo 1,8 secondi prima di riposizionare il relè sullo stato iniziale.
                  //Operazione necessaria per simulare la pressione del tasto altrimenti la centralina non riceve correttamente il comando
                  digitalWrite(RCH2, HIGH);
                  while (value_int > Apertura)
                  {
                    StampaApertura();
                    //Serial.println(Apertura);
                  }
                  digitalWrite(RCH2, LOW);
                  delay(250); //Attendo 250 millisecondi prima di riposizionare il relè sullo stato iniziale, altrimenti la centralina non processa correttamente il comando
                  digitalWrite(RCH2, HIGH);
                  client.print(F("status ok"));
                  /*
                    client.print(value_int);
                    client.print(",Apertura:");
                    client.print(Apertura);
                  */
                }
                if (value_int < Apertura)
                {
                  /*
                    Serial.print("Chiudo la tenda da posizione:");
                    Serial.print(Apertura);
                    Serial.print("% a posizione:");
                    Serial.print(value_int);
                    Serial.print("%");
                  */
                  digitalWrite(RCH1, LOW);
                  delay(1800); // Attendo 1,8 secondi prima di riposizionare il relè sullo stato iniziale.
                  //Operazione necessaria per simulare la pressione del tasto altrimenti la centralina non riceve correttamente il comando
                  digitalWrite(RCH1, HIGH);
                  while (value_int < Apertura)
                  {
                    StampaApertura();
                    //Serial.println(Apertura);
                  }
                  digitalWrite(RCH1, LOW);
                  delay(250); //Attendo 250 millisecondi prima di riposizionare il relè sullo stato iniziale, altrimenti la centralina non processa correttamente il comando
                  digitalWrite(RCH1, HIGH);
                  client.print(F("status ok"));
                  /*
                    client.print(value_int);
                    client.print(",Apertura:");
                    client.print(Apertura);
                  */
                }
              }
              else
              {
                Serial.println(F("Valore fuori range"));
                client.print(F("Error, value out of range"));
              }
            }
          }
          //Fine apertura comandata tenda


ziopippo

...e la variabile t_tenda che valore ha all'inizio?
Grazie ma avevo dato per scontato di aver dichiarato
Code: [Select]

unsigned long t_tenda = 0; //Timer per la tenda
unsigned long push_timer = 0; //Timer per durata della pressione del tasto della tenda


ziopippo

ciao...secondo me stai facendo una "bruttissima" cosa; hai in un if() con un while() annidato che verificano la stessa condizione...e dopo il while() esegui quella funzione della verifica tempo...tenda_slider().

in quel while c'è una sola funzione...StampaApertura()...spero che in essa ci sia la l'aggiornamento delle variabili value_int e Apertura...altrimenti non ci esci più...e comunque quando ci esci, dato che l'if() confronta la stessa cosa, esegui una volta la tua funzione tenda_slider() e poi !?


In che modo mi consigli di ottimizzarlo?

ORSO2001

ciao...io ragionerei in questo modo...nello spezzone del tuo sketch verifichi se readString.sartsWith(....) ritorna true...se true appunto analizzi altre parti della Stringa ricevuta per ricavarne dei valori...ecco li io, invece di includere tutte le restanti azioni, imposterei una variabile globale (che tra le altre cose includere nella verifica di questo if in modo da non eseguire due volte la stessa azione) da 0 ad 1...con questa variabile ad 1 abilito una funzione od un pezzo di codice, esterno a questo if() che mi gestisca con millis() il digitalWrite()...magari il tempo di partenza di millis() l'ho salvato nella if della Stringa....eseguito il primo digitalWrite metto la stessa variabile a 2 e passo alla funzione StampaApertura...finita questa imposto la variabile a 3 e salvo ancora millis() ...con varibile a 3 eseguo il digitalWrite() di chiusura e finito anche questo imposto la variabile a 0.

non conoscendo tutto il codice e cosa deve fare di preciso mi viene in mente questo.

spero di esseremi spegato bene

ziopippo

Dovrò rileggerlo molto attentamente e più volte domani perchè ora non ho la lucidità per riuscire a capire a pieno quanto mi stai consigliando.
E' un codice che scrissi nel lontano 2013 e solo da poco, dopo anni di pausa sabatica dalla programmazione causa lavoro, ho ripreso in mano cercando di modificarlo dopo aver "scoperto" millis.
Se può esserti utile però poso allegare o, se lo preferisci mandartelo in privato, l'intero codice e volendo anche quello che stavo cercando sostituire con millis.

Claudio_FF

#10
Jan 06, 2018, 01:09 am Last Edit: Jan 21, 2018, 04:15 pm by Claudio_FF
La funzione millis DA SOLA NON È LA SOLUZIONE se non si modifica anche la struttura del programma, trasformandola in macchine a stati finiti (quello che dice orso con altre parole).

Anzi, organizzando tutto come macchine a stati, per assurdo si può anche continuare ad usare delay, ma solo per piccoli tempi e/o per "regolare" il tempo di esecuzione del loop principale.

Si deve perciò pensare al programma non come sequenza monolitica di istruzioni (ritardi compresi) da eseguire dall'inizio alla fine in un colpo solo, ma spezzare invece l'esecuzione di tutte le parti in più passaggi ciclici, singolarmente molto veloci e senza ritardi di alcun genere.

La funzione loop potrebbe incaricarsi solamente di richiamare ciclicamente i vari processi (ad esempio circa 50 volte al secondo come nell'esempio seguente):
Code: [Select]
void loop(){
    processo1();
    processo2();
    processo3();
    delay(20);
}

Ogni processo può essere uno switch controllato da una propria variabile di stato:
Code: [Select]
void processo1(){
    switch(stato_proc1){
        case 0:
            break;
        case 1:
            break;
        case 2:
            break;
    }
}

In ogni case si può scrivere il frammento di codice da eseguire nello stato attuale, che deve:

  • Controllare se si verifica una certa condizione
  • Eventualmente eseguire qualche operazione
  • Eventualmente cambiare la variabile di stato

Con questa struttura un ritardo è semplicemente la permanenza in un certo stato per un certo numero di cicli. Si può ottenere con millis, o con un contatore che incrementa/decrementa ad ogni chiamata del processo, è -quasi- indifferente, quello che conta è che la logica sequenziale di una lunga funzione che monopolizza tutto il tempo si trasforma in una velocissima logica di controllo: rilevazione_evento->azione->cambio_stato.
(Un esempio relativo alla rilevazione click/longpress di un pulsante)

Questo permette di:

  • Portare avanti N processi in parallelo in tempo reale
  • Aggiungere nuove funzionalità o processi indipendenti senza toccare quelli preesistenti
  • Scomporre una logica troppo complessa da affrontare tutta assieme in piccole macchine semplici, che inviano "segnali" alle altre tramite flag (esempio variabili globali)
* * * * Una domanda ben posta è già mezza risposta * * * *

Maurotec

@Claudio_FF

Ho li hai tramortiti, oppure è l'effetto delle feste (o entrambe).  ;)
Purtroppo ho costato più volte che quando tiri in ballo le macchine a stati finiti, l'utente rimane sconvolto. Mentre se gli suggerisci la funzione millis(), alla fine ne vengono fuori, si ma con un pastrocchio di if.

Comunque speriamo non siano rimasti tramortiti, ma solo sconvolti e che quindi a breve si riprenderanno, postando qualcosa. :)

Sempre in merito a FSM, qualche hanno addietro mi è venuta la fantastica idea di creare una applicazione basata su FSM facente uso di puntatori a funzione, la macchina ricordava anche lo stato precedente e in base a questo e ad altre flags decideva quale doveva essere lo stato successivo, fatto ciò, il break terminava la funzione e il controllo tornava al loop.

Dentro al loop venivano svolte alcune operazione cardine e poi c'era la chiamata a funzione tramite puntatore.

L'idea sembrava figa e funzionava alla grande, purtroppo dal punto di vista del programmatore seguire il flusso diventava macchinoso, sono andato nel pallone più volte, perché non sapevo chi decideva e quale sarebbe stata la funzione chiamata nel loop.

Allora ho implementato per ogni stato, le tre transizioni:
A) Ingresso (poteva decidere se confermare lo stato o dirottarlo o abortire);   
B) Stato corrente: poteva decidere se e quando dirottare verso l'ultima transizione;
C) Uscita da stato corrente: poteva o meno decidere il prossimo stato, se non lo faceva la macchina eseguiva una funzione standard.
D) Abort opzionale: transizione alternativa scelta da A al posto di B.

Conclusione:
1) Implementare una macchina a stati finiti è divertente.
2) Usarla è un incubo
3) Sentivo la mancanza di un gestore di eventi che li catturasse.

Quote
Comunque speriamo non siano rimasti tramortiti, ma solo sconvolti e che quindi a breve si riprenderanno, postando qualcosa.
Gli ho dato la botta finale, non si riprenderanno mai più. :smiley-twist:

Ciao.


ziopippo

#12
Jan 06, 2018, 01:51 pm Last Edit: Jan 06, 2018, 01:52 pm by ziopippo
Né l'uno né l'altro @Maurotec.
È solo l'effetto smartphone/tablet che ha inibito sin'ora la mia risposta.
Nella versione che sto cercando di revisionare per l'appunto anche con la sostituzione del dalay a favore di millis ho rivisto un po' tutto spezzando il codice e creando diverse schede per una maggiore leggibilità.
Il grosso problema resta però un buon pezzo di codice tra cui l'estratto che ho pubblicato che si occupa di rispondere alle richieste json per mostrare i dati su una app da me creata ad hoc su smartphone Android o direttamente facendo una specifica richiesta da browser
Ovviamente con lo smartphone non ho possibilità di allegare codice

Claudio_FF

#13
Jan 06, 2018, 04:56 pm Last Edit: Jan 13, 2018, 12:55 pm by Claudio_FF
Quote from: Maurotec
1) Implementare una macchina a stati finiti è divertente.
2) Usarla è un incubo
Forse stiamo pensando a due diverse implementazioni, perché tra le varie opzioni per scrivere il codice (simulazione porte logiche/relé, codice procedurale ciclico con flag/if, macchina a stati realizzata con switch) la macchina a stati mi è sempre sembrata la più chiara. Però bisogna scomporre tutte le azioni del programma in piccole macchine, e non realizzare un'unica grossa macchina che gestisce tutto, quello si diventa un incubo.

Quote from: Maurotec
3) Sentivo la mancanza di un gestore di eventi che li catturasse.
Una macchina apposita che li rileva e comanda le altre macchine... o qualcosa del genere?

Quote from: Maurotec
Il grosso problema resta però un buon pezzo di codice tra cui l'estratto che ho pubblicato che si occupa di rispondere alle richieste json per mostrare i dati
Perché volevi usare millis al posto di delay? Per rendere non bloccante quella funzione? Ok si può riscrivere in modo non bloccante, ma poi va usata in modo diverso, cioè va richiamata in continuazione condizionando il suo comportamento con una variabile di stato. Questa modifica però potrebbe essere incompatibile con l'intera struttura del resto del programma.
* * * * Una domanda ben posta è già mezza risposta * * * *

ziopippo

Allego il codice completo visto che non riesco ad allegare solo la parte relativa alle richieste JSON essedo troppo lungo.


Perché volevi usare millis al posto di delay? Per rendere non bloccante quella funzione? Ok si può riscrivere in modo non bloccante, ma poi va usata in modo diverso, cioè va richiamata in continuazione condizionando il suo comportamento con una variabile di stato. Questa modifica però potrebbe essere incompatibile con l'intera struttura del resto del programma.
Esattamente, hai centrato il nocciolo del problema. Convertendo il delay con millis spero di ridurre i tempi di risposta ed evitare noiosi blocchi seppur momentanei della funzione.

Go Up