help in making engine digital RPM meter

Thank you so much Dc42..... :slight_smile:

some parts of the code is getting a little complicated to me...

It would be kind of you if you can explain what have you done..
I always face problems with delays ..
I hope I will learn to solve them if I understand what are you doing in the code..

Well, I've moved the code that displays the RPM into a separate function, and the code that displays voltage into another function. I've changed loop() so that it keeps a count of how many times it has gone through. When the count reaches 420, it displays the voltage and resets the count to 0; otherwise it doesn't update the voltage. So it only displays the voltage once for every 420 times it goes through the loop. Since the loop still includes the 0.2 second delay call, and the rest of the loop takes much less than 0.2 seconds, the displayed voltage will be updated about every 84 seconds.

I had to remove the lcd.clear() command from the loop since we no longer write the displayed voltage to the lcd every time. So I've changed displayRpm() and displayVoltage() to clear out the bits of the display they print to, by writing spaces. Otherwise, if e.g. the RPM changed from 1000 to 999 then the display would go from "RPM1,000" to "RPM 9990" (the last '0' is left over from when the RPM was 1000).

I changed your calc of voltage from vin/(R1/(R1+R2)) to vin*((R1+R2)/R1) because multiplication is generally preferred to division on microcontrollers (it's faster).

Hope this explanation helps.

I went through your code several times and then uploaded to the arduino ..The RPM was updating every 0.2 seconds but the voltage was being updated after a lot of time intervals..
I am still going through the code to understand properly and re edit it to solve the problem..

In the meantime I took a complicated way.. But it worked.. :slight_smile:
I took the way of blink without delay.. :stuck_out_tongue:

#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 / (R2/(R1+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(6, 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'));
    lcd.print(" ");
  }
  else
  {
    lcd.setCursor(6, 0);
    lcd.print("RPM ");
    lcd.print(rpmToDisplay);
    lcd.print(" ");
    
  }
   }
   
  
  
}

In your earlier post, you said you wanted to update the voltage every 1.4 minutes, so that is what I coded. Your sketch updates the voltage every 1.4 seconds.

Your method of checking how long it is since you last did an update to see whether to do a new one is a good solution.

with your code the rpm was updating at 0.2 seconds but the voltage was taking more that 5 seconds to update... :frowning:

Now I am adding and odometer to the code..

The challenge is to store the Km traveled when I shut of ignition and pull off the key or disconnect the vehicle battery..

I do not want to use the inbuilt atmega 328 EEPROM as I want to write to the EEPROM evere time there is a change in the Km, so I rather want to use an external EEPROM as it will much cheaper and save the Atmega's life....
Will that be a good idea or I can do something else..??

I suggest using the mcu's own EEPROM using one of the following schemes:

  1. You are already monitoring the battery voltage, presumably after the ignition key. Therefore, if you power the Arduino via a diode and a large capacitor, you can detect when the ignition has been switched off some while before the Arduino loses power. You could detect the impending loss of power and write to EEPROM then, so that you only do 1 write to EEPROM each time the ignition is turned off (and only if the number of km has changed since last time).

  2. You could write to the EEPROM every km but use a wear-levelling scheme so that you don't write to the same cell every time. This would allow you to do well over 10 million km before you approach the 100,000 cycle write/erase lifetime of the built-in eeprom.

dc42:
I suggest using the mcu's own EEPROM using one of the following schemes:

  1. You are already monitoring the battery voltage, presumably after the ignition key. Therefore, if you power the Arduino via a diode and a large capacitor, you can detect when the ignition has been switched off some while before the Arduino loses power. You could detect the impending loss of power and write to EEPROM then, so that you only do 1 write to EEPROM each time the ignition is turned off (and only if the number of km has changed since last time).

  2. You could write to the EEPROM every km but use a wear-levelling scheme so that you don't write to the same cell every time. This would allow you to do well over 10 million km before you approach the 100,000 cycle write/erase lifetime of the built-in eeprom.

I am happy with the option 1. ... :slight_smile:

and in option 2. are you telling me to write to the eeprom in this way...??

{
  for (int i = 0; i < 512; i++)
    EEPROM.write(i, i);
}

Joy:
and in option 2. are you telling me to write to the eeprom in this way...??

{

for (int i = 0; i < 512; i++)
    EEPROM.write(i, i);
}

Yes, that's the basic idea. Since the number of km is monotonically increasing, the highest value in the region you are writing to must be the most recent one. When you want to store a new value, store it in the cell containing the lowest existing value.

Hey..

If I am going with the 1st option
I will probably just power only the atmega via a diode and do

If Vin == 0 then write to eeprom..

what do You think if I use a 1500 mfd capacitor after the diode in parallel to the atmega Vcc then how long will it run..??
The eeprom write will probably take nothing more than 3.3ms time..

Or will I need a higher value capacitor...??

To work out how long the charge in the capacitor will last, we need to work out the total current consumption.

The current drawn by the atmega328p depends on the clock speed. The datasheet gives 4.2mA typical, 12mA maximum at 8MHz (it will be nearly double at 16MHz). The last 16x2 LCD I bought draws 4mA maximum. The 555 draws 6mA maximum (less if it is the CMOS type). The regulator will draw 2mA or so more than the output current. So we have a total worst-case current of about 24mA, assuming no other loads on the 5v rail. I assume you will drive the LCD backlight direct from the 12v supply through a series resistor, so that it doesn't draw current from the capacitor.

If the battery provides 12v when not charging (engine idling), you should get about 11.3v after the diode. If you choose a low-dropout 5v regulator, it will provide 5v to the atmega328p with barely more than 5v input. So the capacitor may discharge by about 6v before the 5v supply is affected.

To work out how long the capacitor provides power, we use the equation Q = CV, where Q is charge, i.e. current * time. So:

24e-3 * t = 1500e-6 * 6

which gives t ~= 400ms. So you should have ample time to write the data to flash memory, provided you check for zero battery voltage at least every 50ms or so. Note that these figures assume you run the mcu at 8MHz and use a low-dropout 5v regulator.

He he he...
you took along a lot of things..

I just thought powering only the MCU with the capacitor not the LCD..
SO I think it will only be 24mA current by the MCU..

It is not a good idea to power the LCD and the MCU from different 5v supplies. Apart from the fact that you would need 2 regulators, if the LCD is powered down while the MCU is still powered, the LCD may put a substantial load on the MCU. You might even damage the LCD.

I was talking about this way...

powering all with the same regulator but only the MCU via a diode and then after that a capacitor so that when the ignition is turned off I do not want to power the LCD with the capacitor, so I will have more time.. :slight_smile:

No, you need to put the diode and the large capacitor before the regulator, not after it. By putting it before the regulator, you can tolerate a much larger voltage drop on the capacitor (6v in my calculation). If you place the capacitor after the regulator, you can tolerate a much smaller voltage drop - say 0.6v - so you will need around 10 times the capacitance to power the system for the same amount of time.

In addition, with the scheme shown in your schematic, you would still have the problem that inputs to the LCD from the mcu will be at voltages higher than Vcc on the LCD, so the mcu would probably end up powering the LCD anyway.

In this code how to do that when I stop the engine the rpm reading will return to 0..

Otherwise when with this if I stop the engine the last shown rpm stay on the screen..

#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 / (R2/(R1+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("   ");
    
  }
   }
   
  
  
}

In my project I just use a simple time out. Once a certain amount of time has passed since the last pulse event I set the values to 0. For RPM this goes by pretty quick since you don't have to track speeds nearly as low as when you're reading road speed. A .5 second timeout would kill the tach at < 120 RPM or so with a wasted-spark ignition? Unless your bike actually idles that slow.

You're already storing the time that the last pulse was received, so when updating the rpm you can just subtract that time from the current time returned by micros() and display 0 if it exceeds the timeout.

:frowning:

I am making mistake somewhere... :frowning:
Just cant figure out...