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 Interrupts: Hardware and Software Interrupt Tutorial

Arduino Interrupts: Hardware and Software Interrupt Tutorial

April 1, 2026 /Posted by / 0

Table of Contents

  • What Are Interrupts and Why Do They Matter?
  • Hardware Interrupts: External Pin Triggers
  • Software Interrupts: Timer-Based Triggers
  • ISR Rules: What You Can and Cannot Do
  • Practical Examples: Rotary Encoder and RPM Counter
  • Pin Change Interrupts: Use Any Pin as an Interrupt
  • Debugging Interrupt Problems
  • Frequently Asked Questions
  • Conclusion

What Are Interrupts and Why Do They Matter?

An arduino interrupts tutorial begins with understanding what interrupts are: signals that tell the processor to immediately pause its current task and execute a special function called an Interrupt Service Routine (ISR). Once the ISR completes, the processor resumes exactly where it left off. Think of it as a doorbell — no matter what you are doing, you stop, answer the door, and then go back to your activity.

Without interrupts, your Arduino sketch runs sequentially from top to bottom in the loop() function. If you need to check a button press, you must poll the button pin repeatedly. If your loop takes 500 ms to complete (reading sensors, updating a display, sending data), you might miss a button press that lasts only 50 ms. Interrupts solve this problem by reacting to events instantly, regardless of what the main loop is doing.

Interrupts are essential for time-critical applications: counting encoder pulses for motor position, detecting zero-crossings in AC power for dimmer circuits, capturing precise timing events, responding to emergency stop buttons, and processing high-speed communication data. Any application where missing an event or responding late causes problems needs interrupts.

🛒 Recommended: Arduino Uno R3 CH340G Development Board — Great board for learning interrupts with 2 external interrupt pins (D2 and D3).

Hardware Interrupts: External Pin Triggers

Hardware interrupts (also called external interrupts) are triggered by voltage changes on specific pins. The Arduino Uno supports two hardware interrupts: INT0 on pin 2 and INT1 on pin 3. The Mega 2560 supports six: INT0-INT5 on pins 2, 3, 18, 19, 20, and 21.

Trigger Modes:

  • LOW: Triggers whenever the pin is LOW (continuous triggering)
  • CHANGE: Triggers on any state change (LOW to HIGH or HIGH to LOW)
  • RISING: Triggers only on a LOW-to-HIGH transition
  • FALLING: Triggers only on a HIGH-to-LOW transition
// Basic hardware interrupt example - button press counter
volatile unsigned long pressCount = 0;
volatile unsigned long lastPress = 0;

void setup() {
  Serial.begin(9600);
  pinMode(2, INPUT_PULLUP); // Button on pin 2 with internal pull-up
  attachInterrupt(digitalPinToInterrupt(2), buttonISR, FALLING);
}

void loop() {
  // Main loop does its work without worrying about the button
  Serial.print("Presses: ");
  Serial.println(pressCount);
  delay(1000);
}

void buttonISR() {
  // Simple debounce: ignore presses within 200ms of last press
  unsigned long now = millis();
  if (now - lastPress > 200) {
    pressCount++;
    lastPress = now;
  }
}

Key Points:

  • Always use digitalPinToInterrupt(pin) to convert the pin number to the interrupt number. Do not hardcode interrupt numbers.
  • The ISR function takes no parameters and returns nothing (void).
  • Variables shared between the ISR and main code must be declared volatile. This tells the compiler not to optimise away reads of the variable, since it can change at any time.
  • Use INPUT_PULLUP for buttons connected to ground — this eliminates the need for an external pull-up resistor.

Software Interrupts: Timer-Based Triggers

Timer interrupts use the ATMega328P’s internal hardware timers to trigger ISRs at precise intervals. This is useful for tasks that must happen at exact frequencies: ADC sampling at a fixed rate, generating PWM signals with custom frequencies, or updating a display at a fixed refresh rate.

The ATMega328P has three timers: Timer0 (8-bit, used by millis()/delay()), Timer1 (16-bit), and Timer2 (8-bit). Timer1 is the best choice for custom interrupts because it is 16-bit (allows longer intervals) and is not used by critical Arduino functions.

// Timer1 interrupt at 1 Hz (once per second)
void setup() {
  Serial.begin(9600);

  // Disable interrupts during configuration
  noInterrupts();

  // Reset Timer1
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;

  // Set Compare Match Register for 1 Hz
  // 16 MHz / (prescaler 256 * 1 Hz) - 1 = 62499
  OCR1A = 62499;

  // CTC mode (Clear Timer on Compare Match)
  TCCR1B |= (1 << WGM12);

  // Prescaler 256
  TCCR1B |= (1 << CS12);

  // Enable Timer1 Compare A interrupt
  TIMSK1 |= (1 << OCIE1A);

  interrupts(); // Re-enable interrupts
}

volatile unsigned long seconds = 0;

ISR(TIMER1_COMPA_vect) {
  seconds++;
}

void loop() {
  Serial.print("Uptime: ");
  Serial.print(seconds);
  Serial.println(" seconds");
  delay(500);
}

Timer Frequency Formula:

OCR1A = (F_CPU / (prescaler * desired_frequency)) - 1

Where F_CPU = 16,000,000 for standard Arduino boards. Available prescaler values are 1, 8, 64, 256, and 1024. Choose the prescaler that keeps OCR1A within the timer’s range (0-65535 for Timer1, 0-255 for Timer0/Timer2).

Common Timer Interrupt Frequencies:

  • 1 kHz (1ms): OCR1A = 1999, prescaler 8
  • 100 Hz (10ms): OCR1A = 624, prescaler 256
  • 10 Hz (100ms): OCR1A = 6249, prescaler 256
  • 1 Hz (1s): OCR1A = 62499, prescaler 256

ISR Rules: What You Can and Cannot Do

Interrupt Service Routines have strict rules that beginners often violate, leading to crashes, lockups, and bizarre behaviour.

Rule 1: Keep ISRs short. An ISR should execute in microseconds, not milliseconds. While inside an ISR, other interrupts are disabled by default. If your ISR takes too long, you will miss timer ticks (millis() will drift), miss serial data, and miss other external interrupts.

Rule 2: No Serial.print() inside ISRs. Serial communication relies on interrupts internally. Calling Serial functions inside an ISR causes a deadlock. Instead, set a flag in the ISR and handle Serial output in the main loop.

// WRONG - will cause lockups
void myISR() {
  Serial.println("Interrupt!"); // NEVER do this
}

// CORRECT - set a flag
volatile bool eventOccurred = false;

void myISR() {
  eventOccurred = true; // Fast - just set a flag
}

void loop() {
  if (eventOccurred) {
    eventOccurred = false;
    Serial.println("Interrupt occurred!"); // Safe in main loop
  }
}

Rule 3: No delay() inside ISRs. The delay() function depends on Timer0 interrupts, which are disabled during your ISR. Calling delay() will hang the processor indefinitely.

Rule 4: Use volatile for shared variables. Without volatile, the compiler may cache a variable’s value in a register and never re-read it from memory, even though the ISR has changed it. This causes the main loop to see stale values.

Rule 5: Protect multi-byte reads. On an 8-bit processor, reading a 16-bit or 32-bit variable is not atomic. If an interrupt fires between reading the high and low bytes, you get a corrupted value. Use noInterrupts()/interrupts() around multi-byte reads:

volatile unsigned long counter = 0;

void loop() {
  noInterrupts();
  unsigned long safeCounter = counter; // Atomic copy
  interrupts();
  Serial.println(safeCounter);
}

Practical Examples: Rotary Encoder and RPM Counter

Rotary Encoder Reading:

Rotary encoders output two square waves (A and B channels) that are 90 degrees out of phase. By reading one channel in the ISR triggered by the other, you can determine both position and direction.

// Rotary encoder with interrupt-driven reading
#define ENCODER_A 2  // Interrupt pin
#define ENCODER_B 4  // Regular digital pin

volatile int encoderPosition = 0;

void setup() {
  Serial.begin(9600);
  pinMode(ENCODER_A, INPUT_PULLUP);
  pinMode(ENCODER_B, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(ENCODER_A), encoderISR, RISING);
}

void encoderISR() {
  if (digitalRead(ENCODER_B) == HIGH) {
    encoderPosition++; // Clockwise
  } else {
    encoderPosition--; // Counter-clockwise
  }
}

void loop() {
  noInterrupts();
  int pos = encoderPosition;
  interrupts();
  Serial.println(pos);
  delay(100);
}

RPM Counter with IR Sensor:

Mount an IR reflective sensor near a rotating shaft with a reflective strip. Each rotation triggers an interrupt. Count pulses over a fixed time interval to calculate RPM.

// RPM counter using external interrupt
volatile unsigned long pulseCount = 0;
unsigned long lastTime = 0;
float rpm = 0;

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

void countPulse() {
  pulseCount++;
}

void loop() {
  if (millis() - lastTime >= 1000) { // Calculate every second
    noInterrupts();
    unsigned long count = pulseCount;
    pulseCount = 0;
    interrupts();

    rpm = count * 60.0; // Pulses per second * 60 = RPM
    Serial.print("RPM: ");
    Serial.println(rpm);
    lastTime = millis();
  }
}
🛒 Recommended: Arduino Uno R3 Beginners Kit — Complete kit with buttons, LEDs, and sensors to practise interrupt-driven projects.

Pin Change Interrupts: Use Any Pin as an Interrupt

The ATMega328P supports Pin Change Interrupts (PCINT) on every I/O pin, not just pins 2 and 3. However, PCINT triggers on any change (both rising and falling edges) and groups pins into three banks that share interrupt vectors. You must determine which pin triggered the interrupt manually.

// Pin Change Interrupt on pin 8 (PCINT0 bank)
volatile bool pin8Changed = false;
volatile byte lastPIN_B = 0;

void setup() {
  Serial.begin(9600);
  pinMode(8, INPUT_PULLUP);

  // Enable Pin Change Interrupt for pin 8 (PCINT0)
  PCICR |= (1 << PCIE0);    // Enable PCINT0 bank (pins 8-13)
  PCMSK0 |= (1 << PCINT0);  // Enable interrupt for pin 8
  lastPIN_B = PINB;          // Store initial state
}

ISR(PCINT0_vect) {
  byte current = PINB;
  byte changed = current ^ lastPIN_B;
  lastPIN_B = current;

  if (changed & (1 << PINB0)) { // Pin 8 changed
    pin8Changed = true;
  }
}

void loop() {
  if (pin8Changed) {
    pin8Changed = false;
    Serial.println("Pin 8 changed!");
  }
}

PCINT Banks:

  • Bank 0 (PCINT0-7): Digital pins 8-13 and the crystal pins
  • Bank 1 (PCINT8-14): Analog pins A0-A5
  • Bank 2 (PCINT16-23): Digital pins 0-7

For simpler implementation, use the EnableInterrupt library, which wraps PCINT configuration in an Arduino-friendly API similar to attachInterrupt().

Debugging Interrupt Problems

Interrupt-related bugs are among the hardest to track down because they involve timing and concurrency. Here are common issues and how to fix them:

Problem: Variables not updating. Cause: Missing volatile keyword. The compiler optimises away reads of non-volatile variables, so your main loop never sees the updated value.

Problem: Random crashes or lockups. Cause: Too much code inside the ISR, or calling functions that use interrupts (Serial, delay, millis inside ISR). Solution: Move all heavy processing to the main loop; use flags in the ISR.

Problem: Missed interrupts. Cause: ISR takes too long, or interrupts are disabled for too long in the main code. Solution: Shorten ISR execution time. Use noInterrupts() for the minimum possible duration.

Problem: Double-counting (bouncing). Cause: Mechanical switches bounce, causing multiple interrupts per press. Solution: Add a 100nF capacitor across the button (hardware debounce) or use a time-based debounce in the ISR (check if 50-200 ms has elapsed since the last interrupt).

Problem: Corrupted multi-byte variables. Cause: Reading a 16-bit or 32-bit variable without disabling interrupts. An interrupt between byte reads gives a value with the high byte from before the update and the low byte from after. Solution: Always wrap multi-byte reads with noInterrupts()/interrupts().

🛒 Recommended: Arduino Mega 2560 R3 Board — 6 external interrupt pins for complex multi-interrupt projects.

Frequently Asked Questions

How many interrupts can the Arduino Uno handle?

The Uno has 2 external hardware interrupts (pins 2 and 3) and 20 pin change interrupts (all digital and analogue pins). It also has timer interrupts (Timer0, Timer1, Timer2) and communication interrupts (UART, SPI, I2C). In practice, 2-4 active interrupt sources work well; more than that requires careful priority management.

Can I use interrupts with analogRead()?

Do not call analogRead() inside an ISR — it takes approximately 100 microseconds and blocks other interrupts. Instead, set a flag in the ISR and perform the analogRead in the main loop. Alternatively, use the ADC’s own interrupt to handle conversion completion asynchronously.

Do interrupts work during delay()?

Yes, hardware interrupts still fire during delay(). The delay() function uses a polling loop on millis(), and interrupts are enabled throughout. However, your ISR code does not “interrupt” the delay itself — the main code still waits for the full delay period after the ISR returns.

What happens if two interrupts fire at the same time?

The ATMega328P processes interrupts in order of priority (lower interrupt vector number = higher priority). INT0 has the highest priority among external interrupts, followed by INT1, then PCINT0, PCINT1, PCINT2, and so on. The lower-priority interrupt waits until the higher-priority ISR completes.

Conclusion

Interrupts are a fundamental tool for building responsive, reliable Arduino systems. Hardware interrupts respond to external events in microseconds, timer interrupts provide precise periodic execution, and pin change interrupts extend interrupt capability to every I/O pin. By following ISR rules (keep them short, use volatile, avoid Serial/delay), you can build projects that react to real-world events without polling overhead.

Master interrupts, and you unlock the ability to build rotary encoder interfaces, RPM counters, frequency meters, real-time controllers, and any application where timing and responsiveness are critical.

🛒 Recommended: Arduino Uno R3 Development Board — Start practising interrupts today. Browse all Arduino boards at Zbotic.in
Tags: Advanced, Arduino, interrupts
Share Post
  • Facebook
  • Linkedin
  • Whatsapp
UV Sanitizer Box: Disinfection...
blog uv sanitizer box disinfection chamber build 613245
blog voice recognition lock keyword based door access 613247
Voice Recognition Lock: Keywor...

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