Over the last couple of weeks we've been hard at work on a couple of major features - finishing off the firmware for Pip's games controllers, and programming the most complicated part of Pip that you'll probably never see - SID, the "system interop daemon".
We've finished work on the games controller firmware. Here's a screenshot demonstrating each of the controllers' button reports in the macOS app "Joystick Show":
As you can see, each controller's USB device name includes the side (either L or R) that this controller represents - this will be important later when we talk about SID.
Once we had the controllers in a working state, we didn't stop there. Pip is a battery powered device so it's important to ensure that everything is as power efficient as possible; it's doubly important for the games controllers as their firmware is not user-upgradeable, so any opportunities to improve power consumption are gone once the devices leave the factory. We got hold of USB power monitor from Amazon and rolled up our sleeves to do some power optimisation.
This model is particularly well suited to our needs as it accurate down to a milliamp; most of the other USB power meters on the market are limited to a couple of decimal places; fine for measure battery output etc., but largely useless for peripherals with a tiny current draw.
Initial tests were decent - here's the right controller showing a current draw of 12mA - but we knew we could do better.
After spending some time with our heads buried in the micro-controller reference manual and various other application notes we'd managed to reduce the current draw to 7mA - a 42% in power reduction.
Pip has a bunch of places where, compared with a traditional Linux installation, it needs to integrate with hardware in fairly unusual ways:
- Two physical games controllers that should logically appear as one - Three physical buttons whose function depends on the mode the system is in - An external GPIO controller that can be concurrently accessed by both system and multiple user tasks.
One of our key design philosophies for Pip is that as far as possible everything that the user interacts with should be done so using standard techniques. So, for the games controllers, you shouldn't need to merge data from the two controllers yourself. And the physical buttons should only generate events when they are in the correct mode - you shouldn't need to filter them yourself. And you shouldn't need to worry about mediating with the system and/or other apps for accessing to the GPIOs and LEDs.
All of the above functionality is handled by what we've come to affectionately know as SID - the system interop daemon:
That looks pretty complicated. Let's go over it, piece by piece.
Virtual Games Controller: uses libusb to watch for controllers being connected/disconnected. When it detects the presence of both a left and right controller (remember from above each controller reports its side), they are opened for exclusive access, and Linux's uinput system is used to create a single "virtual" gamepad device that exposes a merged stream of events from both controllers.
Button Manager: watches for presses on Pip's hardware buttons, keeps track/changes the current button mode, and dispatches presses to the correct location based on said mode. Initially, the buttons are in "keyboard" mode, and presses on buttons 1 + 3 (left/right) become standard key presses (escape and enter, respectively), dispatched to the Virtual Keyboard. Quick taps on the centre button cycle between battery, volume and brightness control modes. In these modes, presses on buttons 1/3 are diverted to the System Controller. Finally, a long press on the centre button is interpreted as an intent to quit the current app, and emits a "back" key press to the Virtual Keyboard.
System Controller: receives requests to change the screen brightness and audio volume. Screen brightness changes are actioned by the GPIO controller (which controls the brightness via PWM), so these requests are submitted with high priority to the multiplexer. Volume changes are simpler, carried out at the Linux OS level.
Multiplexer: mediates communications between the external GPIO/LED micro-controller and any SID subsystem that wishes to use GPIO/LED functionality. The multiplexer exposes two priority levels so the System Controller is able to ensure its request are handled regardless of what any user applications are doing. The multiplexer also keeps track of which subsystem sent which request so replies can be relayed to the correct sender.
Virtual Keyboard: creates a virtual Linux keyboard device, again using uinput, and turns requests from the button manager into Linux key events.
User GPIO/LED server: creates a server (via Unix Domain Socket) that any client can connect to and send GPIO requests (like e.g. Arduino's digitalWrite, digitalRead). The protocol is efficient, allowing batches of pins to be read and written atomically. The server submits these requests to the GPIO multiplexer, and when replies come in they are relayed back to the client.
As it stands, SID is around 50% complete and will be finished over the next two weeks. Next time we'll be introducing you to the build system used to create Pip's final SD card images.
Jason and Sukhvir