Skip to content
Buy 10+ on select items — save 10% auto-applied
Free US shipping on orders $35+
Order by 3pm ET — ships same-day from the US
Skip to main content

Arduino Uno Pushbutton Interrupts: Instant LED Toggle | ShillehTek

September 05, 2026 8 views

Arduino Uno Pushbutton Interrupts: Instant LED Toggle | ShillehTek
Project

Build an Arduino Uno pushbutton interrupt demo that toggles an LED instantly even during long delays, with debouncing and safe shared counters by ShillehTek.

30 min Beginner to Intermediate6 parts

Project Overview

Arduino Uno pushbutton interrupts demo: Normal Arduino code checks inputs only when loop() gets around to it, so a press can be missed during a long delay(). Interrupts fix that by pausing whatever is running, executing a tiny function the instant the pin changes, then resuming. In this guide, you will build a minimal demo where a button toggles an LED instantly while the main loop is deliberately busy, and you will learn the key rules that keep interrupt code reliable.

  • Time: ~30 minutes
  • Skill level: Beginner to Intermediate
  • What you will build: A side-by-side demonstration of polling versus interrupts, with a debounced interrupt handler and a press counter shared safely with the main loop.
Arduino Uno with a pushbutton and LED setup demonstrating instant interrupt response
The button fires an interrupt; the LED responds no matter what loop() is doing.

Parts List

From ShillehTek

External

  • None

Note: On the Uno and Nano only pins D2 and D3 support external interrupts (INT0/INT1). The Mega has six, and ESP32 boards can use nearly any GPIO. Always write digitalPinToInterrupt(pin) instead of a raw interrupt number so your sketch ports between boards.

Step-by-Step Guide

Step 1 - Wire button and LED

Goal: Connect one input on an interrupt-capable pin.

What to do: Wire the button between D2 and GND. The internal pull-up will be enabled in code, so pressed equals LOW.

Wire the LED as: D12 to 220Ω resistor to LED to GND.

Arduino Uno wiring diagram with a pushbutton on D2 and an LED on D12 through a 220 ohm resistor
Button on D2, one of the two interrupt-capable pins on the Uno.

Expected result: Hardware is ready for both the polling and interrupt versions.

Step 2 - See the problem first (polling)

Goal: Experience why interrupts exist.

What to do: Upload a sketch whose loop() reads the button, toggles the LED, then calls delay(3000) to simulate a long job (for example, a sensor read or a network call). Press the button at random moments.

Expected result: Most presses are missed because the Arduino only checks the button once every three seconds.

Step 3 - Upload the interrupt version

Goal: Toggle the LED instantly using an interrupt service routine (ISR).

Code:

const int LED = 12, BUTTON = 2;

volatile bool ledState = false;           // 'volatile': shared between ISR and loop()
volatile unsigned long lastEdge = 0;
volatile unsigned long presses = 0;

void onButton() {                         // the ISR: keep it short, no Serial, no delay
  unsigned long now = millis();           // reading millis() inside an ISR is fine
  if (now - lastEdge < 200) return;       // debounce: ignore bounces within 200 ms
  lastEdge = now;
  ledState = !ledState;
  digitalWrite(LED, ledState);
  presses++;
}

void setup() {
  Serial.begin(9600);
  pinMode(LED, OUTPUT);
  pinMode(BUTTON, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(BUTTON), onButton, FALLING);   // fire on press
}

void loop() {
  // the "long job": loop() is blocked, but the button still works
  noInterrupts();                         // read the shared counter atomically
  unsigned long p = presses;
  interrupts();
  Serial.print("Busy for 3 s... presses so far: ");
  Serial.println(p);
  delay(3000);
}

What to do: Upload the sketch and press the button while the Serial Monitor shows that the board is busy.

Expected result: The LED toggles instantly on every press, even during delay(), and the counter prints on the next Serial line.

Step 4 - Apply the three ISR rules

Goal: Avoid the classic interrupt bugs.

What to do: Keep the ISR tiny: set a flag or bump a counter, then do the real work in loop(). Mark every variable the ISR touches as volatile, or the compiler may cache a stale copy. When loop() reads a multi-byte shared variable (anything bigger than a byte on an 8-bit AVR), wrap the read in noInterrupts()/interrupts() so it cannot be updated halfway through.

Also note: delay(), Serial printing, and timekeeping depend on interrupts, so they do not work as expected inside an ISR.

Expected result: Interrupt code that behaves consistently on the bench and in the field.

Step 5 - Try other interrupt modes and observe bounce

Goal: Understand edge modes and why debouncing is needed.

What to do: Change FALLING to RISING (fires on release), CHANGE (both edges so you can time how long the button was held), or LOW (fires continuously while pressed). If you have a logic analyzer, clip it onto D2 and press the button to observe contact chatter that creates many fast edges from one press.

Expected result: You can match interrupt trigger modes to the job and understand what the debounce window is filtering.

Conclusion

Arduino interrupts are how microcontrollers stay responsive while doing slow things. With a button on an interrupt pin, the Uno can toggle an LED instantly even when loop() is blocked in a long delay, as long as you follow the volatile, atomic-read, and keep-it-short rules.

Want the exact parts used in this build? Grab them from ShillehTek.com. If you want help customizing this project or building something for your product, check out our IoT consulting services.

Image credit: Photos and diagrams are credited to Rafi Rasheed T C on Hackster.io. The original guide by Rafi Rasheed T C served as the reference for this ShillehTek version.