Z Axis Probing Issues

Basic Information:

Printer Model: Custom CoreXY
MCU / Printerboard: rPi 4B, Octopus V1.1 & FLY SHT42
klippy.log (851.2 KB)

I have two issues that need to be addressed when trying to implement sensorless homing that will allow SAVE_CONFIG updates from functions like SHAPER_CONFIGURATION. This is a continuation to Problem with SHAPER_CALIBRATE & TMC2209s with Sensorless Homing

1. Updating printer.toolhead.homed_axes after single axis homing:

I’m using the following code to implement TMC2209 sensorless homing on a CoreXY printer by overriding the G28 gcode. I want to be able to home individual axes (which is problematic to do when using [homing_override] - I couldn’t get reliable single axis requests received):

[gcode_macro _HOME_X]
gcode:
    M118 Execute _HOME_X
    # Always use consistent run_current on A/B steppers during sensorless homing
    {% set RUN_CURRENT  = printer.configfile.settings['tmc2209 stepper_x'].run_current|float %}
    {% set HOME_CURRENT = 0.7 %}
    SET_TMC_CURRENT STEPPER=stepper_x CURRENT={HOME_CURRENT}
    # Wait just a second… (give StallGuard registers time to clear)
    G4 P1000
    # Home
    G28.1 X
    # Move away
    G91
    G1 X-10 F1200
    # Wait just a second… (give StallGuard registers time to clear)
    G4 P1000
    # Set current during print
    SET_TMC_CURRENT STEPPER=stepper_x CURRENT={RUN_CURRENT}

[gcode_macro _HOME_Y]
gcode:
    M118 Execute _HOME_Y
    # Set current for sensorless homing
    {% set RUN_CURRENT  = printer.configfile.settings['tmc2209 stepper_y'].run_current|float %}
    {% set HOME_CURRENT = 0.7 %}
    SET_TMC_CURRENT STEPPER=stepper_y CURRENT={HOME_CURRENT}
    # Wait just a second… (give StallGuard registers time to clear)
    G4 P1000
    # Home
    G28.1 Y
    # Move away
    G91
    G1 Y+10 F1200
    # Wait just a second… (give StallGuard registers time to clear)
    G4 P1000
    # Set current during print
    SET_TMC_CURRENT STEPPER=stepper_y CURRENT={RUN_CURRENT}

[gcode_macro G28]
rename_existing: G28.1
gcode:
    {% if   "x"   == printer.toolhead.homed_axes %}  #### - Output Current State
        M118 X Homed Upon Entry
    {% elif "xy"  == printer.toolhead.homed_axes %}
        M118 XY Homed Upon Entry
    {% elif "xyz" == printer.toolhead.homed_axes %}
        M118 All Homed Upon Entry
    {% else %}
        M118 Nothing Homed Upon Entry
    {% endif %}
    {% if 'X' in params %}                           #### - Output G28 gcode
        M118 "G28 X"
    {% elif "Y" in params %}
        M118 "G28 Y"
    {% elif "Z" in params %}
        M118 "G28 Z"
    {% else %}
        M118 "G28"
    {% endif %}

    {% set MOVEUP = true %}  ### Specify Z Axis movment
    {% if MOVEUP %}
        SET_KINEMATIC_POSITION Z=0
        G0 Z10 F600
    {% else %}  ### Move Down
        SET_KINEMATIC_POSITION Z=10
        G0 Z0 F600
    {% endif %}

    {% if 'X' in params %}
        _HOME_X
#        printer.toolhead.homed_axes = "x" 
    {% elif "Y" in params %}
        {% if ('x' != printer.toolhead.homed_axes) and ('xy' != printer.toolhead.homed_axes) and ('xyz' != printer.toolhead.homed_axes) %}
            _HOME_X
        {% endif %}
        _HOME_Y
#        printer.toolhead.homed_axes = "xy" 
    {% else %}  ### Either "Z" in params or no params which means all three axes
        {% if ('xy' != printer.toolhead.homed_axes) and ('xyz' != printer.toolhead.homed_axes) %}
            {% if ('x' != printer.toolhead.homed_axes) and ('xy' != printer.toolhead.homed_axes) and ('xyz' != printer.toolhead.homed_axes) %}
                _HOME_X
            {% endif %}
        _HOME_Y
        {% endif %}
        G28.1 Z  ### This was put in for a test to see if "safe_z_homing" has returned
    {% endif %}

The start of the [gcode_macro G28] code displays on the console the request and the current homing state of the printer. Next, the gantry is moved up to ensure no collisions with parts of the printer (something other people don’t do as I discovered to my great anger) - I can set it to go down when the gantry starts hitting the upper limit while I was testing the macros.

Next, I look for specific axes to home after first looking to see that the prequisite axes have been homed (ie “X” must be homed before anything else, “Y” must be homed before “Z”) and if they aren’t (based on the information that comes from printer.toolhead.homed_axes) then the code homes them before doing the requested homing.

For example, homing the “Y” axis without the “z” axis being homed first executes like (note the sequence of the action takes place from the bottom up):

In which the X axis is homed first. This is exactly what should happen.

BUT, if I home the X axis first and then home the Z axis, when the macro invocation polls printer.toolhead.homed_axes, it returns the information that all the axes are homed and moves along merrily as if BOTH X AND Y were homed as you can see in this console log:

I believe that the reason for the gantry crash I experienced in Problem with SHAPER_CALIBRATE & TMC2209s with Sensorless Homing.

So either printer.toolhead.homed_axes needs to be updated correctly when working with sensorless homing or there needs to be a way to update it from the macro code. Maybe there is a way to define a global variable that works across macro invocations (which would be an acceptable work around for cases like this), but I haven’t found a reference to being able to do that.

I would consider this a moderate to severe issue because it can result in damage to the printer.

2. Getting the Z axis probe to check the surface height twice:

This one is really confusing to me. My other printers (Voron and my other custom design) probe the bed surface twice, once at a fair clip and a second time much slower to get an accurate reading of the bed position. Any time I’ve made any changes to the X/Y homing to support sensorless homing, G28 (and the built in homing function) just goes down once at the faster speed with the resulting position information being quite inaccurate.

I can’t find any reference to a different gcode for function call for the dual probing operation. This seems to have been discussed in a couple of bug identifications (the one that discussed multiple Z axis probing is Multi samples support for safe_z_home · Issue #3573 · Klipper3d/klipper · GitHub).

Could somebody please point me in the right direction or explain how I can add this functionality to my homing macro(s) myself?

Without trying it in detail, I think there are multiple things to deal with:

  • In _HOME_X/Y you only set the homing current of the one respective axis. In a corexy setup you always need both axes for the movement
  • The issue with the single Z may be due to you overriding G28. I would not do this but instead use [homing_override] as you then can simply call a standard G28 Z
  • The issue with printer.toolhead.homed_axes I have no idea but it could very well be linked to the point above

First point. Argggh. You’re right. I’ve updated the _HOME_X & _HOME_Y macros. Good catch thanx.

Second point. It’s been like this since I set homing_retract_dist:0 - you can see it in the second video of my post here: Problem with SHAPER_CALIBRATE & TMC2209s with Sensorless Homing - #7 by mykepredko

Third point. Which point above? [homing_override] with “xyz” as the “axes” parameter didn’t go a good job of providing a single axis specified. I had a number of cases where it started doing all three axes when only one was specified or only the specified one when the higher order precedence one needed to be done.

Maybe I should retry it now that I’ve got a day or so of experience with macros (although I’m still making mistakes like the one you caught right at the top) but with overriding G28, I had things rock solid right from the start.

But, right now it’s 4:08AM here in Toronto, so I’m getting some sleep.

Good night :slight_smile:

The one directly above, i.e. No. 2
But on the other hand it should work ok, since you always use the original G28.1 command.
Maybe an interaction with [safe_z_home]? I would comment / delete the entire block (including [safe_z_home]).

You could also sprinkle your code with some:

[gcode_macro CHECK_HOME]
gcode:
    {% set msg = params.MSG|string %}
    { action_respond_info(msg ~ ': ' ~ printer.toolhead.homed_axes) }

e.g.

    {% if 'X' in params %}
        _HOME_X
        CHECK_HOME MSG=After_HOME_X

To see how the homing states are changing.

Okay, I’ve done some testing and, unfortunately, confirmed the issues brought up in this post are valid under different conditions and all behave in exactly the same way regardless of how the sensorless homing is invoked.

I’ve updated the macro code so that I can switch between [homing_override] as well as [gcode_macro G28] as well as enabling/disabling [safe_z_homing] with adding/deleting comments.

The relevant macro code is:

#### Following Statements and Macros to implement sensorless homing
#### NOTE: Commented out First Column Macros are to select between '[homing_override]' and '[gcode_macro G28]'

[force_move]
# Enable commands that force potentially unsafe movement
enable_force_move: True

[respond]

#[safe_z_home]                                        #### cannot be used with '[homing_override]'
#home_xy_position: 147, 105.5
#speed:50
#z_hop:10

[gcode_macro _HOME_X]
gcode:
    M118 Execute _HOME_X
    # Always use consistent run_current on A/B steppers during sensorless homing
    {% set RUN_CURRENT_X  = printer.configfile.settings['tmc2209 stepper_x'].run_current|float %}
    {% set RUN_CURRENT_Y  = printer.configfile.settings['tmc2209 stepper_y'].run_current|float %}
    {% set HOME_CURRENT   = 0.7 %}
    SET_TMC_CURRENT STEPPER=stepper_x CURRENT={HOME_CURRENT}
    SET_TMC_CURRENT STEPPER=stepper_y CURRENT={HOME_CURRENT}
    # Wait just a second… (give StallGuard registers time to clear)
    G4 P1000
    # Home
    G28.1 X                                          #### for '[gcode_macro G28]'
#    G28 X                                            #### for '[homing_override]'
    # Move away
    G91
    G1 X-10 F1200
    # Wait just a second… (give StallGuard registers time to clear)
    G4 P1000
    # Restore print current
    SET_TMC_CURRENT STEPPER=stepper_x CURRENT={RUN_CURRENT_X}
    SET_TMC_CURRENT STEPPER=stepper_y CURRENT={RUN_CURRENT_Y}

[gcode_macro _HOME_Y]
gcode:
    M118 Execute _HOME_Y
    # Set current for sensorless homing
    {% set RUN_CURRENT_X  = printer.configfile.settings['tmc2209 stepper_x'].run_current|float %}
    {% set RUN_CURRENT_Y  = printer.configfile.settings['tmc2209 stepper_y'].run_current|float %}
    {% set HOME_CURRENT   = 0.7 %}
    SET_TMC_CURRENT STEPPER=stepper_y CURRENT={HOME_CURRENT}
    # Wait just a second… (give StallGuard registers time to clear)
    G4 P1000
    # Home
     G28.1 Y                                         #### for '[gcode_macro G28]'
#    G28 Y                                            #### for '[homing_override]'
    # Move away
    G91
    G1 Y+10 F1200
    # Wait just a second… (give StallGuard registers time to clear)
    G4 P1000
    # Restore print current
    SET_TMC_CURRENT STEPPER=stepper_x CURRENT={RUN_CURRENT_X}
    SET_TMC_CURRENT STEPPER=stepper_y CURRENT={RUN_CURRENT_Y}

[gcode_macro G28]
rename_existing: G28.1                               #### for '[gcode_macro G28]'
#[homing_override]
#axes: xyz                                            #### for '[homing_override]'
gcode:
    {% if 'X' in params %}                           # - Output G28 gcode option invoked
        M118 "G28 X"
    {% elif "Y" in params %}
        M118 "G28 Y"
    {% elif "Z" in params %}
        M118 "G28 Z"
    {% else %}
        M118 "G28"
    {% endif %}
    {% if   "x"   == printer.toolhead.homed_axes %}  # - Output Current State
        M118 x homed upon entry
    {% elif "xy"  == printer.toolhead.homed_axes %}
        M118 XY homed Upon entry
    {% elif "xyz" == printer.toolhead.homed_axes %}
        M118 all homed upon entry
    {% else %}
        M118 nothing homed upon entry
    {% endif %}

    {% set MOVEUP = true %}                         # - Specify Z Axis Movement (to prevent carriage from going too high during debug)
    {% if MOVEUP %}
        SET_KINEMATIC_POSITION Z=0
        G0 Z10 F600
    {% else %}
        SET_KINEMATIC_POSITION Z=10
        G0 Z0 F600
    {% endif %}

    {% if 'X' in params %}
        _HOME_X
    {% elif "Y" in params %}
        {% if ('x' != printer.toolhead.homed_axes) and ('xy' != printer.toolhead.homed_axes) and ('xyz' != printer.toolhead.homed_axes) %}
            _HOME_X
        {% endif %}
        _HOME_Y
    {% else %}                                       # Either "Z" in params or no params which means all three axes
        {% if ('xy' != printer.toolhead.homed_axes) and ('xyz' != printer.toolhead.homed_axes) %}
            {% if ('x' != printer.toolhead.homed_axes) and ('xy' != printer.toolhead.homed_axes) and ('xyz' != printer.toolhead.homed_axes) %}
                _HOME_X
            {% endif %}
            _HOME_Y
        {% endif %}
        G90                                          # Set absolute position mode
                                                     #### for '[safe_z_home]' active
        G1 X115 Y115 F1200                           #### for '[safe_z_home]' commented out
        G28.1 Z                                      #### for '[gcode_macro G28]'
#        G28 Z                                        #### for '[homing_override]'
        G90                                          # Move Up to 10 when finished Z axis homing
        G1 Z10 F600                           
    {% endif %}

    {% if   "x"   == printer.toolhead.homed_axes %}  # - Output FINAL State
        M118 X HOMED AFTER G28 COMMAND
    {% elif "xy"  == printer.toolhead.homed_axes %}
        M118 XY HOMED AFTER G28 COMMAND
    {% elif "xyz" == printer.toolhead.homed_axes %}
        M118 ALL HOMED AFTER G28 COMMAND
    {% else %}
        M118 NOTHING HOMED AFTER G28 COMMAND
    {% endif %}

Here are the latest files:
printer.cfg (26.7 KB)
klippy.log (2.0 MB)

I just ran three tests:

  1. X axis homing with motors off (ie no homing done at all) to see the operation plus the printer.toolhead.homed_axes state upon exit.
  2. After doing the X axis homing, doing a Z axis homing to see if printer.toolhead.homed_axes is correct upon entry and if a Y axis homing (which is required in this case) is performed.
  3. Seeing if the Z homing probing happens once or twice.

For three different test setups:

  1. [homing_override] active. [safe_z_homing] cannot be active when this statement is encountered by Klipper.
  2. [gcode_macro G28] active with [safe_z_homing] active.
  3. [gcode_macro G28] active with [safe_z_homing] commented out.

All three test setups behaved identically through the three tests.

For test 1, X axis homing did not detect any homed axis upon entry. I was surprised to see that there weren’t any homed axis upon exit.

For test 2, entry into the homing gcode had printer.toolhead.homed_axes always indicating that all the axes were homed (as I indicated above). The console screen was consistent for all three test setups:

In the image above, you can see the “NOTHING HOMED AFTER G28 COMMAND” at the end of the “G28 X” command’s execution as well as “all homed upon entry” at the start of the “g28 Z” command.

In all three test setups, the Z axis probe only happened once and not twice.

So, I think I should put in a bug report for the first issue, a single axis homing operation results in all axes being indicated as homed, (unless you have a work around) as I consider this to be a safety issue.

As for the second issue, Z axis probing only occurs once (and not twice to ensure accurate location of Z probe) - how can I get the two probing action?

Next steps?

I’m not sure if your macro is entirely valid since all of it will be evaluated upon calling even before a single movement or homing has started.
(I find it extremely difficult to wrap my head around around the macro logic especially on more complex ones and I’m always fascinated, what some people are able to do with it - unfortunately my coding capabilities are abysmal)

For the bug question, I would like to see @koconnor opinion. I’m pretty sure, we are missing something obvious.

I’m not sure what you mean by the first statement here. Are you saying that the system state information is static within the macro’s execution and never updates until exit?

What part of the macro do you not understand? Let’s take the time to walk through it to make sure we’re both comfortable with everything.

I’m referring to Commands templates - Klipper documentation

Important! Macros are first evaluated in entirety and only then are the resulting commands executed. If a macro issues a command that alters the state of the printer, the results of that state change will not be visible during the evaluation of the macro. This can also result in subtle behavior when a macro generates commands that call other macros, as the called macro is evaluated when it is invoked (which is after the entire evaluation of the calling macro).

So I think / assume / could be / I don’t know / Hell, who invented Jinja? / Oh, come on if this is also making that part of your macro and thus your results correct:

   {% if   "x"   == printer.toolhead.homed_axes %}  # - Output FINAL State
        M118 X HOMED AFTER G28 COMMAND
    {% elif "xy"  == printer.toolhead.homed_axes %}
        M118 XY HOMED AFTER G28 COMMAND
    {% elif "xyz" == printer.toolhead.homed_axes %}
        M118 ALL HOMED AFTER G28 COMMAND
    {% else %}
        M118 NOTHING HOMED AFTER G28 COMMAND
    {% endif %}

or this part for that matter

    {% if 'X' in params %}                           # - Output G28 gcode option invoked
        M118 "G28 X"
    {% elif "Y" in params %}
        M118 "G28 Y"
    {% elif "Z" in params %}
        M118 "G28 Z"
    {% else %}
        M118 "G28"
    {% endif %}
    {% if   "x"   == printer.toolhead.homed_axes %}  # - Output Current State
        M118 x homed upon entry
    {% elif "xy"  == printer.toolhead.homed_axes %}
        M118 XY homed Upon entry
    {% elif "xyz" == printer.toolhead.homed_axes %}
        M118 all homed upon entry
    {% else %}
        M118 nothing homed upon entry
    {% endif %}

So based on that reference, I would say system information is static within the macro’s execution. That does explain why the printer.toolhead.homed_axes status isn’t updated before exit from the macro. However, that doesn’t explain why it’s wrong (or incorrectly updated) after the macro execution.

@koconnor can you weigh in please?

Data point for you a @koconnor

I changed the [gcode_macro]/[homing_override] to [gcode_macro HOME_TEST] and…

Double Z axis probing works again both for using the homing controls on the Mainsail UI as well as when I run HOME_TEST.

I should point out that when I do the homing test above (Do X axis followed by Z axis) on Mainsail, it puts an error on the console saying that both X & Y have to be homed before Z will be.

So, there’s something about running homing from a macro…

I don’t think I can contribute here - I don’t home my axes individually nor use the GUI buttons that do that.

If you do want my opinion, I would suggest not homing axes independently nor use the GUI buttons. The issue is homing repeatability. I’ve found on several printers that homing the Z at different X/Y locations provides different results. Ideally one wants to obtain a Z axis calibration to a repeatability within a few microns - a very challenging task. I’ve found the best way to do that is to arrange a precise set of steps with the goal of repeatable heating effects, vibrations, axis weighting, etc. Then run that same set of steps with every homing attempt.

If you think the Klipper code has done something wrong, then I’m afraid it got lost somewhere in the above. Best to summarize the problem in a few sentences and attach a klipper log showing the error. Be aware that homing_override is intended to home all axes (and documented as such). I know some people use advanced macros in it, but if one does that and something breaks, then they get to keep both parts.

Cheers,
-Kevin

Hey,

Yeah, I have come to the conclusion that I am unusual in using the homing of individual axes rather than just doing a G28 and being done with it. Honestly, I think you have a safety problem with how the individual axis homing (ie G28 X) incorrectly updates printer.toolhead.homed_axes and allows incorrect homing operations but I seem to be alone in having that opinion. You can do with my warning and observations what you will.

What I am seeing, which will require you to look at, is that the toolhead only comes down once (and not twice as is normal without using sensorless homing).

I have just spent the evening setting up the basic macros (based on TMC drivers - Klipper documentation and TMC drivers - Klipper documentation) so that you can see the problems as well as characterize the accuracy of the Z axis homing (we’re talking about accuracy on the order of millimetres; there’s a long way to go to talk about microns).

When I have everything together, I’ll update this thread and send you a message directly so you know it’s available. I will put up a klippy.log, my printer.cfg and a video showing what is happening.

Cheers.

Hi @koconnor & @Sineos

Here is a video showing what happens when you select “Home All” from Mainsail on my custom CoreXY printer:

Note that there is only one probe of the Z axis. I kept the video running for a few extra seconds to show that nothing happens after the initial probe.

Here is the klippy.log and printer.cfg as well as the canFly.cfg (for the FLY SHT42 board on the toolhead) :
: klippy.log (452.3 KB)
: printer.cfg (22.1 KB)
: canFly.cfg (3.7 KB)

Let me know what I can do to help you resolve this issue.

Maybe you should set the samples parameter in [bltouch]

#samples: 1
#   The number of times to probe each point. The probed z-values will
#   be averaged. The default is to probe 1 time.

from:
https://www.klipper3d.org/Config_Reference.html#probe

1 Like

This is again a configuration issue on your end. You’ve set homing_retract_dist to 0 for stepper_z. This tells the printer to home Z at homing_speed and stop. If you want it to home a second time at half of homing_speed, then set homing_retract_dist to a nonzero value.

2 Likes

I do not know how reliable the homing process now is, so just as a note: The HOME_CURRENT (0.7) is equal to your run_current. There is no benefit as such in detection a stall earlier.

1 Like

Thank you - that got it. The homing_retract_dist: 0 parameter in the [stepper_z] statement seems to be the problem. I wasn’t sure about it (which is why it had the “#### Delete for this case?” comment).

Now, I’ve run the following process five times to look at the accuracy of my setup:

  1. Set the position of the toolhead to the corner opposite to the origin
  2. Power Cycle the printer with the power off time at least 15 seconds
  3. Reconnect to the printer using a FIRMWARE_RESTART (as that doesn’t seem to happen automatically)
  4. Home the printer using G28. Observe and make sure the Z axis probing occurs twice
  5. Run PROBE_ACCURACY

The cut down (show important) results of PROBE_ACCURACY are as follows:

max 2.1225, min 2.1150, range 0.0075, avg 2.1198, median 2.1200, sd 0.001750
max 2.1200, min 2.1100, range 0.0100, avg 2.1185, median 2.1200, sd 0.003000
max 2.1050, min 2.1025, range 0.0025, avg 2.1035, median 2.1025, sd 0.001225
max 2.1000, min 2.0975, range 0.0025, avg 2.0998, median 2.1000, sd 0.000750
max 2.1100, min 2.1050, range 0.0050, avg 2.1088, median 2.1100, sd 0.001677

So, with a maximum standard deviation of 3 microns, I think I have something that now works.

Thanx to everyone for their help!

I just tried samples: 3 and there was no change to the operation of the probing.

Thank you for the suggestion.

1 Like

That’s a happy coincidence. The Voron 2.4 normal running current is 0.8A and then move it down to 0.7A for homing.

When I was looking at the operation on the X/Y system, 0.7A seemed to be optimal for my printer so I changed them to that.

I’m going to leave in the statements in case I decide either the HOME_CURRENT or basic operating current is going to change.


[gcode_macro SENSORLESS_HOME_X]
gcode:
    {% set HOME_CUR = 0.700 %}
    {% set driver_config = printer.configfile.settings['tmc2209 stepper_x'] %}
    {% set RUN_CUR = driver_config.run_current %}
    # Set current for sensorless homing
    SET_TMC_CURRENT STEPPER=stepper_x CURRENT={HOME_CUR}
    # Pause to ensure driver stall flag is clear
    G4 P2000
    # Home
    G28 X0
    # Move away
    G90
    G1 X157 F1200
    # Set current during print
    SET_TMC_CURRENT STEPPER=stepper_x CURRENT={RUN_CUR}


[gcode_macro SENSORLESS_HOME_Y]
gcode:
    {% set HOME_CUR = 0.700 %}
    {% set driver_config = printer.configfile.settings['tmc2209 stepper_y'] %}
    {% set RUN_CUR = driver_config.run_current %}
    # Set current for sensorless homing
    SET_TMC_CURRENT STEPPER=stepper_y CURRENT={HOME_CUR}
    # Pause to ensure driver stall flag is clear
    G4 P2000
    # Home
    G28 Y0
    # Move away
    G90
    G1 Y174 F1200
    # Set current during print
    SET_TMC_CURRENT STEPPER=stepper_y CURRENT={RUN_CUR}


[homing_override]
axes: z
set_position_z: 0
gcode:
   G90
   G0 Z6 F600

   {% set home_all = 'X' not in params and 'Y' not in params and 'Z' not in params %}

 {% set home_all = 'X' not in params and 'Y' not in params and 'Z' not in params %}

  {% if home_all or 'X' in params %}
  SENSORLESS_HOME_X
  {% endif %}
  
  {% if home_all or 'Y' in params %}
  SENSORLESS_HOME_Y
  {% endif %}
  
  {% if home_all or 'Z' in params %}

   G28 Z
   G0 Z10 F1800
   {% endif %}
2 Likes