Skip to content

Instantly share code, notes, and snippets.

@topalex
Last active August 15, 2023 21:36
Show Gist options
  • Save topalex/17f6a66f0249de610e98b0e3a071ffc3 to your computer and use it in GitHub Desktop.
Save topalex/17f6a66f0249de610e98b0e3a071ffc3 to your computer and use it in GitHub Desktop.
Raspberry PI fan control with hardware PWM
#!/usr/bin/python
# -*- coding: utf-8 -*-
from rpi_hardware_pwm import HardwarePWM
import time
import sys
import syslog
import os
import signal
# Configuration
PWM_CHANNEL = 0 # Hardware PWM channel
PWM_FREQ = 25 # [Hz] Change this value if fan has strange behavior
WAIT_TIME = 1 # [s] Time to wait between each refresh
# Configurable temperature and fan speed steps
tempSteps = [60, 70, 80] # [°C]
speedSteps = [40, 60, 100] # [%]
# Fan speed will change only of the difference of temperature is higher than hysteresis
hyst = 1
# Will keep fan running until temperature drops below lowest of tempSteps minus this value
shutdown_hyst = 10 # [°C]
# Setup hardware PWM
pwm = HardwarePWM(pwm_channel=PWM_CHANNEL, hz=PWM_FREQ)
pwm.start(0)
i = 0
fanSpeed = 0
cpuTempOld = 0
fanSpeedOld = 0
systemdRun = bool(os.environ.get('INVOCATION_ID'))
if systemdRun:
syslog.openlog("fancontrol", logoption=syslog.LOG_PID)
def logger(message):
if systemdRun:
syslog.syslog(message)
else:
print(message)
def error(error):
pwm.stop()
if systemdRun:
syslog.syslog(syslog.LOG_ERR, error)
sys.exit(-1)
else:
sys.exit(error)
def get_temp():
cpuTempFile = open("/sys/class/thermal/thermal_zone0/temp", "r")
cpuTemp = float(cpuTempFile.read()) / 1000
cpuTempFile.close()
return cpuTemp
def stop():
pwm.stop()
sys.exit()
def handle_sigterm(sig, frame):
logger("Interrupted by SIGTERM")
stop()
signal.signal(signal.SIGTERM, handle_sigterm)
# We must set a speed value for each temperature step
if len(speedSteps) != len(tempSteps):
error("Error: Numbers of temp steps and speed steps are different")
logger("Starting, CPU temp %.1f'C" % get_temp())
try:
while 1:
# Read CPU temperature
cpuTemp = get_temp()
# Calculate desired fan speed
if abs(cpuTemp - cpuTempOld) > hyst:
# Below first value, fan will run at min speed.
if cpuTemp < tempSteps[0]:
if (cpuTemp < tempSteps[0] - shutdown_hyst or fanSpeed == 0):
fanSpeed = 0
else:
fanSpeed = speedSteps[0]
# Above last value, fan will run at max speed
elif cpuTemp >= tempSteps[len(tempSteps) - 1]:
fanSpeed = speedSteps[len(tempSteps) - 1]
# If temperature is between 2 steps, fan speed is calculated by linear interpolation
else:
for i in range(0, len(tempSteps) - 1):
if (cpuTemp >= tempSteps[i]) and (cpuTemp < tempSteps[i + 1]):
fanSpeed = round((speedSteps[i + 1] - speedSteps[i])
/ (tempSteps[i + 1] - tempSteps[i])
* (cpuTemp - tempSteps[i])
+ speedSteps[i], 1)
if (fanSpeed != fanSpeedOld):
logger("CPU temp %.1f'C, set fan speed to %.1f%%" % (cpuTemp, fanSpeed))
pwm.change_duty_cycle(fanSpeed)
fanSpeedOld = fanSpeed
cpuTempOld = cpuTemp
# Wait until next refresh
time.sleep(WAIT_TIME)
# If a keyboard interrupt occurs (ctrl + c), the PwM enable is set to 0 and the program exits.
except KeyboardInterrupt:
logger("Interrupted by keyboard")
stop()
[Unit]
Description=FanControl Service
After=multi-user.target
[Service]
Restart=always
ExecStart=/usr/bin/python3 /opt/FanControl/fancontrol.py
[Install]
WantedBy=default.target
#!/usr/bin/python
# -*- coding: utf-8 -*-
from rpi_hardware_pwm import HardwarePWM
import sys
# Configuration
PWM_CHANNEL = 0 # Hardware PWM channel
PWM_FREQ = 25 # [Hz] Change this value if fan has strange behavior
# Setup hardware PWM
pwm = HardwarePWM(pwm_channel=PWM_CHANNEL, hz=PWM_FREQ)
pwm.start(0)
try:
while 1:
fanSpeed=float(input("Fan Speed: "))
pwm.change_duty_cycle(fanSpeed)
except(KeyboardInterrupt):
print("Fan ctrl interrupted by keyboard")
pwm.stop()
sys.exit()
@topalex
Copy link
Author

topalex commented Aug 14, 2023

To enable hardware PWM control you will need:

  1. add one of this to /boot/config.txt:
  • dtoverlay=pwm-2chan to use GPIO_18 as the pin for PWM0 and GPIO_19 as the pin for PWM1
  • dtoverlay=pwm-2chan,pin=12,func=4,pin2=13,func2=4 to use GPIO_12 as the pin for PWM0 and GPIO_13 as the pin for PWM1
  1. reboot
  2. install library sudo pip3 install rpi-hardware-pwm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment