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.
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:
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.