# attachInterrupt does nothing: only some pins support it

> Fix an interrupt that never fires: on an Uno only pins 2 and 3 work, so use digitalPinToInterrupt and pick a supported pin.

Canonical: https://roboticsengineering.in/fix/arduino-attachinterrupt-wrong-pin-uno-2-3/ | Published: 2026-11-03 | Updated: 2026-11-04

**On an Uno and Nano only pins 2 and 3 support external interrupts.** Other boards allow more pins.

## What the message means

The chip routes interrupt signals through certain pins. Attaching to another pin compiles but never triggers.

## Fix it in this order

1. Check the interrupt-capable pins for your board in its documentation.
2. Use digitalPinToInterrupt(pin) rather than a raw number.
3. Pick RISING, FALLING or CHANGE to match the signal.
4. Keep the handler short and use volatile variables.
5. Use pin-change interrupts or a library if you need more pins on an Uno.

## Why it happens

On an Arduino Uno or Nano (ATmega328P), the hardware external interrupts are on pins 2 and 3 only. Calling attachInterrupt() on any other pin does not work, because the function expects an interrupt number that maps to those two pins. On a Mega 2560 the pins are 2, 3, 18, 19, 20 and 21. On an ESP32 almost any GPIO can be used.

## Example: use digitalPinToInterrupt

```
const byte SENSOR_PIN = 2;      // Uno/Nano: 2 or 3
volatile unsigned long pulses = 0;

void onPulse() { pulses++; }    // keep it short, no Serial or delay

void setup() {
  pinMode(SENSOR_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(SENSOR_PIN), onPulse, RISING);
}
```

Example sketch: change pins and values for your own wiring. It has not been bench-tested on your hardware.

## If you need another pin

Move the signal to pin 2 or 3 if you can. If both are taken, the ATmega328P also supports pin-change interrupts on every pin through the PCINT registers or a pin-change library; these fire on any edge for a whole group of pins, so you read the pin inside the handler to find which one changed. Variables shared with the handler must be declared volatile.

## If you see this, do this

| You see | Do this |
| --- | --- |
| Never fires | Pin not supported. |
| Fires many times per press | Bounce. Debounce the signal. |

## Related

[volatile variables](https://roboticsengineering.in/fix/arduino-interrupt-variable-not-updating-volatile/), [encoder bounce](https://roboticsengineering.in/fix/rotary-encoder-wrong-counts-bounce/).

Last reviewed: November 2026. These are the usual causes and the order that finds them fastest. Test on your own board; clones and cables differ.

## Why this happens

External interrupts are tied to specific pins because the chip routes only certain pin signals to its edge-detection hardware. On an Uno or Nano (ATmega328P) these are the pins marked INT0 and INT1, which are pins 2 and 3. A call on a different pin still compiles, but there is no hardware connected to it, so the handler never runs.

The other trap is shared data. An interrupt can strike between the two bytes of a 16-bit or 32-bit variable on an 8-bit chip, so reading the variable in loop() can give a half-updated number. Reading it with interrupts briefly off avoids that.

## How to confirm the cause

- Check the pin against your board’s interrupt-pin list. The Mega 2560 supports 2, 3, 18, 19, 20 and 21.
- Use digitalPinToInterrupt() and see if the sketch still compiles; it returns an invalid value for pins that cannot interrupt.
- Toggle the pin with a wire to ground by hand. The handler should count each press.
- If the count rises by many per press, the contact is bouncing; add a small capacitor or a time check.
- Check that the handler’s variable is declared volatile; otherwise the compiler may keep an old value in a register.

## Worked example

Counting pulses from a 20-slot disc and converting to RPM once a second. 100 pulses in one second equals 5 revolutions per second, or 300 RPM:

```
volatile unsigned long pulses = 0;
void onPulse() { pulses++; }

void setup() {
  Serial.begin(115200);
  pinMode(2, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(2), onPulse, RISING);
}

void loop() {
  delay(1000);
  noInterrupts(); unsigned long n = pulses; pulses = 0; interrupts();
  Serial.println(n / 20.0 * 60.0);   // RPM
}
```

## Common mistakes

- Calling Serial.print or delay() inside the handler.
- Using long calculations in the handler.
- Choosing a pin number from a different board’s table.
- Reading a multi-byte variable without turning interrupts off briefly.
- Using the older raw interrupt numbers, which differ between boards.

## If this did not fix it

- No pull-up on the input, so it floats and picks up noise or never changes.
- A trigger mode that does not match the signal, such as RISING when the sensor pulls low.
- On the ATmega328P, pin-change interrupts for other pins; they share three groups and fire for any pin in the group.
