I am trying to write a class to help me interface a Bluetooth module with an ESP32 over serial, but I am having trouble. Sometimes, the Bluetooth module sends events without being prompted, so I'm trying to set up a FreeRTOS task that runs in the background that constantly collects events and stores them in a queue.
Here is that task:
void ICBluetoothController::_rxTask(void *pvParameters)
{
ESP_LOGI("ICBluetoothController _serial_task", "started");
auto self = static_cast<ICBluetoothController*>(pvParameters);
uint8_t b = 0;
char payload[128];
size_t index = 0;
while (true)
{
while (self->_serial.available())
{
b = self->_serial.read();
if (b == '\r' || b == '\n')
{
if (index > 0)
{
payload[index] = '\0'; // Null-terminate the string
if (xQueueSend(self->_rx_queue, &payload, portMAX_DELAY) != pdPASS)
{
ESP_LOGE("ICBluetoothController", "Failed to send to queue");
}
else
{
ESP_LOGI("ICBluetoothController", "Received: %s", payload);
}
index = 0;
}
continue;
}
if (index < sizeof(payload) - 1)
{
payload[index++] = static_cast<char>(b);
}
}
vTaskDelay(pdMS_TO_TICKS(20));
}
}
It works great! ....but only when the call to vTaskDelay()
is commented out at the bottom. When it's not, the code crashes with this:
Guru Meditation Error: Core 0 panic'ed (LoadProhibited). Exception was unhandled.
Core 0 register dump:
PC : 0x400d17bc PS : 0x00060130 A0 : 0x8008a298 A1 : 0x3ffb4a40
A2 : 0x3ffb21f0 A3 : 0x00000000 A4 : 0x3ffb4a6c A5 : 0x00000001
A6 : 0x00000000 A7 : 0x3f400161 A8 : 0x800d17cc A9 : 0x3ffb4a20
A10 : 0xffffffff A11 : 0x00000049 A12 : 0x3f400124 A13 : 0x00000021
A14 : 0x3f400161 A15 : 0x3f40013e SAR : 0x0000001e EXCCAUSE: 0x0000001c
EXCVADDR: 0xffffffff LBEG : 0x40086271 LEND : 0x40086281 LCOUNT : 0xfffffff7
Backtrace: 0x400d17b9:0x3ffb4a40 0x4008a295:0x3ffb4b10
#0 0x400d17b9 in ICBluetoothController::_rxTask(void*) at src/ICBluetoothController.cpp:41
#1 0x4008a295 in vPortTaskWrapper at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:162
I am confused because the backtrace points to self->_serial.available()
being the culprit, even though it runs fine without the delay. What is the issue here? I don't have a ton of experience with C++ or Arduino, if it's not already obvious.
EXCVADDR: 0xffffffff
almost always indicates a null or invalid pointer dereference.
Based on this line: auto self = static_cast<ICBluetoothController*>(pvParameters);
, this
is being passed into the task upon its creation. Then, first line of code that touches that pointer (self->_serial.available()
) is highlighted as crash reason in the trace, leading to deduction that the culprit is null pointer dereference.
The reason for it to happen only when vTaskDelay(pdMS_TO_TICKS(20));
is commented out is most likely the race condition. When task runs without any delay, it does it quickly enough for the object to outlive it, but when the delay is added, object gets destroyed, thus leading to null pointer dereference.
It is not possible to be exactly sure as provided code is incomplete. As a simple test, if (self)
guard can be added before first pointer usage.