diff --git a/config/sample-iqex.cfg b/config/sample-iqex.cfg new file mode 100644 index 000000000..89f5e2521 --- /dev/null +++ b/config/sample-iqex.cfg @@ -0,0 +1,348 @@ +# This file contains a configuration snippet for a quad extruder printer +# with two independent gantries each having two independent tools. + +# See docs/Config_Reference.md for a description of parameters. +# Test config for generic cartesian kinematics with quad independent extruders + +[mcu] +serial: /dev/serial/by-id/usb-Klipper_Klipper_firmware_12345-if00 + +[mcu auxboard] +serial: /dev/serial/by-id/usb-Klipper_Klipper_firmware_67890-if11 + +[carriage carriage_t0] +axis: x +position_endstop: 0 +position_max: 300 +homing_speed: 50 +endstop_pin: auxboard:PG6 + +[dual_carriage carriage_t1] +primary_carriage: carriage_t0 +safe_distance: 70 +position_endstop: 300 +position_max: 300 +homing_speed: 50 +endstop_pin: auxboard:PG9 + +[dual_carriage carriage_t2] +axis: x +position_endstop: 0 +position_max: 300 +homing_speed: 50 +endstop_pin: auxboard:PG10 + +[dual_carriage carriage_t3] +primary_carriage: carriage_t2 +safe_distance: 70 +position_endstop: 300 +position_max: 300 +homing_speed: 50 +endstop_pin: auxboard:PG11 + +[carriage carriage_gantry0] +axis: y +position_endstop: 0 +position_max: 200 +homing_speed: 50 +endstop_pin: PG6 + +[dual_carriage carriage_gantry1] +primary_carriage: carriage_gantry0 +safe_distance: 50 +position_endstop: 200 +position_max: 200 +homing_speed: 50 +endstop_pin: PG10 + +[carriage carriage_z] +axis: z +position_endstop: 0.5 +position_max: 100 +endstop_pin: PG12 + +[stepper stepper_t0_x] +carriages: carriage_t0 +step_pin: auxboard:PF13 +dir_pin: auxboard:PF12 +enable_pin: !auxboard:PF14 +microsteps: 16 +rotation_distance: 40 + +[stepper stepper_t1_x] +carriages: carriage_t1 +step_pin: auxboard:PG0 +dir_pin: auxboard:PG1 +enable_pin: !auxboard:PF15 +microsteps: 16 +rotation_distance: 40 + +[stepper stepper_t2_x] +carriages: carriage_t2 +step_pin: auxboard:PF11 +dir_pin: auxboard:PG3 +enable_pin: !auxboard:PG5 +microsteps: 16 +rotation_distance: 40 + +[stepper stepper_t3_x] +carriages: carriage_t3 +step_pin: auxboard:PG4 +dir_pin: auxboard:PC1 +enable_pin: !auxboard:PA2 +microsteps: 16 +rotation_distance: 40 + +[stepper gantry0] +carriages: carriage_gantry0 +step_pin: PF13 +dir_pin: PF12 +enable_pin: !PF14 +microsteps: 16 +rotation_distance: 40 + +[stepper gantry1] +carriages: carriage_gantry1 +step_pin: PF11 +dir_pin: PG3 +enable_pin: !PG5 +microsteps: 16 +rotation_distance: 40 + +[stepper z0] +carriages: carriage_z0 +step_pin: PF9 +dir_pin: PF10 +enable_pin: !PG2 +microsteps: 16 +rotation_distance: 8 + +[extruder] +step_pin: auxboard:PF9 +dir_pin: auxboard:PF10 +enable_pin: !auxboard:PG2 +heater_pin: auxboard:PA0 +sensor_pin: auxboard:PF4 +microsteps: 16 +rotation_distance: 33.500 +nozzle_diameter: 0.400 +filament_diameter: 1.750 +sensor_type: EPCOS 100K B57560G104F +control: pid +pid_Kp: 22.2 +pid_Ki: 1.08 +pid_Kd: 114 +min_temp: 0 +max_temp: 250 + +[extruder1] +step_pin: auxboard:PC13 +dir_pin: auxboard:PF0 +enable_pin: !auxboard:PF1 +heater_pin: auxboard:PA3 +sensor_pin: auxboard:PF5 +microsteps: 16 +rotation_distance: 33.5 +nozzle_diameter: 0.400 +filament_diameter: 1.750 +sensor_type: EPCOS 100K B57560G104F +control: pid +pid_Kp: 22.2 +pid_Ki: 1.08 +pid_Kd: 114 +min_temp: 0 +max_temp: 250 + +[extruder2] +step_pin: auxboard:PE2 +dir_pin: auxboard:PE3 +enable_pin: !auxboard:PD4 +heater_pin: auxboard:PB0 +sensor_pin: auxboard:PF6 +microsteps: 16 +rotation_distance: 33.5 +nozzle_diameter: 0.400 +filament_diameter: 1.750 +sensor_type: EPCOS 100K B57560G104F +control: pid +pid_Kp: 22.2 +pid_Ki: 1.08 +pid_Kd: 114 +min_temp: 0 +max_temp: 250 + +[extruder3] +step_pin: auxboard:PE6 +dir_pin: auxboard:PA14 +enable_pin: !auxboard:PE0 +heater_pin: auxboard:PB11 +sensor_pin: auxboard:PF7 +microsteps: 16 +rotation_distance: 33.5 +nozzle_diameter: 0.400 +filament_diameter: 1.750 +sensor_type: EPCOS 100K B57560G104F +control: pid +pid_Kp: 22.2 +pid_Ki: 1.08 +pid_Kd: 114 +min_temp: 0 +max_temp: 250 + +[gcode_macro PARK_EXTRUDERS] +gcode: + G90 + SET_DUAL_CARRIAGE CARRIAGE=carriage_gantry1 + G1 Y{printer.configfile.settings["dual_carriage carriage_gantry1"].position_max} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_gantry0 + G1 Y{printer.configfile.settings["carriage carriage_gantry0"].position_min} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t3 + G1 X{printer.configfile.settings["dual_carriage carriage_t3"].position_max} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t1 + G1 X{printer.configfile.settings["dual_carriage carriage_t1"].position_max} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t2 + G1 X{printer.configfile.settings["dual_carriage carriage_t2"].position_min} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t0 + G1 X{printer.configfile.settings["carriage carriage_t0"].position_min} F12000 + +[gcode_macro T0] +gcode: + PARK_EXTRUDERS + ACTIVATE_EXTRUDER EXTRUDER=extruder + SET_DUAL_CARRIAGE CARRIAGE=carriage_gantry0 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t0 + +[gcode_macro T1] +gcode: + PARK_EXTRUDERS + SYNC_EXTRUDER_MOTION EXTRUDER=extruder1 MOTION_QUEUE=extruder1 + ACTIVATE_EXTRUDER EXTRUDER=extruder1 + SET_DUAL_CARRIAGE CARRIAGE=carriage_gantry0 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t1 + +[gcode_macro T2] +gcode: + PARK_EXTRUDERS + SYNC_EXTRUDER_MOTION EXTRUDER=extruder2 MOTION_QUEUE=extruder2 + ACTIVATE_EXTRUDER EXTRUDER=extruder2 + SET_DUAL_CARRIAGE CARRIAGE=carriage_gantry1 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t2 + +[gcode_macro T3] +gcode: + PARK_EXTRUDERS + SYNC_EXTRUDER_MOTION EXTRUDER=extruder3 MOTION_QUEUE=extruder3 + ACTIVATE_EXTRUDER EXTRUDER=extruder3 + SET_DUAL_CARRIAGE CARRIAGE=carriage_gantry1 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t2 + +[gcode_macro SET_COPY_MODE] +gcode: + G90 + {% set y_center = 0.5 * (printer.configfile.settings["dual_carriage carriage_gantry1"].position_max + printer.configfile.settings["carriage carriage_gantry0"].position_min) %} + {% set x_max = [printer.configfile.settings["dual_carriage carriage_t3"].position_max, printer.configfile.settings["dual_carriage carriage_t1"].position_max]|min %} + {% set x_min = [printer.configfile.settings["dual_carriage carriage_t2"].position_min, printer.configfile.settings["carriage carriage_t0"].position_min]|max %} + {% set x_center = 0.5 * (x_max + x_min) %} + SET_DUAL_CARRIAGE CARRIAGE=carriage_gantry0 + G1 Y{printer.configfile.settings["carriage carriage_gantry0"].position_min} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_gantry1 + G1 Y{y_center} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t2 + G1 X{printer.configfile.settings["dual_carriage carriage_t2"].position_min} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t0 + G1 X{printer.configfile.settings["carriage carriage_t0"].position_min} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t3 + G1 X{x_center} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t1 + G1 X{x_center} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t0 MODE=PRIMARY + SET_DUAL_CARRIAGE CARRIAGE=carriage_t1 MODE=COPY + SET_DUAL_CARRIAGE CARRIAGE=carriage_t2 MODE=COPY + SET_DUAL_CARRIAGE CARRIAGE=carriage_t3 MODE=COPY + SET_DUAL_CARRIAGE CARRIAGE=carriage_gantry0 MODE=PRIMARY + SET_DUAL_CARRIAGE CARRIAGE=carriage_gantry1 MODE=COPY + ACTIVATE_EXTRUDER EXTRUDER=extruder + SYNC_EXTRUDER_MOTION EXTRUDER=extruder1 MOTION_QUEUE=extruder + SYNC_EXTRUDER_MOTION EXTRUDER=extruder2 MOTION_QUEUE=extruder + SYNC_EXTRUDER_MOTION EXTRUDER=extruder3 MOTION_QUEUE=extruder + +[gcode_macro SET_MIRROR_MODE1] +gcode: + G90 + {% set y_center = 0.5 * (printer.configfile.settings["dual_carriage carriage_gantry1"].position_max + printer.configfile.settings["carriage carriage_gantry0"].position_min) %} + SET_DUAL_CARRIAGE CARRIAGE=carriage_gantry0 + G1 Y{printer.configfile.settings["carriage carriage_gantry0"].position_min} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_gantry1 + G1 Y{y_center} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t2 + G1 X{printer.configfile.settings["dual_carriage carriage_t2"].position_min} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t0 + G1 X{printer.configfile.settings["carriage carriage_t0"].position_min} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t3 + G1 X{printer.configfile.settings["dual_carriage carriage_t3"].position_max} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t1 + G1 X{printer.configfile.settings["dual_carriage carriage_t1"].position_max} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t0 MODE=PRIMARY + SET_DUAL_CARRIAGE CARRIAGE=carriage_t1 MODE=MIRROR + SET_DUAL_CARRIAGE CARRIAGE=carriage_t2 MODE=COPY + SET_DUAL_CARRIAGE CARRIAGE=carriage_t3 MODE=MIRROR + SET_DUAL_CARRIAGE CARRIAGE=carriage_gantry0 MODE=PRIMARY + SET_DUAL_CARRIAGE CARRIAGE=carriage_gantry1 MODE=COPY + ACTIVATE_EXTRUDER EXTRUDER=extruder + SYNC_EXTRUDER_MOTION EXTRUDER=extruder1 MOTION_QUEUE=extruder + SYNC_EXTRUDER_MOTION EXTRUDER=extruder2 MOTION_QUEUE=extruder + SYNC_EXTRUDER_MOTION EXTRUDER=extruder3 MOTION_QUEUE=extruder + +[gcode_macro SET_MIRROR_MODE2] +gcode: + G90 + {% set x_max = [printer.configfile.settings["dual_carriage carriage_t3"].position_max, printer.configfile.settings["dual_carriage carriage_t1"].position_max]|min %} + {% set x_min = [printer.configfile.settings["dual_carriage carriage_t2"].position_min, printer.configfile.settings["carriage carriage_t0"].position_min]|max %} + {% set x_center = 0.5 * (x_max + x_min) %} + SET_DUAL_CARRIAGE CARRIAGE=carriage_gantry0 + G1 Y{printer.configfile.settings["carriage carriage_gantry0"].position_min} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_gantry1 + G1 Y{printer.configfile.settings["dual_carriage carriage_gantry1"].position_max} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t2 + G1 X{printer.configfile.settings["dual_carriage carriage_t2"].position_min} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t0 + G1 X{printer.configfile.settings["carriage carriage_t0"].position_min} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t3 + G1 X{x_center} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t1 + G1 X{x_center} F12000 + SET_DUAL_CARRIAGE CARRIAGE=carriage_t0 MODE=PRIMARY + SET_DUAL_CARRIAGE CARRIAGE=carriage_t1 MODE=COPY + SET_DUAL_CARRIAGE CARRIAGE=carriage_t2 MODE=COPY + SET_DUAL_CARRIAGE CARRIAGE=carriage_t3 MODE=COPY + SET_DUAL_CARRIAGE CARRIAGE=carriage_gantry0 MODE=PRIMARY + SET_DUAL_CARRIAGE CARRIAGE=carriage_gantry1 MODE=MIRROR + ACTIVATE_EXTRUDER EXTRUDER=extruder + SYNC_EXTRUDER_MOTION EXTRUDER=extruder1 MOTION_QUEUE=extruder + SYNC_EXTRUDER_MOTION EXTRUDER=extruder2 MOTION_QUEUE=extruder + SYNC_EXTRUDER_MOTION EXTRUDER=extruder3 MOTION_QUEUE=extruder + +[printer] +kinematics: generic_cartesian +max_velocity: 300 +max_accel: 3000 +max_z_velocity: 5 +max_z_accel: 100 + +## An optional input shaper support +#[input_shaper] +## The section is intentionally empty +# +#[delayed_gcode init_shaper] +#initial_duration: 0.1 +#gcode: +# SET_DUAL_CARRIAGE CARRIAGE=carriage_gantry1 +# SET_DUAL_CARRIAGE CARRIAGE=carriage_t3 +# SET_INPUT_SHAPER SHAPER_TYPE_X= SHAPER_FREQ_X= SHAPER_TYPE_Y= SHAPER_FREQ_Y= +# SET_DUAL_CARRIAGE CARRIAGE=carriage_t2 +# SET_INPUT_SHAPER SHAPER_TYPE_X= SHAPER_FREQ_X= SHAPER_TYPE_Y= SHAPER_FREQ_Y= +# SET_DUAL_CARRIAGE CARRIAGE=carriage_gantry0 +# SET_DUAL_CARRIAGE CARRIAGE=carriage_t1 +# SET_INPUT_SHAPER SHAPER_TYPE_X= SHAPER_FREQ_X= SHAPER_TYPE_Y= SHAPER_FREQ_Y= +# SET_DUAL_CARRIAGE CARRIAGE=carriage_t0 +# SET_INPUT_SHAPER SHAPER_TYPE_X= SHAPER_FREQ_X= SHAPER_TYPE_Y= SHAPER_FREQ_Y= diff --git a/docs/Config_Changes.md b/docs/Config_Changes.md index 6846e034d..4af463243 100644 --- a/docs/Config_Changes.md +++ b/docs/Config_Changes.md @@ -8,6 +8,10 @@ All dates in this document are approximate. ## Changes +20251229: `SET_DUAL_CARRIAGE CARRIAGE= MODE=` +command for modes `COPY` and `MIRROR` now requires that another +carriage is activated as `PRIMARY` first for the corresponding axis. + 20251122: An option `axis` has been added to `[carriage ]` sections for `generic_cartesian` kinematics, allowing arbitrary names for primary carriages. Users are encouraged to explicitly specify `axis` option now. diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index e722a1465..a19f1a4b9 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -2445,6 +2445,12 @@ axis: # See the "stepper" section for the definition of the above parameters. ``` +`[dual_carriage]` is also supported with `generic_cartesian` kinematic, +in which case it can be thought of as any additional carriage of an axis +that can be moved independently from the primary `[carriage]` of that axis. +The main difference between `[carriage]` and `[dual_carriage]` is that +the latter is not activated by default after the printer startup or +homing, and must be enabled explicitly via `SET_DUAL_CARRIAGE` command. For an example of dual carriage configuration with `generic_cartesian` kinematic, see the following configuration [sample](../config/example-generic-caretesian.cfg). @@ -2524,9 +2530,12 @@ Note that `SHAPER_TYPE_Y` and `SHAPER_FREQ_Y` must be the same in both commands in this case, since the same motors drive Y axis when either of the `carriage_x` and `carriage_u` carriages are active. -It is worth noting that `generic_cartesian` kinematic can support two -dual carriages for X and Y axes. For reference, see for instance a -[sample](../config/sample-corexyuv.cfg) of CoreXYUV configuration. +It is worth noting that `generic_cartesian` kinematic can support more +than a single `[dual_carriage]`, e.g. dual-gantry setups with one or +two independent carriages per gantry. For reference, see for instance +[CoreXYUV](../config/sample-corexyuv.cfg) or +[IQEX](../config/sample-iqex.cfg) (independent quad extruders) +printers sample configurations. ### [extruder_stepper] diff --git a/klippy/kinematics/idex_modes.py b/klippy/kinematics/idex_modes.py index 4fe6df830..36c32628d 100644 --- a/klippy/kinematics/idex_modes.py +++ b/klippy/kinematics/idex_modes.py @@ -244,6 +244,7 @@ class DualCarriages: cmd_SET_DUAL_CARRIAGE_help = "Configure the dual carriages mode" def cmd_SET_DUAL_CARRIAGE(self, gcmd): carriage_str = gcmd.get('CARRIAGE', None) + index = None if carriage_str is None: raise gcmd.error('CARRIAGE must be specified') if carriage_str in self.dc_rails: @@ -265,9 +266,6 @@ class DualCarriages: if mode not in self.VALID_MODES: raise gcmd.error("Invalid mode=%s specified" % (mode,)) if mode in [COPY, MIRROR]: - if self.primary_mode_dcs[dc_rail.axis] in [None, dc_rail]: - raise gcmd.error( - "Must activate another carriage as PRIMARY first") curtime = self.printer.get_reactor().monotonic() kin = self.printer.lookup_object('toolhead').get_kinematics() axis = 'xyz'[dc_rail.axis] @@ -275,6 +273,12 @@ class DualCarriages: raise gcmd.error( "Axis %s must be homed prior to enabling mode=%s" % (axis.upper(), mode)) + if index is not None: + self.toggle_active_dc_rail( + self.get_dc_rail_wrapper(dc_rail.dual_rail)) + elif self.primary_mode_dcs[dc_rail.axis] in [None, dc_rail]: + raise gcmd.error( + "Must activate another carriage as PRIMARY first") self.activate_dc_mode(dc_rail, mode) cmd_SAVE_DUAL_CARRIAGE_STATE_help = \ "Save dual carriages modes and positions"