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"]

GameSquare Hardware Assembly Notes

Introduction here.  Software notes here.

First, some photos for inspiration and reference.




Case:

The case slots together from the laser cut parts, and the screws and standoffs hold it together.  The four longer (31 mm) standoffs go in the four corners of the case, and the four shorter standoffs (25 mm) go in the back of the case to hold the controller PCB in place.  The screws fit snugly in the laser cut holes, so take care to ease them in the first time.

The two switches, three buttons, one potentiometer, headphone jack, and micro USB jack panel mount on the case.  The LCD is hot-glued on the face plate.  The controller buttons just sit on the PCB and are carefully wiggled into place when the face plate is fastened.  Velcro holds the battery, the speaker, and the Raspberry Pi in place.  I also used some Lego to hold the Raspberry Pi in place when plugging in a USB device.  Hot glue and electrical tape were used as needed to fasten parts and to prevent accidental short circuits.

LCD screen:

The plastic outer case can be pried open.  This photo shows where the composite video (yellow), ground (black), and 5V (blue) wires are to be soldered to the control board for the LCD.  (I used different colored wires in the final assembly shown above.)




Wii Classic Controller:

Open the controller case with a tri-wing screwdriver, and separate the PCB with a phillips screwdriver.  Desolder all the connections at J1, J2, J3, J4, ZL, and ZR.  Save the main PCB and the buttons and membranes on face of the controller.  I snipped off an empty corner of the PCB under J4 to make it fit in the case.

Battery:

In initial tests the 2500 mAh LiPo battery provides about 2 hours of play time.  When the battery goes low, the LED next to the power switch turns off.  The next thing that happens is the I2C bus stops working.  This is problematic since system halt (without an attached keyboard) relies on buttons connected to the I2C bus.  (If I can stop playing video games) I may add functionality to monitor the Powerboost 1000C LBO pin with a Raspberry Pi pin to force a system halt when it drops to ground (indicating low battery).

Powerboost 1000C:

Powerboost 1000C documentation 

PAM8302 audio amplifier:

PAM8302 datasheet

Wiring:

The wiring schematic shows all the relevant parts except the Raspberry Pi 3.  The Raspberry Pi pins are simply labeled.  This is a good reference for the forty-pin connector.  The PP# pins are labeled on the underside of the PCB near the analog audio/video TRRS connector.  I made all the Raspberry Pi connections by soldering to pins on the underside.





Presenting the GameSquare!





Design Considerations:

I wanted to design my own Raspberry Pi handheld gaming system.  I logged most of my gaming hours in the early '90s.  So I have a soft spot for games from that era, and I particularly like the Wii Classic controller for its SNES-like layout.  

I knew from a previous project that Wii controllers can communicate over an I2C bus such as the one available on a Raspberry Pi.  I chose a 5" LCD screen based on being a similar width as a Wii Classic Controller.

When designing the case I realized the dimensions were roughly square.  So I decided to make it square and the name came naturally as an homage to the GameCube.  

The case is 160 mm x 160 mm x 37 mm.  The face has D-pad, select (-), home, start (+), A, B, X, and Y.  The back has the L and R buttons.  The bottom has a headphone jack and micro B USB jack for charging.  The right side has access to two of the Raspberry Pi USB ports which is good for connecting a keyboard.  The top has heat vents, a power switch for the external amp/speaker, a volume knob, a halt button, a power switch, and an LED that indicates battery high or low.  In initial tests I am getting about two hours of play time running on only the battery while using the external amp/speaker and with the Wi-Fi turned on. 

Parts List:

Purchase the Inkscape design file for the laser cut case here.  My case was laser cut by Ponoko using a P2 sheet of Amber Bamboo.

See the hardware assembly notes here.   

See the software notes here.






















Tuesday, February 14, 2017

Simple Emulator Frontend for Raspberry Pi

I was annoyed by how complex the Raspberry Pi emulator frontends are, and I was looking for an excuse to write my first Python program.  So, here it is, a barebone curses-based frontend for launching emulators.  But really it could also be used to launch anything, like videos, ebooks, etc.

All the customization happens at the top of the code.  The extensions, directories, and launch commands will need corresponding entries for each emulator to be included.


# GUI keys
exit_key = 27 # ESC key
up_key = curses.KEY_UP
down_key = curses.KEY_DOWN
left_key = curses.KEY_LEFT
right_key = curses.KEY_RIGHT
launch_key = ord("j")
a_key = ord("x")
b_key = ord("z")

# 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",
"mednafen"]

Thursday, February 2, 2017

Wii Classic Controller over I2C to Raspberry Pi

I wanted to connect my Wii Classic Controller to my Raspberry Pi 3.  Here is how I did it.

The Hardware:

The Wiibrew extension controller page explains how to connect an official Wii Classic Controller via I2C.  (I tried a cheap ZettaGuard one, and I couldn't figure out the appropriate I2C initialization codes to make it work.)

The site Robot Electronics has an I2C tutorial that explains the need for pull up resistors when making I2C connections.  I used two 10 K pull up resistors and connected the four pins (3v3, SDA, SCL, and ground) from the Wii Classic Controller to the Raspberry Pi connector (pins 1, 3, 5, and 6).



The Software:

I am using a stock Raspbian image (as of January 2017).  The goal is to read the Wii Classic data and translate it to virtual keyboard presses thru the linux uinput device.

First, 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 is 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


Now, this page has useful lower level info about writing C code to use uinput, but ultimately I installed libsuinput to handle the details.

I installed wiringPi to use the I2C library.  I2C can be enabled on the Raspberry Pi using raspi-config.

Here is my code.

The delay times after I2C transactions and keys to output can be customized.  The delay times provide enough time for I2C transactions to occur.  They probably can be less, but the values I've used 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.

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

Monday, January 23, 2017

Chuanganzhuo 5" TFT LCD Monitor Plus Raspberry Pi 3





I recently got this 5" TFT LCD composite monitor, and I was able to connect it to and power it from a Raspberry Pi 3.  Here is how I did it.

I opened up the case to expose the bare LCD and driver board.  The case is just two pieces of plastic snapped together, so I pried it open with a screwdriver.  You can see the original input wires connected to the board:  red - 12-24 VDC, black - ground, yellow - composite video 1, and white - composite video 2.



I removed the original wires and connected the three wires shown below:  yellow - composite video, black - ground, and blue - 5 VDC.  The blue wire is (thin) 30 AWG wire that is easily soldered onto the PCB.



I connected the three wires to the Raspberry Pi as shown below using a TRRS connector:  yellow - composite video (S), black - ground (R adjacent to S), and blue - 5 VDC (pin 2 of the Raspberry Pi connector).




Here are the relevant /boot/config.txt settings I am using.

overscan_left=-24
overscan_right=-24
overscan_top=-20
overscan_bottom=-20
framebuffer_width=400
framebuffer_height=240
sdtv_mode=0
sdtv_aspect=3

Enjoy!

Friday, January 1, 2016

DADSR Envelope Generator in Pure Data

I am new to Pure Data, and I created this DADSR envelope generator with looping capability.  I found the ADSR example in Miller Puckette's Pd book most useful.

Unfortunately I also read this blog and spent some time being confused because the Sustain stage is (incorrectly) implemented as a timed stage instead of as a sustained level to be held until the MIDI note off command is received.




Here is the Pd file.