Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add USB Joystick / Game Controller library to Arduino Leonardo and Arduino Micro #2843

Closed
wants to merge 10 commits into from
236 changes: 235 additions & 1 deletion hardware/arduino/avr/cores/arduino/HID.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@

//#define RAWHID_ENABLED

// Singletons for mouse and keyboard
// Singletons for mouse, keyboard, and joystick

Mouse_ Mouse;
Keyboard_ Keyboard;
Joystick_ Joystick;

//================================================================================
//================================================================================
Expand All @@ -40,6 +41,7 @@ Keyboard_ Keyboard;
#define RAWHID_USAGE 0x0C00
#define RAWHID_TX_SIZE 64
#define RAWHID_RX_SIZE 64
#define JOYSTICK_REPORT_ID 0x03

extern const u8 _hidReportDescriptor[] PROGMEM;
const u8 _hidReportDescriptor[] = {
Expand Down Expand Up @@ -123,6 +125,89 @@ const u8 _hidReportDescriptor[] = {
0x09, 0x02, // usage
0x91, 0x02, // Output (array)
0xC0 // end collection
#else
// Joystick
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x04, // USAGE (Joystick)
0xa1, 0x01, // COLLECTION (Application)
0x85, JOYSTICK_REPORT_ID, // REPORT_ID (3)

// 32 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 (8 Positions)
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)

// X, Y, and Z Axis
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x01, // USAGE (Pointer)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0xA1, 0x00, // COLLECTION (Physical)
0x09, 0x30, // USAGE (x)
0x09, 0x31, // USAGE (y)
0x09, 0x32, // USAGE (z)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x03, // REPORT_COUNT (3)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION

// X, Y, and Z Axis Rotation
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x01, // USAGE (Pointer)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0xA1, 0x00, // COLLECTION (Physical)
0x09, 0x33, // USAGE (rx)
0x09, 0x34, // USAGE (ry)
0x09, 0x35, // USAGE (rz)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x03, // REPORT_COUNT (3)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION

0xc0 // END_COLLECTION

#endif
};

Expand Down Expand Up @@ -513,6 +598,155 @@ size_t Keyboard_::write(uint8_t c)
return p; // just return the result of press() since release() almost always returns 1
}

//================================================================================
//================================================================================
// Joystick
//

#define joystickStateSize 13

Joystick_::Joystick_()
{
xAxis = 0;
yAxis = 0;
zAxis = 0;
xAxisRotation = 0;
yAxisRotation = 0;
zAxisRotation = 0;
buttons = 0;
throttle = 0;
rudder = 0;
hatSwitch[0] = -1;
hatSwitch[1] = -1;
}

void Joystick_::begin(bool initAutoSendState)
{
autoSendState = initAutoSendState;
sendState();
}

void Joystick_::end()
{
}

void Joystick_::setButton(uint8_t button, uint8_t value)
{
if (value == 0)
{
releaseButton(button);
}
else
{
pressButton(button);
}
}
void Joystick_::pressButton(uint8_t button)
{
bitSet(buttons, button);
if (autoSendState) sendState();
}
void Joystick_::releaseButton(uint8_t button)
{
bitClear(buttons, button);
if (autoSendState) sendState();
}

void Joystick_::setThrottle(uint8_t value)
{
throttle = value;
if (autoSendState) sendState();
}
void Joystick_::setRudder(uint8_t value)
{
rudder = value;
if (autoSendState) sendState();
}

void Joystick_::setXAxis(int8_t value)
{
xAxis = value;
if (autoSendState) sendState();
}
void Joystick_::setYAxis(int8_t value)
{
yAxis = value;
if (autoSendState) sendState();
}
void Joystick_::setZAxis(int8_t value)
{
zAxis = value;
if (autoSendState) sendState();
}

void Joystick_::setXAxisRotation(int16_t value)
{
xAxisRotation = value;
if (autoSendState) sendState();
}
void Joystick_::setYAxisRotation(int16_t value)
{
yAxisRotation = value;
if (autoSendState) sendState();
}
void Joystick_::setZAxisRotation(int16_t value)
{
zAxisRotation = value;
if (autoSendState) sendState();
}

void Joystick_::setHatSwitch(int8_t hatSwitchIndex, int16_t value)
{
hatSwitch[hatSwitchIndex % 2] = value;
if (autoSendState) sendState();
}

void Joystick_::sendState()
{
uint8_t data[joystickStateSize];
uint32_t buttonTmp = buttons;

// Split 32 bit button-state into 4 bytes
data[0] = buttonTmp & 0xFF;
buttonTmp >>= 8;
data[1] = buttonTmp & 0xFF;
buttonTmp >>= 8;
data[2] = buttonTmp & 0xFF;
buttonTmp >>= 8;
data[3] = buttonTmp & 0xFF;

data[4] = throttle;
data[5] = rudder;

// Calculate hat-switch values
uint8_t convertedHatSwitch[2];
for (int hatSwitchIndex = 0; hatSwitchIndex < 2; hatSwitchIndex++)
{
if (hatSwitch[hatSwitchIndex] < 0)
{
convertedHatSwitch[hatSwitchIndex] = 8;
}
else
{
convertedHatSwitch[hatSwitchIndex] = (hatSwitch[hatSwitchIndex] % 360) / 45;
}
}

// Pack hat-switch states into a single byte
data[6] = (convertedHatSwitch[1] << 4) | (B00001111 & convertedHatSwitch[0]);

data[7] = xAxis;
data[8] = yAxis;
data[9] = zAxis;

data[10] = (xAxisRotation % 360) * 0.708;
data[11] = (yAxisRotation % 360) * 0.708;
data[12] = (zAxisRotation % 360) * 0.708;

// HID_SendReport(Report number, array of values in same order as HID descriptor, length)
HID_SendReport(JOYSTICK_REPORT_ID, data, joystickStateSize);
}

#endif

#endif /* if defined(USBCON) */
45 changes: 45 additions & 0 deletions hardware/arduino/avr/cores/arduino/USBAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,51 @@ class Keyboard_ : public Print
};
extern Keyboard_ Keyboard;

//================================================================================
//================================================================================
// Joystick (Gamepad)
class Joystick_
{
private:
bool autoSendState;
int8_t xAxis;
int8_t yAxis;
int8_t zAxis;
int16_t xAxisRotation;
int16_t yAxisRotation;
int16_t zAxisRotation;
uint32_t buttons;
uint8_t throttle;
uint8_t rudder;
int16_t hatSwitch[2];

public:
Joystick_();

void begin(bool initAutoSendState = true);
void end();

void setXAxis(int8_t value);
void setYAxis(int8_t value);
void setZAxis(int8_t value);

void setXAxisRotation(int16_t value);
void setYAxisRotation(int16_t value);
void setZAxisRotation(int16_t value);

void setButton(uint8_t button, uint8_t value);
void pressButton(uint8_t button);
void releaseButton(uint8_t button);

void setThrottle(uint8_t value);
void setRudder(uint8_t value);

void setHatSwitch(int8_t hatSwitch, int16_t value);

void sendState();
};
extern Joystick_ Joystick;

//================================================================================
//================================================================================
// Low level API
Expand Down