How to go out of print area for tool change?

Basic Information:

Printer Model: CoreXY
MCU / Printerboard: Not relevant
`klippy.log not relevant

Describe your issue:

Hi, new to the klipper here. I haven’t found how to go around this:
To make it short, in gcode macro, how to get out of print area for tool change.(defined in config.cfg stepper_x or _y : position_max: )

So far I have had to extend the position_max out of the actual print area. But in long run, I don’t wan’t to be possible to accidentally command, or try to print to the tool parking area. Crashing to the tools can do damages.

Is there command to bypass the position_max value temporarily? (I know, this should not be the way to go)
Can I manipulate the positon_max in macro temporarily and then restore after tool chg?
Is there a config setting for actual printable area?
Is there setting for “no go” area, but accessible with permission?

If there is no solution, this could be a feature request. Could be helpful for other features too, like nozzle cleaning. In my opinion, tool parking area should be somehow protected in normal operation.

Best Regards

Print area is controlled by the slicer, not the firmware. All the firmware does is define the physical limits of each axis.

I’m pretty sure that the firmware also prevents the gcode from actually causing the tool head to go outside the defined “physical limits”, right? So I think what OP is asking is how to use different “physical limits” definitions while printing and while changing tools.

1 Like

Yes, that is what I’m looking for and would be good feature.

Unbreakable limits for the machine (as they are now, position_min / _max)
Additional llimits that could be defined but bypass when needed. Should be <positon_max OR >Position_min.

Something like:

position_min= 0
position_max= 250

position_min= 0
position_max= 300

print_area_min: 0, 0                      #default stepper_x position_min, stepper_y position_min
print_area_max: 250, 210             #default stepper_x position_max, stepper_y position_max

#error produced if defined outside of machine limits

[cgode macro tool_change]
   SAVE_GCODE_STATE NAME=state_tool_change
   G0 X20 Y230                         #tool change code


If there is a user that don’t care to define slicer print area, one could easily and accidentally try to print to tool park area = mayhem

Manual move does not care about slicer.
The buttons/menus in (whatever) UI, its easy to confuse and run Y100 when he meant to go Y10 because failed to select 10mm movement instead of 100mm and rams 300mm/s to the tools parked = Mayhem.

Also it would be easier to code automatic defintions, without need to take the tool change area in to account.

Like define center point of print area. Should be easy as:
Center point = Print area X / 2, Y / 2

As @jakep_82 already indicated: Presently this is not foreseen. Klipper’s architecture requires:

  • Physical machine limits (that should stay immutable, since well, they are the physical limits) are defined in Klipper
  • All movements and actions within these limits are defined in the slicer. This is exactly the reason why slicers already offer machine dependent profiles to care for such

My personal opinion is that it belongs into the realm of slicers and has no real benefit in a firmware. Each puzzle piece in the 3D printing process chain shall care for their core competency and not start duplicating stuff. There is already enough “grey area” out of history.

If you like then you can consider this a feature request and hope to find a developer willing to dig into it.

@mattiras have you try to make the print area in variable ? so you can add in your tool changing scripts/macros (I come to klipper from RRF) there are 3 files tfree tpre tpost and you can change in tfree in first line your tool_move_area and in tpost you can after picking your tool is done change back to print_save area. Maybe this help you I can show you e3d tool changer scripts for duet3d to get more inspired.

Thanks for the input.

Yes, I have custom print area variables, and the tool change macros are working.
But they don’t prevent accidental G1/G0 moves to tool park area, which in my consept = trouble.

I can live with the idea that the slicer is always configured correctly (in perfect world)

But still, Slicer has NOTHING to do with moves made other ways (say, by the user) in printer.
I don’t understand that the slicer is to be held responsible of moves in printer when it comes to implementing tools. Specially I should have means to prevent accidental manual moves (which WILL happen to everyone, not one time, not twice but many times) to tool park area.

I totally understand that the Min/Max are there to protect the machine.
But not the tools. Aren’t the tools part of the machine?

English is not my native language, I hope my input here does not seem too harsh.
Klipper is great and flexible firmware but I just disagree here. Could this thread be changed to be FR?

EDIT BTW. I’m converting my machine from Marlin, there the tool change is executed outside of defined min/max by default. And the machine prevented G1/G0 commands to go to tool park area.

1 Like

This is simply not true. E.g., when I move the gantry using the buttons in mainsail it has absolutely nothing to do with the slicer.

I’m with @mattiras on this one; there are cases where you would want to have two (or more) sets of “physical limits”, and ability to switch between them somehow (if not automatically/procedurally then at least manually using some macro). Maybe you have some toolchanger, or maybe you have asymmetric idex heads (i.e., the left head can’t go fully to the right, and/or vice versa), or perhaps you have a delta printer where the print area is smaller high up, or you have some cleaning brush or poop-chute or heating system at some height range, or whatever. The fact is, this can very well be a feature that is immensely useful for many people in multiple different situations.

Marcus, thanks.

Or do I need to open a new one there?

This is how I do it with my T-Rex 3 IDEX:

Print area is 400 mm x 400 mm
Endstop for T0 is X = -50
Endstop for T1 is X = 450

So the print area is still 400 mm x 400 mm
But the tool change macro moves the tools heads to their positions outside the print area

Either change this post to the “Features” Category or create a new one.

But let me consult my magic crystal ball: …The mists of the distant future are clearing up…I see…I see…<scared gasp (for the drama)>…very limited developer resources…tons of features with a higher impact still undeveloped…then…nothing

Just kidding. Good luck :wink:

1 Like

I can totally get behind the idea that certain functionality belongs in a certain place and not try to duplicate functionality “just in case” someone might think of a reason.

But for this request I’m on the side of allowing the firmware to move outside the defined limits. There are many real life, non-hypothetical reasons to allow this. I’ll add my own to the list @marcus-in-3d already made:

I have dual piggyback z steppers on my gantry. I’ve placed carefully measured hard stops at the upper extreme of the vertical limit. To level the gantry I drive the z axis up and into the hard stops, the steppers skip as many steps as they need to until it’s perfectly level, and then I re-home the z. In regular printing the z axis has no business going that high, in fact I want the firmware to throw an error if it tries to go out of bounds. But to level the gantry I need some way to forcibly drive into the hard stops.

In marlin there are codes to turn off/on “soft end stops” (M211). It’s up to the user to not be stupid when the end stops are turned off. This would be my preferred implementation, but I’m sure it’s not the only way to address it.

While this post has nothing to do with ignoring the limits and crashing the printer but rather defining different “keep in” / “keep out” areas within the physical limits and while I think this is a highly debatable way of leveling (please don’t! It’s your printer, do what you want with it) you can probably achieve this with set_kinematic_position

In RRF its east to do as you can change the limits with an m code.

The process would be something like this.

  1. move the printhead to a designated position inside the current max/min limits
  2. run an M208 to see the new limits (print head has to already be inside this)
  3. move around within the new limits, and do what you need to do.
  4. move back to the original position used in step #1
  5. run an M208 to set the limits to the original values.
  6. continue on with a print

this works for purge buckets tool changes, dockable probes etc, and ensures no matter what you are doing you can never crash the print head (assuming you defined the limits properly).

Imo, it is disappointing that klipper doesn’t already support this.

I should add, SET_KINEMATIC_POSITION doesn’t give me warm fuzzy feelings.

from the documentation

Setting an incorrect or invalid position may lead to internal software errors. This command may invalidate future boundary checks; issue a G28 afterwards to reset the kinematics.

1 Like

Since @Sineos mentioned set_kinematic_position, I decided to play around with it to see what was possible.

The following is a mostly working purge routine. I say mostly because it seems like maybe 1 time out of 5 Klipper goes stupid, and swaps the x and y axis around. what’s odd about it, is that the swap happens before SET_KINEMATIC_POSITION comes into play. it almost seems like a race condition or something be run non sequentially.

The sequence of commands I’m using are.

G28 Z
# Macro Parameters
# amount - How much filament to extrude. The default is 10 mm.
# rate - How fast the filament should be extruded. The default is 5 mm/s.
# wipes - the number of wipe iterations to perform The default is 2.
# temp -  the temp the extruder needs to be brought to if it needs to change
# ret - should the toolhead return to the point it was before a purge was 
#         commanded. The default is false. 
[gcode_macro PURGE]
description: Runs a purge routine 
   {% set ammount = params.AMOUNT|default(10)|float %}
   {% set rate = params.RATE|default(5)|float %}
   {% set wipes = params.WIPES|default(2)|int %}
   {% set temp = params.TEMP|default(-1)|float %}
   {% set ret = params.RET|default("false")|string %}
   {% set moveVelocity = printer.toolhead.max_velocity * 60 %}
   {% set extrudeVelocity = rate * 60 %}
   {% set minTemp = printer.configfile.config['extruder'].min_extrude_temp|int %}

   # the point at the limit of the bed where the kinematic position change will take place
   {% set refPointA = {
       'x': 0.25, 
       'y': 135} 

   # when  the print head reaches refPointA SET_KINEMATIC_POSITION will swap to this value
   # this is so when the purge and wipe tasks don't accidentally move out of range  
   {% set refPointB = {
       'x': 150, 
       'y': 150} 

   # the point where a purge will take place, the values are relative to refPointA/refPointB   
   {% set purgePoint = {
       'x': -25.25, 
       'y': -30} 

   # the length of a wipe pass
   {% set wipeDistance = 60 %}   

   # input validation
   {% if temp > 0.0  and  temp <  minTemp %}
       {action_raise_error("temp must be greater than or equal to the minimum extruder temp")}
   {% endif %}

   {% if ret != "true" and  ret != "false" %}
       {action_raise_error("ret must be 'true' or 'false'")}
   {% endif %}
   {% if ret == "true" %}
       {% set ret = True %}
   {% else %}
       {% set ret = False %}
   {% endif %}

   # start processing and generating gcode
   {% set StartPoint = printer.toolhead.position %}

   # move the toolhead into position
   G0 X{refPointA.x} Y{refPointA.y} F{moveVelocity}
   SET_KINEMATIC_POSITION X={refPointB.x} Y={refPointB.y} Z={StartPoint.z}
   G0 X{refPointB.x + purgePoint.x} Y{refPointB.y + purgePoint.y}

   # change the temp if needed
   {% if temp > 0.0 %}
       M109 S{temp}
   {% endif %}

   # conditionally purge
   {% if ammount > 0 %}
       G1 E{ammount} F{extrudeVelocity}
   {% endif %}

   # wipe
   {% for wipe in range(wipes) %}
       G0 Y{refPointB.y + purgePoint.y + wipeDistance} F{moveVelocity}
       G0 Y{refPointB.y + purgePoint.y}
   {% endfor %}

   # return to refPointA/refPointB
   G0 X{refPointB.x} Y{refPointB.y}
   SET_KINEMATIC_POSITION X={refPointA.x} Y={refPointA.y} Z={StartPoint.z}

   {% if ret %}
       G0 X{StartPoint.x} Y{StartPoint.y}
   {% endif %}

   G92 E{StartPoint.e}


The issue seems to be related to some kind of race condition when calling G28. adding an m400 between the G28 and calling the macros fixed the axis swapping issue.

Hmm, I seem to have a similar problem as the OP, but with the added complication of having a Delta printer. Delta printers are constrained to print within a cylinder even though the Delta mechanism can stretch out a fair bit further between the towers.

What I need to do is have a parking dock out of the normal print range of the printer where the sled of a dockable Z probe can be parked. What would be nice is a command such as FORCE_MOVE_XYZ, but the existing FORCE_MOVE addresses the stepper motors.

I remember seeing a Delta printer with a nearly triangular bed at the TCT show (Birmingham) some years ago, so it is at least possible.