Having posted my initial message above, it made me think more clearly about the processes employed by the library. It seems I was overthinking the functions and callbacks. As a result, simplifying my code has made the ISR's work as they should. @qubits-us , thanks for your code, I will look at that… Answer from a_pemberton on forum.arduino.cc
🌐
Espressif
docs.espressif.com › projects › esp-idf › en › latest › esp32 › api-reference › system › esp_timer.html
ESP Timer (High Resolution Timer) - ESP32 - — ESP-IDF Programming Guide latest documentation
Timer represented by timer should not be running when this function is called. This function starts the timer which will trigger every period microseconds. The first alarm will be triggered at first_alarm_us time.
🌐
ESP32 Forum
esp32.com › viewtopic.php
Fast timer for microsecond level duration measurements - ESP32 Forum
February 2, 2021 - There was a system variable indicating the CPU frequency to allow the translation of the counter reading into nano seconds or microseconds. ... #define initCycleCounter() \ CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; \ DWT->CYCCNT = 0; \ DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk #define readCycleCounter() (DWT->CYCCNT) I was missing similar functionality in ESP32 and created the following macros for ESP-IDF VSCE. ... #include "soc/frc_timer_reg.h" #ifdef CONFIG_ESP_TIMER_IMPL_FRC2 #define timer_u32() (REG_READ(0x3ff47004)); // FRC2 #elif CONFIG_ESP_TIMER_IMPL_TG0_LAC #define timer_u32() (REG_READ(0x3ff5F078)); // TG0_LAC #else #define timer_u32() (0); // SYSTIMER #endif The SYSTIMER is too slow (50 us) for the fast timing measurements.
Discussions

Need to have sub microsecond delay precision
Can this be solved using the RMT peripheral? More on reddit.com
🌐 r/esp32
17
3
July 29, 2020
ESP32 S3: sub-microsecond time sync and disciplined timers
Since you seem interested, there's a cool library called TicSync. I think it was developed by Meta. https://sci-hub.se/10.1109/icra.2011.5980112 It's a digital algorithm for aligning time for multiple devices. It works by sending data to devices over a digital connection, this could be wi-fi, Bluetooth, Light... etc. Due to the latency of the signal, the time from when a message is sent to when it's received often has a few ms of offset to it. The TicSync algorithm uses successive approximation to reduce this time to micro seconds after a few hundred samples, roughly 3 seconds. It's super sick what they did. The goal was aligning time series data from microphones, video, and accelerometers in the same room to trail ML algorithms. More on reddit.com
🌐 r/embedded
47
444
December 3, 2025
🌐
Espressif
docs.espressif.com › projects › esp-idf › en › v5.0 › esp32 › api-reference › system › esp_timer.html
High Resolution Timer (ESP Timer) - ESP32 - — ESP-IDF Programming Guide v5.0 documentation
esp_timer set of APIs provides one-shot and periodic timers, microsecond time resolution, and 64-bit range. Internally, esp_timer uses a 64-bit hardware timer, where the implementation depends on the target. LAC timer is used for ESP32.
🌐
Reddit
reddit.com › r/esp32 › need to have sub microsecond delay precision
r/esp32 on Reddit: Need to have sub microsecond delay precision
July 29, 2020 -

Hi guys,

I'm trying to implement something that will generate two triggers at regular intervals where the two triggers are separated by a configurable delay. The delay has to be configurable to sub microsecond resolution. Ideally, 500ns or less.

I chose the ESP32 because of its 240MHz clock, but it still seems to be having trouble.

I'm messing around with the following code:

#define cyclespermicro 240
#define microcycles(n) (n*cyclespermicro)

uint32_t startCounter, counter, cpu_cycles;
int cyclediff, totalcycles, corrected;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);

  startCounter = ESP.getCycleCount();
  totalcycles = startCounter + microcycles(2);
  //delayMicroseconds(1);

 while (ESP.getCycleCount() < totalcycles) {
        __asm__ __volatile__ ("nop");
 }
  
  counter = ESP.getCycleCount();
  cpu_cycles = counter - startCounter;
  corrected = cpu_cycles - 262;
  Serial.print("StartCounter: ");
  Serial.print(startCounter);
  Serial.print(" counter: ");
  Serial.print(counter);
  Serial.print(" cpu_cycles: ");
  Serial.print(cpu_cycles);
  Serial.print(" corrected: ");
  Serial.println(corrected);
}

void loop() {

}

The problem is that I get the same number of clock cycles for 1 us as I do for 2 us. I'm guessing this is because the while loop overhead is overwhelming the 1 us delay.

The problem is I need to have the amount of delay configurable and I don't know the best way to do this. Anyone have any ideas how to efficiently create configurable sub-microsecond delays?

EDIT: I am also noticing, when I get rid of the while loop entirely, and just straight up copy in NOP lines, it works as expected for like 3 NOPs, but once I hit 5 NOPS, the overhead jumps to like 500 cycles. What is going on here?

🌐
Espressif Docs
espressif-docs.readthedocs-hosted.com › projects › arduino-esp32 › en › latest › api › timer.html
Timer — Arduino-ESP32 2.0.14 documentation
*/ // Stop button is attached to PIN 0 (IO0) #define BTN_STOP_ALARM 0 hw_timer_t * timer = NULL; volatile SemaphoreHandle_t timerSemaphore; portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; volatile uint32_t isrCounter = 0; volatile uint32_t lastIsrAt = 0; void ARDUINO_ISR_ATTR onTimer(){ // Increment the counter and set the time of ISR portENTER_CRITICAL_ISR(&timerMux); isrCounter = isrCounter + 1; lastIsrAt = millis(); portEXIT_CRITICAL_ISR(&timerMux); // Give a semaphore that we can check in the loop xSemaphoreGiveFromISR(timerSemaphore, NULL); // It is safe to use digitalRead/Write her
🌐
GitHub
github.com › OliviliK › ESP32_timer_u32
GitHub - OliviliK/ESP32_timer_u32 · GitHub
The timer_u32.h file allows an application to use a read only timer for timing measurements done at and below 1 microsecond level.
Author   OliviliK
🌐
Arduino Forum
forum.arduino.cc › other hardware › hardware development
ESP32-S2 One Shot Microsecond non-blocking timer(s) - Hardware Development - Arduino Forum
September 23, 2023 - I am struggling with understanding the ESP32-S2 timer interrupt library(s). Every example I see requires the use of millis() but I need an accurate, 100uSec, non-blocking ISR driven timer. This is for display purposes wh…
Find elsewhere
🌐
CircuitDigest
circuitdigest.com › microcontroller-projects › esp32-timers-and-timer-interrupts
ESP32 Timers & Timer Interrupt Tutorial
August 5, 2025 - This is done with a call to the ESP32 attachInterrupt Function. In this example, we have attached the ISR function called onTimer to the timer interrupt. ... timerAlarmWrite function is used to specify the counter value at which the timer interrupt should be generated. So, for this example, we assume that we want to generate an interrupt each second, and thus we pass the value of 1000000 microseconds, which is equal to 1 second.
🌐
Espressif
docs.espressif.com › projects › esp-idf › en › v4.4-rc1 › esp32 › api-reference › system › esp_timer.html
High Resolution Timer - ESP32 - — ESP-IDF Programming Guide v4.4-rc1 documentation
esp_timer set of APIs provides one-shot and periodic timers, microsecond time resolution, and 64-bit range. Internally, esp_timer uses a 64-bit hardware timer, where the implemention depends on CONFIG_ESP_TIMER_IMPL. Available options are: ... The FRC2 is a legacy option for ESP32 until v4.2, ...
🌐
Espressif
docs.espressif.com › projects › esp-idf › en › v4.3 › esp32s2 › api-reference › system › esp_timer.html
High Resolution Timer - ESP32-S2 - — ESP-IDF Programming Guide v4.3 documentation
name — timer name (if CONFIG_ESP_TIMER_PROFILING is defined), or timer pointer period — period of timer, in microseconds, or 0 for one-shot timer alarm - time of the next alarm, in microseconds since boot, or 0 if the timer is not started
🌐
Espressif
docs.espressif.com › projects › arduino-esp32 › en › latest › api › timer.html
Timer - - — Arduino ESP32 latest documentation
*/ #include <Arduino.h> // Stop button is attached to PIN 0 (IO0) #define BTN_STOP_ALARM 0 hw_timer_t *timer = NULL; volatile SemaphoreHandle_t timerSemaphore; portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; volatile uint32_t isrCounter = 0; volatile uint32_t lastIsrAt = 0; void ARDUINO_ISR_ATTR onTimer() { // Increment the counter and set the time of ISR portENTER_CRITICAL_ISR(&timerMux); isrCounter = isrCounter + 1; lastIsrAt = millis(); portEXIT_CRITICAL_ISR(&timerMux); // Give a semaphore that we can check in the loop xSemaphoreGiveFromISR(timerSemaphore, NULL); // It is safe to use
🌐
ESP-IDF Programming Guide
demo-dijiudu.readthedocs.io › en › latest › api-reference › system › esp_timer.html
High Resolution Timer — ESP-IDF Programming Guide v3.0-dev-1474-gf8bda32 documentation
esp_timer also provides a convenience function to obtain the time passed since start-up, with microsecond precision: esp_timer_get_time(). This function returns the number of microseconds since esp_timer was initialized, which usually happens shortly before app_main function is called.
🌐
ESP32 Forum
esp32.com › viewtopic.php
100microsecond timer in esp32 - ESP32 Forum
April 24, 2017 - #include <stddef.h> #include "esp_intr_alloc.h" #include "esp_attr.h" #include "driver/timer.h" static intr_handle_t s_timer_handle; static void timer_isr(void* arg) { TIMERG0.int_clr_timers.t0 = 1; TIMERG0.hw_timer[0].config.alarm_en = 1; // your code, runs in the interrupt } void init_timer(int timer_period_us) { timer_config_t config = { .alarm_en = true, .counter_en = false, .intr_type = TIMER_INTR_LEVEL, .counter_dir = TIMER_COUNT_UP, .auto_reload = true, .divider = 80 /* 1 us per tick */ }; timer_init(TIMER_GROUP_0, TIMER_0, &config); timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0); t
🌐
Espressif
docs.espressif.com › projects › esp-idf › en › v4.4.1 › esp32 › api-reference › system › esp_timer.html
High Resolution Timer - ESP32 - — ESP-IDF Programming Guide v4.4.1 documentation
esp_timer set of APIs provides one-shot and periodic timers, microsecond time resolution, and 64-bit range. Internally, esp_timer uses a 64-bit hardware timer, where the implemention depends on CONFIG_ESP_TIMER_IMPL. Available options are: ... The FRC2 is a legacy option for ESP32 until v4.2, ...
🌐
Reddit
reddit.com › r/embedded › esp32 s3: sub-microsecond time sync and disciplined timers
r/embedded on Reddit: ESP32 S3: sub-microsecond time sync and disciplined timers
December 3, 2025 -

Fine Time Sync is a library to build synchronised, high-precision timing network using off-the-shelf ESP32 boards, using nothing but its built in Wi-Fi Fine Timing Measurement (FTM) system. No GPS, no wired clock, no PTP stack — just Wi-Fi.

The video shows 3 slaves syncing their clocks to a master. The code also implements low jitter disciplined timers, driving GPIO — the pulses can be seen with an oscilloscope, so jitter below 100ns is not my imagination.

Supported hardware:

  • Developed on S3, uses MCPWM timer to drive digital output from hardware

  • Should work without modifications on other chips with FTM and MCPWM (S2, C6)

  • Should work on C2 and C3 using with GPTimer instead of MCPWM

  • Will not work at all on chips without FTM (classic ESP32, ESP32 H2)

I will release the code later this week.

UPDATE 3/Dec/2025:

  • Source code (under GPLv3): https://github.com/abbbe/fts/

  • Technical details: https://github.com/abbbe/fts/blob/main/docs/fts-presa-20251203.pdf

🌐
ESP32 Forum
esp32.com › viewtopic.php
esp32 high resolution timer - ESP32 Forum
May 6, 2018 - */ #define BLINK_GPIO CONFIG_BLINK_GPIO char state; static void timer_func(void* arg) { // your code, runs in the interrupt if (state) { state = 0; gpio_set_level(BLINK_GPIO, 1); } else { state = 1; gpio_set_level(BLINK_GPIO, 0); } } void init_timer(int timer_period_us) { int64_t t_end; esp_timer_handle_t timer1; esp_timer_create_args_t args = { .callback = &timer_func, .arg = &t_end, .name = "timer1" }; // --- gpio_pad_select_gpio(BLINK_GPIO); /* Set the GPIO as a push/pull output */ gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT); //---- esp_timer_create(&args, &timer1); //ref_clock_init(); esp_timer_start_periodic(timer1, timer_period_us); while(1) { vTaskDelay(1000 / portTICK_PERIOD_MS); } esp_timer_delete(timer1); } void app_main() { init_timer(10); // 10uS }
🌐
ESP32 Forum
esp32.com › viewtopic.php
Wait microsecond - ESP32 Forum
I need 10 and 40 microseconds delay support as per request. ... The arduino-esp32 core achieves this using the following code for the delayMicroseconds() func. ... #include "freertos/FreeRTOS.h" #define NOP() asm volatile ("nop") unsigned long IRAM_ATTR micros() { return (unsigned long) ...
🌐
Upsy
upesy.com › tutorials › esp32 › esp32 programming › micropython › basics › timers
Timer ESP32 with MicroPython: Master the time - uPesy
January 23, 2023 - The minimum timer period is one millisecond in MicroPython, whereas with Arduino code, it can easily be reduced to one microsecond. Timer performances are limited so that MicroPython can keep up with the pace..
🌐
ESP32 Forum
esp32.com › viewtopic.php
Fast timer for microsecond level duration measurements - Page 2 - ESP32 Forum
To erase the confusion caused in this post, I have stored in GitHub a working code with documentation, sample code, and test results. The microsecond level time measurement function timer_u32() is available in https://github.com/OliviliK/ESP32_timer_u32 The example is timing of fibonacci function up to F92.