Rainbowleidoscope: A Rainbowduino Kaliedoscope Introduction

Rainbowduino Kaleidoscope
Rainbowleidoscope: A Rainbowduino
Kaliedoscope
A SeeedStudio 2009 Rainbowduino Carnival Entry
by blue.zener
Introduction
In my family the stock response whenever something couldn't be readily explained was always "they do it
with mirrors".
Owning just one Rainbowduino and pondering over just how to get the most out of it, it occurred to me that it
was possible to give the effect of having many Rainbowduinos. How? By using mirrors of course.
It then occurred to me that what, in effect, I would produce is a kaleidoscope. These classic children's toys
employ a couple of mirrors to reproduce a pattern many times over. The pattern itself is made up of coloured
glass beads, or some such, filtering the ambient white light. By rotating the kaleidoscope, the beads rearrange
and a new and colourful pattern is produced.
The difference between the classic kaleidoscope and mine would be that a Rainbowduino would replace glass
beads. By making the Rainbowduino cycle through the full gamut of beautiful colours, in beautiful
arrangements of patterns it would be possible to simulate the glass beads. So far as I can tell, this is an original
idea (if such a thing is possible) -- a quick google search reveals that no one has documented a LED matrix
kaleidoscope (till now).
To produce interesting patterns I implemented Conway's Game of Life on the Rainbowduino. Most of the
time the game is short lived on the small 8x8 playing field of the Rainbowduino. Sometimes, however a
Glider or a Pulsar-type pattern will be seen. It is the possibility of seeing one of these explode into life on the
kaleidoscope that makes looking into it quite addictive. To cycle through a range of appealing colours, I use a
lookup table that contains the RGB values for 64 hues in the HSV colourspace (see below for full details).
My Rainbowduino kaleidoscope has two modes of operation, selected by a single momentary switch. If the
switch is not pressed when power is applied to the Rainbowduino, the kaleidocope simply shows a test pattern
(Figure 1). This is really handy for testing that everything is connected OK. It will continue to show the test
pattern until the switch is pressed. Once the switch is pressed, the Rainbowduino enters "random mode" in
which Conway's Game of Life is played over and over using a (pseudo-)randomized initial state each time.
The instant at which the switch is pressed is used to generate a seed that improves the randomness of the
numbers.
If, on the other hand, the switch is pressed when power is applied, the kaleidoscope enters "canned life" mode
in which the Game of Life is played from a number of starting conditions that generate interesting patterns,
such as Blinkers, Toads, Gliders and F-Pentamentos.
Rainbowleidoscope: A Rainbowduino Kaliedoscope
1
Rainbowduino Kaleidoscope
Figure 1. Rainbowduino showing the test pattern
In practice the images produced by my Rainbowduino kaleidoscope look a little different to those produced by
a classic kaleidoscope. But they are interesting and captivating in their own right. You can check the patterns
out in this video -- but, of course, the kaleidoscope is far more impressive when experienced first hand:
Watch the Kaleidoscope in action (Vimeo).
In this document I give details of the kaleidoscope code and the build process. Hopefully there will be enough
detail provided for others to make their own Rainbowduino kaleidoscopes, and to take the concept further.
Parts List
To build your own Rainbowduino kaleidoscope, you will need:
• 1 x SeeedStudio Rainbowduino module
• 1 x Seeedstudio 8x8 RGB led matrix
• 1 x Momentary switch or jumper
• 1 x Power supply to suit Rainbowduino
• 2 x Elastic bands
• 2 pieces of acrylic mirror cut to 250mm x 60.5mm
• 2 pieces of acrylic mirror cut to 250mm x 66.5mm
• Foamboard or thick cardboard
• Double-sided tape
You will also need a ruler, a pencil, a craft knife, and an AVR ISP programmer to program the firmware.
Finally, you will need the source code and/or firmware, both of which are available in kaleidoscope_v1.zip.
Building the Kaleidoscope
To build your own Rainbowduino kaleidoscope, follow these steps:
Introduction
2
Rainbowduino Kaleidoscope
1. Program your Rainbowduino with the firmware main.hex. Because my code is written in vanilla avr-gcc
(with no Arduino bootloader), you will need to program it using the in-circuit programming header (ISP) on
the Rainbowduino using a suitable AVR programmer.
2. Wire up a momentary switch between the SCL and GND pins of the ATMEGA168. This is very easy to do
since both of these pins are brought out to a header connection on the Rainbowduino. Wiring the switch to a
two pin header socket (Figure 2) makes for a very clean connection.
Figure 2. Connect the switch to the SCL and GND header pins.
3. Grab your foamboard (or thick cardboard) and mark out four rectangles matching the dimensions of your
four pieces of mirror. (Tracing around each mirror is a quick way to do this). Cut out the four rectangles.
Using the double-sided tape, attach the back of each mirror to a piece of foamboard (Figure 3). This will help
protect the delicate mirror backing of the mirror acrylic.
Figure 3. Each mirror is backed by a piece of foamboard, which in turn is attached to the frame, also made of
foamboard.
Building the Kaleidoscope
3
Rainbowduino Kaleidoscope
4. Cut out a large rectangular piece of foamboard to form the rigid frame of your kaleidoscope. It should be as
close to 250 x 306mm as possible. On this piece of foamboard, mark three parallel lines such that the board is
divided into four rectangular sections of 250 x 76.5mm. Carefully score along each of these three lines, such
that the foamboard is almost, but not quite completely cut through. Flip the foamboard such that the
non-scored side is shown.
5. Apply double-sided tape to the foamboard side of each foamboard backed mirror and affix each mirror to
one of the four 250 x 76.5mm sections of the frame. Make sure each mirror is centered on its respective
section. Also make sure you alternate mirrors of different sizes -- i.e. fat mirror (66.5mm), then thin mirror
(60.5mm), then fat mirror (66.5mm), then thin (60.5mm). Once this has been done, your frame should look
like Figure 4.
Figure 4. Each mirror is carefully affixed to the foamboard frame.
6. Fold the completed frame along the scored cuts into a rigid tube with a square cross-section (Figure 5).
Building the Kaleidoscope
4
Rainbowduino Kaleidoscope
Figure 5. The completed structure.
7. Secure the frame with a rubber band at each end (Figure 6).
Figure 6. Another view of the completed structure.
8. Insert the Rainbowduino into one end of the tube (Figure 7). It should be a snug fit.
Building the Kaleidoscope
5
Rainbowduino Kaleidoscope
Figure 7. Inserting the Rainbowduino into the completed structure.
Your Rainbowduino kaleidoscope is now complete. Just hook it up to a power supply and enjoy the beautiful
colours and enchanting patterns.
The Code
There are two distinct parts to the code: the part which handles the multiplexing of the RGB matrix, and the
part which plays Conway's Game of Life. The source code is heavily commented, so it should hopefully be
easy to understand and adapt. I will discuss interesting elements of the code here.
Conway's Game of Life
I can't promise that my implementation of Conway's Game of Life is the most compact or memory efficient.
But it does appear to work and is hopefully easy to follow and adapt to other projects.
The code snippet below plays life indefinitely. The function life_clear() wipes the life playing field clean;
life_init() populates it with a random initial pattern; life_put() displays the playing field on the RGB matrix;
and life_tick() 'evolves' life.
There are two ways the current game can come to an end. Firstly, life_tick() is able to detect still life (i.e.
when no evolution has taken place). If still life is detected, the game is immediately ended, and the next
started. Secondly, after 255 iterations the game is ended. On the 8x8 playing field, life is very likely to
converge to a fairly uninteresting pattern after 255 ticks.
This approach works pretty well at keeping the game from getting boring whilst requiring minimal resources
and code from the AVR.
The Code
6
Rainbowduino Kaleidoscope
// play life using random playing fields
// this function will run indefinitely
void play_random_life(void)
{
uint8_t i;
// cache some random numbers for the initial playing field
for(i=0;i<8;i++)
cache_a_random_number();
while(1) {
// setup the initial playing field
life_clear();
life_init();
life_put();
_delay_ms(10);
// play life for up to 255 iterations
for(i = 0; i < 255; i++) {
// still life detected, so move on to next pattern
if (life_tick()) break;
life_put();
_delay_ms(10);
// cache a new random number on each iteration of life
cache_a_random_number();
}
}
}
As noted above, life_tick() 'evolves' the playing field once. The first part of the function iterates over each cell
and applies the simple rules of the game to decide if a cell should continue to live, die, stay dead, or come to
life.
After this, the new state of the playing field is compared with the last. If they compare, we have still life and
it's time to start a new game.
Lastly, the function makes the next iteration of life the current one.
uint8_t life_tick(void)
{
uint8_t x, y, n, a;
// iterate over each cell
for(y=0; y<8; y++)
{
for(x=0; x<8; x++) {
a = cell_alive(x,y);
n = alive_neighbours(x,y);
// apply the simply rules of Conway's Game of Life to the current cell
kill_cell(x,y);
(a
if (a
if (a
if
&&
&&
&&
(n < 2)) kill_cell(x, y);
(n > 3)) kill_cell(x, y);
((n == 2) || (n == 3))) birth_cell(x, y);
Conway's Game of Life
7
Rainbowduino Kaleidoscope
if
((!a)
&&
(n == 3)) birth_cell(x, y);
}
}
// detect if next iteration of life matches the last iteration
// in which case we have still life
if (memcmp(life[current], life[next], 8) == 0)
return 1;
// swap the indexes of the current and next playing fields
current = (current == 0 ? 1 : 0);
next = (next == 0 ? 1 : 0);
return 0;
}
You may have noticed the cache_a_random_number() function above. This function generates a random
number and saves it in an array. The contents of this array are used to initialise the next game of life.
Because it uses floating point maths, this function is kind of slow. So as to minimise a noticable delay
between games, this function is called once per every tick of the game, at a point where we would want a
delay anyway. Assuming the game runs for at least eight iterations, the cache should be completely filled with
a fresh batch of random numbers by the time a new game is started.
This function adds extra complexity to the implementation, but does a good job of distributing the processing
load on the micro over time.
// Cache a random number into the next available
// position in random_cache[]
void cache_a_random_number(void)
{
static uint8_t next_index
= 0;
// method of generating random number recommended by AVR LIBC developers
// unfortunately requires floating point math
random_cache[next_index] = (uint8_t)
((double)rand() / ((double)RAND_MAX + 1) * 256);
next_index++;
if (next_index == RANDOM_CACHE_COUNT)
next_index = 0;
}
Multiplexing
My multiplexing code is heavily based on the sample sketch provided by SeeedStudio. However I take a
slightly different approach to addressing the rows of the RGB LED matrix.
First, I set up three parallel arrays that hold information about the port used to address each row, as shown in
[1]. The first holds the address of each data direction register. The second holds the address of each port
register. And the third holds the bit of the DDR or port register that needs to be used to address that row.
This makes it really easy, for instance, to configure all the rows bits as outputs, as shown in [2].
Multiplexing
8
Rainbowduino Kaleidoscope
And when it comes to selecting the relevant row to display at that instant in time, the switch statement used in
the original sketch code can be replaced by a single line, as shown in [4].
All this is very expressive and straightforward, which I like. However when we want to turn off all rows
en-mass, we're still better off using bit masks, as shown in [3]. Although a for loop like in [2] could be used, it
will be far too slow in a time critical situation such as an interrupt service routine (ISR).
// [1] DDR, PORT and BIT assignments for all 8 rows of RGB matrix
uint8_t *rowDdrs[] = {
&DDRB, &DDRB, &DDRB, &DDRD, &DDRD, &DDRD, &DDRD, &DDRD
};
uint8_t *rowPorts[] = {
&PORTB, &PORTB, &PORTB,
&PORTD, &PORTD, &PORTD, &PORTD, &PORTD
};
uint8_t rowBits[]
= {
PB2,
PB1,
PB0,
PD7,
PD6,
PD5,
PD4,
PD3
};
// [2] make all row control lines outputs
for (i=0; i<8; i++) {
*rowDdrs[i] |= _BV(rowBits[i]);
}
// [3] turn off all rows
PORTD&=~0xf8;
PORTB&=~0x07;
// [4] turn on relevant row
*rowPorts[row] |= _BV(rowBits[row]);
The Colour Lookup Table
As noted in the introduction, I use a lookup table to store some appealing colours to cycle through. In order to
generate this table, I produced a little Ruby script "hsvtorgb.rb" (included with the source code) to convert
between the Hue-Saturation-Value (HSV) and RGB colourspaces.
The reason for this is that the HSV colourspace is a far more intuitive way of thinking about colour. You
simply decide how pure (i.e. saturated) you want the colour to be, and how bright you wanted it to be (i.e. its
value). You can then simply cycle through all the available hues to generate a nice set of colours in which
each colour transitions gracefully into the next.
Because the Rainbowduino operates in the RGB colourspace, a method of converting between colourspaces is
required. There is a fair bit of maths involved, so rather than doing this on the AVR, I produced the
"hsvtorgb.rb" script. This takes a saturation and a value parameter and produces a C-formatted array
containing 64 hues. This lookup table can then be pasted into the main source code.
Below is the main conversion function from this script. It is a simple translation of the pseudocode available
here, scaled to suit hues between 0 and 63 and the Rainbowduino's 4-bit greyscale. Apart from these changes I
believe that my translation is true to the original.
# Convert from hsv colourspace to the rgb colourspace.
# Adapted from pseudocode at http://www.mandelbrot-dazibao.com/HSV/HSV.htm
The Colour Lookup Table
9
Rainbowduino Kaleidoscope
# h: hue from 0 to 63
# s: saturation from 0 to 99
# v: brightness from 0 to 99
# output [r,g,b] data in array format, 16 shades per channel
def hsv_to_rgb (h, s, v)
angle = (h) * 2 * PI / 64.0
ur = v * 15 / 100.0
radius = ur * tan(s * atan(sqrt(6)) / 100.0)
vr = radius * cos(angle) / sqrt(2)
wr = radius * sin(angle) / sqrt(6)
r = ur - vr - wr
g = ur + vr - wr
b = ur + wr + wr
if r < 0
rdim = ur / (vr + wr)
r = 0
g = ur + (vr - wr) * rdim
b = ur + 2 * wr * rdim
elsif g < 0
rdim = -ur / (vr - wr)
r = ur - (vr + wr) * rdim
g = 0
b = ur + 2 * wr * rdim
elsif b < 0
rdim = -ur / (wr + wr)
r = ur - (vr + wr) * rdim
g = ur + (vr - wr) * rdim
b = 0
end
if r > 15
rdim = (ur - 15) / (vr + wr)
r = 15
g = ur + (vr - wr) * rdim
b = ur + 2 * wr * rdim
end
if g > 15
rdim = (15 - ur) / (vr - wr)
r = ur - (vr + wr) * rdim
g = 15
b = ur + 2 * wr * rdim
end
if b > 15
rdim = (15 - ur) / (wr + wr)
r = ur - (vr + wr) * rdim
g = ur + (vr - wr) * rdim
b = 15
end
[r.to_i, g.to_i, b.to_i]
end
Conclusion
The Rainbowduino kaleidoscope was a fun project to put together. The patterns and colours produced by the
kaleidoscope are quite spectacular. That said, I still have a certain fondness for the classic kaleidoscopes with
Conclusion
10
Rainbowduino Kaleidoscope
the glass beads -- elegant simplicity!
There are lots of ways the project could be extended. Here are some ideas, but I'm sure you will have your
own:
• Use a diffuser over the Rainbowduino to help blend the red, green and blue channels into a pure
colour. Even greaseproof paper from the kitchen cupboard functions as a pretty good diffuser.
• Experiment with different mirror configurations -- three mirrors, six mirrors, etc.
• In the game of life, select the hue of colour displayed for a cell on the basis of how many alive
neighbours it has. Or perhaps determine the brightness of a cell in this manner.
• Play three separate games of life and display them using the red, green and blue channels respectively.
Where games overlap there should be interesting blends of colour produced.
• Replace the game of life with another means of generating patterns. Some kind of plasma display
could look really cool.
• Use a lens to project the kaleidoscope onto a wall, rather than having to stare into it.
Credits
• SeeedStudio for the original sample sketch. And for organising the Rainbowduino Carnival!
• http://www.mandelbrot-dazibao.com for their handy HSV conversion information.
• http://tohtml.com and http://www.chami.com/colorizer for their useful syntax highlighting services.
• The music on the video was "Penetrating Light" by Necros. See here for download and details.
License & Disclaimer
You are free to use this information and the source code as you wish, on the condition that you don't pass this
information off as your own, and that when you adapt the code you consider adding a note to say that it was
adapted from source code by blue.zener.
In using this information and the source code, you must understand and accept that blue.zener cannot be held
responsible in the event any harm or injury results from using the information or the source code provided
here.
Credits
11