Was hat sich geändert?
Mit dem Klipper-Update 2026 hat sich eine fundamentale Änderung am Probe-System ergeben: Der Parameter z_offset ist im [probe_eddy_current]-Block jetzt deprecated (veraltet). An seine Stelle tritt descend_z – ein Wert mit einer völlig anderen Bedeutung.
Während z_offset den vertikalen Versatz zwischen Düse und Sensorpunkt beschrieb, definiert descend_z lediglich die Anfahrhöhe für den Kalibrierungsvorgang. Der eigentliche Z-Offset wird jetzt intern über die Kalibrierungstabelle verrechnet.
Wer nach dem Update einfach seinen alten z_offset-Wert in descend_z einträgt, riskiert einen Crash der Düse ins Bett. Diese Anleitung zeigt dir Schritt für Schritt, wie du alles korrekt umstellst.
Anzeige · Affiliate-Link – kaufst du darüber, erhalte ich ggf. eine Provision. Für dich ändert sich am Preis nichts.
Teil 1: Die richtige Konfiguration (printer.cfg)
Stelle sicher, dass dein [probe_eddy_current]-Block exakt so aussieht. Der Wert descend_z ist kein Offset, sondern nur die Anfahrhöhe für den Kalibriertest:
[probe_eddy_current btt_eddy]
sensor_type: ldc1612
i2c_mcu: eddy
i2c_bus: i2c0f
x_offset: 0
y_offset: 74.0
# descend_z ist die Höhe, auf die der Kopf beim Kalibrieren fährt.
# 1.0 ist sicher. Stell es NICHT auf deinen alten Z-Offset!
descend_z: 1.0
samples: 2
samples_result: medianWichtig: Ein descend_z von 1.0 ist ein sicherer Startwert – der Kopf fährt auf 1 mm über das Bett.
Voraussetzungen: Was das Homing-Makro braucht
Das Makro aus Teil 2 ist kein Einzelstück. Es greift auf drei Dinge zu, die vorher in der Config stehen müssen. Fehlen sie, bricht Klipper beim ersten G28 ab – typischerweise mit jinja2.exceptions.UndefinedError: 'GetStatusWrapper' object has no attribute 'gcode_macro SET_GCODE_OFFSET'. Dieser Abschnitt ist nachträglich entstanden, weil zwei Leser in den Kommentaren zu Recht angemerkt haben, dass genau diese Voraussetzungen fehlten – danke dafür.
Bei mir liegen die Blöcke nicht in der printer.cfg, sondern in einer eigenen btt_eddy.cfg, die per [include ./btt_eddy.cfg] eingebunden wird. Das meiste davon liefert BTT in seiner Beispiel-Config schon mit – dort ist es nur auskommentiert.
1. Eddy als Z-Endstop und [safe_z_home]
Der Eddy ersetzt den mechanischen Z-Endschalter. Damit gibt es keinen festen Punkt mehr, an dem Z null ist – gemessen wird dort, wo der Kopf gerade steht. Deshalb muss er zum Homing erst über das Bett fahren, und genau das erledigt [safe_z_home]: erst X und Y homen, dann in die Bettmitte fahren, dann Z messen.
safe_z_home ist also keine Variable, sondern ein ganz normaler Klipper-Config-Abschnitt. Das Homing-Makro liest daraus genau einen Wert aus: cf.safe_z_home.z_hop ist die Höhe, auf die der Kopf nach der Messung zurückfährt.
# Z-Endschalter: der Eddy statt eines Mikroschalters
[stepper_z]
endstop_pin: probe:z_virtual_endstop
# Homing-Position - der Eddy misst nur über dem Bett
[safe_z_home]
home_xy_position: 199, 200 # Bettmitte (hier 398 x 400) - an dein Bett anpassen
z_hop: 10 # DIESEN Wert liest SET_Z_FROM_PROBE aus
z_hop_speed: 25
speed: 2002. Der SET_GCODE_OFFSET-Wrapper
Klipper selbst kennt kein runtime_offset – diese Variable gehört nicht zu Klipper, sondern zum Eddy-Paket. Es legt einen eigenen Makro-Wrapper um Klippers eingebautes SET_GCODE_OFFSET, benennt das Original per rename_existing in SET_GCODE_OFFSET_ORIG um und summiert jedes Babystepping (Z_ADJUST) stattdessen in die Makro-Variable runtime_offset.
Erst dadurch existieren überhaupt beide Namen, die das Homing-Makro benutzt. Ohne diesen Block läuft SET_Z_FROM_PROBE genau in den Fehler oben.
[save_variables] und der RESTORE_PROBE_OFFSET-Timer gehören dazu: Sie holen den dauerhaft gespeicherten Offset nach jedem Klipper-Neustart aus der variables.cfg zurück. Z_OFFSET_APPLY_PROBE aus Teil 5 schreibt ihn dorthin.
# Speicher für den dauerhaften Z-Offset
[save_variables]
filename: ~/printer_data/config/variables.cfg
# Wrapper um Klippers SET_GCODE_OFFSET.
# Das Original heißt danach SET_GCODE_OFFSET_ORIG - genau der Name,
# den SET_Z_FROM_PROBE aufruft.
[gcode_macro SET_GCODE_OFFSET]
rename_existing: SET_GCODE_OFFSET_ORIG
variable_restored: False
variable_runtime_offset: 0
gcode:
{% if params.Z_ADJUST %}
SET_GCODE_VARIABLE MACRO=SET_GCODE_OFFSET VARIABLE=runtime_offset VALUE={ printer["gcode_macro SET_GCODE_OFFSET"].runtime_offset + params.Z_ADJUST|float }
{% endif %}
{% if params.Z %}
{% set paramList = rawparams.split() %}
{% for i in range(paramList|length) %}
{% if paramList[i]=="Z=0" %}
{% set temp=paramList.pop(i) %}
{% set temp="Z_ADJUST=" + (-printer["gcode_macro SET_GCODE_OFFSET"].runtime_offset)|string %}
{% if paramList.append(temp) %}{% endif %}
{% endif %}
{% endfor %}
{% set rawparams=paramList|join(' ') %}
SET_GCODE_VARIABLE MACRO=SET_GCODE_OFFSET VARIABLE=runtime_offset VALUE=0
{% endif %}
SET_GCODE_OFFSET_ORIG { rawparams }
# Holt den gespeicherten Offset nach jedem Klipper-Neustart zurück
[delayed_gcode RESTORE_PROBE_OFFSET]
initial_duration: 1.
gcode:
{% set svv = printer.save_variables.variables %}
{% if not printer["gcode_macro SET_GCODE_OFFSET"].restored %}
SET_GCODE_VARIABLE MACRO=SET_GCODE_OFFSET VARIABLE=runtime_offset VALUE={ svv.nvm_offset|default(0) }
SET_GCODE_VARIABLE MACRO=SET_GCODE_OFFSET VARIABLE=restored VALUE=True
{% endif %}
# Schreibt den aktuellen Runtime-Offset dauerhaft in die variables.cfg
[gcode_macro Z_OFFSET_APPLY_PROBE]
rename_existing: Z_OFFSET_APPLY_PROBE_ORIG
gcode:
SAVE_VARIABLE VARIABLE=nvm_offset VALUE={ printer["gcode_macro SET_GCODE_OFFSET"].runtime_offset }3. Das G28-Wrapper-Makro
Und das Bindeglied, das leicht übersehen wird: SET_Z_FROM_PROBE ruft sich nicht von selbst auf. Damit es beim Homing greift, wird auch G28 umbenannt und um zwei Zeilen ergänzt – erst normal homen, dann einmal PROBE, dann den Z-Nullpunkt setzen. Die Abfrage sorgt dafür, dass das nur bei einem vollen G28 oder einem G28 Z passiert, nicht bei G28 X Y.
[gcode_macro G28]
rename_existing: G28.1
gcode:
G28.1 {rawparams}
{% if not rawparams or (rawparams and 'Z' in rawparams) %}
PROBE
SET_Z_FROM_PROBE
{% endif %}Teil 2: Das korrigierte Homing-Makro
Hier lag bei vielen Nutzern die Ursache für Crashes: Das alte Makro hat descend_z vom Messwert abgezogen. Das neue Klipper verrechnet den Offset intern über die Kalibrierungstabelle. Ersetze dein altes Makro:
[gcode_macro SET_Z_FROM_PROBE]
gcode:
{% set cf = printer.configfile.settings %}
# Z auf Messwert des Eddy + manuellen Runtime-Offset setzen
SET_GCODE_OFFSET_ORIG Z={printer.probe.last_z_result + printer["gcode_macro SET_GCODE_OFFSET"].runtime_offset}
G90
# Nach Homing auf Sicherheitsabstand fahren
G1 Z{cf.safe_z_home.z_hop}Was das Makro tut:
- Liest den Messwert des Eddy (
last_z_result) - Addiert deinen manuellen Runtime-Offset
- Fährt auf die sichere Z-Hop-Höhe
Was es nicht mehr tut: descend_z abziehen – das war der Bug-Verursacher.
Teil 3: Basis-Kalibrierung (Frequenz-Mapping)
Bevor der Eddy präzise arbeiten kann, muss er lernen, wie das Bett unter ihm aussieht. Diese Kalibrierung erstellt eine Frequenz-Abstands-Tabelle.
Vorbereitung
- Bett auf 60 °C vorheizen
- Druckkopf in die Mitte des Betts fahren
Kalibrierung starten
PROBE_EDDY_CURRENT_CALIBRATE CHIP=btt_eddy METHOD=paperAblauf
- Der Kopf fährt auf Z = 1.0 (dein
descend_z-Wert) - Nutze die Buttons in Mainsail oder
TESTZ Z=-0.1um die Düse abzusenken - Papier zwischen Düse und Bett – es sollte leicht klemmen
- Klicke auf ACCEPT
- Gib
SAVE_CONFIGein
Klipper schreibt eine Datentabelle ans Ende deiner printer.cfg.
Teil 4: Temperatur-Kompensation
Induktive Sensoren reagieren auf Temperaturänderungen. Die Temperatur-Kalibrierung erstellt eine Korrekturtabelle.
Voraussetzung
Der Drucker muss kalt sein (Zimmertemperatur). Alle Heizungen aus.
Kalibrierung starten
TEMPERATURE_PROBE_CALIBRATE PROBE=btt_eddy TARGET=56 STEP=4Heizung manuell starten
Da der Befehl die Heizung nicht selbst aktiviert:
SET_HEATER_TEMPERATURE HEATER=heater_bed TARGET=80Ablauf
- Klipper fordert zum Papiertest auf → ACCEPT
- Warten: Der Eddy erwärmt sich durch die Bettwärme
- Bei jeder Stufe (+4 °C): erneuter Papiertest
- Fortfahren bis Zieltemperatur (56 °C) erreicht
Abschluss
TEMPERATURE_PROBE_COMPLETE
SAVE_CONFIGTipp: Dieser Vorgang dauert 30–60 Minuten.
Teil 5: Der tägliche Betrieb
1. Homing (G28)
Der Eddy misst den Abstand. SET_Z_FROM_PROBE setzt den Z-Nullpunkt automatisch.
2. Bed Mesh
BED_MESH_CALIBRATEFührt einen rapid_scan aus – blitzschnell, der Kopf hält nicht an.
3. Feinjustierung
- Nutze den Z-Offset-Regler im Mainsail/Fluidd-Interface
Z_OFFSET_APPLY_PROBEspeichert dauerhaft invariables.cfg
Sicherheitshinweis für den ersten Test
- Führe ein G28 aus
- Halte den Finger am Not-Aus!
- ✅ Okay: Kopf fährt nach oben auf ca. 10 mm
- ❌ Stopp: Kopf fährt ins Bett → alter Wert in der Berechnung
Prüfe: Zieht dein Makro noch descend_z ab? Hast du Teil 3 neu durchgeführt? Steht noch ein alter z_offset in der Config?
Zusammenfassung
| Was | Alt (vor 2026) | Neu (ab 2026) |
|---|---|---|
| Z-Offset im Probe-Block | z_offset: -1.54 | Deprecated – durch descend_z ersetzen |
| Anfahrhöhe Kalibrierung | implizit | descend_z: 1.0 |
| Offset-Verrechnung | Manuell im Makro | Intern via Kalibrierungstabelle |
| Makro-Logik | last_z_result - descend_z | last_z_result + runtime_offset |
Die Umstellung macht das System am Ende robuster und genauer. Der Eddy arbeitet nach der neuen Kalibrierung deutlich zuverlässiger.
Anzeige · Affiliate-Link – kaufst du darüber, erhalte ich ggf. eine Provision. Für dich ändert sich am Preis nichts.
I get the following error:
Error evaluating 'gcode_macro SET_Z_FROM_PROBE:gcode': jinja2.exceptions.UndefinedError: 'extras.gcode_macro.GetStatusWrapper object' has no attribute 'gcode_macro SET_GCODE_OFFSET'
You don't give us the gcode_macro SET_GCODE_OFFSET.
I've added a new "Prerequisites" section before Part 2 with the full macro, plus [save_variables] and the G28 wrapper that actually calls SET_Z_FROM_PROBE - without that one the macro is never invoked at all. Sorry for the wasted debugging time.
The macro reads exactly one value from it: cf.safe_z_home.z_hop is the height the head returns to after measuring. I've added the section with my actual config - see "Prerequisites: What the homing macro needs".