Arduino Bike Speedometer

428K382182

Intro: Arduino Bike Speedometer

Monitor your road speed using the Arduino. This project uses a magnetic switch (also called a reed switch) to measure the speed of one of the bike's wheels.  The Arduino calculates the mph, and send this information out to the LCD screen on the handlebars as you ride.  It is compatible with any kind of bike/wheel, simply enter the radius of the wheel in the firmware to calibrate the device for your setup.



Parts List:

(1x) Arduino Uno REV 3 Radioshack 276-128
(1x) Switch-Magnetic Reed Radioshack 55050593
(1x) 10K Ohm 1/4-Watt Carbon Film Resistor Radioshack #271-1335
(1x) 9V Alkaline Battery Radioshack #23-866
(1x) Heavy-Duty 9V Snap Connectors Radioshack #270-324
(1x) PC Board with Copper Radioshack #276-147
(1x) Parallax 27977-RT Serial Backlit LCD Radioshack 276-120
(x2) SPST PC-Mountable Submini Toggle Switch Radioshack #275-645
(2x) Male Header Pins Jameco 103393
(1x) Female Pin Sockets Jameco 308567

Additional Materials:
22 Gauge Wire Radioshack #278-1224
Solder Radioshack #64-013
sand paper
plywood
wood glue
hot glue
screws
zip ties
sugru

Download Arduino IDE

STEP 1: Schematic

The schematic for this project is shown above.

It consists of three switches:
-one to connect to a 9V power supply
-one to switch the backlight of the LCD on and off
-a magnetic switch (called a reed switch) which closes each time the wheel completes one full rotation.

The Parallex LCD is designed to connect to the arduino using only three pins (ignore the labels and the other pins int his schematic).  One to 5V, one to ground, and a third to serial out (TX)- on the arduino, serial out is digital pin 1.

10kOhm resistors are connected to the reed and backlight switches to prevent excess current between 5V and ground (you should never directly connect 5V and ground on the arduino!)

STEP 2: Protoboard

Solder three rows of header pins on the protoboard so that the arduino will snap to it as shown in the images above.

STEP 3: Reed Switch

The reed switch is comprised of two pieces, a switch and a magnet. The switch has two wires extending out from it, when a magnet comes near the switch it causes a small mechanical piece to move and close the switch momentarily.

Solder a 10kOhm (current limiting) resistor between A0 and ground on the protoboard. Connect long pieces of stranded wire to A0 and 5V- these wires will wrap around the bike and attach to the reed switch.

STEP 4: Mount Reed Switch on Wheel

Secure both the magnet and reed switch to your bike wheel with electrical tape (either wheel is fine).  As shown in the images above, the magnet connects to one of the tire spokes and the reed switch connects to the frame of the bike.  This way, each time the bike wheel turns the magnet moves past the switch. Connect the leads form the reed switch to the long wires from your protoboard (orientation does not matter here- it's just a switch)

Use the code below to test your reed switch. When the magnet on the wheel moves past the switch, the arduino should print ~1023, otherwise it will print ~0. Open the serial monitor (Tools>>Serial Monitor) in Arduino IDE to test for your own setup.  If the magnet does not seem to be affecting the reed switch, try repositioning it or even adding a stronger magnet if you have one.

//arduino bike speedometer w serial.print()
//by Amanda Ghassaei 2012
//https://www.instructables.com/id/Arduino-Bike-Speedometer/

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
*/

#define reed A0//pin connected to read switch

//storage variable
int reedVal;

void setup(){
  Serial.begin(9600);
}


void loop(){
  reedVal = analogRead(reed);//get val of A0
  Serial.println(reedVal);
  delay(10);
}


STEP 5: Test Switch

Load the code below onto the Arduino. Turn on the serial monitor. It should output 0.00. Start turning the bike wheel, you should see a print of the current mph each second.

//arduino bike speedometer w serial.print()
//by Amanda Ghassaei 2012
//https://www.instructables.com/id/Arduino-Bike-Speedometer/

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
*/

//calculations
//tire radius ~ 13.5 inches
//circumference = pi*2*r =~85 inches
//max speed of 35mph =~ 616inches/second
//max rps =~7.25

#define reed A0//pin connected to read switch

//storage variables
int reedVal;
long timer;// time between one full rotation (in ms)
float mph;
float radius = 13.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);
  
  // 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 timer count 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 A0
  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 displayMPH(){
  Serial.println(mph);
}

void loop(){
  //print mph once a second
  displayMPH();
  delay(1000);
}


STEP 6: LCD

Solder a row of female header sockets on the copper side of the protoboard- three of these will be used to connect to the LCD screen. The LCD should fit nicely on top of the protoboard.

STEP 7: Install Parallax LCD Library

Connect Arduino 5V, Ground, and TX (Arduino digital Pin 1) to the LCD socket. Read the labels on the LCD pins to make sure you have everything oriented correctly.

STEP 8: Parallax LCD

The underside of the Parallax LCD has two switches and a potentiometer. The pot controls the contrast of the display- you can adjust this to what you like. The switches must be set as they are shown in the image above for proper functioning.

STEP 9: Test LCD

Test the following code. For some reason my LCD starts making noise and displaying random characters when I first upload, but works fine once I unplug and reconnect the usb connection. I think this may have something to do will interference from the arduino communicating with the computer via digital pin 1 (TX) during the upload.

The LCD should display "Hello World" when it is turned on.
//test of parallax 2x16 lcd
//by Amanda Ghassaei 2012
//https://www.instructables.com/id/Arduino-Bike-Speedometer/

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
*/

//this code should print "Hello World" on the LCD and the backlight switch connected to digital pin 2 should work


//Serial.write(13);//start a new line

void setup() {
  Serial.begin(9600);
  pinMode(1,OUTPUT);//tx
  
  Serial.write(12);//clear
  Serial.write("Hello World");
}

void loop() {
}  

STEP 10: Backlight Switch

Wire a switch as shown in the image above. Connect a 10kOhm resistor and a green wire to one lead, and a red wire to the other.

Connect the red wire to Arduino 5V, the other side of the resistor to ground, and the green wire to D2.

STEP 11: Final Speedometer Code

Upload the following code onto the Arduino. Test to make sure the backlight switch works and the speed displays properly.  (Again, you may have to unplug the board after loading the firmware and plug it back in again to get it to work properly.)

Measure the radius of your tire wheel (in inches) and insert it in the line: float radius = ''''';
//bike speedometer
//by Amanda Ghassaei 2012
//https://www.instructables.com/id/Arduino-Bike-Speedometer/

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
*/

//outputs speed of bicycle to LCD

//calculations
//tire radius ~ 13.5 inches
//circumference = pi*2*r =~85 inches
//max speed of 35mph =~ 616inches/second
//max rps =~7.25

#define reed A0//pin connected to read switch

//storage variables
float radius = 13.5;// tire radius (in inches)- CHANGE THIS FOR YOUR OWN BIKE

int reedVal;
long timer = 0;// time between one full rotation (in ms)
float mph = 0.00;
float circumference;
boolean backlight;

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


void setup(){
  
  reedCounter = maxReedCounter;
  circumference = 2*3.14*radius;
  pinMode(1,OUTPUT);//tx
  pinMode(2,OUTPUT);//backlight switch
  pinMode(reed, INPUT);
  
  checkBacklight();
  
  Serial.write(12);//clear
  
  // TIMER SETUP- the timer interrupt allows preceise timed measurements of the reed switch
  //for mor 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 timer count 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);
}

void checkBacklight(){
  backlight = digitalRead(2);
  if (backlight){
    Serial.write(17);//turn backlight on
  }
  else{
    Serial.write(18);//turn backlight off
  }
}

ISR(TIMER1_COMPA_vect) {//Interrupt at freq of 1kHz to measure reed switch
  reedVal = digitalRead(reed);//get val of A0
  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 displayMPH(){
  Serial.write(12);//clear
  Serial.write("Speed =");
  Serial.write(13);//start a new line
  Serial.print(mph);
  Serial.write(" MPH ");
  //Serial.write("0.00 MPH ");
}

void loop(){
  //print mph once a second
  displayMPH();
  delay(1000);
  checkBacklight();
}




I used timer interrupts in this piece of code to keep the variable "timer" incrementing at 1kHz.  More info about interrupts and an explanation of how I set it up can be found here.

STEP 12: Battery

Wire the battery connector and switch in series as shown in the first image above. Connect the read lead from the switch to Arduino Vin and the black wire from the battery connector to Arduino ground.

STEP 13: Enclosure

I cut my project enclosure from 1/4" ply on an epilog 120Watt laser cutter.  The dimensions of the enclosure are 3.5"x4"x2".  I modeled the box in AutoCAD and generated the laser cut files (with finger joints) in Autodesk 123D Make.  Then I added two holes for the switches and a rectangular opening for the LCD screen.  I also added some holes on the bottom of the enclosure to make attaching it to the bike easier.

I glued the project enclosure together with wood glue and sanded the edges down. I finished the enclosure with some clear polycrylic.

STEP 14: Install Components in Enclosure

Secure the switches onto the enclosure with nuts. Glue or screw the lcd to the underside of the front panel.
Fit the Arduino and Protoboard as well as the battery into the enclosure and secure with velcro or glue.
Screw or fasten the enclosure shut.

STEP 15: Attach to Bike

Wrap the reed switch wires around the bike frame, away from any moving bike parts. I used sugru and some zip ties to attach the speedometer to the handle bars.

STEP 16: Take It Out on the Road

You should be ready to hit the road. Don't let the awesomeness of your new bike speedometer distract you from road hazards!

163 Comments

If suppose my bike wheel is standstill and magnet is facing on the Reed switch then it close the circuit and speed is in high level. Is there any logic will include to when wheel is standstill speed is zero while magnet facing on switch?
You could add a wait time to take a new reading that is shorter than the trigger interval for a speed the bike would realistically travel. If the sensor doesnt change assume 0 mph, or have wait after the falling edge to avoid (but not prevent entirely) fluttering.

lets use a 12" diameter circle scribed by the magnet (so 6 inches from the hub) and 40 miles per hour (idk if you bike faster, but I figure its a nice uncomfortably fast upper limit for the sake of the explanation). At 12" diameter that gives about 37" circumference, and at 40mph the magnet and reed switch would be triggering around every 0.05356 seconds (or 18.67hz). Inserting a "do not accept any inputs that are faster than, say 0.025 seconds apart" should do the trick and only take a line or two of code to implement. But coding it is up to you 😉
hi, I have used bike speedometer cable to read the speed using hall effect sensor, it gets 6 peaks per revolution of my wheel. the voltage gradually increases from 0 to 4.9 and reduce to 0.0 and again to 4.9, repeat for 6 peak value like a sine wave for a single revolution. what modification to be done in this program to calculate the speed. please help out. ASAP
Set a threshold for a trigger event, and divide by the number of trigger events per rotation.
how would you modify the code to allow it to show you distance ridden?
I want to use an arduino pro mini, a lithium cell and a recharging board,
Take the circumference of the wheel (to get circumference from the radius (the center hub nut) use 2r*pi where r is radius (hub nut to outter edge of rubber. Or simply use d*pi where d is diameter since 2r returns the diameter) and pi is 3.14159 (though your calculator should have the pi symbol, π, in it already, you may need to change it to 'scientific mode'). Now that you have circumference, as the circumference is the distance around the wheel, it is therefore the distance the wheel will travel in one rotation (minus any squishing at the contact patch or loss of traction that could throw the distance traveled to wheel rotations calculations off of course). All you need to do now is set a variable to the circumference multiplied by the number of rotations, feed it to the display, and BAM! distance read out! Of course with a single trigger event per rotation it will only be in increments of the circumference, updating per rotation. And you might want to use your phone to track you for a ride and check the accuracy, you can add a modifier if it ends up ridiculously inaccurate for some reason (actual distance from phone over reported distance by bike will give you that value).

Thats the method, its up to you to code it 😉

Anyone I NEED HELP. It works perfect but when magnet is in front of the reed with no rotation, it gives some 40.38 like value.

You need to write a special code testing also if the magnet is stopped right over the sensor... I found similar situation but decided to rewrite nearly all the code though using the same approaches.
Hi! I tried your speedometer code based on the 1KHz timer, but I don't use the serial port; I display the information on another user Interface and the signal is read from a hall sensor conditioned to TTL by a voltage comparator. The speedometer full exemple that you wrote here seems not to be working correctly, however the general interrupt/polling idea was great and I could made a code myself. I changed it to 10 KHz for better precisionm and as I plan to use it on a motorcycle I added a section and pin input for RPM count inside the ISR. Thanks for the usefull information.

what its the max speed ?

Why does your timer variable only reachs 2001?

check weather you've connected a resistor to the voltage supply of reed instead of connecting to the ground. 5V = 1023 in analog in I guess

My reed switch test has a lot of noise. Is it because I haven't soldered the switch into the board?

You should add a resistor to ground in the connection of reed switch to Arduino

I connected the Arduino to a 16x2 LCD, but its not powering on. Will any 16x2 LCD turn on by just connecting those 3 pins mentioned?

For those asking about the mph calc:

mph = (56.8*float(circumference))/float(timer);

"circumference" is measured in inches

time per revolution of the wheel ("timer") is measured in milliseconds (0.001s)

miles/hr = 1/(inches per mile) * (miliseconds per hr) * (circumference / timer)

there are 63360 inches in a mile and 1000*60*60 = 3,600,000 ms in an hr:

mph = 1/63360*3600000*circumference/timer

mph = 56.8*circumference/timer

to do this in km/hr:

km/hr = 1/(inches per km) * (miliseconds per hr) * (circumference / timer)

km/hr = 1/39370*3600000*circumference/timer

km/hr = 91.4*circumference/timer

hi amanda , may i know which library u use for the coding .

Hi amanda,same request like PhremC "Which library u use for the coding"

it will help me.

More Comments