Sketch Crashing (memory/heap fragmentation/something) (SOLVED!)

I would first like to thank everyone who has ever responded to a post like this. Reading the old posts has help me work out a lot of the issues before asking. :wink:

I have been working on a Room sensor for HA using a NodeMCU, DHT11, BMP180, LDR for light sensing, and PIR for motion sensing. I know it has been done before but this is a learning exercise more than anything.

On first run it would work for about 15min then everything would stop working. After some reading I went back over the code and changed all the Strings to proper var types and sizes. That got it to run for about an hour before crashing. After more reading and some testing I optimized some of the code and got it to work for a couple of hours before crashing. Which is a great improvement!

I then cut out 1/2 of my sensor data and corresponding variables. It was all stuff that I really dont need like altitude, Barometric pressure at sea level, the second temperature reading, etc. That got it to run for about 2.5 hours and it seems that when it crashes it is rebooting the unit. (based on ping responses from the unit)

Personally Im leaning towards the issue being in the handleRoot function for the webpage as I put the page into a variable that is a char HTML_CODE[2000] which is very large for a single variable but I have not come up with a better way to handle it.

I have read enough of the old posts to know that the first statement will be "We need to see all your code to help!" so here it is

Any help would be greatly appreciated!

Thank you

so here it is

There is NOT here.

You completely failed to mention all the other stuff your code is (apparently, based on the included files) doing.

 long state = digitalRead(PIR_PIN);

Are you sure that you don't need a long long to hold HIGH or LOW?

                    "<html><head> <title>Room Sensor</title></head>\
                    <body><h1 style=\"text-align: center;\">Room Sensor</h1><p>&nbsp;</p>\
                    <table align=\"center\" border=\"1\" cellpadding=\"1\" cellspacing=\"1\" style=\"width:500px;\">\
                    <caption><B>Readings</b></caption><tbody>\
                    <tr><td>Motion</td><td>%s</td></tr>\
                    <tr><td>Light reading</td><td>%1.2f</td></tr>\
                    <tr><td>Temperature</td><td>%2.2f C</td></tr>\
                    <tr><td>Temperature in F</td><td>%2.2f F</td></tr>\
                    <tr><td>Humidity</td><td>%2.2f %</td></tr>\
                    <tr><td>Heat Index</td><td>%2.2f %</td></tr>\
                    <tr><td>Barometric Pressure</td><td>%4.2f Pa</td></tr>\
                    <tr><td>MQTT Status</td><td>%s</td></tr>\
                    <tr><td>Last MQTT Message</td><td>%s</td></tr>\
                    </tbody></table>\
                    <table align=\"center\" border=\"1\" cellpadding=\"1\" cellspacing=\"1\" style=\"width:500px;\">\
                    <caption><b>Settings</b></caption><tbody>\
                    <tr><td>Sensor Name</td><td>%s</td></tr>\
                    <tr><td>MQTT Server</td><td>%s</td></tr>\
                    <tr><td>MQTT Port</td><td>%s</td></tr>\
                    <tr><td>Update Server</td><td>%s</td></tr>\
                    </tbody></table>\
                    <p><center><h3><a href=\"/reset\">Reset Config</a></p></h3><center></body></html>",
                    MotionSensor ? "true" : "false", LightReading, Temperature, TemperatureF, humidity, HeatIndex, Pressure, MQTT_Status  ? "Connected" : "Connection Failed", MQTT_Message ? "Success" : "Failed", Sensor_Name, mqtt_server, mqtt_port, update_server
);

All those spaces ARE part of the array. 20 useless characters per line * 23 lines = a lot of waste.

I would dump either the server part of the code OR the MQTT publishing, to see which is causing your code to "crash". The ESP is generally pretty good at telling you what the problem is - but perhaps not in a way that makes sense to you. You could share the data displayed when the ESP "crashes". It might mean more to someone else.

You could save your self some 400 bytes of memory by declaring "char HTML_BUFFER[2000]" globally so that it can be used in "handleRoot()", "handleReset()", "mqtt_post()" and so on. In "mqtt_post()" you are using JSON with "StaticJsonBuffer<BUFFER_SIZE> jsonBuffer" and "JsonObject& root = jsonBuffer.createObject();" - this may be a problem and you should tru to manually create the JSON-object in the global "HTML_BUFFER". And last but not least, the "ESP8266WebServer" is using the "String" object. If possible, try a webserver that does not use the String object.

EDIT: Try to add this to your sketch, and see if the available memory drops:

//Other code

unsigned long lastMemReport = 0;

int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

void setup()
{
  Serial.begin(9600);
  //Other code
}

void loop()
{
  //Other code
  if (millis() - lastMemReport >= 2000)
  {
    lastMemReport = millis();
    Serial.print("available memory=");
    Serial.println(freeRam(), DEC);
  }
}

PaulS:

 long state = digitalRead(PIR_PIN);

Are you sure that you don't need a long long to hold HIGH or LOW?

                    "<html><head> <title>Room Sensor</title></head>\

<h1 style="text-align: center;">Room Sensor

 


                   <table align="center" border="1" cellpadding="1" cellspacing="1" style="width:500px;">
                   Readings
                   Motion%s
                   Light reading%1.2f
                   Temperature%2.2f C
                   Temperature in F%2.2f F
                   Humidity%2.2f %
                   Heat Index%2.2f %
                   Barometric Pressure%4.2f Pa
                   MQTT Status%s
                   Last MQTT Message%s
                   
                   <table align="center" border="1" cellpadding="1" cellspacing="1" style="width:500px;">
                   Settings
                   Sensor Name%s
                   MQTT Server%s
                   MQTT Port%s
                   Update Server%s
                   
                   

<a href="/reset">Reset Config

",
                   MotionSensor ? "true" : "false", LightReading, Temperature, TemperatureF, humidity, HeatIndex, Pressure, MQTT_Status  ? "Connected" : "Connection Failed", MQTT_Message ? "Success" : "Failed", Sensor_Name, mqtt_server, mqtt_port, update_server
);



All those spaces ARE part of the array. 20 useless characters per line * 23 lines = a lot of waste.

I would dump either the server part of the code OR the MQTT publishing, to see which is causing your code to "crash". The ESP is generally pretty good at telling you what the problem is - but perhaps not in a way that makes sense to you. You could share the data displayed when the ESP "crashes". It might mean more to someone else.

I completely missed that being a long, should have been a bool. Thank you for catching that.

I expected the compiler to identify that as formatting and remove it. Looking at the html src from the browser, you are right it is in there. Ill have to fix that. Thanks again!

Danois90:
You could save your self some 400 bytes of memory by declaring "char HTML_BUFFER[2000]" globally so that it can be used in "handleRoot()", "handleReset()", "mqtt_post()" and so on. In "mqtt_post()" you are using JSON with "StaticJsonBuffer<BUFFER_SIZE> jsonBuffer" and "JsonObject& root = jsonBuffer.createObject();" - this may be a problem and you should tru to manually create the JSON-object in the global "HTML_BUFFER". And last but not least, the "ESP8266WebServer" is using the "String" object. If possible, try a webserver that does not use the String object.

EDIT: Try to add this to your sketch, and see if the available memory drops:

//Other code

unsigned long lastMemReport = 0;

int freeRam () {
 extern int __heap_start, *__brkval;
 int v;
 return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

void setup()
{
 Serial.begin(9600);
 //Other code
}

void loop()
{
 //Other code
 if (millis() - lastMemReport >= 2000)
 {
   lastMemReport = millis();
   Serial.print("available memory=");
   Serial.println(freeRam(), DEC);
 }
}

Thanks for the response. Im not sure I know how to manually create the JSON-object in the global "HTML_BUFFER". Im still new to this and to JSON in general. Ill do a google search and see if there is some documentation that will help explain how to do that.
Would you happen to have a web server library that is recommended? Or should I look into writing the functions to handle the web calls?
At the moment I seem to have a problem getting serial output. The only way I can get the serial port to show up is if I push the flash button and then it will show the comport but all I can do is upload a sketch to it. There does not appear to be any output. I've not see an issue like this reported and considering it is a cheep chinese knockoff I was chalking it up to that. lol That is why you dont see a single Serial.print in the code. I've tended to just output it to the web page.
Thank you for your response.

Danois90:
EDIT: Try to add this to your sketch, and see if the available memory drops:

//Other code

unsigned long lastMemReport = 0;

int freeRam () {
 extern int __heap_start, *__brkval;
 int v;
 return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

void setup()
{
 Serial.begin(9600);
 //Other code
}

void loop()
{
 //Other code
 if (millis() - lastMemReport >= 2000)
 {
   lastMemReport = millis();
   Serial.print("available memory=");
   Serial.println(freeRam(), DEC);
 }
}

Well the unit bombed out on me and suddenly I started getting data on the serial line. So I tried adding your suggestion and when I go to compile I get the following error.
sketch/HA_Room_Sensor.ino.cpp.o: In function ArduinoJson::JsonBuffer::createObject()': /home/cl/Arduino/libraries/ArduinoJson/src/ArduinoJson/JsonBufferImpl.hpp:16: undefined reference to __brkval'
/home/cl/Arduino/libraries/ArduinoJson/src/ArduinoJson/JsonBufferImpl.hpp:17: undefined reference to `__heap_start'
collect2: error: ld returned 1 exit status
exit status 1
Error compiling for board NodeMCU 1.0 (ESP-12E Module).

Sorry, I missed that you where using an NodeMCU - you should be able to get free memory with "node.heap()" then. A JSON object is quite simple and the code required is leaner than what you've got. Here is how a stable approach could be:

//This is only for the 2 first values, fill in the rest
const char MQTT_JSON[] = "{\"temperature\":%.2f,\"temperatureF\":%.2f}";

//Global buffer, call it what you want
#define GLOBAL_BUFFER_SIZE 2000
char globuf[GLOBAL_BUFFER_SIZE];

void mqtt_post()
{

  snprintf(globuf, GLOBAL_BUFFER_SIZE, MQTT_JSON, temperature, temperatureF);

  //Post globuf as JSON object

}

You can read more about JSON here: JSON - Wikipedia

Danois90:
Sorry, I missed that you where using an NodeMCU - you should be able to get free memory with "node.heap()" then.

No problem, it turns out that ESP.getFreeHeap() works for pulling the free heap memory. So I got the bit of code in and working. Combine that with the fact that the serial started working and I have some debug output.Yay!
I did some code rework and eliminated a few variables. It now runs for 6 hours without crashing which is a great improvement but still not right.
When it crashes it drops an Exception
Exception (29):
epc1=0x40227543 epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000004 depc=0x00000000

Which basically says it ran out of heap and can not store the variable.

With the heap free output I have noticed something odd. Every 38 seconds the free heap gets smaller by 56 bytes. It is so dependable that it is exactly 19 runs through the heap print loop you provided. Running some math that comes out to right at 6 hours to run out of heap.

Now I just need to figure out where that 56 bytes is going lol.

Turns out there is a bug in the code for the ESP8266 board (under Board manager) V2.4.1 and V2.4.0 both have it but downgrading to V2.3.0 fixed the 56 byte memory leak!

Thank you for all your help in tracking this down!

Edit: and for the code to view the available heap, that is useful!

Sure I can do that

I started searching google because it was odd to see it loose exactly 56 bytes every 30 seconds or so because I did not have a loop that was running at a 30 sec interval.

That led me to the github issue tracker for the esp8266 Arduino board with this issue

It was reported back in March and is still open, the last message from a RyanFort was about downgrading to 2.3.0

It creates a 56 byte memory leak when WiFiClient is called globally.