Skip to content

Add hardware PWM version #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions fan_hardware_pwm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#!/usr/bin/python3

### IMPORTANT ###
# you must enable pwm chanels in /boot/config.txt
# see https://github.com/Pioreactor/rpi_hardware_pwm

# python modules
from rpi_hardware_pwm import HardwarePWM
import time
import signal
import sys
import os
import sys, getopt

# Configuration
PWM_CHANNEL = 0 # PWM channel used to drive PWM fan (gpio18 = channel 0)
WAIT_TIME = 1 # [s] Time to wait between each refresh
PWM_FREQ = 10000 # [Hz] 10kHz for Noctua PWM control

# Configurable temperature and fan speed
MIN_TEMP = 40
MAX_TEMP = 60
FAN_LOW = 20
FAN_HIGH = 100
FAN_OFF = 0
FAN_MAX = 100

# logging and metrics (enable = 1)
VERBOSE = 0
NODE_EXPORTER = 0

# parse input arguments
try:
opts, args = getopt.getopt(sys.argv[1:],"hv",["min-temp=","max-temp=","fan-low=","fan-high=","wait-time=","help","pwm-channel=","pwm-freq=","verbose","node-exporter"])
except getopt.GetoptError:
print('fan.py [--min-temp=40] [--max-temp=70] [--fan-low=20] [--fan-high=100] [--wait-time=1] [--pwm-channel=0] [--pwm-freq=10000] [--node-exporter] [-v|--verbose] [-h|--help]')
sys.exit(2)
for opt, arg in opts:
if opt in ("-h", "--help"):
print('fan.py [--min-temp=40] [--max-temp=70] [--fan-low=20] [--fan-high=100] [--wait-time=1] [--pwm-channel=0] [--pwm-freq=10000] [--node-exporter] [-v|--verbose] [-h|--help]')
sys.exit()
elif opt in ("-v", "--verbose"):
VERBOSE = 1
elif opt in ("--min-temp"):
MIN_TEMP = int(arg)
elif opt in ("--max-temp"):
MAX_TEMP = int(arg)
elif opt in ("--fan-low"):
FAN_LOW = int(arg)
elif opt in ("--fan-high"):
FAN_HIGH = int(arg)
elif opt in ("--wait-time"):
WAIT_TIME = int(arg)
elif opt in ("--pwm-channel"):
PWM_CHANNEL = int(arg)
elif opt in ("--pwm-freq"):
PWM_FREQ = int(arg)
elif opt in ("--node-exporter"):
NODE_EXPORTER = 1
print("")
print("MIN_TEMP:",MIN_TEMP)
print("MAX_TEMP:",MAX_TEMP)
print("FAN_LOW:",FAN_LOW)
print("FAN_HIGH:",FAN_HIGH)
print("WAIT_TIME:",WAIT_TIME)
print("PWM_CHANNEL:",PWM_CHANNEL)
print("PWM_FREQ:",PWM_FREQ)
print("VERBOSE:",VERBOSE)
print("NODE_EXPORTER:",NODE_EXPORTER)
print("")

# Get CPU's temperature
def getCpuTemperature():
res = os.popen('cat /sys/devices/virtual/thermal/thermal_zone0/temp').readline()
temp = (float(res)/1000)
return temp

# Set fan speed
def setFanSpeed(speed,temp):
pwm.change_duty_cycle(speed)

# print fan speed and temperature
if VERBOSE == 1:
print("fan speed: ",int(speed)," temp: ",temp)

# write fan metrics to file for node-exporter/prometheus
if NODE_EXPORTER == 1:
# Save a reference to the original standard output
original_stdout = sys.stdout
with open('/var/lib/node_exporter/fan-metrics.prom', 'w') as f:
# Change the standard output to the file we created.
sys.stdout = f
print('raspberry_fan_speed ',speed)
print('raspberry_fan_temp ',temp)
print('raspberry_fan_min_temp ',MIN_TEMP)
print('raspberry_fan_max_temp ',MAX_TEMP)
print('raspberry_fan_fan_low ',FAN_LOW)
print('raspberry_fan_fan_high ',FAN_HIGH)
print('raspberry_fan_wait_time ',WAIT_TIME)
print('raspberry_fan_pwm_channel ',PWM_CHANNEL)
print('raspberry_fan_freq ',PWM_FREQ)
# Reset the standard output to its original value
sys.stdout = original_stdout
f.close()

return()

# Handle fan speed
def handleFanSpeed():
temp = getCpuTemperature()

# Turn off the fan if temperature is below MIN_TEMP
if temp < MIN_TEMP:
setFanSpeed(FAN_OFF,temp)

# Set fan speed to MAXIMUM if the temperature is above MAX_TEMP
elif temp > MAX_TEMP:
setFanSpeed(FAN_MAX,temp)

# Caculate dynamic fan speed
else:
step = (FAN_HIGH - FAN_LOW)/(MAX_TEMP - MIN_TEMP)
delta = temp - MIN_TEMP
speed = FAN_LOW + ( round(delta) * step )
setFanSpeed(speed,temp)

return ()

try:
# Setup hardware PWM
pwm = HardwarePWM(pwm_channel=PWM_CHANNEL, hz=60)
pwm.change_frequency(PWM_FREQ)
pwm.start(FAN_LOW) # full duty cycle

# Handle fan speed every WAIT_TIME sec
while True:
handleFanSpeed()
time.sleep(WAIT_TIME)

except KeyboardInterrupt: # trap a CTRL+C keyboard interrupt
setFanSpeed(FAN_LOW,MIN_TEMP)