RPM sensing withot causing delay in sketch?

Hi guys,

I need some help here. I am far away from an expert coder, but done some smaller arduino projects with succes. However, now im stuck. In my project, I need to calculate the RPM of a rotating motor using a hall sensor. I have 4 pulses per rotation, usually, the RPM is between 200 - 4000 per minute.
As you will see in my code i am using interrupt, which needs delay to calculate the RPM. As i experienced, at low rev i need at least 500 milli sec to have enough pulses for decent calculation. But this half sec gives me a lot of delay. I would like to use other functions, like switches to be able to turn on immediately.
I found other topics regarding similar issues, but as i mentioned earlier, i am not an expert, and many of the answers were kind of Chinese for me :slight_smile:
Eventually i would like to build an engine ignition timing device, where I can control ignition advance, in which case any high delay is clearly unacceptable.
Could you guys please help me in which direction I should go? I am really happy to learn new stuffs, just on my own I don't know what do do know.

here is my code so far:

float revolutions = 0;
int rpm = 0;
long startTime = 0;
long elapsedTime;
const int hall = 2;

void setup()
{
pinMode(hall, INPUT_PULLUP);
Serial.begin(9600);
}

void loop() {

revolutions = 0;
rpm = 0;

startTime=millis();
attachInterrupt(digitalPinToInterrupt(2),interruptFunction,RISING);
delay(500);
detachInterrupt(2);

elapsedTime = millis() - startTime;

if(revolutions > 0){

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

String outMsg = String("RPM :") + rpm;
Serial.println(outMsg);

}

void interruptFunction() {
revolutions++;
}

Please follow the advice given in the link below when posting code , use code tags and post the code here to make it easier to read and copy for examination

float revolutions = 0;
int rpm = 0; 
long  startTime = 0;
long  elapsedTime;
const int hall = 2;


void setup() 
{
    pinMode(hall, INPUT_PULLUP);           
    Serial.begin(9600);
    }
 
void loop() {

revolutions = 0; 
rpm = 0;
  
  startTime=millis();         
  attachInterrupt(digitalPinToInterrupt(2),interruptFunction,RISING);
  delay(500);
  detachInterrupt(2);                


elapsedTime = millis() - startTime;     

if(revolutions > 0){
  
  rpm=((max(1, revolutions) * 60000) / 4) / elapsedTime;        
}

String outMsg = String("RPM :") + rpm;
Serial.println(outMsg);

}

void interruptFunction() {  
  revolutions++;
}

Why ?

why am i using interrupt, or why the delay?

How about this variant:

const uint8_t hallPin = 2;
volatile uint32_t hallPulses;
uint32_t startTime;
uint32_t lastPulses;
int rpm;

void setup() {
  Serial.begin(115200);
  pinMode(hallPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(hallPin), interruptFunction, RISING);
}

void loop() {
  uint32_t topLoop = millis();
  uint32_t elapsedTime = topLoop - startTime;
  if (elapsedTime >= 500) {
    noInterrupts();
    uint32_t hallPulsesCopy = hallPulses;
    interrupts();
    rpm = (((hallPulsesCopy - lastPulses) * 60000) / 4) / elapsedTime;
    lastPulses = hallPulsesCopy;
    startTime = topLoop;
    Serial.print(F("RPM :"));
    Serial.println(rpm);
  }
}

void interruptFunction() {
  hallPulses++;
}

It’s untested, but compiles.

Not the usage of volatile and how to access multibyte data manipulated by an ISR.

Thanks for your answer dude! I have just tried it, but it gives mirrored question marks in serial monitor. Not even" RPM:"

I changed your ridiculous slow 9600 baud rate to 115200, adjust the monitor. :grinning:

Why the delay()

Oh sorry my bad :slight_smile: yes it seems to be working, could you quickly explain me what is happening and what is this uint32_t for?
I can see that the 500 ms sampleing is there

uint32_t is a standard unsigned 32 bit integer on any platform.
int or long can mean different sizes depending on the platform.

without the delay I did not get anything. around 200ms was the first sort of usable rpm data but only at higher rev.

Basically you just count all hall pulses, anytime.
You just sample the count each 500 ms and compute the rpm.

For the count you use an unsigned int of 32 bit, so differences work over the wrapping point,
just like millis.

that makes perfect sense. so basically i can drop that 500ms down until i still got enough pulses to make the calculation. if im correct, at lower rev i need longer times

You can make the sample period dynamic, no problem, maybe the next reading would be bad.

uint16_t samplePeriod = 500; // 60 sec max

  if (elapsedTime >= samplePeriod) {

yes, i think i will give it a go and test how reliable the reading will be. Thank you very much for your help! It gave me a lot new to learn an think about!

An example was simpler that to pinpoint out all the bad decisions in your starting sketch. :wink:

thats the thing! without the knowledge of the functions im just guessing. but thats the way of learning anyway

If you are using an interrupt just note the change in micros() down in the ISR. The frequency = 1 / period.

Something like this:

volatile unsigned long prev_time ;
volatile unsigned long period ;

void isr()
{
  unsigned long new_time = micros() ;
  period = new_time - prev_time ;
  prev_time = new_time ;
}

#define MICROSECS_PER_MINUTE 60e6

void loop()
{
  noInterrupts() ;  // sample period in a critical section:
  unsigned long the_period = period ;
  interrupts () ; 

  float rpm = MICROSECS_PER_MINUTE / the_period / 4 ;
  ....
}

At higher speeds you'll need to apply some averaging of pwm due to quantization of the period.

Hm, i have been thinking since last night, may be i get the whole idea wrong. Its ok, that i have no delay in the sketch anymore, but any time i need to calculate rpm it obviously takes time. What if instead of constantly counting the pulses, i count time, and measure time differences between 2 pulses?