Use Your NES Controller As a Smart TV Remote

Turn your classic Nintendo controller into a Smart TV remote with the help of an Arduino Uno.

This is a quick and simple project suitable for beginners.

I will also show you how to fix the Panasonic bug in the Arduino IRremote library.

daniscubla.malignani's Instructable and randofo's Instructable were used for inspiration

Teacher Notes

Teachers! Did you use this instructable in your classroom?
Add a Teacher Note to share how you incorporated it into your lesson.

Step 1: Equipment

*You could use an IR LED and 470Ω resistor between cathode and ground

Step 2: Connect Your NES Controller to Your Arduino

  • Secure your NES Controller, Arduino Uno, Power Supply, Transmitter Module, Receiver Module and Breadboard to your board with blu-tack
  • Red jumper wire from + Rail on Breadboard to Pin 7 NES Controller (5V)
  • Black wire from - Rail to Pin 1 NES Controller (GND)
  • Orange wire from Arduino D5 to Pin 2 NES Controller (Clock)
  • Yellow wire from Arduino D6 to Pin 3 NES Controller (Latch)
  • Green wire from Arduino D7 to Pin 4 NES Controller (Data)
  • Red wire from Arduino +5V to + Rail
  • Black wire from Arduino GND to - Rail
  • The NES Controller's Pin 5 and Pin 6 serve no function

Step 3: Add an Infrared Transmitter Module

  • Black wire from - IR Transmitter Module to - Rail
  • Blue wire from S IR Transmitter Module to Arduino D3 (PWM)

Step 4: Add an Infrared Receiver Module

  • Black wire from - IR Receiver Module to - Rail
  • Red wire from 5V IR Receiver Module (middle pin) to + Rail
  • Purple wire from S IR Receiver Module (signal pin) to Arduino D11

Step 5: The NES Controller’s 4021 8-bit Shift Register Chip

It is worthwhile learning a bit about how the NES controller works and how it interfaces with an Arduino.

The NES controller has 8 buttons with pull-up resistors. Each button is connected to a pin on the 4021 8-bit shift register chip. When a button is pressed the shift register will make a '0' - Analogous to LOW when a pushbutton is pressed. When a button is not pressed the shift register will make a '1' - Analogous to HIGH when a pushbutton is released. Triggering the Latch input pin will make the first button state available to the Data pin. The Clock pin is then pulsed repeatedly to determine the button states connected to the remaining pins. The 8-bit data is then made available on the Data pin.

The getControllerData() function used in the sketch for this Instructable tells the Arduino to trigger the Latch pin:

byte getControllerData(){
/* adapted from Gordon McComb's "Teachbot gamepad controllersketch" in "Arduino Robot Bonanza" McGraw-Hill 2013 */
byte value = 0;
digitalWrite (nesLatch, HIGH); //Trigger Latch pin on 4021 Shift Register
delayMicroseconds(5);
digitalWrite (nesLatch, LOW);

Then to pulse the Clock pin:

for (int i=0; i<8; i++){
digitalWrite (nesClock,LOW);
value |= digitalRead (nesData) << (7-i); // read button state first pin then //shift the bit to the left
digitalWrite (nesClock, HIGH); // Pulse the Clock pin to high
}return (value); // returns value which is called on later in the loop()
}

Then read the Data pin and return a value which can be used once the function is called in the main loop() of the sketch:

void loop(){
code = getControllerData();

The box below describes the 8-bit data sent to the Arduino when each button* is pressed (binary, decimal, hexadecimal)*:

/*	Button			Binary		Dec	Hex
-------------------------------------------------------------------------------------
Right B01111111 127 0x7F
Left B10111111 191 0xBF
Down B11011111 223 0xDF
Up B11101111 239 0xEF
Start B11110111 247 0xF7
Select B11111011 251 0xFB
B B11111101 253 0xFD
A B11111110 254 0xFE
B + Right B01111101 125 0x7D
B + Left B10111101 189 0xBD
B + Down B11011101 221 0xDD
B + Up B11101101 237 0xED
B + Start B11110101 245 0xF5
B + Select B11111001 249 0xF9
B + A B11111100 252 0xFC
A + Right B01111110 126 0x7E
A + Left B10111110 190 0xBE
A + Down B11011110 222 0xDE
A + Up B11101110 238 0xEE
A + Start B11110110 246 0xF6
A + Select B11111010 250 0xFA
No button pressed B11111111 255 0xFF */

*Note pressing 2 buttons at once will provide additional values - This is part of the beauty of the NES Controller / 4021 8-bit shift Register. Using just 8 buttons and 5 Arduino pins you can control many tasks.

Step 6: Download IR Library

The key to any Arduino-based IR remote control is Ken Shirriff’s comprehensive IRremote library.

  • Download IRremote library from here.
  • Save it in your Arduino Libraries folder.
  • Open Arduino IDE.
  • Explore the documentation and example sketches.
  • Plug your NES controller remote into your computer using USB cable.
  • Upload IRrecvDumpV2 to your NES controller remote.
  • Open the serial monitor.
  • Point your electrical appliance’s remote at your project’s IR receiver and press a button.
  • You should see results like those pictured above.
  • Record the hexadecimal* data received with each button press - This is used in the final sketch

*Hexadecimal is easier for the human brain to visualise, understand and process rather than raw binary data - Panasonic uses a 48-bit (i.e. a sequence of 48 '1's and '0's)protocol.

Step 7: Ir_Panasonic.cpp Fix for Smart TV

  • Open Arduino IDE
  • Got to Sketch menu
  • Select Include Library
  • Select Manage Libraries
  • Search for IRremote library
  • Download it and include it in your sketch
#include <IRremote.h>
  • Most IR remotes that work with Arduino use the NEC protocol. Unfortunately if you want to use your Arduino as a remote for a Panasonic Smart TV you'll need to fix the ir_Panasonic.cpp.
  • .cpp files cannot be altered with Arduino IDE
  • Download Visual Studio Code
  • Open ir_Panasonic.cpp (in IRremote library folder)
  • Go to line 24 of code
enableIROut(35);
  • Change 35 to 37 (A different frequency is used for Panasonic TVs) - I found the answer reading this Adafruit tutorial by Chris Young. In his tutorial he mentioned most infrared remote appliances have a bandpass filter tuned between 36kHz and 40kHz, so it was just a matter of trial and error in modifying the enableIROut value.
enableIROut(37);
  • Save file as ir_Panasonic.cpp

Step 8: Sketch

  • The final sketch is written in the box below - Pay attention to the comments for further explanations(lines with //, /* or ...*/) and adapt it to your own needs
#include <boarddefs.h>
#include <ir_lego_pf_bitstreamencoder.h>
#include <irremote.h>
#include <irremoteint.h>

#define nesClock 5 // CLOCK plugs into D5
#define nesLatch 6 // LATCH plugs into D6
#define nesData 7 // DATA plugs into D7
#define led 3

#define address       0x4004 //Obtained using IRrecvDumpV2
#define powerButton   0x100BCBD //Obtained using IRrecvDumpV2

#define rightButton    0x100F2F3
#define leftButton     0x1007273
#define downButton     0x100D2D3
#define upButton       0x1005253
#define okButton       0x1009293

#define exitButton    0x100CBCA
#define returnButton  0x1002B2A

#define channelDown   0x100ACAD
#define channelUp     0x1002C2D

#define volumeDown    0x1008485
#define volumeUp      0x1000405

#define netFlix       0x1904FDE
#define iView         0x1000E0F
#define freeView      0x1008E8F
#define info          0x1009C9D
#define guide         0x190E170

#define debounce      180

int nesCode; //Variable used with switch...case command in loop()  

IRsend irsend;

void setup() {
Serial.begin (9600); // Turn the serial monitor on
pinMode (nesLatch, OUTPUT); // LATCH is an OUTPUT pin
pinMode (nesClock, OUTPUT); // CLOCK is an OUTPUT pin
pinMode (nesData, INPUT); // DATA is an INPUT pin
}

byte getControllerData(){ //Gordon McComb Arduino Robot Bonanza 2013
  digitalWrite (nesLatch, HIGH);
  delayMicroseconds (12);
  digitalWrite (nesLatch, LOW);
  byte output = 0;
  for (int i = 0; i<8; i++){
    output |= digitalRead (nesData) << i;
    digitalWrite (nesClock, HIGH);
    delayMicroseconds (6);
    digitalWrite (nesClock, LOW);
    delayMicroseconds (6); 
  }
  return output;
}

void loop(){
nesCode = getControllerData(); //Check if button pressed
nesCode = getControllerData();
nesCode = getControllerData();//Works more smoothly if you do this 3 x
switch(nesCode){
  case 0x7F: // RIGHT button pressed
  digitalWrite (led,HIGH);
  irsend.sendPanasonic (address,channelUp);
  digitalWrite (led, LOW);
  delay (debounce);
  break;

  case 0xBF: // LEFT button pressed
  digitalWrite (led,HIGH);
  irsend.sendPanasonic (address,channelDown);
  digitalWrite (led, LOW);  
  delay (debounce);
  break;

  case 0xDF: // DOWN button pressed
  digitalWrite (led,HIGH);
  irsend.sendPanasonic (address,volumeDown);
  digitalWrite (led, LOW);
  delay (debounce);
  break;

  case 0xEF:
  digitalWrite (led,HIGH); //UP
  irsend.sendPanasonic (address,volumeUp);
  digitalWrite (led, LOW);
  delay (debounce);
  break;

  case 0xF7: //Start
  digitalWrite (led,HIGH);
  irsend.sendPanasonic (address,powerButton);
  digitalWrite (led, LOW);
  delay (debounce);
  break;

  case 0xFB: //Select
  digitalWrite (led,HIGH);
  irsend.sendPanasonic (address, netFlix);
  digitalWrite (led, LOW);
  delay (debounce);
  break;

  case 0xFA: // A + Select
  digitalWrite (led,HIGH);
  irsend.sendPanasonic (address, exitButton);
  digitalWrite (led, LOW);
  delay (debounce);
  break;

  case 0xF9: // B + Select
  digitalWrite (led,HIGH);
  irsend.sendPanasonic (address, returnButton);
  digitalWrite (led, LOW);
  delay (debounce);
  break;

  case 0x7E: // A + Right
  digitalWrite (led,HIGH);
  irsend.sendPanasonic (address, rightButton);
  digitalWrite (led, LOW);
  delay (debounce);
  break;

  case 0xBE: // A + Left
  digitalWrite (led,HIGH);
  irsend.sendPanasonic (address, leftButton);
  digitalWrite (led, LOW);
  delay (debounce);
  break;

  case 0xDE: // A + Down
  digitalWrite (led,HIGH);
  irsend.sendPanasonic (address, downButton);
  digitalWrite (led, LOW);
  delay (debounce);
  break;

  case 0xEE: // A + UP
  digitalWrite (led,HIGH);
  irsend.sendPanasonic (address, upButton);
  digitalWrite (led, LOW);
  delay (debounce);
  break;

  case 0xF6: // A + Start
  digitalWrite (led,HIGH);
  irsend.sendPanasonic (address, okButton);
  digitalWrite (led, LOW);
  delay (debounce);
  break;

  case 0x7D: // B + Right
  digitalWrite (led,HIGH);
  irsend.sendPanasonic (address, freeView);
  digitalWrite (led, LOW);
  delay (debounce);
  break;

  case 0xBD: // B + Left
  digitalWrite (led, HIGH);
  irsend.sendPanasonic (address, iView);
  digitalWrite (led, LOW);
  break;

  case 0xDD: // B + Down
  digitalWrite (led, HIGH);
  irsend.sendPanasonic (address, info);
  digitalWrite (led, LOW);
  break;

  case 0xED: // B + UP
  digitalWrite (led, HIGH);
  irsend.sendPanasonic (address, guide);
  digitalWrite (led, LOW);
  break;
  }
}

Get out there experiment and have fun!

Step 9: RGB LED Variation

I altered the code to control an LED night lamp to demonstrate the remote in action - I didn't want to breach any copyright with filming TV content

Arduino Contest 2019

Participated in the
Arduino Contest 2019

Be the First to Share

    Recommendations

    • Made with Math Contest

      Made with Math Contest
    • Multi-Discipline Contest

      Multi-Discipline Contest
    • Robotics Contest

      Robotics Contest

    3 Discussions

    None
    barbecue_donutsaudreyobscura

    Reply 5 months ago

    Thanks. The prototype works well. I think I might try and integrate an Arduino Pro Mini, rechargeable battery and IR transmitter into the case. I just need to work out if I can drill out the rusty screws without damaging it.