RPM counter (Tachometer) with HALL sensor

Hi everyone

I' am currently making a project involving a Hallstate sensor to messure the RPM of a 4-stroke engine.
I think I am almost ther exept for one part. when I open the serial monitor I the RPM is quite accurate but how longer the program is running the worse the accuracy of the program. I think I have to use an Intterupt function but I dont know how and where. Can anyone help ith this?

Thanks a Lot.

unsigned long time;
const int  buttonPin = 2; 
const int ledPin = 13; 
float RPM;
float SEC;
float MIN;


int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button

void setup() {
 // initialize the button pin as a input:
  pinMode(buttonPin, INPUT);
  // initialize the LED as an output:
  pinMode(ledPin, OUTPUT);
  // initialize serial communication:
  Serial.begin(9600);
}
void loop() {

  buttonState = digitalRead(buttonPin);     // read the pushbutton input pin:
  if (buttonState != lastButtonState)       // compare the buttonState to its previous state 
 {if (buttonState == HIGH)                  // if the state has changed, increment the counter
 {buttonPushCounter++; }}                     // if the current state is HIGH then the button went from off to on:
  //Serial.println(buttonPushCounter);}}
  lastButtonState = buttonState;          // save the current state as the last state, for next time through the loop
  time = millis();
  SEC = (time/1000);
  MIN = (SEC/60);
  RPM = (buttonPushCounter/MIN);
  Serial.println(RPM);
  delay(50);}

Hi,
Try

Serial.begin(115200);

Then adjust your IDE monitor to that baud rate.

Have you googled;
arduino hall effect tachometer

Tom... :slight_smile:

I've tackled this in the past and have shared this code elsewhere in the forum I hope this helps:

#define ClockPin 2 // Must be pin 2 or 3

      // My Encoder has 400 Clock pulses per revolution
      // note that 150000.0 = (60 seonds * 1000000 microseconds)microseconds in a minute / 400 pulses in 1 revolution)
      // change the math to get the proper multiplier for RPM for your encoder
     // yours has 4 pulses in 1 revolution
      // note that 15000000.0 = (60 seonds * 1000000 microseconds)microseconds in a minute / 4 pulses in 1 revolution)
#define Multiplier 15000000.0 // don't forget a decimal place to make this number a floating point number
volatile long count = 0;
volatile int32_t dTime; // Delt in time
volatile bool DataPinVal;

void onPin2CHANGECallBackFunction(){ 
    static uint32_t lTime; // Saved Last Time of Last Pulse
    uint32_t cTime; // Current Time
    cTime = micros(); // Store the time for RPM Calculations
// calculate the DeltaT between pulses
    dTime = cTime - lTime; 
    lTime = cTime;

}


void setup() {
  Serial.begin(9600);
  // put your setup code here, to run once:
  pinMode(ClockPin, INPUT);  
//  pinMode(DataPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(ClockPin),onPin2CHANGECallBackFunction,RISING);
}

void loop() {
  float DeltaTime;
  float SpeedInRPM = 0;
  
  noInterrupts (); 
// Because when the interrupt occurs the EncoderCounter and SpeedInRPM could be interrupted while they 
// are being used we need to say hold for a split second while we copy these values down. This doesn't keep the 
// interrupt from occurring it just slightly delays it while we maneuver values.
// if we don't do this we could be interrupted in the middle of copying a value and the result get a corrupted value.
  DeltaTime = dTime;
  interrupts ();
  
  SpeedInRPM = Multiplier / DeltaTime; // Calculate the RPM Switch DeltaT to either positive or negative to represent Forward or reverse RPM
// use the speed and counter values for whatever you need to do.

  static unsigned long SpamTimer;
  if ( (unsigned long)(millis() - SpamTimer) >= (100)) {
    SpamTimer = millis();
    Serial.print(SpeedInRPM , 3);
    Serial.print(" RPM");
    Serial.println();
    SpeedInRPM = 0; // if no pulses occure in the next 100 miliseconds then we must assume that the motor has stopped
  }
}

Z

thx for the quick response.
I have tried to change baudrate but it doesn't schange the accurecy of the project.
and zhomeslice, your code works fine except for the last rule: the RPM does not change back to 0 after 100 milli seconds. any way to make sure it does?

Thanks a lot all!

millis() is the number of milliseconds since the Arduino started. You are basically computing the average RPM since the Arduino started which is why it gets worse the longer it runs. As it is coded your counter will overflow eventually and then it get even goofier.

I would count the pulses and smooth (running average) over about 3 seconds. The way I did it was to count the pulses every second and then average the last 3 seconds and finally convert to RPM.

Try this

    noInterrupts ();
// Because when the interrupt occurs the EncoderCounter and SpeedInRPM could be interrupted while they
// are being used we need to say hold for a split second while we copy these values down. This doesn't keep the
// interrupt from occurring it just slightly delays it while we maneuver values.
// if we don't do this we could be interrupted in the middle of copying a value and the result get a corrupted value.
  DeltaTime = dTime;
  dTime = 0; //<<< Added
  interrupts ();
  SpeedInRPM = (DeltaTime)? Multiplier / DeltaTime: 0.0; // Calculate the RPM Switch DeltaT to either positive or negative to represent Forward or reverse RPM

Z