[solved] Trouble with hall sensor and timed interrupts for revolution counting

Hi,
I’m trying to write a program that calculates the speed of a wheel with a hall sensor and am having trouble with counting the revolutions.
When I see the nowHall and lastHall values in the main loop on my serial monitor, the values properly reflect the rotational behavior of the wheel – when the magnet on the wheel moves close to the sensor, the value drops below 200. When the magnet is not present, the value stabilizes between 200 and 210. (The value goes above 210 when the magnet’s polarity is reversed.) I have plotted the serial data in excel and saw consistent results – downward spikes in the hall sensor values that gain (negative) amplitude and spread further apart as time goes on and the wheel slows down.
Every 1 seconds, it displays the value of “revolutions”. However this value is incorrect. Most clearly, at very slow speeds, it gives extremely high values sometimes higher than 50. No wild oscillations are observed on the serial monitor for the nowHall and lastHall variables, which would result in high values for revolutions. I’ve simplified my code (below) for troubleshooting purposes. It seems like the issue is with inaccurate counting of the “revolutions” variable.

#include <TimerOne.h>

int hallPin = A0;
int nowHall;
int lastHall;
int mph = 0;
//76 mm wheel = 3"
int circumference = 94; // 10x (inches)
int revolutions;
int count;


void setup() {
  pinMode(hallPin, INPUT);
  
  Timer1.initialize(1000000);
  Timer1.attachInterrupt(calcMPH);

// LED PIN SETUP
  pinMode(12,OUTPUT);
  pinMode(11,OUTPUT);
  pinMode(10,OUTPUT);
  pinMode(9,OUTPUT);
  pinMode(8,OUTPUT);
  pinMode(6,OUTPUT);
  pinMode(5,OUTPUT);
  
  digitalWrite(12,LOW);
  digitalWrite(11,LOW);
  digitalWrite(10,LOW);
  digitalWrite(9,LOW);
  digitalWrite(8,LOW);
  digitalWrite(6,LOW);
  digitalWrite(5,LOW);
  // END LED PIN SETUP
  
  //SERIAL DEBUGGUING
  Serial.begin(115200);
  
  revolutions = 0;
  
  lastHall = 205;
}


void loop() {
  
    nowHall = analogRead(hallPin);
      
    if ((nowHall < 200 ) && (lastHall > 200)){ 
          revolutions = revolutions + 1; //there has been 1 revolution
    }
  //  Serial.println(nowHall);
  //  Serial.println(lastHall);
    lastHall = nowHall;
}
    


void calcMPH()  {
  Serial.println("revolutions");
  Serial.println(revolutions);
  revolutions = 0;
}

TimerOne.h is from http://code.google.com/p/arduino-timerone/downloads/list (I’m using r11).

I am using an Arduino Uno SMD, a Texas Instruments DRV5053VAQLPG hall sensor (wired per the diagram in Section 9.2.2 of http://www.ti.com/lit/ds/slis153b/slis153b.pdf), and a 0.25 square magnet from Sparkfun (Magnet Square - 0.25" - COM-08643 - SparkFun Electronics). I don’t suspect these are causing the problem since the sensor values appear to read fine, but I could be wrong.
Thank you very much in advance for the help!

void calcMPH()  {
  Serial.println("revolutions");
  Serial.println(revolutions);
  revolutions = 0;
}

#1, you can’t do Serial inside an ISR. It will break things and cause weird stuff to happen.

#2 If revolutions is going to change in an ISR it should be volatile.

Really, you should get rid of the timer library and this interrupt altogether. This is NOT what interrupts are for. You can accomplish the same thing using the good ole Blink Without Delay technique to print your data out every second.

Like this:

int hallPin = A0;
int nowHall;
int lastHall;
int mph = 0;
//76 mm wheel = 3"
int circumference = 94; // 10x (inches)
int revolutions;
int count;

unsigned long previousMillis = 0;


void setup() {
  pinMode(hallPin, INPUT);

  // LED PIN SETUP
  pinMode(12,OUTPUT);
  pinMode(11,OUTPUT);
  pinMode(10,OUTPUT);
  pinMode(9,OUTPUT);
  pinMode(8,OUTPUT);
  pinMode(6,OUTPUT);
  pinMode(5,OUTPUT);

  digitalWrite(12,LOW);
  digitalWrite(11,LOW);
  digitalWrite(10,LOW);
  digitalWrite(9,LOW);
  digitalWrite(8,LOW);
  digitalWrite(6,LOW);
  digitalWrite(5,LOW);
  // END LED PIN SETUP

  //SERIAL DEBUGGUING
  Serial.begin(115200);

  revolutions = 0;

  lastHall = 205;
}


void loop() {

  nowHall = analogRead(hallPin);

  if ((nowHall < 200 ) && (lastHall > 200)){ 
    revolutions = revolutions + 1; //there has been 1 revolution
  }
  //  Serial.println(nowHall);
  //  Serial.println(lastHall);
  lastHall = nowHall;

  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= 1000){
    Serial.println("revolutions");
    Serial.println(revolutions);
    revolutions = 0;
    previousMillis = currentMillis;
  }
}

Thanks for the quick response Delta_G, but still getting similar results. This was my output when spinning the wheel, and includes it coming to a stop:

revolutions
0
revolutions
0
revolutions
0
revolutions
23
revolutions
19
revolutions
13
revolutions
14
revolutions
14
revolutions
7
revolutions
12
revolutions
17
revolutions
10
revolutions
22
revolutions
11
revolutions
11
revolutions
6
revolutions
18
revolutions
6
revolutions
8
revolutions
12
revolutions
11
revolutions
0
revolutions
34
revolutions
0
revolutions
0

I thought that I may have my defining values for when the magnet is present or not (<200 and >200 respectively) too close, so I tried separating to <198 and >202 based on the attached data for nowHall. I ended up getting 0 for all the rotational speeds I tried, which is not what I expected based on the data. I’m going to keep playing around with it - any other thoughts on the problem in the meantime?

Thanks!

I think I’ve solved the issue.

The way I was determining whether the magnet was present (nowHall<200 and lastHall>200) was resulting in additional revolutions due to the values being too close, and the sensor occasionally bouncing around those values.

Instead, I switched to using more separated thresholds, which seem to yield results which reflect the rotational speed of the wheel. Here is a snippet of the update:

void loop() {

  nowHall = analogRead(hallPin);
  
  if (nowHall < 198)  {
    if (count == 0)  {
      revolutions = revolutions+1;
    }
    count = 1;
  }
  
  if (nowHall > 204)  {
    count = 0;
  }
  
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= 1000){
   // Serial.println("revolutions");
   // Serial.println(revolutions);
    mph = .0568*circumference*revolutions;

Thanks again for the support on these forums and I hope this helps others.