Lesson 92: Using noInterrupts() Function
Learn how noInterrupts() temporarily disables global interrupts for safe critical sections with shared ISR data.
Progress indicator
Lesson 92 of 92
Learning Objectives
- Understand what noInterrupts() does and why it pauses global interrupt handling.
- Learn noInterrupts() syntax and where to place it safely.
- Use short critical sections to copy shared ISR-updated values without race conditions.
- Compare noInterrupts() and interrupts() roles in safe interrupt control flow.
- Avoid common mistakes like long interrupt-disable windows and missing re-enable calls.
- Build a clear mental model for ISR updates, snapshot copies, and restored responsiveness.
Concept Explanation
What is noInterrupts()
noInterrupts() disables global interrupts temporarily.
It prevents ISR callbacks from running while you execute a very short critical operation.
Use it only when you truly need a protected region. It is a precision tool, not a default pattern for all loop logic.
noInterrupts() Syntax
Basic syntax: noInterrupts();
Typical safe pattern: noInterrupts(); copy_shared(); interrupts();
Keep the protected code between these two calls as short and simple as possible.
How noInterrupts() Works
- Program calls noInterrupts() and pauses global interrupt handling.
- Program quickly performs critical read/write operation.
- Program calls interrupts() to resume normal interrupt processing.
The critical section must stay very short to avoid event latency or missed interrupts.
If the section is too long, events can queue or be missed depending on hardware and timing.
Global Interrupt Disable Concept
Global disable means the CPU temporarily ignores registered interrupt events.
This is useful for data consistency, but dangerous if kept active too long.
Beginner rule: disable briefly, copy safely, re-enable immediately.
noInterrupts() vs interrupts() (Comparison)
noInterrupts(): pause global interrupt handling.interrupts(): resume global interrupt handling.
They should usually be used as a matched pair around small critical code blocks.
Think of them as open/close brackets for protected code.
When to Use noInterrupts()
- When copying ISR-updated shared variables safely.
- When protecting tiny multi-step updates that must stay atomic.
- When race conditions appear between loop() and ISR logic.
- When critical data consistency matters more than brief latency.
Real-life example: an ISR updates a pulse counter rapidly, and loop() needs a stable snapshot for calculations and display without mid-read changes.
Example Code
This example uses noInterrupts() to copy pressCount safely before using it in loop logic.
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(); // Start critical section
int snapshot = pressCount; // Safe copy from shared variable
interrupts(); // End critical section
if (snapshot > 0) {
digitalWrite(LED_PIN, HIGH);
} else {
digitalWrite(LED_PIN, LOW);
}
Serial.print("Press count snapshot: ");
Serial.println(snapshot);
delay(200);
}Example Code Explanation
- ISR increments volatile pressCount when button interrupt occurs.
- loop() calls noInterrupts() before reading shared pressCount.
- Value is copied into local snapshot while interrupts are paused.
- interrupts() is called immediately to restore responsiveness.
- Program uses snapshot to update LED and print stable debug output.
- Snapshot logic avoids inconsistent reads where ISR modifies pressCount during loop processing.
- ISR remains lightweight (just increment), which keeps interrupt service fast and predictable.
What Happens Inside
- Button edge can trigger ISR and update pressCount.
- Main loop disables interrupts before shared-value copy.
- Shared variable is copied atomically into snapshot.
- Main loop re-enables interrupts.
- Loop logic operates on snapshot without race-condition risk.
- Serial output shows consistent copied value.
Common Mistakes with noInterrupts()
- Forgetting to call interrupts() after noInterrupts().
- Running slow code inside critical section (delay, Serial, heavy loops).
- Using noInterrupts() when simpler logic would avoid shared-state races.
- Not declaring ISR-shared variables as volatile.
- Disabling interrupts too frequently and hurting responsiveness.
Best Practices for noInterrupts()
- Keep critical sections minimal and fast.
- Always pair noInterrupts() with interrupts() promptly.
- Use local snapshots for loop logic after critical copy.
- Keep ISR simple: counters/flags only.
- Validate behavior with serial logs in loop(), not inside ISR.
Try it now
Open the simulator workspace and step through noInterrupts()/interrupts() critical section behavior with snapshot values.