From f6e87c99ab3aa5c3e334ee5d480f005ca169577b Mon Sep 17 00:00:00 2001
From: Stu Campbell
Date: Mon, 27 Mar 2023 15:47:23 -0700
Subject: [PATCH 01/16] Changes ./install.sh to allow for multiple klipper
instances via kiauh (#85)
* modified install to allow for multiple klipper instances
* uncommented link and remove funcs for full functionality
* formatting
---
install.sh | 43 +++++++++++++++++++++++++++++++++----------
1 file changed, 33 insertions(+), 10 deletions(-)
diff --git a/install.sh b/install.sh
index 316a56d..0fae02a 100755
--- a/install.sh
+++ b/install.sh
@@ -1,17 +1,28 @@
#!/bin/bash
KLIPPER_PATH="${HOME}/klipper"
SYSTEMDDIR="/etc/systemd/system"
+NUM_INSTALLS=0
# Step 1: Verify Klipper has been installed
check_klipper()
{
- if [ "$(sudo systemctl list-units --full -all -t service --no-legend | grep -F "klipper.service")" ]; then
- echo "Klipper service found!"
- else
- echo "Klipper service not found, please install Klipper first"
- exit -1
- fi
-
+ if [[ $NUM_INSTALLS == 0 ]]; then
+ if [ "$(sudo systemctl list-units --full -all -t service --no-legend | grep -F "klipper.service")" ]; then
+ echo "klipper.service found!"
+ else
+ echo "klipper.service not found, please install Klipper first of if using multiple instances use the -n flag"
+ exit -1
+ fi
+ else
+ for (( klip = 1; klip<=$NUM_INSTALLS; klip++)); do
+ if [ "$(sudo systemctl list-units --full -all -t service --no-legend | grep -F "klipper-$klip.service")" ]; then
+ echo "klipper-$klip.service found!"
+ else
+ echo "klipper-$klip.service NOT found, please ensure you've entered the correct number of klipper instances you're running!"
+ exit -1
+ fi
+ done
+ fi
}
# Step 2: link extension to Klipper
@@ -43,8 +54,13 @@ remove_service()
# Step 4: restarting Klipper
restart_klipper()
{
- echo "Restarting Klipper..."
- sudo systemctl restart klipper
+ if [[ $NUM_INSTALLS == 0 ]]; then
+ sudo systemctl restart klipper && echo "Restarting klipper..."
+ else
+ for (( klip = 1; klip<=$NUM_INSTALLS; klip++)); do
+ sudo systemctl restart klipper-$klip && echo "Restarting klipper-$klip.service"
+ done
+ fi
}
# Helper functions
@@ -54,6 +70,11 @@ verify_ready()
echo "This script must not run as root"
exit -1
fi
+ if [[ $NUM_INSTALLS == 0 ]]; then
+ echo "Defaulted to one klipper install, if more than one instance, use -n"
+ else
+ echo "Number of Installs Selected: $NUM_INSTALLS"
+ fi
}
# Force script to exit if an error occurs
@@ -63,14 +84,16 @@ set -e
SRCDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/ && pwd )"
# Parse command line arguments
-while getopts "k:" arg; do
+while getopts "k:n:" arg; do
case $arg in
k) KLIPPER_PATH=$OPTARG;;
+ n) NUM_INSTALLS=$OPTARG;;
esac
done
# Run steps
verify_ready
+check_klipper
link_extension
remove_service
restart_klipper
From f700f528ac2234d2f29b5497f9bc35d8e7e68482 Mon Sep 17 00:00:00 2001
From: ScramblerUSA
Date: Mon, 27 Mar 2023 17:48:08 -0500
Subject: [PATCH 02/16] Always treat clearance as an absolute value (#68)
Rationale: current algorithm treats clearance as an absolute value when comparing current z position, but as a relative value when it comes to moving the head up:
if pos_z < clearance
move(pos_z + clearance)
This kind of makes sense when the head is all the way down on the bed: if 0 < 20 then move_to(20)
Then the higher the head is, the less sense it makes: if 10 < 20 then move_to(30)
In an extreme case (z at 19.9 and clearance is 20) it results in moving the head all the way up to 39.9! At the same time if the head is already at 20, it's just kept at this height.
Basically, in my opinion the logic should be "if we are below the safe level, let's move up to that level to clear the things" rather than "if we are below the safe level, let's move up the additional safe level height".
---
z_calibration.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/z_calibration.py b/z_calibration.py
index b5ceed4..89d9d6c 100644
--- a/z_calibration.py
+++ b/z_calibration.py
@@ -147,7 +147,7 @@ def cmd_PROBE_Z_ACCURACY(self, gcmd):
pos = toolhead.get_position()
if pos[2] < self.clearance:
# no clearance, better to move up
- self._move([None, None, pos[2] + self.clearance], lift_speed)
+ self._move([None, None, self.clearance], lift_speed)
# move to z-endstop position
self._move(list(self.nozzle_site), self.speed)
pos = toolhead.get_position()
@@ -266,7 +266,7 @@ def _probe_on_site(self, endstop, site, check_probe=False):
pos = self.toolhead.get_position()
if pos[2] < self.helper.clearance:
# no clearance, better to move up
- self.helper._move([None, None, pos[2] + self.helper.clearance],
+ self.helper._move([None, None, self.helper.clearance],
self.helper.lift_speed)
# move to position
self.helper._move(list(site), self.helper.speed)
From dc0d3a7888860cae6561e3f28a4047b0620795bb Mon Sep 17 00:00:00 2001
From: Titus Meyer
Date: Wed, 22 Feb 2023 00:22:22 +0100
Subject: [PATCH 03/16] split movement to endstop into seperate x and y moves
---
README.md | 2 ++
z_calibration.py | 11 ++++++++---
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 0d1e8d6..866927d 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,8 @@ And, if you love my work and would like to support me, please feel free to do th
# Changes
+- **v0.9.3** (2022-..........)
+ - Split the first movement to the endstop into separate X and Y moves to prevent hitting the dock
- **v0.9.2** (2022-07-08)
- The probe is now docked again before raising an error, if end_gcode is used (thanks to top-gun)
- Probings are more consistent now as the probing sequence is also applied for the bed probing
diff --git a/z_calibration.py b/z_calibration.py
index 89d9d6c..9f9da78 100644
--- a/z_calibration.py
+++ b/z_calibration.py
@@ -262,14 +262,18 @@ def __init__(self, helper, gcmd):
self.probe = helper.printer.lookup_object('probe')
self.toolhead = helper.printer.lookup_object('toolhead')
self.gcode_move = helper.printer.lookup_object('gcode_move')
- def _probe_on_site(self, endstop, site, check_probe=False):
+ def _probe_on_site(self, endstop, site, check_probe=False, split_xy=False):
pos = self.toolhead.get_position()
if pos[2] < self.helper.clearance:
# no clearance, better to move up
self.helper._move([None, None, self.helper.clearance],
self.helper.lift_speed)
# move to position
- self.helper._move(list(site), self.helper.speed)
+ if split_xy:
+ self.helper._move([site[0], pos[1], None], self.helper.speed)
+ self.helper._move([site[0], site[1], site[2]], self.helper.speed)
+ else:
+ self.helper._move(site, self.helper.speed)
if check_probe:
# check if probe is attached and switch is closed
time = self.toolhead.get_last_move_time()
@@ -325,7 +329,8 @@ def calibrate_z(self):
self.helper.start_gcode.run_gcode_from_command()
# probe the nozzle
nozzle_zero = self._probe_on_site(self.z_endstop,
- self.helper.nozzle_site)
+ self.helper.nozzle_site,
+ False, True)
# probe the probe-switch
self.helper.switch_gcode.run_gcode_from_command()
# probe the body of the switch
From 88cda7e7bdb67d5bf79275966c43bebb0edc88ae Mon Sep 17 00:00:00 2001
From: Titus Meyer
Date: Sat, 25 Feb 2023 01:17:04 +0100
Subject: [PATCH 04/16] add option to wiggle after nozzle probing - thanks to
poernahi
---
z_calibration.py | 43 +++++++++++++++++++++++++++----------------
1 file changed, 27 insertions(+), 16 deletions(-)
diff --git a/z_calibration.py b/z_calibration.py
index 9f9da78..da2353f 100644
--- a/z_calibration.py
+++ b/z_calibration.py
@@ -35,9 +35,10 @@ def __init__(self, config):
None, above=0.)
self.position_min = config.getfloat('position_min', None)
self.first_fast = config.getboolean('probing_first_fast', False)
- self.nozzle_site = self._get_site("nozzle_xy_position", "probe_nozzle")
- self.switch_site = self._get_site("switch_xy_position", "probe_switch")
- self.bed_site = self._get_site("bed_xy_position", "probe_bed", True)
+ self.nozzle_site = self._get_xy("nozzle_xy_position", "probe_nozzle")
+ self.switch_site = self._get_xy("switch_xy_position", "probe_switch")
+ self.bed_site = self._get_xy("bed_xy_position", "probe_bed", True)
+ self.wiggle_offsets = self._get_xy("wiggle_xy_offsets", "wiggle_offset", True)
gcode_macro = self.printer.load_object(config, 'gcode_macro')
self.start_gcode = gcode_macro.load_template(config, 'start_gcode', '')
self.switch_gcode = gcode_macro.load_template(config,
@@ -112,10 +113,10 @@ def cmd_CALIBRATE_Z(self, gcmd):
site_attr = gcmd.get("BED_POSITION", None)
if site_attr is not None:
# set bed site from BED_POSITION parameter
- self.bed_site = self._parse_site("BED_POSITION", site_attr)
- elif self._get_site("bed_xy_position", "probe_bed", True) is not None:
+ self.bed_site = self._parse_xy("BED_POSITION", site_attr)
+ elif self._get_xy("bed_xy_position", "probe_bed", True) is not None:
# set bed site from configuration
- self.bed_site = self._get_site("bed_xy_position", "probe_bed", False)
+ self.bed_site = self._get_xy("bed_xy_position", "probe_bed", False)
else:
# else get the mesh's relative reference index point
# a round mesh/bed would not work here so far...
@@ -182,7 +183,7 @@ def cmd_PROBE_Z_ACCURACY(self, gcmd):
"probe accuracy results: maximum %.6f, minimum %.6f, range %.6f,"
" average %.6f, median %.6f, standard deviation %.6f" % (
max_value, min_value, range_value, avg_value, median, sigma))
- def _get_site(self, name, legacy_prefix, optional=False):
+ def _get_xy(self, name, legacy_prefix, optional=False):
legacy_x = self.config.getfloat("%s_x" % (legacy_prefix), -1.0)
legacy_y = self.config.getfloat("%s_y" % (legacy_prefix), -1.0)
if (optional and self.config.get(name, None) is None
@@ -191,15 +192,15 @@ def _get_site(self, name, legacy_prefix, optional=False):
if legacy_x >= 0 and legacy_y >= 0:
return [legacy_x, legacy_y, None]
else:
- return self._parse_site(name, self.config.get(name))
- def _parse_site(self, name, site):
+ return self._parse_xy(name, self.config.get(name))
+ def _parse_xy(self, name, site):
try:
x_pos, y_pos = site.split(',')
return [float(x_pos), float(y_pos), None]
except:
raise self.config.error("Unable to parse %s in %s"
% (name, self.config.get_name()))
- def _probe(self, mcu_endstop, z_position, speed):
+ def _probe(self, mcu_endstop, z_position, speed, wiggle=False):
toolhead = self.printer.lookup_object('toolhead')
pos = toolhead.get_position()
pos[2] = z_position
@@ -209,6 +210,12 @@ def _probe(self, mcu_endstop, z_position, speed):
# retract
self._move([None, None, curpos[2] + self.retract_dist],
self.lift_speed)
+ if wiggle and self.wiggle_offsets is not None:
+ self._move([curpos[0] + self.wiggle_offsets[0],
+ curpos[1] + self.wiggle_offsets[1],
+ None],
+ self.speed)
+ self._move([curpos[0], curpos[1], None], self.speed)
self.gcode.respond_info("probe at %.3f,%.3f is z=%.6f"
% (curpos[0], curpos[1], curpos[2]))
return curpos
@@ -262,7 +269,8 @@ def __init__(self, helper, gcmd):
self.probe = helper.printer.lookup_object('probe')
self.toolhead = helper.printer.lookup_object('toolhead')
self.gcode_move = helper.printer.lookup_object('gcode_move')
- def _probe_on_site(self, endstop, site, check_probe=False, split_xy=False):
+ def _probe_on_site(self, endstop, site, check_probe=False, split_xy=False,
+ wiggle=False):
pos = self.toolhead.get_position()
if pos[2] < self.helper.clearance:
# no clearance, better to move up
@@ -284,14 +292,15 @@ def _probe_on_site(self, endstop, site, check_probe=False, split_xy=False):
if self.helper.first_fast:
# first probe just to get down faster
self.helper._probe(endstop, self.helper.position_min,
- self.helper.probing_speed)
+ self.helper.probing_speed, wiggle=wiggle)
retries = 0
positions = []
while len(positions) < self.helper.samples:
# probe with second probing speed
curpos = self.helper._probe(endstop,
self.helper.position_min,
- self.helper.second_speed)
+ self.helper.second_speed,
+ wiggle=wiggle)
positions.append(curpos[:3])
# check tolerance
z_positions = [p[2] for p in positions]
@@ -330,18 +339,20 @@ def calibrate_z(self):
# probe the nozzle
nozzle_zero = self._probe_on_site(self.z_endstop,
self.helper.nozzle_site,
- False, True)
+ check_probe=False,
+ split_xy=True,
+ wiggle=True)
# probe the probe-switch
self.helper.switch_gcode.run_gcode_from_command()
# probe the body of the switch
switch_zero = self._probe_on_site(self.z_endstop,
self.helper.switch_site,
- True)
+ check_probe=True)
# probe position on bed
probe_site = self._add_probe_offset(self.helper.bed_site)
probe_zero = self._probe_on_site(self.probe.mcu_probe,
probe_site,
- True)
+ check_probe=True)
# calculate the offset
offset = probe_zero - (switch_zero - nozzle_zero
+ self.helper.switch_offset)
From 6afafa1f74854cd05588f88c578c1151dbc5c011 Mon Sep 17 00:00:00 2001
From: Titus Meyer
Date: Sat, 25 Feb 2023 02:13:30 +0100
Subject: [PATCH 05/16] add fallback to safe_z_home position for the nozzle
site - thanks to whi-tw
---
z_calibration.py | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/z_calibration.py b/z_calibration.py
index da2353f..f8d9b61 100644
--- a/z_calibration.py
+++ b/z_calibration.py
@@ -1,7 +1,6 @@
-
-# Klipper plugin for a selfcalibrating Z offset.
+# Klipper plugin for a self-calibrating Z offset.
#
-# Copyright (C) 2021-2022 Titus Meyer
+# Copyright (C) 2021-2023 Titus Meyer
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging
@@ -35,7 +34,7 @@ def __init__(self, config):
None, above=0.)
self.position_min = config.getfloat('position_min', None)
self.first_fast = config.getboolean('probing_first_fast', False)
- self.nozzle_site = self._get_xy("nozzle_xy_position", "probe_nozzle")
+ self.nozzle_site = self._get_xy("nozzle_xy_position", "probe_nozzle", True)
self.switch_site = self._get_xy("switch_xy_position", "probe_switch")
self.bed_site = self._get_xy("bed_xy_position", "probe_bed", True)
self.wiggle_offsets = self._get_xy("wiggle_xy_offsets", "wiggle_offset", True)
@@ -61,7 +60,7 @@ def get_status(self, eventtime):
return {'last_query': self.last_state,
'last_z_offset': self.last_z_offset}
def handle_connect(self):
- # get z-endstop
+ # get z-endstop object
for endstop, name in self.query_endstops.endstops:
if name == 'z':
# check for virtual endstops..
@@ -70,6 +69,17 @@ def handle_connect(self):
" is not supported for %s"
% (self.config.get_name()))
self.z_endstop = EndstopWrapper(self.config, endstop)
+ # get z-endstop position from safe_z_home
+ if self.nozzle_site is None:
+ safe_z_home = self.printer.lookup_object('safe_z_home',
+ default=None)
+ if safe_z_home is None:
+ raise self.printer.config_error("No nozzle position"
+ " configured for %s"
+ % (self.config.get_name()))
+ self.nozzle_site = [safe_z_home.home_x_pos,
+ safe_z_home.home_y_pos,
+ None]
# get probing settings
probe = self.printer.lookup_object('probe', default=None)
if probe is None:
From 7602b8e3e7d39a77b13c48e405f85049e5e459ab Mon Sep 17 00:00:00 2001
From: Titus Meyer
Date: Sat, 25 Feb 2023 02:45:54 +0100
Subject: [PATCH 06/16] add new switch_xy_offsets parameter
---
z_calibration.py | 22 +++++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/z_calibration.py b/z_calibration.py
index f8d9b61..daa9f23 100644
--- a/z_calibration.py
+++ b/z_calibration.py
@@ -34,10 +34,17 @@ def __init__(self, config):
None, above=0.)
self.position_min = config.getfloat('position_min', None)
self.first_fast = config.getboolean('probing_first_fast', False)
- self.nozzle_site = self._get_xy("nozzle_xy_position", "probe_nozzle", True)
- self.switch_site = self._get_xy("switch_xy_position", "probe_switch")
+ self.nozzle_site = self._get_xy("nozzle_xy_position", "probe_nozzle",
+ True)
+ self.switch_site = self._get_xy("switch_xy_position", "probe_switch",
+ True)
+ self.switch_xy_offsets = self._get_xy("switch_xy_offsets",
+ "switch_offset",
+ True)
self.bed_site = self._get_xy("bed_xy_position", "probe_bed", True)
- self.wiggle_offsets = self._get_xy("wiggle_xy_offsets", "wiggle_offset", True)
+ self.wiggle_offsets = self._get_xy("wiggle_xy_offsets",
+ "wiggle_offset",
+ True)
gcode_macro = self.printer.load_object(config, 'gcode_macro')
self.start_gcode = gcode_macro.load_template(config, 'start_gcode', '')
self.switch_gcode = gcode_macro.load_template(config,
@@ -80,6 +87,15 @@ def handle_connect(self):
self.nozzle_site = [safe_z_home.home_x_pos,
safe_z_home.home_y_pos,
None]
+ # check/calculate switch position by offsets
+ if self.switch_site is None:
+ if self.switch_xy_offsets is None:
+ raise self.printer.config_error("No switch position"
+ " configured for %s"
+ % (self.config.get_name()))
+ self.switch_site = [self.nozzle_site[0] + self.switch_xy_offsets[0],
+ self.nozzle_site[1] + self.switch_xy_offsets[1],
+ None]
# get probing settings
probe = self.printer.lookup_object('probe', default=None)
if probe is None:
From b5622ebc74a3abc15390d9f5afeff4733a7d768a Mon Sep 17 00:00:00 2001
From: Titus Meyer
Date: Sat, 25 Feb 2023 03:00:38 +0100
Subject: [PATCH 07/16] remove support for deprecated x/y parameters
---
z_calibration.py | 29 +++++++++--------------------
1 file changed, 9 insertions(+), 20 deletions(-)
diff --git a/z_calibration.py b/z_calibration.py
index daa9f23..b0277ce 100644
--- a/z_calibration.py
+++ b/z_calibration.py
@@ -34,17 +34,11 @@ def __init__(self, config):
None, above=0.)
self.position_min = config.getfloat('position_min', None)
self.first_fast = config.getboolean('probing_first_fast', False)
- self.nozzle_site = self._get_xy("nozzle_xy_position", "probe_nozzle",
- True)
- self.switch_site = self._get_xy("switch_xy_position", "probe_switch",
- True)
- self.switch_xy_offsets = self._get_xy("switch_xy_offsets",
- "switch_offset",
- True)
- self.bed_site = self._get_xy("bed_xy_position", "probe_bed", True)
- self.wiggle_offsets = self._get_xy("wiggle_xy_offsets",
- "wiggle_offset",
- True)
+ self.nozzle_site = self._get_xy("nozzle_xy_position", True)
+ self.switch_site = self._get_xy("switch_xy_position", True)
+ self.switch_xy_offsets = self._get_xy("switch_xy_offsets", True)
+ self.bed_site = self._get_xy("bed_xy_position", True)
+ self.wiggle_offsets = self._get_xy("wiggle_xy_offsets", True)
gcode_macro = self.printer.load_object(config, 'gcode_macro')
self.start_gcode = gcode_macro.load_template(config, 'start_gcode', '')
self.switch_gcode = gcode_macro.load_template(config,
@@ -140,9 +134,9 @@ def cmd_CALIBRATE_Z(self, gcmd):
if site_attr is not None:
# set bed site from BED_POSITION parameter
self.bed_site = self._parse_xy("BED_POSITION", site_attr)
- elif self._get_xy("bed_xy_position", "probe_bed", True) is not None:
+ elif self._get_xy("bed_xy_position", True) is not None:
# set bed site from configuration
- self.bed_site = self._get_xy("bed_xy_position", "probe_bed", False)
+ self.bed_site = self._get_xy("bed_xy_position", False)
else:
# else get the mesh's relative reference index point
# a round mesh/bed would not work here so far...
@@ -209,14 +203,9 @@ def cmd_PROBE_Z_ACCURACY(self, gcmd):
"probe accuracy results: maximum %.6f, minimum %.6f, range %.6f,"
" average %.6f, median %.6f, standard deviation %.6f" % (
max_value, min_value, range_value, avg_value, median, sigma))
- def _get_xy(self, name, legacy_prefix, optional=False):
- legacy_x = self.config.getfloat("%s_x" % (legacy_prefix), -1.0)
- legacy_y = self.config.getfloat("%s_y" % (legacy_prefix), -1.0)
- if (optional and self.config.get(name, None) is None
- and (legacy_x < 0 or legacy_y < 0)):
+ def _get_xy(self, name, optional=False):
+ if optional and self.config.get(name, None) is None:
return None
- if legacy_x >= 0 and legacy_y >= 0:
- return [legacy_x, legacy_y, None]
else:
return self._parse_xy(name, self.config.get(name))
def _parse_xy(self, name, site):
From cd417082a51a58f26bdc939128009f4cdbfdb6a2 Mon Sep 17 00:00:00 2001
From: Titus Meyer
Date: Wed, 22 Mar 2023 23:29:58 +0100
Subject: [PATCH 08/16] add hint for position_endstop, change message
formatting
---
z_calibration.py | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/z_calibration.py b/z_calibration.py
index b0277ce..0608f63 100644
--- a/z_calibration.py
+++ b/z_calibration.py
@@ -13,6 +13,7 @@ def __init__(self, config):
self.z_homing = None
self.last_state = False
self.last_z_offset = 0.
+ self.position_z_endstop = None
self.config = config
self.printer = config.get_printer()
self.switch_offset = config.getfloat('switch_offset', 0.0, above=0.)
@@ -123,6 +124,7 @@ def handle_home_rails_end(self, homing_state, rails):
self.retract_dist = rail.homing_retract_dist
if self.position_min is None:
self.position_min = rail.position_min
+ self.position_z_endstop = rail.position_endstop
def _build_config(self):
pass
cmd_CALIBRATE_Z_help = ("Automatically calibrates the nozzle offset"
@@ -372,16 +374,21 @@ def calibrate_z(self):
offset = probe_zero - (switch_zero - nozzle_zero
+ self.helper.switch_offset)
# print result
- self.gcmd.respond_info("Z-CALIBRATION: ENDSTOP=%.3f NOZZLE=%.3f"
- " SWITCH=%.3f PROBE=%.3f --> OFFSET=%.6f"
- % (self.helper.z_homing, nozzle_zero,
- switch_zero, probe_zero, offset))
+ self.gcmd.respond_info("Z-CALIBRATION: probe=%.3f - (switch=%.3f"
+ " - nozzle=%.3f + switch_offset=%.3f) -->"
+ " new offset=%.6f"
+ % (probe_zero, switch_zero, nozzle_zero,
+ self.helper.switch_offset, offset))
+ self.gcmd.respond_info("Z-CALIBRATION: position_endstop=%.3f -"
+ " offset=%.6f --> new z position_endstop=%.3f"
+ % (self.helper.position_z_endstop, offset,
+ self.helper.position_z_endstop - offset))
# check max deviation
if abs(offset) > self.helper.max_deviation:
self.helper.end_gcode.run_gcode_from_command()
raise self.helper.printer.command_error("Offset is larger as"
- " allowed: OFFSET=%.3f"
- " MAX_DEVIATION=%.3f"
+ " allowed: offset=%.3f"
+ " > max_deviation=%.3f"
% (offset,
self.helper.max_deviation))
# set new offset
From a3c634b1fe9cc157a12790d677c35bf14273092c Mon Sep 17 00:00:00 2001
From: Titus Meyer
Date: Wed, 22 Mar 2023 23:32:31 +0100
Subject: [PATCH 09/16] raise minimum clearance to 3mm
---
z_calibration.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/z_calibration.py b/z_calibration.py
index 0608f63..821b6ba 100644
--- a/z_calibration.py
+++ b/z_calibration.py
@@ -106,7 +106,7 @@ def handle_connect(self):
self.lift_speed = probe.lift_speed
if self.clearance is None:
self.clearance = probe.z_offset * 2
- if self.clearance == 0:
+ if self.clearance < 3:
self.clearance = 20 # defaults to 20mm
if self.samples_result is None:
self.samples_result = probe.samples_result
From 6f2da2e5994e3b438f055199db9653bdb72f033b Mon Sep 17 00:00:00 2001
From: Titus Meyer
Date: Thu, 23 Mar 2023 22:54:01 +0100
Subject: [PATCH 10/16] add new command CALCULATE_SWITCH_OFFSET
---
z_calibration.py | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/z_calibration.py b/z_calibration.py
index 821b6ba..6931556 100644
--- a/z_calibration.py
+++ b/z_calibration.py
@@ -58,6 +58,9 @@ def __init__(self, config):
self.gcode.register_command('PROBE_Z_ACCURACY',
self.cmd_PROBE_Z_ACCURACY,
desc=self.cmd_PROBE_Z_ACCURACY_help)
+ self.gcode.register_command('CALCULATE_SWITCH_OFFSET',
+ self.cmd_CALCULATE_SWITCH_OFFSET,
+ desc=self.cmd_CALCULATE_SWITCH_OFFSET_help)
def get_status(self, eventtime):
return {'last_query': self.last_state,
'last_z_offset': self.last_z_offset}
@@ -205,6 +208,23 @@ def cmd_PROBE_Z_ACCURACY(self, gcmd):
"probe accuracy results: maximum %.6f, minimum %.6f, range %.6f,"
" average %.6f, median %.6f, standard deviation %.6f" % (
max_value, min_value, range_value, avg_value, median, sigma))
+ cmd_CALCULATE_SWITCH_OFFSET_help = ("Calculates a switch_offset based on"
+ " the current z position")
+ def cmd_CALCULATE_SWITCH_OFFSET(self, gcmd):
+ if self.last_z_offset is None:
+ raise gcmd.error("Must calibrate z first")
+ toolhead = self.printer.lookup_object('toolhead')
+ pos = toolhead.get_position()
+ new_switch_offset = self.switch_offset - (pos[2] - self.last_z_offset)
+ if new_switch_offset > 0.0:
+ gcmd.respond_info("switch_offset=%.3f - (current_z=%.3f - z_offset=%.3f"
+ ") --> new switch_offset=%.3f"
+ % (self.switch_offset, pos[2],
+ self.last_z_offset, new_switch_offset))
+ else:
+ gcmd.respond_info("The resulting switch offset is negative! Either"
+ " the nozzle is still too far away or something"
+ " else is wrong...")
def _get_xy(self, name, optional=False):
if optional and self.config.get(name, None) is None:
return None
From c5780706fa46e329faeca2a0961588ca6181a42a Mon Sep 17 00:00:00 2001
From: Titus Meyer
Date: Tue, 28 Mar 2023 00:34:32 +0200
Subject: [PATCH 11/16] add new readme with robot banner
---
README.md | 629 +++-----------------------------------------
pictures/banner.png | Bin 0 -> 741726 bytes
2 files changed, 40 insertions(+), 589 deletions(-)
create mode 100644 pictures/banner.png
diff --git a/README.md b/README.md
index 866927d..1e4a1f2 100644
--- a/README.md
+++ b/README.md
@@ -1,607 +1,58 @@
-# Klipper plugin for a self calibrating Z offset
+
+
+
Automatic Z-Calibration
+
-This is a plugin to self calibrate the nozzle offset to the print surface on a 3D printer
-using Klipper. There is no need for a manual Z offset or first layer calibration any more.
-It is possible to change any variable in the printer from the temperature, the nozzle,
-the flex plate, any modding on the print head or bed or even changing the Z endstop
-position value in the Klipper configuration. Any of these changes or even all of them
-together do **not** affect the first layer at all.
+
+It's like automatically baby-stepping on your 3D printer before every print, and the first layer will
+always be perfect - no matter which nozzle or new flex-plate is being tested.
+
-Here is a small video for a short demonstration:
-[https://streamable.com/wclrmc](https://streamable.com/wclrmc)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-And now, we have a nice how-to from Kapman:
-[https://youtu.be/oQYHFecsTto](https://youtu.be/oQYHFecsTto)
+## Documentation
-### If you are looking for a RRF version of this automatic Z offset calibration
+Visit the [Wiki](https://github.com/protoloft/klipper_z_calibration/wiki) to view the full documentation.
-Then, you can find it [here](https://github.com/pRINTERnOODLE/Auto-Z-calibration-for-RRF-3.3-or-later-and-Klicky-Probe) from pRINTERnOODLE - really fantastic to see this :tada:
+The latest release notes are [here](https://github.com/protoloft/klipper_z_calibration/wiki/Changelog).
-### Many thanks for all your feedback to make this happen
+:pushpin: **And remember:** The smaller the switch-offset, the further the
+ nozzle is away from the bed! :wink:
-And, if you love my work and would like to support me, please feel free to do this here:
+## Further Resources
-[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/X8X1C0DTD)
-
-# Changes
-
-- **v0.9.3** (2022-..........)
- - Split the first movement to the endstop into separate X and Y moves to prevent hitting the dock
-- **v0.9.2** (2022-07-08)
- - The probe is now docked again before raising an error, if end_gcode is used (thanks to top-gun)
- - Probings are more consistent now as the probing sequence is also applied for the bed probing
- - More secure: the check for an attached probe is now after the movement to the probing site
-- **v0.9.1** (2022-04-06)
- - The dummy system service for Moonraker's update manager is removed :tada:
- - **:exclamation:Please update this plugin and Moonraker, adapt your moonraker.conf and execute
- the install.sh script again!**
-- **v0.9.0** (2022-04-05)
- - **CAUTION:** The X and Y position configurations are now merged to a combined X,Y parameter,
- - **:exclamation:Please update your Configuration!**
- - The CALIBRATE_Z command has an optional parameter for the probing position on the bed
- - The restriction for a relative reference index in the "bed_mesh" section is removed
- - Some other small changes from the failed Klipper PR are merged back now
- - Example configurations are removed because they were just examples and confused many people
-- **v0.8.1** (2022-02-21)
- - Now, the relative reference index (RRI) of the bed mesh is read every time the calibration
- starts. So, feel free to use any adaptive mesh macro :tada:
- - Checks for homed axes and attached probe just before using it
- - A new Z-Tilt macro in the examples
- - Improvements of the documentation for installation, configuration and switch_offset
-- **v0.8.0** (2021-08-09)
- - New configurations for executing G-Code commands (useful for V1 users)
- - Bugfix for configuring the z_calibration too early (many thanks to Frix-x),
- - New example configurations
- - **Action needed** for the Moonraker update, see: [Moonraker Update Manager](#moonraker-update-manager)
-- **v0.7.0** (2021-06-23)
- - New "PROBE_Z_ACCURACY" command
- - Renaming of the dummy service (**CAUTION**: the configuration needs to be adapted for this!)
- - Fix in "_SET_ACC" Macro
-- **v0.6.2** (2021-06-01)
- - As desired, added Moonraker Update possibility.
-- **v0.5** (2021-05-30)
- - Added compatibility for newer Klipper versions.
-- **v0.4** (2021-05-17)
- - The "calibrate_z:probe_bed_x|y" settings can be omitted in the configuration and the
- "mesh:relative_reference_index" of the bed mesh is taken as default instead.
-- **v0.3** (2021-05-13)
- - A new option to first probe down fast before recording the probing samples is added.
- - And all indirect properties from other sections can be customized now.
-- **v0.2** (2021-05-12)
- - The probing repeatability is now increased dramatically by using the probing
- procedure instead of the homing procedure!
-
-# Table of Content
-
->:pray: **Please:** read this document carefully! Any details from feedbacks and trouble
->shootings are documented here!
-
-- [Why This](#why-this)
-- [Requirements](#requirements)
-- [What It Does](#what-it-does)
- - [Drawback](#drawback)
- - [Interference](#interference)
- - [Example](#example)
- - [Thermal Frame Expansion](#thermal-frame-expansion)
-- [How To Install It](#how-to-install-it)
- - [Moonraker Update Manager](#moonraker-update-manager)
-- [How To Configure It](#how-to-configure-it)
- - [Preconditions](#preconditions)
- - [Configurations](#configurations)
- - [Bed Mesh](#bed-mesh)
- - [Switch Offset](#switch-offset)
-- [How To Test It](#how-to-test-it)
-- [How To Use It](#how-to-use-it)
- - [Command CALIBRATE_Z](#command-calibrate_z)
- - [Command PROBE_Z_ACCURACY](#command-probe_z_accuracy)
-- [Ooze Mitigation](#ooze-mitigation)
-- [Disclaimer](#disclaimer)
-
-## Why This
-
-- With a Z endstop where the tip of nozzle drives on the endstop switch (like the one in
- the Voron V1 or V2), you can exchange nozzles without adapting the offset:
- ![endstop offset](pictures/endstop-offset.png)
-- Or, by using a mag-probe (or SuperPinda, but this is not probing the surface directly
- and thus needs an other offset which is not as constant as the one of a switch)
- configured as Z endstop, you can exchange the flex plates without adapting the offset:
- ![probe offset](pictures/probe-offset.png)
-- But, why can't you get both of it? Or even more.. ?
-
-And this is what I did with this plugin. I just combined these two probing methods to be
-completely independent of any offset calibrations - forever. This is so amazing! :tada:
-
-## Requirements
-
-- A Z endstop where the tip of the nozzle drives on a switch (like the standard
- Voron V1/V2 enstop). **It will not work with the virtual pin of the probe as endstop!**
- It is not essential to have a Voron printer for this - but this kind of endstop.
-- A magnetic switch based probe at the print head - instead of the stock inductive probe
- (e.g. [this ones from Annex](https://github.com/Annex-Engineering/Quickdraw_Probe),
- or the popular drop in replacement [KlickyProbe](https://github.com/jlas1/Klicky-Probe)
- with many mounting options)
-- Both, the Z endstop and mag-probe are configured properly and homing and QGL are working.
-- The "z_calibration.py" file needs to be copied to the `klipper/klippy/extras` folder.
- Klipper will then load this file if it finds the "[z_calibration]" configuration section.
- It does not interfere with the Moonraker's Klipper update since git ignores unknown
- files.
-- It's good practice to use the probe switch as normally closed. Then, macros can detect
- if the probe is attached/released properly. The plugin is also able to detect that
- the mag-probe is attached to the print head - otherwise it will stop!
-- (My previous Klipper macro for compensating the temperature based expansion of the
- Z endstop rod is **not** needed anymore.)
-
->:point_up: **Note:** After copying the pyhton script, a full Klipper service restart is
-> needed to load it!
-
-## What It Does
-
-1. A normal homing of all axes using the Z endstop for Z (this is not part of this plugin).
- Now we have a zero point in Z. Everything is in relation to this point now. So, a new
- homing would change everything, since the homing is not that precise. That is one point,
- why absolute values of the offset are not so relevant.
-2. Determine the height of the nozzle by probing the tip of it on the Z endstop
- (this can be slightly different to the homed one):
- ![nozzle position](pictures/nozzle-position.png)
-3. Determine the height of the mag-probe by probing the body of the switch on the
- z-endstop:
- ![switch position](pictures/switch-position.png)
-4. Calculate the offset between the tip of the nozzle and the trigger point of the
- mag-probe:
-
- `nozzle switch offset = mag probe height - nozzle height + switch offset`
-
- ![switch offset](pictures/switch-offset.png)
-5. Determine the height of the print surface by probing one point with the mag-probe.
-6. Now, calculate the final offset:
-
- `probe offset = probed height - calculated nozzle switch offset`
-
-7. Finally, the calculated offset is applied by using the `SET_GCODE_OFFSET` command
- (a previous offset is reset before!).
-
-### Drawback
-
-The only downside is, that the trigger point of the mag-probe cannot be probed directly.
-This is why the body of the switch is clicked on the endstop. This small offset between the
-body of the switch and the trigger point can be taken from the data sheet of the switch and
-is hardly ever influenced in any way. And, this is the perfect setting for fine tuning
-the first layer.
-
-### Interference
-
-Temperature or humidity changes are not a big deal since the switch is not affected much
-by them and all values are probed in a small time period and only the relations to each
-other are used. The nozzle height in step 2 can be determined some time later and even
-many celsius higher in the printer, compared to the homing in step 1. That is why the
-nozzle is probed again and can vary a little to the first homing position.
-
-### Example
-
-The output of the calibration with all determined positions looks like this
-(the offset is the one which is applied as GCode offset):
-
-```text
-Z-CALIBRATION: ENDSTOP=-0.300 NOZZLE=-0.300 SWITCH=6.208 PROBE=7.013 --> OFFSET=-0.170
-```
-
-The endstop value is the homed Z position which is always zero or the configure
-"stepper_z:position_endstop" setting - and in this case, it's even the same as the
-probed nozzle hight.
-
-### Thermal Frame Expansion
-
-There is a further Klipper plugin for adapting the Z height continuously to the thermal
-expansion of the printer frame after starting a print. It is from alchemyEngine and
-can be found
-[here](https://github.com/alchemyEngine/klipper_frame_expansion_comp).
-
-## How To Install It
-
-To install this plugin, you need to copy the `z_calibration.py` file into the `extras`
-folder of klipper. Like:
-
-```bash
-/home/pi/klipper/klippy/extras/z_calibration.py
-```
-
-An alternative would be to clone this repo and run the `install.sh` script. Like:
-
-```bash
-cd /home/pi
-git clone https://github.com/protoloft/klipper_z_calibration.git
-./klipper_z_calibration/install.sh
-```
-
-It's safe to execute the install script multiple times.
-
-More on this in the [Moonraker Update Manager](#moonraker-update-manager) section.
-
-### Moonraker Update Manager
-
->:bulb: **NEW:** With a current Moonraker version, the dummy service is not
-> necessary anymore. If you have updated to the 0.9.1 version, you need to adapt the
-> following configuration block in your "moonraker.conf" file. The old dummy service
-> can be removed by executing the install script again. Like:
-> `/home/pi/klipper_z_calibration/install.sh`
-
-It's possible to keep this extension up to date with the Moonraker's update manager by
-adding this configuration block to the "moonraker.conf" of your printer:
-
-```text
-[update_manager client z_calibration]
-type: git_repo
-path: ~/klipper_z_calibration
-origin: https://github.com/protoloft/klipper_z_calibration.git
-install_script: install.sh
-managed_services: klipper
-```
-
-This requires this repository to be cloned into your home directory (e.g. /home/pi):
-
-```bash
-git clone https://github.com/protoloft/klipper_z_calibration.git
-```
-
-The install script assumes that Klipper is also installed in your home directory under
-"klipper": `${HOME}/klipper`.
-
->:point_up: **NOTE:** If your Moonraker is not on a recent version, you may get an error
-> with the "managed_services" line!
-
-## How To Configure It
-
-### Preconditions
-
-As a precondition, the probe needs to be configured properly. It must work flawlessly!
-If you don't know how, **please have a look at the
-[KlickyProbe](https://github.com/jlas1/Klicky-Probe) and how to configure it!**
-There is also a very good but complex example configuration from
-[zellneralex](https://github.com/zellneralex/klipper_config/tree/master).
-
-Now, if the probe works reliably, Then this auto Z offset calibration is basically configured
-by adding the `z_calibration` section. All possible properties and it's default values
-are documented here under [configurations](#configurations).
-
-A minimal start configuration could look like this:
-
-```text
-[z_calibration]
-nozzle_xy_position:
-switch_xy_position:
-bed_xy_position:
-switch_offset:
-start_gcode:
-#before_switch_gcode:
-end_gcode:
-```
-
-It is good practice to use more than one sample and use "median" as "probe:samples_result".
-And it's **important** to configure an appropriate probe offset in X, Y and **Z**. The
-Z offset does not need to be an exact value, since we do not use it as an offset, but it
-needs to be roughly a real value!
-
-It even doesn't matter what "stepper_z:position_endstop" value is configured in Klipper.
-All positions are relative to this point - only the absolute values are different. But,
-it's advisable to configure a safe value here to not crash the nozzle into the build
-plate by accident. The plugin only changes the GCode offset and it's still possible to
-move the nozzle beyond this offset.
-
-### Configurations
-
->:bulb: **NEW:** The X and Y coordinates are now combined into one option with a "X,Y" value.
-
-The following configuration is needed to activate the plugin and to set some needed values:
-
-```text
-[z_calibration]
-nozzle_xy_position:
-# A X, Y coordinate (e.g. 100,100) of the nozzle, clicking on the Z endstop.
-switch_xy_position:
-# A X, Y coordinate (e.g. 100,100) of the probe's switch body, clicking on
-# the Z endstop.
-bed_xy_position: default from relative_reference_index of bed_mesh
-# a X, Y coordinate (e.g. 100,100) where the print surface (e.g. the center
-# point) is probed. These coordinates will be adapted by the
-# probe's X and Y offsets. The default is the relative_reference_index
-# of the configured bed_mesh, if configured. It's possible to change the relative
-# reference index at runtime or use the GCode argument BED_POSITION of CALIBRATE_Z.
-switch_offset:
-# The trigger point offset of the used mag-probe switch.
-# Larger values will position the nozzle closer to the bed.
-# This needs to be find out manually. More on this later
-# in this section..
-max_deviation: 1.0
-# The maximum allowed deviation of the calculated offset.
-# If the offset exceeds this value, it will stop!
-# The default is 1.0 mm.
-samples: default from "probe:samples" section
-# The number of times to probe each point. The probed z-values
-# will be averaged. The default is from the probe's configuration.
-samples_tolerance: default from "probe:samples_tolerance" section
-# The maximum Z distance (in mm) that a sample may differ from other
-# samples. The default is from the probe's configuration.
-samples_tolerance_retries: default from "probe:samples_tolerance_retries" section
-# The number of times to retry if a sample is found that exceeds
-# samples_tolerance. The default is from the probe's configuration.
-samples_result: default from "probe:samples_result" section
-# The calculation method when sampling more than once - either
-# "median" or "average". The default is from the probe's configuration.
-clearance: 2 * z_offset from the "probe:z_offset" section
-# The distance in mm to move up before moving to the next
-# position. The default is two times the z_offset from the probe's
-# configuration.
-position_min: default from "stepper_z:position_min" section.
-# Minimum valid distance (in mm) used for probing move. The
-# default is from the Z rail configuration.
-speed: 50
-# The moving speed in X and Y. The default is 50 mm/s.
-lift_speed: default from "probe:lift_speed" section
-# Speed (in mm/s) of the Z axis when lifting the probe between
-# samples and clearance moves. The default is from the probe's
-# configuration.
-probing_speed: default from "stepper_z:homing_speed" section.
-# The fast probing speed (in mm/s) used, when probing_first_fast
-# is activated. The default is from the Z rail configuration.
-probing_second_speed: default from "stepper_z:second_homing_speed" section.
-# The slower speed (in mm/s) for probing the recorded samples.
-# The default is second_homing_speed of the Z rail configuration.
-probing_retract_dist: default from "stepper_z:homing_retract_dist" section.
-# Distance to retract (in mm) before probing the next sample.
-# The default is homing_retract_dist from the Z rail configuration.
-probing_first_fast: false
-# If true, the first probing is done faster by the probing speed.
-# This is to get faster down and the result is not recorded as a
-# probing sample. The default is false.
-start_gcode:
-# A list of G-Code commands to execute prior to each calibration command.
-# See docs/Command_Templates.md for G-Code format. This can be used to
-# attach the probe.
-before_switch_gcode:
-# A list of G-Code commands to execute prior to each probing on the
-# mag-probe. See docs/Command_Templates.md for G-Code format. This can be
-# used to attach the probe after probing on the nozzle and before probing
-# on the mag-probe.
-end_gcode:
-# A list of G-Code commands to execute after each calibration command.
-# See docs/Command_Templates.md for G-Code format. This can be used to
-# detach the probe afterwards.
-```
+A great how-to video by Kapman: [https://youtu.be/oQYHFecsTto](https://youtu.be/oQYHFecsTto)
->:bulb: **INFO:** The settings about probing from this section do not apply to the probing on the
->bed, since the script just calls the probe to do it's job at this point. Only the first fast down
->probing is covered by this script directly.
+##### And if you are looking for an RRF version of this automatic z-offset calibration
-### Bed Mesh
+You can find one [here](https://github.com/pRINTERnOODLE/Auto-Z-calibration-for-RRF-3.3-or-later-and-Klicky-Probe) from pRINTERnOODLE - This is really fantastic to see :tada:
->:bulb: **NEW:** Adaptive mesh macros can be used now by redefining the RRI of the mesh and/or by
-> using the new "BED_POSITION" parameter of the "CALIBRATE_Z" command.
+## Thanks for all your feedback and support!
-If you use a bed mesh, it is advised to configure it with a relative reference index
-("bed_mesh:relative_reference_index" setting). But this is not enforced anymore. With a configured
-relative reference, the position at this index will become the Z=0 point of the mesh. So, it's
-good to calibrate Z at this point. If the configuration lacks a "bed_xy_position", then the
-relative reference index will be read every time the calibration is started. Thereby it's
-possible to change this index by a macro at runtime.
+And if you like my work and want to support me, you can do so here:
-This is used by adaptive mesh macros. They create a smaller mesh which is only on the area used
-by the starting print. One example would be the macros from Frix-x which can be found
-[here](https://github.com/Frix-x/klipper-voron-V2).
-
->:point_up: **NOTE:** Be careful with adaptive mesh macros because they use Klipper's mesh function
->in an unsupported way! It's important to calibrate Z correctly for the created mesh!
-
-### Switch Offset
-
-The "z_calibration:switch_offset" is the already mentioned offset from the switch body
-(which is the probed position) to the actual trigger point above it. A starting point
-for this value can be taken from the data sheet of the Omron switch (D2F-5: 0.5mm and SSG-5H: 0.7mm).
-It's safe to start with a little less depending on the squishiness you prefer for the
-first layer (for me, it's about 0.46 for the D2F-5). So, with a smaller offset value, the nozzle
-is more away from the bed! The value cannot be negative.
-
-For example, the data sheet of the D2F-5:
-
-![endstop offset](pictures/d2f-example.png)
-
-And the calculation of the offset base:
-
-```text
-offset base = OP (Operation Position) - switch body height
- 0.5 mm = 5.5 mm - 5 mm
-```
-
-#### How About A Negative Switch Offset?
-
-First of all, there cannot be a negative switch_offset! If the switch_offset is already
-really small after tuning it and the nozzle is still too close to the bed, then there is
-something wrong measuring the probe's body. The following image illustrates this context:
-
-![switch offset](pictures/negative-switch-offset.png)
-
-So, please check your endstop, the rod of the endstop and the position touching the body
-of the probe's switch!
-
-> **:exclamation: Please, do NOT drive the endstop pin on the switch's actuator directly!
-> Otherwise, you do it on your own risk and I will reject any request for support!**
-
-If you do so, a correct or at least a working measured hight at the switch is all up to the
-different forces in this system. But forces can change due to many reasons. The best case
-would be that the actuator is pushed all the way in until the pin touches the body of the
-switch - before the endstop is triggered! But it can also be anything in between...
-So, there is no reason to not touch the body directly in a safe and robust way :thumbsup:
-
-## How To Test It
-
-Do not bother too much about absolute values of the calculated offsets. These can vary a lot.
-Only the real position from the nozzle to the bed counts. To test this, the result of the
-calibration can be queried by `GET_POSITION` first:
-
-```text
-> CALIBRATE_Z
-> Z-CALIBRATION: ENDSTOP=-0.300 NOZZLE=-0.267 SWITCH=2.370 PROBE=3.093 --> OFFSET=-0.010000
-> GET_POSITION
-> mcu: stepper_x:17085 stepper_y:15625 stepper_z:-51454 stepper_z1:-51454 stepper_z2:-51454 stepper_z3:-51454
-> stepper: stepper_x:552.500000 stepper_y:-47.500000 stepper_z:10.022500 stepper_z1:10.022500 stepper_z2:10.022500 stepper_z3:10.022500
-> kinematic: X:252.500000 Y:300.000000 Z:10.022500
-> toolhead: X:252.500000 Y:300.000000 Z:10.021472 E:0.000000
-> gcode: X:252.500000 Y:300.000000 Z:9.990000 E:0.000000
-> gcode base: X:0.000000 Y:0.000000 Z:-0.010000 E:0.000000
-> gcode homing: X:0.000000 Y:0.000000 Z:-0.010000
-```
-
-Here, the Z position in "gcode base" reflects the calibrated Z offset.
-
-Then, the offset can be tested by moving the nozzle slowly down to zero by moving it in
-multiple steps. It's good to do this by using GCodes, since the offset is applied as
-GCode-Offset. For example like this:
-
-```gcode
-> G90
-> G0 Z5
-> G0 Z3
-> G0 Z1
-> G0 Z0.5
-> G0 Z0.3
-> G0 Z0.1
-```
-
-Check the distance to the print surface after every step. If there is a small discrepancy
-(which should be smaller than the offset base from the switch's data sheet), then adapt
-the "z_calibration:switch_offset" by that value. Decreasing the "switch_offset" will move
-the nozzle more away from the bed.
-
-And finally, if you have double checked, that the calibrated offset is correct, you can go
-for fine tuning the "z_calibration:switch_offset" by actually printing first layer tests.
-
-## How To Use It
-
->:point_up: **NOTE:** If you calibrate Z within a `SAVE_GCODE_STATE` and `RESTORE_GCODE_STATE`,
-> the calibrated offset will be lost after calling `RESTORE_GCODE_STATE`!
-
-### Command CALIBRATE_Z
-
->:bulb: **NEW:** The configured position for probing on the bed can now be overwritten by
-> using the new "BED_POSITION" parameter.
-
-The Z offset calibration is started by:
-
-```text
-CALIBRATE_Z [BED_POSITION=]
-```
-
-The optional "BED_POSITION" parameter can be used to define a different position to be
-probed on the bed. But, if the "bed_xy_position" is not configured and there is no bed mesh
-or relative reference index, this parameter becomes mandatory.
-
-The "BED_POSITION" argument overrules any configured bed position.
-
-If the probe is not attached to the print head, it will abort the calibration process
-(if configured normally closed). So, macros can help here to attach and detach the probe like
-this:
-
-```gcode
-[gcode_macro CALIBRATE_Z]
-rename_existing: BASE_CALIBRATE_Z
-gcode:
- {% set bed_position = params.BED_POSITION|default('None') %}
- G28 # can also be a conditional homing macro
- M117 Z-Calibration..
- ATTACH_PROBE # a macro for fetching the probe first
- {% if bed_position != 'None' %}
- BASE_CALIBRATE_Z BED_POSITION={bed_position}
- {% else %}
- BASE_CALIBRATE_Z
- {% endif %}
- DETACH_PROBE # and parking it afterwards (or DOCK_PROBE in klicky macros)
- M117
-```
-
->:bulb: **INFO:** Meanwhile, it's possible to use the "start_gcode" or "before_switch_gcode"
-> and the "end_gcode" instead of rewriting the `CALIBRATE_Z` command!
-
-Then the `CALIBRATE_Z` command needs to be added to the `PRINT_START` macro.
-
-**:exclamation: And remove any Z offset adjustments here (like `SET_GCODE_OFFSET`) :exclamation:**
-
-The print start sequence could look like this:
-
-1. Home all axes
-2. Heat up the bed and nozzle (and chamber)
-3. Get probe, make QGL or Z-Tilt, park probe
-4. Purge and clean the nozzle if available
-5. (Get probe), CALIBRATE_Z, (park probe)
-6. (Adjust Z offset if needed)
-7. Print intro line
-8. Start printing...
-
-For textured print plates it can be necessary to adjust the offset to be more close to the bed.
-This can be done from the Slicers start GCode by adding a parameter to the `PRINT_START` macro
-**after** the Z calibration:
-
-```text
-# Adjust the G-Code Z offset if needed
-SET_GCODE_OFFSET Z_ADJUST={params.Z_ADJUST|default(0.0)|float} MOVE=1
-```
-
-Then, you can use `PRINT_START Z_ADJUST=0.0` in your Slicer. This does **not** reset the
-offset set by the calibration but adjusts it by the given value!
-
->:point_up: **NOTE:** Do not home Z again after running this calibration or it needs to be executed again!
-
-Now, I wish you happy printing with an always perfect first layer - doesn't matter what you just
-modded on your printer's head or bed or what nozzle and flex plate you like to use for your next
-project. It's just perfect :smiley:
-
-#### Resetting the Calibration
-
-The calibration can be resetted by executing this GCode command:
-
-```text
-SET_GCODE_OFFSET Z=0.0
-```
-
-### Command PROBE_Z_ACCURACY
-
-There is also a PROBE_Z_ACCURACY command to test the accuracy of the Z endstop:
-
-```text
-PROBE_Z_ACCURACY [PROBE_SPEED=] [LIFT_SPEED=] [SAMPLES=] [SAMPLE_RETRACT_DIST=]
-```
-
-It calculates the maximum, minimum, average, median and standard deviation of multiple probe samples on
-the endstop by taking the configured nozzle position on the endstop. The optional parameters default
-to their equivalent setting in the z_calibration config section.
-
-## Ooze Mitigation
-
-Ooze with any nozzle probe endstop can cause inaccuracies, as the filament will continue to leak or
-deform over the space of multiple probes. It is highly recommended to take some measures to prevent
-ooze buildup before the nozzle probe portion of this plugin.
-
-A slow long retraction, of as much as 15mm at print end, can reduce the potential for ooze. If you do
-this, consider adding a comparable extrude as the last command in your print start sequence to bring
-the plastic back to the tip. (Retracts longer than 5mm have been linked to clogs in many hotends,
-especially the Rapido. This may be best considered a last resort, depending on exact hardware and
-filament.)
-
-Heating the nozzle about a minute before scrubbing - using a purge bucket - will allow all the
-remaining plastic time to drain from the nozzle and be cleaned away by a simple wipe. If using a
-purge and scrubbing bucket, do not purge filament at this stage.
-
-An endstop switch that requires a stronger activation force, such as sexbolt with a spring, or
-unklicky z, can help squash any remaining ooze and improve consistency.
+[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/X8X1C0DTD)
-Probing can be done with a hotend temperature below the full temperature for the print. If you print
-at 250, you can preheat the nozzle to 180, and run this script before finishing the heat up to full
-temperature. This may have varying effects depending on temperatures used.
+## A Quick Demonstration
-Also consider picking up your probe prior to your nozzle wipe, to allow this script to probe the
-nozzle immediately after cleaning it.
+
## Disclaimer
-You use this on your onw risk! I'm not responsible for any damage this could lead to. Although,
-this extension works rock solid for me and many more for almost a year now. Be always carefully
-and double check everything while configuring or working with your printer. Do never leave it
-unattended during prints!
+You use it at your onw risk! I'm not responsible for any damage that might result. Although,
+this extension works rock solid for me and many others for years now. Always be careful
+and double check everything when configuring or working with your printer. And as always,
+never leave unattended while printing!
diff --git a/pictures/banner.png b/pictures/banner.png
new file mode 100644
index 0000000000000000000000000000000000000000..1512c36b948e16760f775954110caab5f7431f27
GIT binary patch
literal 741726
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