Monday, August 22, 2016

Cubey: Arduino-based Video Game Console


I've just finished (mostly) a new video game project: It's an Arduino-based video game console, and I've nick-named it 'Cubey'.

Cubey plugs into a TV via a set of RCA cables to provide a low-res, black and white video game experience roughly equivalent to video game consoles of the late 1970s and early 1980s. It has a custom 3D printed case, is compatible with the Nintendo nunchuk controller (supporting two players) and currently features four built-in games (I'm in the process of working on more). On the inside Cubey uses an Arduino Mega 2560. The Mega is used to input data from the nunchuk controllers via I2C, run game logic and drive an analogue PAL video signal with two colours (black and white) at resolution of 120 by 96 pixels and a mono-audio signal with four channel sound/music.



Initial Design:

About two years ago I had been playing around with the awesome Arduino library 'TV-out' using an Arduino UNO. I was using the UNO to run an implementation of the game 'snake' using two digital input pins and two momentary buttons to turn the snake left or right. 'TV-out' provides a bunch of basic functions for generating PAL video signals using interrupts on the Arduino and a pair of digital output pins to construct a two colour video signal via a 2-bit DAC (the library documentation page provides a really good explanation on putting this together using a pair of resistors). I hadn't gone any further with this project, so I picked it back up recently and decided to make something a bit more sophisticated. I was inspired by and drew heaps of ideas from a bunch of other Arduino-based video game projects including the HackVision, The Box and Gamebuino. I wanted to make something that would support multiple games and at least two players. To reduce the complexity of the project, I decided to make the console compatible with an existing game controller, rather than build my own. I found this example for using the Nintendo nunchuk controller (an accessory that plugs into the Nintendo wii-mote) with Arduino and thought this would work perfectly. The nunchuk has two buttons, an analogue joystick and three-axis accelerometers all packaged up in something you can buy for about $5 to $7 (aftermarket models).


I started off by building a breadboarded version of the console to systematically test out the various things I wanted to get running. I started off by connecting up my UNO to an RCA output jack using a 2-pin DAC with a pair of resistors and did a test run of the TV-out library: all working fine. I downloaded a library for communicating with the nunchuk controller via I2C here and compiled this into a project to read joystick state and move a pixel around the screen: all working fine, everything compatible.

Getting two-player control to work:

I really wanted to have at least one more controller hooked in so that I could run two player games. Problem with I2C is that there is only one set of pins on the Arduino for this. Normally this would be OK: multiple I2C devices can be slaved on one line as long as they have different addresses. Unfortunately all nunchuks use the same 0x52 address, so I found I couldn't talk to two simultaneously on the same line. One option was to run a software I2C for one of the nunchuks, but this didn't seem ideal. I found this solution which connects two nunchuks to the I2C clock line (SCL) and uses a pair of transistor switches to toggle the data line (SDA) using two additional digital pins on the Arduino. I wrote up a test sketch that basically switches on the first SDA line (nunchuk A) and switches off the second line (nunchuk B) before grabbing data (nunchuk A), then reverses the switches and grabs data again (nunchuk B). Breadboarded it up and it worked perfectly! I had a couple of npn type transistors sitting around from a previous project ... probably a bit over spec'd for this, but they work! I used 1kohm resistors to connect the transistor bases to the digital output pins from the micro. Voila: two player control with two nunchuks.

So at this point I was thinking "why not put in another nunchuk for even more players?" I guess I am only limited by how many unused digital output pins I have (twelve in total on the UNO). I think the main thing stopping me here was (a) making games on a 120 x 96 pixel screen with only two colours (to specify what player is which) for more than two players is hard (b) I only own 3 nunchuks anyway (c) the likelihood of getting more than four people together to test it out seemed low ... plus the nunchuk cables are quite short, so it would start to get a bit squashy sitting around playing with that many people in close proximity (actually, I guess this could potentially enhance the 'social' experience of playing together :) ).

Initial software tests and switching over to the Mega:

I thought it prudent at this point to try and get some game code running with what I had working so far, so I started implementing a clone of the game Missile Command (a popular Atari title from 1980). Problems began. Everytime I would do any relatively larger drawing to the screen (using TV-out library functions "bitmap" or "print"), I would lose I2C connectivity with the player two nunchuk, requiring a power cycle. Eventually I found I could sort of overcome this by adding in "delay(1)" call in-between any graphics intensive call.

About halfway through programming the game, I hit a wall: running out of RAM. The Arduino IDE indicates the amount of memory being used by global variables during compilation. I was lulled into a false sense of security when I saw that my program was only using about 30-40% of available memory on global variables. Then I realised that I was not accounting for memory taken up by local variables/functions on the stack; my sketch was compiling fine but wasn't running, presumably because of stack overflow ... so I was about half way into the code for my first game and already running out of memory. This seems to confirm what I was seeing. I guess the old ATmega328 (chip inside the UNO with 2K of RAM) wasn't going to cut it. OK, so I wasn't really being super memory-conscious when writing the code, but I wanted more than one game up and running and I didn't really want to have to be skimpy with my game design, so I made the choice to switch over to the Arduino Mega (with 8K RAM), because I have two sitting around not doing anything at the moment.

3D printed case design:

I played around with a lot of ideas for the case. I had a look around on Thingiverse hoping to find a cool 3D model I could slice an enclosure into, but didn't see exactly what I wanted. I kind of had the idea that it would be cool to make the enclosure a giant dodecahedron, but it seemed like a fair bit of wasted space seeing as the mega is quite long on one axis only. At some point I was going to make a space invader shaped case, but it had to be pretty big to fit things properly, so I abandoned it. I ended up doing some sketches of some tetromino-inspired shapes, and settled on a little thing built out of cubes. I nicknamed him 'Cubey'.


I built him up in Openscad. He's broken up into a few sections so I could print him without too many supports: it was a real pain picking off the support material from my last print project (in fact most of it is still stuck on the case because it was just too damn hard to get off). I made ports for the two nunchuks, RCA cables and power and usb from the Mega. His mouth is a reset switch for the Mega and, because I had so many available ports on the Mega, I added some leds in his eyes for some blinky fun :). I printed him mostly in white except for some small detailing around the eyes which were printed in black as separate pieces and then super glued onto the final design.


Problems with the case: I had a few issues with the case after printing. Firstly, I had real problems getting the raft off the main body. There were sections that were literally fused into the case, and at some point I had to abandon a 1.5 hour effort to cut them off. I ended up filing it down as best I could. Unfortunately this interfered with the sockets for where the back legs were originally designed to fit into, so rather than reprint the piece (seven hour print and probably face the same issue), I redesigned the clearance on the leg parts before printing them. I also realised that the holes and corresponding plugs didn't fit (the tolerance on the size of these bits wasn't as tight as I thought it would be), so needed a reprint anyway! Other problems with the case: I had only allowed for a 1 mm tolerance for the space to fit the mega: not enough apparently, so I ended up breaking off the support that would have held the reset switch in place and improvising. I ended up cutting a small strip of perfboard and soldered the switch to that before sticking it in the small gap between the Mega and Mega protoboard (which was to hold all of the electrical components for nunchuk switching, the DAC etc.), held in place with a little bit of bluetac :). I redesigned the nose button so it was about 5 mm thicker at the back to compensate for the extra reach. One thing that did work was the support for the RCA cables: clipped together like dream (after a tiny bit of filing :) ). I had to print the legs a number of times due to issues with tolerances on 3D printed parts connecting together and bits in the nunchuk connector spaces that didn't fit right. Eventually they were printed such that they fit with a bit of give and I used superglue to connect them to the main body.

Putting Cubey together:


On top of the Mega I used a protoboard (especially designed to fit and line up exactly with the Mega) to hold additional electronic components. This provided space for the resistors made up the DAC and that connected up the digital pins for driving the video and audio signals to the RCA cable connectors, resistors for the led eyes and the transistor circuits. I used a female-to-female RCA adaptor to connect the lines from the board to an external plug. I got two nunchuk connector adaptors (Nunchukies) to use in my controller ports. These came with convenient "wings" at the side of the board to help click and hold the nunchuk connector into place, but I found they got in the road of my design, so I hacksawed them off and made my own indentations built into the 3D printed controller port. I screwed these boards into the cavities of Cubey's front legs such that they sat flush with the controller port/socket. I then soldered the power and data lines from these up to the Mega protoboard where they were interfaced to the Mega. The front and back legs were then super glued on and the top case just clicks into place (so I can open it up again to if I needed to).


Getting some music:

I had tested the audio through RCA jack when I setup my original breadboard model, working great with 470ohm resistor in line with pin 10, and was able to generate a single channel of sound: a square wave generated via an implementation of Arduino's 'Tone' function built into the TV-out library. I wasn't very satisfied with the ability to only play a single audio tone at a time: I wanted cool video game music and any decent music needs harmony and melody, so I wanted to have some level of polyphonic sound. Reading a bit about how the Arduino built-in tone library works, I came upon this library which seemed to be exactly what I wanted. I ported over the functionality described within to use Timer 3 on the Mega as a counter for an interrupt routine that would flip the output to produce a square wave. Everything seemed to work fine when playing tones on their own, however, when this was run alongside the video signal generation using the TV-out library, it was producing a nasty clicking mess. From what I can gather, the interrupts being driven for video signal generation were messing with the timing for the tone interrupts.

I found this library for running PWM signals using different timers on the Arduino, without the need for an explicit interrupt routine to switch the output, but instead using a PWM mode of the timers and a register value to store the direction in which to flip. I ported over this implementation, and it worked perfectly: I now had two simultaneous square wave tones that I could play. I ended up implementing an additional (third) channels using this method on Timer 4 on the Arduino such that I had three square wave generators (three simultaneous tones at any one time) and no interference with first audio channel or video signal generation. I found that I could vary the duty cycle to 25% on one of the three channels to produce a slightly different timbre of sound such that there was a bit of audible distinction between the different channels/voices in the music.

Good music requires some sort of beat or percussion, so I used my last available timer on the Mega (timer 5: Timer 0 is used for game logic timing and Timer 1 is used by the video signal generation) to do this. I used Timer 5 to drive an interupt that would randomly assign a high or low value at a fixed frequency to a fourth channel in order to create a white noise signal that would emulate the sound of a drum or other percussive instrument. I found this page on using a Fibonacci Linear Feedback Shift Register as an efficient means of generating a pseudo-random signal for the noise. I found I could emulate an acceptable kick-drum by running this routine at ~1kHz and an acceptable snare at ~5kHz. Since the video signal generation interfered with these interrupt-driven signals, I had to make the sounds limited to a duration of 20ms (inside the time between video updates), which was fine for a basic beat.

Now that I had the ability to generate three square wave tones and one percussive sound simultaneously, I was ready to make some music. I wrote a routine that would read in arrays of data containing pitch and note duration information for each channel and play them at a fixed speed of 50 Hz (in line with the video signal). I found I could store music data in the flash memory on board the Mega (256K available, also used to store image and sprite data) and read it in as it was played.

At some point I will upload my updated version of the TV-out library containing these extra functions (and my game code): stay tuned!

Games:


So far I've written four games: 'Space Blast': a two player space shooter, 'Missile Command': a clone of the popular Atari title, 'Breakout': another clone of an old Atari game, and 'Achtung!', a clone of an old DOS PC game called "Achtung, Die Kurve!". I'm currently working on a few more: I'm keen on writing something that can exploit the accelerometer/motion sensing on the nunchuk in a cool way, but haven't quite got a good game concept working yet. Work in progress!

Overall, I've had a lot of fun with this project, however it did take a decent amount of time (about 6-8 weeks total). In the end I was super hyped about getting the four channel sound to work: I learnt a lot about how old video games made the sorts of sounds they did and how composers had to work within these constraints, which I've found really interesting. I may end up trying to expand on this in a future project, perhaps some sort of Arduino-based chiptune player project. Although the black and white video was sufficient for simple games, it probably would have been nice to try and get a few more shades of gray in (or even colour signals!), however it was a bit outside of the scope of my knowledge: if I did a follow-on to this project, this is something that I would definitely attempt.

No comments:

Post a Comment