23 #define DEBUG // Uncomment to print data for debugging
41 const uint32_t BUTTONS[]
PROGMEM = {
60 const uint32_t PROCONTROLLERBUTTONS[]
PROGMEM = {
97 control_dcid[0] = 0x60;
98 control_dcid[1] = 0x00;
99 interrupt_dcid[0] = 0x61;
100 interrupt_dcid[1] = 0x00;
108 activateNunchuck =
false;
109 motionValuesReset =
false;
110 activeConnection =
false;
114 l2cap_event_flag = 0;
128 if((l2capinbuf[12] | (l2capinbuf[13] << 8)) ==
HID_CTRL_PSM) {
131 activeConnection =
true;
137 if ((l2capinbuf[0] | (l2capinbuf[1] << 8)) == (hci_handle | 0x2000)) {
138 if ((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001) {
141 Notify(PSTR(
"\r\nL2CAP Command Rejected - Reason: "));
142 PrintHex<uint8_t>(l2capinbuf[13]);
144 PrintHex<uint8_t>(l2capinbuf[12]);
146 PrintHex<uint8_t>(l2capinbuf[17]);
148 PrintHex<uint8_t>(l2capinbuf[16]);
150 PrintHex<uint8_t>(l2capinbuf[15]);
152 PrintHex<uint8_t>(l2capinbuf[14]);
156 if (((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) && ((l2capinbuf[18] | (l2capinbuf[19] << 8)) ==
SUCCESSFUL)) {
157 if (l2capinbuf[14] == control_dcid[0] && l2capinbuf[15] == control_dcid[1]) {
159 identifier = l2capinbuf[9];
160 control_scid[0] = l2capinbuf[12];
161 control_scid[1] = l2capinbuf[13];
164 else if (l2capinbuf[14] == interrupt_dcid[0] && l2capinbuf[15] == interrupt_dcid[1]) {
166 identifier = l2capinbuf[9];
167 interrupt_scid[0] = l2capinbuf[12];
168 interrupt_scid[1] = l2capinbuf[13];
175 Notify(PSTR(
"\r\nL2CAP Connection Request - PSM: "));
176 PrintHex<uint8_t>(l2capinbuf[13]);
178 PrintHex<uint8_t>(l2capinbuf[12]);
180 PrintHex<uint8_t>(l2capinbuf[15]);
182 PrintHex<uint8_t>(l2capinbuf[14]);
183 Notify(PSTR(
" Identifier: "));
184 PrintHex<uint8_t>(l2capinbuf[9]);
186 if ((l2capinbuf[12] | (l2capinbuf[13] << 8)) ==
HID_CTRL_PSM) {
187 identifier = l2capinbuf[9];
188 control_scid[0] = l2capinbuf[14];
189 control_scid[1] = l2capinbuf[15];
192 else if ((l2capinbuf[12] | (l2capinbuf[13] << 8)) ==
HID_INTR_PSM) {
193 identifier = l2capinbuf[9];
194 interrupt_scid[0] = l2capinbuf[14];
195 interrupt_scid[1] = l2capinbuf[15];
200 if ((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) {
201 if (l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
203 identifier = l2capinbuf[9];
206 else if (l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
208 identifier = l2capinbuf[9];
214 if (l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
218 else if (l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
224 if (l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
226 Notify(PSTR(
"\r\nDisconnect Request: Control Channel"));
228 identifier = l2capinbuf[9];
232 else if (l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
234 Notify(PSTR(
"\r\nDisconnect Request: Interrupt Channel"));
236 identifier = l2capinbuf[9];
242 if (l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) {
244 identifier = l2capinbuf[9];
247 else if (l2capinbuf[12] == interrupt_scid[0] && l2capinbuf[13] == interrupt_scid[1]) {
249 identifier = l2capinbuf[9];
255 identifier = l2capinbuf[9];
256 Notify(PSTR(
"\r\nL2CAP Unknown Signaling Command: "));
257 PrintHex<uint8_t>(l2capinbuf[8]);
260 }
else if (l2capinbuf[6] == interrupt_dcid[0] && l2capinbuf[7] == interrupt_dcid[1]) {
263 if(l2capinbuf[8] == 0xA1) {
264 if((l2capinbuf[9] >= 0x20 && l2capinbuf[9] <= 0x22) || (l2capinbuf[9] >= 0x30 && l2capinbuf[9] <= 0x37) || l2capinbuf[9] == 0x3e || l2capinbuf[9] == 0x3f) {
265 if((l2capinbuf[9] >= 0x20 && l2capinbuf[9] <= 0x22) || l2capinbuf[9] == 0x31 || l2capinbuf[9] == 0x33)
266 ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8));
268 ButtonState = (uint32_t)(((~l2capinbuf[23]) & 0xFE) | ((uint16_t)(~l2capinbuf[24]) << 8) | ((uint32_t)((~l2capinbuf[25]) & 0x03) << 16));
270 if(l2capinbuf[20] & 0x02)
271 ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8) | ((uint32_t)(ButtonState & 0xFFFF0000)));
273 ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8) | ((uint32_t)((~l2capinbuf[20]) & 0x0C) << 14));
277 ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8) | ((uint32_t)((~l2capinbuf[20]) & 0x03) << 16));
279 else if(!unknownExtensionConnected)
280 ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8));
282 Notify(PSTR(
"ButtonState: "));
283 PrintHex<uint32_t>(ButtonState);
286 if(ButtonState != OldButtonState) {
287 ButtonClickState = ButtonState & ~OldButtonState;
288 OldButtonState = ButtonState;
291 if(l2capinbuf[9] == 0x31 || l2capinbuf[9] == 0x33 || l2capinbuf[9] == 0x35 || l2capinbuf[9] == 0x37) {
292 accX = ((l2capinbuf[12] << 2) | (l2capinbuf[10] & 0x60 >> 5))-500;
293 accY = ((l2capinbuf[13] << 2) | (l2capinbuf[11] & 0x20 >> 4))-500;
294 accZ = ((l2capinbuf[14] << 2) | (l2capinbuf[11] & 0x40 >> 5))-500;
298 switch (l2capinbuf[9]) {
300 wiiState = l2capinbuf[12];
301 batteryLevel = l2capinbuf[15];
302 if(l2capinbuf[12] & 0x01) {
304 Notify(PSTR(
"\r\nWARNING: Battery is nearly empty"));
307 if(l2capinbuf[12] & 0x02) {
309 if(!unknownExtensionConnected)
310 Notify(PSTR(
"\r\nExtension connected"));
312 unknownExtensionConnected =
true;
316 setReportMode(
false,0x35);
320 Notify(PSTR(
"\r\nExtension disconnected"));
324 Notify(PSTR(
" - from Motion Plus"));
327 if(!activateNunchuck)
333 Notify(PSTR(
" - Nunchuck"));
338 setReportMode(
false,0x31);
340 setReportMode(
false,0x31);
345 if((l2capinbuf[12] & 0x0F) == 0) {
347 if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA4 && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x00 && l2capinbuf[20] == 0x00) {
349 Notify(PSTR(
"\r\nNunchuck connected"));
352 }
else if(l2capinbuf[16] == 0x00 && (l2capinbuf[17] == 0xA6 || l2capinbuf[17] == 0xA4) && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x00 && l2capinbuf[20] == 0x05) {
354 Notify(PSTR(
"\r\nMotion Plus connected"));
357 }
else if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA4 && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x04 && l2capinbuf[20] == 0x05) {
359 Notify(PSTR(
"\r\nMotion Plus activated in normal mode"));
362 }
else if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA4 && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x05 && l2capinbuf[20] == 0x05) {
364 Notify(PSTR(
"\r\nMotion Plus activated in Nunchuck pass-through mode"));
366 activateNunchuck =
false;
369 }
else if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA6 && l2capinbuf[18] == 0x20 && (l2capinbuf[19] == 0x00 || l2capinbuf[19] == 0x04 || l2capinbuf[19] == 0x05 || l2capinbuf[19] == 0x07) && l2capinbuf[20] == 0x05) {
371 Notify(PSTR(
"\r\nInactive Wii Motion Plus"));
372 Notify(PSTR(
"\r\nPlease unplug the Motion Plus, disconnect the Wiimote and then replug the Motion Plus Extension"));
375 }
else if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA4 && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x01 && l2capinbuf[20] == 0x20) {
377 Notify(PSTR(
"\r\nWii U Pro Controller connected"));
383 Notify(PSTR(
"\r\nUnknown Device: "));
384 PrintHex<uint8_t>(l2capinbuf[13]);
385 PrintHex<uint8_t>(l2capinbuf[14]);
386 Notify(PSTR(
"\r\nData: "));
387 for(uint8_t i = 0; i < ((l2capinbuf[12] >> 4)+1); i++) {
388 PrintHex<uint8_t>(l2capinbuf[15+i]);
396 Notify(PSTR(
"\r\nReport Error: "));
397 PrintHex<uint8_t>(l2capinbuf[13]);
398 PrintHex<uint8_t>(l2capinbuf[14]);
404 if(l2capinbuf[13] != 0x00) {
405 Notify(PSTR(
"\r\nCommand failed: "));
406 PrintHex<uint8_t>(l2capinbuf[12]);
422 IR_object_x1 = (l2capinbuf[15] | ((uint16_t)(l2capinbuf[17] & 0x30) << 4));
423 IR_object_y1 = (l2capinbuf[16] | ((uint16_t)(l2capinbuf[17] & 0xC0) << 2));
424 IR_object_s1 = (l2capinbuf[17] & 0x0F);
426 IR_object_x2 = (l2capinbuf[18] | ((uint16_t)(l2capinbuf[20] & 0x30) << 4));
427 IR_object_y2 = (l2capinbuf[19] | ((uint16_t)(l2capinbuf[20] & 0xC0) << 2));
428 IR_object_s2 = (l2capinbuf[20] & 0x0F);
430 IR_object_x3 = (l2capinbuf[21] | ((uint16_t)(l2capinbuf[23] & 0x30) << 4));
431 IR_object_y3 = (l2capinbuf[22] | ((uint16_t)(l2capinbuf[23] & 0xC0) << 2));
432 IR_object_s3 = (l2capinbuf[23] & 0x0F);
434 IR_object_x4 = (l2capinbuf[24] | ((uint16_t)(l2capinbuf[26] & 0x30) << 4));
435 IR_object_y4 = (l2capinbuf[25] | ((uint16_t)(l2capinbuf[26] & 0xC0) << 2));
436 IR_object_s4 = (l2capinbuf[26] & 0x0F);
464 if(l2capinbuf[20] & 0x02) {
465 if(motionValuesReset) {
475 if(!(l2capinbuf[18] & 0x02))
477 if(!(l2capinbuf[18] & 0x01))
479 if(!(l2capinbuf[19] & 0x02))
505 if((micros() - timer) > 1000000) {
507 Notify(PSTR(
"\r\nThe gyro values has been reset"));
509 gyroYawZero = (l2capinbuf[15] | ((l2capinbuf[18] & 0xFC) << 6));
510 gyroRollZero = (l2capinbuf[16] | ((l2capinbuf[19] & 0xFC) << 6));
511 gyroPitchZero = (l2capinbuf[17] | ((l2capinbuf[20] & 0xFC) << 6));
521 motionValuesReset =
true;
527 hatValues[
HatX] = l2capinbuf[15];
528 hatValues[
HatY] = l2capinbuf[16];
529 accX = ((l2capinbuf[17] << 2) | (l2capinbuf[20] & 0x10 >> 3))-416;
530 accY = ((l2capinbuf[18] << 2) | (l2capinbuf[20] & 0x20 >> 4))-416;
531 accZ = (((l2capinbuf[19] & 0xFE) << 2) | (l2capinbuf[20] & 0xC0 >> 5))-416;
537 if(l2capinbuf[19] & 0x01) {
538 if(!extensionConnected) {
539 extensionConnected =
true;
540 unknownExtensionConnected =
true;
542 Notify(PSTR(
"\r\nExtension connected to Motion Plus"));
547 if(extensionConnected && !unknownExtensionConnected) {
548 extensionConnected =
false;
549 unknownExtensionConnected =
true;
551 Notify(PSTR(
"\r\nExtension disconnected from Motion Plus"));
558 hatValues[
HatX] = l2capinbuf[15];
559 hatValues[
HatY] = l2capinbuf[16];
560 accX = ((l2capinbuf[17] << 2) | (l2capinbuf[20] & 0x0C >> 2))-416;
561 accY = ((l2capinbuf[18] << 2) | (l2capinbuf[20] & 0x30 >> 4))-416;
562 accZ = ((l2capinbuf[19] << 2) | (l2capinbuf[20] & 0xC0 >> 6))-416;
569 hatValues[
LeftHatX] = (l2capinbuf[15] | l2capinbuf[16] << 8);
570 hatValues[
RightHatX] = (l2capinbuf[17] | l2capinbuf[18] << 8);
571 hatValues[
LeftHatY] = (l2capinbuf[19] | l2capinbuf[20] << 8);
572 hatValues[
RightHatY] = (l2capinbuf[21] | l2capinbuf[22] << 8);
577 Notify(PSTR(
"\r\nUnknown Report type: "));
578 Serial.print(l2capinbuf[9],HEX);
588 void WII::L2CAP_task() {
589 switch (l2cap_state) {
594 Notify(PSTR(
"\r\nHID Control Successfully Configured"));
603 Notify(PSTR(
"\r\nHID Interrupt Incoming Connection Request"));
620 Notify(PSTR(
"\r\nSend HID Control Config Request"));
631 Notify(PSTR(
"\r\nSend HID Interrupt Connection Request"));
642 Notify(PSTR(
"\r\nSend HID Interrupt Config Request"));
653 Notify(PSTR(
"\r\nHID Channels Established"));
668 Notify(PSTR(
"\r\nDisconnected Interrupt Channel"));
679 Notify(PSTR(
"\r\nDisconnected Control Channel"));
683 l2cap_event_flag = 0;
690 switch (l2cap_state) {
694 activeConnection =
true;
696 Notify(PSTR(
"\r\nSend HID Control Connection Request"));
699 l2cap_event_flag = 0;
705 Notify(PSTR(
"\r\nHID Control Incoming Connection Request"));
719 if(stateCounter == 0)
720 Notify(PSTR(
"\r\nChecking if a Motion Plus is connected"));
723 if(stateCounter%200 == 0)
724 checkMotionPresent();
730 if(unknownExtensionConnected) {
732 Notify(PSTR(
"\r\nA extension is also connected"));
734 activateNunchuck =
true;
738 else if(stateCounter == 601) {
740 Notify(PSTR(
"\r\nNo Motion Plus was detected"));
749 if(stateCounter == 0)
750 Notify(PSTR(
"\r\nChecking if there is any extension connected"));
753 if(stateCounter == 1)
755 if(stateCounter == 100) {
756 if(unknownExtensionConnected)
760 }
else if(stateCounter == 200)
762 else if(stateCounter == 300) {
764 unknownExtensionConnected =
false;
765 }
else if(stateCounter == 400) {
773 if(stateCounter == 1)
775 else if(stateCounter == 100)
776 activateMotionPlus();
777 else if(stateCounter == 200)
779 else if(stateCounter == 300) {
781 unknownExtensionConnected =
false;
794 if(unknownExtensionConnected) {
796 if(stateCounter == 0)
797 Notify(PSTR(
"\r\nChecking extension port"));
800 if(stateCounter == 50)
802 else if(stateCounter == 100)
804 else if(stateCounter == 150)
809 else if(stateCounter == 200)
811 else if(stateCounter == 250) {
814 Notify(PSTR(
"\r\nNunchuck was reconnected"));
816 activateNunchuck =
true;
822 else if (stateCounter == 300) {
825 Notify(PSTR(
"\r\nReactivating the Motion Plus"));
831 else if(stateCounter == 350)
832 activateMotionPlus();
833 else if(stateCounter == 400)
835 else if(stateCounter == 450) {
838 unknownExtensionConnected =
false;
849 void WII::HID_Command(uint8_t* data, uint8_t nbytes) {
851 pBtd->
L2CAP_Command(hci_handle,data,nbytes,interrupt_scid[0],interrupt_scid[1]);
853 pBtd->
L2CAP_Command(hci_handle,data,nbytes,control_scid[0],control_scid[1]);
858 HID_Command(HIDBuffer, 3);
862 HIDBuffer[2] &= ~0x01;
863 HID_Command(HIDBuffer, 3);
867 HIDBuffer[2] |= 0x01;
868 HID_Command(HIDBuffer, 3);
872 HIDBuffer[2] ^= 0x01;
873 HID_Command(HIDBuffer, 3);
877 HIDBuffer[2] &= ~(pgm_read_byte(&LEDS[(uint8_t)a]));
878 HID_Command(HIDBuffer, 3);
882 HIDBuffer[2] |= pgm_read_byte(&LEDS[(uint8_t)a]);
883 HID_Command(HIDBuffer, 3);
887 HIDBuffer[2] ^= pgm_read_byte(&LEDS[(uint8_t)a]);
888 HID_Command(HIDBuffer, 3);
892 HIDBuffer[2] = (HIDBuffer[2] & 0x01);
894 HIDBuffer[2] |= 0x10;
896 HIDBuffer[2] |= 0x20;
898 HIDBuffer[2] |= 0x40;
900 HID_Command(HIDBuffer, 3);
902 void WII::setReportMode(
bool continuous, uint8_t mode) {
907 cmd_buf[2] = 0x04 | (HIDBuffer[2] & 0x01);
909 cmd_buf[2] = 0x00 | (HIDBuffer[2] & 0x01);
911 HID_Command(cmd_buf, 4);
913 void WII::statusRequest() {
917 cmd_buf[2] = (HIDBuffer[2] & 0x01);
918 HID_Command(cmd_buf, 3);
924 void WII::writeData(uint32_t offset, uint8_t size, uint8_t* data) {
928 cmd_buf[2] = 0x04 | (HIDBuffer[2] & 0x01);
929 cmd_buf[3] = (uint8_t)((offset & 0xFF0000) >> 16);
930 cmd_buf[4] = (uint8_t)((offset & 0xFF00) >> 8);
931 cmd_buf[5] = (uint8_t)(offset & 0xFF);
935 cmd_buf[7+i] = data[i];
938 HID_Command(cmd_buf,23);
940 void WII::initExtension1() {
943 writeData(0xA400F0,1,buf);
945 void WII::initExtension2() {
948 writeData(0xA400FB,1,buf);
950 void WII::initMotionPlus() {
953 writeData(0xA600F0,1,buf);
955 void WII::activateMotionPlus() {
959 Notify(PSTR(
"\r\nActivating Wii U Pro Controller"));
962 }
else if(activateNunchuck) {
964 Notify(PSTR(
"\r\nActivating Motion Plus in pass-through mode"));
972 Notify(PSTR(
"\r\nActivating Motion Plus in normal mode"));
976 writeData(0xA600FE,1,buf);
978 void WII::readData(uint32_t offset, uint16_t size,
bool EEPROM) {
983 cmd_buf[2] = 0x00 | (HIDBuffer[2] & 0x01);
985 cmd_buf[2] = 0x04 | (HIDBuffer[2] & 0x01);
986 cmd_buf[3] = (uint8_t)((offset & 0xFF0000) >> 16);
987 cmd_buf[4] = (uint8_t)((offset & 0xFF00) >> 8);
988 cmd_buf[5] = (uint8_t)(offset & 0xFF);
989 cmd_buf[6] = (uint8_t)((size & 0xFF00) >> 8);
990 cmd_buf[7] = (uint8_t)(size & 0xFF);
992 HID_Command(cmd_buf,8);
994 void WII::readExtensionType() {
995 readData(0xA400FA,6,
false);
997 void WII::readCalData() {
998 readData(0x0016,8,
true);
1000 void WII::checkMotionPresent() {
1001 readData(0xA600FA,6,
false);
1010 return (ButtonState & pgm_read_dword(&PROCONTROLLERBUTTONS[(uint8_t)b]));
1012 return (ButtonState & pgm_read_dword(&BUTTONS[(uint8_t)b]));
1017 button = pgm_read_dword(&PROCONTROLLERBUTTONS[(uint8_t)b]);
1019 button = pgm_read_dword(&BUTTONS[(uint8_t)b]);
1020 bool click = (ButtonClickState & button);
1021 ButtonClickState &= ~button;
1028 uint8_t output = hatValues[(uint8_t)a];
1029 if(output == 0xFF || output == 0x00)
1039 uint16_t output = hatValues[(uint8_t)a];
1056 Notify(PSTR(
"\r\nEnable IR Camera1 Complete"));
1062 Notify(PSTR(
"\r\nEnable IR Camera2 Complete"));
1068 Notify(PSTR(
"\r\nWrote hex number 0x08"));
1072 writeSensitivityBlock1();
1074 Notify(PSTR(
"\r\nWrote Sensitivity Block 1"));
1078 writeSensitivityBlock2();
1080 Notify(PSTR(
"\r\nWrote Sensitivity Block 2"));
1084 uint8_t mode_num = 0x03;
1085 setWiiModeNumber(mode_num);
1087 Notify(PSTR(
"\r\nSet Wii Mode Number To 0x"));
1088 PrintHex<uint8_t>(mode_num);
1094 Notify(PSTR(
"\r\nWrote Hex Number 0x08"));
1098 setReportMode(
false, 0x33);
1101 Notify(PSTR(
"\r\nSet Report Mode to 0x33"));
1107 Notify(PSTR(
"\r\nIR Initialized"));
1111 void WII::enableIRCamera1(){
1115 cmd_buf[2] = 0x04 | (HIDBuffer[2] & 0x01);
1116 HID_Command(cmd_buf, 3);
1119 void WII::enableIRCamera2(){
1123 cmd_buf[2] = 0x04 | (HIDBuffer[2] & 0x01);
1124 HID_Command(cmd_buf, 3);
1127 void WII::writeSensitivityBlock1(){
1139 writeData(0xB00000, 9, buf);
1142 void WII::writeSensitivityBlock2(){
1147 writeData(0xB0001A, 2, buf);
1150 void WII::write0x08Value(){
1152 writeData(0xb00030, 1, &cmd);
1155 void WII::setWiiModeNumber(uint8_t mode_number){
1156 writeData(0xb00033,1,&mode_number);