diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index 7077274f8..a027ca4c4 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -4807,6 +4807,13 @@ more information. # If set to true the sensor will use the virtual_sd_card module to determine # whether the printer is printing which is more reliable but will not work # when streaming a print over usb or similar. +#always_fire_events: +# If set to true, runout events will always fire no matter whether the sensor +# is enabled or disabled. Usefull for MMUs +#check_on_print_start: +# If set to true, the sensor will be reevaluated when a print starts and if +# no filament is detected the runout_gcode will be run no matter the defined +# runout_distance(immediate_runout_gcode will not be run in this case) ``` ### [filament_motion_sensor] @@ -4834,6 +4841,7 @@ switch_pin: #event_delay: #pause_delay: #smart: +#always_fire_events: # See the "filament_switch_sensor" section for a description of the # above parameters. ``` diff --git a/docs/G-Codes.md b/docs/G-Codes.md index 94091aaa1..c501d7e89 100644 --- a/docs/G-Codes.md +++ b/docs/G-Codes.md @@ -549,29 +549,34 @@ depend on the sensor type defined in the configuration. #### SET_FILAMENT_SENSOR ###### For filament_switch_sensor: `SET_FILAMENT_SENSOR SENSOR= [ENABLE=0|1] [RESET=0|1] -[RUNOUT_DISTANCE=] [SMART=0|1]`: Sets values for the -filament sensor. If all parameters are omitted, the current stats will -be reported.
+[RUNOUT_DISTANCE=] [SMART=0|1] [ALWAYS_FIRE_EVENTS=0|1] [CHECK_ON_PRINT_START=0|1]`: +Sets values for the filament sensor. +If all parameters are omitted, the current stats will be reported.
ENABLE sets the filament sensor on/off. If ENABLE is set to 0, the filament sensor will be disabled, if set to 1 it is enabled. If the state of the sensor changes, a reset will be triggered.
RESET removes all pending runout_gcodes and pauses and force a reevaluation of the sensor state.
RUNOUT_DISTANCE sets the runout_distance.
-SMART sets the smart parameter. +SMART sets the smart parameter.
+ALWAYS_FIRE_EVENTS sets the always_fire_events parameter, if set to true, +a reset of the sensor will be triggered.
+CHECK_ON_PRINT_START sets the check_on_print_start parameter. ###### For filament_motion_sensor: `SET_FILAMENT_SENSOR SENSOR= [ENABLE=0|1] [RESET=0|1] -[DETECTION_LENGTH=] [SMART=0|1]`: Sets values for the -filament sensor. If all parameters are omitted, the current stats will -be reported.
+[DETECTION_LENGTH=] [SMART=0|1] [ALWAYS_FIRE_EVENTS=0|1]`: +Sets values for the filament sensor. +If all parameters are omitted, the current stats will be reported.
ENABLE sets the filament sensor on/off. If ENABLE is set to 0, the filament sensor will be disabled, if set to 1 it is enabled. If the sensor was previously disabled and gets enabled, a reset will be triggered.
RESET resets the state of the sensor and sets it to filament detected.
DETECTION_LENGTH sets the detection_length, if the new detection length is different from the old one, a reset will be triggered.
-SMART sets the smart parameter. +SMART sets the smart parameter.
+ALWAYS_FIRE_EVENTS sets the always_fire_events parameter, no reset will +be triggered. ### [firmware_retraction] diff --git a/klippy/extras/filament_motion_sensor.py b/klippy/extras/filament_motion_sensor.py index c245f35e4..226e0cf48 100644 --- a/klippy/extras/filament_motion_sensor.py +++ b/klippy/extras/filament_motion_sensor.py @@ -45,7 +45,7 @@ def _update_filament_runout_pos(self, eventtime=None): if eventtime is None: eventtime = self.reactor.monotonic() self.filament_runout_pos = ( - self._get_extruder_pos(eventtime) + self.detection_length + self.get_extruder_pos(eventtime) + self.detection_length ) def _handle_ready(self): @@ -68,14 +68,14 @@ def _handle_not_printing(self, print_time): self._extruder_pos_update_timer, self.reactor.NEVER ) - def _get_extruder_pos(self, eventtime=None): + def get_extruder_pos(self, eventtime=None): if eventtime is None: eventtime = self.reactor.monotonic() print_time = self.estimated_print_time(eventtime) return self.extruder.find_past_position(print_time) def _extruder_pos_update_event(self, eventtime): - extruder_pos = self._get_extruder_pos(eventtime) + extruder_pos = self.get_extruder_pos(eventtime) # Check for filament runout self.runout_helper.note_filament_present( extruder_pos < self.filament_runout_pos @@ -94,7 +94,8 @@ def get_sensor_status(self): "Filament Sensor %s: %s\n" "Filament Detected: %s\n" "Detection Length: %.2f\n" - "Smart: %s" + "Smart: %s\n" + "Always Fire Events: %s" % ( self.runout_helper.name, ( @@ -105,6 +106,7 @@ def get_sensor_status(self): "true" if self.runout_helper.filament_present else "false", self.detection_length, "true" if self.runout_helper.smart else "false", + "true" if self.runout_helper.always_fire_events else "false", ) ) @@ -118,7 +120,7 @@ def get_info(self, gcmd): return True return False - def reset_needed(self, enable): + def reset_needed(self, enable=None, always_fire_events=None): if enable and not self.runout_helper.sensor_enabled: return True return False diff --git a/klippy/extras/filament_switch_sensor.py b/klippy/extras/filament_switch_sensor.py index 58951ba1d..b8d08f1cb 100644 --- a/klippy/extras/filament_switch_sensor.py +++ b/klippy/extras/filament_switch_sensor.py @@ -5,7 +5,6 @@ # This file may be distributed under the terms of the GNU GPLv3 license. import logging - CHECK_RUNOUT_TIMEOUT = 0.250 @@ -40,15 +39,12 @@ def __init__(self, config, defined_sensor, runout_distance=0): self.filament_present = False self.sensor_enabled = True self.smart = config.getboolean("smart", False) + self.always_fire_events = config.getboolean("always_fire_events", False) self.runout_position = 0.0 self.runout_elapsed = 0.0 self.runout_distance_timer = None - self.force_trigger = False # Register commands and event handlers self.printer.register_event_handler("klippy:ready", self._handle_ready) - self.printer.register_event_handler( - "idle_timeout:printing", self._handle_printing - ) self.gcode.register_mux_command( "QUERY_FILAMENT_SENSOR", "SENSOR", @@ -67,9 +63,6 @@ def __init__(self, config, defined_sensor, runout_distance=0): def _handle_ready(self): self.min_event_systime = self.reactor.monotonic() + 2.0 - def _handle_printing(self, print_time): - self.note_filament_present(self.filament_present, True) - def _runout_event_handler(self, eventtime): if self.immediate_runout_gcode is not None: self._exec_gcode("", self.immediate_runout_gcode) @@ -125,12 +118,18 @@ def _exec_gcode(self, prefix, template): logging.exception("Script running error") self.min_event_systime = self.reactor.monotonic() + self.event_delay - def note_filament_present(self, is_filament_present, force=False): + def note_filament_present( + self, is_filament_present=None, force=False, immediate=False + ): + if is_filament_present is None: + is_filament_present = self.filament_present if is_filament_present == self.filament_present and not force: return self.filament_present = is_filament_present eventtime = self.reactor.monotonic() - if eventtime < self.min_event_systime: + if eventtime < self.min_event_systime or ( + not self.always_fire_events and not self.sensor_enabled + ): # do not process during the initialization time, duplicates, # during the event delay time, while an event is running, or # when the sensor is disabled @@ -166,13 +165,18 @@ def note_filament_present(self, is_filament_present, force=False): "Filament Sensor %s: runout event detected, Time %.2f" % (self.name, eventtime) ) - self.reactor.register_callback(self._runout_event_handler) + self.reactor.register_callback( + self._execute_runout + if immediate + else self._runout_event_handler + ) def get_status(self, eventtime): status = { "filament_detected": bool(self.filament_present), "enabled": bool(self.sensor_enabled), "smart": bool(self.smart), + "always_fire_events": bool(self.always_fire_events), } status.update(self.defined_sensor.sensor_get_status(eventtime)) return status @@ -192,24 +196,32 @@ def cmd_SET_FILAMENT_SENSOR(self, gcmd): enable = gcmd.get_int("ENABLE", None, minval=0, maxval=1) reset = gcmd.get_int("RESET", None, minval=0, maxval=1) smart = gcmd.get_int("SMART", None, minval=0, maxval=1) + always_fire_events = gcmd.get_int( + "ALWAYS_FIRE_EVENTS", None, minval=0, maxval=1 + ) reset_needed = False if ( enable is None and reset is None and smart is None + and always_fire_events is None and self.defined_sensor.get_info(gcmd) ): return if enable is not None: - if self.defined_sensor.reset_needed(enable): - reset_needed = True self.sensor_enabled = enable if reset is not None and reset: reset_needed = True if smart is not None: self.smart = smart + if always_fire_events is not None: + self.always_fire_events = always_fire_events if self.defined_sensor.set_filament_sensor(gcmd): reset_needed = True + if self.defined_sensor.reset_needed( + enable=enable, always_fire_events=always_fire_events + ): + reset_needed = True if reset_needed: self.defined_sensor.reset() @@ -222,6 +234,9 @@ def __init__(self, config): switch_pin = config.get("switch_pin") runout_distance = config.getfloat("runout_distance", 0.0, minval=0.0) buttons.register_buttons([switch_pin], self._button_handler) + self.check_on_print_start = config.getboolean( + "check_on_print_start", False + ) self.reactor = self.printer.get_reactor() self.estimated_print_time = None self.runout_helper = RunoutHelper(config, self, runout_distance) @@ -231,12 +246,19 @@ def __init__(self, config): ) self.get_status = self.runout_helper.get_status self.printer.register_event_handler("klippy:ready", self._handle_ready) + self.printer.register_event_handler( + "idle_timeout:printing", self._handle_printing + ) def _handle_ready(self): self.estimated_print_time = self.printer.lookup_object( "mcu" ).estimated_print_time + def _handle_printing(self, print_time): + if self.check_on_print_start: + self.runout_helper.note_filament_present(None, True, True) + def _button_handler(self, eventtime, state): self.runout_helper.note_filament_present(state) @@ -253,7 +275,9 @@ def get_sensor_status(self): "Filament Detected: %s\n" "Runout Distance: %.2f\n" "Runout Elapsed: %.2f\n" - "Smart: %s" + "Smart: %s\n" + "Always Fire Events: %s\n" + "Check on Print Start: %s" % ( self.runout_helper.name, "enabled" if self.runout_helper.sensor_enabled else "disabled", @@ -261,6 +285,8 @@ def get_sensor_status(self): self.runout_helper.runout_distance, self.runout_helper.runout_elapsed, "true" if self.runout_helper.smart else "false", + "true" if self.runout_helper.always_fire_events else "false", + "true" if self.check_on_print_start else "false", ) ) @@ -268,24 +294,35 @@ def sensor_get_status(self, eventtime): return { "runout_distance": float(self.runout_helper.runout_distance), "runout_elapsed": float(self.runout_helper.runout_elapsed), + "check_on_print_start": bool(self.check_on_print_start), } def get_info(self, gcmd): runout_distance = gcmd.get_float("RUNOUT_DISTANCE", None, minval=0.0) - if runout_distance is None: + check_on_print_start = gcmd.get_int( + "CHECK_ON_PRINT_START", None, minval=0, maxval=1 + ) + if runout_distance is None and check_on_print_start is None: gcmd.respond_info(self.get_sensor_status()) return True return False - def reset_needed(self, enable): - if enable != self.runout_helper.sensor_enabled: + def reset_needed(self, enable=None, always_fire_events=None): + if enable is not None and enable != self.runout_helper.sensor_enabled: + return True + if always_fire_events is not None and always_fire_events: return True return False def set_filament_sensor(self, gcmd): runout_distance = gcmd.get_float("RUNOUT_DISTANCE", None, minval=0.0) + check_on_print_start = gcmd.get_int( + "CHECK_ON_PRINT_START", None, minval=0, maxval=1 + ) if runout_distance is not None: self.runout_helper.runout_distance = runout_distance + if check_on_print_start is not None: + self.check_on_print_start = check_on_print_start # No reset is needed when changing the runout_distance, so we always # return False return False