Repairing Motorcycle Tachometer

philba

Joined Aug 17, 2017
959
Instead of counting pulses, why don't you measure the time between them? Or better yet, measure the time it takes to see 12 pulses (or how ever many per rev) for a full rev (call it Trev). Then RPM = 60/Trev. micros() on the arduino is your friend here. If you use long ints and do the math in microseconds you can avoid floating point which is slow. You should get a very stable result.
 

crutschow

Joined Mar 14, 2008
38,503
Instead of counting pulses, why don't you measure the time between them? Or better yet, measure the time it takes to see 12 pulses (or how ever many per rev) for a full rev (call it Trev).........
I believe that is what he is essentially now doing.
(From post #17):
I have managed to get a fairly stable output on the gauge. I measured the interval of 30 pulses, and changed the unit of measurement to microseconds rather than milliseconds.
 

Thread Starter

David Schofield

Joined Sep 1, 2016
32
Instead of counting pulses, why don't you measure the time between them? Or better yet, measure the time it takes to see 12 pulses (or how ever many per rev) for a full rev (call it Trev). Then RPM = 60/Trev. micros() on the arduino is your friend here. If you use long ints and do the math in microseconds you can avoid floating point which is slow. You should get a very stable result.
This is what I have done, measured the time it takes to see 30 pulses, using micros() as you said.

Here is the sketch.

C:
// Pins
const int sqWaveOutPin = 11;
const int rpmInPin     = 3;

volatile byte pulses;
unsigned long oldMicros;
const byte pulsesPerRot = 12;

int frequency = 0;
int curfrequency = 0;

void setup() {
  // Serial, remove once tested
  Serial.begin(2000000);

  pinMode(rpmInPin, INPUT);
  pinMode(sqWaveOutPin, OUTPUT);
  digitalWrite(sqWaveOutPin, HIGH);
  attachInterrupt(digitalPinToInterrupt(rpmInPin), countPulses, FALLING);
}

void loop() {
  readRPM();
  produceSquareWave();
}

void countPulses() {
  pulses++;
}

void readRPM() {
  if (pulses >= 30) {
    //noInterrupts();
    unsigned int copy = pulses;
    pulses = 0;
    //interrupts();
    frequency = ( (copy * 1000000L) / (micros() - oldMicros) ) / 6;
    oldMicros = micros();

    /*
   
    unsigned long elapsed = micros() - oldMicros;
    unsigned int rpm = (copy * (60 / pulsesPerRot) * 1000000L) / ( micros() - oldMicros);
    Serial.print("Elaspsed: ");
    Serial.println(elapsed);
    Serial.print("Pulses: ");
    Serial.println(copy);
    Serial.print("RPM: ");
    Serial.println(rpm);
    Serial.print("Freq: ");
    Serial.println(frequency);
    */
  }
  return;
}

void produceSquareWave() {
  if (frequency < 31) {
    // Tone does not work below 32 Hz
    noTone(sqWaveOutPin);
  } else {
    if (frequency != curfrequency) {
      tone(sqWaveOutPin, frequency);
      curfrequency = frequency;
    }
  }
  return;
}
 
Last edited by a moderator:

philba

Joined Aug 17, 2017
959
Ah, I see.

You aren't precisely counting the time of 30 pulses because you aren't syncing up with the actual pulse to start the timing. It's probably close but not exact. Here's my take on some changes to do that. Basically spin on pulses until you see 30. This guarantees that you start timing right after the 30th pulse and also calls micros() once not twice. It's ok to do a spin loop as you won't be updating frequency at least until pulses hits 30. If your code was going to do other things in the main loop then I'd rethink the design. Maybe use another timer that you start and stop in the interrupt handler. By the way, I would change copy to a long but it's probably being promoted by the compiler anyway when you do copy*100000L. I like to keep my variable types consistent. Also, you could just get rid of copy and use 30000000 in the numerator. [edit] or just use 5000000/(tempMicros - oldMicros). you'll never get to that point with pulse not being 30.[/edit].
Code:
void loop() {
  while(1){
    readRPM();
    produceSquareWave();
  }
}
void countPulses() {
  pulses++;
}
void readRPM() {
  long tempMicros;
  while(pulses < 30);        // wait until 30th pulse is seen
  tempMicros = micros();    // get microseconds
  unsigned int copy = pulses;
  pulses = 0;
  frequency = ( (copy * 1000000L) / (tempMicros - oldMicros) ) / 6;
  oldMicros = tempMicros;
  return;
}
 
Last edited:

Thread Starter

David Schofield

Joined Sep 1, 2016
32
Hi,

I am now fully healed and actually just completed my first race weekend since the crash. This was on the Honda sidecar, and with the code above and circuit I was able to repair the tachometer. I have now added extra functionality to the Arduino and am actually displaying the rpm on a 16 LED neopixel strip instead, one LED per 1,000 rpm.
 
Top