# The MIT License (MIT) # # Copyright (c) 2019 Dave Astels for Adafruit Industries # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. """ `adafruit_debouncer` ==================================================== Debounces an arbitrary predicate function (typically created as a lambda) of 0 arguments. Since a very common use is debouncing a digital input pin, the initializer accepts a pin number instead of a lambda. * Author(s): Dave Astels Implementation Notes -------------------- **Hardware:** **Software and Dependencies:** * Adafruit CircuitPython firmware for the supported boards: https://github.com/adafruit/circuitpython/releases """ # imports __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Debouncer.git" import time from micropython import const _DEBOUNCED_STATE = const(0x01) _UNSTABLE_STATE = const(0x02) _CHANGED_STATE = const(0x04) class Debouncer(): """Debounce an input pin or an arbitrary predicate""" def __init__(self, io_or_predicate, interval=0.010): """Make am instance. :param DigitalInOut/function io_or_predicate: the DigitalIO or function to debounce :param int interval: bounce threshold in seconds (default is 0.010, i.e. 10 milliseconds) """ self.state = 0x00 if hasattr(io_or_predicate, "value"): self.function = lambda: io_or_predicate.value else: self.function = io_or_predicate if self.function(): self._set_state(_DEBOUNCED_STATE | _UNSTABLE_STATE) self.previous_time = 0 self.interval = interval self._previous_state_duration = 0 self._state_changed_time = 0 def _set_state(self, bits): self.state |= bits def _unset_state(self, bits): self.state &= ~bits def _toggle_state(self, bits): self.state ^= bits def _get_state(self, bits): return (self.state & bits) != 0 def update(self): """Update the debouncer state. MUST be called frequently""" now = time.monotonic() self._unset_state(_CHANGED_STATE) current_state = self.function() if current_state != self._get_state(_UNSTABLE_STATE): self.previous_time = now self._toggle_state(_UNSTABLE_STATE) else: if now - self.previous_time >= self.interval: if current_state != self._get_state(_DEBOUNCED_STATE): self.previous_time = now self._toggle_state(_DEBOUNCED_STATE) self._set_state(_CHANGED_STATE) self._previous_state_duration = now - self._state_changed_time self._state_changed_time = now @property def value(self): """Return the current debounced value.""" return self._get_state(_DEBOUNCED_STATE) @property def rose(self): """Return whether the debounced value went from low to high at the most recent update.""" return self._get_state(_DEBOUNCED_STATE) and self._get_state(_CHANGED_STATE) @property def fell(self): """Return whether the debounced value went from high to low at the most recent update.""" return (not self._get_state(_DEBOUNCED_STATE)) and self._get_state( _CHANGED_STATE ) @property def last_duration(self): """Return the amount of time the state was stable prior to the most recent transition.""" return self._previous_state_duration @property def current_duration(self): """Return the time since the most recent transition.""" return time.monotonic() - self._state_changed_time