Every way to keep time without delay()
millis() and elapsed time, a superloop of timed jobs, a state machine, a hardware timer, a FreeRTOS task delay, or a sleep timer. How precise, how many jobs, and how much power decide which.
delay() stops everything else, so the question is what should keep
running while you wait. Ask how many things run at once, how
exact the timing must be, and whether the chip should sleep in
between.
- One or a few periodic jobs, a few milliseconds of jitter is
fine → check
millis()inloop():if (now - start >= interval), with every time inuint32_t. It survives the 49.7-day wrap. - Several jobs, each on its own period → a cooperative superloop: each job has its own last-run time and returns quickly. One slow or blocking job delays all the others.
- Behaviour that depends on what already happened (a button that means different things in different modes) → a finite-state machine, stepped from the same non-blocking loop.
- Timing that must be exact, or faster than a loop pass → a hardware timer: an alarm interrupt, a compare output (PWM), or a capture of an input edge.
- Jobs that must preempt each other, with priorities → FreeRTOS
tasks that wait with
vTaskDelay, orvTaskDelayUntilfor a fixed rhythm. On the ESP32-S3 Arduino core,loop()is already a task. - Long gaps on a battery → don't wait awake at all: deep-sleep
with a timer wake source, which restarts the sketch at
setup().
Why not delay()
Section
Why delay() hurts
During delay() nothing else in loop() runs, so a press can be missed; worst-case reaction time is the sum of the waits.
Term
blocking
delay(), so nothing else can run.Elapsed time in loop()
Section
millis() and elapsed time
Compare elapsed time, never two moments, and keep both in uint32_t.
Example
Term
wraps
Section
Debouncing in software
The same elapsed-time test, used to ignore contact bounce.
Many jobs in one loop
Section
Many things at once
A superloop of short jobs, each run when its own timer is due, and a tiny scheduler table.
Section
Finite-state machines
States, events and transitions, coded with switch or a transition table.
Term
superloop
Hardware timers
Section
Hardware timers and counters
A counter clocked through a prescaler; compare for alarms and PWM, capture for periods, counting for pulses.
Section
Polling vs interrupts
When an edge deserves an interrupt and when a loop that polls is enough.
Term
prescaler
Example
Term
timer
An RTOS
Section
Cooperative vs preemptive
A superloop waits for each job to return; a preemptive scheduler can stop one, at a cost in RAM and shared-data safety.
Section
FreeRTOS tasks and priorities
vTaskDelay(pdMS_TO_TICKS(ms)) blocks only this task; vTaskDelayUntil keeps a fixed rhythm.
Term
preemptive
Term
RTOS tick
Sleep between events
Section
Sleep modes and wake sources
A timer wake source in microseconds; deep sleep restarts at setup(), light sleep carries on.
Term