diff --git a/PS3BT.cpp b/PS3BT.cpp new file mode 100644 index 00000000..8fe0d639 --- /dev/null +++ b/PS3BT.cpp @@ -0,0 +1,1560 @@ +/* Copyright (C) 2011 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 + ------------------- + + TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : mail@tkjelectronics.com + */ + +#include "PS3BT.h" +#define DEBUG // Uncomment to print data for debugging +//#define PRINTREPORT // Uncomment to print the report send by the PS3 Controllers + +prog_char OUTPUT_REPORT_BUFFER[] PROGMEM = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +PS3BT::PS3BT(USB *p): + pUsb(p), //pointer to USB class instance - mandatory + bAddress(0), //device address - mandatory + bPollEnable(false) //don't start polling before dongle is connected +{ + for(uint8_t i=0; iRegisterDeviceClass(this); //set devConfig[] entry + + my_bdaddr[5] = 0x00;//Change to your dongle's Bluetooth address instead + my_bdaddr[4] = 0x1F; + my_bdaddr[3] = 0x81; + my_bdaddr[2] = 0x00; + my_bdaddr[1] = 0x08; + my_bdaddr[0] = 0x30; +} + +uint8_t PS3BT::Init(uint8_t parent, uint8_t port, bool lowspeed) +{ + const uint8_t constBufSize = sizeof(USB_DEVICE_DESCRIPTOR); + uint8_t buf[constBufSize]; + 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(); + + //Notify(PSTR("\r\nPS3BT Init"); + + // check if address has already been assigned to an instance + if (bAddress) + { + #ifdef DEBUG + Notify(PSTR("\r\nAddress in use")); + #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 + Notify(PSTR("\r\nAddress not found")); + #endif + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + } + + if (!p->epinfo) + { + #ifdef DEBUG + Notify(PSTR("\r\nepinfo is null")); + #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, constBufSize, (uint8_t*)buf);// Get device descriptor - addr, ep, nbytes, data + + // Restore p->epinfo + p->epinfo = oldep_ptr; + + if( rcode ){ + goto FailGetDevDescr; + } + + // 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 = (uint8_t)((USB_DEVICE_DESCRIPTOR*)buf)->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 + Notify(PSTR("\r\nsetAddr: ")); + #endif + PrintHex(rcode); + return rcode; + } + //Notify(PSTR("\r\nAddr: ")); + //PrintHex(bAddress); + + 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; + } + + VID = ((USB_DEVICE_DESCRIPTOR*)buf)->idVendor; + PID = ((USB_DEVICE_DESCRIPTOR*)buf)->idProduct; + + if(VID == CSR_VID && PID == CSR_PID) + { + #ifdef DEBUG + Notify(PSTR("\r\nBluetooth Dongle Connected")); + #endif + //Needed for PS3 Dualshock Controller commands to work via bluetooth + for (uint8_t i = 0; i < OUTPUT_REPORT_BUFFER_SIZE; i++) + HIDBuffer[i + 2] = pgm_read_byte(&OUTPUT_REPORT_BUFFER[i]);//First two bytes reserved for report type and ID + + HIDBuffer[0] = 0x52;// HID BT Set_report (0x50) | Report Type (Output 0x02) + HIDBuffer[1] = 0x01;// Report ID + + //Needed for PS3 Move Controller commands to work via bluetooth + HIDMoveBuffer[0] = 0xA2;// HID BT DATA_request (0xA0) | Report Type (Output 0x02) + HIDMoveBuffer[1] = 0x02;// Report ID + + /* Set device cid for the control and intterrupt channelse - LSB */ + control_dcid[0] = 0x40;//0x0040 + control_dcid[1] = 0x00; + interrupt_dcid[0] = 0x41;//0x0041 + interrupt_dcid[1] = 0x00; + + /* Initialize data structures for endpoints of device */ + epInfo[ CSR_EVENT_PIPE ].epAddr = 0x01; // Bluetooth event endpoint + epInfo[ CSR_EVENT_PIPE ].epAttribs = EP_INTERRUPT; + epInfo[ CSR_EVENT_PIPE ].bmNakPower = USB_NAK_NOWAIT;//Only poll once for interrupt endpoints + epInfo[ CSR_EVENT_PIPE ].maxPktSize = INT_MAXPKTSIZE; + epInfo[ CSR_EVENT_PIPE ].bmSndToggle = bmSNDTOG0; + epInfo[ CSR_EVENT_PIPE ].bmRcvToggle = bmRCVTOG0; + epInfo[ CSR_DATAIN_PIPE ].epAddr = 0x02; // Bluetoth data endpoint + epInfo[ CSR_DATAIN_PIPE ].epAttribs = EP_BULK; + epInfo[ CSR_DATAIN_PIPE ].maxPktSize = BULK_MAXPKTSIZE; + epInfo[ CSR_DATAIN_PIPE ].bmSndToggle = bmSNDTOG0; + epInfo[ CSR_DATAIN_PIPE ].bmRcvToggle = bmRCVTOG0; + epInfo[ CSR_DATAOUT_PIPE ].epAddr = 0x02; // Bluetooth data endpoint + epInfo[ CSR_DATAOUT_PIPE ].epAttribs = EP_BULK; + epInfo[ CSR_DATAOUT_PIPE ].maxPktSize = BULK_MAXPKTSIZE; + epInfo[ CSR_DATAOUT_PIPE ].bmSndToggle = bmSNDTOG0; + epInfo[ CSR_DATAOUT_PIPE ].bmRcvToggle = bmRCVTOG0; + + rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo); + if( rcode ) + goto FailSetDevTblEntry; + + delay(200);//Give time for address change + + rcode = pUsb->setConf(bAddress, epInfo[ CSR_CONTROL_PIPE ].epAddr, bConfigurationValue);//bConfigurationValue = 0x01 + if( rcode ) + goto FailSetConf; + + hci_state = HCI_INIT_STATE; + hci_counter = 0; + l2cap_state = L2CAP_EV_WAIT; + #ifdef DEBUG + Notify(PSTR("\r\nCSR Initialized")); + #endif + delay(200); + + bPollEnable = true; + } + else if((VID == PS3_VID || VID == PS3NAVIGATION_VID || VID == PS3MOVE_VID) && (PID == PS3_PID || PID == PS3NAVIGATION_PID || PID == PS3MOVE_PID)) + { + /* Initialize data structures for endpoints of device */ + epInfo[ PS3_OUTPUT_PIPE ].epAddr = 0x02; // PS3 output endpoint + epInfo[ PS3_OUTPUT_PIPE ].epAttribs = EP_INTERRUPT; + epInfo[ PS3_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT;//Only poll once for interrupt endpoints + epInfo[ PS3_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE; + epInfo[ PS3_OUTPUT_PIPE ].bmSndToggle = bmSNDTOG0; + epInfo[ PS3_OUTPUT_PIPE ].bmRcvToggle = bmRCVTOG0; + epInfo[ PS3_INPUT_PIPE ].epAddr = 0x01; // PS3 report endpoint + epInfo[ PS3_INPUT_PIPE ].epAttribs = EP_INTERRUPT; + epInfo[ PS3_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT;//Only poll once for interrupt endpoints + epInfo[ PS3_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE; + epInfo[ PS3_INPUT_PIPE ].bmSndToggle = bmSNDTOG0; + epInfo[ PS3_INPUT_PIPE ].bmRcvToggle = bmRCVTOG0; + + + rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo); + if( rcode ) + goto FailSetDevTblEntry; + + delay(200);//Give time for address change + + + rcode = pUsb->setConf(bAddress, epInfo[ PS3_CONTROL_PIPE ].epAddr, bConfigurationValue);//bConfigurationValue = 0x01 + if( rcode ) + goto FailSetConf; + + if((VID == PS3_VID || VID == PS3NAVIGATION_VID) && (PID == PS3_PID || PID == PS3NAVIGATION_PID)) + { + if(VID == PS3_VID && PID == PS3_PID) { + #ifdef DEBUG + Notify(PSTR("\r\nDualshock 3 Controller Connected")); + #endif + } else { // must be a navigation controller + #ifdef DEBUG + Notify(PSTR("\r\nNavigation Controller Connected")); + #endif + } + /* Set internal bluetooth address */ + setBdaddr(my_bdaddr); + } + else // must be a Motion controller + { + #ifdef DEBUG + Notify(PSTR("\r\nMotion Controller Connected")); + #endif + setMoveBdaddr(my_bdaddr); + } + } + else + goto FailUnknownDevice; + + return 0; //successful configuration + + /* diagnostic messages */ + FailGetDevDescr: + #ifdef DEBUG + Notify(PSTR("\r\ngetDevDescr:")); + #endif + goto Fail; + FailSetDevTblEntry: + #ifdef DEBUG + Notify(PSTR("\r\nsetDevTblEn:")); + #endif + goto Fail; + FailSetConf: + #ifdef DEBUG + Notify(PSTR("\r\nsetConf:")); + #endif + goto Fail; + FailUnknownDevice: + #ifdef DEBUG + Notify(PSTR("\r\nUnknown Device Connected - VID: ")); + PrintHex(VID); + Notify(PSTR(" PID: ")); + PrintHex(PID); + #endif + goto Fail; + Fail: + #ifdef DEBUG + Notify(PSTR("\r\nPS3 Init Failed, error code: ")); + Serial.print(rcode); + #endif + Release(); + return rcode; +} + +/* Performs a cleanup after failed Init() attempt */ +uint8_t PS3BT::Release() +{ + pUsb->GetAddressPool().FreeAddress(bAddress); + bAddress = 0; + bPollEnable = false; + return 0; +} +uint8_t PS3BT::Poll() +{ + if (!bPollEnable) + return 0; + + HCI_event_task(); // poll the HCI event pipe + ACL_event_task(); // start polling the ACL input pipe too, though discard data until connected + + return 0; +} +void PS3BT::setBdaddr(uint8_t* BDADDR) +{ + /* Store the bluetooth address */ + for(uint8_t i = 0; i <6;i++) + my_bdaddr[i] = BDADDR[i]; + + /* Set the internal bluetooth address */ + uint8_t buf[8]; + buf[0] = 0x01; + buf[1] = 0x00; + for (uint8_t i = 0; i < 6; i++) + buf[i+2] = my_bdaddr[5 - i];//Copy into buffer, has to be written reversed + + //bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0xF5), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data) + pUsb->ctrlReq(bAddress,epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HIDOUT, 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--) + { + PrintHex(my_bdaddr[i]); + Serial.print(":"); + } + PrintHex(my_bdaddr[0]); + #endif + return; +} +void PS3BT::setMoveBdaddr(uint8_t* BDADDR) +{ + /* Store the bluetooth address */ + for(uint8_t i = 0; i <6;i++) + my_bdaddr[i] = BDADDR[i]; + + /* Set the internal bluetooth address */ + uint8_t buf[11]; + buf[0] = 0x05; + buf[7] = 0x10; + buf[8] = 0x01; + buf[9] = 0x02; + buf[10] = 0x12; + + for (uint8_t i = 0; i < 6; i++) + buf[i + 1] = my_bdaddr[i]; + + //bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x05), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data) + pUsb->ctrlReq(bAddress,epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HIDOUT, 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--) + { + PrintHex(my_bdaddr[i]); + Serial.print(":"); + } + PrintHex(my_bdaddr[0]); + #endif + return; +} +bool PS3BT::getButton(Button b) +{ + if (l2capinbuf == NULL) + return false; + if ((l2capinbuf[(uint16_t)b >> 8] & ((uint8_t)b & 0xff)) > 0) + return true; + else + return false; +} +uint8_t PS3BT::getAnalogButton(AnalogButton a) +{ + if (l2capinbuf == NULL) + return 0; + return (uint8_t)(l2capinbuf[(uint16_t)a]); +} +uint8_t PS3BT::getAnalogHat(AnalogHat a) +{ + if (l2capinbuf == NULL) + return 0; + return (uint8_t)(l2capinbuf[(uint16_t)a]); +} +uint32_t PS3BT::getSensor(Sensor a) +{ + if (a == aX || a == aY || a == aZ || a == gZ) + { + if (l2capinbuf == NULL) + return 0; + return ((l2capinbuf[(uint16_t)a] << 8) | l2capinbuf[(uint16_t)a + 1]); + } + else if (a == mXmove || a == mYmove || a == mZmove) + { + //Might not be correct, haven't tested it yet + if (l2capinbuf == NULL) + return 0; + if (a == mXmove) + return ((l2capinbuf[(uint16_t)a + 1] << 0x04) | (l2capinbuf[(uint16_t)a] << 0x0C)); + //return (((unsigned char)l2capinbuf[(unsigned int)a + 1]) | (((unsigned char)l2capinbuf[(unsigned int)a] & 0x0F)) << 8); + else if (a == mYmove) + return ((l2capinbuf[(uint16_t)a + 1] & 0xF0) | (l2capinbuf[(uint16_t)a] << 0x08)); + //return (((unsigned char)l2capinbuf[(unsigned int)a + 1]) | (((unsigned char)l2capinbuf[(unsigned int)a] & 0x0F)) << 8); + else if (a == mZmove) + return ((l2capinbuf[(uint16_t)a + 1] << 0x0F) | (l2capinbuf[(uint16_t)a] << 0x0C)); + //return ((((unsigned char)l2capinbuf[(unsigned int)a + 1] & 0xF0) >> 4) | ((unsigned char)l2capinbuf[(unsigned int)a] << 4)); + else + return 0; + } + else if (a == tempMove) + { + if (l2capinbuf == NULL) + return 0; + return (((l2capinbuf[(uint16_t)a + 1] & 0xF0) >> 4) | (l2capinbuf[(uint16_t)a] << 4)); + } + else + { + if (l2capinbuf == NULL) + return 0; + return (((l2capinbuf[(uint16_t)a + 1] << 8) | l2capinbuf[(uint16_t)a]) - 0x8000); + } +} +double PS3BT::getAngle(Angle a, boolean resolution)//Boolean indicate if 360-degrees resolution is used or not - set false if you want to use both axis +{ + double accXin; + double accXval; + double Pitch; + + double accYin; + double accYval; + double Roll; + + double accZin; + double accZval; + + //Data for the Kionix KXPC4 used in DualShock 3 + double sensivity = 204.6;//0.66/3.3*1023 (660mV/g) + double zeroG = 511.5;//1.65/3.3*1023 (1,65V) + double R;//force vector + + accXin = getSensor(aX); + accXval = (zeroG - accXin) / sensivity;//Convert to g's + accXval *= 2; + + accYin = getSensor(aY); + accYval = (zeroG - accYin) / sensivity;//Convert to g's + accYval *= 2; + + accZin = getSensor(aZ); + accZval = (zeroG - accZin) / sensivity;//Convert to g's + accZval *= 2; + + R = sqrt(pow(accXval, 2) + pow(accYval, 2) + pow(accZval, 2)); + + if (a == Pitch) + { + //the result will come out as radians, so it is multiplied by 180/pi, to convert to degrees + //In the end it is minus by 90, so its 0 degrees when in horizontal postion + Pitch = acos(accXval / R) * 180 / PI - 90; + if(resolution) + { + if (accZval < 0)//Convert to 360 degrees resolution - set resolution false if you need both pitch and roll + { + if (Pitch < 0) + Pitch = -180 - Pitch; + else + Pitch = 180 - Pitch; + } + } + return Pitch; + + } + else + { + //the result will come out as radians, so it is multiplied by 180/pi, to convert to degrees + //In the end it is minus by 90, so its 0 degrees when in horizontal postion + Roll = acos(accYval / R) * 180 / PI - 90; + if(resolution) + { + if (accZval < 0)//Convert to 360 degrees resolution - set resolution false if you need both pitch and roll + { + if (Roll < 0) + Roll = -180 - Roll; + else + Roll = 180 - Roll; + } + } + return Roll; + } +} +bool PS3BT::getStatus(Status c) +{ + if (l2capinbuf == NULL) + return false; + if (l2capinbuf[(uint16_t)c >> 8] == ((uint8_t)c & 0xff)) + return true; + return false; +} +String PS3BT::getStatusString() +{ + if (PS3BTConnected || PS3NavigationBTConnected) + { + char statusOutput[100]; + + strcpy(statusOutput,"ConnectionStatus: "); + + if (getStatus(Plugged)) strcat(statusOutput,"Plugged"); + else if (getStatus(Unplugged)) strcat(statusOutput,"Unplugged"); + else strcat(statusOutput,"Error"); + + + strcat(statusOutput," - PowerRating: "); + if (getStatus(Charging)) strcat(statusOutput,"Charging"); + else if (getStatus(NotCharging)) strcat(statusOutput,"Not Charging"); + else if (getStatus(Shutdown)) strcat(statusOutput,"Shutdown"); + else if (getStatus(Dying)) strcat(statusOutput,"Dying"); + else if (getStatus(Low)) strcat(statusOutput,"Low"); + else if (getStatus(High)) strcat(statusOutput,"High"); + else if (getStatus(Full)) strcat(statusOutput,"Full"); + else strcat(statusOutput,"Error"); + + strcat(statusOutput," - WirelessStatus: "); + + if (getStatus(CableRumble)) strcat(statusOutput,"Cable - Rumble is on"); + else if (getStatus(Cable)) strcat(statusOutput,"Cable - Rumble is off"); + else if (getStatus(BluetoothRumble)) strcat(statusOutput,"Bluetooth - Rumble is on"); + else if (getStatus(Bluetooth)) strcat(statusOutput,"Bluetooth - Rumble is off"); + else strcat(statusOutput,"Error"); + + return statusOutput; + + } + else if(PS3MoveBTConnected) + { + char statusOutput[50]; + + strcpy(statusOutput,"PowerRating: "); + + if (getStatus(MoveCharging)) strcat(statusOutput,"Charging"); + else if (getStatus(MoveNotCharging)) strcat(statusOutput,"Not Charging"); + else if (getStatus(MoveShutdown)) strcat(statusOutput,"Shutdown"); + else if (getStatus(MoveDying)) strcat(statusOutput,"Dying"); + else if (getStatus(MoveLow)) strcat(statusOutput,"Low"); + else if (getStatus(MoveHigh)) strcat(statusOutput,"High"); + else if (getStatus(MoveFull)) strcat(statusOutput,"Full"); + else strcat(statusOutput,"Error"); + + return statusOutput; + } +} +void PS3BT::disconnect()//Use this void to disconnect any of the controllers +{ + if (PS3BTConnected) + PS3BTConnected = false; + else if (PS3MoveBTConnected) + PS3MoveBTConnected = false; + else if (PS3NavigationBTConnected) + PS3NavigationBTConnected = false; + + //First the HID interrupt channel has to be disconencted, then the HID control channel and finally the HCI connection + l2cap_disconnection_request(0x0A, interrupt_dcid, interrupt_scid); + l2cap_state = L2CAP_EV_INTERRUPT_DISCONNECT; +} + +void PS3BT::HCI_event_task() +{ + /* check the event pipe*/ + uint16_t MAX_BUFFER_SIZE = BULK_MAXPKTSIZE; + pUsb->inTransfer(bAddress, epInfo[ CSR_EVENT_PIPE ].epAddr, &MAX_BUFFER_SIZE, hcibuf); // input on endpoint 1 + switch (hcibuf[0]) //switch on event type + { + case EV_COMMAND_COMPLETE: + hci_event_flag |= HCI_FLAG_CMD_COMPLETE; // set command complete flag + 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]; + } + break; + + case EV_COMMAND_STATUS: + //hci_command_packets = hcibuf[3]; // update flow control + hci_event_flag |= HCI_FLAG_CMD_STATUS; //set status flag + if(hcibuf[2]) // show status on serial if not OK + { + #ifdef DEBUG + Notify(PSTR("\r\nHCI Command Failed: ")); + PrintHex(hcibuf[2]); + Serial.print(" "); + PrintHex(hcibuf[4]); + Serial.print(" "); + PrintHex(hcibuf[5]); + #endif + } + break; + + case EV_CONNECT_COMPLETE: + hci_event_flag |= HCI_FLAG_CONN_COMPLETE; // set connection complete flag + if (!hcibuf[2]) // check if connected OK + { + hci_handle = hcibuf[3] | hcibuf[4] << 8; //store the handle for the ACL connection + hci_event_flag |= HCI_FLAG_CONNECT_OK; //set connection OK flag + } + break; + + case EV_DISCONNECT_COMPLETE: + hci_event_flag |= HCI_FLAG_DISCONN_COMPLETE; //set disconnect commend complete flag + if (!hcibuf[2]) // check if disconnected OK + hci_event_flag &= ~(HCI_FLAG_CONNECT_OK); //clear connection OK flag + break; + + case EV_NUM_COMPLETE_PKT: + break; + case EV_REMOTE_NAME_COMPLETE: + for (uint8_t i = 0; i < 30; i++) + remote_name[i] = hcibuf[9 + i]; //store first 30 bytes + hci_event_flag |=HCI_FLAG_REMOTE_NAME_COMPLETE; + break; + + case EV_INCOMING_CONNECT: + disc_bdaddr[0] = hcibuf[2]; + disc_bdaddr[1] = hcibuf[3]; + disc_bdaddr[2] = hcibuf[4]; + disc_bdaddr[3] = hcibuf[5]; + disc_bdaddr[4] = hcibuf[6]; + disc_bdaddr[5] = hcibuf[7]; + hci_event_flag |=HCI_FLAG_INCOMING_REQUEST; + break; + + case EV_ROLE_CHANGED: + /* + #ifdef DEBUG + Notify(PSTR("\r\nRole Changed")); + #endif + */ + break; + default: + /* + #ifdef DEBUG + if(hcibuf[0] != 0x00) + { + Notify(PSTR("\r\nUnmanaged Event: ")); + PrintHex(hcibuf[0]); + } + #endif + */ + break; + } // switch + HCI_task(); +} + +/* Poll Bluetooth and print result */ +void PS3BT::HCI_task() +{ + switch (hci_state){ + case HCI_INIT_STATE: + hci_counter++; + if (hci_counter > 10) // wait until we have looped 10 times to clear any old events + { + hci_reset(); + hci_state = HCI_RESET_STATE; + hci_counter = 0; + } + break; + + case HCI_RESET_STATE: + hci_counter++; + if (hci_cmd_complete) + { + #ifdef DEBUG + Notify(PSTR("\r\nHCI Reset complete")); + #endif + hci_state = HCI_BDADDR_STATE; + hci_read_bdaddr(); + } + if (hci_counter > 100) + { + #ifdef DEBUG + Notify(PSTR("\r\nNo response to HCI Reset")); + #endif + hci_state = HCI_INIT_STATE; + hci_counter = 0; + } + break; + case HCI_BDADDR_STATE: + if (hci_cmd_complete) + { + #ifdef DEBUG + Notify(PSTR("\r\nLocal Bluetooth Address: ")); + for(int8_t i = 5; i > 0;i--) + { + PrintHex(my_bdaddr[i]); + Serial.print(":"); + } + PrintHex(my_bdaddr[0]); + #endif + hci_state = HCI_SCANNING_STATE; + } + break; + case HCI_SCANNING_STATE: + #ifdef DEBUG + Notify(PSTR("\r\nWait For Incoming Connection Request")); + #endif + hci_write_scan_enable(); + hci_state = HCI_CONNECT_IN_STATE; + break; + + case HCI_CONNECT_IN_STATE: + if(hci_incoming_connect_request) + { + #ifdef DEBUG + Notify(PSTR("\r\nIncoming Request")); + #endif + hci_remote_name(); + hci_state = HCI_REMOTE_NAME_STATE; + } + break; + + case HCI_REMOTE_NAME_STATE: + if(hci_remote_name_complete) + { + #ifdef DEBUG + Notify(PSTR("\r\nRemote Name: ")); + for (uint8_t i = 0; i < 30; i++) + { + if(remote_name[i] == NULL) + break; + Serial.write(remote_name[i]); + } + #endif + hci_accept_connection(); + hci_state = HCI_CONNECTED_STATE; + } + break; + + case HCI_CONNECTED_STATE: + if (hci_connect_complete) + { + #ifdef DEBUG + Notify(PSTR("\r\nConnected to Device: ")); + for(int8_t i = 5; i>0;i--) + { + PrintHex(disc_bdaddr[i]); + Serial.print(":"); + } + PrintHex(disc_bdaddr[0]); + #endif + hci_write_scan_disable();//Only allow one controller + hci_state = HCI_DISABLE_SCAN; + } + break; + + case HCI_DISABLE_SCAN: + if (hci_cmd_complete) + { + #ifdef DEBUG + Notify(PSTR("\r\nScan Disabled")); + #endif + l2cap_event_flag = 0; + l2cap_state = L2CAP_EV_CONTROL_SETUP; + hci_state = HCI_DONE_STATE; + } + break; + + case HCI_DONE_STATE: + if (hci_disconnect_complete) + hci_state = HCI_DISCONNECT_STATE; + break; + + case HCI_DISCONNECT_STATE: + if (hci_disconnect_complete) + { + #ifdef DEBUG + Notify(PSTR("\r\nDisconnected from Device: ")); + for(int8_t i = 5; i>0;i--) + { + PrintHex(disc_bdaddr[i]); + Serial.print(":"); + } + PrintHex(disc_bdaddr[0]); + #endif + l2cap_event_flag = 0;//Clear all flags + hci_event_flag = 0;//Clear all flags + + //Reset all buffers + for (uint8_t i = 0; i < BULK_MAXPKTSIZE; i++) + hcibuf[i] = 0; + for (uint8_t i = 0; i < BULK_MAXPKTSIZE; i++) + l2capinbuf[i] = 0; + for (uint8_t i = 0; i < BULK_MAXPKTSIZE; i++) + l2capoutbuf[i] = 0; + + for (uint8_t i = 0; i < OUTPUT_REPORT_BUFFER_SIZE; i++) + HIDBuffer[i + 2] = pgm_read_byte(&OUTPUT_REPORT_BUFFER[i]);//First two bytes reserved for report type and ID + for (uint8_t i = 2; i < HIDMOVEBUFFERSIZE; i++) + HIDMoveBuffer[i] = 0; + + l2cap_state = L2CAP_EV_WAIT; + hci_state = HCI_SCANNING_STATE; + } + break; + default: + break; + } +} + +void PS3BT::ACL_event_task() +{ + uint16_t MAX_BUFFER_SIZE = BULK_MAXPKTSIZE; + pUsb->inTransfer(bAddress, epInfo[ CSR_DATAIN_PIPE ].epAddr, &MAX_BUFFER_SIZE, l2capinbuf); // input on endpoint 2 + 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] != 0x00) + { + Serial.print("\r\nL2CAP Signaling Command - 0x"); + PrintHex(l2capoutbuf[8]); + } + */ + if (l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) + { + #ifdef DEBUG + Notify(PSTR("\r\nL2CAP Command Rejected - Reason: ")); + PrintHex(l2capinbuf[13]); + Serial.print(" "); + PrintHex(l2capinbuf[12]); + Serial.print(" Data: "); + PrintHex(l2capinbuf[17]); + Serial.print(" "); + PrintHex(l2capinbuf[16]); + Serial.print(" "); + PrintHex(l2capinbuf[15]); + Serial.print(" "); + PrintHex(l2capinbuf[14]); + #endif + } + else if (l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) + { + /* + Notify(PSTR("\r\nPSM: ")); + PrintHex(l2capinbuf[13]); + Serial.print(" "); + PrintHex(l2capinbuf[12]); + Serial.print(" "); + + Notify(PSTR(" SCID: ")); + PrintHex(l2capinbuf[15]); + Serial.print(" "); + PrintHex(l2capinbuf[14]); + + Notify(PSTR(" Identifier: ")); + PrintHex(l2capinbuf[9]); + */ + if ((l2capinbuf[13] | l2capinbuf[12]) == L2CAP_PSM_HID_CTRL) + { + identifier = l2capinbuf[9]; + control_scid[0] = l2capinbuf[14]; + control_scid[1] = l2capinbuf[15]; + l2cap_event_flag |= L2CAP_EV_CONTROL_CONNECTION_REQUEST; + } + else if ((l2capinbuf[13] | l2capinbuf[12]) == L2CAP_PSM_HID_INTR) + { + identifier = l2capinbuf[9]; + interrupt_scid[0] = l2capinbuf[14]; + interrupt_scid[1] = l2capinbuf[15]; + l2cap_event_flag |= L2CAP_EV_INTERRUPT_CONNECTION_REQUEST; + } + } + else if (l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) + { + if (l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) + { + if ((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000)//Success + { + //Serial.print("\r\nHID Control Configuration Complete"); + l2cap_event_flag |= L2CAP_EV_CONTROL_CONFIG_SUCCESS; + } + } + else if (l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) + { + if ((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000)//Success + { + //Serial.print("\r\nHID Interrupt Configuration Complete"); + l2cap_event_flag |= L2CAP_EV_INTERRUPT_CONFIG_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"); + identifier = l2capinbuf[9]; + l2cap_event_flag |= L2CAP_EV_CONTROL_CONFIG_REQUEST; + } + else if (l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) + { + //Serial.print("\r\nHID Interrupt Configuration Request"); + identifier = l2capinbuf[9]; + l2cap_event_flag |= L2CAP_EV_INTERRUPT_CONFIG_REQUEST; + } + } + else if (l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) + { + if (l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) + { + #ifdef DEBUG + Notify(PSTR("\r\nDisconnected Request: Disconnected Control")); + #endif + identifier = l2capinbuf[9]; + l2cap_disconnection_response(identifier,control_dcid,control_scid); + } + else if (l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) + { + #ifdef DEBUG + Notify(PSTR("\r\nDisconnected Request: Disconnected Interrupt")); + #endif + identifier = l2capinbuf[9]; + l2cap_disconnection_response(identifier,interrupt_dcid,interrupt_scid); + } + } + else if (l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) + { + if (l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) + { + //Serial.print("\r\nDisconnected Response: Disconnected Control"); + identifier = l2capinbuf[9]; + l2cap_event_flag |= L2CAP_EV_CONTROL_DISCONNECT_RESPONSE; + } + else if (l2capinbuf[12] == interrupt_scid[0] && l2capinbuf[13] == interrupt_scid[1]) + { + //Serial.print("\r\nDisconnected Response: Disconnected Interrupt"); + identifier = l2capinbuf[9]; + l2cap_event_flag |= L2CAP_EV_INTERRUPT_DISCONNECT_RESPONSE; + } + } + } + else if (l2capinbuf[6] == interrupt_dcid[0] && l2capinbuf[7] == interrupt_dcid[1])//l2cap_interrupt + { + //Serial.print("\r\nL2CAP Interrupt"); + if(PS3BTConnected || PS3MoveBTConnected || PS3NavigationBTConnected) + { + readReport(); + #ifdef PRINTREPORT + printReport(); //Uncomment "#define PRINTREPORT" to print the report send by the PS3 Controllers + #endif + } + } + L2CAP_task(); + } +} +void PS3BT::L2CAP_task() +{ + switch (l2cap_state) + { + case L2CAP_EV_WAIT: + break; + case L2CAP_EV_CONTROL_SETUP: + if (l2cap_control_connection_request) + { + #ifdef DEBUG + Notify(PSTR("\r\nHID Control Incoming Connection Request")); + #endif + l2cap_connection_response(identifier, control_dcid, control_scid, PENDING); + delay(1); + l2cap_connection_response(identifier, control_dcid, control_scid, SUCCESSFUL); + identifier++; + delay(1); + l2cap_config_request(identifier, control_scid); + l2cap_state = L2CAP_EV_CONTROL_REQUEST; + } + break; + case L2CAP_EV_CONTROL_REQUEST: + if (l2cap_control_config_request) + { + #ifdef DEBUG + Notify(PSTR("\r\nHID Control Configuration Request")); + #endif + l2cap_config_response(identifier, control_scid); + l2cap_state = L2CAP_EV_CONTROL_SUCCESS; + } + break; + + case L2CAP_EV_CONTROL_SUCCESS: + if (l2cap_control_config_success) + { + #ifdef DEBUG + Notify(PSTR("\r\nHID Control Successfully Configured")); + #endif + l2cap_state = L2CAP_EV_INTERRUPT_SETUP; + } + break; + case L2CAP_EV_INTERRUPT_SETUP: + if (l2cap_interrupt_connection_request) + { + #ifdef DEBUG + Notify(PSTR("\r\nHID Interrupt Incoming Connection Request")); + #endif + l2cap_connection_response(identifier, interrupt_dcid, interrupt_scid, PENDING); + delay(1); + l2cap_connection_response(identifier, interrupt_dcid, interrupt_scid, SUCCESSFUL); + identifier++; + delay(1); + l2cap_config_request(identifier, interrupt_scid); + + l2cap_state = L2CAP_EV_INTERRUPT_REQUEST; + } + break; + case L2CAP_EV_INTERRUPT_REQUEST: + if (l2cap_interrupt_config_request) + { + #ifdef DEBUG + Notify(PSTR("\r\nHID Interrupt Configuration Request")); + #endif + l2cap_config_response(identifier, interrupt_scid); + l2cap_state = L2CAP_EV_INTERRUPT_SUCCESS; + } + break; + case L2CAP_EV_INTERRUPT_SUCCESS: + if (l2cap_interrupt_config_success) + { + #ifdef DEBUG + Notify(PSTR("\r\nHID Interrupt Successfully Configured")); + #endif + l2cap_state = L2CAP_EV_HID_ENABLE_SIXAXIS; + } + break; + case L2CAP_EV_HID_ENABLE_SIXAXIS: + delay(1000);//There has to be a delay before sending the commands + + for (uint8_t i = 0; i < BULK_MAXPKTSIZE; i++)//Reset l2cap in buffer as it sometimes read it as a button has been pressed + l2capinbuf[i] = 0; + ButtonState = 0; + OldButtonState = 0; + + if (remote_name[0] == 'P')//First letter in PLAYSTATION(R)3 Controller ('P') - 0x50 + { + enable_sixaxis(); + + for (uint8_t i = 15; i < 19; i++) + l2capinbuf[i] = 0x7F;//Set the analog joystick values to center position + + delay(1000);//There has to be a delay before data can be read + setLedOn(LED1); + #ifdef DEBUG + Notify(PSTR("\r\nDualshock 3 Controller Enabled")); + #endif + PS3BTConnected = true; + } + else if (remote_name[0] == 'N')//First letter in Navigation Controller ('N') - 0x4E + { + enable_sixaxis(); + + for (uint8_t i = 15; i < 19; i++) + l2capinbuf[i] = 0x7F;//Set the analog joystick values to center + + delay(1000);//There has to be a delay before data can be read + setLedOn(LED1);//This just turns LED constantly on, on the Navigation controller + #ifdef DEBUG + Notify(PSTR("\r\nNavigation Controller Enabled")); + #endif + PS3NavigationBTConnected = true; + } + else if (remote_name[0] == 'M')//First letter in Motion Controller ('M') - 0x4D + { + delay(1000);//There has to be a delay before data can be read + moveSetBulb(Red); + #ifdef DEBUG + Notify(PSTR("\r\nMotion Controller Enabled")); + #endif + PS3MoveBTConnected = true; + + timerBulbRumble = millis(); + } + l2cap_state = L2CAP_EV_L2CAP_DONE; + break; + + case L2CAP_EV_L2CAP_DONE: + if (PS3MoveBTConnected)//The Bulb and rumble values, has to be send at aproximatly every 5th second for it to stay on + { + dtimeBulbRumble = millis() - timerBulbRumble; + if (dtimeBulbRumble > 4000)//Send at least every 4th second + { + HIDMove_Command(HIDMoveBuffer, HIDMOVEBUFFERSIZE);//The Bulb and rumble values, has to be written again and again, for it to stay turned on + timerBulbRumble = millis(); + } + } + break; + + case L2CAP_EV_INTERRUPT_DISCONNECT: + if (l2cap_interrupt_disconnect_response) + { + #ifdef DEBUG + Notify(PSTR("\r\nDisconnected Interrupt Channel")); + #endif + identifier++; + l2cap_disconnection_request(identifier, control_dcid, control_scid); + l2cap_state = L2CAP_EV_CONTROL_DISCONNECT; + } + break; + + case L2CAP_EV_CONTROL_DISCONNECT: + if (l2cap_control_disconnect_response) + { + #ifdef DEBUG + Notify(PSTR("\r\nDisconnected Control Channel")); + #endif + hci_disconnect(); + l2cap_state = L2CAP_EV_L2CAP_DONE; + hci_state = HCI_DISCONNECT_STATE; + } + break; + } +} + +/************************************************************/ +/* HID Report (HCI ACL Packet) */ +/************************************************************/ +void PS3BT::readReport() +{ + if(l2capinbuf[8] == 0xA1)//HID_THDR_DATA_INPUT + { + if(PS3BTConnected || PS3NavigationBTConnected) + ButtonState = (uint32_t)(l2capinbuf[11] | ((uint16_t)l2capinbuf[12] << 8) | ((uint32_t)l2capinbuf[13] << 16)); + else if(PS3MoveBTConnected) + ButtonState = (uint32_t)(l2capinbuf[10] | ((uint16_t)l2capinbuf[11] << 8) | ((uint32_t)l2capinbuf[12] << 16)); + + //Notify(PSTR("\r\nButtonState"); + //PrintHex(ButtonState); + + if(ButtonState != OldButtonState) + { + ButtonChanged = true; + if(ButtonState != 0x00) + ButtonPressed = true; + else + ButtonPressed = false; + } + + else + { + ButtonChanged = false; + ButtonPressed = false; + } + + OldButtonState = ButtonState; + } +} + +void PS3BT::printReport() //Uncomment "#define PRINTREPORT" to print the report send by the PS3 Controllers +{ + if(l2capinbuf[8] == 0xA1)//HID_THDR_DATA_INPUT + { + for(uint8_t i = 10; i < 58;i++) + { + PrintHex(l2capinbuf[i]); + Serial.print(" "); + } + Serial.println(""); + } +} + +/************************************************************/ +/* HCI Commands */ +/************************************************************/ + +//perform HCI Command +void PS3BT::HCI_Command(uint8_t* data, uint16_t nbytes) +{ + hci_event_flag &= ~HCI_FLAG_CMD_COMPLETE; + pUsb->ctrlReq(bAddress, epInfo[ CSR_CONTROL_PIPE ].epAddr, bmREQ_HCI_OUT, 0x00, 0x00, 0x00 ,0x00, nbytes, nbytes, data, NULL); +} + +void PS3BT::hci_reset() +{ + hci_event_flag = 0; // clear all the flags + hcibuf[0] = 0x03; // HCI OCF = 3 + hcibuf[1] = 0x03 << 2; // HCI OGF = 3 + hcibuf[2] = 0x00; + HCI_Command(hcibuf, 3); +} +void PS3BT::hci_write_scan_enable() +{ + hcibuf[0] = 0x1A; // HCI OCF = 1A + hcibuf[1] = 0x03 << 2; // HCI OGF = 3 + hcibuf[2] = 0x01;// parameter length = 1 + hcibuf[3] = 0x02;// Inquiry Scan disabled. Page Scan enabled. + HCI_Command(hcibuf, 4); +} +void PS3BT::hci_write_scan_disable() +{ + hcibuf[0] = 0x1A; // HCI OCF = 1A + hcibuf[1] = 0x03 << 2; // HCI OGF = 3 + hcibuf[2] = 0x01;// parameter length = 1 + hcibuf[3] = 0x00;// Inquiry Scan disabled. Page Scan disabled. + HCI_Command(hcibuf, 4); +} +void PS3BT::hci_read_bdaddr() +{ + hcibuf[0] = 0x09; // HCI OCF = 9 + hcibuf[1] = 0x04 << 2; // HCI OGF = 4 + hcibuf[2] = 0x00; + HCI_Command(hcibuf, 3); +} +void PS3BT::hci_accept_connection() +{ + hci_event_flag |= HCI_FLAG_CONNECT_OK; + hci_event_flag &= ~(HCI_FLAG_INCOMING_REQUEST); + + hcibuf[0] = 0x09; // HCI OCF = 9 + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x07; // parameter length 7 + 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] = 0; //switch role to master + + HCI_Command(hcibuf, 10); +} +void PS3BT::hci_remote_name() +{ + hci_event_flag &= ~(HCI_FLAG_REMOTE_NAME_COMPLETE); + hcibuf[0] = 0x19; // HCI OCF = 19 + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x0A; // parameter length = 10 + 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] = 0x01; //Page Scan Repetition Mode + hcibuf[10] = 0x00; //Reserved + hcibuf[11] = 0x00; //Clock offset - low byte + hcibuf[12] = 0x00; //Clock offset - high byte + + HCI_Command(hcibuf, 13); +} +void PS3BT::hci_disconnect() +{ + hci_event_flag &= ~HCI_FLAG_DISCONN_COMPLETE; + hcibuf[0] = 0x06; // HCI OCF = 6 + 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] = 0x13; // reason + + HCI_Command(hcibuf, 6); +} +/************************************************************/ +/* L2CAP Commands */ +/************************************************************/ +void PS3BT::L2CAP_Command(uint8_t* data, uint16_t nbytes) +{ + uint8_t buf[64]; + buf[0] = (uint8_t)(hci_handle & 0xff); // HCI handle with PB,BC flag + buf[1] = (uint8_t)(((hci_handle >> 8) & 0x0f) | 0x20); + buf[2] = (uint8_t)((4 + nbytes) & 0xff); // HCI ACL total data length + buf[3] = (uint8_t)((4 + nbytes) >> 8); + buf[4] = (uint8_t)(nbytes & 0xff); // L2CAP header: Length + buf[5] = (uint8_t)(nbytes >> 8); + buf[6] = 0x01; // L2CAP header: Channel ID + buf[7] = 0x00; // L2CAP Signalling channel over ACL-U logical link + + for (uint16_t i = 0; i < nbytes; i++)//L2CAP C-frame + buf[8 + i] = data[i]; + + uint8_t rcode = pUsb->outTransfer(bAddress, epInfo[ CSR_DATAOUT_PIPE ].epAddr, (8 + nbytes), buf); + if(rcode) + { + #ifdef DEBUG + Notify(PSTR("\r\nError sending message: 0x")); + PrintHex(rcode); + #endif + } +} +void PS3BT::l2cap_connection_response(uint8_t rxid, uint8_t dcid[], uint8_t scid[], uint8_t result) +{ + l2capoutbuf[0] = L2CAP_CMD_CONNECTION_RESPONSE;// Code + l2capoutbuf[1] = rxid;// Identifier + l2capoutbuf[2] = 0x08;// Length + l2capoutbuf[3] = 0x00; + l2capoutbuf[4] = dcid[0];// Destination CID + l2capoutbuf[5] = dcid[1]; + l2capoutbuf[6] = scid[0];// Source CID + l2capoutbuf[7] = scid[1]; + l2capoutbuf[8] = result;// Result: Pending or Success + l2capoutbuf[9] = 0x00; + l2capoutbuf[10] = 0x00;//No further information + l2capoutbuf[11] = 0x00; + + L2CAP_Command(l2capoutbuf, 12); +} +void PS3BT::l2cap_config_request(uint8_t rxid, uint8_t dcid[]) +{ + l2capoutbuf[0] = L2CAP_CMD_CONFIG_REQUEST;// Code + l2capoutbuf[1] = rxid;// Identifier + l2capoutbuf[2] = 0x08;// Length + l2capoutbuf[3] = 0x00; + l2capoutbuf[4] = dcid[0];// Destination CID + l2capoutbuf[5] = dcid[1]; + l2capoutbuf[6] = 0x00;// Flags + l2capoutbuf[7] = 0x00; + l2capoutbuf[8] = 0x01;// Config Opt: type = MTU (Maximum Transmission Unit) - Hint + l2capoutbuf[9] = 0x02;// Config Opt: length + l2capoutbuf[10] = 0xFF;// MTU + l2capoutbuf[11] = 0xFF; + + L2CAP_Command(l2capoutbuf, 12); +} +void PS3BT::l2cap_config_response(uint8_t rxid, uint8_t scid[]) +{ + l2capoutbuf[0] = L2CAP_CMD_CONFIG_RESPONSE;// Code + l2capoutbuf[1] = rxid;// Identifier + l2capoutbuf[2] = 0x0A;// Length + l2capoutbuf[3] = 0x00; + l2capoutbuf[4] = scid[0];// Source CID + l2capoutbuf[5] = scid[1]; + l2capoutbuf[6] = 0x00;// Flag + l2capoutbuf[7] = 0x00; + l2capoutbuf[8] = 0x00;// Result + l2capoutbuf[9] = 0x00; + l2capoutbuf[10] = 0x01;// Config + l2capoutbuf[11] = 0x02; + l2capoutbuf[12] = 0xA0; + l2capoutbuf[13] = 0x02; + + L2CAP_Command(l2capoutbuf, 14); +} +void PS3BT::l2cap_disconnection_request(uint8_t rxid, uint8_t dcid[], uint8_t scid[]) +{ + l2capoutbuf[0] = L2CAP_CMD_DISCONNECT_REQUEST;// Code + l2capoutbuf[1] = rxid;// Identifier + l2capoutbuf[2] = 0x04;// Length + l2capoutbuf[3] = 0x00; + l2capoutbuf[4] = scid[0];// Really Destination CID + l2capoutbuf[5] = scid[1]; + l2capoutbuf[6] = dcid[0];// Really Source CID + l2capoutbuf[7] = dcid[1]; + L2CAP_Command(l2capoutbuf, 8); +} +void PS3BT::l2cap_disconnection_response(uint8_t rxid, uint8_t dcid[], uint8_t scid[]) +{ + l2capoutbuf[0] = L2CAP_CMD_DISCONNECT_RESPONSE;// Code + l2capoutbuf[1] = rxid;// Identifier + l2capoutbuf[2] = 0x04;// Length + l2capoutbuf[3] = 0x00; + l2capoutbuf[4] = scid[0];// Really Destination CID + l2capoutbuf[5] = scid[1]; + l2capoutbuf[6] = dcid[0];// Really Source CID + l2capoutbuf[7] = dcid[1]; + L2CAP_Command(l2capoutbuf, 8); +} +/******************************************************************* + * * + * HCI ACL Data Packet * + * * + * buf[0] buf[1] buf[2] buf[3] + * 0 4 8 11 12 16 24 31 MSB + * .-+-+-+-+-+-+-+-|-+-+-+-|-+-|-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. + * | HCI Handle |PB |BC | Data Total Length | HCI ACL Data Packet + * .-+-+-+-+-+-+-+-|-+-+-+-|-+-|-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. + * + * buf[4] buf[5] buf[6] buf[7] + * 0 8 16 31 MSB + * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. + * | Length | Channel ID | Basic L2CAP header + * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. + * + * buf[8] buf[9] buf[10] buf[11] + * 0 8 16 31 MSB + * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. + * | Code | Identifier | Length | Control frame (C-frame) + * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. (signaling packet format) + */ + +/************************************************************/ +/* HID Commands */ +/************************************************************/ + +//Playstation Sixaxis Dualshock and Navigation Controller commands +void PS3BT::HID_Command(uint8_t* data, uint16_t nbytes) +{ + uint8_t buf[64]; + buf[0] = (uint8_t)(hci_handle & 0xff); // HCI handle with PB,BC flag + buf[1] = (uint8_t)(((hci_handle >> 8) & 0x0f) | 0x20); + buf[2] = (uint8_t)((4 + nbytes) & 0xff); // HCI ACL total data length + buf[3] = (uint8_t)((4 + nbytes) >> 8); + buf[4] = (uint8_t)(nbytes & 0xff); // L2CAP header: Length + buf[5] = (uint8_t)(nbytes >> 8); + buf[6] = control_scid[0];//Both the Navigation and Dualshock controller sends data via the controller channel + buf[7] = control_scid[1]; + + for (uint16_t i = 0; i < nbytes; i++)//L2CAP C-frame + buf[8 + i] = data[i]; + + dtimeHID = millis() - timerHID; + + if (dtimeHID <= 250)// Check if is has been more than 250ms since last command + delay((uint32_t)(250 - dtimeHID));//There have to be a delay between commands + + pUsb->outTransfer(bAddress, epInfo[ CSR_DATAOUT_PIPE ].epAddr, (8 + nbytes), buf); + + timerHID = millis(); +} +void PS3BT::setAllOff() +{ + for (uint8_t i = 0; i < OUTPUT_REPORT_BUFFER_SIZE; i++) + HIDBuffer[i + 2] = pgm_read_byte(&OUTPUT_REPORT_BUFFER[i]);//First two bytes reserved for report type and ID + + HID_Command(HIDBuffer, OUTPUT_REPORT_BUFFER_SIZE + 2); +} +void PS3BT::setRumbleOff() +{ + HIDBuffer[3] = 0x00; + HIDBuffer[4] = 0x00;//low mode off + HIDBuffer[5] = 0x00; + HIDBuffer[6] = 0x00;//high mode off + + HID_Command(HIDBuffer, OUTPUT_REPORT_BUFFER_SIZE + 2); +} +void PS3BT::setRumbleOn(Rumble mode) +{ + /* Still not totally sure how it works, maybe something like this instead? + * 3 - duration_right + * 4 - power_right + * 5 - duration_left + * 6 - power_left + */ + if ((mode & 0x30) > 0) + { + HIDBuffer[3] = 0xfe; + HIDBuffer[5] = 0xfe; + + if (mode == RumbleHigh) + { + HIDBuffer[4] = 0;//low mode off + HIDBuffer[6] = 0xff;//high mode on + } + else + { + HIDBuffer[4] = 0xff;//low mode on + HIDBuffer[6] = 0;//high mode off + } + + HID_Command(HIDBuffer, OUTPUT_REPORT_BUFFER_SIZE + 2); + } +} +void PS3BT::setLedOff(LED a) +{ + //check if LED is already off + if ((uint8_t)((uint8_t)(((uint16_t)a << 1) & HIDBuffer[11])) != 0) + { + //set the LED into the write buffer + HIDBuffer[11] = (uint8_t)((uint8_t)(((uint16_t)a & 0x0f) << 1) ^ HIDBuffer[11]); + + HID_Command(HIDBuffer, OUTPUT_REPORT_BUFFER_SIZE + 2); + } +} +void PS3BT::setLedOn(LED a) +{ + HIDBuffer[11] = (uint8_t)((uint8_t)(((uint16_t)a & 0x0f) << 1) | HIDBuffer[11]); + + HID_Command(HIDBuffer, OUTPUT_REPORT_BUFFER_SIZE + 2); +} +void PS3BT::enable_sixaxis()//Command used to enable the Dualshock 3 and Navigation controller to send data via USB +{ + uint8_t cmd_buf[12]; + cmd_buf[0] = 0x53;// HID BT Set_report (0x50) | Report Type (Feature 0x03) + cmd_buf[1] = 0xF4;// Report ID + cmd_buf[2] = 0x42;// Special PS3 Controller enable commands + cmd_buf[3] = 0x03; + cmd_buf[4] = 0x00; + cmd_buf[5] = 0x00; + + HID_Command(cmd_buf, 6); +} + +//Playstation Move Controller commands +void PS3BT::HIDMove_Command(uint8_t* data,uint16_t nbytes) +{ + uint8_t buf[64]; + buf[0] = (uint8_t)(hci_handle & 0xff); // HCI handle with PB,BC flag + buf[1] = (uint8_t)(((hci_handle >> 8) & 0x0f) | 0x20); + buf[2] = (uint8_t)((4 + nbytes) & 0xff); // HCI ACL total data length + buf[3] = (uint8_t)((4 + nbytes) >> 8); + buf[4] = (uint8_t)(nbytes & 0xff); // L2CAP header: Length + buf[5] = (uint8_t)(nbytes >> 8); + buf[6] = interrupt_scid[0];//The Move controller sends it's data via the intterrupt channel + buf[7] = interrupt_scid[1]; + + for (uint16_t i = 0; i < nbytes; i++)//L2CAP C-frame + buf[8 + i] = data[i]; + + dtimeHID = millis() - timerHID; + + if (dtimeHID <= 250)// Check if is has been less than 200ms since last command + delay((uint32_t)(250 - dtimeHID));//There have to be a delay between commands + + pUsb->outTransfer(bAddress, epInfo[ CSR_DATAOUT_PIPE ].epAddr, (8 + nbytes), buf); + + timerHID = millis(); +} +void PS3BT::moveSetBulb(uint8_t r, uint8_t g, uint8_t b)//Use this to set the Color using RGB values +{ + //set the Bulb's values into the write buffer + HIDMoveBuffer[3] = r; + HIDMoveBuffer[4] = g; + HIDMoveBuffer[5] = b; + + HIDMove_Command(HIDMoveBuffer, HIDMOVEBUFFERSIZE); +} +void PS3BT::moveSetBulb(Colors color)//Use this to set the Color using the predefined colors in "enums.h" +{ + //set the Bulb's values into the write buffer + HIDMoveBuffer[3] = (uint8_t)(color >> 16); + HIDMoveBuffer[4] = (uint8_t)(color >> 8); + HIDMoveBuffer[5] = (uint8_t)(color); + + HIDMove_Command(HIDMoveBuffer, HIDMOVEBUFFERSIZE); +} +void PS3BT::moveSetRumble(uint8_t rumble) +{ + //set the rumble value into the write buffer + HIDMoveBuffer[7] = rumble; + + HIDMove_Command(HIDMoveBuffer, HIDMOVEBUFFERSIZE); +} \ No newline at end of file diff --git a/PS3BT.h b/PS3BT.h new file mode 100644 index 00000000..d2fb2126 --- /dev/null +++ b/PS3BT.h @@ -0,0 +1,441 @@ +/* Copyright (C) 2011 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 + ------------------- + + TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : mail@tkjelectronics.com + */ + +#ifndef _ps3bt_h_ +#define _ps3bt_h_ + +#if defined(ARDUINO) && ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +#include "Usb.h" + +/*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 and HID */ + +/* CSR Bluetooth data taken from descriptors */ +#define INT_MAXPKTSIZE 16 // max size for HCI data +#define BULK_MAXPKTSIZE 64 // max size for ACL data + +/* PS3 data taken from descriptors */ +#define EP_MAXPKTSIZE 64 // max size for data via USB + +/* Endpoint types */ +#define EP_INTERRUPT 0x03 +#define EP_BULK 0x02 + +#define CSR_CONTROL_PIPE 0 // names we give to the 4 pipes +#define CSR_EVENT_PIPE 1 +#define CSR_DATAIN_PIPE 2 +#define CSR_DATAOUT_PIPE 3 + +#define PS3_CONTROL_PIPE 0 // names we give to the 3 pipes +#define PS3_OUTPUT_PIPE 1 +#define PS3_INPUT_PIPE 2 + +//PID and VID of the different devices +#define CSR_VID 0x0A12 //Cambridge Silicon Radio Ltd. +#define CSR_PID 0x0001 //Bluetooth HCI Device +#define PS3_VID 0x054C //Sony Corporation +#define PS3_PID 0x0268 //PS3 Controller DualShock 3 +#define PS3NAVIGATION_VID 0x054C //Sony Corporation +#define PS3NAVIGATION_PID 0x042F //Navigation controller +#define PS3MOVE_VID 0x054C //Sony Corporation +#define PS3MOVE_PID 0x03D5 //Motion controller + +#define HIDMOVEBUFFERSIZE 50 // size of the buffer for the Playstation Motion Controller +#define OUTPUT_REPORT_BUFFER_SIZE 48 //Size of the output report buffer for the controllers + +// used in control endpoint header for HCI Commands +#define bmREQ_HCI_OUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE + +// used in control endpoint header for HID Commands +#define bmREQ_HIDOUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE +#define HID_REQUEST_SET_REPORT 0x09 + +/* Bluetooth HCI states for hci_task() */ +#define HCI_INIT_STATE 0 +#define HCI_RESET_STATE 1 +#define HCI_BDADDR_STATE 2 +#define HCI_SCANNING_STATE 3 +#define HCI_CONNECT_IN_STATE 4 +#define HCI_REMOTE_NAME_STATE 5 +#define HCI_CONNECTED_STATE 6 +#define HCI_DISABLE_SCAN 7 +#define HCI_DONE_STATE 8 +#define HCI_DISCONNECT_STATE 9 + +/* HCI event flags*/ +#define HCI_FLAG_CMD_COMPLETE 0x01 +#define HCI_FLAG_CMD_STATUS 0x02 +#define HCI_FLAG_CONN_COMPLETE 0x04 +#define HCI_FLAG_DISCONN_COMPLETE 0x08 +#define HCI_FLAG_CONNECT_OK 0x10 +#define HCI_FLAG_REMOTE_NAME_COMPLETE 0x20 +#define HCI_FLAG_INCOMING_REQUEST 0x40 + +/*Macros for HCI event flag tests */ +#define hci_cmd_complete (hci_event_flag & HCI_FLAG_CMD_COMPLETE) +#define hci_cmd_status (hci_event_flag & HCI_FLAG_CMD_STATUS) +#define hci_connect_complete (hci_event_flag & HCI_FLAG_CONN_COMPLETE) +#define hci_disconnect_complete (hci_event_flag & HCI_FLAG_DISCONN_COMPLETE) +#define hci_connect_ok (hci_event_flag & HCI_FLAG_CONNECT_OK) +#define hci_remote_name_complete (hci_event_flag & HCI_FLAG_REMOTE_NAME_COMPLETE) +#define hci_incoming_connect_request (hci_event_flag & HCI_FLAG_INCOMING_REQUEST) + +/* HCI Events managed */ +#define EV_COMMAND_COMPLETE 0x0E +#define EV_COMMAND_STATUS 0x0F +#define EV_CONNECT_COMPLETE 0x03 +#define EV_DISCONNECT_COMPLETE 0x05 +#define EV_NUM_COMPLETE_PKT 0x13 +#define EV_INQUIRY_COMPLETE 0x01 +#define EV_INQUIRY_RESULT 0x02 +#define EV_REMOTE_NAME_COMPLETE 0x07 +#define EV_INCOMING_CONNECT 0x04 +#define EV_ROLE_CHANGED 0x12 + +/* Bluetooth L2CAP states for L2CAP_task() */ +#define L2CAP_EV_WAIT 0 +#define L2CAP_EV_CONTROL_SETUP 1 +#define L2CAP_EV_CONTROL_REQUEST 2 +#define L2CAP_EV_CONTROL_SUCCESS 3 +#define L2CAP_EV_INTERRUPT_SETUP 4 +#define L2CAP_EV_INTERRUPT_REQUEST 5 +#define L2CAP_EV_INTERRUPT_SUCCESS 6 +#define L2CAP_EV_HID_ENABLE_SIXAXIS 7 +#define L2CAP_EV_L2CAP_DONE 8 +#define L2CAP_EV_INTERRUPT_DISCONNECT 9 +#define L2CAP_EV_CONTROL_DISCONNECT 10 + +/* L2CAP event flags */ +#define L2CAP_EV_CONTROL_CONNECTION_REQUEST 0x01 +#define L2CAP_EV_CONTROL_CONFIG_REQUEST 0x02 +#define L2CAP_EV_CONTROL_CONFIG_SUCCESS 0x04 +#define L2CAP_EV_INTERRUPT_CONNECTION_REQUEST 0x08 +#define L2CAP_EV_INTERRUPT_CONFIG_REQUEST 0x10 +#define L2CAP_EV_INTERRUPT_CONFIG_SUCCESS 0x20 +#define L2CAP_EV_CONTROL_DISCONNECT_RESPONSE 0x40 +#define L2CAP_EV_INTERRUPT_DISCONNECT_RESPONSE 0x80 + +/*Macros for L2CAP event flag tests */ +#define l2cap_control_connection_request (l2cap_event_flag & L2CAP_EV_CONTROL_CONNECTION_REQUEST) +#define l2cap_control_config_request (l2cap_event_flag & L2CAP_EV_CONTROL_CONFIG_REQUEST) +#define l2cap_control_config_success (l2cap_event_flag & L2CAP_EV_CONTROL_CONFIG_SUCCESS) +#define l2cap_interrupt_connection_request (l2cap_event_flag & L2CAP_EV_INTERRUPT_CONNECTION_REQUEST) +#define l2cap_interrupt_config_request (l2cap_event_flag & L2CAP_EV_INTERRUPT_CONFIG_REQUEST) +#define l2cap_interrupt_config_success (l2cap_event_flag & L2CAP_EV_INTERRUPT_CONFIG_SUCCESS) +#define l2cap_control_disconnect_response (l2cap_event_flag & L2CAP_EV_CONTROL_DISCONNECT_RESPONSE) +#define l2cap_interrupt_disconnect_response (l2cap_event_flag & L2CAP_EV_INTERRUPT_DISCONNECT_RESPONSE) + +/* L2CAP signaling commands */ +#define L2CAP_CMD_COMMAND_REJECT 0x01 +#define L2CAP_CMD_CONNECTION_REQUEST 0x02 +#define L2CAP_CMD_CONNECTION_RESPONSE 0x03 +#define L2CAP_CMD_CONFIG_REQUEST 0x04 +#define L2CAP_CMD_CONFIG_RESPONSE 0x05 +#define L2CAP_CMD_DISCONNECT_REQUEST 0x06 +#define L2CAP_CMD_DISCONNECT_RESPONSE 0x07 + +/* Bluetooth L2CAP PSM */ +#define L2CAP_PSM_HID_CTRL 0x11 // HID_Control +#define L2CAP_PSM_HID_INTR 0x13 // HID_Interrupt + +// Used For Connection Response - Remember to Include High Byte +#define PENDING 0x01 +#define SUCCESSFUL 0x00 + +#define bConfigurationValue 0x01 // Used to set configuration + +#define PS3_MAX_ENDPOINTS 4 + +enum LED +{ + LED1 = 0x01, + LED2 = 0x02, + LED3 = 0x04, + LED4 = 0x08, + + LED5 = 0x09, + LED6 = 0x0A, + LED7 = 0x0C, + LED8 = 0x0D, + LED9 = 0x0E, + LED10 = 0x0F, +}; +enum Colors +{ + //Used to set the colors of the move controller + Red = 0xFF0000,//((255 << 16) | (0 << 8) | 0); + Green = 0xFF00,//((0 << 16) | (255 << 8) | 0); + Blue = 0xFF,//((0 << 16) | (0 << 8) | 255); + + Yellow = 0xFFEB04,//((255 << 16) | (235 << 8) | 4); + Lightblue = 0xFFFF,//((0 << 16) | (255 << 8) | 255); + Purble = 0xFF00FF,//((255 << 16) | (0 << 8) | 255); + + White = 0xFFFFFF,//((255 << 16) | (255 << 8) | 255); + Off = 0x00,//((0 << 16) | (0 << 8) | 0); +}; + +enum Button +{ + // byte location | bit location + + //Sixaxis Dualshcock 3 & Navigation controller + SELECT = (11 << 8) | 0x01, + L3 = (11 << 8) | 0x02, + R3 = (11 << 8) | 0x04, + START = (11 << 8) | 0x08, + UP = (11 << 8) | 0x10, + RIGHT = (11 << 8) | 0x20, + DOWN = (11 << 8) | 0x40, + LEFT = (11 << 8) | 0x80, + + L2 = (12 << 8) | 0x01, + R2 = (12 << 8) | 0x02, + L1 = (12 << 8) | 0x04, + R1 = (12 << 8) | 0x08, + TRIANGLE = (12 << 8) | 0x10, + CIRCLE = (12 << 8) | 0x20, + CROSS = (12 << 8) | 0x40, + SQUARE = (12 << 8) | 0x80, + + PS = (13 << 8) | 0x01, + + //Playstation Move Controller + SELECT_MOVE = (10 << 8) | 0x01, + START_MOVE = (10 << 8) | 0x08, + + TRIANGLE_MOVE = (11 << 8) | 0x10, + CIRCLE_MOVE = (11 << 8) | 0x20, + CROSS_MOVE = (11 << 8) | 0x40, + SQUARE_MOVE = (11 << 8) | 0x80, + + PS_MOVE = (12 << 8) | 0x01, + MOVE_MOVE = (12 << 8) | 0x08,//covers 12 bits - we only need to read the top 8 + T_MOVE = (12 << 8) | 0x10,//covers 12 bits - we only need to read the top 8 +}; +enum AnalogButton +{ + //Sixaxis Dualshcock 3 & Navigation controller + UP_ANALOG = 23, + RIGHT_ANALOG = 24, + DOWN_ANALOG = 25, + LEFT_ANALOG = 26, + + L2_ANALOG = 27, + R2_ANALOG = 28, + L1_ANALOG = 29, + R1_ANALOG = 30, + TRIANGLE_ANALOG = 31, + CIRCLE_ANALOG = 32, + CROSS_ANALOG = 33, + SQUARE_ANALOG = 34, + + //Playstation Move Controller + T_MOVE_ANALOG = 15,//Both at byte 14 (last reading) and byte 15 (current reading) +}; +enum AnalogHat +{ + LeftHatX = 15, + LeftHatY = 16, + RightHatX = 17, + RightHatY = 18, +}; +enum Sensor +{ + //Sensors inside the Sixaxis Dualshock 3 controller + aX = 50, + aY = 52, + aZ = 54, + gZ = 56, + + //Sensors inside the move motion controller - it only reads the 2nd frame + aXmove = 28, + aZmove = 30, + aYmove = 32, + + gXmove = 40, + gZmove = 42, + gYmove = 44, + + tempMove = 46, + + mXmove = 47, + mZmove = 49, + mYmove = 50, +}; +enum Angle +{ + Pitch = 0x01, + Roll = 0x02, +}; +enum Status +{ + // byte location | bit location + Plugged = (38 << 8) | 0x02, + Unplugged = (38 << 8) | 0x03, + + Charging = (39 << 8) | 0xEE, + NotCharging = (39 << 8) | 0xF1, + Shutdown = (39 << 8) | 0x01, + Dying = (39 << 8) | 0x02, + Low = (39 << 8) | 0x03, + High = (39 << 8) | 0x04, + Full = (39 << 8) | 0x05, + + MoveCharging = (21 << 8) | 0xEE, + MoveNotCharging = (21 << 8) | 0xF1, + MoveShutdown = (21 << 8) | 0x01, + MoveDying = (21 << 8) | 0x02, + MoveLow = (21 << 8) | 0x03, + MoveHigh = (21 << 8) | 0x04, + MoveFull = (21 << 8) | 0x05, + + CableRumble = (40 << 8) | 0x10,//Opperating by USB and rumble is turned on + Cable = (40 << 8) | 0x12,//Opperating by USB and rumble is turned off + BluetoothRumble = (40 << 8) | 0x14,//Opperating by bluetooth and rumble is turned on + Bluetooth = (40 << 8) | 0x16,//Opperating by bluetooth and rumble is turned off +}; +enum Rumble +{ + RumbleHigh = 0x10, + RumbleLow = 0x20, +}; + +class PS3BT : public USBDeviceConfig +{ +public: + PS3BT(USB *pUsb); + + // USBDeviceConfig implementation + virtual uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); + virtual uint8_t Release(); + virtual uint8_t Poll(); + virtual uint8_t GetAddress() { return bAddress; }; + + void setBdaddr(uint8_t* BDADDR); + void setMoveBdaddr(uint8_t* BDADDR); + + /* PS3 Controller Commands */ + bool getButton(Button b); + uint8_t getAnalogButton(AnalogButton a); + uint8_t getAnalogHat(AnalogHat a); + uint32_t getSensor(Sensor a); + double getAngle(Angle a, boolean resolution); + bool getStatus(Status c); + String getStatusString(); + void disconnect(); // use this void to disconnect any of the controllers + + /* HID Commands */ + /* Commands for Dualshock 3 and Navigation controller */ + void setAllOff(); + void setRumbleOff(); + void setRumbleOn(Rumble mode); + void setLedOff(LED a); + void setLedOn(LED a); + /* Commands for Motion controller only */ + void moveSetBulb(uint8_t r, uint8_t g, uint8_t b);//Use this to set the Color using RGB values + void moveSetBulb(Colors color);//Use this to set the Color using the predefined colors in "enum Colors" + void moveSetRumble(uint8_t rumble); + + bool PS3BTConnected;// Variable used to indicate if the normal playstation controller is successfully connected + bool PS3MoveBTConnected;// Variable used to indicate if the move controller is successfully connected + bool PS3NavigationBTConnected;// Variable used to indicate if the navigation controller is successfully connected + bool ButtonChanged;//Indicate if a button has been changed + bool ButtonPressed;//Indicate if a button has been pressed + +protected: + /* mandatory members */ + USB *pUsb; + uint8_t bAddress; + EpInfo epInfo[PS3_MAX_ENDPOINTS]; //endpoint info structure + +private: + bool bPollEnable; + + /*variables filled from HCI event management */ + int16_t hci_handle; + uint8_t disc_bdaddr[6]; // maximum of three discovered devices + uint8_t remote_name[30]; // first 30 chars of remote name + + /* variables used by high level HCI task */ + uint8_t hci_state; //current state of bluetooth hci connection + uint16_t hci_counter; // counter used for bluetooth hci reset loops + uint16_t hci_event_flag;// hci flags of received bluetooth events + + /* variables used by high level L2CAP task */ + uint8_t l2cap_state; + uint16_t l2cap_event_flag;// l2cap flags of received bluetooth events + + uint32_t ButtonState; + uint32_t OldButtonState; + uint32_t timerHID;// timer used see if there has to be a delay before a new HID command + uint32_t dtimeHID;// delta time since last HID command + uint32_t timerBulbRumble;// used to continuously set PS3 Move controller Bulb and rumble values + uint32_t dtimeBulbRumble;// used to know how longs since last since the Bulb and rumble values was written + + uint8_t my_bdaddr[6]; //Change to your dongles Bluetooth address in PS3BT.cpp + uint8_t hcibuf[BULK_MAXPKTSIZE];//General purpose buffer for hci data + uint8_t l2capinbuf[BULK_MAXPKTSIZE];//General purpose buffer for l2cap in data + uint8_t l2capoutbuf[BULK_MAXPKTSIZE];//General purpose buffer for l2cap out data + uint8_t HIDBuffer[BULK_MAXPKTSIZE];// Used to store HID commands + uint8_t HIDMoveBuffer[HIDMOVEBUFFERSIZE];// Used to store HID commands for the Move controller + + /* L2CAP Channels */ + uint8_t control_scid[2];// L2CAP source CID for HID_Control + uint8_t control_dcid[2];//0x0040 + uint8_t interrupt_scid[2];// L2CAP source CID for HID_Interrupt + uint8_t interrupt_dcid[2];//0x0041 + uint8_t identifier;//Identifier for connection + + void HCI_event_task(); //poll the HCI event pipe + void HCI_task(); // HCI state machine + void ACL_event_task(); // start polling the ACL input pipe too, though discard data until connected + void L2CAP_task(); // L2CAP state machine + + void readReport(); // read incoming data + void printReport(); // print incoming date - Uncomment for debugging + + /* HCI Commands */ + void HCI_Command(uint8_t* data, uint16_t nbytes); + void hci_reset(); + void hci_write_scan_enable(); + void hci_write_scan_disable(); + void hci_read_bdaddr(); + void hci_accept_connection(); + void hci_remote_name(); + void hci_disconnect(); + + /* L2CAP Commands */ + void L2CAP_Command(uint8_t* data, uint16_t nbytes); + void l2cap_connection_response(uint8_t rxid, uint8_t dcid[], uint8_t scid[], uint8_t result); + void l2cap_config_request(uint8_t rxid, uint8_t dcid[]); + void l2cap_config_response(uint8_t rxid, uint8_t scid[]); + void l2cap_disconnection_request(uint8_t rxid, uint8_t dcid[], uint8_t scid[]); + void l2cap_disconnection_response(uint8_t rxid, uint8_t dcid[], uint8_t scid[]); + + /* HID Commands */ + void HID_Command(uint8_t* data, uint16_t nbytes); + void HIDMove_Command(uint8_t* data, uint16_t nbytes); + void enable_sixaxis();//Command used to enable the Dualshock 3 and Navigation controller to send data via USB +}; +#endif \ No newline at end of file diff --git a/PS3BTREADME b/PS3BTREADME new file mode 100644 index 00000000..ab330ada --- /dev/null +++ b/PS3BTREADME @@ -0,0 +1,24 @@ +The PS3BT.cpp PS3BT.h was developed by Kristian Lauszus + +If it doesn't work then try comment "nak_limit--;" in "SetAddress()" in "Usb.cpp", as this caused some problems. + +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. + +All three PS3 Controllers are supported (Dualshock 3-, Navigation-, and Motioncontroller). +They communicate with the Arduino via Bluetooth using the USB Host Shield from http://www.circuitsathome.com/ + +A special thanks go to the following people: +"Richard Ibbotson" who made this guide: http://www.circuitsathome.com/mcu/ps3-and-wiimote-game-controllers-on-the-arduino-host-shield-part-1 +- It inspired me to get starting and had a lot of good information for the USB communication + +"Tomoyuki Tanaka" for releasing his code for the Arduino USB Host shield connected to the wiimote: http://www.circuitsathome.com/mcu/rc-car-controlled-by-wii-remote-on-arduino +- It helped me a lot to see the structure of the bluetooth communication + +Also I would like to thank all the people behind these sites about the Motion controller: +http://thp.io/2010/psmove/ +http://www.copenhagengamecollective.org/unimove/ +https://github.com/thp/psmoveapi +http://code.google.com/p/moveonpc/ + +And at last I would like to thank Oleg from http://www.circuitsathome.com/ for making such an awesome shield! \ No newline at end of file diff --git a/examples/PS3BT/PS3BT.ino b/examples/PS3BT/PS3BT.ino new file mode 100644 index 00000000..4841e263 --- /dev/null +++ b/examples/PS3BT/PS3BT.ino @@ -0,0 +1,206 @@ +#include +USB Usb; +PS3BT BT(&Usb); + +boolean printTemperature; + +void setup() +{ + Serial.begin(115200); + + if (Usb.Init() == -1) + { + Notify(PSTR("\r\nOSC did not start")); + while(1); //halt + } + Notify(PSTR("\r\nPS3 Bluetooth Library Started")); +} +void loop() +{ + Usb.Task(); + + if(BT.PS3BTConnected || BT.PS3NavigationBTConnected) + { + if(BT.getAnalogHat(LeftHatX) > 137 || BT.getAnalogHat(LeftHatX) < 117) + { + Notify(PSTR("\r\nLeftHatX: ")); + Serial.print(BT.getAnalogHat(LeftHatX), DEC); + } + if(BT.getAnalogHat(LeftHatY) > 137 || BT.getAnalogHat(LeftHatY) < 117) + { + Notify(PSTR("\r\nLeftHatY: ")); + Serial.print(BT.getAnalogHat(LeftHatY), DEC); + } + if(BT.getAnalogHat(RightHatX) > 137 || BT.getAnalogHat(RightHatX) < 117) + { + Notify(PSTR("\r\nRightHatX: ")); + Serial.print(BT.getAnalogHat(RightHatX), DEC); + } + if(BT.getAnalogHat(RightHatY) > 137 || BT.getAnalogHat(RightHatY) < 117) + { + Notify(PSTR("\r\nRightHatY: ")); + Serial.print(BT.getAnalogHat(RightHatY), DEC); + } + + //Analog button values can be read from almost all buttons + if(BT.getAnalogButton(L2_ANALOG) > 0) + { + Notify(PSTR("\r\nL2: ")); + Serial.print(BT.getAnalogButton(L2_ANALOG), DEC); + } + if(BT.getAnalogButton(R2_ANALOG) > 0) + { + Notify(PSTR("\r\nR2: ")); + Serial.print(BT.getAnalogButton(R2_ANALOG), DEC); + } + + if(BT.ButtonPressed) + { + Notify(PSTR("\r\nPS3 Controller")); + + if(BT.getButton(PS)) + { + Notify(PSTR(" - PS")); + BT.disconnect(); + } + else + { + if(BT.getButton(TRIANGLE)) + Notify(PSTR(" - Traingle")); + if(BT.getButton(CIRCLE)) + Notify(PSTR(" - Circle")); + if(BT.getButton(CROSS)) + Notify(PSTR(" - Cross")); + if(BT.getButton(SQUARE)) + Notify(PSTR(" - Square")); + + if(BT.getButton(UP)) + { + Notify(PSTR(" - Up")); + BT.setAllOff(); + BT.setLedOn(LED4); + } + if(BT.getButton(RIGHT)) + { + Notify(PSTR(" - Right")); + BT.setAllOff(); + BT.setLedOn(LED1); + } + if(BT.getButton(DOWN)) + { + Notify(PSTR(" - Down")); + BT.setAllOff(); + BT.setLedOn(LED2); + } + if(BT.getButton(LEFT)) + { + Notify(PSTR(" - Left")); + BT.setAllOff(); + BT.setLedOn(LED3); + } + + if(BT.getButton(L1)) + Notify(PSTR(" - L1")); + //if(BT.getButton(L2)) + //Notify(PSTR(" - L2")); + if(BT.getButton(L3)) + Notify(PSTR(" - L3")); + if(BT.getButton(R1)) + Notify(PSTR(" - R1")); + //if(BT.getButton(R2)) + //Notify(PSTR(" - R2")); + if(BT.getButton(R3)) + Notify(PSTR(" - R3")); + + if(BT.getButton(SELECT)) + { + Notify(PSTR(" - Select - ")); + Serial.print(BT.getStatusString()); + } + if(BT.getButton(START)) + Notify(PSTR(" - Start")); + } + } + } + else if(BT.PS3MoveBTConnected) + { + if(BT.getAnalogButton(T_MOVE_ANALOG) > 0) + { + Notify(PSTR("\r\nT: ")); + Serial.print(BT.getAnalogButton(T_MOVE_ANALOG), DEC); + } + if(BT.ButtonPressed) + { + Notify(PSTR("\r\nPS3 Move Controller")); + + if(BT.getButton(PS_MOVE)) + { + Notify(PSTR(" - PS")); + BT.disconnect(); + } + else + { + if(BT.getButton(SELECT_MOVE)) + { + Notify(PSTR(" - Select")); + printTemperature = false; + } + if(BT.getButton(START_MOVE)) + { + Notify(PSTR(" - Start")); + printTemperature = true; + } + if(BT.getButton(TRIANGLE_MOVE)) + { + Notify(PSTR(" - Triangle")); + BT.moveSetBulb(Red); + } + if(BT.getButton(CIRCLE_MOVE)) + { + Notify(PSTR(" - Circle")); + BT.moveSetBulb(Green); + } + if(BT.getButton(SQUARE_MOVE)) + { + Notify(PSTR(" - Square")); + BT.moveSetBulb(Blue); + } + if(BT.getButton(CROSS_MOVE)) + { + Notify(PSTR(" - Cross")); + BT.moveSetBulb(Yellow); + } + if(BT.getButton(MOVE_MOVE)) + { + BT.moveSetBulb(Off); + Notify(PSTR(" - Move")); + Notify(PSTR(" - ")); + Serial.print(BT.getStatusString()); + } + //if(BT.getButton(T_MOVE)) + //Notify(PSTR(" - T")); + } + } + if(printTemperature) + { + String templow; + String temphigh; + String input = String(BT.getSensor(tempMove), DEC); + + if (input.length() > 3) + { + temphigh = input.substring(0, 2); + templow = input.substring(2); + } + else + { + temphigh = input.substring(0, 1); + templow = input.substring(1); + } + Notify(PSTR("\r\nTemperature: ")); + Serial.print(temphigh); + Notify(PSTR(".")); + Serial.print(templow); + } + } +} diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 00000000..c95dcdf5 --- /dev/null +++ b/keywords.txt @@ -0,0 +1,149 @@ +################################################ +# Syntax Coloring Map For PS3 Bluetooth Library +################################################ + +################################################ +# Datatypes (KEYWORD1) +################################################ + +PS3BT KEYWORD1 + +################################################ +# Methods and Functions (KEYWORD2) +################################################ +setBdaddr KEYWORD2 +setMoveBdaddr KEYWORD2 + +getButton KEYWORD2 +getAnalogButton KEYWORD2 +getAnalogHat KEYWORD2 +getSensor KEYWORD2 +getAngle KEYWORD2 +getStatus KEYWORD2 +getStatusString KEYWORD2 +disconnect KEYWORD2 + +setAllOff KEYWORD2 +setRumbleOff KEYWORD2 +setRumbleOn KEYWORD2 +setLedOff KEYWORD2 +setLedOn KEYWORD2 +moveSetBulb KEYWORD2 +moveSetRumble KEYWORD2 + +PS3BTConnected KEYWORD2 +PS3MoveBTConnected KEYWORD2 +PS3NavigationBTConnected KEYWORD2 +ButtonChanged KEYWORD2 +ButtonPressed KEYWORD2 + +################################################ +# Constants and enums (LITERAL1) +################################################ +LED1 LITERAL1 +LED2 LITERAL1 +LED3 LITERAL1 +LED4 LITERAL1 +LED5 LITERAL1 +LED6 LITERAL1 +LED7 LITERAL1 +LED8 LITERAL1 +LED9 LITERAL1 +LED10 LITERAL1 + +Red LITERAL1 +Green LITERAL1 +Blue LITERAL1 +Yellow LITERAL1 +Lightblue LITERAL1 +Purble LITERAL1 +White LITERAL1 +Off LITERAL1 + +SELECT LITERAL1 +L3 LITERAL1 +R3 LITERAL1 +START LITERAL1 +UP LITERAL1 +RIGHT LITERAL1 +DOWN LITERAL1 +LEFT LITERAL1 +L2 LITERAL1 +R2 LITERAL1 +L1 LITERAL1 +R1 LITERAL1 +TRIANGLE LITERAL1 +CIRCLE LITERAL1 +CROSS LITERAL1 +SQUARE LITERAL1 +PS LITERAL1 +SELECT_MOVE LITERAL1 +START_MOVE LITERAL1 +TRIANGLE_MOVE LITERAL1 +CIRCLE_MOVE LITERAL1 +CROSS_MOVE LITERAL1 +SQUARE_MOVE LITERAL1 +PS_MOVE LITERAL1 +MOVE_MOVE LITERAL1 +T_MOVE 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_MOVE_ANALOG LITERAL1 + +LeftHatX LITERAL1 +LeftHatY LITERAL1 +RightHatX LITERAL1 +RightHatY LITERAL1 + +aX LITERAL1 +aY LITERAL1 +aZ LITERAL1 +gZ LITERAL1 +aXmove LITERAL1 +aYmove LITERAL1 +aZmove LITERAL1 +gXmove LITERAL1 +gYmove LITERAL1 +gZmove LITERAL1 +tempMove LITERAL1 +mXmove LITERAL1 +mZmove LITERAL1 +mYmove LITERAL1 + +Pitch LITERAL1 +Roll LITERAL1 + +Plugged LITERAL1 +Unplugged LITERAL1 +Charging LITERAL1 +NotCharging LITERAL1 +Shutdown LITERAL1 +Dying LITERAL1 +Low LITERAL1 +High LITERAL1 +Full LITERAL1 +MoveCharging LITERAL1 +MoveNotCharging LITERAL1 +MoveShutdown LITERAL1 +MoveDying LITERAL1 +MoveLow LITERAL1 +MoveHigh LITERAL1 +MoveFull LITERAL1 +CableRumble LITERAL1 +Cable LITERAL1 +BluetoothRumble LITERAL1 +Bluetooth LITERAL1 + +RumbleHigh LITERAL1 +RumbleLow LITERAL1 \ No newline at end of file