The internet isn’t exactly short of fan speed controller projects – nor am I. I’ve probably got about four or five variants of these I’ve built over the years. Recently I’ve taken the time to round up the “best of” these into a single open-source friendly AVR based project. Historically it was based on PIC microcontrollers, using various expensive proprietary compilers – the main reason I’ve not released it to-date.
The initial reason I embarked on this is because back 10 or so years ago when I first built one, PC motherboards lacked intelligent fan speed control, and if they did have it, it was limited to the point of being practically useless. The situation is a lot better these days, but the level of control sometimes desired isn’t a given. I still have quite a few of these in use for non PC scenarios, for example, inside networking equipment with excessively noisy overkill cooling arrangements.
Trapezoid control algorithm
This is what I call the algorithm which I’ve used since the beginning. Essentially, it always gives me exactly what I want, no matter what the scenario.
The name comes from the four configurable points which is used to calculate the fan PWM duty based on the temperature reading:
- Minimum Temperature (tempmin)
- Maximum Temperature (tempmax)
- Minimum Fan Duty (fanmin)
- Maximum Fan Duty (fanmax)
Just in case the previous graphic isn’t clear, here are some example calculations the algorithm would make:
|tempmin||tempmax||fanmin||fanmax||Measured temperature||Final fan duty|
Is not supported at present. It could be added fairly easily.
There are two variations of the software for this board, with different use-cases:
Dual thermal zone
This was my original implementation. It supports two fans and two sensors only. The second fan/sensor can be disabled if only one is required. Each sensor and fan have individually configurable set-points and operate entirely separate from each other.
The obvious (and original) use-case for this is within a PC, with one sensor/fan on the CPU, and the second for the chassis.
Single thermal zone
This build is designed for the case where there is a set of fans on the rear of a chassis, with various items / areas within that chassis being monitored individually, all in a “single” thermal area. This build has been more useful to me in recent years.
Only a single set of configuration parameters is supported, however many temperature sensors are supported (default max is 4). The effective temperature used for the calculation is the maximum of all installed sensors.
The PWM duty of the two fan headers are always set to the same value, as it is assumed that the fans are both the same. Tachos are still monitored individually.
I do have versions of this which can monitor up to 6 fans (with the extra tachos connected to the SPARE header) however haven’t ported this code across to the version on github at this time.
No special software is required to configure the board, simply open up a terminal emulator to the attached COM port at 9600 baud and press Ctrl+C during power on to enter the configuration prompt. Help can be viewed by entering the command “?”.
This is the output from the “dual zone” build:
config>? Commands: show Show current configuration default Load the default configuration save Save current configuration exit Exit this menu and start fan1max [0 to 100] fan2max [0 to 100] Sets the maximum duty cycle for fan fan1min [0 to 100] fan2min [0 to 100] Sets the minimum duty cycle for fan fan1start [0 to 100] fan2start [0 to 100] Sets the duty cycle to use between reset and first calculation and when in the configuration prompt fan1minrpm [0 to 65535] fan2minrpm [0 to 65535] Sets the stall-restart threshold RPM for fan fan1minoff [0 or 1] fan2minoff [0 or 1] Set to '1' to power off fan below minimum temp Stall checking is not performed when set to '1' temp1max [-55.0 to 125.0] Sets the temperature at which fan is set to the maximum configured duty cycle temp1min [-55.0 to 125.0] Sets the temperature threshold at which fan starts to increase from the minimum configured duty cycle temp1hyst [0 to 180.0] Sets hysteresis when using 'minoff'. The fan will not switch off until current temp is less than temp1min, minus temp1hyst temp2max [-55.0 to 125.0] temp2min [-55.0 to 125.0] temp2hyst [0 to 180.0] Configuration for sensor 2 will apply to fan 2 if it is connected. Otherwise fan 2 uses sensor 1 with temp2max/min/hyst fan2enabled [0 or 1] Set to '1' if fan 2 is connected temp1desc [desc] temp2desc [desc] Sets descriptions (15 chars max) readtemp Probe and read out all attached sensors authcheck Check authenticity of attached DS18B20 sensors manualassignment [0 or 1] Set to '1' to enable manual assignment of sensor address-to-index sensor1addr [addr or 'none'] sensor2addr [addr or 'none'] Sets addresses of sensors config>
Counterfeit DS18B20 detection
The matter of counterfeit DS18B20’s only came to my attention relatively recently, when I was trying to attach some to the end of a long noisy line, and observed that one worked, and the other (which turned out to be counterfeit) didn’t.
There is a brilliant project on github including some source code for detecting and classifying clones, i.e. what kind of clone you are dealing with, there are many.
I’ve included this code in the project. It can be run with the “authcheck” command from the configuration prompt:
config>authcheck Found 2 of 4 maximum sensors 28:FF:C9:D5:73:16:05:6B: ROM does not follow expected pattern 28:XX:XX:XX:XX:00:00:CRC. Error. Scratchpad Register: 64/01/01/01/7F/FF/0C/10/03 Info only: Scratchpad bytes 2,3,4 (01/01/7F): not Maxim default values 4B/46/7F. Scratchpad byte 5 (0xFF): ok. Scratchpad byte 6 (0x0C): ok. Scratchpad byte 7 (0x10): ok. 0x4E modifies alarm registers: ok. 0x4E accepts 10 bit resolution: ok. 0x4E preserves reserved bytes: no, got: FF/3F/10. Error. 0x4E accepts 12 bit resolution: ok. 0x4E preserves reserved bytes: no, got: FF/7F/10. Error. Checking byte 6 upon temperature change: not necessary. Skipped. --> Sensor appears to be counterfeit based on 3 deviations. 28:FF:C9:D5:73:16:05:6B: Family B2 (Clone). 28:9B:50:17:0D:00:00:6C: ROM ok. Scratchpad Register: 63/01/4B/46/7F/FF/0D/10/15 Info only: Scratchpad bytes 2,3,4 (4B/46/7F): Maxim default values. Scratchpad byte 5 (0xFF): ok. Scratchpad byte 6 (0x0D): ok. Scratchpad byte 7 (0x10): ok. 0x4E modifies alarm registers: ok. 0x4E accepts 10 bit resolution: ok. 0x4E preserves reserved bytes: ok. 0x4E accepts 12 bit resolution: ok. 0x4E preserves reserved bytes: ok. Checking byte 6 upon temperature change: not necessary. Skipped. --> Sensor responded like a genuie Maxim. 28:9B:50:17:0D:00:00:6C: Family A1 (Genuie Maxim). See https://github.com/cpetrich/counterfeit_DS18B20 for more information.
In the above example, it turns out that one of my sensors is a 7Q-Tek QT18B20, despite it being labelled as “DALLAS DS18B20”. I knew it. £1 per sensor was too good to be true. My saving grace is that apparently the QT18B20 is a fairly good clone. Nonetheless, I won’t be buying any more off eBay.
I have provided all of the gerbers and source code, as well as pre-compiled builds in the links below.
As can be seen in the pictures, there are quite a few components which aren’t installed. These are mostly for my own various special use-cases and should not be installed for any of the cases described on this page.
EDIT: RS-232? Say what?
Having only just published this, the usage of antique interfaces has already been pointed out. In my defence the project is quite old, adding USB-to-serial would have bought in surface mount components. There is sort-of a workaround:
Building sources and programming the AVR
As with every project I publish including source code, and in this instance happening to use the same MCU as the Arduino UNO, this is not an Arduino project in any respect. If re-compiling, sources must be compiled with GNU binutils and AVR-GCC.
I’ll leave you to do your own research as to how to program the AVR. The programmer will need the pre-compiled .HEX file, and the following fuse settings:
- LFUSE: 0xDF
- HFUSE: 0xD1
- EFUSE: 0xFC