A Proportional 12V Fan Controller

The Ubuntu machine covered in my previous post lives in a cupboard with a wireless router and a DSL modem. Its not a very big cupboard, and… it gets rather warm in there. Even though the computer doesn’t use a lot of power, the heat doesn’t have anywhere much to go. Thats not good for longevity or reliability. I made a couple of 100mm diameter vents of the kind you use to vent clothes dryers, high and low, from the back of the cupboard into an adjacent storage room, but convection just didn’t seem to be doing enough, especially on warm days.

The answer was, of course, to add a fan. The PC can provide 12V power, and a standard 92mm PC case fan will fit nicely into the upper vent. Sort of an out-of-case fan. The only disadvantage is the sound of the fan running all the time. And the solution to that is a fan controller, preferably a proportional one which would run the fan only as fast as it needed to to stop the air in the cupboard exceeding some reasonable temperature. It is perhaps a bit of overkill for this particular location, but the design could be useful in other places as well, like an HTPC.

Fan controllers aren’t difficult to come by, but they seem to me to be surprisingly expensive for what they are. Proportional controllers used to be quite complicated things before microprocessors came along, but now the necessary algorithm can be coded into something very small indeed. I already had an appropriate part, the PIC12F675. This is an 8-pin chip that has a complete processor, 64 bytes of RAM, 1K of flash memory, and an A/D converter. Its one disadvantage is that it has to be programmed in assembler, but I have some experience with the PIC family and this application is simple enough it was worth putting up with assembly language to get the job done. I also had a temperature sensor, the LM34. It puts out 10mV per degree F, which the PIC can read directly to an accuracy of about half a degree F. More than good enough.

Now, the 12F675 may have an A/D to read the temperature with, but it doesn’t have a D/A converter to generate a voltage to drive the fan with. We can make a virtue of this, though. Fans and motors – and the circuitry that drives them – work more efficiently if driven with pulses of power rather than continuous voltages. To increase the speed of the fan, we generate more pulses, or make the pulses wider. This technique, called pulse-width modulation (PWM) is very widely used. The 12F675 can generate pulses just fine, so we drive a small power MOSFET with one pin and we are done.

So my first circuit could hardly have been much simpler, and looked like this:First Fan Controller Circuit

The three-pin standard PC fan connectors at the input and output carry a speed signal back to the PC, which can be useful if it lets you monitor the fans.

This design certainly works, and controls temperature, and uses very few components, but unfortunately — it isn’t very quiet. The pulses that drive the fan are generated at a frequency of 2kHz and below, which is in the audio range. The fan motor resonates with the pulses and makes various high-pitched squealing sounds. This isn’t very restful.

The solution is to add another component or two to turn the pulses into a linear drive. What I ended up with was this:

Fan Controller Second Circuit

Here the 10K resistor and 1uF capacitor act as an integrator that converts the pulse train into a continuous voltage over the range 0-5V. The op amp and the power transistor convert the 0-5V into 0-12V and provide enough power to drive the fan. The 22uF capacitor across the output stabilises things, and we get a nice continuous voltage range. This isn’t as efficient — but its much quieter. I have also added some LEDs attached to the spare I/O pins for debugging. These tell me if the fan is meant to be off (green) or full on (red), plus a yellow one that just blinks to show that the CPU is actually working.

The code that runs all this was assembled with the free MicroChip MPLAB system, and can be found here. The code is best considered in two parts, the PWM generator and the temperature-to-speed code. They communicate only through the Speed variable but are otherwise quite separate.

The PWM generator is interrupt-driven at a fixed rate of about 3900Hz. The frequency isn’t critical and happens to be what the Timer0 hardware in the PIC can easily generate. The generator makes use of a clever idea that appears in quite a few algorithms, which is to focus not on calculating an ideal result, but on tracking the error created by its approximation of that result. Here we want to convert the Speed value, which is between 0 and 127, into a stream of pulses where the density of high pulses runs from 0/127 (no pulses) to 127/127 (always on). Instead of trying to work out patterns of 1’s and 0’s to generate the required density, the code simply accumulates an error value. The error represents the difference between the Speed value (which it can’t generate) and the actual 0 or 127 that it can generate by doing or not doing a pulse. At each interrupt, it looks at the error and decides to generate a pulse if it is negative and no pulse if it is positive. It then updates the error to reflect what it has done, by adding the difference between the generated value and Speed. If it did a pulse it adds 127-Speed, and if there was no pulse it adds 0-Speed – or in other words, subtracts Speed.

The temperature-to-speed code uses the same idea. It runs much less frequently – the temperature can’t be expected to change very quickly. It also uses 16-bit values and arithmetic so it can cope with the 10-bit A/D conversion result. It determines the difference between the actual and target temperatures and adds it to an accumulated error value. When the error value reaches a positive threshold it increments the fan speed and subtracts the threshold from the error. If the error goes below a negative threshold it decrements the fan speed and adds the threshold to the error. By adjusting the threshold and the frequency at which the code runs it is easy to make the fan react appropriately slowly.

The calculated speed is limited to a suitable range by checking it against a minimum value before decrementing and a maximum value before incrementing, which is where things get slightly more complicated. It would be nice to run the fan through 128 speed steps from stopped to full speed, but real fans won’t do that. Real fans have a minimum voltage that they will spin at. For 12V case fans it is usually around 5V, but you should check your fan specifications. Below the minimum voltage, the fan will stall, and sit there heating up and possibly damaging itself. The code deals with this by separating the calculated fan speed, FSpeed, from the actual fan speed, Speed. The calculated FSpeed is incremented and decremented over the range kMinSpeed to kMaxSpeed, and Speed is an exact copy of it — except when FSpeed equals kMinSpeed. Then we set Speed to zero, and the fan stops. A further detail is that the code makes sure that the fan starts when FSpeed goes from stopped to kMinSpeed+1 by actually setting Speed to a higher value, defined in kStartSpeed, for one time interval before falling back to kMinSpeed+1.

This design and code should be adaptable to a wide range of different parts. Any temperature sensor should work with it with a few adjustments. The power transistor isn’t critical; the TIP 127 is actually a Darlington, but it doesn’t need to be. A BD140 would be fine. I put a small heatsink on it, but I’m not sure its necessary unless the fan is running slowly in a hot environment, which shouldn’t happen.


, , , ,

  1. #1 by rui on May 4, 2008 - 9:52 am

    Your controller is just what i’m looking for
    infortunently, wen I put the code in mplab there are errors to eliminate. some diferences are in characteres, mabe because my software is configured to portuguese.
    I appreciate if you send to me the .hex code and/ or the .asm file


  2. #2 by johnarthur on May 4, 2008 - 8:11 pm

    I have uploaded an archive of the project to:


    I hope this solves your problem.

  3. #3 by rui on June 19, 2008 - 12:19 am

    the controller is now working properly.
    thanks for help.

    sorry for the delayed reply

  4. #4 by Pasan on July 10, 2008 - 11:16 pm

    This is awesome

    Thank you

  5. #5 by David on March 5, 2009 - 1:59 am

    Your articles are very helpful. I have built an IP weather camera that is hidden in a bird house and I am rigging up a fan control circuit to keep the internals cool especially for those hot summer days and your proportional fan controller circuit is great. At what temperature does the fan beging to operate?



    • #6 by johnarthur on March 5, 2009 - 6:20 pm

      The fan aims to keep the temperature below the constant kTargetTemp which equals 160 / 2.04 = 78.5F. This should be the temperature at which the fan will start running. Inaccuracies in the LM34, however, mean it could be anywhere between 74 and 81F. You might need to test your particular setup and tweak kTargetTemp to suit.

  6. #7 by David on March 30, 2009 - 10:29 am

    Doe the LM358 power get connected to the +12v or +5 volts?



    • #8 by johnarthur on March 30, 2009 - 10:33 am

      To the 12V rail. Sorry, that really should have been in the circuit diagram…

  7. #9 by David on March 30, 2009 - 1:29 pm

    OK I double and tripple checked the circuit and there is a PWM signal on pin 2 regardless of the temperature on the LM34. I ran the LM34 down to 64 degrees and observed .643 volts on pin 7 but the PIC code still continues to generate a PWM signal. Pin 3 and 6 are high and pin 5 is toggling indicating the PIC code is executing. I also disconnected pin 2 on the PIC from the circuit and momentarily applied 5v to the 10K resistor on the side that was connected to pin 2 and the fan executes and full rate. Once I momentarily ground that same side of the 10K resistor, the fan stops running. I am using the PIC code unaltered from your web site. Any suggestions would be greatly appreciated.



  8. #10 by johnarthur on March 30, 2009 - 1:48 pm

    It sounds as though it is basically OK. You may be being misled by the length of time it takes to react to things; it takes tens of seconds for it to appreciably slow down or speed up – or come to a stop. If you put a DMM on the 1uF capacitor and cool the LM34 to 64F you should see the average voltage slowly falling there until it gets to about a 20% PWM at which point it will turn off. At worst you could try disconnecting the LM34 and using a 10K pot instead to generate voltages in the .32 – 1V range to see how it behaves.

  9. #11 by shamus on November 24, 2011 - 3:16 am

    Is a pic really nessasary? Just an LM35 and a couple of transistors usually work for me, add an op-amp and it will wash you windows.

    • #12 by michaelhamel on November 24, 2011 - 9:43 am

      The PIC was good for PWM, once you start smoothing that out you have a point. What it can still do that is difficult with a linear circuit is a clean cut-off and start-up, which I think is better for the life of the motor.

  10. #13 by Peter on April 30, 2012 - 8:54 am


    I made in brazil and if i use lm35 is have problem ??
    I need modify asm ??

    thank you

    • #14 by michaelhamel on April 30, 2012 - 9:44 am

      Yes, you would need to modify the code. It assumes that 0V = 0F = -18C. So an LM35 will make it think it is too cold and it will run the fan more than it should.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: