In my opinion, one of the more novel things you can do with an Arduino is put it to use as a custom game controller for your favorite games. Whether you’re retrofitting a Nerf gun, converting a rhythm controller to play an FPS game, or playing PUBG with a frying pan – using an Arduino makes it quick and easy to build your own custom controller.

In this tutorial, I’m going to show you how to program your own Arduino to emulate an Xbox controller using the ArduinoXInput library.

Getting Started

Hardware

The first thing you’ll need is an Arduino-compatible microcontroller that has native USB support. Supported boards include:

Using a Leonardo or 5V Pro Micro is my usual suggestion, although if you need a little more ‘oomph’ you can pick up a Teensy LC or Teensy 3.2. This is not a complete list however! Check the supported boards list in the library repository to see if your board is compatible.

Note that the Arduino Uno, Nano, and Mega are missing from this list. Those three boards do not have native USB support and will not work for this. You will need to buy another microcontroller. Sorry Charlie 🙁

Software

The next thing you’ll need is the software to make this work. There are two separate but related pieces: the boards package that contains the USB descriptors for the XInput controller, and the library that makes it easy to interact with.

Boards Package

First, you’ll need a copy of the XInput boards package for your specific microcontroller. Because of the way this XInput emulation works it’s not possible to include the USB descriptors with the library – it requires modifying the Arduino core files which means installing some purpose-built boards files.

As of this writing, there are three boards packages available:

You need to download the one(s) required for your microcontroller and install them by copying the relevant files to your Arduino installation’s ‘hardware’ folder. Make sure you have the latest version of the Arduino IDE to avoid any issues.

After you have installed the new boards packages, restart your IDE and you should see the new boards available in the ‘Tools’ menu. The AVR boards will have “w / XInput” in their name.

XInput AVR boards, properly installed and available in the IDE

XInput Library

Next, you’ll need a copy of the ArduinoXInput library. You can install this like any other Arduino library – download the latest release as a .zip file and use the IDE’s library manager to install it ( Sketch -> Include Library -> Add .ZIP Library ).

Upload Instructions

A word of warning: as a consequence of using the USB layout of an XInput device, the programmer auto-reset will no longer work. Don’t worry, you can still use the bootloader to program the board! You will just have to reset it by hand when uploading. (This section does not apply if you’re using a Teensy 3 board or a dedicated programmer.)

To do this you need to know where the ‘reset’ button is on your Arduino. If your board does not have a ‘reset’ button, you can wire your own by momentarily connecting the ‘reset’ pin to ground.

To upload a new sketch, connect the board to your computer using a USB cable, and make sure you have the proper board selected in the IDE’s ‘boards’ menu (with or without XInput). If the board already has an XInput sketch on it, you do not need to select a serial port. You should also turn on ‘verbose output’ for uploading in the Arduino IDE’s preferences (File -> Preferences). This makes it easier to time the manual reset and see if the upload succeeded.

To upload to the board:

  • Press the ‘Upload’ button in the IDE
  • Wait until the status bar says “Uploading…”
  • Press the reset button twice, quickly

If you did these steps properly, the board should reset to the bootloader and the upload should begin. AVRDUDE will do its thing and you should see avrdude done. Thank you. near the bottom of the output window. Note that the IDE may say that it “Couldn’t find a Board on the selected port” even if the upload succeeded. This is why it’s important to turn on the verbose uploading output – look for the avrdude message.

Do not upload XInput sketches to your Arduino unless you know how to reset it! Otherwise you will not be able to program it anymore and you’ll have to reflash the bootloader with an external programmer. You’ve been warned…

Hello World

Now it’s time to put this baby to the test. In the IDE’s menu, go to File -> Examples -> XInput and load the “Blink” example. Here’s what it looks like:

The example will press and then release the controller’s “A” button every two seconds. It’s a good way to test that the XInput emulation is working properly.

Double check that you have the correct board selected in the “Tools” menu and that you’re using the “w/ XInput” version. Upload the sketch to the board, making sure you follow the uploading instructions above.

On Windows you can test the output by using either the joystick control panel (Run: joy.cpl) or, preferably, a nicer interface like GamepadViewer.com (here’s the link for player 1). You should see the “A” button (#1) steadily blinking on and off. Success!

Using the Library

“Blinking” buttons is fun, but we’re just scratching the surface with what’s possible. The library gives you access to the following controls available on the Xbox 360 controller:

  • 10 + 1 Digital Buttons
  • 2 Analog Joysticks (16 bit)
  • 2 Analog Triggers (8 bit)
  • 1 Four-Way Directional Pad (D-Pad)

In addition to these, you can also read the state of the virtual rumble motors (big and small), and even get the pattern that is running on the LED ring. Let’s jump in!

Setup

To set up the library, you need to include the library header and then call XInput.begin() once in the setup function:

The USB data is automatically handled by the library, although you can choose to handle the output yourself if you want to update a bunch of controls at once:

Digital Inputs

Buttons

There are three functions for dealing with the buttons: press(), release(), and setButton():

There are ten buttons on the controller, plus the center ‘Xbox’ button. Here is the list:

Pass these values into the button functions above to ‘press’ or ‘release’ the respective buttons.

Directional Pad (D-Pad)

The directional pad inputs function the same as buttons, and you can use the button functions (above) to manipulate them with the following names:

You can also use the setDpad() function to set all four of these at once. The function takes four boolean values, one for each direction: up, down, left, and right:

For use with fighting games, this function also includes a simultaneous opposite cardinal directions (SOCD) cleaner to avoid erroneous states (up and down is up, left and right is neutral).

Analog Inputs

Joysticks

There are two joysticks: JOY_LEFT and JOY_RIGHT. Both of these have two axes (XY) and use 16-bit signed values (-32768 is min, 32767 is max, and 0 is centered). The north-east corner of the joystick is positive for both axes.

To set the joystick value, call the setJoystick() function. The first argument is the joystick ID, the second is the ‘x’ position, and the third is the ‘y’ position:

As of version 1.1.0 there is also a function to set the joystick using four digital inputs (like a direction pad), for use with arcade joysticks that use micro switches instead of potentiometers:

Version 1.2.0 also adds functions to manipulate the joystick axes separately, in case you’re mapping independent potentiometers (e.g. for a racing wheel) or other types of inputs. Prior to this, you would have to pass the ‘center’ value for the pre-scaled joystick range for the other axis:

Triggers

There are two triggers: TRIGGER_LEFT and TRIGGER_RIGHT. These use 8-bit unsigned values (0 is min, 255 is max) where the trigger is normally ‘off’ at 0. You can set the values for these by calling the setTrigger() function:

You can also use button functions (press(), release(), etc.)  to manipulate the analog triggers as if they were digital buttons. This will ignore the range and just set them to their min / max values.

Range Rescaling

The library provides built-in range rescaling options for both the joysticks and the triggers! This means you can set an input range for the functions and pass the raw values to the library without having to rescale them beforehand. For example, you could set an input range of 0 – 1023 to use a value from analogRead(), or set a range of 48 – 57 to use an ASCII number value from ‘0’ through ‘9’.

This rescaling is entirely optional. If you don’t set a custom range, the functions will use their output ranges by default (16-bit signed for the joysticks, 8-bit unsigned for the triggers). Values outside of the output range will be clipped to the min/max values, so you don’t have to worry about overflow.

Debug Mode

I know this can get confusing and it can be difficult to keep track of what’s going on without being able to print over serial, so the library comes with a ‘debug mode’! Just set your USB-capable board to the non-XInput version and the library will print a nicely formatted output of all of the control states for you, instead of acting like an Xbox controller:

Rumble Motors and LED Patterns

The library also gives you access to the XInput data the Arduino receives. Namely, the state of the gamepad’s two rumble motors and the pattern playing on the LED ring.

There are two rumble motors: a ‘big’ one in the left grip and a ‘small’ one in the right grip. These are stored as 8-bit unsigned integers (0 – 255), where the normal state is ‘0’ for off. There are functions for getting these values separately, or combined into a single packed 16-bit value:

The LED patterns work similarly. The pattern IDs can be retrieved by calling the getLEDPattern() function:

You can find an enumeration of the available LED patterns in the library’s header file. This library does not handle the LED animations themselves. For that you can use the Xbox 360 Controller LEDs library. For more information on the LED animations, see this blog post.


If you want to deal with these values as they come in, check out the ReceiveCallback example.

Example: Rocket League Controller

Now that you know the basics of using the library, let’s build a simple example to put it into practice! In this case, a barebones controller for Rocket League.

Wiring

I’m going to be using some basic components that you should have on hand. You’ll need a breadboard, two tactile push-buttons switches, a 10K potentiometer or joystick, and an XInput-capable Arduino (I’m using an Arduino Leonardo). You’ll also need a few wires to connect everything together.

Both switches are going to be wired using the Arduino’s internal pull-ups. One pin of the button should connect to a digital IO pin and the other pin of the button should connect to ground. I’m using pins 2 and 3, but you can use whatever pins you like. The potentiometer should have pin 1 connect to 5V, pin 2 connect to an analog pin (I’m using A0), and pin 3 connect to ground. Here’s a Fritzing diagram of the completed circuit:

 

Programming

Now it’s time to connect these hardware inputs to the XInput outputs! There are a myriad of controls in Rocket League, but for now we’ll just focus on three:

  • Turning – Left Joystick X Axis
  • Moving Forward – Right Trigger
  • Jumping – Button ‘A’

At the top of the sketch, we’ll start by including the library and then defining some constants for our pins:

Next up is the setup() function. Here we’ll initialize the pins we’re using, set the input range for the joystick (potentiometer), and ‘begin’ the library:

Then in the loop() function we’ll add the bits we want to run continuously. First we’ll read the pin states using the built-in digitalRead and analogRead functions and then assign those values to the XInput controls, which are automatically sent to the PC:

Note that the digital read functions have a not operator (‘!’) to invert the output because we’re using the pull-up resistors, so ‘LOW’ is ‘pressed’. You can also see that I’m treating the trigger like a digital output by using the setButton function instead of the typical setTrigger function that takes an analog value. The potentiometer value is written to the joystick ‘x’ axis, and the ‘y’ axis is centered based on the constant we set at the top of the code.

Lastly, we’ll check whether the Arduino has received any commands to turn on the rumble motors. If the motors are supposed to be rumbling, we’ll turn on the built-in LED. Otherwise, we’ll turn off the LED:

And that’s it! Just 50 lines of code and we’re up and running. Here’s the sketch in full:

Soccar!

Time to take this baby for a spin! Upload the sketch, boot up the game, and head into Free Play (Training) to try it out!

Tweaking

It’s time to practice what you’ve learned! Try to make some changes to the Rocket League controller sketch. Here are some ideas to get you started:

  • Change the ‘jump’ button (A) to ‘boost’ (B)
  • Make the turning more responsive (narrow the range)
  • Turn off the ball-cam (Y) whenever the controller rumbles

If you have the extra hardware, try replacing the potentiometer with a two-axis thumbstick or add a few extra buttons for the missing controls.

Further Reading

That should give you a good overview of how to use the ArduinoXInput library to emulate an Xbox controller. For further reading, check out some of the library’s built-in examples. You can find them either through the IDE by going to File -> Examples -> XInput, or in the library’s repository on GitHub.

This library opens up so many possibilities. Let me know what you create!

21 Comments

  1. Thank you so much for this! I just spent about six hours researching HID/PID report descriptors in order to get force feedback to work. Then I thought Xinput might work and wow does it seem so much easier with your code. I’ll let you know how it works out for me.

    Eric F
  2. Hi,

    I have installed & copied the files from “ArduinoXInput-master” to Arduino/hardware folder. But I could not find any new X/ input boards from the Tools list..

    Please help

    Nishar Ahamed
    1. “ArduinoXInput-master” is the library, which goes in your libraries folder. You need the boards files, which are specific to the board you’re using.

      The installation instructions are slightly different depending on which board you’re using. Please read the documentation on GitHub closely.

      Dave
  3. Hi,

    Is there anybody who is tested it with xbox 360 console? Is this works with 360 console?

    I have a plan about convert ps2 mouse and keyboard data for 360 console. If this library and hardware work with 360 console, i think i can make it.

    Ertugrul
    1. No. This emulates the Xbox 360 wired controller and the hardware doesn’t support displaying itself as multiple USB devices simultaneously. You would need multiple Arduinos to emulate multiple controllers.

      Dave
  4. I got the PC to see my Teensy LC as an Xbox controller with the Triggers being buttons but when i hook mine up to a Raspberry Pi running Retropi… triggers are not recognized. Everything else is.

    Justin
    1. Yep, I just tested that as well and it looks like RetroPie refuses to see the trigger axis unless it moves progressively and not all at once. If I were you I’d report a bug to the RetroPie GitHub repository with steps to reproduce.

      For a workaround you could write a function to ‘step’ the trigger from pressed/released over a series of updates, but that will introduce some delay.

      Dave
  5. Hi, is it possible to use 4 pots (two for x axis and two for y axis) instead of just 2 (one for x axis and one for y axis)? I was messing around with another xinput library (MSF-XINPUT) and I have exhausted all possibilities without a positive result so I thought that maybe your library will be more flexible. I need every direction (left, right, up, down) to hove its own potentiometer.

    Piotr
    1. Yes and no.

      You can use two potentiometers for each direction but there’s no way for them to be wholly separate because they feed into a single axis. The controller doesn’t have values for each direction, just a sliding scale for each axis. Let me put it this way: if you have 25% left and 25% right it’s no different than having 0% left and 0% right because they get summed together before they’re sent to the PC. The PC won’t see any change so long as both axes are perfectly opposed. That has nothing to do with the library, that’s just the way joysticks work.

      That being said, you can absolutely do that. I don’t know why you’d want to, but it’s easy enough to do. Read from two potentiometers, invert one value and sum the result, then pass it into the joystick function.

      Remember that the library is just a tool not the complete solution. You’ll almost always have to write a little bit of code to supplement it.

      Dave
      1. Many thanks for the prompt reply. I was able to use 4 separate pots to control one analog stick by modifying the MSF-XINPUT so I am not a complete newbie but I was not able to make both analog sticks (left and right) work simultaneously on my Teensy LC. Mostly because I’ve used “while” functions and it is impossible to have two “while” functions running at the same time. With that being said I will try now your solution by read from two potentiometers, inverting one value and summing the result.

        Piotr
      2. Sorry for bothering you but your library is much more complicated (although I think meticulous would be a better word) and I am struggling a bit with it. I guess this is the first part I need to modifie?

        const int Pin_LeftJoyX = A0;
        const int Pin_LeftJoyY = A1;
        const int Pin_RightJoyX = A2;
        const int Pin_RightJoyY = A3;

        and this is the second part:

        if (UseLeftJoystick == true) {
        int leftJoyX = analogRead(Pin_LeftJoyX);
        int leftJoyY = analogRead(Pin_LeftJoyY);

        if (InvertLeftYAxis == false) {
        leftJoyY = ADC_Max – leftJoyY;
        }

        XInput.setJoystick(JOY_LEFT, leftJoyX, leftJoyY);

        Can you help me by adding the necessary lines of code in the second part? Normally I would not ask for such a thing but it would take a minute to type these few lines of code for you and hours of trial and error for me.

        Piotr
        1. That’s part of an example, not the library itself. You shouldn’t need to modify the library at all to get this done.

          Just read from whatever potentiometers you want, add them together, and call the joystick ‘set’ function. Something like:

          int x = analogRead(Pin_JoyLeft) + -analogRead(Pin_JoyRight);
          int y = analogRead(Pin_JoyUp) + -analogRead(Pin_JoyDown);
          XInput.setJoystick(JOY_LEFT, x, y);

          Then you just need to set the library’s joystick input range to 0 – 2046 (i.e. 1023 * 2). If you get stuck look at some of the other examples.

          Dave
          1. One last thing just be sure. Where exactly can/should I set the library’s joystick input range to 0 – 2046 ? Remember I am not that familiar with your code as you are.

            Piotr
    1. Not with this method, no. To get this to work I reverse engineered the Xbox controller’s USB descriptors – the Bluetooth functionality is a different animal altogether.

      Dave

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.