From 355ca892eb612607bc3208d379a60e9b2c455eef Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sat, 2 Feb 2013 22:14:01 +0100 Subject: [PATCH] Added support for Wii U Pro Controller --- BTD.cpp | 77 +++++++++++----- BTD.h | 1 + PS3Enums.h | 2 +- Wii.cpp | 77 ++++++++++++++-- Wii.h | 10 +- XBOXRECV.cpp | 4 +- controllerEnums.h | 11 ++- .../WiiUProController/WiiUProController.ino | 91 +++++++++++++++++++ keywords.txt | 57 +++++++++--- 9 files changed, 272 insertions(+), 58 deletions(-) create mode 100644 examples/Bluetooth/WiiUProController/WiiUProController.ino diff --git a/BTD.cpp b/BTD.cpp index 43e0bcad..480456a2 100755 --- a/BTD.cpp +++ b/BTD.cpp @@ -581,7 +581,7 @@ void BTD::HCI_task() { case HCI_CHECK_WII_SERVICE: if(pairWithWii) { // Check if it should try to connect to a wiimote #ifdef DEBUG - Notify(PSTR("\r\nStarting inquiry\r\nPress 1 & 2 on the Wiimote")); + Notify(PSTR("\r\nStarting inquiry\r\nPress 1 & 2 on the Wiimote\r\nOr press sync if you are using a Wii U Pro Controller")); #endif hci_inquiry(); hci_state = HCI_INQUIRY_STATE; @@ -594,14 +594,16 @@ void BTD::HCI_task() { if(hci_wii_found) { hci_inquiry_cancel(); // Stop inquiry #ifdef DEBUG - Notify(PSTR("\r\nWiimote found")); - if(motionPlusInside) - Notify(PSTR(" with Motion Plus Inside")); + Notify(PSTR("\r\nWiimote found")); Notify(PSTR("\r\nNow just create the instance like so:")); Notify(PSTR("\r\nWII Wii(&Btd);")); Notify(PSTR("\r\nAnd then press any button on the Wiimote")); #endif - hci_state = HCI_CONNECT_WII_STATE; + if(motionPlusInside) { + hci_remote_name(); // We need to know the name to distinguish between a Wiimote and a Wii U Pro Controller + hci_state = HCI_REMOTE_NAME_STATE; + } else + hci_state = HCI_CONNECT_WII_STATE; } break; @@ -662,11 +664,37 @@ void BTD::HCI_task() { for (uint8_t i = 0; i < 30; i++) { if(remote_name[i] == NULL) break; - Serial.write(remote_name[i]); - } + Serial.write(remote_name[i]); + } #endif - hci_accept_connection(); - hci_state = HCI_CONNECTED_STATE; + if(strncmp((const char*)remote_name, "Nintendo", 8) == 0) { +#ifdef DEBUG + Notify(PSTR("\r\nWiimote is connecting")); +#endif + if(strncmp((const char*)remote_name, "Nintendo RVL-CNT-01-TR", 22) == 0) { +#ifdef DEBUG + Notify(PSTR(" with Motion Plus Inside")); +#endif + motionPlusInside = true; + } + else if(strncmp((const char*)remote_name, "Nintendo RVL-CNT-01-UC", 22) == 0) { +#ifdef DEBUG + Notify(PSTR(" - Wii U Pro Controller")); +#endif + motionPlusInside = true; + wiiUProController = true; + } else { + motionPlusInside = false; + wiiUProController = false; + } + incomingWii = true; + } + if(pairWithWii && motionPlusInside) + hci_state = HCI_CONNECT_WII_STATE; + else { + hci_accept_connection(); + hci_state = HCI_CONNECTED_STATE; + } } break; @@ -680,21 +708,7 @@ void BTD::HCI_task() { } PrintHex(disc_bdaddr[0]); #endif - l2capConnectionClaimed = false; - if(strncmp((const char*)remote_name, "Nintendo", 8) == 0) { -#ifdef DEBUG - Notify(PSTR("\r\nWiimote is connecting")); -#endif - if(strncmp((const char*)remote_name, "Nintendo RVL-CNT-01-TR", 22) == 0) { -#ifdef DEBUG - Notify(PSTR(" with Motion Plus Inside")); -#endif - motionPlusInside = true; - } else - motionPlusInside = false; - - incomingWii = true; - } + l2capConnectionClaimed = false; hci_event_flag = 0; hci_state = HCI_DONE_STATE; } @@ -887,7 +901,7 @@ void BTD::hci_pin_code_request_reply() { hcibuf[6] = disc_bdaddr[3]; hcibuf[7] = disc_bdaddr[4]; hcibuf[8] = disc_bdaddr[5]; - if(pairWithWii) { + if(pairWithWii && !wiiUProController) { hcibuf[9] = 6; // Pin length is the length of the bt address hcibuf[10] = disc_bdaddr[0]; // The pin is the Wiimotes bt address backwards hcibuf[11] = disc_bdaddr[1]; @@ -897,6 +911,19 @@ void BTD::hci_pin_code_request_reply() { hcibuf[15] = disc_bdaddr[5]; for(uint8_t i = 16; i < 26; i++) hcibuf[i] = 0x00; // The rest should be 0 + } else if(pairWithWii && wiiUProController) { +#ifdef DEBUG + Notify(PSTR("\r\nParing with Wii U Pro Controller")); +#endif + hcibuf[9] = 6; // Pin length is the length of the bt address + hcibuf[10] = my_bdaddr[0]; // The pin is the Wiimotes bt address backwards + hcibuf[11] = my_bdaddr[1]; + hcibuf[12] = my_bdaddr[2]; + hcibuf[13] = my_bdaddr[3]; + hcibuf[14] = my_bdaddr[4]; + hcibuf[15] = my_bdaddr[5]; + for(uint8_t i = 16; i < 26; i++) + hcibuf[i] = 0x00; // The rest should be 0 } else { hcibuf[9] = strlen(btdPin); // Length of pin uint8_t i; diff --git a/BTD.h b/BTD.h index 2cae3b7a..c409d1c7 100755 --- a/BTD.h +++ b/BTD.h @@ -185,6 +185,7 @@ public: bool incomingWii; bool pairWithWii; bool motionPlusInside; // True if it's the new Wiimote with the Motion Plus Inside + bool wiiUProController; // True if it's a Wii U Pro Controller /* HCI Commands */ void HCI_Command(uint8_t* data, uint16_t nbytes); diff --git a/PS3Enums.h b/PS3Enums.h index 43b329a8..26e92281 100644 --- a/PS3Enums.h +++ b/PS3Enums.h @@ -40,9 +40,9 @@ const uint32_t BUTTONS[] PROGMEM = { 0x80, // LEFT 0x01, // SELECT + 0x08, // START 0x02, // L3 0x04, // R3 - 0x08, // START 0x0100, // L2 0x0200, // R2 diff --git a/Wii.cpp b/Wii.cpp index 37b5706d..f7238bb9 100755 --- a/Wii.cpp +++ b/Wii.cpp @@ -57,6 +57,31 @@ const uint32_t BUTTONS[] PROGMEM = { 0x00400, // B 0x00800 // A }; +const uint32_t PROCONTROLLERBUTTONS[] PROGMEM = { + 0x00100, // UP + 0x00080, // RIGHT + 0x00040, // DOWN + 0x00200, // LEFT + + 0, // Skip + 0x00004, // PLUS + 0x20000, // L3 + 0x10000, // R3 + + 0x00010, // MINUS + 0x00008, // HOME + 0,0, // Skip + + 0x04000, // B + 0x01000, // A + 0x00800, // X + 0x02000, // Y + + 0x00020, // L + 0x00002, // R + 0x08000, // ZL + 0x00400 // ZR +}; WII::WII(BTD *p, bool pair): pBtd(p) // pointer to USB class instance - mandatory @@ -84,6 +109,8 @@ void WII::Reset() { motionValuesReset = false; activeConnection = false; pBtd->motionPlusInside = false; + pBtd->wiiUProController = false; + wiiUProControllerConnected = false; l2cap_event_flag = 0; // Reset flags l2cap_state = L2CAP_WAIT; } @@ -237,6 +264,8 @@ void WII::ACLData(uint8_t* l2capinbuf) { if((l2capinbuf[9] >= 0x20 && l2capinbuf[9] <= 0x22) || (l2capinbuf[9] >= 0x30 && l2capinbuf[9] <= 0x37) || l2capinbuf[9] == 0x3e || l2capinbuf[9] == 0x3f) { // These reports include the buttons if((l2capinbuf[9] >= 0x20 && l2capinbuf[9] <= 0x22) || l2capinbuf[9] == 0x31 || l2capinbuf[9] == 0x33) // These reports have no extensions bytes ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8)); + else if(wiiUProControllerConnected) + ButtonState = (uint32_t)(((~l2capinbuf[23]) & 0xFE) | ((uint16_t)(~l2capinbuf[24]) << 8) | ((uint32_t)((~l2capinbuf[25]) & 0x03) << 16)); else if(motionPlusConnected) { if(l2capinbuf[20] & 0x02) // Only update the wiimote buttons, since the extension bytes are from the Motion Plus ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8) | ((uint32_t)(ButtonState & 0xFFFF0000))); @@ -343,6 +372,11 @@ void WII::ACLData(uint8_t* l2capinbuf) { Notify(PSTR("\r\nPlease unplug the Motion Plus, disconnect the Wiimote and then replug the Motion Plus Extension")); #endif stateCounter = 300; // Skip the rest in "L2CAP_CHECK_MOTION_PLUS_STATE" + } else if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA4 && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x01 && l2capinbuf[20] == 0x20) { +#ifdef DEBUG + Notify(PSTR("\r\nWii U Pro Controller connected")); +#endif + wiiUProControllerConnected = true; } #ifdef DEBUG else { @@ -490,8 +524,8 @@ void WII::ACLData(uint8_t* l2capinbuf) { } } else { if(nunchuckConnected) { - hatValues[0] = l2capinbuf[15]; - hatValues[1] = l2capinbuf[16]; + hatValues[HatX] = l2capinbuf[15]; + hatValues[HatY] = l2capinbuf[16]; accX = ((l2capinbuf[17] << 2) | (l2capinbuf[20] & 0x10 >> 3))-416; accY = ((l2capinbuf[18] << 2) | (l2capinbuf[20] & 0x20 >> 4))-416; accZ = (((l2capinbuf[19] & 0xFE) << 2) | (l2capinbuf[20] & 0xC0 >> 5))-416; @@ -521,8 +555,8 @@ void WII::ACLData(uint8_t* l2capinbuf) { } } else if(nunchuckConnected) { - hatValues[0] = l2capinbuf[15]; - hatValues[1] = l2capinbuf[16]; + hatValues[HatX] = l2capinbuf[15]; + hatValues[HatY] = l2capinbuf[16]; accX = ((l2capinbuf[17] << 2) | (l2capinbuf[20] & 0x0C >> 2))-416; accY = ((l2capinbuf[18] << 2) | (l2capinbuf[20] & 0x30 >> 4))-416; accZ = ((l2capinbuf[19] << 2) | (l2capinbuf[20] & 0xC0 >> 6))-416; @@ -531,6 +565,11 @@ void WII::ACLData(uint8_t* l2capinbuf) { pitch = wiiMotePitch; // The pitch is just equal to the angle calculated from the wiimote as there is no Motion Plus connected roll = wiiMoteRoll; + } else if(wiiUProControllerConnected) { + hatValues[LeftHatX] = (l2capinbuf[15] | l2capinbuf[16] << 8); + hatValues[RightHatX] = (l2capinbuf[17] | l2capinbuf[18] << 8); + hatValues[LeftHatY] = (l2capinbuf[19] | l2capinbuf[20] << 8); + hatValues[RightHatY] = (l2capinbuf[21] | l2capinbuf[22] << 8); } break; #ifdef DEBUG @@ -915,7 +954,12 @@ void WII::initMotionPlus() { } void WII::activateMotionPlus() { uint8_t buf[1]; - if(activateNunchuck) { + if(pBtd->wiiUProController) { +#ifdef DEBUG + Notify(PSTR("\r\nActivating Wii U Pro Controller")); +#endif + buf[0] = 0x00; // It seems like you can send anything but 0x04, 0x05, and 0x07 + } else if(activateNunchuck) { #ifdef DEBUG Notify(PSTR("\r\nActivating Motion Plus in pass-through mode")); #endif @@ -962,10 +1006,17 @@ void WII::checkMotionPresent() { /************************************************************/ bool WII::getButtonPress(Button b) { // Return true when a button is pressed - return (ButtonState & pgm_read_dword(&BUTTONS[(uint8_t)b])); + if(wiiUProControllerConnected) + return (ButtonState & pgm_read_dword(&PROCONTROLLERBUTTONS[(uint8_t)b])); + else + return (ButtonState & pgm_read_dword(&BUTTONS[(uint8_t)b])); } bool WII::getButtonClick(Button b) { // Only return true when a button is clicked - uint32_t button = pgm_read_dword(&BUTTONS[(uint8_t)b]); + uint32_t button; + if(wiiUProControllerConnected) + button = pgm_read_dword(&PROCONTROLLERBUTTONS[(uint8_t)b]); + else + button = pgm_read_dword(&BUTTONS[(uint8_t)b]); bool click = (ButtonClickState & button); ButtonClickState &= ~button; // clear "click" event return click; @@ -981,7 +1032,17 @@ uint8_t WII::getAnalogHat(Hat a) { return output; } } - +uint16_t WII::getAnalogHat(AnalogHat a) { + if(!wiiUProControllerConnected) + return 2000; + else { + uint16_t output = hatValues[(uint8_t)a]; + if(output == 0x00) // The joystick will only read 0 when it is first initializing, so we will just return the center position + return 2000; + else + return output; + } +} /************************************************************/ /* The following functions are for the IR camera */ /************************************************************/ diff --git a/Wii.h b/Wii.h index e8f7ca5b..1d4b1c2b 100755 --- a/Wii.h +++ b/Wii.h @@ -104,6 +104,7 @@ public: bool getButtonClick(Button b); // This will only be true when the button is clicked the first time uint8_t getAnalogHat(Hat a); // Used to read the joystick of the Nunchuck + uint16_t getAnalogHat(AnalogHat a); // Used to read the joystick of the Wii U Pro Controller double getPitch() { return pitch; }; // Fusioned angle using a complimentary filter if the Motion Plus is connected double getRoll() { return roll; }; // Fusioned angle using a complimentary filter if the Motion Plus is connected @@ -117,10 +118,14 @@ public: void setLedOn(LED a); void setLedToggle(LED a); void setLedStatus(); // This will set the LEDs, so the user can see which connections are active + + uint8_t getBatteryLevel() { return batteryLevel; }; // Return the battery level + uint8_t getWiiState() { return wiiState; }; // Return the wii state, see: http://wiibrew.org/wiki/Wiimote#0x20:_Status bool wiimoteConnected; // Variable used to indicate if a Wiimote is connected bool nunchuckConnected; // Variable used to indicate if a Nunchuck controller is connected bool motionPlusConnected; // Variable used to indicate if a Nunchuck controller is connected + bool wiiUProControllerConnected; // Variable used to indicate if a Wii U Pro controller is connected /* IMU Data, might be usefull if you need to do something more advanced than just calculating the angle */ @@ -154,9 +159,6 @@ public: int16_t gyroRollZero; int16_t gyroPitchZero; - uint8_t getBatteryLevel() { return batteryLevel; }; - uint8_t getWiiState() { return wiiState; }; - #ifdef WIICAMERA /* These are functions for the IR camera */ void IRinitialize(); // Initialises the camera as per the steps from http://wiibrew.org/wiki/Wiimote#IR_Camera @@ -197,7 +199,7 @@ private: uint32_t ButtonState; uint32_t OldButtonState; uint32_t ButtonClickState; - uint8_t hatValues[2]; + uint16_t hatValues[4]; uint8_t HIDBuffer[3];// Used to store HID commands diff --git a/XBOXRECV.cpp b/XBOXRECV.cpp index 353d71da..59d301c7 100644 --- a/XBOXRECV.cpp +++ b/XBOXRECV.cpp @@ -36,9 +36,9 @@ const uint16_t BUTTONS[] PROGMEM = { 0x0400, // LEFT 0x2000, // BACK - 0x4000, // L3 - 0x8000, // R3 0x1000, // START + 0x4000, // L3 + 0x8000, // R3 0,0, // Skip L2 and R2 as these are analog buttons 0x0001, // L1 diff --git a/controllerEnums.h b/controllerEnums.h index 2785657d..544750c8 100644 --- a/controllerEnums.h +++ b/controllerEnums.h @@ -54,12 +54,17 @@ enum Button { C = 11, B = 12, A = 13, + /* These are only available on the Wii U Pro Controller */ + L = 16, + R = 17, + ZL = 18, + ZR = 19, /* PS3 controllers buttons */ SELECT = 4, - L3 = 5, - R3 = 6, - START = 7, + START = 5, + L3 = 6, + R3 = 7, L2 = 8, R2 = 9, diff --git a/examples/Bluetooth/WiiUProController/WiiUProController.ino b/examples/Bluetooth/WiiUProController/WiiUProController.ino new file mode 100644 index 00000000..738bc899 --- /dev/null +++ b/examples/Bluetooth/WiiUProController/WiiUProController.ino @@ -0,0 +1,91 @@ +/* + Example sketch for the Wiimote Bluetooth library - developed by Kristian Lauszus + For more information visit my blog: http://blog.tkjelectronics.dk/ or + send me an e-mail: kristianl@tkjelectronics.com + */ + +#include +USB Usb; +BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so +/* You can create the instance of the class in two ways */ +WII Wii(&Btd,PAIR); // This will start an inquiry and then pair with your Wiimote - you only have to do this once +//WII Wii(&Btd); // After that you can simply create the instance like so and then press any button on the Wiimote + +void setup() { + Serial.begin(115200); + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while(1); //halt + } + Serial.print(F("\r\nWiimote Bluetooth Library Started")); +} +void loop() { + Usb.Task(); + if(Wii.wiiUProControllerConnected) { + if(Wii.getButtonClick(HOME)) { // You can use getButtonPress to see if the button is held down + Serial.print(F("\r\nHome")); + Wii.disconnect(); + } + else { + if(Wii.getButtonClick(LEFT)) { + Wii.setAllOff(); + Wii.setLedOn(LED1); + Serial.print(F("\r\nLeft")); + } + if(Wii.getButtonClick(RIGHT)) { + Wii.setAllOff(); + Wii.setLedOn(LED3); + Serial.print(F("\r\nRight")); + } + if(Wii.getButtonClick(DOWN)) { + Wii.setAllOff(); + Wii.setLedOn(LED4); + Serial.print(F("\r\nDown")); + } + if(Wii.getButtonClick(UP)) { + Wii.setAllOff(); + Wii.setLedOn(LED2); + Serial.print(F("\r\nUp")); + } + + if(Wii.getButtonClick(PLUS)) + Serial.print(F("\r\nPlus")); + if(Wii.getButtonClick(MINUS)) + Serial.print(F("\r\nMinus")); + + if(Wii.getButtonClick(A)) + Serial.print(F("\r\nA")); + if(Wii.getButtonClick(B)) { + Wii.setRumbleToggle(); + Serial.print(F("\r\nB")); + } + if(Wii.getButtonClick(X)) + Serial.print(F("\r\nX")); + if(Wii.getButtonClick(Y)) + Serial.print(F("\r\nY")); + + if(Wii.getButtonClick(L)) + Serial.print(F("\r\nL")); + if(Wii.getButtonClick(R)) + Serial.print(F("\r\nR")); + if(Wii.getButtonClick(ZL)) + Serial.print(F("\r\nZL")); + if(Wii.getButtonClick(ZR)) + Serial.print(F("\r\nZR")); + if(Wii.getButtonClick(L3)) + Serial.print(F("\r\nL3")); + if(Wii.getButtonClick(R3)) + Serial.print(F("\r\nR3")); + } + if(Wii.getAnalogHat(LeftHatX) > 2200 || Wii.getAnalogHat(LeftHatX) < 1800 || Wii.getAnalogHat(LeftHatY) > 2200 || Wii.getAnalogHat(LeftHatY) < 1800 || Wii.getAnalogHat(RightHatX) > 2200 || Wii.getAnalogHat(RightHatX) < 1800 || Wii.getAnalogHat(RightHatY) > 2200 || Wii.getAnalogHat(RightHatY) < 1800) { + Serial.print(F("\r\nLeftHatX: ")); + Serial.print(Wii.getAnalogHat(LeftHatX)); + Serial.print(F("\tLeftHatY: ")); + Serial.print(Wii.getAnalogHat(LeftHatY)); + Serial.print(F("\tRightHatX: ")); + Serial.print(Wii.getAnalogHat(RightHatX)); + Serial.print(F("\tRightHatY: ")); + Serial.print(Wii.getAnalogHat(RightHatY)); + } + } +} diff --git a/keywords.txt b/keywords.txt index 486536d1..28b13ff1 100644 --- a/keywords.txt +++ b/keywords.txt @@ -85,20 +85,6 @@ PS LITERAL1 MOVE LITERAL1 T LITERAL1 -UP_ANALOG LITERAL1 -RIGHT_ANALOG LITERAL1 -DOWN_ANALOG LITERAL1 -LEFT_ANALOG LITERAL1 -L2_ANALOG LITERAL1 -R2_ANALOG LITERAL1 -L1_ANALOG LITERAL1 -R1_ANALOG LITERAL1 -TRIANGLE_ANALOG LITERAL1 -CIRCLE_ANALOG LITERAL1 -CROSS_ANALOG LITERAL1 -SQUARE_ANALOG LITERAL1 -T_ANALOG LITERAL1 - LeftHatX LITERAL1 LeftHatY LITERAL1 RightHatX LITERAL1 @@ -230,4 +216,45 @@ setRumbleToggle KEYWORD2 getPitch KEYWORD2 getRoll KEYWORD2 getYaw KEYWORD2 -PAIR KEYWORD2 \ No newline at end of file +PAIR KEYWORD2 +getBatteryLevel KEYWORD2 +getWiiState KEYWORD2 + + +#################################################### +# Constants and enums (LITERAL1) +#################################################### + +PLUS LITERAL1 +MINUS LITERAL1 +ONE LITERAL1 +TWO LITERAL1 +HOME LITERAL1 +Z LITERAL1 +C LITERAL1 +L LITERAL1 +R LITERAL1 +ZL LITERAL1 +ZR LITERAL1 +HatX LITERAL1 +HatY LITERAL1 + + +#################################################### +# Methods and Functions for the IR Camera +#################################################### + +IRinitialize KEYWORD2 +isIRCameraEnabled KEYWORD2 +getIRx1 KEYWORD2 +getIRy1 KEYWORD2 +getIRs1 KEYWORD2 +getIRx2 KEYWORD2 +getIRy2 KEYWORD2 +getIRs2 KEYWORD2 +getIRx3 KEYWORD2 +getIRy3 KEYWORD2 +getIRs3 KEYWORD2 +getIRx4 KEYWORD2 +getIRy4 KEYWORD2 +getIRs4 KEYWORD2 \ No newline at end of file