30
30
31
31
import time
32
32
import struct
33
+ import binascii
33
34
from digitalio import Direction , Pull
34
35
from adafruit_bus_device .spi_device import SPIDevice
35
36
from micropython import const
66
67
_PACKET_BUTTON_LEN = const (5 )
67
68
_PACKET_COLOR_LEN = const (6 )
68
69
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
+
69
105
70
106
class BluefruitSPI :
71
107
"""Helper for the Bluefruit LE SPI Friend"""
72
108
73
109
def __init__ (
74
- self , spi , cs , irq , reset , debug = False
110
+ self , spi , cs , irq , reset , debug = False , fifo_len = 20
75
111
): # pylint: disable=too-many-arguments
76
112
self ._irq = irq
77
113
self ._buf_tx = bytearray (20 )
78
114
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 ()
79
123
self ._debug = debug
80
124
81
125
# a cache of data, used for packet parsing
@@ -98,6 +142,64 @@ def __init__(
98
142
99
143
self ._spi_device = SPIDevice (spi , cs , baudrate = 4000000 , phase = 0 , polarity = 0 )
100
144
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
+
101
203
def _cmd (self , cmd ): # pylint: disable=too-many-branches
102
204
"""
103
205
Executes the supplied AT command, which must be terminated with
@@ -112,26 +214,17 @@ def _cmd(self, cmd): # pylint: disable=too-many-branches
112
214
print ("ERROR: Command too long." )
113
215
raise ValueError ("Command too long." )
114
216
115
- more = 0x80 # More bit is in pos 8, 1 = more data available
217
+ more = True
116
218
pos = 0
117
219
while len (cmd ) - pos :
118
220
# Construct the SDEP packet
119
221
if len (cmd ) - pos <= 16 :
120
222
# Last or sole packet
121
- more = 0
223
+ more = False
122
224
plen = len (cmd ) - pos
123
225
if plen > 16 :
124
226
plen = 16
125
- # Note the 'more' value in bit 8 of the packet len
126
- struct .pack_into (
127
- "<BHB16s" ,
128
- self ._buf_tx ,
129
- 0 ,
130
- _MSG_COMMAND ,
131
- _SDEP_ATCOMMAND ,
132
- plen | more ,
133
- cmd [pos : pos + plen ],
134
- )
227
+ self ._create_sdep_raw (self ._buf_tx , cmd [pos : pos + plen ], more = more )
135
228
if self ._debug :
136
229
print ("Writing: " , [hex (b ) for b in self ._buf_tx ])
137
230
else :
0 commit comments