This commit is contained in:
adapt-to-it 2026-01-11 23:54:59 +01:00 committed by GitHub
commit 11ca4a3f78
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 319 additions and 0 deletions

View file

@ -223,6 +223,81 @@ far above the nozzle as possible to avoid it touching printed parts.
If an adjustment is made to the probe position, then rerun the probe
calibration steps.
## Automatic Z-Offset Calibration
The BLTouch supports automatic z-offset calibration using electrical
contact detection between the nozzle and a conductive bed surface.
This feature eliminates the need for manual paper testing and provides
accurate, repeatable results.
### Hardware Requirements
- **Conductive bed surface**: Aluminum, steel, or exposed metal
- **GPIO connection**: Bed surface connected to a GPIO pin with pull-up resistor
- **Electrical ground**: Nozzle/hotend connected to ground
- **Clean nozzle**: Free of plastic residue for reliable contact
### Configuration
Add the `nozzle_probe_pin` parameter to your [bltouch] section:
```
[bltouch]
# ... existing configuration ...
nozzle_probe_pin: ^!P1.25 # GPIO pin for bed connection
calibration_position: 100, 100 # XY position for calibration
nozzle_temp: 200 # Heating temperature during calibration
```
### Usage
Basic automatic calibration:
```
BLTOUCH_AUTO_Z_OFFSET
```
With options:
```
# Calibration without heating (for testing)
BLTOUCH_AUTO_Z_OFFSET HEAT_NOZZLE=0
# Calibration at specific position
BLTOUCH_AUTO_Z_OFFSET POSITION=150,150
# Custom temperature
BLTOUCH_AUTO_Z_OFFSET NOZZLE_TEMP=210
```
The calibration process:
1. Homes all axes if needed
2. Moves to the calibration position
3. Heats the nozzle (if enabled)
4. Performs BLTouch probing for reference
5. Detects electrical contact between nozzle and bed
6. Calculates and updates the z-offset
After calibration, use `SAVE_CONFIG` to make the new z-offset permanent.
### Bed Preparation
**For PEI/PET coated beds**: Carefully scrape away a small 5x5mm area
of coating to expose the underlying metal at your calibration position.
Clean with isopropyl alcohol.
**For non-conductive beds**: Apply copper tape or aluminum foil at the
calibration position and connect with wire to the GPIO pin.
### Troubleshooting
**"Nozzle probe already triggered" error**: Check GPIO pin configuration
(pull-up and inversion), verify wiring with multimeter.
**Inconsistent results**: Clean nozzle thoroughly, check bed conductivity,
ensure stable electrical connections.
**Large offset errors**: Verify BLTouch mounting security, confirm
electrical continuity with multimeter.
## BL-Touch output mode
* A BL-Touch V3.0 supports setting a 5V or OPEN-DRAIN output mode,

View file

@ -2199,6 +2199,45 @@ control_pin:
#samples_tolerance:
#samples_tolerance_retries:
# See the "probe" section for information on these parameters.
#nozzle_probe_pin:
# Pin connected to the conductive bed surface for nozzle contact detection
# during automatic z-offset calibration. This parameter enables the
# BLTOUCH_AUTO_Z_OFFSET command. The pin should be configured with a
# pull-up resistor (prefix with ^) and may need logic inversion (prefix
# with !). Example: "^!P1.25" for an inverted pin P1.25 with pull-up.
# This parameter is optional; if not specified, auto calibration will
# not be available.
#calibration_position: 5, 5
# X, Y coordinates (in mm) where automatic z-offset calibration will be
# performed. This should be a position on the bed with good electrical
# conductivity and within the probe's reachable area considering the
# probe offsets. The default is 5, 5.
#nozzle_temp: 200
# Nozzle temperature (in Celsius) to use during automatic calibration.
# Heating ensures any residual filament is soft and won't interfere
# with electrical contact detection. The default is 200.
#calibration_probe_speed: 5.0
# Speed (in mm/s) for initial probe movements during calibration.
# The default is 5.0.
#calibration_probe_speed_slow: 1.0
# Speed (in mm/s) for final accurate probe movements during calibration.
# Lower speeds increase accuracy. The default is 1.0.
#calibration_lift_speed: 10.0
# Speed (in mm/s) for lift movements between probing operations during
# calibration. The default is 10.0.
#calibration_retract_dist: 2.0
# Distance (in mm) to lift between BLTouch probe attempts during
# calibration. The default is 2.0.
#calibration_nozzle_retract_dist: 2.0
# Distance (in mm) to lift between nozzle touch attempts during
# calibration. The default is 2.0.
#calibration_nozzle_samples: 1
# Number of nozzle touch samples to average for increased accuracy.
# More samples increase calibration time but may improve consistency.
# Must be between 1 and 10. The default is 1.
#calibration_nozzle_diameter: 0.4
# Nozzle diameter (in mm) for informational purposes and future
# enhancements to the calibration algorithm. The default is 0.4.
```
### [smart_effector]

View file

@ -267,6 +267,28 @@ V3.0 or V3.1 may also support `set_5V_output_mode`,
`BLTOUCH_STORE MODE=<output_mode>`: This stores an output mode in the
EEPROM of a BLTouch V3.1 Available output_modes are: `5V`, `OD`
#### BLTOUCH_AUTO_Z_OFFSET
`BLTOUCH_AUTO_Z_OFFSET [HEAT_NOZZLE=<0|1>] [NOZZLE_TEMP=<temp>] [POSITION=<x,y>]`:
Performs automatic z-offset calibration for the BLTouch using electrical
contact detection between the nozzle and a conductive bed surface. This
command requires `nozzle_probe_pin` to be configured in the [bltouch]
section.
The calibration process:
1. Homes all axes if not already done
2. Moves to the calibration position
3. Heats the nozzle (if enabled)
4. Performs BLTouch probing to establish a reference
5. Detects electrical contact between nozzle and bed
6. Calculates and updates the z-offset
Parameters:
- `HEAT_NOZZLE`: Set to 0 to skip nozzle heating, 1 to heat (default: 1)
- `NOZZLE_TEMP`: Override nozzle temperature in Celsius (default: from config)
- `POSITION`: Override calibration position as "x,y" in mm (default: from config)
Note: After calibration, use `SAVE_CONFIG` to make the new z-offset permanent.
### [configfile]
The configfile module is automatically loaded.

View file

@ -1,6 +1,7 @@
# BLTouch support
#
# Copyright (C) 2018-2024 Kevin O'Connor <kevin@koconnor.net>
# Copyright (C) 2025 Marco Abbattista <max.abbattista@llive.it>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging
@ -41,6 +42,34 @@ class BLTouchProbe:
self.finish_home_complete = self.wait_trigger_complete = None
# Create an "endstop" object to handle the sensor pin
self.mcu_endstop = ppins.setup_pin('endstop', config.get('sensor_pin'))
# Setup nozzle probe pin for auto z-offset calibration (optional)
nozzle_probe_pin = config.get('nozzle_probe_pin', None)
if nozzle_probe_pin is not None:
self.nozzle_endstop = ppins.setup_pin('endstop', nozzle_probe_pin)
# Register Z steppers with the nozzle endstop
probe.LookupZSteppers(config, self.nozzle_endstop.add_stepper)
self.calibration_position = config.getfloatlist('calibration_position',
default=[5., 5.], count=2)
self.nozzle_temp = config.getfloat('nozzle_temp', 200., above=0.)
# Calibration probe speeds and distances
self.cal_probe_speed = config.getfloat('calibration_probe_speed', 5.0, above=0.)
self.cal_probe_speed_slow = config.getfloat('calibration_probe_speed_slow', 1.0, above=0.)
self.cal_lift_speed = config.getfloat('calibration_lift_speed', 10.0, above=0.)
self.cal_retract_dist = config.getfloat('calibration_retract_dist', 2.0, above=0.)
self.cal_nozzle_retract_dist = config.getfloat('calibration_nozzle_retract_dist', 2.0, above=0.)
self.cal_nozzle_samples = config.getint('calibration_nozzle_samples', 1, minval=1, maxval=10)
self.cal_nozzle_diameter = config.getfloat('calibration_nozzle_diameter', 0.4, above=0.)
else:
self.nozzle_endstop = None
self.calibration_position = None
self.nozzle_temp = None
self.cal_probe_speed = None
self.cal_probe_speed_slow = None
self.cal_lift_speed = None
self.cal_retract_dist = None
self.cal_nozzle_retract_dist = None
self.cal_nozzle_samples = None
self.cal_nozzle_diameter = None
# output mode
omodes = ['5V', 'OD', None]
self.output_mode = config.getchoice('set_output_mode', omodes, None)
@ -75,6 +104,11 @@ class BLTouchProbe:
desc=self.cmd_BLTOUCH_DEBUG_help)
self.gcode.register_command("BLTOUCH_STORE", self.cmd_BLTOUCH_STORE,
desc=self.cmd_BLTOUCH_STORE_help)
# Register auto z-offset calibration command if nozzle probe is configured
if self.nozzle_endstop is not None:
self.gcode.register_command("BLTOUCH_AUTO_Z_OFFSET",
self.cmd_BLTOUCH_AUTO_Z_OFFSET,
desc=self.cmd_BLTOUCH_AUTO_Z_OFFSET_help)
# Register events
self.printer.register_event_handler("klippy:connect",
self.handle_connect)
@ -281,6 +315,155 @@ class BLTouchProbe:
self.sync_print_time()
self.store_output_mode(cmd)
self.sync_print_time()
cmd_BLTOUCH_AUTO_Z_OFFSET_help = "Automatically calibrate BLTouch z-offset using nozzle probe"
def cmd_BLTOUCH_AUTO_Z_OFFSET(self, gcmd):
if self.nozzle_endstop is None:
raise gcmd.error("nozzle_probe_pin not configured in [bltouch] section")
# Check if nozzle probe is already triggered before starting
curtime = self.printer.get_reactor().monotonic()
nozzle_triggered = self.nozzle_endstop.query_endstop(curtime)
if nozzle_triggered:
raise gcmd.error("Nozzle probe is already triggered. Please check for obstructions and ensure the nozzle is clear before starting calibration.")
toolhead = self.printer.lookup_object('toolhead')
gcode = self.printer.lookup_object('gcode')
heaters = self.printer.lookup_object('heaters')
# Get optional parameters
heat_nozzle = gcmd.get_int('HEAT_NOZZLE', 1) # Default: 1 (enabled)
nozzle_temp = gcmd.get_float('NOZZLE_TEMP', self.nozzle_temp, above=0.)
cal_pos = gcmd.get('POSITION', None)
if cal_pos:
cal_x, cal_y = [float(v.strip()) for v in cal_pos.split(',')]
else:
cal_x, cal_y = self.calibration_position
# Step 1: Home all axes
gcode.run_script_from_command("G28")
# Step 2: Move to calibration position considering BLTouch offset
# If we want BLTouch at position (cal_x, cal_y), we need to move toolhead to:
# toolhead_x = cal_x - x_offset, toolhead_y = cal_y - y_offset
x_offset, y_offset, _ = self.get_offsets()
toolhead_x = cal_x - x_offset
toolhead_y = cal_y - y_offset
toolhead.manual_move([toolhead_x, toolhead_y, None], 50.)
# Step 3: Heat nozzle if needed and enabled
if heat_nozzle and nozzle_temp > 0:
gcmd.respond_info("Heating nozzle to %.1f°C..." % nozzle_temp)
extruder = toolhead.get_extruder()
heaters.set_temperature(extruder.get_heater(), nozzle_temp, True)
# Step 4: BLTouch probe - EXACT SAME as PROBE_CALIBRATE
gcmd.respond_info("Performing BLTouch probe (PROBE_CALIBRATE method)...")
# Import probe utilities
from . import probe
# Use run_single_probe - EXACT SAME method as PROBE_CALIBRATE
# This ensures same coordinate reference system
bltouch_pos = probe.run_single_probe(self, gcmd)
z_bltouch_reference = bltouch_pos[2]
gcmd.respond_info("BLTouch reference position: %.6f" % z_bltouch_reference)
# Step 5: Move up and position nozzle - SAME as PROBE_CALIBRATE
# Move away from the bed (same 5mm lift as PROBE_CALIBRATE)
lift_pos = list(bltouch_pos)
lift_pos[2] += 5.
toolhead.manual_move(lift_pos, self.cal_lift_speed)
# Move the nozzle over the probe point - SAME as PROBE_CALIBRATE
x_offset, y_offset, z_offset = self.get_offsets()
lift_pos[0] += x_offset
lift_pos[1] += y_offset
toolhead.manual_move(lift_pos, self.cal_probe_speed)
gcmd.respond_info("Nozzle positioned over probe point at Z=%.6f" % lift_pos[2])
# Step 6: Nozzle probe - Use direct probing method for endstop
gcmd.respond_info("Performing nozzle probe (PROBE_CALIBRATE coordinate system)...")
# Use homing module directly with nozzle endstop - SAME coordinate system
phoming = self.printer.lookup_object('homing')
# Get current position and probe down to find bed
current_pos = toolhead.get_position()
# Get Z minimum position safely
z_min = -5.0 # Use position_min from config
try:
config = self.printer.lookup_object('configfile')
z_config = config.status_raw_config.get('stepper_z', {})
z_min = float(z_config.get('position_min', -5.0))
except:
z_min = -5.0
# Create probe target position - probe down to find bed
probe_target = [current_pos[0], current_pos[1], z_min]
gcmd.respond_info("Nozzle probing from Z=%.3f to Z=%.3f..." % (current_pos[2], z_min))
# Use probing_move with nozzle endstop - SAME coordinate system as BLTouch
nozzle_pos = phoming.probing_move(self.nozzle_endstop, probe_target, self.cal_probe_speed_slow)
z_nozzle_reference = nozzle_pos[2]
gcmd.respond_info("Nozzle reference position: %.6f" % z_nozzle_reference)
# Step 7: Calculate z_offset - EXACT SAME formula as PROBE_CALIBRATE
# z_offset = probe_calibrate_z - final_nozzle_z
# In PROBE_CALIBRATE: z_offset = self.probe_calibrate_z - kin_pos[2]
new_z_offset = z_bltouch_reference - z_nozzle_reference
gcmd.respond_info("BLTouch reference: %.6f, Nozzle reference: %.6f" %
(z_bltouch_reference, z_nozzle_reference))
gcmd.respond_info("Calculated z_offset: %.6f" % new_z_offset)
# Validate against expected physical measurement
expected_offset = 4.1 # Your measured physical distance
offset_error = abs(new_z_offset - expected_offset)
gcmd.respond_info("Calculated: %.6f, Expected: %.6f, Error: %.6f" %
(new_z_offset, expected_offset, offset_error))
if offset_error > 1.0:
gcmd.respond_info("WARNING: Large offset error detected. Check BLTouch mounting and nozzle probe configuration.")
# Validate that the offset makes physical sense
if new_z_offset < 0:
raise gcmd.error(
"Invalid z-offset calculation: %.6f\n"
"BLTouch Z: %.6f, Nozzle Z: %.6f\n"
"The nozzle should trigger at a lower Z position than the BLTouch.\n"
"Please check your wiring and probe configuration." %
(new_z_offset, z_bltouch_reference, z_nozzle_reference))
# Move up for safety
toolhead.manual_move([None, None, max(z_bltouch_reference, z_nozzle_reference) + 10.], 50.)
# Save the positive offset value
save_z_offset = new_z_offset
# Save the new z-offset
self.position_endstop = save_z_offset
configfile = self.printer.lookup_object('configfile')
configfile.set('bltouch', 'z_offset', "%.6f" % save_z_offset)
gcmd.respond_info(
"BLTouch z_offset calibration complete!\n"
"New z_offset: %.6f\n"
"The SAVE_CONFIG command will update the printer config file\n"
"with the above and restart the printer." % save_z_offset)
# Re-home to restore coordinate system integrity
gcmd.respond_info("Re-homing to restore coordinate system...")
gcode.run_script_from_command("G28")
# Verify coordinate restoration
restored_pos = toolhead.get_position()
gcmd.respond_info("Coordinate system restored. Current Z position: %.6f" % restored_pos[2])
def load_config(config):
blt = BLTouchProbe(config)