Using Arduino to control a 4 Pin PC Fan - Help!

Despite spending what seems like forever searching for this I can't get anywhere so I thought I would ask you guys! I'm trying to use an Arduino Uno to control a PC fan that has 4 pins with pwm. I found loads of ways to control it with NPN Transistors, but my fan has a wire/pin just for pwm control and so I would prefer to use just that instead of having to control the entire fan's power supply.

I've already sorted the software side of things its just the hardware that's confusing me. Should I add a resistor or a diode (or both) onto the pwm wire for protection? (if so what kinds?) and do I need to ground the fan on the Arduino ground seen as I'm sending out power through the Arduino?

thank you in advance, any help is very much appreciated (and I apologize if I haven't been clear enough).

I am having the same problem… I have hooked up the fan as per std and I can’t get the Arduino to control the speed. I can read the tach. Not trying to hack the thread!! I am just pulling out my hair! :slight_smile:

Black: GND
Yellow: 12+
Green: Tach
Blue: PWM


This sketch use PID controller to control PC fan speed and display result on a LCD.
Setpoint for speed is read from a potentiometer. Data also send thru serial to PC 
running a real-time graph under Processing software.

Author - Bro_Az August 2010.

  The circuit:
* LCD RS pin to digital pin 7
* LCD Enable pin to digital pin 6
* LCD D4 pin to digital pin 5
* LCD D5 pin to digital pin 4
* LCD D6 pin to digital pin 3
* LCD D7 pin to digital pin 2
* ends to +5V and ground
* 10k pot wiper to LCD VO pin (pin 3)


#include <LiquidCrystal.h>

// If this is defined it prints out the FPS that we can send a
// complete set of data over the serial port.
//#define CHECK_FPS
int backLight = 13; 
int tachPin = 6;   // fan tachometer connected to digital pin 8
int fanPin = 8;  // fan drive connected to digital pin 11
int sensorPin = A2; // analog input 0 for the potentiometer

int sensorValue;  // variable to store the value coming from the sensor
unsigned long duration; // time duration between tachometer pulses
int max_rpm=4800; // max rpm at 100% PWM output
int setpoint; // setpoint in %
int speed; // fan speed in %
float rpm; // fan speed in RPM

int error; 
int last_error=0;
int diff;
float integ=0;
float kp=1.0; // proportional gain
float ki=0.5; // integral gain
float kd=0.05; // derivative gain
int out; // PID controller output

boolean auto_manual=1; // setpoint manual or from pot
int manual_setpoint=77;
int sampletime=40; // Number of sample to average-out sampling values
unsigned long val; // Working variable to hold the accumulated value

unsigned int an0=0; // Variables for sending data to PC
unsigned int an1=0;
unsigned int an2=0;
unsigned int an3=0;
unsigned int an4=0;
unsigned int an5=0;
unsigned int offset=2;

// initialize the LCD library routine with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup() {
lcd.begin(16, 2); // set up the LCD's number of rows and columns:
 pinMode(backLight, OUTPUT);
  digitalWrite(backLight, HIGH);
digitalWrite(tachPin, HIGH);  // turn on pull-up resistor 
Serial.begin(9600); // Serial port baud rate

void loop() {
unsigned int startTag = 0xDEAD;  // Analog port maxes at 1023 so this is a safe termination value
                                 // for sending values to PC.
#ifdef CHECK_FPS  
  unsigned long startTime, endTime;
  startTime = millis();


for (int i=0;i<sampletime;i++) {
sensorValue = analogRead(sensorPin); // Read the value from the pot. AnalogRead values go from 0 to 1023.

if (auto_manual) {
setpoint=float(sensorValue)*100/1024;  // convert to percentage
else {


for (int i = 0; i < sampletime; i++) { // Average out pulseIn time reading in microseconds
    duration = duration+pulseIn(tachPin, HIGH);   
    rpm=600000000/float(duration); // in RPM
    speed=rpm/max_rpm*100; // convert to percentage
    if (out<0) {out=0;integ=0;} //min limit, anti-reset wind-up
    if (out>255) {out=255;integ=integ-error;} //max limit, anti-reset wind-up
    analogWrite(fanPin, out);  // analogWrite values from 0 to 255, PWM output
    lcd.setCursor(0, 0); // Set for first line
    lcd.print(int(rpm)); // displays RPM value
    lcd.print(" RPM CO=");
    lcd.print(out);// displays Controller Output value
    lcd.print("   ");
    lcd.setCursor(0, 1); //set for second line
    lcd.print(setpoint);// displays setpoint value
    lcd.print("% ");
    lcd.print("PV=");// displays PV value
    lcd.print("%   ");     
    if (auto_manual) { // if set-point in Auto, display "A"
      lcd.setCursor(15, 1);
    else {
     lcd.setCursor(15, 1);// else display "M" for Manual
    an0=setpoint*9+offset; // scaling for graph on PC
  Serial.write( (unsigned byte*)&startTag, 2); // Send serial data to PC
  Serial.write((unsigned byte*)&an0, 2);
  Serial.write((unsigned byte*)&an1, 2);
  Serial.write((unsigned byte*)&an2, 2);
  Serial.write((unsigned byte*)&an3, 2);
  Serial.write((unsigned byte*)&an4, 2);
  Serial.write((unsigned byte*)&an5, 2);
#ifdef CHECK_FPS  
  endTime = millis();
  Serial.print(" - FPS: ");
  Serial.println(1.f / (endTime-startTime) * 1000);

missisipihipi: Should I add a resistor or a diode (or both) onto the pwm wire for protection? (if so what kinds?) and do I need to ground the fan on the Arduino ground seen as I'm sending out power through the Arduino?

To answer most of your questions and possible future ones look here and here

i cant understand this line in the code

rpm=600000000/float(duration); // in RPM

can somebody explain why 600000000 ???

The duration is measured in microseconds. There are 60000000 microseconds in a minute. It looks like it multiplies it up to make later calculation easier.

The PWM input on the fan is intended to be driven from an open-collector or open-drain output. So the easiest way to drive it is to use an NPN transistor or opto isolator - not to switch the fan power supply but to drive the PWM input. See attached schematics

The Intel PWM fan spec says the PWM frequency should be 25KHz. See the post by Riva for suitable code to generate a 25KHz PWM signal. If you use an opto isolator, you need to connect a resistor between base and emitter of the receiver, otherwise it won’t switch off fast enough to pass a 25KHz signal.