c++arduinoesp32freertos

ESP32 LoadProhibited with FreeRTOS tasks


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.


Solution

  • 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.