mirror of
https://github.com/felis/USB_Host_Shield_2.0.git
synced 2024-03-22 11:31:26 +01:00
Merged SPPi class in from @magictaler - see: #79
I have made several improvements. Including making a new SPPBase class.
This commit is contained in:
parent
d9dfa3cf35
commit
ee90afde31
13 changed files with 1356 additions and 377 deletions
86
BTD.cpp
86
BTD.cpp
|
@ -29,6 +29,7 @@ connectToWii(false),
|
|||
pairWithWii(false),
|
||||
connectToHIDDevice(false),
|
||||
pairWithHIDDevice(false),
|
||||
pairWithOtherDevice(false),
|
||||
pUsb(p), // Pointer to USB class instance - mandatory
|
||||
bAddress(0), // Device address - mandatory
|
||||
bNumEP(1), // If config descriptor needs to be parsed
|
||||
|
@ -301,6 +302,7 @@ void BTD::Initialize() {
|
|||
connectToWii = false;
|
||||
incomingWii = false;
|
||||
connectToHIDDevice = false;
|
||||
connectToOtherDevice = false;
|
||||
incomingHIDDevice = false;
|
||||
incomingPS4 = false;
|
||||
bAddress = 0; // Clear device address
|
||||
|
@ -410,11 +412,13 @@ void BTD::HCI_event_task() {
|
|||
break;
|
||||
|
||||
case EV_INQUIRY_COMPLETE:
|
||||
if(inquiry_counter >= 5 && (pairWithWii || pairWithHIDDevice)) {
|
||||
if (inquiry_counter >= 5 && (pairWithWii || pairWithHIDDevice || pairWithOtherDevice)) {
|
||||
inquiry_counter = 0;
|
||||
#ifdef DEBUG_USB_HOST
|
||||
if(pairWithWii)
|
||||
Notify(PSTR("\r\nCouldn't find Wiimote"), 0x80);
|
||||
else if (pairWithOtherDevice)
|
||||
Notify(PSTR("\r\nCouldn't find 'Other' device"), 0x80);
|
||||
else
|
||||
Notify(PSTR("\r\nCouldn't find HID device"), 0x80);
|
||||
#endif
|
||||
|
@ -422,6 +426,8 @@ void BTD::HCI_event_task() {
|
|||
pairWithWii = false;
|
||||
connectToHIDDevice = false;
|
||||
pairWithHIDDevice = false;
|
||||
connectToOtherDevice = false;
|
||||
pairWithOtherDevice = false;
|
||||
hci_state = HCI_SCANNING_STATE;
|
||||
}
|
||||
inquiry_counter++;
|
||||
|
@ -464,17 +470,37 @@ void BTD::HCI_event_task() {
|
|||
disc_bdaddr[j] = hcibuf[j + 3 + 6 * i];
|
||||
|
||||
hci_set_flag(HCI_FLAG_DEVICE_FOUND);
|
||||
}
|
||||
} else {
|
||||
#ifdef EXTRADEBUG
|
||||
else {
|
||||
Notify(PSTR("\r\nClass of device: "), 0x80);
|
||||
D_PrintHex<uint8_t > (classOfDevice[2], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (classOfDevice[1], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (classOfDevice[0], 0x80);
|
||||
}
|
||||
#endif
|
||||
uint8_t discovered = true;
|
||||
for (uint8_t j = 0; j < 6; j++) {
|
||||
if (hcibuf[j + 3 + 6 * i] != remote_bdaddr[j])
|
||||
discovered = false;
|
||||
disc_bdaddr[j] = hcibuf[j + 3 + 6 * i];
|
||||
}
|
||||
if (discovered) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nDevice: "), 0x80);
|
||||
for (int8_t j = 5; j > 0; j--) {
|
||||
D_PrintHex<uint8_t > (disc_bdaddr[j], 0x80);
|
||||
Notify(PSTR(":"), 0x80);
|
||||
}
|
||||
D_PrintHex<uint8_t > (disc_bdaddr[0], 0x80);
|
||||
|
||||
Notify(PSTR(" has been found"), 0x80);
|
||||
#endif
|
||||
hci_set_flag(HCI_FLAG_DEVICE_FOUND);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -582,6 +608,11 @@ void BTD::HCI_event_task() {
|
|||
Notify(PSTR("\r\nPairing successful with HID device"), 0x80);
|
||||
#endif
|
||||
connectToHIDDevice = true; // Used to indicate to the BTHID service, that it should connect to this device
|
||||
} else if (pairWithOtherDevice && !connectToOtherDevice) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nPairing successful with 'Other' device"), 0x80);
|
||||
#endif
|
||||
connectToOtherDevice = true;
|
||||
}
|
||||
break;
|
||||
/* We will just ignore the following events */
|
||||
|
@ -694,12 +725,14 @@ void BTD::HCI_task() {
|
|||
break;
|
||||
|
||||
case HCI_CHECK_DEVICE_SERVICE:
|
||||
if(pairWithHIDDevice || pairWithWii) { // Check if it should try to connect to a Wiimote
|
||||
if(pairWithHIDDevice || pairWithWii || pairWithOtherDevice) { // Check if it should try to connect to a HID device, Wiimote or 'Other device'
|
||||
#ifdef DEBUG_USB_HOST
|
||||
if(pairWithWii)
|
||||
Notify(PSTR("\r\nStarting inquiry\r\nPress 1 & 2 on the Wiimote\r\nOr press sync if you are using a Wii U Pro Controller"), 0x80);
|
||||
else
|
||||
else if (pairWithHIDDevice)
|
||||
Notify(PSTR("\r\nPlease enable discovery of your device"), 0x80);
|
||||
else
|
||||
Notify(PSTR("\r\nPairing with 'Other' device with predefined address"), 0x80);
|
||||
#endif
|
||||
hci_inquiry();
|
||||
hci_state = HCI_INQUIRY_STATE;
|
||||
|
@ -713,20 +746,28 @@ void BTD::HCI_task() {
|
|||
#ifdef DEBUG_USB_HOST
|
||||
if(pairWithWii)
|
||||
Notify(PSTR("\r\nWiimote found"), 0x80);
|
||||
else if (pairWithOtherDevice)
|
||||
Notify(PSTR("\r\n'Other' device found"), 0x80);
|
||||
else
|
||||
Notify(PSTR("\r\nHID device found"), 0x80);
|
||||
|
||||
Notify(PSTR("\r\nNow just create the instance like so:"), 0x80);
|
||||
if(pairWithWii)
|
||||
Notify(PSTR("\r\nWII Wii(&Btd);"), 0x80);
|
||||
else
|
||||
Notify(PSTR("\r\nBTHID bthid(&Btd);"), 0x80);
|
||||
if (!pairWithOtherDevice) {
|
||||
Notify(PSTR("\r\nNow just create the instance like so:"), 0x80);
|
||||
if(pairWithWii)
|
||||
Notify(PSTR("\r\nWII Wii(&Btd);"), 0x80);
|
||||
else
|
||||
#ifdef _ps4bt_h_ // Check if the PS4 driver is being used
|
||||
Notify(PSTR("\r\nPS4BT PS4(&Btd);"), 0x80);
|
||||
#else
|
||||
Notify(PSTR("\r\nBTHID bthid(&Btd);"), 0x80);
|
||||
#endif
|
||||
|
||||
Notify(PSTR("\r\nAnd then press any button on the "), 0x80);
|
||||
if(pairWithWii)
|
||||
Notify(PSTR("Wiimote"), 0x80);
|
||||
else
|
||||
Notify(PSTR("device"), 0x80);
|
||||
Notify(PSTR("\r\nAnd then press any button on the "), 0x80);
|
||||
if(pairWithWii)
|
||||
Notify(PSTR("Wiimote"), 0x80);
|
||||
else
|
||||
Notify(PSTR("device"), 0x80);
|
||||
}
|
||||
#endif
|
||||
if(motionPlusInside) {
|
||||
hci_remote_name(); // We need to know the name to distinguish between a Wiimote and a Wii U Pro Controller
|
||||
|
@ -741,6 +782,8 @@ void BTD::HCI_task() {
|
|||
#ifdef DEBUG_USB_HOST
|
||||
if(pairWithWii)
|
||||
Notify(PSTR("\r\nConnecting to Wiimote"), 0x80);
|
||||
else if (pairWithOtherDevice)
|
||||
Notify(PSTR("\r\nConnecting to 'Other' device"), 0x80);
|
||||
else
|
||||
Notify(PSTR("\r\nConnecting to HID device"), 0x80);
|
||||
#endif
|
||||
|
@ -755,6 +798,8 @@ void BTD::HCI_task() {
|
|||
#ifdef DEBUG_USB_HOST
|
||||
if(pairWithWii)
|
||||
Notify(PSTR("\r\nConnected to Wiimote"), 0x80);
|
||||
else if (pairWithOtherDevice)
|
||||
Notify(PSTR("\r\nConnected to 'Other' device"), 0x80);
|
||||
else
|
||||
Notify(PSTR("\r\nConnected to HID device"), 0x80);
|
||||
#endif
|
||||
|
@ -770,7 +815,8 @@ void BTD::HCI_task() {
|
|||
break;
|
||||
|
||||
case HCI_SCANNING_STATE:
|
||||
if(!connectToWii && !pairWithWii && !connectToHIDDevice && !pairWithHIDDevice) {
|
||||
if (!connectToWii && !pairWithWii && !connectToHIDDevice && !pairWithHIDDevice && !connectToOtherDevice && !pairWithOtherDevice) {
|
||||
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nWait For Incoming Connection Request"), 0x80);
|
||||
#endif
|
||||
|
@ -879,6 +925,7 @@ void BTD::HCI_task() {
|
|||
|
||||
connectToWii = incomingWii = pairWithWii = false;
|
||||
connectToHIDDevice = incomingHIDDevice = pairWithHIDDevice = false;
|
||||
pairWithOtherDevice = connectToOtherDevice = false;
|
||||
incomingPS4 = false;
|
||||
|
||||
hci_state = HCI_SCANNING_STATE;
|
||||
|
@ -1040,7 +1087,10 @@ void BTD::hci_inquiry_cancel() {
|
|||
}
|
||||
|
||||
void BTD::hci_connect() {
|
||||
hci_connect(disc_bdaddr); // Use last discovered device
|
||||
if (pairWithOtherDevice)
|
||||
hci_connect(remote_bdaddr);
|
||||
else
|
||||
hci_connect(disc_bdaddr); // Use last discovered device
|
||||
}
|
||||
|
||||
void BTD::hci_connect(uint8_t *bdaddr) {
|
||||
|
|
13
BTD.h
13
BTD.h
|
@ -524,6 +524,19 @@ public:
|
|||
/** True when it should pair with a device like a mouse or keyboard. */
|
||||
bool pairWithHIDDevice;
|
||||
|
||||
/** True when it should pair with a device with a specific Bluetooth address. */
|
||||
bool pairWithOtherDevice;
|
||||
/* True when it should connect to a device with a specific Bluetooth address. */
|
||||
bool connectToOtherDevice;
|
||||
/** Call this to pair with a device with a specific Bluetooth address. */
|
||||
void pairWithOther() {
|
||||
pairWithOtherDevice = true;
|
||||
hci_state = HCI_CONNECT_DEVICE_STATE;
|
||||
};
|
||||
|
||||
/** Remote address of Bluetooth device to connect to. */
|
||||
uint8_t remote_bdaddr[6];
|
||||
|
||||
/**
|
||||
* Read the poll interval taken from the endpoint descriptors.
|
||||
* @return The poll interval in ms.
|
||||
|
|
|
@ -73,6 +73,7 @@ Currently the following boards are supported by the library:
|
|||
* Balanduino
|
||||
* Sanguino
|
||||
* Black Widdow
|
||||
* Luminardo
|
||||
|
||||
The following boards need to be activated manually in [settings.h](settings.h):
|
||||
|
||||
|
|
201
SPP.cpp
Normal file → Executable file
201
SPP.cpp
Normal file → Executable file
|
@ -20,30 +20,8 @@
|
|||
//#define EXTRADEBUG // Uncomment to get even more debugging data
|
||||
//#define PRINTREPORT // Uncomment to print the report sent to the Arduino
|
||||
|
||||
/*
|
||||
* CRC (reversed crc) lookup table as calculated by the table generator in ETSI TS 101 369 V6.3.0.
|
||||
*/
|
||||
const uint8_t rfcomm_crc_table[256] PROGMEM = {/* reversed, 8-bit, poly=0x07 */
|
||||
0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B,
|
||||
0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67,
|
||||
0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43,
|
||||
0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F,
|
||||
0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B,
|
||||
0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17,
|
||||
0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33,
|
||||
0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F,
|
||||
0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B,
|
||||
0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87,
|
||||
0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3,
|
||||
0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF,
|
||||
0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB,
|
||||
0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7,
|
||||
0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3,
|
||||
0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF
|
||||
};
|
||||
|
||||
SPP::SPP(BTD *p, const char* name, const char* pin) :
|
||||
pBtd(p) // Pointer to BTD class instance - mandatory
|
||||
SPP::SPP(BTD *p, const char *name, const char *pin, bool pair, uint8_t *addr) :
|
||||
SPPBase(p)
|
||||
{
|
||||
if(pBtd)
|
||||
pBtd->registerServiceClass(this); // Register it as a Bluetooth service
|
||||
|
@ -51,9 +29,16 @@ pBtd(p) // Pointer to BTD class instance - mandatory
|
|||
pBtd->btdName = name;
|
||||
pBtd->btdPin = pin;
|
||||
|
||||
if (addr) // Make sure address is set
|
||||
pBtd->pairWithOtherDevice = pair;
|
||||
|
||||
for (uint8_t i = 0; i < 6; i++)
|
||||
pBtd->remote_bdaddr[i] = addr[i];
|
||||
|
||||
/* Set device cid for the SDP and RFCOMM channelse */
|
||||
sdp_dcid[0] = 0x50; // 0x0050
|
||||
sdp_dcid[1] = 0x00;
|
||||
|
||||
rfcomm_dcid[0] = 0x51; // 0x0051
|
||||
rfcomm_dcid[1] = 0x00;
|
||||
|
||||
|
@ -71,19 +56,7 @@ void SPP::Reset() {
|
|||
sppIndex = 0;
|
||||
}
|
||||
|
||||
void SPP::disconnect() {
|
||||
connected = false;
|
||||
// First the two L2CAP channels has to be disconnected and then the HCI connection
|
||||
if(RFCOMMConnected)
|
||||
pBtd->l2cap_disconnection_request(hci_handle, ++identifier, rfcomm_scid, rfcomm_dcid);
|
||||
if(RFCOMMConnected && SDPConnected)
|
||||
delay(1); // Add delay between commands
|
||||
if(SDPConnected)
|
||||
pBtd->l2cap_disconnection_request(hci_handle, ++identifier, sdp_scid, sdp_dcid);
|
||||
l2cap_sdp_state = L2CAP_DISCONNECT_RESPONSE;
|
||||
}
|
||||
|
||||
void SPP::ACLData(uint8_t* l2capinbuf) {
|
||||
void SPP::ACLData(uint8_t *l2capinbuf) {
|
||||
if(!connected) {
|
||||
if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {
|
||||
if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == SDP_PSM && !pBtd->sdpConnectionClaimed) {
|
||||
|
@ -99,7 +72,7 @@ void SPP::ACLData(uint8_t* l2capinbuf) {
|
|||
}
|
||||
//if((l2capinbuf[0] | (uint16_t)l2capinbuf[1] << 8) == (hci_handle | 0x2000U)) { // acl_handle_ok
|
||||
if(UHS_ACL_HANDLE_OK(l2capinbuf, hci_handle)) { // acl_handle_ok
|
||||
if((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001U) { //l2cap_control - Channel ID for ACL-U
|
||||
if((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001U) { // l2cap_control - Channel ID for ACL-U
|
||||
if(l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nL2CAP Command Rejected - Reason: "), 0x80);
|
||||
|
@ -528,9 +501,8 @@ void SPP::RFCOMM_task() {
|
|||
}
|
||||
/************************************************************/
|
||||
/* SDP Commands */
|
||||
|
||||
/************************************************************/
|
||||
void SPP::SDP_Command(uint8_t* data, uint8_t nbytes) { // See page 223 in the Bluetooth specs
|
||||
void SPP::SDP_Command(uint8_t *data, uint8_t nbytes) { // See page 223 in the Bluetooth specs
|
||||
pBtd->L2CAP_Command(hci_handle, data, nbytes, sdp_scid[0], sdp_scid[1]);
|
||||
}
|
||||
|
||||
|
@ -660,156 +632,7 @@ void SPP::l2capResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow) {
|
|||
}
|
||||
/************************************************************/
|
||||
/* RFCOMM Commands */
|
||||
|
||||
/************************************************************/
|
||||
void SPP::RFCOMM_Command(uint8_t* data, uint8_t nbytes) {
|
||||
pBtd->L2CAP_Command(hci_handle, data, nbytes, rfcomm_scid[0], rfcomm_scid[1]);
|
||||
}
|
||||
|
||||
void SPP::sendRfcomm(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t* data, uint8_t length) {
|
||||
l2capoutbuf[0] = channel | direction | CR | extendAddress; // RFCOMM Address
|
||||
l2capoutbuf[1] = channelType | pfBit; // RFCOMM Control
|
||||
l2capoutbuf[2] = length << 1 | 0x01; // Length and format (always 0x01 bytes format)
|
||||
uint8_t i = 0;
|
||||
for(; i < length; i++)
|
||||
l2capoutbuf[i + 3] = data[i];
|
||||
l2capoutbuf[i + 3] = calcFcs(l2capoutbuf);
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR(" - RFCOMM Data: "), 0x80);
|
||||
for(i = 0; i < length + 4; i++) {
|
||||
D_PrintHex<uint8_t > (l2capoutbuf[i], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
}
|
||||
#endif
|
||||
RFCOMM_Command(l2capoutbuf, length + 4);
|
||||
}
|
||||
|
||||
void SPP::sendRfcommCredit(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t credit) {
|
||||
l2capoutbuf[0] = channel | direction | CR | extendAddress; // RFCOMM Address
|
||||
l2capoutbuf[1] = channelType | pfBit; // RFCOMM Control
|
||||
l2capoutbuf[2] = 0x01; // Length = 0
|
||||
l2capoutbuf[3] = credit; // Credit
|
||||
l2capoutbuf[4] = calcFcs(l2capoutbuf);
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR(" - RFCOMM Credit Data: "), 0x80);
|
||||
for(uint8_t i = 0; i < 5; i++) {
|
||||
D_PrintHex<uint8_t > (l2capoutbuf[i], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
}
|
||||
#endif
|
||||
RFCOMM_Command(l2capoutbuf, 5);
|
||||
}
|
||||
|
||||
/* CRC on 2 bytes */
|
||||
uint8_t SPP::crc(uint8_t *data) {
|
||||
return (pgm_read_byte(&rfcomm_crc_table[pgm_read_byte(&rfcomm_crc_table[0xFF ^ data[0]]) ^ data[1]]));
|
||||
}
|
||||
|
||||
/* Calculate FCS */
|
||||
uint8_t SPP::calcFcs(uint8_t *data) {
|
||||
uint8_t temp = crc(data);
|
||||
if((data[1] & 0xEF) == RFCOMM_UIH)
|
||||
return (0xFF - temp); // FCS on 2 bytes
|
||||
else
|
||||
return (0xFF - pgm_read_byte(&rfcomm_crc_table[temp ^ data[2]])); // FCS on 3 bytes
|
||||
}
|
||||
|
||||
/* Check FCS */
|
||||
bool SPP::checkFcs(uint8_t *data, uint8_t fcs) {
|
||||
uint8_t temp = crc(data);
|
||||
if((data[1] & 0xEF) != RFCOMM_UIH)
|
||||
temp = pgm_read_byte(&rfcomm_crc_table[temp ^ data[2]]); // FCS on 3 bytes
|
||||
return (pgm_read_byte(&rfcomm_crc_table[temp ^ fcs]) == 0xCF);
|
||||
}
|
||||
|
||||
/* Serial commands */
|
||||
#if defined(ARDUINO) && ARDUINO >=100
|
||||
|
||||
size_t SPP::write(uint8_t data) {
|
||||
return write(&data, 1);
|
||||
}
|
||||
#else
|
||||
|
||||
void SPP::write(uint8_t data) {
|
||||
write(&data, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >=100
|
||||
|
||||
size_t SPP::write(const uint8_t *data, size_t size) {
|
||||
#else
|
||||
|
||||
void SPP::write(const uint8_t *data, size_t size) {
|
||||
#endif
|
||||
for(uint8_t i = 0; i < size; i++) {
|
||||
if(sppIndex >= sizeof (sppOutputBuffer) / sizeof (sppOutputBuffer[0]))
|
||||
send(); // Send the current data in the buffer
|
||||
sppOutputBuffer[sppIndex++] = data[i]; // All the bytes are put into a buffer and then send using the send() function
|
||||
}
|
||||
#if defined(ARDUINO) && ARDUINO >=100
|
||||
return size;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SPP::send() {
|
||||
if(!connected || !sppIndex)
|
||||
return;
|
||||
uint8_t length; // This is the length of the string we are sending
|
||||
uint8_t offset = 0; // This is used to keep track of where we are in the string
|
||||
|
||||
l2capoutbuf[0] = rfcommChannelConnection | 0 | 0 | extendAddress; // RFCOMM Address
|
||||
l2capoutbuf[1] = RFCOMM_UIH; // RFCOMM Control
|
||||
|
||||
while(sppIndex) { // We will run this while loop until this variable is 0
|
||||
if(sppIndex > (sizeof (l2capoutbuf) - 4)) // Check if the string is larger than the outgoing buffer
|
||||
length = sizeof (l2capoutbuf) - 4;
|
||||
else
|
||||
length = sppIndex;
|
||||
|
||||
l2capoutbuf[2] = length << 1 | 1; // Length
|
||||
uint8_t i = 0;
|
||||
for(; i < length; i++)
|
||||
l2capoutbuf[i + 3] = sppOutputBuffer[i + offset];
|
||||
l2capoutbuf[i + 3] = calcFcs(l2capoutbuf); // Calculate checksum
|
||||
|
||||
RFCOMM_Command(l2capoutbuf, length + 4);
|
||||
|
||||
sppIndex -= length;
|
||||
offset += length; // Increment the offset
|
||||
}
|
||||
}
|
||||
|
||||
int SPP::available(void) {
|
||||
return rfcommAvailable;
|
||||
};
|
||||
|
||||
void SPP::discard(void) {
|
||||
rfcommAvailable = 0;
|
||||
}
|
||||
|
||||
int SPP::peek(void) {
|
||||
if(rfcommAvailable == 0) // Don't read if there is nothing in the buffer
|
||||
return -1;
|
||||
return rfcommDataBuffer[0];
|
||||
}
|
||||
|
||||
int SPP::read(void) {
|
||||
if(rfcommAvailable == 0) // Don't read if there is nothing in the buffer
|
||||
return -1;
|
||||
uint8_t output = rfcommDataBuffer[0];
|
||||
for(uint8_t i = 1; i < rfcommAvailable; i++)
|
||||
rfcommDataBuffer[i - 1] = rfcommDataBuffer[i]; // Shift the buffer one left
|
||||
rfcommAvailable--;
|
||||
bytesRead++;
|
||||
if(bytesRead > (sizeof (rfcommDataBuffer) - 5)) { // We will send the command just before it runs out of credit
|
||||
bytesRead = 0;
|
||||
sendRfcommCredit(rfcommChannelConnection, rfcommDirection, 0, RFCOMM_UIH, 0x10, sizeof (rfcommDataBuffer)); // Send more credit
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nSent "), 0x80);
|
||||
Notify((uint8_t)sizeof (rfcommDataBuffer), 0x80);
|
||||
Notify(PSTR(" more credit"), 0x80);
|
||||
#endif
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
|
175
SPP.h
Normal file → Executable file
175
SPP.h
Normal file → Executable file
|
@ -18,205 +18,56 @@
|
|||
#ifndef _spp_h_
|
||||
#define _spp_h_
|
||||
|
||||
#include "BTD.h"
|
||||
|
||||
/* Used for SDP */
|
||||
#define SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU 0x06 // See the RFCOMM specs
|
||||
#define SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU 0x07 // See the RFCOMM specs
|
||||
#define SERIALPORT_UUID 0x1101 // See http://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm
|
||||
#define L2CAP_UUID 0x0100
|
||||
|
||||
/* Used for RFCOMM */
|
||||
#define RFCOMM_SABM 0x2F
|
||||
#define RFCOMM_UA 0x63
|
||||
#define RFCOMM_UIH 0xEF
|
||||
//#define RFCOMM_DM 0x0F
|
||||
#define RFCOMM_DISC 0x43
|
||||
|
||||
#define extendAddress 0x01 // Always 1
|
||||
|
||||
// Multiplexer message types
|
||||
#define BT_RFCOMM_PN_CMD 0x83
|
||||
#define BT_RFCOMM_PN_RSP 0x81
|
||||
#define BT_RFCOMM_MSC_CMD 0xE3
|
||||
#define BT_RFCOMM_MSC_RSP 0xE1
|
||||
#define BT_RFCOMM_RPN_CMD 0x93
|
||||
#define BT_RFCOMM_RPN_RSP 0x91
|
||||
/*
|
||||
#define BT_RFCOMM_TEST_CMD 0x23
|
||||
#define BT_RFCOMM_TEST_RSP 0x21
|
||||
#define BT_RFCOMM_FCON_CMD 0xA3
|
||||
#define BT_RFCOMM_FCON_RSP 0xA1
|
||||
#define BT_RFCOMM_FCOFF_CMD 0x63
|
||||
#define BT_RFCOMM_FCOFF_RSP 0x61
|
||||
#define BT_RFCOMM_RLS_CMD 0x53
|
||||
#define BT_RFCOMM_RLS_RSP 0x51
|
||||
#define BT_RFCOMM_NSC_RSP 0x11
|
||||
*/
|
||||
#include "SPPBase.h"
|
||||
|
||||
/**
|
||||
* This BluetoothService class implements the Serial Port Protocol (SPP).
|
||||
* It inherits the Arduino Stream class. This allows it to use all the standard Arduino print and stream functions.
|
||||
* This BluetoothService class a Serial Port Protocol (SPP) server.
|
||||
* It inherits the Arduino Stream class. This allows it to use all the standard Arduino print functions.
|
||||
*/
|
||||
class SPP : public BluetoothService, public Stream {
|
||||
class SPP : public SPPBase {
|
||||
public:
|
||||
/**
|
||||
* Constructor for the SPP class.
|
||||
* @param p Pointer to BTD class instance.
|
||||
* @param name Set the name to BTD#btdName. If argument is omitted, then "Arduino" will be used.
|
||||
* @param p Pointer to BTD class instance.
|
||||
* @param name Set the name to BTD#btdName. If argument is omitted, then "Arduino" will be used.
|
||||
* @param pin Write the pin to BTD#btdPin. If argument is omitted, then "0000" will be used.
|
||||
* @param pair Set this to true if you want to pair with a device.
|
||||
* @param addr Set this to the address you want to connect to.
|
||||
*/
|
||||
SPP(BTD *p, const char *name = "Arduino", const char *pin = "0000");
|
||||
SPP(BTD *p, const char *name = "Arduino", const char *pin = "0000", bool pair = false, uint8_t *addr = NULL);
|
||||
|
||||
/**
|
||||
* Used to provide Boolean tests for the class.
|
||||
* @return Return true if SPP communication is connected.
|
||||
*/
|
||||
operator bool() {
|
||||
return connected;
|
||||
}
|
||||
/** Variable used to indicate if the connection is established. */
|
||||
bool connected;
|
||||
|
||||
/** @name BluetoothService implementation */
|
||||
/** @name SPPBase implementation */
|
||||
/**
|
||||
* Used to pass acldata to the services.
|
||||
* @param ACLData Incoming acldata.
|
||||
*/
|
||||
virtual void ACLData(uint8_t* ACLData);
|
||||
virtual void ACLData(uint8_t *ACLData);
|
||||
/** Used to establish the connection automatically. */
|
||||
virtual void Run();
|
||||
/** Use this to reset the service. */
|
||||
virtual void Reset();
|
||||
/** Used this to disconnect the virtual serial port. */
|
||||
virtual void disconnect();
|
||||
/**@}*/
|
||||
|
||||
/** @name Serial port profile (SPP) Print functions */
|
||||
/**
|
||||
* Get number of bytes waiting to be read.
|
||||
* @return Return the number of bytes ready to be read.
|
||||
*/
|
||||
virtual int available(void);
|
||||
|
||||
/** Send out all bytes in the buffer. */
|
||||
virtual void flush(void) {
|
||||
send();
|
||||
};
|
||||
/**
|
||||
* Used to read the next value in the buffer without advancing to the next one.
|
||||
* @return Return the byte. Will return -1 if no bytes are available.
|
||||
*/
|
||||
virtual int peek(void);
|
||||
/**
|
||||
* Used to read the buffer.
|
||||
* @return Return the byte. Will return -1 if no bytes are available.
|
||||
*/
|
||||
virtual int read(void);
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >=100
|
||||
/**
|
||||
* Writes the byte to send to a buffer. The message is send when either send() or after Usb.Task() is called.
|
||||
* @param data The byte to write.
|
||||
* @return Return the number of bytes written.
|
||||
*/
|
||||
virtual size_t write(uint8_t data);
|
||||
/**
|
||||
* Writes the bytes to send to a buffer. The message is send when either send() or after Usb.Task() is called.
|
||||
* @param data The data array to send.
|
||||
* @param size Size of the data.
|
||||
* @return Return the number of bytes written.
|
||||
*/
|
||||
virtual size_t write(const uint8_t* data, size_t size);
|
||||
/** Pull in write(const char *str) from Print */
|
||||
using Print::write;
|
||||
#else
|
||||
/**
|
||||
* Writes the byte to send to a buffer. The message is send when either send() or after Usb.Task() is called.
|
||||
* @param data The byte to write.
|
||||
*/
|
||||
virtual void write(uint8_t data);
|
||||
/**
|
||||
* Writes the bytes to send to a buffer. The message is send when either send() or after Usb.Task() is called.
|
||||
* @param data The data array to send.
|
||||
* @param size Size of the data.
|
||||
*/
|
||||
virtual void write(const uint8_t* data, size_t size);
|
||||
#endif
|
||||
|
||||
/** Discard all the bytes in the buffer. */
|
||||
void discard(void);
|
||||
/**
|
||||
* This will send all the bytes in the buffer.
|
||||
* This is called whenever Usb.Task() is called,
|
||||
* but can also be called via this function.
|
||||
*/
|
||||
void send(void);
|
||||
/**@}*/
|
||||
|
||||
private:
|
||||
/* Bluetooth dongle library pointer */
|
||||
BTD *pBtd;
|
||||
|
||||
/* Set true when a channel is created */
|
||||
bool SDPConnected;
|
||||
bool RFCOMMConnected;
|
||||
|
||||
uint16_t hci_handle; // The HCI Handle for the connection
|
||||
|
||||
/* Variables used by L2CAP state machines */
|
||||
uint8_t l2cap_sdp_state;
|
||||
uint8_t l2cap_rfcomm_state;
|
||||
uint32_t l2cap_event_flag; // l2cap flags of received Bluetooth events
|
||||
|
||||
uint8_t l2capoutbuf[BULK_MAXPKTSIZE]; // General purpose buffer for l2cap out data
|
||||
uint8_t rfcommbuf[10]; // Buffer for RFCOMM Commands
|
||||
|
||||
/* L2CAP Channels */
|
||||
uint8_t sdp_scid[2]; // L2CAP source CID for SDP
|
||||
uint8_t sdp_dcid[2]; // 0x0050
|
||||
uint8_t rfcomm_scid[2]; // L2CAP source CID for RFCOMM
|
||||
uint8_t rfcomm_dcid[2]; // 0x0051
|
||||
uint8_t identifier; // Identifier for command
|
||||
|
||||
/* RFCOMM Variables */
|
||||
uint8_t rfcommChannel;
|
||||
uint8_t rfcommChannelConnection; // This is the channel the SPP channel will be running at
|
||||
uint8_t rfcommDirection;
|
||||
uint8_t rfcommCommandResponse;
|
||||
uint8_t rfcommChannelType;
|
||||
uint8_t rfcommPfBit;
|
||||
|
||||
unsigned long timer;
|
||||
bool waitForLastCommand;
|
||||
bool creditSent;
|
||||
|
||||
uint8_t rfcommDataBuffer[100]; // Create a 100 sized buffer for incoming data
|
||||
uint8_t sppOutputBuffer[100]; // Create a 100 sized buffer for outgoing SPP data
|
||||
uint8_t sppIndex;
|
||||
uint8_t rfcommAvailable;
|
||||
|
||||
bool firstMessage; // Used to see if it's the first SDP request received
|
||||
uint8_t bytesRead; // Counter to see when it's time to send more credit
|
||||
|
||||
/* State machines */
|
||||
void SDP_task(); // SDP state machine
|
||||
void RFCOMM_task(); // RFCOMM state machine
|
||||
|
||||
/* SDP Commands */
|
||||
void SDP_Command(uint8_t *data, uint8_t nbytes);
|
||||
virtual void SDP_Command(uint8_t *data, uint8_t nbytes);
|
||||
void serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow);
|
||||
void serialPortResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow);
|
||||
void serialPortResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow);
|
||||
void l2capResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow);
|
||||
void l2capResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow);
|
||||
|
||||
/* RFCOMM Commands */
|
||||
void RFCOMM_Command(uint8_t *data, uint8_t nbytes);
|
||||
void sendRfcomm(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t *data, uint8_t length);
|
||||
void sendRfcommCredit(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t credit);
|
||||
uint8_t calcFcs(uint8_t *data);
|
||||
bool checkFcs(uint8_t *data, uint8_t fcs);
|
||||
uint8_t crc(uint8_t *data);
|
||||
virtual void RFCOMM_Command(uint8_t *data, uint8_t nbytes); // Used for RFCOMM commands
|
||||
};
|
||||
#endif
|
||||
|
|
185
SPPBase.cpp
Normal file
185
SPPBase.cpp
Normal file
|
@ -0,0 +1,185 @@
|
|||
/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved.
|
||||
|
||||
This software may be distributed and modified under the terms of the GNU
|
||||
General Public License version 2 (GPL2) as published by the Free Software
|
||||
Foundation and appearing in the file GPL2.TXT included in the packaging of
|
||||
this file. Please note that GPL2 Section 2[b] requires that all works based
|
||||
on this software must also be made publicly available under the terms of
|
||||
the GPL2 ("Copyleft").
|
||||
|
||||
Contact information
|
||||
-------------------
|
||||
|
||||
Kristian Lauszus, TKJ Electronics
|
||||
Web : http://www.tkjelectronics.com
|
||||
e-mail : kristianl@tkjelectronics.com
|
||||
*/
|
||||
|
||||
#include "SPPBase.h"
|
||||
|
||||
SPPBase::SPPBase(BTD *p) : pBtd(p) {};
|
||||
|
||||
void SPPBase::disconnect() {
|
||||
connected = false;
|
||||
// First the two L2CAP channels has to be disconnected and then the HCI connection
|
||||
if (RFCOMMConnected)
|
||||
pBtd->l2cap_disconnection_request(hci_handle, ++identifier, rfcomm_scid, rfcomm_dcid);
|
||||
if (RFCOMMConnected && SDPConnected)
|
||||
delay(1); // Add delay between commands
|
||||
if (SDPConnected)
|
||||
pBtd->l2cap_disconnection_request(hci_handle, ++identifier, sdp_scid, sdp_dcid);
|
||||
l2cap_sdp_state = L2CAP_DISCONNECT_RESPONSE;
|
||||
}
|
||||
|
||||
void SPPBase::sendRfcomm(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t* data, uint8_t length) {
|
||||
l2capoutbuf[0] = channel | direction | CR | extendAddress; // RFCOMM Address
|
||||
l2capoutbuf[1] = channelType | pfBit; // RFCOMM Control
|
||||
l2capoutbuf[2] = length << 1 | 0x01; // Length and format (always 0x01 bytes format)
|
||||
uint8_t i = 0;
|
||||
for (; i < length; i++)
|
||||
l2capoutbuf[i + 3] = data[i];
|
||||
l2capoutbuf[i + 3] = calcFcs(l2capoutbuf);
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR(" - RFCOMM Data: "), 0x80);
|
||||
for (i = 0; i < length + 4; i++) {
|
||||
D_PrintHex<uint8_t > (l2capoutbuf[i], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
}
|
||||
#endif
|
||||
RFCOMM_Command(l2capoutbuf, length + 4);
|
||||
}
|
||||
|
||||
void SPPBase::sendRfcommCredit(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t credit) {
|
||||
l2capoutbuf[0] = channel | direction | CR | extendAddress; // RFCOMM Address
|
||||
l2capoutbuf[1] = channelType | pfBit; // RFCOMM Control
|
||||
l2capoutbuf[2] = 0x01; // Length = 0
|
||||
l2capoutbuf[3] = credit; // Credit
|
||||
l2capoutbuf[4] = calcFcs(l2capoutbuf);
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR(" - RFCOMM Credit Data: "), 0x80);
|
||||
for (uint8_t i = 0; i < 5; i++) {
|
||||
D_PrintHex<uint8_t > (l2capoutbuf[i], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
}
|
||||
#endif
|
||||
RFCOMM_Command(l2capoutbuf, 5);
|
||||
}
|
||||
|
||||
|
||||
/* CRC on 2 bytes */
|
||||
uint8_t SPPBase::crc(uint8_t *data) {
|
||||
return (pgm_read_byte(&rfcomm_crc_table[pgm_read_byte(&rfcomm_crc_table[0xFF ^ data[0]]) ^ data[1]]));
|
||||
}
|
||||
|
||||
/* Calculate FCS */
|
||||
uint8_t SPPBase::calcFcs(uint8_t *data) {
|
||||
uint8_t temp = crc(data);
|
||||
if ((data[1] & 0xEF) == RFCOMM_UIH)
|
||||
return (0xFF - temp); // FCS on 2 bytes
|
||||
else
|
||||
return (0xFF - pgm_read_byte(&rfcomm_crc_table[temp ^ data[2]])); // FCS on 3 bytes
|
||||
}
|
||||
|
||||
/* Check FCS */
|
||||
bool SPPBase::checkFcs(uint8_t *data, uint8_t fcs) {
|
||||
uint8_t temp = crc(data);
|
||||
if ((data[1] & 0xEF) != RFCOMM_UIH)
|
||||
temp = pgm_read_byte(&rfcomm_crc_table[temp ^ data[2]]); // FCS on 3 bytes
|
||||
return (pgm_read_byte(&rfcomm_crc_table[temp ^ fcs]) == 0xCF);
|
||||
}
|
||||
|
||||
/* Serial commands */
|
||||
#if defined(ARDUINO) && ARDUINO >=100
|
||||
|
||||
size_t SPPBase::write(uint8_t data) {
|
||||
return write(&data, 1);
|
||||
}
|
||||
#else
|
||||
|
||||
void SPPBase::write(uint8_t data) {
|
||||
write(&data, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >=100
|
||||
|
||||
size_t SPPBase::write(const uint8_t *data, size_t size) {
|
||||
#else
|
||||
|
||||
void SPPBase::write(const uint8_t *data, size_t size) {
|
||||
#endif
|
||||
for(uint8_t i = 0; i < size; i++) {
|
||||
if(sppIndex >= sizeof (sppOutputBuffer) / sizeof (sppOutputBuffer[0]))
|
||||
send(); // Send the current data in the buffer
|
||||
sppOutputBuffer[sppIndex++] = data[i]; // All the bytes are put into a buffer and then send using the send() function
|
||||
}
|
||||
#if defined(ARDUINO) && ARDUINO >=100
|
||||
return size;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SPPBase::send() {
|
||||
if(!connected || !sppIndex)
|
||||
return;
|
||||
uint8_t length; // This is the length of the string we are sending
|
||||
uint8_t offset = 0; // This is used to keep track of where we are in the string
|
||||
|
||||
l2capoutbuf[0] = rfcommChannelConnection | 0 | 0 | extendAddress; // RFCOMM Address
|
||||
l2capoutbuf[1] = RFCOMM_UIH; // RFCOMM Control
|
||||
|
||||
while(sppIndex) { // We will run this while loop until this variable is 0
|
||||
if(sppIndex > (sizeof (l2capoutbuf) - 4)) // Check if the string is larger than the outgoing buffer
|
||||
length = sizeof (l2capoutbuf) - 4;
|
||||
else
|
||||
length = sppIndex;
|
||||
|
||||
l2capoutbuf[2] = length << 1 | 1; // Length
|
||||
uint8_t i = 0;
|
||||
for(; i < length; i++)
|
||||
l2capoutbuf[i + 3] = sppOutputBuffer[i + offset];
|
||||
l2capoutbuf[i + 3] = calcFcs(l2capoutbuf); // Calculate checksum
|
||||
|
||||
RFCOMM_Command(l2capoutbuf, length + 4);
|
||||
|
||||
sppIndex -= length;
|
||||
offset += length; // Increment the offset
|
||||
}
|
||||
}
|
||||
|
||||
int SPPBase::available(void) {
|
||||
return rfcommAvailable;
|
||||
};
|
||||
|
||||
void SPPBase::flush(void) {
|
||||
send();
|
||||
};
|
||||
|
||||
void SPPBase::discard(void) {
|
||||
rfcommAvailable = 0;
|
||||
}
|
||||
|
||||
int SPPBase::peek(void) {
|
||||
if(rfcommAvailable == 0) // Don't read if there is nothing in the buffer
|
||||
return -1;
|
||||
return rfcommDataBuffer[0];
|
||||
}
|
||||
|
||||
int SPPBase::read(void) {
|
||||
if(rfcommAvailable == 0) // Don't read if there is nothing in the buffer
|
||||
return -1;
|
||||
uint8_t output = rfcommDataBuffer[0];
|
||||
for(uint8_t i = 1; i < rfcommAvailable; i++)
|
||||
rfcommDataBuffer[i - 1] = rfcommDataBuffer[i]; // Shift the buffer one left
|
||||
rfcommAvailable--;
|
||||
bytesRead++;
|
||||
if(bytesRead > (sizeof (rfcommDataBuffer) - 5)) { // We will send the command just before it runs out of credit
|
||||
bytesRead = 0;
|
||||
sendRfcommCredit(rfcommChannelConnection, rfcommDirection, 0, RFCOMM_UIH, 0x10, sizeof (rfcommDataBuffer)); // Send more credit
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nSent "), 0x80);
|
||||
Notify((uint8_t)sizeof (rfcommDataBuffer), 0x80);
|
||||
Notify(PSTR(" more credit"), 0x80);
|
||||
#endif
|
||||
}
|
||||
return output;
|
||||
}
|
229
SPPBase.h
Normal file
229
SPPBase.h
Normal file
|
@ -0,0 +1,229 @@
|
|||
/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved.
|
||||
|
||||
This software may be distributed and modified under the terms of the GNU
|
||||
General Public License version 2 (GPL2) as published by the Free Software
|
||||
Foundation and appearing in the file GPL2.TXT included in the packaging of
|
||||
this file. Please note that GPL2 Section 2[b] requires that all works based
|
||||
on this software must also be made publicly available under the terms of
|
||||
the GPL2 ("Copyleft").
|
||||
|
||||
Contact information
|
||||
-------------------
|
||||
|
||||
Kristian Lauszus, TKJ Electronics
|
||||
Web : http://www.tkjelectronics.com
|
||||
e-mail : kristianl@tkjelectronics.com
|
||||
*/
|
||||
|
||||
#ifndef _sppbase_h_
|
||||
#define _sppbase_h_
|
||||
|
||||
#include "BTD.h"
|
||||
|
||||
/* Used for SDP */
|
||||
#define SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU 0x06 // See the RFCOMM specs
|
||||
#define SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU 0x07 // See the RFCOMM specs
|
||||
#define SDP_SERVICE_SEARCH_REQUEST_PDU 0x02
|
||||
#define SDP_SERVICE_SEARCH_RESPONSE_PDU 0x03
|
||||
|
||||
#define SERIALPORT_UUID 0x1101 // See http://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm
|
||||
#define L2CAP_UUID 0x0100
|
||||
|
||||
/* Used for RFCOMM */
|
||||
#define RFCOMM_SABM 0x2F
|
||||
#define RFCOMM_UA 0x63
|
||||
#define RFCOMM_UIH 0xEF
|
||||
#define RFCOMM_DM 0x0F
|
||||
#define RFCOMM_DISC 0x43
|
||||
|
||||
#define extendAddress 0x01 // Always 1
|
||||
|
||||
// Multiplexer message types
|
||||
#define BT_RFCOMM_PN_CMD 0x83
|
||||
#define BT_RFCOMM_PN_RSP 0x81
|
||||
#define BT_RFCOMM_MSC_CMD 0xE3
|
||||
#define BT_RFCOMM_MSC_RSP 0xE1
|
||||
#define BT_RFCOMM_RPN_CMD 0x93
|
||||
#define BT_RFCOMM_RPN_RSP 0x91
|
||||
/*
|
||||
#define BT_RFCOMM_TEST_CMD 0x23
|
||||
#define BT_RFCOMM_TEST_RSP 0x21
|
||||
#define BT_RFCOMM_FCON_CMD 0xA3
|
||||
#define BT_RFCOMM_FCON_RSP 0xA1
|
||||
#define BT_RFCOMM_FCOFF_CMD 0x63
|
||||
#define BT_RFCOMM_FCOFF_RSP 0x61
|
||||
#define BT_RFCOMM_RLS_CMD 0x53
|
||||
#define BT_RFCOMM_RLS_RSP 0x51
|
||||
#define BT_RFCOMM_NSC_RSP 0x11
|
||||
*/
|
||||
|
||||
/*
|
||||
* CRC (reversed crc) lookup table as calculated by the table generator in ETSI TS 101 369 V6.3.0.
|
||||
*/
|
||||
const uint8_t rfcomm_crc_table[256] PROGMEM = {/* reversed, 8-bit, poly=0x07 */
|
||||
0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B,
|
||||
0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67,
|
||||
0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43,
|
||||
0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F,
|
||||
0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B,
|
||||
0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17,
|
||||
0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33,
|
||||
0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F,
|
||||
0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B,
|
||||
0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87,
|
||||
0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3,
|
||||
0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF,
|
||||
0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB,
|
||||
0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7,
|
||||
0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3,
|
||||
0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF
|
||||
};
|
||||
|
||||
/**
|
||||
* This BluetoothService class implements the Serial Port Protocol (SPP).
|
||||
* It inherits the Arduino Stream class. This allows it to use all the standard Arduino print and stream functions.
|
||||
*/
|
||||
class SPPBase : public BluetoothService, public Stream {
|
||||
public:
|
||||
/**
|
||||
* Constructor for the SPPBase class.
|
||||
* @param p Pointer to BTD class instance.
|
||||
*/
|
||||
SPPBase(BTD *p);
|
||||
|
||||
/**
|
||||
* Used to provide Boolean tests for the class.
|
||||
* @return Return true if SPP communication is connected.
|
||||
*/
|
||||
operator bool() {
|
||||
return connected;
|
||||
};
|
||||
|
||||
/** Variable used to indicate if the connection is established. */
|
||||
bool connected;
|
||||
|
||||
/** @name Serial port profile (SPP) Print functions */
|
||||
/**
|
||||
* Get number of bytes waiting to be read.
|
||||
* @return Return the number of bytes ready to be read.
|
||||
*/
|
||||
virtual int available(void);
|
||||
/** Send out all bytes in the buffer. */
|
||||
virtual void flush(void);
|
||||
/**
|
||||
* Used to read the next value in the buffer without advancing to the next one.
|
||||
* @return Return the byte. Will return -1 if no bytes are available.
|
||||
*/
|
||||
virtual int peek(void);
|
||||
/**
|
||||
* Used to read the buffer.
|
||||
* @return Return the byte. Will return -1 if no bytes are available.
|
||||
*/
|
||||
virtual int read(void);
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >=100
|
||||
/**
|
||||
* Writes the byte to send to a buffer. The message is send when either send() or after Usb.Task() is called.
|
||||
* @param data The byte to write.
|
||||
* @return Return the number of bytes written.
|
||||
*/
|
||||
virtual size_t write(uint8_t data);
|
||||
/**
|
||||
* Writes the bytes to send to a buffer. The message is send when either send() or after Usb.Task() is called.
|
||||
* @param data The data array to send.
|
||||
* @param size Size of the data.
|
||||
* @return Return the number of bytes written.
|
||||
*/
|
||||
virtual size_t write(const uint8_t* data, size_t size);
|
||||
/** Pull in write(const char *str) from Print */
|
||||
using Print::write;
|
||||
#else
|
||||
/**
|
||||
* Writes the byte to send to a buffer. The message is send when either send() or after Usb.Task() is called.
|
||||
* @param data The byte to write.
|
||||
*/
|
||||
virtual void write(uint8_t data);
|
||||
/**
|
||||
* Writes the bytes to send to a buffer. The message is send when either send() or after Usb.Task() is called.
|
||||
* @param data The data array to send.
|
||||
* @param size Size of the data.
|
||||
*/
|
||||
virtual void write(const uint8_t* data, size_t size);
|
||||
#endif
|
||||
|
||||
/** Discard all the bytes in the buffer. */
|
||||
void discard(void);
|
||||
/**
|
||||
* This will send all the bytes in the buffer.
|
||||
* This is called whenever Usb.Task() is called,
|
||||
* but can also be called via this function.
|
||||
*/
|
||||
void send(void);
|
||||
/**@}*/
|
||||
protected:
|
||||
/** @name BluetoothService implementation */
|
||||
/**
|
||||
* Used to pass acldata to the services.
|
||||
* @param ACLData Incoming acldata.
|
||||
*/
|
||||
virtual void ACLData(uint8_t *ACLData) = 0;
|
||||
/** Used to establish the connection automatically. */
|
||||
virtual void Run() = 0;
|
||||
/** Use this to reset the service. */
|
||||
virtual void Reset() = 0;
|
||||
/** Used this to disconnect the virtual serial port. */
|
||||
virtual void disconnect();
|
||||
/**@}*/
|
||||
|
||||
/* Pointer to Bluetooth dongle library instance */
|
||||
BTD *pBtd;
|
||||
|
||||
/* Set true when a channel is created */
|
||||
bool SDPConnected;
|
||||
bool RFCOMMConnected;
|
||||
|
||||
uint16_t hci_handle; // The HCI Handle for the connection
|
||||
|
||||
/* Variables used by L2CAP state machines */
|
||||
uint8_t l2cap_sdp_state;
|
||||
uint8_t l2cap_rfcomm_state;
|
||||
|
||||
uint8_t l2capoutbuf[BULK_MAXPKTSIZE]; // General purpose buffer for l2cap out data
|
||||
uint8_t rfcommbuf[10]; // Buffer for RFCOMM Commands
|
||||
|
||||
/* L2CAP Channels */
|
||||
uint8_t sdp_scid[2]; // L2CAP source CID for SDP
|
||||
uint8_t sdp_dcid[2]; // 0x0050
|
||||
uint8_t rfcomm_scid[2]; // L2CAP source CID for RFCOMM
|
||||
uint8_t rfcomm_dcid[2]; // 0x0051
|
||||
uint8_t identifier; // Identifier for command
|
||||
|
||||
/* RFCOMM Variables */
|
||||
uint8_t rfcommChannel;
|
||||
uint8_t rfcommChannelConnection; // This is the channel the SPP channel will be running at
|
||||
uint8_t rfcommDirection;
|
||||
uint8_t rfcommCommandResponse;
|
||||
uint8_t rfcommChannelType;
|
||||
uint8_t rfcommPfBit;
|
||||
|
||||
bool creditSent;
|
||||
|
||||
uint8_t rfcommDataBuffer[100]; // Create a 100 sized buffer for incoming data
|
||||
uint8_t sppOutputBuffer[100]; // Create a 100 sized buffer for outgoing SPP data
|
||||
uint8_t sppIndex;
|
||||
uint8_t rfcommAvailable;
|
||||
|
||||
uint8_t bytesRead; // Counter to see when it's time to send more credit
|
||||
|
||||
virtual void SDP_Command(uint8_t *data, uint8_t nbytes) = 0; // Used for SDP commands
|
||||
|
||||
/* RFCOMM Commands */
|
||||
virtual void RFCOMM_Command(uint8_t *data, uint8_t nbytes) = 0; // Used for RFCOMM commands
|
||||
void sendRfcomm(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t *data, uint8_t length);
|
||||
void sendRfcommCredit(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t credit);
|
||||
uint8_t calcFcs(uint8_t *data);
|
||||
bool checkFcs(uint8_t *data, uint8_t fcs);
|
||||
uint8_t crc(uint8_t *data);
|
||||
};
|
||||
|
||||
#endif
|
687
SPPi.cpp
Executable file
687
SPPi.cpp
Executable file
|
@ -0,0 +1,687 @@
|
|||
/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
|
||||
|
||||
This software may be distributed and modified under the terms of the GNU
|
||||
General Public License version 2 (GPL2) as published by the Free Software
|
||||
Foundation and appearing in the file GPL2.TXT included in the packaging of
|
||||
this file. Please note that GPL2 Section 2[b] requires that all works based
|
||||
on this software must also be made publicly available under the terms of
|
||||
the GPL2 ("Copyleft").
|
||||
|
||||
Contact information
|
||||
-------------------
|
||||
|
||||
Kristian Lauszus, TKJ Electronics
|
||||
Web : http://www.tkjelectronics.com
|
||||
e-mail : kristianl@tkjelectronics.com
|
||||
|
||||
Enhanced by Dmitry Pakhomenko to initiate connection with remote SPP-aware device
|
||||
04.04.2014, Magictale Electronics
|
||||
*/
|
||||
|
||||
|
||||
#include "SPPi.h"
|
||||
// To enable serial debugging see "settings.h"
|
||||
//#define EXTRADEBUG // Uncomment to get even more debugging data
|
||||
//#define PRINTREPORT // Uncomment to print the report sent to the Arduino
|
||||
|
||||
/*
|
||||
* "RFCOMM UUID" signature, a channel number comes immediately after it
|
||||
*/
|
||||
const uint8_t rfcomm_uuid_sign[6] PROGMEM = { 0x35, 0x05, 0x19, 0x00, 0x03, 0x08 };
|
||||
|
||||
SPPi::SPPi(BTD *p, const char* name, const char* pin, bool pair, uint8_t *addr) :
|
||||
SPPBase(p)
|
||||
{
|
||||
if (pBtd)
|
||||
pBtd->registerServiceClass(this); // Register it as a Bluetooth service
|
||||
|
||||
pBtd->btdName = name;
|
||||
pBtd->btdPin = pin;
|
||||
|
||||
if (addr) // Make sure address is set
|
||||
pBtd->pairWithOtherDevice = pair;
|
||||
|
||||
for (uint8_t i = 0; i < 6; i++)
|
||||
pBtd->remote_bdaddr[i] = addr[i];
|
||||
|
||||
/* Set device cid for the SDP and RFCOMM channels */
|
||||
sdp_scid[0] = 0x50; // 0x0050
|
||||
sdp_scid[1] = 0x00;
|
||||
|
||||
rfcomm_scid[0] = 0x51; // 0x0051
|
||||
rfcomm_scid[1] = 0x00;
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
void SPPi::Reset() {
|
||||
connected = false;
|
||||
RFCOMMConnected = false;
|
||||
SDPConnected = false;
|
||||
l2cap_sdp_state = L2CAP_SDP_WAIT;
|
||||
l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT;
|
||||
sppIndex = 0;
|
||||
|
||||
rfcomm_uuid_sign_idx = 0;
|
||||
rfcomm_found = false;
|
||||
}
|
||||
|
||||
void SPPi::ACLData(uint8_t *l2capinbuf) {
|
||||
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nIncoming Packet: "), 0x80);
|
||||
|
||||
for (uint8_t i = 0; i < (l2capinbuf[2] + 4); i++) {
|
||||
D_PrintHex<uint8_t > (l2capinbuf[i], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
}
|
||||
Notify(PSTR("\r\n"), 0x80);
|
||||
#endif
|
||||
|
||||
//if((l2capinbuf[0] | (uint16_t)l2capinbuf[1] << 8) == (hci_handle | 0x2000U)) { // acl_handle_ok
|
||||
if(UHS_ACL_HANDLE_OK(l2capinbuf, hci_handle)) { // acl_handle_ok
|
||||
if((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001U) { // l2cap_control - Channel ID for ACL-U
|
||||
if (l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nL2CAP Command Rejected - Reason: "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);
|
||||
Notify(PSTR(" Data: "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[17], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[16], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);
|
||||
#endif
|
||||
}else if (l2capinbuf[8] == L2CAP_CMD_CONNECTION_RESPONSE) {
|
||||
if (((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) && ((l2capinbuf[18] | (l2capinbuf[19] << 8)) == SUCCESSFUL)) { // Success
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nL2CAP Connection Response"), 0x80);
|
||||
#endif
|
||||
if (l2capinbuf[14] == sdp_scid[0] && l2capinbuf[15] == sdp_scid[1]) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nSDP Connection Response"), 0x80);
|
||||
#endif
|
||||
sdp_dcid[0] = l2capinbuf[12];
|
||||
sdp_dcid[1] = l2capinbuf[13];
|
||||
|
||||
identifier++;
|
||||
l2cap_sdp_state = L2CAP_SDP_CONN_RESPONSE;
|
||||
} else if (l2capinbuf[14] == rfcomm_scid[0] && l2capinbuf[15] == rfcomm_scid[1]) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nRFComm Connection Response"), 0x80);
|
||||
#endif
|
||||
rfcomm_dcid[0] = l2capinbuf[12];
|
||||
rfcomm_dcid[1] = l2capinbuf[13];
|
||||
|
||||
identifier++;
|
||||
l2cap_rfcomm_state = L2CAP_RFCOMM_CONN_RESPONSE;
|
||||
}
|
||||
}
|
||||
} else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) {
|
||||
if((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success
|
||||
if(l2capinbuf[12] == sdp_scid[0] && l2capinbuf[13] == sdp_scid[1]) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nSDP Configuration Response Received"), 0x80);
|
||||
Notify(PSTR("\r\nSDP Successfully Configured"), 0x80);
|
||||
#endif
|
||||
l2cap_sdp_state = L2CAP_SDP_SERVICE_SEARCH_ATTR1;
|
||||
identifier++;
|
||||
SDP_Service_Search_Attr(0, identifier);
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nSDP Service Search Attribute Request 1 Sent"), 0x80);
|
||||
#endif
|
||||
} else if(l2capinbuf[12] == rfcomm_scid[0] && l2capinbuf[13] == rfcomm_scid[1]) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nRFComm Configuration Response Received"), 0x80);
|
||||
Notify(PSTR("\r\nRFComm Successfully Configured"), 0x80);
|
||||
#endif
|
||||
l2cap_sdp_state = L2CAP_RFCOMM_DONE;
|
||||
identifier++;
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nRFComm SABM Sent"), 0x80);
|
||||
#endif
|
||||
rfcommAvailable = 0; // Reset number of bytes available
|
||||
bytesRead = 0; // Reset number of bytes received
|
||||
RFCOMMConnected = true;
|
||||
|
||||
// channel direction, CR,channelType, pfBit, data, length
|
||||
sendRfcomm(0, 0, (1 << 1), RFCOMM_SABM, (1 << 4), rfcommbuf, 0);
|
||||
}
|
||||
}
|
||||
} else if (l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) {
|
||||
if (l2capinbuf[12] == sdp_scid[0] && l2capinbuf[13] == sdp_scid[1]) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nSDP Configuration Request Received"), 0x80);
|
||||
#endif
|
||||
identifier = l2capinbuf[9];
|
||||
pBtd->l2cap_config_response(hci_handle, identifier, sdp_dcid);
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nSDP Configuration Response Sent"), 0x80);
|
||||
#endif
|
||||
l2cap_sdp_state = L2CAP_SDP_CONFIG_REQUEST;
|
||||
identifier++;
|
||||
delay(1);
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nSDP Configuration Request Sent"), 0x80);
|
||||
#endif
|
||||
pBtd->l2cap_config_request(hci_handle, identifier, sdp_dcid);
|
||||
|
||||
}else if (l2capinbuf[12] == rfcomm_scid[0] && l2capinbuf[13] == rfcomm_scid[1]) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nRFComm Configuration Request Received"), 0x80);
|
||||
#endif
|
||||
identifier = l2capinbuf[9];
|
||||
|
||||
pBtd->l2cap_config_response(hci_handle, identifier, rfcomm_dcid);
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nRFComm Configuration Response Sent"), 0x80);
|
||||
#endif
|
||||
l2cap_rfcomm_state = L2CAP_RFCOMM_CONFIG_REQUEST;
|
||||
identifier++;
|
||||
delay(1);
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nRFComm Configuration Request Sent"), 0x80);
|
||||
#endif
|
||||
pBtd->l2cap_config_request(hci_handle, identifier, rfcomm_dcid);
|
||||
}
|
||||
|
||||
} else if (l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) {
|
||||
if (l2capinbuf[12] == sdp_scid[0] && l2capinbuf[13] == sdp_scid[1]) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nDisconnect Request: SDP Channel"), 0x80);
|
||||
#endif
|
||||
identifier = l2capinbuf[9];
|
||||
|
||||
SDPConnected = false;
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nDisconnected SDP Channel"), 0x80);
|
||||
#endif
|
||||
pBtd->l2cap_disconnection_response(hci_handle, identifier, sdp_scid, sdp_dcid);
|
||||
l2cap_sdp_state = L2CAP_SDP_WAIT;
|
||||
|
||||
}else if (l2capinbuf[12] == rfcomm_scid[0] && l2capinbuf[13] == rfcomm_scid[1]) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nDisconnect Request: RFComm Channel"), 0x80);
|
||||
#endif
|
||||
identifier = l2capinbuf[9];
|
||||
|
||||
RFCOMMConnected = false;
|
||||
connected = false;
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nDisconnected RFComm Channel"), 0x80);
|
||||
#endif
|
||||
pBtd->l2cap_disconnection_response(hci_handle, identifier, rfcomm_dcid, rfcomm_scid);
|
||||
l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT;
|
||||
|
||||
}
|
||||
} else if (l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) {
|
||||
if (l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nDisconnect Response: SDP Channel"), 0x80);
|
||||
#endif
|
||||
SDPConnected = false;
|
||||
l2cap_sdp_state = L2CAP_SDP_WAIT;
|
||||
|
||||
}else if (l2capinbuf[12] == rfcomm_dcid[0] && l2capinbuf[13] == rfcomm_dcid[1]) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nDisconnect Response: RFComm Channel"), 0x80);
|
||||
Notify(PSTR("\r\nDisconnected L2CAP Connection"), 0x80);
|
||||
#endif
|
||||
RFCOMMConnected = false;
|
||||
|
||||
pBtd->hci_disconnect(hci_handle);
|
||||
hci_handle = -1; // Reset handle
|
||||
|
||||
l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT;
|
||||
}
|
||||
|
||||
} else if (l2capinbuf[8] == L2CAP_CMD_INFORMATION_REQUEST) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nInformation request"), 0x80);
|
||||
#endif
|
||||
identifier = l2capinbuf[9];
|
||||
pBtd->l2cap_information_response(hci_handle, identifier, l2capinbuf[12], l2capinbuf[13]);
|
||||
}
|
||||
else {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nL2CAP Unknown Signaling Command: "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[8], 0x80);
|
||||
#endif
|
||||
}
|
||||
} else if (l2capinbuf[6] == sdp_scid[0] && l2capinbuf[7] == sdp_scid[1]) { // SDP
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nSDP data:"), 0x80);
|
||||
#endif
|
||||
if (l2capinbuf[8] == SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU) {
|
||||
|
||||
if (l2cap_sdp_state == L2CAP_SDP_SERVICE_SEARCH_ATTR1){
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR(" - SDP Service Search Attribute Response 1: "), 0x80);
|
||||
#endif
|
||||
remainingBytes = l2capinbuf[l2capinbuf[2] + 3];
|
||||
|
||||
parseAttrReply(l2capinbuf);
|
||||
|
||||
l2cap_sdp_state = L2CAP_SDP_SERVICE_SEARCH_ATTR2;
|
||||
identifier++;
|
||||
if (remainingBytes) {
|
||||
SDP_Service_Search_Attr(0, identifier, remainingBytes);
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nSDP Service Search Attribute Request 2 Sent"), 0x80);
|
||||
#endif
|
||||
} else {
|
||||
l2cap_sdp_state = L2CAP_SDP_DONE;
|
||||
|
||||
if (rfcomm_found) {
|
||||
pBtd->l2cap_connection_request(hci_handle, identifier, rfcomm_scid, RFCOMM_PSM);
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nRFComm Connection Request Sent"), 0x80);
|
||||
#endif
|
||||
l2cap_rfcomm_state = L2CAP_RFCOMM_REQUEST;
|
||||
} else{
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nRFComm Channel Number not found"), 0x80);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
} else if (l2cap_sdp_state == L2CAP_SDP_SERVICE_SEARCH_ATTR2){
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR(" - SDP Service Search Attribute Response 2: "), 0x80);
|
||||
#endif
|
||||
|
||||
parseAttrReply(l2capinbuf);
|
||||
|
||||
l2cap_sdp_state = L2CAP_SDP_DONE;
|
||||
identifier++;
|
||||
|
||||
if (rfcomm_found) {
|
||||
pBtd->l2cap_connection_request(hci_handle, identifier, rfcomm_scid, RFCOMM_PSM);
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nRFComm Connection Request Sent"), 0x80);
|
||||
#endif
|
||||
l2cap_rfcomm_state = L2CAP_RFCOMM_REQUEST;
|
||||
}else{
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nRFComm Channel Number not found"), 0x80);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nUnknown PDU: "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[8], 0x80);
|
||||
#endif
|
||||
}
|
||||
|
||||
} else if (l2capinbuf[6] == rfcomm_scid[0] && l2capinbuf[7] == rfcomm_scid[1]) { // RFCOMM
|
||||
|
||||
rfcommChannel = l2capinbuf[8] & 0xF8;
|
||||
rfcommDirection = l2capinbuf[8] & 0x04;
|
||||
rfcommCommandResponse = l2capinbuf[8] & 0x02;
|
||||
rfcommChannelType = l2capinbuf[9] & 0xEF;
|
||||
rfcommPfBit = l2capinbuf[9] & 0x10;
|
||||
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nRFComm Channel: "), 0x80);
|
||||
D_PrintHex<uint8_t > (rfcommChannel >> 3, 0x80);
|
||||
Notify(PSTR(" Direction: "), 0x80);
|
||||
D_PrintHex<uint8_t > (rfcommDirection >> 2, 0x80);
|
||||
Notify(PSTR(" CommandResponse: "), 0x80);
|
||||
D_PrintHex<uint8_t > (rfcommCommandResponse >> 1, 0x80);
|
||||
Notify(PSTR(" ChannelType: "), 0x80);
|
||||
D_PrintHex<uint8_t > (rfcommChannelType, 0x80);
|
||||
Notify(PSTR(" PF_BIT: "), 0x80);
|
||||
D_PrintHex<uint8_t > (rfcommPfBit >> 4, 0x80);
|
||||
#endif
|
||||
if (rfcommChannelType == RFCOMM_DISC) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nReceived Disconnect RFComm Command on channel: "), 0x80);
|
||||
D_PrintHex<uint8_t > (rfcommChannel, 0x80);
|
||||
#endif
|
||||
connected = false;
|
||||
// sendRfcomm(rfcommChannel, rfcommDirection, rfcommCommandResponse, RFCOMM_UA, rfcommPfBit, rfcommbuf, 0x00); // UA Command
|
||||
}
|
||||
|
||||
if (connected)
|
||||
{
|
||||
/* Read the incoming message */
|
||||
if (rfcommChannelType == RFCOMM_UIH && rfcommChannel == (rfcommChannelConnection << 3)) {
|
||||
uint8_t length = l2capinbuf[10] >> 1; // Get length
|
||||
uint8_t offset = l2capinbuf[4] - length - 4; // Check if there is credit
|
||||
if (checkFcs(&l2capinbuf[8], l2capinbuf[11 + length + offset])) {
|
||||
uint8_t i = 0;
|
||||
for (; i < length; i++) {
|
||||
if (rfcommAvailable + i >= sizeof (rfcommDataBuffer)) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nWarning: Buffer is full!"), 0x80);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
rfcommDataBuffer[rfcommAvailable + i] = l2capinbuf[11 + i + offset];
|
||||
}
|
||||
rfcommAvailable += i;
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nRFCOMM Data Available: "), 0x80);
|
||||
Notify(rfcommAvailable, 0x80);
|
||||
if (offset) {
|
||||
Notify(PSTR(" - Credit: 0x"), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[11], 0x80);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifdef DEBUG_USB_HOST
|
||||
else
|
||||
Notify(PSTR("\r\nError in FCS checksum!"), 0x80);
|
||||
#endif
|
||||
#ifdef PRINTREPORT // Uncomment "#define PRINTREPORT" to print the report send to the Arduino via Bluetooth
|
||||
for (uint8_t i = 0; i < length; i++)
|
||||
Notifyc(l2capinbuf[i + 11 + offset], 0x80);
|
||||
#endif
|
||||
} else if (rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_RPN_CMD) { // UIH Remote Port Negotiation Command
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nReceived UIH Remote Port Negotiation Command"), 0x80);
|
||||
#endif
|
||||
/* rfcommbuf[0] = BT_RFCOMM_RPN_RSP; // Command
|
||||
rfcommbuf[1] = l2capinbuf[12]; // Length and shiftet like so: length << 1 | 1
|
||||
rfcommbuf[2] = l2capinbuf[13]; // Channel: channel << 1 | 1
|
||||
rfcommbuf[3] = l2capinbuf[14]; // Pre difined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM
|
||||
rfcommbuf[4] = l2capinbuf[15]; // Priority
|
||||
rfcommbuf[5] = l2capinbuf[16]; // Timer
|
||||
rfcommbuf[6] = l2capinbuf[17]; // Max Fram Size LSB
|
||||
rfcommbuf[7] = l2capinbuf[18]; // Max Fram Size MSB
|
||||
rfcommbuf[8] = l2capinbuf[19]; // MaxRatransm.
|
||||
rfcommbuf[9] = l2capinbuf[20]; // Number of Frames
|
||||
sendRfcomm(rfcommChannel, rfcommDirection, 0, RFCOMM_UIH, rfcommPfBit, rfcommbuf, 0x0A); // UIH Remote Port Negotiation Response*/
|
||||
} else if (rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_MSC_CMD) { // UIH Modem Status Command
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nSend UIH Modem Status Response"), 0x80);
|
||||
#endif
|
||||
/*rfcommbuf[0] = BT_RFCOMM_MSC_RSP; // UIH Modem Status Response
|
||||
rfcommbuf[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1
|
||||
rfcommbuf[2] = l2capinbuf[13]; // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3)
|
||||
rfcommbuf[3] = l2capinbuf[14];
|
||||
sendRfcomm(rfcommChannel, rfcommDirection, 0, RFCOMM_UIH, rfcommPfBit, rfcommbuf, 0x04);*/
|
||||
}
|
||||
}else{
|
||||
|
||||
if (rfcommChannelType == RFCOMM_SABM) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nReceived SAMB RFComm Packet on channel: "), 0x80);
|
||||
D_PrintHex<uint8_t > (rfcommChannel >> 3, 0x80);
|
||||
#endif
|
||||
} else if (rfcommChannelType == RFCOMM_UA) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nReceived UA RFComm Packet on channel: "), 0x80);
|
||||
D_PrintHex<uint8_t > (rfcommChannel >> 3, 0x80);
|
||||
#endif
|
||||
if ((rfcommChannel >> 3) == 0)
|
||||
{
|
||||
//Reply on 1-st SAMB command on channel 0
|
||||
rfcommbuf[0] = BT_RFCOMM_PN_CMD; // UIH Parameter Negotiation Request
|
||||
rfcommbuf[1] = ((8 << 1) | 1); // Length and shiftet like so: length << 1 | 1
|
||||
rfcommbuf[2] = ((0x10<< 1)); // Channel: channel << 1 | 1 TODO: replace channel with variable
|
||||
rfcommbuf[3] = 0xE0; // Pre defined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM
|
||||
rfcommbuf[4] = 0x00; // Priority
|
||||
rfcommbuf[5] = 0x00; // Timer
|
||||
rfcommbuf[6] = BULK_MAXPKTSIZE - 14; // Max Fram Size LSB - set to the size of received data (50)
|
||||
rfcommbuf[7] = 0x00; // Max Fram Size MSB
|
||||
rfcommbuf[8] = 0x00; // MaxRetransm.
|
||||
rfcommbuf[9] = 0x00; // Number of Frames
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nSent UAH RFComm Cmd BT_RFCOMM_PN_CMD (Parameter Negotiation Request) on channel: "), 0x80);
|
||||
D_PrintHex<uint8_t > (rfcommChannel >> 3, 0x80);
|
||||
#endif
|
||||
// channel direction, CR,channelType,pfBit, data, length
|
||||
sendRfcomm(0, 0, (1 << 1), RFCOMM_UIH, 0, rfcommbuf, 0x0A);
|
||||
}else{
|
||||
//Reply on 2-nd SAMB command on channel 0
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nSend UIH RFComm Cmd BT_RFCOMM_MSC_CMD (Modem Status Command)"), 0x80);
|
||||
#endif
|
||||
rfcommbuf[0] = BT_RFCOMM_MSC_CMD; // UIH Modem Status Command
|
||||
rfcommbuf[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1
|
||||
rfcommbuf[2] = (1 << 0) | (1 << 1) | (0 << 2) | (rfcommChannelConnection << 3); //0x83 // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3)
|
||||
rfcommbuf[3] = 0x8D; // Can receive frames (YES), Ready to Communicate (YES), Ready to Receive (YES), Incomig Call (NO), Data is Value (YES)
|
||||
|
||||
// channel direction, CR,channelType,pfBit, data, length
|
||||
sendRfcomm(0, 0, (1 << 1), RFCOMM_UIH, 0, rfcommbuf, 0x04);
|
||||
}
|
||||
} else if (rfcommChannelType == RFCOMM_UIH) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nReceived UIH RFComm Packet on channel: "), 0x80);
|
||||
D_PrintHex<uint8_t > (rfcommChannel >> 3, 0x80);
|
||||
#endif
|
||||
if (l2capinbuf[11] == BT_RFCOMM_PN_RSP)
|
||||
{
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR(" - BT_RFCOMM_PN_RSP (Parameter Negotiation Response)"), 0x80);
|
||||
Notify(PSTR("\r\nRFComm 2-nd SABM Sent"), 0x80);
|
||||
#endif
|
||||
// channel direction, CR,channelType, pfBit, data, length
|
||||
sendRfcomm((rfcommChannelConnection << 3), 0, (1 << 1), RFCOMM_SABM, (1 << 4), rfcommbuf, 0);
|
||||
}else if (l2capinbuf[11] == BT_RFCOMM_MSC_CMD)
|
||||
{
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR(" - BT_RFCOMM_MSC_CMD (Modem Status Cmd)"), 0x80);
|
||||
#endif
|
||||
rfcommbuf[0] = BT_RFCOMM_MSC_RSP; // UIH Modem Status Command
|
||||
rfcommbuf[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1
|
||||
rfcommbuf[2] = (1 << 0) | (1 << 1) | (0 << 2) | (rfcommChannelConnection << 3); //0x83 // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3)
|
||||
rfcommbuf[3] = 0x8D; // Can receive frames (YES), Ready to Communicate (YES), Ready to Receive (YES), Incomig Call (NO), Data is Value (YES)
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nSend UIH RFComm Cmd BT_RFCOMM_MSC_RSP (Modem Status Response)"), 0x80);
|
||||
#endif
|
||||
// channel direction, CR,channelType,pfBit, data, length
|
||||
sendRfcomm(0, 0, (1 << 1), RFCOMM_UIH, 0, rfcommbuf, 0x04);
|
||||
}else if (l2capinbuf[11] == BT_RFCOMM_MSC_RSP)
|
||||
{
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR(" - BT_RFCOMM_MSC_RSP (Modem Status Response)"), 0x80);
|
||||
Notify(PSTR("\r\nRFComm Cmd with Credit Sent"), 0x80);
|
||||
#endif
|
||||
sendRfcommCredit((0x10 << 3), 0, (1 << 1), RFCOMM_UIH, 0x10,
|
||||
sizeof (rfcommDataBuffer)); // Send credit
|
||||
|
||||
connected = true; // The RFCOMM channel is now established
|
||||
sppIndex = 0;
|
||||
}else if (l2capinbuf[11] == BT_RFCOMM_RPN_CMD)
|
||||
{
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR(" - BT_RFCOMM_RPN_CMD (Remote Port Negotiation Cmd)"), 0x80);
|
||||
#endif
|
||||
//Just a stub. This command is optional according to the spec
|
||||
}else if (l2capinbuf[11] == BT_RFCOMM_RPN_RSP)
|
||||
{
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR(" - BT_RFCOMM_RPN_RSP (Remote Port Negotiation Response)"), 0x80);
|
||||
#endif
|
||||
//Just a stub. This command is optional according to the spec
|
||||
}else
|
||||
{
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR(" - Unknown Response: "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[11], 0x80);
|
||||
#endif
|
||||
}
|
||||
} else if (rfcommChannelType == RFCOMM_DM) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nReceived DM RFComm Command on channel: "), 0x80);
|
||||
D_PrintHex<uint8_t > (rfcommChannel >> 3, 0x80);
|
||||
#endif
|
||||
} else {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nUnknown Response, rfCommChannelType: "), 0x80);
|
||||
D_PrintHex<uint8_t > (rfcommChannelType, 0x80);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nUnsupported L2CAP Data - Channel ID: "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[7], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[6], 0x80);
|
||||
Notify(PSTR("\r\n"), 0x80);
|
||||
|
||||
for (uint8_t i = 0; i < BULK_MAXPKTSIZE; i++) {
|
||||
D_PrintHex<uint8_t > (l2capinbuf[i], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}else{
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nBad ACL handle: "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[0], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
D_PrintHex<uint8_t > (l2capinbuf[1], 0x80);
|
||||
Notify(PSTR(" "), 0x80);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void SPPi::Run() {
|
||||
if (pBtd->pairWithOtherDevice){
|
||||
if (l2cap_sdp_state == L2CAP_SDP_WAIT) {
|
||||
if (pBtd->connectToOtherDevice && !pBtd->l2capConnectionClaimed && !connected) {
|
||||
pBtd->l2capConnectionClaimed = true;
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nSDP Connection Request Sent"), 0x80);
|
||||
#endif
|
||||
hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection
|
||||
identifier = 0;
|
||||
|
||||
pBtd->l2cap_connection_request(hci_handle, identifier, sdp_scid, SDP_PSM);
|
||||
l2cap_sdp_state = L2CAP_SDP_REQUEST;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************/
|
||||
/* SDP Commands */
|
||||
/************************************************************/
|
||||
void SPPi::SDP_Command(uint8_t *data, uint8_t nbytes) { // See page 223 in the Bluetooth specs
|
||||
pBtd->L2CAP_Command(hci_handle, data, nbytes, sdp_dcid[0], sdp_dcid[1]);
|
||||
}
|
||||
|
||||
void SPPi::SDP_Service_Search_Attr(uint8_t transactionIDHigh, uint8_t transactionIDLow, uint8_t remainingLen) {
|
||||
l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU;
|
||||
l2capoutbuf[1] = transactionIDHigh;
|
||||
l2capoutbuf[2] = transactionIDLow;
|
||||
|
||||
l2capoutbuf[3] = 0x00; // Parameter Length
|
||||
if (remainingLen == 0)
|
||||
l2capoutbuf[4] = 0x0F; // Parameter Length
|
||||
else
|
||||
l2capoutbuf[4] = 0x11; // Parameter Length
|
||||
|
||||
l2capoutbuf[5] = 0x35; // Data Element Sequence, data size in next 8 bits
|
||||
l2capoutbuf[6] = 0x03; // Data size
|
||||
|
||||
l2capoutbuf[7] = 0x19; // 00011 001 UUID, 2 bytes
|
||||
l2capoutbuf[8] = 0x01;
|
||||
l2capoutbuf[9] = 0x00; // 0x0100 - L2CAP_UUID
|
||||
|
||||
l2capoutbuf[10] = 0x00;
|
||||
l2capoutbuf[11] = 0x26;// 0x0026 Maximum attribute byte count
|
||||
|
||||
l2capoutbuf[12] = 0x35;// Data Element Sequence, data size in next 8 bits
|
||||
l2capoutbuf[13] = 0x05;// Data size
|
||||
|
||||
l2capoutbuf[14] = 0x0A;// 00001 010 unsigned int, 4 bytes - Attribute ID range
|
||||
|
||||
l2capoutbuf[15] = 0x00;
|
||||
l2capoutbuf[16] = 0x00;// range from 0x0000...
|
||||
|
||||
l2capoutbuf[17] = 0xFF;
|
||||
l2capoutbuf[18] = 0xFF;// ... to 0xFFFF
|
||||
|
||||
if (remainingLen == 0) {
|
||||
l2capoutbuf[19] = 0x00;// No more data
|
||||
SDP_Command(l2capoutbuf, 20);
|
||||
} else{
|
||||
l2capoutbuf[19] = 0x02;
|
||||
l2capoutbuf[20] = 0x0; //will be 0 anyway
|
||||
l2capoutbuf[21] = remainingLen;
|
||||
SDP_Command(l2capoutbuf, 22);
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************/
|
||||
/* RFCOMM Commands */
|
||||
/************************************************************/
|
||||
void SPPi::RFCOMM_Command(uint8_t* data, uint8_t nbytes) {
|
||||
pBtd->L2CAP_Command(hci_handle, data, nbytes, rfcomm_dcid[0], rfcomm_dcid[1]);
|
||||
}
|
||||
|
||||
void SPPi::parseAttrReply(uint8_t *l2capinbuf) {
|
||||
if ((l2capinbuf[2] + 4) < 15) return; // Sanity check
|
||||
|
||||
if (rfcomm_found) {
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nChannel is already found"), 0x80);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (rfcomm_uuid_sign_idx == sizeof(rfcomm_uuid_sign)) {
|
||||
// Signature has been already found but channel is in next packet
|
||||
rfcommChannelConnection = l2capinbuf[15];
|
||||
rfcomm_found = true;
|
||||
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nChannel found in second packet: "), 0x80);
|
||||
Notify((uint8_t)rfcommChannelConnection, 0x80);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t nextSignBt;
|
||||
for (uint8_t i = 15; i < (l2capinbuf[2] + 4); i++) { // Searching through packet payload only
|
||||
// Keep searching for signature
|
||||
if (l2capinbuf[i] == pgm_read_byte(&rfcomm_uuid_sign[rfcomm_uuid_sign_idx])) {
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nFound match @"), 0x80);
|
||||
Notify((uint8_t)i, 0x80);
|
||||
#endif
|
||||
rfcomm_uuid_sign_idx++;
|
||||
if (rfcomm_uuid_sign_idx == sizeof(rfcomm_uuid_sign)) {
|
||||
// Signature found, trying to get channel number
|
||||
if (l2capinbuf[i + 1] == 0x2) {
|
||||
// Is the byte we are looking at the second last in the packet?
|
||||
if ((l2capinbuf[2] + 1) == (i + 1)) {
|
||||
// Oh well, channel number didn't fit in this packet, waiting for next
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nChannel will be in the next packet"), 0x80);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
rfcommChannelConnection = l2capinbuf[i + 1];
|
||||
rfcomm_found = true;
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nChannel found in first packet: "), 0x80);
|
||||
D_PrintHex<uint8_t > (rfcommChannelConnection, 0x80);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
} else if ((l2capinbuf[i] == 0x2) && (i == l2capinbuf[2] + 1)) {
|
||||
// This is an indication of packet end with more data to come in next packet - do not reset rfcomm_uuid_sign_idx, we will continue when next packet arrives
|
||||
#ifdef DEBUG_USB_HOST
|
||||
Notify(PSTR("\r\nEnd of packet reached, no full signature yet"), 0x80);
|
||||
#endif
|
||||
return;
|
||||
} else {
|
||||
// Otherwise the signature is not found - start over again
|
||||
rfcomm_uuid_sign_idx = 0;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
89
SPPi.h
Executable file
89
SPPi.h
Executable file
|
@ -0,0 +1,89 @@
|
|||
/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
|
||||
|
||||
This software may be distributed and modified under the terms of the GNU
|
||||
General Public License version 2 (GPL2) as published by the Free Software
|
||||
Foundation and appearing in the file GPL2.TXT included in the packaging of
|
||||
this file. Please note that GPL2 Section 2[b] requires that all works based
|
||||
on this software must also be made publicly available under the terms of
|
||||
the GPL2 ("Copyleft").
|
||||
|
||||
Contact information
|
||||
-------------------
|
||||
|
||||
Kristian Lauszus, TKJ Electronics
|
||||
Web : http://www.tkjelectronics.com
|
||||
e-mail : kristianl@tkjelectronics.com
|
||||
|
||||
Enhanced by Dmitry Pakhomenko to initiate connection with remote SPP-aware device
|
||||
04.04.2014, Magictale Electronics
|
||||
*/
|
||||
|
||||
#ifndef _sppi_h_
|
||||
#define _sppi_h_
|
||||
|
||||
#include "SPPBase.h"
|
||||
|
||||
/* Bluetooth L2CAP states for SDP_task() */
|
||||
#define L2CAP_SDP_WAIT 0
|
||||
#define L2CAP_SDP_REQUEST 1
|
||||
#define L2CAP_SDP_DONE 2
|
||||
#define L2CAP_DISCONNECT_RESPONSE 3
|
||||
#define L2CAP_SDP_CONN_RESPONSE 4
|
||||
#define L2CAP_SDP_CONFIG_REQUEST 5
|
||||
#define L2CAP_SDP_SERVICE_SEARCH_ATTR1 6
|
||||
#define L2CAP_SDP_SERVICE_SEARCH_ATTR2 7
|
||||
|
||||
/* Bluetooth L2CAP states for RFCOMM_task() */
|
||||
#define L2CAP_RFCOMM_WAIT 0
|
||||
#define L2CAP_RFCOMM_REQUEST 1
|
||||
#define L2CAP_RFCOMM_DONE 3
|
||||
#define L2CAP_RFCOMM_CONN_RESPONSE 4
|
||||
#define L2CAP_RFCOMM_CONFIG_REQUEST 6
|
||||
#define L2CAP_RFCOMM_CONFIG_RESPONSE 7
|
||||
|
||||
/**
|
||||
* This BluetoothService class a Serial Port Protocol (SPP) client.
|
||||
* It inherits the Arduino Stream class. This allows it to use all the standard Arduino print functions.
|
||||
*/
|
||||
class SPPi : public SPPBase {
|
||||
public:
|
||||
/**
|
||||
* Constructor for the SPPi class.
|
||||
* @param p Pointer to BTD class instance.
|
||||
* @param name Set the name to BTD#btdName. If argument is omitted, then "Arduino" will be used.
|
||||
* @param pin Write the pin to BTD#btdPin. If argument is omitted, then "0000" will be used.
|
||||
* @param pair Set this to true if you want to pair with a device.
|
||||
* @param addr Set this to the address you want to connect to.
|
||||
*/
|
||||
SPPi(BTD *p, const char *name = "Arduino", const char *pin = "0000", bool pair = false, uint8_t *addr = NULL);
|
||||
|
||||
#if GCC_VERSION > 40700 // Test for GCC > 4.7.0
|
||||
SPPi(BTD *p, bool pair = false, uint8_t *addr = NULL) : SPPi(p, "Arduino", "0000", pair, addr) {}; // Use a delegating constructor
|
||||
#endif
|
||||
|
||||
/** @name SPPBase implementation */
|
||||
/**
|
||||
* Used to pass acldata to the services.
|
||||
* @param ACLData Incoming acldata.
|
||||
*/
|
||||
virtual void ACLData(uint8_t *ACLData);
|
||||
/** Used to establish the connection automatically. */
|
||||
virtual void Run();
|
||||
/** Use this to reset the service. */
|
||||
virtual void Reset();
|
||||
/**@}*/
|
||||
|
||||
private:
|
||||
uint8_t remainingBytes;
|
||||
uint8_t rfcomm_uuid_sign_idx; // A progressing index while searching for "RFCOMM UUID" signature, starts from 0 (nothing found) and ends with 5 (found full sequence)
|
||||
uint8_t rfcomm_found;
|
||||
|
||||
/* SDP Commands */
|
||||
virtual void SDP_Command(uint8_t *data, uint8_t nbytes);
|
||||
void SDP_Service_Search_Attr(uint8_t transactionIDHigh, uint8_t transactionIDLow, uint8_t remainingLen = 0);
|
||||
|
||||
/* RFCOMM Commands */
|
||||
virtual void RFCOMM_Command(uint8_t *data, uint8_t nbytes); // Used for RFCOMM commands
|
||||
void parseAttrReply(uint8_t *l2capinbuf);
|
||||
};
|
||||
#endif
|
2
UsbCore.h
Normal file → Executable file
2
UsbCore.h
Normal file → Executable file
|
@ -37,6 +37,8 @@ typedef MAX3421e<P9, P8> MAX3421E; // Teensy++ 1.0 and 2.0
|
|||
typedef MAX3421e<P53, P54> MAX3421E; // Arduino Mega ADK
|
||||
#elif defined(ARDUINO_AVR_BALANDUINO)
|
||||
typedef MAX3421e<P20, P19> MAX3421E; // Balanduino
|
||||
#elif defined(LUMINARDO)
|
||||
typedef MAX3421e<P4, P18> MAX3421E; // Luminardo
|
||||
#else
|
||||
typedef MAX3421e<P10, P9> MAX3421E; // Official Arduinos (UNO, Duemilanove, Mega, 2560, Leonardo, Due etc.) or Teensy 2.0 and 3.0
|
||||
#endif
|
||||
|
|
48
examples/Bluetooth/SPP/SPPClient/SPPClient.ino
Normal file
48
examples/Bluetooth/SPP/SPPClient/SPPClient.ino
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
Example sketch for the RFCOMM/SPP Client Bluetooth library - developed by Kristian Lauszus
|
||||
For more information visit my blog: http://blog.tkjelectronics.dk/ or
|
||||
send me an e-mail: kristianl@tkjelectronics.com
|
||||
*/
|
||||
|
||||
#include <SPPi.h>
|
||||
#include <usbhub.h>
|
||||
// Satisfy IDE, which only needs to see the include statment in the ino.
|
||||
#ifdef dobogusinclude
|
||||
#include <spi4teensy3.h>
|
||||
#endif
|
||||
|
||||
USB Usb;
|
||||
//USBHub Hub1(&Usb); // Some dongles have a hub inside
|
||||
|
||||
BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so
|
||||
|
||||
uint8_t addr[6] = { 0x71, 0xB4, 0xB0, 0xC8, 0xBC, 0xC8 }; // Set this to the Bluetooth address you want to connect to
|
||||
SPPi SerialBT(&Btd, true, addr);
|
||||
|
||||
boolean firstMessage = true;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
|
||||
if (Usb.Init() == -1) {
|
||||
Serial.println(F("OSC did not start"));
|
||||
while (1); // Halt
|
||||
}
|
||||
Serial.print(F("\r\nSPP Client Started"));
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Usb.Task(); // The SPP data is actually not send until this is called, one could call SerialBT.send() directly as well
|
||||
|
||||
if (SerialBT.connected) {
|
||||
if (firstMessage) {
|
||||
firstMessage = false;
|
||||
SerialBT.println(F("Hello from Arduino SPP client")); // Send welcome message
|
||||
}
|
||||
while (Serial.available())
|
||||
SerialBT.write(Serial.read());
|
||||
while (SerialBT.available())
|
||||
Serial.write(SerialBT.read());
|
||||
} else
|
||||
firstMessage = true;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Example sketch for the RFCOMM/SPP Bluetooth library - developed by Kristian Lauszus
|
||||
Example sketch for the RFCOMM/SPP Server Bluetooth library - developed by Kristian Lauszus
|
||||
For more information visit my blog: http://blog.tkjelectronics.dk/ or
|
||||
send me an e-mail: kristianl@tkjelectronics.com
|
||||
*/
|
||||
|
@ -15,6 +15,7 @@ USB Usb;
|
|||
//USBHub Hub1(&Usb); // Some dongles have a hub inside
|
||||
|
||||
BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so
|
||||
|
||||
/* You can create the instance of the class in two ways */
|
||||
SPP SerialBT(&Btd); // This will set the name to the defaults: "Arduino" and the pin to "0000"
|
||||
//SPP SerialBT(&Btd, "Lauszus's Arduino", "1234"); // You can also set the name and pin like so
|
||||
|
@ -26,23 +27,23 @@ void setup() {
|
|||
while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
|
||||
if (Usb.Init() == -1) {
|
||||
Serial.print(F("\r\nOSC did not start"));
|
||||
while (1); //halt
|
||||
while (1); // Halt
|
||||
}
|
||||
Serial.print(F("\r\nSPP Bluetooth Library Started"));
|
||||
Serial.print(F("\r\nSPP Server Started"));
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Usb.Task(); // The SPP data is actually not send until this is called, one could call SerialBT.send() directly as well
|
||||
|
||||
if (SerialBT.connected) {
|
||||
if (firstMessage) {
|
||||
firstMessage = false;
|
||||
SerialBT.println(F("Hello from Arduino")); // Send welcome message
|
||||
SerialBT.println(F("Hello from Arduino SPP server")); // Send welcome message
|
||||
}
|
||||
if (Serial.available())
|
||||
while (Serial.available())
|
||||
SerialBT.write(Serial.read());
|
||||
if (SerialBT.available())
|
||||
while (SerialBT.available())
|
||||
Serial.write(SerialBT.read());
|
||||
}
|
||||
else
|
||||
} else
|
||||
firstMessage = true;
|
||||
}
|
Loading…
Reference in a new issue