欧博abgESP32 Timer reloading
bdrmachine March 17, 2025, 2:35am 1
I have been struggling with a piece of code that will trigger an interrupt X seconds after a input is received, 6 seconds in my code example. The timer needs to be re-triggered every time the input arrives. There is no way the input will still be present at the end of the timer delay and it will not reappear until sometime after. The only way I have been able to get it working is with a guard variable (detectLowSignal). I do think there is a easier way by disabling or not reloading the timer but I haven't succeed in that technique. I would appreciate any input from members familiar with the workings of ESP32 timers.
#include <Arduino.h> #define LED_PIN 38 // Pin connected to the LED #define INPUT_PIN 35 // Pin to detect the LOW signal hw_timer_t * timer = NULL; // Declare a pointer for Timer 3 bool toggleLED = false; // Flag to toggle the LED bool detectLowSignal = false; // Flag to indicate LOW signal detection void IRAM_ATTR onTimer() { // This function is called by Timer 3 interrupt every 6 seconds if (detectLowSignal) { // Only toggle LED after LOW signal is detected toggleLED = true; // Flag to toggle LED after 6 seconds } } void setup() { pinMode(LED_PIN, OUTPUT); // Set LED pin as output pinMode(INPUT_PIN, INPUT_PULLUP); // Set input pin for detecting LOW signal Serial.begin(115200); // Start serial communication at 115200 baud rate // Initialize Timer 3 to trigger every 6 seconds (6,000,000 microseconds) timer = timerBegin(3, 80, true); // Timer 3, prescaler of 80 for 1-second intervals timerAttachInterrupt(timer, &onTimer, true); // Attach onTimer function as interrupt handler timerAlarmWrite(timer, 6000000, true); // Set the timer to trigger every 6 seconds (6,000,000 microseconds) } void loop() { // Check for a LOW signal on INPUT_PIN (GPIO 35) int inputState = digitalRead(INPUT_PIN); if (inputState == LOW && !detectLowSignal) { // Detect LOW signal and not already in process detectLowSignal = true; // Start the 6-second timer timerAlarmEnable(timer); // Enable the timer after LOW signal detection Serial.println("LOW signal detected, starting 6-second delay..."); } if (toggleLED) { digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // Toggle the LED state Serial.println("LED Toggled!"); toggleLED = false; // Reset the flag after toggling LED detectLowSignal = false; // Reset LOW signal detection flag } delay(100); // Small delay to avoid flooding the serial monitor too quickly }
horace March 17, 2025, 6:27am 2
what is the "input" which triggers the six second delay?
if a rising edge on a digital pin
on pin 5 rising edge a message is displayed - serial monitor output
ESP32 external interrupt test on pin 5 interrupt on pin 5 interrupt on pin 5 interrupt on pin 5 interrupt on pin 5 interrupt on pin 5 interrupt on pin 5your interrupt routine could start the timer which could interrupt six seconds later
bdrmachine March 17, 2025, 2:13pm 3
In the long view of things, I want the delay to be triggered every time a DS3231 RTC interrupts. The RTC interrupt happens every 30 minutes which will turn on a motor. I want the motor on for 5 minutes. I need to figure out how to use a ESP32 timer to start when it sees the RTR interrupt and send its own interrupt trigger after 5 minutes is up so the code knows when to turn the motor off again. The code I enclosed was a test that used a switch as the trigger and a LED that lights for 6 seconds. This was just a test to get the delay feature debugged. Thanks for the reply but your code is not using the timer function I need.
blh64 March 17, 2025, 4:41pm 4
You could more easily do the entire thing without any interrupts. Every time through loop(), if the motor is not running, check if it is time to start the motor. If it is time to start the motor, turn it on and note the time.
If the motor is running, check how long it has been running (millis() - startTime) and if it has been running long enough, turn it off.
It is more of a state machine approach. Just another way to accomplish your goal.
ec2021 March 17, 2025, 7:57pm 5
Hi @bdrmachine ,
I second @blh64 's post that it's much easier to realize the required functionality inside loop(). However if using the timer is the main goal ("if the journey is the destination") feel free to check this out:
/* Forum: https://forum.arduino.cc/t/esp32-timer-reloading/1364408 Wokwi: https://wokwi.com/projects/425693519758915585 References: [1] https://espressif-docs.readthedocs-hosted.com/projects/arduino-esp32/en/latest/api/timer.html [2] https://docs.espressif.com/projects/arduino-esp32/en/latest/migration_guides/2.x_to_3.0.html#timer 2025/03/17 ec2021 Due to an example in reference [1] it is allowed to use digitalRead/write in the ISR outside of the critical part As the timer did not stop (as expected from the documentation) it is freed and newly created in the routines */ #include <Arduino.h> constexpr byte BTN_START_ALARM {12}; constexpr byte LED_PIN {14}; volatile byte _ledState = LOW; hw_timer_t * timer = NULL; portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; boolean detectLowSignal = false; void ARDUINO_ISR_ATTR onTimer() { setLed(LOW); } void setup() { Serial.begin(115200); pinMode(BTN_START_ALARM, INPUT_PULLUP); pinMode(LED_PIN, OUTPUT); } void loop() { byte inputState = digitalRead(BTN_START_ALARM); if (inputState == LOW && !ledState()) { setLed(HIGH); detectLowSignal = true; Serial.println("Led ON"); startTimer(); } if (detectLowSignal && !ledState()) { stopTimer(); Serial.println("Led OFF"); detectLowSignal = false; } } void startTimer(){ timer = timerBegin(1000000); timerAttachInterrupt(timer, &onTimer); timerAlarm(timer, 6000000, false, 0); Serial.println("Timer started"); } void stopTimer(){ if (timer){ timerEnd(timer); Serial.println("Timer stopped"); } } void setLed(byte state){ digitalWrite(LED_PIN, state); portENTER_CRITICAL_ISR(&timerMux); _ledState = state; portEXIT_CRITICAL_ISR(&timerMux); } byte ledState(){ portENTER_CRITICAL_ISR(&timerMux); byte state = _ledState; portEXIT_CRITICAL_ISR(&timerMux); return state; }To be tested on Wokwi: https://wokwi.com/projects/425693519758915585
Please be aware that the sketch is based on the ESP32 Arduino API 3.0 not on 2.x as your sketch and other pins are used for the led and a button.
As the timer (at least on Wokwi) did not stop after one shot, I finally created and deleted the timer inside loop(). The ISR does not toggle but switch the led off. The variable shared between loop() and ISR is handled using the ESP32 typical portENTER/port_EXIT_CRITICAL_ISR() macros. The recent ledState() is used as an additional criteria to enter the if clauses to start and to stop the timer only when the led is off.
In total I think it's quite likely less effort to use millis() functions in loop() for the timing ... But just for the fun of it ...
Good luck!
ec2021
1 Like
horace March 17, 2025, 9:15pm 6
post 1 specified a delay 6seconds but post 3 mentions 5minutes
anyway this code delays for 6 seconds
serial monitor output
ESP32 external interrupt test on pin 5 interrupt on pin 5 timer Interrupt 6000mSec interrupt on pin 5 timer Interrupt 6000mSec interrupt on pin 5 timer Interrupt 6000mSeafter pressing button which takes pin 5 low the timer messages appears
using ESP32 core 3.1.3
ec2021 March 17, 2025, 10:38pm 7
Hi @horace !
Looks nice!
Just a question: Does timerBegin() create a new timer instance? If yes the previous timer should be freed calling timerEnd(timer)...
Could be done just before the new timer is created
if (timer) {timerEnd(timer);};In the meantime I found the functions
timerStart(timer)
timerStop(timer) and
timer restart(timer)
which might be interesting... Too late for me today...

Regards
ec2021
qubits-us March 18, 2025, 12:57am 8
might want to take a peek at High Resolution Timer (ESP Timer)..
experimented with them a while back..
An example, One shot that reloads itself, just blinks onboard led once per second..
good luck.. ~q
bdrmachine March 18, 2025, 2:08am 9
Thanks for the reply but The compiler flags numerous error in your code and won't build.
horace March 18, 2025, 6:50am 10
![]()
The compiler flags numerous error in your code and won't build.
as @ec2021 mentioned in post 5 you are running ESP32 core 2.x the code I posted used core V3..1.3 - see Migration from ESP32 core 2.x to 3.0
1 Like
bdrmachine March 18, 2025, 2:31pm 11
With all these moving parts My hats off to you. Thanks Much for pointing me in the right direction!!
bdrmachine April 2, 2025, 2:51pm 12
I have had some time to play around with the code that this forum has so graciously provided. I tried to boil it down to the shortest and most understandable for me, while I'm sure a lot of you find this stuff trivial. One gotcha I fought with was the testing to see if (timer) was defined. It seems that timerEnd(timer) doesn't set timer back to nullptr as expected so I had to manually do so. In my view this is a error in version 3.0 of the api. The test code is enclosed in hopes it helps someone out there. Here is a brief explanation of the code: If a switch is pressed turn a LED off and start a timer. The timer will create a interrupt and turn the LED back on.
#include <Arduino.h> byte SW {35}; byte LED {38}; volatile byte _ledState = LOW; hw_timer_t * timer = NULL; boolean detectLowSignal = false; boolean Triggered = false; void ARDUINO_ISR_ATTR onTimer() { digitalWrite(LED, LOW) ; Triggered = true; } void setup() { Serial.begin(115200); pinMode(SW, INPUT_PULLUP); pinMode(LED, OUTPUT); } void loop() { if ( !digitalRead(SW) && timer == nullptr) // replaced !digitalRead(LED) { digitalWrite(LED, HIGH); detectLowSignal = true; Serial.println("Led OFF"); {// start timer } timer = timerBegin(1000000); timerAttachInterrupt(timer, &onTimer); timerAlarm(timer, 6000000, false, 0); Serial.println("Timer started"); } } if (Triggered) { if (timer) { // timer stop timerEnd(timer); timer = nullptr; // Explicitly set the timer pointer to nullptr Serial.println("Timer stopped"); } Serial.println("Led ON"); detectLowSignal = false; Triggered = false; } }
system Closed September 29, 2025, 2:52pm 13
This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.