Attitude Estimation· Live
Home / Chapter 1 / 1.5

ESP32 (Arduino IDE): a fixed-rate loop with micros()

Lesson 1.5 · ESP32 · free

Video coming soon

On the ESP32, the loop keeps its own time: micros() gives microseconds since start, and every 20000 µs (fifty times per second) we do our work. We then add the period to the last start time instead of reading micros() again, so small delays never add up.

In this lesson · 3 steps
  1. What goes where (as in the video)
  2. Run the loop at SAMPLE_RATE_HZ
  3. Check the rate in the serial plotter

1What goes where (as in the video)

  1. Top of the sketch: the rate, #define SAMPLE_RATE_HZ 50
  2. Below it: the period, last_sample and the counter (delete the old counter line)
  3. In setup: last_sample = micros();, and no more delay(1000)
Why?
In the video we paste the code in three parts into the 1.4 sketch, and keep the hello line. The code below replaces the whole sketch in one go; the result is the same.

2Run the loop at SAMPLE_RATE_HZ

ch1_serial.ino · replace everything

micros() minus last_sample is the time since the last run; when it reaches the period, we run the code.

#define SAMPLE_RATE_HZ 50

const unsigned long SAMPLE_PERIOD_US = 1000000UL / SAMPLE_RATE_HZ;
unsigned long last_sample = 0;
int counter = 0;

void setup()
{
  Serial.begin(115200);
  last_sample = micros();
}

void loop()
{
  if (micros() - last_sample >= SAMPLE_PERIOD_US)
  {
    last_sample += SAMPLE_PERIOD_US;
    counter++;
    Serial.print("counter:");
    Serial.print(counter);
    Serial.print(",time:");
    Serial.println(millis());
  }
}
Why?
The period is one million microseconds divided by fifty: 20000 µs, or 20 ms. Then we add the period to last_sample instead of reading micros() again: each check is a little late, and reading micros() again would move the schedule, but adding the period keeps it fixed. There is no delay: the loop never waits, it just checks the time again and again.

3Check the rate in the serial plotter

  1. Upload; in the serial plotter press Pause / Clear if it still shows old data, then Connect and choose the port
  2. Two lines, counter and time; time goes up by 20 on every line → 50 lines per second
Why?
time is in milliseconds and grows much faster than the counter, so next to it the counter looks flat. In the console, time goes up by 20 on each line: fifty lines per second.
Check & troubleshoot

What you should see

  • Lines like counter:226,time:4520
  • time goes up by 20 on every line: fifty lines per second
  • On the plot, the counter looks flat because time grows much faster: that is normal

If it doesn't work

  • Rate slowly drifts: use last_sample += SAMPLE_PERIOD_US, not last_sample = micros()
  • Nothing printed: close the Serial Monitor, connect the plotter, check 115200 baud
  • The hello line and the 1-second delay from 1.4 are gone: that is expected, because this step replaces the whole sketch
  • After about 71 minutes micros() starts again from zero: the subtraction micros() - last_sample still works, so the loop keeps its rate

Good to know

  • Why a fixed rate? Later we turn gyro readings into an angle by adding up many small steps, so we must know the time between two readings. If the timing is wrong, the angle slowly drifts
  • For example, if one run starts 300 µs late, the next deadline is still on the 20 ms grid, so the error does not add up

Next: Chapter 2: we connect our first sensor, the IMU, over I2C.