Adaptive tap-threshold tuning for Klipper's eddy-current METHOD=tap probing.
This is a Klipper extra module that wraps upstream eddy tap probing with a bounded retry loop. It was built after testing the newer Klipper eddy tap implementation across multiple removable build plates, bed temperatures, and chamber conditions where one fixed tap_threshold was not reliable enough.
The module does not replace Klipper's eddy probe implementation. It drives the existing probe session API, reads the upstream tap analysis from probe_eddy_current, and chooses a better threshold before returning a normal probe result.
eddy_tap_autotune.py adds two G-code commands:
PROBE_EDDY_CURRENT_TAP_AUTO
PROBE_EDDY_CURRENT_TAP_PLATE_CALIBRATEPROBE_EDDY_CURRENT_TAP_AUTO:
- Starts from a configured or provided threshold.
- Runs normal upstream
PROBE METHOD=tap TAP_THRESHOLD=.... - Classifies common failures such as weak/early triggers, invalid depress distance, and insufficient slope delta.
- Raises or lowers the threshold inside configured bounds.
- Repeats until it gets a stable group of tap samples.
- Updates
printer["eddy_tap_autotune <probe>"].last_thresholdso later macros can reuse the threshold during the same Klipper session.
PROBE_EDDY_CURRENT_TAP_PLATE_CALIBRATE:
- Runs the same search in a slower "find this plate" mode.
- Writes the discovered threshold to
[probe_eddy_current <probe>] tap_threshold. - Requires
SAVE_CONFIGto persist the value and restart Klipper.
Klipper's eddy tap support needs a tap_threshold. In practice that threshold can move with:
- build plate material and magnetic behavior
- bed temperature
- chamber temperature
- nozzle contamination
- sensor mount height
- probe speed and sample timing
On our printers, a threshold that was good cold could fail hot, and a threshold that worked on one spring steel plate could false-trigger on another. The failure was not always safe or obvious: sometimes the probe stopped too high, sometimes it touched and failed validation, and sometimes it needed several manual threshold changes before homing would complete.
The goal of this module is not to make eddy tap magical. The goal is to make tap probing recover automatically inside sane limits, so a print start or plate swap does not turn into manual threshold tuning.
Tested against Klipper upstream eddy tap code around commit 02cfc65eb.
This module expects:
- A configured
[probe_eddy_current <name>]section. - Upstream eddy tap support, including
PROBE METHOD=tap. - Klipper's current
probe_eddy_currentinternals witheddy_tap.get_last_tap_info().
Because this is an external extra, it is intentionally small and isolated. If upstream Klipper changes the eddy tap analysis API, this file may need a small update.
Clone the repo, then run the installer:
cd ~/eddy_tap_autotune
./install.shIf your Klipper checkout is somewhere else, pass the path:
./install.sh /home/pi/klipperThe installer creates a symlink by default, so pulling updates in this repo updates the installed extra after a Klipper restart. If you prefer copying the file, use:
./install.sh --copy /home/pi/klipperTo uninstall:
./install.sh --uninstall /home/pi/klipperThe installer only installs eddy_tap_autotune.py into klippy/extras/. It does not patch Klipper source or firmware files.
Add an [eddy_tap_autotune <probe_name>] section to your printer config. The <probe_name> should match your [probe_eddy_current <probe_name>] suffix.
Example:
[probe_eddy_current fly_eddy]
sensor_type: ldc1612
# ... your normal eddy config ...
tap_z_offset: 0
[eddy_tap_autotune fly_eddy]
start_threshold: 5000
min_threshold: 4000
max_threshold: 30000
step: 100
min_step: 10
max_attempts: 60
probe_speed: 8
min_probe_speed: 8
probe_speed_retry_factor: 0.5
min_valid_probe_travel: 0.45
lift_speed: 10
samples: 5
sample_retract_dist: 3
start_z: 3
min_start_z: 1
retry_lower_start_z_margin: 0.5
settle_time: 0.250
samples_tolerance: 0.02
samples_tolerance_retries: 10
samples_result: median
accepted_min_depress_dist: 0.050
accepted_max_depress_dist: 0.250
accepted_min_slope_delta: 18000
accepted_slope_target_ratio: 0.20
accepted_max_z_rise: 0.050
weak_probe_z_threshold: 0.200
weak_probe_fast_step: 2000
retry_same_threshold_limit: 2
retry_raise_step: 5000
max_slope_delta_jump: 40000
final_threshold_margin: 0Restart Klipper after installing the file and adding the config section.
Manual install is also fine:
cp eddy_tap_autotune.py ~/klipper/klippy/extras/eddy_tap_autotune.pyor:
ln -sf /path/to/eddy_tap_autotune/eddy_tap_autotune.py ~/klipper/klippy/extras/eddy_tap_autotune.pyRun a tap auto probe:
PROBE_EDDY_CURRENT_TAP_AUTO START=5000 MIN=4000 MAX=30000 STEP=100 SAMPLES=5 START_Z=3Run a very conservative first-time discovery from a deliberately low threshold:
PROBE_EDDY_CURRENT_TAP_PLATE_CALIBRATE START=2500 MIN=2500 MAX=30000 STEP=100 MAX_ATTEMPTS=360 SAMPLES=5 FINAL_MARGIN=2000
SAVE_CONFIGRun a conservative plate threshold calibration:
PROBE_EDDY_CURRENT_TAP_PLATE_CALIBRATE START=5000 MIN=4000 MAX=30000 STEP=100 MAX_ATTEMPTS=360 SAMPLES=5
SAVE_CONFIGThe plate calibration command writes the threshold into [probe_eddy_current <probe>] tap_threshold. It does not persist until SAVE_CONFIG. During the current Klipper session it also updates the runtime autotune start threshold in memory.
The module only provides the adaptive probe command. Your Klipper macros decide when to call it.
A common pattern is:
- Home XY.
- Move to a known tap point.
- Coarse-home Z.
- Raise to
START_Z. - Run
PROBE_EDDY_CURRENT_TAP_AUTO. - Use the probe result to reset the kinematic Z position.
- Reuse
last_thresholdfor any follow-up tap during the same print start. - For eddy scan or rapid-scan mesh, use
[bed_mesh] zero_reference_positionand do not apply the scan/tap delta as a globalSET_GCODE_OFFSET.
See examples/eddy_tap_autotune.cfg for a working macro pattern.
If you use METHOD=scan or METHOD=rapid_scan for bed mesh, configure a
zero_reference_position in [bed_mesh] at the same physical point you use for
the tap reference. The scan mesh should then be treated as relative bed shape.
Older local macros sometimes did this after a tap-plus-scan reference:
SET_GCODE_OFFSET Z={-printer.probe.last_probe_position.z}That can lift or lower every later move by the eddy scan/tap delta. On current
Klipper this produced first-layer height errors during testing. The example
macro now logs the scan/tap delta for diagnostics, but leaves the gcode Z offset
unchanged by default. Use normal slicer or filament tuning with
SET_GCODE_OFFSET Z_ADJUST=... for first-layer squish.
This creates an easy Mainsail/Fluidd button for a slow, conservative plate threshold search:
[gcode_macro tune_plate]
description: Slow conservative eddy tap threshold tuning for the current plate
gcode:
PROBE_EDDY_CURRENT_TAP_PLATE_CALIBRATE START=5000 MIN=4000 MAX=30000 STEP=100 MAX_ATTEMPTS=360 SAMPLES=5
SAVE_CONFIGUse this after swapping to a very different build plate. Once saved, normal homing can start closer to the known-good threshold and converge much faster.
For first setup, or when you genuinely do not know a sane starting threshold yet, use a separate lower-start discovery macro:
[gcode_macro discover_tap_threshold]
description: First-time conservative eddy tap threshold discovery
gcode:
PROBE_EDDY_CURRENT_TAP_PLATE_CALIBRATE START=2500 MIN=2500 MAX=30000 STEP=100 MAX_ATTEMPTS=360 SAMPLES=5 FINAL_MARGIN=2000
SAVE_CONFIGThis path is expected to take longer. It intentionally starts low enough to catch early/weak false triggers, then raises the threshold until it finds a stable tap. FINAL_MARGIN adds extra threshold above the highest rejected low threshold, then validates that value before saving it. After it saves a threshold, normal homing should start from the saved or configured value instead of repeating the whole discovery process every print.
start_thresholdshould be conservative. The examples default to5000because it is safer for first setup, though it may take longer to converge. After a successful plate calibration, starting closer to the saved threshold can speed up normal homing.max_thresholdis a hard safety bound. Do not set it higher than you are comfortable testing.min_valid_probe_travelrejects early stops that happen far above the bed. This was important on plates that produced strong false triggers.accepted_min_depress_distrejects taps that Klipper technically accepted but barely depressed the toolhead.accepted_slope_target_ratiocan jump the threshold upward based on a strong observed contact slope. This is why plate calibration can move quickly from a low start to a useful threshold.weak_probe_fast_stepcontrols how aggressively the search climbs after weak or early false triggers. Increase it if first-time discovery from a very lowSTARTtakes too long.final_threshold_margin/FINAL_MARGINvalidates an extra threshold margin above the highest rejected low threshold. This is useful for first-time discovery where the first passing threshold may be too close to the false-trigger boundary. It is not blindly added when there were no lower rejected thresholds.samples_toleranceandsamples_tolerance_retrieskeep the final result from accepting a scattered set of taps.probe_speedmatters. Some machines are cleaner at faster tap speeds, others need slower speeds. Tune it as a machine parameter.
This code physically taps the nozzle into the bed. Test with your hand near E-stop, especially after changing sensor height, plates, probe speed, or threshold limits.
Before relying on it:
- Run the upstream eddy current calibration.
- Verify normal
PROBE METHOD=scanworks. - Verify manual
PROBE METHOD=tap TAP_THRESHOLD=<known_safe_value>works. - Make sure Z can move low enough for tap probing.
- Use sane
MIN,MAX, andSTART_Zvalues. - Keep the nozzle clean and at a temperature appropriate for your bed surface.
- It cannot fix bad eddy calibration.
- It cannot make an eddy sensor immune to all magnetic plate artifacts.
- It currently depends on upstream Klipper tap internals to read the last tap analysis.
- It does not automatically choose your first-layer offset. It only establishes the tap contact point and threshold.
eddy_tap_autotune.py Klipper extra module
install.sh Shell wrapper for install.py
install.py Install/uninstall helper
examples/eddy_tap_autotune.cfg Example config and macro snippets
README.md Project documentation