In the world of embedded systems and microcontroller programming, achieving real-time responsiveness is often a critical requirement. Whether it’s reading sensor data, detecting external events, or controlling actuators, the ability to respond swiftly and accurately can make all the difference. This is where Arduino interrupts come into play, offering a powerful mechanism to enhance control and efficiency in Arduino projects.
In our previous articles, we have discussed about:
They are important to read theories for the beginners. An interrupt is a signal that halts the normal execution flow of a program to immediately attend to a specific task or event. Instead of continuously polling for changes or waiting for certain conditions to be met, interrupts allow Arduino boards to respond swiftly to external stimuli.
---
In this article, the phrase Arduino will mean Arduino UNO R3 unless otherwise specified. Arduino supports two types of interrupts:
- External Interrupts: These interrupts are triggered by external events such as a change in voltage level, a signal pulse, or a button press. Arduino Uno, for instance, has two external interrupt pins (2 and 3), while Arduino Mega has more external interrupt pins available.
- Pin Change Interrupts: Some Arduino boards, like the Arduino Uno, also support pin change interrupts. These interrupts are triggered when any of the configured pins change their state.
Setting Up Interrupts in Arduino
Setting up interrupts in Arduino involves a few straightforward steps:
- Choose the Interrupt Pin: Decide which pin will be used to trigger the interrupt. For external interrupts, select one of the pins that support interrupts (e.g., pins 2 and 3 on Arduino Uno).
- Attach Interrupt: Use the
attachInterrupt()function to specify which function should be called when the interrupt occurs. This function takes three arguments: the interrupt number or pin, the name of the function to call, and the mode of the interrupt (e.g.,CHANGE,RISING,FALLING). - Define Interrupt Service Routine (ISR): Create a function that will be executed when the interrupt occurs. This function, often referred to as the Interrupt Service Routine (ISR), should be short and efficient to minimize disruption to the main program.
Here’s a simple example demonstrating how to set up an external interrupt on Arduino Uno:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | int led1 = 7; int led2 = 8; int led3 = 13; int interrCount = 0; void setup() { pinMode(led1, OUTPUT); pinMode(led2, OUTPUT); pinMode(led3, OUTPUT); attachInterrupt(digitalPinToInterrupt(2),interruptGiallo,CHANGE); } void loop() { interrCount++; digitalWrite(led1, HIGH); digitalWrite(led2, LOW); delay(1000); digitalWrite(led1, LOW); digitalWrite(led2, HIGH); delay(1000); if (interrCount == 5) { interrCount=0; digitalWrite(led3, LOW); } } void interruptGiallo() { digitalWrite(led3, HIGH); } |
Here is the simulation:
The Arduino external interrupt pins can be configured to have one of the these modes:
- RISING: Interrupt fires when the signal moves LOW to HIGH
- FALLING: Opposite of the above. Interrupt fires when the signal moves from HIGH to LOW
- CHANGE: Interrupt fires when the signal changes (any of the above)
- LOW: Interrupt fires whenever the signal is held LOW
To write an ISR handler function, we need to name it ISR(vector_name) and include the vector name in the space of the argument. The vector name for each interrupt signal for each type of microcontrollers are distributed as tables. You can web search with “Arduino UNO interrupt vector table”, “ESP32 interrupt vector table” etc. Here is an example with some boards:

To write ISR handler function, we need to name it ISR(vector_name) and include the vector name in the space of the argument. Interrupt is super complicated topic. I will suggest reading these webpage:
1 | https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/ |
Best Practices and Considerations
When working with interrupts in Arduino, it’s essential to keep certain best practices in mind:
- Interrupt Service Routines (ISR) should be brief and focused on handling the interrupt task efficiently. Avoid performing time-consuming operations or delays within the ISR.
- Variables shared between the main program and the ISR should be declared as volatile to ensure their values are properly updated and synchronized.
- Serial communication functions (e.g., Serial.print()) should be avoided within ISRs as they can cause delays and disrupt the timing of the system.
- Interrupt latency refers to the delay between the occurrence of the interrupt and the execution of the ISR. Minimizing interrupt latency is crucial for real-time systems.
Applications of Arduino Interrupts
Arduino interrupts find applications in various domains, including:
Button and Switch Debouncing: Interrupts can be used to detect button presses or switch toggles reliably, eliminating the need for polling and debounce algorithms.
Sensor Readings: Interrupts are useful for capturing sensor readings precisely and in a timely manner, especially in applications where timing is critical.
Pulse Counting and Frequency Measurement: Interrupts can be employed to count pulses or measure the frequency of external signals accurately.
Time-Critical Control Tasks: In applications requiring time-critical control tasks, interrupts ensure that critical events are handled promptly without delay.
Conclusion
Arduino interrupts provide a powerful mechanism for handling real-time events and improving the responsiveness of embedded systems. But it is complicated too. By understanding how interrupts work and following best practices, Arduino developers can leverage this feature to design more robust and efficient applications. Whether it’s detecting button presses, capturing sensor readings, or performing time-critical control tasks, mastering Arduino interrupts opens up a world of possibilities for building innovative projects.