ESP32-C6 strange spikes in loop execution time

Hi guys!

I’ve been investigating ESP32C6 recently. I’ve got one of th seedstudios XIAO boards.

And I’ve been encountering a strange issue, there are huge spikes in current measurement, velocity measurement and everything else. The motor stumbles each couple of seconds (2-3).

I tried to get to the bottom of this issue and it seems that it does not come from SimpleFOC. I was able to reproduce it even by just running an empty loop and measuring the time.

#include <Arduino.h>

void setup() {
  Serial.begin(115200);
  Serial.println("Hello, world!");
}

uint32_t last_time = 0;
uint32_t counter = 0;
uint32_t dtmax = 0;
void loop() {

    auto dt = micros() - last_time;
    last_time = micros();
    if (dtmax < dt ) dtmax = dt;

    if (counter++ > 100) {
        Serial.print(">dt: ");
        Serial.println(dtmax);
        dtmax = 0;
        counter = 0;
    }
}

And I’ve got this:

(VIsualised with teleplot.fr)

The loop execution time goes from 30us to 5ms (5000us) for one brief moment.

Did someone have the same issue, can someone reproduce it with the code above?
Do you guys know where this could be coming from?

I tried to disable bluetooth and wifi but I still get the same issue.
btStop() and WiFi.mode(WIFI_OFF) and this did not change anything.

Basically this jitter makes the board unusable for FOC at the moment!!

Ok I found a solution or a workaround…
Seems like for c6 using loop is not a good option for SimpleFOC. If I use a higher priority task I dont have jitter any more:

#include <Arduino.h>

// Global variables for tracking
volatile uint32_t dtmax = 0;
uint32_t last_time = 0;
uint32_t counter = 0;

// This function will run as a high-priority task
void highPriorityTask(void *pvParameters) {
  last_time = micros();
  
  while (1) {
    uint32_t now = micros();
    uint32_t dt = now - last_time;
    last_time = now;

    // Track the maximum jitter/delay encountered
    if (dt > dtmax) {
      dtmax = dt;
    }

    // Every ~2000 iterations, print the result
    // We use a larger counter so Serial doesn't become the bottleneck
    if (counter++ > 2000) {
      Serial.print(">dt_max_us:");
      Serial.println(dtmax);
      dtmax = 0;
      counter = 0;
    }

    // Critical: Tell FreeRTOS to allow other tasks to breathe for 1 tick
    if(counter % 100 == 0) {
        vPortYield(); // can be long...
    }
  }
}

void setup() {
  // Use a high baud rate to minimize Serial buffer delays
  Serial.begin(921600); 
  delay(1000);

  // Create the task:
  // Name: "FOC_Control", Stack: 4096, Priority: 24 (High)
  xTaskCreate(
    highPriorityTask, 
    "FOC_Control",    
    4096,             
    NULL,             
    24,               
    NULL              
  );
}

void loop() {
  // The standard loop is now empty. 
  // We put it to sleep forever so it doesn't interfere.
  vTaskDelay(portMAX_DELAY);
}

Example simplefoc code

#include <Arduino.h>
#include <SimpleFOC.h>


// BLDC motor & driver instance
BLDCMotor motor = BLDCMotor(7);
BLDCDriver3PWM driver = BLDCDriver3PWM(M1, M2, M3); // PWM pins for nucleo QtPyFOC board

// sensor instance
MagneticSensorSPI sensor = MagneticSensorSPI(AS5048_SPI, CS, 10000000); // RX pin for esp32s3 QtPyFOC board

// inline current sensor instance
LowsideCurrentSense current_sense = LowsideCurrentSense(150.0f,  CS1, CS2);

// commander communication instance
Commander command = Commander(Serial);
void doMotor(char* cmd){ command.motor(&motor, cmd); }

// This function will run as a high-priority task
void highPriorityTask(void *pvParameters) {

  uint32_t counter = 0;
  while (1) {
    
    motor.loopFOC();
    motor.move();
    // monitoring
    motor.monitor();
    // user communication
    command.run();

    // Critical: Tell FreeRTOS to allow other tasks to breathe for 1 tick
    if(counter++ >  100) {
        vPortYield(); 
        counter = 0;
    }
  }
}


void setup() {
  // use monitoring with serial 
  Serial.begin(115200);
  // enable more verbose output for debugging
  // comment out if not needed
  SimpleFOCDebug::enable(&Serial);

  sensor.init();

  _delay(5000); // wait for the serial to be ready

  // initialise magnetic sensor hardware
  sensor.init();
  // link sensor to the motor
  motor.linkSensor(&sensor);

  // driver config
  // power supply voltage [V]
  driver.voltage_power_supply = 20;
  driver.voltage_limit = 20; // voltage limit [V]
  driver.pwm_frequency = 18000; // 18kHz
  driver.init();
  // link drivers
  motor.linkDriver(&driver);
  // link current sense and the driver
  current_sense.linkDriver(&driver)

  motor.PID_velocity.P = 0.05;
  motor.PID_velocity.I = 1;

  // set control loop type to be used
  motor.controller = MotionControlType::torque;
  motor.torque_controller = TorqueControlType::voltage;

  // default voltage_power_supply
  motor.voltage_limit = 12; // [V]
  // default current limit
  motor.current_limit = 2.0; // [A]

  motor.voltage_sensor_align = 5.0; // [V]

  current_sense.skip_align = true; // enable current sense alignment
  // current sense init and linking
  motor.linkCurrentSense(&current_sense);

  // comment out if not needed
  motor.useMonitoring(Serial);

  command.verbose = VerboseMode::machine_readable; // can be set using the webcontroller - optional
  
  // initialise motor
  motor.init();
  current_sense.init();
  
  // align encoder and start FOC
  motor.initFOC();

  // set the inital target value
  motor.target = 0;

  // subscribe motor to the commander
  command.add('M', doMotor, "motor");
  
  // Run user commands to configure and the motor (find the full command list in docs.simplefoc.com)
  Serial.println("Motor ready.");

  _delay(1000);


  // Create the task:
  // Name: "FOC_Control", Stack: 4096, Priority: 24 (High)
  xTaskCreate(
    highPriorityTask, 
    "FOC_Control",    
    4096,             
    NULL,             
    24,               
    NULL              
  );
}

void loop() {

  delay(20); // do nothing basically

}```

This combination works for me without jitters. So if anyone encounters the same issue, this would be one solution. 

But i'd still be happy to discuss this more and see if there are more elegant solutions or should we expect this on other ESP32 boards.