Wednesday, November 7, 2012

Arduino HID Gamepad, Part 2

Well, I'm finally getting around to moving forward with my Arduino HID Gamepad project (part 1 here).  I finished the wiring on the gamepad shield, and started trying to get the gamepad device report descriptor and event handlers added to the Arduino libraries.  First of all, you need to add the device report descriptor to HID.cpp, located in hardware\arduino\cores\arduino.  Sorry, I can't explain this very well, I barely understand it myself, but this page helped me stumble through.  Add the following to const u8 _hidReportDescriptor[] (near the top of the .cpp file), above the #if RAWHID_ENABLED directive:


    // Gamepad
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x05,                    // USAGE (Game Pad)
    0xa1, 0x01,                    // COLLECTION (Application)
    0xa1, 0x00,                    //   COLLECTION (Physical)
    0x85, 0x03,                    //     REPORT_ID (3)
    0x05, 0x09,                    //     USAGE_PAGE (Button)
    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
    0x29, 0x08,                    //     USAGE_MAXIMUM (Button 8)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x95, 0x04,                    //     REPORT_COUNT (4)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
    0x09, 0x30,                    //     USAGE (X)
    0x09, 0x31,                    //     USAGE (Y)
    0x15, 0xff,                    //     LOGICAL_MINIMUM (-1)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x95, 0x02,                    //     REPORT_COUNT (2)
    0x75, 0x02,                    //     REPORT_SIZE (2)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0xc0,                          //   END_COLLECTION
    0xc0                           // END_COLLECTION


This defines a 2-axis, 4-button gamepad.  Next, you need to add the Gamepad class and event handlers to USBAPI.h:


class Gamepad_
{
private:
uint8_t _buttons;
public:
Gamepad_(void);
void begin(void);
void end(void);
void press(uint8_t b);
void release(uint8_t b);
bool isPressed(uint8_t b);
};
extern Gamepad_ Gamepad;


Now you need to put the class definitions in HID.cpp:


//================================================================================
//================================================================================
// Gamepad

Gamepad_::Gamepad_(void) : _buttons(0)
{
}

void Gamepad_::begin(void)
{
}

void Gamepad_::end(void)
{
}

void Gamepad_::press(uint8_t b)
{
_buttons |= b;
HID_SendReport(3,&_buttons,1);
}

void Gamepad_::release(uint8_t b)
{
_buttons &= ~b;
HID_SendReport(3,&_buttons,1);
}

bool Gamepad_::isPressed(uint8_t b)
{
if ((b & _buttons) > 0) 
return true;
return false;
}


Finally, you need to instantiate the Gamepad singleton.  At the top of HID.cpp, add the following line under the Mouse and Keyboard instantiations:

Gamepad_ Gamepad;

Now, you should be able to call Gamepad.begin() in your sketch setup() function, and then call Gamepad.press() and Gamepad.release() to send button press and release reports.  As you can see, Windows now recognizes my new 2-axis, 4-button gamepad :)