ESP32 crashes when using RT loop with SimpleFOC

Hi!

Because using serial communication in the main loop with SimpleFOC changes loop timings, I want to use RT execution for time-critical parts of the code. The manual shows a simple implementation of such loop on Real-time loop | Arduino-FOC , but I can’t get the code to work.

the RT timer loop used by itself works fine blinking an LED, but when SimpleFOC is added to the mix, the board crashes as soon as the timer callback is executed:

rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:4744
load:0x40078000,len:15712
load:0x40080400,len:3164
entry 0x4008059c
RT test
Delay
RT timer attach
Guru Meditation Error: Core  1 panic'ed (Interrupt wdt timeout on CPU1). 

Core  1 register dump:
PC      : 0x400890b7  PS      : 0x00060c35  A0      : 0x80089be0  A1      : 0x3ffbf9dc  
A2      : 0x3ffb9364  A3      : 0x3ffb81a0  A4      : 0x00000000  A5      : 0x00060c23  
A6      : 0x3ffc2c40  A7      : 0x0000abab  A8      : 0x3ffb81a0  A9      : 0x00000018  
A10     : 0x00000018  A11     : 0x0000008e  A12     : 0xb33fffff  A13     : 0x3f40f36c  
A14     : 0x00000001  A15     : 0x0000abab  SAR     : 0x00000000  EXCCAUSE: 0x00000006  
EXCVADDR: 0x00000000  LBEG    : 0x40084df8  LEND    : 0x40084e03  LCOUNT  : 0xffffffff  
Core  1 was running in ISR context:
EPC1    : 0x400dfd13  EPC2    : 0x00000000  EPC3    : 0x00000000  EPC4    : 0x00000000


Backtrace: 0x400890b4:0x3ffbf9dc |<-CORRUPTED


Core  0 register dump:
PC      : 0x400858e9  PS      : 0x00060035  A0      : 0x80088778  A1      : 0x3ffbf45c  
A2      : 0x00000000  A3      : 0xb33f5454  A4      : 0x0000abab  A5      : 0x3f40f2e8  
A6      : 0x3ffbc30c  A7      : 0x3ffb656c  A8      : 0x007bdf00  A9      : 0x3ffbdf00  
A10     : 0x003fffff  A11     : 0x00060b23  A12     : 0x00060b20  A13     : 0x3f40f2e8  
A14     : 0x00000000  A15     : 0x0000cdcd  SAR     : 0x0000001c  EXCCAUSE: 0x00000006  
EXCVADDR: 0x00000000  LBEG    : 0x00000000  LEND    : 0x00000000  LCOUNT  : 0x00000000  


Backtrace: 0x400858e6:0x3ffbf45c |<-CORRUPTED




ELF file SHA256: 32ecd9e7f

@P�@?ON@?T�@?�N@? ets Jul 29 2019 12:21:46&�?8��?j<@?z<@?�<@?T}). @?*J@?9J@?��@? K@?�H@?��

I tried changing timings of the loop RT, but even setting execution to once per second does not eliminate the crash. Just to be sure that it’s not an overrun I tried adding a mutex, but it did not help.

Here’s the code I use for testing:

#include <SimpleFOC.h>

MagneticSensorI2C sensor = MagneticSensorI2C(AS5600_I2C);
TwoWire I2Cone = TwoWire(0);
BLDCMotor motor = BLDCMotor(11);
BLDCDriver3PWM driver = BLDCDriver3PWM(32,33,25,22);
hw_timer_t *timer = NULL;

portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;

void ARDUINO_ISR_ATTR /*IRAM_ATTR*/ foc_loop() {
  // portENTER_CRITICAL_ISR(&timerMux);
  motor.loopFOC();
  motor.move();
  // portEXIT_CRITICAL_ISR(&timerMux);
} 

void setup() {
  Serial.begin(115200);
  Serial.println("RT test");
  
  I2Cone.begin(19, 18, 400000); 
  sensor.init(&I2Cone);
  motor.linkSensor(&sensor);

  driver.voltage_power_supply = 12;
  driver.init();
  motor.linkDriver(&driver);

  motor.foc_modulation = FOCModulationType::SpaceVectorPWM;
  motor.controller = MotionControlType::velocity;

  motor.init();
  motor.initFOC();

   Serial.println("Delay");
  _delay(1000);

  Serial.println("RT timer attach");
  timer = timerBegin(1000000);
  timerAttachInterrupt(timer, &foc_loop);
  timerAlarm(timer, 100, true, 0);
 
  Serial.println("Go!");
}

void loop() {
}

Library version:

SimpleFOC: 2.3.5
IDF: v5.5.1-710-g8410210c9a
plarform-espresif32: 55.03.34

Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1).
Sounds like it’s due to how long the update takes to execute. See if you can find how to disable the watchdog.

Although if you’ll be needing any other interrupts, then it could be a problem to block for so long. Something more like multi-threading would be better in that case, so the timer activates the SimpleFOC thread and re-enables interrupts, and the SimpleFOC thread relinquishes control after it finishes updating.

Maybe, as @dekutree64 said, the update loop takes too long. The I2C sensor is very slow, and this might be the issue.

Did you try to set a lower update frequency, going to 1kHz for example

  Serial.println("RT timer attach");
  timer = timerBegin(1000000);
  timerAttachInterrupt(timer, &foc_loop);
  timerAlarm(timer, 1000, true, 0); //1khz ( default is 100us or 10Khz )

Actually I think the issue is with the interrupts being called during the execution. I just tried the code and had the same issue.

Do this instead

void ARDUINO_ISR_ATTR foc_loop() {
  noInterrupts();
  motor.loopFOC();
  motor.move();
  interrupts();
}

This should solve it

I ended up going a multi-core route. The SimpleFOC loop task is pinned to core 0, and the main loop runs on core 1. IIRC core 0 also runs the WiFi code, but hopefully I will not need to use network connectivity.

With this setup adding time-consuming code to the main loop does not affect timing of the RT components.

Here’s the code of my test program:

#include <SimpleFOC.h>

TwoWire I2Cone = TwoWire(0);
MagneticSensorI2C sensor = MagneticSensorI2C(AS5600_I2C);
BLDCMotor motor = BLDCMotor(11);
BLDCDriver3PWM driver = BLDCDriver3PWM(32,33,25,22);

TaskHandle_t Task0;

void Task0code(void* pvParameters) 
{
  Serial.print("Task0 running on core ");
  Serial.println(xPortGetCoreID());
  for(;;) 
  {
    sensor.update();
    motor.loopFOC();
    motor.move();
    // vTaskDelay(1);
  }
}

void setup() {
  Serial.begin(115200);
  
  Serial.println("SimpleFOC multi-task test");

  Serial.print("Task running on core ");
  Serial.println(xPortGetCoreID());
  
  I2Cone.begin(19, 18, 400000); 
  sensor.init(&I2Cone);
  motor.linkSensor(&sensor);

  driver.voltage_power_supply = 12;
  driver.init();
  motor.linkDriver(&driver);

  motor.foc_modulation = FOCModulationType::SpaceVectorPWM;
  motor.controller = MotionControlType::velocity;

  motor.PID_velocity.output_ramp = 1000;
  motor.voltage_limit = 6.0f;
  motor.LPF_velocity.Tf = 0.01f;
  motor.P_angle.P = 20.0;

  motor.PID_velocity.P = 0.45;
  motor.PID_velocity.I = 28;
  motor.PID_velocity.D = 0.001;


  motor.init();
  motor.initFOC();

  Serial.println("Delay");
  _delay(1000);

  xTaskCreatePinnedToCore(Task0code, "Task0", 10000, NULL, 1, &Task0, 0);

  motor.monitor_variables = _MON_TARGET | _MON_VOLT_Q| _MON_VOLT_D| _MON_CURR_Q| _MON_CURR_D|  _MON_VEL| _MON_ANGLE;
  motor.useMonitoring(Serial);
  motor.monitor_downsample = 1;

}

int vel_change_cnt_max = 200;
int vel_change_cnt = 0;

int vel_idx = 0;
float vel_values[] = {6, 15, 25, 50};


void loop() {
  if (++vel_change_cnt >= vel_change_cnt_max)
  {
    vel_change_cnt = 0;
    vel_idx = (vel_idx + 1) % 4;
    motor.target = vel_values[vel_idx];
  }
  
  motor.monitor();
}