Automatic 4-channel pwm PC fan controller.

How to make “Automatic 4-channel quiet pwm PC fan controller.” With optional programmable RGB LED display.

Arduino 4-channel automatic fan controller with programmable RGB LED strip

I received an new Antec 902 case with tons of air flow but the unit came with these intensely bright blue LED 2-wire fans. These fans while not particularly loud themselves, were just noisy enough to force me to turn up my volume excessively when watching movies. This wasn’t considerate to the rest of the household as I’m the only nite-owl. Not to mention the ultra bright LEDs glaring over the movies of a darkened room, not cool.

So to remedy this I made a 4-channel automatic pwm fan controller using an Arduino UNO,  LCD Smartie, Sensor Bridge (an add-on for LCD Smartie).

Here’s how I did it:

Novice Help: How to wire up a fan to be controlled by an Arduino.

Hardware:

4chFanControllerUNO_bb 4chFanControllerUNO_schem

4chFanControllerUNO_pcb

You can grab the Fritzing file for this here.

Or if you intend to make/order a PCB, snag the whole Fan Controller archive to get the Gerber files too.

Please note: The 2-Ohm resistors and the 1000mf capacitors are optional, but they really do help a LOT to make PWM’d fans quiet. You will need heat-sinks if you do include them. Alternatively, you can omit the 2-Ohm resistors and replace the TIP120’s with TIP31’s, for they have a higher internal resistance. However, all the heat dissipation will then depend entirely upon the heat-sink/air-flow.

For anyone that would like to know more about controlling 3 or 4-wire fans click here. Otherwise read on…

Software:

I use LCDsmartie to send data to my Arduino UNO via usbSerial link:

  1. Get/extract LCD Smartie (it just runs from the place you extracted it to).
  2. Get an add-on for it called “Sensor Bridge” extract it to the LCD Smartie/plugins/ folder. If it gives you trouble running it, just read the forum for advice.
  3. Inside LCD Smartie select the display plugin: testdriver.dll
  4. Set the COM port your Arduino uses. (Mine is COM5) ie, I use the init string of: COM5,115200,8,N,1
  5. Just to the left of the init string box there is a vertical tab called ‘Screen’, set the ‘LCD size’ to 1×40.
  6. Close the program. This frees the com port so we can upload the sketch.

Grab a copy of the sketch here.

Unzip it. There should be 3 files:

  1. SA4chanFCv4.ino – were the options are for you to play with.
  2. addLEDstrip.ino – this is an optional file for playing with programmable RGB LED light strips. (Colors and intensity are temperature responsive.)
  3. program.ino – the actual program that does the work.

Don’t want the LED stuff, its just junk to you? No, problem. Just throw it out! (delete the file)

Open the SA4chanFCv4.ino file in your Arduino IDE tweak as needed and upload.

Now lets get this show on the road.

Remember the folder that you extracted the SensorBridge.dll plugin into? Look at it again. Since we ran LCD Smartie once already, SensorBridge made a log file called SBReport.txt, the lines were interested in are the ones that start with ‘Command : ‘ followed by something like: $dll(SB,3,0,0.00) its these entries we need to use later.

Open it and you’ll see the list of the sensors it found in your PC. Will need this info later, so keep it open for now.

Almost done:

Fire up LCD Smartie again, this time lets put it to work…

The Arduino sketch indexes the numbers by using the preceding lowercase letter, records the data sent, then validates the end of entry by looking for the next alphabetical letter to cap it off. Then repeats until the serial buffer is empty.

In the setup screen under Screen settings in the long text box put, an lowercase a followed (no spaces anywhere) by one of those $dll(SB,#,#,0) entries, follow that with the subsequent lowercase letter, in this example its a b so a chain of these would look like:

a$dll(SB,3,0,0)bb$dll(SB,2,9,0)cc$dll(SB,2,10,0)dd$dll(SB,2,11,0)e

For me I used:

a$dll(SB,5,0,0)bb$dll(SB,3,9,0)cc$dll(SB,1,10,0)dd$dll(SB,1,11,0)ee$dll(SB,9,0,0)ff$dll(SB,9,2,0)gg$dll(SB,3,5,0)hh$dll(SB,3,6,0)ii$dll(SB,3,7,0)jj$dll(SB,3,8,0)k

My layout: aGPUtempbbCPUtempccVregTempddSysTempeeHD0TempffHD1TempggCore0TemphhCore1TempiiCore2TempjjCore3Tempk

Looks like: LCD Smartie

That’s it. Hook up your fans. From here on out its just a matter of tweaking the sketch.

Although its easy to change, just remember; By default the Arduino sketch is looking for data in the order of GPUtemp, CPUtemp, VregTemp, SysTemp, HD0Temp, HD1Temp, Core0Temp, Core1Temp, Core2Temp and Core3Temp.

25 Responses to Automatic 4-channel pwm PC fan controller.

  1. cichlidman1 says:

    would this layout work if i were to use n-channel mosfets as well?

    • dewy721 says:

      Yes they can.

      For example the IRLB8721 mosfet (hexfet) would be a great choice, just omit the 1Kohm resistors and drive them directly from the arduino output pins. Just be sure to use some 10watt 2.2-Ohm power resistors to soften up the power going into the caps.

      You can find some here: http://www.adafruit.com/products/355

  2. FW says:

    Hello.
    How would a sketch need to look like when to channel 20 of these PC fans?
    I am new to this.
    Any help would be wonderful.

    Best
    FW

    • dewy721 says:

      Hmm, 20 fans you say? Wow that’s epic!

      There is some hidden problems with your request though:

    • 1. Do you have 20 separate temperature sensors in your PC?
      2. If you need *all* 20 fans to be variable speed your going to face some additional challenges…
    • The actual problem is that an Arduino has only so many PWM channels to offer. For example, the Arduino model Mega2560 has the most PWM channels but, still it only has 15.

      For the last five pins you can:

    • a. Let them run in binary mode. (on/off) Fairly easy method, just modify existing code.
      b. Use a PCA9685 i2c chip that allows you to add +16 pwm channels per chip (62 chips max). But you’ll need to do your own code for it. Most complex ‘over the top’ method. Lots of coding.
      c. Fake it with softPwm library to bit bang those last five on/off fairly rapidly. Mildly complex, light coding required.
      d. Slave them into 5 other fan channels. (Make a 15 channel controller with 5 of those as dual-fan outputs) Easy, ‘pimp my stock Arduino’ method.
      e. Wire 5 fans per transistor (INCLUDE A HEATSINK), use stock 4-channel code. Easiest, ‘Bubba fix’d it’ method.

    • Ok, on to the software stuff…

      Well, first take a look at the setup options for LCD Smartie. Under “Display settings” you’ll see a tab labeled “Screen”… change the drop down from “1×40” to “2×40” Add in another 10 sensor entries using the same letter sensorOutput nextLetter scheme, just be sure to start where the first line leaves of at.

      Then in the arduino code you would need to change every line that has [11] in it to [21]. Then add in ten more values between the {} braces of each one you modify. Change pin[7] to pin[21] and replace the values with the 20 pins your going to use for each fan.

      Optional:
      By default the sketch looks for 4 fans to kick on while waiting for data; to change this behavior replace pinBootup[5] with pinBootup[20] and also replace the values with your fan output pins, only this time place the entries in the order you want to see them boot-up in.

      Afterwards, click on the tab labeled “program” and under void setup() find the line:
      for(int i=0;i<4;i++){
      replace with:
      for(int i=0;i<20;i++){
      This will allow the controller to boot all 20 fans fading them in from 0~pwmMax per fan sequentially upon bootup. NOTE: That’s a lot of waiting so several lines below it you see delay(stepDelay); change it to delay(stepDelay/5); if you want a faster boot sequence.

  • FW says:

    Hello dewy721,

    I see that I need to go into details.
    Thank you very much for your answer.

    Well, this is for an artistic installation, where the 20 fans blow up fixed paperstripes.
    So it is not for any pc cooling.
    I also do not need the LED/LCD Smartie stuff.

    The 20 fans also shouldn’t be controlled individually by 20 PWMs.
    They should run synchron on the same power level.
    Well, I am really new to Arduino.
    What I do is researching possible ways of solving this task for me.

    I spotted the information that one Arduino PWM should be enough to send
    the signal to the fans because each fan has got an inbuild PWM pin.
    These fans should react to a „HC-SR04 ultrasonic range finder“: the closer the person is in front of the fans the faster they should run.

    —-
    this is fan-type i want to use
    Link: http://www.conrad.de/ce/de/product/871001/LogiLink-8-cm-PC-Luefter

    12V DC
    0,14A
    4 pins
    ——
    I have yours and another persons tutorial/sketch which I hoped to understand and join togehter to have my soltuion.

    This person here controlls LEDs with the „HCR-SR04“.
    Link: http://fritzing.org/projects/hc-sr04-and-led-project

    Combining your way and this person’s way should be a solution I hoped.

    Are you interested in helping me out to create the technical backbone for my project?
    My major work for this project is the conceptual part and the designing part.
    For the fan controlling part I really need some help.

    It would be wonderful if you would help me out on this.

    Best,
    FW

  • dewy721 says:

    Try this instead. I found an example at https://gist.github.com/flakas/3294829 that looks to be more useful for you.

    I’ve modified the example below to do exactly what you asked. That is, control 20 fans from one transistor using the SR04 ping transducer for input.
    I hope it helps. 🙂


    /* HC-SR04 Sensor
    https://www.dealextreme.com/p/hc-sr04-ultrasonic-sensor-distance-measuring-module-133696
    This sketch reads a HC-SR04 ultrasonic rangefinder and returns the
    distance to the closest object in range. To do this, it sends a pulse
    to the sensor to initiate a reading, then listens for a pulse
    to return. The length of the returning pulse is proportional to
    the distance of the object from the sensor.
    The circuit:
    * VCC connection of the sensor attached to +5V
    * GND connection of the sensor attached to ground
    * TRIG connection of the sensor attached to digital pin 2
    * ECHO connection of the sensor attached to digital pin 4
    * FAN output connection on pin 6

    Original code for Ping))) example was created by David A. Mellis
    Adapted for HC-SR04 by Tautvidas Sipavicius

    This example code is in the public domain.
    */

    const int trigPin = 2;
    const int echoPin = 4;

    void setup() {
    // initialize serial communication:
    Serial.begin(9600);
    }

    void loop()
    {
    // establish variables for duration of the ping,
    // and the distance result in inches and centimeters:
    long duration, inches, cm;

    // The sensor is triggered by a HIGH pulse of 10 or more microseconds.
    // Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
    pinMode(trigPin, OUTPUT);
    digitalWrite(trigPin, LOW);
    delayMicroseconds(2);
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);

    // Read the signal from the sensor: a HIGH pulse whose
    // duration is the time (in microseconds) from the sending
    // of the ping to the reception of its echo off of an object.
    pinMode(echoPin, INPUT);
    duration = pulseIn(echoPin, HIGH);

    // convert the time into a distance
    inches = microsecondsToInches(duration);
    cm = microsecondsToCentimeters(duration);

    ///////////////// Added fan output /////////////////

    // scale and invert the distance data (cm) so fans
    // go to max output when distance is zero.
    #define pingMinimumDistance 2
    #define pingMaximumDistance 400
    #define fanMinimumOutput 0
    #define fanMaximumOutput 255
    int invertedValue = map(cm, pingMinimumDistance, pingMaximumDistance, fanMinimumOutput, fanMaximumOutput);

    // constrain the data within valid limits.
    int constrainedValue = constrain(invertedValue, fanMinimumOutput, fanMaximumOutput);

    // apply data to the fan.
    int fanOutputValue = constrainedValue;

    // actually set the fan to the desired speed.
    analogWrite(6, fanOutputValue);

    ///////////////// End of fan code. /////////////////

    Serial.print(inches);
    Serial.print("in, ");
    Serial.print(cm);
    Serial.print("cm");
    Serial.println();
    delay(100);
    }

    long microsecondsToInches(long microseconds)
    {
    // According to Parallax's datasheet for the PING))), there are
    // 73.746 microseconds per inch (i.e. sound travels at 1130 feet per
    // second). This gives the distance travelled by the ping, outbound
    // and return, so we divide by 2 to get the distance of the obstacle.
    // See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf
    return microseconds / 74 / 2;
    }

    long microsecondsToCentimeters(long microseconds)
    {
    // The speed of sound is 340 m/s or 29 microseconds per centimeter.
    // The ping travels out and back, so to find the distance of the
    // object we take half of the distance travelled.
    return microseconds / 29 / 2;
    }

  • FW says:

    Thank you so much.
    I will give it a try as soon as possible.
    Thank you.

    FW

  • Pingback: Anonymous

  • AndersHF says:

    I have a question about the 2ohm resistors. Can i use 1/4W resisistors here or do I need some that can handle more W?

    And you say you need heatsink if you use the 1000µF capacitor and 2ohm reisistor, exactly what needs to be heat sinked?

  • dewy721 says:

    I’d recommend 5watt or bigger if you plan to use TIP120’s. (Or use smaller/no caps and get a little more noise.) Reason being is that the transistor is (for a microsecond or two) short circuited via the capacitor. If want to you use lossy TIP31’s like I did then the transistors become the resistor as well. Then you can skip the 5w resistors, but you WILL NEED a heat sink for the transistors. I salvaged a PSU heatsink and put the circuit in direct airflow. It works a treat for me at least.

    NOTE: This circuit make the most heat when the fans run slow, as the fans would be relying mostly on stored capacitor power (slow PWM). Then the heavily drained caps pull more current (when it can) to fill back up.

  • Anders says:

    Okay, thank you very much!

    Great project, cant wait to get a quiet PC

  • djbetterly says:

    Can you use an arduino motor shield with this setup?

    • Duane Bishop says:

      I don’t why you couldn’t use the motor shield but it would only provide output for two fan channels. You’d just need to change the pin settings to point to the motor shield’s pwm pins. Just set the ‘brake’, enable, direction pins to either high or low (I dont remember which atm.)

  • djbetterly says:

    Awesome, although I like your setup better, I’m going to evaluate both.

    On a side note, what does the unit use for measuring temperature? Or did I miss that in the write up?

  • djbetterly says:

    Ok but the breadboard setup will work well for connecting several PWM fans, then I just need to work out the code correct?

  • djbetterly says:

    Well, I’ll have to do a lot more research…I thought it would be a lot easier to implement a DS18B20 and a PWM fan together. My lack of PWM / Arduino knowledge is killing me now..ugh!

  • dewy721 says:

    Believe me, I know how you feel. <:)

    At the moment I'm eyeball deep, cross-referencing 7 different knowledge articles on: 3 LinuxCNC configuration guides, 2 BeagleBone Black hardware reference manuals, and 1 MachineKit setup example and finally a spreadsheet regarding one of several PRU LinuxCNC-HAL driver modules on a microcontroller platform I just received 2 weeks ago.

    If your willing to take a few Baby steps. Its all the courage you'll really ever need.

  • Gabriel says:

    Could you please share the Sensor Bridge add-on? The link provided is down. Thanks!

  • Pingback: Automatic 4-channel pwm PC fan controller. | basheerabdulwahab

  • Andrew says:

    Hi, thanks for all the great info.

    Just wondering, what PWM frequency is this running at? I couldn’t work out from your code, but I read that the default PWM freq for the Arduino using analogWrite is 980Hz, much too low to be out of audible range.

    Do you think it would be quiet if I ran an n-channel mosfet using PWM at over 25kHz, even with the capacitors/resistors omitted?

    Also, just a small correction: where you said, “Please note: The 2-Ohm resistors and the 1000mf capacitors are optional”, I think you meant to say “1000uF capacitors”.

    Thanks again.

    • Duane says:

      Yes, you can change the pwm frequency, just be mindful that (if I recall correctly) timer0 is also used by micros() & millis() which is used for the LED code & serial library.

      Also, the 3 different timers are hardwired to specific pins as well. So while you can do it, you’ll have to accomodate those details into the build as well.

      Here’s a cheatsheet on the subject….
      http://playground.arduino.cc/Main/TimerPWMCheatsheet

      Happy hacking.

      • Andrew says:

        Thanks. Do you think the noise will be lowered by making the PWM much faster? I will test it if you don’t know.

  • pp says:

    I have a project where I want to use all the PWM outputs of a Arduino Due (12) for controlling 12 fans. I guess it is possible to use the same electronics. Just make them 12 instead of 4?

    And what about this:
    “The 2-Ohm resistors and the 1000mf capacitors are optional, but they really do help a LOT to make PWM’d fans quiet. You will need heat-sinks if you do include them”

    For me it is important to make them as quiet as possible. How and where do I place the heatsink? Next to the capacitor?

  • Leave a comment