Go Down

Topic: IR tachometer, weird readings  (Read 621 times) previous topic - next topic


Sep 18, 2019, 03:54 am Last Edit: Sep 18, 2019, 03:55 am by SteveMann
I know this is a Band-Aid, but could you accept throwing out readings that aren't within, say 20% of the prior reading?

But, I am reluctant to do Band-Aids in my projects without knowing what is really happening.
Fritzing pictures are NOT schematics. I don't speak Fritzing.

Please do not ask for help by PM. I will not respond. If you need help, post a question on the appropriate forum.

Click on Add Karma if I helped you.


Sep 18, 2019, 08:18 am Last Edit: Sep 18, 2019, 08:21 am by TomGeorge
Thanks. OPs pic/circuit.

Tom. :)
Everything runs on smoke, let the smoke out, it stops running....


Sep 18, 2019, 02:25 pm Last Edit: Sep 18, 2019, 02:29 pm by travisr100
Tom, how did you get that image inline on the post?  When I click the insert image button it's asking me for a URL.  Doing it as an attachment?

Steve, I have considered throwing out the readings based on some criteria.  You can see my console output says "discard" before several of the readings which is just me debugging to determine what to throw away.  As you said, I hate doing that without knowing what's going on.  In addition, kind of difficult to know what to throw away with this particular problem.  My "discard" is looking  at the length of the current period compared to last period and throwing it away if it's more than 2000.  This won't catch it every time however.  In addition there's the situation of acceleration.  If I share this code someone else's motor parameters may be completely different than mine and what I use as a means to discard readings may not work for someone else's situation.


For any of you that want to see what the final code looks like here it is.  Feel free to criticize at will. 

Code: [Select]

volatile unsigned long period = 0; //time between pulses
volatile unsigned long prevTime = 0; //the last micros() we measured
volatile bool newMeasurementAvailable = 0;
unsigned long stablePeriod = 0; //
byte pointer = 0; //pointer to keep track of next position in array to store value
byte numPrevReadings = 0; //the number of readings to smooth, can be from 1 to 10, always starts at 1, dynamically changed based on period/RPM

unsigned long matrix[4][10] = {  //initialize the array with zeros
                              {0,0,0,0,0,0,0,0,0,0}, //stores the pulse period
                              {0,0,0,0,0,0,0,0,0,0}, //stores raw rpm
                              {0,0,0,0,0,0,0,0,0,0}, //stores smoothed rpm of current reading and numPrevReadingss
                              {0,0,0,0,0,0,0,0,0,0}  //not used just haven't decided if I'm going to or not

unsigned long total = 0;

void setup(){
  pinMode(21, INPUT);
  attachInterrupt(digitalPinToInterrupt(2), isr, FALLING); //Interrupts are called on Rise of Input

void loop(){
  if (newMeasurementAvailable) {
    stablePeriod = period;

    newMeasurementAvailable = 0;
    int rpmd = getRPM(0) - getRPM(1);  // rpm delta from the previous reading
    Serial.print(" period0=");
    Serial.print("\t rpm=");
    Serial.print("\t rpmd=");
    Serial.print("\t srpm=");


void storeAndProcess() {
  matrix[0][pointer] = stablePeriod;  //store the period
  matrix[1][pointer] = (60 * 1000000) / matrix[0][pointer];  //store the raw RPM

  //The next line determines the number of previous periods to use to calculate the average. As speed increases, number of previous readings will increase to a maximum of 9.
  //Using 125000 as a value below is a period of 1/8 of a second (1,000,000/125,000).  This equates to 480 RPM.  So up to 480 RPM it uses one reading.  At every multiple
  //of 480 RPM the number of values used for smoothing will increase by 1 up to 4800 RPM.  With an array of 10 elements as defined in this sketch that only gives us a max
  //of the current value plus the 9 previous values.  The array could be defined with more than 10 elements if you wanted to change this.  Additionally instead of using
  //simple division as I've done you could use the map function to define the range of potential RPM's at which you want smoothing done.
  numPrevReadings = 125000 / stablePeriod;
  numPrevReadings = constrain(numPrevReadings, 0, 9);
  total = stablePeriod;  //start with the period we just measured
  if (numPrevReadings != 0) {
    for (int i = 0; i < numPrevReadings; i++) {  //add numPrevReadings previous periods
      total += getPeriod(i);
  matrix[2][pointer] = ((numPrevReadings + 1) * (60 * 1000000)) / total;  //store the smoothed RPM
  if (pointer++ >= 9) pointer = 0;  //increment the pointer to the position to store the next reading

unsigned long getPeriod(byte position) {
  //position  = 0 returns the last recorded value
  //position = 1 returns next oldest and so on
  int positionToGet = (pointer - 1 - position);
  if (positionToGet < 0) positionToGet += 10;
  unsigned long value = matrix[0][positionToGet];
  return value;

unsigned long getRPM(byte position) {
  int positionToGet = (pointer - 1 - position);
  if (positionToGet < 0) positionToGet += 10;
  unsigned long value = matrix[1][positionToGet];
  return value;

unsigned long getSmoothedRPM(byte position) {
  int positionToGet = (pointer - 1 - position);
  if (positionToGet < 0) positionToGet += 10;
  unsigned long value = matrix[2][positionToGet];
  return value;

void isr(){
  newMeasurementAvailable = 1;
  period = micros() - prevTime;
  prevTime = micros();

Go Up