Skip to content

Commit a56ac56

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

File tree

1 file changed

+110
-13
lines changed

1 file changed

+110
-13
lines changed

adafruit_bluefruitspi.py

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

3131
import time
3232
import struct
33+
34+
try:
35+
import binascii as ba
36+
except ImportError:
37+
import adafruit_binascii as ba
3338
from digitalio import Direction, Pull
3439
from adafruit_bus_device.spi_device import SPIDevice
3540
from micropython import const
@@ -66,16 +71,59 @@
6671
_PACKET_BUTTON_LEN = const(5)
6772
_PACKET_COLOR_LEN = const(6)
6873

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

70110
class BluefruitSPI:
71111
"""Helper for the Bluefruit LE SPI Friend"""
72112

73113
def __init__(
74-
self, spi, cs, irq, reset, debug=False
114+
self, spi, cs, irq, reset, debug=False, fifo_len=20
75115
): # pylint: disable=too-many-arguments
76116
self._irq = irq
77117
self._buf_tx = bytearray(20)
78118
self._buf_rx = bytearray(20)
119+
self._keycode_template = [
120+
bytearray(20),
121+
bytearray(20),
122+
bytearray(20),
123+
bytearray(20),
124+
]
125+
self._fifo_buffer = FIFOBuffer(maxlen=fifo_len)
126+
self._init_keycode_template()
79127
self._debug = debug
80128

81129
# a cache of data, used for packet parsing
@@ -98,6 +146,64 @@ def __init__(
98146

99147
self._spi_device = SPIDevice(spi, cs, baudrate=4000000, phase=0, polarity=0)
100148

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

115-
more = 0x80 # More bit is in pos 8, 1 = more data available
221+
more = True
116222
pos = 0
117223
while len(cmd) - pos:
118224
# Construct the SDEP packet
119225
if len(cmd) - pos <= 16:
120226
# Last or sole packet
121-
more = 0
227+
more = False
122228
plen = len(cmd) - pos
123229
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-
)
230+
self._create_sdep_raw(self._buf_tx, cmd[pos : pos + plen], more=more)
134231
if self._debug:
135232
print("Writing: ", [hex(b) for b in self._buf_tx])
136233
else:

0 commit comments

Comments
 (0)