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:
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
- Create a directory /home/pi/Scripts/ and place the ctrl_fan.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 # navigate to home directory, then to this directory, then execute python script, then back home
sudo python3 ./fan_ctrl.py &
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.