Blynk Non Blocking for FAN RPM

Hi, hoping for some guidance on a non-blocking method for calculating fan RPM. I have the below running using millis using Blynk Timers, how-ever this essentially is an alternative method to delay()? which I’m guessing can cause the same issue with blynk server connections?

Code compiles/runs and I’m seeing correct FAN RPMs being reported in serial monitor every 5 seconds.

The issue is I need exactly 1 second delay between the below two lines for it to calculate th number of reotations. Anyone have an example of how to do this which is more friendly to blynk, or is Millis fine to use in this case?

attachInterrupt(digitalPinToInterrupt(fanrpmsensor), countup, RISING);
delay here for 1 second is needed.
detachInterrupt(digitalPinToInterrupt(fanrpmsensor));

thanks heaps!

#define BLYNK_PRINT Serial          
#define BLYNK_TEMPLATE_ID "REMOVED"
#define BLYNK_DEVICE_NAME "REMOVED"
char auth[] = "REMOVED"; 
char ssid[] = "REMOVED";
char pass[] = "REMOVED";

#include <Wire.h>                     
#include <SPI.h>                      
#include <BlynkSimpleEsp32.h>         
BlynkTimer timer1;

//FAN RPM Code
const int fanrpmsensor = 14;  
int InterruptCounter, rpm;
int period = 1000;
unsigned long time_now = 0;
int fanrpm = 0;

void setup()
{
  Serial.begin(9600);
  pinMode(fanrpmsensor, INPUT);
  timer1.setInterval(5000L, fanrpmmeasure);
  Blynk.begin(auth, ssid, pass);
}

void loop()
{
  Blynk.run();
  timer1.run();
  time_now = millis();
}

void fanrpmmeasure() {
  InterruptCounter = 0;
  attachInterrupt(digitalPinToInterrupt(fanrpmsensor), countup, RISING);
  while (millis() < time_now + period) {
  //wait approx. [period] ms
  }
  detachInterrupt(digitalPinToInterrupt(fanrpmsensor));
  rpm = (InterruptCounter / 2) * 60;
  display_rpm();
}
void countup() {
  InterruptCounter++;
}

void display_rpm() {
  Serial.print(" RPM: ");
  Serial.println(rpm);

  if (rpm >= 6000) {
    Serial.println("I am more than 6000 RPM");
  }
}

Delay(x) effectively causes all code execution to halt for x milliseconds. This creates problems with Blynk, as the Blynk library needs to be talking to the Blynk server all the time.
Delay() would also cause you problems with your code, as it would stop the interrupts and the counter increments as well.

BlynkTimer and SimpleTimer are non-blocking, so don’t cause these problems. In effect, they both use a millis comparison process. Every time timer.run (or timer1.run in your case) is executed, it causes the Blynk/SimpleTimer library to check if any of the scheduled timer tasks need to be executed yet.

In your case, you’ve constructed a combination of timer useage and millis comparison, but you’ve done it incorrectly as far as I can see.

There are two ways to tackle this in my opinion, one uses the timer/millis combo, the other uses a timeout timer in place of the millis comparison.

Option 1 - timer plus millis…

Timer calls a function (every 5 seconds in your case)
Function captures the current millis reading, zeros the interrupt counter and enables interrupts
Interrupts occur and are counted
In your void loop you do a millis comparison - has 1 second elapsed since the millis value that was captured above?
If yes then you call a second function which disables interrupts, calculates the fan speed, prints the results, sends the results to Blynk etc.

Option 2 - Timer plus Timeout timer

Basically the same as above, except that instead of doing the millis comparison, a timeout timer is used instead…

Timer calls a function (every 5 seconds in your case)
Function initiates a timeout timer of 1000ms, zeros the interrupt counter and enables interrupts
The timeout timer will call a ‘calculate_results’ function when it times-out
Interrupts occur and are counted
When the timeout timer times-out then the ‘calculate results’ function is called which disables interrupts, calculates the fan speed, prints the results, sends the results to Blynk etc.

Option 2 would be my preferred method, as it is cleaner because only one set of millis comparisons is being done (inside the BlynkTimer library) rather than two.

More info on timeout timers towards the end of this tutorial…

Pete.

thanks heaps for the detailed clarification. This definitely exceeds my skill level in code haha :frowning:

Do you have any examples codes I can bend and manipulate around what I’m trying to do?

So the original code wasn’t yours then?

Pete.

nope, definitely not.

source: Read fan speed with Arduino

It’s always useful if you flag that up in your initial post.

Pete.

Looks like I might have got a simply solution, trusty old IF statements. Non-blocking and provides accurate results;

Timer fanrpmmeasure runs every 1 second, can reduce frequency and adjust below to calculate accurately.

void fanrpmmeasure() {

  if (fanrpmmeasurestate == 0) {
      InterruptCounter = 0;
      attachInterrupt(digitalPinToInterrupt(FANRPMSENSOR), countup, RISING);
      fanrpmmeasurestate = 1;
    } else {
      detachInterrupt(digitalPinToInterrupt(FANRPMSENSOR));
      rpm = (InterruptCounter / 2) * 60;
      fanrpmmeasurestate = 0;
      if (rpm >= fanrpmmin) {
        fanrpmhealthy = true;
      } else {
        fanrpmhealthy = false;
      }
    }
  }

cheers,

Did some further testing and results are not as accurate as I thought, some times its <>30% out, it appears Blynk.virtualWrite is causing a blocking event for other timers.

So went digging some more and found a pretty good solution (I think). As i’m using an ESP32 it has two (2) addressable CORES.

By default all arduino code runs in CORE1. So created a task that runs on CORE0 and results are very very nice, been testing for the last few days and I think I’m happy, the delay doesnt pause the rest of the code!

void Task1code( void * pvParameters ) { 
  for (;;) {
    InterruptCounter = 0;
    attachInterrupt(digitalPinToInterrupt(FANRPMSENSOR), countup, RISING);
    delay(1000);
    detachInterrupt(digitalPinToInterrupt(FANRPMSENSOR));
    fanrpm = (InterruptCounter / 2) * 60;
  }
}

Using timer on CORE1 but being blocked randomly by “Blynk.virtualWrite” (bad internet)

Using FAN RPM code in CORE0 with simple delay.

I’m thinking maybe to replace the delay with a non blocking timer, so I can using other push button code which relys on fast processing for press/release and bounce handling.

cheers