diff --git a/klippy/clocksync.py b/klippy/clocksync.py index 9dbd82de4d..81e2ab2d7e 100644 --- a/klippy/clocksync.py +++ b/klippy/clocksync.py @@ -9,7 +9,11 @@ RTT_AGE = 0.000010 / (60.0 * 60.0) DECAY = 1.0 / 30.0 -TRANSMIT_EXTRA = 0.001 +# Default forward-scheduling headroom added to the clock estimate sent +# to the serialqueue. Can be overridden via [danger_options] +# transmit_extra. Increase this (e.g. to 0.003-0.005) if you +# experience 'Timer too close' shutdowns on a heavily loaded host. +TRANSMIT_EXTRA = 0.002 class ClockSync: @@ -30,6 +34,11 @@ def __init__(self, reactor): self.clock_avg = self.clock_covariance = 0.0 self.prediction_variance = 0.0 self.last_prediction_time = 0.0 + self.transmit_extra = TRANSMIT_EXTRA + + def set_transmit_extra(self, value): + """Override the transmit_extra headroom (called from danger_options).""" + self.transmit_extra = value def disconnect(self): self.reactor.update_timer(self.get_clock_timer, self.reactor.NEVER) @@ -145,7 +154,7 @@ def _handle_clock(self, params): pred_stddev = math.sqrt(self.prediction_variance) self.serial.set_clock_est( new_freq, - self.time_avg + TRANSMIT_EXTRA, + self.time_avg + self.transmit_extra, int(self.clock_avg - 3.0 * pred_stddev), clock, ) @@ -192,7 +201,7 @@ def dump_debug(self): "clocksync state: mcu_freq=%d last_clock=%d" " clock_est=(%.3f %d %.3f) min_half_rtt=%.6f min_rtt_time=%.3f" " time_avg=%.3f(%.3f) clock_avg=%.3f(%.3f)" - " pred_variance=%.3f" + " pred_variance=%.3f transmit_extra=%.6f" % ( self.mcu_freq, self.last_clock, @@ -206,6 +215,7 @@ def dump_debug(self): self.clock_avg, self.clock_covariance, self.prediction_variance, + self.transmit_extra, ) ) diff --git a/klippy/extras/danger_options.py b/klippy/extras/danger_options.py index 554f2978fb..f2f2f54c75 100644 --- a/klippy/extras/danger_options.py +++ b/klippy/extras/danger_options.py @@ -1,3 +1,6 @@ +from ..clocksync import TRANSMIT_EXTRA + + class DangerOptions: def __init__(self, config): self.minimal_logging = config.getboolean("minimal_logging", False) @@ -38,6 +41,14 @@ def __init__(self, config): self.homing_elapsed_distance_tolerance = config.getfloat( "homing_elapsed_distance_tolerance", 0.5, minval=0.0 ) + # transmit_extra: forward-scheduling headroom (seconds) added to the + # MCU clock estimate passed to the serialqueue. Increasing this helps + # prevent 'Timer too close' MCU shutdowns on hosts that experience CPU + # or scheduling jitter. See docs/Config_Reference.md ([danger_options]) + # for guidance. + self.transmit_extra = config.getfloat( + "transmit_extra", TRANSMIT_EXTRA, minval=0.0, maxval=0.010 + ) temp_ignore_limits = False if config.getboolean("temp_ignore_limits", None) is None: @@ -78,4 +89,20 @@ def get_danger_options(): def load_config(config): global DANGER_OPTIONS DANGER_OPTIONS = DangerOptions(config) + # Apply transmit_extra to all ClockSync instances already registered + printer = config.get_printer() + # Wire transmit_extra into MCU clocksync objects at connect time + printer.register_event_handler( + "klippy:mcu_identify", + lambda: _apply_transmit_extra(printer, DANGER_OPTIONS.transmit_extra), + ) return DANGER_OPTIONS + + +def _apply_transmit_extra(printer, value): + """Push transmit_extra into all MCU clocksync objects.""" + for name, obj in printer.objects.items(): + if name == "mcu" or name.startswith("mcu "): + clocksync = getattr(obj, "_clocksync", None) + if clocksync is not None and hasattr(clocksync, "set_transmit_extra"): + clocksync.set_transmit_extra(value)