Need help to debug a simple custom macro

Basic Information:

Printer Model: Ender 5 Plus
MCU / Printerboard: SKR E3 mini v3
klippy.log
klippy.log (974.7 KB)

Describe your issue:

Hello I’m just a new Klipper user and I’m just struggling with a custom macro I’m try to create. The idea of this macro is to move the toolhead in the 4 corners of the bed for a certain number of times, pause on each corner and restart after a resume. It was working fine in Marlin (was written in g-code) and now I would like to migrate it into Klipper. This is my macro

[gcode_macro CUSTOM_BED_LEVELING]
variable_loops: 2 # number of probing angles
gcode:
{% set LF_x = 10 %}
{% set LF_y = 10 %}
{% set LR_x = 10 %}
{% set LR_y = 350 %}
{% set RR_x = 350 %}
{% set RR_y = 350 %}
{% set RF_x = 350 %}
{% set RF_y = 10 %}
SET_FILAMENT_SENSOR SENSOR=BTT_SFS ENABLE=0
G28
G1 Z20
PAUSE_BASE
G1 F3000
{% for i in range(loops) %}
M117 start loop
G1 X{LR_x} Y{LR_y}
PAUSE_BASE
G1 X{LF_x} Y{LF_y}
PAUSE_BASE
G1 X{RR_x} Y{RR_y}
PAUSE_BASE
G1 X{RF_x} Y{RF_y}
PAUSE_BASE
{% endfor %}
G1 X180 Y180
SET_FILAMENT_SENSOR SENSOR=BTT_SFS ENABLE=1

The toolhead correctly moves on all the 4 corners for the number of times configured but it never pause on each of them. From console I see

17:48:39 $ custom_bed_leveling
17:49:01 // action:paused
17:49:01 // Print already paused
17:49:02 // Print already paused

Did I do or missed something?

thank you for your support

Hello @dnasini !

Short question, what do you want to achieve with that macro?
I mean, you don’t move the printhead to the corners just for fun.

1 Like

In Marlin I attach a dial gauge to the toolhead and probe each corner multiple times to level the bed. The idea is to do the same with Klipper too. This macro is not the final one, in this phase I just want to reach the corners but I fail to let the toolhead stop on each till I press a resume. Funny thing is that if I use the one wrote for Marlin with the M0 macro that uses PAUSE_BASE, it works fine

G1 X10 Y340 F3000 ; Position on Back Left
G91 ; Relative position
G1 Z-6 ; lower Z 6mm
G90 ; Absolute position
M0 ;Pause

with M0 macro like this

[gcode_macro M0]
gcode:
PAUSE_BASE

Maybe there is already something in Klipper that you can use?

https://www.klipper3d.org/Bed_Level.html

https://www.klipper3d.org/Manual_Level.html

I just had a look into the two links you shared but both processes are prone to “human error” since are based on the feeling of the friction of the paper card or turning the wheel clockwise/counterclockwise…
With the Marlin setup bed leveling with dial gauge was really precise so I’m wondering is not possible to reproduce the same in Klipper

Instead of the paper you can use your digital gauge?

Or you use a probe.

Without digging too deep into it, I see multiple issues:

  • PAUSE_BASE always calls _TOOLHEAD_PARK_PAUSE_CANCEL which again calls other macros (do not feel like digging to through it)
  • When a macro is run, then all states / variables are evaluated at macro start and printer state changes are not recognized in within the macro. This might well bite you here. Also see Help with Macro: SET_GCODE_VARIABLE doesnt set my variable - #12 by theophile for a discussion on this
  • PAUSE needs to always be combined with either CLEAR_PAUSE or RESUME or it will leave the printer in a confused state

mmm I’m figuring out how to use the dial gauge instead of the paper…

For the probe, the “limitation” is that it makes the measurement than you have to manually compensate turning the wheels. In my opinion this is an “unprecise” process you have to manually compensate turning the wheels

I don’t get this point. In my fluidd.cfg I have a macro named PAUSE like this

[gcode_macro PAUSE]
description: Pause the actual running print
rename_existing: PAUSE_BASE

so, per my understanding, the original PAUSE function now can be engaged with PAUSE_BASE, am I correct? If so, using PAUSE_BASE in a macro should not engage any other macro

well, this is something I can take in consideration, anyway I was expecting at least the first call to PAUSE_BASE should work and then fall in a “umpredictable” state. In my case looks like no PAUSE_BASE is never engaged

Does the toolhead pause at least the first time?

No, that’s the weird thing. From console I receive the message “printer pause” on the first command, toolhead doesn’t pause and moves to the next coordinate, then I get the massage “printer already pause” on the second command and so on and so on.

If I use the original gcode that I was using in Marlin with M0 to pause the printer, the full routine works. In Klipper I use this macro to simulate M0

[gcode_macro M0]
gcode:
PAUSE_BASE

I’m not able to realize what’s the difference between my custom macro and the one for M0, both use the same command PAUSE_BASE. The only difference I can see is that my macro is “native” in Klipper config and the g-code is “external”

I think the answer and solution are here: Pause doesn't work · Issue #4940 · Klipper3d/klipper · GitHub

Basically, PAUSE (or BASE_PAUSE in this case) doesn’t instantly freeze the printer, it just pauses the execution of the gcode queue before the next command in the queue is executed. When you put it in a macro, that entire macro exists in the queue as a single command, so the pause would not take effect until just before the execution of the next command, which doesn’t occur until the current one (i.e. your macro) has completed. This is actually pretty helpful because it allows for macros that (for example), pause the print and then retract and move the toolhead to a safe area so as not to drip onto or melt the surface of the print.

In your case, the actual pause would not take effect until after the macro has completed. But because your macro has called BASE_PAUSE more than once, when the printer does go to execute the pause, it gets confused and throws an error because it thinks it’s already paused.

To accomplish what you want to do may depend in part on what interface you’re using to interact with the printer. Basically, you want to create separate macros, one for each part of the process that you want to end with a pause. Then instead of “pausing” and “resuming” between segments, you would have the first segment run until completion and then just start the next segment instead of “resuming.” You could chain the segments together this way to produce the behavior you’re aiming for.

1 Like

Well, I got the point and honestly without this explanation I’ve never realize the way a macro works in Klipper. I did some code in the past years and my understanding of a macro was a bunch of instruction executed one by one. Here a macro is like a “single” command and it can be paused only at the end, am I correct?

So, I’ll have to rethink my macro in a different way. thx so much for the hint

Sort of. Think of it like this. There’s a top-level command queue that continuously executes gcode commands one after the other, in order. It is almost exclusively a synchronous queue, which means that the current command must complete before the next one begins to be executed.

A macro is a single command but it works (on a conceptual level if not a technical one) by spawning another synchronous queue that contains only the gcode commands defined in the macro. When the macro is called by the “Parent Queue,” the “Child Queue” is initiated, the gcode commands are evaluated and then inserted into the Child Queue, the commands in the Child Queue are executed until the Child Queue is empty, the Child Queue is destroyed, and the Parent Queue then proceeds to execute the next command in the Parent Queue.

(This is of course an overly simplified explanation and there are exceptions like non-blocking gcode commands and delayed gcode, etc.)

The PAUSE command essentially instructs the printer, “Finish whatever Parent Queue command you’re currently executing, but don’t execute the next one until you get the RESUME signal.” Most of the time, from a practical standpoint, this looks like the printer was told, “Freeze NOW,” because most gcode commands execute very quickly so the pause appears to take effect more or less instantly. But if a gcode command takes a significant amount of time to execute (like M109/M190), that’s how long it will take before the pause actually happens because that’s how long it takes before the next command in the Parent Queue would normally be executed.

In the case of a macro, the macro is a single command in the Parent Queue. When a macro is running, the commands you’re seeing are being executed by the Child Queue, not the Parent Queue. The Parent Queue is simply waiting for the Child Queue to finish before continuing on. So if a PAUSE is issued while a macro is running, or if it is issued by the macro itself, you don’t see the practical effect of the pause until the macro has finished executing because that’s the earliest place the next Parent Queue command would otherwise be executed.

So all that to say, it’s not really the case that you can only pause a macro at the end. Rather, you can’t PAUSE a macro at all because PAUSE only operates at the level above the macro. So you can issue PAUSE in the middle of the macro if you like, but since the PAUSE command tells the printer to finish the current command, that means the macro will finish executing before you notice the effect of the PAUSE.

And if your macro issues PAUSE more than once, the printer will react more or less the same as if you rapidly mash the Pause button several times. In both cases you’ll get the warning you got: Print already paused.

1 Like

I miss some Klipper basic macro interaction how, i.e., call a macro from another macro, pass variable values during the call and get a result.

For example, if I have macroA and macroB and from macroA I want to call macroB and send it a X Y coordinate, how I can accomplish it?

There is no really elegant way for this. You typically would “abuse” a macro to hold some “global variables” and use this construct to pass around values.
See Encoder runout will only pause when switching to T0, T1 and T2 runout does not trigger - #3 by Sineos for an example

1 Like

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.