I'm having hard time to debug an ESP32 application that occasionally shows odd behaviors like fatal error that causes resets, often related to network stuff, i.e.:
assert failed: tcp_update_rcv_ann_wnd IDF/components/lwip/lwip/src/core/tcp.c:951 (new_rcv_ann_wnd <= 0xffff)
Backtrace: 0x40083fd1:0x3ffdd470 0x400960d1:0x3ffdd490 0x4009b9e5:0x3ffdd4b0 0x4012feee:0x3ffdd5e0 0x4012ff9c:0x3ffdd600 0x400f416b:0x3ffdd620 0x4012c890:0x3ffdd640
#0 0x40083fd1:0x3ffdd470 in panic_abort at /Users/mark/Desktop/ESP32/ESP32S2/esp-idf-public/components/esp_system/panic.c:408
#1 0x400960d1:0x3ffdd490 in esp_system_abort at /Users/mark/Desktop/ESP32/ESP32S2/esp-idf-public/components/esp_system/esp_system.c:137
#2 0x4009b9e5:0x3ffdd4b0 in __assert_func at /Users/mark/Desktop/ESP32/ESP32S2/esp-idf-public/components/newlib/assert.c:85
#3 0x4012feee:0x3ffdd5e0 in tcp_update_rcv_ann_wnd at /Users/mark/Desktop/ESP32/ESP32S2/esp-idf-public/components/lwip/lwip/src/core/tcp.c:951
(inlined by) tcp_update_rcv_ann_wnd at /Users/mark/Desktop/ESP32/ESP32S2/esp-idf-public/components/lwip/lwip/src/core/tcp.c:931
#4 0x4012ff9c:0x3ffdd600 in tcp_recved at /Users/mark/Desktop/ESP32/ESP32S2/esp-idf-public/components/lwip/lwip/src/core/tcp.c:991
#5 0x400f416b:0x3ffdd620 in _tcp_recved_api(tcpip_api_call_data*) at .pio/libdeps/Aphrodite/Async TCP/src/AsyncTCP.cpp:444
#6 0x4012c890:0x3ffdd640 in tcpip_thread_handle_msg at /Users/mark/Desktop/ESP32/ESP32S2/esp-idf-public/components/lwip/lwip/src/api/tcpip.c:172
(inlined by) tcpip_thread at /Users/mark/Desktop/ESP32/ESP32S2/esp-idf-public/components/lwip/lwip/src/api/tcpip.c:154
It's not easy to replicate the problem, so I'm trying to understand what can cause it.
At first, I tried to inspect the RAM usage. The ESP.getFreeHeap()
and ESP.getMaxAllocHeap()
return:
Free heap: 76452 bytes
Max alloc: 61428 bytes
I don't think I'm running out of memory, even considering the fragmentation due to the usage of the String
class. I'm using it since the ESPAsyncWebServer
library uses it for the processor.
For this reason I have a lot of code that use the String
class, example:
String WebApp::processor(const String &var)
{
if (var == F("FORM_ALARM_DURATION")) return String(_alarm.UserProgram()->duration / 60);
if (var == F("FORM_ALARM_FADE")) return String(_alarm.UserProgram()->fadeIn);
if (var == F("FORM_ALARM_MODE")) return String(static_cast<int>(_alarm.UserProgram()->mode));
// ...
if (var == F("SELECT_SESSION_PROGRAMS")) return selectSessionPrograms();
if (var == F("SELECT_ALARM_PROGRAMS")) return selectAlarmPrograms();
if (var == F("SELECT_ALARM_MODES")) return selectAlarmModes();
// ...
}
where each function returns a String
, like:
String WebApp::selectSessionPrograms()
{
String html = "";
for (int i = 0; i < _session.ProgramsSize(); i++)
{
SessionProgram *program = _session.GetProgramAtPosition(i);
if (i > 0 && !_session.HasBouquetAvailable(program)) continue;
html += addSelectOption(String(i), i == 0 ? _languages.GetText("LANG_WEB_CUSTOM") : program->name);
}
return html;
}
I understand it's hard to know, but as a general rule of thumb, I kindly ask:
- is my heap status critical?
- should I find a way to NOT use
String
even withESPAsyncWebServer
? - can a class level
String
variable (instead of function level one) could improve the situation? I mean, creating aString html = "";
as class member, instead of having one for each function.