This project consist of schematics and a linux kernel driver module for a controller board used for building an Arcade machine. The board is easily build on a strip-board with minimal components and costs.
The requirement for using the arcade controller board is that you have an hardware platform with 5V, I2C bus to chain your board on and a gpio pin available for each board you hook up, used for interrupt driven input. With other words, a normal computer is not working and you need to look into hardware of the Raspberry Pi era that meets specified hardware requirements.
Three boards are needed for a two player setup cabinet and two boards for a single player configuration. The design of the arcade controller board allows a maximum of 7 boards to be connected to the I2C bus. This means a maximum of 112 individual inputs and allows for any imaginable cabinet build as long that you have 7 gpio pins available for allocation.
Conceptually, there are two types of controller boards player and auxiliary. You need at least one player and one auxiliary controller for a single player setup, you can skip the auxiliary but how fun is that. The actual hardware used for the two types of controller boards does not differ, they are both design the same way and specifying a type is just a matter of a software configuration.
The differences are that the player controller board will provide 16 individual inputs for the player this is more than enough for game play of most arcade and consoles. The auxiliary controller board will provide additional 8 individual inputs used for generic input for example special emulator commands etc. and 8 generic outputs which you can use for steering hardware in your cabinet build.
This project provides an Linux kernel module with the driver for the two types of boards and some parameters to define which board is of what type. The type mapping is done by the configurable I2C address using jumpers on the arcade controller board.
When you load the Linux kernel driver module, it will try to instantiate three instances of the driver, two player and one auxiliary instance. Each instance creates and evdev input device for use with the Linux input system that is then mapped and used by the emulator of your choice.
The special auxiliary board will also create and evdev input device with eight buttons. The 8 outputs of an auxiliary board is exported into sysfs to be controlled from software or scripts. Use it as you see fits, for example turn on / off marque lights or what not.
Arcade controller board is built around MCP23017 I2C I/O expander chip and is very simple to build on a strip board.
Each board is connected to a I2C bus and a gpio pin used for interrupt when any input changes. You will use jumpers to set the I2C address of the board which is used by the Linux kernel driver to detect which type of board to instantiate. You can provide module parameters when loading the Linux kernel module driver to specify which address and gpio pin to use for each board types. Read section Using the Linux kernel module for more details how to use the module.
The image below shows how to build one using strip board, it is also marked up of which I/O pin that is mapped to input device event by the driver for reference when hooking up the board with your hardware.
The result of a built arcade controller board of above layout looks like the following image. However you could with some ease actually build three copies of the board into a hat design to sit on top of a Raspberry Pi. Its a matter of taste really, however I will not provide any hardware specific designs in this project.
As the built example above, you will see that this is very minimalistic and clean build, anyone could do it.
Each input on the board should short with ground to generate an event. A typical build is that you make an daisy chain of the the ground through your switches and then hook up the individual gpio pin with corresponding switch described in the strip-board schematics above.
For my setup I choose the following standar layouts for the two player controllers, first one is Player 1 setup, the left side of cabinet and the second if for Player 2, right side. This is just for ideas and reference, use any layout that fits your needs.
My initial choice of emulation platform is the Raspberry Pi 3 and therefor I will also describe how to hook up the boards with this specific device.
Here follows a diagram for the pin header of Raspberry Pi 3. The pins labled SDA1 / SCL1 is used for the I2C bus, and all your arcade controller boards should be connected in parallel on those two pins, 5v and ground. Each board will also allocate a individual gpio pin used for interrupts which is also labled in diagram below.
Here follows the pin header layout for Raspberry Pi 3 and the pins you will hook up the board to:
.---.
|[]o| 5v
SDA1 |o o| 5v
SCL1 |o o| Ground
|o o|
|o o|
GPIO17 |o o|
GPIO27 |o o|
GPIO22 |o o|
. .
. .
Default settings for the Linux kernel driver is targeted the Raspberry Pi 3 and if your configure your boards and hook as described below, it will work just right out of the box.
- Player 1, I2C Address: 0x20, Interrupt: GPIO17
- Player 2, I2C Address: 0x21, Interrupt: GPIO27
- Auxiliary, I2C Address: 0x22, Interrupt: GPIO22
If you want to use other addresses or gpio pins, you can change this using module parameters. This is needed for other hardware platforms that is not Raspberry Pi 3 compatilble. Read more about how to do this in section Using the Linux kernel module.
The driver is loaded using insmod or adding the module for automatic load upon startup, this is distribution specific and I will not go into details.
insmod src/acb.ko boards="player:0x20:17","player:0x21:27","auxiliary:0x22:22"
As you noticed the above command describes each board with type and which I2C addresses and gpio pin numbers to be used. Those values above is the default and can be left out if you are using Raspberry Pi and hooking up the boards as described in section Hook up with Raspberry Pi 3.
To build the Linux kernel module driver you need to install the kernel source package for your distribution, then just build the module using:
make
This will build the kernel module src/acb.ko
for your current
kernel. If you upgrade the kernel the module might need to be rebuilt
to work as expected, this is identified by error messages in dmesg log
while loading the module.
Retroarch uses controller specific configuration files. These files
maps input events to retroarch buttons and exists in
/usr/share/retroarch/autoconfig/udev/
. You need to create two
configuration files one for player and one for auxiliary and here
follows the examples configuration files.
/usr/share/retroarch/autoconfig/udev/acb_player.cfg
input_device = "Arcade player controller"
input_driver = "udev"
input_b_btn = "1"
input_y_btn = "3"
input_select_btn = "6"
input_start_btn = "7"
input_up_axis = "-1"
input_down_axis = "+1"
input_left_axis = "-0"
input_right_axis = "+0"
input_a_btn = "0"
input_x_btn = "2"
input_l_btn = "4"
input_r_btn = "5"
/usr/share/retroarch/autoconfig/udev/acb_auxiliary.cfg
input_device = "Arcade auxiliary controller"
input_driver = "udev"
input_save_state_btn = "0"
input_load_state_btn = "1"
input_exit_emulator_btn = "2"
input_reset_btn = "3"
input_menu_toggle_btn = "4"
input_enable_hotkey_btn = "5"
input_pause_toggle = "6"
-
Verify that expected boards are instantiated correctly using the tool
evtest
, which shows a list of registered input devices one for each board. Verify that the list contains expected input controllers, names to look for areArcade player controller
andArcade auxiliary controller
. Then select one of them and test the controller inputs.evtest
-
If controller shows up in step 1, and nothing happens when testing the controllers, verify that you are using the correct gpio pin for input interrupt.
-
There are some minor logging when the Linux kernel module is loaded. Check the that there are no errors while loading the module using dmesg.
-
Probe the I2C bus for available devices and verify that your boards are found using the following tool. If no devices are found, probe other buses to find the correct one on you system:
i2cdetect -y 1