Maker.io main logo

Make an NES Controller Interface for Arduino UNO

174

2017-02-17 | By All About Circuits

License: See Original Project Arduino

Courtesy of All About Circuits

The Nintendo Entertainment System, AKA the NES, was once the pinnacle of video game systems.

NES controller

It's time to take over the world.

These days, the NES is pretty antiquated and only the most nostalgic of gamers will fire one up. The NES has become a dust collector for many of us who have moved on to modern systems. But, if you like to tinker with electronics, the NES can become powerful in the DIY world with its easily hackable controller. The NES controller can control a multitude of items from lights to robots and even a doomsday device if you really want – the possibilities go as far as your imagination! To demonstrate how simple it really is to interface the controller to an Arduino, we will use no more than an Arduino UNO and an NES controller.

BOM

Hardware:

  • Arduino Uno
  • Breadboard wire jumpers
  • NES controller (original or otherwise)
  • Breadboard (optional)
  • NES extension cord (optional)

Software:

  • Arduino IDE

Theory

When I opened up an NES controller, I found it to have an elegant simplicity to it. It consists of no more than a few pull-up resistors and a shift register. Specifically, it is a Parallel-In-Serial-Out, PISO for short. The 4021 shift register is used inside these controllers. Although it isn’t necessary for understanding the Arduino code in this project, here is a datasheet of a typical version for curious readers.

And for readers who are even more curious, you can read about the theory and operation of shift registers here.

You can see how the 4021 is implemented below. This is a schematic of the NES controller’s insides.

 

 

The 4021 is an 8-bit register, as you can see in the schematic, which is just enough to connect all eight of the NES controller’s buttons to it. One side of each button is tied to ground. The other end of each switch goes to a separate data input on the shift register. A pull-up resistor is used to keep a defined state on the register’s data inputs. This means that when a button isn’t pressed, the shift register will interpret a logical “1”. If a button is pressed, the shift register will interpret a “0”. This is important because this is where the user’s input will first appear.

The shift register will need signal stating in order to “grab” the inputs from the data lines. This is the LATCH line’s purpose. The button states sitting on the shift register’s pin will be latched into the register by transitioning the LATCH line LOW-HIGH-LOW. The duration on the HIGH can be quite short, the NES protocol will do this for about 12 microseconds. The first button state will become available on the DATA line once the data is latched. The DATA line will scroll through the button states every time that the CLOCK pin is pulsed in the same manner that the LATCH line was. This means that clocking seven times will exhaust all the data for the buttons’ states after latching the data. The Arduino code describes the exact order of buttons that will be sent serially.

The Arduino code must:

  • Pulse the LATCH line so the shift register can "grab" the data.
  • Read the DATA pin an dsave the button state.
  • Pulse the CLOCK line save teh button state.
  • Repeat step 3 six more times.
  • Do something cool with all of this data!

To get a better idea of the NES controller’s construction, you can always open it up. This is completely optional, it will give you a better idea of how you would build your own product and a better breadth of experience in terms of  electronic assembly and product design. To begin disassembling, you will need to use a small Phillips head screwdriver. There are six screws on the controller’s backside.

Back of the NES controller

Be careful when unscrewing them. These screws are on the cheaper side, so they can strip easily. Be sure to use a screwdriver that lets you apply ample pressure when turning the screw. Once the screws are out, lay the controller on its face with the buttons facing down. Then, lift the backside off. 

Back of NES Controller - opened

Now, you will expose one side of the PCB. This particular PCB is made of Phenolic. Phenolic is common in consumer electronics because it is cheap to produce. As a side note, most quality PCBs are made from glass epoxy resin. The 4021 IC is in plain sight along with the wires connecting to the cable. You have to admire how Nintendo engineers made this so simple while still being very functional, all by one IC. You can see the other side of the PCB by gently lifting the board out. Depending on how dirty the controller has become, you may have the rubber pads stuck to it (some are very dirty!). If your controller is dirty, there is no harm in pulling the pads off so you can inspect the PCB. If you look closely at the traces, you can see that they were hand drawn. That must have been a pretty laborious process since CAD has taken over this industry. It may look like there are no resistors on this side, but to the skilled eye, they’re in plain sight. They are actually the black rectangles with the shiny green surface on them. These carbon resistors were directly printed on the PCB to save time and money. It was probably the ideal solution at the time since they are all the same value. The last things to remove from the controller are the rubber button pads and the  button contacts. These work by having the black, circular part of the rubber pad bridge a connection across the carbon deposited button contacts. Although it does not appear conductive, it is enough to register a logic high or low for the button states.

NES controller

Wire It Up

The wiring for this project is fairly simple because the NES controller’s connector end makes it easy to probe breadboard wires into. If you would like something more secure, you can buy an NES controller extension cable and cut the male end off so you can easily grab the wires from there. That way, you can insert that directly into the Arduino and have a secure connection and the controller’s end.

Below shows how to wire this gem up.

Wiring Diagram

The controller only needs to be interfaced to 5 of its 7 pins. 5V and GND are for power, D2 is for latching the button states, D3 is for clocking the button states, and D4 is for reading the serial data of button states.

Arduino Code

This code is heavily commented in order to describe the flow of state and order of operation. After all of this, you should see the button states pop up in the serial monitor. The code within the serial monitor should give you a more clear idea on how you might interface this to your next doomsday device harmless electronics project. The Arduino code here should also coincide well with the Theory section and it should be a thorough step-by-step process. If you need more explanation, please comment below. The All About Circuits Forum is also a very good place to ask questions in case you would like a faster response.

Copy Code
/*
================================================================================

File........... NES Controller Test Code
Purpose........ To demonstrate how to interface to an NES controller
Author......... Joseph Corleto
E-mail......... corleto.joseph @ gmail.com
Started........ 04/13/2016
Finished....... 04/14/2016
Updated........ --/--/----

================================================================================
Notes
================================================================================
- The NES controller contains one 8-bit 4021 shift register inside.

- This register takes parallel inputs and converts them into a serial output.

- This code first latches the data and then shifts in the first bit on the data line.
Then it clocks and shifts in on the data line until all bits are received.

- What is debugged are the button states of the NES controller.

- A logical "1" means the button is not pressed. A logical "0" means the button is
pressed.

- This code shifts the first bit of data into the LSB.

- The order of shifting for the buttons is shown in the table below:

Bit# | Button
--------------
0 | A
--------------
1 | B
--------------
2 | Select
--------------
3 | Start
--------------
4 | Up
--------------
5 | Down
--------------
6 | Left
--------------
7 | Right
--------------

- The NES controller pinout is shown below (looking into controllers
connector end):
__________
/ |
/ O 1 | 1 - Ground
| | 2 - Clock
| 7 O O 2 | 3 - Latch
| | 4 - Data Out
| 6 O O 3 | 5 - No Connection
| | 6 - No Connection
| 5 O O 4 | 7 - 5V
|___________|

- Please visit http://www.allaboutcircuits.com to search for complete article!

================================================================================
Updates
================================================================================
*/

//===============================================================================
// Header Files
//===============================================================================

//===============================================================================
// Constants
//===============================================================================
// Here we have a bunch of constants that will become clearer when we look at the
// readNesController() function. Basically, we will use these contents to clear
// a bit. These are chosen according to the table above.
const int A_BUTTON = 0;
const int B_BUTTON = 1;
const int SELECT_BUTTON = 2;
const int START_BUTTON = 3;
const int UP_BUTTON = 4;
const int DOWN_BUTTON = 5;
const int LEFT_BUTTON = 6;
const int RIGHT_BUTTON = 7;

//===============================================================================
// Variables
//===============================================================================
byte nesRegister = 0; // We will use this to hold current button states

//===============================================================================
// Pin Declarations
//===============================================================================
//Inputs:
int nesData = 4; // The data pin for the NES controller

//Outputs:
int nesClock = 2; // The clock pin for the NES controller
int nesLatch = 3; // The latch pin for the NES controller

//===============================================================================
// Initialization
//===============================================================================
void setup()
{
// Initialize serial port speed for the serial terminal
Serial.begin(9600);

// Set appropriate pins to inputs
pinMode(nesData, INPUT);

// Set appropriate pins to outputs
pinMode(nesClock, OUTPUT);
pinMode(nesLatch, OUTPUT);

// Set initial states
digitalWrite(nesClock, LOW);
digitalWrite(nesLatch, LOW);
}

//===============================================================================
// Main
//===============================================================================
void loop()
{
// This function call will return the states of all NES controller's register
// in a nice 8 bit variable format. Remember to refer to the table and
// constants above for which button maps where!
nesRegister = readNesController();

// Slight delay before we debug what was pressed so we don't spam the
// serial monitor.
delay(180);

// To give you an idea on how to use this data to control things for your
// next project, look through the serial terminal code below. Basically,
// just choose a bit to look at and decide what to do whether HIGH (not pushed)
// or LOW (pushed). What is nice about this test code is that we mapped all
// of the bits to the actual button name so no useless memorizing!
if (bitRead(nesRegister, A_BUTTON) == 0)
Serial.println("JUMP!");

if (bitRead(nesRegister, B_BUTTON) == 0)
Serial.println("PUNCH!");

if (bitRead(nesRegister, START_BUTTON) == 0)
Serial.println("DOOMSDAY ACTIVATED");

if (bitRead(nesRegister, SELECT_BUTTON) == 0)
Serial.println("WHY DON'T YOU MAP SOMETHING HERE?");

if (bitRead(nesRegister, UP_BUTTON) == 0)
Serial.println("...OR HERE?");

if (bitRead(nesRegister, DOWN_BUTTON) == 0)
Serial.println("PLAY WITH THE CODE ALREADY!");

if (bitRead(nesRegister, LEFT_BUTTON) == 0)
Serial.println("MAKE SOMETHING WITH THIS!");

if (bitRead(nesRegister, RIGHT_BUTTON) == 0)
Serial.println("GOOD LUCK WITH YOUR PROJECT ");
}

//===============================================================================
// Functions
//===============================================================================
///////////////////////
// readNesController //
///////////////////////
byte readNesController()
{
// Pre-load a variable with all 1's which assumes all buttons are not
// pressed. But while we cycle through the bits, if we detect a LOW, which is
// a 0, we clear that bit. In the end, we find all the buttons states at once.
int tempData = 255;

// Quickly pulse the nesLatch pin so that the register grab what it see on
// its parallel data pins.
digitalWrite(nesLatch, HIGH);
digitalWrite(nesLatch, LOW);

// Upon latching, the first bit is available to look at, which is the state
// of the A button. We see if it is low, and if it is, we clear out variable's
// first bit to indicate this is so.
if (digitalRead(nesData) == LOW)
bitClear(tempData, A_BUTTON);

// Clock the next bit which is the B button and determine its state just like
// we did above.
digitalWrite(nesClock, HIGH);
digitalWrite(nesClock, LOW);
if (digitalRead(nesData) == LOW)
bitClear(tempData, B_BUTTON);

// Now do this for the rest of them!

// Select button
digitalWrite(nesClock, HIGH);
digitalWrite(nesClock, LOW);
if (digitalRead(nesData) == LOW)
bitClear(tempData, SELECT_BUTTON);

// Start button
digitalWrite(nesClock, HIGH);
digitalWrite(nesClock, LOW);
if (digitalRead(nesData) == LOW)
bitClear(tempData, START_BUTTON);

// Up button
digitalWrite(nesClock, HIGH);
digitalWrite(nesClock, LOW);
if (digitalRead(nesData) == LOW)
bitClear(tempData, UP_BUTTON);

// Down button
digitalWrite(nesClock, HIGH);
digitalWrite(nesClock, LOW);
if (digitalRead(nesData) == LOW)
bitClear(tempData, DOWN_BUTTON);

// Left button
digitalWrite(nesClock, HIGH);
digitalWrite(nesClock, LOW);
if (digitalRead(nesData) == LOW)
bitClear(tempData, LEFT_BUTTON);

// Right button
digitalWrite(nesClock, HIGH);
digitalWrite(nesClock, LOW);
if (digitalRead(nesData) == LOW)
bitClear(tempData, RIGHT_BUTTON);

// After all of this, we now have our variable all bundled up
// with all of the NES button states.*/
return tempData;
}

Future Thoughts

If you enjoyed this project and learned how to interface to the NES controller well, the SNES controller is pretty much the same idea, but with a bigger shift register (16 bits instead of 8 bits).

Happy hacking!

Mfr Part # A000066
ARDUINO UNO R3 ATMEGA328P BOARD
Arduino
$ 27,60
View More Details
Add all DigiKey Parts to Cart
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.