Skip to content

Commit 3612e65

Browse files
authored
Add files via upload
1 parent 84d8afe commit 3612e65

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+5856
-0
lines changed

modules/aswitch.py

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# aswitch.py Switch and pushbutton classes for asyncio
2+
# Delay_ms A retriggerable delay class. Can schedule a coro on timeout.
3+
# Switch Simple debounced switch class for normally open grounded switch.
4+
# Pushbutton extend the above to support logical state, long press and
5+
# double-click events
6+
# Tested on Pyboard but should run on other microcontroller platforms
7+
# running MicroPython and uasyncio.
8+
9+
# The MIT License (MIT)
10+
#
11+
# Copyright (c) 2017 Peter Hinch
12+
#
13+
# Permission is hereby granted, free of charge, to any person obtaining a copy
14+
# of this software and associated documentation files (the "Software"), to deal
15+
# in the Software without restriction, including without limitation the rights
16+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17+
# copies of the Software, and to permit persons to whom the Software is
18+
# furnished to do so, subject to the following conditions:
19+
#
20+
# The above copyright notice and this permission notice shall be included in
21+
# all copies or substantial portions of the Software.
22+
#
23+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29+
# THE SOFTWARE.
30+
31+
try:
32+
import asyncio_priority as asyncio
33+
except ImportError:
34+
import uasyncio as asyncio
35+
import utime as time
36+
from asyn import launch
37+
# launch: run a callback or initiate a coroutine depending on which is passed.
38+
39+
40+
class Delay_ms(object):
41+
def __init__(self, func=None, args=()):
42+
self.func = func
43+
self.args = args
44+
self._running = False
45+
46+
def stop(self):
47+
self._running = False
48+
49+
def trigger(self, duration): # Update end time
50+
loop = asyncio.get_event_loop()
51+
self.tstop = time.ticks_add(loop.time(), duration)
52+
if not self._running:
53+
# Start a task which stops the delay after its period has elapsed
54+
loop.create_task(self.killer())
55+
self._running = True
56+
57+
def running(self):
58+
return self._running
59+
60+
async def killer(self):
61+
loop = asyncio.get_event_loop()
62+
twait = time.ticks_diff(self.tstop, loop.time())
63+
while twait > 0 and self._running: # Return if stop() called during wait
64+
# Must loop here: might be retriggered
65+
await asyncio.sleep_ms(twait)
66+
twait = time.ticks_diff(self.tstop, loop.time())
67+
if self._running and self.func is not None:
68+
launch(self.func, self.args) # Execute callback
69+
self._running = False
70+
71+
72+
class Switch(object):
73+
debounce_ms = 50
74+
def __init__(self, pin):
75+
self.pin = pin # Should be initialised for input with pullup
76+
self._open_func = False
77+
self._close_func = False
78+
self.switchstate = self.pin.value() # Get initial state
79+
loop = asyncio.get_event_loop()
80+
loop.create_task(self.switchcheck()) # Thread runs forever
81+
82+
def open_func(self, func, args=()):
83+
self._open_func = func
84+
self._open_args = args
85+
86+
def close_func(self, func, args=()):
87+
self._close_func = func
88+
self._close_args = args
89+
90+
# Return current state of switch (0 = pressed)
91+
def __call__(self):
92+
return self.switchstate
93+
94+
async def switchcheck(self):
95+
loop = asyncio.get_event_loop()
96+
while True:
97+
state = self.pin.value()
98+
if state != self.switchstate:
99+
# State has changed: act on it now.
100+
self.switchstate = state
101+
if state == 0 and self.close_func:
102+
launch(self._close_func, self._close_args)
103+
elif state == 1 and self._open_func:
104+
launch(self._open_func, self._open_args)
105+
# Ignore further state changes until switch has settled
106+
await asyncio.sleep_ms(Switch.debounce_ms)
107+
108+
class Pushbutton(object):
109+
debounce_ms = 50
110+
long_press_ms = 1000
111+
double_click_ms = 400
112+
def __init__(self, pin):
113+
self.pin = pin # Initialise for input
114+
self._true_func = False
115+
self._false_func = False
116+
self._double_func = False
117+
self._long_func = False
118+
self.sense = pin.value() # Convert from electrical to logical value
119+
self.buttonstate = self.rawstate() # Initial state
120+
loop = asyncio.get_event_loop()
121+
loop.create_task(self.buttoncheck()) # Thread runs forever
122+
123+
def press_func(self, func, args=()):
124+
self._true_func = func
125+
self._true_args = args
126+
127+
def release_func(self, func, args=()):
128+
self._false_func = func
129+
self._false_args = args
130+
131+
def double_func(self, func, args=()):
132+
self._double_func = func
133+
self._double_args = args
134+
135+
def long_func(self, func, args=()):
136+
self._long_func = func
137+
self._long_args = args
138+
139+
# Current non-debounced logical button state: True == pressed
140+
def rawstate(self):
141+
return bool(self.pin.value() ^ self.sense)
142+
143+
# Current debounced state of button (True == pressed)
144+
def __call__(self):
145+
return self.buttonstate
146+
147+
async def buttoncheck(self):
148+
loop = asyncio.get_event_loop()
149+
if self._long_func:
150+
longdelay = Delay_ms(self._long_func, self._long_args)
151+
if self._double_func:
152+
doubledelay = Delay_ms()
153+
while True:
154+
state = self.rawstate()
155+
# State has changed: act on it now.
156+
if state != self.buttonstate:
157+
self.buttonstate = state
158+
if state:
159+
# Button is pressed
160+
if self._long_func and not longdelay.running():
161+
# Start long press delay
162+
longdelay.trigger(Pushbutton.long_press_ms)
163+
if self._double_func:
164+
if doubledelay.running():
165+
launch(self._double_func, self._double_args)
166+
else:
167+
# First click: start doubleclick timer
168+
doubledelay.trigger(Pushbutton.double_click_ms)
169+
if self._true_func:
170+
launch(self._true_func, self._true_args)
171+
else:
172+
# Button release
173+
if self._long_func and longdelay.running():
174+
# Avoid interpreting a second click as a long push
175+
longdelay.stop()
176+
if self._false_func:
177+
launch(self._false_func, self._false_args)
178+
# Ignore state changes until switch has settled
179+
await asyncio.sleep_ms(Pushbutton.debounce_ms)

0 commit comments

Comments
 (0)