Stazione meteo si blocca dopo qualche giorno

Ciao a tutti!
Come già trattato in alcuni altri topic in questo forum, scrivo per parlare della mia stazione meteo.
Si basa su una scheda ESP32, alla quale sono collegati un sensore BME280 per rilevare temperatura, pressione e umidità, una fotoresistenza per rilevare la luminosità, un anemometro ALMOT 08880905 ed un pluviometro MS-WH-SP-RG.

Riporto di seguito il codice completo.

Purtroppo riscontro che dopo alcuni giorni di corretto funzionamento non si caricano più i dati, la scheda e tutto il sistema rimangono apparentemente bloccati fino a quando non stacco l'alimentazione e la ricollego.

Sapete aiutarmi ad individuare quale potrebbe essere la causa di questi blocchi e come correggere il problema?

Grazie a tutti

#include <Adafruit_Sensor.h>      //per gestire il sensore BME280
#include <Adafruit_BME280.h>      //per gestire il sensore BME280
#include <WiFiClientSecure.h>     //per gestire la connessione alla rete Wifi e al client xyz.it
#include <WiFi.h>                 //per gestire la connessione wifi
#include <Wire.h>                 //per gestire I2C

//dati per la connessione alla rete Wifi
const char* SSID = "xxx";
const char* PASSWORD = "xxx";

//dati per la connessione ad xyz.it
const char* server = "www.xyz.it";
const uint16_t port = 443;

//dati intervallo lettura sensori
unsigned long tempo_start_lettura_sensori;
unsigned long intervallo_lettura_sensori = 60000;           //effettuiamo una lettura dei sensori ogni 60 secondi 

//dati sensore BME280
Adafruit_BME280 sensore_temp_umid_press_BME280;

//dati sensore fotoresistenza
const int pinFotoresistenza = 32;

//dati anemometro
const float pigreco = 3.141593; 
const float raggio = 0.06;                          //raggio dell'anemometro in metri
const int pinAnemometro = 27;     
int stato_reed_anemometro = 0; 
int stato_reed_old_anemometro = 0;                  //variabile per evitare doppie pulsazioni
int num_pulsazioni = 0;                             //numero di pulsazioni (una per giro, c'è un solo magnete)
unsigned long int tempo_massimo_anemometro = 2000;  //tempo (in millisecondi) dopo il quale calcolare la velocità e azzerare il numero di pulsazioni
                                                    //così non si conteggiano brevi folate di vento
unsigned long int inizio_elaborazione_anemometro;
float velocita_vento;                               //velocita vento rilevata dalla lettura
float velocita_vento_max = 0;                       //velocita vento massima misurata nelle 10 rilevazioni
float velocita_vento_min = 1000;                    //velocita vento minima misurata nelle 10 rilevazioni

//dati pluviometro
const float mmBasculata = 0.40;                     //millimetri per ogni basculata
const int pinPluviometro = 13;                      
int basculate = 0;                                  //conteggio numero basculate
float mmPrecipitazioni = 0.0;                       //conteggio millimetri di pioggia
int stato_reed_pluviometro = 0; 
int stato_reed_old_pluviometro = 0;                 //variabile per evitare doppie pulsazioni
                           
//dichiarazione variabili ed array
int num_rilevazioni = 0; 

float temperatura_media;
float umidita_media;
float pressione_media;
float luminosita_media;
float velocita_vento_media;      

float temperatura_tot;
float umidita_tot;
float pressione_tot;
float luminosita_tot;
float velocita_vento_tot;  

float precipitazioni;         //valore da trasmettere al database

void setup() {

  delay(500);

  //Serial.begin(9600);
  
  //connessione Wifi
  WiFi.begin(SSID, PASSWORD);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    //Serial.println("Tentativo di connessione");
  }
  //Serial.println("Connesso");

  //impostazione avvio millis per lettura sensori
  tempo_start_lettura_sensori = millis();
  
  //inizializzazione I2C
  Wire.begin();        

  //inizializzazione sensore BME280      
  sensore_temp_umid_press_BME280.begin(0x76);

  //impostazione pin anemometro
  pinMode(pinAnemometro, INPUT);  

  //impostazione pin pluviometro
  pinMode(pinPluviometro, INPUT);         
}

void loop() {

  //reset di tempo_start_lettura_sensori in caso di overflow di millis che si azzera
  if (millis()-tempo_start_lettura_sensori<0) {
    tempo_start_lettura_sensori = millis();
  }

  //pluviometro
  stato_reed_pluviometro = digitalRead(pinPluviometro);         //legge il contatto reed 

  if (stato_reed_pluviometro != stato_reed_old_pluviometro){    //verifica se è cambiato lo stato
        
    stato_reed_old_pluviometro = stato_reed_pluviometro;        //se SI aggiorna lo stato
  
    if (stato_reed_pluviometro == HIGH){                        //controlla se lo stato è alto (passaggio magnete)
          
      basculate++;                                              //incrementa il numero di basculate
      mmPrecipitazioni = mmPrecipitazioni + mmBasculata;        //sommatoria millimetri di pioggia rilevati
    }
  }

  //verifica se è trascorso l'intervallo per la lettura dei sensori
  if ((millis()-tempo_start_lettura_sensori) > intervallo_lettura_sensori){

    tempo_start_lettura_sensori = millis();
    
    //anemometro    
    inizio_elaborazione_anemometro = millis();      //memorizza l'inizio dell'elaborazione
  
    while (millis() - inizio_elaborazione_anemometro < tempo_massimo_anemometro) { //esegue fino a quando il tempo trascorso è minore al tempo massimo impostato
    
      stato_reed_anemometro = digitalRead(pinAnemometro);    //legge il contatto reed 

      if (stato_reed_anemometro != stato_reed_old_anemometro) {         //verifica se è cambiato lo stato
        stato_reed_old_anemometro = stato_reed_anemometro;              //se SI aggiorna lo stato
  
        if (stato_reed_anemometro == HIGH) {                            //controlla se lo stato è alto (passaggio magnete)
          
          num_pulsazioni = num_pulsazioni + 1;                          //aggiorna il contatore delle pulsazioni    

        }
      }
      delay(10);
      
      //reset di tempo_start_lettura_sensori in caso di overflow di millis che si azzera
      if (millis()-inizio_elaborazione_anemometro<0) {
        inizio_elaborazione_anemometro = millis();
      }
    }
    
    float tempo_trascorso_anemometro_in_secondi = (tempo_massimo_anemometro/1000.0);            //converte in secondi
    //velocita_vento[num_rilevazioni] = (3.6*num_pulsazioni*2*pigreco*raggio)/tempo_trascorso_anemometro_in_secondi;      //velocità in km/h (formula iniziale non utilizzata)

    //2 impulsi/sec -> velocità vento 5km/h quindi: 5km/h : 2 impulsi/sec = velcoità vento : (impulsi/secondo rilevati)
    //coefficiente correzione attrito x2.5
    velocita_vento = ((num_pulsazioni/tempo_trascorso_anemometro_in_secondi) * 5 / 2) * 2.5;
    velocita_vento_tot = velocita_vento_tot + velocita_vento;
  
    //calcolo velocita_vento_max
        
    if (velocita_vento>=velocita_vento_max){
      velocita_vento_max=velocita_vento;
    }

    //calcolo velocita_vento_min
        
    if (velocita_vento<=velocita_vento_min){
      velocita_vento_min=velocita_vento;
    }
  
    num_pulsazioni = 0;       //azzera il contatore delle pulsazioni per una nuova lettura        
    
    //sensore BME280
    temperatura_tot = temperatura_tot + sensore_temp_umid_press_BME280.readTemperature();
    umidita_tot = umidita_tot + sensore_temp_umid_press_BME280.readHumidity();
    pressione_tot = pressione_tot + sensore_temp_umid_press_BME280.readPressure();

    //sensore fotoresistenza
    luminosita_tot = luminosita_tot + analogRead(pinFotoresistenza);

    //operazioni da eseguire alla decima lettura eseguita
    if (num_rilevazioni==9){

      //calcolo dei valori da trasmettere al database
      temperatura_media=temperatura_tot / 10.00;
      umidita_media=umidita_tot / 10.00;
      pressione_media=pressione_tot / 10.00;
      luminosita_media=luminosita_tot / 10.00;
      velocita_vento_media=velocita_vento_tot / 10.00;

      precipitazioni=mmPrecipitazioni;
      
      //trasmissione dati al server

      if (WiFi.status() != WL_CONNECTED) {
      //verifica connessione Wifi

        //connessione Wifi
        WiFi.begin(SSID, PASSWORD);
        while (WiFi.status() != WL_CONNECTED) {
          delay(500);
          //Serial.println("Tentativo di connessione");
        }
        //Serial.println("Connesso");

      }

      if(WiFi.status()== WL_CONNECTED){
      //verifica che il collegamento wifi sia attivo

        String var="temp=" + String(temperatura_media) + "&umid=" + String(umidita_media) + "&pres=" + String(pressione_media) + "&lumi=" + String(luminosita_media) + "&velo=" + String(velocita_vento_media) + "&velo_max=" + String(velocita_vento_max) + "&velo_min=" + String(velocita_vento_min) + "&prec=" + String(precipitazioni);
        String url = "GET /aggiungi.php?" + var + " HTTP/1.1";

        //collegamento all'host xyz.it
        
        WiFiClientSecure client;
        client.setInsecure();               //non si utilizzano certificati di sicurezza

        if (client.connect(server, port)) {
        //verifica che il collegamento col client sia andato a buon fine

          client.println(url);
          client.println("Host: www.xyz.it");
          client.println("Access-Control-Allow-Origin: *");
          client.println("Connection: close");
          client.println();

          delay(500);             //delay fondamentale altrimenti a volte chiude la connessione prima che la pagina sia stata correttamente interrogata
     
          client.stop();
        }

      }
    
    //azzeramento delle variabili
    num_rilevazioni=-1;           //appena fuori dal if ci sarà subito un +1 chela porta al valore iniziale 0

    temperatura_tot=0;
    umidita_tot=0;
    pressione_tot=0;
    luminosita_tot=0;
    velocita_vento_tot=0;

    velocita_vento_max=0;
    velocita_vento_min=1000;

    precipitazioni=0;   //valore da trasmettere al database

    basculate=0;                                      
    mmPrecipitazioni=0;

    }
  
    num_rilevazioni=num_rilevazioni+1;

  } //millis lettura sensori

} //loop


Quella sfilza di String ogni 10 letture?...

Con cosa dovrei sostituirle?

Premesso che non sono esperto come altre persone di questo forum ma le cose che mi saltano all'occhio sono:

Non hai previsto un reset della connessione wifi. Su alcuni miei progetti ho riscontrato che a volte il controllo WiFi.status()== WL_CONNECTED non è affidabilissimo in quanto a volte risulta ed è effettivamente connesso ma per qualche motivo i dati non passano correttamente. Tra l'altro ho notato che questo avviene se l'esecuzione del codice ha dei tempi abbastanza lunghi in cui non viene trasmesso o ricevuto niente. Anche qua ti consiglierei di effettuare periodicamente delle disconnessioni volontarie seguite da riconnessione. Aspetto che anche per me aveva portato a un blocco della scheda che tentava di trasmettere i dati all'infinito senza riuscirci e senza andare avanti.

Quindi siccome più o meno la trasmissione dati avviene ogni 10 minuti farei disconnettere il wifi

WiFi.disconnect(true);

Per riconnetterlo quando trasmetti i dati. In modo che quando ricomincia il loop si ridisconnette e dopo 10 minuti riconnette e così via.

Grazie mille per questo consiglio, vedrò sicuramente di integrarlo in modo da scongiurare problemi con la connessione e la trasmissione dei dati.

In merito invece al problema delle stringhe, quale è la soluzione migliore e più semplice?

Grazie mille

Intanto queste 2 righe dovresti metterle subito prima della
client.println(url);
visto che servono solo li

Poi, non sò se sono quelle String a dare problemi. Nessuno vieta di fare mooolte client.print() dei vari pezzi invece di quell'unico Stringone

// client.println(url) ;
client.print("GET /aggiungi.php?") ;
client.print("temp=");  client.print(temperatura_media);
client.print("&umid="); client.print(umidita_media);
client.print("&pres="); client.print(pressione_media);
... etc...
client.println(" HTTP/1.1") ;
3 Likes

Io farei attenzione a quei cicli while() inseriti nel loop()

Questa condizione ad esempio è sempre falsa perché una sottrazione tra due unsigned non sarà mai minore di 0

  //reset di tempo_start_lettura_sensori in caso di overflow di millis che si azzera
  if (millis() - inizio_elaborazione_anemometro < 0) {
    inizio_elaborazione_anemometro = millis();
  }

Però il sospettato numero 1 a mio avviso è questo all'interno di loop():

  //connessione Wifi
  WiFi.begin(SSID, PASSWORD);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    //Serial.println("Tentativo di connessione");
  }

Che tra le altre cose è anche inutile perchè l'ESP32 gestisce in autonomia la connessione quando il WiFi torna disponibile.

1 Like

Provvederò a sostituire le stringhe con più client.print() come consigliato.

In merito al codice segnalato all'interno del loop, lo avevo appositamente messo lì per essere sempre sicuro che la connessione fosse ok, vedendo che spesso andava tutto in blocco.

Quale è il modo più corretto di gestire la connessione quindi?

Grazie mille

Nessuno, o meglio quello che lo stack WiFi già fa per conto proprio anche se nello sketch Arduino non lo vedi.

Prova a far girare l'esempio WiFiCLientEvents.ino incluso nel core e potrai vedere che se anche nel loop() non c'è nessuna istruzione, l'ESP32 è in grado di riconnettersi senza problemi.

Non credo che il problema sia di tipo disconnessione. Se si trattasse della stessa anomalia che ho riscontrato anche io QUI la connessione WIFI rimaneva a tutti gli effetti connessa. Anche controllando al router la vedevo connessa ma per qualche motivo nessuna informazione riusciva più a passare come se fosse disconnessa.

Per quelle che sono le mie capacità ho risolto disconnettendo forzatamente e riconnettendo cosa che ha risolto il problema ma sicuramente a volte si verifica qualcosa che non fa cadere la connessione ma inibisce il passaggio di dati. Nei miei esperimenti ho visto che si verificava laddove eseguivo quel ciclo di attesa lungo durante l'innaffiatura. Se invece riducevo quel ciclo a 1 minuto o comunque attese brevi non si verificava.

Bisogna vedere che cosa si intende per attesa...
una qualsiasi delay() blocca il processore, quindi è da evitare, al limite accettabili piccole delay() di 10 o 20 per ad esempio input da push button per evitare problema rimbalzi (ma ci sono lib che usano millis)

Nel mio caso avevo usato un ciclo che faceva dei delay di 250 per far respirare la CPU fino ad arrivare a 15 minuti. Funzionava tutto tranne appunto quella anomalia sul WIFI.

Ho seguito sia il consiglio di eliminare le variabili String utilizzando più client.print() sia ho aggiunto un WiFi.disconnect(true) al termine del caricamento dei dati e quindi al successivo invio, ovvero dopo 10 minuti, si riconnetterà.

Tengo monitorata la situazione ed aggiorno il topic.

Intanto grazie mille!

Ho eliminato completamente le stringhe sostituendole con più client.print() ed ho aggiunto un WiFi.disconnect(true) dopo il caricamento dei dati.
La stazione funziona correttamente per mezz'ora, un'ora, massimo 2 ore, e poi si blocca ancora.
Dal modem vedo che esp32 risulta disconnesso subito dopo l'ultimo dato ricevuto e da quel momento non si è più riconnesso.

Qualche altro consiglio per risolvere questa anomalia?

Grazie mille a tutti

Ogni tanto sono capitate anche a me delle board con ESP32 che non riescono a riconnettersi.
Prova ad inserire un timeout di invio dei dati (in caso di invio corretto resetti il timer a millis()) ed in caso di fallimento di 1/2 volte fai riavviare l'ESP32.

uint32_t sendTime;
uint32_t sendTimeout = 2* intervallo_lettura_sensori ;

if (invioDatiOk) {
  ....
  sendTime = millis();
}


if (millis() - sendTime > sendTimeout) {
   ESP.restart();
}

Ciao cotestatnt
Ho intuito lo scopo del tuo consiglio è mi sembra una ottima idea.
Ma nel mio codice dove mi consigli di inserire le due condizioni?
Non vorrei aggiungerle nel posto errato e rendere inutile la miglioria.
Inoltre la condizione (invioDatiOk) come la verifico?

Grazie mille

Ok ho capito come inserire il codice nel mio programma.

Non riesco però a capire come verificare la condizione (invioDatiOk).
Sapete aiutarmi?

Grazie mille

Si tratta della logica che hai già implementato per inviare i tuoi dati al server...
Io ho messo quel nome solo per rendere il concetto, ma non ha nulla a che fare con il tuo codice.

Se l'invio dati viene eseguito con successo resetti il timer, in caso contrario no.

Non funziona nemmeno così..
Potrebbe essere la scheda esp32 difettata o qualche problema hardware?

Fai questa prova tanto non ti costa niente.

Crea un processo parallelo che invece di caricare i dati via URL li carica su thingspeak (è gratis e trovi facilmente delle guide su come farlo).

Perché a questo punto inizio ad avere il dubbio che il problema non sia la scheda ma nel caricamento dati con quelle stringhe.

Quindi: se aggiungendo thingspeak i dati continuano a essere caricati e solo sull'altro sito non vengono caricati il problema non è il wifi ma altrove altrimenti siamo punto e a capo ma almeno hai fatto un test.