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
·126 lines (90 loc) · 4.61 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
#!/usr/bin/env python3
import argparse
import atexit
import io
import time
from gpiozero import PWMLED
from fan_tacho import FanTacho
from pid_controller import PIDController
from value_mapper import ValueMapper
########################################################################
# Set up some default values (can be overridden from cmdline)
PWM_PIN = 'GPIO12'
TACHO_PIN = 'GPIO6'
MIN_TEMP = 35
TARGET_TEMP = 40
MAX_TEMP = 45
FAN_MIN = 0.2
FAN_MAX = 1.0
DELTA_T = 2
# PID Parameters
KP = 2
KI = 1
KD = 1
PDI_LOWER_LIMIT = -100
PDI_UPPER_LIMIT = 100
PWM_FREQUENCY = 50
########################################################################
def get_cpu_temp():
with io.open('/sys/class/thermal/thermal_zone0/temp', 'r') as f:
return round(float(f.read().strip()) / 1000, 2)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-pp', '--pin-pwm', type=str, default=PWM_PIN, help=f'PWM PIN e.g. {PWM_PIN}')
parser.add_argument('-pt', '--pin-tacho', type=str, default=TACHO_PIN, help=f'Fan Tacho PIN e.g. {TACHO_PIN}')
parser.add_argument('-t', '--target-temp', type=float, default=TARGET_TEMP, help='Target temperature')
parser.add_argument('-tl', '--minimum-temp', type=float, default=MIN_TEMP, help='Minimum temperature, below fan is turned off')
parser.add_argument('-tu', '--maximum-temp', type=float, default=MAX_TEMP, help='Maximum temperature, abive this value the fan runs at full speed')
parser.add_argument('-fl', '--minimum-fan-duty-cycle', type=float, default=FAN_MIN, help=f'Minimum duty cycle of the fan (usually {FAN_MIN})')
parser.add_argument('-fu', '--maximum-fan-duty-cycle', type=float, default=FAN_MAX, help=f'Maximum duty cycle of the fan {FAN_MAX}')
parser.add_argument('-u', '--update-time', type=float, default=DELTA_T, help='Update time for all internal values')
parser.add_argument('-kp', type=float, default=KP, help='PDI Controller proportional factor')
parser.add_argument('-ki', type=float, default=KI, help='PDI Controller integrative factor')
parser.add_argument('-kd', type=float, default=KD, help='PDI Controller derivative factor')
parser.add_argument('-ll', '--pdi-lower-limit', type=float, default=PDI_LOWER_LIMIT, help='PDI Controller output value lower limit')
parser.add_argument('-ul', '--pdi-upper-limit', type=float, default=PDI_UPPER_LIMIT, help='PDI Controller output value upper limit')
parser.add_argument('-f', '--pwm-frequency', type=float, default=PWM_FREQUENCY, help='PWM Output frequency')
parser.add_argument('-s', '--silent', action='store_true', help='Do not output any console logs')
parser.add_argument('--use-pigpio', action='store_true', help='Use the pigpio library to support hardware PWM - see README before using')
args = parser.parse_args()
if args.use_pigpio:
from gpiozero import Device
from gpiozero.pins.pigpio import PiGPIOFactory
Device.pin_factory = PiGPIOFactory()
fan = PWMLED(args.pin_pwm, frequency=args.pwm_frequency)
# make sure that we start with 100% an end with 100% fan power
def fan_to_full():
fan.value = 1.0
fan_to_full()
atexit.register(fan_to_full)
tacho = FanTacho(args.pin_tacho)
pid_controller = PIDController(args.update_time,
args.kp, args.ki, args.kd,
args.pdi_lower_limit, args.pdi_upper_limit,
positive_feedback=True)
value_mapper = ValueMapper(args.pdi_lower_limit, args.pdi_upper_limit,
args.minimum_fan_duty_cycle, args.maximum_fan_duty_cycle)
while True:
tacho.update()
current_cpu_temp = get_cpu_temp()
pid_controller.calculate(args.target_temp, current_cpu_temp)
controller_value = pid_controller.output
fan_pwm_duty = round(value_mapper(controller_value), 2)
# print(f'controller_value: {controller_value} -> mapped: {fan_pwm_duty}')
# override (this is more like a safeguard)
limit = ''
if current_cpu_temp < args.minimum_temp:
# turn fan off if we're below min temp
fan_pwm_duty = 0.0
limit = 'l'
if current_cpu_temp > args.maximum_temp:
# set to 100% in case we are overtemp
fan_pwm_duty = 1.0
limit = 'h'
# actually set fan value
fan.value = fan_pwm_duty
if args.silent is False:
print(f'CPU Temp: {current_cpu_temp} C - Fan: {tacho.rpm} rpm - Fan PWM: {fan_pwm_duty*100:.2f} % {limit}')
time.sleep(args.update_time)
if __name__ == '__main__':
main()