Created
November 12, 2021 17:08
-
-
Save Foolson/87cfebe847f39030fd305058f96363ca to your computer and use it in GitHub Desktop.
pi-pwm-fan.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# Python modules | |
import time | |
import sys | |
import signal | |
import getopt | |
from rpi_hardware_pwm import HardwarePWM | |
# Configuration | |
waitTime = 5 # [s] Time to wait between each refresh | |
pwmFrequency = 25000 | |
# Configurable temperature and fan speed | |
minTemp = 44 | |
maxTemp = 80 | |
fanLow = 15 | |
fanHigh = 100 | |
fanOff = 0 | |
fanMax = 100 | |
# Logging and metrics (enable = 1) | |
verbose = 0 | |
quiet = 0 | |
# Parse input arguments | |
helpText = ('pi-pwm-fan.py [--min-temp=40] [--max-temp=70] [--fan-low=20] ' | |
'[--fan-high=100] [--wait-time=1] [-v|--verbose] ' | |
'[-q|--quiet] [-h|--help]') | |
try: | |
opts, args = getopt.getopt(sys.argv[1:], "hvq", [ | |
"min-temp=", "max-temp=", "fan-low=", "fan-high=", "wait-time=", | |
"help", "verbose", "quiet" | |
]) | |
except getopt.GetoptError: | |
print(helpText) | |
sys.exit(2) | |
for opt, arg in opts: | |
if opt in ("-h", "--help"): | |
print(helpText) | |
sys.exit() | |
elif opt in ("-v", "--verbose"): | |
verbose = 1 | |
elif opt in ("-q", "--quiet"): | |
quiet = 1 | |
elif opt in ("--min-temp"): | |
minTemp = int(arg) | |
elif opt in ("--max-temp"): | |
maxTemp = int(arg) | |
elif opt in ("--fan-low"): | |
fanLow = int(arg) | |
elif opt in ("--fan-high"): | |
fanHigh = int(arg) | |
elif opt in ("--wait-time"): | |
waitTime = int(arg) | |
if quiet == 0: | |
print("Min temp:", minTemp) | |
print("Max temp:", maxTemp) | |
print("Fan low:", fanLow) | |
print("Fan high:", fanHigh) | |
print("Wait time:", waitTime) | |
print("Verbose:", verbose) | |
print("Quiet:", quiet) | |
# Get CPU's temperature | |
def getCpuTemperature(): | |
with open('/sys/devices/virtual/thermal/thermal_zone0/temp') as f: | |
lines = f.read() | |
temp = (float(lines)/1000) | |
return temp | |
# Set fan speed | |
def setFanSpeed(pwm, speed): | |
pwm.change_duty_cycle(speed) | |
# Get fan speed | |
def getFanSpeed(temp): | |
# Turn off the fan if temperature is below minTemp | |
if temp < minTemp: | |
speed = fanOff | |
# Set fan speed to MAXIMUM if the temperature is above maxTemp | |
elif temp > maxTemp: | |
speed = fanMax | |
# Caculate dynamic fan speed | |
else: | |
step = (fanHigh - fanLow)/(maxTemp - minTemp) | |
delta = temp - minTemp | |
speed = fanLow + (round(delta) * step) | |
return speed | |
# Stop the PWM fan and exit the script | |
def exitCleanup(signum, frame): | |
pwm.change_duty_cycle(0) | |
pwm.stop() | |
print("PWM fan is turned off") | |
sys.exit(signum) | |
# Setup GPIO pin for PWM | |
pwm = HardwarePWM(0, hz=pwmFrequency) | |
pwm.start(0) | |
# Catch signals to gracefully stop the fans if killed | |
signal.signal(signal.SIGINT, exitCleanup) | |
signal.signal(signal.SIGTERM, exitCleanup) | |
# Manage fan speed every waitTime sec | |
try: | |
while True: | |
temp = getCpuTemperature() | |
speed = getFanSpeed(temp) | |
setFanSpeed(pwm, speed) | |
if verbose == 1 and quiet == 0: | |
print("fan speed: ", int(speed), " temp: ", temp) | |
time.sleep(waitTime) | |
except KeyboardInterrupt: | |
exitCleanup() |
systemd service
[Unit]
Description=Raspberry pi PWM fan control
[Service]
Restart=always
ExecStart=/usr/bin/env python3 -u /opt/pi-pwm-fan/pi-pwm-fan.py -v
[Install]
WantedBy=multi-user.target
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Based om https://github.com/tedsluis/raspberry-pi-pwm-fan-control but majorly overhauled.
Biggest change is that lgpio is replaced by rpi_hardware_pwm (https://github.com/Pioreactor/rpi_hardware_pwm) because of the sofware PWM limitation regarding the PWM frequency. Software PWM is limited to 10KHz and PWM fans wants 25KHz. 10KHz works but, at least for me, produces high a high pitch noise. For this to work
dtoverlay=pwm-2chan
must be added to/boot/config.txt
andGPIO_18
must be used for the PWM signal.My Raspberry Pi 4 has the stock case but with a hole drilled for a 40mm fan and is overclocked to 2GHz.
The graph is made with data that was generated by using a script written by Jeff Geerling (https://gist.github.com/geerlingguy/91d4736afe9321cbfc1062165188dda4). 5 minutes idle, 10 full load and 5 idle.