Skip to content

Commit e9b8614

Browse files
committed
Update and also rename duration/time to ticks when measured in ticks
1 parent ab19bce commit e9b8614

File tree

2 files changed

+43
-21
lines changed

2 files changed

+43
-21
lines changed

adafruit_debouncer.py

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,11 @@
6767
# Find out whether the current CircuitPython supports time.monotonic_ns(),
6868
# which doesn't have the accuracy limitation.
6969
if hasattr(time, 'monotonic_ns'):
70-
MONOTONIC_UNITS_PER_SEC = 1_000_000_000
71-
MONOTONIC_TIME = time.monotonic_ns
70+
TICKS_PER_SEC = 1_000_000_000
71+
MONOTONIC_TICKS = time.monotonic_ns
7272
else:
73-
MONOTONIC_UNITS_PER_SEC = 1
74-
MONOTONIC_TIME = time.monotonic
73+
TICKS_PER_SEC = 1
74+
MONOTONIC_TICKS = time.monotonic
7575

7676

7777
class Debouncer(object):
@@ -89,10 +89,13 @@ def __init__(self, io_or_predicate, interval=0.010):
8989
self.function = io_or_predicate
9090
if self.function():
9191
self._set_state(_DEBOUNCED_STATE | _UNSTABLE_STATE)
92-
self.previous_time = 0
93-
self.interval = interval
94-
self._previous_state_duration = 0
95-
self._state_changed_time = 0
92+
self._last_bounce_ticks = 0
93+
self._last_duration_ticks = 0
94+
self._state_changed_ticks = 0
95+
96+
# Could use the .interval setter, but pylint prefers that we explicitly
97+
# set the real underlying attribute:
98+
self._interval_ticks = interval * TICKS_PER_SEC
9699

97100

98101
def _set_state(self, bits):
@@ -113,30 +116,30 @@ def _get_state(self, bits):
113116

114117
def update(self):
115118
"""Update the debouncer state. MUST be called frequently"""
116-
now = MONOTONIC_TIME()
119+
now_ticks = MONOTONIC_TICKS()
117120
self._unset_state(_CHANGED_STATE)
118121
current_state = self.function()
119122
if current_state != self._get_state(_UNSTABLE_STATE):
120-
self.previous_time = now
123+
self._last_bounce_ticks = now_ticks
121124
self._toggle_state(_UNSTABLE_STATE)
122125
else:
123-
if now - self.previous_time >= self._interval:
126+
if now_ticks - self._last_bounce_ticks >= self._interval_ticks:
124127
if current_state != self._get_state(_DEBOUNCED_STATE):
125-
self.previous_time = now
128+
self._last_bounce_ticks = now_ticks
126129
self._toggle_state(_DEBOUNCED_STATE)
127130
self._set_state(_CHANGED_STATE)
128-
self._previous_state_duration = now - self._state_changed_time
129-
self._state_changed_time = now
131+
self._last_duration_ticks = now_ticks - self._state_changed_ticks
132+
self._state_changed_ticks = now_ticks
130133

131134
@property
132135
def interval(self):
133136
"""The debounce delay, in seconds"""
134-
return self._interval / MONOTONIC_UNITS_PER_SEC
137+
return self._interval_ticks / TICKS_PER_SEC
135138

136139

137140
@interval.setter
138141
def interval(self, new_interval_s):
139-
self._interval = new_interval_s * MONOTONIC_UNITS_PER_SEC
142+
self._interval_ticks = new_interval_s * TICKS_PER_SEC
140143

141144

142145
@property
@@ -158,10 +161,10 @@ def fell(self):
158161

159162
@property
160163
def last_duration(self):
161-
"""Return the amount of time the state was stable prior to the most recent transition."""
162-
return self._previous_state_duration
164+
"""Return the number of seconds the state was stable prior to the most recent transition."""
165+
return self._last_duration_ticks / TICKS_PER_SEC
163166

164167
@property
165168
def current_duration(self):
166-
"""Return the time since the most recent transition."""
167-
return time.monotonic() - self._state_changed_time
169+
"""Return the number of seconds since the most recent transition."""
170+
return (MONOTONIC_TICKS() - self._state_changed_ticks) / TICKS_PER_SEC

tests.py renamed to tests/tests.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,30 +13,49 @@ def assertEqual(a, b):
1313
assert a == b, "Want %r, got %r" % (a, b)
1414

1515

16-
def test_simple():
16+
def test_back_and_forth():
17+
# Start false
1718
db = adafruit_debouncer.Debouncer(_false)
1819
assertEqual(db.value, False)
1920

21+
# Set the raw state to true, update, and make sure the debounced
22+
# state has not changed yet:
2023
db.function = _true
2124
db.update()
2225
assertEqual(db.value, False)
26+
assert not db.last_duration, "There was no previous interval??"
27+
28+
# Sleep longer than the debounce interval, so state can change:
2329
time.sleep(0.02)
2430
db.update()
31+
assert db.last_duration # is actually duration between powerup and now
2532
assertEqual(db.value, True)
2633
assertEqual(db.rose, True)
2734
assertEqual(db.fell, False)
35+
# Duration since last change has only been long enough to run these
36+
# asserts, which should be well under 1/10 second
37+
assert db.current_duration < 0.1, "Unit error? %d" % db.current_duration
2838

39+
# Set raw state back to false, make sure it's not instantly reflected,
40+
# then wait and make sure it IS reflected after the interval has passed.
2941
db.function = _false
3042
db.update()
3143
assertEqual(db.value, True)
3244
assertEqual(db.fell, False)
3345
assertEqual(db.rose, False)
3446
time.sleep(0.02)
47+
assert 0.019 < db.current_duration <= 1, \
48+
"Unit error? sleep .02 -> duration %d" % db.current_duration
3549
db.update()
3650
assertEqual(db.value, False)
3751
assertEqual(db.rose, False)
3852
assertEqual(db.fell, True)
3953

54+
assert 0 < db.current_duration <= 0.1, \
55+
"Unit error? time to run asserts %d" % db.current_duration
56+
assert 0 < db.last_duration < 0.1, \
57+
"Unit error? Last dur should be ~.02, is %d" % db.last_duration
58+
4059

4160
def test_interval_is_the_same():
4261
db = adafruit_debouncer.Debouncer(_false, interval=0.25)

0 commit comments

Comments
 (0)