Tag Archives: club

Maker Wedding: Animated Arduino LED matrix lounge table top


Finally got around to making an LED table top, as it turns out — for my wedding reception. We decided to have a lounge area and an LED coffee table seemed like the perfect centerpiece for it. I decided instead of making a full table that I would make a table top that fit onto an existing ottoman. I affixed the LED strips to a plywood board which had a 2″ raised frame with aluminium duct tape, to help with brightness.

Arranging the LED’s in a proper matrix turned out to be quite a job as the strips I used came pre-wired and there isn’t all that much length between LED’s on the strip. I ended up having to cut and re-splice the connection leads for each row of the 7 x 7 matrix, after that the construction went quickly. You could get around this by using a more modular LED strip solution, I initially had ShiftBrites slated for this project, but I made something else with them and when I got around to this table top there were much less expensive options available.

I created an outer frame with a bevel to support a glass top. Initially I went with plexiglass but it would bow in the middle with anything of weight on the table, I didn’t want to add supports as this would disrupt the light diffusion, so I opted for a piece of tempered glass (actually intended for table tops to boot).

Adding adhesive obscuring film to the glass didn’t have the diffusion effect I’d hoped for so I sandwiched a sheet of white tracing paper between the tempered glass and a similarly sized piece of plexiglass and this gave the soft white diffused look I wanted.

The issue of programming the animations took a little longer. I wanted to use the disco(esc) animations available from the fine folks responsible for the 1E Disco Dance Floor — which I also used in my ohDisco! app for iPad. These animations are 32 x 16 and can have hundreds of frames — too much to load completely into the Arduino’s memory. But I didn’t want to have to deal with reducing the animations to 7 x 7, or reducing their total frame count, as this would affect the quality and overall impression. Instead I opted to add an SD card reader to the setup which stores the animations. The 7 x 7 section of each frame is loaded on-demand from each animation file and displayed on the table, with this setup the Arduino has no memory problems whatsoever and with a little more code it could index and play animations from the SD card without the need for code changes.

Worth noting is that the SdFat library used to interface with the Seeedstudio SD Card Shield wouldn’t run reliably (or at all sometimes) on an ATmega128 so be sure to use a more powerful Arduino running an ATmega328.

Parts

Arduino Sketch


const int chipSelect = 10;

#include <SdFat.h>
#include "SPI.h"
#include "Adafruit_WS2801.h"

uint8_t dataPin  = 2;
uint8_t clockPin = 3;   

SdFat sd;
SdFile myFile;

int rows = 32;
int cols = 16;

long framesize = rows*cols*3;
long rowsize = cols*3;

int ledrows = 6;
int ledcols = 6;

int rep = 0;
long reps = 5;

int brightness = 15;
int delaytime = 40;

char* files[]={
  "pulsar.ddf",
  "snake.ddf",
  "inter3.ddf",
  "inter4.ddf",
  "inter5.ddf",
  "rings.ddf",
  "rings2.ddf",
  "rings3.ddf",
  "matrix.ddf"
  };

int fileCount = 9;

char* file;
int frame = 0;
int frames;

Adafruit_WS2801 strip = Adafruit_WS2801(50, dataPin, clockPin);

// strip to matrix addressing array
byte addressMatrix[7][7] = {
  1,2,3,4,5,6,7,
  14,13,12,11,10,9,8,
  15,16,17,18,19,20,21,
  28,27,26,25,24,23,22,
  29,30,31,32,33,34,35,
  42,41,40,39,38,37,36,
  43,44,45,46,47,48,49
};

void setup() {
  Serial.begin(9600);
  randomSeed(analogRead(0));
  delay(400);  // catch Due reset problem
  if (!sd.begin(chipSelect, SPI_FULL_SPEED))
    sd.initErrorHalt();

  file = files[ random(fileCount) ];

  strip.begin();
  strip.setPixelColor(0, 0, 0, 0);
  strip.show();
}

void loop() {
  if (!myFile.open(file, O_READ)) {
    sd.errorHalt("failed");
    rep = reps + 1;
    return;
  }

  // seek to next frame
  if(myFile.fileSize() < ((frame*framesize)+1))
  {
    myFile.close(); 

    frame = 0;
    rep = rep + 1;

    if(rep > reps)
    {
      rep = 0;
      file = files[ random(fileCount) ];
      Serial.println(file);
    }

    return;
  }
  else
  {
    myFile.seekSet(frame*framesize);
  }

  // adjust reps for number of frames
  if(frame == 0)
  {
    frames = myFile.fileSize()/framesize;
    reps = 750/frames;
    Serial.println(frames,DEC);
    Serial.println(reps,DEC);
  }

  int data;

  int column = 0;
  int row = 0;

  while (row <= ledrows)
  {
    while (column <= ledcols)
    {
      data = myFile.read();
      // read red
      int r = map(data,0,255,0,255);
      // read green
      data = myFile.read();
      int g = map(data,0,255,0,255);
      // read blue
      data = myFile.read();
      int b = map(data,0,255,0,255);

      // set pixel address
      byte address = addressMatrix[row][column];

      // set pixel color
      strip.setPixelColor(address, map(r,0,255,0,brightness), map(g,0,255,0,brightness), map(b,0,255,0,brightness));

      // next column
      column = column + 1;
    }

    // reset column count
    column = 0;

    // increment row
    row = row + 1;

    // skip extra pixels
    myFile.seekSet((frame*framesize)+(row*rowsize));
  }

  // turn off first pixel (7x7 matrix, 1 unused pixel)
  strip.setPixelColor(0, 0, 0, 0);

  // send current frame to strip
  strip.show();

  // close the file
  myFile.close();

  // increment frame
  frame = frame + 1;

  // rest
  delay(delaytime);
}