Sunday, September 14, 2014

Yet Another Daft Punk Coffee Table (5x5 LED Matrix) using Arduino [Full Tutorial]


Yes, I know this has been done before, but I wanted to build my own, using as few parts as possible.  I built this as a table top or wall mount model, but it can be scaled up to make a coffee table.  I built it as a study for a more ambitious project, which will be an 8x8 RGB coffee table.  In designing this project, I wanted to keep the part count and cost as low as possible, and also to design the software for speed and minimal memory usage.

The code I used in the Arduino sketch demonstrates direct port manipulation, which is a fast and powerful alternative to digitalWrite().

Update 3/10/2011 : Finished adding the Music Synchronization section of this Instructable.
Update 3/20/2011:  I decided to build Lampduino instead of the coffee table.

Note that the flickering in the video below was caused by my video camera. When viewed with the naked eye, no scanning is perceived, and the lights do not flicker.

Music Sync Mode:
Arvid "Try (fenomenon remix)"


Syncing to Daft Punk's "Around the World:


Free-running mode (without music synchronization):














Step 1: Acquire the Parts

Parts List

25 LED's - I used warm white 5mm LED's purchased on eBay
5 resistors - I used 100 ohm, but the exact value will depend on what LED's you use. I will explain how to calculate in the Wiring step.
NOTE: You may need also need 10 diodes and different resistors, especially if you are using white LED's... please the Troubleshooting section in step 3 for details.
Arduino - I used a Duemilanove clone - in Step 7, I switch to a much cheaper alternative.
wire
white foam board
.005" thick matte drafting film - I bought a sheet from a local artist's supply store.  The smallest sheet they had was way more than I needed - 24x36"

If you want to implement music synchronization capabilities, you will also need some more parts, as discussed in the Music Synchronization step.

Tools Needed

hot glue gun
mat knife
soldering iron
solder
pliers/wire cutters

The LED's I used were point source water clear.  If you don't want to deal with hotspots in your display, diffused would be better.  I had to put blobs of hot glue on mine to diffuse them.  The photo below only shows bits of the foam board, because I forgot to photograph the parts before I started.  The piece I used was about 16x20" to start with.


Step 2: Build the Display

I used foam board, which was recycled from one of my kids' science fair projects. It is a sandwich of white paper with a foam core, 3/16" thick. The overall size is approximately of our project is approximately 10x10x2".  I cut the parts with a mat knife, and hot glued it together.

To make the grid, cut 8 identical 2x10" pieces.  Then cut 4 equally spaced slits into each one.  The slits should be 1" deep,  the same width as the foam board.  Interlock the pieces to create the 5x5 grid.

Next, cut a 10x10" piece of foam for the back.  Hot glue it to the 5x5 grid.

Cut 2 more 2x10" pieces to form the sides, and glue them on.

Finally, cut the 2 (10 3/8)x2" pieces for the top and bottom.  My foam was 3/16" thick, so I had to add 3/8" to the length to make them long enough to cover the sides.  Hot glue them on.

Note that in the photo below which includes the top & bottom & sides, the LED's are already installed and wired up.  This is because I didn't attach the top/bottom/sides until I was in the testing phase, but it's easier to deal with if you glue them on earlier in the process.

Step 3: Wire it up

Cut small diagonal slits in the center of each cell, and insert one LED in each cell. Make sure to orient them the same way in each cell. This will make it less confusing when wiring it up. I laid mine out w/ the flat side (cathode) facing the bottom right corner. Using diagonal slits for the pins makes it easier to wire it up without shorting the wires, since we are going to wire
it as a crossbar.

While one can use any of the I/O pins on the Arduino, I chose the pins specifically to allow me to use very compact code to turn on the columns. This will be explained in detail later.

To drive our 5x5 LED matrix, we directly drive the LED's using 10 digital I/O pins on the Arduino.
The anodes are connected to pins.  Some existing designs, such as the one in the Arduino Playground  don't bother to use current limiting resistors.  This is not a good design practice, and can result in burned out LED's, or worse yet, a burned out Arduino.  Each I/O pin on the Arduino can source or sink up to 40mA of current.  The LED's which I used have the following electrical characteristics:

  Forward Voltage  = 3.2 ~ 3.4V
  Max Continuous Forward Current = 20mA

So if we want to drive the LED's for maximum brightness, we need to target 20mA of current.
To calculate the proper resistor value, we use Ohm's Law:

   R = (Vcc - Vf) / If

where

  R = resistor value in ohms
  Vcc = supply voltage = 5V for the Arduino Duemilanove
  Vf = LED forward voltage.  I used the average, 3.3V
  If = LED current in amperes = .020A

Plugging in the values, we get R = (5 - 3.3) / .02 = 85 ohms.  The nearest available standard resistor value is 100 ohms.  Always round up instead of down, because if you round down, you will exceed the maximum allowable current.

Notice that we only use 5 resistors.  We don't have to put one at each LED, because we will only be driving one row, a maximum of 5 LED's at a time.  I said above that each I/O pin can drive 40mA of continuous current, so why can't we drive the whole LED array at once?  It's because another constraint is that the total drive current summed up across all the pins can't exceed 200mA.  If we turn on all 25 LED's at once, then 25*20mA = 500mA flow, which is way over spec.

So maybe we can turn on 1 row at a time, and scan the rows, like the way a CRT works?   If we turn on a whole row of LED's at once, the current is 20mA * 5 = 100mA.  This, at first, appears to be OK, because each column (anode) pin is only sourcing 20mA, and we're below the Atmega368P's total 200mA current limit.  However, upon more analysis, we can't even drive 5 LED's at once.  Why?  Because the cathodes of all 5 LED's in a row are connected together into a single I/O pin, and we're not allowed to sink more than 40mA per pin.  Therefore, we will write our software so that no more than 2 LED's are turned on at a time, so the row (cathode) pins will sink a maximum of 40mA each.  Now, even though we're at the allowable continuous current limit, it's generally not good practice to run a device at its maximum limits.  However, since we're going to pulse each LED briefly, and let persistence of vision create the illusion that they're all on at once, it's OK.

Note:  I tried running mine w/ 5 LED's lit per row for several hours, and it worked fine, but it's always best to design your circuits within specifications, to ensure long term reliability.

The circuit diagram is below.  To summarize the connections:

  LED Columns (anodes)
  col 0 connects to digital pin 12 (via a 100 ohm resistor)
  col 1 connects to digital pin 11 (via a 100 ohm resistor)
  col 2 connects to digital pin 10 (via a 100 ohm resistor)
  col 3 connects to digital pin 9 (via a 100 ohm resistor)
  col 4 connects to digital pin 8 (via a 100 ohm resistor)

LED Rows (cathodes)
  row 0 connects to digital pin 7
  row 1 connects to digital pin 6
  row 2 connects to digital pin 5
  row 3 connects to digital pin 4
  row 4 connects to digital pin 3

I used simple point to point wiring, fastened with hot melt glue to the back of the display.  I know it's messy looking, but it won't be seen, anyway.

Troubleshooting

If your matrix doesn't function properly, first, you should double check your wiring.   The sketch also has a testing mode, which cycles through the LED's one by one slowly enough that you can see it.  You can enable it by uncommenting the following line in the sketch:

  //#define TESTMODE // continuously sequence thru the LED's

by removing the leading //.

One potential mistake is accidentally swapping the columns and rows.  If your matrix looks like this video http://www.youtube.com/watch?v=JpLgLbWMrWo in TESTMODE, then you've made this error, and need to swap the row and column connections to your Arduino.  Thanks to Instructables user 303_addict for posting the video.

If your wiring is correct, and you are getting more than one LED at a time lighting up, you might be one of the unlucky ones who has LED's that have a high leakage current when reverse biased.  White LED's are particulary susceptible to this problem.  If this is the case, you will need to add series blocking diodes on the inputs to all the columns, as well as on all the row outputs.  So you will need 10 diodes.  Any small signal diode will work, such as 1N4001, 1N914, 1N4148, etc.  You will also need to adjust the resistor values, because  two series diodes will add ~1.4V voltage drop.  So in my equation above, use 3.6 for VCC.  For my 3.3V LED's you end up with R = (5-1.4-3.3V)/20mA = 15 ohms.  I didn't have any 15 ohm resistors handy, so I substituted 10 ohms instead, and using an ammeter, measured 19.5mA .. still within spec.  See the last attached image.


Step 4: Diffusing the LED's

I used point source LED's, because that's what I had in my parts bin.  This caused hot spots in the display.  If you like it that way, you don't have modify them.  I wanted more even lighting. The two most common ways of converting point source LED's to diffused is to either sand them or just encase them in hot melt glue.  I decided to use hot melt glue.  Just put a big blob of hot glue on each LED.  I found that it looks more even if you can keep the glue blob smooth.  The shape doesn't have to be perfectly round, but if you apply it in layers, the edges between the layers tend to cause variations in the brightness.  In the photo below, the LED in the top right cell has hot glue on it, while the rest are bare.  Note that it does a decent job of evening out the light.

At first, I used white copier paper to cover the LED's.  It darkened the display to bit too much, and looked grainy.  Then I went to an art store, and found .005" thick matte drafting film.  It looks a lot better.  I cut a 10 3/8 x 10 3/8" piece and glued it to the front.

Step 5: Code

The Arduino sketch draws the screen scanning row by row from top to bottom, much like a CRT-based TV screen.  It has the added complexity that in order to keep our current within the limits that the Atmega328P can handle, we can only light at most 2 LED's at a time.   Therefore, we also scan from left to right.  Even though only 2 LED's at a time are lit, to our eye, they appear to all be lit at the same time due to a phenomenon called persistence of vision.

The display code is interrupt driven, and uses the Timer1 library .  Timer1 isn't bundled in the Arduino software installation.  Therefore, to install it, you must download TimerOne.zip , and unzip its contents into your sketchbook/libraries/TimerOne.

A Frame is a screenful of data. A very straightforward and common way of storing the data would be to use a byte for each LED.  This would use 25 bytes per frame.   To save memory, we define it as an array of 5 bytes.  Each byte represents an entire row of our display.  Since a byte is composed of 8 bits, and we only have 5 LED's per row, we only use the bottom 5 bits, and the top 3 bits are ignored.  There are 5 bytes because we have 5 rows.  We could save another byte by packing 25 bits into 4 bytes, but that complicates the code unnecessarily, and makes it impossible to graphically view the frame declarations in frames.h.

  #define DIM 5 // x/y dimension - 5x5 matrix
  typedef byte Frame[DIM];


The column LED pins are defined in the cols array.

  int cols[DIM] = {12,11,10,9,8};


The row LED pins are defined in the rows array.

  int rows[DIM] = {7,6,5,4,3};

To turn on a particular LED, we set its cols pin HIGH and its rows pin LOW.  This causes current to flow through the selected LED.  The most straightforward way to do this would be to use the digitalWrite() function on each LED in sequence.  So to turn on the top left LED, we could use

  digitalWrite(cols[0],HIGH);
  digitalWrite(rows[0],LOW);

and do that for each LED.  However, that's not very efficient.   Instead we use direct port manipulation to turn on the columns.  The PORTB register corresponds to digital pins 8 to 13.
Note the ordering which we used in our declaration of the cols array above:  12,11,10,9,8.  This way, bit0 of PORTB corresponds to the far right column, bit1 corresponds to the next column to the left, until bit4, which corresponds to the far left column.  If we set a particular bit to 1, it's the same as calling digitalWrite() with HIGH, and setting a bit to 0 is equivalent to calling digitalWrite() with LOW.   Therefore, we could theoretically replace calling digitalWrite() 5 times to set all 5 columns with one write to PORTB.  But since we are allowed to turn on at most only 2 LED's at a time, we have to loop 3 times for each column, scanning 2 bits at a time.

// Interrupt routine
// each time display() is called, it turns off the previous row
// and turns on the next row
byte bitMask = B00000011;
void display() {
  digitalWrite(rows[row], HIGH); // Turn whole previous row off

  if (bitMask == B00010000) {
    bitMask = B00000011; // light the right 2 columns (pins 9,8)
    // increment row and wrap if necessary
    if (++row == DIM) {
      row = 0;
    }
  }
  else if (bitMask == B00000011) {
    bitMask = B00001100; // light the middle 2 columns (pins 11,10)
  }
  else { // bitMaskIdx == B00001100
    bitMask = B00010000; // light the leftmost column (pin 12)
  }

  // direct port manipulation.
  // PORTB is a pseudo variable for digital pins 8 to 13 The two high bits (6 & 7) map to the crystal pins and are not usable
  // the bottom 5 bits are our columns. We don't want to change the other bits,
  // so we first mask the bits we ignore, and then set the bits we want to light
  PORTB &= B11100000;
  PORTB |= curFrame[row] & bitMask;

  digitalWrite(rows[row], LOW); // turn on the row
}

Note that our comment above says that display() is an interrupt routine.  This is because instead of handling the scanning in our loop() function, we call it in the background via the Timer1 library.

  // interrupt interval in uSec
  // this determines how long to keep each row turned on
  // so if we have 5 rows, we redraw the whole screen
  // once every 5 rows * 3 cycles per row * 1000 usec = .015 sec -> 66.67Hz
  // if you perceive flickering you can decrease to 500 for a 133.33Hz rate
  Timer1.initialize(1000); // fire the interrupt every 1000 microseconds
  Timer1.attachInterrupt(display); // call display() at every interrupt.

The main loop simply steps through all of our animation frames.

  void loop() {
    // increment the frame index and wrap if necessary
    if (++curFrameIdx == FRAMECNT) {
      curFrameIdx = 0;
    }

    // select frame for display
    setFrame(curFrameIdx);

    delay(400); // wait time between frames in ms - reduce this value to speed up the animation, increase it to slow it down
  }

The entire sketch is attached below.

Step 6: Animation Design

The animations are stored in frames.h.

   #define FRAMECNT 117

specifies how many frames are contained in your animation.
A frame is just a full screen of data.  The default animation that I put in it contains 117 frames.

You can specify frames by just typing them in one by one.   For instance, a capital 'Z' could look like:

{ B00011111,
  B00000010,
  B00000100,
  B00001000,
  B00011111}

The bottom 5 bits of each byte correspond to the pixels of each row.  Any bit which is set to 1 is lit, and 0 is off.  The top 3 bits are ignored.  This manual process can get rather cumbersome if you want to make complex animations

Ian was kind enough to share the code for his Internet Graffiti Wall . (Thanks, Ian!) I hacked it up to generate code for frames.h.  To create your own animations, simply load daftpunkanimationbuilder.html into any java-enabled web browser.  Follow the instructions on the screen to create your own animation.  When you're satisfied with it, click the Generate button, and copy the code from the text box, and paste it into frames.h, replacing its entire contents.

If you want to save your animation for editing later, check native fmt before clicking Generate, and then copy and paste the contents of the text box into a text file.  You can later load your saved animation back into the animation builder by pasting it into the Restore from sequence code text box, and then clicking Add before current frame.

Please send me any cool animations which you are particularly proud of, and I'll add them to this Instructable for others to download.

Step 7: Music Synchronization

Circuit Modifications

To allow our LED matrix to synchronize the animations to music, we need to interface it to an audio source. The ATmega328 MCU conveniently contains A/D converters which we can use for this purpose. Although there are various designs for color organs, which work with various frequency bands, I prefer syncing with the bass track.

We could use an analog pin to sample music, and then do frequency analysis on the Arduino, but this would introduce a delay, and still require us to build an antialiasing filter. Since we need to build a filter, we might as well keep things simple. I built an analog low pass filter, fed its output into an analog input in, and then simply tested the amplitude against a threshold.  I started with a the simplest possible filter, a first order low pass RC filter.  Much to my surprise, it worked fairly well.  To design the filter, I first needed to figure out what cutoff frequency to use.  The cutoff frequency, Fc,  of a filter is the frequency where the power is cut in half.  After playing around with graphic equalizers in Winamp while playing music with thumpy bass tracks, I settled on Fc = 100Hz.  I didn't have the proper resistor handy, so I used the closest value I had, which resulted in Fc = 133Hz.  Here is the circuit:

Parts Needed:
(1) 10uF electrolytic capacitor
(1) 120 ohm resistor
(1) 1/8"  TRS jack



The output connects to analog pin 0 on the Arduino.  Connect its input to an audio source, such as the headphone jack of a stereo, or an MP3 player.  DO NOT CONNECT IT TO SPEAKER LEVEL OUTPUTS FROM AN AMPLIFIER UNLESS YOU'RE SURE IT OUTPUTS <= 5V.  Any audio source designed to drive headphones should be safe.  Since I use the headphone jack on a stereo, I can still hear the music even when the circuit is plugged in.  If you're using something with only one output, such as an MP3 player, you can hook up two TRS jacks in parallel, and then feed the audio from the 2nd jack to a set of powered speakers or headphones.

Note that the circuit above only monitors one channel of audio, which works with most songs, since the bass is generally approximately equal amplitude in both channels.  It works OK, but I found that in order to get a high enough input for the Arduino to respond nicely, I have to turn the volume up a bit too high for my tastes.  Therefore, I also built the alternate circuit below:

Parts needed:
(1) 10uF electrolytic capacitor
(1) 120 ohm resistor
(3) 10K ohm resistor
(1) 100K ohm resistor
(1) 1/8" stereo TRS jack
(1) TLC272CP op amp IC



UPDATE: Sorry, the schematic diagram above is wrong.  There should be a third 10K resistor, R5, which connects between pin 2 (- input of opamp) and GND.  The gain of the amp is R4 / R5.

You can substitute any suitable op amp. I used the TLC272CP because that happens to be what I had on hand, and it has the particularly nice characteristic that it doesn't mind running on a single-ended power supply of only 5V, so I could just hook it up to the Arduino's power.  The circuit above mixes the left and right audio channels, and has a gain of 10.  Just hook it up to your stereo audio source, and connect the output to analog pin 0, as before.  The 5V and GND also connect to your Arduino.

If you prefer to use a microphone rather than direct wiring, pepehdes has posted an alternate circuit using an electret mic in Step 1's comments, which he says works well.  Note that it is an all-pass design so it will trigger on all frequencies, not just bass.  I didn't use a microphone, because my design tracks the bass which might not be picked up very well via small speakers and a microphone.

Sketch Revisions

Next, we need to modify the sketch to read the audio input, and trigger the next animation frame when a threshold is exceeded.  The new sketch, daftPunkSyncV2.zip, is attached below.  You can switch the sketch between music sync and free-running by modifying

   // musicSync =1= sync to music
   //                      =0= use timer
   int musicSync = 1;

  You will need to manually tune the threshold variable:

   // trigger next frame when music amplitude > threshold
   // increase threshold to decrease sensitivity
   // decrease threshold to increase sensitivity
   int threshold = 5;

The next frame will be triggered when the input read from analog pin 0 exceeds threshold.  If you're running the sketch on a real arduino that has an LED on digital pin 13, it is helpful to uncomment the following line:

   #define BLINKY // blink LED on pin 13

by removing the leading // characters.  This will cause the LED to blink when threshold is exceeded.  I have it turned off in the sketch by default, because I'm using my tiny breadboard instead of an Arduino now, and it doesn't have an LED.  Finally, you may want to experiment a bit with minBeatInterval:

  // debouncing - filter out consecutive triggers that are too close
  // together to slow down the animation. decrease this value
  // to make it more sensitive to shorter beat intervals
  int minBeatInterval = 1000;

Note that this variable is unit-less.  It simply defines how many consecutive loops after the input exceeds our threshold before the sketch is allowed to trigger the next frame.  I found that in many songs, bass beats are actually composed of several very closely spaced peaks.  If we don't filter them out, then the display will advance several frames for each beat.  You might actually like this effect, because it makes the display more lively.  Play around with it, and see what setting you prefer.


Step 8: Costing Down

While using an Arduino is convenient for development/prototyping, it's not a very cost effective way to go once your project is done.  Also, the board is rather bulky.  After initial development, I swapped out the Arduino Duemilanove for a tiny ATmega pcb kit from allgaiershops for $2.99, and a bare ATmega328P from Sparkfun for $4.30.   This board is fully Arduino compatible if you have a USB->Serial converter cable or a hardware AVR programmer.  I have a clone of Adafruit's USBtinyISP.

To program this board using the Arduino IDE, plug your programmer into the ICSP header.  The USBtinyISP supplies 5V power, so not other connection is needed to program and test it.  You will need to make some minor tweaks to your Arduino IDE in order to make it play nicely with the USBtinyISP.  I have posted detailed instructions on my blog:  How to Use the Arduino IDE with an External Programmer.

The next complication is that you need to figure out which pins on your ATmega328P MCU map to the digital pins on the Arduino.  The pin mappings can be found on the Arduino website:  ATmega8-Arduino Pin Mapping
Someone also made a PDF which you can use to print labels to attach directly to your ATmega328P here: Arduino Breadboard Labels .  Here is the specific pin mapping we need for our project:

Arduino Digital Pin  -> ATmega328P pin:
digital pin 12 -> ATmega pin 18
digital pin 11 -> ATmega pin 17
digital pin 10 -> ATmega pin 16
digital pin 9 -> ATmega pin 15
digital pin 8 -> ATmega pin 14
digital pin 7 -> ATmega pin 13
digital pin 6 -> ATmega pin 12
digital pin 5 -> ATmega pin 11
digital pin 4 -> ATmega pin 6
digital pin 3 ->ATmega pin 5
analog pin 0->ATmega pin 23

So take the pin that was connected to digital pin 12 on the Arduino, and connect it to pin 18 on the breadboard, and so on.

A cheap way to power the project once you're done building it is to buy a cheap USB wall wart, such as is used to charge various devices.  You can get them on eBay, or if you're lucky, your local 99 cent store may carry them. Just cut off the USB connector, and solder the power wires to VCC (pin 7) and GND (pin 8) on the ATmega (MAKE SURE TO CHECK THE POLARITY WITH A METER FIRST.  VCC is the positive lead, GND is the negative one). If you solder a USB power supply to your board, make sure to remove the PWR jumper from your USBtinyISP, or unplug your charger from the wall before programming it.

2 comments:

  1. Thanks for the code its working 100%
    But if try to change the pins in the code its not working then

    ReplyDelete
  2. Hello
    We are working on this project with my senior class. We are not experienced with the coding. We are having hard time to understand the code. We tried to copy the different sections of the code on the arduino software then run it but only the top left(I guess) light up and blinked. Could you add a complete code like a text file so we can download. Thank you in advance.

    ReplyDelete