# _Z_CHECK: compare where Klipper thinks the nozzle is with what the Beacon
# measures (proximity), at X/Y (default the bed centre) and Z (default 3 mm,
# inside the model range 0.2-5 mm). Catches a Z that changed since the last
# homing (lost Z steps, gantry moved, much thermal drift) before the nozzle
# goes low; a difference above MAX stops the print. A wrong Beacon model
# fools both readings the same way, so this does not replace contact homing.
#
# BEACON_QUERY stores its reading in printer.beacon.last_sample. When it
# gives no new reading there, the check only warns, so a Beacon version
# without that field does not stop every print.
[gcode_macro _Z_CHECK]
description: Compare Klipper's Z with the Beacon reading at X Y Z (default 175 175 3), stop the print when they differ by more than MAX (default 0.5 mm)
gcode:
{% set x=params.X|default(175)|float %}
{% set y = params.Y|default(175)|float %}
{% set z = params.Z|default(3)|float %}
{% set max = params.MAX|default(0.5)|float %}
{% if 'xyz' not in printer.toolhead.homed_axes %}
_ERROR MSG="_Z_CHECK: printer not homed" DISPLAY="Z check: not homed"
{% elif 'beacon' not in printer %}
_WARN MSG="_Z_CHECK: no [beacon], check skipped"
{% else %}
{% set last = printer.beacon.last_sample %}
{% set prev = last.time if last and last.time is defined else -1 %}
SAVE_GCODE_STATE NAME=_z_check
G90
_GUARD_ON PHASE=z_check
{% if printer.toolhead.position.z < z %}
G1 Z{z} F600
{% endif %}
G1 X{x} Y{y} F12000
G1 Z{z} F600
_GUARD_OFF
G4 P300
BEACON_QUERY
_Z_CHECK_EVAL MAX={max} PREV={prev}
RESTORE_GCODE_STATE NAME=_z_check
{% endif %}
# Separate macro: rendered after BEACON_QUERY has run. toolhead.position is
# the real nozzle height (gcode offset and mesh included).
[gcode_macro _Z_CHECK_EVAL]
description: Helper for _Z_CHECK: compare the BEACON_QUERY reading with the toolhead Z
gcode:
{% set s=printer.beacon.last_sample %}
{% set z = printer.toolhead.position.z %}
{% set max = params.MAX|default(0.5)|float %}
{% if not s or s.time is not defined or s.dist is not defined or s.time == params.PREV|default(-1)|float %}
_WARN MSG="_Z_CHECK: BEACON_QUERY left no new reading in printer.beacon.last_sample, Z check skipped" DISPLAY="Z check skipped"
{% elif s.dist is none %}
_ERROR MSG="_Z_CHECK: Klipper Z={'%.2f' % z} but the Beacon reads nothing (more than 5 mm away or no model), print stopped" DISPLAY="Z check failed, print stopped"