Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
This sketch allows an Arduino to act as a speedometer. Data input can come from
magnets glued to the rim or spoke of a wheel along with a hall-effect sensor or
reed switch. You can also use an optical sensor using the spokes to break the
beam of light.
(If you use a reed switch instead of a hall-effect sensor, you will probably
need to modify the sketch to include some debouncing. If you use an optical
sensor, you might need Schmitt trigger circuitry.)
The sketch times how long it takes in milliseconds between pulses from the
sensor, and converts it into feet per minute. It is written for a bandsaw with
16-inch wheels, and six magnets evenly spaced on one of the wheels.
The data is written to an LCD with Hitachi HD44780 compatible chipsets using the
LiquidCrystal library.
For more information about LiquidCrystal, visit
The circuit:
* LCD RS pin to digital pin 12
* LCD Enable pin to digital pin 11
* LCD D4 pin to digital pin 5
* LCD D5 pin to digital pin 4
* LCD D6 pin to digital pin 3
* LCD D7 pin to digital pin 2
* LCD R/W pin to ground
* LCD VSS pin to ground
* LCD VCC pin to 5V
* 10K resistor:
* ends to +5V and ground
* wiper to LCD VO pin (pin 3)
Library originally added 18 Apr 2008
by David A. Mellis
library modified 5 Jul 2009
by Limor Fried (
example added 9 Jul 2009
by Tom Igoe
modified 22 Nov 2010
by Tom Igoe
modified 7 Nov 2016
by Arturo Guadalupi
modified 21 Aug 2018 from LiquidCrystal Hello World sketch by Emily Velasco
This example code is in the public domain.
// Include the library code:
#include <LiquidCrystal.h>
// Initialize the library by associating any needed LCD interface pin with the
// arduino pin number it is connected to
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// Unsigned long is necessary for these variables because they hold millisecond
// values, which add up quickly as the program runs, and would overflow as int
// variables
unsigned long TimerCount;
unsigned long previousTimerCount;
unsigned long LCDUpdateTime;
int sensorPin = 8;
int pinState = LOW;
int oldpinState = LOW;
int LCDprintValue = 0;
int sawPulse = 1;
void setup() {
pinMode(sensorPin, INPUT);
// Gives TimerCount a millisecond value to start with. millis() is the current
// time in milliseconds since the Arduino booted up
TimerCount = millis();
// Gives previousTimerCount a millisecond value to start with
previousTimerCount = millis();
// Set up the LCD's number of columns and rows. Change this to match your LCD
// dimensions
lcd.begin(20, 4);//
lcd.print("Feet per minute:");
void loop() {
pinState = digitalRead(sensorPin);
// Checks to see if the sensor is reading high and if that's
// different than the last time it checked. if both are true, it
//means a pulse from the sensor is being received
if (pinState == HIGH && pinState != oldpinState){
// Records the time in milliseconds so we know when the pulse was
// received
TimerCount = millis();
// Subtracts the time the last pulse was received from the time the newest
// pulse was receieved to determine the milliseconds between each pulse.
// Fewer milliseconds between pulses mean the wheel is turning faster. The
// constant, 41887.8, is derived from a series of calculations that turn
// milliseconds between pulses into revolutions per minute, and then
// revolutions per minute into feet per minute. This constant depends on how
// many pulses per rotation the wheel sensor generates, and the diameter of
// the wheel. In this case, I have six pulses per rotation, and 16-inch
// wheels.
// mpp = milliseconds per pulse = TimerCount - previousTimerCount
// circumference of band saw wheel = 16 inch diameter * Pi
// cif = circumference in feet = (16 * Pi)/12
// 1 pulse 60000 ms 1 rev cif 41887.8
// ------- X -------- X ------- X ----- = ------- = feet per minute
// mpp ms 1 min 6 pulse 1 rev mpb
LCDprintValue = (((unsigned long)41887.8)/(TimerCount - previousTimerCount));
// Sets a flag that we can use to tell if the speed has dropped
// to zero
sawPulse = 1;
// Sets previousTimerCount to whatever value was recorded for
// TimerCount before math was calculated
previousTimerCount = TimerCount;
// Checks to see if LCDUpdateTime is less than current time in
// milliseconds. this is so the LCD only updates once a second
if (LCDUpdateTime <= millis()){
// Places the cursor in the first column on the second row of the
// LCD
lcd.setCursor(0, 1);
// Checks to see if sawPulse was set to 1 by the first if
// statement. if it has been, it means a pulse was detected
// within the last second and the wheel is still turning
if (sawPulse == 1){
// Prints the speed value calculated above to the LCD
// Prints spaces to clear the line now in preparation for the
// next speed value to be displayed
lcd.print(" ");
// Resets sawPulse to zero in preparation for the else statement
// below
sawPulse = 0;
// If sawPulse is anything besides 1, it means the program has
// not detected a new pulse within the last second, and it can
// be assumed speed has dropped to zero
else {
lcd.print("0"); //Prints zero for the speed
// Prints spaces to clear the line now in preparation for the
// next speed value to be displayed
lcd.print(" ");
// Checks the time, adds 1000 milliseconds (1 second) to it, and
// records it. this value is compared against the time in
// milliseconds above to see if a second has elapsed.
LCDUpdateTime = (millis()+1000);
// Sets oldpinState to the last measured state from the sensor so it can be
// used to see if that state has changed next time the loop runs
oldpinState = pinState;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment