Initial commit for BTHID library

To see incoming data uncomment PRINTREPORT in BTHID.cpp
This commit is contained in:
Kristian Lauszus 2013-11-24 21:55:15 +01:00
parent 4512f0ee0c
commit f783b97cb9
8 changed files with 749 additions and 63 deletions

130
BTD.cpp
View file

@ -27,6 +27,8 @@ const uint8_t BTD::BTD_DATAOUT_PIPE = 3;
BTD::BTD(USB *p) :
connectToWii(false),
pairWithWii(false),
connectToHIDDevice(false),
pairWithHIDDevice(false),
pUsb(p), // Pointer to USB class instance - mandatory
bAddress(0), // Device address - mandatory
bNumEP(1), // If config descriptor needs to be parsed
@ -303,6 +305,8 @@ void BTD::clearAllVariables() {
connectToWii = false;
incomingWii = false;
connectToHIDDevice = false;
incomingHIDDevice = false;
bAddress = 0; // Clear device address
bNumEP = 1; // Must have to be reset to 1
qNextPollTime = 0; // Reset next poll time
@ -411,13 +415,18 @@ void BTD::HCI_event_task() {
break;
case EV_INQUIRY_COMPLETE:
if (inquiry_counter >= 5 && pairWithWii) {
if (inquiry_counter >= 5 && (pairWithWii || pairWithHIDDevice)) {
inquiry_counter = 0;
#ifdef DEBUG_USB_HOST
if (pairWithWii)
Notify(PSTR("\r\nCouldn't find Wiimote"), 0x80);
else
Notify(PSTR("\r\nCouldn't find HID device"), 0x80);
#endif
connectToWii = false;
pairWithWii = false;
connectToHIDDevice = false;
pairWithHIDDevice = false;
hci_state = HCI_SCANNING_STATE;
}
inquiry_counter++;
@ -430,28 +439,44 @@ void BTD::HCI_event_task() {
Notify(hcibuf[2], 0x80);
#endif
for (uint8_t i = 0; i < hcibuf[2]; i++) {
if ((hcibuf[4 + 8 * hcibuf[2] + 3 * i] == 0x04 && hcibuf[5 + 8 * hcibuf[2] + 3 * i] == 0x25 && hcibuf[6 + 8 * hcibuf[2] + 3 * i] == 0x00) || (hcibuf[4 + 8 * hcibuf[2] + 3 * i] == 0x08 && hcibuf[5 + 8 * hcibuf[2] + 3 * i] == 0x05 && hcibuf[6 + 8 * hcibuf[2] + 3 * i] == 0x00)) { // See http://bluetooth-pentest.narod.ru/software/bluetooth_class_of_device-service_generator.html and http://wiibrew.org/wiki/Wiimote#SDP_information
if (hcibuf[4 + 8 * hcibuf[2] + 3 * i] == 0x08) // Check if it's the new Wiimote with motion plus inside that was detected
uint8_t offset = 8 * hcibuf[2] + 3 * i;
uint8_t classOfDevice[3];
for (uint8_t j = 0; j < 3; j++)
classOfDevice[j] = hcibuf[j + 4 + offset];
if (pairWithWii && classOfDevice[2] == 0x00 && (classOfDevice[1] & 0x05) && (classOfDevice[0] & 0x0C)) { // See http://bluetooth-pentest.narod.ru/software/bluetooth_class_of_device-service_generator.html and http://wiibrew.org/wiki/Wiimote#SDP_information
if (classOfDevice[0] & 0x08) // Check if it's the new Wiimote with motion plus inside that was detected
motionPlusInside = true;
else
motionPlusInside = false;
disc_bdaddr[0] = hcibuf[3 + 6 * i];
disc_bdaddr[1] = hcibuf[4 + 6 * i];
disc_bdaddr[2] = hcibuf[5 + 6 * i];
disc_bdaddr[3] = hcibuf[6 + 6 * i];
disc_bdaddr[4] = hcibuf[7 + 6 * i];
disc_bdaddr[5] = hcibuf[8 + 6 * i];
hci_event_flag |= HCI_FLAG_WII_FOUND;
for (uint8_t j = 0; j < 6; j++)
disc_bdaddr[j] = hcibuf[j + 3 + 6 * i];
hci_event_flag |= HCI_FLAG_DEVICE_FOUND;
break;
} else if (pairWithHIDDevice && (classOfDevice[1] & 0x05) && (classOfDevice[0] & 0xC0)) { // Check if it is a mouse or keyboard
#ifdef DEBUG_USB_HOST
if (classOfDevice[0] & 0x80)
Notify(PSTR("\r\nMouse found"), 0x80);
if (classOfDevice[0] & 0x40)
Notify(PSTR("\r\nKeyboard found"), 0x80);
#endif
for (uint8_t j = 0; j < 6; j++)
disc_bdaddr[j] = hcibuf[j + 3 + 6 * i];
hci_event_flag |= HCI_FLAG_DEVICE_FOUND;
}
#ifdef EXTRADEBUG
else {
Notify(PSTR("\r\nClass of device: "), 0x80);
D_PrintHex<uint8_t > (hcibuf[6 + 8 * hcibuf[2] + 3 * i], 0x80);
D_PrintHex<uint8_t > (classOfDevice[2], 0x80);
Notify(PSTR(" "), 0x80);
D_PrintHex<uint8_t > (hcibuf[5 + 8 * hcibuf[2] + 3 * i], 0x80);
D_PrintHex<uint8_t > (classOfDevice[1], 0x80);
Notify(PSTR(" "), 0x80);
D_PrintHex<uint8_t > (hcibuf[4 + 8 * hcibuf[2] + 3 * i], 0x80);
D_PrintHex<uint8_t > (classOfDevice[0], 0x80);
}
#endif
}
@ -467,7 +492,7 @@ void BTD::HCI_event_task() {
hci_handle = hcibuf[3] | ((hcibuf[4] & 0x0F) << 8); // store the handle for the ACL connection
hci_event_flag |= HCI_FLAG_CONN_COMPLETE; // set connection complete flag
} else {
hci_state = HCI_CHECK_WII_SERVICE;
hci_state = HCI_CHECK_DEVICE_SERVICE;
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nConnection Failed: "), 0x80);
D_PrintHex<uint8_t > (hcibuf[2], 0x80);
@ -491,12 +516,19 @@ void BTD::HCI_event_task() {
break;
case EV_INCOMING_CONNECT:
disc_bdaddr[0] = hcibuf[2];
disc_bdaddr[1] = hcibuf[3];
disc_bdaddr[2] = hcibuf[4];
disc_bdaddr[3] = hcibuf[5];
disc_bdaddr[4] = hcibuf[6];
disc_bdaddr[5] = hcibuf[7];
for (uint8_t i = 0; i < 6; i++)
disc_bdaddr[i] = hcibuf[i + 2];
if ((hcibuf[9] & 0x05) && (hcibuf[8] & 0xC0)) { // Check if it is a mouse or keyboard
#ifdef DEBUG_USB_HOST
if (hcibuf[8] & 0x80)
Notify(PSTR("\r\nMouse is connecting"), 0x80);
if (hcibuf[8] & 0x40)
Notify(PSTR("\r\nKeyboard is connecting"), 0x80);
#endif
incomingHIDDevice = true;
}
#ifdef EXTRADEBUG
Notify(PSTR("\r\nClass of device: "), 0x80);
D_PrintHex<uint8_t > (hcibuf[10], 0x80);
@ -538,9 +570,14 @@ void BTD::HCI_event_task() {
case EV_AUTHENTICATION_COMPLETE:
if (pairWithWii && !connectToWii) {
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nPairing successful"), 0x80);
Notify(PSTR("\r\nPairing successful with Wiimote"), 0x80);
#endif
connectToWii = true; // Only send the ACL data to the Wii service
} else if (pairWithHIDDevice && !connectToHIDDevice) {
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nPairing successful with HID device"), 0x80);
#endif
connectToHIDDevice = true; // Only send the ACL data to the Wii service
}
break;
/* We will just ignore the following events */
@ -639,7 +676,7 @@ void BTD::HCI_task() {
hci_set_local_name(btdName);
hci_state = HCI_SET_NAME_STATE;
} else
hci_state = HCI_CHECK_WII_SERVICE;
hci_state = HCI_CHECK_DEVICE_SERVICE;
}
break;
@ -649,14 +686,17 @@ void BTD::HCI_task() {
Notify(PSTR("\r\nThe name is set to: "), 0x80);
NotifyStr(btdName, 0x80);
#endif
hci_state = HCI_CHECK_WII_SERVICE;
hci_state = HCI_CHECK_DEVICE_SERVICE;
}
break;
case HCI_CHECK_WII_SERVICE:
if (pairWithWii) { // Check if it should try to connect to a wiimote
case HCI_CHECK_DEVICE_SERVICE:
if (pairWithHIDDevice || pairWithWii) { // Check if it should try to connect to a wiimote
#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
Notify(PSTR("\r\nPlease enable discovery of your device"), 0x80);
#endif
hci_inquiry();
hci_state = HCI_INQUIRY_STATE;
@ -665,37 +705,55 @@ void BTD::HCI_task() {
break;
case HCI_INQUIRY_STATE:
if (hci_wii_found) {
if (hci_device_found) {
hci_inquiry_cancel(); // Stop inquiry
#ifdef DEBUG_USB_HOST
if (pairWithWii)
Notify(PSTR("\r\nWiimote 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);
Notify(PSTR("\r\nAnd then press any button on the Wiimote"), 0x80);
else
Notify(PSTR("\r\nBTHID hid(&Btd);"), 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
hci_state = HCI_REMOTE_NAME_STATE;
} else
hci_state = HCI_CONNECT_WII_STATE;
hci_state = HCI_CONNECT_DEVICE_STATE;
}
break;
case HCI_CONNECT_WII_STATE:
case HCI_CONNECT_DEVICE_STATE:
if (hci_cmd_complete) {
#ifdef DEBUG_USB_HOST
if (pairWithWii)
Notify(PSTR("\r\nConnecting to Wiimote"), 0x80);
else
Notify(PSTR("\r\nConnecting to HID device"), 0x80);
#endif
hci_connect();
hci_state = HCI_CONNECTED_WII_STATE;
hci_state = HCI_CONNECTED_DEVICE_STATE;
}
break;
case HCI_CONNECTED_WII_STATE:
case HCI_CONNECTED_DEVICE_STATE:
if (hci_connect_event) {
if (hci_connect_complete) {
#ifdef DEBUG_USB_HOST
if (pairWithWii)
Notify(PSTR("\r\nConnected to Wiimote"), 0x80);
else
Notify(PSTR("\r\nConnected to HID device"), 0x80);
#endif
hci_authentication_request(); // This will start the pairing with the wiimote
hci_state = HCI_SCANNING_STATE;
@ -709,7 +767,7 @@ void BTD::HCI_task() {
break;
case HCI_SCANNING_STATE:
if (!connectToWii && !pairWithWii) {
if (!connectToWii && !pairWithWii && !connectToHIDDevice && !pairWithHIDDevice) {
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nWait For Incoming Connection Request"), 0x80);
#endif
@ -763,7 +821,7 @@ void BTD::HCI_task() {
}
}
if (pairWithWii && motionPlusInside)
hci_state = HCI_CONNECT_WII_STATE;
hci_state = HCI_CONNECT_DEVICE_STATE;
else {
hci_accept_connection();
hci_state = HCI_CONNECTED_STATE;
@ -816,6 +874,10 @@ void BTD::HCI_task() {
incomingWii = false;
pairWithWii = false;
connectToHIDDevice = false;
incomingHIDDevice = false;
pairWithHIDDevice = false;
hci_state = HCI_SCANNING_STATE;
}
break;
@ -947,7 +1009,7 @@ void BTD::hci_set_local_name(const char* name) {
}
void BTD::hci_inquiry() {
hci_event_flag &= ~HCI_FLAG_WII_FOUND;
hci_event_flag &= ~HCI_FLAG_DEVICE_FOUND;
hcibuf[0] = 0x01;
hcibuf[1] = 0x01 << 2; // HCI OGF = 1
hcibuf[2] = 0x05; // Parameter Total Length = 5

28
BTD.h
View file

@ -45,11 +45,11 @@
#define HCI_BDADDR_STATE 3
#define HCI_LOCAL_VERSION_STATE 4
#define HCI_SET_NAME_STATE 5
#define HCI_CHECK_WII_SERVICE 6
#define HCI_CHECK_DEVICE_SERVICE 6
#define HCI_INQUIRY_STATE 7 // These three states are only used if it should pair and connect to a Wii controller
#define HCI_CONNECT_WII_STATE 8
#define HCI_CONNECTED_WII_STATE 9
#define HCI_CONNECT_DEVICE_STATE 8
#define HCI_CONNECTED_DEVICE_STATE 9
#define HCI_SCANNING_STATE 10
#define HCI_CONNECT_IN_STATE 11
@ -67,7 +67,7 @@
#define HCI_FLAG_INCOMING_REQUEST 0x10
#define HCI_FLAG_READ_BDADDR 0x20
#define HCI_FLAG_READ_VERSION 0x40
#define HCI_FLAG_WII_FOUND 0x80
#define HCI_FLAG_DEVICE_FOUND 0x80
#define HCI_FLAG_CONNECT_EVENT 0x100
/*Macros for HCI event flag tests */
@ -78,7 +78,7 @@
#define hci_incoming_connect_request (hci_event_flag & HCI_FLAG_INCOMING_REQUEST)
#define hci_read_bdaddr_complete (hci_event_flag & HCI_FLAG_READ_BDADDR)
#define hci_read_version_complete (hci_event_flag & HCI_FLAG_READ_VERSION)
#define hci_wii_found (hci_event_flag & HCI_FLAG_WII_FOUND)
#define hci_device_found (hci_event_flag & HCI_FLAG_DEVICE_FOUND)
#define hci_connect_event (hci_event_flag & HCI_FLAG_CONNECT_EVENT)
/* HCI Events managed */
@ -133,6 +133,8 @@
#define BTD_MAX_ENDPOINTS 4
#define BTD_NUMSERVICES 4 // Max number of Bluetooth services - if you need more than four simply increase this number
#define PAIR 1
/** All Bluetooth services should include this class. */
class BluetoothService {
public:
@ -422,19 +424,31 @@ public:
/** Call this function to pair with a Wiimote */
void pairWithWiimote() {
pairWithWii = true;
hci_state = HCI_CHECK_WII_SERVICE;
hci_state = HCI_CHECK_DEVICE_SERVICE;
};
/** Used to only send the ACL data to the wiimote. */
bool connectToWii;
/** True if a Wiimote is connecting. */
bool incomingWii;
/** True when it should pair with the incoming Wiimote. */
/** True when it should pair with a Wiimote. */
bool pairWithWii;
/** True if it's the new Wiimote with the Motion Plus Inside or a Wii U Pro Controller. */
bool motionPlusInside;
/** True if it's a Wii U Pro Controller. */
bool wiiUProController;
/** Call this function to pair with a Wiimote */
void pairWithHID() {
pairWithHIDDevice = true;
hci_state = HCI_CHECK_DEVICE_SERVICE;
};
/** Used to only send the ACL data to the wiimote. */
bool connectToHIDDevice;
/** True if a Wiimote is connecting. */
bool incomingHIDDevice;
/** True when it should pair with a device like a mouse or keyboard. */
bool pairWithHIDDevice;
/**
* Read the poll interval taken from the endpoint descriptors.
* @return The poll interval in ms.

410
BTHID.cpp Normal file
View file

@ -0,0 +1,410 @@
/* Copyright (C) 2013 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 "BTHID.h"
// To enable serial debugging see "settings.h"
//#define EXTRADEBUG // Uncomment to get even more debugging data
//#define PRINTREPORT // Uncomment to print the report send by the Wii controllers
const uint8_t BUTTONS[] PROGMEM = { // Mouse buttons
0x04, // MIDDLE
0x02, // RIGHT
0, // Skip
0x01, // LEFT
};
BTHID::BTHID(BTD *p, bool pair, const char *pin) :
pBtd(p) // pointer to USB class instance - mandatory
{
if (pBtd)
pBtd->registerServiceClass(this); // Register it as a Bluetooth service
pBtd->pairWithHIDDevice = pair;
if (pair)
pBtd->btdPin= pin;
/* Set device cid for the control and intterrupt channelse - LSB */
control_dcid[0] = 0x70; // 0x0070
control_dcid[1] = 0x00;
interrupt_dcid[0] = 0x71; // 0x0071
interrupt_dcid[1] = 0x00;
Reset();
}
void BTHID::Reset() {
connected = false;
activeConnection = false;
l2cap_event_flag = 0; // Reset flags
l2cap_state = L2CAP_WAIT;
}
void BTHID::disconnect() { // Use this void to disconnect any of the controllers
// First the HID interrupt channel has to be disconnected, then the HID control channel and finally the HCI connection
pBtd->l2cap_disconnection_request(hci_handle, 0x0A, interrupt_scid, interrupt_dcid);
Reset();
l2cap_state = L2CAP_INTERRUPT_DISCONNECT;
}
void BTHID::ACLData(uint8_t* l2capinbuf) {
if (!pBtd->l2capConnectionClaimed && pBtd->incomingHIDDevice && !connected && !activeConnection) {
if (l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {
if ((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) {
pBtd->incomingHIDDevice = false;
pBtd->l2capConnectionClaimed = true; // Claim that the incoming connection belongs to this service
activeConnection = true;
hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection
l2cap_state = L2CAP_WAIT;
}
}
}
if ((l2capinbuf[0] | (l2capinbuf[1] << 8)) == (hci_handle | 0x2000)) { // acl_handle_ok or it's a new connection
if ((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001) { //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(" "), 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
if (l2capinbuf[14] == control_dcid[0] && l2capinbuf[15] == control_dcid[1]) { // Success
//Notify(PSTR("\r\nHID Control Connection Complete"), 0x80);
identifier = l2capinbuf[9];
control_scid[0] = l2capinbuf[12];
control_scid[1] = l2capinbuf[13];
l2cap_event_flag |= L2CAP_FLAG_CONTROL_CONNECTED;
} else if (l2capinbuf[14] == interrupt_dcid[0] && l2capinbuf[15] == interrupt_dcid[1]) {
//Notify(PSTR("\r\nHID Interrupt Connection Complete"), 0x80);
identifier = l2capinbuf[9];
interrupt_scid[0] = l2capinbuf[12];
interrupt_scid[1] = l2capinbuf[13];
l2cap_event_flag |= L2CAP_FLAG_INTERRUPT_CONNECTED;
}
}
} else if (l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {
#ifdef EXTRADEBUG
Notify(PSTR("\r\nL2CAP Connection Request - PSM: "), 0x80);
D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);
Notify(PSTR(" "), 0x80);
D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);
Notify(PSTR(" SCID: "), 0x80);
D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);
Notify(PSTR(" "), 0x80);
D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);
Notify(PSTR(" Identifier: "), 0x80);
D_PrintHex<uint8_t > (l2capinbuf[9], 0x80);
#endif
if ((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) {
identifier = l2capinbuf[9];
control_scid[0] = l2capinbuf[14];
control_scid[1] = l2capinbuf[15];
l2cap_event_flag |= L2CAP_FLAG_CONNECTION_CONTROL_REQUEST;
} else if ((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_INTR_PSM) {
identifier = l2capinbuf[9];
interrupt_scid[0] = l2capinbuf[14];
interrupt_scid[1] = l2capinbuf[15];
l2cap_event_flag |= L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST;
}
} else if (l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) {
if ((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success
if (l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
//Notify(PSTR("\r\nHID Control Configuration Complete"), 0x80);
identifier = l2capinbuf[9];
l2cap_event_flag |= L2CAP_FLAG_CONFIG_CONTROL_SUCCESS;
} else if (l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
//Notify(PSTR("\r\nHID Interrupt Configuration Complete"), 0x80);
identifier = l2capinbuf[9];
l2cap_event_flag |= L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS;
}
}
} else if (l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) {
if (l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
//Notify(PSTR("\r\nHID Control Configuration Request"), 0x80);
pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], control_scid);
} else if (l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
//Notify(PSTR("\r\nHID Interrupt Configuration Request"), 0x80);
pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], interrupt_scid);
}
} else if (l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) {
if (l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nDisconnect Request: Control Channel"), 0x80);
#endif
identifier = l2capinbuf[9];
pBtd->l2cap_disconnection_response(hci_handle, identifier, control_dcid, control_scid);
Reset();
} else if (l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nDisconnect Request: Interrupt Channel"), 0x80);
#endif
identifier = l2capinbuf[9];
pBtd->l2cap_disconnection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid);
Reset();
}
} else if (l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) {
if (l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) {
//Notify(PSTR("\r\nDisconnect Response: Control Channel"), 0x80);
identifier = l2capinbuf[9];
l2cap_event_flag |= L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE;
} else if (l2capinbuf[12] == interrupt_scid[0] && l2capinbuf[13] == interrupt_scid[1]) {
//Notify(PSTR("\r\nDisconnect Response: Interrupt Channel"), 0x80);
identifier = l2capinbuf[9];
l2cap_event_flag |= L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE;
}
}
#ifdef EXTRADEBUG
else {
identifier = l2capinbuf[9];
Notify(PSTR("\r\nL2CAP Unknown Signaling Command: "), 0x80);
D_PrintHex<uint8_t > (l2capinbuf[8], 0x80);
}
#endif
} else if (l2capinbuf[6] == interrupt_dcid[0] && l2capinbuf[7] == interrupt_dcid[1]) { // l2cap_interrupt
//Notify(PSTR("\r\n\r\nL2CAP Interrupt: "), 0x80);
#ifdef PRINTREPORT
Notify(PSTR("\r\n"), 0x80);
for (uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) {
D_PrintHex<uint8_t > (l2capinbuf[i + 8], 0x80);
Notifyc(' ', 0x80);
}
#endif
if (l2capinbuf[8] == 0xA1) { // HID_THDR_DATA_INPUT
switch (l2capinbuf[9]) {
case 0x01: // Keyboard events
break;
case 0x02: // Mouse events
case 0x1A:
ButtonState = l2capinbuf[10];
/*xAxis = l2capinbuf[11] | ((int16_t)l2capinbuf[12] << 8);
yAxis = l2capinbuf[13] | ((int16_t)l2capinbuf[14] << 8);
scroll = l2capinbuf[15] | ((int16_t)l2capinbuf[16] << 8);*/
if (ButtonState != OldButtonState) {
ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
OldButtonState = ButtonState;
}
break;
case 0x03:
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nChange mode event: "), 0x80);
D_PrintHex<uint8_t > (l2capinbuf[11], 0x80);
#endif
break;
#ifdef DEBUG_USB_HOST
default:
Notify(PSTR("\r\nUnknown Report type: "), 0x80);
D_PrintHex<uint8_t > (l2capinbuf[9], 0x80);
break;
#endif
}
}
}
#ifdef EXTRADEBUG
else {
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\nData: "), 0x80);
Notify(PSTR("\r\n"), 0x80);
for (uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) {
D_PrintHex<uint8_t > (l2capinbuf[i + 8], 0x80);
Notifyc(' ', 0x80);
}
}
#endif
L2CAP_task();
}
}
void BTHID::L2CAP_task() {
switch (l2cap_state) {
/* These states are used if the Wiimote is the host */
case L2CAP_CONTROL_SUCCESS:
if (l2cap_config_success_control_flag) {
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nHID Control Successfully Configured"), 0x80);
#endif
l2cap_state = L2CAP_INTERRUPT_SETUP;
}
break;
case L2CAP_INTERRUPT_SETUP:
if (l2cap_connection_request_interrupt_flag) {
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nHID Interrupt Incoming Connection Request"), 0x80);
#endif
pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, PENDING);
delay(1);
pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, SUCCESSFUL);
identifier++;
delay(1);
pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid);
l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST;
}
break;
/* These states are used if the Arduino is the host */
case L2CAP_CONTROL_CONNECT_REQUEST:
if (l2cap_connected_control_flag) {
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nSend HID Control Config Request"), 0x80);
#endif
identifier++;
pBtd->l2cap_config_request(hci_handle, identifier, control_scid);
l2cap_state = L2CAP_CONTROL_CONFIG_REQUEST;
}
break;
case L2CAP_CONTROL_CONFIG_REQUEST:
if (l2cap_config_success_control_flag) {
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nSend HID Interrupt Connection Request"), 0x80);
#endif
identifier++;
pBtd->l2cap_connection_request(hci_handle, identifier, interrupt_dcid, HID_INTR_PSM);
l2cap_state = L2CAP_INTERRUPT_CONNECT_REQUEST;
}
break;
case L2CAP_INTERRUPT_CONNECT_REQUEST:
if (l2cap_connected_interrupt_flag) {
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nSend HID Interrupt Config Request"), 0x80);
#endif
identifier++;
pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid);
l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST;
}
break;
case L2CAP_INTERRUPT_CONFIG_REQUEST:
if (l2cap_config_success_interrupt_flag) { // Now the HID channels is established
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nHID Channels Established"), 0x80);
#endif
pBtd->connectToHIDDevice = false;
pBtd->pairWithHIDDevice = false;
connected = true;
setProtocol();
onInit();
l2cap_state = L2CAP_DONE;
}
break;
case L2CAP_DONE:
break;
case L2CAP_INTERRUPT_DISCONNECT:
if (l2cap_disconnect_response_interrupt_flag) {
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nDisconnected Interrupt Channel"), 0x80);
#endif
identifier++;
pBtd->l2cap_disconnection_request(hci_handle, identifier, control_scid, control_dcid);
l2cap_state = L2CAP_CONTROL_DISCONNECT;
}
break;
case L2CAP_CONTROL_DISCONNECT:
if (l2cap_disconnect_response_control_flag) {
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nDisconnected Control Channel"), 0x80);
#endif
pBtd->hci_disconnect(hci_handle);
hci_handle = -1; // Reset handle
l2cap_event_flag = 0; // Reset flags
l2cap_state = L2CAP_WAIT;
}
break;
}
}
void BTHID::Run() {
switch (l2cap_state) {
case L2CAP_WAIT:
if (pBtd->connectToHIDDevice && !pBtd->l2capConnectionClaimed && !connected && !activeConnection) {
pBtd->l2capConnectionClaimed = true;
activeConnection = true;
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nSend HID Control Connection Request"), 0x80);
#endif
hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection
l2cap_event_flag = 0; // Reset flags
identifier = 0;
pBtd->l2cap_connection_request(hci_handle, identifier, control_dcid, HID_CTRL_PSM);
l2cap_state = L2CAP_CONTROL_CONNECT_REQUEST;
} else if (l2cap_connection_request_control_flag) {
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nHID Control Incoming Connection Request"), 0x80);
#endif
pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, PENDING);
delay(1);
pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, SUCCESSFUL);
identifier++;
delay(1);
pBtd->l2cap_config_request(hci_handle, identifier, control_scid);
l2cap_state = L2CAP_CONTROL_SUCCESS;
}
break;
}
}
/************************************************************/
/* HID Commands */
/************************************************************/
void BTHID::setProtocol() {
uint8_t command = 0x71; // Set Protocol to "Report Protocol Mode", see HID specs page 33
pBtd->L2CAP_Command(hci_handle, &command, 1, control_scid[0], control_scid[1]);
}
/************************************************************/
/* BT HID Commands */
/************************************************************/
bool BTHID::getButtonPress(Button b) { // Return true when a button is pressed
return (bool)((ButtonState & pgm_read_byte(&BUTTONS[(uint8_t)b])));
}
bool BTHID::getButtonClick(Button b) { // Only return true when a button is clicked
uint8_t button = pgm_read_byte(&BUTTONS[(uint8_t)b]);
bool click = (ButtonClickState & button);
ButtonClickState &= ~button; // Clear "click" event
return click;
}
void BTHID::onInit() {
if (pFuncOnInit)
pFuncOnInit(); // Call the user function
else {
// Do nothing
}
}

162
BTHID.h Normal file
View file

@ -0,0 +1,162 @@
/* Copyright (C) 2013 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 _bthid_h_
#define _bthid_h_
#include "BTD.h"
#include "controllerEnums.h"
/* Bluetooth L2CAP states for L2CAP_task() */
#define L2CAP_WAIT 0
// These states are used if the device is the host
#define L2CAP_CONTROL_SUCCESS 1
#define L2CAP_INTERRUPT_SETUP 2
// These states are used if the Arduino is the host
#define L2CAP_CONTROL_CONNECT_REQUEST 3
#define L2CAP_CONTROL_CONFIG_REQUEST 4
#define L2CAP_INTERRUPT_CONNECT_REQUEST 5
#define L2CAP_INTERRUPT_CONFIG_REQUEST 6
#define L2CAP_DONE 7
#define L2CAP_INTERRUPT_DISCONNECT 8
#define L2CAP_CONTROL_DISCONNECT 9
/* L2CAP event flags */
#define L2CAP_FLAG_CONTROL_CONNECTED 0x01
#define L2CAP_FLAG_INTERRUPT_CONNECTED 0x02
#define L2CAP_FLAG_CONFIG_CONTROL_SUCCESS 0x04
#define L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS 0x08
#define L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE 0x10
#define L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE 0x20
#define L2CAP_FLAG_CONNECTION_CONTROL_REQUEST 0x40
#define L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST 0x80
/* Macros for L2CAP event flag tests */
#define l2cap_connected_control_flag (l2cap_event_flag & L2CAP_FLAG_CONTROL_CONNECTED)
#define l2cap_connected_interrupt_flag (l2cap_event_flag & L2CAP_FLAG_INTERRUPT_CONNECTED)
#define l2cap_config_success_control_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)
#define l2cap_config_success_interrupt_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS)
#define l2cap_disconnect_response_control_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE)
#define l2cap_disconnect_response_interrupt_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE)
#define l2cap_connection_request_control_flag (l2cap_event_flag & L2CAP_FLAG_CONNECTION_CONTROL_REQUEST)
#define l2cap_connection_request_interrupt_flag (l2cap_event_flag & L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST)
/** This BluetoothService class implements support for the HID keyboard and mice. */
class BTHID : public BluetoothService {
public:
/**
* Constructor for the BTHID class.
* @param p Pointer to the BTD class instance.
* @param pair Set this to true in order to pair with the device. If the argument is omitted then it will not pair with it. One can use ::PAIR to set it to true.
* @param pin Write the pin to BTD#btdPin. If argument is omitted, then "1234" will be used.
*/
BTHID(BTD *p, bool pair = false, const char *pin = "1234");
/** @name BluetoothService implementation */
/**
* Used to pass acldata to the services.
* @param ACLData Incoming acldata.
*/
virtual void ACLData(uint8_t* ACLData);
/** Used to run part of the state maschine. */
virtual void Run();
/** Use this to reset the service. */
virtual void Reset();
/** Used this to disconnect any of the controllers. */
virtual void disconnect();
/**@}*/
/** True if a device is connected */
bool connected;
/** @name HID mouse functions */
/**
* getButtonPress(Button b) will return true as long as the button is held down.
*
* While getButtonClick(Button b) will only return it once.
*
* So you instance if you need to increase a variable once you would use getButtonClick(Button b),
* but if you need to drive a robot forward you would use getButtonPress(Button b).
*/
bool getButtonPress(Button b);
bool getButtonClick(Button b);
/**@}*/
/** @name HID mouse functions */
/*int16_t getXaxis() {
return xAxis;
}
int16_t getYaxis() {
return yAxis;
}
int16_t getScroll() {
return scroll;
}*/
/**@}*/
/** Call this to start the paring sequence with a controller */
void pair(void) {
if (pBtd)
pBtd->pairWithHID();
}
/**
* Used to call your own function when the controller is successfully initialized.
* @param funcOnInit Function to call.
*/
void attachOnInit(void (*funcOnInit)(void)) {
pFuncOnInit = funcOnInit;
};
private:
BTD *pBtd; // Pointer to BTD instance
/** Set report protocol. */
void setProtocol();
/**
* Called when the controller is successfully initialized.
* Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
* This is useful for instance if you want to set the LEDs in a specific way.
*/
void onInit();
void (*pFuncOnInit)(void); // Pointer to function called in onInit()
void L2CAP_task(); // L2CAP state machine
/* Variables filled from HCI event management */
uint16_t hci_handle;
bool activeConnection; // Used to indicate if it's already has established a connection
/* Variables used by high level L2CAP task */
uint8_t l2cap_state;
uint8_t l2cap_event_flag; // l2cap flags of received Bluetooth events
uint8_t ButtonState, OldButtonState, ButtonClickState;
int16_t xAxis, yAxis, scroll;
/* L2CAP Channels */
uint8_t control_scid[2]; // L2CAP source CID for HID_Control
uint8_t control_dcid[2]; // 0x0070
uint8_t interrupt_scid[2]; // L2CAP source CID for HID_Interrupt
uint8_t interrupt_dcid[2]; // 0x0071
uint8_t identifier; // Identifier for connection
};
#endif

2
Wii.h
View file

@ -76,8 +76,6 @@
#define motion_plus_connected_flag (l2cap_event_flag & WII_FLAG_MOTION_PLUS_CONNECTED)
#define nunchuck_connected_flag (l2cap_event_flag & WII_FLAG_NUNCHUCK_CONNECTED)
#define PAIR 1
/** Enum used to read the joystick on the Nunchuck. */
enum Hat {
/** Read the x-axis on the Nunchuck joystick. */

View file

@ -103,6 +103,8 @@ enum Button {
BLACK = 8, // Available on the original Xbox controller
WHITE = 9, // Available on the original Xbox controller
/**@}*/
MIDDLE = 0, // Middle mouse button
};
/** Joysticks on the PS3 and Xbox controllers. */

View file

@ -0,0 +1,37 @@
/*
Example sketch for the HID 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 <BTHID.h>
#include <usbhub.h>
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 */
BTHID hid(&Btd, PAIR, "0000"); // This will start an inquiry and then pair with your Wiimote - you only have to do this once
//BTHID hid(&Btd); // After that you can simply create the instance like so and then press any button on the Wiimote
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.print(F("\r\nOSC did not start"));
while (1); //halt
}
Serial.print(F("\r\nHID Bluetooth Library Started"));
}
void loop() {
Usb.Task();
if (hid.connected) {
if (hid.getButtonClick(LEFT)) // Print mouse buttons
Serial.println(F("Left"));
if (hid.getButtonClick(RIGHT))
Serial.println(F("Right"));
if (hid.getButtonClick(MIDDLE))
Serial.println(F("Middle"));
}
}

View file

@ -97,6 +97,7 @@ L3 LITERAL1
R3 LITERAL1
START LITERAL1
UP LITERAL1
MIDDLE LITERAL1
RIGHT LITERAL1
DOWN LITERAL1
LEFT LITERAL1