Added support for Wiimote

This commit is contained in:
Kristian Lauszus 2012-08-21 14:31:11 +02:00
parent c8de94a060
commit a3ad5f2cdd
9 changed files with 816 additions and 43 deletions

211
BTD.cpp
View file

@ -38,7 +38,9 @@ bPollEnable(false) // Don't start polling before dongle is connected
}
if (pUsb) // register in USB subsystem
pUsb->RegisterDeviceClass(this); //set devConfig[] entry
pUsb->RegisterDeviceClass(this); //set devConfig[] entry
wiiServiceID = -1;
}
uint8_t BTD::Init(uint8_t parent, uint8_t port, bool lowspeed) {
@ -325,7 +327,7 @@ void BTD::HCI_event_task() {
{
switch (hcibuf[0]) //switch on event type
{
case EV_COMMAND_COMPLETE:
case EV_COMMAND_COMPLETE:
if (!hcibuf[5]) { // Check if command succeeded
hci_event_flag |= HCI_FLAG_CMD_COMPLETE; // set command complete flag
if((hcibuf[3] == 0x01) && (hcibuf[4] == 0x10)) { // parameters from read local version information
@ -352,11 +354,51 @@ void BTD::HCI_event_task() {
}
break;
case EV_INQUIRY_COMPLETE: // We don't use this for anything
break;
case EV_INQUIRY_RESULT:
if (hcibuf[2]) { // Check that there is more than zero responses
#ifdef EXTRADEBUG
Notify(PSTR("\r\nNumber of responses: "));
Serial.print(hcibuf[2]);
#endif
for(uint8_t i = 0; i < hcibuf[2]; i++) {
if(hcibuf[4+8*hcibuf[2]+3*i] == 0x04 && hcibuf[5+8*hcibuf[2]+3*i] == 0x25 && hcibuf[6+8*hcibuf[2]+3*i] == 0x00) { // See http://bluetooth-pentest.narod.ru/software/bluetooth_class_of_device-service_generator.html
disc_bdaddr[0] = hcibuf[3+6*i];
disc_bdaddr[1] = hcibuf[4+6*i];
disc_bdaddr[2] = hcibuf[5+6*i];
disc_bdaddr[3] = hcibuf[6+6*i];
disc_bdaddr[4] = hcibuf[7+6*i];
disc_bdaddr[5] = hcibuf[8+6*i];
hci_event_flag |= HCI_FLAG_WII_FOUND;
break;
}
#ifdef EXTRADEBUG
else {
Notify(PSTR("\r\nClass of device: "));
PrintHex<uint8_t>(hcibuf[6+8*hcibuf[2]+3*i]);
Notify(PSTR(" "));
PrintHex<uint8_t>(hcibuf[5+8*hcibuf[2]+3*i]);
Notify(PSTR(" "));
PrintHex<uint8_t>(hcibuf[4+8*hcibuf[2]+3*i]);
}
#endif
}
}
break;
case EV_CONNECT_COMPLETE:
hci_event_flag |= HCI_FLAG_CONNECT_EVENT;
if (!hcibuf[2]) { // check if connected OK
hci_handle = hcibuf[3] | hcibuf[4] << 8; // store the handle for the ACL connection
hci_handle = hcibuf[3] | ((hcibuf[4] & 0x0F) << 8); // store the handle for the ACL connection
hci_event_flag |= HCI_FLAG_CONN_COMPLETE; // set connection complete flag
}
#ifdef EXTRADEBUG
else {
Notify(PSTR("\r\nConnection Failed"));
}
#endif
break;
case EV_DISCONNECT_COMPLETE:
@ -479,7 +521,7 @@ void BTD::HCI_task() {
Notify(PSTR("\r\nLocal Bluetooth Address: "));
for(int8_t i = 5; i > 0;i--) {
PrintHex<uint8_t>(my_bdaddr[i]);
Serial.print(":");
Notify(PSTR(":"));
}
PrintHex<uint8_t>(my_bdaddr[0]);
#endif
@ -494,7 +536,7 @@ void BTD::HCI_task() {
hci_set_local_name(btdName);
hci_state = HCI_SET_NAME_STATE;
} else
hci_state = HCI_SCANNING_STATE;
hci_state = HCI_CHECK_WII_SERVICE;
}
break;
@ -504,17 +546,82 @@ void BTD::HCI_task() {
Notify(PSTR("\r\nThe name is set to: "));
Serial.print(btdName);
#endif
hci_state = HCI_SCANNING_STATE;
hci_state = HCI_CHECK_WII_SERVICE;
}
break;
case HCI_CHECK_WII_SERVICE:
if(wiiServiceID != -1) { // Check if it should try to connect to a wiimote
if(disc_bdaddr[5] == 0 && disc_bdaddr[4] == 0 && disc_bdaddr[3] == 0 && disc_bdaddr[2] == 0 && disc_bdaddr[1] == 0 && disc_bdaddr[0] == 0) {
#ifdef DEBUG
Notify(PSTR("\r\nStarting inquiry\r\nPress A & B on the Wiimote"));
#endif
hci_inquiry();
hci_state = HCI_INQUIRY_STATE;
}
else
hci_state = HCI_CONNECT_WII_STATE;
}
else
hci_state = HCI_SCANNING_STATE; // Don't try to connect to a Wiimote
break;
case HCI_INQUIRY_STATE:
if(hci_wii_found) {
hci_inquiry_cancel(); // Stop inquiry
#ifdef DEBUG
Notify(PSTR("\r\nWiimote found"));
Notify(PSTR("\r\nCreate the instance like so to connect automatically:"));
Notify(PSTR("\r\nWII Wii(&Btd,"));
for(int8_t i = 5; i>0;i--) {
Notify(PSTR("0x"));
PrintHex<uint8_t>(disc_bdaddr[i]);
Notify(PSTR(","));
}
Notify(PSTR("0x"));
PrintHex<uint8_t>(disc_bdaddr[0]);
Notify(PSTR(");"));
#endif
hci_state = HCI_CONNECT_WII_STATE;
}
break;
case HCI_CONNECT_WII_STATE:
if(!hci_wii_found || hci_cmd_complete) {
#ifdef DEBUG
Notify(PSTR("\r\nConnecting to Wiimote"));
#endif
hci_connect();
hci_state = HCI_CONNECTED_WII_STATE;
}
break;
case HCI_CONNECTED_WII_STATE:
if(hci_connect_event) {
if(hci_connect_complete) {
#ifdef DEBUG
Notify(PSTR("\r\nConnected to Wiimote"));
#endif
connectToWii = true; // Only send the ACL data to the Wii service
hci_state = HCI_SCANNING_STATE;
} else {
#ifdef DEBUG
Notify(PSTR("\r\nTrying to connect one more time..."));
#endif
hci_connect(); // Try to connect one more time
}
}
break;
case HCI_SCANNING_STATE:
if(!connectToWii) {
#ifdef DEBUG
Notify(PSTR("\r\nWait For Incoming Connection Request"));
Notify(PSTR("\r\nWait For Incoming Connection Request"));
#endif
hci_write_scan_enable();
watingForConnection = true;
hci_state = HCI_CONNECT_IN_STATE;
hci_write_scan_enable();
watingForConnection = true;
hci_state = HCI_CONNECT_IN_STATE;
}
break;
case HCI_CONNECT_IN_STATE:
@ -533,8 +640,7 @@ void BTD::HCI_task() {
if(hci_remote_name_complete) {
#ifdef DEBUG
Notify(PSTR("\r\nRemote Name: "));
for (uint8_t i = 0; i < 30; i++)
{
for (uint8_t i = 0; i < 30; i++) {
if(remote_name[i] == NULL)
break;
Serial.write(remote_name[i]);
@ -549,10 +655,9 @@ void BTD::HCI_task() {
if (hci_connect_complete) {
#ifdef DEBUG
Notify(PSTR("\r\nConnected to Device: "));
for(int8_t i = 5; i>0;i--)
{
for(int8_t i = 5; i>0;i--) {
PrintHex<uint8_t>(disc_bdaddr[i]);
Serial.print(":");
Notify(PSTR(":"));
}
PrintHex<uint8_t>(disc_bdaddr[0]);
#endif
@ -604,9 +709,13 @@ void BTD::ACL_event_task() {
uint16_t MAX_BUFFER_SIZE = BULK_MAXPKTSIZE;
uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ BTD_DATAIN_PIPE ].epAddr, &MAX_BUFFER_SIZE, l2capinbuf); // input on endpoint 2
if(!rcode) { // Check for errors
for (uint8_t i=0; i<BTD_NUMSERVICES; i++)
if (btService[i])
btService[i]->ACLData(l2capinbuf);
if(connectToWii) // Only send the data to the Wii service
btService[wiiServiceID]->ACLData(l2capinbuf);
else {
for (uint8_t i=0; i<BTD_NUMSERVICES; i++)
if (btService[i])
btService[i]->ACLData(l2capinbuf);
}
}
#ifdef EXTRADEBUG
else if (rcode != hrNAK) {
@ -664,6 +773,7 @@ void BTD::hci_read_local_version_information() {
HCI_Command(hcibuf, 3);
}
void BTD::hci_accept_connection() {
hci_event_flag &= ~HCI_FLAG_CONN_COMPLETE;
hcibuf[0] = 0x09; // HCI OCF = 9
hcibuf[1] = 0x01 << 2; // HCI OGF = 1
hcibuf[2] = 0x07; // parameter length 7
@ -706,6 +816,47 @@ void BTD::hci_set_local_name(const char* name) {
HCI_Command(hcibuf, 4+strlen(name));
}
void BTD::hci_inquiry() {
hci_event_flag &= ~HCI_FLAG_WII_FOUND;
hcibuf[0] = 0x01;
hcibuf[1] = 0x01 << 2; // HCI OGF = 1
hcibuf[2] = 0x05; // Parameter Total Length = 5
hcibuf[3] = 0x33; // LAP: Genera/Unlimited Inquiry Access Code (GIAC = 0x9E8B33) - see https://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm
hcibuf[4] = 0x8B;
hcibuf[5] = 0x9E;
hcibuf[6] = 0x0A; // Inquiry time = 12.8 sec
hcibuf[7] = 0x03; // 3 number of responses
HCI_Command(hcibuf, 8);
}
void BTD::hci_inquiry_cancel() {
hcibuf[0] = 0x02;
hcibuf[1] = 0x01 << 2; // HCI OGF = 1
hcibuf[2] = 0x0; // Parameter Total Length = 0
HCI_Command(hcibuf, 3);
}
void BTD::hci_connect() {
hci_event_flag &= ~(HCI_FLAG_CONN_COMPLETE | HCI_FLAG_CONNECT_EVENT);
hcibuf[0] = 0x05;
hcibuf[1] = 0x01 << 2; // HCI OGF = 1
hcibuf[2] = 0x0D; // parameter Total Length = 13
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] = 0x18; // DM1 or DH1 may be used
hcibuf[10] = 0xCC; // DM3, DH3, DM5, DH5 may be used
hcibuf[11] = 0x01; // Page repetition mode R1
hcibuf[12] = 0x00; // Reserved
hcibuf[13] = 0x00; // Clock offset
hcibuf[14] = 0x00; // Invalid clock offset
hcibuf[15] = 0x00; // Do not allow role switch
HCI_Command(hcibuf, 16);
}
void BTD::hci_pin_code_request_reply(const char* key) {
hcibuf[0] = 0x0D; // HCI OCF = 0D
hcibuf[1] = 0x01 << 2; // HCI OGF = 1
@ -814,6 +965,18 @@ void BTD::L2CAP_Command(uint16_t handle, uint8_t* data, uint8_t nbytes, uint8_t
#endif
}
}
void BTD::l2cap_connection_request(uint16_t handle, uint8_t rxid, uint8_t* scid, uint16_t psm) {
l2capoutbuf[0] = L2CAP_CMD_CONNECTION_REQUEST; // Code
l2capoutbuf[1] = rxid; // Identifier
l2capoutbuf[2] = 0x04; // Length
l2capoutbuf[3] = 0x00;
l2capoutbuf[4] = (uint8_t)(psm & 0xff); // PSM
l2capoutbuf[5] = (uint8_t)(psm >> 8);
l2capoutbuf[6] = scid[0]; // Source CID
l2capoutbuf[7] = scid[1];
L2CAP_Command(handle, l2capoutbuf, 8);
}
void BTD::l2cap_connection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid, uint8_t result) {
l2capoutbuf[0] = L2CAP_CMD_CONNECTION_RESPONSE; // Code
l2capoutbuf[1] = rxid; // Identifier
@ -856,7 +1019,7 @@ void BTD::l2cap_config_response(uint16_t handle, uint8_t rxid, uint8_t* scid) {
l2capoutbuf[6] = 0x00; // Flag
l2capoutbuf[7] = 0x00;
l2capoutbuf[8] = 0x00; // Result
l2capoutbuf[9] = 0x00;
l2capoutbuf[9] = 0x00;
l2capoutbuf[10] = 0x01; // Config
l2capoutbuf[11] = 0x02;
l2capoutbuf[12] = 0xA0;
@ -915,10 +1078,9 @@ void BTD::setBdaddr(uint8_t* BDADDR) {
pUsb->ctrlReq(bAddress,epInfo[BTD_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0xF5, 0x03, 0x00, 8, 8, buf, NULL);
#ifdef DEBUG
Notify(PSTR("\r\nBluetooth Address was set to: "));
for(int8_t i = 5; i > 0; i--)
{
for(int8_t i = 5; i > 0; i--) {
PrintHex<uint8_t>(my_bdaddr[i]);
Serial.print(":");
Notify(PSTR(":"));
}
PrintHex<uint8_t>(my_bdaddr[0]);
#endif
@ -939,10 +1101,9 @@ void BTD::setMoveBdaddr(uint8_t* BDADDR) {
pUsb->ctrlReq(bAddress,epInfo[BTD_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x05, 0x03, 0x00,11,11, buf, NULL);
#ifdef DEBUG
Notify(PSTR("\r\nBluetooth Address was set to: "));
for(int8_t i = 5; i > 0; i--)
{
for(int8_t i = 5; i > 0; i--) {
PrintHex<uint8_t>(my_bdaddr[i]);
Serial.print(":");
Notify(PSTR(":"));
}
PrintHex<uint8_t>(my_bdaddr[0]);
#endif

41
BTD.h
View file

@ -42,13 +42,19 @@
#define HCI_BDADDR_STATE 2
#define HCI_LOCAL_VERSION_STATE 3
#define HCI_SET_NAME_STATE 4
#define HCI_SCANNING_STATE 5
#define HCI_CONNECT_IN_STATE 6
#define HCI_REMOTE_NAME_STATE 7
#define HCI_CONNECTED_STATE 8
#define HCI_DISABLE_SCAN_STATE 9
#define HCI_DONE_STATE 10
#define HCI_DISCONNECT_STATE 11
#define HCI_CHECK_WII_SERVICE 5
#define HCI_INQUIRY_STATE 6 // These three states are only used if it should connect to a Wii controller
#define HCI_CONNECT_WII_STATE 7
#define HCI_CONNECTED_WII_STATE 8
#define HCI_SCANNING_STATE 9
#define HCI_CONNECT_IN_STATE 10
#define HCI_REMOTE_NAME_STATE 11
#define HCI_CONNECTED_STATE 12
#define HCI_DISABLE_SCAN_STATE 13
#define HCI_DONE_STATE 14
#define HCI_DISCONNECT_STATE 15
/* HCI event flags*/
#define HCI_FLAG_CMD_COMPLETE 0x01
@ -58,6 +64,8 @@
#define HCI_FLAG_INCOMING_REQUEST 0x10
#define HCI_FLAG_READ_BDADDR 0x20
#define HCI_FLAG_READ_VERSION 0x40
#define HCI_FLAG_WII_FOUND 0x80
#define HCI_FLAG_CONNECT_EVENT 0x100
/*Macros for HCI event flag tests */
#define hci_cmd_complete (hci_event_flag & HCI_FLAG_CMD_COMPLETE)
@ -67,8 +75,12 @@
#define hci_incoming_connect_request (hci_event_flag & HCI_FLAG_INCOMING_REQUEST)
#define hci_read_bdaddr_complete (hci_event_flag & HCI_FLAG_READ_BDADDR)
#define hci_read_version_complete (hci_event_flag & HCI_FLAG_READ_VERSION)
#define hci_wii_found (hci_event_flag & HCI_FLAG_WII_FOUND)
#define hci_connect_event (hci_event_flag & HCI_FLAG_CONNECT_EVENT)
/* HCI Events managed */
#define EV_INQUIRY_COMPLETE 0x01
#define EV_INQUIRY_RESULT 0x02
#define EV_CONNECT_COMPLETE 0x03
#define EV_INCOMING_CONNECT 0x04
#define EV_DISCONNECT_COMPLETE 0x05
@ -105,6 +117,12 @@
#define PENDING 0x01
#define SUCCESSFUL 0x00
/* Bluetooth L2CAP PSM - see http://www.bluetooth.org/Technical/AssignedNumbers/logical_link.htm */
#define SDP_PSM 0x01 // Service Discovery Protocol PSM Value
#define RFCOMM_PSM 0x03 // RFCOMM PSM Value
#define HID_CTRL_PSM 0x11 // HID_Control PSM Value
#define HID_INTR_PSM 0x13 // HID_Interrupt PSM Value
// Used to determine if it is a Bluetooth dongle
#define WI_SUBCLASS_RF 0x01 // RF Controller
#define WI_PROTOCOL_BT 0x01 // Bluetooth Programming Interface
@ -163,6 +181,9 @@ public:
uint8_t remote_name[30]; // First 30 chars of last remote name
uint8_t hci_version;
int8_t wiiServiceID; // Stores the service ID of the Wii service
bool connectToWii; // Used to only send the ACL data to the wiimote
/* HCI Commands */
void HCI_Command(uint8_t* data, uint16_t nbytes);
void hci_reset();
@ -177,9 +198,13 @@ public:
void hci_pin_code_request_reply(const char* key);
void hci_pin_code_negative_request_reply();
void hci_link_key_request_negative_reply();
void hci_inquiry();
void hci_inquiry_cancel();
void hci_connect();
/* L2CAP Commands */
void L2CAP_Command(uint16_t handle, uint8_t* data, uint8_t nbytes, uint8_t channelLow = 0x01, uint8_t channelHigh = 0x00); // Standard L2CAP header: Channel ID (0x01) for ACL-U
void l2cap_connection_request(uint16_t handle, uint8_t rxid, uint8_t* scid, uint16_t psm);
void l2cap_connection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid, uint8_t result);
void l2cap_config_request(uint16_t handle, uint8_t rxid, uint8_t* dcid);
void l2cap_config_response(uint16_t handle, uint8_t rxid, uint8_t* scid);
@ -214,7 +239,7 @@ private:
uint8_t hci_state; //current state of bluetooth hci connection
uint16_t hci_counter; // counter used for bluetooth hci reset loops
uint8_t hci_num_reset_loops; // this value indicate how many times it should read before trying to reset
uint16_t hci_event_flag; // hci flags of received bluetooth events
uint16_t hci_event_flag; // hci flags of received bluetooth events
uint8_t hcibuf[BULK_MAXPKTSIZE];//General purpose buffer for hci data
uint8_t l2capinbuf[BULK_MAXPKTSIZE];//General purpose buffer for l2cap in data

View file

@ -1,4 +1,4 @@
The BTD.cpp, BTD.h, SPP.cpp, SPP.h, PS3BT.cpp, PS3BT.h, PS3USB.cpp, PS3USB.h, XBOXUSB.cpp, and XBOXUSB.h is developed by Kristian Lauszus
The BTD.cpp, BTD.h, SPP.cpp, SPP.h, PS3BT.cpp, PS3BT.h, Wii.cpp, Wii.h PS3USB.cpp, PS3USB.h, XBOXUSB.cpp, and XBOXUSB.h is developed by Kristian Lauszus
For more information regarding the PS3 protocol etc. visit my blog at: http://blog.tkjelectronics.dk/ or send me an email at kristianl at tkjelectronics dot dk.
You could also visit the official wiki: https://github.com/TKJElectronics/USB_Host_Shield_2.0/wiki for information.
@ -26,4 +26,7 @@ http://pingus.seul.org/~grumbel/xboxdrv/
To implement the RFCOMM protocol I used a bluetooth sniffing tool called PacketLogger developed by Apple.
It enables me to see the bluetooth communication between my Mac and any device.
All the information about the Wii controller is from this site: http://wiibrew.org/wiki/Wiimote
And the old Wii library created by Moyuchin: https://github.com/moyuchin/WiiRemote_on_Arduino
And at last I would like to thank Oleg from http://www.circuitsathome.com/ for making such an awesome shield!

View file

@ -56,10 +56,6 @@
#define l2cap_disconnect_response_control_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE)
#define l2cap_disconnect_response_interrupt_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE)
/* Bluetooth L2CAP PSM */
#define HID_CTRL_PSM 0x11 // HID_Control
#define HID_INTR_PSM 0x13 // HID_Interrupt
enum LED {
LED1 = 0x01,
LED2 = 0x02,

4
SPP.h
View file

@ -55,10 +55,6 @@
#define l2cap_disconnect_request_rfcomm_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST)
#define l2cap_disconnect_response_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_RESPONSE)
/* Bluetooth L2CAP PSM */
#define SDP_PSM 0x01 // Service Discovery Protocol PSM Value
#define RFCOMM_PSM 0x03 // RFCOMM PSM Value
/* 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

359
Wii.cpp Normal file
View file

@ -0,0 +1,359 @@
/* Copyright (C) 2012 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 "Wii.h"
#define DEBUG // Uncomment to print data for debugging
//#define EXTRADEBUG // Uncomment to get even more debugging data
//#define PRINTREPORT // Uncomment to print the report send by the Wiimote
WII::WII(BTD *p, uint8_t btadr5, uint8_t btadr4, uint8_t btadr3, uint8_t btadr2, uint8_t btadr1, uint8_t btadr0):
pBtd(p) // pointer to USB class instance - mandatory
{
if (pBtd)
pBtd->wiiServiceID = pBtd->registerServiceClass(this); // Register it as a Bluetooth service
pBtd->disc_bdaddr[5] = btadr5; // Change to your dongle's Bluetooth address instead
pBtd->disc_bdaddr[4] = btadr4;
pBtd->disc_bdaddr[3] = btadr3;
pBtd->disc_bdaddr[2] = btadr2;
pBtd->disc_bdaddr[1] = btadr1;
pBtd->disc_bdaddr[0] = btadr0;
HIDBuffer[0] = 0xA2;// HID BT DATA_request (0x50) | Report Type (Output 0x02)
/* Set device cid for the control and intterrupt channelse - LSB */
control_dcid[0] = 0x60;//0x0060
control_dcid[1] = 0x00;
interrupt_dcid[0] = 0x61;//0x0061
interrupt_dcid[1] = 0x00;
Reset();
}
void WII::Reset() {
connected = false;
l2cap_event_flag = 0; // Reset flags
l2cap_state = L2CAP_WAIT;
}
void WII::disconnect() { // Use this void to disconnect any of the controllers
//First the HID interrupt channel has to be disconencted, then the HID control channel and finally the HCI connection
pBtd->l2cap_disconnection_request(hci_handle,0x0A, interrupt_scid, interrupt_dcid);
Reset();
l2cap_state = L2CAP_INTERRUPT_DISCONNECT;
}
void WII::ACLData(uint8_t* l2capinbuf) {
if (((l2capinbuf[0] | (l2capinbuf[1] << 8)) == (hci_handle | 0x2000))) { //acl_handle_ok
if ((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001) { //l2cap_control - Channel ID for ACL-U
if (l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) {
#ifdef DEBUG
Notify(PSTR("\r\nL2CAP Command Rejected - Reason: "));
PrintHex<uint8_t>(l2capinbuf[13]);
Notify(PSTR(" "));
PrintHex<uint8_t>(l2capinbuf[12]);
Notify(PSTR(" "));
PrintHex<uint8_t>(l2capinbuf[17]);
Notify(PSTR(" "));
PrintHex<uint8_t>(l2capinbuf[16]);
Notify(PSTR(" "));
PrintHex<uint8_t>(l2capinbuf[15]);
Notify(PSTR(" "));
PrintHex<uint8_t>(l2capinbuf[14]);
#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]) { // Success
//Serial.print("\r\nHID Control Connection Complete");
identifier = l2capinbuf[9];
control_scid[0] = l2capinbuf[12];
control_scid[1] = l2capinbuf[13];
l2cap_event_flag |= L2CAP_FLAG_CONTROL_CONNECTED;
}
else if (l2capinbuf[14] == interrupt_dcid[0] && l2capinbuf[15] == interrupt_dcid[1]) {
//Serial.print("\r\nHID Interrupt Connection Complete");
identifier = l2capinbuf[9];
interrupt_scid[0] = l2capinbuf[12];
interrupt_scid[1] = l2capinbuf[13];
l2cap_event_flag |= L2CAP_FLAG_INTERRUPT_CONNECTED;
}
}
}
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]) {
//Serial.print("\r\nHID Control Configuration Complete");
identifier = l2capinbuf[9];
l2cap_event_flag |= L2CAP_FLAG_CONFIG_CONTROL_SUCCESS;
}
else if (l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
//Serial.print("\r\nHID Interrupt Configuration Complete");
identifier = l2capinbuf[9];
l2cap_event_flag |= L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS;
}
}
}
else if (l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) {
if (l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
//Serial.print("\r\nHID Control Configuration Request");
pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], control_scid);
}
else if (l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
//Serial.print("\r\nHID Interrupt Configuration Request");
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]) {
#ifdef DEBUG
Notify(PSTR("\r\nDisconnect Request: Control Channel"));
#endif
connected = false;
identifier = l2capinbuf[9];
pBtd->l2cap_disconnection_response(hci_handle,identifier,control_dcid,control_scid);
Reset();
}
else if (l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
#ifdef DEBUG
Notify(PSTR("\r\nDisconnect Request: Interrupt Channel"));
#endif
connected = false;
identifier = l2capinbuf[9];
pBtd->l2cap_disconnection_response(hci_handle,identifier,interrupt_dcid,interrupt_scid);
Reset();
}
}
else if (l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) {
if (l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) {
//Serial.print("\r\nDisconnect Response: Control Channel");
identifier = l2capinbuf[9];
l2cap_event_flag |= L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE;
}
else if (l2capinbuf[12] == interrupt_scid[0] && l2capinbuf[13] == interrupt_scid[1]) {
//Serial.print("\r\nDisconnect Response: Interrupt Channel");
identifier = l2capinbuf[9];
l2cap_event_flag |= L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE;
}
}
#ifdef EXTRADEBUG
else {
identifier = l2capinbuf[9];
Notify(PSTR("\r\nL2CAP Unknown Signaling Command: "));
PrintHex<uint8_t>(l2capinbuf[8]);
}
#endif
} else if (l2capinbuf[6] == interrupt_dcid[0] && l2capinbuf[7] == interrupt_dcid[1]) { // l2cap_interrupt
//Serial.print("\r\nL2CAP Interrupt");
if(connected) {
/* Read Report */
if(l2capinbuf[8] == 0xA1) { // HID_THDR_DATA_INPUT
switch (l2capinbuf[9]) {
case 0x30: // Core buttons
ButtonState = (uint16_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8));
ButtonClickState = ButtonState; // Update click state variable
#ifdef PRINTREPORT
Notify(PSTR("ButtonState: "));
PrintHex<uint16_t>(ButtonState);
Notify(PSTR("\r\n"));
#endif
if(ButtonState != OldButtonState) {
buttonChanged = true;
if(ButtonState != 0x0000) {
buttonPressed = true;
buttonReleased = false;
} else {
buttonPressed = false;
buttonReleased = true;
}
}
else {
buttonChanged = false;
buttonPressed = false;
buttonReleased = false;
}
OldButtonState = ButtonState;
break;
#ifdef DEBUG
default:
Notify(PSTR("\r\nUnknown Report type: "));
Serial.print(l2capinbuf[9],HEX);
break;
#endif
}
}
}
}
L2CAP_task();
}
}
void WII::L2CAP_task() {
switch (l2cap_state) {
case L2CAP_CONTROL_CONNECT_REQUEST:
if (l2cap_connected_control_flag) {
#ifdef DEBUG
Notify(PSTR("\r\nSend HID Control Config Request"));
#endif
identifier++;
pBtd->l2cap_config_request(hci_handle, identifier, control_scid);
l2cap_state = L2CAP_CONTROL_CONFIG_REQUEST;
}
break;
case L2CAP_CONTROL_CONFIG_REQUEST:
if(l2cap_config_success_control_flag) {
#ifdef DEBUG
Notify(PSTR("\r\nSend HID Interrupt Connection Request"));
#endif
identifier++;
pBtd->l2cap_connection_request(hci_handle,identifier,interrupt_dcid,HID_INTR_PSM);
l2cap_state = L2CAP_INTERRUPT_CONNECT_REQUEST;
}
break;
case L2CAP_INTERRUPT_CONNECT_REQUEST:
if(l2cap_connected_interrupt_flag) {
#ifdef DEBUG
Notify(PSTR("\r\nSend HID Interrupt Config Request"));
#endif
identifier++;
pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid);
l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST;
}
break;
case L2CAP_INTERRUPT_CONFIG_REQUEST:
if(l2cap_config_success_interrupt_flag) {
#ifdef DEBUG
Notify(PSTR("\r\nHID Channels Established"));
#endif
connected = true;
pBtd->connectToWii = false;
ButtonState = 0;
OldButtonState = 0;
ButtonClickState = 0;
setLedOn(LED1);
l2cap_state = L2CAP_DONE;
}
break;
/*
case L2CAP_WIIREMOTE_CAL_STATE:
//Todo enable support for Motion Plus
break;
*/
case L2CAP_DONE:
break;
case L2CAP_INTERRUPT_DISCONNECT:
if (l2cap_disconnect_response_interrupt_flag) {
#ifdef DEBUG
Notify(PSTR("\r\nDisconnected Interrupt Channel"));
#endif
identifier++;
pBtd->l2cap_disconnection_request(hci_handle, identifier, control_scid, control_dcid);
l2cap_state = L2CAP_CONTROL_DISCONNECT;
}
break;
case L2CAP_CONTROL_DISCONNECT:
if (l2cap_disconnect_response_control_flag) {
#ifdef DEBUG
Notify(PSTR("\r\nDisconnected Control Channel"));
#endif
pBtd->hci_disconnect(hci_handle);
l2cap_event_flag = 0; // Reset flags
l2cap_state = L2CAP_WAIT;
}
break;
}
}
void WII::Run() {
switch (l2cap_state) {
case L2CAP_WAIT:
if(pBtd->connectToWii) {
#ifdef DEBUG
Notify(PSTR("\r\nSend HID Control Connection Request"));
#endif
hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection
l2cap_event_flag = 0; // Reset flags
identifier = 0;
pBtd->l2cap_connection_request(hci_handle,identifier,control_dcid,HID_CTRL_PSM);
l2cap_state = L2CAP_CONTROL_CONNECT_REQUEST;
}
break;
}
}
/************************************************************/
/* HID Commands */
/************************************************************/
void WII::HID_Command(uint8_t* data, uint8_t nbytes) {
pBtd->L2CAP_Command(hci_handle,data,nbytes,control_scid[0],control_scid[1]); // Both the Navigation and Dualshock controller sends data via the control channel
}
void WII::setAllOff() {
HIDBuffer[1] = 0x11;
HIDBuffer[2] = 0x00;
HID_Command(HIDBuffer, 3);
}
void WII::setRumbleOff() {
HIDBuffer[1] = 0x11;
HIDBuffer[2] &= ~0x01; // Bit 0 control the rumble
HID_Command(HIDBuffer, 3);
}
void WII::setRumbleOn() {
HIDBuffer[1] = 0x11;
HIDBuffer[2] |= 0x01; // Bit 0 control the rumble
HID_Command(HIDBuffer, 3);
}
void WII::setRumbleToggle() {
HIDBuffer[1] = 0x11;
HIDBuffer[2] ^= 0x01; // Bit 0 control the rumble
HID_Command(HIDBuffer, 3);
}
void WII::setLedOff(LED a) {
HIDBuffer[1] = 0x11;
HIDBuffer[2] &= ~((uint8_t)a);
HID_Command(HIDBuffer, 3);
}
void WII::setLedOn(LED a) {
HIDBuffer[1] = 0x11;
HIDBuffer[2] |= (uint8_t)a;
HID_Command(HIDBuffer, 3);
}
void WII::setLedToggle(LED a) {
HIDBuffer[1] = 0x11;
HIDBuffer[2] ^= (uint8_t)a;
HID_Command(HIDBuffer, 3);
}
/************************************************************/
/* WII Commands */
/************************************************************/
bool WII::getButtonPress(Button b) {
if(ButtonState & (uint16_t)b)
return true;
else
return false;
}
bool WII::getButtonClick(Button b) {
bool click = ((ButtonClickState & (uint16_t)b) != 0);
ButtonClickState &= ~((uint16_t)b); // clear "click" event
return click;
}

138
Wii.h Normal file
View file

@ -0,0 +1,138 @@
/* Copyright (C) 2012 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
*/
#ifndef _wii_h_
#define _wii_h_
#include "BTD.h"
/* Bluetooth L2CAP states for L2CAP_task() */
#define L2CAP_WAIT 0
#define L2CAP_CONTROL_CONNECT_REQUEST 1
#define L2CAP_CONTROL_CONFIG_REQUEST 2
#define L2CAP_INTERRUPT_CONNECT_REQUEST 3
#define L2CAP_INTERRUPT_CONFIG_REQUEST 4
//#define L2CAP_WIIREMOTE_CAL_STATE 9 /* TODO: Enable support for Motion Plus */
#define L2CAP_DONE 5
#define L2CAP_INTERRUPT_DISCONNECT 6
#define L2CAP_CONTROL_DISCONNECT 7
/* L2CAP event flags */
#define L2CAP_FLAG_CONTROL_CONNECTED 0x01
#define L2CAP_FLAG_INTERRUPT_CONNECTED 0x02
#define L2CAP_FLAG_CONFIG_CONTROL_SUCCESS 0x04
#define L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS 0x08
#define L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE 0x40
#define L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE 0x80
/*Macros for L2CAP event flag tests */
#define l2cap_connected_control_flag (l2cap_event_flag & L2CAP_FLAG_CONTROL_CONNECTED)
#define l2cap_connected_interrupt_flag (l2cap_event_flag & L2CAP_FLAG_INTERRUPT_CONNECTED)
#define l2cap_config_success_control_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)
#define l2cap_config_success_interrupt_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS)
#define l2cap_disconnect_response_control_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE)
#define l2cap_disconnect_response_interrupt_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE)
enum LED {
LED1 = 0x10,
LED2 = 0x20,
LED3 = 0x40,
LED4 = 0x80,
LED5 = 0x90,
LED6 = 0xA0,
LED7 = 0xC0,
LED8 = 0xD0,
LED9 = 0xE0,
LED10 = 0xF0,
};
enum Button {
LEFT = 0x0001,
RIGHT = 0x0002,
DOWN = 0x0004,
UP = 0x0008,
PLUS = 0x0010,
TWO = 0x0100,
ONE = 0x0200,
B = 0x0400,
A = 0x0800,
MINUS = 0x1000,
HOME = 0x8000,
};
class WII : public BluetoothService {
public:
WII(BTD *pBtd, uint8_t btadr5=0, uint8_t btadr4=0, uint8_t btadr3=0, uint8_t btadr2=0, uint8_t btadr1=0, uint8_t btadr0=0);
// BluetoothService implementation
virtual void ACLData(uint8_t* ACLData); // Used to pass acldata to the services
virtual void Run(); // Used to run part of the state maschine
virtual void Reset(); // Use this to reset the service
virtual void disconnect(); // Use this void to disconnect any of the controllers
bool getButtonPress(Button b); // This will read true as long as the button is held down
bool getButtonClick(Button b); // This will only be true when the button is clicked the first time
/*
TODO: Enable support for Motion Plus
int16_t getSensor(Sensor a);
double getAngle(Angle a);
*/
void setAllOff(); // Turn both rumble and all LEDs off
void setRumbleOff();
void setRumbleOn();
void setRumbleToggle();
void setLedOff(LED a);
void setLedOn(LED a);
void setLedToggle(LED a);
bool connected;// Variable used to indicate if a Wiimote is connected
bool buttonChanged;//Indicate if a button has been changed
bool buttonPressed;//Indicate if a button has been pressed
bool buttonReleased;//Indicate if a button has been released
private:
/* Mandatory members */
BTD *pBtd;
void L2CAP_task(); // L2CAP state machine
/* Variables filled from HCI event management */
uint16_t hci_handle;
/* variables used by high level L2CAP task */
uint8_t l2cap_state;
uint16_t l2cap_event_flag;// l2cap flags of received bluetooth events
uint16_t ButtonState;
uint16_t OldButtonState;
uint16_t ButtonClickState;
uint8_t HIDBuffer[3];// Used to store HID commands
/* L2CAP Channels */
uint8_t control_scid[2];// L2CAP source CID for HID_Control
uint8_t control_dcid[2];//0x0060
uint8_t interrupt_scid[2];// L2CAP source CID for HID_Interrupt
uint8_t interrupt_dcid[2];//0x0061
uint8_t identifier;//Identifier for connection
/* HID Commands */
void HID_Command(uint8_t* data, uint8_t nbytes);
};
#endif

View file

@ -0,0 +1,77 @@
/*
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); // This will start inquiry which will connect to any Wiimote
//WII Wii(&Btd,0x00,0x26,0x59,0x48,0xFF,0xFB); // This will connect to the Wiimote with that specific Bluetooth Address
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.connected) {
if(Wii.buttonPressed) {
if(Wii.getButtonClick(HOME)) { // You can use getButtonPress to see if the button is held down
Serial.print(F("\r\nHOME"));
Wii.disconnect(); // If you disconnect you have to reset the Arduino to establish the connection again
}
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(ONE)) {
Serial.print(F("\r\nOne"));
}
if(Wii.getButtonClick(TWO)) {
Serial.print(F("\r\nTwo"));
}
if(Wii.getButtonClick(A)) {
Serial.print(F("\r\nA"));
}
if(Wii.getButtonClick(B)) {
Wii.setRumbleToggle();
Serial.print(F("\r\nB"));
}
}
}
}
}

View file

@ -201,4 +201,22 @@ SPP KEYWORD1
connected KEYWORD2
printNumber KEYWORD2
printNumberln KEYWORD2
printNumberln KEYWORD2
####################################################
# Syntax Coloring Map For Wiimote Library
####################################################
####################################################
# Datatypes (KEYWORD1)
####################################################
WII KEYWORD1
####################################################
# Methods and Functions (KEYWORD2)
####################################################
getButtonPress KEYWORD2
getButtonClick KEYWORD2
setRumbleToggle KEYWORD2