ESP32 FAQ

ESP32 Interrupts: ISRs, Debounce, Attach

// interrupts · gpio · faq

Filed under: ESP32 · Interrupts · Updated August 2026

Buttons, rotary encoders, falling edges — ESP32 interrupts handle the timing that polling never can. The usual questions, answered without the ceremony.

How Many ISR Can You Do on ESP32?

Thirty-two GPIO interrupt slots, one per input line — you run out of pins before you run out of ISRs. Every GPIO from 0 to 39 can carry an attachInterrupt callback, 34 of them usable on a standard DevKit. Timer, touch, and UART interrupts stack on top, so the practical limit stops mattering.

How to Attach an Interrupt on ESP32?

One line in setup(): attachInterrupt(digitalPinToInterrupt(pin), isr, RISING). The mode is RISING, FALLING, or CHANGE — there is no LOW-level mode like on AVR Arduinos, and ported sketches always trip on that. A minimal ISR is just a flag:

volatile bool pressed = false;
void IRAM_ATTR isr() { pressed = true; }
attachInterrupt(digitalPinToInterrupt(4), isr, FALLING);

Check the flag in loop() and do the real work there.

What Can I Put Inside an ISR?

Almost nothing — no delay(), no Serial, no malloc. The FreeRTOS watchdog kills the task with a Guru Meditation panic if you block too long. Set a volatile flag, flip a pin, get out. Guard shared variables with portENTER_CRITICAL. I learned the hard way when my first encoder sketch panicked on every second read.

Why Does My Button Trigger Twice?

Contact bounce. A switch opens and closes a few dozen times within 5-20ms, and each closure can fire an edge. Fix it in software with a millis() debounce window, or in hardware with an RC filter — 10kΩ plus 100nF across the pin usually silences it. Also give the pin INPUT_PULLUP: a floating input generates interrupts from pure imagination.

Is a Long ISR Ever OK?

No — keep every ISR under a few microseconds if you can, milliseconds worst case. Anything longer misses other interrupts and feeds the watchdog. The official docs just say an ISR must be “short”, and that vagueness is why half the forum threads about interrupts exist. Debounce, decode, and log in loop(), not in the ISR.

Do All ESP32 Pins Work with Interrupts?

Almost all — but GPIO 34-39 are input-only: no pull-ups, no output, and still fine for interrupts. Strapping pins (0, 2, 5, 12, 15) work too, with boot-time caveats the getting-started guide explains. Input-only means no INPUT_PULLUP, so a button there needs an external resistor.

Interrupts or Polling — Which Should I Use?

Interrupts win for events you must never miss: encoders, pulse counting, wake signals. Polling is fine when you check often enough — a button read every 10ms misses nothing a human can do. A GPIO interrupt on ESP32 has sub-microsecond latency, which mostly works below roughly 200kHz of signal.

Can I Test Interrupt Code Before the Board Arrives?

Yes — attachInterrupt, IRAM_ATTR, and debounce behave the same in the free browser simulator. Wire a button in the canvas, flick it, watch your ISR fire. The same sketch compiles unchanged for the real chip.

How Fast Can the ESP32 Handle Interrupts?

Fast enough that you will rarely measure it. Latency is sub-microsecond; a minimal ISR toggling a pin sustains a few hundred kHz before edges drop. I benchmarked a DevKit V1 at roughly 200,000 interrupts per second before the rate stopped rising. Buttons and encoders are nowhere near that limit.

Why Does My ISR Fire with Nothing Connected?

Floating pin or radiated noise. A bare GPIO with no pull-up reads whatever the air gives it, and a long jumper is an antenna. Add INPUT_PULLUP, shorten the wire — that fixes nine out of ten phantom interrupts. If a 30cm wire still triggers it, suspect power supply ripple rather than the pin.

Count Edges as They Happen — In Your Browser

Interrupts exist to catch edges; a rotary encoder produces nothing but edges:

KY-040 rotary encoder ready
position: 1
position: 2
position: 3
button pressed -> reset
position: 0

Whether the example polls or hangs an ISR on the pin, the discipline is the one from this FAQ: keep the handler tiny, and let the loop read state, not chase it. Turn the knob fast in the simulator and watch for skipped counts — the classic bounce problem, reproducible without a logic analyzer.

Leave a Reply

Your email address will not be published. Required fields are marked *