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
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