diff --git a/hardware/arduino/avr/cores/arduino/HID.cpp b/hardware/arduino/avr/cores/arduino/HID.cpp index 75c37b24b2f..c5b1a984d90 100644 --- a/hardware/arduino/avr/cores/arduino/HID.cpp +++ b/hardware/arduino/avr/cores/arduino/HID.cpp @@ -23,10 +23,11 @@ //#define RAWHID_ENABLED -// Singletons for mouse and keyboard +// Singletons for mouse, keyboard, and joystick Mouse_ Mouse; Keyboard_ Keyboard; +Joystick_ Joystick; //================================================================================ //================================================================================ @@ -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[] = { @@ -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 }; @@ -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) */ diff --git a/hardware/arduino/avr/cores/arduino/USBAPI.h b/hardware/arduino/avr/cores/arduino/USBAPI.h index 2fab957f930..0536595881d 100644 --- a/hardware/arduino/avr/cores/arduino/USBAPI.h +++ b/hardware/arduino/avr/cores/arduino/USBAPI.h @@ -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