PWM Regulated Fan Based on CPU Temperature for Raspberry Pi

104,903

139

120

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.

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.

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

  1. Create a directory /home/pi/Scripts/ and place the fan_ctrl.py file inside that directory.
  2. In the same directory, create a file named launcher.sh and copy the script bellow.
  3. 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.

  1. Download the fanctrl.service file.
  2. Check the path to your python file.
  3. Place fanctrl.service in /lib/systemd/system.
  4. 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.

Raspberry Pi Contest 2017

Participated in the
Raspberry Pi Contest 2017

First Time Author Contest 2018

Participated in the
First Time Author Contest 2018

28 People Made This Project!

Recommendations

  • 3D Printed Student Design Challenge

    3D Printed Student Design Challenge
  • Tiny Things Speed Challenge

    Tiny Things Speed Challenge
  • Edible Art Challenge

    Edible Art Challenge

120 Comments

0
MattP186
MattP186

3 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.

0
Aerandir14
Aerandir14

Reply 3 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.

0
Bayram
Bayram

Reply 2 months 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?

0
KhurramFHassan
KhurramFHassan

Question 5 months 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

0
Nikita2001
Nikita2001

Question 5 months 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

0
Nikita2001
Nikita2001

5 months 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

0
CaiusHubris
CaiusHubris

Question 6 months 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?

0
CarlosP94
CarlosP94

7 months 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!

0
Selfp1
Selfp1

9 months 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

0
LDMaughan
LDMaughan

Reply 9 months 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()

0
Selfp1
Selfp1

Reply 7 months ago

thanks for replay
i have twister os can you help me how to run the fan on it?
rubashkaic5@gmail.com

0
trippy-tux
trippy-tux

Question 9 months 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!

IMG_20210213_203821.jpg
0
LDMaughan
LDMaughan

Answer 9 months 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.

0
trippy-tux
trippy-tux

Reply 9 months 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!

0
LDMaughan
LDMaughan

9 months 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.

0
itainelken
itainelken

Question 1 year ago

can I use a LED instead of a diode?

0
LDMaughan
LDMaughan

Answer 9 months ago

You [i]could[/i] but an LED doesn't have the same forward & reverse voltage characteristics (usually a lot less than a Diode) and therefore wouldn't offer the best protection against EMF

0
kostas.hellas
kostas.hellas

10 months ago

Nice and neat DIY circuit, thank you for sharing!
...a similar to this design using the Pi-OS standard 2wire Fan control via GPIO #14 is this (no script needed, >Raspberry Pi configuration/ Performance/Fan [Enable]):

Pi_2wire_Fan_via_GPIO.png
0
Stressig
Stressig

Reply 9 months ago

Could I use a 10uF 50v electrolitic capacitor and a 2N2222A transistor for this? Also, what is the purpose of Q1? Is it two diodes? What happened to the protective diode in the original project?
Could I smooth out the PWN signal using the original circuit from the post and just adding R2, C1 and D1 from yours?

Sorry if these are stupid questions, I bought my first breadboard just for this project and I'm still figuring everything out.

0
kostas.hellas
kostas.hellas

Reply 9 months ago

Hi, there are no stupid questions.
"Could I use a 10uF 5v electrolytic capacitor and a 2N2222A transistor for this?" Yes you can.
Q1 is N-Ch mosfet with very low internal resistor, same results as 2N2222
No two diodes, D1 is the only diode; the PWM is just an input sign.
You don't need protection diode for DC brushless motors, they have internal circuit with protection.
"Could I smooth out the PWN signal using the original circuit from the post and just adding R2, C1 and D1 from yours?" of course you can (you don't need the R2, this resistor needed only with FET transistor).
R1 is optional/nice to have (current limiter/protection for the GPIO)
as per my comment this design is just an alternative, mosfet based, to the working design already posted.