Introduction: Arduino Bandsaw Speedometer

About: Tinkerer, dumpster-diver, occasional artist. I like to work in wood, metal, and electronics

A couple of years ago, I converted my bandsaw to run off a DC motor from a treadmill instead of the AC-induction motor that it came with. The addition of the the DC motor, plus the motor controller that came out of the treadmill, allows my bandsaw to operate at a big range of speeds without requiring me to change the position of the belts in the back of the saw.

My main motivation in doing this was to give myself the ability to cut metals like steel on the bandsaw, while also keeping the capability of cutting wood. The conversion has overall been a success, but after ruining blades while cutting steel, I found I really needed some way of knowing how fast my blade was moving. Enter the Arduino speedometer.


For this project you will need the following:

- Arduino (Uno, Nano, knockoff or genuine) Buy a Nano Every here:

- Rare-earth magnets:

- Hall-effect sensor:

- Perfboard:

- LCD screen of the Hitachi-driver type. You can get these on Adafruit and elsewhere. My project uses a 4x20 screen, but that's just what I had. You can use any size you want as long as it has a minimum of two lines to write text to.

- 10K potentiometer

- 3D printer (optional, but makes fabrication easier)

- Wire and assorted bits of hardware like screws, nuts, bolts

- Two-part epoxy (for attaching the magnets)

- Power supply for your Arduino

Step 1: Wiring

Wiring this project is relatively straightforward, if a bit time consuming because of how many wires need to be soldered. There's nothing tricky to it, and the components that need soldering are minimal.

Step 2: The Code!

For this project to work, your Arduino needs to know what it's doing. This is my code. It's written to work on a bandsaw with 14-inch wheels and six magnets glued around the rim. Consult the code comments if you want to tweak those specifications for your own project.

Arduino Speedometer

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:
// 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;