XIAO ESP32C2 ~5 ms pause/freeze every 2 seconds

Ok , so looking into this for S3 here is what we come up with, I tested the code snip and it does pin the task to that core, see the serial port.
:+1:

the ESP32-S3 is a much better fit for real-time stepper motor control than the ESP32-C6, especially when you’re trying to avoid jitter in bit-banged or timing-sensitive applications like step pulses.


:white_check_mark: Why the ESP32-S3 is Better:

:brain: 1. Dual Cores (Real Use)

  • The S3 has two application cores (both Xtensa):
    • Core 0 often runs system/background tasks
    • Core 1 can be used exclusively for real-time user code (e.g. stepping)
  • You can pin your stepper control loop to Core 1 with xTaskCreatePinnedToCore() so it avoids scheduler interference from FreeRTOS system tasks.

:joystick: 2. More Stable Bit-Banging

  • Since you can reserve one core for timing-critical tasks, bit-banging becomes much more stable. You’re less likely to see those periodic hiccups or timing jitter.
  • The S3 also has higher GPIO throughput, making gpio_set_level() or digitalWriteFast() style calls quicker and more consistent. Check out the “TouchRing” BLE demo you can see visually the performance difference between the two Identical systems with only the MCU are C6 and S3 The S3 is Visually :face_with_monocle:FASTER , Much Faster too!

:gear: 3. Advanced PWM/Timer Hardware

  • The S3 has:
    • LEDC for PWM (up to 8 high-speed channels)
    • MCPWM which is excellent for stepper and servo control
    • General-purpose hardware timers usable via esp_timer or timer_group

Even if you don’t use these at first, they’re available if you decide to optimize later.

:signal_strength: 4. Peripheral Compatibility

  • AccelStepper + ESP32-S3 + LEDC/MCPWM works more reliably and has wider community support than on the ESP32-C6.
  • Even libraries that don’t support hardware timers out of the box are easier to patch or replace on the S3

:green_circle: Bottom Line:

For stepper motor control where consistent, jitter-free pulses are required, and you want to avoid LEDC/MCPWM hacks or limitations, ESP32-S3 is the better choice.

It gives you more timing control, more hardware resources, and the ability to dedicate a full core to your motion loop.

  • FreeRTOS tasks, even without radios, still affect jitter.
  • No MCPWM module on C6; only LEDC and software timers.

Here’s a working AccelStepper + ESP32-S3 example that uses xTaskCreatePinnedToCore() to run the motor step loop on Core 1, ensuring better timing stability:

// Stepper Core1 Task Demo for ESP32-S3
// ============================
// Uses AccelStepper with DRV8825 (or similar driver)
// Ensures stepper update runs on Core 1 for jitter-free control

#include <AccelStepper.h>

#define STEP_PIN  9   // Change to your actual STEP pin
#define DIR_PIN   8   // Change to your actual DIR pin

AccelStepper stepper(AccelStepper::DRIVER, STEP_PIN, DIR_PIN);

TaskHandle_t stepperTaskHandle = NULL;

void setup() {
  Serial.begin(115200);
  delay(500);
  Serial.println("\n=== AccelStepper Core1 Demo ===");

  stepper.setMaxSpeed(1000);   // steps/sec
  stepper.setAcceleration(200); // steps/sec^2
  stepper.moveTo(2000);         // target position

  // Create task pinned to Core 1 (ESP32-S3 has two Xtensa cores: 0 and 1)
  xTaskCreatePinnedToCore(
    stepperTask,     // task function
    "StepperTask",   // name
    4096,            // stack size
    NULL,            // param
    1,               // priority
    &stepperTaskHandle,
    1                // Core 1
  );
}

void loop() {
  // Main core (Core 0) can do other things
  Serial.printf("Main loop running on core %d\n", xPortGetCoreID());
  delay(1000);
}

void stepperTask(void *parameter) {
  Serial.printf("Stepper task running on core %d\n", xPortGetCoreID());

  while (true) {
    stepper.run();  // continuously updates motor position
    delayMicroseconds(200); // fine-tune delay to smooth motion
  }
}

Notes:

  • You’ll see in the Serial output which core each task is running on.
  • The stepper.run() function must be called as frequently as possible to ensure smooth motion.
  • You can use runToNewPosition() or runSpeed() in the loop depending on your use case.

With this, the motor should run smoothly without periodic hiccups, even if the main loop is busy or FreeRTOS background tasks trigger on Core 0. Give it a go and let us know how you make out…

I use the C6 but prefer the S3 and Now the PLUS version really kicks it up a Notch :+1:

a lot to digest but should get you there.

HTH
GL :slight_smile: PJ :v: