Polling Versus Interrupts
In a normal sketch, loop() repeatedly checks inputs — this is polling. Polling is simple, but if your loop is busy doing other work it can miss a brief event, like a fast button press or a rotary encoder pulse. An interrupt flips the model: instead of asking 'has it happened yet?' over and over, the hardware notifies the processor the instant an event occurs, immediately pausing loop() to run a special function. This guarantees a timely response to critical or fast events and frees your main code from constantly watching a pin.
Cricket analogy: Polling is like a fielder glancing at the batsman every few seconds and possibly missing a quick single; an interrupt is like a slip fielder reacting the instant of the nick — notified immediately rather than checking on a schedule.
Attaching an Interrupt
You register an interrupt with attachInterrupt(digitalPinToInterrupt(pin), ISR, mode). The ISR (interrupt service routine) is a function you write that runs when the event fires. Not every pin supports interrupts: on the Uno only pins 2 and 3 do, while boards like the Mega or the 32-bit ATSAMD and ESP boards support many more. The mode argument defines the trigger: RISING fires on a LOW-to-HIGH transition, FALLING on HIGH-to-LOW, CHANGE on any transition, and LOW fires continuously while the pin is held low. Always wrap the pin number in digitalPinToInterrupt() for portability.
Cricket analogy: Choosing a trigger mode is like setting a fielding trap for a specific shot — RISING for one stroke, FALLING for another; attachInterrupt arms the response for exactly the event you expect, like a plan for a particular delivery.
const int buttonPin = 2; // interrupt-capable pin on the Uno
volatile unsigned long pressCount = 0;
volatile bool newPress = false;
void handlePress() { // the ISR: keep it short!
pressCount++;
newPress = true;
}
void setup() {
Serial.begin(9600);
pinMode(buttonPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(buttonPin), handlePress, FALLING);
}
void loop() {
if (newPress) { // handle the event in loop(), not the ISR
newPress = false;
Serial.print("Presses: ");
Serial.println(pressCount);
}
}Any variable shared between an ISR and the rest of your code must be declared volatile, or the compiler may cache it in a register and your main loop will never see updates the ISR makes. For multi-byte shared variables, disable interrupts briefly (noInterrupts()/interrupts()) while reading them to avoid reading a half-updated value.
Writing Safe Interrupt Service Routines
An ISR should be as short and fast as possible because it blocks everything else while it runs. Avoid delay() (it relies on interrupts and won't advance), avoid lengthy Serial prints, and don't expect millis() or micros() to increment inside an ISR since the timer interrupt that drives them is paused. The recommended pattern is to do the minimum in the ISR — set a volatile flag or increment a counter — and perform the real work back in loop() when it next runs. This keeps interrupts responsive and prevents subtle timing bugs.
Cricket analogy: A short ISR is like a wicketkeeper's split-second take then a quick throw — do the minimum instantly and defer the rest; you flag the event and let loop() do the heavy work, just as fielders complete the play afterward.
Interrupts are perfect for reacting to fast, sporadic events: rotary encoders, tachometers counting pulses, emergency stop buttons, and waking a sleeping board to save power. For slow or predictable timing, ordinary polling in loop() is usually simpler and perfectly adequate.
- Polling checks inputs repeatedly; interrupts notify the CPU the instant an event occurs.
- Register with attachInterrupt(digitalPinToInterrupt(pin), ISR, mode).
- On the Uno only pins 2 and 3 support external interrupts; other boards support more.
- Trigger modes are RISING, FALLING, CHANGE, and LOW.
- Share data with the ISR using volatile variables.
- Keep ISRs short: no delay(), no long Serial prints, and millis()/micros() don't advance inside them.
- Set a flag in the ISR and do the real work back in loop().
Practice what you learned
1. What is the main advantage of an interrupt over polling?
2. Which pins support external interrupts on an Arduino Uno?
3. Why must variables shared with an ISR be declared volatile?
4. Why should you avoid delay() and long Serial prints inside an ISR?
5. Which trigger mode fires on any change of the pin's logic level?
Was this page helpful?
You May Also Like
Digital Input and Output
Learn how Arduino pins read and drive binary HIGH/LOW signals using pinMode(), digitalWrite(), and digitalRead(), including the role of pull-up and pull-down resistors.
Reading Common Sensors
Learn how to interface Arduino with common sensors — potentiometers, light-dependent resistors, temperature sensors, PIR motion detectors, and ultrasonic rangefinders — using voltage dividers, libraries, and pulse timing.
Controlling Motors and Servos
Drive DC motors, servos, and stepper motors from an Arduino using transistors, H-bridge drivers, and the Servo library, and understand why motors must not be powered directly from a pin.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics