From 01fabc89e944a3c7931e9371a69dddd76fa1e32d Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 18 Aug 2024 20:50:28 +0200 Subject: [PATCH 001/158] . --- klippy/extras/controller_fan.py | 6 ++---- klippy/extras/heater_fan.py | 6 +++--- klippy/extras/mpc_calibrate.py | 6 ++---- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/klippy/extras/controller_fan.py b/klippy/extras/controller_fan.py index a8281923f..eeb25678c 100644 --- a/klippy/extras/controller_fan.py +++ b/klippy/extras/controller_fan.py @@ -9,8 +9,6 @@ from . import fan -PIN_MIN_TIME = 0.100 - class ControllerFan: def __init__(self, config, defined_fan=None): @@ -102,7 +100,7 @@ def callback(self): speed = self.get_speed(print_time) if self.enabled and speed != self.last_speed: self.last_speed = speed - self.fan.set_speed(print_time + PIN_MIN_TIME, speed) + self.fan.set_speed(print_time, speed) return 1.0 cmd_SET_CONTROLLER_FAN_help = "Enable or Disable a controller_fan" @@ -112,7 +110,7 @@ def cmd_SET_CONTROLLER_FAN(self, gcmd): if not self.enabled: 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, 0.0) + self.fan.set_speed(print_time, 0.0) def load_config_prefix(config): diff --git a/klippy/extras/heater_fan.py b/klippy/extras/heater_fan.py index 5cd7ef1e9..61c975372 100644 --- a/klippy/extras/heater_fan.py +++ b/klippy/extras/heater_fan.py @@ -62,7 +62,7 @@ def callback(self, eventtime): 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) + self.fan.set_speed(print_time, speed) return eventtime + 1.0 cmd_SET_HEATER_FAN_help = "Enable or Disable a heater_fan" @@ -73,11 +73,11 @@ def cmd_SET_HEATER_FAN(self, gcmd): if self.enabled: 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, self.fan_speed) + self.fan.set_speed(print_time, self.fan_speed) if not self.enabled: 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, 0.0) + self.fan.set_speed(print_time, 0.0) def load_config_prefix(config): diff --git a/klippy/extras/mpc_calibrate.py b/klippy/extras/mpc_calibrate.py index 7bc337c12..bb37bd2f7 100644 --- a/klippy/extras/mpc_calibrate.py +++ b/klippy/extras/mpc_calibrate.py @@ -1,8 +1,6 @@ import math import logging -PIN_MIN_TIME = 0.100 - class MPCCalibrate: def __init__(self, config): @@ -284,7 +282,7 @@ def transfer_test( speed = idx / (fan_breakpoints - 1) curtime = self.heater.reactor.monotonic() print_time = fan.get_mcu().estimated_print_time(curtime) - fan.set_speed(print_time + PIN_MIN_TIME, speed) + fan.set_speed(print_time, speed) gcmd.respond_info("Waiting for temperature to stabilize") self.wait_stable(3) gcmd.respond_info( @@ -299,7 +297,7 @@ def transfer_test( fan_powers.append((speed, power)) curtime = self.heater.reactor.monotonic() print_time = fan.get_mcu().estimated_print_time(curtime) - fan.set_speed(print_time + PIN_MIN_TIME, 0.0) + fan.set_speed(print_time, 0.0) power_base = fan_powers[0][1] return { From a5f04c625bf2bf5a0a9b9657ced0ea01a21d4174 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 18 Aug 2024 21:00:18 +0200 Subject: [PATCH 002/158] Update fan.py --- klippy/extras/fan.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 6a89d643e..bac212be8 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -4,12 +4,10 @@ # # This file may be distributed under the terms of the GNU GPLv3 license. import logging -import threading -import time from . import pulse_counter -FAN_MIN_TIME = 0.100 +FAN_MIN_TIME = 0.250 SAFETY_CHECK_INIT_TIME = 3.0 @@ -292,7 +290,7 @@ def fan_check(self): self.gcode.run_script_from_command(self.on_error_gcode) else: self.printer.invoke_shutdown(msg) - return self.printer.get_reactor().NEVER + return 0 else: self.num_err = 0 if self.last_fan_value: From 3431f764fa44fe83c6affe84d437ee970c1da338 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Mon, 19 Aug 2024 13:31:46 +0200 Subject: [PATCH 003/158] . --- klippy/extras/fan.py | 189 ++++++++++++++++++++++++++++++------------- 1 file changed, 134 insertions(+), 55 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index f1c568e99..790b0be02 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -4,10 +4,11 @@ # # This file may be distributed under the terms of the GNU GPLv3 license. import logging +import threading from . import pulse_counter -FAN_MIN_TIME = 0.250 +FAN_MIN_TIME = 0.100 SAFETY_CHECK_INIT_TIME = 3.0 @@ -133,6 +134,12 @@ def __init__(self, config, default_shutdown_speed=0.0): self.self_checking = False self.startup_check_timer = None + real_time = config.getboolean("real_time", False) + if real_time: + self.set_speed = RealTimeFan(self).set_speed + else: + self.set_speed = StandardFan(self).set_speed + self.printer.register_event_handler("klippy:ready", self.handle_ready) # Register callbacks self.printer.register_event_handler( @@ -157,10 +164,9 @@ def handle_ready(self): def set_startup_fan_speed(self, print_time): self.mcu_fan.set_pwm(print_time, self.max_power) - reactor = self.printer.get_reactor() - self.startup_check_timer = reactor.register_timer( + self.startup_check_timer = self.reactor.register_timer( self.startup_self_check, - reactor.monotonic() + self.startup_check_delay, + self.reactor.monotonic() + self.startup_check_delay, ) def startup_self_check(self, eventtime): @@ -177,61 +183,17 @@ def startup_self_check(self, eventtime): (lambda pt: self.set_speed(pt, self.pwm_value, force=True)) ) self.self_checking = False - self.printer.get_reactor().unregister_timer(self.startup_check_timer) + self.reactor.unregister_timer(self.startup_check_timer) self.startup_check_timer = None - return self.printer.get_reactor().NEVER + return self.reactor.NEVER def get_mcu(self): return self.mcu_fan.get_mcu() - def set_speed(self, print_time, value, force=False, end_print=False): - if value > 0: - # Scale value between min_power and max_power - pwm_value = value * (self.max_power - self.min_power) + self.min_power - pwm_value = max(self.min_power, min(self.max_power, pwm_value)) - else: - pwm_value = 0 - if pwm_value == self.pwm_value and not force: - return - print_time = max(self.last_fan_time + FAN_MIN_TIME, print_time) - if force or not self.self_checking: - if self.enable_pin: - if value > 0 and self.last_fan_value == 0: - self.enable_pin.set_digital(print_time, 1) - elif value == 0 and self.last_fan_value > 0: - self.enable_pin.set_digital(print_time, 0) - if ( - value - and value < self.max_power - and self.kick_start_time - and ( - not self.last_fan_value - or value - self.last_fan_value > self.kick_start_threshold - ) - ): - # Run fan at full speed for specified kick_start_time - self.mcu_fan.set_pwm(print_time, self.max_power) - print_time += self.kick_start_time - self.mcu_fan.set_pwm(print_time, pwm_value) - self.last_fan_time = print_time - self.last_fan_value = value - self.pwm_value = pwm_value - - if self.min_rpm > 0 and (force or not self.self_checking): - if pwm_value > 0: - if self.fan_check_thread is None: - self.fan_check_thread = self.klipper_threads.register_job( - target=self.fan_check - ) - self.fan_check_thread.start() - else: - if self.fan_check_thread is not None: - self.fan_check_thread = None - - def set_speed_from_command(self, value, force=False, end_print=False): + def set_speed_from_command(self, value, force=False): toolhead = self.printer.lookup_object("toolhead") toolhead.register_lookahead_callback( - (lambda pt: self.set_speed(pt, value, force, end_print)) + (lambda pt: self.set_speed(pt, value, force)) ) def _handle_request_restart(self, print_time): @@ -282,11 +244,129 @@ def cmd_SET_FAN(self, gcmd): "MAX_POWER", self.max_power, above=self.min_power, maxval=1.0 ) self.min_rpm = gcmd.get_float("MIN_RPM", self.min_rpm, minval=0.0) - curtime = self.printer.get_reactor().monotonic() + curtime = self.reactor.monotonic() print_time = self.get_mcu().estimated_print_time(curtime) self.set_speed(print_time, self.last_fan_value, force=True) +class StandardFan: + def __init__(self, fan): + self.fan = fan + + def set_speed(self, print_time, value, force=False): + if value > 0: + # Scale value between min_power and max_power + pwm_value = value * (self.fan.max_power - self.fan.min_power) + self.fan.min_power + pwm_value = max(self.fan.min_power, min(self.fan.max_power, pwm_value)) + else: + pwm_value = 0 + if pwm_value == self.fan.pwm_value and not force: + return + print_time = max(self.fan.last_fan_time + FAN_MIN_TIME, print_time) + if force or not self.fan.self_checking: + if self.fan.enable_pin: + if value > 0 and self.fan.last_fan_value == 0: + self.fan.enable_pin.set_digital(print_time, 1) + elif value == 0 and self.fan.last_fan_value > 0: + self.fan.enable_pin.set_digital(print_time, 0) + if ( + value + and value < self.fan.max_power + and self.fan.kick_start_time + and ( + not self.fan.last_fan_value + or value - self.fan.last_fan_value > self.fan.kick_start_threshold + ) + ): + # Run fan at full speed for specified kick_start_time + self.fan.mcu_fan.set_pwm(print_time, self.fan.max_power) + print_time += self.fan.kick_start_time + self.fan.mcu_fan.set_pwm(print_time, pwm_value) + self.fan.last_fan_time = print_time + self.fan.last_fan_value = value + self.fan.pwm_value = pwm_value + + if self.fan.min_rpm > 0 and (force or not self.fan.self_checking): + if pwm_value > 0: + if self.fan.fan_check_thread is None: + self.fan.fan_check_thread = self.fan.klipper_threads.register_job( + target=self.fan.fan_check + ) + self.fan.fan_check_thread.start() + else: + if self.fan.fan_check_thread is not None: + self.fan.fan_check_thread = None + + +class RealTimeFan: + def __init__(self, fan): + self.fan = fan + self.lock = threading.Lock() + self.target = self.fan.last_fan_value + self.force = False + self.fan.printer.register_event_handler("klippy:ready", self._handle_ready) + + def _handle_ready(self): + self.fan.reactor.register_timer( + self.callback, self.fan.reactor.NOW + ) + + def set_speed(self, print_time, value, force=False): + self.target = value + self.force = force + + def callback(self, eventtime): + self._callback(eventtime) + return FAN_MIN_TIME + + def _callback(self, eventtime): + with self.lock: + if self.target > 0: + # Scale value between min_power and max_power + pwm_value = self.target * (self.fan.max_power - self.fan.min_power) + self.fan.min_power + pwm_value = max(self.fan.min_power, min(self.fan.max_power, pwm_value)) + else: + pwm_value = 0 + if pwm_value == self.fan.pwm_value and not self.force: + return + if eventtime < self.fan.last_fan_time: + return + if self.force or not self.fan.self_checking: + if self.fan.enable_pin: + if self.target > 0 and self.fan.last_fan_value == 0: + self.fan.enable_pin.set_digital(eventtime, 1) + elif self.target == 0 and self.fan.last_fan_value > 0: + self.fan.enable_pin.set_digital(eventtime, 0) + if ( + self.target + and self.target < self.fan.max_power + and self.fan.kick_start_time + and ( + not self.fan.last_fan_value + or self.target - self.fan.last_fan_value > self.fan.kick_start_threshold + ) + ): + # Run fan at full speed for specified kick_start_time + self.fan.mcu_fan.set_pwm(eventtime, self.fan.max_power) + eventtime += self.fan.kick_start_time + self.fan.mcu_fan.set_pwm(eventtime, pwm_value) + self.fan.last_fan_time = eventtime + self.fan.last_fan_value = self.target + self.fan.pwm_value = pwm_value + + if self.fan.min_rpm > 0 and (self.force or not self.fan.self_checking): + if pwm_value > 0: + if self.fan.fan_check_thread is None: + self.fan.fan_check_thread = self.fan.klipper_threads.register_job( + target=self.fan.fan_check + ) + self.fan.fan_check_thread.start() + else: + if self.fan.fan_check_thread is not None: + self.fan.fan_check_thread = None + + + class FanTachometer: def __init__(self, config): printer = config.get_printer() @@ -329,8 +409,7 @@ def cmd_M106(self, gcmd): def cmd_M107(self, gcmd): # Turn fan off force = gcmd.get_int("F", 0, minval=0, maxval=1) - end_print = gcmd.get_int("E", 0, minval=0, maxval=1) - self.fan.set_speed_from_command(0.0, force, end_print) + self.fan.set_speed_from_command(0.0, force) def load_config(config): From e61b54aa52718e9da891b456af9501452da80b3f Mon Sep 17 00:00:00 2001 From: Zeanon Date: Mon, 19 Aug 2024 13:35:24 +0200 Subject: [PATCH 004/158] Update fan.py --- klippy/extras/fan.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 790b0be02..34fac47ba 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -9,6 +9,7 @@ from . import pulse_counter FAN_MIN_TIME = 0.100 +FAN_REPORT_TIME = 0.250 SAFETY_CHECK_INIT_TIME = 3.0 @@ -317,7 +318,7 @@ def set_speed(self, print_time, value, force=False): def callback(self, eventtime): self._callback(eventtime) - return FAN_MIN_TIME + return FAN_REPORT_TIME def _callback(self, eventtime): with self.lock: From aadd99535e724f8d18daecb3a963c4d88a242098 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Mon, 19 Aug 2024 13:37:31 +0200 Subject: [PATCH 005/158] Update fan.py --- klippy/extras/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 34fac47ba..7f21898c9 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -9,7 +9,7 @@ from . import pulse_counter FAN_MIN_TIME = 0.100 -FAN_REPORT_TIME = 0.250 +FAN_REPORT_TIME = 0.300 SAFETY_CHECK_INIT_TIME = 3.0 From 68416feca2d8ce7836c8dfb6f92ac576e1dceed7 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Mon, 19 Aug 2024 13:45:39 +0200 Subject: [PATCH 006/158] . --- klippy/extras/controller_fan.py | 6 ++++-- klippy/extras/heater_fan.py | 6 +++--- klippy/extras/mpc_calibrate.py | 6 ++++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/klippy/extras/controller_fan.py b/klippy/extras/controller_fan.py index eeb25678c..a8281923f 100644 --- a/klippy/extras/controller_fan.py +++ b/klippy/extras/controller_fan.py @@ -9,6 +9,8 @@ from . import fan +PIN_MIN_TIME = 0.100 + class ControllerFan: def __init__(self, config, defined_fan=None): @@ -100,7 +102,7 @@ def callback(self): speed = self.get_speed(print_time) if self.enabled and speed != self.last_speed: self.last_speed = speed - self.fan.set_speed(print_time, speed) + self.fan.set_speed(print_time + PIN_MIN_TIME, speed) return 1.0 cmd_SET_CONTROLLER_FAN_help = "Enable or Disable a controller_fan" @@ -110,7 +112,7 @@ def cmd_SET_CONTROLLER_FAN(self, gcmd): if not self.enabled: curtime = self.printer.get_reactor().monotonic() print_time = self.fan.get_mcu().estimated_print_time(curtime) - self.fan.set_speed(print_time, 0.0) + self.fan.set_speed(print_time + PIN_MIN_TIME, 0.0) def load_config_prefix(config): diff --git a/klippy/extras/heater_fan.py b/klippy/extras/heater_fan.py index 61c975372..5cd7ef1e9 100644 --- a/klippy/extras/heater_fan.py +++ b/klippy/extras/heater_fan.py @@ -62,7 +62,7 @@ def callback(self, eventtime): 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, speed) + self.fan.set_speed(print_time + PIN_MIN_TIME, speed) return eventtime + 1.0 cmd_SET_HEATER_FAN_help = "Enable or Disable a heater_fan" @@ -73,11 +73,11 @@ def cmd_SET_HEATER_FAN(self, gcmd): if self.enabled: curtime = self.printer.get_reactor().monotonic() print_time = self.fan.get_mcu().estimated_print_time(curtime) - self.fan.set_speed(print_time, self.fan_speed) + self.fan.set_speed(print_time + PIN_MIN_TIME, self.fan_speed) if not self.enabled: curtime = self.printer.get_reactor().monotonic() print_time = self.fan.get_mcu().estimated_print_time(curtime) - self.fan.set_speed(print_time, 0.0) + self.fan.set_speed(print_time + PIN_MIN_TIME, 0.0) def load_config_prefix(config): diff --git a/klippy/extras/mpc_calibrate.py b/klippy/extras/mpc_calibrate.py index bb37bd2f7..7bc337c12 100644 --- a/klippy/extras/mpc_calibrate.py +++ b/klippy/extras/mpc_calibrate.py @@ -1,6 +1,8 @@ import math import logging +PIN_MIN_TIME = 0.100 + class MPCCalibrate: def __init__(self, config): @@ -282,7 +284,7 @@ def transfer_test( speed = idx / (fan_breakpoints - 1) curtime = self.heater.reactor.monotonic() print_time = fan.get_mcu().estimated_print_time(curtime) - fan.set_speed(print_time, speed) + fan.set_speed(print_time + PIN_MIN_TIME, speed) gcmd.respond_info("Waiting for temperature to stabilize") self.wait_stable(3) gcmd.respond_info( @@ -297,7 +299,7 @@ def transfer_test( fan_powers.append((speed, power)) curtime = self.heater.reactor.monotonic() print_time = fan.get_mcu().estimated_print_time(curtime) - fan.set_speed(print_time, 0.0) + fan.set_speed(print_time + PIN_MIN_TIME, 0.0) power_base = fan_powers[0][1] return { From 34509b26ce56eff794416f2d1608475a2c149a0d Mon Sep 17 00:00:00 2001 From: Zeanon Date: Mon, 19 Aug 2024 13:49:29 +0200 Subject: [PATCH 007/158] Update fan.py --- klippy/extras/fan.py | 210 ++++++++++++++++--------------------------- 1 file changed, 78 insertions(+), 132 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 7f21898c9..6a89d643e 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -5,11 +5,11 @@ # This file may be distributed under the terms of the GNU GPLv3 license. import logging import threading +import time from . import pulse_counter FAN_MIN_TIME = 0.100 -FAN_REPORT_TIME = 0.300 SAFETY_CHECK_INIT_TIME = 3.0 @@ -21,7 +21,10 @@ def __init__(self, config, default_shutdown_speed=0.0): self.klipper_threads = self.printer.get_klipper_threads() self.last_fan_value = 0.0 self.pwm_value = 0.0 - self.last_fan_time = 0.0 + self.queued_speed = None + self.queued_force = False + self.locking = False + self.unlock_timer = None # Read config self.kick_start_time = config.getfloat("kick_start_time", 0.1, minval=0.0) self.kick_start_threshold = config.getfloat( @@ -135,12 +138,6 @@ def __init__(self, config, default_shutdown_speed=0.0): self.self_checking = False self.startup_check_timer = None - real_time = config.getboolean("real_time", False) - if real_time: - self.set_speed = RealTimeFan(self).set_speed - else: - self.set_speed = StandardFan(self).set_speed - self.printer.register_event_handler("klippy:ready", self.handle_ready) # Register callbacks self.printer.register_event_handler( @@ -156,6 +153,7 @@ def __init__(self, config, default_shutdown_speed=0.0): ) def handle_ready(self): + self.unlock_timer = self.reactor.register_timer(self._unlock_lock) if self.startup_check: self.self_checking = True toolhead = self.printer.lookup_object("toolhead") @@ -165,9 +163,10 @@ def handle_ready(self): def set_startup_fan_speed(self, print_time): self.mcu_fan.set_pwm(print_time, self.max_power) - self.startup_check_timer = self.reactor.register_timer( + reactor = self.printer.get_reactor() + self.startup_check_timer = reactor.register_timer( self.startup_self_check, - self.reactor.monotonic() + self.startup_check_delay, + reactor.monotonic() + self.startup_check_delay, ) def startup_self_check(self, eventtime): @@ -184,13 +183,78 @@ def startup_self_check(self, eventtime): (lambda pt: self.set_speed(pt, self.pwm_value, force=True)) ) self.self_checking = False - self.reactor.unregister_timer(self.startup_check_timer) + self.printer.get_reactor().unregister_timer(self.startup_check_timer) self.startup_check_timer = None - return self.reactor.NEVER + return self.printer.get_reactor().NEVER def get_mcu(self): return self.mcu_fan.get_mcu() + def set_speed(self, print_time, value, force=False): + if self.locking: + self.queued_speed = value + self.queued_force = force + else: + self._set_speed(print_time, value, force) + + def _set_speed(self, print_time, value, force=False): + if value > 0: + # Scale value between min_power and max_power + pwm_value = value * (self.max_power - self.min_power) + self.min_power + pwm_value = max(self.min_power, min(self.max_power, pwm_value)) + else: + pwm_value = 0 + if pwm_value == self.pwm_value and not force: + return + if force or not self.self_checking: + self.locking = True + if self.enable_pin: + if value > 0 and self.last_fan_value == 0: + self.enable_pin.set_digital(print_time, 1) + elif value == 0 and self.last_fan_value > 0: + self.enable_pin.set_digital(print_time, 0) + if ( + value + and value < self.max_power + and self.kick_start_time + and ( + not self.last_fan_value + or value - self.last_fan_value > self.kick_start_threshold + ) + ): + # Run fan at full speed for specified kick_start_time + self.mcu_fan.set_pwm(print_time, self.max_power) + print_time += self.kick_start_time + self.mcu_fan.set_pwm(print_time, pwm_value) + self.reactor.update_timer(self.unlock_timer, print_time + FAN_MIN_TIME) + self.last_fan_value = value + self.pwm_value = pwm_value + + if self.min_rpm > 0 and (force or not self.self_checking): + if pwm_value > 0: + if self.fan_check_thread is None: + self.fan_check_thread = self.klipper_threads.register_job( + target=self.fan_check + ) + self.fan_check_thread.start() + else: + if self.fan_check_thread is not None: + self.fan_check_thread = None + + def _unlock_lock(self, eventtime): + if self.queued_speed is not None: + speed = self.queued_speed + force = self.queued_force + self.queued_speed = None + self.queued_force = False + self._set_speed( + self.get_mcu().estimated_print_time(self.reactor.monotonic()), + speed, + force, + ) + self.locking = False + return self.reactor.NEVER + def set_speed_from_command(self, value, force=False): toolhead = self.printer.lookup_object("toolhead") toolhead.register_lookahead_callback( @@ -228,7 +292,7 @@ def fan_check(self): self.gcode.run_script_from_command(self.on_error_gcode) else: self.printer.invoke_shutdown(msg) - return 0 + return self.printer.get_reactor().NEVER else: self.num_err = 0 if self.last_fan_value: @@ -245,129 +309,11 @@ def cmd_SET_FAN(self, gcmd): "MAX_POWER", self.max_power, above=self.min_power, maxval=1.0 ) self.min_rpm = gcmd.get_float("MIN_RPM", self.min_rpm, minval=0.0) - curtime = self.reactor.monotonic() + curtime = self.printer.get_reactor().monotonic() print_time = self.get_mcu().estimated_print_time(curtime) self.set_speed(print_time, self.last_fan_value, force=True) -class StandardFan: - def __init__(self, fan): - self.fan = fan - - def set_speed(self, print_time, value, force=False): - if value > 0: - # Scale value between min_power and max_power - pwm_value = value * (self.fan.max_power - self.fan.min_power) + self.fan.min_power - pwm_value = max(self.fan.min_power, min(self.fan.max_power, pwm_value)) - else: - pwm_value = 0 - if pwm_value == self.fan.pwm_value and not force: - return - print_time = max(self.fan.last_fan_time + FAN_MIN_TIME, print_time) - if force or not self.fan.self_checking: - if self.fan.enable_pin: - if value > 0 and self.fan.last_fan_value == 0: - self.fan.enable_pin.set_digital(print_time, 1) - elif value == 0 and self.fan.last_fan_value > 0: - self.fan.enable_pin.set_digital(print_time, 0) - if ( - value - and value < self.fan.max_power - and self.fan.kick_start_time - and ( - not self.fan.last_fan_value - or value - self.fan.last_fan_value > self.fan.kick_start_threshold - ) - ): - # Run fan at full speed for specified kick_start_time - self.fan.mcu_fan.set_pwm(print_time, self.fan.max_power) - print_time += self.fan.kick_start_time - self.fan.mcu_fan.set_pwm(print_time, pwm_value) - self.fan.last_fan_time = print_time - self.fan.last_fan_value = value - self.fan.pwm_value = pwm_value - - if self.fan.min_rpm > 0 and (force or not self.fan.self_checking): - if pwm_value > 0: - if self.fan.fan_check_thread is None: - self.fan.fan_check_thread = self.fan.klipper_threads.register_job( - target=self.fan.fan_check - ) - self.fan.fan_check_thread.start() - else: - if self.fan.fan_check_thread is not None: - self.fan.fan_check_thread = None - - -class RealTimeFan: - def __init__(self, fan): - self.fan = fan - self.lock = threading.Lock() - self.target = self.fan.last_fan_value - self.force = False - self.fan.printer.register_event_handler("klippy:ready", self._handle_ready) - - def _handle_ready(self): - self.fan.reactor.register_timer( - self.callback, self.fan.reactor.NOW - ) - - def set_speed(self, print_time, value, force=False): - self.target = value - self.force = force - - def callback(self, eventtime): - self._callback(eventtime) - return FAN_REPORT_TIME - - def _callback(self, eventtime): - with self.lock: - if self.target > 0: - # Scale value between min_power and max_power - pwm_value = self.target * (self.fan.max_power - self.fan.min_power) + self.fan.min_power - pwm_value = max(self.fan.min_power, min(self.fan.max_power, pwm_value)) - else: - pwm_value = 0 - if pwm_value == self.fan.pwm_value and not self.force: - return - if eventtime < self.fan.last_fan_time: - return - if self.force or not self.fan.self_checking: - if self.fan.enable_pin: - if self.target > 0 and self.fan.last_fan_value == 0: - self.fan.enable_pin.set_digital(eventtime, 1) - elif self.target == 0 and self.fan.last_fan_value > 0: - self.fan.enable_pin.set_digital(eventtime, 0) - if ( - self.target - and self.target < self.fan.max_power - and self.fan.kick_start_time - and ( - not self.fan.last_fan_value - or self.target - self.fan.last_fan_value > self.fan.kick_start_threshold - ) - ): - # Run fan at full speed for specified kick_start_time - self.fan.mcu_fan.set_pwm(eventtime, self.fan.max_power) - eventtime += self.fan.kick_start_time - self.fan.mcu_fan.set_pwm(eventtime, pwm_value) - self.fan.last_fan_time = eventtime - self.fan.last_fan_value = self.target - self.fan.pwm_value = pwm_value - - if self.fan.min_rpm > 0 and (self.force or not self.fan.self_checking): - if pwm_value > 0: - if self.fan.fan_check_thread is None: - self.fan.fan_check_thread = self.fan.klipper_threads.register_job( - target=self.fan.fan_check - ) - self.fan.fan_check_thread.start() - else: - if self.fan.fan_check_thread is not None: - self.fan.fan_check_thread = None - - - class FanTachometer: def __init__(self, config): printer = config.get_printer() From 137f0f70ad43362833186f54915148c4a4a6ed3f Mon Sep 17 00:00:00 2001 From: Zeanon Date: Mon, 19 Aug 2024 13:58:16 +0200 Subject: [PATCH 008/158] Update fan.py --- klippy/extras/fan.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 6a89d643e..81233e432 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -163,10 +163,9 @@ def handle_ready(self): def set_startup_fan_speed(self, print_time): self.mcu_fan.set_pwm(print_time, self.max_power) - reactor = self.printer.get_reactor() - self.startup_check_timer = reactor.register_timer( + self.startup_check_timer = self.reactor.register_timer( self.startup_self_check, - reactor.monotonic() + self.startup_check_delay, + self.reactor.monotonic() + self.startup_check_delay, ) def startup_self_check(self, eventtime): @@ -183,9 +182,9 @@ def startup_self_check(self, eventtime): (lambda pt: self.set_speed(pt, self.pwm_value, force=True)) ) self.self_checking = False - self.printer.get_reactor().unregister_timer(self.startup_check_timer) + self.reactor.unregister_timer(self.startup_check_timer) self.startup_check_timer = None - return self.printer.get_reactor().NEVER + return self.reactor.NEVER def get_mcu(self): return self.mcu_fan.get_mcu() @@ -292,7 +291,7 @@ def fan_check(self): self.gcode.run_script_from_command(self.on_error_gcode) else: self.printer.invoke_shutdown(msg) - return self.printer.get_reactor().NEVER + return self.reactor.NEVER else: self.num_err = 0 if self.last_fan_value: @@ -309,7 +308,7 @@ def cmd_SET_FAN(self, gcmd): "MAX_POWER", self.max_power, above=self.min_power, maxval=1.0 ) self.min_rpm = gcmd.get_float("MIN_RPM", self.min_rpm, minval=0.0) - curtime = self.printer.get_reactor().monotonic() + curtime = self.reactor.monotonic() print_time = self.get_mcu().estimated_print_time(curtime) self.set_speed(print_time, self.last_fan_value, force=True) From da893168aed3ee721aac817c97759841f270a87b Mon Sep 17 00:00:00 2001 From: Zeanon Date: Mon, 19 Aug 2024 15:56:57 +0200 Subject: [PATCH 009/158] Update fan.py --- klippy/extras/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 81233e432..717cc1bac 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -9,7 +9,7 @@ from . import pulse_counter -FAN_MIN_TIME = 0.100 +FAN_MIN_TIME = 0.200 SAFETY_CHECK_INIT_TIME = 3.0 From 15fa41884a0d46aae03c5c3f0924c52174e8e99f Mon Sep 17 00:00:00 2001 From: Zeanon Date: Mon, 19 Aug 2024 18:12:41 +0200 Subject: [PATCH 010/158] Update fan.py --- klippy/extras/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 717cc1bac..8b1530845 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -247,7 +247,7 @@ def _unlock_lock(self, eventtime): self.queued_speed = None self.queued_force = False self._set_speed( - self.get_mcu().estimated_print_time(self.reactor.monotonic()), + eventtime, speed, force, ) From 01d1524968dbe831e60380d774454561ee80ff4d Mon Sep 17 00:00:00 2001 From: Zeanon Date: Mon, 19 Aug 2024 18:12:57 +0200 Subject: [PATCH 011/158] Update fan.py --- klippy/extras/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 8b1530845..26c52289f 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -9,7 +9,7 @@ from . import pulse_counter -FAN_MIN_TIME = 0.200 +FAN_MIN_TIME = 0.100 SAFETY_CHECK_INIT_TIME = 3.0 From 183006f3535b9448fb54ffa8b8b5e17250f7d40c Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 10:47:47 +0200 Subject: [PATCH 012/158] . --- klippy/extras/danger_options.py | 2 ++ klippy/extras/fan.py | 3 ++- klippy/extras/gcode_macro.py | 8 ++++++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/klippy/extras/danger_options.py b/klippy/extras/danger_options.py index b16f0434e..ca407e95e 100644 --- a/klippy/extras/danger_options.py +++ b/klippy/extras/danger_options.py @@ -22,6 +22,8 @@ def __init__(self, config): self.error_on_unused_config_options = config.getboolean( "error_on_unused_config_options", True ) + self.jinja_boolean_filter = config.getlist("jinja_boolean_filter", ["true", "1"]) + self.jinja_extensions = config.getlist("jinja_extensions", []) self.allow_plugin_override = config.getboolean("allow_plugin_override", False) self.multi_mcu_trsync_timeout = config.getfloat( "multi_mcu_trsync_timeout", 0.025, minval=0.0 diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 26c52289f..34182afdf 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -19,6 +19,7 @@ def __init__(self, config, default_shutdown_speed=0.0): self.gcode = self.printer.lookup_object("gcode") self.reactor = self.printer.get_reactor() self.klipper_threads = self.printer.get_klipper_threads() + self.last_fan_time = 0.0 self.last_fan_value = 0.0 self.pwm_value = 0.0 self.queued_speed = None @@ -247,7 +248,7 @@ def _unlock_lock(self, eventtime): self.queued_speed = None self.queued_force = False self._set_speed( - eventtime, + eventtime + FAN_MIN_TIME, speed, force, ) diff --git a/klippy/extras/gcode_macro.py b/klippy/extras/gcode_macro.py index 986341a9a..282e7ca94 100644 --- a/klippy/extras/gcode_macro.py +++ b/klippy/extras/gcode_macro.py @@ -8,6 +8,8 @@ import jinja2, math import configfile +from extras.danger_options import get_danger_options + ###################################################################### # Template handling ###################################################################### @@ -101,12 +103,14 @@ def reload(self, script): class PrinterGCodeMacro: def __init__(self, config): self.printer = config.get_printer() + extensions = ["jinja2.ext.do", "jinja2.ext.loopcontrols"] + extensions.extend(get_danger_options().jinja_extensions) self.env = jinja2.Environment( "{%", "%}", "{", "}", - extensions=["jinja2.ext.do", "jinja2.ext.loopcontrols"], + extensions=extensions, ) self.env.filters["bool"] = self.boolean @@ -118,7 +122,7 @@ def __init__(self, config): def boolean(self, value): lowercase_value = str(value).lower() - return lowercase_value in ["true", "1"] + return lowercase_value in get_danger_options().jinja_boolean_filter def load_template(self, config, option, default=None): name = "%s:%s" % (config.get_name(), option) From d9bfb9567e4f559902ec568feb42a89a05497ab7 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 10:50:20 +0200 Subject: [PATCH 013/158] Update fan.py --- klippy/extras/fan.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 34182afdf..1657eb21a 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -19,9 +19,9 @@ def __init__(self, config, default_shutdown_speed=0.0): self.gcode = self.printer.lookup_object("gcode") self.reactor = self.printer.get_reactor() self.klipper_threads = self.printer.get_klipper_threads() - self.last_fan_time = 0.0 self.last_fan_value = 0.0 self.pwm_value = 0.0 + self.last_fan_time = 0.0 self.queued_speed = None self.queued_force = False self.locking = False @@ -229,6 +229,7 @@ def _set_speed(self, print_time, value, force=False): self.reactor.update_timer(self.unlock_timer, print_time + FAN_MIN_TIME) self.last_fan_value = value self.pwm_value = pwm_value + self.last_fan_time = print_time + FAN_MIN_TIME if self.min_rpm > 0 and (force or not self.self_checking): if pwm_value > 0: @@ -242,6 +243,8 @@ def _set_speed(self, print_time, value, force=False): self.fan_check_thread = None def _unlock_lock(self, eventtime): + if eventtime < self.last_fan_time: + return eventtime + FAN_MIN_TIME if self.queued_speed is not None: speed = self.queued_speed force = self.queued_force From f562c4ba6dc8bc514709b7609c71944a26427209 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 10:56:13 +0200 Subject: [PATCH 014/158] Update fan.py --- klippy/extras/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 1657eb21a..70a29ccc1 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -295,7 +295,7 @@ def fan_check(self): self.gcode.run_script_from_command(self.on_error_gcode) else: self.printer.invoke_shutdown(msg) - return self.reactor.NEVER + return 0 else: self.num_err = 0 if self.last_fan_value: From 4b2d6bd0501ed3a3b14c63563da986a6f67eb632 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 10:56:21 +0200 Subject: [PATCH 015/158] Update fan.py --- klippy/extras/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 70a29ccc1..e4b6a96ed 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -251,7 +251,7 @@ def _unlock_lock(self, eventtime): self.queued_speed = None self.queued_force = False self._set_speed( - eventtime + FAN_MIN_TIME, + eventtime, speed, force, ) From 26b9e6db44f2c8dbbc7e9de1fcf7e320bb2df7ac Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 12:07:13 +0200 Subject: [PATCH 016/158] . --- klippy/extras/danger_options.py | 7 ++++++- klippy/toolhead.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/klippy/extras/danger_options.py b/klippy/extras/danger_options.py index ca407e95e..e36af962a 100644 --- a/klippy/extras/danger_options.py +++ b/klippy/extras/danger_options.py @@ -22,12 +22,17 @@ def __init__(self, config): self.error_on_unused_config_options = config.getboolean( "error_on_unused_config_options", True ) - self.jinja_boolean_filter = config.getlist("jinja_boolean_filter", ["true", "1"]) + self.jinja_boolean_filter = config.getlist( + "jinja_boolean_filter", ["true", "1"] + ) self.jinja_extensions = config.getlist("jinja_extensions", []) self.allow_plugin_override = config.getboolean("allow_plugin_override", False) self.multi_mcu_trsync_timeout = config.getfloat( "multi_mcu_trsync_timeout", 0.025, minval=0.0 ) + self.drip_move_wait_time = config.getfloat( + "drip_move_wait_time", default=0.0, minval=0.0, maxval=0.3 + ) self.homing_elapsed_distance_tolerance = config.getfloat( "homing_elapsed_distance_tolerance", 0.5, minval=0.0 ) diff --git a/klippy/toolhead.py b/klippy/toolhead.py index daab6ddab..807b035b3 100644 --- a/klippy/toolhead.py +++ b/klippy/toolhead.py @@ -605,7 +605,7 @@ def _update_drip_move_time(self, next_print_time): curtime = self.reactor.monotonic() est_print_time = self.mcu.estimated_print_time(curtime) wait_time = self.print_time - est_print_time - flush_delay - if wait_time > 0.0 and self.can_pause: + if wait_time > get_danger_options().drip_move_wait_time and self.can_pause: # Pause before sending more steps self.drip_completion.wait(curtime + wait_time) continue From de596b6ba77b173ad6f4d54d6a010962bfd62b74 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 12:13:30 +0200 Subject: [PATCH 017/158] . --- klippy/extras/adxl345.py | 6 +----- klippy/extras/fan.py | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/klippy/extras/adxl345.py b/klippy/extras/adxl345.py index 0318dd2c1..51cf26d6f 100644 --- a/klippy/extras/adxl345.py +++ b/klippy/extras/adxl345.py @@ -131,7 +131,6 @@ def __init__(self, config, chip): self.name = name_parts[-1] self.register_commands(self.name) self.disabled_heaters = [] - self.init_timer = None if hasattr(config, "getlist"): self.disabled_heaters = config.getlist( "disable_heaters", self.disabled_heaters @@ -151,7 +150,7 @@ def read_accelerometer(self): raise Exception("No accelerometer measurements found") def _handle_ready(self): - self.init_timer = self.reactor.register_timer( + self.reactor.register_callback( self._init_accel, self.reactor.NOW ) @@ -173,9 +172,6 @@ def _init_accel(self, eventtime): "'%s' is not a valid heater." % (heater_name,) ) heater.set_enabled(not connected) - self.reactor.unregister_timer(self.init_timer) - self.init_timer = None - return self.reactor.NEVER def register_commands(self, name): # Register commands diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index e4b6a96ed..92eb7acdf 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -137,7 +137,6 @@ def __init__(self, config, default_shutdown_speed=0.0): self.min_rpm if self.startup_check_rpm is None else self.startup_check_rpm ) self.self_checking = False - self.startup_check_timer = None self.printer.register_event_handler("klippy:ready", self.handle_ready) # Register callbacks @@ -164,7 +163,7 @@ def handle_ready(self): def set_startup_fan_speed(self, print_time): self.mcu_fan.set_pwm(print_time, self.max_power) - self.startup_check_timer = self.reactor.register_timer( + self.reactor.register_callback( self.startup_self_check, self.reactor.monotonic() + self.startup_check_delay, ) @@ -183,9 +182,6 @@ def startup_self_check(self, eventtime): (lambda pt: self.set_speed(pt, self.pwm_value, force=True)) ) self.self_checking = False - self.reactor.unregister_timer(self.startup_check_timer) - self.startup_check_timer = None - return self.reactor.NEVER def get_mcu(self): return self.mcu_fan.get_mcu() From f92109db12a88981518e81e09f9cf814cc7c9262 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 12:15:38 +0200 Subject: [PATCH 018/158] Update fan.py --- klippy/extras/fan.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 92eb7acdf..149991540 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -193,7 +193,7 @@ def set_speed(self, print_time, value, force=False): else: self._set_speed(print_time, value, force) - def _set_speed(self, print_time, value, force=False): + def _set_speed(self, print_time, value, force=False, resend=False): if value > 0: # Scale value between min_power and max_power pwm_value = value * (self.max_power - self.min_power) + self.min_power @@ -222,7 +222,8 @@ def _set_speed(self, print_time, value, force=False): self.mcu_fan.set_pwm(print_time, self.max_power) print_time += self.kick_start_time self.mcu_fan.set_pwm(print_time, pwm_value) - self.reactor.update_timer(self.unlock_timer, print_time + FAN_MIN_TIME) + if not resend: + self.reactor.update_timer(self.unlock_timer, print_time + FAN_MIN_TIME) self.last_fan_value = value self.pwm_value = pwm_value self.last_fan_time = print_time + FAN_MIN_TIME @@ -239,8 +240,6 @@ def _set_speed(self, print_time, value, force=False): self.fan_check_thread = None def _unlock_lock(self, eventtime): - if eventtime < self.last_fan_time: - return eventtime + FAN_MIN_TIME if self.queued_speed is not None: speed = self.queued_speed force = self.queued_force @@ -250,7 +249,9 @@ def _unlock_lock(self, eventtime): eventtime, speed, force, + True ) + return eventtime + FAN_MIN_TIME self.locking = False return self.reactor.NEVER From d4e04d684e368c3fbed7d46fbe31add187998909 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 12:23:05 +0200 Subject: [PATCH 019/158] Update fan.py --- klippy/extras/fan.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 149991540..a05791cb2 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -245,13 +245,14 @@ def _unlock_lock(self, eventtime): force = self.queued_force self.queued_speed = None self.queued_force = False - self._set_speed( - eventtime, - speed, - force, - True - ) - return eventtime + FAN_MIN_TIME + if self.queued_speed != self.last_fan_value: + self._set_speed( + eventtime, + speed, + force, + True + ) + return eventtime + FAN_MIN_TIME self.locking = False return self.reactor.NEVER From e1c3e40d40cf066aa3f2a157ac4129bfbec0167d Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 12:42:49 +0200 Subject: [PATCH 020/158] Update fan.py --- klippy/extras/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index a05791cb2..374488a59 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -245,7 +245,7 @@ def _unlock_lock(self, eventtime): force = self.queued_force self.queued_speed = None self.queued_force = False - if self.queued_speed != self.last_fan_value: + if self.queued_speed != self.last_fan_value and not self.queued_force: self._set_speed( eventtime, speed, From b27c86d5a346ce3cb5b5fb35568ea368dfc4a3e4 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 12:45:09 +0200 Subject: [PATCH 021/158] . --- klippy/extras/adxl345.py | 4 +--- klippy/extras/fan.py | 7 +------ 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/klippy/extras/adxl345.py b/klippy/extras/adxl345.py index 51cf26d6f..69361a489 100644 --- a/klippy/extras/adxl345.py +++ b/klippy/extras/adxl345.py @@ -150,9 +150,7 @@ def read_accelerometer(self): raise Exception("No accelerometer measurements found") def _handle_ready(self): - self.reactor.register_callback( - self._init_accel, self.reactor.NOW - ) + self.reactor.register_callback(self._init_accel, self.reactor.NOW) def _init_accel(self, eventtime): try: diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 374488a59..d299011da 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -246,12 +246,7 @@ def _unlock_lock(self, eventtime): self.queued_speed = None self.queued_force = False if self.queued_speed != self.last_fan_value and not self.queued_force: - self._set_speed( - eventtime, - speed, - force, - True - ) + self._set_speed(eventtime, speed, force, True) return eventtime + FAN_MIN_TIME self.locking = False return self.reactor.NEVER From d06e85614c74e063312d3e5fb792743920a95adb Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 13:30:54 +0200 Subject: [PATCH 022/158] Update fan.py --- klippy/extras/fan.py | 47 +++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index d299011da..804dd0fbe 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -20,12 +20,13 @@ def __init__(self, config, default_shutdown_speed=0.0): self.reactor = self.printer.get_reactor() self.klipper_threads = self.printer.get_klipper_threads() self.last_fan_value = 0.0 - self.pwm_value = 0.0 + self.last_pwm_value = 0.0 self.last_fan_time = 0.0 - self.queued_speed = None + self.queued_value = None + self.queued_pwm_value = None self.queued_force = False self.locking = False - self.unlock_timer = None + self.unlock_timer = self.reactor.register_timer(self._unlock_lock) # Read config self.kick_start_time = config.getfloat("kick_start_time", 0.1, minval=0.0) self.kick_start_threshold = config.getfloat( @@ -153,7 +154,6 @@ def __init__(self, config, default_shutdown_speed=0.0): ) def handle_ready(self): - self.unlock_timer = self.reactor.register_timer(self._unlock_lock) if self.startup_check: self.self_checking = True toolhead = self.printer.lookup_object("toolhead") @@ -179,7 +179,7 @@ def startup_self_check(self, eventtime): logging.error(msg) self.printer.invoke_shutdown(msg) self.printer.lookup_object("toolhead").register_lookahead_callback( - (lambda pt: self.set_speed(pt, self.pwm_value, force=True)) + (lambda pt: self.set_speed(pt, self.last_pwm_value, force=True)) ) self.self_checking = False @@ -187,20 +187,21 @@ def get_mcu(self): return self.mcu_fan.get_mcu() def set_speed(self, print_time, value, force=False): - if self.locking: - self.queued_speed = value - self.queued_force = force - else: - self._set_speed(print_time, value, force) - - def _set_speed(self, print_time, value, force=False, resend=False): if value > 0: # Scale value between min_power and max_power pwm_value = value * (self.max_power - self.min_power) + self.min_power pwm_value = max(self.min_power, min(self.max_power, pwm_value)) else: pwm_value = 0 - if pwm_value == self.pwm_value and not force: + if self.locking: + self.queued_value = value + self.queued_pwm_value = pwm_value + self.queued_force = force + else: + self._set_speed(print_time, value, pwm_value, force) + + def _set_speed(self, print_time, value, pwm_value, force=False, resend=False): + if value == self.last_fan_value and pwm_value == self.last_pwm_value and not force: return if force or not self.self_checking: self.locking = True @@ -225,7 +226,7 @@ def _set_speed(self, print_time, value, force=False, resend=False): if not resend: self.reactor.update_timer(self.unlock_timer, print_time + FAN_MIN_TIME) self.last_fan_value = value - self.pwm_value = pwm_value + self.last_pwm_value = pwm_value self.last_fan_time = print_time + FAN_MIN_TIME if self.min_rpm > 0 and (force or not self.self_checking): @@ -240,13 +241,19 @@ def _set_speed(self, print_time, value, force=False, resend=False): self.fan_check_thread = None def _unlock_lock(self, eventtime): - if self.queued_speed is not None: - speed = self.queued_speed + if self.queued_pwm_value is not None: + value = self.queued_value + pwm_value = self.queued_pwm_value force = self.queued_force - self.queued_speed = None + self.queued_value = None + self.queued_pwm_value = None self.queued_force = False - if self.queued_speed != self.last_fan_value and not self.queued_force: - self._set_speed(eventtime, speed, force, True) + if ( + self.queued_value != self.last_fan_value + or self.queued_pwm_value != self.last_pwm_value + or not self.queued_force + ): + self._set_speed(eventtime, value, pwm_value, force, True) return eventtime + FAN_MIN_TIME self.locking = False return self.reactor.NEVER @@ -263,7 +270,7 @@ def _handle_request_restart(self, print_time): def get_status(self, eventtime): tachometer_status = self.tachometer.get_status(eventtime) return { - "speed": self.pwm_value, + "speed": self.last_pwm_value, "normalized_speed": self.last_fan_value, "rpm": tachometer_status["rpm"], } From 7f7570529a7d58ea45516a6ea42b4390f43c7dd3 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 13:46:42 +0200 Subject: [PATCH 023/158] Update fan.py --- klippy/extras/fan.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 804dd0fbe..432dcbd91 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -21,7 +21,6 @@ def __init__(self, config, default_shutdown_speed=0.0): self.klipper_threads = self.printer.get_klipper_threads() self.last_fan_value = 0.0 self.last_pwm_value = 0.0 - self.last_fan_time = 0.0 self.queued_value = None self.queued_pwm_value = None self.queued_force = False @@ -206,9 +205,9 @@ def _set_speed(self, print_time, value, pwm_value, force=False, resend=False): if force or not self.self_checking: self.locking = True if self.enable_pin: - if value > 0 and self.last_fan_value == 0: + if value > 0 and self.last_pwm_value == 0: self.enable_pin.set_digital(print_time, 1) - elif value == 0 and self.last_fan_value > 0: + elif value == 0 and self.last_pwm_value > 0: self.enable_pin.set_digital(print_time, 0) if ( value @@ -227,7 +226,6 @@ def _set_speed(self, print_time, value, pwm_value, force=False, resend=False): self.reactor.update_timer(self.unlock_timer, print_time + FAN_MIN_TIME) self.last_fan_value = value self.last_pwm_value = pwm_value - self.last_fan_time = print_time + FAN_MIN_TIME if self.min_rpm > 0 and (force or not self.self_checking): if pwm_value > 0: @@ -239,6 +237,7 @@ def _set_speed(self, print_time, value, pwm_value, force=False, resend=False): else: if self.fan_check_thread is not None: self.fan_check_thread = None + return print_time + FAN_MIN_TIME def _unlock_lock(self, eventtime): if self.queued_pwm_value is not None: @@ -253,8 +252,7 @@ def _unlock_lock(self, eventtime): or self.queued_pwm_value != self.last_pwm_value or not self.queued_force ): - self._set_speed(eventtime, value, pwm_value, force, True) - return eventtime + FAN_MIN_TIME + return self._set_speed(eventtime, value, pwm_value, force, True) self.locking = False return self.reactor.NEVER From a1a142fd5023d7eeca6a05d983a4579db35e891d Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 14:16:08 +0200 Subject: [PATCH 024/158] Update fan.py --- klippy/extras/fan.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 432dcbd91..912cab6ee 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -18,6 +18,7 @@ def __init__(self, config, default_shutdown_speed=0.0): self.printer = config.get_printer() self.gcode = self.printer.lookup_object("gcode") self.reactor = self.printer.get_reactor() + self.estimated_print_time = None self.klipper_threads = self.printer.get_klipper_threads() self.last_fan_value = 0.0 self.last_pwm_value = 0.0 @@ -153,6 +154,7 @@ def __init__(self, config, default_shutdown_speed=0.0): ) def handle_ready(self): + self.estimated_print_time = self.get_mcu().estimated_print_time if self.startup_check: self.self_checking = True toolhead = self.printer.lookup_object("toolhead") @@ -252,6 +254,7 @@ def _unlock_lock(self, eventtime): or self.queued_pwm_value != self.last_pwm_value or not self.queued_force ): + self.estimated_print_time(eventtime) return self._set_speed(eventtime, value, pwm_value, force, True) self.locking = False return self.reactor.NEVER From d4c058149573379c20925d587421b6143cf96725 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 14:16:32 +0200 Subject: [PATCH 025/158] Update fan.py --- klippy/extras/fan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 912cab6ee..7e406cbb6 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -254,8 +254,8 @@ def _unlock_lock(self, eventtime): or self.queued_pwm_value != self.last_pwm_value or not self.queued_force ): - self.estimated_print_time(eventtime) - return self._set_speed(eventtime, value, pwm_value, force, True) + print_time = self.estimated_print_time(eventtime) + return self._set_speed(print_time, value, pwm_value, force, True) self.locking = False return self.reactor.NEVER From ae95c36b39bc1de924798f8a9c6ff4ec28192695 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 14:17:46 +0200 Subject: [PATCH 026/158] Update fan.py --- klippy/extras/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 7e406cbb6..e730d1bfb 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -9,7 +9,7 @@ from . import pulse_counter -FAN_MIN_TIME = 0.100 +FAN_MIN_TIME = 1.0 SAFETY_CHECK_INIT_TIME = 3.0 From 887cae4a37f385ee50ac1db8949374c94f27ee2d Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 14:19:18 +0200 Subject: [PATCH 027/158] Update fan.py --- klippy/extras/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index e730d1bfb..b0a681c51 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -225,7 +225,7 @@ def _set_speed(self, print_time, value, pwm_value, force=False, resend=False): print_time += self.kick_start_time self.mcu_fan.set_pwm(print_time, pwm_value) if not resend: - self.reactor.update_timer(self.unlock_timer, print_time + FAN_MIN_TIME) + self.reactor.update_timer(self.unlock_timer, self.reactor.monotonic() + FAN_MIN_TIME) self.last_fan_value = value self.last_pwm_value = pwm_value From f57d18b2b5395873502692da9ccb04709b059ada Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 14:21:21 +0200 Subject: [PATCH 028/158] . --- klippy/extras/fan.py | 6 +++--- klippy/extras/state_notify.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index b0a681c51..c43b28955 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -201,7 +201,7 @@ def set_speed(self, print_time, value, force=False): else: self._set_speed(print_time, value, pwm_value, force) - def _set_speed(self, print_time, value, pwm_value, force=False, resend=False): + def _set_speed(self, print_time, value, pwm_value, force=False, resend=False, eventtime=0.0): if value == self.last_fan_value and pwm_value == self.last_pwm_value and not force: return if force or not self.self_checking: @@ -239,7 +239,7 @@ def _set_speed(self, print_time, value, pwm_value, force=False, resend=False): else: if self.fan_check_thread is not None: self.fan_check_thread = None - return print_time + FAN_MIN_TIME + return eventtime + FAN_MIN_TIME def _unlock_lock(self, eventtime): if self.queued_pwm_value is not None: @@ -255,7 +255,7 @@ def _unlock_lock(self, eventtime): or not self.queued_force ): print_time = self.estimated_print_time(eventtime) - return self._set_speed(print_time, value, pwm_value, force, True) + return self._set_speed(print_time, value, pwm_value, force, True, eventtime) self.locking = False return self.reactor.NEVER diff --git a/klippy/extras/state_notify.py b/klippy/extras/state_notify.py index eda767f3f..3a8d8c29a 100644 --- a/klippy/extras/state_notify.py +++ b/klippy/extras/state_notify.py @@ -203,7 +203,7 @@ def _menu_check_timer_handler(self, eventtime): if not self.menu.is_running(): self._state_handler("menu_exit", eventtime) return self.reactor.NEVER - return self.reactor.monotonic() + TIMER_DURATION + return eventtime + TIMER_DURATION # Timer to monitor print statistics for state changes. This is needed to catch # print pauses. From 8626e291770c308b14597b30615c4bf054fc6188 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 14:22:19 +0200 Subject: [PATCH 029/158] Update fan.py --- klippy/extras/fan.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index c43b28955..40ed9c29f 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -201,7 +201,7 @@ def set_speed(self, print_time, value, force=False): else: self._set_speed(print_time, value, pwm_value, force) - def _set_speed(self, print_time, value, pwm_value, force=False, resend=False, eventtime=0.0): + def _set_speed(self, print_time, value, pwm_value, force=False, resend=False): if value == self.last_fan_value and pwm_value == self.last_pwm_value and not force: return if force or not self.self_checking: @@ -239,7 +239,7 @@ def _set_speed(self, print_time, value, pwm_value, force=False, resend=False, ev else: if self.fan_check_thread is not None: self.fan_check_thread = None - return eventtime + FAN_MIN_TIME + return self.reactor.monotonic() + FAN_MIN_TIME def _unlock_lock(self, eventtime): if self.queued_pwm_value is not None: @@ -255,7 +255,7 @@ def _unlock_lock(self, eventtime): or not self.queued_force ): print_time = self.estimated_print_time(eventtime) - return self._set_speed(print_time, value, pwm_value, force, True, eventtime) + return self._set_speed(print_time, value, pwm_value, force, True) self.locking = False return self.reactor.NEVER From b5d4f5104f855f36aee5256580fc817bd7d81f1f Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 14:24:13 +0200 Subject: [PATCH 030/158] Update fan.py --- klippy/extras/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 40ed9c29f..22ae7dd38 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -254,7 +254,7 @@ def _unlock_lock(self, eventtime): or self.queued_pwm_value != self.last_pwm_value or not self.queued_force ): - print_time = self.estimated_print_time(eventtime) + print_time = self.estimated_print_time(self.reactor.monotonic()) return self._set_speed(print_time, value, pwm_value, force, True) self.locking = False return self.reactor.NEVER From ab392abb76caff6621b39b901b89ae685b0ac299 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 14:25:12 +0200 Subject: [PATCH 031/158] Update fan.py --- klippy/extras/fan.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 22ae7dd38..57f9498cb 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -9,7 +9,7 @@ from . import pulse_counter -FAN_MIN_TIME = 1.0 +FAN_MIN_TIME = 0.100 SAFETY_CHECK_INIT_TIME = 3.0 @@ -254,8 +254,8 @@ def _unlock_lock(self, eventtime): or self.queued_pwm_value != self.last_pwm_value or not self.queued_force ): - print_time = self.estimated_print_time(self.reactor.monotonic()) - return self._set_speed(print_time, value, pwm_value, force, True) + print_time = self.estimated_print_time(eventtime) + return self._set_speed(print_time + FAN_MIN_TIME, value, pwm_value, force, True) self.locking = False return self.reactor.NEVER From ba2fcf7941c1e60881c562dc7f85f2fccf40772c Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 14:27:41 +0200 Subject: [PATCH 032/158] Update fan.py --- klippy/extras/fan.py | 1 + 1 file changed, 1 insertion(+) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 57f9498cb..3e211f4fa 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -255,6 +255,7 @@ def _unlock_lock(self, eventtime): or not self.queued_force ): print_time = self.estimated_print_time(eventtime) + logging.info("UNLOCK_FAN") return self._set_speed(print_time + FAN_MIN_TIME, value, pwm_value, force, True) self.locking = False return self.reactor.NEVER From f346715bc2f8fdd7e3ee5abe3a6699321d26e0c9 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 14:32:00 +0200 Subject: [PATCH 033/158] Update fan.py --- klippy/extras/fan.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 3e211f4fa..9a2ff9671 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -225,7 +225,9 @@ def _set_speed(self, print_time, value, pwm_value, force=False, resend=False): print_time += self.kick_start_time self.mcu_fan.set_pwm(print_time, pwm_value) if not resend: - self.reactor.update_timer(self.unlock_timer, self.reactor.monotonic() + FAN_MIN_TIME) + w_time = self.reactor.monotonic() + logging.info("w_time=%f" % w_time) + self.reactor.update_timer(self.unlock_timer, w_time + FAN_MIN_TIME) self.last_fan_value = value self.last_pwm_value = pwm_value @@ -255,8 +257,8 @@ def _unlock_lock(self, eventtime): or not self.queued_force ): print_time = self.estimated_print_time(eventtime) - logging.info("UNLOCK_FAN") - return self._set_speed(print_time + FAN_MIN_TIME, value, pwm_value, force, True) + logging.info("UNLOCK_FAN=%f" % eventtime) + return self._set_speed(print_time, value, pwm_value, force, True) self.locking = False return self.reactor.NEVER From fa224d2a366fef761b01e42de642a51a989e55be Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 14:35:14 +0200 Subject: [PATCH 034/158] Update fan.py --- klippy/extras/fan.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 9a2ff9671..4170db8f1 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -225,9 +225,8 @@ def _set_speed(self, print_time, value, pwm_value, force=False, resend=False): print_time += self.kick_start_time self.mcu_fan.set_pwm(print_time, pwm_value) if not resend: - w_time = self.reactor.monotonic() - logging.info("w_time=%f" % w_time) - self.reactor.update_timer(self.unlock_timer, w_time + FAN_MIN_TIME) + logging.info("w_time=%f" % print_time) + self.reactor.update_timer(self.unlock_timer, self.reactor.monotonic() + FAN_MIN_TIME) self.last_fan_value = value self.last_pwm_value = pwm_value @@ -257,7 +256,7 @@ def _unlock_lock(self, eventtime): or not self.queued_force ): print_time = self.estimated_print_time(eventtime) - logging.info("UNLOCK_FAN=%f" % eventtime) + logging.info("UNLOCK_FAN=%f" % print_time) return self._set_speed(print_time, value, pwm_value, force, True) self.locking = False return self.reactor.NEVER From c2913f0033ceb2175180ad86787b26f13fcbc137 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 14:39:09 +0200 Subject: [PATCH 035/158] Update fan.py --- klippy/extras/fan.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 4170db8f1..6eb1c550f 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -22,6 +22,7 @@ def __init__(self, config, default_shutdown_speed=0.0): self.klipper_threads = self.printer.get_klipper_threads() self.last_fan_value = 0.0 self.last_pwm_value = 0.0 + self.last_fan_time = 0. self.queued_value = None self.queued_pwm_value = None self.queued_force = False @@ -229,6 +230,7 @@ def _set_speed(self, print_time, value, pwm_value, force=False, resend=False): self.reactor.update_timer(self.unlock_timer, self.reactor.monotonic() + FAN_MIN_TIME) self.last_fan_value = value self.last_pwm_value = pwm_value + self.last_fan_time = print_time if self.min_rpm > 0 and (force or not self.self_checking): if pwm_value > 0: @@ -257,7 +259,7 @@ def _unlock_lock(self, eventtime): ): print_time = self.estimated_print_time(eventtime) logging.info("UNLOCK_FAN=%f" % print_time) - return self._set_speed(print_time, value, pwm_value, force, True) + return self._set_speed(self.last_fan_time + FAN_MIN_TIME, value, pwm_value, force, True) self.locking = False return self.reactor.NEVER From dc1f3e1b1d69f32e4adffc8f516fca3cf1b4087f Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 14:44:57 +0200 Subject: [PATCH 036/158] Update fan.py --- klippy/extras/fan.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 6eb1c550f..4b6d1a731 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -226,7 +226,6 @@ def _set_speed(self, print_time, value, pwm_value, force=False, resend=False): print_time += self.kick_start_time self.mcu_fan.set_pwm(print_time, pwm_value) if not resend: - logging.info("w_time=%f" % print_time) self.reactor.update_timer(self.unlock_timer, self.reactor.monotonic() + FAN_MIN_TIME) self.last_fan_value = value self.last_pwm_value = pwm_value @@ -242,7 +241,6 @@ def _set_speed(self, print_time, value, pwm_value, force=False, resend=False): else: if self.fan_check_thread is not None: self.fan_check_thread = None - return self.reactor.monotonic() + FAN_MIN_TIME def _unlock_lock(self, eventtime): if self.queued_pwm_value is not None: @@ -257,9 +255,8 @@ def _unlock_lock(self, eventtime): or self.queued_pwm_value != self.last_pwm_value or not self.queued_force ): - print_time = self.estimated_print_time(eventtime) - logging.info("UNLOCK_FAN=%f" % print_time) - return self._set_speed(self.last_fan_time + FAN_MIN_TIME, value, pwm_value, force, True) + self._set_speed(self.last_fan_time + FAN_MIN_TIME, value, pwm_value, force, True) + return eventtime + FAN_MIN_TIME self.locking = False return self.reactor.NEVER From daa7f05a6c84d464b85cd213f4bf711f45b40b7c Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 15:23:21 +0200 Subject: [PATCH 037/158] . --- klippy/extras/fan.py | 17 +++++++++++++---- klippy/klipper_threads.py | 23 ++++++++++++++++++++--- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 4b6d1a731..6c39f89de 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -22,7 +22,7 @@ def __init__(self, config, default_shutdown_speed=0.0): self.klipper_threads = self.printer.get_klipper_threads() self.last_fan_value = 0.0 self.last_pwm_value = 0.0 - self.last_fan_time = 0. + self.last_fan_time = 0.0 self.queued_value = None self.queued_pwm_value = None self.queued_force = False @@ -203,7 +203,11 @@ def set_speed(self, print_time, value, force=False): self._set_speed(print_time, value, pwm_value, force) def _set_speed(self, print_time, value, pwm_value, force=False, resend=False): - if value == self.last_fan_value and pwm_value == self.last_pwm_value and not force: + if ( + value == self.last_fan_value + and pwm_value == self.last_pwm_value + and not force + ): return if force or not self.self_checking: self.locking = True @@ -226,7 +230,9 @@ def _set_speed(self, print_time, value, pwm_value, force=False, resend=False): print_time += self.kick_start_time self.mcu_fan.set_pwm(print_time, pwm_value) if not resend: - self.reactor.update_timer(self.unlock_timer, self.reactor.monotonic() + FAN_MIN_TIME) + self.reactor.update_timer( + self.unlock_timer, self.reactor.monotonic() + FAN_MIN_TIME + ) self.last_fan_value = value self.last_pwm_value = pwm_value self.last_fan_time = print_time @@ -240,6 +246,7 @@ def _set_speed(self, print_time, value, pwm_value, force=False, resend=False): self.fan_check_thread.start() else: if self.fan_check_thread is not None: + self.fan_check_thread.unregister() self.fan_check_thread = None def _unlock_lock(self, eventtime): @@ -255,7 +262,9 @@ def _unlock_lock(self, eventtime): or self.queued_pwm_value != self.last_pwm_value or not self.queued_force ): - self._set_speed(self.last_fan_time + FAN_MIN_TIME, value, pwm_value, force, True) + self._set_speed( + self.last_fan_time + FAN_MIN_TIME, value, pwm_value, force, True + ) return eventtime + FAN_MIN_TIME self.locking = False return self.reactor.NEVER diff --git a/klippy/klipper_threads.py b/klippy/klipper_threads.py index 4a5fdec0d..bd0593653 100644 --- a/klippy/klipper_threads.py +++ b/klippy/klipper_threads.py @@ -25,6 +25,10 @@ def register_job( daemon=daemon, ) + def unregister_job(self, job): + if job in self.registered_threads: + job.running = False + def end(self): self.running = False @@ -55,21 +59,34 @@ def __init__( daemon=daemon, ) self.k_threads.registered_threads.append(self) + self.running = True def start(self): self.thread.start() def _run_job(self, job, args=(), **kwargs): try: - wait_time = job(*args, **kwargs) - while wait_time > 0 and self.k_threads.running: - time.sleep(wait_time) + if self.k_threads.running and self.running: wait_time = job(*args, **kwargs) + while wait_time > 0 and self.k_threads.running and self.running: + time.sleep(wait_time) + if not self.running: + return + wait_time = job(*args, **kwargs) finally: self.k_threads.registered_threads.remove(self) self.thread = None sys.exit() + def end(self): + self.running = False + + def kill(self): + self.running = False + + def unregister(self): + self.running = False + def finalize(self): if self.thread is not None and self.thread.is_alive(): self.thread.join() From 526a293a706f75158ba334ca4001fa7b3cdbe767 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 17:38:46 +0200 Subject: [PATCH 038/158] Update fan.py --- klippy/extras/fan.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 6c39f89de..cccbbfdc5 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -200,15 +200,19 @@ def set_speed(self, print_time, value, force=False): self.queued_pwm_value = pwm_value self.queued_force = force else: - self._set_speed(print_time, value, pwm_value, force) + self._set_speed( + print_time=print_time, value=value, pwm_value=pwm_value, force=force + ) - def _set_speed(self, print_time, value, pwm_value, force=False, resend=False): + def _set_speed( + self, print_time, value, pwm_value, force=False, resend=False, eventtime=0.0 + ): if ( value == self.last_fan_value and pwm_value == self.last_pwm_value and not force ): - return + return eventtime + FAN_MIN_TIME if force or not self.self_checking: self.locking = True if self.enable_pin: @@ -228,11 +232,10 @@ def _set_speed(self, print_time, value, pwm_value, force=False, resend=False): # Run fan at full speed for specified kick_start_time self.mcu_fan.set_pwm(print_time, self.max_power) print_time += self.kick_start_time + eventtime += self.kick_start_time self.mcu_fan.set_pwm(print_time, pwm_value) if not resend: - self.reactor.update_timer( - self.unlock_timer, self.reactor.monotonic() + FAN_MIN_TIME - ) + self.reactor.update_timer(self.unlock_timer, eventtime + FAN_MIN_TIME) self.last_fan_value = value self.last_pwm_value = pwm_value self.last_fan_time = print_time @@ -248,6 +251,7 @@ def _set_speed(self, print_time, value, pwm_value, force=False, resend=False): if self.fan_check_thread is not None: self.fan_check_thread.unregister() self.fan_check_thread = None + return eventtime + FAN_MIN_TIME def _unlock_lock(self, eventtime): if self.queued_pwm_value is not None: @@ -262,10 +266,14 @@ def _unlock_lock(self, eventtime): or self.queued_pwm_value != self.last_pwm_value or not self.queued_force ): - self._set_speed( - self.last_fan_time + FAN_MIN_TIME, value, pwm_value, force, True + return self._set_speed( + print_time=self.last_fan_time + FAN_MIN_TIME, + value=value, + pwm_value=pwm_value, + force=force, + resend=True, + eventtime=eventtime, ) - return eventtime + FAN_MIN_TIME self.locking = False return self.reactor.NEVER From 95319005e625055253a0aeceba84e6e541d9fd64 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 21:56:07 +0200 Subject: [PATCH 039/158] Update heaters.py --- klippy/extras/heaters.py | 1 + 1 file changed, 1 insertion(+) diff --git a/klippy/extras/heaters.py b/klippy/extras/heaters.py index 59213065e..bb98cc0f7 100644 --- a/klippy/extras/heaters.py +++ b/klippy/extras/heaters.py @@ -98,6 +98,7 @@ def __init__(self, config, sensor, sensor_config=None): sensor_config.getfloat("target_reach_time", None) sensor_config.getfloat("smoothing", None) sensor_config.getfloat("heater_power", None) + sensor_config.getfloat("heater_powers", None) sensor_config.getfloat("sensor_responsiveness", None) sensor_config.getfloat("min_ambient_change", None) sensor_config.getfloat("steady_state_rate", None) From 1d6b3ef291dfb5bc332a06f7956dcb6ed39b21b8 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 21:59:50 +0200 Subject: [PATCH 040/158] Update heaters.py --- klippy/extras/heaters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/heaters.py b/klippy/extras/heaters.py index bb98cc0f7..11f64c319 100644 --- a/klippy/extras/heaters.py +++ b/klippy/extras/heaters.py @@ -98,7 +98,7 @@ def __init__(self, config, sensor, sensor_config=None): sensor_config.getfloat("target_reach_time", None) sensor_config.getfloat("smoothing", None) sensor_config.getfloat("heater_power", None) - sensor_config.getfloat("heater_powers", None) + sensor_config.getlist("heater_powers", None) sensor_config.getfloat("sensor_responsiveness", None) sensor_config.getfloat("min_ambient_change", None) sensor_config.getfloat("steady_state_rate", None) From 8eb82b34e8bcfc83e0e49aa7624625bbfe0e229f Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 22:11:44 +0200 Subject: [PATCH 041/158] Update tmc.py --- klippy/extras/tmc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/klippy/extras/tmc.py b/klippy/extras/tmc.py index 6f447331a..2e1c08694 100644 --- a/klippy/extras/tmc.py +++ b/klippy/extras/tmc.py @@ -244,8 +244,8 @@ def get_status(self, eventtime=None): return { "drv_status": None, "temperature": None, - "measured_min_temp": self.measured_min, - "measured_max_temp": self.measured_max, + "measured_min_temp": 0, + "measured_max_temp": 0, } temp = self.get_temperature() last_value, reg_name = self.drv_status_reg_info[:2] @@ -263,8 +263,8 @@ def get_status(self, eventtime=None): return { "drv_status": self.last_drv_fields, "temperature": temp, - "measured_min_temp": self.measured_min, - "measured_max_temp": self.measured_max, + "measured_min_temp": 0, + "measured_max_temp": 0, } From ff56e7b3302eab6a46b2b5dfc7f743d6108ecbd8 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 22:25:44 +0200 Subject: [PATCH 042/158] Update tmc.py --- klippy/extras/tmc.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/klippy/extras/tmc.py b/klippy/extras/tmc.py index 2e1c08694..6cacea14a 100644 --- a/klippy/extras/tmc.py +++ b/klippy/extras/tmc.py @@ -241,11 +241,17 @@ def get_temperature(self): def get_status(self, eventtime=None): if self.check_timer is None: + measured_min = ( + None if self.measured_min is None else round(self.measured_min, 2) + ) + measured_max = ( + None if self.measured_max is None else round(self.measured_max, 2) + ) return { "drv_status": None, "temperature": None, - "measured_min_temp": 0, - "measured_max_temp": 0, + "measured_min_temp": measured_min, + "measured_max_temp": measured_max, } temp = self.get_temperature() last_value, reg_name = self.drv_status_reg_info[:2] @@ -255,16 +261,22 @@ def get_status(self, eventtime=None): self.last_drv_fields = {n: v for n, v in fields.items() if v} if temp: self.measured_min = min( - self.measured_min if self.measured_min else 99999999.0, temp + 99999999.0 if self.measured_min is None else self.measured_min, temp ) self.measured_max = max( - self.measured_max if self.measured_max else 0.0, temp + 0.0 if self.measured_max is None else self.measured_max, temp ) + measured_min = ( + None if self.measured_min is None else round(self.measured_min, 2) + ) + measured_max = ( + None if self.measured_max is None else round(self.measured_max, 2) + ) return { "drv_status": self.last_drv_fields, "temperature": temp, - "measured_min_temp": 0, - "measured_max_temp": 0, + "measured_min_temp": measured_min, + "measured_max_temp": measured_max, } From c2dbc2159654ba2288564fc0333e74ef04ba6fa6 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 22:58:05 +0200 Subject: [PATCH 043/158] Update tmc.py --- klippy/extras/tmc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/klippy/extras/tmc.py b/klippy/extras/tmc.py index 6cacea14a..8ec1c1a95 100644 --- a/klippy/extras/tmc.py +++ b/klippy/extras/tmc.py @@ -242,10 +242,10 @@ def get_temperature(self): def get_status(self, eventtime=None): if self.check_timer is None: measured_min = ( - None if self.measured_min is None else round(self.measured_min, 2) + 99999999.0 if self.measured_min is None else round(self.measured_min, 2) ) measured_max = ( - None if self.measured_max is None else round(self.measured_max, 2) + 99999999.0 if self.measured_max is None else round(self.measured_max, 2) ) return { "drv_status": None, @@ -267,10 +267,10 @@ def get_status(self, eventtime=None): 0.0 if self.measured_max is None else self.measured_max, temp ) measured_min = ( - None if self.measured_min is None else round(self.measured_min, 2) + 99999999.0 if self.measured_min is None else round(self.measured_min, 2) ) measured_max = ( - None if self.measured_max is None else round(self.measured_max, 2) + 99999999.0 if self.measured_max is None else round(self.measured_max, 2) ) return { "drv_status": self.last_drv_fields, From fedac159e45bc781dc33b1c7c7881483d1c4b4e7 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 20 Aug 2024 23:03:59 +0200 Subject: [PATCH 044/158] Update tmc.py --- klippy/extras/tmc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/klippy/extras/tmc.py b/klippy/extras/tmc.py index 8ec1c1a95..be95db4ff 100644 --- a/klippy/extras/tmc.py +++ b/klippy/extras/tmc.py @@ -242,10 +242,10 @@ def get_temperature(self): def get_status(self, eventtime=None): if self.check_timer is None: measured_min = ( - 99999999.0 if self.measured_min is None else round(self.measured_min, 2) + 0.0 if self.measured_min is None else round(self.measured_min, 2) ) measured_max = ( - 99999999.0 if self.measured_max is None else round(self.measured_max, 2) + 0.0 if self.measured_max is None else round(self.measured_max, 2) ) return { "drv_status": None, @@ -267,10 +267,10 @@ def get_status(self, eventtime=None): 0.0 if self.measured_max is None else self.measured_max, temp ) measured_min = ( - 99999999.0 if self.measured_min is None else round(self.measured_min, 2) + 0.0 if self.measured_min is None else round(self.measured_min, 2) ) measured_max = ( - 99999999.0 if self.measured_max is None else round(self.measured_max, 2) + 0.0 if self.measured_max is None else round(self.measured_max, 2) ) return { "drv_status": self.last_drv_fields, From 7771b48c164e25edde319cd50cd59a6613aeed08 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 21 Aug 2024 11:30:37 +0200 Subject: [PATCH 045/158] . --- klippy/extras/z_tilt.py | 5 +---- klippy/extras/z_tilt_ng.py | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/klippy/extras/z_tilt.py b/klippy/extras/z_tilt.py index 79ec8f89f..74ef727ef 100644 --- a/klippy/extras/z_tilt.py +++ b/klippy/extras/z_tilt.py @@ -85,10 +85,7 @@ def __init__(self, printer): printer.register_event_handler("unhome:mark_as_unhomed_z", self._motor_off) def check_retry_result(self, retry_result): - if retry_result and ( - (isinstance(retry_result, str) and retry_result == "done") - or (isinstance(retry_result, float) and int(retry_result) == 0) - ): + if not retry_result: self.applied = True return retry_result diff --git a/klippy/extras/z_tilt_ng.py b/klippy/extras/z_tilt_ng.py index 5a075384a..2d19f5a90 100644 --- a/klippy/extras/z_tilt_ng.py +++ b/klippy/extras/z_tilt_ng.py @@ -106,10 +106,7 @@ def __init__(self, printer): printer.register_event_handler("unhome:mark_as_unhomed_z", self._motor_off) def check_retry_result(self, retry_result): - if retry_result and ( - (isinstance(retry_result, str) and retry_result == "done") - or (isinstance(retry_result, float) and int(retry_result) == 0) - ): + if not retry_result: self.applied = True return retry_result From f2699aa391d53f94215834057e3245ac851c9973 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 21 Aug 2024 11:44:33 +0200 Subject: [PATCH 046/158] . --- klippy/extras/tmc.py | 8 ++------ klippy/extras/z_tilt.py | 4 +++- klippy/extras/z_tilt_ng.py | 4 +++- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/klippy/extras/tmc.py b/klippy/extras/tmc.py index be95db4ff..4d6b5a6ff 100644 --- a/klippy/extras/tmc.py +++ b/klippy/extras/tmc.py @@ -266,12 +266,8 @@ def get_status(self, eventtime=None): self.measured_max = max( 0.0 if self.measured_max is None else self.measured_max, temp ) - measured_min = ( - 0.0 if self.measured_min is None else round(self.measured_min, 2) - ) - measured_max = ( - 0.0 if self.measured_max is None else round(self.measured_max, 2) - ) + measured_min = 0.0 if self.measured_min is None else round(self.measured_min, 2) + measured_max = 0.0 if self.measured_max is None else round(self.measured_max, 2) return { "drv_status": self.last_drv_fields, "temperature": temp, diff --git a/klippy/extras/z_tilt.py b/klippy/extras/z_tilt.py index 74ef727ef..f14c6e45a 100644 --- a/klippy/extras/z_tilt.py +++ b/klippy/extras/z_tilt.py @@ -85,7 +85,9 @@ def __init__(self, printer): printer.register_event_handler("unhome:mark_as_unhomed_z", self._motor_off) def check_retry_result(self, retry_result): - if not retry_result: + if (isinstance(retry_result, str) and retry_result == "done") or ( + isinstance(retry_result, (int, float)) and not retry_result + ): self.applied = True return retry_result diff --git a/klippy/extras/z_tilt_ng.py b/klippy/extras/z_tilt_ng.py index 2d19f5a90..ba0811b61 100644 --- a/klippy/extras/z_tilt_ng.py +++ b/klippy/extras/z_tilt_ng.py @@ -106,7 +106,9 @@ def __init__(self, printer): printer.register_event_handler("unhome:mark_as_unhomed_z", self._motor_off) def check_retry_result(self, retry_result): - if not retry_result: + if (isinstance(retry_result, str) and retry_result == "done") or ( + isinstance(retry_result, (int, float)) and not retry_result + ): self.applied = True return retry_result From bf46f4f6fa2ad998b6fb3da7fafabd66e607ad75 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 21 Aug 2024 13:54:47 +0200 Subject: [PATCH 047/158] Update stepper_enable.py --- klippy/extras/stepper_enable.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/klippy/extras/stepper_enable.py b/klippy/extras/stepper_enable.py index a90b810d7..9559a2291 100644 --- a/klippy/extras/stepper_enable.py +++ b/klippy/extras/stepper_enable.py @@ -66,14 +66,15 @@ def _resend_current_val(self, eventtime): self.resend_timer = None return self.reactor.NEVER - systime = self.reactor.monotonic() - print_time = self.mcu_enable.get_mcu().estimated_print_time(systime) + # systime = self.reactor.monotonic() + # print_time = self.mcu_enable.get_mcu().estimated_print_time(eventtime) + print_time = self.last_print_time + self.resend_interval time_diff = (self.last_print_time + self.resend_interval) - print_time if time_diff > 0.0: # Reschedule for resend time - return systime + time_diff + return eventtime + time_diff self._set_pin(print_time + PIN_MIN_TIME, self.last_value, True) - return systime + self.resend_interval + return eventtime + self.resend_interval def setup_enable_pin( From 7d9063fc482868ab4f4d837e090c41e992e89fef Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 21 Aug 2024 14:02:04 +0200 Subject: [PATCH 048/158] Update stepper_enable.py --- klippy/extras/stepper_enable.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/klippy/extras/stepper_enable.py b/klippy/extras/stepper_enable.py index 9559a2291..6a3772edc 100644 --- a/klippy/extras/stepper_enable.py +++ b/klippy/extras/stepper_enable.py @@ -67,8 +67,7 @@ def _resend_current_val(self, eventtime): return self.reactor.NEVER # systime = self.reactor.monotonic() - # print_time = self.mcu_enable.get_mcu().estimated_print_time(eventtime) - print_time = self.last_print_time + self.resend_interval + print_time = self.mcu_enable.get_mcu().estimated_print_time(eventtime) time_diff = (self.last_print_time + self.resend_interval) - print_time if time_diff > 0.0: # Reschedule for resend time From 31307aceba3df3e04ac4f6d75d9c9ad78a3a2c4d Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 21 Aug 2024 14:10:38 +0200 Subject: [PATCH 049/158] Update stepper_enable.py --- klippy/extras/stepper_enable.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/klippy/extras/stepper_enable.py b/klippy/extras/stepper_enable.py index 6a3772edc..9559a2291 100644 --- a/klippy/extras/stepper_enable.py +++ b/klippy/extras/stepper_enable.py @@ -67,7 +67,8 @@ def _resend_current_val(self, eventtime): return self.reactor.NEVER # systime = self.reactor.monotonic() - print_time = self.mcu_enable.get_mcu().estimated_print_time(eventtime) + # print_time = self.mcu_enable.get_mcu().estimated_print_time(eventtime) + print_time = self.last_print_time + self.resend_interval time_diff = (self.last_print_time + self.resend_interval) - print_time if time_diff > 0.0: # Reschedule for resend time From 43ced17d3419233127e78d575accfd73bd22097a Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 21 Aug 2024 14:15:10 +0200 Subject: [PATCH 050/158] Update tmc.py --- klippy/extras/tmc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/klippy/extras/tmc.py b/klippy/extras/tmc.py index 4d6b5a6ff..02143655a 100644 --- a/klippy/extras/tmc.py +++ b/klippy/extras/tmc.py @@ -250,8 +250,8 @@ def get_status(self, eventtime=None): return { "drv_status": None, "temperature": None, - "measured_min_temp": measured_min, - "measured_max_temp": measured_max, + "measured_min_temp": None, + "measured_max_temp": None, } temp = self.get_temperature() last_value, reg_name = self.drv_status_reg_info[:2] From 80b8492fc384b3642b7badfd771a85dca5c0cc01 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 21 Aug 2024 14:15:59 +0200 Subject: [PATCH 051/158] Update tmc.py --- klippy/extras/tmc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/klippy/extras/tmc.py b/klippy/extras/tmc.py index 02143655a..4d6b5a6ff 100644 --- a/klippy/extras/tmc.py +++ b/klippy/extras/tmc.py @@ -250,8 +250,8 @@ def get_status(self, eventtime=None): return { "drv_status": None, "temperature": None, - "measured_min_temp": None, - "measured_max_temp": None, + "measured_min_temp": measured_min, + "measured_max_temp": measured_max, } temp = self.get_temperature() last_value, reg_name = self.drv_status_reg_info[:2] From 394ee35db051b3b432510366c611eb9e95377722 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 21 Aug 2024 15:31:34 +0200 Subject: [PATCH 052/158] Update stepper_enable.py --- klippy/extras/stepper_enable.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/klippy/extras/stepper_enable.py b/klippy/extras/stepper_enable.py index 9559a2291..a90b810d7 100644 --- a/klippy/extras/stepper_enable.py +++ b/klippy/extras/stepper_enable.py @@ -66,15 +66,14 @@ def _resend_current_val(self, eventtime): self.resend_timer = None return self.reactor.NEVER - # systime = self.reactor.monotonic() - # print_time = self.mcu_enable.get_mcu().estimated_print_time(eventtime) - print_time = self.last_print_time + self.resend_interval + systime = self.reactor.monotonic() + print_time = self.mcu_enable.get_mcu().estimated_print_time(systime) time_diff = (self.last_print_time + self.resend_interval) - print_time if time_diff > 0.0: # Reschedule for resend time - return eventtime + time_diff + return systime + time_diff self._set_pin(print_time + PIN_MIN_TIME, self.last_value, True) - return eventtime + self.resend_interval + return systime + self.resend_interval def setup_enable_pin( From d88ce356894740cfe591748b6ac86c7014d8dd09 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 21 Aug 2024 15:47:16 +0200 Subject: [PATCH 053/158] Update fan.py --- klippy/extras/fan.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index cccbbfdc5..e6c71a8f0 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -207,6 +207,8 @@ def set_speed(self, print_time, value, force=False): def _set_speed( self, print_time, value, pwm_value, force=False, resend=False, eventtime=0.0 ): + if not resend: + eventtime = self.reactor.monotonic() if ( value == self.last_fan_value and pwm_value == self.last_pwm_value From 6feddaac533c9a64187525e24fd5e26a605d3639 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 21 Aug 2024 15:49:53 +0200 Subject: [PATCH 054/158] Update fan.py --- klippy/extras/fan.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index e6c71a8f0..fad74e804 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -27,6 +27,7 @@ def __init__(self, config, default_shutdown_speed=0.0): self.queued_pwm_value = None self.queued_force = False self.locking = False + self.resend_delay = None self.unlock_timer = self.reactor.register_timer(self._unlock_lock) # Read config self.kick_start_time = config.getfloat("kick_start_time", 0.1, minval=0.0) @@ -207,8 +208,6 @@ def set_speed(self, print_time, value, force=False): def _set_speed( self, print_time, value, pwm_value, force=False, resend=False, eventtime=0.0 ): - if not resend: - eventtime = self.reactor.monotonic() if ( value == self.last_fan_value and pwm_value == self.last_pwm_value @@ -237,7 +236,8 @@ def _set_speed( eventtime += self.kick_start_time self.mcu_fan.set_pwm(print_time, pwm_value) if not resend: - self.reactor.update_timer(self.unlock_timer, eventtime + FAN_MIN_TIME) + self.resend_delay = eventtime + FAN_MIN_TIME + self.reactor.update_timer(self.unlock_timer, self.reactor.NOW) self.last_fan_value = value self.last_pwm_value = pwm_value self.last_fan_time = print_time @@ -256,6 +256,10 @@ def _set_speed( return eventtime + FAN_MIN_TIME def _unlock_lock(self, eventtime): + if self.resend_delay is not None: + delay = self.resend_delay + self.resend_delay = None + return eventtime + delay if self.queued_pwm_value is not None: value = self.queued_value pwm_value = self.queued_pwm_value From 87df3f0e74a27a820d9e38cb95fb9f5a4eacab50 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 21 Aug 2024 15:54:17 +0200 Subject: [PATCH 055/158] Update fan.py --- klippy/extras/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index fad74e804..a55087e9d 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -273,7 +273,7 @@ def _unlock_lock(self, eventtime): or not self.queued_force ): return self._set_speed( - print_time=self.last_fan_time + FAN_MIN_TIME, + print_time=self.last_fan_time, value=value, pwm_value=pwm_value, force=force, From 6893f5d91d55a7c75d6ef13051cba8c15946ac80 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 21 Aug 2024 15:58:27 +0200 Subject: [PATCH 056/158] . --- klippy/extras/fan.py | 2 +- klippy/extras/stepper_enable.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index a55087e9d..fad74e804 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -273,7 +273,7 @@ def _unlock_lock(self, eventtime): or not self.queued_force ): return self._set_speed( - print_time=self.last_fan_time, + print_time=self.last_fan_time + FAN_MIN_TIME, value=value, pwm_value=pwm_value, force=force, diff --git a/klippy/extras/stepper_enable.py b/klippy/extras/stepper_enable.py index a90b810d7..def2ef208 100644 --- a/klippy/extras/stepper_enable.py +++ b/klippy/extras/stepper_enable.py @@ -32,6 +32,7 @@ def __init__( self.is_dedicated = True self.last_value = 0 self.resend_timer = None + self.resending = False self.resend_interval = resend_interval if max_enable_time else 0.0 self.last_print_time = 0.0 @@ -56,17 +57,21 @@ def _set_pin(self, print_time, value, is_resend=False): self.last_print_time = print_time self.mcu_enable.set_digital(print_time, value) if self.resend_interval and self.resend_timer is None: + self.resending = True self.resend_timer = self.reactor.register_timer( self._resend_current_val, self.reactor.NOW ) def _resend_current_val(self, eventtime): + systime = self.reactor.monotonic() + if self.resending: + self.resending = False + return systime + self.resend_interval if self.last_value == 0: self.reactor.unregister_timer(self.resend_timer) self.resend_timer = None return self.reactor.NEVER - systime = self.reactor.monotonic() print_time = self.mcu_enable.get_mcu().estimated_print_time(systime) time_diff = (self.last_print_time + self.resend_interval) - print_time if time_diff > 0.0: From 46ec9c867cf2592012abfa66b45af1158cf61a46 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 21 Aug 2024 16:03:00 +0200 Subject: [PATCH 057/158] Update stepper_enable.py --- klippy/extras/stepper_enable.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/klippy/extras/stepper_enable.py b/klippy/extras/stepper_enable.py index def2ef208..a09f1dcfa 100644 --- a/klippy/extras/stepper_enable.py +++ b/klippy/extras/stepper_enable.py @@ -32,7 +32,6 @@ def __init__( self.is_dedicated = True self.last_value = 0 self.resend_timer = None - self.resending = False self.resend_interval = resend_interval if max_enable_time else 0.0 self.last_print_time = 0.0 @@ -57,21 +56,21 @@ def _set_pin(self, print_time, value, is_resend=False): self.last_print_time = print_time self.mcu_enable.set_digital(print_time, value) if self.resend_interval and self.resend_timer is None: - self.resending = True + self.reactor.register_callback(self._init_resend, self.reactor.NOW) + + def _init_resend(self, eventtime): + if self.resend_interval and self.resend_timer is None: self.resend_timer = self.reactor.register_timer( - self._resend_current_val, self.reactor.NOW + self._resend_current_val, eventtime + self.resend_interval ) def _resend_current_val(self, eventtime): - systime = self.reactor.monotonic() - if self.resending: - self.resending = False - return systime + self.resend_interval if self.last_value == 0: self.reactor.unregister_timer(self.resend_timer) self.resend_timer = None return self.reactor.NEVER + systime = self.reactor.monotonic() print_time = self.mcu_enable.get_mcu().estimated_print_time(systime) time_diff = (self.last_print_time + self.resend_interval) - print_time if time_diff > 0.0: From 05532deae085926dc47554994cea6a34082db30d Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 21 Aug 2024 16:07:02 +0200 Subject: [PATCH 058/158] Update fan.py --- klippy/extras/fan.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index fad74e804..213e43dba 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -27,8 +27,7 @@ def __init__(self, config, default_shutdown_speed=0.0): self.queued_pwm_value = None self.queued_force = False self.locking = False - self.resend_delay = None - self.unlock_timer = self.reactor.register_timer(self._unlock_lock) + self.unlock_timer = None # Read config self.kick_start_time = config.getfloat("kick_start_time", 0.1, minval=0.0) self.kick_start_threshold = config.getfloat( @@ -235,9 +234,8 @@ def _set_speed( print_time += self.kick_start_time eventtime += self.kick_start_time self.mcu_fan.set_pwm(print_time, pwm_value) - if not resend: - self.resend_delay = eventtime + FAN_MIN_TIME - self.reactor.update_timer(self.unlock_timer, self.reactor.NOW) + if self.unlock_timer is None: + self.reactor.register_callback(self._init_unlock, self.reactor.NOW) self.last_fan_value = value self.last_pwm_value = pwm_value self.last_fan_time = print_time @@ -255,11 +253,13 @@ def _set_speed( self.fan_check_thread = None return eventtime + FAN_MIN_TIME + def _init_unlock(self, eventtime): + if self.unlock_timer is None: + self.unlock_timer = self.reactor.register_timer( + self._unlock_lock, eventtime + FAN_MIN_TIME + ) + def _unlock_lock(self, eventtime): - if self.resend_delay is not None: - delay = self.resend_delay - self.resend_delay = None - return eventtime + delay if self.queued_pwm_value is not None: value = self.queued_value pwm_value = self.queued_pwm_value @@ -281,6 +281,8 @@ def _unlock_lock(self, eventtime): eventtime=eventtime, ) self.locking = False + self.reactor.unregister_timer(self.unlock_timer) + self.unlock_timer = None return self.reactor.NEVER def set_speed_from_command(self, value, force=False): From 87dd9a2e2ecc59d1dc9e54b466cc8f04fb127f17 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 21 Aug 2024 16:11:22 +0200 Subject: [PATCH 059/158] Update fan.py --- klippy/extras/fan.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 213e43dba..84d8b3837 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -205,7 +205,7 @@ def set_speed(self, print_time, value, force=False): ) def _set_speed( - self, print_time, value, pwm_value, force=False, resend=False, eventtime=0.0 + self, print_time, value, pwm_value, force=False, eventtime=0.0 ): if ( value == self.last_fan_value @@ -277,7 +277,6 @@ def _unlock_lock(self, eventtime): value=value, pwm_value=pwm_value, force=force, - resend=True, eventtime=eventtime, ) self.locking = False From 4f5709d2f1f6c5dbbad09e44d3d9f079dfbb1b58 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 21 Aug 2024 16:11:52 +0200 Subject: [PATCH 060/158] Update fan.py --- klippy/extras/fan.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 84d8b3837..fad74e804 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -27,7 +27,8 @@ def __init__(self, config, default_shutdown_speed=0.0): self.queued_pwm_value = None self.queued_force = False self.locking = False - self.unlock_timer = None + self.resend_delay = None + self.unlock_timer = self.reactor.register_timer(self._unlock_lock) # Read config self.kick_start_time = config.getfloat("kick_start_time", 0.1, minval=0.0) self.kick_start_threshold = config.getfloat( @@ -205,7 +206,7 @@ def set_speed(self, print_time, value, force=False): ) def _set_speed( - self, print_time, value, pwm_value, force=False, eventtime=0.0 + self, print_time, value, pwm_value, force=False, resend=False, eventtime=0.0 ): if ( value == self.last_fan_value @@ -234,8 +235,9 @@ def _set_speed( print_time += self.kick_start_time eventtime += self.kick_start_time self.mcu_fan.set_pwm(print_time, pwm_value) - if self.unlock_timer is None: - self.reactor.register_callback(self._init_unlock, self.reactor.NOW) + if not resend: + self.resend_delay = eventtime + FAN_MIN_TIME + self.reactor.update_timer(self.unlock_timer, self.reactor.NOW) self.last_fan_value = value self.last_pwm_value = pwm_value self.last_fan_time = print_time @@ -253,13 +255,11 @@ def _set_speed( self.fan_check_thread = None return eventtime + FAN_MIN_TIME - def _init_unlock(self, eventtime): - if self.unlock_timer is None: - self.unlock_timer = self.reactor.register_timer( - self._unlock_lock, eventtime + FAN_MIN_TIME - ) - def _unlock_lock(self, eventtime): + if self.resend_delay is not None: + delay = self.resend_delay + self.resend_delay = None + return eventtime + delay if self.queued_pwm_value is not None: value = self.queued_value pwm_value = self.queued_pwm_value @@ -277,11 +277,10 @@ def _unlock_lock(self, eventtime): value=value, pwm_value=pwm_value, force=force, + resend=True, eventtime=eventtime, ) self.locking = False - self.reactor.unregister_timer(self.unlock_timer) - self.unlock_timer = None return self.reactor.NEVER def set_speed_from_command(self, value, force=False): From 1bee99a5ecb74a10f0b80278ddca46f5e1b7ac6d Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 21 Aug 2024 16:54:34 +0200 Subject: [PATCH 061/158] . --- klippy/extras/fan.py | 14 ++++---------- klippy/extras/stepper_enable.py | 7 ++----- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index fad74e804..23fa5d183 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -4,8 +4,6 @@ # # This file may be distributed under the terms of the GNU GPLv3 license. import logging -import threading -import time from . import pulse_counter @@ -27,7 +25,6 @@ def __init__(self, config, default_shutdown_speed=0.0): self.queued_pwm_value = None self.queued_force = False self.locking = False - self.resend_delay = None self.unlock_timer = self.reactor.register_timer(self._unlock_lock) # Read config self.kick_start_time = config.getfloat("kick_start_time", 0.1, minval=0.0) @@ -206,8 +203,10 @@ def set_speed(self, print_time, value, force=False): ) def _set_speed( - self, print_time, value, pwm_value, force=False, resend=False, eventtime=0.0 + self, print_time, value, pwm_value, force=False, resend=False, eventtime=None ): + if eventtime is None: + eventtime = self.reactor.monotonic() if ( value == self.last_fan_value and pwm_value == self.last_pwm_value @@ -236,8 +235,7 @@ def _set_speed( eventtime += self.kick_start_time self.mcu_fan.set_pwm(print_time, pwm_value) if not resend: - self.resend_delay = eventtime + FAN_MIN_TIME - self.reactor.update_timer(self.unlock_timer, self.reactor.NOW) + self.reactor.update_timer(self.unlock_timer, eventtime + FAN_MIN_TIME) self.last_fan_value = value self.last_pwm_value = pwm_value self.last_fan_time = print_time @@ -256,10 +254,6 @@ def _set_speed( return eventtime + FAN_MIN_TIME def _unlock_lock(self, eventtime): - if self.resend_delay is not None: - delay = self.resend_delay - self.resend_delay = None - return eventtime + delay if self.queued_pwm_value is not None: value = self.queued_value pwm_value = self.queued_pwm_value diff --git a/klippy/extras/stepper_enable.py b/klippy/extras/stepper_enable.py index a09f1dcfa..f452b85fc 100644 --- a/klippy/extras/stepper_enable.py +++ b/klippy/extras/stepper_enable.py @@ -55,13 +55,10 @@ def _set_pin(self, print_time, value, is_resend=False): self.last_value = value self.last_print_time = print_time self.mcu_enable.set_digital(print_time, value) - if self.resend_interval and self.resend_timer is None: - self.reactor.register_callback(self._init_resend, self.reactor.NOW) - - def _init_resend(self, eventtime): if self.resend_interval and self.resend_timer is None: self.resend_timer = self.reactor.register_timer( - self._resend_current_val, eventtime + self.resend_interval + self._resend_current_val, + self.reactor.monotonic() + self.resend_interval, ) def _resend_current_val(self, eventtime): From 436ad6e132509fad4280fb5895f7e9a2f3104d94 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Thu, 22 Aug 2024 13:40:33 +0200 Subject: [PATCH 062/158] Update fan.py --- klippy/extras/fan.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 23fa5d183..9349649c3 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -70,13 +70,13 @@ def __init__(self, config, default_shutdown_speed=0.0): self.mcu_fan.setup_cycle_time(cycle_time, hardware_pwm) if hardware_pwm: - shutdown_power = max(0.0, min(self.max_power, shutdown_speed)) + self.shutdown_power = max(0.0, min(self.max_power, shutdown_speed)) else: # the config allows shutdown_power to be > 0 and < 1, but it is validated # in MCU_pwm._build_config(). - shutdown_power = max(0.0, shutdown_speed) + self.shutdown_power = max(0.0, shutdown_speed) - self.mcu_fan.setup_start_value(0.0, shutdown_power) + self.mcu_fan.setup_start_value(0.0, self.shutdown_power) self.enable_pin = None enable_pin = config.get("enable_pin", None) if enable_pin is not None: @@ -284,7 +284,7 @@ def set_speed_from_command(self, value, force=False): ) def _handle_request_restart(self, print_time): - self.set_speed(print_time, 0.0) + self.set_speed(print_time, self.shutdown_power) def get_status(self, eventtime): tachometer_status = self.tachometer.get_status(eventtime) From f227149ec09dcf12ce4430d6084e15cfb73969de Mon Sep 17 00:00:00 2001 From: Zeanon Date: Thu, 22 Aug 2024 18:32:05 +0200 Subject: [PATCH 063/158] Update mpc_calibrate.py --- klippy/extras/mpc_calibrate.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/klippy/extras/mpc_calibrate.py b/klippy/extras/mpc_calibrate.py index 7bc337c12..1e374e3d4 100644 --- a/klippy/extras/mpc_calibrate.py +++ b/klippy/extras/mpc_calibrate.py @@ -224,9 +224,7 @@ def process(eventtime): self.printer.wait_while(process) self.heater.alter_target(0.0) - return self.orig_control.ambient_sensor.get_temp( - self.heater.reactor.monotonic() - )[0] + return ambient_sensor.get_temp(self.heater.reactor.monotonic())[0] gcmd.respond_info("Waiting for heater to settle at ambient temperature") ambient_temp = self.wait_settle(0.01) From 0c28cca62689292f4481ed90d3436b625748670e Mon Sep 17 00:00:00 2001 From: Zeanon Date: Thu, 22 Aug 2024 18:41:53 +0200 Subject: [PATCH 064/158] Update heaters.py --- klippy/extras/heaters.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/klippy/extras/heaters.py b/klippy/extras/heaters.py index 11f64c319..a192cef6d 100644 --- a/klippy/extras/heaters.py +++ b/klippy/extras/heaters.py @@ -772,14 +772,14 @@ def __init__(self, profile, heater, load_clean=False): self.Kp = profile["pid_kp"] / PID_PARAM_BASE self.Ki = profile["pid_ki"] / PID_PARAM_BASE self.Kd = profile["pid_kd"] / PID_PARAM_BASE + self.dt = heater.pwm_delay smooth_time = ( self.heater.get_smooth_time() if profile["smooth_time"] is None else profile["smooth_time"] ) - self.dt = heater.pwm_delay - self.smooth_time = smooth_time self.heater.set_inv_smooth_time(1.0 / smooth_time) + self.smooth_time = smooth_time # smoothing window self.prev_temp_time = 0.0 if load_clean else self.heater.reactor.monotonic() self.prev_temp = ( AMBIENT_TEMP if load_clean else self.heater.get_temp(self.prev_temp_time)[0] @@ -837,7 +837,7 @@ def check_busy(self, eventtime, smoothed_temp, target_temp): ) def update_smooth_time(self): - self.smooth = 1.0 + self.heater.get_smooth_time() / self.dt + self.smooth_time = self.heater.get_smooth_time() # smoothing window def set_pid_kp(self, kp): self.Kp = kp / PID_PARAM_BASE From 362dc53bf19cecf231ef99b0777ff0ef0285946b Mon Sep 17 00:00:00 2001 From: Zeanon Date: Thu, 22 Aug 2024 20:43:47 +0200 Subject: [PATCH 065/158] . --- klippy/extras/temperature_combined.py | 10 +++++++--- klippy/extras/z_thermal_adjust.py | 3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/klippy/extras/temperature_combined.py b/klippy/extras/temperature_combined.py index 9f6d4e962..1fb49ffcc 100644 --- a/klippy/extras/temperature_combined.py +++ b/klippy/extras/temperature_combined.py @@ -21,8 +21,12 @@ def __init__(self, config): # get sensor names self.sensor_names = config.getlist("sensor_list") # get maximum_deviation parameter from config - self.max_deviation = config.getfloat("maximum_deviation", above=0.0) - self.ignore = self.name in get_danger_options().temp_ignore_limits + self.max_deviation = config.getfloat( + "maximum_deviation", default=None, above=0.0 + ) + self.ignore = ( + self.name in get_danger_options().temp_ignore_limits + ) or self.max_deviation is None # ensure compatibility with itself self.sensor = self # get empty list for sensors, could be any sensor class or a heater @@ -91,7 +95,7 @@ def update_temp(self, eventtime): if values: # check if values are out of max_deviation range - if (max(values) - min(values)) > self.max_deviation and not self.ignore: + if not self.ignore and (max(values) - min(values)) > self.max_deviation: self.printer.invoke_shutdown( "COMBINED SENSOR maximum deviation exceeded limit of %0.1f, " "max sensor value %0.1f, min sensor value %0.1f." diff --git a/klippy/extras/z_thermal_adjust.py b/klippy/extras/z_thermal_adjust.py index c16952a97..c95f017a1 100644 --- a/klippy/extras/z_thermal_adjust.py +++ b/klippy/extras/z_thermal_adjust.py @@ -42,7 +42,8 @@ def __init__(self, config): pheaters.register_sensor(config, self) self.last_temp = 0.0 - self.measured_min = self.measured_max = 0.0 + self.measured_min = 99999999.0 + self.measured_max = -99999999.0 self.smoothed_temp = 0.0 self.last_temp_time = 0.0 self.ref_temperature = 0.0 From 50cdf38ab1f86bf911478aa0e4d1f5332f140ce5 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Fri, 23 Aug 2024 20:15:36 +0200 Subject: [PATCH 066/158] Update fan.py --- klippy/extras/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 9349649c3..6dcb43f44 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -284,7 +284,7 @@ def set_speed_from_command(self, value, force=False): ) def _handle_request_restart(self, print_time): - self.set_speed(print_time, self.shutdown_power) + self.set_speed(print_time, 0.0) def get_status(self, eventtime): tachometer_status = self.tachometer.get_status(eventtime) From 601863335ce81c239cb6b044aa07a2e75593e8ab Mon Sep 17 00:00:00 2001 From: Zeanon Date: Fri, 23 Aug 2024 20:23:50 +0200 Subject: [PATCH 067/158] Update fan.py --- klippy/extras/fan.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 6dcb43f44..ab8da208c 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -284,7 +284,8 @@ def set_speed_from_command(self, value, force=False): ) def _handle_request_restart(self, print_time): - self.set_speed(print_time, 0.0) + self.reactor.update_timer(self.unlock_timer, self.reactor.NEVER) + self.set_speed(print_time, self.shutdown_power) def get_status(self, eventtime): tachometer_status = self.tachometer.get_status(eventtime) From 4db938ab0473ed6b2aa28500659411fb08c047af Mon Sep 17 00:00:00 2001 From: Zeanon Date: Fri, 23 Aug 2024 20:26:39 +0200 Subject: [PATCH 068/158] Update fan.py --- klippy/extras/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index ab8da208c..9524265ef 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -285,7 +285,7 @@ def set_speed_from_command(self, value, force=False): def _handle_request_restart(self, print_time): self.reactor.update_timer(self.unlock_timer, self.reactor.NEVER) - self.set_speed(print_time, self.shutdown_power) + self.mcu_fan.set_pwm(print_time, self.shutdown_power) def get_status(self, eventtime): tachometer_status = self.tachometer.get_status(eventtime) From 1c628070ecd799fd8477a67d74179d0fe8fe6267 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Fri, 23 Aug 2024 20:27:38 +0200 Subject: [PATCH 069/158] Update fan.py --- klippy/extras/fan.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 9524265ef..fc3d6e9e3 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -254,7 +254,7 @@ def _set_speed( return eventtime + FAN_MIN_TIME def _unlock_lock(self, eventtime): - if self.queued_pwm_value is not None: + if self.queued_value is not None: value = self.queued_value pwm_value = self.queued_pwm_value force = self.queued_force @@ -285,6 +285,7 @@ def set_speed_from_command(self, value, force=False): def _handle_request_restart(self, print_time): self.reactor.update_timer(self.unlock_timer, self.reactor.NEVER) + self.queued_value = None self.mcu_fan.set_pwm(print_time, self.shutdown_power) def get_status(self, eventtime): From 04f6f7b9349de4ae6c6e78e3dd91a41e20475db3 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sat, 24 Aug 2024 11:05:27 +0200 Subject: [PATCH 070/158] . --- klippy/extras/gcode_shell_command.py | 1 - klippy/extras/led_interpolate.py | 87 ++++++++++++++++++++++++++++ klippy/extras/loop_macro.py | 69 ++++++++++++++++++++++ klippy/extras/state_notify.py | 21 ++++--- 4 files changed, 166 insertions(+), 12 deletions(-) create mode 100644 klippy/extras/led_interpolate.py create mode 100644 klippy/extras/loop_macro.py diff --git a/klippy/extras/gcode_shell_command.py b/klippy/extras/gcode_shell_command.py index 25b93d7d4..ca195bc38 100644 --- a/klippy/extras/gcode_shell_command.py +++ b/klippy/extras/gcode_shell_command.py @@ -83,7 +83,6 @@ def cmd_RUN_SHELL_COMMAND(self, params): gcode_params = shlex.split(gcode_params) reactor = self.printer.get_reactor() try: - logging.info("%s", self.command + gcode_params) proc = subprocess.Popen( self.command + gcode_params, stdout=subprocess.PIPE, diff --git a/klippy/extras/led_interpolate.py b/klippy/extras/led_interpolate.py new file mode 100644 index 000000000..f6087b633 --- /dev/null +++ b/klippy/extras/led_interpolate.py @@ -0,0 +1,87 @@ +# Smoothly transition RGB LEDs between two colors +# +# Copyright (C) 2022-2023 Mitko Haralanov +# +# This file may be distributed under the terms of the GNU GPLv3 license. +import logging + +FRAME_RATE = 24 +INTERPOLATE_STEP_TIME = 1.0 / FRAME_RATE + + +class LedInterpolate: + def __init__(self, config): + self.printer = config.get_printer() + self.gcode = self.printer.lookup_object("gcode") + self.led = self.printer.load_object(config, "led") + self.gcode.register_command( + "LED_INTERPOLATE", + self.cmd_LED_INTERPOLATE, + False, + desc=self.cmd_LED_INTERPOLATE_help, + ) + + def compute_color(self, start, end, factor): + return round(((end - start) * factor) + start, 5) + + cmd_LED_INTERPOLATE_help = "Smootly transition LEDs between two colors" + + def _interpolate_leds(self, eventtime): + all_done = False + reactor = self.printer.get_reactor() + led_count = self.led_helper.get_led_count() + for index in range(led_count): + current_state = [round(c * 255) for c in self.led_helper.led_state[index]] + + if current_state == self.interpolation_params[0]: + all_done = True + continue + + target_colors, factor = self.interpolation_params + colors = self.current_colors[index] + red = self.compute_color(colors[0], target_colors[0], factor) + green = self.compute_color(colors[1], target_colors[1], factor) + blue = self.compute_color(colors[2], target_colors[2], factor) + white = self.compute_color(colors[3], target_colors[3], factor) + + self.led_helper.set_color( + index, + ( + round(red / 255, 2), + round(green / 255, 2), + round(blue / 255, 2), + round(white / 255, 2), + ), + ) + self.current_colors[index] = [red, green, blue, white] + all_done = False + self.led_helper.check_transmit(None) + if all_done or self.printer.is_shutdown(): + return reactor.NEVER + return reactor.monotonic() + INTERPOLATE_STEP_TIME + + def cmd_LED_INTERPOLATE(self, gcmd): + target_name = gcmd.get("LED") + target_colors = [ + round(gcmd.get_float("RED", 0.0, minval=0.0, maxval=1.0) * 255), + round(gcmd.get_float("GREEN", 0.0, minval=0.0, maxval=1.0) * 255), + round(gcmd.get_float("BLUE", 0.0, minval=0.0, maxval=1.0) * 255), + round(gcmd.get_float("WHITE", 0.0, minval=0.0, maxval=1.0) * 255), + ] + runtime = gcmd.get_float("DURATION", 0.0) + + factor = (100 / (runtime / INTERPOLATE_STEP_TIME)) / 100 + if target_name not in self.led.led_helpers: + raise gcmd.error("Unknown LED object '%s'" % target_name) + + self.led_helper = self.led.led_helpers[target_name] + self.interpolation_params = (target_colors, factor) + self.current_colors = [ + [round(c * 255, 2) for c in led] for led in self.led_helper.led_state + ] + reactor = self.printer.get_reactor() + self.timer = reactor.register_timer(self._interpolate_leds, reactor.NOW) + + +def load_config(config): + return LedInterpolate(config) diff --git a/klippy/extras/loop_macro.py b/klippy/extras/loop_macro.py new file mode 100644 index 000000000..75f243f1b --- /dev/null +++ b/klippy/extras/loop_macro.py @@ -0,0 +1,69 @@ +import logging +from extras.gcode_macro import GCodeMacro + + +def log(fmt, *args): + logging.info("loop_macro: " + fmt % args) + + +class LoopMacro(GCodeMacro): + def __init__(self, config): + name = config.get_name().split()[1] + self.log = lambda fmt, *args: log(f"[{name}]: " + fmt, *args) + GCodeMacro.__init__(self, config) + self.gcode = self.printer.lookup_object("gcode") + macro_obj = self.printer.load_object(config, "gcode_macro") + self.entry_template = macro_obj.load_template(config, "entry", "") + self.exit_template = macro_obj.load_template(config, "exit", "") + self.iteration_limit = config.getint("iteration_limit", 0) + + def _create_context(self, gcmd, template): + context = dict(self.variables) + context.update(template.create_template_context()) + context["params"] = gcmd.get_command_parameters() + context["rawparams"] = gcmd.get_raw_command_parameters() + return context + + def cmd(self, gcmd): + if self.printer.is_shutdown(): + return + + limit = gcmd.get_int("LIMIT", None) + if limit is None: + limit = self.iteration_limit + + # LIMIT is a special argument that is provided by the + # implementation. So, reach into the GCode command + # parameters and remove it. + gcmd._params.pop("LIMIT", None) + parts = gcmd._commandline.split() + parts = [x for x in parts if not x.startswith("LIMIT")] + gcmd._commandline = " ".join(parts) + + self.variables["iter"] = 0 + self.variables["limit"] = limit + + context = self._create_context(gcmd, self.entry_template) + self.entry_template.run_gcode_from_command(context) + + stop_execution = False + while not self.printer.is_shutdown() and not stop_execution: + context = self._create_context(gcmd, self.template) + script = self.template.render(context) + for gcode in script.split("\n"): + self.log("Running GCode: '%s'", gcode) + if gcode.lower() in ("continue", "break"): + if gcode.lower() == "break": + stop_execution = True + break + self.gcode.run_script_from_command(gcode) + self.variables["iter"] += 1 + if limit > 0 and self.variables["iter"] >= limit: + break + + context = self._create_context(gcmd, self.exit_template) + self.exit_template.run_gcode_from_command(context) + + +def load_config_prefix(config): + return LoopMacro(config) diff --git a/klippy/extras/state_notify.py b/klippy/extras/state_notify.py index 3a8d8c29a..d27dd912d 100644 --- a/klippy/extras/state_notify.py +++ b/klippy/extras/state_notify.py @@ -48,7 +48,7 @@ def __init__(self, config): desc=self.cmd_STATE_NOTIFY_STATE_help, ) self.printer.register_event_handler( - "klippy:ready", lambda: self._klippy_handler("ready") + "klippy:mcu_identify", self._register_ready_handler ) self.printer.register_event_handler( "klippy:shutdown", lambda: self._klippy_handler("shutdown") @@ -57,6 +57,11 @@ def __init__(self, config): "klippy:disconnect", lambda: self._klippy_handler("disconnect") ) + def _register_ready_handler(self): + self.printer.register_event_handler( + "klippy:ready", lambda: self._klippy_handler("ready") + ) + def _klippy_handler(self, state): self.handle_state_change(state, self.reactor.monotonic()) if state == "ready": @@ -69,12 +74,10 @@ def _klippy_handler(self, state): self.print_stats = self.printer.lookup_object("print_stats") self.pheaters = self.printer.lookup_object("heaters") self.printer.register_event_handler( - "idle_timeout:idle", - lambda e: self._state_handler("idle_idle", e), + "idle_timeout:idle", lambda e: self._state_handler("idle_idle", e) ) self.printer.register_event_handler( - "idle_timeout:ready", - lambda e: self._state_handler("idle_ready", e), + "idle_timeout:ready", lambda e: self._state_handler("idle_ready", e) ) self.printer.register_event_handler( "idle_timeout:printing", @@ -111,10 +114,7 @@ def _klippy_handler(self, state): + self.idle_gcode ) idle_timeout.idle_gcode = TemplateWrapper( - self.printer, - self.gcode_macro.env, - "idle_timeout:gcode", - idle_gcode, + self.printer, self.gcode_macro.env, "idle_timeout:gcode", idle_gcode ) # Handle a corner case in which we may miss a transition to "active" @@ -177,8 +177,7 @@ def _state_handler(self, state, eventtime): self.reactor.update_timer(self.inactive_timer, self.reactor.NEVER) if state == "menu_begin": self.reactor.update_timer( - self.menu_check_timer, - self.reactor.monotonic() + TIMER_DURATION, + self.menu_check_timer, self.reactor.monotonic() + TIMER_DURATION ) state = "active" self.reactor.update_timer(self.pause_timer, self.reactor.NEVER) From 0a9468e3ab13d486e785eee55d30e65c8430a9bc Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sat, 24 Aug 2024 11:10:28 +0200 Subject: [PATCH 071/158] Update filament_switch_sensor.py --- klippy/extras/filament_switch_sensor.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/klippy/extras/filament_switch_sensor.py b/klippy/extras/filament_switch_sensor.py index 950624c1a..348f63fbe 100644 --- a/klippy/extras/filament_switch_sensor.py +++ b/klippy/extras/filament_switch_sensor.py @@ -43,7 +43,7 @@ def __init__(self, config, defined_sensor, runout_distance=0): # Internal state self.min_event_systime = self.reactor.NEVER self.filament_present = False - self.sensor_enabled = True + self.sensor_enabled = config.getboolean("enabled_on_start", True) self.smart = config.getboolean("smart", False) self.always_fire_events = config.getboolean("always_fire_events", False) self.runout_position = 0.0 @@ -68,15 +68,15 @@ def __init__(self, config, defined_sensor, runout_distance=0): def _handle_ready(self): self.min_event_systime = self.reactor.monotonic() + 2.0 - # if ( - # self.check_runout_timeout is not None - # and not get_danger_options().modify_check_runout_timeout - # ): - # raise self.config.error( - # "'modify_check_runout_timeout' is not enabled in 'danger_options'" - # ) - # if self.check_runout_timeout is None: - # self.check_runout_timeout = CHECK_RUNOUT_TIMEOUT + if ( + self.check_runout_timeout is not None + and not get_danger_options().modify_check_runout_timeout + ): + raise self.config.error( + "'modify_check_runout_timeout' is not enabled in 'danger_options'" + ) + if self.check_runout_timeout is None: + self.check_runout_timeout = CHECK_RUNOUT_TIMEOUT def _runout_event_handler(self, eventtime): if self.immediate_runout_gcode is not None: From c8fab95d9d9ecc8e9a4f5dbc14c37f953942c30c Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sat, 24 Aug 2024 11:26:44 +0200 Subject: [PATCH 072/158] Update filament_switch_sensor.py --- klippy/extras/filament_switch_sensor.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/klippy/extras/filament_switch_sensor.py b/klippy/extras/filament_switch_sensor.py index 348f63fbe..c3ad693fc 100644 --- a/klippy/extras/filament_switch_sensor.py +++ b/klippy/extras/filament_switch_sensor.py @@ -31,9 +31,6 @@ def __init__(self, config, defined_sensor, runout_distance=0): self.insert_gcode = gcode_macro.load_template(config, "insert_gcode") self.pause_delay = config.getfloat("pause_delay", 0.5, minval=0.0) self.event_delay = config.getfloat("event_delay", 3.0, minval=0.0) - # self.check_runout_timeout = self.config.getfloat( - # "check_runout_timeout", None, above=0 - # ) self.check_runout_timeout = CHECK_RUNOUT_TIMEOUT if get_danger_options().modify_check_runout_timeout: self.check_runout_timeout = self.config.getfloat( @@ -68,15 +65,15 @@ def __init__(self, config, defined_sensor, runout_distance=0): def _handle_ready(self): self.min_event_systime = self.reactor.monotonic() + 2.0 - if ( - self.check_runout_timeout is not None - and not get_danger_options().modify_check_runout_timeout - ): - raise self.config.error( - "'modify_check_runout_timeout' is not enabled in 'danger_options'" - ) - if self.check_runout_timeout is None: - self.check_runout_timeout = CHECK_RUNOUT_TIMEOUT + # if ( + # self.check_runout_timeout is not None + # and not get_danger_options().modify_check_runout_timeout + # ): + # raise self.config.error( + # "'modify_check_runout_timeout' is not enabled in 'danger_options'" + # ) + # if self.check_runout_timeout is None: + # self.check_runout_timeout = CHECK_RUNOUT_TIMEOUT def _runout_event_handler(self, eventtime): if self.immediate_runout_gcode is not None: From 962b2d13255895db21937a161d12a8674830f0d2 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 25 Aug 2024 17:49:20 +0200 Subject: [PATCH 073/158] . --- klippy/extras/input_shaper.py | 38 ++++++++++++++++++++++++++--------- klippy/extras/shaper_defs.py | 1 + 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/klippy/extras/input_shaper.py b/klippy/extras/input_shaper.py index f9afd81ac..6348fc9d1 100644 --- a/klippy/extras/input_shaper.py +++ b/klippy/extras/input_shaper.py @@ -31,25 +31,45 @@ def __init__(self, axis, shaper_type, config): self.shaper_type = shaper_type self.damping_ratio = shaper_defs.DEFAULT_DAMPING_RATIO self.shaper_freq = 0.0 + self.motor_damping = shaper_defs.DEFAULT_MOTOR_DAMPING_RATIO + self.motor_freq = 0.0 if config is not None: if shaper_type not in self.shapers: raise config.error("Unsupported shaper type: %s" % (shaper_type,)) + global_damping_ratio = config.getfloat( + "damping_ratio", + self.damping_ratio, + minval=0.0, + maxval=1.0, + ) self.damping_ratio = config.getfloat( "damping_ratio_" + axis, - self.damping_ratio, + global_damping_ratio, minval=0.0, maxval=1.0, ) + global_shaper_freq = config.getfloat( + "shaper_freq", self.shaper_freq, minval=0.0 + ) self.shaper_freq = config.getfloat( - "shaper_freq_" + axis, self.shaper_freq, minval=0.0 + "shaper_freq_" + axis, global_shaper_freq, minval=0.0 + ) + + global_motor_damping = config.getfloat( + "motor_damping_ratio", self.motor_damping, minval=0.0, maxval=1.0 + ) + self.motor_damping = config.getfloat( + "motor_damping_ratio_" + axis, + global_motor_damping, + minval=0.0, + maxval=1.0, + ) + global_motor_freq = config.getfloat( + "motor_freq", self.motor_freq, minval=0.0 + ) + self.motor_freq = config.getfloat( + "motor_freq_" + axis, global_motor_freq, minval=0.0 ) - self.motor_damping = config.getfloat( - "motor_damping_" + axis, - shaper_defs.DEFAULT_DAMPING_RATIO, - minval=0.0, - maxval=1.0, - ) - self.motor_freq = config.getfloat("motor_freq_" + axis, 0.0, minval=0.0) def get_type(self): return self.shaper_type diff --git a/klippy/extras/shaper_defs.py b/klippy/extras/shaper_defs.py index 258d89471..36e8da046 100644 --- a/klippy/extras/shaper_defs.py +++ b/klippy/extras/shaper_defs.py @@ -7,6 +7,7 @@ SHAPER_VIBRATION_REDUCTION = 20.0 DEFAULT_DAMPING_RATIO = 0.1 +DEFAULT_MOTOR_DAMPING_RATIO = 0.05 InputShaperCfg = collections.namedtuple( "InputShaperCfg", ("name", "init_func", "min_freq") From c8d0a9bd543679b3de43bcdf0886a49235c5f66e Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 28 Aug 2024 16:24:47 +0200 Subject: [PATCH 074/158] Update mpc_calibrate.py --- klippy/extras/mpc_calibrate.py | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/klippy/extras/mpc_calibrate.py b/klippy/extras/mpc_calibrate.py index 1e374e3d4..ca9035f46 100644 --- a/klippy/extras/mpc_calibrate.py +++ b/klippy/extras/mpc_calibrate.py @@ -36,6 +36,11 @@ def __init__(self, printer, heater, config): self.pmgr = heater.pmgr self.orig_control = heater.get_control() self.ambient_sensor_name = self.config.get("ambient_temp_sensor", None) + self.max_error = self.config.getfloat("calibrate_max_error", None) + self.check_gain_time = self.config.getfloat("calibrate_check_gain_time", None) + self.hysteresis = self.config.getfloat("calibrate_hysteresis", None) + self.heating_gain = self.config.getfloat("calibrate_heating_gain", None) + def run(self, gcmd): profile_name = gcmd.get("PROFILE", "default") @@ -60,6 +65,28 @@ def run(self, gcmd): raise self.config.error( f"Unknown ambient_temp_sensor '{ambient_sensor_name}' " f"specified" ) + max_error = gcmd.get_float("MAX_ERROR", self.max_error) + check_gain_time = gcmd.get_float("CHECK_GAIN_TIME", self.check_gain_time) + hysteresis = gcmd.get_float("HYSTERESIS", self.hysteresis) + heating_gain = gcmd.get_float("HEATING_GAIN", self.heating_gain) + + verify_heater = self.printer.lookup_object("VERIFY_HEATER %s" % self.heater.short_name, None) + old_max_error = None + old_check_gain_time = None + old_hysteresis = None + old_heating_gain = None + if max_error is not None: + old_max_error = verify_heater.max_error + verify_heater.max_error = max_error + if check_gain_time is not None: + old_check_gain_time = verify_heater.check_gain_time + verify_heater.check_gain_time = check_gain_time + if hysteresis is not None: + old_hysteresis = verify_heater.hysteresis + verify_heater.hysteresis = hysteresis + if heating_gain is not None: + old_heating_gain = verify_heater.heating_gain + verify_heater.heating_gain = heating_gain control = TuningControl(self.heater) old_control = self.heater.set_control(control) @@ -145,6 +172,14 @@ def run(self, gcmd): except self.printer.command_error as e: raise gcmd.error("%s failed: %s" % (gcmd.get_command(), e)) finally: + if old_max_error is not None: + verify_heater.max_error = old_max_error + if old_check_gain_time is not None: + verify_heater.check_gain_time = old_check_gain_time + if old_hysteresis is not None: + verify_heater.hysteresis = old_hysteresis + if old_heating_gain is not None: + verify_heater.heating_gain = old_heating_gain self.heater.set_control(old_control) self.heater.alter_target(0.0) From 3c9495fc88cc7b0bcebc91495d91993032b05066 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 28 Aug 2024 16:56:48 +0200 Subject: [PATCH 075/158] Update heaters.py --- klippy/extras/heaters.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/klippy/extras/heaters.py b/klippy/extras/heaters.py index a192cef6d..309a2a383 100644 --- a/klippy/extras/heaters.py +++ b/klippy/extras/heaters.py @@ -106,6 +106,10 @@ def __init__(self, config, sensor, sensor_config=None): sensor_config.getfloat("filament_density", None) sensor_config.getfloat("filament_heat_capacity", None) sensor_config.get("ambient_temp_sensor", None) + sensor_config.getfloat("calibrate_max_error", None) + sensor_config.getfloat("calibrate_check_gain_time", None) + sensor_config.getfloat("calibrate_hysteresis", None) + sensor_config.getfloat("calibrate_heating_gain", None) sensor_config.get("cooling_fan", None) sensor_config.getfloatlist("fan_ambient_transfer", None) # Setup output heater pin From 89935d2f51b9b3ada0c30e001be8dcba78b728e0 Mon Sep 17 00:00:00 2001 From: fbeaukmi Date: Thu, 29 Aug 2024 00:14:01 +0200 Subject: [PATCH 076/158] Doc : add sensor_pin to belay config reference --- docs/Config_Reference.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index 742d31094..622807442 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -5224,6 +5224,9 @@ extruder_stepper_name: # example, if the config section for the secondary extruder is # [extruder_stepper my_extruder_stepper], this parameter's value # would be 'my_extruder_stepper'. +sensor_pin: +# Input pin connected to the sensor. This parameter must be +# provided. #multiplier_high: 1.05 # High multiplier to set for the secondary extruder when extruding # forward and Belay is compressed or when extruding backward and From cc07c150333be3caa65e70d53f7aa78a1423825c Mon Sep 17 00:00:00 2001 From: Zeanon Date: Thu, 29 Aug 2024 13:15:12 +0200 Subject: [PATCH 077/158] . --- docs/Config_Reference.md | 7 +++++++ klippy/extras/mpc_calibrate.py | 5 +++-- klippy/extras/z_tilt.py | 2 +- klippy/extras/z_tilt_ng.py | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index 4484bf11e..90851352d 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -1453,6 +1453,13 @@ extended [G-Code command](G-Codes.md#z_tilt) becomes available. # values yield better results, but can also lead to situations where the # bed is tilted in a way that the nozzle touched the bed before the probe. # The default is conservative. +#use_probe_offsets: false +# If set to true the probe x&y offsets will be taken into acccount when +# positioning the toolhead (that way if you define a point, your probe will +# at the given coordinates instead of the nozzle) +# (usefull for probes like beacon where the offsets switch when using +# scan/dive mode vs contact, that way the actually probed points will always +# stay the same since the probe offset dynamically changes) ``` ### [quad_gantry_level] diff --git a/klippy/extras/mpc_calibrate.py b/klippy/extras/mpc_calibrate.py index ca9035f46..5bebe1cd2 100644 --- a/klippy/extras/mpc_calibrate.py +++ b/klippy/extras/mpc_calibrate.py @@ -41,7 +41,6 @@ def __init__(self, printer, heater, config): self.hysteresis = self.config.getfloat("calibrate_hysteresis", None) self.heating_gain = self.config.getfloat("calibrate_heating_gain", None) - def run(self, gcmd): profile_name = gcmd.get("PROFILE", "default") use_analytic = gcmd.get("USE_DELTA", None) is not None @@ -70,7 +69,9 @@ def run(self, gcmd): hysteresis = gcmd.get_float("HYSTERESIS", self.hysteresis) heating_gain = gcmd.get_float("HEATING_GAIN", self.heating_gain) - verify_heater = self.printer.lookup_object("VERIFY_HEATER %s" % self.heater.short_name, None) + verify_heater = self.printer.lookup_object( + "VERIFY_HEATER %s" % self.heater.short_name, None + ) old_max_error = None old_check_gain_time = None old_hysteresis = None diff --git a/klippy/extras/z_tilt.py b/klippy/extras/z_tilt.py index f14c6e45a..e7bfa04cc 100644 --- a/klippy/extras/z_tilt.py +++ b/klippy/extras/z_tilt.py @@ -194,7 +194,7 @@ def __init__(self, config): self.z_positions = config.getlists( "z_positions", seps=(",", "\n"), parser=float, count=2 ) - self.use_offsets = config.getboolean("use_offsets", False) + self.use_offsets = config.getboolean("use_probe_offsets", False) self.retry_helper = RetryHelper(config) self.probe_helper = probe.ProbePointsHelper(config, self.probe_finalize) self.probe_helper.minimum_points(2) diff --git a/klippy/extras/z_tilt_ng.py b/klippy/extras/z_tilt_ng.py index ba0811b61..158b6291e 100644 --- a/klippy/extras/z_tilt_ng.py +++ b/klippy/extras/z_tilt_ng.py @@ -223,7 +223,7 @@ def __init__(self, config): self.z_positions = config.getlists( "z_positions", seps=(",", "\n"), parser=float, count=2 ) - self.use_offsets = config.getboolean("use_offsets", False) + self.use_offsets = config.getboolean("use_probe_offsets", False) self.z_count = len(self.z_positions) self.retry_helper = RetryHelper(config) From c8dfbd3acc80f9849fb04ccd14beed46dd8bc6dd Mon Sep 17 00:00:00 2001 From: Zeanon Date: Thu, 29 Aug 2024 13:19:26 +0200 Subject: [PATCH 078/158] . --- klippy/extras/z_tilt.py | 8 ++++---- klippy/extras/z_tilt_ng.py | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/klippy/extras/z_tilt.py b/klippy/extras/z_tilt.py index e7bfa04cc..e37ebd7f0 100644 --- a/klippy/extras/z_tilt.py +++ b/klippy/extras/z_tilt.py @@ -194,7 +194,7 @@ def __init__(self, config): self.z_positions = config.getlists( "z_positions", seps=(",", "\n"), parser=float, count=2 ) - self.use_offsets = config.getboolean("use_probe_offsets", False) + self.use_probe_offsets = config.getboolean("use_probe_offsets", False) self.retry_helper = RetryHelper(config) self.probe_helper = probe.ProbePointsHelper(config, self.probe_finalize) self.probe_helper.minimum_points(2) @@ -213,11 +213,11 @@ def __init__(self, config): def cmd_Z_TILT_ADJUST(self, gcmd): self.z_status.reset() self.retry_helper.start(gcmd) - use_offsets = self.probe_helper.use_offsets - if self.use_offsets: + use_probe_offsets = self.probe_helper.use_offsets + if self.use_probe_offsets: self.probe_helper.use_xy_offsets(True) self.probe_helper.start_probe(gcmd) - self.probe_helper.use_xy_offsets(use_offsets) + self.probe_helper.use_xy_offsets(use_probe_offsets) def probe_finalize(self, offsets, positions): # Setup for coordinate descent analysis diff --git a/klippy/extras/z_tilt_ng.py b/klippy/extras/z_tilt_ng.py index 158b6291e..b7dd5a74f 100644 --- a/klippy/extras/z_tilt_ng.py +++ b/klippy/extras/z_tilt_ng.py @@ -223,7 +223,7 @@ def __init__(self, config): self.z_positions = config.getlists( "z_positions", seps=(",", "\n"), parser=float, count=2 ) - self.use_offsets = config.getboolean("use_probe_offsets", False) + self.use_probe_offsets = config.getboolean("use_probe_offsets", False) self.z_count = len(self.z_positions) self.retry_helper = RetryHelper(config) @@ -295,11 +295,11 @@ def cmd_Z_TILT_ADJUST(self, gcmd): return self.z_status.reset() self.retry_helper.start(gcmd) - use_offsets = self.probe_helper.use_offsets - if self.use_offsets: + use_probe_offsets = self.probe_helper.use_offsets + if self.use_probe_offsets: self.probe_helper.use_xy_offsets(True) self.probe_helper.start_probe(gcmd) - self.probe_helper.use_xy_offsets(use_offsets) + self.probe_helper.use_xy_offsets(use_probe_offsets) def perform_coordinate_descent(self, offsets, positions): # Setup for coordinate descent analysis @@ -362,11 +362,11 @@ def cmd_Z_TILT_CALIBRATE(self, gcmd): self.cal_avg_len = gcmd.get_int("AVGLEN", self.cal_conf_avg_len) self.cal_gcmd = gcmd self.cal_runs = [] - use_offsets = self.cal_helper.use_offsets - if self.use_offsets: + use_probe_offsets = self.cal_helper.use_offsets + if self.use_probe_offsets: self.cal_helper.use_xy_offsets(True) self.cal_helper.start_probe(gcmd) - self.cal_helper.use_xy_offsets(use_offsets) + self.cal_helper.use_xy_offsets(use_probe_offsets) def cal_finalize(self, offsets, positions): np = self.numpy @@ -415,11 +415,11 @@ def cmd_Z_TILT_AUTODETECT(self, gcmd): self.ad_runs = [] self.ad_points = [] self.ad_error = None - use_offsets = self.ad_helper.use_offsets - if self.use_offsets: + use_probe_offsets = self.ad_helper.use_offsets + if self.use_probe_offsets: self.ad_helper.use_xy_offsets(True) self.ad_helper.start_probe(gcmd) - self.ad_helper.use_xy_offsets(use_offsets) + self.ad_helper.use_xy_offsets(use_probe_offsets) ad_adjustments = [ [0.5, -0.5, -0.5], # p1 up From cb6f2a4bbc41e1c3c736ef5c2ff303c471db5677 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Thu, 29 Aug 2024 18:07:58 +0200 Subject: [PATCH 079/158] Update probe.py --- klippy/extras/probe.py | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/klippy/extras/probe.py b/klippy/extras/probe.py index 025cc6304..972fddbd9 100644 --- a/klippy/extras/probe.py +++ b/klippy/extras/probe.py @@ -520,6 +520,21 @@ def __init__( if self.def_min_horizontal_move_z is None else self.def_min_horizontal_move_z ) + self.def_additional_horizontal_move_z = config.getfloat( + "additional_horizontal_move_z", None + ) + if ( + self.def_additional_horizontal_move_z is not None + and not self.def_adaptive_horizontal_move_z + ): + raise config.error( + "'adaptive_horizontal_move_z' must be enabled before enabling 'additional_horizontal_move_z'" + ) + self.def_additional_horizontal_move_z = ( + 0.0 + if self.def_additional_horizontal_move_z is None + else self.def_additional_horizontal_move_z + ) self.speed = config.getfloat("speed", 50.0, above=0.0) self.use_offsets = False # Internal probing state @@ -570,8 +585,10 @@ def _move_next(self): if self.adaptive_horizontal_move_z: # then res is error error = math.ceil(res) or 1.0 - self.horizontal_move_z = error + max( - self.probe_offsets[2], self.min_horizontal_move_z + self.horizontal_move_z = ( + error + + max(self.probe_offsets[2], self.min_horizontal_move_z) + + self.additional_horizontal_move_z ) elif res != "retry": done = True @@ -613,6 +630,22 @@ def start_probe(self, gcmd): if self.min_horizontal_move_z is None else self.min_horizontal_move_z ) + self.additional_horizontal_move_z = gcmd.get_float( + "ADDITIONAL_HORIZONTAL_MOVE_Z", None + ) + if ( + self.additional_horizontal_move_z is not None + and not self.adaptive_horizontal_move_z + ): + raise gcmd.error( + "additional_horizontal_move_z can not be set when " + "adaptive_horizontal_move_z is disabled" + ) + self.additional_horizontal_move_z = ( + self.def_additional_horizontal_move_z + if self.additional_horizontal_move_z is None + else self.additional_horizontal_move_z + ) if probe is None or method != "automatic": # Manual probe self.lift_speed = self.speed From d3f3ce7059451c41b0b0e2512152687a95e6ab2c Mon Sep 17 00:00:00 2001 From: Zeanon Date: Thu, 29 Aug 2024 22:28:57 +0200 Subject: [PATCH 080/158] Update fan.py --- klippy/extras/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index fc3d6e9e3..d9a796f3b 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -254,7 +254,7 @@ def _set_speed( return eventtime + FAN_MIN_TIME def _unlock_lock(self, eventtime): - if self.queued_value is not None: + if self.queued_value is not None or self.queued_pwm_value is not None: value = self.queued_value pwm_value = self.queued_pwm_value force = self.queued_force From 542c30edc679194ab97e741109aa86d69a407c78 Mon Sep 17 00:00:00 2001 From: B Date: Tue, 21 May 2024 22:20:30 +0100 Subject: [PATCH 081/158] noncritical mcus --- README.md | 4 +- docs/Config_Reference.md | 4 + docs/Danger_Features.md | 2 +- klippy/clocksync.py | 4 + klippy/extras/adxl345.py | 7 ++ klippy/extras/display/display.py | 7 ++ klippy/extras/display/st7920.py | 1 + klippy/extras/display/uc1701.py | 2 + klippy/extras/lis2dw.py | 7 ++ klippy/extras/mpu9250.py | 7 ++ klippy/extras/neopixel.py | 3 + klippy/extras/temperature_mcu.py | 4 + klippy/extras/tmc.py | 9 ++- klippy/extras/tmc2130.py | 1 + klippy/extras/tmc2660.py | 1 + klippy/extras/tmc_uart.py | 1 + klippy/mcu.py | 132 +++++++++++++++++++++++++++++-- klippy/serialhdl.py | 109 ++++++++++++++++++++++++- klippy/stepper.py | 2 +- 19 files changed, 292 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index cfa674812..032d0c158 100644 --- a/README.md +++ b/README.md @@ -120,13 +120,13 @@ See the [Danger Features document](https://dangerklipper.io/Danger_Features.html - [z_tilt, quad_gantry_level: adaptive horizontal move z](https://github.com/DangerKlippers/danger-klipper/pull/336) +- [core: non-critical-mcus](https://github.com/DangerKlippers/danger-klipper/pull/339) + If you're feeling adventurous, take a peek at the extra features in the bleeding-edge-v2 branch [feature documentation](docs/Bleeding_Edge.md) and [feature configuration reference](docs/Config_Reference_Bleeding_Edge.md): - [extruder/pa: do not smooth base extruder position, only advance](https://github.com/DangerKlippers/danger-klipper/pull/266) -- [core: non-critical-mcus](https://github.com/DangerKlippers/danger-klipper/pull/265) - - [dmbutyugin's advanced-features branch - Pull Request #262](https://github.com/DangerKlippers/danger-klipper/pull/262) - stepper: high precision stepping protocol - extruder: sync extruder motion with input shaper diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index 622807442..4ceaa46d5 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -58,6 +58,10 @@ serial: # sending a Klipper command to the micro-controller so that it can # reset itself. The default is 'arduino' if the micro-controller # communicates over a serial port, 'command' otherwise. +#is_non_critical: False +# Setting this to True will allow the mcu to be disconnected and +# reconnected at will without errors. Helpful for USB-accelerometer boards +# and USB-probes ``` ### [mcu my_extra_mcu] diff --git a/docs/Danger_Features.md b/docs/Danger_Features.md index cff859907..a2cb6dd00 100644 --- a/docs/Danger_Features.md +++ b/docs/Danger_Features.md @@ -7,7 +7,7 @@ - [`[exclude_object]`](./Config_Reference.md#exclude_object) is enabled by default. Use `[exclude_object] enable_exclude_object: False` to disable it ## Additional configuration options - +- [`[mcu] is_non_critical`](./Config_Reference.md#mcu) enables marking of an mcu as optional - it can be freely disconnected and connected at will. (useful for MCU-based accelerometer boards, mcu-based probes that shut down in hot chambers, etc...) - [`[danger_options]`](./Config_Reference.md#danger-options) - New configuration options to adjust klipper values that were previously hidden - Additional kinematics versions enabled per-axis acceleration, see [limited_cartesian](./Config_Reference.md#⚠️-cartesian-kinematics-with-limits-for-x-and-y-axes) and [limited_corexy](./Config_Reference.md#⚠️-corexy-kinematics-with-limits-for-x-and-y-axes) - `--rotate-log-at-restart` can be added to your klipper start script or service to force log rotation every restart. diff --git a/klippy/clocksync.py b/klippy/clocksync.py index 33bd1b196..3fd1a1a1e 100644 --- a/klippy/clocksync.py +++ b/klippy/clocksync.py @@ -29,6 +29,10 @@ def __init__(self, reactor): self.prediction_variance = 0.0 self.last_prediction_time = 0.0 + # XXX - test to see if this is used or not + # def disconnect(self): + # self.reactor.update_timer(self.get_clock_timer, self.reactor.NEVER) + def connect(self, serial): self.serial = serial self.mcu_freq = serial.msgparser.get_constant_float("CLOCK_FREQ") diff --git a/klippy/extras/adxl345.py b/klippy/extras/adxl345.py index 17eb9e68f..f88c8345b 100644 --- a/klippy/extras/adxl345.py +++ b/klippy/extras/adxl345.py @@ -310,6 +310,12 @@ def _build_config(self): "query_adxl345_status oid=%c", oid=self.oid, cq=cmdqueue ) + def check_connected(self): + if self.mcu.non_critical_disconnected: + raise self.printer.command_error( + f"ADXL: {self.name} could not connect because mcu: {self.mcu.get_name()} is non_critical_disconnected!" + ) + def read_reg(self, reg): params = self.spi.spi_transfer([reg | REG_MOD_READ, 0x00]) response = bytearray(params["response"]) @@ -327,6 +333,7 @@ def set_reg(self, reg, val, minclock=0): ) def start_internal_client(self): + self.check_connected() aqh = AccelQueryHelper(self.printer) self.batch_bulk.add_client(aqh.handle_batch) return aqh diff --git a/klippy/extras/display/display.py b/klippy/extras/display/display.py index bd61aa399..a246afa78 100644 --- a/klippy/extras/display/display.py +++ b/klippy/extras/display/display.py @@ -233,6 +233,10 @@ def __init__(self, config): raise config.error("Unknown display_data group '%s'" % (dgroup,)) # Screen updating self.printer.register_event_handler("klippy:ready", self.handle_ready) + self.printer.register_event_handler( + self.lcd_chip.mcu.get_non_critical_reconnect_event_name(), + self.handle_reconnect, + ) self.screen_update_timer = self.reactor.register_timer( self.screen_update_event ) @@ -255,6 +259,9 @@ def __init__(self, config): def get_dimensions(self): return self.lcd_chip.get_dimensions() + def handle_reconnect(self): + self.lcd_chip.init() + def handle_ready(self): self.lcd_chip.init() # Start screen update timer diff --git a/klippy/extras/display/st7920.py b/klippy/extras/display/st7920.py index f087d5a35..687314ae3 100644 --- a/klippy/extras/display/st7920.py +++ b/klippy/extras/display/st7920.py @@ -260,6 +260,7 @@ def __init__(self, config): sw_pins = tuple([pin_params["pin"] for pin_params in sw_pin_params]) speed = config.getint("spi_speed", 1000000, minval=100000) self.spi = bus.MCU_SPI(mcu, None, None, 0, speed, sw_pins) + self.mcu = mcu # create enable helper self.en_helper = EnableHelper(config.get("en_pin"), self.spi) self.en_set = False diff --git a/klippy/extras/display/uc1701.py b/klippy/extras/display/uc1701.py index 75e058883..05810921f 100644 --- a/klippy/extras/display/uc1701.py +++ b/klippy/extras/display/uc1701.py @@ -191,6 +191,7 @@ def init(self): class UC1701(DisplayBase): def __init__(self, config): io = SPI4wire(config, "a0_pin") + self.mcu = io.spi.get_mcu() DisplayBase.__init__(self, io) self.contrast = config.getint("contrast", 40, minval=0, maxval=63) self.reset = ResetHelper(config.get("rst_pin", None), io.spi) @@ -232,6 +233,7 @@ def __init__(self, config, columns=128, x_offset=0): else: io = SPI4wire(config, "dc_pin") io_bus = io.spi + self.mcu = io_bus.get_mcu() self.reset = ResetHelper(config.get("reset_pin", None), io_bus) DisplayBase.__init__(self, io, columns, x_offset) self.contrast = config.getint("contrast", 239, minval=0, maxval=255) diff --git a/klippy/extras/lis2dw.py b/klippy/extras/lis2dw.py index 3a16b6847..8392c54cb 100644 --- a/klippy/extras/lis2dw.py +++ b/klippy/extras/lis2dw.py @@ -79,6 +79,12 @@ def _build_config(self): "query_lis2dw_status oid=%c", oid=self.oid, cq=cmdqueue ) + def check_connected(self): + if self.mcu.non_critical_disconnected: + raise self.printer.command_error( + f"LIS2DW: {self.name} could not connect because mcu: {self.mcu.get_name()} is non_critical_disconnected!" + ) + def read_reg(self, reg): params = self.spi.spi_transfer([reg | REG_MOD_READ, 0x00]) response = bytearray(params["response"]) @@ -96,6 +102,7 @@ def set_reg(self, reg, val, minclock=0): ) def start_internal_client(self): + self.check_connected() aqh = adxl345.AccelQueryHelper(self.printer) self.batch_bulk.add_client(aqh.handle_batch) return aqh diff --git a/klippy/extras/mpu9250.py b/klippy/extras/mpu9250.py index 189f1f981..3aef2428c 100644 --- a/klippy/extras/mpu9250.py +++ b/klippy/extras/mpu9250.py @@ -105,6 +105,12 @@ def _build_config(self): "query_mpu9250_status oid=%c", oid=self.oid, cq=cmdqueue ) + def check_connected(self): + if self.mcu.non_critical_disconnected: + raise self.printer.command_error( + f"MPU: {self.name} could not connect because mcu: {self.mcu.get_name()} is non_critical_disconnected!" + ) + def read_reg(self, reg): params = self.i2c.i2c_read([reg], 1) return bytearray(params["response"])[0] @@ -113,6 +119,7 @@ def set_reg(self, reg, val, minclock=0): self.i2c.i2c_write([reg, val & 0xFF], minclock=minclock) def start_internal_client(self): + self.check_connected() aqh = adxl345.AccelQueryHelper(self.printer) self.batch_bulk.add_client(aqh.handle_batch) return aqh diff --git a/klippy/extras/neopixel.py b/klippy/extras/neopixel.py index b0a41440e..8ba42c3e3 100644 --- a/klippy/extras/neopixel.py +++ b/klippy/extras/neopixel.py @@ -50,6 +50,9 @@ def __init__(self, config): self.old_color_data = bytearray([d ^ 1 for d in self.color_data]) # Register callbacks printer.register_event_handler("klippy:connect", self.send_data) + printer.register_event_handler( + self.mcu.get_non_critical_reconnect_event_name(), self.send_data + ) def build_config(self): bmt = self.mcu.seconds_to_clock(BIT_MAX_TIME) diff --git a/klippy/extras/temperature_mcu.py b/klippy/extras/temperature_mcu.py index c1c893b26..529062e2f 100644 --- a/klippy/extras/temperature_mcu.py +++ b/klippy/extras/temperature_mcu.py @@ -58,6 +58,7 @@ def __init__(self, config): self.printer.register_event_handler( "klippy:mcu_identify", self._mcu_identify ) + self.mcu_adc.get_mcu().register_config_callback(self._build_config) def setup_callback(self, temperature_callback): self.temperature_callback = temperature_callback @@ -80,6 +81,9 @@ def calc_base(self, temp, adc): return temp - adc * self.slope def _mcu_identify(self): + self._build_config() + + def _build_config(self): # Obtain mcu information _mcu = self.mcu_adc.get_mcu() self.debug_read_cmd = _mcu.lookup_query_command( diff --git a/klippy/extras/tmc.py b/klippy/extras/tmc.py index 2cc3e7122..473ddcd42 100644 --- a/klippy/extras/tmc.py +++ b/klippy/extras/tmc.py @@ -517,7 +517,14 @@ def _handle_connect(self): ) # Send init try: - self._init_registers() + if self.mcu_tmc.mcu.non_critical_disconnected: + logging.info( + "TMC %s failed to init - non_critical_mcu: %s is disconnected!", + self.name, + self.mcu_tmc.mcu.get_name(), + ) + else: + self._init_registers() except self.printer.command_error as e: logging.info("TMC %s failed to init: %s", self.name, str(e)) diff --git a/klippy/extras/tmc2130.py b/klippy/extras/tmc2130.py index 3ecae3f2e..99c25fb8e 100644 --- a/klippy/extras/tmc2130.py +++ b/klippy/extras/tmc2130.py @@ -382,6 +382,7 @@ def __init__(self, config, name_to_reg, fields, tmc_frequency): self.name_to_reg = name_to_reg self.fields = fields self.tmc_frequency = tmc_frequency + self.mcu = self.tmc_spi.spi.get_mcu() def get_fields(self): return self.fields diff --git a/klippy/extras/tmc2660.py b/klippy/extras/tmc2660.py index 5cd3620b0..7356c658d 100644 --- a/klippy/extras/tmc2660.py +++ b/klippy/extras/tmc2660.py @@ -211,6 +211,7 @@ def __init__(self, config, name_to_reg, fields): self.spi = bus.MCU_SPI_from_config(config, 0, default_speed=4000000) self.name_to_reg = name_to_reg self.fields = fields + self.mcu = self.spi.get_mcu() def get_fields(self): return self.fields diff --git a/klippy/extras/tmc_uart.py b/klippy/extras/tmc_uart.py index 74a4b9664..0d7f8faa8 100644 --- a/klippy/extras/tmc_uart.py +++ b/klippy/extras/tmc_uart.py @@ -272,6 +272,7 @@ def __init__(self, config, name_to_reg, fields, max_addr, tmc_frequency): ) self.mutex = self.mcu_uart.mutex self.tmc_frequency = tmc_frequency + self.mcu = self.mcu_uart.mcu def get_fields(self): return self.fields diff --git a/klippy/mcu.py b/klippy/mcu.py index 3256af342..f7551c1d7 100644 --- a/klippy/mcu.py +++ b/klippy/mcu.py @@ -735,7 +735,9 @@ class MCU: error = error def __init__(self, config, clocksync): + self._config = config self._printer = printer = config.get_printer() + self.danger_options = printer.lookup_object("danger_options") self.gcode = printer.lookup_object("gcode") self._clocksync = clocksync self._reactor = printer.get_reactor() @@ -744,7 +746,9 @@ def __init__(self, config, clocksync): self._name = self._name[4:] # Serial port wp = "mcu '%s': " % (self._name) - self._serial = serialhdl.SerialReader(self._reactor, warn_prefix=wp) + self._serial = serialhdl.SerialReader( + self._reactor, warn_prefix=wp, mcu=self + ) self._baud = 0 self._canbus_iface = None canbus_uuid = config.get("canbus_uuid", None) @@ -796,6 +800,32 @@ def __init__(self, config, clocksync): self._mcu_tick_avg = 0.0 self._mcu_tick_stddev = 0.0 self._mcu_tick_awake = 0.0 + + # noncritical mcus + self.is_non_critical = config.getboolean("is_non_critical", False) + if self.is_non_critical and self.get_name() == "mcu": + raise error("Primary MCU cannot be marked as non-critical!") + if self.is_non_critical: + self.non_critical_recon_timer = self._reactor.register_timer( + self.non_critical_recon_event + ) + if canbus_uuid: + raise error("CAN MCUs can't be non-critical yet!") + self.non_critical_disconnected = False + self._non_critical_reconnect_event_name = ( + f"danger:non_critical_mcu_{self.get_name()}:reconnected" + ) + self._non_critical_disconnect_event_name = ( + f"danger:non_critical_mcu_{self.get_name()}:disconnected" + ) + self.reconnect_interval = ( + config.getfloat("reconnect_interval", 2.0) + 0.12 + ) # add small change to not collide with other events + self._cached_init_state = False + self._oid_count_post_inits = 0 + self._config_cmds_post_inits = [] + self._init_cmds_post_inits = [] + self._restart_cmds_post_inits = [] # Register handlers printer.register_event_handler( "klippy:firmware_restart", self._firmware_restart @@ -884,7 +914,7 @@ def _handle_shutdown(self, params): ) def _handle_starting(self, params): - if not self._is_shutdown: + if not self._is_shutdown and not self.is_non_critical: self._printer.invoke_async_shutdown( "MCU '%s' spontaneous restart" % (self._name,) ) @@ -924,23 +954,55 @@ def dummy_estimated_print_time(eventtime): self.estimated_print_time = dummy_estimated_print_time + def handle_non_critical_disconnect(self): + self.non_critical_disconnected = True + self._clocksync.disconnect() + self._disconnect() + self._reactor.update_timer( + self.non_critical_recon_timer, self._reactor.NOW + ) + self._printer.send_event(self._non_critical_disconnect_event_name) + self.gcode.respond_info(f"mcu: '{self._name}' disconnected!", log=True) + + def non_critical_recon_event(self, eventtime): + success = self.recon_mcu() + if success: + self.gcode.respond_info( + f"mcu: '{self._name}' reconnected!", log=True + ) + return self._reactor.NEVER + else: + return eventtime + self.reconnect_interval + def _send_config(self, prev_crc): + if not self._cached_init_state: + # first time config, we haven't created callback oids yet + # so save the oid count for state reset later + self._oid_count_post_inits = self._oid_count + self._config_cmds_post_inits = self._config_cmds.copy() + self._init_cmds_post_inits = self._init_cmds.copy() + self._restart_cmds_post_inits = self._restart_cmds.copy() + self._cached_init_state = True # Build config commands for cb in self._config_callbacks: cb() - self._config_cmds.insert( + + local_config_cmds = self._config_cmds.copy() + + local_config_cmds.insert( 0, "allocate_oids count=%d" % (self._oid_count,) ) + # Resolve pin names ppins = self._printer.lookup_object("pins") pin_resolver = ppins.get_pin_resolver(self._name) - for cmdlist in (self._config_cmds, self._restart_cmds, self._init_cmds): + for cmdlist in (local_config_cmds, self._restart_cmds, self._init_cmds): for i, cmd in enumerate(cmdlist): cmdlist[i] = pin_resolver.update_command(cmd) # Calculate config CRC - encoded_config = "\n".join(self._config_cmds).encode() + encoded_config = "\n".join(local_config_cmds).encode() config_crc = zlib.crc32(encoded_config) & 0xFFFFFFFF - self.add_config_cmd("finalize_config crc=%d" % (config_crc,)) + local_config_cmds.append("finalize_config crc=%d" % (config_crc,)) if prev_crc is not None and config_crc != prev_crc: self._check_restart("CRC mismatch") raise error("MCU '%s' CRC does not match config" % (self._name,)) @@ -951,7 +1013,7 @@ def _send_config(self, prev_crc): logging.info( "Sending MCU '%s' printer configuration...", self._name ) - for c in self._config_cmds: + for c in local_config_cmds: self._serial.send(c) else: for c in self._restart_cmds: @@ -1006,7 +1068,32 @@ def _log_info(self): ] return "\n".join(log_info) + def recon_mcu(self): + res = self._mcu_identify() + if not res: + return False + self.reset_to_initial_state() + self.non_critical_disconnected = False + self._connect() + self._printer.send_event(self._non_critical_reconnect_event_name) + return True + + def reset_to_initial_state(self): + if self._cached_init_state: + self._oid_count = self._oid_count_post_inits + self._config_cmds = self._config_cmds_post_inits.copy() + self._init_cmds = self._init_cmds_post_inits.copy() + self._restart_cmds = self._restart_cmds_post_inits.copy() + self._reserved_move_slots = 0 + self._steppersync = None + def _connect(self): + if self.non_critical_disconnected: + self._reactor.update_timer( + self.non_critical_recon_timer, + self._reactor.NOW + self.reconnect_interval, + ) + return config_params = self._send_get_config() if not config_params["is_config"]: if self._restart_method == "rpi_usb": @@ -1046,7 +1133,24 @@ def _connect(self): log_info = self._log_info() + "\n" + move_msg self._printer.set_rollover_info(self._name, log_info, log=False) + def _check_serial_exists(self): + # if self._canbus_iface is not None: + # cbid = self._printer.lookup_object("canbus_ids") + # nodeid = cbid.get_nodeid(self._serialport) + # # self._serial.check_canbus_connect is not functional yet + # return self._serial.check_canbus_connect( + # self._serialport, nodeid, self._canbus_iface + # ) + # else: + rts = self._restart_method != "cheetah" + return self._serial.check_connect(self._serialport, self._baud, rts) + def _mcu_identify(self): + if self.is_non_critical and not self._check_serial_exists(): + self.non_critical_disconnected = True + return False + else: + self.non_critical_disconnected = False if self.is_fileoutput(): self._connect_file() else: @@ -1103,6 +1207,7 @@ def _mcu_identify(self): self.register_response(self._handle_shutdown, "shutdown") self.register_response(self._handle_shutdown, "is_shutdown") self.register_response(self._handle_mcu_stats, "stats") + return True def _ready(self): if self.is_fileoutput(): @@ -1168,6 +1273,12 @@ def get_printer(self): def get_name(self): return self._name + def get_non_critical_reconnect_event_name(self): + return self._non_critical_reconnect_event_name + + def get_non_critical_disconnect_event_name(self): + return self._non_critical_disconnect_event_name + def register_response(self, cb, msg, oid=None): self._serial.register_response(cb, msg, oid) @@ -1269,7 +1380,9 @@ def _restart_rpi_usb(self): chelper.run_hub_ctrl(1) def _firmware_restart(self, force=False): - if self._is_mcu_bridge and not force: + if ( + self._is_mcu_bridge and not force + ) or self.non_critical_disconnected: return if self._restart_method == "rpi_usb": self._restart_rpi_usb() @@ -1323,6 +1436,9 @@ def check_active(self, print_time, eventtime): or self._is_timeout ): return + if self.is_non_critical: + self.handle_non_critical_disconnect() + return self._is_timeout = True logging.info( "Timeout with MCU '%s' (eventtime=%f)", self._name, eventtime diff --git a/klippy/serialhdl.py b/klippy/serialhdl.py index fa051cd94..cad36dc99 100644 --- a/klippy/serialhdl.py +++ b/klippy/serialhdl.py @@ -15,9 +15,10 @@ class error(Exception): class SerialReader: - def __init__(self, reactor, warn_prefix=""): + def __init__(self, reactor, warn_prefix="", mcu=None): self.reactor = reactor self.warn_prefix = warn_prefix + self.mcu = mcu # Serial port self.serial_dev = None self.msgparser = msgproto.MessageParser(warn_prefix=warn_prefix) @@ -36,6 +37,9 @@ def __init__(self, reactor, warn_prefix=""): # Sent message notification tracking self.last_notify_id = 0 self.pending_notifications = {} + self.danger_options = self.mcu.get_printer().lookup_object( + "danger_options" + ) def _bg_thread(self): response = self.ffi_main.new("struct pull_queue_message *") @@ -124,6 +128,82 @@ def _start_session(self, serial_dev, serial_fd_type=b"u", client_id=0): ) return True + def check_canbus_connect( + self, canbus_uuid, canbus_nodeid, canbus_iface="can0" + ): + # this doesn't work + # because we don't have a way to query for the _existence_ of a device + # on the network, without "assigning" the device. + # if we query for unassigned, we get a response from the device + # but then klipper can't connect to it. + # same reason we klipper can't connect to a can device after we + # do a ~/scripts/canbus_query.py command + import can # XXX + + logging.getLogger("can").setLevel(logging.WARN) + txid = canbus_nodeid * 2 + 256 + filters = [{"can_id": txid + 1, "can_mask": 0x7FF, "extended": False}] + # Prep for SET_NODEID command + try: + uuid = int(canbus_uuid, 16) + except ValueError: + uuid = -1 + if uuid < 0 or uuid > 0xFFFFFFFFFFFF: + self._error("Invalid CAN uuid") + + CANBUS_ID_ADMIN = 0x3F0 + CMD_QUERY_UNASSIGNED = 0x00 + RESP_NEED_NODEID = 0x20 + filters = [ + { + "can_id": CANBUS_ID_ADMIN + 1, + "can_mask": 0x7FF, + "extended": False, + } + ] + + msg = can.Message( + arbitration_id=CANBUS_ID_ADMIN, + data=[CMD_QUERY_UNASSIGNED], + is_extended_id=False, + ) + try: + bus = can.interface.Bus( + channel=canbus_iface, + can_filters=filters, + bustype="socketcan", + ) + bus.send(msg) + except (can.CanError, os.error) as e: + logging.warning("%scan issue: %s", self.warn_prefix, e) + return False + + start_time = curtime = self.reactor.monotonic() + while True: + tdiff = start_time + 1.0 - curtime + if tdiff <= 0.0: + break + msg = bus.recv(tdiff) + curtime = self.reactor.monotonic() + if ( + msg is None + or msg.arbitration_id != CANBUS_ID_ADMIN + 1 + or msg.dlc < 7 + or msg.data[0] != RESP_NEED_NODEID + ): + continue + found_uuid = sum( + [v << ((5 - i) * 8) for i, v in enumerate(msg.data[1:7])] + ) + logging.info(f"found_uuid: {found_uuid}") + if found_uuid == uuid: + self.disconnect() + bus.close = bus.shutdown # XXX + return True + bus.close = bus.shutdown # XXX + # logging.info(f"couldn't find uuid: {uuid}") + return False + def connect_canbus(self, canbus_uuid, canbus_nodeid, canbus_iface="can0"): import can # XXX @@ -206,7 +286,11 @@ def connect_uart(self, serialport, baud, rts=True): # Initial connection logging.info("%sStarting serial connect", self.warn_prefix) start_time = self.reactor.monotonic() - while True: + while 1: + if ( + self.serialqueue is not None + ): # if we're already connected, don't recon + break if self.reactor.monotonic() > start_time + 90.0: self._error("Unable to connect") try: @@ -227,6 +311,17 @@ def connect_uart(self, serialport, baud, rts=True): if ret: break + def check_connect(self, serialport, baud, rts=True): + serial_dev = serial.Serial(baudrate=baud, timeout=0, exclusive=False) + serial_dev.port = serialport + serial_dev.rts = rts + try: + serial_dev.open() + except Exception: + return False + serial_dev.close() + return True + def connect_file(self, debugoutput, dictionary, pace=False): self.serial_dev = debugoutput self.msgparser.process_identify(dictionary, decompress=False) @@ -281,13 +376,23 @@ def register_response(self, callback, name, oid=None): else: self.handlers[name, oid] = callback + def _check_noncritical_disconnected(self): + if self.mcu is not None and self.mcu.non_critical_disconnected: + self._error("non-critical MCU is disconnected") + # Command sending def raw_send(self, cmd, minclock, reqclock, cmd_queue): + self._check_noncritical_disconnected() + if self.serialqueue is None: + return self.ffi_lib.serialqueue_send( self.serialqueue, cmd_queue, cmd, len(cmd), minclock, reqclock, 0 ) def raw_send_wait_ack(self, cmd, minclock, reqclock, cmd_queue): + self._check_noncritical_disconnected() + if self.serialqueue is None: + return self.last_notify_id += 1 nid = self.last_notify_id completion = self.reactor.completion() diff --git a/klippy/stepper.py b/klippy/stepper.py index 1087a8fca..20f35a1e1 100644 --- a/klippy/stepper.py +++ b/klippy/stepper.py @@ -248,7 +248,7 @@ def note_homing_end(self): self._query_mcu_position() def _query_mcu_position(self): - if self._mcu.is_fileoutput(): + if self._mcu.is_fileoutput() or self._mcu.non_critical_disconnected: return params = self._get_position_cmd.send([self._oid]) last_pos = params["pos"] From 687fa7df32cd5475af4af7aabc5644ab07a250c6 Mon Sep 17 00:00:00 2001 From: Rogerio Goncalves Date: Sat, 31 Aug 2024 13:35:22 +0100 Subject: [PATCH 082/158] clocksync.disconnect() is used --- klippy/clocksync.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/klippy/clocksync.py b/klippy/clocksync.py index 3fd1a1a1e..080b4e7ae 100644 --- a/klippy/clocksync.py +++ b/klippy/clocksync.py @@ -29,9 +29,8 @@ def __init__(self, reactor): self.prediction_variance = 0.0 self.last_prediction_time = 0.0 - # XXX - test to see if this is used or not - # def disconnect(self): - # self.reactor.update_timer(self.get_clock_timer, self.reactor.NEVER) + def disconnect(self): + self.reactor.update_timer(self.get_clock_timer, self.reactor.NEVER) def connect(self, serial): self.serial = serial From 85e32c9db8fa88b3e43ed0a44faddfe274f17a45 Mon Sep 17 00:00:00 2001 From: Timofey Titovets Date: Sat, 31 Aug 2024 14:19:32 +0100 Subject: [PATCH 083/158] sht3x: use periodic report mode (#6634) Signed-off-by: Timofey Titovets --- klippy/extras/bus.py | 5 +++++ klippy/extras/sht3x.py | 32 ++++++++++++++++++++------------ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/klippy/extras/bus.py b/klippy/extras/bus.py index d81f3fb68..c78acdd44 100644 --- a/klippy/extras/bus.py +++ b/klippy/extras/bus.py @@ -245,6 +245,11 @@ def i2c_write(self, data, minclock=0, reqclock=0): [self.oid, data], minclock=minclock, reqclock=reqclock ) + def i2c_write_wait_ack(self, data, minclock=0, reqclock=0): + self.i2c_write_cmd.send_wait_ack( + [self.oid, data], minclock=minclock, reqclock=reqclock + ) + def i2c_read(self, write, read_len): return self.i2c_read_cmd.send([self.oid, write, read_len]) diff --git a/klippy/extras/sht3x.py b/klippy/extras/sht3x.py index 6bbc9301c..69b1120f6 100644 --- a/klippy/extras/sht3x.py +++ b/klippy/extras/sht3x.py @@ -29,6 +29,13 @@ "LOW_REP": [0x24, 0x16], }, }, + "PERIODIC": { + "2HZ": { + "HIGH_REP": [0x22, 0x36], + "MED_REP": [0x22, 0x20], + "LOW_REP": [0x22, 0x2B], + }, + }, "OTHER": { "STATUS": { "READ": [0xF3, 0x2D], @@ -78,10 +85,12 @@ def get_report_time_delta(self): def _init_sht3x(self): # Device Soft Reset - self.i2c.i2c_write(SHT3X_CMD["OTHER"]["SOFTRESET"]) - - # Wait 2ms after reset - self.reactor.pause(self.reactor.monotonic() + 0.02) + self.i2c.i2c_write_wait_ack(SHT3X_CMD["OTHER"]["BREAK"]) + # Break takes ~ 1ms + self.reactor.pause(self.reactor.monotonic() + 0.0015) + self.i2c.i2c_write_wait_ack(SHT3X_CMD["OTHER"]["SOFTRESET"]) + # Wait <=1.5ms after reset + self.reactor.pause(self.reactor.monotonic() + 0.0015) status = self.i2c.i2c_read(SHT3X_CMD["OTHER"]["STATUS"]["READ"], 3) response = bytearray(status["response"]) @@ -92,16 +101,15 @@ def _init_sht3x(self): if self._crc8(status) != checksum: logging.warning("sht3x: Reading status - checksum error!") + # Enable periodic mode + self.i2c.i2c_write_wait_ack(SHT3X_CMD["PERIODIC"]["2HZ"]["HIGH_REP"]) + # Wait <=15.5ms for first measurment + self.reactor.pause(self.reactor.monotonic() + 0.0155) + def _sample_sht3x(self, eventtime): try: - # Read Temeprature - params = self.i2c.i2c_write( - SHT3X_CMD["MEASURE"]["STRETCH_ENABLED"]["HIGH_REP"] - ) - # Wait - self.reactor.pause(self.reactor.monotonic() + 0.20) - - params = self.i2c.i2c_read([], 6) + # Read measurment + params = self.i2c.i2c_read(SHT3X_CMD["OTHER"]["FETCH"], 6) response = bytearray(params["response"]) rtemp = response[0] << 8 From 7b37bf26cd17242571c6dd277bebe091c3b2f78f Mon Sep 17 00:00:00 2001 From: Nicholas Huskie <59385028+Huskia@users.noreply.github.com> Date: Sat, 31 Aug 2024 14:27:31 +0100 Subject: [PATCH 084/158] stm32: Fix getting wrong ADC value on PA0 of STM32G431 (#6660) * Fix getting wrong ADC value on PA0 * Fix invalid/unused pin being used as adc channel on STM32H7/G431/L4 Signed-off-by: Nicholas Huskie --- src/stm32/stm32h7_adc.c | 44 +++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/stm32/stm32h7_adc.c b/src/stm32/stm32h7_adc.c index 3c217ca27..6740edd3e 100644 --- a/src/stm32/stm32h7_adc.c +++ b/src/stm32/stm32h7_adc.c @@ -12,6 +12,8 @@ #include "internal.h" // GPIO #include "sched.h" // sched_shutdown +#define ADC_INVALID_PIN 0xFF + #define ADC_TEMPERATURE_PIN 0xfe DECL_ENUMERATION("pin", "ADC_TEMPERATURE", ADC_TEMPERATURE_PIN); @@ -24,8 +26,8 @@ DECL_CONSTANT("ADC_MAX", 4095); static const uint8_t adc_pins[] = { #if CONFIG_MACH_STM32H7 // ADC1 - 0, // PA0_C ADC12_INP0 - 0, // PA1_C ADC12_INP1 + ADC_INVALID_PIN, // PA0_C ADC12_INP0 + ADC_INVALID_PIN, // PA1_C ADC12_INP1 GPIO('F', 11), // ADC1_INP2 GPIO('A', 6), // ADC12_INP3 GPIO('C', 4), // ADC12_INP4 @@ -45,8 +47,8 @@ static const uint8_t adc_pins[] = { GPIO('A', 4), // ADC12_INP18 GPIO('A', 5), // ADC12_INP19 // ADC2 - 0, // PA0_C ADC12_INP0 - 0, // PA1_C ADC12_INP1 + ADC_INVALID_PIN, // PA0_C ADC12_INP0 + ADC_INVALID_PIN, // PA1_C ADC12_INP1 GPIO('F', 13), // ADC2_INP2 GPIO('A', 6), // ADC12_INP3 GPIO('C', 4), // ADC12_INP4 @@ -61,13 +63,13 @@ static const uint8_t adc_pins[] = { GPIO('C', 3), // ADC12_INP13 GPIO('A', 2), // ADC12_INP14 GPIO('A', 3), // ADC12_INP15 - 0, // dac_out1 - 0, // dac_out2 + ADC_INVALID_PIN, // dac_out1 + ADC_INVALID_PIN, // dac_out2 GPIO('A', 4), // ADC12_INP18 GPIO('A', 5), // ADC12_INP19 // ADC3 - 0, // PC2_C ADC3_INP0 - 0, // PC3_C ADC3_INP1 + ADC_INVALID_PIN, // PC2_C ADC3_INP0 + ADC_INVALID_PIN, // PC3_C ADC3_INP1 GPIO('F', 9) , // ADC3_INP2 GPIO('F', 7), // ADC3_INP3 GPIO('F', 5), // ADC3_INP4 @@ -85,14 +87,14 @@ static const uint8_t adc_pins[] = { GPIO('H', 5), // ADC3_INP16 #if CONFIG_MACH_STM32H723 ADC_TEMPERATURE_PIN, - 0, + ADC_INVALID_PIN, #else - 0, // Vbat/4 + ADC_INVALID_PIN, // Vbat/4 ADC_TEMPERATURE_PIN,// VSENSE #endif - 0, // VREFINT + ADC_INVALID_PIN, // VREFINT #elif CONFIG_MACH_STM32G4 - 0, // [0] vssa + ADC_INVALID_PIN, // [0] vssa GPIO('A', 0), // [1] GPIO('A', 1), // [2] GPIO('A', 2), // [3] @@ -105,14 +107,14 @@ static const uint8_t adc_pins[] = { GPIO('F', 0), // [10] GPIO('B', 12), // [11] GPIO('B', 1), // [12] - 0, // [13] opamp + ADC_INVALID_PIN, // [13] opamp GPIO('B', 11), // [14] GPIO('B', 0), // [15] ADC_TEMPERATURE_PIN, // [16] vtemp - 0, // [17] vbat/3 - 0, // [18] vref - 0, - 0, // [0] vssa ADC 2 + ADC_INVALID_PIN, // [17] vbat/3 + ADC_INVALID_PIN, // [18] vref + ADC_INVALID_PIN, + ADC_INVALID_PIN, // [0] vssa ADC 2 GPIO('A', 0), // [1] GPIO('A', 1), // [2] GPIO('A', 6), // [3] @@ -128,11 +130,11 @@ static const uint8_t adc_pins[] = { GPIO('A', 5), // [13] GPIO('B', 11), // [14] GPIO('B', 15), // [15] - 0, // [16] opamp + ADC_INVALID_PIN, // [16] opamp GPIO('A', 4), // [17] - 0, // [18] opamp + ADC_INVALID_PIN, // [18] opamp #else // stm32l4 - 0, // vref + ADC_INVALID_PIN, // vref GPIO('C', 0), // ADC12_IN1 .. 16 GPIO('C', 1), GPIO('C', 2), @@ -150,7 +152,7 @@ static const uint8_t adc_pins[] = { GPIO('B', 0), GPIO('B', 1), ADC_TEMPERATURE_PIN, // temp - 0, // vbat + ADC_INVALID_PIN, // vbat #endif }; From 1ed6a32713da587dc218fd8a7308f841ee6eed43 Mon Sep 17 00:00:00 2001 From: JamesH1978 <87171443+JamesH1978@users.noreply.github.com> Date: Sat, 31 Aug 2024 14:28:45 +0100 Subject: [PATCH 085/158] docs: Update Installation.md (#6650) Added links for Fluidd/Mainsail/Octoprint Added references to overview.md and mkdocs.yml and spelling errors. Signed-off-by: James Hartley --- docs/Installation.md | 158 ++++++++++++++++++++----------------- docs/OctoPrint.md | 79 +++++++++++++++++++ docs/Overview.md | 1 + docs/_klipper3d/mkdocs.yml | 4 +- 4 files changed, 169 insertions(+), 73 deletions(-) create mode 100644 docs/OctoPrint.md diff --git a/docs/Installation.md b/docs/Installation.md index 1acfcf6bd..ca64259aa 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -1,15 +1,20 @@ # Installation -These instructions assume the software will run on a Raspberry Pi -computer in conjunction with OctoPrint. It is recommended that a -Raspberry Pi 2 (or later) be used as the host machine (see the +These instructions assume the software will run on a linux based host +running a Klipper compatible front end. It is recommended that a +SBC(Small Board Computer) such as a Raspberry Pi or Debian based Linux +device be used as the host machine (see the [FAQ](FAQ.md#can-i-run-klipper-on-something-other-than-a-raspberry-pi-3) -for other machines). +for other options). + +For the purposes of these instructions host relates to the Linux device and +mcu relates to the printboard. SBC relates to the term Small Board Computer +such as the Raspberry Pi. ## Obtain a Klipper Configuration File Most Klipper settings are determined by a "printer configuration file" -that will be stored on the Raspberry Pi. An appropriate configuration +printer.cfg, that will be stored on the host. An appropriate configuration file can often be found by looking in the Klipper [config directory](../config/) for a file starting with a "printer-" prefix that corresponds to the target printer. The Klipper @@ -35,38 +40,51 @@ printer configuration file, then start with the closest example [config file](../config/) and use the Klipper [config reference](Config_Reference.md) for further information. -## Prepping an OS image +## Interacting with Klipper -Start by installing [OctoPi](https://github.com/guysoft/OctoPi) on the -Raspberry Pi computer. Use OctoPi v0.17.0 or later - see the -[OctoPi releases](https://github.com/guysoft/OctoPi/releases) for -release information. One should verify that OctoPi boots and that the -OctoPrint web server works. After connecting to the OctoPrint web -page, follow the prompt to upgrade OctoPrint to v1.4.2 or later. +Klipper is a 3d printer firmware, so it needs some way for the user to +interact with it. -After installing OctoPi and upgrading OctoPrint, it will be necessary -to ssh into the target machine to run a handful of system commands. If -using a Linux or MacOS desktop, then the "ssh" software should already -be installed on the desktop. There are free ssh clients available for -other desktops (eg, -[PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/)). Use the -ssh utility to connect to the Raspberry Pi (`ssh pi@octopi` -- password -is "raspberry") and run the following commands: +Currently the best choices are front ends that retrieve information through +the [Moonraker web API](https://moonraker.readthedocs.io/) and there is also +the option to use [Octoprint](https://octoprint.org/) to control Klipper. -``` -git clone https://github.com/DangerKlippers/danger-klipper klipper -./klipper/scripts/install-octopi.sh -``` +The choice is up to the user on what to use, but the underlying Klipper is the +same in all cases. We encourage users to research the options available and +make an informed decision. + +## Obtaining an OS image for SBC's + +There are many ways to obtain an OS image for Klipper for SBC use, most depend on +what front end you wish to use. Some manafactures of these SBC boards also provide +their own Klipper-centric images. + +The two main Moonraker based front ends are [Fluidd](https://docs.fluidd.xyz/) +and [Mainsail](https://docs.mainsail.xyz/), the latter of which has a premade install +image ["MainsailOS"](http://docs.mainsailOS.xyz), this has the option for Raspberry Pi +and some OrangePi varianta. -The above will download Klipper, install some system dependencies, -setup Klipper to run at system startup, and start the Klipper host -software. It will require an internet connection and it may take a few -minutes to complete. +Fluidd can be installed via KIAUH(Klipper Install And Update Helper), which +is explained below and is a 3rd party installer for all things Klipper. + +OctoPrint can be installed via the popular OctoPi image or via KIAUH, this +process is explained in [OctoPrint.md](OctoPrint.md) + +## Installing via KIAUH + +Normally you would start with a base image for your SBC, RPiOS Lite for example, +or in the case of a x86 Linux device, Ubuntu Server. Please note that Desktop +variants are not recommended due to certain helper programs that can stop some +Klipper functions working and even mask access to some print boards. + +KIAUH can be used to install Klipper and its associated programs on a variety +of Linux based systems that run a form of Debian. More information can be found +at https://github.com/dw-0/kiauh ## Building and flashing the micro-controller To compile the micro-controller code, start by running these commands -on the Raspberry Pi: +on your host device: ``` cd ~/klipper/ @@ -108,10 +126,21 @@ It should report something similar to the following: It's common for each printer to have its own unique serial port name. This unique name will be used when flashing the micro-controller. It's possible there may be multiple lines in the above output - if so, -choose the line corresponding to the micro-controller (see the +choose the line corresponding to the micro-controller. If many +items are listed and the choice is ambiguous, unplug the board and +run the command again, the missing item will be your print board(see the [FAQ](FAQ.md#wheres-my-serial-port) for more information). -For common micro-controllers, the code can be flashed with something +For common micro-controllers with STM32 or clone chips, LPC chips and +others it is usual that these need an initial Klipper flash via SD card. + +When flashing with this method, it is important to make sure that the +print board is not connected with USB to the host, due to some boards +being able to feed power back to the board and stopping a flash from +occuring. + +For common micro-controllers using Atmega chips, for example the 2560, +the code can be flashed with something similar to: ``` @@ -123,53 +152,38 @@ sudo service klipper start Be sure to update the FLASH_DEVICE with the printer's unique serial port name. -When flashing for the first time, make sure that OctoPrint is not -connected directly to the printer (from the OctoPrint web page, under -the "Connection" section, click "Disconnect"). - -## Configuring OctoPrint to use Klipper - -The OctoPrint web server needs to be configured to communicate with -the Klipper host software. Using a web browser, login to the OctoPrint -web page and then configure the following items: - -Navigate to the Settings tab (the wrench icon at the top of the -page). Under "Serial Connection" in "Additional serial ports" add -`/tmp/printer`. Then click "Save". - -Enter the Settings tab again and under "Serial Connection" change the -"Serial Port" setting to `/tmp/printer`. +For common micro-controllers using RP2040 chips, the code can be flashed +with something similar to: -In the Settings tab, navigate to the "Behavior" sub-tab and select the -"Cancel any ongoing prints but stay connected to the printer" -option. Click "Save". +``` +sudo service klipper stop +make flash FLASH_DEVICE=first +sudo service klipper start +``` -From the main page, under the "Connection" section (at the top left of -the page) make sure the "Serial Port" is set to `/tmp/printer` and -click "Connect". (If `/tmp/printer` is not an available selection then -try reloading the page.) +It is important to note that RP2040 chips may need to be put into Boot mode +before this operation. -Once connected, navigate to the "Terminal" tab and type "status" -(without the quotes) into the command entry box and click "Send". The -terminal window will likely report there is an error opening the -config file - that means OctoPrint is successfully communicating with -Klipper. Proceed to the next section. ## Configuring Klipper The next step is to copy the [printer configuration file](#obtain-a-klipper-configuration-file) to -the Raspberry Pi. +the host. + +Arguably the easiest way to set the Klipper configuration file is using the +built in editors in Mainsail or Fluidd. These will allow the user to open +the configuration examples and save them to be printer.cfg. -Arguably the easiest way to set the Klipper configuration file is to -use a desktop editor that supports editing files over the "scp" and/or -"sftp" protocols. There are freely available tools that support this -(eg, Notepad++, WinSCP, and Cyberduck). Load the printer config file -in the editor and then save it as a file named `printer.cfg` in the -home directory of the pi user (ie, `/home/pi/printer.cfg`). +Another option is to use a desktop editor that supports editing files +over the "scp" and/or "sftp" protocols. There are freely available tools +that support this (eg, Notepad++, WinSCP, and Cyberduck). +Load the printer config file in the editor and then save it as a file +named "printer.cfg" in the home directory of the pi user +(ie, /home/pi/printer.cfg). Alternatively, one can also copy and edit the file directly on the -Raspberry Pi via ssh. That may look something like the following (be +host via ssh. That may look something like the following (be sure to update the command to use the appropriate printer config filename): @@ -201,7 +215,7 @@ serial: /dev/serial/by-id/usb-1a86_USB2.0-Serial-if00-port0 ``` After creating and editing the file it will be necessary to issue a -"restart" command in the OctoPrint web terminal to load the config. A +"restart" command in the command console to load the config. A "status" command will report the printer is ready if the Klipper config file is successfully read and the micro-controller is successfully found and configured. @@ -211,10 +225,10 @@ Klipper to report a configuration error. If an error occurs, make any necessary corrections to the printer config file and issue "restart" until "status" reports the printer is ready. -Klipper reports error messages via the OctoPrint terminal tab. The -"status" command can be used to re-report error messages. The default -Klipper startup script also places a log in **/tmp/klippy.log** which -provides more detailed information. +Klipper reports error messages via the command console and via pop up in +Fluidd and Mainsail. The "status" command can be used to re-report error +messages. A log is available and usually located in ~/printer_data/logs +this is named klippy.log After Klipper reports that the printer is ready, proceed to the [config check document](Config_checks.md) to perform some basic checks diff --git a/docs/OctoPrint.md b/docs/OctoPrint.md new file mode 100644 index 000000000..4fa1b0217 --- /dev/null +++ b/docs/OctoPrint.md @@ -0,0 +1,79 @@ +# OctoPrint for Klipper + +Klipper has a few options for its front ends, Octoprint was the first +and original front end for Klipper. This document will give +a brief overview of installing with this option. + +## Install with OctoPi + +Start by installing [OctoPi](https://github.com/guysoft/OctoPi) on the +Raspberry Pi computer. Use OctoPi v0.17.0 or later - see the +[OctoPi releases](https://github.com/guysoft/OctoPi/releases) for +release information. + +One should verify that OctoPi boots and that the +OctoPrint web server works. After connecting to the OctoPrint web +page, follow the prompt to upgrade OctoPrint if needed. + +After installing OctoPi and upgrading OctoPrint, it will be necessary +to ssh into the target machine to run a handful of system commands. + +Start by running these commands on your host device: + +__If you do not have git installed, please do so with:__ +``` +sudo apt install git +``` +then proceed: +``` +cd ~ +git clone https://github.com/Klipper3d/klipper +./klipper/scripts/install-octopi.sh +``` + +The above will download Klipper, install the needed system dependencies, +setup Klipper to run at system startup, and start the Klipper host +software. It will require an internet connection and it may take a few +minutes to complete. + +## Installing with KIAUH + +KIAUH can be used to install OctoPrint on a variety of Linux based systems +that run a form of Debian. More information can be found +at https://github.com/dw-0/kiauh + +## Configuring OctoPrint to use Klipper + +The OctoPrint web server needs to be configured to communicate with the Klipper +host software. Using a web browser, login to the OctoPrint web page and then +configure the following items: + +Navigate to the Settings tab (the wrench icon at the top of the page). +Under "Serial Connection" in "Additional serial ports" add: + +``` +~/printer_data/comms/klippy.sock +``` +Then click "Save". + +_In some older setups this address may be `/tmp/printer`_ + + +Enter the Settings tab again and under "Serial Connection" change the "Serial Port" +setting to the one added above. + +In the Settings tab, navigate to the "Behavior" sub-tab and select the +"Cancel any ongoing prints but stay connected to the printer" option. Click "Save". + +From the main page, under the "Connection" section (at the top left of the page) +make sure the "Serial Port" is set to the new additional one added +and click "Connect". (If it is not in the available selection then +try reloading the page.) + +Once connected, navigate to the "Terminal" tab and type "status" (without the quotes) +into the command entry box and click "Send". The terminal window will likely report +there is an error opening the config file - that means OctoPrint is successfully +communicating with Klipper. + +Please proceed to [Installation.md](Installation.md) and the +_Building and flashing the micro-controller_ section diff --git a/docs/Overview.md b/docs/Overview.md index ef951fb2e..e9c9fd885 100644 --- a/docs/Overview.md +++ b/docs/Overview.md @@ -17,6 +17,7 @@ communication with the Klipper developers. ## Installation and Configuration - [Installation](Installation.md): Guide to installing Klipper. + - [Octoprint](OctoPrint.md): Guide to installing Octoprint with Klipper. - [Config Reference](Config_Reference.md): Description of config parameters. - [Rotation Distance](Rotation_Distance.md): Calculating the diff --git a/docs/_klipper3d/mkdocs.yml b/docs/_klipper3d/mkdocs.yml index 9b8711f3e..d546dba0a 100644 --- a/docs/_klipper3d/mkdocs.yml +++ b/docs/_klipper3d/mkdocs.yml @@ -89,7 +89,9 @@ nav: - Config_Changes.md - Contact.md - Installation and Configuration: - - Installation.md + - Installation: + - Installation.md + - OctoPrint.md - Configuration Reference: - Config_Reference.md - Rotation_Distance.md From 1343d8feddd029d779618b202c35b5eeb1388f18 Mon Sep 17 00:00:00 2001 From: Dmitry Butyugin Date: Sat, 31 Aug 2024 14:36:12 +0100 Subject: [PATCH 086/158] servo: Asynchronous adjustments of servo position This change follows the same approach as implemented for fan control. The change removes the move queue flushing when changing servo position, which does not appear to be necessary. This can be beneficial, for example, for WS7040-based cooling on IDEX setups where the servo can be used to control the air flow between the toolheads, with this change eliminating micro-stutters of the toolhead on servo position adjustment. Signed-off-by: Dmitry Butyugin --- klippy/extras/servo.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/klippy/extras/servo.py b/klippy/extras/servo.py index d9e6f2ade..b7309c239 100644 --- a/klippy/extras/servo.py +++ b/klippy/extras/servo.py @@ -76,13 +76,16 @@ def _get_pwm_from_pulse_width(self, width): cmd_SET_SERVO_help = "Set servo angle" def cmd_SET_SERVO(self, gcmd): - print_time = self.printer.lookup_object("toolhead").get_last_move_time() width = gcmd.get_float("WIDTH", None) if width is not None: - self._set_pwm(print_time, self._get_pwm_from_pulse_width(width)) + value = self._get_pwm_from_pulse_width(width) else: angle = gcmd.get_float("ANGLE") - self._set_pwm(print_time, self._get_pwm_from_angle(angle)) + value = self._get_pwm_from_angle(angle) + toolhead = self.printer.lookup_object("toolhead") + toolhead.register_lookahead_callback( + (lambda pt: self._set_pwm(pt, value)) + ) def load_config_prefix(config): From 2e92769139d54b6353061bd11d3da5d0b51548ef Mon Sep 17 00:00:00 2001 From: Dmitry Butyugin Date: Sat, 31 Aug 2024 14:39:04 +0100 Subject: [PATCH 087/158] idex_modes: Improved restoring position in RESTORE_DUAL_CARRIAGE_STATE Previous implementation could crash the idex carriages into each other. The new code moves the idex carriages together, eliminating this risk and decreasing the time needed to restore the carriages positions. Signed-off-by: Dmitry Butyugin --- klippy/kinematics/idex_modes.py | 41 +++++++++++++++++++++++++++++---- test/klippy/dual_carriage.cfg | 2 +- test/klippy/dual_carriage.test | 12 ++++++++++ 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/klippy/kinematics/idex_modes.py b/klippy/kinematics/idex_modes.py index a48adefc2..ab317e4cb 100644 --- a/klippy/kinematics/idex_modes.py +++ b/klippy/kinematics/idex_modes.py @@ -4,6 +4,7 @@ # Copyright (C) 2023 Dmitry Butyugin # # This file may be distributed under the terms of the GNU GPLv3 license. +import math import chelper INACTIVE = "INACTIVE" @@ -235,15 +236,39 @@ def cmd_RESTORE_DUAL_CARRIAGE_STATE(self, gcmd): move_speed = gcmd.get_float("MOVE_SPEED", 0.0, above=0.0) toolhead = self.printer.lookup_object("toolhead") toolhead.flush_step_generation() - pos = toolhead.get_position() if gcmd.get_int("MOVE", 1): + homing_speed = 99999999.0 + cur_pos = [] for i, dc in enumerate(self.dc): self.toggle_active_dc_rail(i) - saved_pos = saved_state["axes_positions"][i] - toolhead.manual_move( - pos[: self.axis] + [saved_pos] + pos[self.axis + 1 :], - move_speed or dc.get_rail().homing_speed, + homing_speed = min(homing_speed, dc.get_rail().homing_speed) + cur_pos.append(toolhead.get_position()) + move_pos = list(cur_pos[0]) + dl = [ + saved_state["axes_positions"][i] - cur_pos[i][self.axis] + for i in range(2) + ] + primary_ind = 0 if abs(dl[0]) >= abs(dl[1]) else 1 + self.toggle_active_dc_rail(primary_ind) + move_pos[self.axis] = saved_state["axes_positions"][primary_ind] + dc_mode = ( + INACTIVE + if min(abs(dl[0]), abs(dl[1])) < 0.000000001 + else COPY + if dl[0] * dl[1] > 0 + else MIRROR + ) + if dc_mode != INACTIVE: + self.dc[1 - primary_ind].activate(dc_mode, cur_pos[primary_ind]) + self.dc[1 - primary_ind].override_axis_scaling( + abs(dl[1 - primary_ind] / dl[primary_ind]), + cur_pos[primary_ind], ) + toolhead.manual_move(move_pos, move_speed or homing_speed) + toolhead.flush_step_generation() + # Make sure the scaling coefficients are restored with the mode + self.dc[0].inactivate(move_pos) + self.dc[1].inactivate(move_pos) for i, dc in enumerate(self.dc): saved_mode = saved_state["carriage_modes"][i] self.activate_dc_mode(i, saved_mode) @@ -301,3 +326,9 @@ def inactivate(self, position): self.scale = 0.0 self.apply_transform() self.mode = INACTIVE + + def override_axis_scaling(self, new_scale, position): + old_axis_position = self.get_axis_position(position) + self.scale = math.copysign(new_scale, self.scale) + self.offset = old_axis_position - position[self.axis] * self.scale + self.apply_transform() diff --git a/test/klippy/dual_carriage.cfg b/test/klippy/dual_carriage.cfg index 9ae01c2bc..93c574440 100644 --- a/test/klippy/dual_carriage.cfg +++ b/test/klippy/dual_carriage.cfg @@ -61,7 +61,7 @@ pid_Kd: 114 min_temp: 0 max_temp: 250 -[gcode_macro PARK_extruder0] +[gcode_macro PARK_extruder] gcode: G90 G1 X0 diff --git a/test/klippy/dual_carriage.test b/test/klippy/dual_carriage.test index 5b2f9e65d..ed40c236e 100644 --- a/test/klippy/dual_carriage.test +++ b/test/klippy/dual_carriage.test @@ -17,6 +17,18 @@ G1 X190 F6000 SET_DUAL_CARRIAGE CARRIAGE=0 G1 X20 F6000 +# Save dual carriage state +SAVE_DUAL_CARRIAGE_STATE + +G1 X50 F6000 + +# Go back to alternate carriage +SET_DUAL_CARRIAGE CARRIAGE=1 +G1 X170 F6000 + +# Restore dual carriage state +RESTORE_DUAL_CARRIAGE_STATE + # Test changing extruders G1 X5 T1 From cf7c1595033ff629cd53216c8b6daeb2d4bc5b8b Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Sat, 31 Aug 2024 14:50:40 +0100 Subject: [PATCH 088/158] gcode: Minor change to suppress python warning on '\s' Reported by @matdibu. Signed-off-by: Kevin O'Connor --- klippy/gcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/gcode.py b/klippy/gcode.py index 0422755f3..fbd14451d 100644 --- a/klippy/gcode.py +++ b/klippy/gcode.py @@ -537,7 +537,7 @@ def _handle_shutdown(self): if self.is_fileinput: self.printer.request_exit("error_exit") - m112_r = re.compile("^(?:[nN][0-9]+)?\s*[mM]112(?:\s|$)") + m112_r = re.compile(r"^(?:[nN][0-9]+)?\s*[mM]112(?:\s|$)") def _process_data(self, eventtime): # Read input, separate by newline, and add to pending_commands From 571c0752fabfd152f9f3cc369c3898f50d254b7a Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Sat, 31 Aug 2024 14:51:12 +0100 Subject: [PATCH 089/158] stm32: Fix setting USB clock with USB to CANbus mode on stm32g4/stm32l4 Signed-off-by: Kevin O'Connor --- src/stm32/stm32g4.c | 2 +- src/stm32/stm32l4.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stm32/stm32g4.c b/src/stm32/stm32g4.c index 1eed3ec18..5255cb19a 100644 --- a/src/stm32/stm32g4.c +++ b/src/stm32/stm32g4.c @@ -104,7 +104,7 @@ enable_clock_stm32g4(void) RCC->CR |= RCC_CR_PLLON; // Enable 48Mhz USB clock using clock recovery - if (CONFIG_USBSERIAL) { + if (CONFIG_USB) { RCC->CRRCR |= RCC_CRRCR_HSI48ON; while (!(RCC->CRRCR & RCC_CRRCR_HSI48RDY)) ; diff --git a/src/stm32/stm32l4.c b/src/stm32/stm32l4.c index 7db15fff0..ae099d6bc 100644 --- a/src/stm32/stm32l4.c +++ b/src/stm32/stm32l4.c @@ -96,7 +96,7 @@ enable_clock_stm32l4(void) RCC->CR |= RCC_CR_PLLON; // Enable 48Mhz USB clock using clock recovery - if (CONFIG_USBSERIAL) { + if (CONFIG_USB) { RCC->CRRCR |= RCC_CRRCR_HSI48ON; while (!(RCC->CRRCR & RCC_CRRCR_HSI48RDY)) ; From 82b779a476cd6bf550d6bde53c4544e141f2318e Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Sat, 31 Aug 2024 14:53:41 +0100 Subject: [PATCH 090/158] smart_effector: Define get_position_endstop() wrapper Reported by @noisyfox. Signed-off-by: Kevin O'Connor --- klippy/extras/smart_effector.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/klippy/extras/smart_effector.py b/klippy/extras/smart_effector.py index cf7ee1307..043cab17c 100644 --- a/klippy/extras/smart_effector.py +++ b/klippy/extras/smart_effector.py @@ -69,6 +69,13 @@ def __init__(self, config): self.query_endstop = self.probe_wrapper.query_endstop self.multi_probe_begin = self.probe_wrapper.multi_probe_begin self.multi_probe_end = self.probe_wrapper.multi_probe_end + self.get_position_endstop = self.probe_wrapper.get_position_endstop + # Common probe implementation helpers + self.cmd_helper = probe.ProbeCommandHelper( + config, self, self.probe_wrapper.query_endstop + ) + self.probe_offsets = probe.ProbeOffsetsHelper(config) + self.probe_session = probe.ProbeSessionHelper(config, self) # SmartEffector control control_pin = config.get("control_pin", None) if control_pin: From 73f778df825917848dfaa7102b6a3477bee88817 Mon Sep 17 00:00:00 2001 From: bryan065 <30362590+bryan065@users.noreply.github.com> Date: Sat, 31 Aug 2024 14:23:37 +0100 Subject: [PATCH 091/158] spi_flash: Add stm32g0b0xx to board_defs.py (#6646) Added board definition for stm32g0b0xx variant of the SKR Mini v3.0. Signed-off-by: Bryan Le --- scripts/spi_flash/board_defs.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/spi_flash/board_defs.py b/scripts/spi_flash/board_defs.py index d1949f0e0..848e1ebb8 100644 --- a/scripts/spi_flash/board_defs.py +++ b/scripts/spi_flash/board_defs.py @@ -19,6 +19,11 @@ "spi_bus": "spi1", "cs_pin": "PA4", }, + "btt-skr-mini-v3-b0": { + "mcu": "stm32g0b0xx", + "spi_bus": "spi1", + "cs_pin": "PA4", + }, "flyboard-mini": { "mcu": "stm32f103xe", "spi_bus": "spi2", @@ -124,6 +129,7 @@ "btt-skr-mini-e3-v1.2": BOARD_DEFS["btt-skr-mini"], "btt-skr-mini-e3-v2": BOARD_DEFS["btt-skr-mini"], "btt-skr-mini-e3-v3": BOARD_DEFS["btt-skr-mini-v3"], + "btt-skr-mini-e3-v3-b0": BOARD_DEFS["btt-skr-mini-v3-b0"], "btt-skr-mini-mz": BOARD_DEFS["btt-skr-mini"], "btt-skr-e3-dip": BOARD_DEFS["btt-skr-mini"], "btt002-v1": BOARD_DEFS["btt-skr-mini"], From 1cb2cb21ac283ef29ff3d3906efc6dd438443f19 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 1 Sep 2024 19:11:23 +0200 Subject: [PATCH 092/158] . --- klippy/extras/bed_mesh.py | 35 ++++++++++++++++++++++++++++++++--- klippy/extras/probe.py | 12 +++++++----- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index ea4e30458..9404ff1a4 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -50,8 +50,12 @@ def lerp(t, v0, v1): # retreive commma separated pair from config -def parse_config_pair(config, option, default, minval=None, maxval=None): - pair = config.getintlist(option, (default, default)) +def parse_config_pair( + config, option, default, minval=None, maxval=None, default_x=None, default_y=None +): + default_x = default if default_x is None else default_x + default_y = default if default_y is None else default_y + pair = config.getintlist(option, (default_x, default_y)) if len(pair) != 2: if len(pair) != 1: raise config.error( @@ -552,6 +556,25 @@ def _init_mesh_config(self, config): pps = parse_config_pair(config, "mesh_pps", 2, minval=0) orig_cfg["mesh_x_pps"] = mesh_cfg["mesh_x_pps"] = pps[0] orig_cfg["mesh_y_pps"] = mesh_cfg["mesh_y_pps"] = pps[1] + + self.contact_probe_count = parse_config_pair( + config, + "contact_probe_count", + default=None, + minval=3, + default_x=x_cnt, + default_y=y_cnt, + ) + + self.contact_mesh_pps = parse_config_pair( + config, + "contact_mesh_pps", + default=None, + minval=0, + default_x=pps[0], + default_y=pps[1], + ) + orig_cfg["algo"] = mesh_cfg["algo"] = ( config.get("algorithm", "lagrange").strip().lower() ) @@ -781,6 +804,13 @@ def update_config(self, gcmd): params = gcmd.get_command_parameters() need_cfg_update = False + probe_method = gcmd.get("METHOD", "automatic") + if probe_method == "contact": + self.mesh_config["x_count"] = self.contact_probe_count[0] + self.mesh_config["y_count"] = self.contact_probe_count[1] + self.mesh_config["mesh_x_pps"] = self.contact_mesh_pps[0] + self.mesh_config["mesh_y_pps"] = self.contact_mesh_pps[1] + need_cfg_update = True if self.radius is not None: if "MESH_RADIUS" in params: self.radius = gcmd.get_float("MESH_RADIUS") @@ -819,7 +849,6 @@ def update_config(self, gcmd): need_cfg_update = True need_cfg_update |= self.set_adaptive_mesh(gcmd) - probe_method = gcmd.get("METHOD", "automatic") if need_cfg_update: self._verify_algorithm(gcmd.error) diff --git a/klippy/extras/probe.py b/klippy/extras/probe.py index 972fddbd9..af85792e7 100644 --- a/klippy/extras/probe.py +++ b/klippy/extras/probe.py @@ -572,7 +572,7 @@ def _lift_toolhead(self): speed = self.move_z_speed toolhead.manual_move([None, None, self.horizontal_move_z], speed) - def _move_next(self): + def _move_next(self, speed=None): toolhead = self.printer.lookup_object("toolhead") # Check if done probing done = False @@ -601,10 +601,12 @@ def _move_next(self): if self.use_offsets: nextpos[0] -= self.probe_offsets[0] nextpos[1] -= self.probe_offsets[1] - toolhead.manual_move(nextpos, self.speed) + speed = self.speed if speed is None else speed + toolhead.manual_move(nextpos, speed) return False - def start_probe(self, gcmd): + def start_probe(self, gcmd, speed=None): + speed = self.speed if speed is None else speed manual_probe.verify_no_manual_probe(self.printer) # Lookup objects probe = self.printer.lookup_object("probe", None) @@ -648,7 +650,7 @@ def start_probe(self, gcmd): ) if probe is None or method != "automatic": # Manual probe - self.lift_speed = self.speed + self.lift_speed = speed self.probe_offsets = (0.0, 0.0, 0.0) self._manual_probe_start() return @@ -659,7 +661,7 @@ def start_probe(self, gcmd): raise gcmd.error("horizontal_move_z can't be less than probe's z_offset") probe.multi_probe_begin() while True: - done = self._move_next() + done = self._move_next(speed=speed) if done: break pos = probe.run_probe(gcmd) From 2e24cc2a4e6d52868f8088217fc78cd38bfcc7cb Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 1 Sep 2024 19:19:22 +0200 Subject: [PATCH 093/158] . --- klippy/extras/display/display.py | 4 +--- klippy/extras/servo.py | 4 +--- klippy/extras/temperature_mcu.py | 4 +--- klippy/kinematics/idex_modes.py | 4 +--- klippy/mcu.py | 20 +++++--------------- klippy/serialhdl.py | 16 ++++------------ 6 files changed, 13 insertions(+), 39 deletions(-) diff --git a/klippy/extras/display/display.py b/klippy/extras/display/display.py index 7c03cb716..4cd2498ee 100644 --- a/klippy/extras/display/display.py +++ b/klippy/extras/display/display.py @@ -230,9 +230,7 @@ def __init__(self, config): self.lcd_chip.mcu.get_non_critical_reconnect_event_name(), self.handle_reconnect, ) - self.screen_update_timer = self.reactor.register_timer( - self.screen_update_event - ) + self.screen_update_timer = self.reactor.register_timer(self.screen_update_event) self.redraw_request_pending = False self.redraw_time = 0.0 # Register g-code commands diff --git a/klippy/extras/servo.py b/klippy/extras/servo.py index a871e5e3b..70b3a821e 100644 --- a/klippy/extras/servo.py +++ b/klippy/extras/servo.py @@ -139,9 +139,7 @@ def cmd_SET_SERVO(self, gcmd): angle = gcmd.get_float("ANGLE") value = self._get_pwm_from_angle(angle) toolhead = self.printer.lookup_object("toolhead") - toolhead.register_lookahead_callback( - (lambda pt: self._set_pwm(pt, value)) - ) + toolhead.register_lookahead_callback((lambda pt: self._set_pwm(pt, value))) cmd_SET_SERVO_TEMPLATE_help = "Assign a display_template to a SERVO" diff --git a/klippy/extras/temperature_mcu.py b/klippy/extras/temperature_mcu.py index bfe1f11fc..39542cd3b 100644 --- a/klippy/extras/temperature_mcu.py +++ b/klippy/extras/temperature_mcu.py @@ -59,9 +59,7 @@ def __init__(self, config): range_check_count=self._danger_check_count, ) return - self.printer.register_event_handler( - "klippy:mcu_identify", self._mcu_identify - ) + self.printer.register_event_handler("klippy:mcu_identify", self._mcu_identify) self.mcu_adc.get_mcu().register_config_callback(self._build_config) def handle_beacon_ready(self): diff --git a/klippy/kinematics/idex_modes.py b/klippy/kinematics/idex_modes.py index 733a7ab84..f0bc6e4fb 100644 --- a/klippy/kinematics/idex_modes.py +++ b/klippy/kinematics/idex_modes.py @@ -241,9 +241,7 @@ def cmd_RESTORE_DUAL_CARRIAGE_STATE(self, gcmd): dc_mode = ( INACTIVE if min(abs(dl[0]), abs(dl[1])) < 0.000000001 - else COPY - if dl[0] * dl[1] > 0 - else MIRROR + else COPY if dl[0] * dl[1] > 0 else MIRROR ) if dc_mode != INACTIVE: self.dc[1 - primary_ind].activate(dc_mode, cur_pos[primary_ind]) diff --git a/klippy/mcu.py b/klippy/mcu.py index 824153034..a1cac51d7 100644 --- a/klippy/mcu.py +++ b/klippy/mcu.py @@ -729,9 +729,7 @@ def __init__(self, config, clocksync): self._name = self._name[4:] # Serial port wp = "mcu '%s': " % (self._name) - self._serial = serialhdl.SerialReader( - self._reactor, warn_prefix=wp, mcu=self - ) + self._serial = serialhdl.SerialReader(self._reactor, warn_prefix=wp, mcu=self) self._baud = 0 self._canbus_iface = None canbus_uuid = config.get("canbus_uuid", None) @@ -961,9 +959,7 @@ def handle_non_critical_disconnect(self): def non_critical_recon_event(self, eventtime): success = self.recon_mcu() if success: - self.gcode.respond_info( - f"mcu: '{self._name}' reconnected!", log=True - ) + self.gcode.respond_info(f"mcu: '{self._name}' reconnected!", log=True) return self._reactor.NEVER else: return eventtime + self.reconnect_interval @@ -983,9 +979,7 @@ def _send_config(self, prev_crc): local_config_cmds = self._config_cmds.copy() - local_config_cmds.insert( - 0, "allocate_oids count=%d" % (self._oid_count,) - ) + local_config_cmds.insert(0, "allocate_oids count=%d" % (self._oid_count,)) # Resolve pin names ppins = self._printer.lookup_object("pins") @@ -1005,9 +999,7 @@ def _send_config(self, prev_crc): self.register_response(self._handle_starting, "starting") try: if prev_crc is None: - logging.info( - "Sending MCU '%s' printer configuration...", self._name - ) + logging.info("Sending MCU '%s' printer configuration...", self._name) for c in local_config_cmds: self._serial.send(c) else: @@ -1366,9 +1358,7 @@ def _restart_rpi_usb(self): chelper.run_hub_ctrl(1) def _firmware_restart(self, force=False): - if ( - self._is_mcu_bridge and not force - ) or self.non_critical_disconnected: + if (self._is_mcu_bridge and not force) or self.non_critical_disconnected: return if self._restart_method == "rpi_usb": self._restart_rpi_usb() diff --git a/klippy/serialhdl.py b/klippy/serialhdl.py index b338053a8..9879f2da1 100644 --- a/klippy/serialhdl.py +++ b/klippy/serialhdl.py @@ -37,9 +37,7 @@ def __init__(self, reactor, warn_prefix="", mcu=None): # Sent message notification tracking self.last_notify_id = 0 self.pending_notifications = {} - self.danger_options = self.mcu.get_printer().lookup_object( - "danger_options" - ) + self.danger_options = self.mcu.get_printer().lookup_object("danger_options") def _bg_thread(self): response = self.ffi_main.new("struct pull_queue_message *") @@ -122,9 +120,7 @@ def _start_session(self, serial_dev, serial_fd_type=b"u", client_id=0): ) return True - def check_canbus_connect( - self, canbus_uuid, canbus_nodeid, canbus_iface="can0" - ): + def check_canbus_connect(self, canbus_uuid, canbus_nodeid, canbus_iface="can0"): # this doesn't work # because we don't have a way to query for the _existence_ of a device # on the network, without "assigning" the device. @@ -186,9 +182,7 @@ def check_canbus_connect( or msg.data[0] != RESP_NEED_NODEID ): continue - found_uuid = sum( - [v << ((5 - i) * 8) for i, v in enumerate(msg.data[1:7])] - ) + found_uuid = sum([v << ((5 - i) * 8) for i, v in enumerate(msg.data[1:7])]) logging.info(f"found_uuid: {found_uuid}") if found_uuid == uuid: self.disconnect() @@ -273,9 +267,7 @@ def connect_uart(self, serialport, baud, rts=True): logging.info("%sStarting serial connect", self.warn_prefix) start_time = self.reactor.monotonic() while 1: - if ( - self.serialqueue is not None - ): # if we're already connected, don't recon + if self.serialqueue is not None: # if we're already connected, don't recon break if self.reactor.monotonic() > start_time + 90.0: self._error("Unable to connect") From 5e44de59f2c12e96c88a05b4704855b9dd7930d8 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 1 Sep 2024 19:32:55 +0200 Subject: [PATCH 094/158] . --- klippy/extras/bed_mesh.py | 7 +- klippy/extras/probe.py | 128 +++++++++++++++-------------- klippy/extras/quad_gantry_level.py | 4 +- klippy/extras/z_tilt.py | 4 +- klippy/extras/z_tilt_ng.py | 13 ++- 5 files changed, 89 insertions(+), 67 deletions(-) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index 9404ff1a4..7eba2c33b 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -575,6 +575,8 @@ def _init_mesh_config(self, config): default_y=pps[1], ) + self.contact_speed = config.getfloat("contact_speed", None, above=0.0) + orig_cfg["algo"] = mesh_cfg["algo"] = ( config.get("algorithm", "lagrange").strip().lower() ) @@ -889,7 +891,10 @@ def cmd_BED_MESH_CALIBRATE(self, gcmd): raise gcmd.error("Value for parameter 'PROFILE' must be specified") self.bedmesh.set_mesh(None) self.update_config(gcmd) - self.probe_helper.start_probe(gcmd) + speed = ( + self.contact_speed if gcmd.get("METHOD", "automatic") == "contact" else None + ) + self.probe_helper.start_probe(gcmd, speed) def probe_finalize(self, offsets, positions): x_offset, y_offset, z_offset = offsets diff --git a/klippy/extras/probe.py b/klippy/extras/probe.py index af85792e7..aef794d04 100644 --- a/klippy/extras/probe.py +++ b/klippy/extras/probe.py @@ -491,8 +491,10 @@ def __init__( finalize_callback, default_points=None, option_name="points", + enable_adaptive_move_z=False, ): self.printer = config.get_printer() + self.enable_adaptive_move_z = enable_adaptive_move_z self.finalize_callback = finalize_callback self.probe_points = default_points self.name = config.get_name() @@ -504,37 +506,40 @@ def __init__( ) self.move_z_speed = config.getfloat("horizontal_move_z_speed", None, above=0.0) self.default_horizontal_move_z = config.getfloat("horizontal_move_z", 5.0) - self.def_adaptive_horizontal_move_z = config.getboolean( - "adaptive_horizontal_move_z", False - ) - self.def_min_horizontal_move_z = config.getfloat("min_horizontal_move_z", None) - if ( - self.def_min_horizontal_move_z is not None - and not self.def_adaptive_horizontal_move_z - ): - raise config.error( - "'adaptive_horizontal_move_z' must be enabled before enabling 'min_horizontal_move_z'" + if self.enable_adaptive_move_z: + self.def_adaptive_horizontal_move_z = config.getboolean( + "adaptive_horizontal_move_z", False ) - self.def_min_horizontal_move_z = ( - 0.0 - if self.def_min_horizontal_move_z is None - else self.def_min_horizontal_move_z - ) - self.def_additional_horizontal_move_z = config.getfloat( - "additional_horizontal_move_z", None - ) - if ( - self.def_additional_horizontal_move_z is not None - and not self.def_adaptive_horizontal_move_z - ): - raise config.error( - "'adaptive_horizontal_move_z' must be enabled before enabling 'additional_horizontal_move_z'" + self.def_min_horizontal_move_z = config.getfloat( + "min_horizontal_move_z", None + ) + if ( + self.def_min_horizontal_move_z is not None + and not self.def_adaptive_horizontal_move_z + ): + raise config.error( + "'adaptive_horizontal_move_z' must be enabled before enabling 'min_horizontal_move_z'" + ) + self.def_min_horizontal_move_z = ( + 0.0 + if self.def_min_horizontal_move_z is None + else self.def_min_horizontal_move_z + ) + self.def_additional_horizontal_move_z = config.getfloat( + "additional_horizontal_move_z", None + ) + if ( + self.def_additional_horizontal_move_z is not None + and not self.def_adaptive_horizontal_move_z + ): + raise config.error( + "'adaptive_horizontal_move_z' must be enabled before enabling 'additional_horizontal_move_z'" + ) + self.def_additional_horizontal_move_z = ( + 0.0 + if self.def_additional_horizontal_move_z is None + else self.def_additional_horizontal_move_z ) - self.def_additional_horizontal_move_z = ( - 0.0 - if self.def_additional_horizontal_move_z is None - else self.def_additional_horizontal_move_z - ) self.speed = config.getfloat("speed", 50.0, above=0.0) self.use_offsets = False # Internal probing state @@ -582,7 +587,7 @@ def _move_next(self, speed=None): if isinstance(res, (int, float)): if res == 0: done = True - if self.adaptive_horizontal_move_z: + if self.enable_adaptive_move_z and self.adaptive_horizontal_move_z: # then res is error error = math.ceil(res) or 1.0 self.horizontal_move_z = ( @@ -615,39 +620,40 @@ def start_probe(self, gcmd, speed=None): self.horizontal_move_z = gcmd.get_float( "HORIZONTAL_MOVE_Z", self.default_horizontal_move_z ) - self.adaptive_horizontal_move_z = gcmd.get_int( - "ADAPTIVE_HORIZONTAL_MOVE_Z", self.def_adaptive_horizontal_move_z - ) - self.min_horizontal_move_z = gcmd.get_float("MIN_HORIZONTAL_MOVE_Z", None) - if ( - self.min_horizontal_move_z is not None - and not self.adaptive_horizontal_move_z - ): - raise gcmd.error( - "min_horizontal_move_z can not be set when " - "adaptive_horizontal_move_z is disabled" + if self.enable_adaptive_move_z: + self.adaptive_horizontal_move_z = gcmd.get_int( + "ADAPTIVE_HORIZONTAL_MOVE_Z", self.def_adaptive_horizontal_move_z ) - self.min_horizontal_move_z = ( - self.def_min_horizontal_move_z - if self.min_horizontal_move_z is None - else self.min_horizontal_move_z - ) - self.additional_horizontal_move_z = gcmd.get_float( - "ADDITIONAL_HORIZONTAL_MOVE_Z", None - ) - if ( - self.additional_horizontal_move_z is not None - and not self.adaptive_horizontal_move_z - ): - raise gcmd.error( - "additional_horizontal_move_z can not be set when " - "adaptive_horizontal_move_z is disabled" + self.min_horizontal_move_z = gcmd.get_float("MIN_HORIZONTAL_MOVE_Z", None) + if ( + self.min_horizontal_move_z is not None + and not self.adaptive_horizontal_move_z + ): + raise gcmd.error( + "min_horizontal_move_z can not be set when " + "adaptive_horizontal_move_z is disabled" + ) + self.min_horizontal_move_z = ( + self.def_min_horizontal_move_z + if self.min_horizontal_move_z is None + else self.min_horizontal_move_z + ) + self.additional_horizontal_move_z = gcmd.get_float( + "ADDITIONAL_HORIZONTAL_MOVE_Z", None + ) + if ( + self.additional_horizontal_move_z is not None + and not self.adaptive_horizontal_move_z + ): + raise gcmd.error( + "additional_horizontal_move_z can not be set when " + "adaptive_horizontal_move_z is disabled" + ) + self.additional_horizontal_move_z = ( + self.def_additional_horizontal_move_z + if self.additional_horizontal_move_z is None + else self.additional_horizontal_move_z ) - self.additional_horizontal_move_z = ( - self.def_additional_horizontal_move_z - if self.additional_horizontal_move_z is None - else self.additional_horizontal_move_z - ) if probe is None or method != "automatic": # Manual probe self.lift_speed = speed diff --git a/klippy/extras/quad_gantry_level.py b/klippy/extras/quad_gantry_level.py index f878142e9..eb0d13d07 100644 --- a/klippy/extras/quad_gantry_level.py +++ b/klippy/extras/quad_gantry_level.py @@ -31,7 +31,9 @@ def __init__(self, config): ) self.max_adjust = config.getfloat("max_adjust", 4, above=0) self.horizontal_move_z = config.getfloat("horizontal_move_z", 5.0) - self.probe_helper = probe.ProbePointsHelper(config, self.probe_finalize) + self.probe_helper = probe.ProbePointsHelper( + config, self.probe_finalize, enable_adaptive_move_z=True + ) if len(self.probe_helper.probe_points) != 4: raise config.error("Need exactly 4 probe points for quad_gantry_level") self.z_status = z_tilt.ZAdjustStatus(self.printer) diff --git a/klippy/extras/z_tilt.py b/klippy/extras/z_tilt.py index e37ebd7f0..2451862a9 100644 --- a/klippy/extras/z_tilt.py +++ b/klippy/extras/z_tilt.py @@ -196,7 +196,9 @@ def __init__(self, config): ) self.use_probe_offsets = config.getboolean("use_probe_offsets", False) self.retry_helper = RetryHelper(config) - self.probe_helper = probe.ProbePointsHelper(config, self.probe_finalize) + self.probe_helper = probe.ProbePointsHelper( + config, self.probe_finalize, enable_adaptive_move_z=True + ) self.probe_helper.minimum_points(2) self.z_status = ZAdjustStatus(self.printer) self.z_helper = ZAdjustHelper(config, len(self.z_positions)) diff --git a/klippy/extras/z_tilt_ng.py b/klippy/extras/z_tilt_ng.py index b7dd5a74f..1ce1ad055 100644 --- a/klippy/extras/z_tilt_ng.py +++ b/klippy/extras/z_tilt_ng.py @@ -227,7 +227,9 @@ def __init__(self, config): self.z_count = len(self.z_positions) self.retry_helper = RetryHelper(config) - self.probe_helper = probe.ProbePointsHelper(config, self.probe_finalize) + self.probe_helper = probe.ProbePointsHelper( + config, self.probe_finalize, enable_adaptive_move_z=True + ) self.probe_helper.minimum_points(2) self.config_z_offsets = config.getfloatlist( @@ -243,11 +245,16 @@ def __init__(self, config): self.cal_helper = None if config.get("extra_points", None) is not None: self.cal_helper = probe.ProbePointsHelper( - config, self.cal_finalize, option_name="extra_points" + config, + self.cal_finalize, + option_name="extra_points", + enable_adaptive_move_z=True, ) cal_probe_points.extend(self.cal_helper.get_probe_points()) self.cal_helper.update_probe_points(cal_probe_points, 3) - self.ad_helper = probe.ProbePointsHelper(config, self.ad_finalize) + self.ad_helper = probe.ProbePointsHelper( + config, self.ad_finalize, enable_adaptive_move_z=True + ) self.ad_helper.update_probe_points(cal_probe_points, 3) self.cal_conf_avg_len = config.getint("averaging_len", 3, minval=1) self.ad_conf_delta = config.getfloat("autodetect_delta", 1.0, minval=0.1) From db79db4003eeb8be9a60be7f5253ae7ba58ac48a Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 1 Sep 2024 19:33:17 +0200 Subject: [PATCH 095/158] Update bed_mesh.py --- klippy/extras/bed_mesh.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index 9404ff1a4..7eba2c33b 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -575,6 +575,8 @@ def _init_mesh_config(self, config): default_y=pps[1], ) + self.contact_speed = config.getfloat("contact_speed", None, above=0.0) + orig_cfg["algo"] = mesh_cfg["algo"] = ( config.get("algorithm", "lagrange").strip().lower() ) @@ -889,7 +891,10 @@ def cmd_BED_MESH_CALIBRATE(self, gcmd): raise gcmd.error("Value for parameter 'PROFILE' must be specified") self.bedmesh.set_mesh(None) self.update_config(gcmd) - self.probe_helper.start_probe(gcmd) + speed = ( + self.contact_speed if gcmd.get("METHOD", "automatic") == "contact" else None + ) + self.probe_helper.start_probe(gcmd, speed) def probe_finalize(self, offsets, positions): x_offset, y_offset, z_offset = offsets From 836a8fa9ecc542249dc79bcb3d59d501be335be0 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 1 Sep 2024 22:20:00 +0200 Subject: [PATCH 096/158] Update bed_mesh.py --- klippy/extras/bed_mesh.py | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index 7eba2c33b..6b8e7b6ef 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -557,25 +557,25 @@ def _init_mesh_config(self, config): orig_cfg["mesh_x_pps"] = mesh_cfg["mesh_x_pps"] = pps[0] orig_cfg["mesh_y_pps"] = mesh_cfg["mesh_y_pps"] = pps[1] - self.contact_probe_count = parse_config_pair( + self.scan_probe_count = parse_config_pair( config, - "contact_probe_count", + "scan_probe_count", default=None, minval=3, default_x=x_cnt, default_y=y_cnt, ) - self.contact_mesh_pps = parse_config_pair( + self.scan_mesh_pps = parse_config_pair( config, - "contact_mesh_pps", + "scan_mesh_pps", default=None, minval=0, default_x=pps[0], default_y=pps[1], ) - self.contact_speed = config.getfloat("contact_speed", None, above=0.0) + self.scan_speed = config.getfloat("scan_speed", None, above=0.0) orig_cfg["algo"] = mesh_cfg["algo"] = ( config.get("algorithm", "lagrange").strip().lower() @@ -795,7 +795,7 @@ def set_adaptive_mesh(self, gcmd): self._profile_name = None return True - def update_config(self, gcmd): + def update_config(self, gcmd, beacon_scan=None): # reset default configuration self.radius = self.orig_config["radius"] self.origin = self.orig_config["origin"] @@ -806,12 +806,11 @@ def update_config(self, gcmd): params = gcmd.get_command_parameters() need_cfg_update = False - probe_method = gcmd.get("METHOD", "automatic") - if probe_method == "contact": - self.mesh_config["x_count"] = self.contact_probe_count[0] - self.mesh_config["y_count"] = self.contact_probe_count[1] - self.mesh_config["mesh_x_pps"] = self.contact_mesh_pps[0] - self.mesh_config["mesh_y_pps"] = self.contact_mesh_pps[1] + if beacon_scan: + self.mesh_config["x_count"] = self.scan_probe_count[0] + self.mesh_config["y_count"] = self.scan_probe_count[1] + self.mesh_config["mesh_x_pps"] = self.scan_mesh_pps[0] + self.mesh_config["mesh_y_pps"] = self.scan_mesh_pps[1] need_cfg_update = True if self.radius is not None: if "MESH_RADIUS" in params: @@ -851,6 +850,7 @@ def update_config(self, gcmd): need_cfg_update = True need_cfg_update |= self.set_adaptive_mesh(gcmd) + probe_method = gcmd.get("METHOD", "automatic") if need_cfg_update: self._verify_algorithm(gcmd.error) @@ -890,10 +890,16 @@ def cmd_BED_MESH_CALIBRATE(self, gcmd): if not self._profile_name.strip(): raise gcmd.error("Value for parameter 'PROFILE' must be specified") self.bedmesh.set_mesh(None) - self.update_config(gcmd) - speed = ( - self.contact_speed if gcmd.get("METHOD", "automatic") == "contact" else None - ) + beacon = self.printer.lookup_object("beacon", None) + beacon_scan = None + if beacon is not None: + beacon_scan = ( + gcmd.get("PROBE_METHOD", beacon.default_mesh_method).lower() + != "contact" + or self.bedmesh.horizontal_move_z <= beacon.trigger_dive_threshold + ) + self.update_config(gcmd, beacon_scan=beacon_scan) + speed = self.scan_speed if beacon_scan else None self.probe_helper.start_probe(gcmd, speed) def probe_finalize(self, offsets, positions): From d25fc2af6dbe715b61e2ead7b711985d43dd2bb8 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 1 Sep 2024 22:20:22 +0200 Subject: [PATCH 097/158] Update bed_mesh.py --- klippy/extras/bed_mesh.py | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index 7eba2c33b..6b8e7b6ef 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -557,25 +557,25 @@ def _init_mesh_config(self, config): orig_cfg["mesh_x_pps"] = mesh_cfg["mesh_x_pps"] = pps[0] orig_cfg["mesh_y_pps"] = mesh_cfg["mesh_y_pps"] = pps[1] - self.contact_probe_count = parse_config_pair( + self.scan_probe_count = parse_config_pair( config, - "contact_probe_count", + "scan_probe_count", default=None, minval=3, default_x=x_cnt, default_y=y_cnt, ) - self.contact_mesh_pps = parse_config_pair( + self.scan_mesh_pps = parse_config_pair( config, - "contact_mesh_pps", + "scan_mesh_pps", default=None, minval=0, default_x=pps[0], default_y=pps[1], ) - self.contact_speed = config.getfloat("contact_speed", None, above=0.0) + self.scan_speed = config.getfloat("scan_speed", None, above=0.0) orig_cfg["algo"] = mesh_cfg["algo"] = ( config.get("algorithm", "lagrange").strip().lower() @@ -795,7 +795,7 @@ def set_adaptive_mesh(self, gcmd): self._profile_name = None return True - def update_config(self, gcmd): + def update_config(self, gcmd, beacon_scan=None): # reset default configuration self.radius = self.orig_config["radius"] self.origin = self.orig_config["origin"] @@ -806,12 +806,11 @@ def update_config(self, gcmd): params = gcmd.get_command_parameters() need_cfg_update = False - probe_method = gcmd.get("METHOD", "automatic") - if probe_method == "contact": - self.mesh_config["x_count"] = self.contact_probe_count[0] - self.mesh_config["y_count"] = self.contact_probe_count[1] - self.mesh_config["mesh_x_pps"] = self.contact_mesh_pps[0] - self.mesh_config["mesh_y_pps"] = self.contact_mesh_pps[1] + if beacon_scan: + self.mesh_config["x_count"] = self.scan_probe_count[0] + self.mesh_config["y_count"] = self.scan_probe_count[1] + self.mesh_config["mesh_x_pps"] = self.scan_mesh_pps[0] + self.mesh_config["mesh_y_pps"] = self.scan_mesh_pps[1] need_cfg_update = True if self.radius is not None: if "MESH_RADIUS" in params: @@ -851,6 +850,7 @@ def update_config(self, gcmd): need_cfg_update = True need_cfg_update |= self.set_adaptive_mesh(gcmd) + probe_method = gcmd.get("METHOD", "automatic") if need_cfg_update: self._verify_algorithm(gcmd.error) @@ -890,10 +890,16 @@ def cmd_BED_MESH_CALIBRATE(self, gcmd): if not self._profile_name.strip(): raise gcmd.error("Value for parameter 'PROFILE' must be specified") self.bedmesh.set_mesh(None) - self.update_config(gcmd) - speed = ( - self.contact_speed if gcmd.get("METHOD", "automatic") == "contact" else None - ) + beacon = self.printer.lookup_object("beacon", None) + beacon_scan = None + if beacon is not None: + beacon_scan = ( + gcmd.get("PROBE_METHOD", beacon.default_mesh_method).lower() + != "contact" + or self.bedmesh.horizontal_move_z <= beacon.trigger_dive_threshold + ) + self.update_config(gcmd, beacon_scan=beacon_scan) + speed = self.scan_speed if beacon_scan else None self.probe_helper.start_probe(gcmd, speed) def probe_finalize(self, offsets, positions): From 9aa0f00d54b4da027edfe13b04dda06411d8c05f Mon Sep 17 00:00:00 2001 From: Zeanon Date: Mon, 2 Sep 2024 00:45:56 +0200 Subject: [PATCH 098/158] Update bed_mesh.py --- klippy/extras/bed_mesh.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index 6b8e7b6ef..bd21f5176 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -895,8 +895,8 @@ def cmd_BED_MESH_CALIBRATE(self, gcmd): if beacon is not None: beacon_scan = ( gcmd.get("PROBE_METHOD", beacon.default_mesh_method).lower() - != "contact" - or self.bedmesh.horizontal_move_z <= beacon.trigger_dive_threshold + == "proximity" + and self.bedmesh.horizontal_move_z <= beacon.trigger_dive_threshold ) self.update_config(gcmd, beacon_scan=beacon_scan) speed = self.scan_speed if beacon_scan else None From 9a2beadfa905b4a1e22dc5b78dde85b299208743 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Mon, 2 Sep 2024 01:01:20 +0200 Subject: [PATCH 099/158] . --- klippy/extras/bed_mesh.py | 24 +++++++++++++++++------- klippy/extras/probe.py | 8 +++++--- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index bd21f5176..c644cd04c 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -121,6 +121,9 @@ def __init__(self, config): self.z_mesh = None self.toolhead = None self.horizontal_move_z = config.getfloat("horizontal_move_z", 5.0) + self.contact_horizontal_move_z = config.getfloat( + "contact_horizontal_move_z", self.horizontal_move_z + ) self.fade_start = config.getfloat("fade_start", 1.0) self.fade_end = config.getfloat("fade_end", 0.0) self.fade_dist = self.fade_end - self.fade_start @@ -795,7 +798,7 @@ def set_adaptive_mesh(self, gcmd): self._profile_name = None return True - def update_config(self, gcmd, beacon_scan=None): + def update_config(self, gcmd, beacon_scan=False): # reset default configuration self.radius = self.orig_config["radius"] self.origin = self.orig_config["origin"] @@ -891,16 +894,23 @@ def cmd_BED_MESH_CALIBRATE(self, gcmd): raise gcmd.error("Value for parameter 'PROFILE' must be specified") self.bedmesh.set_mesh(None) beacon = self.printer.lookup_object("beacon", None) - beacon_scan = None + beacon_scan = False + horizontal_move_z = gcmd.get_float( + "HORIZONTAL_MOVE_Z", self.bedmesh.horizontal_move_z + ) if beacon is not None: - beacon_scan = ( + if ( gcmd.get("PROBE_METHOD", beacon.default_mesh_method).lower() - == "proximity" - and self.bedmesh.horizontal_move_z <= beacon.trigger_dive_threshold - ) + == "contact" + ): + horizontal_move_z = gcmd.get_float( + "HORIZONTAL_MOVE_Z", self.bedmesh.contact_horizontal_move_z + ) + elif horizontal_move_z <= beacon.trigger_dive_threshold: + beacon_scan = True self.update_config(gcmd, beacon_scan=beacon_scan) speed = self.scan_speed if beacon_scan else None - self.probe_helper.start_probe(gcmd, speed) + self.probe_helper.start_probe(gcmd, speed, horizontal_move_z) def probe_finalize(self, offsets, positions): x_offset, y_offset, z_offset = offsets diff --git a/klippy/extras/probe.py b/klippy/extras/probe.py index aef794d04..3995765e0 100644 --- a/klippy/extras/probe.py +++ b/klippy/extras/probe.py @@ -610,15 +610,17 @@ def _move_next(self, speed=None): toolhead.manual_move(nextpos, speed) return False - def start_probe(self, gcmd, speed=None): + def start_probe(self, gcmd, speed=None, horizontal_move_z=None): speed = self.speed if speed is None else speed manual_probe.verify_no_manual_probe(self.printer) # Lookup objects probe = self.printer.lookup_object("probe", None) method = gcmd.get("METHOD", "automatic").lower() self.results = [] - self.horizontal_move_z = gcmd.get_float( - "HORIZONTAL_MOVE_Z", self.default_horizontal_move_z + self.horizontal_move_z = ( + gcmd.get_float("HORIZONTAL_MOVE_Z", self.default_horizontal_move_z) + if horizontal_move_z is None + else horizontal_move_z ) if self.enable_adaptive_move_z: self.adaptive_horizontal_move_z = gcmd.get_int( From f69e74fb61a50a6a0a9aeb8f7d999c7b52619c01 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Mon, 2 Sep 2024 01:03:55 +0200 Subject: [PATCH 100/158] . --- klippy/extras/bed_mesh.py | 24 +++++++++++++++++------- klippy/extras/probe.py | 7 ++++++- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index 6b8e7b6ef..c644cd04c 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -121,6 +121,9 @@ def __init__(self, config): self.z_mesh = None self.toolhead = None self.horizontal_move_z = config.getfloat("horizontal_move_z", 5.0) + self.contact_horizontal_move_z = config.getfloat( + "contact_horizontal_move_z", self.horizontal_move_z + ) self.fade_start = config.getfloat("fade_start", 1.0) self.fade_end = config.getfloat("fade_end", 0.0) self.fade_dist = self.fade_end - self.fade_start @@ -795,7 +798,7 @@ def set_adaptive_mesh(self, gcmd): self._profile_name = None return True - def update_config(self, gcmd, beacon_scan=None): + def update_config(self, gcmd, beacon_scan=False): # reset default configuration self.radius = self.orig_config["radius"] self.origin = self.orig_config["origin"] @@ -891,16 +894,23 @@ def cmd_BED_MESH_CALIBRATE(self, gcmd): raise gcmd.error("Value for parameter 'PROFILE' must be specified") self.bedmesh.set_mesh(None) beacon = self.printer.lookup_object("beacon", None) - beacon_scan = None + beacon_scan = False + horizontal_move_z = gcmd.get_float( + "HORIZONTAL_MOVE_Z", self.bedmesh.horizontal_move_z + ) if beacon is not None: - beacon_scan = ( + if ( gcmd.get("PROBE_METHOD", beacon.default_mesh_method).lower() - != "contact" - or self.bedmesh.horizontal_move_z <= beacon.trigger_dive_threshold - ) + == "contact" + ): + horizontal_move_z = gcmd.get_float( + "HORIZONTAL_MOVE_Z", self.bedmesh.contact_horizontal_move_z + ) + elif horizontal_move_z <= beacon.trigger_dive_threshold: + beacon_scan = True self.update_config(gcmd, beacon_scan=beacon_scan) speed = self.scan_speed if beacon_scan else None - self.probe_helper.start_probe(gcmd, speed) + self.probe_helper.start_probe(gcmd, speed, horizontal_move_z) def probe_finalize(self, offsets, positions): x_offset, y_offset, z_offset = offsets diff --git a/klippy/extras/probe.py b/klippy/extras/probe.py index af85792e7..9d94b150e 100644 --- a/klippy/extras/probe.py +++ b/klippy/extras/probe.py @@ -605,7 +605,7 @@ def _move_next(self, speed=None): toolhead.manual_move(nextpos, speed) return False - def start_probe(self, gcmd, speed=None): + def start_probe(self, gcmd, speed=None, horizontal_move_z=None): speed = self.speed if speed is None else speed manual_probe.verify_no_manual_probe(self.printer) # Lookup objects @@ -626,6 +626,11 @@ def start_probe(self, gcmd, speed=None): raise gcmd.error( "min_horizontal_move_z can not be set when " "adaptive_horizontal_move_z is disabled" + self.horizontal_move_z = ( + gcmd.get_float("HORIZONTAL_MOVE_Z", self.default_horizontal_move_z) + if horizontal_move_z is None + else horizontal_move_z + ) ) self.min_horizontal_move_z = ( self.def_min_horizontal_move_z From 66b75de4a1dc1d21788ed69459a631fa6ce1b549 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Mon, 2 Sep 2024 10:42:14 +0200 Subject: [PATCH 101/158] Update bed_mesh.py --- klippy/extras/bed_mesh.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index c644cd04c..a1cce9443 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -798,7 +798,7 @@ def set_adaptive_mesh(self, gcmd): self._profile_name = None return True - def update_config(self, gcmd, beacon_scan=False): + def update_config(self, gcmd, beacon_scan=False, recompute=True): # reset default configuration self.radius = self.orig_config["radius"] self.origin = self.orig_config["origin"] @@ -855,19 +855,22 @@ def update_config(self, gcmd, beacon_scan=False): need_cfg_update |= self.set_adaptive_mesh(gcmd) probe_method = gcmd.get("METHOD", "automatic") - if need_cfg_update: - self._verify_algorithm(gcmd.error) - self._generate_points(gcmd.error, probe_method) - gcmd.respond_info("Generating new points...") - self.print_generated_points(gcmd.respond_info) - pts = self._get_adjusted_points() - self.probe_helper.update_probe_points(pts, 3) - msg = "\n".join(["%s: %s" % (k, v) for k, v in self.mesh_config.items()]) - logging.info("Updated Mesh Configuration:\n" + msg) - else: - self._generate_points(gcmd.error, probe_method) - pts = self._get_adjusted_points() - self.probe_helper.update_probe_points(pts, 3) + if recompute: + if need_cfg_update: + self._verify_algorithm(gcmd.error) + self._generate_points(gcmd.error, probe_method) + gcmd.respond_info("Generating new points...") + self.print_generated_points(gcmd.respond_info) + pts = self._get_adjusted_points() + self.probe_helper.update_probe_points(pts, 3) + msg = "\n".join( + ["%s: %s" % (k, v) for k, v in self.mesh_config.items()] + ) + logging.info("Updated Mesh Configuration:\n" + msg) + else: + self._generate_points(gcmd.error, probe_method) + pts = self._get_adjusted_points() + self.probe_helper.update_probe_points(pts, 3) def _get_adjusted_points(self): adj_pts = [] From e4597624f49c7e2750c6a30242f8a881f9ac284f Mon Sep 17 00:00:00 2001 From: Zeanon Date: Mon, 2 Sep 2024 10:42:30 +0200 Subject: [PATCH 102/158] Update bed_mesh.py --- klippy/extras/bed_mesh.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index c644cd04c..a1cce9443 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -798,7 +798,7 @@ def set_adaptive_mesh(self, gcmd): self._profile_name = None return True - def update_config(self, gcmd, beacon_scan=False): + def update_config(self, gcmd, beacon_scan=False, recompute=True): # reset default configuration self.radius = self.orig_config["radius"] self.origin = self.orig_config["origin"] @@ -855,19 +855,22 @@ def update_config(self, gcmd, beacon_scan=False): need_cfg_update |= self.set_adaptive_mesh(gcmd) probe_method = gcmd.get("METHOD", "automatic") - if need_cfg_update: - self._verify_algorithm(gcmd.error) - self._generate_points(gcmd.error, probe_method) - gcmd.respond_info("Generating new points...") - self.print_generated_points(gcmd.respond_info) - pts = self._get_adjusted_points() - self.probe_helper.update_probe_points(pts, 3) - msg = "\n".join(["%s: %s" % (k, v) for k, v in self.mesh_config.items()]) - logging.info("Updated Mesh Configuration:\n" + msg) - else: - self._generate_points(gcmd.error, probe_method) - pts = self._get_adjusted_points() - self.probe_helper.update_probe_points(pts, 3) + if recompute: + if need_cfg_update: + self._verify_algorithm(gcmd.error) + self._generate_points(gcmd.error, probe_method) + gcmd.respond_info("Generating new points...") + self.print_generated_points(gcmd.respond_info) + pts = self._get_adjusted_points() + self.probe_helper.update_probe_points(pts, 3) + msg = "\n".join( + ["%s: %s" % (k, v) for k, v in self.mesh_config.items()] + ) + logging.info("Updated Mesh Configuration:\n" + msg) + else: + self._generate_points(gcmd.error, probe_method) + pts = self._get_adjusted_points() + self.probe_helper.update_probe_points(pts, 3) def _get_adjusted_points(self): adj_pts = [] From 7cbde77da62de3d120b6dc46f8a7f2fa52161aac Mon Sep 17 00:00:00 2001 From: Zeanon Date: Mon, 2 Sep 2024 13:22:24 +0200 Subject: [PATCH 103/158] . --- klippy/extras/bed_mesh.py | 6 +++--- klippy/extras/probe.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index a1cce9443..66da860ed 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -852,10 +852,10 @@ def update_config(self, gcmd, beacon_scan=False, recompute=True): self.mesh_config["algo"] = gcmd.get("ALGORITHM").strip().lower() need_cfg_update = True - need_cfg_update |= self.set_adaptive_mesh(gcmd) - probe_method = gcmd.get("METHOD", "automatic") - if recompute: + need_cfg_update |= self.set_adaptive_mesh(gcmd) + probe_method = gcmd.get("METHOD", "automatic") + if need_cfg_update: self._verify_algorithm(gcmd.error) self._generate_points(gcmd.error, probe_method) diff --git a/klippy/extras/probe.py b/klippy/extras/probe.py index 9d94b150e..0fd777a7a 100644 --- a/klippy/extras/probe.py +++ b/klippy/extras/probe.py @@ -626,12 +626,12 @@ def start_probe(self, gcmd, speed=None, horizontal_move_z=None): raise gcmd.error( "min_horizontal_move_z can not be set when " "adaptive_horizontal_move_z is disabled" + ) self.horizontal_move_z = ( gcmd.get_float("HORIZONTAL_MOVE_Z", self.default_horizontal_move_z) if horizontal_move_z is None else horizontal_move_z ) - ) self.min_horizontal_move_z = ( self.def_min_horizontal_move_z if self.min_horizontal_move_z is None From e2a2b5c0c61e90c6b54250375a99bb633b3c9bd5 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Mon, 2 Sep 2024 13:23:07 +0200 Subject: [PATCH 104/158] Update bed_mesh.py --- klippy/extras/bed_mesh.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index a1cce9443..66da860ed 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -852,10 +852,10 @@ def update_config(self, gcmd, beacon_scan=False, recompute=True): self.mesh_config["algo"] = gcmd.get("ALGORITHM").strip().lower() need_cfg_update = True - need_cfg_update |= self.set_adaptive_mesh(gcmd) - probe_method = gcmd.get("METHOD", "automatic") - if recompute: + need_cfg_update |= self.set_adaptive_mesh(gcmd) + probe_method = gcmd.get("METHOD", "automatic") + if need_cfg_update: self._verify_algorithm(gcmd.error) self._generate_points(gcmd.error, probe_method) From e039a594ba1d6da807df7aad4fc63d1fd9ff53d7 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Mon, 2 Sep 2024 13:45:50 +0200 Subject: [PATCH 105/158] Update temperature_mcu.py --- klippy/extras/temperature_mcu.py | 65 +++++++++++++------------------- 1 file changed, 27 insertions(+), 38 deletions(-) diff --git a/klippy/extras/temperature_mcu.py b/klippy/extras/temperature_mcu.py index 39542cd3b..38c75be26 100644 --- a/klippy/extras/temperature_mcu.py +++ b/klippy/extras/temperature_mcu.py @@ -26,9 +26,18 @@ def __init__(self, config): self.report_time = REPORT_TIME # Read config mcu_name = config.get("sensor_mcu", "mcu") - self.beacon = None + self.beacon_mcu_temp_wrapper = None if mcu_name == "beacon": - self.beacon = self.printer.load_object(config, "beacon").mcu_temp_wrapper + beacon = self.printer.load_object(config, "beacon").mcu_temp_wrapper + if beacon is None: + raise self.printer.config_error( + "Beacon module not installed, can not register beacon_mcu as temperature_sensor" + ) + if not hasattr(beacon, "mcu_temp_wrapper"): + raise self.printer.config_error( + "Beacon module has no wrapper for mcu temperature and thus can not be registered as temperature_sensor" + ) + self.beacon_mcu_temp_wrapper = beacon.mcu_temp_wrapper self.printer.register_event_handler( "klippy:ready", self.handle_beacon_ready ) @@ -63,16 +72,27 @@ def __init__(self, config): self.mcu_adc.get_mcu().register_config_callback(self._build_config) def handle_beacon_ready(self): - self.beacon.activate_wrapper(self.config) + self.beacon_mcu_temp_wrapper.activate_wrapper(self.config) def get_report_time_delta(self): - return REPORT_TIME + if self.beacon_mcu_temp_wrapper is not None: + return self.beacon_mcu_temp_wrapper.report_time + return self.report_time + + def set_report_time(self, report_time): + if self.beacon_mcu_temp_wrapper is not None: + self.beacon_mcu_temp_wrapper.set_report_time(report_time) + return + self.report_time = report_time def adc_callback(self, read_time, read_value): temp = self.base_temperature + read_value * self.slope self.temperature_callback(read_time + SAMPLE_COUNT * SAMPLE_TIME, temp) def setup_minmax(self, min_temp, max_temp): + if self.beacon_mcu_temp_wrapper is not None: + self.beacon_mcu_temp_wrapper.setup_minmax(min_temp, max_temp) + return self.min_temp = min_temp self.max_temp = max_temp @@ -86,7 +106,7 @@ def _mcu_identify(self): self._build_config() def _build_config(self): - if self.beacon is not None: + if self.beacon_mcu_temp_wrapper is not None: return # Obtain mcu information _mcu = self.mcu_adc.get_mcu() @@ -145,36 +165,11 @@ def _build_config(self): ) def setup_callback(self, temperature_callback): - if self.beacon is not None: - self.beacon.setup_callback(temperature_callback) + if self.beacon_mcu_temp_wrapper is not None: + self.beacon_mcu_temp_wrapper.setup_callback(temperature_callback) return self.temperature_callback = temperature_callback - def get_report_time_delta(self): - if self.beacon is not None: - return self.beacon.report_time - return self.report_time - - def adc_callback(self, read_time, read_value): - temp = self.base_temperature + read_value * self.slope - self.temperature_callback(read_time + SAMPLE_COUNT * SAMPLE_TIME, temp) - - def setup_minmax(self, min_temp, max_temp): - if self.beacon is not None: - self.beacon.setup_minmax(min_temp, max_temp) - return - self.min_temp = min_temp - self.max_temp = max_temp - - def calc_adc(self, temp): - return (temp - self.base_temperature) / self.slope - - def calc_base(self, temp, adc): - return temp - adc * self.slope - - def _mcu_identify(self): - self._build_config() - def config_unknown(self): raise self.printer.config_error( "MCU temperature not supported on %s" % (self.mcu_type,) @@ -264,12 +259,6 @@ def read32(self, addr): params = self.debug_read_cmd.send([2, addr]) return params["val"] - def set_report_time(self, report_time): - if self.beacon is not None: - self.beacon.set_report_time(report_time) - return - self.report_time = report_time - def load_config(config): pheaters = config.get_printer().load_object(config, "heaters") From f9c3e572cca64a5e692fb01a28d624b472164949 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Mon, 2 Sep 2024 13:56:02 +0200 Subject: [PATCH 106/158] Update temperature_mcu.py --- klippy/extras/temperature_mcu.py | 37 ++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/klippy/extras/temperature_mcu.py b/klippy/extras/temperature_mcu.py index ceecc94e3..7fc11c0ce 100644 --- a/klippy/extras/temperature_mcu.py +++ b/klippy/extras/temperature_mcu.py @@ -26,13 +26,18 @@ def __init__(self, config): self.report_time = REPORT_TIME # Read config mcu_name = config.get("sensor_mcu", "mcu") - self.beacon = None + self.beacon_mcu_temp_wrapper = None if mcu_name == "beacon": - self.beacon = self.printer.load_object(config, "beacon").mcu_temp_wrapper - self.printer.register_event_handler( - "klippy:ready", self.handle_beacon_ready - ) - return + beacon = self.printer.load_object(config, "beacon").mcu_temp_wrapper + if beacon is None: + raise self.printer.config_error( + "Beacon module not installed, can not register beacon_mcu as temperature_sensor" + ) + if not hasattr(beacon, "mcu_temp_wrapper"): + raise self.printer.config_error( + "Beacon module has no wrapper for mcu temperature and thus can not be registered as temperature_sensor" + ) + self.beacon_mcu_temp_wrapper = beacon.mcu_temp_wrapper self.reference_voltage = config.getfloat("reference_voltage", default=3.3) self.temp1 = config.getfloat("sensor_temperature1", None) if self.temp1 is not None: @@ -63,10 +68,10 @@ def __init__(self, config): self.mcu_adc.get_mcu().register_config_callback(self._build_config) def handle_beacon_ready(self): - self.beacon.activate_wrapper(self.config) + self.beacon_mcu_temp_wrapper.activate_wrapper(self.config) def _build_config(self): - if self.beacon is not None: + if self.beacon_mcu_temp_wrapper is not None: return self.debug_read_cmd = self.mcu_adc.get_mcu().lookup_query_command( "debug_read order=%c addr=%u", "debug_result val=%u" @@ -123,14 +128,14 @@ def _build_config(self): ) def setup_callback(self, temperature_callback): - if self.beacon is not None: - self.beacon.setup_callback(temperature_callback) + if self.beacon_mcu_temp_wrapper is not None: + self.beacon_mcu_temp_wrapper.setup_callback(temperature_callback) return self.temperature_callback = temperature_callback def get_report_time_delta(self): - if self.beacon is not None: - return self.beacon.report_time + if self.beacon_mcu_temp_wrapper is not None: + return self.beacon_mcu_temp_wrapper.report_time return self.report_time def adc_callback(self, read_time, read_value): @@ -138,8 +143,8 @@ def adc_callback(self, read_time, read_value): self.temperature_callback(read_time + SAMPLE_COUNT * SAMPLE_TIME, temp) def setup_minmax(self, min_temp, max_temp): - if self.beacon is not None: - self.beacon.setup_minmax(min_temp, max_temp) + if self.beacon_mcu_temp_wrapper is not None: + self.beacon_mcu_temp_wrapper.setup_minmax(min_temp, max_temp) return self.min_temp = min_temp self.max_temp = max_temp @@ -243,8 +248,8 @@ def read32(self, addr): return params["val"] def set_report_time(self, report_time): - if self.beacon is not None: - self.beacon.set_report_time(report_time) + if self.beacon_mcu_temp_wrapper is not None: + self.beacon_mcu_temp_wrapper.set_report_time(report_time) return self.report_time = report_time From 3a27396863878fd10e71c39ed079c574f5da447e Mon Sep 17 00:00:00 2001 From: Pieterv24 <9167905+Pieterv24@users.noreply.github.com> Date: Tue, 3 Sep 2024 11:40:15 +0200 Subject: [PATCH 107/158] Added None check to SerialReader constructor --- klippy/serialhdl.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/klippy/serialhdl.py b/klippy/serialhdl.py index cad36dc99..783c08304 100644 --- a/klippy/serialhdl.py +++ b/klippy/serialhdl.py @@ -37,9 +37,10 @@ def __init__(self, reactor, warn_prefix="", mcu=None): # Sent message notification tracking self.last_notify_id = 0 self.pending_notifications = {} - self.danger_options = self.mcu.get_printer().lookup_object( - "danger_options" - ) + if self.mcu is not None + self.danger_options = self.mcu.get_printer().lookup_object( + "danger_options" + ) def _bg_thread(self): response = self.ffi_main.new("struct pull_queue_message *") From dc5f8615c87e25e9858f369d9d0893c1451a29f6 Mon Sep 17 00:00:00 2001 From: Pieterv24 <9167905+Pieterv24@users.noreply.github.com> Date: Tue, 3 Sep 2024 11:56:26 +0200 Subject: [PATCH 108/158] Fixed typo --- klippy/serialhdl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/serialhdl.py b/klippy/serialhdl.py index 783c08304..01d3139d8 100644 --- a/klippy/serialhdl.py +++ b/klippy/serialhdl.py @@ -37,7 +37,7 @@ def __init__(self, reactor, warn_prefix="", mcu=None): # Sent message notification tracking self.last_notify_id = 0 self.pending_notifications = {} - if self.mcu is not None + if self.mcu is not None: self.danger_options = self.mcu.get_printer().lookup_object( "danger_options" ) From 3a79f19dbff94f5c5541b7e5693b24a15bfab6e6 Mon Sep 17 00:00:00 2001 From: Pieterv24 <9167905+Pieterv24@users.noreply.github.com> Date: Tue, 3 Sep 2024 13:38:37 +0200 Subject: [PATCH 109/158] Removed offending lines --- klippy/serialhdl.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/klippy/serialhdl.py b/klippy/serialhdl.py index 01d3139d8..c055ddd92 100644 --- a/klippy/serialhdl.py +++ b/klippy/serialhdl.py @@ -37,10 +37,6 @@ def __init__(self, reactor, warn_prefix="", mcu=None): # Sent message notification tracking self.last_notify_id = 0 self.pending_notifications = {} - if self.mcu is not None: - self.danger_options = self.mcu.get_printer().lookup_object( - "danger_options" - ) def _bg_thread(self): response = self.ffi_main.new("struct pull_queue_message *") From 84821c5421b5d751ef02940ea002f1bce037c9f7 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Thu, 5 Sep 2024 12:00:38 +0200 Subject: [PATCH 110/158] Update bed_mesh.py --- klippy/extras/bed_mesh.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index 66da860ed..474c5d0a5 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -121,9 +121,6 @@ def __init__(self, config): self.z_mesh = None self.toolhead = None self.horizontal_move_z = config.getfloat("horizontal_move_z", 5.0) - self.contact_horizontal_move_z = config.getfloat( - "contact_horizontal_move_z", self.horizontal_move_z - ) self.fade_start = config.getfloat("fade_start", 1.0) self.fade_end = config.getfloat("fade_end", 0.0) self.fade_dist = self.fade_end - self.fade_start @@ -904,12 +901,9 @@ def cmd_BED_MESH_CALIBRATE(self, gcmd): if beacon is not None: if ( gcmd.get("PROBE_METHOD", beacon.default_mesh_method).lower() - == "contact" + != "contact" + and horizontal_move_z <= beacon.trigger_dive_threshold ): - horizontal_move_z = gcmd.get_float( - "HORIZONTAL_MOVE_Z", self.bedmesh.contact_horizontal_move_z - ) - elif horizontal_move_z <= beacon.trigger_dive_threshold: beacon_scan = True self.update_config(gcmd, beacon_scan=beacon_scan) speed = self.scan_speed if beacon_scan else None From e9964b1cf2b91e998889da358824af4d6ba0e977 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Thu, 5 Sep 2024 12:00:54 +0200 Subject: [PATCH 111/158] Update bed_mesh.py --- klippy/extras/bed_mesh.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index 66da860ed..474c5d0a5 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -121,9 +121,6 @@ def __init__(self, config): self.z_mesh = None self.toolhead = None self.horizontal_move_z = config.getfloat("horizontal_move_z", 5.0) - self.contact_horizontal_move_z = config.getfloat( - "contact_horizontal_move_z", self.horizontal_move_z - ) self.fade_start = config.getfloat("fade_start", 1.0) self.fade_end = config.getfloat("fade_end", 0.0) self.fade_dist = self.fade_end - self.fade_start @@ -904,12 +901,9 @@ def cmd_BED_MESH_CALIBRATE(self, gcmd): if beacon is not None: if ( gcmd.get("PROBE_METHOD", beacon.default_mesh_method).lower() - == "contact" + != "contact" + and horizontal_move_z <= beacon.trigger_dive_threshold ): - horizontal_move_z = gcmd.get_float( - "HORIZONTAL_MOVE_Z", self.bedmesh.contact_horizontal_move_z - ) - elif horizontal_move_z <= beacon.trigger_dive_threshold: beacon_scan = True self.update_config(gcmd, beacon_scan=beacon_scan) speed = self.scan_speed if beacon_scan else None From 815ba7866f48fd817fc4bbd267671ea732baa11c Mon Sep 17 00:00:00 2001 From: Zeanon Date: Thu, 5 Sep 2024 13:34:25 +0200 Subject: [PATCH 112/158] . --- klippy/extras/tmc2130.py | 10 +++++----- klippy/extras/tmc2208.py | 2 +- klippy/extras/tmc2209.py | 2 +- klippy/extras/tmc2240.py | 18 ++++++++++++++++-- klippy/extras/tmc2660.py | 18 ++++++++++++++++-- klippy/extras/tmc5160.py | 6 +++--- 6 files changed, 42 insertions(+), 14 deletions(-) diff --git a/klippy/extras/tmc2130.py b/klippy/extras/tmc2130.py index 81d9583e2..7824efa60 100644 --- a/klippy/extras/tmc2130.py +++ b/klippy/extras/tmc2130.py @@ -199,23 +199,23 @@ class TMC2130CurrentHelper(tmc.BaseTMCCurrentHelper): - def __init__(self, config, mcu_tmc): + def __init__(self, config, mcu_tmc, type="tmc2130"): super().__init__(config, mcu_tmc, MAX_CURRENT) pconfig: PrinterConfig = self.printer.lookup_object("configfile") - self.sense_resistor = config.get("sense_resistor", None) + self.sense_resistor = config.get("sense_resistor", None, above=0.0) if self.sense_resistor is None: pconfig.warn( "config", - f"""[{self.name}] sense_resistor not specified; using default = 0.110. + f"""[{type} {self.name}] sense_resistor not specified; using default = 0.110. If this value is wrong, it might burn your house down. This parameter will be mandatory in future versions. Specify the parameter to resolve this warning""", - self.name, + f"{type} {self.name}", "sense_resistor", ) + self.sense_resistor = 0.110 - self.sense_resistor = config.getfloat("sense_resistor", 0.110, above=0.0) vsense, irun, ihold = self._calc_current( self.req_run_current, self.req_hold_current ) diff --git a/klippy/extras/tmc2208.py b/klippy/extras/tmc2208.py index 9f03f8d9a..4a13c0ebd 100644 --- a/klippy/extras/tmc2208.py +++ b/klippy/extras/tmc2208.py @@ -189,7 +189,7 @@ def __init__(self, config): ) self.fields.set_field("pdn_disable", True) # Register commands - current_helper = tmc2130.TMC2130CurrentHelper(config, self.mcu_tmc) + current_helper = tmc2130.TMC2130CurrentHelper(config, self.mcu_tmc, "tmc2208") cmdhelper = tmc.TMCCommandHelper(config, self.mcu_tmc, current_helper) cmdhelper.setup_register_dump(ReadRegisters, self.read_translate) self.get_phase_offset = cmdhelper.get_phase_offset diff --git a/klippy/extras/tmc2209.py b/klippy/extras/tmc2209.py index 2075575d7..7470c83bc 100644 --- a/klippy/extras/tmc2209.py +++ b/klippy/extras/tmc2209.py @@ -58,7 +58,7 @@ def __init__(self, config): # Allow virtual pins to be created tmc.TMCVirtualPinHelper(config, self.mcu_tmc) # Register commands - current_helper = tmc2130.TMC2130CurrentHelper(config, self.mcu_tmc) + current_helper = tmc2130.TMC2130CurrentHelper(config, self.mcu_tmc, "tmc2209") cmdhelper = tmc.TMCCommandHelper(config, self.mcu_tmc, current_helper) cmdhelper.setup_register_dump(ReadRegisters) self.get_phase_offset = cmdhelper.get_phase_offset diff --git a/klippy/extras/tmc2240.py b/klippy/extras/tmc2240.py index 7593f4766..27b4789ff 100644 --- a/klippy/extras/tmc2240.py +++ b/klippy/extras/tmc2240.py @@ -6,6 +6,7 @@ # This file may be distributed under the terms of the GNU GPLv3 license. import math from . import tmc, tmc2130, tmc_uart +from ..configfile import PrinterConfig TMC_FREQUENCY = 12500000.0 @@ -265,12 +266,25 @@ class TMC2240CurrentHelper(tmc.BaseTMCCurrentHelper): - def __init__(self, config, mcu_tmc): + def __init__(self, config, mcu_tmc, type="tmc2240"): self.printer = config.get_printer() + pconfig: PrinterConfig = self.printer.lookup_object("configfile") self.name = config.get_name().split()[-1] self.mcu_tmc = mcu_tmc self.fields = mcu_tmc.get_fields() - self.Rref = config.getfloat("rref", 12000.0, minval=12000.0, maxval=60000.0) + self.Rref = config.getfloat("rref", None, minval=12000.0, maxval=60000.0) + if self.Rref is None: + pconfig.warn( + "config", + f"""[{type} {self.name}] rref not specified; using default = 12000. + If this value is wrong, it might burn your house down. + This parameter will be mandatory in future versions. + Specify the parameter to resolve this warning""", + f"{type} {self.name}", + "rref", + ) + self.Rref = 12000.0 + self.max_current = self._get_ifs_rms(3) self.config_run_current = config.getfloat( "run_current", above=0.0, maxval=self.max_current diff --git a/klippy/extras/tmc2660.py b/klippy/extras/tmc2660.py index 1654323fe..5ce7f2177 100644 --- a/klippy/extras/tmc2660.py +++ b/klippy/extras/tmc2660.py @@ -6,6 +6,7 @@ # This file may be distributed under the terms of the GNU GPLv3 license. import math from . import bus, tmc, tmc2130 +from ..configfile import PrinterConfig Registers = { "DRVCONF": 0xE, @@ -115,11 +116,24 @@ class TMC2660CurrentHelper(tmc.BaseTMCCurrentHelper): - def __init__(self, config, mcu_tmc): + def __init__(self, config, mcu_tmc, type="tmc2660"): super().__init__(config, mcu_tmc, MAX_CURRENT) + pconfig: PrinterConfig = self.printer.lookup_object("configfile") self.current = self.req_run_current - self.sense_resistor = config.getfloat("sense_resistor") + self.sense_resistor = config.getfloat("sense_resistor", None, above=0.0) + if self.sense_resistor is None: + pconfig.warn( + "config", + f"""[{type} {self.name}] sense_resistor not specified; using default = 0.051. + If this value is wrong, it might burn your house down. + This parameter will be mandatory in future versions. + Specify the parameter to resolve this warning""", + f"{type} {self.name}", + "sense_resistor", + ) + self.sense_resistor = 0.051 + vsense, cs = self._calc_current(self.req_run_current) self.fields.set_field("cs", cs) self.fields.set_field("vsense", vsense) diff --git a/klippy/extras/tmc5160.py b/klippy/extras/tmc5160.py index 4cd91d5bb..573a460fe 100644 --- a/klippy/extras/tmc5160.py +++ b/klippy/extras/tmc5160.py @@ -257,7 +257,7 @@ def __init__(self, config, mcu_tmc): super().__init__(config, mcu_tmc, MAX_CURRENT) pconfig: PrinterConfig = self.printer.lookup_object("configfile") - self.sense_resistor = config.get("sense_resistor", None) + self.sense_resistor = config.get("sense_resistor", None, above=0.0) if self.sense_resistor is None: pconfig.warn( "config", @@ -265,11 +265,11 @@ def __init__(self, config, mcu_tmc): If this value is wrong, it might burn your house down. This parameter will be mandatory in future versions. Specify the parameter to resolve this warning""", - self.name, + f"{type} {self.name}", "sense_resistor", ) + self.sense_resistor = 0.075 - self.sense_resistor = config.getfloat("sense_resistor", 0.075, above=0.0) gscaler, irun, ihold = self._calc_current( self.req_run_current, self.req_hold_current ) From 6f7c69a8ce3abf080849fcb4269e116bc5f4d9a8 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Thu, 5 Sep 2024 13:34:25 +0200 Subject: [PATCH 113/158] . --- klippy/extras/tmc2130.py | 10 +++++----- klippy/extras/tmc2208.py | 2 +- klippy/extras/tmc2209.py | 2 +- klippy/extras/tmc2240.py | 18 ++++++++++++++++-- klippy/extras/tmc2660.py | 18 ++++++++++++++++-- klippy/extras/tmc5160.py | 6 +++--- 6 files changed, 42 insertions(+), 14 deletions(-) diff --git a/klippy/extras/tmc2130.py b/klippy/extras/tmc2130.py index 81d9583e2..7824efa60 100644 --- a/klippy/extras/tmc2130.py +++ b/klippy/extras/tmc2130.py @@ -199,23 +199,23 @@ class TMC2130CurrentHelper(tmc.BaseTMCCurrentHelper): - def __init__(self, config, mcu_tmc): + def __init__(self, config, mcu_tmc, type="tmc2130"): super().__init__(config, mcu_tmc, MAX_CURRENT) pconfig: PrinterConfig = self.printer.lookup_object("configfile") - self.sense_resistor = config.get("sense_resistor", None) + self.sense_resistor = config.get("sense_resistor", None, above=0.0) if self.sense_resistor is None: pconfig.warn( "config", - f"""[{self.name}] sense_resistor not specified; using default = 0.110. + f"""[{type} {self.name}] sense_resistor not specified; using default = 0.110. If this value is wrong, it might burn your house down. This parameter will be mandatory in future versions. Specify the parameter to resolve this warning""", - self.name, + f"{type} {self.name}", "sense_resistor", ) + self.sense_resistor = 0.110 - self.sense_resistor = config.getfloat("sense_resistor", 0.110, above=0.0) vsense, irun, ihold = self._calc_current( self.req_run_current, self.req_hold_current ) diff --git a/klippy/extras/tmc2208.py b/klippy/extras/tmc2208.py index 9f03f8d9a..4a13c0ebd 100644 --- a/klippy/extras/tmc2208.py +++ b/klippy/extras/tmc2208.py @@ -189,7 +189,7 @@ def __init__(self, config): ) self.fields.set_field("pdn_disable", True) # Register commands - current_helper = tmc2130.TMC2130CurrentHelper(config, self.mcu_tmc) + current_helper = tmc2130.TMC2130CurrentHelper(config, self.mcu_tmc, "tmc2208") cmdhelper = tmc.TMCCommandHelper(config, self.mcu_tmc, current_helper) cmdhelper.setup_register_dump(ReadRegisters, self.read_translate) self.get_phase_offset = cmdhelper.get_phase_offset diff --git a/klippy/extras/tmc2209.py b/klippy/extras/tmc2209.py index 2075575d7..7470c83bc 100644 --- a/klippy/extras/tmc2209.py +++ b/klippy/extras/tmc2209.py @@ -58,7 +58,7 @@ def __init__(self, config): # Allow virtual pins to be created tmc.TMCVirtualPinHelper(config, self.mcu_tmc) # Register commands - current_helper = tmc2130.TMC2130CurrentHelper(config, self.mcu_tmc) + current_helper = tmc2130.TMC2130CurrentHelper(config, self.mcu_tmc, "tmc2209") cmdhelper = tmc.TMCCommandHelper(config, self.mcu_tmc, current_helper) cmdhelper.setup_register_dump(ReadRegisters) self.get_phase_offset = cmdhelper.get_phase_offset diff --git a/klippy/extras/tmc2240.py b/klippy/extras/tmc2240.py index 7593f4766..27b4789ff 100644 --- a/klippy/extras/tmc2240.py +++ b/klippy/extras/tmc2240.py @@ -6,6 +6,7 @@ # This file may be distributed under the terms of the GNU GPLv3 license. import math from . import tmc, tmc2130, tmc_uart +from ..configfile import PrinterConfig TMC_FREQUENCY = 12500000.0 @@ -265,12 +266,25 @@ class TMC2240CurrentHelper(tmc.BaseTMCCurrentHelper): - def __init__(self, config, mcu_tmc): + def __init__(self, config, mcu_tmc, type="tmc2240"): self.printer = config.get_printer() + pconfig: PrinterConfig = self.printer.lookup_object("configfile") self.name = config.get_name().split()[-1] self.mcu_tmc = mcu_tmc self.fields = mcu_tmc.get_fields() - self.Rref = config.getfloat("rref", 12000.0, minval=12000.0, maxval=60000.0) + self.Rref = config.getfloat("rref", None, minval=12000.0, maxval=60000.0) + if self.Rref is None: + pconfig.warn( + "config", + f"""[{type} {self.name}] rref not specified; using default = 12000. + If this value is wrong, it might burn your house down. + This parameter will be mandatory in future versions. + Specify the parameter to resolve this warning""", + f"{type} {self.name}", + "rref", + ) + self.Rref = 12000.0 + self.max_current = self._get_ifs_rms(3) self.config_run_current = config.getfloat( "run_current", above=0.0, maxval=self.max_current diff --git a/klippy/extras/tmc2660.py b/klippy/extras/tmc2660.py index 1654323fe..5ce7f2177 100644 --- a/klippy/extras/tmc2660.py +++ b/klippy/extras/tmc2660.py @@ -6,6 +6,7 @@ # This file may be distributed under the terms of the GNU GPLv3 license. import math from . import bus, tmc, tmc2130 +from ..configfile import PrinterConfig Registers = { "DRVCONF": 0xE, @@ -115,11 +116,24 @@ class TMC2660CurrentHelper(tmc.BaseTMCCurrentHelper): - def __init__(self, config, mcu_tmc): + def __init__(self, config, mcu_tmc, type="tmc2660"): super().__init__(config, mcu_tmc, MAX_CURRENT) + pconfig: PrinterConfig = self.printer.lookup_object("configfile") self.current = self.req_run_current - self.sense_resistor = config.getfloat("sense_resistor") + self.sense_resistor = config.getfloat("sense_resistor", None, above=0.0) + if self.sense_resistor is None: + pconfig.warn( + "config", + f"""[{type} {self.name}] sense_resistor not specified; using default = 0.051. + If this value is wrong, it might burn your house down. + This parameter will be mandatory in future versions. + Specify the parameter to resolve this warning""", + f"{type} {self.name}", + "sense_resistor", + ) + self.sense_resistor = 0.051 + vsense, cs = self._calc_current(self.req_run_current) self.fields.set_field("cs", cs) self.fields.set_field("vsense", vsense) diff --git a/klippy/extras/tmc5160.py b/klippy/extras/tmc5160.py index 4cd91d5bb..573a460fe 100644 --- a/klippy/extras/tmc5160.py +++ b/klippy/extras/tmc5160.py @@ -257,7 +257,7 @@ def __init__(self, config, mcu_tmc): super().__init__(config, mcu_tmc, MAX_CURRENT) pconfig: PrinterConfig = self.printer.lookup_object("configfile") - self.sense_resistor = config.get("sense_resistor", None) + self.sense_resistor = config.get("sense_resistor", None, above=0.0) if self.sense_resistor is None: pconfig.warn( "config", @@ -265,11 +265,11 @@ def __init__(self, config, mcu_tmc): If this value is wrong, it might burn your house down. This parameter will be mandatory in future versions. Specify the parameter to resolve this warning""", - self.name, + f"{type} {self.name}", "sense_resistor", ) + self.sense_resistor = 0.075 - self.sense_resistor = config.getfloat("sense_resistor", 0.075, above=0.0) gscaler, irun, ihold = self._calc_current( self.req_run_current, self.req_hold_current ) From fb7ecce33dbee0b4f5b5bc12c4749271f7e07baf Mon Sep 17 00:00:00 2001 From: Zeanon Date: Thu, 5 Sep 2024 13:37:24 +0200 Subject: [PATCH 114/158] . --- klippy/extras/tmc2240.py | 2 +- klippy/extras/tmc2660.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/klippy/extras/tmc2240.py b/klippy/extras/tmc2240.py index 27b4789ff..c11521501 100644 --- a/klippy/extras/tmc2240.py +++ b/klippy/extras/tmc2240.py @@ -6,7 +6,7 @@ # This file may be distributed under the terms of the GNU GPLv3 license. import math from . import tmc, tmc2130, tmc_uart -from ..configfile import PrinterConfig +from configfile import PrinterConfig TMC_FREQUENCY = 12500000.0 diff --git a/klippy/extras/tmc2660.py b/klippy/extras/tmc2660.py index 5ce7f2177..c15e70d64 100644 --- a/klippy/extras/tmc2660.py +++ b/klippy/extras/tmc2660.py @@ -6,7 +6,7 @@ # This file may be distributed under the terms of the GNU GPLv3 license. import math from . import bus, tmc, tmc2130 -from ..configfile import PrinterConfig +from configfile import PrinterConfig Registers = { "DRVCONF": 0xE, From d35f41748a01c8fb3f290128196aa1a3c4b40e8d Mon Sep 17 00:00:00 2001 From: Zeanon Date: Thu, 5 Sep 2024 13:37:44 +0200 Subject: [PATCH 115/158] . --- klippy/extras/tmc2240.py | 2 +- klippy/extras/tmc2660.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/klippy/extras/tmc2240.py b/klippy/extras/tmc2240.py index 27b4789ff..c11521501 100644 --- a/klippy/extras/tmc2240.py +++ b/klippy/extras/tmc2240.py @@ -6,7 +6,7 @@ # This file may be distributed under the terms of the GNU GPLv3 license. import math from . import tmc, tmc2130, tmc_uart -from ..configfile import PrinterConfig +from configfile import PrinterConfig TMC_FREQUENCY = 12500000.0 diff --git a/klippy/extras/tmc2660.py b/klippy/extras/tmc2660.py index 5ce7f2177..c15e70d64 100644 --- a/klippy/extras/tmc2660.py +++ b/klippy/extras/tmc2660.py @@ -6,7 +6,7 @@ # This file may be distributed under the terms of the GNU GPLv3 license. import math from . import bus, tmc, tmc2130 -from ..configfile import PrinterConfig +from configfile import PrinterConfig Registers = { "DRVCONF": 0xE, From e4668210c7d0d26451fd53e145e7a2b383b06198 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sat, 7 Sep 2024 23:56:06 +0200 Subject: [PATCH 116/158] Update probe.py --- klippy/extras/probe.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/klippy/extras/probe.py b/klippy/extras/probe.py index 3995765e0..95adcf961 100644 --- a/klippy/extras/probe.py +++ b/klippy/extras/probe.py @@ -504,7 +504,7 @@ def __init__( self.probe_points = config.getlists( option_name, seps=(",", "\n"), parser=float, count=2 ) - self.move_z_speed = config.getfloat("horizontal_move_z_speed", None, above=0.0) + self.z_move_speed = config.getfloat("z_move_speed", None, above=0.0) self.default_horizontal_move_z = config.getfloat("horizontal_move_z", 5.0) if self.enable_adaptive_move_z: self.def_adaptive_horizontal_move_z = config.getboolean( @@ -573,8 +573,8 @@ def _lift_toolhead(self): if not self.results: # Use full speed to first probe position speed = self.speed - elif self.move_z_speed is not None: - speed = self.move_z_speed + elif self.z_move_speed is not None: + speed = self.z_move_speed toolhead.manual_move([None, None, self.horizontal_move_z], speed) def _move_next(self, speed=None): From 2fa472b64e98a447833ad93c4b188a1a980369dd Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sat, 7 Sep 2024 23:57:25 +0200 Subject: [PATCH 117/158] Update probe.py --- klippy/extras/probe.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/klippy/extras/probe.py b/klippy/extras/probe.py index 0fd777a7a..e4fad7016 100644 --- a/klippy/extras/probe.py +++ b/klippy/extras/probe.py @@ -502,7 +502,7 @@ def __init__( self.probe_points = config.getlists( option_name, seps=(",", "\n"), parser=float, count=2 ) - self.move_z_speed = config.getfloat("horizontal_move_z_speed", None, above=0.0) + self.z_move_speed = config.getfloat("z_move_speed", None, above=0.0) self.default_horizontal_move_z = config.getfloat("horizontal_move_z", 5.0) self.def_adaptive_horizontal_move_z = config.getboolean( "adaptive_horizontal_move_z", False @@ -568,8 +568,8 @@ def _lift_toolhead(self): if not self.results: # Use full speed to first probe position speed = self.speed - elif self.move_z_speed is not None: - speed = self.move_z_speed + elif self.z_move_speed is not None: + speed = self.z_move_speed toolhead.manual_move([None, None, self.horizontal_move_z], speed) def _move_next(self, speed=None): From 3888f7767ef3504f67e5812c5b9a27a4a54e8f80 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 8 Sep 2024 11:31:17 +0200 Subject: [PATCH 118/158] Update fan.py --- klippy/extras/fan.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index d9a796f3b..543b11c10 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -57,6 +57,7 @@ def __init__(self, config, default_shutdown_speed=0.0): "min_power=%f can't be larger than max_power=%f" % (self.min_power, self.max_power) ) + self.full_speed_max_power = config.getboolean("full_speed_max_power", False) cycle_time = config.getfloat("cycle_time", 0.010, above=0.0) hardware_pwm = config.getboolean("hardware_pwm", False) @@ -188,9 +189,12 @@ def get_mcu(self): def set_speed(self, print_time, value, force=False): if value > 0: - # Scale value between min_power and max_power - pwm_value = value * (self.max_power - self.min_power) + self.min_power - pwm_value = max(self.min_power, min(self.max_power, pwm_value)) + if self.full_speed_max_power and value == 1.0: + pwm_value = 1.0 + else: + # Scale value between min_power and max_power + pwm_value = value * (self.max_power - self.min_power) + self.min_power + pwm_value = max(self.min_power, min(self.max_power, pwm_value)) else: pwm_value = 0 if self.locking: @@ -221,8 +225,8 @@ def _set_speed( elif value == 0 and self.last_pwm_value > 0: self.enable_pin.set_digital(print_time, 0) if ( - value - and value < self.max_power + pwm_value + and pwm_value < self.max_power and self.kick_start_time and ( not self.last_fan_value @@ -373,7 +377,7 @@ def get_status(self, eventtime): def cmd_M106(self, gcmd): # Set fan speed - value = gcmd.get_float("S", 255.0, minval=0.0) / 255.0 + value = gcmd.get_float("S", 255.0, minval=0.0, maxval=255.0) / 255.0 force = gcmd.get_int("F", 0, minval=0, maxval=1) self.fan.set_speed_from_command(value, force) From 62ebda261ed3f6c190322caaa1a6dad438bb2418 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 8 Sep 2024 11:31:33 +0200 Subject: [PATCH 119/158] Update fan.py --- klippy/extras/fan.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index d9a796f3b..543b11c10 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -57,6 +57,7 @@ def __init__(self, config, default_shutdown_speed=0.0): "min_power=%f can't be larger than max_power=%f" % (self.min_power, self.max_power) ) + self.full_speed_max_power = config.getboolean("full_speed_max_power", False) cycle_time = config.getfloat("cycle_time", 0.010, above=0.0) hardware_pwm = config.getboolean("hardware_pwm", False) @@ -188,9 +189,12 @@ def get_mcu(self): def set_speed(self, print_time, value, force=False): if value > 0: - # Scale value between min_power and max_power - pwm_value = value * (self.max_power - self.min_power) + self.min_power - pwm_value = max(self.min_power, min(self.max_power, pwm_value)) + if self.full_speed_max_power and value == 1.0: + pwm_value = 1.0 + else: + # Scale value between min_power and max_power + pwm_value = value * (self.max_power - self.min_power) + self.min_power + pwm_value = max(self.min_power, min(self.max_power, pwm_value)) else: pwm_value = 0 if self.locking: @@ -221,8 +225,8 @@ def _set_speed( elif value == 0 and self.last_pwm_value > 0: self.enable_pin.set_digital(print_time, 0) if ( - value - and value < self.max_power + pwm_value + and pwm_value < self.max_power and self.kick_start_time and ( not self.last_fan_value @@ -373,7 +377,7 @@ def get_status(self, eventtime): def cmd_M106(self, gcmd): # Set fan speed - value = gcmd.get_float("S", 255.0, minval=0.0) / 255.0 + value = gcmd.get_float("S", 255.0, minval=0.0, maxval=255.0) / 255.0 force = gcmd.get_int("F", 0, minval=0, maxval=1) self.fan.set_speed_from_command(value, force) From 98cd7dadd006c89100f0edeede274b99d5abee3e Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 8 Sep 2024 22:53:56 +0200 Subject: [PATCH 120/158] . --- klippy/extras/z_tilt.py | 2 +- klippy/extras/z_tilt_ng.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/klippy/extras/z_tilt.py b/klippy/extras/z_tilt.py index 2451862a9..cbff4b5cb 100644 --- a/klippy/extras/z_tilt.py +++ b/klippy/extras/z_tilt.py @@ -181,7 +181,7 @@ def check_retry(self, z_positions): % (self.value_label, self.error_msg_extra) ) if error <= self.retry_tolerance: - return 0.0 + return "done" self.current_retry += 1 if self.current_retry > self.max_retries: raise self.gcode.error("Too many retries") diff --git a/klippy/extras/z_tilt_ng.py b/klippy/extras/z_tilt_ng.py index 1ce1ad055..f446a1c36 100644 --- a/klippy/extras/z_tilt_ng.py +++ b/klippy/extras/z_tilt_ng.py @@ -202,7 +202,7 @@ def check_retry(self, z_positions): % (self.value_label, self.error_msg_extra) ) if error <= self.retry_tolerance: - return 0.0 + return "done" self.current_retry += 1 if self.current_retry > self.max_retries: raise self.gcode.error("Too many retries") From 2a68c2fed6291c3bc0a6633dcd4b28edc2b9f752 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 10 Sep 2024 23:26:47 +0200 Subject: [PATCH 121/158] Update tmc5160.py --- klippy/extras/tmc5160.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/klippy/extras/tmc5160.py b/klippy/extras/tmc5160.py index 573a460fe..a7a668c13 100644 --- a/klippy/extras/tmc5160.py +++ b/klippy/extras/tmc5160.py @@ -253,7 +253,7 @@ class TMC5160CurrentHelper(tmc.BaseTMCCurrentHelper): - def __init__(self, config, mcu_tmc): + def __init__(self, config, mcu_tmc, type="tmc5160"): super().__init__(config, mcu_tmc, MAX_CURRENT) pconfig: PrinterConfig = self.printer.lookup_object("configfile") @@ -261,7 +261,7 @@ def __init__(self, config, mcu_tmc): if self.sense_resistor is None: pconfig.warn( "config", - f"""[{self.name}] sense_resistor not specified; using default = 0.075. + f"""[{type} {self.name}] sense_resistor not specified; using default = 0.075. If this value is wrong, it might burn your house down. This parameter will be mandatory in future versions. Specify the parameter to resolve this warning""", From 1f7d05dda15d3f4fe7c8561daab91942875a673d Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 10 Sep 2024 23:27:04 +0200 Subject: [PATCH 122/158] Update tmc5160.py --- klippy/extras/tmc5160.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/klippy/extras/tmc5160.py b/klippy/extras/tmc5160.py index 573a460fe..a7a668c13 100644 --- a/klippy/extras/tmc5160.py +++ b/klippy/extras/tmc5160.py @@ -253,7 +253,7 @@ class TMC5160CurrentHelper(tmc.BaseTMCCurrentHelper): - def __init__(self, config, mcu_tmc): + def __init__(self, config, mcu_tmc, type="tmc5160"): super().__init__(config, mcu_tmc, MAX_CURRENT) pconfig: PrinterConfig = self.printer.lookup_object("configfile") @@ -261,7 +261,7 @@ def __init__(self, config, mcu_tmc): if self.sense_resistor is None: pconfig.warn( "config", - f"""[{self.name}] sense_resistor not specified; using default = 0.075. + f"""[{type} {self.name}] sense_resistor not specified; using default = 0.075. If this value is wrong, it might burn your house down. This parameter will be mandatory in future versions. Specify the parameter to resolve this warning""", From 17d95cf9b5ed40a153b88de2c18fc16a7ad83071 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 10 Sep 2024 23:27:45 +0200 Subject: [PATCH 123/158] Update tmc5160.py --- klippy/extras/tmc5160.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/tmc5160.py b/klippy/extras/tmc5160.py index a7a668c13..6c20b1592 100644 --- a/klippy/extras/tmc5160.py +++ b/klippy/extras/tmc5160.py @@ -257,7 +257,7 @@ def __init__(self, config, mcu_tmc, type="tmc5160"): super().__init__(config, mcu_tmc, MAX_CURRENT) pconfig: PrinterConfig = self.printer.lookup_object("configfile") - self.sense_resistor = config.get("sense_resistor", None, above=0.0) + self.sense_resistor = config.getfloat("sense_resistor", None, above=0.0) if self.sense_resistor is None: pconfig.warn( "config", From 3b178ec860a4ff791d8769e8443517be6096bcb0 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 10 Sep 2024 23:27:53 +0200 Subject: [PATCH 124/158] Update tmc5160.py --- klippy/extras/tmc5160.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/tmc5160.py b/klippy/extras/tmc5160.py index a7a668c13..6c20b1592 100644 --- a/klippy/extras/tmc5160.py +++ b/klippy/extras/tmc5160.py @@ -257,7 +257,7 @@ def __init__(self, config, mcu_tmc, type="tmc5160"): super().__init__(config, mcu_tmc, MAX_CURRENT) pconfig: PrinterConfig = self.printer.lookup_object("configfile") - self.sense_resistor = config.get("sense_resistor", None, above=0.0) + self.sense_resistor = config.getfloat("sense_resistor", None, above=0.0) if self.sense_resistor is None: pconfig.warn( "config", From 5c5ad3a8b1a244ee710c1c5f30fcedcf1e5205c2 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 11 Sep 2024 14:07:34 +0200 Subject: [PATCH 125/158] Update temperature_combined.py --- klippy/extras/temperature_combined.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/klippy/extras/temperature_combined.py b/klippy/extras/temperature_combined.py index 1fb49ffcc..3411bfbbb 100644 --- a/klippy/extras/temperature_combined.py +++ b/klippy/extras/temperature_combined.py @@ -91,7 +91,8 @@ def update_temp(self, eventtime): if sensor.initialized: sensor_status = sensor.get_status(eventtime) sensor_temperature = sensor_status["temperature"] - values.append(sensor_temperature) + if sensor_temperature is not None: + values.append(sensor_temperature) if values: # check if values are out of max_deviation range @@ -107,8 +108,8 @@ def update_temp(self, eventtime): ) temp = self.apply_mode(values) - self.last_temp = temp - if temp: + if temp is not None: + self.last_temp = temp self.measured_min = min(self.measured_min, temp) self.measured_max = max(self.measured_max, temp) @@ -159,7 +160,7 @@ def _temperature_update_event(self): def mean(values): if not values: - return + return None return sum(values) / len(values) From c0e4d0cf4822ad471e2aa1aaaef0764ab64c73fd Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 11 Sep 2024 14:07:48 +0200 Subject: [PATCH 126/158] Update temperature_combined.py --- klippy/extras/temperature_combined.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/klippy/extras/temperature_combined.py b/klippy/extras/temperature_combined.py index 1fb49ffcc..3411bfbbb 100644 --- a/klippy/extras/temperature_combined.py +++ b/klippy/extras/temperature_combined.py @@ -91,7 +91,8 @@ def update_temp(self, eventtime): if sensor.initialized: sensor_status = sensor.get_status(eventtime) sensor_temperature = sensor_status["temperature"] - values.append(sensor_temperature) + if sensor_temperature is not None: + values.append(sensor_temperature) if values: # check if values are out of max_deviation range @@ -107,8 +108,8 @@ def update_temp(self, eventtime): ) temp = self.apply_mode(values) - self.last_temp = temp - if temp: + if temp is not None: + self.last_temp = temp self.measured_min = min(self.measured_min, temp) self.measured_max = max(self.measured_max, temp) @@ -159,7 +160,7 @@ def _temperature_update_event(self): def mean(values): if not values: - return + return None return sum(values) / len(values) From 292a509a97278bc3b45306b7c7aed12b927ece6e Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 11 Sep 2024 14:23:17 +0200 Subject: [PATCH 127/158] . --- docs/Config_Reference.md | 10 ++++++++++ klippy/extras/tmc2240.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index c8a6b4376..bef85850c 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -4259,6 +4259,16 @@ run_current: # velocity" threshold (THIGH) to. This is typically used to disable # the "CoolStep" feature at high speeds. The default is to not set a # TMC "high velocity" threshold. +#overvoltage_threshold: 37.735 +# The threshold voltage (in V) of overvoltage protection. The decelerating +# stepper motor can cause a raise of V_supply of the driver. When the V_supply +# is higher than overvoltage_threshold, the mosfet is turned on and dump the +# excess energy into the power resistor, keep the supply voltage below +# the threshold. +#overtemperature_warning_threshold: 120.0 +# The threshold temperature (in ℃) of the overtemperature warning. +# When the temperature of the driver is higher than this threshold, +# a warning will be reported. #driver_MSLUT0: 2863314260 #driver_MSLUT1: 1251300522 #driver_MSLUT2: 608774441 diff --git a/klippy/extras/tmc2240.py b/klippy/extras/tmc2240.py index c11521501..1316001f3 100644 --- a/klippy/extras/tmc2240.py +++ b/klippy/extras/tmc2240.py @@ -379,6 +379,38 @@ def apply_current(self, print_time): self.mcu_tmc.set_register("IHOLD_IRUN", val, print_time) +###################################################################### +# TMC stepper overvoltage config helper +###################################################################### + + +def TMC2240OvervoltageHelper(config, mcu_tmc): + fields = mcu_tmc.get_fields() + ov_thres = config.getfloat( + "overvoltage_threshold", + 37.735, # 0xF25 by default + maxval=40.0, + ) + overvoltage_vth = int(ov_thres / 0.009732) + fields.set_field("overvoltage_vth", overvoltage_vth) + + +###################################################################### +# TMC stepper overtemperature warning config helper +###################################################################### + + +def TMC2240OvertemperatureWarningHelper(config, mcu_tmc): + fields = mcu_tmc.get_fields() + otw_thres = config.getfloat( + "overtemperature_warning_threshold", + 120.0, # 0xB92 by default + maxval=120.0, + ) + overtempprewarning_vth = int((otw_thres * 7.7) + 2038) + fields.set_field("overtempprewarning_vth", overtempprewarning_vth) + + ###################################################################### # TMC2240 printer object ###################################################################### @@ -402,6 +434,8 @@ def __init__(self, config): tmc.TMCVirtualPinHelper(config, self.mcu_tmc) # Register commands current_helper = TMC2240CurrentHelper(config, self.mcu_tmc) + TMC2240OvervoltageHelper(config, self.mcu_tmc) + TMC2240OvertemperatureWarningHelper(config, self.mcu_tmc) cmdhelper = tmc.TMCCommandHelper(config, self.mcu_tmc, current_helper) cmdhelper.setup_register_dump(ReadRegisters) self.get_phase_offset = cmdhelper.get_phase_offset From 4a457ecb4641ad294b616e09551151d788fda932 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 11 Sep 2024 14:23:32 +0200 Subject: [PATCH 128/158] Update tmc2240.py --- klippy/extras/tmc2240.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/klippy/extras/tmc2240.py b/klippy/extras/tmc2240.py index c11521501..1316001f3 100644 --- a/klippy/extras/tmc2240.py +++ b/klippy/extras/tmc2240.py @@ -379,6 +379,38 @@ def apply_current(self, print_time): self.mcu_tmc.set_register("IHOLD_IRUN", val, print_time) +###################################################################### +# TMC stepper overvoltage config helper +###################################################################### + + +def TMC2240OvervoltageHelper(config, mcu_tmc): + fields = mcu_tmc.get_fields() + ov_thres = config.getfloat( + "overvoltage_threshold", + 37.735, # 0xF25 by default + maxval=40.0, + ) + overvoltage_vth = int(ov_thres / 0.009732) + fields.set_field("overvoltage_vth", overvoltage_vth) + + +###################################################################### +# TMC stepper overtemperature warning config helper +###################################################################### + + +def TMC2240OvertemperatureWarningHelper(config, mcu_tmc): + fields = mcu_tmc.get_fields() + otw_thres = config.getfloat( + "overtemperature_warning_threshold", + 120.0, # 0xB92 by default + maxval=120.0, + ) + overtempprewarning_vth = int((otw_thres * 7.7) + 2038) + fields.set_field("overtempprewarning_vth", overtempprewarning_vth) + + ###################################################################### # TMC2240 printer object ###################################################################### @@ -402,6 +434,8 @@ def __init__(self, config): tmc.TMCVirtualPinHelper(config, self.mcu_tmc) # Register commands current_helper = TMC2240CurrentHelper(config, self.mcu_tmc) + TMC2240OvervoltageHelper(config, self.mcu_tmc) + TMC2240OvertemperatureWarningHelper(config, self.mcu_tmc) cmdhelper = tmc.TMCCommandHelper(config, self.mcu_tmc, current_helper) cmdhelper.setup_register_dump(ReadRegisters) self.get_phase_offset = cmdhelper.get_phase_offset From efe01333580262631c5997ad8eac3fc8f689b53c Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 11 Sep 2024 14:25:00 +0200 Subject: [PATCH 129/158] Update Config_Reference.md --- docs/Config_Reference.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index 90851352d..27a90e70a 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -4255,6 +4255,16 @@ run_current: # velocity" threshold (THIGH) to. This is typically used to disable # the "CoolStep" feature at high speeds. The default is to not set a # TMC "high velocity" threshold. +#overvoltage_threshold: 37.735 +# The threshold voltage (in V) of overvoltage protection. The decelerating +# stepper motor can cause a raise of V_supply of the driver. When the V_supply +# is higher than overvoltage_threshold, the mosfet is turned on and dump the +# excess energy into the power resistor, keep the supply voltage below +# the threshold. +#overtemperature_warning_threshold: 120.0 +# The threshold temperature (in ℃) of the overtemperature warning. +# When the temperature of the driver is higher than this threshold, +# a warning will be reported. #driver_MSLUT0: 2863314260 #driver_MSLUT1: 1251300522 #driver_MSLUT2: 608774441 From e518840625e1f31116519e1239e4688f0b63413d Mon Sep 17 00:00:00 2001 From: Zeanon Date: Tue, 10 Sep 2024 17:45:35 +0200 Subject: [PATCH 130/158] Add new Dragon Ace heater to MPC doc Added the new dragon ace "high power" heater to the MPC power-temp chart --- docs/MPC.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/MPC.md b/docs/MPC.md index 61562b5c2..5e69c435a 100644 --- a/docs/MPC.md +++ b/docs/MPC.md @@ -90,15 +90,15 @@ These can be specified in the config but should not need to be changed from the The `heater power:` for PTC style heaters is recommended to be set at the normal print temperature for the printer. Some common PTC heaters are given below for reference. If your heater is not listed the manufacturer should be able to provide a temperature and power curve. -| Heater Temp (C) | Rapido 2 (W) | Rapido 1 (W) | Dragon Ace (W) | Revo 40 (W) |Revo 60 (W) | -|:---------------:|:------------:|:------------:|:--------------:|:-----------:|:----------:| -| 180 | 72 | 52 | 51 | 30 |45 | -| 200 | 70 | 51 | 48 | 29 |44 | -| 220 | 67 | 50 | 46 | 28 |43 | -| 240 | 65 | 49 | 44 | 28 |42 | -| 260 | 64 | 48 | 43 | 27 |40 | -| 280 | 62 | 47 | 41 | 27 |39 | -| 300 | 60 | 46 | 39 | 26 |38 | +| Heater Temp (C) | Rapido 2 (W) | Rapido 1 (W) | Dragon Ace old (W) | Dragon Ace new (W) | Revo 40 (W) |Revo 60 (W) | +|:---------------:|:------------:|:------------:|:------------------:|:------------------:|:-----------:|:----------:| +| 180 | 72 | 52 | 51 | 66 | 30 |45 | +| 200 | 70 | 51 | 48 | 63 | 29 |44 | +| 220 | 67 | 50 | 46 | 60 | 28 |43 | +| 240 | 65 | 49 | 44 | 58 | 28 |42 | +| 260 | 64 | 48 | 43 | 55 | 27 |40 | +| 280 | 62 | 47 | 41 | 53 | 27 |39 | +| 300 | 60 | 46 | 39 | 51 | 26 |38 | ## Filament Feed Forward Configuration From 75f31bcf2de0fedb03c6dfa2c388380c5cde7bf2 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 11 Sep 2024 14:45:36 +0200 Subject: [PATCH 131/158] Update graph_temp_sensor.py --- scripts/graph_temp_sensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/graph_temp_sensor.py b/scripts/graph_temp_sensor.py index 769c04742..03622046b 100755 --- a/scripts/graph_temp_sensor.py +++ b/scripts/graph_temp_sensor.py @@ -32,10 +32,10 @@ def get_name(self): return "dummy" # Emulate printer class - def load_object(self, config, name): + def load_object(self, config, name, default=None): return self - def lookup_object(self, name): + def lookup_object(self, name, default=None): return self # Emulate heaters class From b0fba53fc0013bcf9d2254e3736a2490072b72f0 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 11 Sep 2024 14:46:53 +0200 Subject: [PATCH 132/158] Update temperature_mcu.py --- klippy/extras/temperature_mcu.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/klippy/extras/temperature_mcu.py b/klippy/extras/temperature_mcu.py index ea8c8d82d..f87291fc7 100644 --- a/klippy/extras/temperature_mcu.py +++ b/klippy/extras/temperature_mcu.py @@ -72,7 +72,8 @@ def __init__(self, config): self.mcu_adc.get_mcu().register_config_callback(self._build_config) def handle_beacon_ready(self): - self.beacon_mcu_temp_wrapper.activate_wrapper(self.config) + if self.beacon_mcu_temp_wrapper is not None: + self.beacon_mcu_temp_wrapper.activate_wrapper(self.config) def get_report_time_delta(self): if self.beacon_mcu_temp_wrapper is not None: From a90f7ee2bb5f866771fe8a10eb149c2b9402783b Mon Sep 17 00:00:00 2001 From: Zeanon Date: Thu, 12 Sep 2024 14:41:45 +0200 Subject: [PATCH 133/158] Update heaters.py --- klippy/extras/heaters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/heaters.py b/klippy/extras/heaters.py index 309a2a383..174d5807d 100644 --- a/klippy/extras/heaters.py +++ b/klippy/extras/heaters.py @@ -661,7 +661,7 @@ def temperature_update(self, read_time, temp, target_temp): self.temps.pop(0) self.temps.append(temp) self.smoothed_temps.pop(0) - self.smoothed_temps.append(self.median(self.temps[-self.smoothing_elements :])) + self.smoothed_temps.append(self.median(self.temps[-self.smoothing_elements:])) self.times.pop(0) self.times.append(read_time) From 980b2c22d9c55cee0184b0b20847561bda1ef7cb Mon Sep 17 00:00:00 2001 From: Zeanon Date: Fri, 13 Sep 2024 15:00:23 +0200 Subject: [PATCH 134/158] . --- klippy/extras/fan.py | 1 + klippy/extras/heater_profile_manager.py | 193 ++++--- klippy/extras/heaters.py | 638 +++++++++++++----------- klippy/extras/mpc_calibrate.py | 103 ++-- klippy/extras/pid_calibrate.py | 27 +- 5 files changed, 507 insertions(+), 455 deletions(-) diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py index 543b11c10..85a5a2edb 100644 --- a/klippy/extras/fan.py +++ b/klippy/extras/fan.py @@ -14,6 +14,7 @@ class Fan: def __init__(self, config, default_shutdown_speed=0.0): self.printer = config.get_printer() + self.name = config.get_name().split()[-1] self.gcode = self.printer.lookup_object("gcode") self.reactor = self.printer.get_reactor() self.estimated_print_time = None diff --git a/klippy/extras/heater_profile_manager.py b/klippy/extras/heater_profile_manager.py index 8fcb459ad..48765b957 100644 --- a/klippy/extras/heater_profile_manager.py +++ b/klippy/extras/heater_profile_manager.py @@ -2,54 +2,81 @@ class ProfileManager: - def __init__(self, outer_instance, control_types): - self.outer_instance = outer_instance + def __init__(self, heater, control_types): + self.heater = heater self.control_types = control_types self.profiles = {} self.incompatible_profiles = [] # Fetch stored profiles from Config - stored_profs = self.outer_instance.config.get_prefix_sections( - "heater_profile %s" % self.outer_instance.sensor_name + stored_profs = self.heater.config.get_prefix_sections( + "heater_profile %s" % self.heater.sensor_name ) for profile in stored_profs: - if len(self.outer_instance.sensor_name.split(" ")) > 1: + if len(self.heater.sensor_name.split(" ")) > 1: name = profile.get_name().split(" ", 3)[-1] else: name = profile.get_name().split(" ", 2)[-1] self._init_profile(profile, name) - def _init_profile(self, config_section, name): - control = self._check_value_config("control", config_section, str, False) + def _init_profile(self, config_section, name, force_control=None): + control = self._check_value_config( + "control", config_section, str, False, default=force_control + ) if control in self.control_types.keys(): temp_profile = self.control_types[control].init_profile( config_section, name, self ) if temp_profile is not None: - temp_profile["control"] = control temp_profile["name"] = name + temp_profile["control"] = control self.profiles[name] = temp_profile return temp_profile else: - raise self.outer_instance.printer.config_error( + raise self.heater.printer.config_error( "Unknown control type '%s' " - "in [heater_profile %s %s]." - % (control, self.outer_instance.sensor_name, name) + "in [heater_profile %s %s]." % (control, self.heater.sensor_name, name) ) - def _check_value_config(self, key, config_section, type, can_be_none): + def _check_value_config( + self, + key, + config_section, + type, + can_be_none, + default=None, + above=None, + minval=None, + ): if type is int: - value = config_section.getint(key, None) + value = config_section.getint(key, default=default, minval=minval) elif type is float: - value = config_section.getfloat(key, None) + value = config_section.getfloat( + key, default=default, minval=minval, above=above + ) + elif isinstance(type, str) and type == "floatlist": + value = config_section.getfloatlist(key, default=default) + elif ( + isinstance(type, list) + and len(type) == 4 + and isinstance(type[0], str) + and type[0] == "lists" + ): + value = config_section.getlists( + key, + seps=type[1], + parser=type[2], + count=type[3], + default=default, + ) else: - value = config_section.get(key, None) + value = config_section.get(key, default=default) if not can_be_none and value is None: - raise self.outer_instance.gcode.error( + raise self.heater.gcode.error( "heater_profile: '%s' has to be " "specified in [heater_profile %s %s]." % ( key, - self.outer_instance.sensor_name, + self.heater.sensor_name, config_section.get_name(), ) ) @@ -57,11 +84,9 @@ def _check_value_config(self, key, config_section, type, can_be_none): def _compute_section_name(self, profile_name): return ( - self.outer_instance.sensor_name + self.heater.sensor_name if profile_name == "default" - else ( - "heater_profile " + self.outer_instance.sensor_name + " " + profile_name - ) + else ("heater_profile " + self.heater.sensor_name + " " + profile_name) ) def _check_value_gcmd( @@ -81,69 +106,30 @@ def _check_value_gcmd( else: value = gcmd.get(name, default) if not can_be_none and value is None: - raise self.outer_instance.gcode.error( + raise self.heater.gcode.error( "heater_profile: '%s' has to be specified." % name ) return value.lower() if type == "lower" else value def init_default_profile(self): - return self._init_profile(self.outer_instance.sensor_config, "default") + return self._init_profile(self.heater.sensor_config, "default") - def set_values(self, profile_name, gcmd, verbose): - current_profile = self.outer_instance.get_control().get_profile() - target = self._check_value_gcmd("TARGET", None, gcmd, float, False) - tolerance = self._check_value_gcmd( - "TOLERANCE", - current_profile["pid_tolerance"], - gcmd, - float, - False, - ) + def set_values(self, profile_name, gcmd, verbose=True): + current_profile = self.heater.get_control().get_profile() control = self._check_value_gcmd( - "CONTROL", current_profile["control"], gcmd, "lower", False - ) - kp = self._check_value_gcmd("KP", None, gcmd, float, False) - ki = self._check_value_gcmd("KI", None, gcmd, float, False) - kd = self._check_value_gcmd("KD", None, gcmd, float, False) - smooth_time = self._check_value_gcmd("SMOOTH_TIME", None, gcmd, float, True) - smoothing_elements = self._check_value_gcmd( - "SMOOTHING_ELEMENTS", None, gcmd, int, True - ) - keep_target = self._check_value_gcmd( - "KEEP_TARGET", 0, gcmd, int, True, minval=0, maxval=1 - ) - load_clean = self._check_value_gcmd( - "LOAD_CLEAN", 0, gcmd, int, True, minval=0, maxval=1 + "CONTROL", current_profile["control"], gcmd, "lower", True ) - temp_profile = { - "pid_target": target, - "pid_tolerance": tolerance, - "control": control, - "smooth_time": smooth_time, - "smoothing_elements": smoothing_elements, - "pid_kp": kp, - "pid_ki": ki, - "pid_kd": kd, - } - temp_control = self.outer_instance.lookup_control(temp_profile, load_clean) - self.outer_instance.set_control(temp_control, keep_target) - msg = ( - "PID Parameters:\n" - "Target: %.2f,\n" - "Tolerance: %.4f\n" - "Control: %s\n" % (target, tolerance, control) + save_profile = self._check_value_gcmd( + "SAVE_PROFILE", True, gcmd, int, True, minval=0, maxval=1 ) - if smooth_time is not None: - msg += "Smooth Time: %.3f\n" % smooth_time - msg += ( - "pid_Kp=%.3f pid_Ki=%.3f pid_Kd=%.3f\n" - "have been set as current profile." % (kp, ki, kd) + self.control_types[control].set_values( + pmgr=self, gcmd=gcmd, control=control, profile_name=profile_name ) - self.outer_instance.gcode.respond_info(msg) - self.save_profile(profile_name=profile_name, verbose=True) + if save_profile: + self.save_profile(profile_name=profile_name, gcmd=gcmd, verbose=verbose) - def get_values(self, profile_name, gcmd, verbose): - temp_profile = self.outer_instance.get_control().get_profile() + def get_values(self, profile_name, gcmd): + temp_profile = self.heater.get_control().get_profile() target = temp_profile["pid_target"] tolerance = temp_profile["pid_tolerance"] control = temp_profile["control"] @@ -151,17 +137,17 @@ def get_values(self, profile_name, gcmd, verbose): ki = temp_profile["pid_ki"] kd = temp_profile["pid_kd"] smooth_time = ( - self.outer_instance.get_smooth_time() + self.heater.get_smooth_time() if temp_profile["smooth_time"] is None else temp_profile["smooth_time"] ) smoothing_elements = ( - self.outer_instance.get_smoothing_elements() + self.heater.get_smoothing_elements() if temp_profile["smoothing_elements"] is None else temp_profile["smoothing_elements"] ) name = temp_profile["name"] - self.outer_instance.gcode.respond_info( + self.heater.gcode.respond_info( "PID Parameters:\n" "Target: %.2f,\n" "Tolerance: %.4f\n" @@ -184,7 +170,7 @@ def get_values(self, profile_name, gcmd, verbose): ) def save_profile(self, profile_name=None, gcmd=None, verbose=True): - temp_profile = self.outer_instance.get_control().get_profile() + temp_profile = self.heater.get_control().get_profile() self.control_types[temp_profile["control"]].save_profile( pmgr=self, temp_profile=temp_profile, @@ -192,20 +178,22 @@ def save_profile(self, profile_name=None, gcmd=None, verbose=True): gcmd=gcmd, verbose=verbose, ) + if profile_name is not None: + self.heater.get_control().set_name(profile_name) - def load_profile(self, profile_name, gcmd, verbose): + def load_profile(self, profile_name, gcmd): verbose = self._check_value_gcmd("VERBOSE", "low", gcmd, "lower", True) load_clean = self._check_value_gcmd( "LOAD_CLEAN", 0, gcmd, int, True, minval=0, maxval=1 ) if ( - profile_name == self.outer_instance.get_control().get_profile()["name"] + profile_name == self.heater.get_control().get_profile()["name"] and not load_clean ): if verbose == "high" or verbose == "low": - self.outer_instance.gcode.respond_info( + self.heater.gcode.respond_info( "Heater Profile [%s] already loaded for heater [%s]." - % (profile_name, self.outer_instance.sensor_name) + % (profile_name, self.heater.sensor_name) ) return keep_target = self._check_value_gcmd( @@ -216,42 +204,41 @@ def load_profile(self, profile_name, gcmd, verbose): default = gcmd.get("DEFAULT", None) if profile is None: if default is None: - raise self.outer_instance.gcode.error( + raise self.heater.gcode.error( "heater_profile: Unknown profile [%s] for heater [%s]." - % (profile_name, self.outer_instance.sensor_name) + % (profile_name, self.heater.sensor_name) ) profile = self.profiles.get(default, None) defaulted = True if profile is None: - raise self.outer_instance.gcode.error( + raise self.heater.gcode.error( "heater_profile: Unknown default " - "profile [%s] for heater [%s]." - % (default, self.outer_instance.sensor_name) + "profile [%s] for heater [%s]." % (default, self.heater.sensor_name) ) - control = self.outer_instance.lookup_control(profile, load_clean) - self.outer_instance.set_control(control, keep_target) + control = self.heater.lookup_control(profile, load_clean) + self.heater.set_control(control, keep_target) if verbose != "high" and verbose != "low": return if defaulted: - self.outer_instance.gcode.respond_info( + self.heater.gcode.respond_info( "Couldn't find profile " "[%s] for heater [%s]" ", defaulted to [%s]." - % (profile_name, self.outer_instance.sensor_name, default) + % (profile_name, self.heater.sensor_name, default) ) - self.outer_instance.gcode.respond_info( + self.heater.gcode.respond_info( "Heater Profile [%s] loaded for heater [%s].\n" - % (profile["name"], self.outer_instance.sensor_name) + % (profile["name"], self.heater.sensor_name) ) if verbose == "high": smooth_time = ( - self.outer_instance.get_smooth_time() + self.heater.get_smooth_time() if profile["smooth_time"] is None else profile["smooth_time"] ) smoothing_elements = ( - self.outer_instance.get_smoothing_elements() + self.heater.get_smoothing_elements() if profile["smoothing_elements"] is None else profile["smoothing_elements"] ) @@ -274,24 +261,24 @@ def load_profile(self, profile_name, gcmd, verbose): profile["pid_ki"], profile["pid_kd"], ) - self.outer_instance.gcode.respond_info(msg) + self.heater.gcode.respond_info(msg) - def remove_profile(self, profile_name, gcmd, verbose): + def remove_profile(self, profile_name, gcmd): if profile_name in self.profiles: section_name = self._compute_section_name(profile_name) - self.outer_instance.configfile.remove_section(section_name) + self.heater.configfile.remove_section(section_name) profiles = dict(self.profiles) del profiles[profile_name] self.profiles = profiles - self.outer_instance.gcode.respond_info( + self.heater.gcode.respond_info( "Profile [%s] for heater [%s] " "removed from storage for this session.\n" "The SAVE_CONFIG command will update the printer\n" "configuration and restart the printer" - % (profile_name, self.outer_instance.sensor_name) + % (profile_name, self.heater.sensor_name) ) else: - self.outer_instance.gcode.respond_info( + self.heater.gcode.respond_info( "No profile named [%s] to remove" % profile_name ) @@ -311,11 +298,11 @@ def cmd_HEATER_PROFILE(self, gcmd): profile_name = gcmd.get(key, None) if profile_name is not None: if not profile_name.strip(): - raise self.outer_instance.gcode.error( + raise self.heater.gcode.error( "heater_profile: Profile must be specified" ) - options[key](profile_name, gcmd, True) + options[key](profile_name, gcmd) return - raise self.outer_instance.gcode.error( + raise self.heater.gcode.error( "heater_profile: Invalid syntax '%s'" % (gcmd.get_commandline(),) ) diff --git a/klippy/extras/heaters.py b/klippy/extras/heaters.py index 174d5807d..88ce9190f 100644 --- a/klippy/extras/heaters.py +++ b/klippy/extras/heaters.py @@ -20,15 +20,44 @@ AMBIENT_TEMP = 25.0 PID_PARAM_BASE = 255.0 HEATER_PROFILE_VERSION = 1 + +# (type, placeholder, default, can_be_none) +WATERMARK_PROFILE_OPTIONS = { + "control": (str, "%s", "watermark", False), + "max_delta": (float, "%.4f", 2.0, True), +} +# (type, placeholder, default, can_be_none) PID_PROFILE_OPTIONS = { - "pid_target": (float, "%.2f"), - "pid_tolerance": (float, "%.4f"), - "control": (str, "%s"), - "smooth_time": (float, "%.3f"), - "smoothing_elements": (int, "%d"), - "pid_kp": (float, "%.3f"), - "pid_ki": (float, "%.3f"), - "pid_kd": (float, "%.3f"), + "pid_target": (float, "%.2f", None, True), + "pid_tolerance": (float, "%.4f", None, True), + "control": (str, "%s", "pid", False), + "smooth_time": (float, "%.3f", None, True), + "smoothing_elements": (int, "%d", None, True), + "pid_kp": (float, "%.3f", None, False), + "pid_ki": (float, "%.3f", None, False), + "pid_kd": (float, "%.3f", None, False), +} +# (type, placeholder, default, above, minval, can_be_none) +MPC_PROFILE_OPTIONS = { + "mpc_target": (float, "%.2f", None, True), + "control": (str, "%s", "mpc", False), + "block_heat_capacity": (float, "%f", None, True), + "ambient_transfer": (float, "%f", None, True), + "fan_ambient_transfer": ("floatlist", "%s", None, True), + "target_reach_time": (float, "%f", 2.0, False), + "smoothing": (float, "%f", 0.25, False), + "heater_power": (float, "%f", None, True), + "heater_powers": (("lists", (",", "\n"), float, 2), "%s", None, True), + "sensor_responsiveness": (float, "%f", None, True), + "min_ambient_change": (float, "%f", 1.0, False), + "steady_state_rate": (float, "%f", 0.5, False), + "filament_diameter": (float, "%.2f", 1.75, False), + "filament_density": (float, "%f", 1.2, False), + "filament_heat_capacity": (float, "%f", 1.8, False), + "maximum_retract": (float, "%f", 2.0, False), + "filament_temperature_source": (str, "%s", "ambient", True), + "ambient_temp_sensor": (str, "%s", None, True), + "cooling_fan": (str, "%s", None, True), } FILAMENT_TEMP_SRC_AMBIENT = "ambient" FILAMENT_TEMP_SRC_FIXED = "fixed" @@ -87,31 +116,23 @@ def __init__(self, config, sensor, sensor_config=None): self.next_pwm_time = 0.0 self.last_pwm_value = 0.0 # Those are necessary so the klipper config check does not complain - sensor_config.get("control", None) - sensor_config.getint("profile_version", None) - sensor_config.getfloat("pid_kp", None) - sensor_config.getfloat("pid_ki", None) - sensor_config.getfloat("pid_kd", None) - sensor_config.getfloat("max_delta", None) - sensor_config.getfloat("block_heat_capacity", None) - sensor_config.getfloat("ambient_transfer", None) - sensor_config.getfloat("target_reach_time", None) - sensor_config.getfloat("smoothing", None) - sensor_config.getfloat("heater_power", None) - sensor_config.getlist("heater_powers", None) - sensor_config.getfloat("sensor_responsiveness", None) - sensor_config.getfloat("min_ambient_change", None) - sensor_config.getfloat("steady_state_rate", None) - sensor_config.getfloat("filament_diameter", None) - sensor_config.getfloat("filament_density", None) - sensor_config.getfloat("filament_heat_capacity", None) - sensor_config.get("ambient_temp_sensor", None) + for key, (type, placeholder, default, can_be_none) in WATERMARK_PROFILE_OPTIONS.items(): + sensor_config.get(key, None) + for key, (type, placeholder, default, can_be_none) in PID_PROFILE_OPTIONS.items(): + sensor_config.get(key, None) + for key, ( + type, + placeholder, + default, + above, + minval, + can_be_none, + ) in MPC_PROFILE_OPTIONS.items(): + sensor_config.get(key, None) sensor_config.getfloat("calibrate_max_error", None) sensor_config.getfloat("calibrate_check_gain_time", None) sensor_config.getfloat("calibrate_hysteresis", None) sensor_config.getfloat("calibrate_heating_gain", None) - sensor_config.get("cooling_fan", None) - sensor_config.getfloatlist("fan_ambient_transfer", None) # Setup output heater pin heater_pin = sensor_config.get("heater_pin") ppins = self.printer.lookup_object("pins") @@ -151,6 +172,13 @@ def __init__(self, config, sensor, sensor_config=None): self.cmd_SET_SMOOTH_TIME, desc=self.cmd_SET_SMOOTH_TIME_help, ) + self.gcode.register_mux_command( + "SET_SMOOTHING_ELEMENTS", + "HEATER", + short_name, + self.cmd_SET_SMOOTHING_ELEMENTS, + desc=self.cmd_SET_SMOOTHING_ELEMENTS_help, + ) self.gcode.register_mux_command( "HEATER_PROFILE", "HEATER", @@ -344,6 +372,18 @@ def cmd_SET_SMOOTH_TIME(self, gcmd): self.get_control().get_profile()["smooth_time"] = self.smooth_time self.pmgr.save_profile() + cmd_SET_SMOOTHING_ELEMENTS_help = "Set the smooth time for the given heater" + + def cmd_SET_SMOOTHING_ELEMENTS(self, gcmd): + save_to_profile = gcmd.get_int("SAVE_TO_PROFILE", 0, minval=0, maxval=1) + self.smoothing_elements = gcmd.get_int( + "SMOOTHING_ELEMENTS", self.config_smoothing_elements, minval=1 + ) + self.get_control().update_smoothing_elements() + if save_to_profile: + self.get_control().get_profile()["smoothing_elements"] = self.smooth_time + self.pmgr.save_profile() + cmd_SET_HEATER_PID_help = "Sets a heater PID parameter" def cmd_SET_HEATER_PID(self, gcmd): @@ -351,15 +391,24 @@ def cmd_SET_HEATER_PID(self, gcmd): self.control, (ControlPID, ControlVelocityPID, ControlPositionalPID) ): raise gcmd.error("Not a PID/PID_V controlled heater") + save_to_profile = gcmd.get_int("SAVE_TO_PROFILE", 0, minval=0, maxval=1) kp = gcmd.get_float("KP", None) if kp is not None: self.control.set_pid_kp(kp) + if save_to_profile: + self.get_control().get_profile()["pid_kp"] = kp ki = gcmd.get_float("KI", None) if ki is not None: self.control.set_pid_ki(ki) + if save_to_profile: + self.get_control().get_profile()["pid_ki"] = ki kd = gcmd.get_float("KD", None) if kd is not None: self.control.set_pid_kd(kd) + if save_to_profile: + self.get_control().get_profile()["pid_kd"] = kd + if save_to_profile: + self.pmgr.save_profile() cmd_MPC_SET_help = "Set MPC parameter" @@ -386,28 +435,59 @@ def cmd_MPC_SET(self, gcmd): class ControlBangBang: @staticmethod def init_profile(config_section, name, pmgr=None): - temp_profile = { - "max_delta": config_section.getfloat("max_delta", 2.0, above=0.0) - } + temp_profile = {} + for key, (type, placeholder, default, can_be_none) in WATERMARK_PROFILE_OPTIONS.items(): + temp_profile[key] = pmgr._check_value_config( + key, config_section, type, can_be_none, default=default + ) if name != "default": profile_version = config_section.getint("profile_version", None) if HEATER_PROFILE_VERSION != profile_version: logging.info( "heater_profile: Profile [%s] not compatible with this version\n" - "of heater_profile. Profile Version: %d Current Version: %d " + "of heater_profile. Profile Version: %d Current Version: %d " % (name, profile_version, HEATER_PROFILE_VERSION) ) return None return temp_profile + @staticmethod + def set_values(pmgr, gcmd, control, profile_name): + current_profile = pmgr.heater.get_control().get_profile() + max_delta = pmgr._check_value_gcmd( + "MAX_DELTA", + current_profile["max_delta"], + gcmd, + float, + False, + ) + keep_target = pmgr._check_value_gcmd( + "KEEP_TARGET", 0, gcmd, int, True, minval=0, maxval=1 + ) + load_clean = pmgr._check_value_gcmd( + "LOAD_CLEAN", 0, gcmd, int, True, minval=0, maxval=1 + ) + temp_profile = { + "name": profile_name, + "control": control, + "max_delta": max_delta, + } + temp_control = pmgr.heater.lookup_control(temp_profile, load_clean) + pmgr.heater.set_control(temp_control, keep_target) + msg = ( + "Watermark Parameters:\n" + "Control: %s\n" + "Max Delta: %.4f\n" + "have been set as current profile." % (control, max_delta) + ) + pmgr.heater.gcode.respond_info(msg) + @staticmethod def save_profile(pmgr, temp_profile, profile_name=None, gcmd=None, verbose=True): if profile_name is None: profile_name = temp_profile["name"] section_name = pmgr._compute_section_name(profile_name) - pmgr.outer_instance.configfile.set( - section_name, "max_delta", temp_profile["max_delta"] - ) + pmgr.heater.configfile.set(section_name, "max_delta", temp_profile["max_delta"]) def __init__(self, profile, heater, load_clean=False): self.profile = profile @@ -447,46 +527,100 @@ def get_type(self): PID_SETTLE_SLOPE = 0.1 +def median(temps): + sorted_temps = sorted(temps) + length = len(sorted_temps) + return float( + ( + sorted_temps[length // 2 - 1] / 2.0 + sorted_temps[length // 2] / 2.0, + sorted_temps[length // 2], + )[length % 2] + ) + + class ControlPID: @staticmethod def init_profile(config_section, name, pmgr): temp_profile = {} - for key, (type, placeholder) in PID_PROFILE_OPTIONS.items(): - can_be_none = key != "pid_kp" and key != "pid_ki" and key != "pid_kd" + for key, (type, placeholder, default, can_be_none) in PID_PROFILE_OPTIONS.items(): temp_profile[key] = pmgr._check_value_config( - key, config_section, type, can_be_none + key, config_section, type, can_be_none, default=default ) - if name == "default": - temp_profile["smooth_time"] = None - temp_profile["smoothing_elements"] = None - else: + if name != "default": profile_version = config_section.getint("profile_version", 0) if HEATER_PROFILE_VERSION != profile_version: logging.info( "heater_profile: Profile [%s] not compatible with this version\n" - "of heater_profile. Profile Version: %d Current Version: %d " + "of heater_profile. Profile Version: %d Current Version: %d " % (name, profile_version, HEATER_PROFILE_VERSION) ) return None return temp_profile @staticmethod - def save_profile( - pmgr, temp_profile=None, profile_name=None, gcmd=None, verbose=True - ): - temp_profile = pmgr.outer_instance.get_control().get_profile() + def set_values(pmgr, gcmd, control, profile_name): + target = pmgr._check_value_gcmd("TARGET", None, gcmd, float, True) + tolerance = pmgr._check_value_gcmd( + "TOLERANCE", + None, + gcmd, + float, + True, + ) + kp = pmgr._check_value_gcmd("KP", None, gcmd, float, False) + ki = pmgr._check_value_gcmd("KI", None, gcmd, float, False) + kd = pmgr._check_value_gcmd("KD", None, gcmd, float, False) + smooth_time = pmgr._check_value_gcmd("SMOOTH_TIME", None, gcmd, float, True) + smoothing_elements = pmgr._check_value_gcmd( + "SMOOTHING_ELEMENTS", None, gcmd, int, True + ) + keep_target = pmgr._check_value_gcmd( + "KEEP_TARGET", 0, gcmd, int, True, minval=0, maxval=1 + ) + load_clean = pmgr._check_value_gcmd( + "LOAD_CLEAN", 0, gcmd, int, True, minval=0, maxval=1 + ) + temp_profile = { + "name": profile_name, + "pid_target": target, + "pid_tolerance": tolerance, + "control": control, + "smooth_time": smooth_time, + "smoothing_elements": smoothing_elements, + "pid_kp": kp, + "pid_ki": ki, + "pid_kd": kd, + } + temp_control = pmgr.heater.lookup_control(temp_profile, load_clean) + pmgr.heater.set_control(temp_control, keep_target) + msg = "PID Parameters:\n" + if target is not None: + msg += "Target: %.2f,\n" % target + if tolerance is not None: + msg += "Tolerance: %.4f\n" % tolerance + msg += "Control: %s\n" % control + if smooth_time is not None: + msg += "Smooth Time: %.3f\n" % smooth_time + if smoothing_elements is not None: + msg += "Smoothing Elements: %d\n" % smoothing_elements + msg += ( + "pid_Kp=%.3f pid_Ki=%.3f pid_Kd=%.3f\n" + "have been set as current profile." % (kp, ki, kd) + ) + pmgr.heater.gcode.respond_info(msg) + + @staticmethod + def save_profile(pmgr, temp_profile, profile_name=None, gcmd=None, verbose=True): if profile_name is None: profile_name = temp_profile["name"] section_name = pmgr._compute_section_name(profile_name) - pmgr.outer_instance.configfile.set( + pmgr.heater.configfile.set( section_name, "profile_version", HEATER_PROFILE_VERSION ) - for key, (type, placeholder) in PID_PROFILE_OPTIONS.items(): + for key, (type, placeholder, default, can_be_none) in PID_PROFILE_OPTIONS.items(): value = temp_profile[key] if value is not None: - pmgr.outer_instance.configfile.set( - section_name, key, placeholder % value - ) + pmgr.heater.configfile.set(section_name, key, placeholder % value) temp_profile["name"] = profile_name pmgr.profiles[profile_name] = temp_profile if verbose and gcmd is not None: @@ -495,7 +629,7 @@ def save_profile( "has been saved to profile [%s] " "for the current session. The SAVE_CONFIG command will\n" "update the printer config file and restart the printer." - % (pmgr.outer_instance.sensor_name, profile_name) + % (pmgr.heater.sensor_name, profile_name) ) def __init__(self, profile, heater, load_clean=False): @@ -571,6 +705,9 @@ def set_pid_ki(self, ki): def set_pid_kd(self, kd): self.Kd = kd / PID_PARAM_BASE + def set_name(self, name): + self.profile["name"] = name + def get_profile(self): return self.profile @@ -589,32 +726,12 @@ def init_profile(config_section, name, pmgr): return ControlPID.init_profile(config_section, name, pmgr) @staticmethod - def save_profile( - pmgr, temp_profile=None, profile_name=None, gcmd=None, verbose=True - ): - temp_profile = pmgr.outer_instance.get_control().get_profile() - if profile_name is None: - profile_name = temp_profile["name"] - section_name = pmgr._compute_section_name(profile_name) - pmgr.outer_instance.configfile.set( - section_name, "profile_version", HEATER_PROFILE_VERSION - ) - for key, (type, placeholder) in PID_PROFILE_OPTIONS.items(): - value = temp_profile[key] - if value is not None: - pmgr.outer_instance.configfile.set( - section_name, key, placeholder % value - ) - temp_profile["name"] = profile_name - pmgr.profiles[profile_name] = temp_profile - if verbose and gcmd is not None: - gcmd.respond_info( - "Current Heater profile for heater [%s] " - "has been saved to profile [%s] " - "for the current session. The SAVE_CONFIG command will\n" - "update the printer config file and restart the printer." - % (pmgr.outer_instance.sensor_name, profile_name) - ) + def set_values(pmgr, gcmd, control, profile_name): + ControlPID.set_values(pmgr, gcmd, control, profile_name) + + @staticmethod + def save_profile(pmgr, temp_profile, profile_name=None, gcmd=None, verbose=True): + ControlPID.save_profile(pmgr, temp_profile, profile_name, gcmd, verbose) def __init__(self, profile, heater, load_clean=False): self.profile = profile @@ -661,7 +778,7 @@ def temperature_update(self, read_time, temp, target_temp): self.temps.pop(0) self.temps.append(temp) self.smoothed_temps.pop(0) - self.smoothed_temps.append(self.median(self.temps[-self.smoothing_elements:])) + self.smoothed_temps.append(median(self.temps[-self.smoothing_elements :])) self.times.pop(0) self.times.append(read_time) @@ -703,16 +820,6 @@ def temperature_update(self, read_time, temp, target_temp): # update the heater self.heater.set_pwm(read_time, self.pwm) - def median(self, temps): - sorted_temps = sorted(temps) - length = len(sorted_temps) - return float( - ( - sorted_temps[length // 2 - 1] / 2.0 + sorted_temps[length // 2] / 2.0, - sorted_temps[length // 2], - )[length % 2] - ) - def check_busy(self, eventtime, smoothed_temp, target_temp): temp_diff = target_temp - smoothed_temp return abs(temp_diff) > PID_SETTLE_DELTA or abs(self.d1) > PID_SETTLE_SLOPE @@ -720,6 +827,9 @@ def check_busy(self, eventtime, smoothed_temp, target_temp): def update_smooth_time(self): self.smooth_time = self.heater.get_smooth_time() # smoothing window + def update_smoothing_elements(self): + self.smoothing_elements = self.heater.get_smoothing_elements() + def set_pid_kp(self, kp): self.Kp = kp / PID_PARAM_BASE @@ -729,6 +839,9 @@ def set_pid_ki(self, ki): def set_pid_kd(self, kd): self.Kd = kd / PID_PARAM_BASE + def set_name(self, name): + self.profile["name"] = name + def get_profile(self): return self.profile @@ -742,32 +855,12 @@ def init_profile(config_section, name, pmgr): return ControlPID.init_profile(config_section, name, pmgr) @staticmethod - def save_profile( - pmgr, temp_profile=None, profile_name=None, gcmd=None, verbose=True - ): - temp_profile = pmgr.outer_instance.get_control().get_profile() - if profile_name is None: - profile_name = temp_profile["name"] - section_name = pmgr._compute_section_name(profile_name) - pmgr.outer_instance.configfile.set( - section_name, "profile_version", HEATER_PROFILE_VERSION - ) - for key, (type, placeholder) in PID_PROFILE_OPTIONS.items(): - value = temp_profile[key] - if value is not None: - pmgr.outer_instance.configfile.set( - section_name, key, placeholder % value - ) - temp_profile["name"] = profile_name - pmgr.profiles[profile_name] = temp_profile - if verbose and gcmd is not None: - gcmd.respond_info( - "Current Heater profile for heater [%s] " - "has been saved to profile [%s] " - "for the current session. The SAVE_CONFIG command will\n" - "update the printer config file and restart the printer." - % (pmgr.outer_instance.sensor_name, profile_name) - ) + def set_values(pmgr, gcmd, control, profile_name): + ControlPID.set_values(pmgr, gcmd, control, profile_name) + + @staticmethod + def save_profile(pmgr, temp_profile, profile_name=None, gcmd=None, verbose=True): + ControlPID.save_profile(pmgr, temp_profile, profile_name, gcmd, verbose) def __init__(self, profile, heater, load_clean=False): self.profile = profile @@ -852,6 +945,9 @@ def set_pid_ki(self, ki): def set_pid_kd(self, kd): self.Kd = kd / PID_PARAM_BASE + def set_name(self, name): + self.profile["name"] = name + def get_profile(self): return self.profile @@ -859,56 +955,62 @@ def get_type(self): return "pid_p" +def get_power_at_temp(temp, powers): + def _interpolate(below, above, temp): + return ((below[1] * (above[0] - temp)) + (above[1] * (temp - below[0]))) / ( + above[0] - below[0] + ) + + if temp < powers[0][0]: + return powers[0][1] + if temp > powers[-1][0]: + return powers[-1][1] + + below = [ + powers[0][0], + powers[0][1], + ] + above = [ + powers[-1][0], + powers[-1][1], + ] + for config_temp in powers: + if config_temp[0] < temp: + below = config_temp + else: + above = config_temp + break + return _interpolate(below, above, temp) + + class ControlMPC: @staticmethod def init_profile(config_section, name, pmgr=None): - temp_profile = { - "block_heat_capacity": config_section.getfloat( - "block_heat_capacity", above=0.0, default=None - ), - "ambient_transfer": config_section.getfloat( - "ambient_transfer", minval=0.0, default=None - ), - "fan_ambient_transfer": config_section.getfloatlist( - "fan_ambient_transfer", default=None - ), - "target_reach_time": config_section.getfloat( - "target_reach_time", above=0.0, default=2.0 - ), - "smoothing": config_section.getfloat("smoothing", above=0.0, default=0.25), - "sensor_responsiveness": config_section.getfloat( - "sensor_responsiveness", above=0.0, default=None - ), - "min_ambient_change": config_section.getfloat( - "min_ambient_change", above=0.0, default=1.0 - ), - "steady_state_rate": config_section.getfloat( - "steady_state_rate", above=0.0, default=0.5 - ), - "filament_diameter": config_section.getfloat( - "filament_diameter", above=0.0, default=1.75 - ), - "filament_density": config_section.getfloat( - "filament_density", above=0.0, default=1.2 - ), - "filament_heat_capacity": ( - config_section.getfloat( - "filament_heat_capacity", above=0.0, default=1.8 - ) - ), - "maximum_retract": ( - config_section.getfloat("maximum_retract", above=0.0, default=2.0) - ), - } + temp_profile = {} + for key, ( + type, + placeholder, + default, + can_be_none, + ) in MPC_PROFILE_OPTIONS.items(): + if key == "ambient_transfer": + minval= 0.0 + above = None + else: + minval = None + above = 0.0 + temp_profile[key] = pmgr._check_value_config( + key, + config_section, + type, + can_be_none, + default=default, + above=above, + minval=minval, + ) - heater_power = config_section.getfloat("heater_power", above=0.0, default=None) - heater_powers = config_section.getlists( - "heater_powers", - seps=(",", "\n"), - parser=float, - count=2, - default=None, - ) + heater_power = temp_profile["heater_power"] + heater_powers = temp_profile["heater_powers"] if heater_power is None and heater_powers is None: raise config_section.error( "Option 'heater_power' or 'heater_powers' " @@ -922,36 +1024,25 @@ def init_profile(config_section, name, pmgr=None): + " " + name ) - temp_profile["heater_power"] = config_section.getfloat( - "heater_power", above=0.0, default=None - ) - temp_profile["heater_powers"] = config_section.getlists( - "heater_powers", - seps=(",", "\n"), - parser=float, - count=2, - default=None, - ) - filament_temp_src_raw = config_section.get( - "filament_temperature_source", "ambient" + filament_temp_src_raw = ( + temp_profile["filament_temperature_source"].lower().strip() ) - temp = filament_temp_src_raw.lower().strip() - if temp == "sensor": + if filament_temp_src_raw == "sensor": filament_temp_src = (FILAMENT_TEMP_SRC_SENSOR,) - elif temp == "ambient": + elif filament_temp_src_raw == "ambient": filament_temp_src = (FILAMENT_TEMP_SRC_AMBIENT,) else: try: - value = float(temp) + value = float(filament_temp_src_raw) except ValueError: raise config_section.error( f"Unable to parse option 'filament_temperature_source' in section '{config_section.get_name()}'" ) filament_temp_src = (FILAMENT_TEMP_SRC_FIXED, value) - temp_profile["filament_temp_src"] = filament_temp_src + temp_profile["filament_temperature_source"] = filament_temp_src - ambient_sensor_name = config_section.get("ambient_temp_sensor", None) + ambient_sensor_name = temp_profile["ambient_temp_sensor"] ambient_sensor = None if ambient_sensor_name is not None: try: @@ -964,7 +1055,7 @@ def init_profile(config_section, name, pmgr=None): ) temp_profile["ambient_temp_sensor"] = ambient_sensor - fan_name = config_section.get("cooling_fan", None) + fan_name = temp_profile["cooling_fan"] fan = None if fan_name is not None: try: @@ -980,90 +1071,50 @@ def init_profile(config_section, name, pmgr=None): fan = fan_obj.fan temp_profile["cooling_fan"] = fan - temp_profile["fan_ambient_transfer"] = config_section.getfloatlist( - "fan_ambient_transfer", [] - ) if name != "default": profile_version = config_section.getint("profile_version", None) if HEATER_PROFILE_VERSION != profile_version: logging.info( "heater_profile: Profile [%s] not compatible with this version\n" - "of heater_profile. Profile Version: %d Current Version: %d " + "of heater_profile. Profile Version: %d Current Version: %d " % (name, profile_version, HEATER_PROFILE_VERSION) ) return None return temp_profile + @staticmethod + def set_values(pmgr, gcmd, control, profile_name): + pass + # TODO + @staticmethod def save_profile( pmgr, temp_profile=None, profile_name=None, gcmd=None, verbose=True ): - temp_profile = pmgr.outer_instance.get_control().get_profile() if profile_name is None: profile_name = temp_profile["name"] section_name = pmgr._compute_section_name(profile_name) - for key, (type, placeholder) in PID_PROFILE_OPTIONS.items(): + pmgr.heater.configfile.set( + section_name, "profile_version", HEATER_PROFILE_VERSION + ) + for key, ( + type, + placeholder, + default, + above, + minval, + can_be_none, + ) in MPC_PROFILE_OPTIONS.items(): value = temp_profile[key] if value is not None: - pmgr.outer_instance.configfile.set( - section_name, key, placeholder % value - ) - pmgr.outer_instance.configfile.set( - section_name, - "block_heat_capacity", - "%.6f" % temp_profile["block_heat_capacity"], - ) - pmgr.outer_instance.configfile.set( - section_name, - "ambient_transfer", - "%.6f" % temp_profile["ambient_transfer"], - ) - pmgr.outer_instance.configfile.set( - section_name, - "fan_ambient_transfer", - "%.6f" % temp_profile["fan_ambient_transfer"], - ) - pmgr.outer_instance.configfile.set( - section_name, "target_reach_time", temp_profile["target_reach_time"] - ) - pmgr.outer_instance.configfile.set( - section_name, "smoothing", temp_profile["smoothing"] - ) - pmgr.outer_instance.configfile.set( - section_name, "heater_power", temp_profile["heater_power"] - ) - pmgr.outer_instance.configfile.set( - section_name, "heater_powers", temp_profile["heater_powers"] - ) - pmgr.outer_instance.configfile.set( - section_name, - "sensor_responsiveness", - "%.6f" % temp_profile["sensor_responsiveness"], - ) - pmgr.outer_instance.configfile.set( - section_name, - "min_ambient_change", - temp_profile["min_ambient_change"], - ) - pmgr.outer_instance.configfile.set( - section_name, "steady_state_rate", temp_profile["steady_state_rate"] - ) - pmgr.outer_instance.configfile.set( - section_name, "filament_diameter", temp_profile["filament_diameter"] - ) - pmgr.outer_instance.configfile.set( - section_name, "filament_density", temp_profile["filament_density"] - ) - pmgr.outer_instance.configfile.set( - section_name, - "filament_heat_capacity", - temp_profile["filament_heat_capacity"], - ) - pmgr.outer_instance.configfile.set( - section_name, - "ambient_temp_sensor", - temp_profile["ambient_temp_sensor"], - ) + if key == "heater_powers": + pmgr.heater.configfile.set(section_name, key, value) + elif key == "ambient_temp_sensor" or key == "cooling_fan": + pmgr.heater.configfile.set(section_name, key, value.name) + elif key == "filament_temperature_source": + pmgr.heater.configfile.set(section_name, key, value[-1]) + else: + pmgr.heater.configfile.set(section_name, key, placeholder % value) temp_profile["name"] = profile_name pmgr.profiles[profile_name] = temp_profile if verbose and gcmd is not None: @@ -1072,19 +1123,44 @@ def save_profile( "has been saved to profile [%s] " "for the current session. The SAVE_CONFIG command will\n" "update the printer config file and restart the printer." - % (pmgr.outer_instance.sensor_name, profile_name) + % (pmgr.heater.sensor_name, profile_name) ) def __init__(self, profile, heater, load_clean=False): self.profile = profile - self._load_profile() self.heater = heater - if self.const_heater_power is None: - heater_power = self.const_heater_powers[0][1] + self.printer = heater.printer + self.const_block_heat_capacity = self.profile["block_heat_capacity"] + self.const_ambient_transfer = self.profile["ambient_transfer"] + self.const_target_reach_time = self.profile["target_reach_time"] + const_heater_power = self.profile["heater_power"] + if const_heater_power is not None: + self.const_heater_power = [ + (self.heater.min_temp, const_heater_power), + (self.heater.max_temp, const_heater_power), + ] else: - heater_power = self.const_heater_powers - self.heater_max_power = heater.get_max_power() * heater_power - self.max_power = heater.get_max_power() + self.const_heater_power = self.profile["heater_powers"] + self.const_smoothing = self.profile["smoothing"] + self.const_sensor_responsiveness = self.profile["sensor_responsiveness"] + self.const_min_ambient_change = self.profile["min_ambient_change"] + self.const_steady_state_rate = self.profile["steady_state_rate"] + self.const_filament_diameter = self.profile["filament_diameter"] + self.const_filament_density = self.profile["filament_density"] + self.const_filament_heat_capacity = self.profile["filament_heat_capacity"] + self.const_maximum_retract = self.profile["maximum_retract"] + self.filament_temp_src = self.profile["filament_temp_src"] + self._update_filament_const() + self.ambient_sensor = self.profile["ambient_temperature_sensor"] + self.cooling_fan = self.profile["cooling_fan"] + self.const_fan_ambient_transfer = self.profile["fan_ambient_transfer"] + if self.const_fan_ambient_transfer is None: + self.const_fan_ambient_transfer = [] + self.pwm_max_power = [ + (temp, heater.get_max_power() * power) + for temp, power in self.const_heater_power + ] + self.heater_max_power = heater.get_max_power() self.want_ambient_refresh = self.ambient_sensor is not None self.state_block_temp = AMBIENT_TEMP if load_clean else self._heater_temp() @@ -1097,7 +1173,6 @@ def __init__(self, profile, heater, load_clean=False): self.last_time = 0.0 self.last_temp_time = 0.0 - self.printer = heater.printer self.toolhead = None # Helpers @@ -1109,8 +1184,15 @@ def _load_profile(self): self.const_block_heat_capacity = self.profile["block_heat_capacity"] self.const_ambient_transfer = self.profile["ambient_transfer"] self.const_target_reach_time = self.profile["target_reach_time"] - self.const_heater_power = self.profile["heater_power"] - self.const_heater_powers = self.profile["heater_powers"] + const_heater_power = self.profile["heater_power"] + const_heater_powers = self.profile["heater_powers"] + if const_heater_power is not None: + self.const_heater_power = [ + (self.heater.min_temp, const_heater_power), + (self.heater.max_temp, const_heater_power), + ] + else: + self.const_heater_power = const_heater_powers self.const_smoothing = self.profile["smoothing"] self.const_sensor_responsiveness = self.profile["sensor_responsiveness"] self.const_min_ambient_change = self.profile["min_ambient_change"] @@ -1121,9 +1203,11 @@ def _load_profile(self): self.const_maximum_retract = self.profile["maximum_retract"] self.filament_temp_src = self.profile["filament_temp_src"] self._update_filament_const() - self.ambient_sensor = self.profile["ambient_temp_sensor"] + self.ambient_sensor = self.profile["ambient_temperature_sensor"] self.cooling_fan = self.profile["cooling_fan"] self.const_fan_ambient_transfer = self.profile["fan_ambient_transfer"] + if self.const_fan_ambient_transfer is None: + self.const_fan_ambient_transfer = [] def is_valid(self): return ( @@ -1243,7 +1327,7 @@ def temperature_update(self, read_time, temp, target_temp): if temp != 0.0: self.state_ambient_temp = temp self.want_ambient_refresh = False - if (self.last_power > 0 and self.last_power < 1.0) or abs( + if (0 < self.last_power < 1.0) or abs( expected_block_dT + adjustment_dT ) < self.const_steady_state_rate * dt: if adjustment_dT > 0.0: @@ -1278,38 +1362,16 @@ def temperature_update(self, read_time, temp, target_temp): power = max( 0.0, min( - self.heater_max_power, + get_power_at_temp(target_temp, self.pwm_max_power), heating_power + loss_ambient + loss_filament, ), ) else: power = 0 - if self.const_heater_power is None: - if temp < self.const_heater_powers[0][0]: - heater_power = self.const_heater_powers[0][1] - elif temp > self.const_heater_powers[-1][0]: - heater_power = self.const_heater_powers[-1][1] - else: - below = [ - self.const_heater_powers[0][0], - self.const_heater_powers[0][1], - ] - above = [ - self.const_heater_powers[-1][0], - self.const_heater_powers[-1][1], - ] - for config_temp in self.const_heater_powers: - if config_temp[0] < temp: - below = config_temp - else: - above = config_temp - break - heater_power = self._interpolate(below, above, temp) - else: - heater_power = self.const_heater_power + heater_power = get_power_at_temp(temp, self.const_heater_power) - duty = max(0.0, min(self.max_power, power / heater_power)) + duty = max(0.0, min(self.heater_max_power, power / heater_power)) # logging.info( # "mpc: [%.3f/%.3f] %.2f => %.2f / %.2f / %.2f = %.2f[%.2f+%.2f+%.2f] / %.2f, dT %.2f, E %.2f=>%.2f", @@ -1335,11 +1397,6 @@ def temperature_update(self, read_time, temp, target_temp): self.last_temp_time = read_time self.heater.set_pwm(read_time, duty) - def _interpolate(self, below, above, temp): - return ((below[1] * (above[0] - temp)) + (above[1] * (temp - below[0]))) / ( - above[0] - below[0] - ) - def filament_temp(self, read_time, ambient_temp): src = self.filament_temp_src if src[0] == FILAMENT_TEMP_SRC_FIXED: @@ -1355,6 +1412,9 @@ def check_busy(self, eventtime, smoothed_temp, target_temp): def update_smooth_time(self): self.const_smoothing = self.heater.get_smooth_time() # smoothing window + def set_name(self, name): + self.profile["name"] = name + def get_profile(self): return self.profile diff --git a/klippy/extras/mpc_calibrate.py b/klippy/extras/mpc_calibrate.py index 5bebe1cd2..cc5bbd3f7 100644 --- a/klippy/extras/mpc_calibrate.py +++ b/klippy/extras/mpc_calibrate.py @@ -1,5 +1,6 @@ import math import logging +from extras.heaters import get_power_at_temp PIN_MIN_TIME = 0.100 @@ -34,8 +35,8 @@ def __init__(self, printer, heater, config): self.config = config self.heater = heater self.pmgr = heater.pmgr - self.orig_control = heater.get_control() - self.ambient_sensor_name = self.config.get("ambient_temp_sensor", None) + self.orig_control = None + self.temp_control = None self.max_error = self.config.getfloat("calibrate_max_error", None) self.check_gain_time = self.config.getfloat("calibrate_check_gain_time", None) self.hysteresis = self.config.getfloat("calibrate_hysteresis", None) @@ -55,15 +56,22 @@ def run(self, gcmd): threshold_temp = gcmd.get_float( "THRESHOLD", max(50.0, min(100, target_temp - 100.0)) ) - ambient_sensor_name = gcmd.get("AMBIENT_TEMP_SENSOR", self.ambient_sensor_name) - ambient_sensor = None + + control = TuningControl(self.heater) + self.orig_control = self.heater.set_control(control) + self.temp_control = self.orig_control + if self.temp_control.get_type() != "mpc": + self.temp_control = self.pmgr._init_profile(self.config, "autotune", "mpc") + ambient_sensor_name = gcmd.get("AMBIENT_TEMP_SENSOR", None) if ambient_sensor_name is not None: try: ambient_sensor = self.printer.lookup_object(ambient_sensor_name) except Exception: raise self.config.error( - f"Unknown ambient_temp_sensor '{ambient_sensor_name}' " f"specified" + f"Unknown ambient_temp_sensor '{ambient_sensor_name}' specified" ) + else: + ambient_sensor = self.temp_control["ambient_temp_sensor"] max_error = gcmd.get_float("MAX_ERROR", self.max_error) check_gain_time = gcmd.get_float("CHECK_GAIN_TIME", self.check_gain_time) hysteresis = gcmd.get_float("HYSTERESIS", self.hysteresis) @@ -89,8 +97,6 @@ def run(self, gcmd): old_heating_gain = verify_heater.heating_gain verify_heater.heating_gain = heating_gain - control = TuningControl(self.heater) - old_control = self.heater.set_control(control) try: ambient_temp = self.await_ambient( gcmd, control, threshold_temp, ambient_sensor @@ -98,14 +104,14 @@ def run(self, gcmd): samples = self.heatup_test(gcmd, target_temp, control) first_res = self.process_first_pass( samples, - self.orig_control.heater_max_power, + get_power_at_temp(target_temp, self.temp_control.pwm_max_power), ambient_temp, threshold_temp, use_analytic, ) logging.info("First pass: %s", first_res) - profile = dict(self.orig_control.profile) + profile = dict(self.temp_control.profile) for key in [ "block_heat_capacity", "ambient_transfer", @@ -118,19 +124,19 @@ def run(self, gcmd): new_control.state_ambient_temp = ambient_temp self.heater.set_control(new_control) + second_target_temp = round(first_res["post_block_temp"]) transfer_res = self.transfer_test( gcmd, ambient_max_measure_time, ambient_measure_sample_time, fan_breakpoints, - new_control, first_res, ) second_res = self.process_second_pass( first_res, transfer_res, ambient_temp, - self.orig_control.heater_max_power, + get_power_at_temp(second_target_temp, self.temp_control.pwm_max_power), ) logging.info("Second pass: %s", second_res) @@ -149,16 +155,15 @@ def run(self, gcmd): [f"{p:.6g}" for p in second_res["fan_ambient_transfer"]] ) - for key in [ - "block_heat_capacity", - "ambient_transfer", - "fan_ambient_transfer", - "sensor_responsiveness", - ]: - profile[key] = first_res[key] + profile["block_heat_capacity"] = block_heat_capacity + profile["ambient_transfer"] = ambient_transfer + profile["fan_ambient_transfer"] = fan_ambient_transfer + profile["sensor_responsiveness"] = sensor_responsiveness + profile["name"] = profile_name + profile["mpc_target"] = target_temp - new_control = self.heater.lookup_control(profile, True) - self.heater.set_control(new_control, False) + self.heater.set_control(self.heater.lookup_control(profile, True), False) + self.heater.pmgr.save_profile(profile_name=profile_name, verbose=False) gcmd.respond_info( f"Finished MPC calibration of heater '{self.heater.short_name}'\n" @@ -167,10 +172,12 @@ def run(self, gcmd): f" sensor_responsiveness={sensor_responsiveness:#.6g} [K/s/K]\n" f" ambient_transfer={ambient_transfer:#.6g} [W/K]\n" f" fan_ambient_transfer={fan_ambient_transfer} [W/K]\n" + "The SAVE_CONFIG command will update the printer config file\n" + "with these parameters and restart the printer." ) - self.heater.pmgr.save_profile(profile_name=profile_name, verbose=False) except self.printer.command_error as e: + self.heater.set_control(self.orig_control, False) raise gcmd.error("%s failed: %s" % (gcmd.get_command(), e)) finally: if old_max_error is not None: @@ -181,8 +188,6 @@ def run(self, gcmd): verify_heater.hysteresis = old_hysteresis if old_heating_gain is not None: verify_heater.heating_gain = old_heating_gain - self.heater.set_control(old_control) - self.heater.alter_target(0.0) def wait_stable(self, cycles=5): """ @@ -293,10 +298,8 @@ def transfer_test( ambient_max_measure_time, ambient_measure_sample_time, fan_breakpoints, - control, - first_pass_results, + target_temp, ): - target_temp = round(first_pass_results["post_block_temp"]) self.heater.set_temp(target_temp) gcmd.respond_info( "Performing ambient transfer tests, target is %.1f degrees" % (target_temp,) @@ -304,7 +307,7 @@ def transfer_test( self.wait_stable(5) - fan = self.orig_control.cooling_fan + fan = self.temp_control.cooling_fan fan_powers = [] if fan is None: @@ -313,27 +316,24 @@ def transfer_test( ) gcmd.respond_info(f"Average stable power: {power_base} W") else: - if fan is not None: - for idx in range(0, fan_breakpoints): - speed = idx / (fan_breakpoints - 1) - curtime = self.heater.reactor.monotonic() - print_time = fan.get_mcu().estimated_print_time(curtime) - fan.set_speed(print_time + PIN_MIN_TIME, speed) - gcmd.respond_info("Waiting for temperature to stabilize") - self.wait_stable(3) - gcmd.respond_info( - f"Temperature stable, measuring power usage with {speed*100.:.0f}% fan speed" - ) - power = self.measure_power( - ambient_max_measure_time, ambient_measure_sample_time - ) - gcmd.respond_info( - f"{speed*100.:.0f}% fan average power: {power:.2f} W" - ) - fan_powers.append((speed, power)) + for idx in range(0, fan_breakpoints): + speed = idx / (fan_breakpoints - 1) curtime = self.heater.reactor.monotonic() print_time = fan.get_mcu().estimated_print_time(curtime) - fan.set_speed(print_time + PIN_MIN_TIME, 0.0) + fan.set_speed(print_time + PIN_MIN_TIME, speed) + gcmd.respond_info("Waiting for temperature to stabilize") + self.wait_stable(3) + gcmd.respond_info( + f"Temperature stable, measuring power usage with {speed*100.:.0f}% fan speed" + ) + power = self.measure_power( + ambient_max_measure_time, ambient_measure_sample_time + ) + gcmd.respond_info(f"{speed*100.:.0f}% fan average power: {power:.2f} W") + fan_powers.append((speed, power)) + curtime = self.heater.reactor.monotonic() + print_time = fan.get_mcu().estimated_print_time(curtime) + fan.set_speed(print_time + PIN_MIN_TIME, 0.0) power_base = fan_powers[0][1] return { @@ -468,10 +468,13 @@ def process_second_pass(self, first_res, transfer_res, ambient_temp, heater_powe / (first_res["t1"] - asymp_T) ) - fan_ambient_transfer = [ - power / target_ambient_temp - for (_speed, power) in transfer_res["fan_powers"] - ] + if transfer_res["fan_powers"]: + fan_ambient_transfer = [ + power / target_ambient_temp + for (_speed, power) in transfer_res["fan_powers"] + ] + else: + fan_ambient_transfer = None return { "ambient_transfer": ambient_transfer, diff --git a/klippy/extras/pid_calibrate.py b/klippy/extras/pid_calibrate.py index f895b5524..d59c856d0 100644 --- a/klippy/extras/pid_calibrate.py +++ b/klippy/extras/pid_calibrate.py @@ -59,26 +59,15 @@ def cmd_PID_CALIBRATE(self, gcmd): try: pheaters.set_temperature(heater, target, True, gcmd=gcmd) except self.printer.command_error as e: - heater.set_control(old_control, False) raise - heater.set_control(old_control, False) + finally: + heater.set_control(old_control, False) if write_file: calibrate.write_file("/tmp/heattest.csv") if calibrate.check_busy(0.0, 0.0, 0.0): raise gcmd.error("pid_calibrate interrupted") # Log and report results Kp, Ki, Kd = calibrate.calc_pid(calculation_method) - logging.info("Autotune: final: Kp=%f Ki=%f Kd=%f", Kp, Ki, Kd) - gcmd.respond_info( - "PID parameters for %.2f\xb0C: " - "pid_Kp=%.3f pid_Ki=%.3f pid_Kd=%.3f\n" - "Heater: %s\n" - "Tolerance: %.4f\n" - "Profile: %s\n" - "The SAVE_CONFIG command will update the printer config file\n" - "with these parameters and restart the printer." - % (target, Kp, Ki, Kd, heater_name, tolerance, profile_name) - ) control = algorithm profile = { @@ -96,6 +85,18 @@ def cmd_PID_CALIBRATE(self, gcmd): heater.set_control(heater.lookup_control(profile, True), False) heater.pmgr.save_profile(profile_name=profile_name, verbose=False) + logging.info("Autotune: final: Kp=%f Ki=%f Kd=%f", Kp, Ki, Kd) + gcmd.respond_info( + "PID parameters for %.2f\xb0C: " + "pid_Kp=%.3f pid_Ki=%.3f pid_Kd=%.3f\n" + "Heater: %s\n" + "Tolerance: %.4f\n" + "Profile: %s\n" + "The SAVE_CONFIG command will update the printer config file\n" + "with these parameters and restart the printer." + % (target, Kp, Ki, Kd, heater_name, tolerance, profile_name) + ) + def calculate_ziegler_nichols(tu, ku): ti = 0.5 * tu From c6134b9886356310a814d7d61fcdb35279aff3b2 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Fri, 13 Sep 2024 15:53:27 +0200 Subject: [PATCH 135/158] Update heater_profile_manager.py --- klippy/extras/heater_profile_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/heater_profile_manager.py b/klippy/extras/heater_profile_manager.py index 48765b957..edc56cc29 100644 --- a/klippy/extras/heater_profile_manager.py +++ b/klippy/extras/heater_profile_manager.py @@ -56,7 +56,7 @@ def _check_value_config( elif isinstance(type, str) and type == "floatlist": value = config_section.getfloatlist(key, default=default) elif ( - isinstance(type, list) + isinstance(type, tuple) and len(type) == 4 and isinstance(type[0], str) and type[0] == "lists" From cabbadac3314b96d6dea462998db8b81e2cadc1d Mon Sep 17 00:00:00 2001 From: Zeanon Date: Fri, 13 Sep 2024 16:47:13 +0200 Subject: [PATCH 136/158] . --- klippy/extras/heater_profile_manager.py | 2 +- klippy/extras/heaters.py | 221 +++++++++++++----------- klippy/extras/mpc_calibrate.py | 33 ++-- klippy/extras/z_tilt_ng.py | 4 +- 4 files changed, 144 insertions(+), 116 deletions(-) diff --git a/klippy/extras/heater_profile_manager.py b/klippy/extras/heater_profile_manager.py index edc56cc29..9dccdfca5 100644 --- a/klippy/extras/heater_profile_manager.py +++ b/klippy/extras/heater_profile_manager.py @@ -53,7 +53,7 @@ def _check_value_config( value = config_section.getfloat( key, default=default, minval=minval, above=above ) - elif isinstance(type, str) and type == "floatlist": + elif type == "floatlist": value = config_section.getfloatlist(key, default=default) elif ( isinstance(type, tuple) diff --git a/klippy/extras/heaters.py b/klippy/extras/heaters.py index 88ce9190f..e1304c75b 100644 --- a/klippy/extras/heaters.py +++ b/klippy/extras/heaters.py @@ -37,27 +37,27 @@ "pid_ki": (float, "%.3f", None, False), "pid_kd": (float, "%.3f", None, False), } -# (type, placeholder, default, above, minval, can_be_none) +# (type, placeholder, default, can_be_none) MPC_PROFILE_OPTIONS = { "mpc_target": (float, "%.2f", None, True), "control": (str, "%s", "mpc", False), - "block_heat_capacity": (float, "%f", None, True), - "ambient_transfer": (float, "%f", None, True), - "fan_ambient_transfer": ("floatlist", "%s", None, True), - "target_reach_time": (float, "%f", 2.0, False), - "smoothing": (float, "%f", 0.25, False), "heater_power": (float, "%f", None, True), "heater_powers": (("lists", (",", "\n"), float, 2), "%s", None, True), - "sensor_responsiveness": (float, "%f", None, True), - "min_ambient_change": (float, "%f", 1.0, False), + "cooling_fan": (str, "%s", None, True), + "ambient_temp_sensor": (str, "%s", None, True), + "filament_temperature_source": (str, "%s", "ambient", False), + "smoothing": (float, "%f", 0.25, False), + "target_reach_time": (float, "%f", 2.0, False), + "maximum_retract": (float, "%f", 2.0, False), "steady_state_rate": (float, "%f", 0.5, False), + "min_ambient_change": (float, "%f", 1.0, False), "filament_diameter": (float, "%.2f", 1.75, False), "filament_density": (float, "%f", 1.2, False), "filament_heat_capacity": (float, "%f", 1.8, False), - "maximum_retract": (float, "%f", 2.0, False), - "filament_temperature_source": (str, "%s", "ambient", True), - "ambient_temp_sensor": (str, "%s", None, True), - "cooling_fan": (str, "%s", None, True), + "block_heat_capacity": (float, "%f", None, True), + "ambient_transfer": (float, "%f", None, True), + "fan_ambient_transfer": ("floatlist", "%.6f", [], True), + "sensor_responsiveness": (float, "%f", None, True), } FILAMENT_TEMP_SRC_AMBIENT = "ambient" FILAMENT_TEMP_SRC_FIXED = "fixed" @@ -116,16 +116,24 @@ def __init__(self, config, sensor, sensor_config=None): self.next_pwm_time = 0.0 self.last_pwm_value = 0.0 # Those are necessary so the klipper config check does not complain - for key, (type, placeholder, default, can_be_none) in WATERMARK_PROFILE_OPTIONS.items(): + for key, ( + type, + placeholder, + default, + can_be_none, + ) in WATERMARK_PROFILE_OPTIONS.items(): sensor_config.get(key, None) - for key, (type, placeholder, default, can_be_none) in PID_PROFILE_OPTIONS.items(): + for key, ( + type, + placeholder, + default, + can_be_none, + ) in PID_PROFILE_OPTIONS.items(): sensor_config.get(key, None) for key, ( type, placeholder, default, - above, - minval, can_be_none, ) in MPC_PROFILE_OPTIONS.items(): sensor_config.get(key, None) @@ -436,7 +444,12 @@ class ControlBangBang: @staticmethod def init_profile(config_section, name, pmgr=None): temp_profile = {} - for key, (type, placeholder, default, can_be_none) in WATERMARK_PROFILE_OPTIONS.items(): + for key, ( + type, + placeholder, + default, + can_be_none, + ) in WATERMARK_PROFILE_OPTIONS.items(): temp_profile[key] = pmgr._check_value_config( key, config_section, type, can_be_none, default=default ) @@ -487,7 +500,25 @@ def save_profile(pmgr, temp_profile, profile_name=None, gcmd=None, verbose=True) if profile_name is None: profile_name = temp_profile["name"] section_name = pmgr._compute_section_name(profile_name) - pmgr.heater.configfile.set(section_name, "max_delta", temp_profile["max_delta"]) + for key, ( + type, + placeholder, + default, + can_be_none, + ) in WATERMARK_PROFILE_OPTIONS.items(): + value = temp_profile[key] + if value is not None: + pmgr.heater.configfile.set(section_name, key, placeholder % value) + temp_profile["name"] = profile_name + pmgr.profiles[profile_name] = temp_profile + if verbose and gcmd is not None: + gcmd.respond_info( + "Current Heater profile for heater [%s] " + "has been saved to profile [%s] " + "for the current session. The SAVE_CONFIG command will\n" + "update the printer config file and restart the printer." + % (pmgr.heater.sensor_name, profile_name) + ) def __init__(self, profile, heater, load_clean=False): self.profile = profile @@ -512,6 +543,9 @@ def check_busy(self, eventtime, smoothed_temp, target_temp): def update_smooth_time(self): self.smooth_time = self.heater.get_smooth_time() # smoothing window + def set_name(self, name): + self.profile["name"] = name + def get_profile(self): return self.profile @@ -527,22 +561,16 @@ def get_type(self): PID_SETTLE_SLOPE = 0.1 -def median(temps): - sorted_temps = sorted(temps) - length = len(sorted_temps) - return float( - ( - sorted_temps[length // 2 - 1] / 2.0 + sorted_temps[length // 2] / 2.0, - sorted_temps[length // 2], - )[length % 2] - ) - - class ControlPID: @staticmethod def init_profile(config_section, name, pmgr): temp_profile = {} - for key, (type, placeholder, default, can_be_none) in PID_PROFILE_OPTIONS.items(): + for key, ( + type, + placeholder, + default, + can_be_none, + ) in PID_PROFILE_OPTIONS.items(): temp_profile[key] = pmgr._check_value_config( key, config_section, type, can_be_none, default=default ) @@ -617,7 +645,12 @@ def save_profile(pmgr, temp_profile, profile_name=None, gcmd=None, verbose=True) pmgr.heater.configfile.set( section_name, "profile_version", HEATER_PROFILE_VERSION ) - for key, (type, placeholder, default, can_be_none) in PID_PROFILE_OPTIONS.items(): + for key, ( + type, + placeholder, + default, + can_be_none, + ) in PID_PROFILE_OPTIONS.items(): value = temp_profile[key] if value is not None: pmgr.heater.configfile.set(section_name, key, placeholder % value) @@ -627,7 +660,7 @@ def save_profile(pmgr, temp_profile, profile_name=None, gcmd=None, verbose=True) gcmd.respond_info( "Current Heater profile for heater [%s] " "has been saved to profile [%s] " - "for the current session. The SAVE_CONFIG command will\n" + "for the current session. The SAVE_CONFIG command will\n" "update the printer config file and restart the printer." % (pmgr.heater.sensor_name, profile_name) ) @@ -733,6 +766,17 @@ def set_values(pmgr, gcmd, control, profile_name): def save_profile(pmgr, temp_profile, profile_name=None, gcmd=None, verbose=True): ControlPID.save_profile(pmgr, temp_profile, profile_name, gcmd, verbose) + @staticmethod + def median(temps): + sorted_temps = sorted(temps) + length = len(sorted_temps) + return float( + ( + sorted_temps[length // 2 - 1] / 2.0 + sorted_temps[length // 2] / 2.0, + sorted_temps[length // 2], + )[length % 2] + ) + def __init__(self, profile, heater, load_clean=False): self.profile = profile self.heater = heater @@ -778,7 +822,9 @@ def temperature_update(self, read_time, temp, target_temp): self.temps.pop(0) self.temps.append(temp) self.smoothed_temps.pop(0) - self.smoothed_temps.append(median(self.temps[-self.smoothing_elements :])) + self.smoothed_temps.append( + ControlVelocityPID.median(self.temps[-self.smoothing_elements :]) + ) self.times.pop(0) self.times.append(read_time) @@ -955,34 +1001,6 @@ def get_type(self): return "pid_p" -def get_power_at_temp(temp, powers): - def _interpolate(below, above, temp): - return ((below[1] * (above[0] - temp)) + (above[1] * (temp - below[0]))) / ( - above[0] - below[0] - ) - - if temp < powers[0][0]: - return powers[0][1] - if temp > powers[-1][0]: - return powers[-1][1] - - below = [ - powers[0][0], - powers[0][1], - ] - above = [ - powers[-1][0], - powers[-1][1], - ] - for config_temp in powers: - if config_temp[0] < temp: - below = config_temp - else: - above = config_temp - break - return _interpolate(below, above, temp) - - class ControlMPC: @staticmethod def init_profile(config_section, name, pmgr=None): @@ -993,8 +1011,11 @@ def init_profile(config_section, name, pmgr=None): default, can_be_none, ) in MPC_PROFILE_OPTIONS.items(): - if key == "ambient_transfer": - minval= 0.0 + if key == "mpc_target": + minval = None + above = None + elif key == "ambient_transfer": + minval = 0.0 above = None else: minval = None @@ -1108,7 +1129,14 @@ def save_profile( value = temp_profile[key] if value is not None: if key == "heater_powers": - pmgr.heater.configfile.set(section_name, key, value) + powers = "" + for temp, power in value: + powers += "%.2f, %.2f\n" % (temp, power) + pmgr.heater.configfile.set(section_name, key, powers) + elif key == "fan_ambient_transfer" and value: + pmgr.heater.configfile.set( + section_name, key, ", ".join([placeholder % p for p in value]) + ) elif key == "ambient_temp_sensor" or key == "cooling_fan": pmgr.heater.configfile.set(section_name, key, value.name) elif key == "filament_temperature_source": @@ -1121,11 +1149,39 @@ def save_profile( gcmd.respond_info( "Current Heater profile for heater [%s] " "has been saved to profile [%s] " - "for the current session. The SAVE_CONFIG command will\n" + "for the current session. The SAVE_CONFIG command will\n" "update the printer config file and restart the printer." % (pmgr.heater.sensor_name, profile_name) ) + @staticmethod + def get_power_at_temp(temperature, power_table): + def _interpolate(below, above, temp): + return ((below[1] * (above[0] - temp)) + (above[1] * (temp - below[0]))) / ( + above[0] - below[0] + ) + + if temperature < power_table[0][0]: + return power_table[0][1] + if temperature > power_table[-1][0]: + return power_table[-1][1] + + below = [ + power_table[0][0], + power_table[0][1], + ] + above = [ + power_table[-1][0], + power_table[-1][1], + ] + for temp, power in power_table: + if temp < temperature: + below = (temp, power) + else: + above = (temp, power) + break + return _interpolate(below, above, temperature) + def __init__(self, profile, heater, load_clean=False): self.profile = profile self.heater = heater @@ -1154,8 +1210,6 @@ def __init__(self, profile, heater, load_clean=False): self.ambient_sensor = self.profile["ambient_temperature_sensor"] self.cooling_fan = self.profile["cooling_fan"] self.const_fan_ambient_transfer = self.profile["fan_ambient_transfer"] - if self.const_fan_ambient_transfer is None: - self.const_fan_ambient_transfer = [] self.pwm_max_power = [ (temp, heater.get_max_power() * power) for temp, power in self.const_heater_power @@ -1180,35 +1234,6 @@ def __init__(self, profile, heater, load_clean=False): def _heater_temp(self): return self.heater.get_temp(self.heater.reactor.monotonic())[0] - def _load_profile(self): - self.const_block_heat_capacity = self.profile["block_heat_capacity"] - self.const_ambient_transfer = self.profile["ambient_transfer"] - self.const_target_reach_time = self.profile["target_reach_time"] - const_heater_power = self.profile["heater_power"] - const_heater_powers = self.profile["heater_powers"] - if const_heater_power is not None: - self.const_heater_power = [ - (self.heater.min_temp, const_heater_power), - (self.heater.max_temp, const_heater_power), - ] - else: - self.const_heater_power = const_heater_powers - self.const_smoothing = self.profile["smoothing"] - self.const_sensor_responsiveness = self.profile["sensor_responsiveness"] - self.const_min_ambient_change = self.profile["min_ambient_change"] - self.const_steady_state_rate = self.profile["steady_state_rate"] - self.const_filament_diameter = self.profile["filament_diameter"] - self.const_filament_density = self.profile["filament_density"] - self.const_filament_heat_capacity = self.profile["filament_heat_capacity"] - self.const_maximum_retract = self.profile["maximum_retract"] - self.filament_temp_src = self.profile["filament_temp_src"] - self._update_filament_const() - self.ambient_sensor = self.profile["ambient_temperature_sensor"] - self.cooling_fan = self.profile["cooling_fan"] - self.const_fan_ambient_transfer = self.profile["fan_ambient_transfer"] - if self.const_fan_ambient_transfer is None: - self.const_fan_ambient_transfer = [] - def is_valid(self): return ( self.const_block_heat_capacity is not None @@ -1362,14 +1387,14 @@ def temperature_update(self, read_time, temp, target_temp): power = max( 0.0, min( - get_power_at_temp(target_temp, self.pwm_max_power), + ControlMPC.get_power_at_temp(target_temp, self.pwm_max_power), heating_power + loss_ambient + loss_filament, ), ) else: power = 0 - heater_power = get_power_at_temp(temp, self.const_heater_power) + heater_power = ControlMPC.get_power_at_temp(temp, self.const_heater_power) duty = max(0.0, min(self.heater_max_power, power / heater_power)) diff --git a/klippy/extras/mpc_calibrate.py b/klippy/extras/mpc_calibrate.py index cc5bbd3f7..3f520712f 100644 --- a/klippy/extras/mpc_calibrate.py +++ b/klippy/extras/mpc_calibrate.py @@ -1,6 +1,6 @@ import math import logging -from extras.heaters import get_power_at_temp +from extras.heaters import ControlMPC PIN_MIN_TIME = 0.100 @@ -104,7 +104,9 @@ def run(self, gcmd): samples = self.heatup_test(gcmd, target_temp, control) first_res = self.process_first_pass( samples, - get_power_at_temp(target_temp, self.temp_control.pwm_max_power), + ControlMPC.get_power_at_temp( + target_temp, self.temp_control.pwm_max_power + ), ambient_temp, threshold_temp, use_analytic, @@ -136,7 +138,9 @@ def run(self, gcmd): first_res, transfer_res, ambient_temp, - get_power_at_temp(second_target_temp, self.temp_control.pwm_max_power), + ControlMPC.get_power_at_temp( + second_target_temp, self.temp_control.pwm_max_power + ), ) logging.info("Second pass: %s", second_res) @@ -151,9 +155,7 @@ def run(self, gcmd): else first_res["sensor_responsiveness"] ) ambient_transfer = second_res["ambient_transfer"] - fan_ambient_transfer = ", ".join( - [f"{p:.6g}" for p in second_res["fan_ambient_transfer"]] - ) + fan_ambient_transfer = second_res["fan_ambient_transfer"] profile["block_heat_capacity"] = block_heat_capacity profile["ambient_transfer"] = ambient_transfer @@ -165,16 +167,20 @@ def run(self, gcmd): self.heater.set_control(self.heater.lookup_control(profile, True), False) self.heater.pmgr.save_profile(profile_name=profile_name, verbose=False) - gcmd.respond_info( + msg = ( f"Finished MPC calibration of heater '{self.heater.short_name}'\n" "Measured:\n " f" block_heat_capacity={block_heat_capacity:#.6g} [J/K]\n" f" sensor_responsiveness={sensor_responsiveness:#.6g} [K/s/K]\n" f" ambient_transfer={ambient_transfer:#.6g} [W/K]\n" - f" fan_ambient_transfer={fan_ambient_transfer} [W/K]\n" + ) + if fan_ambient_transfer: + msg += f" fan_ambient_transfer={', '.join([f'{p:.6g}' for p in fan_ambient_transfer])} [W/K]\n" + msg += ( "The SAVE_CONFIG command will update the printer config file\n" "with these parameters and restart the printer." ) + gcmd.respond_info(msg) except self.printer.command_error as e: self.heater.set_control(self.orig_control, False) @@ -468,13 +474,10 @@ def process_second_pass(self, first_res, transfer_res, ambient_temp, heater_powe / (first_res["t1"] - asymp_T) ) - if transfer_res["fan_powers"]: - fan_ambient_transfer = [ - power / target_ambient_temp - for (_speed, power) in transfer_res["fan_powers"] - ] - else: - fan_ambient_transfer = None + fan_ambient_transfer = [ + power / target_ambient_temp + for (_speed, power) in transfer_res["fan_powers"] + ] return { "ambient_transfer": ambient_transfer, diff --git a/klippy/extras/z_tilt_ng.py b/klippy/extras/z_tilt_ng.py index 794be3712..ea9b0a74e 100644 --- a/klippy/extras/z_tilt_ng.py +++ b/klippy/extras/z_tilt_ng.py @@ -402,7 +402,7 @@ def cal_finalize(self, offsets, positions): z_offsets = [z - offsets[2] for z in z_offsets] self.z_offsets = z_offsets s_zoff = "" - for off in z_offsets[0 : self.num_probe_points]: + for off in z_offsets[: self.num_probe_points]: s_zoff += "%.6f, " % off s_zoff = s_zoff[:-2] self.cal_gcmd.respond_info("final z_offsets are: %s" % (s_zoff)) @@ -550,7 +550,7 @@ def ad_finalize_done(self, offsets): configfile = self.printer.lookup_object("configfile") section = self.section s_zoff = "" - for off in z_offsets: + for off in z_offsets[: len(self.z_positions)]: s_zoff += "%.6f, " % off s_zoff = s_zoff[:-2] configfile.set(section, "z_offsets", s_zoff) From a33f893112379219b31b4af4e9536d84c7716e87 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Fri, 13 Sep 2024 16:53:05 +0200 Subject: [PATCH 137/158] Update heater_profile_manager.py --- klippy/extras/heater_profile_manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/klippy/extras/heater_profile_manager.py b/klippy/extras/heater_profile_manager.py index 9dccdfca5..d49cf7186 100644 --- a/klippy/extras/heater_profile_manager.py +++ b/klippy/extras/heater_profile_manager.py @@ -58,7 +58,6 @@ def _check_value_config( elif ( isinstance(type, tuple) and len(type) == 4 - and isinstance(type[0], str) and type[0] == "lists" ): value = config_section.getlists( From 9e68438345646bbd811500103c31cb454640126a Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sat, 14 Sep 2024 12:14:50 +0200 Subject: [PATCH 138/158] . --- klippy/extras/heater_profile_manager.py | 17 +-- klippy/extras/heaters.py | 139 +++++++++++++----------- 2 files changed, 83 insertions(+), 73 deletions(-) diff --git a/klippy/extras/heater_profile_manager.py b/klippy/extras/heater_profile_manager.py index d49cf7186..8f2c0a263 100644 --- a/klippy/extras/heater_profile_manager.py +++ b/klippy/extras/heater_profile_manager.py @@ -55,11 +55,7 @@ def _check_value_config( ) elif type == "floatlist": value = config_section.getfloatlist(key, default=default) - elif ( - isinstance(type, tuple) - and len(type) == 4 - and type[0] == "lists" - ): + elif isinstance(type, tuple) and len(type) == 4 and type[0] == "lists": value = config_section.getlists( key, seps=type[1], @@ -105,9 +101,7 @@ def _check_value_gcmd( else: value = gcmd.get(name, default) if not can_be_none and value is None: - raise self.heater.gcode.error( - "heater_profile: '%s' has to be specified." % name - ) + raise gcmd.error("heater_profile: '%s' has to be specified." % name) return value.lower() if type == "lower" else value def init_default_profile(self): @@ -125,9 +119,9 @@ def set_values(self, profile_name, gcmd, verbose=True): pmgr=self, gcmd=gcmd, control=control, profile_name=profile_name ) if save_profile: - self.save_profile(profile_name=profile_name, gcmd=gcmd, verbose=verbose) + self.save_profile(profile_name=profile_name, verbose=verbose) - def get_values(self, profile_name, gcmd): + def get_values(self, profile_name=None, gcmd=None): temp_profile = self.heater.get_control().get_profile() target = temp_profile["pid_target"] tolerance = temp_profile["pid_tolerance"] @@ -174,7 +168,6 @@ def save_profile(self, profile_name=None, gcmd=None, verbose=True): pmgr=self, temp_profile=temp_profile, profile_name=profile_name, - gcmd=gcmd, verbose=verbose, ) if profile_name is not None: @@ -262,7 +255,7 @@ def load_profile(self, profile_name, gcmd): ) self.heater.gcode.respond_info(msg) - def remove_profile(self, profile_name, gcmd): + def remove_profile(self, profile_name, gcmd=None): if profile_name in self.profiles: section_name = self._compute_section_name(profile_name) self.heater.configfile.remove_section(section_name) diff --git a/klippy/extras/heaters.py b/klippy/extras/heaters.py index e1304c75b..c98cefbd5 100644 --- a/klippy/extras/heaters.py +++ b/klippy/extras/heaters.py @@ -42,7 +42,7 @@ "mpc_target": (float, "%.2f", None, True), "control": (str, "%s", "mpc", False), "heater_power": (float, "%f", None, True), - "heater_powers": (("lists", (",", "\n"), float, 2), "%s", None, True), + "heater_powers": (("lists", (",", "\n"), float, 2), "%.2f, %.2f\n", None, True), "cooling_fan": (str, "%s", None, True), "ambient_temp_sensor": (str, "%s", None, True), "filament_temperature_source": (str, "%s", "ambient", False), @@ -423,6 +423,7 @@ def cmd_SET_HEATER_PID(self, gcmd): def cmd_MPC_SET(self, gcmd): if not isinstance(self.control, ControlMPC): raise gcmd.error("Not a MPC controlled heater") + save_profile = gcmd.get("SAVE_PROFILE", None) self.control.const_filament_diameter = gcmd.get_float( "FILAMENT_DIAMETER", self.control.const_filament_diameter ) @@ -432,7 +433,9 @@ def cmd_MPC_SET(self, gcmd): self.control.const_filament_heat_capacity = gcmd.get_float( "FILAMENT_HEAT_CAPACITY", self.control.const_filament_heat_capacity ) - self.control._update_filament_const() + self.control.update_filament_const() + if save_profile is not None: + self.pmgr.save_profile(profile_name=save_profile) ###################################################################### @@ -496,7 +499,7 @@ def set_values(pmgr, gcmd, control, profile_name): pmgr.heater.gcode.respond_info(msg) @staticmethod - def save_profile(pmgr, temp_profile, profile_name=None, gcmd=None, verbose=True): + def save_profile(pmgr, temp_profile, profile_name=None, verbose=True): if profile_name is None: profile_name = temp_profile["name"] section_name = pmgr._compute_section_name(profile_name) @@ -511,8 +514,8 @@ def save_profile(pmgr, temp_profile, profile_name=None, gcmd=None, verbose=True) pmgr.heater.configfile.set(section_name, key, placeholder % value) temp_profile["name"] = profile_name pmgr.profiles[profile_name] = temp_profile - if verbose and gcmd is not None: - gcmd.respond_info( + if verbose: + pmgr.heater.gcode.respond_info( "Current Heater profile for heater [%s] " "has been saved to profile [%s] " "for the current session. The SAVE_CONFIG command will\n" @@ -574,7 +577,10 @@ def init_profile(config_section, name, pmgr): temp_profile[key] = pmgr._check_value_config( key, config_section, type, can_be_none, default=default ) - if name != "default": + if name == "default": + temp_profile["smooth_time"] = None + temp_profile["smoothing_elements"] = None + else: profile_version = config_section.getint("profile_version", 0) if HEATER_PROFILE_VERSION != profile_version: logging.info( @@ -638,7 +644,7 @@ def set_values(pmgr, gcmd, control, profile_name): pmgr.heater.gcode.respond_info(msg) @staticmethod - def save_profile(pmgr, temp_profile, profile_name=None, gcmd=None, verbose=True): + def save_profile(pmgr, temp_profile, profile_name=None, verbose=True): if profile_name is None: profile_name = temp_profile["name"] section_name = pmgr._compute_section_name(profile_name) @@ -656,8 +662,8 @@ def save_profile(pmgr, temp_profile, profile_name=None, gcmd=None, verbose=True) pmgr.heater.configfile.set(section_name, key, placeholder % value) temp_profile["name"] = profile_name pmgr.profiles[profile_name] = temp_profile - if verbose and gcmd is not None: - gcmd.respond_info( + if verbose: + pmgr.heater.gcode.respond_info( "Current Heater profile for heater [%s] " "has been saved to profile [%s] " "for the current session. The SAVE_CONFIG command will\n" @@ -763,8 +769,8 @@ def set_values(pmgr, gcmd, control, profile_name): ControlPID.set_values(pmgr, gcmd, control, profile_name) @staticmethod - def save_profile(pmgr, temp_profile, profile_name=None, gcmd=None, verbose=True): - ControlPID.save_profile(pmgr, temp_profile, profile_name, gcmd, verbose) + def save_profile(pmgr, temp_profile, profile_name=None, verbose=True): + ControlPID.save_profile(pmgr, temp_profile, profile_name, verbose) @staticmethod def median(temps): @@ -905,8 +911,8 @@ def set_values(pmgr, gcmd, control, profile_name): ControlPID.set_values(pmgr, gcmd, control, profile_name) @staticmethod - def save_profile(pmgr, temp_profile, profile_name=None, gcmd=None, verbose=True): - ControlPID.save_profile(pmgr, temp_profile, profile_name, gcmd, verbose) + def save_profile(pmgr, temp_profile, profile_name=None, verbose=True): + ControlPID.save_profile(pmgr, temp_profile, profile_name, verbose) def __init__(self, profile, heater, load_clean=False): self.profile = profile @@ -1105,13 +1111,14 @@ def init_profile(config_section, name, pmgr=None): @staticmethod def set_values(pmgr, gcmd, control, profile_name): - pass - # TODO + gcmd.respond_info( + "Can not create a mpc profile by command.\n" + "If you want to set the filament parameters,\n" + "use the 'MPC_SET' command." + ) @staticmethod - def save_profile( - pmgr, temp_profile=None, profile_name=None, gcmd=None, verbose=True - ): + def save_profile(pmgr, temp_profile, profile_name=None, verbose=True): if profile_name is None: profile_name = temp_profile["name"] section_name = pmgr._compute_section_name(profile_name) @@ -1131,22 +1138,31 @@ def save_profile( if key == "heater_powers": powers = "" for temp, power in value: - powers += "%.2f, %.2f\n" % (temp, power) + powers += placeholder % (temp, power) pmgr.heater.configfile.set(section_name, key, powers) - elif key == "fan_ambient_transfer" and value: - pmgr.heater.configfile.set( - section_name, key, ", ".join([placeholder % p for p in value]) - ) + elif key == "fan_ambient_transfer": + if value: + pmgr.heater.configfile.set( + section_name, + key, + ", ".join([placeholder % p for p in value]), + ) elif key == "ambient_temp_sensor" or key == "cooling_fan": pmgr.heater.configfile.set(section_name, key, value.name) elif key == "filament_temperature_source": - pmgr.heater.configfile.set(section_name, key, value[-1]) + if value[-1] != default: + pmgr.heater.configfile.set( + section_name, key, placeholder % value[-1] + ) else: - pmgr.heater.configfile.set(section_name, key, placeholder % value) + if value != default: + pmgr.heater.configfile.set( + section_name, key, placeholder % value + ) temp_profile["name"] = profile_name pmgr.profiles[profile_name] = temp_profile - if verbose and gcmd is not None: - gcmd.respond_info( + if verbose: + pmgr.heater.gcode.respond_info( "Current Heater profile for heater [%s] " "has been saved to profile [%s] " "for the current session. The SAVE_CONFIG command will\n" @@ -1186,6 +1202,23 @@ def __init__(self, profile, heater, load_clean=False): self.profile = profile self.heater = heater self.printer = heater.printer + + self._init_profile() + + self.want_ambient_refresh = self.ambient_sensor is not None + self.state_block_temp = AMBIENT_TEMP if load_clean else self._heater_temp() + self.state_sensor_temp = self.state_block_temp + self.state_ambient_temp = AMBIENT_TEMP + + self.last_power = 0.0 + self.last_loss_ambient = 0.0 + self.last_loss_filament = 0.0 + self.last_time = 0.0 + self.last_temp_time = 0.0 + + self.toolhead = self.printer.lookup_object("toolhead") + + def _init_profile(self): self.const_block_heat_capacity = self.profile["block_heat_capacity"] self.const_ambient_transfer = self.profile["ambient_transfer"] self.const_target_reach_time = self.profile["target_reach_time"] @@ -1206,28 +1239,15 @@ def __init__(self, profile, heater, load_clean=False): self.const_filament_heat_capacity = self.profile["filament_heat_capacity"] self.const_maximum_retract = self.profile["maximum_retract"] self.filament_temp_src = self.profile["filament_temp_src"] - self._update_filament_const() self.ambient_sensor = self.profile["ambient_temperature_sensor"] self.cooling_fan = self.profile["cooling_fan"] self.const_fan_ambient_transfer = self.profile["fan_ambient_transfer"] self.pwm_max_power = [ - (temp, heater.get_max_power() * power) + (temp, self.heater.get_max_power() * power) for temp, power in self.const_heater_power ] - self.heater_max_power = heater.get_max_power() - - self.want_ambient_refresh = self.ambient_sensor is not None - self.state_block_temp = AMBIENT_TEMP if load_clean else self._heater_temp() - self.state_sensor_temp = self.state_block_temp - self.state_ambient_temp = AMBIENT_TEMP - - self.last_power = 0.0 - self.last_loss_ambient = 0.0 - self.last_loss_filament = 0.0 - self.last_time = 0.0 - self.last_temp_time = 0.0 - - self.toolhead = None + self.heater_max_power = self.heater.get_max_power() + self.update_filament_const() # Helpers @@ -1253,7 +1273,7 @@ def check_valid(self): f"'ambient_transfer' settings are defined for '{name}'." ) - def _update_filament_const(self): + def update_filament_const(self): radius = self.const_filament_diameter / 2.0 self.const_filament_cross_section_heat_capacity = ( (radius * radius) # mm^2 @@ -1278,23 +1298,20 @@ def temperature_update(self, read_time, temp, target_temp): extrude_speed_prev = 0.0 extrude_speed_next = 0.0 if target_temp != 0.0: - if self.toolhead is None: - self.toolhead = self.printer.lookup_object("toolhead") - if self.toolhead is not None: - extruder = self.toolhead.get_extruder() - if ( - hasattr(extruder, "find_past_position") - and extruder.get_heater() == self.heater - ): - pos = extruder.find_past_position(read_time) - - pos_prev = extruder.find_past_position(read_time - dt) - pos_moved = max(-self.const_maximum_retract, pos - pos_prev) - extrude_speed_prev = pos_moved / dt - - pos_next = extruder.find_past_position(read_time + dt) - pos_move = max(-self.const_maximum_retract, pos_next - pos) - extrude_speed_next = pos_move / dt + extruder = self.toolhead.get_extruder() + if ( + hasattr(extruder, "find_past_position") + and extruder.get_heater() == self.heater + ): + pos = extruder.find_past_position(read_time) + + pos_prev = extruder.find_past_position(read_time - dt) + pos_moved = max(-self.const_maximum_retract, pos - pos_prev) + extrude_speed_prev = pos_moved / dt + + pos_next = extruder.find_past_position(read_time + dt) + pos_move = max(-self.const_maximum_retract, pos_next - pos) + extrude_speed_next = pos_move / dt # Modulate ambient transfer coefficient with fan speed ambient_transfer = self.const_ambient_transfer From 2666e93b28635a70cebe98b393b480d8f681f1c0 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sat, 14 Sep 2024 13:02:20 +0200 Subject: [PATCH 139/158] Update heaters.py --- klippy/extras/heaters.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/klippy/extras/heaters.py b/klippy/extras/heaters.py index c98cefbd5..3b148f224 100644 --- a/klippy/extras/heaters.py +++ b/klippy/extras/heaters.py @@ -389,7 +389,9 @@ def cmd_SET_SMOOTHING_ELEMENTS(self, gcmd): ) self.get_control().update_smoothing_elements() if save_to_profile: - self.get_control().get_profile()["smoothing_elements"] = self.smooth_time + self.get_control().get_profile()[ + "smoothing_elements" + ] = self.smoothing_elements self.pmgr.save_profile() cmd_SET_HEATER_PID_help = "Sets a heater PID parameter" @@ -453,8 +455,12 @@ def init_profile(config_section, name, pmgr=None): default, can_be_none, ) in WATERMARK_PROFILE_OPTIONS.items(): + if key == "max_delta": + above = 0.0 + else: + above = None temp_profile[key] = pmgr._check_value_config( - key, config_section, type, can_be_none, default=default + key, config_section, type, can_be_none, default=default, above=above ) if name != "default": profile_version = config_section.getint("profile_version", None) @@ -574,8 +580,16 @@ def init_profile(config_section, name, pmgr): default, can_be_none, ) in PID_PROFILE_OPTIONS.items(): + if ( + key == "smooth_time" + or key == "smoothing_elements" + or key == "pid_tolerance" + ): + above = 0.0 + else: + above = None temp_profile[key] = pmgr._check_value_config( - key, config_section, type, can_be_none, default=default + key, config_section, type, can_be_none, default=default, above=above ) if name == "default": temp_profile["smooth_time"] = None @@ -740,6 +754,8 @@ def set_pid_ki(self, ki): self.Ki = ki / PID_PARAM_BASE if self.Ki: self.temp_integ_max = self.heater_max_power / self.Ki + else: + self.temp_integ_max = 0.0 def set_pid_kd(self, kd): self.Kd = kd / PID_PARAM_BASE From 4f7d3076c67b3d4cd2b1c7837f09edbf3e88be07 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 15 Sep 2024 15:27:53 +0200 Subject: [PATCH 140/158] . --- klippy/extras/z_tilt.py | 10 +++++----- klippy/extras/z_tilt_ng.py | 10 +++++----- klippy/klippy.py | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/klippy/extras/z_tilt.py b/klippy/extras/z_tilt.py index 900474b26..8a7630dcd 100644 --- a/klippy/extras/z_tilt.py +++ b/klippy/extras/z_tilt.py @@ -194,13 +194,13 @@ def __init__(self, config): self.z_positions = config.getlists( "z_positions", seps=(",", "\n"), parser=float, count=2 ) - self.use_offsets = config.getboolean("use_probe_offsets", None) - if self.use_offsets is None: - self.use_offsets = config.getboolean("use_offsets", None) - if self.use_offsets is not None: + self.use_probe_offsets = config.getboolean("use_probe_offsets", None) + if self.use_probe_offsets is None: + self.use_probe_offsets = config.getboolean("use_offsets", None) + if self.use_probe_offsets is not None: config.deprecate("use_offsets") else: - self.use_offsets = False + self.use_probe_offsets = False self.retry_helper = RetryHelper(config) self.probe_helper = probe.ProbePointsHelper( config, self.probe_finalize, enable_adaptive_move_z=True diff --git a/klippy/extras/z_tilt_ng.py b/klippy/extras/z_tilt_ng.py index ea9b0a74e..d2e5ee573 100644 --- a/klippy/extras/z_tilt_ng.py +++ b/klippy/extras/z_tilt_ng.py @@ -223,13 +223,13 @@ def __init__(self, config): self.z_positions = config.getlists( "z_positions", seps=(",", "\n"), parser=float, count=2 ) - self.use_offsets = config.getboolean("use_probe_offsets", None) - if self.use_offsets is None: - self.use_offsets = config.getboolean("use_offsets", None) - if self.use_offsets is not None: + self.use_probe_offsets = config.getboolean("use_probe_offsets", None) + if self.use_probe_offsets is None: + self.use_probe_offsets = config.getboolean("use_offsets", None) + if self.use_probe_offsets is not None: config.deprecate("use_offsets") else: - self.use_offsets = False + self.use_probe_offsets = False self.z_count = len(self.z_positions) self.retry_helper = RetryHelper(config) diff --git a/klippy/klippy.py b/klippy/klippy.py index 05c42b962..2cbe0efca 100644 --- a/klippy/klippy.py +++ b/klippy/klippy.py @@ -379,8 +379,8 @@ def send_event(self, event, *params): def request_exit(self, result): if self.run_result is None: self.run_result = result - self.klipper_threads.end() self.reactor.end() + self.klipper_threads.end() wait_interrupted = WaitInterruption From e618b6b8620774e6046aae23362ae51ddbd8341d Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 15 Sep 2024 15:35:17 +0200 Subject: [PATCH 141/158] Update mcu.py --- klippy/mcu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/mcu.py b/klippy/mcu.py index a1cac51d7..1752ee00d 100644 --- a/klippy/mcu.py +++ b/klippy/mcu.py @@ -802,7 +802,7 @@ def __init__(self, config, clocksync): self.non_critical_recon_timer = self._reactor.register_timer( self.non_critical_recon_event ) - self.non_critical_disconnected = False + self.non_critical_disconnected = True self._non_critical_reconnect_event_name = ( f"danger:non_critical_mcu_{self.get_name()}:reconnected" ) From 7072e3b26eceb656d9dcd55c9c37763c534e87a2 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 15 Sep 2024 15:38:47 +0200 Subject: [PATCH 142/158] Update mcu.py --- klippy/mcu.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/klippy/mcu.py b/klippy/mcu.py index 1752ee00d..77dcf9c4a 100644 --- a/klippy/mcu.py +++ b/klippy/mcu.py @@ -802,7 +802,7 @@ def __init__(self, config, clocksync): self.non_critical_recon_timer = self._reactor.register_timer( self.non_critical_recon_event ) - self.non_critical_disconnected = True + self.non_critical_disconnected = None self._non_critical_reconnect_event_name = ( f"danger:non_critical_mcu_{self.get_name()}:reconnected" ) @@ -1358,7 +1358,11 @@ def _restart_rpi_usb(self): chelper.run_hub_ctrl(1) def _firmware_restart(self, force=False): - if (self._is_mcu_bridge and not force) or self.non_critical_disconnected: + if ( + (self._is_mcu_bridge and not force) + or self.non_critical_disconnected + or self.non_critical_disconnected is None + ): return if self._restart_method == "rpi_usb": self._restart_rpi_usb() From de92568b62a0d74c23c967798f72d40f43d62e4d Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 15 Sep 2024 15:48:35 +0200 Subject: [PATCH 143/158] Update heaters.py --- klippy/extras/heaters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/heaters.py b/klippy/extras/heaters.py index 3b148f224..7f41451c1 100644 --- a/klippy/extras/heaters.py +++ b/klippy/extras/heaters.py @@ -1254,7 +1254,7 @@ def _init_profile(self): self.const_filament_density = self.profile["filament_density"] self.const_filament_heat_capacity = self.profile["filament_heat_capacity"] self.const_maximum_retract = self.profile["maximum_retract"] - self.filament_temp_src = self.profile["filament_temp_src"] + self.filament_temp_src = self.profile["filament_temperature_source"] self.ambient_sensor = self.profile["ambient_temperature_sensor"] self.cooling_fan = self.profile["cooling_fan"] self.const_fan_ambient_transfer = self.profile["fan_ambient_transfer"] From 9f84a471a7fd4d6e326092a067a689e37b5c9eb1 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 15 Sep 2024 15:50:39 +0200 Subject: [PATCH 144/158] Update heaters.py --- klippy/extras/heaters.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/klippy/extras/heaters.py b/klippy/extras/heaters.py index 7f41451c1..22d12c682 100644 --- a/klippy/extras/heaters.py +++ b/klippy/extras/heaters.py @@ -45,7 +45,7 @@ "heater_powers": (("lists", (",", "\n"), float, 2), "%.2f, %.2f\n", None, True), "cooling_fan": (str, "%s", None, True), "ambient_temp_sensor": (str, "%s", None, True), - "filament_temperature_source": (str, "%s", "ambient", False), + "filament_temp_source": (str, "%s", "ambient", False), "smoothing": (float, "%f", 0.25, False), "target_reach_time": (float, "%f", 2.0, False), "maximum_retract": (float, "%f", 2.0, False), @@ -1069,7 +1069,7 @@ def init_profile(config_section, name, pmgr=None): ) filament_temp_src_raw = ( - temp_profile["filament_temperature_source"].lower().strip() + temp_profile["filament_temp_source"].lower().strip() ) if filament_temp_src_raw == "sensor": filament_temp_src = (FILAMENT_TEMP_SRC_SENSOR,) @@ -1083,7 +1083,7 @@ def init_profile(config_section, name, pmgr=None): f"Unable to parse option 'filament_temperature_source' in section '{config_section.get_name()}'" ) filament_temp_src = (FILAMENT_TEMP_SRC_FIXED, value) - temp_profile["filament_temperature_source"] = filament_temp_src + temp_profile["filament_temp_source"] = filament_temp_src ambient_sensor_name = temp_profile["ambient_temp_sensor"] ambient_sensor = None @@ -1165,7 +1165,7 @@ def save_profile(pmgr, temp_profile, profile_name=None, verbose=True): ) elif key == "ambient_temp_sensor" or key == "cooling_fan": pmgr.heater.configfile.set(section_name, key, value.name) - elif key == "filament_temperature_source": + elif key == "filament_temp_source": if value[-1] != default: pmgr.heater.configfile.set( section_name, key, placeholder % value[-1] @@ -1254,7 +1254,7 @@ def _init_profile(self): self.const_filament_density = self.profile["filament_density"] self.const_filament_heat_capacity = self.profile["filament_heat_capacity"] self.const_maximum_retract = self.profile["maximum_retract"] - self.filament_temp_src = self.profile["filament_temperature_source"] + self.filament_temp_src = self.profile["filament_temp_source"] self.ambient_sensor = self.profile["ambient_temperature_sensor"] self.cooling_fan = self.profile["cooling_fan"] self.const_fan_ambient_transfer = self.profile["fan_ambient_transfer"] From f4283f2d8f63d11dfbc4519ea896cf341a0a0c49 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 15 Sep 2024 15:52:32 +0200 Subject: [PATCH 145/158] Update heaters.py --- klippy/extras/heaters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/heaters.py b/klippy/extras/heaters.py index 22d12c682..12a74deb1 100644 --- a/klippy/extras/heaters.py +++ b/klippy/extras/heaters.py @@ -1255,7 +1255,7 @@ def _init_profile(self): self.const_filament_heat_capacity = self.profile["filament_heat_capacity"] self.const_maximum_retract = self.profile["maximum_retract"] self.filament_temp_src = self.profile["filament_temp_source"] - self.ambient_sensor = self.profile["ambient_temperature_sensor"] + self.ambient_sensor = self.profile["ambient_temp_sensor"] self.cooling_fan = self.profile["cooling_fan"] self.const_fan_ambient_transfer = self.profile["fan_ambient_transfer"] self.pwm_max_power = [ From a833bdfc1df47dde5212cc57b001c48709c15922 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 15 Sep 2024 16:04:40 +0200 Subject: [PATCH 146/158] Update heaters.py --- klippy/extras/heaters.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/klippy/extras/heaters.py b/klippy/extras/heaters.py index 12a74deb1..7d693ad1c 100644 --- a/klippy/extras/heaters.py +++ b/klippy/extras/heaters.py @@ -45,6 +45,7 @@ "heater_powers": (("lists", (",", "\n"), float, 2), "%.2f, %.2f\n", None, True), "cooling_fan": (str, "%s", None, True), "ambient_temp_sensor": (str, "%s", None, True), + "filament_temp_sensor": (str, "%s", None, True), "filament_temp_source": (str, "%s", "ambient", False), "smoothing": (float, "%f", 0.25, False), "target_reach_time": (float, "%f", 2.0, False), @@ -59,9 +60,10 @@ "fan_ambient_transfer": ("floatlist", "%.6f", [], True), "sensor_responsiveness": (float, "%f", None, True), } -FILAMENT_TEMP_SRC_AMBIENT = "ambient" FILAMENT_TEMP_SRC_FIXED = "fixed" +FILAMENT_TEMP_SRC_AMBIENT = "ambient" FILAMENT_TEMP_SRC_SENSOR = "sensor" +FILAMENT_TEMP_SRC_SENSOR_AMBIENT = "sensor_ambient" class Heater: @@ -1068,10 +1070,10 @@ def init_profile(config_section, name, pmgr=None): + name ) - filament_temp_src_raw = ( - temp_profile["filament_temp_source"].lower().strip() - ) - if filament_temp_src_raw == "sensor": + filament_temp_src_raw = temp_profile["filament_temp_source"].lower().strip() + if filament_temp_src_raw == "sensor_ambient": + filament_temp_src = (FILAMENT_TEMP_SRC_SENSOR_AMBIENT,) + elif filament_temp_src_raw == "sensor": filament_temp_src = (FILAMENT_TEMP_SRC_SENSOR,) elif filament_temp_src_raw == "ambient": filament_temp_src = (FILAMENT_TEMP_SRC_AMBIENT,) @@ -1256,6 +1258,7 @@ def _init_profile(self): self.const_maximum_retract = self.profile["maximum_retract"] self.filament_temp_src = self.profile["filament_temp_source"] self.ambient_sensor = self.profile["ambient_temp_sensor"] + self.filament_temp_sensor = self.profile["filament_temp_sensor"] self.cooling_fan = self.profile["cooling_fan"] self.const_fan_ambient_transfer = self.profile["fan_ambient_transfer"] self.pwm_max_power = [ @@ -1459,8 +1462,15 @@ def filament_temp(self, read_time, ambient_temp): src = self.filament_temp_src if src[0] == FILAMENT_TEMP_SRC_FIXED: return src[1] - elif src[0] == FILAMENT_TEMP_SRC_SENSOR and self.ambient_sensor is not None: + elif ( + src[0] == FILAMENT_TEMP_SRC_SENSOR_AMBIENT + and self.ambient_sensor is not None + ): return self.ambient_sensor.get_temp(read_time)[0] + elif ( + src[0] == FILAMENT_TEMP_SRC_SENSOR and self.filament_temp_sensor is not None + ): + return self.filament_temp_sensor.get_temp(read_time)[0] else: return ambient_temp From 54792161a9c45cb6af85f5f1d93a758754a983c9 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 15 Sep 2024 17:10:34 +0200 Subject: [PATCH 147/158] Update bed_mesh.py --- klippy/extras/bed_mesh.py | 49 ++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index 474c5d0a5..425b21de0 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -557,32 +557,39 @@ def _init_mesh_config(self, config): orig_cfg["mesh_x_pps"] = mesh_cfg["mesh_x_pps"] = pps[0] orig_cfg["mesh_y_pps"] = mesh_cfg["mesh_y_pps"] = pps[1] - self.scan_probe_count = parse_config_pair( - config, - "scan_probe_count", - default=None, - minval=3, - default_x=x_cnt, - default_y=y_cnt, - ) - - self.scan_mesh_pps = parse_config_pair( - config, - "scan_mesh_pps", - default=None, - minval=0, - default_x=pps[0], - default_y=pps[1], - ) - - self.scan_speed = config.getfloat("scan_speed", None, above=0.0) - orig_cfg["algo"] = mesh_cfg["algo"] = ( config.get("algorithm", "lagrange").strip().lower() ) + orig_cfg["tension"] = mesh_cfg["tension"] = config.getfloat( "bicubic_tension", 0.2, minval=0.0, maxval=2.0 ) + + if self.radius is None: + self.scan_probe_count = parse_config_pair( + config, + "scan_probe_count", + default=None, + minval=3, + default_x=x_cnt, + default_y=y_cnt, + ) + + self.scan_mesh_pps = parse_config_pair( + config, + "scan_mesh_pps", + default=None, + minval=0, + default_x=pps[0], + default_y=pps[1], + ) + + self.scan_algo = config.get("scan_algorithm", orig_cfg["algo"]).strip().lower() + + self.scan_tension = config.getfloat("bicubic_tension", orig_cfg["tension"], minval=0.0, maxval=2.0) + + self.scan_speed = config.getfloat("scan_speed", None, above=0.0) + for i in list(range(1, 100, 1)): start = config.getfloatlist("faulty_region_%d_min" % (i,), None, count=2) if start is None: @@ -901,7 +908,7 @@ def cmd_BED_MESH_CALIBRATE(self, gcmd): if beacon is not None: if ( gcmd.get("PROBE_METHOD", beacon.default_mesh_method).lower() - != "contact" + == "scan" and horizontal_move_z <= beacon.trigger_dive_threshold ): beacon_scan = True From 2c6b5d1d074223632e599232c6fd322ec0e41a4d Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 15 Sep 2024 18:50:17 +0200 Subject: [PATCH 148/158] Update bed_mesh.py --- klippy/extras/bed_mesh.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index 425b21de0..e644f76ff 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -571,8 +571,8 @@ def _init_mesh_config(self, config): "scan_probe_count", default=None, minval=3, - default_x=x_cnt, - default_y=y_cnt, + default_x=orig_cfg["x_count"], + default_y=orig_cfg["y_count"], ) self.scan_mesh_pps = parse_config_pair( @@ -580,13 +580,13 @@ def _init_mesh_config(self, config): "scan_mesh_pps", default=None, minval=0, - default_x=pps[0], - default_y=pps[1], + default_x=orig_cfg["mesh_x_pps"], + default_y=orig_cfg["mesh_y_pps"], ) self.scan_algo = config.get("scan_algorithm", orig_cfg["algo"]).strip().lower() - self.scan_tension = config.getfloat("bicubic_tension", orig_cfg["tension"], minval=0.0, maxval=2.0) + self.scan_tension = config.getfloat("scan_bicubic_tension", orig_cfg["tension"], minval=0.0, maxval=2.0) self.scan_speed = config.getfloat("scan_speed", None, above=0.0) @@ -818,6 +818,8 @@ def update_config(self, gcmd, beacon_scan=False, recompute=True): self.mesh_config["y_count"] = self.scan_probe_count[1] self.mesh_config["mesh_x_pps"] = self.scan_mesh_pps[0] self.mesh_config["mesh_y_pps"] = self.scan_mesh_pps[1] + self.mesh_config["algo"] = self.scan_algo + self.mesh_config["tension"] = self.scan_tension need_cfg_update = True if self.radius is not None: if "MESH_RADIUS" in params: From be09c361d026600a368b793d47c9ae617207fd6f Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 15 Sep 2024 18:51:07 +0200 Subject: [PATCH 149/158] Update bed_mesh.py --- klippy/extras/bed_mesh.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index e644f76ff..43002e7bf 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -565,7 +565,7 @@ def _init_mesh_config(self, config): "bicubic_tension", 0.2, minval=0.0, maxval=2.0 ) - if self.radius is None: + if True: self.scan_probe_count = parse_config_pair( config, "scan_probe_count", From f6fabd72e3753fb4dcf1b203c6c94b5b8fc653ea Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 15 Sep 2024 18:58:40 +0200 Subject: [PATCH 150/158] Update bed_mesh.py --- klippy/extras/bed_mesh.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index 43002e7bf..e644f76ff 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -565,7 +565,7 @@ def _init_mesh_config(self, config): "bicubic_tension", 0.2, minval=0.0, maxval=2.0 ) - if True: + if self.radius is None: self.scan_probe_count = parse_config_pair( config, "scan_probe_count", From fdd6726aa7f00407bef0c5a283613d2ad226ac27 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 15 Sep 2024 19:02:13 +0200 Subject: [PATCH 151/158] Update bed_mesh.py --- klippy/extras/bed_mesh.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index e644f76ff..265f16d9c 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -378,6 +378,9 @@ def __init__(self, config, bedmesh): self.probe_helper = probe.ProbePointsHelper( config, self.probe_finalize, self._get_adjusted_points() ) + self.scan_speed = None + if self.radius is None: + self.scan_speed = config.getfloat("scan_speed", self.probe_helper.speed, above=0.0) self.probe_helper.minimum_points(3) self.probe_helper.use_xy_offsets(True) self.gcode = self.printer.lookup_object("gcode") @@ -565,6 +568,10 @@ def _init_mesh_config(self, config): "bicubic_tension", 0.2, minval=0.0, maxval=2.0 ) + self.scan_probe_count = None + self.scan_mesh_pps = None + self.scan_algo = None + self.scan_tension = None if self.radius is None: self.scan_probe_count = parse_config_pair( config, @@ -588,8 +595,6 @@ def _init_mesh_config(self, config): self.scan_tension = config.getfloat("scan_bicubic_tension", orig_cfg["tension"], minval=0.0, maxval=2.0) - self.scan_speed = config.getfloat("scan_speed", None, above=0.0) - for i in list(range(1, 100, 1)): start = config.getfloatlist("faulty_region_%d_min" % (i,), None, count=2) if start is None: From 6020d35dba75f5b2ae9d7a5eba78f3049f42d024 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 15 Sep 2024 19:08:58 +0200 Subject: [PATCH 152/158] Update bed_mesh.py --- klippy/extras/bed_mesh.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index 265f16d9c..6a2861e11 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -595,6 +595,9 @@ def _init_mesh_config(self, config): self.scan_tension = config.getfloat("scan_bicubic_tension", orig_cfg["tension"], minval=0.0, maxval=2.0) + config.get("contact_mesh_min", None) + config.get("contact_mesh_max", None) + for i in list(range(1, 100, 1)): start = config.getfloatlist("faulty_region_%d_min" % (i,), None, count=2) if start is None: From b0c125c1bd25cf0c3c35ca10862323f46d97526c Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 15 Sep 2024 20:41:15 +0200 Subject: [PATCH 153/158] Update bed_mesh.py --- klippy/extras/bed_mesh.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/klippy/extras/bed_mesh.py b/klippy/extras/bed_mesh.py index 6a2861e11..615d1fe7c 100644 --- a/klippy/extras/bed_mesh.py +++ b/klippy/extras/bed_mesh.py @@ -356,7 +356,7 @@ class ZrefMode: class BedMeshCalibrate: - ALGOS = ["lagrange", "bicubic"] + ALGOS = ["lagrange", "bicubic", "direct"] def __init__(self, config, bedmesh): self.printer = config.get_printer() @@ -380,7 +380,9 @@ def __init__(self, config, bedmesh): ) self.scan_speed = None if self.radius is None: - self.scan_speed = config.getfloat("scan_speed", self.probe_helper.speed, above=0.0) + self.scan_speed = config.getfloat( + "scan_speed", self.probe_helper.speed, above=0.0 + ) self.probe_helper.minimum_points(3) self.probe_helper.use_xy_offsets(True) self.gcode = self.printer.lookup_object("gcode") @@ -591,9 +593,13 @@ def _init_mesh_config(self, config): default_y=orig_cfg["mesh_y_pps"], ) - self.scan_algo = config.get("scan_algorithm", orig_cfg["algo"]).strip().lower() + self.scan_algo = ( + config.get("scan_algorithm", orig_cfg["algo"]).strip().lower() + ) - self.scan_tension = config.getfloat("scan_bicubic_tension", orig_cfg["tension"], minval=0.0, maxval=2.0) + self.scan_tension = config.getfloat( + "scan_bicubic_tension", orig_cfg["tension"], minval=0.0, maxval=2.0 + ) config.get("contact_mesh_min", None) config.get("contact_mesh_max", None) @@ -654,7 +660,7 @@ def _verify_algorithm(self, error): # Check the algorithm against the current configuration max_probe_cnt = max(params["x_count"], params["y_count"]) min_probe_cnt = min(params["x_count"], params["y_count"]) - if max(x_pps, y_pps) == 0: + if max(x_pps, y_pps) == 0 or self.mesh_config["algo"] == "direct": # Interpolation disabled self.mesh_config["algo"] = "direct" elif params["algo"] == "lagrange" and max_probe_cnt > 6: @@ -917,8 +923,7 @@ def cmd_BED_MESH_CALIBRATE(self, gcmd): ) if beacon is not None: if ( - gcmd.get("PROBE_METHOD", beacon.default_mesh_method).lower() - == "scan" + gcmd.get("PROBE_METHOD", beacon.default_mesh_method).lower() == "scan" and horizontal_move_z <= beacon.trigger_dive_threshold ): beacon_scan = True From 9529dd1cba6a52831aa8003736b6282277893158 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Sun, 15 Sep 2024 22:54:18 +0200 Subject: [PATCH 154/158] Update heater_fan.py --- klippy/extras/heater_fan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/klippy/extras/heater_fan.py b/klippy/extras/heater_fan.py index 5cd7ef1e9..fe7f6fc6b 100644 --- a/klippy/extras/heater_fan.py +++ b/klippy/extras/heater_fan.py @@ -16,7 +16,7 @@ def __init__(self, config): self.printer.register_event_handler("klippy:ready", self.handle_ready) self.heater_names = config.getlist("heater", ("extruder",)) self.heater_temp = config.getfloat("heater_temp", 50.0) - self.heater_temp_off_offset = config.getfloat("heater_temp_off_offset", 0.0) + self.fan_off_hysteresis = config.getfloat("fan_off_hysteresis", 0.0) self.heaters = [] self.fan = fan.Fan(config, default_shutdown_speed=1.0) self.config_fan_speed = config.getfloat( @@ -53,7 +53,7 @@ def callback(self, eventtime): self.last_speed > 0 and ( target_temp - or current_temp > self.heater_temp - self.heater_temp_off_offset + or current_temp > self.heater_temp - self.fan_off_hysteresis ) ): speed = self.fan_speed From 60e6f4c0cccc49189b9bd57fc13888859002e124 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Mon, 16 Sep 2024 15:32:33 +0200 Subject: [PATCH 155/158] Update gcode_macro.py --- klippy/extras/gcode_macro.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/klippy/extras/gcode_macro.py b/klippy/extras/gcode_macro.py index 282e7ca94..48abb51eb 100644 --- a/klippy/extras/gcode_macro.py +++ b/klippy/extras/gcode_macro.py @@ -140,6 +140,10 @@ def _action_respond_info(self, msg): self.printer.lookup_object("gcode").respond_info(msg) return "" + def _action_log(self, msg): + logging.info(msg) + return "" + def _action_raise_error(self, msg): raise self.printer.command_error(msg) @@ -156,6 +160,7 @@ def create_template_context(self, eventtime=None): "printer": GetStatusWrapper(self.printer, eventtime), "action_emergency_stop": self._action_emergency_stop, "action_respond_info": self._action_respond_info, + "action_log": self._action_log, "action_raise_error": self._action_raise_error, "action_call_remote_method": self._action_call_remote_method, "math": math, From c853554de8c4e777ed3900abf1407eaa2b8a5fc6 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 18 Sep 2024 23:34:55 +0200 Subject: [PATCH 156/158] . --- klippy/klipper_threads.py | 32 +++++++++++++++++++++++++++++--- klippy/klippy.py | 5 +++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/klippy/klipper_threads.py b/klippy/klipper_threads.py index bd0593653..b2c861801 100644 --- a/klippy/klipper_threads.py +++ b/klippy/klipper_threads.py @@ -1,13 +1,27 @@ -import logging +import _thread import sys import threading import time +from signal import signal, SIGINT + + +EXCEPTION = [] + + +def handle_sigint(signalnum, handler): + try: + if EXCEPTION: + raise Exception(EXCEPTION[0]) + raise Exception + finally: + EXCEPTION.clear() class KlipperThreads: def __init__(self): self.running = False self.registered_threads = [] + signal(SIGINT, handle_sigint) def run(self): self.running = True @@ -60,12 +74,21 @@ def __init__( ) self.k_threads.registered_threads.append(self) self.running = True + self.initial_wait_time = None - def start(self): + def start(self, wait_time=None): + self.initial_wait_time = wait_time self.thread.start() def _run_job(self, job, args=(), **kwargs): try: + if ( + self.k_threads.running + and self.running + and self.initial_wait_time is not None + ): + time.sleep(self.initial_wait_time) + self.initial_wait_time = None if self.k_threads.running and self.running: wait_time = job(*args, **kwargs) while wait_time > 0 and self.k_threads.running and self.running: @@ -73,10 +96,13 @@ def _run_job(self, job, args=(), **kwargs): if not self.running: return wait_time = job(*args, **kwargs) + sys.exit() + except Exception as e: + EXCEPTION.append(e) + _thread.interrupt_main() finally: self.k_threads.registered_threads.remove(self) self.thread = None - sys.exit() def end(self): self.running = False diff --git a/klippy/klippy.py b/klippy/klippy.py index 2cbe0efca..c241755a8 100644 --- a/klippy/klippy.py +++ b/klippy/klippy.py @@ -5,6 +5,8 @@ # # This file may be distributed under the terms of the GNU GPLv3 license. import sys, os, gc, optparse, logging, time, collections, importlib, importlib.util +import threading + import util, reactor, queuelogger, msgproto, klipper_threads import gcode, configfile, pins, mcu, toolhead, webhooks, non_critical_mcus from extras.danger_options import get_danger_options @@ -355,6 +357,9 @@ def set_rollover_info(self, name, info, log=True): self.bglogger.set_rollover_info(name, info) def invoke_shutdown(self, msg): + if threading.current_thread() is not threading.main_thread(): + self.invoke_async_shutdown(msg) + return if self.in_shutdown_state: return logging.error("Transition to shutdown state: %s", msg) From 75e5e03a56b21a61abb64ebd3b0cf859fe953064 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 18 Sep 2024 23:34:59 +0200 Subject: [PATCH 157/158] Update tmc.py --- klippy/extras/tmc.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/klippy/extras/tmc.py b/klippy/extras/tmc.py index 4d6b5a6ff..6056b149a 100644 --- a/klippy/extras/tmc.py +++ b/klippy/extras/tmc.py @@ -147,6 +147,12 @@ def __init__(self, config, mcu_tmc): if self.adc_temp_reg is not None: pheaters = self.printer.load_object(config, "heaters") pheaters.register_monitor(config) + self.query_while_disabled = config.getboolean("query_while_disabled", False) + if self.query_while_disabled: + self.printer.register_event_handler("klippy:ready", self._handle_ready) + + def _handle_ready(self): + self.printer.get_reactor().register_callback(self.start_checks) def _query_register(self, reg_info, try_clear=False): last_value, reg_name, mask, err_mask, cs_actual_mask = reg_info @@ -209,12 +215,12 @@ def _do_periodic_check(self, eventtime): return eventtime + 1.0 def stop_checks(self): - if self.check_timer is None: + if self.check_timer is None or self.query_while_disabled: return self.printer.get_reactor().unregister_timer(self.check_timer) self.check_timer = None - def start_checks(self): + def start_checks(self, eventtime=None): if self.check_timer is not None: self.stop_checks() cleared_flags = 0 From 88721613fbe10f616cdf58a3a7900b73467a9701 Mon Sep 17 00:00:00 2001 From: Zeanon Date: Wed, 18 Sep 2024 23:35:03 +0200 Subject: [PATCH 158/158] Update temperature_combined.py --- klippy/extras/temperature_combined.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/klippy/extras/temperature_combined.py b/klippy/extras/temperature_combined.py index 3411bfbbb..11a466d5b 100644 --- a/klippy/extras/temperature_combined.py +++ b/klippy/extras/temperature_combined.py @@ -4,9 +4,6 @@ # Copyright (C) 2023 Michael Jäger # # This file may be distributed under the terms of the GNU GPLv3 license. -import threading -import time - from extras.danger_options import get_danger_options REPORT_TIME = 0.300 @@ -83,14 +80,13 @@ def update_temp(self, eventtime): if not self.initialized: initialized = True for sensor in self.sensors: - if not sensor.initialized: + if hasattr(sensor, "initialized") and not sensor.initialized: initialized = False self.initialized = initialized values = [] for sensor in self.sensors: - if sensor.initialized: - sensor_status = sensor.get_status(eventtime) - sensor_temperature = sensor_status["temperature"] + if not hasattr(sensor, "initialized") or sensor.initialized: + sensor_temperature = sensor.get_status(eventtime)["temperature"] if sensor_temperature is not None: values.append(sensor_temperature) @@ -98,9 +94,11 @@ def update_temp(self, eventtime): # check if values are out of max_deviation range if not self.ignore and (max(values) - min(values)) > self.max_deviation: self.printer.invoke_shutdown( - "COMBINED SENSOR maximum deviation exceeded limit of %0.1f, " + "[temperature_combined %s]\n" + "Maximum deviation exceeded limit of %0.1f, " "max sensor value %0.1f, min sensor value %0.1f." % ( + self.name, self.max_deviation, max(values), min(values),