diff --git a/config/example-extras.cfg b/config/example-extras.cfg index 5cd56902..987d5296 100644 --- a/config/example-extras.cfg +++ b/config/example-extras.cfg @@ -598,6 +598,25 @@ # with "!". This parameter must be provided. +# Manual steppers (one may define any number of sections with a +# "manual_stepper" prefix). These are steppers that are controlled by +# the MANUAL_STEPPER g-code command. For example: "MANUAL_STEPPER +# STEPPER=my_stepper MOVE=10 SPEED=5". See the docs/G-Codes.md file +# for a description of the MANUAL_STEPPER command. The steppers are +# not connected to the normal printer kinematics. +#[manual_stepper my_stepper] +#step_pin: +#dir_pin: +#enable_pin: +#step_distance: +# See the "[stepper_x]" section in example.cfg for a description of +# these parameters. +#endstop_pin: +# Endstop switch detection pin. If specified, then one may perform +# "homing moves" by adding a STOP_ON_ENDSTOP parameter to +# MANUAL_STEPPER movement commands. + + # Run-time configurable output pins (one may define any number of # sections with an "output_pin" prefix). Pins configured here will be # setup as output pins and one may modify them at run-time using diff --git a/docs/G-Codes.md b/docs/G-Codes.md index 38b90a13..77fd70e9 100644 --- a/docs/G-Codes.md +++ b/docs/G-Codes.md @@ -162,6 +162,21 @@ enabled: - `SET_SERVO SERVO=config_name [WIDTH=] [ENABLE=<0|1>]` - `SET_SERVO SERVO=config_name [ANGLE=] [ENABLE=<0|1>]` +## Manual stepper Commands + +The following command is available when a "manual_stepper" config +section is enabled: +- `MANUAL_STEPPER STEPPER=config_name [ENABLE=[0|1]] + [SET_POSITION=] + [MOVE= SPEED= [STOP_ON_ENDSTOP=1]]`: This command will + alter the state of the stepper. Use the ENABLE parameter to + enable/disable the stepper. Use the SET_POSITION parameter to force + the stepper to think it is at the given position. Use the MOVE + parameter to request a movement to the given position at the given + SPEED. If STOP_ON_ENDSTOP is specified then the move will end early + should the endstop report as triggered (use STOP_ON_ENDSTOP=-1 to + stop early should the endstop report not triggered). + ## Probe The following commands are available when a "probe" config section is diff --git a/klippy/extras/manual_stepper.py b/klippy/extras/manual_stepper.py new file mode 100644 index 00000000..fdb9fc10 --- /dev/null +++ b/klippy/extras/manual_stepper.py @@ -0,0 +1,117 @@ +# Support for a manual controlled stepper +# +# Copyright (C) 2019 Kevin O'Connor +# +# This file may be distributed under the terms of the GNU GPLv3 license. +import stepper, homing, chelper + +ENDSTOP_SAMPLE_TIME = .000015 +ENDSTOP_SAMPLE_COUNT = 4 + +class ManualStepper: + def __init__(self, config): + self.printer = config.get_printer() + if config.get('endstop_pin', None) is not None: + self.can_home = True + self.stepper = stepper.PrinterRail( + config, need_position_minmax=False, default_position_endstop=0.) + else: + self.can_home = False + self.stepper = stepper.PrinterStepper(config) + self.next_cmd_time = 0. + # Setup iterative solver + ffi_main, ffi_lib = chelper.get_ffi() + self.cmove = ffi_main.gc(ffi_lib.move_alloc(), ffi_lib.free) + self.move_fill = ffi_lib.move_fill + self.stepper.setup_itersolve('cartesian_stepper_alloc', 'x') + self.stepper.set_max_jerk(9999999.9, 9999999.9) + # Register commands + stepper_name = config.get_name().split()[1] + self.gcode = self.printer.lookup_object('gcode') + self.gcode.register_mux_command('MANUAL_STEPPER', "STEPPER", + stepper_name, self.cmd_MANUAL_STEPPER, + desc=self.cmd_MANUAL_STEPPER_help) + self.printer.register_event_handler( + "toolhead:motor_off", self.handle_motor_off) + def sync_print_time(self): + toolhead = self.printer.lookup_object('toolhead') + print_time = toolhead.get_last_move_time() + if self.next_cmd_time > print_time: + toolhead.dwell(self.next_cmd_time - print_time) + else: + self.next_cmd_time = print_time + def do_enable(self, enable): + self.sync_print_time() + self.stepper.motor_enable(self.next_cmd_time, enable) + self.sync_print_time() + def do_set_position(self, setpos): + self.stepper.set_position([setpos, 0., 0.]) + def do_move(self, movepos, speed): + self.sync_print_time() + cp = self.stepper.get_commanded_position() + dist = movepos - cp + move_t = abs(dist / speed) + self.move_fill(self.cmove, self.next_cmd_time, 0., move_t, 0., + cp, 0., 0., dist, 0., 0., 0., speed, 0.) + self.stepper.step_itersolve(self.cmove) + self.next_cmd_time += move_t + self.sync_print_time() + def do_homing_move(self, movepos, speed, triggered): + if not self.can_home: + raise self.gcode.error("No endstop for this manual stepper") + # Notify endstops of upcoming home + endstops = self.stepper.get_endstops() + for mcu_endstop, name in endstops: + mcu_endstop.home_prepare() + # Start endstop checking + self.sync_print_time() + for mcu_endstop, name in endstops: + min_step_dist = min([s.get_step_dist() + for s in mcu_endstop.get_steppers()]) + mcu_endstop.home_start( + self.next_cmd_time, ENDSTOP_SAMPLE_TIME, ENDSTOP_SAMPLE_COUNT, + min_step_dist / speed, triggered=triggered) + # Issue move + self.do_move(movepos, speed) + # Wait for endstops to trigger + error = None + for mcu_endstop, name in endstops: + try: + mcu_endstop.home_wait(self.next_cmd_time) + except mcu_endstop.TimeoutError as e: + if error is None: + error = "Failed to home %s: %s" % (name, str(e)) + for mcu_endstop, name in endstops: + try: + mcu_endstop.home_finalize() + except homing.EndstopError as e: + if error is None: + error = str(e) + self.sync_print_time() + if error is not None: + raise self.gcode.error(error) + cmd_MANUAL_STEPPER_help = "Command a manually configured stepper" + def cmd_MANUAL_STEPPER(self, params): + if 'ENABLE' in params: + self.do_enable(self.gcode.get_int('ENABLE', params)) + if 'SET_POSITION' in params: + setpos = self.gcode.get_float('SET_POSITION', params) + self.do_set_position(setpos) + homing_move = self.gcode.get_int('STOP_ON_ENDSTOP', params, 0) + if homing_move: + movepos = self.gcode.get_float('MOVE', params) + speed = self.gcode.get_float('SPEED', params, above=0.) + if 'ENABLE' not in params and not self.stepper.is_motor_enabled(): + self.do_enable(True) + self.do_homing_move(movepos, speed, homing_move > 0) + elif 'MOVE' in params: + movepos = self.gcode.get_float('MOVE', params) + speed = self.gcode.get_float('SPEED', params, above=0.) + if 'ENABLE' not in params and not self.stepper.is_motor_enabled(): + self.do_enable(True) + self.do_move(movepos, speed) + def handle_motor_off(self, print_time): + self.do_enable(0) + +def load_config_prefix(config): + return ManualStepper(config) diff --git a/klippy/extras/tmc2130.py b/klippy/extras/tmc2130.py index acb1ca51..0374a747 100644 --- a/klippy/extras/tmc2130.py +++ b/klippy/extras/tmc2130.py @@ -129,7 +129,7 @@ def get_config_stealthchop(config, tmc_freq): velocity = config.getfloat('stealthchop_threshold', 0., minval=0.) if not velocity: return mres, False, 0 - stepper_name = config.get_name().split()[1] + stepper_name = " ".join(config.get_name().split()[1:]) stepper_config = config.getsection(stepper_name) step_dist = stepper_config.getfloat('step_distance') step_dist_256 = step_dist / (1 << mres) @@ -144,7 +144,7 @@ def get_config_stealthchop(config, tmc_freq): class TMC2130: def __init__(self, config): self.printer = config.get_printer() - self.name = config.get_name().split()[1] + self.name = config.get_name().split()[-1] self.spi = bus.MCU_SPI_from_config(config, 3, default_speed=4000000) # Allow virtual endstop to be created self.diag1_pin = config.get('diag1_pin', None) diff --git a/klippy/extras/tmc2208.py b/klippy/extras/tmc2208.py index 60bf5a20..ef476e3b 100644 --- a/klippy/extras/tmc2208.py +++ b/klippy/extras/tmc2208.py @@ -259,7 +259,7 @@ def decode_tmc2208_read(reg, data): class TMC2208: def __init__(self, config): self.printer = config.get_printer() - self.name = config.get_name().split()[1] + self.name = config.get_name().split()[-1] self.printer.register_event_handler("klippy:connect", self.handle_connect) # pin setup diff --git a/klippy/toolhead.py b/klippy/toolhead.py index 9c7369e8..32bda0fc 100644 --- a/klippy/toolhead.py +++ b/klippy/toolhead.py @@ -372,6 +372,7 @@ class ToolHead: self.kin.motor_off(last_move_time) for ext in kinematics.extruder.get_printer_extruders(self.printer): ext.motor_off(last_move_time) + self.printer.send_event("toolhead:motor_off", last_move_time) self.dwell(STALL_TIME) logging.debug('; Max time of %f', last_move_time) def wait_moves(self): diff --git a/test/klippy/manual_stepper.cfg b/test/klippy/manual_stepper.cfg new file mode 100644 index 00000000..4ca734a3 --- /dev/null +++ b/test/klippy/manual_stepper.cfg @@ -0,0 +1,22 @@ +# Test config for manual_stepper +[manual_stepper basic_stepper] +step_pin: ar54 +dir_pin: ar55 +enable_pin: !ar38 +step_distance: .0125 + +[manual_stepper homing_stepper] +step_pin: ar60 +dir_pin: !ar61 +enable_pin: !ar56 +step_distance: .0125 +endstop_pin: ^ar14 + +[mcu] +serial: /dev/ttyACM0 +pin_map: arduino + +[printer] +kinematics: none +max_velocity: 300 +max_accel: 3000 diff --git a/test/klippy/manual_stepper.test b/test/klippy/manual_stepper.test new file mode 100644 index 00000000..4f682331 --- /dev/null +++ b/test/klippy/manual_stepper.test @@ -0,0 +1,24 @@ +# Test case for manual_stepper +CONFIG manual_stepper.cfg +DICTIONARY atmega2560-16mhz.dict + +# Test basic moves +MANUAL_STEPPER STEPPER=basic_stepper ENABLE=1 +MANUAL_STEPPER STEPPER=basic_stepper SET_POSITION=0 +MANUAL_STEPPER STEPPER=basic_stepper MOVE=10 SPEED=10 +MANUAL_STEPPER STEPPER=basic_stepper MOVE=5 SPEED=5 +MANUAL_STEPPER STEPPER=basic_stepper MOVE=12 SPEED=12 +MANUAL_STEPPER STEPPER=basic_stepper ENABLE=0 + +# Test homing move +MANUAL_STEPPER STEPPER=homing_stepper ENABLE=1 +MANUAL_STEPPER STEPPER=homing_stepper SET_POSITION=0 +MANUAL_STEPPER STEPPER=homing_stepper MOVE=10 SPEED=10 +MANUAL_STEPPER STEPPER=homing_stepper ENABLE=0 + +# Test motor off +M84 + +# Verify stepper_buzz +STEPPER_BUZZ STEPPER="manual_stepper basic_stepper" +STEPPER_BUZZ STEPPER="manual_stepper homing_stepper"