11from time import sleep
22from datetime import datetime
33import subprocess
4+ from enum import IntEnum
5+
46import RPi .GPIO as gpio
7+
58from .base import PWMDevice
69
710
11+ class SM (IntEnum ):
12+ """ Variable positions inside speed_mapping """
13+ TEMP = 0
14+ SPEED = 1
15+
16+
817class Fan (PWMDevice ):
918 """ Class for controlling a fan. You can use any fan that has at least 2 wires.
1019 If your fan has 3-4 wires (sense and PWM) you can control it with PWM signal,
@@ -16,23 +25,23 @@ class Fan(PWMDevice):
1625 -:param pwm: Pin used for PWM control
1726 -:param cycletime: Cycle time of auto fan control (s)
1827 -:param frequency: Frequency used for the pwm signal (hz)
19- -:param idle_limit: If fan is on automode, and the temp is below min, only turn off after this limit is reached (m )
20- -:param speed_map : This is used to match temperature readings to fan speed
28+ -:param idle_limit: If fan is on automode, and the temp is below min, only turn off after this limit is reached (s )
29+ -:param speed_mapping : This is used to match temperature readings to fan speed
2130 Values are series of tuples in form of: (from_temp, duty_cycle)
2231 -:param rpm_measurement_timeout: Timeout for the edge detection (ms)
2332 -:param rpm_measurement_edges: How many edges to record for the calculation
2433 -:param rpm_measurement_bouncetime: After an edge detection, the next edge will be ignored for this duration (ms)
2534 This value is in miliseconds, and should be less than the time for half revolution on maximum speed.
2635 Calculation: rev_per_sec = max_rpm / 60 --> 1000 / rev_per_sec / 2 = max_bouncetime
2736 """
28- def __init__ (self , power = False , sense = False , pwm = False , cycletime = 5 , frequency = 25000 , idle_limit = 5 ,
29- speed_map = False , rpm_measurement_timeout = 2000 , rpm_measurement_edges = 40 ,
37+ def __init__ (self , power = False , sense = False , pwm = False , cycletime = 5 , frequency = 25000 , idle_limit = 300 ,
38+ speed_mapping = False , rpm_measurement_timeout = 2000 , rpm_measurement_edges = 40 ,
3039 rpm_measurement_bouncetime = 10 , ** kwargs ):
31- super ().__init__ (power , pwm , frequency , ** kwargs )
40+ super ().__init__ (pwm , power , frequency , ** kwargs )
3241 self .idle_limit = idle_limit
33- self .speed_map = speed_map or [
34- (60 , 1 ),
35- (65 , 30 ),
42+ self .speed_mapping = speed_mapping or [
43+ (20 , 1 ),
44+ (40 , 30 ),
3645 (70 , 50 ),
3746 (75 , 70 ),
3847 (80 , 100 )
@@ -46,7 +55,7 @@ def __init__(self, power=False, sense=False, pwm=False, cycletime=5, frequency=2
4655 if sense :
4756 gpio .setup (sense , gpio .IN , pull_up_down = gpio .PUD_UP )
4857 if not power and not pwm :
49- raise ValueError (" No pins provided for controlling the fan!" )
58+ raise ValueError (' No pins provided for controlling the fan!' )
5059
5160 def set_speed (self , percent ):
5261 """ Set fan speed.
@@ -63,38 +72,39 @@ def set_speed(self, percent):
6372
6473 def smart_set_speed (self , percent ):
6574 """ Set fan speed with respect to other parameters """
66- # If the desired speed is 0, but the fan not reached the idle limit, do nothing
75+ # If the desired speed is 0, but the fan not reached the idle limit, set idle speed
6776 if percent == 0 and self .is_on () and self .ontime () < self .idle_limit :
68- return
77+ percent = 1
6978 self .set_speed (percent )
7079
7180 def read_hw_temperature (self ):
7281 """ Read hardware temperatures """
73- cpu_temp = round (int (subprocess .check_output ([" cat" , " /sys/class/thermal/thermal_zone0/temp" ]).strip ()) / 1000 )
74- self .message (" Temperature reading {}c" . format ( cpu_temp ) )
82+ cpu_temp = round (int (subprocess .check_output ([' cat' , ' /sys/class/thermal/thermal_zone0/temp' ]).strip ()) / 1000 )
83+ self .message (f' Temperature reading { cpu_temp } c.' )
7584 return cpu_temp
7685
7786 def measure_rpm (self ):
7887 """ Measure fan rpm.
7988
8089 Note: Measuring RPM from a script running under an OS (with many other things) can be inaccurate.
90+ You can improve it by fine tune the "rpm_measurement_..." variables for a given fan.
8191 """
8292 if not self .sense :
83- raise ValueError (" No pin was provided for sensing rpm." )
93+ raise ValueError (' No pin was provided for sensing rpm.' )
8494 edges = self .rpm_measurement_edges
8595 start_time = datetime .now ()
8696 for _ in range (edges ):
8797 channel = gpio .wait_for_edge (self .sense , gpio .FALLING ,
8898 timeout = self .rpm_measurement_timeout ,
8999 bouncetime = self .rpm_measurement_bouncetime )
90100 if channel is None :
91- self .message (" RPM measurement timeout" )
101+ self .message (' RPM measurement timeout.' )
92102 edges = 0
93103 break
94104 end_time = datetime .now ()
95105 difference = (end_time - start_time ).total_seconds ()
96106 rpm = int ((edges * (60 / difference )) / 2 )
97- self .message (" Current rpm: {}" . format ( rpm ) )
107+ self .message (f' Current rpm: { rpm } ' )
98108 return rpm
99109
100110 def temp_to_speed (self , temp ):
@@ -103,19 +113,15 @@ def temp_to_speed(self, temp):
103113 -:param temp: Temperature in celsius
104114 """
105115 # If the temp is below the minimum level, we dont need further processing
106- if temp < self .speed_map [0 ][0 ]:
107- self .message ("Temp below minimum" )
116+ if temp < self .speed_mapping [0 ][SM . TEMP ]:
117+ self .message ('Temperature below minimum.' )
108118 return 0
109119
110- for i in range (len (self .speed_map )):
111- current_map = self .speed_map [i ]
112- next_map = self .speed_map [i + 1 ] if len (self .speed_map ) > i + 1 else (500 , 100 )
113- if current_map [0 ] <= temp < next_map [0 ]:
114- return current_map [1 ]
115-
116- # If nothing was matched raise error
117- self .cleanup ()
118- raise ValueError ("Matching speed to temperature failed!" )
120+ for i in range (len (self .speed_mapping )):
121+ current_mapping = self .speed_mapping [i ]
122+ next_mapping = self .speed_mapping [i + 1 ] if len (self .speed_mapping ) > i + 1 else (500 , 100 )
123+ if current_mapping [SM .TEMP ] <= temp < next_mapping [SM .TEMP ]:
124+ return current_mapping [SM .SPEED ]
119125
120126 def auto_set (self , temp = False ):
121127 """ Set fan speed automatically based on temperature and the speedmap
0 commit comments