SALES INQUIRIES: 1 (888) 767-9864

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.

Arduino with breadboard and attached to LCD screen

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:

Motorcycle rider with helmet, smoking back tireStefan is a IT consultant from Sweden, who manages networks and servers by day, and apparently drive motorbikes really fast (he’s a self proclaimed adrenaline junky).  I am sure he follows all applicable speed limits…uh, well…maybe.  He has been studying electronics for the past year, and has gotten into programming Arduino over the last couple months.  You can check out some other cool stuff he is up to here (let’s just say it includes weapons and I let your curiosity be the driver).

Leave a Comment