Skip to content

Commit 0c86db0

Browse files
committed
Switch, Pushbutton and Delay_ms: can change callbacks at runtime.
1 parent f1962d4 commit 0c86db0

File tree

5 files changed

+76
-27
lines changed

5 files changed

+76
-27
lines changed

v3/docs/DRIVERS.md

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ goes outside defined bounds.
3232

3333
# 2. Installation and usage
3434

35-
The drivers are in the primitives package. To install copy the `primitives`
35+
The drivers require a daily build of firmware or a release build >=1.15. The
36+
drivers are in the primitives package. To install copy the `primitives`
3637
directory and its contents to the target hardware.
3738

3839
Drivers are imported with:
@@ -158,20 +159,21 @@ Constructor arguments:
158159

159160
Methods:
160161

161-
1. `press_func` Args: `func` (mandatory) a `callable` to run on button push.
162-
`args` a tuple of arguments for the `callable` (default `()`).
163-
2. `release_func` Args: `func` (mandatory) a `callable` to run on button
164-
release. `args` a tuple of arguments for the `callable` (default `()`).
165-
3. `long_func` Args: `func` (mandatory) a `callable` to run on long button
166-
push. `args` a tuple of arguments for the `callable` (default `()`).
167-
4. `double_func` Args: `func` (mandatory) a `callable` to run on double
168-
push. `args` a tuple of arguments for the `callable` (default `()`).
162+
1. `press_func` Args: `func=False` a `callable` to run on button push,
163+
`args=()` a tuple of arguments for the `callable`.
164+
2. `release_func` Args: `func=False` a `callable` to run on button release,
165+
`args=()` a tuple of arguments for the `callable`.
166+
3. `long_func` Args: `func=False` a `callable` to run on long button push,
167+
`args=()` a tuple of arguments for the `callable`.
168+
4. `double_func` Args: `func=False` a `callable` to run on double push,
169+
`args=()` a tuple of arguments for the `callable`.
169170
5. `__call__` Call syntax e.g. `mybutton()` Returns the logical debounced
170171
state of the button (`True` corresponds to pressed).
171172
6. `rawstate()` Returns the logical instantaneous state of the button. There
172173
is probably no reason to use this.
173174

174-
Methods 1 - 4 should be called before starting the scheduler.
175+
Methods 1 - 4 may be called at any time. If `False` is passed for a callable,
176+
any existing callback will be disabled.
175177

176178
Class attributes:
177179
1. `debounce_ms` Debounce time in ms. Default 50.
@@ -188,12 +190,12 @@ def toggle(led):
188190
led.toggle()
189191

190192
async def my_app():
193+
pin = Pin('X1', Pin.IN, Pin.PULL_UP) # Pushbutton to gnd
194+
red = LED(1)
195+
pb = Pushbutton(pin)
196+
pb.press_func(toggle, (red,)) # Note how function and args are passed
191197
await asyncio.sleep(60) # Dummy
192198

193-
pin = Pin('X1', Pin.IN, Pin.PULL_UP) # Pushbutton to gnd
194-
red = LED(1)
195-
pb = Pushbutton(pin)
196-
pb.press_func(toggle, (red,)) # Note how function and args are passed
197199
asyncio.run(my_app()) # Run main application code
198200
```
199201

v3/docs/TUTORIAL.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,6 +1125,8 @@ Methods:
11251125
the `Task` instance. This allows the `Task` to be cancelled or awaited.
11261126
6. `wait` One or more tasks may wait on a `Delay_ms` instance. Execution will
11271127
proceed when the instance has timed out.
1128+
7. `callback` args `func=None`, `args=()`. Allows the callable and its args to
1129+
be assigned, reassigned or disabled at run time.
11281130

11291131
In this example a `Delay_ms` instance is created with the default duration of
11301132
1s. It is repeatedly triggered for 5 secs, preventing the callback from

v3/primitives/delay_ms.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,7 @@ def __call__(self): # Current running status
6565

6666
def rvalue(self):
6767
return self._retn
68+
69+
def callback(self, func=None, args=()):
70+
self._func = func
71+
self._args = args

v3/primitives/pushbutton.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# pushbutton.py
22

3-
# Copyright (c) 2018-2020 Peter Hinch
3+
# Copyright (c) 2018-2021 Peter Hinch
44
# Released under the MIT License (MIT) - see LICENSE file
55

66
import uasyncio as asyncio
@@ -23,34 +23,37 @@ def __init__(self, pin, suppress=False, sense=None):
2323
self._tf = False
2424
self._ff = False
2525
self._df = False
26-
self._lf = False
2726
self._ld = False # Delay_ms instance for long press
2827
self._dd = False # Ditto for doubleclick
2928
self.sense = pin.value() if sense is None else sense # Convert from electrical to logical value
3029
self.state = self.rawstate() # Initial state
3130
asyncio.create_task(self.buttoncheck()) # Thread runs forever
3231

33-
def press_func(self, func, args=()):
32+
def press_func(self, func=False, args=()):
3433
self._tf = func
3534
self._ta = args
3635

37-
def release_func(self, func, args=()):
36+
def release_func(self, func=False, args=()):
3837
self._ff = func
3938
self._fa = args
4039

4140
def double_func(self, func=False, args=()):
4241
self._df = func
4342
self._da = args
44-
if self._dd:
45-
self._dd.stop()
46-
self._dd = Delay_ms(self._ddto) if func else False
43+
if func: # If double timer already in place, leave it
44+
if not self._dd:
45+
self._dd = Delay_ms(self._ddto)
46+
else:
47+
self._dd = False # Clearing down double func
4748

4849
def long_func(self, func=False, args=()):
49-
self._lf = func
50-
self._la = args
51-
if self._ld:
52-
self._ld.stop()
53-
self._ld = Delay_ms(self._lf, self._la) if func else False
50+
if func:
51+
if self._ld:
52+
self._ld.callback(func, args)
53+
else:
54+
self._ld = Delay_ms(func, args)
55+
else:
56+
self._ld = False
5457

5558
# Current non-debounced logical button state: True == pressed
5659
def rawstate(self):
@@ -75,7 +78,7 @@ async def buttoncheck(self):
7578
if state: # Button pressed: launch pressed func
7679
if self._tf:
7780
launch(self._tf, self._ta)
78-
if self._lf: # There's a long func: start long press delay
81+
if self._ld: # There's a long func: start long press delay
7982
self._ld.trigger(Pushbutton.long_press_ms)
8083
if self._df:
8184
if self._dd(): # Second click: timer running

v3/primitives/tests/switches.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
test_swcb Switch with callback
2727
test_btn Pushutton launching coros
2828
test_btncb Pushbutton launching callbacks
29+
btn_dynamic Change coros launched at runtime.
2930
'''
3031
print(tests)
3132

@@ -141,3 +142,40 @@ def test_btncb():
141142
pb.double_func(toggle, (yellow,))
142143
pb.long_func(toggle, (blue,))
143144
run()
145+
146+
# Test for the Pushbutton class where callback coros change dynamically
147+
def setup(pb, press, release, dbl, lng, t=1000):
148+
s = '''
149+
Functions are changed:
150+
LED's pulse for 2 seconds
151+
press pulses blue
152+
release pulses red
153+
double click pulses green
154+
long pulses yellow
155+
'''
156+
pb.press_func(pulse, (press, t))
157+
pb.release_func(pulse, (release, t))
158+
pb.double_func(pulse, (dbl, t))
159+
if lng is not None:
160+
pb.long_func(pulse, (lng, t))
161+
print(s)
162+
163+
def btn_dynamic():
164+
s = '''
165+
press pulses red
166+
release pulses green
167+
double click pulses yellow
168+
long press changes button functions.
169+
'''
170+
print('Test of pushbutton scheduling coroutines.')
171+
print(helptext)
172+
print(s)
173+
pin = Pin('X1', Pin.IN, Pin.PULL_UP)
174+
red = LED(1)
175+
green = LED(2)
176+
yellow = LED(3)
177+
blue = LED(4)
178+
pb = Pushbutton(pin)
179+
setup(pb, red, green, yellow, None)
180+
pb.long_func(setup, (pb, blue, red, green, yellow, 2000))
181+
run()

0 commit comments

Comments
 (0)