Go Down

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

travisr100

I'm working on a tachometer using an IR sensor.  It mostly works but I keep getting some peculiar readings.  Here is some serial output showing what I'm talking about...

currTime=1659791008 prevTime=1659664548 interval=126460 spindleRPM=474 rpmd=0
currTime=1659791008 prevTime=1659664548 interval=126460 spindleRPM=474 rpmd=0
currTime=1659917544 prevTime=1659791008 interval=126536 spindleRPM=474 rpmd=0
currTime=1660053264 prevTime=1660044076 interval=9188 spindleRPM=6530 rpmd=6056
currTime=1660053264 prevTime=1660044076 interval=9188 spindleRPM=6530 rpmd=0
currTime=1660170544 prevTime=1660053264 interval=117280 spindleRPM=511 rpmd=-6019
currTime=1660297076 prevTime=1660170544 interval=126532 spindleRPM=474 rpmd=-37
currTime=1660297076 prevTime=1660170544 interval=126532 spindleRPM=474 rpmd=0
currTime=1660423608 prevTime=1660297076 interval=126532 spindleRPM=474 rpmd=0
currTime=1660550084 prevTime=1660423608 interval=126476 spindleRPM=474 rpmd=0
currTime=1660550084 prevTime=1660423608 interval=126476 spindleRPM=474 rpmd=0


If the code helps, here it is...

Code: [Select]

// Screen dimensions
#define SCREEN_WIDTH  128
#define SCREEN_HEIGHT 128 

// You can use any (4 or) 5 pins
#define SCLK_PIN 13 //clk/sck
#define MOSI_PIN 3 //din
#define DC_PIN   4 //data command
#define CS_PIN   5
#define RST_PIN  6

// Color definitions
#define  BLACK           0x0000
#define BLUE            0x001F
#define RED             0xF800
#define GREEN           0x07E0
#define CYAN            0x07FF
#define MAGENTA         0xF81F
#define YELLOW          0xFFE0 
#define WHITE           0xFFFF

#define LED_PIN     2
#define LED_COUNT  24
#define BRIGHTNESS 20 //0 to 255

#include <Adafruit_GFX.h>
//#include <Adafruit_SSD1351.h>
#include <Adafruit_SSD1331.h>

#include <Adafruit_NeoPixel.h>
#include <SPI.h>
#include <Fonts/FreeMono9pt7b.h>
//#include <Fonts/FreeMono12pt7b.h>
#include <Fonts/FreeMono18pt7b.h>
//#include <Fonts/FreeMono24pt7b.h>
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeMonoBold18pt7b.h>

//Adafruit_SSD1351 tft = Adafruit_SSD1351(SCREEN_WIDTH, SCREEN_HEIGHT, CS_PIN, DC_PIN, MOSI_PIN, SCLK_PIN, RST_PIN);
Adafruit_SSD1331 tft = Adafruit_SSD1331(CS_PIN, DC_PIN, MOSI_PIN, SCLK_PIN, RST_PIN);

Adafruit_NeoPixel ring(LED_COUNT, LED_PIN, NEO_RGBW + NEO_KHZ800);


int spindleRPM = 0;
int prevRpm = 0;
volatile unsigned long rpmArray[2] = {0,0}; //array to hold the latest microseconds and previous microseconds
unsigned long tempArray[2] = {60000000, 0};  //temporary array for copying the value of rpmArray so it can be worked on after interrupts started again
long interval = 0; //time between IR pulses
int rightDigits = 0; //stores the portion of the rpm value from 0 to 99 for the ring display
int rpmDifference = 0;  //difference between the current rpm reading and the previous reading
//long currTime = 0;
//long prevTime = 0;
byte mappedPixel = 0;  //the pixel to light up on the ring representing RPM from 0 to 99
int tailSize = 0;  //the size of the "tail" trailing the lighted pixel
byte rpmDigits[4] = {0,0,0,0};  //array to hold digits of the RPM.  Max RPM 9,999.
byte rpmDigitsPrev[4] = {0,0,0,0};  //array to hold previous value of rpmDigits for comparison purposes


void setup(){
  pinMode(21, INPUT_PULLUP);
  Serial.begin(9600);
  tft.begin();
  attachInterrupt(digitalPinToInterrupt(21), isr, FALLING); //Interrupts are called on Rise of Input
  ring.begin();           // Initialize NeoPixel ring object
  ring.show();            // Turn OFF all pixels ASAP
  ring.setBrightness(20); // Set brightness to about 1/5 (max = 255)
  tftPrintBackground();
}

 
void loop(){
  noInterrupts();
  memcpy(tempArray, rpmArray, sizeof tempArray);
  interrupts();
  interval = (tempArray[0] - tempArray[1]);
  spindleRPM = (60*1000000)/interval;
  if (spindleRPM < 0) spindleRPM = 0;  //prevent rpm from being negative from dividing by 0, issue on startup
  rpmDifference = (spindleRPM - prevRpm);
  rightDigits = (spindleRPM % 100);
  displayDigits();  //Displays the RPM digits on the display


 

  if (spindleRPM > 0 ) {
    tftPrintSpindleRPM();
    memcpy(rpmDigitsPrev, rpmDigits, sizeof rpmDigits);
    printx100(); //prints "x100" under RPM if the RPM is > 100
    displayPixel(ring.Color(255,   0,   0), rightDigits, rpmDifference);
    ring.show();
    prevRpm = spindleRPM;
    Serial.print("currTime=");
    Serial.print(tempArray[0]);
    Serial.print(" prevTime=");
    Serial.print(tempArray[1]);
    Serial.print(" interval=");
    Serial.print(interval);
    Serial.print(" spindleRPM=");
    Serial.print(spindleRPM);
    Serial.print(" rpmd=");
    Serial.println(rpmDifference);
    //prevTime = currTime;
  } 
}

void isr(){
  rpmArray[1] = rpmArray[0];
  rpmArray[0] = micros();
}

void displayDigits(){  //breaks out the digits of the RPM and puts them in an array for display.
  int myRPM = spindleRPM;
  if (spindleRPM > 99) myRPM /= 100;
  for (byte i = 0; i < 2; i++) {
    rpmDigits[i] = myRPM % 10;
    myRPM /= 10;
  }
}

void printx100(){
  if (spindleRPM >= 100 && prevRpm < 100) {
    tft.setCursor(5, 20);
    tft.setTextColor(RED); 
    tft.println("x100");
    tft.setFont();
  }
  else if (spindleRPM < 100 &&  prevRpm >= 100) {
    tft.setCursor(5, 20);
    tft.setTextColor(BLACK); 
    tft.println("x100");
    tft.setFont();
  }
}




void displayPixel(uint32_t color, byte rightDigits, int rpmDifference){
//  Serial.print("rpmd= ");
//  Serial.print(rpmDifference);
  rpmDifference = constrain(rpmDifference, -50, 50);  //limiting the acceleration value to 100 or less in either direction
//  Serial.print(" rpmdc= ");
//  Serial.print(rpmDifference);
 
  mappedPixel = map(rightDigits, 0, 99, 0, 23);
  tailSize = map(rpmDifference, -50, 50, -23, 23);
//  Serial.print(" tailsize= ");
//  Serial.println(tailSize);
  ring.clear();
  ring.setPixelColor(mappedPixel, color);         //  Set pixel's color (in RAM)
 
//  Serial.print("Mapped=");
//  Serial.println(mappedPixel);
 
  int m = mappedPixel;
  int brightness = 50;
  int x = 50;
  for (int i=mappedPixel-tailSize; i < mappedPixel; i++){
    uint32_t rgbcolor = ring.ColorHSV(65000, 255, 255);
    x += 10;
    m = i;
    if (m < 0) {
      m=m+24;
    }
//    Serial.println(m);
    ring.setPixelColor(m, rgbcolor);
  }
 
}




void tftPrintSpindleRPM(){
  tft.setFont(&FreeMono18pt7b);
  tft.setTextSize(1);

 //print previous spindleRPM in black to clear it
  tft.setCursor(50, 23);
  tft.setTextColor(BLACK);
 
  if (rpmDigits[1] != rpmDigitsPrev[1]){
    tft.print(rpmDigitsPrev[1]);
  }
  tft.setCursor(70, 23);
  if (rpmDigits[0] != rpmDigitsPrev[0]){
    tft.print(rpmDigitsPrev[0]);
  }
   
  //print the spindleRPM in white
  tft.setCursor(50, 23);
  tft.setTextColor(WHITE);
 
  if (rpmDigits[1] > 0 && rpmDigits[1] != rpmDigitsPrev[1]){
    tft.print(rpmDigits[1]);
  }
  tft.setCursor(70, 23);
  if (rpmDigits[0] != rpmDigitsPrev[0]){
    tft.print(rpmDigits[0]);
  }
  tft.setFont();
}
 

   





void tftPrintBackground() {
  tft.fillScreen(BLACK);

  tft.drawRect(0, 33, 96, 31, GREEN);
  tft.drawLine(31, 33, 31, 64, GREEN);
  tft.drawLine(62, 33, 62, 64, GREEN);

  tft.setFont(&FreeSans9pt7b);
  tft.setCursor(1, 13);
  tft.setTextColor(RED); 
  tft.println("RPM");
  tft.setFont();

  tft.setCursor(7, 38);
  tft.setTextColor(RED);
  tft.setTextSize(1);
  tft.println("MTR");

  tft.setCursor(3, 52);
  tft.setTextColor(WHITE);
  tft.setTextSize(1);
  tft.println("3600");
 
  tft.setCursor(38, 38);
  tft.setTextColor(RED);
  tft.setTextSize(1);
  tft.println("DRV");

  tft.setCursor(35, 52);
  tft.setTextColor(WHITE);
  tft.println("1350");
  tft.setFont();

  tft.setCursor(71, 38);
  tft.setTextColor(RED);
  tft.setTextSize(1);
  tft.println("FPR");

  tft.setCursor(64, 52);
  //tft.setCursor(60, 72);
  tft.setTextColor(WHITE);
  tft.println(".0209");
  tft.setFont();
}

noweare

We don't know what doing.You need to give more information.
Like what are you expecting, other than the numbers look funny.

What are you expecting from the numbers? 
What are you measuring the rpm of ( a motor spindle) ?
Is what ever your measuring constant speed.

I looks like there is gearing of some kind involved. 126 milliseconds is about 8 rpms but you have
474 rpms.

Your turning off interrupts to do a copy. Could you be missing interrupts in that period of time?
Whats your loop time ? If it is fast compared to the interrupts you don't have to turn off interrupts.

set a flag in the interrupt and in loop when the flag is 1 then do the copy or any other stuff that way
your not doing it more than once.


Gates

You might want to look at the .h files for these include files.  This could help you see some pins have already defined.
Code: [Select]
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1331.h>
#include <Adafruit_NeoPixel.h>
#include <SPI.h>
#include <Fonts/FreeMono9pt7b.h>
#include <Fonts/FreeMono18pt7b.h>
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeMonoBold18pt7b.h>

travisr100

We don't know what doing.You need to give more information.
Like what are you expecting, other than the numbers look funny.

What are you expecting from the numbers? 
What are you measuring the rpm of ( a motor spindle) ?
Is what ever your measuring constant speed.

I looks like there is gearing of some kind involved. 126 milliseconds is about 8 rpms but you have
474 rpms.

Your turning off interrupts to do a copy. Could you be missing interrupts in that period of time?
Whats your loop time ? If it is fast compared to the interrupts you don't have to turn off interrupts.

set a flag in the interrupt and in loop when the flag is 1 then do the copy or any other stuff that way
your not doing it more than once.


Sorry for not giving more information.  There is a lot of code that I posted that doesn't have anything to do with the problem but I've seen people before get upset when the entire code isn't posted.

I've got an IR sensor that's looking at a piece of reflective tape on a rotating wheel. Each time it sees the tape the ISR is called.  When it's called it measures the time in microseconds into an array and moves the previous value stored to a second position in the array.  Each trip through the sketches main loop it is looking at these two times and calculating what the latest RPM is.

What I posted is the serial output I recorded on the console.  It is showing the two time values from the array (currTime and prevTime), the difference between these two times (interval), the calcluated RPM based on this interval, and the RPM difference from the previous time it was calculated. 

During the serial output I posted the motor is running at a near constant RPM of 474.  Even though the wheel is spinning at a constant RPM I will periodically get "noise."   You can see where the RPM jumps to approximately 6500 all of the sudden when in reality the wheel was running at a constant RPM. 

It's microseconds, not milliseconds I'm measuring thus the formula of 60*1000000/interval to come up with the RPM.  So no, no gearing involved. 

I am turning off interrupts to do a copy as that's what I've seen suggested in other posts.  Yes, I could be missing interrupts with each trip through the main loop of the sketch but it really shouldn't matter.  Or at least I don't believe it should.  It's obvious through the serial output I posted that the ISR is running at least more than once with each trip through the loop otherwise the prevtime value would always be equal to the previous currtime value.   

There are two approaches to calculating RPM.  One is to run the loop at some interval and count the number of pulses during that interval.  This proved to be very inaccurate and causes the RPM to jump by multiples of 60 which has been documented in lots of other posts.  The solution everyone offered was to calculate the time between pulses of the IR instead which is what I'm doing.  Seems to work fine except for the random odd numbers as I posted above.  Something is going on I just can't seem to figure out what.  It's almost like I'm making it through the loop twice before the ISR has had a chance to fire?

travisr100

You might want to look at the .h files for these include files.  This could help you see some pins have already defined.

I looked at the libraries and don't see any pins defined in them that I'm trying to use.  I also just tried using a different IR sensor on a different pin.  Results don't change.

johnwasser

After removing the double-sampling (reporting the same 'curr' and 'prev' times twice):


currTime=1659791008 prevTime=1659664548 interval=126460 spindleRPM=474 rpmd=0
currTime=1659917544 prevTime=1659791008 interval=126536 spindleRPM=474 rpmd=0
Note: One rotation not reported. Next 'prevTime' is 126532 microseconds after previous 'currTime'
currTime=1660053264 prevTime=1660044076 interval=9188 spindleRPM=6530 rpmd=0
currTime=1660170544 prevTime=1660053264 interval=117280 spindleRPM=511 rpmd=-6019
currTime=1660297076 prevTime=1660170544 interval=126532 spindleRPM=474 rpmd=-37
currTime=1660423608 prevTime=1660297076 interval=126532 spindleRPM=474 rpmd=0
currTime=1660550084 prevTime=1660423608 interval=126476 spindleRPM=474 rpmd=0


I note that 117280 + 9188 = 126468 so it looks like there was one spurious interrupt part way through a rotation.  Perhaps a flash of light or some electrical noise triggered the interrupt.
Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp

travisr100

After removing the double-sampling (reporting the same 'curr' and 'prev' times twice):


currTime=1659791008 prevTime=1659664548 interval=126460 spindleRPM=474 rpmd=0
currTime=1659917544 prevTime=1659791008 interval=126536 spindleRPM=474 rpmd=0
Note: One rotation not reported. Next 'prevTime' is 126532 microseconds after previous 'currTime'
currTime=1660053264 prevTime=1660044076 interval=9188 spindleRPM=6530 rpmd=0
currTime=1660170544 prevTime=1660053264 interval=117280 spindleRPM=511 rpmd=-6019
currTime=1660297076 prevTime=1660170544 interval=126532 spindleRPM=474 rpmd=-37
currTime=1660423608 prevTime=1660297076 interval=126532 spindleRPM=474 rpmd=0
currTime=1660550084 prevTime=1660423608 interval=126476 spindleRPM=474 rpmd=0


I note that 117280 + 9188 = 126468 so it looks like there was one spurious interrupt part way through a rotation.  Perhaps a flash of light or some electrical noise triggered the interrupt.
I too have been thinking this is some kind of "noise" or spurious interrupt.  Perhaps the IR sensor when it sees the reflective tape is quickly rising and falling twice or something.  In order to make this more understandable I stripped out all the other code and tried to simplify it for understanding it here.  With the other code stripped out, here's what I have...

Code: [Select]


int spindleRPM = 0;
int prevRpm = 0;
volatile unsigned long rpmArray[2] = {0,0}; //array to hold the latest microseconds and previous microseconds
unsigned long tempArray[2] = {60000000, 0};  //temporary array for copying the value of rpmArray so it can be worked on after interrupts started again
long interval = 0; //time between IR pulses
int rpmDifference = 0;  //difference between the current rpm reading and the previous reading



void setup(){
  pinMode(21, INPUT);
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(20), isr, FALLING); //Interrupts are called on Rise of Input
 
}

 
void loop(){
  noInterrupts();
  memcpy(tempArray, rpmArray, sizeof tempArray);
  interrupts();
  interval = (tempArray[0] - tempArray[1]);
  spindleRPM = (60*1000000)/interval;
  if (spindleRPM < 0) spindleRPM = 0;  //prevent rpm from being negative from dividing by 0, issue on startup
  rpmDifference = (spindleRPM - prevRpm);
 


 

  if (spindleRPM > 0 ) {
   
    prevRpm = spindleRPM;
    Serial.print("currTime=");
    Serial.print(tempArray[0]);
    Serial.print(" prevTime=");
    Serial.print(tempArray[1]);
    Serial.print(" interval=");
    Serial.print(interval);
    Serial.print(" spindleRPM=");
    Serial.print(spindleRPM);
    Serial.print(" rpmd=");
    Serial.println(rpmDifference);
  } 
}

void isr(){
  rpmArray[1] = rpmArray[0];
  rpmArray[0] = micros();
}



I decided to go ahead and run it to verify I didn't remove anything that should have been left in.  What's interesting is that now that I stripped everything else out, the problem goes away.  I can sit and watch the serial monitor and don't see the problem ever occur where as with all the other code in there the problem seems to occur at least once every 15 seconds or so.  Now I'm really stumped.  I didn't figure stripping out all the other code would make a difference.

travisr100

I spoke to soon.  I let it run for about 5 minutes and never observed the problem then suddenly saw it again.  But now it seems to only occur once every 5 to 7 minutes where with the other code it was happening every 10 to 15 seconds.

I'm almost to the point of trying to set this up with a hall sensor instead to see if I observe the same problem.  But seems way too much of a coincidence that with the other code taken out it almost never occurs.

travisr100

And of course now I'm running it with all the other code and almost can't get it to occur.  Ran for 10 minutes without seeing it.

John, I went back and read your post again.  Interesting observation you had about the sum of those two numbers.  I'm thinking you may be right.  During my latest run I only got it to happen once in 5 minutes but when it did, the two numbers added up to close to all the other intervals that were being measured.  I wonder if it's not stray light triggering the sensor.

travisr100

After removing the double-sampling (reporting the same 'curr' and 'prev' times twice):


currTime=1659791008 prevTime=1659664548 interval=126460 spindleRPM=474 rpmd=0
currTime=1659917544 prevTime=1659791008 interval=126536 spindleRPM=474 rpmd=0
Note: One rotation not reported. Next 'prevTime' is 126532 microseconds after previous 'currTime'
currTime=1660053264 prevTime=1660044076 interval=9188 spindleRPM=6530 rpmd=0
currTime=1660170544 prevTime=1660053264 interval=117280 spindleRPM=511 rpmd=-6019
currTime=1660297076 prevTime=1660170544 interval=126532 spindleRPM=474 rpmd=-37
currTime=1660423608 prevTime=1660297076 interval=126532 spindleRPM=474 rpmd=0
currTime=1660550084 prevTime=1660423608 interval=126476 spindleRPM=474 rpmd=0


I note that 117280 + 9188 = 126468 so it looks like there was one spurious interrupt part way through a rotation.  Perhaps a flash of light or some electrical noise triggered the interrupt.
John, I think you nailed it.  I've been sitting here watching it run and looking at each instance where it occurs.  So far each time it happens if you add the two up they come out to right at what the interval should have been.  It must be seeing some stray light that's somehow triggering it part way through a rotation.

TomGeorge

HI,
Just out of curiosity, you have an IR sensor, reflective tape.
What is your source of IR energy to reflect off the tape?
Is the tape specified for IR reflection?

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

travisr100

I'm using these emitter/sensors:

FTCBlock 10PCS IR Infrared Obstacle Avoidance Sensor Module for Arduino Smart Car Robot https://www.amazon.com/dp/B07H2TH8BH/ref=cm_sw_r_cp_tai_dj6DDb8HJ0KGM

The tape is tape that came with a handheld IR tachometer so I assume so.

From what I'm observing it doesn't seem to have any problem sensing the tape.  It seems like it's sensing an extra pulse during a revolution.

johnwasser

From what I'm observing it doesn't seem to have any problem sensing the tape.  It seems like it's sensing an extra pulse during a revolution.
Does the extra pulse usually come around the same place in the revolution?  If so you might just have a shiny spot.
Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp

travisr100

Not in front of the setup at the moment to check that.  But I'll give it a look when I can.  That is possible.  Seems to be awfully intermittent though.  It can run over 1000 revolutions without the problem happening.  I've got a hall sensor on order and going to see if that fixes the problem although I wanted to stay away from using any magnets.

KrisKasprzak

Travisr100.

Did you get this issue fixed? I have the same exact problem using these sensors. I suspect my issue is stray light, but not sure--I feel my setup is well hidden from light.

I've implemented software and hardware debouncers, and that seemed to help but not totally fixed my issue. Hoping you have the magic sauce :)

Thanks,

Kris

Go Up