Calculating wheel RPM with reed switch and interrupts

Hello everyone,

I'm new to this forum, and this is my first post here. I hope I'm not posting in the wrong place.

Alright, so I have built a laptop carrying robot that is powered by two DC motors. I'm using the Arduino as an interface between my laptop and the robot's electronics. I want the Arduino to tell my computer the RPM of the robot's wheels.

Since the motors I'm using don't have built-in encoders, I have used a reed switch/magnet combination.

To test it, I fixed a magnet to the robot's wheel, and connected a reed switch to the Arduino. I have set up hardware interrupts to fire (on a "falling" pulse) whenever the magnet passes the reed switch. It fires on a falling pulse because the reed switch connects pin 2 to ground whenever a magnet passes it.
I have also enabled the internal pull-up on pin 2 so that it normally stays high. See code below:

  pinMode(2, INPUT); //Pin2 is connected to the reed switch
  digitalWrite(2, HIGH); //Enable internal pull-up
    
  attachInterrupt(0, rpm_fun, FALLING);

The rpm_fun function just sends a message to the computer:

Serial.println("Received a pulse!");

Now, the problem is that this function fires twice whenever the magnet passes it. I tried turning the wheel slowly, and noticed that as soon as the reed switch senses the magnet, it fires the interrupt function twice. Why is that? Shouldn't there be just one "falling pulse" from high to low?

Looking forward to a response.

Hi,
Your reed switch might be bouncing, look up switch 'debouncing' and the strategies to get around it.

I have successfully used infra red to measure wheel speed, the advantage is that in high rpm applications there is no magnet to unbalance the wheels -

See here -

Duane B

rcarduino.blogspot.com

A reed switch is a mechanical switch - they can generate many (not just two) edges when switching because the contacts bounce.

BTW an interrupt routine shouldn't be doing anything time consuming like Serial calls at all, just set a few volatile variables and exit. Because of all the time spent in the serial calls you are only getting to see a single bounce, there are likely to be many on each edge.

The solution to bouncing contacts is "debouncing" - the simplest strategy is to ignore every edge for a short period (a few ms) after the first edge is detected. Only after that delay do you start believing the switch output again. There will be several tutorials and posts going into more detail if you search.

Seriously , you need to swap your reed switches for 'hall effect' sensors or you are going to spend all your time trying to fix-up a 'mechanical' issue related to the reeds and as they get older they start to get sticky so the bounce characteristics change.

If you cannot find 'hall effect' sensors on the market, take a old low voltage computer cooling fan (3 wire) or 3.5" disk drive and you will usually find one inside, on the pcb near the spindle.

Wow, thanks for the great feedback. I feel stupid that I assumed the reed switch wouldn't bounce. :stuck_out_tongue:

Duane, I like your idea of using an IR emitter/detector and using reflectance (instead of beam braking). Maybe I'll use this technique if the reed switch and magnet give me more headache.

I will also try hall effect sensors when I get one. I've noticed that most people use hall effect sensors (instead of reed switches) when making a magnet based wheel encoder.

Anyway, since all I have are reed switches to play with right now, I'm going to try to debounce them. I am trying a software based technique to debounce before trying hardware based techniques.

The code I came up with is:

volatile int counter = 0; //For counting number of times the magnet passes the reed switch
volatile long lastRiseTime = 0; //Time at which pin2 (interrupt 0) goes from LOW to HIGH


void setup()
{
  Serial.begin(9600);
 attachInterrupt(0, rising, RISING);
 attachInterrupt(0, falling, FALLING);
 
 pinMode(2, INPUT);
 digitalWrite(2, HIGH); //Enable pullup
}

void loop()
{
  Serial.println(counter, DEC);
}

void falling()
{
  //If more than 50 ms has elapsed since the last time pin 2 went high
  if ((millis() - lastRiseTime) > 50)
 {
   counter++;
 }
} 

void rising()
{
 lastRiseTime = millis();
}

I don't have an oscilloscope to see how the reed switch actually bounces, but I'm guessing it's something like this:

Right?

So, whenever pin 2 goes from High to Low (i.e. a magnet passes the reed switch), it checks how much time has elapsed since the last time pin 2 went from Low to High. If the elapsed time is greater than 50 ms, increase the counter. If the pin goes from High to Low very quickly (< 50 ms) because of bouncing, the counter will not be incremented.

The algorithm seemed very straightforward, but it did not work. :~ I think it's because the millis() function doesn't work in an ISR? Is that correct?

Another approach I used was to simply set a flag in the ISR (whenever pin 2 goes from high to low). Then, I check the flag in the main loop. If it is '1', I increase the pulse counter variable. I don't measure time at all. This surprisingly seems to work! I think it's because by the time the program returns to the main loop to check the flag variable, the reed switch is already done bouncing. Seems like a very cheap technique though.

Any more ideas for a better software debouncing algorithm I could use?

Thanks!

Or if you have an old mouse with a thumb ball they have 4 very nice hall effect sensors,
With some beautifully engineered spindle mounted magnets.
(ear wax and finger bogies extra)