Added support for Wii U Pro Controller

This commit is contained in:
Kristian Sloth Lauszus 2013-02-02 22:14:01 +01:00
parent f8beb5cfa7
commit 355ca892eb
9 changed files with 272 additions and 58 deletions

69
BTD.cpp
View file

@ -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;
@ -595,13 +595,15 @@ void BTD::HCI_task() {
hci_inquiry_cancel(); // Stop inquiry
#ifdef DEBUG
Notify(PSTR("\r\nWiimote found"));
if(motionPlusInside)
Notify(PSTR(" with Motion Plus Inside"));
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;
@ -665,8 +667,34 @@ void BTD::HCI_task() {
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;
@ -681,20 +709,6 @@ void BTD::HCI_task() {
PrintHex<uint8_t>(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;
}
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;

1
BTD.h
View file

@ -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);

View file

@ -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

77
Wii.cpp
View file

@ -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 */
/************************************************************/

10
Wii.h
View file

@ -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
@ -118,9 +119,13 @@ public:
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

View file

@ -36,9 +36,9 @@ const uint16_t BUTTONS[] PROGMEM = {
0x0400, // LEFT
0x2000, // BACK
0x1000, // START
0x4000, // L3
0x8000, // R3
0x1000, // START
0,0, // Skip L2 and R2 as these are analog buttons
0x0001, // L1

View file

@ -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,

View file

@ -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 <Wii.h>
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));
}
}
}

View file

@ -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
@ -231,3 +217,44 @@ getPitch KEYWORD2
getRoll KEYWORD2
getYaw KEYWORD2
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