getBatteryLevel and some other improvement by timstamp.co.uk

This commit is contained in:
Kristian Sloth Lauszus 2013-01-06 03:43:03 +01:00
parent 33698b0091
commit 15d8cf1660
2 changed files with 132 additions and 85 deletions

View file

@ -13,6 +13,8 @@
Kristian Lauszus, TKJ Electronics Kristian Lauszus, TKJ Electronics
Web : http://www.tkjelectronics.com Web : http://www.tkjelectronics.com
e-mail : kristianl@tkjelectronics.com e-mail : kristianl@tkjelectronics.com
getBatteryLevel and checkStatus functions made by timstamp.co.uk found using BusHound from Perisoft.net
*/ */
#include "XBOXRECV.h" #include "XBOXRECV.h"
@ -252,7 +254,7 @@ Fail:
uint8_t XBOXRECV::Release() { uint8_t XBOXRECV::Release() {
XboxReceiverConnected = false; XboxReceiverConnected = false;
for(uint8_t i=0;i<4;i++) for(uint8_t i=0;i<4;i++)
Xbox360Connected[i] = false; Xbox360Connected[i] = 0x00;
pUsb->GetAddressPool().FreeAddress(bAddress); pUsb->GetAddressPool().FreeAddress(bAddress);
bAddress = 0; bAddress = 0;
bPollEnable = false; bPollEnable = false;
@ -261,33 +263,30 @@ uint8_t XBOXRECV::Release() {
uint8_t XBOXRECV::Poll() { uint8_t XBOXRECV::Poll() {
if (!bPollEnable) if (!bPollEnable)
return 0; return 0;
for(uint8_t i=0;i<4;i++) { if(!timer || ((millis() - timer) > 3000)) { // Run checkStatus every 3 seconds
uint16_t BUFFER_SIZE = EP_MAXPKTSIZE; timer = millis();
checkStatus();
}
uint8_t inputPipe;
uint16_t bufferSize;
for(uint8_t i=0;i<4;i++) {
switch (i) { switch (i) {
case 0: case 0: inputPipe = XBOX_INPUT_PIPE_1; break;
pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE_1 ].epAddr, &BUFFER_SIZE, readBuf); case 1: inputPipe = XBOX_INPUT_PIPE_2; break;
break; case 2: inputPipe = XBOX_INPUT_PIPE_3; break;
case 1: case 3: inputPipe = XBOX_INPUT_PIPE_4; break;
pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE_2 ].epAddr, &BUFFER_SIZE, readBuf);
break;
case 2:
pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE_3 ].epAddr, &BUFFER_SIZE, readBuf);
break;
case 3:
pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE_4 ].epAddr, &BUFFER_SIZE, readBuf);
break;
default:
break;
} }
if(BUFFER_SIZE > 0) { bufferSize = EP_MAXPKTSIZE; // This is the maximum number of bytes we want to receive
pUsb->inTransfer(bAddress, epInfo[ inputPipe ].epAddr, &bufferSize, readBuf);
if(bufferSize > 0) { // The number of received bytes
#ifdef EXTRADEBUG #ifdef EXTRADEBUG
Notify(PSTR("Bytes Received: ")); Notify(PSTR("Bytes Received: "));
Serial.print(BUFFER_SIZE); Serial.print(bufferSize);
Notify(PSTR("\r\n")); Notify(PSTR("\r\n"));
#endif #endif
readReport(i); readReport(i);
#ifdef PRINTREPORT #ifdef PRINTREPORT
printReport(i,BUFFER_SIZE); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller printReport(i,bufferSize); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
#endif #endif
} }
} }
@ -297,30 +296,32 @@ uint8_t XBOXRECV::Poll() {
void XBOXRECV::readReport(uint8_t controller) { void XBOXRECV::readReport(uint8_t controller) {
if (readBuf == NULL) if (readBuf == NULL)
return; return;
if(readBuf[0] == 0x08) { // This report is send when a controller is connected and disconnected // This report is send when a controller is connected and disconnected
Xbox360Connected[controller] = (bool)(readBuf[1] == 0x80); if(readBuf[0] == 0x08 && readBuf[1] != Xbox360Connected[controller]) {
Xbox360Connected[controller] = readBuf[1];
#ifdef DEBUG #ifdef DEBUG
Notify(PSTR("Controller ")); Notify(PSTR("Controller "));
Serial.print(controller); Serial.print(controller);
#endif #endif
if(Xbox360Connected[controller]) { if(Xbox360Connected[controller]) {
#ifdef DEBUG #ifdef DEBUG
Notify(PSTR(": connected\r\n")); char* str = 0;
switch(readBuf[1]) {
case 0x80: str = PSTR(" as controller\r\n"); break;
case 0x40: str = PSTR(" as headset\r\n"); break;
case 0xC0: str = PSTR(" as controller+headset\r\n"); break;
}
Notify(PSTR(": connected"));
Notify(str);
#endif #endif
LED led;
switch (controller) { switch (controller) {
case 0: case 0: led = LED1; break;
setLedOn(controller,LED1); case 1: led = LED2; break;
break; case 2: led = LED3; break;
case 1: case 3: led = LED4; break;
setLedOn(controller,LED2);
break;
case 2:
setLedOn(controller,LED3);
break;
case 3:
setLedOn(controller,LED4);
break;
} }
setLedOn(controller,led);
} }
#ifdef DEBUG #ifdef DEBUG
else else
@ -328,17 +329,24 @@ void XBOXRECV::readReport(uint8_t controller) {
#endif #endif
return; return;
} }
// Controller status report
if(readBuf[1] == 0x00 && readBuf[3] & 0x13 && readBuf[4] >= 0x22) {
controllerStatus[controller] = ((uint16_t)readBuf[3] << 8) | readBuf[4];
return;
}
if(readBuf[1] != 0x01) // Check if it's the correct report - the receiver also sends different status reports if(readBuf[1] != 0x01) // Check if it's the correct report - the receiver also sends different status reports
return; return;
Xbox360Connected[controller] = true; // A controller must be connected if it's sending data // A controller must be connected if it's sending data
if(!Xbox360Connected[controller])
Xbox360Connected[controller] |= 0x80;
ButtonState[controller] = (uint32_t)(readBuf[9] | ((uint16_t)readBuf[8] << 8) | ((uint32_t)readBuf[7] << 16) | ((uint32_t)readBuf[6] << 24)); ButtonState[controller] = (uint32_t)(readBuf[9] | ((uint16_t)readBuf[8] << 8) | ((uint32_t)readBuf[7] << 16) | ((uint32_t)readBuf[6] << 24));
hatValue[controller][0] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]); hatValue[controller][LeftHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);
hatValue[controller][1] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]); hatValue[controller][LeftHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
hatValue[controller][2] = (int16_t)(((uint16_t)readBuf[15] << 8) | readBuf[14]); hatValue[controller][RightHatX] = (int16_t)(((uint16_t)readBuf[15] << 8) | readBuf[14]);
hatValue[controller][3] = (int16_t)(((uint16_t)readBuf[17] << 8) | readBuf[16]); hatValue[controller][RightHatY] = (int16_t)(((uint16_t)readBuf[17] << 8) | readBuf[16]);
//Notify(PSTR("\r\nButtonState: ")); //Notify(PSTR("\r\nButtonState: "));
//PrintHex<uint32_t>(ButtonState[controller]); //PrintHex<uint32_t>(ButtonState[controller]);
@ -396,25 +404,48 @@ bool XBOXRECV::getButtonClick(uint8_t controller, Button b) {
int16_t XBOXRECV::getAnalogHat(uint8_t controller, AnalogHat a) { int16_t XBOXRECV::getAnalogHat(uint8_t controller, AnalogHat a) {
return hatValue[controller][a]; return hatValue[controller][a];
} }
/*
ControllerStatus Breakdown
ControllerStatus[controller] & 0x0001 // 0
ControllerStatus[controller] & 0x0002 // normal batteries, no rechargeable battery pack
ControllerStatus[controller] & 0x0004 // controller starting up / settling
ControllerStatus[controller] & 0x0008 // headset adapter plugged in, but no headphones connected (mute?)
ControllerStatus[controller] & 0x0010 // 0
ControllerStatus[controller] & 0x0020 // 1
ControllerStatus[controller] & 0x0040 // battery level (high bit)
ControllerStatus[controller] & 0x0080 // battery level (low bit)
ControllerStatus[controller] & 0x0100 // 1
ControllerStatus[controller] & 0x0200 // 1
ControllerStatus[controller] & 0x0400 // headset adapter plugged in
ControllerStatus[controller] & 0x0800 // 0
ControllerStatus[controller] & 0x1000 // 1
ControllerStatus[controller] & 0x2000 // 0
ControllerStatus[controller] & 0x4000 // 0
ControllerStatus[controller] & 0x8000 // 0
*/
uint8_t XBOXRECV::getBatteryLevel(uint8_t controller) {
if(((controllerStatus[controller] & 0x00C0) >> 6) == 0x3)
return 100;
else if(((controllerStatus[controller] & 0x00C0) >> 6) == 0x2)
return 67;
else if(((controllerStatus[controller] & 0x00C0) >> 6) == 0x1)
return 33;
else if(((controllerStatus[controller] & 0x00C0) >> 6) == 0x0)
return 0;
else
return -1;
}
void XBOXRECV::XboxCommand(uint8_t controller, uint8_t* data, uint16_t nbytes) { void XBOXRECV::XboxCommand(uint8_t controller, uint8_t* data, uint16_t nbytes) {
uint8_t rcode; uint8_t rcode;
uint8_t outputPipe;
switch (controller) { switch (controller) {
case 0: case 0: outputPipe = XBOX_OUTPUT_PIPE_1; break;
rcode = pUsb->outTransfer(bAddress, epInfo[ XBOX_OUTPUT_PIPE_1 ].epAddr, nbytes, data); case 1: outputPipe = XBOX_OUTPUT_PIPE_2; break;
break; case 2: outputPipe = XBOX_OUTPUT_PIPE_3; break;
case 1: case 3: outputPipe = XBOX_OUTPUT_PIPE_4; break;
rcode = pUsb->outTransfer(bAddress, epInfo[ XBOX_OUTPUT_PIPE_2 ].epAddr, nbytes, data);
break;
case 2:
rcode = pUsb->outTransfer(bAddress, epInfo[ XBOX_OUTPUT_PIPE_3 ].epAddr, nbytes, data);
break;
case 3:
rcode = pUsb->outTransfer(bAddress, epInfo[ XBOX_OUTPUT_PIPE_4 ].epAddr, nbytes, data);
break;
default:
break;
} }
rcode = pUsb->outTransfer(bAddress, epInfo[ outputPipe ].epAddr, nbytes, data);
#ifdef EXTRADEBUG #ifdef EXTRADEBUG
if(rcode) if(rcode)
Notify(PSTR("Error sending Xbox message\r\n")); Notify(PSTR("Error sending Xbox message\r\n"));
@ -424,32 +455,46 @@ void XBOXRECV::setLedRaw(uint8_t controller, uint8_t value) {
writeBuf[0] = 0x00; writeBuf[0] = 0x00;
writeBuf[1] = 0x00; writeBuf[1] = 0x00;
writeBuf[2] = 0x08; writeBuf[2] = 0x08;
writeBuf[3] = value+0x40; writeBuf[3] = value | 0x40;
writeBuf[4] = 0x00;
writeBuf[5] = 0x00;
writeBuf[6] = 0x00;
writeBuf[7] = 0x00;
writeBuf[8] = 0x00;
writeBuf[9] = 0x00;
writeBuf[10] = 0x00;
writeBuf[11] = 0x00;
for(uint8_t i=0;i<10;i++) { // This small hack is needed for some reason as the controller doesn't always respond to the command XboxCommand(controller, writeBuf, 4);
XboxCommand(controller, writeBuf, 12); }
delay(1); void XBOXRECV::setLedOn(uint8_t controller, LED led) {
if(led != ALL) // All LEDs can't be on a the same time
setLedRaw(controller,((uint8_t)led)+4);
}
void XBOXRECV::setLedBlink(uint8_t controller, LED led) {
setLedRaw(controller,(uint8_t)led);
}
void XBOXRECV::setLedMode(uint8_t controller, LEDMode ledMode) { // This function is used to do some speciel LED stuff the controller supports
setLedRaw(controller,(uint8_t)ledMode);
}
/* PC runs this at interval of approx 2 seconds
Thanks to BusHound from Perisoft.net for the Windows USB Analysis output
Found by timstamp.co.uk
*/
void XBOXRECV::checkStatus() {
if(!bPollEnable)
return;
// Get controller info
writeBuf[0] = 0x08;
writeBuf[1] = 0x00;
writeBuf[2] = 0x0f;
writeBuf[3] = 0xc0;
for(uint8_t i=0; i<4; i++) {
XboxCommand(i, writeBuf, 4);
}
// Get battery status
writeBuf[0] = 0x00;
writeBuf[1] = 0x00;
writeBuf[2] = 0x00;
writeBuf[3] = 0x40;
for(uint8_t i=0; i<4; i++) {
if(Xbox360Connected[i])
XboxCommand(i, writeBuf, 4);
} }
} }
void XBOXRECV::setLedOn(uint8_t controller, LED l) {
if(l == ALL) // All LEDs can't be on a the same time
return;
setLedRaw(controller,((uint8_t)l)+4);
}
void XBOXRECV::setLedBlink(uint8_t controller, LED l) {
setLedRaw(controller,(uint8_t)l);
}
void XBOXRECV::setLedMode(uint8_t controller, LEDMode lm) { // This function is used to do some speciel LED stuff the controller supports
setLedRaw(controller,(uint8_t)lm);
}
void XBOXRECV::setRumbleOn(uint8_t controller, uint8_t lValue, uint8_t rValue) { void XBOXRECV::setRumbleOn(uint8_t controller, uint8_t lValue, uint8_t rValue) {
writeBuf[0] = 0x00; writeBuf[0] = 0x00;
writeBuf[1] = 0x01; writeBuf[1] = 0x01;
@ -458,11 +503,6 @@ void XBOXRECV::setRumbleOn(uint8_t controller, uint8_t lValue, uint8_t rValue) {
writeBuf[4] = 0x00; writeBuf[4] = 0x00;
writeBuf[5] = lValue; // big weight writeBuf[5] = lValue; // big weight
writeBuf[6] = rValue; // small weight writeBuf[6] = rValue; // small weight
writeBuf[7] = 0x00;
writeBuf[8] = 0x00;
writeBuf[9] = 0x00;
writeBuf[10] = 0x00;
writeBuf[11] = 0x00;
XboxCommand(controller, writeBuf, 12); XboxCommand(controller, writeBuf, 7);
} }

View file

@ -13,6 +13,8 @@
Kristian Lauszus, TKJ Electronics Kristian Lauszus, TKJ Electronics
Web : http://www.tkjelectronics.com Web : http://www.tkjelectronics.com
e-mail : kristianl@tkjelectronics.com e-mail : kristianl@tkjelectronics.com
getBatteryLevel and checkStatus functions made by timstamp.co.uk found using BusHound from Perisoft.net
*/ */
#ifndef _xboxrecv_h_ #ifndef _xboxrecv_h_
@ -129,9 +131,10 @@ public:
void setLedOn(uint8_t controller, LED l); void setLedOn(uint8_t controller, LED l);
void setLedBlink(uint8_t controller, LED l); void setLedBlink(uint8_t controller, LED l);
void setLedMode(uint8_t controller, LEDMode lm); void setLedMode(uint8_t controller, LEDMode lm);
uint8_t getBatteryLevel(uint8_t controller); // Returns the battery level in percentage in 25% steps
bool XboxReceiverConnected; // True if a wireless receiver is connected bool XboxReceiverConnected; // True if a wireless receiver is connected
bool Xbox360Connected[4]; // Variable used to indicate if the XBOX 360 controller is successfully connected uint8_t Xbox360Connected[4]; // Variable used to indicate if the XBOX 360 controller is successfully connected
protected: protected:
/* Mandatory members */ /* Mandatory members */
@ -147,9 +150,12 @@ private:
uint32_t OldButtonState[4]; uint32_t OldButtonState[4];
uint16_t ButtonClickState[4]; uint16_t ButtonClickState[4];
int16_t hatValue[4][4]; int16_t hatValue[4][4];
uint16_t controllerStatus[4];
bool L2Clicked[4]; // These buttons are analog, so we use we use these bools to check if they where clicked or not bool L2Clicked[4]; // These buttons are analog, so we use we use these bools to check if they where clicked or not
bool R2Clicked[4]; bool R2Clicked[4];
unsigned long timer; // Timing for checkStatus() signals
uint8_t readBuf[EP_MAXPKTSIZE]; // General purpose buffer for input data uint8_t readBuf[EP_MAXPKTSIZE]; // General purpose buffer for input data
uint8_t writeBuf[EP_MAXPKTSIZE]; // General purpose buffer for output data uint8_t writeBuf[EP_MAXPKTSIZE]; // General purpose buffer for output data
@ -159,5 +165,6 @@ private:
/* Private commands */ /* Private commands */
void XboxCommand(uint8_t controller, uint8_t* data, uint16_t nbytes); void XboxCommand(uint8_t controller, uint8_t* data, uint16_t nbytes);
void checkStatus();
}; };
#endif #endif