SNES Controller adapter

I used an Arduino micro clone to make a simple SNES to USB adapter, but with the added bonus of supporting the rare NTT data controller.

snes ntt controller

I also added the option to allow different ‘modes’ which are changed by holding one of the number buttons when plugging it into USB. These modes currently are –

0 – D-pad and face buttons only as gamepad.

1 –  D-pad and face buttons as gamepad with numbers as keyboard keys.

2 – Everything as keyboard keys.

Also, there’s an RGB LED to indicate what mode is being used.

 

// Requires joystick library from https://github.com/MHeironimus/ArduinoJoystickLibrary
//
// Extended SNES controller for use with NTT Data controller
// Snes controller with a keypad!
//

#include <EEPROM.h>
#include <Keyboard.h>
#include <Joystick.h>

#define red_light_pin 3
#define green_light_pin 5
#define blue_light_pin 9

Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,JOYSTICK_TYPE_GAMEPAD,
  8, 0,                  // Button Count, Hat Switch Count
  true, true, false,     // X and Y, but no Z Axis
  false, false, false,   // No Rx, Ry, or Rz
  false, false,          // No rudder or throttle
  false, false, false);  // No accelerator, brake, or steering

const int dataPin = 4;    //  ,-------------.---------.
const int latchPin = 6;   //  |(5v)(7)(6)(4)|( )( )(G) )
const int clockPin = 7;   //  '-------------'---------'

int n_buttons = 32;
bool buttonData[32]; // read upto 32 buttons
bool oldKeypad[32]; // previous keypad inputs
char tempText[32];

// number keypad
const int PROGMEM buttons[] = {
'0','1','2','3',
'4','5','6','7',
'8','9','*',185, // 185 = # on US kayboard (which is what arduino uses).
'.','c',' ',KEY_RETURN,
}; 
// all inputs
const int PROGMEM allButtons[] = {
'b','y',211,214,  // B,Y,PageUp, PageDown
218,217,216,215,  // Up,Down,Left,Right
'a','x','l','r',  // A,X,L,R
' ',' ',' ',' ',  // Not button presses
'0','1','2','3',  // 0,1,2,3
'4','5','6','7',  // 4,5,6,7
'8','9','*',185,  // 8,9,*,#
'.','c',' ',176,  // .,c, Space, Return
}; 

/** the current address in the EEPROM (i.e. which byte we're going to write to next) **/
int address = 0;

int menuItem = 0;

void RGB_color(int red_light_value, int green_light_value, int blue_light_value){
  analogWrite(red_light_pin, red_light_value);
  analogWrite(green_light_pin, green_light_value);
  analogWrite(blue_light_pin, blue_light_value);
}

void setup(){
  // LED Pins
  pinMode(red_light_pin, OUTPUT);
  pinMode(green_light_pin, OUTPUT);
  pinMode(blue_light_pin, OUTPUT);

  //define pin modes
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT); 
  pinMode(dataPin, INPUT_PULLUP);

  // Initialize Joystick Library
  Joystick.begin();
  Joystick.setXAxisRange(-1, 1);
  Joystick.setYAxisRange(-1, 1);
  // set all buttons to 'off' just in case
  for(int t=0; t<16; t++){
    oldKeypad[t] = 0;
  }

  // read setting from EEPROM
  menuItem = EEPROM.read(address);
  // if setting is outside of range, default to 0
  if(menuItem >= 9)menuItem = 0;

  // read the controller, if any keypad button is pressed, change the setting
  readSnesController();
  if(buttonData[17]){ // 1 pressed
    menuItem = 0;
  }else if(buttonData[18]){ // 2 pressed
    menuItem = 1;
  }else if(buttonData[19]){ // 3 pressed
    menuItem = 2;
  }else if(buttonData[20]){ // 4 pressed
    menuItem = 3;
  }else if(buttonData[21]){ // 5 pressed
    menuItem = 4;
  }else if(buttonData[22]){ // 6 pressed
    menuItem = 5;
  }else if(buttonData[23]){ // 7 pressed
    menuItem = 6;
  }else if(buttonData[24]){ // 8 pressed
    menuItem = 7;
  }else if(buttonData[25]){ // 9 pressed
    menuItem = 8;
  }

  // save the setting to EEPROM if it is different
  EEPROM.update(address, menuItem);
}

void readSnesController(){
  // Latch Signal
  digitalWrite(latchPin, HIGH);
  delayMicroseconds(12);
  digitalWrite(latchPin, LOW);
  
  int temp = 0;
  n_buttons = 16;
  for (int i = 0; i < n_buttons; i++){
    digitalWrite(clockPin, LOW);
    delayMicroseconds(1);
    temp = digitalRead(dataPin);
    buttonData[i] = !temp;
    digitalWrite(clockPin, HIGH);
    // if extra data detected, read it
    if(i==13 && temp==0)n_buttons = 32;
    }
}

void loop() {
  readSnesController();
  /*
  char string[50];
  sprintf(string,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", buttonData[0],buttonData[1],buttonData[2],buttonData[3],
                                buttonData[4],buttonData[5],buttonData[6],buttonData[7],
                                buttonData[8],buttonData[9],buttonData[10],buttonData[11],
                                buttonData[12],buttonData[13],buttonData[14],buttonData[15]);
  Serial.println(string);

  Serial.println(menuItem);
*/
    switch(menuItem){
      case 0: // do nothing with extra buttons
        RGB_color(15,0,0);
        // Gamepad for D-pad & buttons,
        // Keypad uses keyboard
        // now to convert all of the data unto gamepad/keyboard presses
        Joystick.setYAxis(buttonData[5] - buttonData[4]); // Up/Down
        Joystick.setXAxis(buttonData[7] - buttonData[6]); // Left/Right

        // B A Y X L R Sel Start -- to match cheap usb adapter I have.
      
        Joystick.setButton(0, buttonData[0]); // B
        Joystick.setButton(1, buttonData[8]); // A
        Joystick.setButton(2, buttonData[1]); // Y
        Joystick.setButton(3, buttonData[9]); // X
        Joystick.setButton(4, buttonData[10]); // L
        Joystick.setButton(5, buttonData[11]); // R
        Joystick.setButton(6, buttonData[2]); // Select
        Joystick.setButton(7, buttonData[3]); // Start
      
      break;

      case 1:
        RGB_color(0,0,15);
        // Keyboard keys for all inputs
        // now to convert all of the data unto gamepad/keyboard presses
        for(int t=0; t<12; t++){
          if(oldKeypad[t] != buttonData[t]){
            if(buttonData[t]){
            Keyboard.press(pgm_read_byte(allButtons + t));
          } else {
            Keyboard.release(pgm_read_byte(allButtons + t));
            };
          };
          oldKeypad[t] = buttonData[t];
        }
        //skip 13,14,15,16
        for(int t=16; t<32; t++){
          if(oldKeypad[t] != buttonData[t]){
            if(buttonData[t]){
            Keyboard.press(pgm_read_byte(allButtons + t));
          } else {
            Keyboard.release(pgm_read_byte(allButtons + t));
            };
          };
          oldKeypad[t] = buttonData[t];
        }
        
      break;

      case 2:
        RGB_color(0,15,0);
        // Gamepad for D-pad & buttons,
        // Keypad uses keyboard
        // now to convert all of the data unto gamepad/keyboard presses
        Joystick.setYAxis(buttonData[5] - buttonData[4]); // Up/Down
        Joystick.setXAxis(buttonData[7] - buttonData[6]); // Left/Right

        // B A Y X L R Sel Start -- to match cheap usb adapter I have.
      
        Joystick.setButton(0, buttonData[0]); // B
        Joystick.setButton(1, buttonData[8]); // A
        Joystick.setButton(2, buttonData[1]); // Y
        Joystick.setButton(3, buttonData[9]); // X
        Joystick.setButton(4, buttonData[10]); // L
        Joystick.setButton(5, buttonData[11]); // R
        Joystick.setButton(6, buttonData[2]); // Select
        Joystick.setButton(7, buttonData[3]); // Start

        // then the keypad buttons to keyboard keys
        for(int t=0; t<16; t++){
          if(oldKeypad[t] != buttonData[t+16]){
            if(buttonData[t+16]){
              Keyboard.press(pgm_read_byte(buttons + t));
            } else {
              Keyboard.release(pgm_read_byte(buttons + t));
            };
          };
          oldKeypad[t] = buttonData[t+16];
        }
        
      break;

    } // switch

  
}