mirror of
				https://github.com/Klipper3d/klipper.git
				synced 2025-10-31 18:36:09 +01:00 
			
		
		
		
	buttons: Debounce gcode_button and filament_switch_sensor (#6848)
Add `debounce_delay` config option which sets the debounce time, defaults to 0 Signed-off-by: Gareth Farrington <gareth@waves.ky>
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							06d65ef5ac
						
					
				
				
					commit
					272e815522
				
			| @@ -3278,6 +3278,10 @@ pin: | |||||||
| #   A list of G-Code commands to execute when the button is released. | #   A list of G-Code commands to execute when the button is released. | ||||||
| #   G-Code templates are supported. The default is to not run any | #   G-Code templates are supported. The default is to not run any | ||||||
| #   commands on a button release. | #   commands on a button release. | ||||||
|  | #debounce_delay: | ||||||
|  | #   A period of time in seconds to debounce events prior to running the | ||||||
|  | #   button gcode. If the button is pressed and released during this | ||||||
|  | #   delay, the entire button press is ignored. Default is 0. | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ### [output_pin] | ### [output_pin] | ||||||
| @@ -4641,6 +4645,11 @@ more information. | |||||||
| #   dispatch and execution of the runout_gcode. It may be useful to | #   dispatch and execution of the runout_gcode. It may be useful to | ||||||
| #   increase this delay if OctoPrint exhibits strange pause behavior. | #   increase this delay if OctoPrint exhibits strange pause behavior. | ||||||
| #   Default is 0.5 seconds. | #   Default is 0.5 seconds. | ||||||
|  | #debounce_delay: | ||||||
|  | #   A period of time in seconds to debounce events prior to running the | ||||||
|  | #   switch gcode. The switch must he held in a single state for at least | ||||||
|  | #   this long to activate. If the switch is toggled on/off during this delay, | ||||||
|  | #   the event is ignored. Default is 0. | ||||||
| #switch_pin: | #switch_pin: | ||||||
| #   The pin on which the switch is connected. This parameter must be | #   The pin on which the switch is connected. This parameter must be | ||||||
| #   provided. | #   provided. | ||||||
|   | |||||||
| @@ -244,6 +244,33 @@ class HalfStepRotaryEncoder(BaseRotaryEncoder): | |||||||
|          BaseRotaryEncoder.R_START | BaseRotaryEncoder.R_DIR_CCW), |          BaseRotaryEncoder.R_START | BaseRotaryEncoder.R_DIR_CCW), | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|  | class DebounceButton: | ||||||
|  |     def __init__(self, config, button_action): | ||||||
|  |         self.printer = config.get_printer() | ||||||
|  |         self.reactor = self.printer.get_reactor() | ||||||
|  |         self.button_action = button_action | ||||||
|  |         self.debounce_delay = config.getfloat('debounce_delay', 0., minval=0.) | ||||||
|  |         self.logical_state = None | ||||||
|  |         self.physical_state = None | ||||||
|  |         self.latest_eventtime = None | ||||||
|  |     def button_handler(self, eventtime, state): | ||||||
|  |         self.physical_state = state | ||||||
|  |         self.latest_eventtime = eventtime | ||||||
|  |         # if there would be no state transition, ignore the event: | ||||||
|  |         if self.logical_state == self.physical_state: | ||||||
|  |             return | ||||||
|  |         trigger_time = eventtime + self.debounce_delay | ||||||
|  |         self.reactor.register_callback(self._debounce_event, trigger_time) | ||||||
|  |     def _debounce_event(self, eventtime): | ||||||
|  |         # if there would be no state transition, ignore the event: | ||||||
|  |         if self.logical_state == self.physical_state: | ||||||
|  |             return | ||||||
|  |         # if there were more recent events, they supersede this one: | ||||||
|  |         if (eventtime - self.debounce_delay) < self.latest_eventtime: | ||||||
|  |             return | ||||||
|  |         # enact state transition and trigger action | ||||||
|  |         self.logical_state = self.physical_state | ||||||
|  |         self.button_action(self.latest_eventtime, self.logical_state) | ||||||
|  |  | ||||||
| ###################################################################### | ###################################################################### | ||||||
| # Button registration code | # Button registration code | ||||||
| @@ -261,6 +288,14 @@ class PrinterButtons: | |||||||
|             self.adc_buttons[pin] = adc_buttons = MCU_ADC_buttons( |             self.adc_buttons[pin] = adc_buttons = MCU_ADC_buttons( | ||||||
|                 self.printer, pin, pullup) |                 self.printer, pin, pullup) | ||||||
|         adc_buttons.setup_button(min_val, max_val, callback) |         adc_buttons.setup_button(min_val, max_val, callback) | ||||||
|  |     def register_debounce_button(self, pin, callback, config): | ||||||
|  |         debounce = DebounceButton(config, callback) | ||||||
|  |         return self.register_buttons([pin], debounce.button_handler) | ||||||
|  |     def register_debounce_adc_button(self, pin, min_val, max_val, pullup | ||||||
|  |                                      , callback, config): | ||||||
|  |         debounce = DebounceButton(config, callback) | ||||||
|  |         return self.register_adc_button(pin, min_val. min_val, max_val, pullup | ||||||
|  |                                         , debounce.button_handler) | ||||||
|     def register_adc_button_push(self, pin, min_val, max_val, pullup, callback): |     def register_adc_button_push(self, pin, min_val, max_val, pullup, callback): | ||||||
|         def helper(eventtime, state, callback=callback): |         def helper(eventtime, state, callback=callback): | ||||||
|             if state: |             if state: | ||||||
|   | |||||||
| @@ -63,7 +63,7 @@ class EncoderSensor: | |||||||
|     def _extruder_pos_update_event(self, eventtime): |     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 |         # Check for filament runout | ||||||
|         self.runout_helper.note_filament_present( |         self.runout_helper.note_filament_present(eventtime, | ||||||
|                 extruder_pos < self.filament_runout_pos) |                 extruder_pos < self.filament_runout_pos) | ||||||
|         return eventtime + CHECK_RUNOUT_TIMEOUT |         return eventtime + CHECK_RUNOUT_TIMEOUT | ||||||
|     def encoder_event(self, eventtime, state): |     def encoder_event(self, eventtime, state): | ||||||
| @@ -71,7 +71,7 @@ class EncoderSensor: | |||||||
|             self._update_filament_runout_pos(eventtime) |             self._update_filament_runout_pos(eventtime) | ||||||
|             # Check for filament insertion |             # Check for filament insertion | ||||||
|             # Filament is always assumed to be present on an encoder event |             # Filament is always assumed to be present on an encoder event | ||||||
|             self.runout_helper.note_filament_present(True) |             self.runout_helper.note_filament_present(eventtime, True) | ||||||
|  |  | ||||||
| def load_config_prefix(config): | def load_config_prefix(config): | ||||||
|     return EncoderSensor(config) |     return EncoderSensor(config) | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ | |||||||
| # This file may be distributed under the terms of the GNU GPLv3 license. | # This file may be distributed under the terms of the GNU GPLv3 license. | ||||||
| import logging | import logging | ||||||
|  |  | ||||||
|  |  | ||||||
| class RunoutHelper: | class RunoutHelper: | ||||||
|     def __init__(self, config): |     def __init__(self, config): | ||||||
|         self.name = config.get_name().split()[-1] |         self.name = config.get_name().split()[-1] | ||||||
| @@ -24,7 +25,7 @@ class RunoutHelper: | |||||||
|             self.insert_gcode = gcode_macro.load_template( |             self.insert_gcode = gcode_macro.load_template( | ||||||
|                 config, 'insert_gcode') |                 config, 'insert_gcode') | ||||||
|         self.pause_delay = config.getfloat('pause_delay', .5, above=.0) |         self.pause_delay = config.getfloat('pause_delay', .5, above=.0) | ||||||
|         self.event_delay = config.getfloat('event_delay', 3., above=0.) |         self.event_delay = config.getfloat('event_delay', 3., minval=.0) | ||||||
|         # Internal state |         # Internal state | ||||||
|         self.min_event_systime = self.reactor.NEVER |         self.min_event_systime = self.reactor.NEVER | ||||||
|         self.filament_present = False |         self.filament_present = False | ||||||
| @@ -59,19 +60,20 @@ class RunoutHelper: | |||||||
|         except Exception: |         except Exception: | ||||||
|             logging.exception("Script running error") |             logging.exception("Script running error") | ||||||
|         self.min_event_systime = self.reactor.monotonic() + self.event_delay |         self.min_event_systime = self.reactor.monotonic() + self.event_delay | ||||||
|     def note_filament_present(self, is_filament_present): |     def note_filament_present(self, eventtime, is_filament_present): | ||||||
|         if is_filament_present == self.filament_present: |         if is_filament_present == self.filament_present: | ||||||
|             return |             return | ||||||
|         self.filament_present = is_filament_present |         self.filament_present = is_filament_present | ||||||
|         eventtime = self.reactor.monotonic() |  | ||||||
|         if eventtime < self.min_event_systime or not self.sensor_enabled: |         if eventtime < self.min_event_systime or not self.sensor_enabled: | ||||||
|             # do not process during the initialization time, duplicates, |             # do not process during the initialization time, duplicates, | ||||||
|             # during the event delay time, while an event is running, or |             # during the event delay time, while an event is running, or | ||||||
|             # when the sensor is disabled |             # when the sensor is disabled | ||||||
|             return |             return | ||||||
|         # Determine "printing" status |         # Determine "printing" status | ||||||
|  |         now = self.reactor.monotonic() | ||||||
|         idle_timeout = self.printer.lookup_object("idle_timeout") |         idle_timeout = self.printer.lookup_object("idle_timeout") | ||||||
|         is_printing = idle_timeout.get_status(eventtime)["state"] == "Printing" |         is_printing = idle_timeout.get_status(now)["state"] == "Printing" | ||||||
|         # Perform filament action associated with status change (if any) |         # Perform filament action associated with status change (if any) | ||||||
|         if is_filament_present: |         if is_filament_present: | ||||||
|             if not is_printing and self.insert_gcode is not None: |             if not is_printing and self.insert_gcode is not None: | ||||||
| @@ -79,14 +81,14 @@ class RunoutHelper: | |||||||
|                 self.min_event_systime = self.reactor.NEVER |                 self.min_event_systime = self.reactor.NEVER | ||||||
|                 logging.info( |                 logging.info( | ||||||
|                     "Filament Sensor %s: insert event detected, Time %.2f" % |                     "Filament Sensor %s: insert event detected, Time %.2f" % | ||||||
|                     (self.name, eventtime)) |                     (self.name, now)) | ||||||
|                 self.reactor.register_callback(self._insert_event_handler) |                 self.reactor.register_callback(self._insert_event_handler) | ||||||
|         elif is_printing and self.runout_gcode is not None: |         elif is_printing and self.runout_gcode is not None: | ||||||
|             # runout detected |             # runout detected | ||||||
|             self.min_event_systime = self.reactor.NEVER |             self.min_event_systime = self.reactor.NEVER | ||||||
|             logging.info( |             logging.info( | ||||||
|                 "Filament Sensor %s: runout event detected, Time %.2f" % |                 "Filament Sensor %s: runout event detected, Time %.2f" % | ||||||
|                 (self.name, eventtime)) |                 (self.name, now)) | ||||||
|             self.reactor.register_callback(self._runout_event_handler) |             self.reactor.register_callback(self._runout_event_handler) | ||||||
|     def get_status(self, eventtime): |     def get_status(self, eventtime): | ||||||
|         return { |         return { | ||||||
| @@ -108,11 +110,12 @@ class SwitchSensor: | |||||||
|         printer = config.get_printer() |         printer = config.get_printer() | ||||||
|         buttons = printer.load_object(config, 'buttons') |         buttons = printer.load_object(config, 'buttons') | ||||||
|         switch_pin = config.get('switch_pin') |         switch_pin = config.get('switch_pin') | ||||||
|         buttons.register_buttons([switch_pin], self._button_handler) |         buttons.register_debounce_button(switch_pin, self._button_handler | ||||||
|  |                                          , config) | ||||||
|         self.runout_helper = RunoutHelper(config) |         self.runout_helper = RunoutHelper(config) | ||||||
|         self.get_status = self.runout_helper.get_status |         self.get_status = self.runout_helper.get_status | ||||||
|     def _button_handler(self, eventtime, state): |     def _button_handler(self, eventtime, state): | ||||||
|         self.runout_helper.note_filament_present(state) |         self.runout_helper.note_filament_present(eventtime, state) | ||||||
|  |  | ||||||
| def load_config_prefix(config): | def load_config_prefix(config): | ||||||
|     return SwitchSensor(config) |     return SwitchSensor(config) | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ | |||||||
| # This file may be distributed under the terms of the GNU GPLv3 license. | # This file may be distributed under the terms of the GNU GPLv3 license. | ||||||
| import logging | import logging | ||||||
|  |  | ||||||
|  |  | ||||||
| class GCodeButton: | class GCodeButton: | ||||||
|     def __init__(self, config): |     def __init__(self, config): | ||||||
|         self.printer = config.get_printer() |         self.printer = config.get_printer() | ||||||
| @@ -13,12 +14,13 @@ class GCodeButton: | |||||||
|         self.last_state = 0 |         self.last_state = 0 | ||||||
|         buttons = self.printer.load_object(config, "buttons") |         buttons = self.printer.load_object(config, "buttons") | ||||||
|         if config.get('analog_range', None) is None: |         if config.get('analog_range', None) is None: | ||||||
|             buttons.register_buttons([self.pin], self.button_callback) |             buttons.register_debounce_button(self.pin, self.button_callback | ||||||
|  |                                              , config) | ||||||
|         else: |         else: | ||||||
|             amin, amax = config.getfloatlist('analog_range', count=2) |             amin, amax = config.getfloatlist('analog_range', count=2) | ||||||
|             pullup = config.getfloat('analog_pullup_resistor', 4700., above=0.) |             pullup = config.getfloat('analog_pullup_resistor', 4700., above=0.) | ||||||
|             buttons.register_adc_button(self.pin, amin, amax, pullup, |             buttons.register_debounce_adc_button(self.pin, amin, amax, pullup, | ||||||
|                                         self.button_callback) |                                         self.button_callback, config) | ||||||
|         gcode_macro = self.printer.load_object(config, 'gcode_macro') |         gcode_macro = self.printer.load_object(config, 'gcode_macro') | ||||||
|         self.press_template = gcode_macro.load_template(config, 'press_gcode') |         self.press_template = gcode_macro.load_template(config, 'press_gcode') | ||||||
|         self.release_template = gcode_macro.load_template(config, |         self.release_template = gcode_macro.load_template(config, | ||||||
|   | |||||||
| @@ -125,7 +125,7 @@ class HallFilamentWidthSensor: | |||||||
|         # Update filament array for lastFilamentWidthReading |         # Update filament array for lastFilamentWidthReading | ||||||
|         self.update_filament_array(last_epos) |         self.update_filament_array(last_epos) | ||||||
|         # Check runout |         # Check runout | ||||||
|         self.runout_helper.note_filament_present( |         self.runout_helper.note_filament_present(eventtime, | ||||||
|             self.runout_dia_min <= self.diameter <= self.runout_dia_max) |             self.runout_dia_min <= self.diameter <= self.runout_dia_max) | ||||||
|         # Does filament exists |         # Does filament exists | ||||||
|         if self.diameter > 0.5: |         if self.diameter > 0.5: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user