Introduction: Arduino Interrupts
This is a guide on implementing interrupts for your Arduino code. There is a lot of good information about interrupts out there, but this guide is part of a series on running your Arduino with tiny power consumption. And we mean tiny. We’re talking years of operation depending on the application…. On a coin cell battery. This is the stuff you need to know to create your future embedded or Internet of Things projects. Frankly, it’s ridiculous how much power a stock Arduino wastes. Using the various tricks in this guide series, you will be able to reduce power consumption by more than a factor of 1000! Sound interesting? Then this is the guide for you! If you like this guide, or want to see how we implemented interrupts in our own project, check out our website at:
Why Do I Need Interrupts?
The answer is simple, really. You need interrupts because without interrupts you cannot put your Arduino to sleep and expect it to wake up again (in general. There are ways to wake from sleep without interrupts in limited situations). And if you can’t put it to sleep, you’ll be guzzling power like a marathoner running through Death Valley in the middle of the summer guzzles water. Sleep mode consumes very, very little power… but it requires special effort to set up. The first thing you must know is how to write your code to take advantage of interrupts, then you can use the more powerful techniques.
WHAT is an interrupt?
An interrupt is a few things. But at its core, it’s a piece of code that gets called when a special action happens. What special action? Basically, when a certain pin has its voltage changed. On the Duemilanove/UNO, there are only 2 such pins that can qualify. But as you can see, this is a very broad requirement. You can use anything to change the voltage on those pins. It can be user input through a button, it can be a peripheral device like a sensor, it can be another microchip, it can even be an internal timer. The first thing you need to do to begin using interrupts is figure out how your application can be converted to have an external, electrical trigger. Often other devices that you want to interface to your Arduino will use interrupts to get your attention. Our previous instructable, How to Add Bluetooth to your Arduino Project, is a perfect example of interfacing an external device in such a way.
This guide will demonstrate how to wire an example circuit to fire interrupts. Then, we’ll provide some example code. Hopefully after seeing how it’s done, you’ll get into the right frame of mind to figure out how to apply an interrupt to your project.
Say I don’t care about Power Consumption. Do I still need interrupts?
Yes! Even if you don’t plan to put the processor to sleep, you may need interrupts! Interrupts are essential if you have a timing dependent application. This can mean you need an action to happen every x milliseconds, or that you need an action to happen immediately after an external event occurs. If you’re familiar with the basics of Arduino programming, you might be wondering why you can’t just use a while loop to check when it is time to perform your action. The typical solution is to have your while loop “poll” the state of the pin as fast as it can. This method will often work, but is unreliable. What happens if you are polling for a button press, but the button gets pressed and released before your while loop gets around to checking the button state again? You’ll miss the button press. What happens if you are looking for a very quick event, such as a signal from a sensor? You’ll have to be polling pretty much constantly and your program can’t do anything else for fear of missing the critical event. But with interrupts, you are 100% guaranteed to catch the event. Using an interrupt also saves you from having to check the state constantly- saving computation power and letting your while loop get to its other tasks faster.
Step 1: Circuit Overview
Here is the circuit you should build to create your first interrupt:
...Or at least it’s the circuit you SHOULD build. Fortunately most Arduino boards already have the LED and resistor built in, in exactly this configuration. They can be enabled in software. Unfortunately, just using the built-in functionality doesn’t show you everything that’s going on! So it’s worth your time to study this schematic and understand what it’s doing. The fact that this exact circuit is built in to Arduino already should give you a major hint as to how important it is.
The most significant part of this circuit is the 20k resistor connected to +5V. This is called a pull-up resistor. If the resistor had been connected to GND instead of +5V, it would be called a pull-down resistor (Arduino doesn’t have pull-down resistors built in, you have to add them externally). The pull-up resistor holds the pin it’s associated with at +5V normally. So left alone, the pin will always read HIGH. If you connect the pin directly to GND (say, by closing the button), the pin will be brought down to 0V and will read LOW. If we didn’t have the pull-up, we couldn’t get the button to do anything meaningful. Any Arduino pin can be connected to +5V internally by specifying pinMode( pin#, INPUT_PULLUP ). Note that the pullup is only meaningful when the pin is configured as an input. For more information on pull-ups check out the DigitalPins page.
The INTERRUPT_PIN should go to pin 2 or 3 on your Arduino UNO (or other 328p based Arduino). For other Arduinos, check this page to find an interrupt capable pin. For our code example we will wire it to pin 2. The LED connected to ANY_PIN is a straightforward circuit. Arduino already has an LED connected to pin 13, so we will use that but feel free to modify the circuit with your own LED on a different pin. The Arduino LED has an “L” next to it.
Buttons often have 4 pins - usually two of the pins are just internally connected to the other pin on the same side of the button. If you press the button and nothing happens, check that you actually wired your circuit across the pins that connect when the button is pressed, not across the pins that are always connected.
Step 2: Actual Wiring
With all that in mind, if you want to use the internal Arduino pullups and the onboard LED, here’s the super simple circuit that you actually have to wire.
Step 3: The Code
There are a few things worth pointing out here. The most interesting is line 10, where we configure the interrupt. attachInterrupt( ) takes 3 arguments. The first is the interrupt number, not the pin number. You don’t have to worry about this, just make sure to always use digitalPinToInterrupt( Pin# ) and it will be translated automatically. Remember, only certain pins can be turned into interrupt pins. The second argument is the name of the function you want your code to jump to whenever the interrupt is triggered. You can see how this function is defined on line 21.
The third argument to attachInterrupt( ) is, in this case, the keyword FALLING. This means that the interrupt will be triggered when the voltage on pin 2 goes from HIGH to LOW. You can also specify RISING, which will trigger when voltage goes LOW to HIGH; or you could use CHANGE, which will trigger when voltage changes in either direction. In our circuit, when the button is pressed, the voltage will go from high to low, and when the button is released, it will go from high to low. We don’t want to trigger the code twice every time the button is toggled, so we choose either RISING or FALLING. Your application might call for something different.
Trying it Out
If you load the code, fire up the Serial Monitor, and click the button you’ll notice that the LED doesn’t behave exactly as we intended. We want the LED to cycle between on and off every time the button is pressed. But for you, it will likely blink on and off a few times every time you hit the button. This happens because buttons aren’t perfect. Your finger might be depressing the button, but if you look at the microscopic level the button is actually bouncing off the contacts multiple times before closing completely. Every bounce will trigger an interrupt. Our system is so precise and so sensitive that we have actually introduced a new problem! To fix this we employ a routine known as debouncing to make the interrupt detection less sensitive.
Step 4: Example
Here’s one I whipped up. It isn’t perfect, there are certainly better ways, but it should be enough to get you started on using buttons.
The basic idea is to log the last time the button was pressed using the millis( ) function. (The millis( ) function tells you how many milliseconds have elapsed since the sketch started running). If the previous interrupt trigger happened less than 200 milliseconds before the current one, it’s likely that the button is bouncing, so no action is taken. Otherwise, everything happens normally. Any variables you intend to modify inside an Interrupt function should be declared as volatile so that the interrupt and main loop never disagree on the value.
Step 5: What's Next?
That’s it! Every time you click the button the LED should change state. You now have a working interrupt. It’s up to you to imagine ways to incorporate interrupts into your project. As you can see, interrupts are essential when timing and computation power are important. In the next guide we will cover how to put the ATMega328p into ultra-low power sleep mode, relying on interrupts to wake us up again. Until then, subscribe to us on Instructables and check out our product page at www.doteverything.co. We’re building Dot, an Internet of Things device that we hope you’ll find cool and useful, and through this series of guides we’re going to show you exactly how to build your own version of the hardware!