The Raspberry Pi 4 Model B runs significantly hotter than its predecessors, often throttling the BCM2711 SoC at 80°C under sustained load. To fix this, you need active cooling. The direct answer for Raspberry Pi 4 fan pins is: use Physical Pin 2 (5V) for power, Physical Pin 6 (GND) for ground, and Physical Pin 12 (GPIO 18) for hardware PWM control.
However, you cannot wire a standard 5V fan directly to a GPIO pin. The Pi's 3.3V GPIO pins are rated for a maximum of 16mA per pin (and 50mA total across all banks). A typical 30mm 5V DC fan draws between 100mA and 200mA. Connecting it directly will instantly fry the GPIO bank or the SoC itself. Instead, we use a transistor switching circuit to let the low-current GPIO pin control the high-current 5V fan circuit.
Component Specifications and Power Limits
Before stripping wires, verify your components against this spec sheet. This build targets the Raspberry Pi 4 Model B (Rev 1.4 or 1.5, 4GB/8GB variants) running Raspberry Pi OS (64-bit, Bookworm). The shift to Bookworm is critical because it replaces the legacy RPi.GPIO library with lgpio under the hood, which changes how we handle Python dependencies.
| Component | Specification | Role & Bench Notes |
|---|---|---|
| Raspberry Pi 4 Model B | Rev 1.4 / 1.5, 4GB+ RAM | Target board. Ensure your USB-C PSU can deliver a full 3A (15W) to prevent brownouts when the fan spins up. |
| 5V DC Fan | 30x30x7mm, 100-200mA | 2-wire brushless. Avoid 12V fans; the Pi's 5V rail cannot step-up without a boost converter. |
| 2N2222 Transistor | NPN BJT, 800mA max Ic | Acts as the low-side switch. Handles the fan's current draw safely with massive headroom. |
| 1kΩ Resistor | 1/4W, 5% tolerance | Limits base current to ~3.3mA, keeping the GPIO pin well within its 16mA safety limit. |
| 1N4007 Diode | 1A, 1000V PIV | Flyback protection. Absorbs inductive voltage spikes when the fan motor switches off. |
Raspberry Pi 4 Fan Pin Mapping
We use BCM (Broadcom) numbering in the software, but physical pin numbers on the bench. GPIO 18 is chosen specifically because it is one of the few pins on the Pi 4 that supports hardware PWM (PWM0). Software PWM on other pins can cause jitter and audible motor whine.
| BCM GPIO | Physical Pin | Function | Wiring Destination |
|---|---|---|---|
| N/A (5V) | 2 (or 4) | 5V Power Rail | Fan Red Wire (+) & Diode Cathode (Stripe) |
| N/A (GND) | 6 (or 9) | Ground Return | 2N2222 Emitter & Pi GND |
| 18 | 12 | Hardware PWM0 | 1kΩ Resistor -> 2N2222 Base |
Step-by-Step Wiring Procedure
- Prep the Transistor: Place the 2N2222 on your breadboard. With the flat side facing you, the pins are Emitter (left), Base (middle), Collector (right).
- Base Resistor: Connect one leg of the 1kΩ resistor to the Base pin. Connect the other leg to Physical Pin 12 (GPIO 18) on the Pi.
- Ground the Emitter: Wire the Emitter pin directly to Physical Pin 6 (GND) on the Pi.
- Fan Negative: Connect the fan's black (negative) wire to the Collector pin of the transistor.
- Fan Positive: Connect the fan's red (positive) wire to Physical Pin 2 (5V) on the Pi.
- Flyback Diode (Crucial): Place the 1N4007 diode across the fan's terminals. The silver stripe (cathode) must point toward the 5V red wire, and the anode toward the black wire. Skipping this will eventually kill your transistor due to inductive kickback.
PWM Control Code (Bookworm Compatible)
This Python script uses gpiozero, which natively supports the lgpio backend required by Raspberry Pi OS Bookworm. It reads the SoC temperature and scales the fan speed proportionally between 45°C and 70°C.
import sys
import time
import logging
from gpiozero import PWMOutputDevice, CPUTemperature
from signal import pause
# --- Configuration ---
FAN_PIN = 18 # BCM 18 / Physical Pin 12
MIN_TEMP = 45 # Temp (C) where fan starts
MAX_TEMP = 70 # Temp (C) where fan hits 100%
PWM_FREQ = 25000 # 25kHz avoids audible motor whine
# Setup logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
def map_temp_to_duty(temp, min_t, max_t):
"""Maps temperature to a 0.0 - 1.0 PWM duty cycle."""
if temp <= min_t:
return 0.0
if temp >= max_t:
return 1.0
return (temp - min_t) / (max_t - min_t)
try:
# Initialize hardware
fan = PWMOutputDevice(FAN_PIN, frequency=PWM_FREQ)
cpu = CPUTemperature()
logging.info(f"Fan control started on GPIO {FAN_PIN}.")
while True:
current_temp = cpu.temperature
duty_cycle = map_temp_to_duty(current_temp, MIN_TEMP, MAX_TEMP)
# Ensure minimum starting voltage if duty is > 0 (overcome stall torque)
if 0 < duty_cycle < 0.3:
duty_cycle = 0.3
fan.value = duty_cycle
logging.info(f"CPU: {current_temp:.1f}C | Fan Duty: {duty_cycle*100:.0f}%")
time.sleep(5)
except KeyboardInterrupt:
logging.info("Manual interrupt received. Shutting down.")
except Exception as e:
logging.error(f"Fatal error: {e}")
finally:
# Cleanup ensures the fan turns off and GPIO is released
if 'fan' in locals():
fan.off()
fan.close()
logging.info("GPIO cleaned up. Exiting.")
Debugging: First Three Things to Check When It Fails
Embedded debugging is about isolating the failure domain: mechanical, electrical, or software. If your fan isn't spinning or the script crashes, check these three things in order.
1. Exact Error: gpiozero.exc.BadPinFactory: Unable to load any default pin factory!
The Cause: You are running this in a Python virtual environment on Raspberry Pi OS Bookworm, but the lgpio C-extension isn't installed. Bookworm dropped RPi.GPIO from the default image.
The Fix: Activate your virtual environment and install the correct backend:
pip install rpi-lgpio gpiozero
2. Symptom: Fan runs at 100% and ignores PWM / Emits a high-pitched whine
The Cause: You are using a cheap 2-wire brushless fan with internal commutation electronics that get confused by low-frequency PWM, or you are using a software-PWM pin (like GPIO 17) instead of hardware-PWM (GPIO 18).
The Fix: Verify your wiring is on Physical Pin 12 (BCM 18). Ensure the PWM_FREQ variable in the code is set to 25000 (25kHz). Frequencies below 20kHz cause acoustic noise; frequencies above 30kHz can cause the 2N2222 to switch inefficiently without a dedicated gate driver.
3. Symptom: Pi reboots randomly when the fan spins up to 100%
The Cause: Voltage sag on the 5V rail. When the fan ramps up, it draws a startup surge (often 2x its rated current). If your USB-C power supply is marginal (e.g., a cheap phone charger delivering only 2A), the Pi's brownout detector trips and reboots the board.
The Fix: Use the official Raspberry Pi 27W USB-C Power Supply. As a bench workaround, solder a 100µF electrolytic capacitor across the fan's 5V and GND terminals to supply the localized surge current.
Extending or Simplifying the Build
Simplify: The Press-Fit Alternative
If breadboarding a transistor circuit feels like overkill, buy the Pimoroni Fan SHIM (~$12). It presses directly onto the Pi 4's GPIO header, uses a dedicated friction-fit 5V fan, and includes an RGB LED and tactile button. It bypasses the need for resistors and flyback diodes entirely, though you sacrifice the ability to easily swap the fan for a larger, quieter 40mm model.
Extend: Closed-Loop PID & OLED
To turn this into a true thermal management system, add an SSD1306 128x64 I2C OLED display. Wire SDA to GPIO 2 (Pin 3) and SCL to GPIO 3 (Pin 5). Use the luma.oled Python library to render a real-time graph of the SoC temperature and the calculated PWM duty cycle. For advanced control, replace the linear mapping function with a PID controller using the simple-pid library to eliminate temperature oscillation.
By respecting the current limits of the Raspberry Pi 4 fan pins and utilizing hardware PWM via a transistor switch, you guarantee silent, reliable cooling that extends the lifespan of your board without risking silicon damage.






