'ESP32 FreeRTOS how to avoid hardware conflict using MCP23017

I have an ESP32 using a MCP23017 IC using a rotary encoder working perfect with interrupts and a task. Then I added to a L293D connected to the MCP23017 and works fine.

The problem happens when I interact with the rotary encoder while the stepper motor is working. The ESP32 reboots and I'm pretty sure the reason is that both tasks want to access the MCP23017 hardware at the same time.

For the MCP23017 is using a Sempahore:

SemaphoreHandle_t rotaryISRSemaphore = nullptr;
// MCP23017
Adafruit_MCP23017 mcp;
....

void setup(){
   ....
   rotaryISRSemaphore = xSemaphoreCreateBinary();
   mcp.begin(1);      // use default address 0

   xTaskCreatePinnedToCore(&rotaryReaderTask, "rotary reader", 2048, NULL, 20, NULL, 1);
   attachInterrupt(esp32IntPin, intCallBack, FALLING);
  
   xTaskCreate(stepperMotor,"Stepper Motor",2048, NULL, 1, NULL);
}

// The int handler will just signal that the int has happened
// we will do the work from a task.
void IRAM_ATTR intCallBack() {
   //Serial.println("intCallback");
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    xSemaphoreGiveFromISR(rotaryISRSemaphore, &xHigherPriorityTaskWoken);
    if(xHigherPriorityTaskWoken) {
        portYIELD_FROM_ISR ();
    }
}

void rotaryReaderTask(void* pArgs) {
    (void)pArgs;
    Serial.println("Started rotary reader task.");
    while(true) {
        if(xSemaphoreTake(rotaryISRSemaphore, portMAX_DELAY) == pdPASS) {
        
            handleInterrupt();
         
        }

void handleInterrupt(){

    //Read the entire state when the interrupt occurred
    ....
    // Here some work to do with the MCP23017
}

void loop() {


}

int stepperValues[4][4] = {{HIGH,LOW,HIGH,LOW},{LOW,HIGH,HIGH,LOW},{LOW,HIGH,LOW,HIGH}, {HIGH,LOW,LOW,HIGH}};

void stepperMotor(void * params){
  (void) params;
  for(;;){ 
         if(!stopStepper){
        for ( int i = 0; i < 4; ++i ) {

      // loop through columns of current row
        mcp.digitalWrite(10, stepperValues[i][0]);
        mcp.digitalWrite(11, stepperValues[i][1]);
        mcp.digitalWrite(12, stepperValues[i][2]);
        mcp.digitalWrite(13, stepperValues[i][3]);
       } 
    }
    vTaskDelay(10 / portTICK_PERIOD_MS);
  }
}

Any clue on how to avoid ESP32 reboot issue?



Solution 1:[1]

When you have a shared resource that needs to be accessed by multiple tasks, you need to protect the resource using a mutex.

If you have full control over the code, the best option would be to add mutex to the I2C bus, as you may have other devices on the I2C bus that need to be accessed from multiple tasks.

If you cannot modify the I2C code and MCP23017 is the only device on the bus, you can write some wrapper functions/class that lock a mutex before accessing the MCP23017 and unlock it afterwards.

Please note, that you cannot use mutexes in ISR, so your code may need to be further refactored to only access the device from a task context.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1