├── .gitattributes ├── .gitignore ├── CONTRIBUTING.md ├── Joystick ├── examples │ ├── DrivingControllerTest │ │ └── DrivingControllerTest.ino │ ├── FlightControllerTest │ │ └── FlightControllerTest.ino │ ├── GamepadExample │ │ └── GamepadExample.ino │ ├── HatSwitchTest │ │ └── HatSwitchTest.ino │ ├── JoystickButton │ │ └── JoystickButton.ino │ ├── JoystickKeyboard │ │ └── JoystickKeyboard.ino │ ├── JoystickTest │ │ └── JoystickTest.ino │ ├── JoystickTestButtons │ │ └── JoystickTestButtons.ino │ └── MultipleJoystickTest │ │ └── MultipleJoystickTest.ino ├── library.properties └── src │ ├── DynamicHID │ ├── DynamicHID.cpp │ └── DynamicHID.h │ ├── Joystick.cpp │ └── Joystick.h ├── LICENSE ├── README.md ├── deploy.bat ├── deploy.sh ├── examples ├── SwitchJoystickIMUTest │ └── SwitchJoystickIMUTest.ino └── SwitchJoystickTest │ └── SwitchJoystickTest.ino ├── keywords.txt ├── library.properties └── src ├── DynamicHID ├── DynamicHID.cpp └── DynamicHID.h ├── SwitchJoystick.cpp └── SwitchJoystick.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## How to contribute to the Arduino Joystick Library 2 | 3 | ### Report a bug 4 | 5 | * **Check to see if the bug has already reported** by searching on GitHub under [Issues](https://github.com/MHeironimus/ArduinoJoystickLibrary/issues). 6 | 7 | * If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/MHeironimus/ArduinoJoystickLibrary/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible (e.g. host OS, Arduino IDE version, etc.), any important hardware information (e.g. name of Arduino board being used, specific pin wirings if they are relavant, etc.), and a **sample sketch** file demonstrating the issue. 8 | 9 | ### Fix a bug 10 | 11 | * Open a new GitHub pull request with the fix. 12 | 13 | * Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. 14 | 15 | ### Whitespace, format code, or cosmetic updates 16 | 17 | * Changes that are cosmetic in nature and do not add anything substantial to the stability, functionality, or testability will generally not be accepted. 18 | 19 | ### Adding a new feature 20 | 21 | * Feel free to [create a new issue](https://github.com/MHeironimus/ArduinoJoystickLibrary/issues/new) to document the feature you are planning to add. This will allow others to see and comment on what you would plan to add to the library. 22 | 23 | ### Questions about the library 24 | 25 | * Be sure to check the [FAQ](https://github.com/MHeironimus/ArduinoJoystickLibrary/wiki/FAQ) in the [Wiki](https://github.com/MHeironimus/ArduinoJoystickLibrary/wiki) to see if your question has already been answered. 26 | * Another good place to look for general Arduino questions is the [Arduino]() website. 27 | 28 | The Arduino Joystick Library is a volunteer effort done in volunteer's spare time. Please be patient as it may take some time for bugs to be fixed, features to be added, and questions to be answered. 29 | -------------------------------------------------------------------------------- /Joystick/examples/DrivingControllerTest/DrivingControllerTest.ino: -------------------------------------------------------------------------------- 1 | // Program used to test the driving simulator functions on 2 | // the USB Joystick object on the Arduino Leonardo or 3 | // Arduino Micro. 4 | // 5 | // Matthew Heironimus 6 | // 2016-05-29 Original version. 7 | //------------------------------------------------------------ 8 | 9 | #include "Joystick.h" 10 | 11 | Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, 12 | JOYSTICK_TYPE_MULTI_AXIS, 4, 0, 13 | false, false, false, false, false, false, 14 | false, false, true, true, true); 15 | 16 | // Set to true to test "Auto Send" mode or false to test "Manual Send" mode. 17 | //const bool testAutoSendMode = true; 18 | const bool testAutoSendMode = false; 19 | 20 | const unsigned long gcCycleDelta = 1000; 21 | const unsigned long gcButtonDelta = 500; 22 | const unsigned long gcAnalogDelta = 25; 23 | unsigned long gNextTime = 0; 24 | unsigned int gCurrentStep = 0; 25 | 26 | void testSingleButtonPush(unsigned int button) 27 | { 28 | if (button > 0) 29 | { 30 | Joystick.releaseButton(button - 1); 31 | } 32 | if (button < 4) 33 | { 34 | Joystick.pressButton(button); 35 | } 36 | } 37 | 38 | void testMultiButtonPush(unsigned int currentStep) 39 | { 40 | for (int button = 0; button < 4; button++) 41 | { 42 | if ((currentStep == 0) || (currentStep == 2)) 43 | { 44 | if ((button % 2) == 0) 45 | { 46 | Joystick.pressButton(button); 47 | } else if (currentStep != 2) 48 | { 49 | Joystick.releaseButton(button); 50 | } 51 | } // if ((currentStep == 0) || (currentStep == 2)) 52 | if ((currentStep == 1) || (currentStep == 2)) 53 | { 54 | if ((button % 2) != 0) 55 | { 56 | Joystick.pressButton(button); 57 | } else if (currentStep != 2) 58 | { 59 | Joystick.releaseButton(button); 60 | } 61 | } // if ((currentStep == 1) || (currentStep == 2)) 62 | if (currentStep == 3) 63 | { 64 | Joystick.releaseButton(button); 65 | } // if (currentStep == 3) 66 | } // for (int button = 0; button < 32; button++) 67 | } 68 | 69 | void testAcceleratorBrake(int value) 70 | { 71 | Joystick.setAccelerator(value); 72 | Joystick.setBrake(260 - value); 73 | } 74 | 75 | void testSteering(int value) 76 | { 77 | if (value < 300) { 78 | Joystick.setSteering(value); 79 | } else { 80 | Joystick.setSteering(600 - value); 81 | } 82 | } 83 | 84 | void setup() { 85 | 86 | Joystick.setAcceleratorRange(0, 260); 87 | Joystick.setBrakeRange(0, 260); 88 | Joystick.setSteeringRange(0, 300); 89 | 90 | if (testAutoSendMode) 91 | { 92 | Joystick.begin(); 93 | } 94 | else 95 | { 96 | Joystick.begin(false); 97 | } 98 | 99 | pinMode(A0, INPUT_PULLUP); 100 | pinMode(13, OUTPUT); 101 | } 102 | 103 | void loop() { 104 | 105 | // System Disabled 106 | if (digitalRead(A0) != 0) 107 | { 108 | // Turn indicator light off. 109 | digitalWrite(13, 0); 110 | return; 111 | } 112 | 113 | // Turn indicator light on. 114 | digitalWrite(13, 1); 115 | 116 | if (millis() >= gNextTime) 117 | { 118 | 119 | if (gCurrentStep < 4) 120 | { 121 | gNextTime = millis() + gcButtonDelta; 122 | testSingleButtonPush(gCurrentStep); 123 | } 124 | else if (gCurrentStep < 9) 125 | { 126 | gNextTime = millis() + gcButtonDelta; 127 | testMultiButtonPush(gCurrentStep - 5); 128 | } 129 | else if (gCurrentStep < (9 + 260)) 130 | { 131 | gNextTime = millis() + gcAnalogDelta; 132 | testAcceleratorBrake(gCurrentStep - 9); 133 | } 134 | else if (gCurrentStep < (9 + 260 + 600)) 135 | { 136 | gNextTime = millis() + gcAnalogDelta; 137 | testSteering(gCurrentStep - (9 + 260)); 138 | } 139 | 140 | if (testAutoSendMode == false) 141 | { 142 | Joystick.sendState(); 143 | } 144 | 145 | gCurrentStep++; 146 | if (gCurrentStep >= (9 + 260 + 600)) 147 | { 148 | gNextTime = millis() + gcCycleDelta; 149 | gCurrentStep = 0; 150 | } 151 | } 152 | } 153 | 154 | -------------------------------------------------------------------------------- /Joystick/examples/FlightControllerTest/FlightControllerTest.ino: -------------------------------------------------------------------------------- 1 | // Program used to test the USB Joystick library when used as 2 | // a Flight Controller on the Arduino Leonardo or Arduino 3 | // Micro. 4 | // 5 | // Matthew Heironimus 6 | // 2016-05-29 - Original Version 7 | //------------------------------------------------------------ 8 | 9 | #include "Joystick.h" 10 | 11 | Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, 12 | JOYSTICK_TYPE_MULTI_AXIS, 32, 0, 13 | true, true, false, false, false, false, 14 | true, true, false, false, false); 15 | 16 | // Set to true to test "Auto Send" mode or false to test "Manual Send" mode. 17 | //const bool testAutoSendMode = true; 18 | const bool testAutoSendMode = false; 19 | 20 | const unsigned long gcCycleDelta = 1000; 21 | const unsigned long gcAnalogDelta = 25; 22 | const unsigned long gcButtonDelta = 500; 23 | unsigned long gNextTime = 0; 24 | unsigned int gCurrentStep = 0; 25 | 26 | void testSingleButtonPush(unsigned int button) 27 | { 28 | if (button > 0) 29 | { 30 | Joystick.releaseButton(button - 1); 31 | } 32 | if (button < 32) 33 | { 34 | Joystick.pressButton(button); 35 | } 36 | } 37 | 38 | void testMultiButtonPush(unsigned int currentStep) 39 | { 40 | for (int button = 0; button < 32; button++) 41 | { 42 | if ((currentStep == 0) || (currentStep == 2)) 43 | { 44 | if ((button % 2) == 0) 45 | { 46 | Joystick.pressButton(button); 47 | } else if (currentStep != 2) 48 | { 49 | Joystick.releaseButton(button); 50 | } 51 | } // if ((currentStep == 0) || (currentStep == 2)) 52 | if ((currentStep == 1) || (currentStep == 2)) 53 | { 54 | if ((button % 2) != 0) 55 | { 56 | Joystick.pressButton(button); 57 | } else if (currentStep != 2) 58 | { 59 | Joystick.releaseButton(button); 60 | } 61 | } // if ((currentStep == 1) || (currentStep == 2)) 62 | if (currentStep == 3) 63 | { 64 | Joystick.releaseButton(button); 65 | } // if (currentStep == 3) 66 | } // for (int button = 0; button < 32; button++) 67 | } 68 | 69 | void testXYAxis(unsigned int currentStep) 70 | { 71 | int xAxis; 72 | int yAxis; 73 | 74 | if (currentStep < 256) 75 | { 76 | xAxis = currentStep - 127; 77 | yAxis = -127; 78 | Joystick.setXAxis(xAxis); 79 | Joystick.setYAxis(yAxis); 80 | } 81 | else if (currentStep < 512) 82 | { 83 | yAxis = currentStep - 256 - 127; 84 | Joystick.setYAxis(yAxis); 85 | } 86 | else if (currentStep < 768) 87 | { 88 | xAxis = 128 - (currentStep - 512); 89 | Joystick.setXAxis(xAxis); 90 | } 91 | else if (currentStep < 1024) 92 | { 93 | yAxis = 128 - (currentStep - 768); 94 | Joystick.setYAxis(yAxis); 95 | } 96 | else if (currentStep < 1024 + 128) 97 | { 98 | xAxis = currentStep - 1024 - 127; 99 | Joystick.setXAxis(xAxis); 100 | Joystick.setYAxis(xAxis); 101 | } 102 | } 103 | 104 | void testThrottleRudder(unsigned int value) 105 | { 106 | Joystick.setThrottle(value); 107 | Joystick.setRudder(255 - value); 108 | } 109 | 110 | void setup() { 111 | 112 | Joystick.setXAxisRange(-127, 127); 113 | Joystick.setYAxisRange(-127, 127); 114 | Joystick.setZAxisRange(-127, 127); 115 | Joystick.setThrottleRange(0, 255); 116 | Joystick.setRudderRange(0, 255); 117 | 118 | if (testAutoSendMode) 119 | { 120 | Joystick.begin(); 121 | } 122 | else 123 | { 124 | Joystick.begin(false); 125 | } 126 | 127 | pinMode(A0, INPUT_PULLUP); 128 | pinMode(13, OUTPUT); 129 | } 130 | 131 | void loop() { 132 | 133 | // System Disabled 134 | if (digitalRead(A0) != 0) 135 | { 136 | // Turn indicator light off. 137 | digitalWrite(13, 0); 138 | return; 139 | } 140 | 141 | // Turn indicator light on. 142 | digitalWrite(13, 1); 143 | 144 | if (millis() >= gNextTime) 145 | { 146 | 147 | if (gCurrentStep < 33) 148 | { 149 | gNextTime = millis() + gcButtonDelta; 150 | testSingleButtonPush(gCurrentStep); 151 | } 152 | else if (gCurrentStep < 37) 153 | { 154 | gNextTime = millis() + gcButtonDelta; 155 | testMultiButtonPush(gCurrentStep - 33); 156 | } 157 | else if (gCurrentStep < (37 + 256)) 158 | { 159 | gNextTime = millis() + gcAnalogDelta; 160 | testThrottleRudder(gCurrentStep - 37); 161 | } 162 | else if (gCurrentStep < (37 + 256 + 1024 + 128)) 163 | { 164 | gNextTime = millis() + gcAnalogDelta; 165 | testXYAxis(gCurrentStep - (37 + 256)); 166 | } 167 | 168 | if (testAutoSendMode == false) 169 | { 170 | Joystick.sendState(); 171 | } 172 | 173 | gCurrentStep++; 174 | if (gCurrentStep >= (37 + 256 + 1024 + 128)) 175 | { 176 | gNextTime = millis() + gcCycleDelta; 177 | gCurrentStep = 0; 178 | } 179 | } 180 | } 181 | 182 | -------------------------------------------------------------------------------- /Joystick/examples/GamepadExample/GamepadExample.ino: -------------------------------------------------------------------------------- 1 | // Simple gamepad example that demonstraits how to read five Arduino 2 | // digital pins and map them to the Arduino Joystick library. 3 | // 4 | // The digital pins 2 - 6 are grounded when they are pressed. 5 | // Pin 2 = UP 6 | // Pin 3 = RIGHT 7 | // Pin 4 = DOWN 8 | // Pin 5 = LEFT 9 | // Pin 6 = FIRE 10 | // 11 | // NOTE: This sketch file is for use with Arduino Leonardo and 12 | // Arduino Micro only. 13 | // 14 | // by Matthew Heironimus 15 | // 2016-11-24 16 | //-------------------------------------------------------------------- 17 | 18 | #include 19 | 20 | Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,JOYSTICK_TYPE_GAMEPAD, 21 | 1, 0, // Button Count, Hat Switch Count 22 | true, true, false, // X and Y, but no Z Axis 23 | false, false, false, // No Rx, Ry, or Rz 24 | false, false, // No rudder or throttle 25 | false, false, false); // No accelerator, brake, or steering 26 | 27 | void setup() { 28 | // Initialize Button Pins 29 | pinMode(2, INPUT_PULLUP); 30 | pinMode(3, INPUT_PULLUP); 31 | pinMode(4, INPUT_PULLUP); 32 | pinMode(5, INPUT_PULLUP); 33 | pinMode(6, INPUT_PULLUP); 34 | 35 | // Initialize Joystick Library 36 | Joystick.begin(); 37 | Joystick.setXAxisRange(-1, 1); 38 | Joystick.setYAxisRange(-1, 1); 39 | } 40 | 41 | // Last state of the buttons 42 | int lastButtonState[5] = {0,0,0,0,0}; 43 | 44 | void loop() { 45 | 46 | // Read pin values 47 | for (int index = 0; index < 5; index++) 48 | { 49 | int currentButtonState = !digitalRead(index + 2); 50 | if (currentButtonState != lastButtonState[index]) 51 | { 52 | switch (index) { 53 | case 0: // UP 54 | if (currentButtonState == 1) { 55 | Joystick.setYAxis(-1); 56 | } else { 57 | Joystick.setYAxis(0); 58 | } 59 | break; 60 | case 1: // RIGHT 61 | if (currentButtonState == 1) { 62 | Joystick.setXAxis(1); 63 | } else { 64 | Joystick.setXAxis(0); 65 | } 66 | break; 67 | case 2: // DOWN 68 | if (currentButtonState == 1) { 69 | Joystick.setYAxis(1); 70 | } else { 71 | Joystick.setYAxis(0); 72 | } 73 | break; 74 | case 3: // LEFT 75 | if (currentButtonState == 1) { 76 | Joystick.setXAxis(-1); 77 | } else { 78 | Joystick.setXAxis(0); 79 | } 80 | break; 81 | case 4: // FIRE 82 | Joystick.setButton(0, currentButtonState); 83 | break; 84 | } 85 | lastButtonState[index] = currentButtonState; 86 | } 87 | } 88 | 89 | delay(10); 90 | } 91 | 92 | -------------------------------------------------------------------------------- /Joystick/examples/HatSwitchTest/HatSwitchTest.ino: -------------------------------------------------------------------------------- 1 | // Simple example application that shows how to read four Arduino 2 | // digital pins and map them to the USB Joystick library's hat switch. 3 | // 4 | // The digital pins 4, 5, 6, 7, 8, 9, 10, and 11 are grounded when 5 | // they are pressed. 6 | // 7 | // Pin Mappings: 8 | // 4 - Hat Switch #0 UP 9 | // 5 - Hat Switch #0 RIGHT 10 | // 6 - Hat Switch #0 DOWN 11 | // 7 - Hat Switch #0 LEFT 12 | // 8 - Hat Switch #1 UP 13 | // 9 - Hat Switch #1 RIGHT 14 | // 10 - Hat Switch #1 DOWN 15 | // 11 - Hat Switch #1 LEFT 16 | // 17 | // NOTE: This sketch file is for use with Arduino Leonardo and 18 | // Arduino Micro only. 19 | // 20 | // by Matthew Heironimus 21 | // 2016-05-30 22 | //-------------------------------------------------------------------- 23 | 24 | #include 25 | 26 | Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, 27 | JOYSTICK_TYPE_GAMEPAD, 0, 28 | JOYSTICK_DEFAULT_HATSWITCH_COUNT, 29 | false, false, false, false, false, false, 30 | false, false, false, false, false); 31 | 32 | void setup() { 33 | 34 | // Initialize Button Pins 35 | for (int index = 4; index < 12; index++) 36 | { 37 | pinMode(index, INPUT_PULLUP); 38 | } 39 | 40 | // Initialize Joystick Library 41 | Joystick.begin(); 42 | } 43 | 44 | // Last state of the pins 45 | int lastButtonState[2][4] = {{0,0,0,0}, {0,0,0,0}}; 46 | 47 | void loop() { 48 | 49 | bool valueChanged[2] = {false, false}; 50 | int currentPin = 4; 51 | 52 | // Read pin values 53 | for (int hatSwitch = 0; hatSwitch < 2; hatSwitch++) 54 | { 55 | for (int index = 0; index < 4; index++) 56 | { 57 | int currentButtonState = !digitalRead(currentPin++); 58 | if (currentButtonState != lastButtonState[hatSwitch][index]) 59 | { 60 | valueChanged[hatSwitch] = true; 61 | lastButtonState[hatSwitch][index] = currentButtonState; 62 | } 63 | } 64 | } 65 | 66 | for (int hatSwitch = 0; hatSwitch < 2; hatSwitch++) 67 | { 68 | if (valueChanged[hatSwitch]) { 69 | 70 | if ((lastButtonState[hatSwitch][0] == 0) 71 | && (lastButtonState[hatSwitch][1] == 0) 72 | && (lastButtonState[hatSwitch][2] == 0) 73 | && (lastButtonState[hatSwitch][3] == 0)) { 74 | Joystick.setHatSwitch(hatSwitch, -1); 75 | } 76 | if (lastButtonState[hatSwitch][0] == 1) { 77 | Joystick.setHatSwitch(hatSwitch, 0); 78 | } 79 | if (lastButtonState[hatSwitch][1] == 1) { 80 | Joystick.setHatSwitch(hatSwitch, 90); 81 | } 82 | if (lastButtonState[hatSwitch][2] == 1) { 83 | Joystick.setHatSwitch(hatSwitch, 180); 84 | } 85 | if (lastButtonState[hatSwitch][3] == 1) { 86 | Joystick.setHatSwitch(hatSwitch, 270); 87 | } 88 | 89 | } // if the value changed 90 | 91 | } // for each hat switch 92 | 93 | delay(50); 94 | } 95 | 96 | -------------------------------------------------------------------------------- /Joystick/examples/JoystickButton/JoystickButton.ino: -------------------------------------------------------------------------------- 1 | // Simple example application that shows how to read four Arduino 2 | // digital pins and map them to the USB Joystick library. 3 | // 4 | // Ground digital pins 9, 10, 11, and 12 to press the joystick 5 | // buttons 0, 1, 2, and 3. 6 | // 7 | // NOTE: This sketch file is for use with Arduino Leonardo and 8 | // Arduino Micro only. 9 | // 10 | // by Matthew Heironimus 11 | // 2015-11-20 12 | //-------------------------------------------------------------------- 13 | 14 | #include 15 | 16 | Joystick_ Joystick; 17 | 18 | void setup() { 19 | // Initialize Button Pins 20 | pinMode(9, INPUT_PULLUP); 21 | pinMode(10, INPUT_PULLUP); 22 | pinMode(11, INPUT_PULLUP); 23 | pinMode(12, INPUT_PULLUP); 24 | 25 | // Initialize Joystick Library 26 | Joystick.begin(); 27 | } 28 | 29 | // Constant that maps the phyical pin to the joystick button. 30 | const int pinToButtonMap = 9; 31 | 32 | // Last state of the button 33 | int lastButtonState[4] = {0,0,0,0}; 34 | 35 | void loop() { 36 | 37 | // Read pin values 38 | for (int index = 0; index < 4; index++) 39 | { 40 | int currentButtonState = !digitalRead(index + pinToButtonMap); 41 | if (currentButtonState != lastButtonState[index]) 42 | { 43 | Joystick.setButton(index, currentButtonState); 44 | lastButtonState[index] = currentButtonState; 45 | } 46 | } 47 | 48 | delay(50); 49 | } 50 | 51 | -------------------------------------------------------------------------------- /Joystick/examples/JoystickKeyboard/JoystickKeyboard.ino: -------------------------------------------------------------------------------- 1 | // Simple example application that shows how to read four Arduino 2 | // digital pins and map them to buttons on a joystick or keys on a 3 | // keyboard uisng the Arduino Joystick and Keyboard libraries. 4 | // 5 | // The digital pins 9, 10, 11, and 12 are grounded when they are pressed. 6 | // 7 | // NOTE: This sketch file is for use with Arduino Leonardo and 8 | // Arduino Micro only. 9 | // 10 | // Pin 9 = Joystick Button 0 11 | // Pin 10 = Joystick Button 1 12 | // Pin 11 = 1 key on the Keyboard 13 | // Pin 12 = 2 key on the Keyboard 14 | // 15 | // by Matthew Heironimus 16 | // 2016-05-13 17 | //-------------------------------------------------------------------- 18 | 19 | #include 20 | #include 21 | 22 | Joystick_ Joystick; 23 | 24 | void setup() { 25 | // Initialize Button Pins 26 | pinMode(9, INPUT_PULLUP); 27 | pinMode(10, INPUT_PULLUP); 28 | pinMode(11, INPUT_PULLUP); 29 | pinMode(12, INPUT_PULLUP); 30 | 31 | // Initialize Joystick Library 32 | Joystick.begin(); 33 | } 34 | 35 | // Constant that maps the phyical pin to the joystick button. 36 | const int pinToButtonMap = 9; 37 | 38 | // Last state of the button 39 | int lastButtonState[4] = {0,0,0,0}; 40 | 41 | void loop() { 42 | 43 | // Read pin values 44 | for (int index = 0; index < 4; index++) 45 | { 46 | int currentButtonState = !digitalRead(index + pinToButtonMap); 47 | if (currentButtonState != lastButtonState[index]) 48 | { 49 | if (index < 2) { 50 | Joystick.setButton(index, currentButtonState); 51 | lastButtonState[index] = currentButtonState; 52 | } else { 53 | if (currentButtonState) { 54 | Keyboard.write(47 + index); 55 | delay(500); 56 | } 57 | } 58 | } 59 | } 60 | 61 | delay(100); 62 | } 63 | 64 | -------------------------------------------------------------------------------- /Joystick/examples/JoystickTest/JoystickTest.ino: -------------------------------------------------------------------------------- 1 | // Program used to test the USB Joystick object on the 2 | // Arduino Leonardo or Arduino Micro. 3 | // 4 | // Matthew Heironimus 5 | // 2015-03-28 - Original Version 6 | // 2015-11-18 - Updated to use the new Joystick library 7 | // written for Arduino IDE Version 1.6.6 and 8 | // above. 9 | // 2016-05-13 Updated to use new dynamic Joystick library 10 | // that can be customized. 11 | //------------------------------------------------------------ 12 | 13 | #include "Joystick.h" 14 | 15 | // Create Joystick 16 | Joystick_ Joystick; 17 | 18 | // Set to true to test "Auto Send" mode or false to test "Manual Send" mode. 19 | //const bool testAutoSendMode = true; 20 | const bool testAutoSendMode = false; 21 | 22 | const unsigned long gcCycleDelta = 1000; 23 | const unsigned long gcAnalogDelta = 25; 24 | const unsigned long gcButtonDelta = 500; 25 | unsigned long gNextTime = 0; 26 | unsigned int gCurrentStep = 0; 27 | 28 | void testSingleButtonPush(unsigned int button) 29 | { 30 | if (button > 0) 31 | { 32 | Joystick.releaseButton(button - 1); 33 | } 34 | if (button < 32) 35 | { 36 | Joystick.pressButton(button); 37 | } 38 | } 39 | 40 | void testMultiButtonPush(unsigned int currentStep) 41 | { 42 | for (int button = 0; button < 32; button++) 43 | { 44 | if ((currentStep == 0) || (currentStep == 2)) 45 | { 46 | if ((button % 2) == 0) 47 | { 48 | Joystick.pressButton(button); 49 | } else if (currentStep != 2) 50 | { 51 | Joystick.releaseButton(button); 52 | } 53 | } // if ((currentStep == 0) || (currentStep == 2)) 54 | if ((currentStep == 1) || (currentStep == 2)) 55 | { 56 | if ((button % 2) != 0) 57 | { 58 | Joystick.pressButton(button); 59 | } else if (currentStep != 2) 60 | { 61 | Joystick.releaseButton(button); 62 | } 63 | } // if ((currentStep == 1) || (currentStep == 2)) 64 | if (currentStep == 3) 65 | { 66 | Joystick.releaseButton(button); 67 | } // if (currentStep == 3) 68 | } // for (int button = 0; button < 32; button++) 69 | } 70 | 71 | void testXYAxis(unsigned int currentStep) 72 | { 73 | int xAxis; 74 | int yAxis; 75 | 76 | if (currentStep < 256) 77 | { 78 | xAxis = currentStep - 127; 79 | yAxis = -127; 80 | Joystick.setXAxis(xAxis); 81 | Joystick.setYAxis(yAxis); 82 | } 83 | else if (currentStep < 512) 84 | { 85 | yAxis = currentStep - 256 - 127; 86 | Joystick.setYAxis(yAxis); 87 | } 88 | else if (currentStep < 768) 89 | { 90 | xAxis = 128 - (currentStep - 512); 91 | Joystick.setXAxis(xAxis); 92 | } 93 | else if (currentStep < 1024) 94 | { 95 | yAxis = 128 - (currentStep - 768); 96 | Joystick.setYAxis(yAxis); 97 | } 98 | else if (currentStep < 1024 + 128) 99 | { 100 | xAxis = currentStep - 1024 - 127; 101 | Joystick.setXAxis(xAxis); 102 | Joystick.setYAxis(xAxis); 103 | } 104 | } 105 | 106 | void testZAxis(unsigned int currentStep) 107 | { 108 | if (currentStep < 128) 109 | { 110 | Joystick.setZAxis(-currentStep); 111 | } 112 | else if (currentStep < 256 + 128) 113 | { 114 | Joystick.setZAxis(currentStep - 128 - 127); 115 | } 116 | else if (currentStep < 256 + 128 + 127) 117 | { 118 | Joystick.setZAxis(127 - (currentStep - 383)); 119 | } 120 | } 121 | 122 | void testHatSwitch(unsigned int currentStep) 123 | { 124 | if (currentStep < 8) 125 | { 126 | Joystick.setHatSwitch(0, currentStep * 45); 127 | } 128 | else if (currentStep == 8) 129 | { 130 | Joystick.setHatSwitch(0, -1); 131 | } 132 | else if (currentStep < 17) 133 | { 134 | Joystick.setHatSwitch(1, (currentStep - 9) * 45); 135 | } 136 | else if (currentStep == 17) 137 | { 138 | Joystick.setHatSwitch(1, -1); 139 | } 140 | else if (currentStep == 18) 141 | { 142 | Joystick.setHatSwitch(0, 0); 143 | Joystick.setHatSwitch(1, 0); 144 | } 145 | else if (currentStep < 27) 146 | { 147 | Joystick.setHatSwitch(0, (currentStep - 18) * 45); 148 | Joystick.setHatSwitch(1, (8 - (currentStep - 18)) * 45); 149 | } 150 | else if (currentStep == 27) 151 | { 152 | Joystick.setHatSwitch(0, -1); 153 | Joystick.setHatSwitch(1, -1); 154 | } 155 | } 156 | 157 | void testThrottleRudder(unsigned int value) 158 | { 159 | Joystick.setThrottle(value); 160 | Joystick.setRudder(value); 161 | } 162 | 163 | void testXYZAxisRotation(unsigned int degree) 164 | { 165 | Joystick.setRxAxis(degree); 166 | Joystick.setRyAxis(degree); 167 | Joystick.setRzAxis(degree * 2); 168 | } 169 | 170 | void setup() { 171 | 172 | // Set Range Values 173 | Joystick.setXAxisRange(-127, 127); 174 | Joystick.setYAxisRange(-127, 127); 175 | Joystick.setZAxisRange(-127, 127); 176 | Joystick.setRxAxisRange(0, 360); 177 | Joystick.setRyAxisRange(360, 0); 178 | Joystick.setRzAxisRange(0, 720); 179 | Joystick.setThrottleRange(0, 255); 180 | Joystick.setRudderRange(255, 0); 181 | 182 | if (testAutoSendMode) 183 | { 184 | Joystick.begin(); 185 | } 186 | else 187 | { 188 | Joystick.begin(false); 189 | } 190 | 191 | pinMode(A0, INPUT_PULLUP); 192 | pinMode(13, OUTPUT); 193 | } 194 | 195 | void loop() { 196 | 197 | // System Disabled 198 | if (digitalRead(A0) != 0) 199 | { 200 | // Turn indicator light off. 201 | digitalWrite(13, 0); 202 | return; 203 | } 204 | 205 | // Turn indicator light on. 206 | digitalWrite(13, 1); 207 | 208 | if (millis() >= gNextTime) 209 | { 210 | 211 | if (gCurrentStep < 33) 212 | { 213 | gNextTime = millis() + gcButtonDelta; 214 | testSingleButtonPush(gCurrentStep); 215 | } 216 | else if (gCurrentStep < 37) 217 | { 218 | gNextTime = millis() + gcButtonDelta; 219 | testMultiButtonPush(gCurrentStep - 33); 220 | } 221 | else if (gCurrentStep < (37 + 256)) 222 | { 223 | gNextTime = millis() + gcAnalogDelta; 224 | testThrottleRudder(gCurrentStep - 37); 225 | } 226 | else if (gCurrentStep < (37 + 256 + 1024 + 128)) 227 | { 228 | gNextTime = millis() + gcAnalogDelta; 229 | testXYAxis(gCurrentStep - (37 + 256)); 230 | } 231 | else if (gCurrentStep < (37 + 256 + 1024 + 128 + 510)) 232 | { 233 | gNextTime = millis() + gcAnalogDelta; 234 | testZAxis(gCurrentStep - (37 + 256 + 1024 + 128)); 235 | } 236 | else if (gCurrentStep < (37 + 256 + 1024 + 128 + 510 + 28)) 237 | { 238 | gNextTime = millis() + gcButtonDelta; 239 | testHatSwitch(gCurrentStep - (37 + 256 + 1024 + 128 + 510)); 240 | } 241 | else if (gCurrentStep < (37 + 256 + 1024 + 128 + 510 + 28 + 360)) 242 | { 243 | gNextTime = millis() + gcAnalogDelta; 244 | testXYZAxisRotation(gCurrentStep - (37 + 256 + 1024 + 128 + 510 + 28)); 245 | } 246 | 247 | if (testAutoSendMode == false) 248 | { 249 | Joystick.sendState(); 250 | } 251 | 252 | gCurrentStep++; 253 | if (gCurrentStep == (37 + 256 + 1024 + 128 + 510 + 28 + 360)) 254 | { 255 | gNextTime = millis() + gcCycleDelta; 256 | gCurrentStep = 0; 257 | } 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /Joystick/examples/JoystickTestButtons/JoystickTestButtons.ino: -------------------------------------------------------------------------------- 1 | // Program used to test the USB Joystick object on the 2 | // Arduino Leonardo or Arduino Micro. 3 | // 4 | // Matthew Heironimus 5 | // 2015-03-28 - Original Version 6 | // 2015-11-18 - Updated to use the new Joystick library 7 | // written for Arduino IDE Version 1.6.6 and 8 | // above. 9 | // 2016-05-13 Updated to use new dynamic Joystick library 10 | // that can be customized. 11 | //------------------------------------------------------------ 12 | 13 | #include "Joystick.h" 14 | 15 | // Create Joystick 16 | Joystick_ Joystick; 17 | 18 | // Set to true to test "Auto Send" mode or false to test "Manual Send" mode. 19 | //const bool testAutoSendMode = true; 20 | const bool testAutoSendMode = false; 21 | 22 | void setup() { 23 | 24 | if (testAutoSendMode) 25 | { 26 | Joystick.begin(); 27 | } 28 | else 29 | { 30 | Joystick.begin(false); 31 | } 32 | 33 | pinMode(A0, INPUT); 34 | pinMode(A1, INPUT); 35 | pinMode(A2, INPUT); 36 | pinMode(A3, INPUT); 37 | pinMode(0, INPUT); 38 | pinMode(1, INPUT); 39 | pinMode(2, INPUT); 40 | pinMode(3, INPUT); 41 | pinMode(4, INPUT); 42 | pinMode(5, INPUT); 43 | pinMode(6, INPUT); 44 | pinMode(7, INPUT); 45 | pinMode(13, OUTPUT); 46 | 47 | Joystick.setXAxis(128); 48 | Joystick.setYAxis(128); 49 | Joystick.setZAxis(128); 50 | Joystick.setRzAxis(128); 51 | Joystick.sendState(); 52 | } 53 | 54 | void loop() { 55 | if(!digitalRead(A0)){ 56 | if (!digitalRead(0)){ 57 | Joystick.pressButton(0); // Y 58 | }else if (!digitalRead(1)){ 59 | Joystick.pressButton(1); // B 60 | }else if (!digitalRead(2)){ 61 | Joystick.pressButton(2); // A 62 | }else if (!digitalRead(3)){ 63 | Joystick.pressButton(3); // Z 64 | }else if (!digitalRead(4)){ 65 | Joystick.pressButton(4); // L 66 | }else if (!digitalRead(5)){ 67 | Joystick.pressButton(5); // R 68 | }else if (!digitalRead(6)){ 69 | Joystick.pressButton(6); // Zl 70 | }else if (!digitalRead(7)){ 71 | Joystick.pressButton(7); // Zr 72 | }else{ 73 | Joystick.releaseButton(0); 74 | Joystick.releaseButton(1); 75 | Joystick.releaseButton(2); 76 | Joystick.releaseButton(3); 77 | Joystick.releaseButton(4); 78 | Joystick.releaseButton(5); 79 | Joystick.releaseButton(6); 80 | Joystick.releaseButton(7); 81 | Joystick.releaseButton(8); 82 | } 83 | } 84 | else if(!digitalRead(A1)){ 85 | if (!digitalRead(0)){ 86 | Joystick.pressButton(8); // - 87 | }else if (!digitalRead(1)){ 88 | Joystick.pressButton(9); // + 89 | }else if (!digitalRead(2)){ 90 | Joystick.pressButton(10); // Lstick 91 | }else if (!digitalRead(3)){ 92 | Joystick.pressButton(11); // Rstick 93 | }else if (!digitalRead(4)){ 94 | Joystick.pressButton(12); // Home 95 | }else if (!digitalRead(5)){ 96 | Joystick.pressButton(13); // capture 97 | }else if (!digitalRead(6)){ 98 | Joystick.pressButton(14); 99 | }else if (!digitalRead(7)){ 100 | Joystick.pressButton(15); 101 | }else{ 102 | Joystick.releaseButton(8); 103 | Joystick.releaseButton(9); 104 | Joystick.releaseButton(10); 105 | Joystick.releaseButton(11); 106 | Joystick.releaseButton(12); 107 | Joystick.releaseButton(13); 108 | Joystick.releaseButton(14); 109 | Joystick.releaseButton(15); 110 | } 111 | }else if(!digitalRead(A2)){ 112 | if (!digitalRead(0)){ 113 | Joystick.setXAxis(255); // Left joystick RIGHT 114 | }else if (!digitalRead(1)){ 115 | Joystick.setXAxis(0); // Left joystick LEFT 116 | }else if (!digitalRead(2)){ 117 | Joystick.setYAxis(255); // Left joystick DOWN 118 | }else if (!digitalRead(3)){ 119 | Joystick.setYAxis(0); // Left joystick UP 120 | }else if (!digitalRead(4)){ 121 | Joystick.setZAxis(255); // Right joystick RIGHT (camera left) 122 | }else if (!digitalRead(5)){ 123 | Joystick.setZAxis(0); // Right joystick LEFT (camera right) 124 | }else if (!digitalRead(6)){ 125 | Joystick.setRzAxis(255); // Right joystick DOWN (camera zoom out) 126 | }else if (!digitalRead(7)){ 127 | Joystick.setRzAxis(0); // Right joystick DOWN (camera zoom in) 128 | }else{ 129 | Joystick.setXAxis(128); 130 | Joystick.setYAxis(128); 131 | Joystick.setZAxis(128); 132 | Joystick.setRzAxis(128); 133 | } 134 | } 135 | else if(!digitalRead(A3)){ 136 | if (!digitalRead(0)){ 137 | Joystick.setHatSwitch(0); // UP 138 | }else if (!digitalRead(1)){ 139 | Joystick.setHatSwitch(45); // UP 140 | }else if (!digitalRead(2)){ 141 | Joystick.setHatSwitch(90); // RIGHT 142 | }else if (!digitalRead(3)){ 143 | Joystick.setHatSwitch(135); // DOWN 144 | }else if (!digitalRead(4)){ 145 | Joystick.setHatSwitch(180); // DOWN 146 | }else if (!digitalRead(5)){ 147 | Joystick.setHatSwitch(225); // DOWN 148 | }else if (!digitalRead(6)){ 149 | Joystick.setHatSwitch(270); // LEFT 150 | }else if (!digitalRead(7)){ 151 | Joystick.setHatSwitch(315); // UP 152 | }else{ 153 | Joystick.setHatSwitch(-1); 154 | } 155 | } 156 | Joystick.sendState(); 157 | delay(50); 158 | } 159 | -------------------------------------------------------------------------------- /Joystick/examples/MultipleJoystickTest/MultipleJoystickTest.ino: -------------------------------------------------------------------------------- 1 | // Program used to test using the Arduino Joystick Library 2 | // to create multiple Joystick objects on a single Arduino 3 | // Leonardo or Arduino Micro. 4 | // 5 | // Each joystick has a unique configuration. 6 | // 7 | // Matthew Heironimus 8 | // 2016-05-13 9 | //------------------------------------------------------------ 10 | #include 11 | 12 | #define JOYSTICK_COUNT 4 13 | 14 | Joystick_ Joystick[JOYSTICK_COUNT] = { 15 | Joystick_(0x03, JOYSTICK_TYPE_GAMEPAD, 4, 2, true, true, false, false, false, false, false, false, false, false, false), 16 | Joystick_(0x04, JOYSTICK_TYPE_JOYSTICK, 8, 1, true, true, true, true, false, false, false, false, false, false, false), 17 | Joystick_(0x05, JOYSTICK_TYPE_MULTI_AXIS, 16, 0, false, true, false, true, false, false, true, true, false, false, false), 18 | Joystick_(0x06, JOYSTICK_TYPE_MULTI_AXIS, 32, 1, true, true, false, true, true, false, false, false, true, true, true) 19 | }; 20 | 21 | // Set to true to test "Auto Send" mode or false to test "Manual Send" mode. 22 | //const bool testAutoSendMode = true; 23 | const bool testAutoSendMode = false; 24 | 25 | const unsigned long gcCycleDelta = 1000; 26 | const unsigned long gcAnalogDelta = 25; 27 | const unsigned long gcButtonDelta = 500; 28 | unsigned long gNextTime = 0; 29 | unsigned int gCurrentStep = 0; 30 | int gJoystickId = 0; 31 | 32 | void testSingleButtonPush(int joystickId, unsigned int button) 33 | { 34 | if (button > 0) 35 | { 36 | Joystick[joystickId].releaseButton(button - 1); 37 | } 38 | if (button < 16) 39 | { 40 | Joystick[joystickId].pressButton(button); 41 | } 42 | } 43 | 44 | void testMultiButtonPush(int joystickId, unsigned int currentStep) 45 | { 46 | for (int button = 0; button < 16; button++) 47 | { 48 | if ((currentStep == 0) || (currentStep == 2)) 49 | { 50 | if ((button % 2) == 0) 51 | { 52 | Joystick[joystickId].pressButton(button); 53 | } else if (currentStep != 2) 54 | { 55 | Joystick[joystickId].releaseButton(button); 56 | } 57 | } // if ((currentStep == 0) || (currentStep == 2)) 58 | if ((currentStep == 1) || (currentStep == 2)) 59 | { 60 | if ((button % 2) != 0) 61 | { 62 | Joystick[joystickId].pressButton(button); 63 | } else if (currentStep != 2) 64 | { 65 | Joystick[joystickId].releaseButton(button); 66 | } 67 | } // if ((currentStep == 1) || (currentStep == 2)) 68 | if (currentStep == 3) 69 | { 70 | Joystick[joystickId].releaseButton(button); 71 | } // if (currentStep == 3) 72 | } // for (int button = 0; button < 32; button++) 73 | } 74 | 75 | void testXYAxis(int joystickId, unsigned int currentStep) 76 | { 77 | if (currentStep < 255) 78 | { 79 | Joystick[joystickId].setXAxis(currentStep - 127); 80 | Joystick[joystickId].setYAxis(-127); 81 | } 82 | else if (currentStep < 510) 83 | { 84 | Joystick[joystickId].setYAxis(currentStep - 255 - 127); 85 | } 86 | else if (currentStep < 765) 87 | { 88 | Joystick[joystickId].setXAxis(127 - (currentStep - 510)); 89 | } 90 | else if (currentStep < 1020) 91 | { 92 | Joystick[joystickId].setYAxis(127 - (currentStep - 765)); 93 | } 94 | else if (currentStep <= 1020 + 127) 95 | { 96 | Joystick[joystickId].setXAxis(currentStep - 1020 - 127); 97 | Joystick[joystickId].setYAxis(currentStep - 1020 - 127); 98 | } 99 | } 100 | 101 | void setup() { 102 | 103 | for (int index = 0; index < JOYSTICK_COUNT; index++) 104 | { 105 | Joystick[index].setXAxisRange(-127, 127); 106 | Joystick[index].setYAxisRange(-127, 127); 107 | 108 | if (testAutoSendMode) 109 | { 110 | Joystick[index].begin(); 111 | } 112 | else 113 | { 114 | Joystick[index].begin(false); 115 | } 116 | } 117 | 118 | pinMode(A0, INPUT_PULLUP); 119 | pinMode(13, OUTPUT); 120 | } 121 | 122 | void loop() { 123 | 124 | // System Disabled 125 | if (digitalRead(A0) != 0) 126 | { 127 | digitalWrite(13, 0); 128 | return; 129 | } 130 | 131 | // Turn indicator light on. 132 | digitalWrite(13, 1); 133 | 134 | if (millis() >= gNextTime) 135 | { 136 | 137 | if (gCurrentStep < 17) 138 | { 139 | gNextTime = millis() + gcButtonDelta; 140 | testSingleButtonPush(gJoystickId, gCurrentStep); 141 | } 142 | else if (gCurrentStep < (17 + 4)) 143 | { 144 | gNextTime = millis() + gcButtonDelta; 145 | testMultiButtonPush(gJoystickId, gCurrentStep - 17); 146 | } 147 | else if (gCurrentStep < (17 + 4 + 1024 + 128)) 148 | { 149 | gNextTime = millis() + gcAnalogDelta; 150 | testXYAxis(gJoystickId, gCurrentStep - (17 + 4)); 151 | } 152 | 153 | if (testAutoSendMode == false) 154 | { 155 | Joystick[gJoystickId].sendState(); 156 | } 157 | 158 | gCurrentStep++; 159 | if (gCurrentStep == (17 + 4 + 1024 + 128)) 160 | { 161 | gNextTime = millis() + gcCycleDelta; 162 | gCurrentStep = 0; 163 | 164 | if (++gJoystickId >= JOYSTICK_COUNT) 165 | { 166 | gJoystickId = 0; 167 | } 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /Joystick/library.properties: -------------------------------------------------------------------------------- 1 | name=Joystick 2 | version=2.0.4 3 | author=Matthew Heironimus 4 | maintainer=Matthew Heironimus 5 | sentence=Arduino library that allows an Arduino Leonardo or Arduino Micro to appear as a Joystick or Gamepad. 6 | paragraph=This library is built on the PluggableUSB library. It can be used with or without other HID-based libraries (Mouse, Keyboard, etc.). 7 | category=Device Control 8 | url=https://github.com/MHeironimus/ArduinoJoystickLibrary 9 | architectures=avr 10 | -------------------------------------------------------------------------------- /Joystick/src/DynamicHID/DynamicHID.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Modified by Matthew Heironimus to support HID Report Descriptors to be in 3 | standard RAM in addition to program memory (PROGMEM). 4 | 5 | Copyright (c) 2015, Arduino LLC 6 | Original code (pre-library): Copyright (c) 2011, Peter Barrett 7 | 8 | Permission to use, copy, modify, and/or distribute this software for 9 | any purpose with or without fee is hereby granted, provided that the 10 | above copyright notice and this permission notice appear in all copies. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 14 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 15 | BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES 16 | OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 17 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 18 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 19 | SOFTWARE. 20 | */ 21 | 22 | #include "DynamicHID.h" 23 | 24 | #if defined(USBCON) 25 | 26 | DynamicHID_& DynamicHID() 27 | { 28 | static DynamicHID_ obj; 29 | return obj; 30 | } 31 | 32 | int DynamicHID_::getInterface(uint8_t* interfaceCount) 33 | { 34 | *interfaceCount += 1; // uses 1 35 | DYNAMIC_HIDDescriptor hidInterface = { 36 | D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, DYNAMIC_HID_SUBCLASS_NONE, DYNAMIC_HID_PROTOCOL_NONE), 37 | D_HIDREPORT(descriptorSize), 38 | D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01) 39 | }; 40 | return USB_SendControl(0, &hidInterface, sizeof(hidInterface)); 41 | } 42 | 43 | int DynamicHID_::getDescriptor(USBSetup& setup) 44 | { 45 | // Check if this is a HID Class Descriptor request 46 | if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) { return 0; } 47 | if (setup.wValueH != DYNAMIC_HID_REPORT_DESCRIPTOR_TYPE) { return 0; } 48 | 49 | // In a HID Class Descriptor wIndex cointains the interface number 50 | if (setup.wIndex != pluggedInterface) { return 0; } 51 | 52 | int total = 0; 53 | DynamicHIDSubDescriptor* node; 54 | for (node = rootNode; node; node = node->next) { 55 | int res = USB_SendControl((node->inProgMem ? TRANSFER_PGM : 0), node->data, node->length); 56 | if (res == -1) 57 | return -1; 58 | total += res; 59 | } 60 | 61 | // Reset the protocol on reenumeration. Normally the host should not assume the state of the protocol 62 | // due to the USB specs, but Windows and Linux just assumes its in report mode. 63 | protocol = DYNAMIC_HID_REPORT_PROTOCOL; 64 | 65 | return total; 66 | } 67 | 68 | uint8_t DynamicHID_::getShortName(char *name) 69 | { 70 | name[0] = 'H'; 71 | name[1] = 'I'; 72 | name[2] = 'D'; 73 | name[3] = 'A' + (descriptorSize & 0x0F); 74 | name[4] = 'A' + ((descriptorSize >> 4) & 0x0F); 75 | return 5; 76 | } 77 | 78 | void DynamicHID_::AppendDescriptor(DynamicHIDSubDescriptor *node) 79 | { 80 | if (!rootNode) { 81 | rootNode = node; 82 | } else { 83 | DynamicHIDSubDescriptor *current = rootNode; 84 | while (current->next) { 85 | current = current->next; 86 | } 87 | current->next = node; 88 | } 89 | descriptorSize += node->length; 90 | } 91 | 92 | int DynamicHID_::SendReport(uint8_t id, const void* data, int len) 93 | { 94 | //auto ret = USB_Send(pluggedEndpoint, 0, 0); 95 | //if (ret < 0) return ret; 96 | auto ret2 = USB_Send(pluggedEndpoint, data, len); 97 | auto ret = USB_Send(pluggedEndpoint | TRANSFER_RELEASE, &id, 1); 98 | //auto ret2 = USB_Send(pluggedEndpoint, data, len); 99 | //if (ret2 < 0) return ret2; 100 | return ret + ret2; 101 | } 102 | 103 | bool DynamicHID_::setup(USBSetup& setup) 104 | { 105 | if (pluggedInterface != setup.wIndex) { 106 | return false; 107 | } 108 | 109 | uint8_t request = setup.bRequest; 110 | uint8_t requestType = setup.bmRequestType; 111 | 112 | if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE) 113 | { 114 | if (request == DYNAMIC_HID_GET_REPORT) { 115 | // TODO: DYNAMIC_HID_GetReport(); 116 | return true; 117 | } 118 | if (request == DYNAMIC_HID_GET_PROTOCOL) { 119 | // TODO: Send8(protocol); 120 | return true; 121 | } 122 | if (request == DYNAMIC_HID_GET_IDLE) { 123 | // TODO: Send8(idle); 124 | } 125 | } 126 | 127 | if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) 128 | { 129 | if (request == DYNAMIC_HID_SET_PROTOCOL) { 130 | // The USB Host tells us if we are in boot or report mode. 131 | // This only works with a real boot compatible device. 132 | protocol = setup.wValueL; 133 | return true; 134 | } 135 | if (request == DYNAMIC_HID_SET_IDLE) { 136 | idle = setup.wValueL; 137 | return true; 138 | } 139 | if (request == DYNAMIC_HID_SET_REPORT) 140 | { 141 | //uint8_t reportID = setup.wValueL; 142 | //uint16_t length = setup.wLength; 143 | //uint8_t data[length]; 144 | // Make sure to not read more data than USB_EP_SIZE. 145 | // You can read multiple times through a loop. 146 | // The first byte (may!) contain the reportID on a multreport. 147 | //USB_RecvControl(data, length); 148 | } 149 | } 150 | 151 | return false; 152 | } 153 | 154 | DynamicHID_::DynamicHID_(void) : PluggableUSBModule(1, 1, epType), 155 | rootNode(NULL), descriptorSize(0), 156 | protocol(DYNAMIC_HID_REPORT_PROTOCOL), idle(1) 157 | { 158 | epType[0] = EP_TYPE_INTERRUPT_IN; 159 | PluggableUSB().plug(this); 160 | } 161 | 162 | int DynamicHID_::begin(void) 163 | { 164 | return 0; 165 | } 166 | 167 | #endif /* if defined(USBCON) */ 168 | -------------------------------------------------------------------------------- /Joystick/src/DynamicHID/DynamicHID.h: -------------------------------------------------------------------------------- 1 | /* 2 | Modified by Matthew Heironimus to support HID Report Descriptors to be in 3 | standard RAM in addition to program memory (PROGMEM). 4 | 5 | Copyright (c) 2015, Arduino LLC 6 | Original code (pre-library): Copyright (c) 2011, Peter Barrett 7 | 8 | Permission to use, copy, modify, and/or distribute this software for 9 | any purpose with or without fee is hereby granted, provided that the 10 | above copyright notice and this permission notice appear in all copies. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 14 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 15 | BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES 16 | OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 17 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 18 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 19 | SOFTWARE. 20 | */ 21 | 22 | #ifndef DYNAMIC_HID_h 23 | #define DYNAMIC_HID_h 24 | 25 | #include 26 | #include 27 | #include "PluggableUSB.h" 28 | 29 | #if defined(USBCON) 30 | 31 | #define _USING_DYNAMIC_HID 32 | 33 | // DYNAMIC_HID 'Driver' 34 | // ------------ 35 | #define DYNAMIC_HID_GET_REPORT 0x01 36 | #define DYNAMIC_HID_GET_IDLE 0x02 37 | #define DYNAMIC_HID_GET_PROTOCOL 0x03 38 | #define DYNAMIC_HID_SET_REPORT 0x09 39 | #define DYNAMIC_HID_SET_IDLE 0x0A 40 | #define DYNAMIC_HID_SET_PROTOCOL 0x0B 41 | 42 | #define DYNAMIC_HID_DESCRIPTOR_TYPE 0x21 43 | #define DYNAMIC_HID_REPORT_DESCRIPTOR_TYPE 0x22 44 | #define DYNAMIC_HID_PHYSICAL_DESCRIPTOR_TYPE 0x23 45 | 46 | // HID subclass HID1.11 Page 8 4.2 Subclass 47 | #define DYNAMIC_HID_SUBCLASS_NONE 0 48 | #define DYNAMIC_HID_SUBCLASS_BOOT_INTERFACE 1 49 | 50 | // HID Keyboard/Mouse bios compatible protocols HID1.11 Page 9 4.3 Protocols 51 | #define DYNAMIC_HID_PROTOCOL_NONE 0 52 | #define DYNAMIC_HID_PROTOCOL_KEYBOARD 1 53 | #define DYNAMIC_HID_PROTOCOL_MOUSE 2 54 | 55 | // Normal or bios protocol (Keyboard/Mouse) HID1.11 Page 54 7.2.5 Get_Protocol Request 56 | // "protocol" variable is used for this purpose. 57 | #define DYNAMIC_HID_BOOT_PROTOCOL 0 58 | #define DYNAMIC_HID_REPORT_PROTOCOL 1 59 | 60 | // HID Request Type HID1.11 Page 51 7.2.1 Get_Report Request 61 | #define DYNAMIC_HID_REPORT_TYPE_INPUT 1 62 | #define DYNAMIC_HID_REPORT_TYPE_OUTPUT 2 63 | #define DYNAMIC_HID_REPORT_TYPE_FEATURE 3 64 | 65 | typedef struct 66 | { 67 | uint8_t len; // 9 68 | uint8_t dtype; // 0x21 69 | uint8_t addr; 70 | uint8_t versionL; // 0x101 71 | uint8_t versionH; // 0x101 72 | uint8_t country; 73 | uint8_t desctype; // 0x22 report 74 | uint8_t descLenL; 75 | uint8_t descLenH; 76 | } DYNAMIC_HIDDescDescriptor; 77 | 78 | typedef struct 79 | { 80 | InterfaceDescriptor hid; 81 | DYNAMIC_HIDDescDescriptor desc; 82 | EndpointDescriptor in; 83 | } DYNAMIC_HIDDescriptor; 84 | 85 | class DynamicHIDSubDescriptor { 86 | public: 87 | DynamicHIDSubDescriptor *next = NULL; 88 | DynamicHIDSubDescriptor(const void *d, const uint16_t l, const bool ipm = true) : data(d), length(l), inProgMem(ipm) { } 89 | 90 | const void* data; 91 | const uint16_t length; 92 | const bool inProgMem; 93 | }; 94 | 95 | class DynamicHID_ : public PluggableUSBModule 96 | { 97 | public: 98 | DynamicHID_(void); 99 | int begin(void); 100 | int SendReport(uint8_t id, const void* data, int len); 101 | void AppendDescriptor(DynamicHIDSubDescriptor* node); 102 | 103 | protected: 104 | // Implementation of the PluggableUSBModule 105 | int getInterface(uint8_t* interfaceCount); 106 | int getDescriptor(USBSetup& setup); 107 | bool setup(USBSetup& setup); 108 | uint8_t getShortName(char* name); 109 | 110 | private: 111 | uint8_t epType[1]; 112 | 113 | DynamicHIDSubDescriptor* rootNode; 114 | uint16_t descriptorSize; 115 | 116 | uint8_t protocol; 117 | uint8_t idle; 118 | }; 119 | 120 | // Replacement for global singleton. 121 | // This function prevents static-initialization-order-fiasco 122 | // https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use 123 | DynamicHID_& DynamicHID(); 124 | 125 | #define D_HIDREPORT(length) { 9, 0x21, 0x01, 0x01, 0, 1, 0x22, lowByte(length), highByte(length) } 126 | 127 | #endif // USBCON 128 | 129 | #endif // DYNAMIC_HID_h 130 | -------------------------------------------------------------------------------- /Joystick/src/Joystick.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Joystick.cpp 3 | 4 | Copyright (c) 2015-2017, Matthew Heironimus 5 | 6 | This library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Lesser General Public 8 | License as published by the Free Software Foundation; either 9 | version 2.1 of the License, or (at your option) any later version. 10 | 11 | This library is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public 17 | License along with this library; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #include "Joystick.h" 22 | 23 | #if defined(_USING_DYNAMIC_HID) 24 | 25 | #define JOYSTICK_REPORT_ID_INDEX 7 26 | #define JOYSTICK_AXIS_MINIMUM 0 27 | #define JOYSTICK_AXIS_MAXIMUM 255 28 | 29 | #define JOYSTICK_INCLUDE_X_AXIS B00000001 30 | #define JOYSTICK_INCLUDE_Y_AXIS B00000010 31 | #define JOYSTICK_INCLUDE_Z_AXIS B00000100 32 | #define JOYSTICK_INCLUDE_RZ_AXIS B00100000 33 | 34 | Joystick_::Joystick_() 35 | { 36 | // Set the USB HID Report ID 37 | _hidReportId = JOYSTICK_DEFAULT_REPORT_ID; 38 | 39 | // Save Joystick Settings 40 | _buttonCount = 16; 41 | _includeAxisFlags |= JOYSTICK_INCLUDE_X_AXIS | JOYSTICK_INCLUDE_Y_AXIS | JOYSTICK_INCLUDE_Z_AXIS | JOYSTICK_INCLUDE_RZ_AXIS; 42 | _hatSwitchCount = 1; 43 | 44 | // Build Joystick HID Report Description 45 | 46 | // Button Calculations 47 | uint8_t buttonsInLastByte = _buttonCount % 8; 48 | uint8_t buttonPaddingBits = 0; 49 | if (buttonsInLastByte > 0) 50 | { 51 | buttonPaddingBits = 8 - buttonsInLastByte; 52 | } 53 | 54 | // Axis Calculations 55 | uint8_t axisCount = 4; 56 | 57 | uint8_t tempHidReportDescriptor[150]; 58 | int hidReportDescriptorSize = 0; 59 | 60 | // USAGE_PAGE (Generic Desktop) 61 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x05; 62 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 63 | 64 | // USAGE (Joystick - 0x04; Gamepad - 0x05; Multi-axis Controller - 0x08) 65 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 66 | tempHidReportDescriptor[hidReportDescriptorSize++] = JOYSTICK_TYPE_GAMEPAD; 67 | 68 | // COLLECTION (Application) 69 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xa1; 70 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 71 | 72 | // REPORT_ID (Default: 3) 73 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x85; 74 | //tempHidReportDescriptor[hidReportDescriptorSize++] = _hidReportId; 75 | 76 | // USAGE_PAGE (Button) 77 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x05; 78 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 79 | 80 | // USAGE_MINIMUM (Button 1) 81 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x19; 82 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 83 | 84 | // USAGE_MAXIMUM (Button 32) 85 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x29; 86 | tempHidReportDescriptor[hidReportDescriptorSize++] = _buttonCount; 87 | 88 | // LOGICAL_MINIMUM (0) 89 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x15; 90 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 91 | 92 | // LOGICAL_MAXIMUM (1) 93 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x25; 94 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 95 | 96 | // REPORT_SIZE (1) 97 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; 98 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 99 | 100 | // REPORT_COUNT (# of buttons) 101 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 102 | tempHidReportDescriptor[hidReportDescriptorSize++] = _buttonCount; 103 | 104 | // UNIT_EXPONENT (0) 105 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x55; 106 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 107 | 108 | // UNIT (None) 109 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x65; 110 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 111 | 112 | // INPUT (Data,Var,Abs) 113 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; 114 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; 115 | 116 | if (buttonPaddingBits > 0) { 117 | 118 | // REPORT_SIZE (1) 119 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; 120 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 121 | 122 | // REPORT_COUNT (# of padding bits) 123 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 124 | tempHidReportDescriptor[hidReportDescriptorSize++] = buttonPaddingBits; 125 | 126 | // INPUT (Const,Var,Abs) 127 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; 128 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x03; 129 | 130 | } // Padding Bits Needed 131 | 132 | // Buttons 133 | 134 | // USAGE_PAGE (Generic Desktop) 135 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x05; 136 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 137 | 138 | // USAGE (Hat Switch) 139 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 140 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x39; 141 | 142 | // LOGICAL_MINIMUM (0) 143 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x15; 144 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 145 | 146 | // LOGICAL_MAXIMUM (7) 147 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x25; 148 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x07; 149 | 150 | // PHYSICAL_MINIMUM (0) 151 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x35; 152 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 153 | 154 | // PHYSICAL_MAXIMUM (315) 155 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x46; 156 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x3B; 157 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 158 | 159 | // UNIT (Eng Rot:Angular Pos) 160 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x65; 161 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x14; 162 | 163 | // REPORT_SIZE (4) 164 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; 165 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x04; 166 | 167 | // REPORT_COUNT (1) 168 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 169 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 170 | 171 | // INPUT (Data,Var,Abs) 172 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; 173 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; 174 | 175 | // Use Padding Bits 176 | 177 | // REPORT_SIZE (1) 178 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; 179 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 180 | 181 | // REPORT_COUNT (4) 182 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 183 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x04; 184 | 185 | // INPUT (Const,Var,Abs) 186 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; 187 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 188 | 189 | // Hat Switch 190 | 191 | // USAGE (Pointer) 192 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 193 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 194 | 195 | // LOGICAL_MAXIMUM (255) 196 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x26; 197 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xFF; 198 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 199 | 200 | // PHYSICAL_MAXIMUM (255) 201 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x46; 202 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xFF; 203 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 204 | 205 | // REPORT_SIZE (8) 206 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; 207 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x08; 208 | 209 | // REPORT_COUNT (axisCount) 210 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 211 | tempHidReportDescriptor[hidReportDescriptorSize++] = axisCount; 212 | 213 | // COLLECTION (Physical) 214 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xA1; 215 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 216 | 217 | // USAGE (X) 218 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 219 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x30; 220 | 221 | // USAGE (Y) 222 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 223 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x31; 224 | 225 | // USAGE (Z) 226 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 227 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x32; 228 | 229 | // USAGE (Rz) 230 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 231 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x35; 232 | 233 | // INPUT (Data,Var,Abs) 234 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; 235 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; 236 | 237 | // END_COLLECTION (Physical) 238 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xc0; 239 | 240 | // X, Y, Z, and Rz Axis 241 | 242 | // END_COLLECTION 243 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xc0; 244 | 245 | // Create a copy of the HID Report Descriptor template that is just the right size 246 | uint8_t *customHidReportDescriptor = new uint8_t[hidReportDescriptorSize]; 247 | memcpy(customHidReportDescriptor, tempHidReportDescriptor, hidReportDescriptorSize); 248 | 249 | // Register HID Report Description 250 | DynamicHIDSubDescriptor *node = new DynamicHIDSubDescriptor(customHidReportDescriptor, hidReportDescriptorSize, false); 251 | DynamicHID().AppendDescriptor(node); 252 | 253 | 254 | if (_buttonCount > 0) { 255 | _buttonValuesArraySize = _buttonCount / 8; 256 | if ((_buttonCount % 8) > 0) { 257 | _buttonValuesArraySize++; 258 | } 259 | _buttonValues = new uint8_t[_buttonValuesArraySize]; 260 | } 261 | 262 | // Calculate HID Report Size 263 | _hidReportSize = _buttonValuesArraySize; 264 | _hidReportSize += (axisCount); 265 | _hidReportSize += (_hatSwitchCount > 0); 266 | 267 | // Initalize Joystick State 268 | _xAxis = 0; 269 | _yAxis = 0; 270 | _zAxis = 0; 271 | _zAxisRotation = 0; 272 | 273 | _hatSwitchValue = JOYSTICK_HATSWITCH_RELEASE; 274 | 275 | for (int index = 0; index < _buttonValuesArraySize; index++) 276 | { 277 | _buttonValues[index] = 0; 278 | } 279 | } 280 | 281 | void Joystick_::begin(bool initAutoSendState) 282 | { 283 | _autoSendState = initAutoSendState; 284 | sendState(); 285 | } 286 | 287 | void Joystick_::end() 288 | { 289 | } 290 | 291 | void Joystick_::setButton(uint8_t button, uint8_t value) 292 | { 293 | if (value == 0) 294 | { 295 | releaseButton(button); 296 | } 297 | else 298 | { 299 | pressButton(button); 300 | } 301 | } 302 | void Joystick_::pressButton(uint8_t button) 303 | { 304 | if (button >= _buttonCount) return; 305 | 306 | int index = button / 8; 307 | int bit = button % 8; 308 | 309 | bitSet(_buttonValues[index], bit); 310 | if (_autoSendState) sendState(); 311 | } 312 | void Joystick_::releaseButton(uint8_t button) 313 | { 314 | if (button >= _buttonCount) return; 315 | 316 | int index = button / 8; 317 | int bit = button % 8; 318 | 319 | bitClear(_buttonValues[index], bit); 320 | if (_autoSendState) sendState(); 321 | } 322 | 323 | void Joystick_::setXAxis(int8_t value) 324 | { 325 | _xAxis = value; 326 | if (_autoSendState) sendState(); 327 | } 328 | void Joystick_::setYAxis(int8_t value) 329 | { 330 | _yAxis = value; 331 | if (_autoSendState) sendState(); 332 | } 333 | void Joystick_::setZAxis(int8_t value) 334 | { 335 | _zAxis = value; 336 | if (_autoSendState) sendState(); 337 | } 338 | 339 | void Joystick_::setRzAxis(int8_t value) 340 | { 341 | _zAxisRotation = value; 342 | if (_autoSendState) sendState(); 343 | } 344 | 345 | void Joystick_::setHatSwitch(int16_t value) 346 | { 347 | _hatSwitchValue = value; 348 | if (_autoSendState) sendState(); 349 | } 350 | 351 | void Joystick_::sendState() 352 | { 353 | uint8_t data[_hidReportSize]; 354 | int index = 0; 355 | 356 | // Load Button State 357 | for (; index < _buttonValuesArraySize; index++) 358 | { 359 | data[index] = _buttonValues[index]; 360 | } 361 | 362 | // Pack hat-switch states into a single byte 363 | uint8_t hatSwitchConvertedValue = 8; 364 | if (_hatSwitchValue>=0) hatSwitchConvertedValue = ((((_hatSwitchValue% 360) / 45))); 365 | data[index++] = hatSwitchConvertedValue; 366 | // Set Axis Values 367 | data[index++] = _xAxis; 368 | data[index++] = _yAxis; 369 | data[index++] = _zAxis; 370 | data[index++] = _zAxisRotation; 371 | 372 | DynamicHID().SendReport(_hidReportId, data, _hidReportSize); 373 | } 374 | 375 | void Joystick_::sendState(uint8_t *data) 376 | { 377 | DynamicHID().SendReport(_hidReportId, data, _hidReportSize); 378 | } 379 | 380 | #endif 381 | -------------------------------------------------------------------------------- /Joystick/src/Joystick.h: -------------------------------------------------------------------------------- 1 | /* 2 | Joystick.h 3 | 4 | Copyright (c) 2015-2017, Matthew Heironimus 5 | 6 | This library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Lesser General Public 8 | License as published by the Free Software Foundation; either 9 | version 2.1 of the License, or (at your option) any later version. 10 | 11 | This library is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public 17 | License along with this library; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #ifndef JOYSTICK_h 22 | #define JOYSTICK_h 23 | 24 | #include 25 | 26 | #if ARDUINO < 10606 27 | #error The Joystick library requires Arduino IDE 1.6.6 or greater. Please update your IDE. 28 | #endif // ARDUINO < 10606 29 | 30 | #if ARDUINO > 10606 31 | #if !defined(USBCON) 32 | #error The Joystick library can only be used with a USB MCU (e.g. Arduino Leonardo, Arduino Micro, etc.). 33 | #endif // !defined(USBCON) 34 | #endif // ARDUINO > 10606 35 | 36 | #if !defined(_USING_DYNAMIC_HID) 37 | 38 | #warning "Using legacy HID core (non pluggable)" 39 | 40 | #else // !defined(_USING_DYNAMIC_HID) 41 | 42 | //================================================================================ 43 | // Joystick (Gamepad) 44 | 45 | #define JOYSTICK_DEFAULT_REPORT_ID 0x01 46 | #define JOYSTICK_DEFAULT_BUTTON_COUNT 16 47 | #define JOYSTICK_DEFAULT_AXIS_MINIMUM 0 48 | #define JOYSTICK_DEFAULT_AXIS_MAXIMUM 255 49 | #define JOYSTICK_DEFAULT_SIMULATOR_MINIMUM 0 50 | #define JOYSTICK_DEFAULT_SIMULATOR_MAXIMUM 1023 51 | #define JOYSTICK_DEFAULT_HATSWITCH_COUNT 1 52 | #define JOYSTICK_HATSWITCH_COUNT_MAXIMUM 1 53 | #define JOYSTICK_HATSWITCH_RELEASE -1 54 | #define JOYSTICK_TYPE_JOYSTICK 0x04 55 | #define JOYSTICK_TYPE_GAMEPAD 0x05 56 | #define JOYSTICK_TYPE_MULTI_AXIS 0x08 57 | 58 | class Joystick_ 59 | { 60 | private: 61 | 62 | // Joystick State 63 | int8_t _xAxis; 64 | int8_t _yAxis; 65 | int8_t _zAxis; 66 | int8_t _zAxisRotation; 67 | int16_t _hatSwitchValue; 68 | uint8_t *_buttonValues = NULL; 69 | 70 | // Joystick Settings 71 | bool _autoSendState; 72 | uint8_t _buttonCount; 73 | uint8_t _buttonValuesArraySize = 0; 74 | uint8_t _hatSwitchCount; 75 | uint8_t _includeAxisFlags; 76 | int8_t _xAxisMinimum = JOYSTICK_DEFAULT_AXIS_MINIMUM; 77 | int8_t _xAxisMaximum = JOYSTICK_DEFAULT_AXIS_MAXIMUM; 78 | int8_t _yAxisMinimum = JOYSTICK_DEFAULT_AXIS_MINIMUM; 79 | int8_t _yAxisMaximum = JOYSTICK_DEFAULT_AXIS_MAXIMUM; 80 | int8_t _zAxisMinimum = JOYSTICK_DEFAULT_AXIS_MINIMUM; 81 | int8_t _zAxisMaximum = JOYSTICK_DEFAULT_AXIS_MAXIMUM; 82 | int8_t _rzAxisMinimum = JOYSTICK_DEFAULT_AXIS_MINIMUM; 83 | int8_t _rzAxisMaximum = JOYSTICK_DEFAULT_AXIS_MAXIMUM; 84 | 85 | uint8_t _hidReportId; 86 | uint8_t _hidReportSize; 87 | 88 | public: 89 | Joystick_(); 90 | 91 | void begin(bool initAutoSendState = true); 92 | void end(); 93 | 94 | // Set Axis Values 95 | void setXAxis(int8_t value); 96 | void setYAxis(int8_t value); 97 | void setZAxis(int8_t value); 98 | void setRzAxis(int8_t value); 99 | 100 | void setButton(uint8_t button, uint8_t value); 101 | void pressButton(uint8_t button); 102 | void releaseButton(uint8_t button); 103 | 104 | void setHatSwitch(int16_t value); 105 | 106 | void sendState(); 107 | void sendState(uint8_t *data); 108 | }; 109 | 110 | #endif // !defined(_USING_DYNAMIC_HID) 111 | #endif // JOYSTICK_h 112 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arduino SwitchJoystick Library 2 | #### Version 1.0.0 3 | This library can be used with Arduino IDE 1.6.6 (or above) to add one or more joysticks (or gamepads) to the list of HID devices an [Arduino Leonardo](https://www.arduino.cc/en/Main/ArduinoBoardLeonardo) or [Arduino Micro](https://www.arduino.cc/en/Main/ArduinoBoardMicro) (or any Arduino clone that is based on the ATmega32u4) can support. This will not work with Arduino IDE 1.6.5 (or below) or with non-32u4 based Arduino devices (e.g. Arduino UNO, Arduino MEGA, etc.). 4 | 5 | Based on the Arduino Joystick Library (https://github.com/MHeironimus/ArduinoJoystickLibrary) by Matthew Heironimus and on the Switch LUFA Proof-of-Concept (https://github.com/progmem/Switch-Fightstick) by Jason DeLeon. 6 | ## Features 7 | The gamepad has the following features: 8 | - 16 buttons (14 used) 9 | - 1 Hat Switch 10 | - X and Y Axis(Left Stick), 8-bit precision 11 | - Z and Z Axis Rotation (Rigth Stick), 8-bit precision 12 | 13 | ## Installation Instructions 14 | Since you need to modify a file that will affect all other Arduino boards used in your system, it is STRONGLY suggested to install this library in a separate Arduino installation. 15 | Copy the `SwitchJoystick` folder to the Arduino libraries folder. Once the folder has been copied, the SwitchJoystick library should appear in the Arduino IDE list of libraries. The example should also appear in the examples menu in the Arduino IDE. 16 | Open the `boards.txt` file under `hardware\arduino\avr` and append to it the following entry: 17 | ``` 18 | myboard.name=SwitchJoystick 19 | myboard.vid.0=0x0F0D 20 | myboard.pid.0=0x8092 21 | myboard.vid.1=0x0F0D 22 | myboard.pid.1=0x0092 23 | 24 | myboard.upload.tool=avrdude 25 | myboard.upload.protocol=avr109 26 | myboard.upload.maximum_size=28672 27 | myboard.upload.maximum_data_size=2560 28 | myboard.upload.speed=57600 29 | myboard.upload.disable_flushing=true 30 | myboard.upload.use_1200bps_touch=true 31 | myboard.upload.wait_for_upload_port=true 32 | 33 | myboard.bootloader.tool=avrdude 34 | myboard.bootloader.low_fuses=0xff 35 | myboard.bootloader.high_fuses=0xd8 36 | myboard.bootloader.extended_fuses=0xcb 37 | myboard.bootloader.file=caterina/Caterina-Leonardo.hex 38 | myboard.bootloader.unlock_bits=0x3F 39 | myboard.bootloader.lock_bits=0x2F 40 | 41 | myboard.build.mcu=atmega32u4 42 | myboard.build.f_cpu=16000000L 43 | myboard.build.vid=0x0F0D 44 | myboard.build.pid=0x0092 45 | myboard.build.usb_product="POKKEN CONTROLLER" 46 | myboard.build.board=AVR_LEONARDO 47 | myboard.build.core=arduino 48 | myboard.build.variant=leonardo 49 | myboard.build.extra_flags={build.usb_flags} 50 | ``` 51 | You can use this board entry as a template for other boards, in case you're not using an Arduino Leonardo; the important things are that the board must be ATmega32u4-based, that you replace `myboard` with a unique name, and that you keep the `vid`, `pid`, and `usb_product` as in this entry. 52 | Finally, open the `platform.txt` file under `hardware\arduino\avr`, and at the end of the file replace `build.usb_manufacturer="Unknown"` with `build.usb_manufacturer="HORI CO.,LTD."`. 53 | 54 | ## Alternative Menu Installation 55 | Do the same procedure but in the `boards.txt` file under `hardware\arduino\avr` append to it the following entry: 56 | ``` 57 | menu.build_variant=Build Variant 58 | leonardo.menu.build_variant.leonardo=Leonardo 59 | 60 | leonardo.menu.build_variant.leonardo_switch=SwitchJoystick 61 | 62 | leonardo.menu.build_variant.leonardo_switch.name=SwitchJoystick 63 | leonardo.menu.build_variant.leonardo_switch.vid.0=0x0F0D 64 | leonardo.menu.build_variant.leonardo_switch.pid.0=0x8092 65 | leonardo.menu.build_variant.leonardo_switch.vid.1=0x0F0D 66 | leonardo.menu.build_variant.leonardo_switch.pid.1=0x0092 67 | 68 | leonardo.menu.build_variant.leonardo_switch.upload.tool=avrdude 69 | leonardo.menu.build_variant.leonardo_switch.upload.protocol=avr109 70 | leonardo.menu.build_variant.leonardo_switch.upload.maximum_size=28672 71 | leonardo.menu.build_variant.leonardo_switch.upload.maximum_data_size=2560 72 | leonardo.menu.build_variant.leonardo_switch.upload.speed=57600 73 | leonardo.menu.build_variant.leonardo_switch.upload.disable_flushing=true 74 | leonardo.menu.build_variant.leonardo_switch.upload.use_1200bps_touch=true 75 | leonardo.menu.build_variant.leonardo_switch.upload.wait_for_upload_port=true 76 | 77 | leonardo.menu.build_variant.leonardo_switch.bootloader.tool=avrdude 78 | leonardo.menu.build_variant.leonardo_switch.bootloader.low_fuses=0xff 79 | leonardo.menu.build_variant.leonardo_switch.bootloader.high_fuses=0xd8 80 | leonardo.menu.build_variant.leonardo_switch.bootloader.extended_fuses=0xcb 81 | leonardo.menu.build_variant.leonardo_switch.bootloader.file=caterina/Caterina-Leonardo.hex 82 | leonardo.menu.build_variant.leonardo_switch.bootloader.unlock_bits=0x3F 83 | leonardo.menu.build_variant.leonardo_switch.bootloader.lock_bits=0x2F 84 | 85 | leonardo.menu.build_variant.leonardo_switch.build.mcu=atmega32u4 86 | leonardo.menu.build_variant.leonardo_switch.build.f_cpu=16000000L 87 | leonardo.menu.build_variant.leonardo_switch.build.vid=0x0F0D 88 | leonardo.menu.build_variant.leonardo_switch.build.pid=0x0092 89 | leonardo.menu.build_variant.leonardo_switch.build.usb_product="POKKEN CONTROLLER" 90 | leonardo.menu.build_variant.leonardo_switch.build.board=AVR_LEONARDO 91 | leonardo.menu.build_variant.leonardo_switch.build.core=arduino 92 | leonardo.menu.build_variant.leonardo_switch.build.variant=leonardo 93 | leonardo.menu.build_variant.leonardo_switch.build.extra_flags={build.usb_flags} 94 | 95 | leonardo.menu.build_variant.leonardo_switch.build.usb_manufacturer="HORI CO.,LTD." 96 | ``` 97 | Restart the IDE and now in the board Arduino Leonardo menu you should see a "Buid Variant" menu. 98 | 99 | ## Example 100 | The following example Arduino sketch file is included in this library: 101 | 102 | - `JoystickTest` - Simple test of the SwitchJoystick library. Use 12 buttons with pullups connected to pins A0 to A3 and to pins 4 to 15 to control all the joystick features. 103 | 104 | - `JoystickIMUTest` - Simple test of the SwitchJoystick library. Use an MPU6050 as a joystick. 105 | 106 | ## Mapping 107 | 108 | | Button | Switch | 109 | | ------- | ------- | 110 | | 0 | Y | 111 | | 1 | B | 112 | | 2 | A | 113 | | 3 | Z | 114 | | 4 | L | 115 | | 5 | R | 116 | | 6 | Zl | 117 | | 7 | Zr | 118 | | 8 | - | 119 | | 9 | + | 120 | | 10 | Lstick | 121 | | 11 | Rstick | 122 | | 12 | Home | 123 | | 13 | Capture | 124 | 125 | | Axis | Switch | 126 | | ------- | -------- | 127 | | X,Y | LeftJoy | 128 | | Z,Rz | RightJoy | 129 | 130 | | Hat/D-pad | Switch | 131 | | --------- | ------- | 132 | | 0 | UP | 133 | | 90 | RIGHT | 134 | | 180 | DOWN | 135 | | 270 | LEFT | 136 | | -1 | RELEASE | 137 | 138 | ## SwitchJoystick Library API 139 | The following API is available if the SwitchJoystick library in included in a sketch file. 140 | 141 | ### SwitchJoystick\_() 142 | Constructor used to initialize and setup the Joystick. 143 | 144 | ### SwitchJoystick.begin(bool initAutoSendState) 145 | Starts emulating a game controller connected to a Nintendo Switch. By default, all methods update the game controller state immediately. If `initAutoSendState` is set to `false`, the `sendState` method must be called to update the game controller state. 146 | 147 | ### SwitchJoystick.end() 148 | Stops the game controller emulation to a connected Nintendo Switch (not actually implemented). 149 | 150 | ### SwitchJoystick.setXAxis(uint8_t value) 151 | Sets the X axis value. The range is 152 | 153 | ### Joystick.setYAxis(uint8_t value) 154 | Sets the Y axis value. 155 | 156 | ### Joystick.setZAxis(uint8_t value) 157 | Sets the Z axis value. 158 | 159 | ### Joystick.setRzAxis(uint8_t value) 160 | Sets the Z axis rotation value. 161 | 162 | ### Joystick.setButton(uint8_t button, uint8_t value) 163 | Sets the state (`0` or `1`) of the specified button (range: `0` - `15`). The button is the 0-based button number (i.e. button #1 is `0`, button #2 is `1`, etc.). The value is `1` if the button is pressed and `0` if the button is released. 164 | 165 | ### Joystick.pressButton(uint8_t button) 166 | Press the indicated button (range: `0` - `15`). The button is the 0-based button number (i.e. button #1 is `0`, button #2 is `1`, etc.). 167 | 168 | ### Joystick.releaseButton(uint8_t button) 169 | Release the indicated button (range: `0` - `15`). The button is the 0-based button number (i.e. button #1 is `0`, button #2 is `1`, etc.). 170 | 171 | ### Joystick.setHatSwitch(int16_t value) 172 | Sets the value of the D-pad (hat switch). The value is from 0° to 360°, but in 45° increments. Any value less than 45° will be rounded down (i.e. 44° is rounded down to 0°, 89° is rounded down to 45°, etc.). Set the value to `JOYSTICK_HATSWITCH_RELEASE` or `-1` to release the hat switch. 173 | 174 | ### Joystick.sendState() 175 | Sends the updated joystick state to the Nintendo Switch. Only needs to be called if `AutoSendState` is `false` (see `SwitchJoystick.begin` for more details). -------------------------------------------------------------------------------- /deploy.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | IF EXIST "%HOMEDRIVE%%HOMEPATH%\Documents\Arduino\libraries\Joystick\" rmdir /Q /S "%HOMEDRIVE%%HOMEPATH%\Documents\Arduino\libraries\Joystick\" 3 | xcopy /E /I Joystick\*.* "%HOMEDRIVE%%HOMEPATH%\Documents\Arduino\libraries\Joystick" -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | INSTALLPATH="$HOME/Arduino/libraries/Joystick" 3 | 4 | rm -r -d -f "$INSTALLPATH" 5 | cp -r "Joystick" "$INSTALLPATH" 6 | -------------------------------------------------------------------------------- /examples/SwitchJoystickIMUTest/SwitchJoystickIMUTest.ino: -------------------------------------------------------------------------------- 1 | // Program used to test the Nintento Switch Joystick object on the 2 | // Arduino Leonardo or Arduino Micro. 3 | // 4 | // Michele Perla for Hackerloop 5 | //------------------------------------------------------------ 6 | // 7 | 8 | #include "I2Cdev.h" 9 | #include "MPU6050_6Axis_MotionApps20.h" 10 | #include "Wire.h" 11 | #include "SwitchJoystick.h" 12 | 13 | // Create Joystick 14 | SwitchJoystick_ Joystick; 15 | 16 | MPU6050 mpu; 17 | 18 | const int ADC_Max = 1023; // 10 bit 19 | 20 | // Set to true to test "Auto Send" mode or false to test "Manual Send" mode. 21 | //const bool testAutoSendMode = true; 22 | const bool testAutoSendMode = false; 23 | 24 | // MPU6050 25 | #define SCALING 4 // max Joystick value at 90deg/SCALING. 26 | 27 | #define INTERRUPT_PIN 7 // use pin 2 on Arduino Uno & most boards 28 | 29 | // MPU control/status vars 30 | bool dmpReady = false; // set true if DMP init was successful 31 | uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU 32 | uint8_t devStatus; // return status after each device operation (0 = success, !0 = error) 33 | uint16_t packetSize; // expected DMP packet size (default is 42 bytes) 34 | uint16_t fifoCount; // count of all bytes currently in FIFO 35 | uint8_t fifoBuffer[64]; // FIFO storage buffer 36 | 37 | // orientation/motion vars 38 | Quaternion q; // [w, x, y, z] quaternion container 39 | VectorFloat gravity; // [x, y, z] gravity vector 40 | float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector 41 | 42 | float pitchOffset = 0; 43 | float rollOffset = 0; 44 | 45 | volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high 46 | void dmpDataReady() { mpuInterrupt = true; } 47 | 48 | int leftJoyX = 128; 49 | int leftJoyY = 128; 50 | 51 | unsigned long onTime = 0; 52 | 53 | #define BUTTON_PIN A0 54 | bool buttonPressed = false; 55 | 56 | void setup() { 57 | 58 | if (testAutoSendMode) 59 | { 60 | Joystick.begin(); 61 | } 62 | else 63 | { 64 | Joystick.begin(false); 65 | } 66 | 67 | pinMode(A0, INPUT_PULLUP); 68 | pinMode(A1, INPUT_PULLUP); 69 | pinMode(A2, INPUT_PULLUP); 70 | pinMode(A3, INPUT_PULLUP); 71 | 72 | pinMode(0, INPUT_PULLUP); 73 | pinMode(1, INPUT_PULLUP); 74 | //pinMode(2, INPUT_PULLUP); SDA 75 | //pinMode(3, INPUT_PULLUP); SCL 76 | pinMode(4, INPUT_PULLUP); 77 | pinMode(5, INPUT_PULLUP); 78 | pinMode(6, INPUT_PULLUP); 79 | //pinMode(7, INPUT_PULLUP); INT MPU 80 | //pinMode(8, INPUT_PULLUP); Wierd 81 | pinMode(9, INPUT_PULLUP); 82 | pinMode(10, INPUT_PULLUP); 83 | pinMode(16, INPUT_PULLUP); 84 | pinMode(14, INPUT_PULLUP); 85 | pinMode(15, INPUT_PULLUP); 86 | 87 | Joystick.setXAxis(128); 88 | Joystick.setYAxis(128); 89 | Joystick.setZAxis(128); 90 | Joystick.setRzAxis(128); 91 | Joystick.sendState(); 92 | 93 | // set up MPU6050 94 | Wire.begin(); 95 | Wire.setClock(400000); 96 | mpu.initialize(); 97 | pinMode(INTERRUPT_PIN, INPUT); 98 | 99 | devStatus = mpu.dmpInitialize(); 100 | 101 | if (devStatus == 0) { 102 | mpu.setDMPEnabled(true); 103 | 104 | attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING); 105 | mpuIntStatus = mpu.getIntStatus(); 106 | dmpReady = true; 107 | packetSize = mpu.dmpGetFIFOPacketSize(); 108 | } else { 109 | fail(); 110 | } 111 | } 112 | 113 | void loop() { 114 | 115 | // push button: when released, take current pitch & roll as offset 116 | if (digitalRead(BUTTON_PIN) == LOW && !buttonPressed) { 117 | buttonPressed = true; 118 | } else if (digitalRead(BUTTON_PIN) == HIGH && buttonPressed) { 119 | pitchOffset = ypr[1]; 120 | rollOffset = ypr[2]; 121 | buttonPressed = false; 122 | } 123 | 124 | if (!dmpReady) return; 125 | 126 | while (!mpuInterrupt && fifoCount < packetSize) { 127 | if (mpuInterrupt && fifoCount < packetSize) { 128 | fifoCount = mpu.getFIFOCount(); 129 | } 130 | } 131 | mpuInterrupt = false; 132 | mpuIntStatus = mpu.getIntStatus(); 133 | fifoCount = mpu.getFIFOCount(); 134 | if ((mpuIntStatus & _BV(MPU6050_INTERRUPT_FIFO_OFLOW_BIT)) || fifoCount >= 1024) { 135 | mpu.resetFIFO(); 136 | fifoCount = mpu.getFIFOCount(); 137 | //Serial.println(F("FIFO overflow!")); 138 | 139 | } else if (mpuIntStatus & _BV(MPU6050_INTERRUPT_DMP_INT_BIT)) { 140 | while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount(); 141 | mpu.getFIFOBytes(fifoBuffer, packetSize); 142 | fifoCount -= packetSize; 143 | mpu.dmpGetQuaternion(&q, fifoBuffer); 144 | mpu.dmpGetGravity(&gravity, &q); 145 | mpu.dmpGetYawPitchRoll(ypr, &q, &gravity); 146 | 147 | // Set left joystick 148 | leftJoyX = (ypr[2]-rollOffset) * ADC_Max/M_PI*SCALING; 149 | leftJoyY = (ypr[1]-pitchOffset) * ADC_Max/M_PI*SCALING; 150 | 151 | leftJoyX = map(leftJoyX, -512, 511, 255, 0); 152 | leftJoyY = map(leftJoyY, -512, 511, 255, 0); 153 | 154 | leftJoyX = constrain(leftJoyX, 0, 255); 155 | leftJoyY = constrain(leftJoyY, 0, 255); 156 | } 157 | 158 | if (!digitalRead(15)){ 159 | Joystick.pressButton(2); // A 160 | } else { 161 | Joystick.releaseButton(2); 162 | } 163 | if (!digitalRead(14)){ 164 | Joystick.pressButton(1); // B 165 | } else { 166 | Joystick.releaseButton(1); 167 | } 168 | if (!digitalRead(16)){ 169 | Joystick.pressButton(4); // L 170 | } else { 171 | Joystick.releaseButton(4); 172 | } 173 | if (!digitalRead(10)){ 174 | Joystick.pressButton(5); // R 175 | } else{ 176 | Joystick.releaseButton(5); 177 | } 178 | 179 | if (digitalRead(9)) { 180 | onTime = millis(); 181 | Joystick.pressButton(10); // Lstick 182 | } 183 | else { 184 | // Long press Set offset IMU 185 | if ((millis() - onTime) > 1000) { 186 | pitchOffset = ypr[1]; 187 | rollOffset = ypr[2]; 188 | } 189 | Joystick.releaseButton(10); // Lstick 190 | } 191 | 192 | Joystick.setXAxis(leftJoyX); 193 | Joystick.setYAxis(leftJoyY); 194 | 195 | Joystick.sendState(); 196 | //delay(50); delay result in FIFO overflow! 197 | } 198 | 199 | void fail() { 200 | while (1) { 201 | RXLED0; 202 | delay(250); 203 | RXLED1; 204 | delay(250); 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /examples/SwitchJoystickTest/SwitchJoystickTest.ino: -------------------------------------------------------------------------------- 1 | // Program used to test the Nintento Switch Joystick object on the 2 | // Arduino Leonardo or Arduino Micro. 3 | // 4 | // Michele Perla for Hackerloop 5 | // Edited by Jorand Le Pape 6 | //------------------------------------------------------------ 7 | // 8 | // Connect 12 buttons with pullups to pins A0 to A3 and to pins 0 to 7; 9 | // When the button on A0 is pressed, buttons on pins 0-7 control Y,B,A,X,L,R,Zl,Zr; 10 | // When the button on A1 is pressed, buttons on pins 0-5 control +,-,Lstick,Rstick,Home,Capture; 11 | // When the button on A2 is pressed, buttons on pins 0-1 control Left Stick X axis, buttons on pins 2-3 control Left Stick Y axis, 12 | // buttons on pins 4-5 control Right Stick X axis, buttons on pins 4-5 control Right Stick Y axis 13 | // When the button on A3 is pressed, buttons on pins 0-7 control the Hat Switch 14 | 15 | #include "SwitchJoystick.h" 16 | 17 | // Create Joystick 18 | SwitchJoystick_ Joystick; 19 | 20 | // Set to true to test "Auto Send" mode or false to test "Manual Send" mode. 21 | //const bool testAutoSendMode = true; 22 | const bool testAutoSendMode = false; 23 | 24 | uint32_t timer = 0; 25 | 26 | void setup() { 27 | 28 | if (testAutoSendMode) 29 | { 30 | Joystick.begin(); 31 | } 32 | else 33 | { 34 | Joystick.begin(false); 35 | } 36 | 37 | pinMode(A0, INPUT_PULLUP); 38 | pinMode(A1, INPUT_PULLUP); 39 | pinMode(A2, INPUT_PULLUP); 40 | pinMode(A3, INPUT_PULLUP); 41 | 42 | //pinMode(0, INPUT_PULLUP); TX0 43 | //pinMode(1, INPUT_PULLUP); RX1 44 | //pinMode(2, INPUT_PULLUP); SDA 45 | //pinMode(3, INPUT_PULLUP); SCL 46 | pinMode(4, INPUT_PULLUP); 47 | pinMode(5, INPUT_PULLUP); 48 | pinMode(6, INPUT_PULLUP); 49 | pinMode(7, INPUT_PULLUP); 50 | //pinMode(8, INPUT_PULLUP); Wierd 51 | pinMode(9, INPUT_PULLUP); 52 | pinMode(10, INPUT_PULLUP); 53 | pinMode(16, INPUT_PULLUP); 54 | pinMode(14, INPUT_PULLUP); 55 | pinMode(15, INPUT_PULLUP); 56 | 57 | Joystick.setXAxis(128); 58 | Joystick.setYAxis(128); 59 | Joystick.setZAxis(128); 60 | Joystick.setRzAxis(128); 61 | Joystick.sendState(); 62 | } 63 | 64 | void loop() { 65 | if (millis() > timer) { 66 | timer = millis() + 10; // 10 ms = 1/0.010 == 100Hz 67 | 68 | if (!digitalRead(A0)) { 69 | if (!digitalRead(4)){ 70 | Joystick.pressButton(0); // Y 71 | } else { 72 | Joystick.releaseButton(0); // Y 73 | } 74 | if (!digitalRead(5)){ 75 | Joystick.pressButton(1); // B 76 | } else { 77 | Joystick.releaseButton(1); // B 78 | } 79 | if (!digitalRead(6)){ 80 | Joystick.pressButton(2); // A 81 | } else { 82 | Joystick.releaseButton(2); // A 83 | } 84 | if (!digitalRead(7)){ 85 | Joystick.pressButton(3); // Z 86 | } else { 87 | Joystick.releaseButton(3); // Z 88 | } 89 | if (!digitalRead(9)){ 90 | Joystick.pressButton(4); // L 91 | } else { 92 | Joystick.releaseButton(4); // L 93 | } 94 | if (!digitalRead(10)){ 95 | Joystick.pressButton(5); // R 96 | } else { 97 | Joystick.releaseButton(5); // R 98 | } 99 | if (!digitalRead(16)){ 100 | Joystick.pressButton(6); // Zl 101 | } else { 102 | Joystick.releaseButton(6); // Zl 103 | } 104 | if (!digitalRead(14)){ 105 | Joystick.pressButton(7); // Zr 106 | } else { 107 | Joystick.releaseButton(7); // Zr 108 | } 109 | } 110 | else if (!digitalRead(A1)) { 111 | if (!digitalRead(4)){ 112 | Joystick.pressButton(8); // - 113 | } else { 114 | Joystick.releaseButton(8); // - 115 | } 116 | if (!digitalRead(5)){ 117 | Joystick.pressButton(9); // + 118 | } else { 119 | Joystick.releaseButton(9); // + 120 | } 121 | if (!digitalRead(6)){ 122 | Joystick.pressButton(10); // Lstick 123 | } else { 124 | Joystick.releaseButton(10); // Lstick 125 | } 126 | if (!digitalRead(7)){ 127 | Joystick.pressButton(11); // Rstick 128 | } else { 129 | Joystick.releaseButton(11); // Rstick 130 | } 131 | if (!digitalRead(9)){ 132 | Joystick.pressButton(12); // Home 133 | } else { 134 | Joystick.releaseButton(12); // Home 135 | } 136 | if (!digitalRead(10)){ 137 | Joystick.pressButton(13); // capture 138 | } else { 139 | Joystick.releaseButton(13); // capture 140 | } 141 | } 142 | else if (!digitalRead(A2)) { 143 | if (!digitalRead(4)){ 144 | Joystick.setXAxis(255); // Left joystick RIGHT 145 | } 146 | else if (!digitalRead(5)){ 147 | Joystick.setXAxis(0); // Left joystick LEFT 148 | } 149 | else { 150 | Joystick.setXAxis(128); // Release 151 | } 152 | 153 | if (!digitalRead(6)){ 154 | Joystick.setYAxis(255); // Left joystick DOWN 155 | } 156 | else if (!digitalRead(7)){ 157 | Joystick.setYAxis(0); // Left joystick UP 158 | } 159 | else { 160 | Joystick.setYAxis(128); // Release 161 | } 162 | 163 | if (!digitalRead(9)){ 164 | Joystick.setZAxis(255); // Right joystick RIGHT (camera left) 165 | } 166 | else if (!digitalRead(10)){ 167 | Joystick.setZAxis(0); // Right joystick LEFT (camera right) 168 | } 169 | else { 170 | Joystick.setZAxis(128);// Release 171 | } 172 | 173 | if (!digitalRead(16)){ 174 | Joystick.setRzAxis(255); // Right joystick DOWN (camera zoom out) 175 | } 176 | else if (!digitalRead(14)){ 177 | Joystick.setRzAxis(0); // Right joystick UP (camera zoom in) 178 | } 179 | else { 180 | Joystick.setRzAxis(128); // Release 181 | } 182 | } 183 | else if (!digitalRead(A3)) { 184 | if (!digitalRead(4)){ 185 | Joystick.setHatSwitch(0); // UP 186 | } 187 | if (!digitalRead(5)){ 188 | Joystick.setHatSwitch(90); // RIGHT 189 | } 190 | if (!digitalRead(6)){ 191 | Joystick.setHatSwitch(180); // DOWN 192 | } 193 | if (!digitalRead(7)){ 194 | Joystick.setHatSwitch(270); // LEFT 195 | } 196 | 197 | if (digitalRead(4) && digitalRead(5) && digitalRead(6) && digitalRead(7)) { 198 | Joystick.setHatSwitch(-1); // RELEASE 199 | } 200 | } 201 | 202 | Joystick.sendState(); 203 | } 204 | } -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | SwitchJoystick_ KEYWORD1 2 | begin KEYWORD2 3 | end KEYWORD2 4 | setXAxis KEYWORD2 5 | setYAxis KEYWORD2 6 | setZAxis KEYWORD2 7 | setRzAxis KEYWORD2 8 | setButton KEYWORD2 9 | pressButton KEYWORD2 10 | releaseButton KEYWORD2 11 | setHatSwitch KEYWORD2 12 | sendState KEYWORD2 -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=SwitchJoystick 2 | version=1.0.0 3 | author=Michele Perla 4 | maintainer= 5 | sentence=Arduino library that allows an Arduino Leonardo or Arduino Micro to appear as a gamepad on the Nintendo Switch. 6 | paragraph= 7 | category=Device Control 8 | url= 9 | architectures=avr 10 | -------------------------------------------------------------------------------- /src/DynamicHID/DynamicHID.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Modified by Michele Perla for compatibility with Nintendo Switch. 3 | 4 | Modified by Matthew Heironimus to support HID Report Descriptors to be in 5 | standard RAM in addition to program memory (PROGMEM). 6 | 7 | Copyright (c) 2015, Arduino LLC 8 | Original code (pre-library): Copyright (c) 2011, Peter Barrett 9 | 10 | Permission to use, copy, modify, and/or distribute this software for 11 | any purpose with or without fee is hereby granted, provided that the 12 | above copyright notice and this permission notice appear in all copies. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 15 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 17 | BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES 18 | OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 19 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 20 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 21 | SOFTWARE. 22 | */ 23 | 24 | #include "DynamicHID.h" 25 | 26 | #if defined(USBCON) 27 | 28 | DynamicHID_& DynamicHID() 29 | { 30 | static DynamicHID_ obj; 31 | return obj; 32 | } 33 | 34 | int DynamicHID_::getInterface(uint8_t* interfaceCount) 35 | { 36 | *interfaceCount += 1; // uses 1 37 | DYNAMIC_HIDDescriptor hidInterface = { 38 | D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, DYNAMIC_HID_SUBCLASS_NONE, DYNAMIC_HID_PROTOCOL_NONE), 39 | D_HIDREPORT(descriptorSize), 40 | D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01) 41 | }; 42 | return USB_SendControl(0, &hidInterface, sizeof(hidInterface)); 43 | } 44 | 45 | int DynamicHID_::getDescriptor(USBSetup& setup) 46 | { 47 | // Check if this is a HID Class Descriptor request 48 | if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) { return 0; } 49 | if (setup.wValueH != DYNAMIC_HID_REPORT_DESCRIPTOR_TYPE) { return 0; } 50 | 51 | // In a HID Class Descriptor wIndex cointains the interface number 52 | if (setup.wIndex != pluggedInterface) { return 0; } 53 | 54 | int total = 0; 55 | DynamicHIDSubDescriptor* node; 56 | for (node = rootNode; node; node = node->next) { 57 | int res = USB_SendControl((node->inProgMem ? TRANSFER_PGM : 0), node->data, node->length); 58 | if (res == -1) 59 | return -1; 60 | total += res; 61 | } 62 | 63 | // Reset the protocol on reenumeration. Normally the host should not assume the state of the protocol 64 | // due to the USB specs, but Windows and Linux just assumes its in report mode. 65 | protocol = DYNAMIC_HID_REPORT_PROTOCOL; 66 | 67 | return total; 68 | } 69 | 70 | uint8_t DynamicHID_::getShortName(char *name) 71 | { 72 | name[0] = 'H'; 73 | name[1] = 'I'; 74 | name[2] = 'D'; 75 | name[3] = 'A' + (descriptorSize & 0x0F); 76 | name[4] = 'A' + ((descriptorSize >> 4) & 0x0F); 77 | return 5; 78 | } 79 | 80 | void DynamicHID_::AppendDescriptor(DynamicHIDSubDescriptor *node) 81 | { 82 | if (!rootNode) { 83 | rootNode = node; 84 | } else { 85 | DynamicHIDSubDescriptor *current = rootNode; 86 | while (current->next) { 87 | current = current->next; 88 | } 89 | current->next = node; 90 | } 91 | descriptorSize += node->length; 92 | } 93 | 94 | int DynamicHID_::SendReport(uint8_t id, const void* data, int len) 95 | { 96 | //auto ret = USB_Send(pluggedEndpoint, id, 1); 97 | //if (ret < 0) return ret; 98 | //auto ret2 = USB_Send(pluggedEndpoint, data, len); 99 | //if (ret2 < 0) return ret2; 100 | //return ret + ret2; 101 | 102 | // Nintendo Switch does not like it if we send a one byte packet with the HID Report ID 103 | // and then the data; in fact, doing so makes us lose the first byte of data which 104 | // contains the information of the buttons Y B A X L R Zl Zr, crucial for any game; 105 | // by sending the data without a HID Report ID the Switch properly processes the 7 bytes 106 | // of data, but we lose compatibility with Windows (not tested on other platforms). 107 | // We still send the ID, but AFTER the data, and actually just to tell the USB stack 108 | // to release the endpoint. 109 | auto ret2 = USB_Send(pluggedEndpoint, data, len); 110 | auto ret = USB_Send(pluggedEndpoint | TRANSFER_RELEASE, &id, 1); 111 | return ret + ret2; 112 | } 113 | 114 | bool DynamicHID_::setup(USBSetup& setup) 115 | { 116 | if (pluggedInterface != setup.wIndex) { 117 | return false; 118 | } 119 | 120 | uint8_t request = setup.bRequest; 121 | uint8_t requestType = setup.bmRequestType; 122 | 123 | if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE) 124 | { 125 | if (request == DYNAMIC_HID_GET_REPORT) { 126 | // TODO: DYNAMIC_HID_GetReport(); 127 | return true; 128 | } 129 | if (request == DYNAMIC_HID_GET_PROTOCOL) { 130 | // TODO: Send8(protocol); 131 | return true; 132 | } 133 | if (request == DYNAMIC_HID_GET_IDLE) { 134 | // TODO: Send8(idle); 135 | } 136 | } 137 | 138 | if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) 139 | { 140 | if (request == DYNAMIC_HID_SET_PROTOCOL) { 141 | // The USB Host tells us if we are in boot or report mode. 142 | // This only works with a real boot compatible device. 143 | protocol = setup.wValueL; 144 | return true; 145 | } 146 | if (request == DYNAMIC_HID_SET_IDLE) { 147 | idle = setup.wValueL; 148 | return true; 149 | } 150 | if (request == DYNAMIC_HID_SET_REPORT) 151 | { 152 | //uint8_t reportID = setup.wValueL; 153 | //uint16_t length = setup.wLength; 154 | //uint8_t data[length]; 155 | // Make sure to not read more data than USB_EP_SIZE. 156 | // You can read multiple times through a loop. 157 | // The first byte (may!) contain the reportID on a multreport. 158 | //USB_RecvControl(data, length); 159 | } 160 | } 161 | 162 | return false; 163 | } 164 | 165 | DynamicHID_::DynamicHID_(void) : PluggableUSBModule(1, 1, epType), 166 | rootNode(NULL), descriptorSize(0), 167 | protocol(DYNAMIC_HID_REPORT_PROTOCOL), idle(1) 168 | { 169 | epType[0] = EP_TYPE_INTERRUPT_IN; 170 | PluggableUSB().plug(this); 171 | } 172 | 173 | int DynamicHID_::begin(void) 174 | { 175 | return 0; 176 | } 177 | 178 | #endif /* if defined(USBCON) */ 179 | -------------------------------------------------------------------------------- /src/DynamicHID/DynamicHID.h: -------------------------------------------------------------------------------- 1 | /* 2 | Modified by Matthew Heironimus to support HID Report Descriptors to be in 3 | standard RAM in addition to program memory (PROGMEM). 4 | 5 | Copyright (c) 2015, Arduino LLC 6 | Original code (pre-library): Copyright (c) 2011, Peter Barrett 7 | 8 | Permission to use, copy, modify, and/or distribute this software for 9 | any purpose with or without fee is hereby granted, provided that the 10 | above copyright notice and this permission notice appear in all copies. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 14 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 15 | BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES 16 | OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 17 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 18 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 19 | SOFTWARE. 20 | */ 21 | 22 | #ifndef DYNAMIC_HID_h 23 | #define DYNAMIC_HID_h 24 | 25 | #include 26 | #include 27 | #include "PluggableUSB.h" 28 | 29 | #if defined(USBCON) 30 | 31 | #define _USING_DYNAMIC_HID 32 | 33 | // DYNAMIC_HID 'Driver' 34 | // ------------ 35 | #define DYNAMIC_HID_GET_REPORT 0x01 36 | #define DYNAMIC_HID_GET_IDLE 0x02 37 | #define DYNAMIC_HID_GET_PROTOCOL 0x03 38 | #define DYNAMIC_HID_SET_REPORT 0x09 39 | #define DYNAMIC_HID_SET_IDLE 0x0A 40 | #define DYNAMIC_HID_SET_PROTOCOL 0x0B 41 | 42 | #define DYNAMIC_HID_DESCRIPTOR_TYPE 0x21 43 | #define DYNAMIC_HID_REPORT_DESCRIPTOR_TYPE 0x22 44 | #define DYNAMIC_HID_PHYSICAL_DESCRIPTOR_TYPE 0x23 45 | 46 | // HID subclass HID1.11 Page 8 4.2 Subclass 47 | #define DYNAMIC_HID_SUBCLASS_NONE 0 48 | #define DYNAMIC_HID_SUBCLASS_BOOT_INTERFACE 1 49 | 50 | // HID Keyboard/Mouse bios compatible protocols HID1.11 Page 9 4.3 Protocols 51 | #define DYNAMIC_HID_PROTOCOL_NONE 0 52 | #define DYNAMIC_HID_PROTOCOL_KEYBOARD 1 53 | #define DYNAMIC_HID_PROTOCOL_MOUSE 2 54 | 55 | // Normal or bios protocol (Keyboard/Mouse) HID1.11 Page 54 7.2.5 Get_Protocol Request 56 | // "protocol" variable is used for this purpose. 57 | #define DYNAMIC_HID_BOOT_PROTOCOL 0 58 | #define DYNAMIC_HID_REPORT_PROTOCOL 1 59 | 60 | // HID Request Type HID1.11 Page 51 7.2.1 Get_Report Request 61 | #define DYNAMIC_HID_REPORT_TYPE_INPUT 1 62 | #define DYNAMIC_HID_REPORT_TYPE_OUTPUT 2 63 | #define DYNAMIC_HID_REPORT_TYPE_FEATURE 3 64 | 65 | typedef struct 66 | { 67 | uint8_t len; // 9 68 | uint8_t dtype; // 0x21 69 | uint8_t addr; 70 | uint8_t versionL; // 0x101 71 | uint8_t versionH; // 0x101 72 | uint8_t country; 73 | uint8_t desctype; // 0x22 report 74 | uint8_t descLenL; 75 | uint8_t descLenH; 76 | } DYNAMIC_HIDDescDescriptor; 77 | 78 | typedef struct 79 | { 80 | InterfaceDescriptor hid; 81 | DYNAMIC_HIDDescDescriptor desc; 82 | EndpointDescriptor in; 83 | } DYNAMIC_HIDDescriptor; 84 | 85 | class DynamicHIDSubDescriptor { 86 | public: 87 | DynamicHIDSubDescriptor *next = NULL; 88 | DynamicHIDSubDescriptor(const void *d, const uint16_t l, const bool ipm = true) : data(d), length(l), inProgMem(ipm) { } 89 | 90 | const void* data; 91 | const uint16_t length; 92 | const bool inProgMem; 93 | }; 94 | 95 | class DynamicHID_ : public PluggableUSBModule 96 | { 97 | public: 98 | DynamicHID_(void); 99 | int begin(void); 100 | int SendReport(uint8_t id, const void* data, int len); 101 | void AppendDescriptor(DynamicHIDSubDescriptor* node); 102 | 103 | protected: 104 | // Implementation of the PluggableUSBModule 105 | int getInterface(uint8_t* interfaceCount); 106 | int getDescriptor(USBSetup& setup); 107 | bool setup(USBSetup& setup); 108 | uint8_t getShortName(char* name); 109 | 110 | private: 111 | uint8_t epType[1]; 112 | 113 | DynamicHIDSubDescriptor* rootNode; 114 | uint16_t descriptorSize; 115 | 116 | uint8_t protocol; 117 | uint8_t idle; 118 | }; 119 | 120 | // Replacement for global singleton. 121 | // This function prevents static-initialization-order-fiasco 122 | // https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use 123 | DynamicHID_& DynamicHID(); 124 | 125 | #define D_HIDREPORT(length) { 9, 0x21, 0x01, 0x01, 0, 1, 0x22, lowByte(length), highByte(length) } 126 | 127 | #endif // USBCON 128 | 129 | #endif // DYNAMIC_HID_h 130 | -------------------------------------------------------------------------------- /src/SwitchJoystick.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | SwitchJoystick.cpp 3 | 4 | Copyright (c) 2017, Michele Perla for Hackerloop 5 | 6 | Based on the Joystick Library (https://github.com/MHeironimus/ArduinoJoystickLibrary) - Copyright (c) 2015-2017, Matthew Heironimus 7 | and on the Switch-Fighstick LUFA Proof-of-concept project (https://github.com/progmem/Switch-Fightstick) by Jason DeLeon 8 | 9 | This library is free software; you can redistribute it and/or 10 | modify it under the terms of the GNU Lesser General Public 11 | License as published by the Free Software Foundation; either 12 | version 2.1 of the License, or (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public 20 | License along with this library; if not, write to the Free Software 21 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22 | */ 23 | 24 | #include "SwitchJoystick.h" 25 | 26 | #if defined(_USING_DYNAMIC_HID) 27 | 28 | #define JOYSTICK_REPORT_ID_INDEX 7 29 | #define JOYSTICK_AXIS_MINIMUM 0 30 | #define JOYSTICK_AXIS_MAXIMUM 255 31 | 32 | #define JOYSTICK_INCLUDE_X_AXIS B00000001 33 | #define JOYSTICK_INCLUDE_Y_AXIS B00000010 34 | #define JOYSTICK_INCLUDE_Z_AXIS B00000100 35 | #define JOYSTICK_INCLUDE_RZ_AXIS B00100000 36 | 37 | SwitchJoystick_::SwitchJoystick_() 38 | { 39 | // Set the USB HID Report ID 40 | _hidReportId = JOYSTICK_DEFAULT_REPORT_ID; 41 | 42 | // Save Joystick Settings 43 | _buttonCount = 16; 44 | _includeAxisFlags |= JOYSTICK_INCLUDE_X_AXIS | JOYSTICK_INCLUDE_Y_AXIS | JOYSTICK_INCLUDE_Z_AXIS | JOYSTICK_INCLUDE_RZ_AXIS; 45 | _hatSwitchCount = 1; 46 | 47 | // Build Joystick HID Report Description 48 | 49 | // Button Calculations 50 | uint8_t buttonsInLastByte = _buttonCount % 8; 51 | uint8_t buttonPaddingBits = 0; 52 | if (buttonsInLastByte > 0) 53 | { 54 | buttonPaddingBits = 8 - buttonsInLastByte; 55 | } 56 | 57 | // Axis Calculations 58 | uint8_t axisCount = 4; 59 | 60 | uint8_t tempHidReportDescriptor[150]; 61 | int hidReportDescriptorSize = 0; 62 | 63 | // USAGE_PAGE (Generic Desktop) 64 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x05; 65 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 66 | 67 | // USAGE (Joystick - 0x04; Gamepad - 0x05; Multi-axis Controller - 0x08) 68 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 69 | tempHidReportDescriptor[hidReportDescriptorSize++] = JOYSTICK_TYPE_GAMEPAD; 70 | 71 | // COLLECTION (Application) 72 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xa1; 73 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 74 | 75 | // REPORT_ID (Default: 3) 76 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x85; 77 | //tempHidReportDescriptor[hidReportDescriptorSize++] = _hidReportId; 78 | 79 | // USAGE_PAGE (Button) 80 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x05; 81 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 82 | 83 | // USAGE_MINIMUM (Button 1) 84 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x19; 85 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 86 | 87 | // USAGE_MAXIMUM (Button 32) 88 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x29; 89 | tempHidReportDescriptor[hidReportDescriptorSize++] = _buttonCount; 90 | 91 | // LOGICAL_MINIMUM (0) 92 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x15; 93 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 94 | 95 | // LOGICAL_MAXIMUM (1) 96 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x25; 97 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 98 | 99 | // REPORT_SIZE (1) 100 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; 101 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 102 | 103 | // REPORT_COUNT (# of buttons) 104 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 105 | tempHidReportDescriptor[hidReportDescriptorSize++] = _buttonCount; 106 | 107 | // UNIT_EXPONENT (0) 108 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x55; 109 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 110 | 111 | // UNIT (None) 112 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x65; 113 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 114 | 115 | // INPUT (Data,Var,Abs) 116 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; 117 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; 118 | 119 | if (buttonPaddingBits > 0) { 120 | 121 | // REPORT_SIZE (1) 122 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; 123 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 124 | 125 | // REPORT_COUNT (# of padding bits) 126 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 127 | tempHidReportDescriptor[hidReportDescriptorSize++] = buttonPaddingBits; 128 | 129 | // INPUT (Const,Var,Abs) 130 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; 131 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x03; 132 | 133 | } // Padding Bits Needed 134 | 135 | // Buttons 136 | 137 | // USAGE_PAGE (Generic Desktop) 138 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x05; 139 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 140 | 141 | // USAGE (Hat Switch) 142 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 143 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x39; 144 | 145 | // LOGICAL_MINIMUM (0) 146 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x15; 147 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 148 | 149 | // LOGICAL_MAXIMUM (7) 150 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x25; 151 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x07; 152 | 153 | // PHYSICAL_MINIMUM (0) 154 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x35; 155 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 156 | 157 | // PHYSICAL_MAXIMUM (315) 158 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x46; 159 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x3B; 160 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 161 | 162 | // UNIT (Eng Rot:Angular Pos) 163 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x65; 164 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x14; 165 | 166 | // REPORT_SIZE (4) 167 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; 168 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x04; 169 | 170 | // REPORT_COUNT (1) 171 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 172 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 173 | 174 | // INPUT (Data,Var,Abs) 175 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; 176 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; 177 | 178 | // Use Padding Bits 179 | 180 | // REPORT_SIZE (1) 181 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; 182 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 183 | 184 | // REPORT_COUNT (4) 185 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 186 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x04; 187 | 188 | // INPUT (Const,Var,Abs) 189 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; 190 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 191 | 192 | // Hat Switch 193 | 194 | // USAGE (Pointer) 195 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 196 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 197 | 198 | // LOGICAL_MAXIMUM (255) 199 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x26; 200 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xFF; 201 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 202 | 203 | // PHYSICAL_MAXIMUM (255) 204 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x46; 205 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xFF; 206 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 207 | 208 | // REPORT_SIZE (8) 209 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; 210 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x08; 211 | 212 | // REPORT_COUNT (axisCount) 213 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 214 | tempHidReportDescriptor[hidReportDescriptorSize++] = axisCount; 215 | 216 | // COLLECTION (Physical) 217 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xA1; 218 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 219 | 220 | // USAGE (X) 221 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 222 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x30; 223 | 224 | // USAGE (Y) 225 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 226 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x31; 227 | 228 | // USAGE (Z) 229 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 230 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x32; 231 | 232 | // USAGE (Rz) 233 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 234 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x35; 235 | 236 | // INPUT (Data,Var,Abs) 237 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; 238 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; 239 | 240 | // END_COLLECTION (Physical) 241 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xc0; 242 | 243 | // X, Y, Z, and Rz Axis 244 | 245 | // END_COLLECTION 246 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xc0; 247 | 248 | // Create a copy of the HID Report Descriptor template that is just the right size 249 | uint8_t *customHidReportDescriptor = new uint8_t[hidReportDescriptorSize]; 250 | memcpy(customHidReportDescriptor, tempHidReportDescriptor, hidReportDescriptorSize); 251 | 252 | // Register HID Report Description 253 | DynamicHIDSubDescriptor *node = new DynamicHIDSubDescriptor(customHidReportDescriptor, hidReportDescriptorSize, false); 254 | DynamicHID().AppendDescriptor(node); 255 | 256 | 257 | if (_buttonCount > 0) { 258 | _buttonValuesArraySize = _buttonCount / 8; 259 | if ((_buttonCount % 8) > 0) { 260 | _buttonValuesArraySize++; 261 | } 262 | _buttonValues = new uint8_t[_buttonValuesArraySize]; 263 | } 264 | 265 | // Calculate HID Report Size 266 | _hidReportSize = _buttonValuesArraySize; 267 | _hidReportSize += (axisCount); 268 | _hidReportSize += (_hatSwitchCount > 0); 269 | 270 | // Initalize Joystick State 271 | _xAxis = 0; 272 | _yAxis = 0; 273 | _zAxis = 0; 274 | _zAxisRotation = 0; 275 | 276 | _hatSwitchValue = JOYSTICK_HATSWITCH_RELEASE; 277 | 278 | for (int index = 0; index < _buttonValuesArraySize; index++) 279 | { 280 | _buttonValues[index] = 0; 281 | } 282 | } 283 | 284 | void SwitchJoystick_::begin(bool initAutoSendState) 285 | { 286 | _autoSendState = initAutoSendState; 287 | sendState(); 288 | } 289 | 290 | void SwitchJoystick_::end() 291 | { 292 | } 293 | 294 | void SwitchJoystick_::setButton(uint8_t button, uint8_t value) 295 | { 296 | if (value == 0) 297 | { 298 | releaseButton(button); 299 | } 300 | else 301 | { 302 | pressButton(button); 303 | } 304 | } 305 | void SwitchJoystick_::pressButton(uint8_t button) 306 | { 307 | if (button >= _buttonCount) return; 308 | 309 | int index = button / 8; 310 | int bit = button % 8; 311 | 312 | bitSet(_buttonValues[index], bit); 313 | if (_autoSendState) sendState(); 314 | } 315 | void SwitchJoystick_::releaseButton(uint8_t button) 316 | { 317 | if (button >= _buttonCount) return; 318 | 319 | int index = button / 8; 320 | int bit = button % 8; 321 | 322 | bitClear(_buttonValues[index], bit); 323 | if (_autoSendState) sendState(); 324 | } 325 | 326 | void SwitchJoystick_::setXAxis(uint8_t value) 327 | { 328 | _xAxis = value; 329 | if (_autoSendState) sendState(); 330 | } 331 | void SwitchJoystick_::setYAxis(uint8_t value) 332 | { 333 | _yAxis = value; 334 | if (_autoSendState) sendState(); 335 | } 336 | void SwitchJoystick_::setZAxis(uint8_t value) 337 | { 338 | _zAxis = value; 339 | if (_autoSendState) sendState(); 340 | } 341 | 342 | void SwitchJoystick_::setRzAxis(uint8_t value) 343 | { 344 | _zAxisRotation = value; 345 | if (_autoSendState) sendState(); 346 | } 347 | 348 | void SwitchJoystick_::setHatSwitch(int16_t value) 349 | { 350 | _hatSwitchValue = value; 351 | if (_autoSendState) sendState(); 352 | } 353 | 354 | void SwitchJoystick_::sendState() 355 | { 356 | uint8_t data[_hidReportSize]; 357 | int index = 0; 358 | 359 | // Load Button State 360 | for (; index < _buttonValuesArraySize; index++) 361 | { 362 | data[index] = _buttonValues[index]; 363 | } 364 | 365 | // Pack hat-switch states into a single byte 366 | uint8_t hatSwitchConvertedValue = 8; 367 | if (_hatSwitchValue>=0) hatSwitchConvertedValue = ((((_hatSwitchValue% 360) / 45))); 368 | data[index++] = hatSwitchConvertedValue; 369 | // Set Axis Values 370 | data[index++] = _xAxis; 371 | data[index++] = _yAxis; 372 | data[index++] = _zAxis; 373 | data[index++] = _zAxisRotation; 374 | 375 | DynamicHID().SendReport(_hidReportId, data, _hidReportSize); 376 | } 377 | 378 | void SwitchJoystick_::sendState(uint8_t *data) 379 | { 380 | DynamicHID().SendReport(_hidReportId, data, _hidReportSize); 381 | } 382 | 383 | #endif 384 | -------------------------------------------------------------------------------- /src/SwitchJoystick.h: -------------------------------------------------------------------------------- 1 | /* 2 | SwitchJoystick.h 3 | 4 | Copyright (c) 2017, Michele Perla for Hackerloop 5 | 6 | Based on the Joystick Library (https://github.com/MHeironimus/ArduinoJoystickLibrary) - Copyright (c) 2015-2017, Matthew Heironimus 7 | and on the Switch-Fighstick LUFA Proof-of-concept project (https://github.com/progmem/Switch-Fightstick) by Jason DeLeon 8 | 9 | This library is free software; you can redistribute it and/or 10 | modify it under the terms of the GNU Lesser General Public 11 | License as published by the Free Software Foundation; either 12 | version 2.1 of the License, or (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public 20 | License along with this library; if not, write to the Free Software 21 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22 | */ 23 | 24 | 25 | #ifndef SWITCHJOYSTICK_h 26 | #define SWITCHJOYSTICK_h 27 | 28 | #include 29 | 30 | #if ARDUINO < 10606 31 | #error The SwitchJoystick library requires Arduino IDE 1.6.6 or greater. Please update your IDE. 32 | #endif // ARDUINO < 10606 33 | 34 | #if ARDUINO > 10606 35 | #if !defined(USBCON) 36 | #error The SwitchJoystick library can only be used with a USB MCU (e.g. Arduino Leonardo, Arduino Micro, etc.). 37 | #endif // !defined(USBCON) 38 | #endif // ARDUINO > 10606 39 | 40 | #if !defined(_USING_DYNAMIC_HID) 41 | 42 | #warning "Using legacy HID core (non pluggable)" 43 | 44 | #else // !defined(_USING_DYNAMIC_HID) 45 | 46 | //================================================================================ 47 | // Joystick (Gamepad) 48 | 49 | #define JOYSTICK_DEFAULT_REPORT_ID 0x01 50 | #define JOYSTICK_DEFAULT_BUTTON_COUNT 16 51 | #define JOYSTICK_DEFAULT_AXIS_MINIMUM 0 52 | #define JOYSTICK_DEFAULT_AXIS_MAXIMUM 255 53 | #define JOYSTICK_DEFAULT_SIMULATOR_MINIMUM 0 54 | #define JOYSTICK_DEFAULT_SIMULATOR_MAXIMUM 1023 55 | #define JOYSTICK_DEFAULT_HATSWITCH_COUNT 1 56 | #define JOYSTICK_HATSWITCH_COUNT_MAXIMUM 1 57 | #define JOYSTICK_HATSWITCH_RELEASE -1 58 | #define JOYSTICK_TYPE_JOYSTICK 0x04 59 | #define JOYSTICK_TYPE_GAMEPAD 0x05 60 | #define JOYSTICK_TYPE_MULTI_AXIS 0x08 61 | 62 | class SwitchJoystick_ 63 | { 64 | private: 65 | 66 | // Joystick State 67 | uint8_t _xAxis; 68 | uint8_t _yAxis; 69 | uint8_t _zAxis; 70 | uint8_t _zAxisRotation; 71 | int16_t _hatSwitchValue; 72 | uint8_t *_buttonValues = NULL; 73 | 74 | // Joystick Settings 75 | bool _autoSendState; 76 | uint8_t _buttonCount; 77 | uint8_t _buttonValuesArraySize = 0; 78 | uint8_t _hatSwitchCount; 79 | uint8_t _includeAxisFlags; 80 | 81 | uint8_t _hidReportId; 82 | uint8_t _hidReportSize; 83 | 84 | public: 85 | SwitchJoystick_(); 86 | 87 | void begin(bool initAutoSendState = true); 88 | void end(); 89 | 90 | // Set Axis Values 91 | void setXAxis(uint8_t value); 92 | void setYAxis(uint8_t value); 93 | void setZAxis(uint8_t value); 94 | void setRzAxis(uint8_t value); 95 | 96 | void setButton(uint8_t button, uint8_t value); 97 | void pressButton(uint8_t button); 98 | void releaseButton(uint8_t button); 99 | 100 | void setHatSwitch(int16_t value); 101 | 102 | void sendState(); 103 | 104 | // sendState(uint8_t *data) is used to send raw data directly, useful for debugging 105 | // format is bit7 6 5 4 3 2 1 0 106 | // 1st byte: Zr Zl R L X A B Y 107 | // 2nd byte: x x Capture Home Rstick Lstick + - 108 | // 3rd byte: Hat Switch (values 0 from 7, 15 = neutral state); 109 | // 4th byte: Left Joystick X axis (0 is left, 255 is right, 128 is center) 110 | // 5th byte: Left Joystick Y axis (0 is up, 255 is down, 128 is center) 111 | // 6th byte: Right Joystick X axis (0 is left, 255 is right, 128 is center) 112 | // 7th byte: Right Joystick Y axis (0 is up, 255 is down, 128 is center) 113 | void sendState(uint8_t *data); 114 | }; 115 | 116 | #endif // !defined(_USING_DYNAMIC_HID) 117 | #endif // SWITCHJOYSTICK_h 118 | --------------------------------------------------------------------------------