RPM Measurement off about every five seconds

Hello all,

I am currently setting up an Arduino to control pneumatic shifting and shift lights on a University FSAE race car and am running in to a small issue. It is likely not a large issue, but I am still confused at what is causing it. I set my ESP8266 to output a pulse at 750rpm (basically used the blink sketch but with 40ms delays) to use as a pulse generator. This would normally be provided by the ECU of the car as a pulse every rev, but it’s easier to prototype with this setup. Is the fact that I am using an ESP8266 as a waveform generator my issue? Or is it on the Arduino side? At first I thought using Serial.println(rpm); could be interfering with the interrupts so I used the lcd display instead, but I’m getting the same issue. It measures 750 rpm fine, but every 5ish seconds it drops to around 740rpm. Again, this is not a huge issue, but I’m stumped. Most of the code that matters should be in the findRpm() function.

Thanks

#include <Adafruit_NeoPixel.h>
#include <Arduino.h>
#include <LiquidCrystal.h>

//liquidcrystal stuffs
const int rs = 12, en = 11, d4 = 10, d5 = 9, d6 = 8, d7 = 7;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

// vars
int rpm = 0;
// time intervals to measure RPM
long currentTime;
long lastTime;
float timeDif;//float?
// vars to check input wave of ECU
int currentState = 0;
int lastState = 0;
int count = 0;
int i = 0;
// Inputs and Outputs

int shiftUpButton = 1;
int shiftDownButton = 4;
int sparkCutPin = 3;
int ecuIn = 2; // MUST BE 2 OR 3 FOR INTERRUPT
int shiftUpOut = 5;
int shiftDownOut = 6;

// Neopixels
int neoPixelPin = 1;
Adafruit_NeoPixel ledStrip =
    Adafruit_NeoPixel(16, neoPixelPin, NEO_RGBW + NEO_KHZ800);

// pulses heartbeat through lights
void heartbeat() {
  // set brightness to 80%
  // delay(100);
  // set brightness to 60%
  // set brightness to 80%
  // delay(250);
}

// calculate rpm from ecu pulses
void findRpm() {
  currentTime = micros();

  if (currentTime != lastTime) {
    timeDif =
        ceil(currentTime - lastTime); // timedif is now equal to pulse period
    lastTime = currentTime;

    rpm = ceil((60 * (1 / (timeDif / 1000000))));

       //Serial.println(rpm);
  }


}

// 10k shift
void tachLights() {
  if (rpm > 12300) {
    digitalWrite(13, HIGH);
  } else {
    digitalWrite(13, LOW);
  }
}

void setup() {
  lcd.begin(16, 2);
    // Print a message to the LCD.
    lcd.print("RPM");

  rpm = 0;

  pinMode(ecuIn, INPUT_PULLUP);
  pinMode(sparkCutPin, OUTPUT);

  pinMode(shiftUpButton, INPUT_PULLUP); // check if pullup is correct
  pinMode(shiftDownButton, INPUT_PULLUP);

  pinMode(shiftDownOut, OUTPUT);
  pinMode(shiftUpOut, OUTPUT);

  attachInterrupt(digitalPinToInterrupt(ecuIn), findRpm, RISING);
  delay(100);

  //Serial.begin(115200);

  pinMode(13, OUTPUT); // delete, for testing only

  //initialize neoPixel ledStrip
  for (int colorSetVar = 0; colorSetVar <= 15; colorSetVar++) {
    ledStrip.setPixelColor(colorSetVar, 0, 0, 0, 255);
  }
  ledStrip.setBrightness(100); // 0 to 255
  ledStrip.show();
}

void loop() {

  lcd.setCursor(0, 1);
  lcd.print(rpm);

  delay(150);
  if (rpm <= 1900) {
    heartbeat();
  }

  tachLights();
}

Got a scope you can use to look at the waveform and make sure there are no anomalies?

Delta_G: Got a scope you can use to look at the waveform and make sure there are no anomalies?

Not at the moment, I can try to get access to one during the week at school. I was hoping someone would be able to point out any mistakes/breach of best practice that I have made.

I imagine it’s something grabbing the cpu for a bit and delaying your interrupt. The timer for millis() or something to do with the led strip.

Do you get repeated readings in a row at 740 or just one? If it’s repeated i think it would have to be the generator.

I would run through the calculations by hand and see how much delay it takes.

bill2009:
I imagine it’s something grabbing the cpu for a bit and delaying your interrupt. The timer for millis() or something to do with the led strip.

Do you get repeated readings in a row at 740 or just one? If it’s repeated i think it would have to be the generator.

I would run through the calculations by hand and see how much delay it takes.

It’s usually just one or two, and I get the same issue with the led strip code commented out. For what it’s worth, the hardware setup is just connecting the grounds of the two boards together and connecting a pulsed digital pin of the ESP to my Arduino. I have tried using both a separate pulldown resistor and the programmable pull ups.

pm = ceil((60 * (1 / (timeDif / 1000000))));

I'll note that this math could be simplified. Remember that micros values are all by 4's, so figure out what value of timeDif is giving you the 750 and try it being off by 4 or 8 in one direction or the other and see if that gets you the 740. In that case it is just a matter of the small offset in the timing. Every so often you come up one increment slower.

Time values should be unsigned long.

Why are you using ceil() on an integer value?

Do you need better than 1 RPM resolution? If not you might be better off doing everything in integers:

unsigned long lastMicros;


// calculate rpm from ecu pulses
void findRpm() {
  unsigned long currentMicros = micros();


  if (currentMicros != lastMicros) {
    unsigned long timeDif = currentMicros - lastMicros;
    lastMicros = currentMicros;
    rpm = 60000000UL / timeDif;
  }
}

johnwasser: Time values should be unsigned long.

Why are you using ceil() on an integer value?

Do you need better than 1 RPM resolution? If not you might be better off doing everything in integers:

unsigned long lastMicros;

// calculate rpm from ecu pulses void findRpm() {   unsigned long currentMicros = micros();

  if (currentMicros != lastMicros) {     unsigned long timeDif = currentMicros - lastMicros;     lastMicros = currentMicros;     rpm = 60000000UL / timeDif;   } }

Nope, I would be happy with +- 200rpm actually. I'll give this a shot. The problem is that our engine redlines at around 12,500rpm, and at that point the code fluctuates about 1k rpm which I can't have.

I had bad experiences with the esp8266. It randomly stopped transmitting for seconds at a time, for no apparent reason and with no warning. Usually that wasn't a huge problem but one time in our test/production environment it didn't turn the water off until 2 seconds too late and ruined a batch of concrete. That was not acceptable. We yanked the device and built a new one that used RS485. It was slower and more of a pain to program, but more reliable.

Gave it a shot, did everything with ints and I'm getting huge fluctuations still above 12k rpm. At this point I think it's the source but I can't test it right now.

@jimmus This gives me hope then. I'll try and test it on last year's car or with an oscilloscope. In the mean time, any ideas on how to generate a clean pulse? I was thinking of using a headphone jack with an audio pulse but I'm not sure if that will work.

I just remembered I have a Bluno beetle, I'll give that a shot.

EDIT: Thank you to everyone with suggestions, turned out it was the ESP8266 that was causing the issues. When I hooked up the Bluno as a pulse generator, the Arduino managed to be accurate to about 45rpm @ 15k rpm.

Hi,

Put together a simple LM555 astable circuit, designed to output 750RPM signal.

Tom.... :)

If you want precise timing independent of software, use the Input Capture feature of Timer1. It will copy the count in Timer1 to the Input Capture Register on the edge of an external signal. You can then get timing between edges with a precision to 1/16th of a microsecond. This will avoid the problem of other interrupts delaying your timing interrupt.