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.
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.
| 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) & GND | Ties 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 Anode | MOSFET Drain (Pin 2) | Parallel to the fan motor |
| MOSFET Source (Pin 3) | 12V Power Supply (-) & Arduino GND | Common 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:
- 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 theledcPWM API. Ensure your IDE is set to 'Arduino Uno'. - Missing Pin Definition: You deleted or altered the
#define FAN_PWM_PIN 9line, or placed theanalogWrite()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:
- 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.
- 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_PWMthreshold. - 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.
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 Libraryto 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.






