Skip to content

Commit 13da8ab

Browse files
committed
Create separate buffer for keycode commands
1 parent 6e4cf47 commit 13da8ab

File tree

1 file changed

+106
-13
lines changed

1 file changed

+106
-13
lines changed

adafruit_bluefruitspi.py

Lines changed: 106 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
import time
3232
import struct
33+
import binascii
3334
from digitalio import Direction, Pull
3435
from adafruit_bus_device.spi_device import SPIDevice
3536
from micropython import const
@@ -66,16 +67,59 @@
6667
_PACKET_BUTTON_LEN = const(5)
6768
_PACKET_COLOR_LEN = const(6)
6869

70+
_KEY_CODE_CMD = "AT+BLEKEYBOARDCODE=00-00-00-00-00-00-00-00\n"
71+
72+
# TODO: replace with collections.deque in CircuitPython 7
73+
class FIFOBuffer:
74+
"""FIFO buffer vaguely based on collections.deque.
75+
76+
Uses a tuple internally to allow for O(1) enqueue and dequeue
77+
"""
78+
79+
def __init__(self, maxlen=20):
80+
self.maxlen = maxlen
81+
self._buf = (None,) * self.maxlen
82+
self._end_idx = 0
83+
self._front_idx = 0
84+
85+
def enqueue(self, data):
86+
"""Put an item at the end of the FIFO queue"""
87+
if self._buf[self._end_idx] is not None:
88+
raise IndexError("FIFOBuffer full")
89+
self._buf[self._end_idx] = data
90+
self._end_idx += 1
91+
if self._end_idx >= self.maxlen:
92+
self._end_idx = 0
93+
94+
def dequeue(self):
95+
"""Pop an item from the front of the FIFO queue"""
96+
data = self._buf[self._front_idx]
97+
if data is None:
98+
return None
99+
self._buf[self._front_idx] = None
100+
self._front_idx += 1
101+
if self._front_idx >= self.maxlen:
102+
self._front_idx = 0
103+
return data
104+
69105

70106
class BluefruitSPI:
71107
"""Helper for the Bluefruit LE SPI Friend"""
72108

73109
def __init__(
74-
self, spi, cs, irq, reset, debug=False
110+
self, spi, cs, irq, reset, debug=False, fifo_len=20
75111
): # pylint: disable=too-many-arguments
76112
self._irq = irq
77113
self._buf_tx = bytearray(20)
78114
self._buf_rx = bytearray(20)
115+
self._keycode_template = [
116+
bytearray(20),
117+
bytearray(20),
118+
bytearray(20),
119+
bytearray(20),
120+
]
121+
self._fifo_buffer = FIFOBuffer(maxlen=fifo_len)
122+
self._init_keycode_template()
79123
self._debug = debug
80124

81125
# a cache of data, used for packet parsing
@@ -98,6 +142,64 @@ def __init__(
98142

99143
self._spi_device = SPIDevice(spi, cs, baudrate=4000000, phase=0, polarity=0)
100144

145+
def _init_keycode_template(self):
146+
"""Prebuild SDEP packets for AT+BLEKEYBOARDCODE command"""
147+
self._create_sdep_raw(
148+
self._keycode_template[0], _KEY_CODE_CMD[:16], True # AT+BLEKEYBOARDCO
149+
)
150+
self._create_sdep_raw(
151+
self._keycode_template[1], _KEY_CODE_CMD[16:32], True # DE=00-00-00-00-0
152+
)
153+
self._create_sdep_raw(
154+
self._keycode_template[2], _KEY_CODE_CMD[32:48], False # 0-00-00-00\n
155+
)
156+
157+
def send_keyboard_code(self, evt):
158+
"""
159+
Put an AT+BLEKEYBOARDCODE command into the FIFO buffer.
160+
Call pop_keyboard_code() to send a single packet to the Bluefruit.
161+
:param evt: bytearray(8) representing keyboard code to send
162+
"""
163+
evt = binascii.hexlify(evt)
164+
self._keycode_template[1][7:9] = evt[0:2]
165+
# self._keycode_template[1][10:12] = evt[2:4] # Should always be 0
166+
self._keycode_template[1][13:15] = evt[4:6]
167+
self._keycode_template[1][16:18] = evt[6:8]
168+
self._keycode_template[1][19] = evt[8]
169+
self._keycode_template[2][4] = evt[9]
170+
self._keycode_template[2][6:8] = evt[10:12]
171+
self._keycode_template[2][9:11] = evt[12:14]
172+
self._keycode_template[2][12:14] = evt[14:16]
173+
for k in self._keycode_template:
174+
self._fifo_buffer.enqueue(k)
175+
176+
def pop_keyboard_code_queue(self):
177+
"""Send an SDEP packet from the FIFO buffer to the Bluefruit"""
178+
data = self._fifo_buffer.dequeue()
179+
if data is not None:
180+
with self._spi_device as spi:
181+
spi.write(data, end=24)
182+
183+
@staticmethod
184+
def _create_sdep_raw(dest, payload, more):
185+
"""
186+
Create an SDEP packet
187+
:param dest: bytearray(20) to place SDEP packet in
188+
:param payload: iterable with length <= 16 containing the payload data
189+
:param more: True to set the more bit, False otherwise
190+
"""
191+
_more = 0x80 if more else 0
192+
plen = len(payload)
193+
struct.pack_into(
194+
"<BHB16s",
195+
dest,
196+
0,
197+
_MSG_COMMAND,
198+
_SDEP_ATCOMMAND,
199+
plen | _more,
200+
payload,
201+
)
202+
101203
def _cmd(self, cmd): # pylint: disable=too-many-branches
102204
"""
103205
Executes the supplied AT command, which must be terminated with
@@ -112,25 +214,16 @@ def _cmd(self, cmd): # pylint: disable=too-many-branches
112214
print("ERROR: Command too long.")
113215
raise ValueError("Command too long.")
114216

115-
more = 0x80 # More bit is in pos 8, 1 = more data available
217+
more = True
116218
pos = 0
117219
while len(cmd) - pos:
118220
# Construct the SDEP packet
119221
if len(cmd) - pos <= 16:
120222
# Last or sole packet
121-
more = 0
223+
more = False
122224
plen = len(cmd) - pos
123225
plen = min(plen, 16)
124-
# Note the 'more' value in bit 8 of the packet len
125-
struct.pack_into(
126-
"<BHB16s",
127-
self._buf_tx,
128-
0,
129-
_MSG_COMMAND,
130-
_SDEP_ATCOMMAND,
131-
plen | more,
132-
cmd[pos : pos + plen],
133-
)
226+
self._create_sdep_raw(self._buf_tx, cmd[pos : pos + plen], more=more)
134227
if self._debug:
135228
print("Writing: ", [hex(b) for b in self._buf_tx])
136229
else:

0 commit comments

Comments
 (0)