obligatory obscure reference


self-deprecating yet still self-promotional witty comment

2009/09/20

Hall Effect Sensors and Arduino Interrupts

Filed under: Arduino,Hacking — jet @ 16:51

The Hall Effect sensor (usually shortened to “Hall sensor”) is an amazingly useful bit of circuitry: a simple, easy-to-use component that responds to a magnetic field by changing the voltage level it emits. What it does is so simple it’s not even obvious why it’s useful until you start seeing where and how hall sensors are used. One of the more useful things you can do with a Hall sensor is to detect motion and to count motions without touching the thing you’re detecting. Imagine you want to detect if a door is open or closed, and you want to count how many times the door is opened or closed. The simplest solution might be to put a physical switch somewhere near a hinge or latch, but this is hard to do without interfering with the door’s regular operation. Instead of modifying the door, you could instead use a Hall sensor with a magnet. The magnet goes on the edge of the door and a Hall sensor goes on the door frame so that it lines up with the magnet when the door is closed.

Because Hall sensors output a voltage, the first solution for hooking it up to an Arduino might be to connect it to an analog pin, repeatedly read the value, and wait for it to change. This is called “busy waiting” or a “polling loop” in CS lingo and it is generally frowned upon for reasons best illustrated by a code example:

// if the door is opened, call a function that turns on alarms.  otherwise, turn them off.
loop()
{
  int i = analogRead(0);
  if (i > 0) {
    Serial.println("door is closed");
    DisableAlarms(); // a function that turns off all the flashing lights.
  }
  else {
     Serial.println("door is opened");
     DoAlarm(); // a function that turns on a bunch of flashing lights
     }
   }

Looks ok, right? And it mostly is, except for one thing. What happens if the door is quickly opened and closed while the DisableAlarms() function is running? What if you want to do other things in your script, like read a temperature sensor and print out the value? What if you want to keep track of how long the door was open the last time it was opened?

A common solution for this is to use an "interrupt". Interrupts aren’t always obvious at first, but once you understand how they interact with an Arduino script you’ll find all sorts of places to use them. (They’re instrumental to modern computing and the Wikipedia entry is a CS-heavy description as a result.)

An interrupt is exactly what it sounds like — it interrupts your Arduino script when an outside event happens, your script can do some simple task, then the script goes back to what it was doing earlier. The doorbell on your front door is an excellent example of an interrupt. You could go to the door every minute and check to see if someone is there or you can wait for the doorbell to ring. When it rings ("an interrupt occurs"), you temporarily stop whatever you’re doing and check to see who is at the door ("execute the interrupt routine"). When you’re done answering the door ("servicing the interrupt") you go back to whatever it is you were doing earlier.

So let’s say you want to know how long it’s been since the last time a door was opened. You could poll the Hall sensor, but your script has a lot of other things that need to be done, like display values from sensors or take input from other sensors. Instead of polling, let’s use an interrupt:

unsigned long doorNow = 0;
   unsigned long doorLast = 0;
   unsigned long doorDiff = 0;
   int doorCount = 0;
void setup()
   {
   // We need to tell the Arduino what to do when the interrupt happens and
   // the details of our interrupt.  We want to use interrupt #0, we want
   // to know when it's going from a low value to a high value, and we want
   // it to run the SetDoor() function.  See the arduino.cc reference for
   // the various options and pins available on your particular Arduino.
   attachInterrupt(0, SetDoor, RISING); 
   Serial.begin(9600);
   }
void loop () 
   {
   Serial.println(doorCount);
   Serial.println(doorDiff);
   }

 // this is the function that is called to service the interrupt
 // It checks the time, then computes the difference between now
 // and the last time the interrupt was serviced.
 void SetDoor()
 {
 doorCount++;
 doorLast = doorNow; 
 doorNow = millis();
 doorDiff = doorNow - doorLast;
 }
 

Note that the only thing that happens in loop() is printing the value of "doorDiff" and “doorCount”. We never actually go read the sensor to see if the door is opened or closed, we let the interrupt handler update the values of doorLast, doorNow, and doorDiff on every occurance of the interrupt.

Another common application of this is to compute the speed of a rotating object. Instead of putting the magnet on the edge of the door, put the magnet on the edge of a disc, say a gear or pully. Every time the magnet goes by we know the disc has made a revolution. We can use the time between revolutions to compute the current speed of the disc and we also know how many times the disc has revolved.

Interrupts are useful in other places and don’t always require Hall sensors. How many scripts have you seen (or written :-) that use digitalRead() to determine if a button has been pushed? You could easily use an interrupt to simulate an on/off switch using a simple pushbutton switch and an interrupt handler that did something like:

void ButtonPress()
{
if (buttonState == 1) {
    buttonState = 0;
}
else {
    buttonState = 1;
}

Then in your code, instead of doing a digitalRead() and hoping it’s while the button is pressed, you could just test the value of buttonState:

if (buttonState == 1) { // someone pushed the button!
    DoButtonStuff();
}

What are the downsides of interrupts on the Arduino? The biggest problem is that most Arduino only have two interrupt pins available. Another problem is that it’s risky to do anything complex in your interrupt handler and it’s a bad idea to call other functions from within your handler. When an interrupt handler is called, the Arduino temporarily stops doing everything else, so it’s best to stick with simple math and setting/testing global variables in your interrupt handler as in the above examples. If you do something involving input/output in your interrupt handler — say call Serial.println() — you might run into problems that are very difficult to debug.

Now, go make something and tell people what you did and how it works.

[tags]arduino, hall sensor, interrupt[/tags]

Powered by WordPress