Skip to content

uPesy ships directly only in France !

uPesy products available on Amazon for EU

Contents Menu Expand Light mode Dark mode Auto light/dark mode

Using ESP32 timers in Arduino code

(Updated at 02/02/2023)

In this article, we will explore the operation of a timer on the ESP32 using Arduino code. We’ll look at how to set up and use a timer effectively, using practical examples to help you understand the basic concepts. We will discover the steps to configure a timer on the ESP32 with the critical parameters for optimal operation. Here we go. 😊

How do ESP32 timers work?

This article will not cover the timer’s theoretical workings to keep it manageable. If you are a beginner and need to learn how a timer works, read the theoretical article on how it works . It will allow you to understand better how to choose the parameters’ values to use in your Arduino code.

Configuring and using an ESP32 timer with Arduino code

Here is the minimal skeleton code to use a timer on the ESP32 with Arduino code. It allows you to trigger an interrupt when the timer reaches a threshold value, commonly called autoreload .

hw_timer_t * timer = NULL;

void IRAM_ATTR timer_isr() {
    // This code will be executed every 1000 ticks, 1ms

void setup() {
  uint8_t timer_id = 0;
  uint16_t prescaler = 80; // Between 0 and 65 535
  int threshold = 1000000; // 64 bits value (limited to int size of 32bits)

  timer = timerBegin(timer_id, prescaler, true);
  timerAttachInterrupt(timer, &timer_isr, true);
  timerAlarmWrite(timer, threshold, true);

void loop() {

Timer selection and basic configuration

We define an object hw_timer_t object outside the setup() function to be able to access it from different functions. The function timerBegin(uint8_t id, uint16_t prescaler, bool countUp) allows to configure the timer :

The ESP32 has 4 independent timers, selected by an id between 0 and 3. Then we select the prescaler to apply to the timer clock signal. On the ESP32, this is the APB_CLK clock, clocked at 80 MHz.


If you use external libraries in your code, they may use timers. In this case, you must be careful not to choose one they already use; otherwise, your program will undoubtedly have bugs!

In most examples available, a prescaler of 80 is used to obtain a counting period of 1 µs. From a period of the timer in microseconds, we can directly know the value of the corresponding autoreload without any complicated calculations:

\[autoreload = T_{timer}\times10^{6}\space ou \space T_{timer} = autoreload\times10^{-6}\]

The argument countUp argument specifies the direction we want to count: true in ascending order, false in descending order.

Configuring the alarm and triggering an interrupt routine

We attach an interrupt to the timer, which is triggered every time the threshold value is exceeded with timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge) .

The central argument must be the name of the interrupt routine to be executed (here, timer_isr() ). You can also choose whether the interrupt is to be triggered on the rising or fall in select: we generally prefer the increasing edge for timers.

The threshold value of the counter is defined by timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload) . We activate the mode autoreload mode by setting autoreload to true . As soon as the timer has exceeded the value alarm_value value, it starts counting again from zero, and the interrupt function is triggered.


With a prescaler of 80, the value of alarm_value corresponds directly to the global period of the timer in microseconds.

Once the timer is fully configured, you can enable it with its alarm using timerAlarmEnable(timer) .

If we run this code, the function timer_isr() will be executed every second. Nothing will happen because the function is empty in this “skeleton” code. I suggest a version that flashes the blue LED of the ESP32 on pin GPIO2 .

Increment a variable and generate flags

The interrupt routine must be executed as quickly as possible to avoid disturbing the main program. So we often use flags that change state (usually a boolean: true or false ) in the isr . These changes are then processed in the main loop:

hw_timer_t * timer = NULL;
volatile boolean tick_flags = false;

void IRAM_ATTR timer_isr() {
  tick_flags = true;

void setup() {

  uint8_t timer_id = 0;
  uint16_t prescaler = 80;

This section is available to premium members only. You still have 76% to discover.

Subscribe for only 5$/month

Already subscribed? Sign in

Advanced use of ESP32 timers in Arduino code

Manage read/write access to a shared variable using semaphores

It is good practice to frame the interrupt code of a timer in a semaphore to manage access to global variables. This helps predict how the program will behave if the interrupt routine and the main program want to simultaneously write to the same variable. Since the Arduino code for ESP32 uses freeRTOS as its real-time OS, the semaphores are already integrated: we need to use them directly in our program:

This section is available to premium members only. You still have 93% to discover.

Subscribe for only 5$/month

Already subscribed? Sign in