Reed Switch Speedometer

Hi guys,

So I'm doing a project in which I need to create a speedometer using Arduino Uno. I'm using a reed switch (this one to be specific: $1.49 - Magnetism Sensor / Reed Switch - Tinkersphere) to detect a magnetic field to track the rotation of a wheel to Arduino.

Here's the setup & code (mostly borrowed from (http://www.instructables.com/id/Arduino-Bike-Speedometer/):

The blue wire is going to GND, and yellow to digital pin 2.

#define reed 2//pin connected to read switch

//storage variables
int reedVal;
long timer;// time between one full rotation (in ms)
float mph;
float radius = 8.5;// tire radius (in inches)
float circumference;

int maxReedCounter = 100;//min time (in ms) of one rotation (for debouncing)
int reedCounter;


void setup(){

  reedCounter = maxReedCounter;
  circumference = 2*3.14*radius;
  pinMode(reed, INPUT_PULLUP);

  // TIMER SETUP- the timer interrupt allows precise timed measurements of the reed switch
  //for more info about configuration of arduino timers see http://arduino.cc/playground/Code/Timer1
  cli();//stop interrupts

  //set timer1 interrupt at 1kHz
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCNT1  = 0;
  // set timercount for 1khz increments
  OCR1A = 1999;// = (1/1000) / ((1/(16*10^6))*8 ) - 1
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS11 bit for 8 prescaler
  TCCR1B |= (1 << CS11);   
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);

  sei();//allow interrupts
  //END TIMER SETUP

  Serial.begin(9600);
}


ISR(TIMER1_COMPA_vect) {//Interrupt at freq of 1kHz to measure reed switch
  reedVal = digitalRead(reed);//get val of 2
  if (reedVal){//if reed switch is closed
    if (reedCounter == 0){//min time between pulses has passed
      mph = (56.8*float(circumference))/float(timer);//calculate miles per hour
      timer = 0;//reset timer
      reedCounter = maxReedCounter;//reset reedCounter
    }
    else{
      if (reedCounter > 0){//don't let reedCounter go negative
        reedCounter -= 1;//decrement reedCounter
      }
    }
  }
  else{//if reed switch is open
    if (reedCounter > 0){//don't let reedCounter go negative
      reedCounter -= 1;//decrement reedCounter
    }
  }
  if (timer > 2000){
    mph = 0;//if no new pulses from reed switch- tire is still, set mph to 0
  }
  else{
    timer += 1;//increment timer
  } 
}

    
void loop(){
  //print mph once a second
  Serial.println(mph);;
  delay(100);
}

Anyway, my problem is that when the magnet is not close, it prints the value 30.02 instead of 0.0 for some reason. When it is close, it quickly prints what appears to be the proper speed, but then goes back to 30.02 in the next println

Please change your original post to use code tags, not table tags. (Good effort though!)

 reedVal = digitalRead(reed);//get val of 2

if (reedVal){//if reed switch is closed

Your comment is wrong. This is true when the switch is OPEN. INPUT_PULLUP means that it's HIGH (or true) when the button isn't pressed.

30.02 is equivalent to one pulse per 100 milliseconds. Your code is just counting to 100 and using that value. It never gets to 2000, where it resets mph to zero.

This is an awfully convoluted way of doing things. The Arduino framework makes this so much easier with the millis() function. You don't need to use the ISR at all. You also will have errors if the time that the switch is closed is shorter than a millisecond - your code will miss that event. The Arduino can detect pulses on the order of nanoseconds, if it is coded properly.

If you want to use an interrupt, make it a pin interrupt that is called the moment the switch is called.

Then you use the millis() function to know how much time has elapsed since the previous time the interrupt was called, and then it's just a simple calculation to get to the speed.

Much simpler than your solution!

wvmarle is right, you want to use an interrupt – luckily, Arduino makes this easy. You can see a good tutorial here on the Arduino site. Interrupts take some getting used to, if you haven't used them before. Essentially, Arduino ties a pin to a function you define (called ISR). This can be triggered various ways, depending on what you need: when the pin has changed, when it's rising or falling, or when it's just high or low.

Here's some code I used to read a wind sensor, that works similarly. It's cut from a larger program, so it might not work without some testing.

const int sampleInterval = 2 * 60 * 1000;      // duration of sensor reading (ms)
const int speedPin =       9;                           // pin for speed sensor

// calc value to convert numRotations to mph
// 1 mph = 1600 revs per hour, therefore:
// speed in mph = pulsesPerPeriod * (2.25 / samplePeriodInSeconds)
const float mphConversion = 2.25 / (sampleInterval / 1000.0);

int numRotations;           // keep track of rotations to get wind speed
float windSpeed;            // vars for wind data
long prevMillis = 0;        // keep track of interval for reading/upload


void setup() {
  
  // speed sensor pin setup
  pinMode(speedPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(speedPin), updateRotationCount, FALLING);
}


void loop() {

  // if time, get sensor reading
  if (millis() > prevMillis + sampleInterval) {
    
    // calculate wind speed
    windSpeed = numRotations * mphConversion;           // convert from pulses to mph

    // reset timer and counter
    prevMillis = millis();
    numRotations = 0;
  }
}


// function to run when speed interrupt is triggered
void updateRotationCount() {
  numRotations += 1;
}