6
6
import uasyncio as asyncio
7
7
import utime as time
8
8
from . import launch , Delay_ms
9
+
9
10
try :
10
11
from machine import TouchPad
11
12
except ImportError :
12
13
pass
13
14
15
+
14
16
class Pushbutton :
15
17
debounce_ms = 50
16
18
long_press_ms = 1000
17
19
double_click_ms = 400
20
+
18
21
def __init__ (self , pin , suppress = False , sense = None ):
19
- self .pin = pin # Initialise for input
22
+ self ._pin = pin # Initialise for input
20
23
self ._supp = suppress
21
24
self ._dblpend = False # Doubleclick waiting for 2nd click
22
25
self ._dblran = False # Doubleclick executed user function
@@ -25,10 +28,59 @@ def __init__(self, pin, suppress=False, sense=None):
25
28
self ._df = False
26
29
self ._ld = False # Delay_ms instance for long press
27
30
self ._dd = False # Ditto for doubleclick
28
- self .sense = pin .value () if sense is None else sense # Convert from electrical to logical value
29
- self .state = self .rawstate () # Initial state
30
- self ._run = asyncio .create_task (self .buttoncheck ()) # Thread runs forever
31
+ # Convert from electrical to logical value
32
+ self ._sense = pin .value () if sense is None else sense
33
+ self ._state = self .rawstate () # Initial state
34
+ self ._run = asyncio .create_task (self ._go ()) # Thread runs forever
35
+
36
+ async def _go (self ):
37
+ while True :
38
+ self ._check (self .rawstate ())
39
+ # Ignore state changes until switch has settled. Also avoid hogging CPU.
40
+ # See https://github.com/peterhinch/micropython-async/issues/69
41
+ await asyncio .sleep_ms (Pushbutton .debounce_ms )
42
+
43
+ def _check (self , state ):
44
+ if state == self ._state :
45
+ return
46
+ # State has changed: act on it now.
47
+ self ._state = state
48
+ if state : # Button pressed: launch pressed func
49
+ if self ._tf :
50
+ launch (self ._tf , self ._ta )
51
+ if self ._ld : # There's a long func: start long press delay
52
+ self ._ld .trigger (Pushbutton .long_press_ms )
53
+ if self ._df :
54
+ if self ._dd (): # Second click: timer running
55
+ self ._dd .stop ()
56
+ self ._dblpend = False
57
+ self ._dblran = True # Prevent suppressed launch on release
58
+ launch (self ._df , self ._da )
59
+ else :
60
+ # First click: start doubleclick timer
61
+ self ._dd .trigger (Pushbutton .double_click_ms )
62
+ self ._dblpend = True # Prevent suppressed launch on release
63
+ else : # Button release. Is there a release func?
64
+ if self ._ff :
65
+ if self ._supp :
66
+ d = self ._ld
67
+ # If long delay exists, is running and doubleclick status is OK
68
+ if not self ._dblpend and not self ._dblran :
69
+ if (d and d ()) or not d :
70
+ launch (self ._ff , self ._fa )
71
+ else :
72
+ launch (self ._ff , self ._fa )
73
+ if self ._ld :
74
+ self ._ld .stop () # Avoid interpreting a second click as a long push
75
+ self ._dblran = False
76
+
77
+ def _ddto (self ): # Doubleclick timeout: no doubleclick occurred
78
+ self ._dblpend = False
79
+ if self ._supp and not self ._state :
80
+ if not self ._ld or (self ._ld and not self ._ld ()):
81
+ launch (self ._ff , self ._fa )
31
82
83
+ # ****** API ******
32
84
def press_func (self , func = False , args = ()):
33
85
if func is None :
34
86
self .press = asyncio .Event ()
@@ -67,62 +119,19 @@ def long_func(self, func=False, args=()):
67
119
68
120
# Current non-debounced logical button state: True == pressed
69
121
def rawstate (self ):
70
- return bool (self .pin . value () ^ self .sense )
122
+ return bool (self ._pin () ^ self ._sense )
71
123
72
124
# Current debounced state of button (True == pressed)
73
125
def __call__ (self ):
74
- return self .state
75
-
76
- def _ddto (self ): # Doubleclick timeout: no doubleclick occurred
77
- self ._dblpend = False
78
- if self ._supp and not self .state :
79
- if not self ._ld or (self ._ld and not self ._ld ()):
80
- launch (self ._ff , self ._fa )
81
-
82
- async def buttoncheck (self ):
83
- while True :
84
- state = self .rawstate ()
85
- # State has changed: act on it now.
86
- if state != self .state :
87
- self .state = state
88
- if state : # Button pressed: launch pressed func
89
- if self ._tf :
90
- launch (self ._tf , self ._ta )
91
- if self ._ld : # There's a long func: start long press delay
92
- self ._ld .trigger (Pushbutton .long_press_ms )
93
- if self ._df :
94
- if self ._dd (): # Second click: timer running
95
- self ._dd .stop ()
96
- self ._dblpend = False
97
- self ._dblran = True # Prevent suppressed launch on release
98
- launch (self ._df , self ._da )
99
- else :
100
- # First click: start doubleclick timer
101
- self ._dd .trigger (Pushbutton .double_click_ms )
102
- self ._dblpend = True # Prevent suppressed launch on release
103
- else : # Button release. Is there a release func?
104
- if self ._ff :
105
- if self ._supp :
106
- d = self ._ld
107
- # If long delay exists, is running and doubleclick status is OK
108
- if not self ._dblpend and not self ._dblran :
109
- if (d and d ()) or not d :
110
- launch (self ._ff , self ._fa )
111
- else :
112
- launch (self ._ff , self ._fa )
113
- if self ._ld :
114
- self ._ld .stop () # Avoid interpreting a second click as a long push
115
- self ._dblran = False
116
- # Ignore state changes until switch has settled
117
- # See https://github.com/peterhinch/micropython-async/issues/69
118
- await asyncio .sleep_ms (Pushbutton .debounce_ms )
126
+ return self ._state
119
127
120
128
def deinit (self ):
121
129
self ._run .cancel ()
122
130
123
131
124
132
class ESP32Touch (Pushbutton ):
125
133
thresh = (80 << 8 ) // 100
134
+
126
135
@classmethod
127
136
def threshold (cls , val ):
128
137
if not (isinstance (val , int ) and 0 < val < 100 ):
0 commit comments