diff --git a/docs/Config_Changes.md b/docs/Config_Changes.md index 063af8ac390f..6d83238d0295 100644 --- a/docs/Config_Changes.md +++ b/docs/Config_Changes.md @@ -8,6 +8,15 @@ All dates in this document are approximate. ## Changes +20230826: If `safe_distance` is set or calculated to be 0 in `[dual_carriage]`, +the carriages proximity checks will be disabled as per documentation. A user +may wish to configure `safe_distance` explicitly to prevent accidental crashes +of the carriages with each other. Additionally, the homing order of the primary +and the dual carriage is changed in some configurations (certain configurations +when both carriages home in the same direction, see +[[dual_carriage] configuration reference](./Config_Reference.md#dual_carriage) +for more details). + 20230810: The flash-sdcard.sh script now supports both variants of the Bigtreetech SKR-3, STM32H743 and STM32H723. For this, the original tag of btt-skr-3 now has changed to be either btt-skr-3-h743 or btt-skr-3-h723. diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index d0b617a36e40..f6a2b986faf8 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -2044,6 +2044,14 @@ in this section (CARRIAGE=0 will return activation to the primary carriage). Dual carriage support is typically combined with extra extruders - the SET_DUAL_CARRIAGE command is often called at the same time as the ACTIVATE_EXTRUDER command. Be sure to park the carriages during deactivation. +Note that during G28 homing, typically the primary carriage is homed first +followed by the carriage defined in the `[dual_carriage]` config section. +However, the `[dual_carriage]` carriage will be homed first if both carriages +home in a positive direction and the [dual_carriage] carriage has a +`position_endstop` greater than the primary carriage, or if both carriages home +in a negative direction and the `[dual_carriage]` carriage has a +`position_endstop` less than the primary carriage. + Additionally, one could use "SET_DUAL_CARRIAGE CARRIAGE=1 MODE=COPY" or "SET_DUAL_CARRIAGE CARRIAGE=1 MODE=MIRROR" commands to activate either copying or mirroring mode of the dual carriage, in which case it will follow the diff --git a/klippy/kinematics/idex_modes.py b/klippy/kinematics/idex_modes.py index b54f96579c9b..2ce91afe85a5 100644 --- a/klippy/kinematics/idex_modes.py +++ b/klippy/kinematics/idex_modes.py @@ -65,7 +65,13 @@ def toggle_active_dc_rail(self, index, override_rail=False): kin.update_limits(self.axis, target_dc.get_rail().get_range()) def home(self, homing_state): kin = self.printer.lookup_object('toolhead').get_kinematics() - for i, dc_rail in enumerate(self.dc): + enumerated_dcs = list(enumerate(self.dc)) + if (self.get_dc_order(0, 1) > 0) != \ + self.dc[0].get_rail().get_homing_info().positive_dir: + # The second carriage must home first, because the carriages home in + # the same direction and the first carriage homes on the second one + enumerated_dcs.reverse() + for i, dc_rail in enumerated_dcs: self.toggle_active_dc_rail(i, override_rail=True) kin.home_axis(homing_state, self.axis, dc_rail.get_rail()) # Restore the original rails ordering @@ -78,9 +84,15 @@ def get_kin_range(self, toolhead, mode): axes_pos = [dc.get_axis_position(pos) for dc in self.dc] dc0_rail = self.dc[0].get_rail() dc1_rail = self.dc[1].get_rail() - range_min = dc0_rail.position_min - range_max = dc0_rail.position_max + if mode != PRIMARY or self.dc[0].is_active(): + range_min = dc0_rail.position_min + range_max = dc0_rail.position_max + else: + range_min = dc1_rail.position_min + range_max = dc1_rail.position_max safe_dist = self.safe_dist + if not safe_dist: + return (range_min, range_max) if mode == COPY: range_min = max(range_min, @@ -88,7 +100,7 @@ def get_kin_range(self, toolhead, mode): range_max = min(range_max, axes_pos[0] - axes_pos[1] + dc1_rail.position_max) elif mode == MIRROR: - if dc0_rail.get_homing_info().positive_dir: + if self.get_dc_order(0, 1) > 0: range_min = max(range_min, 0.5 * (sum(axes_pos) + safe_dist)) range_max = min(range_max, @@ -102,14 +114,39 @@ def get_kin_range(self, toolhead, mode): # mode == PRIMARY active_idx = 1 if self.dc[1].is_active() else 0 inactive_idx = 1 - active_idx - if active_idx: - range_min = dc1_rail.position_min - range_max = dc1_rail.position_max - if self.dc[active_idx].get_rail().get_homing_info().positive_dir: + if self.get_dc_order(active_idx, inactive_idx) > 0: range_min = max(range_min, axes_pos[inactive_idx] + safe_dist) else: range_max = min(range_max, axes_pos[inactive_idx] - safe_dist) + if range_min > range_max: + # During multi-MCU homing it is possible that the carriage + # position will end up below position_min or above position_max + # if position_endstop is too close to the rail motion ends due + # to inherent latencies of the data transmission between MCUs. + # This can result in an invalid range_min > range_max range + # in certain modes, which may confuse the kinematics code. + # So, return an empty range instead, which will correctly + # block the carriage motion until a different mode is selected + # which actually permits carriage motion. + return (range_min, range_min) return (range_min, range_max) + def get_dc_order(self, first, second): + if first == second: + return 0 + # Check the relative order of the first and second carriages and + # return -1 if the first carriage position is always smaller + # than the second one and 1 otherwise + first_rail = self.dc[first].get_rail() + second_rail = self.dc[second].get_rail() + first_homing_info = first_rail.get_homing_info() + second_homing_info = second_rail.get_homing_info() + if first_homing_info.positive_dir != second_homing_info.positive_dir: + # Carriages home away from each other + return 1 if first_homing_info.positive_dir else -1 + # Carriages home in the same direction + if first_rail.position_endstop > second_rail.position_endstop: + return 1 + return -1 def activate_dc_mode(self, index, mode): toolhead = self.printer.lookup_object('toolhead') toolhead.flush_step_generation()