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
|
# Printer cooling fan
#
# Copyright (C) 2016-2024 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
from . import pulse_counter, output_pin
class Fan:
def __init__(self, config, default_shutdown_speed=0.0):
self.printer = config.get_printer()
self.last_fan_value = self.last_req_value = 0.0
# Read config
self.max_power = config.getfloat("max_power", 1.0, above=0.0, maxval=1.0)
self.kick_start_time = config.getfloat("kick_start_time", 0.1, minval=0.0)
self.off_below = config.getfloat(
"off_below", default=0.0, minval=0.0, maxval=1.0
)
cycle_time = config.getfloat("cycle_time", 0.010, above=0.0)
hardware_pwm = config.getboolean("hardware_pwm", False)
shutdown_speed = config.getfloat(
"shutdown_speed", default_shutdown_speed, minval=0.0, maxval=1.0
)
# Setup pwm object
ppins = self.printer.lookup_object("pins")
self.mcu_fan = ppins.setup_pin("pwm", config.get("pin"))
self.mcu_fan.setup_max_duration(0.0)
self.mcu_fan.setup_cycle_time(cycle_time, hardware_pwm)
shutdown_power = max(0.0, min(self.max_power, shutdown_speed))
self.mcu_fan.setup_start_value(0.0, shutdown_power)
self.enable_pin = None
enable_pin = config.get("enable_pin", None)
if enable_pin is not None:
self.enable_pin = ppins.setup_pin("digital_out", enable_pin)
self.enable_pin.setup_max_duration(0.0)
# Create gcode request queue
self.gcrq = output_pin.GCodeRequestQueue(
config, self.mcu_fan.get_mcu(), self._apply_speed
)
# Setup tachometer
self.tachometer = FanTachometer(config)
# Register callbacks
self.printer.register_event_handler(
"gcode:request_restart", self._handle_request_restart
)
def get_mcu(self):
return self.mcu_fan.get_mcu()
def _apply_speed(self, print_time, value):
if value < self.off_below:
value = 0.0
value = max(0.0, min(self.max_power, value * self.max_power))
if value == self.last_fan_value:
return "discard", 0.0
if self.enable_pin:
if value > 0 and self.last_fan_value == 0:
self.enable_pin.set_digital(print_time, 1)
elif value == 0 and self.last_fan_value > 0:
self.enable_pin.set_digital(print_time, 0)
if (
value
and self.kick_start_time
and (not self.last_fan_value or value - self.last_fan_value > 0.5)
):
# Run fan at full speed for specified kick_start_time
self.last_req_value = value
self.last_fan_value = self.max_power
self.mcu_fan.set_pwm(print_time, self.max_power)
return "delay", self.kick_start_time
self.last_fan_value = self.last_req_value = value
self.mcu_fan.set_pwm(print_time, value)
def set_speed(self, value, print_time=None):
self.gcrq.send_async_request(value, print_time)
def set_speed_from_command(self, value):
self.gcrq.queue_gcode_request(value)
def _handle_request_restart(self, print_time):
self.set_speed(0.0, print_time)
def get_status(self, eventtime):
tachometer_status = self.tachometer.get_status(eventtime)
return {
"speed": self.last_req_value,
"rpm": tachometer_status["rpm"],
}
class FanTachometer:
def __init__(self, config):
printer = config.get_printer()
self._freq_counter = None
pin = config.get("tachometer_pin", None)
if pin is not None:
self.ppr = config.getint("tachometer_ppr", 2, minval=1)
poll_time = config.getfloat("tachometer_poll_interval", 0.0015, above=0.0)
sample_time = 1.0
self._freq_counter = pulse_counter.FrequencyCounter(
printer, pin, sample_time, poll_time
)
def get_status(self, eventtime):
if self._freq_counter is not None:
rpm = self._freq_counter.get_frequency() * 30.0 / self.ppr
else:
rpm = None
return {"rpm": rpm}
class PrinterFan:
def __init__(self, config):
self.fan = Fan(config)
# Register commands
gcode = config.get_printer().lookup_object("gcode")
gcode.register_command("M106", self.cmd_M106)
gcode.register_command("M107", self.cmd_M107)
def get_status(self, eventtime):
return self.fan.get_status(eventtime)
def cmd_M106(self, gcmd):
# Set fan speed
value = gcmd.get_float("S", 255.0, minval=0.0) / 255.0
self.fan.set_speed_from_command(value)
def cmd_M107(self, gcmd):
# Turn fan off
self.fan.set_speed_from_command(0.0)
def load_config(config):
return PrinterFan(config)
|