From f075ea302543b264889a74fff3929625b49aed61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Laheurte?= Date: Fri, 28 Feb 2020 18:03:16 +0100 Subject: [PATCH 01/93] Add a ParseBTHIDControlData virtual method similar to ParseBTHIDData, for reports that are sent through the control pipe. This happens for instance in response to a GET_REPORT. --- BTHID.cpp | 4 ++++ BTHID.h | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/BTHID.cpp b/BTHID.cpp index d32e9aa6..917cf4c9 100644 --- a/BTHID.cpp +++ b/BTHID.cpp @@ -215,6 +215,10 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { Notify(PSTR(" "), 0x80); } #endif + if(l2capinbuf[8] == 0xA3) { + uint16_t length = ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); + ParseBTHIDControlData((uint8_t)(length - 1), &l2capinbuf[9]); + } } #ifdef EXTRADEBUG else { diff --git a/BTHID.h b/BTHID.h index 7d1d1116..f51bb40d 100644 --- a/BTHID.h +++ b/BTHID.h @@ -125,6 +125,13 @@ protected: virtual void ParseBTHIDData(uint8_t len __attribute__((unused)), uint8_t *buf __attribute__((unused))) { return; }; + /** + * Same as ParseBTHIDData for reports that are sent through the + * interrupt pipe (in response to a GET_REPORT). + */ + virtual void ParseBTHIDControlData(uint8_t len __attribute__((unused)), uint8_t *buf __attribute__((unused))) { + return; + } /** Called when a device is connected */ virtual void OnInitBTHID() { return; From 30ac619331fd43cbbc12150dc244a2457b733566 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 30 Jun 2019 20:44:12 +0200 Subject: [PATCH 02/93] Initial code for Xbox One S controller support I lack a dongle that support simply paring, so it has been confirmed working yet --- BTD.cpp | 181 +++++++++++++++++++++++++++-- BTD.h | 13 +++ examples/Bluetooth/BTHID/BTHID.ino | 2 +- settings.h | 2 +- 4 files changed, 185 insertions(+), 13 deletions(-) diff --git a/BTD.cpp b/BTD.cpp index 536aa84e..f74cda7a 100644 --- a/BTD.cpp +++ b/BTD.cpp @@ -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; @@ -408,7 +408,46 @@ void BTD::HCI_event_task() { hci_set_flag(HCI_FLAG_CMD_COMPLETE); // Set command complete flag if((hcibuf[3] == 0x01) && (hcibuf[4] == 0x10)) { // Parameters from read local version information hci_version = hcibuf[6]; // Used to check if it supports 2.0+EDR - see http://www.bluetooth.org/Technical/AssignedNumbers/hci.htm +#ifdef EXTRADEBUG + if(!hci_check_flag(HCI_FLAG_READ_VERSION)) { + Notify(PSTR("\r\nHCI version: "), 0x80); + D_PrintHex (hci_version, 0x80); + } +#endif hci_set_flag(HCI_FLAG_READ_VERSION); + } else if((hcibuf[3] == 0x04) && (hcibuf[4] == 0x10)) { // Parameters from read local extended features + if(!hci_check_flag(HCI_FLAG_LOCAL_EXTENDED_FEATURES)) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nPage number: "), 0x80); + D_PrintHex (hcibuf[6], 0x80); + Notify(PSTR("\r\nMaximum page number: "), 0x80); + D_PrintHex (hcibuf[7], 0x80); + Notify(PSTR("\r\nExtended LMP features:"), 0x80); + for(uint8_t i = 0; i < 8; i++) { + Notify(PSTR(" "), 0x80); + D_PrintHex (hcibuf[8 + i], 0x80); + } +#endif +#ifdef DEBUG_USB_HOST + if(hcibuf[6] == 0) { // Page 0 + Notify(PSTR("\r\nLocal "), 0x80); + if(hcibuf[8 + 6] & (1U << 3)) + Notify(PSTR("supports"), 0x80); + else + Notify(PSTR("does NOT support"), 0x80); + Notify(PSTR(" secure simple paring (controller support)"), 0x80); + } else if(hcibuf[6] == 1) { // Page 1 + Notify(PSTR("\r\nLocal "), 0x80); + if(hcibuf[8 + 0] & (1U << 0)) + Notify(PSTR("supports"), 0x80); + else + Notify(PSTR("does NOT support"), 0x80); + Notify(PSTR(" secure simple paring (host support)"), 0x80); + } +#endif + } + + hci_set_flag(HCI_FLAG_LOCAL_EXTENDED_FEATURES); } else if((hcibuf[3] == 0x09) && (hcibuf[4] == 0x10)) { // Parameters from read local bluetooth address for(uint8_t i = 0; i < 6; i++) my_bdaddr[i] = hcibuf[6 + i]; @@ -446,7 +485,7 @@ void BTD::HCI_event_task() { case EV_INQUIRY_RESULT: if(hcibuf[2]) { // Check that there is more than zero responses -#ifdef EXTRADEBUG +#if defined(EXTRADEBUG) && 0 Notify(PSTR("\r\nNumber of responses: "), 0x80); Notify(hcibuf[2], 0x80); #endif @@ -456,7 +495,7 @@ void BTD::HCI_event_task() { for(uint8_t j = 0; j < 3; j++) classOfDevice[j] = hcibuf[j + 4 + offset]; -#ifdef EXTRADEBUG +#if defined(EXTRADEBUG) && 0 Notify(PSTR("\r\nClass of device: "), 0x80); D_PrintHex (classOfDevice[2], 0x80); Notify(PSTR(" "), 0x80); @@ -465,7 +504,7 @@ void BTD::HCI_event_task() { D_PrintHex (classOfDevice[0], 0x80); #endif - if(pairWithWii && classOfDevice[2] == 0x00 && (classOfDevice[1] & 0x05) && (classOfDevice[0] & 0x0C)) { // See http://wiibrew.org/wiki/Wiimote#SDP_information + if(pairWithWii && classOfDevice[2] == 0x00 && (classOfDevice[1] == 0x05) && (classOfDevice[0] & 0x0C)) { // See http://wiibrew.org/wiki/Wiimote#SDP_information checkRemoteName = true; // Check remote name to distinguish between the different controllers for(uint8_t j = 0; j < 6; j++) @@ -473,8 +512,10 @@ void BTD::HCI_event_task() { hci_set_flag(HCI_FLAG_DEVICE_FOUND); break; - } else if(pairWithHIDDevice && (classOfDevice[1] & 0x05) && (classOfDevice[0] & 0xC8)) { // Check if it is a mouse, keyboard or a gamepad - see: http://bluetooth-pentest.narod.ru/software/bluetooth_class_of_device-service_generator.html + } else if(pairWithHIDDevice && (classOfDevice[1] & 0x0F) == 0x05 && (classOfDevice[0] & 0xC8)) { // Check if it is a mouse, keyboard or a gamepad - see: http://bluetooth-pentest.narod.ru/software/bluetooth_class_of_device-service_generator.html #ifdef DEBUG_USB_HOST + checkRemoteName = true; // Used to print name in the serial monitor if serial debugging is enabled + if(classOfDevice[0] & 0x80) Notify(PSTR("\r\nMouse found"), 0x80); if(classOfDevice[0] & 0x40) @@ -524,7 +565,7 @@ void BTD::HCI_event_task() { if(remote_name[i] == '\0') // End of string break; } - // TODO: Altid sæt '\0' i remote name! + // TODO: Always set '\0' in remote name! hci_set_flag(HCI_FLAG_REMOTE_NAME_COMPLETE); } break; @@ -536,7 +577,7 @@ void BTD::HCI_event_task() { for(uint8_t i = 0; i < 3; i++) classOfDevice[i] = hcibuf[i + 8]; - if((classOfDevice[1] & 0x05) && (classOfDevice[0] & 0xC8)) { // Check if it is a mouse, keyboard or a gamepad + if((classOfDevice[1] & 0x0F) == 0x05 && (classOfDevice[0] & 0xC8)) { // Check if it is a mouse, keyboard or a gamepad #ifdef DEBUG_USB_HOST if(classOfDevice[0] & 0x80) Notify(PSTR("\r\nMouse is connecting"), 0x80); @@ -608,18 +649,84 @@ void BTD::HCI_event_task() { hci_state = HCI_DISCONNECT_STATE; } break; + + case EV_READ_REMOTE_EXTENDED_FEATURES_COMPLETE: + if(!hcibuf[2]) { // Check if connected OK + if(!hci_check_flag(HCI_FLAG_REMOTE_EXTENDED_FEATURES)) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nPage number: "), 0x80); + D_PrintHex (hcibuf[5], 0x80); + Notify(PSTR("\r\nMaximum page number: "), 0x80); + D_PrintHex (hcibuf[6], 0x80); + Notify(PSTR("\r\nExtended LMP features:"), 0x80); + for(uint8_t i = 0; i < 8; i++) { + Notify(PSTR(" "), 0x80); + D_PrintHex (hcibuf[7 + i], 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 paring (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 paring (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 (hcibuf[2], 0x80); +#endif + } + break; + + case EV_USER_CONFIRMATION_REQUEST: +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nUser confirmation Request: "), 0x80); +#endif +#ifdef EXTRADEBUG + Notify(PSTR("\r\nNumeric value: "), 0x80); + for(uint8_t i = 0; i < 4; i++) { + Notify(PSTR(" "), 0x80); + D_PrintHex (hcibuf[8 + i], 0x80); + } +#endif + + hci_user_confirmation_request_reply(); + hci_state = HCI_SCANNING_STATE; + break; + /* We will just ignore the following events */ + case EV_MAX_SLOTS_CHANGE: + break; case EV_NUM_COMPLETE_PKT: case EV_ROLE_CHANGED: case EV_PAGE_SCAN_REP_MODE: case EV_LOOPBACK_COMMAND: case EV_DATA_BUFFER_OVERFLOW: case EV_CHANGE_CONNECTION_LINK: - case EV_MAX_SLOTS_CHANGE: case EV_QOS_SETUP_COMPLETE: case EV_LINK_KEY_NOTIFICATION: case EV_ENCRYPTION_CHANGE: case EV_READ_REMOTE_VERSION_INFORMATION_COMPLETE: +#ifdef EXTRADEBUG + if(hcibuf[0] != 0x00) { + Notify(PSTR("\r\nIgnore HCI Event: "), 0x80); + D_PrintHex (hcibuf[0], 0x80); + } +#endif break; #ifdef EXTRADEBUG default: @@ -699,6 +806,14 @@ void BTD::HCI_task() { case HCI_LOCAL_VERSION_STATE: // The local version is used by the PS3BT class if(hci_check_flag(HCI_FLAG_READ_VERSION)) { + hci_read_local_extended_features(0); // "Requests the normal LMP features as returned by Read_Local_Supported_Features" + //hci_read_local_extended_features(1); // Read page 1 + hci_state = HCI_LOCAL_EXTENDED_FEATURES_STATE; + } + break; + + case HCI_LOCAL_EXTENDED_FEATURES_STATE: + if(hci_check_flag(HCI_FLAG_LOCAL_EXTENDED_FEATURES)) { if(btdName != NULL) { hci_set_local_name(btdName); hci_state = HCI_SET_NAME_STATE; @@ -783,8 +898,9 @@ void BTD::HCI_task() { 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; + 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; } else { #ifdef DEBUG_USB_HOST Notify(PSTR("\r\nTrying to connect one more time..."), 0x80); @@ -794,6 +910,13 @@ void BTD::HCI_task() { } break; + case HCI_REMOTE_EXTENDED_FEATURES_STATE: + if(hci_check_flag(HCI_FLAG_REMOTE_EXTENDED_FEATURES)) { + hci_authentication_request(); + hci_state = HCI_SCANNING_STATE; + } + break; + case HCI_SCANNING_STATE: if(!connectToWii && !pairWithWii && !connectToHIDDevice && !pairWithHIDDevice) { #ifdef DEBUG_USB_HOST @@ -855,7 +978,7 @@ void BTD::HCI_task() { #endif incomingPS4 = true; } - if(pairWithWii && checkRemoteName) + if((pairWithWii || pairWithHIDDevice) && checkRemoteName) hci_state = HCI_CONNECT_DEVICE_STATE; else { hci_accept_connection(); @@ -999,6 +1122,16 @@ void BTD::hci_read_local_version_information() { HCI_Command(hcibuf, 3); } +void BTD::hci_read_local_extended_features(uint8_t page_number) { + hci_clear_flag(HCI_FLAG_LOCAL_EXTENDED_FEATURES); + hcibuf[0] = 0x04; // HCI OCF = 4 + hcibuf[1] = 0x04 << 2; // HCI OGF = 4 + hcibuf[2] = 0x01; // parameter length = 1 + hcibuf[3] = page_number; + + HCI_Command(hcibuf, 4); +} + void BTD::hci_accept_connection() { hci_clear_flag(HCI_FLAG_CONNECT_COMPLETE); hcibuf[0] = 0x09; // HCI OCF = 9 @@ -1034,6 +1167,18 @@ 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_set_local_name(const char* name) { hcibuf[0] = 0x13; // HCI OCF = 13 hcibuf[1] = 0x03 << 2; // HCI OGF = 3 @@ -1158,6 +1303,20 @@ void BTD::hci_link_key_request_negative_reply() { HCI_Command(hcibuf, 9); } +void BTD::hci_user_confirmation_request_reply() { + hcibuf[0] = 0x2C; // HCI OCF = 2C + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x06; // parameter length 6 + 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]; + + HCI_Command(hcibuf, 9); +} + void BTD::hci_authentication_request() { hcibuf[0] = 0x11; // HCI OCF = 11 hcibuf[1] = 0x01 << 2; // HCI OGF = 1 diff --git a/BTD.h b/BTD.h index baf39364..e7ab9f96 100644 --- a/BTD.h +++ b/BTD.h @@ -59,6 +59,8 @@ #define HCI_DISABLE_SCAN_STATE 14 #define HCI_DONE_STATE 15 #define HCI_DISCONNECT_STATE 16 +#define HCI_LOCAL_EXTENDED_FEATURES_STATE 17 +#define HCI_REMOTE_EXTENDED_FEATURES_STATE 18 /* HCI event flags*/ #define HCI_FLAG_CMD_COMPLETE (1UL << 0) @@ -70,6 +72,8 @@ #define HCI_FLAG_READ_VERSION (1UL << 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)) @@ -99,6 +103,9 @@ #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_USER_CONFIRMATION_REQUEST 0x33 + /* Bluetooth states for the different Bluetooth drivers */ #define L2CAP_WAIT 0 @@ -320,6 +327,8 @@ public: void hci_read_bdaddr(); /** Read the HCI Version of the Bluetooth dongle. */ void hci_read_local_version_information(); + + void hci_read_local_extended_features(uint8_t page_number); /** * Set the local name of the Bluetooth dongle. * @param name Desired name. @@ -331,6 +340,8 @@ public: 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(); /** @@ -351,6 +362,8 @@ public: * if the Host does not have a stored Link Key for the connection. */ void hci_link_key_request_negative_reply(); + + void hci_user_confirmation_request_reply(); /** Used to try to authenticate with the remote device. */ void hci_authentication_request(); /** Start a HCI inquiry. */ diff --git a/examples/Bluetooth/BTHID/BTHID.ino b/examples/Bluetooth/BTHID/BTHID.ino index 58a2f92f..3d848726 100644 --- a/examples/Bluetooth/BTHID/BTHID.ino +++ b/examples/Bluetooth/BTHID/BTHID.ino @@ -25,7 +25,7 @@ BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so BTHID bthid(&Btd, PAIR, "0000"); // After that you can simply create the instance like so and then press any button on the device -//BTHID hid(&Btd); +//BTHID bthid(&Btd); KbdRptParser keyboardPrs; MouseRptParser mousePrs; diff --git a/settings.h b/settings.h index 2d176053..63be1770 100644 --- a/settings.h +++ b/settings.h @@ -39,7 +39,7 @@ e-mail : support@circuitsathome.com //////////////////////////////////////////////////////////////////////////////// /* Set this to 1 to activate serial debugging */ -#define ENABLE_UHS_DEBUGGING 0 +#define ENABLE_UHS_DEBUGGING 1 /* This can be used to select which serial port to use for debugging if * multiple serial ports are available. From a347b3bacea4157eac012babf598d4733861b2e0 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Tue, 2 Jul 2019 20:30:33 +0200 Subject: [PATCH 03/93] Enable simple paring mode --- BTD.cpp | 41 ++++++++++++++++++++++++++++++----------- BTD.h | 13 ++++++++----- BTHID.cpp | 14 +++++++++++++- 3 files changed, 51 insertions(+), 17 deletions(-) diff --git a/BTD.cpp b/BTD.cpp index f74cda7a..36e7e705 100644 --- a/BTD.cpp +++ b/BTD.cpp @@ -430,19 +430,19 @@ void BTD::HCI_event_task() { #endif #ifdef DEBUG_USB_HOST if(hcibuf[6] == 0) { // Page 0 - Notify(PSTR("\r\nLocal "), 0x80); + Notify(PSTR("\r\nDongle "), 0x80); if(hcibuf[8 + 6] & (1U << 3)) Notify(PSTR("supports"), 0x80); else Notify(PSTR("does NOT support"), 0x80); - Notify(PSTR(" secure simple paring (controller support)"), 0x80); + Notify(PSTR(" secure simple pairing (controller support)"), 0x80); } else if(hcibuf[6] == 1) { // Page 1 - Notify(PSTR("\r\nLocal "), 0x80); + Notify(PSTR("\r\nDongle "), 0x80); if(hcibuf[8 + 0] & (1U << 0)) Notify(PSTR("supports"), 0x80); else Notify(PSTR("does NOT support"), 0x80); - Notify(PSTR(" secure simple paring (host support)"), 0x80); + Notify(PSTR(" secure simple pairing (host support)"), 0x80); } #endif } @@ -671,14 +671,14 @@ void BTD::HCI_event_task() { Notify(PSTR("supports"), 0x80); else Notify(PSTR("does NOT support"), 0x80); - Notify(PSTR(" secure simple paring (controller 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 paring (host support)"), 0x80); + Notify(PSTR(" secure simple pairing (host support)"), 0x80); } #endif } @@ -814,18 +814,28 @@ void BTD::HCI_task() { case HCI_LOCAL_EXTENDED_FEATURES_STATE: if(hci_check_flag(HCI_FLAG_LOCAL_EXTENDED_FEATURES)) { + hci_write_simple_pairing_mode(true); + hci_state = HCI_WRITE_SIMPLE_PAIRING_STATE; + } + break; + + case HCI_WRITE_SIMPLE_PAIRING_STATE: + if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSimple pairing was enabled"), 0x80); +#endif if(btdName != NULL) { - hci_set_local_name(btdName); - hci_state = HCI_SET_NAME_STATE; + hci_write_local_name(btdName); + hci_state = HCI_WRITE_NAME_STATE; } else hci_state = HCI_CHECK_DEVICE_SERVICE; } break; - case HCI_SET_NAME_STATE: + case HCI_WRITE_NAME_STATE: if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) { #ifdef DEBUG_USB_HOST - Notify(PSTR("\r\nThe name is set to: "), 0x80); + Notify(PSTR("\r\nThe name was set to: "), 0x80); NotifyStr(btdName, 0x80); #endif hci_state = HCI_CHECK_DEVICE_SERVICE; @@ -1179,7 +1189,7 @@ void BTD::hci_read_remote_extended_features(uint8_t page_number) { HCI_Command(hcibuf, 6); } -void BTD::hci_set_local_name(const char* name) { +void BTD::hci_write_local_name(const char* name) { hcibuf[0] = 0x13; // HCI OCF = 13 hcibuf[1] = 0x03 << 2; // HCI OGF = 3 hcibuf[2] = strlen(name) + 1; // parameter length = the length of the string + end byte @@ -1191,6 +1201,15 @@ void BTD::hci_set_local_name(const char* name) { HCI_Command(hcibuf, 4 + strlen(name)); } +void BTD::hci_write_simple_pairing_mode(bool enable) { + hcibuf[0] = 0x56; // HCI OCF = 56 + hcibuf[1] = 0x03 << 2; // HCI OGF = 3 + hcibuf[2] = 1; // parameter length = 1 + hcibuf[3] = enable ? 1 : 0; + + HCI_Command(hcibuf, 4); +} + void BTD::hci_inquiry() { hci_clear_flag(HCI_FLAG_DEVICE_FOUND); hcibuf[0] = 0x01; diff --git a/BTD.h b/BTD.h index e7ab9f96..ed179937 100644 --- a/BTD.h +++ b/BTD.h @@ -45,7 +45,7 @@ #define HCI_CLASS_STATE 2 #define HCI_BDADDR_STATE 3 #define HCI_LOCAL_VERSION_STATE 4 -#define HCI_SET_NAME_STATE 5 +#define HCI_WRITE_NAME_STATE 5 #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 device @@ -59,8 +59,9 @@ #define HCI_DISABLE_SCAN_STATE 14 #define HCI_DONE_STATE 15 #define HCI_DISCONNECT_STATE 16 -#define HCI_LOCAL_EXTENDED_FEATURES_STATE 17 -#define HCI_REMOTE_EXTENDED_FEATURES_STATE 18 +#define HCI_LOCAL_EXTENDED_FEATURES_STATE 17 +#define HCI_WRITE_SIMPLE_PAIRING_STATE 18 +#define HCI_REMOTE_EXTENDED_FEATURES_STATE 19 /* HCI event flags*/ #define HCI_FLAG_CMD_COMPLETE (1UL << 0) @@ -72,7 +73,7 @@ #define HCI_FLAG_READ_VERSION (1UL << 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_LOCAL_EXTENDED_FEATURES (1UL << 9) #define HCI_FLAG_REMOTE_EXTENDED_FEATURES (1UL << 10) /* Macros for HCI event flag tests */ @@ -333,7 +334,9 @@ public: * Set the local name of the Bluetooth dongle. * @param name Desired name. */ - void hci_set_local_name(const char* name); + void hci_write_local_name(const char* name); + + void hci_write_simple_pairing_mode(bool enable); /** Enable visibility to other Bluetooth devices. */ void hci_write_scan_enable(); /** Disable visibility to other Bluetooth devices. */ diff --git a/BTHID.cpp b/BTHID.cpp index d32e9aa6..225aa1f8 100644 --- a/BTHID.cpp +++ b/BTHID.cpp @@ -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) : @@ -54,6 +54,18 @@ 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 (l2capinbuf[7], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (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 (l2capinbuf[i + 8], 0x80); + Notify(PSTR(" "), 0x80); + } + if(!pBtd->l2capConnectionClaimed && pBtd->incomingHIDDevice && !connected && !activeConnection) { if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) { if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) { From 7714e807f6ea505bca8aabe8f75c3f82d4789b8b Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Wed, 3 Jul 2019 00:30:18 +0200 Subject: [PATCH 04/93] Added SDP support to the BTHID, as needed for the Xbox One S controller --- BTD.cpp | 61 ++++++++------- BTD.h | 12 +++ BTHID.cpp | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- BTHID.h | 10 +++ SPP.h | 6 -- 5 files changed, 268 insertions(+), 38 deletions(-) diff --git a/BTD.cpp b/BTD.cpp index 36e7e705..20d6b9cf 100644 --- a/BTD.cpp +++ b/BTD.cpp @@ -34,6 +34,7 @@ bAddress(0), // Device address - mandatory bNumEP(1), // If config descriptor needs to be parsed qNextPollTime(0), // Reset NextPollTime pollInterval(0), +simple_pairing_supported(false), bPollEnable(false) // Don't start polling before dongle is connected { for(uint8_t i = 0; i < BTD_NUM_SERVICES; i++) @@ -321,6 +322,7 @@ void BTD::Initialize() { qNextPollTime = 0; // Reset next poll time pollInterval = 0; bPollEnable = false; // Don't start polling before dongle is connected + simple_pairing_supported = false; } /* Extracts interrupt-IN, bulk-IN, bulk-OUT endpoint information from config descriptor */ @@ -431,10 +433,13 @@ void BTD::HCI_event_task() { #ifdef DEBUG_USB_HOST if(hcibuf[6] == 0) { // Page 0 Notify(PSTR("\r\nDongle "), 0x80); - if(hcibuf[8 + 6] & (1U << 3)) + if(hcibuf[8 + 6] & (1U << 3)) { + simple_pairing_supported = true; Notify(PSTR("supports"), 0x80); - else + } else { + simple_pairing_supported = false; Notify(PSTR("does NOT support"), 0x80); + } Notify(PSTR(" secure simple pairing (controller support)"), 0x80); } else if(hcibuf[6] == 1) { // Page 1 Notify(PSTR("\r\nDongle "), 0x80); @@ -485,7 +490,7 @@ void BTD::HCI_event_task() { case EV_INQUIRY_RESULT: if(hcibuf[2]) { // Check that there is more than zero responses -#if defined(EXTRADEBUG) && 0 +#ifdef EXTRADEBUG Notify(PSTR("\r\nNumber of responses: "), 0x80); Notify(hcibuf[2], 0x80); #endif @@ -495,7 +500,7 @@ void BTD::HCI_event_task() { for(uint8_t j = 0; j < 3; j++) classOfDevice[j] = hcibuf[j + 4 + offset]; -#if defined(EXTRADEBUG) && 0 +#ifdef EXTRADEBUG Notify(PSTR("\r\nClass of device: "), 0x80); D_PrintHex (classOfDevice[2], 0x80); Notify(PSTR(" "), 0x80); @@ -710,8 +715,8 @@ void BTD::HCI_event_task() { /* We will just ignore the following events */ case EV_MAX_SLOTS_CHANGE: - break; case EV_NUM_COMPLETE_PKT: + break; case EV_ROLE_CHANGED: case EV_PAGE_SCAN_REP_MODE: case EV_LOOPBACK_COMMAND: @@ -806,29 +811,14 @@ void BTD::HCI_task() { case HCI_LOCAL_VERSION_STATE: // The local version is used by the PS3BT class if(hci_check_flag(HCI_FLAG_READ_VERSION)) { - hci_read_local_extended_features(0); // "Requests the normal LMP features as returned by Read_Local_Supported_Features" - //hci_read_local_extended_features(1); // Read page 1 - hci_state = HCI_LOCAL_EXTENDED_FEATURES_STATE; - } - break; - - case HCI_LOCAL_EXTENDED_FEATURES_STATE: - if(hci_check_flag(HCI_FLAG_LOCAL_EXTENDED_FEATURES)) { - hci_write_simple_pairing_mode(true); - hci_state = HCI_WRITE_SIMPLE_PAIRING_STATE; - } - break; - - case HCI_WRITE_SIMPLE_PAIRING_STATE: - if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) { -#ifdef DEBUG_USB_HOST - Notify(PSTR("\r\nSimple pairing was enabled"), 0x80); -#endif if(btdName != NULL) { hci_write_local_name(btdName); hci_state = HCI_WRITE_NAME_STATE; - } else - hci_state = HCI_CHECK_DEVICE_SERVICE; + } else { + hci_read_local_extended_features(0); // "Requests the normal LMP features as returned by Read_Local_Supported_Features" + //hci_read_local_extended_features(1); // Read page 1 + hci_state = HCI_LOCAL_EXTENDED_FEATURES_STATE; + } } break; @@ -837,6 +827,27 @@ void BTD::HCI_task() { #ifdef DEBUG_USB_HOST Notify(PSTR("\r\nThe name was set to: "), 0x80); NotifyStr(btdName, 0x80); +#endif + hci_read_local_extended_features(0); // "Requests the normal LMP features as returned by Read_Local_Supported_Features" + //hci_read_local_extended_features(1); // Read page 1 + hci_state = HCI_LOCAL_EXTENDED_FEATURES_STATE; + } + break; + + case HCI_LOCAL_EXTENDED_FEATURES_STATE: + if(hci_check_flag(HCI_FLAG_LOCAL_EXTENDED_FEATURES)) { + if(simple_pairing_supported) { + hci_write_simple_pairing_mode(true); + hci_state = HCI_WRITE_SIMPLE_PAIRING_STATE; + } else + hci_state = HCI_CHECK_DEVICE_SERVICE; + } + break; + + case HCI_WRITE_SIMPLE_PAIRING_STATE: + if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSimple pairing was enabled"), 0x80); #endif hci_state = HCI_CHECK_DEVICE_SERVICE; } diff --git a/BTD.h b/BTD.h index ed179937..c12e325b 100644 --- a/BTD.h +++ b/BTD.h @@ -191,6 +191,17 @@ #define HID_CTRL_PSM 0x11 // HID_Control PSM Value #define HID_INTR_PSM 0x13 // HID_Interrupt PSM Value +/* Used for SDP */ +#define SDP_SERVICE_SEARCH_REQUEST 0x02 +#define SDP_SERVICE_SEARCH_RESPONSE 0x03 +#define SDP_SERVICE_ATTRIBUTE_REQUEST 0x04 +#define SDP_SERVICE_ATTRIBUTE_RESPONSE 0x05 +#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 PNP_INFORMATION_UUID 0x1200 +#define SERIALPORT_UUID 0x1101 // See http://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm +#define L2CAP_UUID 0x0100 + // Used to determine if it is a Bluetooth dongle #define WI_SUBCLASS_RF 0x01 // RF Controller #define WI_PROTOCOL_BT 0x01 // Bluetooth Programming Interface @@ -553,6 +564,7 @@ private: uint16_t PID, VID; // PID and VID of device connected uint8_t pollInterval; + bool simple_pairing_supported; bool bPollEnable; bool pairWiiUsingSync; // True if pairing was done using the Wii SYNC button. diff --git a/BTHID.cpp b/BTHID.cpp index 225aa1f8..3928c7dd 100644 --- a/BTHID.cpp +++ b/BTHID.cpp @@ -41,15 +41,20 @@ protocolMode(USB_HID_BOOT_PROTOCOL) { void BTHID::Reset() { connected = false; activeConnection = false; + SDPConnected = false; l2cap_event_flag = 0; // Reset flags + l2cap_sdp_state = L2CAP_SDP_WAIT; l2cap_state = L2CAP_WAIT; ResetBTHID(); } void BTHID::disconnect() { // Use this void to disconnect the device + if(SDPConnected) + pBtd->l2cap_disconnection_request(hci_handle, ++identifier, sdp_scid, sdp_dcid); // 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, ++identifier, interrupt_scid, interrupt_dcid); Reset(); + l2cap_sdp_state = L2CAP_DISCONNECT_RESPONSE; l2cap_state = L2CAP_INTERRUPT_DISCONNECT; } @@ -59,13 +64,22 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { Notify(PSTR(" "), 0x80); D_PrintHex (l2capinbuf[6], 0x80); - Notify(PSTR("\r\nData: "), 0x80); - Notify(PSTR("\r\n"), 0x80); + Notify(PSTR(", data: "), 0x80); for(uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) { D_PrintHex (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) { + pBtd->sdpConnectionClaimed = true; + hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection + l2cap_sdp_state = L2CAP_SDP_WAIT; // Reset state + } + } + } + if(!pBtd->l2capConnectionClaimed && pBtd->incomingHIDDevice && !connected && !activeConnection) { if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) { if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) { @@ -124,7 +138,12 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { Notify(PSTR(" Identifier: "), 0x80); D_PrintHex (l2capinbuf[9], 0x80); #endif - if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) { + if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == SDP_PSM) { + identifier = l2capinbuf[9]; + sdp_scid[0] = l2capinbuf[14]; + sdp_scid[1] = l2capinbuf[15]; + l2cap_set_flag(L2CAP_FLAG_CONNECTION_SDP_REQUEST); + } else if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) { identifier = l2capinbuf[9]; control_scid[0] = l2capinbuf[14]; control_scid[1] = l2capinbuf[15]; @@ -137,7 +156,11 @@ 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] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { + if(l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) { + //Notify(PSTR("\r\nSDP Configuration Complete"), 0x80); + identifier = l2capinbuf[9]; + l2cap_set_flag(L2CAP_FLAG_CONFIG_SDP_SUCCESS); + } else if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { //Notify(PSTR("\r\nHID Control Configuration Complete"), 0x80); identifier = l2capinbuf[9]; l2cap_set_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS); @@ -148,7 +171,10 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { } } } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) { - if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { + if(l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) { + //Notify(PSTR("\r\nSDP Configuration Request"), 0x80); + pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], sdp_scid); + } else 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]) { @@ -156,7 +182,13 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { 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]) { + if(l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnect Request: SDP Channel"), 0x80); +#endif + identifier = l2capinbuf[9]; + l2cap_set_flag(L2CAP_FLAG_DISCONNECT_SDP_REQUEST); + } else if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { #ifdef DEBUG_USB_HOST Notify(PSTR("\r\nDisconnect Request: Control Channel"), 0x80); #endif @@ -172,7 +204,11 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { Reset(); } } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) { - if(l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) { + if(l2capinbuf[12] == sdp_scid[0] && l2capinbuf[13] == sdp_scid[1]) { + //Notify(PSTR("\r\nDisconnect Response: SDP Channel"), 0x80); + 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); identifier = l2capinbuf[9]; l2cap_set_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE); @@ -181,6 +217,12 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { identifier = l2capinbuf[9]; l2cap_set_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE); } + } 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]); } #ifdef EXTRADEBUG else { @@ -188,6 +230,97 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { Notify(PSTR("\r\nL2CAP Unknown Signaling Command: "), 0x80); D_PrintHex (l2capinbuf[8], 0x80); } +#endif + } else if(l2capinbuf[6] == sdp_dcid[0] && l2capinbuf[7] == sdp_dcid[1]) { // SDP + if(l2capinbuf[8] == SDP_SERVICE_SEARCH_REQUEST) { + Notify(PSTR("\r\nSDP_SERVICE_SEARCH_REQUEST"), 0x80); + + // Send response + 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 + + l2capoutbuf[5] = 0x00; // MSB TotalServiceRecordCount + l2capoutbuf[6] = 0x00; // LSB TotalServiceRecordCount = 0 + + l2capoutbuf[7] = 0x00; // MSB CurrentServiceRecordCount + l2capoutbuf[8] = 0x00; // LSB CurrentServiceRecordCount = 0 + + 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 + } else if(l2capinbuf[8] == SDP_SERVICE_ATTRIBUTE_REQUEST) { + Notify(PSTR("\r\nSDP_SERVICE_ATTRIBUTE_REQUEST"), 0x80); + + // Send response + l2capoutbuf[0] = SDP_SERVICE_ATTRIBUTE_RESPONSE; + l2capoutbuf[1] = l2capinbuf[9];//transactionIDHigh; + l2capoutbuf[2] = l2capinbuf[10];//transactionIDLow; + + l2capoutbuf[3] = 0x00; // MSB Parameter Length + l2capoutbuf[4] = 0x05; // LSB Parameter Length = 5 + + l2capoutbuf[5] = 0x00; // MSB AttributeListByteCount + l2capoutbuf[6] = 0x02; // LSB AttributeListByteCount = 2 + + // TODO: What to send? + 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); + } else if(l2capinbuf[8] == SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU) { + Notify(PSTR("\r\nSDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU"), 0x80); + +#ifdef EXTRADEBUG + 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 + uuid = (l2capinbuf[18] << 8 | l2capinbuf[19]); + else // Short UUID + uuid = (l2capinbuf[16] << 8 | l2capinbuf[17]); + D_PrintHex (uuid, 0x80); + + Notify(PSTR("\r\nLength: "), 0x80); + uint16_t length = l2capinbuf[11] << 8 | l2capinbuf[12]; + D_PrintHex (length, 0x80); + Notify(PSTR("\r\nData: "), 0x80); + for(uint8_t i = 0; i < length; i++) { + D_PrintHex (l2capinbuf[13 + i], 0x80); + Notify(PSTR(" "), 0x80); + } +#endif + + serviceNotSupported(l2capinbuf[9], l2capinbuf[10]); // The service is not supported + } +#ifdef EXTRADEBUG + else { + Notify(PSTR("\r\nUnknown PDU: "), 0x80); + D_PrintHex (l2capinbuf[8], 0x80); + } #endif } else if(l2capinbuf[6] == interrupt_dcid[0] && l2capinbuf[7] == interrupt_dcid[1]) { // l2cap_interrupt #ifdef PRINTREPORT @@ -243,10 +376,59 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { } } #endif + SDP_task(); L2CAP_task(); } } +void BTHID::SDP_task() { + switch(l2cap_sdp_state) { + case L2CAP_SDP_WAIT: + if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_SDP_REQUEST)) { + l2cap_clear_flag(L2CAP_FLAG_CONNECTION_SDP_REQUEST); // Clear flag +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSDP Incoming Connection Request"), 0x80); +#endif + pBtd->l2cap_connection_response(hci_handle, identifier, sdp_dcid, sdp_scid, PENDING); + delay(1); + pBtd->l2cap_connection_response(hci_handle, identifier, sdp_dcid, sdp_scid, SUCCESSFUL); + identifier++; + delay(1); + pBtd->l2cap_config_request(hci_handle, identifier, sdp_scid); + l2cap_sdp_state = L2CAP_SDP_SUCCESS; + } else if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_SDP_REQUEST)) { + l2cap_clear_flag(L2CAP_FLAG_DISCONNECT_SDP_REQUEST); // Clear flag + SDPConnected = false; +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnected SDP Channel"), 0x80); +#endif + pBtd->l2cap_disconnection_response(hci_handle, identifier, sdp_dcid, sdp_scid); + } + break; + case L2CAP_SDP_SUCCESS: + if(l2cap_check_flag(L2CAP_FLAG_CONFIG_SDP_SUCCESS)) { + l2cap_clear_flag(L2CAP_FLAG_CONFIG_SDP_SUCCESS); // Clear flag +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSDP Successfully Configured"), 0x80); +#endif + SDPConnected = true; + l2cap_sdp_state = L2CAP_SDP_WAIT; + } + break; + + case L2CAP_DISCONNECT_RESPONSE: // This is for both disconnection response from the RFCOMM and SDP channel if they were connected + if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_RESPONSE)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnected L2CAP Connection"), 0x80); +#endif + pBtd->hci_disconnect(hci_handle); + hci_handle = -1; // Reset handle + Reset(); + } + break; + } +} + void BTHID::L2CAP_task() { switch(l2cap_state) { /* These states are used if the HID device is the host */ @@ -383,6 +565,27 @@ void BTHID::Run() { } } +void BTHID::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 BTHID::serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow) { // See page 235 in the Bluetooth specs + l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU; + 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); +} + /************************************************************/ /* HID Commands */ diff --git a/BTHID.h b/BTHID.h index 7d1d1116..b15649b5 100644 --- a/BTHID.h +++ b/BTHID.h @@ -141,20 +141,30 @@ protected: /** L2CAP source CID for HID_Interrupt */ uint8_t interrupt_scid[2]; + uint8_t l2cap_sdp_state; + uint8_t sdp_scid[2]; // L2CAP source CID for SDP + private: HIDReportParser *pRptParser[NUM_PARSERS]; // Pointer to HIDReportParsers. + uint8_t l2capoutbuf[BULK_MAXPKTSIZE]; // General purpose buffer for l2cap out data + void SDP_Command(uint8_t* data, uint8_t nbytes); + void serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow); + /** Set report protocol. */ void setProtocol(); uint8_t protocolMode; + void SDP_task(); void L2CAP_task(); // L2CAP state machine bool activeConnection; // Used to indicate if it already has established a connection + bool SDPConnected; /* Variables used for L2CAP communication */ uint8_t control_dcid[2]; // L2CAP device CID for HID_Control - Always 0x0070 uint8_t interrupt_dcid[2]; // L2CAP device CID for HID_Interrupt - Always 0x0071 + uint8_t sdp_dcid[2]; uint8_t l2cap_state; }; #endif diff --git a/SPP.h b/SPP.h index bb3027b4..116296ca 100644 --- a/SPP.h +++ b/SPP.h @@ -20,12 +20,6 @@ #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 From 8d7265f92c02e258538c7f77d606f8d6c551fe2d Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Wed, 3 Jul 2019 00:54:27 +0200 Subject: [PATCH 05/93] Try to figure out which HCI command that is failing --- BTD.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/BTD.cpp b/BTD.cpp index 20d6b9cf..4855c65c 100644 --- a/BTD.cpp +++ b/BTD.cpp @@ -466,6 +466,12 @@ void BTD::HCI_event_task() { #ifdef DEBUG_USB_HOST Notify(PSTR("\r\nHCI Command Failed: "), 0x80); D_PrintHex (hcibuf[2], 0x80); + Notify(PSTR("\r\nNum HCI Command Packets: "), 0x80); + D_PrintHex (hcibuf[3], 0x80); + Notify(PSTR("\r\nCommand Opcode: "), 0x80); + D_PrintHex (hcibuf[4], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (hcibuf[5], 0x80); #endif } break; From ad9360b8653a9cc35291c563b8eea5e02f661c5d Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sat, 31 Aug 2019 18:22:21 +0200 Subject: [PATCH 06/93] Yet another go at getting the Xbox One S controller working It just connects and opens a SDP channel it then disconnects the SDP channel and I get a "Paring Faild: 05 (Authentication Failure)" For some reason I never receive the "IO Capability Request" event --- BTD.cpp | 64 +++++++++++++++++++- BTD.h | 11 +++- BTHID.cpp | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++---- SPP.cpp | 27 +-------- 4 files changed, 236 insertions(+), 41 deletions(-) diff --git a/BTD.cpp b/BTD.cpp index 4855c65c..ade16f4c 100644 --- a/BTD.cpp +++ b/BTD.cpp @@ -430,26 +430,34 @@ void BTD::HCI_event_task() { D_PrintHex (hcibuf[8 + i], 0x80); } #endif -#ifdef DEBUG_USB_HOST if(hcibuf[6] == 0) { // Page 0 +#ifdef DEBUG_USB_HOST Notify(PSTR("\r\nDongle "), 0x80); +#endif if(hcibuf[8 + 6] & (1U << 3)) { simple_pairing_supported = true; +#ifdef DEBUG_USB_HOST Notify(PSTR("supports"), 0x80); +#endif } else { simple_pairing_supported = false; +#ifdef DEBUG_USB_HOST Notify(PSTR("does NOT support"), 0x80); +#endif } +#ifdef DEBUG_USB_HOST Notify(PSTR(" secure simple pairing (controller support)"), 0x80); +#endif } else if(hcibuf[6] == 1) { // Page 1 +#ifdef DEBUG_USB_HOST Notify(PSTR("\r\nDongle "), 0x80); if(hcibuf[8 + 0] & (1U << 0)) Notify(PSTR("supports"), 0x80); else Notify(PSTR("does NOT support"), 0x80); Notify(PSTR(" secure simple pairing (host support)"), 0x80); - } #endif + } } hci_set_flag(HCI_FLAG_LOCAL_EXTENDED_FEATURES); @@ -650,6 +658,10 @@ 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 { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nPairing was successful"), 0x80); +#endif } } else { #ifdef DEBUG_USB_HOST @@ -744,6 +756,12 @@ void BTD::HCI_event_task() { if(hcibuf[0] != 0x00) { Notify(PSTR("\r\nUnmanaged HCI Event: "), 0x80); D_PrintHex (hcibuf[0], 0x80); + + Notify(PSTR(", data: "), 0x80); + for(uint16_t i = 0; i < hcibuf[1]; i++) { + D_PrintHex (hcibuf[2 + i], 0x80); + Notify(PSTR(" "), 0x80); + } } break; #endif @@ -939,6 +957,12 @@ void BTD::HCI_task() { 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; } @@ -1255,7 +1279,7 @@ void BTD::hci_connect() { void BTD::hci_connect(uint8_t *bdaddr) { hci_clear_flag(HCI_FLAG_CONNECT_COMPLETE | HCI_FLAG_CONNECT_EVENT); - hcibuf[0] = 0x05; + hcibuf[0] = 0x05; // HCI OCF = 5 hcibuf[1] = 0x01 << 2; // HCI OGF = 1 hcibuf[2] = 0x0D; // parameter Total Length = 13 hcibuf[3] = bdaddr[0]; // 6 octet bdaddr (LSB) @@ -1275,6 +1299,40 @@ 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 diff --git a/BTD.h b/BTD.h index c12e325b..4ba59ef3 100644 --- a/BTD.h +++ b/BTD.h @@ -196,8 +196,8 @@ #define SDP_SERVICE_SEARCH_RESPONSE 0x03 #define SDP_SERVICE_ATTRIBUTE_REQUEST 0x04 #define SDP_SERVICE_ATTRIBUTE_RESPONSE 0x05 -#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_ATTRIBUTE_REQUEST 0x06 // See the RFCOMM specs +#define SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE 0x07 // See the RFCOMM specs #define PNP_INFORMATION_UUID 0x1200 #define SERIALPORT_UUID 0x1101 // See http://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm #define L2CAP_UUID 0x0100 @@ -386,6 +386,13 @@ 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(); + /** * Connect to device. * @param bdaddr Bluetooth address of the device. diff --git a/BTHID.cpp b/BTHID.cpp index 3928c7dd..6f9e0492 100644 --- a/BTHID.cpp +++ b/BTHID.cpp @@ -30,6 +30,8 @@ protocolMode(USB_HID_BOOT_PROTOCOL) { pBtd->btdPin = pin; /* Set device cid for the control and intterrupt channelse - LSB */ + sdp_dcid[0] = 0x50; // 0x0050 + sdp_dcid[1] = 0x00; control_dcid[0] = 0x70; // 0x0070 control_dcid[1] = 0x00; interrupt_dcid[0] = 0x71; // 0x0071 @@ -59,6 +61,7 @@ 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 (l2capinbuf[7], 0x80); Notify(PSTR(" "), 0x80); @@ -69,6 +72,7 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { D_PrintHex (l2capinbuf[i + 8], 0x80); Notify(PSTR(" "), 0x80); } +*/ if(!connected) { if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) { @@ -111,14 +115,25 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { #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]) { - //Notify(PSTR("\r\nHID Control Connection Complete"), 0x80); + if(l2capinbuf[14] == sdp_dcid[0] && l2capinbuf[15] == sdp_dcid[1]) { + Notify(PSTR("\r\nSDP Connection Complete"), 0x80); + 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]) { + Notify(PSTR("\r\nHID Control Connection Complete"), 0x80); 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]) { - //Notify(PSTR("\r\nHID Interrupt Connection Complete"), 0x80); + Notify(PSTR("\r\nHID Interrupt Connection Complete"), 0x80); identifier = l2capinbuf[9]; interrupt_scid[0] = l2capinbuf[12]; interrupt_scid[1] = l2capinbuf[13]; @@ -157,28 +172,28 @@ 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]) { - //Notify(PSTR("\r\nSDP Configuration Complete"), 0x80); + Notify(PSTR("\r\nSDP Configuration Complete"), 0x80); identifier = l2capinbuf[9]; l2cap_set_flag(L2CAP_FLAG_CONFIG_SDP_SUCCESS); } else if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { - //Notify(PSTR("\r\nHID Control Configuration Complete"), 0x80); + Notify(PSTR("\r\nHID Control Configuration Complete"), 0x80); identifier = l2capinbuf[9]; l2cap_set_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); + Notify(PSTR("\r\nHID Interrupt Configuration Complete"), 0x80); 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]) { - //Notify(PSTR("\r\nSDP Configuration Request"), 0x80); + Notify(PSTR("\r\nSDP Configuration Request"), 0x80); pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], sdp_scid); } else if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { - //Notify(PSTR("\r\nHID Control Configuration Request"), 0x80); + 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); + 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) { @@ -271,6 +286,10 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { 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); @@ -279,6 +298,7 @@ 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 @@ -292,8 +312,139 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { l2capoutbuf[9] = 0x00; // No continuation state SDP_Command(l2capoutbuf, 10); - } else if(l2capinbuf[8] == SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU) { - Notify(PSTR("\r\nSDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU"), 0x80); +#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\nUUID: "), 0x80); @@ -570,7 +721,7 @@ void BTHID::SDP_Command(uint8_t* data, uint8_t nbytes) { // See page 223 in the } void BTHID::serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow) { // See page 235 in the Bluetooth specs - l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU; + l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE; l2capoutbuf[1] = transactionIDHigh; l2capoutbuf[2] = transactionIDLow; l2capoutbuf[3] = 0x00; // MSB Parameter Length diff --git a/SPP.cpp b/SPP.cpp index 009ea7bc..5e421097 100644 --- a/SPP.cpp +++ b/SPP.cpp @@ -189,7 +189,7 @@ void SPP::ACLData(uint8_t* l2capinbuf) { } #endif } else if(l2capinbuf[6] == sdp_dcid[0] && l2capinbuf[7] == sdp_dcid[1]) { // SDP - if(l2capinbuf[8] == SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU) { + if(l2capinbuf[8] == SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST) { if(((l2capinbuf[16] << 8 | l2capinbuf[17]) == SERIALPORT_UUID) || ((l2capinbuf[16] << 8 | l2capinbuf[17]) == 0x0000 && (l2capinbuf[18] << 8 | l2capinbuf[19]) == SERIALPORT_UUID)) { // Check if it's sending the full UUID, see: https://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm, we will just check the first four bytes if(firstMessage) { serialPortResponse1(l2capinbuf[9], l2capinbuf[10]); @@ -531,29 +531,8 @@ 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_PDU; - 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_PDU; + l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE; l2capoutbuf[1] = transactionIDHigh; l2capoutbuf[2] = transactionIDLow; l2capoutbuf[3] = 0x00; // MSB Parameter Length @@ -615,7 +594,7 @@ void SPP::serialPortResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLo } void SPP::serialPortResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow) { - l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU; + l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE; l2capoutbuf[1] = transactionIDHigh; l2capoutbuf[2] = transactionIDLow; l2capoutbuf[3] = 0x00; // MSB Parameter Length From 102746ef6bb2fe5f5cce428522673be1591c2b6b Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 15 Nov 2020 17:29:54 +0100 Subject: [PATCH 07/93] Xbox One S controller support is now finally working --- BTD.cpp | 186 +++++++++------------ BTD.h | 34 ++-- BTHID.cpp | 207 ++++-------------------- README.md | 11 +- SPP.cpp | 21 +++ XBOXONESBT.h | 100 ++++++++++++ XBOXONESParser.cpp | 200 +++++++++++++++++++++++ XBOXONESParser.h | 127 +++++++++++++++ controllerEnums.h | 6 + examples/Xbox/XBOXONESBT/XBOXONESBT.ino | 135 ++++++++++++++++ keywords.txt | 4 + settings.h | 2 +- 12 files changed, 726 insertions(+), 307 deletions(-) create mode 100644 XBOXONESBT.h create mode 100644 XBOXONESParser.cpp create mode 100644 XBOXONESParser.h create mode 100644 examples/Xbox/XBOXONESBT/XBOXONESBT.ino diff --git a/BTD.cpp b/BTD.cpp index ade16f4c..bd2d2587 100644 --- a/BTD.cpp +++ b/BTD.cpp @@ -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 (hcibuf[5], 0x80); - Notify(PSTR("\r\nMaximum page number: "), 0x80); - D_PrintHex (hcibuf[6], 0x80); - Notify(PSTR("\r\nExtended LMP features:"), 0x80); - for(uint8_t i = 0; i < 8; i++) { - Notify(PSTR(" "), 0x80); - D_PrintHex (hcibuf[7 + i], 0x80); - } + Notify(PSTR("\r\nReceived IO Capability Response: "), 0x80); + Notify(PSTR("\r\nIO capability: "), 0x80); + D_PrintHex (hcibuf[8], 0x80); + Notify(PSTR("\r\nOOB data present: "), 0x80); + D_PrintHex (hcibuf[9], 0x80); + Notify(PSTR("\r\nAuthentication request: "), 0x80); + D_PrintHex (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 (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 (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 (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 (hcibuf[0], 0x80); - Notify(PSTR(", data: "), 0x80); for(uint16_t i = 0; i < hcibuf[1]; i++) { D_PrintHex (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 diff --git a/BTD.h b/BTD.h index 4ba59ef3..e0b3a46d 100644 --- a/BTD.h +++ b/BTD.h @@ -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. diff --git a/BTHID.cpp b/BTHID.cpp index 6f9e0492..ad2a0ac2 100644 --- a/BTHID.cpp +++ b/BTHID.cpp @@ -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 (l2capinbuf[7], 0x80); - Notify(PSTR(" "), 0x80); - D_PrintHex (l2capinbuf[6], 0x80); - - Notify(PSTR(", data: "), 0x80); - for(uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) { - D_PrintHex (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 diff --git a/README.md b/README.md index 8b8a5d4b..ea42cdca 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ For more information about the hardware see the [Hardware Manual](http://www.cir * __Oleg Mazurov, Circuits@Home__ - * __Alexei Glushchenko, Circuits@Home__ - * Developers of the USB Core, HID, FTDI, ADK, ACM, and PL2303 libraries -* __Kristian Lauszus, TKJ Electronics__ - +* __Kristian Sloth Lauszus__ - * 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__ - * 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: * * * +#### 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: . + ### [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. diff --git a/SPP.cpp b/SPP.cpp index 5e421097..910f7db8 100644 --- a/SPP.cpp +++ b/SPP.cpp @@ -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; diff --git a/XBOXONESBT.h b/XBOXONESBT.h new file mode 100644 index 00000000..82b17d24 --- /dev/null +++ b/XBOXONESBT.h @@ -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 (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 diff --git a/XBOXONESParser.cpp b/XBOXONESParser.cpp new file mode 100644 index 00000000..e8f0064f --- /dev/null +++ b/XBOXONESParser.cpp @@ -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 (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 (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 diff --git a/XBOXONESParser.h b/XBOXONESParser.h new file mode 100644 index 00000000..41dc30b2 --- /dev/null +++ b/XBOXONESParser.h @@ -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 diff --git a/controllerEnums.h b/controllerEnums.h index 631825b2..c1a5a497 100644 --- a/controllerEnums.h +++ b/controllerEnums.h @@ -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, diff --git a/examples/Xbox/XBOXONESBT/XBOXONESBT.ino b/examples/Xbox/XBOXONESBT/XBOXONESBT.ino new file mode 100644 index 00000000..c691e9bf --- /dev/null +++ b/examples/Xbox/XBOXONESBT/XBOXONESBT.ino @@ -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 +#include + +// Satisfy the IDE, which needs to see the include statement in the ino too. +#ifdef dobogusinclude +#include +#endif +#include + +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")); + } +} diff --git a/keywords.txt b/keywords.txt index eed0eb5c..2513e98e 100644 --- a/keywords.txt +++ b/keywords.txt @@ -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 diff --git a/settings.h b/settings.h index 63be1770..2d176053 100644 --- a/settings.h +++ b/settings.h @@ -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. From f500cc9b4c639ed90502bb52fe87be1185b9347f Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 15 Nov 2020 19:02:54 +0100 Subject: [PATCH 08/93] Only use simple pairing with the Xbox One S controller, as it did not work with the PS4 --- BTD.cpp | 17 +++++++++++------ BTD.h | 3 +++ BTHID.cpp | 6 ++++-- XBOXONESBT.h | 1 + 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/BTD.cpp b/BTD.cpp index bd2d2587..32efeb86 100644 --- a/BTD.cpp +++ b/BTD.cpp @@ -29,6 +29,7 @@ connectToWii(false), pairWithWii(false), connectToHIDDevice(false), pairWithHIDDevice(false), +useSimplePairing(false), pUsb(p), // Pointer to USB class instance - mandatory bAddress(0), // Device address - mandatory bNumEP(1), // If config descriptor needs to be parsed @@ -658,7 +659,7 @@ void BTD::HCI_event_task() { #endif connectToHIDDevice = true; // Used to indicate to the BTHID service, that it should connect to this device } else { -#ifdef DEBUG_USB_HOST +#ifdef EXTRADEBUG Notify(PSTR("\r\nPairing was successful"), 0x80); #endif } @@ -823,11 +824,12 @@ void BTD::HCI_task() { if(btdName != NULL) { hci_write_local_name(btdName); hci_state = HCI_WRITE_NAME_STATE; - } else { + } else if(useSimplePairing) { hci_read_local_extended_features(0); // "Requests the normal LMP features as returned by Read_Local_Supported_Features" //hci_read_local_extended_features(1); // Read page 1 hci_state = HCI_LOCAL_EXTENDED_FEATURES_STATE; - } + } else + hci_state = HCI_CHECK_DEVICE_SERVICE; } break; @@ -837,9 +839,12 @@ void BTD::HCI_task() { Notify(PSTR("\r\nThe name was set to: "), 0x80); NotifyStr(btdName, 0x80); #endif - hci_read_local_extended_features(0); // "Requests the normal LMP features as returned by Read_Local_Supported_Features" - //hci_read_local_extended_features(1); // Read page 1 - hci_state = HCI_LOCAL_EXTENDED_FEATURES_STATE; + if(useSimplePairing) { + hci_read_local_extended_features(0); // "Requests the normal LMP features as returned by Read_Local_Supported_Features" + //hci_read_local_extended_features(1); // Read page 1 + hci_state = HCI_LOCAL_EXTENDED_FEATURES_STATE; + } else + hci_state = HCI_CHECK_DEVICE_SERVICE; } break; diff --git a/BTD.h b/BTD.h index e0b3a46d..5337dea9 100644 --- a/BTD.h +++ b/BTD.h @@ -530,6 +530,9 @@ public: return pollInterval; }; + /** Used by the drivers to enable simple pairing */ + bool useSimplePairing; + protected: /** Pointer to USB class instance. */ USB *pUsb; diff --git a/BTHID.cpp b/BTHID.cpp index ad2a0ac2..dc5ab207 100644 --- a/BTHID.cpp +++ b/BTHID.cpp @@ -258,8 +258,9 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { #endif } else if(l2capinbuf[6] == sdp_dcid[0] && l2capinbuf[7] == sdp_dcid[1]) { // SDP if(l2capinbuf[8] == SDP_SERVICE_SEARCH_REQUEST) { +#ifdef EXTRADEBUG Notify(PSTR("\r\nSDP_SERVICE_SEARCH_REQUEST"), 0x80); - +#endif // Send response l2capoutbuf[0] = SDP_SERVICE_SEARCH_RESPONSE; l2capoutbuf[1] = l2capinbuf[9];//transactionIDHigh; @@ -278,8 +279,9 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { SDP_Command(l2capoutbuf, 10); } else if(l2capinbuf[8] == SDP_SERVICE_ATTRIBUTE_REQUEST) { +#ifdef EXTRADEBUG Notify(PSTR("\r\nSDP_SERVICE_ATTRIBUTE_REQUEST"), 0x80); - +#endif // Send response l2capoutbuf[0] = SDP_SERVICE_ATTRIBUTE_RESPONSE; l2capoutbuf[1] = l2capinbuf[9];//transactionIDHigh; diff --git a/XBOXONESBT.h b/XBOXONESBT.h index 82b17d24..58395790 100644 --- a/XBOXONESBT.h +++ b/XBOXONESBT.h @@ -35,6 +35,7 @@ public: XBOXONESBT(BTD *p, bool pair = false) : BTHID(p, pair) { XBOXONESParser::Reset(); + pBtd->useSimplePairing = true; // The Xbox One S controller only works via simple pairing }; /** From e101482478c40410e09df76ebcf39e9dfa986620 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 15 Nov 2020 19:29:54 +0100 Subject: [PATCH 09/93] Updated Travis to use Python 3.9 and added the 'XBOXONESBT.ino' example --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3dc25114..6a105b8e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: python python: - - "2.7" + - "3.9" # Cache PlatformIO packages using Travis CI container-based infrastructure sudo: false @@ -69,6 +69,7 @@ env: - PLATFORMIO_CI_SRC=examples/USBH_MIDI/USBH_MIDI_dump - PLATFORMIO_CI_SRC=examples/Xbox/XBOXOLD - PLATFORMIO_CI_SRC=examples/Xbox/XBOXONE + - PLATFORMIO_CI_SRC=examples/Xbox/XBOXONESBT - PLATFORMIO_CI_SRC=examples/Xbox/XBOXRECV - PLATFORMIO_CI_SRC=examples/Xbox/XBOXUSB From edc5198976fafe323e52d4818ce8f105000598d1 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 15 Nov 2020 23:27:08 +0100 Subject: [PATCH 10/93] Fix 'warning: this statement may fall through [-Wimplicit-fallthrough=]' Closes #532 --- confdescparser.h | 4 ++++ hidescriptorparser.cpp | 6 ++++++ parsetools.cpp | 3 +++ parsetools.h | 1 + usbh_midi.cpp | 1 + 5 files changed, 15 insertions(+) diff --git a/confdescparser.h b/confdescparser.h index 54053545..5991182a 100644 --- a/confdescparser.h +++ b/confdescparser.h @@ -108,12 +108,14 @@ bool ConfigDescParser::ParseDescriptor theBuffer.valueSize = 2; valParser.Initialize(&theBuffer); stateParseDescr = 1; + // fall through case 1: if(!valParser.Parse(pp, pcntdn)) return false; dscrLen = *((uint8_t*)theBuffer.pValue); dscrType = *((uint8_t*)theBuffer.pValue + 1); stateParseDescr = 2; + // fall through case 2: // This is a sort of hack. Assuming that two bytes are all ready in the buffer // the pointer is positioned two bytes ahead in order for the rest of descriptor @@ -122,6 +124,7 @@ bool ConfigDescParser::ParseDescriptor // in the buffer. theBuffer.pValue = varBuffer + 2; stateParseDescr = 3; + // fall through case 3: switch(dscrType) { case USB_DESCRIPTOR_INTERFACE: @@ -135,6 +138,7 @@ bool ConfigDescParser::ParseDescriptor theBuffer.valueSize = dscrLen - 2; valParser.Initialize(&theBuffer); stateParseDescr = 4; + // fall through case 4: switch(dscrType) { case USB_DESCRIPTOR_CONFIGURATION: diff --git a/hidescriptorparser.cpp b/hidescriptorparser.cpp index 43e3f7d6..42e3103b 100644 --- a/hidescriptorparser.cpp +++ b/hidescriptorparser.cpp @@ -1113,16 +1113,19 @@ uint8_t ReportDescParserBase::ParseItem(uint8_t **pp, uint16_t *pcntdn) { if(!pcntdn) return enErrorIncomplete; + // fall through case 1: //USBTRACE2("\r\niSz:",itemSize); theBuffer.valueSize = itemSize; valParser.Initialize(&theBuffer); itemParseState = 2; + // fall through case 2: if(!valParser.Parse(pp, pcntdn)) return enErrorIncomplete; itemParseState = 3; + // fall through case 3: { uint8_t data = *((uint8_t*)varBuffer); @@ -1448,14 +1451,17 @@ uint8_t ReportDescParser2::ParseItem(uint8_t **pp, uint16_t *pcntdn) { if(!pcntdn) return enErrorIncomplete; + // fall through case 1: theBuffer.valueSize = itemSize; valParser.Initialize(&theBuffer); itemParseState = 2; + // fall through case 2: if(!valParser.Parse(pp, pcntdn)) return enErrorIncomplete; itemParseState = 3; + // fall through case 3: { uint8_t data = *((uint8_t*)varBuffer); diff --git a/parsetools.cpp b/parsetools.cpp index 1b5efaf3..47c0d2a1 100644 --- a/parsetools.cpp +++ b/parsetools.cpp @@ -44,6 +44,7 @@ bool PTPListParser::Parse(uint8_t **pp, uint16_t *pcntdn, PTP_ARRAY_EL_FUNC pf, pBuf->valueSize = lenSize; theParser.Initialize(pBuf); nStage = 1; + // fall through case 1: if(!theParser.Parse(pp, pcntdn)) @@ -53,11 +54,13 @@ bool PTPListParser::Parse(uint8_t **pp, uint16_t *pcntdn, PTP_ARRAY_EL_FUNC pf, arLen = (pBuf->valueSize >= 4) ? *((uint32_t*)pBuf->pValue) : (uint32_t)(*((uint16_t*)pBuf->pValue)); arLenCntdn = arLen; nStage = 2; + // fall through case 2: pBuf->valueSize = valSize; theParser.Initialize(pBuf); nStage = 3; + // fall through case 3: for(; arLenCntdn; arLenCntdn--) { diff --git a/parsetools.h b/parsetools.h index f7525369..7812e938 100644 --- a/parsetools.h +++ b/parsetools.h @@ -79,6 +79,7 @@ public: case 0: countDown = bytes_to_skip; nStage++; + // fall through case 1: for(; countDown && (*pcntdn); countDown--, (*pp)++, (*pcntdn)--); diff --git a/usbh_midi.cpp b/usbh_midi.cpp index 104eb816..a9f0f719 100644 --- a/usbh_midi.cpp +++ b/usbh_midi.cpp @@ -589,6 +589,7 @@ uint8_t USBH_MIDI::SendSysEx(uint8_t *dataptr, uint16_t datasize, uint8_t nCable break; case 3 : buf[wptr] = (nCable << 4) | 0x7; //x7 SysEx ends with following three bytes. + // fall through default : wptr++; buf[wptr++] = *(dataptr++); From 1121d8d9ad85f7ac55fb47de4e322a0f2614e506 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 15 Nov 2020 23:29:05 +0100 Subject: [PATCH 11/93] Updated my email and full name --- README.md | 6 +++--- library.json | 6 +++--- library.properties | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 3329278a..cac71f6e 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,10 @@ For more information about the hardware see the [Hardware Manual](https://chome. # Developed By -* __Oleg Mazurov - -* __Alexei Glushchenko, Circuits@Home__ - +* __Oleg Mazurov__ - +* __Alexei Glushchenko__ - * Developers of the USB Core, HID, FTDI, ADK, ACM, and PL2303 libraries -* __Kristian Sloth Lauszus__ - +* __Kristian Sloth Lauszus__ - * 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__ - * Major contributor to mass storage code diff --git a/library.json b/library.json index 5baffe60..76e1069f 100644 --- a/library.json +++ b/library.json @@ -15,9 +15,9 @@ "email": "alex-gl@mail.ru" }, { - "name": "Kristian Lauszus", - "email": "kristianl@tkjelectronics.com", - "url": "http://tkjelectronics.com", + "name": "Kristian Sloth Lauszus", + "email": "lauszus@gmail.com", + "url": "https://lauszus.com", "maintainer": true }, { diff --git a/library.properties b/library.properties index 2dd60a44..179a515c 100644 --- a/library.properties +++ b/library.properties @@ -1,7 +1,7 @@ name=USB Host Shield Library 2.0 version=1.3.2 -author=Oleg Mazurov (Circuits@Home) , Kristian Lauszus (TKJ Electronics) , Andrew Kroll , Alexei Glushchenko (Circuits@Home) -maintainer=Oleg Mazurov (Circuits@Home) , Kristian Lauszus (TKJ Electronics) , Andrew Kroll +author=Oleg Mazurov (Circuits@Home) , Kristian Sloth Lauszus , Andrew Kroll , Alexei Glushchenko (Circuits@Home) +maintainer=Oleg Mazurov (Circuits@Home) , Kristian Sloth Lauszus , Andrew Kroll sentence=Revision 2.0 of MAX3421E-based USB Host Shield Library. paragraph=Supports HID devices, FTDI, ADK, ACM, PL2303, Bluetooth HID devices, SPP communication and mass storage devices. Furthermore it supports PS3, PS4, PS Buzz, Wii and Xbox controllers. category=Other From 96aae6667b6d7fd2d6dbd0ce7793af34080a9c1c Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 15 Nov 2020 23:31:27 +0100 Subject: [PATCH 12/93] Do not compile the Wii.ino example on the Arduino Uno, as it will not fit --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fd20ffa6..89b32847 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,7 +31,7 @@ env: - PLATFORMIO_CI_SRC=examples/Bluetooth/PS4BT - PLATFORMIO_CI_SRC=examples/Bluetooth/SPP - PLATFORMIO_CI_SRC=examples/Bluetooth/SPPMulti - - PLATFORMIO_CI_SRC=examples/Bluetooth/Wii + - PLATFORMIO_CI_SRC=examples/Bluetooth/Wii SKIP_UNO=true - PLATFORMIO_CI_SRC=examples/Bluetooth/WiiBalanceBoard SKIP_UNO=true - PLATFORMIO_CI_SRC=examples/Bluetooth/WiiIRCamera PLATFORMIO_BUILD_FLAGS="-DWIICAMERA" SKIP_UNO=true - PLATFORMIO_CI_SRC=examples/Bluetooth/WiiMulti SKIP_UNO=true From 0519b43456b16228e284207fe79a39c7d4d688a3 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 15 Nov 2020 23:32:30 +0100 Subject: [PATCH 13/93] Do not compile the bidirectional_converter.ino for Teensy 3.5 and 3.6, as there is some conflict with the MIDI library See: https://travis-ci.org/github/felis/USB_Host_Shield_2.0/jobs/743787235 --- .travis.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 89b32847..1b1f1378 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,7 +62,8 @@ env: - PLATFORMIO_CI_SRC=examples/PSBuzz # - PLATFORMIO_CI_SRC=examples/testusbhostFAT - PLATFORMIO_CI_SRC=examples/USB_desc - - PLATFORMIO_CI_SRC=examples/USBH_MIDI/bidirectional_converter + # See: https://travis-ci.org/github/felis/USB_Host_Shield_2.0/jobs/743787235 + - PLATFORMIO_CI_SRC=examples/USBH_MIDI/bidirectional_converter SKIP_TEENSY35=true SKIP_TEENSY36=true - PLATFORMIO_CI_SRC=examples/USBH_MIDI/eVY1_sample - PLATFORMIO_CI_SRC=examples/USBH_MIDI/USB_MIDI_converter - PLATFORMIO_CI_SRC=examples/USBH_MIDI/USB_MIDI_converter_multi @@ -87,7 +88,9 @@ install: script: - if [[ -z "$SKIP_UNO" ]]; then UNO="--board=uno"; fi - - platformio ci --lib="." $UNO --board=genuino101 --board=teensy30 --board=teensy31 --board=teensy35 --board=teensy36 --board=teensylc --board=esp12e --board=nodemcu --board=esp32dev + - if [[ -z "$SKIP_TEENSY35" ]]; then TEENSY35="--board=teensy35"; fi + - if [[ -z "$SKIP_TEENSY36" ]]; then TEENSY36="--board=teensy36"; fi + - platformio ci --lib="." $UNO --board=genuino101 --board=teensy30 --board=teensy31 $TEENSY35 $TEENSY36 --board=teensylc --board=esp12e --board=nodemcu --board=esp32dev - platformio ci --lib="." --board=due --project-option="build_flags=-Wno-misleading-indentation" # Workaround https://travis-ci.org/felis/USB_Host_Shield_2.0/jobs/569237654 and https://github.com/arduino/ArduinoCore-sam/issues/69 before_deploy: From 71aeadeab1d67714306662856778f7e39c7cb534 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Tue, 17 Nov 2020 15:20:13 +0100 Subject: [PATCH 14/93] Added support for Teensy 4.0 and 4.1 Fixes #529, fixes #560 --- .gitignore | 3 +- .travis.yml | 2 +- README.md | 2 +- avrpins.h | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 7e69f457..6daf2ebc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.bak *.zip *.rar -build/ \ No newline at end of file +build/ +venv/ diff --git a/.travis.yml b/.travis.yml index 1b1f1378..1f5eb687 100644 --- a/.travis.yml +++ b/.travis.yml @@ -90,7 +90,7 @@ script: - if [[ -z "$SKIP_UNO" ]]; then UNO="--board=uno"; fi - if [[ -z "$SKIP_TEENSY35" ]]; then TEENSY35="--board=teensy35"; fi - if [[ -z "$SKIP_TEENSY36" ]]; then TEENSY36="--board=teensy36"; fi - - platformio ci --lib="." $UNO --board=genuino101 --board=teensy30 --board=teensy31 $TEENSY35 $TEENSY36 --board=teensylc --board=esp12e --board=nodemcu --board=esp32dev + - platformio ci --lib="." $UNO --board=genuino101 --board=teensy30 --board=teensy31 $TEENSY35 $TEENSY36 --board=teensylc --board=teensy40 --board=teensy41 --board=esp12e --board=nodemcu --board=esp32dev - platformio ci --lib="." --board=due --project-option="build_flags=-Wno-misleading-indentation" # Workaround https://travis-ci.org/felis/USB_Host_Shield_2.0/jobs/569237654 and https://github.com/arduino/ArduinoCore-sam/issues/69 before_deploy: diff --git a/README.md b/README.md index cac71f6e..40b51d91 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ Currently the following boards are supported by the library: * Arduino Due, Intel Galileo, Intel Galileo 2, and Intel Edison * Note that the Intel Galileo uses pin 2 and 3 as INT and SS pin respectively by default, so some modifications to the shield are needed. See the "Interface modifications" section in the [hardware manual](https://chome.nerpa.tech/usb-host-shield-hardware-manual) for more information. * Note native USB host is not supported on any of these platforms. You will have to use the shield for now. -* Teensy (Teensy++ 1.0, Teensy 2.0, Teensy++ 2.0, Teensy 3.x, and Teensy LC) +* Teensy (Teensy++ 1.0, Teensy 2.0, Teensy++ 2.0, Teensy 3.x, Teensy LC and Teensy 4.x) * Note if you are using the Teensy 3.x you should download this SPI library as well: . You should then add ```#include ``` to your .ino file. * Balanduino * Sanguino diff --git a/avrpins.h b/avrpins.h index d5c7c4aa..8598d93d 100644 --- a/avrpins.h +++ b/avrpins.h @@ -814,6 +814,7 @@ public: #define pgm_read_pointer(p) pgm_read_dword(p) #if defined(CORE_TEENSY) && (defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)) +// Teensy 3.x #include "core_pins.h" #include "avr_emulation.h" @@ -913,6 +914,7 @@ MAKE_PIN(P63, CORE_PIN63_PORTREG, CORE_PIN63_BIT, CORE_PIN63_CONFIG); #undef MAKE_PIN #elif defined(CORE_TEENSY) && (defined(__MKL26Z64__)) +// Teensy-LC // we could get lower level by making these macros work properly. // for now just use the semi optimised version, it costs a lookup in the pin pgm table per op @@ -976,6 +978,91 @@ MAKE_PIN(P26, CORE_PIN26_PORTREG, 26, CORE_PIN26_CONFIG); #undef MAKE_PIN +#elif defined(__IMXRT1062__) && (defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41)) +// Teensy 4.x + +#include "core_pins.h" + +#define MAKE_PIN(className, pin) \ +class className { \ +public: \ + static void Set() { \ + digitalWriteFast(pin, HIGH);\ + } \ + static void Clear() { \ + digitalWriteFast(pin, LOW); \ + } \ + static void SetDirRead() { \ + pinMode(pin, INPUT); \ + } \ + static void SetDirWrite() { \ + pinMode(pin, OUTPUT); \ + } \ + static uint8_t IsSet() { \ + return digitalReadFast(pin); \ + } \ +}; + +MAKE_PIN(P0, 0); +MAKE_PIN(P1, 1); +MAKE_PIN(P2, 2); +MAKE_PIN(P3, 3); +MAKE_PIN(P4, 4); +MAKE_PIN(P5, 5); +MAKE_PIN(P6, 6); +MAKE_PIN(P7, 7); +MAKE_PIN(P8, 8); +MAKE_PIN(P9, 9); +MAKE_PIN(P10, 10); +MAKE_PIN(P11, 11); +MAKE_PIN(P12, 12); +MAKE_PIN(P13, 13); +MAKE_PIN(P14, 14); +MAKE_PIN(P15, 15); +MAKE_PIN(P16, 16); +MAKE_PIN(P17, 17); +MAKE_PIN(P18, 18); +MAKE_PIN(P19, 19); +MAKE_PIN(P20, 20); +MAKE_PIN(P21, 21); +MAKE_PIN(P22, 22); +MAKE_PIN(P23, 23); +MAKE_PIN(P24, 24); +MAKE_PIN(P25, 25); +MAKE_PIN(P26, 26); +MAKE_PIN(P27, 27); +MAKE_PIN(P28, 28); +MAKE_PIN(P29, 29); +MAKE_PIN(P30, 30); +MAKE_PIN(P31, 31); +MAKE_PIN(P32, 35); +MAKE_PIN(P33, 33); +MAKE_PIN(P34, 34); +MAKE_PIN(P35, 35); +MAKE_PIN(P36, 36); +MAKE_PIN(P37, 37); +MAKE_PIN(P38, 38); +MAKE_PIN(P39, 39); +#ifdef ARDUINO_TEENSY41 +MAKE_PIN(P40, 40); +MAKE_PIN(P41, 41); +MAKE_PIN(P42, 42); +MAKE_PIN(P43, 43); +MAKE_PIN(P44, 44); +MAKE_PIN(P45, 45); +MAKE_PIN(P46, 46); +MAKE_PIN(P47, 47); +MAKE_PIN(P48, 48); +MAKE_PIN(P49, 49); +MAKE_PIN(P50, 50); +MAKE_PIN(P51, 51); +MAKE_PIN(P52, 52); +MAKE_PIN(P53, 53); +MAKE_PIN(P54, 54); +#endif + +#undef MAKE_PIN + #elif defined(ARDUINO_SAM_DUE) && defined(__SAM3X8E__) // SetDirRead: From e78795af22bd7a56f24ffcf5935b4fdb95ee39a2 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Tue, 17 Nov 2020 16:46:11 +0100 Subject: [PATCH 15/93] Only use spi4teensy3 on Teensy 3.x --- settings.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/settings.h b/settings.h index 2d176053..7e6ae6c7 100644 --- a/settings.h +++ b/settings.h @@ -80,17 +80,15 @@ e-mail : support@circuitsathome.com #endif //////////////////////////////////////////////////////////////////////////////// -// Set to 1 to use the faster spi4teensy3 driver. +// Set to 1 to use the faster spi4teensy3 driver on Teensy 3.x //////////////////////////////////////////////////////////////////////////////// #ifndef USE_SPI4TEENSY3 +#if defined(CORE_TEENSY) && (defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)) #define USE_SPI4TEENSY3 1 -#endif - -// Disabled on the Teensy LC, as it is incompatible for now -#if defined(__MKL26Z64__) -#undef USE_SPI4TEENSY3 +#else #define USE_SPI4TEENSY3 0 #endif +#endif //////////////////////////////////////////////////////////////////////////////// // AUTOMATIC Settings From 9f92baf19171ae427d13722f3bcf16ded3576835 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Wed, 18 Nov 2020 14:49:59 +0100 Subject: [PATCH 16/93] Use Github actions, as Travis is too slow --- .github/workflows/main.yml | 65 ++++++++++++++++++++++ .travis.yml | 110 ------------------------------------- README.md | 4 +- 3 files changed, 67 insertions(+), 112 deletions(-) create mode 100644 .github/workflows/main.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..b716dbad --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,65 @@ +name: CI + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + # find examples -type f -name "*.ino" | rev | cut -d/ -f2- | rev | sort | sed -z 's/\n/, /g' + example: [examples/acm/acm_terminal, examples/adk/adk_barcode, examples/adk/ArduinoBlinkLED, examples/adk/demokit_20, examples/adk/term_test, examples/adk/term_time, examples/Bluetooth/BTHID, examples/Bluetooth/PS3BT, examples/Bluetooth/PS3Multi, examples/Bluetooth/PS3SPP, examples/Bluetooth/PS4BT, examples/Bluetooth/SPP, examples/Bluetooth/SPPMulti, examples/Bluetooth/Wii, examples/Bluetooth/WiiBalanceBoard, examples/Bluetooth/WiiIRCamera, examples/Bluetooth/WiiMulti, examples/Bluetooth/WiiUProController, examples/board_qc, examples/cdc_XR21B1411/XR_terminal, examples/ftdi/USBFTDILoopback, examples/GPIO/Blink, examples/GPIO/Blink_LowLevel, examples/GPIO/Input, examples/HID/le3dp, examples/HID/scale, examples/HID/SRWS1, examples/HID/t16km, examples/HID/USBHIDBootKbd, examples/HID/USBHIDBootKbdAndMouse, examples/HID/USBHIDBootMouse, examples/HID/USBHID_desc, examples/HID/USBHIDJoystick, examples/HID/USBHIDMultimediaKbd, examples/hub_demo, examples/max_LCD, examples/pl2303/pl2303_gprs_terminal, examples/pl2303/pl2303_gps, examples/pl2303/pl2303_tinygps, examples/pl2303/pl2303_xbee_terminal, examples/PS3USB, examples/PS4USB, examples/PSBuzz, examples/USB_desc, examples/USBH_MIDI/bidirectional_converter, examples/USBH_MIDI/eVY1_sample, examples/USBH_MIDI/USBH_MIDI_dump, examples/USBH_MIDI/USB_MIDI_converter, examples/USBH_MIDI/USB_MIDI_converter_multi, examples/Xbox/XBOXOLD, examples/Xbox/XBOXONE, examples/Xbox/XBOXONESBT, examples/Xbox/XBOXRECV, examples/Xbox/XBOXUSB] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + - name: Install PlatformIO + run: | + pip install -U pip setuptools wheel + pip install platformio + - name: Install MIDI library + if: contains(matrix.example, 'MIDI') + # https://platformio.org/lib/show/62/MIDI%20Library + run: pio lib -g install 62 + - name: Install TinyGPS library + if: contains(matrix.example, 'tinygps') + # https://platformio.org/lib/show/416/TinyGPS + run: pio lib -g install 416 + - name: Run PlatformIO + run: | + # Skip all Wii examples and the PS3SPP example on Uno, as they will not fit with debugging enabled + if [[ "${{ matrix.example }}" != *"Wii"* && "${{ matrix.example }}" != *"PS3SPP" ]]; then UNO="--board=uno"; fi + + # There is a conflict with the internal Teensy MIDI library, so skip this example on Teensy 3.x and 4.x + # See: https://travis-ci.org/github/felis/USB_Host_Shield_2.0/jobs/743787235 + if [[ "${{ matrix.example }}" != *"bidirectional_converter" ]]; then TEENSY35="--board=teensy35"; TEENSY36="--board=teensy36"; TEENSY40="--board=teensy40"; TEENSY41="--board=teensy41"; fi + + pio ci --lib="." $UNO --board=genuino101 --board=teensylc $TEENSY40 $TEENSY41 --board=esp12e --board=nodemcu --board=esp32dev + + # Teensy 3.x depends on the SPI4Teensy3 library: https://platformio.org/lib/show/417/SPI4Teensy3 + pio ci --lib="." --board=teensy30 --board=teensy31 $TEENSY35 $TEENSY36 --project-option="lib_deps=SPI4Teensy3" + + # Workaround https://github.com/arduino/ArduinoCore-sam/issues/69 + pio ci --lib="." --board=due --project-option="build_flags=-Wno-misleading-indentation" + env: + PLATFORMIO_CI_SRC: ${{ matrix.example }} + PLATFORMIO_BUILD_FLAGS: -DWIICAMERA -DDEBUG_USB_HOST -Wall -Werror + deploy: + needs: [build] + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/master' + steps: + - name: Install dependencies + # We need GraphViz to draw figures and graphs + # Doxygen is used for generating the documentation + run: apt-get -y install doxygen graphviz + - name: Generate documentation + run: | + # Fix error in the Doxygen Markdown parser and generate the documentation + sed -i 's/@YuuichiAkagawa/\\@YuuichiAkagawa/' README.md + doxygen doc/Doxyfile + touch doc/html/.nojekyll + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./doc/html diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 1f5eb687..00000000 --- a/.travis.yml +++ /dev/null @@ -1,110 +0,0 @@ -language: python -python: - - "3.9" - -# Cache PlatformIO packages using Travis CI container-based infrastructure -sudo: false -cache: - directories: - - "~/.platformio" - -addons: - apt: - packages: - # We need GraphViz to draw figures and graphs - - graphviz - # Doxygen is used for generating the documentation - - doxygen - -# Generated using: find examples -type f -name "*.ino" | rev | cut -d/ -f2- | rev | sed 's/^/ - PLATFORMIO_CI_SRC=/' > tmp.yml -env: - - PLATFORMIO_CI_SRC=examples/acm/acm_terminal - - PLATFORMIO_CI_SRC=examples/adk/adk_barcode - - PLATFORMIO_CI_SRC=examples/adk/ArduinoBlinkLED - - PLATFORMIO_CI_SRC=examples/adk/demokit_20 - - PLATFORMIO_CI_SRC=examples/adk/term_test - - PLATFORMIO_CI_SRC=examples/adk/term_time - - PLATFORMIO_CI_SRC=examples/Bluetooth/BTHID - - PLATFORMIO_CI_SRC=examples/Bluetooth/PS3BT - - PLATFORMIO_CI_SRC=examples/Bluetooth/PS3Multi - - PLATFORMIO_CI_SRC=examples/Bluetooth/PS3SPP SKIP_UNO=true - - PLATFORMIO_CI_SRC=examples/Bluetooth/PS4BT - - PLATFORMIO_CI_SRC=examples/Bluetooth/SPP - - PLATFORMIO_CI_SRC=examples/Bluetooth/SPPMulti - - PLATFORMIO_CI_SRC=examples/Bluetooth/Wii SKIP_UNO=true - - PLATFORMIO_CI_SRC=examples/Bluetooth/WiiBalanceBoard SKIP_UNO=true - - PLATFORMIO_CI_SRC=examples/Bluetooth/WiiIRCamera PLATFORMIO_BUILD_FLAGS="-DWIICAMERA" SKIP_UNO=true - - PLATFORMIO_CI_SRC=examples/Bluetooth/WiiMulti SKIP_UNO=true - - PLATFORMIO_CI_SRC=examples/Bluetooth/WiiUProController SKIP_UNO=true - - PLATFORMIO_CI_SRC=examples/board_qc - - PLATFORMIO_CI_SRC=examples/cdc_XR21B1411/XR_terminal - - PLATFORMIO_CI_SRC=examples/ftdi/USBFTDILoopback - - PLATFORMIO_CI_SRC=examples/GPIO/Blink - - PLATFORMIO_CI_SRC=examples/GPIO/Blink_LowLevel - - PLATFORMIO_CI_SRC=examples/GPIO/Input - - PLATFORMIO_CI_SRC=examples/HID/le3dp - - PLATFORMIO_CI_SRC=examples/HID/scale - - PLATFORMIO_CI_SRC=examples/HID/SRWS1 - - PLATFORMIO_CI_SRC=examples/HID/USBHID_desc - - PLATFORMIO_CI_SRC=examples/HID/USBHIDBootKbd - - PLATFORMIO_CI_SRC=examples/HID/USBHIDBootKbdAndMouse - - PLATFORMIO_CI_SRC=examples/HID/USBHIDBootMouse - - PLATFORMIO_CI_SRC=examples/HID/USBHIDJoystick - - PLATFORMIO_CI_SRC=examples/HID/USBHIDMultimediaKbd - - PLATFORMIO_CI_SRC=examples/hub_demo - - PLATFORMIO_CI_SRC=examples/max_LCD - - PLATFORMIO_CI_SRC=examples/pl2303/pl2303_gprs_terminal - - PLATFORMIO_CI_SRC=examples/pl2303/pl2303_gps - - PLATFORMIO_CI_SRC=examples/pl2303/pl2303_tinygps - - PLATFORMIO_CI_SRC=examples/pl2303/pl2303_xbee_terminal - - PLATFORMIO_CI_SRC=examples/PS3USB - - PLATFORMIO_CI_SRC=examples/PS4USB - - PLATFORMIO_CI_SRC=examples/PSBuzz - # - PLATFORMIO_CI_SRC=examples/testusbhostFAT - - PLATFORMIO_CI_SRC=examples/USB_desc - # See: https://travis-ci.org/github/felis/USB_Host_Shield_2.0/jobs/743787235 - - PLATFORMIO_CI_SRC=examples/USBH_MIDI/bidirectional_converter SKIP_TEENSY35=true SKIP_TEENSY36=true - - PLATFORMIO_CI_SRC=examples/USBH_MIDI/eVY1_sample - - PLATFORMIO_CI_SRC=examples/USBH_MIDI/USB_MIDI_converter - - PLATFORMIO_CI_SRC=examples/USBH_MIDI/USB_MIDI_converter_multi - - PLATFORMIO_CI_SRC=examples/USBH_MIDI/USBH_MIDI_dump - - PLATFORMIO_CI_SRC=examples/Xbox/XBOXOLD - - PLATFORMIO_CI_SRC=examples/Xbox/XBOXONE - - PLATFORMIO_CI_SRC=examples/Xbox/XBOXONESBT - - PLATFORMIO_CI_SRC=examples/Xbox/XBOXRECV - - PLATFORMIO_CI_SRC=examples/Xbox/XBOXUSB - -install: - - pip install -U platformio - - export PLATFORMIO_BUILD_FLAGS="$PLATFORMIO_BUILD_FLAGS -DDEBUG_USB_HOST -Wall -Werror" - - # - # Libraries from PlatformIO Library Registry: - # - # http://platformio.org/lib/show/62/MIDI - # http://platformio.org/lib/show/416/TinyGPS - # http://platformio.org/lib/show/417/SPI4Teensy3 - - platformio lib install 62 416 417 - -script: - - if [[ -z "$SKIP_UNO" ]]; then UNO="--board=uno"; fi - - if [[ -z "$SKIP_TEENSY35" ]]; then TEENSY35="--board=teensy35"; fi - - if [[ -z "$SKIP_TEENSY36" ]]; then TEENSY36="--board=teensy36"; fi - - platformio ci --lib="." $UNO --board=genuino101 --board=teensy30 --board=teensy31 $TEENSY35 $TEENSY36 --board=teensylc --board=teensy40 --board=teensy41 --board=esp12e --board=nodemcu --board=esp32dev - - platformio ci --lib="." --board=due --project-option="build_flags=-Wno-misleading-indentation" # Workaround https://travis-ci.org/felis/USB_Host_Shield_2.0/jobs/569237654 and https://github.com/arduino/ArduinoCore-sam/issues/69 - -before_deploy: - # Fix errors in the Doxygen Markdown parser and generate the docs - - sed -i 's/Circuits@/Circuits\\@/' README.md - - sed -i 's/@YuuichiAkagawa/\\@YuuichiAkagawa/' README.md - - doxygen doc/Doxyfile - - touch doc/html/.nojekyll - -deploy: - provider: pages - github-token: $GITHUB_TOKEN - local_dir: doc/html - skip_cleanup: true - keep-history: true - on: - branch: master diff --git a/README.md b/README.md index 40b51d91..facff8dd 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# USB Host Library Rev.2.0 +# USB Host Library Rev. 2.0 The code is released under the GNU General Public License. __________ -[![Build Status](https://travis-ci.org/felis/USB_Host_Shield_2.0.svg?branch=master)](https://travis-ci.org/felis/USB_Host_Shield_2.0) +[![](https://github.com/felis/USB_Host_Shield_2.0/workflows/CI/badge.svg)](https://github.com/felis/USB_Host_Shield_2.0/actions?query=branch%3Amaster) # Summary This is Revision 2.0 of MAX3421E-based USB Host Shield Library for AVR's. From 2c82f4e4c6181d5eeca33756d95fa4dc25201cdb Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Wed, 18 Nov 2020 19:06:27 +0100 Subject: [PATCH 17/93] Forgot to add sudo when installing documentation dependencies --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b716dbad..cf5c1bbf 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -51,7 +51,7 @@ jobs: - name: Install dependencies # We need GraphViz to draw figures and graphs # Doxygen is used for generating the documentation - run: apt-get -y install doxygen graphviz + run: sudo apt-get -y install doxygen graphviz - name: Generate documentation run: | # Fix error in the Doxygen Markdown parser and generate the documentation From c8ef5d55500a1b7a74dc83bdac88cd4959fe98c3 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Wed, 18 Nov 2020 19:42:13 +0100 Subject: [PATCH 18/93] Check out the repo before generating the documentation --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cf5c1bbf..823232aa 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -48,6 +48,7 @@ jobs: runs-on: ubuntu-latest if: github.ref == 'refs/heads/master' steps: + - uses: actions/checkout@v2 - name: Install dependencies # We need GraphViz to draw figures and graphs # Doxygen is used for generating the documentation From 4e8d5e8a06429b23e0aa547e425fccf47a8c0aa1 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Wed, 18 Nov 2020 20:20:00 +0100 Subject: [PATCH 19/93] Always generate the documentation, so it is always in sync with the master branch --- .github/workflows/main.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 823232aa..ecc50c75 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -43,8 +43,7 @@ jobs: env: PLATFORMIO_CI_SRC: ${{ matrix.example }} PLATFORMIO_BUILD_FLAGS: -DWIICAMERA -DDEBUG_USB_HOST -Wall -Werror - deploy: - needs: [build] + doc: runs-on: ubuntu-latest if: github.ref == 'refs/heads/master' steps: @@ -59,7 +58,7 @@ jobs: sed -i 's/@YuuichiAkagawa/\\@YuuichiAkagawa/' README.md doxygen doc/Doxyfile touch doc/html/.nojekyll - - name: Deploy + - name: Deploy documentation uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} From f8217818a554e555d1191ec18d20def098b8a00c Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Wed, 18 Nov 2020 20:22:02 +0100 Subject: [PATCH 20/93] Updated link to James's PS4 guides --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index facff8dd..61b921a8 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,7 @@ For information see the following blog post: and this Linux driver: . -Several guides on how to use the PS4 library has been written by Dr. James E. Barger and are available at the following link: . +Several guides on how to use the PS4 library has been written by Dr. James E. Barger and are available at the following link: . ### PS3 Library From 86ac21730575fcb063ef6318cc65b9eeb4f76b6d Mon Sep 17 00:00:00 2001 From: Yuuichi Akagawa Date: Mon, 23 Nov 2020 09:59:31 +0900 Subject: [PATCH 21/93] Update vender specific code for Novation --- usbh_midi.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/usbh_midi.cpp b/usbh_midi.cpp index a9f0f719..71aece3b 100644 --- a/usbh_midi.cpp +++ b/usbh_midi.cpp @@ -346,9 +346,19 @@ void USBH_MIDI::setupDeviceSpecific() { // Novation if( vid == 0x1235 ) { - // LaunchPad's endpoint attirbute is interrupt (0x20:S, 0x36:Mini, 0x51:Pro, 0x69:MK2, 0x7b:Launchkey25 MK2) - if(pid == 0x20 || pid == 0x36 || pid == 0x51 || pid == 0x69 || pid == 0x7b ) { + // LaunchPad and LaunchKey endpoint attribute is interrupt + // https://github.com/YuuichiAkagawa/USBH_MIDI/wiki/Novation-USB-Product-ID-List + + // LaunchPad: 0x20:S, 0x36:Mini, 0x51:Pro, 0x69:MK2 + if( pid == 0x20 || pid == 0x36 || pid == 0x51 || pid == 0x69 ) { bTransferTypeMask = 2; + return; + } + + // LaunchKey: 0x30-32, 0x35:Mini, 0x7B-0x7D:MK2 + if( ( 0x30 <= pid && pid <= 0x32) || pid == 0x35 || ( 0x7B <= pid && pid <= 0x7D) ) { + bTransferTypeMask = 2; + return; } } } From cf0fad4045a395fb7128e4be9a63cc7560684951 Mon Sep 17 00:00:00 2001 From: Yuuichi Akagawa Date: Mon, 23 Nov 2020 20:08:07 +0900 Subject: [PATCH 22/93] Change to Windows style enumeration process. Some devices return only 8 bytes of Device Descriptor by default address. (e.g. Novation Circuit) --- usbh_midi.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/usbh_midi.cpp b/usbh_midi.cpp index 71aece3b..df2672cc 100644 --- a/usbh_midi.cpp +++ b/usbh_midi.cpp @@ -147,10 +147,9 @@ uint8_t USBH_MIDI::Init(uint8_t parent, uint8_t port, bool lowspeed) p->epinfo = epInfo; p->lowspeed = lowspeed; - // Get device descriptor - rcode = pUsb->getDevDescr( 0, 0, sizeof(USB_DEVICE_DESCRIPTOR), (uint8_t*)buf ); - vid = udd->idVendor; - pid = udd->idProduct; + // First Device Descriptor Request (Initially first 8 bytes) + rcode = pUsb->getDevDescr( 0, 0, 8, (uint8_t*)buf ); + // Restore p->epinfo p->epinfo = oldep_ptr; @@ -186,6 +185,13 @@ uint8_t USBH_MIDI::Init(uint8_t parent, uint8_t port, bool lowspeed) } p->lowspeed = lowspeed; + // Second Device Descriptor Request (Full) + rcode = pUsb->getDevDescr( bAddress, 0, sizeof(USB_DEVICE_DESCRIPTOR), (uint8_t*)buf ); + if( rcode ){ + goto FailGetDevDescr; + } + vid = udd->idVendor; + pid = udd->idProduct; num_of_conf = udd->bNumConfigurations; // Assign epInfo to epinfo pointer From bfe134bf399e001bc345ebc46562b77a52151a8f Mon Sep 17 00:00:00 2001 From: Yuuichi Akagawa Date: Mon, 23 Nov 2020 21:51:36 +0900 Subject: [PATCH 23/93] Added a link to the article --- usbh_midi.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/usbh_midi.cpp b/usbh_midi.cpp index df2672cc..4de21770 100644 --- a/usbh_midi.cpp +++ b/usbh_midi.cpp @@ -148,6 +148,7 @@ uint8_t USBH_MIDI::Init(uint8_t parent, uint8_t port, bool lowspeed) p->lowspeed = lowspeed; // First Device Descriptor Request (Initially first 8 bytes) + // https://techcommunity.microsoft.com/t5/microsoft-usb-blog/how-does-usb-stack-enumerate-a-device/ba-p/270685#_First_Device_Descriptor rcode = pUsb->getDevDescr( 0, 0, 8, (uint8_t*)buf ); // Restore p->epinfo From c43e08b4450437081fa053b2d1d1b9912f09b5e2 Mon Sep 17 00:00:00 2001 From: Yuuichi Akagawa Date: Wed, 2 Dec 2020 00:29:56 +0900 Subject: [PATCH 24/93] Add Serial.flush() option for MIDI examples --- .../USB_MIDI_converter/USB_MIDI_converter.ino | 10 +++++++++- .../USB_MIDI_converter_multi.ino | 10 +++++++++- .../bidirectional_converter.ino | 12 +++++++++++- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/examples/USBH_MIDI/USB_MIDI_converter/USB_MIDI_converter.ino b/examples/USBH_MIDI/USB_MIDI_converter/USB_MIDI_converter.ino index acd5fe10..1f5d1561 100644 --- a/examples/USBH_MIDI/USB_MIDI_converter/USB_MIDI_converter.ino +++ b/examples/USBH_MIDI/USB_MIDI_converter/USB_MIDI_converter.ino @@ -1,7 +1,7 @@ /* ******************************************************************************* * USB-MIDI to Legacy Serial MIDI converter - * Copyright (C) 2012-2017 Yuuichi Akagawa + * Copyright (C) 2012-2020 Yuuichi Akagawa * * Idea from LPK25 USB-MIDI to Serial MIDI converter * by Collin Cunningham - makezine.com, narbotic.com @@ -24,6 +24,11 @@ #else #define _MIDI_SERIAL_PORT Serial #endif + +// Set to 1 if you want to wait for the Serial MIDI transmission to complete. +// For more information, see https://github.com/felis/USB_Host_Shield_2.0/issues/570 +#define ENABLE_MIDI_SERIAL_FLUSH 0 + ////////////////////////// // MIDI Pin assign // 2 : GND @@ -68,6 +73,9 @@ void MIDI_poll() if ( (size = Midi.RecvData(outBuf)) > 0 ) { //MIDI Output _MIDI_SERIAL_PORT.write(outBuf, size); +#if ENABLE_MIDI_SERIAL_FLUSH + _MIDI_SERIAL_PORT.flush(); +#endif } } while (size > 0); } diff --git a/examples/USBH_MIDI/USB_MIDI_converter_multi/USB_MIDI_converter_multi.ino b/examples/USBH_MIDI/USB_MIDI_converter_multi/USB_MIDI_converter_multi.ino index c6a72e23..5c841a0c 100644 --- a/examples/USBH_MIDI/USB_MIDI_converter_multi/USB_MIDI_converter_multi.ino +++ b/examples/USBH_MIDI/USB_MIDI_converter_multi/USB_MIDI_converter_multi.ino @@ -1,7 +1,7 @@ /* ******************************************************************************* * USB-MIDI to Legacy Serial MIDI converter - * Copyright (C) 2012-2017 Yuuichi Akagawa + * Copyright (C) 2012-2020 Yuuichi Akagawa * * Idea from LPK25 USB-MIDI to Serial MIDI converter * by Collin Cunningham - makezine.com, narbotic.com @@ -24,6 +24,11 @@ #else #define _MIDI_SERIAL_PORT Serial #endif + +// Set to 1 if you want to wait for the Serial MIDI transmission to complete. +// For more information, see https://github.com/felis/USB_Host_Shield_2.0/issues/570 +#define ENABLE_MIDI_SERIAL_FLUSH 0 + ////////////////////////// // MIDI Pin assign // 2 : GND @@ -73,6 +78,9 @@ void MIDI_poll(USBH_MIDI &Midi) if ( (size = Midi.RecvData(outBuf)) > 0 ) { //MIDI Output _MIDI_SERIAL_PORT.write(outBuf, size); +#if ENABLE_MIDI_SERIAL_FLUSH + _MIDI_SERIAL_PORT.flush(); +#endif } } while (size > 0); } diff --git a/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino b/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino index 552cadf6..b752a05f 100644 --- a/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino +++ b/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino @@ -1,7 +1,7 @@ /* ******************************************************************************* * Legacy Serial MIDI and USB Host bidirectional converter - * Copyright (C) 2013-2017 Yuuichi Akagawa + * Copyright (C) 2013-2020 Yuuichi Akagawa * * for use with Arduino MIDI library * https://github.com/FortySevenEffects/arduino_midi_library/ @@ -32,6 +32,10 @@ MIDI_CREATE_DEFAULT_INSTANCE(); #define _MIDI_SERIAL_PORT Serial #endif +// Set to 1 if you want to wait for the Serial MIDI transmission to complete. +// For more information, see https://github.com/felis/USB_Host_Shield_2.0/issues/570 +#define ENABLE_MIDI_SERIAL_FLUSH 0 + ////////////////////////// // MIDI Pin assign // 2 : GND @@ -125,6 +129,9 @@ void MIDI_poll() _MIDI_SERIAL_PORT.write(outbuf, rc); p += 4; } +#if ENABLE_MIDI_SERIAL_FLUSH + _MIDI_SERIAL_PORT.flush(); +#endif readPtr += 4; } #else @@ -133,6 +140,9 @@ void MIDI_poll() if ( (size = Midi.RecvData(outBuf)) > 0 ) { //MIDI Output _MIDI_SERIAL_PORT.write(outBuf, size); +#if ENABLE_MIDI_SERIAL_FLUSH + _MIDI_SERIAL_PORT.flush(); +#endif } } while (size > 0); #endif From 4dfda5a151d4bd2de908018605ae1e81d6f75984 Mon Sep 17 00:00:00 2001 From: David Madison Date: Tue, 8 Dec 2020 13:10:04 -0500 Subject: [PATCH 25/93] Add Xbox Core Controller PID --- XBOXONE.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/XBOXONE.h b/XBOXONE.h index 90ce8304..c89f558c 100644 --- a/XBOXONE.h +++ b/XBOXONE.h @@ -45,6 +45,7 @@ #define XBOX_ONE_PID3 0x02E3 // Microsoft X-Box One Elite pad #define XBOX_ONE_PID4 0x02EA // Microsoft X-Box One S pad #define XBOX_ONE_PID13 0x0B0A // Microsoft X-Box One Adaptive Controller +#define XBOX_ONE_PID14 0x0B12 // Microsoft X-Box Core Controller // Unofficial controllers #define XBOX_VID2 0x0738 // Mad Catz @@ -123,9 +124,10 @@ public: */ virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { return ((vid == XBOX_VID1 || vid == XBOX_VID2 || vid == XBOX_VID3 || vid == XBOX_VID4 || vid == XBOX_VID5 || vid == XBOX_VID6) && - (pid == XBOX_ONE_PID1 || pid == XBOX_ONE_PID2 || pid == XBOX_ONE_PID3 || pid == XBOX_ONE_PID4 || + (pid == XBOX_ONE_PID1 || pid == XBOX_ONE_PID2 || pid == XBOX_ONE_PID3 || pid == XBOX_ONE_PID4 || pid == XBOX_ONE_PID5 || pid == XBOX_ONE_PID6 || pid == XBOX_ONE_PID7 || pid == XBOX_ONE_PID8 || - pid == XBOX_ONE_PID9 || pid == XBOX_ONE_PID10 || pid == XBOX_ONE_PID11 || pid == XBOX_ONE_PID12 || pid == XBOX_ONE_PID13)); + pid == XBOX_ONE_PID9 || pid == XBOX_ONE_PID10 || pid == XBOX_ONE_PID11 || pid == XBOX_ONE_PID12 || + pid == XBOX_ONE_PID13 || pid == XBOX_ONE_PID14)); }; /**@}*/ From 757f4282348f2863cfb53e0f486bc9674d87d5e2 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Thu, 7 Jan 2021 20:54:46 +0100 Subject: [PATCH 26/93] Release version 1.4.0 --- library.json | 2 +- library.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library.json b/library.json index 76e1069f..0c240db4 100644 --- a/library.json +++ b/library.json @@ -31,7 +31,7 @@ "type": "git", "url": "https://github.com/felis/USB_Host_Shield_2.0.git" }, - "version": "1.3.2", + "version": "1.4.0", "license": "GPL-2.0", "examples": [ diff --git a/library.properties b/library.properties index 179a515c..edd159ff 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=USB Host Shield Library 2.0 -version=1.3.2 +version=1.4.0 author=Oleg Mazurov (Circuits@Home) , Kristian Sloth Lauszus , Andrew Kroll , Alexei Glushchenko (Circuits@Home) maintainer=Oleg Mazurov (Circuits@Home) , Kristian Sloth Lauszus , Andrew Kroll sentence=Revision 2.0 of MAX3421E-based USB Host Shield Library. From 59af2be74f3d4397704c5958c83bb7fc2af6d540 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Tue, 12 Jan 2021 01:42:48 +0100 Subject: [PATCH 27/93] HIDUniversal, HIDComposite: Don't overflow hidInterfaces[] or epInfo[] If a connected device has more than 3 (maxHidInterfaces) HID interfaces, which is not unusual with modern keyboards, EndpointXtract() wrote beyond the hidInterfaces[] array and corrupted bHasReportId, PID + VID. The same could happen with the epInfo[] array. Now this is fixed by checking bNumIface/bNMumEP before adding new elements to those arrays. --- hidcomposite.cpp | 4 +++- hiduniversal.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/hidcomposite.cpp b/hidcomposite.cpp index 3f0a21c2..62cc9d0a 100644 --- a/hidcomposite.cpp +++ b/hidcomposite.cpp @@ -306,6 +306,8 @@ void HIDComposite::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint // Fill in interface structure in case of new interface if(!piface) { + if(bNumIface >= maxHidInterfaces) + return; // don't overflow hidInterfaces[] piface = hidInterfaces + bNumIface; piface->bmInterface = iface; piface->bmAltSet = alt; @@ -319,7 +321,7 @@ void HIDComposite::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint if(!SelectInterface(iface, proto)) index = 0; - if(index) { + if(index && bNumEP < totalEndpoints) { // Fill in the endpoint info structure epInfo[bNumEP].epAddr = (pep->bEndpointAddress & 0x0F); epInfo[bNumEP].maxPktSize = (uint8_t)pep->wMaxPacketSize; diff --git a/hiduniversal.cpp b/hiduniversal.cpp index 49309df4..4ced3b14 100644 --- a/hiduniversal.cpp +++ b/hiduniversal.cpp @@ -308,6 +308,8 @@ void HIDUniversal::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint // Fill in interface structure in case of new interface if(!piface) { + if(bNumIface >= maxHidInterfaces) + return; // don't overflow hidInterfaces[] piface = hidInterfaces + bNumIface; piface->bmInterface = iface; piface->bmAltSet = alt; @@ -318,7 +320,7 @@ void HIDUniversal::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_INTERRUPT) index = (pep->bEndpointAddress & 0x80) == 0x80 ? epInterruptInIndex : epInterruptOutIndex; - if(index) { + if(index && bNumEP < totalEndpoints) { // Fill in the endpoint info structure epInfo[bNumEP].epAddr = (pep->bEndpointAddress & 0x0F); epInfo[bNumEP].maxPktSize = (uint8_t)pep->wMaxPacketSize; From 6d7984ade21f34d30c484ee4cc34ff1bc8951829 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Wed, 13 Jan 2021 07:14:36 +0100 Subject: [PATCH 28/93] Log info when not adding Interface/Endpoint because max is reached --- hidcomposite.cpp | 18 +++++++++++++++--- hiduniversal.cpp | 18 +++++++++++++++--- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/hidcomposite.cpp b/hidcomposite.cpp index 62cc9d0a..6cb866d3 100644 --- a/hidcomposite.cpp +++ b/hidcomposite.cpp @@ -306,8 +306,13 @@ void HIDComposite::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint // Fill in interface structure in case of new interface if(!piface) { - if(bNumIface >= maxHidInterfaces) - return; // don't overflow hidInterfaces[] + if(bNumIface >= maxHidInterfaces) { + // don't overflow hidInterfaces[] + Notify(PSTR("\r\n HIDComposite::EndpointXtract(): Not adding HID interface because we already have "), 0x80); + Notify(bNumIface, 0x80); + Notify(PSTR(" interfaces and can't hold more. "), 0x80); + return; + } piface = hidInterfaces + bNumIface; piface->bmInterface = iface; piface->bmAltSet = alt; @@ -321,7 +326,14 @@ void HIDComposite::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint if(!SelectInterface(iface, proto)) index = 0; - if(index && bNumEP < totalEndpoints) { + if(index) { + if(bNumEP >= totalEndpoints) { + // don't overflow epInfo[] either + Notify(PSTR("\r\n HIDComposite::EndpointXtract(): Not adding endpoint info because we already have "), 0x80); + Notify(bNumEP, 0x80); + Notify(PSTR(" endpoints and can't hold more. "), 0x80); + return; + } // Fill in the endpoint info structure epInfo[bNumEP].epAddr = (pep->bEndpointAddress & 0x0F); epInfo[bNumEP].maxPktSize = (uint8_t)pep->wMaxPacketSize; diff --git a/hiduniversal.cpp b/hiduniversal.cpp index 4ced3b14..bbd06af7 100644 --- a/hiduniversal.cpp +++ b/hiduniversal.cpp @@ -308,8 +308,13 @@ void HIDUniversal::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint // Fill in interface structure in case of new interface if(!piface) { - if(bNumIface >= maxHidInterfaces) - return; // don't overflow hidInterfaces[] + if(bNumIface >= maxHidInterfaces) { + // don't overflow hidInterfaces[] + Notify(PSTR("\r\n HIDUniversal::EndpointXtract(): Not adding HID interface because we already have "), 0x80); + Notify(bNumIface, 0x80); + Notify(PSTR(" interfaces and can't hold more. "), 0x80); + return; + } piface = hidInterfaces + bNumIface; piface->bmInterface = iface; piface->bmAltSet = alt; @@ -320,7 +325,14 @@ void HIDUniversal::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_INTERRUPT) index = (pep->bEndpointAddress & 0x80) == 0x80 ? epInterruptInIndex : epInterruptOutIndex; - if(index && bNumEP < totalEndpoints) { + if(index) { + if(bNumEP >= totalEndpoints) { + // don't overflow epInfo[] either + Notify(PSTR("\r\n HIDUniversal::EndpointXtract(): Not adding endpoint info because we already have "), 0x80); + Notify(bNumEP, 0x80); + Notify(PSTR(" endpoints and can't hold more. "), 0x80); + return; + } // Fill in the endpoint info structure epInfo[bNumEP].epAddr = (pep->bEndpointAddress & 0x0F); epInfo[bNumEP].maxPktSize = (uint8_t)pep->wMaxPacketSize; From 3658dc68f3736765246eedec7a70a6dd79884365 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Wed, 13 Jan 2021 07:44:36 +0100 Subject: [PATCH 29/93] Increase USBHID::maxHidInterfaces from 3 to 5 It's not that uncommon to have more than three HID interfaces in a USB device - even an Arduino Leonardo (or Pro Micro) always has two just for the CDC; if you add a Boot Keyboard HID interface and one generic HID interface for consumer control (multimedia) keys you already have four.. Many commercial gaming keyboards also have more then three interfaces (I don't think they really need that many but it is what it is). --- usbhid.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usbhid.h b/usbhid.h index cf74f57d..e85ca575 100644 --- a/usbhid.h +++ b/usbhid.h @@ -149,7 +149,7 @@ protected: static const uint8_t epInterruptInIndex = 1; // InterruptIN endpoint index static const uint8_t epInterruptOutIndex = 2; // InterruptOUT endpoint index - static const uint8_t maxHidInterfaces = 3; + static const uint8_t maxHidInterfaces = 5; static const uint8_t maxEpPerInterface = 2; static const uint8_t totalEndpoints = (maxHidInterfaces * maxEpPerInterface + 1); // We need to make room for the control endpoint From f66d02d6083969e55eea21155c527c4634d224cc Mon Sep 17 00:00:00 2001 From: tomasyk <50111214+tomasyk@users.noreply.github.com> Date: Sun, 5 Jan 2020 15:45:46 +0100 Subject: [PATCH 30/93] Remove comparing bufffer functionality I am using HID for Oregon Scientific WMRS200, and polling reports removed any sequence of 2 or more identical characters, e.g. instead valid sequence FF FF 0 42 0 B8 0 2C 3C 0 0 0 62 1 i get FF 0 42 0 B8 0 2C 3C 0 62 1 There is no need to compare previous buffer with current buffer, as they can be identical and still this should not be ignored. My sequences are built from smaller sequences, where first byte is lenght of sequence to concatenate. Usually it is concatenated from sequences of 1 byte. E.g. 01 FF, 01 FF, 01 00, 01 42 etc. But current code is filtering second 01 FF and result is only one FF in sequence, instead of FF FF. Filtering is not needed, because data are obtained using inTransfer. --- hiduniversal.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/hiduniversal.cpp b/hiduniversal.cpp index bbd06af7..cb9b0576 100644 --- a/hiduniversal.cpp +++ b/hiduniversal.cpp @@ -406,12 +406,6 @@ uint8_t HIDUniversal::Poll() { if(read > constBuffLen) read = constBuffLen; - bool identical = BuffersIdentical(read, buf, prevBuf); - - SaveBuffer(read, buf, prevBuf); - - if(identical) - return 0; #if 0 Notify(PSTR("\r\nBuf: "), 0x80); From 323de96d6e1858073db024f07646c40a4eca048c Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Wed, 13 Jan 2021 10:59:35 +0100 Subject: [PATCH 31/93] HIDUniversal: Remove unused member and methods With the comparing buffer/removing duplicates functionality gone, prevBuf, BuffersIdentical() and SaveBuffer() aren't used anymore. --- hiduniversal.cpp | 14 -------------- hiduniversal.h | 3 --- 2 files changed, 17 deletions(-) diff --git a/hiduniversal.cpp b/hiduniversal.cpp index cb9b0576..501a8e63 100644 --- a/hiduniversal.cpp +++ b/hiduniversal.cpp @@ -67,8 +67,6 @@ void HIDUniversal::Initialize() { bNumIface = 0; bConfNum = 0; pollInterval = 0; - - ZeroMemory(constBuffLen, prevBuf); } bool HIDUniversal::SetReportParser(uint8_t id, HIDReportParser *prs) { @@ -361,23 +359,11 @@ uint8_t HIDUniversal::Release() { return 0; } -bool HIDUniversal::BuffersIdentical(uint8_t len, uint8_t *buf1, uint8_t *buf2) { - for(uint8_t i = 0; i < len; i++) - if(buf1[i] != buf2[i]) - return false; - return true; -} - void HIDUniversal::ZeroMemory(uint8_t len, uint8_t *buf) { for(uint8_t i = 0; i < len; i++) buf[i] = 0; } -void HIDUniversal::SaveBuffer(uint8_t len, uint8_t *src, uint8_t *dest) { - for(uint8_t i = 0; i < len; i++) - dest[i] = src[i]; -} - uint8_t HIDUniversal::Poll() { uint8_t rcode = 0; diff --git a/hiduniversal.h b/hiduniversal.h index 757f6243..0859beb8 100644 --- a/hiduniversal.h +++ b/hiduniversal.h @@ -51,14 +51,11 @@ class HIDUniversal : public USBHID { bool bPollEnable; // poll enable flag static const uint16_t constBuffLen = 64; // event buffer length - uint8_t prevBuf[constBuffLen]; // previous event buffer void Initialize(); HIDInterface* FindInterface(uint8_t iface, uint8_t alt, uint8_t proto); void ZeroMemory(uint8_t len, uint8_t *buf); - bool BuffersIdentical(uint8_t len, uint8_t *buf1, uint8_t *buf2); - void SaveBuffer(uint8_t len, uint8_t *src, uint8_t *dest); protected: EpInfo epInfo[totalEndpoints]; From 4214caff700e0c14996e24b3e1556f6172c8f135 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Wed, 13 Jan 2021 15:18:27 +0100 Subject: [PATCH 32/93] Derive HIDUniversal from HIDComposite They were mostly the same, I only had to keep the original version of Poll() because while the differences are very small, I don't think that unifying them is possible without needlessly complicating things. For this to work I had to make the private fields of HIDComposite protected instead. I took the "+1" in HIDInterface::epIndex[] from the original HIDUniversal class. --- hidcomposite.cpp | 4 +- hidcomposite.h | 9 +- hiduniversal.cpp | 357 +---------------------------------------------- hiduniversal.h | 95 ++++--------- 4 files changed, 39 insertions(+), 426 deletions(-) diff --git a/hidcomposite.cpp b/hidcomposite.cpp index 6cb866d3..73103d0a 100644 --- a/hidcomposite.cpp +++ b/hidcomposite.cpp @@ -308,7 +308,7 @@ void HIDComposite::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint if(!piface) { if(bNumIface >= maxHidInterfaces) { // don't overflow hidInterfaces[] - Notify(PSTR("\r\n HIDComposite::EndpointXtract(): Not adding HID interface because we already have "), 0x80); + Notify(PSTR("\r\n EndpointXtract(): Not adding HID interface because we already have "), 0x80); Notify(bNumIface, 0x80); Notify(PSTR(" interfaces and can't hold more. "), 0x80); return; @@ -329,7 +329,7 @@ void HIDComposite::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint if(index) { if(bNumEP >= totalEndpoints) { // don't overflow epInfo[] either - Notify(PSTR("\r\n HIDComposite::EndpointXtract(): Not adding endpoint info because we already have "), 0x80); + Notify(PSTR("\r\n EndpointXtract(): Not adding endpoint info because we already have "), 0x80); Notify(bNumEP, 0x80); Notify(PSTR(" endpoints and can't hold more. "), 0x80); return; diff --git a/hidcomposite.h b/hidcomposite.h index d108f45a..d265289a 100644 --- a/hidcomposite.h +++ b/hidcomposite.h @@ -23,6 +23,8 @@ e-mail : support@circuitsathome.com class HIDComposite : public USBHID { +protected: + struct ReportParser { uint8_t rptId; HIDReportParser *rptParser; @@ -40,7 +42,7 @@ class HIDComposite : public USBHID { uint8_t bmAltSet : 3; uint8_t bmProtocol : 2; }; - uint8_t epIndex[maxEpPerInterface]; + uint8_t epIndex[maxEpPerInterface + 1]; // We need to make room for the control endpoint as well }; uint8_t bConfNum; // configuration number @@ -57,10 +59,13 @@ class HIDComposite : public USBHID { void ZeroMemory(uint8_t len, uint8_t *buf); -protected: + EpInfo epInfo[totalEndpoints]; HIDInterface hidInterfaces[maxHidInterfaces]; + // FIXME: bHasReportId is never set (except to false in constructor) + // should probably be in EpInfo, /maybe/ in HIDInterface + // but setting it isn't that easy (requires parsing report descriptors) bool bHasReportId; uint16_t PID, VID; // PID and VID of connected device diff --git a/hiduniversal.cpp b/hiduniversal.cpp index 501a8e63..4002f414 100644 --- a/hiduniversal.cpp +++ b/hiduniversal.cpp @@ -17,353 +17,6 @@ e-mail : support@circuitsathome.com #include "hiduniversal.h" -HIDUniversal::HIDUniversal(USB *p) : -USBHID(p), -qNextPollTime(0), -pollInterval(0), -bPollEnable(false), -bHasReportId(false) { - Initialize(); - - if(pUsb) - pUsb->RegisterDeviceClass(this); -} - -uint16_t HIDUniversal::GetHidClassDescrLen(uint8_t type, uint8_t num) { - for(uint8_t i = 0, n = 0; i < HID_MAX_HID_CLASS_DESCRIPTORS; i++) { - if(descrInfo[i].bDescrType == type) { - if(n == num) - return descrInfo[i].wDescriptorLength; - n++; - } - } - return 0; -} - -void HIDUniversal::Initialize() { - for(uint8_t i = 0; i < MAX_REPORT_PARSERS; i++) { - rptParsers[i].rptId = 0; - rptParsers[i].rptParser = NULL; - } - for(uint8_t i = 0; i < HID_MAX_HID_CLASS_DESCRIPTORS; i++) { - descrInfo[i].bDescrType = 0; - descrInfo[i].wDescriptorLength = 0; - } - for(uint8_t i = 0; i < maxHidInterfaces; i++) { - hidInterfaces[i].bmInterface = 0; - hidInterfaces[i].bmProtocol = 0; - - for(uint8_t j = 0; j < maxEpPerInterface; j++) - hidInterfaces[i].epIndex[j] = 0; - } - for(uint8_t i = 0; i < totalEndpoints; i++) { - epInfo[i].epAddr = 0; - epInfo[i].maxPktSize = (i) ? 0 : 8; - epInfo[i].bmSndToggle = 0; - epInfo[i].bmRcvToggle = 0; - epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER; - } - bNumEP = 1; - bNumIface = 0; - bConfNum = 0; - pollInterval = 0; -} - -bool HIDUniversal::SetReportParser(uint8_t id, HIDReportParser *prs) { - for(uint8_t i = 0; i < MAX_REPORT_PARSERS; i++) { - if(rptParsers[i].rptId == 0 && rptParsers[i].rptParser == NULL) { - rptParsers[i].rptId = id; - rptParsers[i].rptParser = prs; - return true; - } - } - return false; -} - -HIDReportParser* HIDUniversal::GetReportParser(uint8_t id) { - if(!bHasReportId) - return ((rptParsers[0].rptParser) ? rptParsers[0].rptParser : NULL); - - for(uint8_t i = 0; i < MAX_REPORT_PARSERS; i++) { - if(rptParsers[i].rptId == id) - return rptParsers[i].rptParser; - } - return NULL; -} - -uint8_t HIDUniversal::Init(uint8_t parent, uint8_t port, bool lowspeed) { - const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR); - - uint8_t buf[constBufSize]; - USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast(buf); - uint8_t rcode; - UsbDevice *p = NULL; - EpInfo *oldep_ptr = NULL; - uint8_t len = 0; - - uint8_t num_of_conf; // number of configurations - //uint8_t num_of_intf; // number of interfaces - - AddressPool &addrPool = pUsb->GetAddressPool(); - - USBTRACE("HU Init\r\n"); - - if(bAddress) - return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; - - // Get pointer to pseudo device with address 0 assigned - p = addrPool.GetUsbDevicePtr(0); - - if(!p) - return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; - - if(!p->epinfo) { - USBTRACE("epinfo\r\n"); - return USB_ERROR_EPINFO_IS_NULL; - } - - // Save old pointer to EP_RECORD of address 0 - oldep_ptr = p->epinfo; - - // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence - p->epinfo = epInfo; - - p->lowspeed = lowspeed; - - // Get device descriptor - rcode = pUsb->getDevDescr(0, 0, 8, (uint8_t*)buf); - - if(!rcode) - len = (buf[0] > constBufSize) ? constBufSize : buf[0]; - - if(rcode) { - // Restore p->epinfo - p->epinfo = oldep_ptr; - - goto FailGetDevDescr; - } - - // Restore p->epinfo - p->epinfo = oldep_ptr; - - // Allocate new address according to device class - bAddress = addrPool.AllocAddress(parent, false, port); - - if(!bAddress) - return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; - - // Extract Max Packet Size from the device descriptor - epInfo[0].maxPktSize = udd->bMaxPacketSize0; - - // Assign new address to the device - rcode = pUsb->setAddr(0, 0, bAddress); - - if(rcode) { - p->lowspeed = false; - addrPool.FreeAddress(bAddress); - bAddress = 0; - USBTRACE2("setAddr:", rcode); - return rcode; - } - - //delay(2); //per USB 2.0 sect.9.2.6.3 - - USBTRACE2("Addr:", bAddress); - - p->lowspeed = false; - - p = addrPool.GetUsbDevicePtr(bAddress); - - if(!p) - return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; - - p->lowspeed = lowspeed; - - if(len) - rcode = pUsb->getDevDescr(bAddress, 0, len, (uint8_t*)buf); - - if(rcode) - goto FailGetDevDescr; - - VID = udd->idVendor; // Can be used by classes that inherits this class to check the VID and PID of the connected device - PID = udd->idProduct; - - num_of_conf = udd->bNumConfigurations; - - // Assign epInfo to epinfo pointer - rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); - - if(rcode) - goto FailSetDevTblEntry; - - USBTRACE2("NC:", num_of_conf); - - for(uint8_t i = 0; i < num_of_conf; i++) { - //HexDumper HexDump; - ConfigDescParser confDescrParser(this); - - //rcode = pUsb->getConfDescr(bAddress, 0, i, &HexDump); - rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser); - - if(rcode) - goto FailGetConfDescr; - - if(bNumEP > 1) - break; - } // for - - if(bNumEP < 2) - return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; - - // Assign epInfo to epinfo pointer - rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo); - - USBTRACE2("Cnf:", bConfNum); - - // Set Configuration Value - rcode = pUsb->setConf(bAddress, 0, bConfNum); - - if(rcode) - goto FailSetConfDescr; - - for(uint8_t i = 0; i < bNumIface; i++) { - if(hidInterfaces[i].epIndex[epInterruptInIndex] == 0) - continue; - - rcode = SetIdle(hidInterfaces[i].bmInterface, 0, 0); - - if(rcode && rcode != hrSTALL) - goto FailSetIdle; - } - - USBTRACE("HU configured\r\n"); - - OnInitSuccessful(); - - bPollEnable = true; - return 0; - -FailGetDevDescr: -#ifdef DEBUG_USB_HOST - NotifyFailGetDevDescr(); - goto Fail; -#endif - -FailSetDevTblEntry: -#ifdef DEBUG_USB_HOST - NotifyFailSetDevTblEntry(); - goto Fail; -#endif - -FailGetConfDescr: -#ifdef DEBUG_USB_HOST - NotifyFailGetConfDescr(); - goto Fail; -#endif - -FailSetConfDescr: -#ifdef DEBUG_USB_HOST - NotifyFailSetConfDescr(); - goto Fail; -#endif - - -FailSetIdle: -#ifdef DEBUG_USB_HOST - USBTRACE("SetIdle:"); -#endif - -#ifdef DEBUG_USB_HOST -Fail: - NotifyFail(rcode); -#endif - Release(); - return rcode; -} - -HIDUniversal::HIDInterface* HIDUniversal::FindInterface(uint8_t iface, uint8_t alt, uint8_t proto) { - for(uint8_t i = 0; i < bNumIface && i < maxHidInterfaces; i++) - if(hidInterfaces[i].bmInterface == iface && hidInterfaces[i].bmAltSet == alt - && hidInterfaces[i].bmProtocol == proto) - return hidInterfaces + i; - return NULL; -} - -void HIDUniversal::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) { - // If the first configuration satisfies, the others are not concidered. - if(bNumEP > 1 && conf != bConfNum) - return; - - //ErrorMessage(PSTR("\r\nConf.Val"), conf); - //ErrorMessage(PSTR("Iface Num"), iface); - //ErrorMessage(PSTR("Alt.Set"), alt); - - bConfNum = conf; - - uint8_t index = 0; - HIDInterface *piface = FindInterface(iface, alt, proto); - - // Fill in interface structure in case of new interface - if(!piface) { - if(bNumIface >= maxHidInterfaces) { - // don't overflow hidInterfaces[] - Notify(PSTR("\r\n HIDUniversal::EndpointXtract(): Not adding HID interface because we already have "), 0x80); - Notify(bNumIface, 0x80); - Notify(PSTR(" interfaces and can't hold more. "), 0x80); - return; - } - piface = hidInterfaces + bNumIface; - piface->bmInterface = iface; - piface->bmAltSet = alt; - piface->bmProtocol = proto; - bNumIface++; - } - - if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_INTERRUPT) - index = (pep->bEndpointAddress & 0x80) == 0x80 ? epInterruptInIndex : epInterruptOutIndex; - - if(index) { - if(bNumEP >= totalEndpoints) { - // don't overflow epInfo[] either - Notify(PSTR("\r\n HIDUniversal::EndpointXtract(): Not adding endpoint info because we already have "), 0x80); - Notify(bNumEP, 0x80); - Notify(PSTR(" endpoints and can't hold more. "), 0x80); - return; - } - // Fill in the endpoint info structure - epInfo[bNumEP].epAddr = (pep->bEndpointAddress & 0x0F); - epInfo[bNumEP].maxPktSize = (uint8_t)pep->wMaxPacketSize; - epInfo[bNumEP].bmSndToggle = 0; - epInfo[bNumEP].bmRcvToggle = 0; - epInfo[bNumEP].bmNakPower = USB_NAK_NOWAIT; - - // Fill in the endpoint index list - piface->epIndex[index] = bNumEP; //(pep->bEndpointAddress & 0x0F); - - if(pollInterval < pep->bInterval) // Set the polling interval as the largest polling interval obtained from endpoints - pollInterval = pep->bInterval; - - bNumEP++; - } - //PrintEndpointDescriptor(pep); -} - -uint8_t HIDUniversal::Release() { - pUsb->GetAddressPool().FreeAddress(bAddress); - - bNumEP = 1; - bAddress = 0; - qNextPollTime = 0; - bPollEnable = false; - return 0; -} - -void HIDUniversal::ZeroMemory(uint8_t len, uint8_t *buf) { - for(uint8_t i = 0; i < len; i++) - buf[i] = 0; -} - uint8_t HIDUniversal::Poll() { uint8_t rcode = 0; @@ -392,6 +45,11 @@ uint8_t HIDUniversal::Poll() { if(read > constBuffLen) read = constBuffLen; + // TODO: handle read == 0 ? continue like HIDComposite, + // return early like in the error case above? + // Either way passing an empty buffer to the functions below + // probably makes no sense? + #if 0 Notify(PSTR("\r\nBuf: "), 0x80); @@ -412,8 +70,3 @@ uint8_t HIDUniversal::Poll() { } return rcode; } - -// Send a report to interrupt out endpoint. This is NOT SetReport() request! -uint8_t HIDUniversal::SndRpt(uint16_t nbytes, uint8_t *dataptr) { - return pUsb->outTransfer(bAddress, epInfo[epInterruptOutIndex].epAddr, nbytes, dataptr); -} diff --git a/hiduniversal.h b/hiduniversal.h index 0859beb8..396bc27e 100644 --- a/hiduniversal.h +++ b/hiduniversal.h @@ -18,88 +18,43 @@ e-mail : support@circuitsathome.com #if !defined(__HIDUNIVERSAL_H__) #define __HIDUNIVERSAL_H__ -#include "usbhid.h" -//#include "hidescriptorparser.h" +#include "hidcomposite.h" -class HIDUniversal : public USBHID { +class HIDUniversal : public HIDComposite { - struct ReportParser { - uint8_t rptId; - HIDReportParser *rptParser; - } rptParsers[MAX_REPORT_PARSERS]; + bool SelectInterface(uint8_t iface __attribute__((unused)), uint8_t proto __attribute__((unused))) final { + // the original HIDUniversal didn't have this at all so make it a no-op + // (and made it final so users don't override this - if they want to use + // SelectInterface() they should be deriving from HIDComposite directly) + return true; + } - // HID class specific descriptor type and length info obtained from HID descriptor - HID_CLASS_DESCRIPTOR_LEN_AND_TYPE descrInfo[HID_MAX_HID_CLASS_DESCRIPTORS]; - - // Returns HID class specific descriptor length by its type and order number - uint16_t GetHidClassDescrLen(uint8_t type, uint8_t num); - - struct HIDInterface { - struct { - uint8_t bmInterface : 3; - uint8_t bmAltSet : 3; - uint8_t bmProtocol : 2; - }; - uint8_t epIndex[maxEpPerInterface + 1]; // We need to make room for the control endpoint as well - }; - - uint8_t bConfNum; // configuration number - uint8_t bNumIface; // number of interfaces in the configuration - uint8_t bNumEP; // total number of EP in the configuration - uint32_t qNextPollTime; // next poll time - uint8_t pollInterval; - bool bPollEnable; // poll enable flag - - static const uint16_t constBuffLen = 64; // event buffer length - - void Initialize(); - HIDInterface* FindInterface(uint8_t iface, uint8_t alt, uint8_t proto); - - void ZeroMemory(uint8_t len, uint8_t *buf); + void ParseHIDData(USBHID *hid, uint8_t ep __attribute__((unused)), bool is_rpt_id, uint8_t len, uint8_t *buf) final { + // override the HIDComposite version of this method to call the HIDUniversal version + // (which doesn't use the endpoint), made it final to make sure users + // of HIDUniversal override the right version of ParseHIDData() (the other one, below) + ParseHIDData(hid, is_rpt_id, len, buf); + } protected: - EpInfo epInfo[totalEndpoints]; - HIDInterface hidInterfaces[maxHidInterfaces]; - - bool bHasReportId; - - uint16_t PID, VID; // PID and VID of connected device - - // HID implementation - HIDReportParser* GetReportParser(uint8_t id); - - virtual uint8_t OnInitSuccessful() { - return 0; - }; - virtual void ParseHIDData(USBHID *hid __attribute__((unused)), bool is_rpt_id __attribute__((unused)), uint8_t len __attribute__((unused)), uint8_t *buf __attribute__((unused))) { return; - }; + } public: - HIDUniversal(USB *p); + HIDUniversal(USB *p) : HIDComposite(p) {} - // HID implementation - bool SetReportParser(uint8_t id, HIDReportParser *prs); - - // USBDeviceConfig implementation - uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); - uint8_t Release(); - uint8_t Poll(); - - virtual uint8_t GetAddress() { - return bAddress; - }; - - virtual bool isReady() { - return bPollEnable; - }; + uint8_t Poll() override; // UsbConfigXtracter implementation - void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep); - - // Send report - do not mix with SetReport()! - uint8_t SndRpt(uint16_t nbytes, uint8_t *dataptr); + void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep) override + { + // If the first configuration satisfies, the others are not considered. + if(bNumEP > 1 && conf != bConfNum) + return; + // otherwise HIDComposite does what HIDUniversal needs + HIDComposite::EndpointXtract(conf, iface, alt, proto, ep); + } }; #endif // __HIDUNIVERSAL_H__ From ee7bf6e5a0575d52a4bd283f0c94c212adea66ca Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 17 Jan 2021 20:17:32 +0100 Subject: [PATCH 33/93] PS5 controller is now working via USB --- PS5Parser.cpp | 160 +++++++++++++++ PS5Parser.h | 405 +++++++++++++++++++++++++++++++++++++ PS5Trigger.cpp | 94 +++++++++ PS5Trigger.h | 168 +++++++++++++++ PS5USB.h | 154 ++++++++++++++ controllerEnums.h | 6 + examples/PS5USB/PS5USB.ino | 146 +++++++++++++ keywords.txt | 2 + 8 files changed, 1135 insertions(+) create mode 100644 PS5Parser.cpp create mode 100644 PS5Parser.h create mode 100644 PS5Trigger.cpp create mode 100644 PS5Trigger.h create mode 100644 PS5USB.h create mode 100644 examples/PS5USB/PS5USB.ino diff --git a/PS5Parser.cpp b/PS5Parser.cpp new file mode 100644 index 00000000..b59d32c6 --- /dev/null +++ b/PS5Parser.cpp @@ -0,0 +1,160 @@ +/* Copyright (C) 2021 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 + + Thanks to Joseph Duchesne for the initial code. + Based on Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows PS5 port. + */ + +#include "PS5Parser.h" + +enum DPADEnum { + DPAD_UP = 0x0, + DPAD_UP_RIGHT = 0x1, + DPAD_RIGHT = 0x2, + DPAD_RIGHT_DOWN = 0x3, + DPAD_DOWN = 0x4, + DPAD_DOWN_LEFT = 0x5, + DPAD_LEFT = 0x6, + DPAD_LEFT_UP = 0x7, + DPAD_OFF = 0x8, +}; + +// To enable serial debugging see "settings.h" +//#define PRINTREPORT // Uncomment to print the report send by the PS5 Controller + +bool PS5Parser::checkDpad(ButtonEnum b) { + switch (b) { + case UP: + return ps5Data.btn.dpad == DPAD_LEFT_UP || ps5Data.btn.dpad == DPAD_UP || ps5Data.btn.dpad == DPAD_UP_RIGHT; + case RIGHT: + return ps5Data.btn.dpad == DPAD_UP_RIGHT || ps5Data.btn.dpad == DPAD_RIGHT || ps5Data.btn.dpad == DPAD_RIGHT_DOWN; + case DOWN: + return ps5Data.btn.dpad == DPAD_RIGHT_DOWN || ps5Data.btn.dpad == DPAD_DOWN || ps5Data.btn.dpad == DPAD_DOWN_LEFT; + case LEFT: + return ps5Data.btn.dpad == DPAD_DOWN_LEFT || ps5Data.btn.dpad == DPAD_LEFT || ps5Data.btn.dpad == DPAD_LEFT_UP; + default: + return false; + } +} + +bool PS5Parser::getButtonPress(ButtonEnum b) { + if (b <= LEFT) // Dpad + return checkDpad(b); + else + return ps5Data.btn.val & (1UL << pgm_read_byte(&PS5_BUTTONS[(uint8_t)b])); +} + +bool PS5Parser::getButtonClick(ButtonEnum b) { + uint32_t mask = 1UL << pgm_read_byte(&PS5_BUTTONS[(uint8_t)b]); + bool click = buttonClickState.val & mask; + buttonClickState.val &= ~mask; // Clear "click" event + return click; +} + +uint8_t PS5Parser::getAnalogButton(ButtonEnum b) { + if (b == L2) // These are the only analog buttons on the controller + return ps5Data.trigger[0]; + else if (b == R2) + return ps5Data.trigger[1]; + return 0; +} + +uint8_t PS5Parser::getAnalogHat(AnalogHatEnum a) { + return ps5Data.hatValue[(uint8_t)a]; +} + +void PS5Parser::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 (buf[i], 0x80); + Notify(PSTR(" "), 0x80); + } +#endif + + if (buf[0] == 0x01) // Check report ID + memcpy(&ps5Data, buf + 1, min((uint8_t)(len - 1), MFK_CASTUINT8T sizeof(ps5Data))); + else if (buf[0] == 0x11) { // This report is send via Bluetooth, it has an offset of 2 compared to the USB data + if (len < 4) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nReport is too short: "), 0x80); + D_PrintHex (len, 0x80); +#endif + return; + } + memcpy(&ps5Data, buf + 3, min((uint8_t)(len - 3), MFK_CASTUINT8T sizeof(ps5Data))); + } else { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nUnknown report id: "), 0x80); + D_PrintHex (buf[0], 0x80); +#endif + return; + } + + if (ps5Data.btn.val != oldButtonState.val) { // Check if anything has changed + buttonClickState.val = ps5Data.btn.val & ~oldButtonState.val; // Update click state variable + oldButtonState.val = ps5Data.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; + } + } + } + message_counter++; + + if (ps5Output.reportChanged || leftTrigger.reportChanged || rightTrigger.reportChanged) + sendOutputReport(&ps5Output); // Send output report +} + + +void PS5Parser::Reset() { + uint8_t i; + for (i = 0; i < sizeof(ps5Data.hatValue); i++) + ps5Data.hatValue[i] = 127; // Center value + ps5Data.btn.val = 0; + oldButtonState.val = 0; + for (i = 0; i < sizeof(ps5Data.trigger); i++) + ps5Data.trigger[i] = 0; + for (i = 0; i < sizeof(ps5Data.xy.finger)/sizeof(ps5Data.xy.finger[0]); i++) + ps5Data.xy.finger[i].touching = 1; // The bit is cleared if the finger is touching the touchpad + + ps5Data.btn.dpad = DPAD_OFF; + oldButtonState.dpad = DPAD_OFF; + buttonClickState.dpad = 0; + oldDpad = 0; + + leftTrigger.Reset(); + rightTrigger.Reset(); + + ps5Output.bigRumble = ps5Output.smallRumble = 0; + ps5Output.microphoneLed = 0; + ps5Output.disableLeds = 0; + ps5Output.playerLeds = 0; + ps5Output.r = ps5Output.g = ps5Output.b = 0; + ps5Output.reportChanged = false; +}; diff --git a/PS5Parser.h b/PS5Parser.h new file mode 100644 index 00000000..c299b700 --- /dev/null +++ b/PS5Parser.h @@ -0,0 +1,405 @@ +/* Copyright (C) 2021 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 + + Thanks to Joseph Duchesne for the initial code. + Based on Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows PS5 port. + */ + +#ifndef _ps5parser_h_ +#define _ps5parser_h_ + +#include "Usb.h" +#include "controllerEnums.h" +#include "PS5Trigger.h" + +/** Buttons on the controller */ +const uint8_t PS5_BUTTONS[] PROGMEM = { + UP, // UP + RIGHT, // RIGHT + DOWN, // DOWN + LEFT, // LEFT + + 0x0C, // SHARE + 0x0D, // OPTIONS + 0x0E, // L3 + 0x0F, // R3 + + 0x0A, // L2 + 0x0B, // R2 + 0x08, // L1 + 0x09, // R1 + + 0x07, // TRIANGLE + 0x06, // CIRCLE + 0x05, // CROSS + 0x04, // SQUARE + + 0x10, // PS + 0x11, // TOUCHPAD + 0x12, // MICROPHONE +}; + +union PS5Buttons { + struct { + uint8_t dpad : 4; + uint8_t square : 1; + uint8_t cross : 1; + uint8_t circle : 1; + uint8_t triangle : 1; + + uint8_t l1 : 1; + uint8_t r1 : 1; + uint8_t l2 : 1; + uint8_t r2 : 1; + uint8_t share : 1; + uint8_t menu : 1; + uint8_t l3 : 1; + uint8_t r3 : 1; + + uint8_t ps : 1; + uint8_t touchpad : 1; + uint8_t mic : 1; + uint8_t dummy : 5; + } __attribute__((packed)); + uint32_t val : 24; +} __attribute__((packed)); + +struct ps5TouchpadXY { + struct { + uint8_t counter : 7; // Increments every time a finger is touching the touchpad + uint8_t touching : 1; // The top bit is cleared if the finger is touching the touchpad + uint16_t x : 12; + uint16_t y : 12; + } __attribute__((packed)) finger[2]; // 0 = first finger, 1 = second finger +} __attribute__((packed)); + +union PS5Status { + struct { + // first byte + uint8_t headphone : 1; + uint8_t dummy : 2; // Seems to change when a jack is plugged in. First bit stays on when a mic is plugged in. + uint8_t usb : 1; // charging + uint8_t dummy2: 4; + + // second byte + uint8_t mic : 1; + uint8_t dummy4 : 3; + } __attribute__((packed)); + uint16_t val; +} __attribute__((packed)); + +struct PS5Data { + /* Button and joystick values */ + uint8_t hatValue[4]; // 0-3 bytes + uint8_t trigger[2]; // 4-5 + + uint8_t dummy; // 6 unknown + + PS5Buttons btn; // 7-9 + + uint8_t dummy2[5]; // 0xA-0xD unknown + + /* Gyro and accelerometer values */ + int16_t gyroX, gyroZ, gyroY; // 0x0F - 0x14 + int16_t accX, accZ, accY; // 0x15-0x1A + + uint8_t dummy3[5]; // 0x1B - 0x1F unknown + + // 0x20 - 0x23 touchpad point 1 + // 0x24 - 0x27 touchpad point 2 + ps5TouchpadXY xy; + + uint8_t dummy4; //0x28 unknown + + uint8_t rightTriggerFeedback; // 0x29 + uint8_t leftTriggerFeedback; // 0x2A + + uint8_t dummy5[10]; // 0x2B - 0x34 unknown + + // status bytes 0x35-0x36 + PS5Status status; +} __attribute__((packed)); + +struct PS5Output { + uint8_t bigRumble, smallRumble; // Rumble + uint8_t microphoneLed; + uint8_t disableLeds; + uint8_t playerLeds; + uint8_t r, g, b; // RGB for lightbar + bool reportChanged; // The data is send when data is received from the controller +} __attribute__((packed)); + +/** This class parses all the data sent by the PS5 controller */ +class PS5Parser { +public: + PS5Trigger leftTrigger; + PS5Trigger rightTrigger; + + /** Constructor for the PS5Parser class. */ + PS5Parser() : leftTrigger(), rightTrigger() { + Reset(); + }; + + /** @name PS5 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. + */ + bool getButtonPress(ButtonEnum b); + bool getButtonClick(ButtonEnum b); + /**@}*/ + /** @name PS5 Controller functions */ + /** + * Used to get the analog value from button presses. + * @param b The ::ButtonEnum to read. + * The supported buttons are: + * ::L2 and ::R2. + * @return Analog value in the range of 0-255. + */ + uint8_t getAnalogButton(ButtonEnum b); + + /** + * Used to read the analog joystick. + * @param a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY. + * @return Return the analog value in the range of 0-255. + */ + uint8_t getAnalogHat(AnalogHatEnum a); + + /** + * Get the x-coordinate of the touchpad. Position 0 is in the top left. + * @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used. + */ + uint16_t getX(uint8_t finger = 0) { + return ps5Data.xy.finger[finger].x; + }; + + /** + * Get the y-coordinate of the touchpad. Position 0 is in the top left. + * @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used. + * @return Returns the y-coordinate of the finger. + */ + uint16_t getY(uint8_t finger = 0) { + return ps5Data.xy.finger[finger].y; + }; + + /** + * Returns whenever the user is toucing the touchpad. + * @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used. + * @return Returns true if the specific finger is touching the touchpad. + */ + bool isTouching(uint8_t finger = 0) { + return !(ps5Data.xy.finger[finger].touching); // The bit is cleared when a finger is touching the touchpad + }; + + /** + * This counter increments every time a finger touches the touchpad. + * @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used. + * @return Return the value of the counter, note that it is only a 7-bit value. + */ + uint8_t getTouchCounter(uint8_t finger = 00) { + return ps5Data.xy.finger[finger].counter; + }; + + /** + * Get the angle of the controller calculated using the accelerometer. + * @param a Either ::Pitch or ::Roll. + * @return Return the angle in the range of 0-360. + */ + float getAngle(AngleEnum a) { + if (a == Pitch) + return (atan2f(ps5Data.accY, ps5Data.accZ) + PI) * RAD_TO_DEG; + else + return (atan2f(ps5Data.accX, ps5Data.accZ) + PI) * RAD_TO_DEG; + }; + + /** + * Used to get the raw values from the 3-axis gyroscope and 3-axis accelerometer inside the PS5 controller. + * @param s The sensor to read. + * @return Returns the raw sensor reading. + */ + int16_t getSensor(SensorEnum s) { + switch(s) { + case gX: + return ps5Data.gyroX; + case gY: + return ps5Data.gyroY; + case gZ: + return ps5Data.gyroZ; + case aX: + return ps5Data.accX; + case aY: + return ps5Data.accY; + case aZ: + return ps5Data.accZ; + default: + return 0; + } + }; + + /** + * Return the battery level of the PS5 controller. + * @return The battery level in the range 0-15. + */ + /*uint8_t getBatteryLevel() { + return ps5Data.status.battery; + };*/ + + /** + * Use this to check if an USB cable is connected to the PS5 controller. + * @return Returns true if an USB cable is connected. + */ + bool getUsbStatus() { + return ps5Data.status.usb; + }; + + /** + * Use this to check if an audio jack cable is connected to the PS5 controller. + * @return Returns true if an audio jack cable is connected. + */ + bool getAudioStatus() { + return ps5Data.status.headphone; + }; + + /** + * Use this to check if a microphone is connected to the PS5 controller. + * @return Returns true if a microphone is connected. + */ + bool getMicStatus() { + return ps5Data.status.mic; + }; + + /** Turn both rumble and the LEDs off. */ + void setAllOff() { + setRumbleOff(); + setLedOff(); + }; + + /** Set rumble off. */ + void setRumbleOff() { + setRumbleOn(0, 0); + }; + + /** + * Turn on rumble. + * @param mode Either ::RumbleHigh or ::RumbleLow. + */ + void setRumbleOn(RumbleEnum mode) { + if (mode == RumbleLow) + setRumbleOn(0x00, 0xFF); + else + setRumbleOn(0xFF, 0x00); + }; + + /** + * Turn on rumble. + * @param bigRumble Value for big motor. + * @param smallRumble Value for small motor. + */ + void setRumbleOn(uint8_t bigRumble, uint8_t smallRumble) { + ps5Output.bigRumble = bigRumble; + ps5Output.smallRumble = smallRumble; + ps5Output.reportChanged = true; + }; + + /** Turn all LEDs off. */ + void setLedOff() { + setLed(0, 0, 0); + }; + + /** + * Use this to set the color using RGB values. + * @param r,g,b RGB value. + */ + void setLed(uint8_t r, uint8_t g, uint8_t b) { + ps5Output.r = r; + ps5Output.g = g; + ps5Output.b = b; + ps5Output.reportChanged = true; + }; + + /** + * Use this to set the color using the predefined colors in ::ColorsEnum. + * @param color The desired color. + */ + void setLed(ColorsEnum color) { + setLed((uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color)); + }; + + /** Turn all player LEDs off. */ + void setPlayerLedOff() { + setPlayerLed(0); + } + + /** + * Use this to set five player LEDs. + * @param mask Bit mask to set the five player LEDs. The first 5 bits represent a LED each. + */ + void setPlayerLed(uint8_t mask) { + ps5Output.playerLeds = mask; + } + + /** Use to turn the microphone LED off. */ + void setMicLedOff() { + setMicLed(0); + } + + /** + * Use this to turn the microphone LED on/off. + * @param on Turn the microphone LED on/off. + */ + void setMicLed(bool on) { + ps5Output.microphoneLed = on ? 1 : 0; + } + + /** Get the incoming message count. */ + uint16_t getMessageCounter(){ + return message_counter; + } + +protected: + /** + * Used to parse data sent from the PS5 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 PS5 controller. This is implemented in PS5BT.h and PS5USB.h. + * @param output Pointer to PS5Output buffer; + */ + virtual void sendOutputReport(PS5Output *output) = 0; + + +private: + bool checkDpad(ButtonEnum b); // Used to check PS5 DPAD buttons + + PS5Data ps5Data; + PS5Buttons oldButtonState, buttonClickState; + PS5Output ps5Output; + uint8_t oldDpad; + uint16_t message_counter = 0; +}; +#endif diff --git a/PS5Trigger.cpp b/PS5Trigger.cpp new file mode 100644 index 00000000..534158ab --- /dev/null +++ b/PS5Trigger.cpp @@ -0,0 +1,94 @@ +/** + * @file PS5Trigger.cpp + * @author Ludwig Füchsl, adapted for USB_Host_Library SAMD by Joseph Duchesne + * @brief Based on Ludwig Füchsl's DualSense Windows driver https://github.com/Ohjurot/DualSense-Windows + * @date 2020-11-25 + * + * @copyright Copyright (c) 2020 Ludwig Füchsl + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Minor updates by Kristian Sloth Lauszus. + */ + +#include "PS5Trigger.h" + +void PS5Trigger::processTrigger(uint8_t* buffer) { + // Switch on effect + switch (data.effectType) { + // Continious + case EffectType::ContinuousResitance: + // Mode + buffer[0x00] = 0x01; + // Parameters + buffer[0x01] = data.Continuous.startPosition; + buffer[0x02] = data.Continuous.force; + + break; + + // Section + case EffectType::SectionResitance: + // Mode + buffer[0x00] = 0x02; + // Parameters + buffer[0x01] = data.Section.startPosition; + buffer[0x02] = data.Section.endPosition; + + break; + + // EffectEx + case EffectType::EffectEx: + // Mode + buffer[0x00] = 0x02 | 0x20 | 0x04; + // Parameters + buffer[0x01] = 0xFF - data.EffectEx.startPosition; + // Keep flag + if (data.EffectEx.keepEffect) + buffer[0x02] = 0x02; + // Forces + buffer[0x04] = data.EffectEx.beginForce; + buffer[0x05] = data.EffectEx.middleForce; + buffer[0x06] = data.EffectEx.endForce; + // Frequency + buffer[0x09] = data.EffectEx.frequency / 2; + if(buffer[0x09] < 1) buffer[0x09] = 1; // minimum frequency + + break; + + // Calibrate + case EffectType::Calibrate: + // Mode + buffer[0x00] = 0xFC; + + break; + + // No resistance / default + case EffectType::NoResitance: + default: + // All zero + buffer[0x00] = 0x00; + buffer[0x01] = 0x00; + buffer[0x02] = 0x00; + + break; + } + reportChanged = false; +} diff --git a/PS5Trigger.h b/PS5Trigger.h new file mode 100644 index 00000000..6c1fc024 --- /dev/null +++ b/PS5Trigger.h @@ -0,0 +1,168 @@ +/** + * @file PS5Trigger.h + * @author Ludwig Füchsl, adapted for USB_Host_Library SAMD by Joseph Duchesne + * @brief Based on Ludwig Füchsl's DualSense Windows driver https://github.com/Ohjurot/DualSense-Windows + * @version 0.1 + * @date 2020-11-25 + * + * @copyright Copyright (c) 2020 Ludwig Füchsl + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Minor updates by Kristian Sloth Lauszus. + */ + +#ifndef _ps5trigger_h_ +#define _ps5trigger_h_ + +#include + +class PS5Trigger { +private: + // Type of trigger effect + typedef enum _EffectType : uint8_t { + NoResitance = 0x00, // No resistance is applied + ContinuousResitance = 0x01, // Continuous Resitance is applied + SectionResitance = 0x02, // Seciton resistance is appleyed + EffectEx = 0x26, // Extended trigger effect + Calibrate = 0xFC, // Calibrate triggers + } EffectType; + + // Trigger effect + typedef struct _EffectData { + // Trigger effect type + EffectType effectType; + + // Union for effect parameters + union { + // Union one raw data + uint8_t _u1_raw[6]; + + // For type == ContinuousResitance + struct { + uint8_t startPosition; // Start position of resistance + uint8_t force; // Force of resistance + uint8_t _pad[4]; // PAD / UNUSED + } __attribute__((packed)) Continuous; + + // For type == SectionResitance + struct { + uint8_t startPosition; // Start position of resistance + uint8_t endPosition; // End position of resistance (>= start) + uint8_t _pad[4]; // PAD / UNUSED + } __attribute__((packed)) Section; + + // For type == EffectEx + struct { + uint8_t startPosition; // Position at witch the effect starts + bool keepEffect; // Wher the effect should keep playing when trigger goes beyond 255 + uint8_t beginForce; // Force applied when trigger >= (255 / 2) + uint8_t middleForce; // Force applied when trigger <= (255 / 2) + uint8_t endForce; // Force applied when trigger is beyond 255 + uint8_t frequency; // Vibration frequency of the trigger + } __attribute__((packed)) EffectEx; + } __attribute__((packed)); + } __attribute__((packed)) EffectData; + + EffectData data; + +public: + bool reportChanged = false; + + /** + * @brief Apply the trigger data to a PS5 update buffer + * + * @param buffer The buffer at the start offset for this trigger data + */ + void processTrigger(uint8_t* buffer); + + /** + * Clear force feedback on trigger without report changed + */ + void Reset() { + data.effectType = EffectType::NoResitance; + + reportChanged = false; + }; + + /** + * Clear force feedback on trigger + */ + void clearTriggerForce() { + data.effectType = EffectType::NoResitance; + + reportChanged = true; + }; + + /** + * Set continuous force feedback on trigger + * @param start 0-255 trigger pull to start resisting + * @param force The force amount + */ + void setTriggerForce(uint8_t start, uint8_t force) { + if (force == 0) + data.effectType = EffectType::NoResitance; + else { + data.effectType = EffectType::ContinuousResitance; + data.Continuous.startPosition = start; + data.Continuous.force = force; + } + + reportChanged = true; + }; + + /** + * Set section force feedback on trigger + * @param start trigger pull to start resisting + * @param end trigger pull to stop resisting + */ + void setTriggerForceSection(uint8_t start, uint8_t end) { + data.effectType = EffectType::SectionResitance; + data.Section.startPosition = start; + data.Section.endPosition = end; + + reportChanged = true; + }; + + /** + * Set effect force feedback on trigger + * @param start trigger pull to start resisting + * @param keep Keep effect active after max trigger pull + * @param begin_force 0-255 force at start position + * @param mid_force 0-255 force half way between start and max pull + * @param end_force 0-255 force at max pull + * @param frequency Vibration frequency of the trigger + */ + void setTriggerForceEffect(uint8_t start, bool keep, uint8_t begin_force, uint8_t mid_force, uint8_t end_force, uint8_t frequency) { + data.effectType = EffectType::SectionResitance; + data.EffectEx.startPosition = start; + data.EffectEx.keepEffect = keep; + data.EffectEx.beginForce = begin_force; + data.EffectEx.middleForce = mid_force; + data.EffectEx.endForce = end_force; + data.EffectEx.frequency = frequency; + + reportChanged = true; + }; + +}; + +#endif diff --git a/PS5USB.h b/PS5USB.h new file mode 100644 index 00000000..a7ce9dfc --- /dev/null +++ b/PS5USB.h @@ -0,0 +1,154 @@ +/* Copyright (C) 2021 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 + + Thanks to Joseph Duchesne for the initial port. Data structure mapping partially based + on values from Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows + */ + +#ifndef _ps5usb_h_ +#define _ps5usb_h_ + +#include "hiduniversal.h" +#include "PS5Parser.h" + +#define PS5_VID 0x054C // Sony Corporation +#define PS5_PID 0x0CE6 // PS5 Controller + +/** + * This class implements support for the PS5 controller via USB. + * It uses the HIDUniversal class for all the USB communication. + */ +class PS5USB : public HIDUniversal, public PS5Parser { +public: + /** + * Constructor for the PS5USB class. + * @param p Pointer to the USB class instance. + */ + PS5USB(USB *p) : + HIDUniversal(p) { + PS5Parser::Reset(); + }; + + /** + * Used to check if a PS5 controller is connected. + * @return Returns true if it is connected. + */ + bool connected() { + return HIDUniversal::isReady() && HIDUniversal::VID == PS5_VID && HIDUniversal::PID == PS5_PID; + }; + + /** + * Used to call your own function when the device is successfully initialized. + * @param funcOnInit Function to call. + */ + void attachOnInit(void (*funcOnInit)(void)) { + pFuncOnInit = funcOnInit; + }; + +protected: + /** @name HIDUniversal implementation */ + /** + * Used to parse USB HID data. + * @param hid Pointer to the HID class. + * @param is_rpt_id Only used for Hubs. + * @param len The length of the incoming data. + * @param buf Pointer to the data buffer. + */ + virtual void ParseHIDData(USBHID *hid __attribute__((unused)), bool is_rpt_id __attribute__((unused)), uint8_t len, uint8_t *buf) { + if (HIDUniversal::VID == PS5_VID && HIDUniversal::PID == PS5_PID) + PS5Parser::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 uint8_t OnInitSuccessful() { + if (HIDUniversal::VID == PS5_VID && HIDUniversal::PID == PS5_PID) { + PS5Parser::Reset(); + if (pFuncOnInit) + pFuncOnInit(); // Call the user function + else + setLed(Blue); + }; + return 0; + }; + /**@}*/ + + /** @name PS5Parser implementation */ + virtual void sendOutputReport(PS5Output *output) { // Source: https://github.com/chrippa/ds4drv + // PS4 Source: https://github.com/chrippa/ds4drv + // PS5 values from https://www.reddit.com/r/gamedev/comments/jumvi5/dualsense_haptics_leds_and_more_hid_output_report/ + // and Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows + uint8_t buf[48]; + memset(buf, 0, sizeof(buf)); + + buf[0x00] = 0x02; // report type + buf[0x01] = 0xFF; // feature flags 1 + buf[0x02]= 0xF7; // feature flags 2 + buf[0x03] = output->smallRumble; // Small Rumble + buf[0x04] = output->bigRumble; // Big rumble + + // 5-7 headphone, speaker, mic volume, audio flags + + buf[0x09] = (uint8_t)output->microphoneLed; + + // 0x0A mute flags + + // Adaptive Triggers: 0x0B-0x14 right, 0x15 unknown, 0x16-0x1F left + rightTrigger.processTrigger(&buf[0x0B]); // right + leftTrigger.processTrigger(&buf[0x16]); // left + + // 0x20-0x24 unknown + // 0x25 trigger motor effect strengths + // 0x26 speaker volume + + // player LEDs + buf[0x27] = 0x03; // led brightness, pulse + buf[0x2A] = output->disableLeds ? 0x01 : 0x2; // led pulse option + // buf[0x2B] LED brightness, 0 = full, 1= medium, 2 = low + buf[0x2C] = output->playerLeds; // 5 white player LEDs + + // lightbar + buf[0x2D] = output->r; // Red + buf[0x2E] = output->g; // Green + buf[0x2F] = output->b; // Blue + + output->reportChanged = false; + + // The PS5 console actually set the four last bytes to a CRC32 checksum, but it seems like it is actually not needed + + pUsb->outTransfer(bAddress, epInfo[ hidInterfaces[0].epIndex[epInterruptOutIndex] ].epAddr, sizeof(buf), buf); + }; + /**@}*/ + + /** @name USBDeviceConfig implementation */ + /** + * Used by the USB core to check what this driver support. + * @param vid The device's VID. + * @param pid The device's PID. + * @return Returns true if the device's VID and PID matches this driver. + */ + virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { + return (vid == PS5_VID && pid == PS5_PID); + }; + /**@}*/ + +private: + void (*pFuncOnInit)(void); // Pointer to function called in onInit() +}; +#endif diff --git a/controllerEnums.h b/controllerEnums.h index c1a5a497..66963ee4 100644 --- a/controllerEnums.h +++ b/controllerEnums.h @@ -150,6 +150,7 @@ enum ButtonEnum { MENU = 5, /**@}*/ + /**@{*/ /** PS Buzz controllers */ RED = 0, YELLOW = 1, @@ -157,6 +158,11 @@ enum ButtonEnum { ORANGE = 3, BLUE = 4, /**@}*/ + + /**@{*/ + /** PS5 buttons */ + MICROPHONE = 18, + /**@}*/ }; /** Joysticks on the PS3 and Xbox controllers. */ diff --git a/examples/PS5USB/PS5USB.ino b/examples/PS5USB/PS5USB.ino new file mode 100644 index 00000000..3f008fc8 --- /dev/null +++ b/examples/PS5USB/PS5USB.ino @@ -0,0 +1,146 @@ +/* + Example sketch for the PS5 USB 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 + +// Satisfy the IDE, which needs to see the include statment in the ino too. +#ifdef dobogusinclude +#include +#endif +#include + +USB Usb; +PS5USB PS5(&Usb); + +bool printAngle, printTouch; +uint16_t lastMessageCounter = -1; +uint8_t player_led_mask = 0; +bool microphone_led = false; + +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\nPS5 USB Library Started")); +} + +void loop() { + Usb.Task(); + + if (PS5.connected() && lastMessageCounter != PS5.getMessageCounter()) { + lastMessageCounter = PS5.getMessageCounter(); + + if (PS5.getAnalogHat(LeftHatX) > 137 || PS5.getAnalogHat(LeftHatX) < 117 || PS5.getAnalogHat(LeftHatY) > 137 || PS5.getAnalogHat(LeftHatY) < 117 || PS5.getAnalogHat(RightHatX) > 137 || PS5.getAnalogHat(RightHatX) < 117 || PS5.getAnalogHat(RightHatY) > 137 || PS5.getAnalogHat(RightHatY) < 117) { + Serial.print(F("\r\nLeftHatX: ")); + Serial.print(PS5.getAnalogHat(LeftHatX)); + Serial.print(F("\tLeftHatY: ")); + Serial.print(PS5.getAnalogHat(LeftHatY)); + Serial.print(F("\tRightHatX: ")); + Serial.print(PS5.getAnalogHat(RightHatX)); + Serial.print(F("\tRightHatY: ")); + Serial.print(PS5.getAnalogHat(RightHatY)); + } + + if (PS5.getAnalogButton(L2) || PS5.getAnalogButton(R2)) { // These are the only analog buttons on the PS5 controller + Serial.print(F("\r\nL2: ")); + Serial.print(PS5.getAnalogButton(L2)); + Serial.print(F("\tR2: ")); + Serial.print(PS5.getAnalogButton(R2)); + } + + // Set the left trigger to resist at the right trigger's level + PS5.leftTrigger.setTriggerForce(PS5.getAnalogButton(R2), 255); + + if (PS5.getButtonClick(PS)) + Serial.print(F("\r\nPS")); + if (PS5.getButtonClick(TRIANGLE)) { + Serial.print(F("\r\nTriangle")); + PS5.setRumbleOn(RumbleLow); + } + if (PS5.getButtonClick(CIRCLE)) { + Serial.print(F("\r\nCircle")); + PS5.setRumbleOn(RumbleHigh); + } + if (PS5.getButtonClick(CROSS)) { + Serial.print(F("\r\nCross")); + + // Set the player LEDs + player_led_mask = (player_led_mask << 1) | 1; + if (player_led_mask > 0x1F) + player_led_mask = 0; + PS5.setPlayerLed(player_led_mask); // The bottom 5 bits set player LEDs + } + if (PS5.getButtonClick(SQUARE)) { + Serial.print(F("\r\nSquare")); + PS5.setRumbleOff(); + } + + if (PS5.getButtonClick(UP)) { + Serial.print(F("\r\nUp")); + PS5.setLed(Red); + } if (PS5.getButtonClick(RIGHT)) { + Serial.print(F("\r\nRight")); + PS5.setLed(Blue); + } if (PS5.getButtonClick(DOWN)) { + Serial.print(F("\r\nDown")); + PS5.setLed(Yellow); + } if (PS5.getButtonClick(LEFT)) { + Serial.print(F("\r\nLeft")); + PS5.setLed(Green); + } + + if (PS5.getButtonClick(L1)) + Serial.print(F("\r\nL1")); + if (PS5.getButtonClick(L3)) + Serial.print(F("\r\nL3")); + if (PS5.getButtonClick(R1)) + Serial.print(F("\r\nR1")); + if (PS5.getButtonClick(R3)) + Serial.print(F("\r\nR3")); + + if (PS5.getButtonClick(SHARE)) + Serial.print(F("\r\nShare")); + if (PS5.getButtonClick(OPTIONS)) { + Serial.print(F("\r\nOptions")); + printAngle = !printAngle; + } + if (PS5.getButtonClick(TOUCHPAD)) { + Serial.print(F("\r\nTouchpad")); + printTouch = !printTouch; + } + if (PS5.getButtonClick(MICROPHONE)) { + Serial.print(F("\r\nMicrophone")); + microphone_led = !microphone_led; + PS5.setMicLed(microphone_led); + } + + if (printAngle) { // Print angle calculated using the accelerometer only + Serial.print(F("\r\nPitch: ")); + Serial.print(PS5.getAngle(Pitch)); + Serial.print(F("\tRoll: ")); + Serial.print(PS5.getAngle(Roll)); + } + + if (printTouch) { // Print the x, y coordinates of the touchpad + if (PS5.isTouching(0) || PS5.isTouching(1)) // Print newline and carriage return if any of the fingers are touching the touchpad + Serial.print(F("\r\n")); + for (uint8_t i = 0; i < 2; i++) { // The touchpad track two fingers + if (PS5.isTouching(i)) { // Print the position of the finger if it is touching the touchpad + Serial.print(F("X")); Serial.print(i + 1); Serial.print(F(": ")); + Serial.print(PS5.getX(i)); + Serial.print(F("\tY")); Serial.print(i + 1); Serial.print(F(": ")); + Serial.print(PS5.getY(i)); + Serial.print(F("\t")); + } + } + } + } +} diff --git a/keywords.txt b/keywords.txt index 2513e98e..0f3ca523 100644 --- a/keywords.txt +++ b/keywords.txt @@ -135,6 +135,8 @@ SHARE LITERAL1 OPTIONS LITERAL1 TOUCHPAD LITERAL1 +MICROPHONE LITERAL1 + LeftHatX LITERAL1 LeftHatY LITERAL1 RightHatX LITERAL1 From 28a75dea6b615b517205932e243c583fed85b5eb Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 17 Jan 2021 23:34:31 +0100 Subject: [PATCH 34/93] PS5 is now also working via Bluetooth However the output report is still not working --- .github/workflows/main.yml | 2 +- BTD.cpp | 12 +- BTD.h | 2 +- PS5BT.h | 282 +++++++++++++++++++++++++++++ PS5Parser.cpp | 11 +- PS5Parser.h | 23 +-- PS5USB.h | 9 +- controllerEnums.h | 1 + examples/Bluetooth/PS5BT/PS5BT.ino | 159 ++++++++++++++++ examples/PS5USB/PS5USB.ino | 4 +- keywords.txt | 3 + 11 files changed, 480 insertions(+), 28 deletions(-) create mode 100644 PS5BT.h create mode 100644 examples/Bluetooth/PS5BT/PS5BT.ino diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ecc50c75..5cc29556 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: # find examples -type f -name "*.ino" | rev | cut -d/ -f2- | rev | sort | sed -z 's/\n/, /g' - example: [examples/acm/acm_terminal, examples/adk/adk_barcode, examples/adk/ArduinoBlinkLED, examples/adk/demokit_20, examples/adk/term_test, examples/adk/term_time, examples/Bluetooth/BTHID, examples/Bluetooth/PS3BT, examples/Bluetooth/PS3Multi, examples/Bluetooth/PS3SPP, examples/Bluetooth/PS4BT, examples/Bluetooth/SPP, examples/Bluetooth/SPPMulti, examples/Bluetooth/Wii, examples/Bluetooth/WiiBalanceBoard, examples/Bluetooth/WiiIRCamera, examples/Bluetooth/WiiMulti, examples/Bluetooth/WiiUProController, examples/board_qc, examples/cdc_XR21B1411/XR_terminal, examples/ftdi/USBFTDILoopback, examples/GPIO/Blink, examples/GPIO/Blink_LowLevel, examples/GPIO/Input, examples/HID/le3dp, examples/HID/scale, examples/HID/SRWS1, examples/HID/t16km, examples/HID/USBHIDBootKbd, examples/HID/USBHIDBootKbdAndMouse, examples/HID/USBHIDBootMouse, examples/HID/USBHID_desc, examples/HID/USBHIDJoystick, examples/HID/USBHIDMultimediaKbd, examples/hub_demo, examples/max_LCD, examples/pl2303/pl2303_gprs_terminal, examples/pl2303/pl2303_gps, examples/pl2303/pl2303_tinygps, examples/pl2303/pl2303_xbee_terminal, examples/PS3USB, examples/PS4USB, examples/PSBuzz, examples/USB_desc, examples/USBH_MIDI/bidirectional_converter, examples/USBH_MIDI/eVY1_sample, examples/USBH_MIDI/USBH_MIDI_dump, examples/USBH_MIDI/USB_MIDI_converter, examples/USBH_MIDI/USB_MIDI_converter_multi, examples/Xbox/XBOXOLD, examples/Xbox/XBOXONE, examples/Xbox/XBOXONESBT, examples/Xbox/XBOXRECV, examples/Xbox/XBOXUSB] + example: [examples/acm/acm_terminal, examples/adk/adk_barcode, examples/adk/ArduinoBlinkLED, examples/adk/demokit_20, examples/adk/term_test, examples/adk/term_time, examples/Bluetooth/BTHID, examples/Bluetooth/PS3BT, examples/Bluetooth/PS3Multi, examples/Bluetooth/PS3SPP, examples/Bluetooth/PS4BT, examples/Bluetooth/PS5BT, examples/Bluetooth/SPP, examples/Bluetooth/SPPMulti, examples/Bluetooth/Wii, examples/Bluetooth/WiiBalanceBoard, examples/Bluetooth/WiiIRCamera, examples/Bluetooth/WiiMulti, examples/Bluetooth/WiiUProController, examples/board_qc, examples/cdc_XR21B1411/XR_terminal, examples/ftdi/USBFTDILoopback, examples/GPIO/Blink, examples/GPIO/Blink_LowLevel, examples/GPIO/Input, examples/HID/le3dp, examples/HID/scale, examples/HID/SRWS1, examples/HID/t16km, examples/HID/USBHIDBootKbd, examples/HID/USBHIDBootKbdAndMouse, examples/HID/USBHIDBootMouse, examples/HID/USBHID_desc, examples/HID/USBHIDJoystick, examples/HID/USBHIDMultimediaKbd, examples/hub_demo, examples/max_LCD, examples/pl2303/pl2303_gprs_terminal, examples/pl2303/pl2303_gps, examples/pl2303/pl2303_tinygps, examples/pl2303/pl2303_xbee_terminal, examples/PS3USB, examples/PS4USB, examples/PS5USB, examples/PSBuzz, examples/USB_desc, examples/USBH_MIDI/bidirectional_converter, examples/USBH_MIDI/eVY1_sample, examples/USBH_MIDI/USBH_MIDI_dump, examples/USBH_MIDI/USB_MIDI_converter, examples/USBH_MIDI/USB_MIDI_converter_multi, examples/Xbox/XBOXOLD, examples/Xbox/XBOXONE, examples/Xbox/XBOXONESBT, examples/Xbox/XBOXRECV, examples/Xbox/XBOXUSB] steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 diff --git a/BTD.cpp b/BTD.cpp index 32efeb86..6ada433a 100644 --- a/BTD.cpp +++ b/BTD.cpp @@ -317,7 +317,7 @@ void BTD::Initialize() { incomingWii = false; connectToHIDDevice = false; incomingHIDDevice = false; - incomingPS4 = false; + incomingPSController = false; bAddress = 0; // Clear device address bNumEP = 1; // Must have to be reset to 1 qNextPollTime = 0; // Reset next poll time @@ -1011,9 +1011,9 @@ void BTD::HCI_task() { } if(classOfDevice[2] == 0 && classOfDevice[1] == 0x25 && classOfDevice[0] == 0x08 && strncmp((const char*)remote_name, "Wireless Controller", 19) == 0) { #ifdef DEBUG_USB_HOST - Notify(PSTR("\r\nPS4 controller is connecting"), 0x80); + Notify(PSTR("\r\nPS4/PS5 controller is connecting"), 0x80); #endif - incomingPS4 = true; + incomingPSController = true; } if((pairWithWii || pairWithHIDDevice) && checkRemoteName) hci_state = HCI_CONNECT_DEVICE_STATE; @@ -1034,8 +1034,8 @@ void BTD::HCI_task() { } D_PrintHex (disc_bdaddr[0], 0x80); #endif - if(incomingPS4) - connectToHIDDevice = true; // We should always connect to the PS4 controller + if(incomingPSController) + connectToHIDDevice = true; // We should always connect to the PS4/PS5 controller // Clear these flags for a new connection l2capConnectionClaimed = false; @@ -1068,7 +1068,7 @@ void BTD::HCI_task() { connectToWii = incomingWii = pairWithWii = false; connectToHIDDevice = incomingHIDDevice = pairWithHIDDevice = checkRemoteName = false; - incomingPS4 = false; + incomingPSController = false; hci_state = HCI_SCANNING_STATE; } diff --git a/BTD.h b/BTD.h index 5337dea9..a070cb58 100644 --- a/BTD.h +++ b/BTD.h @@ -575,7 +575,7 @@ private: bool pairWiiUsingSync; // True if pairing was done using the Wii SYNC button. bool checkRemoteName; // Used to check remote device's name before connecting. - bool incomingPS4; // True if a PS4 controller is connecting + bool incomingPSController; // True if a PS4/PS5 controller is connecting uint8_t classOfDevice[3]; // Class of device of last device /* Variables used by high level HCI task */ diff --git a/PS5BT.h b/PS5BT.h new file mode 100644 index 00000000..e00f0351 --- /dev/null +++ b/PS5BT.h @@ -0,0 +1,282 @@ +/* Copyright (C) 2021 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 _ps5bt_h_ +#define _ps5bt_h_ + +#include "BTHID.h" +#include "PS5Parser.h" + +/*const uint32_t crc32_table[] PROGMEM = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +uint32_t crc32(uint8_t *buffer, size_t length) { // Inspired by: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/libkern/crc32.c and http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=28214 + uint32_t crc = ~0L; // Initial value + for (size_t i = 0; i < length; i++) + crc = (crc >> 8) ^ pgm_read_dword(&crc32_table[*buffer++ ^ (crc & 0xFF)]); + return ~crc; +};*/ + +/* + * There are multiple 16-bit CRC polynomials in common use, but this is + * *the* standard CRC-32 polynomial, first popularized by Ethernet. + * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0 + */ +#if 0 +#define CRC32_POLY_LE 0xedb88320 +#define CRC32_POLY_BE 0x04c11db7 + +#define CRC_LE_BITS 1 + +typedef uint32_t u32; + +static inline u32 crc32_le_generic(u32 crc, unsigned char const *p, size_t len, const u32 (*tab)[256], u32 polynomial) +{ +#if CRC_LE_BITS == 1 + int i; + while (len--) { + crc ^= *p++; + for (i = 0; i < 8; i++) + crc = (crc >> 1) ^ ((crc & 1) ? polynomial : 0); + } +# elif CRC_LE_BITS == 2 + while (len--) { + crc ^= *p++; + crc = (crc >> 2) ^ tab[0][crc & 3]; + crc = (crc >> 2) ^ tab[0][crc & 3]; + crc = (crc >> 2) ^ tab[0][crc & 3]; + crc = (crc >> 2) ^ tab[0][crc & 3]; + } +# elif CRC_LE_BITS == 4 + while (len--) { + crc ^= *p++; + crc = (crc >> 4) ^ tab[0][crc & 15]; + crc = (crc >> 4) ^ tab[0][crc & 15]; + } +# elif CRC_LE_BITS == 8 + /* aka Sarwate algorithm */ + while (len--) { + crc ^= *p++; + crc = (crc >> 8) ^ tab[0][crc & 255]; + } +# else + crc = (__force u32) __cpu_to_le32(crc); + crc = crc32_body(crc, p, len, tab); + crc = __le32_to_cpu((__force __le32)crc); +#endif + return crc; +} + +#if CRC_LE_BITS == 1 +static u32 crc32_le(u32 crc, unsigned char const *p, size_t len) +{ + return crc32_le_generic(crc, p, len, NULL, CRC32_POLY_LE); +} +#else +static u32 crc32_le(u32 crc, unsigned char const *p, size_t len) +{ + return crc32_le_generic(crc, p, len, (const u32 (*)[256])crc32table_le, CRC32_POLY_LE); +} +#endif + +#endif + +/** + * This class implements support for the PS5 controller via Bluetooth. + * It uses the BTHID class for all the Bluetooth communication. + */ +class PS5BT : public BTHID, public PS5Parser { +public: + /** + * Constructor for the PS5BT 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 "0000" will be used. + */ + PS5BT(BTD *p, bool pair = false, const char *pin = "0000") : + BTHID(p, pair, pin) { + PS5Parser::Reset(); + }; + + /** + * Used to check if a PS5 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) { + PS5Parser::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() { + PS5Parser::Reset(); + enable_sixaxis(); // Make the controller send out the entire output report + if (pFuncOnInit) + pFuncOnInit(); // Call the user function + else + setLed(Blue); + }; + + /** Used to reset the different buffers to there default values */ + virtual void ResetBTHID() { + PS5Parser::Reset(); + }; + /**@}*/ + + /** @name PS5Parser implementation */ + virtual void sendOutputReport(PS5Output *output) { +#if 1 + return; // TODO: Fix this +#else + // See the series of patches here: https://patchwork.kernel.org/project/linux-input/patch/20201219062336.72568-14-roderick@gaikai.com/ + + uint8_t buf[1 /* BT Set Output Report */ + 1 /* report id */ + 1 /* seq_tag */ + 1 /* tag */ + 47 /* common */ + 24 /* reserved */ + 4 /* crc32 */]; + memset(buf, 0, sizeof(buf)); + + buf[0] = 0x52; // HID BT Set_report (0x50) | Report Type (Output 0x02) + + buf[0x01] = 0x31; // Report ID + buf[0x02] = (output_sequence << 4) | 0x0; // Highest 4-bit is a sequence number, which needs to be increased every report. Lowest 4-bit is tag and can be zero for now. + if(++output_sequence == 15) + output_sequence = 0; + buf[0x03] = 0x10; // Magic number must be set to 0x10 + + buf[0x01 + 3] = 0xFF; // feature flags 1 + buf[0x02 + 3]= 0xF7; // feature flags 2 + + buf[0x03 + 3] = output->smallRumble; // Small Rumble + buf[0x04 + 3] = output->bigRumble; // Big rumble + + // 5-7 headphone, speaker, mic volume, audio flags + + buf[0x09 + 3] = (uint8_t)output->microphoneLed; + + // 0x0A mute flags + + // Adaptive Triggers: 0x0B-0x14 right, 0x15 unknown, 0x16-0x1F left + rightTrigger.processTrigger(&buf[0x0B + 3]); // right + leftTrigger.processTrigger(&buf[0x16 + 3]); // left + + // 0x20-0x24 unknown + // 0x25 trigger motor effect strengths + // 0x26 speaker volume + + // player LEDs + buf[0x27 + 3] = 0x03; // led brightness, pulse + buf[0x2A + 3] = output->disableLeds ? 0x01 : 0x2; // led pulse option + // buf[0x2B] LED brightness, 0 = full, 1= medium, 2 = low + buf[0x2C + 3] = output->playerLeds; // 5 white player LEDs + + // lightbar + buf[0x2D + 3] = output->r; // Red + buf[0x2E + 3] = output->g; // Green + buf[0x2F + 3] = output->b; // Blue + + //uint32_t crc = crc32(&buf[1], 79 - 1 /* do not include the BT Set Output Report */ - 4 /* crc */); + + uint8_t seed = 0xA2; + uint32_t crc = crc32_le(0xFFFFFFFF, &seed, 1); + crc = ~crc32_le(crc, &buf[1], 79 - 1 /* do not include the BT Set Output Report */ - 4 /* crc */); + + buf[75] = crc; + buf[76] = crc >> 8; + buf[77] = crc >> 16; + buf[78] = crc >> 24; + + output->reportChanged = false; + + // The PS5 console actually set the four last bytes to a CRC32 checksum, but it seems like it is actually not needed + + HID_Command(buf, sizeof(buf)); +#endif + }; + /**@}*/ + +private: + uint8_t output_sequence = 0; + + void enable_sixaxis() { // Command used to make the PS5 controller send out the entire output report + // Request the paring info. This makes the controller send out the full report - see: https://patchwork.kernel.org/project/linux-input/patch/20201219062336.72568-14-roderick@gaikai.com/ + uint8_t buf[2]; + buf[0] = 0x43; // HID BT Get_report (0x40) | Report Type (Feature 0x03) + buf[1] = 9; // Report ID for paring info + + HID_Command(buf, 2); + }; + + void HID_Command(uint8_t *data, uint8_t nbytes) { + pBtd->L2CAP_Command(hci_handle, data, nbytes, control_scid[0], control_scid[1]); + }; +}; +#endif diff --git a/PS5Parser.cpp b/PS5Parser.cpp index b59d32c6..1a28e82e 100644 --- a/PS5Parser.cpp +++ b/PS5Parser.cpp @@ -15,7 +15,8 @@ e-mail : lauszus@gmail.com Thanks to Joseph Duchesne for the initial code. - Based on Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows PS5 port. + Based on Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows PS5 port + and the series of patches found here: https://patchwork.kernel.org/project/linux-input/patch/20201219062336.72568-14-roderick@gaikai.com/ */ #include "PS5Parser.h" @@ -88,19 +89,21 @@ void PS5Parser::Parse(uint8_t len, uint8_t *buf) { if (buf[0] == 0x01) // Check report ID memcpy(&ps5Data, buf + 1, min((uint8_t)(len - 1), MFK_CASTUINT8T sizeof(ps5Data))); - else if (buf[0] == 0x11) { // This report is send via Bluetooth, it has an offset of 2 compared to the USB data - if (len < 4) { + else if (buf[0] == 0x31) { // This report is send via Bluetooth, it has an offset of 2 compared to the USB data + if (len < 3) { #ifdef DEBUG_USB_HOST Notify(PSTR("\r\nReport is too short: "), 0x80); D_PrintHex (len, 0x80); #endif return; } - memcpy(&ps5Data, buf + 3, min((uint8_t)(len - 3), MFK_CASTUINT8T sizeof(ps5Data))); + memcpy(&ps5Data, buf + 2, min((uint8_t)(len - 2), MFK_CASTUINT8T sizeof(ps5Data))); } else { #ifdef DEBUG_USB_HOST Notify(PSTR("\r\nUnknown report id: "), 0x80); D_PrintHex (buf[0], 0x80); + Notify(PSTR(", len: "), 0x80); + D_PrintHex (len, 0x80); #endif return; } diff --git a/PS5Parser.h b/PS5Parser.h index c299b700..3286d6df 100644 --- a/PS5Parser.h +++ b/PS5Parser.h @@ -15,7 +15,8 @@ e-mail : lauszus@gmail.com Thanks to Joseph Duchesne for the initial code. - Based on Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows PS5 port. + Based on Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows PS5 port + and the series of patches found here: https://patchwork.kernel.org/project/linux-input/patch/20201219062336.72568-14-roderick@gaikai.com/ */ #ifndef _ps5parser_h_ @@ -32,7 +33,7 @@ const uint8_t PS5_BUTTONS[] PROGMEM = { DOWN, // DOWN LEFT, // LEFT - 0x0C, // SHARE + 0x0C, // CREATE 0x0D, // OPTIONS 0x0E, // L3 0x0F, // R3 @@ -64,7 +65,7 @@ union PS5Buttons { uint8_t r1 : 1; uint8_t l2 : 1; uint8_t r2 : 1; - uint8_t share : 1; + uint8_t create : 1; uint8_t menu : 1; uint8_t l3 : 1; uint8_t r3 : 1; @@ -96,7 +97,7 @@ union PS5Status { // second byte uint8_t mic : 1; - uint8_t dummy4 : 3; + uint8_t dummy3 : 3; } __attribute__((packed)); uint16_t val; } __attribute__((packed)); @@ -106,28 +107,28 @@ struct PS5Data { uint8_t hatValue[4]; // 0-3 bytes uint8_t trigger[2]; // 4-5 - uint8_t dummy; // 6 unknown + uint8_t sequence_number; // 6 PS5Buttons btn; // 7-9 - uint8_t dummy2[5]; // 0xA-0xD unknown + uint8_t reserved[5]; // 0xA-0xD /* Gyro and accelerometer values */ int16_t gyroX, gyroZ, gyroY; // 0x0F - 0x14 int16_t accX, accZ, accY; // 0x15-0x1A + int32_t sensor_timestamp; - uint8_t dummy3[5]; // 0x1B - 0x1F unknown + uint8_t reserved2; // 0x20 - 0x23 touchpad point 1 // 0x24 - 0x27 touchpad point 2 ps5TouchpadXY xy; - uint8_t dummy4; //0x28 unknown + uint8_t reserved3; // 0x28 uint8_t rightTriggerFeedback; // 0x29 uint8_t leftTriggerFeedback; // 0x2A - - uint8_t dummy5[10]; // 0x2B - 0x34 unknown + uint8_t reserved4[10]; // 0x2B - 0x34 // status bytes 0x35-0x36 PS5Status status; @@ -260,7 +261,7 @@ public: * @return The battery level in the range 0-15. */ /*uint8_t getBatteryLevel() { - return ps5Data.status.battery; + return ps5Data.status.battery; // TODO: Where to read the battery level? };*/ /** diff --git a/PS5USB.h b/PS5USB.h index a7ce9dfc..b656f161 100644 --- a/PS5USB.h +++ b/PS5USB.h @@ -93,13 +93,16 @@ protected: virtual void sendOutputReport(PS5Output *output) { // Source: https://github.com/chrippa/ds4drv // PS4 Source: https://github.com/chrippa/ds4drv // PS5 values from https://www.reddit.com/r/gamedev/comments/jumvi5/dualsense_haptics_leds_and_more_hid_output_report/ - // and Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows - uint8_t buf[48]; + // , Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows + // and the series of patches found here: https://patchwork.kernel.org/project/linux-input/patch/20201219062336.72568-14-roderick@gaikai.com/ + uint8_t buf[1 /* report id */ + 47 /* common */]; memset(buf, 0, sizeof(buf)); - buf[0x00] = 0x02; // report type + buf[0x00] = 0x02; // Report ID + buf[0x01] = 0xFF; // feature flags 1 buf[0x02]= 0xF7; // feature flags 2 + buf[0x03] = output->smallRumble; // Small Rumble buf[0x04] = output->bigRumble; // Big rumble diff --git a/controllerEnums.h b/controllerEnums.h index 66963ee4..3b45d5ef 100644 --- a/controllerEnums.h +++ b/controllerEnums.h @@ -161,6 +161,7 @@ enum ButtonEnum { /**@{*/ /** PS5 buttons */ + CREATE = 4, MICROPHONE = 18, /**@}*/ }; diff --git a/examples/Bluetooth/PS5BT/PS5BT.ino b/examples/Bluetooth/PS5BT/PS5BT.ino new file mode 100644 index 00000000..39bf361a --- /dev/null +++ b/examples/Bluetooth/PS5BT/PS5BT.ino @@ -0,0 +1,159 @@ +/* + Example sketch for the PS5 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 +#include + +// Satisfy the IDE, which needs to see the include statment in the ino too. +#ifdef dobogusinclude +#include +#endif +#include + +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 PS5BT class in two ways */ +// This will start an inquiry and then pair with the PS5 controller - you only have to do this once +// You will need to hold down the PS and Share button at the same time, the PS5 controller will then start to blink rapidly indicating that it is in pairing mode +PS5BT PS5(&Btd, PAIR); + +// After that you can simply create the instance like so and then press the PS button on the device +//PS5BT PS5(&Btd); + +bool printAngle, printTouch; +uint16_t lastMessageCounter = -1; +uint8_t player_led_mask = 0; +bool microphone_led = false; + +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\nPS5 Bluetooth Library Started")); +} + +void loop() { + Usb.Task(); + + if (PS5.connected() && lastMessageCounter != PS5.getMessageCounter()) { + lastMessageCounter = PS5.getMessageCounter(); + + if (PS5.getAnalogHat(LeftHatX) > 137 || PS5.getAnalogHat(LeftHatX) < 117 || PS5.getAnalogHat(LeftHatY) > 137 || PS5.getAnalogHat(LeftHatY) < 117 || PS5.getAnalogHat(RightHatX) > 137 || PS5.getAnalogHat(RightHatX) < 117 || PS5.getAnalogHat(RightHatY) > 137 || PS5.getAnalogHat(RightHatY) < 117) { + Serial.print(F("\r\nLeftHatX: ")); + Serial.print(PS5.getAnalogHat(LeftHatX)); + Serial.print(F("\tLeftHatY: ")); + Serial.print(PS5.getAnalogHat(LeftHatY)); + Serial.print(F("\tRightHatX: ")); + Serial.print(PS5.getAnalogHat(RightHatX)); + Serial.print(F("\tRightHatY: ")); + Serial.print(PS5.getAnalogHat(RightHatY)); + } + + if (PS5.getAnalogButton(L2) || PS5.getAnalogButton(R2)) { // These are the only analog buttons on the PS5 controller + Serial.print(F("\r\nL2: ")); + Serial.print(PS5.getAnalogButton(L2)); + Serial.print(F("\tR2: ")); + Serial.print(PS5.getAnalogButton(R2)); + } + + // Set the left trigger to resist at the right trigger's level + PS5.leftTrigger.setTriggerForce(PS5.getAnalogButton(R2), 255); + + if (PS5.getButtonClick(PS)) { + Serial.print(F("\r\nPS")); + PS5.disconnect(); + } else { + if (PS5.getButtonClick(TRIANGLE)) { + Serial.print(F("\r\nTriangle")); + PS5.setRumbleOn(RumbleLow); + } + if (PS5.getButtonClick(CIRCLE)) { + Serial.print(F("\r\nCircle")); + PS5.setRumbleOn(RumbleHigh); + } + if (PS5.getButtonClick(CROSS)) { + Serial.print(F("\r\nCross")); + + // Set the player LEDs + player_led_mask = (player_led_mask << 1) | 1; + if (player_led_mask > 0x1F) + player_led_mask = 0; + PS5.setPlayerLed(player_led_mask); // The bottom 5 bits set player LEDs + } + if (PS5.getButtonClick(SQUARE)) { + Serial.print(F("\r\nSquare")); + PS5.setRumbleOff(); + } + + if (PS5.getButtonClick(UP)) { + Serial.print(F("\r\nUp")); + PS5.setLed(Red); + } if (PS5.getButtonClick(RIGHT)) { + Serial.print(F("\r\nRight")); + PS5.setLed(Blue); + } if (PS5.getButtonClick(DOWN)) { + Serial.print(F("\r\nDown")); + PS5.setLed(Yellow); + } if (PS5.getButtonClick(LEFT)) { + Serial.print(F("\r\nLeft")); + PS5.setLed(Green); + } + + if (PS5.getButtonClick(L1)) + Serial.print(F("\r\nL1")); + if (PS5.getButtonClick(L3)) + Serial.print(F("\r\nL3")); + if (PS5.getButtonClick(R1)) + Serial.print(F("\r\nR1")); + if (PS5.getButtonClick(R3)) + Serial.print(F("\r\nR3")); + + if (PS5.getButtonClick(CREATE)) + Serial.print(F("\r\nCreate")); + if (PS5.getButtonClick(OPTIONS)) { + Serial.print(F("\r\nOptions")); + printAngle = !printAngle; + } + if (PS5.getButtonClick(TOUCHPAD)) { + Serial.print(F("\r\nTouchpad")); + printTouch = !printTouch; + } + if (PS5.getButtonClick(MICROPHONE)) { + Serial.print(F("\r\nMicrophone")); + microphone_led = !microphone_led; + PS5.setMicLed(microphone_led); + } + + if (printAngle) { // Print angle calculated using the accelerometer only + Serial.print(F("\r\nPitch: ")); + Serial.print(PS5.getAngle(Pitch)); + Serial.print(F("\tRoll: ")); + Serial.print(PS5.getAngle(Roll)); + } + + if (printTouch) { // Print the x, y coordinates of the touchpad + if (PS5.isTouching(0) || PS5.isTouching(1)) // Print newline and carriage return if any of the fingers are touching the touchpad + Serial.print(F("\r\n")); + for (uint8_t i = 0; i < 2; i++) { // The touchpad track two fingers + if (PS5.isTouching(i)) { // Print the position of the finger if it is touching the touchpad + Serial.print(F("X")); Serial.print(i + 1); Serial.print(F(": ")); + Serial.print(PS5.getX(i)); + Serial.print(F("\tY")); Serial.print(i + 1); Serial.print(F(": ")); + Serial.print(PS5.getY(i)); + Serial.print(F("\t")); + } + } + } + } + } +} diff --git a/examples/PS5USB/PS5USB.ino b/examples/PS5USB/PS5USB.ino index 3f008fc8..a28ab3fd 100644 --- a/examples/PS5USB/PS5USB.ino +++ b/examples/PS5USB/PS5USB.ino @@ -106,8 +106,8 @@ void loop() { if (PS5.getButtonClick(R3)) Serial.print(F("\r\nR3")); - if (PS5.getButtonClick(SHARE)) - Serial.print(F("\r\nShare")); + if (PS5.getButtonClick(CREATE)) + Serial.print(F("\r\nCreate")); if (PS5.getButtonClick(OPTIONS)) { Serial.print(F("\r\nOptions")); printAngle = !printAngle; diff --git a/keywords.txt b/keywords.txt index 0f3ca523..ac952691 100644 --- a/keywords.txt +++ b/keywords.txt @@ -36,6 +36,8 @@ PS3BT KEYWORD1 PS3USB KEYWORD1 PS4BT KEYWORD1 PS4USB KEYWORD1 +PS5BT KEYWORD1 +PS5USB KEYWORD1 #################################################### # Methods and Functions (KEYWORD2) @@ -135,6 +137,7 @@ SHARE LITERAL1 OPTIONS LITERAL1 TOUCHPAD LITERAL1 +CREATE LITERAL1 MICROPHONE LITERAL1 LeftHatX LITERAL1 From 9a0a4940b3155748603ee2b27716a7fec5304784 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Tue, 19 Jan 2021 10:57:14 +0100 Subject: [PATCH 35/93] Setting the LED, lightbar, rumble via Bluetooth is now working --- BTHID.cpp | 20 +++- PS5BT.h | 133 +++++++---------------- PS5Parser.cpp | 10 +- PS5Parser.h | 14 ++- PS5USB.h | 10 +- examples/Bluetooth/PS5BT/PS5BT.ino | 169 +++++++++++++++-------------- examples/PS5USB/PS5USB.ino | 10 +- 7 files changed, 173 insertions(+), 193 deletions(-) diff --git a/BTHID.cpp b/BTHID.cpp index 00b09085..d0d93c2e 100644 --- a/BTHID.cpp +++ b/BTHID.cpp @@ -336,11 +336,11 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { Notify(PSTR(" "), 0x80); } #endif - if(l2capinbuf[8] == 0xA1) { // HID_THDR_DATA_INPUT + if(l2capinbuf[8] == 0xA1) { // HID BT DATA (0xA0) | Report Type (Input 0x01) uint16_t length = ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); - ParseBTHIDData((uint8_t)(length - 1), &l2capinbuf[9]); + ParseBTHIDData((uint8_t)(length - 1), &l2capinbuf[9]); // First byte will be the report ID - switch(l2capinbuf[9]) { + switch(l2capinbuf[9]) { // Report ID case 0x01: // Keyboard or Joystick events if(pRptParser[KEYBOARD_PARSER_ID]) pRptParser[KEYBOARD_PARSER_ID]->Parse(reinterpret_cast(this), 0, (uint8_t)(length - 2), &l2capinbuf[10]); // Use reinterpret_cast again to extract the instance @@ -357,6 +357,11 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { break; #endif } + } else { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nUnhandled L2CAP interrupt report: "), 0x80); + D_PrintHex (l2capinbuf[8], 0x80); +#endif } } else if(l2capinbuf[6] == control_dcid[0] && l2capinbuf[7] == control_dcid[1]) { // l2cap_control #ifdef PRINTREPORT @@ -366,9 +371,14 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { Notify(PSTR(" "), 0x80); } #endif - if(l2capinbuf[8] == 0xA3) { + if(l2capinbuf[8] == 0xA3) { // HID BT DATA (0xA0) | Report Type (Feature 0x03) uint16_t length = ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); - ParseBTHIDControlData((uint8_t)(length - 1), &l2capinbuf[9]); + ParseBTHIDControlData((uint8_t)(length - 1), &l2capinbuf[9]); // First byte will be the report ID + } else { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nUnhandled L2CAP control report: "), 0x80); + D_PrintHex (l2capinbuf[8], 0x80); +#endif } } #ifdef EXTRADEBUG diff --git a/PS5BT.h b/PS5BT.h index e00f0351..dbb3c8a6 100644 --- a/PS5BT.h +++ b/PS5BT.h @@ -21,7 +21,12 @@ #include "BTHID.h" #include "PS5Parser.h" -/*const uint32_t crc32_table[] PROGMEM = { +/** + * Generated from the standard Ethernet CRC-32 polynomial: + * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0 + * Source: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/libkern/crc32.c + */ +const uint32_t crc32_table[] PROGMEM = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, @@ -67,76 +72,35 @@ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; -uint32_t crc32(uint8_t *buffer, size_t length) { // Inspired by: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/libkern/crc32.c and http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=28214 - uint32_t crc = ~0L; // Initial value - for (size_t i = 0; i < length; i++) - crc = (crc >> 8) ^ pgm_read_dword(&crc32_table[*buffer++ ^ (crc & 0xFF)]); - return ~crc; -};*/ - /* * There are multiple 16-bit CRC polynomials in common use, but this is * *the* standard CRC-32 polynomial, first popularized by Ethernet. * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0 */ -#if 0 #define CRC32_POLY_LE 0xedb88320 -#define CRC32_POLY_BE 0x04c11db7 -#define CRC_LE_BITS 1 - -typedef uint32_t u32; - -static inline u32 crc32_le_generic(u32 crc, unsigned char const *p, size_t len, const u32 (*tab)[256], u32 polynomial) -{ -#if CRC_LE_BITS == 1 +static inline uint32_t crc32_le_generic(uint32_t crc, uint8_t const *p, size_t len, uint32_t polynomial) { + // Source: https://github.com/torvalds/linux/blob/c4cf498dc0241fa2d758dba177634268446afb06/lib/crc32.c int i; while (len--) { crc ^= *p++; for (i = 0; i < 8; i++) crc = (crc >> 1) ^ ((crc & 1) ? polynomial : 0); } -# elif CRC_LE_BITS == 2 - while (len--) { - crc ^= *p++; - crc = (crc >> 2) ^ tab[0][crc & 3]; - crc = (crc >> 2) ^ tab[0][crc & 3]; - crc = (crc >> 2) ^ tab[0][crc & 3]; - crc = (crc >> 2) ^ tab[0][crc & 3]; - } -# elif CRC_LE_BITS == 4 - while (len--) { - crc ^= *p++; - crc = (crc >> 4) ^ tab[0][crc & 15]; - crc = (crc >> 4) ^ tab[0][crc & 15]; - } -# elif CRC_LE_BITS == 8 - /* aka Sarwate algorithm */ - while (len--) { - crc ^= *p++; - crc = (crc >> 8) ^ tab[0][crc & 255]; - } -# else - crc = (__force u32) __cpu_to_le32(crc); - crc = crc32_body(crc, p, len, tab); - crc = __le32_to_cpu((__force __le32)crc); -#endif return crc; } -#if CRC_LE_BITS == 1 -static u32 crc32_le(u32 crc, unsigned char const *p, size_t len) -{ - return crc32_le_generic(crc, p, len, NULL, CRC32_POLY_LE); -} -#else -static u32 crc32_le(u32 crc, unsigned char const *p, size_t len) -{ - return crc32_le_generic(crc, p, len, (const u32 (*)[256])crc32table_le, CRC32_POLY_LE); -} -#endif - +static inline uint32_t crc32(uint32_t crc, const void *buf, size_t size) { +#if 1 // Use a table, as it's faster, but takes up more space + // Inspired by: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/libkern/crc32.c + const uint8_t *p = (const uint8_t*)buf; + while (size--) + crc = pgm_read_dword(&crc32_table[*p++ ^ (crc & 0xFF)]) ^ (crc >> 8); + return crc; +#else // Can be used to save flash, but is slower + return crc32_le_generic(crc, (uint8_t const*)buf, size, CRC32_POLY_LE); #endif +}; /** * This class implements support for the PS5 controller via Bluetooth. @@ -151,7 +115,7 @@ public: * @param pin Write the pin to BTD#btdPin. If argument is omitted, then "0000" will be used. */ PS5BT(BTD *p, bool pair = false, const char *pin = "0000") : - BTHID(p, pair, pin) { + BTHID(p, pair, pin), output_sequence_counter(0) { PS5Parser::Reset(); }; @@ -182,10 +146,10 @@ protected: virtual void OnInitBTHID() { PS5Parser::Reset(); enable_sixaxis(); // Make the controller send out the entire output report - if (pFuncOnInit) - pFuncOnInit(); // Call the user function - else - setLed(Blue); + + // Only call this is a user function has not been set + if (!pFuncOnInit) + setLed(Red); // Set the LED to red, as the PS5 controller turns Bluetooth when searching for a device }; /** Used to reset the different buffers to there default values */ @@ -196,20 +160,17 @@ protected: /** @name PS5Parser implementation */ virtual void sendOutputReport(PS5Output *output) { -#if 1 - return; // TODO: Fix this -#else - // See the series of patches here: https://patchwork.kernel.org/project/linux-input/patch/20201219062336.72568-14-roderick@gaikai.com/ - - uint8_t buf[1 /* BT Set Output Report */ + 1 /* report id */ + 1 /* seq_tag */ + 1 /* tag */ + 47 /* common */ + 24 /* reserved */ + 4 /* crc32 */]; + // See the series of patches here: https://patchwork.kernel.org/project/linux-input/cover/20201219062336.72568-1-roderick@gaikai.com/ + uint8_t buf[1 /* BT DATA Output Report */ + 1 /* report id */ + 1 /* seq_tag */ + 1 /* tag */ + 47 /* common */ + 24 /* reserved */ + 4 /* crc32 */]; memset(buf, 0, sizeof(buf)); - buf[0] = 0x52; // HID BT Set_report (0x50) | Report Type (Output 0x02) + // Send as a Bluetooth HID DATA output report on the interrupt channel + buf[0] = 0xA2; // HID BT DATA (0xA0) | Report Type (Output 0x02) buf[0x01] = 0x31; // Report ID - buf[0x02] = (output_sequence << 4) | 0x0; // Highest 4-bit is a sequence number, which needs to be increased every report. Lowest 4-bit is tag and can be zero for now. - if(++output_sequence == 15) - output_sequence = 0; + buf[0x02] = (output_sequence_counter << 4) | 0x0; // Highest 4-bit is a sequence number, which needs to be increased every report. Lowest 4-bit is tag and can be zero for now. + if(++output_sequence_counter == 15) + output_sequence_counter = 0; buf[0x03] = 0x10; // Magic number must be set to 0x10 buf[0x01 + 3] = 0xFF; // feature flags 1 @@ -243,40 +204,30 @@ protected: buf[0x2E + 3] = output->g; // Green buf[0x2F + 3] = output->b; // Blue - //uint32_t crc = crc32(&buf[1], 79 - 1 /* do not include the BT Set Output Report */ - 4 /* crc */); - - uint8_t seed = 0xA2; - uint32_t crc = crc32_le(0xFFFFFFFF, &seed, 1); - crc = ~crc32_le(crc, &buf[1], 79 - 1 /* do not include the BT Set Output Report */ - 4 /* crc */); - - buf[75] = crc; - buf[76] = crc >> 8; - buf[77] = crc >> 16; - buf[78] = crc >> 24; + uint32_t crc = ~crc32(0xFFFFFFFF, buf, sizeof(buf) - 4 /* Do not include the crc32 */); // Note how the report type is also included in the output report + buf[75] = crc & 0xFF; + buf[76] = (crc >> 8) & 0xFF; + buf[77] = (crc >> 16); + buf[78] = (crc >> 24); output->reportChanged = false; - // The PS5 console actually set the four last bytes to a CRC32 checksum, but it seems like it is actually not needed - - HID_Command(buf, sizeof(buf)); -#endif + // Send the Bluetooth DATA output report on the interrupt channel + pBtd->L2CAP_Command(hci_handle, buf, sizeof(buf), interrupt_scid[0], interrupt_scid[1]); }; /**@}*/ private: - uint8_t output_sequence = 0; - void enable_sixaxis() { // Command used to make the PS5 controller send out the entire output report - // Request the paring info. This makes the controller send out the full report - see: https://patchwork.kernel.org/project/linux-input/patch/20201219062336.72568-14-roderick@gaikai.com/ + // Request the paring info. This makes the controller send out the full report - see: https://patchwork.kernel.org/project/linux-input/cover/20201219062336.72568-1-roderick@gaikai.com/ uint8_t buf[2]; buf[0] = 0x43; // HID BT Get_report (0x40) | Report Type (Feature 0x03) - buf[1] = 9; // Report ID for paring info + buf[1] = 0x09; // Report ID for paring info - HID_Command(buf, 2); + // Send the Bluetooth Get_report Feature report on the control channel + pBtd->L2CAP_Command(hci_handle, buf, 2, control_scid[0], control_scid[1]); }; - void HID_Command(uint8_t *data, uint8_t nbytes) { - pBtd->L2CAP_Command(hci_handle, data, nbytes, control_scid[0], control_scid[1]); - }; + uint8_t output_sequence_counter; }; #endif diff --git a/PS5Parser.cpp b/PS5Parser.cpp index 1a28e82e..fa3576c2 100644 --- a/PS5Parser.cpp +++ b/PS5Parser.cpp @@ -16,7 +16,7 @@ Thanks to Joseph Duchesne for the initial code. Based on Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows PS5 port - and the series of patches found here: https://patchwork.kernel.org/project/linux-input/patch/20201219062336.72568-14-roderick@gaikai.com/ + and the series of patches found here: https://patchwork.kernel.org/project/linux-input/cover/20201219062336.72568-1-roderick@gaikai.com/ */ #include "PS5Parser.h" @@ -80,7 +80,8 @@ uint8_t PS5Parser::getAnalogHat(AnalogHatEnum a) { void PS5Parser::Parse(uint8_t len, uint8_t *buf) { if (len > 1 && buf) { #ifdef PRINTREPORT - Notify(PSTR("\r\n"), 0x80); + Notify(PSTR("\r\nLen: "), 0x80); Notify(len, 0x80); + Notify(PSTR(", data: "), 0x80); for (uint8_t i = 0; i < len; i++) { D_PrintHex (buf[i], 0x80); Notify(PSTR(" "), 0x80); @@ -89,7 +90,7 @@ void PS5Parser::Parse(uint8_t len, uint8_t *buf) { if (buf[0] == 0x01) // Check report ID memcpy(&ps5Data, buf + 1, min((uint8_t)(len - 1), MFK_CASTUINT8T sizeof(ps5Data))); - else if (buf[0] == 0x31) { // This report is send via Bluetooth, it has an offset of 2 compared to the USB data + else if (buf[0] == 0x31) { // This report is send via Bluetooth, it has an offset of 1 compared to the USB data if (len < 3) { #ifdef DEBUG_USB_HOST Notify(PSTR("\r\nReport is too short: "), 0x80); @@ -127,8 +128,9 @@ void PS5Parser::Parse(uint8_t len, uint8_t *buf) { oldDpad = newDpad; } } + + message_counter++; } - message_counter++; if (ps5Output.reportChanged || leftTrigger.reportChanged || rightTrigger.reportChanged) sendOutputReport(&ps5Output); // Send output report diff --git a/PS5Parser.h b/PS5Parser.h index 3286d6df..df7eb3b2 100644 --- a/PS5Parser.h +++ b/PS5Parser.h @@ -16,7 +16,7 @@ Thanks to Joseph Duchesne for the initial code. Based on Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows PS5 port - and the series of patches found here: https://patchwork.kernel.org/project/linux-input/patch/20201219062336.72568-14-roderick@gaikai.com/ + and the series of patches found here: https://patchwork.kernel.org/project/linux-input/cover/20201219062336.72568-1-roderick@gaikai.com/ */ #ifndef _ps5parser_h_ @@ -146,14 +146,14 @@ struct PS5Output { /** This class parses all the data sent by the PS5 controller */ class PS5Parser { public: - PS5Trigger leftTrigger; - PS5Trigger rightTrigger; - /** Constructor for the PS5Parser class. */ PS5Parser() : leftTrigger(), rightTrigger() { Reset(); }; + /** Used these to manipulate the haptic triggers */ + PS5Trigger leftTrigger, rightTrigger; + /** @name PS5 Controller functions */ /** * getButtonPress(ButtonEnum b) will return true as long as the button is held down. @@ -227,9 +227,9 @@ public: */ float getAngle(AngleEnum a) { if (a == Pitch) - return (atan2f(ps5Data.accY, ps5Data.accZ) + PI) * RAD_TO_DEG; + return (atan2f(-ps5Data.accY, -ps5Data.accZ) + PI) * RAD_TO_DEG; else - return (atan2f(ps5Data.accX, ps5Data.accZ) + PI) * RAD_TO_DEG; + return (atan2f(ps5Data.accX, -ps5Data.accZ) + PI) * RAD_TO_DEG; }; /** @@ -356,6 +356,7 @@ public: */ void setPlayerLed(uint8_t mask) { ps5Output.playerLeds = mask; + ps5Output.reportChanged = true; } /** Use to turn the microphone LED off. */ @@ -369,6 +370,7 @@ public: */ void setMicLed(bool on) { ps5Output.microphoneLed = on ? 1 : 0; + ps5Output.reportChanged = true; } /** Get the incoming message count. */ diff --git a/PS5USB.h b/PS5USB.h index b656f161..7c7ee793 100644 --- a/PS5USB.h +++ b/PS5USB.h @@ -83,7 +83,7 @@ protected: if (pFuncOnInit) pFuncOnInit(); // Call the user function else - setLed(Blue); + setLed(Red); // Set the LED to red, so it is consistent with the PS5BT driver }; return 0; }; @@ -92,9 +92,9 @@ protected: /** @name PS5Parser implementation */ virtual void sendOutputReport(PS5Output *output) { // Source: https://github.com/chrippa/ds4drv // PS4 Source: https://github.com/chrippa/ds4drv - // PS5 values from https://www.reddit.com/r/gamedev/comments/jumvi5/dualsense_haptics_leds_and_more_hid_output_report/ - // , Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows - // and the series of patches found here: https://patchwork.kernel.org/project/linux-input/patch/20201219062336.72568-14-roderick@gaikai.com/ + // PS5 values from https://www.reddit.com/r/gamedev/comments/jumvi5/dualsense_haptics_leds_and_more_hid_output_report/, + // Ludwig Füchsl's https://github.com/Ohjurot/DualSense-Windows and + // the series of patches found here: https://patchwork.kernel.org/project/linux-input/cover/20201219062336.72568-1-roderick@gaikai.com/ uint8_t buf[1 /* report id */ + 47 /* common */]; memset(buf, 0, sizeof(buf)); @@ -133,7 +133,7 @@ protected: output->reportChanged = false; - // The PS5 console actually set the four last bytes to a CRC32 checksum, but it seems like it is actually not needed + // There is no need to calculate a crc32 when the controller is connected via USB pUsb->outTransfer(bAddress, epInfo[ hidInterfaces[0].epIndex[epInterruptOutIndex] ].epAddr, sizeof(buf), buf); }; diff --git a/examples/Bluetooth/PS5BT/PS5BT.ino b/examples/Bluetooth/PS5BT/PS5BT.ino index 39bf361a..6d6fc0d4 100644 --- a/examples/Bluetooth/PS5BT/PS5BT.ino +++ b/examples/Bluetooth/PS5BT/PS5BT.ino @@ -1,7 +1,7 @@ /* Example sketch for the PS5 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 + send me an e-mail: lauszus@gmail.com */ #include @@ -25,10 +25,11 @@ PS5BT PS5(&Btd, PAIR); // After that you can simply create the instance like so and then press the PS button on the device //PS5BT PS5(&Btd); -bool printAngle, printTouch; +bool printAngle = false, printTouch = false; uint16_t lastMessageCounter = -1; uint8_t player_led_mask = 0; bool microphone_led = false; +uint32_t ps_timer; void setup() { Serial.begin(115200); @@ -67,91 +68,101 @@ void loop() { } // Set the left trigger to resist at the right trigger's level - PS5.leftTrigger.setTriggerForce(PS5.getAnalogButton(R2), 255); + static uint8_t oldR2Value = 0xFF; + if (PS5.getAnalogButton(R2) != oldR2Value) { + oldR2Value = PS5.getAnalogButton(R2); + PS5.leftTrigger.setTriggerForce(oldR2Value, 255); + } - if (PS5.getButtonClick(PS)) { + // Hold the PS button for 1 second to disconnect the controller + // This prevents the controller from disconnecting when it is reconnected, + // as the PS button is sent when it reconnects + if (PS5.getButtonPress(PS)) { + if (millis() - ps_timer > 1000) + PS5.disconnect(); + } else + ps_timer = millis(); + + if (PS5.getButtonClick(PS)) Serial.print(F("\r\nPS")); - PS5.disconnect(); - } else { - if (PS5.getButtonClick(TRIANGLE)) { - Serial.print(F("\r\nTriangle")); - PS5.setRumbleOn(RumbleLow); - } - if (PS5.getButtonClick(CIRCLE)) { - Serial.print(F("\r\nCircle")); - PS5.setRumbleOn(RumbleHigh); - } - if (PS5.getButtonClick(CROSS)) { - Serial.print(F("\r\nCross")); + if (PS5.getButtonClick(TRIANGLE)) { + Serial.print(F("\r\nTriangle")); + PS5.setRumbleOn(RumbleLow); + } + if (PS5.getButtonClick(CIRCLE)) { + Serial.print(F("\r\nCircle")); + PS5.setRumbleOn(RumbleHigh); + } + if (PS5.getButtonClick(CROSS)) { + Serial.print(F("\r\nCross")); - // Set the player LEDs - player_led_mask = (player_led_mask << 1) | 1; - if (player_led_mask > 0x1F) - player_led_mask = 0; - PS5.setPlayerLed(player_led_mask); // The bottom 5 bits set player LEDs - } - if (PS5.getButtonClick(SQUARE)) { - Serial.print(F("\r\nSquare")); - PS5.setRumbleOff(); - } + // Set the player LEDs + player_led_mask = (player_led_mask << 1) | 1; + if (player_led_mask > 0x1F) + player_led_mask = 0; + PS5.setPlayerLed(player_led_mask); // The bottom 5 bits set player LEDs + } + if (PS5.getButtonClick(SQUARE)) { + Serial.print(F("\r\nSquare")); + PS5.setRumbleOff(); + } - if (PS5.getButtonClick(UP)) { - Serial.print(F("\r\nUp")); - PS5.setLed(Red); - } if (PS5.getButtonClick(RIGHT)) { - Serial.print(F("\r\nRight")); - PS5.setLed(Blue); - } if (PS5.getButtonClick(DOWN)) { - Serial.print(F("\r\nDown")); - PS5.setLed(Yellow); - } if (PS5.getButtonClick(LEFT)) { - Serial.print(F("\r\nLeft")); - PS5.setLed(Green); - } + if (PS5.getButtonClick(UP)) { + Serial.print(F("\r\nUp")); + PS5.setLed(Red); + } if (PS5.getButtonClick(RIGHT)) { + Serial.print(F("\r\nRight")); + PS5.setLed(Blue); + } if (PS5.getButtonClick(DOWN)) { + Serial.print(F("\r\nDown")); + PS5.setLed(Yellow); + } if (PS5.getButtonClick(LEFT)) { + Serial.print(F("\r\nLeft")); + PS5.setLed(Green); + } - if (PS5.getButtonClick(L1)) - Serial.print(F("\r\nL1")); - if (PS5.getButtonClick(L3)) - Serial.print(F("\r\nL3")); - if (PS5.getButtonClick(R1)) - Serial.print(F("\r\nR1")); - if (PS5.getButtonClick(R3)) - Serial.print(F("\r\nR3")); + if (PS5.getButtonClick(L1)) + Serial.print(F("\r\nL1")); + if (PS5.getButtonClick(L3)) + Serial.print(F("\r\nL3")); + if (PS5.getButtonClick(R1)) + Serial.print(F("\r\nR1")); + if (PS5.getButtonClick(R3)) + Serial.print(F("\r\nR3")); - if (PS5.getButtonClick(CREATE)) - Serial.print(F("\r\nCreate")); - if (PS5.getButtonClick(OPTIONS)) { - Serial.print(F("\r\nOptions")); - printAngle = !printAngle; - } - if (PS5.getButtonClick(TOUCHPAD)) { - Serial.print(F("\r\nTouchpad")); - printTouch = !printTouch; - } - if (PS5.getButtonClick(MICROPHONE)) { - Serial.print(F("\r\nMicrophone")); - microphone_led = !microphone_led; - PS5.setMicLed(microphone_led); - } + if (PS5.getButtonClick(CREATE)) + Serial.print(F("\r\nCreate")); + if (PS5.getButtonClick(OPTIONS)) { + Serial.print(F("\r\nOptions")); + printAngle = !printAngle; + } + if (PS5.getButtonClick(TOUCHPAD)) { + Serial.print(F("\r\nTouchpad")); + printTouch = !printTouch; + } + if (PS5.getButtonClick(MICROPHONE)) { + Serial.print(F("\r\nMicrophone")); + microphone_led = !microphone_led; + PS5.setMicLed(microphone_led); + } - if (printAngle) { // Print angle calculated using the accelerometer only - Serial.print(F("\r\nPitch: ")); - Serial.print(PS5.getAngle(Pitch)); - Serial.print(F("\tRoll: ")); - Serial.print(PS5.getAngle(Roll)); - } + if (printAngle) { // Print angle calculated using the accelerometer only + Serial.print(F("\r\nPitch: ")); + Serial.print(PS5.getAngle(Pitch)); + Serial.print(F("\tRoll: ")); + Serial.print(PS5.getAngle(Roll)); + } - if (printTouch) { // Print the x, y coordinates of the touchpad - if (PS5.isTouching(0) || PS5.isTouching(1)) // Print newline and carriage return if any of the fingers are touching the touchpad - Serial.print(F("\r\n")); - for (uint8_t i = 0; i < 2; i++) { // The touchpad track two fingers - if (PS5.isTouching(i)) { // Print the position of the finger if it is touching the touchpad - Serial.print(F("X")); Serial.print(i + 1); Serial.print(F(": ")); - Serial.print(PS5.getX(i)); - Serial.print(F("\tY")); Serial.print(i + 1); Serial.print(F(": ")); - Serial.print(PS5.getY(i)); - Serial.print(F("\t")); - } + if (printTouch) { // Print the x, y coordinates of the touchpad + if (PS5.isTouching(0) || PS5.isTouching(1)) // Print newline and carriage return if any of the fingers are touching the touchpad + Serial.print(F("\r\n")); + for (uint8_t i = 0; i < 2; i++) { // The touchpad track two fingers + if (PS5.isTouching(i)) { // Print the position of the finger if it is touching the touchpad + Serial.print(F("X")); Serial.print(i + 1); Serial.print(F(": ")); + Serial.print(PS5.getX(i)); + Serial.print(F("\tY")); Serial.print(i + 1); Serial.print(F(": ")); + Serial.print(PS5.getY(i)); + Serial.print(F("\t")); } } } diff --git a/examples/PS5USB/PS5USB.ino b/examples/PS5USB/PS5USB.ino index a28ab3fd..36582dad 100644 --- a/examples/PS5USB/PS5USB.ino +++ b/examples/PS5USB/PS5USB.ino @@ -1,7 +1,7 @@ /* Example sketch for the PS5 USB 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 + send me an e-mail: lauszus@gmail.com */ #include @@ -15,7 +15,7 @@ USB Usb; PS5USB PS5(&Usb); -bool printAngle, printTouch; +bool printAngle = false, printTouch = false; uint16_t lastMessageCounter = -1; uint8_t player_led_mask = 0; bool microphone_led = false; @@ -57,7 +57,11 @@ void loop() { } // Set the left trigger to resist at the right trigger's level - PS5.leftTrigger.setTriggerForce(PS5.getAnalogButton(R2), 255); + static uint8_t oldR2Value = 0xFF; + if (PS5.getAnalogButton(R2) != oldR2Value) { + oldR2Value = PS5.getAnalogButton(R2); + PS5.leftTrigger.setTriggerForce(oldR2Value, 255); + } if (PS5.getButtonClick(PS)) Serial.print(F("\r\nPS")); From fdb0ee31e79debd2f807996a7554177212404aa0 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Tue, 19 Jan 2021 20:29:04 +0100 Subject: [PATCH 36/93] Do not try to parse the PS5 status byte for now --- PS5Parser.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/PS5Parser.h b/PS5Parser.h index df7eb3b2..0b311f42 100644 --- a/PS5Parser.h +++ b/PS5Parser.h @@ -124,6 +124,7 @@ struct PS5Data { // 0x24 - 0x27 touchpad point 2 ps5TouchpadXY xy; +#if 0 // The status byte depends on if it's sent via USB or Bluetooth, so is not parsed for now uint8_t reserved3; // 0x28 uint8_t rightTriggerFeedback; // 0x29 @@ -132,6 +133,7 @@ struct PS5Data { // status bytes 0x35-0x36 PS5Status status; +#endif } __attribute__((packed)); struct PS5Output { @@ -256,14 +258,17 @@ public: } }; +#if 0 // Seems to only be available via Bluetooth, so have been disabled for now /** * Return the battery level of the PS5 controller. * @return The battery level in the range 0-15. */ - /*uint8_t getBatteryLevel() { - return ps5Data.status.battery; // TODO: Where to read the battery level? - };*/ + uint8_t getBatteryLevel() { + return ps5Data.status.battery; + }; +#endif +#if 0 // These are only valid via USB, so have been commented out for now /** * Use this to check if an USB cable is connected to the PS5 controller. * @return Returns true if an USB cable is connected. @@ -287,6 +292,7 @@ public: bool getMicStatus() { return ps5Data.status.mic; }; +#endif /** Turn both rumble and the LEDs off. */ void setAllOff() { From 534fdb30cecc8cd1db4cbbcbd1d96afc6967f7df Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Tue, 19 Jan 2021 20:40:06 +0100 Subject: [PATCH 37/93] Updated documentation, as the PS5 controller is now working via USB and Bluetooth --- README.md | 21 +++++++++++++++++++-- library.json | 2 +- library.properties | 2 +- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 61b921a8..968377d7 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ For more information about the hardware see the [Hardware Manual](https://chome. * __Alexei Glushchenko__ - * Developers of the USB Core, HID, FTDI, ADK, ACM, and PL2303 libraries * __Kristian Sloth Lauszus__ - - * 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 + * Developer of the [BTD](#bluetooth-libraries), [BTHID](#bthid-library), [SPP](#spp-library), [PS5](#ps5-library), [PS4](#ps4-library), [PS3](#ps3-library), [Wii](#wii-library), [Xbox](#xbox-library), and [PSBuzz](#ps-buzz-library) libraries * __Andrew Kroll__ - * Major contributor to mass storage code * __guruthree__ @@ -44,6 +44,7 @@ For more information about the hardware see the [Hardware Manual](https://chome. * [Bluetooth libraries](#bluetooth-libraries) * [BTHID library](#bthid-library) * [SPP library](#spp-library) + * [PS5 Library](#ps5-library) * [PS4 Library](#ps4-library) * [PS3 Library](#ps3-library) * [Xbox Libraries](#xbox-libraries) @@ -171,9 +172,25 @@ More information can be found at these blog posts: To implement the SPP protocol I used a Bluetooth sniffing tool called [PacketLogger](http://www.tkjelectronics.com/uploads/PacketLogger.zip) developed by Apple. It enables me to see the Bluetooth communication between my Mac and any device. +### PS5 Library + +The PS5 library is split up into the [PS5BT](PS5BT.h) and the [PS5USB](PS5USB.h) library. These allow you to use the Sony PS5 controller via Bluetooth and USB. + +The [PS5BT.ino](examples/Bluetooth/PS5BT/PS5BT.ino) and [PS5USB.ino](examples/PS5USB/PS5USB.ino) examples shows how to easily read the buttons, joysticks, touchpad and IMU on the controller via Bluetooth and USB respectively. It is also possible to control the rumble, lightbar, microphone LED and player LEDs on the controller. Furthermore the new haptic trigger effects are also supported. + +To pair with the PS5 controller via Bluetooth you need create the PS5BT instance like so: ```PS5BT PS5(&Btd, PAIR);``` and then hold down the Create button and then hold down the PS without releasing the Create button. The PS5 controller will then start to blink blue indicating that it is in pairing mode. + +It should then automatically pair the dongle with your controller. This only have to be done once. + +Thanks to Joseph Duchesne for the initial USB code. + +The driver is based on the official Sony driver for Linux: . + +Also thanks to Ludwig Füchsl's for his work on the haptic triggers. + ### PS4 Library -The PS4BT library is split up into the [PS4BT](PS4BT.h) and the [PS4USB](PS4USB.h) library. These allow you to use the Sony PS4 controller via Bluetooth and USB. +The PS4 library is split up into the [PS4BT](PS4BT.h) and the [PS4USB](PS4USB.h) library. These allow you to use the Sony PS4 controller via Bluetooth and USB. The [PS4BT.ino](examples/Bluetooth/PS4BT/PS4BT.ino) and [PS4USB.ino](examples/PS4USB/PS4USB.ino) examples shows how to easily read the buttons, joysticks, touchpad and IMU on the controller via Bluetooth and USB respectively. It is also possible to control the rumble and light on the controller and get the battery level. diff --git a/library.json b/library.json index 0c240db4..559b7682 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "USB-Host-Shield-20", - "keywords": "usb, host, ftdi, adk, acm, pl2303, hid, bluetooth, spp, ps3, ps4, buzz, xbox, wii, mass storage", + "keywords": "usb, host, ftdi, adk, acm, pl2303, hid, bluetooth, spp, ps3, ps4, ps5, buzz, xbox, wii, mass storage", "description": "Revision 2.0 of MAX3421E-based USB Host Shield Library", "authors": [ diff --git a/library.properties b/library.properties index edd159ff..05a06ab9 100644 --- a/library.properties +++ b/library.properties @@ -3,7 +3,7 @@ version=1.4.0 author=Oleg Mazurov (Circuits@Home) , Kristian Sloth Lauszus , Andrew Kroll , Alexei Glushchenko (Circuits@Home) maintainer=Oleg Mazurov (Circuits@Home) , Kristian Sloth Lauszus , Andrew Kroll sentence=Revision 2.0 of MAX3421E-based USB Host Shield Library. -paragraph=Supports HID devices, FTDI, ADK, ACM, PL2303, Bluetooth HID devices, SPP communication and mass storage devices. Furthermore it supports PS3, PS4, PS Buzz, Wii and Xbox controllers. +paragraph=Supports HID devices, FTDI, ADK, ACM, PL2303, Bluetooth HID devices, SPP communication and mass storage devices. Furthermore it supports PS3, PS4, PS5, PS Buzz, Wii and Xbox controllers. category=Other url=https://github.com/felis/USB_Host_Shield_2.0 architectures=* From 3915e96962e8a71334d90da1119f5a65d3907e4c Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 24 Jan 2021 14:33:44 +0100 Subject: [PATCH 38/93] Added support for rumble functionality on the Xbox One S controller via Bluetooth --- XBOXONESBT.h | 17 +++-------------- XBOXONESParser.cpp | 18 +++++++++++++++--- XBOXONESParser.h | 6 +++--- examples/Xbox/XBOXONESBT/XBOXONESBT.ino | 2 -- 4 files changed, 21 insertions(+), 22 deletions(-) diff --git a/XBOXONESBT.h b/XBOXONESBT.h index 58395790..53f81c7a 100644 --- a/XBOXONESBT.h +++ b/XBOXONESBT.h @@ -74,28 +74,17 @@ protected: }; /**@}*/ -#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[0] = 0xA2; // HID BT DATA (0xA0) | 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 (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]); + // Send the Bluetooth DATA output report on the interrupt channel + pBtd->L2CAP_Command(hci_handle, buf, sizeof(buf), interrupt_scid[0], interrupt_scid[1]); }; /**@}*/ -#endif }; #endif diff --git a/XBOXONESParser.cpp b/XBOXONESParser.cpp index e8f0064f..9e5edd59 100644 --- a/XBOXONESParser.cpp +++ b/XBOXONESParser.cpp @@ -183,11 +183,24 @@ void XBOXONESParser::Reset() { oldDpad = 0; }; -#if 0 +void XBOXONESParser::setRumbleOff() { + // See: https://lore.kernel.org/patchwork/patch/973394/ + uint8_t buf[8]; + buf[0] = 0x0F; // Disable all rumble motors + buf[1] = 0; + buf[2] = 0; + buf[3] = 0; + buf[4] = 0; + buf[5] = 0; // Duration of effect in 10 ms + buf[6] = 0; // Start delay in 10 ms + buf[7] = 0; // Loop count + sendOutputReport(buf, sizeof(buf)); +} + 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[0] = 1 << 3 /* Left trigger */ | 1 << 2 /* Right trigger */ | 1 << 1 /* Left motor */ | 1 << 0 /* Right motor */; buf[1] = leftTrigger; buf[2] = rightTrigger; buf[3] = leftMotor; @@ -197,4 +210,3 @@ void XBOXONESParser::setRumbleOn(uint8_t leftTrigger, uint8_t rightTrigger, uint buf[7] = 255; // Loop count sendOutputReport(buf, sizeof(buf)); } -#endif diff --git a/XBOXONESParser.h b/XBOXONESParser.h index 41dc30b2..d031631a 100644 --- a/XBOXONESParser.h +++ b/XBOXONESParser.h @@ -81,7 +81,7 @@ public: int16_t getAnalogHat(AnalogHatEnum a); /** Used to set the rumble off. */ - //void setRumbleOff(); + void setRumbleOff(); /** * Used to turn on rumble continuously. @@ -90,7 +90,7 @@ public: * @param leftMotor Left motor force. * @param rightMotor Right motor force. */ - //void setRumbleOn(uint8_t leftTrigger, uint8_t rightTrigger, uint8_t leftMotor, uint8_t rightMotor); + void setRumbleOn(uint8_t leftTrigger, uint8_t rightTrigger, uint8_t leftMotor, uint8_t rightMotor); protected: /** @@ -108,7 +108,7 @@ protected: * @param output Pointer to data buffer. * @param nbytes Bytes to send. */ - //virtual void sendOutputReport(uint8_t *data, uint8_t nbytes) = 0; + virtual void sendOutputReport(uint8_t *data, uint8_t nbytes) = 0; private: bool checkDpad(ButtonEnum b); // Used to check Xbox One S DPAD buttons diff --git a/examples/Xbox/XBOXONESBT/XBOXONESBT.ino b/examples/Xbox/XBOXONESBT/XBOXONESBT.ino index c691e9bf..e7d6c419 100644 --- a/examples/Xbox/XBOXONESBT/XBOXONESBT.ino +++ b/examples/Xbox/XBOXONESBT/XBOXONESBT.ino @@ -77,7 +77,6 @@ void loop() { 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) { @@ -90,7 +89,6 @@ void loop() { else Xbox.setRumbleOff(); } -#endif if (Xbox.getButtonClick(UP)) Serial.println(F("Up")); From 785886eadf6e75cf1d01c9c9c775cfcd977b2bb6 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 24 Jan 2021 14:36:45 +0100 Subject: [PATCH 39/93] The user function was called twice in the PS4BT and XBOXONESBT drivers --- PS4BT.h | 6 ++---- XBOXONESBT.h | 2 -- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/PS4BT.h b/PS4BT.h index b7eb4b5a..3a23ee0c 100644 --- a/PS4BT.h +++ b/PS4BT.h @@ -65,10 +65,8 @@ protected: virtual void OnInitBTHID() { PS4Parser::Reset(); enable_sixaxis(); // Make the controller send out the entire output report - if (pFuncOnInit) - pFuncOnInit(); // Call the user function - else - setLed(Blue); + if (!pFuncOnInit) + setLed(Blue); // Only call this is a user function has not been set }; /** Used to reset the different buffers to there default values */ diff --git a/XBOXONESBT.h b/XBOXONESBT.h index 58395790..f3e7e4da 100644 --- a/XBOXONESBT.h +++ b/XBOXONESBT.h @@ -64,8 +64,6 @@ protected: */ virtual void OnInitBTHID() { XBOXONESParser::Reset(); - if (pFuncOnInit) - pFuncOnInit(); // Call the user function }; /** Used to reset the different buffers to there default values */ From a3e52f8ee69357df0c65cd973f4cef7cdb52f170 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 24 Jan 2021 14:40:25 +0100 Subject: [PATCH 40/93] Fixed typo --- PS5Parser.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PS5Parser.h b/PS5Parser.h index 0b311f42..49fadd7b 100644 --- a/PS5Parser.h +++ b/PS5Parser.h @@ -218,7 +218,7 @@ public: * @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used. * @return Return the value of the counter, note that it is only a 7-bit value. */ - uint8_t getTouchCounter(uint8_t finger = 00) { + uint8_t getTouchCounter(uint8_t finger = 0) { return ps5Data.xy.finger[finger].counter; }; From e82f26db7a1cfc7568bbf7c9d6b029bc0669ee8d Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 24 Jan 2021 14:41:21 +0100 Subject: [PATCH 41/93] Fix compiler warning --- PS4USB.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PS4USB.h b/PS4USB.h index 9d9dbb40..83cf122f 100644 --- a/PS4USB.h +++ b/PS4USB.h @@ -65,7 +65,7 @@ protected: * @param len The length of the incoming data. * @param buf Pointer to the data buffer. */ - virtual void ParseHIDData(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) { + virtual void ParseHIDData(USBHID *hid __attribute__((unused)), bool is_rpt_id __attribute__((unused)), uint8_t len, uint8_t *buf) { if (HIDUniversal::VID == PS4_VID && (HIDUniversal::PID == PS4_PID || HIDUniversal::PID == PS4_PID_SLIM)) PS4Parser::Parse(len, buf); }; From b4bbfecd093b0032fa8795369ed914854a8b706e Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Tue, 26 Jan 2021 23:41:05 +0100 Subject: [PATCH 42/93] Store the timestamp of the last Bluetooth DATA input report received on the interrupt channel This can be used detect if the connection to a Bluetooth device is lost fx if the battery runs out or it gets out of range --- BTHID.cpp | 2 ++ BTHID.h | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/BTHID.cpp b/BTHID.cpp index d0d93c2e..7fb429ad 100644 --- a/BTHID.cpp +++ b/BTHID.cpp @@ -337,6 +337,8 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { } #endif if(l2capinbuf[8] == 0xA1) { // HID BT DATA (0xA0) | Report Type (Input 0x01) + lastBtDataInputIntMillis = (uint32_t)millis(); // Store the timestamp of the report + uint16_t length = ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); ParseBTHIDData((uint8_t)(length - 1), &l2capinbuf[9]); // First byte will be the report ID diff --git a/BTHID.h b/BTHID.h index f493adce..dabf70f9 100644 --- a/BTHID.h +++ b/BTHID.h @@ -93,6 +93,15 @@ public: pBtd->pairWithHID(); }; + /** + * Used to get the millis() of the last Bluetooth DATA input report received on the interrupt channel. + * This can be used detect if the connection to a Bluetooth device is lost fx if the battery runs out or if it gets out of range. + * @return Timestamp in milliseconds of the last Bluetooth DATA input report received on the interrupt channel. + */ + uint32_t getLastMessageTime() { + return lastBtDataInputIntMillis; + }; + protected: /** @name BluetoothService implementation */ /** @@ -173,5 +182,7 @@ private: uint8_t interrupt_dcid[2]; // L2CAP device CID for HID_Interrupt - Always 0x0071 uint8_t sdp_dcid[2]; uint8_t l2cap_state; + + uint32_t lastBtDataInputIntMillis; // Variable used to store the millis value of the last Bluetooth DATA input report received on the interrupt channel }; #endif From 66b03d99d6b67878add6ad1c4eca90609b5d223f Mon Sep 17 00:00:00 2001 From: Yuuichi Akagawa Date: Fri, 29 Jan 2021 07:26:53 +0900 Subject: [PATCH 43/93] Fix for handling MIDI channels in the bidirectional converter example (#586) --- .../bidirectional_converter/bidirectional_converter.ino | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino b/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino index b752a05f..0b9854d5 100644 --- a/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino +++ b/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino @@ -1,7 +1,7 @@ /* ******************************************************************************* * Legacy Serial MIDI and USB Host bidirectional converter - * Copyright (C) 2013-2020 Yuuichi Akagawa + * Copyright (C) 2013-2021 Yuuichi Akagawa * * for use with Arduino MIDI library * https://github.com/FortySevenEffects/arduino_midi_library/ @@ -44,6 +44,7 @@ MIDI_CREATE_DEFAULT_INSTANCE(); ////////////////////////// USB Usb; +USBHub Hub1(&Usb); USBH_MIDI Midi(&Usb); void MIDI_poll(); @@ -87,6 +88,9 @@ void loop() //SysEx is handled by event. break; default : + if( msg[0] < 0xf0 ){ + msg[0] |= MIDI.getChannel() - 1; + } msg[1] = MIDI.getData1(); msg[2] = MIDI.getData2(); Midi.SendData(msg, 0); From e81b04519c6c1dbd6641495a3f0dbd5ca4e9e810 Mon Sep 17 00:00:00 2001 From: Yuuichi Akagawa Date: Fri, 29 Jan 2021 07:33:17 +0900 Subject: [PATCH 44/93] Hub commented out --- .../bidirectional_converter/bidirectional_converter.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino b/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino index 0b9854d5..25f8e00b 100644 --- a/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino +++ b/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino @@ -44,7 +44,7 @@ MIDI_CREATE_DEFAULT_INSTANCE(); ////////////////////////// USB Usb; -USBHub Hub1(&Usb); +//USBHub Hub1(&Usb); USBH_MIDI Midi(&Usb); void MIDI_poll(); From ecce90052c709d7efea3e5b4a28e2fbe1c425864 Mon Sep 17 00:00:00 2001 From: Yuuichi Akagawa Date: Tue, 2 Feb 2021 00:16:16 +0900 Subject: [PATCH 45/93] add comment --- .../bidirectional_converter/bidirectional_converter.ino | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino b/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino index 25f8e00b..18c7cc8a 100644 --- a/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino +++ b/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino @@ -88,8 +88,10 @@ void loop() //SysEx is handled by event. break; default : + // If this is a channel messages, set the channel number. if( msg[0] < 0xf0 ){ - msg[0] |= MIDI.getChannel() - 1; + // The getchannel() returns 1-16, but the MIDI status byte starts at 0. + msg[0] |= MIDI.getChannel() - 1; } msg[1] = MIDI.getData1(); msg[2] = MIDI.getData2(); From 715fef691f2046038e1632237cae1da080720b68 Mon Sep 17 00:00:00 2001 From: Dennis Frett Date: Mon, 25 Jan 2021 11:07:05 +0100 Subject: [PATCH 46/93] Add support for MiniDSP 2x4HD. --- MiniDSP.cpp | 117 ++++++++++++++++++++++++++ MiniDSP.h | 157 +++++++++++++++++++++++++++++++++++ examples/MiniDSP/MiniDSP.ino | 48 +++++++++++ 3 files changed, 322 insertions(+) create mode 100644 MiniDSP.cpp create mode 100644 MiniDSP.h create mode 100644 examples/MiniDSP/MiniDSP.ino diff --git a/MiniDSP.cpp b/MiniDSP.cpp new file mode 100644 index 00000000..dd7a32bd --- /dev/null +++ b/MiniDSP.cpp @@ -0,0 +1,117 @@ +/* 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 "MiniDSP.h" + +namespace { +uint8_t RequestStatusOutputCommand[] = {0x05, 0xFF, 0xDA, 0x02}; +uint8_t StatusInputCommand[]{0x05, 0xFF, 0xDA}; + +// Returns first byte of the sum of given bytes. +uint8_t Checksum(const uint8_t *data, uint16_t nbytes) { + int sum = 0; + for (int i = 0; i < nbytes; i++) { + sum += data[i]; + } + + return sum & 0xFF; +} +} // namespace + +void MiniDSP::ParseHIDData(USBHID *hid __attribute__((unused)), + bool is_rpt_id __attribute__((unused)), uint8_t len, + uint8_t *buf) { + + // Only care about valid data for the MiniDSP 2x4HD. + if (HIDUniversal::VID != MINIDSP_VID || HIDUniversal::PID != MINIDSP_PID || + len <= 2 || buf == nullptr) { + return; + } + + // Check if this is a status update. + // First byte is the length, we ignore that for now. + if (memcmp(buf + 1, StatusInputCommand, sizeof(StatusInputCommand)) == 0) { + + // Parse data. + // Response is of format [ length ] [ 0x05 0xFF 0xDA ] [ volume ] [ muted ]. + const auto newVolume = buf[sizeof(StatusInputCommand) + 1]; + const auto newIsMuted = (bool)buf[sizeof(StatusInputCommand) + 2]; + + const auto volumeUpdated = newVolume != volume; + const auto isMutedUpdated = newIsMuted != isMuted; + + // Update status. + volume = newVolume; + isMuted = newIsMuted; + + // Call callbacks. + if (volumeChangeCallback != nullptr && volumeUpdated) { + volumeChangeCallback(volume); + } + + if (mutedChangeCallback != nullptr && isMutedUpdated) { + mutedChangeCallback(isMuted); + } + } +}; + +uint8_t MiniDSP::OnInitSuccessful() { + // Verify we're actually connected to the MiniDSP 2x4HD. + if (HIDUniversal::VID != MINIDSP_VID || HIDUniversal::PID != MINIDSP_PID) { + return 0; + } + + // Request current status so we can initialize the values. + RequestStatus(); + + if (onInitCallback != nullptr) { + onInitCallback(); + } + + return 0; +}; + +void MiniDSP::SendCommand(uint8_t *command, uint16_t command_length) const { + // Only send command if we're actually connected to the MiniDSP 2x4HD. + if (HIDUniversal::VID != MINIDSP_VID || HIDUniversal::PID != MINIDSP_PID) { + return; + } + + // Message is padded to 64 bytes with 0xFF and is of format: + // [ length (command + checksum byte) ] [ command ] [ checksum ] [ OxFF... ] + + // MiniDSP expects 64 byte messages. + uint8_t buf[64]; + + // Set length, including checksum byte. + buf[0] = command_length + 1; + + // Copy actual command. + memcpy(&buf[1], command, command_length); + + const auto checksumOffset = command_length + 1; + + // Set checksum byte. + buf[checksumOffset] = Checksum(buf, command_length + 1); + + // Pad the rest. + memset(&buf[checksumOffset + 1], 0xFF, sizeof(buf) - checksumOffset - 1); + + pUsb->outTransfer(bAddress, epInfo[epInterruptOutIndex].epAddr, sizeof(buf), + buf); +} + +void MiniDSP::RequestStatus() const { + SendCommand(RequestStatusOutputCommand, sizeof(RequestStatusOutputCommand)); +} diff --git a/MiniDSP.h b/MiniDSP.h new file mode 100644 index 00000000..cea2aa34 --- /dev/null +++ b/MiniDSP.h @@ -0,0 +1,157 @@ +/* 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 + */ + +#pragma once + +#include "controllerEnums.h" +#include "hiduniversal.h" + +#define MINIDSP_VID 0x2752 // MiniDSP +#define MINIDSP_PID 0x0011 // MiniDSP 2x4HD + +/** + * Arduino MiniDSP 2x4HD USB Host Driver by Dennis Frett. + * + * This class implements support for the MiniDSP 2x4HD via USB. + * Based on NodeJS implementation by Mathieu Rene: + * https://github.com/mrene/node-minidsp and the Python implementation by Mark + * Kubiak: https://github.com/markubiak/python3-minidsp. + * + * It uses the HIDUniversal class for all the USB communication. + */ +class MiniDSP : public HIDUniversal { +public: + /** + * Constructor for the MiniDSP class. + * @param p Pointer to the USB class instance. + */ + MiniDSP(USB *p) : HIDUniversal(p){}; + + /** + * Used to check if a MiniDSP 2x4HD is connected. + * @return Returns true if it is connected. + */ + bool connected() { + return HIDUniversal::isReady() && HIDUniversal::VID == MINIDSP_VID && + HIDUniversal::PID == MINIDSP_PID; + }; + + /** + * Used to call your own function when the device is successfully initialized. + * @param func Function to call. + */ + void SetOnInitCallback(void (*func)(void)) { onInitCallback = func; }; + + /** + * Used to call your own function when the volume has changed. + * The volume is passed as an unsigned integer that represents twice the -dB + * value. Example: 19 represents -9.5dB. + * @param func Function to call. + */ + void SetVolumeChangeCallback(void (*func)(uint8_t)) { + volumeChangeCallback = func; + } + + /** + * Used to call your own function when the muted status has changed. + * The muted status is passed as a boolean. True means muted, false means + * unmuted. + * @param func Function to call. + */ + void SetMutedChangeCallback(void (*func)(bool)) { + mutedChangeCallback = func; + } + + /** + * Retrieve the current volume of the MiniDSP. + * The volume is passed as an unsigned integer that represents twice the -dB + * value. Example: 19 represents -9.5dB. + * @return Current volume. + */ + int GetVolume() const { return volume; } + + /** + * Retrieve the current muted status of the MiniDSP + * @return `true` if the device is muted, `false` otherwise. + */ + bool IsMuted() const { return isMuted; } + +protected: + /** @name HIDUniversal implementation */ + /** + * Used to parse USB HID data. + * @param hid Pointer to the HID class. + * @param is_rpt_id Only used for Hubs. + * @param len The length of the incoming data. + * @param buf Pointer to the data buffer. + */ + void ParseHIDData(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *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. + */ + uint8_t OnInitSuccessful(); + /**@}*/ + + /** @name USBDeviceConfig implementation */ + /** + * Used by the USB core to check what this driver support. + * @param vid The device's VID. + * @param pid The device's PID. + * @return Returns true if the device's VID and PID matches this driver. + */ + virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { + return vid == MINIDSP_VID && pid == MINIDSP_PID; + }; + /**@}*/ + +private: + /** + * Send the "Request status" command to the MiniDSP. The response includes the + * current volume and the muted status. + */ + void RequestStatus() const; + + /** + * Send the given MiniDSP command. This function will create a buffer with the + * expected header and checksum and send it to the MiniDSP. Responses will + * come in throug `ParseHIDData`. + * @param command Buffer of the command to send. + * @param command_length Length of the buffer. + */ + void SendCommand(uint8_t *command, uint16_t command_length) const; + + // Callbacks + + // Pointer to function called in onInit(). + void (*onInitCallback)(void) = nullptr; + + // Pointer to function called when volume changes. + void (*volumeChangeCallback)(uint8_t) = nullptr; + + // Pointer to function called when muted status changes. + void (*mutedChangeCallback)(bool) = nullptr; + + // ----------------------------------------------------------------------------- + + // MiniDSP state. Currently only volume and muted status are implemented, but + // others can be added easily if needed. + + // The volume is stored as an unsigned integer that represents twice the + // -dB value. Example: 19 represents -9.5dB. + uint8_t volume = 0; + bool isMuted = false; +}; diff --git a/examples/MiniDSP/MiniDSP.ino b/examples/MiniDSP/MiniDSP.ino new file mode 100644 index 00000000..031a3ac1 --- /dev/null +++ b/examples/MiniDSP/MiniDSP.ino @@ -0,0 +1,48 @@ +/* + Example sketch for the Playstation Buzz 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 + +// Satisfy the IDE, which needs to see the include statment in the ino too. +#ifdef dobogusinclude +#include +#endif +#include + +USB Usb; +MiniDSP MiniDSP(&Usb); + +void OnMiniDSPConnected() { Serial.println("MiniDSP connected"); } + +void OnVolumeChange(uint8_t volume) { + Serial.println("Volume is: " + String(volume)); +} + +void OnMutedChange(bool isMuted) { + Serial.println("Muted status: " + String(isMuted ? "muted" : "unmuted")); +} + +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.println(F("\r\nMiniDSP 2x4HD Library Started")); + + // Register callbacks. + MiniDSP.SetOnInitCallback(&OnMiniDSPConnected); + MiniDSP.SetVolumeChangeCallback(&OnVolumeChange); + MiniDSP.SetMutedChangeCallback(&OnMutedChange); +} + +void loop() { Usb.Task(); } From a4e9521c4a257c842f38237f2ca063561a148ef0 Mon Sep 17 00:00:00 2001 From: Dennis Frett Date: Thu, 4 Feb 2021 19:39:55 +0100 Subject: [PATCH 47/93] Fix signed/unsigned comparison. --- MiniDSP.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MiniDSP.cpp b/MiniDSP.cpp index dd7a32bd..1aadefe2 100644 --- a/MiniDSP.cpp +++ b/MiniDSP.cpp @@ -20,8 +20,8 @@ uint8_t StatusInputCommand[]{0x05, 0xFF, 0xDA}; // Returns first byte of the sum of given bytes. uint8_t Checksum(const uint8_t *data, uint16_t nbytes) { - int sum = 0; - for (int i = 0; i < nbytes; i++) { + uint64_t sum = 0; + for (uint16_t i = 0; i < nbytes; i++) { sum += data[i]; } From df0a711a3a378797dc4cb596b3804258cf1319ca Mon Sep 17 00:00:00 2001 From: Dennis Frett Date: Thu, 4 Feb 2021 23:40:22 +0100 Subject: [PATCH 48/93] Review comments. --- MiniDSP.cpp | 132 +++++++++----------- MiniDSP.h | 234 +++++++++++++++++++---------------- examples/MiniDSP/MiniDSP.ino | 44 +++---- 3 files changed, 209 insertions(+), 201 deletions(-) diff --git a/MiniDSP.cpp b/MiniDSP.cpp index 1aadefe2..90b7c716 100644 --- a/MiniDSP.cpp +++ b/MiniDSP.cpp @@ -14,104 +14,88 @@ #include "MiniDSP.h" -namespace { -uint8_t RequestStatusOutputCommand[] = {0x05, 0xFF, 0xDA, 0x02}; -uint8_t StatusInputCommand[]{0x05, 0xFF, 0xDA}; +void MiniDSP::ParseHIDData(USBHID *hid __attribute__((unused)), bool is_rpt_id __attribute__((unused)), uint8_t len, uint8_t *buf) { -// Returns first byte of the sum of given bytes. -uint8_t Checksum(const uint8_t *data, uint16_t nbytes) { - uint64_t sum = 0; - for (uint16_t i = 0; i < nbytes; i++) { - sum += data[i]; - } + constexpr uint8_t StatusInputCommand[]{0x05, 0xFF, 0xDA}; - return sum & 0xFF; -} -} // namespace + // Only care about valid data for the MiniDSP 2x4HD. + if (HIDUniversal::VID != MINIDSP_VID || HIDUniversal::PID != MINIDSP_PID || len <= 4 || buf == nullptr) + return; -void MiniDSP::ParseHIDData(USBHID *hid __attribute__((unused)), - bool is_rpt_id __attribute__((unused)), uint8_t len, - uint8_t *buf) { + // Check if this is a status update. + // First byte is the length, we ignore that for now. + if (memcmp(buf + 1, StatusInputCommand, sizeof(StatusInputCommand)) == 0) { - // Only care about valid data for the MiniDSP 2x4HD. - if (HIDUniversal::VID != MINIDSP_VID || HIDUniversal::PID != MINIDSP_PID || - len <= 2 || buf == nullptr) { - return; - } + // Parse data. + // Response is of format [ length ] [ 0x05 0xFF 0xDA ] [ volume ] [ muted ]. + const auto newVolume = buf[sizeof(StatusInputCommand) + 1]; + const auto newIsMuted = (bool)buf[sizeof(StatusInputCommand) + 2]; - // Check if this is a status update. - // First byte is the length, we ignore that for now. - if (memcmp(buf + 1, StatusInputCommand, sizeof(StatusInputCommand)) == 0) { + // Update status. + volume = newVolume; + muted = newIsMuted; - // Parse data. - // Response is of format [ length ] [ 0x05 0xFF 0xDA ] [ volume ] [ muted ]. - const auto newVolume = buf[sizeof(StatusInputCommand) + 1]; - const auto newIsMuted = (bool)buf[sizeof(StatusInputCommand) + 2]; + // Call callbacks. + if (pFuncOnVolumeChange != nullptr && newVolume != volume) + pFuncOnVolumeChange(volume); - const auto volumeUpdated = newVolume != volume; - const auto isMutedUpdated = newIsMuted != isMuted; - - // Update status. - volume = newVolume; - isMuted = newIsMuted; - - // Call callbacks. - if (volumeChangeCallback != nullptr && volumeUpdated) { - volumeChangeCallback(volume); - } - - if (mutedChangeCallback != nullptr && isMutedUpdated) { - mutedChangeCallback(isMuted); - } - } + if (pFuncOnMutedChange != nullptr && newIsMuted != muted) + pFuncOnMutedChange(muted); + } }; uint8_t MiniDSP::OnInitSuccessful() { - // Verify we're actually connected to the MiniDSP 2x4HD. - if (HIDUniversal::VID != MINIDSP_VID || HIDUniversal::PID != MINIDSP_PID) { - return 0; - } + // Verify we're actually connected to the MiniDSP 2x4HD. + if (HIDUniversal::VID != MINIDSP_VID || HIDUniversal::PID != MINIDSP_PID) + return 0; - // Request current status so we can initialize the values. - RequestStatus(); + // Request current status so we can initialize the values. + RequestStatus(); - if (onInitCallback != nullptr) { - onInitCallback(); - } + if (pFuncOnInit != nullptr) + pFuncOnInit(); - return 0; + return 0; }; -void MiniDSP::SendCommand(uint8_t *command, uint16_t command_length) const { - // Only send command if we're actually connected to the MiniDSP 2x4HD. - if (HIDUniversal::VID != MINIDSP_VID || HIDUniversal::PID != MINIDSP_PID) { - return; - } +uint8_t MiniDSP::Checksum(const uint8_t *data, uint8_t data_length) const { + uint16_t sum = 0; + for (uint8_t i = 0; i < data_length; i++) + sum += data[i]; - // Message is padded to 64 bytes with 0xFF and is of format: - // [ length (command + checksum byte) ] [ command ] [ checksum ] [ OxFF... ] + return sum & 0xFF; +} - // MiniDSP expects 64 byte messages. - uint8_t buf[64]; +void MiniDSP::SendCommand(uint8_t *command, uint8_t command_length) const { + // Sanity check on command length. + if (command_length > 63) + return; - // Set length, including checksum byte. - buf[0] = command_length + 1; + // Message is padded to 64 bytes with 0xFF and is of format: + // [ length (command + checksum byte) ] [ command ] [ checksum ] [ OxFF... ] - // Copy actual command. - memcpy(&buf[1], command, command_length); + // MiniDSP expects 64 byte messages. + uint8_t buf[64]; - const auto checksumOffset = command_length + 1; + // Set length, including checksum byte. + buf[0] = command_length + 1; - // Set checksum byte. - buf[checksumOffset] = Checksum(buf, command_length + 1); + // Copy actual command. + memcpy(&buf[1], command, command_length); - // Pad the rest. - memset(&buf[checksumOffset + 1], 0xFF, sizeof(buf) - checksumOffset - 1); + const auto checksumOffset = command_length + 1; - pUsb->outTransfer(bAddress, epInfo[epInterruptOutIndex].epAddr, sizeof(buf), - buf); + // Set checksum byte. + buf[checksumOffset] = Checksum(buf, command_length + 1); + + // Pad the rest. + memset(&buf[checksumOffset + 1], 0xFF, sizeof(buf) - checksumOffset - 1); + + pUsb->outTransfer(bAddress, epInfo[epInterruptOutIndex].epAddr, sizeof(buf), buf); } void MiniDSP::RequestStatus() const { - SendCommand(RequestStatusOutputCommand, sizeof(RequestStatusOutputCommand)); + uint8_t RequestStatusOutputCommand[] = {0x05, 0xFF, 0xDA, 0x02}; + + SendCommand(RequestStatusOutputCommand, sizeof(RequestStatusOutputCommand)); } diff --git a/MiniDSP.h b/MiniDSP.h index cea2aa34..4f5dc000 100644 --- a/MiniDSP.h +++ b/MiniDSP.h @@ -14,7 +14,6 @@ #pragma once -#include "controllerEnums.h" #include "hiduniversal.h" #define MINIDSP_VID 0x2752 // MiniDSP @@ -31,127 +30,150 @@ * It uses the HIDUniversal class for all the USB communication. */ class MiniDSP : public HIDUniversal { -public: - /** - * Constructor for the MiniDSP class. - * @param p Pointer to the USB class instance. - */ - MiniDSP(USB *p) : HIDUniversal(p){}; + public: + /** + * Constructor for the MiniDSP class. + * @param p Pointer to the USB class instance. + */ + MiniDSP(USB *p) : HIDUniversal(p){}; - /** - * Used to check if a MiniDSP 2x4HD is connected. - * @return Returns true if it is connected. - */ - bool connected() { - return HIDUniversal::isReady() && HIDUniversal::VID == MINIDSP_VID && - HIDUniversal::PID == MINIDSP_PID; - }; + /** + * Used to check if a MiniDSP 2x4HD is connected. + * @return Returns true if it is connected. + */ + bool connected() { + return HIDUniversal::isReady() && HIDUniversal::VID == MINIDSP_VID && HIDUniversal::PID == MINIDSP_PID; + }; - /** - * Used to call your own function when the device is successfully initialized. - * @param func Function to call. - */ - void SetOnInitCallback(void (*func)(void)) { onInitCallback = func; }; + /** + * Used to call your own function when the device is successfully + * initialized. + * @param funcOnInit Function to call. + */ + void attachOnInit(void (*funcOnInit)(void)) { pFuncOnInit = funcOnInit; }; - /** - * Used to call your own function when the volume has changed. - * The volume is passed as an unsigned integer that represents twice the -dB - * value. Example: 19 represents -9.5dB. - * @param func Function to call. - */ - void SetVolumeChangeCallback(void (*func)(uint8_t)) { - volumeChangeCallback = func; - } + /** + * Used to call your own function when the volume has changed. + * The volume is passed as an unsigned integer that represents twice the + * -dB value. Example: 19 represents -9.5dB. + * @param funcOnVolumeChange Function to call. + */ + void attachOnVolumeChange(void (*funcOnVolumeChange)(uint8_t)) { + pFuncOnVolumeChange = funcOnVolumeChange; + } - /** - * Used to call your own function when the muted status has changed. - * The muted status is passed as a boolean. True means muted, false means - * unmuted. - * @param func Function to call. - */ - void SetMutedChangeCallback(void (*func)(bool)) { - mutedChangeCallback = func; - } + /** + * Used to call your own function when the muted status has changed. + * The muted status is passed as a boolean. True means muted, false + * means unmuted. + * @param funcOnMutedChange Function to call. + */ + void attachOnMutedChange(void (*funcOnMutedChange)(bool)) { + pFuncOnMutedChange = funcOnMutedChange; + } - /** - * Retrieve the current volume of the MiniDSP. - * The volume is passed as an unsigned integer that represents twice the -dB - * value. Example: 19 represents -9.5dB. - * @return Current volume. - */ - int GetVolume() const { return volume; } + /** + * Retrieve the current volume of the MiniDSP. + * The volume is passed as an unsigned integer that represents twice the + * -dB value. Example: 19 represents -9.5dB. + * @return Current volume. + */ + int getVolume() const { + return volume; + } - /** - * Retrieve the current muted status of the MiniDSP - * @return `true` if the device is muted, `false` otherwise. - */ - bool IsMuted() const { return isMuted; } + /** + * Retrieve the current volume of the MiniDSP in -dB. + * @return Current volume. + */ + float getVolumeDB() const { + return volume / -2.0; + } -protected: - /** @name HIDUniversal implementation */ - /** - * Used to parse USB HID data. - * @param hid Pointer to the HID class. - * @param is_rpt_id Only used for Hubs. - * @param len The length of the incoming data. - * @param buf Pointer to the data buffer. - */ - void ParseHIDData(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf); + /** + * Retrieve the current muted status of the MiniDSP + * @return `true` if the device is muted, `false` otherwise. + */ + bool isMuted() const { + return muted; + } - /** - * 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. - */ - uint8_t OnInitSuccessful(); - /**@}*/ + protected: + /** @name HIDUniversal implementation */ + /** + * Used to parse USB HID data. + * @param hid Pointer to the HID class. + * @param is_rpt_id Only used for Hubs. + * @param len The length of the incoming data. + * @param buf Pointer to the data buffer. + */ + void ParseHIDData(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf); - /** @name USBDeviceConfig implementation */ - /** - * Used by the USB core to check what this driver support. - * @param vid The device's VID. - * @param pid The device's PID. - * @return Returns true if the device's VID and PID matches this driver. - */ - virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { - return vid == MINIDSP_VID && pid == MINIDSP_PID; - }; - /**@}*/ + /** + * 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. + */ + uint8_t OnInitSuccessful(); + /**@}*/ -private: - /** - * Send the "Request status" command to the MiniDSP. The response includes the - * current volume and the muted status. - */ - void RequestStatus() const; + /** @name USBDeviceConfig implementation */ + /** + * Used by the USB core to check what this driver support. + * @param vid The device's VID. + * @param pid The device's PID. + * @return Returns true if the device's VID and PID matches this + * driver. + */ + virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { + return vid == MINIDSP_VID && pid == MINIDSP_PID; + }; + /**@}*/ - /** - * Send the given MiniDSP command. This function will create a buffer with the - * expected header and checksum and send it to the MiniDSP. Responses will - * come in throug `ParseHIDData`. - * @param command Buffer of the command to send. - * @param command_length Length of the buffer. - */ - void SendCommand(uint8_t *command, uint16_t command_length) const; + private: + /** + * Calculate checksum for given buffer. + * Checksum is given by summing up all bytes in `data` and returning the first byte. + * @param data Buffer to calculate checksum for. + * @param data_length Length of the buffer. + */ + uint8_t Checksum(const uint8_t *data, uint8_t data_length) const; - // Callbacks + /** + * Send the "Request status" command to the MiniDSP. The response + * includes the current volume and the muted status. + */ + void + RequestStatus() const; - // Pointer to function called in onInit(). - void (*onInitCallback)(void) = nullptr; + /** + * Send the given MiniDSP command. This function will create a buffer + * with the expected header and checksum and send it to the MiniDSP. + * Responses will come in throug `ParseHIDData`. + * @param command Buffer of the command to send. + * @param command_length Length of the buffer. + */ + void SendCommand(uint8_t *command, uint8_t command_length) const; - // Pointer to function called when volume changes. - void (*volumeChangeCallback)(uint8_t) = nullptr; + // Callbacks - // Pointer to function called when muted status changes. - void (*mutedChangeCallback)(bool) = nullptr; + // Pointer to function called in onInit(). + void (*pFuncOnInit)(void) = nullptr; - // ----------------------------------------------------------------------------- + // Pointer to function called when volume changes. + void (*pFuncOnVolumeChange)(uint8_t) = nullptr; - // MiniDSP state. Currently only volume and muted status are implemented, but - // others can be added easily if needed. + // Pointer to function called when muted status changes. + void (*pFuncOnMutedChange)(bool) = nullptr; - // The volume is stored as an unsigned integer that represents twice the - // -dB value. Example: 19 represents -9.5dB. - uint8_t volume = 0; - bool isMuted = false; + // ----------------------------------------------------------------------------- + + // MiniDSP state. Currently only volume and muted status are + // implemented, but others can be added easily if needed. + + // The volume is stored as an unsigned integer that represents twice the + // -dB value. Example: 19 represents -9.5dB. + uint8_t volume = 0; + bool muted = false; }; diff --git a/examples/MiniDSP/MiniDSP.ino b/examples/MiniDSP/MiniDSP.ino index 031a3ac1..8b7a20be 100644 --- a/examples/MiniDSP/MiniDSP.ino +++ b/examples/MiniDSP/MiniDSP.ino @@ -1,7 +1,5 @@ /* - Example sketch for the Playstation Buzz library - developed by Kristian Lauszus - For more information visit my blog: http://blog.tkjelectronics.dk/ or - send me an e-mail: kristianl@tkjelectronics.com + Example sketch for the MiniDSP 2x4HD library - developed by Dennis Frett */ #include @@ -15,34 +13,38 @@ USB Usb; MiniDSP MiniDSP(&Usb); -void OnMiniDSPConnected() { Serial.println("MiniDSP connected"); } +void OnMiniDSPConnected() { + Serial.println("MiniDSP connected"); +} void OnVolumeChange(uint8_t volume) { - Serial.println("Volume is: " + String(volume)); + Serial.println("Volume is: " + String(volume)); } void OnMutedChange(bool isMuted) { - Serial.println("Muted status: " + String(isMuted ? "muted" : "unmuted")); + Serial.println("Muted status: " + String(isMuted ? "muted" : "unmuted")); } void setup() { - Serial.begin(115200); + 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 + 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.println(F("\r\nMiniDSP 2x4HD Library Started")); + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while (1) + ; // Halt + } + Serial.println(F("\r\nMiniDSP 2x4HD Library Started")); - // Register callbacks. - MiniDSP.SetOnInitCallback(&OnMiniDSPConnected); - MiniDSP.SetVolumeChangeCallback(&OnVolumeChange); - MiniDSP.SetMutedChangeCallback(&OnMutedChange); + // Register callbacks. + MiniDSP.attachOnInit(&OnMiniDSPConnected); + MiniDSP.attachOnVolumeChange(&OnVolumeChange); + MiniDSP.attachOnMutedChange(&OnMutedChange); } -void loop() { Usb.Task(); } +void loop() { + Usb.Task(); +} From 17cb44b5a27d17372c79e3fe95f757b61d332db5 Mon Sep 17 00:00:00 2001 From: Dennis Frett Date: Fri, 5 Feb 2021 10:43:02 +0100 Subject: [PATCH 49/93] Applied @xxxajk's formatting. --- MiniDSP.cpp | 28 ++++++++++++++-------------- MiniDSP.h | 17 +++++++++++------ examples/MiniDSP/MiniDSP.ino | 9 +++------ 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/MiniDSP.cpp b/MiniDSP.cpp index 90b7c716..3f869d59 100644 --- a/MiniDSP.cpp +++ b/MiniDSP.cpp @@ -14,45 +14,45 @@ #include "MiniDSP.h" -void MiniDSP::ParseHIDData(USBHID *hid __attribute__((unused)), bool is_rpt_id __attribute__((unused)), uint8_t len, uint8_t *buf) { +void MiniDSP::ParseHIDData(USBHID *hid __attribute__ ((unused)), bool is_rpt_id __attribute__ ((unused)), uint8_t len, uint8_t *buf) { constexpr uint8_t StatusInputCommand[]{0x05, 0xFF, 0xDA}; // Only care about valid data for the MiniDSP 2x4HD. - if (HIDUniversal::VID != MINIDSP_VID || HIDUniversal::PID != MINIDSP_PID || len <= 4 || buf == nullptr) + if(HIDUniversal::VID != MINIDSP_VID || HIDUniversal::PID != MINIDSP_PID || len <= 4 || buf == nullptr) return; // Check if this is a status update. // First byte is the length, we ignore that for now. - if (memcmp(buf + 1, StatusInputCommand, sizeof(StatusInputCommand)) == 0) { + if(memcmp(buf + 1, StatusInputCommand, sizeof (StatusInputCommand)) == 0) { // Parse data. // Response is of format [ length ] [ 0x05 0xFF 0xDA ] [ volume ] [ muted ]. - const auto newVolume = buf[sizeof(StatusInputCommand) + 1]; - const auto newIsMuted = (bool)buf[sizeof(StatusInputCommand) + 2]; + const auto newVolume = buf[sizeof (StatusInputCommand) + 1]; + const auto newIsMuted = (bool)buf[sizeof (StatusInputCommand) + 2]; // Update status. volume = newVolume; muted = newIsMuted; // Call callbacks. - if (pFuncOnVolumeChange != nullptr && newVolume != volume) + if(pFuncOnVolumeChange != nullptr && newVolume != volume) pFuncOnVolumeChange(volume); - if (pFuncOnMutedChange != nullptr && newIsMuted != muted) + if(pFuncOnMutedChange != nullptr && newIsMuted != muted) pFuncOnMutedChange(muted); } }; uint8_t MiniDSP::OnInitSuccessful() { // Verify we're actually connected to the MiniDSP 2x4HD. - if (HIDUniversal::VID != MINIDSP_VID || HIDUniversal::PID != MINIDSP_PID) + if(HIDUniversal::VID != MINIDSP_VID || HIDUniversal::PID != MINIDSP_PID) return 0; // Request current status so we can initialize the values. RequestStatus(); - if (pFuncOnInit != nullptr) + if(pFuncOnInit != nullptr) pFuncOnInit(); return 0; @@ -60,7 +60,7 @@ uint8_t MiniDSP::OnInitSuccessful() { uint8_t MiniDSP::Checksum(const uint8_t *data, uint8_t data_length) const { uint16_t sum = 0; - for (uint8_t i = 0; i < data_length; i++) + for(uint8_t i = 0; i < data_length; i++) sum += data[i]; return sum & 0xFF; @@ -68,7 +68,7 @@ uint8_t MiniDSP::Checksum(const uint8_t *data, uint8_t data_length) const { void MiniDSP::SendCommand(uint8_t *command, uint8_t command_length) const { // Sanity check on command length. - if (command_length > 63) + if(command_length > 63) return; // Message is padded to 64 bytes with 0xFF and is of format: @@ -89,13 +89,13 @@ void MiniDSP::SendCommand(uint8_t *command, uint8_t command_length) const { buf[checksumOffset] = Checksum(buf, command_length + 1); // Pad the rest. - memset(&buf[checksumOffset + 1], 0xFF, sizeof(buf) - checksumOffset - 1); + memset(&buf[checksumOffset + 1], 0xFF, sizeof (buf) - checksumOffset - 1); - pUsb->outTransfer(bAddress, epInfo[epInterruptOutIndex].epAddr, sizeof(buf), buf); + pUsb->outTransfer(bAddress, epInfo[epInterruptOutIndex].epAddr, sizeof (buf), buf); } void MiniDSP::RequestStatus() const { uint8_t RequestStatusOutputCommand[] = {0x05, 0xFF, 0xDA, 0x02}; - SendCommand(RequestStatusOutputCommand, sizeof(RequestStatusOutputCommand)); + SendCommand(RequestStatusOutputCommand, sizeof (RequestStatusOutputCommand)); } diff --git a/MiniDSP.h b/MiniDSP.h index 4f5dc000..9cfe3c82 100644 --- a/MiniDSP.h +++ b/MiniDSP.h @@ -30,12 +30,14 @@ * It uses the HIDUniversal class for all the USB communication. */ class MiniDSP : public HIDUniversal { - public: +public: + /** * Constructor for the MiniDSP class. * @param p Pointer to the USB class instance. */ - MiniDSP(USB *p) : HIDUniversal(p){}; + MiniDSP(USB *p) : HIDUniversal(p) { + }; /** * Used to check if a MiniDSP 2x4HD is connected. @@ -50,7 +52,9 @@ class MiniDSP : public HIDUniversal { * initialized. * @param funcOnInit Function to call. */ - void attachOnInit(void (*funcOnInit)(void)) { pFuncOnInit = funcOnInit; }; + void attachOnInit(void (*funcOnInit)(void)) { + pFuncOnInit = funcOnInit; + }; /** * Used to call your own function when the volume has changed. @@ -98,7 +102,7 @@ class MiniDSP : public HIDUniversal { return muted; } - protected: +protected: /** @name HIDUniversal implementation */ /** * Used to parse USB HID data. @@ -119,6 +123,7 @@ class MiniDSP : public HIDUniversal { /**@}*/ /** @name USBDeviceConfig implementation */ + /** * Used by the USB core to check what this driver support. * @param vid The device's VID. @@ -131,9 +136,9 @@ class MiniDSP : public HIDUniversal { }; /**@}*/ - private: +private: /** - * Calculate checksum for given buffer. + * Calculate checksum for given buffer. * Checksum is given by summing up all bytes in `data` and returning the first byte. * @param data Buffer to calculate checksum for. * @param data_length Length of the buffer. diff --git a/examples/MiniDSP/MiniDSP.ino b/examples/MiniDSP/MiniDSP.ino index 8b7a20be..a289df2d 100644 --- a/examples/MiniDSP/MiniDSP.ino +++ b/examples/MiniDSP/MiniDSP.ino @@ -28,14 +28,11 @@ void OnMutedChange(bool isMuted) { 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 + 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) { + if(Usb.Init() == -1) { Serial.print(F("\r\nOSC did not start")); - while (1) - ; // Halt + while(1); // Halt } Serial.println(F("\r\nMiniDSP 2x4HD Library Started")); From 0d57603adb0232e52279747bd7063bc4d6bfe939 Mon Sep 17 00:00:00 2001 From: Dennis Frett Date: Fri, 5 Feb 2021 11:49:23 +0100 Subject: [PATCH 50/93] Update copyright header + Arduino example indentation. --- MiniDSP.cpp | 15 +++++++++++---- MiniDSP.h | 15 +++++++++++---- examples/MiniDSP/MiniDSP.ino | 30 +++++++++++++++--------------- 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/MiniDSP.cpp b/MiniDSP.cpp index 3f869d59..ddb180c4 100644 --- a/MiniDSP.cpp +++ b/MiniDSP.cpp @@ -1,15 +1,22 @@ -/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved. +/* Copyright (C) 2020 Kristian Sloth Lauszus and Dennis Frett. 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 + + Kristian Sloth Lauszus + Web : https://lauszus.com + e-mail : lauszus@gmail.com + + Dennis Frett + GitHub : https://github.com/dennisfrett + e-mail : dennis.frett@live.com */ #include "MiniDSP.h" diff --git a/MiniDSP.h b/MiniDSP.h index 9cfe3c82..63e72c6f 100644 --- a/MiniDSP.h +++ b/MiniDSP.h @@ -1,15 +1,22 @@ -/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved. +/* Copyright (C) 2020 Kristian Sloth Lauszus and Dennis Frett. 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 + + Kristian Sloth Lauszus + Web : https://lauszus.com + e-mail : lauszus@gmail.com + + Dennis Frett + GitHub : https://github.com/dennisfrett + e-mail : dennis.frett@live.com */ #pragma once diff --git a/examples/MiniDSP/MiniDSP.ino b/examples/MiniDSP/MiniDSP.ino index a289df2d..8048b25c 100644 --- a/examples/MiniDSP/MiniDSP.ino +++ b/examples/MiniDSP/MiniDSP.ino @@ -14,34 +14,34 @@ USB Usb; MiniDSP MiniDSP(&Usb); void OnMiniDSPConnected() { - Serial.println("MiniDSP connected"); + Serial.println("MiniDSP connected"); } void OnVolumeChange(uint8_t volume) { - Serial.println("Volume is: " + String(volume)); + Serial.println("Volume is: " + String(volume)); } void OnMutedChange(bool isMuted) { - Serial.println("Muted status: " + String(isMuted ? "muted" : "unmuted")); + Serial.println("Muted status: " + String(isMuted ? "muted" : "unmuted")); } void setup() { - Serial.begin(115200); + 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 + 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.println(F("\r\nMiniDSP 2x4HD Library Started")); + if(Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while(1); // Halt + } + Serial.println(F("\r\nMiniDSP 2x4HD Library Started")); - // Register callbacks. - MiniDSP.attachOnInit(&OnMiniDSPConnected); - MiniDSP.attachOnVolumeChange(&OnVolumeChange); - MiniDSP.attachOnMutedChange(&OnMutedChange); + // Register callbacks. + MiniDSP.attachOnInit(&OnMiniDSPConnected); + MiniDSP.attachOnVolumeChange(&OnVolumeChange); + MiniDSP.attachOnMutedChange(&OnMutedChange); } void loop() { - Usb.Task(); + Usb.Task(); } From dfe3b79bae01aa3ee0369136c2eb08abd0c4ee5d Mon Sep 17 00:00:00 2001 From: Dennis Frett Date: Fri, 5 Feb 2021 20:09:30 +0100 Subject: [PATCH 51/93] Fix callbacks not being invoked. --- MiniDSP.cpp | 108 ----------------------------- MiniDSP.h | 191 ---------------------------------------------------- 2 files changed, 299 deletions(-) delete mode 100644 MiniDSP.cpp delete mode 100644 MiniDSP.h diff --git a/MiniDSP.cpp b/MiniDSP.cpp deleted file mode 100644 index ddb180c4..00000000 --- a/MiniDSP.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* Copyright (C) 2020 Kristian Sloth Lauszus and Dennis Frett. 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 - - Dennis Frett - GitHub : https://github.com/dennisfrett - e-mail : dennis.frett@live.com - */ - -#include "MiniDSP.h" - -void MiniDSP::ParseHIDData(USBHID *hid __attribute__ ((unused)), bool is_rpt_id __attribute__ ((unused)), uint8_t len, uint8_t *buf) { - - constexpr uint8_t StatusInputCommand[]{0x05, 0xFF, 0xDA}; - - // Only care about valid data for the MiniDSP 2x4HD. - if(HIDUniversal::VID != MINIDSP_VID || HIDUniversal::PID != MINIDSP_PID || len <= 4 || buf == nullptr) - return; - - // Check if this is a status update. - // First byte is the length, we ignore that for now. - if(memcmp(buf + 1, StatusInputCommand, sizeof (StatusInputCommand)) == 0) { - - // Parse data. - // Response is of format [ length ] [ 0x05 0xFF 0xDA ] [ volume ] [ muted ]. - const auto newVolume = buf[sizeof (StatusInputCommand) + 1]; - const auto newIsMuted = (bool)buf[sizeof (StatusInputCommand) + 2]; - - // Update status. - volume = newVolume; - muted = newIsMuted; - - // Call callbacks. - if(pFuncOnVolumeChange != nullptr && newVolume != volume) - pFuncOnVolumeChange(volume); - - if(pFuncOnMutedChange != nullptr && newIsMuted != muted) - pFuncOnMutedChange(muted); - } -}; - -uint8_t MiniDSP::OnInitSuccessful() { - // Verify we're actually connected to the MiniDSP 2x4HD. - if(HIDUniversal::VID != MINIDSP_VID || HIDUniversal::PID != MINIDSP_PID) - return 0; - - // Request current status so we can initialize the values. - RequestStatus(); - - if(pFuncOnInit != nullptr) - pFuncOnInit(); - - return 0; -}; - -uint8_t MiniDSP::Checksum(const uint8_t *data, uint8_t data_length) const { - uint16_t sum = 0; - for(uint8_t i = 0; i < data_length; i++) - sum += data[i]; - - return sum & 0xFF; -} - -void MiniDSP::SendCommand(uint8_t *command, uint8_t command_length) const { - // Sanity check on command length. - if(command_length > 63) - return; - - // Message is padded to 64 bytes with 0xFF and is of format: - // [ length (command + checksum byte) ] [ command ] [ checksum ] [ OxFF... ] - - // MiniDSP expects 64 byte messages. - uint8_t buf[64]; - - // Set length, including checksum byte. - buf[0] = command_length + 1; - - // Copy actual command. - memcpy(&buf[1], command, command_length); - - const auto checksumOffset = command_length + 1; - - // Set checksum byte. - buf[checksumOffset] = Checksum(buf, command_length + 1); - - // Pad the rest. - memset(&buf[checksumOffset + 1], 0xFF, sizeof (buf) - checksumOffset - 1); - - pUsb->outTransfer(bAddress, epInfo[epInterruptOutIndex].epAddr, sizeof (buf), buf); -} - -void MiniDSP::RequestStatus() const { - uint8_t RequestStatusOutputCommand[] = {0x05, 0xFF, 0xDA, 0x02}; - - SendCommand(RequestStatusOutputCommand, sizeof (RequestStatusOutputCommand)); -} diff --git a/MiniDSP.h b/MiniDSP.h deleted file mode 100644 index 63e72c6f..00000000 --- a/MiniDSP.h +++ /dev/null @@ -1,191 +0,0 @@ -/* Copyright (C) 2020 Kristian Sloth Lauszus and Dennis Frett. 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 - - Dennis Frett - GitHub : https://github.com/dennisfrett - e-mail : dennis.frett@live.com - */ - -#pragma once - -#include "hiduniversal.h" - -#define MINIDSP_VID 0x2752 // MiniDSP -#define MINIDSP_PID 0x0011 // MiniDSP 2x4HD - -/** - * Arduino MiniDSP 2x4HD USB Host Driver by Dennis Frett. - * - * This class implements support for the MiniDSP 2x4HD via USB. - * Based on NodeJS implementation by Mathieu Rene: - * https://github.com/mrene/node-minidsp and the Python implementation by Mark - * Kubiak: https://github.com/markubiak/python3-minidsp. - * - * It uses the HIDUniversal class for all the USB communication. - */ -class MiniDSP : public HIDUniversal { -public: - - /** - * Constructor for the MiniDSP class. - * @param p Pointer to the USB class instance. - */ - MiniDSP(USB *p) : HIDUniversal(p) { - }; - - /** - * Used to check if a MiniDSP 2x4HD is connected. - * @return Returns true if it is connected. - */ - bool connected() { - return HIDUniversal::isReady() && HIDUniversal::VID == MINIDSP_VID && HIDUniversal::PID == MINIDSP_PID; - }; - - /** - * Used to call your own function when the device is successfully - * initialized. - * @param funcOnInit Function to call. - */ - void attachOnInit(void (*funcOnInit)(void)) { - pFuncOnInit = funcOnInit; - }; - - /** - * Used to call your own function when the volume has changed. - * The volume is passed as an unsigned integer that represents twice the - * -dB value. Example: 19 represents -9.5dB. - * @param funcOnVolumeChange Function to call. - */ - void attachOnVolumeChange(void (*funcOnVolumeChange)(uint8_t)) { - pFuncOnVolumeChange = funcOnVolumeChange; - } - - /** - * Used to call your own function when the muted status has changed. - * The muted status is passed as a boolean. True means muted, false - * means unmuted. - * @param funcOnMutedChange Function to call. - */ - void attachOnMutedChange(void (*funcOnMutedChange)(bool)) { - pFuncOnMutedChange = funcOnMutedChange; - } - - /** - * Retrieve the current volume of the MiniDSP. - * The volume is passed as an unsigned integer that represents twice the - * -dB value. Example: 19 represents -9.5dB. - * @return Current volume. - */ - int getVolume() const { - return volume; - } - - /** - * Retrieve the current volume of the MiniDSP in -dB. - * @return Current volume. - */ - float getVolumeDB() const { - return volume / -2.0; - } - - /** - * Retrieve the current muted status of the MiniDSP - * @return `true` if the device is muted, `false` otherwise. - */ - bool isMuted() const { - return muted; - } - -protected: - /** @name HIDUniversal implementation */ - /** - * Used to parse USB HID data. - * @param hid Pointer to the HID class. - * @param is_rpt_id Only used for Hubs. - * @param len The length of the incoming data. - * @param buf Pointer to the data buffer. - */ - void ParseHIDData(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *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. - */ - uint8_t OnInitSuccessful(); - /**@}*/ - - /** @name USBDeviceConfig implementation */ - - /** - * Used by the USB core to check what this driver support. - * @param vid The device's VID. - * @param pid The device's PID. - * @return Returns true if the device's VID and PID matches this - * driver. - */ - virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { - return vid == MINIDSP_VID && pid == MINIDSP_PID; - }; - /**@}*/ - -private: - /** - * Calculate checksum for given buffer. - * Checksum is given by summing up all bytes in `data` and returning the first byte. - * @param data Buffer to calculate checksum for. - * @param data_length Length of the buffer. - */ - uint8_t Checksum(const uint8_t *data, uint8_t data_length) const; - - /** - * Send the "Request status" command to the MiniDSP. The response - * includes the current volume and the muted status. - */ - void - RequestStatus() const; - - /** - * Send the given MiniDSP command. This function will create a buffer - * with the expected header and checksum and send it to the MiniDSP. - * Responses will come in throug `ParseHIDData`. - * @param command Buffer of the command to send. - * @param command_length Length of the buffer. - */ - void SendCommand(uint8_t *command, uint8_t command_length) const; - - // Callbacks - - // Pointer to function called in onInit(). - void (*pFuncOnInit)(void) = nullptr; - - // Pointer to function called when volume changes. - void (*pFuncOnVolumeChange)(uint8_t) = nullptr; - - // Pointer to function called when muted status changes. - void (*pFuncOnMutedChange)(bool) = nullptr; - - // ----------------------------------------------------------------------------- - - // MiniDSP state. Currently only volume and muted status are - // implemented, but others can be added easily if needed. - - // The volume is stored as an unsigned integer that represents twice the - // -dB value. Example: 19 represents -9.5dB. - uint8_t volume = 0; - bool muted = false; -}; From 54e8728a30120a1dbde815271ae6a65f5717f567 Mon Sep 17 00:00:00 2001 From: Dennis Frett Date: Fri, 5 Feb 2021 23:30:13 +0100 Subject: [PATCH 52/93] Re-add deleted files. --- MiniDSP.cpp | 111 ++++++++++++++++++++++++++++++ MiniDSP.h | 191 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 302 insertions(+) create mode 100644 MiniDSP.cpp create mode 100644 MiniDSP.h diff --git a/MiniDSP.cpp b/MiniDSP.cpp new file mode 100644 index 00000000..d251e519 --- /dev/null +++ b/MiniDSP.cpp @@ -0,0 +1,111 @@ +/* Copyright (C) 2020 Kristian Sloth Lauszus and Dennis Frett. 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 + + Dennis Frett + GitHub : https://github.com/dennisfrett + e-mail : dennis.frett@live.com + */ + +#include "MiniDSP.h" + +void MiniDSP::ParseHIDData(USBHID *hid __attribute__ ((unused)), bool is_rpt_id __attribute__ ((unused)), uint8_t len, uint8_t *buf) { + + constexpr uint8_t StatusInputCommand[] = {0x05, 0xFF, 0xDA}; + + // Only care about valid data for the MiniDSP 2x4HD. + if(HIDUniversal::VID != MINIDSP_VID || HIDUniversal::PID != MINIDSP_PID || len <= 4 || buf == nullptr) + return; + + // Check if this is a status update. + // First byte is the length, we ignore that for now. + if(memcmp(buf + 1, StatusInputCommand, sizeof (StatusInputCommand)) == 0) { + + // Parse data. + // Response is of format [ length ] [ 0x05 0xFF 0xDA ] [ volume ] [ muted ]. + const auto newVolume = buf[sizeof (StatusInputCommand) + 1]; + const auto newIsMuted = (bool)buf[sizeof (StatusInputCommand) + 2]; + + const auto volumeChanged = newVolume != volume; + const auto mutedChanged = newIsMuted != muted; + + // Update status. + volume = newVolume; + muted = newIsMuted; + + // Call callbacks. + if(pFuncOnVolumeChange != nullptr && volumeChanged) + pFuncOnVolumeChange(volume); + + if(pFuncOnMutedChange != nullptr && mutedChanged) + pFuncOnMutedChange(muted); + } +}; + +uint8_t MiniDSP::OnInitSuccessful() { + // Verify we're actually connected to the MiniDSP 2x4HD. + if(HIDUniversal::VID != MINIDSP_VID || HIDUniversal::PID != MINIDSP_PID) + return 0; + + // Request current status so we can initialize the values. + RequestStatus(); + + if(pFuncOnInit != nullptr) + pFuncOnInit(); + + return 0; +}; + +uint8_t MiniDSP::Checksum(const uint8_t *data, uint8_t data_length) const { + uint16_t sum = 0; + for(uint8_t i = 0; i < data_length; i++) + sum += data[i]; + + return sum & 0xFF; +} + +void MiniDSP::SendCommand(uint8_t *command, uint8_t command_length) const { + // Sanity check on command length. + if(command_length > 63) + return; + + // Message is padded to 64 bytes with 0xFF and is of format: + // [ length (command + checksum byte) ] [ command ] [ checksum ] [ OxFF... ] + + // MiniDSP expects 64 byte messages. + uint8_t buf[64]; + + // Set length, including checksum byte. + buf[0] = command_length + 1; + + // Copy actual command. + memcpy(&buf[1], command, command_length); + + const auto checksumOffset = command_length + 1; + + // Set checksum byte. + buf[checksumOffset] = Checksum(buf, command_length + 1); + + // Pad the rest. + memset(&buf[checksumOffset + 1], 0xFF, sizeof (buf) - checksumOffset - 1); + + pUsb->outTransfer(bAddress, epInfo[epInterruptOutIndex].epAddr, sizeof (buf), buf); +} + +void MiniDSP::RequestStatus() const { + uint8_t RequestStatusOutputCommand[] = {0x05, 0xFF, 0xDA, 0x02}; + + SendCommand(RequestStatusOutputCommand, sizeof (RequestStatusOutputCommand)); +} diff --git a/MiniDSP.h b/MiniDSP.h new file mode 100644 index 00000000..63e72c6f --- /dev/null +++ b/MiniDSP.h @@ -0,0 +1,191 @@ +/* Copyright (C) 2020 Kristian Sloth Lauszus and Dennis Frett. 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 + + Dennis Frett + GitHub : https://github.com/dennisfrett + e-mail : dennis.frett@live.com + */ + +#pragma once + +#include "hiduniversal.h" + +#define MINIDSP_VID 0x2752 // MiniDSP +#define MINIDSP_PID 0x0011 // MiniDSP 2x4HD + +/** + * Arduino MiniDSP 2x4HD USB Host Driver by Dennis Frett. + * + * This class implements support for the MiniDSP 2x4HD via USB. + * Based on NodeJS implementation by Mathieu Rene: + * https://github.com/mrene/node-minidsp and the Python implementation by Mark + * Kubiak: https://github.com/markubiak/python3-minidsp. + * + * It uses the HIDUniversal class for all the USB communication. + */ +class MiniDSP : public HIDUniversal { +public: + + /** + * Constructor for the MiniDSP class. + * @param p Pointer to the USB class instance. + */ + MiniDSP(USB *p) : HIDUniversal(p) { + }; + + /** + * Used to check if a MiniDSP 2x4HD is connected. + * @return Returns true if it is connected. + */ + bool connected() { + return HIDUniversal::isReady() && HIDUniversal::VID == MINIDSP_VID && HIDUniversal::PID == MINIDSP_PID; + }; + + /** + * Used to call your own function when the device is successfully + * initialized. + * @param funcOnInit Function to call. + */ + void attachOnInit(void (*funcOnInit)(void)) { + pFuncOnInit = funcOnInit; + }; + + /** + * Used to call your own function when the volume has changed. + * The volume is passed as an unsigned integer that represents twice the + * -dB value. Example: 19 represents -9.5dB. + * @param funcOnVolumeChange Function to call. + */ + void attachOnVolumeChange(void (*funcOnVolumeChange)(uint8_t)) { + pFuncOnVolumeChange = funcOnVolumeChange; + } + + /** + * Used to call your own function when the muted status has changed. + * The muted status is passed as a boolean. True means muted, false + * means unmuted. + * @param funcOnMutedChange Function to call. + */ + void attachOnMutedChange(void (*funcOnMutedChange)(bool)) { + pFuncOnMutedChange = funcOnMutedChange; + } + + /** + * Retrieve the current volume of the MiniDSP. + * The volume is passed as an unsigned integer that represents twice the + * -dB value. Example: 19 represents -9.5dB. + * @return Current volume. + */ + int getVolume() const { + return volume; + } + + /** + * Retrieve the current volume of the MiniDSP in -dB. + * @return Current volume. + */ + float getVolumeDB() const { + return volume / -2.0; + } + + /** + * Retrieve the current muted status of the MiniDSP + * @return `true` if the device is muted, `false` otherwise. + */ + bool isMuted() const { + return muted; + } + +protected: + /** @name HIDUniversal implementation */ + /** + * Used to parse USB HID data. + * @param hid Pointer to the HID class. + * @param is_rpt_id Only used for Hubs. + * @param len The length of the incoming data. + * @param buf Pointer to the data buffer. + */ + void ParseHIDData(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *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. + */ + uint8_t OnInitSuccessful(); + /**@}*/ + + /** @name USBDeviceConfig implementation */ + + /** + * Used by the USB core to check what this driver support. + * @param vid The device's VID. + * @param pid The device's PID. + * @return Returns true if the device's VID and PID matches this + * driver. + */ + virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { + return vid == MINIDSP_VID && pid == MINIDSP_PID; + }; + /**@}*/ + +private: + /** + * Calculate checksum for given buffer. + * Checksum is given by summing up all bytes in `data` and returning the first byte. + * @param data Buffer to calculate checksum for. + * @param data_length Length of the buffer. + */ + uint8_t Checksum(const uint8_t *data, uint8_t data_length) const; + + /** + * Send the "Request status" command to the MiniDSP. The response + * includes the current volume and the muted status. + */ + void + RequestStatus() const; + + /** + * Send the given MiniDSP command. This function will create a buffer + * with the expected header and checksum and send it to the MiniDSP. + * Responses will come in throug `ParseHIDData`. + * @param command Buffer of the command to send. + * @param command_length Length of the buffer. + */ + void SendCommand(uint8_t *command, uint8_t command_length) const; + + // Callbacks + + // Pointer to function called in onInit(). + void (*pFuncOnInit)(void) = nullptr; + + // Pointer to function called when volume changes. + void (*pFuncOnVolumeChange)(uint8_t) = nullptr; + + // Pointer to function called when muted status changes. + void (*pFuncOnMutedChange)(bool) = nullptr; + + // ----------------------------------------------------------------------------- + + // MiniDSP state. Currently only volume and muted status are + // implemented, but others can be added easily if needed. + + // The volume is stored as an unsigned integer that represents twice the + // -dB value. Example: 19 represents -9.5dB. + uint8_t volume = 0; + bool muted = false; +}; From 37c7c5155ad62f6bced800fcb78555e72d781409 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sat, 6 Feb 2021 20:41:32 +0100 Subject: [PATCH 53/93] Fixed copyright year --- MiniDSP.cpp | 4 ++-- MiniDSP.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MiniDSP.cpp b/MiniDSP.cpp index d251e519..359018ba 100644 --- a/MiniDSP.cpp +++ b/MiniDSP.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Kristian Sloth Lauszus and Dennis Frett. All rights reserved. +/* Copyright (C) 2021 Kristian Sloth Lauszus and Dennis Frett. 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 @@ -15,7 +15,7 @@ e-mail : lauszus@gmail.com Dennis Frett - GitHub : https://github.com/dennisfrett + GitHub : https://github.com/dennisfrett e-mail : dennis.frett@live.com */ diff --git a/MiniDSP.h b/MiniDSP.h index 63e72c6f..442a8f39 100644 --- a/MiniDSP.h +++ b/MiniDSP.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Kristian Sloth Lauszus and Dennis Frett. All rights reserved. +/* Copyright (C) 2021 Kristian Sloth Lauszus and Dennis Frett. 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 @@ -15,7 +15,7 @@ e-mail : lauszus@gmail.com Dennis Frett - GitHub : https://github.com/dennisfrett + GitHub : https://github.com/dennisfrett e-mail : dennis.frett@live.com */ From 09fe72e7549ef5327607e4e5a20afa469283d2e2 Mon Sep 17 00:00:00 2001 From: Aran Vink Date: Tue, 16 Mar 2021 23:49:12 +0100 Subject: [PATCH 54/93] Commit for AMBX support --- AMBX.cpp | 253 +++++++++++++++++++++++++++++++++++++++++ AMBX.h | 159 ++++++++++++++++++++++++++ AMBXEnums.h | 38 +++++++ examples/ambx/AMBX.ino | 55 +++++++++ 4 files changed, 505 insertions(+) create mode 100644 AMBX.cpp create mode 100644 AMBX.h create mode 100644 AMBXEnums.h create mode 100644 examples/ambx/AMBX.ino diff --git a/AMBX.cpp b/AMBX.cpp new file mode 100644 index 00000000..4df787d3 --- /dev/null +++ b/AMBX.cpp @@ -0,0 +1,253 @@ +/* Copyright (C) 2021 Aran Vink. 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 + ------------------- + + Aran Vink + e-mail : aranvink@gmail.com + */ + +#include "AMBX.h" +// To enable serial debugging see "settings.h" +//#define EXTRADEBUG // Uncomment to get even more debugging data + +AMBX::AMBX(USB *p) : +pUsb(p), // pointer to USB class instance - mandatory +bAddress(0) // device address - mandatory +{ + for(uint8_t i = 0; i < AMBX_MAX_ENDPOINTS; i++) { + epInfo[i].epAddr = 0; + epInfo[i].maxPktSize = (i) ? 0 : 8; + epInfo[i].bmSndToggle = 0; + epInfo[i].bmRcvToggle = 0; + epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER; + } + + if(pUsb) // register in USB subsystem + pUsb->RegisterDeviceClass(this); //set devConfig[] entry +} + +uint8_t AMBX::Init(uint8_t parent, uint8_t port, bool lowspeed) { + uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)]; + USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast(buf); + uint8_t rcode; + UsbDevice *p = NULL; + EpInfo *oldep_ptr = NULL; + uint16_t PID; + uint16_t VID; + + // get memory address of USB device address pool + AddressPool &addrPool = pUsb->GetAddressPool(); +#ifdef EXTRADEBUG + Notify(PSTR("\r\nAMBX Init"), 0x80); +#endif + // check if address has already been assigned to an instance + if(bAddress) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nAddress in use"), 0x80); +#endif + return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; + } + + // Get pointer to pseudo device with address 0 assigned + p = addrPool.GetUsbDevicePtr(0); + + if(!p) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nAddress not found"), 0x80); +#endif + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + } + + if(!p->epinfo) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nepinfo is null"), 0x80); +#endif + return USB_ERROR_EPINFO_IS_NULL; + } + + // Save old pointer to EP_RECORD of address 0 + oldep_ptr = p->epinfo; + + // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence + p->epinfo = epInfo; + + p->lowspeed = lowspeed; + + // Get device descriptor + rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data + // Restore p->epinfo + p->epinfo = oldep_ptr; + + if(rcode) + goto FailGetDevDescr; + + VID = udd->idVendor; + PID = udd->idProduct; + + if(VID != AMBX_VID || (PID != AMBX_PID)) + goto FailUnknownDevice; + + // Allocate new address according to device class + bAddress = addrPool.AllocAddress(parent, false, port); + + if(!bAddress) + return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; + + // Extract Max Packet Size from device descriptor + epInfo[0].maxPktSize = udd->bMaxPacketSize0; + + // Assign new address to the device + rcode = pUsb->setAddr(0, 0, bAddress); + if(rcode) { + p->lowspeed = false; + addrPool.FreeAddress(bAddress); + bAddress = 0; +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nsetAddr: "), 0x80); + D_PrintHex (rcode, 0x80); +#endif + return rcode; + } +#ifdef EXTRADEBUG + Notify(PSTR("\r\nAddr: "), 0x80); + D_PrintHex (bAddress, 0x80); +#endif + + p->lowspeed = false; + + //get pointer to assigned address record + p = addrPool.GetUsbDevicePtr(bAddress); + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + p->lowspeed = lowspeed; + + // Assign epInfo to epinfo pointer - only EP0 is known + rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); + if(rcode) + goto FailSetDevTblEntry; + + + /* The application will work in reduced host mode, so we can save program and data + memory space. After verifying the PID and VID we will use known values for the + configuration values for device, interface, endpoints for the AMBX Controller */ + + /* Initialize data structures for endpoints of device */ + epInfo[ AMBX_OUTPUT_PIPE ].epAddr = AMBX_ENDPOINT_OUT; // AMBX output endpoint + epInfo[ AMBX_OUTPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT; + epInfo[ AMBX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints + epInfo[ AMBX_OUTPUT_PIPE ].maxPktSize = AMBX_EP_MAXPKTSIZE; + epInfo[ AMBX_OUTPUT_PIPE ].bmSndToggle = 0; + epInfo[ AMBX_OUTPUT_PIPE ].bmRcvToggle = 0; + + rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo); + if(rcode) + goto FailSetDevTblEntry; + + delay(200); //Give time for address change + + //For some reason this is need to make it work + rcode = pUsb->setConf(bAddress, epInfo[ AMBX_CONTROL_PIPE ].epAddr, 1); + if(rcode) + goto FailSetConfDescr; + + if(PID == AMBX_PID || PID) { + AMBXConnected = true; + } + onInit(); + + Notify(PSTR("\r\n"), 0x80); + return 0; // Successful configuration + + /* Diagnostic messages */ +FailGetDevDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetDevDescr(); + goto Fail; +#endif + +FailSetDevTblEntry: +#ifdef DEBUG_USB_HOST + NotifyFailSetDevTblEntry(); + goto Fail; +#endif + +FailSetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailSetConfDescr(); +#endif + goto Fail; + +FailUnknownDevice: +#ifdef DEBUG_USB_HOST + NotifyFailUnknownDevice(VID, PID); +#endif + rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; + +Fail: +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nAMBX Init Failed, error code: "), 0x80); + NotifyFail(rcode); +#endif + Release(); + return rcode; +} + +/* Performs a cleanup after failed Init() attempt */ +uint8_t AMBX::Release() { + AMBXConnected = false; + pUsb->GetAddressPool().FreeAddress(bAddress); + bAddress = 0; + return 0; +} + +uint8_t AMBX::Poll() { + return 0; +} + +void AMBX::Light_Command(uint8_t *data, uint16_t nbytes) { + #ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nLight command "), 0x80); + #endif + pUsb->outTransfer(bAddress, epInfo[ AMBX_OUTPUT_PIPE ].epAddr, nbytes, data); + //Do really short delay, I've noticed otherwise the controller will receive all command at once, and might not process all of them. + delay(1); +} + +void AMBX::setLight(uint8_t ambx_light, uint8_t r, uint8_t g, uint8_t b) { + writeBuf[0] = AMBX_PREFIX_COMMAND; + writeBuf[1] = ambx_light; + writeBuf[2] = AMBX_SET_COLOR_COMMAND; + writeBuf[3] = r; + writeBuf[4] = g; + writeBuf[5] = b; + Light_Command(writeBuf, AMBX_LIGHT_COMMAND_BUFFER_SIZE); +} + +void AMBX::setLight(AmbxLightsEnum ambx_light, AmbxColorsEnum color) { // Use this to set the Light with Color using the predefined in "AMBXEnums.h" + setLight(ambx_light, (uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color)); +} + +void AMBX::setAllLights(AmbxColorsEnum color) { // Use this to set the Color using the predefined colors in "AMBXEnums.h" + setLight(Sidelight_left, (uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color)); + setLight(Sidelight_right, (uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color)); + setLight(Wallwasher_center, (uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color)); + setLight(Wallwasher_left, (uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color)); + setLight(Wallwasher_right, (uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color)); +} + +void AMBX::onInit() { + #ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nOnInit execute "), 0x80); + #endif + if(pFuncOnInit) + pFuncOnInit(); // Call the user function +} diff --git a/AMBX.h b/AMBX.h new file mode 100644 index 00000000..aed3a6fc --- /dev/null +++ b/AMBX.h @@ -0,0 +1,159 @@ +/* Copyright (C) 2021 Aran Vink. 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 + ------------------- + + Aran Vink + e-mail : aranvink@gmail.com + */ + +#ifndef _ambxusb_h_ +#define _ambxusb_h_ + +#include "Usb.h" +#include "AMBXEnums.h" + +/* AMBX data taken from descriptors */ +#define AMBX_EP_MAXPKTSIZE 40 // max size for data via USB + +/* Names we give to the 3 AMBX but note only one is actually used (output) */ +#define AMBX_CONTROL_PIPE 0 +#define AMBX_OUTPUT_PIPE 1 +#define AMBX_INPUT_PIPE 2 + +/* PID and VID of the different devices */ +#define AMBX_VID 0x0471 // Philips +#define AMBX_PID 0x083F // AMBX Controller + +/* Endpoint addresses */ +#define AMBX_ENDPOINT_IN 0x81 +#define AMBX_ENDPOINT_OUT 0x02 +#define AMBX_ENDPOINT_PNP 0x83 + +/* Output payload constants */ +#define AMBX_PREFIX_COMMAND 0xA1 +#define AMBX_SET_COLOR_COMMAND 0x03 + +/* LEFT/RIGHT lights. Normally placed adjecent to your screen. */ +#define AMBX_LIGHT_LEFT 0x0B +#define AMBX_LIGHT_RIGHT 0x1B + +/* Wallwasher lights. Normally placed behind your screen. */ +#define AMBX_LIGHT_WW_LEFT 0x2B +#define AMBX_LIGHT_WW_CENTER 0x3B +#define AMBX_LIGHT_WW_RIGHT 0x4B + +#define AMBX_LIGHT_COMMAND_BUFFER_SIZE 6 + + +#define AMBX_MAX_ENDPOINTS 3 + +/** + * This class implements support for AMBX + * One can only set the color of the bulbs, no other accesories like rumble pad, fans, etc. are supported + * + */ +class AMBX : public USBDeviceConfig { +public: + /** + * Constructor for the AMBX class. + * @param pUsb Pointer to USB class instance. + */ + AMBX(USB *pUsb); + + /** @name USBDeviceConfig implementation */ + /** + * Initialize the AMBX Controller. + * @param parent Hub number. + * @param port Port number on the hub. + * @param lowspeed Speed of the device. + * @return 0 on success. + */ + uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); + /** + * Release the USB device. + * @return 0 on success. + */ + uint8_t Release(); + /** + * Poll the USB Input endpoins and run the state machines. + * @return 0 on success. + */ + uint8_t Poll(); + + /** + * Get the device address. + * @return The device address. + */ + virtual uint8_t GetAddress() { + return bAddress; + }; + + /** + * Used by the USB core to check what this driver support. + * @param vid The device's VID. + * @param pid The device's PID. + * @return Returns true if the device's VID and PID matches this driver. + */ + virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { + return (vid == AMBX_VID && (pid == AMBX_PID)); + }; + /**@}*/ + + /** + * Use this to set the Color using RGB values. + * @param r,g,b RGB value. + */ + void setLight(uint8_t ambx_light, uint8_t r, uint8_t g, uint8_t b); + /** + * Use this to set the color using the predefined colors in ::ColorsEnum. + * @param color The desired color. + */ + void setLight(AmbxLightsEnum ambx_light, AmbxColorsEnum color); + + /** + * Use this to set the color using the predefined colors in ::ColorsEnum. + * @param color The desired color. + */ + void setAllLights(AmbxColorsEnum color); + + /** + * Used to call your own function when the controller is successfully initialized. + * @param funcOnInit Function to call. + */ + void attachOnInit(void (*funcOnInit)(void)) { + pFuncOnInit = funcOnInit; + }; + /**@}*/ + + bool AMBXConnected; + +protected: + /** Pointer to USB class instance. */ + USB *pUsb; + /** Device address. */ + uint8_t bAddress; + /** Endpoint info structure. */ + EpInfo epInfo[AMBX_MAX_ENDPOINTS]; + +private: + /** + * Called when the AMBX controller is successfully initialized. + */ + void onInit(); + void (*pFuncOnInit)(void); // Pointer to function called in onInit() + + uint8_t writeBuf[AMBX_EP_MAXPKTSIZE]; // General purpose buffer for output data + + /* Private commands */ + void Light_Command(uint8_t *data, uint16_t nbytes); +}; + +#endif diff --git a/AMBXEnums.h b/AMBXEnums.h new file mode 100644 index 00000000..e3c4c5ca --- /dev/null +++ b/AMBXEnums.h @@ -0,0 +1,38 @@ +/* Copyright (C) 2021 Aran Vink. 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 + ------------------- + + Aran Vink + e-mail : aranvink@gmail.com + */ + +#ifndef _ambxenums_h +#define _ambxenums_h + +/** Used to set the colors of the AMBX lights. This is just a limited predefined set, the lights allow ANY value between 0x00 and 0xFF */ +enum AmbxColorsEnum { + Red = 0xFF0000, + Green = 0x00FF00, + Blue = 0x0000FF, + White = 0xFFFFFF, + Off = 0x000000, +}; + +/** Used to select light in the AMBX system */ +enum AmbxLightsEnum { + Sidelight_left = 0x0B, + Sidelight_right = 0x1B, + Wallwasher_left = 0x2B, + Wallwasher_center = 0x3B, + Wallwasher_right = 0x4B, +}; + +#endif diff --git a/examples/ambx/AMBX.ino b/examples/ambx/AMBX.ino new file mode 100644 index 00000000..e1eb9147 --- /dev/null +++ b/examples/ambx/AMBX.ino @@ -0,0 +1,55 @@ +/* + Example sketch for the AMBX library - developed by Aran Vink + */ + +#include + +// Satisfy the IDE, which needs to see the include statment in the ino too. +#ifdef dobogusinclude +#include +#endif +#include + +USB Usb; +AMBX AMBX(&Usb); // This will just create the instance + +bool printAngle; +uint8_t state = 0; + +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\nAMBX USB Library Started")); +} +void loop() { + Usb.Task(); + + if (AMBX.AMBXConnected) { // One can only set the color of the bulb, set the rumble, set and get the bluetooth address and calibrate the magnetometer via USB + if (state == 0) { + + } else if (state == 1) { + AMBX.setAllLights(Red); + } else if (state == 2) { + AMBX.setAllLights(Green); + } else if (state == 3) { + AMBX.setAllLights(Blue); + } else if (state == 4) { + AMBX.setAllLights(White); + } + + //Example using single light: + //AMBX.setLight(Wallwasher_center, White); + + state++; + if (state > 4) + state = 0; + delay(1000); + } + delay(10); +} From 38dcb823d5e0b2f2a2d11c429e0ca23a9dad4443 Mon Sep 17 00:00:00 2001 From: Aran Vink Date: Wed, 17 Mar 2021 00:07:13 +0100 Subject: [PATCH 55/93] Add ambx to github workflow --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5cc29556..9551edc2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: # find examples -type f -name "*.ino" | rev | cut -d/ -f2- | rev | sort | sed -z 's/\n/, /g' - example: [examples/acm/acm_terminal, examples/adk/adk_barcode, examples/adk/ArduinoBlinkLED, examples/adk/demokit_20, examples/adk/term_test, examples/adk/term_time, examples/Bluetooth/BTHID, examples/Bluetooth/PS3BT, examples/Bluetooth/PS3Multi, examples/Bluetooth/PS3SPP, examples/Bluetooth/PS4BT, examples/Bluetooth/PS5BT, examples/Bluetooth/SPP, examples/Bluetooth/SPPMulti, examples/Bluetooth/Wii, examples/Bluetooth/WiiBalanceBoard, examples/Bluetooth/WiiIRCamera, examples/Bluetooth/WiiMulti, examples/Bluetooth/WiiUProController, examples/board_qc, examples/cdc_XR21B1411/XR_terminal, examples/ftdi/USBFTDILoopback, examples/GPIO/Blink, examples/GPIO/Blink_LowLevel, examples/GPIO/Input, examples/HID/le3dp, examples/HID/scale, examples/HID/SRWS1, examples/HID/t16km, examples/HID/USBHIDBootKbd, examples/HID/USBHIDBootKbdAndMouse, examples/HID/USBHIDBootMouse, examples/HID/USBHID_desc, examples/HID/USBHIDJoystick, examples/HID/USBHIDMultimediaKbd, examples/hub_demo, examples/max_LCD, examples/pl2303/pl2303_gprs_terminal, examples/pl2303/pl2303_gps, examples/pl2303/pl2303_tinygps, examples/pl2303/pl2303_xbee_terminal, examples/PS3USB, examples/PS4USB, examples/PS5USB, examples/PSBuzz, examples/USB_desc, examples/USBH_MIDI/bidirectional_converter, examples/USBH_MIDI/eVY1_sample, examples/USBH_MIDI/USBH_MIDI_dump, examples/USBH_MIDI/USB_MIDI_converter, examples/USBH_MIDI/USB_MIDI_converter_multi, examples/Xbox/XBOXOLD, examples/Xbox/XBOXONE, examples/Xbox/XBOXONESBT, examples/Xbox/XBOXRECV, examples/Xbox/XBOXUSB] + example: [examples/ambx/AMBX, examples/acm/acm_terminal, examples/adk/adk_barcode, examples/adk/ArduinoBlinkLED, examples/adk/demokit_20, examples/adk/term_test, examples/adk/term_time, examples/Bluetooth/BTHID, examples/Bluetooth/PS3BT, examples/Bluetooth/PS3Multi, examples/Bluetooth/PS3SPP, examples/Bluetooth/PS4BT, examples/Bluetooth/PS5BT, examples/Bluetooth/SPP, examples/Bluetooth/SPPMulti, examples/Bluetooth/Wii, examples/Bluetooth/WiiBalanceBoard, examples/Bluetooth/WiiIRCamera, examples/Bluetooth/WiiMulti, examples/Bluetooth/WiiUProController, examples/board_qc, examples/cdc_XR21B1411/XR_terminal, examples/ftdi/USBFTDILoopback, examples/GPIO/Blink, examples/GPIO/Blink_LowLevel, examples/GPIO/Input, examples/HID/le3dp, examples/HID/scale, examples/HID/SRWS1, examples/HID/t16km, examples/HID/USBHIDBootKbd, examples/HID/USBHIDBootKbdAndMouse, examples/HID/USBHIDBootMouse, examples/HID/USBHID_desc, examples/HID/USBHIDJoystick, examples/HID/USBHIDMultimediaKbd, examples/hub_demo, examples/max_LCD, examples/pl2303/pl2303_gprs_terminal, examples/pl2303/pl2303_gps, examples/pl2303/pl2303_tinygps, examples/pl2303/pl2303_xbee_terminal, examples/PS3USB, examples/PS4USB, examples/PS5USB, examples/PSBuzz, examples/USB_desc, examples/USBH_MIDI/bidirectional_converter, examples/USBH_MIDI/eVY1_sample, examples/USBH_MIDI/USBH_MIDI_dump, examples/USBH_MIDI/USB_MIDI_converter, examples/USBH_MIDI/USB_MIDI_converter_multi, examples/Xbox/XBOXOLD, examples/Xbox/XBOXONE, examples/Xbox/XBOXONESBT, examples/Xbox/XBOXRECV, examples/Xbox/XBOXUSB] steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 From a23ba6d4938cbaa652e677fbd0ad7f223191f0b1 Mon Sep 17 00:00:00 2001 From: Aran Vink Date: Wed, 17 Mar 2021 00:18:23 +0100 Subject: [PATCH 56/93] Correct example name in workflow CICD --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9551edc2..95deb178 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: # find examples -type f -name "*.ino" | rev | cut -d/ -f2- | rev | sort | sed -z 's/\n/, /g' - example: [examples/ambx/AMBX, examples/acm/acm_terminal, examples/adk/adk_barcode, examples/adk/ArduinoBlinkLED, examples/adk/demokit_20, examples/adk/term_test, examples/adk/term_time, examples/Bluetooth/BTHID, examples/Bluetooth/PS3BT, examples/Bluetooth/PS3Multi, examples/Bluetooth/PS3SPP, examples/Bluetooth/PS4BT, examples/Bluetooth/PS5BT, examples/Bluetooth/SPP, examples/Bluetooth/SPPMulti, examples/Bluetooth/Wii, examples/Bluetooth/WiiBalanceBoard, examples/Bluetooth/WiiIRCamera, examples/Bluetooth/WiiMulti, examples/Bluetooth/WiiUProController, examples/board_qc, examples/cdc_XR21B1411/XR_terminal, examples/ftdi/USBFTDILoopback, examples/GPIO/Blink, examples/GPIO/Blink_LowLevel, examples/GPIO/Input, examples/HID/le3dp, examples/HID/scale, examples/HID/SRWS1, examples/HID/t16km, examples/HID/USBHIDBootKbd, examples/HID/USBHIDBootKbdAndMouse, examples/HID/USBHIDBootMouse, examples/HID/USBHID_desc, examples/HID/USBHIDJoystick, examples/HID/USBHIDMultimediaKbd, examples/hub_demo, examples/max_LCD, examples/pl2303/pl2303_gprs_terminal, examples/pl2303/pl2303_gps, examples/pl2303/pl2303_tinygps, examples/pl2303/pl2303_xbee_terminal, examples/PS3USB, examples/PS4USB, examples/PS5USB, examples/PSBuzz, examples/USB_desc, examples/USBH_MIDI/bidirectional_converter, examples/USBH_MIDI/eVY1_sample, examples/USBH_MIDI/USBH_MIDI_dump, examples/USBH_MIDI/USB_MIDI_converter, examples/USBH_MIDI/USB_MIDI_converter_multi, examples/Xbox/XBOXOLD, examples/Xbox/XBOXONE, examples/Xbox/XBOXONESBT, examples/Xbox/XBOXRECV, examples/Xbox/XBOXUSB] + example: [examples/ambx, examples/acm/acm_terminal, examples/adk/adk_barcode, examples/adk/ArduinoBlinkLED, examples/adk/demokit_20, examples/adk/term_test, examples/adk/term_time, examples/Bluetooth/BTHID, examples/Bluetooth/PS3BT, examples/Bluetooth/PS3Multi, examples/Bluetooth/PS3SPP, examples/Bluetooth/PS4BT, examples/Bluetooth/PS5BT, examples/Bluetooth/SPP, examples/Bluetooth/SPPMulti, examples/Bluetooth/Wii, examples/Bluetooth/WiiBalanceBoard, examples/Bluetooth/WiiIRCamera, examples/Bluetooth/WiiMulti, examples/Bluetooth/WiiUProController, examples/board_qc, examples/cdc_XR21B1411/XR_terminal, examples/ftdi/USBFTDILoopback, examples/GPIO/Blink, examples/GPIO/Blink_LowLevel, examples/GPIO/Input, examples/HID/le3dp, examples/HID/scale, examples/HID/SRWS1, examples/HID/t16km, examples/HID/USBHIDBootKbd, examples/HID/USBHIDBootKbdAndMouse, examples/HID/USBHIDBootMouse, examples/HID/USBHID_desc, examples/HID/USBHIDJoystick, examples/HID/USBHIDMultimediaKbd, examples/hub_demo, examples/max_LCD, examples/pl2303/pl2303_gprs_terminal, examples/pl2303/pl2303_gps, examples/pl2303/pl2303_tinygps, examples/pl2303/pl2303_xbee_terminal, examples/PS3USB, examples/PS4USB, examples/PS5USB, examples/PSBuzz, examples/USB_desc, examples/USBH_MIDI/bidirectional_converter, examples/USBH_MIDI/eVY1_sample, examples/USBH_MIDI/USBH_MIDI_dump, examples/USBH_MIDI/USB_MIDI_converter, examples/USBH_MIDI/USB_MIDI_converter_multi, examples/Xbox/XBOXOLD, examples/Xbox/XBOXONE, examples/Xbox/XBOXONESBT, examples/Xbox/XBOXRECV, examples/Xbox/XBOXUSB] steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 From 4f74db45f05794a1c8fd4f3ee0f8705d4e807868 Mon Sep 17 00:00:00 2001 From: Aran Vink Date: Wed, 17 Mar 2021 00:26:14 +0100 Subject: [PATCH 57/93] Remove obsolete comment from example --- examples/ambx/AMBX.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ambx/AMBX.ino b/examples/ambx/AMBX.ino index e1eb9147..f7280e2c 100644 --- a/examples/ambx/AMBX.ino +++ b/examples/ambx/AMBX.ino @@ -30,7 +30,7 @@ void setup() { void loop() { Usb.Task(); - if (AMBX.AMBXConnected) { // One can only set the color of the bulb, set the rumble, set and get the bluetooth address and calibrate the magnetometer via USB + if (AMBX.AMBXConnected) { if (state == 0) { } else if (state == 1) { From a56613ae661e473b911f85048a5405cb4c969914 Mon Sep 17 00:00:00 2001 From: Joseph Duchesne Date: Mon, 22 Mar 2021 09:28:22 -0400 Subject: [PATCH 58/93] Added support for Adafruit NRF52840 Feather Express Added definitions for Adafruit NRF52840 Feather Express based on the existing RBL_NRF51822 variant. The PS5 controller and USB_desc examples have been tested and seem to work fine with a knockoff Duinofun USBmini 2.0 shield (after accounting for the mislabled pins on this hardware, and cutting the USB VDD trace to wire it to 5V). --- SPP.h | 2 +- avrpins.h | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ settings.h | 5 +++++ usbhost.h | 4 +++- 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/SPP.h b/SPP.h index 116296ca..c5418fc3 100644 --- a/SPP.h +++ b/SPP.h @@ -114,7 +114,7 @@ public: */ size_t write(const uint8_t* data, size_t size); /** Pull in write(const char *str) from Print */ -#if !defined(RBL_NRF51822) +#if !defined(RBL_NRF51822) && !defined(NRF52_SERIES) using Print::write; #endif #else diff --git a/avrpins.h b/avrpins.h index 8598d93d..6e4ef085 100644 --- a/avrpins.h +++ b/avrpins.h @@ -1294,6 +1294,68 @@ MAKE_PIN(P19, GPIOC, GPIO_PIN_0); // A5 #undef MAKE_PIN + +#elif defined(ARDUINO_NRF52840_FEATHER) + +#define MAKE_PIN(className, pin) \ +class className { \ +public: \ + static void Set() { \ + nrf_gpio_pin_set(pin); \ + } \ + static void Clear() { \ + nrf_gpio_pin_clear(pin); \ + } \ + static void SetDirRead() { \ + nrf_gpio_cfg_input(pin, NRF_GPIO_PIN_NOPULL); \ + } \ + static void SetDirWrite() { \ + nrf_gpio_cfg_output(pin); \ + } \ + static uint8_t IsSet() { \ + return (uint8_t)nrf_gpio_pin_read(pin); \ + } \ +}; + +// Based on variants/feather_nrf52840_express/variant.cpp +// g_ADigitalPinMap ould be used directly, but it would be slower +MAKE_PIN(P0, (25)); +MAKE_PIN(P1, (24)); +MAKE_PIN(P2, (10)); +MAKE_PIN(P3, (47)); +MAKE_PIN(P4, (42)); +MAKE_PIN(P5, (40)); +MAKE_PIN(P6, (7)); +MAKE_PIN(P7, (34)); +MAKE_PIN(P8, (16)); +MAKE_PIN(P9, (26)); +MAKE_PIN(P10, (27)); +MAKE_PIN(P11, (6)); +MAKE_PIN(P12, (8)); +MAKE_PIN(P13, (41)); +MAKE_PIN(P14, (4)); +MAKE_PIN(P15, (5)); +MAKE_PIN(P17, (30)); +MAKE_PIN(P18, (28)); +MAKE_PIN(P16, (2)); +MAKE_PIN(P19, (3)); +MAKE_PIN(P20, (29)); +MAKE_PIN(P21, (31)); +MAKE_PIN(P22, (12)); +MAKE_PIN(P23, (11)); +MAKE_PIN(P24, (15)); +MAKE_PIN(P25, (13)); +MAKE_PIN(P26, (14)); +MAKE_PIN(P27, (19)); +MAKE_PIN(P28, (20)); +MAKE_PIN(P29, (17)); +MAKE_PIN(P30, (22)); +MAKE_PIN(P31, (23)); +MAKE_PIN(P32, (21)); +MAKE_PIN(P33, (9)); + +#undef MAKE_PIN + #else #error "Please define board in avrpins.h" diff --git a/settings.h b/settings.h index 7e6ae6c7..6b134dfb 100644 --- a/settings.h +++ b/settings.h @@ -157,6 +157,11 @@ e-mail : support@circuitsathome.com #define SPI SPI_Master #define MFK_CASTUINT8T (uint8_t) // RBLs return type for sizeof needs casting to uint8_t #endif +#ifdef NRF52_SERIES +#include +#include +#define MFK_CASTUINT8T (uint8_t) // NRF return type for sizeof needs casting to uint8_t +#endif #if defined(__PIC32MX__) || defined(__PIC32MZ__) #include <../../../../hardware/pic32/libraries/SPI/SPI.h> // Hack to use the SPI library #endif diff --git a/usbhost.h b/usbhost.h index a81ea210..de34813b 100644 --- a/usbhost.h +++ b/usbhost.h @@ -70,7 +70,7 @@ public: #else USB_SPI.setClockDivider(SPI_CLOCK_DIV2); // This will set the SPI frequency to 8MHz - it could be higher, but it is not supported in the old API #endif -#elif !defined(RBL_NRF51822) +#elif !defined(RBL_NRF51822) && !defined(NRF52_SERIES) USB_SPI.setClockDivider(4); // Set speed to 84MHz/4=21MHz - the MAX3421E can handle up to 26MHz #endif } @@ -120,6 +120,8 @@ typedef SPi< P16, P18, P17, P10 > spi; typedef SPi< P14, P13, P12, P15 > spi; #elif defined(ESP32) typedef SPi< P18, P23, P19, P5 > spi; +#elif defined(ARDUINO_NRF52840_FEATHER) +typedef SPi< P26, P25, P24, P5 > spi; #else #error "No SPI entry in usbhost.h" #endif From e93c24df6f88b89253df8f2c48d4ad258bd6d79f Mon Sep 17 00:00:00 2001 From: Joseph Duchesne Date: Mon, 22 Mar 2021 09:31:54 -0400 Subject: [PATCH 59/93] Fixed small typo --- avrpins.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avrpins.h b/avrpins.h index 6e4ef085..89127f19 100644 --- a/avrpins.h +++ b/avrpins.h @@ -1318,7 +1318,7 @@ public: \ }; // Based on variants/feather_nrf52840_express/variant.cpp -// g_ADigitalPinMap ould be used directly, but it would be slower +// g_ADigitalPinMap could be used directly, but it would be slower MAKE_PIN(P0, (25)); MAKE_PIN(P1, (24)); MAKE_PIN(P2, (10)); From 10a17b4ae3f7d525aa1b1869442650445a68cb38 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Tue, 23 Mar 2021 12:46:31 +0100 Subject: [PATCH 60/93] The Adafruit Feather nRF52840 Express is now a supported board --- .github/workflows/main.yml | 2 +- README.md | 1 + library.json | 7 +++---- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5cc29556..8c5c4d90 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -33,7 +33,7 @@ jobs: # See: https://travis-ci.org/github/felis/USB_Host_Shield_2.0/jobs/743787235 if [[ "${{ matrix.example }}" != *"bidirectional_converter" ]]; then TEENSY35="--board=teensy35"; TEENSY36="--board=teensy36"; TEENSY40="--board=teensy40"; TEENSY41="--board=teensy41"; fi - pio ci --lib="." $UNO --board=genuino101 --board=teensylc $TEENSY40 $TEENSY41 --board=esp12e --board=nodemcu --board=esp32dev + pio ci --lib="." $UNO --board=genuino101 --board=teensylc $TEENSY40 $TEENSY41 --board=esp12e --board=nodemcu --board=esp32dev --board=adafruit_feather_nrf52840 # Teensy 3.x depends on the SPI4Teensy3 library: https://platformio.org/lib/show/417/SPI4Teensy3 pio ci --lib="." --board=teensy30 --board=teensy31 $TEENSY35 $TEENSY36 --project-option="lib_deps=SPI4Teensy3" diff --git a/README.md b/README.md index 968377d7..eba03c3c 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,7 @@ Currently the following boards are supported by the library: * Sanguino * Black Widdow * RedBearLab nRF51822 +* Adafruit Feather nRF52840 Express * Digilent chipKIT * Please see: . * STM32F4 diff --git a/library.json b/library.json index 559b7682..fbe6bc81 100644 --- a/library.json +++ b/library.json @@ -7,8 +7,7 @@ { "name": "Oleg Mazurov", "email": "mazurov@circuitsathome.com", - "url": "http://www.circuitsathome.com", - "maintainer": true + "url": "http://www.circuitsathome.com" }, { "name": "Alexei Glushchenko", @@ -22,8 +21,7 @@ }, { "name": "Andrew Kroll", - "email": "xxxajk@gmail.com", - "maintainer": true + "email": "xxxajk@gmail.com" } ], "repository": @@ -50,6 +48,7 @@ "teensy", "atmelsam", "nordicnrf51", + "nordicnrf52", "ststm32", "espressif8266", "espressif32" From 4d20580b184ed3612e3e2031578d16b5cf45f0db Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Tue, 23 Mar 2021 12:53:33 +0100 Subject: [PATCH 61/93] Workaround "warning: comparison between signed and unsigned integer expressions" in the Arduino core --- .github/workflows/main.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8c5c4d90..ceb64f9e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -33,13 +33,16 @@ jobs: # See: https://travis-ci.org/github/felis/USB_Host_Shield_2.0/jobs/743787235 if [[ "${{ matrix.example }}" != *"bidirectional_converter" ]]; then TEENSY35="--board=teensy35"; TEENSY36="--board=teensy36"; TEENSY40="--board=teensy40"; TEENSY41="--board=teensy41"; fi - pio ci --lib="." $UNO --board=genuino101 --board=teensylc $TEENSY40 $TEENSY41 --board=esp12e --board=nodemcu --board=esp32dev --board=adafruit_feather_nrf52840 + pio ci --lib="." $UNO --board=genuino101 --board=teensylc $TEENSY40 $TEENSY41 --board=esp12e --board=nodemcu --board=esp32dev # Teensy 3.x depends on the SPI4Teensy3 library: https://platformio.org/lib/show/417/SPI4Teensy3 pio ci --lib="." --board=teensy30 --board=teensy31 $TEENSY35 $TEENSY36 --project-option="lib_deps=SPI4Teensy3" # Workaround https://github.com/arduino/ArduinoCore-sam/issues/69 pio ci --lib="." --board=due --project-option="build_flags=-Wno-misleading-indentation" + + # Workaround "warning: comparison between signed and unsigned integer expressions" in the Arduino core + pio ci --lib="." --board=adafruit_feather_nrf52840 --project-option="build_flags=-Wno-sign-compare" env: PLATFORMIO_CI_SRC: ${{ matrix.example }} PLATFORMIO_BUILD_FLAGS: -DWIICAMERA -DDEBUG_USB_HOST -Wall -Werror From f31bca66d5577d9c564fa68f4bd21daa46fbcd99 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Tue, 23 Mar 2021 13:02:04 +0100 Subject: [PATCH 62/93] Also ignore 'unused-function' warnings --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ceb64f9e..e5ad35cb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -41,8 +41,8 @@ jobs: # Workaround https://github.com/arduino/ArduinoCore-sam/issues/69 pio ci --lib="." --board=due --project-option="build_flags=-Wno-misleading-indentation" - # Workaround "warning: comparison between signed and unsigned integer expressions" in the Arduino core - pio ci --lib="." --board=adafruit_feather_nrf52840 --project-option="build_flags=-Wno-sign-compare" + # Ignore warnings in the Arduino core + pio ci --lib="." --board=adafruit_feather_nrf52840 --project-option="build_flags=-Wno-sign-compare -Wno-unused-function" env: PLATFORMIO_CI_SRC: ${{ matrix.example }} PLATFORMIO_BUILD_FLAGS: -DWIICAMERA -DDEBUG_USB_HOST -Wall -Werror From 3be8da5ff606cb4c0ebf08e4c978bda485650f5a Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Tue, 23 Mar 2021 13:34:15 +0100 Subject: [PATCH 63/93] Ignore 'unused-variable' warnings as well --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e5ad35cb..7cb4f6b1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -42,7 +42,7 @@ jobs: pio ci --lib="." --board=due --project-option="build_flags=-Wno-misleading-indentation" # Ignore warnings in the Arduino core - pio ci --lib="." --board=adafruit_feather_nrf52840 --project-option="build_flags=-Wno-sign-compare -Wno-unused-function" + pio ci --lib="." --board=adafruit_feather_nrf52840 --project-option="build_flags=-Wno-sign-compare -Wno-unused-function -Wno-unused-variable" env: PLATFORMIO_CI_SRC: ${{ matrix.example }} PLATFORMIO_BUILD_FLAGS: -DWIICAMERA -DDEBUG_USB_HOST -Wall -Werror From a121516eff66b52c0f5f2435fe9dc6d931dadbb3 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Wed, 24 Mar 2021 13:07:15 +0100 Subject: [PATCH 64/93] Install adafruit-nrfutil, as it is needed for building the application even though it is only used for uploading --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7cb4f6b1..458b4890 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,7 +15,7 @@ jobs: - name: Install PlatformIO run: | pip install -U pip setuptools wheel - pip install platformio + pip install platformio adafruit-nrfutil - name: Install MIDI library if: contains(matrix.example, 'MIDI') # https://platformio.org/lib/show/62/MIDI%20Library From 0b4213319b50628221e95f50331bc0874dd70b8f Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Wed, 24 Mar 2021 13:39:51 +0100 Subject: [PATCH 65/93] Include for the Adafruit Feather nRF52840 Express manually --- examples/HID/scale/scale_rptparser.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/HID/scale/scale_rptparser.cpp b/examples/HID/scale/scale_rptparser.cpp index 97e216d5..bc240a59 100644 --- a/examples/HID/scale/scale_rptparser.cpp +++ b/examples/HID/scale/scale_rptparser.cpp @@ -1,7 +1,8 @@ -/* Parser for standard HID scale (usage page 0x8d) data input report (ID 3) */ -#ifdef ARDUINO_SAM_DUE +#if defined(ARDUINO_SAM_DUE) || defined(ARDUINO_NRF52840_FEATHER) #include #endif + +/* Parser for standard HID scale (usage page 0x8d) data input report (ID 3) */ #include "scale_rptparser.h" const char* UNITS[13] = { From a321ad36ab4dd9ea000ffa809532a937ffca06d0 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Thu, 25 Mar 2021 19:06:34 +0100 Subject: [PATCH 66/93] Release version 1.5.0 --- library.json | 2 +- library.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library.json b/library.json index fbe6bc81..d384b7a9 100644 --- a/library.json +++ b/library.json @@ -29,7 +29,7 @@ "type": "git", "url": "https://github.com/felis/USB_Host_Shield_2.0.git" }, - "version": "1.4.0", + "version": "1.5.0", "license": "GPL-2.0", "examples": [ diff --git a/library.properties b/library.properties index 05a06ab9..0ccff386 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=USB Host Shield Library 2.0 -version=1.4.0 +version=1.5.0 author=Oleg Mazurov (Circuits@Home) , Kristian Sloth Lauszus , Andrew Kroll , Alexei Glushchenko (Circuits@Home) maintainer=Oleg Mazurov (Circuits@Home) , Kristian Sloth Lauszus , Andrew Kroll sentence=Revision 2.0 of MAX3421E-based USB Host Shield Library. From 82df4b990ed046b74e929b16ddefa70ed65b2946 Mon Sep 17 00:00:00 2001 From: Yuuichi Akagawa Date: Fri, 26 Mar 2021 14:43:31 +0900 Subject: [PATCH 67/93] Recover from OUT-NAK --- Usb.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Usb.cpp b/Usb.cpp index 7843d49b..5fa75ec8 100644 --- a/Usb.cpp +++ b/Usb.cpp @@ -388,6 +388,11 @@ uint8_t USB::OutTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t nbytes, uint8 data_p += bytes_tosend; }//while( bytes_left... breakout: + /* If rcode(=rHRSL) is non-zero, untransmitted data remains in the SNDFIFO. */ + if(rcode != 0) { + //Switch the FIFO containing the OUT data back under microcontroller control and reset pointer. + regWr(rSNDBC, 0); + } pep->bmSndToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 1 : 0; //bmSNDTOG1 : bmSNDTOG0; //update toggle return ( rcode); //should be 0 in all cases From 26c66f455e5b51653dbbfe530ca3dc139e9ca794 Mon Sep 17 00:00:00 2001 From: David Madison Date: Thu, 25 Mar 2021 16:18:36 -0400 Subject: [PATCH 68/93] Remove shared identifiers in ButtonEnum Move shared button identifiers from enum to an inline function, so formerly shared identifiers can be separately filtered and processed on a class-by-class basis. Fixes #611. --- PS3BT.cpp | 9 ++- PS3Enums.h | 6 ++ PS3USB.cpp | 9 ++- PS4Parser.cpp | 14 +++- PS4Parser.h | 1 + PS5Parser.cpp | 14 +++- PS5Parser.h | 1 + PSBuzz.cpp | 12 ++- PSBuzz.h | 1 + Wii.cpp | 36 +++++++-- Wii.h | 2 + XBOXOLD.cpp | 117 +++++++++++++++++++++--------- XBOXOLD.h | 3 + XBOXONE.cpp | 12 +-- XBOXONESParser.cpp | 12 ++- XBOXONESParser.h | 2 + XBOXRECV.cpp | 6 +- XBOXUSB.cpp | 6 +- controllerEnums.h | 177 +++++++++++++++++++++++++++++++++------------ xboxEnums.h | 6 ++ 20 files changed, 331 insertions(+), 115 deletions(-) diff --git a/PS3BT.cpp b/PS3BT.cpp index 1e4e7f41..3326f1f6 100644 --- a/PS3BT.cpp +++ b/PS3BT.cpp @@ -47,18 +47,21 @@ BluetoothService(p) // Pointer to USB class instance - mandatory } bool PS3BT::getButtonPress(ButtonEnum b) { - return (ButtonState & pgm_read_dword(&PS3_BUTTONS[(uint8_t)b])); + const int8_t index = getPS3ButtonIndex(b); if (index < 0) return 0; + return (ButtonState & pgm_read_dword(&PS3_BUTTONS[index])); } bool PS3BT::getButtonClick(ButtonEnum b) { - uint32_t button = pgm_read_dword(&PS3_BUTTONS[(uint8_t)b]); + const int8_t index = getPS3ButtonIndex(b); if (index < 0) return 0; + uint32_t button = pgm_read_dword(&PS3_BUTTONS[index]); bool click = (ButtonClickState & button); ButtonClickState &= ~button; // Clear "click" event return click; } uint8_t PS3BT::getAnalogButton(ButtonEnum a) { - return (uint8_t)(l2capinbuf[pgm_read_byte(&PS3_ANALOG_BUTTONS[(uint8_t)a])]); + const int8_t index = getPS3ButtonIndex(a); if (index < 0) return 0; + return (uint8_t)(l2capinbuf[pgm_read_byte(&PS3_ANALOG_BUTTONS[index])]); } uint8_t PS3BT::getAnalogHat(AnalogHatEnum a) { diff --git a/PS3Enums.h b/PS3Enums.h index 77801945..77c7c536 100644 --- a/PS3Enums.h +++ b/PS3Enums.h @@ -138,4 +138,10 @@ enum StatusEnum { Bluetooth = (40 << 8) | 0x16, // Operating by Bluetooth and rumble is turned off }; +inline int8_t getPS3ButtonIndex(ButtonEnum b) { + const uint8_t index = legacyButtonValues(b); + if (index >= sizeof(PS3_BUTTONS) / sizeof(PS3_BUTTONS[0])) return -1; + return index; +} + #endif diff --git a/PS3USB.cpp b/PS3USB.cpp index 081a7a20..4aba0e54 100644 --- a/PS3USB.cpp +++ b/PS3USB.cpp @@ -314,18 +314,21 @@ void PS3USB::printReport() { // Uncomment "#define PRINTREPORT" to print the rep } bool PS3USB::getButtonPress(ButtonEnum b) { - return (ButtonState & pgm_read_dword(&PS3_BUTTONS[(uint8_t)b])); + const int8_t index = getPS3ButtonIndex(b); if (index < 0) return 0; + return (ButtonState & pgm_read_dword(&PS3_BUTTONS[index])); } bool PS3USB::getButtonClick(ButtonEnum b) { - uint32_t button = pgm_read_dword(&PS3_BUTTONS[(uint8_t)b]); + const int8_t index = getPS3ButtonIndex(b); if (index < 0) return 0; + uint32_t button = pgm_read_dword(&PS3_BUTTONS[index]); bool click = (ButtonClickState & button); ButtonClickState &= ~button; // Clear "click" event return click; } uint8_t PS3USB::getAnalogButton(ButtonEnum a) { - return (uint8_t)(readBuf[(pgm_read_byte(&PS3_ANALOG_BUTTONS[(uint8_t)a])) - 9]); + const int8_t index = getPS3ButtonIndex(a); if (index < 0) return 0; + return (uint8_t)(readBuf[(pgm_read_byte(&PS3_ANALOG_BUTTONS[index])) - 9]); } uint8_t PS3USB::getAnalogHat(AnalogHatEnum a) { diff --git a/PS4Parser.cpp b/PS4Parser.cpp index c484b83e..ed619a51 100644 --- a/PS4Parser.cpp +++ b/PS4Parser.cpp @@ -32,6 +32,12 @@ enum DPADEnum { // To enable serial debugging see "settings.h" //#define PRINTREPORT // Uncomment to print the report send by the PS4 Controller +int8_t PS4Parser::getButtonIndex(ButtonEnum b) { + const uint8_t index = legacyButtonValues(b); + if (index >= sizeof(PS4_BUTTONS) / sizeof(PS4_BUTTONS[0])) return -1; + return index; +} + bool PS4Parser::checkDpad(ButtonEnum b) { switch (b) { case UP: @@ -48,14 +54,16 @@ bool PS4Parser::checkDpad(ButtonEnum b) { } bool PS4Parser::getButtonPress(ButtonEnum b) { - if (b <= LEFT) // Dpad + const int8_t index = getButtonIndex(b); if (index < 0) return 0; + if (index <= LEFT) // Dpad return checkDpad(b); else - return ps4Data.btn.val & (1UL << pgm_read_byte(&PS4_BUTTONS[(uint8_t)b])); + return ps4Data.btn.val & (1UL << pgm_read_byte(&PS4_BUTTONS[index])); } bool PS4Parser::getButtonClick(ButtonEnum b) { - uint32_t mask = 1UL << pgm_read_byte(&PS4_BUTTONS[(uint8_t)b]); + const int8_t index = getButtonIndex(b); if (index < 0) return 0; + uint32_t mask = 1UL << pgm_read_byte(&PS4_BUTTONS[index]); bool click = buttonClickState.val & mask; buttonClickState.val &= ~mask; // Clear "click" event return click; diff --git a/PS4Parser.h b/PS4Parser.h index 86999623..4d6088ba 100644 --- a/PS4Parser.h +++ b/PS4Parser.h @@ -362,6 +362,7 @@ protected: virtual void sendOutputReport(PS4Output *output) = 0; private: + static int8_t getButtonIndex(ButtonEnum b); bool checkDpad(ButtonEnum b); // Used to check PS4 DPAD buttons PS4Data ps4Data; diff --git a/PS5Parser.cpp b/PS5Parser.cpp index fa3576c2..061ab8fe 100644 --- a/PS5Parser.cpp +++ b/PS5Parser.cpp @@ -36,6 +36,12 @@ enum DPADEnum { // To enable serial debugging see "settings.h" //#define PRINTREPORT // Uncomment to print the report send by the PS5 Controller +int8_t PS5Parser::getButtonIndex(ButtonEnum b) { + const uint8_t index = legacyButtonValues(b); + if (index >= sizeof(PS5_BUTTONS) / sizeof(PS5_BUTTONS[0])) return -1; + return index; +} + bool PS5Parser::checkDpad(ButtonEnum b) { switch (b) { case UP: @@ -52,14 +58,16 @@ bool PS5Parser::checkDpad(ButtonEnum b) { } bool PS5Parser::getButtonPress(ButtonEnum b) { - if (b <= LEFT) // Dpad + const int8_t index = getButtonIndex(b); if (index < 0) return 0; + if (index <= LEFT) // Dpad return checkDpad(b); else - return ps5Data.btn.val & (1UL << pgm_read_byte(&PS5_BUTTONS[(uint8_t)b])); + return ps5Data.btn.val & (1UL << pgm_read_byte(&PS5_BUTTONS[index])); } bool PS5Parser::getButtonClick(ButtonEnum b) { - uint32_t mask = 1UL << pgm_read_byte(&PS5_BUTTONS[(uint8_t)b]); + const int8_t index = getButtonIndex(b); if (index < 0) return 0; + uint32_t mask = 1UL << pgm_read_byte(&PS5_BUTTONS[index]); bool click = buttonClickState.val & mask; buttonClickState.val &= ~mask; // Clear "click" event return click; diff --git a/PS5Parser.h b/PS5Parser.h index 49fadd7b..7c0c29c9 100644 --- a/PS5Parser.h +++ b/PS5Parser.h @@ -403,6 +403,7 @@ protected: private: + static int8_t getButtonIndex(ButtonEnum b); bool checkDpad(ButtonEnum b); // Used to check PS5 DPAD buttons PS5Data ps5Data; diff --git a/PSBuzz.cpp b/PSBuzz.cpp index 2d4f2123..0ec452d6 100644 --- a/PSBuzz.cpp +++ b/PSBuzz.cpp @@ -49,12 +49,20 @@ uint8_t PSBuzz::OnInitSuccessful() { return 0; }; +int8_t PSBuzz::getButtonIndex(ButtonEnum b) { + const uint8_t index = legacyButtonValues(b); + if (index > 4) return -1; // 5 buttons, 0-4 inclusive + return index; +} + bool PSBuzz::getButtonPress(ButtonEnum b, uint8_t controller) { - return psbuzzButtons.val & (1UL << (b + 5 * controller)); // Each controller uses 5 bits, so the value is shifted 5 for each controller + const int8_t index = getButtonIndex(b); if (index < 0) return 0; + return psbuzzButtons.val & (1UL << (index + 5 * controller)); // Each controller uses 5 bits, so the value is shifted 5 for each controller }; bool PSBuzz::getButtonClick(ButtonEnum b, uint8_t controller) { - uint32_t mask = (1UL << (b + 5 * controller)); // Each controller uses 5 bits, so the value is shifted 5 for each controller + const int8_t index = getButtonIndex(b); if (index < 0) return 0; + uint32_t mask = (1UL << (index + 5 * controller)); // Each controller uses 5 bits, so the value is shifted 5 for each controller bool click = buttonClickState.val & mask; buttonClickState.val &= ~mask; // Clear "click" event return click; diff --git a/PSBuzz.h b/PSBuzz.h index da83dff0..8f891c35 100644 --- a/PSBuzz.h +++ b/PSBuzz.h @@ -177,6 +177,7 @@ protected: private: void (*pFuncOnInit)(void); // Pointer to function called in onInit() + static int8_t getButtonIndex(ButtonEnum b); void PSBuzz_Command(uint8_t *data, uint16_t nbytes); PSBUZZButtons psbuzzButtons, oldButtonState, buttonClickState; diff --git a/Wii.cpp b/Wii.cpp index 5f79104c..48ff3739 100644 --- a/Wii.cpp +++ b/Wii.cpp @@ -1094,19 +1094,39 @@ void WII::readWiiBalanceBoardCalibration() { /* WII Commands */ /************************************************************/ +int8_t WII::getButtonIndex(ButtonEnum b) { + const uint8_t index = legacyButtonValues(b); + if (index >= sizeof(WII_BUTTONS) / sizeof(WII_BUTTONS[0])) return -1; + return index; +} + +int8_t WII::getButtonIndexPro(ButtonEnum b) { + const uint8_t index = legacyButtonValues(b); + if (index >= sizeof(WII_PROCONTROLLER_BUTTONS) / sizeof(WII_PROCONTROLLER_BUTTONS[0])) return -1; + return index; +} + bool WII::getButtonPress(ButtonEnum b) { // Return true when a button is pressed - if(wiiUProControllerConnected) - return (ButtonState & pgm_read_dword(&WII_PROCONTROLLER_BUTTONS[(uint8_t)b])); - else - return (ButtonState & pgm_read_dword(&WII_BUTTONS[(uint8_t)b])); + if (wiiUProControllerConnected) { + const int8_t index = getButtonIndexPro(b); if (index < 0) return 0; + return (ButtonState & pgm_read_dword(&WII_PROCONTROLLER_BUTTONS[index])); + } + else { + const int8_t index = getButtonIndex(b); if (index < 0) return 0; + return (ButtonState & pgm_read_dword(&WII_BUTTONS[index])); + } } bool WII::getButtonClick(ButtonEnum b) { // Only return true when a button is clicked uint32_t button; - if(wiiUProControllerConnected) - button = pgm_read_dword(&WII_PROCONTROLLER_BUTTONS[(uint8_t)b]); - else - button = pgm_read_dword(&WII_BUTTONS[(uint8_t)b]); + if (wiiUProControllerConnected) { + const int8_t index = getButtonIndexPro(b); if (index < 0) return 0; + button = pgm_read_dword(&WII_PROCONTROLLER_BUTTONS[index]); + } + else { + const int8_t index = getButtonIndex(b); if (index < 0) return 0; + button = pgm_read_dword(&WII_BUTTONS[index]); + } bool click = (ButtonClickState & button); ButtonClickState &= ~button; // clear "click" event return click; diff --git a/Wii.h b/Wii.h index dde886b3..d70b2510 100644 --- a/Wii.h +++ b/Wii.h @@ -431,6 +431,8 @@ protected: /**@}*/ private: + static int8_t getButtonIndex(ButtonEnum b); + static int8_t getButtonIndexPro(ButtonEnum b); void L2CAP_task(); // L2CAP state machine diff --git a/XBOXOLD.cpp b/XBOXOLD.cpp index bcc353b3..440f8e0d 100644 --- a/XBOXOLD.cpp +++ b/XBOXOLD.cpp @@ -20,29 +20,6 @@ //#define EXTRADEBUG // Uncomment to get even more debugging data //#define PRINTREPORT // Uncomment to print the report send by the Xbox controller -/** Buttons on the controllers */ -const uint8_t XBOXOLD_BUTTONS[] PROGMEM = { - 0x01, // UP - 0x08, // RIGHT - 0x02, // DOWN - 0x04, // LEFT - - 0x20, // BACK - 0x10, // START - 0x40, // L3 - 0x80, // R3 - - // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons - 4, // BLACK - 5, // WHTIE - 6, // L1 - 7, // R1 - - 1, // B - 0, // A - 2, // X - 3, // Y -}; XBOXOLD::XBOXOLD(USB *p) : pUsb(p), // pointer to USB class instance - mandatory @@ -292,26 +269,96 @@ void XBOXOLD::printReport(uint16_t length __attribute__((unused))) { //Uncomment #endif } +int8_t XBOXOLD::getAnalogIndex(ButtonEnum b) { + // A, B, X, Y, BLACK, WHITE, L, and R are analog buttons + uint8_t out; + + switch (b) { + case A: + out = 0; break; + case B: + out = 1; break; + case X: + out = 2; break; + case Y: + out = 3; break; + case WHITE: + out = 5; break; + case BLACK: + out = 4; break; + case L: + case L1: + case L2: + out = 6; break; + case R: + case R1: + case R2: + out = 7; break; + default: + out = -1; break; + } + + return out; +} + +int8_t XBOXOLD::getDigitalOffset(ButtonEnum b) { + // UP, DOWN, LEFT, RIGHT, START, BACK, L3, and R3 are digital buttons + // (these are offets for the bitshift) + uint8_t out; + + switch (b) { + case UP: + out = 0; break; + case DOWN: + out = 1; break; + case LEFT: + out = 2; break; + case RIGHT: + out = 3; break; + case START: + out = 4; break; + case BACK: + out = 5; break; + case L3: + out = 6; break; + case R3: + out = 7; break; + default: + out = -1; break; + } + + return out; +} + uint8_t XBOXOLD::getButtonPress(ButtonEnum b) { - uint8_t button = pgm_read_byte(&XBOXOLD_BUTTONS[(uint8_t)b]); - if(b == A || b == B || b == X || b == Y || b == BLACK || b == WHITE || b == L1 || b == R1) // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons - return buttonValues[button]; // Analog buttons - return (ButtonState & button); // Digital buttons + const int8_t analogIndex = getAnalogIndex(b); + if (analogIndex >= 0) { + return buttonValues[analogIndex]; + } + const int8_t digitalOffset = getDigitalOffset(b); + if (digitalOffset >= 0) { + return (ButtonState & (1 << digitalOffset)); + } + return 0; } bool XBOXOLD::getButtonClick(ButtonEnum b) { - uint8_t button = pgm_read_byte(&XBOXOLD_BUTTONS[(uint8_t)b]); - if(b == A || b == B || b == X || b == Y || b == BLACK || b == WHITE || b == L1 || b == R1) { // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons - if(buttonClicked[button]) { - buttonClicked[button] = false; + const int8_t analogIndex = getAnalogIndex(b); + if (analogIndex >= 0) { + if (buttonClicked[analogIndex]) { + buttonClicked[analogIndex] = false; return true; } return false; } - - bool click = (ButtonClickState & button); - ButtonClickState &= ~button; // clear "click" event - return click; + const int8_t digitalOffset = getDigitalOffset(b); + if (digitalOffset >= 0) { + const uint8_t mask = (1 << digitalOffset); + const bool click = (ButtonClickState & mask); + ButtonClickState &= ~mask; + return click; + } + return 0; } int16_t XBOXOLD::getAnalogHat(AnalogHatEnum a) { diff --git a/XBOXOLD.h b/XBOXOLD.h index 6b0757b2..319e435d 100644 --- a/XBOXOLD.h +++ b/XBOXOLD.h @@ -153,6 +153,9 @@ protected: EpInfo epInfo[XBOX_MAX_ENDPOINTS]; private: + static int8_t getAnalogIndex(ButtonEnum b); + static int8_t getDigitalOffset(ButtonEnum b); + /** * Called when the controller is successfully initialized. * Use attachOnInit(void (*funcOnInit)(void)) to call your own function. diff --git a/XBOXONE.cpp b/XBOXONE.cpp index e84be5b4..336618be 100644 --- a/XBOXONE.cpp +++ b/XBOXONE.cpp @@ -331,9 +331,9 @@ void XBOXONE::readReport() { if(readBuf[0] == 0x07) { // The XBOX button has a separate message if(readBuf[4] == 1) - ButtonState |= pgm_read_word(&XBOX_BUTTONS[XBOX]); + ButtonState |= pgm_read_word(&XBOX_BUTTONS[legacyButtonValues(XBOX)]); else - ButtonState &= ~pgm_read_word(&XBOX_BUTTONS[XBOX]); + ButtonState &= ~pgm_read_word(&XBOX_BUTTONS[legacyButtonValues(XBOX)]); if(ButtonState != OldButtonState) { ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable @@ -348,7 +348,7 @@ void XBOXONE::readReport() { return; } - uint16_t xbox = ButtonState & pgm_read_word(&XBOX_BUTTONS[XBOX]); // Since the XBOX button is separate, save it and add it back in + uint16_t xbox = ButtonState & pgm_read_word(&XBOX_BUTTONS[legacyButtonValues(XBOX)]); // Since the XBOX button is separate, save it and add it back in // xbox button from before, dpad, abxy, start/back, sync, stick click, shoulder buttons ButtonState = xbox | (((uint16_t)readBuf[5] & 0xF) << 8) | (readBuf[4] & 0xF0) | (((uint16_t)readBuf[4] & 0x0C) << 10) | ((readBuf[4] & 0x01) << 3) | (((uint16_t)readBuf[5] & 0xC0) << 8) | ((readBuf[5] & 0x30) >> 4); @@ -382,7 +382,8 @@ uint16_t XBOXONE::getButtonPress(ButtonEnum b) { return triggerValue[0]; else if(b == R2) return triggerValue[1]; - return (bool)(ButtonState & ((uint16_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]))); + const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; + return (bool)(ButtonState & ((uint16_t)pgm_read_word(&XBOX_BUTTONS[index]))); } bool XBOXONE::getButtonClick(ButtonEnum b) { @@ -399,7 +400,8 @@ bool XBOXONE::getButtonClick(ButtonEnum b) { } return false; } - uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]); + const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; + uint16_t button = pgm_read_word(&XBOX_BUTTONS[index]); bool click = (ButtonClickState & button); ButtonClickState &= ~button; // Clear "click" event return click; diff --git a/XBOXONESParser.cpp b/XBOXONESParser.cpp index 9e5edd59..fce6d776 100644 --- a/XBOXONESParser.cpp +++ b/XBOXONESParser.cpp @@ -54,6 +54,12 @@ enum DPADEnum { DPAD_LEFT_UP = 0x8, }; +int8_t XBOXONESParser::getButtonIndex(ButtonEnum b) { + const uint8_t index = legacyButtonValues(b); + if (index >= sizeof(XBOX_ONE_S_BUTTONS) / sizeof(XBOX_ONE_S_BUTTONS[0])) return -1; + return index; +} + bool XBOXONESParser::checkDpad(ButtonEnum b) { switch (b) { case UP: @@ -78,7 +84,8 @@ uint16_t XBOXONESParser::getButtonPress(ButtonEnum b) { return checkDpad(b); else if (b == XBOX) return xboxButtonState; - return xboxOneSData.btn.val & (1UL << pgm_read_byte(&XBOX_ONE_S_BUTTONS[(uint8_t)b])); + const int8_t index = getButtonIndex(b); if (index < 0) return 0; + return xboxOneSData.btn.val & (1UL << pgm_read_byte(&XBOX_ONE_S_BUTTONS[index])); } bool XBOXONESParser::getButtonClick(ButtonEnum b) { @@ -99,7 +106,8 @@ bool XBOXONESParser::getButtonClick(ButtonEnum b) { xboxbuttonClickState = 0; // Clear "click" event return click; } - uint32_t mask = 1UL << pgm_read_byte(&XBOX_ONE_S_BUTTONS[(uint8_t)b]); + const int8_t index = getButtonIndex(b); if (index < 0) return 0; + uint32_t mask = 1UL << pgm_read_byte(&XBOX_ONE_S_BUTTONS[index]); bool click = buttonClickState.val & mask; buttonClickState.val &= ~mask; // Clear "click" event return click; diff --git a/XBOXONESParser.h b/XBOXONESParser.h index d031631a..531bb3eb 100644 --- a/XBOXONESParser.h +++ b/XBOXONESParser.h @@ -111,6 +111,8 @@ protected: virtual void sendOutputReport(uint8_t *data, uint8_t nbytes) = 0; private: + static int8_t getButtonIndex(ButtonEnum b); + bool checkDpad(ButtonEnum b); // Used to check Xbox One S DPAD buttons XboxOneSData xboxOneSData; diff --git a/XBOXRECV.cpp b/XBOXRECV.cpp index 031713d4..d00c1fa2 100644 --- a/XBOXRECV.cpp +++ b/XBOXRECV.cpp @@ -412,7 +412,8 @@ uint8_t XBOXRECV::getButtonPress(ButtonEnum b, uint8_t controller) { return (uint8_t)(ButtonState[controller] >> 8); else if(b == R2) return (uint8_t)ButtonState[controller]; - return (bool)(ButtonState[controller] & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]) << 16)); + const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; + return (bool)(ButtonState[controller] & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[index]) << 16)); } bool XBOXRECV::getButtonClick(ButtonEnum b, uint8_t controller) { @@ -429,7 +430,8 @@ bool XBOXRECV::getButtonClick(ButtonEnum b, uint8_t controller) { } return false; } - uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]); + const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; + uint16_t button = pgm_read_word(&XBOX_BUTTONS[index]); bool click = (ButtonClickState[controller] & button); ButtonClickState[controller] &= ~button; // clear "click" event return click; diff --git a/XBOXUSB.cpp b/XBOXUSB.cpp index 6799029d..3749a5ad 100644 --- a/XBOXUSB.cpp +++ b/XBOXUSB.cpp @@ -285,7 +285,8 @@ uint8_t XBOXUSB::getButtonPress(ButtonEnum b) { return (uint8_t)(ButtonState >> 8); else if(b == R2) return (uint8_t)ButtonState; - return (bool)(ButtonState & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]) << 16)); + const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; + return (bool)(ButtonState & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[index]) << 16)); } bool XBOXUSB::getButtonClick(ButtonEnum b) { @@ -302,7 +303,8 @@ bool XBOXUSB::getButtonClick(ButtonEnum b) { } return false; } - uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]); + const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; + uint16_t button = pgm_read_word(&XBOX_BUTTONS[index]); bool click = (ButtonClickState & button); ButtonClickState &= ~button; // clear "click" event return click; diff --git a/controllerEnums.h b/controllerEnums.h index 3b45d5ef..cda84253 100644 --- a/controllerEnums.h +++ b/controllerEnums.h @@ -86,86 +86,169 @@ enum ButtonEnum { /**@{*/ /** Wii buttons */ - PLUS = 5, - TWO = 6, - ONE = 7, - MINUS = 8, - HOME = 9, - Z = 10, - C = 11, - B = 12, - A = 13, + PLUS, + TWO, + ONE, + MINUS, + HOME, + Z, + C, + B, + A, /**@}*/ /**@{*/ /** These are only available on the Wii U Pro Controller */ - L = 16, - R = 17, - ZL = 18, - ZR = 19, + L, + R, + ZL, + ZR, /**@}*/ /**@{*/ /** PS3 controllers buttons */ - SELECT = 4, - START = 5, - L3 = 6, - R3 = 7, + SELECT, + START, + L3, + R3, - L2 = 8, - R2 = 9, - L1 = 10, - R1 = 11, - TRIANGLE = 12, - CIRCLE = 13, - CROSS = 14, - SQUARE = 15, + L2, + R2, + L1, + R1, + TRIANGLE, + CIRCLE, + CROSS, + SQUARE, - PS = 16, + PS, - MOVE = 17, // Covers 12 bits - we only need to read the top 8 - T = 18, // Covers 12 bits - we only need to read the top 8 + MOVE, // Covers 12 bits - we only need to read the top 8 + T, // Covers 12 bits - we only need to read the top 8 /**@}*/ /** PS4 controllers buttons - SHARE and OPTIONS are present instead of SELECT and START */ - SHARE = 4, - OPTIONS = 5, - TOUCHPAD = 17, + SHARE, + OPTIONS, + TOUCHPAD, /**@}*/ /**@{*/ /** Xbox buttons */ - BACK = 4, - X = 14, - Y = 15, - XBOX = 16, - SYNC = 17, - BLACK = 8, // Available on the original Xbox controller - WHITE = 9, // Available on the original Xbox controller + BACK, + X, + Y, + XBOX, + SYNC, + BLACK, // Available on the original Xbox controller + WHITE, // Available on the original Xbox controller /**@}*/ /**@{*/ /** Xbox One S buttons */ - VIEW = 4, - MENU = 5, + VIEW, + MENU, /**@}*/ /**@{*/ /** PS Buzz controllers */ - RED = 0, - YELLOW = 1, - GREEN = 2, - ORANGE = 3, - BLUE = 4, + RED, + YELLOW, + GREEN, + ORANGE, + BLUE, /**@}*/ /**@{*/ /** PS5 buttons */ - CREATE = 4, - MICROPHONE = 18, + CREATE, + MICROPHONE, /**@}*/ }; +inline uint8_t legacyButtonValues(ButtonEnum key) { + uint8_t out; + + switch (key) { + case UP: + case RED: + out = 0; break; + case RIGHT: + case YELLOW: + out = 1; break; + case DOWN: + case GREEN: + out = 2; break; + case LEFT: + case ORANGE: + out = 3; break; + case SELECT: + case SHARE: + case BACK: + case VIEW: + case BLUE: + case CREATE: + out = 4; break; + case PLUS: + case START: + case OPTIONS: + case MENU: + out = 5; break; + case TWO: + case L3: + out = 6; break; + case ONE: + case R3: + out = 7; break; + case MINUS: + case L2: + case BLACK: + out = 8; break; + case HOME: + case R2: + case WHITE: + out = 9; break; + case Z: + case L1: + out = 10; break; + case C: + case R1: + out = 11; break; + case B: + case TRIANGLE: + out = 12; break; + case A: + case CIRCLE: + out = 13; break; + case CROSS: + case X: + out = 14; break; + case SQUARE: + case Y: + out = 15; break; + case L: + case PS: + case XBOX: + out = 16; break; + case R: + case MOVE: + case TOUCHPAD: + out = 17; break; + case SYNC: + out = 17; break; + case ZL: + case T: + case MICROPHONE: + out = 18; break; + case ZR: + out = 19; break; + default: + out = 0; break; + } + + return out; +} + /** Joysticks on the PS3 and Xbox controllers. */ enum AnalogHatEnum { /** Left joystick x-axis */ diff --git a/xboxEnums.h b/xboxEnums.h index 84b137bb..309a3a8d 100644 --- a/xboxEnums.h +++ b/xboxEnums.h @@ -62,4 +62,10 @@ const uint16_t XBOX_BUTTONS[] PROGMEM = { 0x0008, // SYNC }; +inline int8_t getXboxButtonIndex(ButtonEnum b) { + const uint8_t index = legacyButtonValues(b); + if (index >= sizeof(XBOX_BUTTONS) / sizeof(XBOX_BUTTONS[0])) return -1; + return index; +} + #endif From 5753cc661a4a690ec3dd35eaf6d6a5962e1c87c6 Mon Sep 17 00:00:00 2001 From: David Madison Date: Thu, 25 Mar 2021 19:20:05 -0400 Subject: [PATCH 69/93] Reorganized ButtonEnum list Trying to form a more logical order based on how these are used. --- controllerEnums.h | 129 ++++++++++++++++++++++++++-------------------- 1 file changed, 73 insertions(+), 56 deletions(-) diff --git a/controllerEnums.h b/controllerEnums.h index cda84253..22c1043a 100644 --- a/controllerEnums.h +++ b/controllerEnums.h @@ -77,7 +77,7 @@ enum RumbleEnum { /** This enum is used to read all the different buttons on the different controllers */ enum ButtonEnum { /**@{*/ - /** These buttons are available on all the the controllers */ + /** Directional Pad Buttons - available on most controllers */ UP = 0, RIGHT = 1, DOWN = 2, @@ -85,71 +85,32 @@ enum ButtonEnum { /**@}*/ /**@{*/ - /** Wii buttons */ - PLUS, - TWO, - ONE, - MINUS, - HOME, - Z, - C, - B, - A, - /**@}*/ - - /**@{*/ - /** These are only available on the Wii U Pro Controller */ - L, - R, - ZL, - ZR, - /**@}*/ - - /**@{*/ - /** PS3 controllers buttons */ - SELECT, - START, - L3, - R3, - - L2, - R2, - L1, - R1, + /** Playstation buttons */ TRIANGLE, CIRCLE, CROSS, SQUARE, - PS, + SELECT, + START, + L3, + R3, + + L1, + R1, + L2, + R2, + + PS, + /**@}*/ + + /**@{*/ + /** PS3 Move Controller */ MOVE, // Covers 12 bits - we only need to read the top 8 T, // Covers 12 bits - we only need to read the top 8 /**@}*/ - /** PS4 controllers buttons - SHARE and OPTIONS are present instead of SELECT and START */ - SHARE, - OPTIONS, - TOUCHPAD, - /**@}*/ - - /**@{*/ - /** Xbox buttons */ - BACK, - X, - Y, - XBOX, - SYNC, - BLACK, // Available on the original Xbox controller - WHITE, // Available on the original Xbox controller - /**@}*/ - - /**@{*/ - /** Xbox One S buttons */ - VIEW, - MENU, - /**@}*/ - /**@{*/ /** PS Buzz controllers */ RED, @@ -159,11 +120,67 @@ enum ButtonEnum { BLUE, /**@}*/ + /**@{*/ + /** PS4 buttons - SHARE and OPTIONS are present instead of SELECT and START */ + SHARE, + OPTIONS, + TOUCHPAD, + /**@}*/ + /**@{*/ /** PS5 buttons */ CREATE, MICROPHONE, /**@}*/ + + /**@{*/ + /** Xbox buttons */ + A, + B, + X, + Y, + + BACK, + // START, // listed under Playstation buttons + + // L1, // listed under Playstation buttons + // R1, // listed under Playstation buttons + // L2, // listed under Playstation buttons + // R2, // listed under Playstation buttons + + XBOX, + SYNC, + + BLACK, // Available on the original Xbox controller + WHITE, // Available on the original Xbox controller + /**@}*/ + + /**@{*/ + /** Xbox One S buttons */ + VIEW, + MENU, + /**@}*/ + + /**@{*/ + /** Wii buttons */ + PLUS, + TWO, + ONE, + MINUS, + HOME, + Z, + C, + // B, // listed under Xbox buttons + // A, // listed under Xbox buttons + /**@}*/ + + /**@{*/ + /** Wii U Pro Controller */ + L, + R, + ZL, + ZR, + /**@}*/ }; inline uint8_t legacyButtonValues(ButtonEnum key) { From f9a408204d60160963637edf55c88baac721c16d Mon Sep 17 00:00:00 2001 From: David Madison Date: Thu, 25 Mar 2021 20:48:37 -0400 Subject: [PATCH 70/93] Use signed index values for ButtonEnum --- PS3Enums.h | 4 ++-- PS4Parser.cpp | 4 ++-- PS5Parser.cpp | 4 ++-- PSBuzz.cpp | 2 +- Wii.cpp | 8 ++++---- XBOXONESParser.cpp | 4 ++-- controllerEnums.h | 50 +++++++++++++++++++++------------------------- xboxEnums.h | 4 ++-- 8 files changed, 38 insertions(+), 42 deletions(-) diff --git a/PS3Enums.h b/PS3Enums.h index 77c7c536..2c0116e4 100644 --- a/PS3Enums.h +++ b/PS3Enums.h @@ -139,8 +139,8 @@ enum StatusEnum { }; inline int8_t getPS3ButtonIndex(ButtonEnum b) { - const uint8_t index = legacyButtonValues(b); - if (index >= sizeof(PS3_BUTTONS) / sizeof(PS3_BUTTONS[0])) return -1; + const int8_t index = legacyButtonValues(b); + if ((uint8_t) index >= (sizeof(PS3_BUTTONS) / sizeof(PS3_BUTTONS[0]))) return -1; return index; } diff --git a/PS4Parser.cpp b/PS4Parser.cpp index ed619a51..a8353ce5 100644 --- a/PS4Parser.cpp +++ b/PS4Parser.cpp @@ -33,8 +33,8 @@ enum DPADEnum { //#define PRINTREPORT // Uncomment to print the report send by the PS4 Controller int8_t PS4Parser::getButtonIndex(ButtonEnum b) { - const uint8_t index = legacyButtonValues(b); - if (index >= sizeof(PS4_BUTTONS) / sizeof(PS4_BUTTONS[0])) return -1; + const int8_t index = legacyButtonValues(b); + if ((uint8_t) index >= (sizeof(PS4_BUTTONS) / sizeof(PS4_BUTTONS[0]))) return -1; return index; } diff --git a/PS5Parser.cpp b/PS5Parser.cpp index 061ab8fe..b0c6bc0b 100644 --- a/PS5Parser.cpp +++ b/PS5Parser.cpp @@ -37,8 +37,8 @@ enum DPADEnum { //#define PRINTREPORT // Uncomment to print the report send by the PS5 Controller int8_t PS5Parser::getButtonIndex(ButtonEnum b) { - const uint8_t index = legacyButtonValues(b); - if (index >= sizeof(PS5_BUTTONS) / sizeof(PS5_BUTTONS[0])) return -1; + const int8_t index = legacyButtonValues(b); + if ((uint8_t) index >= (sizeof(PS5_BUTTONS) / sizeof(PS5_BUTTONS[0]))) return -1; return index; } diff --git a/PSBuzz.cpp b/PSBuzz.cpp index 0ec452d6..c542c499 100644 --- a/PSBuzz.cpp +++ b/PSBuzz.cpp @@ -50,7 +50,7 @@ uint8_t PSBuzz::OnInitSuccessful() { }; int8_t PSBuzz::getButtonIndex(ButtonEnum b) { - const uint8_t index = legacyButtonValues(b); + const int8_t index = legacyButtonValues(b); if (index > 4) return -1; // 5 buttons, 0-4 inclusive return index; } diff --git a/Wii.cpp b/Wii.cpp index 48ff3739..6073f250 100644 --- a/Wii.cpp +++ b/Wii.cpp @@ -1095,14 +1095,14 @@ void WII::readWiiBalanceBoardCalibration() { /************************************************************/ int8_t WII::getButtonIndex(ButtonEnum b) { - const uint8_t index = legacyButtonValues(b); - if (index >= sizeof(WII_BUTTONS) / sizeof(WII_BUTTONS[0])) return -1; + const int8_t index = legacyButtonValues(b); + if ((uint8_t) index >= (sizeof(WII_BUTTONS) / sizeof(WII_BUTTONS[0]))) return -1; return index; } int8_t WII::getButtonIndexPro(ButtonEnum b) { - const uint8_t index = legacyButtonValues(b); - if (index >= sizeof(WII_PROCONTROLLER_BUTTONS) / sizeof(WII_PROCONTROLLER_BUTTONS[0])) return -1; + const int8_t index = legacyButtonValues(b); + if ((uint8_t) index >= (sizeof(WII_PROCONTROLLER_BUTTONS) / sizeof(WII_PROCONTROLLER_BUTTONS[0]))) return -1; return index; } diff --git a/XBOXONESParser.cpp b/XBOXONESParser.cpp index fce6d776..4c9c23e4 100644 --- a/XBOXONESParser.cpp +++ b/XBOXONESParser.cpp @@ -55,8 +55,8 @@ enum DPADEnum { }; int8_t XBOXONESParser::getButtonIndex(ButtonEnum b) { - const uint8_t index = legacyButtonValues(b); - if (index >= sizeof(XBOX_ONE_S_BUTTONS) / sizeof(XBOX_ONE_S_BUTTONS[0])) return -1; + const int8_t index = legacyButtonValues(b); + if ((uint8_t) index >= (sizeof(XBOX_ONE_S_BUTTONS) / sizeof(XBOX_ONE_S_BUTTONS[0]))) return -1; return index; } diff --git a/controllerEnums.h b/controllerEnums.h index 22c1043a..f2df387d 100644 --- a/controllerEnums.h +++ b/controllerEnums.h @@ -183,87 +183,83 @@ enum ButtonEnum { /**@}*/ }; -inline uint8_t legacyButtonValues(ButtonEnum key) { - uint8_t out; - +inline int8_t legacyButtonValues(ButtonEnum key) { switch (key) { case UP: case RED: - out = 0; break; + return 0; case RIGHT: case YELLOW: - out = 1; break; + return 1; case DOWN: case GREEN: - out = 2; break; + return 2; case LEFT: case ORANGE: - out = 3; break; + return 3; case SELECT: case SHARE: case BACK: case VIEW: case BLUE: case CREATE: - out = 4; break; + return 4; case PLUS: case START: case OPTIONS: case MENU: - out = 5; break; + return 5; case TWO: case L3: - out = 6; break; + return 6; case ONE: case R3: - out = 7; break; + return 7; case MINUS: case L2: case BLACK: - out = 8; break; + return 8; case HOME: case R2: case WHITE: - out = 9; break; + return 9; case Z: case L1: - out = 10; break; + return 10; case C: case R1: - out = 11; break; + return 11; case B: case TRIANGLE: - out = 12; break; + return 12; case A: case CIRCLE: - out = 13; break; + return 13; case CROSS: case X: - out = 14; break; + return 14; case SQUARE: case Y: - out = 15; break; + return 15; case L: case PS: case XBOX: - out = 16; break; + return 16; case R: case MOVE: case TOUCHPAD: - out = 17; break; case SYNC: - out = 17; break; + return 17; case ZL: case T: case MICROPHONE: - out = 18; break; + return 18; case ZR: - out = 19; break; + return 19; default: - out = 0; break; + return -1; } - - return out; + return -1; } /** Joysticks on the PS3 and Xbox controllers. */ diff --git a/xboxEnums.h b/xboxEnums.h index 309a3a8d..037be594 100644 --- a/xboxEnums.h +++ b/xboxEnums.h @@ -63,8 +63,8 @@ const uint16_t XBOX_BUTTONS[] PROGMEM = { }; inline int8_t getXboxButtonIndex(ButtonEnum b) { - const uint8_t index = legacyButtonValues(b); - if (index >= sizeof(XBOX_BUTTONS) / sizeof(XBOX_BUTTONS[0])) return -1; + const int8_t index = legacyButtonValues(b); + if ((uint8_t) index >= (sizeof(XBOX_BUTTONS) / sizeof(XBOX_BUTTONS[0]))) return -1; return index; } From 57afaefd2ccc5c2c6577f37994c5033af903435b Mon Sep 17 00:00:00 2001 From: David Madison Date: Sat, 27 Mar 2021 06:07:42 -0400 Subject: [PATCH 71/93] Check direct enum comparisons by index To not break support for users who used the "wrong" control enumeration for that controller, but had their program work because the "wrong" enum used the same ID. --- PS4Parser.cpp | 5 +++-- PS5Parser.cpp | 5 +++-- XBOXONE.cpp | 14 +++++++------- XBOXONESParser.cpp | 24 ++++++++++++------------ XBOXRECV.cpp | 14 +++++++------- XBOXUSB.cpp | 14 +++++++------- 6 files changed, 39 insertions(+), 37 deletions(-) diff --git a/PS4Parser.cpp b/PS4Parser.cpp index a8353ce5..aee04d2c 100644 --- a/PS4Parser.cpp +++ b/PS4Parser.cpp @@ -70,9 +70,10 @@ bool PS4Parser::getButtonClick(ButtonEnum b) { } uint8_t PS4Parser::getAnalogButton(ButtonEnum b) { - if (b == L2) // These are the only analog buttons on the controller + const int8_t index = getButtonIndex(b); if (index < 0) return 0; + if (index == legacyButtonValues(L2)) // These are the only analog buttons on the controller return ps4Data.trigger[0]; - else if (b == R2) + else if (index == legacyButtonValues(R2)) return ps4Data.trigger[1]; return 0; } diff --git a/PS5Parser.cpp b/PS5Parser.cpp index b0c6bc0b..8d53997c 100644 --- a/PS5Parser.cpp +++ b/PS5Parser.cpp @@ -74,9 +74,10 @@ bool PS5Parser::getButtonClick(ButtonEnum b) { } uint8_t PS5Parser::getAnalogButton(ButtonEnum b) { - if (b == L2) // These are the only analog buttons on the controller + const int8_t index = getButtonIndex(b); if (index < 0) return 0; + if (index == legacyButtonValues(L2)) // These are the only analog buttons on the controller return ps5Data.trigger[0]; - else if (b == R2) + else if (index == legacyButtonValues(R2)) return ps5Data.trigger[1]; return 0; } diff --git a/XBOXONE.cpp b/XBOXONE.cpp index 336618be..7345c734 100644 --- a/XBOXONE.cpp +++ b/XBOXONE.cpp @@ -378,29 +378,29 @@ void XBOXONE::readReport() { } uint16_t XBOXONE::getButtonPress(ButtonEnum b) { - if(b == L2) // These are analog buttons - return triggerValue[0]; - else if(b == R2) - return triggerValue[1]; const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; + if(index == legacyButtonValues(L2)) // These are analog buttons + return triggerValue[0]; + else if(index == legacyButtonValues(R2)) + return triggerValue[1]; return (bool)(ButtonState & ((uint16_t)pgm_read_word(&XBOX_BUTTONS[index]))); } bool XBOXONE::getButtonClick(ButtonEnum b) { - if(b == L2) { + const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; + if(index == legacyButtonValues(L2)) { if(L2Clicked) { L2Clicked = false; return true; } return false; - } else if(b == R2) { + } else if(index == legacyButtonValues(R2)) { if(R2Clicked) { R2Clicked = false; return true; } return false; } - const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; uint16_t button = pgm_read_word(&XBOX_BUTTONS[index]); bool click = (ButtonClickState & button); ButtonClickState &= ~button; // Clear "click" event diff --git a/XBOXONESParser.cpp b/XBOXONESParser.cpp index 4c9c23e4..b92bbeb7 100644 --- a/XBOXONESParser.cpp +++ b/XBOXONESParser.cpp @@ -76,37 +76,37 @@ bool XBOXONESParser::checkDpad(ButtonEnum b) { } 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; const int8_t index = getButtonIndex(b); if (index < 0) return 0; + if (index == legacyButtonValues(L2)) + return xboxOneSData.trigger[0]; + else if (index == legacyButtonValues(R2)) + return xboxOneSData.trigger[1]; + else if (index <= LEFT) // Dpad + return checkDpad(b); + else if (index == legacyButtonValues(XBOX)) + return xboxButtonState; return xboxOneSData.btn.val & (1UL << pgm_read_byte(&XBOX_ONE_S_BUTTONS[index])); } bool XBOXONESParser::getButtonClick(ButtonEnum b) { - if(b == L2) { + const int8_t index = getButtonIndex(b); if (index < 0) return 0; + if(index == legacyButtonValues(L2)) { if(L2Clicked) { L2Clicked = false; return true; } return false; - } else if(b == R2) { + } else if(index == legacyButtonValues(R2)) { if(R2Clicked) { R2Clicked = false; return true; } return false; - } else if (b == XBOX) { + } else if (index == legacyButtonValues(XBOX)) { bool click = xboxbuttonClickState; xboxbuttonClickState = 0; // Clear "click" event return click; } - const int8_t index = getButtonIndex(b); if (index < 0) return 0; uint32_t mask = 1UL << pgm_read_byte(&XBOX_ONE_S_BUTTONS[index]); bool click = buttonClickState.val & mask; buttonClickState.val &= ~mask; // Clear "click" event diff --git a/XBOXRECV.cpp b/XBOXRECV.cpp index d00c1fa2..1a7ac318 100644 --- a/XBOXRECV.cpp +++ b/XBOXRECV.cpp @@ -408,29 +408,29 @@ void XBOXRECV::printReport(uint8_t controller __attribute__((unused)), uint8_t n } uint8_t XBOXRECV::getButtonPress(ButtonEnum b, uint8_t controller) { - if(b == L2) // These are analog buttons - return (uint8_t)(ButtonState[controller] >> 8); - else if(b == R2) - return (uint8_t)ButtonState[controller]; const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; + if(index == legacyButtonValues(L2)) // These are analog buttons + return (uint8_t)(ButtonState[controller] >> 8); + else if(index == legacyButtonValues(R2)) + return (uint8_t)ButtonState[controller]; return (bool)(ButtonState[controller] & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[index]) << 16)); } bool XBOXRECV::getButtonClick(ButtonEnum b, uint8_t controller) { - if(b == L2) { + const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; + if(index == legacyButtonValues(L2)) { if(L2Clicked[controller]) { L2Clicked[controller] = false; return true; } return false; - } else if(b == R2) { + } else if(index == legacyButtonValues(R2)) { if(R2Clicked[controller]) { R2Clicked[controller] = false; return true; } return false; } - const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; uint16_t button = pgm_read_word(&XBOX_BUTTONS[index]); bool click = (ButtonClickState[controller] & button); ButtonClickState[controller] &= ~button; // clear "click" event diff --git a/XBOXUSB.cpp b/XBOXUSB.cpp index 3749a5ad..6b44299e 100644 --- a/XBOXUSB.cpp +++ b/XBOXUSB.cpp @@ -281,29 +281,29 @@ void XBOXUSB::printReport() { //Uncomment "#define PRINTREPORT" to print the rep } uint8_t XBOXUSB::getButtonPress(ButtonEnum b) { - if(b == L2) // These are analog buttons - return (uint8_t)(ButtonState >> 8); - else if(b == R2) - return (uint8_t)ButtonState; const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; + if(index == legacyButtonValues(L2)) // These are analog buttons + return (uint8_t)(ButtonState >> 8); + else if(index == legacyButtonValues(R2)) + return (uint8_t)ButtonState; return (bool)(ButtonState & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[index]) << 16)); } bool XBOXUSB::getButtonClick(ButtonEnum b) { - if(b == L2) { + const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; + if(index == legacyButtonValues(L2)) { if(L2Clicked) { L2Clicked = false; return true; } return false; - } else if(b == R2) { + } else if(index == legacyButtonValues(R2)) { if(R2Clicked) { R2Clicked = false; return true; } return false; } - const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; uint16_t button = pgm_read_word(&XBOX_BUTTONS[index]); bool click = (ButtonClickState & button); ButtonClickState &= ~button; // clear "click" event From 2fe5574e71898446e8a6653541e0f532ed56876b Mon Sep 17 00:00:00 2001 From: David Madison Date: Sat, 27 Mar 2021 07:20:55 -0400 Subject: [PATCH 72/93] Change button values function to constexpr Requires a chained ternary in place of a switch for compatibility with older compilers. --- controllerEnums.h | 101 +++++++++++----------------------------------- 1 file changed, 24 insertions(+), 77 deletions(-) diff --git a/controllerEnums.h b/controllerEnums.h index f2df387d..bfffb83d 100644 --- a/controllerEnums.h +++ b/controllerEnums.h @@ -183,83 +183,30 @@ enum ButtonEnum { /**@}*/ }; -inline int8_t legacyButtonValues(ButtonEnum key) { - switch (key) { - case UP: - case RED: - return 0; - case RIGHT: - case YELLOW: - return 1; - case DOWN: - case GREEN: - return 2; - case LEFT: - case ORANGE: - return 3; - case SELECT: - case SHARE: - case BACK: - case VIEW: - case BLUE: - case CREATE: - return 4; - case PLUS: - case START: - case OPTIONS: - case MENU: - return 5; - case TWO: - case L3: - return 6; - case ONE: - case R3: - return 7; - case MINUS: - case L2: - case BLACK: - return 8; - case HOME: - case R2: - case WHITE: - return 9; - case Z: - case L1: - return 10; - case C: - case R1: - return 11; - case B: - case TRIANGLE: - return 12; - case A: - case CIRCLE: - return 13; - case CROSS: - case X: - return 14; - case SQUARE: - case Y: - return 15; - case L: - case PS: - case XBOX: - return 16; - case R: - case MOVE: - case TOUCHPAD: - case SYNC: - return 17; - case ZL: - case T: - case MICROPHONE: - return 18; - case ZR: - return 19; - default: - return -1; - } - return -1; +inline constexpr int8_t legacyButtonValues(ButtonEnum key) { + // using a chained ternary in place of a switch for constexpr on older compilers + return + (key == UP || key == RED) ? 0 : + (key == RIGHT || key == YELLOW) ? 1 : + (key == DOWN || key == GREEN) ? 2 : + (key == LEFT || key == ORANGE) ? 3 : + (key == SELECT || key == SHARE || key == BACK || key == VIEW || key == BLUE || key == CREATE) ? 4 : + (key == PLUS || key == START || key == OPTIONS || key == MENU) ? 5 : + (key == TWO || key == L3) ? 6 : + (key == ONE || key == R3) ? 7 : + (key == MINUS || key == L2 || key == BLACK) ? 8 : + (key == HOME || key == R2 || key == WHITE) ? 9 : + (key == Z || key == L1) ? 10 : + (key == C || key == R1) ? 11 : + (key == B || key == TRIANGLE) ? 12 : + (key == A || key == CIRCLE) ? 13 : + (key == CROSS || key == X) ? 14 : + (key == SQUARE || key == Y) ? 15 : + (key == L || key == PS || key == XBOX) ? 16 : + (key == R || key == MOVE || key == TOUCHPAD || key == SYNC) ? 17 : + (key == ZL || key == T || key == MICROPHONE) ? 18 : + (key == ZR) ? 19 : + -1; // not a match } /** Joysticks on the PS3 and Xbox controllers. */ From 89575639b103dd6f5edbbe393a5c60e676e23d22 Mon Sep 17 00:00:00 2001 From: David Madison Date: Sat, 27 Mar 2021 07:22:55 -0400 Subject: [PATCH 73/93] Change button index 'get' functions to constexpr --- PS3Enums.h | 6 ++---- PS4Parser.cpp | 6 ++---- PS4Parser.h | 2 +- PS5Parser.cpp | 6 ++---- PS5Parser.h | 2 +- Wii.cpp | 12 ++++-------- Wii.h | 4 ++-- XBOXONESParser.cpp | 6 ++---- XBOXONESParser.h | 2 +- xboxEnums.h | 6 ++---- 10 files changed, 19 insertions(+), 33 deletions(-) diff --git a/PS3Enums.h b/PS3Enums.h index 2c0116e4..08d6ae0b 100644 --- a/PS3Enums.h +++ b/PS3Enums.h @@ -138,10 +138,8 @@ enum StatusEnum { Bluetooth = (40 << 8) | 0x16, // Operating by Bluetooth and rumble is turned off }; -inline int8_t getPS3ButtonIndex(ButtonEnum b) { - const int8_t index = legacyButtonValues(b); - if ((uint8_t) index >= (sizeof(PS3_BUTTONS) / sizeof(PS3_BUTTONS[0]))) return -1; - return index; +inline constexpr int8_t getPS3ButtonIndex(ButtonEnum b) { + return (uint8_t) legacyButtonValues(b) >= (sizeof(PS3_BUTTONS) / sizeof(PS3_BUTTONS[0])) ? -1 : legacyButtonValues(b); } #endif diff --git a/PS4Parser.cpp b/PS4Parser.cpp index aee04d2c..ff685293 100644 --- a/PS4Parser.cpp +++ b/PS4Parser.cpp @@ -32,10 +32,8 @@ enum DPADEnum { // To enable serial debugging see "settings.h" //#define PRINTREPORT // Uncomment to print the report send by the PS4 Controller -int8_t PS4Parser::getButtonIndex(ButtonEnum b) { - const int8_t index = legacyButtonValues(b); - if ((uint8_t) index >= (sizeof(PS4_BUTTONS) / sizeof(PS4_BUTTONS[0]))) return -1; - return index; +inline constexpr int8_t PS4Parser::getButtonIndex(ButtonEnum b) { + return (uint8_t) legacyButtonValues(b) >= (sizeof(PS4_BUTTONS) / sizeof(PS4_BUTTONS[0])) ? -1 : legacyButtonValues(b); } bool PS4Parser::checkDpad(ButtonEnum b) { diff --git a/PS4Parser.h b/PS4Parser.h index 4d6088ba..bd5d9d6f 100644 --- a/PS4Parser.h +++ b/PS4Parser.h @@ -362,7 +362,7 @@ protected: virtual void sendOutputReport(PS4Output *output) = 0; private: - static int8_t getButtonIndex(ButtonEnum b); + static inline constexpr int8_t getButtonIndex(ButtonEnum b); bool checkDpad(ButtonEnum b); // Used to check PS4 DPAD buttons PS4Data ps4Data; diff --git a/PS5Parser.cpp b/PS5Parser.cpp index 8d53997c..a29b7486 100644 --- a/PS5Parser.cpp +++ b/PS5Parser.cpp @@ -36,10 +36,8 @@ enum DPADEnum { // To enable serial debugging see "settings.h" //#define PRINTREPORT // Uncomment to print the report send by the PS5 Controller -int8_t PS5Parser::getButtonIndex(ButtonEnum b) { - const int8_t index = legacyButtonValues(b); - if ((uint8_t) index >= (sizeof(PS5_BUTTONS) / sizeof(PS5_BUTTONS[0]))) return -1; - return index; +inline constexpr int8_t PS5Parser::getButtonIndex(ButtonEnum b) { + return (uint8_t) legacyButtonValues(b) >= (sizeof(PS5_BUTTONS) / sizeof(PS5_BUTTONS[0])) ? -1 : legacyButtonValues(b); } bool PS5Parser::checkDpad(ButtonEnum b) { diff --git a/PS5Parser.h b/PS5Parser.h index 7c0c29c9..121f710f 100644 --- a/PS5Parser.h +++ b/PS5Parser.h @@ -403,7 +403,7 @@ protected: private: - static int8_t getButtonIndex(ButtonEnum b); + static inline constexpr int8_t getButtonIndex(ButtonEnum b); bool checkDpad(ButtonEnum b); // Used to check PS5 DPAD buttons PS5Data ps5Data; diff --git a/Wii.cpp b/Wii.cpp index 6073f250..0a69de69 100644 --- a/Wii.cpp +++ b/Wii.cpp @@ -1094,16 +1094,12 @@ void WII::readWiiBalanceBoardCalibration() { /* WII Commands */ /************************************************************/ -int8_t WII::getButtonIndex(ButtonEnum b) { - const int8_t index = legacyButtonValues(b); - if ((uint8_t) index >= (sizeof(WII_BUTTONS) / sizeof(WII_BUTTONS[0]))) return -1; - return index; +inline constexpr int8_t WII::getButtonIndex(ButtonEnum b) { + return (uint8_t)legacyButtonValues(b) >= (sizeof(WII_BUTTONS) / sizeof(WII_BUTTONS[0])) ? -1 : legacyButtonValues(b); } -int8_t WII::getButtonIndexPro(ButtonEnum b) { - const int8_t index = legacyButtonValues(b); - if ((uint8_t) index >= (sizeof(WII_PROCONTROLLER_BUTTONS) / sizeof(WII_PROCONTROLLER_BUTTONS[0]))) return -1; - return index; +inline constexpr int8_t WII::getButtonIndexPro(ButtonEnum b) { + return (uint8_t)legacyButtonValues(b) >= (sizeof(WII_PROCONTROLLER_BUTTONS) / sizeof(WII_PROCONTROLLER_BUTTONS[0])) ? -1 : legacyButtonValues(b); } bool WII::getButtonPress(ButtonEnum b) { // Return true when a button is pressed diff --git a/Wii.h b/Wii.h index d70b2510..343edf18 100644 --- a/Wii.h +++ b/Wii.h @@ -431,8 +431,8 @@ protected: /**@}*/ private: - static int8_t getButtonIndex(ButtonEnum b); - static int8_t getButtonIndexPro(ButtonEnum b); + static inline constexpr int8_t getButtonIndex(ButtonEnum b); + static inline constexpr int8_t getButtonIndexPro(ButtonEnum b); void L2CAP_task(); // L2CAP state machine diff --git a/XBOXONESParser.cpp b/XBOXONESParser.cpp index b92bbeb7..20e25833 100644 --- a/XBOXONESParser.cpp +++ b/XBOXONESParser.cpp @@ -54,10 +54,8 @@ enum DPADEnum { DPAD_LEFT_UP = 0x8, }; -int8_t XBOXONESParser::getButtonIndex(ButtonEnum b) { - const int8_t index = legacyButtonValues(b); - if ((uint8_t) index >= (sizeof(XBOX_ONE_S_BUTTONS) / sizeof(XBOX_ONE_S_BUTTONS[0]))) return -1; - return index; +inline constexpr int8_t XBOXONESParser::getButtonIndex(ButtonEnum b) { + return (uint8_t) legacyButtonValues(b) >= (sizeof(XBOX_ONE_S_BUTTONS) / sizeof(XBOX_ONE_S_BUTTONS[0])) ? -1 : legacyButtonValues(b); } bool XBOXONESParser::checkDpad(ButtonEnum b) { diff --git a/XBOXONESParser.h b/XBOXONESParser.h index 531bb3eb..67c3c34d 100644 --- a/XBOXONESParser.h +++ b/XBOXONESParser.h @@ -111,7 +111,7 @@ protected: virtual void sendOutputReport(uint8_t *data, uint8_t nbytes) = 0; private: - static int8_t getButtonIndex(ButtonEnum b); + static inline constexpr int8_t getButtonIndex(ButtonEnum b); bool checkDpad(ButtonEnum b); // Used to check Xbox One S DPAD buttons diff --git a/xboxEnums.h b/xboxEnums.h index 037be594..791f0cd4 100644 --- a/xboxEnums.h +++ b/xboxEnums.h @@ -62,10 +62,8 @@ const uint16_t XBOX_BUTTONS[] PROGMEM = { 0x0008, // SYNC }; -inline int8_t getXboxButtonIndex(ButtonEnum b) { - const int8_t index = legacyButtonValues(b); - if ((uint8_t) index >= (sizeof(XBOX_BUTTONS) / sizeof(XBOX_BUTTONS[0]))) return -1; - return index; +inline constexpr int8_t getXboxButtonIndex(ButtonEnum b) { + return (uint8_t) legacyButtonValues(b) >= (sizeof(XBOX_BUTTONS) / sizeof(XBOX_BUTTONS[0])) ? -1 : legacyButtonValues(b); } #endif From 6e0748d0972fb23851d3e44d6cf994ff1200aa68 Mon Sep 17 00:00:00 2001 From: David Madison Date: Sat, 27 Mar 2021 07:22:57 -0400 Subject: [PATCH 74/93] Revert "Change button index 'get' functions to constexpr" On second thought, reverting this so that in the case that the function is *not* evaluated at compile-time, the button values function is not called twice. --- PS3Enums.h | 6 ++++-- PS4Parser.cpp | 6 ++++-- PS4Parser.h | 2 +- PS5Parser.cpp | 6 ++++-- PS5Parser.h | 2 +- Wii.cpp | 12 ++++++++---- Wii.h | 4 ++-- XBOXONESParser.cpp | 6 ++++-- XBOXONESParser.h | 2 +- xboxEnums.h | 6 ++++-- 10 files changed, 33 insertions(+), 19 deletions(-) diff --git a/PS3Enums.h b/PS3Enums.h index 08d6ae0b..2c0116e4 100644 --- a/PS3Enums.h +++ b/PS3Enums.h @@ -138,8 +138,10 @@ enum StatusEnum { Bluetooth = (40 << 8) | 0x16, // Operating by Bluetooth and rumble is turned off }; -inline constexpr int8_t getPS3ButtonIndex(ButtonEnum b) { - return (uint8_t) legacyButtonValues(b) >= (sizeof(PS3_BUTTONS) / sizeof(PS3_BUTTONS[0])) ? -1 : legacyButtonValues(b); +inline int8_t getPS3ButtonIndex(ButtonEnum b) { + const int8_t index = legacyButtonValues(b); + if ((uint8_t) index >= (sizeof(PS3_BUTTONS) / sizeof(PS3_BUTTONS[0]))) return -1; + return index; } #endif diff --git a/PS4Parser.cpp b/PS4Parser.cpp index ff685293..aee04d2c 100644 --- a/PS4Parser.cpp +++ b/PS4Parser.cpp @@ -32,8 +32,10 @@ enum DPADEnum { // To enable serial debugging see "settings.h" //#define PRINTREPORT // Uncomment to print the report send by the PS4 Controller -inline constexpr int8_t PS4Parser::getButtonIndex(ButtonEnum b) { - return (uint8_t) legacyButtonValues(b) >= (sizeof(PS4_BUTTONS) / sizeof(PS4_BUTTONS[0])) ? -1 : legacyButtonValues(b); +int8_t PS4Parser::getButtonIndex(ButtonEnum b) { + const int8_t index = legacyButtonValues(b); + if ((uint8_t) index >= (sizeof(PS4_BUTTONS) / sizeof(PS4_BUTTONS[0]))) return -1; + return index; } bool PS4Parser::checkDpad(ButtonEnum b) { diff --git a/PS4Parser.h b/PS4Parser.h index bd5d9d6f..4d6088ba 100644 --- a/PS4Parser.h +++ b/PS4Parser.h @@ -362,7 +362,7 @@ protected: virtual void sendOutputReport(PS4Output *output) = 0; private: - static inline constexpr int8_t getButtonIndex(ButtonEnum b); + static int8_t getButtonIndex(ButtonEnum b); bool checkDpad(ButtonEnum b); // Used to check PS4 DPAD buttons PS4Data ps4Data; diff --git a/PS5Parser.cpp b/PS5Parser.cpp index a29b7486..8d53997c 100644 --- a/PS5Parser.cpp +++ b/PS5Parser.cpp @@ -36,8 +36,10 @@ enum DPADEnum { // To enable serial debugging see "settings.h" //#define PRINTREPORT // Uncomment to print the report send by the PS5 Controller -inline constexpr int8_t PS5Parser::getButtonIndex(ButtonEnum b) { - return (uint8_t) legacyButtonValues(b) >= (sizeof(PS5_BUTTONS) / sizeof(PS5_BUTTONS[0])) ? -1 : legacyButtonValues(b); +int8_t PS5Parser::getButtonIndex(ButtonEnum b) { + const int8_t index = legacyButtonValues(b); + if ((uint8_t) index >= (sizeof(PS5_BUTTONS) / sizeof(PS5_BUTTONS[0]))) return -1; + return index; } bool PS5Parser::checkDpad(ButtonEnum b) { diff --git a/PS5Parser.h b/PS5Parser.h index 121f710f..7c0c29c9 100644 --- a/PS5Parser.h +++ b/PS5Parser.h @@ -403,7 +403,7 @@ protected: private: - static inline constexpr int8_t getButtonIndex(ButtonEnum b); + static int8_t getButtonIndex(ButtonEnum b); bool checkDpad(ButtonEnum b); // Used to check PS5 DPAD buttons PS5Data ps5Data; diff --git a/Wii.cpp b/Wii.cpp index 0a69de69..6073f250 100644 --- a/Wii.cpp +++ b/Wii.cpp @@ -1094,12 +1094,16 @@ void WII::readWiiBalanceBoardCalibration() { /* WII Commands */ /************************************************************/ -inline constexpr int8_t WII::getButtonIndex(ButtonEnum b) { - return (uint8_t)legacyButtonValues(b) >= (sizeof(WII_BUTTONS) / sizeof(WII_BUTTONS[0])) ? -1 : legacyButtonValues(b); +int8_t WII::getButtonIndex(ButtonEnum b) { + const int8_t index = legacyButtonValues(b); + if ((uint8_t) index >= (sizeof(WII_BUTTONS) / sizeof(WII_BUTTONS[0]))) return -1; + return index; } -inline constexpr int8_t WII::getButtonIndexPro(ButtonEnum b) { - return (uint8_t)legacyButtonValues(b) >= (sizeof(WII_PROCONTROLLER_BUTTONS) / sizeof(WII_PROCONTROLLER_BUTTONS[0])) ? -1 : legacyButtonValues(b); +int8_t WII::getButtonIndexPro(ButtonEnum b) { + const int8_t index = legacyButtonValues(b); + if ((uint8_t) index >= (sizeof(WII_PROCONTROLLER_BUTTONS) / sizeof(WII_PROCONTROLLER_BUTTONS[0]))) return -1; + return index; } bool WII::getButtonPress(ButtonEnum b) { // Return true when a button is pressed diff --git a/Wii.h b/Wii.h index 343edf18..d70b2510 100644 --- a/Wii.h +++ b/Wii.h @@ -431,8 +431,8 @@ protected: /**@}*/ private: - static inline constexpr int8_t getButtonIndex(ButtonEnum b); - static inline constexpr int8_t getButtonIndexPro(ButtonEnum b); + static int8_t getButtonIndex(ButtonEnum b); + static int8_t getButtonIndexPro(ButtonEnum b); void L2CAP_task(); // L2CAP state machine diff --git a/XBOXONESParser.cpp b/XBOXONESParser.cpp index 20e25833..b92bbeb7 100644 --- a/XBOXONESParser.cpp +++ b/XBOXONESParser.cpp @@ -54,8 +54,10 @@ enum DPADEnum { DPAD_LEFT_UP = 0x8, }; -inline constexpr int8_t XBOXONESParser::getButtonIndex(ButtonEnum b) { - return (uint8_t) legacyButtonValues(b) >= (sizeof(XBOX_ONE_S_BUTTONS) / sizeof(XBOX_ONE_S_BUTTONS[0])) ? -1 : legacyButtonValues(b); +int8_t XBOXONESParser::getButtonIndex(ButtonEnum b) { + const int8_t index = legacyButtonValues(b); + if ((uint8_t) index >= (sizeof(XBOX_ONE_S_BUTTONS) / sizeof(XBOX_ONE_S_BUTTONS[0]))) return -1; + return index; } bool XBOXONESParser::checkDpad(ButtonEnum b) { diff --git a/XBOXONESParser.h b/XBOXONESParser.h index 67c3c34d..531bb3eb 100644 --- a/XBOXONESParser.h +++ b/XBOXONESParser.h @@ -111,7 +111,7 @@ protected: virtual void sendOutputReport(uint8_t *data, uint8_t nbytes) = 0; private: - static inline constexpr int8_t getButtonIndex(ButtonEnum b); + static int8_t getButtonIndex(ButtonEnum b); bool checkDpad(ButtonEnum b); // Used to check Xbox One S DPAD buttons diff --git a/xboxEnums.h b/xboxEnums.h index 791f0cd4..037be594 100644 --- a/xboxEnums.h +++ b/xboxEnums.h @@ -62,8 +62,10 @@ const uint16_t XBOX_BUTTONS[] PROGMEM = { 0x0008, // SYNC }; -inline constexpr int8_t getXboxButtonIndex(ButtonEnum b) { - return (uint8_t) legacyButtonValues(b) >= (sizeof(XBOX_BUTTONS) / sizeof(XBOX_BUTTONS[0])) ? -1 : legacyButtonValues(b); +inline int8_t getXboxButtonIndex(ButtonEnum b) { + const int8_t index = legacyButtonValues(b); + if ((uint8_t) index >= (sizeof(XBOX_BUTTONS) / sizeof(XBOX_BUTTONS[0]))) return -1; + return index; } #endif From 2120adf2830b5a0d6a6c3d15599d9189b6c121ef Mon Sep 17 00:00:00 2001 From: David Madison Date: Sat, 27 Mar 2021 08:35:55 -0400 Subject: [PATCH 75/93] Refactor button index function names It's not really fair to refer to the main index function as "legacy" if it's actively being used. All controller-specific 'get' filtering functions are now appended with the name of the controller rather than having it in the middle. --- PS3BT.cpp | 6 +++--- PS3Enums.h | 4 ++-- PS3USB.cpp | 6 +++--- PS4Parser.cpp | 14 +++++++------- PS4Parser.h | 2 +- PS5Parser.cpp | 14 +++++++------- PS5Parser.h | 2 +- PSBuzz.cpp | 8 ++++---- PSBuzz.h | 3 ++- Wii.cpp | 16 ++++++++-------- Wii.h | 4 ++-- XBOXONE.cpp | 18 +++++++++--------- XBOXONESParser.cpp | 20 ++++++++++---------- XBOXONESParser.h | 2 +- XBOXRECV.cpp | 12 ++++++------ XBOXUSB.cpp | 12 ++++++------ controllerEnums.h | 2 +- xboxEnums.h | 4 ++-- 18 files changed, 75 insertions(+), 74 deletions(-) diff --git a/PS3BT.cpp b/PS3BT.cpp index 3326f1f6..8c181f45 100644 --- a/PS3BT.cpp +++ b/PS3BT.cpp @@ -47,12 +47,12 @@ BluetoothService(p) // Pointer to USB class instance - mandatory } bool PS3BT::getButtonPress(ButtonEnum b) { - const int8_t index = getPS3ButtonIndex(b); if (index < 0) return 0; + const int8_t index = getButtonIndexPS3(b); if (index < 0) return 0; return (ButtonState & pgm_read_dword(&PS3_BUTTONS[index])); } bool PS3BT::getButtonClick(ButtonEnum b) { - const int8_t index = getPS3ButtonIndex(b); if (index < 0) return 0; + const int8_t index = getButtonIndexPS3(b); if (index < 0) return 0; uint32_t button = pgm_read_dword(&PS3_BUTTONS[index]); bool click = (ButtonClickState & button); ButtonClickState &= ~button; // Clear "click" event @@ -60,7 +60,7 @@ bool PS3BT::getButtonClick(ButtonEnum b) { } uint8_t PS3BT::getAnalogButton(ButtonEnum a) { - const int8_t index = getPS3ButtonIndex(a); if (index < 0) return 0; + const int8_t index = getButtonIndexPS3(a); if (index < 0) return 0; return (uint8_t)(l2capinbuf[pgm_read_byte(&PS3_ANALOG_BUTTONS[index])]); } diff --git a/PS3Enums.h b/PS3Enums.h index 2c0116e4..ababecec 100644 --- a/PS3Enums.h +++ b/PS3Enums.h @@ -138,8 +138,8 @@ enum StatusEnum { Bluetooth = (40 << 8) | 0x16, // Operating by Bluetooth and rumble is turned off }; -inline int8_t getPS3ButtonIndex(ButtonEnum b) { - const int8_t index = legacyButtonValues(b); +inline int8_t getButtonIndexPS3(ButtonEnum b) { + const int8_t index = ButtonIndex(b); if ((uint8_t) index >= (sizeof(PS3_BUTTONS) / sizeof(PS3_BUTTONS[0]))) return -1; return index; } diff --git a/PS3USB.cpp b/PS3USB.cpp index 4aba0e54..0b0438b4 100644 --- a/PS3USB.cpp +++ b/PS3USB.cpp @@ -314,12 +314,12 @@ void PS3USB::printReport() { // Uncomment "#define PRINTREPORT" to print the rep } bool PS3USB::getButtonPress(ButtonEnum b) { - const int8_t index = getPS3ButtonIndex(b); if (index < 0) return 0; + const int8_t index = getButtonIndexPS3(b); if (index < 0) return 0; return (ButtonState & pgm_read_dword(&PS3_BUTTONS[index])); } bool PS3USB::getButtonClick(ButtonEnum b) { - const int8_t index = getPS3ButtonIndex(b); if (index < 0) return 0; + const int8_t index = getButtonIndexPS3(b); if (index < 0) return 0; uint32_t button = pgm_read_dword(&PS3_BUTTONS[index]); bool click = (ButtonClickState & button); ButtonClickState &= ~button; // Clear "click" event @@ -327,7 +327,7 @@ bool PS3USB::getButtonClick(ButtonEnum b) { } uint8_t PS3USB::getAnalogButton(ButtonEnum a) { - const int8_t index = getPS3ButtonIndex(a); if (index < 0) return 0; + const int8_t index = getButtonIndexPS3(a); if (index < 0) return 0; return (uint8_t)(readBuf[(pgm_read_byte(&PS3_ANALOG_BUTTONS[index])) - 9]); } diff --git a/PS4Parser.cpp b/PS4Parser.cpp index aee04d2c..e27974bb 100644 --- a/PS4Parser.cpp +++ b/PS4Parser.cpp @@ -32,8 +32,8 @@ enum DPADEnum { // To enable serial debugging see "settings.h" //#define PRINTREPORT // Uncomment to print the report send by the PS4 Controller -int8_t PS4Parser::getButtonIndex(ButtonEnum b) { - const int8_t index = legacyButtonValues(b); +int8_t PS4Parser::getButtonIndexPS4(ButtonEnum b) { + const int8_t index = ButtonIndex(b); if ((uint8_t) index >= (sizeof(PS4_BUTTONS) / sizeof(PS4_BUTTONS[0]))) return -1; return index; } @@ -54,7 +54,7 @@ bool PS4Parser::checkDpad(ButtonEnum b) { } bool PS4Parser::getButtonPress(ButtonEnum b) { - const int8_t index = getButtonIndex(b); if (index < 0) return 0; + const int8_t index = getButtonIndexPS4(b); if (index < 0) return 0; if (index <= LEFT) // Dpad return checkDpad(b); else @@ -62,7 +62,7 @@ bool PS4Parser::getButtonPress(ButtonEnum b) { } bool PS4Parser::getButtonClick(ButtonEnum b) { - const int8_t index = getButtonIndex(b); if (index < 0) return 0; + const int8_t index = getButtonIndexPS4(b); if (index < 0) return 0; uint32_t mask = 1UL << pgm_read_byte(&PS4_BUTTONS[index]); bool click = buttonClickState.val & mask; buttonClickState.val &= ~mask; // Clear "click" event @@ -70,10 +70,10 @@ bool PS4Parser::getButtonClick(ButtonEnum b) { } uint8_t PS4Parser::getAnalogButton(ButtonEnum b) { - const int8_t index = getButtonIndex(b); if (index < 0) return 0; - if (index == legacyButtonValues(L2)) // These are the only analog buttons on the controller + const int8_t index = getButtonIndexPS4(b); if (index < 0) return 0; + if (index == ButtonIndex(L2)) // These are the only analog buttons on the controller return ps4Data.trigger[0]; - else if (index == legacyButtonValues(R2)) + else if (index == ButtonIndex(R2)) return ps4Data.trigger[1]; return 0; } diff --git a/PS4Parser.h b/PS4Parser.h index 4d6088ba..c0d27321 100644 --- a/PS4Parser.h +++ b/PS4Parser.h @@ -362,7 +362,7 @@ protected: virtual void sendOutputReport(PS4Output *output) = 0; private: - static int8_t getButtonIndex(ButtonEnum b); + static int8_t getButtonIndexPS4(ButtonEnum b); bool checkDpad(ButtonEnum b); // Used to check PS4 DPAD buttons PS4Data ps4Data; diff --git a/PS5Parser.cpp b/PS5Parser.cpp index 8d53997c..019c80e1 100644 --- a/PS5Parser.cpp +++ b/PS5Parser.cpp @@ -36,8 +36,8 @@ enum DPADEnum { // To enable serial debugging see "settings.h" //#define PRINTREPORT // Uncomment to print the report send by the PS5 Controller -int8_t PS5Parser::getButtonIndex(ButtonEnum b) { - const int8_t index = legacyButtonValues(b); +int8_t PS5Parser::getButtonIndexPS5(ButtonEnum b) { + const int8_t index = ButtonIndex(b); if ((uint8_t) index >= (sizeof(PS5_BUTTONS) / sizeof(PS5_BUTTONS[0]))) return -1; return index; } @@ -58,7 +58,7 @@ bool PS5Parser::checkDpad(ButtonEnum b) { } bool PS5Parser::getButtonPress(ButtonEnum b) { - const int8_t index = getButtonIndex(b); if (index < 0) return 0; + const int8_t index = getButtonIndexPS5(b); if (index < 0) return 0; if (index <= LEFT) // Dpad return checkDpad(b); else @@ -66,7 +66,7 @@ bool PS5Parser::getButtonPress(ButtonEnum b) { } bool PS5Parser::getButtonClick(ButtonEnum b) { - const int8_t index = getButtonIndex(b); if (index < 0) return 0; + const int8_t index = getButtonIndexPS5(b); if (index < 0) return 0; uint32_t mask = 1UL << pgm_read_byte(&PS5_BUTTONS[index]); bool click = buttonClickState.val & mask; buttonClickState.val &= ~mask; // Clear "click" event @@ -74,10 +74,10 @@ bool PS5Parser::getButtonClick(ButtonEnum b) { } uint8_t PS5Parser::getAnalogButton(ButtonEnum b) { - const int8_t index = getButtonIndex(b); if (index < 0) return 0; - if (index == legacyButtonValues(L2)) // These are the only analog buttons on the controller + const int8_t index = getButtonIndexPS5(b); if (index < 0) return 0; + if (index == ButtonIndex(L2)) // These are the only analog buttons on the controller return ps5Data.trigger[0]; - else if (index == legacyButtonValues(R2)) + else if (index == ButtonIndex(R2)) return ps5Data.trigger[1]; return 0; } diff --git a/PS5Parser.h b/PS5Parser.h index 7c0c29c9..dc2ddcac 100644 --- a/PS5Parser.h +++ b/PS5Parser.h @@ -403,7 +403,7 @@ protected: private: - static int8_t getButtonIndex(ButtonEnum b); + static int8_t getButtonIndexPS5(ButtonEnum b); bool checkDpad(ButtonEnum b); // Used to check PS5 DPAD buttons PS5Data ps5Data; diff --git a/PSBuzz.cpp b/PSBuzz.cpp index c542c499..5683aa0c 100644 --- a/PSBuzz.cpp +++ b/PSBuzz.cpp @@ -49,19 +49,19 @@ uint8_t PSBuzz::OnInitSuccessful() { return 0; }; -int8_t PSBuzz::getButtonIndex(ButtonEnum b) { - const int8_t index = legacyButtonValues(b); +int8_t PSBuzz::getButtonIndexBuzz(ButtonEnum b) { + const int8_t index = ButtonIndex(b); if (index > 4) return -1; // 5 buttons, 0-4 inclusive return index; } bool PSBuzz::getButtonPress(ButtonEnum b, uint8_t controller) { - const int8_t index = getButtonIndex(b); if (index < 0) return 0; + const int8_t index = getButtonIndexBuzz(b); if (index < 0) return 0; return psbuzzButtons.val & (1UL << (index + 5 * controller)); // Each controller uses 5 bits, so the value is shifted 5 for each controller }; bool PSBuzz::getButtonClick(ButtonEnum b, uint8_t controller) { - const int8_t index = getButtonIndex(b); if (index < 0) return 0; + const int8_t index = getButtonIndexBuzz(b); if (index < 0) return 0; uint32_t mask = (1UL << (index + 5 * controller)); // Each controller uses 5 bits, so the value is shifted 5 for each controller bool click = buttonClickState.val & mask; buttonClickState.val &= ~mask; // Clear "click" event diff --git a/PSBuzz.h b/PSBuzz.h index 8f891c35..b092af3b 100644 --- a/PSBuzz.h +++ b/PSBuzz.h @@ -175,9 +175,10 @@ protected: /**@}*/ private: + static int8_t getButtonIndexBuzz(ButtonEnum b); + void (*pFuncOnInit)(void); // Pointer to function called in onInit() - static int8_t getButtonIndex(ButtonEnum b); void PSBuzz_Command(uint8_t *data, uint16_t nbytes); PSBUZZButtons psbuzzButtons, oldButtonState, buttonClickState; diff --git a/Wii.cpp b/Wii.cpp index 6073f250..f2fa5a96 100644 --- a/Wii.cpp +++ b/Wii.cpp @@ -1094,25 +1094,25 @@ void WII::readWiiBalanceBoardCalibration() { /* WII Commands */ /************************************************************/ -int8_t WII::getButtonIndex(ButtonEnum b) { - const int8_t index = legacyButtonValues(b); +int8_t WII::getButtonIndexWii(ButtonEnum b) { + const int8_t index = ButtonIndex(b); if ((uint8_t) index >= (sizeof(WII_BUTTONS) / sizeof(WII_BUTTONS[0]))) return -1; return index; } -int8_t WII::getButtonIndexPro(ButtonEnum b) { - const int8_t index = legacyButtonValues(b); +int8_t WII::getButtonIndexWiiPro(ButtonEnum b) { + const int8_t index = ButtonIndex(b); if ((uint8_t) index >= (sizeof(WII_PROCONTROLLER_BUTTONS) / sizeof(WII_PROCONTROLLER_BUTTONS[0]))) return -1; return index; } bool WII::getButtonPress(ButtonEnum b) { // Return true when a button is pressed if (wiiUProControllerConnected) { - const int8_t index = getButtonIndexPro(b); if (index < 0) return 0; + const int8_t index = getButtonIndexWiiPro(b); if (index < 0) return 0; return (ButtonState & pgm_read_dword(&WII_PROCONTROLLER_BUTTONS[index])); } else { - const int8_t index = getButtonIndex(b); if (index < 0) return 0; + const int8_t index = getButtonIndexWii(b); if (index < 0) return 0; return (ButtonState & pgm_read_dword(&WII_BUTTONS[index])); } } @@ -1120,11 +1120,11 @@ bool WII::getButtonPress(ButtonEnum b) { // Return true when a button is pressed bool WII::getButtonClick(ButtonEnum b) { // Only return true when a button is clicked uint32_t button; if (wiiUProControllerConnected) { - const int8_t index = getButtonIndexPro(b); if (index < 0) return 0; + const int8_t index = getButtonIndexWiiPro(b); if (index < 0) return 0; button = pgm_read_dword(&WII_PROCONTROLLER_BUTTONS[index]); } else { - const int8_t index = getButtonIndex(b); if (index < 0) return 0; + const int8_t index = getButtonIndexWii(b); if (index < 0) return 0; button = pgm_read_dword(&WII_BUTTONS[index]); } bool click = (ButtonClickState & button); diff --git a/Wii.h b/Wii.h index d70b2510..7d65afcf 100644 --- a/Wii.h +++ b/Wii.h @@ -431,8 +431,8 @@ protected: /**@}*/ private: - static int8_t getButtonIndex(ButtonEnum b); - static int8_t getButtonIndexPro(ButtonEnum b); + static int8_t getButtonIndexWii(ButtonEnum b); + static int8_t getButtonIndexWiiPro(ButtonEnum b); void L2CAP_task(); // L2CAP state machine diff --git a/XBOXONE.cpp b/XBOXONE.cpp index 7345c734..d2e7aabd 100644 --- a/XBOXONE.cpp +++ b/XBOXONE.cpp @@ -331,9 +331,9 @@ void XBOXONE::readReport() { if(readBuf[0] == 0x07) { // The XBOX button has a separate message if(readBuf[4] == 1) - ButtonState |= pgm_read_word(&XBOX_BUTTONS[legacyButtonValues(XBOX)]); + ButtonState |= pgm_read_word(&XBOX_BUTTONS[ButtonIndex(XBOX)]); else - ButtonState &= ~pgm_read_word(&XBOX_BUTTONS[legacyButtonValues(XBOX)]); + ButtonState &= ~pgm_read_word(&XBOX_BUTTONS[ButtonIndex(XBOX)]); if(ButtonState != OldButtonState) { ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable @@ -348,7 +348,7 @@ void XBOXONE::readReport() { return; } - uint16_t xbox = ButtonState & pgm_read_word(&XBOX_BUTTONS[legacyButtonValues(XBOX)]); // Since the XBOX button is separate, save it and add it back in + uint16_t xbox = ButtonState & pgm_read_word(&XBOX_BUTTONS[ButtonIndex(XBOX)]); // Since the XBOX button is separate, save it and add it back in // xbox button from before, dpad, abxy, start/back, sync, stick click, shoulder buttons ButtonState = xbox | (((uint16_t)readBuf[5] & 0xF) << 8) | (readBuf[4] & 0xF0) | (((uint16_t)readBuf[4] & 0x0C) << 10) | ((readBuf[4] & 0x01) << 3) | (((uint16_t)readBuf[5] & 0xC0) << 8) | ((readBuf[5] & 0x30) >> 4); @@ -378,23 +378,23 @@ void XBOXONE::readReport() { } uint16_t XBOXONE::getButtonPress(ButtonEnum b) { - const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; - if(index == legacyButtonValues(L2)) // These are analog buttons + const int8_t index = getButtonIndexXbox(b); if (index < 0) return 0; + if(index == ButtonIndex(L2)) // These are analog buttons return triggerValue[0]; - else if(index == legacyButtonValues(R2)) + else if(index == ButtonIndex(R2)) return triggerValue[1]; return (bool)(ButtonState & ((uint16_t)pgm_read_word(&XBOX_BUTTONS[index]))); } bool XBOXONE::getButtonClick(ButtonEnum b) { - const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; - if(index == legacyButtonValues(L2)) { + const int8_t index = getButtonIndexXbox(b); if (index < 0) return 0; + if(index == ButtonIndex(L2)) { if(L2Clicked) { L2Clicked = false; return true; } return false; - } else if(index == legacyButtonValues(R2)) { + } else if(index == ButtonIndex(R2)) { if(R2Clicked) { R2Clicked = false; return true; diff --git a/XBOXONESParser.cpp b/XBOXONESParser.cpp index b92bbeb7..4ce12d99 100644 --- a/XBOXONESParser.cpp +++ b/XBOXONESParser.cpp @@ -54,8 +54,8 @@ enum DPADEnum { DPAD_LEFT_UP = 0x8, }; -int8_t XBOXONESParser::getButtonIndex(ButtonEnum b) { - const int8_t index = legacyButtonValues(b); +int8_t XBOXONESParser::getButtonIndexXboxOneS(ButtonEnum b) { + const int8_t index = ButtonIndex(b); if ((uint8_t) index >= (sizeof(XBOX_ONE_S_BUTTONS) / sizeof(XBOX_ONE_S_BUTTONS[0]))) return -1; return index; } @@ -76,33 +76,33 @@ bool XBOXONESParser::checkDpad(ButtonEnum b) { } uint16_t XBOXONESParser::getButtonPress(ButtonEnum b) { - const int8_t index = getButtonIndex(b); if (index < 0) return 0; - if (index == legacyButtonValues(L2)) + const int8_t index = getButtonIndexXboxOneS(b); if (index < 0) return 0; + if (index == ButtonIndex(L2)) return xboxOneSData.trigger[0]; - else if (index == legacyButtonValues(R2)) + else if (index == ButtonIndex(R2)) return xboxOneSData.trigger[1]; else if (index <= LEFT) // Dpad return checkDpad(b); - else if (index == legacyButtonValues(XBOX)) + else if (index == ButtonIndex(XBOX)) return xboxButtonState; return xboxOneSData.btn.val & (1UL << pgm_read_byte(&XBOX_ONE_S_BUTTONS[index])); } bool XBOXONESParser::getButtonClick(ButtonEnum b) { - const int8_t index = getButtonIndex(b); if (index < 0) return 0; - if(index == legacyButtonValues(L2)) { + const int8_t index = getButtonIndexXboxOneS(b); if (index < 0) return 0; + if(index == ButtonIndex(L2)) { if(L2Clicked) { L2Clicked = false; return true; } return false; - } else if(index == legacyButtonValues(R2)) { + } else if(index == ButtonIndex(R2)) { if(R2Clicked) { R2Clicked = false; return true; } return false; - } else if (index == legacyButtonValues(XBOX)) { + } else if (index == ButtonIndex(XBOX)) { bool click = xboxbuttonClickState; xboxbuttonClickState = 0; // Clear "click" event return click; diff --git a/XBOXONESParser.h b/XBOXONESParser.h index 531bb3eb..c9dbb51a 100644 --- a/XBOXONESParser.h +++ b/XBOXONESParser.h @@ -111,7 +111,7 @@ protected: virtual void sendOutputReport(uint8_t *data, uint8_t nbytes) = 0; private: - static int8_t getButtonIndex(ButtonEnum b); + static int8_t getButtonIndexXboxOneS(ButtonEnum b); bool checkDpad(ButtonEnum b); // Used to check Xbox One S DPAD buttons diff --git a/XBOXRECV.cpp b/XBOXRECV.cpp index 1a7ac318..0ba3b6b0 100644 --- a/XBOXRECV.cpp +++ b/XBOXRECV.cpp @@ -408,23 +408,23 @@ void XBOXRECV::printReport(uint8_t controller __attribute__((unused)), uint8_t n } uint8_t XBOXRECV::getButtonPress(ButtonEnum b, uint8_t controller) { - const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; - if(index == legacyButtonValues(L2)) // These are analog buttons + const int8_t index = getButtonIndexXbox(b); if (index < 0) return 0; + if(index == ButtonIndex(L2)) // These are analog buttons return (uint8_t)(ButtonState[controller] >> 8); - else if(index == legacyButtonValues(R2)) + else if(index == ButtonIndex(R2)) return (uint8_t)ButtonState[controller]; return (bool)(ButtonState[controller] & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[index]) << 16)); } bool XBOXRECV::getButtonClick(ButtonEnum b, uint8_t controller) { - const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; - if(index == legacyButtonValues(L2)) { + const int8_t index = getButtonIndexXbox(b); if (index < 0) return 0; + if(index == ButtonIndex(L2)) { if(L2Clicked[controller]) { L2Clicked[controller] = false; return true; } return false; - } else if(index == legacyButtonValues(R2)) { + } else if(index == ButtonIndex(R2)) { if(R2Clicked[controller]) { R2Clicked[controller] = false; return true; diff --git a/XBOXUSB.cpp b/XBOXUSB.cpp index 6b44299e..0ce0997f 100644 --- a/XBOXUSB.cpp +++ b/XBOXUSB.cpp @@ -281,23 +281,23 @@ void XBOXUSB::printReport() { //Uncomment "#define PRINTREPORT" to print the rep } uint8_t XBOXUSB::getButtonPress(ButtonEnum b) { - const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; - if(index == legacyButtonValues(L2)) // These are analog buttons + const int8_t index = getButtonIndexXbox(b); if (index < 0) return 0; + if(index == ButtonIndex(L2)) // These are analog buttons return (uint8_t)(ButtonState >> 8); - else if(index == legacyButtonValues(R2)) + else if(index == ButtonIndex(R2)) return (uint8_t)ButtonState; return (bool)(ButtonState & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[index]) << 16)); } bool XBOXUSB::getButtonClick(ButtonEnum b) { - const int8_t index = getXboxButtonIndex(b); if (index < 0) return 0; - if(index == legacyButtonValues(L2)) { + const int8_t index = getButtonIndexXbox(b); if (index < 0) return 0; + if(index == ButtonIndex(L2)) { if(L2Clicked) { L2Clicked = false; return true; } return false; - } else if(index == legacyButtonValues(R2)) { + } else if(index == ButtonIndex(R2)) { if(R2Clicked) { R2Clicked = false; return true; diff --git a/controllerEnums.h b/controllerEnums.h index bfffb83d..60181823 100644 --- a/controllerEnums.h +++ b/controllerEnums.h @@ -183,7 +183,7 @@ enum ButtonEnum { /**@}*/ }; -inline constexpr int8_t legacyButtonValues(ButtonEnum key) { +inline constexpr int8_t ButtonIndex(ButtonEnum key) { // using a chained ternary in place of a switch for constexpr on older compilers return (key == UP || key == RED) ? 0 : diff --git a/xboxEnums.h b/xboxEnums.h index 037be594..d3d3f907 100644 --- a/xboxEnums.h +++ b/xboxEnums.h @@ -62,8 +62,8 @@ const uint16_t XBOX_BUTTONS[] PROGMEM = { 0x0008, // SYNC }; -inline int8_t getXboxButtonIndex(ButtonEnum b) { - const int8_t index = legacyButtonValues(b); +inline int8_t getButtonIndexXbox(ButtonEnum b) { + const int8_t index = ButtonIndex(b); if ((uint8_t) index >= (sizeof(XBOX_BUTTONS) / sizeof(XBOX_BUTTONS[0]))) return -1; return index; } From 33975cfca94c026ac0ca2a3f24fabfad8212fd56 Mon Sep 17 00:00:00 2001 From: David Madison Date: Sat, 27 Mar 2021 14:13:05 -0400 Subject: [PATCH 76/93] Modify XBOXOLD to catch previous button cases Getting a bit ahead of myself and forgetting that the enum checks no longer work to maintain the previous aliases. --- XBOXOLD.cpp | 122 +++++++++++++++++++++++++++------------------------- XBOXOLD.h | 2 +- 2 files changed, 65 insertions(+), 59 deletions(-) diff --git a/XBOXOLD.cpp b/XBOXOLD.cpp index 440f8e0d..1d31edf8 100644 --- a/XBOXOLD.cpp +++ b/XBOXOLD.cpp @@ -20,6 +20,30 @@ //#define EXTRADEBUG // Uncomment to get even more debugging data //#define PRINTREPORT // Uncomment to print the report send by the Xbox controller +/** Buttons on the controllers */ +const uint8_t XBOXOLD_BUTTONS[] PROGMEM = { + 0x01, // UP + 0x08, // RIGHT + 0x02, // DOWN + 0x04, // LEFT + + 0x20, // BACK + 0x10, // START + 0x40, // L3 + 0x80, // R3 + + // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons + 4, // BLACK + 5, // WHTIE + 6, // L1 + 7, // R1 + + 1, // B + 0, // A + 2, // X + 3, // Y +}; + XBOXOLD::XBOXOLD(USB *p) : pUsb(p), // pointer to USB class instance - mandatory @@ -270,74 +294,55 @@ void XBOXOLD::printReport(uint16_t length __attribute__((unused))) { //Uncomment } int8_t XBOXOLD::getAnalogIndex(ButtonEnum b) { - // A, B, X, Y, BLACK, WHITE, L, and R are analog buttons - uint8_t out; + // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons + const int8_t index = ButtonIndex(b); - switch (b) { - case A: - out = 0; break; - case B: - out = 1; break; - case X: - out = 2; break; - case Y: - out = 3; break; - case WHITE: - out = 5; break; - case BLACK: - out = 4; break; - case L: - case L1: - case L2: - out = 6; break; - case R: - case R1: - case R2: - out = 7; break; - default: - out = -1; break; + switch (index) { + case ButtonIndex(A): + case ButtonIndex(B): + case ButtonIndex(X): + case ButtonIndex(Y): + case ButtonIndex(BLACK): + case ButtonIndex(WHITE): + case ButtonIndex(L1): + case ButtonIndex(R1): + return index; + default: break; } - return out; + return -1; } -int8_t XBOXOLD::getDigitalOffset(ButtonEnum b) { +int8_t XBOXOLD::getDigitalIndex(ButtonEnum b) { // UP, DOWN, LEFT, RIGHT, START, BACK, L3, and R3 are digital buttons - // (these are offets for the bitshift) - uint8_t out; + const int8_t index = ButtonIndex(b); - switch (b) { - case UP: - out = 0; break; - case DOWN: - out = 1; break; - case LEFT: - out = 2; break; - case RIGHT: - out = 3; break; - case START: - out = 4; break; - case BACK: - out = 5; break; - case L3: - out = 6; break; - case R3: - out = 7; break; - default: - out = -1; break; + switch (index) { + case ButtonIndex(UP): + case ButtonIndex(DOWN): + case ButtonIndex(LEFT): + case ButtonIndex(RIGHT): + case ButtonIndex(START): + case ButtonIndex(BACK): + case ButtonIndex(L3): + case ButtonIndex(R3): + return index; + default: break; } - return out; + return -1; } uint8_t XBOXOLD::getButtonPress(ButtonEnum b) { const int8_t analogIndex = getAnalogIndex(b); if (analogIndex >= 0) { - return buttonValues[analogIndex]; + const uint8_t buttonIndex = pgm_read_byte(&XBOXOLD_BUTTONS[analogIndex]); + return buttonValues[buttonIndex]; } - const int8_t digitalOffset = getDigitalOffset(b); - if (digitalOffset >= 0) { - return (ButtonState & (1 << digitalOffset)); + const int8_t digitalIndex = getDigitalIndex(b); + if (digitalIndex >= 0) { + const uint8_t buttonMask = pgm_read_byte(&XBOXOLD_BUTTONS[digitalIndex]); + return (ButtonState & buttonMask); } return 0; } @@ -345,15 +350,16 @@ uint8_t XBOXOLD::getButtonPress(ButtonEnum b) { bool XBOXOLD::getButtonClick(ButtonEnum b) { const int8_t analogIndex = getAnalogIndex(b); if (analogIndex >= 0) { - if (buttonClicked[analogIndex]) { - buttonClicked[analogIndex] = false; + const uint8_t buttonIndex = pgm_read_byte(&XBOXOLD_BUTTONS[analogIndex]); + if (buttonClicked[buttonIndex]) { + buttonClicked[buttonIndex] = false; return true; } return false; } - const int8_t digitalOffset = getDigitalOffset(b); - if (digitalOffset >= 0) { - const uint8_t mask = (1 << digitalOffset); + const int8_t digitalIndex = getDigitalIndex(b); + if (digitalIndex >= 0) { + const uint8_t mask = pgm_read_byte(&XBOXOLD_BUTTONS[digitalIndex]); const bool click = (ButtonClickState & mask); ButtonClickState &= ~mask; return click; diff --git a/XBOXOLD.h b/XBOXOLD.h index 319e435d..9acd3a4e 100644 --- a/XBOXOLD.h +++ b/XBOXOLD.h @@ -154,7 +154,7 @@ protected: private: static int8_t getAnalogIndex(ButtonEnum b); - static int8_t getDigitalOffset(ButtonEnum b); + static int8_t getDigitalIndex(ButtonEnum b); /** * Called when the controller is successfully initialized. From 5d89999be88239b75d1b70ff3029180410eb4b8d Mon Sep 17 00:00:00 2001 From: David Madison Date: Sat, 27 Mar 2021 15:05:16 -0400 Subject: [PATCH 77/93] ButtonEnum style order nitpicks Aligning like-system identifiers for visual congruity. No functional change. --- XBOXOLD.cpp | 1 - controllerEnums.h | 18 +++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/XBOXOLD.cpp b/XBOXOLD.cpp index 1d31edf8..1d38b694 100644 --- a/XBOXOLD.cpp +++ b/XBOXOLD.cpp @@ -44,7 +44,6 @@ const uint8_t XBOXOLD_BUTTONS[] PROGMEM = { 3, // Y }; - XBOXOLD::XBOXOLD(USB *p) : pUsb(p), // pointer to USB class instance - mandatory bAddress(0), // device address - mandatory diff --git a/controllerEnums.h b/controllerEnums.h index 60181823..f2ec6963 100644 --- a/controllerEnums.h +++ b/controllerEnums.h @@ -191,15 +191,15 @@ inline constexpr int8_t ButtonIndex(ButtonEnum key) { (key == DOWN || key == GREEN) ? 2 : (key == LEFT || key == ORANGE) ? 3 : (key == SELECT || key == SHARE || key == BACK || key == VIEW || key == BLUE || key == CREATE) ? 4 : - (key == PLUS || key == START || key == OPTIONS || key == MENU) ? 5 : - (key == TWO || key == L3) ? 6 : - (key == ONE || key == R3) ? 7 : - (key == MINUS || key == L2 || key == BLACK) ? 8 : - (key == HOME || key == R2 || key == WHITE) ? 9 : - (key == Z || key == L1) ? 10 : - (key == C || key == R1) ? 11 : - (key == B || key == TRIANGLE) ? 12 : - (key == A || key == CIRCLE) ? 13 : + (key == START || key == OPTIONS || key == MENU || key == PLUS) ? 5 : + (key == L3 || key == TWO) ? 6 : + (key == R3 || key == ONE) ? 7 : + (key == L2 || key == MINUS || key == BLACK) ? 8 : + (key == R2 || key == HOME || key == WHITE) ? 9 : + (key == L1 || key == Z) ? 10 : + (key == R1 || key == C) ? 11 : + (key == TRIANGLE || key == B) ? 12 : + (key == CIRCLE || key == A) ? 13 : (key == CROSS || key == X) ? 14 : (key == SQUARE || key == Y) ? 15 : (key == L || key == PS || key == XBOX) ? 16 : From 425ae4584c8b9500ae78da06bdfafd0f28431a80 Mon Sep 17 00:00:00 2001 From: Aran Vink Date: Sun, 28 Mar 2021 00:32:57 +0100 Subject: [PATCH 78/93] Remove unneeded delays, rewrite main loop in example --- AMBX.cpp | 2 -- examples/ambx/AMBX.ino | 52 +++++++++++++++++++++++------------------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/AMBX.cpp b/AMBX.cpp index 4df787d3..6c31021a 100644 --- a/AMBX.cpp +++ b/AMBX.cpp @@ -218,8 +218,6 @@ void AMBX::Light_Command(uint8_t *data, uint16_t nbytes) { Notify(PSTR("\r\nLight command "), 0x80); #endif pUsb->outTransfer(bAddress, epInfo[ AMBX_OUTPUT_PIPE ].epAddr, nbytes, data); - //Do really short delay, I've noticed otherwise the controller will receive all command at once, and might not process all of them. - delay(1); } void AMBX::setLight(uint8_t ambx_light, uint8_t r, uint8_t g, uint8_t b) { diff --git a/examples/ambx/AMBX.ino b/examples/ambx/AMBX.ino index f7280e2c..e3e53087 100644 --- a/examples/ambx/AMBX.ino +++ b/examples/ambx/AMBX.ino @@ -13,8 +13,9 @@ USB Usb; AMBX AMBX(&Usb); // This will just create the instance -bool printAngle; uint8_t state = 0; +const long interval = 1000; +unsigned long previousMillis = 0; void setup() { Serial.begin(115200); @@ -27,29 +28,34 @@ void setup() { } Serial.print(F("\r\nAMBX USB Library Started")); } + void loop() { Usb.Task(); - - if (AMBX.AMBXConnected) { - if (state == 0) { - - } else if (state == 1) { - AMBX.setAllLights(Red); - } else if (state == 2) { - AMBX.setAllLights(Green); - } else if (state == 3) { - AMBX.setAllLights(Blue); - } else if (state == 4) { - AMBX.setAllLights(White); + + if (AMBX.AMBXConnected) { + unsigned long currentMillis = millis(); + if (currentMillis - previousMillis >= interval) { + previousMillis = currentMillis; + + if (state > 4) { + state = 0; + } + if (state == 0) { + Serial.print(F("\r\nRed")); + AMBX.setAllLights(Red); + } else if (state == 1) { + Serial.print(F("\r\nGreen")); + AMBX.setAllLights(Green); + } else if (state == 2) { + Serial.print(F("\r\nBlue")); + AMBX.setAllLights(Blue); + } else if (state == 3) { + Serial.print(F("\r\nWhite")); + AMBX.setAllLights(White); + } + state++; + } } - - //Example using single light: - //AMBX.setLight(Wallwasher_center, White); - - state++; - if (state > 4) - state = 0; - delay(1000); - } - delay(10); + //Example using single light: + //AMBX.setLight(Wallwasher_center, White); } From e2a5cd9401d8a52da3b3ef3981598061ac8a7ea9 Mon Sep 17 00:00:00 2001 From: Aran Vink Date: Sun, 28 Mar 2021 00:46:39 +0100 Subject: [PATCH 79/93] Update README --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 968377d7..a348e4ce 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,8 @@ For more information about the hardware see the [Hardware Manual](https://chome. * [Xbox ONE](#xbox-one-library) controller support * __Yuuichi Akagawa__ - [@YuuichiAkagawa](https://twitter.com/yuuichiakagawa) * Developer of the [MIDI](#midi-library) library +* __Aran Vink__ - + * Developer of the [amBX](#amBX) library # Table of Contents @@ -56,6 +58,7 @@ For more information about the hardware see the [Hardware Manual](https://chome. * [PS Buzz Library](#ps-buzz-library) * [HID Libraries](#hid-libraries) * [MIDI Library](#midi-library) + * [amBX Library](#amBX-library) * [Interface modifications](#interface-modifications) * [FAQ](#faq) @@ -353,6 +356,13 @@ You can convert USB MIDI keyboard to legacy serial MIDI. For information see the following page: . +### [amBX Library](AMBX.cpp) + +This library support Philips amBX lights. +You can set the colors of the lights individually or all at once. The rumble pad and fans are not supported. + +* [AMBX.ino](examples/ambx/AMBX.ino) + # Interface modifications The shield is using SPI for communicating with the MAX3421E USB host controller. It uses the SCK, MISO and MOSI pins via the ICSP on your board. From 396820bdbec80fb8b19f34a3bd8089c3825eb5c7 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 2 May 2021 15:09:59 +0200 Subject: [PATCH 80/93] Ignore "initialization from incompatible pointer type" warning in the ESP32 core --- .github/workflows/main.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 385e67e2..bcec23cb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -33,11 +33,14 @@ jobs: # See: https://travis-ci.org/github/felis/USB_Host_Shield_2.0/jobs/743787235 if [[ "${{ matrix.example }}" != *"bidirectional_converter" ]]; then TEENSY35="--board=teensy35"; TEENSY36="--board=teensy36"; TEENSY40="--board=teensy40"; TEENSY41="--board=teensy41"; fi - pio ci --lib="." $UNO --board=genuino101 --board=teensylc $TEENSY40 $TEENSY41 --board=esp12e --board=nodemcu --board=esp32dev + pio ci --lib="." $UNO --board=genuino101 --board=teensylc $TEENSY40 $TEENSY41 --board=esp12e --board=nodemcu # Teensy 3.x depends on the SPI4Teensy3 library: https://platformio.org/lib/show/417/SPI4Teensy3 pio ci --lib="." --board=teensy30 --board=teensy31 $TEENSY35 $TEENSY36 --project-option="lib_deps=SPI4Teensy3" + # Ignore "initialization from incompatible pointer type" warning in the ESP32 core + pio ci --lib="." --board=esp32dev --project-option="build_flags=-Wno-incompatible-pointer-types" + # Workaround https://github.com/arduino/ArduinoCore-sam/issues/69 pio ci --lib="." --board=due --project-option="build_flags=-Wno-misleading-indentation" From 4314c006831a16d071fa699e07405423d11cd466 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 2 May 2021 15:25:35 +0200 Subject: [PATCH 81/93] Fixed link to amBX library --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7a3179e8..3d230cf6 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ For more information about the hardware see the [Hardware Manual](https://chome. * __Yuuichi Akagawa__ - [@YuuichiAkagawa](https://twitter.com/yuuichiakagawa) * Developer of the [MIDI](#midi-library) library * __Aran Vink__ - - * Developer of the [amBX](#amBX) library + * Developer of the [amBX](#amBX-library) library # Table of Contents From 3f61d9af2a2847149eb7cfe13b07d63503bf176a Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 2 May 2021 15:45:54 +0200 Subject: [PATCH 82/93] Fixed reading the XBOX button on the Xbox One S controller via Bluetooth This bug was introduced in #618 --- XBOXONESParser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/XBOXONESParser.cpp b/XBOXONESParser.cpp index 4ce12d99..588477e5 100644 --- a/XBOXONESParser.cpp +++ b/XBOXONESParser.cpp @@ -40,6 +40,7 @@ const uint8_t XBOX_ONE_S_BUTTONS[] PROGMEM = { 0x08, // A 0x0A, // X 0x0B, // Y + 0, // XBOX - this is sent in another report }; enum DPADEnum { From e94343a49e322bb10b985158388e963dfa5a5552 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 2 May 2021 15:52:12 +0200 Subject: [PATCH 83/93] Only print if the XboxCommand fails --- XBOXONE.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/XBOXONE.cpp b/XBOXONE.cpp index d2e7aabd..f84a090f 100644 --- a/XBOXONE.cpp +++ b/XBOXONE.cpp @@ -416,8 +416,10 @@ uint8_t XBOXONE::XboxCommand(uint8_t* data, uint16_t nbytes) { data[2] = cmdCounter++; // Increment the output command counter uint8_t rcode = pUsb->outTransfer(bAddress, epInfo[ XBOX_ONE_OUTPUT_PIPE ].epAddr, nbytes, data); #ifdef DEBUG_USB_HOST - Notify(PSTR("\r\nXboxCommand, Return: "), 0x80); - D_PrintHex (rcode, 0x80); + if(rcode) { + Notify(PSTR("\r\nXboxCommand failed. Return: "), 0x80); + D_PrintHex (rcode, 0x80); + } #endif return rcode; } From 13b9047106a94a68f8b7c6abce0e8ecf13907c2d Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 2 May 2021 18:17:07 +0200 Subject: [PATCH 84/93] Added support for the Switch Pro controller via Bluetooth --- .github/workflows/main.yml | 2 +- SwitchProBT.h | 89 +++++++ SwitchProParser.cpp | 151 ++++++++++++ SwitchProParser.h | 271 +++++++++++++++++++++ controllerEnums.h | 7 +- examples/Bluetooth/SwitchPro/SwitchPro.ino | 126 ++++++++++ keywords.txt | 2 + library.json | 2 +- library.properties | 2 +- 9 files changed, 648 insertions(+), 4 deletions(-) create mode 100644 SwitchProBT.h create mode 100644 SwitchProParser.cpp create mode 100644 SwitchProParser.h create mode 100644 examples/Bluetooth/SwitchPro/SwitchPro.ino diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bcec23cb..67891798 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: # find examples -type f -name "*.ino" | rev | cut -d/ -f2- | rev | sort | sed -z 's/\n/, /g' - example: [examples/ambx, examples/acm/acm_terminal, examples/adk/adk_barcode, examples/adk/ArduinoBlinkLED, examples/adk/demokit_20, examples/adk/term_test, examples/adk/term_time, examples/Bluetooth/BTHID, examples/Bluetooth/PS3BT, examples/Bluetooth/PS3Multi, examples/Bluetooth/PS3SPP, examples/Bluetooth/PS4BT, examples/Bluetooth/PS5BT, examples/Bluetooth/SPP, examples/Bluetooth/SPPMulti, examples/Bluetooth/Wii, examples/Bluetooth/WiiBalanceBoard, examples/Bluetooth/WiiIRCamera, examples/Bluetooth/WiiMulti, examples/Bluetooth/WiiUProController, examples/board_qc, examples/cdc_XR21B1411/XR_terminal, examples/ftdi/USBFTDILoopback, examples/GPIO/Blink, examples/GPIO/Blink_LowLevel, examples/GPIO/Input, examples/HID/le3dp, examples/HID/scale, examples/HID/SRWS1, examples/HID/t16km, examples/HID/USBHIDBootKbd, examples/HID/USBHIDBootKbdAndMouse, examples/HID/USBHIDBootMouse, examples/HID/USBHID_desc, examples/HID/USBHIDJoystick, examples/HID/USBHIDMultimediaKbd, examples/hub_demo, examples/max_LCD, examples/pl2303/pl2303_gprs_terminal, examples/pl2303/pl2303_gps, examples/pl2303/pl2303_tinygps, examples/pl2303/pl2303_xbee_terminal, examples/PS3USB, examples/PS4USB, examples/PS5USB, examples/PSBuzz, examples/USB_desc, examples/USBH_MIDI/bidirectional_converter, examples/USBH_MIDI/eVY1_sample, examples/USBH_MIDI/USBH_MIDI_dump, examples/USBH_MIDI/USB_MIDI_converter, examples/USBH_MIDI/USB_MIDI_converter_multi, examples/Xbox/XBOXOLD, examples/Xbox/XBOXONE, examples/Xbox/XBOXONESBT, examples/Xbox/XBOXRECV, examples/Xbox/XBOXUSB] + example: [examples/ambx, examples/acm/acm_terminal, examples/adk/adk_barcode, examples/adk/ArduinoBlinkLED, examples/adk/demokit_20, examples/adk/term_test, examples/adk/term_time, examples/Bluetooth/BTHID, examples/Bluetooth/PS3BT, examples/Bluetooth/PS3Multi, examples/Bluetooth/PS3SPP, examples/Bluetooth/PS4BT, examples/Bluetooth/PS5BT, examples/Bluetooth/SPP, examples/Bluetooth/SPPMulti, examples/Bluetooth/SwitchPro, examples/Bluetooth/Wii, examples/Bluetooth/WiiBalanceBoard, examples/Bluetooth/WiiIRCamera, examples/Bluetooth/WiiMulti, examples/Bluetooth/WiiUProController, examples/board_qc, examples/cdc_XR21B1411/XR_terminal, examples/ftdi/USBFTDILoopback, examples/GPIO/Blink, examples/GPIO/Blink_LowLevel, examples/GPIO/Input, examples/HID/le3dp, examples/HID/scale, examples/HID/SRWS1, examples/HID/t16km, examples/HID/USBHIDBootKbd, examples/HID/USBHIDBootKbdAndMouse, examples/HID/USBHIDBootMouse, examples/HID/USBHID_desc, examples/HID/USBHIDJoystick, examples/HID/USBHIDMultimediaKbd, examples/hub_demo, examples/max_LCD, examples/pl2303/pl2303_gprs_terminal, examples/pl2303/pl2303_gps, examples/pl2303/pl2303_tinygps, examples/pl2303/pl2303_xbee_terminal, examples/PS3USB, examples/PS4USB, examples/PS5USB, examples/PSBuzz, examples/USB_desc, examples/USBH_MIDI/bidirectional_converter, examples/USBH_MIDI/eVY1_sample, examples/USBH_MIDI/USBH_MIDI_dump, examples/USBH_MIDI/USB_MIDI_converter, examples/USBH_MIDI/USB_MIDI_converter_multi, examples/Xbox/XBOXOLD, examples/Xbox/XBOXONE, examples/Xbox/XBOXONESBT, examples/Xbox/XBOXRECV, examples/Xbox/XBOXUSB] steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 diff --git a/SwitchProBT.h b/SwitchProBT.h new file mode 100644 index 00000000..d4f39e9e --- /dev/null +++ b/SwitchProBT.h @@ -0,0 +1,89 @@ +/* Copyright (C) 2021 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 _switch_pro_bt_h_ +#define _switch_pro_bt_h_ + +#include "BTHID.h" +#include "SwitchProParser.h" + +/** + * This class implements support for the Switch Pro controller via Bluetooth. + * It uses the BTHID class for all the Bluetooth communication. + */ +class SwitchProBT : public BTHID, public SwitchProParser { +public: + /** + * Constructor for the SwitchProBT 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 "0000" will be used. + */ + SwitchProBT(BTD *p, bool pair = false, const char *pin = "0000") : + BTHID(p, pair, pin) { + SwitchProParser::Reset(); + }; + + /** + * Used to check if a Switch Pro 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) { + SwitchProParser::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() { + SwitchProParser::Reset(); + + // Only call this is a user function has not been set + if (!pFuncOnInit) + setLedOn(LED1); // Turn on the LED1 + }; + + /** Used to reset the different buffers to there default values */ + virtual void ResetBTHID() { + SwitchProParser::Reset(); + }; + /**@}*/ + + /** @name SwitchProParser implementation */ + virtual void sendOutputReport(SwitchProOutput *output) { + output->reportChanged = false; +#if 0 + // Send the Bluetooth DATA output report on the interrupt channel + pBtd->L2CAP_Command(hci_handle, buf, sizeof(buf), interrupt_scid[0], interrupt_scid[1]); +#endif + }; + /**@}*/ +}; +#endif diff --git a/SwitchProParser.cpp b/SwitchProParser.cpp new file mode 100644 index 00000000..916a526c --- /dev/null +++ b/SwitchProParser.cpp @@ -0,0 +1,151 @@ +/* Copyright (C) 2021 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 "SwitchProParser.h" + +enum DPADEnum { + DPAD_UP = 0x0, + DPAD_UP_RIGHT = 0x1, + DPAD_RIGHT = 0x2, + DPAD_RIGHT_DOWN = 0x3, + DPAD_DOWN = 0x4, + DPAD_DOWN_LEFT = 0x5, + DPAD_LEFT = 0x6, + DPAD_LEFT_UP = 0x7, + DPAD_OFF = 0x8, +}; + +// To enable serial debugging see "settings.h" +//#define PRINTREPORT // Uncomment to print the report send by the Switch Pro Controller + +int8_t SwitchProParser::getButtonIndexSwitchPro(ButtonEnum b) { + const int8_t index = ButtonIndex(b); + if ((uint8_t) index >= (sizeof(SWITCH_PRO_BUTTONS) / sizeof(SWITCH_PRO_BUTTONS[0]))) return -1; + return index; +} + +bool SwitchProParser::checkDpad(ButtonEnum b) { + switch (b) { + case UP: + return switchProData.btn.dpad == DPAD_LEFT_UP || switchProData.btn.dpad == DPAD_UP || switchProData.btn.dpad == DPAD_UP_RIGHT; + case RIGHT: + return switchProData.btn.dpad == DPAD_UP_RIGHT || switchProData.btn.dpad == DPAD_RIGHT || switchProData.btn.dpad == DPAD_RIGHT_DOWN; + case DOWN: + return switchProData.btn.dpad == DPAD_RIGHT_DOWN || switchProData.btn.dpad == DPAD_DOWN || switchProData.btn.dpad == DPAD_DOWN_LEFT; + case LEFT: + return switchProData.btn.dpad == DPAD_DOWN_LEFT || switchProData.btn.dpad == DPAD_LEFT || switchProData.btn.dpad == DPAD_LEFT_UP; + default: + return false; + } +} + +bool SwitchProParser::getButtonPress(ButtonEnum b) { + const int8_t index = getButtonIndexSwitchPro(b); if (index < 0) return 0; + if (index <= LEFT) // Dpad + return checkDpad(b); + else + return switchProData.btn.val & (1UL << pgm_read_byte(&SWITCH_PRO_BUTTONS[index])); +} + +bool SwitchProParser::getButtonClick(ButtonEnum b) { + const int8_t index = getButtonIndexSwitchPro(b); if (index < 0) return 0; + uint32_t mask = 1UL << pgm_read_byte(&SWITCH_PRO_BUTTONS[index]); + bool click = buttonClickState.val & mask; + buttonClickState.val &= ~mask; // Clear "click" event + return click; +} + +int16_t SwitchProParser::getAnalogHat(AnalogHatEnum a) { + return switchProData.hatValue[(uint8_t)a] - 0x7FFF; // Subtract the center value +} + +void SwitchProParser::Parse(uint8_t len, uint8_t *buf) { + if (len > 1 && buf) { +#ifdef PRINTREPORT + Notify(PSTR("\r\nLen: "), 0x80); Notify(len, 0x80); + Notify(PSTR(", data: "), 0x80); + for (uint8_t i = 0; i < len; i++) { + D_PrintHex (buf[i], 0x80); + Notify(PSTR(" "), 0x80); + } +#endif + + if (buf[0] == 0x3F) // Check report ID + memcpy(&switchProData, buf + 1, min((uint8_t)(len - 1), MFK_CASTUINT8T sizeof(switchProData))); + else { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nUnknown report id: "), 0x80); + D_PrintHex (buf[0], 0x80); + Notify(PSTR(", len: "), 0x80); + D_PrintHex (len, 0x80); +#endif + return; + } + + // Workaround issue with the controller sending invalid joystick values when it is connected + for (uint8_t i = 0; i < sizeof(switchProData.hatValue) / sizeof(switchProData.hatValue[0]); i++) { + if (switchProData.hatValue[i] < 1000 || switchProData.hatValue[i] > 0xFFFF - 1000) + switchProData.hatValue[i] = 0x7FFF; // Center value + } + + if (switchProData.btn.val != oldButtonState.val) { // Check if anything has changed + buttonClickState.val = switchProData.btn.val & ~oldButtonState.val; // Update click state variable + oldButtonState.val = switchProData.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; + } + } + + message_counter++; + } + + if (switchProOutput.reportChanged) + sendOutputReport(&switchProOutput); // Send output report +} + +void SwitchProParser::Reset() { + for (uint8_t i = 0; i < sizeof(switchProData.hatValue) / sizeof(switchProData.hatValue[0]); i++) + switchProData.hatValue[i] = 0x7FFF; // Center value + switchProData.btn.val = 0; + oldButtonState.val = 0; + + switchProData.btn.dpad = DPAD_OFF; + oldButtonState.dpad = DPAD_OFF; + buttonClickState.dpad = 0; + oldDpad = 0; + +#if 0 + ps5Output.bigRumble = ps5Output.smallRumble = 0; + ps5Output.microphoneLed = 0; + ps5Output.disableLeds = 0; + ps5Output.playerLeds = 0; + ps5Output.r = ps5Output.g = ps5Output.b = 0; +#endif + switchProOutput.reportChanged = false; +}; diff --git a/SwitchProParser.h b/SwitchProParser.h new file mode 100644 index 00000000..d44630d1 --- /dev/null +++ b/SwitchProParser.h @@ -0,0 +1,271 @@ +/* Copyright (C) 2021 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 _switch_pro_parser_h_ +#define _switch_pro_parser_h_ + +#include "Usb.h" +#include "controllerEnums.h" + +/** Buttons on the controller */ +const uint8_t SWITCH_PRO_BUTTONS[] PROGMEM = { + 0x10, // UP + 0x11, // RIGHT + 0x12, // DOWN + 0x13, // LEFT + + 0x0D, // Capture + 0x09, // PLUS + 0x0A, // L3 + 0x0B, // R3 + + 0x08, // MINUS + 0x0C, // HOME + 0, 0, // Skip + + 0x00, // B + 0x01, // A + 0x03, // X + 0x02, // Y + + 0x04, // L + 0x05, // R + 0x06, // ZL + 0x07, // ZR +}; + +union SwitchProButtons { + struct { + uint8_t b : 1; + uint8_t a : 1; + uint8_t y : 1; + uint8_t x : 1; + + uint8_t l : 1; + uint8_t r : 1; + uint8_t zl : 1; + uint8_t zr : 1; + + uint8_t minus : 1; + uint8_t plus : 1; + uint8_t l3 : 1; + uint8_t r3 : 1; + + uint8_t home : 1; + uint8_t capture : 1; + uint8_t dummy1 : 2; + + uint8_t dpad : 4; + uint8_t dummy2 : 4; + } __attribute__((packed)); + uint32_t val : 24; +} __attribute__((packed)); + +struct SwitchProData { + /* Button and joystick values */ + SwitchProButtons btn; // 0-2 bytes + uint16_t hatValue[4]; // 3-10 bytes +} __attribute__((packed)); + +struct SwitchProOutput { + bool reportChanged; // The data is send when data is received from the controller +} __attribute__((packed)); + +/** This class parses all the data sent by the Switch Pro controller */ +class SwitchProParser { +public: + /** Constructor for the SwitchProParser class. */ + SwitchProParser() { + Reset(); + }; + + /** @name Switch Pro 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. + */ + bool getButtonPress(ButtonEnum b); + bool getButtonClick(ButtonEnum b); + /**@}*/ + /** @name Switch Pro Controller functions */ + /** + * Used to read the analog joystick. + * @param a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY. + * @return Return the analog value in the range of 0-255. + */ + int16_t getAnalogHat(AnalogHatEnum a); + +#if 0 + /** + * Get the angle of the controller calculated using the accelerometer. + * @param a Either ::Pitch or ::Roll. + * @return Return the angle in the range of 0-360. + */ + float getAngle(AngleEnum a) { + if (a == Pitch) + return (atan2f(-ps5Data.accY, -ps5Data.accZ) + PI) * RAD_TO_DEG; + else + return (atan2f(ps5Data.accX, -ps5Data.accZ) + PI) * RAD_TO_DEG; + }; + + /** + * Used to get the raw values from the 3-axis gyroscope and 3-axis accelerometer inside the PS5 controller. + * @param s The sensor to read. + * @return Returns the raw sensor reading. + */ + int16_t getSensor(SensorEnum s) { + switch(s) { + case gX: + return ps5Data.gyroX; + case gY: + return ps5Data.gyroY; + case gZ: + return ps5Data.gyroZ; + case aX: + return ps5Data.accX; + case aY: + return ps5Data.accY; + case aZ: + return ps5Data.accZ; + default: + return 0; + } + }; +#endif + + /** Turn both rumble and the LEDs off. */ + void setAllOff() { + setRumbleOff(); + setLedOff(); + }; + + /** Set rumble off. */ + void setRumbleOff() { + setRumbleOn(0, 0); + } + + /** Toggle rumble. */ + void setRumbleToggle() { + // TODO: Implement this + switchProOutput.reportChanged = true; + } + + /** + * Turn on rumble. + * @param mode Either ::RumbleHigh or ::RumbleLow. + */ + void setRumbleOn(RumbleEnum mode) { + if (mode == RumbleLow) + setRumbleOn(0x00, 0xFF); + else + setRumbleOn(0xFF, 0x00); + } + + /** + * Turn on rumble. + * @param bigRumble Value for big motor. + * @param smallRumble Value for small motor. + */ + void setRumbleOn(uint8_t bigRumble, uint8_t smallRumble) { + // TODO: Implement this + (void)bigRumble; + (void)smallRumble; + switchProOutput.reportChanged = true; + } + + /** + * Set LED value without using the ::LEDEnum. + * @param value See: ::LEDEnum. + */ + void setLedRaw(uint8_t value) { + // TODO: Implement this + (void)value; + switchProOutput.reportChanged = true; + } + + /** Turn all LEDs off. */ + void setLedOff() { + setLedRaw(0); + } + /** + * Turn the specific ::LEDEnum off. + * @param a The ::LEDEnum to turn off. + */ + void setLedOff(LEDEnum a) { + // TODO: Implement this + (void)a; + switchProOutput.reportChanged = true; + } + /** + * Turn the specific ::LEDEnum on. + * @param a The ::LEDEnum to turn on. + */ + void setLedOn(LEDEnum a) { + // TODO: Implement this + (void)a; + switchProOutput.reportChanged = true; + } + /** + * Toggle the specific ::LEDEnum. + * @param a The ::LEDEnum to toggle. + */ + void setLedToggle(LEDEnum a) { + // TODO: Implement this + (void)a; + switchProOutput.reportChanged = true; + } + + /** Get the incoming message count. */ + uint16_t getMessageCounter(){ + return message_counter; + } + +protected: + /** + * Used to parse data sent from the Switch Pro 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 Switch Pro controller. This is implemented in SwitchProBT.h and SwitchProUSB.h. + * @param output Pointer to SwitchProOutput buffer; + */ + virtual void sendOutputReport(SwitchProOutput *output) = 0; + + +private: + static int8_t getButtonIndexSwitchPro(ButtonEnum b); + bool checkDpad(ButtonEnum b); // Used to check Switch Pro DPAD buttons + + SwitchProData switchProData; + SwitchProButtons oldButtonState, buttonClickState; + SwitchProOutput switchProOutput; + uint8_t oldDpad; + uint16_t message_counter = 0; +}; +#endif diff --git a/controllerEnums.h b/controllerEnums.h index f2ec6963..b45d8847 100644 --- a/controllerEnums.h +++ b/controllerEnums.h @@ -181,6 +181,11 @@ enum ButtonEnum { ZL, ZR, /**@}*/ + + /**@{*/ + /** Switch Pro Controller */ + CAPTURE, + /**@}*/ }; inline constexpr int8_t ButtonIndex(ButtonEnum key) { @@ -190,7 +195,7 @@ inline constexpr int8_t ButtonIndex(ButtonEnum key) { (key == RIGHT || key == YELLOW) ? 1 : (key == DOWN || key == GREEN) ? 2 : (key == LEFT || key == ORANGE) ? 3 : - (key == SELECT || key == SHARE || key == BACK || key == VIEW || key == BLUE || key == CREATE) ? 4 : + (key == SELECT || key == SHARE || key == BACK || key == VIEW || key == BLUE || key == CREATE || key == CAPTURE) ? 4 : (key == START || key == OPTIONS || key == MENU || key == PLUS) ? 5 : (key == L3 || key == TWO) ? 6 : (key == R3 || key == ONE) ? 7 : diff --git a/examples/Bluetooth/SwitchPro/SwitchPro.ino b/examples/Bluetooth/SwitchPro/SwitchPro.ino new file mode 100644 index 00000000..eb39d33f --- /dev/null +++ b/examples/Bluetooth/SwitchPro/SwitchPro.ino @@ -0,0 +1,126 @@ +/* + Example sketch for the Switch Pro 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 +#include + +// Satisfy the IDE, which needs to see the include statement in the ino too. +#ifdef dobogusinclude +#include +#endif +#include + +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 SwitchProBT class in two ways */ +// This will start an inquiry and then pair with the Switch Pro controller - you only have to do this once +// You will need to press the Sync button next to the USB connector to put the controller into pairing mode. +SwitchProBT SwitchPro(&Btd, PAIR); + +// After that you can simply create the instance like so and then press a button on the device +//SwitchProBT SwitchPro(&Btd); + +uint16_t lastMessageCounter = -1; +uint32_t home_timer; + +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\nSwitch Pro Bluetooth Library Started")); +} + +void loop() { + Usb.Task(); + + if (SwitchPro.connected() && lastMessageCounter != SwitchPro.getMessageCounter()) { + lastMessageCounter = SwitchPro.getMessageCounter(); + + if (SwitchPro.getAnalogHat(LeftHatX) > 5000 || SwitchPro.getAnalogHat(LeftHatX) < -5000 || + SwitchPro.getAnalogHat(LeftHatY) > 5000 || SwitchPro.getAnalogHat(LeftHatY) < -5000 || + SwitchPro.getAnalogHat(RightHatX) > 5000 || SwitchPro.getAnalogHat(RightHatX) < -5000 || + SwitchPro.getAnalogHat(RightHatY) > 5000 || SwitchPro.getAnalogHat(RightHatY) < -5000) { + Serial.print(F("\r\nLeftHatX: ")); + Serial.print(SwitchPro.getAnalogHat(LeftHatX)); + Serial.print(F("\tLeftHatY: ")); + Serial.print(SwitchPro.getAnalogHat(LeftHatY)); + Serial.print(F("\tRightHatX: ")); + Serial.print(SwitchPro.getAnalogHat(RightHatX)); + Serial.print(F("\tRightHatY: ")); + Serial.print(SwitchPro.getAnalogHat(RightHatY)); + } + + // Hold the HOME button for 1 second to disconnect the controller + // This prevents the controller from disconnecting when it is reconnected, + // as the HOME button is sent when it reconnects + if (SwitchPro.getButtonPress(HOME)) { + if (millis() - home_timer > 1000) + SwitchPro.disconnect(); + } else + home_timer = millis(); + + if (SwitchPro.getButtonClick(HOME)) + Serial.print(F("\r\nHome")); + if (SwitchPro.getButtonClick(CAPTURE)) + Serial.print(F("\r\nCapture")); + if (SwitchPro.getButtonClick(LEFT)) { + /*SwitchPro.setLedOff(); + SwitchPro.setLedOn(LED1);*/ + Serial.print(F("\r\nLeft")); + } + if (SwitchPro.getButtonClick(RIGHT)) { + /*SwitchPro.setLedOff(); + SwitchPro.setLedOn(LED3);*/ + Serial.print(F("\r\nRight")); + } + if (SwitchPro.getButtonClick(DOWN)) { + /*SwitchPro.setLedOff(); + SwitchPro.setLedOn(LED4);*/ + Serial.print(F("\r\nDown")); + } + if (SwitchPro.getButtonClick(UP)) { + /*SwitchPro.setLedOff(); + SwitchPro.setLedOn(LED2);*/ + Serial.print(F("\r\nUp")); + } + + if (SwitchPro.getButtonClick(PLUS)) + Serial.print(F("\r\nPlus")); + if (SwitchPro.getButtonClick(MINUS)) + Serial.print(F("\r\nMinus")); + + if (SwitchPro.getButtonClick(A)) + Serial.print(F("\r\nA")); + if (SwitchPro.getButtonClick(B)) { + //SwitchPro.setRumbleToggle(); + Serial.print(F("\r\nB")); + } + if (SwitchPro.getButtonClick(X)) + Serial.print(F("\r\nX")); + if (SwitchPro.getButtonClick(Y)) + Serial.print(F("\r\nY")); + + if (SwitchPro.getButtonClick(L)) + Serial.print(F("\r\nL")); + if (SwitchPro.getButtonClick(R)) + Serial.print(F("\r\nR")); + if (SwitchPro.getButtonClick(ZL)) + Serial.print(F("\r\nZL")); + if (SwitchPro.getButtonClick(ZR)) + Serial.print(F("\r\nZR")); + if (SwitchPro.getButtonClick(L3)) + Serial.print(F("\r\nL3")); + if (SwitchPro.getButtonClick(R3)) + Serial.print(F("\r\nR3")); + } +} diff --git a/keywords.txt b/keywords.txt index ac952691..e4f5e7d8 100644 --- a/keywords.txt +++ b/keywords.txt @@ -38,6 +38,7 @@ PS4BT KEYWORD1 PS4USB KEYWORD1 PS5BT KEYWORD1 PS5USB KEYWORD1 +SwitchProBT KEYWORD1 #################################################### # Methods and Functions (KEYWORD2) @@ -317,6 +318,7 @@ TopRight LITERAL1 BotRight LITERAL1 TopLeft LITERAL1 BotLeft LITERAL1 +CAPTURE LITERAL1 #################################################### # Methods and Functions for the IR Camera diff --git a/library.json b/library.json index d384b7a9..591af18c 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "USB-Host-Shield-20", - "keywords": "usb, host, ftdi, adk, acm, pl2303, hid, bluetooth, spp, ps3, ps4, ps5, buzz, xbox, wii, mass storage", + "keywords": "usb, host, ftdi, adk, acm, pl2303, hid, bluetooth, spp, ps3, ps4, ps5, buzz, xbox, wii, switch pro, mass storage", "description": "Revision 2.0 of MAX3421E-based USB Host Shield Library", "authors": [ diff --git a/library.properties b/library.properties index 0ccff386..6ab93e66 100644 --- a/library.properties +++ b/library.properties @@ -3,7 +3,7 @@ version=1.5.0 author=Oleg Mazurov (Circuits@Home) , Kristian Sloth Lauszus , Andrew Kroll , Alexei Glushchenko (Circuits@Home) maintainer=Oleg Mazurov (Circuits@Home) , Kristian Sloth Lauszus , Andrew Kroll sentence=Revision 2.0 of MAX3421E-based USB Host Shield Library. -paragraph=Supports HID devices, FTDI, ADK, ACM, PL2303, Bluetooth HID devices, SPP communication and mass storage devices. Furthermore it supports PS3, PS4, PS5, PS Buzz, Wii and Xbox controllers. +paragraph=Supports HID devices, FTDI, ADK, ACM, PL2303, Bluetooth HID devices, SPP communication and mass storage devices. Furthermore it supports PS3, PS4, PS5, PS Buzz, Wii, Switch Pro and Xbox controllers. category=Other url=https://github.com/felis/USB_Host_Shield_2.0 architectures=* From 2501f4c2e35c8c9aa5a8153aee48e32b9ad8bcfa Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 2 May 2021 21:47:17 +0200 Subject: [PATCH 85/93] Can now control the LEDs and rumble on the Switch Pro controller --- SwitchProBT.h | 15 ++- SwitchProParser.cpp | 130 +++++++++++++++++++-- SwitchProParser.h | 112 +++++++++++------- examples/Bluetooth/SwitchPro/SwitchPro.ino | 51 ++++---- keywords.txt | 4 + 5 files changed, 235 insertions(+), 77 deletions(-) diff --git a/SwitchProBT.h b/SwitchProBT.h index d4f39e9e..b3d9507c 100644 --- a/SwitchProBT.h +++ b/SwitchProBT.h @@ -66,8 +66,10 @@ protected: SwitchProParser::Reset(); // Only call this is a user function has not been set - if (!pFuncOnInit) + if (!pFuncOnInit) { setLedOn(LED1); // Turn on the LED1 + setLedHomeOn(); // Turn on the home LED + } }; /** Used to reset the different buffers to there default values */ @@ -77,12 +79,15 @@ protected: /**@}*/ /** @name SwitchProParser implementation */ - virtual void sendOutputReport(SwitchProOutput *output) { - output->reportChanged = false; -#if 0 + virtual void sendOutputReport(uint8_t *data, uint8_t len) { + uint8_t buf[1 /* BT DATA Output Report */ + len]; + + // Send as a Bluetooth HID DATA output report on the interrupt channel + buf[0] = 0xA2; // HID BT DATA (0xA0) | Report Type (Output 0x02) + memcpy(&buf[1], data, len); + // Send the Bluetooth DATA output report on the interrupt channel pBtd->L2CAP_Command(hci_handle, buf, sizeof(buf), interrupt_scid[0], interrupt_scid[1]); -#endif }; /**@}*/ }; diff --git a/SwitchProParser.cpp b/SwitchProParser.cpp index 916a526c..73924ffe 100644 --- a/SwitchProParser.cpp +++ b/SwitchProParser.cpp @@ -84,8 +84,10 @@ void SwitchProParser::Parse(uint8_t len, uint8_t *buf) { } #endif - if (buf[0] == 0x3F) // Check report ID + if (buf[0] == 0x3F) // Simple input report memcpy(&switchProData, buf + 1, min((uint8_t)(len - 1), MFK_CASTUINT8T sizeof(switchProData))); + else if (buf[0] == 0x21) // Subcommand reply + return; else { #ifdef DEBUG_USB_HOST Notify(PSTR("\r\nUnknown report id: "), 0x80); @@ -125,8 +127,114 @@ void SwitchProParser::Parse(uint8_t len, uint8_t *buf) { message_counter++; } - if (switchProOutput.reportChanged) - sendOutputReport(&switchProOutput); // Send output report + if (switchProOutput.ledReportChanged || switchProOutput.ledHomeReportChanged) + sendLedOutputReport(); // Send output report + else if (switchProOutput.leftRumbleOn || switchProOutput.rightRumbleOn) { + // We need to send the rumble report repeatedly to keep it on + uint32_t now = millis(); + if (now - rumble_on_timer > 1000) { + rumble_on_timer = now; + sendRumbleOutputReport(); + } + } else + rumble_on_timer = 0; +} + +void SwitchProParser::sendLedOutputReport() { + // See: https://github.com/Dan611/hid-procon + // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering + // https://github.com/HisashiKato/USB_Host_Shield_Library_2.0_BTXBOX/blob/master/src/SWProBTParser.h#L152-L153 + uint8_t buf[14] = { 0 }; + buf[0x00] = 0x01; // Report ID - PROCON_CMD_AND_RUMBLE + buf[0x01] = output_sequence_counter++; // Lowest 4-bit is a sequence number, which needs to be increased for every report + + // Left rumble data + if (switchProOutput.leftRumbleOn) { + buf[0x02 + 0] = 0x28; + buf[0x02 + 1] = 0x88; + buf[0x02 + 2] = 0x60; + buf[0x02 + 3] = 0x61; + } else { + buf[0x02 + 0] = 0x00; + buf[0x02 + 1] = 0x01; + buf[0x02 + 2] = 0x40; + buf[0x02 + 3] = 0x40; + } + + // Right rumble data + if (switchProOutput.rightRumbleOn) { + buf[0x02 + 4] = 0x28; + buf[0x02 + 5] = 0x88; + buf[0x02 + 6] = 0x60; + buf[0x02 + 7] = 0x61; + } else { + buf[0x02 + 4] = 0x00; + buf[0x02 + 5] = 0x01; + buf[0x02 + 6] = 0x40; + buf[0x02 + 7] = 0x40; + } + + // Sub commands + if (switchProOutput.ledReportChanged) { + switchProOutput.ledReportChanged = false; + + // See: https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/bluetooth_hid_subcommands_notes.md#subcommand-0x30-set-player-lights + buf[0x0A + 0] = 0x30; // PROCON_CMD_LED + + buf[0x0A + 1] = switchProOutput.ledMask; // Lower 4-bits sets the LEDs constantly on, the higher 4-bits can be used to flash the LEDs + + sendOutputReport(buf, 10 + 2); + } else if (switchProOutput.ledHomeReportChanged) { + switchProOutput.ledHomeReportChanged = false; + + // It is possible set up to 15 mini cycles, but we simply just set the LED constantly on/off + // See: https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/bluetooth_hid_subcommands_notes.md#subcommand-0x38-set-home-light + buf[0x0A + 0] = 0x38; // PROCON_CMD_LED_HOME + + buf[0x0A + 1] = (0 /* Number of cycles */ << 4) | (switchProOutput.ledHome ? 0xF : 0) /* Global mini cycle duration */; + buf[0x0A + 2] = (0xF /* LED start intensity */ << 4) | 0x0 /* Number of full cycles */; + buf[0x0A + 3] = (0xF /* Mini Cycle 1 LED intensity */ << 4) | 0x0 /* Mini Cycle 2 LED intensity */; + + sendOutputReport(buf, 10 + 4); + } +} + +void SwitchProParser::sendRumbleOutputReport() { + // See: https://github.com/Dan611/hid-procon + // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering + // https://github.com/HisashiKato/USB_Host_Shield_Library_2.0_BTXBOX/blob/master/src/SWProBTParser.h#L152-L153 + + uint8_t buf[10] = { 0 }; + buf[0x00] = 0x10; // Report ID - PROCON_CMD_RUMBLE_ONLY + buf[0x01] = output_sequence_counter++; // Lowest 4-bit is a sequence number, which needs to be increased for every report + + // Left rumble data + if (switchProOutput.leftRumbleOn) { + buf[0x02 + 0] = 0x28; + buf[0x02 + 1] = 0x88; + buf[0x02 + 2] = 0x60; + buf[0x02 + 3] = 0x61; + } else { + buf[0x02 + 0] = 0x00; + buf[0x02 + 1] = 0x01; + buf[0x02 + 2] = 0x40; + buf[0x02 + 3] = 0x40; + } + + // Right rumble data + if (switchProOutput.rightRumbleOn) { + buf[0x02 + 4] = 0x28; + buf[0x02 + 5] = 0x88; + buf[0x02 + 6] = 0x60; + buf[0x02 + 7] = 0x61; + } else { + buf[0x02 + 4] = 0x00; + buf[0x02 + 5] = 0x01; + buf[0x02 + 6] = 0x40; + buf[0x02 + 7] = 0x40; + } + + sendOutputReport(buf, 10); } void SwitchProParser::Reset() { @@ -139,13 +247,13 @@ void SwitchProParser::Reset() { oldButtonState.dpad = DPAD_OFF; buttonClickState.dpad = 0; oldDpad = 0; + output_sequence_counter = 0; + rumble_on_timer = 0; -#if 0 - ps5Output.bigRumble = ps5Output.smallRumble = 0; - ps5Output.microphoneLed = 0; - ps5Output.disableLeds = 0; - ps5Output.playerLeds = 0; - ps5Output.r = ps5Output.g = ps5Output.b = 0; -#endif - switchProOutput.reportChanged = false; + switchProOutput.leftRumbleOn = false; + switchProOutput.rightRumbleOn = false; + switchProOutput.ledMask = 0; + switchProOutput.ledHome = false; + switchProOutput.ledReportChanged = false; + switchProOutput.ledHomeReportChanged = false; }; diff --git a/SwitchProParser.h b/SwitchProParser.h index d44630d1..c600147a 100644 --- a/SwitchProParser.h +++ b/SwitchProParser.h @@ -21,6 +21,22 @@ #include "Usb.h" #include "controllerEnums.h" +/** Used to set the LEDs on the controller */ +const uint8_t SWITCH_PRO_LEDS[] PROGMEM = { + 0x00, // OFF + 0x01, // LED1 + 0x02, // LED2 + 0x04, // LED3 + 0x08, // LED4 + + 0x09, // LED5 + 0x0A, // LED6 + 0x0C, // LED7 + 0x0D, // LED8 + 0x0E, // LED9 + 0x0F, // LED10 +}; + /** Buttons on the controller */ const uint8_t SWITCH_PRO_BUTTONS[] PROGMEM = { 0x10, // UP @@ -82,14 +98,21 @@ struct SwitchProData { } __attribute__((packed)); struct SwitchProOutput { - bool reportChanged; // The data is send when data is received from the controller + bool leftRumbleOn; + bool rightRumbleOn; + uint8_t ledMask; // Higher nibble flashes the LEDs, lower nibble set them on/off + bool ledHome; + + // Used to only send the report when the state changes + bool ledReportChanged; + bool ledHomeReportChanged; } __attribute__((packed)); /** This class parses all the data sent by the Switch Pro controller */ class SwitchProParser { public: /** Constructor for the SwitchProParser class. */ - SwitchProParser() { + SwitchProParser() : output_sequence_counter(0) { Reset(); }; @@ -111,7 +134,7 @@ public: /** * Used to read the analog joystick. * @param a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY. - * @return Return the analog value in the range of 0-255. + * @return Return the analog value as a signed 16-bit value. */ int16_t getAnalogHat(AnalogHatEnum a); @@ -157,6 +180,7 @@ public: void setAllOff() { setRumbleOff(); setLedOff(); + setLedHomeOff(); }; /** Set rumble off. */ @@ -166,73 +190,78 @@ public: /** Toggle rumble. */ void setRumbleToggle() { - // TODO: Implement this - switchProOutput.reportChanged = true; + setRumbleOn(!switchProOutput.leftRumbleOn, !switchProOutput.rightRumbleOn); } /** * Turn on rumble. - * @param mode Either ::RumbleHigh or ::RumbleLow. + * @param leftRumbleOn Turn on left rumble motor. + * @param rightRumbleOn Turn on right rumble motor. */ - void setRumbleOn(RumbleEnum mode) { - if (mode == RumbleLow) - setRumbleOn(0x00, 0xFF); - else - setRumbleOn(0xFF, 0x00); - } - - /** - * Turn on rumble. - * @param bigRumble Value for big motor. - * @param smallRumble Value for small motor. - */ - void setRumbleOn(uint8_t bigRumble, uint8_t smallRumble) { - // TODO: Implement this - (void)bigRumble; - (void)smallRumble; - switchProOutput.reportChanged = true; + void setRumbleOn(bool leftRumbleOn, bool rightRumbleOn) { + switchProOutput.leftRumbleOn = leftRumbleOn; + switchProOutput.rightRumbleOn = rightRumbleOn; + switchProOutput.ledReportChanged = true; // Set this, so the rumble effect gets changed immediately } /** * Set LED value without using the ::LEDEnum. + * This can also be used to flash the LEDs by setting the high 4-bits of the mask. * @param value See: ::LEDEnum. */ - void setLedRaw(uint8_t value) { - // TODO: Implement this - (void)value; - switchProOutput.reportChanged = true; + void setLedRaw(uint8_t mask) { + switchProOutput.ledMask = mask; + switchProOutput.ledReportChanged = true; } /** Turn all LEDs off. */ void setLedOff() { setLedRaw(0); } + /** * Turn the specific ::LEDEnum off. * @param a The ::LEDEnum to turn off. */ void setLedOff(LEDEnum a) { - // TODO: Implement this - (void)a; - switchProOutput.reportChanged = true; + switchProOutput.ledMask &= ~((uint8_t)(pgm_read_byte(&SWITCH_PRO_LEDS[(uint8_t)a]) & 0x0f)); + switchProOutput.ledReportChanged = true; } + /** * Turn the specific ::LEDEnum on. * @param a The ::LEDEnum to turn on. */ void setLedOn(LEDEnum a) { - // TODO: Implement this - (void)a; - switchProOutput.reportChanged = true; + switchProOutput.ledMask |= (uint8_t)(pgm_read_byte(&SWITCH_PRO_LEDS[(uint8_t)a]) & 0x0f); + switchProOutput.ledReportChanged = true; } + /** * Toggle the specific ::LEDEnum. * @param a The ::LEDEnum to toggle. */ void setLedToggle(LEDEnum a) { - // TODO: Implement this - (void)a; - switchProOutput.reportChanged = true; + switchProOutput.ledMask ^= (uint8_t)(pgm_read_byte(&SWITCH_PRO_LEDS[(uint8_t)a]) & 0x0f); + switchProOutput.ledReportChanged = true; + } + + /** Turn home LED off. */ + void setLedHomeOff() { + switchProOutput.ledHome = false; + switchProOutput.ledHomeReportChanged = true; + } + + /** Turn home LED on. */ + void setLedHomeOn() { + switchProOutput.ledHome = true; + switchProOutput.ledHomeReportChanged = true; + } + + /** Toggle home LED. */ + void setLedHomeToggle() { + switchProOutput.ledHome = !switchProOutput.ledHome; + switchProOutput.ledHomeReportChanged = true; } /** Get the incoming message count. */ @@ -253,19 +282,24 @@ protected: /** * Send the output to the Switch Pro controller. This is implemented in SwitchProBT.h and SwitchProUSB.h. - * @param output Pointer to SwitchProOutput buffer; + * @param data Pointer to buffer to send by the derived class. + * @param len Length of buffer. */ - virtual void sendOutputReport(SwitchProOutput *output) = 0; - + virtual void sendOutputReport(uint8_t *data, uint8_t len) = 0; private: static int8_t getButtonIndexSwitchPro(ButtonEnum b); bool checkDpad(ButtonEnum b); // Used to check Switch Pro DPAD buttons + void sendLedOutputReport(); + void sendRumbleOutputReport(); + SwitchProData switchProData; SwitchProButtons oldButtonState, buttonClickState; SwitchProOutput switchProOutput; uint8_t oldDpad; uint16_t message_counter = 0; + uint8_t output_sequence_counter : 4; + uint32_t rumble_on_timer = 0; }; #endif diff --git a/examples/Bluetooth/SwitchPro/SwitchPro.ino b/examples/Bluetooth/SwitchPro/SwitchPro.ino index eb39d33f..add2d002 100644 --- a/examples/Bluetooth/SwitchPro/SwitchPro.ino +++ b/examples/Bluetooth/SwitchPro/SwitchPro.ino @@ -26,7 +26,7 @@ SwitchProBT SwitchPro(&Btd, PAIR); //SwitchProBT SwitchPro(&Btd); uint16_t lastMessageCounter = -1; -uint32_t home_timer; +uint32_t capture_timer; void setup() { Serial.begin(115200); @@ -60,55 +60,62 @@ void loop() { Serial.print(SwitchPro.getAnalogHat(RightHatY)); } - // Hold the HOME button for 1 second to disconnect the controller + // Hold the CAPTURE button for 1 second to disconnect the controller // This prevents the controller from disconnecting when it is reconnected, - // as the HOME button is sent when it reconnects - if (SwitchPro.getButtonPress(HOME)) { - if (millis() - home_timer > 1000) + // as the CAPTURE button is sent when it reconnects + if (SwitchPro.getButtonPress(CAPTURE)) { + if (millis() - capture_timer > 1000) SwitchPro.disconnect(); } else - home_timer = millis(); + capture_timer = millis(); - if (SwitchPro.getButtonClick(HOME)) - Serial.print(F("\r\nHome")); if (SwitchPro.getButtonClick(CAPTURE)) Serial.print(F("\r\nCapture")); + if (SwitchPro.getButtonClick(HOME)) { + Serial.print(F("\r\nHome")); + SwitchPro.setLedHomeToggle(); // Toggle the home LED + } + if (SwitchPro.getButtonClick(LEFT)) { - /*SwitchPro.setLedOff(); - SwitchPro.setLedOn(LED1);*/ + SwitchPro.setLedOff(); + SwitchPro.setLedOn(LED1); Serial.print(F("\r\nLeft")); } + if (SwitchPro.getButtonClick(UP)) { + SwitchPro.setLedOff(); + SwitchPro.setLedOn(LED2); + Serial.print(F("\r\nUp")); + } if (SwitchPro.getButtonClick(RIGHT)) { - /*SwitchPro.setLedOff(); - SwitchPro.setLedOn(LED3);*/ + SwitchPro.setLedOff(); + SwitchPro.setLedOn(LED3); Serial.print(F("\r\nRight")); } if (SwitchPro.getButtonClick(DOWN)) { - /*SwitchPro.setLedOff(); - SwitchPro.setLedOn(LED4);*/ + SwitchPro.setLedOff(); + SwitchPro.setLedOn(LED4); Serial.print(F("\r\nDown")); } - if (SwitchPro.getButtonClick(UP)) { - /*SwitchPro.setLedOff(); - SwitchPro.setLedOn(LED2);*/ - Serial.print(F("\r\nUp")); - } if (SwitchPro.getButtonClick(PLUS)) Serial.print(F("\r\nPlus")); if (SwitchPro.getButtonClick(MINUS)) Serial.print(F("\r\nMinus")); - if (SwitchPro.getButtonClick(A)) + if (SwitchPro.getButtonClick(A)) { + SwitchPro.setRumbleOn(false, true); // Turn on the right rumble motor Serial.print(F("\r\nA")); + } if (SwitchPro.getButtonClick(B)) { - //SwitchPro.setRumbleToggle(); + SwitchPro.setRumbleOn(true, false); // Turn on the left rumble motor Serial.print(F("\r\nB")); } if (SwitchPro.getButtonClick(X)) Serial.print(F("\r\nX")); - if (SwitchPro.getButtonClick(Y)) + if (SwitchPro.getButtonClick(Y)) { + SwitchPro.setRumbleOn(false, false); Serial.print(F("\r\nY")); + } if (SwitchPro.getButtonClick(L)) Serial.print(F("\r\nL")); diff --git a/keywords.txt b/keywords.txt index e4f5e7d8..827432d3 100644 --- a/keywords.txt +++ b/keywords.txt @@ -71,6 +71,10 @@ setLedFlash KEYWORD2 moveSetBulb KEYWORD2 moveSetRumble KEYWORD2 +setLedHomeOff KEYWORD2 +setLedHomeOn KEYWORD2 +setLedHomeToggle KEYWORD2 + attachOnInit KEYWORD2 PS3Connected KEYWORD2 From 7e96d9fb09dc68e2ec19bcc08e273292d7621653 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 2 May 2021 22:34:49 +0200 Subject: [PATCH 86/93] Expose API for controlling the left and right rumble individually --- SwitchProParser.h | 30 +++++++++++++++++----- examples/Bluetooth/SwitchPro/SwitchPro.ino | 30 ++++++++++++---------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/SwitchProParser.h b/SwitchProParser.h index c600147a..c8336eb7 100644 --- a/SwitchProParser.h +++ b/SwitchProParser.h @@ -185,25 +185,43 @@ public: /** Set rumble off. */ void setRumbleOff() { - setRumbleOn(0, 0); + setRumble(false, false); } /** Toggle rumble. */ void setRumbleToggle() { - setRumbleOn(!switchProOutput.leftRumbleOn, !switchProOutput.rightRumbleOn); + setRumble(!switchProOutput.leftRumbleOn, !switchProOutput.rightRumbleOn); } /** - * Turn on rumble. - * @param leftRumbleOn Turn on left rumble motor. - * @param rightRumbleOn Turn on right rumble motor. + * Turn on/off rumble. + * @param leftRumbleOn Turn on/off left rumble motor. + * @param rightRumbleOn Turn on/off right rumble motor. */ - void setRumbleOn(bool leftRumbleOn, bool rightRumbleOn) { + void setRumble(bool leftRumbleOn, bool rightRumbleOn) { switchProOutput.leftRumbleOn = leftRumbleOn; switchProOutput.rightRumbleOn = rightRumbleOn; switchProOutput.ledReportChanged = true; // Set this, so the rumble effect gets changed immediately } + /** + * Turn on/off the left rumble. + * @param on Turn on/off left rumble motor. + */ + void setRumbleLeft(bool on) { + switchProOutput.leftRumbleOn = on; + switchProOutput.ledReportChanged = true; // Set this, so the rumble effect gets changed immediately + } + + /** + * Turn on/off the right rumble. + * @param on Turn on/off right rumble motor. + */ + void setRumbleRight(bool on) { + switchProOutput.rightRumbleOn = on; + switchProOutput.ledReportChanged = true; // Set this, so the rumble effect gets changed immediately + } + /** * Set LED value without using the ::LEDEnum. * This can also be used to flash the LEDs by setting the high 4-bits of the mask. diff --git a/examples/Bluetooth/SwitchPro/SwitchPro.ino b/examples/Bluetooth/SwitchPro/SwitchPro.ino index add2d002..75a0eb83 100644 --- a/examples/Bluetooth/SwitchPro/SwitchPro.ino +++ b/examples/Bluetooth/SwitchPro/SwitchPro.ino @@ -73,7 +73,7 @@ void loop() { Serial.print(F("\r\nCapture")); if (SwitchPro.getButtonClick(HOME)) { Serial.print(F("\r\nHome")); - SwitchPro.setLedHomeToggle(); // Toggle the home LED + SwitchPro.setLedHomeToggle(); } if (SwitchPro.getButtonClick(LEFT)) { @@ -102,29 +102,31 @@ void loop() { if (SwitchPro.getButtonClick(MINUS)) Serial.print(F("\r\nMinus")); - if (SwitchPro.getButtonClick(A)) { - SwitchPro.setRumbleOn(false, true); // Turn on the right rumble motor + if (SwitchPro.getButtonClick(A)) Serial.print(F("\r\nA")); - } - if (SwitchPro.getButtonClick(B)) { - SwitchPro.setRumbleOn(true, false); // Turn on the left rumble motor + if (SwitchPro.getButtonClick(B)) Serial.print(F("\r\nB")); - } if (SwitchPro.getButtonClick(X)) Serial.print(F("\r\nX")); - if (SwitchPro.getButtonClick(Y)) { - SwitchPro.setRumbleOn(false, false); + if (SwitchPro.getButtonClick(Y)) Serial.print(F("\r\nY")); - } - if (SwitchPro.getButtonClick(L)) + if (SwitchPro.getButtonClick(L)) { + SwitchPro.setRumbleLeft(false); Serial.print(F("\r\nL")); - if (SwitchPro.getButtonClick(R)) + } + if (SwitchPro.getButtonClick(R)) { + SwitchPro.setRumbleRight(false); Serial.print(F("\r\nR")); - if (SwitchPro.getButtonClick(ZL)) + } + if (SwitchPro.getButtonClick(ZL)) { + SwitchPro.setRumbleLeft(true); Serial.print(F("\r\nZL")); - if (SwitchPro.getButtonClick(ZR)) + } + if (SwitchPro.getButtonClick(ZR)) { + SwitchPro.setRumbleRight(true); Serial.print(F("\r\nZR")); + } if (SwitchPro.getButtonClick(L3)) Serial.print(F("\r\nL3")); if (SwitchPro.getButtonClick(R3)) From f19f610081a6562d08c3b2943817ce6bbe3b257f Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Mon, 3 May 2021 17:48:59 +0200 Subject: [PATCH 87/93] Renamed Bluetooth example --- .github/workflows/main.yml | 2 +- .../{SwitchPro/SwitchPro.ino => SwitchProBT/SwitchProBT.ino} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename examples/Bluetooth/{SwitchPro/SwitchPro.ino => SwitchProBT/SwitchProBT.ino} (100%) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 67891798..d93b5e35 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: # find examples -type f -name "*.ino" | rev | cut -d/ -f2- | rev | sort | sed -z 's/\n/, /g' - example: [examples/ambx, examples/acm/acm_terminal, examples/adk/adk_barcode, examples/adk/ArduinoBlinkLED, examples/adk/demokit_20, examples/adk/term_test, examples/adk/term_time, examples/Bluetooth/BTHID, examples/Bluetooth/PS3BT, examples/Bluetooth/PS3Multi, examples/Bluetooth/PS3SPP, examples/Bluetooth/PS4BT, examples/Bluetooth/PS5BT, examples/Bluetooth/SPP, examples/Bluetooth/SPPMulti, examples/Bluetooth/SwitchPro, examples/Bluetooth/Wii, examples/Bluetooth/WiiBalanceBoard, examples/Bluetooth/WiiIRCamera, examples/Bluetooth/WiiMulti, examples/Bluetooth/WiiUProController, examples/board_qc, examples/cdc_XR21B1411/XR_terminal, examples/ftdi/USBFTDILoopback, examples/GPIO/Blink, examples/GPIO/Blink_LowLevel, examples/GPIO/Input, examples/HID/le3dp, examples/HID/scale, examples/HID/SRWS1, examples/HID/t16km, examples/HID/USBHIDBootKbd, examples/HID/USBHIDBootKbdAndMouse, examples/HID/USBHIDBootMouse, examples/HID/USBHID_desc, examples/HID/USBHIDJoystick, examples/HID/USBHIDMultimediaKbd, examples/hub_demo, examples/max_LCD, examples/pl2303/pl2303_gprs_terminal, examples/pl2303/pl2303_gps, examples/pl2303/pl2303_tinygps, examples/pl2303/pl2303_xbee_terminal, examples/PS3USB, examples/PS4USB, examples/PS5USB, examples/PSBuzz, examples/USB_desc, examples/USBH_MIDI/bidirectional_converter, examples/USBH_MIDI/eVY1_sample, examples/USBH_MIDI/USBH_MIDI_dump, examples/USBH_MIDI/USB_MIDI_converter, examples/USBH_MIDI/USB_MIDI_converter_multi, examples/Xbox/XBOXOLD, examples/Xbox/XBOXONE, examples/Xbox/XBOXONESBT, examples/Xbox/XBOXRECV, examples/Xbox/XBOXUSB] + example: [examples/ambx, examples/acm/acm_terminal, examples/adk/adk_barcode, examples/adk/ArduinoBlinkLED, examples/adk/demokit_20, examples/adk/term_test, examples/adk/term_time, examples/Bluetooth/BTHID, examples/Bluetooth/PS3BT, examples/Bluetooth/PS3Multi, examples/Bluetooth/PS3SPP, examples/Bluetooth/PS4BT, examples/Bluetooth/PS5BT, examples/Bluetooth/SPP, examples/Bluetooth/SPPMulti, examples/Bluetooth/SwitchProBT, examples/Bluetooth/Wii, examples/Bluetooth/WiiBalanceBoard, examples/Bluetooth/WiiIRCamera, examples/Bluetooth/WiiMulti, examples/Bluetooth/WiiUProController, examples/board_qc, examples/cdc_XR21B1411/XR_terminal, examples/ftdi/USBFTDILoopback, examples/GPIO/Blink, examples/GPIO/Blink_LowLevel, examples/GPIO/Input, examples/HID/le3dp, examples/HID/scale, examples/HID/SRWS1, examples/HID/t16km, examples/HID/USBHIDBootKbd, examples/HID/USBHIDBootKbdAndMouse, examples/HID/USBHIDBootMouse, examples/HID/USBHID_desc, examples/HID/USBHIDJoystick, examples/HID/USBHIDMultimediaKbd, examples/hub_demo, examples/max_LCD, examples/pl2303/pl2303_gprs_terminal, examples/pl2303/pl2303_gps, examples/pl2303/pl2303_tinygps, examples/pl2303/pl2303_xbee_terminal, examples/PS3USB, examples/PS4USB, examples/PS5USB, examples/PSBuzz, examples/USB_desc, examples/USBH_MIDI/bidirectional_converter, examples/USBH_MIDI/eVY1_sample, examples/USBH_MIDI/USBH_MIDI_dump, examples/USBH_MIDI/USB_MIDI_converter, examples/USBH_MIDI/USB_MIDI_converter_multi, examples/Xbox/XBOXOLD, examples/Xbox/XBOXONE, examples/Xbox/XBOXONESBT, examples/Xbox/XBOXRECV, examples/Xbox/XBOXUSB] steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 diff --git a/examples/Bluetooth/SwitchPro/SwitchPro.ino b/examples/Bluetooth/SwitchProBT/SwitchProBT.ino similarity index 100% rename from examples/Bluetooth/SwitchPro/SwitchPro.ino rename to examples/Bluetooth/SwitchProBT/SwitchProBT.ino From 2dfb76709351444a7fa32f67124c6bc8322de297 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Mon, 3 May 2021 22:34:46 +0200 Subject: [PATCH 88/93] The Switch Pro controller now works via USB and we can now read the IMU data as well --- .github/workflows/main.yml | 2 +- README.md | 18 ++- SwitchProParser.cpp | 152 +++++++++--------- SwitchProParser.h | 115 ++++++++----- SwitchProUSB.h | 145 +++++++++++++++++ .../Bluetooth/SwitchProBT/SwitchProBT.ino | 22 ++- examples/SwitchProUSB/SwitchProUSB.ino | 128 +++++++++++++++ keywords.txt | 4 + 8 files changed, 464 insertions(+), 122 deletions(-) create mode 100644 SwitchProUSB.h create mode 100644 examples/SwitchProUSB/SwitchProUSB.ino diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d93b5e35..668b2b61 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: # find examples -type f -name "*.ino" | rev | cut -d/ -f2- | rev | sort | sed -z 's/\n/, /g' - example: [examples/ambx, examples/acm/acm_terminal, examples/adk/adk_barcode, examples/adk/ArduinoBlinkLED, examples/adk/demokit_20, examples/adk/term_test, examples/adk/term_time, examples/Bluetooth/BTHID, examples/Bluetooth/PS3BT, examples/Bluetooth/PS3Multi, examples/Bluetooth/PS3SPP, examples/Bluetooth/PS4BT, examples/Bluetooth/PS5BT, examples/Bluetooth/SPP, examples/Bluetooth/SPPMulti, examples/Bluetooth/SwitchProBT, examples/Bluetooth/Wii, examples/Bluetooth/WiiBalanceBoard, examples/Bluetooth/WiiIRCamera, examples/Bluetooth/WiiMulti, examples/Bluetooth/WiiUProController, examples/board_qc, examples/cdc_XR21B1411/XR_terminal, examples/ftdi/USBFTDILoopback, examples/GPIO/Blink, examples/GPIO/Blink_LowLevel, examples/GPIO/Input, examples/HID/le3dp, examples/HID/scale, examples/HID/SRWS1, examples/HID/t16km, examples/HID/USBHIDBootKbd, examples/HID/USBHIDBootKbdAndMouse, examples/HID/USBHIDBootMouse, examples/HID/USBHID_desc, examples/HID/USBHIDJoystick, examples/HID/USBHIDMultimediaKbd, examples/hub_demo, examples/max_LCD, examples/pl2303/pl2303_gprs_terminal, examples/pl2303/pl2303_gps, examples/pl2303/pl2303_tinygps, examples/pl2303/pl2303_xbee_terminal, examples/PS3USB, examples/PS4USB, examples/PS5USB, examples/PSBuzz, examples/USB_desc, examples/USBH_MIDI/bidirectional_converter, examples/USBH_MIDI/eVY1_sample, examples/USBH_MIDI/USBH_MIDI_dump, examples/USBH_MIDI/USB_MIDI_converter, examples/USBH_MIDI/USB_MIDI_converter_multi, examples/Xbox/XBOXOLD, examples/Xbox/XBOXONE, examples/Xbox/XBOXONESBT, examples/Xbox/XBOXRECV, examples/Xbox/XBOXUSB] + example: [examples/ambx, examples/acm/acm_terminal, examples/adk/adk_barcode, examples/adk/ArduinoBlinkLED, examples/adk/demokit_20, examples/adk/term_test, examples/adk/term_time, examples/Bluetooth/BTHID, examples/Bluetooth/PS3BT, examples/Bluetooth/PS3Multi, examples/Bluetooth/PS3SPP, examples/Bluetooth/PS4BT, examples/Bluetooth/PS5BT, examples/Bluetooth/SPP, examples/Bluetooth/SPPMulti, examples/Bluetooth/SwitchProBT, examples/Bluetooth/Wii, examples/Bluetooth/WiiBalanceBoard, examples/Bluetooth/WiiIRCamera, examples/Bluetooth/WiiMulti, examples/Bluetooth/WiiUProController, examples/board_qc, examples/cdc_XR21B1411/XR_terminal, examples/ftdi/USBFTDILoopback, examples/GPIO/Blink, examples/GPIO/Blink_LowLevel, examples/GPIO/Input, examples/HID/le3dp, examples/HID/scale, examples/HID/SRWS1, examples/HID/t16km, examples/HID/USBHIDBootKbd, examples/HID/USBHIDBootKbdAndMouse, examples/HID/USBHIDBootMouse, examples/HID/USBHID_desc, examples/HID/USBHIDJoystick, examples/HID/USBHIDMultimediaKbd, examples/hub_demo, examples/max_LCD, examples/pl2303/pl2303_gprs_terminal, examples/pl2303/pl2303_gps, examples/pl2303/pl2303_tinygps, examples/pl2303/pl2303_xbee_terminal, examples/PS3USB, examples/PS4USB, examples/PS5USB, examples/PSBuzz, examples/SwitchProUSB, examples/USB_desc, examples/USBH_MIDI/bidirectional_converter, examples/USBH_MIDI/eVY1_sample, examples/USBH_MIDI/USBH_MIDI_dump, examples/USBH_MIDI/USB_MIDI_converter, examples/USBH_MIDI/USB_MIDI_converter_multi, examples/Xbox/XBOXOLD, examples/Xbox/XBOXONE, examples/Xbox/XBOXONESBT, examples/Xbox/XBOXRECV, examples/Xbox/XBOXUSB] steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 diff --git a/README.md b/README.md index 3d230cf6..df855097 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ For more information about the hardware see the [Hardware Manual](https://chome. * __Alexei Glushchenko__ - * Developers of the USB Core, HID, FTDI, ADK, ACM, and PL2303 libraries * __Kristian Sloth Lauszus__ - - * Developer of the [BTD](#bluetooth-libraries), [BTHID](#bthid-library), [SPP](#spp-library), [PS5](#ps5-library), [PS4](#ps4-library), [PS3](#ps3-library), [Wii](#wii-library), [Xbox](#xbox-library), and [PSBuzz](#ps-buzz-library) libraries + * Developer of the [BTD](#bluetooth-libraries), [BTHID](#bthid-library), [SPP](#spp-library), [PS5](#ps5-library), [PS4](#ps4-library), [PS3](#ps3-library), [Wii](#wii-library), [Switch Pro](#switch-pro-library), [Xbox](#xbox-library), and [PSBuzz](#ps-buzz-library) libraries * __Andrew Kroll__ - * Major contributor to mass storage code * __guruthree__ @@ -55,6 +55,7 @@ For more information about the hardware see the [Hardware Manual](https://chome. * [Xbox ONE Library](#xbox-one-library) * [Xbox ONE S Library](#xbox-one-s-library) * [Wii library](#wii-library) + * [Switch Pro Library](#switch-pro-library) * [PS Buzz Library](#ps-buzz-library) * [HID Libraries](#hid-libraries) * [MIDI Library](#midi-library) @@ -330,6 +331,21 @@ All the information about the Wii controllers are from these sites: * * The old library created by _Tomoyuki Tanaka_: also helped a lot. +### Switch Pro Library + +The Switch Pro library is split up into the [SwitchProBT](SwitchProBT.h) and the [SwitchProUSB](SwitchProUSB.h) library. These allow you to use the Nintendo Switch Pro controller via Bluetooth and USB. + +The [SwitchProBT.ino](examples/Bluetooth/SwitchProBT/SwitchProBT.ino) and [SwitchProUSB.ino](examples/SwitchProUSB/SwitchProUSB.ino) examples shows how to easily read the buttons, joysticks and IMU on the controller via Bluetooth and USB respectively. It is also possible to control the rumble and LEDs on the controller. + +To pair with the Switch Pro controller via Bluetooth you need create the SwitchProBT instance like so: ```SwitchProBT SwitchPro(&Btd, PAIR);``` and then press the Sync button next to the USB connector to put the controller into pairing mode. + +It should then automatically pair the dongle with your controller. This only have to be done once. + +All the information about the controller are from these sites: + +* +* + ### [PS Buzz Library](PSBuzz.cpp) This library implements support for the Playstation Buzz controllers via USB. diff --git a/SwitchProParser.cpp b/SwitchProParser.cpp index 73924ffe..10c9155a 100644 --- a/SwitchProParser.cpp +++ b/SwitchProParser.cpp @@ -17,18 +17,6 @@ #include "SwitchProParser.h" -enum DPADEnum { - DPAD_UP = 0x0, - DPAD_UP_RIGHT = 0x1, - DPAD_RIGHT = 0x2, - DPAD_RIGHT_DOWN = 0x3, - DPAD_DOWN = 0x4, - DPAD_DOWN_LEFT = 0x5, - DPAD_LEFT = 0x6, - DPAD_LEFT_UP = 0x7, - DPAD_OFF = 0x8, -}; - // To enable serial debugging see "settings.h" //#define PRINTREPORT // Uncomment to print the report send by the Switch Pro Controller @@ -38,27 +26,9 @@ int8_t SwitchProParser::getButtonIndexSwitchPro(ButtonEnum b) { return index; } -bool SwitchProParser::checkDpad(ButtonEnum b) { - switch (b) { - case UP: - return switchProData.btn.dpad == DPAD_LEFT_UP || switchProData.btn.dpad == DPAD_UP || switchProData.btn.dpad == DPAD_UP_RIGHT; - case RIGHT: - return switchProData.btn.dpad == DPAD_UP_RIGHT || switchProData.btn.dpad == DPAD_RIGHT || switchProData.btn.dpad == DPAD_RIGHT_DOWN; - case DOWN: - return switchProData.btn.dpad == DPAD_RIGHT_DOWN || switchProData.btn.dpad == DPAD_DOWN || switchProData.btn.dpad == DPAD_DOWN_LEFT; - case LEFT: - return switchProData.btn.dpad == DPAD_DOWN_LEFT || switchProData.btn.dpad == DPAD_LEFT || switchProData.btn.dpad == DPAD_LEFT_UP; - default: - return false; - } -} - bool SwitchProParser::getButtonPress(ButtonEnum b) { const int8_t index = getButtonIndexSwitchPro(b); if (index < 0) return 0; - if (index <= LEFT) // Dpad - return checkDpad(b); - else - return switchProData.btn.val & (1UL << pgm_read_byte(&SWITCH_PRO_BUTTONS[index])); + return switchProData.btn.val & (1UL << pgm_read_byte(&SWITCH_PRO_BUTTONS[index])); } bool SwitchProParser::getButtonClick(ButtonEnum b) { @@ -70,11 +40,20 @@ bool SwitchProParser::getButtonClick(ButtonEnum b) { } int16_t SwitchProParser::getAnalogHat(AnalogHatEnum a) { - return switchProData.hatValue[(uint8_t)a] - 0x7FFF; // Subtract the center value + switch((uint8_t)a) { + case 0: + return switchProData.leftHatX - 2048; // Subtract the center value + case 1: + return 2048 - switchProData.leftHatY; // Invert, so it follows the same coordinate as the simple report + case 2: + return switchProData.rightHatX - 2048; // Subtract the center value + default: + return 2048 - switchProData.rightHatY; // Invert, so it follows the same coordinate as the simple report + } } void SwitchProParser::Parse(uint8_t len, uint8_t *buf) { - if (len > 1 && buf) { + if (len > 0 && buf) { #ifdef PRINTREPORT Notify(PSTR("\r\nLen: "), 0x80); Notify(len, 0x80); Notify(PSTR(", data: "), 0x80); @@ -84,51 +63,50 @@ void SwitchProParser::Parse(uint8_t len, uint8_t *buf) { } #endif - if (buf[0] == 0x3F) // Simple input report - memcpy(&switchProData, buf + 1, min((uint8_t)(len - 1), MFK_CASTUINT8T sizeof(switchProData))); - else if (buf[0] == 0x21) // Subcommand reply - return; - else { + // This driver always uses the standard full report that includes the IMU data. + // The downside is that it requires more processing power, as the data is send contentiously + // while the simple input report is only send when the button state changes however the simple + // input report is not available via USB and does not include the IMU data. + + if (buf[0] == 0x3F) // Simple input report via Bluetooth + switchProOutput.enableFullReportMode = true; // Switch over to the full report + else if (buf[0] == 0x30) { // Standard full mode + if (len < 4) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nReport is too short: "), 0x80); + D_PrintHex (len, 0x80); +#endif + return; + } + memcpy(&switchProData, buf + 3, min((uint8_t)(len - 3), MFK_CASTUINT8T sizeof(switchProData))); + + if (switchProData.btn.val != oldButtonState.val) { // Check if anything has changed + buttonClickState.val = switchProData.btn.val & ~oldButtonState.val; // Update click state variable + oldButtonState.val = switchProData.btn.val; + } + + message_counter++; + } else if (buf[0] == 0x21) { + // Subcommand reply via Bluetooth + } else if (buf[0] == 0x81) { + // Subcommand reply via USB + } else { #ifdef DEBUG_USB_HOST Notify(PSTR("\r\nUnknown report id: "), 0x80); D_PrintHex (buf[0], 0x80); Notify(PSTR(", len: "), 0x80); D_PrintHex (len, 0x80); #endif - return; } - - // Workaround issue with the controller sending invalid joystick values when it is connected - for (uint8_t i = 0; i < sizeof(switchProData.hatValue) / sizeof(switchProData.hatValue[0]); i++) { - if (switchProData.hatValue[i] < 1000 || switchProData.hatValue[i] > 0xFFFF - 1000) - switchProData.hatValue[i] = 0x7FFF; // Center value - } - - if (switchProData.btn.val != oldButtonState.val) { // Check if anything has changed - buttonClickState.val = switchProData.btn.val & ~oldButtonState.val; // Update click state variable - oldButtonState.val = switchProData.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; - } - } - - message_counter++; } - if (switchProOutput.ledReportChanged || switchProOutput.ledHomeReportChanged) - sendLedOutputReport(); // Send output report + if (switchProOutput.sendHandshake) + sendHandshake(); + else if (switchProOutput.disableTimeout) + disableTimeout(); + else if (switchProOutput.ledReportChanged || switchProOutput.ledHomeReportChanged || + switchProOutput.enableFullReportMode || switchProOutput.enableImu != -1) + sendOutputCmd(); else if (switchProOutput.leftRumbleOn || switchProOutput.rightRumbleOn) { // We need to send the rumble report repeatedly to keep it on uint32_t now = millis(); @@ -136,11 +114,10 @@ void SwitchProParser::Parse(uint8_t len, uint8_t *buf) { rumble_on_timer = now; sendRumbleOutputReport(); } - } else - rumble_on_timer = 0; + } } -void SwitchProParser::sendLedOutputReport() { +void SwitchProParser::sendOutputCmd() { // See: https://github.com/Dan611/hid-procon // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering // https://github.com/HisashiKato/USB_Host_Shield_Library_2.0_BTXBOX/blob/master/src/SWProBTParser.h#L152-L153 @@ -196,6 +173,21 @@ void SwitchProParser::sendLedOutputReport() { buf[0x0A + 3] = (0xF /* Mini Cycle 1 LED intensity */ << 4) | 0x0 /* Mini Cycle 2 LED intensity */; sendOutputReport(buf, 10 + 4); + } else if (switchProOutput.enableFullReportMode) { + switchProOutput.enableFullReportMode = false; + + // See: https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/bluetooth_hid_subcommands_notes.md#subcommand-0x03-set-input-report-mode + buf[0x0A + 0] = 0x03; // PROCON_CMD_MODE + buf[0x0A + 1] = 0x30; // PROCON_ARG_INPUT_FULL + + sendOutputReport(buf, 10 + 2); + } else if (switchProOutput.enableImu != -1) { + // See: https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/bluetooth_hid_subcommands_notes.md#subcommand-0x40-enable-imu-6-axis-sensor + buf[0x0A + 0] = 0x40; // PROCON_CMD_GYRO + buf[0x0A + 1] = switchProOutput.enableImu ? 1 : 0; // The new state is stored in the variable + switchProOutput.enableImu = -1; + + sendOutputReport(buf, 12); } } @@ -203,7 +195,6 @@ void SwitchProParser::sendRumbleOutputReport() { // See: https://github.com/Dan611/hid-procon // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering // https://github.com/HisashiKato/USB_Host_Shield_Library_2.0_BTXBOX/blob/master/src/SWProBTParser.h#L152-L153 - uint8_t buf[10] = { 0 }; buf[0x00] = 0x10; // Report ID - PROCON_CMD_RUMBLE_ONLY buf[0x01] = output_sequence_counter++; // Lowest 4-bit is a sequence number, which needs to be increased for every report @@ -238,15 +229,14 @@ void SwitchProParser::sendRumbleOutputReport() { } void SwitchProParser::Reset() { - for (uint8_t i = 0; i < sizeof(switchProData.hatValue) / sizeof(switchProData.hatValue[0]); i++) - switchProData.hatValue[i] = 0x7FFF; // Center value + // Center joysticks + switchProData.leftHatX = switchProData.leftHatY = switchProData.rightHatX = switchProData.rightHatY = 2048; + + // Reset buttons variables switchProData.btn.val = 0; oldButtonState.val = 0; + buttonClickState.val = 0; - switchProData.btn.dpad = DPAD_OFF; - oldButtonState.dpad = DPAD_OFF; - buttonClickState.dpad = 0; - oldDpad = 0; output_sequence_counter = 0; rumble_on_timer = 0; @@ -256,4 +246,8 @@ void SwitchProParser::Reset() { switchProOutput.ledHome = false; switchProOutput.ledReportChanged = false; switchProOutput.ledHomeReportChanged = false; + switchProOutput.enableFullReportMode = false; + switchProOutput.enableImu = -1; + switchProOutput.sendHandshake = false; + switchProOutput.disableTimeout = false; }; diff --git a/SwitchProParser.h b/SwitchProParser.h index c8336eb7..72d1fcb7 100644 --- a/SwitchProParser.h +++ b/SwitchProParser.h @@ -39,73 +39,99 @@ const uint8_t SWITCH_PRO_LEDS[] PROGMEM = { /** Buttons on the controller */ const uint8_t SWITCH_PRO_BUTTONS[] PROGMEM = { - 0x10, // UP - 0x11, // RIGHT - 0x12, // DOWN + 0x11, // UP + 0x12, // RIGHT + 0x10, // DOWN 0x13, // LEFT 0x0D, // Capture 0x09, // PLUS - 0x0A, // L3 - 0x0B, // R3 + 0x0B, // L3 + 0x0A, // R3 0x08, // MINUS 0x0C, // HOME 0, 0, // Skip - 0x00, // B - 0x01, // A - 0x03, // X - 0x02, // Y + 0x02, // B + 0x03, // A + 0x01, // X + 0x00, // Y - 0x04, // L - 0x05, // R - 0x06, // ZL + 0x16, // L + 0x06, // R + 0x17, // ZL 0x07, // ZR }; +// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/bluetooth_hid_notes.md#standard-input-report-format union SwitchProButtons { struct { - uint8_t b : 1; - uint8_t a : 1; uint8_t y : 1; uint8_t x : 1; + uint8_t b : 1; + uint8_t a : 1; - uint8_t l : 1; + uint8_t dummy1 : 2; uint8_t r : 1; - uint8_t zl : 1; uint8_t zr : 1; uint8_t minus : 1; uint8_t plus : 1; - uint8_t l3 : 1; uint8_t r3 : 1; + uint8_t l3 : 1; uint8_t home : 1; uint8_t capture : 1; - uint8_t dummy1 : 2; + uint8_t dummy2 : 2; uint8_t dpad : 4; - uint8_t dummy2 : 4; + + uint8_t dummy3 : 2; + uint8_t l : 1; + uint8_t zl : 1; } __attribute__((packed)); uint32_t val : 24; } __attribute__((packed)); +struct ImuData { + int16_t accX, accY, accZ; + int16_t gyroX, gyroY, gyroZ; +} __attribute__((packed)); + struct SwitchProData { + // TODO: Add byte 2 containing battery level and connection info + /* Button and joystick values */ - SwitchProButtons btn; // 0-2 bytes - uint16_t hatValue[4]; // 3-10 bytes + SwitchProButtons btn; // Bytes 3-5 + + // Bytes 6-11 + uint16_t leftHatX : 12; + uint16_t leftHatY : 12; + uint16_t rightHatX : 12; + uint16_t rightHatY : 12; + + uint8_t vibratorInput; // What is this used for? + + // Bytes 13-48 + // Three samples of the IMU is sent in one message + // See: https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/imu_sensor_notes.md + ImuData imu[3]; } __attribute__((packed)); struct SwitchProOutput { bool leftRumbleOn; bool rightRumbleOn; - uint8_t ledMask; // Higher nibble flashes the LEDs, lower nibble set them on/off + uint8_t ledMask; // Higher nibble flashes the LEDs, lower nibble sets them on/off bool ledHome; - // Used to only send the report when the state changes + // Used to send the reports at the same rate as the controller is sending messages bool ledReportChanged; bool ledHomeReportChanged; + bool enableFullReportMode; + int8_t enableImu; // -1 == Do nothing, 0 == disable IMU, 1 == enable IMU + bool sendHandshake; + bool disableTimeout; } __attribute__((packed)); /** This class parses all the data sent by the Switch Pro controller */ @@ -138,7 +164,15 @@ public: */ int16_t getAnalogHat(AnalogHatEnum a); -#if 0 + /** + * Used to enable/disable the IMU. By default it is disabled. + * @param enable Enable/disable the IMU. + */ + void enableImu(bool enable) { + // TODO: Should we just always enable it? + switchProOutput.enableImu = enable ? 1 : 0; + } + /** * Get the angle of the controller calculated using the accelerometer. * @param a Either ::Pitch or ::Roll. @@ -146,9 +180,9 @@ public: */ float getAngle(AngleEnum a) { if (a == Pitch) - return (atan2f(-ps5Data.accY, -ps5Data.accZ) + PI) * RAD_TO_DEG; + return (atan2f(-switchProData.imu[0].accY, -switchProData.imu[0].accZ) + PI) * RAD_TO_DEG; else - return (atan2f(ps5Data.accX, -ps5Data.accZ) + PI) * RAD_TO_DEG; + return (atan2f(switchProData.imu[0].accX, -switchProData.imu[0].accZ) + PI) * RAD_TO_DEG; }; /** @@ -159,22 +193,21 @@ public: int16_t getSensor(SensorEnum s) { switch(s) { case gX: - return ps5Data.gyroX; + return switchProData.imu[0].gyroX; case gY: - return ps5Data.gyroY; + return switchProData.imu[0].gyroY; case gZ: - return ps5Data.gyroZ; + return switchProData.imu[0].gyroZ; case aX: - return ps5Data.accX; + return switchProData.imu[0].accX; case aY: - return ps5Data.accY; + return switchProData.imu[0].accY; case aZ: - return ps5Data.accZ; + return switchProData.imu[0].accZ; default: return 0; } }; -#endif /** Turn both rumble and the LEDs off. */ void setAllOff() { @@ -305,17 +338,27 @@ protected: */ virtual void sendOutputReport(uint8_t *data, uint8_t len) = 0; + /** Used to send a handshake command via USB before disabling the timeout. */ + virtual void sendHandshake() {} + + /** + * Needed to disable USB timeout for the controller, + * so it sends out data without the host having to send data continuously. + */ + virtual void disableTimeout() {} + + /** Allow derived classes to access the output variables. */ + SwitchProOutput switchProOutput; + private: static int8_t getButtonIndexSwitchPro(ButtonEnum b); bool checkDpad(ButtonEnum b); // Used to check Switch Pro DPAD buttons - void sendLedOutputReport(); + void sendOutputCmd(); void sendRumbleOutputReport(); SwitchProData switchProData; SwitchProButtons oldButtonState, buttonClickState; - SwitchProOutput switchProOutput; - uint8_t oldDpad; uint16_t message_counter = 0; uint8_t output_sequence_counter : 4; uint32_t rumble_on_timer = 0; diff --git a/SwitchProUSB.h b/SwitchProUSB.h new file mode 100644 index 00000000..99ee7d05 --- /dev/null +++ b/SwitchProUSB.h @@ -0,0 +1,145 @@ +/* Copyright (C) 2021 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 _switch_pro_usb_h_ +#define _switch_pro_usb_h_ + +#include "hiduniversal.h" +#include "SwitchProParser.h" + +#define SWITCH_PRO_VID 0x057E // Nintendo Corporation +#define SWITCH_PRO_PID 0x2009 // Switch Pro Controller + +/** + * This class implements support for the Switch Pro controller via USB. + * It uses the HIDUniversal class for all the USB communication. + */ +class SwitchProUSB : public HIDUniversal, public SwitchProParser { +public: + /** + * Constructor for the SwitchProUSB class. + * @param p Pointer to the USB class instance. + */ + SwitchProUSB(USB *p) : + HIDUniversal(p) { + SwitchProParser::Reset(); + }; + + /** + * Used to check if a Switch Pro controller is connected. + * @return Returns true if it is connected. + */ + bool connected() { + return HIDUniversal::isReady() && HIDUniversal::VID == SWITCH_PRO_VID && HIDUniversal::PID == SWITCH_PRO_PID; + }; + + /** + * Used to call your own function when the device is successfully initialized. + * @param funcOnInit Function to call. + */ + void attachOnInit(void (*funcOnInit)(void)) { + pFuncOnInit = funcOnInit; + }; + +protected: + /** @name HIDUniversal implementation */ + /** + * Used to parse USB HID data. + * @param hid Pointer to the HID class. + * @param is_rpt_id Only used for Hubs. + * @param len The length of the incoming data. + * @param buf Pointer to the data buffer. + */ + virtual void ParseHIDData(USBHID *hid __attribute__((unused)), bool is_rpt_id __attribute__((unused)), uint8_t len, uint8_t *buf) { + if (HIDUniversal::VID == SWITCH_PRO_VID && HIDUniversal::PID == SWITCH_PRO_PID) + SwitchProParser::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 uint8_t OnInitSuccessful() { + if (HIDUniversal::VID == SWITCH_PRO_VID && HIDUniversal::PID == SWITCH_PRO_PID) { + SwitchProParser::Reset(); + + // We need to send a handshake and disable the timeout or the Pro controller will stop sending data via USB + // We can not send the commands quickly after each other, so we simply send out the commands at the same + // rate as the controller is sending data + switchProOutput.sendHandshake = switchProOutput.disableTimeout = true; + + if (pFuncOnInit) + pFuncOnInit(); // Call the user function + else { + setLedOn(LED1); // Turn on the LED1 + setLedHomeOn(); // Turn on the home LED + } + }; + return 0; + }; + /**@}*/ + + /** @name SwitchProParser implementation */ + virtual void sendOutputReport(uint8_t *data, uint8_t len) { + // The first 8 bytes are always the same. The actual report follows + uint8_t buf[8 + len] = { 0x80 /* PROCON_REPORT_SEND_USB */, 0x92 /*PROCON_USB_DO_CMD */, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00 }; + memcpy(buf + 8, data, len); + + // Endpoint (control endpoint), Interface (0x00), Report Type (Output 0x02), Report ID (0x80), nbytes, data + SetReport(epInfo[0].epAddr, 0, 0x02, buf[0], sizeof(buf), buf); + }; + + virtual void sendHandshake() { + switchProOutput.sendHandshake = false; + + // See: https://github.com/Dan611/hid-procon/blob/master/hid-procon.c + // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/USB-HID-Notes.md + uint8_t buf[2] = { 0x80 /* PROCON_REPORT_SEND_USB */, 0x02 /* PROCON_USB_HANDSHAKE */ }; + + // Endpoint (control endpoint), Interface (0x00), Report Type (Output 0x02), Report ID (0x80), nbytes, data + SetReport(epInfo[0].epAddr, 0, 0x02, buf[0], sizeof(buf), buf); + }; + + virtual void disableTimeout() { + switchProOutput.disableTimeout = false; + + // See: https://github.com/Dan611/hid-procon/blob/master/hid-procon.c + // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/USB-HID-Notes.md + uint8_t buf[2] = { 0x80 /* PROCON_REPORT_SEND_USB */, 0x04 /* PROCON_USB_ENABLE */ }; + + // Endpoint (control endpoint), Interface (0x00), Report Type (Output 0x02), Report ID (0x80), nbytes, data + SetReport(epInfo[0].epAddr, 0, 0x02, buf[0], sizeof(buf), buf); + }; + /**@}*/ + + /** @name USBDeviceConfig implementation */ + /** + * Used by the USB core to check what this driver support. + * @param vid The device's VID. + * @param pid The device's PID. + * @return Returns true if the device's VID and PID matches this driver. + */ + virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { + return (vid == SWITCH_PRO_VID && pid == SWITCH_PRO_PID); + }; + /**@}*/ + +private: + void (*pFuncOnInit)(void); // Pointer to function called in onInit() +}; +#endif diff --git a/examples/Bluetooth/SwitchProBT/SwitchProBT.ino b/examples/Bluetooth/SwitchProBT/SwitchProBT.ino index 75a0eb83..b21f693c 100644 --- a/examples/Bluetooth/SwitchProBT/SwitchProBT.ino +++ b/examples/Bluetooth/SwitchProBT/SwitchProBT.ino @@ -25,6 +25,7 @@ SwitchProBT SwitchPro(&Btd, PAIR); // After that you can simply create the instance like so and then press a button on the device //SwitchProBT SwitchPro(&Btd); +bool printAngle = false; uint16_t lastMessageCounter = -1; uint32_t capture_timer; @@ -46,10 +47,11 @@ void loop() { if (SwitchPro.connected() && lastMessageCounter != SwitchPro.getMessageCounter()) { lastMessageCounter = SwitchPro.getMessageCounter(); - if (SwitchPro.getAnalogHat(LeftHatX) > 5000 || SwitchPro.getAnalogHat(LeftHatX) < -5000 || - SwitchPro.getAnalogHat(LeftHatY) > 5000 || SwitchPro.getAnalogHat(LeftHatY) < -5000 || - SwitchPro.getAnalogHat(RightHatX) > 5000 || SwitchPro.getAnalogHat(RightHatX) < -5000 || - SwitchPro.getAnalogHat(RightHatY) > 5000 || SwitchPro.getAnalogHat(RightHatY) < -5000) { + // The joysticks values are uncalibrated + if (SwitchPro.getAnalogHat(LeftHatX) > 500 || SwitchPro.getAnalogHat(LeftHatX) < -500 || + SwitchPro.getAnalogHat(LeftHatY) > 500 || SwitchPro.getAnalogHat(LeftHatY) < -500 || + SwitchPro.getAnalogHat(RightHatX) > 500 || SwitchPro.getAnalogHat(RightHatX) < -500 || + SwitchPro.getAnalogHat(RightHatY) > 500 || SwitchPro.getAnalogHat(RightHatY) < -500) { Serial.print(F("\r\nLeftHatX: ")); Serial.print(SwitchPro.getAnalogHat(LeftHatX)); Serial.print(F("\tLeftHatY: ")); @@ -97,8 +99,11 @@ void loop() { Serial.print(F("\r\nDown")); } - if (SwitchPro.getButtonClick(PLUS)) + if (SwitchPro.getButtonClick(PLUS)) { + printAngle = !printAngle; + SwitchPro.enableImu(printAngle); Serial.print(F("\r\nPlus")); + } if (SwitchPro.getButtonClick(MINUS)) Serial.print(F("\r\nMinus")); @@ -131,5 +136,12 @@ void loop() { Serial.print(F("\r\nL3")); if (SwitchPro.getButtonClick(R3)) Serial.print(F("\r\nR3")); + + if (printAngle) { // Print angle calculated using the accelerometer only + Serial.print(F("\r\nPitch: ")); + Serial.print(SwitchPro.getAngle(Pitch)); + Serial.print(F("\tRoll: ")); + Serial.print(SwitchPro.getAngle(Roll)); + } } } diff --git a/examples/SwitchProUSB/SwitchProUSB.ino b/examples/SwitchProUSB/SwitchProUSB.ino new file mode 100644 index 00000000..b2b74d43 --- /dev/null +++ b/examples/SwitchProUSB/SwitchProUSB.ino @@ -0,0 +1,128 @@ +/* + Example sketch for the Switch Pro USB 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 + +// Satisfy the IDE, which needs to see the include statement in the ino too. +#ifdef dobogusinclude +#include +#endif +#include + +USB Usb; +SwitchProUSB SwitchPro(&Usb); + +bool printAngle = false; +uint16_t lastMessageCounter = -1; +uint32_t capture_timer; + +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\nSwitch Pro USB Library Started")); +} + +void loop() { + Usb.Task(); + + if (SwitchPro.connected() && lastMessageCounter != SwitchPro.getMessageCounter()) { + lastMessageCounter = SwitchPro.getMessageCounter(); + + // The joysticks values are uncalibrated + if (SwitchPro.getAnalogHat(LeftHatX) > 500 || SwitchPro.getAnalogHat(LeftHatX) < -500 || + SwitchPro.getAnalogHat(LeftHatY) > 500 || SwitchPro.getAnalogHat(LeftHatY) < -500 || + SwitchPro.getAnalogHat(RightHatX) > 500 || SwitchPro.getAnalogHat(RightHatX) < -500 || + SwitchPro.getAnalogHat(RightHatY) > 500 || SwitchPro.getAnalogHat(RightHatY) < -500) { + Serial.print(F("\r\nLeftHatX: ")); + Serial.print(SwitchPro.getAnalogHat(LeftHatX)); + Serial.print(F("\tLeftHatY: ")); + Serial.print(SwitchPro.getAnalogHat(LeftHatY)); + Serial.print(F("\tRightHatX: ")); + Serial.print(SwitchPro.getAnalogHat(RightHatX)); + Serial.print(F("\tRightHatY: ")); + Serial.print(SwitchPro.getAnalogHat(RightHatY)); + } + + if (SwitchPro.getButtonClick(CAPTURE)) + Serial.print(F("\r\nCapture")); + if (SwitchPro.getButtonClick(HOME)) { + Serial.print(F("\r\nHome")); + SwitchPro.setLedHomeToggle(); + } + + if (SwitchPro.getButtonClick(LEFT)) { + SwitchPro.setLedOff(); + SwitchPro.setLedOn(LED1); + Serial.print(F("\r\nLeft")); + } + if (SwitchPro.getButtonClick(UP)) { + SwitchPro.setLedOff(); + SwitchPro.setLedOn(LED2); + Serial.print(F("\r\nUp")); + } + if (SwitchPro.getButtonClick(RIGHT)) { + SwitchPro.setLedOff(); + SwitchPro.setLedOn(LED3); + Serial.print(F("\r\nRight")); + } + if (SwitchPro.getButtonClick(DOWN)) { + SwitchPro.setLedOff(); + SwitchPro.setLedOn(LED4); + Serial.print(F("\r\nDown")); + } + + if (SwitchPro.getButtonClick(PLUS)) { + printAngle = !printAngle; + SwitchPro.enableImu(printAngle); + Serial.print(F("\r\nPlus")); + } + if (SwitchPro.getButtonClick(MINUS)) + Serial.print(F("\r\nMinus")); + + if (SwitchPro.getButtonClick(A)) + Serial.print(F("\r\nA")); + if (SwitchPro.getButtonClick(B)) + Serial.print(F("\r\nB")); + if (SwitchPro.getButtonClick(X)) + Serial.print(F("\r\nX")); + if (SwitchPro.getButtonClick(Y)) + Serial.print(F("\r\nY")); + + if (SwitchPro.getButtonClick(L)) { + SwitchPro.setRumbleLeft(false); + Serial.print(F("\r\nL")); + } + if (SwitchPro.getButtonClick(R)) { + SwitchPro.setRumbleRight(false); + Serial.print(F("\r\nR")); + } + if (SwitchPro.getButtonClick(ZL)) { + SwitchPro.setRumbleLeft(true); + Serial.print(F("\r\nZL")); + } + if (SwitchPro.getButtonClick(ZR)) { + SwitchPro.setRumbleRight(true); + Serial.print(F("\r\nZR")); + } + if (SwitchPro.getButtonClick(L3)) + Serial.print(F("\r\nL3")); + if (SwitchPro.getButtonClick(R3)) + Serial.print(F("\r\nR3")); + + if (printAngle) { // Print angle calculated using the accelerometer only + Serial.print(F("\r\nPitch: ")); + Serial.print(SwitchPro.getAngle(Pitch)); + Serial.print(F("\tRoll: ")); + Serial.print(SwitchPro.getAngle(Roll)); + } + } +} diff --git a/keywords.txt b/keywords.txt index 827432d3..3c4b7487 100644 --- a/keywords.txt +++ b/keywords.txt @@ -39,6 +39,7 @@ PS4USB KEYWORD1 PS5BT KEYWORD1 PS5USB KEYWORD1 SwitchProBT KEYWORD1 +SwitchProUSB KEYWORD1 #################################################### # Methods and Functions (KEYWORD2) @@ -62,8 +63,11 @@ getTemperature KEYWORD2 disconnect KEYWORD2 setAllOff KEYWORD2 +setRumble KEYWORD2 setRumbleOff KEYWORD2 setRumbleOn KEYWORD2 +setRumbleLeft KEYWORD2 +setRumbleRight KEYWORD2 setLedOff KEYWORD2 setLedOn KEYWORD2 setLedToggle KEYWORD2 From 9b754a8a09e3e7a925fa94a5e4bcf560579b250b Mon Sep 17 00:00:00 2001 From: Yuuichi Akagawa Date: Mon, 10 May 2021 00:06:47 +0900 Subject: [PATCH 89/93] MIDI driver v0.6.0 --- README.md | 2 +- .../USBH_MIDI_dump/USBH_MIDI_dump.ino | 41 +- .../USB_MIDI_converter/USB_MIDI_converter.ino | 26 +- .../USB_MIDI_converter_multi.ino | 17 +- .../bidirectional_converter.ino | 7 - .../USBH_MIDI/eVY1_sample/eVY1_sample.ino | 29 +- usbh_midi.cpp | 426 ++++++++++-------- usbh_midi.h | 86 +++- 8 files changed, 334 insertions(+), 300 deletions(-) diff --git a/README.md b/README.md index 3d230cf6..f053fffb 100644 --- a/README.md +++ b/README.md @@ -355,7 +355,7 @@ You can convert USB MIDI keyboard to legacy serial MIDI. * [USB_MIDI_converter.ino](examples/USBH_MIDI/USB_MIDI_converter/USB_MIDI_converter.ino) * [USB_MIDI_converter_multi.ino](examples/USBH_MIDI/USB_MIDI_converter_multi/USB_MIDI_converter_multi.ino) -For information see the following page: . +For more information see : . ### [amBX Library](AMBX.cpp) diff --git a/examples/USBH_MIDI/USBH_MIDI_dump/USBH_MIDI_dump.ino b/examples/USBH_MIDI/USBH_MIDI_dump/USBH_MIDI_dump.ino index 377a5a63..645131ad 100644 --- a/examples/USBH_MIDI/USBH_MIDI_dump/USBH_MIDI_dump.ino +++ b/examples/USBH_MIDI/USBH_MIDI_dump/USBH_MIDI_dump.ino @@ -1,7 +1,7 @@ /* ******************************************************************************* * USB-MIDI dump utility - * Copyright (C) 2013-2017 Yuuichi Akagawa + * Copyright (C) 2013-2021 Yuuichi Akagawa * * for use with USB Host Shield 2.0 from Circuitsathome.com * https://github.com/felis/USB_Host_Shield_2.0 @@ -13,35 +13,37 @@ #include #include -// Satisfy the IDE, which needs to see the include statment in the ino too. -#ifdef dobogusinclude -#include -#endif -#include - USB Usb; -//USBHub Hub(&Usb); +USBHub Hub(&Usb); USBH_MIDI Midi(&Usb); void MIDI_poll(); -uint16_t pid, vid; +void onInit() +{ + char buf[20]; + uint16_t vid = Midi.idVendor(); + uint16_t pid = Midi.idProduct(); + sprintf(buf, "VID:%04X, PID:%04X", vid, pid); + Serial.println(buf); +} void setup() { - vid = pid = 0; Serial.begin(115200); if (Usb.Init() == -1) { while (1); //halt }//if (Usb.Init() == -1... delay( 200 ); + + // Register onInit() function + Midi.attachOnInit(onInit); } void loop() { Usb.Task(); - //uint32_t t1 = (uint32_t)micros(); if ( Midi ) { MIDI_poll(); } @@ -50,23 +52,16 @@ void loop() // Poll USB MIDI Controler and send to serial MIDI void MIDI_poll() { - char buf[20]; - uint8_t bufMidi[64]; + char buf[16]; + uint8_t bufMidi[MIDI_EVENT_PACKET_SIZE]; uint16_t rcvd; - if (Midi.idVendor() != vid || Midi.idProduct() != pid) { - vid = Midi.idVendor(); - pid = Midi.idProduct(); - sprintf(buf, "VID:%04X, PID:%04X", vid, pid); - Serial.println(buf); - } if (Midi.RecvData( &rcvd, bufMidi) == 0 ) { uint32_t time = (uint32_t)millis(); - sprintf(buf, "%04X%04X: ", (uint16_t)(time >> 16), (uint16_t)(time & 0xFFFF)); // Split variable to prevent warnings on the ESP8266 platform + sprintf(buf, "%04X%04X:%3d:", (uint16_t)(time >> 16), (uint16_t)(time & 0xFFFF), rcvd); // Split variable to prevent warnings on the ESP8266 platform Serial.print(buf); - Serial.print(rcvd); - Serial.print(':'); - for (int i = 0; i < 64; i++) { + + for (int i = 0; i < MIDI_EVENT_PACKET_SIZE; i++) { sprintf(buf, " %02X", bufMidi[i]); Serial.print(buf); } diff --git a/examples/USBH_MIDI/USB_MIDI_converter/USB_MIDI_converter.ino b/examples/USBH_MIDI/USB_MIDI_converter/USB_MIDI_converter.ino index 1f5d1561..4f988183 100644 --- a/examples/USBH_MIDI/USB_MIDI_converter/USB_MIDI_converter.ino +++ b/examples/USBH_MIDI/USB_MIDI_converter/USB_MIDI_converter.ino @@ -1,7 +1,7 @@ /* ******************************************************************************* * USB-MIDI to Legacy Serial MIDI converter - * Copyright (C) 2012-2020 Yuuichi Akagawa + * Copyright (C) 2012-2021 Yuuichi Akagawa * * Idea from LPK25 USB-MIDI to Serial MIDI converter * by Collin Cunningham - makezine.com, narbotic.com @@ -13,12 +13,6 @@ #include #include -// Satisfy the IDE, which needs to see the include statment in the ino too. -#ifdef dobogusinclude -#include -#endif -#include - #ifdef USBCON #define _MIDI_SERIAL_PORT Serial1 #else @@ -40,7 +34,6 @@ USB Usb; USBH_MIDI Midi(&Usb); void MIDI_poll(); -void doDelay(uint32_t t1, uint32_t t2, uint32_t delayTime); void setup() { @@ -55,12 +48,12 @@ void setup() void loop() { Usb.Task(); - uint32_t t1 = (uint32_t)micros(); + if ( Midi ) { MIDI_poll(); } - //delay(1ms) - doDelay(t1, (uint32_t)micros(), 1000); + //delay(1ms) if you want + //delayMicroseconds(1000); } // Poll USB MIDI Controler and send to serial MIDI @@ -79,14 +72,3 @@ void MIDI_poll() } } while (size > 0); } - -// Delay time (max 16383 us) -void doDelay(uint32_t t1, uint32_t t2, uint32_t delayTime) -{ - uint32_t t3; - - t3 = t2 - t1; - if ( t3 < delayTime ) { - delayMicroseconds(delayTime - t3); - } -} diff --git a/examples/USBH_MIDI/USB_MIDI_converter_multi/USB_MIDI_converter_multi.ino b/examples/USBH_MIDI/USB_MIDI_converter_multi/USB_MIDI_converter_multi.ino index 5c841a0c..ebbf63ad 100644 --- a/examples/USBH_MIDI/USB_MIDI_converter_multi/USB_MIDI_converter_multi.ino +++ b/examples/USBH_MIDI/USB_MIDI_converter_multi/USB_MIDI_converter_multi.ino @@ -1,7 +1,7 @@ /* ******************************************************************************* * USB-MIDI to Legacy Serial MIDI converter - * Copyright (C) 2012-2020 Yuuichi Akagawa + * Copyright (C) 2012-2021 Yuuichi Akagawa * * Idea from LPK25 USB-MIDI to Serial MIDI converter * by Collin Cunningham - makezine.com, narbotic.com @@ -13,12 +13,6 @@ #include #include -// Satisfy the IDE, which needs to see the include statment in the ino too. -#ifdef dobogusinclude -#include -#endif -#include - #ifdef USBCON #define _MIDI_SERIAL_PORT Serial1 #else @@ -41,8 +35,7 @@ USBHub Hub1(&Usb); USBH_MIDI Midi1(&Usb); USBH_MIDI Midi2(&Usb); -void MIDI_poll(); -void doDelay(uint32_t t1, uint32_t t2, uint32_t delayTime); +void MIDI_poll(USBH_MIDI &Midi); void setup() { @@ -57,15 +50,15 @@ void setup() void loop() { Usb.Task(); - uint32_t t1 = (uint32_t)micros(); + if ( Midi1 ) { MIDI_poll(Midi1); } if ( Midi2 ) { MIDI_poll(Midi2); } - //delay(1ms) - doDelay(t1, (uint32_t)micros(), 1000); + //delay(1ms) if you want + //delayMicroseconds(1000); } // Poll USB MIDI Controler and send to serial MIDI diff --git a/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino b/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino index 18c7cc8a..ece2774f 100644 --- a/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino +++ b/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino @@ -16,12 +16,6 @@ #include #include -// Satisfy the IDE, which needs to see the include statment in the ino too. -#ifdef dobogusinclude -#include -#endif -#include - //Arduino MIDI library v4.2 compatibility #ifdef MIDI_CREATE_DEFAULT_INSTANCE MIDI_CREATE_DEFAULT_INSTANCE(); @@ -44,7 +38,6 @@ MIDI_CREATE_DEFAULT_INSTANCE(); ////////////////////////// USB Usb; -//USBHub Hub1(&Usb); USBH_MIDI Midi(&Usb); void MIDI_poll(); diff --git a/examples/USBH_MIDI/eVY1_sample/eVY1_sample.ino b/examples/USBH_MIDI/eVY1_sample/eVY1_sample.ino index c0e2afab..0b4e0bed 100644 --- a/examples/USBH_MIDI/eVY1_sample/eVY1_sample.ino +++ b/examples/USBH_MIDI/eVY1_sample/eVY1_sample.ino @@ -1,7 +1,7 @@ /* ******************************************************************************* * eVY1 Shield sample - Say 'Konnichiwa' - * Copyright (C) 2014-2016 Yuuichi Akagawa + * Copyright (C) 2014-2021 Yuuichi Akagawa * * This is sample program. Do not expect perfect behavior. ******************************************************************************* @@ -9,12 +9,6 @@ #include #include -// Satisfy the IDE, which needs to see the include statment in the ino too. -#ifdef dobogusinclude -#include -#endif -#include - USB Usb; //USBHub Hub(&Usb); USBH_MIDI Midi(&Usb); @@ -23,7 +17,6 @@ void MIDI_poll(); void noteOn(uint8_t note); void noteOff(uint8_t note); -uint16_t pid, vid; uint8_t exdata[] = { 0xf0, 0x43, 0x79, 0x09, 0x00, 0x50, 0x10, 'k', ' ', 'o', ',', //Ko @@ -34,15 +27,22 @@ uint8_t exdata[] = { 0x00, 0xf7 }; +void onInit() +{ + // Send Phonetic symbols via SysEx + Midi.SendSysEx(exdata, sizeof(exdata)); + delay(500); +} + void setup() { - vid = pid = 0; - Serial.begin(115200); - if (Usb.Init() == -1) { while (1); //halt }//if (Usb.Init() == -1... delay( 200 ); + + // Register onInit() function + Midi.attachOnInit(onInit); } void loop() @@ -61,13 +61,6 @@ void loop() void MIDI_poll() { uint8_t inBuf[ 3 ]; - - //first call? - if (Midi.idVendor() != vid || Midi.idProduct() != pid) { - vid = Midi.idVendor(); pid = Midi.idProduct(); - Midi.SendSysEx(exdata, sizeof(exdata)); - delay(500); - } Midi.RecvData(inBuf); } diff --git a/usbh_midi.cpp b/usbh_midi.cpp index 4de21770..e7bfcaf1 100644 --- a/usbh_midi.cpp +++ b/usbh_midi.cpp @@ -1,7 +1,7 @@ /* ******************************************************************************* * USB-MIDI class driver for USB Host Shield 2.0 Library - * Copyright (c) 2012-2018 Yuuichi Akagawa + * Copyright (c) 2012-2021 Yuuichi Akagawa * * Idea from LPK25 USB-MIDI to Serial MIDI converter * by Collin Cunningham - makezine.com, narbotic.com @@ -25,6 +25,9 @@ */ #include "usbh_midi.h" +// To enable serial debugging see "settings.h" +//#define EXTRADEBUG // Uncomment to get even more debugging data + ////////////////////////// // MIDI MESAGES // midi.org/techspecs/ @@ -79,24 +82,16 @@ //| 0xF | 1 |Single Byte //+-----+-----------+------------------------------------------------------------------- -const uint8_t USBH_MIDI::epDataInIndex = 1; -const uint8_t USBH_MIDI::epDataOutIndex = 2; -const uint8_t USBH_MIDI::epDataInIndexVSP = 3; -const uint8_t USBH_MIDI::epDataOutIndexVSP = 4; - USBH_MIDI::USBH_MIDI(USB *p) : pUsb(p), bAddress(0), -bNumEP(1), bPollEnable(false), -isMidiFound(false), readPtr(0) { // initialize endpoint data structures for(uint8_t i=0; igetConfDescr(bAddress, 0, i, &midiDescParser); + if(rcode) // Check error code goto FailGetConfDescr; - if (bNumEP > 1) + bNumEP += midiDescParser.getNumEPs(); + if(bNumEP > 1) {// All endpoints extracted + bConfNum = midiDescParser.getConfValue(); break; - } // for - - USBTRACE2("\r\nNumEP:", bNumEP); + } + } + USBTRACE2("STEP1: MIDI,NumEP:", bNumEP); + //Found the MIDI device? + if( bNumEP == 1 ){ //Device not found. + USBTRACE("MIDI not found.\r\nSTEP2: Attempts vendor specific bulk device\r\n"); + // STEP2: Check if attached device is a MIDI device and fill endpoint data structure + for(uint8_t i = 0; i < num_of_conf; i++) { + MidiDescParser midiDescParser(this, false); // Allow all devices, vendor specific class with Bulk transfer + rcode = pUsb->getConfDescr(bAddress, 0, i, &midiDescParser); + if(rcode) // Check error code + goto FailGetConfDescr; + bNumEP += midiDescParser.getNumEPs(); + if(bNumEP > 1) {// All endpoints extracted + bConfNum = midiDescParser.getConfValue(); + break; + } + } + USBTRACE2("\r\nSTEP2: Vendor,NumEP:", bNumEP); + } if( bNumEP < 2 ){ //Device not found. rcode = 0xff; goto FailGetConfDescr; } - if( !isMidiFound ){ //MIDI Device not found. Try last Bulk transfer device - USBTRACE("MIDI not found. Attempts bulk device\r\n"); - epInfo[epDataInIndex].epAddr = epInfo[epDataInIndexVSP].epAddr; - epInfo[epDataInIndex].maxPktSize = epInfo[epDataInIndexVSP].maxPktSize; - epInfo[epDataOutIndex].epAddr = epInfo[epDataOutIndexVSP].epAddr; - epInfo[epDataOutIndex].maxPktSize = epInfo[epDataOutIndexVSP].maxPktSize; - } - // Assign epInfo to epinfo pointer rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo); USBTRACE2("Conf:", bConfNum); @@ -242,9 +255,12 @@ uint8_t USBH_MIDI::Init(uint8_t parent, uint8_t port, bool lowspeed) // Set Configuration Value rcode = pUsb->setConf(bAddress, 0, bConfNum); - if (rcode) { + if (rcode) goto FailSetConfDescr; - } + + if(pFuncOnInit) + pFuncOnInit(); // Call the user function + bPollEnable = true; USBTRACE("Init done.\r\n"); return 0; @@ -256,92 +272,10 @@ FailSetConfDescr: return rcode; } -/* get and parse config descriptor */ -uint8_t USBH_MIDI::parseConfigDescr( uint8_t addr, uint8_t conf ) -{ - uint8_t buf[ DESC_BUFF_SIZE ]; - uint8_t* buf_ptr = buf; - uint8_t rcode; - uint8_t descr_length; - uint8_t descr_type; - uint16_t total_length; - USB_ENDPOINT_DESCRIPTOR *epDesc; - bool isMidi = false; - - // get configuration descriptor (get descriptor size only) - rcode = pUsb->getConfDescr( addr, 0, 4, conf, buf ); - if( rcode ){ - return rcode; - } - total_length = buf[2] | ((int)buf[3] << 8); - if( total_length > DESC_BUFF_SIZE ) { //check if total length is larger than buffer - total_length = DESC_BUFF_SIZE; - } - - // get configuration descriptor (all) - rcode = pUsb->getConfDescr( addr, 0, total_length, conf, buf ); //get the whole descriptor - if( rcode ){ - return rcode; - } - - //parsing descriptors - while( buf_ptr < buf + total_length ) { - descr_length = *( buf_ptr ); - descr_type = *( buf_ptr + 1 ); - switch( descr_type ) { - case USB_DESCRIPTOR_CONFIGURATION : - bConfNum = buf_ptr[5]; - break; - case USB_DESCRIPTOR_INTERFACE : - USBTRACE("\r\nConf:"), D_PrintHex(bConfNum, 0x80); - USBTRACE(" Int:"), D_PrintHex(buf_ptr[2], 0x80); - USBTRACE(" Alt:"), D_PrintHex(buf_ptr[3], 0x80); - USBTRACE(" EPs:"), D_PrintHex(buf_ptr[4], 0x80); - USBTRACE(" IntCl:"), D_PrintHex(buf_ptr[5], 0x80); - USBTRACE(" IntSubCl:"), D_PrintHex(buf_ptr[6], 0x80); - USBTRACE("\r\n"); - - if( buf_ptr[5] == USB_CLASS_AUDIO && buf_ptr[6] == USB_SUBCLASS_MIDISTREAMING ) { //p[5]; bInterfaceClass = 1(Audio), p[6]; bInterfaceSubClass = 3(MIDI Streaming) - isMidiFound = true; //MIDI device found. - isMidi = true; - USBTRACE("MIDI Device\r\n"); - }else{ - isMidi = false; - USBTRACE("No MIDI Device\r\n"); - } - break; - case USB_DESCRIPTOR_ENDPOINT : - epDesc = (USB_ENDPOINT_DESCRIPTOR *)buf_ptr; - USBTRACE("-EPAddr:"), D_PrintHex(epDesc->bEndpointAddress, 0x80); - USBTRACE(" bmAttr:"), D_PrintHex(epDesc->bmAttributes, 0x80); - USBTRACE2(" MaxPktSz:", (uint8_t)epDesc->wMaxPacketSize); - if ((epDesc->bmAttributes & bTransferTypeMask) == USB_TRANSFER_TYPE_BULK) {//bulk - uint8_t index; - if( isMidi ) - index = ((epDesc->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex; - else - index = ((epDesc->bEndpointAddress & 0x80) == 0x80) ? epDataInIndexVSP : epDataOutIndexVSP; - epInfo[index].epAddr = (epDesc->bEndpointAddress & 0x0F); - epInfo[index].maxPktSize = (uint8_t)epDesc->wMaxPacketSize; - bNumEP ++; -#ifdef DEBUG_USB_HOST - PrintEndpointDescriptor(epDesc); -#endif - } - break; - default: - break; - }//switch( descr_type - buf_ptr += descr_length; //advance buffer pointer - }//while( buf_ptr <=... - return 0; -} - /* Performs a cleanup after failed Init() attempt */ uint8_t USBH_MIDI::Release() { pUsb->GetAddressPool().FreeAddress(bAddress); - bNumEP = 1; //must have to be reset to 1 bAddress = 0; bPollEnable = false; readPtr = 0; @@ -375,7 +309,10 @@ uint8_t USBH_MIDI::RecvData(uint16_t *bytes_rcvd, uint8_t *dataptr) { *bytes_rcvd = (uint16_t)epInfo[epDataInIndex].maxPktSize; uint8_t r = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, bytes_rcvd, dataptr); - +#ifdef EXTRADEBUG + if( r ) + USBTRACE2("inTransfer():", r); +#endif if( *bytes_rcvd < (MIDI_EVENT_PACKET_SIZE-4)){ dataptr[*bytes_rcvd] = '\0'; dataptr[(*bytes_rcvd)+1] = '\0'; @@ -421,60 +358,52 @@ RecvData_return_from_buffer: *(outBuf++) = m = recvBuf[readPtr++]; *(outBuf++) = recvBuf[readPtr++]; *(outBuf++) = recvBuf[readPtr++]; - return lookupMsgSize(m, cin); -} -/* Receive raw data from MIDI device */ -uint8_t USBH_MIDI::RecvRawData(uint8_t *outBuf) -{ - return RecvData(outBuf, true); + return getMsgSizeFromCin(cin); } /* Send data to MIDI device */ uint8_t USBH_MIDI::SendData(uint8_t *dataptr, uint8_t nCable) { uint8_t buf[4]; - uint8_t msg; + uint8_t status = dataptr[0]; - msg = dataptr[0]; - // SysEx long message ? - if( msg == 0xf0 ) - { + uint8_t cin = convertStatus2Cin(status); + if ( status == 0xf0 ) { + // SysEx long message return SendSysEx(dataptr, countSysExDataSize(dataptr), nCable); } - buf[0] = (nCable << 4) | (msg >> 4); - if( msg < 0xf0 ) msg = msg & 0xf0; - - //Building USB-MIDI Event Packets + buf[0] = (uint8_t)(nCable << 4) | cin; buf[1] = dataptr[0]; - buf[2] = dataptr[1]; - buf[3] = dataptr[2]; - switch(lookupMsgSize(msg)) { + uint8_t msglen = getMsgSizeFromCin(cin); + switch(msglen) { //3 bytes message case 3 : - if(msg == 0xf2) {//system common message(SPP) - buf[0] = (nCable << 4) | 3; - } + buf[2] = dataptr[1]; + buf[3] = dataptr[2]; break; //2 bytes message case 2 : - if(msg == 0xf1 || msg == 0xf3) {//system common message(MTC/SongSelect) - buf[0] = (nCable << 4) | 2; - } + buf[2] = dataptr[1]; buf[3] = 0; break; //1 byte message case 1 : - default : buf[2] = 0; buf[3] = 0; break; + default : + break; } +#ifdef EXTRADEBUG + //Dump for raw USB-MIDI event packet + Notify(PSTR("SendData():"), 0x80), D_PrintHex((buf[0]), 0x80), D_PrintHex((buf[1]), 0x80), D_PrintHex((buf[2]), 0x80), D_PrintHex((buf[3]), 0x80), Notify(PSTR("\r\n"), 0x80); +#endif return pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, 4, buf); } @@ -495,54 +424,13 @@ void USBH_MIDI::PrintEndpointDescriptor( const USB_ENDPOINT_DESCRIPTOR* ep_ptr ) /*Return */ /* 0 : undefined message */ /* 0<: Vaild message size(1-3) */ -uint8_t USBH_MIDI::lookupMsgSize(uint8_t midiMsg, uint8_t cin) +//uint8_t USBH_MIDI::lookupMsgSize(uint8_t midiMsg, uint8_t cin) +uint8_t USBH_MIDI::lookupMsgSize(uint8_t status, uint8_t cin) { - uint8_t msgSize = 0; - - //SysEx message? - cin = cin & 0x0f; - if( (cin & 0xc) == 4 ) { - if( cin == 4 || cin == 7 ) return 3; - if( cin == 6 ) return 2; - if( cin == 5 ) return 1; + if( cin == 0 ){ + cin = convertStatus2Cin(status); } - - if( midiMsg < 0xf0 ) midiMsg &= 0xf0; - switch(midiMsg) { - //3 bytes messages - case 0xf2 : //system common message(SPP) - case 0x80 : //Note off - case 0x90 : //Note on - case 0xa0 : //Poly KeyPress - case 0xb0 : //Control Change - case 0xe0 : //PitchBend Change - msgSize = 3; - break; - - //2 bytes messages - case 0xf1 : //system common message(MTC) - case 0xf3 : //system common message(SongSelect) - case 0xc0 : //Program Change - case 0xd0 : //Channel Pressure - msgSize = 2; - break; - - //1 byte messages - case 0xf8 : //system realtime message - case 0xf9 : //system realtime message - case 0xfa : //system realtime message - case 0xfb : //system realtime message - case 0xfc : //system realtime message - case 0xfe : //system realtime message - case 0xff : //system realtime message - msgSize = 1; - break; - - //undefine messages - default : - break; - } - return msgSize; + return getMsgSizeFromCin(cin); } /* SysEx data size counter */ @@ -555,11 +443,9 @@ uint16_t USBH_MIDI::countSysExDataSize(uint8_t *dataptr) } //Search terminator(0xf7) - while(*dataptr != 0xf7) - { + while(*dataptr != 0xf7) { dataptr++; c++; - //Limiter (default: 256 bytes) if(c > MIDI_MAX_SYSEX_SIZE){ c = 0; @@ -575,15 +461,15 @@ uint8_t USBH_MIDI::SendSysEx(uint8_t *dataptr, uint16_t datasize, uint8_t nCable uint8_t buf[MIDI_EVENT_PACKET_SIZE]; uint8_t rc = 0; uint16_t n = datasize; - uint16_t pktSize = (n*10/3+7)/10*4; //Calculate total USB MIDI packet size uint8_t wptr = 0; uint8_t maxpkt = epInfo[epDataInIndex].maxPktSize; - if( maxpkt > MIDI_EVENT_PACKET_SIZE ) maxpkt = MIDI_EVENT_PACKET_SIZE; - USBTRACE("SendSysEx:\r\t"); USBTRACE2(" Length:\t", datasize); +#ifdef EXTRADEBUG + uint16_t pktSize = (n+2)/3; //Calculate total USB MIDI packet size USBTRACE2(" Total pktSize:\t", pktSize); +#endif while(n > 0) { //Byte 0 @@ -595,14 +481,14 @@ uint8_t USBH_MIDI::SendSysEx(uint8_t *dataptr, uint16_t datasize, uint8_t nCable buf[wptr++] = *(dataptr++); buf[wptr++] = 0x00; buf[wptr++] = 0x00; - n = n - 1; + n = 0; break; case 2 : buf[wptr++] = (nCable << 4) | 0x6; //x6 SysEx ends with following two bytes. buf[wptr++] = *(dataptr++); buf[wptr++] = *(dataptr++); buf[wptr++] = 0x00; - n = n - 2; + n = 0; break; case 3 : buf[wptr] = (nCable << 4) | 0x7; //x7 SysEx ends with following three bytes. @@ -621,19 +507,12 @@ uint8_t USBH_MIDI::SendSysEx(uint8_t *dataptr, uint16_t datasize, uint8_t nCable if( (rc = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, wptr, buf)) != 0 ){ break; } - wptr = 0; //rewind data pointer + wptr = 0; //rewind write pointer } } return(rc); } -/* Send raw data to MIDI device */ -uint8_t USBH_MIDI::SendRawData(uint16_t bytes_send, uint8_t *dataptr) -{ - return pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, bytes_send, dataptr); - -} - uint8_t USBH_MIDI::extractSysExData(uint8_t *p, uint8_t *buf) { uint8_t rc = 0; @@ -664,3 +543,150 @@ uint8_t USBH_MIDI::extractSysExData(uint8_t *p, uint8_t *buf) } return(rc); } + +// Configuration Descriptor Parser +// Copied from confdescparser.h and modifiy. +MidiDescParser::MidiDescParser(UsbMidiConfigXtracter *xtractor, bool modeMidi) : +theXtractor(xtractor), +stateParseDescr(0), +dscrLen(0), +dscrType(0), +nEPs(0), +isMidiSearch(modeMidi){ + theBuffer.pValue = varBuffer; + valParser.Initialize(&theBuffer); + theSkipper.Initialize(&theBuffer); +} +void MidiDescParser::Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset __attribute__((unused))) { + uint16_t cntdn = (uint16_t)len; + uint8_t *p = (uint8_t*)pbuf; + + while(cntdn) + if(!ParseDescriptor(&p, &cntdn)) + return; +} + +bool MidiDescParser::ParseDescriptor(uint8_t **pp, uint16_t *pcntdn) { + USB_CONFIGURATION_DESCRIPTOR* ucd = reinterpret_cast(varBuffer); + USB_INTERFACE_DESCRIPTOR* uid = reinterpret_cast(varBuffer); + switch(stateParseDescr) { + case 0: + theBuffer.valueSize = 2; + valParser.Initialize(&theBuffer); + stateParseDescr = 1; + // fall through + case 1: + if(!valParser.Parse(pp, pcntdn)) + return false; + dscrLen = *((uint8_t*)theBuffer.pValue); + dscrType = *((uint8_t*)theBuffer.pValue + 1); + stateParseDescr = 2; + // fall through + case 2: + // This is a sort of hack. Assuming that two bytes are all ready in the buffer + // the pointer is positioned two bytes ahead in order for the rest of descriptor + // to be read right after the size and the type fields. + // This should be used carefully. varBuffer should be used directly to handle data + // in the buffer. + theBuffer.pValue = varBuffer + 2; + stateParseDescr = 3; + // fall through + case 3: + switch(dscrType) { + case USB_DESCRIPTOR_INTERFACE: + isGoodInterface = false; + break; + case USB_DESCRIPTOR_CONFIGURATION: + case USB_DESCRIPTOR_ENDPOINT: + case HID_DESCRIPTOR_HID: + break; + } + theBuffer.valueSize = dscrLen - 2; + valParser.Initialize(&theBuffer); + stateParseDescr = 4; + // fall through + case 4: + switch(dscrType) { + case USB_DESCRIPTOR_CONFIGURATION: + if(!valParser.Parse(pp, pcntdn)) + return false; + confValue = ucd->bConfigurationValue; + break; + case USB_DESCRIPTOR_INTERFACE: + if(!valParser.Parse(pp, pcntdn)) + return false; + USBTRACE("Interface descriptor:\r\n"); + USBTRACE2(" Inf#:\t\t", uid->bInterfaceNumber); + USBTRACE2(" Alt:\t\t", uid->bAlternateSetting); + USBTRACE2(" EPs:\t\t", uid->bNumEndpoints); + USBTRACE2(" IntCl:\t\t", uid->bInterfaceClass); + USBTRACE2(" IntSubcl:\t", uid->bInterfaceSubClass); + USBTRACE2(" Protocol:\t", uid->bInterfaceProtocol); + // MIDI check mode ? + if( isMidiSearch ){ //true: MIDI Streaming, false: ALL + if( uid->bInterfaceClass == USB_CLASS_AUDIO && uid->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING ) { + // MIDI found. + USBTRACE("+MIDI found\r\n\r\n"); + }else{ + USBTRACE("-MIDI not found\r\n\r\n"); + break; + } + } + isGoodInterface = true; + // Initialize the counter if no two endpoints can be found in one interface. + if(nEPs < 2) + // reset endpoint counter + nEPs = 0; + break; + case USB_DESCRIPTOR_ENDPOINT: + if(!valParser.Parse(pp, pcntdn)) + return false; + if(isGoodInterface && nEPs < 2){ + USBTRACE(">Extracting endpoint\r\n"); + if( theXtractor->EndpointXtract(confValue, 0, 0, 0, (USB_ENDPOINT_DESCRIPTOR*)varBuffer) ) + nEPs++; + } + break; + + default: + if(!theSkipper.Skip(pp, pcntdn, dscrLen - 2)) + return false; + } + theBuffer.pValue = varBuffer; + stateParseDescr = 0; + } + return true; +} + +/* Extracts endpoint information from config descriptor */ +bool USBH_MIDI::EndpointXtract(uint8_t conf __attribute__((unused)), + uint8_t iface __attribute__((unused)), + uint8_t alt __attribute__((unused)), + uint8_t proto __attribute__((unused)), + const USB_ENDPOINT_DESCRIPTOR *pep) +{ + uint8_t index; + +#ifdef DEBUG_USB_HOST + PrintEndpointDescriptor(pep); +#endif + // Is the endpoint transfer type bulk? + if((pep->bmAttributes & bTransferTypeMask) == USB_TRANSFER_TYPE_BULK) { + USBTRACE("+valid EP found.\r\n"); + index = (pep->bEndpointAddress & 0x80) == 0x80 ? epDataInIndex : epDataOutIndex; + } else { + USBTRACE("-No valid EP found.\r\n"); + return false; + } + + // Fill the rest of endpoint data structure + epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F); + // The maximum packet size for the USB Host Shield 2.0 library is 64 bytes. + if(pep->wMaxPacketSize > MIDI_EVENT_PACKET_SIZE) { + epInfo[index].maxPktSize = MIDI_EVENT_PACKET_SIZE; + } else { + epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize; + } + + return true; +} diff --git a/usbh_midi.h b/usbh_midi.h index 4f1c4494..5309a185 100644 --- a/usbh_midi.h +++ b/usbh_midi.h @@ -1,7 +1,7 @@ /* ******************************************************************************* * USB-MIDI class driver for USB Host Shield 2.0 Library - * Copyright (c) 2012-2018 Yuuichi Akagawa + * Copyright (c) 2012-2021 Yuuichi Akagawa * * Idea from LPK25 USB-MIDI to Serial MIDI converter * by Collin Cunningham - makezine.com, narbotic.com @@ -26,31 +26,67 @@ #if !defined(_USBH_MIDI_H_) #define _USBH_MIDI_H_ -//#define DEBUG_USB_HOST #include "Usb.h" -#define MIDI_MAX_ENDPOINTS 5 //endpoint 0, bulk_IN(MIDI), bulk_OUT(MIDI), bulk_IN(VSP), bulk_OUT(VSP) +#define USBH_MIDI_VERSION 600 +#define MIDI_MAX_ENDPOINTS 3 //endpoint 0, bulk_IN(MIDI), bulk_OUT(MIDI) #define USB_SUBCLASS_MIDISTREAMING 3 -#define DESC_BUFF_SIZE 256 #define MIDI_EVENT_PACKET_SIZE 64 #define MIDI_MAX_SYSEX_SIZE 256 -class USBH_MIDI; -class USBH_MIDI : public USBDeviceConfig +namespace _ns_USBH_MIDI { +const uint8_t cin2len[] PROGMEM = {0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1}; +const uint8_t sys2cin[] PROGMEM = {0, 2, 3, 2, 0, 0, 5, 0, 0xf, 0, 0xf, 0xf, 0xf, 0, 0xf, 0xf}; +} + +// Endpoint Descriptor extracter Class +class UsbMidiConfigXtracter { +public: + //virtual void ConfigXtract(const USB_CONFIGURATION_DESCRIPTOR *conf) = 0; + //virtual void InterfaceXtract(uint8_t conf, const USB_INTERFACE_DESCRIPTOR *iface) = 0; + + virtual bool EndpointXtract(uint8_t conf __attribute__((unused)), uint8_t iface __attribute__((unused)), uint8_t alt __attribute__((unused)), uint8_t proto __attribute__((unused)), const USB_ENDPOINT_DESCRIPTOR *ep __attribute__((unused))) { + return true; + }; +}; +// Configuration Descriptor Parser Class +class MidiDescParser : public USBReadParser { + UsbMidiConfigXtracter *theXtractor; + MultiValueBuffer theBuffer; + MultiByteValueParser valParser; + ByteSkipper theSkipper; + uint8_t varBuffer[16 /*sizeof(USB_CONFIGURATION_DESCRIPTOR)*/]; + + uint8_t stateParseDescr; // ParseDescriptor state + + uint8_t dscrLen; // Descriptor length + uint8_t dscrType; // Descriptor type + uint8_t nEPs; // number of valid endpoint + bool isMidiSearch; //Configuration mode true: MIDI, false: Vendor specific + + bool isGoodInterface; // Apropriate interface flag + uint8_t confValue; // Configuration value + + bool ParseDescriptor(uint8_t **pp, uint16_t *pcntdn); + +public: + MidiDescParser(UsbMidiConfigXtracter *xtractor, bool modeMidi); + void Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset); + inline uint8_t getConfValue() { return confValue; }; + inline uint8_t getNumEPs() { return nEPs; }; +}; + +/** This class implements support for a MIDI device. */ +class USBH_MIDI : public USBDeviceConfig, public UsbMidiConfigXtracter { protected: - static const uint8_t epDataInIndex; // DataIn endpoint index(MIDI) - static const uint8_t epDataOutIndex; // DataOUT endpoint index(MIDI) - static const uint8_t epDataInIndexVSP; // DataIn endpoint index(Vendor Specific Protocl) - static const uint8_t epDataOutIndexVSP; // DataOUT endpoint index(Vendor Specific Protocl) + static const uint8_t epDataInIndex = 1; // DataIn endpoint index(MIDI) + static const uint8_t epDataOutIndex= 2; // DataOUT endpoint index(MIDI) /* mandatory members */ USB *pUsb; uint8_t bAddress; - uint8_t bConfNum; // configuration number - uint8_t bNumEP; // total number of EP in the configuration bool bPollEnable; - bool isMidiFound; uint16_t pid, vid; // ProductID, VendorID uint8_t bTransferTypeMask; /* Endpoint data structure */ @@ -59,27 +95,36 @@ protected: uint8_t recvBuf[MIDI_EVENT_PACKET_SIZE]; uint8_t readPtr; - uint8_t parseConfigDescr(uint8_t addr, uint8_t conf); uint16_t countSysExDataSize(uint8_t *dataptr); void setupDeviceSpecific(); + inline uint8_t convertStatus2Cin(uint8_t status) { + return ((status < 0xf0) ? ((status & 0xF0) >> 4) : pgm_read_byte_near(_ns_USBH_MIDI::sys2cin + (status & 0x0F))); + }; + inline uint8_t getMsgSizeFromCin(uint8_t cin) { + return pgm_read_byte_near(_ns_USBH_MIDI::cin2len + cin); + }; + + /* UsbConfigXtracter implementation */ + bool EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep); + #ifdef DEBUG_USB_HOST void PrintEndpointDescriptor( const USB_ENDPOINT_DESCRIPTOR* ep_ptr ); #endif public: USBH_MIDI(USB *p); // Misc functions - operator bool() { return (pUsb->getUsbTaskState()==USB_STATE_RUNNING); } + operator bool() { return (bPollEnable); } uint16_t idVendor() { return vid; } uint16_t idProduct() { return pid; } // Methods for recieving and sending data uint8_t RecvData(uint16_t *bytes_rcvd, uint8_t *dataptr); uint8_t RecvData(uint8_t *outBuf, bool isRaw=false); - uint8_t RecvRawData(uint8_t *outBuf); + inline uint8_t RecvRawData(uint8_t *outBuf) { return RecvData(outBuf, true); }; uint8_t SendData(uint8_t *dataptr, uint8_t nCable=0); + inline uint8_t SendRawData(uint16_t bytes_send, uint8_t *dataptr) { return pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, bytes_send, dataptr); }; uint8_t lookupMsgSize(uint8_t midiMsg, uint8_t cin=0); uint8_t SendSysEx(uint8_t *dataptr, uint16_t datasize, uint8_t nCable=0); uint8_t extractSysExData(uint8_t *p, uint8_t *buf); - uint8_t SendRawData(uint16_t bytes_send, uint8_t *dataptr); // backward compatibility functions inline uint8_t RcvData(uint16_t *bytes_rcvd, uint8_t *dataptr) { return RecvData(bytes_rcvd, dataptr); }; inline uint8_t RcvData(uint8_t *outBuf) { return RecvData(outBuf); }; @@ -88,5 +133,12 @@ public: virtual uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); virtual uint8_t Release(); virtual uint8_t GetAddress() { return bAddress; }; + + void attachOnInit(void (*funcOnInit)(void)) { + pFuncOnInit = funcOnInit; + }; +private: + void (*pFuncOnInit)(void) = nullptr; // Pointer to function called in onInit() }; + #endif //_USBH_MIDI_H_ From 4651acd2c66e1dc506d0e9deca6bba82287eba69 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 9 May 2021 21:06:22 +0200 Subject: [PATCH 90/93] Fixed "error: variable-sized object buf may not be initialized" warning --- SwitchProUSB.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/SwitchProUSB.h b/SwitchProUSB.h index 99ee7d05..43fdadfb 100644 --- a/SwitchProUSB.h +++ b/SwitchProUSB.h @@ -89,15 +89,26 @@ protected: setLedOn(LED1); // Turn on the LED1 setLedHomeOn(); // Turn on the home LED } - }; + } return 0; }; /**@}*/ /** @name SwitchProParser implementation */ virtual void sendOutputReport(uint8_t *data, uint8_t len) { + // Based on: https://github.com/Dan611/hid-procon // The first 8 bytes are always the same. The actual report follows - uint8_t buf[8 + len] = { 0x80 /* PROCON_REPORT_SEND_USB */, 0x92 /*PROCON_USB_DO_CMD */, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00 }; + uint8_t buf[8 + len]; + buf[0] = 0x80; // PROCON_REPORT_SEND_USB + buf[1] = 0x92; // PROCON_USB_DO_CMD + buf[2] = 0x00; + buf[3] = 0x31; + buf[4] = 0x00; + buf[5] = 0x00; + buf[6] = 0x00; + buf[7] = 0x00; + + // Cope over the report memcpy(buf + 8, data, len); // Endpoint (control endpoint), Interface (0x00), Report Type (Output 0x02), Report ID (0x80), nbytes, data From ec8890d40ee888fedd84a955862ec6126d3edd1d Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 9 May 2021 21:07:14 +0200 Subject: [PATCH 91/93] Can now also parse the battery level and charging status --- SwitchProParser.cpp | 6 ++--- SwitchProParser.h | 25 ++++++++++++++++--- .../Bluetooth/SwitchProBT/SwitchProBT.ino | 7 +++++- examples/SwitchProUSB/SwitchProUSB.ino | 7 +++++- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/SwitchProParser.cpp b/SwitchProParser.cpp index 10c9155a..61704cc1 100644 --- a/SwitchProParser.cpp +++ b/SwitchProParser.cpp @@ -71,14 +71,14 @@ void SwitchProParser::Parse(uint8_t len, uint8_t *buf) { if (buf[0] == 0x3F) // Simple input report via Bluetooth switchProOutput.enableFullReportMode = true; // Switch over to the full report else if (buf[0] == 0x30) { // Standard full mode - if (len < 4) { + if (len < 3) { #ifdef DEBUG_USB_HOST Notify(PSTR("\r\nReport is too short: "), 0x80); D_PrintHex (len, 0x80); #endif return; } - memcpy(&switchProData, buf + 3, min((uint8_t)(len - 3), MFK_CASTUINT8T sizeof(switchProData))); + memcpy(&switchProData, buf + 2, min((uint8_t)(len - 2), MFK_CASTUINT8T sizeof(switchProData))); if (switchProData.btn.val != oldButtonState.val) { // Check if anything has changed buttonClickState.val = switchProData.btn.val & ~oldButtonState.val; // Update click state variable @@ -250,4 +250,4 @@ void SwitchProParser::Reset() { switchProOutput.enableImu = -1; switchProOutput.sendHandshake = false; switchProOutput.disableTimeout = false; -}; +} diff --git a/SwitchProParser.h b/SwitchProParser.h index 72d1fcb7..9eebf405 100644 --- a/SwitchProParser.h +++ b/SwitchProParser.h @@ -100,7 +100,10 @@ struct ImuData { } __attribute__((packed)); struct SwitchProData { - // TODO: Add byte 2 containing battery level and connection info + struct { + uint8_t connection_info : 4; + uint8_t battery_level : 4; + } __attribute__((packed)); /* Button and joystick values */ SwitchProButtons btn; // Bytes 3-5 @@ -316,10 +319,27 @@ public: } /** Get the incoming message count. */ - uint16_t getMessageCounter(){ + uint16_t getMessageCounter() { return message_counter; } + /** + * Return the battery level of the Switch Pro Controller. + * @return The battery level as a bit mask according to ::SwitchProBatteryLevel: + * 4=full, 3=medium, 2=low, 1=critical, 0=empty. + */ + uint8_t getBatteryLevel() { + return switchProData.battery_level >> 1; + } + + /** + * Returns whenever the controller is plugged in and charging. + * @return Returns True if the controller is charging. + */ + bool isCharging() { + return switchProData.battery_level & 0x01; + } + protected: /** * Used to parse data sent from the Switch Pro controller. @@ -352,7 +372,6 @@ protected: private: static int8_t getButtonIndexSwitchPro(ButtonEnum b); - bool checkDpad(ButtonEnum b); // Used to check Switch Pro DPAD buttons void sendOutputCmd(); void sendRumbleOutputReport(); diff --git a/examples/Bluetooth/SwitchProBT/SwitchProBT.ino b/examples/Bluetooth/SwitchProBT/SwitchProBT.ino index b21f693c..7e53c1eb 100644 --- a/examples/Bluetooth/SwitchProBT/SwitchProBT.ino +++ b/examples/Bluetooth/SwitchProBT/SwitchProBT.ino @@ -109,8 +109,13 @@ void loop() { if (SwitchPro.getButtonClick(A)) Serial.print(F("\r\nA")); - if (SwitchPro.getButtonClick(B)) + if (SwitchPro.getButtonClick(B)) { Serial.print(F("\r\nB")); + Serial.print(F("\r\nBattery level: ")); + Serial.print(SwitchPro.getBatteryLevel()); + Serial.print(F(", charging: ")); + Serial.print(SwitchPro.isCharging()); + } if (SwitchPro.getButtonClick(X)) Serial.print(F("\r\nX")); if (SwitchPro.getButtonClick(Y)) diff --git a/examples/SwitchProUSB/SwitchProUSB.ino b/examples/SwitchProUSB/SwitchProUSB.ino index b2b74d43..842e6394 100644 --- a/examples/SwitchProUSB/SwitchProUSB.ino +++ b/examples/SwitchProUSB/SwitchProUSB.ino @@ -90,8 +90,13 @@ void loop() { if (SwitchPro.getButtonClick(A)) Serial.print(F("\r\nA")); - if (SwitchPro.getButtonClick(B)) + if (SwitchPro.getButtonClick(B)) { Serial.print(F("\r\nB")); + Serial.print(F("\r\nBattery level: ")); + Serial.print(SwitchPro.getBatteryLevel()); + Serial.print(F(", charging: ")); + Serial.print(SwitchPro.isCharging()); + } if (SwitchPro.getButtonClick(X)) Serial.print(F("\r\nX")); if (SwitchPro.getButtonClick(Y)) From fd7a51cca400cd1239901dd2cee1f69ec2b84550 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 9 May 2021 22:59:42 +0200 Subject: [PATCH 92/93] Release version 1.6.0 --- library.json | 2 +- library.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library.json b/library.json index 591af18c..108b1fb4 100644 --- a/library.json +++ b/library.json @@ -29,7 +29,7 @@ "type": "git", "url": "https://github.com/felis/USB_Host_Shield_2.0.git" }, - "version": "1.5.0", + "version": "1.6.0", "license": "GPL-2.0", "examples": [ diff --git a/library.properties b/library.properties index 6ab93e66..3fbd082c 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=USB Host Shield Library 2.0 -version=1.5.0 +version=1.6.0 author=Oleg Mazurov (Circuits@Home) , Kristian Sloth Lauszus , Andrew Kroll , Alexei Glushchenko (Circuits@Home) maintainer=Oleg Mazurov (Circuits@Home) , Kristian Sloth Lauszus , Andrew Kroll sentence=Revision 2.0 of MAX3421E-based USB Host Shield Library. From a2df807ad43ee12be36a505e358c57910a7e40ed Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Fri, 14 May 2021 21:31:40 +0200 Subject: [PATCH 93/93] Added another PID for the Xbox 360 wireless receiver --- XBOXRECV.cpp | 2 +- XBOXRECV.h | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/XBOXRECV.cpp b/XBOXRECV.cpp index 0ba3b6b0..3620a997 100644 --- a/XBOXRECV.cpp +++ b/XBOXRECV.cpp @@ -89,7 +89,7 @@ uint8_t XBOXRECV::ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) { VID = udd->idVendor; PID = udd->idProduct; - if((VID != XBOX_VID && VID != MADCATZ_VID && VID != JOYTECH_VID) || (PID != XBOX_WIRELESS_RECEIVER_PID && PID != XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID)) { // Check if it's a Xbox receiver using the Vendor ID and Product ID + if((VID != XBOX_VID && VID != MADCATZ_VID && VID != JOYTECH_VID) || (PID != XBOX_WIRELESS_RECEIVER_PID_1 && PID != XBOX_WIRELESS_RECEIVER_PID_2 && PID != XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID)) { // Check if it's a Xbox receiver using the Vendor ID and Product ID #ifdef DEBUG_USB_HOST Notify(PSTR("\r\nYou'll need a wireless receiver for this libary to work"), 0x80); #endif diff --git a/XBOXRECV.h b/XBOXRECV.h index 4f921465..34047608 100644 --- a/XBOXRECV.h +++ b/XBOXRECV.h @@ -42,7 +42,8 @@ #define MADCATZ_VID 0x1BAD // For unofficial Mad Catz receivers #define JOYTECH_VID 0x162E // For unofficial Joytech controllers -#define XBOX_WIRELESS_RECEIVER_PID 0x0719 // Microsoft Wireless Gaming Receiver +#define XBOX_WIRELESS_RECEIVER_PID_1 0x0719 // Microsoft Wireless Gaming Receiver +#define XBOX_WIRELESS_RECEIVER_PID_2 0x02A9 // Microsoft Wireless Gaming Receiver #define XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID 0x0291 // Third party Wireless Gaming Receiver #define XBOX_MAX_ENDPOINTS 9 @@ -111,7 +112,7 @@ public: * @return Returns true if the device's VID and PID matches this driver. */ virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { - return ((vid == XBOX_VID || vid == MADCATZ_VID || vid == JOYTECH_VID) && (pid == XBOX_WIRELESS_RECEIVER_PID || pid == XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID)); + return ((vid == XBOX_VID || vid == MADCATZ_VID || vid == JOYTECH_VID) && (pid == XBOX_WIRELESS_RECEIVER_PID_1 || pid == XBOX_WIRELESS_RECEIVER_PID_2 || pid == XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID)); }; /**@}*/