Go Down

Topic: help in making engine digital RPM meter (Read 12181 times) previous topic - next topic

Techone

@Joy

Connect the output ( pin 3 ) of the 555 to digital pin 2. It is the interrupt # 0.

Here why :

const int ignitionInterrupt = 0;

attachInterrupt(ignitionInterrupt, ignitionIsr, RISING);

Here the reference : http://arduino.cc/en/Reference/AttachInterrupt

Joy

OK..

I just rewrote the code..Please correct me in places where I am wrong..

Code: [Select]
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 9, 8);

const int ignitionPin = 2;
const int ignitionInterrupt = 0;
const unsigned int pulsesPerRev = 1;

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);
  pinMode(ignitionPin, INPUT);
  attachInterrupt(ignitionInterrupt, &ignitionIsr, RISING);
}

void loop(){
  lcd.setCursor(0, 0);
  lcd.print("RPM- ");
  lcd.print(rpm);
  delay(200);

}

dc42

#32
Sep 14, 2011, 09:48 am Last Edit: Sep 14, 2011, 09:55 am by dc42 Reason: 1
Your code looks good to me, although as you are feeding it with negative-going pulses, I suggest changing RISING to FALLING in the attachInterrupt call.. The 555 circuit is probably overkill, but should work, and you can connect the 555 output to pin 2 of the Arduino as Techone says. If you want to try something simpler, just take the left hand half of the circuit (transistor, 2 resistors, diode, capacitor) and feed the output from the transistor into the Arduino pin 2 instead of into the 555.

The line:

rpm = 60000000UL/(interval * pulsesPerRev);

says take 60 million (which is the number of microseconds in 1 minute) and divide it by the interval in microseconds betweern pulses mutiplied by the number of pulses per revolution (which is 1 for your 2-stroke single cylinder bike). The UL suffix on the 60 million says that you want it interpreted as an unsigned long integer, because 60 million is too big to fit in an int.
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

Joy

WOW...

I tested it with a pulse generated by the 555 and counted the no. of blinks of the led attached on output of 555 and matched it with the reading on the led of arduino...
It was fully perfect... :)

Now. the only thing is that i am trying to do is when the rpm reaches 1000 and above I want to put a comma ( , ) after the thousand unit ( for eg..   if the rpm is 1500 I want a comma after 1. So it should look like 1,500)..

I wrote a thread on this but helpful members  are unable to understand me..

http://arduino.cc/forum/index.php/topic,71974.msg538685.html#msg538685

dc42

Code: [Select]

void loop()
{
 noInterrupts();
 unsigned long rpmToDisplay = rpm;  // take a copy with interrupts disabled in case it changes
 interrupts();
 if (rpmToDisplay >= 1000)
 {
   Serial.print(rpmToDisplay/1000);
   Serial.print(',');
   Serial.print((char)(((rpmToDisplay/100) % 10) + '0'));
   Serial.print((char)(((rpmToDisplay/10) % 10) + '0'));
   Serial.print((char)((rpmToDisplay %10) + '0'));
 }
 else
 {
   Serial.print(rpmToDisplay);
 }
 Serial.println();
 delay(200);
}


I've coded and tested it with Serial.print but it should work with lcd.print as well.
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

Joy


Code: [Select]

void loop()
{
 noInterrupts();
 unsigned long rpmToDisplay = rpm;  // take a copy with interrupts disabled in case it changes
 interrupts();
 if (rpmToDisplay >= 1000)
 {
   Serial.print(rpmToDisplay/1000);
   Serial.print(',');
   Serial.print((char)(((rpmToDisplay/100) % 10) + '0'));
   Serial.print((char)(((rpmToDisplay/10) % 10) + '0'));
   Serial.print((char)((rpmToDisplay %10) + '0'));
 }
 else
 {
   Serial.print(rpmToDisplay);
 }
 Serial.println();
 delay(200);
}


I've coded and tested it with Serial.print but it should work with lcd.print as well.



Your code just worked like a charm... Felt so relief that u atleast understood and solved what I wanted to print... :)

Joy

I am having a little problem with the delays in my rewritten code..
Please check out if there is any other way..

Code: [Select]
#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;

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);

  pinMode(analoginput, INPUT);
  pinMode(ignitionPin, INPUT);
  attachInterrupt(ignitionInterrupt, &ignitionIsr, FALLING);
}

void loop()
{
  /***************THIS PART IS FOR VOLTAGE*********************/
  lcd.clear();
   // 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*************************/
  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'));
   
  }
  else
  {
    lcd.setCursor(6, 0);
    lcd.print("RPM ");
    lcd.print(rpmToDisplay);
   
  }
   
  delay(200);
 
}


In this code I want to call for RPM print every 0.2 seconds... But I want to call for Voltage every 1.4minutes..

But that delay(200); is call my voltage at the same intervals...
How to solve this...??

dc42

#37
Sep 14, 2011, 10:15 pm Last Edit: Sep 14, 2011, 10:17 pm by dc42 Reason: 1
First, your loop() function is getting rather complicated, so best split out the bits that display voltage and rpm into separate functions.

Next, you want to display voltage every 1.4 minutes i.e. 84 seconds (I wonder where you got this figure from?) and rpm every 0.2 seconds. So if you only update the voltage every 84/0.2 = 420 iterations of the loop, that should do it. Something like this (warning - untested code!):

Code: [Select]

void displayRpm()
{
 noInterrupts();
 unsigned long rpmToDisplay = rpm;  // take a copy with interrupts disabled in case it changes
 interrupts();

 lcd.setCursor(6, 0);
 lcd.print("        ");    // clear old RPM display in case the new value is shorter
 lcd.setCursor(6, 0);
 if (rpmToDisplay >= 1000)
 {
   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.print("RPM ");
   lcd.print(rpmToDisplay);    
 }
}

void displayVoltage()
{
 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("     ");   // clear old value in case the new value is shorter
 lcd.setCursor(0,0);
 lcd.print(vin, 1);   //Print float "vin" with 1 decimal
 lcd.print("V");
}

const int maxCount = 420;

int loopCount = maxCount;

void loop()
{
 displayRpm();
 if (loopCount == maxCount )
 {
   loopCount = 0;
   displayVoltage();
 }
 ++loopCount;
 delay(200);
}


You may also need to do an lcd.clear() at the end of setup().
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

Joy

Thank you so much Dc42..... :)

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..

dc42

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.
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

Joy

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.. :)
I took the way of blink without delay.. :P

Code: [Select]
#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(" ");
   
  }
   }
   
 
 
}

dc42

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.
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

Joy

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

Joy

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..??

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.
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

Go Up