Lesson 91: Using interrupts() Function
Learn how interrupts() re-enables global interrupts and safely resumes event handling after critical sections.
Progress indicator
Lesson 91 of 91
Learning Objectives
- Understand what interrupts() does and why it re-enables global interrupt handling.
- Learn interrupts() syntax and how it pairs with noInterrupts().
- Use critical sections to safely read shared interrupt-updated variables.
- Understand when to re-enable interrupts quickly for responsive systems.
- Avoid common interrupt mistakes that cause missed events or unstable behavior.
- Build a clear mental model of ISR-to-loop data flow using volatile and snapshots.
Concept Explanation
What is interrupts()
interrupts() re-enables global interrupts after they were disabled by noInterrupts().
It tells the CPU that interrupt events can be handled again.
In practice, you use it to end a protected critical section. If you forget to call it, your system may appear slow, unresponsive, or miss external events.
interrupts() Syntax
Basic syntax: interrupts();
It is usually placed right after a short critical section that used noInterrupts().
Typical pattern: noInterrupts(); copy_shared_data(); interrupts();
How interrupts() Works
- Program disables interrupts to protect a shared operation.
- Program performs quick critical read/write work.
- Program calls
interrupts()to restore interrupt handling.
Keep the disabled window as short as possible. Long critical sections increase latency for interrupt-driven tasks like button capture, encoder reading, or time-critical signaling.
Global Interrupt Enable Concept
Global interrupt enable means the CPU is allowed to respond to registered interrupt events.
If global interrupts stay disabled too long, important events can be delayed or missed.
Beginner rule: disable only when necessary, do minimal work, then re-enable immediately.
interrupts() vs noInterrupts() (Comparison)
noInterrupts(): disables global interrupts.interrupts(): re-enables global interrupts.
Best practice is to keep the disabled window very short and re-enable as soon as possible.
They are a pair. Using one without the other usually indicates a logic bug.
When to Use interrupts()
- After copying shared variables updated in ISR.
- After tiny critical sections protecting shared state.
- When you temporarily paused interrupts and must restore responsiveness.
- When working with counters/flags modified by ISR callbacks.
Real-life example: you copy a multi-byte counter updated by ISR, then re-enable interrupts so incoming button/encoder events are not blocked.
Example Code
This example counts button interrupts and safely reads the count in loop().
const int BUTTON_PIN = 0;
const int LED_PIN = 2;
volatile int pressCount = 0;
void IRAM_ATTR onButtonPress() {
pressCount = pressCount + 1;
}
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(LED_PIN, OUTPUT);
Serial.begin(115200);
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), onButtonPress, FALLING);
}
void loop() {
noInterrupts(); // Enter critical section
int snapshot = pressCount; // Copy shared value safely
interrupts(); // Exit critical section
if (snapshot % 2 == 0) {
digitalWrite(LED_PIN, LOW);
} else {
digitalWrite(LED_PIN, HIGH);
}
Serial.print("Button presses: ");
Serial.println(snapshot);
delay(200);
}Example Code Explanation
- The ISR
onButtonPress()increments a volatile counter. - attachInterrupt() links button edge events to ISR callback.
- loop() uses
noInterrupts()before reading shared counter. - Shared counter is copied into local
snapshot. interrupts()is called immediately to restore normal interrupt handling.- LED and serial output use the safe snapshot copy, not direct volatile access logic.
- Using snapshot avoids race conditions where ISR changes value while loop logic is reading it.
- ISR stays minimal (counter increment only), which keeps interrupt latency low.
What Happens Inside
- Button edge triggers hardware interrupt signal.
- CPU runs ISR and updates
pressCount. - Main loop enters critical section and pauses interrupts briefly.
- Main loop copies shared value to local snapshot.
- Main loop calls
interrupts()to resume event handling. - Application logic uses snapshot for stable output and debug printing.
Common Mistakes with interrupts()
- Forgetting to call interrupts() after noInterrupts().
- Keeping interrupts disabled too long, causing missed events.
- Doing heavy work inside ISR instead of in loop().
- Not using volatile for ISR-shared variables.
- Reading shared data without a short critical section.
Best Practices for interrupts()
- Keep critical sections very short.
- Call interrupts() immediately after protected copy/update.
- Use volatile for variables shared between ISR and loop().
- Keep ISR minimal: set flags/counters only.
- Use serial logs in loop() to validate interrupt flow safely.
Try it now
Open the simulator workspace and step through critical-section snapshots with interrupt-style counter updates.