Rombus Pinball - A LattePanda Mini Pinball Machine

Well, I’ve gone and done it again. I’ve built another arcade machine. Man, these things are rather addictive to build :)

It started with the Rombus3000 followed by the RombusCT and then most recently the Tomy Turning Dashboard Outrun Arcade (which I had actually code-named RombusGT), but this time I’ve taken on another classic, the pinball machine. And so I present to you, [insert drumroll here], the Rombus Pinball.

The Hardware

I generally start my projects by thinking about the hardware that I’m going to want to use as I’ll need to know sizes when it comes to the design phase. My first thought was to go with a Rasberry Pi as it’s what I’m familiar with and it’s what I’ve used for my other arcades, but after looking online, there really didn’t seem to be any good options for pinball emulation on Linux at all. It all seemed to be windows based. Thankfully I remembered reading about a single board Windows computer called a LattePanda so I thought why not give that a go and so this was the approach I ended up taking.

A good thing about the LattePanda is that it already has an inbuilt Arduino Leonardo which was perfect for handling the controls, as well as support for multiple monitors which is ultimately what made the live “back glass” possible.

For the screens themselves I used the official LattePanda 7” display for the playing area, and a cheap 5” HDMI display for the back glass. Other key hardware elements include an Adafruit 3.7W stereo amp combined with a pair of 4ohm mini speakers from Pimoroni and lastly a selection of arcade buttons from Arcade World and your friendly online auction site.

The Build

Design

The design was all worked out in InkScape by hand. The core elements which dictated the size of the machine were really the 2 monitors. I started with the playing area, leaving space for a bezel as well as the aluminium edging then worked out the sides and overall base dimensions from this. A comfortable button location was worked out, and then the back layout was designed to house the speakers, power socket, auxiliary buttons and a fan which was pretty important to keep the LattePanda cool.

The backglass housing was then worked out in a similar way, starting with the screen dimensions and worked out from there, leaving enough space around for the HDMI and mini USB connectors needed.

Prototype

As this was a pretty experimental build I did a lot of prototyping as I went along, so it wasn’t really a case of design it and build it, there was actually quite a bit of back and forth.

The first prototype I did was for some internal scaffolding design I came up with in order to hold the screen into position. I didn’t want to have too many screws used, so I came up with some inner walls and beams to hold the screen where I needed it whilst keeping the internal space for wiring and components. It ended up working really well and is the solution I went with in the final build. Though in the final build I did tweak it slightly to incorporate some captive nuts for the side rails which I’ll get on to later.

From hear I then experimented with the main cabinet itself trying out button hole placement and different connection methods. Again, I didn’t want a load of screws everywhere so I ended up going with a notched edge system and wood glue. My prototypes were made with 3mm laser ply which I had intended to switch to MDF for the final build as I thought it would be too flimsy, but actually, after building the prototype, I found it to be quite rigid yet light so I actually ended up using it in the final build too.

Whilst working on the base, I was originally going to make the back removable, but when I found I could fit all the peripheral components on it, it made sense to just fit it into place and go in through the playing field area if I needed access to anything later on. This whole area did end up being pretty tight and awkward to work in, but it was manageable so I was happy with it.

With the base fully mocked up, the next bit was to work out the side rails and legs. I ended up using aluminium angle for this and cut it down using my Dremel tool and then gave it a light sanding for a brushed aluminium look. Whilst this was just a prototype at this stage, I did make sure to take care with this element as I knew I wanted whatever I made now to be the final solution too. I was planning to just mount the “lockdown bar” over the top of the side rails, but when I tried it, it didn’t look very nice so I ended up notching the side rails instead so that it would sit flush, which looked much neater.

In terms of mounting everything, the lockdown bar would be mounted to the actual front glass (sandwiching a bezel cutout too) which had notches cut into to locate it in the base. The side rails would then be screwed to the sides holding the front glass safely in position and hiding the locating notches. As mentioned earlier, to make this mounting easier, I designed some captive nuts into the screen scaffold so that they could be held in position when I came to closing things up at the end as there was no way I would have access to hold them in place myself.

For the legs, these were cut to size and then a small jig was used to work out hole placements with the holes themselves being drilled on my drill press.

It was also at this point that I quickly designed up and 3D printed some little feet for the legs to so that they wouldn’t scratch any surfaces it gets played on. They may have been a small element, but I was actually really pleased with how these looked in the end. It’s the little details.

The last element to prototype was the back glass. I ended up doing a few of these as on the earlier designed I left quite a lot of space around the monitor which made it totally the wrong scale, but in the end, reduced it as much as I could to be more in scale with the base. This did mean the HDMI / USB connectors for the monitor are pretty tight internally, but they worked, so I was happy.

Also on the early designs, I did plan to have the glass/bezel fix into the sides/top with notches but then changed this later on to keep the side and top clean by instead creating a lip around the front and wedging everything inside up against this lip to keep everything in position. As part of this element, I also came up with a mounting strategy for the monitor, creating a backplate for it to mount against using the supplied standoffs and having this fit snugly in the housing with the wedges pushing against this to keep it and the front glass in place. This was another thing that worked out really well in the end.

The last step for the back glass housing was a removable back, so for this, I had it notch into the top of the casing, and then used a cool fastening system I picked up from the Pimoroni gang of using a notch and O ring to keep it closed. Cheap and effective. PERFECT!

Decals

One of the first decals that I did was the coin mech button. I did a similar effect on the RombusCT so thought I’d do the same again for this project as it really works quite nicely. This was mocked up in InkScape printed on some acetate and then inserted into an illuminating push button.

For the main cabinet art, I spent quite a bit of time coming up with suitably retro logo and then a simple but bold striped pattern to go around the sides. I also came up with a cool fiery pinball design for the side of the back glass housing for some added pizzazz.

Once I was happy with the designs, I printed them out on paper and mounted them on the prototype to check I was happy with everything.

As part of the decals phase I also laser cut some screen bezels to go around the 2 monitors, originally in red card to test, but then from 0.8mm black plastic sheets for the final build. These were to help hide the screen edges whilst giving a nice clean outline to the displays.

Build

With the prototype and decals honed in, it was time to go for the main cabinet build. I did this all in one day. First re-cutting out all the wood pieces then glueing them together with wood glue and a glue gun (the wood glue is the permanent fix, but the glue gun gives an instance temporary fix so I don’t have to wait for stuff to dry).

At this point, I also did a bit of edge painting. The decals that I had designed in the previous phase were all made to be 1 or 2mm away from the edge. This meant I didn’t have to get them perfectly aligned. In order that you couldn’t see any raw wood edges, however, I gave all visible areas a black covering. For this, I just used an Oil based black marker. On my RombusCT build, I spend hours painting, but I found the marker approach to be a really nice and quick way to get a similar finish.

With the glue and paint dry I went ahead and assembled everything, printed out and stuck on the decals using sticker vinyl, cutting the black plastic bezels, attaching all the peripherals (speakers, fan, buttons, etc) and screwed on the legs.

Wiring

For the wiring, I started by mounting the LattePanda in the base. I’d taken care to plot the mount holes in the design files and had also created a load of vent holes beneath it to ensure good airflow when the fan is on, keeping the LattePanda cool, which is extremely important. Unfortunately the LattePanda doesn’t have any pins for external power switches so I also had to plan holes in the based for me to be able to reach a finger in to turn it on.

The LattePanda itself is mounted with some standoffs and then also has on top a custom cut acrylic panel I made to hold the audio amp and a little strip of headers to act as a power distribution area. Power will come from a 5v 4A power supply, via a panel mount barrel jack in the back, and feed into this strip where all the different peripherals can then take a spur off.

An import thing to take note of with the power lines is to make sure you use the right wiring gauge. Originally I just used some cheap jumper jerky wires from the barrel jack and then to the LattePanda and I had some serious power issues with the device constantly browning out. It wasn’t until I switched the wires for some 22AWG solid core wire (thanks Pimoroni for helping work this one out) that these issues went away. Moral of the story, don’t underestimate the need for good quality wiring.

The peripheral wiring was pretty straightforward (if a little fiddly). All buttons connected to pins on the LattePanda headers as well as to ground. The fan connected directly to the power rail. The Speakers connected to the amp, and then wire connected to a headphone jack with screw terminal ends was used to feed audio into the amp. And a custom micro USB adapter was made to feed power to the back glass monitor.

Most of the buttons used were all pretty standard buttons, with one exception, the plunger. I had originally hoped to make this actually plunge, but in the end, I decided to use a decorative metal knob to represent the plunger but made it capacitive touch instead. It wouldn’t spring, but it would perform the same function. For this, I used a cheap capacitive touch module I had lying around. Soldering on a little metal spacer I could put between the knob and the fastening screw. Truth be told, this can be a little buggy and a little over sensitive (you can actually fire a ball without touching it), but for the most part, it’s close enough.

The last thing to connect up was the monitors. For the back glass monitor, a slim HDMI cable was fed into the base and into the LattePandas HDMI socket, and the main playing field was attached to the dedicated onboard socket. The cable from the monitor though was pretty short, so I did buy an extra ribbon cable and an adapter I found on eBay to add some extra length and make it easier to be able to remove without damaging the cable.

The Software

Arduino Button Controls Setup

As I mentioned before, one of the nice things about the LattePanda is that it already comes with an Arduino Leonardo on board. This worked out perfectly for handling button controls. In order to make the buttons work with the emulator though, I would need to convert these button presses into keyboard key presses. Thankfully, the Arduino Leonardo has the ability to emulate a keyboard, so this would be perfect.

To make this happen though a sketch was needed to map the buttons to the required keys. I actually cheated a little here and repurposed the Picade sketch used for the Picade board (which is effectively an Arduino Leonardo under the hood) stripping out the bits I didn’t need and modifying the key mappings to suit my setup.

All the scripts for this project can be found in my Rombus Pinball GitHub repository which I assume is placed in a folder called RombusPinball on the LattePandas desktop. The key Arduino files are RombusPinball.h and RombusPinball.ino (there are some libraries needed in the repo as well, but I won’t explain them here, just make sure you have them sat next to the main scripts).

The RombusPinball.h file simply defines some variables to make referencing our buttons and the button state a bit easier.

#include <arduino.h>
#include <Keyboard.h>

#define RP_BUTTON_COUNT         11

#define BTN_FLIPPER_LEFT        11 // Green
#define BTN_FLIPPER_RIGHT        5 // Purple
#define BTN_MAGNA_SAVE_LEFT     12 // Yellow
#define BTN_MAGNA_SAVE_RIGHT     4 // Grey
#define BTN_START               10 // Blue
#define BTN_COIN                13 // Brown
#define BTN_PLUNGER              6 // White

// Back buttons labelled as if looking
// directly at them with the back of 
// the pinball machine facing you

#define BTN_BACK_LEFT_TOP       A0 // Blue
#define BTN_BACK_LEFT_BOTTOM    A1 // Orange
#define BTN_BACK_RIGHT_TOP      A3 // Yellow
#define BTN_BACK_RIGHT_BOTTOM   A4 // Green

typedef struct
{
  uint8_t state; // what state was the input last in ( HIGH/LOW )
  unsigned long last_change;
} button_state;

Where the RombusPinball.ino file contains the main routine for handling and mapping the button presses.

/* 
 *  Borrowed heavily from Pimoronis Picade sketch at https://github.com/pimoroni/Picade-Sketch/tree/master/Picade
 */

#include "RombusPinball.h"
#include "TimerOne.h"
#include <Keyboard.h>

#define DEBOUNCE_DELAY 25

const uint8_t rp_pins[] = { BTN_FLIPPER_LEFT, BTN_FLIPPER_RIGHT, BTN_MAGNA_SAVE_LEFT, BTN_MAGNA_SAVE_RIGHT, BTN_START, BTN_COIN, BTN_PLUNGER, BTN_BACK_LEFT_TOP, BTN_BACK_LEFT_BOTTOM, BTN_BACK_RIGHT_TOP, BTN_BACK_RIGHT_BOTTOM };
const uint8_t rp_keys[] = { KEY_LEFT_ARROW,   KEY_RIGHT_ARROW,   KEY_LEFT_CTRL,       KEY_RIGHT_CTRL,       '1',       '5',      KEY_RETURN,  'u',               'd',                  'q',                KEY_ESC               };

static button_state button_states[RP_BUTTON_COUNT] = {};

void setup() {
  Serial.begin(9600);
  //while(!Serial);
  Serial.setTimeout(100);

  // Setup pins as input with pullup resistor
  for ( uint8_t i = 0; i < RP_BUTTON_COUNT; i++ ) {
    if (rp_pins[i] == BTN_PLUNGER) {
        pinMode(rp_pins[i], INPUT);
    } else {
        pinMode(rp_pins[i], INPUT_PULLUP);
    }
  }

  Timer1.initialize(5000);
  Timer1.attachInterrupt(update);
  Timer1.start(); 
}

void update(void) {
  for (int i = 0; i < RP_BUTTON_COUNT; i++)
  {
    uint8_t state;
    state = !digitalRead(rp_pins[i]);
    if (state != button_states[i].state && (millis() - button_states[i].last_change) > DEBOUNCE_DELAY) // has this input changed state since the last time we checked?
    {
      button_states[i].state = state; // update our state map so we know what's happening with this key in future
      button_states[i].last_change = millis();
      handle_key(i, state);
    }
  }
}

bool handle_key(uint8_t i, uint8_t state) {
  uint8_t pin = rp_pins[i];
  uint8_t key = rp_keys[i];
  
  if (pin == BTN_PLUNGER) {
    state = state ? LOW : HIGH;
  }
  
  if (state) {
    Keyboard.press(key);
  } else {
    Keyboard.release(key);
  }
}

void loop() {
  // Do nothing...
}

Hopefully, it’s pretty self explanatory, but really it’s just watching for button press/releases constantly and converts them into keyboard key press/releases. The only little spanner was that the cap touch sensor was active high (which you can make active low like the rest of the buttons, but I didn’t realise till after) so we handle that explicitly and flip the HIGH and LOW values.

With the sketch written it can be uploaded via the onboard Arduino IDE and, and that’s our controller setup and we are now ready to set up the emulator.

Emulator Setup

This is the section of the blog post I’ve been somewhat dreading. From my Rombus3000 and RombusCT builds, the emulator setup was a pretty easy step using RetroPie, however, the pinball emulation scene is completely different, and is extremely fragmented. If you are going to tackle a pinball build yourself I’m afraid there is no way around it but getting on the forums and researching. From my findings there are 2 main emulators, Visual Pinball and Future Pinball, however, both of these need further mods to work with back glass displays and DMD displays (the Dot Matrix Displays which show your score etc), and then you are going to want a table selector UI to make launching the tables a nicer experience too, which again seem to be two main options. Hyperspin or Pinball X.

For my build, I decided to go with Visual Pinball and Pinball X for the front end.

Now, I’m afraid I’m not going to be able to give a step by step for this part as, frankly, it was a lot of trial and error to get everything setup and in all honesty, I don’t know which steps I took were necessary and resulted in the thing finally working as I needed so I’m just going to have to be pretty vague and just say do your homework if you want to build one for yourself.

Essentially what you want to get working is Visual Pinball with DirectB2S, which is the back glass driver. There should be a package on the VPForums that has both of these together, which should make the install a bit easier. You’ll want to make sure you have both monitors on and setup before you run the installer though as there are steps to select the monitors to use for the playing field and backglass.

You’ll also want to download some tables from the same forums, which, you’ve guessed it, isn’t exactly a one-step process either. All I’ll say though is search for “B2S” tables as these will work best with the configuration we have set up.

With at least one table installed, you should be able to test Visual Pinball and ensure the configuration is how you need it (especially the Key configuration to work without controller config) and that everything works as expected. If it doesn’t, be sure to check the forums for help.

With Visual Pinball working, you’ll then want to get Pinball X installed. Sorry to say it, but again, this isn’t the most straightforward thing in the world. You’ll want to get it installed and run through the config wizard to set it up as you like. With it set up, you then need to find and download media packs for the various tables you have installed in VP. With those installed, you’ll then be able to select them in the front end UI and this will launch Visual Pinball for you.

As I say, this is all a little vague and really oversimplifies this whole process, but I hope it at least gives you a structure in which to work from if you do want to attempt the same.

Hacks and Tweaks

At this point I was hoping everything would play nicely, however, there were I few extra things I had to tweak.

The first was that for some reason the LattePanda would occasionally wipe whatever sketch was on the Arduino when it booted up, so I ended up having to write a batch script to flash the firmware to the Arduino whenever the Pinball X UI was launched (hint, there is an option in the Pinball X setup that lets you configure a script to run before the app launches).

Secondly, I also had an issue where the Visual Pinball windows weren’t opening in the right order, and more importantly, the main playing field wasn’t being made the active window, meaning it was unresponsive until you clicked on the window with the mouse. This wasn’t going to work, so I ended up having to use an app called AutoHotKey to maintain the playing field as the active window. I also have a similar problem with the DMD window appearing behind the back glass which I have tried to fix with the same approach, however, I’m yet to get this to work so it is still a bit of a problem. To activate this script on startup, a shortcut to the script was created in the C:\Users\LattePanda\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup folder.

Both of these scripts can be found in my Rombus Pinball GitHub repository.

Conclusion

I entered into this project thinking it would be another nice emulator project, similar to what I had done before, but it was actually a lot harder working with the pinball emulators than I had expected. This ultimately forced me to make some compromises but in the end, I still think what I produced was something rather cool and special. I don’t think it can really compete with a true dedicated PC based pinball emulator with 4K displays like the pros are making, but for a casual gamer like myself, it’s a fun little thing that doesn’t require a lot of space, so it suites my needs perfectly.

Tomy Mr Money Google AIY Assistant

If you are a regular MagPi magazine subscriber, you’ll know in issue 57 they had their most popular giveaway with the Google AIY Project Voice Kit. Unfortunately, I was one of those unlucky people not to get my hands one. After seeing all the fun everyone was having on twitter though, I decided to do what I do best and make my own. Now, if you know me at all by now, you know I like my retro tech, so for this project, rather than just creating a plain old box for it I decided to repurpose another beloved 80s toy of mine, my Mr Money moneybox. The result? Hilarious :D

The Build

The Core

To start with, I carefully opened up my Mr Money money bank and removed all the insides to leave me with the basic shell. The inside surface of the base was pretty uneven, so I started by cutting a circular piece of acrylic to place on top of this to give a nice clean surface on which I could mount all the internals components.

Given the limited internal space, my only choice was going to be a Raspberry Pi Zero. Thankfully the new “W” versions have wifi built in, so this allowed me to leave the USB port free for a USB microphone.

Even using a Pi Zero, placement of the Pi was still pretty tight, so the only real option was to mount it centrally. I knew I was going to need space either side for cables and things, so this dictated that it would need to face front to back. To fix into place I used some nylon stand offs and stacked the Speaker pHat on top. On top of this, I added an extra plate to allow me to zip tie the mic to it, which I connected to the Pi via a short micro USB to USB female cable. Finally, a short right angled micro USB extension cable was fed out the back to act as a USB power connector.

The Arm

If you are familiar with the original Mr Money, you’ll know his arm was spring mounted to feed money into the money bank. Well, this seemed a prime candidate to convert into the button to trigger the AIY into action.

Looking inside, there was already an attachment to the arm socket with a little nub which triggered a switch from before (though it wasn’t permanently attached so I JB Welded it) which luckily was at just about the same height as the top plate, so re-cutting a new top plate with a “wing” created a perfect mounting position for a micro switch. I love it when things just line up :D

Whilst the arm was originally spring mounted, unfortunately the spring mechanism was part of the guts of the original toy which I had removed so I had to come up with an alternative. On inspection, there were a few nubs on the inside of the chassis and on the arm socket which I could just fit a small rubber band around so this worked great.

On testing the arm though, I found its travel was a bit too much. Before, it used to feed money into the mouth, so it needed to go pretty high, whereas now I really just wanted it to stop at body level. Looking inside the arms shoulder cover, I noticed this is where they limited this before with another nub to stop the arm traveling too far. To limit this further then, I drilled a small hole a little further in and JB Welded a small nylon shaft to act as a new nub.

With the arm sitting where I wanted it, and suitably springy, the final touch was to change the old “£” sign printed on the hand, to a question mark which I did by printing a replacement onto some sticker vinyl and cutting it out by hand.

The Head

Another key function of the original toy is the opening and closing head which I really wanted to have move as it talks.

To achieve this I chose to use a micro servo. I figured out a mounting point for the servo in the neck area on the underside of the head. To hold the servo in place I created a bracket on the laser cutter and fixed it to the head. To make the head actually move, I attached some paper clip wire from the servo horn through a small hole I made in the upper part of the head. Now when the servo moves it pushes and pulls the head open and closed.

One element I wanted to add was some kind of status LED to see what state the Pi was in. The AIY code already accommodates a status LED, so all I had to do was plumb it in. To keep it on theme, I thought I’d attached this to the head as a kind of antenna. To do this, I drilled a hole, then hot glued the LED in to place and ran wires down the next into the body compartment for wiring up later. To help limit the brightness of the LED a resistor was added in line with the LED.

Another element I liked from the original toy was the moving tongue, however I couldn’t find a way to incorporate this (did I mention space was tight?) so unfortunately I just had to cut it down and glue it in to place so I could at least keep the aesthetic.

The Speaker

As mentioned already, to allow the little guy to talk I decided to use the speaker pHat from Pimoroni. This does come with a speaker, however it isn’t the loudest so I decided to replace the speaker with another one I had. To be honest, the new speaker wasn’t much better spec’d, however I was able to wedge it against the base which made it reverberate much better when everything was closed up.

The Wiring

With all the components now in place it was just a case of wiring them up. To do this I used a Pico Hat Hacker, another Pimoroni special. So as not to interfere with the other components though I decided to mount it to the bottom of the Pi, rather than between the stacked pHat’s as is custom.

I wired the button and the servo as per the pinout used by the official Voicehat.

For the servo, I wanted to power this from the Pi itself so that I didn’t need an extra power supply. Given its only one micro servo this isn’t too much of a problem, but I wanted to put a precaution in place to try and prevent any current dips and spikes. For this I created a mini breakout board with a large capacitor bridging the power and ground lines so when the servo starts or stalls, it can pull or push a spike of current from the cap, rather than the Pi.

The Code

With all the hardware side complete, it was time to move on to the code side of things.

Basic Setup

The first thing to do is install the official AIY Project Voice Kit SD Image onto an SD card. I won’t go into detail on how to do this, but if you are unsure, you can find instructions on the official Raspberry Pi site. You’ll also want to configure a wifi connection.

Configure sound / mic

The standard AIY SD image is designed to work with the official voicehat hardware, however we are using the speaker pHat and a USB mic, so we’ll need to configure these.

First up, we’ll re-enable the default audio configuration by editing the boot config:

sudo nano /etc/boot.config

Scroll down to the bottom of the file and comment out 2 lines responsible for the google kit soundcard and enable the default driver:

dtparam=audio=on
#dtoverlay=i2s-mmap
#dtoverlay=googlevoicehat-soundcard

Next up, we’ll install the speaker pHat drivers by issuing the following command:

sudo curl -sS https://get.pimoroni.com/speakerphat | bash

Finally, we’ll make a few changes to the audio settings. The first being to disable the speaker pHats VU meter and secondly to combine the USB mic and speaker pHat driver into a single configuration. To do this, open up the audio settings config file in an editor:

sudo nano /etc/asound.conf

And then replace its contents with the following:

pcm.!default {
  type asym
  capture.pcm "mic"
  playback.pcm "speaker"
}

ctl.!default {
    type hw
    card 0
}

pcm.mic {
  type plug
  slave.pcm "hw:1,0"
}

pcm.speaker {
  type plug
  slave.pcm "softvol"
}

pcm.dmixer {
    type dmix
    ipc_key 1024
    ipc_perm 0666
    slave.pcm 'hw:0,0'
    slave {
        period_time 0
        period_size 1024
        buffer_size 8192
    }
    bindings {
        0 0
        1 1
    }
}

ctl.dmixer {
    type hw
    card 0
}

pcm.softvol {
    type softvol
    slave.pcm "dmixer"
    control {
        name "PCM"
        card 0
    }    
}

Configure AIY

Next up is to configure AIY. There quite a few steps to go through here, but thankfully Goole have done a really good job of documenting this step so rather than repeat this, I’ll just point you to the official docs and have you come back once you’re all set. Don’t worry, I’ll wait.

At this point you should have the AIY project working and responding to our commands, so now it’s time to add out little flourishes.

Configure Servo

The first of the changes is to have the head move. I decided on 3 states. The first was to open the head when you start a new question. Then it should move in a talking motion as it reads a response, and lastly it should close once again when it’s finished speaking.

To help with this, I created a small python class using RPi.GPIO to control the servo

import itertools
import logging
import threading
import time

import RPi.GPIO as GPIO

logger = logging.getLogger('mouth')

MIN_PWM_DC = 10
MAX_PWM_DC = 20

class Mouth:
    
    def __init__(self, channel):
        self.animator = threading.Thread(target=self._animate)
        self.channel = channel
        self.iterator = None
        self.running = False
        self.state = None
        self.sleep = 0

        GPIO.setup(channel, GPIO.OUT)

    def start(self):
        self.running = True
        self.animator.start()

    def stop(self):
        self.running = False
        self.animator.join()
        GPIO.output(self.channel, GPIO.LOW)

    def set_state(self, state):
        self.state = state

    def _start(self, dc):
        self.pwm = GPIO.PWM(self.channel, 100)
        self.pwm.start(dc)

    def _animate(self):
        while self.running:
            if self.state:
                if self.state == 'open':
                    self.iterator = None
                    self.sleep = 0.0
                    self._start(MAX_PWM_DC)
                    time.sleep(0.18)
                    self.pwm.stop()
                elif self.state == 'close':
                    self.iterator = None
                    self.sleep = 0.0
                    self._start(MIN_PWM_DC)
                    time.sleep(0.18)
                    self.pwm.stop()
                elif self.state == 'talk':
                    self.iterator = itertools.cycle([MIN_PWM_DC, MAX_PWM_DC])
                    self.sleep = 0.18
                    self._start(MIN_PWM_DC)
                else:
                    logger.warning("unsupported state: %s", self.state)
                self.state = None
            if self.iterator:
                self.pwm.ChangeDutyCycle(next(self.iterator))
                time.sleep(self.sleep)
            else:
                time.sleep(1)

def main():
    logging.basicConfig(
        level=logging.INFO,
        format="[%(asctime)s] %(levelname)s:%(name)s:%(message)s"
    )

    mouth = None
    try:
        GPIO.setmode(GPIO.BCM)

        mouth = Mouth(12)
        mouth.start()
        while True:
            try:
                state = input()
                if not state:
                    continue
                mouth.set_state(state)
            except EOFError:
                time.sleep(1)
    except KeyboardInterrupt:
        pass
    finally:
        mouth.stop()
        GPIO.cleanup()

if __name__ == '__main__':
    main()

There is quite a bit going on here, but mostly I just copied from the existing LED.py and adjusted it for my needs. One thing I did find during this, was that the servo would jitter quite a bit when holding a position, so to get around this, I just disengage the servo whenever it stops a motion. Thankfully the head is stiff enough that it holds its position, so this is not a problem.

I saved this code in a Mouth.py file in a folder ~/voice-recognizer-raspi/src/classes along with a blank file __init__.py which allows the class to be imported into other python code in the same folder.

With this saved, it was now a case of importing it in the Main.py file, and incorporating it into the main code base.

In the includes section at the top, add an include to our file and one for the RPi.GPIO class.

from classes.mouth import Mouth
import RPi.GPIO as GPIO

Then in the main function, setup an instance of the mouth class

GPIO.setmode(GPIO.BSM)
mouth = Mouth(12)
mouth.start()

Now, we need to get our mouth instance to the SyncMicRecognizer class so we need to pass it through the do_recognition method first. To do this, just add a mouth param to the method declaration and one to the constructor of SyncMicRecognizer and pass the mouth instance through when the method is called and when the class is instantiated.

Within the SyncMicRecognizer class, we’ll make a few changes to trigger our mouth actions. First off, update the _status method like so:

def _status(self, status):
    if self.led_fifo:
      with open(self.led_fifo, 'w') as led:
        led.write(status + '\n')
    logger.info('%s...', status)

    if status == 'listening':
      self.mouth.set_state('open')
    elif status == 'ready':
      self.mouth.set_state('close')

The start is the standard code, it’s just the bottom bit that we’ve add to trigger out mouth to open and close.

Next, we will trigger the talking action by editing the _handle_result method and setting the talk state whenever we start to play a sound

def _handle_result(self, result):
  if result.transcript and self.actor.handle(result.transcript):
    logger.info('handled local command: %s', result.transcript)
  elif result.response_audio:
    self.mouth.set_state('talk')
    self._play_assistant_response(result.response_audio)
  elif result.transcript:
    logger.warning('%r was not handled', result.transcript)
    self.mouth.set_state('talk')
    self.say(_("I don’t know how to answer that."))
  else:
    logger.warning('no command recognized')
    self.mouth.set_state('talk')
    self.say(_("Could you try that again?"))

With this, we should now have our head moving whenever we ask our little guy a question.

NOTE At this point, I started seeing a segmentation fault. To be honest, I don’t know what was causing this, but the following command seemed to fix it:

ulimit -s 32768

Change Voice Pitch

The last thing I wanted to do was make the voice fit our little robot. For me, the default Google voice wasn’t quite cute enough. To achieve this I decided to swap out the default player used by AIY for SOX, a robust sound processing program that supports a bunch of filters, including pitch shifting.

First up you’ll want to install sox

sudo apt-get install sox

Then we’ll modify the Player class inside the audio.py file to use the sox player rather than aplay. In this class, change the cmd array to the following:

cmd = [
  'play',
  '-q',
  '-t', 'raw',
  '-c', '1',
  '-e', 'signed',
  '-b', sample_width_to_sox_string(sample_width),
  '-r', str(sample_rate),
  '-',
  'pitch', '800'
]

We’ll also need to add a custom sample_width_to_sox_string helper method to the top of the file:

def sample_width_to_sox_string(sample_width):
  """Convert sample width (bytes) to SOX format string."""
  return {1: '8', 2: '16', 4: '32'}[sample_width]

And with that, we should have our little guy speaking in a much more suitable pitch for it’s size :)

Conclusion

This was actually meant to be a pretty quick build for me, but it ended up being quite a big mod, however I’m really happy with the result and I’m glad I persisted with the requirements of the moving head and pitch shifted voice.

Full disclosure I actually started this project a while ago, and got stumped with the code and only just finished it recently. Unfortunately the AIY code has changed a bit since I started so some of the steps might not be immediately copy and paste-able, but I hope it at least gives you a good starting point if you do end up trying it for yourself.

Tomy Turnin' Turbo Dashboard Outrun Arcade

If you’ve read RombusCT, Rombus3000 blog posts or follow me on twitter, you’ll probably know I love nothing more than hacking some retro tech, preferably from the 80s, and upgrading it with today’s technology to make something that might have existed if only the original designers/engineers had access to the same tech we do. Well, I’ve only gone and done it again, and this times it’s with the Tomy Turnin’ Turbo Dashboard, an old 80s desktop motorized driving game which I’ve converted into a suitably retro Outrun arcade.

Some key features are an integrated 3.5” TFT screen, fully usable steering wheel and gear shifter (dashboard turbo light comes on when in high gear), working ignition key for power, true MPH speed and rev counter displays, and a fuel gauge to represent the stage time remaining.

The project itself was quite a big one for me, filled with several moments of frustration, from burnt out potentiometers, to soldering LEDs backwards, multiple TFT screen purchases and more than one change in direction as approaches to problems were found to be inadequate.

What’s documented in this post then are the final approaches I took (or at least the best I can recall as some times I’m sure I was just trying anything :)).

Inputs

When hacking hardware, I usually follow a pretty simple rule, “keep it as stock looking as possible”, so the first constraint I set myself was I must use only the existing inputs, so I’d have to use the shifter, steering wheel, ignition and push button that came with the toy. Sounds simple, but these became a real challenge.

Gear Shifter

The first input I tackled was the gear shifter. Looking at the normal outrun arcades, these tend to have a 2 position shifter for high and low gear. Given the lack of an accelerator in the toy though, I decided to go with a 3 position setup, low, neutral and high for which I’ll later handle acceleration in code such that being in low or high gear will automatically apply the accelerator and being in neutral will apply the brakes.

Given this approach, I decided I’d use two switches to detect low / high gear and would also incorporate a self centering mechanism to automatically spring back to neutral if you let go of the shifter.

For the switches, I examined the areas around the shifters internals to see where I could mount them, and found I could fit some just to one side of the shifter. I also noticed that the shifter stick itself had a nice cutout which would be perfect to insert a 3D printed actuator, so this is exactly what I did. A bit of hot glue and JB weld later and the switches and actuator were mounted.

For the self centering mechanism, I kept a gear with a rod attachment from the original internals which sweeps side to side as the gear shifter is moved, and rigged up some rubber bands around some nearby internal structures to force it to stay centred, which in turn forces the shifter to stay centred. A bit Heath Robinson, but nobody is going to see it, so hey :)

Steering

The next tricky one to solve was the steering. Again reviewing the original mechanism, the end of the steering wheel already had an actuator that converts the rotary motion of the wheel into a linear, side to side motion. The first thought that came to mind then was to use a slide potentiometer with a custom head to match the original connector. Well, a custom PCB, a Digikey order of a few different sized slide pots, and some 3D printed tinkering and the approach worked :)

The PCB isn’t anything fancy, it just mounts to a plate that was already in the toy, and exposes the three pins of the potentiometer that get hooked up to the controller board later on.

Ignition (Power)

For the ignition, I really wanted this to be the power switch, so turning the ignition on turns on the Pi, and turning it off powers everything down. Any good Raspberry Pi developer knows you can’t just turn a Raspberry Pi off, but thankfully I’d recently purchased a few PowerBlock PCB’s which handled the powering up / down for you, so I simply used one of these and connected the wires that already came from the ignition key.

A couple of mods I did make to the PowerBlock though were to replace the upright pins with some right angled ones as I knew I was going to be stacking other components above it, so wanted to save some vertical space, and I also knew I was going to need access to the I2C pins which the PowerBlock connects to, as well as access to the 5/3v pins, so I modified a Pimoroni Pico HAT Hacker (making sure not to break any traces I’d need) and soldered it to the head of the pin tops on the PowerBlock PCB.

Another nice feature of the PowerBlock is the ability to attach an indicator LED, so for this I (carefully) drilled a hole in the chassis next to the ignition key and mounted a 3mm LED with hot glue.

Lastly, I needed to decide on an actual power source. I did consider battery power, but this was going to take up too much space, so I just went with an approach I’ve done in my other projects which is to use a panel mount barrel jack. This is really the only non original modification, however using panel mount components makes for a really clean solution, so I was happy to go with it. For the power source I use a 5V DC 2A power supply, so it should suffice for all my power needs and saves the need of any converters.

Start Button

The only really “push button” element from the original toy was a reset button for a counter, but again, this was all rather mechanical. From an arcade point of view, the only thing we’ll need a button for is to start the game (we’ll configure the game later to be freeplay so we don’t need a coin button) so this seemed a perfect candidate.

For this, I had some large tactile switches lying around already from another project so mounted one of these on some scrap prototyping board, cut it to size and mounted it roughly inline with the original hole. I 3D printed a block to act as a mounting wall, but really it was just a case of using something to keep it straight and provide something to glue against.

For the button, I used my dremel to cut it down to size and then JB welded it to a button cap that attaches perfectly to the tactile switch. It doesn’t really have much “in/out” movement, but it does the job. Wires were attached to the board and then linked up to the controller.

Volume Controls

I knew I was going to be adding a speaker, but I also knew I’d probably want to be able to control the volume of this, so volume controls were going to be needed. I really didn’t want to add any extra external controls for these to ruin the aesthetic, so instead I decided to mount them inside the old battery compartment out of the way. As they wouldn’t be seen, they didn’t need to beautiful, so I just soldered some small tactile switches to some spare perf board and hot glued them to the inside of the compartment.

Controller (Picade PCB)

The final element for the inputs was some kind of controller. As I’d used one before in both the Rombus3000 and the RombusCT, Rombus3000, a Picade PCB seemed the perfect candidate. Another positive for this was that it was already capable of handling analog joysticks, so with a bit of config, should just work with my steering wheel potentiometer no problem, as well has having an onboard amplifier for the speaker.

To configure the board, I downloaded the latest firmware update, and from the Pi, flashed it to the PCB (see the github repo for instructions). I kept the default keyboard configuration, but just changed the single “Up” connector to be a joystick input by issuing the serial command b 0 249 followed by an s command to save this to the EPROM.

The last thing to do was to wire everything up, which was pretty much standard. The only other thing I had to do was feed some 5v + GND wires to the steering wheel potentiometer. For this, I just pulled them off the ISP pins on top of the Picade board.

Outputs

With the inputs sorted, it was time to start thinking of how to actually display things. I knew I was going to need a screen for the actual game play, but I also really wanted to make the dashboard display interactive as this was such a key element of the original game.

Screen

In the end, I think I tried 3 or 4 different screen configurations, looking for the ideal setup. I was hoping to find something that would fit into the original bezel, but I ended up having to make a custom one instead as the screen was a little smaller than the cutout.

The screen I ended up going for was a KeDei 3.5” HDMI display (also sold under the brand KOOKYE). The HDMI element was really important, as I had tried a monitor that connected to the GPIO, but this just used up all the GPIO pins, which I knew I’d need, and on testing with the game later on, caused serious lag, so it had to be HDMI.

With the screen chosen, I then went about creating the mount and bezel. Starting with the bezel, I did a bunch of trial and error laser cuts out of black acrylic till I got something that fit snugly in the original space. The black acrylic ended up being a bit too shiny, so I sanded it down a bit to give it more of a matt look.

With the bezel mounted, then next tricky part was mounting the screen. To help with this, I 3D printed a thin surround to fit round the edge of the screen which I could glue into place without damaging the screen and then just use a few small dabs of hot glue on the back of the screen to hold it into place. It took some adjusting to get everything lined up perfectly, but it was really just patience and several attempts until it was spot on.

For the wiring, I purchased as low profile and flexible a HDMI cable as I could find, and for power, used a couple of jumper cables to take 5V and GND from the Pi’s GPIO header, and just hot glued those into place to make sure they wouldn’t fall out.

Dashboard

The dashboard was quite a tricky element. My initial hope was to reuse the original display and just mount some LEDs into the original cutouts. I designed and had manufactured a PCB for this, but on build, I realised it just wasn’t going to be good enough. There was also limitations in the speedo area as well as the original toy didn’t have cutouts for all the 7 seg segments of the speed display, so in the end, I decided to go with a full on custom display, but in the graphical style of the original.

The first PCB I had manufactured used SMD components, and during that test I found my SMD soldering skills weren’t great, so for this second attempt I knew I’d want to use through hole components. With that in mind, I did some DigiKey searching and found some rectangular LEDs and some right sized 7 Seg displays and went about updating the PCB design around those. The 7 Segs I got were actually a grey colour, but I wanted them black so I stuck some windscreen tinting film I’d purchased a sample of from ebay to the fronts and this worked out great.

For the LED controller, I already had an Adafruit IS31FL3731 charlieplexing LED driver board lying around, which was capable of driving more LEDs than I would need, so I went with that as the driver.

With the PCB designed and sent off to Dirty PCBs to be manufactured, I then focused on the dashboard decal. This was done in inkscape and I just transfered as much of the original design as I could making sure to line everything up with the PCB design being manufactured.

I printed the design out onto some vinyl sticker material I had purchased, cut out all the holes with a scalpel and then sandwiched it between 2 layers of clear acrylic, which also has cutouts for all the electrical components, which I’d cut out on my laser cutter.

When the PCB arrived, I soldered everything up, and did some testing with the Adafruit arduino driver, and it all worked just how I wanted it (well, maybe not first time, but this post is already getting pretty long :)).

To mount, I used the original dashboard mounting holes and screws, but then also hot glued the edges to make sure it wasn’t going to go anywhere.

Sound

The final output I would need would be a speaker. Outrun has some pretty nostalgic music so I knew I’d want to keep this element.

The speaker I used was one that I had pulled out of some other old toy a while back, and installation was just a case of finding a place it could mount easily. I ended up mounting it inside the old battery compartment, drilling a few holes to let the sound through.

Lastly, I wired the speaker up to the Picade PCBs speaker terminals, and that was that job done.

Construction

With all the inputs and outputs setup, the final hardware element to finish was the mounting of the Raspberry Pi itself, and any final wiring. There isn’t any rocket science here, I just chose the biggest clear space, and mounted everything in that spot stacked via some nylon standoffs, and being sure to leave enough room around the edge to make all the connections I would need.

Code

Now that all the hardware hacking was done, it was now time to move on to the code side of things.

Screen config

First up was getting the screen working. Being HDMI, I was hoping it would just be plug and play, which wasn’t the case, but thankfully it didn’t take much config to get going. All that was needed was to add the following to /boot/config.txt

disable_hdmi_overscan=1
hdmi_force_hotplug=1
hdmi_group=2
hdmi_mode=87
hdmi_cvt=480 320 60 6 0 0 0

There is a great review of this screen, including an explanation of this config over on the Raspberry Pi Forums, so if you want to know what all this means, be sure to check it out.

The screen is capable of supporting touch screen, but as this wasn’t really needed for this project, I just didn’t bother setting it up and moved on to the next item.

Sound config

For sound config I needed to do two things. Force analog audio out of the audio jack and merge the audio into a single mono track as I was only using a single speaker.

To force analog audio out, I issued the following commands at the terminal

sudo modprobe snd_bcm2835
sudo amixer cset numid=3 1 # Force analog

And then to merge the sound channels, I modified /etc/asound.conf with the following configuration

pcm.card0 {
  type hw
  card 0
}

ctl.card0 {
  type hw
  card 0
}

pcm.monocard {
  slave.pcm card0
  slave.channels 2
  type route
  ttable {
    # Copy both input channels to ouput channel 0 (left)
    0.0 0.5
    1.0 0.5
    # Send nothing to output channel 1 (right)
    0.1 0
    1.1 0
  }
}

ctl.monocard {
  type hw
  card 0
}

pcm.!default monocard

To make these changes take effect, you can either reboot, or just issue the command

sudo /etc/init.d/alsa-utils restart

Cannonball setup

Next up was the actual game itself. Given I knew I would want to read out some game variables such as speed, revs, turbo and time remaining etc, it was clear I was going to need something I could get pretty low level with. Thankfully I had already come across Cannonball, which is a C++ port of the original Outrun game and was capable of running on a Pi so I knew this would be my best option.

To get Cannonball running on the Pi, it boils down to the following commands

sudo apt-get update -y
sudo apt-get install -y cmake libboost-dev libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev libsdl2-mixer-dev
git clone https://github.com/djyt/cannonball.git
cd cannonball
mkdir build
cd build
cmake -G "Unix Makefiles" -DTARGET=sdl2_rpi ../cmake
make
ln -s ../roms roms

There is quite a bit going on here, but ultimately it’s just installing the needed libraries and setting up the cannonball build process. Cannonball makes use of the original MAME roms for the sprites so you’ll need to “acquire” these and drop them in the ROMs folder. With this setup, you can issue the command ./cannonball to start cannonball playing.

One thing you’ll probably want to do is enable OpenGL via raspi-config as without it the gameplay can be pretty laggy. I found the “Fake” option to be the best result.

With the game working it was now a case of exploring the code base and working out where certain things happen. This was quite time consuming and a lot to explain so I’ll just summarise here, but if you want to see all the changes I made, you can find them in my github repository here.

Basically though I hid all the graphic elements I was now going to be showing on the dashboard, and moved some other graphic elements around, such as the score and timer at the top of the screen.

Interfacing Cannonball with the Dashboard

With the game up and running, it was now time to get it talking to the dashboard.

For this I would use WiringPi and custom Dashboard class I wrote. Again, explaining all of the Dashboard code would be a bit too much for this post, but in summary I ported the Arduino code for the Adafruit IS31F3731 library to work on the Raspberry Pi by using WiringPi to do the I2C communications. Additional methods to interface with the individual dashboard elements were added, such as updateTacho, updateFuel, updateSpeed etc, and then the game code was updated to call these methods in place of the previously commented out code that would draw them to the game screen, making the dashboard update with the actual real time game values.

Note: Because cannonball is written in C++ this made this all a lot easier, but if you were using some other games engine, you could potentially inspect register values to pull out the different variables.

To incorporate our custom Dashboard class into the Cannonball codebase, I had to first install WiringPi (see instructions here, you’ll want to make sure you have it installed from src, so uninstall / reinstall if you don’t) then I had to update both the sdl2_rpi.cmake file to include the WiringPi dependency, as well as the CMakeList.txt file to include the new Dashboard C++ class files.

With those updated, you then need to re-generate your build files by navigating to ~/cannonball/build and re-issuing the commands

cmake -G "Unix Makefiles" -DTARGET=sdl2_rpi ../cmake

Now when you build the cannonball codebase using the make command, our custom Dashboard class and WiringPi will be fully compiled into the actual game executable and run as part of the game.

With all the code updates made, and our solution built, we can fire up the game to test everything by issuing the command

./cannonball

Now as we play the game, the revs, speed, time remaining (fuel) and turbo mode should all update our dashboard.

The last thing to do with Cannonball, was to make some configuration changes in config.xml file such as enabling fullscreen mode, enabling freeplay mode, setting the frame rate, disabling the menu system and enabling our analog steering wheel controller.

Autostart Cannonball

With cannonball working, we want to make it so that the game automatically starts when the system boots up. To do this I created a run.sh file in the ~/cannonball folder containing the following

#!/bin/bash
cd /home/pi/cannonball/build
./cannonball

Made it executable by issuing the command

chmod +x run.sh

Then added it to the desktop autostart file ~/.config/lxsession/LXDE-pi/autostart

@lxterminal -e “/home/pi/cannonball/run.sh”

Now whenever the Pi reboots, our game will automatically start.

PowerBlock config

Finally, the last thing to configure is the PowerBlock module so that our Pi safely shuts down when the ignition key is turned off. This is as simple as issuing the command

wget -O - https://raw.githubusercontent.com/petrockblog/PowerBlock/master/install.sh | sudo bash

Finishing Touches

With all the hardware and software done, the last things to do were just the finishing touches, which in this case was some custom decals. For these I designed them in inkscape again and printed them out on some printable sticker vinyl. Some careful scissor work later, and even more careful sticker placement and we were done!

Conclusion

If you actually read all of this post, then I salute you :) It was a pretty epic build, and a hard one to document so if I’ve skimmed over anything, be sure to leave a question in the comments. Overall though, it was a really challenging build, but I’m super happy with the result.

UPDATE: 2017-09-23

Thanks to feedback from a few people on twitter, I got reminded of a major feature that was present in the original Outrun arcades but that I had missed in my build, which was of course haptic feedback. Well, I couldn’t live knowing I was missing such an awesome feature, so I went ahead and added it in :)

For this I’m using a vibrating mini motor disc which I have attached to the underside of the steering wheel and routed the wires through the steering wheel shaft to the inside of the cabinet. There I attach them to an Adafruit DRV2605L Haptic Motor Controller which I have soldered directly on top of the IS31FL3731 boards I2C pins.

Cannonball already had an outputs class for haptic feedback, but I pretty much cleared that out and started fresh for my implementation. Similar to how I did the dashboard, I started by porting the Adafruit DRV2605L Arduino Driver to work on the Pi, then tweaked it to trigger different vibrations during the game (see my cannonball repo on GitHub for the full code). Currently I have it configured to vibrate when the car skids, goes off road or crashes.

All in all, I’m really glad I added this feature as it really does add an extra dimension and takes it to another level.