Vehicle RPM Calculations Using the Arduino

I plan to use inturrupts to count the time between each pulse, do the math and know the RPM. Not only do I want to capture the RPM but I also want to smooth it a bit as some ignition systems are a bit "ugly", so I plan to capture the last 5 pulse times average them, and then come up with my RPM. I then want to output a clean RPM signal at the spcified rate.

Know if my calcs suit me right... at 8000 RPM, I will have 4,000 pulses per minute (each cylinder fires once per 2 RPM), which works out to 66 pulses per second, .06 pulses per ms, which is roughly a pulse every 15ms...

Will this give the processor enough time to do the RPM calcs, output it to an LCD, and even reoutput a "clean" RPM pulse? Any suggestions on other ways you can think to do it?

As to timing, I have done something like this for a motorcycle which easily handled 8000 rpm. What I did in the interrupt routine was to process only every 10 tach pulses. Even at idle I'd be getting an update evry second and I never had to worry about overruns. My code was handling wheel interrupts as well as tach and just loading them into a queue or table. The "Loop" routine would pull them out of the table and do the calculations. That way I could do anything I liked for debugging and, worst case, I would find the table full and just flush data until I caught up. This did happen occasionally when I had very complex debugging or crappy signalling so it's worth checking.

As to data sources, I was pulling from a formal tach signal. You might find that most cars at least have that interface. I have also heard of catching a tach signal by wrapping a turn or two of wire around a spark plug lead but I don't know what signal level you'd get.

I would recommend getting it working for some particular car that you've got easy access to and go from there. Search for my stuff here and you may find the code or other people's comments of use.

Keep posting - I'm interested and others will be.

Be careful of your circuit interfaces to the car, the spikes can be wild. consider using an ipod/usb type power plug for your supply voltage to make life simpler.

I saw a basic tach for sale in an auto discount store for $20, again, might be a source of ideas.

Thank you all for your help on this! Here is what I have so far, and it seems to be working great when testing it with a simple push button to the inturrupt pin.

Next I will build the tach filter to take those harsh tach outputs I will be dealing with (with huge spikes). Below is my program thus far, and I will attach my filter I will be using also.

// Program to count pulses per revolution in an automobile
// TODO: Includes a running average to insure proper RPM output
// TODO: Output a clean averaged 5v Sq wave of RPM
// NOTE: May need to go to (us) as opposed to (ms) for better resolution

int Cycle = 0;                  // set to 0 for PulseStartTime and set to 
                        //   1 for PulseEndTime
unsigned long PulseStartTime;   // Saves Start of pulse in ms
unsigned long PulseEndTime;     // Saves End of pulse in ms
unsigned long PulseTime;        // Stores dif between start and stop of pulse
unsigned long RPM = 0;          // RPM to ouptut (30*1000/PulseTime)

void setup()
{
 Serial.begin(9600);            // OPENS SERIAL PORT SETS DATA TO 9600 bps
 attachInterrupt(0, RPMPulse, RISING); // Attaches interrupt to Digi Pin 2
}

void RPMPulse()
{
  if (Cycle == 0)                // Check to see if start pulse
  {
    PulseStartTime = millis();  // stores start time
    CycleOnOrOff = 1;           // sets counter for start of pulse
    return;                     // a return so it doesnt run the next if
  }
  if (Cycle == 1)             // Check to see if end pulse
  {
    detachInterrupt(0);         // Turns off inturrupt for calculations
    PulseEndTime = millis();    // stores end time
    Cycle = 0;                  // resets counter for pulse cycle
    calcRPM();                  // call to calculate pulse time
  }
}

void calcRPM()
{
  PulseTime = PulseEndTime - PulseStartTime; // Gets pulse duration
  Serial.print("PulseTime =");               // Output pulse time for debug
  Serial.print(PulseTime);                   // Pulse debug output
  Serial.print(" ");                         
  RPM = 30*1000/PulseTime*2;                 // Calculates RPM
  attachInterrupt(0, RPMPulse, RISING);      // re-attaches interrupt to Digi Pin 2
}

void loop()
{ 
 Serial.print("RPM = ");      // Output RPM for debug
 Serial.print(int(RPM));      // RPM debug output
 Serial.print(" ");
 delay(1000);                  // 1 sec delay for debug output
}

Here is the filter I plan to make:

Next step is to test it with the filter in a vehicle. Then add the averaging, then output a clean 5V sq wave RPM signal.

Thanks All!
I will keep you posted.

Chris

Alright here is what I have... The exact code above, but added some serial LCD commands so I could view things on a small LCD.

When I have a push button (with debouncing) hooked up to the interrupt pin (digi pin 2), all seems to work great. If I push the button wait about 1 sec press again, my LCD outputs roughly 60 RPM. If I wait two seconds and press the button, it output 30 RPM. So it seems my basic math is working great.

So it was on to the vehicle. I built my little tach filter (see design above), and got things wired into my vehicle. I used an o-scope to make sure my signals were good, and verified that the output on the filter looked good, which it was, as it was a pretty much a perfect sq wave 5V output, with a freq change as RPM increased or decreased.

Figured all was good to hook up the arduino with my program on it. Thats when things went kinda south... I started getting an RPM reading of -5536, or -2, and very rarely 30,000. The -5536 and the -2 was pretty random, but those were the only 3 values it would output, nothing else.

So I bring the whole setup back in, hook up my switch to the interrupt and everything outputs great. Please let me know if you have any suggestions or would like to see any information. I will re-attach my current code.

I will be tinkering some more tomorrow, do some debugging and get back to you all with my findings.

Thanks,
Chris

// Program to count pulses per revolution in an automobile
// TODO: Includes a running average to insure proper RPM output
// TODO: Output a clean averaged 5v Sq wave of RPM
// NOTE: May need to go to (us) as opposed to (ms) for better resolution

int Cycle = 0;                  // set to 0 for PulseStartTime and set to 
                        //   1 for PulseEndTime
unsigned long PulseStartTime;   // Saves Start of pulse in ms
unsigned long PulseEndTime;     // Saves End of pulse in ms
unsigned long PulseTime;        // Stores dif between start and stop of pulse
unsigned long RPM = 0;          // RPM to ouptut (30*1000/PulseTime)

void setup()
{
 Serial.begin(9600);            // OPENS SERIAL PORT SETS DATA TO 9600 bps
 Serial.print(0x0B,BYTE);      // LCD Setup
 Serial.print(0x40,BYTE);       // LCD Setup
 delay(100);
 Serial.print(0x1F,BYTE);       // LCD Setup
 Serial.print(0x28,BYTE);       // LCD Setup
 Serial.print(0x61,BYTE);      // LCD Setup
 Serial.print(0x40,BYTE);      // LCD Setup
 Serial.print(0x01,BYTE);      // LCD Setup
 Serial.print(0x0C,BYTE);      // LCD Setup
 delay(100);
 attachInterrupt(0, RPMPulse, RISING); // Attaches interrupt to Digi Pin 2
}

void RPMPulse()
{
  if (Cycle == 0)                // Check to see if start pulse
  {
    PulseStartTime = millis();  // stores start time
    CycleOnOrOff = 1;           // sets counter for start of pulse
    return;                     // a return so it doesnt run the next if
  }
  if (Cycle == 1)             // Check to see if end pulse
  {
    detachInterrupt(0);         // Turns off inturrupt for calculations
    PulseEndTime = millis();    // stores end time
    Cycle = 0;                  // resets counter for pulse cycle
    calcRPM();                  // call to calculate pulse time
  }
}

void calcRPM()
{
  PulseTime = PulseEndTime - PulseStartTime; // Gets pulse duration
  Serial.print("PulseTime =");               // Output pulse time for debug
  Serial.print(PulseTime);                   // Pulse debug output
  Serial.print(" ");                         
  RPM = 30*1000/PulseTime*2;                 // Calculates RPM
  attachInterrupt(0, RPMPulse, RISING);      // re-attaches interrupt to Digi Pin 2
}

void loop()
{ 
 Serial.print("RPM = ");      // Output RPM for debug
 Serial.print(int(RPM));      // RPM debug output
 Serial.print(" ");
 delay(1000);                  // 1 sec delay for debug output
 Serial.print(0x0C,BYTE);       // Clear LCD screen cmd
}
  1. What is CycleOnOrOff? Your posted code doesn't define it. (It looks like it should be just Cycle and you forgot to change it in one instance.)

  2. You can optimize by using type boolean for variable Cycle. Then void RPMPulse() becomes this...

void RPMPulse() {
  if (Cycle)                // Check to see if start pulse
  {
    PulseStartTime = millis();  // stores start time
    // CycleOnOrOff = 1;           // not defined.  Maybe this should be just "Cycle = true;" ??sets counter for start of pulse
  }
  else // otherwise it must be end pulse
  {
    detachInterrupt(0);         // Turns off inturrupt for calculations
    PulseEndTime = millis();    // stores end time
    Cycle = false;                  // resets counter for pulse cycle
    calcRPM();                  // call to calculate pulse time
  }
}
  1. Serial.print is very slow. At 9600 bps, to serial.print "PulseTime =" is 88 bits which is almost 10 ms just to clock out the data. I think you're probably getting some revolutions while the interrupt is detached and thus missing the pulse.
  • of all things I'm listing here I think this is the most likely cause for the garbage data you're seeing.
  1. An engine running at 3000 RPM = 50 Hz has a period of only 20 ms. If you're using millis(); to measure the period you have a quantization error of ~140 RPM (21 ms period = 2857 RPM). Is that precise enough? Calculate this at your redline too.

  2. You could optimize RPMPulse() to this (untested)...

void RPMPulse() {
  static unsigned long event_time = 0;
  unsigned long this_time = micros();

  RPM = 60000000 / (this_time - event_time);
  event_time = this_time;
}

Hello Mitch_CA,

Thank you for these suggestions, I will be trying them to see the results I get. You are correct the CycleOnOrOff should have just been Cycle. This has been fixed.

Awesome suggestion on setting the cycle just to a boolean as this simplifies things. I'll minmize the data I output to the LCD down to just the RPM figure, so that should help drop that to no more then 32 bits which should help.

I'll integrate in your optimization to use the micros instead of the millis. Thank you so much for these wonderful suggestions. Hopefully today I find some time to test them out!

Thanks again!
Chris

Hi all, can anyone tell me (a car novice) how to physically plug into an ignition pulse in a car or motorcycle? Fuse box or what? Can you take a picture? Also are you powering your arduino from the car battery?

I'm trying to figure out a way to turn on a device only when the engine is running and it seems the simplest way would be to check the "sparks". Having the rpm's would be a big bonus because I could use that to control the duty cycle for my device as well...

How about using a hall effect sensor? Wouldn't that detect the pulses if you simple taped it on a spark lead? :stuck_out_tongue:

cheers
Eiki

Do you really need the engine to be running or would having the key in "acc" or "on" position be enough.
There's all kinds of 12V power available that's hot only when the key is in "acc". Your stereo system works this way.

I really need the engine to be running (alternator) because the device draws alot of current and is also pointless when the engine isn't running since it's an hho booster...

Any ideas on sensing the ignition pulse like I asked? :-/

It might be time to make your own thread for your own problem here. But what you want to know is whether the alternator is turning, which can be presumed if the engine is turning. There are lots of ways to sense RPM.

Hi,

I beleive I'm simply asking how the other guys did theirs...but sure if no one answers here i'll ask somewhere else.

Hi all, can anyone tell me (a car novice) how to physically plug into an ignition pulse in a car or motorcycle? Fuse box or what? Can you take a picture? Also are you powering your arduino from the car battery?

I'm trying to figure out a way to turn on a device only when the engine is running and it seems the simplest way would be to check the "sparks". Having the rpm's would be a big bonus because I could use that to control the duty cycle for my device as well...

How about using a hall effect sensor? Wouldn't that detect the pulses if you simple taped it on a spark lead? :stuck_out_tongue:

most cars, even old and simple ones, will have a tachometer connection somewhere that you can hook up to. It's likely to be in the wiring harness behind the dash rather than at a fuse box. It will be 12 volts and may be messy(see the filter circuit pointed to above). Try ro find a wiring diagram for your car. I have heard of capturing the ignition pulse with a few turns of wire wrapped around a plug lead but I have no idea exactly how that would work.

Also, maybe another idea: If you look at your battery voltage, you'll find that the voltage is much higher - like 14V or more when it's running and as low as 11 when it's not running. That would be pretty easy to check for and it seems particularly useful for you because you care about having the juice available for your device.

Thanks for the reply. I'm gonna try to get some info from mechanics I know about finding the "tach" signal to leech. I have the option of doing an OBDII connection for reading but I need to have a fairly universal solution.

About the battery and if the engine in running. It is true that the voltage goes up to 14V when the engine is running (13.8V) on the majority of cars. I could perhaps do a simple voltage divider to scale that down to be readable to the arduino?

What about the current though...I'm afraid of frying the Arduino on the first test.

The Arduino will source the current it needs (a tiny amount) on the analog and digital pins. The voltage needs to be dropped to +5V, but the current shouldn't be a problem.

Drawing too much current FROM the Arduino is a problem, though.

From the arduino? :slight_smile:
Could you elaborate on that.

This clearly exposes my hobbyist status in electronics (I'm a programmer with some EE studies behind me). I have a fundamental boggle in my thinking of current. Text books talk about current DRAW but then always use the analogy of a water pipe system which obviously doesn't draw water (current) but pushes it, right?

This is very much off topic and newbie-ish but you are saying that if I hook up an arduino/acmedevice on a wire that is already drawing/pulling 10A to the next device on the wire the Arduino is only going to take what it needs?...unless there is enough potential (voltage) on the wire as well?

If there are 10 amps flowing through a wire, running a big air compressor, for instance, and you plug a lamp into the same circuit, the lamp works just fine, doesn't it? The light only draws as much current as it needs, because it is connected in parallel to the other devices on the circuit.

The current flow though the wire increases when the lamp is turned on, but that's equivalent to connecting a garden hose to the same water supply that supplies a fire hydrant.

The potential is there to supply all the water required by a fire hose, but that doesn't affect the garden hose.

Now, connecting the Arduino in series with a high current device, such as an LED without a current-limiting-resistor, CAN cause damage. It would be the same as connecting the fire hose to the end of the garden hose.

Ok, so the battery is a "source" (source vs. sink) here with a fixed pressure (12-14V) that won't go down unless you connect draw more current then it can handle. So each parallel device gets 12-14V and only the current it needs.

What has been confusing me with the water analogy is the "speed"/pressure of the current. With your firehydrant example the garden hose would rip apart from the pressure of the amount of water trying to push trough it if you would connect it to the hydrant, right?
So perhaps the firehydrant could be though of as another "source" with much higher voltage that you shouldn't connect to in series...?

A LED is a high current device? :stuck_out_tongue:
Ok I see what you mean though devices connected to the Arduino outputs will draw their current through the Arduino so if they need some crazy amount they can fry the board because they in effect "widen" the hose to the Arduino (unless they are connected with a resistor).

You have a garden hose, right? There's a fire hydrant out by the street, right?

Grab a shovel, and start digging around the fire hydrant. Dig along the pipe that the hydrant is attached to, towards the house. The pipe size gets smaller and smaller as it branches, until the pipe that the spigot is on matches the hose size. The pressure inside the pipe the whole way remains the same.

If you could get a fitting that you could attach the garden hose to, with the same diameter as the pipe that the fire hydrant is attached to, you could attach the garden hose to the same pipe.

The garden hose and fire hose are in parallel. The pressure in each is the same. The AMOUNT of water flowing through the hoses is different.

Now, go back and change hose and pipe to wire, pressure with voltage, and amount with current.

As long as devices are connected in parallel, the current through them is independent. When they are connected in series, the current is the same.

And, yes, an LED IS a high current device.

This time, I'll put in a plug for Grumpy Mike:

http://www.thebox.myzen.co.uk/Tutorial/LEDs.html

Thank you kindly Paul for clearing this up for me (without calling me an idiot hehe). Certainly gives me more confidence with current issues and thanks for the link to the LED info very nice page.

Last question (not often I get a chance to ask an expert! Sorry for the off topic de-railing) because you raised the question of devices connected to the Arduino frying it in my mind...

I plan to PWM the duty cycle of the device that is connected to the battery in parallel with the Arduino. For this I bought a power mosfet that should handle the current draw of the device (8A+). Do I need to worry about the Arduino in this case? I mean I'm pwm'ing the Base of the mosfet and that isn't drawing any serious current trough the Arduino. I mean it doesn't need more current depending on the loads' current?