I decided that I would like to have a programmable temperature-based fan speed controller for a fume extractor I am building from scrap parts, and the logical thing to do seemed to be to use an Arduino Nano as modern fans use a 5 volt PWM signal to control their speed, and also report back their speed with another 5 volt signal. They are also wicked cheap if you are willing to use clones.
The basic scheme goes like this: use the cheapest convenient microcontroller with the cheapest convenient temperature sensor to control the speed of a "PWM fan" using code cobbled from samples found via google in as little time as possible — around as long as it takes to blog about it.
The Nano is my go-to Arduino because although it is small, it has onboard USB, an onboard wide input voltage regulator, and all the AVR features that you expect like variable-rate hardware PWM and multiple interrupt pins, albeit only two of them. The clones with the CH340G USB-to-Serial chip are available for around $3 each; I actually prefer this chip since FTDI started playing funny games with their drivers. We will use all of these features for this device. This code should run on most Arduinos, with some changes to hardware pin assignments; it should work on the Pro Mini without modification.
If you've got a small DC fan with continuously variable speed and more than two wires, then it's almost certainly a PWM-controlled fan. Typically, you've got a black wire for the ground, a red wire for 12 volt power, a yellow wire for a 5 volt, 25 kHz PWM speed signal, and then possibly a blue wire for a 5 volt tachometer return. Both the 3-wire and 4-wire fans are in common use.
The fan I'm using here is a small-diameter but deep (60x60x38mm) 4-wire fan salvaged from a server by someone else; I bought four of these Delta PFC0612DE fans at a flea market for a couple of dollars. They draw a peak of 1.68 amps at 12 volts and peak out at 12,000 RPM and 67.85 CFM, which makes them fairly beastly — perfect for this job. The idea is to fiddle around with fan speeds to see what makes the most sense — do I need more fan speed when it's cooler, or when it's warmer? Anyway, the fan followed the typical color code and is labeled with the direction of rotation and exhaust, so there were really no surprises there.
Since these fans had been used in a custom design with pairs of fans attached to a cute little cardedge connector which also bore LEDs, they had no connectors. In order to provide a fan connector both on the fan and the controller, I cut a fan extension lead which came with a braided cover. After shortening the cover appropriately, I wrapped the end with masking tape and then slipped a piece of heat shrink tubing over the tape. The tape could then be removed and the heat shrink tubing slipped down into place, to secure the cut end of the braided cover. I twisted some color-coded power input leads together with the matching red and black leads on the male end of the cut cable, and soldered those to the VIN and GND leads on one side of the Nano. I soldered the yellow lead to D9 and the blue lead to D2 — more on these choices later.
The Temperature Sensor
There were some surprises in the sensor department. After searching hither and yon, I found the sample code for using the device I had bought from eBay — the breadboardable KY-013 thermistor sensor. These were by far the cheapest breadboardable temperature sensor available (which is what I was looking for when I ordered some) and they are plenty accurate for my purposes. The vendor provided code which produces a centigrade temperature measurement. Unfortunately, they also provide mismarked modules; the power and ground leads are the reverse of marked. If you swap those pins, the code works great, the sensor works great, everyone is happy. I cut a servo extension lead and repinned the power and ground leads. The brown wire is the ground, and soldered to a GND pin on the other side of the Nano. The red wire goes to the 5V pin (which is fed by the voltage regulator on the Nano) and the orange wire goes to A0.
25 kHz PWM
The really interesting thing about this project is the 25 kHz PWM. While AVR chips (and thus Arduinos and their clones) provide numerous PWM channels, they do not all have the same capabilities. On the atmega168 and 328 running at 16 MHz, you have access to two hardware PWM channels capable of doing the 25 kHz PWM expected by typical PWM fans. You get access to that by tweaking the PWM timers. I did not have to figure this out at all myself, since the specific example comes up fairly frequently. Pins D9 and D10 are capable of 25 kHz PWM.
KY-013 temp sensor
The two big pieces of this project (besides the Arduino itself) are the fan and the thermistor. Thermistors are typically wired up as a divider with a static resistor, and that's what's provided on the KY-013. Reading it is very simple; a straightforward
analogRead(A0) is all it takes to get the raw data. The manufacturer provides the following code for making sense of it:
Temp = log(((10240000/RawADC) - 10000));
Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp );
Temp = Temp - 273.15;// Convert Kelvin to Celcius
RawADC is the passed int value from
analogRead. This works pretty well. The sensor heats up very readily, and cools down in a reasonable period.
Since the Nano has onboard USB to serial, why not make use of it? The code outputs the temperature in both centigrade and Fahrenheit measurements, as well the desired fan speed as a value from 0-320.1 If the fan speed monitoring function is enabled (the default in the code so far) then the actual fan speed is printed as well.
Because this is a 4-pin fan, it also supports reading the fan speed back by counting pulses. This must be done on pin D2 or D3, which are the pins on the Nano which support interrupts. It is relatively simple to count these pulses, do a little math, and report the speed of the fan in RPM. To this end, the tachometer pin is wired to D2, one of only two interrupt pins on the Nano. The other is D3. This signal requires a pull-up resistor, usually 10k. The internal pull-up resistor is used here.
The only tricky part is figuring out how many pulses your fan sends per revolution. If you have a transparent fan with no sticker, you can simply count the poles of your motor and come up with the likely answer. If there is a data sheet for your fan (as I found for mine) you can simply look up the number of poles and try using that as the value of
fanDiv. It's also possible for the fan to simply send one pulse per revolution, in which case you simply set it to 1. If you're using your own optical sensor to count revolutions, it's either 1 (for your reflective sticker) or the number of fan blades, if they're sufficiently reflective.
interrupts(); // enable interrupts for counting fan pulses
attachInterrupt(digitalPinToInterrupt(hallsensor), pickrpm, RISING);
One obvious improvement would be to add run-time configuration and EEPROM support so that new values for the fan do not have to be compiled in, but could instead be set by serial port. The interface could also allow selecting fan speed directly. Another obvious improvement would be to add support for a second fan, which the device has sufficient power and I/O to support. Fan speed auto-calibration could be implemented for fans with tachometers; fans have a minimum start power which could be calculated, and the output power could be calibrated from the fan's maximum RPM automatically, to permit limiting maximum RPM. Fan speed control could easily benefit from some hysteresis, and the RPM monitoring algorithm could be improved. And finally, this code could reasonably be the basis of a PID BBQ controller, if the thermistor were swapped out for a thermocouple interface.
This is a cheap little project which uses interrupt handling, basic timing loops based on the internal millis() timer, hardware serial, defines, and an internal pull-up, analog reads, odd PWM timing, as well as the on-board LED :) You can bang this one out for around five to seven bucks worth of parts, especially if you solder your fan direct to the Nano. This is actually considerably cheaper than buying a decent fan speed controller, although most of those will also work with two-wire fans. The serial output could be easily modified to be monitored by software, if necessary, although it is not very difficult to parse as-is.
- 1. For internal reasons, though this could be changed easily enough.
|The Arduino sketch for the fan controller||2.26 KB|