Control Raspberry Pi Fan and Temperature with Python

Automatically Control Your Raspberry Pi Fan (and Temperature) with Python

It's getting hot in here (so hot), so let's just write some code.
Tyler Tyler (258)
Total time: 120 minutes 

Since the Raspberry Pi 4 was released, many have noticed that it can get pretty hot, especially when the CPU is under heavy load.

A Raspberry Pi enthusiast, Jeff Geerling, released a pretty cool video showing how to add a fan to the Raspberry Pi to help keep the temperature under control. That project was pretty great but I wanted to take it to the next level and add the ability to only turn the fan on when needed by monitoring the core temperature. This way, a noisy fan isn't running all the time.

In this guide, I'll cover the whole project from start to finish. First, we'll install the fan onto the official Raspberry Pi case and wire it up so it can be activated by a GPIO pin. Then we'll write some Python code to monitor the temperature and activate the Pi when the temperature reaches a certain threshold.

The fan installation is pretty simple, and we've covered it in more detail elsewhere. In this guide, I'll cover the basics, but if you need more detailed instructions head on over to our guide on installing the fan onto your Raspberry Pi 4.

Drill a hole for the fan

There is plenty of room for the fan as long as you keep it away from the USB ports. See the image for reference. Place a mark on the Pi case where you want the center of the fan to be. Then drill a hole using a 1-1/8" hole saw. I was fortunate enough to have access to a drill press, but if you don't, a handheld drill will work. After the hole is drilled, smooth out the rough edges with sandpaper or a file.

Drill screw holes

With the fan hole drilled, place the fan on the inside of the case, centered in the hole. Then mark the case at the center of each screw hole. For this you can use a center punch, small screwdriver, or pencil. Then, remove the fan and carefully drill the screw holes using a 7/64" drill bit.

Mount the fan

I'm going to add this step here but I actually recommend doing this after the fan is wired up. When you're ready, mount the fan inside the case with the Pi-FAN sticker facing up. Use the included nuts and bolts to secure the fan inside the case.

Raspberry Pi fan controller schematic

In this step, I'll provide a breakdown of the circuit. We're only using a few components: the 5V fan (represented by the big DC motor in the image), a 680Ω resistor, and an NPN transistor (2N2222).

Fan power needs

The transistor is the most interesting piece of this circuit. It's necessary because the fan requires 5V to operate but the Pi's GPIO pins are only capable of supplying 3.3V. GPIO pins could power something small, like an LED, but they shouldn't be used for anything more. Likewise, the Pi's 5V pins are connected directly to the power supply and cannot be controlled via software. Therefore, we need a way to power the fan using the 5V pin, but switch it on and off using a GPIO pin. Enter the transistor.

Transistor

The transistor is an interesting electrical component used to switch or amplify power. In our case, we're using it as a switch. There are many types of resistors, but we're using an NPN transistor. So the "base" pin of the transistor is connected to the BCM 17 (a GPIO pin) with a 680Ω resistor in between. The positive lead on the fan is connected to the 5v pin, and the ground is connect to the "collector" pin in our transistor. Lastly, the "emitter" of the transistor is connected to the ground.

So when pin 17 is switched to HIGH, it will send some voltage to the base of the transistor, which closes the circuit and turns on the fan.

Raspberry Pi fan controller test circuit on the breadboard.

Before soldering anything, it's wise to test the circuit using a breadboard. I will avoid another explanation of the circuit but I hope that, between the previous step and this image, you'll be able to reconstruct the circuit. We won't be able to test the circuit until the software is written, so let's move on to the next step!

I created the fan script and published it to a public Howchoo repo called pi-fan-controller.

The easiest way to install the fan controller scripts is to use our install script. To do so, SSH into your Pi and run:

git clone https://github.com/Howchoo/pi-fan-controller.git

./pi-fan-controller/script/install

This script installs fancontrol.py which monitors the core temperature and controls the fan. Also, it adds a script called fancontrol.sh to /etc/init.d and configures the script to run when the system boots.

Skip this step if you used the install script above.

We're going to need code that continuously monitors the core temperature and turns on the fan when the temperature reaches a certain threshold.

So we'll connect to the Pi via SSH and create a file called fancontrol.py. To create this file, run:

nano fancontrol.py

Add the following to the file, save, and exit:

#!/usr/bin/env python3

import subprocess
import time

from gpiozero import OutputDevice


ON_THRESHOLD = 65  # (degrees Celsius) Fan kicks on at this temperature.
OFF_THRESHOLD = 55  # (degress Celsius) Fan shuts off at this temperature.
SLEEP_INTERVAL = 5  # (seconds) How often we check the core temperature.
GPIO_PIN = 17  # Which GPIO pin you're using to control the fan.


def get_temp():
    """Get the core temperature.
    Run a shell script to get the core temp and parse the output.
    Raises:
        RuntimeError: if response cannot be parsed.
    Returns:
        float: The core temperature in degrees Celsius.
    """
    output = subprocess.run(['vcgencmd', 'measure_temp'], capture_output=True)
    temp_str = output.stdout.decode()
    try:
        return float(temp_str.split('=')[1].split('\'')[0])
    except (IndexError, ValueError):
        raise RuntimeError('Could not parse temperature output.')


if __name__ == '__main__':
    # Validate the on and off thresholds
    if OFF_THRESHOLD >= ON_THRESHOLD:
        raise RuntimeError('OFF_THRESHOLD must be less than ON_THRESHOLD')

    fan = OutputDevice(GPIO_PIN)

    while True:
        temp = get_temp()

        # Start the fan if the temperature has reached the limit and the fan
        # isn't already running.
        # NOTE: `fan.value` returns 1 for "on" and 0 for "off"
        if temp > ON_THRESHOLD and not fan.value:
            fan.on()

        # Stop the fan if the fan is running and the temperature has dropped
        # to 10 degrees below the limit.
        elif fan.value and temp < OFF_THRESHOLD:
            fan.off()

        time.sleep(SLEEP_INTERVAL)

Now we'll move the script to /usr/local/bin, which is the ideal location for scripts that normal users can run. Then we'll make it executable.

sudo mv fancontrol.py /usr/local/bin/
sudo chmod +x /usr/local/bin/fancontrol.py

Skip this step if you used the install script above.

We'll want to run this script when the Pi boots, otherwise it won't do us much good. To do so, we'll create a shell script that will execute on boot and launch our script.

Create a file called fancontrol.sh and add the following:

#! /bin/sh

### BEGIN INIT INFO
# Provides:          fancontrol.py
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
### END INIT INFO

# Carry out specific functions when asked to by the system
case "$1" in
  start)
    echo "Starting fancontrol.py"
    /usr/local/bin/fancontrol.py &
    ;;
  stop)
    echo "Stopping fancontrol.py"
    pkill -f /usr/local/bin/fancontrol.py
    ;;
  *)
    echo "Usage: /etc/init.d/fancontrol.sh {start|stop}"
    exit 1
    ;;
esac

exit 0

Move this file to /etc/init.d, and make it executable:

sudo mv fancontrol.sh /etc/init.d/
sudo chmod +x /etc/init.d/fancontrol.sh

Now we'll register the script to run on boot:

sudo update-rc.d fancontrol.sh defaults

Now, you can either restart your machine, or kick this off manually since it won't already be running:

sudo reboot

or

sudo /etc/init.d/fancontrol.sh start
Raspberry Pi fan wired up.

With everything working, let's wire up the fan!

Use the schematic and breadboard photo to build the circuit. In this step, I'll provide a photo of the fan fully wired and connected to the Pi. If you need more detailed help with step, please let me know in the comments section below.

Here are the basic steps (always refer to the diagram for help):

  1. Strip the ground (black) lead on the fan.
  2. Grab a female jumper wire (or multiple if you want different colors), cut it in half and strip the ends.
  3. Solder the resistor(s) to one female jumper wire, then to the "base" pin on the transistor.
  4. Solder the other jumper wire to the "emitter" pin on the transistor.
  5. Solder the ground lead from the fan to the "collector" pin on the transistor.
A graph showing the Pi fan controller at work.
As expected, the fan kicks on at 65° Celsius and shuts back off at 55°.

I wanted to visualize my work, so I rigged up a demo. This required two scripts:

  1. outputtemp.py outputs the time and core temperature every second.
  2. cpuload.py runs a busy process on each core.

So during my test window, I measured the core Pi temperature every second, and at some point during the window increased the cpu load, hoping to increase the core temperature. At 65° C, I expected the fan to kick on and start cooling off the Pi. Then when I stopped the load test, I expected the temperature to drop quickly. And once it reached 55° C, I expected the fan to turn back off.

And sure enough, as you can see from the graph, it worked as expected!

In the script above, you may have noticed two variables at the top:

ON_THRESHOLD = 65 
OFF_THRESHOLD = 55
SLEEP_INTERVAL = 5
GPIO_PIN = 17 

These variables can be configured to your liking. ON_THRESHOLD and OFF_THRESHOLD are the temperatures at which the fan will turn on and off (respectively), and SLEEP_INTERVAL is how often the program checks the core temperature. If you need to change the GPIO pin, you can do that here as well.

Feel free to customize either of these variables, but keep in mind that the max temperature is 85°C, and the CPU will be throttled at around 80°C, so we'll want to keep the temperature well below that.

I hope you enjoyed this guide. If you have any comments, questions, or even recommendations for improvement, please let me know in the comments below. And be sure to check out the GitHub repo for this project!