1 /* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
3 This software may be distributed and modified under the terms of the GNU
4 General Public License version 2 (GPL2) as published by the Free Software
5 Foundation and appearing in the file GPL2.TXT included in the packaging of
6 this file. Please note that GPL2 Section 2[b] requires that all works based
7 on this software must also be made publicly available under the terms of
13 Kristian Lauszus, TKJ Electronics
14 Web : http://www.tkjelectronics.com
15 e-mail : kristianl@tkjelectronics.com
19 // To enable serial debugging see "settings.h"
20 //#define EXTRADEBUG // Uncomment to get even more debugging data
21 //#define PRINTREPORT // Uncomment to print the report sent to the Arduino
24 * CRC (reversed crc) lookup table as calculated by the table generator in ETSI TS 101 369 V6.3.0.
26 const uint8_t rfcomm_crc_table
[256] PROGMEM
= {/* reversed, 8-bit, poly=0x07 */
27 0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B,
28 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67,
29 0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43,
30 0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F,
31 0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B,
32 0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17,
33 0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33,
34 0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F,
35 0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B,
36 0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87,
37 0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3,
38 0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF,
39 0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB,
40 0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7,
41 0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3,
42 0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF
45 SPP::SPP(BTD
*p
, const char* name
, const char* pin
) :
46 BluetoothService(p
) // Pointer to BTD class instance - mandatory
51 /* Set device cid for the SDP and RFCOMM channelse */
52 sdp_dcid
[0] = 0x50; // 0x0050
54 rfcomm_dcid
[0] = 0x51; // 0x0051
55 rfcomm_dcid
[1] = 0x00;
62 RFCOMMConnected
= false;
64 waitForLastCommand
= false;
65 l2cap_sdp_state
= L2CAP_SDP_WAIT
;
66 l2cap_rfcomm_state
= L2CAP_RFCOMM_WAIT
;
72 void SPP::disconnect() {
74 // First the two L2CAP channels has to be disconnected and then the HCI connection
76 pBtd
->l2cap_disconnection_request(hci_handle
, ++identifier
, rfcomm_scid
, rfcomm_dcid
);
77 if(RFCOMMConnected
&& SDPConnected
)
78 delay(1); // Add delay between commands
80 pBtd
->l2cap_disconnection_request(hci_handle
, ++identifier
, sdp_scid
, sdp_dcid
);
81 l2cap_sdp_state
= L2CAP_DISCONNECT_RESPONSE
;
84 void SPP::ACLData(uint8_t* l2capinbuf
) {
86 if(l2capinbuf
[8] == L2CAP_CMD_CONNECTION_REQUEST
) {
87 if((l2capinbuf
[12] | (l2capinbuf
[13] << 8)) == SDP_PSM
&& !pBtd
->sdpConnectionClaimed
) {
88 pBtd
->sdpConnectionClaimed
= true;
89 hci_handle
= pBtd
->hci_handle
; // Store the HCI Handle for the connection
90 l2cap_sdp_state
= L2CAP_SDP_WAIT
; // Reset state
91 } else if((l2capinbuf
[12] | (l2capinbuf
[13] << 8)) == RFCOMM_PSM
&& !pBtd
->rfcommConnectionClaimed
) {
92 pBtd
->rfcommConnectionClaimed
= true;
93 hci_handle
= pBtd
->hci_handle
; // Store the HCI Handle for the connection
94 l2cap_rfcomm_state
= L2CAP_RFCOMM_WAIT
; // Reset state
99 if(checkHciHandle(l2capinbuf
, hci_handle
)) { // acl_handle_ok
100 if((l2capinbuf
[6] | (l2capinbuf
[7] << 8)) == 0x0001U
) { // l2cap_control - Channel ID for ACL-U
101 if(l2capinbuf
[8] == L2CAP_CMD_COMMAND_REJECT
) {
102 #ifdef DEBUG_USB_HOST
103 Notify(PSTR("\r\nL2CAP Command Rejected - Reason: "), 0x80);
104 D_PrintHex
<uint8_t > (l2capinbuf
[13], 0x80);
105 Notify(PSTR(" "), 0x80);
106 D_PrintHex
<uint8_t > (l2capinbuf
[12], 0x80);
107 Notify(PSTR(" Data: "), 0x80);
108 D_PrintHex
<uint8_t > (l2capinbuf
[17], 0x80);
109 Notify(PSTR(" "), 0x80);
110 D_PrintHex
<uint8_t > (l2capinbuf
[16], 0x80);
111 Notify(PSTR(" "), 0x80);
112 D_PrintHex
<uint8_t > (l2capinbuf
[15], 0x80);
113 Notify(PSTR(" "), 0x80);
114 D_PrintHex
<uint8_t > (l2capinbuf
[14], 0x80);
116 } else if(l2capinbuf
[8] == L2CAP_CMD_CONNECTION_REQUEST
) {
118 Notify(PSTR("\r\nL2CAP Connection Request - PSM: "), 0x80);
119 D_PrintHex
<uint8_t > (l2capinbuf
[13], 0x80);
120 Notify(PSTR(" "), 0x80);
121 D_PrintHex
<uint8_t > (l2capinbuf
[12], 0x80);
122 Notify(PSTR(" SCID: "), 0x80);
123 D_PrintHex
<uint8_t > (l2capinbuf
[15], 0x80);
124 Notify(PSTR(" "), 0x80);
125 D_PrintHex
<uint8_t > (l2capinbuf
[14], 0x80);
126 Notify(PSTR(" Identifier: "), 0x80);
127 D_PrintHex
<uint8_t > (l2capinbuf
[9], 0x80);
129 if((l2capinbuf
[12] | (l2capinbuf
[13] << 8)) == SDP_PSM
) { // It doesn't matter if it receives another reqeust, since it waits for the channel to disconnect in the L2CAP_SDP_DONE state, and the l2cap_event_flag will be cleared if so
130 identifier
= l2capinbuf
[9];
131 sdp_scid
[0] = l2capinbuf
[14];
132 sdp_scid
[1] = l2capinbuf
[15];
133 l2cap_set_flag(L2CAP_FLAG_CONNECTION_SDP_REQUEST
);
134 } else if((l2capinbuf
[12] | (l2capinbuf
[13] << 8)) == RFCOMM_PSM
) { // ----- || -----
135 identifier
= l2capinbuf
[9];
136 rfcomm_scid
[0] = l2capinbuf
[14];
137 rfcomm_scid
[1] = l2capinbuf
[15];
138 l2cap_set_flag(L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST
);
140 } else if(l2capinbuf
[8] == L2CAP_CMD_CONFIG_RESPONSE
) {
141 if((l2capinbuf
[16] | (l2capinbuf
[17] << 8)) == 0x0000) { // Success
142 if(l2capinbuf
[12] == sdp_dcid
[0] && l2capinbuf
[13] == sdp_dcid
[1]) {
143 //Notify(PSTR("\r\nSDP Configuration Complete"), 0x80);
144 l2cap_set_flag(L2CAP_FLAG_CONFIG_SDP_SUCCESS
);
145 } else if(l2capinbuf
[12] == rfcomm_dcid
[0] && l2capinbuf
[13] == rfcomm_dcid
[1]) {
146 //Notify(PSTR("\r\nRFCOMM Configuration Complete"), 0x80);
147 l2cap_set_flag(L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS
);
150 } else if(l2capinbuf
[8] == L2CAP_CMD_CONFIG_REQUEST
) {
151 if(l2capinbuf
[12] == sdp_dcid
[0] && l2capinbuf
[13] == sdp_dcid
[1]) {
152 //Notify(PSTR("\r\nSDP Configuration Request"), 0x80);
153 pBtd
->l2cap_config_response(hci_handle
, l2capinbuf
[9], sdp_scid
);
154 } else if(l2capinbuf
[12] == rfcomm_dcid
[0] && l2capinbuf
[13] == rfcomm_dcid
[1]) {
155 //Notify(PSTR("\r\nRFCOMM Configuration Request"), 0x80);
156 pBtd
->l2cap_config_response(hci_handle
, l2capinbuf
[9], rfcomm_scid
);
158 } else if(l2capinbuf
[8] == L2CAP_CMD_DISCONNECT_REQUEST
) {
159 if(l2capinbuf
[12] == sdp_dcid
[0] && l2capinbuf
[13] == sdp_dcid
[1]) {
160 //Notify(PSTR("\r\nDisconnect Request: SDP Channel"), 0x80);
161 identifier
= l2capinbuf
[9];
162 l2cap_set_flag(L2CAP_FLAG_DISCONNECT_SDP_REQUEST
);
163 } else if(l2capinbuf
[12] == rfcomm_dcid
[0] && l2capinbuf
[13] == rfcomm_dcid
[1]) {
164 //Notify(PSTR("\r\nDisconnect Request: RFCOMM Channel"), 0x80);
165 identifier
= l2capinbuf
[9];
166 l2cap_set_flag(L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST
);
168 } else if(l2capinbuf
[8] == L2CAP_CMD_DISCONNECT_RESPONSE
) {
169 if(l2capinbuf
[12] == sdp_scid
[0] && l2capinbuf
[13] == sdp_scid
[1]) {
170 //Notify(PSTR("\r\nDisconnect Response: SDP Channel"), 0x80);
171 identifier
= l2capinbuf
[9];
172 l2cap_set_flag(L2CAP_FLAG_DISCONNECT_RESPONSE
);
173 } else if(l2capinbuf
[12] == rfcomm_scid
[0] && l2capinbuf
[13] == rfcomm_scid
[1]) {
174 //Notify(PSTR("\r\nDisconnect Response: RFCOMM Channel"), 0x80);
175 identifier
= l2capinbuf
[9];
176 l2cap_set_flag(L2CAP_FLAG_DISCONNECT_RESPONSE
);
178 } else if(l2capinbuf
[8] == L2CAP_CMD_INFORMATION_REQUEST
) {
179 #ifdef DEBUG_USB_HOST
180 Notify(PSTR("\r\nInformation request"), 0x80);
182 identifier
= l2capinbuf
[9];
183 pBtd
->l2cap_information_response(hci_handle
, identifier
, l2capinbuf
[12], l2capinbuf
[13]);
187 Notify(PSTR("\r\nL2CAP Unknown Signaling Command: "), 0x80);
188 D_PrintHex
<uint8_t > (l2capinbuf
[8], 0x80);
191 } else if(l2capinbuf
[6] == sdp_dcid
[0] && l2capinbuf
[7] == sdp_dcid
[1]) { // SDP
192 if(l2capinbuf
[8] == SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU
) {
193 if(((l2capinbuf
[16] << 8 | l2capinbuf
[17]) == SERIALPORT_UUID
) || ((l2capinbuf
[16] << 8 | l2capinbuf
[17]) == 0x0000 && (l2capinbuf
[18] << 8 | l2capinbuf
[19]) == SERIALPORT_UUID
)) { // Check if it's sending the full UUID, see: https://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm, we will just check the first four bytes
195 serialPortResponse1(l2capinbuf
[9], l2capinbuf
[10]);
196 firstMessage
= false;
198 serialPortResponse2(l2capinbuf
[9], l2capinbuf
[10]); // Serialport continuation state
201 } else if(((l2capinbuf
[16] << 8 | l2capinbuf
[17]) == L2CAP_UUID
) || ((l2capinbuf
[16] << 8 | l2capinbuf
[17]) == 0x0000 && (l2capinbuf
[18] << 8 | l2capinbuf
[19]) == L2CAP_UUID
)) {
203 l2capResponse1(l2capinbuf
[9], l2capinbuf
[10]);
204 firstMessage
= false;
206 l2capResponse2(l2capinbuf
[9], l2capinbuf
[10]); // L2CAP continuation state
210 serviceNotSupported(l2capinbuf
[9], l2capinbuf
[10]); // The service is not supported
212 Notify(PSTR("\r\nUUID: "), 0x80);
214 if((l2capinbuf
[16] << 8 | l2capinbuf
[17]) == 0x0000) // Check if it's sending the UUID as a 128-bit UUID
215 uuid
= (l2capinbuf
[18] << 8 | l2capinbuf
[19]);
217 uuid
= (l2capinbuf
[16] << 8 | l2capinbuf
[17]);
218 D_PrintHex
<uint16_t > (uuid
, 0x80);
220 Notify(PSTR("\r\nLength: "), 0x80);
221 uint16_t length
= l2capinbuf
[11] << 8 | l2capinbuf
[12];
222 D_PrintHex
<uint16_t > (length
, 0x80);
223 Notify(PSTR("\r\nData: "), 0x80);
224 for(uint8_t i
= 0; i
< length
; i
++) {
225 D_PrintHex
<uint8_t > (l2capinbuf
[13 + i
], 0x80);
226 Notify(PSTR(" "), 0x80);
232 Notify(PSTR("\r\nUnknown PDU: "), 0x80);
233 D_PrintHex
<uint8_t > (l2capinbuf
[8], 0x80);
236 } else if(l2capinbuf
[6] == rfcomm_dcid
[0] && l2capinbuf
[7] == rfcomm_dcid
[1]) { // RFCOMM
237 rfcommChannel
= l2capinbuf
[8] & 0xF8;
238 rfcommDirection
= l2capinbuf
[8] & 0x04;
239 rfcommCommandResponse
= l2capinbuf
[8] & 0x02;
240 rfcommChannelType
= l2capinbuf
[9] & 0xEF;
241 rfcommPfBit
= l2capinbuf
[9] & 0x10;
243 if(rfcommChannel
>> 3 != 0x00)
244 rfcommChannelConnection
= rfcommChannel
;
247 Notify(PSTR("\r\nRFCOMM Channel: "), 0x80);
248 D_PrintHex
<uint8_t > (rfcommChannel
>> 3, 0x80);
249 Notify(PSTR(" Direction: "), 0x80);
250 D_PrintHex
<uint8_t > (rfcommDirection
>> 2, 0x80);
251 Notify(PSTR(" CommandResponse: "), 0x80);
252 D_PrintHex
<uint8_t > (rfcommCommandResponse
>> 1, 0x80);
253 Notify(PSTR(" ChannelType: "), 0x80);
254 D_PrintHex
<uint8_t > (rfcommChannelType
, 0x80);
255 Notify(PSTR(" PF_BIT: "), 0x80);
256 D_PrintHex
<uint8_t > (rfcommPfBit
, 0x80);
258 if(rfcommChannelType
== RFCOMM_DISC
) {
259 #ifdef DEBUG_USB_HOST
260 Notify(PSTR("\r\nReceived Disconnect RFCOMM Command on channel: "), 0x80);
261 D_PrintHex
<uint8_t > (rfcommChannel
>> 3, 0x80);
264 sendRfcomm(rfcommChannel
, rfcommDirection
, rfcommCommandResponse
, RFCOMM_UA
, rfcommPfBit
, rfcommbuf
, 0x00); // UA Command
267 /* Read the incoming message */
268 if(rfcommChannelType
== RFCOMM_UIH
&& rfcommChannel
== rfcommChannelConnection
) {
269 uint8_t length
= l2capinbuf
[10] >> 1; // Get length
270 uint8_t offset
= l2capinbuf
[4] - length
- 4; // Check if there is credit
271 if(checkFcs(&l2capinbuf
[8], l2capinbuf
[11 + length
+ offset
])) {
273 for(; i
< length
; i
++) {
274 if(rfcommAvailable
+ i
>= sizeof (rfcommDataBuffer
)) {
275 #ifdef DEBUG_USB_HOST
276 Notify(PSTR("\r\nWarning: Buffer is full!"), 0x80);
280 rfcommDataBuffer
[rfcommAvailable
+ i
] = l2capinbuf
[11 + i
+ offset
];
282 rfcommAvailable
+= i
;
284 Notify(PSTR("\r\nRFCOMM Data Available: "), 0x80);
285 Notify(rfcommAvailable
, 0x80);
287 Notify(PSTR(" - Credit: 0x"), 0x80);
288 D_PrintHex
<uint8_t > (l2capinbuf
[11], 0x80);
292 #ifdef DEBUG_USB_HOST
294 Notify(PSTR("\r\nError in FCS checksum!"), 0x80);
296 #ifdef PRINTREPORT // Uncomment "#define PRINTREPORT" to print the report send to the Arduino via Bluetooth
297 for(uint8_t i
= 0; i
< length
; i
++)
298 Notifyc(l2capinbuf
[i
+ 11 + offset
], 0x80);
300 } else if(rfcommChannelType
== RFCOMM_UIH
&& l2capinbuf
[11] == BT_RFCOMM_RPN_CMD
) { // UIH Remote Port Negotiation Command
301 #ifdef DEBUG_USB_HOST
302 Notify(PSTR("\r\nReceived UIH Remote Port Negotiation Command"), 0x80);
304 rfcommbuf
[0] = BT_RFCOMM_RPN_RSP
; // Command
305 rfcommbuf
[1] = l2capinbuf
[12]; // Length and shiftet like so: length << 1 | 1
306 rfcommbuf
[2] = l2capinbuf
[13]; // Channel: channel << 1 | 1
307 rfcommbuf
[3] = l2capinbuf
[14]; // Pre difined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM
308 rfcommbuf
[4] = l2capinbuf
[15]; // Priority
309 rfcommbuf
[5] = l2capinbuf
[16]; // Timer
310 rfcommbuf
[6] = l2capinbuf
[17]; // Max Fram Size LSB
311 rfcommbuf
[7] = l2capinbuf
[18]; // Max Fram Size MSB
312 rfcommbuf
[8] = l2capinbuf
[19]; // MaxRatransm.
313 rfcommbuf
[9] = l2capinbuf
[20]; // Number of Frames
314 sendRfcomm(rfcommChannel
, rfcommDirection
, 0, RFCOMM_UIH
, rfcommPfBit
, rfcommbuf
, 0x0A); // UIH Remote Port Negotiation Response
315 } else if(rfcommChannelType
== RFCOMM_UIH
&& l2capinbuf
[11] == BT_RFCOMM_MSC_CMD
) { // UIH Modem Status Command
316 #ifdef DEBUG_USB_HOST
317 Notify(PSTR("\r\nSend UIH Modem Status Response"), 0x80);
319 rfcommbuf
[0] = BT_RFCOMM_MSC_RSP
; // UIH Modem Status Response
320 rfcommbuf
[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1
321 rfcommbuf
[2] = l2capinbuf
[13]; // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3)
322 rfcommbuf
[3] = l2capinbuf
[14];
323 sendRfcomm(rfcommChannel
, rfcommDirection
, 0, RFCOMM_UIH
, rfcommPfBit
, rfcommbuf
, 0x04);
326 if(rfcommChannelType
== RFCOMM_SABM
) { // SABM Command - this is sent twice: once for channel 0 and then for the channel to establish
327 #ifdef DEBUG_USB_HOST
328 Notify(PSTR("\r\nReceived SABM Command"), 0x80);
330 sendRfcomm(rfcommChannel
, rfcommDirection
, rfcommCommandResponse
, RFCOMM_UA
, rfcommPfBit
, rfcommbuf
, 0x00); // UA Command
331 } else if(rfcommChannelType
== RFCOMM_UIH
&& l2capinbuf
[11] == BT_RFCOMM_PN_CMD
) { // UIH Parameter Negotiation Command
332 #ifdef DEBUG_USB_HOST
333 Notify(PSTR("\r\nReceived UIH Parameter Negotiation Command"), 0x80);
335 rfcommbuf
[0] = BT_RFCOMM_PN_RSP
; // UIH Parameter Negotiation Response
336 rfcommbuf
[1] = l2capinbuf
[12]; // Length and shiftet like so: length << 1 | 1
337 rfcommbuf
[2] = l2capinbuf
[13]; // Channel: channel << 1 | 1
338 rfcommbuf
[3] = 0xE0; // Pre difined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM
339 rfcommbuf
[4] = 0x00; // Priority
340 rfcommbuf
[5] = 0x00; // Timer
341 rfcommbuf
[6] = BULK_MAXPKTSIZE
- 14; // Max Fram Size LSB - set to the size of received data (50)
342 rfcommbuf
[7] = 0x00; // Max Fram Size MSB
343 rfcommbuf
[8] = 0x00; // MaxRatransm.
344 rfcommbuf
[9] = 0x00; // Number of Frames
345 sendRfcomm(rfcommChannel
, rfcommDirection
, 0, RFCOMM_UIH
, rfcommPfBit
, rfcommbuf
, 0x0A);
346 } else if(rfcommChannelType
== RFCOMM_UIH
&& l2capinbuf
[11] == BT_RFCOMM_MSC_CMD
) { // UIH Modem Status Command
347 #ifdef DEBUG_USB_HOST
348 Notify(PSTR("\r\nSend UIH Modem Status Response"), 0x80);
350 rfcommbuf
[0] = BT_RFCOMM_MSC_RSP
; // UIH Modem Status Response
351 rfcommbuf
[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1
352 rfcommbuf
[2] = l2capinbuf
[13]; // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3)
353 rfcommbuf
[3] = l2capinbuf
[14];
354 sendRfcomm(rfcommChannel
, rfcommDirection
, 0, RFCOMM_UIH
, rfcommPfBit
, rfcommbuf
, 0x04);
357 #ifdef DEBUG_USB_HOST
358 Notify(PSTR("\r\nSend UIH Modem Status Command"), 0x80);
360 rfcommbuf
[0] = BT_RFCOMM_MSC_CMD
; // UIH Modem Status Command
361 rfcommbuf
[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1
362 rfcommbuf
[2] = l2capinbuf
[13]; // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3)
363 rfcommbuf
[3] = 0x8D; // Can receive frames (YES), Ready to Communicate (YES), Ready to Receive (YES), Incomig Call (NO), Data is Value (YES)
365 sendRfcomm(rfcommChannel
, rfcommDirection
, 0, RFCOMM_UIH
, rfcommPfBit
, rfcommbuf
, 0x04);
366 } else if(rfcommChannelType
== RFCOMM_UIH
&& l2capinbuf
[11] == BT_RFCOMM_MSC_RSP
) { // UIH Modem Status Response
368 #ifdef DEBUG_USB_HOST
369 Notify(PSTR("\r\nSend UIH Command with credit"), 0x80);
371 sendRfcommCredit(rfcommChannelConnection
, rfcommDirection
, 0, RFCOMM_UIH
, 0x10, sizeof (rfcommDataBuffer
)); // Send credit
374 waitForLastCommand
= true;
376 } else if(rfcommChannelType
== RFCOMM_UIH
&& l2capinbuf
[10] == 0x01) { // UIH Command with credit
377 #ifdef DEBUG_USB_HOST
378 Notify(PSTR("\r\nReceived UIH Command with credit"), 0x80);
380 } else if(rfcommChannelType
== RFCOMM_UIH
&& l2capinbuf
[11] == BT_RFCOMM_RPN_CMD
) { // UIH Remote Port Negotiation Command
381 #ifdef DEBUG_USB_HOST
382 Notify(PSTR("\r\nReceived UIH Remote Port Negotiation Command"), 0x80);
384 rfcommbuf
[0] = BT_RFCOMM_RPN_RSP
; // Command
385 rfcommbuf
[1] = l2capinbuf
[12]; // Length and shiftet like so: length << 1 | 1
386 rfcommbuf
[2] = l2capinbuf
[13]; // Channel: channel << 1 | 1
387 rfcommbuf
[3] = l2capinbuf
[14]; // Pre difined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM
388 rfcommbuf
[4] = l2capinbuf
[15]; // Priority
389 rfcommbuf
[5] = l2capinbuf
[16]; // Timer
390 rfcommbuf
[6] = l2capinbuf
[17]; // Max Fram Size LSB
391 rfcommbuf
[7] = l2capinbuf
[18]; // Max Fram Size MSB
392 rfcommbuf
[8] = l2capinbuf
[19]; // MaxRatransm.
393 rfcommbuf
[9] = l2capinbuf
[20]; // Number of Frames
394 sendRfcomm(rfcommChannel
, rfcommDirection
, 0, RFCOMM_UIH
, rfcommPfBit
, rfcommbuf
, 0x0A); // UIH Remote Port Negotiation Response
395 #ifdef DEBUG_USB_HOST
396 Notify(PSTR("\r\nRFCOMM Connection is now established\r\n"), 0x80);
401 else if(rfcommChannelType
!= RFCOMM_DISC
) {
402 Notify(PSTR("\r\nUnsupported RFCOMM Data - ChannelType: "), 0x80);
403 D_PrintHex
<uint8_t > (rfcommChannelType
, 0x80);
404 Notify(PSTR(" Command: "), 0x80);
405 D_PrintHex
<uint8_t > (l2capinbuf
[11], 0x80);
412 Notify(PSTR("\r\nUnsupported L2CAP Data - Channel ID: "), 0x80);
413 D_PrintHex
<uint8_t > (l2capinbuf
[7], 0x80);
414 Notify(PSTR(" "), 0x80);
415 D_PrintHex
<uint8_t > (l2capinbuf
[6], 0x80);
424 if(waitForLastCommand
&& (millis() - timer
) > 100) { // We will only wait 100ms and see if the UIH Remote Port Negotiation Command is send, as some deviced don't send it
425 #ifdef DEBUG_USB_HOST
426 Notify(PSTR("\r\nRFCOMM Connection is now established - Automatic\r\n"), 0x80);
430 send(); // Send all bytes currently in the buffer
435 waitForLastCommand
= false;
436 connected
= true; // The RFCOMM channel is now established
439 pFuncOnInit(); // Call the user function
442 void SPP::SDP_task() {
443 switch(l2cap_sdp_state
) {
445 if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_SDP_REQUEST
)) {
446 l2cap_clear_flag(L2CAP_FLAG_CONNECTION_SDP_REQUEST
); // Clear flag
447 #ifdef DEBUG_USB_HOST
448 Notify(PSTR("\r\nSDP Incoming Connection Request"), 0x80);
450 pBtd
->l2cap_connection_response(hci_handle
, identifier
, sdp_dcid
, sdp_scid
, PENDING
);
452 pBtd
->l2cap_connection_response(hci_handle
, identifier
, sdp_dcid
, sdp_scid
, SUCCESSFUL
);
455 pBtd
->l2cap_config_request(hci_handle
, identifier
, sdp_scid
);
456 l2cap_sdp_state
= L2CAP_SDP_SUCCESS
;
457 } else if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_SDP_REQUEST
)) {
458 l2cap_clear_flag(L2CAP_FLAG_DISCONNECT_SDP_REQUEST
); // Clear flag
459 SDPConnected
= false;
460 #ifdef DEBUG_USB_HOST
461 Notify(PSTR("\r\nDisconnected SDP Channel"), 0x80);
463 pBtd
->l2cap_disconnection_response(hci_handle
, identifier
, sdp_dcid
, sdp_scid
);
466 case L2CAP_SDP_SUCCESS
:
467 if(l2cap_check_flag(L2CAP_FLAG_CONFIG_SDP_SUCCESS
)) {
468 l2cap_clear_flag(L2CAP_FLAG_CONFIG_SDP_SUCCESS
); // Clear flag
469 #ifdef DEBUG_USB_HOST
470 Notify(PSTR("\r\nSDP Successfully Configured"), 0x80);
472 firstMessage
= true; // Reset bool
474 l2cap_sdp_state
= L2CAP_SDP_WAIT
;
478 case L2CAP_DISCONNECT_RESPONSE
: // This is for both disconnection response from the RFCOMM and SDP channel if they were connected
479 if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_RESPONSE
)) {
480 #ifdef DEBUG_USB_HOST
481 Notify(PSTR("\r\nDisconnected L2CAP Connection"), 0x80);
483 pBtd
->hci_disconnect(hci_handle
);
484 hci_handle
= -1; // Reset handle
491 void SPP::RFCOMM_task() {
492 switch(l2cap_rfcomm_state
) {
493 case L2CAP_RFCOMM_WAIT
:
494 if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST
)) {
495 l2cap_clear_flag(L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST
); // Clear flag
496 #ifdef DEBUG_USB_HOST
497 Notify(PSTR("\r\nRFCOMM Incoming Connection Request"), 0x80);
499 pBtd
->l2cap_connection_response(hci_handle
, identifier
, rfcomm_dcid
, rfcomm_scid
, PENDING
);
501 pBtd
->l2cap_connection_response(hci_handle
, identifier
, rfcomm_dcid
, rfcomm_scid
, SUCCESSFUL
);
504 pBtd
->l2cap_config_request(hci_handle
, identifier
, rfcomm_scid
);
505 l2cap_rfcomm_state
= L2CAP_RFCOMM_SUCCESS
;
506 } else if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST
)) {
507 l2cap_clear_flag(L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST
); // Clear flag
508 RFCOMMConnected
= false;
510 #ifdef DEBUG_USB_HOST
511 Notify(PSTR("\r\nDisconnected RFCOMM Channel"), 0x80);
513 pBtd
->l2cap_disconnection_response(hci_handle
, identifier
, rfcomm_dcid
, rfcomm_scid
);
516 case L2CAP_RFCOMM_SUCCESS
:
517 if(l2cap_check_flag(L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS
)) {
518 l2cap_clear_flag(L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS
); // Clear flag
519 #ifdef DEBUG_USB_HOST
520 Notify(PSTR("\r\nRFCOMM Successfully Configured"), 0x80);
522 rfcommAvailable
= 0; // Reset number of bytes available
523 bytesRead
= 0; // Reset number of bytes received
524 RFCOMMConnected
= true;
525 l2cap_rfcomm_state
= L2CAP_RFCOMM_WAIT
;
530 /************************************************************/
533 /************************************************************/
534 void SPP::SDP_Command(uint8_t* data
, uint8_t nbytes
) { // See page 223 in the Bluetooth specs
535 pBtd
->L2CAP_Command(hci_handle
, data
, nbytes
, sdp_scid
[0], sdp_scid
[1]);
538 void SPP::serviceNotSupported(uint8_t transactionIDHigh
, uint8_t transactionIDLow
) { // See page 235 in the Bluetooth specs
539 l2capoutbuf
[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU
;
540 l2capoutbuf
[1] = transactionIDHigh
;
541 l2capoutbuf
[2] = transactionIDLow
;
542 l2capoutbuf
[3] = 0x00; // MSB Parameter Length
543 l2capoutbuf
[4] = 0x05; // LSB Parameter Length = 5
544 l2capoutbuf
[5] = 0x00; // MSB AttributeListsByteCount
545 l2capoutbuf
[6] = 0x02; // LSB AttributeListsByteCount = 2
547 /* Attribute ID/Value Sequence: */
548 l2capoutbuf
[7] = 0x35; // Data element sequence - length in next byte
549 l2capoutbuf
[8] = 0x00; // Length = 0
550 l2capoutbuf
[9] = 0x00; // No continuation state
552 SDP_Command(l2capoutbuf
, 10);
555 void SPP::serialPortResponse1(uint8_t transactionIDHigh
, uint8_t transactionIDLow
) {
556 l2capoutbuf
[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU
;
557 l2capoutbuf
[1] = transactionIDHigh
;
558 l2capoutbuf
[2] = transactionIDLow
;
559 l2capoutbuf
[3] = 0x00; // MSB Parameter Length
560 l2capoutbuf
[4] = 0x2B; // LSB Parameter Length = 43
561 l2capoutbuf
[5] = 0x00; // MSB AttributeListsByteCount
562 l2capoutbuf
[6] = 0x26; // LSB AttributeListsByteCount = 38
564 /* Attribute ID/Value Sequence: */
565 l2capoutbuf
[7] = 0x36; // Data element sequence - length in next two bytes
566 l2capoutbuf
[8] = 0x00; // MSB Length
567 l2capoutbuf
[9] = 0x3C; // LSB Length = 60
569 l2capoutbuf
[10] = 0x36; // Data element sequence - length in next two bytes
570 l2capoutbuf
[11] = 0x00; // MSB Length
571 l2capoutbuf
[12] = 0x39; // LSB Length = 57
573 l2capoutbuf
[13] = 0x09; // Unsigned Integer - length 2 bytes
574 l2capoutbuf
[14] = 0x00; // MSB ServiceRecordHandle
575 l2capoutbuf
[15] = 0x00; // LSB ServiceRecordHandle
576 l2capoutbuf
[16] = 0x0A; // Unsigned int - length 4 bytes
577 l2capoutbuf
[17] = 0x00; // ServiceRecordHandle value - TODO: Is this related to HCI_Handle?
578 l2capoutbuf
[18] = 0x01;
579 l2capoutbuf
[19] = 0x00;
580 l2capoutbuf
[20] = 0x06;
582 l2capoutbuf
[21] = 0x09; // Unsigned Integer - length 2 bytes
583 l2capoutbuf
[22] = 0x00; // MSB ServiceClassIDList
584 l2capoutbuf
[23] = 0x01; // LSB ServiceClassIDList
585 l2capoutbuf
[24] = 0x35; // Data element sequence - length in next byte
586 l2capoutbuf
[25] = 0x03; // Length = 3
587 l2capoutbuf
[26] = 0x19; // UUID (universally unique identifier) - length = 2 bytes
588 l2capoutbuf
[27] = 0x11; // MSB SerialPort
589 l2capoutbuf
[28] = 0x01; // LSB SerialPort
591 l2capoutbuf
[29] = 0x09; // Unsigned Integer - length 2 bytes
592 l2capoutbuf
[30] = 0x00; // MSB ProtocolDescriptorList
593 l2capoutbuf
[31] = 0x04; // LSB ProtocolDescriptorList
594 l2capoutbuf
[32] = 0x35; // Data element sequence - length in next byte
595 l2capoutbuf
[33] = 0x0C; // Length = 12
597 l2capoutbuf
[34] = 0x35; // Data element sequence - length in next byte
598 l2capoutbuf
[35] = 0x03; // Length = 3
599 l2capoutbuf
[36] = 0x19; // UUID (universally unique identifier) - length = 2 bytes
600 l2capoutbuf
[37] = 0x01; // MSB L2CAP
601 l2capoutbuf
[38] = 0x00; // LSB L2CAP
603 l2capoutbuf
[39] = 0x35; // Data element sequence - length in next byte
604 l2capoutbuf
[40] = 0x05; // Length = 5
605 l2capoutbuf
[41] = 0x19; // UUID (universally unique identifier) - length = 2 bytes
606 l2capoutbuf
[42] = 0x00; // MSB RFCOMM
607 l2capoutbuf
[43] = 0x03; // LSB RFCOMM
608 l2capoutbuf
[44] = 0x08; // Unsigned Integer - length 1 byte
610 l2capoutbuf
[45] = 0x02; // ContinuationState - Two more bytes
611 l2capoutbuf
[46] = 0x00; // MSB length
612 l2capoutbuf
[47] = 0x19; // LSB length = 25 more bytes to come
614 SDP_Command(l2capoutbuf
, 48);
617 void SPP::serialPortResponse2(uint8_t transactionIDHigh
, uint8_t transactionIDLow
) {
618 l2capoutbuf
[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU
;
619 l2capoutbuf
[1] = transactionIDHigh
;
620 l2capoutbuf
[2] = transactionIDLow
;
621 l2capoutbuf
[3] = 0x00; // MSB Parameter Length
622 l2capoutbuf
[4] = 0x1C; // LSB Parameter Length = 28
623 l2capoutbuf
[5] = 0x00; // MSB AttributeListsByteCount
624 l2capoutbuf
[6] = 0x19; // LSB AttributeListsByteCount = 25
626 /* Attribute ID/Value Sequence: */
627 l2capoutbuf
[7] = 0x01; // Channel 1 - TODO: Try different values, so multiple servers can be used at once
629 l2capoutbuf
[8] = 0x09; // Unsigned Integer - length 2 bytes
630 l2capoutbuf
[9] = 0x00; // MSB LanguageBaseAttributeIDList
631 l2capoutbuf
[10] = 0x06; // LSB LanguageBaseAttributeIDList
632 l2capoutbuf
[11] = 0x35; // Data element sequence - length in next byte
633 l2capoutbuf
[12] = 0x09; // Length = 9
635 // Identifier representing the natural language = en = English - see: "ISO 639:1988"
636 l2capoutbuf
[13] = 0x09; // Unsigned Integer - length 2 bytes
637 l2capoutbuf
[14] = 0x65; // 'e'
638 l2capoutbuf
[15] = 0x6E; // 'n'
640 // "The second element of each triplet contains an identifier that specifies a character encoding used for the language"
641 // Encoding is set to 106 (UTF-8) - see: http://www.iana.org/assignments/character-sets/character-sets.xhtml
642 l2capoutbuf
[16] = 0x09; // Unsigned Integer - length 2 bytes
643 l2capoutbuf
[17] = 0x00; // MSB of character encoding
644 l2capoutbuf
[18] = 0x6A; // LSB of character encoding (106)
646 // Attribute ID that serves as the base attribute ID for the natural language in the service record
647 // "To facilitate the retrieval of human-readable universal attributes in a principal language, the base attribute ID value for the primary language supported by a service record shall be 0x0100"
648 l2capoutbuf
[19] = 0x09; // Unsigned Integer - length 2 bytes
649 l2capoutbuf
[20] = 0x01;
650 l2capoutbuf
[21] = 0x00;
652 l2capoutbuf
[22] = 0x09; // Unsigned Integer - length 2 bytes
653 l2capoutbuf
[23] = 0x01; // MSB ServiceDescription
654 l2capoutbuf
[24] = 0x00; // LSB ServiceDescription
656 l2capoutbuf
[25] = 0x25; // Text string - length in next byte
657 l2capoutbuf
[26] = 0x05; // Name length
658 l2capoutbuf
[27] = 'T';
659 l2capoutbuf
[28] = 'K';
660 l2capoutbuf
[29] = 'J';
661 l2capoutbuf
[30] = 'S';
662 l2capoutbuf
[31] = 'P';
663 l2capoutbuf
[32] = 0x00; // No continuation state
665 SDP_Command(l2capoutbuf
, 33);
668 void SPP::l2capResponse1(uint8_t transactionIDHigh
, uint8_t transactionIDLow
) {
669 serialPortResponse1(transactionIDHigh
, transactionIDLow
); // These has to send all the supported functions, since it only supports virtual serialport it just sends the message again
672 void SPP::l2capResponse2(uint8_t transactionIDHigh
, uint8_t transactionIDLow
) {
673 serialPortResponse2(transactionIDHigh
, transactionIDLow
); // Same data as serialPortResponse2
675 /************************************************************/
676 /* RFCOMM Commands */
678 /************************************************************/
679 void SPP::RFCOMM_Command(uint8_t* data
, uint8_t nbytes
) {
680 pBtd
->L2CAP_Command(hci_handle
, data
, nbytes
, rfcomm_scid
[0], rfcomm_scid
[1]);
683 void SPP::sendRfcomm(uint8_t channel
, uint8_t direction
, uint8_t CR
, uint8_t channelType
, uint8_t pfBit
, uint8_t* data
, uint8_t length
) {
684 l2capoutbuf
[0] = channel
| direction
| CR
| extendAddress
; // RFCOMM Address
685 l2capoutbuf
[1] = channelType
| pfBit
; // RFCOMM Control
686 l2capoutbuf
[2] = length
<< 1 | 0x01; // Length and format (always 0x01 bytes format)
688 for(; i
< length
; i
++)
689 l2capoutbuf
[i
+ 3] = data
[i
];
690 l2capoutbuf
[i
+ 3] = calcFcs(l2capoutbuf
);
692 Notify(PSTR(" - RFCOMM Data: "), 0x80);
693 for(i
= 0; i
< length
+ 4; i
++) {
694 D_PrintHex
<uint8_t > (l2capoutbuf
[i
], 0x80);
695 Notify(PSTR(" "), 0x80);
698 RFCOMM_Command(l2capoutbuf
, length
+ 4);
701 void SPP::sendRfcommCredit(uint8_t channel
, uint8_t direction
, uint8_t CR
, uint8_t channelType
, uint8_t pfBit
, uint8_t credit
) {
702 l2capoutbuf
[0] = channel
| direction
| CR
| extendAddress
; // RFCOMM Address
703 l2capoutbuf
[1] = channelType
| pfBit
; // RFCOMM Control
704 l2capoutbuf
[2] = 0x01; // Length = 0
705 l2capoutbuf
[3] = credit
; // Credit
706 l2capoutbuf
[4] = calcFcs(l2capoutbuf
);
708 Notify(PSTR(" - RFCOMM Credit Data: "), 0x80);
709 for(uint8_t i
= 0; i
< 5; i
++) {
710 D_PrintHex
<uint8_t > (l2capoutbuf
[i
], 0x80);
711 Notify(PSTR(" "), 0x80);
714 RFCOMM_Command(l2capoutbuf
, 5);
718 uint8_t SPP::crc(uint8_t *data
) {
719 return (pgm_read_byte(&rfcomm_crc_table
[pgm_read_byte(&rfcomm_crc_table
[0xFF ^ data
[0]]) ^ data
[1]]));
723 uint8_t SPP::calcFcs(uint8_t *data
) {
724 uint8_t temp
= crc(data
);
725 if((data
[1] & 0xEF) == RFCOMM_UIH
)
726 return (0xFF - temp
); // FCS on 2 bytes
728 return (0xFF - pgm_read_byte(&rfcomm_crc_table
[temp
^ data
[2]])); // FCS on 3 bytes
732 bool SPP::checkFcs(uint8_t *data
, uint8_t fcs
) {
733 uint8_t temp
= crc(data
);
734 if((data
[1] & 0xEF) != RFCOMM_UIH
)
735 temp
= pgm_read_byte(&rfcomm_crc_table
[temp
^ data
[2]]); // FCS on 3 bytes
736 return (pgm_read_byte(&rfcomm_crc_table
[temp
^ fcs
]) == 0xCF);
739 /* Serial commands */
740 #if defined(ARDUINO) && ARDUINO >=100
742 size_t SPP::write(uint8_t data
) {
743 return write(&data
, 1);
747 void SPP::write(uint8_t data
) {
752 #if defined(ARDUINO) && ARDUINO >=100
754 size_t SPP::write(const uint8_t *data
, size_t size
) {
757 void SPP::write(const uint8_t *data
, size_t size
) {
759 for(uint8_t i
= 0; i
< size
; i
++) {
760 if(sppIndex
>= sizeof (sppOutputBuffer
) / sizeof (sppOutputBuffer
[0]))
761 send(); // Send the current data in the buffer
762 sppOutputBuffer
[sppIndex
++] = data
[i
]; // All the bytes are put into a buffer and then send using the send() function
764 #if defined(ARDUINO) && ARDUINO >=100
770 if(!connected
|| !sppIndex
)
772 uint8_t length
; // This is the length of the string we are sending
773 uint8_t offset
= 0; // This is used to keep track of where we are in the string
775 l2capoutbuf
[0] = rfcommChannelConnection
| 0 | 0 | extendAddress
; // RFCOMM Address
776 l2capoutbuf
[1] = RFCOMM_UIH
; // RFCOMM Control
778 while(sppIndex
) { // We will run this while loop until this variable is 0
779 if(sppIndex
> (sizeof (l2capoutbuf
) - 4)) // Check if the string is larger than the outgoing buffer
780 length
= sizeof (l2capoutbuf
) - 4;
784 l2capoutbuf
[2] = length
<< 1 | 1; // Length
786 for(; i
< length
; i
++)
787 l2capoutbuf
[i
+ 3] = sppOutputBuffer
[i
+ offset
];
788 l2capoutbuf
[i
+ 3] = calcFcs(l2capoutbuf
); // Calculate checksum
790 RFCOMM_Command(l2capoutbuf
, length
+ 4);
793 offset
+= length
; // Increment the offset
797 int SPP::available(void) {
798 return rfcommAvailable
;
801 void SPP::discard(void) {
805 int SPP::peek(void) {
806 if(rfcommAvailable
== 0) // Don't read if there is nothing in the buffer
808 return rfcommDataBuffer
[0];
811 int SPP::read(void) {
812 if(rfcommAvailable
== 0) // Don't read if there is nothing in the buffer
814 uint8_t output
= rfcommDataBuffer
[0];
815 for(uint8_t i
= 1; i
< rfcommAvailable
; i
++)
816 rfcommDataBuffer
[i
- 1] = rfcommDataBuffer
[i
]; // Shift the buffer one left
819 if(bytesRead
> (sizeof (rfcommDataBuffer
) - 5)) { // We will send the command just before it runs out of credit
821 sendRfcommCredit(rfcommChannelConnection
, rfcommDirection
, 0, RFCOMM_UIH
, 0x10, sizeof (rfcommDataBuffer
)); // Send more credit
823 Notify(PSTR("\r\nSent "), 0x80);
824 Notify((uint8_t)sizeof (rfcommDataBuffer
), 0x80);
825 Notify(PSTR(" more credit"), 0x80);