Skip to content

Commit 8aa096f

Browse files
committed
feature/hardware: Add keyboard module
Signed-off-by: lbuque <[email protected]>
1 parent 97f2caf commit 8aa096f

File tree

4 files changed

+376
-1
lines changed

4 files changed

+376
-1
lines changed

m5stack/libs/hardware/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@
1111
from .ir import IR
1212
from .rfid import RFID
1313
from .rotary import Rotary
14+
from .keyboard import Keyboard
Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
from machine import Pin
2+
from collections import namedtuple
3+
from .asciimap import (
4+
KEY_BACKSPACE,
5+
KEY_TAB,
6+
KEY_FN,
7+
KEY_LEFT_SHIFT,
8+
KEY_ENTER,
9+
KEY_LEFT_CTRL,
10+
KEY_OPT,
11+
KEY_LEFT_ALT,
12+
kb_asciimap,
13+
)
14+
15+
Point2D = namedtuple('Point2D', ['x', 'y'])
16+
Chart = namedtuple('Chart', ['value', 'x_1', 'x_2'])
17+
KeyValue = namedtuple('KeyValue', ['first', 'second'])
18+
19+
_X_map_chart = (
20+
Chart(1, 0, 1),
21+
Chart(2, 2, 3),
22+
Chart(4, 4, 5),
23+
Chart(8, 6, 7),
24+
Chart(16, 8, 9),
25+
Chart(32, 10, 11),
26+
Chart(64, 12, 13),
27+
)
28+
29+
_key_value_map = (
30+
(
31+
KeyValue(ord('`'), ord('~')),
32+
KeyValue(ord('1'), ord('!')),
33+
KeyValue(ord('2'), ord('@')),
34+
KeyValue(ord('3'), ord('#')),
35+
KeyValue(ord('4'), ord('$')),
36+
KeyValue(ord('5'), ord('%')),
37+
KeyValue(ord('6'), ord('^')),
38+
KeyValue(ord('7'), ord('&')),
39+
KeyValue(ord('8'), ord('*')),
40+
KeyValue(ord('9'), ord('(')),
41+
KeyValue(ord('0'), ord(')')),
42+
KeyValue(ord('-'), ord('_')),
43+
KeyValue(ord('='), ord('+')),
44+
KeyValue(KEY_BACKSPACE, KEY_BACKSPACE),
45+
),
46+
(
47+
KeyValue(KEY_TAB, KEY_TAB),
48+
KeyValue(ord('q'), ord('Q')),
49+
KeyValue(ord('w'), ord('W')),
50+
KeyValue(ord('e'), ord('E')),
51+
KeyValue(ord('r'), ord('R')),
52+
KeyValue(ord('t'), ord('T')),
53+
KeyValue(ord('y'), ord('Y')),
54+
KeyValue(ord('u'), ord('U')),
55+
KeyValue(ord('i'), ord('I')),
56+
KeyValue(ord('o'), ord('O')),
57+
KeyValue(ord('p'), ord('P')),
58+
KeyValue(ord('['), ord('{')),
59+
KeyValue(ord(']'), ord('}')),
60+
KeyValue(ord('\\'), ord('|')),
61+
),
62+
(
63+
KeyValue(KEY_FN, KEY_FN),
64+
KeyValue(KEY_LEFT_SHIFT, KEY_LEFT_SHIFT),
65+
KeyValue(ord('a'), ord('A')),
66+
KeyValue(ord('s'), ord('S')),
67+
KeyValue(ord('d'), ord('D')),
68+
KeyValue(ord('f'), ord('F')),
69+
KeyValue(ord('g'), ord('G')),
70+
KeyValue(ord('h'), ord('H')),
71+
KeyValue(ord('j'), ord('J')),
72+
KeyValue(ord('k'), ord('K')),
73+
KeyValue(ord('l'), ord('L')),
74+
KeyValue(ord(';'), ord(':')),
75+
KeyValue(ord('\''), ord('\"')),
76+
KeyValue(KEY_ENTER, KEY_ENTER),
77+
),
78+
(
79+
KeyValue(KEY_LEFT_CTRL, KEY_LEFT_CTRL),
80+
KeyValue(KEY_OPT, KEY_OPT),
81+
KeyValue(KEY_LEFT_ALT, KEY_LEFT_ALT),
82+
KeyValue(ord('z'), ord('Z')),
83+
KeyValue(ord('x'), ord('X')),
84+
KeyValue(ord('c'), ord('C')),
85+
KeyValue(ord('v'), ord('V')),
86+
KeyValue(ord('b'), ord('B')),
87+
KeyValue(ord('n'), ord('N')),
88+
KeyValue(ord('m'), ord('M')),
89+
KeyValue(ord(','), ord('<')),
90+
KeyValue(ord('.'), ord('>')),
91+
KeyValue(ord('/'), ord('?')),
92+
KeyValue(ord(' '), ord(' ')),
93+
),
94+
)
95+
96+
97+
class KeysState:
98+
tab = False
99+
fn = False
100+
shift = False
101+
ctrl = False
102+
opt = False
103+
alt = False
104+
delete = False
105+
enter = False
106+
space = False
107+
modifiers = 0
108+
109+
def __init__(self) -> None:
110+
self.word = bytearray(0)
111+
self.hid_keys = bytearray(0)
112+
self.modifier_keys = bytearray(0)
113+
114+
def reset(self):
115+
self.tab = False
116+
self.fn = False
117+
self.shift = False
118+
self.ctrl = False
119+
self.opt = False
120+
self.alt = False
121+
self.delete = False
122+
self.enter = False
123+
self.space = False
124+
self.modifiers = 0
125+
self.word = bytearray(0)
126+
self.hid_keys = bytearray(0)
127+
self.modifier_keys = bytearray(0)
128+
129+
130+
class Keyboard:
131+
132+
def __init__(self):
133+
self.output_list = [ Pin(id, Pin.OUT, Pin.PULL_DOWN) for id in (8, 9, 11) ]
134+
self.input_list = [ Pin(id, Pin.IN, Pin.PULL_UP) for id in (13, 15, 3, 4, 5, 6, 7) ]
135+
136+
for pin in self.output_list:
137+
pin.value(0)
138+
139+
self._set_output(self.output_list, 0)
140+
141+
self._key_list_buffer = []
142+
self._key_pos_print_keys = []
143+
self._key_pos_hid_keys = []
144+
self._key_pos_modifier_keys = []
145+
self._keys_state_buffer = KeysState()
146+
self._is_caps_locked = False
147+
self._last_key_size = 0
148+
149+
@staticmethod
150+
def _set_output(pin_list, output):
151+
output = output & 0B00000111
152+
pin_list[0].value(output & 0B00000001)
153+
pin_list[1].value(output & 0B00000010)
154+
pin_list[2].value(output & 0B00000100)
155+
156+
@staticmethod
157+
def _get_input(pin_list):
158+
buffer = 0x00
159+
pin_value = 0x00
160+
161+
for i in range(7):
162+
pin_value = 0x00 if pin_list[i].value() == 1 else 0x01
163+
pin_value = pin_value << i
164+
buffer = buffer | pin_value
165+
166+
return buffer
167+
168+
def getKey(self, point: Point2D):
169+
ret = 0
170+
171+
if point.x < 0 or point.y < 0:
172+
return ret
173+
174+
if (
175+
self._keys_state_buffer.ctrl
176+
or self._keys_state_buffer.shift
177+
or self._is_caps_locked
178+
):
179+
ret = _key_value_map[point.x][point.y].second
180+
else:
181+
ret = _key_value_map[point.x][point.y].first
182+
return ret
183+
184+
def updateKeyList(self):
185+
self._key_list_buffer.clear()
186+
for i in range(8):
187+
self._set_output(self.output_list, i)
188+
input_value = self._get_input(self.input_list)
189+
190+
if input_value != 0:
191+
# Get X
192+
for j in range(7):
193+
if input_value & (0x01 << j):
194+
x = _X_map_chart[j].x_1 if (i > 3) else _X_map_chart[j].x_2
195+
196+
# Get Y
197+
y = (i - 4) if (i > 3) else i
198+
199+
# Keep the same as picture
200+
y = -y
201+
y = y + 3
202+
203+
self._key_list_buffer.append(Point2D(x, y))
204+
205+
def keyList(self) -> list:
206+
return self._key_list_buffer
207+
208+
def getKeyValue(self, point: Point2D) -> KeyValue:
209+
return _key_value_map[point.y][point.x]
210+
211+
def isPressed(self) -> bool:
212+
return len(self._key_list_buffer) > 0
213+
214+
def isChange(self) -> bool:
215+
if self._last_key_size is not len(self._key_list_buffer):
216+
self._last_key_size = len(self._key_list_buffer)
217+
return True
218+
else:
219+
return False
220+
221+
def isKeyPressed(self, ch):
222+
if self._key_list_buffer:
223+
for i in self._key_list_buffer:
224+
if self.getKeyValue(i).first == ch:
225+
return True
226+
return False
227+
228+
def updateKeysState(self):
229+
self._keys_state_buffer.reset()
230+
self._key_pos_print_keys.clear()
231+
self._key_pos_hid_keys.clear()
232+
self._key_pos_modifier_keys.clear()
233+
234+
# Get special keys
235+
for i in self._key_list_buffer:
236+
# modifier
237+
if self.getKeyValue(i).first == KEY_FN:
238+
self._keys_state_buffer.fn = True
239+
continue
240+
if self.getKeyValue(i).first == KEY_OPT:
241+
self._keys_state_buffer.opt = True
242+
continue
243+
244+
if self.getKeyValue(i).first == KEY_LEFT_CTRL:
245+
self._keys_state_buffer.ctrl = True
246+
self._key_pos_modifier_keys.append(i)
247+
continue
248+
249+
if self.getKeyValue(i).first == KEY_LEFT_SHIFT:
250+
self._keys_state_buffer.shift = True
251+
self._key_pos_modifier_keys.append(i)
252+
continue
253+
254+
if self.getKeyValue(i).first == KEY_LEFT_ALT:
255+
self._keys_state_buffer.alt = True
256+
self._key_pos_modifier_keys.append(i)
257+
continue
258+
259+
# function
260+
if self.getKeyValue(i).first == KEY_TAB:
261+
self._keys_state_buffer.tab = True
262+
self._key_pos_hid_keys.append(i)
263+
continue
264+
265+
if self.getKeyValue(i).first == KEY_BACKSPACE:
266+
self._keys_state_buffer.delete = True
267+
self._key_pos_hid_keys.append(i)
268+
continue
269+
270+
if self.getKeyValue(i).first == KEY_ENTER:
271+
self._keys_state_buffer.enter = True
272+
self._key_pos_hid_keys.append(i)
273+
continue
274+
275+
if self.getKeyValue(i).first == ' ':
276+
self._keys_state_buffer.space = True
277+
278+
self._key_pos_hid_keys.append(i)
279+
self._key_pos_print_keys.append(i)
280+
281+
for i in self._key_pos_modifier_keys:
282+
key = self.getKeyValue(i).first
283+
self._keys_state_buffer.modifier_keys.append(key)
284+
285+
for k in self._keys_state_buffer.modifier_keys:
286+
self._keys_state_buffer.modifiers |= (1 << (k - 0x80))
287+
288+
for i in self._key_pos_hid_keys:
289+
k = self.getKeyValue(i).first
290+
if k in (KEY_TAB, KEY_BACKSPACE, KEY_ENTER):
291+
self._keys_state_buffer.hid_keys.append(k)
292+
continue
293+
key = kb_asciimap[k]
294+
if key:
295+
self._keys_state_buffer.hid_keys.append(key)
296+
297+
# Deal with what's left
298+
for i in self._key_pos_print_keys:
299+
if (
300+
self._keys_state_buffer.ctrl
301+
or self._keys_state_buffer.shift
302+
or self._is_caps_locked
303+
):
304+
self._keys_state_buffer.word.append(self.getKeyValue(i).second)
305+
else:
306+
self._keys_state_buffer.word.append(self.getKeyValue(i).first)
307+
308+
def keysState(self) -> KeysState:
309+
return self._keys_state_buffer
310+
311+
def capslocked(self) -> bool:
312+
return self._is_caps_locked
313+
314+
def setCapsLocked(self, isLocked: bool):
315+
self._is_caps_locked = isLocked
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
2+
3+
SHIFT = 0x80
4+
5+
KEY_LEFT_CTRL = 0x80
6+
KEY_LEFT_SHIFT = 0x81
7+
KEY_LEFT_ALT = 0x82
8+
9+
KEY_FN = 0xff
10+
KEY_OPT = 0x00
11+
12+
KEY_BACKSPACE = 0x2a
13+
KEY_TAB = 0x2b
14+
KEY_ENTER = 0x28
15+
16+
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
17+
# | NUL | SOH | STX | ETX | EOT | ENQ | ACK | BEL | BS | TAB | LF | VT | FF | CR | SO | SI |
18+
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
19+
# | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
20+
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
21+
# | DEL | DC1 | DC2 | DC3 | DC4 | NAK | SYN | ETB | CAN | EM | SUB | ESC | FS | GS | RS | US |
22+
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
23+
# | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
24+
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
25+
# | SP | ! | " | # | $ | % | & | ' | ( | ) | * | + | , | - | . | / |
26+
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
27+
# | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
28+
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
29+
# | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | : | ; | < | = | > | ? |
30+
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
31+
# | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
32+
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
33+
# | @ | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O |
34+
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
35+
# | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
36+
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
37+
# | P | Q | R | S | T | U | V | W | X | Y | Z | [ | \ | ] | ^ | _ |
38+
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
39+
# | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
40+
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
41+
# | ` | a | b | c | d | e | f | g | h | i | j | k | l | m | n | o |
42+
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
43+
# | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 |
44+
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
45+
# | p | q | r | s | t | u | v | w | x | y | z | { | | | } | ~ | DEL |
46+
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
47+
# | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 |
48+
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
49+
kb_asciimap = \
50+
b'\x00\x00\x00\x00\x00\x00\x00\x00\x2a\x2b\x28\x00\x00\x00\x00\x00' \
51+
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
52+
b'\x2c\x9e\xb4\xa0\xa1\xa2\xa4\x34\xa6\xa7\xa5\xae\x36\x2d\x37\x38' \
53+
b'\x27\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\xb3\x33\xb6\x2e\xb7\xb8' \
54+
b'\x9f\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92' \
55+
b'\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x2f\x31\x30\xa3\xad' \
56+
b'\x35\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12' \
57+
b'\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\xaf\xb1\xb0\xb5\x00'

m5stack/libs/hardware/manifest.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
"hardware",
33
(
44
"__init__.py",
5+
"keyboard/__init__.py",
6+
"keyboard/asciimap.py",
57
"button.py",
68
"ir.py",
79
"rfid.py",
810
"rgb.py",
9-
"sdcard.py",
1011
"rotary.py",
12+
"sdcard.py",
1113
),
1214
base_path="..",
1315
opt=0,

0 commit comments

Comments
 (0)