ESP32 crashes every 2-3 hours of running code

The code prints time and it crashed at these times:

  • 8:50
  • 6:19
  • 5:19
  • 2:19
  • 0:19
  • 22:49
  • 22:8
  • 21:49
  • 19:49

It might seem like the error could be caused by runnig out of memory? I have no clue.
any help would be appreciated!

Error code: -1
Guru Meditation Error: Core  1 panic'ed (LoadProhibited). Exception was unhandled.

Core  1 register dump:
PC      : 0x4008af56  PS      : 0x00060d30  A0      : 0x8008b086  A1      : 0x3ffcb0b0  
A2      : 0x00000000  A3      : 0x00000000  A4      : 0x00000000  A5      : 0x0000000a  
A6      : 0x00000000  A7      : 0x00000008  A8      : 0x8008f7ca  A9      : 0x3ffcb0a0  
A10     : 0x3ffcb224  A11     : 0x3ff96355  A12     : 0x00001388  A13     : 0x0001d4c0  
A14     : 0x00ff0000  A15     : 0xff000000  SAR     : 0x0000001d  EXCCAUSE: 0x0000001c  
EXCVADDR: 0x00000000  LBEG    : 0x400899e8  LEND    : 0x400899f3  LCOUNT  : 0x00000000  


Backtrace: 0x4008af53:0x3ffcb0b0 0x4008b083:0x3ffcb0e0 0x40088891:0x3ffcb100 0x400d379b:0x3ffcb120 0x400db6d5:0x3ffcb190

0x4008af53: strptime_l at /builds/idf/crosstool-NG/.build/HOST-i686-w64-mingw32/xtensa-esp32-elf/src/newlib/newlib/libc/time/strptime.c line 238
0x4008b083: strptime_l at /builds/idf/crosstool-NG/.build/HOST-i686-w64-mingw32/xtensa-esp32-elf/src/newlib/newlib/libc/time/strptime.c line 323
0x40088891: tx_pwctrl_cal at phy_chip_v7_cal.c line 1879
0x400d379b: loop() at C:\Users\xxx\Documents\Arduino\ESPplay\apireq\LED_to_time/LED_to_time.ino line 112
0x400db6d5: loopTask(void*) at C:\Users\xxx\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.14\cores\esp32\main.cpp line 42
#include <WiFi.h>
#include <time.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <NeoPixelBus.h>

const char* ssid = "ssid";
const char* password = "pass";

//current time stuff
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 0;
const int daylightOffset_sec = 3600;


//Your Domain name with URL path or IP address with path
const char* sunEndpoint = "https://api.sunrise-sunset.org/json?lat=51.5073359&lng=-0.12765&date=today";

// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastTime = 0;
unsigned long timerDelay = 6000;

struct tm timeinfo;
bool Increased = false;

String sunRequest;
DynamicJsonDocument sunriseDoc(1024);

// pixel stuff
const uint16_t PixelCount = 4; // this example assumes 4 pixels, making it smaller will cause a failure
const uint8_t PixelPin = 14;  // make sure to set this to the correct pin, ignored for Esp8266
#define colorSaturation 128
NeoPixelBus<NeoGrbFeature, NeoWs2812xMethod> strip(PixelCount, PixelPin);
RgbColor white(colorSaturation);
RgbColor black(0);

void setup() {
  Serial.begin(115200);

  WiFi.begin(ssid, password);
  Serial.println("Connecting");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to WiFi network with IP Address: ");
  Serial.println(WiFi.localIP());

  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  Serial.println("Timer set to 5 seconds (timerDelay variable), it will take 5 seconds before publishing the first reading.");


  pinMode(12, INPUT_PULLUP);
  // attachInterrupt(digitalPinToInterrupt(12), increase, FALLING);
  pinMode(2, OUTPUT);

  // pixel stuff
  strip.Begin();
  strip.Show();
}

// void increase(){
//   if (!Increased) {
//     currentHour+=1;
//     Increased=true;
//     digitalWrite(2, HIGH);
//   }
// }

void some_call_led(RgbColor colour){
  for (int n = 0 ; n < PixelCount; n++) {
    strip.SetPixelColor(n,colour);
  }
  strip.Show();
}

void loop() {
  //Send an HTTP POST request every 10 minutes
  digitalWrite(2, LOW);
  if ((millis() - lastTime) > timerDelay) {
    if (digitalRead(12) == 0){
      digitalWrite(2, HIGH);
    }

    //Check WiFi connection status
    if (WiFi.status() == WL_CONNECTED) {
      
      if (getLocalTime(&timeinfo)) {
        int currentHour = timeinfo.tm_hour;
        int currentMinute = timeinfo.tm_min;


        // parse json for sun request
        sunRequest = httpGETRequest(sunEndpoint);
        deserializeJson(sunriseDoc, sunRequest);

        const char* sunrise = sunriseDoc["results"]["sunrise"];
        const char* sunset = sunriseDoc["results"]["sunset"];


        // Convert sunrise and sunset times to hours and minutes
        int sunriseHour = atoi(sunrise);
        int sunriseMinute = atoi(sunrise + 3);
        int sunsetHour = atoi(sunset);
        int sunsetMinute = atoi(sunset + 3);



        // compensation for api being stupid
        if (strstr(sunrise, "PM") != NULL && sunriseHour != 12) {
          sunriseHour += 12;
        }
        if (strstr(sunset, "PM") != NULL && sunsetHour != 12) {
          sunsetHour += 12;
        }

        
        int totalHourSun = sunsetHour - sunriseHour;
        int totalMinuteSun = sunsetMinute - sunriseMinute;
        int maxHour = 12;
        int maxMinute = 0;
        int compHour = (totalHourSun - maxHour)/2;
        int compMinute = (totalMinuteSun - maxMinute)/2;
        int LEDoffHour = sunsetHour-compHour;
        int LEDoffMinute;
        if (sunsetMinute-compMinute>61){
          LEDoffMinute = (sunsetMinute-compMinute)-60;
          LEDoffHour += 1;
        }
        else {
          LEDoffMinute = sunsetMinute-compMinute;
        }
        
        
        int LEDonHour = sunriseHour+compHour;
        int LEDonMinute;
        if (sunriseMinute+compMinute<0){
          LEDonMinute = 60+(compMinute+sunriseMinute);
          LEDonHour -= 1;
        }
        else {
          LEDonMinute = sunriseMinute+compMinute;
        }
        
        
        Serial.print("sunrise: ");
        Serial.print(sunriseHour);
        Serial.print(":");
        Serial.println(sunriseMinute);   

        Serial.print("sunset: ");
        Serial.print(sunsetHour);
        Serial.print(":");
        Serial.println(sunsetMinute);                

        Serial.print("total: ");
        Serial.print(totalHourSun);
        Serial.print(":");
        Serial.println(totalMinuteSun);    
        
        Serial.print("comp: ");
        Serial.print(compHour);
        Serial.print(":");
        Serial.println(compMinute);    

        Serial.print("on: ");
        Serial.print(LEDonHour);
        Serial.print(":");
        Serial.println(LEDonMinute);    

        Serial.print("off: ");
        Serial.print(LEDoffHour);
        Serial.print(":");
        Serial.println(LEDoffMinute);  

        Serial.print("Current Time:");
        Serial.print(currentHour);
        Serial.print(":");
        Serial.println(currentMinute);


///to do with debug
        // Serial.print("Increased:");
        // Serial.println(Increased);
        // if (Increased) {
        //   Increased=false;
        //   digitalWrite(2, LOW);
        // }


        // Compare current time with sunrise and sunset times
        if ((currentHour > LEDoffHour || (currentHour == LEDoffHour && currentMinute >= LEDoffMinute)) || (currentHour < LEDonHour || (currentHour == LEDonHour && currentMinute < LEDonMinute))) {
          // It's past sunset, turn off LEDs
          // Code to turn off LEDs
          Serial.println("past sunset turn off LEDS");
          some_call_led(black);
        }
        else {
          // It's past sunrise, turn on LEDs
          // Code to turn on LEDs
          Serial.println("past sunrise turn on LEDS");
          some_call_led(white);
        }


      }
    }
    else {
      Serial.println("WiFi Disconnected");
    }
    lastTime = millis();
  }
}


String httpGETRequest(const char* serverName) {

  HTTPClient http;

  // Your Domain name with URL path or IP address with path
  http.begin(serverName);

  // If you need Node-RED/server authentication, insert user and password below
  //http.setAuthorization("REPLACE_WITH_SERVER_USERNAME", "REPLACE_WITH_SERVER_PASSWORD");

  // Send HTTP POST request
  int httpResponseCode = http.GET();

  String payload = "{}";

  if (httpResponseCode > 0) {
//    Serial.print("HTTP Response code: ");
//    Serial.println(httpResponseCode);
    payload = http.getString();
  }
  else {
    Serial.print("Error code: ");
    Serial.println(httpResponseCode);
  }
  // Free resources
  http.end();

  return payload;
}

When you say it crashed at those times, do you mean that was the last time printed? Looks like every crash happened at or super close to a transition from a numeric 9 to 0, which might make me look at something happening in the code your didn't post.
Best I can do,
Have fun!

Yeah that was the last time it printed, but after restart it printed those exact times once again.

for example:

sunrise: 7:5
sunset: 15:5
total: 8:0
comp: -2:0
on: 5:5
off: 17:5
Current Time:19:49
past sunset turn off LEDS
sunrise: 7:5
sunset: 15:5
total: 8:0
comp: -2:0
on: 5:5
off: 17:5
Current Time:19:49
past sunset turn off LEDS
Error code: -1
Guru Meditation Error: Core  1 panic'ed (LoadProhibited). Exception was unhandled.

Core  1 register dump:
PC      : 0x4008af56  PS      : 0x00060d30  A0      : 0x8008b086  A1      : 0x3ffcb0b0  
A2      : 0x00000000  A3      : 0x00000000  A4      : 0x00000000  A5      : 0x0000000a  
A6      : 0x00000000  A7      : 0x00000008  A8      : 0x8008f7ca  A9      : 0x3ffcb0a0  
A10     : 0x3ffcb224  A11     : 0x3ff96355  A12     : 0x00001388  A13     : 0x0001d4c0  
A14     : 0x00ff0000  A15     : 0xff000000  SAR     : 0x0000001d  EXCCAUSE: 0x0000001c  
EXCVADDR: 0x00000000  LBEG    : 0x400899e8  LEND    : 0x400899f3  LCOUNT  : 0x00000000  


Backtrace: 0x4008af53:0x3ffcb0b0 0x4008b083:0x3ffcb0e0 0x40088891:0x3ffcb100 0x400d379b:0x3ffcb120 0x400db6d5:0x3ffcb190




ELF file SHA256: 21b29729efa7653a

Rebooting...
ets Jun  8 2016 00:22:57

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:1184
load:0x40078000,len:13260
load:0x40080400,len:3028
entry 0x400805e4
Connecting
...
Connected to WiFi network with IP Address: x.x.x.x
Timer set to 5 seconds (timerDelay variable), it will take 5 seconds before publishing the first reading.
sunrise: 7:5
sunset: 15:5
total: 8:0
comp: -2:0
on: 5:5
off: 17:5
Current Time:19:49
past sunset turn off LEDS
sunrise: 7:5
sunset: 15:5
total: 8:0
comp: -2:0
on: 5:5
off: 17:5
Current Time:19:49
past sunset turn off LEDS

It post the time every 6 seconds, so it prints the time every 6 seconds but still I think it is strange that it crashes at those times, not sure why its usually every two hours.

All stack traces are the same for every crash that happened.

Also could you clarify what you mean by the code I didnt post, I thought all my code was there :frowning:

All I can say is, when I first scrolled your post, it wasn't there. I've seen it happen a few times. Either that, or I missed the break between the dump and the listing.

Either way, sorry!

1 Like

If the only use of the internet is to get sunrise and sunset, consider calculating that locally. All you need is date and geographic location.

2 Likes

no worries, are there any tools you could recommend to aid in debugging the code further?

I haven't thought of doing this, I will have a look into it

Sorry, I'm not versed in the '32. I don't see anything that jumps out as incorrect or risky in your code, but that's a superficial view. Had expected a run-amuck pointer for a string, for example, but it's not obvious.

1 Like

I don't know exactly, especially because I never userd Json with ESP32, but like "String" variables (I really hate the way they're implemented...), if I'm you I'd try replacing "DynamicJsonDocument" with "StaticJsonDocument" to avoid continuous allocating/deallocating memory for each usage.
Give it a try, hope it helps...

by google I found this:

https://github.com/espressif/arduino-esp32/issues/3510

Topic looks same like yours. There was a topic with the flash frequency inside the IDE setting.

Any chance of a division by zero ?

Also i tend to avoid pin 14, and pin 12 even more so on an esp32

You could check to see if the heap gets fragmented beyond belief, but with your code and the available memory i somehow doubt that is the issue.

String sunRequest;

does that variable really needs to be global ? Global String variables can be a bad idea unless treated properly. again i somehow doubt that memory deficiency is the issue, more likely GPIO 12 (or 14) related, or a division by zeor, but i don;t see any divisions anywhere.

THere is some kind of an add-in /plug-in tool called ESP Exception decoder
You copy & paste the Exception Hexdump into the Exception decoder and the the exception decoder will find the place in the source code

best regards Stefan

I added printing this

        Serial.print("ESP.getFreeHeap()=");
        Serial.println(ESP.getFreeHeap());

The value of free heap changed only a few bytes up and down.

and changed the delaytime for the printing down to 50 milliseconds.
My intend was to make the code crash faster.

Though the http-requesting takes its time. The printing occurs more or less only once every few seconds ony sometimes faster.

The code crashed after running from 22:10:57 to the next morning 4:22:52
which means the code was running for 6 hours

The exception decoder pointed to somewhere in main.cpp.
I'm not a specialist about what makes a code crash but I assume it was memory-corruption

So I modified your code to

  • use SafeString instead of String but a very big sized SafeString (8196 bytes)
#include <SafeString.h>

//String sunRequest;
//String payload = "{}";

cSF (sunRequest_SS,8196);
//cSF (payload_SS,8196);

directly storing the received payload into the SafeString with name "sunRequest_SS"
instead of returning the String from function httpGETRequest()

        //sunRequest = httpGETRequest(sunEndpoint);
        httpGETRequest(sunEndpoint); // new version store it directly into sunRequest_SS 
  • increased the size of the JSON-object from 1024 to 8196
//DynamicJsonDocument sunriseDoc(1024);
DynamicJsonDocument sunriseDoc(8196);
  • using my own WiFi-router as the NTP-server to take away traffic from the public NTP-server
//const char* ntpServer = "pool.ntp.org";
const char* ntpServer = "fritz.box";

This resulted in changing the freeheap-memory from 246 kB to 234 kB 12kB less.

Now I'm running a test if this code-version is running without crashing.....
I use a serial terminal-software that stores the data printed to serial into a file to be able to analyse it even if it has run for days.

For convenience the complete modified code as I have it running on an ESP32 for testing

#include <WiFi.h>
#include <time.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <NeoPixelBus.h>
#include <SafeString.h>

//String sunRequest;
//String payload = "{}";

cSF (sunRequest_SS,8196);
//cSF (payload_SS,8196);


const char *ssid       = ""; 
const char *password = "";

//current time stuff
//const char* ntpServer = "pool.ntp.org";
const char* ntpServer = "fritz.box";
const long gmtOffset_sec = 0;
const int daylightOffset_sec = 3600;


//Your Domain name with URL path or IP address with path
const char* sunEndpoint = "https://api.sunrise-sunset.org/json?lat=51.5073359&lng=-0.12765&date=today";

// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastTime = 0;
unsigned long timerDelay = 50;

struct tm timeinfo;
bool Increased = false;

//DynamicJsonDocument sunriseDoc(1024);
DynamicJsonDocument sunriseDoc(8196);

// pixel stuff
const uint16_t PixelCount = 4; // this example assumes 4 pixels, making it smaller will cause a failure
const uint8_t PixelPin = 14;  // make sure to set this to the correct pin, ignored for Esp8266
#define colorSaturation 128
NeoPixelBus<NeoGrbFeature, NeoWs2812xMethod> strip(PixelCount, PixelPin);
RgbColor white(colorSaturation);
RgbColor black(0);


void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");

  WiFi.begin(ssid, password);
  Serial.println("Connecting");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to WiFi network with IP Address: ");
  Serial.println(WiFi.localIP());

  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  Serial.println("Timer set to 5 seconds (timerDelay variable), it will take 5 seconds before publishing the first reading.");


  pinMode(12, INPUT_PULLUP);
  // attachInterrupt(digitalPinToInterrupt(12), increase, FALLING);
  pinMode(2, OUTPUT);

  // pixel stuff
  strip.Begin();
  strip.Show();
}

// void increase(){
//   if (!Increased) {
//     currentHour+=1;
//     Increased=true;
//     digitalWrite(2, HIGH);
//   }
// }

void some_call_led(RgbColor colour){
  for (int n = 0 ; n < PixelCount; n++) {
    strip.SetPixelColor(n,colour);
  }
  strip.Show();
}

void loop() {
  //Send an HTTP POST request every 10 minutes
  digitalWrite(2, LOW);
  if ((millis() - lastTime) > timerDelay) {
    if (digitalRead(12) == 0){
      digitalWrite(2, HIGH);
    }

    //Check WiFi connection status
    if (WiFi.status() == WL_CONNECTED) {
      
      if (getLocalTime(&timeinfo)) {
        int currentHour = timeinfo.tm_hour;
        int currentMinute = timeinfo.tm_min;


        // parse json for sun request
        //sunRequest = httpGETRequest(sunEndpoint);
        httpGETRequest(sunEndpoint); // new version store it directly into sunRequest_SS 
        deserializeJson(sunriseDoc, sunRequest_SS.c_str() );

        const char* sunrise = sunriseDoc["results"]["sunrise"];
        const char* sunset = sunriseDoc["results"]["sunset"];


        // Convert sunrise and sunset times to hours and minutes
        int sunriseHour = atoi(sunrise);
        int sunriseMinute = atoi(sunrise + 3);
        int sunsetHour = atoi(sunset);
        int sunsetMinute = atoi(sunset + 3);



        // compensation for api being stupid
        if (strstr(sunrise, "PM") != NULL && sunriseHour != 12) {
          sunriseHour += 12;
        }
        if (strstr(sunset, "PM") != NULL && sunsetHour != 12) {
          sunsetHour += 12;
        }

        
        int totalHourSun = sunsetHour - sunriseHour;
        int totalMinuteSun = sunsetMinute - sunriseMinute;
        int maxHour = 12;
        int maxMinute = 0;
        int compHour = (totalHourSun - maxHour)/2;
        int compMinute = (totalMinuteSun - maxMinute)/2;
        int LEDoffHour = sunsetHour-compHour;
        int LEDoffMinute;
        if (sunsetMinute-compMinute>61){
          LEDoffMinute = (sunsetMinute-compMinute)-60;
          LEDoffHour += 1;
        }
        else {
          LEDoffMinute = sunsetMinute-compMinute;
        }
        
        
        int LEDonHour = sunriseHour+compHour;
        int LEDonMinute;
        if (sunriseMinute+compMinute<0){
          LEDonMinute = 60+(compMinute+sunriseMinute);
          LEDonHour -= 1;
        }
        else {
          LEDonMinute = sunriseMinute+compMinute;
        }
        
        
        Serial.print("sunrise: ");
        Serial.print(sunriseHour);
        Serial.print(":");
        Serial.println(sunriseMinute);   

        Serial.print("sunset: ");
        Serial.print(sunsetHour);
        Serial.print(":");
        Serial.println(sunsetMinute);                

        Serial.print("total: ");
        Serial.print(totalHourSun);
        Serial.print(":");
        Serial.println(totalMinuteSun);    
        
        Serial.print("comp: ");
        Serial.print(compHour);
        Serial.print(":");
        Serial.println(compMinute);    

        Serial.print("on: ");
        Serial.print(LEDonHour);
        Serial.print(":");
        Serial.println(LEDonMinute);    

        Serial.print("off: ");
        Serial.print(LEDoffHour);
        Serial.print(":");
        Serial.println(LEDoffMinute);  

        Serial.print("Current Time:");
        Serial.print(currentHour);
        Serial.print(":");
        Serial.println(currentMinute);
        Serial.print("ESP.getFreeHeap()=");
        Serial.println(ESP.getFreeHeap());


///to do with debug
        // Serial.print("Increased:");
        // Serial.println(Increased);
        // if (Increased) {
        //   Increased=false;
        //   digitalWrite(2, LOW);
        // }


        // Compare current time with sunrise and sunset times
        if ((currentHour > LEDoffHour || (currentHour == LEDoffHour && currentMinute >= LEDoffMinute)) || (currentHour < LEDonHour || (currentHour == LEDonHour && currentMinute < LEDonMinute))) {
          // It's past sunset, turn off LEDs
          // Code to turn off LEDs
          Serial.println("past sunset turn off LEDS");
          some_call_led(black);
        }
        else {
          // It's past sunrise, turn on LEDs
          // Code to turn on LEDs
          Serial.println("past sunrise turn on LEDS");
          some_call_led(white);
        }


      }
    }
    else {
      Serial.println("WiFi Disconnected");
    }
    lastTime = millis();
  }
}


//String httpGETRequest(const char* serverName) {
void httpGETRequest(const char* serverName) {

  HTTPClient http;

  // Your Domain name with URL path or IP address with path
  http.begin(serverName);

  // If you need Node-RED/server authentication, insert user and password below
  //http.setAuthorization("REPLACE_WITH_SERVER_USERNAME", "REPLACE_WITH_SERVER_PASSWORD");

  // Send HTTP POST request
  int httpResponseCode = http.GET();


  if (httpResponseCode > 0) {
//    Serial.print("HTTP Response code: ");
//    Serial.println(httpResponseCode);
    //payload = http.getString();
    sunRequest_SS  = http.getString().c_str();
  }
  else {
    Serial.print("Error code: ");
    Serial.println(httpResponseCode);
  }
  // Free resources
  http.end();

  // return payload; 
  // now done by sunRequest_SS  = http.getString().c_str();  
}

I will report if the code did crash or not.
In the meantime you could look up on GitHub if there is an issue reported that the
DynamicJsonDocument-object or the causes HTTPClient-object crashes

best regards Stefan

1 Like

Huh, well, I agree with that and home the OP will get the problem, but just a small note: ok but if you meant to specify 8k it's a power of 2, so it'd be 8192 not 8196.. :wink:

I actually suspect that it has something to do with the strstr() function that is called here.

if (strstr(sunrise, "PM") != NULL && sunriseHour != 12) {
          sunriseHour += 12;
        }
        if (strstr(sunset, "PM") != NULL && sunsetHour != 12) {
          sunsetHour += 12;

since that is line 112 in the code.

0x400d379b: loop() at C:\Users\xxx\Documents\Arduino\ESPplay\apireq\LED_to_time/LED_to_time.ino line 112

If the null terminator is missing somehow, strstr would keep looking forever until the counter runs out.
There used to be a bug in the ESP8266 core that caused the use of strtok() to crash, just thinking it may be something similar.
Of course i am just fishing here.

1 Like

As this problem seems very weird, I'm intrigued so I wanna Just add my small contribution, hoping the culprit will be finally found...

To start, I think the response of the request doesn't need 8k bytes buffers, the json reply to API call gives me not more than a few hundreds of bytes (actually, it's 366 bytes long) so a 1024 bytes could be enough:

{"results":{"sunrise":"7:49:13 AM","sunset":"3:54:28 PM","solar_noon":"11:51:50 AM","day_length":"08:05:15","civil_twilight_begin":"7:11:45 AM","civil_twilight_end":"4:31:55 PM","nautical_twilight_begin":"6:29:04 AM","nautical_twilight_end":"5:14:36 PM","astronomical_twilight_begin":"5:48:35 AM","astronomical_twilight_end":"5:55:06 PM"},"status":"OK","tzId":"UTC"}

So, the suspect described by @StefanL38 about "strstr()" makes more sense to me, and I agree on getting rid of "String" return value for httpGETRequest() function.

But just in case of an error is returned, I'd return false:

bool httpGETRequest(const char* serverName) {
  bool ok = false;
  HTTPClient http;
  // Your Domain name with URL path or IP address with path
  http.begin(serverName);
  // Send HTTP POST request
  int httpResponseCode = http.GET();
  if (httpResponseCode > 0) {
    sunRequest_SS  = http.getString().c_str();
    ok = true;
  }
  else {
    Serial.print("Error code: ");
    Serial.println(httpResponseCode);
  }
  // Free resources
  http.end();
  return ok;
}

so to avoid an incorrect response processing:

...
        // Get the response, and if good...
        if (httpGETRequest(sunEndpoint)) {
          // ...parse json for sun request
          deserializeJson(sunriseDoc, sunRequest_SS.c_str() );

          const char* sunrise = sunriseDoc["results"]["sunrise"];
          const char* sunset = sunriseDoc["results"]["sunset"];


          // Convert sunrise and sunset times to hours and minutes
          int sunriseHour = atoi(sunrise);
          int sunriseMinute = atoi(sunrise + 3);
...and so on
1 Like

This honor belongs to user @Deva_Rishi
best regards Stefan

1 Like

My codeversion that uses SafeStrings has run now ror 9 hours without any crash.

another reason to not use c_strings because you have to take care about such details on the user-level

1 Like

Yeah it shouldn't matter though if the functions are correctly implemented. On an ESP Strings are rarely an issue in usage, although also then only if they are local variables or get a reserved space. (using .reserve() )
In the end any choice requires the user to observe certain things, just as in any code.
people use FastLED to address WS2812 leds, but any write beyond the size of the array will cause issues.
adafruit_neopixel & Makuna Neopixelbus have functions for filling the output buffer, and these do error checking, but you can also get the pointer to the buffer and address them directly which can save huge amounts of code and time, but comes with the responsibilty for the user to make sure nothing goes wrong.

Thank you everyone for your input it has been really interesting seeing how weird this issue was.

I think I have made a solution from all of your suggestions, one that incorporates safe strings and some error handling on the api front.

I saw that the api sometimes returned as {} which then would unleash the arduino in finding AM or PM in that and run out of space, so I just added a regex check to make sure the api response matches expected input otherwise to use the previous response.

I have also reduced the amount of JSON deserialisation being done by comparing the initial api response and once it is different only then it would trigger deserialisation.

I have the code running now and it has been handling issues well but time will tell, I'm currently running a long test to see if it holds up and will update this with the results :slight_smile: