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
© Copyright 2024