ESP8266 anche un ritardo con millis() sembra non funzionare.

Buon giorno,
Dopo lunga ricerca ho appurato che delay() con ESP8266 non funziona fuori dal loop ed è pertanto deprecabile ... a me però non funziona neanche il ritardo generato con l'utilizzo di millis().

La funzione è semplicissima: un led si accende ed un timer lo spegne dopo 80ms. Ebbene ESP8266 non lo fa' con delay(), ma neanche con millis().
E' forse dovuto all'utilizzo di ESPNow?

Non mi piacerebbe sapere che sono il primo ad avere questo problemino, d'altra parte non trovo "conforto" in rete.
Grazie!
Pino_

fidati che delay() e millis su ESP8266 funzionano alla perfezione

magari delay troppo lunghi provocano un reset del watchdog, ma a maggior ragione funzionano

mostra il programma e lo schema, ma sopratutto l'esito di compilazione.........

Inoltre su ESP8266 c’è la libreria inclusa <PolledTimeout.h> che ti consente di usare dei timer sia one shot che ciclici con semplicità (hai una condizione true/false al verificarsi dell’evento) senza usare software esterno o variabili e codice aggiuntivo tutto gestito in “sicurezza” a livello di core.

Ad esempio questo pezzo di codice, accende il led builtin se start = true e lo spegne dopo 1 secondo.

#include <PolledTimeout.h>
using esp8266::polledTimeout::oneShotFastMs; //import the type to the local namespace
oneShotFastMs  delayOff(1000);
.....
  if(start){
      digitalWrite(LED_BUILTIN, LOW);
      delayOff.reset();
      start = false;
  }
  if (delayOff) {
      digitalWrite(LED_BUILTIN, HIGH);
  }
......

Standardoil:
fidati che delay() e millis su ESP8266 funzionano alla perfezione

magari delay troppo lunghi provocano un reset del watchdog, ma a maggior ragione funzionano

mostra il programma e lo schema, ma sopratutto l’esito di compilazione…

Per lo schema l’ESP8266 è in splendida solitudine perchè dovrà solo azionare due solenoidi attraverso due mosfet, per ora due led indicano l’azionamento; i due led non possono essere accesi contemporaneamente, o uno o l’altro per circa 80 ms. Il punto è che anche impostando un ritardo di 4sec i led sempre un lampo di pochi millis danno. Se imposto invece 5 sec si accende fisso un led e si spegne solo quando l’altro si accende. La differenza tra currentMillis e previousMillis è ballerina e non assomiglia al ritardo impostato.
Siccome il caricamento va a buon fine, non so dove andare a pescare l’esito della compilazione; questo sketch è su un pc linux, lo sketch provvisorio è questo:

/*********
 * Pino novembre 2020.
 * Adattamento da tutorial ESP_NOW di:
 
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp-now-one-to-many-esp32-esp8266/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
  
/**************************************************************
Specifico per scambio 1 e macAddress ESP8266: EC:FA:BC:28:5D:B3
*//////////////////////////////////////////////////////////////

#include <ESP8266WiFi.h>
#include <espnow.h>

//String indirizzo = "S1"; //indirizzo univoco
int scambioDpin = 12;
int scambioRpin = 16;
unsigned long previousMillisD = 0;
unsigned long previousMillisR = 0;

const long interval = 500; 
char indi;
char dr;
int DR;

//Structure example to receive data. Must match the sender structure
typedef struct test_struct {
char comClean[5];
} test_struct;
test_struct myData; //Create a struct_message called myData

void setup() {
  
  Serial.begin(9600);
  pinMode(scambioDpin,OUTPUT);
  pinMode(scambioRpin,OUTPUT);  
  digitalWrite(scambioDpin,HIGH);
  delay(1000);
  digitalWrite(scambioDpin,LOW);
 
  //Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);

  //Init ESP-NOW
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
  
  // Once ESPNow is successfully Init, we will register for recv CB to
  // get recv packer info
          esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
          esp_now_register_recv_cb(OnDataRecv);
}

void attua() {  
 if(DR==68) {
          digitalWrite(scambioDpin,HIGH);
    unsigned long currentMillisD = millis();  
      if (currentMillisD - previousMillisD >= interval) {
          previousMillisD = currentMillisD;  
          digitalWrite(scambioDpin,LOW);
          Serial.print(DR);   
  } 
}   
  
  if(DR==82) {
          digitalWrite(scambioRpin,HIGH);
    unsigned long currentMillisR = millis();  
      if (currentMillisR - previousMillisR >= interval) {
          previousMillisR = currentMillisR;  
          digitalWrite(scambioRpin,LOW);   
      }
  }
}
 
//callback function that will be executed when data is received
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
  memcpy(&myData, incomingData, sizeof(myData));
 
  indi=myData.comClean[1];
  dr = myData.comClean[2];              
  DR = dr; 
            Serial.println(indi);//per Debug
            Serial.println(DR);  //per Debug   
    if(indi=='1')  attua();
}
 
void loop() {
}

cotestatnt:
Inoltre su ESP8266 c’è la libreria inclusa <PolledTimeout.h> …

Questo è molto interessante e mi conforta … dopo cena lo provo!
Grazie
Pino_

>Pino_: Quando si quota un post, NON è necessario riportarlo (inutilmente) tutto; bastano poche righe per far capire di cosa si parla ed a cosa ci si riferisce, inoltre, se si risponde al post immediatamente precedente, normalmente NON è necessario alcun "quote" dato che è sottinteso. :slight_smile:

Gli utenti da device "mobile" (piccoli schermi) ringrazieranno per la cortesia :wink:

Guglielmo

P.S.: Ho troncato io il "quote" del tuo post qui sopra :wink:

Pino_:
La differenza tra currentMillis e previousMillis è ballerina e non assomiglia al ritardo impostato

La mia impressione è che il problema non sia millis ma l'ordine in cui vengono fatte le cose. Se non sbaglio nel codice postato il controllo su millis è condizionato dal ricevere qualcosa, quindi non è un processo che funziona in continuazione, se non arrivano più dati (ipotizziamo sempre corretti) allora millis non viene più testato... e la volta successiva l'intervallo potrebbe essere passato da un pezzo per cui on e off istantaneo.

Se lo scopo è creare degli impulsi 80ms alla ricezione di un codice (quindi creare dei timer one shoot o retriggerabili), l'errore è di logica. Quando avviene l'evento desiderato si carica il tempo e accende il LED, nella funzione loop si controlla continuamente il trascorrere del tempo, e al timeout si spegne.

Concorquoto, in pieno

Claudio_FF:
La mia impressione

Caspita, è vero!
Al di là dell'errore logico c'è anche il fatto, che quando si va' in casa d'altri, che siano librerie o protocolli, come quello che tento di usare ad esempio, non si sa mai del tutto dove si va a parare ... almeno per me.

Mi insospettisce la funzione "OnDataRecv"; anche la funzione suggerita da cotestatnt, non funziona dentro quel loop; non funziona neanche a spostarla nel "void loop()", il risultato è sempre lo stesso, quando arriva il carattere "true" si accende il led ma non si spegno più.
Anche un ritardo maccheronico come un lungo ciclo for che mena il can per l'aia, non ottiene nulla, addirittura non si accende neanche il led, che peraltro dovrebbe .

Ho due strade: o provo ad eliminare OnDataRec() e scrivo di mio nel void loop() saltando il ciclo if quando una variabile di appoggio contiene già il carattere in arrivo, oppure cambio protocollo e mi do' all'UDP, ma questo mi dispiacerebbe perchè con l'UDP ci ho già provato e non sono riuscito a far convivere una wifi generata dal sender (un ESP32) con la wifi del router di casa per poter utilizzare OTA e se non provo non so se con ESP_Now posso fare questo, ma mi pare di sì ... ho ancora un colpo! :slight_smile:

Un grazie ad esntrambi ed anche alla pazienza di Guglielmo ... ma se avete qualche suggerimento...

Pino_


Alla fine ho risolto abbastanza semplicemente nell’ambito della funzione “void OnDataRecv()” utilizzando “while” e due chiamate millis()
Grazie e tutti, particolarmente
Claudio_FF che ha visto subito l’errore e mi ha messo sulla buona strada. :slight_smile:
Pino_

 //callback function that will be executed when data is received
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
  memcpy(&myData, incomingData, sizeof(myData));
  dr = myData.comClean[2];              
  DR = dr; 
             if(DR==68) { 
  digitalWrite(scambioDpin,HIGH);
  unsigned long currentMillis = millis();
  previousMillisD = currentMillis;
        while(currentMillis - previousMillisD < interval){
        currentMillis = millis();                
        }       
  digitalWrite(scambioDpin,LOW);     
 }   
            if(DR==82) { 
  digitalWrite(scambioRpin,HIGH);
  unsigned long currentMillis = millis();
  previousMillisR = currentMillis;
        while(currentMillis - previousMillisR < interval) {
        currentMillis = millis(); 
        }
  digitalWrite(scambioRpin,LOW);     
  } 
}
void loop(){}

Pino_:

  unsigned long currentMillis = millis();

previousMillisD = currentMillis;
       while(currentMillis - previousMillisD < interval){
       currentMillis = millis();                
       }

Non hai fatto altro che simulare delay tramite millis… se al posto di tutta quella roba scrivevi direttamente un delay era meglio:

delay(interval);

Perché era meglio? Perché l’ ESP deve portare avanti dei processi in background, cosa che avviene ad ogni fine loop o durante i delay. Creando un ciclo di ritardo con while non solo si è mantenuto il codice bloccante (immagino che l’idea di usare millis era per non avere un codice bloccante), ma si impedisce anche il funzionamento dei processi sottostanti dell’ESP.

Claudio_FF:
... Perché era meglio? Perché l' ESP deve portare avanti dei processi in background ...

Esatto ... o almeno, se non vuoi usare la delay(), dentro quella while() metti una yield(), così l'ESP ha il tempo di fare le sue cose, altrimenti, se quel ciclo dura troppo, aspettati un bel wdt reset ... ::slight_smile:

Guglielmo