Athena: XBox 360 Pad and Mac OS X (cont)
In my previous entry, Athena: XBox 360 Pad and Mac OS X, I began work on getting the XBox 360 pad working in my game engine. I also said that I’d post how to change the LEDs and get rumble working when I figured it out.
I had no trouble with getting the LEDs flashing, but for some reason the rumble actuators just would not respond to my message with success.
I had searched at all other documentation on the pad on the internet, but although I found two variations of rumbling, neither worked.
It has taken my a week or two, but then I got the inspiration to just fire all possible messages to the pad, nullified apart from the header, and see responds with success. So I wrote some code to send 255 different type ids, with 255 different lengths.
//
//
void HIDInput::TestDevice( IOHIDDeviceRef inDeviceRef )
{
unsigned char reportData[255];
// Nullify Report
memset( reportData, 0, sizeof( unsigned char ) * 255 );
// Loop through most possible messages
for( unsigned char reportType = 0; reportType < 255; ++reportType )
{
for( unsigned char reportLength = 0; reportLength < 255; ++reportLength )
{
// Setup Header
reportData[0] = reportType;
reportData[1] = reportLength;
// Send Report to Pad
if( DeviceSendReport( inDeviceRef, reportType, reportData, reportLength ) )
{
printf( "Success - Type(%u), Length(%u)\n", reportType, reportLength );
}
else
{
printf( "Failed - Type(%u), Length(%u)\n", reportType, reportLength );
}
}
}
//
printf( "Finished\n" );
}
I’ll explain show the function body for DeviceSendReport in a minute. After running this, only two reports returned with success, one was the LED report, and the other was of Type 0 with length 4.
Since the header take two bytes, I guessed the last two must be Actuator A and Actuator B. And after setting one of them to 0xff, I successfully managed to get one of the actuators rumbling, and then the other actuator with the other byte.
Now for the code for sending reports to the pad.
//
//
bool HIDInput::DeviceSendReport( IOHIDDeviceRef inDeviceRef, const unsigned char inType, const unsigned char * inReportData, const unsigned char inReportSize )
{
return IOHIDDeviceSetReport( inDeviceRef, kIOHIDReportTypeOutput, inType, inReportData, inReportSize ) == kIOReturnSuccess;
}
As you can see it takes a parameter called inDeviceRef, this is the value that was passed to DeviceMatchingCallback in the previous entry. And now here is the code to get the LED flashing, there are a few status values for it, so I’ve created an enum with some comments explaining what each do.
//
//
enum LEDStatus
{
kLED_AllOff = 0x00, // All LEDs are turned off
kLED_AllBlink = 0x01, // All LEDs repeatedly blink on and off
kLED_P1_FlashOn = 0x02, // Player 1 LED flashes then stays on
kLED_P2_FlashOn = 0x03, // Player 2 LED flashes then stays on
kLED_P3_FlashOn = 0x04, // Player 3 LED flashes then stays on
kLED_P4_FlashOn = 0x05, // Player 4 LED flashes then stays on
kLED_P1_On = 0x06, // Player 1 LED turns on
kLED_P2_On = 0x07, // Player 2 LED turns on
kLED_P3_On = 0x08, // Player 3 LED turns on
kLED_P4_On = 0x09, // Player 4 LED turns on
kLED_Rotating = 0x0a, // LEDs flash on in sequence
kLED_CurrentBlink = 0x0b, // Currently active LED blinks
kLED_SlowBlink = 0x0c, // All LEDs blink slowly
kLED_Alternating = 0x0d, // Player 1 & 4 alternate blink with Player 2 & 3
};
//
//
void HIDInput::FlashLED( IOHIDDeviceRef inDeviceRef, const LEDStatus inStatus )
{
//
const unsigned char kReportType = 0x01;
const unsigned char kReportSize = 0x03;
const unsigned char kReportData[kReportSize] =
{
kReportType, kReportSize, static_cast<Core::byte>( inStatus )
};
//
DeviceSendReport( inDeviceRef, kReportType, kReportData, kReportSize );
}
As you can see, it is fairly simple to send a message, all you have to remember is that the first two bytes contain a header (one byte for type, and one type for the size of the report including the header size). And finally for rumbling the motors.
//
//
void HIDInput::SetRumble( IOHIDDeviceRef inDeviceRef, const unsigned char inBigSpeed, const unsigned char inLittleSpeed )
{
//
const unsigned char kReportType = 0x00;
const unsigned char kReportSize = 0x04;
const unsigned char kReportData[kReportSize] =
{
kReportType, kReportSize, inBigSpeed, inLittleSpeed
};
//
DeviceSendReport( inDeviceRef, kReportType, kReportData, kReportSize );
}
I had some minor problems with the rumble not changing after the first call to SetRumble originally, however it turned out that I had just accidently set the report data variable to be static, so the initialisation was only setup with the first values passed.
Other Rumble Reports
As I mentioned before, I’ve found other message formats for rumbling the motors, I don’t know if its a version thing, or something, but here are the other two I’ve found if you have trouble with mine.
Version 1 (Tattiebogle)
//
//
void HIDInput::SetRumble( IOHIDDeviceRef inDeviceRef, const unsigned char inBigSpeed, const unsigned char inLittleSpeed )
{
//
const unsigned char kReportType = 0x00;
const unsigned char kReportSize = 0x08;
const unsigned char kReportData[kReportSize] =
{
kReportType, kReportSize, 0x00, inBigSpeed, inLittleSpeed, 0x00, 0x00, 0x00
};
//
DeviceSendReport( inDeviceRef, kReportType, kReportData, kReportSize );
}
Version 2 (http://euc.jp/periphs/xbox-controller.ja.html)
//
//
void HIDInput::SetRumble( IOHIDDeviceRef inDeviceRef, const unsigned char inBigSpeed, const unsigned char inLittleSpeed )
{
//
const unsigned char kReportType = 0x00;
const unsigned char kReportSize = 0x06;
const unsigned char kReportData[kReportSize] =
{
kReportType, kReportSize, 0x00, inBigSpeed, 0x00, inLittleSpeed
};
//
DeviceSendReport( inDeviceRef, kReportType, kReportData, kReportSize );
}
No Comments »
RSS feed for comments on this post. TrackBack URL
Leave a comment
You must be logged in to post a comment.