├── README.md ├── noycetroller slave └── noycetroller master /README.md: -------------------------------------------------------------------------------- 1 | These work on the Xinput library. If you want to learn how to install the board packages required, 2 | please watch my tutorial: https://youtu.be/61U-S7_XTDw or you can read the specifics here: https://github.com/dmadison/ArduinoXInput 3 | 4 | This code is a modification of this example: https://github.com/dmadison/ArduinoXInput/tree/master/examples/GamepadPins 5 | 6 | 3d print files https://www.thingiverse.com/thing:4406183 7 | 8 | This controller is based around using two Arduino Pro Micros that communicate with eather other via I2C. The reason for this is pro micros 9 | do not have enough pins to cover all of the buttons on an Xbox gamepad. If you use a Teensy board 3.1 it has enough pins to cover all the buttons. 10 | 11 | This project is unique because it's an xbox controller split in two, making I2C a great option to limit the number of wires between controllers. 12 | 13 | The I2C section of this code was written by: https://www.twitch.tv/gilbertsgadgets 14 | 15 | If you make this controller I hope to see a photo of it! 16 | 17 | 18 | -------------------------------------------------------------------------------- /noycetroller slave: -------------------------------------------------------------------------------- 1 | 2 | // Right controller code (I2C Slave) 3 | //I2C section created by https://www.twitch.tv/gilbertsgadgets 4 | //Special shoutout to GrayArtificer for I2C foundation 5 | /* 6 | Project Arduino XInput Library 7 | @author David Madison 8 | @link github.com/dmadison/ArduinoXInput 9 | @license MIT - Copyright (c) 2019 David Madison 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in 19 | all copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | THE SOFTWARE. 28 | 29 | Example: GamepadPins 30 | Description: Uses all of the available pin inputs to build a 'complete' 31 | Xbox gamepad, with both analog joysticks, both triggers, 32 | and all of the main buttons. 33 | 34 | * * Joysticks should be your typical 10k dual potentiometers. 35 | * * Triggers can be either analog (pots) or digital (buttons). 36 | Set the 'TriggerButtons' variable to change between the two. 37 | * * Buttons use the internal pull-ups and should be connected 38 | directly to ground. 39 | 40 | These pins are designed around the Leonardo's layout. You 41 | may need to change the pin numbers if you're using a 42 | different board type 43 | 44 | */ 45 | 46 | #include 47 | byte x = 0; 48 | 49 | // Joystick Pins 50 | const int Pin_RightJoyX = A0; 51 | const int Pin_RightJoyY = A1; 52 | 53 | // Trigger Pins 54 | const int Pin_TriggerR = A2; 55 | 56 | // Button Pins 57 | const int Pin_ButtonA = 4; 58 | const int Pin_ButtonB = 5; 59 | const int Pin_ButtonX = 6; 60 | const int Pin_ButtonY = 7; 61 | 62 | const int Pin_ButtonRB = 8; 63 | 64 | const int Pin_ButtonStart = 9; 65 | 66 | const int Pin_ButtonR3 = 10; 67 | 68 | /********* Data packet format *************/ 69 | 70 | typedef struct { 71 | boolean buttonA; 72 | boolean buttonB; 73 | boolean buttonX; 74 | boolean buttonY; 75 | boolean buttonRB; 76 | boolean buttonStart; 77 | boolean buttonR3; 78 | int triggerRight; 79 | int joyX; 80 | int joyY; 81 | } RIGHT_CONTROLLER_BUTTONS; 82 | 83 | RIGHT_CONTROLLER_BUTTONS ButtonStates; 84 | 85 | /**************** SETUP FUNCTION *******************/ 86 | 87 | void setup() { 88 | // Serial.begin(9600); 89 | // Serial.println("Serial started"); 90 | // Pin Setup 91 | /* 92 | // If using buttons for the triggers, use internal pull-up resistors 93 | if (UseTriggerButtons == true) { 94 | pinMode(Pin_TriggerR, INPUT_PULLUP); 95 | } 96 | */ 97 | 98 | // Set buttons as inputs, using internal pull-up resistors 99 | pinMode(Pin_ButtonA, INPUT_PULLUP); 100 | pinMode(Pin_ButtonB, INPUT_PULLUP); 101 | pinMode(Pin_ButtonX, INPUT_PULLUP); 102 | pinMode(Pin_ButtonY, INPUT_PULLUP); 103 | 104 | pinMode(Pin_ButtonRB, INPUT_PULLUP); 105 | 106 | pinMode(Pin_ButtonStart, INPUT_PULLUP); 107 | 108 | pinMode(Pin_ButtonR3, INPUT_PULLUP); 109 | 110 | // Serial.println("Starting I2C"); 111 | // Initialize I2C 112 | Wire.begin(0x08); // join i2c bus with address 0x08 113 | Wire.onRequest(requestEvent); // register event 114 | 115 | // Serial.println("I2C initialized"); 116 | } 117 | 118 | /***************** LOOP FUNCTION *****************/ 119 | 120 | void loop() { 121 | 122 | // Read all button states and store in ButtonStates 123 | ButtonStates.buttonA = !digitalRead(Pin_ButtonA); 124 | ButtonStates.buttonB = !digitalRead(Pin_ButtonB); 125 | ButtonStates.buttonX = !digitalRead(Pin_ButtonX); 126 | ButtonStates.buttonY = !digitalRead(Pin_ButtonY); 127 | ButtonStates.buttonRB = !digitalRead(Pin_ButtonRB); 128 | ButtonStates.buttonStart = !digitalRead(Pin_ButtonStart); 129 | ButtonStates.buttonR3 = !digitalRead(Pin_ButtonR3); 130 | 131 | // Read trigger potentiometer values 132 | ButtonStates.triggerRight = analogRead(Pin_TriggerR); 133 | 134 | // Read joystick values 135 | ButtonStates.joyX = analogRead(Pin_RightJoyX); 136 | ButtonStates.joyY = analogRead(Pin_RightJoyY); 137 | 138 | // A little delay, for kicks. Might not be necessary... 139 | delay(10); 140 | } 141 | 142 | /***************** I2C REQUEST EVENT FUNCTION ****************/ 143 | 144 | // function that executes whenever data is requested by master 145 | // this function is registered as an event, see setup() 146 | void requestEvent() { 147 | // Serial.println("Request for data received by slave."); 148 | // Send the states of all the buttons 149 | 150 | Wire.write((char*) &ButtonStates, sizeof(RIGHT_CONTROLLER_BUTTONS)); 151 | 152 | 153 | // Don't need the following, but might help for debug/bring-up 154 | // Wire.write("hello "); // respond with message of 6 bytes as expected by master 155 | // Serial.println("Data sent"); 156 | } 157 | -------------------------------------------------------------------------------- /noycetroller master: -------------------------------------------------------------------------------- 1 | // Left Controller Code (I2C Master and XInput) 2 | //I2C section created by https://www.twitch.tv/gilbertsgadgets and 3 | //Special shoutout to GrayArtificer for I2C foundation 4 | /* 5 | Project Arduino XInput Library 6 | @author David Madison 7 | @link github.com/dmadison/ArduinoXInput 8 | @license MIT - Copyright (c) 2019 David Madison 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | THE SOFTWARE. 27 | 28 | Example: GamepadPins 29 | Description: Uses all of the available pin inputs to build a 'complete' 30 | Xbox gamepad, with both analog joysticks, both triggers, 31 | and all of the main buttons. 32 | 33 | * * Joysticks should be your typical 10k dual potentiometers. 34 | * * Triggers can be either analog (pots) or digital (buttons). 35 | Set the 'TriggerButtons' variable to change between the two. 36 | * * Buttons use the internal pull-ups and should be connected 37 | directly to ground. 38 | 39 | These pins are designed around the Leonardo's layout. You 40 | may need to change the pin numbers if you're using a 41 | different board type 42 | 43 | */ 44 | 45 | #include 46 | #include 47 | byte x = 0; 48 | 49 | // Setup 50 | const boolean UseLeftJoystick = true; // set to true to enable left joystick 51 | const boolean InvertLeftYAxis = true; // set to true to use inverted left joy Y 52 | 53 | const boolean UseRightJoystick = true; // set to true to enable right joystick 54 | const boolean InvertRightYAxis = true; // set to true to use inverted right joy Y 55 | 56 | const boolean UseTriggerButtons = false; // set to false if using analog triggers 57 | 58 | const int ADC_Max = 1023; // 10 bit 59 | const int ADC_Max_Trigger = 900; 60 | 61 | // Joystick Pins 62 | const int Pin_LeftJoyX = A0; 63 | const int Pin_LeftJoyY = A1; 64 | 65 | // Trigger Pins 66 | const int Pin_TriggerL = A2; 67 | 68 | const int Pin_ButtonLB = 5; 69 | 70 | const int Pin_ButtonBack = 6; 71 | 72 | const int Pin_ButtonL3 = 9; 73 | 74 | // Directional Pad Pins 75 | const int Pin_DpadUp = 10; 76 | const int Pin_DpadDown = 16; 77 | const int Pin_DpadLeft = 14; 78 | const int Pin_DpadRight = 15; 79 | 80 | /********* Data packet format *************/ 81 | 82 | typedef struct { 83 | boolean buttonA; 84 | boolean buttonB; 85 | boolean buttonX; 86 | boolean buttonY; 87 | boolean buttonRB; 88 | boolean buttonStart; 89 | boolean buttonR3; 90 | int triggerRight; 91 | int joyX; 92 | int joyY; 93 | } RIGHT_CONTROLLER_BUTTONS; 94 | 95 | RIGHT_CONTROLLER_BUTTONS RightButtonStates; 96 | 97 | /********************* SETUP FUNCTION ************************/ 98 | 99 | void setup() { 100 | // Serial.begin(9600); 101 | // Serial.println("Serial started"); 102 | 103 | Wire.begin(); // join i2c bus (address optional for master) 104 | // Serial.println("I2C has begun"); 105 | 106 | // If using buttons for the triggers, use internal pull-up resistors 107 | if (UseTriggerButtons == true) { 108 | pinMode(Pin_TriggerL, INPUT_PULLUP); 109 | } 110 | // If using potentiometers for the triggers, set range 111 | else { 112 | XInput.setTriggerRange(0, ADC_Max_Trigger); 113 | } 114 | 115 | // Set buttons as inputs, using internal pull-up resistors 116 | pinMode(Pin_ButtonLB, INPUT_PULLUP); 117 | 118 | pinMode(Pin_ButtonBack, INPUT_PULLUP); 119 | 120 | pinMode(Pin_ButtonL3, INPUT_PULLUP); 121 | 122 | pinMode(Pin_DpadUp, INPUT_PULLUP); 123 | pinMode(Pin_DpadDown, INPUT_PULLUP); 124 | pinMode(Pin_DpadLeft, INPUT_PULLUP); 125 | pinMode(Pin_DpadRight, INPUT_PULLUP); 126 | 127 | XInput.setJoystickRange(0, ADC_Max); // Set joystick range to the ADC 128 | XInput.setAutoSend(false); // Wait for all controls before sending 129 | 130 | XInput.begin(); 131 | } 132 | 133 | /******************* LOOP FUNCTION **********************/ 134 | 135 | void loop() { 136 | // Get the values of the buttons from the right controller via I2C 137 | // Send the request 138 | Wire.requestFrom(0x08, sizeof(RIGHT_CONTROLLER_BUTTONS)); // request data from I2C device with address 0x08 139 | // Serial.println("Request Sent"); 140 | 141 | // Give the right controller a little time to respond (Might not be needed) 142 | // delay(10); 143 | 144 | int i=0; 145 | char* RightButtons = (char*) &RightButtonStates; 146 | while (Wire.available()) { // slave may send less than requested 147 | if(i == sizeof(RIGHT_CONTROLLER_BUTTONS)) { 148 | // We're about to overflow our struct. Abandon ship! 149 | // Serial.println("Too much data!"); 150 | // Flush the buffer before we leave 151 | while (Wire.available()) { (void) Wire.read(); } 152 | } else { 153 | RightButtons[i] = Wire.read(); // receive a byte as character 154 | i++; //Increment to next byte in memory 155 | } 156 | } 157 | 158 | // Serial.print(i); 159 | // Serial.println(" bytes of data received."); 160 | 161 | if(i < sizeof(RIGHT_CONTROLLER_BUTTONS)) { 162 | // Serial.println("***Too little data received. Caution is advised.***"); 163 | } 164 | 165 | // Assign the values received over I2C to our button variables 166 | boolean buttonA = RightButtonStates.buttonA; 167 | boolean buttonB = RightButtonStates.buttonB; 168 | boolean buttonX = RightButtonStates.buttonX; 169 | boolean buttonY = RightButtonStates.buttonY; 170 | boolean buttonRB = RightButtonStates.buttonRB; 171 | boolean buttonStart = RightButtonStates.buttonStart; 172 | boolean buttonR3 = RightButtonStates.buttonR3; 173 | 174 | // Serial.println("\nRight Controller Buttons\n=============================="); 175 | // Serial.print("Button A: "); 176 | // Serial.println(buttonA); 177 | // Serial.print("Button B: "); 178 | // Serial.println(buttonB); 179 | // Serial.print("Button X: "); 180 | // Serial.println(buttonX); 181 | // Serial.print("Button Y: "); 182 | // Serial.println(buttonY); 183 | // Serial.print("Button RB: "); 184 | // Serial.println(buttonRB); 185 | // Serial.print("Button Start: "); 186 | // Serial.println(buttonStart); 187 | // Serial.print("Button R3: "); 188 | // Serial.println(buttonR3); 189 | // Serial.println("==============================\n"); 190 | 191 | // Read pin values and store in variables 192 | // (Note the "!" to invert the state, because LOW = pressed) 193 | boolean buttonLB = !digitalRead(Pin_ButtonLB); 194 | 195 | boolean buttonBack = !digitalRead(Pin_ButtonBack); 196 | 197 | boolean buttonL3 = !digitalRead(Pin_ButtonL3); 198 | 199 | boolean dpadUp = !digitalRead(Pin_DpadUp); 200 | boolean dpadDown = !digitalRead(Pin_DpadDown); 201 | boolean dpadLeft = !digitalRead(Pin_DpadLeft); 202 | boolean dpadRight = !digitalRead(Pin_DpadRight); 203 | 204 | /************** XInput *******************************/ 205 | 206 | // Set XInput buttons 207 | XInput.setButton(BUTTON_A, buttonA); 208 | XInput.setButton(BUTTON_B, buttonB); 209 | XInput.setButton(BUTTON_X, buttonX); 210 | XInput.setButton(BUTTON_Y, buttonY); 211 | 212 | XInput.setButton(BUTTON_LB, buttonLB); 213 | XInput.setButton(BUTTON_RB, buttonRB); 214 | 215 | XInput.setButton(BUTTON_BACK, buttonBack); 216 | XInput.setButton(BUTTON_START, buttonStart); 217 | 218 | XInput.setButton(BUTTON_L3, buttonL3); 219 | XInput.setButton(BUTTON_R3, buttonR3); 220 | 221 | // Set XInput DPAD values 222 | XInput.setDpad(dpadUp, dpadDown, dpadLeft, dpadRight); 223 | 224 | // Set XInput trigger values 225 | if (UseTriggerButtons == true) { 226 | // Read trigger buttons 227 | boolean triggerLeft = !digitalRead(Pin_TriggerL); 228 | boolean triggerRight = !RightButtonStates.triggerRight; 229 | 230 | // Set the triggers as if they were buttons 231 | XInput.setButton(TRIGGER_LEFT, triggerLeft); 232 | XInput.setButton(TRIGGER_RIGHT, triggerRight); 233 | } 234 | else { 235 | // Read trigger potentiometer values 236 | int triggerLeft = analogRead(Pin_TriggerL); 237 | int triggerRight = RightButtonStates.triggerRight; 238 | 239 | // Set the trigger values as analog 240 | XInput.setTrigger(TRIGGER_LEFT, triggerLeft); 241 | XInput.setTrigger(TRIGGER_RIGHT, triggerRight); 242 | } 243 | 244 | // Set left joystick 245 | if (UseLeftJoystick == true) { 246 | int leftJoyX = analogRead(Pin_LeftJoyX); 247 | int leftJoyY = analogRead(Pin_LeftJoyY); 248 | 249 | // White lie here... most generic joysticks are typically 250 | // inverted by default. If the "Invert" variable is false 251 | // then we need to do this transformation. 252 | if (InvertLeftYAxis == false) { 253 | leftJoyY = ADC_Max - leftJoyY; 254 | } 255 | 256 | XInput.setJoystick(JOY_LEFT, leftJoyX, leftJoyY); 257 | } 258 | 259 | // Set right joystick 260 | if (UseRightJoystick == true) { 261 | int rightJoyX = RightButtonStates.joyX; 262 | int rightJoyY = RightButtonStates.joyY; 263 | 264 | if (InvertRightYAxis == false) { 265 | rightJoyY = ADC_Max - rightJoyY; 266 | } 267 | 268 | XInput.setJoystick(JOY_RIGHT, rightJoyX, rightJoyY); 269 | } 270 | 271 | // Send control data to the computer 272 | XInput.send(); 273 | 274 | delay(50); 275 | 276 | } 277 | --------------------------------------------------------------------------------