Skip to content

Commit

Permalink
fan: Support multiple printer fans
Browse files Browse the repository at this point in the history
Multi extruder setups can have separate part cooling fan for each extruder. This change allows runtime switching between fans using ACTIVATE_FAN command, similar to ACTIVATE_EXTRUDER.

The fix is to essentially make [fan] the same as [fan_generic] and use
ACTIVATE_FAN to specifcy which fan is the printer fan.
Inactive printer fans may be manually controlled using existing  SET_FAN_SPEED command.

This helps primarliy with PrusaSlicer gcode, that just emits M106 S110; T1; when changing tools.
Making emulation with macros quite complex.

Signed-off-by: Viesturs Zarins <viesturz@gmail.com>
  • Loading branch information
viesturz committed Jul 8, 2023
1 parent a96608a commit 6ddb2af
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 33 deletions.
10 changes: 10 additions & 0 deletions config/sample-idex.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ pid_Kd: 114
min_temp: 0
max_temp: 250

# The definition for the part cooling fan for primary extruder
[fan]
pin: PH6

# Helper script to park the carriage (called from T0 and T1 macros)
[gcode_macro PARK_extruder]
gcode:
Expand All @@ -47,6 +51,7 @@ gcode:
gcode:
PARK_{printer.toolhead.extruder}
ACTIVATE_EXTRUDER EXTRUDER=extruder
ACTIVATE_FAN FAN=fan
SET_DUAL_CARRIAGE CARRIAGE=0
SET_GCODE_OFFSET Y=0

Expand Down Expand Up @@ -81,6 +86,10 @@ pid_Kd: 114
min_temp: 0
max_temp: 250

# The definition for the part cooling fan for secondary extruder
[fan secondary]
pin: PH7

[gcode_macro PARK_extruder1]
gcode:
SAVE_GCODE_STATE NAME=park1
Expand All @@ -92,5 +101,6 @@ gcode:
gcode:
PARK_{printer.toolhead.extruder}
ACTIVATE_EXTRUDER EXTRUDER=extruder1
ACTIVATE_FAN FAN='fan secondary'
SET_DUAL_CARRIAGE CARRIAGE=1
SET_GCODE_OFFSET Y=15
8 changes: 7 additions & 1 deletion docs/Config_Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -2539,7 +2539,13 @@ serial_no:

### [fan]

Print cooling fan.
Print cooling fan, controlled by M106/M107 gcodes.
One may define any number of sections with a
"fan" prefix. Fan with no prefix is the default.
ACTIVATE_FAN [gcode command](G-Codes.md#fan) can be used
to select another fan to be the print cooling fan.
When not active, can be manually controlled
with the SET_FAN_SPEED [gcode command](G-Codes.md#fan_generic).

```
[fan]
Expand Down
14 changes: 14 additions & 0 deletions docs/G-Codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,18 @@ This command is deprecated and will be removed in the near future.
#### SYNC_STEPPER_TO_EXTRUDER
This command is deprecated and will be removed in the near future.

### [fan]

The following command is available when a
[fan config section](Config_Reference.md#fan is
enabled.

#### ACTIVATE_FAN

`ACTIVATE_FAN FAN=config_name` Selects the active printer fan that reacts to
M106/M107 gcodes. Current fan speed is transferred over to the new fan.
Nameless [fan] is active by default.

### [fan_generic]

The following command is available when a
Expand All @@ -439,6 +451,8 @@ enabled.
`SET_FAN_SPEED FAN=config_name SPEED=<speed>` This command sets the
speed of a fan. "speed" must be between 0.0 and 1.0.

Can also be used with non-active [fan] entries.

### [filament_switch_sensor]

The following command is available when a
Expand Down
41 changes: 29 additions & 12 deletions klippy/extras/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,22 +100,39 @@ def get_status(self, eventtime):
rpm = None
return {'rpm': rpm}

class PrinterFan:
class FanControllable:
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)
self.gcode = config.get_printer().lookup_object('gcode')
self.name = config.get_name()
self.printer_fan = config.get_printer().load_object(
config, 'printer_fan')
self.register_cmds_for_name(self.name)

def register_cmds_for_name(self, name):
self.gcode.register_mux_command("ACTIVATE_FAN", "FAN",
name, self.cmd_ACTIVATE_FAN)
self.gcode.register_mux_command("SET_FAN_SPEED", "FAN",
name,
self.cmd_SET_FAN_SPEED,
desc=self.cmd_SET_FAN_SPEED_help)

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., minval=0.) / 255.
def set_speed_from_command(self, value):
self.fan.set_speed_from_command(value)
def cmd_M107(self, gcmd):
# Turn fan off
self.fan.set_speed_from_command(0.)
cmd_ACTIVATE_FAN_help = 'Set this fan as the active printer fan'
def cmd_ACTIVATE_FAN(self, gcmd):
self.printer_fan.activate_fan(self)
cmd_SET_FAN_SPEED_help = 'Set fan speed'
def cmd_SET_FAN_SPEED(self, gcmd):
speed = gcmd.get_float('SPEED', 0.)
self.fan.set_speed_from_command(speed)

def load_config(config):
return PrinterFan(config)
fan = FanControllable(config)
fan.printer_fan.activate_fan(fan)
return fan

def load_config_prefix(config):
return FanControllable(config)
24 changes: 4 additions & 20 deletions klippy/extras/fan_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,8 @@
# This file may be distributed under the terms of the GNU GPLv3 license.
from . import fan

class PrinterFanGeneric:
cmd_SET_FAN_SPEED_help = "Sets the speed of a fan"
def __init__(self, config):
self.printer = config.get_printer()
self.fan = fan.Fan(config, default_shutdown_speed=0.)
self.fan_name = config.get_name().split()[-1]

gcode = self.printer.lookup_object("gcode")
gcode.register_mux_command("SET_FAN_SPEED", "FAN",
self.fan_name,
self.cmd_SET_FAN_SPEED,
desc=self.cmd_SET_FAN_SPEED_help)

def get_status(self, eventtime):
return self.fan.get_status(eventtime)
def cmd_SET_FAN_SPEED(self, gcmd):
speed = gcmd.get_float('SPEED', 0.)
self.fan.set_speed_from_command(speed)

def load_config_prefix(config):
return PrinterFanGeneric(config)
instance = fan.FanControllable(config)
# Support for suffix only name. The full name is already registered in init.
instance.register_cmds_for_name(instance.name[len('fan_generic '):])
return instance
47 changes: 47 additions & 0 deletions klippy/extras/printer_fan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Support for toolchnagers
#
# Copyright (C) 2023 Viesturs Zarins <viesturz@gmail.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.

# Main printer fan.
# Handles M106 M107 and routes to the active generic fan.
class PrinterFan:
def __init__(self, config):
self.printer = config.get_printer()
self.active_fan = None
self.requested_speed = None
gcode = self.printer.lookup_object('gcode')
gcode.register_command("M106", self.cmd_M106)
gcode.register_command("M107", self.cmd_M107)

def status(self, eventtime):
return {'fan': self.active_fan.name if self.active_fan else None,
'speed': self.requested_speed}

def get_fan(self):
return self.activate_fan

def activate_fan(self, fan):
# Set new active fan and move the set speed to that fan.
if self.active_fan == fan:
return
if self.active_fan and self.requested_speed is not None:
self.active_fan.set_speed_from_command(0.)
self.active_fan = fan
if self.active_fan and self.requested_speed is not None:
self.active_fan.set_speed_from_command(self.requested_speed)

def cmd_M106(self, gcmd):
# Set fan speed
self.requested_speed = gcmd.get_float('S', 255., minval=0.) / 255.
if self.active_fan:
self.active_fan.set_speed_from_command(self.requested_speed)
def cmd_M107(self, gcmd):
# Turn fan off
self.requested_speed = 0.
if self.active_fan:
self.active_fan.set_speed_from_command(self.requested_speed)

def load_config(config):
return PrinterFan(config)

0 comments on commit 6ddb2af

Please sign in to comment.