An arduino-based Atari-joystick-controlled Lite-Brite project

 Questions or comments, email joetcochran AT gmail DOT com

The Idea

As a kid, I really enjoyed the Lite-Brite. I was fascinated that you could take a piece of black construction paper and follow some pre-printed instructions and make a picture by following those directions. I also liked color-by-number activity books for the same reason. More recently I found PixelHobby a relaxing diversion (although they could probably stand for some better marketing. While you can use their downloadable software to order the pixels for any photo you'd like, their "samples" are so cheesy that you half-expect to see a tacky unicorn/rainbow/wolf/native american amalgamation among their "kits" to purchase).

So, as a budding arduino enthusiast with one successful project under my belt (write-up and site), I've been trying to find another way to revive some 80's technologies from their deep slumber. Part of my Sketchduino project involved interfacing with an Atari 2600 joystick.

I came across this video and this video from the arduino forums and thought that they could be combined with an atari joystick instead of a touchscreen. The 8x8 green LED matrix could be replaced with a RGB Matrix, and pressing the joystick button could cycle the LED through its eight available colors (Off, R, G, B, RG, RB, BG, RGB).

Later down the road, I think could get more sophisticated with color selection, depending on how long the button is depressed. This would give much more than 8 discrete color choices.

LED Matrix

I decided to use the 8x8 LED RGB Matrix from LEDSee, since it has a few good resources out there for how to drive it.

A bit intimidating. 64 RGB LEDs. 32 pins. Not sure how to drive these yet. It looked like someone from the arduino forums ("madworm") had built their own driver PCBs for them. But I ended up getting the Rainbowduino from SeeedStudio.

Controller (Rainbowduino)

The Rainbowduinos were on backorder for a long time, plus shipping from Hong Kong always seems to take a while, so the project got stalled for a bit. But in June 2009, they made a batch of about 150. I promptly ordered one and anxiously awaited its arrival.

The Rainbowduino arrived a couple weeks later and I hooked it up to power (5V) and ground from the arduino. After playing poking at the power bus switch on the Rainbowduino and plugging the LED Matrix into it, I got the lovely test pattern (below). Let me say, this thing is pretty bright.

(This is the power bus switch)

(In a well-lit work area) (In an unlit room) (With a piece of

thick cardstock on top)

So, we've got the LED Matrix, which is plugged into the Rainbowduino. The next step was to get the Rainbowduino plugged into my arduino, so that the arduino could send some commands. So, the high level data flow is this:

Setting up the programming environment

The Rainbowduino firmware comes with some basic functionality that let you do a limited number of things:

  1. Display a character (A-Z, a-z, 0-9) in any single color

  2. Display a predefined pattern, which is loaded into Rainbowduino memory

  3. Display all 64 dots as the same color

Since the Rainbowduino comes out-of-the-box with such limited capability, you pretty much have to invest in a little device called a UartSB. This is a USB-to-Serial converter that makes it possible to change the firmware on the Rainbowduino.

 

The Rainbowduino documentation itself says:

"Though you can control Rainbowduino by I2C directly with default sketch, there is obvious need for rewriting the program. To do this, you may use any UART TTL adapter, e,g. UartSB v2.1, USB-Uart cable, Serial-TTL cable..."

Hmmm. Can you say Cross-Sell?

In the download package of sample sketches, Seeedstudio was also kind enough to include a sketch called Seeedmaster (which is what you load on your arduino to control the Rainbowduino). However, a huge oversight in my opinion is the lack of any mention of this sketch in their docs. I wasted a couple hours trying to figure out how to program the arduino so that it could send recognizable commands to the Rainbowduino.

Decoding DisplayChar() and the backwards-G

To reverse engineer the Rainbowduino code, I focused on the character display function (Item #1 from the list above). When the arduino sends the command to the Rainbowduino to display a character, it sends the ASCII code to display, 3 values {0-15 each} which indicate the brightness of the Red, Green and Blue LEDs, and a "shift" variable which lets you slide the character left or right a number of pixels.

I looked at the character "G", which is preloaded on the Rainbowduino with the following instruction set:

G={0x00,0x78,0x44,0x44,0x74,0x04,0x44,0x38}

and, in binary (or nerd-speak):

{00000000,01111000,01000100,01000100,01110100,00000100,01000100,00111000}

and displays data like this:

And guess what the binary codes for "G" look like when stacked up, from bottom to top? Like a backwards G.

00111000
01000100
00000100
01110100
01000100
01000100
01111000
00000000

The reason it's backwards is because the rightmost column of LEDs on the matrix are controlled by the highest-order bits, which are the leftmost bits

And the leftmost column of LEDs on the matrix are controlled by the lowest-order bits, which are the rightmost bits.

 

(I proofread this statement several times and I'm pretty sure I wrote that correctly.)

DisplayChar Colors...OK, so remember that DisplayChar also takes in 3 4-bit values to define the intensity of Red, Green and Blue? DisplayChar uses an internal array structure called dots_color which controls how the Rainbowduino sends signals to the LEDs. For each color and coordinate, the dots_color array requires 4 bits which will describe the intensity of that color. A 4-bit value of {0000} denotes the lowest intensity (off), and a 4-bit value of {1111} denotes the brightest intensity, with 14 values in between ({0001}, {0010}, {0011}, and so on).

DisplayChar has an outer loop over all the colors. For each iteration, it takes the 8-byte code and processes it byte-by-byte (row-by-row). Everywhere it finds an ON bit, it copies the 4-bit intensity value for the current color it's processing (the outermost loop) into the dots_color array. The illustration below shows how a byte value of 9A (10011010) would be loaded into dots_color if we wanted to display the maximum intensity for the Green LEDs.

Creating a Custom Rainbowduino Command

The sample code doesn't enable you to display multiple colors (unless it's one of the preloaded patterns already in Rainbowduino memory). I need to extend the Rainbowduino firmware beyond that to enable me to send On/Off data for each color on-the-fly. This means I needed to send more data over the wire from the arduino. Specifically, I have to send over 192 bits for the command (8 rows * 8 columns * 3 colors). The nice part is that since I only care about Eight Colors: Red, Green, Blue, Purple (R+B), Yellow (R+G), Cyan (G+B), White (R+G+B), and Black (if you count "off" as a color). This means I can assume that my intensity values will always be {1111}. So I have no need to send granular intensity data over the wire. An LED will be full-on or completely off, like a bipolar weiner dog.

Like the preloaded patterns, the character data mappings are stored in the Rainbowduino, which means that all you need to send over the wire for a DisplayChar command is a single byte that corresponds to that character's ASCII code. Then, the Rainbowduino looks up the predefined 8-byte array for that ASCII value. This makes the DisplayChar command a very lightweight, compact message. I won't have that luxury of knowing predefined mapped-row hex values. However, I can piggyback on the DisplayChar technique to encapsulate all the On/Off row data I need (for a single color and a single row) into a single byte. So, I'll increase my message size by 24 bytes (8 rows * 3 colors).

I made the new command look like this:

DisplayCustom(int Address, byte RedRow1, byte RedRow2, byte RedRow3, byte RedRow4, byte RedRow5, byte RedRow6, byte RedRow7, byte RedRow8, byte GreenRow1, byte GreenRow2, byte GreenRow3, byte GreenRow4, byte GreenRow5, byte GreenRow6, byte GreenRow7, byte GreenRow8, byte BlueRow1, byte BlueRow2, byte BlueRow3, byte BlueRow4, byte BlueRow5, byte BlueRow6, byte BlueRow7, byte BlueRow8)

I resized the RainbowCMD array on the firmware and wrote logic to handle the new incoming command. If you do this, you have to change the function "receiveEvent" in the Rainbowduino code to accept more bytes in the message, and "SentCMD" in the Seeedmaster code to send more bytes. Below is a snippet of the code I used on the Rainbowduino that loads dots_color from the new RainbowCMD array:


void DispshowCustom(void)
{

unsigned char color,row,dots;
int i;

RainbowCMD[1]=0;

byte wholerowdata;
byte dotpair;

//render
for(color=0;color<3;color++)
{
    for (row=0;row<8;row++)
    {
        if (color == 0)
            wholerowdata = RainbowCMD[13+row]; //green rows in 13-20
        if (color == 1)
            wholerowdata = RainbowCMD[5+row]; //red rows in 5-12
        if (color == 2)
            wholerowdata = RainbowCMD[21+row]; //blue rows in 21-28
        for (dots=0;dots<4;dots++)
        {
            dotpair = 0x00;
            //higher order 4-bits
            if ((wholerowdata<<(dots*2))&0x80)
                dotpair|=0xF0;

            //lower order 4-bits
            if ((wholerowdata<<(dots*2+1))&0x80)
                dotpair|=0x0F;


            dots_color[((Buffprt+1)&1)][color][row][dots]=dotpair;
        }
    }
}

Buffprt++;
Buffprt&=1;

}

I had a small problem with the command, where the last byte of the command (corresponding to the top row of the blue LEDs) was getting corrupted somehow.

This was resolved by tacking on an extra byte onto the RainbowCMD. I don't know why this worked but it did, and I played around with feeding random(255) into each DisplayCustom argument to give it a final test. Disco, Disco!

 

bool pattern[3][8][8] and the Blinking Cursor

To make life a little easier, I wanted to have an easy-to-work-with data structure in the arduino so that I could easily turn on and off bits without having to do a bunch of binary math (as fun as that is). I decided to use a bool array called pattern[3][8][8].

So for the above desired display, I can set the following bits in the pattern array:

pattern[0][4][2] = true;

pattern[1][2][7] = true;

pattern[2][7][2] = true;

The code snippet which translates pattern[3][8][8] data to R/G/B byte arrays (called argument_arrays) follows. I almost forgot what fun bitshifting was.

  for (color = 0; color < 3; color++)
  {
    i = 0;
    for (int gridrow = 7; gridrow >= 0; gridrow--)
    {
      ThisRow = 0x00;   
      for (int gridcol = 7; gridcol >= 0; gridcol--)
      {
        if (gridcol != 7)
          ThisRow = (byte)(ThisRow << 1);                       

        ThisRow |= ((pattern[color][gridrow][gridcol])?0x01:0x00);       
                                          
      }
      argument_arrays[color][i] = ThisRow;
      i++;
    }               
  }

Using this technique and a little 500ms delay, I was able to get a blinking red cursor in the 0,0 position. Whew. It feels like I just walked 70 miles to get to a McDonald's. Surely the reader feels the same way.

Waiting for the joystick, Other ideas surface

While waiting on the joystick to arrive in the mail, I had an idea for an offshoot project....What if we used a Maze Generation algorithm to put a maze onto the LED matrix and then let the user go through the maze with the joystick? Of course an 8x8 pixel maze would be incredibly easy, but you could easily stitch several of these mazes together and have a large maze that becomes quite a challenge. Here is a proof of concept with the cursor moving around randomly. And here is the writeup for that project, currently in progress.

 

The other offshoot idea I had was interfacing the arduino (via USB/serial connection) to capture data from a webcam, and send that data to the rainbowduino. Of course it would be all pixellated and pretty much unrecognizable, but we're just going for proof-of-concept here. I lifted some free code from devx (here) and changed it to suit my needs (added "pixellation" logic and the resulting RGB color levels are filtered down to 4-bit values so that they can be understood by the Rainbowduino). Apparently, I2C Communication through the arduino is limited to 32 bytes though, so I'll need to get creative with how to send over the extra intensity data values (since I can't rely on pure ON/OFF values with this idea). This guy has figured out some way to do it. I lifted some of his code from here for a future project (BriteCam!!)

Back on track

The joystick arrived, and it wasn't too hard to get the detection code working. I had already written the function to handle how to adjust the pattern[][][] array to show the cursor moving around (MoveCursor). So all I had to do was implement the logic to know when to initiate the move.

Done!

The project is complete! I had a bit of trouble with integrating the cursor blinking logic and persisting the colors of the painted pixels. Code to be posted later.

Code Posted

Code has been posted here. Your mileage may vary in terms of what Atari wires correspond to the directional and button commands, but the black wire will be what you send to GND. 

Update (1/13/2010): "It's a major award!"

My JoyLite Maze project won first place in the arduinofun.com contest. Frankly, I think the secret knock project was really outstanding, but I think I won because of the level of detail in the step-by-step instructions I created. The contest was for beginner to intermediate arduino users, so I think my project was one of the more "approachable" ones for newbies.

 

 Questions or comments, email joetcochran AT gmail DOT com