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