Wednesday, February 27, 2013

We interrupt this program…

Let’s say you’ve got a rotating shaft or axle, and you’d like to use an Arduino to measure its speed, say RPM.  First you need some type of sensor.  I found these great little Hall effect (magnetic) sensors at SparkFun.  They’re great because they’re small, they’re sensitive, they’re easy to use, and best of all they’re cheap, under a buck!  I love cheap, especially when I can get something useful and high quality.  This will be a recurring theme in this blog!
SparkFun has links to the datasheet and a tutorial, so I’ll just describe the sensor at a high level: it’s a three terminal device with power, ground and an open-collector output.  The output can drive an Arduino digital input directly, by using the built-in pull-up resistor in the Arduino.  The output of this sensor latches high when brought close to a magnetic North pole, and latches low when brought close to a magnetic South pole.  This works out perfectly if you mount two magnets to the rotating shaft, with opposite poles facing outward.   Thanks to the latching output, you won’t have to worry about “debouncing” the signal to get an accurate reading. 
Next you’ll need some magnets for the Hall effect sensor to well, sense.  I found these small, but strong magnets at Radio Shack that work great, up to an inch away.
Here’s a tip for mounting the magnets to the shaft.  Let them come together, with opposite poles attracting, and use a Sharpie to mark the outside faces.  Let the ink dry, then pry them apart and mount them to the shaft with the marked surfaces facing out.  If the shaft is steel, simply stick the magnets to it, opposite each other.  Then wrap some black electrical tape around them to keep them in place.  For a more permanent mount, maybe use some quick epoxy.
Caution, I’m talking about low speed shafts here.  If you’re measuring say a router or a jet engine, you’re on your own!
OK, so you’ve got your magnets mounted to the shaft, with opposite poles facing out.  Use some thermostat wire or phone wire to connect the sensor to the Arduino: 5V, GND, and digital input 3.  Mount the sensor so the rotating magnets pass within about a half inch. 
Finally, it’s time for software.  I can’t decide which I like better about playing Arduino, hardware or software.  I guess that’s why I keep making new projects, so I get to do both!
We could write a sketch that reads the digital input state, and measures the time it spends high or low, by noting the number of milliseconds since reset.  That would give us the time per revolution.  But that code gets messy and busy, and it just seems like there ought to be an easier way. 
Luckily, two of the Arduino pins can generate interrupts, and interrupts are a perfect solution for this problem.  I used pin 3, which is interrupt number 1.  Here’s the sketch:
/*
  MeasFreqByInterrupt.ino
  miketranch@comcast.net
  open source, use at will
*/

const int encoderPin = 3; // digital input pin 3
const int intNum = 1;     // can generate interrupt number 1
const int LEDpin = 13;    // standard LED pin 13
boolean toggle = false;   // to toggle LED on/off
volatile long count = 0;  // total # interrupts

void setup() {
  pinMode(encoderPin, INPUT);     // set digit pin to input mode
  digitalWrite(encoderPin, HIGH); // enable pull up
  pinMode(LEDpin, OUTPUT);        // set LED pin to output mode
  Serial.begin(9600);             // init serial monitor
  Serial.println("\nMeasFreqByInterrupt\n");
}

void loop() {               
  count = 0;
  // enable interrupt handler, on falling edge of pin 3
  attachInterrupt(intNum, tally, FALLING);  
  delay(1000);             // measure for 1 second
  detachInterrupt(intNum); // disable interrupt handler
  Serial.print(count);   
  Serial.println(" Hz");
  toggle = !toggle;
  digitalWrite(LEDpin, toggle);
}

void tally() {
  count++;     // increment total
}