diff --git a/config/example-extras.cfg b/config/example-extras.cfg index 410a5835..33b67178 100644 --- a/config/example-extras.cfg +++ b/config/example-extras.cfg @@ -98,6 +98,30 @@ # activation. +# Support for cartesian printers with dual carriages on a single +# axis. The active carriage is set via the SET_DUAL_CARRIAGE extended +# g-code command. The "SET_DUAL_CARRIAGE CARRIAGE=1" command will +# activate the carriage defined in this section (CARRIAGE=0 will +# return activation to the primary carriage). Dual carriage support is +# typically combined with extra extruders - use the SET_DUAL_CARRIAGE +# command in the activate_gcode / deactivate_gcode section of the +# appropriate extruder. Be sure to also use that mechanism to park the +# carriages during deactivation. +#[dual_carriage] +#axis: +# The axis this extra carriage is on (either x or y). This parameter +# must be provided. +#step_pin: +#dir_pin: +#enable_pin: +#step_distance: +#endstop_pin: +#position_endstop: +#position_min: +#position_max: +# See the example.cfg for the definition of the above parameters. + + # Multi-stepper axes. On a cartesian style printer, the stepper # controlling a given axis may have additional config blocks defining # steppers that should be stepped in concert with the primary diff --git a/klippy/cartesian.py b/klippy/cartesian.py index 7e4ee95c..0c1bf09d 100644 --- a/klippy/cartesian.py +++ b/klippy/cartesian.py @@ -10,6 +10,7 @@ StepList = (0, 1, 2) class CartKinematics: def __init__(self, toolhead, printer, config): + self.printer = printer self.steppers = [stepper.LookupMultiHomingStepper( printer, config.getsection('stepper_' + n)) for n in ['x', 'y', 'z']] @@ -26,6 +27,20 @@ class CartKinematics: self.steppers[1].set_max_jerk(max_halt_velocity, max_accel) self.steppers[2].set_max_jerk( min(max_halt_velocity, self.max_z_velocity), max_accel) + # Check for dual carriage support + self.dual_carriage_axis = None + self.dual_carriage_steppers = [] + if config.has_section('dual_carriage'): + dc_config = config.getsection('dual_carriage') + self.dual_carriage_axis = dc_config.getchoice( + 'axis', {'x': 0, 'y': 1}) + dc_stepper = stepper.LookupMultiHomingStepper(printer, dc_config) + dc_stepper.set_max_jerk(max_halt_velocity, max_accel) + self.dual_carriage_steppers = [ + self.steppers[self.dual_carriage_axis], dc_stepper] + printer.lookup_object('gcode').register_command( + 'SET_DUAL_CARRIAGE', self.cmd_SET_DUAL_CARRIAGE, + desc=self.cmd_SET_DUAL_CARRIAGE_help) def get_steppers(self, flags=""): if flags == "Z": return [self.steppers[2]] @@ -38,44 +53,57 @@ class CartKinematics: s.set_position(newpos[i]) if i in homing_axes: self.limits[i] = (s.position_min, s.position_max) + def _home_axis(self, homing_state, axis, stepper): + s = stepper + # Determine moves + if s.homing_positive_dir: + pos = s.position_endstop - 1.5*( + s.position_endstop - s.position_min) + rpos = s.position_endstop - s.homing_retract_dist + r2pos = rpos - s.homing_retract_dist + else: + pos = s.position_endstop + 1.5*( + s.position_max - s.position_endstop) + rpos = s.position_endstop + s.homing_retract_dist + r2pos = rpos + s.homing_retract_dist + # Initial homing + homing_speed = s.homing_speed + if axis == 2: + homing_speed = min(homing_speed, self.max_z_velocity) + homepos = [None, None, None, None] + homepos[axis] = s.position_endstop + coord = [None, None, None, None] + coord[axis] = pos + homing_state.home(coord, homepos, s.get_endstops(), homing_speed) + # Retract + coord[axis] = rpos + homing_state.retract(coord, homing_speed) + # Home again + coord[axis] = r2pos + homing_state.home(coord, homepos, s.get_endstops(), + homing_speed/2.0, second_home=True) + # Set final homed position + coord[axis] = s.position_endstop + s.get_homed_offset() + homing_state.set_homed_position(coord) def home(self, homing_state): # Each axis is homed independently and in order for axis in homing_state.get_axes(): - s = self.steppers[axis] - # Determine moves - if s.homing_positive_dir: - pos = s.position_endstop - 1.5*( - s.position_endstop - s.position_min) - rpos = s.position_endstop - s.homing_retract_dist - r2pos = rpos - s.homing_retract_dist + if axis == self.dual_carriage_axis: + dc1, dc2 = self.dual_carriage_steppers + altc = self.steppers[axis] == dc2 + self._activate_carriage(0) + self._home_axis(homing_state, axis, dc1) + self._activate_carriage(1) + self._home_axis(homing_state, axis, dc2) + self._activate_carriage(altc) else: - pos = s.position_endstop + 1.5*( - s.position_max - s.position_endstop) - rpos = s.position_endstop + s.homing_retract_dist - r2pos = rpos + s.homing_retract_dist - # Initial homing - homing_speed = s.homing_speed - if axis == 2: - homing_speed = min(homing_speed, self.max_z_velocity) - homepos = [None, None, None, None] - homepos[axis] = s.position_endstop - coord = [None, None, None, None] - coord[axis] = pos - homing_state.home(coord, homepos, s.get_endstops(), homing_speed) - # Retract - coord[axis] = rpos - homing_state.retract(coord, homing_speed) - # Home again - coord[axis] = r2pos - homing_state.home(coord, homepos, s.get_endstops(), - homing_speed/2.0, second_home=True) - # Set final homed position - coord[axis] = s.position_endstop + s.get_homed_offset() - homing_state.set_homed_position(coord) + self._home_axis(homing_state, axis, self.steppers[axis]) def motor_off(self, print_time): self.limits = [(1.0, -1.0)] * 3 for stepper in self.steppers: stepper.motor_enable(print_time, 0) + for stepper in self.dual_carriage_steppers: + stepper.motor_enable(print_time, 0) self.need_motor_enable = True def _check_motor_enable(self, print_time, move): need_motor_enable = False @@ -139,3 +167,24 @@ class CartKinematics: if move.decel_r: decel_d = move.decel_r * axis_d step_const(move_time, start_pos, decel_d, cruise_v, -accel) + # Dual carriage support + def _activate_carriage(self, carriage): + toolhead = self.printer.lookup_object('toolhead') + toolhead.get_last_move_time() + dc_stepper = self.dual_carriage_steppers[carriage] + dc_axis = self.dual_carriage_axis + self.steppers[dc_axis] = dc_stepper + extruder_pos = toolhead.get_position()[3] + toolhead.set_position(self.get_position() + [extruder_pos]) + if self.limits[dc_axis][0] <= self.limits[dc_axis][1]: + self.limits[dc_axis] = ( + dc_stepper.position_min, dc_stepper.position_max) + self.need_motor_enable = True + cmd_SET_DUAL_CARRIAGE_help = "Set which carriage is active" + def cmd_SET_DUAL_CARRIAGE(self, params): + gcode = self.printer.lookup_object('gcode') + carriage = gcode.get_int('CARRIAGE', params) + if carriage not in (0, 1): + raise gcode.error("Invalid carriage") + self._activate_carriage(carriage) + gcode.reset_last_position()