Arduino RPM and Speedometer for Fast Bikes (or Cars) :: Student Project
The need for speed. It’s been around for a long time…
I remember reading Dead Souls by Nikolai Vasilievich Gogol, and being amazed by Chichikovs love of driving fast in a horse drawn carriage:
“Selifan flourished his whip and shouted, “Hi, hi!” as the inequalities of the road jerked him vertically on his seat; and meanwhile, reclining against the leather cushions of the vehicle’s interior, Chichikov smiled with gratification at the sensation of driving fast.
For what Russian does not love to drive fast?
Which of us does not at times yearn to give his horses their head, and to let them go, and to cry, “To the devil with the world!”?
At such moments a great force seems to uplift one as on wings; and one flies…”
A customer of ours, Stefan, has the need for speed, and also the desire to achieve high speeds as safely as possible. For this reason he is developing an Arduino based speedometer that will show him the speed at a better visual angle, save the highest speed, and control lights that indicate the best times to shift.
Stefan, why the heck did you build an Arduino RPM and speedometer for fast bikes?
For my own safety. I got the need for speed and want to reach about 250 km/h on my bike. You need to check if you had hit the goal for the ride, and it takes many meters to lower the eyes and look at the original speedometer. With this design, I’ll be able to look at the top of the fairing and have a quick glance on the status.
I also plan on adding shift lights for better personal performance when accelerating.
It’s time for a field test when I have fastened the hall sensors on the bike and checked the signal output from the ignition box.
What was your biggest struggle as you worked through this project?
Doing the coding when C isn’t my native language. I tried avoiding fancy functions. I will keep it simple and fast for now. (Editor’s Note: Sounds like the spirit of prototyping!)
Did the project end up as you expected?
It’s working on the bench right now, but needs some more coding yet to be what I am after exactly.
Looking back on this project, what can you say you have learned about programming and/or electronics through the creation process?
[I’ve realized] there is a real need for thinking through and planning a project. It’s not without struggle, but a plan is very useful.
Was the training at Programming Electronics Academy able to help you build your skill?
Yes, I got motivated to program and do things. (That being said, Stefan says he stills like to crack open a book too!)
What type of Arduino board, Arduino clone, or Arduino compatible board does your project use?
UNO R3
What components did you use in your project?
LCD display, Hall Sensor, SD Card Reader, LEDs, Duct Tape (love it!)
Arduino Code:
Here is Stefans code that he was kind enough to share.
/*Arduino code for measuring speed and engine rpm using two Hall sensors
*
* Coding of anITiot.se summer 2018
* 8x7 LED display used for RPM and Speed.
* Shiftlight function with three stages
* Signal out for engine on to a relay
* inspirated code by Circuitdigest.com On 14-04-2017
*/
/*
* Pins used:
* 2,3 Hallsensors.
* 4 Engine running
* 5,6 Shiftlihgts.
* 7,8,9 Display
* 10,11,12,13 SD-card
*
*/
#include <LedControl.h> //7x8LEDdisplay
#include <SD.h>
#include <SPI.h>
File myFile;
//int ledpin=13; // led on D13 will show blink on / off
int engrun=4;
int shift1=5;
int shift2=6;
int shift3=1;
int pinCS = 10; // Pin 10 on Arduino Uno
volatile byte rotation; // variale for interrupt fun must be volatile, wheel speed
volatile byte engrotation; // variale for interrupt fun must be volatile, engine rpm
float timetaken,rpm,dtime,rps,engrps,timetaken1,engdtime;
//I want to change these definitions with a file on a SD-card that should be copied "stored" in EEprom when they need to be changed.
#define CIRCUM 50 //50 //Measure the circumferens of your wheel (mm) and enter it here
#define ENGINEON 600
#define SHIFT1 800
#define SHIFT2 900
#define SHIFT3 1000
#define REDLINE 1700
//float CIRCUM;
int v;
int r;
int engon;
int engrpm;
int rpmview;
int shiftrpm;
unsigned long pevtime;
unsigned long engpevtime;
//LedControl lc=LedControl(12,11,10,1); //One (1) in the line is the number of displays in chain
LedControl lc=LedControl(9,8,7,1); //One (1) in the line is the number of displays in chain
void setup()
{
Serial.begin(9600);
//Serial.println("Begin SD card 2.");
//pinMode(ledpin,OUTPUT); //LED pin output for debugging
pinMode(engrun,OUTPUT); //LED pin engine running state, low if rpm is under 400rpm
pinMode(shift1,OUTPUT); //Led pin Shiftlight1
pinMode(shift2,OUTPUT); //Led pin Shiftlight2
pinMode(shift3,OUTPUT); //Led pin Shiftlight3
digitalWrite(shift1, HIGH);
digitalWrite(shift2, HIGH);
digitalWrite(shift3, HIGH);
digitalWrite(engrun, HIGH);
attachInterrupt(0, magnet0_detect, RISING); //secound pin of arduino used as interrupt and magnet_detect will be called for each interrupt
rotation = rpm = pevtime = rps = 0; //Initialize all variable to zero
attachInterrupt(1, magnet1_detect, RISING); //third pin of arduino used as interrupt and magnet_detect will be called for each interrupt
engrotation = engrpm = engpevtime = engrps = engon = 0; //Initialize all variable to zero
//Initiera LED-Displayen
lc.shutdown(0,false);
lc.setIntensity(0,5);
lc.clearDisplay(0);
//SD-card reader
pinMode(pinCS, OUTPUT);
//Serial.println("Begin SD card 2.");
DisplayString0("A001"); // Set pgm version number here in the alfa-versions
delay(1000);
// SD Card Initialization
if (SD.begin())
{
//Serial.println("SD card is ready to use.");
DisplayString("5001");
} else
{
//Serial.println("SD card initialization failed");
DisplayString("E501");
return;
}
// Create/Open file
myFile = SD.open("speediot.txt", FILE_WRITE);
// if the file opened okay, write to it:
if (myFile) {
//Serial.println("Writing to file...");
DisplayString("5P01");
// Write to file
myFile.println(SHIFT3);
myFile.close(); // close the file
//Serial.println("Done.");
DisplayString("5P02");
}
// if the file didn't open, print an error:
else {
//Serial.println("error opening test.txt");
DisplayString("5E02");
}
// Reading the file
myFile = SD.open("speediot.txt");
if (myFile) {
//Serial.println("Read:");
DisplayString("5P03");
// Reading the whole file
while (myFile.available()) {
Serial.write(myFile.read());
//REDLINE = 1500;
}
myFile.close();
}
else {
//Serial.println("error opening test.txt");
DisplayString("5E03");
}
}
void loop()
{
/*To drop to zero if vehicle stopped*/
if(millis()-dtime>2000) //no magnet found for 1500ms
{
rpm= v = r = rps = 0; // make rpm and velocity as zero
//Serial.write(v);
dtime=millis();
DisplayString("5ped");
}
if(millis()-engdtime>2000) //no magnet found for 1500ms
{
engrpm = engrps = engon = 0; // make rpm and velocity as zero
//Serial.write("engrpm0");
engdtime=millis();
DisplayString0("dead");
digitalWrite(engrun, LOW);
digitalWrite(shift1, LOW);
digitalWrite(shift2, LOW);
digitalWrite(shift3, LOW);
}
v = CIRCUM * rpm * 0.0036; //speed calc
}
void magnet0_detect() //Called whenever a magnet is detected
{
rotation++;
dtime=millis();
if(rotation>=2)
{
timetaken = millis()-pevtime; //time in millisec for two rotations
rpm = r =(1000/timetaken)*60; //formulae to calculate rpm
rps =(1000/timetaken);
pevtime = millis();
//digitalWrite(ledpin, HIGH);
}
DisplayNumber(v);
}
void magnet1_detect() //Called whenever a magnet is detected
{
engrotation++;
engdtime=millis();
if(engrotation>=2)
{
timetaken1 = millis()-engpevtime; //time in millisec for two rotations
engrpm = engon = shiftrpm =(1000/timetaken1)*60; //formulae to calculate rpm
engrps =(1000/timetaken1);
engpevtime = millis();
//digitalWrite(ledpin, HIGH);
}
//DisplayNumber0(engrpm/10);
switch (engrpm) {
case 0 ... (SHIFT3 -101):
DisplayNumber0(engrpm/10);
break;
case (SHIFT3 -100) ... (SHIFT3 +200):
DisplayString0("5hf7");
break;
case REDLINE ... (REDLINE +5000):
DisplayNumber0(8888);
break;
default:
DisplayNumber0(engrpm/10);
break;
}
//engine on
switch (engon) {
case 100 ... (ENGINEON -1): // used for filter, do nothing.
break;
case ENGINEON ... 20000: // turn on, idling will be higher
digitalWrite(engrun, HIGH);
break;
default:
digitalWrite(engrun, LOW);
break;
}
//shiftlights
switch (shiftrpm) {
case SHIFT1 ... (SHIFT2 -1):
digitalWrite(shift1, HIGH);
digitalWrite(shift2, LOW);
digitalWrite(shift3, LOW);
break;
case SHIFT2 ... (SHIFT3 -1):
digitalWrite(shift1, LOW);
digitalWrite(shift2, HIGH);
digitalWrite(shift3, LOW);
break;
case SHIFT3 ... (SHIFT3 +300):
digitalWrite(shift2, HIGH);
digitalWrite(shift1, LOW);
digitalWrite(shift3, HIGH);
break;
case (REDLINE-50) ... REDLINE:
digitalWrite(shift2, HIGH);
digitalWrite(shift3, HIGH);
digitalWrite(shift1, HIGH);
break;
case (REDLINE +1) ... 20000:
break;
default:
digitalWrite(shift1, LOW);
digitalWrite(shift2, LOW);
digitalWrite(shift3, LOW);
break;
}
}
//Skriver ut ett tal.
void DisplayNumber(unsigned long number){
if(number > 9999){
DisplayString("E5"); //Betyder Error 5, talet är för högt
return;
}
String s = String(number);
DisplayString(s);
}
void DisplayString(String s)
{
int l = s.length();
if(l > 4){
s = "E4"; //Betyder Error 4, strängen är för lång
l = s.length();
}
//lc.setChar(0, 7, getChar(s, l-8), false);
//lc.setChar(0, 6, getChar(s, l-7), false);
//lc.setChar(0, 5, getChar(s, l-6), false);
//lc.setChar(0, 4, getChar(s, l-5), false);
lc.setChar(0, 3, getChar(s, l-4), false);
lc.setChar(0, 2, getChar(s, l-3), false);
lc.setChar(0, 1, getChar(s, l-2), false);
lc.setChar(0, 0, getChar(s, l-1), false);
}
//Denna returnerar vilket tecken som är på platsen i strängen.
//Är platsen utanför strängen returneras ett blanksteg.
char getChar(String s, int i){
char rc = ' ';
if(s.length() > i ){
rc = s.charAt(i);
}
return rc;
}
//Skriver ut ett tal.
//Display första 4 segment
void DisplayNumber0(unsigned long number){
if(number > 9999){
DisplayString0("E5"); //Betyder Error 5, talet är för högt
return;
}
String t = String(number);
DisplayString0(t);
}
void DisplayString0(String t)
{
int k = t.length();
if(k > 4){
t = "E4"; //Betyder Error 4, strängen är för lång
k = t.length();
}
lc.setChar(0, 7, getChar(t, k-4), false);
lc.setChar(0, 6, getChar(t, k-3), false);
lc.setChar(0, 5, getChar(t, k-2), false);
lc.setChar(0, 4, getChar(t, k-1), false);
//lc.setChar(0, 3, getChar(s, l-4), false);
//lc.setChar(0, 2, getChar(s, l-3), false);
//lc.setChar(0, 1, getChar(s, l-2), false);
//lc.setChar(0, 0, getChar(s, l-1), false);
}
//Denna returnerar vilket tecken som är på platsen i strängen.
//Är platsen utanför strängen returneras ett blanksteg.
char getChar0(String t, int j){
char rc = ' ';
if(t.length() > j ){
rc = t.charAt(j);
}
return rc;
}
About Stefan:

