Skip to content

Commit

Permalink
Added 'tool' module to control PWM-tools like lasers or spindles via …
Browse files Browse the repository at this point in the history
…M3-M5

Signed-off-by: Pascal Pieper <[email protected]>
  • Loading branch information
Cirromulus committed Feb 26, 2021
1 parent 64097cb commit 79ff804
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 0 deletions.
38 changes: 38 additions & 0 deletions docs/Tool.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
This document describes the _tool_ module.


## How does it work?
This enables the use of _M3 M4 M5_ GCODE commands.
It is based on the generic fan PWM module, but adds a shutdown value
and makes use of the MCU's safety timeout.

## Configuration
[tool]
pin: !ar9

hardware_pwm: True
# Default: True

cycle_time: 0.001
# Default: 0.01

max_power: 1
# Default: 1

off_below: 0
# Default: 0

shutdown_value: 0
# Default: 0

safety_timeout: 20
# Default: 0 (disabled)
# Suggested value is a minimal amount longer
# than the longest running command.
# Currently, moves are not split into individual sections.


## Commands

`M3/M4 S<value>` : Set PWM duty-cycle. Values between 0 and 255.
`M5` : Stop PWM output to shutdown value.
86 changes: 86 additions & 0 deletions klippy/extras/tool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Printer cooling fan
#
# This file may be distributed under the terms of the GNU GPLv3 license.

class PWM_tool:
def __init__(self, config, default_shutdown_value=0.):
self.printer = config.get_printer()
self.last_value = 0.
self.last_time = 0.
self.safety_timeout = config.getfloat('safety_timeout', 5,
minval=0.)
self.reactor = self.printer.get_reactor()
self.resend_timer = self.reactor.register_timer(
self._resend_current_val)
# Read config
self.max_value = config.getfloat('max_power', 1., above=0., maxval=1.)
self.off_below = config.getfloat('off_below', default=0.,
minval=0., maxval=1.)
cycle_time = config.getfloat('cycle_time', 0.01, above=0.)
hardware_pwm = config.getboolean('hardware_pwm', True)
shutdown_value = config.getfloat(
'shutdown_value', default_shutdown_value, minval=0., maxval=1.)
# Setup pwm object
ppins = self.printer.lookup_object('pins')
self.mcu_pwm = ppins.setup_pin('pwm', config.get('pin'))
self.mcu_pwm.setup_max_duration(self.safety_timeout)
self.mcu_pwm.setup_cycle_time(cycle_time, hardware_pwm)
self.shutdown_value = max(0., min(self.max_value, shutdown_value))
self.mcu_pwm.setup_start_value(0., shutdown_value)
# Register callbacks
self.printer.register_event_handler("gcode:request_restart",
self._handle_request_restart)
def get_mcu(self):
return self.mcu_pwm.get_mcu()
def set_value(self, print_time, value, resend=False):
if value < self.off_below:
value = 0.
value = max(0., min(self.max_value, value * self.max_value))
if value == self.last_value and not resend:
return
print_time = max(self.last_time, print_time)
self.last_time = print_time
self.last_value = value
self.mcu_pwm.set_pwm(print_time, value)
if self.safety_timeout != 0 and not resend:
if value != self.shutdown_value:
self.reactor.update_timer(
self.resend_timer,
self.reactor.monotonic() + 0.75 * self.safety_timeout)
def set_value_from_command(self, value):
toolhead = self.printer.lookup_object('toolhead')
toolhead.register_lookahead_callback((lambda pt:
self.set_value(pt, value)))
def _handle_request_restart(self, print_time):
self.set_value(print_time, 0.)
def get_status(self, eventtime):
return {'value': self.last_value}
def _resend_current_val(self, eventtime):
# TODO: Split moves into smaller segments to enforce resend
toolhead = self.printer.lookup_object('toolhead')
toolhead.register_lookahead_callback((lambda pt:
self.set_value(pt, self.last_value, True)))
if self.last_value != self.shutdown_value:
return eventtime + 0.75 * self.safety_timeout
return self.reactor.NEVER

class PrinterSpindle:
def __init__(self, config):
self.tool = PWM_tool(config)
# Register commands
gcode = config.get_printer().lookup_object('gcode')
gcode.register_command("M3", self.cmd_M3_M4)
gcode.register_command("M4", self.cmd_M3_M4)
gcode.register_command("M5", self.cmd_M5)
def get_status(self, eventtime):
return self.tool.get_status(eventtime)
def cmd_M3_M4(self, gcmd):
# Set spindle speed
value = gcmd.get_float('S', 255., minval=0.) / 255.
self.tool.set_value_from_command(value)
def cmd_M5(self, gcmd):
# Turn spindle off
self.tool.set_value_from_command(0.)

def load_config(config):
return PrinterSpindle(config)

0 comments on commit 79ff804

Please sign in to comment.