Project Overview & Difficulty Rating

Controlling a 12V brushless DC fan with a 5V microcontroller requires bridging the voltage and current gap safely. An Arduino GPIO pin can source a maximum of 20mA at 5V, while a standard 120mm PC fan draws between 100mA and 300mA at 12V. Connecting a fan directly to an Arduino pin will instantly destroy the ATmega328P silicon.

This guide walks through building a robust arduino fan controller using a logic-level N-channel MOSFET as a low-side switch, pulse-width modulation (PWM) for speed control, and a flyback diode to protect the circuit from inductive voltage spikes.

Project Spec Sheet
Target Board: Arduino Uno R3 (ATmega328P)
Difficulty: Intermediate (Requires understanding of inductive loads and MOSFET gate thresholds)
Estimated Time: 45 minutes
Estimated Cost: $15 - $22 (excluding power supply)

Parts List & Spec Sheet

Selecting the right MOSFET is where most hobbyists fail. You must use a logic-level MOSFET, not a standard one.

Component Exact Part / Variant Why This Part? Approx. Cost
Microcontroller Arduino Uno R3 (DIP-28 ATmega328P) Hardware PWM on Pin 9 (Timer1); 5V logic output. $12.00
MOSFET IRLZ44N (Logic-Level N-Channel) VGS(th) is 1.0V-2.0V. Fully turns on at 5V gate drive. Do not use the IRF520, which requires 10V+ to fully saturate and will overheat on an Uno. $1.50
Flyback Diode 1N4007 (1A, 1000V) Clamps inductive kickback from the fan motor coils when the MOSFET switches off. $0.10
Gate Resistor 100Ω (1/4W Carbon Film) Prevents high-frequency ringing and limits inrush current from the GPIO pin into the gate capacitance. $0.05
Pulldown Resistor 10kΩ (1/4W Carbon Film) Keeps the MOSFET gate LOW during Arduino boot-up, preventing the fan from spinning at full speed while pins are floating. $0.05
Fan Noctua NF-A14 or generic 12V 2-pin/3-pin Standard 12V brushless DC motor. 4-pin PWM fans have internal controllers and require a different wiring approach. $15.00

Pin Mapping & Wiring Steps

Follow these steps exactly. Working with inductive loads means a single misplaced wire can fry your microcontroller via voltage spike.

⚠️ Safety Callout: Ensure your 12V power supply is disconnected while wiring the breadboard. Double-check the flyback diode orientation before applying power.
Arduino / Component Connection Target Notes
Arduino Pin 9 (PWM)100Ω Resistor (Lead 1)Gate drive signal
100Ω Resistor (Lead 2)MOSFET Gate (Pin 1)IRLZ44N pinout: Gate, Drain, Source
MOSFET Gate (Pin 1)10kΩ Resistor (Lead 1)Pulldown network
10kΩ Resistor (Lead 2)MOSFET Source (Pin 3) & GNDTies gate to ground when Uno is off
MOSFET Drain (Pin 2)Fan Negative (Black Wire)Low-side switching
Fan Positive (Red Wire)12V Power Supply (+)Do NOT use Arduino 5V/VIN
1N4007 Cathode (Stripe)12V Power Supply (+)Must point TOWARD the positive rail
1N4007 AnodeMOSFET Drain (Pin 2)Parallel to the fan motor
MOSFET Source (Pin 3)12V Power Supply (-) & Arduino GNDCommon ground is mandatory

Complete Compilable Code (Arduino Uno R3)

This sketch targets the Arduino Uno R3. It uses hardware PWM on Pin 9 via the Arduino analogWrite() function. It includes serial input parsing with strict bounds checking to prevent out-of-range values from causing erratic behavior, and enforces a minimum PWM threshold to overcome the fan's static friction.

/*
 * Arduino Fan Speed Controller via Serial Monitor
 * Target: Arduino Uno R3 (ATmega328P)
 * Hardware: IRLZ44N MOSFET, 12V DC Fan on Pin 9
 */

#define FAN_PWM_PIN 9
#define FAN_MIN_PWM 60  // Most 12V fans stall below 25% duty cycle
#define FAN_MAX_PWM 255

void setup() {
  pinMode(FAN_PWM_PIN, OUTPUT);
  digitalWrite(FAN_PWM_PIN, LOW); // Ensure fan is off at boot
  Serial.begin(9600);
  Serial.println(F("Arduino Fan Controller Ready."));
  Serial.println(F("Enter speed 0-255 (0=OFF, 60-255=ON):"));
}

void loop() {
  if (Serial.available() > 0) {
    String input = Serial.readStringUntil('\n');
    input.trim();
    
    // Error handling: Check if input is a valid integer
    bool isValidNumber = true;
    for (unsigned int i = 0; i < input.length(); i++) {
      if (!isDigit(input[i])) {
        isValidNumber = false;
        break;
      }
    }
    
    if (isValidNumber && input.length() > 0) {
      int requestedSpeed = input.toInt();
      
      // Bounds checking and stall prevention
      if (requestedSpeed == 0) {
        analogWrite(FAN_PWM_PIN, 0);
        Serial.println(F("Fan OFF."));
      } 
      else if (requestedSpeed >= 1 && requestedSpeed < FAN_MIN_PWM) {
        analogWrite(FAN_PWM_PIN, FAN_MIN_PWM);
        Serial.print(F("Speed clamped to minimum start threshold: "));
        Serial.println(FAN_MIN_PWM);
      } 
      else if (requestedSpeed >= FAN_MIN_PWM && requestedSpeed <= FAN_MAX_PWM) {
        analogWrite(FAN_PWM_PIN, requestedSpeed);
        Serial.print(F("Fan speed set to: "));
        Serial.println(requestedSpeed);
      } 
      else {
        Serial.println(F("Error: Value exceeds 255. Enter 0-255."));
      }
    } else {
      Serial.println(F("Error: Invalid input. Enter a number 0-255."));
    }
  }
}

Debugging: Compile Errors & Hardware Faults

When building motor control circuits, you will inevitably run into both software and hardware faults. Here is how to diagnose the most common issues.

Compile Error: error: 'analogWrite' was not declared in this scope

If you see this exact error string when compiling, it is almost always caused by one of two things:

  1. Wrong Board Selected: You are trying to compile this exact code for an ESP32 or Raspberry Pi Pico. The ESP32 does not support analogWrite() natively in older core versions; it requires the ledc PWM API. Ensure your IDE is set to 'Arduino Uno'.
  2. Missing Pin Definition: You deleted or altered the #define FAN_PWM_PIN 9 line, or placed the analogWrite() call outside of a valid function block.

Hardware Fault: Fan Clicking, Stalling, or Whining

If the code compiles but the fan clicks rhythmically without spinning, or emits a high-pitched squeal, check these first three things:

  1. PWM Frequency vs. Motor Coil Whine: The Uno's default PWM frequency on Pin 9 is ~490Hz. Brushless DC fans can resonate at this frequency. If the whine is unbearable, you must use a 4-pin PWM fan (which expects a 25kHz logic signal on the blue wire) or use a library to shift the Uno's Timer1 to 31kHz.
  2. Startup Stall Threshold: Fans require more torque to start spinning than to keep spinning. If your code sends a PWM value of 40, the fan may stall. The code above handles this by clamping any non-zero value below 60 to the FAN_MIN_PWM threshold.
  3. Floating Gate / Missing Pulldown: If the fan spins to 100% speed the moment you plug in the USB cable (before the sketch finishes booting), your 10kΩ pulldown resistor is missing or wired incorrectly. The ATmega328P pins float during boot, partially turning on the MOSFET.
Pro-Tip on Inductive Kickback: Never omit the 1N4007 flyback diode. When the MOSFET turns off, the fan's magnetic field collapses, generating a reverse voltage spike that can exceed 50V. This will punch through the MOSFET's drain-source junction and backfeed into your Arduino. For a deeper dive on the physics of this, review the All About Circuits guide on inductive kickback.

Extending and Simplifying the Build

Depending on your end goal, you may want to strip this project down to its bare essentials or scale it up into a closed-loop environmental controller.

How to Simplify

If you just need a fan to turn on when a device powers up, delete the Serial logic entirely. Replace the loop() contents with a hardcoded analogWrite(FAN_PWM_PIN, 180);. This removes serial buffer overhead and guarantees a fixed, quiet cooling speed without needing a PC connected.

How to Extend

To turn this into a smart thermal manager:

  • Add a Sensor: Wire a DHT22 or DS18B20 temperature sensor to Pin 2.
  • Implement PID Control: Use the Arduino PID Library to map the temperature error (Setpoint - Current Temp) to the 60-255 PWM output range. This prevents the fan from aggressively ramping up and down (hunting) when the temperature hovers near your threshold.
  • Add Tachometer Feedback: If using a 3-pin or 4-pin fan, connect the yellow tachometer wire to Pin 2. Use attachInterrupt(digitalPinToInterrupt(2), rpmCalc, RISING) to read the actual RPM and verify the fan hasn't physically stalled due to dust buildup.

Frequently Asked Questions

Can I power an Arduino fan directly from the 5V pin?

No. The Arduino Uno's onboard 5V linear regulator (typically an NCP1117 or LM7805) can only safely supply about 500mA to 800mA total, and that current is shared with the ATmega328P and any attached shields. A 12V PC fan requires 12V, not 5V. Even if you step down a 12V fan to run on 5V (which will run it at a fraction of its rated speed and likely stall), the current draw during motor startup will brownout the microcontroller, causing random resets and corrupted EEPROM data. Always use a dedicated 12V power supply for the fan motor.

How to control a 4-pin PWM fan with an Arduino?

A 4-pin fan (Black=GND, Yellow=12V, Green=Tach, Blue=PWM) has an internal controller. You wire the Black and Yellow wires directly to your 12V supply. The Blue wire connects directly to an Arduino PWM pin (like Pin 9) without needing a MOSFET. However, 4-pin fans expect a 25kHz PWM signal and an open-drain output. While the Uno's 5V push-pull 490Hz signal will often 'work' in a pinch, it violates the Intel 4-Wire PWM spec and can cause audible noise or erratic RPM control. For proper 4-pin control, use a 25kHz timer library or an ESP32 which can natively configure PWM frequencies via the LEDC peripheral.

Why is my Arduino fan making a high-pitched whine?

The whine is magnetostriction and coil resonance caused by the ~490Hz default PWM frequency on Arduino Uno Pins 9 and 10. The rapid switching causes the physical copper windings inside the fan motor to vibrate at an audible frequency. To fix this on a 2-pin or 3-pin fan, you must change the hardware timer prescalers to push the PWM frequency above human hearing (e.g., 31,250 Hz). Note that altering Timer1 on the Uno will break the delay() and millis() functions, so plan your code timing accordingly if you modify the registers.