Paul Makepeace ;-)

March 8, 2008

Arduino showing multiplexed LED matrix

Posted in: Art, Gadgets, Software, Tech

To control any more than 12 LEDs on an Arduino board requires a bit of trickery. The Diecimila with its Atmega168 chip has 14 digital output pins, but you somewhat lose two from the TX/RX serial pins on 0 and 1 leaving just 12 pins. Today I built an LED matrix with 20 lights using nine pins,

The technique is to light row by row, and rely on our persistence of vision effect. Playing around with the delay it seemed like 3ms for a total of 15ms (five rows) was about the point where flicker just started being noticeable.

Geeky detail after the fold...

The software pulls the row pins high one by one while the column pins are set low for every LED to light, sinking the current from the one high row pin. There's a 3ms delay, then the next row pin is brought high and the last one low.

The data for the little animation is a 2D byte array that looks like,

byte PATTERN[][5] = { 
  /* ... */
  /* ... */

('B' is a handy prefix for a binary number.)

You can see a diagonal band of 1's there in the early part of the wipe. Each byte's translated into pin writes by looking at the lowest bit in the byte and then shifting right and looking at the next. LOW is on.

void SetColumn(byte pattern) {
  for (int i = COL_COUNT-1; i >= 0; i--, pattern >>= 1) {
    digitalWrite(COL_PINS[i], pattern & 1 ? LOW : HIGH);

A "frame" of the display is completed by lighting each row in turn,

void ShowPattern(byte pattern[]) {
  int last_row = ROW_COUNT-1;
  for (int row = 0; row < ROW_COUNT; last_row = row++) {
    digitalWrite(ROW_PINS[row], HIGH);
    digitalWrite(ROW_PINS[last_row], LOW);

The main loop draws each frame over and over until a certain amount of time has elapsed and then it goes onto the next one. millis() is a built-in that returns the number of milliseconds since the board was switched on. (This code would fail after about nine hours when the counter resets.)

void loop()
  for (int pos = 0; pos < PATTERN_SIZE; pos++) {
    long start = millis();
    while (millis() - start < PATTERN_DELAY_MS) {


I actually had the circuit the other way around, cycling through column-by-column and the rows programmed in turn with a line of lights. This happened out to make writing patterns awkward so I flipped the orientation which made SetColumn pretty straightforward.

The matrix could've had more lights on it but the breadboard limited width a bit. I was quite proud of the tight wiring under the LEDS.

From Multiplexed LED Matrix

This was one of the projects where the bits that seemed like they'd be hard Just Worked and then silly things wasted time, like C mistakes with sizeof.

int ROW_PINS[] = {13, 12, 11, 10, 9};
int COL_PINS[] = {5, 4, 3, 2};
#define ROW_COUNT (sizeof(ROW_PINS)/sizeof(ROW_PINS[0]))
#define COL_COUNT (sizeof(COL_PINS)/sizeof(COL_PINS[0]))

Parameterizing these kinds of things made moving from a 3x4 board to 4x5, flipping the rows & columns, and using different pins trivial. An unfortunate cargo-cult effect in Arduino and Wiring code I've seen is folks not using #define and sizeof.

Some cute enhancements might be to have an array of structs, the struct containing additionally a delay time for each frame. We could also have, rather than 0/1, a wider range of values to indicate brightness. With that, (e.g. Perl/Python scripted) code could generate the animation sequences rapidly flipping frames to give an impression of anti-alias. Unfortunately I have other things to do... (yeah, I made the animations by hand in vi and pasted into the Arduino editor :-))

And finally, a Wiring-based pair of color LED matrixes are used as toilet signs... that change throughout the night [video]. More on this funny project.

Posted by Paul Makepeace at March 8, 2008 23:27 | TrackBack

Hi, I just recreated your setup going from the pictures you posted, but there is one thing I don't understand (note that I'm very new to electronics.)

On the last column, the lines (orange wires) that connect the negative sides of the LEDs, simply go nowhere?
What does this accomplish? Does this have something to do with putting +5V on the negative sides of the LEDs, to run that power away without damaging the LEDs? But wouldn't it be better then to connect all those last wires to a ground? Or would that require another consumer of some sort then?

Any enlightenment would be very much appreciated :)

Posted by: Bernard Kerckenaere at February 6, 2009 15:17

Hi Bernard,

Well spotted, and congrats on recreating the board! The orange wires on the right (see the board) are only there in case you wanted to connect another board and extend the circuit out to the right. I'd toyed with doing that to make a 5x5 array but didn't get around to it.

So in summary: yes, you could leave those five far-right orange wires out as they're not connected to anything.

Have fun!

Posted by: Paul Makepeace at February 6, 2009 17:16

Hi Paul,

I also managed to rebuild your setup with 4x4 white LEDs. It's all working so far, the only trouble is that some LEDs seem to glow a little bit even though they should be turned off in the pattern. They are not as bright as those that are rightly turned on. Do you have any idea what might cause this? I can send you a pic of my setup if you like, it's looking little different due to the limitations of by mread board ;-)

Anyway, it's great that you put this information on the web, it really helped me to get my matrix working somehow :-)

Thanks in advance for any hints.


Posted by: sMiLe at March 6, 2009 14:43

Hey just a heads up, I wanted to let everyone know that Xio Dibin speaks English. I hope I posted in the right location?

Posted by: Shaun Davis at April 23, 2009 01:59

What kind of power source are you using?

Posted by: Patrick at August 12, 2010 11:31

Thank you for the tutorial AND the pictures.
As a beginner, I could easily recreate your circuit.
Most of people are using shift registers in their tutorial, even for 4*4 grids, this is way easier ;))

Posted by: pim at February 21, 2011 18:01

Great code. It was a little trick at first, but I've got it now. Your code example made it a lot easier for me to figure out. If anybody else stops by and has trouble check out my code based on the code presented here at:

Posted by: Michael Baker at March 18, 2011 00:36
Post a comment

Remember personal info?