PiGRRL Switch: Controller Test Firmware

As a follow-up to my last post, I wanted to go over the Arduino firmware I used for my primary controller test.  Essentially what it does is read the states of multiple buttons and print a sequence of 1’s or 0’s (1 for unpressed and 0 for pressed) to the Bluetooth module.


enum DPad {RIGHT = 2, UP = 3, LEFT = 4, DOWN = 5};
enum Buttons {A = 6, B = 7, X = 8, Y = 9};
void setup() {
// Set the DPad buttons to pullup inputs
pinMode(RIGHT, INPUT_PULLUP);
pinMode(UP, INPUT_PULLUP);
pinMode(LEFT, INPUT_PULLUP);
pinMode(DOWN, INPUT_PULLUP);
// Set the action buttons to pullup inputs
pinMode(A, INPUT_PULLUP);
pinMode(B, INPUT_PULLUP);
pinMode(X, INPUT_PULLUP);
pinMode(Y, INPUT_PULLUP);
Serial.begin(9600);
delay(5000);
}
void loop() {
// Print the Key packet
// DPad
Serial.print("K:");
Serial.print(digitalRead(RIGHT));
Serial.print(digitalRead(UP));
Serial.print(digitalRead(LEFT));
Serial.print(digitalRead(DOWN));
// Action buttons
Serial.print(digitalRead(A));
Serial.print(digitalRead(B));
Serial.print(digitalRead(X));
Serial.println(digitalRead(Y));
delay(10);
}

The whole program is embedded above and I’ll go through each section individually and explain what it does below.


enum DPad {RIGHT = 2, UP = 3, LEFT = 4, DOWN = 5};
enum Buttons {A = 6, B = 7, X = 8, Y = 9};

These enums serve the purpose of defining human-readable names for the pins that are hooked up to the buttons.  That way their usage in the code is immediately obvious upon reading.

Nasty literals!

This isn’t strictly necessary due to the simplicity of this program, but the general best practice is to not use integer literals in code.


// Set the DPad buttons to pullup inputs
pinMode(RIGHT, INPUT_PULLUP);
pinMode(UP, INPUT_PULLUP);
pinMode(LEFT, INPUT_PULLUP);
pinMode(DOWN, INPUT_PULLUP);
// Set the action buttons to pullup inputs
pinMode(A, INPUT_PULLUP);
pinMode(B, INPUT_PULLUP);
pinMode(X, INPUT_PULLUP);
pinMode(Y, INPUT_PULLUP);

This is the pin mode setup in the Arduino setup() function.  The buttons are hooked up directly to ground so that when the button is pressed, the pin sees 0V.  When not pressed the pin is floating so here I’m enabling the Arduino’s pullup resistors so that when the button isn’t pressed the pin sees 5V.


// Print the Key packet
// DPad
Serial.print("K:");
Serial.print(digitalRead(RIGHT));
Serial.print(digitalRead(UP));
Serial.print(digitalRead(LEFT));
Serial.print(digitalRead(DOWN));
// Action buttons
Serial.print(digitalRead(A));
Serial.print(digitalRead(B));
Serial.print(digitalRead(X));
Serial.println(digitalRead(Y));

Here the state of the buttons is read and printed to the Bluetooth module over UART.  Because of the way pullups are handled, ‘1’ corresponds to unpressed and ‘0’ corresponds to pressed.  The printing of the button states is preceded by a “K:” to act as a key-value pair.  This means that when the controller driver on the Raspberry Pi sees a “K” it knows it’s about to get a list of digital buttons.  I’m planning on using this in the future to enable more complex data than just a few buttons.  For example, I could use the keys “JX” and “JY” to denote the two axis values of an analog joystick.  The last serial print is a Serial.println() function call to print a newline character.  This is an easy way to segment the data so the controller driver can differentiate between multiple packets.


delay(10);

Lastly is the delay that sets the update rate of the program.  With a delay of 10 milliseconds at the end of the loop, new button states will be sent to the driver at an update rate of roughly 100Hz.  Right now this rate was arbitrarily decided, but in the future I’ll have to tweak it to balance several factors.

First off is the bandwidth of the Bluetooth connection.  As far as I can tell, the HC-05 module uses Bluetooth 2.0 which has a data throughput of of 2.1 Mbit/s or 275251.2 bytes per second.  Divide this by the update rate and you get the total amount of data that can be sent in one cycle of the program.  This also assumes that the connection is perfect with no lost packets.  Bluetooth’s serial port profile uses re-transmission to verify that packets sent are received.  This means that wireless noise can cause the connection to slow down while the Bluetooth module is attempting to resend a bad packet.  Additionally, if I go back to the BLE module and am able to get it working, BLE only has a throughput of 35389.44 bytes per second, or 13% of Bluetooth 2.0.  So while I’d be saving power I’d be restricting the update rate and amount of data I can send from the controller.

Another thing to consider is the update rate of the controller driver running on the Raspberry Pi.  Serial ports have buffers to store incoming data.  This means that if the driver is updating slower than the controller, the driver won’t be able to process all of the data coming in and the buffer will overflow.  This problem will exhibit itself as lag in the controls.  The driver needs to be updating at the same rate, or faster, than the controller.

The last consideration is debouncing.  This is a topic that has been extensively covered elsewhere, but suffice it to say that if the controller updates too fast it will accidentally send phantom bounces in the switch contacts to the driver and cause erroneous button presses.

Once everything has come together and is working to a degree I’m happy with, I’ll come back to these small issues and tweak everything until it works perfectly.  Until then, my next step is writing the controller driver!  As always, my code is available on my GitHub in the PiGRRL_Switch repository.

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.