Make:DC LED Cube Workshop

by Mark on January 19, 2008

You can read my experience at the workshop in my previous post, but just wanted to post that even making it home without a copy of the instructions, I figured it out, and actually got my cube working. Well, aside from the two dead LEDs, at least.

Here’s a short video of the test, where you can see I have 2 dead LEDs in the middle plane. Guess I should have tested all the LEDs before building the cube, instead of after.

The cube is built on top of a board that connects to headers that plug right in to the Arduino board, and the USB cable leads to my MBP whence I just uploaded the code.

Thanks to Tim Slagle of the local Make:DC group for the test code and putting this project together for the group. I’ll upload some more video once I find a couple of replacement LEDs and work up a fun animation.

UPDATE: Three dead LEDs later, time for some fun flashies:

If you want the Arduino code, it’s after the break:

/* LEDcube Arduino version Tim Slagle 1/12/08

Based on ledcube.c from Make: September 7, 2007 weekend podcast  
http://blog.makezine.com/archive/2007/09/make_a_pocket_led_cube_we.html

Custom animation programmed by Mark Boszko, http://stationinthemetro.com
*/

#include <avr/pgmspace.h>        // allows use of PROGMEM to store patterns in flash

#define CUBESIZE 3
#define PLANESIZE CUBESIZE*CUBESIZE
#define PLANETIME 3333          // time each plane is displayed in us -> 100 Hz refresh
#define TIMECONST 20          // multiplies DisplayTime to get ms - why not =100?

// LED Pattern Table in PROGMEM - last column is display time in 100ms units
// TODO this could be a lot more compact but not with binary pattern representation
prog_uchar PROGMEM PatternTable[] = {
  // blink on and off
    B111, B111, B111, B111, B111, B111, B111, B111, B111, 5,
    B000, B000, B000, B000, B000, B000, B000, B000, B000, 1,
    B111, B111, B111, B111, B111, B111, B111, B111, B111, 5,
    B000, B000, B000, B000, B000, B000, B000, B000, B000, 1,
  // flash each LED in sequence: 
  // Left->Right column, then Top->Bottom row, then Upper->Lower plane
    B100, B000, B000, B000, B000, B000, B000, B000, B000, 1,
    B010, B000, B000, B000, B000, B000, B000, B000, B000, 1,
    B001, B000, B000, B000, B000, B000, B000, B000, B000, 1,
    B000, B100, B000, B000, B000, B000, B000, B000, B000, 1,
    B000, B010, B000, B000, B000, B000, B000, B000, B000, 1,
    B000, B001, B000, B000, B000, B000, B000, B000, B000, 1,
    B000, B000, B100, B000, B000, B000, B000, B000, B000, 1,
    B000, B000, B010, B000, B000, B000, B000, B000, B000, 1,
    B000, B000, B001, B000, B000, B000, B000, B000, B000, 1,
    B000, B000, B000, B100, B000, B000, B000, B000, B000, 1,
    B000, B000, B000, B010, B000, B000, B000, B000, B000, 1,
    B000, B000, B000, B001, B000, B000, B000, B000, B000, 1,
    B000, B000, B000, B000, B100, B000, B000, B000, B000, 1,
    B000, B000, B000, B000, B010, B000, B000, B000, B000, 1,
    B000, B000, B000, B000, B001, B000, B000, B000, B000, 1,
    B000, B000, B000, B000, B000, B100, B000, B000, B000, 1,
    B000, B000, B000, B000, B000, B010, B000, B000, B000, 1,
    B000, B000, B000, B000, B000, B001, B000, B000, B000, 1,
    B000, B000, B000, B000, B000, B000, B100, B000, B000, 1,
    B000, B000, B000, B000, B000, B000, B010, B000, B000, 1,
    B000, B000, B000, B000, B000, B000, B001, B000, B000, 1,
    B000, B000, B000, B000, B000, B000, B000, B100, B000, 1,
    B000, B000, B000, B000, B000, B000, B000, B010, B000, 1,
    B000, B000, B000, B000, B000, B000, B000, B001, B000, 1,
    B000, B000, B000, B000, B000, B000, B000, B000, B100, 1,
    B000, B000, B000, B000, B000, B000, B000, B000, B010, 1,
    B000, B000, B000, B000, B000, B000, B000, B000, B001, 10,

  // Some little cube - big cube fun  
    B000, B000, B000, B000, B011, B011, B000, B011, B011, 10,
    B111, B111, B111, B111, B111, B111, B111, B111, B111, 10,
    B000, B000, B000, B000, B011, B011, B000, B011, B011, 5,
    B000, B000, B000, B000, B000, B000, B000, B000, B001, 2,
    B000, B000, B000, B000, B011, B011, B000, B011, B011, 2,
    B111, B111, B111, B111, B111, B111, B111, B111, B111, 2,
    B000, B000, B000, B000, B011, B011, B000, B011, B011, 2,
    B000, B000, B000, B000, B000, B000, B000, B000, B001, 2,
    B000, B000, B000, B000, B011, B011, B000, B011, B011, 2,
    B111, B111, B111, B111, B111, B111, B111, B111, B111, 1,
    B000, B000, B000, B000, B011, B011, B000, B011, B011, 1,
    B000, B000, B000, B000, B000, B000, B000, B000, B001, 1,
    B000, B000, B000, B000, B011, B011, B000, B011, B011, 1,
    B111, B111, B111, B111, B111, B111, B111, B111, B111, 1,
    B110, B110, B000, B110, B110, B000, B000, B000, B000, 1,
    B100, B000, B000, B000, B000, B000, B000, B000, B000, 1,
    B110, B110, B000, B110, B110, B000, B000, B000, B000, 1,
    B111, B111, B111, B111, B111, B111, B111, B111, B111, 1,
    B000, B000, B000, B000, B011, B011, B000, B011, B011, 1,
    B000, B000, B000, B000, B000, B000, B000, B000, B001, 1,
    B000, B000, B000, B000, B011, B011, B000, B011, B011, 1,
    B111, B111, B111, B111, B111, B111, B111, B111, B111, 1,
    B110, B110, B000, B110, B110, B000, B000, B000, B000, 1,
    B100, B000, B000, B000, B000, B000, B000, B000, B000, 1,
    B110, B110, B000, B110, B110, B000, B000, B000, B000, 1,
    B111, B111, B111, B111, B111, B111, B111, B111, B111, 1,
    B000, B011, B011, B000, B011, B011, B000, B000, B000, 1,
    B000, B000, B001, B000, B000, B000, B000, B000, B000, 1,
    B000, B011, B011, B000, B011, B011, B000, B000, B000, 1,
    B111, B111, B111, B111, B111, B111, B111, B111, B111, 1,
    B000, B000, B000, B110, B110, B000, B110, B110, B000, 1,
    B000, B000, B000, B000, B000, B000, B100, B000, B000, 1,
    B000, B000, B000, B110, B110, B000, B110, B110, B000, 1,
    B111, B111, B111, B111, B111, B111, B111, B111, B111, 1,
    B000, B011, B011, B000, B011, B011, B000, B000, B000, 1,
    B000, B000, B001, B000, B000, B000, B000, B000, B000, 1,
    B000, B011, B011, B000, B011, B011, B000, B000, B000, 1,
    B111, B111, B111, B111, B111, B111, B111, B111, B111, 1,
    B000, B000, B000, B110, B110, B000, B110, B110, B000, 1,
    B000, B000, B000, B000, B000, B000, B100, B000, B000, 1,
    B000, B000, B000, B110, B110, B000, B110, B110, B000, 1,

  // Diagonal wipe, starting in the center
    B111, B111, B111, B111, B111, B111, B111, B111, B111, 5,
    B111, B111, B111, B111, B111, B111, B111, B101, B111, 1,
    B111, B111, B111, B111, B101, B111, B111, B101, B111, 1,
    B111, B111, B111, B111, B101, B111, B111, B100, B111, 1,
    B111, B101, B111, B111, B100, B111, B111, B100, B110, 1,
    B111, B101, B111, B111, B100, B111, B111, B100, B110, 1,
    B111, B011, B111, B111, B100, B110, B111, B100, B100, 1,
    B111, B100, B110, B111, B100, B100, B111, B100, B000, 1,
    B111, B100, B100, B111, B100, B000, B111, B000, B000, 1,
    B111, B100, B000, B111, B000, B000, B011, B000, B000, 1,
    B111, B000, B000, B011, B000, B000, B001, B001, B000, 1,

  // 2-LED wide diaginal stripe that orbits the cube
    B011, B000, B000, B001, B001, B000, B000, B001, B001, 1,
    B001, B001, B000, B000, B001, B001, B000, B000, B011, 1,
    B000, B001, B001, B000, B000, B011, B000, B000, B110, 1,
    B000, B000, B011, B000, B000, B110, B000, B100, B100, 1,
    B000, B000, B110, B000, B100, B100, B100, B100, B000, 1,
    B000, B100, B100, B100, B100, B000, B110, B000, B000, 1,
    B100, B100, B000, B110, B000, B000, B011, B000, B000, 1,
    B110, B000, B000, B011, B000, B000, B001, B001, B000, 1,

  // Now, with center flashies, for flavor  
    B011, B000, B000, B001, B001, B000, B000, B011, B001, 1,
    B001, B001, B000, B000, B001, B001, B000, B000, B011, 1,
    B000, B001, B001, B000, B000, B011, B000, B010, B110, 1,
    B000, B000, B011, B000, B000, B110, B000, B100, B100, 1,
    B000, B000, B110, B000, B100, B100, B100, B110, B000, 1,
    B000, B100, B100, B100, B100, B000, B110, B000, B000, 1,
    B100, B100, B000, B110, B000, B000, B011, B010, B000, 1,
    B110, B000, B000, B011, B000, B000, B001, B001, B000, 1,
    B011, B000, B000, B001, B011, B000, B000, B001, B001, 1,
    B001, B001, B000, B000, B001, B001, B000, B000, B011, 1,
    B000, B001, B001, B000, B010, B011, B000, B000, B110, 1,
    B000, B000, B011, B000, B000, B110, B000, B100, B100, 1,
    B000, B000, B110, B000, B110, B100, B100, B100, B000, 1,
    B000, B100, B100, B100, B100, B000, B110, B000, B000, 1,
    B100, B100, B000, B110, B010, B000, B011, B000, B000, 1,
    B110, B000, B000, B011, B000, B000, B001, B001, B000, 1,
    B011, B010, B000, B001, B001, B000, B000, B001, B001, 1,
    B001, B001, B000, B000, B001, B001, B000, B000, B011, 1,
    B000, B011, B001, B000, B000, B011, B000, B000, B110, 1,
    B000, B000, B011, B000, B000, B110, B000, B100, B100, 1,
    B000, B010, B110, B000, B100, B100, B100, B100, B000, 1,
    B000, B100, B100, B100, B100, B000, B110, B000, B000, 1,
    B100, B110, B000, B110, B000, B000, B011, B000, B000, 1,
    B110, B000, B000, B011, B000, B000, B001, B001, B000, 1,

  // Wrapping up 
    B001, B001, B000, B000, B001, B001, B000, B000, B001, 1,
    B001, B001, B000, B000, B001, B001, B000, B000, B001, 1,
    B000, B001, B001, B000, B000, B001, B000, B000, B001, 1,
    B000, B000, B001, B000, B000, B001, B000, B000, B001, 1,
    B000, B000, B000, B000, B000, B001, B000, B000, B001, 1,
    B000, B000, B000, B000, B000, B000, B000, B000, B001, 5,
    B000, B000, B000, B000, B000, B000, B000, B000, B000, 3,
    B000, B000, B000, B000, B000, B000, B000, B000, B001, 5,
    B000, B000, B000, B000, B000, B000, B000, B000, B000, 3,
    B000, B000, B000, B000, B000, B000, B000, B000, B001, 5,
    B000, B000, B000, B000, B000, B000, B000, B000, B000, 3,

  // this is a dummy element for end of table (duration=0)
    B000, B000, B000, B000, B000, B000, B000, B000, B000, 0  
};

/*
** Defining pins in array makes it easier to rearrange how cube is wired
** Adjust numbers here until LEDs flash in order - L to R, T to B
** Note that analog inputs 0-5 are also digital outputs 14-19!
** Pin DigitalOut0 (serial RX) and AnalogIn5 are left open for future apps
*/

int LEDPin[] = {16, 3, 1, 15, 4, 6, 14, 5, 7};
int PlanePin[] = {19, 18, 17};

// initialization
void setup()
{
  int pin;      // loop counter
  // set up LED pins as output (active HIGH)
  for (pin=0; pin<PLANESIZE; pin++) {
    pinMode( LEDPin[pin], OUTPUT );
  }
  // set up plane pins as outputs (active LOW) 
  for (pin=0; pin<CUBESIZE; pin++) {
    pinMode( PlanePin[pin], OUTPUT );
  }
}

// display pattern in table until DisplayTime is zero (then repeat)
void loop()
{
  // declare variables
  byte PatternBuf[ PLANESIZE ];      // saves current pattern from PatternTable
  int PatternIdx;
  byte DisplayTime;        // time*100ms to display pattern
  unsigned long EndTime;
  int plane;      // loop counter for cube refresh
  int patbufidx;   // indexes which byte from pattern buffer
  int ledrow;    // counts LEDs in refresh loop
  int ledcol;    // counts LEDs in refresh loop
  int ledpin;    // counts LEDs in refresh loop

  // Initialize PatternIdx to beginning of pattern table
  PatternIdx = 0;
  // loop over entries in pattern table - while DisplayTime>0
  do {
    // read pattern from PROGMEM and save in array
    memcpy_P( PatternBuf, PatternTable+PatternIdx, PLANESIZE );
    PatternIdx += PLANESIZE;
    // read DisplayTime from PROGMEM and increment index
    DisplayTime = pgm_read_byte_near( PatternTable + PatternIdx++ );    
    // compute EndTime from current time (ms) and DisplayTime
    EndTime = millis() + ((unsigned long) DisplayTime) * TIMECONST;

    // loop while DisplayTime>0 and current time < EndTime
    while ( millis() < EndTime ) {
      patbufidx = 0;    // reset index counter to beginning of buffer
      // loop over planes
      for (plane=0; plane<CUBESIZE; plane++) {
        // turn previous plane off
        if (plane==0) {
          digitalWrite( PlanePin[CUBESIZE-1], HIGH );
        } else {
          digitalWrite( PlanePin[plane-1], HIGH );
        }

        // load current plane pattern data into ports
        ledpin = 0;
        for (ledrow=0; ledrow<CUBESIZE; ledrow++) {
          for (ledcol=0; ledcol<CUBESIZE; ledcol++) {
            digitalWrite( LEDPin[ledpin++], PatternBuf[patbufidx] & (1 << ledcol) );
          }
          patbufidx++;
        } 

        // turn current plane on
        digitalWrite( PlanePin[plane], LOW );
        // delay PLANETIME us
        delayMicroseconds( PLANETIME );
      }    // for plane
    }    // while <EndTime
  } while (DisplayTime > 0);        // read patterns until time=0 which signals end
}

{ 4 comments… read them below or add one }

1 Jay 01.27.08 at 5:07 am
2 Mark 02.17.08 at 12:56 pm

Thanks for catching that. I had an issue with using the TextMate blogging bundle to post to my blog, whenever I edited a previous post, it would change the time stamp -5 hours, sometimes even making it the day before, which would change the archive link. Fixed now, thanks!

3 Frederick Heald 02.21.08 at 11:49 am

Thanks! Works great for me.

int LEDPin[] = {16, 3, 1, 15, 4, 6, 14, 5, 7}; I’m not quite sure why the pins are in that order - seems arbitrary. int PlanePin[] = {19, 18, 17}; These make sense, three from the last.

Since you’re using pin 1, you get 16 + 3 pins - which means you could do a 4×4x3 cube, I believe; if pin 0 works then 4^3, er, pow(4,3)! Of course the map table would have to be rewritten.

Had a bit of a hard time finding the link to how the cube is wired (before I realized how simple it is - ground per layer, positive per column!) http://blog.makezine.com/archive/2007/09/make_a_pocket_led_cube_we.html

4 Juergen Mentor 10.23.08 at 7:12 pm

I was looking at your project and found it very interesting. As such i have decided to build one. Unfortunately i hit a snag, while i have the schematics for the cube from make magazine, i have no idea how to connect it to the Arduino. The one set of schematics i found seemed to be a little more complicated than the one you had ( http://www.atomicvpp.com/wpblog/category/projects/led-cube/ ) Would it be possible for me to get some kind of schematics or an idea of how to connect the cube to the Arduino.

Leave a Comment

You can use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Half Man, Half Tiki Man