PiGRRL Switch: Test Controller in Action!

2017-03-18 12.55.34

So I didn’t get a chance to take a video of the controller in action, but I do have a picture of me playing the original Zelda using my soldered up controller prototype. The Raspberry Pi and LCD is hooked up to one battery bank and the controller is hooked up to a separate one. Going through this test I noticed a few things that can be improved.

For starters, I had a hellish time getting enough power to both the Raspberry Pi and LCD. For whatever reason the power supply didn’t seem to be able to give enough juice to keep the Raspberry Pi from rebooting. The display has two micro USB ports on it that are used to power the screen and provide touchscreen feedback to the Pi. However since I currently don’t need to use the touchscreen I just powered the screen directly from the battery bank. To circumvent these power issues in the future I’ll need to see if I can get the Pi to provide more current from the USB ports. If that doesn’t work I can use one of the micro USB ports for touchscreen feedback and cut the red power wire to prevent the screen from drawing power from that port and use the second USB port to power the screen directly from the battery bank.

Another issue I noticed with my controller is the lack of menu buttons. The RetroPie interface requires using Start and Select buttons for navigation, so I had to map those to an attached keyboard since I only have four action buttons and four directional buttons on my prototype. Additionally I’ll also want shoulder buttons since the later consoles all have at least two and the PS1 has four.

The next step for the controller interface is designing the PCB!

PiGRRL Switch: Controller Driver

The next step in getting the controller to work as a RetroPie controller is writing a driver so the console interprets the serial data coming in from the Bluetooth module the same way it would interpret a USB controller.

I got this idea from petRockBlog who mapped their custom, two-player joystick to two separate virtual controllers in RetroPie.  Their code is available on GitHub.  After a quick review, I saw that their code is using a C user input emulation library called uinput.  In the interest of simplicity I found a Python uinput library and decided to use that to emulate my controller driver.

Below is a full snippet of the code from the GitHub repository followed by an explanation of how it works:


import serial
from uinput import *
class Controller:
"""docstring for Controller"""
NumButtons = 8
def __init__(self, devName, devKeys):
self.devPath = "/dev/" + devName
try:
print "Connecting to " + self.devPath
self.serial = serial.Serial(self.devPath, 9600, timeout=10)
except Exception as inst:
print type(inst) # the exception instance
print inst.args # arguments stored in .args
print inst
print "Connection to " + self.devPath + " failed!"
self.connected = False
else:
print "Connection to " + self.devPath + " successful!"
self.connected = True
self.devKeys = devKeys
self.device = Device(devKeys)
ValidKeys = [ KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J,
KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T,
KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z,]
ControllerNames = ["rfcomm0"]
Controllers = []
controllerNum = 0
for controllerName in ControllerNames:
Controllers.append(Controller(controllerName, ValidKeys[controllerNum:((controllerNum*Controller.NumButtons) 1)]))
Controllers[controllerNum].serial.reset_input_buffer()
controllerNum = controllerNum + 1
PrevPacket = {}
PrevPacket['K'] = '1' * Controller.NumButtons
while True:
controllerNum = 0
for controller in Controllers:
line = controller.serial.readline()
print line
packet = line.split(':')
if packet[0] == 'K':
charNum = 0
for char, prevChar in zip(packet[1], PrevPacket['K']):
if (char == '0') and (prevChar == '1'):
controller.device.emit(controller.devKeys[(controllerNum*Controller.NumButtons) + charNum], 1)
elif (char == '1') and (prevChar == '0'):
controller.device.emit(controller.devKeys[(controllerNum*Controller.NumButtons) + charNum], 0)
charNum = charNum + 1
PrevPacket['K'] = packet[1]
elif packet[0] == 'A':
pass
elif packet[0] == 'B':
pass
controllerNum = controllerNum + 1


class Controller:
"""docstring for Controller"""
NumButtons = 8
def __init__(self, devName, devKeys):
self.devPath = "/dev/" + devName
try:
print "Connecting to " + self.devPath
self.serial = serial.Serial(self.devPath, 9600, timeout=10)
except Exception as inst:
print type(inst) # the exception instance
print inst.args # arguments stored in .args
print inst
print "Connection to " + self.devPath + " failed!"
self.connected = False
else:
print "Connection to " + self.devPath + " successful!"
self.connected = True
self.devKeys = devKeys
self.device = Device(devKeys)

This is the definition of the Controller class.  A static variable called NumButtons is used to show the maximum number of buttons a Controller has.  A Controller instance is initialized with a path to the Bluetooth serial device which it then connects to.  The initialization also creates a uinput Device that it will use to simulate a virtual controller for providing input to the console.


ValidKeys = [ KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J,
KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T,
KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z,]

An array of keys is created to represent the possible input keys that will be passed into the system.  Right now I’m just using the letters of the alphabet.  RetroPie offers full controller customization so the actual keys used don’t really matter.  I still plan on changing the controller driver to emulate a joystick with analog controls, however, so this method will need to be updated in the future.


ControllerNames = ["rfcomm0"]

This array will store the device names for all of the Bluetooth serial consoles.  At some point I’d like to autodetect all of the serial consoles from the controllers but for now a hardcoded array will work.


Controllers = []
controllerNum = 0
for controllerName in ControllerNames:
Controllers.append(Controller(controllerName, ValidKeys[controllerNum:((controllerNum*Controller.NumButtons) 1)]))
Controllers[controllerNum].serial.reset_input_buffer()
controllerNum = controllerNum + 1

The program creates a controller for each serial console in the ControllerNames array and stores them in the Controllers array.  It also resets the serial input buffer so old input data doesn’t bog down the driver and it can start processing the most current data.


PrevPacket = {}
PrevPacket['K'] = '1' * Controller.NumButtons

My driver operates on button transitions (unpressed->pressed and pressed->unpressed) rather than operating on the button state.  This is to be consistent with keyboards which work in the same way.  Because of this transition-based processing, the previous input packet needs to be stored so the previous packet can be compared to the current packet and differences between the two can be detected.  Here the previous packet is instantiated as a dictionary and is initialized to a known, totally unpressed state.


while True:
controllerNum = 0
for controller in Controllers:
#…
controllerNum = controllerNum + 1

This is just an infinite loop which iterates over the list of controllers and processes each of them.


line = controller.serial.readline()
print line
packet = line.split(':')

A line is read from the serial port and split at the colon.  The left side will be the key character which reflects the type of data and the right side will be the data itself.


if packet[0] == 'K':
#…
elif packet[0] == 'A':
#…
elif packet[0] == 'B':
#…

This if statement detects which key is seen and executes the appropriate code.


charNum = 0
for char, prevChar in zip(packet[1], PrevPacket['K']):
if (char == '0') and (prevChar == '1'):
controller.device.emit(controller.devKeys[(controllerNum*Controller.NumButtons) + charNum], 1)
elif (char == '1') and (prevChar == '0'):
controller.device.emit(controller.devKeys[(controllerNum*Controller.NumButtons) + charNum], 0)
charNum = charNum + 1
PrevPacket['K'] = packet[1]

This is the code for the key ‘K’ which is the only type of data I currently have.  It iterates over each character in the eight bytes of data and compares it against the data from the previous packet.  If the value went to ‘1’ from ‘0’ it emits an event to signal that the key was pressed and if the value went to ‘0’ from ‘1’ it emits an event to signal the opposite.

PiGRRL Switch: Creating the Controllers

With the screen chosen and working, the next step for creating the PiGRRL Switch was prototyping the controllers.  Initially, I wanted something cheap and easy like a breakout board that acted as a Bluetooth HID joystick.  I was immediately drawn to Adafruit’s EZ-Key which acts as a Bluetooth keyboard.  At the time I’m writing this, however, it’s out of stock and seems to have been for a while.  Additionally, because it acts as a Bluetooth keyboard and not a joystick, it rules out any possibility of adding analog controls in the future.

Another alternative to a Bluetooth HID breakout would be taking apart a cheap Bluetooth joystick and putting it in a 3D printed casing.  However, I decided this would greatly reduce the design flexibility of the controllers and might make it difficult to reconfigure the controllers on the fly (i.e. using two JoyCons as one controller vs. using them as two separate controllers).

So with those two options off the table I decided instead to use a Bluetooth serial bridge.  The HM-10 BLE and HC-05 Bluetooth 2.0 modules are both cheap and plentiful and provide a good solution at the cost of some extra work.  These modules can be hooked up to the UART of an Arduino and paired via Bluetooth.  Once connected, it acts as a virtual serial port in Linux, allowing the serial data to be read just as if the Arduino was connected via USB or FTDI.  The only exception to this is that it doesn’t support firmware loading wirelessly.

2017-03-13 22.18.32

The next step was setting up the initial design on a breadboard.  Above is an Arduino Pro Mini, four pushbuttons wired to the digital pins, and the HM-10 BLE module.  I decided to use the HM-10 because of the lower power requirements (BLE being an initialism for Bluetooth Low Energy).  The code for the Arduino reads the values from the digital pins and prints out eight characters to signify which buttons are pressed (‘1’ for unpressed and ‘0’ for pressed).  Right now I’m using a byte for each button which is wasteful, so I’ll go back at some point in the future and make the code more efficient so each button is represented by a bit.

2017-03-14 22.50.16

Once everything was wired up and running I had a lot of trouble finding an app that could connect to the HM-10 as a serial terminal.  Apparently the BLE standard has a lot of bells and whistles that make configuration a bit more difficult.  After trying several different apps I eventually found Serial Bluetooth Terminal which can connect to both BLE and regular Bluetooth devices via a serial terminal.  Above is screenshot of my phone connected to the controller with the button status being transmitted.

2017-03-14 20.31.24

2017-03-14 20.31.37

With my proof of concept working, I soldered everything onto a proto-board, this time with eight buttons to serve as a D-pad and four action buttons.

With that complete the next step was connecting to the Raspberry Pi over a serial terminal.  Unfortunately, this was much more difficult than I expected.  I could pair and connect to the HM-10, but couldn’t find a way to mount it as a serial terminal.

2017-03-15 21.01.17

Rather than continue further down the rabbit hole, I decided to drop the BLE module for now and switch to the HC-05 modules I bought as a backup.  Those have been around for years and have been used extensively with Arduino and Raspberry Pi.  Once that module was paired and connected, mounting it as a serial terminal was as simple as using the following commands to connect and then print out the values read from the module:

sudo rfcomm bind /dev/rfcomm0 <MAC Address>
sudo cat /dev/rfcomm0

2017-03-17 19.20.25

2017-03-17 22.01.22

Lastly I connected the controller, screen, and Raspberry Pi to battery packs and verified everything still worked as suspected.  Success!  The next step is writing a program for Linux that reads the button data coming off the serial port and uses it to emulate a controller for the console.

Working with All the Pixels!

For the past two years, the computer setup in my home office has consisted of a Dell Precision T3500 with an old graphics card and two mismatched Dell monitors with roughly 720p resolution.  While this was plenty capable for running 3D printer software using pre-made files found on Thingiverse, I found the setup lacking when I started designing my own prints in OpenSCAD.  When it came to gaming, I couldn’t even run the original Portal, which came out in 2007.  My other option, my ThinkPad X220, seemed cramped in comparison to my dual 1080p monitor setup at work, and so a few months ago I started looking into upgrading my setup.

In an attempt to futureproof (and because I enjoy being on the bleeding edge), I decided to look at 4K monitors, given that the larger screen resolution would allow me to multitask or be able to keep open reference material (e.g. OpenSCAD cheatsheet) while working in an application.  I originally checked out a dual 4K 24″-27″ monitor setup as suggested in this blog post, but I quickly decided that I would need a larger screen in order to make full use of 4K.  After combining the calculations from isthisretina.com and OSHA’s recommended screen distance, I settled on getting a medium sized TV and using it as a monitor.

After much deliberation and browsing, I purchased a 39″ Seiki UHD TV when it was on sale for $280 and a GeForce GTX 960 to drive all of those pixels. The GTX 960 is a pretty capable graphics card and would be able to provide a decent gaming experience and even drive an additional 4K display if I ever decide to buy another one.  After working with this new setup for several weeks, I found a few pros and cons as outlined below:

Pros:

Activities

  • So many lines of code!  Eclipse was barely usable on my previous monitors.  I had to work with all of the Views closed to get a usable amount of space for editing code.  With my new one I can put Eclipse at half-screen, open a ton of Views, and still see over 100 lines of code.

OpenSCAD

  • Multitasking!  I love never having to switch windows.  This monitor makes it so I almost never have to do that.
  • It’s big enough to fully take advantage of the 4K  with a “Retina” distance at 30″.  I sit a little over two feet away from my monitor, so I can barely distinguish between individual pixels if I’m looking closely.
  • It’s the equivalent of four 1080p displays but cheaper than two 1080p displays.  I did have to upgrade my graphics card to account for the higher resolution, but even with that it was worth it.

Eagle

Cons:

  • There’s a slight backlight dimming on the edge of the screen due to the large contrast between viewing angles.  This isn’t too much of a problem and is easily solved by moving my head or rolling my chair side to side.
  • There’s no inactive monitor mode so I have to manually turn off the display.  Again, not a big problem, just another thing to remember.
  • My wife can steal the remote and turn off the TV when I ignore her for too long.

While I haven’t been working with the new setup long enough to tell for sure, I’m overall happy with my decision and would highly recommend it!