Interrupt procedure for RPM measuring with KY-024

Hey, I'm trying to use the following code to measure the RPM using a KY-024 sensor on my Arduino UNO. I try to count the number of interrupts (revolutions) in a 1000ms delay and calculate the RPM. However, when the interrupt is called, the 'delay(1000)' step (in the loop section!) seems to be skipped and I get erratic results. It seems that the progamme doesn't wait anymore and executes the 'loop' everytime the magnet is sensed/interupt is called.

May be something all too simple..please be gentle, i'm a newbie :slight_smile:

Also note that I used a IR sensor module in the same way and with the same code; it worked perfectly. Problem with that was that daylight messed up the working of the IR. I rather want to use the Hall sensor.

Thanx a lot!

The code:

#include <LiquidCrystal_I2C.h> // Library for LCD

LiquidCrystal_I2C lcd(0x27, 16, 2); // I2C address 0x27, 16 column and 2 rows

volatile float revolutions=0;
int rpm=0; // max value 32,767 16 bit
long  startTime=0;
long  elapsedTime;


void setup() {
  // put your setup code here, to run once:

  pinMode(3, INPUT_PULLUP);           // set pin to input
  
  lcd.init(); // initialize the lcd
  lcd.backlight();
  lcd.setCursor(0, 0);         // move cursor to   (0, 0)
  lcd.print("Snelheid");        // print message at (0, 0)
  lcd.setCursor(0, 1);         // move cursor to   (2, 1)
  lcd.print("0"); 

  Serial.begin(9600);

  attachInterrupt(digitalPinToInterrupt(3),interruptFunction,RISING);
}

void loop() {
  // put your main code here, to run repeatedly:
  revolutions=0; 
  rpm=0;
  startTime=millis();         
  
  delay(1000);
  //detachInterrupt(3);                
  
  //now let's see how many counts we've had from the hall effect sensor and calc the RPM
  elapsedTime=millis()-startTime;     //finds the time, should be very close to 1 sec
  
  if(revolutions>0)
  {
    rpm=(max(1, revolutions) * 60000) / elapsedTime;        //calculates rpm
  }
  lcd.setCursor(0,0);
  String outMsg = String("RPM :") + rpm;
  //fillMessage2DisplayWidth(outMsg);
  lcd.print(outMsg);
  Serial.println(outMsg);
}

void interruptFunction() //interrupt service routine
{  
  revolutions++;
}

The sketch:
Sketch KY-024

Why is 'revolutions' a floating point type? Can you count fractional revolutions?

Well then, it's not likely to be a software problem then, is it? Please post a complete schematic and links to the hardware.

Hi, thnx for your answer. I agree and I changed to 'volatile int' but same problem.

Hi. I attached a link to the sketch in the orginal post. Or what do you mean?

Just to add the output of the serial monitor:

Post a diagram of your hardware connections and provide web links that provide information about the sensors you are using.

DO NOT post screen shots of code or printed output. Post text.

Link to KY-024 sensor

Thank you. Now please post your hardware diagram. Show us how you have connected it.

should be

rpm=(max(1, revolutions) * 60000L) / elapsedTime;

I'll come to back to you for that.

Okay, we'll wait for you to read the forum guidelines.

long startTime=0; 
long elapsedTime;

should be

unsigned long startTime=0;
unsigned long elapsedTime;

Why?

Hi,
I didn't test this code, I just verified that it compiles correctly.
Test and let us know if it meets your needs.

#include <LiquidCrystal_I2C.h>            // Library for LCD
LiquidCrystal_I2C lcd(0x27, 16, 2);       // I2C address 0x27, 16 column and 2 rows
volatile long revolutions = 0;
int rpm = 0; // max value 32,767 16 bit
unsigned long  elapsedTime;
//-----------------------------------------------------------------------------
void setup() {
  pinMode(3, INPUT_PULLUP);           // set pin to input
  lcd.init();                         // initialize the lcd
  lcd.backlight();
  lcd.setCursor(0, 0);                // move cursor to   (0, 0)
  lcd.print("Snelheid");              // print message at (0, 0)
  lcd.setCursor(0, 1);                // move cursor to   (2, 1)
  lcd.print("0");
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(3), interruptFunction, RISING);
  elapsedTime = millis();              // Init elapsedTime
}
//-----------------------------------------------------------------------------
void loop() {
  if (millis() - elapsedTime >= 1000)                       // 1 seg passed?
  {
    cli();                                                  // Disable interrupts
    elapsedTime = millis();                                 // Recharge elapsedTime
    rpm = (max(1, revolutions) * 60000L) / elapsedTime;     //calculates rpm
    revolutions = 0;
    sei();                                                  // Enable interrupts
  }
  lcd.setCursor(0, 0);
  String outMsg = String("RPM :") + rpm;
  lcd.print(outMsg);
  Serial.println(outMsg);
}
//-----------------------------------------------------------------------------
void interruptFunction() //interrupt service routine
{
  revolutions++;
}

Because is the type of millis()

Oh, good catch. I was wondering what is the point of measuring delay(1000) each time through loop(). It's always going to be the same.

I think you could eliminate the whole millis() and time stamps and it wouldn't change the operation of the program.

It would be more appropriate to use millis() to trigger a measurement block every 1000ms., and eliminate the delay() completely.

HI. Thnx for your reply. No, it does still produce erratic results (including negative numbers for RPM, which puzzles me).

Hi,
I found an error in the code I posted. Wait I'll fix it.

actually there were several errors in the code I posted. Sorry.

Test this one now.

#include <LiquidCrystal_I2C.h>            // Library for LCD
LiquidCrystal_I2C lcd(0x27, 16, 2);       // I2C address 0x27, 16 column and 2 rows
volatile long revolutions = 0;
unsigned long rpm = 0;                    // max value 64 bit
unsigned long  elapsedTime;
//-----------------------------------------------------------------------------
void setup() {
  pinMode(3, INPUT_PULLUP);           // set pin to input
  lcd.init();                         // initialize the lcd
  lcd.backlight();
  lcd.setCursor(0, 0);                // move cursor to   (0, 0)
  lcd.print("Snelheid");              // print message at (0, 0)
  lcd.setCursor(0, 1);                // move cursor to   (2, 1)
  lcd.print("0");
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(3), interruptFunction, RISING);
  elapsedTime = millis();              // Init elapsedTime
}
//-----------------------------------------------------------------------------
void loop() {
  if (millis() - elapsedTime >= 1000)                       // 1 seg passed?
  {
    cli();                                                  // Disable interrupts
    elapsedTime = millis();                                 // Recharge elapsedTime
    rpm = (max(1, revolutions) * 60L);                      //calculates rpm
    revolutions = 0;
    sei();                                                  // Enable interrupts
  }
  lcd.setCursor(0, 0);
  String outMsg = "RPM :" + String(rpm);
  lcd.print(outMsg);
  Serial.println(outMsg);
}
//-----------------------------------------------------------------------------
void interruptFunction() //interrupt service routine
{
  revolutions++;
}

Try this modification to your code

#include <LiquidCrystal_I2C.h> // Library for LCD

LiquidCrystal_I2C lcd(0x27, 16, 2); // I2C address 0x27, 16 column and 2 rows

volatile unsigned long revolutions=0;
unsigned long rpm=0; 
// unsigned long  startTime=0;
// unsigned long  elapsedTime;


void setup() {
  // put your setup code here, to run once:

  pinMode(3, INPUT);           // set pin to input
  
  lcd.init(); // initialize the lcd
  lcd.backlight();
  lcd.setCursor(0, 0);         // move cursor to   (0, 0)
  lcd.print("Snelheid");        // print message at (0, 0)
  lcd.setCursor(0, 1);         // move cursor to   (2, 1)
  lcd.print("0"); 

  Serial.begin(9600);

  //attachInterrupt(digitalPinToInterrupt(3),interruptFunction,RISING);
}

void loop() {
  // put your main code here, to run repeatedly:
  revolutions=0; 
  rpm=0;
//  startTime=millis();         
  attachInterrupt(digitalPinToInterrupt(3),interruptFunction,RISING);
  delay(1000);
  detachInterrupt(digitalPinToInterrupt(3));

// invalid instruction corrected
//  detachInterrupt(3);  
// thanks   @gonpezzi

  //now let's see how many counts we've had from the hall effect sensor and calc the RPM
//  elapsedTime=millis()-startTime;     //finds the time, should be very close to 1 sec
  
  if(revolutions>0)
  {
    rpm = revolutions * 60UL;        //calculates rpm
  }
  lcd.setCursor(0,0);
  String outMsg = String("RPM :") + rpm;
  //fillMessage2DisplayWidth(outMsg);
  lcd.print(outMsg);
  Serial.println(outMsg);
}

void interruptFunction() //interrupt service routine
{  
  revolutions++;
}