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.

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.