A macro variable is a value Klipper keeps between macro calls. Declare it, change it, share it and save it, learn when a new value becomes visible, and read position, homing and other printer state in a macro.
A Klipper macro variable is a variable_<name> line in a [gcode_macro] section. The macro reads it by its bare name, other macros read it as printer["gcode_macro NAME"].name, and SET_GCODE_VARIABLE changes it at run time. Changes last until Klipper restarts. For a value that survives a restart, use SAVE_VARIABLE.
How to declare a variable in a Klipper macro
Add one variable_ line per value above gcode:. Klipper’s configuration reference says the value is “parsed as a Python literal” and “will be available during macro expansion”.
[gcode_macro PARK]
description: Lift the nozzle and move to a park spot
variable_park_x: 10.0 # placeholder: a point inside your bed limits
variable_park_y: 10.0 # placeholder
variable_lift: 10.0 # mm to raise Z
gcode:
SAVE_GCODE_STATE NAME=park_state
G91
G1 Z{lift} F600
G90
G1 X{park_x} Y{park_y} F6000
RESTORE_GCODE_STATE NAME=park_state
Inside PARK, the variables are plain names: {lift}, {park_x}. Any other macro reads them through the printer object:
{% set px = printer["gcode_macro PARK"].park_x %}
Variable names must be lowercase: the documentation says they “may not use upper case characters”. Because the value is a Python literal, the way you write it sets its type:
| Type |
In the config |
With SET_GCODE_VARIABLE |
| Integer |
variable_count: 3 |
VALUE=3 |
| Float |
variable_lift: 10.0 |
VALUE=12.5 |
| String |
variable_material: "PLA" |
VALUE='"PETG"' |
| Boolean |
variable_done: False |
VALUE=True |
| None |
variable_last_tool: None |
VALUE=None |
| List |
variable_spots: [10, 20] |
VALUE="[30, 40]" |
Strings need quotes, and booleans are capitalized (True, False). An unquoted string such as variable_material: PLA stops Klipper at startup with “is not a valid literal”. A dict works in the config, but not as a VALUE inside a macro body, where { opens a Jinja expression.
How do you change a Klipper macro variable?
Run SET_GCODE_VARIABLE MACRO=<macro> VARIABLE=<name> VALUE=<value>. The variable must already exist as a variable_<name> line in that macro’s section, and Klipper parses VALUE as a Python literal, so strings need quotes inside the value. The change lasts until Klipper restarts; use SAVE_VARIABLE to keep a value across restarts.
# from the console or any macro
SET_GCODE_VARIABLE MACRO=PARK VARIABLE=park_x VALUE=200
# inside a macro called with LIFT=<mm>
SET_GCODE_VARIABLE MACRO=PARK VARIABLE=lift VALUE={params.LIFT|default(10)|float}
MACRO= must match the section name as written, including case, so MACRO=park will not find [gcode_macro PARK].
Klipper evaluates a macro “in entirety and only then” runs the commands it produces. So a macro that sets a variable and then reads it back still sees the old value. Read the new value in a macro that runs afterwards.
Local variables with set
{% set %} creates a local name that lives for one run of the macro and is never stored. A set inside a for loop is also lost when the loop ends: Jinja’s documentation says it is “not possible to set variables inside a block and have them show up outside of it”. A namespace carries the value out:
{% set ns = namespace(total=0) %}
{% for t in [200, 210, 220] %}
{% set ns.total = ns.total + t %}
{% endfor %}
{action_respond_info("Sum: %d" % ns.total)}
How to make a global variable for Klipper macros
Klipper has no global scope. The usual pattern is a macro that only holds variables, with an empty gcode:. Mainsail’s client.cfg macros read their pause and cancel settings from one called _CLIENT_VARIABLE.
[gcode_macro _GLOBALS]
variable_material: "PLA"
variable_prints_done: 0
gcode:
Read it from any macro as printer["gcode_macro _GLOBALS"].material, with the section name in its exact case. Change it with SET_GCODE_VARIABLE MACRO=_GLOBALS VARIABLE=material VALUE='"ABS"'. The leading underscore keeps the holder off the dashboard: Mainsail’s documentation says any section “with a name starting with an underscore (_) is automatically hidden from the Mainsail interface”.
Can a Klipper macro return a value?
No. A macro expands into G-code commands and has no return statement, so the caller cannot receive a result. The workaround is to store the result in a macro variable with SET_GCODE_VARIABLE and read it in a second macro called afterwards, because Klipper evaluates the whole calling macro before any command it emits runs.
This “function” probes the bed and stores the Z it measured. It needs a [probe] or [bltouch] section.
[gcode_macro MEASURE_Z]
variable_result: 0.0
gcode:
PROBE
_MEASURE_Z_STORE
[gcode_macro _MEASURE_Z_STORE]
gcode:
SET_GCODE_VARIABLE MACRO=MEASURE_Z VARIABLE=result VALUE={printer.probe.last_probe_position.z}
[gcode_macro CHECK_Z]
gcode:
MEASURE_Z
_CHECK_Z_REPORT
[gcode_macro _CHECK_Z_REPORT]
gcode:
{% set z = printer["gcode_macro MEASURE_Z"].result %}
{action_respond_info("Probe triggered at Z=%.3f" % z)}
Each step needs its own macro. Klipper’s documentation states a called macro “is evaluated when it is invoked”, which is after its caller has been evaluated. _MEASURE_Z_STORE is evaluated once PROBE has finished, so it sees the new reading. _CHECK_Z_REPORT then sees the stored result.
Fixing “Unknown gcode_macro variable”
Klipper raises Unknown gcode_macro variable 'name' when the macro named in MACRO= has no variable called name. SET_GCODE_VARIABLE changes existing variables and cannot create one. Check, in order:
- The declaration. Add
variable_name: with a starting value to that macro’s section.
- The case. Klipper lowercases config option names, so
variable_Bed_Temp becomes bed_temp. Write VARIABLE=bed_temp.
- The macro.
MACRO= must point at the section that declares the variable. A name that matches no macro, including a case mismatch, gives a different error that starts “The value ‘park’ is not valid for MACRO”.
- A restart. A
variable_ line added to printer.cfg loads only after RESTART.
A typo on the read side raises no error. Jinja treats the missing value as undefined and prints an empty string.
How to read printer objects in a Klipper macro
printer.<section>.<field> reads live printer state. The name after printer is usually a config section, and the Status reference lists every field. Use brackets when a section name contains a space. Each value is a snapshot taken when the macro is evaluated.
[gcode_macro SHOW_STATE]
gcode:
{% set bed = printer.heater_bed %}
{% set hotend = printer[printer.toolhead.extruder] %}
{action_respond_info("Bed %.1f/%.1f C, hotend target %.1f C" % (bed.temperature, bed.target, hotend.target))}
{% if "temperature_sensor chamber" in printer %}
{action_respond_info("Chamber %.1f C" % printer["temperature_sensor chamber"].temperature)}
{% endif %}
{action_respond_info("X max: %s" % printer.configfile.settings.stepper_x.position_max)}
"name" in printer tests whether an object exists. printer.configfile.settings returns config values as loaded at the last restart. For a chamber heater’s own fields, see chamber heater control.
How to get the current position in a Klipper macro
Read printer.toolhead.position.x (or .y, .z) for the last commanded position in the config file’s coordinates. Read printer.gcode_move.gcode_position.x for the position relative to the G-code origin, the numbers a G1 command would use. From the console, GET_POSITION or M114 prints it.
Both are last commanded values, read before the macro’s own moves run. This macro raises Z by 10 mm without passing the axis limit:
[gcode_macro LIFT_Z]
gcode:
{% set z = printer.toolhead.position.z %}
{% set max_z = printer.toolhead.axis_maximum.z %}
{% set dz = [10, max_z - z]|min %}
SAVE_GCODE_STATE NAME=lift_state
G91
G1 Z{dz} F600
RESTORE_GCODE_STATE NAME=lift_state
The move is relative because position and axis_maximum use config coordinates, while an absolute G1 would add any SET_GCODE_OFFSET. SAVE_GCODE_STATE also stores the current XYZ position, so RESTORE_GCODE_STATE NAME=lift_state MOVE=1 can return the toolhead to it later.
How to check if the printer is homed in a Klipper macro
printer.toolhead.homed_axes is a string holding the homed axes, such as "xyz". Home only when needed:
[gcode_macro SAFE_PARK]
gcode:
{% if "xyz" not in printer.toolhead.homed_axes %}
G28
{% endif %}
PARK
To refuse instead, put {action_raise_error("Home the printer first")} inside the if. It aborts the macro and every macro that called it. For a single axis, test "z" in printer.toolhead.homed_axes. Branching is covered on the if/else page.
How to keep a macro variable after a restart
Values set with SET_GCODE_VARIABLE reset to the config value on every restart. To persist one, enable [save_variables] and write it with SAVE_VARIABLE:
[save_variables]
filename: ~/printer_data/config/variables.cfg # any writable path
[gcode_macro SET_MATERIAL]
gcode:
{% set m = params.MATERIAL|default("PLA")|string %}
SAVE_VARIABLE VARIABLE=material VALUE='"{m}"'
Klipper loads stored values into printer.save_variables.variables at startup. As with SET_GCODE_VARIABLE, the name must be lowercase and VALUE is a Python literal. Read it with {% set svv = printer.save_variables.variables %}, then svv.material. Run SET_MATERIAL MATERIAL=PETG to store a new value.