Description
Good morning!
I'm working on a project that will be continuously powered for months at a time. I'd like to use this (very fine) debouncer implementation, but I think it will lose accuracy over time. As a test, I ran this code on my ItsyBitsy M0 Express for a day or so:
import time
while True:
print("At", time.monotonic(), "- ", end="")
before = time.monotonic()
time.sleep(0.01)
print(time.monotonic() - before)
time.sleep(1)
So it's sleeping for a hundredth of a second, and seeing what the time.monotonic()
difference is over this time.
At 87214.7 - 0.0
At 87215.7 - 0.03125
At 87216.7 - 0.0
At 24 hours or so, most of the hundredths of a second are too fine to be resolved by the floating point number returned by time.monotonic()
, and when it does catch one, it thinks it's a 32nd of a second. So I think you'll have to press a button for longer and longer before the debouncer will acknowledge it.
To fix the issue, I'm thinking about something like this:
diff --git a/adafruit_debouncer.py b/adafruit_debouncer.py
index b4702ff..5567e82 100644
--- a/adafruit_debouncer.py
+++ b/adafruit_debouncer.py
@@ -53,6 +53,14 @@ _DEBOUNCED_STATE = const(0x01)
_UNSTABLE_STATE = const(0x02)
_CHANGED_STATE = const(0x04)
+if hasattr(time, 'monotonic_ns'):
+ INTERVAL_FACTOR = 1_000_000_000
+ MONOTONIC_TIME = time.monotonic_ns
+else:
+ INTERVAL_FACTOR = 1
+ MONOTONIC_TIME = time.monotonic
+
+
class Debouncer(object):
"""Debounce an input pin or an arbitrary predicate"""
@@ -69,7 +77,7 @@ class Debouncer(object):
if self.function():
self._set_state(_DEBOUNCED_STATE | _UNSTABLE_STATE)
self.previous_time = 0
- self.interval = interval
+ self.interval = interval * INTERVAL_FACTOR
def _set_state(self, bits):
@@ -90,7 +98,7 @@ class Debouncer(object):
def update(self):
"""Update the debouncer state. MUST be called frequently"""
- now = time.monotonic()
+ now = MONOTONIC_TIME()
self._unset_state(_CHANGED_STATE)
current_state = self.function()
if current_state != self._get_state(_UNSTABLE_STATE):
There are a few TODOs on that patch:
- Could preserve backwards compatibility for reading
debouncer.interval
by using a separate attribute to storeinterval * INTERVAL_FACTOR
- Could preserve the ability for client code to write
debouncer.interval
by making it into a property with a getter and setter, and doing some bookkeeping - I've never used the micropython
const
system, but I'm sure it would help with speed. I just don't know if it works insideif
statements or anything. I would just test this and see how it works. - I don't know if there's anything helpful to do for systems too small to have
monotonic_ns
, like the Trinket M0. If we could access theticks_ms
CircuitPython internal variable, we could handle it, but we can't see that from Python land :(
So does that sound like something you're interested in? I'm happy to work on the patch and bring a PR if so.