Go Down

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

ziopippo

#15
Jan 06, 2018, 09:51 pm Last Edit: Jan 06, 2018, 10:00 pm by ziopippo
Questa invece è la nuova struttura che sto dando all'intero sketch.



Code: [Select]




void loop(void)
{
  // ************************************* INIZIO loop(void) *************************************
  //RICHIAMO IL VOID JSon  PER IL COLLEGAMENTO CON RICHIESTE JSON A DISPOSITIVO ANDROID
  JSon();

  /* // Stampo i valori del Contatore KWh

     Serial.print(" totPowerCount= ");
     Serial.print(totPowerPulseCounter);
     Serial.print(" totPowerValue= ");
     Serial.print(totPowerValue);
     Serial.println(" Wh");
  */

  if (millis() - t > 5000) //Una volta ogni 5 secondi:
  {
    t = millis();
    // Inizio Sensore di temperatura DALLAS D18B20
    // call sensors.requestTemperatures() to issue a global temperature
    // request to all devices on the bus
    sensors.requestTemperatures(); // Send the command to get temperatures
    // Fine Sensore di temperatura DALLAS D18B20

    Cayenne.run();

    // INVIO I DATI AL DB
    sendDataToDB();
    Serial.println(F("Resto in attesa di un evento"));
    Serial.println(F(" ed invio i valori a cayenne"));
    Serial.println(F("========================================================="));
  }


}
// ************************************* FINE loop(void) *************************************


Claudio_FF

#16
Jan 07, 2018, 11:26 am Last Edit: Jan 07, 2018, 11:28 am by Claudio_FF
Come temevo tutto il resto del programma è "incompatibile", a partire dalle letture dei DHT che portano via almeno 3 secondi ad ogni ciclo di loop  :smiley-confuse:

Si può certamente riscrivere la regolazione dell'apertura sotto forma di macchina a stati (attivata da un flag regAp) da richiamare continuamente dalla loop:
Code: [Select]
/*
    'regolaApertura' viene attivata ponendo a true il flag 'regApp'
    imposta a false il flag 'regApp' quando ha finito
    richiama 'StampaApertura' per ottenere il valore di 'Apertura'
    usa il valore 'value_int' ottenuto dalla request HTTP
    usa le variabili locali statiche 'stato' e 't'
*/
void regolaApertura()
{
    static byte stato = 0;
    static uint32_t t;

    switch(stato)
    {
        case 0:
            if(regAp) {
                digitalWrite(RCH2, LOW);
                t = millis();
                stato = 1;
            }
            break;

        case 1:
            if(millis() - t >= 1800) {
                digitalWrite(RCH2, HIGH);
                stato = 2
            }
            break;

        case 2:
            StampaApertura();   // in realta` legge l'apertura
            if(value_int <= Apertura) {
                digitalWrite(RCH2, LOW);
                t = millis();
                stato = 3;
            }
            break;

        case 3:
            if(millis() - t >= 250)
            {
                digitalWrite(RCH2, HIGH);
                stato = 0;
                regAp = false;
            }
    }
}

...ma anche tutto il resto dovrebbe essere riscritto in questo modo. Inoltre i tempi di esecuzione della regolaApertura possono risultare allungati dai tempi delle altre funzioni (letture dei DHT in primis).
* * * * Una domanda ben posta è già mezza risposta * * * *

ziopippo

#17
Jan 07, 2018, 11:35 am Last Edit: Jan 08, 2018, 06:28 pm by ziopippo
Senza offesa ma in tutta sincerità non ci ho capito molto. Ovviamente la colpa è solo mia perché non sono così avanzato nella programmazione.
Sto cercando di approfondire "scopiazzato " parti di codice quà e là e chiedendo consigli quando incontro problemi.
Accetto onvviamente ancora consigli sul perchè il mio tentativo di sostituzione di delay con millis non funziona.  ;)

elrospo

ma se è solo far "lampeggiare 2 led"  il  Blink without Delay   degli esempi nell ide basta e avanza
te ne fai 2  con le necessarie piccole modifiche.

la funzione di millis  va compresa per bene al più presto possibile,

e va presa in considerazione tutte le volte che bisogna fare qualcosa ogni tot tempo,
il che significa che è uno dei pilastri fondamentali della programmazione

ziopippo

ma se è solo far "lampeggiare 2 led"  il  Blink without Delay   degli esempi nell ide basta e avanza
te ne fai 2  con le necessarie piccole modifiche.

Ti ringrzio per la risposta.
Millis l'ho già usato diverse volte e, ultimamente anche in un progetto un pochino più complicato di quello ma me allegato in questo thread.
Se noti infatti nel mio primo post, è quello che ho fatto, però senza avere il risultato ottenuto e proprio per questo ho chiesto consiglio al forum. ;)

ziopippo

Come temevo tutto il resto del programma è "incompatibile", a partire dalle letture dei DHT che portano via almeno 3 secondi ad ogni ciclo di loop  :smiley-confuse:

Si può certamente riscrivere la regolazione dell'apertura sotto forma di macchina a stati (attivata da un flag regAp) da richiamare continuamente dalla loop:

...ma anche tutto il resto dovrebbe essere riscritto in questo modo. Inoltre i tempi di esecuzione della regolaApertura possono risultare allungati dai tempi delle altre funzioni (letture dei DHT in primis).
@Claudio_FF: ho provato ad apportare le tue modifiche, provvedendo opportunamente a creare due funzioni (una per l'apertura ed una per la chiusura) al posto di "regolaApertura()" però non fa ciò che deve.
Il mio codice deve alzare od abbassare la tenda tramite il comando di una slide bar ricevuto da un'app sviluppata per Android.

Se applico il tuo codice mi funziona solo in chiusura (abassare la tenda) ovvero, solo se trascino la slide bar oltre la percentuale di apertura attuale. Se invece voglio alzarla non mi funziona.
Inoltre se ho per esempio la tenda abbassata del 10% e la comando di portarsi al 60% per esempio, non si ferma alla posizione impostata desiderata ma solo quando arriva a fine corsa ovvero al 100%.
E' come se non considerasse la funzione  StampaApertura()
Ti allego il codice che ho dovuto opportunamente modificare per poter scindere le operazioni di apertura e chiusura.

Code: [Select]


void AlzoTenda()
{
    static byte stato = 0;
    static uint32_t t_time;

    switch(stato)
    {
        case 0:
            if(regAp) {
                digitalWrite(RCH2, LOW);
                t_time = millis();
                stato = 1;
            }
            break;

        case 1:
            if(millis() - t_time >= 1800) {
                digitalWrite(RCH2, HIGH);
                stato = 2;
            }
            break;

        case 2:
            StampaApertura();   // legge l'apertura
            if(value_int >= Apertura) {
                digitalWrite(RCH2, LOW);
                t_time = millis();
                stato = 3;
            }
            break;

        case 3:
            if(millis() - t_time >= 250)
            {
                digitalWrite(RCH2, HIGH);
                stato = 0;
                regAp = false;
            }
    }
}

void AbbassoTenda()
{
    static byte stato = 0;
    static uint32_t t_time; //timer tenda

    switch(stato)
    {
        case 0:
            if(regAp) {
                digitalWrite(RCH1, LOW);
                t_time = millis();
                stato = 1;
            }
            break;

        case 1:
            if(millis() - t_time >= 1800) {
                digitalWrite(RCH1, HIGH);
                stato = 2;
            }
            break;

        case 2:
            StampaApertura();   // legge l'apertura
            if(value_int <= Apertura) {
                digitalWrite(RCH1, LOW);
                t_time = millis();
                stato = 3;
            }
            break;

        case 3:
            if(millis() - t_time >= 250)
            {
                digitalWrite(RCH1, HIGH);
                stato = 0;
                regAp = false;
            }
    }
}




il tutto richiamato nella scheda Json in questo modo:
Code: [Select]
if (value_int >= 0 && value_int <= 100)
                {
                  if (value_int > Apertura)
                  {
                    AlzoTenda();
                  }
                  if (value_int < Apertura)
                  {
                    AbbassoTenda();
                  }
                }
                else
                {
                  Serial.println(F("Valore fuori range"));
                  client.print(F("Error, value out of range"));
                }



Se invece utilizzo questo codice usando però il delay fa esattamente ciò che voglio io.

Code: [Select]

              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"));
              }



Dov'è l'errore?

miky_police

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)


BELLISSIMA SPIEGAZIONE!!!! :) semplice, pulita e chiarissima!
Il vero stupido è colui che fa e rifa la stessa cosa aspettandosi risultati diversi. A.E.

elrospo

Ti ringrzio per la risposta.
Millis l'ho già usato diverse volte e, ultimamente anche in un progetto un pochino più complicato di quello ma me allegato in questo thread.
Se noti infatti nel mio primo post, è quello che ho fatto, però senza avere il risultato ottenuto e proprio per questo ho chiesto consiglio al forum. ;)
che poi alla fine millis()  è solo un numero che avanza sempre, parte appena si accende il micro  e continua ad andare avanti,  e a dirla tutta non c'è  niente di esoterico in questo "contatore"

è semplicemente un numero che avanza con la precisione di un discreto orologio,  forse se l avevano chiamato secondis() scommetto che si capiva meglio  :smiley-mr-green:

poi quello che uno ci vuole fare con un numero che avanza sempre dipende da lui,
e a parte qualche rara eccezione (all'accensione del micro)  mi pare che la maggior parte delle volte si usa sempre la formula  lettura precedente lettura attuale

gpb01

#23
Jan 27, 2018, 02:45 pm Last Edit: Jan 27, 2018, 02:45 pm by gpb01
che poi alla fine millis()  è solo un numero che avanza sempre ...
.. esattamente quello che ho scritto un po' di tempo fa QUI :D :D :D

Guglielmo
Search is Your friend ... or I am Your enemy !

Puso

Ciao

scusa se ne approfitto ma sto cercando una nuova storiella da progettare,mi spieghi per favore cosa dovrebbe fare questa tenda livello umano mi spiego meglio:

ES 1-premo il pulsante si alza tutta,lo ripremo si abbassa tutta.

ES 2-premo un pulsante si alza finchè lo tengo premuto.

ES 3-Ect.Ect

Io non ho ancora capito cosa vuoi da questa tenda.

Se mi spieghi il tuo progetto ed a grandi linee quali componenti utilizzi(Motore normale o passopasso,pulsante 1 o 2,o telecomando ect.ect),ne approfitto per provarci anchio,ovviamente usando millis().

Ciao

elrospo

#25
Jan 30, 2018, 09:03 pm Last Edit: Jan 30, 2018, 09:04 pm by elrospo
Chiedo scusa, ma potreste spiegarmi cosa c'entrano gli ultimi post con il mio problema, tra l'altro NON ancora risolto?!!!
PARDON per avere "inquinato" IL TUO POST,     la prossima volta uso i messaggi privati

gpb01

>ziopippo:  ecco, ho ripulito il tuo thread dagli OT.

Guglielmo
Search is Your friend ... or I am Your enemy !

ziopippo

Ciao

scusa se ne approfitto ma sto cercando una nuova storiella da progettare,mi spieghi per favore cosa dovrebbe fare questa tenda livello umano mi spiego meglio:

ES 1-premo il pulsante si alza tutta,lo ripremo si abbassa tutta.

ES 2-premo un pulsante si alza finchè lo tengo premuto.

ES 3-Ect.Ect

Ti spiego a grandi linee ciò che fa la mia tenda.
È una normale tenda da sole dotata sia di telecomando che di un pulsante a parete.
Sia il pulsante a parete che il telecomando hanno entrambi due pulsanti: uno per alzare la tendenza ed uno per abbassarla. Sul telecomando è sufficiente premere per pochi istanti uno dei due pulsanti per azionare la tenda e premere lo stesso per stopparla sulla posizione preferita. Per il pulsante a parete funziona grossomodo allo stesso modo però per l'azionamento è necessario tenere premuto il pulsante per qualche secondo altrimenti il motore non parte e, come per il telecomando basta premere lo stesso pulsante per stoppare il motore sulla posizione preferita.
Per questo motivo devo simulare con il codice il delay(1800).

Puso


ziopippo

Non ne ho la minima idea e non mi interessa più di tanto perché io mi sono collegato direttamente ai pulsanti a muro per comandarlo con Arduino ;)

Go Up