Interrupts are a handy way of having the micro-controller listen for triggers and respond to them, instead of polling, constantly checking, the controller listens while getting on with other things, this is analogous to when the door bell rings you interrupt what you are doing to answer the door rather than repeatedly going and checking the door every few minutes to see if anyone is there. The interrupt is handled by micro-controller hardware and reacts very quickly and efficiently to a detected event, different micro-controllers have different interrupt pins available, for example the one used in the original Uno only has two digital pins available but on more recent models such as the Genuino 101 or the Teensy 3.2 all digital pins can be used.
In your program when an interrupt is triggered the action performed by your function – the Interrupt Service Routine (ISR) should be kept simple so as not to keep the controller occupied and possibly miss other trigger events, interrupts are for listening and monitoring to tell your main program that something has happened. The micro-controller can react to four different types of trigger:
- LOW – trigger whenever the pin is low
- CHANGE – trigger whenever the pin changes value
- RISING – trigger when the pin goes from low to high
- FALLING – when the pin goes from high to low
On the oscilloscope screen above we can see these conditions on a square wave, the change trigger can be said to be all the corners. If your input is being pulled high and goes low when triggered (as shown), then choose Falling or Low, it is better to detect a falling or rising edge as using low can cause the interrupt to trigger multiple times for the duration of the low state.
Hardware
To illustrate the use of interrupts I have written a program to find the rotation and wind speeds of a 30cm Desk Fan. To do this I added a hall effect switch and magnet to find the rotation speed of the blades and bought a cheap Anemometer to find the speed of air being pushed out by the fan.
To monitor the speed of the fan I have used a A1120EUA-T Hall Effect Switch and 10K pull-up resistor and glued this to the body of the fan near the motor shaft and on the shaft itself glued a small magnet.

The Anemometer is not particularly robust and could be seriously damaged by a pigeon. Taking the cap off you will find a bearing to allow the top to rotate freely as well as a magnet, inside there is a small reed switch that closes every time the magnet passes over it. I have connected this to the Teensy in the same way as would a button switch with a pull-down resistor.


The the reed switch in the anemometer creates a bit of noise when it closes as you can see on the oscilloscope below, these are picked up as false positives by the micro-controller and will give you bad readings. Fortunately this can be fixed in software by having the interrupt handler ignore further events for a few milliseconds.

A thing I found useful when testing this was to have the multimeter set to its frequency counter (Hz) and check that roughly the same cycles per second were displayed on the meter and on the Arduino serial out.
Software
The Arduino commands to look out for are attachInterrupt and digitalPinToInterrupt to convert the named digital pin as you see on the board/piece of card to the interrupt name used by the controller. There are a few rules to remember when using interrupts:
- Keep it simple – the function – Interrupt Service Routine (ISR) called by your interrupt should be short and simple, any extra data processing should be done in the main program.
- Delay() won’t work, and don’t use Serial.print()
- Make variables shared with the main code volatile – this tells the complier that the variable can change at any time, so it must be reloaded every time its referenced.
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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
#include <math.h> #define FAN_PIN 14 #define FAN_LED 23 #define ANEMOMETER_PIN 15 #define ANEMOMETER_LED 22 // all global variables used in a interrupt function need to set as volatile volatile int fanCount = 0; volatile byte fanState = LOW; volatile byte anemometerState = LOW; volatile int anemometerCount = 0; volatile unsigned long lastAnemometerMillis = 0; // reed switch debounce time, keep this as low as possible // set long enough not to give you false readings volatile unsigned long anemometerTimeoutMillis = 10; boolean isrMonitorON = true; int fanRPM = 0; int anemometerRPM = 0; unsigned long lastMillis = 0; const int anemometerRadius = 70; // in mm, the distance from the centre of the anemometer to the middle of a cup // fan Interrupt Service Routine (ISR) void rpmFan() { if (isrMonitorON == false) { return; } fanCount++; fanState = !(fanState); } // anemometer Interrupt Service Routine (ISR) void rpmAnemometer() { if (isrMonitorON == false) { return; } // the reed switch in the anemometer makes a fairly rough signal, so we ignore further interrupts // for the next anemometerTimeoutMillis - switch bounce fix in software if (millis() - lastAnemometerMillis > anemometerTimeoutMillis) { anemometerCount++; anemometerState = !(anemometerState); lastAnemometerMillis = millis(); } } // convert the RPM to m/s float calcMetersPerSecond(int rpm) { float r = (float) anemometerRadius / 1000; // convert to meters float n = (float) rpm; return (2 * M_PI * r * n) / 60; } void setup() { delay(1000); Serial.begin(9600); Serial.println("ready"); pinMode(FAN_PIN, INPUT); attachInterrupt(digitalPinToInterrupt(FAN_PIN), rpmFan, RISING); pinMode(FAN_LED, OUTPUT); digitalWrite(FAN_LED, LOW); pinMode(ANEMOMETER_PIN, INPUT); attachInterrupt(digitalPinToInterrupt(ANEMOMETER_PIN), rpmAnemometer, RISING); pinMode(ANEMOMETER_LED, OUTPUT); digitalWrite(ANEMOMETER_LED, LOW); } void loop() { digitalWrite(FAN_LED, fanState); digitalWrite(ANEMOMETER_LED, anemometerState); if (millis() - lastMillis >= 1000) { isrMonitorON = false; // stop updating the counters while calculating and displaying fanRPM = fanCount * 60; // convert frequency to RPM. anemometerRPM = anemometerCount * 60; Serial.print("fan: "); Serial.print(fanRPM); Serial.print("rpm\t"); Serial.print(fanCount); // if your multimeter has a frequecy counter, use this number to check against when testing Serial.print("Hz\t anemometer RPM: "); Serial.print(anemometerRPM); Serial.print("rpm\t wind speed: "); Serial.print(calcMetersPerSecond(anemometerRPM)); Serial.print("m/s\t"); Serial.print(anemometerCount); Serial.println("Hz"); fanCount = 0; anemometerCount = 0; lastMillis = millis(); isrMonitorON = true; // start updating the counters again } } |
Data
Here is a chart of the information gathered, it might even be interesting for someone with an interest in aerodynamics, anemology or something. The fan is a three speed SFC-300BP 12″/30cm Air Circulator and the Anemometer was placed one meter away from the fan positioned to capture the strongest wind.
fan | anemometer | ||||
setting | RPM | Hz | RPM | Hz | m/s |
1. | 1045 | 17 | 150 | 2.5 | 1.10 |
2. | 1223 | 20 | 201 | 3.35 | 1.47 |
3. | 1366 | 22.7 | 214 | 3.5 | 1.5 |
I took 60 readings for each fan speed then averaged them, in my program I used the following formula to work out the wind speed from the Anemometer in meters per second:
where:
v = velocity in meters per second
r = radius in meters – distance from the centre to the middle of the cup
N = Revolutions Per Minute (RPM)
Links & Sources
- RPM To Linear Velocity Calculator: http://www.calctown.com/calculators/rpm-to-linear-velocity
- A1120EUA-T Hall Effect Switch Datasheet at Hobbytronics
- Teensy Interrupts https://www.pjrc.com/teensy/interrupts.html
- Measuring RPM on the Arduino
- Further reading: Discussion on Arduino Interrupts