Tutorial: Using Interrupts to improve the functionality of your Arduino projects

Please put some money in the tip jar by clicking on the donate button to support me so I can continue creating contend like this. P.S. please donate more than $1 as PayPal takes minimum $0.30 per transaction

A couple of weeks back I wrote a short tutorial on using timers instead of delay() functions to make your Arduino projects more responsive to input from buttons and sensors. Using interrupts is a different way to achieve the same result. By attaching an interrupt to a digital pin the Arduino will halt what it is doing to handle the input as it happens making your project more responsive. 

Explaining what an interrupt is and does.

Let’s say you are listening to your music with your noise canceling headphones on. At that point somebody calls you on your cell phone. Of course you don’t hear your phone ring as your music is loud and you are distracted by it. Luckily there is someone in the room with you who hears the ringing. This person pokes you in the side to let you know that your phone is ringing and tells you can answer it.

An interrupt works much the same. Your Arduino is distracted by running the code you wrote. It might not notice inputs from buttons or sensors. The interrupt is the person in the room that tells the Arduino to respond to the button press. 

Most Arduinos have Interrupts attached to hardware pins. The table below has a list of Arduino’s and what pins have interrupts attached to them. If your board is not on this list just check the specs of your board to see what pins have an interrupt attached to them.

Board

int.0

int.1

int.2

int.3

int.4

int.5

Uno, Ethernet

2

3

 

 

 

 

Mega2560

2

3

21

20

19

18

32u4 based (e.g Leonardo, Micro)

3

2

0

1

7

 

You can attach an input sensors or buttons to one of those pins. When the sensor/button provide input to one of the interrupt pins your Arduino will halt what it is doing and run the code you attached to that pin.

The Code

In your setup() function you would have a line to attach the interrupt to the pin.

attachInterrupt(digitalPinToInterrupt(pin), ISR, mode);
  • pin: is the number of your hardware pin you want to use
  •  ISR: is the function that contains the code that you want to run
  •  Mode: defines when the interrupt should be triggered. Four constants are predefined as valid values:
  •  LOW to trigger the interrupt whenever the pin is low,
  • CHANGE to trigger the interrupt whenever the pin changes value
  • RISING to trigger when the pin goes from low to high,
  •  FALLING for when the pin goes from high to low.

Here is a simple snippet of code that you will find when looking for this topic online. It connects a pushbutton to pin 2 of an Arduino Uno. When you press the button the led on pin 13 will either turn on or of

const byte ledPin = 13;
const byte interruptPin = 2;
volatile byte state = LOW;

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), blink1, CHANGE);
}

void loop() {
  digitalWrite(ledPin, state);
}

void blink1() {
  state = !state;
}

Notice the variable state has the volatile property added to it. This stores the variable in a memory space so that when changed by the interrupt function it gets changed throughout the whole sketch. It will become clear why that is required later on.

Example

To illustrate the effect of the Interrupts we are going to create a project that has 4 LED's that will blink in sequence with a one second interval between them. You will have a push button that will light all the lights at once when pressed. Below is a list of materials you need. You also have to download the two example files.

interrupt_demo1.ino uses a normal digitalRead() to look for the input from the button. In this example it can take up-to 4 seconds before the button will execute. In the interrupt_demo2.ino we use the attachInterrupt() function to read the button input. In this case the button responds instantly.

Downloads:

Fritzing Bill of Materials

Part List

Amount Part Type Properties
2 Red LED color Red ; package 5 mm [THT]; leg yes
2 Yellow LED color Yellow ; package 5 mm [THT]; leg yes
1 Arduino Uno (Rev3) - ICSP type Arduino UNO (Rev3) - ICSP
4 330Ω Resistor resistance 330Ω; tolerance ±5%; bands 4; package THT
1 Pushbutton package [THT]



In the first example we use the button without the interrupt attached on on pin 2 and use the conventional way to look at inputs. When pressing the button you will experience delays in execution and it will seem the button is not functional. You will notice that I don't use a pull up resistor on the button. We use the internal pull up resister on pin 2 with by using this code:

pinMode(interruptPin, INPUT_PULLUP);

In example 2 we use the interrupt connected to pin 2. The respoce of the button press is almost instantaneously. When the interrupt is triggered by pressing the button the Arduino will execute the code in the function blink1(). The moment that code has been executed it will return  to where it previously left  off.  This is why I have the if statement (as you see below) behind every delay to bring us back to the beginning of the loop. 

if(state==LOW)return;

Without the if statement the state variable would have changed it's value, but the program would not use this until it ran through all the code and would reach the top of the loop.

In conclusion

The attachInterrupt() function works great for buttons, but in practice it gets used more for input from sensors like IR receivers, or motion sensors. Be aware to use the volatile property for global variables that get changed by the interrupt.

Also remember that the program will resume where it left off when the code in the interrupt function has finished. If you want to change that you need to add conditional statements that alter the path of your code.