ESP8266 si blocca e non invia più dati a ThingSpeak

Salve a tutti,
come primo progetto “serio” ho pensato di realizzare una piccola stazione meteo che inviasse dati ai server ThingSpeak. Utilizzo una ESP8266 v2, un DHT22 e un BMP280.

Nell’impostare e far funzionare i sensori e la comunicazione con ThingSpeak non ho problemi. I problemi nascono quando lascio lavorare la stazione per diverse ore. Casualmente la stazione si pianta e non invia più informazioni, se premo il pulsante di reset riparte subito. Ho provato anche a lasciare fisicamente il modulo a meno di 1 metro dal mio router ma ottengo lo stesso problema.

Le possibili soluzioni a cui ho pensato sono:

  1. Inserire nel codice un reset della board ogni tot ore. Questo mi permetterebbe di sbloccarla se qualcosa dovesse andare storto. Non ho strettamente bisogno di avere dati con intervalli di 1 minuto se dovessi anche avere un buco temporale di 1 ora mi andrebbe lo stesso bene.

  2. Usare la funzione Deep Sleep. Non l’ho mai provata ma da quanto ho capito documentandomi online ad ogni ciclo è come se la board si resettasse. Questa soluzione mi permetterebbe anche di considerare l’evenienza di alimentare l’ESP con delle batterie.

Di seguito riporto il mio codice semplificato con solo il DHT22:

#include <DHT.h>
#include <ESP8266WiFi.h>
 
String apiKey = "XXXXXXXXXXXXXX";
const char* ssid = "XXXXXXXXXXXXX";
const char* password = "XXXXXXXXXXXXXXXXX";
 
const char* server = "api.thingspeak.com";
#define DHTPIN 4 // Connected to D2 pin of ESP8266
#define DHTTYPE DHT22
 
DHT dht(DHTPIN, DHT22);
WiFiClient client;
 
void setup() {
Serial.begin(115200);
delay(10);
dht.begin();
 
WiFi.begin(ssid, password);
 
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
 
WiFi.begin(ssid, password);
 
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
 
}
 
void loop() {
 
float h = dht.readHumidity();
float t = dht.readTemperature();
 
if (isnan(h) || isnan(t)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
 
float z = dht.computeHeatIndex(t,h, false);
 
double gamma = log(h/100) + ((17.62*t) / (243.5+t)); //compute dewpoint with Magnus Approximation
double dp = 243.5*gamma / (17.62-gamma);
 
if (client.connect(server,80)) {
String postStr = apiKey;
postStr +="&field1=";
postStr += String(t);
postStr +="&field2=";
postStr += String(h);
postStr +="&field3=";
postStr += String(z);
postStr +="&field4=";
postStr += String(dp);
postStr += "\r\n\r\n";
 
client.print("POST /update HTTP/1.1\n");
client.print("Host: api.thingspeak.com\n");
client.print("Connection: close\n");
client.print("X-THINGSPEAKAPIKEY: "+apiKey+"\n");
client.print("Content-Type: application/x-www-form-urlencoded\n");
client.print("Content-Length: ");
client.print(postStr.length());
client.print("\n\n");
client.print(postStr);
 
Serial.print("Temperature: ");
Serial.print(t);
Serial.print("°C Humidity: ");
Serial.print(h);
Serial.print("% Heat Index: ");
Serial.print(z);
Serial.print("°C Dew Point: ");
Serial.print(dp);
Serial.print("°C");
Serial.println("send to thingspeak");
}
 
client.stop();
 
Serial.println("Waiting…");
// thingspeak needs minimum 15 sec delay between updates
delay(20000);
}

Lascio in allegato la foto dei collegamenti, la board è alimentata da un banale alimentatore per cellulari (5V, 1A).

Grazie a tutti in anticipo,
Alessio

non ho letto e studiato tutto il tuo programma ma credo proprio che
il tuo problema sia quasi sicuramente questo

String postStr = apiKey;

se cerchi altri post che parlano di blocchi, necessità di resettare o comportamenti strani troverai che il tutto deriva quasi sempre dall’utilizzo di stringhe che dopo un certo numero di cicli di lavoro saturano la memoria (poca) libera e bloccano arduino
cerca e studia il modo di usare gli array di char al posto di String

ho parlato di Arduino e non di esp8266 perchè non ci avevo fatto caso prima di aprire il tuo allegato....
e visto che qui siamo su Arduino.cc di quello dovremmo parlare :wink:
però presumo che il tuo problema sia equivalente

Grazie cercherò di approfondire meglio questo aspetto. E' difficile implementare come avevo pensato un reset ogni ora per esempio? Potrebbe risolvere sia il problema che mi hai fatto notare tu sia il problema di disconnessioni casuali del modulo.

si può fare (il reset periodico) ma è un metodo barbaro per sopperire ad un "errore" di programmazione

Ho riscritto il codice usando la libreria ThingSpeak.h ma il problema rimane. Ho pensato di usare quindi la funzione WatchDog il problema è che non so come implementarla visto che il mio loop dura più di 8 secondi.

Nuovo Codice

#include <DHT.h>
#include <ESP8266WiFi.h>
#include <ThingSpeak.h>
 
 
//ThingSpeak
unsigned long ChannelNumber = 539396;
const char* WriteAPIKey = "";
const char* server = "api.thingspeak.com";
 
// Wifi
const char* ssid = "NETGEAR";
const char* password = "";
 
// DHT22
#define DHTPIN 4 // connected to pin D2
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHT22);
 
WiFiClient client;
 
void setup() {
Serial.begin(115200);
delay(10);
dht.begin();
 
WiFi.begin(ssid, password);
ThingSpeak.begin(client);
}
 
void loop() {
// Get values
float h = dht.readHumidity();
float t = dht.readTemperature();
float z = dht.computeHeatIndex(t,h, false);
 
//compute dewpoint with Magnus Approximation
double gamma = log(h/100) + ((17.62*t) / (243.5+t));
double dp = 243.5*gamma / (17.62-gamma);
 
 // Set ThingSpeak's Fields
ThingSpeak.setField(1, t);
ThingSpeak.setField(2, h);
ThingSpeak.setField(3, z);
ThingSpeak.setField(4, (float)dp);
 
// Write the fields all at once
ThingSpeak.writeFields(ChannelNumber, WriteAPIKey);
 
//15 sec delay between updates
delay(20000);
}

le password e l'apiKey... io le cancellerei, prima di postare il codice :wink:

quella libreria non la conosco… ho dato ora una veloce occhiata e ho visto che usa al suo interno delle stringhe…
mi sa che se non è zuppa è pan bagnato
però vediamo se qualcun altro sa darti una risposta migliore…

int writeFields(unsigned long channelNumber, const char * writeAPIKey)
	{
		String postMessage = String("");
		bool fFirstItem = true;
		for(size_t iField = 0; iField < 8; iField++)
		{
			if(this->nextWriteField[iField].length() > 0)
			{
				if(!fFirstItem)
				{
					postMessage = postMessage + String("&");
				}
				postMessage = postMessage + String("field") + String(iField + 1) + String("=") + this->nextWriteField[iField];
				fFirstItem = false;
				this->nextWriteField[iField] = "";
			}
}

Ho riaperto il topic proprio perché mi ero reso conto di aver lasciato l'api key :-[
Mi sono anche appena accorto che la scheda ha smesso di inviare dati dopo circa 9 ore. Ora provo ad inserire il DeepSleep ad ogni ciclo inviando i dati ogni 5 minuti. Ho tenuto fino ad adesso 15 secondi ma non ne ho realmente bisogno era solo un test. Non sono riuscito a capire però se la funzione deep sleep possa liberare la memoria saturata dall'uso delle stringhe. Tentar non nuoce...

Il Deep Sleep dell'ESP8266 è diverso da quello di Arduino e di altre MCU.
Al risveglio l'ESP8266 parte daccapo dal Setup(), in pratica è come se premessi il reset, quindi potrebbe esserti d'aiuto.
Se vuoi provare devi spostare tutto il codice del Loop nel Setup e alla fine al posto di Sleep metti

ESP.deepSleep(20 * 1000000);      //20 is in seconds

Inoltre ci vuole un collegamento hardware tra 2 pin, il pin RST e il GPIO16 che sulle nodeMCU si chiama D0.

Però io ho ben 2 ESP8266 (moduli ESP-01) che da mesi ininterrottamente piazzano dati su Thingspeak.
Non ho qui con me il codice però. Gli unici problemi che ho avuto erano dovuti all'alimentazione.

Grazie per la spiegazione, ora proverò. So che si dovrebbe risolvere il problema dal codice ma ad oggi non ho altre soluzioni. Se riesci a riportarmi il tuo codice sarebbe fantastico.
Non penso sia un problema di alimentazione. Uso un buon cavo USB praticamente nuovo e un caricatore da parete per smartphone 5V - 1A.

Il mio codice praticamente è identico al tuo. Le differenze sono solo cosmetiche. Anche io uso String. E va avanti da una decina di mesi ininterrottamente a parte le interruzioni di corrente. Sopravvive anche alla caduta della connessione internet, quando la connessione riprende, riprende anche la raccolta dati.
A differenza del tuo io ho messo e lasciato parecchie Serial.print, puoi fare lo stesso e vedere sulla seriale se succede qualcosa quando si blocca.

/***************************************************************************
  Data sended to Thingspeak for IDE 1.6.8  with Matworks ThingSpeak library
 ***************************************************************************/

#include <Wire.h>
#include <SPI.h>
#include "Adafruit_BME280.h"

#include <ESP8266WiFi.h>
char ssid[] = "your network SSID (name)"; // 
char pass[] = "your network password";
int status = WL_IDLE_STATUS;
WiFiClient  client;
#include "ThingSpeak.h"

#define BME_SCK 13
#define BME_MISO 12
#define BME_MOSI 11
#define BME_CS 10

#define SEALEVELPRESSURE_HPA (1013.25)

#define LED     D0        // Led in NodeMCU at pin GPIO16 (D0)

unsigned long myChannelNumber = 12345
const char * myWriteAPIKey = "myWriteAPIKey";
const int updateThingSpeakInterval = 30 * 1000;      // Time interval in milliseconds to update ThingSpeak (number of seconds * 1000 = interval)

Adafruit_BME280 bme; // I2C
float fpressure;
char buffer[10]; // used for temporary things
String temperature_s, humidity_s, pressure_s;


void setup() {
  Wire.begin(D2, D1); // sda, scl
  Serial.begin(115200);
  Serial.println(F("BME280 to thingspeak"));
  
  pinMode(LED, OUTPUT);
  digitalWrite(LED, HIGH);
  
  if (!bme.begin()) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }


  WiFi.begin(ssid, pass);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  for (int i = 0; i <= 6; i++) {
    blink();
    delay(40);
  }
  ThingSpeak.begin(client);
}

void loop() {
  Serial.print("Temperature = ");
  Serial.print(bme.readTemperature());
  //temperature_s = dtostrf(bme.readTemperature(), 2, 2, buffer);
  Serial.println(" *C");

  Serial.print("Pressure = ");

  Serial.print(bme.readPressure() / 100.0F);
  fpressure = bme.readPressure() / 100.0F;
  pressure_s = dtostrf(fpressure, 2, 2, buffer);
  Serial.println(" hPa");

  Serial.print("Approx. Altitude = ");
  Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
  Serial.println(" m");

  Serial.print("Humidity = ");
  Serial.print(bme.readHumidity());
  //humidity_s = dtostrf(bme.readHumidity(), 2, 2, buffer);
  Serial.println(" %");

  Serial.print("Sending data to ThingSpeak");
  Serial.println();

  ThingSpeak.setField(1, bme.readTemperature());
  ThingSpeak.setField(2, bme.readHumidity());
  ThingSpeak.setField(3, fpressure);


  // Write the fields that you've set all at once.
  ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);

  blink();

  delay(updateThingSpeakInterval);
}

void blink() {
  digitalWrite(LED, LOW);
  delay(50);
  digitalWrite(LED, HIGH);
}

Grazie effettivamente sono molto simili. Proverò a cambiare alimentatore a questo punto...
Ho anche provato ad inserire il deep sleep come mi hai detto ma quando carico dall'IDE di Arduino mi da errore e non mi fa caricare il programma. Ho collegato il D0 con il RST come da guida, non so da cosa dipenda

La versione deep sleep dovrebbe essere così

#include <DHT.h>
#include <ESP8266WiFi.h>
#include <ThingSpeak.h>
 
 
//ThingSpeak
unsigned long ChannelNumber = 539396;
const char* WriteAPIKey = "";
const char* server = "api.thingspeak.com";
 
// Wifi
const char* ssid = "NETGEAR";
const char* password = "";
 
// DHT22
#define DHTPIN 4 // connected to pin D2
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHT22);
 
WiFiClient client;
 
void setup() {
Serial.begin(115200);
delay(10);
dht.begin();
 
WiFi.begin(ssid, password);
ThingSpeak.begin(client);

// Get values
float h = dht.readHumidity();
float t = dht.readTemperature();
float z = dht.computeHeatIndex(t,h, false);
 
//compute dewpoint with Magnus Approximation
double gamma = log(h/100) + ((17.62*t) / (243.5+t));
double dp = 243.5*gamma / (17.62-gamma);
 
 // Set ThingSpeak's Fields
ThingSpeak.setField(1, t);
ThingSpeak.setField(2, h);
ThingSpeak.setField(3, z);
ThingSpeak.setField(4, (float)dp);
 
// Write the fields all at once
ThingSpeak.writeFields(ChannelNumber, WriteAPIKey);

ESP.deepSleep(20 * 1000000);      //20 is in seconds

}
 
void loop() {

}

ma non so se funziona, il mio dubbio riguarda
ThingSpeak.writeFields(ChannelNumber, WriteAPIKey)
se è bloccante, cioè lo sketch non va avanti finché la connessione con Thingspeak non finisce, allora funzionerà ma se non lo è finisce che va in Deep sleep nel mentre che è connesso a Thingspeak.

Per me il problema alimentatore era dovuto al fatto che qualcuno o qualcosa urtava l’alimentatore.

Come ti ho detto non riesco a copiare il programma, lo compilo e poi mi da questo errore

warning: espcomm_sync failed
error: espcomm_open failed
error: espcomm_upload_mem failed
error: espcomm_upload_mem failed

Ho lasciato tutto come nello schema al primo post e ho collegato D0 con RST

Significa che non riesce a caricare il firmware perché il modulo non è in modalità programmazione. Se hai un modulo nodeMCU o il Wemos puoi provare a premere il reset non appena appare la scritta che sta caricando.
A volte capita anche a me ed è una seccatura.
Eventualmente leggi la documentazione per il caricamento via WiFi che però implica una modifica allo sketch e quindi la prima volta va fatto via cavo.

Dopo varie prove ho risolto collegando RST e D0 soltanto mentre caricava il programma e in questo modo funziona. Nei primi test probabilmente accadeva quello che mi avevi scritto cioè che entrava il sleep mode prima che potesse inviare i dati a ThingSpeak. Per risolvere ho inserito un delay di 15s e per ora sembra funzionare. Probabilmente non è una soluzione elegante ma ad oggi non ho altre idee. Riporto qui il codice magari può essere utile a qualcuno.

(Non ho usato la libreria ThingSpeak.h ritornando al codice originale così da avere più chiaro quello che sta accadendo). Lascerò il dispositivo acceso per qualche giorno per vedere se non si interrompe più come prima

#include <DHT.h>
#include <ESP8266WiFi.h>
 
// replace with your channel’s thingspeak API key,
String apiKey = "";
const char* ssid = "NETGEAR";
const char* password = "";
 
const char* server = "api.thingspeak.com";
#define DHTPIN 4 // SO CONNECT THE DHT11/22 SENSOR TO PIN D4 OF THE NODEMCU
#define DHTTYPE DHT22
 
DHT dht(DHTPIN, DHT22); //CHANGE DHT11 TO DHT22 IF YOU ARE USING DHT22
WiFiClient client;
 
void setup() {
Serial.begin(115200);
delay(10);
dht.begin();
 
WiFi.begin(ssid, password);
 
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
 
WiFi.begin(ssid, password);
 
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
 
float h = dht.readHumidity();
float t = dht.readTemperature();
 
if (isnan(h) || isnan(t)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
 
float z = dht.computeHeatIndex(t,h, false);
 
double gamma = log(h/100) + ((17.62*t) / (243.5+t)); //compute dewpoint with Magnus Approximation
double dp = 243.5*gamma / (17.62-gamma);
 
if (client.connect(server,80)) { // "184.106.153.149" or api.thingspeak.com
String postStr = apiKey;
postStr +="&field1=";
postStr += String(t);
postStr +="&field2=";
postStr += String(h);
postStr +="&field3=";
postStr += String(z);
postStr +="&field4=";
postStr += String(dp);
postStr += "\r\n\r\n";
 
client.print("POST /update HTTP/1.1\n");
client.print("Host: api.thingspeak.com\n");
client.print("Connection: close\n");
client.print("X-THINGSPEAKAPIKEY: "+apiKey+"\n");
client.print("Content-Type: application/x-www-form-urlencoded\n");
client.print("Content-Length: ");
client.print(postStr.length());
client.print("\n\n");
client.print(postStr);
 
Serial.print("Temperature: ");
Serial.print(t);
Serial.print("°C Humidity: ");
Serial.print(h);
Serial.print("% Heat Index: ");
Serial.print(z);
Serial.print("°C Dew Point: ");
Serial.print(dp);
Serial.print("°C");
Serial.println("send to thingspeak");
}
 
client.stop();
 
delay(15*1000); //bug fix
 
 
Serial.println("Waiting…");
// sleep for 5 min
ESP.deepSleep(300000000);
 
}
 
void loop() {
 
}

Diavolo, non ci avevo pensato: il collegamento RST-D0 probabilmente influenza il reset da IDE.
Grazie!