⛔ Anyway to make this RPM counter/meter more accurate when running faster?

Hi I found this sketch for my project making an electric gear motor RPM tachometer, rpm in between 0-150 only. My issue is for this sketch to be pretty accurate I have to take readings at 1000ms intervals but I need my reading to be accurate at maybe 100ms?

Is there any way to make this sketch more accurate at 100ms? Maybe somehow save last 5 readings and take an average of the last 5?

volatile int interruptCounter; //counter use to detect hall sensor in fan
int RPM;                      //variable used to store computed value of fan RPM
unsigned long previousmills;

//NODEMCU ESP8266
#define tachInputPIN  D5
#define pwmOutputPin D6
#define pwmDuty 1024
#define calculationPeriod 1000 //Number of milliseconds over which to interrupts

void ICACHE_RAM_ATTR handleInterrupt() { //This is the function called by the interrupt
  interruptCounter++;
}

void setup()
{
  Serial.begin(115200);

  previousmills = 0;
  interruptCounter = 0;
  RPM = 0;

  //NODEMCU esp8266

  pinMode(pwmOutputPin, OUTPUT);
  pinMode(tachInputPIN, INPUT_PULLUP);
  analogWriteFreq(25000);
  analogWrite(pwmOutputPin, pwmDuty);
  attachInterrupt(digitalPinToInterrupt(tachInputPIN), handleInterrupt, FALLING);
}

void loop()
{
  // NODEMCU esp8266
  //  analogWrite(pwmOutputPin, pwmDuty);

  if ((millis() - previousmills) > calculationPeriod) { // Process counters once every second
    previousmills = millis();
    int count = interruptCounter;
    interruptCounter = 0;
    computeFanSpeed(count);
    displayFanSpeed();
  }
  yield();
}

void computeFanSpeed(int count) {
  //interruptCounter counts 1 pulses per revolution of the fan over a one second period
  RPM =  count / 1  ;
}

void displayFanSpeed() {
  Serial.print(RPM);        //Prints the computed fan speed to the serial monitor
  Serial.print(" RPM\r\n");      //Prints " RPM" and a new line to the serial monitor
}

You don't say how it is inaccurate, but...

This line does absolutely nothing:

RPM =  count / 1

So, if you change calculationPeriod to 500, your function is still calculating the RPM over 1,000ms. If you did this:

RPM =  count / calculationPeriod

then you would be doing something more dynamic.

For low RPM you could measure the time between pulses rather than the pulses per time interval.

int RPM;                      //variable used to store computed value of fan RPM
unsigned long previousmills;


//NODEMCU ESP8266
#define tachInputPIN  D5
#define pwmOutputPin D6
#define pwmDuty 1024
#define calculationPeriod 100 //Number of milliseconds over which to interrupts


volatile unsigned long PulseInterval = 0;


void ICACHE_RAM_ATTR handleInterrupt()   //This is the function called by the interrupt
{
  static unsigned long previousMicros = 0;
  unsigned long currentMicros = micros();
  PulseInterval = currentMicros - previousMicros;
  previousMicros = currentMicros;
}


void setup()
{
  Serial.begin(115200);


  previousmills = 0;


  //NODEMCU esp8266


  pinMode(pwmOutputPin, OUTPUT);
  pinMode(tachInputPIN, INPUT_PULLUP);
  analogWriteFreq(25000);
  analogWrite(pwmOutputPin, pwmDuty);
  
  attachInterrupt(digitalPinToInterrupt(tachInputPIN), handleInterrupt, FALLING);
}


void loop()
{
  // NODEMCU esp8266
  //  analogWrite(pwmOutputPin, pwmDuty);


  if ((millis() - previousmills) > calculationPeriod)   // Process counters once every second
  {
    previousmills += calculationPeriod;
    noInterrupts();
    
    unsigned long interval = PulseInterval;
    interrupts();
    if (interval == 0)
    {
      RPM = 0;
    }
    else
    {
      RPM = 60000000UL / interval;
    }


    Serial.print(RPM);        //Prints the computed fan speed to the serial monitor
    Serial.println(" RPM");      //Prints " RPM" and a new line to the serial monitor
  }
  yield();
}

These two lines need to be protected against an interrupt happening while they are being read. Because an int comprises 2 bytes one of the bytes might be changed while the other is being read.

    int count = interruptCounter;
    interruptCounter = 0;

like this

 noInterrupts();
    int count = interruptCounter;
    interruptCounter = 0;
 interrupts();

...R

SteveMann:
You don't say how it is inaccurate, but...

This line does absolutely nothing:

RPM =  count / 1

So, if you change calculationPeriod to 500, your function is still calculating the RPM over 1,000ms. If you did this:

RPM =  count / calculationPeriod

then you would be doing something more dynamic.

That line was modified, I was using

RPM =  count / 1 * 120

So if I change 500ms to something else I also edit the line above

johnwasser:
For low RPM you could measure the time between pulses rather than the pulses per time interval.

int RPM;                      //variable used to store computed value of fan RPM

unsigned long previousmills;

//NODEMCU ESP8266
#define tachInputPIN  D5
#define pwmOutputPin D6
#define pwmDuty 1024
#define calculationPeriod 100 //Number of milliseconds over which to interrupts

volatile unsigned long PulseInterval = 0;

void ICACHE_RAM_ATTR handleInterrupt()  //This is the function called by the interrupt
{
  static unsigned long previousMicros = 0;
  unsigned long currentMicros = micros();
  PulseInterval = currentMicros - previousMicros;
  previousMicros = currentMicros;
}

void setup()
{
  Serial.begin(115200);

previousmills = 0;

//NODEMCU esp8266

pinMode(pwmOutputPin, OUTPUT);
  pinMode(tachInputPIN, INPUT_PULLUP);
  analogWriteFreq(25000);
  analogWrite(pwmOutputPin, pwmDuty);
 
  attachInterrupt(digitalPinToInterrupt(tachInputPIN), handleInterrupt, FALLING);
}

void loop()
{
  // NODEMCU esp8266
  //  analogWrite(pwmOutputPin, pwmDuty);

if ((millis() - previousmills) > calculationPeriod)  // Process counters once every second
  {
    previousmills += calculationPeriod;
    noInterrupts();
   
    unsigned long interval = PulseInterval;
    interrupts();
    if (interval == 0)
    {
      RPM = 0;
    }
    else
    {
      RPM = 60000000UL / interval;
    }

Serial.print(RPM);        //Prints the computed fan speed to the serial monitor
    Serial.println(" RPM");      //Prints " RPM" and a new line to the serial monitor
  }
  yield();
}

I'm actually measuring something where the duty cycle and frequency changes, not sure if measuring between pulses would help??

PS just tested this, it works a lot better thank :slight_smile: