Electrical – 4-pin PC Fan PWM control and speed monitoring

esp8266fanpwm

I want to control and monitor the speed of a 4-pin PC fan using an esp8266.

When there is no PWM control signal sent to the fan from the esp this code reports the fan speed correctly. However, when the esp PWM control signal is connected to the fan things go wrong with the speed reporting, but the fan speed actually responds as expected to the PWM signal.

The setup:

  • The fan is driven from a 12V supply and the NodeMCU v2 esp8266 is
    powered by USB from a PC. The 12V supply and the esp8266 have a
    common ground through the esp’s GND pin; and
  • The tach signal from the fan is pulled up the 3.3V pin of the esp and
    is connected to pin D3 of the esp.

When the system is connected in this state (no PWM signal) the fan speed is at maximum and its speed is correctly reported in the serial monitor at approximately 1410 RPM. I have used the debugging tools in VisualMicro to watch the vaue of the interruptCounter variable in the computFanSpeed() function during operation and its value gets to 49 before it is reset to zero after exiting loop().

The PWM signal originates from esp pin D1 and is measured to be 2.0V using a multimeter. When this PWM signal is connected to the PWM input of the fan it responds as expected, however the speed reported in the serial monitor climbs to a non-sensible number (123300 RPM). The value of interruptCounter variable in the computFanSpeed() function gets to 4180 before it is reset to zero after exiting loop().

I don’t understand where things are going wrong – specifically why is the handleIinterrupt() function being triggered when the fan is operating under PWM control by the esp?

Another note: For some reason if any value other than 0 is provided to the pin argument in the attachInterrupt('pin', 'ISR', 'mode') function the fan speed recorded in the serial monitor is 0 RPM. I have tried a several combinations of different pins and values for 'pin' and have not been able to have the speed reported in the serial monitor – it always shows 0 RPM.

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  4             
#define pwmOutputPin 5              
#define sensorInterrupt 0
#define pwmDuty 630

#define calculationPeriod 1000 //Number of milliseconds over which to interrupts

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

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

    //NODEMCU esp8266
    analogWriteFreq(8000);      
    pinMode(pwmOutputPin, OUTPUT);

    attachInterrupt(sensorInterrupt, handleInterrupt, RISING);
    pinMode(tachInputPIN, INPUT);
}

void loop()
{

    // NODEMCU esp8266
    analogWrite(pwmOutputPin, pwmDuty);

   if((millis() - previousmills) > calculationPeriod) {// Process counters once every second
    detachInterrupt(sensorInterrupt);
    computeFanSpeed();
    displayFanSpeed();
    previousmills = millis();
    interruptCounter = 0;
    attachInterrupt(sensorInterrupt, handleInterrupt, RISING);
   }

}

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

void computeFanSpeed() {
    //interruptCounter counts 2 pulses per revolution of the fan over a one second period
    RPM = (0.5) * (interruptCounter / (calculationPeriod/1000)) * 60;
}

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

Best Answer

The esp seems to cache its interrupts or something weird (related to disabling and enabling interrupts)

try this:

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 2 pulses per revolution of the fan over a one second period
    RPM =  count /2 * 60 ;
}

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

`