Page 1 of 1

Raspberry Pi SNESBot

Posted: Wed Nov 13, 2013 1:04 pm
by Sonny_Jim
Hi there, thought you lot might be interested in what I've been up to:

Controlling a Real SNES using a Raspberry Pi

The main project aims were:

A. Use a USB joystick plugged into the Pi to control a real SNES
B. Record and playback joystick inputs

To accomplish this, the Pi is connected thus:

USB Joystick -> Raspberry Pi USB
Raspberry Pi GPIO -> Real SNES controller port

How it works:

The SNES controller protocol is fairly simple, every 16.67ms (or 60Hz for NTSC, 50Hz for PAL consoles) it sends out a latch pulse to each controller, which contain 2 x 4021 shift registers. These shift registers then clock out the 16 bits of data to the console. Although the latch pulse is fairly slow, the clock is fairly quick, with 16us between clock pulses.

So far so good, but what about randomness in games? For example, how do I know that an enemy will be in the same place each time and not be in a different place due to a different number coming out of the random number generator?

Well, the good thing is that the SNES lacks a source of entropy (hardware random number generator or even a realtime clock), so most games use an absurdly simple principle:

Count the number of latches before a controller input is pressed and use that to seed a PRNG.

Also bear in mind that the SNES doesn't start sending out the latch pulses until the CPU is working correctly, this means that as long as we send out the same button presses on exactly the same latch on each poweron, then our PRNG will act exactly the same, with the same enemy patterns, powerups, items etc.

My different approaches

I tried a few different approaches. I first looked up some benchmarks for the GPIO on the Pi and found that it might be possible to bitbang the SNES directly. That approach ended in failure, mainly due to the Pi not being able to clock out the data fast enough for the SNES when it sees the latch pulse. A lot of people were telling me it wouldn't be possible with a Pi due to the overhead of Linux and I would need to use a microprocessor instead.

The way around this was to use some chips that can handle the faster clock pulse, load up the data into the chips from the Pi between pulses and use the chips to send the data out to the SNES controller port.

I took a cheap knock-off SNES controller (which also supplied the connector I needed for the controller port) and tried to wire up the GPIO directly to the pads, but this again ended in failure due to the crapness of the PCBs, as traces were lifting everywhere.

I then tried my third approach, which was met with some success. I duplicated the hardware inside of the SNES controller (2 x 4021 shift registers) and wired up 12 GPIO pins to that (X, Y, B, A, L, R, Up, Down, Left and Right).

I also needed something to act as a buffer/level converter for the latch pulse, as the output from the SNES is 5V logic whereas the GPIO pins on the Pi are only 3.3v tolerant.

To my amazement it worked and it was detected as a proper controller the Nintendo Controller Test software!

Live input was working fine, both from a USB keyboard and a PS1 controller connected via a USB adapter, but recording and playback would lose sync after a few button presses, which was slightly disheartening. Perhaps all those people telling me that I wouldn't be able to make it work with the overhead of Linux were right and I've have to change my approach again to use a microcontroller instead.

After a bit more playing around I had a bit of a eureka moment, where I found out that I was supplying the 4021's with 3.3V instead of 5V. Also I hadn't put in a blocking diode to stop current flow back into the SNES. Once I had fixed both of these problems playback worked absolutely fine. I could play a 20 minute game of Killer Instinct, switch off the SNES, turn it back on again and watch my Pi play the game exactly the same, as if it were a video recording! Huzzah! Here's a video of the Pi playing Super Mario All-Stars:
http://www.youtube.com/watch?v=McgHFdjaQMc

The circuit diagram:
Image

TAS Videos

So I was at the stage where I could playback from my own playback format and was successful with roughly 90% of the games I tried. So it was time to look at working on using emulator movie files. These are files recorded by an emulator that are used to playback high score attempts/speed runs etc. Sometimes emulators are used to create bizarre and lightning quick videos called 'Tool Assisted Speedruns'. For more information about TAS runs, have a look here:
http://tasvideos.org/

These would be a good source of videos to demonstrate the abilities of SNESBot. To try and get things to work first time, I went the simple route. I chose a TAS video that had previously been verifed on hardware, which was the KFC-Mario speedrun of Super Mario All-Stars: Super Mario Bros. Lost levels. Because I was intending to run on hardware, I would need to use a pretty accurate emulator, so lsnes looked like a good candidate. I found a Lua script by Ilari to output the controller data I needed for the emulator and wrote some importing code for my bot. After a few false starts, (ILari was particularly helpful in getting it working) I was at the stage where I could give this TAS video a try.

I grabbed my copy of SMAS, plugged it in and it went through the title screens, Mario started to run and then.

Nothing. He just sat there hopping on the spot and generally not doing what I wanted him to do.

This was slightly annoying, so I tried it with the console in PAL rather than NTSC, still the same. I took my SNES apart and saw the one of the legs of PPU2 had snapped off after I installed a SuperCIC mod. I believe this was causing it to run in permanent PAL mode, throwing off the timings. With a bit of delicate surgery, I managed to get it soldered back on, setup again and hit start. And I watched Mario go absolutely nuts for 33 minutes, flying impossibly through levels, then destroy Bowser and complete the game, all controlled via the Pi. A terrible video of it can be seen here:
http://www.youtube.com/watch?v=da7j4CaIf8g

Feeling rather pleased. my next stop was to write an importer for SNES9X emulator movie files, which hasn't worked out so well. Which is a shame, as this is a popular file format with a lot of TAS runs available. Because the TAS videos rely on very sharp timing, if the emulation isn't exactly the same as a SNES, the frames 'desync' and the Pi/SNES lose sync with each other.

This normally happens during a 'lag frame', which is a frame where the SNES CPU is too busy to poll the joystick inputs, because it's too busy drawing enemies exploding etc. The reason lsnes input files work and SNES9x files don't is down to the way they emulate these lag frames. Unfortunately atm there's not that many TAS videos in lsnes format, so I don't have much else to show off at the moment.

Hardware required:

A Raspberry Pi + SD card + USB keyboard / joystick
2 x CD4021B CMOS shift registers
1 x buffer / level convertor for the latch pulse
Some perf/vero board to mount it all on
An old floppy cable or something to break out the GPIO pins
A knockoff/broken controller for the SNES connector

Software needed:

Raspbian
WiringPi library by gordonDragon
SNESBot software from here:
https://github.com/SonnyJim/snesbot

Games confirmed to be working:

Assault Suit Vulcan
Contra 3: The Alien Wars
Earthworm Jim
UN Squadron
Super Mario Kart
Wagyan Paradise
Killer Instinct
Street Fighter 2
Street Fighter 2: Turbo
Super Mario All-Stars
Super Mario Kart
Super Mario World 2: Yoshis Story
SuperGB JPN + Tetris DX
SuperGB JPN + Tetris
Super R-Type

As you can see from the list above, I tried as many different genres as possible, as Super Mario isn't a great game to test with as the enemies always appear in the same space at the same time (ie they aren't PRNG controlled). Watching the SNESBot play Tetris is very funny to watch indeed.


Games that don't work

NBA Jam
Super Tennis
Speedy Gonzales

Why they don't work is a bit of a mystery to me at the moment. As the code that reads the controller interrupts isn't the same for each game, it could be that my bot isn't precise enough with the timings. Also some games use uninitialised memory as the seed for the PRNG, which means the game won't be exactly the same with the same inputs. I'll have to run through them with a SNES debugger at some point. Another thing to try which ILari pointed out would be to write a small bit of code to run on the SNES that dumps the initialised WRAM through the controller port to the Pi, so it can be incorporated into an emulator.

Future features


Netplay (depending on the RNG method used by each game)
Support other TAS video files other than lsnes
Right now the code is heavily based around using a USB PS1 controller, it would be nice to support other controllers without hacking the source.
Add support for SNES mouse via a USB mouse attached to the Pi, but this will require different interface hardware then what I have at the moment.
Autofire/special moves on a single button press

Re: Raspberry Pi SNESBot

Posted: Wed Nov 13, 2013 1:55 pm
by Takenover83
Although personally I am not interested in doing such a mod, reading about it was very entertaining. Thanks for sharing.

Re: Raspberry Pi SNESBot

Posted: Wed Nov 13, 2013 2:35 pm
by Sonny_Jim
The main reason for doing it was to be able to verify Tool Assisted Speedruns on hardware, which in turn helps emulator authors improve the accuracy of their emulators.

Re: Raspberry Pi SNESBot

Posted: Wed Feb 04, 2015 9:52 pm
by lagzorz
Hello i am an 18 yours old boy from germany and im currently working on such a project for my computing class.
Sadly my knowledge isnt as big as yours and id really hope you could help me out a little bit ( skype would be absolutely enough )
Any fast response will be appreciated greaty.

Greetings for germany

Kevin Vöhl A.K.A. Lagzorz

Re: Raspberry Pi SNESBot

Posted: Thu Feb 05, 2015 12:14 am
by toxibunny
Takenover83 wrote:Although personally I am not interested in doing such a mod, reading about it was very entertaining. Thanks for sharing.
me too!

Re: Raspberry Pi SNESBot

Posted: Thu Feb 05, 2015 1:35 pm
by DigitalLumberjack
Really nice and interesting job ! (1 year later :))

Re: Raspberry Pi SNESBot

Posted: Tue Mar 08, 2016 6:41 am
by Sonny_Jim
I've picked up working on this again after a 2 year hiatus and made a few changes to the hardware to make it a bit nicer/more robust.

The main change was to switch to using an mcp23017 port expander, which was a great idea for the following reasons:

1. I'm not using up so much of the Pi's GPIO, just 2 lines (SDA/SCL) via i2c
2. Although the MCP23017 itself runs at 5V, the i2c interface is 3.3v, meaning I can cut right back on the amount of level shifting I need to do. Right now the only signal I need to level shift to 3.3v is the latch signal from the SNES, rather than every single button :)

Re: Raspberry Pi SNESBot

Posted: Sun May 14, 2017 11:29 am
by Remiweb
Hi Sonny_Jim,
I'm trying to create a NES Bot, your project helps me a lot :D


I understand the way it works but I have a question :
What does the "sn74lvc245an" exactly do ?

From what I have found it converts the 3.3V GPIO signal to 5V for the console.
So every GPIO acts as a button and is sent from B to A to the shift register.

The Raspberry Pi also needs to have data from the latch so it synchronises with the console.
But here's the problem : why do you have Latch -> B5 -> A5 -> GPIO ?
If the signal was set up for the GPIO buttons outputs, won't B5 be greater than 5V ?
I'm not sure if a "sn74lvc245an" converts automaticaly 3.3V to 5V and 5V to 3.3V at the same time.

Thanks !
(please let me know if this is not clear, I never had to use this kind of english vocabulary)

Re: Raspberry Pi SNESBot

Posted: Mon May 15, 2017 2:49 am
by Sonny_Jim
Yes the 245 is there do to level shifting, but to be honest that original design was very inefficient and I found a better way of doing it after a bit of help from Gordondrogon.

Just use a MCP23017 as can do the level shifting for you:
http://ww1.microchip.com/downloads/en/D ... 21952b.pdf

Because the i2c interface is 3.3v, you can run the chip at 5V to communicate with the 4021 but the GPIO connection will be 3.3v.
Pi GPIO --[i2c]--> MCP23017 --[5V TTL]--> 4021 ----> NES

To get the latch into the GPIO safely, I found it easiest to just use a LVN245 to shift the levels down to 3.3v, I tried a resistor divider network but didn't have much luck with that:
NES --[5V TTL]--> 245 --> [3.3v]--> GPIO.

Just as an side, I found that it wasn't really possible to clock data out of the GPIO fast enough, hence why the 4021's were used. The latch is easy enough to pickup though, just remember to shift it down to 3.3v (5V may 'work', but it'll saturate the GPIO driver and be very unreliable).

Re: Raspberry Pi SNESBot

Posted: Tue Jan 16, 2018 5:21 am
by BitSquad
Hello, Sonny_Jim!

I would like to create a project like this and I wondered if you ever had completed schematics for the second version of the board.

Thank you!