Nintendo Controller mapping to NES Emulator

NOTE: Still a work in progress. I am hoping to finish it in the next couple of days. Thanks! :wink:

Hello this is my first week with my Arduino Uno and I thought I would take some time to write up a quick tutorial on my first project. My hope is that I will save someone time and energy hunting down all of the specs and websites I had to do in order to get this project up and running. The main portion of this project was actually figuring out how to actually control a NES emulator with the controller once we were done wiring. Most of the tutorials I found online were only ‚ÄúHow to control leds with your NES controller?‚ÄĚ or the like, but this isn‚Äôt what I wanted. What I wanted to do was to actually be able to control a NES Emulator on my computer.

So at a very high level this is what we are going to do. First we will wire up the Arduino to an old NES Controller. Then we will write an Arduino Sketch that will output the keypress and keyrelease events from the NES Controller. Lastly we will capture these serial output events in a Java class and then use the Java Robot class to convert those Serial Outputs to actual keystrokes, which we can then map to any NES Emulator.

Step 1 Parts List:

  1. Arduino Uno (Or equivalent)
  2. 22 gauge wire
  3. NES Controller (~$10)
  4. A USB cable type B (~$8)
  5. Project casing (optional ~$6)
  6. NES Port (optional ~$8.99 for two)
  7. Java Knowledge (Not optional…jk)

Step 2 Wiring:

This is the general pin layout for the NES controller, along with the Fritzing wiring diagram. Should be pretty easy to do, because the wire can just be pushed into the NES controller for prototyping. Make sure they get pushed in all of the way though.

Step 3: Coding the Arduino Sketch

Instead of rewriting all of the events by hand I used a current NES Library (NESpad) in my sketch to capture the events. Here is the link to that project http://code.google.com/p/nespad/. So head over there and download the most current version of the library. At the time of writing this is version 1.3. Copy the zip file to ArduinoHome/libraries and unpack it. You might want to also copy the contents of the examples folder to ArduinoHome/examples. Now start the Arduino IDE up and Click Sketch ‚Üí Import Library ‚Üí NESpad. If NESPad is not on this list then you installed the library incorrectly. Please head over to http://www.arduino.cc/en/Reference/Libraries for better instructions on installing libraries.

Now that we have installed the NESpad library correctly we can test out our connections to our controller. Cut and paste the code below, and then burn it to the Arduino. After the code is successfully uploaded you should be able to push the A button on your controller and see the LED from PIN 13 light up. Pretty cool huh? We can do better.

#include <NESpad.h>

NESpad nintendo = NESpad(2,3,4);

byte state = 0;

void setup() {
  pinMode(13, OUTPUT);    
}

void loop() {
  
  state = nintendo.buttons();
 
  digitalWrite(13, state & NES_A);

  delay(1);
}

Now that you see your controller is working cut and paste the code below and upload it to the Arduino board. You can open the Serial Monitor and start pressing buttons. You will see a bunch of numbers and letters appearing as buttons are being pressed and released. These are the outputs we are going to capture in the Java Robot class we will be writing in a minute. If you are curious to see how this code works take a moment to peak through it. I have commented everything, so hopefully it is clear. I have also taken steps to ensure that it is as logical to follow as possible, and I am also well aware there are quite a few areas of the code where optimizations could be made.

#include <NESpad.h>

// put your own strobe/clock/data pin numbers here -- see the pinout in readme.txt
NESpad nintendo = NESpad(2,3,4);

byte state = 0;

/* A button gets marked as true as soon as it is pressed. That way
 we know to not "press" it again */
boolean a = false; //A      Button
boolean b = false; //B      Button
boolean u = false; //Up     Button
boolean d = false; //Down   Button
boolean l = false; //Left   Button
boolean r = false; //Right  Button
boolean s = false; //Start  Button
boolean e = false; //Select Button

/* We will pass on this array whenever a key is released. Once the 
 key is released we will turn that 0 into a 1. That way in our java program
 we will know exactly which keys we just released. The keys will always go
 in this order:        a,b,u,d, l,r,s,e */
int keysReleased[] = {
  0,0,0,0, 0,0,0,0};

/* We will set this to true only when a button has been released. This will
 stop us from sending the keysReleased array every loop to our java robot */
boolean isReleased = false;

void setup() {
  Serial.begin(9600);    
}

void loop() {

  delay(5);

  state = nintendo.buttons();

  // A
  if (state & NES_A){
    if(!a){
      a = true; //Make sure the button is only pressed once
      Serial.println('A'); //Print the button to be picked up by our robot
    }
  }
  //Key might have been released so we check and if so change the
  //value in our released array
  else if (a == true){
    a = false;
    keysReleased[0] = 1;
    isReleased = true;
  }

  // B
  if (state & NES_B){
    if(!b){
      b = true; //Make sure the button is only pressed once
      Serial.println('B'); //Print the button to be picked up by our robot
    }
  }
  //Key might have been released so we check and if so change the
  //value in our released array
  else if (b == true){
    b = false;
    keysReleased[1] = 1;
    isReleased = true;
  }

  // Up
  if (state & NES_UP){
    if(!u){
      u = true; //Make sure the button is only pressed once
      Serial.println('U'); //Print the button to be picked up by our robot
    }
  }
  //Key might have been released so we check and if so change the
  //value in our released array
  else if (u == true){
    u = false;
    keysReleased[2] = 1;
    isReleased = true;
  }

  // Down
  if (state & NES_DOWN){
    if(!d){
      d = true; //Make sure the button is only pressed once
      Serial.println('D'); //Print the button to be picked up by our robot
    }
  }
  //Key might have been released so we check and if so change the
  //value in our released array
  else if (d == true){
    d = false;
    keysReleased[3] = 1;
    isReleased = true;
  }

  // Left
  if (state & NES_LEFT){
    if(!l){
      l = true; //Make sure the button is only pressed once
      Serial.println('L'); //Print the button to be picked up by our robot
    }
  }
  //Key might have been released so we check and if so change the
  //value in our released array
  else if (l == true){
    l = false;
    keysReleased[4] = 1;
    isReleased = true;
  }

  //Right
  if (state & NES_RIGHT){
    if(!r){
      r = true; //Make sure the button is only pressed once
      Serial.println('R'); //Print the button to be picked up by our robot
    }
  }
  //Key might have been released so we check and if so change the
  //value in our released array
  else if (r == true){
    r = false;
    keysReleased[5] = 1;
    isReleased = true;
  }

  //Start
  if (state & NES_START){
    if(!s){
      s = true; //Make sure the button is only pressed once
      Serial.println('S'); //Print the button to be picked up by our robot
    }
  }
  //Key might have been released so we check and if so change the
  //value in our released array
  else if (s == true){
    s = false;
    keysReleased[6] = 1;
    isReleased = true;
  }

  //Select
  if (state & NES_SELECT){
    if(!e){
      e = true; //Make sure the button is only pressed once
      Serial.println('E'); //Print the button to be picked up by our robot
    }
  }
  //Key might have been released so we check and if so change the
  //value in our released array
  else if (e == true){
    e = false;
    keysReleased[7] = 1;
    isReleased = true;
  }

  /* If a key has been released then our java robot needs to know about it. So what we 
   are going to do is to iterate over our array if a key has been released and print out the
   position in the array of that key. So for example if "Up" has been released we will
   see that our array looks like this [0,0,1,0, 0,0,0,0]. So then we will print 2 to the java robot 
   so it knows that "Up" has been released. Likewise we would print 7 for the start button on release. */
  if(isReleased){
    isReleased = false; //Reset the boolean
    for(int i=0; i < 8; i++){
      if(keysReleased[i] == 1){
        keysReleased[i] = 0; //Reset the button listener
        Serial.println(i);
      }
    }
  }
}

Sorry I had to break this down into two threads because of the text limit

Step 4 Coding Java

Now here is where things start to really cook. If you have taken some time to play with the Arduino sketch you will probably understand what events we are looking to capture in this Java class, but if you haven't here is a quick cheat sheet.

Arduino Events

'A' = A button is pressed 0 = A button is released 'B' = B button is pressed 1 = B button is released 'U' = Up button is pressed 2 = Up button is released 'D' = Down button is pressed 3 = Down button is released 'L' = Left button is pressed 4 = Left button is released 'R' = Right button is pressed 5 = Right button is released 'S' = Start button is pressed 6 = Start button is released 'E' = Select button is pressed 7 = Select button is released

Now if you have never captured Arduino Serial output in java before head over and check out this page http://www.arduino.cc/playground/Interfacing/Java. Now cut and paste the Java code below into your favorite IDE. Start up the program, and your controller should automatically be recognized. Start pressing buttons and you will see the same output in the Console as you did when you ran the pure Arduino Sketch except that there might be multiple commands sent in each iteration of our Arduino program. That is why we used String.contains() instead of String .equals() in our if statements. You should now be able to open Notepad and start pressing buttons on your Controller to see letters being outputted.

package driver;

import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.KeyEvent;
import java.io.InputStream;
import java.io.OutputStream;
import gnu.io.CommPortIdentifier; 
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent; 
import gnu.io.SerialPortEventListener; 
import java.util.Enumeration;

public class Driver 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
                  "COM3", // Windows
                  };
      
      private Robot robot;
      
      /** Buffered input stream from the port */
      private InputStream 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;
      
      /*
       * These booleans will ensure that our buttons are only pressed down once. Just like
       * in our Arduino sketch false = not pressed.
       */
      public Boolean a             = false;
      public Boolean b             = false;
      public Boolean up             = false;
      public Boolean down       = false;
      public Boolean left       = false;
      public Boolean right       = false;
      public Boolean start       = false;
      public Boolean select       = false;

      public void initialize() throws AWTException {
            
            CommPortIdentifier portId = null;
            Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();
            robot = new Robot();

            // iterate through, looking for the port
            while (portEnum.hasMoreElements()) {
                  CommPortIdentifier currPortId = (CommPortIdentifier) portEnum.nextElement();
                  for (String portName : PORT_NAMES) {
                        if (currPortId.getName().equals(portName)) {
                              portId = currPortId;
                              break;
                        }
                  }
            }

            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 = 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 {
                        int available = input.available();
                        byte chunk[] = new byte[available];
                        input.read(chunk, 0, available);
                        
                        sendCommand(new String(chunk));
                        
                  } catch (Exception e) {
                        System.err.println(e.toString());
                  }
            }
      }
      
      public void sendCommand(String command){
            if(command.equals(""))
                  return;
            if(command == null)
                  return;
            
            System.out.println(command);
            
            //Switches for the A Button
            if(command.contains("A")){
                  if(!a){
                        robot.keyPress(KeyEvent.VK_A);
                        a = true;
                  }
            }
            if(command.contains("0")){
                  a = false;
                  robot.keyRelease(KeyEvent.VK_A);
            }
            
            //Switches for the B Button
            if(command.contains("B")){
                  if(!b){
                        robot.keyPress(KeyEvent.VK_B);
                        b = true;
                  }
            }
            if(command.contains("1")){
                  b = false;
                  robot.keyRelease(KeyEvent.VK_B);
            }
            
            // Switches for the UP button
            if(command.contains("U")){
                  if(!up){
                        up = true;
                        robot.keyPress(KeyEvent.VK_UP);
                  }
            }
            if(command.contains("2")){
                  up = false;
                  robot.keyRelease(KeyEvent.VK_UP);
            }
            
            //Switches for the down button
            if(command.contains("D")){
                  if(!down){
                        robot.keyPress(KeyEvent.VK_DOWN);
                        down = true;
                  }
            }
            if(command.contains("3")){
                  down = false;
                  robot.keyRelease(KeyEvent.VK_DOWN);
            }
            
            //Switches for the Left button
            if(command.contains("L")){
                  if(!left){
                        robot.keyPress(KeyEvent.VK_LEFT);
                        left = true;
                  }
            }
            if(command.contains("4")){
                  left = false;
                  robot.keyRelease(KeyEvent.VK_LEFT);
            }
            
            //Switches for the right button
            if(command.contains("R")){
                  if(!right){
                        robot.keyPress(KeyEvent.VK_RIGHT);
                        right = true;
                  }
            }
            if(command.contains("5")){
                  right = false;
                  robot.keyRelease(KeyEvent.VK_RIGHT);
            }
            
            //Switches for the Start button
            if(command.contains("S")){
                  if(!start){
                        robot.keyPress(KeyEvent.VK_ENTER);
                        start = true;
                  }
            }
            if(command.contains("6")){
                  start = false;
                  robot.keyRelease(KeyEvent.VK_ENTER);
            }
            
            //Switches for the select button
            if(command.contains("E")){
                  if(!select){
                        robot.keyPress(KeyEvent.VK_S);
                        select = true;
                  }
            }
            if(command.contains("7")){
                  select = false;
                  robot.keyRelease(KeyEvent.VK_S);
            }      
      }

      public static void main(String[] args) throws Exception {
            Driver main = new Driver();
            main.initialize();
            System.out.println("Started");
      }
}

Now finally to test in a real emulator! I am using Nestopia with a Super Mario Bros Rom. First note that when you start up Nestopia head over to Options -> Input to make sure all of our keystrokes are mapped correctly. Once they are start up the Rom of your choice and enjoy! Thanks and I hope to update this tutorial with the NES Port soldering once it comes in the mail. Hope you all enjoyed this.