2018-11-25 05:41:25 +03:00
|
|
|
# BLTouch support
|
|
|
|
#
|
2021-03-29 21:29:33 +03:00
|
|
|
# Copyright (C) 2018-2021 Kevin O'Connor <kevin@koconnor.net>
|
2018-11-25 05:41:25 +03:00
|
|
|
#
|
|
|
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
2020-04-08 22:17:14 +03:00
|
|
|
import logging
|
2020-06-12 16:55:57 +03:00
|
|
|
from . import probe
|
2018-11-25 05:41:25 +03:00
|
|
|
|
bltouch: Update command timing
Prior to the BLTouch v3, the recommended command times were 700us
(pin_down), 1200us (touch_mode), 1500us (pin_up), 1800us (self_test),
and 2200us (reset). However, the recommended Marlin timing (via servo
"angles") was 647.111, 1162.667, 1472, 1781.333, and 2193.778us.
As of the BLTouch v3, the recommended times are now 650, 1165, 1475,
1780, and 2190us. The v3 continues to recommended Marlin timings of
647.111, 1162.667, 1472, 1781.333, and 2193.778us.
Update Klipper to use the new BL-Touch v3 recommended timing. The new
timings are required for the BL-Touch v3 and they are closer to what
the Marlin firmware has historically used.
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2019-05-21 01:50:56 +03:00
|
|
|
SIGNAL_PERIOD = 0.020
|
|
|
|
MIN_CMD_TIME = 5 * SIGNAL_PERIOD
|
2018-11-25 05:41:25 +03:00
|
|
|
|
|
|
|
TEST_TIME = 5 * 60.
|
2019-01-29 20:09:58 +03:00
|
|
|
RETRY_RESET_TIME = 1.
|
2018-12-14 00:45:32 +03:00
|
|
|
ENDSTOP_REST_TIME = .001
|
2018-11-25 05:41:25 +03:00
|
|
|
ENDSTOP_SAMPLE_TIME = .000015
|
|
|
|
ENDSTOP_SAMPLE_COUNT = 4
|
|
|
|
|
2018-12-04 06:52:32 +03:00
|
|
|
Commands = {
|
2020-04-08 22:17:14 +03:00
|
|
|
'pin_down': 0.000650, 'touch_mode': 0.001165,
|
bltouch: Update command timing
Prior to the BLTouch v3, the recommended command times were 700us
(pin_down), 1200us (touch_mode), 1500us (pin_up), 1800us (self_test),
and 2200us (reset). However, the recommended Marlin timing (via servo
"angles") was 647.111, 1162.667, 1472, 1781.333, and 2193.778us.
As of the BLTouch v3, the recommended times are now 650, 1165, 1475,
1780, and 2190us. The v3 continues to recommended Marlin timings of
647.111, 1162.667, 1472, 1781.333, and 2193.778us.
Update Klipper to use the new BL-Touch v3 recommended timing. The new
timings are required for the BL-Touch v3 and they are closer to what
the Marlin firmware has historically used.
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
2019-05-21 01:50:56 +03:00
|
|
|
'pin_up': 0.001475, 'self_test': 0.001780, 'reset': 0.002190,
|
2020-02-24 06:32:07 +03:00
|
|
|
'set_5V_output_mode' : 0.001988, 'set_OD_output_mode' : 0.002091,
|
|
|
|
'output_mode_store' : 0.001884,
|
2018-12-04 06:52:32 +03:00
|
|
|
}
|
2018-11-25 05:41:25 +03:00
|
|
|
|
|
|
|
# BLTouch "endstop" wrapper
|
|
|
|
class BLTouchEndstopWrapper:
|
|
|
|
def __init__(self, config):
|
|
|
|
self.printer = config.get_printer()
|
2019-01-05 04:37:42 +03:00
|
|
|
self.printer.register_event_handler("klippy:connect",
|
|
|
|
self.handle_connect)
|
2018-11-25 05:41:25 +03:00
|
|
|
self.position_endstop = config.getfloat('z_offset')
|
2020-02-13 18:29:28 +03:00
|
|
|
self.stow_on_each_sample = config.getboolean('stow_on_each_sample',
|
|
|
|
True)
|
2020-04-11 18:41:45 +03:00
|
|
|
self.probe_touch_mode = config.getboolean('probe_with_touch_mode',
|
|
|
|
False)
|
2018-11-25 05:41:25 +03:00
|
|
|
# Create a pwm object to handle the control pin
|
|
|
|
ppins = self.printer.lookup_object('pins')
|
|
|
|
self.mcu_pwm = ppins.setup_pin('pwm', config.get('control_pin'))
|
|
|
|
self.mcu_pwm.setup_max_duration(0.)
|
|
|
|
self.mcu_pwm.setup_cycle_time(SIGNAL_PERIOD)
|
2021-03-29 22:25:23 +03:00
|
|
|
# Command timing
|
2020-04-08 22:17:14 +03:00
|
|
|
self.next_cmd_time = self.action_end_time = 0.
|
2021-03-29 22:25:23 +03:00
|
|
|
self.finish_home_complete = self.wait_trigger_complete = None
|
2018-11-25 05:41:25 +03:00
|
|
|
# Create an "endstop" object to handle the sensor pin
|
|
|
|
pin = config.get('sensor_pin')
|
|
|
|
pin_params = ppins.lookup_pin(pin, can_invert=True, can_pullup=True)
|
|
|
|
mcu = pin_params['chip']
|
|
|
|
mcu.register_config_callback(self._build_config)
|
|
|
|
self.mcu_endstop = mcu.setup_pin('endstop', pin_params)
|
2020-02-24 06:32:07 +03:00
|
|
|
# output mode
|
2021-03-29 22:49:20 +03:00
|
|
|
omodes = {'5V': '5V', 'OD': 'OD', None: None}
|
|
|
|
self.output_mode = config.getchoice('set_output_mode', omodes, None)
|
2018-12-14 00:58:34 +03:00
|
|
|
# Setup for sensor test
|
|
|
|
self.next_test_time = 0.
|
2019-01-05 04:20:02 +03:00
|
|
|
self.pin_up_not_triggered = config.getboolean(
|
|
|
|
'pin_up_reports_not_triggered', True)
|
2019-01-05 04:41:56 +03:00
|
|
|
self.pin_up_touch_triggered = config.getboolean(
|
|
|
|
'pin_up_touch_mode_reports_triggered', True)
|
2018-12-04 17:45:11 +03:00
|
|
|
# Calculate pin move time
|
2020-04-08 22:17:14 +03:00
|
|
|
self.pin_move_time = config.getfloat('pin_move_time', 0.680, above=0.)
|
2018-11-25 05:41:25 +03:00
|
|
|
# Wrappers
|
|
|
|
self.get_mcu = self.mcu_endstop.get_mcu
|
|
|
|
self.add_stepper = self.mcu_endstop.add_stepper
|
|
|
|
self.get_steppers = self.mcu_endstop.get_steppers
|
|
|
|
self.home_wait = self.mcu_endstop.home_wait
|
|
|
|
self.query_endstop = self.mcu_endstop.query_endstop
|
2018-12-04 06:52:32 +03:00
|
|
|
# Register BLTOUCH_DEBUG command
|
|
|
|
self.gcode = self.printer.lookup_object('gcode')
|
|
|
|
self.gcode.register_command("BLTOUCH_DEBUG", self.cmd_BLTOUCH_DEBUG,
|
|
|
|
desc=self.cmd_BLTOUCH_DEBUG_help)
|
2020-02-24 06:32:07 +03:00
|
|
|
self.gcode.register_command("BLTOUCH_STORE", self.cmd_BLTOUCH_STORE,
|
|
|
|
desc=self.cmd_BLTOUCH_STORE_help)
|
2020-02-13 18:29:28 +03:00
|
|
|
# multi probes state
|
|
|
|
self.multi = 'OFF'
|
2018-11-25 05:41:25 +03:00
|
|
|
def _build_config(self):
|
|
|
|
kin = self.printer.lookup_object('toolhead').get_kinematics()
|
2020-01-14 05:39:55 +03:00
|
|
|
for stepper in kin.get_steppers():
|
|
|
|
if stepper.is_active_axis('z'):
|
|
|
|
self.add_stepper(stepper)
|
2019-01-05 04:37:42 +03:00
|
|
|
def handle_connect(self):
|
2020-10-07 02:12:55 +03:00
|
|
|
self.sync_mcu_print_time()
|
|
|
|
self.next_cmd_time += 0.200
|
2020-02-24 06:32:07 +03:00
|
|
|
self.set_output_mode(self.output_mode)
|
2019-01-05 04:37:42 +03:00
|
|
|
try:
|
|
|
|
self.raise_probe()
|
2021-03-29 22:25:23 +03:00
|
|
|
self.verify_raise_probe()
|
2020-09-04 18:41:57 +03:00
|
|
|
except self.printer.command_error as e:
|
2019-01-29 19:57:08 +03:00
|
|
|
logging.warning("BLTouch raise probe error: %s", str(e))
|
2019-01-05 03:18:09 +03:00
|
|
|
def sync_mcu_print_time(self):
|
|
|
|
curtime = self.printer.get_reactor().monotonic()
|
|
|
|
est_time = self.mcu_pwm.get_mcu().estimated_print_time(curtime)
|
|
|
|
self.next_cmd_time = max(self.next_cmd_time, est_time + MIN_CMD_TIME)
|
2019-01-05 19:18:07 +03:00
|
|
|
def sync_print_time(self):
|
|
|
|
toolhead = self.printer.lookup_object('toolhead')
|
|
|
|
print_time = toolhead.get_last_move_time()
|
2020-04-05 01:51:41 +03:00
|
|
|
if self.next_cmd_time > print_time:
|
|
|
|
toolhead.dwell(self.next_cmd_time - print_time)
|
|
|
|
else:
|
2019-01-05 19:18:07 +03:00
|
|
|
self.next_cmd_time = print_time
|
|
|
|
def send_cmd(self, cmd, duration=MIN_CMD_TIME):
|
2019-01-07 20:05:37 +03:00
|
|
|
# Translate duration to ticks to avoid any secondary mcu clock skew
|
|
|
|
mcu = self.mcu_pwm.get_mcu()
|
|
|
|
cmd_clock = mcu.print_time_to_clock(self.next_cmd_time)
|
2020-04-08 22:17:14 +03:00
|
|
|
pulse = int((duration - MIN_CMD_TIME) / SIGNAL_PERIOD) * SIGNAL_PERIOD
|
|
|
|
cmd_clock += mcu.seconds_to_clock(max(MIN_CMD_TIME, pulse))
|
|
|
|
end_time = mcu.clock_to_print_time(cmd_clock)
|
|
|
|
# Schedule command followed by PWM disable
|
|
|
|
self.mcu_pwm.set_pwm(self.next_cmd_time, Commands[cmd] / SIGNAL_PERIOD)
|
|
|
|
self.mcu_pwm.set_pwm(end_time, 0.)
|
|
|
|
# Update time tracking
|
|
|
|
self.action_end_time = self.next_cmd_time + duration
|
|
|
|
self.next_cmd_time = max(self.action_end_time, end_time + MIN_CMD_TIME)
|
|
|
|
def verify_state(self, triggered):
|
2019-01-05 04:20:02 +03:00
|
|
|
# Perform endstop check to verify bltouch reports desired state
|
2020-04-08 22:17:14 +03:00
|
|
|
self.mcu_endstop.home_start(self.action_end_time, ENDSTOP_SAMPLE_TIME,
|
2019-01-05 04:20:02 +03:00
|
|
|
ENDSTOP_SAMPLE_COUNT, ENDSTOP_REST_TIME,
|
|
|
|
triggered=triggered)
|
2020-04-08 22:17:14 +03:00
|
|
|
return self.mcu_endstop.home_wait(self.action_end_time + 0.100)
|
2019-01-05 04:37:42 +03:00
|
|
|
def raise_probe(self):
|
2020-03-30 01:02:30 +03:00
|
|
|
self.sync_mcu_print_time()
|
|
|
|
if not self.pin_up_not_triggered:
|
|
|
|
self.send_cmd('reset')
|
2021-03-29 22:25:23 +03:00
|
|
|
self.send_cmd('pin_up', duration=self.pin_move_time)
|
|
|
|
def verify_raise_probe(self):
|
|
|
|
if not self.pin_up_not_triggered:
|
|
|
|
# No way to verify raise attempt
|
2020-03-30 01:02:30 +03:00
|
|
|
return
|
2019-01-29 20:09:58 +03:00
|
|
|
for retry in range(3):
|
2020-04-08 22:17:14 +03:00
|
|
|
success = self.verify_state(False)
|
2020-03-30 00:50:28 +03:00
|
|
|
if success:
|
|
|
|
# The "probe raised" test completed successfully
|
|
|
|
break
|
|
|
|
if retry >= 2:
|
2020-09-04 18:41:57 +03:00
|
|
|
raise self.printer.command_error(
|
|
|
|
"BLTouch failed to raise probe")
|
2020-03-30 00:50:28 +03:00
|
|
|
msg = "Failed to verify BLTouch probe is raised; retrying."
|
|
|
|
self.gcode.respond_info(msg)
|
2020-03-30 01:02:30 +03:00
|
|
|
self.sync_mcu_print_time()
|
2020-03-30 01:06:47 +03:00
|
|
|
self.send_cmd('reset', duration=RETRY_RESET_TIME)
|
2021-03-29 22:25:23 +03:00
|
|
|
self.send_cmd('pin_up', duration=self.pin_move_time)
|
2020-02-13 18:29:28 +03:00
|
|
|
def lower_probe(self):
|
|
|
|
self.test_sensor()
|
|
|
|
self.sync_print_time()
|
2020-04-08 22:17:14 +03:00
|
|
|
self.send_cmd('pin_down', duration=self.pin_move_time)
|
2020-04-11 18:41:45 +03:00
|
|
|
if self.probe_touch_mode:
|
|
|
|
self.send_cmd('touch_mode')
|
2018-11-25 05:41:25 +03:00
|
|
|
def test_sensor(self):
|
2019-01-05 04:41:56 +03:00
|
|
|
if not self.pin_up_touch_triggered:
|
|
|
|
# Nothing to test
|
2018-12-14 00:58:34 +03:00
|
|
|
return
|
2018-11-25 05:41:25 +03:00
|
|
|
toolhead = self.printer.lookup_object('toolhead')
|
|
|
|
print_time = toolhead.get_last_move_time()
|
|
|
|
if print_time < self.next_test_time:
|
|
|
|
self.next_test_time = print_time + TEST_TIME
|
|
|
|
return
|
2018-12-02 06:09:06 +03:00
|
|
|
# Raise the bltouch probe and test if probe is raised
|
2019-01-05 19:18:07 +03:00
|
|
|
self.sync_print_time()
|
2020-03-30 19:32:48 +03:00
|
|
|
for retry in range(3):
|
2020-04-08 22:17:14 +03:00
|
|
|
self.send_cmd('pin_up', duration=self.pin_move_time)
|
2020-03-30 19:32:48 +03:00
|
|
|
self.send_cmd('touch_mode')
|
2020-04-08 22:17:14 +03:00
|
|
|
success = self.verify_state(True)
|
2020-03-30 19:32:48 +03:00
|
|
|
self.sync_print_time()
|
|
|
|
if success:
|
|
|
|
# The "bltouch connection" test completed successfully
|
2020-04-08 22:17:14 +03:00
|
|
|
self.next_test_time = print_time + TEST_TIME
|
2020-03-30 19:32:48 +03:00
|
|
|
return
|
|
|
|
msg = "BLTouch failed to verify sensor state"
|
|
|
|
if retry >= 2:
|
2020-09-04 18:41:57 +03:00
|
|
|
raise self.printer.command_error(msg)
|
2020-03-30 19:32:48 +03:00
|
|
|
self.gcode.respond_info(msg + '; retrying.')
|
|
|
|
self.send_cmd('reset', duration=RETRY_RESET_TIME)
|
2020-02-12 21:37:55 +03:00
|
|
|
def multi_probe_begin(self):
|
2020-02-13 18:29:28 +03:00
|
|
|
if self.stow_on_each_sample:
|
|
|
|
return
|
|
|
|
self.multi = 'FIRST'
|
2020-02-12 21:37:55 +03:00
|
|
|
def multi_probe_end(self):
|
2020-02-13 18:29:28 +03:00
|
|
|
if self.stow_on_each_sample:
|
|
|
|
return
|
2020-10-24 07:22:58 +03:00
|
|
|
self.sync_print_time()
|
2020-02-13 18:29:28 +03:00
|
|
|
self.raise_probe()
|
2021-03-29 22:25:23 +03:00
|
|
|
self.verify_raise_probe()
|
2020-03-30 06:14:26 +03:00
|
|
|
self.sync_print_time()
|
2020-02-13 18:29:28 +03:00
|
|
|
self.multi = 'OFF'
|
2021-03-29 21:23:46 +03:00
|
|
|
def probe_prepare(self, hmove):
|
2020-02-13 18:29:28 +03:00
|
|
|
if self.multi == 'OFF' or self.multi == 'FIRST':
|
|
|
|
self.lower_probe()
|
|
|
|
if self.multi == 'FIRST':
|
|
|
|
self.multi = 'ON'
|
2019-01-05 19:18:07 +03:00
|
|
|
self.sync_print_time()
|
2021-03-29 22:25:23 +03:00
|
|
|
def home_start(self, print_time, sample_time, sample_count, rest_time,
|
|
|
|
triggered=True):
|
|
|
|
rest_time = min(rest_time, ENDSTOP_REST_TIME)
|
|
|
|
self.finish_home_complete = self.mcu_endstop.home_start(
|
|
|
|
print_time, sample_time, sample_count, rest_time, triggered)
|
|
|
|
# Schedule wait_for_trigger callback
|
|
|
|
r = self.printer.get_reactor()
|
|
|
|
self.wait_trigger_complete = r.register_callback(self.wait_for_trigger)
|
|
|
|
return self.finish_home_complete
|
|
|
|
def wait_for_trigger(self, eventtime):
|
|
|
|
self.finish_home_complete.wait()
|
2020-02-13 18:29:28 +03:00
|
|
|
if self.multi == 'OFF':
|
|
|
|
self.raise_probe()
|
2021-03-29 22:25:23 +03:00
|
|
|
def probe_finish(self, hmove):
|
|
|
|
self.wait_trigger_complete.wait()
|
|
|
|
if self.multi == 'OFF':
|
|
|
|
self.verify_raise_probe()
|
2019-01-05 19:18:07 +03:00
|
|
|
self.sync_print_time()
|
2021-03-29 21:29:33 +03:00
|
|
|
if hmove.check_no_movement() is not None:
|
|
|
|
raise self.printer.command_error("BLTouch failed to deploy")
|
2018-11-25 05:41:25 +03:00
|
|
|
def get_position_endstop(self):
|
|
|
|
return self.position_endstop
|
2020-02-24 06:32:07 +03:00
|
|
|
def set_output_mode(self, mode):
|
|
|
|
# If this is inadvertently/purposely issued for a
|
|
|
|
# BLTOUCH pre V3.0 and clones:
|
|
|
|
# No reaction at all.
|
|
|
|
# BLTOUCH V3.0 and V3.1:
|
|
|
|
# This will set the mode.
|
|
|
|
if mode is None:
|
|
|
|
return
|
|
|
|
logging.info("BLTouch set output mode: %s", mode)
|
|
|
|
self.sync_mcu_print_time()
|
|
|
|
if mode == '5V':
|
|
|
|
self.send_cmd('set_5V_output_mode')
|
|
|
|
if mode == 'OD':
|
|
|
|
self.send_cmd('set_OD_output_mode')
|
|
|
|
def store_output_mode(self, mode):
|
|
|
|
# If this command is inadvertently/purposely issued for a
|
|
|
|
# BLTOUCH pre V3.0 and clones:
|
|
|
|
# No reaction at all to this sequence apart from a pin-down/pin-up
|
|
|
|
# BLTOUCH V3.0:
|
|
|
|
# This will set the mode (twice) and sadly, a pin-up is needed at
|
|
|
|
# the end, because of the pin-down
|
|
|
|
# BLTOUCH V3.1:
|
|
|
|
# This will set the mode and store it in the eeprom.
|
|
|
|
# The pin-up is not needed but does not hurt
|
|
|
|
logging.info("BLTouch store output mode: %s", mode)
|
|
|
|
self.sync_print_time()
|
|
|
|
self.send_cmd('pin_down')
|
|
|
|
if mode == '5V':
|
|
|
|
self.send_cmd('set_5V_output_mode')
|
|
|
|
else:
|
|
|
|
self.send_cmd('set_OD_output_mode')
|
|
|
|
self.send_cmd('output_mode_store')
|
|
|
|
if mode == '5V':
|
|
|
|
self.send_cmd('set_5V_output_mode')
|
|
|
|
else:
|
|
|
|
self.send_cmd('set_OD_output_mode')
|
|
|
|
self.send_cmd('pin_up')
|
2018-12-04 06:52:32 +03:00
|
|
|
cmd_BLTOUCH_DEBUG_help = "Send a command to the bltouch for debugging"
|
2020-04-25 06:12:40 +03:00
|
|
|
def cmd_BLTOUCH_DEBUG(self, gcmd):
|
|
|
|
cmd = gcmd.get('COMMAND', None)
|
2018-12-04 06:52:32 +03:00
|
|
|
if cmd is None or cmd not in Commands:
|
2020-04-25 06:12:40 +03:00
|
|
|
gcmd.respond_info("BLTouch commands: %s" % (
|
2018-12-04 06:52:32 +03:00
|
|
|
", ".join(sorted([c for c in Commands if c is not None]))))
|
|
|
|
return
|
2020-04-25 06:12:40 +03:00
|
|
|
gcmd.respond_info("Sending BLTOUCH_DEBUG COMMAND=%s" % (cmd,))
|
2019-01-05 19:18:07 +03:00
|
|
|
self.sync_print_time()
|
|
|
|
self.send_cmd(cmd, duration=self.pin_move_time)
|
|
|
|
self.sync_print_time()
|
2020-02-24 06:32:07 +03:00
|
|
|
cmd_BLTOUCH_STORE_help = "Store an output mode in the BLTouch EEPROM"
|
2020-04-25 06:12:40 +03:00
|
|
|
def cmd_BLTOUCH_STORE(self, gcmd):
|
|
|
|
cmd = gcmd.get('MODE', None)
|
2020-02-24 06:32:07 +03:00
|
|
|
if cmd is None or cmd not in ['5V', 'OD']:
|
2020-04-25 06:12:40 +03:00
|
|
|
gcmd.respond_info("BLTouch output modes: 5V, OD")
|
2020-02-24 06:32:07 +03:00
|
|
|
return
|
2020-04-25 06:12:40 +03:00
|
|
|
gcmd.respond_info("Storing BLTouch output mode: %s" % (cmd,))
|
2020-02-24 06:32:07 +03:00
|
|
|
self.sync_print_time()
|
|
|
|
self.store_output_mode(cmd)
|
|
|
|
self.sync_print_time()
|
2018-11-25 05:41:25 +03:00
|
|
|
|
|
|
|
def load_config(config):
|
|
|
|
blt = BLTouchEndstopWrapper(config)
|
|
|
|
config.get_printer().add_object('probe', probe.PrinterProbe(config, blt))
|
|
|
|
return blt
|