Skip to content

Commit

Permalink
fan: Support multiple printer fans
Browse files Browse the repository at this point in the history
Allows to setup multiple printer fans.

Multi extruder setups can have separate part cooling fan for each extruder.
This integrates support for that in core klipper, removing the need for every multi extruder config to override their macros.

But probably more importantly defines an endorsed way how multi fan setups should be controlled by the slicers.

Signed-off-by: Viesturs Zarins <[email protected]>
  • Loading branch information
viesturz committed Oct 20, 2023
1 parent 7bd3299 commit e4fa373
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 7 deletions.
12 changes: 12 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 primary]
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=primary
SET_DUAL_CARRIAGE CARRIAGE=0
SET_GCODE_OFFSET Y=0

Expand Down Expand Up @@ -83,6 +88,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 @@ -94,6 +103,7 @@ gcode:
gcode:
PARK_{printer.toolhead.extruder}
ACTIVATE_EXTRUDER EXTRUDER=extruder1
ACTIVATE_FAN FAN=secondary
SET_DUAL_CARRIAGE CARRIAGE=1
SET_GCODE_OFFSET Y=15

Expand All @@ -103,6 +113,7 @@ gcode:
SET_DUAL_CARRIAGE CARRIAGE=0 MODE=PRIMARY
G1 X0
ACTIVATE_EXTRUDER EXTRUDER=extruder
ACTIVATE_FAN FAN=primary,secondary
SET_DUAL_CARRIAGE CARRIAGE=1 MODE=PRIMARY
G1 X100
SET_DUAL_CARRIAGE CARRIAGE=1 MODE=COPY
Expand All @@ -114,6 +125,7 @@ gcode:
SET_DUAL_CARRIAGE CARRIAGE=0 MODE=PRIMARY
G1 X0
ACTIVATE_EXTRUDER EXTRUDER=extruder
ACTIVATE_FAN FAN=primary,secondary
SET_DUAL_CARRIAGE CARRIAGE=1 MODE=PRIMARY
G1 X200
SET_DUAL_CARRIAGE CARRIAGE=1 MODE=MIRROR
Expand Down
5 changes: 4 additions & 1 deletion docs/Config_Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -2634,7 +2634,10 @@ sensor_type: temperature_combined

### [fan]

Print cooling fan.
Print cooling fan (one may define any number of sections with a
"fan" prefix). Print cooling fan is controlled by M106/M107 gcodes.
When multiple fans are defined, ACTIVATE_FAN [gcode command](G-Codes.md#fan)
selects which fan is used for cooling.

```
[fan]
Expand Down
11 changes: 11 additions & 0 deletions docs/G-Codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,17 @@ 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=fan_name[,second_fan]` Selects the active printer fan that
reacts to M106/M107 gcodes. Multiple fans can be selected simultaneously by
a comma separated list. Current fan speed is transferred over to the new fan(s).

### [fan_generic]

The following command is available when a
Expand Down
5 changes: 5 additions & 0 deletions docs/Status_Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,18 @@ The following information is available for extruder_stepper objects (as well as

The following information is available in
[fan](Config_Reference.md#fan),
[fan_generic](Config_Reference.md#fan_generic),
[heater_fan some_name](Config_Reference.md#heater_fan) and
[controller_fan some_name](Config_Reference.md#controller_fan)
objects:
- `speed`: The fan speed as a float between 0.0 and 1.0.
- `rpm`: The measured fan speed in rotations per minute if the fan has
a tachometer_pin defined.

Additionally, [fan](Config_Reference.md#fan) also has:
- `active`: If the fan is selected as the active fan via
[ACTIVATE_FAN](G-Codes.md#fan).

## filament_switch_sensor

The following information is available in
Expand Down
71 changes: 65 additions & 6 deletions klippy/extras/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,19 +103,78 @@ def get_status(self, eventtime):
class PrinterFan:
def __init__(self, config):
self.fan = Fan(config)
# Register commands
for k,f in config.get_printer().lookup_objects(module='fan'):
self.controller = f.controller
break
else:
self.controller = FanController(config)
# Register both the fan name "myfan" and object name "fan myfan".
name = config.get_name()
fan_name = name.split()[-1]
self.controller.register_fan(config, self.fan, fan_name)
if name != fan_name:
self.controller.register_fan(config, self.fan, name)

def get_status(self, eventtime):
status = self.fan.get_status(eventtime)
status['active'] = 1 if self.fan in self.controller.active_fans else 0
return status

# Tracks active fan
class FanController:
def __init__(self, config):
self.name_to_fan = {}
self.active_fans = []
self.requested_speed = None
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)
gcode.register_command("ACTIVATE_FAN", self.cmd_ACTIVATE_FAN)

def register_fan(self, config, fan, fan_name):
if fan_name in self.name_to_fan:
raise config.error("Duplicate fan name: %s" % (fan_name,))
self.name_to_fan[fan_name] = fan

def cmd_ACTIVATE_FAN(self, gcmd):
fan_names = gcmd.get("FAN")
fans = []
for name in fan_names.split(","):
fan = self.name_to_fan.get(name.strip())
if not fan:
raise gcmd.error("Fan %s not known" % (name,))
fans.append(fan)
self.activate_fans(fans)

def activate_fans(self, new_fans):
# Set new active fans and transfer the set speed to those fans.
if self.active_fans == new_fans:
return
old_fans = self.active_fans
self.active_fans = new_fans
if self.requested_speed is not None:
for fan in old_fans:
if fan not in new_fans:
fan.set_speed_from_command(0.)
for fan in new_fans:
if fan not in old_fans:
fan.set_speed_from_command(self.requested_speed)
def cmd_M106(self, gcmd):
# Set fan speed
value = gcmd.get_float('S', 255., minval=0.) / 255.
self.fan.set_speed_from_command(value)
self.requested_speed = gcmd.get_float('S', 255., minval=0.) / 255.
for fan in self.active_fans:
fan.set_speed_from_command(self.requested_speed)
def cmd_M107(self, gcmd):
# Turn fan off
self.fan.set_speed_from_command(0.)
self.requested_speed = 0.
for fan in self.active_fans:
fan.set_speed_from_command(self.requested_speed)

def load_config(config):
fan = PrinterFan(config)
# Prefixless is active on startup
fan.controller.activate_fans([fan.fan])
return fan

def load_config_prefix(config):
return PrinterFan(config)
48 changes: 48 additions & 0 deletions test/klippy/fan.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Test config for fan
[fan]
pin: PH6

[fan fan2]
pin: PH7

[mcu]
serial: /dev/ttyACM0

[printer]
kinematics: cartesian
max_velocity: 300
max_accel: 3000
max_z_velocity: 5
max_z_accel: 100

[stepper_x]
step_pin: PF0
dir_pin: PF1
enable_pin: !PD7
microsteps: 16
rotation_distance: 40
endstop_pin: ^PE5
position_endstop: 0
position_max: 200
homing_speed: 50

[stepper_y]
step_pin: PF6
dir_pin: !PF7
enable_pin: !PF2
microsteps: 16
rotation_distance: 40
endstop_pin: ^PJ1
position_endstop: 0
position_max: 200
homing_speed: 50

[stepper_z]
step_pin: PL3
dir_pin: PL1
enable_pin: !PK0
microsteps: 16
rotation_distance: 8
endstop_pin: ^PD3
position_endstop: 0.5
position_max: 200
14 changes: 14 additions & 0 deletions test/klippy/fan.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Test case for fan
CONFIG fan.cfg
DICTIONARY atmega2560.dict

M106 S255
M107

ACTIVATE_FAN FAN=fan
M106 S255
ACTIVATE_FAN FAN=fan2
M106 S128
ACTIVATE_FAN FAN="fan,fan fan2"
ACTIVATE_FAN FAN="fan, fan fan2"
ACTIVATE_FAN FAN=fan

0 comments on commit e4fa373

Please sign in to comment.