Issue with revolutions/minute calculations

Hello, I am currently working on a project motorcycle I have. It's an '82 Yamaha and the ignition module seems to be going out. I wanted to try to construct my own electronic ignition for it using an Arduino Mega 2560. It is a single cylinder bike so I have to read one pulse per revolution from the pickup coil, and from that I should be able to calculate the RPMs. The issue I am having is with the actual calculations, I think. The bike idles at 1200 rpm, but my calculations show about 55.0 at idle. The code I wrote is included below.

// Declare Variables
volatile int revolutions = 0;
int old_rpm[] = {0, 0, 0, 0};
int new_rpm = 0;
unsigned long old_time = 0;
unsigned long current_time = 0;
float average_rpm = 0;


////////////////////////////////////////////////////////////
void setup()
{
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(2), rpm_counter, RISING);

  // Write pin 2 high for pullup.
  digitalWrite(2, HIGH);
}


/////////////////////////////////////////////////////////////
void loop()
{

  // Update rpm every XX number of revolutions.
  if (revolutions >= 25)
  {
    // Detatch Interrupt while calculating
    detachInterrupt(digitalPinToInterrupt(2));

    // Update Current Time
    current_time = millis();

    // Calculate the new rpm
    new_rpm = (revolutions * 1000 * 60) / (current_time - old_time);

    // Save Current Time as old time for next calculation.
    old_time = current_time;
    
    // Calculate 5 point moving average, update old rpm readings.
    average_rpm = (new_rpm + old_rpm[0] + old_rpm[1] + old_rpm[2] + old_rpm[3]) / 5;

    // Update old_rpm with new_rpm, replacing last value of array.
    old_rpm[3] = old_rpm[2];
    old_rpm[2] = old_rpm[1];
    old_rpm[1] = old_rpm[0];
    old_rpm[0] = new_rpm;
    
    // Print values for testing
    Serial.println(average_rpm);
    Serial.println(revolutions);
 
   
    //Reset revolutions to 0.
    revolutions = 0;
   
    //Re-attatch interrupt
    attachInterrupt(digitalPinToInterrupt(2), rpm_counter, RISING);
  }
}


void rpm_counter()
{

  // Update number of revolutions
  ++revolutions;
}

I can turn the engine by hand and increment 'revolutions', and when the value is printed, it always seems to be correct. But when I print 'rpm', which should be somewhere in the neighborhood of 1200, I only get about 55. My guess is that there is an issue with the way I am handling the time calculations but I'm not sure. I guess my main question is; how am I messing up the rpm calculation but counting the number of revolutions correctly? Any help would be appreciated, thank you!

Two stroke or 4 stroke?

This seems like overkill, and a fragile solution. Use something that can be soldered solid.

I don't know what you are saving into old_rpm[], but what rpm_counter() should do, is saving the timestamps every time it detects an edge. When you have 6 timestamps, you compute the average time difference (that will make 5 of them) and reconstruct the rpm value from that. You should also reset some kind of counter after 6 readings, so you can start sampling from reading #0 again.

1 Like

Hello and good morning
Keep the time for the action read the counter served by the ISR as short as possible.
You may use the noInterrupts() and interupts() functions to controll this process.

1 Like

There are similar projects about - have a google .

Don’t underestimate the difficulty of making this a reliable waterproof , vibration proof system . can you not just buy a new ignition module ?

You can use the <FreqCount.h> library and count the frequency, it might be simpler.

Please also pay attention to what JohnRob said, the strokes the engine make changes the rpm that you calculate using ignition stroke! if it is a single stroke engine then that makes sense. if it is a 4 stroke engine, then the rpm you get is 1/4 the real engine rpm... if it is a 2 stroke engine which i suspect to be then it is the half.

Additionally, some engines skip ignitions when they run, some kind of inherited failure.

I would go with a hall sensor and a magnet. much more reliable results.

Best of luck

No. There are no single-stroke engines, at least not in motorbikes! The OP says it's a single, so if it's a two-stroke there will be one spark per revolution. If it's a 4-stroke, there would normally be one spark every two revolutions, but quite a few 4-stroke singles use a "wasted spark" system; if that's true for this Yamaha then it will be one spark per stroke.

"Some kind of inherited failure". What?? This isn't correct at all. The only time that ever happens is when the engine is running into its rev limiter. Some of those work by cutting the ignition.

Anyway, none of us have yet identified if there is anything wrong with the code that's making it read wrongly. I've had a look and it isn't obvious to me.

You are absolutely correct, By single stroke I didnt mean one stroke. One stroke engines do not exist. on 4 stroke engines there will be 2 revolutions. I confused everything in my text. sorry.

Well, that my friend is correct however, if the ignition timing is not electronically regulated, for example if it is timed using a distributor head or a magnet passing through a magnetic switch, a missed ignition will definitely happen every now and then.

With the distributor and cam system, the cam some times lifts the contact breaker but instead a spark keeps the current from dropping. in this case the ignition will not fire or will do very weakly.

Thank you for correcting me however.

volatile int revolutions = 0;
    new_rpm = (revolutions * 1000 * 60) / (current_time - old_time);

The intermediate result revolutions * 1000 * 60 is a 16-bit int value and will overflow.
This will force the intermediate result to be long:

    new_rpm = (revolutions * 1000L * 60L) / (current_time - old_time);
2 Likes

@Isaiah935: Have you considered the issue @ramimehyar touches upon: the ignition timing?

Some ignition modules rely upon the rise time of a magnetic pickup to alter the ignition timing, others are more sophisticated and use a crankshaft position sensor to determine the correct timing for the ignition. All of them advance the ignition at higher rpm.

Which system does your Yamaha use?

Hello everyone, thank you for the replies!

@JohnRob, It's a 4 stroke, wasted spark engine, so it triggers once per revolution.

@330R I should have mentioned this in the original post but I'm just using a mega because that's the only one I have at the moment. I Plan on switching to a Nano but they're still in the mail. I'll also be using one of those solderable breadboards once I get all my code working. My old_rpm variable just saves the old rpm readings so that I can use it to figure out the average rpm. I implemented that part to try to smooth out the rpm readings a little bit. As you and @paulpaulson mentioned, I will try to collect timestamps for each rising edge, and calculate rpm from there.

@hammy The used ignition boxes seem to run in the neighborhood of $450. They're out there but are pretty expensive, that's what prompted this project. I will look around more for similar projects, though.

@ramimehyar Thank you, I will look into the <FreqCount.h> library, sounds like it could be very useful. A hall sensor and magnet would be certainly easier to get a good signal from, but the bike has a variable reluctor sensor that triggers by a single tooth on the flywheel, so I was trying to retain the factory sensor. As I forgot to include in the post, it is a wasted spark 4 stroke, like @SteveThackery says, so it sends a signal once per revolution.

@oqibidipo I will try this, that actually make sense. I didn't think about the fact that the intermediate results could overflow.

@SteveThackery This particular bike (1982 Yamaha sr250 for those interested) uses a CDI ignition with some sort of mystery IC that controls the advance. I have not been able to find any information on the IC though, so I don't know exactly what it is. But I do know that the ignition pulse triggers about 30 degrees before TDC. My plan for the advance was to build a table, or come up with an equation, that describes the advance curve, then use the calculated rpm to find the amount of time delay that would correspond to a given amount of advance. From the documentation I can find, this particular bike reaches full advance at about 3500 rpm which would mean that the ignition would fire as soon as the trigger pulse comes in so I shouldn't have to do any extra calculations at the higher rpm.

Again, thank you all for taking the time to respond. I will try to implement some of the suggestions here and report back!

Nice catch! I missed that. I hope that's the problem.

That's good information, and I think you are taking exactly the right approach. A question: is the spark generator a separate module? Or is it all built in to the existing box? I ask because I was wondering if you've got to design a CD or inductive ignition unit, as well as the control unit.

I recently developed a fuel injection system for a small engine, and I had the most terrible problems stopping it from crashing due to induced voltages and currents flying around the place. I couldn't find a decent source of information on how to "harden" the Arduino against such an environment. We need to find someone who has real experience doing it, rather than someone who just understands the principles. :grin:

@SteveThackery Yeah the actual ignition coil is a separate unit. From what I understand, I need to use a transistor to ground one of the wires on the coil to fire the ignition. I started working on a fuel injection system for one of my other bikes but I never got to the point of actually putting it all together lol, seemed like quite a project. How did you handle engine tuning?

I tried the solution suggested by @oqibidipo and I am getting some odd readings. I seem to have introduced negative rpms lol.

I am printing the calculated rpm, and the number of revolutions counted between calculations each time so that i know it's recalculating every 50 revolutions. These readings were taken at idle so the rpm should be about 1200. I will try to timestamp every revolution and calculate the average RPM from there, like @330R suggested. I'll also double check all my breadboard connections and make sure everything is secure. Again, thanks everyone for the suggestions.

Have you got a signal generator? I'd strongly recommend getting your unit working on your bench, first. Putting it straight into the vehicle is making it hard for you. All sorts of electrical noise will be riding on the signal line and supply lines.

Unfortunately I don't have a signal generator. I was kinda thinking along those same lines though. Who knows what other interference is being generated. I suppose i could always just plug the Arduino into the wall and get a nice, clean 60Hz signal lol. Realistically though, I should have a signal generator anyway, so I should just buy one. I am going to attempt to timestamp each interrupt and see if I can use that to calculate rpm. I should get a little Faraday cage to put the Arduino in.

Yes, you will be able to test your software and hardware under controlled conditions, over the entire rev range required. If you can afford a 'scope, then that would be brilliant, too. Those two, between them, will allow you to debug most of your projects.