After getting MAME going on my Raspberry Pi so that I could play old arcade games. I wanted to hook up a proper joystick. Back in the 80’s I had the excellent and ubiquitous Competition Pro 5000. As mine (foolishly) got sold with my Amiga stuff I got one on eBay, and it came in the original box:
The first step was to get it hooked up to the RPi general purpose input output (GPIO). I used a breadboard with my homebrew Pi Cobbler at one end and a similar connector at the other hooked up to an old PC serial card cable that has the right (male) DB9 connector for the joystick plug (female). It was then just a matter of adding some pull up resistors (10K) and some patch cables:
Since I was originally planning to use gpio-keys I used the joystick pinout to hook up to the RPi thus:
- Up -> 11 (GPIO 17)
- Down -> 13 (GPIO 22)
- Left -> 15 (GPIO 23)
- Right -> 16 (GPIO 24)
- n/c
- Fire -> 7 (GPIO 4)
- n/c
- GND
- n/c
Blind alley
Having already seen gpio-keys I thought I’d be using that, but when it came to the crunch I didn’t know where to start – I probably need a package of RPi kernel source. On reflection I probably really wanted gpio-mouse anyway.
After some digging around the Raspberry Pi Forums I found a comment about using Python to generate keystrokes. This got me headed in the direction of Python uinput, which is a module that can create keypresses.
sudo modprobe uinput git clone https://github.com/tuomasjjrasanen/python-uinput cd python-uinput sudo python setup.py install --prefix=/usr/local
Smelling victory I knocked together some code that turns GPIO into keypresses. Sadly it seems that AdvMAME derives it’s input in such a way that completely ignores uinput for keyboard. Back to the drawing board.
The right(ish) approach
Digging around the examples for Python-uinput I found one for joystick, so I had a go at creating a GPIO connected variant:
""" rpi-gpio-jstk.py by Chris Swan 9 Aug 2012 GPIO Joystick driver for Raspberry Pi for use with 80s 5 switch joysticks based on python-uinput/examples/joystick.py by tuomasjjrasanen https://github.com/tuomasjjrasanen/python-uinput/blob/master/examples/joystick.py requires uinput kernel module (sudo modprobe uinput) requires python-uinput (git clone https://github.com/tuomasjjrasanen/python-uinput) requires (from http://pypi.python.org/pypi/RPi.GPIO/0.3.1a) for detailed usage see http://blog.thestateofme.com/2012/08/10/raspberry-pi-gpio-joystick/ """ import uinput import time import RPi.GPIO as GPIO GPIO.setmode(GPIO.BOARD) # Up, Down, left, right, fire GPIO.setup(11, GPIO.IN) GPIO.setup(13, GPIO.IN) GPIO.setup(15, GPIO.IN) GPIO.setup(16, GPIO.IN) GPIO.setup(7, GPIO.IN) events = (uinput.BTN_JOYSTICK, uinput.ABS_X + (0, 255, 0, 0), uinput.ABS_Y + (0, 255, 0, 0)) device = uinput.Device(events) # Bools to keep track of movement fire = False up = False down = False left = False right = False # Center joystick # syn=False to emit an "atomic" (128, 128) event. device.emit(uinput.ABS_X, 128, syn=False) device.emit(uinput.ABS_Y, 128) while True: if (not fire) and (not GPIO.input(7)): # Fire button pressed fire = True device.emit(uinput.BTN_JOYSTICK, 1) if fire and GPIO.input(7): # Fire button released fire = False device.emit(uinput.BTN_JOYSTICK, 0) if (not up) and (not GPIO.input(11)): # Up button pressed up = True device.emit(uinput.ABS_Y, 0) # Zero Y if up and GPIO.input(11): # Up button released up = False device.emit(uinput.ABS_Y, 128) # Center Y if (not down) and (not GPIO.input(13)): # Down button pressed down = True device.emit(uinput.ABS_Y, 255) # Max Y if down and GPIO.input(13): # Down button released down = False device.emit(uinput.ABS_Y, 128) # Center Y if (not left) and (not GPIO.input(15)): # Left button pressed left = True device.emit(uinput.ABS_X, 0) # Zero X if left and GPIO.input(15): # Left button released left = False device.emit(uinput.ABS_X, 128) # Center X if (not right) and (not GPIO.input(16)):# Right button pressed right = True device.emit(uinput.ABS_X, 255) # Max X if right and GPIO.input(16): # Right button released right = False device.emit(uinput.ABS_X, 128) # Center X time.sleep(.02) # Poll every 20ms (otherwise CPU load gets too high)
With that saved as rpi-gpio-jstk.py it was a simple case of running:
sudo python rpi-gpio-jstk.py &
I tested using advj, and it showed input. When I fired up AdvMAME the joystick worked – horray – time for some gaming:)
Conclusion
I now have a working classic joystick for my classic games, and it seems to perform fine. I’m not entirely happy with the Python based approach, particularly as it uses polling, but I’ve seen nastier hacks. I’d still like to get a proper kernel module working, more so if it can use interrupts rather polling. I should probably also investigate using internal pullups so that I can simplify the wiring for when I make a more permanent (dual?) joystick adaptor.
Updates
19 Aug 2012 – I changed the code (available on GitHub) to use internal pullups. Since this now means that there are no components involved (just wires between the joystick and GPIO) I’m going to see if I can make an adaptor that just uses one side of the GPIO header (as all I need is GND and 5 inputs). This will involve changing some pins. More to follow when my DB9 PCB connectors arrive from China.