Controlling Arduino With Gamepad

Introduction: Controlling Arduino With Gamepad

Lately I've been curious about befriending Arduino or any other microcontroller with a gamepad in order to have physical interaction with the things I make, but there seemed to be no fast or cheap way to do so.

Most solutions involved:

Completely dismantling your game controller and bypassing USB logic with some weird contraption made of wires, protoboards and a microcontroller acting as an UART gate, which then passes messages to HC-05 bluetooth module.

Making your own joystick/gamepad based on the above principles

Buying a microcontroller with USB-host functionality and writing a ton of code for USB driver to have a “puppy on a leash”

Using a bunch of third-party software, like Input remapping programs, Processing IDE and Python to do this one simple thing

For quick testing of remote-controlled the prototype (most likely right on the desk or workbench) we need a simple solution with minimal expenses. This is why I've decided to do a little research in this topic and implement small, but somewhat useful software solution to this problem.

Over the course of development I found out that this material will not only be useful in this one particular application, but can also serve as a foundation for much wider range of applications, like data logging systems, PC-based flight control, remote sensor data acquisition etc. etc. etc.

Step 1: INTRODUCTION

The original article is published on my website. This is still work in progress and requires lots of fine-tuning, but that's what DIY is all about - continuous improvement!

The original amount of material I wrote is a bit too big for this Instructable, so in order to save you some time and save myself from repeating the same task over again I will skip some of the stuff and provide a link to an appropriate resource instead.

General concept of my project consists of the following:

We are going to use a wired/wireless gamepad connected to PC

We will implement a lightweight software written in C++ in order to read the current state of XInput Device(gamepad)

If necessary, we can transform current gamepad state into short useful data sequence (button state, axis position etc.), which will be sent over UART to our microcontroller.

Optionally, we can read some data back from microcontroller, like Force-Feedback triggers for gamepad, or plain-simple sensor data.

These principles will also help us to develop the basis for a two-way communication between Arduino(or any other MCU) and a PC, which we can use, for example, for a low-resolution serial camera feed or almost real-time sensor information update.

The main advantages of this method are:

It does not require any hardware modifications, like torturing the gamepad

It will not cost you a penny, given that you have a computer and some means of serial communication( like USB-UART interface, HC-05/06 module etc.)

In this specific situation it will work on any Windows-powered PC with any XInput compatible gamepad (which includes cheap rumblepad/sixaxis clones)

However, it requires at least some basic C++/Arduino programming skills and a little bit of technical know-how.

Step 2: LEARNING SERIAL COMMUNICATION

Before we dive into development process, I'd like you to go over some preliminary reading in order to understand what we are trying to do. I've already compiled a simple tutorial on serial communication (second link), so once you are done, we can start developing a fully functional program to suit our purposes.

We will start with creating 2 simple functions, which will allow to open and close UART connection.

For this you'll need MS Visual C++, pair of hands and caffeine-infused brain.

The COM port initialization is a straightforward process: first we create port configuration portDCB, which contains all the communication settings, and then we assign the port handle. Notice, that port is initialized with CreateFile() function call, and just like with conventional files we can use ReadFile() and WriteFile() to exchange data.

Then we assign the new configuration with SetCommState() function call. If at any step of this process we encounter an error, we will print the appropriate message and return FALSE.

Otherwise, we return TRUE and as a result of execution of UART_Init(), port variable will now point to a serial port handle.

For the purpose of flexibility we will provide the COM port name and its baud rate as arguments of this function. Default settings are set to 8 bit transmission length with 1 stop bit. Parity, error correction and any type of flow control are disabled by default.

As you've already guessed, the next logical step will be implementing functions to send/receive UART messages. The key moment of this part is that we will use communication events, described in MSDN article mentioned earlier.

Assuming that our arduino might be occupied at the time of transmission and could not provide a proper response, we want to wait for EV_RXCHAR event to occur every time RX has incoming data. To address this problem we will set up a communications mask and wait for our event before reading the next byte.

This code initializes port COM8, which is my USB-UART cable (don't forget to change that part to your port #). Then, it sends 100 messages over UART and prints both original message and response. Implementing the communication event listener earlier really paid off at the end. If you look at this program carefully, you'll see that we have only used about a dozen lines of effective code to make it work!

Now, let's setup our Arduino to work as UART loopback device. We will also implement an event-driven UART communication in order to be able to do some other stuff while not transmitting.

Now you can upload the sketch to Arduino, compile the C++ project and test it!

Step 3: GETTING INPUT FROM GAMEPAD

Now, that we know how to send the information to Arduino, we only need to learn how to acquire input from the XBOX gamepad.

In this section we will learn the basics of XInput and write a very simple program, which displays current gamepad state in the console output. We will also learn some important aspects of pre-processing the input values to avoid problematic thumbstick input ranges ("dead zones").

THE BASICS

XInput API provides the means of getting input from XBOX 360 controllers and includes a variety of tools to set the controller effects(farce feedback), process audio input/output for gaming headsets and do other cool stuff.

XInput supports up to 4 controllers, but in our situation only controller #0 will be used as default.

In order to update the current state of the gamepad we will use XInputGetState() function. It takes 2 parameters: the gamepad ID(which is 0 in most cases) and the pointer to XInput state variable. The return value of XInputGetState can be used to check the availability of the gamepad. The value of ERR_SUCCESS means that the gamepad is on, and XInput state now has it's current state.

sThumbLX, sThumbLY, sThumbRX and sThumbRY are 16-bit signed integers, which take values from −32,768 to 32,767. These correspond to current thumbstick positions.

bLeftTrigger and bRightTrigger take values in 0..255 range.

wButtons represents the state of all buttons on an XBox controller, where each bit corresponds to current state of each individual button. If we want to check whether the X button was pressed we need to perform the following operations:

At this point we have all the tools we need to write our first program with XInput. It will be laughably simple, but it will help to understand how this process works and which elements of XInput we need.

Once you build the solution and run your program, you will see the output changing when you move thumbsticks, or push buttons on your gamepad. We will send this data to Arduino board in the next section of this tutorial.

Right now I want you to pay attention to the output, when you are not doing anything. Values of LX, RX, LY and RY are not equal to 0, as we expect them to. This happens for a number of reasons, but what matters most is that we are aware of this phenomena!

These fluctuations and inconsistencies in values are called "dead zones". To get rid of this nasty anomaly we need to find the lowest marginal value at which we can consider that the thumbstick is actually pushed in some direction.

To do so we need to define a deadzone threshold and compare it to current values. Check out MSDN reference for more info.

There are also predefined dead zone values for left and right thumbsticks and triggers. You can use these, or define your own thresholds(in my case ~6500 worked for left and right stick), but remember that these values largely depend on how beat-up your gamepad is!

It has everything you need to know about XInput, including complete reference and helpful programming guide.

That's it for this part. Now, let's try to combine our skills in programming gamepads and serial connections to control Arduino remotely!!! ...in case you forgot where we started...

Step 4: PUTTING PIECES TOGETHER

If you fully understood all the materials in previous steps, you should be able to write an XBox controller-to-UART interface or implement PC-Arduino communication on your own.

As a final example I will use a very simple contraption: an Arduino with few LEDs and the buzzer.

Originally I wanted to build a small RC car, but due to lengthy delays in parts delivery I won't be able to do it at least for another week or so... If you have a pair of EasyDriver boards you could connect direction pin instead of yellow LED, and motor step pin instead of red LEDs(see schematic above). A piezo-buzzer connected to pin D3 reacts on any button press on your controller.

The entire functional description boils down to this:

Read the XBox controller state and transform it into a short, but well defined string. In my case I'm only sending motor speeds, direction and button state, so the message looks like this:LLL RRR D BBBBwhere LLL is the Left Motor speed, RRR is the Right Motor speed, and BBBB represents button state.D is a motor direction, which takes only two values: 1 for forward and 0 for backward.Both LLL and RRR will be normalized for deadzones and scaled to smaller values (under 255)

Alternatively you can send raw XINPUT data to arduino and process it on the microcontroller itself.

Next, we send this message over UART to Arduino

Set all motor speeds to acquired values and check button state to determine additional actions

Send some data back to PC (I'm just sending motor speeds for debugging)

Acquired data is processed into some visual representation. Use anything you like, be it a simple text output in console window, or GUI-based output, like progress bars, graphs, flowcharts or even OpenGL rendering.

We've already learned how to read UART messages using events, so we don't really need to worry about proper timing. PC-side code can be further improved with such cool things, like multi-threading and asynchronous communication, but we won't be doing that today.

Based on the value of LX we set the difference between Left and Right motor speed. If LX is positive, then Left motor is set to current speed value, and Right motor uses(128-LX). If LX is negative, we assign values the opposite way.

On PC side I've created a small class, called XBoxUart, which combines all the things we've learned previously in a single program.

Please, use links below to download the source code for PC and Arduino side.

First, upload the arduino sketch. You can test if it's working by opening a Serial Monitor at 115200 bod and sending data manually. For example message "100 100 1 0" is an equivalent of moving forward 100 steps(left and right motors) with no buttons pressed. In response you should get strings, shown in the screenshot above.

NOTE: Don't forget to change the COM port name to whatever your Arduino CDC is. If you have an HC-05 module, you can connect it directly to Arduino RX and TX pins, if you wanna try it without wires.

Now you can compile the C++ code in Visual Studio, start your XBox controller, launch the program on your PC and see how LEDs(or motors) change their behavior with movement of the thumbstick! Pressing any button will trigger the buzzer. The output in the console window will be similar to what you see on the last screenshot.

Hi man! I'm kind of a noob to this, I did c£ in 2003-05 when in high school and then went into medicine instead. Im wondering is it possible to write a program that runs in the background & reads whenever the xbox controller vibrates while playing a game and then communicates it to the arduino to turn on another rumble motor? For example, built into my pc chair. Im slowly going through tutorials and figuring it out but i still have so far to go.... I was going to use either a leostick, leonardo or mini with built in usb communication etc but im not really sure I can even put it all together haha any ideas or help would be appreciated!

Hi man! I'm kind of a noob to this, I did c£ in 2003-05 when in high school and then went into medicine instead. Im wondering is it possible to write a program that runs in the background & reads whenever the xbox controller vibrates while playing a game and then communicates it to the arduino to turn on another rumble motor? For example, built into my pc chair. Im slowly going through tutorials and figuring it out but i still have so far to go.... I was going to use either a leostick, leonardo or mini with built in usb communication etc but im not really sure I can even put it all together haha any ideas or help would be appreciated! eagleboysbc"gmail.com