Reading Pulse Signal From 10 PC Fans

Hi All,

I'm currently reading the hall effect sensor from 10 PC fans (Producing a roughly 35Hz signal) by using the external interrupts on two Arduino Mega's. I'd like to reduce this to one Arduino but I'm having difficulty in finding a workable solution.

My first thought was to use an alternative method such as the (blocking) PulseIn function but this causes too much of a delay on five inputs.

I've been looking at an external IC that might be able to take over the work - something like the PCF8575 or MCP23017 but reading the data sheet and Nick Gammon's page it appears to store the port values in a register on interrupt but I'm not sure if this is going to be usable with 10 ports being hammered at 35Hz?

Any advice would be most welcome.

Thanks.

(My current method is as described in the playground article: Arduino Playground - ReadingRPM)
(Nick's notes on the MCP23017 Gammon Forum : Electronics : Microprocessors : Buttons, I2C, interrupts and port-expanders)

PCF8575 data sheet: http://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf
MCP23017 data sheet: http://www.tij.co.jp/jp/lit/ds/symlink/pcf8575.pdf

1 Like

10 ports being hammered at 35Hz?

ROLF!

  1. At that speed the 16Meg CPY most be spending 90+ percent of its time doing nothing

2, Why do you think that you need to monitor all of them at the same time? to me you are not making sense!

  1. Why a such a S l o w S l o w rate are you using interrupts at all?

  2. Pin change or just poll the inputs!

Mark

Perhaps you could adapt a priority interrupt controller to the job.

Hi Mark,

So I have 10 fans providing ventilation across 30+ vivariums. These are not easily monitored visually so all have their hall effect sensor output wired up to a central point. I have an Arduino Mega controlling lighting and water misting via a touch screen so thought it would be useful to also log\display the fan RPM's.

The data is logged to an SD card and also Blynk - in addition to the (on demand) LCD with dynamic images for each fans status:

This allows me to check the fan status from the screen or via Blynk when travelling.
Acuraccy is not a priority, responsiveness to serial commands from the LCD are which is why I thought the below external interrupt method (from zitrons playground example) would be ideal.

Do you have any examples of polling that wouldn't introduce any delay\blocking? Most examples I've found seem to use external interrupts for fan RPM reading.

unsigned long lastmillis = 0;

volatile int rpmcounter1 = 0;
volatile int rpmcounter2 = 0;
volatile int rpmcounter3 = 0;
volatile int rpmcounter4 = 0;
volatile int rpmcounter5 = 0;
volatile int rpmcounter6 = 0;

int rpm1 = 0;
int rpm2 = 0;
int rpm3 = 0;
int rpm4 = 0;
int rpm5 = 0;
int rpm6 = 0;

void hallSensor1() {
  rpmcounter1++;
}

void hallSensor2() {
  rpmcounter2++;
}

void hallSensor3() {
  rpmcounter3++;
}

void hallSensor4() {
  rpmcounter4++;
}

void hallSensor5() {
  rpmcounter5++;
}

void hallSensor6() {
  rpmcounter6++;
}

void setup() {

  Serial.begin(115200);

  attachInterrupt(0, hallSensor1, FALLING);//interrupt 0 is on pin 2
  attachInterrupt(1, hallSensor2, FALLING);//interrupt 1 is on pin 3
  attachInterrupt(2, hallSensor3, FALLING);//interrupt 2 is on pin 21
  attachInterrupt(3, hallSensor4, FALLING);//interrupt 3 is on pin 20
  attachInterrupt(4, hallSensor5, FALLING);//interrupt 4 is on pin 19
  attachInterrupt(5, hallSensor6, FALLING);//interrupt 5 is on pin 18
}

void loop() {

  if (millis() - lastmillis == 1000) {
    noInterrupts();
    rpm1 = rpmcounter1 * 30;
    rpm2 = rpmcounter2 * 30;
    rpm3 = rpmcounter3 * 30;
    rpm4 = rpmcounter4 * 30;
    rpm5 = rpmcounter5 * 30;
    rpm6 = rpmcounter6 * 30;
    interrupts();

    rpmcounter1 = rpmcounter2 = rpmcounter3 =  rpmcounter4 =  rpmcounter5 =  rpmcounter6 = 0;

    lastmillis = millis();

    Serial.print("fan1: ");
    Serial.print(rpm1);
    Serial.print("\t");
    Serial.print("fan2: ");
    Serial.print(rpm2);
    Serial.print("\t");
    Serial.print("fan3: ");
    Serial.print(rpm3);
    Serial.print("\t");
    Serial.print("fan4: ");
    Serial.print(rpm4);
    Serial.print("\t");
    Serial.print("fan5: ");
    Serial.print(rpm5);
    Serial.print("\t");
    Serial.print("fan6: ");
    Serial.println(rpm6);
  }

}

Here's a version of the above code which polls...

int rpmPins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; //Choose any pins you like, any number of pins
#define NUM_RPM (sizeof(rpmPins)/sizeof(rpmPins[0])) //the number of pins is equal to the size of the pins array divided by the size of the first element
int rpmcounter[NUM_RPM];

void checkRpmPins() {
  //inspect each pin to find out if it's just gone high, then add 1 to the counter for that pin
  static bool lastPinState[NUM_RPM];
  
  for(byte i = 0; i<NUM_RPM; i++) {
    
    if(digitalRead(rpmPins[i]) != lastPinState[i]) {
      //pin has changed from what we last saw - flip the state of our record of the pin
      lastPinState[i] = !lastPinState[i];
      
      if(lastPinState[i]) {
        //pin just went HIGH
        rpmcounter[i]++;
      }
      
    }
  }
}

void setup() {

  Serial.begin(115200);
  Serial.println("RPM counter version 0.1 started...");

}

void loop() {
  static unsigned long lastmillis = 0;
  int rpm[NUM_RPM];

  checkRpmPins(); //this is the function that polls all input pins to count pulses. Call it as often as possible. Try not to skip a millisecond.

  if (millis() - lastmillis >= 1000) {

    lastmillis = millis();

    for(byte i=0; i<NUM_RPM; i++) {
      rpm[i] = rpmcounter[i]*30;
      rpmcounter[i] = 0;
    }

    for(byte i=0; i<NUM_RPM; i++) {
      Serial.print("fan");
      Serial.print(i);
      Serial.print(": ");
      Serial.print(rpm[i]);
      Serial.print("\t");
    }

    Serial.println();
  }

}

This code is good as a simple example. Making it more complex like adding the LCD screen may mean that the loop takes too long and you miss a pulse. It would be good to know how long you expect the shortest pulse to be (in milliseconds.) If you cannot call the polling function at least as often as once per pulse then you will miss pulses. Then you have to use interrupts to be able to interrupt the LCD code for a microsecond and increment a counter.

The Arduino MEGA has pin-change interrupts on all pins. They are a little more complex to use than the standard interrupts. Other Arduinos such as the Due and Teensy can use standard interrupts on all pins.