Xbox One S controller support is now finally working

This commit is contained in:
Kristian Sloth Lauszus 2020-11-15 17:29:54 +01:00
parent ad9360b865
commit 102746ef6b
12 changed files with 726 additions and 307 deletions

186
BTD.cpp
View file

@ -17,7 +17,7 @@
#include "BTD.h"
// To enable serial debugging see "settings.h"
#define EXTRADEBUG // Uncomment to get even more debugging data
//#define EXTRADEBUG // Uncomment to get even more debugging data
const uint8_t BTD::BTD_CONTROL_PIPE = 0;
const uint8_t BTD::BTD_EVENT_PIPE = 1;
@ -542,7 +542,6 @@ void BTD::HCI_event_task() {
if(classOfDevice[0] & 0x08)
Notify(PSTR("\r\nGamepad found"), 0x80);
#endif
for(uint8_t j = 0; j < 6; j++)
disc_bdaddr[j] = hcibuf[j + 3 + 6 * i];
@ -673,62 +672,49 @@ void BTD::HCI_event_task() {
}
break;
case EV_READ_REMOTE_EXTENDED_FEATURES_COMPLETE:
if(!hcibuf[2]) { // Check if connected OK
if(!hci_check_flag(HCI_FLAG_REMOTE_EXTENDED_FEATURES)) {
case EV_IO_CAPABILITY_REQUEST:
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nReceived IO Capability Request"), 0x80);
#endif
hci_io_capability_request_reply();
break;
case EV_IO_CAPABILITY_RESPONSE:
#ifdef EXTRADEBUG
Notify(PSTR("\r\nPage number: "), 0x80);
D_PrintHex<uint8_t > (hcibuf[5], 0x80);
Notify(PSTR("\r\nMaximum page number: "), 0x80);
D_PrintHex<uint8_t > (hcibuf[6], 0x80);
Notify(PSTR("\r\nExtended LMP features:"), 0x80);
for(uint8_t i = 0; i < 8; i++) {
Notify(PSTR(" "), 0x80);
D_PrintHex<uint8_t > (hcibuf[7 + i], 0x80);
}
Notify(PSTR("\r\nReceived IO Capability Response: "), 0x80);
Notify(PSTR("\r\nIO capability: "), 0x80);
D_PrintHex<uint8_t > (hcibuf[8], 0x80);
Notify(PSTR("\r\nOOB data present: "), 0x80);
D_PrintHex<uint8_t > (hcibuf[9], 0x80);
Notify(PSTR("\r\nAuthentication request: "), 0x80);
D_PrintHex<uint8_t > (hcibuf[10], 0x80);
#endif
#ifdef DEBUG_USB_HOST
if(hcibuf[5] == 0) { // Page 0
Notify(PSTR("\r\nRemote "), 0x80);
if(hcibuf[7 + 6] & (1U << 3))
Notify(PSTR("supports"), 0x80);
else
Notify(PSTR("does NOT support"), 0x80);
Notify(PSTR(" secure simple pairing (controller support)"), 0x80);
} else if(hcibuf[5] == 1) { // Page 1
Notify(PSTR("\r\nRemote "), 0x80);
if(hcibuf[7 + 0] & (1U << 0))
Notify(PSTR("supports"), 0x80);
else
Notify(PSTR("\r\ndoes NOT support"), 0x80);
Notify(PSTR(" secure simple pairing (host support)"), 0x80);
}
#endif
}
hci_set_flag(HCI_FLAG_REMOTE_EXTENDED_FEATURES);
} else {
hci_state = HCI_CHECK_DEVICE_SERVICE;
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nFailed to read remote extended featues: "), 0x80);
D_PrintHex<uint8_t > (hcibuf[2], 0x80);
#endif
}
break;
case EV_USER_CONFIRMATION_REQUEST:
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nUser confirmation Request: "), 0x80);
#endif
Notify(PSTR("\r\nUser confirmation Request"), 0x80);
#ifdef EXTRADEBUG
Notify(PSTR("\r\nNumeric value: "), 0x80);
Notify(PSTR(": \r\nNumeric value: "), 0x80);
for(uint8_t i = 0; i < 4; i++) {
Notify(PSTR(" "), 0x80);
D_PrintHex<uint8_t > (hcibuf[8 + i], 0x80);
}
#endif
#endif
// Simply confirm the connection, as the host has no "NoInputNoOutput" capabilities
hci_user_confirmation_request_reply();
hci_state = HCI_SCANNING_STATE;
break;
case EV_SIMPLE_PAIRING_COMPLETE:
#ifdef EXTRADEBUG
if(!hcibuf[2]) { // Check if connected OK
Notify(PSTR("\r\nSimple Pairing succeeded"), 0x80);
} else {
Notify(PSTR("\r\nSimple Pairing failed: "), 0x80);
D_PrintHex<uint8_t > (hcibuf[2], 0x80);
}
#endif
break;
/* We will just ignore the following events */
@ -756,7 +742,6 @@ void BTD::HCI_event_task() {
if(hcibuf[0] != 0x00) {
Notify(PSTR("\r\nUnmanaged HCI Event: "), 0x80);
D_PrintHex<uint8_t > (hcibuf[0], 0x80);
Notify(PSTR(", data: "), 0x80);
for(uint16_t i = 0; i < hcibuf[1]; i++) {
D_PrintHex<uint8_t > (hcibuf[2 + i], 0x80);
@ -872,6 +857,16 @@ void BTD::HCI_task() {
if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) {
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nSimple pairing was enabled"), 0x80);
#endif
hci_set_event_mask();
hci_state = HCI_SET_EVENT_MASK_STATE;
}
break;
case HCI_SET_EVENT_MASK_STATE:
if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) {
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nSet event mask completed"), 0x80);
#endif
hci_state = HCI_CHECK_DEVICE_SERVICE;
}
@ -943,9 +938,8 @@ void BTD::HCI_task() {
else
Notify(PSTR("\r\nConnected to HID device"), 0x80);
#endif
hci_read_remote_extended_features(0); // "Requests the normal LMP features as returned by the HCI_Read_Remote_Supported_Features command"
//hci_read_remote_extended_features(1); // Read page 1
hci_state = HCI_REMOTE_EXTENDED_FEATURES_STATE;
hci_authentication_request(); // This will start the pairing with the device
hci_state = HCI_SCANNING_STATE;
} else {
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nTrying to connect one more time..."), 0x80);
@ -955,19 +949,6 @@ void BTD::HCI_task() {
}
break;
case HCI_REMOTE_EXTENDED_FEATURES_STATE:
if(hci_check_flag(HCI_FLAG_REMOTE_EXTENDED_FEATURES)) {
/*hci_read_remote_version_information();
delay(1);
hci_change_connection_packet_type_command();
delay(1);
hci_write_link_policy_settings();
delay(1);*/
hci_authentication_request();
hci_state = HCI_SCANNING_STATE;
}
break;
case HCI_SCANNING_STATE:
if(!connectToWii && !pairWithWii && !connectToHIDDevice && !pairWithHIDDevice) {
#ifdef DEBUG_USB_HOST
@ -1218,18 +1199,6 @@ void BTD::hci_remote_name() {
HCI_Command(hcibuf, 13);
}
void BTD::hci_read_remote_extended_features(uint8_t page_number) {
hci_clear_flag(HCI_FLAG_REMOTE_EXTENDED_FEATURES);
hcibuf[0] = 0x1C; // HCI OCF = 1C
hcibuf[1] = 0x01 << 2; // HCI OGF = 1
hcibuf[2] = 0x03; // parameter length = 3
hcibuf[3] = (uint8_t)(hci_handle & 0xFF); //connection handle - low byte
hcibuf[4] = (uint8_t)((hci_handle >> 8) & 0x0F); //connection handle - high byte
hcibuf[5] = page_number;
HCI_Command(hcibuf, 6);
}
void BTD::hci_write_local_name(const char* name) {
hcibuf[0] = 0x13; // HCI OCF = 13
hcibuf[1] = 0x03 << 2; // HCI OGF = 3
@ -1242,6 +1211,24 @@ void BTD::hci_write_local_name(const char* name) {
HCI_Command(hcibuf, 4 + strlen(name));
}
void BTD::hci_set_event_mask() {
hcibuf[0] = 0x01; // HCI OCF = 01
hcibuf[1] = 0x03 << 2; // HCI OGF = 3
hcibuf[2] = 0x08;
// The first 6 bytes are the default of 1FFF FFFF FFFF
// However we need to set bits 48-55 for simple pairing to work
hcibuf[3] = 0xFF;
hcibuf[4] = 0xFF;
hcibuf[5] = 0xFF;
hcibuf[6] = 0xFF;
hcibuf[7] = 0xFF;
hcibuf[8] = 0x1F;
hcibuf[9] = 0xFF; // Enable bits 48-55 used for simple pairing
hcibuf[10] = 0x00;
HCI_Command(hcibuf, 11);
}
void BTD::hci_write_simple_pairing_mode(bool enable) {
hcibuf[0] = 0x56; // HCI OCF = 56
hcibuf[1] = 0x03 << 2; // HCI OGF = 3
@ -1299,40 +1286,6 @@ void BTD::hci_connect(uint8_t *bdaddr) {
HCI_Command(hcibuf, 16);
}
void BTD::hci_read_remote_version_information() {
hcibuf[0] = 0x1D; // HCI OCF = 1D
hcibuf[1] = 0x01 << 2; // HCI OGF = 1
hcibuf[2] = 0x02; // parameter Total Length = 2
hcibuf[3] = (uint8_t)(hci_handle & 0xFF); //connection handle - low byte
hcibuf[4] = (uint8_t)((hci_handle >> 8) & 0x0F); //connection handle - high byte
HCI_Command(hcibuf, 5);
}
void BTD::hci_change_connection_packet_type_command() {
hcibuf[0] = 0x0F; // HCI OCF = 0F
hcibuf[1] = 0x01 << 2; // HCI OGF = 1
hcibuf[2] = 0x04; // parameter Total Length = 4
hcibuf[3] = (uint8_t)(hci_handle & 0xFF); //connection handle - low byte
hcibuf[4] = (uint8_t)((hci_handle >> 8) & 0x0F); //connection handle - high byte
hcibuf[5] = 0x18; // DM1 or DH1 may be used
hcibuf[6] = 0xCC; // DM3, DH3, DM5, DH5 may be used
HCI_Command(hcibuf, 7);
}
void BTD::hci_write_link_policy_settings() {
hcibuf[0] = 0x0D; // HCI OCF = 0D
hcibuf[1] = 0x02 << 2; // HCI OGF = 2
hcibuf[2] = 0x04; // parameter Total Length = 4
hcibuf[3] = (uint8_t)(hci_handle & 0xFF); //connection handle - low byte
hcibuf[4] = (uint8_t)((hci_handle >> 8) & 0x0F); //connection handle - high byte
hcibuf[5] = 0x0F; // Enable role switch, enable hold mode, enable sniff mode, enable park mode
hcibuf[6] = 0x00;
HCI_Command(hcibuf, 7);
}
void BTD::hci_pin_code_request_reply() {
hcibuf[0] = 0x0D; // HCI OCF = 0D
hcibuf[1] = 0x01 << 2; // HCI OGF = 1
@ -1397,6 +1350,23 @@ void BTD::hci_link_key_request_negative_reply() {
HCI_Command(hcibuf, 9);
}
void BTD::hci_io_capability_request_reply() {
hcibuf[0] = 0x2B; // HCI OCF = 2B
hcibuf[1] = 0x01 << 2; // HCI OGF = 1
hcibuf[2] = 0x09;
hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr
hcibuf[4] = disc_bdaddr[1];
hcibuf[5] = disc_bdaddr[2];
hcibuf[6] = disc_bdaddr[3];
hcibuf[7] = disc_bdaddr[4];
hcibuf[8] = disc_bdaddr[5];
hcibuf[9] = 0x03; // NoInputNoOutput
hcibuf[10] = 0x00; // OOB authentication data not present
hcibuf[11] = 0x00; // MITM Protection Not Required No Bonding. Numeric comparison with automatic accept allowed
HCI_Command(hcibuf, 12);
}
void BTD::hci_user_confirmation_request_reply() {
hcibuf[0] = 0x2C; // HCI OCF = 2C
hcibuf[1] = 0x01 << 2; // HCI OGF = 1

34
BTD.h
View file

@ -61,7 +61,7 @@
#define HCI_DISCONNECT_STATE 16
#define HCI_LOCAL_EXTENDED_FEATURES_STATE 17
#define HCI_WRITE_SIMPLE_PAIRING_STATE 18
#define HCI_REMOTE_EXTENDED_FEATURES_STATE 19
#define HCI_SET_EVENT_MASK_STATE 19
/* HCI event flags*/
#define HCI_FLAG_CMD_COMPLETE (1UL << 0)
@ -74,7 +74,6 @@
#define HCI_FLAG_DEVICE_FOUND (1UL << 7)
#define HCI_FLAG_CONNECT_EVENT (1UL << 8)
#define HCI_FLAG_LOCAL_EXTENDED_FEATURES (1UL << 9)
#define HCI_FLAG_REMOTE_EXTENDED_FEATURES (1UL << 10)
/* Macros for HCI event flag tests */
#define hci_check_flag(flag) (hci_event_flag & (flag))
@ -91,6 +90,10 @@
#define EV_REMOTE_NAME_COMPLETE 0x07
#define EV_ENCRYPTION_CHANGE 0x08
#define EV_CHANGE_CONNECTION_LINK 0x09
#define EV_READ_REMOTE_VERSION_INFORMATION_COMPLETE 0x0C
#define EV_QOS_SETUP_COMPLETE 0x0D
#define EV_COMMAND_COMPLETE 0x0E
#define EV_COMMAND_STATUS 0x0F
#define EV_ROLE_CHANGED 0x12
#define EV_NUM_COMPLETE_PKT 0x13
#define EV_PIN_CODE_REQUEST 0x16
@ -98,15 +101,13 @@
#define EV_LINK_KEY_NOTIFICATION 0x18
#define EV_DATA_BUFFER_OVERFLOW 0x1A
#define EV_MAX_SLOTS_CHANGE 0x1B
#define EV_READ_REMOTE_VERSION_INFORMATION_COMPLETE 0x0C
#define EV_QOS_SETUP_COMPLETE 0x0D
#define EV_COMMAND_COMPLETE 0x0E
#define EV_COMMAND_STATUS 0x0F
#define EV_LOOPBACK_COMMAND 0x19
#define EV_PAGE_SCAN_REP_MODE 0x20
#define EV_READ_REMOTE_EXTENDED_FEATURES_COMPLETE 0x23
#define EV_IO_CAPABILITY_REQUEST 0x31
#define EV_IO_CAPABILITY_RESPONSE 0x32
#define EV_USER_CONFIRMATION_REQUEST 0x33
#define EV_SIMPLE_PAIRING_COMPLETE 0x36
/* Bluetooth states for the different Bluetooth drivers */
#define L2CAP_WAIT 0
@ -339,23 +340,23 @@ public:
void hci_read_bdaddr();
/** Read the HCI Version of the Bluetooth dongle. */
void hci_read_local_version_information();
/** Used to check if the dongle supports simple paring */
void hci_read_local_extended_features(uint8_t page_number);
/**
* Set the local name of the Bluetooth dongle.
* @param name Desired name.
*/
void hci_write_local_name(const char* name);
/** Used to enable simply paring if the dongle supports it */
void hci_write_simple_pairing_mode(bool enable);
/** Used to enable events related to simple paring */
void hci_set_event_mask();
/** Enable visibility to other Bluetooth devices. */
void hci_write_scan_enable();
/** Disable visibility to other Bluetooth devices. */
void hci_write_scan_disable();
/** Read the remote devices name. */
void hci_remote_name();
void hci_read_remote_extended_features(uint8_t page_number);
/** Accept the connection with the Bluetooth device. */
void hci_accept_connection();
/**
@ -376,7 +377,7 @@ public:
* if the Host does not have a stored Link Key for the connection.
*/
void hci_link_key_request_negative_reply();
/** Used to during simple paring to confirm that the we want to connect */
void hci_user_confirmation_request_reply();
/** Used to try to authenticate with the remote device. */
void hci_authentication_request();
@ -386,13 +387,8 @@ public:
void hci_inquiry_cancel();
/** Connect to last device communicated with. */
void hci_connect();
void hci_read_remote_version_information();
void hci_change_connection_packet_type_command();
void hci_write_link_policy_settings();
/** Used during simple paring to reply to a IO capability request */
void hci_io_capability_request_reply();
/**
* Connect to device.
* @param bdaddr Bluetooth address of the device.

207
BTHID.cpp
View file

@ -17,7 +17,7 @@
#include "BTHID.h"
// To enable serial debugging see "settings.h"
#define EXTRADEBUG // Uncomment to get even more debugging data
//#define EXTRADEBUG // Uncomment to get even more debugging data
//#define PRINTREPORT // Uncomment to print the report send by the HID device
BTHID::BTHID(BTD *p, bool pair, const char *pin) :
@ -61,19 +61,6 @@ void BTHID::disconnect() { // Use this void to disconnect the device
}
void BTHID::ACLData(uint8_t* l2capinbuf) {
/*
Notify(PSTR("\r\nL2CAP Data - Channel ID: "), 0x80);
D_PrintHex<uint8_t > (l2capinbuf[7], 0x80);
Notify(PSTR(" "), 0x80);
D_PrintHex<uint8_t > (l2capinbuf[6], 0x80);
Notify(PSTR(", data: "), 0x80);
for(uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) {
D_PrintHex<uint8_t > (l2capinbuf[i + 8], 0x80);
Notify(PSTR(" "), 0x80);
}
*/
if(!connected) {
if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {
if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == SDP_PSM && !pBtd->sdpConnectionClaimed) {
@ -116,24 +103,29 @@ void BTHID::ACLData(uint8_t* l2capinbuf) {
} 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] == sdp_dcid[0] && l2capinbuf[15] == sdp_dcid[1]) {
#ifdef EXTRADEBUG
Notify(PSTR("\r\nSDP Connection Complete"), 0x80);
#endif
identifier = l2capinbuf[9];
sdp_scid[0] = l2capinbuf[12];
sdp_scid[1] = l2capinbuf[13];
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nSend SDP Config Request"), 0x80);
#endif
identifier++;
pBtd->l2cap_config_request(hci_handle, identifier, sdp_scid);
} else if(l2capinbuf[14] == control_dcid[0] && l2capinbuf[15] == control_dcid[1]) {
#ifdef EXTRADEBUG
Notify(PSTR("\r\nHID Control Connection Complete"), 0x80);
#endif
identifier = l2capinbuf[9];
control_scid[0] = l2capinbuf[12];
control_scid[1] = l2capinbuf[13];
l2cap_set_flag(L2CAP_FLAG_CONTROL_CONNECTED);
} else if(l2capinbuf[14] == interrupt_dcid[0] && l2capinbuf[15] == interrupt_dcid[1]) {
#ifdef EXTRADEBUG
Notify(PSTR("\r\nHID Interrupt Connection Complete"), 0x80);
#endif
identifier = l2capinbuf[9];
interrupt_scid[0] = l2capinbuf[12];
interrupt_scid[1] = l2capinbuf[13];
@ -172,28 +164,40 @@ void BTHID::ACLData(uint8_t* l2capinbuf) {
} else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) {
if((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success
if(l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) {
#ifdef EXTRADEBUG
Notify(PSTR("\r\nSDP Configuration Complete"), 0x80);
#endif
identifier = l2capinbuf[9];
l2cap_set_flag(L2CAP_FLAG_CONFIG_SDP_SUCCESS);
} else if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
#ifdef EXTRADEBUG
Notify(PSTR("\r\nHID Control Configuration Complete"), 0x80);
#endif
identifier = l2capinbuf[9];
l2cap_set_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS);
} else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
#ifdef EXTRADEBUG
Notify(PSTR("\r\nHID Interrupt Configuration Complete"), 0x80);
#endif
identifier = l2capinbuf[9];
l2cap_set_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS);
}
}
} else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) {
if(l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) {
#ifdef EXTRADEBUG
Notify(PSTR("\r\nSDP Configuration Request"), 0x80);
#endif
pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], sdp_scid);
} else if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
#ifdef EXTRADEBUG
Notify(PSTR("\r\nHID Control Configuration Request"), 0x80);
#endif
pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], control_scid);
} else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
#ifdef EXTRADEBUG
Notify(PSTR("\r\nHID Interrupt Configuration Request"), 0x80);
#endif
pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], interrupt_scid);
}
} else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) {
@ -220,15 +224,21 @@ void BTHID::ACLData(uint8_t* l2capinbuf) {
}
} else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) {
if(l2capinbuf[12] == sdp_scid[0] && l2capinbuf[13] == sdp_scid[1]) {
//Notify(PSTR("\r\nDisconnect Response: SDP Channel"), 0x80);
#ifdef EXTRADEBUG
Notify(PSTR("\r\nDisconnect Response: SDP Channel"), 0x80);
#endif
identifier = l2capinbuf[9];
l2cap_set_flag(L2CAP_FLAG_DISCONNECT_RESPONSE);
} else if(l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) {
//Notify(PSTR("\r\nDisconnect Response: Control Channel"), 0x80);
#ifdef EXTRADEBUG
Notify(PSTR("\r\nDisconnect Response: Control Channel"), 0x80);
#endif
identifier = l2capinbuf[9];
l2cap_set_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);
#ifdef EXTRADEBUG
Notify(PSTR("\r\nDisconnect Response: Interrupt Channel"), 0x80);
#endif
identifier = l2capinbuf[9];
l2cap_set_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE);
}
@ -254,7 +264,7 @@ void BTHID::ACLData(uint8_t* l2capinbuf) {
l2capoutbuf[0] = SDP_SERVICE_SEARCH_RESPONSE;
l2capoutbuf[1] = l2capinbuf[9];//transactionIDHigh;
l2capoutbuf[2] = l2capinbuf[10];//transactionIDLow;
#if 1
l2capoutbuf[3] = 0x00; // MSB Parameter Length
l2capoutbuf[4] = 0x05; // LSB Parameter Length = 5
@ -267,29 +277,6 @@ void BTHID::ACLData(uint8_t* l2capinbuf) {
l2capoutbuf[9] = 0x00; // No continuation state
SDP_Command(l2capoutbuf, 10);
#else
l2capoutbuf[3] = 0x00; // MSB Parameter Length
l2capoutbuf[4] = 0x09; // LSB Parameter Length = 9
l2capoutbuf[5] = 0x00; // MSB TotalServiceRecordCount
l2capoutbuf[6] = 0x01; // LSB TotalServiceRecordCount = 1
l2capoutbuf[7] = 0x00; // MSB CurrentServiceRecordCount
l2capoutbuf[8] = 0x01; // LSB CurrentServiceRecordCount = 1
l2capoutbuf[9] = 0x00; // ServiceRecordHandleList
l2capoutbuf[10] = 0x01; // ServiceRecordHandleList
l2capoutbuf[11] = 0x00; // ServiceRecordHandleList
l2capoutbuf[12] = 0x01; // ServiceRecordHandleList: 0x10001.
l2capoutbuf[13] = 0x00; // No continuation state
SDP_Command(l2capoutbuf, 14);
#endif
//pBtd->hci_authentication_request();
//identifier++;
//pBtd->l2cap_connection_request(hci_handle, identifier, sdp_dcid, SDP_PSM);
} else if(l2capinbuf[8] == SDP_SERVICE_ATTRIBUTE_REQUEST) {
Notify(PSTR("\r\nSDP_SERVICE_ATTRIBUTE_REQUEST"), 0x80);
@ -298,7 +285,6 @@ void BTHID::ACLData(uint8_t* l2capinbuf) {
l2capoutbuf[1] = l2capinbuf[9];//transactionIDHigh;
l2capoutbuf[2] = l2capinbuf[10];//transactionIDLow;
#if 1
l2capoutbuf[3] = 0x00; // MSB Parameter Length
l2capoutbuf[4] = 0x05; // LSB Parameter Length = 5
@ -312,141 +298,9 @@ void BTHID::ACLData(uint8_t* l2capinbuf) {
l2capoutbuf[9] = 0x00; // No continuation state
SDP_Command(l2capoutbuf, 10);
#else
size_t i = 3;
l2capoutbuf[i++] = 0x00;
l2capoutbuf[i++] = 0x5a;
l2capoutbuf[i++] = 0x00;
l2capoutbuf[i++] = 0x57;
l2capoutbuf[i++] = 0x35;
l2capoutbuf[i++] = 0x55;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x00;
l2capoutbuf[i++] = 0x00;
l2capoutbuf[i++] = 0x0a;
l2capoutbuf[i++] = 0x00;
l2capoutbuf[i++] = 0x01;
l2capoutbuf[i++] = 0x00;
l2capoutbuf[i++] = 0x01;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x00;
l2capoutbuf[i++] = 0x01;
l2capoutbuf[i++] = 0x35;
l2capoutbuf[i++] = 0x03;
l2capoutbuf[i++] = 0x19;
l2capoutbuf[i++] = 0x12;
l2capoutbuf[i++] = 0x00;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x00;
l2capoutbuf[i++] = 0x05;
l2capoutbuf[i++] = 0x35;
l2capoutbuf[i++] = 0x03;
l2capoutbuf[i++] = 0x19;
l2capoutbuf[i++] = 0x10;
l2capoutbuf[i++] = 0x02;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x00;
l2capoutbuf[i++] = 0x06;
l2capoutbuf[i++] = 0x35;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x65;
l2capoutbuf[i++] = 0x6e;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x00;
l2capoutbuf[i++] = 0x6a;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x01;
l2capoutbuf[i++] = 0x00;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x02;
l2capoutbuf[i++] = 0x00;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x01;
l2capoutbuf[i++] = 0x03;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x02;
l2capoutbuf[i++] = 0x01;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x00;
l2capoutbuf[i++] = 0x4c;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x02;
l2capoutbuf[i++] = 0x02;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x4a;
l2capoutbuf[i++] = 0x34;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x02;
l2capoutbuf[i++] = 0x03;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x10;
l2capoutbuf[i++] = 0x11;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x02;
l2capoutbuf[i++] = 0x04;
l2capoutbuf[i++] = 0x28;
l2capoutbuf[i++] = 0x01;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x02;
l2capoutbuf[i++] = 0x05;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x00;
l2capoutbuf[i++] = 0x01;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0xa0;
l2capoutbuf[i++] = 0x00;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x16;
l2capoutbuf[i++] = 0xc4;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0xaf;
l2capoutbuf[i++] = 0xff;
l2capoutbuf[i++] = 0x09;
l2capoutbuf[i++] = 0x00;
l2capoutbuf[i++] = 0x01;
l2capoutbuf[i++] = 0x00;
Serial.print("\nLENGTH: ");
Serial.println(i);
SDP_Command(l2capoutbuf, i);
#endif
/*
l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST;
l2capoutbuf[1] = 0x00; // TODO: Do not hardcode
l2capoutbuf[2] = 0x01;
l2capoutbuf[3] = 0x00; // MSB Parameter Length
l2capoutbuf[4] = 0x0F; // LSB Parameter Length = 15
i = 5;
l2capoutbuf[i++] = 0x35;
l2capoutbuf[i++] = 0x03;
l2capoutbuf[i++] = 0x19;
l2capoutbuf[i++] = 0x12;
l2capoutbuf[i++] = 0x00;
l2capoutbuf[i++] = 0xff;
l2capoutbuf[i++] = 0xff;
l2capoutbuf[i++] = 0x35;
l2capoutbuf[i++] = 0x05;
l2capoutbuf[i++] = 0x0a;
l2capoutbuf[i++] = 0x00;
l2capoutbuf[i++] = 0x00;
l2capoutbuf[i++] = 0xff;
l2capoutbuf[i++] = 0xff;
l2capoutbuf[i++] = 0x00;
Serial.print("\nLENGTH: ");
Serial.println(i);
SDP_Command(l2capoutbuf, i);
*/
} else if(l2capinbuf[8] == SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST) {
Notify(PSTR("\r\nSDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST"), 0x80);
#ifdef EXTRADEBUG
Notify(PSTR("\r\nSDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST"), 0x80);
Notify(PSTR("\r\nUUID: "), 0x80);
uint16_t uuid;
if((l2capinbuf[16] << 8 | l2capinbuf[17]) == 0x0000) // Check if it's sending the UUID as a 128-bit UUID
@ -464,7 +318,6 @@ void BTHID::ACLData(uint8_t* l2capinbuf) {
Notify(PSTR(" "), 0x80);
}
#endif
serviceNotSupported(l2capinbuf[9], l2capinbuf[10]); // The service is not supported
}
#ifdef EXTRADEBUG

View file

@ -22,7 +22,7 @@ For more information about the hardware see the [Hardware Manual](http://www.cir
* __Oleg Mazurov, Circuits@Home__ - <mazurov@circuitsathome.com>
* __Alexei Glushchenko, Circuits@Home__ - <alex-gl@mail.ru>
* Developers of the USB Core, HID, FTDI, ADK, ACM, and PL2303 libraries
* __Kristian Lauszus, TKJ Electronics__ - <kristianl@tkjelectronics.com>
* __Kristian Sloth Lauszus__ - <kristianl@tkjelectronics.com>
* Developer of the [BTD](#bluetooth-libraries), [BTHID](#bthid-library), [SPP](#spp-library), [PS4](#ps4-library), [PS3](#ps3-library), [Wii](#wii-library), [Xbox](#xbox-library), and [PSBuzz](#ps-buzz-library) libraries
* __Andrew Kroll__ - <xxxajk@gmail.com>
* Major contributor to mass storage code
@ -55,6 +55,7 @@ Help yourself by helping us support you! Many thousands of hours have been spent
* [Xbox library](#xbox-library)
* [Xbox 360 Library](#xbox-360-library)
* [Xbox ONE Library](#xbox-one-library)
* [Xbox ONE S Library](#xbox-one-s-library)
* [Wii library](#wii-library)
* [PS Buzz Library](#ps-buzz-library)
* [HID Libraries](#hid-libraries)
@ -262,12 +263,18 @@ All the information regarding the Xbox 360 controller protocol are form these si
#### Xbox ONE Library
An Xbox ONE controller is supported via USB in the [XBOXONE](XBOXONE.cpp) class. It is heavily based on the 360 library above. In addition to cross referencing the above, information on the protocol was found at:
A Xbox ONE controller is supported via USB in the [XBOXONE](XBOXONE.cpp) class. It is heavily based on the 360 library above. In addition to cross referencing the above, information on the protocol was found at:
* <https://github.com/quantus/xbox-one-controller-protocol>
* <https://github.com/torvalds/linux/blob/master/drivers/input/joystick/xpad.c>
* <https://github.com/kylelemons/xbox/blob/master/xbox.go>
#### Xbox ONE S Library
A Xbox ONE controller is supported via Bluetooth in the [XBOXONESBT](XBOXONESBT.cpp) class.
Special thanks to [HisashiKato](https://github.com/HisashiKato) for his help: <https://github.com/felis/USB_Host_Shield_2.0/issues/252#issuecomment-716912362>.
### [Wii library](Wii.cpp)
The [Wii](Wii.cpp) library support the Wiimote, but also the Nunchuch and Motion Plus extensions via Bluetooth. The Wii U Pro Controller and Wii Balance Board are also supported via Bluetooth.

21
SPP.cpp
View file

@ -531,6 +531,27 @@ void SPP::RFCOMM_task() {
/* SDP Commands */
/************************************************************/
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]);
}
void SPP::serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow) { // See page 235 in the Bluetooth specs
l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE;
l2capoutbuf[1] = transactionIDHigh;
l2capoutbuf[2] = transactionIDLow;
l2capoutbuf[3] = 0x00; // MSB Parameter Length
l2capoutbuf[4] = 0x05; // LSB Parameter Length = 5
l2capoutbuf[5] = 0x00; // MSB AttributeListsByteCount
l2capoutbuf[6] = 0x02; // LSB AttributeListsByteCount = 2
/* Attribute ID/Value Sequence: */
l2capoutbuf[7] = 0x35; // Data element sequence - length in next byte
l2capoutbuf[8] = 0x00; // Length = 0
l2capoutbuf[9] = 0x00; // No continuation state
SDP_Command(l2capoutbuf, 10);
}
void SPP::serialPortResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow) {
l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE;
l2capoutbuf[1] = transactionIDHigh;

100
XBOXONESBT.h Normal file
View file

@ -0,0 +1,100 @@
/* Copyright (C) 2020 Kristian Sloth Lauszus. 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 Sloth Lauszus
Web : https://lauszus.com
e-mail : lauszus@gmail.com
*/
#ifndef _xboxonesbt_h_
#define _xboxonesbt_h_
#include "BTHID.h"
#include "XBOXONESParser.h"
/**
* This class implements support for the Xbox One S controller via Bluetooth.
* It uses the BTHID class for all the Bluetooth communication.
*/
class XBOXONESBT : public BTHID, public XBOXONESParser {
public:
/**
* Constructor for the XBOXONESBT 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.
*/
XBOXONESBT(BTD *p, bool pair = false) :
BTHID(p, pair) {
XBOXONESParser::Reset();
};
/**
* Used to check if a Xbox One S controller is connected.
* @return Returns true if it is connected.
*/
bool connected() {
return BTHID::connected;
};
protected:
/** @name BTHID implementation */
/**
* Used to parse Bluetooth HID data.
* @param len The length of the incoming data.
* @param buf Pointer to the data buffer.
*/
virtual void ParseBTHIDData(uint8_t len, uint8_t *buf) {
XBOXONESParser::Parse(len, buf);
};
/**
* Called when a device 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.
*/
virtual void OnInitBTHID() {
XBOXONESParser::Reset();
if (pFuncOnInit)
pFuncOnInit(); // Call the user function
};
/** Used to reset the different buffers to there default values */
virtual void ResetBTHID() {
XBOXONESParser::Reset();
};
/**@}*/
#if 0
/** @name XBOXONESParser implementation */
virtual void sendOutputReport(uint8_t *data, uint8_t nbytes) {
// See: https://lore.kernel.org/patchwork/patch/973394/
uint8_t buf[nbytes + 2];
buf[0] = 0x52; // HID BT Set_report (0x50) | Report Type (Output 0x02)
buf[1] = 0x03; // Report ID
memcpy(buf + 2, data, nbytes);
Notify(PSTR("\r\n"), 0x80);
for (uint8_t i = 0; i < sizeof(buf); i++) {
D_PrintHex<uint8_t > (buf[i], 0x80);
Notify(PSTR(" "), 0x80);
}
Notify(PSTR("\r\n"), 0x80);
//pBtd->L2CAP_Command(hci_handle, data, nbytes, interrupt_scid[0], interrupt_scid[1]);
pBtd->L2CAP_Command(hci_handle, buf, sizeof(buf), control_scid[0], control_scid[1]);
};
/**@}*/
#endif
};
#endif

200
XBOXONESParser.cpp Normal file
View file

@ -0,0 +1,200 @@
/* Copyright (C) 2020 Kristian Sloth Lauszus. 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 Sloth Lauszus
Web : https://lauszus.com
e-mail : lauszus@gmail.com
*/
#include "XBOXONESParser.h"
// To enable serial debugging see "settings.h"
//#define PRINTREPORT // Uncomment to print the report send by the Xbox One S Controller
/** Buttons on the controller */
const uint8_t XBOX_ONE_S_BUTTONS[] PROGMEM = {
UP, // UP
RIGHT, // RIGHT
DOWN, // DOWN
LEFT, // LEFT
0x0E, // VIEW
0x0F, // MENU
0x10, // L3
0x11, // R3
0, 0, // Skip L2 and R2 as these are analog buttons
0x0C, // L1
0x0D, // R1
0x09, // B
0x08, // A
0x0A, // X
0x0B, // Y
};
enum DPADEnum {
DPAD_OFF = 0x0,
DPAD_UP = 0x1,
DPAD_UP_RIGHT = 0x2,
DPAD_RIGHT = 0x3,
DPAD_RIGHT_DOWN = 0x4,
DPAD_DOWN = 0x5,
DPAD_DOWN_LEFT = 0x6,
DPAD_LEFT = 0x7,
DPAD_LEFT_UP = 0x8,
};
bool XBOXONESParser::checkDpad(ButtonEnum b) {
switch (b) {
case UP:
return xboxOneSData.btn.dpad == DPAD_LEFT_UP || xboxOneSData.btn.dpad == DPAD_UP || xboxOneSData.btn.dpad == DPAD_UP_RIGHT;
case RIGHT:
return xboxOneSData.btn.dpad == DPAD_UP_RIGHT || xboxOneSData.btn.dpad == DPAD_RIGHT || xboxOneSData.btn.dpad == DPAD_RIGHT_DOWN;
case DOWN:
return xboxOneSData.btn.dpad == DPAD_RIGHT_DOWN || xboxOneSData.btn.dpad == DPAD_DOWN || xboxOneSData.btn.dpad == DPAD_DOWN_LEFT;
case LEFT:
return xboxOneSData.btn.dpad == DPAD_DOWN_LEFT || xboxOneSData.btn.dpad == DPAD_LEFT || xboxOneSData.btn.dpad == DPAD_LEFT_UP;
default:
return false;
}
}
uint16_t XBOXONESParser::getButtonPress(ButtonEnum b) {
if (b == L2)
return xboxOneSData.trigger[0];
else if (b == R2)
return xboxOneSData.trigger[1];
else if (b <= LEFT) // Dpad
return checkDpad(b);
else if (b == XBOX)
return xboxButtonState;
return xboxOneSData.btn.val & (1UL << pgm_read_byte(&XBOX_ONE_S_BUTTONS[(uint8_t)b]));
}
bool XBOXONESParser::getButtonClick(ButtonEnum b) {
if(b == L2) {
if(L2Clicked) {
L2Clicked = false;
return true;
}
return false;
} else if(b == R2) {
if(R2Clicked) {
R2Clicked = false;
return true;
}
return false;
} else if (b == XBOX) {
bool click = xboxbuttonClickState;
xboxbuttonClickState = 0; // Clear "click" event
return click;
}
uint32_t mask = 1UL << pgm_read_byte(&XBOX_ONE_S_BUTTONS[(uint8_t)b]);
bool click = buttonClickState.val & mask;
buttonClickState.val &= ~mask; // Clear "click" event
return click;
}
int16_t XBOXONESParser::getAnalogHat(AnalogHatEnum a) {
return xboxOneSData.hatValue[(uint8_t)a] - 32768; // Convert to signed integer
}
void XBOXONESParser::Parse(uint8_t len, uint8_t *buf) {
if (len > 1 && buf) {
#ifdef PRINTREPORT
Notify(PSTR("\r\n"), 0x80);
for (uint8_t i = 0; i < len; i++) {
D_PrintHex<uint8_t > (buf[i], 0x80);
Notify(PSTR(" "), 0x80);
}
#endif
if (buf[0] == 0x01) // Check report ID
memcpy(&xboxOneSData, buf + 1, min((uint8_t)(len - 1), MFK_CASTUINT8T sizeof(xboxOneSData)));
else if (buf[0] == 0x02) { // This report contains the Xbox button
xboxButtonState = buf[1];
if(xboxButtonState != xboxOldButtonState) {
xboxbuttonClickState = xboxButtonState & ~xboxOldButtonState; // Update click state variable
xboxOldButtonState = xboxButtonState;
}
return;
} else if (buf[0] == 0x04) // Heartbeat
return;
else {
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nUnknown report id: "), 0x80);
D_PrintHex<uint8_t > (buf[0], 0x80);
#endif
return;
}
if (xboxOneSData.btn.val != oldButtonState.val) { // Check if anything has changed
buttonClickState.val = xboxOneSData.btn.val & ~oldButtonState.val; // Update click state variable
oldButtonState.val = xboxOneSData.btn.val;
// The DPAD buttons does not set the different bits, but set a value corresponding to the buttons pressed, we will simply set the bits ourself
uint8_t newDpad = 0;
if (checkDpad(UP))
newDpad |= 1 << UP;
if (checkDpad(RIGHT))
newDpad |= 1 << RIGHT;
if (checkDpad(DOWN))
newDpad |= 1 << DOWN;
if (checkDpad(LEFT))
newDpad |= 1 << LEFT;
if (newDpad != oldDpad) {
buttonClickState.dpad = newDpad & ~oldDpad; // Override values
oldDpad = newDpad;
}
}
// Handle click detection for triggers
if(xboxOneSData.trigger[0] != 0 && triggerOld[0] == 0)
L2Clicked = true;
triggerOld[0] = xboxOneSData.trigger[0];
if(xboxOneSData.trigger[1] != 0 && triggerOld[1] == 0)
R2Clicked = true;
triggerOld[1] = xboxOneSData.trigger[1];
}
}
void XBOXONESParser::Reset() {
uint8_t i;
for (i = 0; i < sizeof(xboxOneSData.hatValue) / sizeof(xboxOneSData.hatValue[0]); i++)
xboxOneSData.hatValue[i] = 32768; // Center value
xboxOneSData.btn.val = 0;
oldButtonState.val = 0;
for (i = 0; i < sizeof(xboxOneSData.trigger) / sizeof(xboxOneSData.trigger[0]); i++)
xboxOneSData.trigger[i] = 0;
xboxOneSData.btn.dpad = DPAD_OFF;
oldButtonState.dpad = DPAD_OFF;
buttonClickState.dpad = 0;
oldDpad = 0;
};
#if 0
void XBOXONESParser::setRumbleOn(uint8_t leftTrigger, uint8_t rightTrigger, uint8_t leftMotor, uint8_t rightMotor) {
// See: https://lore.kernel.org/patchwork/patch/973394/
uint8_t buf[8];
buf[0] = 0x0F;//1 << 1 | 1 << 0; // Enable weak and strong feedback
buf[1] = leftTrigger;
buf[2] = rightTrigger;
buf[3] = leftMotor;
buf[4] = rightMotor;
buf[5] = 255; // Duration of effect in 10 ms
buf[6] = 0; // Start delay in 10 ms
buf[7] = 255; // Loop count
sendOutputReport(buf, sizeof(buf));
}
#endif

127
XBOXONESParser.h Normal file
View file

@ -0,0 +1,127 @@
/* Copyright (C) 2020 Kristian Sloth Lauszus. 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 Sloth Lauszus
Web : https://lauszus.com
e-mail : lauszus@gmail.com
*/
#ifndef _xboxonesparser_h_
#define _xboxonesparser_h_
#include "Usb.h"
#include "controllerEnums.h"
union XboxOneSButtons {
struct {
uint8_t dpad : 4;
uint8_t reserved : 4;
uint8_t a : 1;
uint8_t b : 1;
uint8_t x : 1;
uint8_t y : 1;
uint8_t l1 : 1;
uint8_t r1 : 1;
uint8_t view : 1;
uint8_t menu : 1;
uint8_t l3 : 1;
uint8_t r3 : 1;
uint8_t reserved2 : 6;
} __attribute__((packed));
uint32_t val : 24;
} __attribute__((packed));
struct XboxOneSData {
/* Button and joystick values */
uint16_t hatValue[4];
uint16_t trigger[2];
XboxOneSButtons btn;
} __attribute__((packed));
/** This class parses all the data sent by the Xbox One S controller */
class XBOXONESParser {
public:
/** Constructor for the XBOXONESParser class. */
XBOXONESParser() {
Reset();
};
/** @name Xbox One S Controller functions */
/**
* getButtonPress(ButtonEnum b) will return true as long as the button is held down.
*
* While getButtonClick(ButtonEnum b) will only return it once.
*
* So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),
* but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).
* @param b ::ButtonEnum to read.
* @return getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press.
*/
uint16_t getButtonPress(ButtonEnum b);
bool getButtonClick(ButtonEnum b);
/**@}*/
/**
* Used to read the analog joystick.
* @param a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY.
* @return Return the analog value as a 16-bit signed integer.
*/
int16_t getAnalogHat(AnalogHatEnum a);
/** Used to set the rumble off. */
//void setRumbleOff();
/**
* Used to turn on rumble continuously.
* @param leftTrigger Left trigger force.
* @param rightTrigger Right trigger force.
* @param leftMotor Left motor force.
* @param rightMotor Right motor force.
*/
//void setRumbleOn(uint8_t leftTrigger, uint8_t rightTrigger, uint8_t leftMotor, uint8_t rightMotor);
protected:
/**
* Used to parse data sent from the Xbox One S controller.
* @param len Length of the data.
* @param buf Pointer to the data buffer.
*/
void Parse(uint8_t len, uint8_t *buf);
/** Used to reset the different buffers to their default values */
void Reset();
/**
* Send the output to the Xbox One S controller. This is implemented in XBOXONESBT.h.
* @param output Pointer to data buffer.
* @param nbytes Bytes to send.
*/
//virtual void sendOutputReport(uint8_t *data, uint8_t nbytes) = 0;
private:
bool checkDpad(ButtonEnum b); // Used to check Xbox One S DPAD buttons
XboxOneSData xboxOneSData;
XboxOneSButtons oldButtonState, buttonClickState;
uint8_t oldDpad;
// The Xbox button is sent in a separate report
uint8_t xboxButtonState, xboxOldButtonState, xboxbuttonClickState;
uint16_t triggerOld[2];
bool L2Clicked; // These buttons are analog, so we use we use these bools to check if they where clicked or not
bool R2Clicked;
};
#endif

View file

@ -144,6 +144,12 @@ enum ButtonEnum {
WHITE = 9, // Available on the original Xbox controller
/**@}*/
/**@{*/
/** Xbox One S buttons */
VIEW = 4,
MENU = 5,
/**@}*/
/** PS Buzz controllers */
RED = 0,
YELLOW = 1,

View file

@ -0,0 +1,135 @@
/*
Example sketch for the Xbox One S Bluetooth library - developed by Kristian Sloth Lauszus
For more information visit the Github repository: github.com/felis/USB_Host_Shield_2.0 or
send me an e-mail: lauszus@gmail.com
*/
#include <XBOXONESBT.h>
#include <usbhub.h>
// Satisfy the IDE, which needs to see the include statement in the ino too.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#endif
#include <SPI.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 XBOXONESBT class in two ways */
// This will start an inquiry and then pair with the Xbox One S controller - you only have to do this once
// You will need to hold down the Sync and Xbox button at the same time, the Xbox One S controller will then start to blink rapidly indicating that it is in pairing mode
XBOXONESBT Xbox(&Btd, PAIR);
// After that you can simply create the instance like so and then press the Xbox button on the device
//XBOXONESBT Xbox(&Btd);
void setup() {
Serial.begin(115200);
#if !defined(__MIPSEL__)
while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
#endif
if (Usb.Init() == -1) {
Serial.print(F("\r\nOSC did not start"));
while (1); //halt
}
Serial.print(F("\r\nXbox One S Bluetooth Library Started"));
}
void loop() {
Usb.Task();
if (Xbox.connected()) {
if (Xbox.getAnalogHat(LeftHatX) > 7500 || Xbox.getAnalogHat(LeftHatX) < -7500 || Xbox.getAnalogHat(LeftHatY) > 7500 || Xbox.getAnalogHat(LeftHatY) < -7500 || Xbox.getAnalogHat(RightHatX) > 7500 || Xbox.getAnalogHat(RightHatX) < -7500 || Xbox.getAnalogHat(RightHatY) > 7500 || Xbox.getAnalogHat(RightHatY) < -7500) {
if (Xbox.getAnalogHat(LeftHatX) > 7500 || Xbox.getAnalogHat(LeftHatX) < -7500) {
Serial.print(F("LeftHatX: "));
Serial.print(Xbox.getAnalogHat(LeftHatX));
Serial.print("\t");
}
if (Xbox.getAnalogHat(LeftHatY) > 7500 || Xbox.getAnalogHat(LeftHatY) < -7500) {
Serial.print(F("LeftHatY: "));
Serial.print(Xbox.getAnalogHat(LeftHatY));
Serial.print("\t");
}
if (Xbox.getAnalogHat(RightHatX) > 7500 || Xbox.getAnalogHat(RightHatX) < -7500) {
Serial.print(F("RightHatX: "));
Serial.print(Xbox.getAnalogHat(RightHatX));
Serial.print("\t");
}
if (Xbox.getAnalogHat(RightHatY) > 7500 || Xbox.getAnalogHat(RightHatY) < -7500) {
Serial.print(F("RightHatY: "));
Serial.print(Xbox.getAnalogHat(RightHatY));
}
Serial.println();
}
if (Xbox.getButtonPress(L2) > 0 || Xbox.getButtonPress(R2) > 0) {
if (Xbox.getButtonPress(L2) > 0) {
Serial.print(F("L2: "));
Serial.print(Xbox.getButtonPress(L2));
Serial.print("\t");
}
if (Xbox.getButtonPress(R2) > 0) {
Serial.print(F("R2: "));
Serial.print(Xbox.getButtonPress(R2));
Serial.print("\t");
}
Serial.println();
}
#if 0 // This is currently not working
// Set rumble effect
static uint16_t oldL2Value, oldR2Value;
if (Xbox.getButtonPress(L2) != oldL2Value || Xbox.getButtonPress(R2) != oldR2Value) {
oldL2Value = Xbox.getButtonPress(L2);
oldR2Value = Xbox.getButtonPress(R2);
uint8_t leftRumble = map(oldL2Value, 0, 1023, 0, 255); // Map the trigger values into a byte
uint8_t rightRumble = map(oldR2Value, 0, 1023, 0, 255);
if (leftRumble > 0 || rightRumble > 0)
Xbox.setRumbleOn(leftRumble, rightRumble, leftRumble, rightRumble);
else
Xbox.setRumbleOff();
}
#endif
if (Xbox.getButtonClick(UP))
Serial.println(F("Up"));
if (Xbox.getButtonClick(DOWN))
Serial.println(F("Down"));
if (Xbox.getButtonClick(LEFT))
Serial.println(F("Left"));
if (Xbox.getButtonClick(RIGHT))
Serial.println(F("Right"));
if (Xbox.getButtonClick(VIEW))
Serial.println(F("View"));
if (Xbox.getButtonClick(MENU))
Serial.println(F("Menu"));
if (Xbox.getButtonClick(XBOX)) {
Serial.println(F("Xbox"));
Xbox.disconnect();
}
if (Xbox.getButtonClick(L1))
Serial.println(F("L1"));
if (Xbox.getButtonClick(R1))
Serial.println(F("R1"));
if (Xbox.getButtonClick(L2))
Serial.println(F("L2"));
if (Xbox.getButtonClick(R2))
Serial.println(F("R2"));
if (Xbox.getButtonClick(L3))
Serial.println(F("L3"));
if (Xbox.getButtonClick(R3))
Serial.println(F("R3"));
if (Xbox.getButtonClick(A))
Serial.println(F("A"));
if (Xbox.getButtonClick(B))
Serial.println(F("B"));
if (Xbox.getButtonClick(X))
Serial.println(F("X"));
if (Xbox.getButtonClick(Y))
Serial.println(F("Y"));
}
}

View file

@ -196,6 +196,7 @@ XBOXUSB KEYWORD1
XBOXONE KEYWORD1
XBOXOLD KEYWORD1
XBOXRECV KEYWORD1
XBOXONESBT KEYWORD1
####################################################
# Methods and Functions (KEYWORD2)
@ -227,6 +228,9 @@ BACK LITERAL1
XBOX LITERAL1
SYNC LITERAL1
VIEW LITERAL1
MENU LITERAL1
BLACK LITERAL1
WHITE LITERAL1

View file

@ -39,7 +39,7 @@ e-mail : support@circuitsathome.com
////////////////////////////////////////////////////////////////////////////////
/* Set this to 1 to activate serial debugging */
#define ENABLE_UHS_DEBUGGING 1
#define ENABLE_UHS_DEBUGGING 0
/* This can be used to select which serial port to use for debugging if
* multiple serial ports are available.