| 
									
										
										
										
											2018-11-24 21:41:25 -05:00
										 |  |  | # BLTouch support | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Copyright (C) 2018  Kevin O'Connor <kevin@koconnor.net> | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # This file may be distributed under the terms of the GNU GPLv3 license. | 
					
						
							| 
									
										
										
										
											2018-12-04 09:45:11 -05:00
										 |  |  | import math, logging | 
					
						
							| 
									
										
										
										
											2018-11-24 21:41:25 -05:00
										 |  |  | import homing, probe | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | SIGNAL_PERIOD = 0.025600 | 
					
						
							|  |  |  | MIN_CMD_TIME = 4 * SIGNAL_PERIOD | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | TEST_TIME = 5 * 60. | 
					
						
							| 
									
										
										
										
											2018-12-13 16:45:32 -05:00
										 |  |  | ENDSTOP_REST_TIME = .001 | 
					
						
							| 
									
										
										
										
											2018-11-24 21:41:25 -05:00
										 |  |  | ENDSTOP_SAMPLE_TIME = .000015 | 
					
						
							|  |  |  | ENDSTOP_SAMPLE_COUNT = 4 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-03 22:52:32 -05:00
										 |  |  | Commands = { | 
					
						
							|  |  |  |     None: 0.0, 'pin_down': 0.000700, 'touch_mode': 0.001200, | 
					
						
							|  |  |  |     'pin_up': 0.001500, 'self_test': 0.001800, 'reset': 0.002200, | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-11-24 21:41:25 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | # BLTouch "endstop" wrapper | 
					
						
							|  |  |  | class BLTouchEndstopWrapper: | 
					
						
							|  |  |  |     def __init__(self, config): | 
					
						
							|  |  |  |         self.printer = config.get_printer() | 
					
						
							| 
									
										
										
										
											2019-01-04 20:37:42 -05:00
										 |  |  |         self.printer.register_event_handler("klippy:connect", | 
					
						
							|  |  |  |                                             self.handle_connect) | 
					
						
							| 
									
										
										
										
											2018-11-24 21:41:25 -05:00
										 |  |  |         self.position_endstop = config.getfloat('z_offset') | 
					
						
							|  |  |  |         # 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) | 
					
						
							| 
									
										
										
										
											2019-01-05 11:18:07 -05:00
										 |  |  |         self.next_cmd_time = 0. | 
					
						
							| 
									
										
										
										
											2018-11-24 21:41:25 -05: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) | 
					
						
							| 
									
										
										
										
											2018-12-13 16:58:34 -05:00
										 |  |  |         # Setup for sensor test | 
					
						
							|  |  |  |         self.next_test_time = 0. | 
					
						
							| 
									
										
										
										
											2019-01-04 20:20:02 -05:00
										 |  |  |         self.pin_up_not_triggered = config.getboolean( | 
					
						
							|  |  |  |             'pin_up_reports_not_triggered', True) | 
					
						
							| 
									
										
										
										
											2018-12-13 16:58:34 -05:00
										 |  |  |         self.test_sensor_pin = config.getboolean('test_sensor_pin', True) | 
					
						
							| 
									
										
										
										
											2019-01-04 19:12:50 -05:00
										 |  |  |         self.start_mcu_pos = [] | 
					
						
							| 
									
										
										
										
											2018-12-04 09:45:11 -05:00
										 |  |  |         # Calculate pin move time | 
					
						
							| 
									
										
										
										
											2018-12-04 11:43:04 -05:00
										 |  |  |         pmt = max(config.getfloat('pin_move_time', 0.200), MIN_CMD_TIME) | 
					
						
							| 
									
										
										
										
											2018-12-04 09:45:11 -05:00
										 |  |  |         self.pin_move_time = math.ceil(pmt / SIGNAL_PERIOD) * SIGNAL_PERIOD | 
					
						
							| 
									
										
										
										
											2018-11-24 21:41:25 -05: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 | 
					
						
							|  |  |  |         self.query_endstop_wait = self.mcu_endstop.query_endstop_wait | 
					
						
							|  |  |  |         self.TimeoutError = self.mcu_endstop.TimeoutError | 
					
						
							| 
									
										
										
										
											2018-12-03 22:52:32 -05: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) | 
					
						
							| 
									
										
										
										
											2018-11-24 21:41:25 -05:00
										 |  |  |     def _build_config(self): | 
					
						
							|  |  |  |         kin = self.printer.lookup_object('toolhead').get_kinematics() | 
					
						
							|  |  |  |         for stepper in kin.get_steppers('Z'): | 
					
						
							|  |  |  |             stepper.add_to_endstop(self) | 
					
						
							| 
									
										
										
										
											2019-01-04 20:37:42 -05:00
										 |  |  |     def handle_connect(self): | 
					
						
							|  |  |  |         self.sync_mcu_print_time() | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self.raise_probe() | 
					
						
							|  |  |  |         except homing.EndstopError as e: | 
					
						
							|  |  |  |             raise self.printer.config_error(str(e)) | 
					
						
							| 
									
										
										
										
											2019-01-04 19:18:09 -05: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 11:18:07 -05:00
										 |  |  |     def sync_print_time(self): | 
					
						
							|  |  |  |         toolhead = self.printer.lookup_object('toolhead') | 
					
						
							|  |  |  |         print_time = toolhead.get_last_move_time() | 
					
						
							|  |  |  |         if self.next_cmd_time > print_time: | 
					
						
							|  |  |  |             toolhead.dwell(self.next_cmd_time - print_time) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.next_cmd_time = print_time | 
					
						
							|  |  |  |     def send_cmd(self, cmd, duration=MIN_CMD_TIME): | 
					
						
							|  |  |  |         self.mcu_pwm.set_pwm(self.next_cmd_time, Commands[cmd] / SIGNAL_PERIOD) | 
					
						
							|  |  |  |         self.next_cmd_time += max(duration, MIN_CMD_TIME) | 
					
						
							|  |  |  |         return self.next_cmd_time | 
					
						
							| 
									
										
										
										
											2019-01-04 20:20:02 -05:00
										 |  |  |     def verify_state(self, check_start_time, check_end_time, triggered, msg): | 
					
						
							|  |  |  |         # Perform endstop check to verify bltouch reports desired state | 
					
						
							|  |  |  |         prev_positions = [s.get_commanded_position() | 
					
						
							|  |  |  |                           for s in self.mcu_endstop.get_steppers()] | 
					
						
							|  |  |  |         self.mcu_endstop.home_start(check_start_time, ENDSTOP_SAMPLE_TIME, | 
					
						
							|  |  |  |                                     ENDSTOP_SAMPLE_COUNT, ENDSTOP_REST_TIME, | 
					
						
							|  |  |  |                                     triggered=triggered) | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self.mcu_endstop.home_wait(check_end_time) | 
					
						
							|  |  |  |         except self.mcu_endstop.TimeoutError as e: | 
					
						
							|  |  |  |             raise homing.EndstopError("BLTouch failed to %s" % (msg,)) | 
					
						
							|  |  |  |         for s, pos in zip(self.mcu_endstop.get_steppers(), prev_positions): | 
					
						
							|  |  |  |             s.set_commanded_position(pos) | 
					
						
							| 
									
										
										
										
											2019-01-04 20:37:42 -05:00
										 |  |  |     def raise_probe(self): | 
					
						
							|  |  |  |         self.send_cmd('reset') | 
					
						
							|  |  |  |         check_start_time = self.send_cmd('pin_up', duration=self.pin_move_time) | 
					
						
							|  |  |  |         check_end_time = self.send_cmd(None) | 
					
						
							|  |  |  |         if self.pin_up_not_triggered: | 
					
						
							|  |  |  |             self.verify_state(check_start_time, check_end_time, | 
					
						
							|  |  |  |                               False, "raise probe") | 
					
						
							| 
									
										
										
										
											2018-11-24 21:41:25 -05:00
										 |  |  |     def test_sensor(self): | 
					
						
							| 
									
										
										
										
											2018-12-13 16:58:34 -05:00
										 |  |  |         if not self.test_sensor_pin: | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											2018-11-24 21:41:25 -05: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-01 22:09:06 -05:00
										 |  |  |         # Raise the bltouch probe and test if probe is raised | 
					
						
							| 
									
										
										
										
											2019-01-05 11:18:07 -05:00
										 |  |  |         self.sync_print_time() | 
					
						
							|  |  |  |         check_start_time = self.send_cmd('reset', duration=self.pin_move_time) | 
					
						
							|  |  |  |         check_end_time = self.send_cmd('touch_mode') | 
					
						
							|  |  |  |         self.send_cmd(None) | 
					
						
							| 
									
										
										
										
											2019-01-04 20:20:02 -05:00
										 |  |  |         self.verify_state(check_start_time, check_end_time, True, | 
					
						
							|  |  |  |                           "verify sensor state") | 
					
						
							| 
									
										
										
										
											2018-12-01 22:09:06 -05:00
										 |  |  |         # Test was successful | 
					
						
							| 
									
										
										
										
											2019-01-05 11:18:07 -05:00
										 |  |  |         self.next_test_time = check_end_time + TEST_TIME | 
					
						
							|  |  |  |         self.sync_print_time() | 
					
						
							| 
									
										
										
										
											2018-11-24 21:41:25 -05:00
										 |  |  |     def home_prepare(self): | 
					
						
							|  |  |  |         self.test_sensor() | 
					
						
							| 
									
										
										
										
											2019-01-05 11:18:07 -05:00
										 |  |  |         self.sync_print_time() | 
					
						
							| 
									
										
										
										
											2019-01-04 19:18:09 -05:00
										 |  |  |         self.send_cmd('pin_down', duration=self.pin_move_time - MIN_CMD_TIME) | 
					
						
							|  |  |  |         self.send_cmd(None) | 
					
						
							| 
									
										
										
										
											2019-01-05 11:18:07 -05:00
										 |  |  |         self.sync_print_time() | 
					
						
							| 
									
										
										
										
											2018-11-24 21:41:25 -05:00
										 |  |  |         self.mcu_endstop.home_prepare() | 
					
						
							| 
									
										
										
										
											2019-01-04 19:12:50 -05:00
										 |  |  |         self.start_mcu_pos = [(s, s.get_mcu_position()) | 
					
						
							|  |  |  |                               for s in self.mcu_endstop.get_steppers()] | 
					
						
							| 
									
										
										
										
											2018-11-24 21:41:25 -05:00
										 |  |  |     def home_finalize(self): | 
					
						
							| 
									
										
										
										
											2019-01-04 19:18:09 -05:00
										 |  |  |         self.sync_mcu_print_time() | 
					
						
							| 
									
										
										
										
											2019-01-04 20:37:42 -05:00
										 |  |  |         self.raise_probe() | 
					
						
							| 
									
										
										
										
											2019-01-05 11:18:07 -05:00
										 |  |  |         self.sync_print_time() | 
					
						
							| 
									
										
										
										
											2019-01-04 19:12:50 -05:00
										 |  |  |         # Verify the probe actually deployed during the attempt | 
					
						
							|  |  |  |         for s, mcu_pos in self.start_mcu_pos: | 
					
						
							|  |  |  |             if s.get_mcu_position() == mcu_pos: | 
					
						
							|  |  |  |                 raise homing.EndstopError("BLTouch failed to deploy") | 
					
						
							| 
									
										
										
										
											2018-11-24 21:41:25 -05:00
										 |  |  |         self.mcu_endstop.home_finalize() | 
					
						
							| 
									
										
										
										
											2018-12-13 16:45:32 -05:00
										 |  |  |     def home_start(self, print_time, sample_time, sample_count, rest_time): | 
					
						
							|  |  |  |         rest_time = min(rest_time, ENDSTOP_REST_TIME) | 
					
						
							|  |  |  |         self.mcu_endstop.home_start( | 
					
						
							|  |  |  |             print_time, sample_time, sample_count, rest_time) | 
					
						
							| 
									
										
										
										
											2018-11-24 21:41:25 -05:00
										 |  |  |     def get_position_endstop(self): | 
					
						
							|  |  |  |         return self.position_endstop | 
					
						
							| 
									
										
										
										
											2018-12-03 22:52:32 -05:00
										 |  |  |     cmd_BLTOUCH_DEBUG_help = "Send a command to the bltouch for debugging" | 
					
						
							|  |  |  |     def cmd_BLTOUCH_DEBUG(self, params): | 
					
						
							|  |  |  |         cmd = self.gcode.get_str('COMMAND', params, None) | 
					
						
							|  |  |  |         if cmd is None or cmd not in Commands: | 
					
						
							|  |  |  |             self.gcode.respond_info("BLTouch commands: %s" % ( | 
					
						
							|  |  |  |                 ", ".join(sorted([c for c in Commands if c is not None])))) | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         msg = "Sending BLTOUCH_DEBUG COMMAND=%s" % (cmd,) | 
					
						
							|  |  |  |         self.gcode.respond_info(msg) | 
					
						
							|  |  |  |         logging.info(msg) | 
					
						
							| 
									
										
										
										
											2019-01-05 11:18:07 -05:00
										 |  |  |         self.sync_print_time() | 
					
						
							|  |  |  |         self.send_cmd(cmd, duration=self.pin_move_time) | 
					
						
							|  |  |  |         self.send_cmd(None) | 
					
						
							|  |  |  |         self.sync_print_time() | 
					
						
							| 
									
										
										
										
											2018-11-24 21:41:25 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | def load_config(config): | 
					
						
							|  |  |  |     blt = BLTouchEndstopWrapper(config) | 
					
						
							|  |  |  |     config.get_printer().add_object('probe', probe.PrinterProbe(config, blt)) | 
					
						
							|  |  |  |     return blt |