SNES controller to PC adapter with Arduino micro.

IMG_20151128_000144

Super Nintendo, together with the Sega Megadrive (or Genesis) are the representative consoles of the 16-bit era. I had the Megadrive, but played some lot in Super Nintendo too.

I have played in emulators since always, a lot of them, including the SNES. I don’t think I play bad using the keyboard, but I prefer using controllers, even better if the controllers are the ones of the console emulated. Some time ago, I built a adapter for  connecting SNES controllers to PC from Raphaël Assénat’s site using a ATmega 8 with the VUSB library. I found the USB implementation to hard to understand, so I just followed the instructions. In the end, it worked.

WP_20150310_11_05_13_Pro
My old SNES controllers adapter.

I wanted to play Super Mario, but I did not have this adapter anymore. However, I had a Arduino Micro and some veroboard. The thing about the Arduino Micro, is that like the Arduino Leonardo, it has native USB, being able to act like a keyboard, a mouse, a joystick, or whatever. The Arduino Leonardo is probably suitable for this too, as it also has a native USB port.

Note: The other Arduino models generally use another ATmega to handle the USB protocol, while the main chip is used for code. Usually the user may not even realize that the board has two processors. The models Micro and Leonardo have a USB port in the ATmega used (32u4), and only one chip is used for USB communication and general coding.

Hardware

I had to connect the controllers to the Arduino. For this I used some veroboard and male headers basically.Please, remember that only the Arduino micro and Leonardo can be used for this.  You can see the details below:

IMG_20151128_000010
I used two male pin from headers to make the connector.

 

IMG_20151127_235920
In my defense, I used the worst soldering iron ever.

 

The SNES controller connector pinout is :

snes

I connected as follows, but you can change in the code if you want.

Controller              Arduino

       5v                         5v

       data                       D7

       clock                      D4

        latch                     D5

        GND                 GND

Note: Every line is shared between the controllers except the data line, this means that latch, clock, 5v and GNd are connected in the controllers connected, the data lines must be separated, I used the pin D6 for the data line of the second controller. A reading in the code can clear this better.

Library

I still don’t know much about the USB protocol and its way of communication. In addition, I wanted to complete the project in one afternoon. It was time for a library.

Ops, I assume that you have the last version of Arduino software, because the library does not work with version 1.6.5 or below. If you don’t have the version 1.6.6, please update so we can continue. :)

Oh, coming back to the code. The library I found works great, it was coded by Matthew Heironimus, and can be found here. Just download it and extract in the libraries folder of the Arduino IDE files.

With this library, you can make the Arduino appear in the computer as up to 3 controllers. Then you can code the board to receive the button states from the controllers and send them to the PC as a USB HID controller.  I choose to use the two controllers code, because I only had two SNES controllers anyway.

SNES controller protocol

The SNES controller protocol is pretty simple. In the connector of the controller there are two lines for power (5v and ground), and three other lines, one for cock, one for data, and the other is the latch line. To read the buttons, the procedure is:

  1. High pulse on the latch pin for 12us. This instructs the controller to lock the state of the buttons for reading.
  2. The first bit(button B state) will be already in the data line, read it.
  3. Toggle the clock light form low to high, read the bit, toggle the clock line to low again.
  4. Repeat the step four for the next 14 bits(16 bits are read, but some are always high).

As said before, pretty easy to read. More information can be found in this useful document.

Arduino Code.

Finally. If you already installed the library and connected everything, just burn the code bellow in the Arduino and you have finished. Yay!

#include "Joystick2.h"

int clk = 4, data1 = 7, data2 = 6, latch = 5; //two data lines, one for each controller.
uint16_t buttons1 = 0, buttons2 = 0;//the variables for each controller state
const bool testAutoSendMode = false;//we choose when to send the values over USB


void setup() {
    Joystick[0].begin(false);//Initialise the controllers
    Joystick[1].begin(false);

  // Pin config
  pinMode(latch, OUTPUT);
  pinMode(clk, OUTPUT);
  pinMode(data1, INPUT);
  pinMode(data2, INPUT);

}
int readControllers() {
  digitalWrite(latch, HIGH);//latch pulse, lock the state of the buttons in the register
  delayMicroseconds(12);
  digitalWrite(latch, LOW);
  delayMicroseconds(6);
  buttons1 = 0;//zero the variables to receive new values.
  buttons2 = 0;
  buttons1 |= (digitalRead(data1) << 0); //first bit read before clock.
  buttons2 |= (digitalRead(data2) << 0);

  for (int i = 1; i < 16; i++) {//clock &read
    digitalWrite(clk, HIGH);
    delayMicroseconds(6);
    buttons1 |= (digitalRead(data1) << i);//the values are stored for each bit
    buttons2 |= (digitalRead(data2) << i);
    digitalWrite(clk, LOW);
    delayMicroseconds(6);
  }
  buttons1 = ~buttons1; //workaround, a button pressed is read as a '0'(pull ups), jus inversing them to my taste. 
  buttons2 = ~buttons2; 

}
void loop() {
  readControllers();
  //Controller 1
  if (buttons1 & (1 << 0)) {
    Joystick[0].pressButton(0); //B
  } else {
    Joystick[0].releaseButton(0);
  }
  if (buttons1 & (1 << 1)) {
    Joystick[0].pressButton(1); //Y
  } else {
    Joystick[0].releaseButton(1);
  }
  if (buttons1 & (1 << 2)) {
    Joystick[0].pressButton(2); //Select
  } else {
    Joystick[0].releaseButton(2);
  }
  if (buttons1 & (1 << 3)) {
    Joystick[0].pressButton(3); //Start
  } else {
    Joystick[0].releaseButton(3);
  }

  if (buttons1 & (1 << 4)) {
    Joystick[0].setYAxis(-127); //Up and Down, -127=total up, 127= total down
  } else if (buttons1 & (1 << 5)) {
    Joystick[0].setYAxis(127);
  } else {
    Joystick[0].setYAxis(0); //the value zero, means the stick is stopped
  }
  if (buttons1 & (1 << 6)) {
    Joystick[0].setXAxis(-127); //Right left
  } else if (buttons1 & (1 << 7)) {
    Joystick[0].setXAxis(127);
  } else {
    Joystick[0].setXAxis(0);
  }


  if (buttons1 & (1 << 8)) {
    Joystick[0].pressButton(4); //A
  } else {
    Joystick[0].releaseButton(4);
  }
  if (buttons1 & (1 << 9)) {
    Joystick[0].pressButton(5); //X
  } else {
    Joystick[0].releaseButton(5);
  }
  if (buttons1 & (1 << 10)) {
    Joystick[0].pressButton(6); //L
  } else {
    Joystick[0].releaseButton(6);
  }
  if (buttons1 & (1 << 11)) {
    Joystick[0].pressButton(7); //R
  } else {
    Joystick[0].releaseButton(7);
  }

  //Controller 2
  if (buttons2 & (1 << 0)) {
    Joystick[1].pressButton(0); //B
  } else {
    Joystick[1].releaseButton(0);
  }
  if (buttons2 & (1 << 1)) {
    Joystick[1].pressButton(1); //Y
  } else {
    Joystick[1].releaseButton(1);
  }
  if (buttons2 & (1 << 2)) {
    Joystick[1].pressButton(2); //Select
  } else {
    Joystick[1].releaseButton(2);
  }
  if (buttons2 & (1 << 3)) {
    Joystick[1].pressButton(3); //Start
  } else {
    Joystick[1].releaseButton(3);
  }

  if (buttons2 & (1 << 4)) {
    Joystick[1].setYAxis(-127); //Up Down
  } else if (buttons2 & (1 << 5)) {
    Joystick[1].setYAxis(127);
  } else {
    Joystick[1].setYAxis(0);
  }
  if (buttons2 & (1 << 6)) {
    Joystick[1].setXAxis(-127); //Right left
  } else if (buttons2 & (1 << 7)) {
    Joystick[1].setXAxis(127);
  } else {
    Joystick[1].setXAxis(0);
  }


  if (buttons2 & (1 << 8)) {
    Joystick[1].pressButton(4); //A
  } else {
    Joystick[1].releaseButton(4);
  }
  if (buttons2 & (1 << 9)) {
    Joystick[1].pressButton(5); //X
  } else {
    Joystick[1].releaseButton(5);
  }
  if (buttons2 & (1 << 10)) {
    Joystick[1].pressButton(6); //L
  } else {
    Joystick[1].releaseButton(6);
  }
  if (buttons2 & (1 << 11)) {
    Joystick[1].pressButton(7); //R
  } else {
    Joystick[1].releaseButton(7);
  }

  if (testAutoSendMode == false)
  {
    Joystick[0].sendState(); //send the state to the computer.
    Joystick[1].sendState();
  }


}

 The end

Of course that is not. Thanks for reading and see you next time. o/

 

 

 

 

Robson Couto

Recentemente graduado Engenheiro Eletricista. Gosto de dedicar meu tempo a aprender sobre computadores, eletrônica, programação e engenharia reversa. Documento meus projetos no Dragão quando possível.

4 thoughts to “SNES controller to PC adapter with Arduino micro.”

  1. Fala , Robson ! bacana esse post gringo que você trouxe e adaptou ! gostei pacas ! eu vivo montando gamepads em Atmegas328p. Uso o próprio Arduino como Programador, mas sem precisar de outro chip ou usá-lo como “as ISP Programmer”. Faço o upload do arquivo hex direto para o Arduino e extraio o Atmega depois para o circuito. Eu modifiquei alguns parâmetros de bibliotecas deste firmware e o próprio “Makefile”. Fiquei interessado neste projeto que você postou. Baixei as bibliotecas. No entanto, surgem inúmeros erros de compilação antes de fazer o upload. Meu Arduino é o UNO R3 e uso a IDE 1.05 R2. Meu O.S é o Windows 7 Ultimate 64 bits. Tem ideia do que pode estar acontecendo ? eu simplesmente baixei o arquivo e nada modifiquei. Apenas extraí para a pasta biblioteca do Arduino e fiz a adição. No mais, o “Dragão sem Chama”, sempre tem me surpreendido com uns posts muito bacanas, tais como este seu. Muito bom ! valeu !

  2. Amigo, muito legal o projeto. Me diz uma coisa, acha que daria pra adaptar ele para fazer uma tarefa mais simples? Eu queria fazer um adaptador para ligar controles de snes no neogeo, e lá os controles não tem codificação nenhuma, cada botão / direção é um fio independente…Então seria uma conversão mais simples, apenas ler o protocolo do snes e jogar cada botão em uma saída (fio) correspondente. Agradeço a atenção. Obrigado!

    1. Oi Marcelo. Se o protocolo do controle de NEOGEO for simples como você diz, então sim.
      Além disso, você pode usar qualquer outro arduino, já que nao vai precisar da usb. Só fique ligado no número de pinos, pois devem ser muitos pelo que disse.
      Lembre se que as portas analógicas do Arduino podem ser usadas como pinos digitais.

      Por nada. Abraço

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *