Motorcycle Data Monitoring project ( need guidance )

I am into making a multiple data monitoring system for my motorcycle.
I am using a 16x2 LCD to display all the data..

The data are as follows
1. Engine RPM
2. Engine Temperature
3. Battery Voltage
4. Speedometer
5. Odometer
6. 4 Trip meters

I am using this circuit to pickup capacitive pulses from the spark-plug of the engine and feeding it to the interrupt pin 2 of arduino to determine RPM. As mine is a single cylinder engine, so every spark means one rotation.

I will be using LM 35 to measure the Engine temperature..

Now the codes which I have is for Volt, RPM, and Odometer..

Code for Volt and RPM combined with millis

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 9, 8);
const int analoginput = 0;
float vout = 0.0;
int value = 0;
float R1 = 1000.0;    // !! resistance of R1 !!
float R2 = 470.0;     // !! resistance of R2 !!
float vin = 0.0;
const int ignitionPin = 2;
const int ignitionInterrupt = 0;
const unsigned int pulsesPerRev = 1;

long VpreviousMillis = 0;
long Vinterval = 1400;

long RPMpreviousMillis = 0;
long RPMinterval = 200;

unsigned long lastPulseTime = 0;
unsigned long rpm = 0;

void ignitionIsr()
{
  unsigned long now = micros();
  unsigned long interval = now - lastPulseTime;
  if (interval > 2000)
  {
     rpm = 60000000UL/(interval * pulsesPerRev);
     lastPulseTime = now;
  }  
}


void setup()
{
  lcd.begin(16,2);
  lcd.setCursor(3, 0);
  lcd.print("Royal");
  lcd.setCursor(7, 1);
  lcd.print("Enfield");
  delay(3000);
  lcd.clear();
  
  pinMode(analoginput, INPUT);
  pinMode(ignitionPin, INPUT);
  attachInterrupt(ignitionInterrupt, &ignitionIsr, FALLING);
}

void loop()
{
    
  /***************THIS PART IS FOR VOLTAGE*********************/
   unsigned long VcurrentMillis = millis();
   if(VcurrentMillis - VpreviousMillis > Vinterval) {
     VpreviousMillis = VcurrentMillis;
  
   // read the value on analog input
 value = analogRead(analoginput);
 vout= (value * 5.0)/1024.0;  //voltage coming out of the voltage divider
 vin = vout * ((R1 + R2)/R2);  //voltage to display
 
   
 lcd.setCursor(0,0);
 lcd.print(vin, 1);   //Print float "vin" with 1 decimal
 lcd.print("V ");

 
   }
 /****************THIS PART IS FOR RPM*************************/
 
    unsigned long RPMcurrentMillis = millis();
   if(RPMcurrentMillis - RPMpreviousMillis > RPMinterval) {
     RPMpreviousMillis = RPMcurrentMillis;
   
  noInterrupts();
  unsigned long rpmToDisplay = rpm;  // take a copy with interrupts disabled in case it changes
  interrupts();
  if (rpmToDisplay >= 1000)
  {
    lcd.setCursor(7, 0);
    lcd.print("RPM");
    lcd.print(rpmToDisplay/1000);
    lcd.print(',');
    lcd.print((char)(((rpmToDisplay/100) % 10) + '0'));
    lcd.print((char)(((rpmToDisplay/10) % 10) + '0'));
    lcd.print((char)((rpmToDisplay %10) + '0'));
    
  }
  else
  {
    lcd.setCursor(7, 0);
    lcd.print("RPM");
    lcd.print(rpmToDisplay);
    lcd.print("   ");
    
  }
   }
   
  
  
}

The odometer code

#include <EEPROM.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 9, 8);

const int odometerPin = 3;
const int odometerInterrupt = 1;
const int odometerEepromLocation = 0;  // address of the first of 4 bytes in which the total wheel revs is stored
const int powerPin = A0;               // analog pin used to sense impending loss of power

const float wheelCircumference = 0.002035;  // wheel circumference in km
const int minPowerOkReading = 700;          // minimum reading on the power sense analog input that indicates good power
const int minPowerRestoredReading = 750;    // minimum reading on the power sense analog input that indicates we have powered back up

volatile unsigned long wheelRevs;       // total wheel revolutions counted, will overflow after 85 million km
unsigned long lastDisplayedMs = 0UL;    // time when we last refreshed the display

void setup()
{
  lcd.begin(16,2);
  // Initialise wheel revs from value stored in EEPROM
  wheelRevs = 0UL;
  for (int i = odometerEepromLocation + 3; i >= odometerEepromLocation; --i)
  {
    wheelRevs <<= 8;
    wheelRevs |= (unsigned long)EEPROM.read(i);
  }
  
  if (wheelRevs == 0xFFFFFFFFUL)
  {
    // must be the first time we have run
    wheelRevs = 0UL;
  }
  
  // Set up the odometer sensor pin and interrupt
  pinMode(odometerPin, INPUT);
  attachInterrupt(odometerInterrupt, odometerIsr, FALLING);  
}

void odometerIsr()
{
  ++wheelRevs;
}

void loop()
{
  // Capture the wheel revs with interrupts disabled in case it changes while we read it
  noInterrupts();
  unsigned long savedWheelRevs = wheelRevs;
  interrupts();
  
  if (analogRead(powerPin) < minPowerOkReading)
  {
    // Looks like power is going down, so write wheel revs to EEPROM (takes 13.2ms to write 4 bytes)
    for (int i = odometerEepromLocation; i < odometerEepromLocation + 4; ++i)
    {
      EEPROM.write(i, (unsigned char)savedWheelRevs);
      savedWheelRevs >>= 8;
    }
    
    // wait until either we die or the power comes back up
    while (analogRead(powerPin) < minPowerRestoredReading) 
    {
      delay(200);
    }    
  }
  else
  {  
    unsigned long now = millis();
    if (now - lastDisplayedMs >= 200)    // update display every 200ms
    {
      lastDisplayedMs = now;
      float km = wheelCircumference * savedWheelRevs;
      lcd.setCursor(0, 0);
      lcd.print(km, 1);
      lcd.print("Km");
      
    }
    delay(5);    // repeat after 5ms so that we check for power down often enough
  }
}

The odometer code is written for a normal HALL effect sensor..

Now I have thought that if the bike is standing and the magnet is close to the Hall sensor and the to and fro movement of the bike will rise the odometer reading..

So to avoid that odd thing now I have thought of using the Hall effect sensor US1881

Please guide me how will I write the code for using this HALL sensor US1881

here is the datasheet I am attaching

Hall-US1881EUA.pdf (500 KB)

I wouldn't worry too much about false sensor readings from the wheel being rocked back and forth. Unless you spend hours with the wheel in exactly the wrong position being rocked back and forth, the impact of a couple of false wheel rotation signals now and then would be negligible.

More significant issues I'd suggest are checking which of your variables need to be unsigned (I'd suggest that anything holding a millis() value should be an unsigned long, for example), and deciding how and when you're going to persist the odometer value.

But still if I would like to use the US1881 how should I write the code ..??

Another thing is that I am confusing myself in how to calculate the speed from the pulses of the Hall sensor... :stuck_out_tongue:

Your odometer code should work with the US1881 already, except that the US1881 needs a pullup resistor, so I suggest you enable the internal pullup on that pin.

To calculate the speed, you can calculate the wheel rpm from the Hall sensor just like you calculate the engine rpm from the ignition input, then multiply by the circumference of the wheel.

For the engine temperature sensor, LM35 is OK if you don't need to read below 2C. Otherwise use LM34 or DS18B20.

dc42:
Your odometer code should work with the US1881 already, except that the US1881 needs a pullup resistor, so I suggest you enable the internal pullup on that pin.

To calculate the speed, you can calculate the wheel rpm from the Hall sensor just like you calculate the engine rpm from the ignition input, then multiply by the circumference of the wheel.

For the engine temperature sensor, LM35 is OK if you don't need to read below 2C. Otherwise use LM34 or DS18B20.

I want to use the US1881 moreover because I wanted to avoid the back and forth movement causing the odometer to increase.. so I thought of putting two magnets, one will pole will trigger the arduino pin and another pole will cancel the trigger on the pin.. So I am thinking what way should I write the code so that i can make this happen.. :stuck_out_tongue:

Hummm for temperature I have DS18B20 but the problem is that my engine gets too hot as I ride for great distances and it can only measure till 125°c and whereas the LM 35 will be able to measure till 150°c, moreover I do not need much precision on the temperature..

Joy:
I want to use the US1881 moreover because I wanted to avoid the back and forth movement causing the odometer to increase.. so I thought of putting two magnets, one will pole will trigger the arduino pin and another pole will cancel the trigger on the pin.. So I am thinking what way should I write the code so that i can make this happen.. :stuck_out_tongue:

You don't need any special code, it will happen like that anyway.

Joy:
Hummm for temperature I have DS18B20 but the problem is that my engine gets too hot as I ride for great distances and it can only measure till 125°c and whereas the LM 35 will be able to measure till 150°c, moreover I do not need much precision on the temperature..

Sounds like LM34 or LM35 is the best bet then. I'd go for LM34 because it's nearly twice as sensitive and can measure lower temperatures without needing a negative supply.

How do I code for the trip meters..??

Your odometer code already keeps track of the total wheel revs and saves it to EEPROM during power off. When you start a trip meter (i.e. reset it to zero), I suggest you just save the wheel revs in a variable for that trip meter, to serve as the zero reference point. Also save/restore it to EEPROM during power down if you want the trip meter to survive power down cycles. To display a trip meter, just subtract its starting wheel revs from current wheel revs and convert to km as you do for the odometer.

I suggest you put all this code (apart from the code to restore trip meters from EEPROM) in the 'else' block in loop(), then you already have the variable savedWheelRevs to get the current wheel revs from.

can u explain this part of the code...??

  if (wheelRevs == 0xFFFFFFFFUL)
  {
    // must be the first time we have run
    wheelRevs = 0UL;
  }

Locations in EEPROM that have never been written have the value 0xFF. So that test is to see whether a value has been stored there yet or not.

Thank you PaulS for all your help in the other thread ( printing numbers in big fonts ) here -> http://arduino.cc/forum/index.php/topic,88459.0.html

Now I need some help in making different tabs for different functions... I dont know how to do it..
Like separate .h files for temperature, odometer, RPM, Big Numbers

And a main tab which will run them all..

Please show me how to do it...

These are the modes I want on my project... I will be using a button to shift modes and in modes use another button to RESET of change any value..

Here in this code I have compiled three things with millis and without delay..

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 9, 8);
const int analoginput = 0;
const int tempPin = 1;
float vout = 0.0;
int value = 0;
float R1 = 1000.0;    // !! resistance of R1 !!
float R2 = 470.0;     // !! resistance of R2 !!
float vin = 0.0;
const int ignitionPin = 2;
const int ignitionInterrupt = 0;
const unsigned int pulsesPerRev = 1;

long VpreviousMillis = 0;          // Voltage refresh rate
long Vinterval = 300;

long RPMpreviousMillis = 0;        // RPM refresh rate
long RPMinterval = 200;

long TemppreviousMillis = 0;       // Temperature refresh rate
long Tempinterval = 200;

unsigned long lastPulseTime = 0;
unsigned long rpm = 0;

void ignitionIsr()
{
  unsigned long now = micros();
  unsigned long interval = now - lastPulseTime;
  if (interval > 2000)
  {
     rpm = 60000000UL/(interval * pulsesPerRev);
     lastPulseTime = now;
  }  
}


void setup()
{
  lcd.begin(16,2);
  lcd.setCursor(3, 0);
  lcd.print("Royal");
  lcd.setCursor(7, 1);
  lcd.print("Enfield");
  delay(3000);
  lcd.clear();
  
  pinMode(analoginput, INPUT);
  pinMode(ignitionPin, INPUT);
  attachInterrupt(ignitionInterrupt, &ignitionIsr, FALLING);
}

void loop()
{
  /**************************************************************/ 
  /*************** THIS PART IS FOR VOLTAGE *********************/
  /**************************************************************/
  
   unsigned long VcurrentMillis = millis();
   if(VcurrentMillis - VpreviousMillis > Vinterval) {
     VpreviousMillis = VcurrentMillis;
  
   
 value = analogRead(analoginput);      // read the value on analog input
 vout= (value * 5.0)/1024.0;           // voltage coming out of the voltage divider
 vin = vout * ((R1 + R2)/R2);          // voltage to display
 
   
 lcd.setCursor(0,0);
 lcd.print(vin, 1);   //Print float "vin" with 1 decimal
 lcd.print("V ");

 
   }
   
 /*************************************************************/
 /*************** THIS PART IS FOR RPM ************************/
 /*************************************************************/
 
    unsigned long RPMcurrentMillis = millis();
   if(RPMcurrentMillis - RPMpreviousMillis > RPMinterval) {
     RPMpreviousMillis = RPMcurrentMillis;
   
  noInterrupts();
  unsigned long rpmToDisplay = rpm;     // take a copy with interrupts disabled in case it changes
  interrupts();
  if (rpmToDisplay >= 1000)
  {
    lcd.setCursor(7, 0);
    lcd.print("RPM");
    lcd.print(rpmToDisplay/1000);
    lcd.print(',');
    lcd.print((char)(((rpmToDisplay/100) % 10) + '0'));
    lcd.print((char)(((rpmToDisplay/10) % 10) + '0'));
    lcd.print((char)((rpmToDisplay %10) + '0'));    
  }
  else
  {
    lcd.setCursor(7, 0);
    lcd.print("RPM");
    lcd.print(rpmToDisplay);
    lcd.print("   ");
    
  }
   }
 
 /*************************************************************/
 /************** THIS PART IS FOR TEMPERATURE *****************/
 /*************************************************************/ 
 
   unsigned long TempcurrentMillis = millis();
    if(TempcurrentMillis - TemppreviousMillis > Tempinterval) {
          TemppreviousMillis = TempcurrentMillis;
  
  float temp = analogRead(tempPin);
  temp = temp * 0.48828125;
  lcd.setCursor(0, 1);
  lcd.print(temp, 0);
  lcd.print((char)223);
  lcd.print("C");
  
  
}
}

Now if I insert this odometer code, which has delays inside conditions. Will that effect the rest of the code...??

void loop()
{
  // Capture the wheel revs with interrupts disabled in case it changes while we read it
  noInterrupts();
  unsigned long savedWheelRevs = wheelRevs;
  interrupts();
  
  if (analogRead(powerPin) < minPowerOkReading)
  {
    // Looks like power is going down, so write wheel revs to EEPROM (takes 13.2ms to write 4 bytes)
    for (int i = odometerEepromLocation; i < odometerEepromLocation + 4; ++i)
    {
      EEPROM.write(i, (unsigned char)savedWheelRevs);
      savedWheelRevs >>= 8;
    }
    
    // wait until either we die or the power comes back up
    while (analogRead(powerPin) < minPowerRestoredReading) 
    {
      delay(200);
    }    
  }
  else
  {  
    unsigned long now = millis();
    if (now - lastDisplayedMs >= 200)    // update display every 200ms
    {
      lastDisplayedMs = now;
      float km = wheelCircumference * savedWheelRevs;
      lcd.setCursor(0, 0);
      lcd.print(km, 1);
      lcd.print("Km");
      
    }
    delay(5);    // repeat after 5ms so that we check for power down often enough
  }
}

Or should I convert them into millis like I did in the previous code for the voltmeter, RPM and temperature... ??

Looks like a cool project!
I'm about to undertake something similar but for car. (one race, one 4x4).

One thing that caught my eye;

Joy:
I am using this circuit to pickup capacitive pulses from the spark-plug of the engine and feeding it to the interrupt pin 2 of arduino to determine RPM. As mine is a single cylinder engine, so every spark means one rotation.

2 stroke or 4?
If it's 4 stroke, then one spark actually means two rotations (one power stroke every 2 rotations). .
Unless it's a wasted spark setup, where it fires the plug on the exhaust stroke too?

I'm also wondering how you create multiple 'pages' to show different values/sensors on an LCD, with a 'mode' or 'next/previous' buttons so will be interested to see any suggestions offered here.

Joy:
Or should I convert them into millis like I did in the previous code for the voltmeter, RPM and temperature... ??

Yes.

2 stroke or 4?
If it's 4 stroke, then one spark actually means two rotations (one power stroke every 2 rotations). .
Unless it's a wasted spark setup, where it fires the plug on the exhaust stroke too?

I have a four stroke engine...

Yes you are right ..in 2 crank rotations there will be 1 spark..It never came to my mind.. :stuck_out_tongue:
even this site shows the same thing :stuck_out_tongue: http://www.oldengineshed.com/forcycthry.html

But if you look in my code for RPM what do you notice.. are the calculations there for 1 spark for one rotation..?
or
1 spark in two rotations..??

I am asking this silly question because I connected my pickup to the arduino and found it was showing the correct value... :frowning:

Does that mean it is uselessly wasting another spark on the exhaust stroke ...?? :stuck_out_tongue:

    delay(5);    // repeat after 5ms so that we check for power down often enough

If you want to do stuff MORE often, you delay LESS. A delay() does not cause a repeat.

Fix the comment, or, better yet, delete the whole line.

Does that mean it is uselessly wasting another spark on the exhaust stroke ...??

Depends on the bike. Mine fires the spark plugs on every stroke, using one coil per plug. The one on the exhaust stroke does nothing, but it is easier to make the plug fire every time than to make it fire every other time. It only takes a small amount of energy to charge up the coil to fire the plug.

If you want to do stuff MORE often, you delay LESS. A delay() does not cause a repeat.

Fix the comment, or, better yet, delete the whole line.

NO no...
I know that...

I meant that will the delay in the odometer code effect the other code like RPM , Voltmeter, Temp, when I will add the odometer code to them...??
I am asking this because there was delay in the voltmeter code which was effecting other codes so I have to set the update time with millis instead of delay()

And as the delay in odometer code is inside the If and while loop I am not sure if it will effect the other codes ..So I asked this question... :stuck_out_tongue:

You need to eliminate all delays, other then any extremely short delays needed to allow hardware to respond, and delays in the code for starting up or shutting down (because you are not trying to do anything else then). So the delay(200) can be left, because that only happens in the code that is executed when the power is going down.

The delay(5) looks unnecessary to me. It doesn't matter how frequently you check for power going down, as long as you check often enough.

Your main loop should be of the form:

What time is it (i.e how many milliseconds since we started)?
Is it time to do X yet? If so, do X, and calculate and record when you need to do X again.
Is it time to do Y yet? If so, do Y, and calculate and record when you need to do Y again.
...
Go back to the beginning.