Best PWM fan library?

So I'm in the early stages of developing a real-world project. I am very much an Arduino beginner.

One part of my project is a PWM fan. I did testing with a PWM fan I stole from a PC chassis. I did some research and found Giorgio Aresu's FanController.h library, which I tried testing with and had perfect success both controlling the fan and reading RPMs. So far so good.

I eventually got my production fan in. But when I try and incorporate that with the same code, running the fan at 100% gives me RPMs of about 2800 but when I set the fan to 50% I'm getting RPMs of over 9000. This isn't right (I do clearly hear the fan slow down, so the fan control is working, it's the RPMs reporting that is broken).

Perhaps there's a bug in the library but that's outside the scope of what I can troubleshoot with my beginner skills. Does anyone know of bugs in the library that can be fixed, or is there a newer/better PWM library I should be using? (Giorgio's was last updated in 2016).

(I realize I'm not showing code but that's kind of irrelevant as with this library you simply read RPMs with fan.getSpeed() and set the fan speed with fan.setDutyCycle(). The code you'd actually care about is what's behind those, which you can find on his Github page linked above)

I realize I'm not showing code but that's kind of irrelevant as with this library you simply read RPMs with fan.getSpeed() and set the fan speed with fan.setDutyCycle().

If you say so, but as we cannot see the parameters used when calling the functions then who knows what you have done

How did you determine the values to be used when calling the functions ?

UKHeliBob:
If you say so, but as we cannot see the parameters used when calling the functions then who knows what you have done

How did you determine the values to be used when calling the functions ?

Basically:

  unsigned int RPMs = fan.getSpeed();
    lcd.print(RPMs);

and at points:

fan.setDutyCycle(int value)

And to re-iterate: the same code works perfect with one fan, and not the other, and setting the fan % is fine on both fans... it's the reporting of the RPMs on the new fan which is the only issue.

The problem fan is a Noctua NF-A6x25. Just like the working fan, it's a 12V fan and the 12V power is supplied from a separate 12VDC power supply. Only the control and RPM-feedback pins are wired into the Arduino.

What value are you using for setDutyCycle() and how did you determine the value to be used ?

A typical PC 12 VDC fan will have a tachometer output of 2 pulses per revolution. The pulses are the result of a hall effect sensor(s). They normally rely on using an output which requires an external pullup to the fan. So what does your new fan have? Pulses per rev?

Less seeing what your fan's tach out signal looks like and knowing the pulses per rev it's hard to say where or why it has problems. Writing the code to PWM a fan is not all that difficult nor is reading the speed difficult. You may want to just try that and see what you get. The library you are using looks like the following:

/*
  FanController.h - Library to control up to six fans.
  Created by Giorgio Aresu, November 13, 2016.
  Released into the public domain.
*/
#ifndef FanController_h
#define FanController_h

#include "Arduino.h"

class FanController
{
	public:
		FanController(byte sensorPin, unsigned int sensorThreshold, byte pwmPin = 0);
		void begin();
		unsigned int getSpeed();
		void setDutyCycle(byte dutyCycle);
		byte getDutyCycle();
	private:
		static FanController *_instances[6];
		byte _sensorPin;
		byte _sensorInterruptPin;
		unsigned int _sensorThreshold;
		byte _pwmPin;
		byte _pwmDutyCycle;
		byte _instance;
		unsigned int _lastReading;
		volatile unsigned int _halfRevs;
		unsigned long _lastMillis;
		void _trigger();
		void _attachInterrupt();
		static void _triggerCaller(byte instance);
		static void _triggerExt0();
		static void _triggerExt1();
		static void _triggerExt2();
		static void _triggerExt3();
		static void _triggerExt4();
		static void _triggerExt5();
};

#endif

I doubt there is any "best library" but there are dozens of basic code samples out there to measure fan speed and use PWM to control fan speed using a variety of inputs.

Ron

UKHeliBob:
What value are you using for setDutyCycle() and how did you determine the value to be used ?

Per the library, you simply pass a value 0-100 which represents the % speed (duty cycle) that you want to set the fan to.

Ron_Blain:
A typical PC 12 VDC fan will have a tachometer output of 2 pulses per revolution. The pulses are the result of a hall effect sensor(s). They normally rely on using an output which requires an external pullup to the fan.

I did end up discovering the bit about the pull-up resistor on my own before seeing your post. I was hoping that was my "voila!" moment, but alas... it didn't help. I tried a 10K and a 5K as Noctua's PWM docs say anything above 2.7K should suffice. I tied it to the +5V on the Uno.

So what does your new fan have? Pulses per rev?

According to Noctua's PWM docs, it's the standard 2 pulses per revolution.

Writing the code to PWM a fan is not all that difficult nor is reading the speed difficult. You may want to just try that and see what you get.

I looked into that, but it gets into territory beyond my current skills and understanding (timers, interrupts, etc). The library made things very straightforward so I could focus on the other parts of my code. I was hoping to continue using it.

The library you are using looks like the following:

Unfortunatley the meat of it is in FanController.cpp which flies right over my head and I can't make sense of 95% of what's in there in order to DIY.

And setting the speed isn't the issue, just reading the RPMs. I can tell the fan speed is proportional to what I set it to, but something about reading the RPMs on this particular fan.

Everyone was understandably asking for code, but I didn't have it ready to present since it was mixed in with the rest of the code which is mostly unrelated to the fan so it'd make it confusing for people. So tonight I wrote a separate short snippet just controlling the fan and reading the RPMs which is enough to demonstrate the problem:

#include <FanController.h>

#define SENSOR_PIN 2

// threshold in milliseconds between readings.
// A smaller value will give more updated results,
// while a higher value will give more accurate and smooth readings
#define SENSOR_THRESHOLD 500

// PWM pin (4th on 4 pin fans)
#define PWM_PIN 9

FanController fan(SENSOR_PIN, SENSOR_THRESHOLD, PWM_PIN);

void setup() {
  Serial.begin(9600);
  fan.begin();

  fan.setDutyCycle(100); // percent, 0-100
}

void loop() {
  Serial.println(fan.getSpeed());
  delay(1000);
}

With the fan set to 100%, the reported RPMs are within spec (a bit over 3000). Set it to anything less than 100, and the RPMs report over 17000.

The original fan I was testing with was a Foxconn PV902512PSPF. With that one I got proportional RPMs to fan duty cycle, with a min RPMs of about 300 before the fan wouldn't spin at all. So the library was working perfectly.

Something weird about this Noctua fan.

I wonder if your pwm signal is somehow getting into the tacho. Do you have a regulated supply between 5 and 10 volts? Maybe you can power the motor at a lower voltage with the control wire disconnected and see if you get a lower and reasonable reading from the tacho.

trojanhawrs:
I wonder if your pwm signal is somehow getting into the tacho. Do you have a regulated supply between 5 and 10 volts? Maybe you can power the motor at a lower voltage with the control wire disconnected and see if you get a lower and reasonable reading from the tacho.

Well that just makes it more confusing. Disconnecting the PWM wire (blue), leaving the RPM sensing wire (green), I get ZERO RPMs reported when using the variable power supply. Change just the 12VDC power wires back to the computer power supply. I get RPMs reported (correct if fan is at 100%, incorrect if anything less). I can switch back and forth changing nothing and it's consistent. The fan is spinning regardless, it's just the RPMs being reported that is changing

I'm just left scratching my head now.

Hi,
Have you looked at the spec and data for that fan.
It is supposed to come with an adapter and it only has three selectable speeds.

Tom... :slight_smile:

TomGeorge:
Hi,
Have you looked at the spec and data for that fan.
It is supposed to come with an adapter and it only has three selectable speeds.

That wasn't my interpretation of that, and would be rather contrary to the whole concept of a PWM-controlled fan.

My reading of that is that it simply lowers the baseline "full" speed by virtue of a resistor. So if it's in a environment where PWM is controlled automatically and you can't control the range yourself, you can use the resistor-cable to limit the top RPMs for noise purposes. This is typical of several other Noctua fans I've purchased in the past.

I'm currently in correspondence w/ Giorgio, who was kind enough to reply to my email. Right now we're wondering if it's because the 12V power for the fan and the 5V for the Arduino aren't currently sharing a common ground as the Arduino is being powered off the USB while I test. So I'm going to try incorporating my voltage converter into the mix so that both are off the same 12VDC

sremick:
Right now we're wondering if it's because the 12V power for the fan and the 5V for the Arduino aren't currently sharing a common ground as the Arduino is being powered off the USB while I test.

Confirmed that isn't the issue.

OK but you do want a common ground. That is important. Your Arduino and your 12 V fan supply should have common ground. I would suggest a 10K pullup between the tach out of the fan and +5 volts but you have tried that. The fact that one fan works and another doesn't is strange and I wish we could see the difference in tach signals.

While I doubt it will matter here is an example of another PC Fan Library and the sample code. You can try it but since one fan works and another doesn't I doubt the problem is the library. You can try the linked code with the library in the article and see if it gets you any results.

Ron

Maybe try a stronger pullup just in case?

If you dont have a 2.7k, two 5K's in parallel will give you 2.5k which would provide the 2mA max current.

I also noted that there appears to be 2 versions of your fan.

Guessing the snippet Tom posted is for the 3 pin version.

trojanhawrs:
Maybe try a stronger pullup just in case?

If you dont have a 2.7k, two 5K's in parallel will give you 2.5k which would provide the 2mA max current.

Was worth a try. But nope, same results.

So at this point, I've tried: 10K, 5K, 2.5K

Not that it matters, but here's an example of switching from 100% to 50%, leaving everything on and connected, just pushing updated code:

3423
3420
3446
3420
3416
3453
3446
3450
3450
3446
3480
3480
3450
3446
0
0
10900
9990
9620
9480
9500
9519
9530
9540
9570
9500
9510
9540
9450
9530
9630

How about trying a potentiometer into your pwm pin? Put your 5K resistor at the pot input.

Hi,
What is the PWM frequency you are using to try and control the speed?

Tom.... :slight_smile:

Your description of the symptoms makes it clear that there is some sort of wiring issue, straight up. I am not sure exactly what it is (I can't see your diagram of how it is wired in the different configurations... am I missing something, or did you not post it)

That said, there may be software issues too.

I second the proposal to do the PWM yourself - setting duty cycle is a single line, and configuring it to work at the appropriate frequency is only a few lines.

Reading the tach is not trivial like driving the fan is, but still not particularly hard. Depending on how they do it, it is definitely possible for bad code in the rest of the sketch to hose it, though.

TomGeorge:
Hi,
What is the PWM frequency you are using to try and control the speed?

I dug into the library and it's using the standard 25KHz as well. That was something I checked early on. :slight_smile:

DrAzzy:
Your description of the symptoms makes it clear that there is some sort of wiring issue, straight up. I am not sure exactly what it is (I can't see your diagram of how it is wired in the different configurations... am I missing something, or did you not post it)

Wiring is straightforward:

Arduino Uno
Pin 9 wired to the PWM input signal (blue wire) on the fan's 4-pin connector
Pin 2 wired to the tach signal pin (green wire) on the fan's 4-pin connector, also tied to a pull-up resistor (currently 2.5Kohm but 5K and 10K have also been tried) to the +5VDC on the Arduino
Other two pins on the fan's connector wired to an external 12VDC power supply. The 12V and Arduino grounds are joined.

That said, there may be software issues too.

I second the proposal to do the PWM yourself - setting duty cycle is a single line, and configuring it to work at the appropriate frequency is only a few lines.

Reading the tach is not trivial like driving the fan is, but still not particularly hard. Depending on how they do it, it is definitely possible for bad code in the rest of the sketch to hose it, though.

The current test code I'm using was posted earlier. I'm not sure where there's room in my code for "bad code" that is "hosing" everything up.

Reading the tach is the key issue here, and the only thing not working properly. I looked a bit into what y'all are saying is trivial and easy... maybe to you, but to a beginner who has not yet become skilled in interrupts and timers, it's above my current skill level (keep in mind that this whole fan thing is but a small subset of what is a much larger project, the fan just being one small component. It's not practical for me to get sidetracked spending days/weeks re-inventing the wheel... I'm the chef who likes the DIY of cooking and is trying to get some flour to make a cake, but is being told to grow some wheat and grind it himself. :stuck_out_tongue: ).