Introduction: PWM Regulated Fan Based on CPU Temperature for Raspberry Pi
Many cases for Raspberry Pi come with a little 5V fan in order to help cooling the CPU. However, these fans are usually pretty noisy and many people plug it on the 3V3 pin to reduce the noise. These fans are usually rated for 200mA which is pretty high for the 3V3 regulator on the RPi. This project will teach you how to regulate the fan speed based on CPU temperature. Unlike most of tutorials covering this subject, we won't only turn on or off the fan, but will control its speed like it's done on mainstream PC, using Python.
Step 1: Parts Needed
For this project, we will use only a few components that are usually included in electronics kits for hobbyist that you can find on Amazon, like this one.
- Raspberry Pi running Raspbian (but should work with other distribs).
- 5V Fan (but a 12V fan could be used with an adapted transistor and a 12V power supply).
- NPN transistor that supports at least 300mA, like a 2N2222A.
- 1K resistor.
- 1 diode.
Optional, to put the components inside the case (but not done yet):
- A little piece of protoboard, to solder the components.
- Large heat shrink, to protect the board.
For those who might not want to build there own circuit board, you can buy a ready-to-use board made by Jeremy Cook on tindie.com: https://www.tindie.com/products/jeremycook/ez-fan2-tiny-raspberry-pi-fan-controller/
Step 2: Electrical Connections
Resistor can be plug in either way, but be careful about transistor's and diode's direction. Diode's cathode must be connected to the +5V (red) wire, and anode must be connected to the GND (black) wire. Check your transistor doc for Emitter, Base and Collector pins. Fan's ground must be connected to the Collector, and Rpi's ground must be connected to Emitter.
In order to control the fan, we need to use a transistor that will be used inopen collector configuration. By doing this, we have a switch that will connect or disconnect the ground wire from the fan to the ground of the raspberry pi.
A NPN BJT transistor conducts depending on the current that flows in its gate. The current that will be allowed to flow from the collector (C) to the emitter (E) is:
Ic = B * Ib
Ic is the current that flows through the collector the emitter, Ib is the current that flows through the base to the emitter, and B (beta) is a value depending on each transistor. We approximate B = 100.
As our fan is rated as 200mA, we need at least 2mA through the base of the transistor. The tension between the base and the emitter (Vbe) is considered constant and Vbe = 0,7V. This means that when the GPIO is on, we have 3.3 - 0.7 = 2.6V at the resistor. To have 2mA through that resistor, we need a resistor of, maximum, 2.6 / 0.002 = 1300 ohm. We use a resistor of 1000 ohm to simplify and keep a margin of error. We will have 2.6mA through the GPIO pin which is totally safe.
As a fan is basically an electrical motor, it is an inductive charge. This means when the transistor stops conducting, the current in the fan will continue flowing as an inductive charge tries to keep the current constant. This would results in a high voltage on the ground pin of the fan and could damage the transistor. That's why we need a diode in parallel with the fan which will make the current flow constantly through the motor. This type of diode setup is called a Flywheel diode
Step 3: Program to Control the Fan Speed
To control fan speed, we use a software PWM signal from the RPi.GPIO library. A PWM Signal is well adapted to drive electric motors, as their reaction time is very high compared to the PWM frequency.
Use the calib_fan.py program to find the FAN_MIN value by running in the terminal:
python calib_fan.py
Check several values between 0 and 100% (should be around 20%) and see what is the minimum value for your fan to turn on.
You can change the correspondence between temperature and fan speed at the beginning of the code. There must be as many tempSteps as speedSteps values. This is the method that is generally used in PC motherboards, moving points on a Temp / Speed 2-axis graph.
Attachments
Step 4: Run the Program at Startup
To run the program automatically at startup, I made a bash script where I put all the programs I want to launch, and then I launch this bash script at startup with rc.locale
- Create a directory /home/pi/Scripts/ and place the fan_ctrl.py file inside that directory.
- In the same directory, create a file named launcher.sh and copy the script bellow.
- Edit the /etc/rc.locale file and add a new line before the "exit 0": sudo sh '/home/pi/Scripts/launcher.sh'
launcher.sh script:
#!/bin/sh
#launcher.sh # navigate to home directory, then to this directory, then execute python script, then back home
locale
cd /
cd /home/pi/Scripts/
sudo python3 ./fan_ctrl.py &
cd /
If you want to use it with OSMC for example, you need to start it as a service with systemd.
- Download the fanctrl.service file.
- Check the path to your python file.
- Place fanctrl.service in /lib/systemd/system.
- Finally, enable the service with sudo systemctl enable fanctrl.service.
This method is safer, as the program will be automatically restarted if killed by the user or the system.
Attachments

Participated in the
Raspberry Pi Contest 2017

Participated in the
First Time Author Contest 2018
123 Comments
2 years ago
I built this and it runs - although I like the look of the protoboard version posted in the images.
To get the systemd.service running on Raspberry Pi OS (64-bit in my case, but shouldn't matter), the fanctrl.service (in /lib/systemd/system/fanctrl.service) needs to be modified as follows:
[Unit]
Description=PWM Fan Control for Raspberry Pi
After=ssh.service
[Service]
Type=simple
User=pi
ExecStart=/usr/bin/python3 /home/pi/Scripts/fan_ctrl.py
Restart=always
[Install]
WantedBy=default.target
Start with:
sudo systemctl start fanctrl.service
Check it is running (you should see it listed) with:
systemctl | grep fan
fanctrl.service loaded active running PWM Fan Control for Raspberry Pi
Permanently enable with:
sudo systemctl enable fanctrl.service
Test with a heavy load - I used Java Minecraft and it turned on during map generation and turned off after quitting, but it never became loud (although I have not overclocked this Pi).
Reply 7 months ago
fanctrl.service not starting, can anyone help?
showing this error:
Service has more than one ExecStart = setting, which is only allowed for Type=oneshot services. Refusing.
Question 7 months ago
fanctrl.service not starting, can anyone help?
showing this error:
Service has more than one ExecStart = setting, which is only allowed for Type=oneshot services. Refusing.
i need help
Tip 1 year ago
Works like a charm! I added a capacitor in parallel with the fan to smooth the PWM signal and make the fan quieter.
Reply 1 year ago
Hi! Could you post the shematic with the capacitor and its capacity? Thank you!
5 years ago
Your code is nice, it helped me achieve what I wanted to do. from what I remember from high school computer class is most fans have a diode built in, so im gonna bank on that fact and not add a 30cent diode. the N2222, should on paper act as a resistor (I MIGHT BE WRONG), either way, the fans only gonna draw the amperage it needs. and thats lower then the gpio capability.
also, i removed the use for launcher.sh, can you tell me why you used it when
sudo python /home/pi/Scripts/fan_ctrl.py &
would probably do the same thing.
i would really be interested in some criticism on my setup.
thank you and ultimatly, good job.
Reply 5 years ago
It's very easy to check if your fan does have a diode built-in or not, with a simple multimeter. I doubt it does.
Reply 1 year ago
Hi, thanks for your instructions. I'm beginner on raspberry and I learnt a lot with this. I have same question with Matt. Why do you need to create Launcher.sh? I also think that "sudo python /home/pi/Scripts/fan_ctrl.py &" can do same thing, isn't it true?
Question 1 year ago on Step 1
Edit:
My bad. It was a soldering mistake. By the way, I used nodered along with the flow at https://forum.openmarine.net/showthread.php?tid=1093 to control the fan.
I am trying to use this with a KN2222 transistor. When I connect the circuit with 5V from the pi, the pi does not turn on. As far as I can tell, the KN2222 is pin equivalent to 2N2222. Can someone help me here?
Thanks
Question 1 year ago
Can you please let me know the python code to interface dht11 sensor and dc motor to raspberry pi and write a python program to perform following using PWM technique
1.Rotate the motor with 20% speed if temperature is between 20 to 30 degree Celsius
2.1.Rotate the motor with 60% speed if temperature is between 31 to 40 degree Celsius
3.1.Rotate the motor with 90% speed if temperature is between 41 to 50 degree Celsius
1 year ago
Can you please let me know the python code to interface dht11 sensor and dc motor to raspberry pi and write a python program to perform following using PWM technique
1.Rotate the motor with 20% speed if temperature is between 20 to 30 degree Celsius
2.1.Rotate the motor with 60% speed if temperature is between 31 to 40 degree Celsius
3.1.Rotate the motor with 90% speed if temperature is between 41 to 50 degree Celsius
Question 2 years ago
I followed all directions trying to do the same with my raspberry pi retropie. On test bench it worked well (using debian), then I did the soldering and tested it again (on debian in Thonny IDE). Then I followed the steps to try and make it auto-launch and when I try to run the program by using 'python3 fan_ctrl.py' it crashes the entire system and reboots the pi. Any ideas on why that might happen?
2 years ago
Hello. The first value of the array speedSteps should be FAN_MIN, to avoid undervoltage to the fan. I corrected and it works ok.
Thanks for your work!
2 years ago
Hello
im really begginer can someone help me with code?
i want the fan works like this
temp 45 degree 40% speed
temp 55 degree 60% speed
temp 65 degree 100% speed
i use the pin 36
thanks
Reply 2 years ago
#!/usr/bin/python
# -*- coding: utf-8 -*-
import RPi.GPIO as GPIO
import time
import sys
GPIO.setwarnings(False)
# Configuration
FAN_PIN = 36 # Physical pin used to drive transistor's base
WAIT_TIME = 1 # [s] Time to wait between each refresh
FAN_MIN = 40 # [%] Fan minimum speed.
PWM_FREQ = 25 # [Hz] Change this value if fan has strange behavior
# Configurable temperature and fan speed steps
tempSteps = [45, 55, 65] # [°C]
speedSteps = [40, 60, 100] # [%]
# Fan speed will change only of the difference of temperature is higher than hysteresis
hyst = 1
# Setup GPIO pin
GPIO.setmode(GPIO.BOARD)
GPIO.setup(FAN_PIN, GPIO.OUT, initial=GPIO.LOW)
fan = GPIO.PWM(FAN_PIN, PWM_FREQ)
fan.start(0)
i = 0
cpuTemp = 0
fanSpeed = 0
cpuTempOld = 0
fanSpeedOld = 0
# We must set a speed value for each temperature step
if len(speedSteps) != len(tempSteps):
print("Numbers of temp steps and speed steps are different")
exit(0)
try:
while 1:
# Read CPU temperature
cpuTempFile = open("/sys/class/thermal/thermal_zone0/temp", "r")
cpuTemp = float(cpuTempFile.read()) / 1000
cpuTempFile.close()
# Calculate desired fan speed
if abs(cpuTemp - cpuTempOld) > hyst:
# Below first value, fan will run at min speed.
if cpuTemp < tempSteps[0]:
fanSpeed = speedSteps[0]
# Above last value, fan will run at max speed
elif cpuTemp >= tempSteps[len(tempSteps) - 1]:
fanSpeed = speedSteps[len(tempSteps) - 1]
# If temperature is between 2 steps, fan speed is calculated by linear interpolation
else:
for i in range(0, len(tempSteps) - 1):
if (cpuTemp >= tempSteps[i]) and (cpuTemp < tempSteps[i + 1]):
fanSpeed = round((speedSteps[i + 1] - speedSteps[i])
/ (tempSteps[i + 1] - tempSteps[i])
* (cpuTemp - tempSteps[i])
+ speedSteps[i], 1)
if fanSpeed != fanSpeedOld:
if (fanSpeed != fanSpeedOld
and (fanSpeed >= FAN_MIN or fanSpeed == 0)):
fan.ChangeDutyCycle(fanSpeed)
fanSpeedOld = fanSpeed
cpuTempOld = cpuTemp
# Wait until next refresh
time.sleep(WAIT_TIME)
# If a keyboard interrupt occurs (ctrl + c), the GPIO is set to 0 and the program exits.
except KeyboardInterrupt:
print("Fan ctrl interrupted by keyboard")
GPIO.cleanup()
sys.exit()
Reply 2 years ago
thanks for replay
i have twister os can you help me how to run the fan on it?
rubashkaic5@gmail.com
Question 2 years ago
I need help with my setup. My fan is working fine on 5V and Gnd, but
with this setup, it never starts spinning. I can light and dim a LED
with the same setup, but it's like power is not enough for the fan to
start: Yesterday with a different setup (something was wrong in it
tough), the fan started to slowly move, but not enough to spin on 100
speed with calib_fan.py. Pushing it a little with a finger was enough to
get it to spin, but that's all. Again, that was with a different setup;
right now nothing is happening whatever speed I input.
Components
are: 1000 ohm resistor, 2n222 transistor and 1N4001 diode. I double
checked the datasheets to make sure everything is plugged right. I'm
using an updated Raspberry Pi 4b.
So if the circuit is working,
the code is working, the components are working... I'm not sure why
nothing is happening to the fan.
Thanks in advance!
Answer 2 years ago
Hi trippy-tux
Please check; is your Diode polarity correct? (it's difficult to tell for sure in your photo)
The cathode (silver band) should be on the +5v rail in this instance.
The diode should be 'reverse biased' because it is acting as a 'flywheel diode' which effectively controls the back EMF from the fan motor windings when it switches off or, in this case, when the Transistor pulses at the set frequency within the coding.
Reply 2 years ago
It is not correct on the photo indeed. But it did not solve the problem.
Turns out 3 out of 5 new transistors I bought were faulty. Is this something normal? Buying dead components? I'm working with IC for some exercises and all my new IC except one are working. It is either me or the components... But it's working now!
Thanks for the reply; that made me go back and make it work!
2 years ago
Great guide, Thanks :D
I used a different transistor, a 9013 (because it's all I had available) but I adjusted the calculations according to it's datasheet and used your formula as a guide.
Really happy with it.