Zbotic Logo Zbotic Logo
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

  • Shop
  • About Us
  • Contact Us
  • Reseller
  • Blogs
020 69134444
1800 209 0998
[email protected]
Help Desk
Facebook Twitter Instagram Linkedin YouTube
Zbotic Logo Zbotic Logo
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

All departments
  • 3D Print Service
  • 3D Printer
  • Batteries & Chargers
  • Development Boards
  • Drone Parts
  • EBike parts
  • Sensor Modules
  • Electronic Components
  • Electronic Modules
  • IoT and Wireless
  • Mechanical Parts and Workbench Tools
  • Motors & Drivers & Pumps & Actuators
  • DIY and Robot Kits
  • Show more
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
Return to previous page
Home Arduino & Microcontrollers

Arduino Interrupt Service Routine: Write Fast ISR Code

Arduino Interrupt Service Routine: Write Fast ISR Code

March 11, 2026 /Posted byJayesh Jain / 0

An Arduino Interrupt Service Routine (ISR) is a special function that the microcontroller jumps to immediately when a hardware event occurs — without waiting for the current loop() iteration to finish. Mastering the arduino interrupt service routine isr is essential for any project that needs to react to events in microseconds: rotary encoders, pulse counting, emergency stop buttons, or communication protocols. This guide covers everything from the basics of attachInterrupt() to advanced ISR design rules that keep your firmware rock-solid.

Table of Contents

  • What Is an Interrupt Service Routine?
  • Using attachInterrupt()
  • The volatile Keyword — Why It Matters
  • Golden Rules for Writing Safe ISRs
  • Debouncing Inside an ISR
  • Timer Interrupts with TimerOne
  • Real-World ISR Examples
  • FAQ

What Is an Interrupt Service Routine?

In a typical Arduino sketch, the processor executes loop() sequentially — reading sensors, driving outputs, checking flags — in a continuous cycle. If an important event (a button press, a rising edge on a sensor pin, a timer tick) happens between two statements in loop(), the processor might not see it for milliseconds or even seconds, depending on how long other tasks take.

A hardware interrupt breaks this model. When the interrupt condition is met, the processor immediately:

  1. Saves its current execution context (program counter, registers) onto the stack
  2. Disables further interrupts (on AVR by default)
  3. Jumps to the ISR function you have registered
  4. On ISR return, restores the saved context and resumes loop() exactly where it left off

This happens in hardware, typically within 3–5 clock cycles on AVR — that is sub-microsecond response time at 16 MHz. No polling loop can match this latency.

Recommended: Arduino Uno R3 Beginners Kit — ideal for practicing ISR experiments; the Uno’s ATmega328P has 2 external interrupt pins (INT0/INT1) on digital pins 2 and 3, plus full timer interrupt support.

Using attachInterrupt()

The Arduino API exposes external interrupts through attachInterrupt(). The correct modern syntax is:

attachInterrupt(digitalPinToInterrupt(pin), ISR_function, mode);

Parameters:

  • digitalPinToInterrupt(pin): converts a digital pin number to the interrupt number. Always use this macro — never hardcode interrupt numbers, as they differ across boards.
  • ISR_function: the name of your ISR function (no parentheses).
  • mode: one of LOW, CHANGE, RISING, or FALLING.

Interrupt-capable pins by board:

  • Uno / Nano / Mini: pins 2, 3
  • Mega 2560: pins 2, 3, 18, 19, 20, 21
  • Leonardo / Micro: pins 0, 1, 2, 3, 7
  • Due / Zero / MKR series / Nano 33 IoT: all digital pins

Example — counting pulses from a Hall effect sensor on pin 2:

volatile unsigned long pulseCount = 0;

void countPulse() {
  pulseCount++;
}

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

void loop() {
  unsigned long localCount;
  noInterrupts();
  localCount = pulseCount;
  interrupts();
  Serial.println(localCount);
  delay(500);
}

The volatile Keyword — Why It Matters

Any variable shared between an ISR and the main code must be declared volatile. Without it, the compiler may optimise the variable into a CPU register and never write back to RAM — meaning your ISR’s updates are invisible to loop(), and vice versa.

// CORRECT
volatile bool buttonPressed = false;

// WRONG — compiler may cache this in a register
bool buttonPressed = false;

volatile tells the compiler: “always read this variable from memory, never from a cached register.” It does not make the variable access atomic on AVR — for multi-byte types (int, long, float), you must also protect reads with noInterrupts() / interrupts() as shown in the example above.

Recommended: Arduino Mega 2560 R3 Board — gives you 6 external interrupt pins, ideal for encoder motor control, multi-axis CNC, or multi-sensor event-driven systems where a single Uno’s 2 interrupt pins are not enough.

Golden Rules for Writing Safe ISRs

ISRs have strict constraints. Violating them causes subtle, hard-to-debug bugs — timing glitches, frozen sketches, or corrupted data.

Rule 1: Keep ISRs as short as possible. An ISR blocks all other interrupts (on AVR). Long ISRs delay millis() updates, UART receive, and other interrupt-driven peripherals. The ideal ISR sets a flag and returns immediately — do the work in loop().

volatile bool eventFlag = false;

void onEvent() {
  eventFlag = true;  // Set flag only — do NOT process here
}

void loop() {
  if (eventFlag) {
    eventFlag = false;
    // Do your processing here, safely
  }
}

Rule 2: Never use delay() or millis() inside an ISR. delay() relies on timer interrupts which are disabled inside the ISR. millis() returns the same frozen value throughout ISR execution. Use micros() only for timestamping — it partially works inside an ISR but does not update on 8-bit AVR during ISR.

Rule 3: Never use Serial.print() inside an ISR. Serial transmission uses interrupts internally. Calling it from an ISR causes a deadlock — the ISR waits for a transmit interrupt that will never fire because interrupts are disabled.

Rule 4: Protect multi-byte variable reads in main code. On 8-bit AVR, reading a 16-bit or 32-bit variable is not atomic — it takes two or four read instructions. An interrupt between those instructions can corrupt the value. Always bracket multi-byte reads with noInterrupts():

noInterrupts();
unsigned long safeCopy = sharedLongVar;
interrupts();
// Use safeCopy, not sharedLongVar directly

Rule 5: Do not allocate memory in an ISR. malloc(), new, and String concatenation are not interrupt-safe and can corrupt the heap.

Debouncing Inside an ISR

Mechanical switches produce multiple rapid transitions (bounce) when pressed. A naive ISR increments a counter dozens of times per button press. Debouncing must be done carefully at ISR speed:

volatile unsigned long lastInterruptTime = 0;
volatile int buttonPresses = 0;

void buttonISR() {
  unsigned long interruptTime = micros();
  if (interruptTime - lastInterruptTime > 50000UL) {  // 50ms debounce
    buttonPresses++;
  }
  lastInterruptTime = interruptTime;
}

The 50 ms window filters mechanical bounce. micros() is safe enough inside a simple ISR for this purpose, even though it partially stalls on AVR. For production designs, a hardware RC filter (10 kΩ + 100 nF) on the button pin is even more reliable and removes the need for software debounce entirely.

Timer Interrupts with TimerOne

External pin interrupts are event-driven. Timer interrupts fire at a fixed periodic rate regardless of external events — perfect for tasks like PID control, sensor sampling at exactly 100 Hz, or LED PWM without using analogWrite.

The TimerOne library wraps AVR Timer1 (16-bit on Uno) for easy periodic ISR setup:

#include <TimerOne.h>

volatile bool sampleFlag = false;

void sampleISR() {
  sampleFlag = true;
}

void setup() {
  Timer1.initialize(10000);  // 10,000 µs = 100 Hz
  Timer1.attachInterrupt(sampleISR);
}

void loop() {
  if (sampleFlag) {
    sampleFlag = false;
    // Read sensor at precise 100 Hz rate
  }
}

The TimerOne library is available in the Arduino Library Manager. For the Mega, consider TimerThree to avoid conflicts with Timer1 used by the Servo library.

Recommended: Arduino Nano Every with Headers — the ATMega4809 on the Nano Every has a more capable timer/counter system (TCA, TCB, TCD) than the classic AVR, enabling higher-resolution timer interrupts with less timer conflict.

Real-World ISR Examples

Frequency Counter: Measure the frequency of a square wave by counting RISING edges per second using an ISR. Clear the count in loop() every 1000 ms:

volatile unsigned long freq_count = 0;

void freqISR() { freq_count++; }

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

void loop() {
  delay(1000);
  noInterrupts();
  unsigned long hz = freq_count;
  freq_count = 0;
  interrupts();
  Serial.print(hz); Serial.println(" Hz");
}

Emergency Stop: Immediately halt all motor outputs when a safety pin goes LOW, regardless of what the main loop is doing:

const int MOTOR_PIN = 9;
const int ESTOP_PIN = 3;
volatile bool estopActive = false;

void estopISR() {
  analogWrite(MOTOR_PIN, 0);  // Direct hardware action — OK here
  estopActive = true;
}

void setup() {
  pinMode(MOTOR_PIN, OUTPUT);
  pinMode(ESTOP_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(ESTOP_PIN), estopISR, FALLING);
}

Note: Direct pin manipulation inside an ISR is acceptable for safety-critical immediate action — it is the processing-heavy tasks (Serial, String, delay) that must be avoided.

Recommended: Arduino Frequency Counter Kit with 16×2 LCD Display — a practical assembled kit for measuring signal frequencies up to several MHz; a great hands-on way to see ISR-based pulse counting in action with a real display output.

FAQ

How many external interrupts does Arduino Uno have?

The Arduino Uno (ATmega328P) has two external interrupt pins: digital pin 2 (INT0) and digital pin 3 (INT1). For more external interrupts, use Arduino Mega (6 interrupt pins) or Nano Every / boards with PCINT (Pin Change Interrupt) support which allows interrupts on any pin, though with less precision.

Can I use delay() inside an ISR?

No. delay() depends on Timer0 overflow interrupts to count milliseconds. Since interrupts are globally disabled while your ISR runs (on AVR), delay() inside an ISR will hang the program indefinitely. Similarly, millis() will not advance inside an ISR.

What is the difference between RISING, FALLING, and CHANGE modes?

RISING triggers when the pin transitions from LOW to HIGH. FALLING triggers on HIGH to LOW. CHANGE triggers on either transition. LOW continuously triggers the ISR as long as the pin is LOW (use with caution — it fires repeatedly, not once). For most buttons and encoders, RISING or FALLING is the correct choice.

What does volatile actually do at the compiler level?

volatile instructs the compiler to always generate a memory read/write instruction for that variable, never substituting a cached register value. Without it, the compiler’s optimiser legally assumes the variable cannot change between two reads in the same function and skips the second read — which breaks ISR communication entirely.

Can ISRs be nested on Arduino?

On AVR Arduinos (Uno, Mega, Nano), interrupts are globally disabled at the start of every ISR, so ISRs do not nest by default. You can re-enable interrupts inside an ISR with sei() to allow nesting, but this is advanced and risky — only do it if you fully understand the re-entrancy implications and have a compelling reason.

Ready to build faster, more responsive Arduino projects? Explore our full range of Arduino boards at Zbotic — from Uno to Mega to Nano Every, shipped fast across India.

Tags: Arduino, attachInterrupt, embedded, interrupt, ISR, real-time, tutorial
Share Post
  • Facebook
  • Linkedin
  • Whatsapp
Arduino PWM Motor Speed Contro...
blog arduino pwm motor speed control beyond analogwrite 594766
blog arduino ph sensor interface water quality monitoring guide 594768
Arduino pH Sensor Interface: W...

Related posts

Svg%3E
Read more

Arduino Batch Programming: Flash Multiple Boards Quickly

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Based Radar System with Ultrasonic Sensor

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Automatic Plant Monitor: Sunlight, Moisture, Temperature

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Lie Detector: GSR Sensor Polygraph Project

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Metal Detector: Build a Treasure Finder

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading

Add comment Cancel reply

Your email address will not be published. Required fields are marked

Facebook Twitter Instagram Pinterest Linkedin Youtube

Get the latest deals and more.

Download on Google Play Download on the App Store

Call us: 020 69134444 / 1800 209 0998

Monday - Saturday 09:30 AM - 06:00 PM
For Technical Supports Email: [email protected]
For Sales / Enquiries Email: [email protected]

  • My Account

    • Cart

    • Wishlist

    • Checkout

    • My Orders

    • Track Order

    • My Account

  • Information

    • FAQs

    • Blogs

    • Career

    • About Us

    • Contact Us

    • Payment Options

  • Policies

    • Privacy Policy

    • Terms & Conditions

    • GST Input Tax Credit

    • Shipping Return Policy

    • E-Waste Collection Points

    • Our Sitemap

© Zbotic.in is registered trademark of Moxie Supply Pvt Ltd – All Rights Reserved
Login
Use Phone Number
Use Email Address
Not a member yet? Register Now
Reset Password
Use Phone Number
Use Email Address
Register
Already a member? Login Now