Friday, April 10, 2020

MRR PiFan: slim fan controller for single board computers

I have a few Raspberry Pi and other single board computers, and most of them do require some form of active cooling (especially if overclocked). A small 5V fan is usually enough, but having those fans run all the time can be a bit noisy over time.

There are many circuits that can be found online to add GPIO control to fans using a transistor or MOSFET. Aliexpress even sells something that plugs into the Raspberry Pi's pin headers like a mini-hat. But most are bulky; the one on Aliexpress kind of sticks out and won't fit inside most cases.

So I designed my own, a slim design that sits over the RPi's 40-pin header. This means it will fit inside cases, but it also covers the pins. Not really a problem for me since I don't use the pins.


The module/mini-hat uses a WST3392 dual-channel N-MOSFET, which I have quite a few lying around because I use them in the MRR ESPE. One channel is used for the fan (which also has a red LED that comes on together with the fan). The other channel is connected to a green LED; use of this LED is optional.

If you don't want to use it as a mini-hat, it can be used like part of the fan's wire. Here is one with 90-degree pin headers, one end which will connect to the fan, and the other to the board. The red and black wires go to 5V and GND, while the yellow to the pin for controlling the fan. The green can be used to control the green LED onboard.


Pin headers can be bulky, so wires can also be directly soldered onto the module. This makes the module even flatter.


Here is the module being used on my RPi4B as a mini-hat, controlling the dual 25mm fans for the "armor" case. You can see that the module fits very snugly inside the case. The female header sits on pins 2, 3, 5V and GND (the topmost set of pins are skipped).


The KiCad files can be found here. A script that can be used to control the module and fan can be found here; you will need to edit it a bit if you want to control the green LED. For me, I changed /usr/local/bin/fancontrol.py to something like:
#!/usr/bin/env python3

import subprocess
import time

from gpiozero import OutputDevice


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


def get_temp():
    """Get the core temperature.

    Read file from /sys to get CPU temp in temp in C *1000

    Returns:
        int: The core temperature in thousanths of degrees Celsius.
    """
    with open("/sys/class/thermal/thermal_zone0/temp") as f:
        temp_data = f.read()

    return int(temp_data)


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)
    led = OutputDevice(LED_PIN)

    led.on()
   
    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()
            led.off()

        # 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()
            led.on()

        time.sleep(SLEEP_INTERVAL)



The following is an excerpt from this article about how to make the script start on boot.
----- start of excerpt -----
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
----- end of excerpt -----

Update (December 28, 2020): I just found out that since 2018, Raspberry Pi's config.txt has device tree overlays that allow a GPIO pin to be used to control a fan. All that is needed is to specify the option in config.txt with the GPIO pin to use and the temperature to turn on the fan. For example, to turn on the fan at 55 degrees Celsius with the control pin connected to GPIO 2, add the following line to config.txt:
dtoverlay=gpio-fan,gpiopin=2,temp=55000
The fan will turn off at 10 degrees Celsius below the specified temperature, so in this case, it will turn off at 45 degrees Celsius. However, there is no option to control the LED. The GPIO numbering is the BCM numbering (not board numbering).

No comments: