This program expects a FULLY up-to-date Raspberry Pi OS (either current and legacy) and firmware
# Ensure OS up-to-date:
sudo apt update -y && sudo apt upgrade -y
# Ensure up-to-date firmware:
# - RUN AT YOUR OWN RISK!
# - Needed to get consistent `sysfs` inteface between all models
# - Needed to address https://forums.raspberrypi.com/viewtopic.php?t=367294#p2205138
sudo rpi-update
PWM_FAN_FAN_OFF_GRACE_S
and PWM_FAN_SLEEP_S
are now PWM_FAN_FAN_OFF_GRACE_MS
and PWM_FAN_SLEEP_MS
; tachometer environment variables have been removedsysfs
is now our PWM and GPIO control surface - This was the only way to get PWM support for all Raspberry Pi OS versions, for all modern Pi's 3-5. Despite any discussions about its deprecation, sysfs is not going away anytime soon. Its integration into the Linux kernel and its importance in existing applications and systems ensure its longevity. Do you want your fans to work consistently for all modern Pi's, with their OS variants? This is your only feasible option.Pin # | GPIO # | Name | Color | Notes |
---|---|---|---|---|
4 | 5V | Yellow | ||
6 | Ground | Black | ||
12 | 18 | Duty cycle signal | Blue | PWM channel #0 |
18 | 24 | Tachometer signal | Green | (optional) Any GPIO pin should work |
17 | Tachometer pull-up | Orange | (optional) bridge to "tachometer signal" with a 1k Ω resistor; this is an additional wire, not from the fan |
From Noctua General PWM Fan Whitepaper
Base configuration is dtoverlay=pwm,pin=18,func=2
in /boot/config.txt
(or /boot/firmware/config.txt
):
# Add `dtoverlay=pwm,pin=18,func=2` devicetree overlay in boot configuration and restart:
# - NOTE: Configuratio may be at /boot/config.txt depending on OS version!
sudo nano /boot/firmware/config.txt
sudo shutdown -r now
See Enabling Hardware PWM on Raspberry Pi for more information (differnt pins, 2 channel support, etc.).
install.sh
Install:IMPORTANT!: The service is not started by default - you are expected to configure the GPIO pin, duty cycle hertz, and misc. configuration options to your fan spec + usecase. See: Environment Variables Config »
# From the Raspberry Pi (builds for installed OS variant)
sudo install.sh
# Display help and exit:
sudo pwm_fan_control2 --help
# Running with defaults:
# - IMPORTANT! Only for Noctua 5V PWM fans!
sudo pwm_fan_control2
# Running with debug logging enabled (meant for CLI):
sudo pwm_fan_control2 debug
# Running with debug logging + tachometer enabled:
# sudo pwm_fan_control2 debug {gpio_tach_pin} {tachometer_pulse_per_rotation}
sudo pwm_fan_control2 debug 24 2
# For testing other fans configurations override environment variables by CLI with sudo:
# export PWM_FAN_SLEEP_MS=60000 && \
# export PWM_FAN_MIN_DUTY_CYCLE=20 && \
# sudo -E bash -c './pwm_fan_control2'
# Configure the service:
# - May want to add environment variables to override configuration defaults
# - Not required for Noctua 5V PWM fans
sudo nano /etc/systemd/system/pwm_fan_control2.service
sudo systemctl daemon-reload
# Start the service:
sudo service pwm_fan_control2 start
# Enable service at boot:
sudo systemctl enable pwm_fan_control2.service
# Misc control:
# sudo service pwm_fan_control2 status
# sudo service pwm_fan_control2 stop
# sudo service pwm_fan_control2 restart
# Uninstall - stops and removes installed system service + binary:
# sudo make uninstall
Requirements: Git and basic build tooling.
# Install build tooling
sudo apt install git build-essential
# Get this project
git clone https://github.com/folkhack/raspberry-pi-pwm-fan-2.git
cd raspberry-pi-pwm-fan
# Build project (builds for installed OS variant)
# - Project binary builds in project root ./pwm_fan_control2
make compile
# Install binary
# - Installs to /usr/sbin/pwm_fan_control2
# - `sudo make uninstall` to stop service and remove binary
sudo make install
After installation you can edit the service file:
# Edit system service:
sudo nano /etc/systemd/system/pwm_fan_control2.service
# Reload systemd and restart service:
sudo systemctl daemon-reload
sudo service pwm_fan_control2 restart
# Enable service at boot:
sudo systemctl enable pwm_fan_control2.service
NOTE: This is a good spot to load in an environment configuration as seen below with the commented out line.
[Unit]
Description=PWM fan speed control
After=sysinit.target
[Service]
# Environment=PWM_FAN_SLEEP_MS=60000
ExecStart=/usr/sbin/pwm_fan_control2
Type=simple
User=root
Group=root
Restart=always
[Install]
WantedBy=multi-user.target
NOTE: These environment variables are fully supported by both the Python 3 POC and the formal C implementation!
Variable | Default | Type | Notes |
---|---|---|---|
PWM_FAN_BCM_GPIO_PIN_PWM |
18 | unsigned short | BCM GPIO pin for PWM duty cycle signal |
PWM_FAN_PWM_FREQ_HZ |
2500 | unsigned short | PWM duty cycle target freqency Hz - from Noctua Spec at 25kHz |
PWM_FAN_MIN_DUTY_CYCLE |
20 | unsigned short | Minimum PWM duty cycle - from Noctua spec at 20% |
PWM_FAN_MAX_DUTY_CYCLE |
100 | unsigned short | Maximum PWM duty cycle |
PWM_FAN_MIN_OFF_TEMP_C |
38 | float | Turn fan off if is on and CPU temp falls below this value |
PWM_FAN_MIN_ON_TEMP_C |
40 | float | Turn fan on if is off and CPU temp rises above this value |
PWM_FAN_MAX_TEMP_C |
46 | float | Set fan duty cycle to PWM_FAN_MAX_DUTY_CYCLE if CPU temp rises above this value |
PWM_FAN_FAN_OFF_GRACE_MS |
60000 | unsigned short | Turn fan off if CPU temp stays below MIN_OFF_TEMP_C this for time period |
PWM_FAN_SLEEP_MS |
250 | unsigned short | Main loop check CPU and set PWM duty cycle delay |
When running the Python POC at full 25khz PWM frequency (Noctua Spec) CPU consumption can be upwards of 5-10%. With C it's at 0% on a Raspberry 4.
A quartic bezier easing function was used to smooth fan speed at the upper/lower boundries of the configured temps PWM_FAN_MIN_OFF_TEMP_C
and PWM_FAN_MAX_TEMP_C
. At temps closer to the lower boundry, the fan speed is kept close to the PWM_FAN_MIN_DUTY_CYCLE
, and at the higher boundry fan speed will stay closer to PWM_FAN_MAX_DUTY_CYCLE
.