Monday, May 15, 2017

GameSquare Software Notes

Introduction here.  Hardware notes here.

The Raspberry Pi 3 is running the current standard Raspbian image.  Here are the relevant contents of my /boot/config.txt file.

gpu_mem_1024=512
overscan_scale=1
overscan_left=-16
overscan_right=0
overscan_top=-16
overscan_bottom=-16
framebuffer_width=400
framebuffer_height=240
sdtv_mode=0
sdtv_aspect=3

I wrote two custom programs:  "WiiClassicPi" allows the Wii Classic Controller plus volume knob and halt button to control the Raspberry Pi, and "emu.py" is a bare-bones file browser for launching games.

WiiClassicPi code and configuration:

WiiClassicPi reads the Wii Classic Controller data over I2C and translates it to virtual keyboard presses thru the linux uinput device and system commands.

Set up the uinput device by adding a line for "uinput" in /etc/modules.  Make it accessible for all users by adding the following line to /etc/udev/rules.d/99-com.rules: KERNEL=="uinput",MODE="0666"

After a reboot, confirm that it shows up with read/write privileges for all.

pi@raspberrypi:~ $ ls -l /dev/uinput
crw-rw-rw- 1 root root 10, 223 Feb  2 13:36 /dev/uinput

I installed libsuinput to generate keyboard presses thru /dev/uinput.  I installed Wiring Pi for the I2C library.  I2C can be enabled on the Raspberry Pi using raspi-config.

The delay times after I2C transactions can be customized at the top of my code.  The delay times provide enough time for I2C transactions to occur.  They probably can be less, but the values I've chosen seem to work okay.  According to wikipedia, input lag becomes distracting around 200 ms, so keep the delay times much less than that.

The valid key codes to be listed in the keys[] array can be found in /usr/include/linux/input.h.  The key codes should be listed in the same order as the commented list of Wii Classic buttons.

Notice that the ZL button is mapped along with the select (-) button to perform a system halt, and the LX analog joystick is mapped to the 10 K potentiometer and used to adjust the volume.

Also, line 147 of the code contains a workaround for a glitch.  When the Raspberry Pi is running something CPU intensive (like an emulator) the I2C read sometimes results in garbage (0xFF)) in the 2nd and 3rd of the 6 bytes.  So the code throws away those reads and moves on.  This might cause some noticeable controller latency if too many reads in a row produce garbage, but I haven't noticed any problems.

Compile using:  gcc -lwiringPi -lsuinput WiiClassicPi.c -oWiiClassicPi

Run as "./WiiClassicPi verbose" to see controller data.

emu.py code and configuration:

I initially considered using RetroPie, however it would not recognize the virtual keyboard presses from /dev/uinput.  I also find it way too complex for such a simple thing as launching an emulator, especially on a small screen.

So I wrote emu.py, a Python 3 barebone curses-based frontend for launching emulators.  All the customization happens at the top of the code.  The keys are mapped to work with WiiClassicPi (except for the exit key (F1) to be used with an attached keyboard), and the extensions, directories, and launch commands have corresponding entries for each system.

# GUI keys
exit_key = curses.KEY_F1
up_key = curses.KEY_UP
down_key = curses.KEY_DOWN
left_key = curses.KEY_LEFT # back 1 page
right_key = curses.KEY_RIGHT # forward 1 page
launch_key = ord("j")
a_key = ord("x") # back 5 pages
b_key = ord("z") # forward 5 pages

# rom file extensions
file_ext = [".gb", ".gbc", ".nes", ".smc"]

# rom directories
romdir = ["/home/pi/roms/gb",
          "/home/pi/roms/gbc",
          "/home/pi/roms/nes",
          "/home/pi/roms/snes"]

# emulator launch commands
emulator = ["/home/pi/emu/./gambatte_sdl -i j g x z up down left right -s 8",
            "/home/pi/emu/./gambatte_sdl -i j g x z up down left right -s 8",
            "mednafen",
            "/home/pi/emu/./snes9x"]