Lesson 95: Using digitalPinToInterrupt() External Interrupts
Learn how digitalPinToInterrupt() maps board pins to interrupt numbers for safe attachInterrupt() usage.
Progress indicator
Lesson 95 of 95
Learning Objectives
- Understand what digitalPinToInterrupt() does and why pin mapping is needed.
- Use digitalPinToInterrupt() syntax correctly with attachInterrupt().
- Learn pin-mapping concept and supported-pin checks for interrupt-safe design.
- Avoid hardcoded interrupt numbers and improve board portability.
- Handle unsupported interrupt pins safely in beginner projects.
- Build a practical interrupt setup checklist for robust attachInterrupt() initialization.
Concept Explanation
What is digitalPinToInterrupt()
digitalPinToInterrupt(pin) converts a board pin number into the interrupt number/value required by attachInterrupt().
It helps you write safer and more portable code across boards.
Without this helper, code may work on one board and fail on another because interrupt numbers are not always the same as pin numbers.
digitalPinToInterrupt() Syntax
Basic syntax: int irq = digitalPinToInterrupt(pin);
Then pass the mapped value into attachInterrupt(): attachInterrupt(irq, isr, mode);
This two-step approach makes it easier to validate mapping before attaching ISR.
How digitalPinToInterrupt() Works
- Read selected digital pin number.
- Lookup internal board mapping table.
- Return matching interrupt ID or NOT_AN_INTERRUPT if unsupported.
The returned value is board-specific mapping output, not just a copy of your pin number.
Pin Mapping Concept
Different boards can map pins to interrupts differently. Mapping helper keeps your code aligned with board definitions.
This avoids fragile hardcoded interrupt numbers.
Think of it as a translator between "physical pin label" and "interrupt controller channel".
Using with attachInterrupt()
Always call digitalPinToInterrupt(pin) first and use its return value in attachInterrupt().
If result is NOT_AN_INTERRUPT, do not attach; handle gracefully with fallback logic.
Good fallback examples: print clear error, blink warning LED, or switch to polling mode.
Supported Pins Note
Not every pin supports external interrupts on every board/configuration.
Always check board docs and runtime mapping result before attach.
Some pins may be restricted by boot mode, special functions, or board routing.
When to Use digitalPinToInterrupt()
- Whenever attachInterrupt() is used in production-friendly code.
- When writing code intended for board portability.
- When validating interrupt-capable pin selection at runtime.
- When you want beginner-safe interrupt setup with explicit checks.
Real-life use: classroom examples that run on different ESP32 variants without manual remap edits.
Example Code
This example maps button pin to interrupt ID, validates support, then attaches ISR safely.
const int BUTTON_PIN = 0;
const int LED_PIN = 2;
volatile unsigned long pulseCount = 0;
void IRAM_ATTR onPulse() {
pulseCount = pulseCount + 1;
}
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(LED_PIN, OUTPUT);
Serial.begin(115200);
int interruptNumber = digitalPinToInterrupt(BUTTON_PIN);
if (interruptNumber == NOT_AN_INTERRUPT) {
Serial.println("Pin does not support external interrupt.");
while (true) { delay(1000); }
}
attachInterrupt(interruptNumber, onPulse, FALLING);
}
void loop() {
unsigned long snapshot;
noInterrupts();
snapshot = pulseCount;
interrupts();
digitalWrite(LED_PIN, (snapshot % 2 == 0) ? LOW : HIGH);
Serial.print("Interrupt pin: ");
Serial.print(BUTTON_PIN);
Serial.print(" | count: ");
Serial.println(snapshot);
delay(200);
}Example Code Explanation
- Map BUTTON_PIN to interrupt ID using digitalPinToInterrupt().
- Check for NOT_AN_INTERRUPT and stop safely if unsupported.
- Attach ISR callback only when mapping is valid.
- ISR increments pulse counter on FALLING edge events.
- loop() snapshots shared counter and updates LED/Serial output.
- Validation step prevents silent failure where attachInterrupt() appears configured but never fires.
- Snapshot logic keeps output consistent even when ISR updates happen asynchronously.
What Happens Inside
- Pin number is translated to board interrupt mapping.
- Interrupt attach happens only if mapping is valid.
- Hardware edge event triggers ISR and updates shared counter.
- Main loop copies counter safely and drives output logic.
- Serial output confirms mapped pin behavior and count updates.
Common Mistakes with digitalPinToInterrupt()
- Skipping mapping helper and hardcoding interrupt IDs directly.
- Ignoring NOT_AN_INTERRUPT return and attaching anyway.
- Assuming all pins support external interrupts equally.
- Choosing wrong trigger mode for selected wiring setup.
- Not using volatile/safe snapshot for ISR-shared variables.
Best Practices for digitalPinToInterrupt()
- Always map pin using digitalPinToInterrupt() before attachInterrupt().
- Check for NOT_AN_INTERRUPT and handle unsupported pins clearly.
- Keep ISR minimal and move heavy work into loop().
- Use volatile variables and critical-section snapshots.
- Document chosen interrupt pin and trigger mode for maintainability.
Try it now
Open the simulator workspace and step through pin mapping, validation checks, and interrupt-driven pulse counting.