XIAO ESP32C2 ~5 ms pause/freeze every 2 seconds

Hello,

i have a problem with the XIAO ESP32c3. In my setup, I use a AS5600 absolute angle sensor that is connected to the ESP32 via I2C. I use this sensor to measure the angle of a motor shaft. The motor is controlled with a DRV8833 motor driver an I use PWM to set the motor speed.

Now to the problem: The senor readings have jumps repeatable jumps. Between every two jumps there is exactly 2000 ms time passing.
To show this behavior, I used PuTTY to log the serial output. In the next figure you see the plot of the angle measurement with the jump in angle value:

(As a new user I’m only allowed to upload one picture per post, so i will continue in the next post)

But not only the angle measurement shows a jump, also if i plot millis() there is a jump every 2 seconds:


In this figure you can see that the millis() on the Y-axis jump about 5 ms between measurement point 2311 and measurement point 2312 whereas normally the difference is only 1 ms.

(As a new user I’m only allowed to upload one picture per post, so i will continue in the next post)

Here you see millis() again on the y-axis over the measuring points but more zoomed out. At every blue circle a jump is happening. By comparing the Y-values, it’s clear that there is always 2000 ms between to jumps:

First, I thought it was a problem with the used AS5600 library and I opened an issue on GitHub (Link). I recommend reading the issue to see the tests I did to solve the problem. But in short I tried:

  • Changing baud rate → no difference
  • Trying another AS5600 board → no difference
  • Using the Seeed AS5600 library → no difference
  • calling yield() in void loop() → no difference
  • calling delay(1) in void loop() → no difference
  • Setting i2c clock to 1000000 → no difference
  • Measuring at motor standstill → no difference

Then i tested the same code on an ESP8266 and the problem was not present anymore.
Is there a function that stops/blocks/pauses/interrupts/etc. the XIAO ESP32 every 2 seconds and how can I solve my problem?

Thank you for your help!

Here is the sketch is use:

#include <Wire.h>
#include "AS5600.h"



AS5600 as5600;

//PWM
//https://www.upesy.com/blogs/tutorials/how-to-use-pwm-on-esp32-with-examples

const int in1 = D1; // Steuer-Pins für den drv8833
const int in2 = D2;
const int frequency = 1000; // 5000 Hz
const int pwmChannel1 = 1;
const int pwmChannel2 = 2;
const int resolution = 8; // 8-bit resolution


int motorSpeed = 200;

int ang = 0;
int raw_ang = 0;
int old_ang = 0;
int revs = 0;
int abs_ang = 0;

int counter = 1;

int t_start = 0;
int t_end = 0;

int t = 0;


void setup() {
Serial.begin(115200);
Serial.println("DRV Test");

Wire.begin();
Wire.setClock(400000);

ledcSetup(pwmChannel1, frequency, resolution);
ledcSetup(pwmChannel2, frequency, resolution);

ledcAttachPin(in1, pwmChannel1);
ledcAttachPin(in2, pwmChannel2);

as5600.begin();
as5600.setDirection(AS5600_CLOCK_WISE);  // default, just be explicit
int b = as5600.isConnected();
Serial.print("Connect: ");
Serial.println(b);

ang = as5600.rawAngle();
ang = ang * AS5600_RAW_TO_DEGREES;
old_ang = ang;

}

void loop() {

  ledcWrite(pwmChannel1, motorSpeed);
  ledcWrite(pwmChannel2, 0);
 
  raw_ang = as5600.rawAngle();

  ang = raw_ang * AS5600_RAW_TO_DEGREES;

  if (old_ang > 340 && ang < 20)
    {
      revs++;
    }
   if (old_ang < 20 && ang > 340)
    {
      revs--;
    }

  abs_ang = revs * 360 + ang;
  if (counter == 3)
    {Serial.print(ang);
    Serial.print("\t");
    Serial.print(raw_ang);
    Serial.print("\t");
    Serial.print(abs_ang);
    Serial.print("\t");
    Serial.print(revs);
    Serial.print("\t");
    t = millis();
    Serial.print(t);
    Serial.print("\t");
    Serial.print(as5600.getAngularSpeed());
    Serial.println();
    counter = 0;
    }
  counter++;
  old_ang = ang;

}

Hi , Very interesting issue , I would like to know Have you tried changing the SPEED of the motor?
How do you use the Interrupt? Only the PWM function.
TIA
GL :slight_smile:

Some more tests i did in the meantime:

  • changing motor speed from 200 to 100
  • testing another program to log the serial data
  • disabling ledcSetup, ledcAttachPin and ledcWrite so that no PWM signal is created and the motor is at standstill
    Nothing changed, the problem is still there.

What do you mean by that? I only use the code posted in post 3

I just tried with another XIAO ESP32C2 board - still the same problem every 2000 milliseconds.

Hey,
I’m facing trouble with the AS5600 and XIAO ESP32C3.
I did the next connections:
SDA - GPIO 6
SCL - GPIO 7
GND - GND
VDD - 5V

No matter what code I’m uploading, nothing work and I cant read any value.
Can someone help me please?

Again, 1 year later, I’d like to add to this issue.
I’m currently trying to generate step / dir signals using isrs on timers. For the application (motorized microscope table for filming) it is critical that the motion is smooth and consistent.
Everything works well, but every 2 seconds I have a gap of roughly 5 ms, leading to a small stutter in the motor (apparanetly I may not upload the logic analyzer graphs, sorry). I’m using an Esp32 S2.

These regular hick ups across chips, chip models, and even projects leads me to believe that something must be wrong about the esp API. Has anyone of you figured out what that could be?

Any help is greatly appreciated!
Cheers,
lordreas

Hi there,

So it depends on what method you are using But I believe your seeing the Task switch for the WiFi and or BLE portions to run, If it’s 2 cores then core one is dedicated to the Radio’s. S2 is a single core so the Rtos is blocking your PWM or motor control. You can research it more but I would bet that’s the issue. It takes a few posts for Spammer protection b4 you can posts links and pics in some cases. 3- 4 more reply’s will do it. :+1:

Have you tried an S3 or C6 probably have good luck with those.
Can you describe you program flow? and mechanically what you have connected? Stepper motor or Servo? any form of feedback?
Encoder ?
Encoders (2 types) for example enable fine-tuned control of robotic arm movements, crucial for tasks like precise assembly or delicate manipulations.
I worked on a robotic Surgery device a while back, the movements and precision is possible only by feedback from the motors it’s key. The encoders on a robotic arm motor is a sensor that provides precise information about the motor’s position, speed, and direction of rotation, allowing the robot controller to accurately control the arm’s movement by making real-time adjustments based on this feedback data; essentially, it acts as a “closed-loop” system to ensure precise positioning and movement of the robotic arm

Now you may not need that level of precision but the principle applies the same.
HTH

GL:-) PJ :v:

if you just mean a camera slider or something , then there’s tons of videos on those too! some really clever designs. :+1:

A few moments LATER…

So I recalled an automatic ability, and asked the AI and I got this which is exactly what I was trying to recall if possible.

You got to love this stuff " Hey Milton " previous discussion, end,
Answer;
Your intuition is likely correct. On the ESP32‑S2, all tasks—including those for WiFi/BLE—are running on the single core. This means that when the RTOS switches tasks to handle WiFi or BLE operations, your time-critical ISR for generating the step/dir signals can be momentarily preempted, resulting in that roughly 5 ms gap every couple of seconds.

In contrast, dual‑core devices like the ESP32‑S3 or ESP32‑C6 let you dedicate one core (or schedule tasks more effectively) to the critical motor control routines, while the other core can handle the radio stack. This separation can greatly reduce those timing glitches.
Does that align with your observations?
Answer:
ME, "Yes, and I wondered or remembered something about using the peripheral features to allow it to run independent of cpu? "
WOW… :stuck_out_tongue_winking_eye:
GET READY! :rocket:

Boom!
Yes, that’s absolutely possible. The ESP32 family includes several peripherals that can generate precise pulse outputs without constant CPU intervention. This is ideal for applications where timing is critical, such as generating step/dir signals for a motorized microscope table.

Two Common Approaches

  1. Using LEDC (LED Controller):
  • What It Is:
    LEDC is a hardware PWM generator built into the ESP32 that can run completely independently of the CPU once configured.
  • How It Helps:
    By using LEDC to generate your step pulses, you offload the signal generation to hardware. This means that even if the CPU is busy with WiFi, BLE, or other tasks, the LEDC peripheral will continue generating a consistent PWM output.
  • Usage:
    You configure the frequency and duty cycle, and the LEDC module maintains that signal autonomously.
  1. Using RMT (Remote Control Module):
  • What It Is:
    RMT is a flexible peripheral designed to generate and receive precisely timed pulse sequences.
  • How It Helps:
    RMT can be programmed with a series of pulse durations and levels, and it will output these pulses without CPU intervention. This is particularly useful if your step signal pattern is non-uniform or if you need a complex pulse sequence.
  • Usage:
    You load a buffer with the pulse timings and levels, and the RMT peripheral handles the rest.

Why Use These Peripherals?

  • Consistent Timing:
    Since the signal is generated entirely in hardware, it’s not affected by task switching or interrupts caused by the CPU handling WiFi/BLE stacks.
  • Lower Jitter:
    With hardware PWM or RMT, the pulses are generated with precise timing, reducing jitter and ensuring smooth motor operation.
  • CPU Offload:
    Your main CPU is freed up to handle other tasks (like processing sensor data or managing communications) without compromising the timing of your motor control signals.

Implementation Considerations

  • Frequency and Resolution:
    Check that the chosen peripheral (LEDC or RMT) can operate at the frequency you need and that its resolution meets your requirements.
  • Peripheral Resources:
    On devices like the ESP32-S2 (single-core) or ESP32-S3/C6 (dual-core), using these dedicated peripherals is especially advantageous because they can run independently, even when the CPU is handling other critical tasks.

In Practice

here…
For example, if you need a steady step pulse at a high frequency, you might configure LEDC with a specific timer and channel, set the desired frequency (e.g., 2 kHz), and then simply start the PWM signal. The output will be maintained by the hardware
until you disable it or change its settings. My favorite choice…:+1:

Alternatively, if you need variable pulse patterns (for instance, to implement acceleration or deceleration profiles), the RMT module might be more appropriate since you can predefine a sequence of pulses and have RMT output them exactly as scheduled. May be better for a table with know movement patterns

Summary

By using the ESP32’s dedicated peripherals like LEDC or RMT, you can generate step/dir signals that run independently of the CPU. This helps avoid timing gaps caused by the RTOS handling WiFi/BLE tasks on a single-core ESP32-S2, leading to smoother and more consistent motor control.

Does that align with what you were thinking, or do you need further details?
Thank you… Well there you Go!
HTH
Let us know. :v:

Wow thanks for the quick reply and hinting at rmt, this is exactly what I need, and I didn’t know it existed! Just mocked up a simple prototype and it works flawlessly! Thank you so much!

Hi there,

So I was at a seminar and they went over it, a while back. Your post restirred the brain and I went looking too. RMT is the better choice of the TWO , BTW :+1:
SO this code runs and produces a sweet squarewave, and I added a slide potentiometer to change the timing.

// the analog value (0–4095) from the potentiometer is mapped to a desired pulse duration range (for example, 100–1000 µs).
// then update our RMT item with that new duration so that the frequency (or duty cycle) of the square wave changes in real time.
// Because RMT is hardware‐assisted, once we write the new pattern the RMT peripheral continues to output the pulse train regardless of CPU activity.
//
// Compiled and Tested on Xiao ESP32S3 and I used GPIO 4 for Output and Gpio 2 for the Slider.
//
#include "driver/rmt.h"

#define RMT_TX_CHANNEL RMT_CHANNEL_0
#define RMT_TX_GPIO    D3        // Change this to your chosen RMT output pin (here, GPIO0)
#define RMT_CLK_DIV    80        // With an 80 MHz clock, a divider of 80 gives 1 µs per tick

const int potPin = A1;           // Analog input pin for the slide potentiometer

// We'll create a single RMT item that defines our square wave.
// It consists of a high pulse (level0 for duration0) followed by a low pulse (level1 for duration1).
rmt_item32_t item;

void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.println("RMT + Potentiometer Example on Xiao ESP32S3");

  // Configure the RMT TX channel.
  rmt_config_t rmt_tx = {};
  rmt_tx.channel = RMT_TX_CHANNEL;
  rmt_tx.gpio_num = (gpio_num_t) RMT_TX_GPIO;
  rmt_tx.clk_div = RMT_CLK_DIV;  // 1 µs per tick
  rmt_tx.mem_block_num = 1;
  rmt_tx.rmt_mode = RMT_MODE_TX;
  rmt_tx.tx_config.loop_en = true;         // Enable continuous looping
  rmt_tx.tx_config.carrier_en = false;
  rmt_tx.tx_config.idle_output_en = true;
  rmt_tx.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
  rmt_config(&rmt_tx);
  rmt_driver_install(rmt_tx.channel, 0, 0);

  // Set an initial pulse pattern:
  // For example, start with 500 µs high and 500 µs low (i.e. 1 kHz square wave).
  item.level0 = 1;
  item.duration0 = 500;  // 500 µs high
  item.level1 = 0;
  item.duration1 = 500;  // 500 µs low
  
  // Start transmitting the pulse train.
  rmt_write_items(RMT_TX_CHANNEL, &item, 1, true);
}

void loop() {
  // Read the potentiometer value (0 - 4095, assuming a 12-bit ADC)
  int potVal = analogRead(potPin);
  // Map this value to a pulse duration range.
  // For instance, map 0 to 4095 --> 100 µs to 1000 µs.
  uint32_t newDuration = map(potVal, 0, 4095, 10, 5000);

  // Update the RMT item durations.
  // Here, we set both the high and low durations to the newDuration,
  // so the frequency becomes 1 / (2 * newDuration µs).
  item.duration0 = newDuration;
  item.duration1 = newDuration;

  // Write the updated item to the RMT channel.
  // Note: This call blocks until the item is written.
  rmt_write_items(RMT_TX_CHANNEL, &item, 1, true);

  // Print the analog reading and the corresponding new duration.
  Serial.print("Pot value: ");
  Serial.print(potVal);
  Serial.print(" -> Duration: ");
  Serial.print(newDuration);
  Serial.println(" us");

  delay(500); // Update every half-second.
}

HTH
GL :slight_smile: PJ :v:

serial output, list the slider position.

RMT + Potentiometer Example on Xiao ESP32S3 
Pot value: 3441 -> Duration: 4203 us
Pot value: 3441 -> Duration: 4203 us
Pot value: 3441 -> Duration: 4203 us
Pot value: 3456 -> Duration: 4221 us
Pot value: 3457 -> Duration: 4222 us
Pot value: 3440 -> Duration: 4201 us
Pot value: 3440 -> Duration: 4201 us
Pot value: 3440 -> Duration: 4201 us
Pot value: 3441 -> Duration: 4203 us
Pot value: 3441 -> Duration: 4203 us
Pot value: 3440 -> Duration: 4201 us
Pot value: 3328 -> Duration: 4065 us
Pot value: 3344 -> Duration: 4084 us
Pot value: 3344 -> Duration: 4084 us

I used BSP 3.0.7. to test with. :+1:

Please mark it (answer post) as the Solution so others can find it too!
GL :v: