DIY Marquee LED Array Display
Have you ever wanted to have one of those big scrolling LED displays, to keep you up to date on e-mails, news, weather, or stock quotes? In this microcontroller project tutorial, we'll show you how to build an array of 120 LEDs, how to control them all from one microcontroller, and how to talk to it from your computer.
November 3, 2009:
Welcome, visitors! We had such a tremendous response to this video tutorial that we decided to make it into a kit of its own!
Ready to build your own LED array? Find more details on our LED Array Kit page.
- Humberto and Mike
This project makes use of row-column addressing, which lets us control lots of LEDs without having one wire going to each one. We also use one more trick -- the fact that diodes only conduct in one direction -- to double the number of LEDs you can handle with the ATmega168 microcontroller. There are some schemes that you can use to control even more LEDs (see Charlieplexing), but part of the tradeoff is that the LEDs would be on for a shorter fraction of the time, so they'd be less bright. Engineering is all about making smart tradeoffs: here, we're going with somewhat fewer LEDs, but they'll be brighter and the wiring will be much simpler.
Here's what the finished product looks like:
In addition to our USB NerdKit microcontroller kit and a wall DC adapter, you'll need:
|T1-3/4 (5mm) standard LEDs||120||
Our video shows green LEDs, but you can definitely use other colors. Keep in mind that the anode is the longer lead, the cathode is the shorter one (also indicated by a flat section on the plastic case, and by the triangle's tip and line on the schematic), and that current only flows from anode to cathode.
We also used cardboard (roughly 21" by 6"), spray paint, wire (roughly 30-50 feet), and some tissue paper as a display diffuser.
LED Wiring Plan
As described in the video, we make use of row-column addressing, as well as one special trick to double the number of pixels. This schematic illustrates the wiring scheme for the 120 LEDs: (click to enlarge)
If you aren't yet familiar with microcontroller basics or microcontroller programming, then what you first need to know is that each of the 12 column wires and 5 row wires goes to an input/output (I/O) pin of the microcontroller. The software running on the chip can put the pin in one of three states: HIGH (driven to +5 volts), LOW (driven to 0 volts), or FLOATING (undriven -- no current in or out). (There is a fourth state which has to do with pull-up resistors, but this is not important for now.) If a path from HIGH to LOW exists across any LED in the forward direction, current will be allowed to flow, and that LED will light up.
To turn on the top left LED in this schematic, we would make row 0 be LOW, column 0 be HIGH, and simply drive no current onto the other row and column wires. For its neighbor one spot to the right, we would make row 0 be HIGH, column 0 be LOW, and not drive the others. The same pattern applies for all of the 5 row and 12 column wires, so that ultimately we can individually control every LED in the array. Row-column addressing, like shown here, is an extremely common way of reducing "pin count" of systems, which allows for simpler wiring and cheaper parts. For example, numeric keypads, like those on a telephone, have a row-column matrix of wires, so that an individual wire per button is not needed.
Also, you should be aware that when using this kind of scheme, we are adding another complexity: time. With any sort of multiplexing scheme like this one, we can control or read lots of outputs or inputs, but we can't do them all at exactly the same time. That's why we have the microcontroller quickly loop through the various row/polarity combinations, as described more below.
The physical construction of the LED Array is where you get to be creative. Feel free to explore different backboard materials, different spacing between the LEDs, even different patterns (who said it had to be a rectangular array). We started off with a piece of cardboard, and spray painted it blue. We measured and marked out a grid with spaces 3/4" apart, and drilled small 1/16" holes using a drill press. We understand most people do not have a drill press at home, but a handheld drill should work fine, or even a sharp pencil.
Once you have holes in the back board, you need to put the LEDs through. We chose to run the column wires down the back of the array, and run the row wires across the front. This makes it easier to keep them from touching, and shorting out. In order to be able to drive more columns using only one microcontroller we will connect the LEDs using a wiring scheme that takes advantage of the diode properties of LEDs (see wiring plan above). This means we have to very careful to put the anodes of the LEDs through on the first column, then cathodes of the LEDs through on the second column, and keep alternating all the way down the array. On the back, every two LED columns is twisted together with one column wire, and soldered in place. On the front we lay the row wire across the entire row of LEDs, twist the LED pin over the row wire, and solder it together. You can then cut off the excess LED pin on the front using wire strippers, or diagonal cutters if you have them.
Click any photo to enlarge:
Display Interrupt Handler
Since we can only actually have 12 of the 120 LEDs on at any time, we have to switch between the sets fast enough that your eyes can not perceive any blinking. For this, we use the ATmega168's Timer0 timer/counter unit, which we have set to count and overflow every 1.1ms. (For more background on the timer/counters on the microcontroller, see our Servo Squirter tutorial page.) We then designate a special function, called an interrupt handler, to be run every time the counter overflows. When this happens, the microcontroller stops whatever code it's running, and switches to the interrupt handler.
In order to pass information to the interrupt handler, we maintain a big array in memory, which contains information telling us whether each LED is supposed to be on or off. The normal code flow sets those bits high or low, and the interrupt handler reads the appropriate ones and turns the LEDs on or off. Whenever writing interrupt handler code, you have to be mindful that some variables might get modified by the interrupt handler, at times when the normal code flow wouldn't expect them. In this case, it isn't a significant issue, because information only goes one way -- from normal code to the interrupt handler. But if it were to go the other way as well, we'd have to be careful to use the "volatile" keyword to tell the compiler to make fewer assumptions about those variables.
In order for a system like this to be useful for displaying text, you need a way to take characters and transform them into the internal array representation of ON/OFF LEDs. In order to abstract this away from the microcontroller code, we wrote a python font maker. This small program reads a file called font.txt which contains a representation of every character using rows of *s. The font maker python code reads this file and generates a C file called font.h. This font.h file is a giant array which contains every character represented by 5 integers. Each integer represents a whole column, and every bit of each integer is a single LED. Depending on whether the bit is high or low, the LED should be on or off. Loading font.h into our microcontroller code allows us to retrieve the representation of each character we want to display.
Computer to Microcontroller Communication
The computer communicates with the microcontroller through a the serial port. The microcontroller sends a lower case "n" when it is able to take a new character. The computer sends the characters one at a time, and the microcontroller scrolls them across the LED display. We wrote some python code that can accept a connection, and receive text over that socket connection and send it to the LED Array to be scrolled. You can use netcat, to send text to this script, or write your own application that pulls data from the web and pushes it to the LED Array! For example, you can run:
echo "HI MOM!!!" | netcat localhost 6666
to scroll that text across the display. When the text is done scrolling, the display will return to showing the current time.
You can download the source code here. This ZIP file includes both microcontroller-side and PC-side code.
More Videos and Projects!
Take a look at more videos and microcontroller projects!