Bike speedometer

Hello everyone, I want to make a speedometer for my bike but I came to the first doubt on the codes...

How do I transform the inputs from the magnetic sensor that just says 0 and 1 into speed measurement in Km/h, I mean the code.

I'm new, sorry for anything.
Thank you for the help...

did you try to Google "arduino bicycle speedometer"

Hello and welcome,

Measure the wheel diameter, multiply by Pi, you get the circumference of the wheel (distance travelled by one revolution of the wheel).

Measure the time it takes for the wheel to make one revolution (time between two '1' from the sensor)

With these two measurements, you have a speed (distance/time), then all you have to do next is convert to km/h.

caiopoit:
How do I transform the inputs from the magnetic sensor that just says 0 and 1 into speed measurement in Km/h, I mean the code.

At first you need the exact circumference of the rotating wheel in millimeters.

For getting the wheel circumference , you can do either

  • a estimated guess/calculation based on the wheel size
  • a measurement using the "mustard method"

The "mustard method" works like that:
Put a small amount of mustard on top of the wheel, then sit on the bicycle and drive straight so that two offprints of mustard appear on the ground. Measure the distance from the end of the first offprint to the end of the second offprint in millimeters.

Hint: The "mustard method" works the same with ketchup. :grin:

The rest is math. Velocity = distance divided by time.

If you divide the circumference in millimeters by the time in microseconds, the resulting velocity is in millimeters per microseond. So the velocity in km/h is:

int circumference=2220; // in millimeters
long time = 1000000; // measured time for one rotation in microseconds
float speedKMH= 3600.0 * circumference/time;

Well, actually the part off the mathematics I know, but what I couldn't find was how to make the code for the Arduino board... I saw some codes but they use system interrupts so I think that it's not possible to drive the LCD while measuring something with interrupts... Can someone give me a basic structure of the code?

Thanks everyone!

I saw some codes but they use system interrupts so I think that it's not possible to drive the LCD while measuring something with interrupts... Can someone give me a basic structure of the code?

This is not correct. Use the example codes with interrupts to get a basic tachometer sketch working. I would recommend that you count pulses for a fixed period of time. I think it is the easiest method and works well with periodic update of the display. It is usually accurate enough

Use the methods of the "Blink without Delay" digital example to update the display of the the speed in the Serial monitor every second. Here's an example without the speed math. Do the speed math in the loop, and not in the ISR.

Convert the serial output to lcd output.

volatile unsigned long  count = 0;
unsigned long copyCount = 0;

unsigned long lastRead = 0;
unsigned long interval = 1000;

void setup()
{
  Serial.begin(115200);
  Serial.println("start...");

  pinMode(2, INPUT_PULLUP); //external interrupt on pin2
  attachInterrupt(0, isrCount, RISING);
  
}

void loop()
{
  if (millis() - lastRead >= interval) //read and display at fixed intervals
  {
    lastRead  += interval; 
    // disable interrupts,make copy of count,reenable interrupts
    noInterrupts();
    copyCount = count;
    count = 0;
    interrupts();

    Serial.println(copyCount); //raw counts in time period
  }
}

void isrCount()
{
  count++;
}

I'm going to test this tomorrow, but one more thing that I suspect that is going to be more challenging is how can we also show the average speed, the time riding and distance traveled? Is that possible? Or there is no enough memory for it? I plan to do that with a Nano... Also if possible use the sensor in a analog pin since the lcd uses several digital pins...

You do not need interrupts for this. The features you mention above are also just math formulas. Yes, you can configure an analog pin as a digital input.

caiopoit:
Well, actually the part off the mathematics I know, but what I couldn't find was how to make the code for the Arduino board... I saw some codes but they use system interrupts so I think that it's not possible to drive the LCD while measuring something with interrupts...

The other way round is correct: You can always count pulses in the background using interrupts, while updating a slow display.

But if the display is slow, it might not be possible to count all the pulses with polling the sensor.

Let's calculate: If the sensor magnet is 5mm and the circumference of the wheel is 2220mm and the wheel is spinning at 4 rotations per second, that results in:

  • driving speed = 4*2.22 m/s = 8.88 m/s = 31.97 km/h
  • each rotation takes 1000ms/4= 250 ms
  • each pulse of the magnet is 5/2220* 250ms = 0.56 ms = 560 µs

In that case it is safe to poll the sensor, if the display can be updated in less than 560 µs. In case updating the display takes a longer time, you may miss pulses from the reed switch contact.

On the other side it is possibe to count pulses with using interrupts, and display the results in a 1 or 2 second time frame, even if the display is slow.

BTW: When you are talking about "bike", do you mean a "bicycle" or a "motor bike"?

I'm not quite sure about. Both "bicycle" and "motor bike" have similar wheel size, but typically the speed range for the speedometer is different.

Oh, yes, I forgot about the display. Well, luckily the interrupt code for this is very simple. It will give more reliable timing.

I mean bicycle, no engine, just me. I would like to update the display at least twice a second, the display is a usual 16x2 characters...

This project is very common on this forum. Please type "bicycle speedometer" into the search box at the upper right of this web page. You will find everything that you need to know.

On the arduino website it didn't find anything. I found one code similar to what I want so I'll try to adapt it... Google only found 2 projects like this.

If I recall correctly, there's something in the Playground about measuring RPM. (And speed is linearly related to that.)

I'm using this code, but it seems to have a bug while measuring speed... Can someone find it? Please... There's also a little bit of junk on it...

////////////////////////////////////
///////Headers/libraries//////////////////////
///////////////////////////////

#include <avr/pgmspace.h>
#include <stdlib.h>
#include <LiquidCrystal.h>
#include <Wire.h>
//#include "Timer.h"

////////////////////////////////////
/////Variables to set/////////////////////////
///////////////////////////////

//set metric = false to use miles
boolean metric = true;
//wheel circumference in centimeters
float wheelC = 212.5;

////////////////////////////////////
///////Pins to set////////////////////////////
///////////////////////////////

/*
Note: Change the pin numbers according
to your individual circuit. For most pins,
it doesn't matter whether you use analog
or digital I/O.
*/

const int photoPin = A0;
const int reedPin = A1; //any input
const int turnRLED = A5; //any output; pin used to turn on button LED
const int turnLLED = A4; //any output; pin used to turn on button LED
const int cs = 10; //any output; LED panel, white wire
const int blueBack = 9; //analog out (~PWM pin); LCD pin #18
int light = 0;
/*
for LCD pin wiring: RGB Backlit LCDs | Character LCDs | Adafruit Learning System
LCD# - Arduino#

1 - GND
2 - 5V
3 - LCD potentiometer (not connected to Arduino)
4 - LCD1 (I used pin D2)
5 - GND
6 - LCD2 (I used pin D3)
7 through 10 aren't connected to the Arduino
11 - LCD3
12 - LCD4
13 - LCD5
14 - LCD6
15 - 5V
16 - redLCD, ~PWM pin (I used pin D5)
17 - greenLCD, ~PWM pin (I used pin D6)
18 - blueLCD, ~PWM pin (I used pin D9)
16 through 18 control the color of the LCD backlight
*/

////////////////////////////////////
///////Bike Variables/////////////////////////
///////////////////////////////

///////Reed Switch//////////////
int circleNum = 0;
float odometer = 0;
float miles = 0;
float kilometers = 0;
float speedometer = 0;
float MPH = 0;
float KPH = 0;
int reedTime = 0;
int reedTimeDelta = 0;
boolean reedOn = false;

///////LCD/////////////
//LiquidCrystal lcd(LCD4, LCD6, LCD11, LCD12, LCD13, LCD14);
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
int LCDButton;
//int brightness = 50;
//int red = 130;
//int green = 222;
int blue = 219;
int counter;
boolean brightUp;
boolean backlightOn;

//////////Timer Variables///////
/*int blinkTime = 1000;
Timer scrollTimer;
int scrollTime = 130;
Timer strobeTimer;
int strobeTime = 100;
boolean blinkOn;
*/
///////////////////////////////////////////////////////////////
//////////////////////////SETUP//////////////////////
///////////////////////////////////////////

void setup() {
lcd.begin(16, 2); // set up the LCD's number of rows and columns:
lcd.print("By Caio M. Poit");
{
light = analogRead (A0); //Backlight control via LDR

analogWrite(blueBack, 255 - light / 4);
}

// scrollTimer.every(scrollTime, scroll);
// strobeTimer.every(strobeTime, strobe);

pinMode(blueBack, OUTPUT);
delay(5000);
}

///////////LOOP/////////////////////

void loop() {

// scrollTimer.update();
// strobeTimer.update();
checkReed();
printSpeed();
printDistance();

{

light = analogRead (A0); //Backlight control via ldr

analogWrite(blueBack, 255 - light / 4);
}

}
////////////////CHECK/////////////////////

void checkReed() {
int r = digitalRead(reedPin);
if (r == 1 && reedOn == false) {
reedOn = true;
reedTimeDelta = millis() - reedTime;
reedTime = millis();
circleNum++;
}
else if (r == 0 && reedOn) {
reedOn = false;
}
}

/////////////////////PRINT/////////////////////

void printSpeed() {
speedometer = wheelC / reedTimeDelta;
if (metric) {
KPH = speedometer * 36;
lcd.setCursor(1, 0);
lcd.print("Km/h: ");
lcd.setCursor(6, 0);
lcd.print(KPH, 2);
}
else {
MPH = speedometer * 22.369;
lcd.setCursor(1, 0);
lcd.print("MPH: ");
lcd.setCursor(6, 0);
lcd.print(MPH, 2);
}
}

void printDistance() {
odometer = wheelC * circleNum;
if (metric) {
kilometers = odometer / 100000;
lcd.setCursor(1, 1);
lcd.print("KM: ");
lcd.setCursor(5, 1);
lcd.print(kilometers, 2);
}
else {
miles = odometer / 160934.4;
lcd.setCursor(1, 1);
lcd.print("Miles: ");
lcd.setCursor(5, 1);
lcd.print(miles, 2);
}
}

And can you put all { on new lines, and ditch the useless curly braces?

if (r == 1 && reedOn == false) {
    reedOn = true;
    reedTimeDelta = millis() - reedTime;
    reedTime = millis();
    circleNum++;
  }
  else if (r == 0 && reedOn) {

And reedOn what? If you are going to use the shortcut, omitting == true (which I endorse), then get rid of the == false bit, too.

if(r == 1 && !reedOn)

caiopoit:
I'm using this code, but it seems to have a bug while measuring speed... Can someone find it? Please... There's also a little bit of junk on it...

A couple of years ago I posted a speedometer code in the German forum;
http://forum.arduino.cc/index.php?topic=172506.msg1289260#msg1289260

It would be much easier for me to translate the old German code to English than to find the bug in your code.

One problem in your code seems to be formatting of float values:

   lcd.setCursor(6, 0);
    lcd.print(MPH, 2);

If the number of digits is changing, this will display very strange speeds on the LCD. Let's say you first display a speed of "12.34" MPH, and then a speed of "6.78" MPH. output will start on column 6 of the LCD, so you can observe:

Display initially shows:
     12.34
then display changes to:
     6.784

when number of digits before the decimal point decreases, you see suddenly a third digit behind the decimal point, because that is never cleared in your code. I'd better format the output to a fixed length of 5 characters, so you can display speed from " 0.00" (first character is space character) up to "99.99".

BTW: In the linked thread in the German forum I also posted a "big digit version" of the speedometer later in the thread, so that by creating and using "user defined" characters I could use very big digits for displaying speed, like in that youtube video:

My code is for metric system km/h only, just let me know if you need different non SI-units and have problems to calculate them for display.

The big digit looks very good. Can you provide your code in english with 2 digits for speed on the left and normal digits for distance traveled?

Sorry for not telling this before... This code is with reed switch but I plan to use a Hall effect sensor...

the code

Velo_pimp.ino (4.2 KB)