LED matrix projector


Lately I was playing with my dual color LED matrix from Sparkfun. It is a matrix of 8 by 8 dual color (red and green) LEDs that measures 5 cm by 5 cm. I just had some sprites flickering across the matrix as the magnifying glass of my “third hand” came in the way. I realized, that, if in the right distance, it will project the sprites on the ceiling. Although the projection is not very bright, it works, if the room is dark enough. Disco, here I come.


Dual color LED matrix

So here is, what you need to build one:

  • Arduino, Boarduino from Adafruits for the breadboard
  • Dual color LED matrix from Sparkfun
  • 2 * 74HC595 shift register
  • 1 * ULN2803 darlington sink driver
  • 16 * 100R resistors
  • 1 * 10kR potentiometer
  • magnifying glass


Schematic dual color matrix
The matrix is orgranized in 8 rows and 16 columns (8 red and 8 green). To address a single LED in the matrix, you have to set one of the 16 column bits and enable the coresponding row. Note that there is always only a single row active. If every row is activated fast enough, a steady picture comes into being. The shift registers are used to provide column data. 16 bits are are loaded serially and put out parallely.

The rows are handled by an ULN2803, an array of darlington transistors. This chip is used to sink the current of the activated row. If every LED in a single row is activated, the current for this row will sum up to 320 mA (16 * 20 mA). Definately too much for a single pin of a microcontroller. The ULN2803 is able to sink up to 500 mA per channel and cheap (about 30 euro cent).


LED matrix projector

To generate a steady picture, we have to provide 16 bit of data, at least every 2.5 ms  (50 Hz * 8 rows). For this purpose the timer 2 of the ATMega168 is used. This timer normally controlls PWM channels 3 and 11 for Arduinos, so they are no longer accessible. The ISR (Interrupt Service Routine) for this timer takes the 16 bits for the current row and shifts them out. Next time it is called, it will output the data for the next row. Take a look at the great uc Hobby tutorial on Arduino interrupts for more details.

The sprites are organized in 8 bytes (8 * 8 bits). So every sprite can cover the whole matrix for a single color. The screen memory is organized as 16 bytes, 8 bytes for red and green. That way you can print a sprite in red or green. If you print it in red and green, the result is orange.

To control the sprites, I hacked a tiny script language. The scripts are arranged in small sequences. Every sequence consists of commands of one or two bytes. Available commands are:

  • select page (none, red, green, orange)
  • select sprite
  • move sprite (up, down, left, right)
  • print selected sprite and selected page
  • sleep
  • call sub sequence

With the possibility to structure commands in sequences and subsequences it is possible to create more complex animation as with simple frame by frame animation. That may be handy if the memory is limited as it is for the ATMega168 with 16kB SRAM.


As you can see, the projection is not bright enough to show up on my camera. So I had to switch to “night mode”. That worked but lacked the speed of the animation. It looks much better in real.

Links and downloads


  1. (first!)
    F* awesome!
    I really love it! And during the weekend you implemented a little disco light effects program!

    well done!

    PS: publish the code for the music controlled effects ;)


  2. Wow – this is really cool!
    Just an idea here – I think some animations may look good when projected out-of-focus to create a low-res smooth image (I’m thinking of something like lava-lamp or slow fire)

    Thanks for sharing!


  3. Very cool!
    It would take a lot more work but you should be able to massively increase the brightness by using different LEDs. Get the highest power ones you can find with the narrowest beam angle (I don’t know how many options there are in RGB). Then do some math to select a lens focal length and diameter. The SparkFun unit is designed to send light in all directions so you can see it from all angles. You want to send as much a possible up through the lens.
    Awesome work


  4. Y’know… a module like that would make an absolutely KICKASS sequential turn signal/brake light unit…


  5. If you turn this project on its side, you get 16 rows x 8 columns, How would you extend the project to get 16 rows x 40 columns?


  6. Steve,
    8 rows with 40 columns could be addressed by adding more shift registers. To enable 16 rows, you have to use shift registers for the rows as well, or switch to 3-to-8 multiplexers. That way you will still have some pins left for other sensors or stuff. Or you could take a look at decent LED controllers.


  7. when i saw the escalator at my local Mall, i imagine how to make that matrixLEDshiftregister, red arrow walking. nice project Alex. i want make it too.


  8. Wow, really cool. I would like to play arround with this, but i’m a little bit confused about how to put your ‘rg_default_main.c’ code into the arduino? Is it possible to post the .pde?



  9. Hi Johnas,
    I just redownloaded the matrix.zip and it contains only a single file: matrix_sketch.pde. Please check again if you got the right file.


  10. great project!

    what other projects regarding led matrix display can you suggest BUT using analab2.0?


  11. Hi

    Your schematic does not show how the 10K pot is connected, please can you advise.



  12. Hi Kevin,
    attach the left pin with GND, the right pin with VCC and the middle pin with analog port 0 (zero) of the Arduino.


  13. hi again

    I have some Le-mm103 bi-colour displays made by sure electronics. I want to use them in place of the one you used. I think the LE-MM103 is common anode rather than common cathode.

    How should I connect it to work correctly



  14. If you use a common anode, then you can not use the ULN2803, because that chip can only sink current, but not source. That means that chip can not provide current for the anodes of your display.


  15. thanks Alex, that is what I was suspecting. Is there an alternative to the ULN2803 for the type of displays I have.


  16. Hi Alex.

    Nice Tutorial.

    Newbie question though:

    Can you tell me why you use 100R resistors please. Don’t they allow too much current through the Red LED’s (according to the spec sheet)?

    Here’s my logic…

    The shift registers output is 5v and you typically want 20MA @ 2.0V for the red LED’s Therefore the resistor should be
    3v/0.02A = 150R minimum



  17. Yes, 150 ohm is more reasonable. On the other hand, if you take into account, that every row lights up only 1/8 of the time, you can use higher currents than 20 mA. Most LEDs can be pulsed with 1/10 duty cycle at 1 kHz and then go up to 50 mA or so.
    The real reason was, I had these laying around. ;)


  18. Thanks Alex.

    Thats very interesting about the LED duty cycle and current. I didn’t know that.


  19. Hi! How to fix this:

    In function ‘void setup()’:
    error: ‘TCCR2A’ was not declared in this scope

    I use Atmega8.


  20. has anyone ese got this working from the schematic? Im not a tard and I know how to build from a schematic , I have all the parts and I built it about 8times and it doesnt work. Just wondering before I try again. Nice project BTW


  21. thanks Alex, that is what I was suspecting. Is there an alternative to the ULN2803 for the type of displays I have.


Comments are closed.