Arduino Computer Fan Controller Project

This site has been great fun, but it has run its course, and it is time to move on. Please get what you want from the site before January 1, 2020. Some pages will move to another site but most will go away.


Arduino computer fan controller.
Arduino Computer Fan Controller

The goal of this project is to make a computer fan controller capable of maintaining temperatures in a computer case area within a range by varying the speed of up to 4 non-PWM (3-pin) fans. It is not an inexpensive alternative to a commercial unit. It may be configured in such a way as to actually work, where many commercial units either don't offer the flexibility or just don't function well.

This project came out of my need for a quieter office, since my server is a little loud, having these fans:

  • 3 120mm fans - 2 in front and 1 in the rear.
  • 2 92mm fans on the CPU cooler.
  • 2 80mm fans in the rear disk drive area.
  • 1 50mm fan in the front disk drive area.

The CPU cooler fans are controlled by the motherboard, but the rest are uncontrolled and run full speed. They are not silent, although being good quality fans they are relatively quiet. Under full load on all cores the CPU temperature gets to about 12°C above ambient, so the fan speeds may be reduced without jeopardizing the cooling of the system. I use a Cooler Master HAF XB EVO case, which has plenty of airflow.


It is important to understand that this is not a temperature control - it is a fan controller. It makes fans go fast when the target temperature is high and slow when the target temperature is low. It can't increase the cooling in your case. In fact it is designed specifically to decrease the cooling when less cooling is appropriate.


To design this I used both the Arduino Nano V3 and an inexpensive Nano clone. It has a small footprint, USB, and is still Uno compatible. USB is important, since that's what I have available in the case without having to run a cable from the outside in. Many systems have unused USB headers on the motherboard, and I just connect directly to one of them. There is a serial port on the motherboard, but that is not common.

To find out the problems people have with fan controllers I searched NewEgg's website for "fan controllers", and then read all of the comments. Problems with reliability I can't address, but I looked at some of the issues users had with functionality. Some of the more common complaints:

  1. The possible fan speeds are limited to just a few, or are either on or off.

    This design uses 256 levels. 75 to 100 are lost to minimum speed, leaving at least 155 levels to be divided up between the discrete temperature values. The ADC can only digitize temperature to 0.488°C. Discrete temperature values are then (temperature_range in °C / 0.488). A 20°C range has 41 unique temperature values and therefore 41 possible PWM values.

  2. The fan speed doesn't appear to change, or varies when it shouldn't.

    I used feedback to lock the fan speed to the set speed. The Arduino gives the fan a two second boost at full power to help it get started, then sets the temperature determined PWM value. From minimum to maximum the PWM duty cycle is proportional to the temperature.

  3. It can't handle fans with differing power needs (1 or 2, large or small).

    I think I know why so many controllers have trouble with different fan loads. The scale of drive from a little 50mm fan to a pair of 120mm fans is huge. I made the fan current range adjustable so each fan has a full range of speeds from stall to full speed. It is a pot adjustment on the board - one for each channel. Once adjusted for a fan or fans, it may be left alone. All other adjustments are in the firmware. It also accommodates two fans on one port. You need a Y-cable to connect two fans.

  4. There is no software accessible user interface.

    The design provides a USB connection to the controller and makes most things configurable. A graphical python app or a serial terminal may be used to configure the fan controller and display fan status (temperature and PWM). You may write your own application to monitor the fans. The controller appears as a 9600 baud serial port on the USB bus. There is an API you can use to get data out of the controller in JSON format.

I hope I've solved these problems with this fan controller in a way that uses the Arduino to advantage. One thing I intentionally left off is RPM. An opto-isolator per fan to deal with the "floating" ground on the fan would not easily fit on the board. The fan RPM is just "interesting info" when you have the actual temperature of the area. Another thing I could have included was a fan failure detection circuit that would alert if the fan was drawing no current, but false alarms were a major complaint with the fan controllers I looked at.

Theory of Operation

If the temperature falls between mintemp and maxtemp the Arduino generates a PWM signal. If the temperature is below the mintemp the fan is either off, or set to a minimum PWM. If the temperature is above the maxtemp, the PWM is set to the maximum PWM or to 255, running the fan at full speed.

The fan speed is set by a simple PWM signal, but the relationship between set speed and actual speed is maintained by the analog circuit shown below. The current through the motor very roughly correlates to the RPM of the fan. Once a simple calibration is done, the percent of fan speed more-or-less corresponds to the percent of PWM duty cycle. All of this discounts the effect of the air on the fan's power consumption.

The top opamp circuit has a non-inverting gain of 3 that provides a 0 - 12VDC signal to the transistor circuit based on the PWM signal. The bottom opamp circuit converts the motor current to a voltage and uses that as the feedback to the top circuit to regulate speed. The potentiometer is adjusted so that the fan just reaches full speed at a pwm value of 255. It will be different for every fan type, and is set by looking at the collector voltage (on the tab of the transistor) while adjusting the trimpot with the fan connected. The controller output circuit robs the fan of about 1.2 volts, from the resistor and the transistor in series, so the fan's true top speed may never be reached with the controller. This is not unusual.

The Arduino Nano adjusts the PWM every two seconds based on changes in the measured temperature. The temperature is measured by an LM35DZ placed in the location to be monitored. All parameters are stored in EEPROM and are fully configurable via USB. Once configured it will survive a reboot or power cycle. It will read its configuration from EEPROM after restart so after making changes you need to store them. See the store command below. 12V power comes from a 4-pin diskette drive power connector. An adapter could be used if your power supply does not have a diskette drive power connector.


For reliability I went with a PC board rather than using a protoboard. Up to four circuits may be built on the 100mm x 100mm (3.92" x 3.92") board. There are PCB design files available for DesignSpark, a free schematic and PCB CAD package for Windows users. If you want to have a PCB made as is, here are the Gerber files.

The following parts are required for four channels:

  • 1 ea. PC board or protoboard
  • 1 ea. Arduino Nano v3 or clone
  • 2 ea. LM324 OpAmp DIP-14
  • 2 ea. 15-pin tall female header
  • 2 ea. 14-pin IC socket
  • 1 ea. 4-pin male header
  • 4 ea. 3-pin male headers Molex 22-11-2032 or equiv.
  • 4 ea. 3-pin male headers Molex 171856-0003 or equiv.
  • 4 ea. 1N4004 diode
  • 4 ea. 0.1µF 50V ceramic capacitor
  • 4 ea. 4.7µF to 10µF 16V tantalum or ceramic capacitor
  • 4 ea. 100kΩ multiturn potentiometer
  • 4 ea. 1Ω 1/2W resistor
  • 8 ea. 1KΩ 1/4W resistor
  • 8 ea. 10kΩ 1/4W resistor
  • 4 ea. 20kΩ 1/4W resistor
  • 4 ea. 100kΩ 1/4W resistor
  • 4 ea. TIP120 Darlington transistor
  • 4 ea. Aavid Thermalloy 577202B00000 Heatsink
  • 1 ea. Bytecc BRACKET-25525 (optional for mounting in 5.25" drive bay)
Arduino computer fan controller.
Arduino Computer Fan Controller

Resistors may be carbon film, except the 1Ω resistor. It needs to be a metal film type for power handling. The motor current goes through it, so you could have as much as 0.7 amp flowing through it if you have 2 x 120mm fans connected. That works out to be around 500mW. The resistor gets warm. With 2 x 80mm DeepCool fans I run 0.143 amps. That is only 20mW on the resistor, but it is 150mW on the transistor. Smallish heatsinks are required on the TIP120 transistors if you are running two large fans fan per port.

I picked up a couple of USB cables to "salvage" for the LM35 cables. Each cable is made for the location it is monitoring. Most of the fan cables turned out to be too short, so I had to make extensions. The fan controller is located in a 5.25" drive bay in the bottom front area of the case. One of the fans is in the top rear. The Bytecc bracket was initially hard to find, but is available on ebay (vistapc-micro) for $11, newegg for $6 + shipping, or Fry's Electronics for $5 + shipping. I picked mine up at the local Fry's store.

You may notice that the pictures do not show the Aavid Thermalloy heatsink. After I spec'd the Aavid, I looked in the parts bins and had these fancy heatsinks. I should have waited for the Aavid's to arrive. These get too close to the fan connectors and make it uncomfortable plugging in the fans. They don't get very hot, but they take up room behind the transistor, where the Aavid's take up room in front of the transistor. Just a convenience.


The fan controller firmware is around 7kB and uses 529B of RAM. It provides commands to set each parameter for each fan independently. Commands are given through the USB port, which is a USB to serial converter. Use a simple serial terminal program, like Hyperterminal or realterm for Windows, or use screen or SerialTools on the Mac. Linux also has screen. The controller commands are of the format:

  command channel value
  • mintemp channel °C

    Set the minimum temperature, at which the fan either stops or goes into a minimum speed mode.

    example: mintemp 0 35

    Sets the minimum temperature on channel 0 to 35°C. This is not the lowest temperature the monitored area will reach - it is the temperature above which the fan will come on. Must be between 0 and 255 inclusive.

  • maxtemp channel °C

    Set the maximum temperature, at which the fan goes to its maximum programmed speed.

    example: maxtemp 3 45

    Sets the maximum temperature on channel 3 to 45°C. This is not the highest temperature the monitored area will reach - it is the temperature above which the fan will be running at maximum speed. It must be between mintemp and 255. The range is not checked, so be careful to make it higher than mintemp.

  • minpwm channel value

    The PWM value (0 - 255) used at the minimum temperature. The starting speed of the fan.

    example: minpwm 0 100

    Sets the PWM used at the minimum temperature to 100. When the minimum temperature is reached, this will be the fan speed value. It needs to be a sustainable fan speed (20 won't work). Use the test command to find a good value. Every fan type will be different.

  • maxpwm channel value

    The PWM value (0 - 255) used at the maximum temperature. The highest speed of the fan. The fan PWM value will be linear between minimum and maximum temperatures.

    example: maxpwm 0 255

    Sets channel 0's PWM value to 255 when the maximum temperature is reached. This will usually be 255, but if the fan moves too much air at 255, backing it down to 240 or 200 may make sense. Again, use the test command to find the PWM value for that perfect maximum speed.

  • lomode channel value

    The mode at minimum speed. 0 = shut the fan off below the minimum temperature. 1 = leave the fan at the minimum PWM below the minimum temperature, so the fan never shuts off.

    example: lomode 0 0

    Sets channel 0's lomode to 0 (fan is shut off below mintemp).

    example: lomode 0 1

    Sets channel 0's lomode to 1 (fan doesn't shutoff at low temperatures).

  • himode channel value

    The mode at maximum temperature. 0 = stay at the maxpwm value. 1 = set the pwm to 255 to get the maximum possible speed from the fan.

    example: himode 0 0

    Sets channel 0's himode to 0 (fan stays at maxpwm over maxtemp).

    example: himode 0 1

    Sets channel 0's himode to 1 (fan set to 255 above the maximum temperature).

  • mintime channel value

    The number of seconds the fan must run any time it is started. Keeps the fan from "thrashing" around the minimum temperature. Higher energy areas increase in temperature faster so should have a longer minimum run time. Maximum is 255.

    example: mintime 0 10

    Sets channel 0's minimum run time to 10 seconds.

  • test channel value

    Set a fixed PWM value on the fan for testing purposes. Useful for finding the best minimum fan speed. The monitored temperature will be ignored.

    example: test 2 85

    Sets channel 2's PWM value to 85 fixed for testing. To turn off test mode:

    example: test 2 0

  • store

    Store the parameters in EEPROM. The commands above work on parameters in RAM. If you reset the Arduino, the parameters go back to the previous values. The store command makes them permanent.

    example: store

  • clear

    Clear the EEPROM memory. Follwed by a reset, it sets the parameters back to "factory defaults".

    example: clear

  • print

    Returns a table of parameters as currently running in RAM. Shown are the defaults.

    example: print

      minTemp: 30  maxTemp: 60  minPwm: 100  maxPwm: 255  loMode: 0  hiMode: 0
      minTemp: 30  maxTemp: 60  minPwm: 100  maxPwm: 255  loMode: 0  hiMode: 0
      minTemp: 30  maxTemp: 60  minPwm: 100  maxPwm: 255  loMode: 0  hiMode: 0
      minTemp: 30  maxTemp: 60  minPwm: 100  maxPwm: 255  loMode: 0  hiMode: 0
  • chnls #channels

    Sets the number of channels to be polled. Default is all four channels. Reduce the number if you have fewer channels in use and the reporting will just show the channels used.

    example: chnls 2

    Sets the number of active channels to 2. Channels always start at zero and work their way up.

  • interval #seconds

    Reports are output for all active channels periodically. The default is 4 seconds. This command sets the number of seconds from 2 to 254 in 2 second intervals.

    example: interval 10

    Sets the number of seconds between reports to 10. Setting it to a value of 2, 1 or 0 will result in a report every two seconds.

  • api

    causes the controller to enter API mode. This is for programs that will do their own polling to get parameters and status. It essentially stops all unsolicited output from the controller.

    example: api

    Stops all output from the controller. Only a reset will bring it back.

  • stat

    Sends the status of all channels to the host in JSON format.

    example: stats

  • dump

    Sends the parameters for all channels to the host in JSON format.

    example: dump


I put together a little python app that should work on Linux, Mac and Windows. It monitors all four channels and graphs about 400 seconds of history. The main parameters are programmable using this app. Once assembled and calibrated the fan controller needs only the USB connection for power to the Arduino. No user interaction is required, but the graph is interesting while you are configuring the unit.

To run it you need python 2.6 or 2.7, the pySerial module and the tkinter module. Most distributions have tkinter, but my Ubuntu server did not. You will need to pick two lines in the top of the file:

  # Mac with Genuine Arduino Nano
  port = "/dev/cu.usbserial-A9AX9VRN"
  display = "Window"

  # Ubuntu 16.02 with Genuine Arduino Nano
  port = "/dev/ttyUSB0"
  display = ":1"

  # Windows 10
  port = "COM3"
  display = "localhost"

All three are in the file, just comment out the ones you don't need and uncomment the one you need. Tested on Mac OS X High Sierra, Ubuntu 16.02 server, and Windows 10. Your port name may be different, based on the USB ID and the number of USB to serial converters on your system. With Mac it is just the USB ID, but Linux and Windows name the ports in order of discovery, changing the port name if you unplug it and plug it back in.


If you change any parameters and want to upload them to the Arduino, click the "Update" button for that channel and all of the parameters for that channel will be uploaded and stored in EEPROM.

Final Assembly

The final assembly involves plugging in fans and attaching LM35DZ temperature sensors, but it is unique to the installation. This case is black inside and out, and so there is no way to get a picture of it, but it wouldn't show anything anyway. The controller is mounted underneath the optical drive which is under the motherboard, and so can't even be seen with a flashlight.

I mounted the controller on a Bytecc 2.5/3.5 to 5.25 adapter plate so it could live in the unused 5.25" drive bay. A USB A to mini cable was modified by replacing the A connector with a single-row header pinned out for the motherboard USB-2 headers. The cables are long so that I can slide the adapter out the front far enough to plug in the connectors.

I put three temperature probes in the case - one between the rear drives, one between the front drives, and one in the rear in front of the rear fan. That one senses the air coming off of the CPU cooler and turns up the rear exhaust fan if it warms up in there.

Using the Controller

When it comes time to hook a fan to the board, you will need to adjust the current feedback to match the fan. Clip a voltmeter between ground (one of the four mounting holes) and the tab of the transistor for the channel you are working on. With the fan off (test 0 1) you should read 12V. With the fan on (test 0 255) you may see the voltage drop to around 1.2V. You want to adjust the pot until that voltage just hits bottom, then back it off until the voltage just starts to move up the tiniest bit. Now the channel is calibrated to that fan. If you don't calibrate, one of three things will happen:

  1. It will miraculously work. This is unlikely.
  2. The fan will run slow when it should run fast.
  3. The fan will not run slow at all.

Calibration adjusts the amount of feedback based on the current through the motor. If the motor draws a lot of current and the pot is not adjusted for it, the fan will run slow because it will get too much feedback. If the fan is a smaller one that draws less current, there will not be enough feedback and the fan will run fast, but may not run slowly when needed.

It seems intuitive to set the minimum and maximum temperatures and PWM values so that the fan speed gently increases with temperature from cool to hot, but that is not going to cool the target area. To cool the area the fan has to run fast enough to drop the temperature. A good way is to set the temperature range so that the maxtemp is just above the temperature you want to maintain, and the temperature at which you need to start the cooling is the mintemp. The fan will then try to pull the temperature down from the maxtemp. Not all environments will work that way. Some areas will have a temperature that varies over a wide range, and the fan controller will work to change the temperature better. Others will have a temperature that stays pretty constant, and just needs some airflow to move that heated air away so it doesn't affect things like the CPU.

Something I learned from this - the two 80mm fans that are intended to cool the disk drives... With the fans running 100% the temperature is 39°C in the center between two stacked drives, while with the fans running 65% the temperature is 40°C. With the fans off, the temperature slowly rises to over 50°C. That tells me the fans will never need to run full speed, and so will benefit from the controller.

Because the case maker put a fan location there it must be needed, right? I monitored the temperature of the X99 chip while doing the same kind of experiment, and in the range of off to full speed the temperature of the X99's heatsink never changed. This is one of the two big 120mm fans on the front. It has no effect on the temperature in the area it serves. That is probably because it sits right beside another just like it that does have an effect. One fan is probably adequate for this location/CPU/motherboard/load/environment.

This controller does not cool better - it uses the minimum amount of fan to maintain the airflow within the set range in the controlled area. More fans cool better, and the controller helps prevent them from all running full speed when the computer is already cool. When the computer is hot, the appropriate fans will still run full speed as needed. It can also help you determine the fans that are useful and the ones that just make noise. A good way to cut the noise is to take out the fans that don't do any cooling.

The following files are part of this project:

Arduino Board Logo


Arduino-Board is the go-to source for information on many available Arduino and Arduino-like boards, tutorials and projects.

Help and Support


Stay updated

Sign up if you would like to receive our once monthly newsletter.