Hey everyone,
I have been working on a project of mine, moving my current Arduino based motor test (for my remote control places) into ESP32 with some enhanced functionality to be able to do all tests through my tablet or PC and retain all the results for later analysis.
The concept here is to run a series of tests on the motor with different throttle power and get readings for battery voltage amps, thrust of the motor etc. All operations for the app are via REST calls to the ESP32 and all results and sensor readings are using a Web Socket opened between the ESP32 and the App (primarily one way).
For this I have been using the ESPAsyncWebServer library to handle REST APIs and the WebSocket., but I am facing an issue for over 5 days now where the AsyncWebSocket::textAll method crashes forcing the ESP32 to abort the thread and restart. The exception Decoder gives me the below trace:
Decoding stack results
0x40083771: panic_abort at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/esp_system/panic.c line 408
0x4008be51: esp_system_abort at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/esp_system/esp_system.c line 137
0x4009149d: abort at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/newlib/abort.c line 46
0x40145d1f: __cxxabiv1::__terminate(void (*)()) at /builds/idf/crosstool-NG/.build/HOST-x86_64-w64-mingw32/xtensa-esp32-elf/src/gcc/libstdc++-v3/libsupc++/eh_terminate.cc line 47
0x40145d66: std::terminate() at /builds/idf/crosstool-NG/.build/HOST-x86_64-w64-mingw32/xtensa-esp32-elf/src/gcc/libstdc++-v3/libsupc++/eh_terminate.cc line 57
0x40145f55: __cxxabiv1::__cxa_allocate_exception(std::size_t) at /builds/idf/crosstool-NG/.build/HOST-x86_64-w64-mingw32/xtensa-esp32-elf/src/gcc/libstdc++-v3/libsupc++/eh_alloc.cc line 300
0x4014600c: operator new(unsigned int) at /builds/idf/crosstool-NG/.build/HOST-x86_64-w64-mingw32/xtensa-esp32-elf/src/gcc/libstdc++-v3/libsupc++/new_op.cc line 54
0x40146835: operator new[](unsigned int) at /builds/idf/crosstool-NG/.build/HOST-x86_64-w64-mingw32/xtensa-esp32-elf/src/gcc/libstdc++-v3/libsupc++/new_opv.cc line 32
0x400d9142: AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(unsigned char*, unsigned int) at .pio/libdeps/esp32dev/ESP Async WebServer/src/AsyncWebSocket.cpp line 138
0x400d945d: AsyncWebSocket::makeBuffer(unsigned char*, unsigned int) at .pio/libdeps/esp32dev/ESP Async WebServer/src/AsyncWebSocket.cpp line 1210
0x400d9d9c: AsyncWebSocket::textAll(char const*, unsigned int) at .pio/libdeps/esp32dev/ESP Async WebServer/src/AsyncWebSocket.cpp line 962
0x400d9db9: AsyncWebSocket::textAll(char const*) at .pio/libdeps/esp32dev/ESP Async WebServer/src/AsyncWebSocket.cpp line 1095
0x400d5d72: streamSensorValues() at src/main.cpp line 281
0x400d5d9c: webServerHandler(void*) at src/main.cpp line 137
For your info, I am using 2 tasks between the 2 cores, one to continuously read sensor data and the other one to serve any REST and WebSocket operations.
xTaskCreatePinnedToCore(processSensors, "Read sensor values", 10000, NULL, 1, &TaskProcessSensors, 0);
xTaskCreatePinnedToCore(webServerHandler, "REST API and Web Streaming", 10000, NULL, 1, &TaskWebServerHandler, 1);
void processSensors(void * pvParameters)
{
for(;;)
{
if (mt.IsActive() && (int) (millis() - timer1) > refreshRate / 2)
{
// read sensor values twice within the refresh period
mt.Handle();
sensors = mt.GetReadings();
timer1 = millis();
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
void webServerHandler(void * pvParameters)
{
for(;;)
{
if (mt.IsActive() && (int) (millis() - timer2) > refreshRate)
{
streamSensorValues();
timer2 = millis();
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
streamSensorValues will only be called if there is an active test and an opened WebSocket. this looks like this:
void streamSensorValues()
{
jsonDocument.clear();
jsonDocument["runningTime"] = mt.GetRunningTime();
jsonDocument["phase"] = TestModes[sensors.SensorValues.Phase];
jsonDocument["throttle"] = sensors.SensorValues.Throttle;
jsonDocument["voltage"] = sensors.SensorValues.Voltage;
jsonDocument["current"] = sensors.SensorValues.Current;
jsonDocument["thrust"] = sensors.SensorValues.Thrust;
jsonDocument["power"] = sensors.SensorValues.Power;
jsonDocument["consumption"] = sensors.SensorValues.Consumption;
if (serializeJson(jsonDocument, buffer) > 0)
ws.textAll(buffer); // Stream sensor values
}
What I have noticed during all the tests I have done so far, ESP32 will restart roughly after 2min and 15s within a test, when roughly 1053 readings have been gone through the WebSocket. the time varies if I change the refresh rate of the entries sent out in the WebSocket but the total number of entries is roughly the same (between 1040 to 1130 sometimes).
I can also confirm that the cause is not the jsonDocument operations, because I get the same results if I sent a static text with roughly the same size as the readings being streamed, and it's all down to the number of entries I send over the WebSocket.
I have been reading a lot of posts in various forums so far and I have seen the occasional post indicating that the AsyncWebSocket::textAll has some memory leak issues, but the posts have been about 3-4 years old and I was wondering if anyone is experiencing something similar or even better if anyone has any idea how I can fix the issue
Looking forward on any thoughts out there
Mike