Skip to content

Instantly share code, notes, and snippets.

@SenpaiSilver
Created May 13, 2015 02:20
Show Gist options
  • Save SenpaiSilver/d3aa26c6f42dec8d191a to your computer and use it in GitHub Desktop.
Save SenpaiSilver/d3aa26c6f42dec8d191a to your computer and use it in GitHub Desktop.
Joystick Emulation for Leonardo
//#define RAWHID_ENABLED
#define JOYHID_ENABLED
Joystick_ Joystick;
const u8 _hidReportDescriptor[] = {
// Mouse
0x05, 0x01, // USAGE_PAGE (Generic Desktop) // 54
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x85, 0x01, // REPORT_ID (1)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x03, // USAGE_MAXIMUM (Button 3)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x03, // REPORT_COUNT (3)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x05, // REPORT_SIZE (5)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x09, 0x38, // USAGE (Wheel)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x03, // REPORT_COUNT (3)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xc0, // END_COLLECTION
0xc0, // END_COLLECTION
// Keyboard
0x05, 0x01, // USAGE_PAGE (Generic Desktop) // 47
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x02, // REPORT_ID (2)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x95, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0, // END_COLLECTION
#ifdef RAWHID_ENABLED
// RAW HID
0x06, LSB(RAWHID_USAGE_PAGE), MSB(RAWHID_USAGE_PAGE), // 30
0x0A, LSB(RAWHID_USAGE), MSB(RAWHID_USAGE),
0xA1, 0x01, // Collection 0x01
0x85, 0x03, // REPORT_ID (3)
0x75, 0x08, // report size = 8 bits
0x15, 0x00, // logical minimum = 0
0x26, 0xFF, 0x00, // logical maximum = 255
0x95, 64, // report count TX
0x09, 0x01, // usage
0x81, 0x02, // Input (array)
0x95, 64, // report count RX
0x09, 0x02, // usage
0x91, 0x02, // Output (array)
0xC0 // end collection
#endif
// *** Here is where the RAW_HID has been converted to a Joystick device
// *** Inspired by helmpcb.com/electronics/usb-joystick
// *** Check out www.usb.org/developers/hidpage/ for more than you'll ever need to know about USB HID
// *** HID descriptor created using the HID descriptor tool from www.usb.org/developers/hidpage/dt2_4.zip (win32)
#ifdef JOYHID_ENABLED
// 32 buttons (and a throttle - just in case the game doesn't recognise a joystick with no analog axis)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x04, // USAGE (Joystick)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x03, // REPORT_ID (3) (This is important when HID_SendReport() is called)
//Buttons:
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x20, // USAGE_MAXIMUM (Button 32)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x20, // REPORT_COUNT (32)
0x55, 0x00, // UNIT_EXPONENT (0)
0x65, 0x00, // UNIT (None)
0x81, 0x02, // INPUT (Data,Var,Abs)
// 8 bit Throttle and Steering
0x05, 0x02, // USAGE_PAGE (Simulation Controls)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0xA1, 0x00, // COLLECTION (Physical)
0x09, 0xBB, // USAGE (Throttle)
0x09, 0xBA, // USAGE (Steering)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
// Two Hat switches
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x39, // USAGE (Hat switch)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x07, // LOGICAL_MAXIMUM (7)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0x3B, 0x01, // PHYSICAL_MAXIMUM (315)
0x65, 0x14, // UNIT (Eng Rot:Angular Pos)
0x75, 0x04, // REPORT_SIZE (4)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x39, // USAGE (Hat switch)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x07, // LOGICAL_MAXIMUM (7)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0x3B, 0x01, // PHYSICAL_MAXIMUM (315)
0x65, 0x14, // UNIT (Eng Rot:Angular Pos)
0x75, 0x04, // REPORT_SIZE (4)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x09, 0x01, // USAGE (Pointer)
0xA1, 0x00, // COLLECTION (Physical)
0x09, 0x30, // USAGE (x)
0x09, 0x31, // USAGE (y)
0x09, 0x32, // USAGE (z)
0x09, 0x33, // USAGE (rx)
0x09, 0x34, // USAGE (ry)
0x09, 0x35, // USAGE (rz)
0x95, 0x06, // REPORT_COUNT (2)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
#endif
};
//================================================================================
//================================================================================
// Joystick
// Usage: Joystick.move(inputs go here)
//
// The report data format must match the one defined in the descriptor exactly
// or it either won't work, or the pc will make a mess of unpacking the data
//
Joystick_::Joystick_()
{
}
#define joyBytes 13 // should be equivalent to sizeof(JoyState_t)
void Joystick_::setState(JoyState_t *joySt)
{
uint8_t data[joyBytes];
uint32_t buttonTmp;
buttonTmp = joySt->buttons;
data[0] = buttonTmp & 0xFF; // Break 32 bit button-state out into 4 bytes, to send over USB
buttonTmp >>= 8;
data[1] = buttonTmp & 0xFF;
buttonTmp >>= 8;
data[2] = buttonTmp & 0xFF;
buttonTmp >>= 8;
data[3] = buttonTmp & 0xFF;
data[4] = joySt->throttle; // Throttle
data[5] = joySt->rudder; // Steering
data[6] = (joySt->hatSw2 << 4) | joySt->hatSw1; // Pack hat-switch states into a single byte
data[7] = joySt->xAxis; // X axis
data[8] = joySt->yAxis; // Y axis
data[9] = joySt->zAxis; // Z axis
data[10] = joySt->xRotAxis; // rX axis
data[11] = joySt->yRotAxis; // rY axis
data[12] = joySt->zRotAxis; // rZ axis
//HID_SendReport(Report number, array of values in same order as HID descriptor, length)
HID_SendReport(3, data, joyBytes);
// The joystick is specified as using report 3 in the descriptor. That's where the "3" comes from
}
JoyState_t joySt;
void setup() {
// put your setup code here, to run once:
pinMode(13, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
joySt.xAxis = random(255);
joySt.yAxis = random(255);
joySt.zAxis = random(255);
joySt.xRotAxis = random(255);
joySt.yRotAxis = random(255);
joySt.zRotAxis = random(255);
//joySt.throttle = random(255);
joySt.rudder = random(255);
joySt.throttle++;
joySt.buttons <<= 1;
if (joySt.buttons == 0)
joySt.buttons = 1;
joySt.hatSw1++;
joySt.hatSw2--;
if (joySt.hatSw1 > 8)
joySt.hatSw1 = 0;
if (joySt.hatSw2 > 8)
joySt.hatSw2 = 8;
delay(100);
if (joySt.throttle > 127)
digitalWrite(13, HIGH);
else
digitalWrite(13, LOW);
// Call Joystick.move
Joystick.setState(&joySt);
}
//================================================================================
//================================================================================
// Joystick
// Implemented in HID.cpp
// The list of parameters here needs to match the implementation in HID.cpp
typedef struct JoyState // Pretty self explanitory. Simple state to store all the joystick parameters
{
uint8_t xAxis;
uint8_t yAxis;
uint8_t zAxis;
uint8_t xRotAxis;
uint8_t yRotAxis;
uint8_t zRotAxis;
uint8_t throttle;
uint8_t rudder;
uint8_t hatSw1;
uint8_t hatSw2;
uint32_t buttons; // 32 general buttons
} JoyState_t;
class Joystick_
{
public:
Joystick_();
void setState(JoyState_t *joySt);
};
extern Joystick_ Joystick;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment