Recently I’ve been playing around with building various alternative controller projects for games, typically using an Arduino-compatible microcontroller acting as an HID input device of some sort. The Arduino ecosystem makes it easy to set up these projects to act as either a Keyboard, a Mouse, a DirectInput Joystick, or a composite device that’s a combination of the above. Unfortunately back in 2005 DirectInput was supplanted by XInput with the release of the Xbox 360 controllers, and modern games have been weaning off of it ever since.
These days, many mainstream games barely support DirectInput at all. Games like Rocket League and Overwatch won’t even recognize a DirectInput joystick – you have to use XInput controller emulation software that can be tricky to set up and doesn’t work with every game.
Wouldn’t it be great if there was a simple, turnkey way to make your Arduino emulate an Xbox controller and work out of the box with these newer games?
Introducing: Arduino XInput!
The Arduino XInput Library makes it easy to convert most USB capable Arduino boards into a fully fledged Xbox controller that is plug and play with Windows. The library gives you access to all of the controls available on an Xbox 360 controller:
- 10 + 1 Digital Buttons
- 2 Analog Joysticks (16 bit)
- 2 Analog Triggers (8 bit)
- 1 Four-Way Directional Pad (D-Pad)
The library also processes received data, so you can read the status of the controller’s 2 rumble motors (8-bit), the assigned player number (1-4), and the index of the current LED animation. Data is sent and received automatically over USB, so the Arduino is free to do whatever else you want in your main sketch.
The Arduino framework makes it easy to append the USB descriptors but not to replace them altogether. This means that unlike most USB mode libraries (Keyboard, Joystick, MIDI, etc.), this library will not work out of the box without some additional configuration. In this case, a boards package specific to the Arduino you’re trying to use.
I’m launching the library with three boards packages, which add support for the USB capable Arduino AVR boards, SparkFun AVR boards, and the Teensy 3 boards. Over twenty microcontrollers are supported, including:
For a full list of supported boards, see here. Note that Arduino boards without native USB support (Uno, Nano, Mega) are not supported.
Much as I hate to say it, unlike most of my other projects this library comes with two main caveats – two main limitations that make this significantly less useful than it could potentially be.
No Console Support
Unfortunately although this will enable the Arduino to act like an Xbox controller for a PC, it will not work with an Xbox console. The Xbox 360 controller contains a dedicated security chip that is used to verify that only authorized devices can communicate with the console. As far as I’m aware this security method has not been fully broken (at least not openly). Without this security chip or a software approximation of it, the Arduino will not be able to communicate with the console.
With some reverse engineering it should be possible to filter for these requests and build a system to pass them on to a security chip that has been desoldered from a genuine controller, but that’s a project for another day.
No Commercial Use
This is a harder pill to swallow but it’s a necessary evil. For this to work the Arduino board emulates an Xbox 360 wired controller, which means it also borrows the use of Microsoft’s USB VID and PID so that the Windows driver will pick it up. Without this VID/PID combination the driver will ignore it, and it won’t function as a controller.
Because this project makes use of Microsoft’s identifiers to work, this is strictly for non-commercial use. Sorry!
For the Future
There’s still some room to grow with this project, mostly in regards to supported boards. Originally this started as a spinoff of Zach Littell’s XInput project working for the Teensy, but I’ve since added support for AVR boards using the Arduino USB stack. With the Arduino IDE’s ability to reference other core files, it’s quite easy to add support for additional boards that use the same architecture (see the SparkFun AVR changes for reference). I believe I’ve covered the basic boards most people will want to use, although with the core defined additional USB capable AVR boards should take only a few minutes to add.
Eventually I’d like this to support other architectures such as ARM. I’ve added some documentation to the library repository explaining how the library’s USB API works, so if anyone wants to build support for other boards please be my guest!
The library is available to download on GitHub, as are the required boards packages. I’ve also written a tutorial post for how to get started with the library. Enjoy!
Martin A · May 28, 2019 at 1:34 pm
Hey, I wanted to try the code for a small project.
But after installing both the library and the boards files and loading the blink test and I try to compile with one xinput boards selected it fails with:
“cc1.exe: error: -fno-fat-lto-objects are supported only with linker plugin.”
With the standard boards (ex. leonardo) the code compiles fine..
I’m on Win10, and Arduino 1.8.9.
Dave · May 28, 2019 at 2:18 pm
That error seems to indicate that there’s a problem with the XInput boards finding the path for the compilation tools. I’d double check that you’ve installed the boards correctly.
If you still need help, please create an issue on the relevant boards repository with full steps to reproduce and I can look into it more.
Martin A · May 29, 2019 at 1:30 am
Hi, just tested on another machine (a raspberry pi) and there it works. So guess somethings borked on my windows machine. 🙂
Btw, is there any chance of making this work on the 16u2 on a Arduino Mega? So that the 16u2 just sets up a controller over the usb and then have a simple serial protocol to talk to the main atmega mcu..
Dave · May 29, 2019 at 9:02 am
Fantastic, glad to hear you got it working.
At this time I don’t have any interest in building a set of tools to get ArduinoXInput working on the Uno/Mega’s 16U2. Personally I’ve always thought that the 16U2 USB mode firmwares were a bit hack-y. Although if there’s enough interest I might come back to this and add 16U2 support at a later date.
If you’d like to tackle it yourself, I have another blog post that breaks down the USB descriptors required. You’ll also need to make a few modifications to the endpoints in order to get them to play nice with the AVR USB controller.
Joe · June 20, 2019 at 4:02 am
Cool thing you did here! I got a few knock off Leonardo boards and tried this for my arcade machine that I built.
Question… When I use them, they work pretty well apart from a small number of PC games (which I assume they are just really fussy with controllers). I’m using a program called reWASD, which remaps X-input buttons to keyboard keys or other X-Input buttons. When I map to keyboard keys and test it out on Notepad, each button press will type out the letter two or three times. It seems that every time I press a button, it actually activates 2 or 3 times. It’s probably been doing that even before I used the remapping program, but it just didn’t cause an issue. It’s definitely the Leonardo board, because it doesn’t happen with a real Xbox controller.
OK, so finally the actual question… do you think it’s an issue with the knock-off Leonardo board? A driver issue? Or and issue with the code that it’s flashed with?
You can test it yourself if you’re interested. The software is called reWASD and there’s a free trial. You just need to map a button to a keyboard and test it on Notepad. Don’t use any of the shift-key function as you’ll need a license.
Dave · June 20, 2019 at 5:03 am
Out of curiosity, which PC games were giving you trouble? Was the controller not working at all or was it not behaving like you expected?
What you’re experiencing is called ‘bouncing‘. When you press down on the switch the metal contacts’ springiness can cause them to push apart momentarily before they make steady contact. The Xbox controller probably have some on-board “debouncing”, either in hardware or software. My library is intentionally barebones because it gives you precise control over the outputs, but that means if you want the outputs debounced you need to do it yourself.
Check out something like the Bounce2 library, which provides easy to use software debouncing. Just call the XInput ‘set’ functions whenever the debounce object’s state changes. As you experienced, foregoing the debouncing usually isn’t an issue because most games can’t take input that quickly anyways. But it usually doesn’t hurt to add.
For what it’s worth, if you’re just using the Leonardos for keyboard inputs you could use the Keyboard library instead of my XInput one and forego the remapping software. Unfortunately you can’t use them at the same time.
Hope that helps!
Joe · June 20, 2019 at 7:49 am
Thanks for the reply.
I’ll get back to you on those games that I have trouble with. But in the meantime, the bouncing thing you’re talking about… It sounds like a great theory, but sometimes the button deactivates while I still have my finger on the button and I have to let the button go and press it again to activate it again. Would that still be a “bouncing” issue?
Joe · June 20, 2019 at 8:14 am
Actually, please ignore that last comment. The deactivation when my finger is still on the button only happens with the mapping software.
Dieter P. · July 10, 2019 at 8:48 am
Hi Dave, thanks for your great work!
Unfortunately I always get the same error as Martin A. when compiling for Arduino boards using Arduino IDE 1.8.9 Windows10 x64:
cc1.exe: error: -fno-fat-lto-objects are supported only with linker plugin
When compiling for Teensy another error appears:
So currently no way to compile for AVR and Teensy, Which IDE version did you use?
Thanks in advance!
Dave · July 10, 2019 at 11:20 am
I tested with 1.8.9 on both Windows and Linux before release.
Questions: Are you installing the IDE from the Windows 10 store or from the standalone installer? What directory are you installing to? When you install the XInput boards are you installing to the AppData cache or the installation directory? Are you sure you’re using the matching Teensyduino version?
The Teensyduino error in particular seems to indicate that you’re installing something incorrectly, because the replacement files for Teensy XInput don’t modify the platform recipes (platform.txt).
Please provide steps to reproduce and I can look into it.
Dieter P. · July 11, 2019 at 5:45 am
thanks a lot for your answer!
Well, what should I say, everything is working now 😉
I reinstalled everything from scratch, and compiling now works flawlessly on both Windows and my Raspberry.
So, thanks again for your great work, looking forward to my lightgun-project using your library.
Dave · July 11, 2019 at 3:50 pm
Glad to hear you got it working :). Good luck on the project!
Green · October 11, 2019 at 8:17 am
This library looks really amazing and I’m happy it exist. This enables me to make my own controller and I have plans for it. But I have a couple questions about it. Two to be exact.
Firstly the Led feedback. I understand how to get feedback and that there is another library around for playing the animations with actual leds but that’s not what I need. I want to know which animation plays for the values of the uint8_t value of XInput.getLEDPattern();.
The second thing is the player led. I really like to know how to use XInput.getPlayer(); or better said when. I couldn’t find anything in the demos or documentation about it. I found it in the Xinput.h but couldn’t figure out how to use it.
If you can help I would greatly appreciate it.
Thanks in advance!
Dave · October 11, 2019 at 10:02 am
Hey there! Glad you’re finding the library helpful.
You can find the full details about the LED patterns (including their index values) in this blog post.
The `getPlayer()` function is pretty self-explanatory, it returns the player (1-4) that’s assigned to the controller by the PC. You’d only need to use it if you need to check the player number for whatever reason, or if you wanted to report the player number to the user. For instance if you’re building a controller specific to a singleplayer game, you could check if the controller is assigned as player 1 otherwise report an error to the player (e.g. status LED, rumble, what-have-you).
Lee Maskell · March 9, 2021 at 3:35 am
Took me a while to figure out (I was using wrong type of variable to store the value), here is example:
// Check for wrong player number – PLAYER 1
uint8_t player = XInput.getPlayer();
if (player == 0 || player == 1)digitalWrite(Pin_wrongPlayer, LOW); //0 = player not connected
if (player == 2 || player == 3 || player ==4)digitalWrite(Pin_wrongPlayer, HIGH); //Send signal to seperate restarter board
This can just be dropped in your normal loop.
I am using this to tell another arduino to restart my 4 Arduino Pro Micro xinput encoders with time delays to fix player order on my 4 player arcade cabinet (pull down the RST pins).
Really appreciate this library Dave!
Dieter P. · July 7, 2020 at 5:12 am
Hi Dave, I finished my Lightgun project mentioned above using your great XInput library and I now think about migrating to a Teensy platform. Do you intend to support Teensy 4.0/4.1 in the near future?
Thanks and regards
Dave · July 7, 2020 at 12:57 pm
Hi Dieter. I have no plans to investigate Teensy 4 support at this time as I’m busy with other projects.
Dieter P. · July 7, 2020 at 2:00 pm
Thanks Dave, so I will upgrade to a Teensy 3.2 “only” 😉
James Potter · October 18, 2020 at 3:16 pm
Nevermind, I’m an idiot. I just saw the UseLeftJoystick and UseRightJoystick flags.
Works perfectly now. Live and learn.
Thanks again, Dave.
Dave · October 18, 2020 at 4:10 pm
Haha, happens to the best of us! I’m glad that you’ve got it working and I’m sincerely flattered that you’re using my library in your course.
Let me know if you run into any other hiccups!
Jermie · December 27, 2020 at 9:47 am
Hi there! Thanks so much for this amazing project! I know i am late to the party (This will be my first time doing any of this) but i am working on a project was wondering if this will work with the “Adafruit Circuit Playground Express”? I know you listed the classic but i was thinking of getting the updated version.
Again thanks so much!
Dave · December 27, 2020 at 10:13 am
Unfortunately the updated version of the Circuit Playground board uses a Cortex M0 processor (SAMD21) which is not compatible with the library. The microcontroller itself has the hardware to support it, but no one has built the requisite “boards” files for that architecture.
Karen · March 29, 2021 at 11:28 am
Thanks for the xinput library, well done!
I just bought a Leonardo board last week and was able to download your example code (Blink and SimulateAll) to it and see the output on the console. When I use an app like https://gamepad-tester.com/ it doesn’t recognize the Leonardo as an xbox controller. Is there something else I should be checking?
Dave · March 29, 2021 at 12:27 pm
Hi Karen. Make sure you are using the “XInput” board files and that you have the “XInput” version of the Leonardo board selected.
Katie · April 21, 2021 at 2:00 am
My project is an adapter dongle to plug PSX / PS2 controllers into a PC and use them like any other controller. I’m using an Arduino Micro for it’s USB support and I’m 99% there – I integrated your library and board packages and most everything with the XInput layer worked flawlessly. I notice that in Steam Big Picture mode, you can’t use the D-pad to navigate the menu. Curiously it works just fine in games, I played Spelunky and Dying Light using my controller and literally everything works perfectly, so the D-pad thing is barely an issue to me. The reason I’m writing is that I can’t seem to get the rumble working on the XInput end. I had to do a lot of tweaking with the PSX controller protocol library to support rumble, so I did plenty of my own testing there, I know 100% that the rumble is working from the controller – so now, in my main arduino code loop, every time I read the controller data, I call getRumbleLeft and getRumbleRight and try to use the values to actuate the motors… But nothing happens. Testing it was difficult, I found some app in the windows store that let me hit a button to try and “buzz” the motors, but nothing happens. I do notice that the RX LED lights up very briefly on the arduino, so it must be getting something! Because of the nature of this XInput I don’t have a way to debug, I don’t have a serial to usb converter handy. My next step is to get one, but I thought I’d pose the question here first – has the rumble feature been tested with an Arduino Micro before? Is there any obvious thing I might have overlooked that is causing the XInput library to not be parsing the USB rumble packet?
Dave · April 21, 2021 at 2:38 am
Hi Katie! That sounds like a great project. I was actually thinking of making something like that myself.
This site is great for testing. There is a button at the top for testing rumble that will trigger both motors for ~1 second. I’ve also used Microsoft’s XInput API sample programs to test rumble functionality; one of the demos sets the motor speed to the trigger pull distance. It appears those API demos are no longer available to download from Microsoft but I did find a mirror here.
I have not tested on a Micro myself, but all of the 32U4 boards should behave the same. I just tried the latest version of the library with a Leonardo and the rumble function seems to work fine. For simple debugging you can try lighting an LED, or send the rumble data back across the bus as one of the analog control surface inputs.
And you’re right about Steam’s Big Picture mode and the directional pad. Unfortunately that’s not Arduino specific – it behaves that way with the standard 360 controller as well.
Katie · April 22, 2021 at 4:09 pm
Thanks for the quick reply, Dave!
I truly thought I was going mad on this one. I couldn’t find my serial to USB adapter so I opted for an Arduino Leonardo as a quick and dirty serial to USB adapter. Using this I quickly determined that the XInput library was of course, receiving the rumble packets just fine. Yet I noticed, when I would try calling getRumbleLeft and getRumbleRight, I’d get zero every time even when it was clear the library was getting the request to set rumble values to 0xFFFF. I noticed as well, setReceiveCallback didn’t seem to work, my callback wasn’t being called even though I could clearly see the debug statements I set in your library printing.
That’s when it finally hit me. The rumble values… We’re being set on a different object. Because I was declaring my own instance of XInputController. I deleted it, and switched everything to reference the XInput object already declared, and boom, controller vibrates on command now. Way to go me for not reading the examples clearly enough!
By the way, is it possible to use multiple controllers with XInput?
Dave · April 23, 2021 at 12:01 am
Haha, I’m glad you got it sorted! It’s always the simple solutions we overlook.
It’s not possible to have multiple controllers with one microcontroller, no. You might be able to do that if you emulate the Xbox 360 wireless receiver, but I’ve never tried it myself. It’s probable that the 32U4 does not have enough USB endpoints to support it.
Dimitar · January 17, 2022 at 8:03 am
I have made my own gamepad breakout boards. I love the library I am using it as an use case for my boards. Saved me a bunch of time and afford.
If there is way to contact me privately I would like to send you some!
Dave · January 17, 2022 at 4:21 pm
Hi Mitko! I’m glad you found the library useful. Thank you so much for the offer but I’m afraid I have to pass. My workshop is already overflowing with projects and I don’t think I’d make good use of the boards.
Dimitar · January 18, 2022 at 2:08 am
As you wish! If you change your mind there is a set waiting for you. Cheers!