Go Down

Topic: Need help with measuring RPM code. (Read 618 times) previous topic - next topic

Southpark

#15
Jul 07, 2017, 08:22 am Last Edit: Jul 07, 2017, 08:23 am by Southpark
Is it possible to just detect the first edge, and record the time... t_ref = micros()..... and then .... if you detect any other edges, where those OTHER edges fall within a certain amount of time of t_ref..... then ignore those other edges. Eg.... if you detect a pulse, current time MINUS t_ref <= 100 microseconds .... then don't do anything.

Eventually, another edge will come where the time gap will be quite large (relative to t_ref).... ie. occurs at time current time MINUS t_ref > 100 microsecond.... then record the stop time ... ie stop_time = micros().

===

UPDATE:

If this was my project I would save the time at the first FALLING and then I would wait the amount of time for about a half revolution before testing for the next FALLING which ought to be the first one of the next group no matter what triggered the initial one.

...R
I agree with Robin2.

aviatorken

#16
Jul 07, 2017, 08:26 am Last Edit: Jul 07, 2017, 08:51 am by aviatorken
I think what's also happening, is that at slower speeds, when the magnets are moving between the fixed coil armature (letter U shaped), there are many HIGH and LOW periods which causes the transistor connected to pin 4 to turn on and off giving the false readings. I suppose a capacitor would fix alot of this, but was looking for a pure software approach if possible.

aviatorken

#17
Jul 07, 2017, 08:45 am Last Edit: Jul 07, 2017, 08:55 am by aviatorken
Is it possible to just detect the first edge, and record the time... t_ref = micros()..... and then .... if you detect any other edges, where those OTHER edges fall within a certain amount of time of t_ref..... then ignore those other edges. Eg.... if you detect a pulse, current time MINUS t_ref <= 100 microseconds .... then don't do anything.

Eventually, another edge will come where the time gap will be quite large (relative to t_ref).... ie. occurs at time current time MINUS t_ref > 100 microsecond.... then record the stop time ... ie stop_time = micros().

===

UPDATE:

I agree with Robin2.
I just tried this using what you suggest of ignoring readings of pulses < X microseconds; the problem is, the engine can go to 3600 rpm when running normally, so I can still get false readings even at a slowish cranking/electric starting speed of 600rpm.

Robin2

I just tried this using what you suggest of ignoring readings of pulses < X microseconds; the problem is, the engine can go to 3600 rpm when ignited, so I can still get false readings even at a slowish cranking/starting speed of 600rpm.
You need to explain exactly how you did that (i.e. post your code). I know that what I recommended in Reply #14 works because I have used it myself.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

aviatorken

#19
Jul 07, 2017, 08:58 am Last Edit: Jul 07, 2017, 09:03 am by aviatorken
You need to explain exactly how you did that (i.e. post your code). I know that what I recommended in Reply #14 works because I have used it myself.

...R
Sure, no problem. Sorry I forgot to include it :)

Code:

Code: [Select]

#include <PinChangeInt.h>

#define ENGINE_KILL_FLAG 1

#define RPM_INPUT_PIN 4

unsigned long rpmTimeoutInterval = 1000000;

volatile unsigned long rpmFirstEdgeMicros = 0;
volatile unsigned long lastRPMReadingMicros = 0;

volatile uint16_t volatileEngineRPM = 0;

volatile uint8_t bUpdateFlagsShared;
volatile uint8_t rpmPulseCounter = 0;

volatile uint16_t unEngineKillInShared;

void setup()
{
  Serial.begin(115200);

  PCintPort::attachInterrupt(RPM_INPUT_PIN, calcRPMInput, FALLING);
}

void loop()
{
  static uint16_t unEngineKillIn;

  static uint8_t bUpdateFlags;

  if (bUpdateFlagsShared)
  {
    noInterrupts();
   
    bUpdateFlags = bUpdateFlagsShared;
   
    if (bUpdateFlags & ENGINE_KILL_FLAG)
    {
      unEngineKillIn = unEngineKillInShared;
    }

    bUpdateFlagsShared = 0;
   
    interrupts();
  }

  uint16_t engineRPM = volatileEngineRPM;

  unsigned long rpmMicrosCopy = lastRPMReadingMicros;

  if (micros() - rpmMicrosCopy > rpmTimeoutInterval)
  {
    engineRPM = 0;
  }

  bUpdateFlags = 0;
}

void calcRPMInput()
{
  rpmPulseCounter++;

  if (rpmPulseCounter == 1)
  {
    rpmFirstEdgeMicros = micros();
  }

  if (rpmPulseCounter > 1)
  {
    unsigned long duration = micros() - rpmFirstEdgeMicros;

    if (duration > 16000)
    {
      if (duration <= rpmTimeoutInterval)
      {
        // 60,000,000 microseconds in one minute

        uint16_t rpm = 60000000 / duration;

        volatileEngineRPM = rpm;

        lastRPMReadingMicros = micros();
      }

      rpmPulseCounter = 0;
    }
  }
}

Robin2

#20
Jul 07, 2017, 09:22 am Last Edit: Jul 07, 2017, 09:33 am by Robin2
I would reduce the ISR to this
Code: [Select]
void calcRPMInput()
{
  pulseMicros = micros();
  rpmPulseCounter++;
  newPulse = true;
}

and do the rest of the calculations in loop()

The duration needs to be long enough to avoid all spurious pulses at the slowest speed and short enough to be significantly less than the time for 1 revolution at the highest speed.  If all the magnets occupy (say) 10 degrees of rotation then at 600 rpm (10rps) the time for 10 degrees would be 2778microsecs. And at 3600 rpm (60rps) the time for (say) 270 degrees would be 12500 µsecs. That suggests that a duration of 5000 µsecs would be suitable and 16000 might be a bit too long. BUT PLEASE check my maths as I frequently make stupid mistakes.

This is the sort of code I would have in loop()
Code: [Select]
void loop() {
    if (newPulse == true) {
        noInterrupts();
            latestMicros = pulseMicros;
            newPulse = false;
        interrupts();
        if (latestMicros - prevRevMicros > minInterval) {
            revInterval = latestMicros - prevRevMicros;
            prevRevMicros = latestMicros;
        }
    }
}


In my own program I turned off the interrupt when the pulse was detected and used a Timer to trigger another interrupt that turned the pulse interrupt back on. That avoided wasting time on unwanted time values - but your motor is running at a much lower speed so that may not matter.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

aviatorken

The magnets occupy about 1/6th the circumference of the flywheel by my measuring. Is this a problem?

Southpark

#22
Jul 07, 2017, 10:14 am Last Edit: Jul 07, 2017, 10:20 am by Southpark
The magnets occupy about 1/6th the circumference of the flywheel by my measuring. Is this a problem?
Maybe not a problem.

At a low RPM range ........  like at 600 RPM ..... use a thresh-hold time of say 20000 microsec.

And, as the measured speed rises ....... just get the code to automatically alter the threshold time to 3500 microsecond or something.

Otherwise..... just allow the motor to run at normal speed.... like 3600 RPM ..... and use a threshold time of 3500 microsecond..... temporarily ignoring low speed issues. And then..... while at normal speed.... get the code to monitor the speed....and then automatically change the threshold time to other values (that you experimentally find suitable). That is.... some testing may need to be done to obtain suitable ranges of threshold time to cover for various speed ranges.

aviatorken

Maybe not a problem.

At a low RPM range ........  like at 600 RPM ..... use a thresh-hold time of say 20000 microsec.

And, as the measured speed rises ....... just get the code to automatically alter the thresh-hold time to 3500 microsecond or something.
This will be prone to flaws if the engine stalls suddenly, no? Seems like I can't win either way. I can't use a hall sensor or any other sensor.

Southpark

#24
Jul 07, 2017, 10:34 am Last Edit: Jul 07, 2017, 10:41 am by Southpark
This will be prone to flaws if the engine stalls suddenly, no? Seems like I can't win either way. I can't use a hall sensor or any other sensor.
That's true. The other option is to have a board with a whole bunch of interrupt channels. In this way..... you can have various threshold settings covering a fair range .... and keep generating a bunch of results, all at the same time. And then do some processing to see which readings are stable or agreeable........ so only output a majority-rules value to the display, or something like that. This is probably too much...and might not work.

But ... going back to variable threshold time.... if the engine stalls, maybe your code could still be responsive (quick) enough to cover the dropping speed due to a stall? So.... like 3500 microsecond threshold at the normal running speed .... of 3600 RPM. Then a stall occurs.... the speed will drop.... and the code detects the slow-down..... and alters the threshold time accordingly.

aviatorken

#25
Jul 07, 2017, 10:52 am Last Edit: Jul 07, 2017, 11:00 am by aviatorken
That's true. The other option is to have a board with a whole bunch of interrupt channels. In this way..... you can have various threshold settings covering a fair range .... and keep generating a bunch of results, all at the same time. And then do some processing to see which readings are stable or agreeable........ so only output a majority-rules value to the display, or something like that. This is probably too much...and might not work.

But ... going back to variable threshold time.... if the engine stalls, maybe your code could still be responsive (quick) enough to cover the dropping speed due to a stall? So.... like 3500 microsecond threshold at the normal running speed .... of 3600 RPM. Then a stall occurs.... the speed will drop.... and the code detects the slow-down..... and alters the threshold time accordingly.
Hmm getting a bit complex. Maybe I should just take maybe 60 pulse readings or so, and grab the one with the longest duration, or.. experiment with capacitor values on the base of the transistor to keep it saturated long enough i.e keep the pin HIGH during the fluctuations? The cap would need to hold a charge long enough for the trailing edge of the 3rd magnet to the leading edge of the 1st magnet pretty much. Of course, engine speed would affect the cap value :(

Robin2

The magnets occupy about 1/6th the circumference of the flywheel by my measuring. Is this a problem?
Why would that be a problem? That means 60 degrees rather than the 10 degrees that I assumed.

This will be prone to flaws if the engine stalls suddenly, no? Seems like I can't win either way. I can't use a hall sensor or any other sensor.
What are you actually trying to do? Why do edge cases like a stall, and start-up matter?

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

aviatorken

#27
Jul 07, 2017, 11:14 am Last Edit: Jul 07, 2017, 11:29 am by aviatorken
Why would that be a problem? That means 60 degrees rather than the 10 degrees that I assumed.

What are you actually trying to do? Why do edge cases like a stall, and start-up matter?

...R
Theres a few simple things I'm trying to do, trying to do it with only the engine's magneto (primary) ground/kill wire.

1: Detect RPM. User can electrically start engine, so once a large rpm is achieved I kill power to the starter motor and prevent the user from engaging it if the engine is running (RPM high).

2: Kill engine with the same wire; this I already have working with a relay. I know when it's shorted the RPM goes to 0 but thats ok, I have a 7 second window that allows for the engine to come to a stop before you can start it again.

I also have a case where if the code detects the RPM as being 0 within half a second or so after engaging the starter motor, it kills power to the starter which means the crankshaft is stalled or the battery is too low/other issues. This case can be omitted if necessary or increased up to 1 second since I realize that there may not be any good RPM readings within half a second.

Robin2

Theres a few simple things I'm trying to do, trying to do it with only the engine's magneto (primary) ground/kill wire.
The suggestions I made in Reply #20 should be compatible with your objectives.

I would not bother with RPM - it just imposes extra calculations. For the purpose of decision making all you need is the number of microseconds per revolution. If that number is too big it means the motor is not rotating. If it is below some value it means the motor has started.

If you want a timeout to prevent excessive use of the starter motor I would record the time when the starter-button is pressed and record it again when the flywheel rotates. If the time interval is too long it means the starter did not work. If the interval is within the acceptable range then you need to consider how fast the flywheel is rotating so you can tell if the engine has started.

It would probably be wise to count a few revolutions at running speed before cutting off the starter motor.

If this was my project I would probably make the speed detector independent of the engine's electrical system.


...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Go Up