forked from alexfukahori/rpi-pwm-fan-control
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrpi-pwmfan.py
executable file
·143 lines (100 loc) · 3.48 KB
/
rpi-pwmfan.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#!/usr/bin/python3
import time
import pigpio
# Pin configuration
PWM_PIN = 12 # Pin to drive PWM fan - HW PWM works on GPIO 12, 13, 18 and 19 on RPi4B
TACH_PIN = 6 # Fan's tachometer output pin
# Temperature thresholds
MAX_TEMP = 70 # [°C] Above this temperature, the fan is at max speed
MIN_TEMP = 45 # [°C] Above this temperature, the fan starts
OFF_TEMP = 40 # [°C] Below this temperature, the fan is off
# Fan settings
PWM_FREQ = 25000 # [kHZ] Noctua Specs: Target_Frequency=25kHz
PULSE = 2 # Noctua Specs: Noctua fans put out two pulses per revolution
# Fan speed settings
RPM_MAX = 5000 # Noctua Specs: Max=5000
RPM_MIN = 1500 # Noctua Specs: Min=1000
RPM_OFF = 0
# Timing
WAIT = 2 # [s] Interval before adjusting RPM
# Initialize pigpio
pi = pigpio.pi()
# Remember pin modes
orig_pwm_pin_mode = -1
orig_tach_pin_mode = -1
# Global variables
t = time.time()
rpm_pulse = 0
tach_pin_callback = None
def getCpuTemperature():
with open('/sys/class/thermal/thermal_zone0/temp') as f:
return float(f.read()) / 1000
def handleTachometerPulse(gpio, level, tick):
"""Handle the interrupt generated by the falling edge of the tachometer pulse."""
global rpm_pulse
rpm_pulse += 1 # Increment pulse count when a pulse is detected
def getFanRPM():
global t, rpm_pulse
dt = time.time() - t
if dt < 0.002:
return # Reject spuriously short pulses
frequency = rpm_pulse / dt
rpm = (frequency / PULSE) * 60
# Reset pulse counter and timestamp
rpm_pulse = 0
t = time.time()
return rpm
def setFanRPM(rpm):
duty_cycle = int((rpm / RPM_MAX) * 1000000)
pi.hardware_PWM(PWM_PIN, PWM_FREQ, duty_cycle)
def setup():
global orig_pwm_pin_mode, orig_tach_pin_mode, tach_pin_callback
print("Setting up...")
orig_pwm_pin_mode = pi.get_mode(PWM_PIN)
orig_tach_pin_mode = pi.get_mode(TACH_PIN)
# Set pin modes
pi.set_mode(PWM_PIN, pigpio.ALT5) # ALT5 mode for hardware PWM
pi.set_mode(TACH_PIN, pigpio.INPUT)
# Add event to detect tachometer pulse
tach_pin_callback = pi.callback(TACH_PIN, pigpio.FALLING_EDGE, handleTachometerPulse)
setFanRPM(RPM_OFF) # Set fan speed to off initially
return
def cleanup():
# Turn off the fan
setFanRPM(RPM_OFF)
# Pin mode cleanup
if orig_pwm_pin_mode != -1:
pi.set_mode(PWM_PIN, orig_pwm_pin_mode)
if orig_tach_pin_mode != -1:
pi.set_mode(TACH_PIN, orig_tach_pin_mode)
if tach_pin_callback is not None:
tach_pin_callback.cancel() # Remove the callback associated with the tachometer pin
pi.stop()
return
def main():
print("PWM FAN control starting...")
setup()
while True:
try:
temp = getCpuTemperature()
print(f"CPU Temperature: {temp:.1f}")
if temp >= MAX_TEMP:
setFanRPM(RPM_MAX)
elif temp >= MIN_TEMP:
delta = temp - MIN_TEMP
rpm = min(RPM_MAX, max(RPM_MIN, int(RPM_MIN + delta)))
setFanRPM(rpm)
elif temp < OFF_TEMP:
setFanRPM(RPM_OFF)
rpm = getFanRPM()
print(f"Fan RPM: {rpm:.2f}")
time.sleep(WAIT)
except KeyboardInterrupt:
cleanup()
break
except Exception as e:
print("Something went wrong")
print(e)
cleanup()
if __name__ == "__main__":
main()