monitoring fan rotation while keeping main loop close to real time

Hi Everybody,

I’m building up an RF power amplifier for a ham radio repeater. Instead of using conventional logic to monitor the “health” of the system (fan speed and heatsink temperature), I though I would press an Arduino into service. I am an Arduino newbie and have only attempted simple sketches so far. I more of a hardware person than software!

There is a 5V trigger in from the radio transmitter controlling an NPN transistor, open collector output to input pin 4 (pttin). Output on pin 3 to make the amplifier operate is an NPN/PNP pair to switch 12V (pttout) to the amplifier.

The overtemp is a simple digital input from a thermal switch mounted on the heatsink. When the temp rises past the switch threshold, it sets digitalRead(temp) LOW and this will stop the amplifier from operating. This the basic code, I hope to add other enhancements that will latch outputs connected to LEDs to indicate that over temp or fan fail have occurred.

void loop() {
  if (digitalRead(pttin) == LOW && digitalRead(temp) == HIGH)
  digitalWrite(pttout, HIGH);   // Pin 3 on 
  digitalWrite(ind, HIGH);   // PTT LED on
  digitalWrite(pttout, LOW);    // Pin 3 off 
  digitalWrite(ind, LOW);    // PTT LED off

My question is about how to monitor fan rotation while still maintaining a near “real time” operation of the main loop.

Most of the example sketches I have found that read the hall pulse output from fans use interrupts and need to have a delay to calculate the fan speed over a period of time. While this is happening, the main loop is not doing its “thing”.

What is the best programming approach in this situation… I don’t really need to know the fans RPM, maybe just a fan fail value set HIGH if the RPM falls below a set value, I could then add a third IF condition. I need to have a minimal impact of reading fan speed on the loop.

I am open to suggestions on how to code this.


This playground article reads the RPM of a fan without using delay() or anything else that stop the processor for a time longer than necessary from doing the relevant stuff:

If that’s not acceptable for you, please specify what problem you have with that code (or better: with that style of code).

Define "near real time"

Thank you for the replies... I will experiment with the code on the linked page. I had not come across that one before.

I need the pttout pin to "activate" (if the other conditions allow it) within 50ms of pttin. Some of the fan RPM code I found would delay for one second to read the pulses and calculate RPM, this would not work in my application.

The other option I thought of was to use an external hardware AND gate, keeping the path from pttin to pttout away from the Arduino and then just write code to monitor the fan and temp. If these then fail, they assert an output which prevents the output from the AND gate from going high. As I said, I am a hardware person. :slight_smile:

I've used a hardware solution for fan monitoring before in other projects.

... but I thought that I should exercise my software coding skills too.

Another way to do this would be to setup an interrupt for one of the timers and let it do the monitoring of pttout/ind. That way you can then use loop() to monitor fans & temperature at leisure as long as none of the code blocks interrupts.

Another new guy mislead by examples with delay() Delay is evil, period.
If you want real time, learn to write code without delay(). delayMicroseconds() is OK.
The basic approach uses micros() or millis() to note the "start" time. Later you note the time again and simply subtract.

if (start detected)
startTime = micros();
if (end detected)
endTime = micros() - startTime;

start & end might be two successive Hall pulses or something similar.


[quote author=joe mcd link=topic=166458.msg1241853#msg1241853 date=1368701229]
Another new guy mislead by examples with delay() Delay is evil, period.[/quote]
I have re-read all the posts here and can see no references/example that use delay.
pylon linked to an example of how not to use delay() and the timer idea I proposed said nothing about delay() so I'm not sure who is being mislead?

If you want real time, learn to write code without delay(). delayMicroseconds() is OK.

Why the hell should delayMicroseconds() be OK while delay() is evil? The only context where this may be true is in an interrupt handler but I don't see where this is related to your "real time" (a horrible term in computer context anyway).

lunokhod, below is an example I've built from the ReadingRPM example:

Rather than counting the number of pulses in a fixed window of time, it measures fan speed period and calculates the frequency.

Only two successive pulses are needed to calculate the frequency, so changes in fan speeds can be detected soon after they occur.

Irregular/small intervals between checking measurements do not impact accuracy of the measurement.

unsigned volatile long _last_sample = 0; // Time of previous interrupt in micros
unsigned volatile long _period = 0; // Length of prevous period in micros (time between previous two interrupts)
const long max_period = 500000; // Maximum time in micros before we assume the fan has stopped.

const int pin = 2;
const int pin_interrupt = 0; // interrupt line for pin 2

void setup() 
  Serial.begin( 115200 );
  pinMode( pin, INPUT );
  digitalWrite( pin, HIGH ); // Enable internal 20K pullup resistor
  attachInterrupt( pin_interrupt, rpm_fan, FALLING );

void rpm_fan()
  unsigned long now = micros();
  _period = now - _last_sample; // Period between successive interrupts.
  _last_sample = now; // Keep for next interrupt.

void loop() 
  // Copy interrupt variables to local varibles.
  noInterrupts(); // Suspend all interrupts.
  // Minimise time spent until interrupts are enabled.
  unsigned long period = _period;
  unsigned long last_sample = _last_sample;
  interrupts(); // Enable all interrupts.
  // Calculate frequency from last period.
  double frequency, rpm;
  unsigned long now = micros();
  if( now - last_sample > period )
    // The last fan signal was longer than the previous period.
    // Either fan is slowing or has stopped.
    period = now - last_sample;
  if( period > max_period )
    // Fan stopped - do something?
    frequency = rpm = 0; 
    // High precision frequency can be obtained by measuring a single period.
    frequency = 1000000.0 / period; // Frequency is 1/period, period is in micros.
    rpm = 60.0 * 1000000.0 / period;
  Serial.print( "RPM = " );
  Serial.print( rpm, 1 );
  Serial.print( "  Hz= " );
  Serial.println( frequency, 1 );

  delay( 50 ); // Slow down serial output.