Skip to content

Commit 6c574ad

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

File tree

1 file changed

+109
-13
lines changed

1 file changed

+109
-13
lines changed

adafruit_bluefruitspi.py

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

3131
import time
3232
import struct
33+
try:
34+
import binascii as ba
35+
except ImportError:
36+
import adafruit_binascii as ba
3337
from digitalio import Direction, Pull
3438
from adafruit_bus_device.spi_device import SPIDevice
3539
from micropython import const
@@ -66,16 +70,59 @@
6670
_PACKET_BUTTON_LEN = const(5)
6771
_PACKET_COLOR_LEN = const(6)
6872

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

70109
class BluefruitSPI:
71110
"""Helper for the Bluefruit LE SPI Friend"""
72111

73112
def __init__(
74-
self, spi, cs, irq, reset, debug=False
113+
self, spi, cs, irq, reset, debug=False, fifo_len=20
75114
): # pylint: disable=too-many-arguments
76115
self._irq = irq
77116
self._buf_tx = bytearray(20)
78117
self._buf_rx = bytearray(20)
118+
self._keycode_template = [
119+
bytearray(20),
120+
bytearray(20),
121+
bytearray(20),
122+
bytearray(20),
123+
]
124+
self._fifo_buffer = FIFOBuffer(maxlen=fifo_len)
125+
self._init_keycode_template()
79126
self._debug = debug
80127

81128
# a cache of data, used for packet parsing
@@ -98,6 +145,64 @@ def __init__(
98145

99146
self._spi_device = SPIDevice(spi, cs, baudrate=4000000, phase=0, polarity=0)
100147

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

115-
more = 0x80 # More bit is in pos 8, 1 = more data available
220+
more = True
116221
pos = 0
117222
while len(cmd) - pos:
118223
# Construct the SDEP packet
119224
if len(cmd) - pos <= 16:
120225
# Last or sole packet
121-
more = 0
226+
more = False
122227
plen = len(cmd) - pos
123228
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-
)
229+
self._create_sdep_raw(self._buf_tx, cmd[pos : pos + plen], more=more)
134230
if self._debug:
135231
print("Writing: ", [hex(b) for b in self._buf_tx])
136232
else:

0 commit comments

Comments
 (0)