Exploring Arduino Interrupts

I’ve been wanting to add wind & rain sensors to my home-built Arduino-powered weather station and thanks to a recent sale at Sparkfun have acquired their nicely-built set.  As a result, though, I have two interesting problems to solve in getting those sensors integrated to the temperature and barometric pressure ones I have deployed today.  First, I need to work out where and how to install the wind/rain sensors as placement is critical in getting good readings  Of more immediate concern, however, is that I need to rethink my weather station application in order to properly integrate the new sensors, especially the anemometer and rain gauge.

At the moment my weather station code uses basic clock timing to read temperature and barometric pressure sensors periodically, average their values, and display the readings on a small attached OLED. Temperature and pressure sensors are continuous so their values are always available and can be read whenever desired just based on the Arduino’s internal clock. While my new wind vane sensor also provides a continuous reading, the new anemometer and rain gauge don’t work that way.  Instead they report data discontinuously whenever they have something to share.

In the case of the anemometer it closes an internal switch every time the cups rotate through one revolution.  Deriving wind speed from the anemometer means counting the number of revolutions over some specific period of time and calculating the number of revolutions per second. Multiplying revolutions-per-second by a fixed factor based on the size and geometry of the anemometer lets you determine average wind speed over that period of time.  You can’t simply poll the anemometer and ask it to tell you what it is observing as you can with temperature, pressure, or wind direction sensors.

Similarly the rain gauge can’t provide a continuously-available reading, but instead closes a switch every time its internal self-emptying bucket fills and dumps.  The volume of the tiny bucket is known, so keeping track of how many times it has emptied over a specific period of time lets you figure out how much rain has fallen.

Therefore a different approach is needed to read sporadic sensors like the anemometer and rain gauge and keep track of every time their internal switches close, whenever that turns out to be and no matter how often.  The asynchronous, switch-closing nature of those sensors is a perfect case for interrupt-driven data gathering.

A good overview on interrupts and how they work on Arduino is available over at EngBlaze. In my case I need to modify my weather station code to add logic to handle the calculations to be done whenever either the anemometer or rain gauge closes their internal switch. Just as with reading my other sensors, I need to devote an Arduino pin to each of the anemometer and rain gauge so I can detect their respective switch closures.  This turns out to be pretty easy as Arduino has already set aside pins to handle two external interrupts: interrupt number 0 (or INT0) on digital pin 2 and interrupt number 1 (or INT1) on digital pin 3.  All you need to do is connect the interrupt-generating device to one of those two pins, write a simple handler function you want to be called every time an interrupt occurs, and use the special built-in attachInterrupt function to associate your handler with the chosen pin.

A simple example is provided in the Arduino reference for attachInterrupt(). Here’s another one aligned with my intent to use interrupts to read my anemometer and calculate wind speed:

/*
 * Example program for Arduino external interrupts, in this case to read
 * an anemometer connected to digital pin 2 (which is INT0).
 *
 * Author: David Bryant (david@orangemoose.com)
 */

volatile int interruptCnt = 0;  // Counts anemometer interrupts

unsigned long sampleDelayMs = 30*1000; // Sample interval, milliseconds
unsigned long prevSampleMs = 0;        // Timestamp for previous sample

void setup()
{
  Serial.begin(9600);
  Serial.println("Gathering data...");

  // Attach our interrupt service routine to INT0
  attachInterrupt(0,anemom_cnt,RISING);
}

void loop()
{
  unsigned long currentMillis = millis();    // Get current timer value
  /* See if it is time to calculate wind speed */
  if( (currentMillis - prevSampleMs) >= sampleDelayMs) {
    Serial.print("Anemometer count: ");
    Serial.println(interruptCnt);

    prevSampleMs = currentMillis;   // Remember clock time
    interruptCnt = 0;               // Reset counter
  }
}

/*
 * Interrupt service routine called when the anemometer switch closes.
 * All it needs to do is increment the interrupt counter.
 */
void anemom_cnt()
{
  interruptCnt++;
}

This works well enough and is quite straightforward, but I’m not completely happy with it.  To add my anemometer and rain gauge I need two interrupts, but I’d like to make broader use of interrupts in my weather station code. For example, I’m currently using a push button to step through a series of screens on an attached OLED so a user can browse a variety of information from the sensor including maximum and minimum readings, system statistics, etc.  At the moment that push button is read periodically as part of the sensor timing loop but it’d make more sense to have it generate an interrupt to trigger stepping to the next screen.  It may also make sense to use interrupts to handle other sensors I could add, or support other system functions. Using all the available external interrupts just for the anemometer and rain gauge seems short sighted.

Happily the Arduino supports handling interrupts on every pin, both analog and digital, through a different mechanism than INT0/INT1 and their attachInterrupt function.  That means there’s lots of room for expansion, but it requires a bit more low-level knowledge and coding.  That’s a good subject for another blog post…