Modifying a Classic Game Controller with the Arduino Leonardo

Introduction

In this article we’ll use an Arduino Leonardo board to convert a classic game controller (with an obsolete game port connector) into a modern controller with USB 2.0. The controller we’re converting is an older Logitech Wingman Formula GP racing wheel, but the concepts you learn can be applied to any old controller or pretty much anything that you’d like to use as a controller.

logitech-wingman-formula-gp
Logitech Wingman Formula GP

We’ll configure the Wingman to look like a USB joystick with six buttons. The joystick X-axis will map to the steering wheel and the Y-axis will map to the accelerator and brake pedals. The joystick buttons will map to the buttons on the steering wheel. This generic configuration will allow our modded controller to be compatible with standard joystick device drivers and any racing games that support joysticks.

Project overview

  • Disassemble the Wingman controller to figure out the wiring
  • Connect the wires to the appropriate pins on the Leonardo board; potentiometers (steering and pedals) to analog pins, buttons to digital pins, and +5V & ground
  • Mod the controller case to accommodate the Leonardo and wires
  • Install modified Arduino USB and HID library files to support joysticks
  • Write an Arduino sketch that reads the potentiometers and buttons and sends the data to the connected computer

Important: Be sure to use an Arduino board like the Leonardo or the Micro. They are based on the ATmega32u4m, which has built-in USB communication and lets the boards act like full speed USB 2.0 devices. Other Arduino boards, like the Uno for example, do not have this native capability.

Arduino Leonardo micro controller board
Arduino Leonardo micro controller board

By the way, I researched off-the-shelf game port-to-USB adapters. They are available and they don’t cost too much, but the reviews on them were inconsistent. I don’t blame the adapters though, as manufacturers of game controllers took many liberties with the game port specification. Some were even multiplexing a proprietary data stream over the button wires to quadruple the number of buttons on the controller. Yeah, it was kind of wild back then!

Figure out the wiring

I thought I’d be keeping the DA-15 game port connector intact and adding a matching connector at the Arduino to hook it up. Unfortunately, not all the pins in the DA-15 cable were connected, and the final implementation needed more wires than the original controller. So I cut off the connector and used a Volt-Ohm meter to identify the wires.

If you’re planning to keep the DA-15 intact, use Wikipedia to look up the “standard” wiring diagram for clues only, and then a Volt-Ohm meter to figure out the real wiring.

The buttons were easy to figure out; find the common ground wire and use an Ohm meter to identify the wire for each button. Label the wires or write down the wire color that’s connected to each button. We’ll need to know that when wiring up the Leonardo.

The steering potentiometer was also easy to figure out. Like any potentiometer that’s connected to the Arduino, the outer wires are +5V and ground, the center wire (wiper) connects to one of the Arduino analog pins. The “brakes” and “gas” were a bit trickier. Each pedal had its own potentiometer and the two were wired in series, which worked like a single rheostat. A neat implementation because this way, pressing both pedals at once would cancel each other out. That is, if you pressed the gas, held it down while also pressing the brakes, the car would slow down.

wingman_pots
The two pedals were wired to function as a single rheostat. A nifty design, but not compatible with the Arduino.

This configuration presented a problem for the Arduino however, which doesn’t measure resistance directly. The Arduino needs three wires to configure a potentiometer as a voltage divider.  The two outer wires go (one each) to +5V and ground, the center wire (wiper) connects to one of the analog pins. This way, as you turn the pot, it maps the range of voltages on the analog pin from 0 to +5V, to the values 0-1023. As long as you’re using a 3-wire potentiometer (and not a 2-wire rheostat) you’re fine.

My workaround was to separate the two pots and give each one its own input on the Arduino board. It was a simple matter to combine the two values in code, before sending a single Y-axis value to the PC.

This is what it all looked like on a breadboard

Breadboard Layout

Arduino Breadboard Layout (fritzing.org)

The software

The standard USB HID library for the Leonardo lets it emulate a keyboard and mouse. This is very cool because without installing special drivers, the computer will just see the Leonardo as a standard keyboard and mouse. I needed a joystick however which means modifying HID descriptors and other things I wasn’t eager to do. Luckily (and like many things Arduino) others have already been working on this stuff! I used the joystick-enabled USB HID libraries I found here: https://github.com/Cryocrat/LeonardoJoystick
Download two files (USBAPI.h and HID.cpp) and copy them to your Arduino IDE installation directory, in  \hardware\arduino\cores\arduino\. You will find there are already two files with the same names in that directory. The downloaded files will replace the ones you already have. If you like to be cautious back them up before you copy over the new files.

There is just one class you’ll be using, called JoyState_t. It has properties for each joystick axis and a bit-aligned “buttons” property. Finally it has a method called “setState()” that sends the HID structure to the computer. I hope you’ll find my code easy to follow. Feel free to leave a comment below if you have any questions about how it all works.

This code was developed and tested with an Arduino Leonardo and the Arduino IDE version 1.6.5

 

Conclusion

In this article we demonstrated how to connect an analog controller with potentiometers and buttons, to the Arduino Leonardo board. We also learned how to download and modify the USB HID library to support Joystick emulation and how to write a sketch to read and send controller data to a PC. The same concepts can be applied to a variety of controllers or sensors that can be connected to the Arduino and used to provide input to a computer.

References

Breadboard diagrams were created with Fritzing
(http://www.fritzing.org/)

The USB and HID files
https://github.com/Cryocrat/LeonardoJoystick

Arduino Leonardo official site
(http://arduino.cc/en/Main/ArduinoBoardLeonardo)

The latest Arduino IDE can be downloaded here
(https://www.arduino.cc/en/Main/Software)

Game port information on Wikipedia
(http://en.wikipedia.org/wiki/Game_port)

11 thoughts on “Modifying a Classic Game Controller with the Arduino Leonardo”

  1. Great article, thanks I have an old Saitek R100 I want to modify. Only problem is the ‘imaginaryindustries’ link no longer works, I am unable to get the HID libraries you speak of. Can you post them in an update or could you send them to me ?

  2. Nice work! I’m trying to make a steering wheel with force feedback using the Arduino Leonardo and a H bridge L298N with a DC motor and a incremental rotary encoder with 600 pulses/r, but no idea how to program … I am studying how to program on my own for some time but nothing came out.

  3. Hey Buddy,
    I’ve Been following your steps, and repeated them thrice and all the time faced just one issue,
    ” ‘JoyState_t’ does not name a type”.
    And because of that i am unable to proceed further please guide.

    “I copied your above program as it is”.

    1. Gregory,

      Joystate_t is declared in USBAPI.h — be sure to use the HID libraries I point you to in the article. The standard libraries don’t include Joystate_t. Good luck with your project!

      achod

  4. thanks for the time and effort made in presenting this article, however I have been ripping hair out last 3 days trying to get this to work, and am getting that same error as Gregory, despite having done the following:

    reinstalled IDE 3 times, reinstalled my OS once to make sure its not something else causing this, finally installing the version stated as tested working above,

    having found different versions of those files including the latest updated ones I tried those too…

    the path the copy the files stated above ( \hardware\arduino\cores\arduino\) doesn’t exist on my system on either the latest release or the 1.6.5 stated above, on mine its:
    \hardware\arduino\avr\cores\arduino, the latest release has the hid file stored in a separate folder – and yes I copied it there too…

    is it a windows 10 x64 issue maybe?

    thanks again for a most informative article, and hope to see some more from you soon

    …Pliers…

    1. Pliers, thanks for your post! If the compiler is not able to resolve JoyState_t then the problem is likely related to your environment setup. I’m going to recommend you post some details on the Arduino forums and get the community involved. This type of error is not uncommon, so I suspect you’ll have it resolved pretty quickly! -ag

  5. forgot to mention I really liked the clever way of handling those pot wipers and combining them – genius

Leave a Reply

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