30
30
31
31
import time
32
32
import struct
33
+
34
+ try :
35
+ import binascii as ba
36
+ except ImportError :
37
+ import adafruit_binascii as ba
33
38
from digitalio import Direction , Pull
34
39
from adafruit_bus_device .spi_device import SPIDevice
35
40
from micropython import const
66
71
_PACKET_BUTTON_LEN = const (5 )
67
72
_PACKET_COLOR_LEN = const (6 )
68
73
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
+
69
109
70
110
class BluefruitSPI :
71
111
"""Helper for the Bluefruit LE SPI Friend"""
72
112
73
113
def __init__ (
74
- self , spi , cs , irq , reset , debug = False
114
+ self , spi , cs , irq , reset , debug = False , fifo_len = 20
75
115
): # pylint: disable=too-many-arguments
76
116
self ._irq = irq
77
117
self ._buf_tx = bytearray (20 )
78
118
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 ()
79
127
self ._debug = debug
80
128
81
129
# a cache of data, used for packet parsing
@@ -98,6 +146,64 @@ def __init__(
98
146
99
147
self ._spi_device = SPIDevice (spi , cs , baudrate = 4000000 , phase = 0 , polarity = 0 )
100
148
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
+
101
207
def _cmd (self , cmd ): # pylint: disable=too-many-branches
102
208
"""
103
209
Executes the supplied AT command, which must be terminated with
@@ -112,25 +218,16 @@ def _cmd(self, cmd): # pylint: disable=too-many-branches
112
218
print ("ERROR: Command too long." )
113
219
raise ValueError ("Command too long." )
114
220
115
- more = 0x80 # More bit is in pos 8, 1 = more data available
221
+ more = True
116
222
pos = 0
117
223
while len (cmd ) - pos :
118
224
# Construct the SDEP packet
119
225
if len (cmd ) - pos <= 16 :
120
226
# Last or sole packet
121
- more = 0
227
+ more = False
122
228
plen = len (cmd ) - pos
123
229
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 )
134
231
if self ._debug :
135
232
print ("Writing: " , [hex (b ) for b in self ._buf_tx ])
136
233
else :
0 commit comments