Introduction: Using an Arduino Uno R3 As a Game Controller

Picture of Using an Arduino Uno R3 As a Game Controller

2016 UPDATE: I will no longer provide support in the comments section. This is an outdated and obsolete method that has very little reason to be used anymore. Only keeping it up for information purposes.

Naturally, the Uno does not natively support keyboard strokes, unlike it's Leonardo brother. Most of Google will tell you you need to do some firmware workarounds and ATMEGA reprogramming just to get it working and the entire ordeal is a mess. If you're like me, you've found that out just after buying one. Here is how to get around all that using a bit of Java coding. The ideal solution would be to purchase an arcade or flight sim joystick button board or an Arduino Leonardo for a few bucks from China (DX.com).

You will need:
Arduino Uno
Any sort of button or switch (or multiple switches, but start off simple)
10kΩ resistor(s) (one for each button)
Eclipse (the front end for Java programming - instructions later on for install)
Arduino Software (the front end for Arduino programming)
Breadboard
Jumper wires
Patience
Any video game that you may want physical switches for (racing/flight sim)

Step 1: Installing & Testing the Software

Picture of Installing & Testing the Software

First of, you will need Eclipse (http://www.eclipse.org/downloads/). For this tutorial, I will be using Eclipse Classic 4.2.2 - 64 Bit. If you're running into problems during the install, refer to either Google or the FAQ. Now that Eclipse is installed, you will also need the Arduino program (http://arduino.cc/en/main/software). I'll be using the stable 1.0.5 version.

You can skip this step if you know how to upload sketches to the Arduino. Now that you have all the software installed, we're going to go ahead and setup Arduino first. In the Arduino software, click tools and make sure the board and port number make sense according to Device Manager. To test the board, go File>Examples>Basic>Blink then hit the arrow in the top left that says upload. If all goes right, the SMD on pin 13 should be blinking. Refer to THIS if you're having difficulties.

Step 2: Wiring Up a Basic Switch & Programming

Picture of Wiring Up a Basic Switch & Programming

In this step, you will be hooking up a basic switch to pin 5. Why pin 5? I have no idea but here's the wiring diagram (Made using Fritzing). There's two diagrams, one for (1) 2 pin switch and one for (4) 4 pin switches.

Now that you have a switch hooked onto pin 5, we begin programming in Arduino. The code I used is attached and below:

// *********************************************
// this constant won't change:
const int  buttonPin = 5; // the pin that the pushbutton is attached to

// Variables will change:
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button

void setup() {
  // Initialize the button pin as an input:
  pinMode(buttonPin, INPUT);
  // Initialize serial communication:
  Serial.begin(9600);
}

void loop() {
  // Read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);
 
    if (buttonState == HIGH) {
      // If the current state is HIGH then the button
      // Send to serial that the engine has started:   
      Serial.println("Start Engine");
      delay (100);
  }
  // Save the current state as the last state,
  // for next time through the loop
  lastButtonState = buttonState;
}
// *********************************************

When uploaded to the Arduino, you can open up the serial monitor (Tools>Serial Monitor) and press the button. It should display "Start Engine" as long as you're pressing the button. You may fiddle with the delay later on to suit your liking but please note this may cause issues in-game. You are now sending a serial string through tactile feedback. This is great!


Step 3: Setting Up Eclipse and Installing Libraries

Picture of Setting Up Eclipse and Installing Libraries

Now we need to do something with that serial string. A program needs to pick it up and translate it into keystrokes. This is where Eclipse comes in and things get messy. Off the bat, you will need to download and install the RXTXcomm library (http://rxtx.qbang.org/wiki/index.php/Download) into Eclipse. I used the rxtx 2.1-7r2 (stable) version. Place RXTXcomm.jar into Eclipse's [Your workspace]/lib/jars folder as well as placing the rxtxParallel.dll and rxtxSerial.dll files into [Your workspace]/natives-win. This is where most people will go wrong if they do so feel free to ask if you have questions.

Now that you've installed the RXTXcomm library, we can begin coding in Eclipse. Open it up and go File>New>Project...>Java Project. For the sake of simplicity and ease, name this project SerialTest. While this may not be the proper method, there's no reason for it not to work this way. Click finish. Under the package explorer sidebar, right click on the SerialTest folder and go New>Class we will also name this SerialTest. Make sure public static void main(string[] args) is checked on. This is a Java program in its simplest, however, we still need to import those libraries from earlier. Right click on the same project folder and go Build Path>Configure Build Path. Under the Libraries tab, click Add External JARs and navigate to where you put the RXTXcomm.jar ([Your workspace]/lib/jars) and open it. Click OK. You are now ready to begin programming Java using the serial communicating library.

If you've made it past this part, congratulations. You didn't spend hours trying to figure it out like I did.

Step 4: Programming in Java to Interpret Incoming Serial & Testing

Picture of Programming in Java to Interpret Incoming Serial & Testing

Now, bare with me because I know nobody likes working with other people's code. It just sucks. You may have to modify the COM channel, parity, stop bits and baud (data) rate settings in the code (all of which can be determined through Device Manager but should be the same as the code). The code below works with Linux, Mac OS X and Windows. My Arduino is on the default COM1 at 9600b/s. I accidentally left some experimental code in there while making a GUI but they won't affect anything so try to ignore understanding some of the library imports. The SerialTest folder is also attached for reference.

// *********************************************

import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import java.util.Enumeration;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.JFrame;
import java.awt.Color;

public class SerialTest implements SerialPortEventListener {
SerialPort serialPort;
/** The port we're normally going to use. */
private static final String PORT_NAMES[] = { "/dev/tty.usbserial-A9007UX1", // Mac OS X
"/dev/ttyUSB0", // Linux
"COM1", // Windows
};
/**
* A BufferedReader which will be fed by a InputStreamReader converting the
* bytes into characters making the displayed results codepage independent
*/
private BufferedReader input;
/** The output stream to the port */
private OutputStream output;
/** Milliseconds to block while waiting for port open */
private static final int TIME_OUT = 2000;
/** Default bits per second for COM port. */
private static final int DATA_RATE = 9600;

public void initialize() {
CommPortIdentifier portId = null;
Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();

// First, Find an instance of serial port as set in PORT_NAMES.
while (portEnum.hasMoreElements()) {
CommPortIdentifier currPortId = (CommPortIdentifier) portEnum
.nextElement();
for (String portName : PORT_NAMES) {
if (currPortId.getName().equals(portName)) {
portId = currPortId;
break;
}
}
}
System.out.println("Port ID: ");
System.out.println(portId);
System.out.println("");
if (portId == null) {
System.out.println("Could not find COM port.");
return;
}

try {
// open serial port, and use class name for the appName.
serialPort = (SerialPort) portId.open(this.getClass().getName(),
TIME_OUT);

// set port parameters
serialPort.setSerialPortParams(DATA_RATE, SerialPort.DATABITS_8,
SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);

// open the streams
input = new BufferedReader(new InputStreamReader(
serialPort.getInputStream()));
output = serialPort.getOutputStream();

// add event listeners
serialPort.addEventListener(this);
serialPort.notifyOnDataAvailable(true);
} catch (Exception e) {
System.err.println(e.toString());
}
}

/**
* This should be called when you stop using the port. This will prevent
* port locking on platforms like Linux.
*/
public synchronized void close() {
if (serialPort != null) {
serialPort.removeEventListener();
serialPort.close();
}
}

/**
* Handle an event on the serial port. Read the data and print it.
*/
public synchronized void serialEvent(SerialPortEvent oEvent) {
if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
try {
String inputLine = input.readLine();
// ENGINE START
if (inputLine.equals("Start Engine")) {
System.out.println("Engine Start Engaged");
try {
Robot robot = new Robot();
robot.keyPress(KeyEvent.VK_S);
robot.delay(500);
robot.keyRelease(KeyEvent.VK_S);
} catch (AWTException e) {
e.printStackTrace();
}
}
// WINDSHIELD WIPERS
if (inputLine.equals("Windshield Wipers")) {
System.out.println("Windshield Wipers Engaged");
try {
Robot robot = new Robot();
robot.keyPress(KeyEvent.VK_W);
robot.delay(500);
robot.keyRelease(KeyEvent.VK_W);
} catch (AWTException e) {
e.printStackTrace();
}
}
// PIT SPEED LIMITER
if (inputLine.equals("Pit Speed Limiter")) {
System.out.println("Pit Limiter Engaged");
try {
Robot robot = new Robot();
robot.keyPress(KeyEvent.VK_P);
robot.delay(500);
robot.keyRelease(KeyEvent.VK_P);
} catch (AWTException e) {
e.printStackTrace();
}
}
// HEADLIGHTS
if (inputLine.equals("Headlights")) {
System.out.println("Headlights Engaged");
try {
Robot robot = new Robot();
robot.keyPress(KeyEvent.VK_H);
robot.delay(500);
robot.keyRelease(KeyEvent.VK_H);
} catch (AWTException e) {
e.printStackTrace();
}
}
} catch (Exception e) {
System.err.println(e.toString());
}
}
if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {

}
// Ignore all the other eventTypes, but you should consider the other
// ones.
}

public static void main(String[] args) throws Exception {
SerialTest main = new SerialTest();
main.initialize();
Thread t = new Thread() {
public void run() {
// the following line will keep this app alive for 1000 seconds,
// waiting for events to occur and responding to them (printing
// incoming messages to console).
try {
Thread.sleep(1000000);
} catch (InterruptedException ie) {
}
}
};
t.start();
System.out.println("- Started -");
System.out.println("");
}
}

// *********************************************

As you can see, there are 3 other buttons hooked up. Whenever Java sees the serial input of "Start Engine" , it will send out a keystroke of S. The output console will throw IO.Exceptions at you but don't fear, it's fixable by commenting out both System.err.println(e.toString()); lines. These errors do not interfere with anything so they're nothing to worry about to begin with. To change what each switch does, simply change the variables that are getting written to serial in Arduino and change the respective conditional statements in Java for when it receives those serial strings. To see the list of available commands for robot in Eclipse, type robot. and a little box will pop up showing various functions. Robot is amazing, you can even assign it to move the mouse based on Arduino input.

Step 5: Compile .jar

Picture of Compile .jar

You now need to compile and convert your exported .jar into something useful (like a .exe that opens to look like the command prompt). I used the free version of http://www.jar2exe.com/ but I'm sure there are much better alternatives out there. This process is pretty straight forward but varies on each program and will allow you to open the program outside of Eclipse/Java. If you're on a Mac, you could try http://sourceforge.net/projects/jarbundler/ . I can't confirm or deny this even works though.

While in Eclipse:

Right click your project and click "Refresh". Alternatively you can right click and hit F5 on your keyboard. This is to ensure all your code is up to date and won't conflict when trying to export.

Right click your project and click "Export".

Expand the "Java" folder and double click the "Runnable JAR file" option.

Configure the specifications of the JAR file. The first thing you should do is select the main class (class with the main method) from the drop down menu under "Launch configuration".Secondly, select the "Export destination" via the "Browse..." button or by manually typing the location. Lastly, ensure the "Extract required libraries into generated JAR" radio button is selected. Do not worry about the rest of the menu. Click "Finish" when you are satisfied with your selections.

While in Jar2Exe:

- You'll have a 30 day trial (which resets if you reinstall it). Ignore the first screen, click next

- Click browse JAR and locate your recently exported JAR file. the two options for JRE version can be ignored. Next.

- You want the 'Console Application' option selected. Next.

- It should automatically select the main class for you (so continue on) but if you're having issues, try selecting others.

- I'd only really suggest considering the system tray option on this page, the rest aren't needed to be on.

- Again, nothing to see on this page. Next.

- Browse to where you'd like to save the .exe program, everything else can be ignored for now as it's just secondary preference.

- After it finishes creating the .exe, you can browse to where you saved it and give it a test run.

Step 6: Assigning the Hotkeys In-game

Picture of Assigning the Hotkeys In-game

I built this setup to integrate into my steering wheel rig so that I can control multiple in-game features with physical toggle switches on a bank. Arduinos will allow you to display any sort of telemetry to an LCD screen or seven segment (providing the game allows you). I'm using Project CARS, a game still in its Pre-Alpha stage and is coming along very nicely. Simply map the keys that Java is pressing to functions in the game. For example, I have S for Start Engine and W for Wipers.

Step 7: What Now?

Picture of What Now?

Well, you can start hooking up more switches to different ports. I'll be getting the handbrake from a car and installing a button that's activated when the lever is pulled. You could swap out the pushbutton start for a real car ignition and key. Heck, you could even have a seatbelt that won't allow you to drive until you've buckled in. Backup all code before experimenting and have fun!

Comments

VinothB1 (author)2016-05-31

Also i wanted to ask do you make the lib folder and the native-wins folder ourselves in the workspace folder or are there already supposed to be somehwere because i couldnt find them in my workspace

VinothB1 (author)2016-05-31

java.lang.UnsatisfiedLinkError: no rxtxSerial in java.library.path thrown while loading gnu.io.RXTXCommDriver

Exception in thread "main" java.lang.UnsatisfiedLinkError: no rxtxSerial in java.library.path

at java.lang.ClassLoader.loadLibrary(Unknown Source)

at java.lang.Runtime.loadLibrary0(Unknown Source)

at java.lang.System.loadLibrary(Unknown Source)

at gnu.io.CommPortIdentifier.<clinit>(CommPortIdentifier.java:83)

at SerialTest.initialize(SerialTest.java:36)

at SerialTest.main(SerialTest.java:123)

Why do i get this error in eclipse when i compile your code ?

please help!!

HåvardH1 (author)2016-03-31

why not give us your .exe so we don't need to progam it by our self

Mine's no longer setup like this and when I made it 3 years ago, it was heavily modified and tailored to my setup. It wouldn't have worked on anyone elses. It doesn't even open on my PC anymore.


Besides, this website isn't for free hand-outs, it's to learn how to do things yourself.

Here they are anyways: https://mega.nz/#F!60UHUTiZ!V35saAPgv-HucqbqE--uhw

I haven't touched those in several years and have no idea what they'll do, just a warning.

SidharthR (author)2016-03-10

I did everything according to the steps and completed the setup. When I open notepad and press the switch, the letter s is printed. But when i open a game and then press the button, i get no response. What do I do?

Damion67 (author)SidharthR2016-03-10

It might be because the keysend is so fast that the game's internal debounce (typically 50ms to prevent multiple sends from a sensitive switch) is ignoring the key presses all together. Try playing around with the delays between a down press and an up press of the key.

I seem to recall this happening to me in Project CARS but not Assetto Corsa. It's been far too long since I used this setup to remember if this was the correct solution.

AndreiN8 (author)2016-02-04

java.lang.UnsatisfiedLinkError: no rxtxSerial in java.library.path thrown while loading gnu.io.RXTXCommDriver
Exception in thread "main" java.lang.UnsatisfiedLinkError: no rxtxSerial in java.library.path
at java.lang.ClassLoader.loadLibrary(Unknown Source)
at java.lang.Runtime.loadLibrary0(Unknown Source)
at java.lang.System.loadLibrary(Unknown Source)
at gnu.io.CommPortIdentifier.<clinit>(CommPortIdentifier.java:123)
at SerialTest.initialize(SerialTest.java:42)
at SerialTest.main(SerialTest.java:164)

please help!!

VedoDZ (author)2015-04-23

Can you read analog signals and use them with this library ??

For example, to use a steering wheel attached to a potentiometer??

KevinD60 (author)VedoDZ2015-10-12

Did you figure it out? I am also looking for this

tridecagon (author)2015-09-25

Change buttonState and lastButtonState to the bool type. If my understanding is correct, it will take up less RAM space.

bool buttonState;

bool lastButtonState;

luisfranco1990 (author)2015-09-03

Hi i´m try to make a game controler for a online pc game but i need use two joysticks could you help me?? i don't know anything in eclipse even install it.. can u help me on visual basic??

k.c101 (author)2015-06-25

first off thanks for a great diy. second off if any one is having problems after making the jar file into an exe and the console close to fast to read just take the rxtxParallel.dll, rxtxSerial.dll and take the exe and put them all into a fold and it so work. that is what i did

KevinR8 (author)2015-02-02

I can't find the directory /lib/jars, or the /natives-win directory

VedoDZ (author)KevinR82015-04-23

Kevin:
This video explains how to add the archives to your class project

https://www.youtube.com/watch?v=43Vdpz1YmdU

TheGorramB (author)2014-12-17

I am hoping you'll be willing to help me again, but I'm hardly sure what to ask. I followed what you gave me, and indeed all of the errors are gone. The code seems to run without problems. But, I'm not seeing any response from my button presses. My button presses are bringing up the expected text in the serial monitor window on my PC. I have confirmed that I am using the same string ("GO" and "JUMP") in both the Arduino code and the Java code. I see the Eclipse console showing a Port ID and the string "- Started -". But, nothing else happens. I see no "Engine Start Engaged" string in the console from my button, and no virtual keyboard presses seem to happen.

When I put in a println at the beginning of the method "public synchronized void serialEvent(SerialPortEvent oEvent)" as a test, it did not show up either, seeming to suggest that the whole method is being skipped altogether.

Can you suggest what my problem might be here?

Sincere thanks.

mathijsw (author)2014-11-29

i got an parsing error and i dont know what to do, it says it needs the rxtx file or something like it. i hope you can help me.

java.lang.UnsatisfiedLinkError: no rxtxSerial in java.library.path thrown while loading gnu.io.RXTXCommDriver

Exception in thread "main" java.lang.UnsatisfiedLinkError: no rxtxSerial in java.library.path

at java.lang.ClassLoader.loadLibrary(Unknown Source)

at java.lang.Runtime.loadLibrary0(Unknown Source)

at java.lang.System.loadLibrary(Unknown Source)

at gnu.io.CommPortIdentifier.<clinit>(CommPortIdentifier.java:83)

at SerialTest.initialize(SerialTest.java:41)

at SerialTest.main(SerialTest.java:163)

Damion67 (author)mathijsw2014-11-29

That's typically the error you'll get if the RXTX.dll library was either never installed or placed in the wrong libraries folder.

mathijsw (author)Damion672014-11-29

thanks for the responds ill look into that :)

i had it like this but it dousnt work

Did you manage to solve this? I've got the same problem.

TheGorramB (author)2014-12-09

I would very much appreciate some help. I pasted your code into Eclipse as-is and I got many, many errors (34 of them, in fact) right out of the gate.

Is it a version problem? I am working with JavaSE v1.8.

Or perhaps, is there any organization of the code (pasting it NOT as-is) that I should be doing, and about which I would know if I were not just a very, very early beginner with Java programming?

Thank you.

They're all errors with permissions to access the rxtx library, so I'd start by trying things like this: http://ubuntuforums.org/showthread.php?t=1756641

You could always try reinstalling using the rxtx wiki as a guide. http://rxtx.qbang.org/wiki/index.php/Main_Page

but as far as I know, nothing is wrong with the rest of the code, just the rxtx.dll library.

Thank you very much! I shall have to try this.

Pixelpacific (author)2014-11-04

I have an error with the .exe

The windows show me this :

Stable Library

================================

Native lib Version = RXTX-2.1-7

Java lib Version = RXTX-2.1-7

Port ID:

gnu.io.CommportIdentifier@e53b93

- Started -

After that i have nothing and they won't show S on screen :(

Help me please :(

(Sorry for my English i'm french :/)

hmm, there's a lot that could be going wrong. It's been awhile since I touched this stuff but double check that the serial monitor on Arduino is sending out what is required and that the code in Java is written properly to accept what's being sent through serial. It should be something like Arduino sends out a word, Java has to watch for incoming serial and if it sees that word, press the S key. It's honestly really hard to pinpoint where it could be going wrong. Try to follow everything logically from the very beginning. It could be something as simple as a capital letter where there shouldn't be.

Good luck.

Thanks it works but with a little problem , when i run/compile in Eclispe it work's very fine with the key S etc.. but when i want to export in a runnable .jar or .jar file i don't know how to convert in .exe , i have errors all the time in .exe


In what format do you "export" your ecplise java files and with Jar2Exe , what do you do ?

Thanks a lot ! :D

I've updated step 5 to hopefully answer your question.

F-16C (author)2014-09-30

Just wondering what to do after step 4? I have saved the project in eclipse and get a project folder with a .class file and a .java file. To make the computer start interoperating the button presses do you need to compile and run the class file or do you need to build this script into the game? If so how (running Mac OSX)? Currently the button presses have no effect except registering on the serial monitor (arduino IDE). Thanks

The Angry Clown (author)F-16C2014-10-01

You're absolutely correct, sorry about that. I've added a new step 5. You merely want to convert that .jar into something you can run (like windows would have an .exe). If that's what Mac users call apps, then you could try http://sourceforge.net/projects/jarbundler/

Good luck.

JakeR2 (author)2014-09-03

could i use this to simulate a mouse click? how?

Damion67 (author)JakeR22014-09-03

Yup! You could use the code:


public static void click(int x,int y) throws AWTException {
Robot bot =new Robot();
bot.mouseMove(x, y);
bot.mousePress(InputEvent.BUTTON1_MASK);
bot.mouseRelease(InputEvent.BUTTON1_MASK);
}

Where the last two lines are a left click press and release. It would go in place of keystroke presses.

Batman_ (author)2014-06-21

Thanks for this tutorial, I am yet to try it but I have been looking for something somewhat straight forward to send keypresses from Arduino to PC that doesn't require another board or flashing the firmware!

Batman_ (author)Batman_2014-06-22

Got it to work!

danagaram (author)2013-11-30

how can i link arduino with steering wheel and brake plz ??

About This Instructable

128,164views

88favorites

License:

More by The Angry Clown:Using an Arduino Uno R3 as a Game Controller
Add instructable to: