Arduino hangs at POST request after 2-3 days

I have an arduino Wemos D1 R1 connected to WiFi. I use it to send weather data every cca 10 seconds and it works great until it doesn't. It hangs at http post request after about 2-3 days and I can't figure it out what is wrong. I tested it if there were memory problems, but there aren't any. I set up watchdog and it also doesn't help even though I thought that watchdog's purpose is to reset the arduino on such events.
When I reset the arduino it starts working again.
Do you have any ideas what could be wrong? Is it that I send too many requests or just that arduino is unreliable for this kind of job?

Thanks and Best regards

or is it that there is a bug in the code... who knows as we can't see it :innocent:

1 Like
//watchdog
extern "C" {
#include "user_interface.h"
}

//wifi and https
#include <ESP8266WiFi.h>
#include "HTTPSRedirect.h"
#include "DebugMacros.h"
#include <ArduinoOTA.h>

//for get time via internet
//#include <NTPClient.h>
//#include <WiFiUdp.h>
#include "user_interface.h"


//for time
#include <Wire.h>
#include <SPI.h>
#include "RTClib.h"

//for temp
#include <DHT.h>

//temp and anemo
#define DHTPIN D7     // pin for termometer
//#define DHTTYPE DHT11   // DHT 22  (AM2302)
DHT dht;
#include <math.h>
#define WindSensorPin D5 // The pin location of the anemometer sensor


//rain gauge
#define RainPin D8      

//wifi settings
const char* ssid = "xxxxx";                //replace with our wifi ssid
const char* password = "yyyyy";         //replace with your wifi password

//for send to google
const char* host = "script.google.com";
const char *GScriptId = "yyyyy"; // Replace with your own google script id
const int httpsPort = 443; //the https port is same
// echo | openssl s_client -connect script.google.com:443 |& openssl x509 -fingerprint -noout
const char* fingerprint = "";
//const uint8_t fingerprint[20] = {};
char url[] = "/macros/s/myid/exec?value=Temperature";  // Write Teperature to Google Spreadsheet at cell A1
// Fetch Google Calendar events for 1 week ahead
char url2[] = "/macros/s/myid/exec?cal";  // Write to Cell A continuosly
//replace with sheet name not with spreadsheet file name taken from google
String payload_base =  "{\"command\": \"appendRow\", \
                    \"sheet_name\": \"Data\", \
                       \"values\": ";
String payload = "";
HTTPSRedirect* client = nullptr;



//for Time                   
RTC_DS3231  rtc;  
const long utcOffsetInSeconds = 3600;
bool wasSet;

//rainGauge
bool bucketPositionA = false;             // one of the two positions of tipping-bucket               
const double bucketAmount = 2.25;        // ml to trip tipping-bucket
double dailyRain = 0.00;                   // rain accumulated for the day
double hourlyRain = 0.0;                  // rain accumulated for one hour
double dailyRain_till_LastHour = 0.0;     // rain accumulated for the day till the last hour          
bool first;
void ICACHE_RAM_ATTR handleInterruptRain();// as we want readings of the (MHz) loops only at the 0th moment 
volatile unsigned long tipCount;
volatile unsigned long ContactBounceTime2;

//anemo
volatile unsigned long Rotations; // cup rotation counter used in interrupt routine
volatile unsigned long ContactBounceTime; // Timer to avoid contact bounce in interrupt routine
float WindSpeed; // speed kmh
float WindMaxSpeedToday; 
void ICACHE_RAM_ATTR handleInterruptWind();
DateTime previousTimeRead;

//temp
//DHT dht(DHTPIN, DHTTYPE); //// Initialize DHT sensor for normal 16mhz Arduino
int chk;
float hum;  //Stores humidity value
float temp; //Stores temperature value
int count;

//for get time over wifi
//WiFiUDP ntpUDP;
//NTPClient timeClient(ntpUDP, "pool.ntp.org", utcOffsetInSeconds);
//String formattedDate;

//-------------------------------------SETUP-------------------------------------------------------------------------
void setup(void) {
  Serial.begin(115200); 
//wifi set
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  ArduinoOTA.setPassword((const char *)"yyyyy");
 ArduinoOTA.onStart([]() {
    Serial.println("Start");
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });
  ArduinoOTA.begin();

  
                           // start the serial port
 //for Time
 // Wire.begin(D4, D3);
  //rtc.begin();
    if (!rtc.begin()) {
      Serial.println("Couldn't find RTC");
     // while (1);
    }
Wire.begin();


     // timeClient.begin();
    //Serial.println("RTC lost power, lets set the time!");
    //rtc.adjust(DateTime(__DATE__, __TIME__));  // Time and date is expanded to date and time on your computer at compiletime
    //rtc.adjust(DateTime(2021, 4, 5, 19, 34, 30 )); // UNCOMMENT TO SET TIME & DATE MANUALLY. (YEAR, MONTH, DAY, 24-HOUR, MINUTE, SECOND) 
     //  timeClient.update();

//temp
    //dht.begin();
    dht.setup(DHTPIN);
    count = 0;
//anemo
    pinMode(WindSensorPin, INPUT);
    attachInterrupt(digitalPinToInterrupt(WindSensorPin), handleInterruptWind, FALLING);
    WindMaxSpeedToday = 0;

//rain gauge
  pinMode(RainPin, INPUT); // set the Rain Pin as input.
  attachInterrupt(digitalPinToInterrupt(RainPin), handleInterruptRain, FALLING);
 tipCount=0;


//for send to GOOGLE
 // Use HTTPSRedirect class to create a new TLS connection
  client = new HTTPSRedirect(httpsPort);
  client->setInsecure();
  //client->setPrintResponseBody(true);
  client->setContentTypeHeader("application/json");
  Serial.print("Connecting to ");
  Serial.println(host);          //try to connect with "script.google.com"
  Serial.println("Status/signal: ");
  Serial.println(WiFi.status());
  Serial.println(WiFi.RSSI());
  Serial.println("--------------");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  // Try to connect for a maximum of 5 times then exit
  bool flag = false;
  for (int i = 0; i < 5; i++) {
    int retval = client->connect(host, httpsPort);
    if (retval == 1) {
      flag = true;
      break;
    }
    else
      Serial.println("Connection failed. Retrying...");
  }

  if (!flag) {
    Serial.print("Could not connect to server: ");
    Serial.println(host);
    Serial.println("Exiting...");
    return;
  }
// Finish setup() function in 1s since it will fire watchdog timer and will reset the chip.
//So avoid too many requests in setup()
 Serial.println("\nStart Sending Sensor Data to Google Spreadsheet");
  // delete HTTPSRedirect object
  delete client;
  client = nullptr;     

previousTimeRead = 1;

//watchdog to 8 seconds
ESP.wdtDisable();
ESP.wdtEnable(WDTO_8S);

  //ENABLE INTERRUPTS FOR ANEMOMETER AND RAIN GAUGE
  sei();
  //---------------


  
  Serial.println("Ready!!!");                    // not necessary too
}

//-----------------------------------------RTC reset. Use this if 165 165 error
#define SDA_LOW()   (GPES = (1 << SDA))
#define SDA_HIGH()  (GPEC = (1 << SDA)) 
#define SCL_LOW()   (GPES = (1 << SCL))
#define SCL_HIGH()  (GPEC = (1 << SCL))
#define SDA_READ()  ((GPI & (1 << SDA)) != 0)
void resetRTC() {

  pinMode(SDA, INPUT_PULLUP);
  pinMode(SCL, INPUT_PULLUP);
  do {
    SDA_HIGH();
    SCL_HIGH();
    if (SDA_READ()) {
      SDA_LOW();
      SDA_HIGH();
    }
    SCL_LOW();
   // ESP.wdtFeed();
  } while (SDA_READ() == 0);

}

// This is the function that the interrupt calls to increment the rotation count
void handleInterruptWind() {
 if ((millis() - ContactBounceTime) > 15 ) { // debounce the switch contact.
  Rotations++;
  ContactBounceTime = millis();
}
}
void handleInterruptRain() {

 if ((millis() - ContactBounceTime2) > 15 ) { // debounce the switch contact.
  dailyRain+= bucketAmount;   
  ContactBounceTime2 = millis();
}
}

bool checkForRTCTimeChange(DateTime now)
{
   if ((now.year() % 4) == 0 && now.day() == 1 && now.month() == 3) {
     rtc.adjust(DateTime(now.year(), 2, 29, now.hour(), now.minute(), now.second()));
     return true;
  }



  if(now.dayOfTheWeek()==0 && now.month() == 3 && now.day() > 24)
  {
     rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour()+1, now.minute(), now.second()));
     wasSet = false;
     return true;
  }
    else if(now.dayOfTheWeek()==0 && now.month() == 10 && now.day() > 24 && wasSet != true)
  {
     rtc.adjust(DateTime(now.year(), now.month(), now.day()-1, 23, now.minute(), now.second()));
     //variable used to prevent forever going back for 1h
     wasSet=true;
     return true;
  }
}

void(* resetFunc) (void) = 0; //declare reset function @ address 0

//----------------------LOOP--------------------------------
//----------------------LOOP--------------------------------
//----------------------LOOP--------------------------------
//----------------------LOOP--------------------------------
void loop(void){
  //reset watchdog back to 8s
ESP.wdtFeed();

Serial.println("---------------------------------------------------");
 //resetRTC();
 DateTime now = rtc.now();

 /*if(previousTimeRead.day() != now.day())
 {

 }*/

 if(previousTimeRead.day() != now.day() && previousTimeRead != 1) {
  if(checkForRTCTimeChange(now))
 {
    now = rtc.now();
 }
     resetFunc();  //call reset
 }

  Serial.print("Status/signal: ");
  Serial.print(WiFi.status());
    Serial.print("/");
  int  rssi = WiFi.RSSI();
  Serial.println(rssi);

//check if wifi was in sleep mode(21:00 - 6:00)
if((previousTimeRead.hour()<6 || previousTimeRead.hour()>21) && previousTimeRead.hour() < 24)
{
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
 // WiFi.reconnect ();
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
}
 
 

//temp and anemo-----------------------------------------------------------------------------------------------
Rotations = 0; // Set Rotations count to 0 ready for calculations

//sei(); // Enables interrupts
//delay (2000); 
//CAUSES HTTPCLIENT TO STOP WORKING!!!!!!
//cli(); // Disable interrupts
//delay (2000);

// Wait 3 seconds to average
delay (3000);
WindSpeed = Rotations * 0.75 *1.609344;//to kmh; 1 mph = 1.609344
if(WindMaxSpeedToday < WindSpeed || (previousTimeRead.day() != now.day()&& previousTimeRead != 1)) WindMaxSpeedToday = WindSpeed;
Serial.print(" WindSpeed: ");
Serial.print(WindSpeed);
Serial.println(" kmh");

  //reset watchdog back to 8s
ESP.wdtFeed();

if(count == 0)
{
cli();
  //Read data and store it to variables hum and temp
  delay(dht.getMinimumSamplingPeriod());
 //Serial.println(dht.getMinimumSamplingPeriod());
  hum = dht.getHumidity();
  temp= dht.getTemperature();
sei();
count = 8;
}
count = count -1;
  //Print temp and humidity values to serial monitor
  Serial.print("Humidity: ");
  Serial.print(hum);
  Serial.print(" %, Temp: ");
  Serial.print(temp);
  Serial.println(" Celsius");


//----------RAIN GAUGE
  if(now.minute() != 0) first = true;                     // after the first minute is over, be ready for next read
bool saveData = false;
  if(now.minute() == 0 && first == true){
 saveData = true;
    hourlyRain = dailyRain - dailyRain_till_LastHour;      // calculate the last hour's rain
    dailyRain_till_LastHour = dailyRain;                   // update the rain till last hour for next calculation
    
    // facny display for humans to comprehend
    Serial.print(now.hour());
    Serial.print(":");
    Serial.print(now.minute());
    Serial.print(":  Total Rain for the day = ");
    Serial.print(dailyRain,8);                            // the '8' ensures the required accuracy
    Serial.println(" ml");
    Serial.println();
    Serial.print("     :  Rain in last hour = ");
    Serial.print(hourlyRain,8);
    Serial.println(" ml");
    Serial.println();
    
    first = 0;                                        // execute calculations only once per hour
  }
  
  if(now.hour()== 0) {
    dailyRain = 0.0;                                      // clear daily-rain at midnight
    dailyRain_till_LastHour = 0.0;                        // we do not want negative rain at 01:00
  } 


  //reset watchdog back to 8s
  ESP.wdtFeed();

//for send to GOOGLE spreadsheets---------------------------------
  static int error_count = 0;

  const unsigned int MAX_CONNECT = 20;
  static bool flag = false;
  //String sheetDateTime = String(now.hour(),1)+":"+String(now.minute(),1)+":"+String(now.second(),1)+" "+String(now.day(),1)+". "+String(now.month(),1)+". "+String(now.year(),1);
  char buf1[20];
   sprintf(buf1, "%02d:%02d:%02d %02d.%02d.%02d",  now.hour(), now.minute(), now.second(), now.day(), now.month(), now.year());
 uint32_t free = system_get_free_heap_size();
 Serial.println(free);
if(saveData == true) 
  payload = payload_base + "\"" + buf1+ ";" +temp + ";" + hum + ";" + WindSpeed+  ";" +WindMaxSpeedToday +";" + hourlyRain+";" +dailyRain+";true;"+free+";"+rssi+"\"}";
else
  payload = payload_base + "\"" + buf1+ ";" +temp + ";" + hum + ";" + WindSpeed+  ";" +WindMaxSpeedToday +";" + hourlyRain+";" +dailyRain+";false;"+free+";"+rssi+"\"}";
 // saveDataString = "false";
    Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  
  Serial.println(payload);
  if (!flag) {
    client = new HTTPSRedirect(httpsPort);
    client->setInsecure();
    flag = true;
    client->setPrintResponseBody(true);
    client->setContentTypeHeader("application/json");
  }

//bool wasSent= false;
  if (client != nullptr) {
    if (!client->connected()) {
      client->connect(host, httpsPort);
      //client->POST(url2, host, payload, false);
      //Serial.print("Sent : ");  Serial.println("Temp and Humid");
    }
  }
  else {
    DPRINTLN("Error creating client object!");
    error_count = 5;
  }

  Serial.println("client->POST");
  bool x = client->POST(url2, host, payload);
  Serial.println("client->POST - done");
  if (x) {
    Serial.println("DATA SENT");
    error_count = 0;
  }
  else {
    ++error_count;
    DPRINT("Error-count while connecting: ");
    DPRINTLN(error_count);
  }
  if (error_count > 3) {
    Serial.println("Error count more than 3");
    Serial.printf("Final free heap: %u\n", ESP.getFreeHeap());
    Serial.printf("Final stack: %u\n", ESP.getFreeContStack());
    Serial.flush();
  }
 previousTimeRead= now;
 ArduinoOTA.handle();

client->stop();

if(now.hour()<6)
{
  Serial.println("go to sleep for 63 seconds");
//reset watchdog back to 8s
ESP.wdtFeed();
WiFi.disconnect();
WiFi.mode(WIFI_OFF);
delay(1);
WiFi.forceSleepBegin();
ESP.wdtFeed();
delay(7000);
ESP.wdtFeed();
delay(7000);
ESP.wdtFeed();
delay(7000);
ESP.wdtFeed();
delay(7000);
ESP.wdtFeed();
delay(7000);
ESP.wdtFeed();
delay(7000);
ESP.wdtFeed();
delay(7000);
ESP.wdtFeed();
delay(7000);
ESP.wdtFeed();
delay(7000);
}
else if(now.hour()>21 && now.hour() < 24)
{
 Serial.println("go to sleep for 21 seconds");
//reset watchdog back to 8s
ESP.wdtFeed();
WiFi.disconnect();
WiFi.mode(WIFI_OFF);
delay(1);
WiFi.forceSleepBegin();
ESP.wdtFeed();
delay(7000);
ESP.wdtFeed();
delay(7000);
ESP.wdtFeed();
delay(7000);
}
}  // end of loop

your payload String keeps getting built through lots of concatenation operator (+) in the loop.

  if (saveData == true)
    payload = payload_base + "\"" + buf1 + ";" + temp + ";" + hum + ";" + WindSpeed +  ";" + WindMaxSpeedToday + ";" + hourlyRain + ";" + dailyRain + ";true;" + free + ";" + rssi + "\"}";
  else
    payload = payload_base + "\"" + buf1 + ";" + temp + ";" + hum + ";" + WindSpeed +  ";" + WindMaxSpeedToday + ";" + hourlyRain + ";" + dailyRain + ";false;" + free + ";" + rssi

This is a good recipe to fragment the heap over time.

As you don't test for the success of those operations you don't know what's going on within the loop function.

side note on WDT

Calling delay() feeds the watchdogs so this is probably over engineered

    Serial.println("go to sleep for 63 seconds");
    //reset watchdog back to 8s/reset watchdog back to 8s
    ESP.wdtFeed();
    WiFi.disconnect();
    WiFi.mode(WIFI_OFF);
    delay(1);
    WiFi.forceSleepBegin();
    ESP.wdtFeed();
    delay(7000);
    ESP.wdtFeed();
    delay(7000);
    ESP.wdtFeed();
    delay(7000);
    ESP.wdtFeed();
    delay(7000);
    ESP.wdtFeed();
    delay(7000);
    ESP.wdtFeed();
    delay(7000);
    ESP.wdtFeed();
    delay(7000);
    ESP.wdtFeed();
    delay(7000);
    ESP.wdtFeed();
    delay(7000);

I set up watchdog and it also doesn't help even though I thought that watchdog's purpose is to reset the arduino on such events.

There was a known problem that only occurs on the first watchdog reset after a firmware download by serial link. I don't know if this has been addressed. If you were to manually reset the ESP8266 once (or turn it off) then from then on the hardware watchdog would resets the ESP without fail.

did you capture the crash log?

1 Like

Taming Arduino Strings has more details on String fragmentation and how to avoid it.

However the ESP8266 uses Strings all over the place for its WiFi internals so it may be simpler for you to just programmatically reboot once a day.
see ESP32/ESP8266 Adding Periodic Automatic Reboots

1 Like

Thank you for the reply.
Regarding delay(), then ESP.wdtFeed() here is redundant?
Sadly I don't have the crash log. But should there even be one if it freezes and doesn't even return any kind of error?

My current ESP32 project freezes after a few thousand sends and I am not using any Strings in my code.
I suspect a memory leak in the underlying libraries.
So I do a restart once a day now at 3:00am

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.