From 16f25976e8a1c31b4c0868224e49ec101929e2ef Mon Sep 17 00:00:00 2001 From: liberodark Date: Thu, 30 Jan 2025 18:02:57 +0100 Subject: [PATCH] Add Qidi Plus 4 --- klippy/configfile.py | 59 ++++++++++- klippy/extras/chamber_fan.py | 52 ++++++++++ klippy/extras/gcode_macro_break.py | 15 +++ klippy/extras/gcode_move.py | 24 +++++ klippy/extras/gcode_shell_command.py | 87 ++++++++++++++++ klippy/extras/heaters.py | 16 ++- klippy/extras/homing.py | 4 +- klippy/extras/qdprobe.py | 142 +++++++++++++++++++++++++++ klippy/extras/save_variables.py | 23 +++++ klippy/extras/smart_effector.py | 4 +- klippy/extras/temperature_sensor.py | 2 + klippy/extras/tmc.py | 2 + klippy/mcu.py | 28 +++++- klippy/stepper.py | 84 ++++++++++++++++ klippy/toolhead.py | 17 ++++ 15 files changed, 553 insertions(+), 6 deletions(-) create mode 100644 klippy/extras/chamber_fan.py create mode 100644 klippy/extras/gcode_macro_break.py create mode 100644 klippy/extras/gcode_shell_command.py create mode 100644 klippy/extras/qdprobe.py diff --git a/klippy/configfile.py b/klippy/configfile.py index 8210de2ba696..1306d238dccc 100644 --- a/klippy/configfile.py +++ b/klippy/configfile.py @@ -251,6 +251,8 @@ def __init__(self, printer): gcode = self.printer.lookup_object('gcode') gcode.register_command("SAVE_CONFIG", self.cmd_SAVE_CONFIG, desc=self.cmd_SAVE_CONFIG_help) + gcode.register_command("SAVE_CONFIG_QD", self.cmd_SAVE_CONFIG_QD, + desc=self.cmd_SAVE_CONFIG_QD_help) def _find_autosave_data(self, data): regular_data = data autosave_data = "" @@ -397,16 +399,66 @@ def cmd_SAVE_CONFIG(self, gcmd): try: f = open(temp_name, 'w') f.write(data) + f.flush() f.close() os.rename(cfgname, backup_name) os.rename(temp_name, cfgname) + os.system("sync") except: msg = "Unable to write config file during SAVE_CONFIG" logging.exception(msg) raise gcmd.error(msg) # Request a restart gcode = self.printer.lookup_object('gcode') - gcode.request_restart('restart') + gcode.request_restart('firmware_restart') + + cmd_SAVE_CONFIG_QD_help = "Overwrite config file and restart" + def cmd_SAVE_CONFIG_QD(self, gcmd): + if not self.autosave.fileconfig.sections(): + return + gcode = self.printer.lookup_object('gcode') + # Create string containing autosave data + autosave_data = self._build_config_string(self.autosave) + lines = [('#*# ' + l).strip() + for l in autosave_data.split('\n')] + lines.insert(0, "\n" + AUTOSAVE_HEADER.rstrip()) + lines.append("") + autosave_data = '\n'.join(lines) + # Read in and validate current config file + cfgname = self.printer.get_start_args()['config_file'] + try: + data = self._read_config_file(cfgname) + regular_data, old_autosave_data = self._find_autosave_data(data) + config = self._build_config_wrapper(regular_data, cfgname) + except error as e: + msg = "Unable to parse existing config on SAVE_CONFIG" + logging.exception(msg) + raise gcode.error(msg) + regular_data = self._strip_duplicates(regular_data, self.autosave) + self._disallow_include_conflicts(regular_data, cfgname, gcode) + data = regular_data.rstrip() + autosave_data + # Determine filenames + datestr = time.strftime("-%Y%m%d_%H%M%S") + backup_name = cfgname + datestr + temp_name = cfgname + "_autosave" + if cfgname.endswith(".cfg"): + backup_name = cfgname[:-4] + datestr + ".cfg" + temp_name = cfgname[:-4] + "_autosave.cfg" + # Create new config file with temporary name and swap with main config + logging.info("SAVE_CONFIG to '%s' (backup in '%s')", + cfgname, backup_name) + try: + f = open(temp_name, 'w') + f.write(data) + f.flush() + f.close() + os.rename(cfgname, backup_name) + os.rename(temp_name, cfgname) + os.system("sync") + except: + msg = "Unable to write config file during SAVE_CONFIG" + logging.exception(msg) + raise gcode.error(msg) ###################################################################### @@ -518,6 +570,11 @@ def deprecate(self, section, option, value=None, msg=None): self.deprecate_warnings.append(res) self.status_warnings = self.runtime_warnings + self.deprecate_warnings # Status reporting + def runtime_warning(self, msg): + logging.warning(msg) + res = {'type': 'runtime_warning', 'message': msg} + self.runtime_warnings.append(res) + self.status_warnings = self.runtime_warnings + self.deprecate_warnings def _build_status_config(self, config): self.status_raw_config = {} for section in config.get_prefix_sections(''): diff --git a/klippy/extras/chamber_fan.py b/klippy/extras/chamber_fan.py new file mode 100644 index 000000000000..39370f8ce174 --- /dev/null +++ b/klippy/extras/chamber_fan.py @@ -0,0 +1,52 @@ +from . import fan + +PIN_MIN_TIME = 0.100 + +class ChamberFan: + def __init__(self, config): + self.printer = config.get_printer() + self.printer.register_event_handler("klippy:ready", self.handle_ready) + self.printer.register_event_handler("klippy:connect", + self.handle_connect) + self.printer.load_object(config, 'heaters') + self.heaters = [] + self.fan = fan.Fan(config) + self.fan_speed = config.getfloat('fan_speed', default=1., + minval=0., maxval=1.) + self.idle_speed = config.getfloat( + 'idle_speed', default=self.fan_speed, minval=0., maxval=1.) + self.idle_timeout = config.getint("idle_timeout", default=30, minval=0) + self.heater_names = config.getlist("heater", ()) + self.last_on = self.idle_timeout + self.last_speed = 0. + def handle_connect(self): + # Heater lookup + pheaters = self.printer.lookup_object('heaters') + self.heaters = [pheaters.lookup_heater(n) for n in self.heater_names] + def handle_ready(self): + reactor = self.printer.get_reactor() + reactor.register_timer(self.callback, reactor.monotonic()+PIN_MIN_TIME) + def get_status(self, eventtime): + return self.fan.get_status(eventtime) + def callback(self, eventtime): + speed = 0. + active = False + for heater in self.heaters: + _, target_temp = heater.get_temp(eventtime) + if target_temp: + active = True + if active: + self.last_on = 0 + speed = self.fan_speed + elif self.last_on < self.idle_timeout: + speed = self.idle_speed + self.last_on += 1 + if speed != self.last_speed: + self.last_speed = speed + curtime = self.printer.get_reactor().monotonic() + print_time = self.fan.get_mcu().estimated_print_time(curtime) + self.fan.set_speed(print_time + PIN_MIN_TIME, speed) + return eventtime + 1. + +def load_config_prefix(config): + return ChamberFan(config) diff --git a/klippy/extras/gcode_macro_break.py b/klippy/extras/gcode_macro_break.py new file mode 100644 index 000000000000..220629fdd9ae --- /dev/null +++ b/klippy/extras/gcode_macro_break.py @@ -0,0 +1,15 @@ +class GCodeMacroBreaker: + def __init__(self, config): + # Gcode macro interupt + self.printer = config.get_printer() + webhooks = self.printer.lookup_object('webhooks') + webhooks.register_endpoint("breakmacro", self._handle_breakmacro) + webhooks.register_endpoint("resumemacro", self._handle_resumemacro) + self.gcode = self.printer.lookup_object('gcode') + def _handle_breakmacro(self, web_request): + self.gcode.break_flag = True + def _handle_resumemacro(self, web_request): + self.gcode.break_flag = False + +def load_config(config): + return GCodeMacroBreaker(config) diff --git a/klippy/extras/gcode_move.py b/klippy/extras/gcode_move.py index ecdadc439584..a174be8244a2 100644 --- a/klippy/extras/gcode_move.py +++ b/klippy/extras/gcode_move.py @@ -49,6 +49,15 @@ def __init__(self, config): self.saved_states = {} self.move_transform = self.move_with_transform = None self.position_with_transform = (lambda: [0., 0., 0., 0.]) + # Save and load z offset + gcode.register_command( + 'SAVE_ZOFFSET_TO_VARIABLE', + self.cmd_SAVE_ZOFFSET_TO_VARIABLE + ) + gcode.register_command( + 'LOAD_ZOFFSET_FROM_VARIABLE', + self.cmd_LOAD_ZOFFSET_FROM_VARIABLE + ) def _handle_ready(self): self.is_printer_ready = True if self.move_transform is None: @@ -272,5 +281,20 @@ def cmd_GET_POSITION(self, gcmd): % (mcu_pos, stepper_pos, kin_pos, toolhead_pos, gcode_pos, base_pos, homing_pos)) + def cmd_SAVE_ZOFFSET_TO_VARIABLE(self, gcmd): + variables = self.printer.lookup_object("save_variables") + gcode_move = self.printer.lookup_object("gcode_move") + variables.save_variable( + 'Variables', 'z_offset', + gcode_move.homing_position[2] + ) + + def cmd_LOAD_ZOFFSET_FROM_VARIABLE(self, gcmd): + variables = self.printer.lookup_object("save_variables") + gcode_move = self.printer.lookup_object("gcode_move") + gcode_move.homing_position[2] = float( + variables.load_variable('Variables', 'z_offset') + ) + def load_config(config): return GCodeMove(config) diff --git a/klippy/extras/gcode_shell_command.py b/klippy/extras/gcode_shell_command.py new file mode 100644 index 000000000000..c3c62c5a2705 --- /dev/null +++ b/klippy/extras/gcode_shell_command.py @@ -0,0 +1,87 @@ +# Run a shell command via gcode +# +# Copyright (C) 2019 Eric Callahan +# +# This file may be distributed under the terms of the GNU GPLv3 license. +import os +import shlex +import subprocess +import logging + +class ShellCommand: + def __init__(self, config): + self.name = config.get_name().split()[-1] + self.printer = config.get_printer() + self.gcode = self.printer.lookup_object('gcode') + cmd = config.get('command') + cmd = os.path.expanduser(cmd) + self.command = shlex.split(cmd) + self.timeout = config.getfloat('timeout', 2., above=0.) + self.verbose = config.getboolean('verbose', True) + self.proc_fd = None + self.partial_output = "" + self.gcode.register_mux_command( + "RUN_SHELL_COMMAND", "CMD", self.name, + self.cmd_RUN_SHELL_COMMAND, + desc=self.cmd_RUN_SHELL_COMMAND_help) + + def _process_output(self, eventime): + if self.proc_fd is None: + return + try: + data = os.read(self.proc_fd, 4096) + except Exception: + pass + data = self.partial_output + data.decode() + if '\n' not in data: + self.partial_output = data + return + elif data[-1] != '\n': + split = data.rfind('\n') + 1 + self.partial_output = data[split:] + data = data[:split] + else: + self.partial_output = "" + self.gcode.respond_info(data) + + cmd_RUN_SHELL_COMMAND_help = "Run a linux shell command" + def cmd_RUN_SHELL_COMMAND(self, params): + gcode_params = params.get('PARAMS','') + gcode_params = shlex.split(gcode_params) + reactor = self.printer.get_reactor() + try: + proc = subprocess.Popen( +self.command + gcode_params, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + except Exception: + logging.exception( + "shell_command: Command {%s} failed" % (self.name)) + raise self.gcode.error("Error running command {%s}" % (self.name)) + if self.verbose: + self.proc_fd = proc.stdout.fileno() + self.gcode.respond_info("Running Command {%s}...:" % (self.name)) + hdl = reactor.register_fd(self.proc_fd, self._process_output) + eventtime = reactor.monotonic() + endtime = eventtime + self.timeout + complete = False + while eventtime < endtime: + eventtime = reactor.pause(eventtime + .05) + if proc.poll() is not None: + complete = True + break + if not complete: + proc.terminate() + if self.verbose: + if self.partial_output: + self.gcode.respond_info(self.partial_output) + self.partial_output = "" + if complete: + msg = "Command {%s} finished\n" % (self.name) + else: + msg = "Command {%s} timed out" % (self.name) + self.gcode.respond_info(msg) + reactor.unregister_fd(hdl) + self.proc_fd = None + + +def load_config_prefix(config): + return ShellCommand(config) diff --git a/klippy/extras/heaters.py b/klippy/extras/heaters.py index 1454354272d2..3fd5d69aa76e 100644 --- a/klippy/extras/heaters.py +++ b/klippy/extras/heaters.py @@ -254,6 +254,16 @@ def __init__(self, config): gcode.register_command("M105", self.cmd_M105, when_not_ready=True) gcode.register_command("TEMPERATURE_WAIT", self.cmd_TEMPERATURE_WAIT, desc=self.cmd_TEMPERATURE_WAIT_help) + # Wait heater interupt + webhooks = self.printer.lookup_object('webhooks') + webhooks.register_endpoint("breakheater", self._handle_breakheater) + self.break_flag=False + def _handle_breakheater(self,web_request): + reactor = self.printer.get_reactor() + for heater in self.heaters.values(): + eventtime = reactor.monotonic() + if heater.check_busy(eventtime): + self.break_flag = True def load_config(self, config): self.have_load_sensors = True # Load default temperature sensors @@ -345,7 +355,11 @@ def _wait_for_temperature(self, heater): gcode = self.printer.lookup_object("gcode") reactor = self.printer.get_reactor() eventtime = reactor.monotonic() + self.break_flag = False while not self.printer.is_shutdown() and heater.check_busy(eventtime): + if self.break_flag: + self.break_flag = False + break print_time = toolhead.get_last_move_time() gcode.respond_raw(self._get_temp(eventtime)) eventtime = reactor.pause(eventtime + 1.) @@ -374,7 +388,7 @@ def cmd_TEMPERATURE_WAIT(self, gcmd): toolhead = self.printer.lookup_object("toolhead") reactor = self.printer.get_reactor() eventtime = reactor.monotonic() - while not self.printer.is_shutdown(): + while not self.printer.is_shutdown() and not self.break_flag: temp, target = sensor.get_temp(eventtime) if temp >= min_temp and temp <= max_temp: return diff --git a/klippy/extras/homing.py b/klippy/extras/homing.py index add209ecf927..a2e48d8c1b31 100644 --- a/klippy/extras/homing.py +++ b/klippy/extras/homing.py @@ -264,8 +264,8 @@ def probing_move(self, mcu_probe, pos, speed): "Probing failed due to printer shutdown") raise if hmove.check_no_movement() is not None: - raise self.printer.command_error( - "Probe triggered prior to movement") + gcode = self.printer.lookup_object('gcode') + gcode.respond_info('Probe triggered prior to movement') return epos def cmd_G28(self, gcmd): # Move to origin diff --git a/klippy/extras/qdprobe.py b/klippy/extras/qdprobe.py new file mode 100644 index 000000000000..e6c89a279a95 --- /dev/null +++ b/klippy/extras/qdprobe.py @@ -0,0 +1,142 @@ +# -*- coding: utf-8 -*- + +import logging +from . import probe + +# Makerbase Endstop wrapper +class MakerbaseProbeEndstopWrapper: + def __init__(self, config): + self.printer = config.get_printer() + self.position_endstop = config.getfloat('z_offset') + self.stow_on_each_sample = config.getboolean( + 'deactivate_on_each_sample', True) + gcode_macro = self.printer.load_object(config, 'gcode_macro') + self.activate_gcode = gcode_macro.load_template( + config, 'activate_gcode', '') + self.deactivate_gcode = gcode_macro.load_template( + config, 'deactivate_gcode', '') + ppins = self.printer.lookup_object('pins') + pin = config.get('pin') + pin_params = ppins.lookup_pin(pin, can_invert=True, can_pullup=True) + mcu = pin_params['chip'] + self.mcu_endstop = mcu.setup_pin('endstop', pin_params) + self.printer.register_event_handler('klippy:mcu_identify', + self._handle_mcu_identify) + # 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_start = self.mcu_endstop.home_start + self.home_wait = self.mcu_endstop.home_wait + self.query_endstop = self.mcu_endstop.query_endstop + # multi probes state + self.multi = 'OFF' + def _handle_mcu_identify(self): + kin = self.printer.lookup_object('toolhead').get_kinematics() + for stepper in kin.get_steppers(): + if stepper.is_active_axis('z'): + self.add_stepper(stepper) + def raise_probe(self): + toolhead = self.printer.lookup_object('toolhead') + start_pos = toolhead.get_position() + self.deactivate_gcode.run_gcode_from_command() + if toolhead.get_position()[:3] != start_pos[:3]: + raise self.printer.command_error( + "Toolhead moved during probe activate_gcode script") + def lower_probe(self): + toolhead = self.printer.lookup_object('toolhead') + start_pos = toolhead.get_position() + self.activate_gcode.run_gcode_from_command() + if toolhead.get_position()[:3] != start_pos[:3]: + raise self.printer.command_error( + "Toolhead moved during probe deactivate_gcode script") + def multi_probe_begin(self): + if self.stow_on_each_sample: + return + self.multi = 'FIRST' + def multi_probe_end(self): + if self.stow_on_each_sample: + return + self.raise_probe() + self.multi = 'OFF' + def probe_prepare(self, hmove): + if self.multi == 'OFF' or self.multi == 'FIRST': + self.lower_probe() + if self.multi == 'FIRST': + self.multi = 'ON' + def probe_finish(self, hmove): + if self.multi == 'OFF': + self.raise_probe() + def get_position_endstop(self): + return self.position_endstop + +class MakerBase: + def __init__(self, config): + self.printer = config.get_printer() + self.gcode = self.printer.lookup_object('gcode') + # self.speed = config.getfloat('speed', 5.0, above=0.) + # self.lift_speed = config.getfloat('lift_speed', self.speed, above=0.) + # self.x_offset = config.getfloat('x_offset', 0.) + # self.y_offset = config.getfloat('y_offset', 0.) + # self.z_offset = config.getfloat('z_offset') + self.probe = self.printer.lookup_object('probe') + self.endstop_wrapper = probe.ProbeEndstopWrapper(config) + # self.probe_accel = config.getfloat('probe_accel', 0., minval=0.) + # self.recovery_time = config.getfloat('recovery_time', 0.4, minval=0.) + # Register MakerBase commands + self.gcode.register_command('MKS_PROBE_PIN_1', self.cmd_MKS_PROBE_PIN_1, + desc=self.cmd_MKS_PROBE_PIN_1_help) + self.gcode.register_command('MKS_PROBE_PIN_2', self.cmd_MKS_PROBE_PIN_2, + desc=self.cmd_MKS_PROBE_PIN_2_help) +self.gcode.register_command('QIDI_PROBE_PIN_1', self.cmd_MKS_PROBE_PIN_1, + desc=self.cmd_MKS_PROBE_PIN_1_help) +self.gcode.register_command('QIDI_PROBE_PIN_2', self.cmd_MKS_PROBE_PIN_2, + desc=self.cmd_MKS_PROBE_PIN_2_help) + self.gcode.register_command('MKS_REMOVE', self.cmd_MKS_REMOVE, + desc=self.cmd_MKS_REMOVE_help) + cmd_MKS_PROBE_PIN_2_help = 'ENABLE_PROBE_PIN_2' + def cmd_MKS_PROBE_PIN_2(self, gcmd): + self.probe.mcu_probe.probe_wrapper = self.endstop_wrapper + # Wrappers + self.probe.mcu_probe.get_mcu = self.endstop_wrapper.get_mcu + self.probe.mcu_probe.add_stepper = self.endstop_wrapper.add_stepper + self.probe.mcu_probe.get_steppers = self.endstop_wrapper.get_steppers + self.probe.mcu_probe.home_start = self.endstop_wrapper.home_start + self.probe.mcu_probe.home_wait = self.endstop_wrapper.home_wait + self.probe.mcu_probe.query_endstop = self.endstop_wrapper.query_endstop +self.probe.mcu_probe.multi_probe_begin = self.endstop_wrapper.multi_probe_begin +self.probe.mcu_probe.multi_probe_end = self.endstop_wrapper.multi_probe_end + # gcmd.respond_raw("%s" % (self.cmd_MKS_PROBE_PIN_2_help, )) + cmd_MKS_REMOVE_help = 'MKS_REMOVE' + def cmd_MKS_REMOVE(self, gcmd): + self.printer.remove_object('probe') + self.printer.lookup_object('gcode').remove_command('PROBE') + self.printer.lookup_object('gcode').remove_command('QUERY_PROBE') + self.printer.lookup_object('gcode').remove_command('PROBE_CALIBRATE') + self.printer.lookup_object('gcode').remove_command('PROBE_ACCURACY') + self.printer.lookup_object( + 'gcode').remove_command('Z_OFFSET_APPLY_PROBE' + ) + self.printer.lookup_object('gcode').remove_command('MKS_SHOW_Z_OFFSET') + self.printer.lookup_object('pins').remove_chip('probe') + cmd_MKS_PROBE_PIN_1_help = 'ENABLE_PROBE_PIN_1' + def cmd_MKS_PROBE_PIN_1(self, gcmd): +self.probe.mcu_probe.probe_wrapper = self.probe.mcu_probe.probe_wrapper_2 + # Wrappers +self.probe.mcu_probe.get_mcu = self.probe.mcu_probe.probe_wrapper_2.get_mcu +self.probe.mcu_probe.add_stepper = + self.probe.mcu_probe.probe_wrapper_2.add_stepper +self.probe.mcu_probe.get_steppers = + self.probe.mcu_probe.probe_wrapper_2.get_steppers +self.probe.mcu_probe.home_start = self.probe.mcu_probe.probe_wrapper_2.home_start +self.probe.mcu_probe.home_wait = self.probe.mcu_probe.probe_wrapper_2.home_wait +self.probe.mcu_probe.query_endstop = + self.probe.mcu_probe.probe_wrapper_2.query_endstop +self.probe.mcu_probe.multi_probe_begin = + self.probe.mcu_probe.probe_wrapper_2.multi_probe_begin +self.probe.mcu_probe.multi_probe_end = + self.probe.mcu_probe.probe_wrapper_2.multi_probe_end + # gcmd.respond_raw("%s" % (self.cmd_MKS_PROBE_PIN_1_help, )) + +def load_config(config): + return MakerBase(config) diff --git a/klippy/extras/save_variables.py b/klippy/extras/save_variables.py index 6cedcf4664ce..5d6b61cc7596 100644 --- a/klippy/extras/save_variables.py +++ b/klippy/extras/save_variables.py @@ -20,6 +20,29 @@ def __init__(self, config): gcode = self.printer.lookup_object('gcode') gcode.register_command('SAVE_VARIABLE', self.cmd_SAVE_VARIABLE, desc=self.cmd_SAVE_VARIABLE_help) + def load_variable(self, section, option): + varfile = configparser.ConfigParser() + try: + varfile.read(self.filename) + return varfile.get(section, option) + except: + msg = "Unable to parse existing variable file" + logging.exception(msg) + raise self.printer.command_error(msg) + def save_variable(self, section, option, value): + varfile = configparser.ConfigParser() + try: + varfile.read(self.filename) + if not varfile.has_section(section): + varfile.add_section(section) + varfile.set(section, option, value) + with open(self.filename, 'w') as configfile: + varfile.write(configfile) + except Exception as e: + msg = "Unable to save variable" + logging.exception(msg) + raise self.printer.command_error(msg) + self.loadVariables() def loadVariables(self): allvars = {} varfile = configparser.ConfigParser() diff --git a/klippy/extras/smart_effector.py b/klippy/extras/smart_effector.py index 6e5867893183..fe0a677bd472 100644 --- a/klippy/extras/smart_effector.py +++ b/klippy/extras/smart_effector.py @@ -54,7 +54,9 @@ def __init__(self, config): self.gcode = self.printer.lookup_object('gcode') self.probe_accel = config.getfloat('probe_accel', 0., minval=0.) self.recovery_time = config.getfloat('recovery_time', 0.4, minval=0.) - self.probe_wrapper = probe.ProbeEndstopWrapper(config) + self.probe_wrapper = self.probe_wrapper_2 = probe.ProbeEndstopWrapper( + config + ) # Wrappers self.get_mcu = self.probe_wrapper.get_mcu self.add_stepper = self.probe_wrapper.add_stepper diff --git a/klippy/extras/temperature_sensor.py b/klippy/extras/temperature_sensor.py index 19733731e526..943fec18fd0f 100644 --- a/klippy/extras/temperature_sensor.py +++ b/klippy/extras/temperature_sensor.py @@ -20,10 +20,12 @@ def __init__(self, config): self.sensor.setup_callback(self.temperature_callback) pheaters.register_sensor(config, self) self.last_temp = 0. + self.last_temp_time=0. self.measured_min = 99999999. self.measured_max = 0. def temperature_callback(self, read_time, temp): self.last_temp = temp + self.last_temp_time=read_time if temp: self.measured_min = min(self.measured_min, temp) self.measured_max = max(self.measured_max, temp) diff --git a/klippy/extras/tmc.py b/klippy/extras/tmc.py index 1d8599e2ed2d..0b2a33b270d3 100644 --- a/klippy/extras/tmc.py +++ b/klippy/extras/tmc.py @@ -162,6 +162,8 @@ def _query_register(self, reg_info, try_clear=False): fmt = self.fields.pretty_format(reg_name, val) raise self.printer.command_error("TMC '%s' reports error: %s" % (self.stepper_name, fmt)) + if "uv_cp" in fmt: + try_clear = True if try_clear and val & err_mask: try_clear = False cleared_flags |= val & err_mask diff --git a/klippy/mcu.py b/klippy/mcu.py index eb71e6bc2a97..8c905741f7f9 100644 --- a/klippy/mcu.py +++ b/klippy/mcu.py @@ -223,7 +223,7 @@ def stop(self): s.note_homing_end() return params['trigger_reason'] -TRSYNC_TIMEOUT = 0.025 +TRSYNC_TIMEOUT = 0.10 TRSYNC_SINGLE_MCU_TIMEOUT = 0.250 class TriggerDispatch: @@ -614,6 +614,27 @@ def __init__(self, config, clocksync): printer.register_event_handler("klippy:shutdown", self._shutdown) printer.register_event_handler("klippy:disconnect", self._disconnect) printer.register_event_handler("klippy:ready", self._ready) + + def _check_temperature(self,msg,name): + heater = self._printer.lookup_object(name) + if heater is not None: + if hasattr(heater,"heater") == True: + heater = heater.heater + last_temp = heater.last_temp + last_time = heater.last_temp_time + if last_time!=0.0 and ( + last_temp < heater.min_temp or last_temp > heater.max_temp + ): + msg += ", {} temperature is {}, has out of range ( + {}, + {})".format(name, + last_temp, + heater.min_temp, + heater.max_temp + ) + else: + msg += ", not find {}".format(name) + return msg # Serial callbacks def _handle_mcu_stats(self, params): count = params['count'] @@ -636,6 +657,11 @@ def _handle_shutdown(self, params): self._printer.invoke_async_shutdown( "MCU shutdown", {"reason": msg, "mcu": self._name, "event_type": event_type}) + if "ADC out of range" in msg: + heaters=self._printer.lookup_object("heaters") + for senser_name in heaters.available_sensors: + msg=self._check_temperature(msg,senser_name) + self._shutdown_msg=msg logging.info("MCU '%s' %s: %s\n%s\n%s", self._name, event_type, self._shutdown_msg, self._clocksync.dump_debug(), self._serial.dump_debug()) diff --git a/klippy/stepper.py b/klippy/stepper.py index fd44effb6ffa..dad4e94a8975 100644 --- a/klippy/stepper.py +++ b/klippy/stepper.py @@ -5,6 +5,7 @@ # This file may be distributed under the terms of the GNU GPLv3 license. import math, logging, collections import chelper +from extras.homing import Homing class error(Exception): pass @@ -304,6 +305,9 @@ def __init__(self, config, need_position_minmax=True, self.steppers = [] self.endstops = [] self.endstop_map = {} + # Reverse homing + self.endstops_reverse = [] + self.endstop_map_reverse = {} self.add_extra_stepper(config) mcu_stepper = self.steppers[0] self.get_name = mcu_stepper.get_name @@ -359,6 +363,35 @@ def __init__(self, config, need_position_minmax=True, raise config.error( "Invalid homing_positive_dir / position_endstop in '%s'" % (config.get_name(),)) + # Reverse homing + if self.get_name() == "stepper_z": + if config.get('endstop_pin_reverse', None): + self.reversed = False + self.position_endstop_reverse = config.getfloat( + "position_endstop_reverse" + ) + self.homing_positive_dir_reverse = config.getboolean( + 'homing_positive_dir_reverse' + ) + self.homing_speed_reverse = config.getfloat( + 'homing_speed_reverse', + self.homing_speed, + above=0. + ) + self.homing_retract_dist_reverse = 0 + self.printer = config.get_printer() + self.gcode = self.printer.lookup_object('gcode') + self.printer.register_event_handler( + "klippy:ready", + self.handle_ready + ) + self.printer.register_event_handler( + "homing:home_rails_end", + self.handle_reverse_home_rails_end + ) + def handle_ready(self): + gcode = self.printer.lookup_object('gcode') + gcode.register_command("REVERSE_HOMING", self.cmd_REVERSE_HOMING) def get_range(self): return self.position_min, self.position_max def get_homing_info(self): @@ -376,6 +409,28 @@ def get_endstops(self): def add_extra_stepper(self, config): stepper = PrinterStepper(config, self.stepper_units_in_radians) self.steppers.append(stepper) + # logging.info("%s begin to add stepper" % (stepper._name)) + if stepper._name == "stepper_z" or stepper._name == "stepper_z1": + endstop_pin_reverse = config.get('endstop_pin_reverse', None) + # logging.info("%s begin to add mcu endstop" % (stepper._name)) + # logging.info(endstop_pin_reverse) + if endstop_pin_reverse: + printer = config.get_printer() + ppins = printer.lookup_object('pins') + pin_params = ppins.parse_pin(endstop_pin_reverse, True, True) + pin_name = "%s:%s" % ( + pin_params['chip_name'], pin_params['pin'] + ) + mcu_endstop_reverse = ppins.setup_pin( + 'endstop', + endstop_pin_reverse + ) +self.endstop_map_reverse[pin_name] = {'endstop': mcu_endstop_reverse, + 'invert': pin_params['invert'], + 'pullup': pin_params['pullup']} + name = stepper.get_name(short=True) + self.endstops_reverse.append((mcu_endstop_reverse, name)) + mcu_endstop_reverse.add_stepper(stepper) if self.endstops and config.get('endstop_pin', None) is None: # No endstop defined - use primary endstop self.endstops[0][0].add_stepper(stepper) @@ -407,6 +462,35 @@ def add_extra_stepper(self, config): "must specify the same pullup/invert settings" % ( self.get_name(), pin_name)) mcu_endstop.add_stepper(stepper) + def homing_params_switch(self): + self.reversed = not self.reversed +self.endstop_map, self.endstop_map_reverse = self.endstop_map_reverse, + self.endstop_map +self.endstops, self.endstops_reverse = self.endstops_reverse, self.endstops +self.position_endstop, self.position_endstop_reverse = + self.position_endstop_reverse, self.position_endstop +self.homing_positive_dir, self.homing_positive_dir_reverse = + self.homing_positive_dir_reverse, self.homing_positive_dir +self.homing_retract_dist, self.homing_retract_dist_reverse = + self.homing_retract_dist_reverse, self.homing_retract_dist +self.homing_speed, self.homing_speed_reverse = self.homing_speed_reverse, + self.homing_speed + def cmd_REVERSE_HOMING(self, gcmd): + self.homing_params_switch() + try: + homing_state = Homing(self.printer) + homing_state.set_axes([2]) + kin = self.printer.lookup_object('toolhead').get_kinematics() + kin.home(homing_state) + except self.printer.command_error: + if self.printer.is_shutdown(): + raise self.printer.command_error( + "Homing failed due to printer shutdown") + self.printer.lookup_object('stepper_enable').motor_off() + raise + def handle_reverse_home_rails_end(self, homing_state, rails): + if self.reversed: + self.homing_params_switch() def setup_itersolve(self, alloc_func, *params): for stepper in self.steppers: stepper.setup_itersolve(alloc_func, *params) diff --git a/klippy/toolhead.py b/klippy/toolhead.py index f1a9afda1042..9c11e072b85f 100644 --- a/klippy/toolhead.py +++ b/klippy/toolhead.py @@ -285,6 +285,9 @@ def __init__(self, config): gcode.register_command('SET_VELOCITY_LIMIT', self.cmd_SET_VELOCITY_LIMIT, desc=self.cmd_SET_VELOCITY_LIMIT_help) + gcode.register_command('SET_Z_VELOCITY_LIMIT', + self.cmd_SET_Z_VELOCITY_LIMIT, + desc=self.cmd_SET_Z_VELOCITY_LIMIT_help) gcode.register_command('M204', self.cmd_M204) self.printer.register_event_handler("klippy:shutdown", self._handle_shutdown) @@ -293,6 +296,20 @@ def __init__(self, config): "manual_probe", "tuning_tower"] for module_name in modules: self.printer.load_object(config, module_name) + + cmd_SET_Z_VELOCITY_LIMIT_help = "Change z velocity limit, parameter VALUE" + def cmd_SET_Z_VELOCITY_LIMIT(self, gcmd): + kin = self.get_kinematics() + value = gcmd.get_float('VALUE', None, above=0.) + gcmd.respond_info( + 'Z velocity before change: ' + str(kin.max_z_velocity) + ) + if value: + kin.max_z_velocity = value + else: + gcmd.respond_info("Value given is illegal") + gcmd.respond_info('Z velocity after change: ' + str(kin.max_z_velocity)) + # Print time and flush tracking def _advance_flush_time(self, flush_time): flush_time = max(flush_time, self.last_flush_time)