Inaccurate Tachometer readings

Hey all,
I've built myself a bldc motor out of an alternator and put together a tachometer using the arduino to monitor the rpm of the motor using one of the hall effect sensors that I built into the motor. The problem is that the arduino is not giving accurate measurement.

here is the video of the set up if you would like to see what I'm talking about:

you can see what I'm talking about at 5:40.

I've basically got it counting every edge that comes by the hall effect sensor inside of a second, multiplying that by 60, then dividing that by seven (seven poles on the rotor in a alternator.)

The information I get back is not correct, in that before it hits 1000 rpm, it shows between 0 and 3000, and before 6000 it does the same thing. so it basically counts from 0 to 3000 then switches to 1000 and counts up to 3000 again, then switches to 6000 and continues counting. this is when the motor is running. When just hooking it up to a button, it will count around 150 rps for one button press. I thought Maybe it was a bounce issue, but counting 150 from 1 button press is a lot of bounce.

Anyway here is the code, thanks!

#include <LiquidCrystal.h>
int uperdown = 0;
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
unsigned long intMillTime = 0;
int alreadyUp = 0;
unsigned int RPS = 0;

void setup() {
lcd.begin(16, 2);
pinMode(11, INPUT);
intMillTime = millis();
lcd.setCursor(0,0);
lcd.print("RPM:");
lcd.setCursor(8,0);
lcd.print("RPS:");
}

void loop() {
//Timing
unsigned long intMillNow = millis();
unsigned long intTimeSpan = intMillNow - intMillTime;

//after a second has passed, print rpm and rps, then set counter to 0, and intMillTime to now.
if (intTimeSpan > 1000) {
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(RPS*60/7);
lcd.setCursor(8,1);
lcd.print(RPS);
intMillTime = millis();
RPS = 0;
}

//if rising edge, add to counter and set alreadyup so it doesn't run again.
uperdown = digitalRead(11);

if (uperdown == HIGH) {
if (alreadyUp == 0) {
RPS = RPS + 1;
}
alreadyUp = 1;
}

//if low keep alreadyup down
if (uperdown == LOW) {
alreadyUp = 0;

}
}

I'm not an expert programmer, but I'll take a guess... The symptoms sound like a classic case of overflowing and "wrapping around" your variable... You know how the odometer in older cars would roll-over, back to zero, after 100,000 miles? Things can get really fouled-up in confusing ways when signed integers roll-over.

Type int can go from around -32,000 to around +32,000. unsigned int goes from 0 to around 64,000. What values are you getting for RPS? Is it possible that you are exceeding the int limits when you multiply
RPS * 60?

If so, try changing unsigned int to unsigned long.

related subject recently discussed here - http://arduino.cc/forum/index.php/topic,97202.0.html -

Ha that integer missed my eye. I meant for everything to be long. Now I've switched it, and the counting up is fine now until it gets around 6000 RPM (700 rps) it starts to count down. another puzzle...

I thought maybe the math was going wrong in the print command so I also created an unsigned long RPM, and did the math in there, but I still get the counting down once it gets up passed 6000.

thanks for the reply!

that link is insightful, The interrupt approach seems a little simpler. I'm not worried about +/- 60 I would just like a general idea of how fast this thing is really spinning. now like I said, it is counting down once it gets around 6000 rpm.
Thanks for the link!

The rps is actually counting down at this point so I'm wondering if maybe I'm missing pulses?

The rps is actually counting down at this point so I'm wondering if maybe I'm missing pulses?

So you want a sketch that counts the missing pulses ..... eh..... think..... go for the interrupt version :wink:

Just tried the interrupt approach, getting the same result. Counts up to about 6600 then starts counting down as the motor gets faster...

#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 5, 4, 3, 11);

unsigned long intMillTime = 0;
unsigned long RPM = 0;
volatile unsigned long RPS = 0;

void setup() {

  attachInterrupt(0, countRPS, RISING);

  intMillTime = millis();

  lcd.begin(16, 2);
  lcd.setCursor(0,0);
  lcd.print("RPM:");
  lcd.setCursor(8,0);
  lcd.print("RPS:");

}

void loop() {
  //Timing
  unsigned long intMillNow = millis();
  unsigned long intTimeSpan = intMillNow - intMillTime;

  //after a second has passed, print rpm and rps, then set counter to 0, and intmilltime to now.
  if (intTimeSpan > 1000) {
   
    RPM = RPS * 60 / 7;

    lcd.setCursor(0,1);
    lcd.print("                ");
    lcd.setCursor(0, 1);
    lcd.print(RPM);
    lcd.setCursor(8,1);
    lcd.print(RPS);

    intMillTime = millis();
    RPS = 0;
  }
}
void countRPS(){
        RPS = ++RPS;
}

Please modify you post, select the code and press the # button, It will still have the same bugs but it will definitely look better. see below

refactored your code a bit

#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 5, 4, 3, 11);

unsigned long lastTime = 0;

unsigned long RPM = 0;
volatile unsigned long RPS = 0;
unsigned long lastRPS = 0;

void setup() 
{
  attachInterrupt(0, countRPS, RISING);

  lcd.begin(16, 2);
  lcd.setCursor(0,0);
  lcd.print("RPM:");
  lcd.setCursor(8,0);
  lcd.print("RPS:");
}

void loop() 
{
  if (millis() > lastTime ) 
  {
    lastTime += 1000;      // OK this fails after 49 days ... but keeps the essence clear

    unsigned long tmp = RPS;             // only read the counter once, 
    RPM = (tmp - lastRPS) * 60UL;      // we do math with the previous reading using the UL for long
    lastRPS = tmp;                           // which we remember
 
    lcd.setCursor(0, 1);
    lcd.print(RPM);
    lcd.print("  ");
    lcd.setCursor(8,1);
    lcd.print(lastRPS);
    lcd.print("  ");
  }
}

void countRPS()
{
  RPS++;
}

Thanks for the tip!

Okay so I tried the reworked code you gave me, and it looked good up until the 6600 mark again. The rpm was bouncing around alot too. The only thing I did from your rework to what I uploaded was dividing the rpm by 7 before it prints. 7 poles...

How many pulses per second do you think the arduino can catch? it gets to about 775 max and then starts counting down like it's missing. I need a real tach ha ha.

Well, I've gone and minimized it as much as I can come up with, thinking there might be to much code, but it won't go over that 6500-6600 RPM cap, just starts counting down. Only thing I can figure is the hall effect sensor is pulsing faster than the arduino can catch, but even that is only 770 hz at 6600 rpm. Don't arduinos run quite a bit faster than that? I was thinking of trying a decade counter to divide the pulse by seven and see if that takes the load off. Any other ideas? Could the LCD be in the way some how?

Code so far:

#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 5, 4, 3, 11);
unsigned long RPM = 0;
volatile unsigned long RPS = 0;
unsigned long lastTime = 0;

void setup() {
  attachInterrupt(0, countRPS, RISING);
  lcd.begin(16, 2);
  lcd.setCursor(0,0);
  lcd.print("RPM:");
}

void loop() {
  //after a second has passed, print RPM, then set RPS to 0.
  if (millis() > lastTime ) 
  {
    lastTime += 1000; 

    RPM = RPS / 7UL * 60UL;
    
    lcd.setCursor(0,1);
    lcd.print(RPM);

    RPS = 0;
  }
}
void countRPS(){
  RPS++;
}

So I went and added the Decade counter to divide the hall effect sensor output by 7. Basically the hall effect sensor clocks the decade counter, then the decade counter outputs to the arduino on the sixth output, and resets on the seventh so the arduino just has to multiply that input by 60 and print it. However it still won't go over 6600, it continues to count down once it gets there. I figure if it was the speed of the sampling, this would have fixed it since a decade counter can generally handle a clock speed of 5 mhz or atleast one mhz, and I'm only talking 770 hz. any clues?

If any one is wondering why it needs to be divided by seven, it's because the hall effect sensors are built into the alternator, and the rotor has 7 poles, so a full rotation would show as 7 pulses from one of the 3 hall effect sensors.

Helps appreciated!

If any one is wondering why it needs to be divided by seven, it's because the hall effect sensors are built into the alternator, and the rotor has 7 poles, so a full rotation would show as 7 pulses from one of the 3 hall effect sensors.

Why are there 3 hall effect sensors? Which one(s) are connected to the interrupt pin?

Why are there 3 hall effect sensors? Which one(s) are connected to the interrupt pin?

1 hall effect sensor for each stator winding. The alternator has three sets of stator windings, so there is one sensor per winding. Each sensor watches the rotor and pulses the winding it's positioned for. Since 7 pulses from any one of the sensors will equal one rotation, I just picked one.

What is the part number for the hall effect sensor you are using?

A3144

datasheet: