Introduction: LED Matrix With Game Controller - a First Project
This project was built for my introductory electronics class at the University of Waterloo in Canada. This was my first introduction to electronics and therefore, my first project.
Follow these steps to build a 16 x 16 LED array with a wall (different colored LED on the border), powered by an Arduino Mega 2560. I have also included the construction details of a game controller.
This project is ideal for a beginner, as it introduces you to several fundamental elements in practical electronics. You can learn how to read data sheets, how to solder, how to work with LEDs, multiplex to save output lines, program a microcontroller through an Arduino, how to take practical factors into account in design, resistances and analog considerations in digital circuits, etc..
At the end of the project, you should be well equipped to move on to more complex ones such as LED cubes or rotating displays.
Follow these steps to build a 16 x 16 LED array with a wall (different colored LED on the border), powered by an Arduino Mega 2560. I have also included the construction details of a game controller.
This project is ideal for a beginner, as it introduces you to several fundamental elements in practical electronics. You can learn how to read data sheets, how to solder, how to work with LEDs, multiplex to save output lines, program a microcontroller through an Arduino, how to take practical factors into account in design, resistances and analog considerations in digital circuits, etc..
At the end of the project, you should be well equipped to move on to more complex ones such as LED cubes or rotating displays.
Step 1: Multiplexing Basics
In order to have an LED Matrix of any significant size, you'll need to control more LEDs than you have outputs. You can multiplex your LEDs to accomplish this. It works like this:
The LEDs are arranged in a rectangle. The anodes are soldered together in one direction and the cathodes are soldered together perpendicular to the anodes. When all cathodes are high and anodes are low a negative voltage is applied to every LED, and if the voltage is low enough (see 'choosing LEDs' in components section), it will not breakdown. To turn on a particular LED, turn its cathode line low and anode line high. For example, to turn on LED (4,2) in the diagram, you would set segB to high and Dig4 to low.
The setup uses POV (Persistence of Vision) to draw an image on the array. Each LED required to draw the image is turned on one at a time, and looped very fast, giving the illusion that all LEDs are on at the same time (see programming section).
In this project, I used the Arduino Mega 2560, and assigned 16 outputs to cathodes and 16 to anodes. I used the HIGH voltage output (5V) as the power source. This was done for simplicity in a first project. However, if you can, you should avoid using a digital signal as a power source. There are a few reasons for this. Digital outputs are signals, and aren't designed to provide much current. You can breach the per-line or overall current limit of your microcontroller (40mA per line supply/sink for the Atmega) if your circuit draws much power. Also, the digital outputs in the Arduino and virtually every board are directly wired to the microcontroller without a fuse. This makes the microcontroller vulnerable to errors in your wiring. A simple short circuit, for example, can potentially render the pins or the microcontroller useless. However, the main fuse that limits overall current draw should prevent this.
The Ideal solution is to use the digital outputs to turn on/off a transistor at each line, and use an external power supply to drive the circuit. This increases wiring complexity, and of course, you need an external power supply for this. If you are building this as practice before you build more complex projects like a cube, you should consider going the transistor route. If you are doing this as a small project, your LED array won't be too large, or you're constrained on time / cost, then you can skip using transistors.
If you are using an external power supply, you can also consider using shift registers and the function shift_out to increase the number of your output pins. This increases wiring and programming complexity, although it is almost impossible to construct projects that needs the controlling of large numbers of LEDs, like a cube, without it.
The LEDs are arranged in a rectangle. The anodes are soldered together in one direction and the cathodes are soldered together perpendicular to the anodes. When all cathodes are high and anodes are low a negative voltage is applied to every LED, and if the voltage is low enough (see 'choosing LEDs' in components section), it will not breakdown. To turn on a particular LED, turn its cathode line low and anode line high. For example, to turn on LED (4,2) in the diagram, you would set segB to high and Dig4 to low.
The setup uses POV (Persistence of Vision) to draw an image on the array. Each LED required to draw the image is turned on one at a time, and looped very fast, giving the illusion that all LEDs are on at the same time (see programming section).
In this project, I used the Arduino Mega 2560, and assigned 16 outputs to cathodes and 16 to anodes. I used the HIGH voltage output (5V) as the power source. This was done for simplicity in a first project. However, if you can, you should avoid using a digital signal as a power source. There are a few reasons for this. Digital outputs are signals, and aren't designed to provide much current. You can breach the per-line or overall current limit of your microcontroller (40mA per line supply/sink for the Atmega) if your circuit draws much power. Also, the digital outputs in the Arduino and virtually every board are directly wired to the microcontroller without a fuse. This makes the microcontroller vulnerable to errors in your wiring. A simple short circuit, for example, can potentially render the pins or the microcontroller useless. However, the main fuse that limits overall current draw should prevent this.
The Ideal solution is to use the digital outputs to turn on/off a transistor at each line, and use an external power supply to drive the circuit. This increases wiring complexity, and of course, you need an external power supply for this. If you are building this as practice before you build more complex projects like a cube, you should consider going the transistor route. If you are doing this as a small project, your LED array won't be too large, or you're constrained on time / cost, then you can skip using transistors.
If you are using an external power supply, you can also consider using shift registers and the function shift_out to increase the number of your output pins. This increases wiring and programming complexity, although it is almost impossible to construct projects that needs the controlling of large numbers of LEDs, like a cube, without it.
Step 2: Components
To build the array and the controller, I used these parts.
- An Arduino Mega 2560
- LEDs (256)
- Resistors (16)
- Prototyping board
- Push buttons (8)
- Breadboard
- IDE / ribbon cables (2)
- Interconnects
- Wires and wirecutter
- Soldering iron and solder
- Insulating tape
- I chose the Arduino Mega because I wanted to build a 16 x 16 matrix, and needed 32 outputs for this. You can use the Arduino Uno (14 I/O pins), but you will be limited to a 7x7 matrix and no space for a controller.
- For an n x n matrix, the number of LEDs required increase as a function of n^2. So the larger you go, the number of LEDs, and your soldering time increases exponentially. Take this into consideration.
- Look for volume discounts (price breaks). The LEDs I used were 365-1189-ND and 365-1190-ND from digikey, using the 100 and 250 price breaks.
- The best place to buy LEDs from however, is ebay. You don't need very high quality components for such projects, and you can easily get 1000 LEDs for around $30 from ebay, if you can wait for shipping from China.
- IDE cables have a missing pin in the middle. But almost all electronics stores have ribbon cables in the old format, without the missing pin.
When choosing your LEDs, consider a few things:
- As the LED array is meant to be viewed from the front only, you don't need LEDs that have a large viewing angle.
- They should function within your input voltage range and current.
- They should have a high enough MCD rating so that they are bright enough at a very low duty cycle (1 / no. of LEDs).
- You should ensure that the LEDs can withstand a negative power voltage (check I-V curve in datasheet).
- An Arduino Mega 2560
- LEDs (256)
- Resistors (16)
- Prototyping board
- Push buttons (8)
- Breadboard
- IDE / ribbon cables (2)
- Interconnects
- Wires and wirecutter
- Soldering iron and solder
- Insulating tape
- I chose the Arduino Mega because I wanted to build a 16 x 16 matrix, and needed 32 outputs for this. You can use the Arduino Uno (14 I/O pins), but you will be limited to a 7x7 matrix and no space for a controller.
- For an n x n matrix, the number of LEDs required increase as a function of n^2. So the larger you go, the number of LEDs, and your soldering time increases exponentially. Take this into consideration.
- Look for volume discounts (price breaks). The LEDs I used were 365-1189-ND and 365-1190-ND from digikey, using the 100 and 250 price breaks.
- The best place to buy LEDs from however, is ebay. You don't need very high quality components for such projects, and you can easily get 1000 LEDs for around $30 from ebay, if you can wait for shipping from China.
- IDE cables have a missing pin in the middle. But almost all electronics stores have ribbon cables in the old format, without the missing pin.
When choosing your LEDs, consider a few things:
- As the LED array is meant to be viewed from the front only, you don't need LEDs that have a large viewing angle.
- They should function within your input voltage range and current.
- They should have a high enough MCD rating so that they are bright enough at a very low duty cycle (1 / no. of LEDs).
- You should ensure that the LEDs can withstand a negative power voltage (check I-V curve in datasheet).
Step 3: The Prototype
Before starting to solder anything, get some practice by building a prototype. It will let you iron out any problems and have a feel for how things work before you spend 10+ hours on soldering. You can do pretty much everything, including programming, on the prototype, and expand it to a larger array.
Use the breadboard and LEDs to build a 4x4 prototype. There will be lot of wires on the breadboard, so it will be neater to use short wires. Follow the diagram above, or build from your own logic, with your knowledge of multiplexing.
Refer to the coding section for programming basics of the prototype.
Use the breadboard and LEDs to build a 4x4 prototype. There will be lot of wires on the breadboard, so it will be neater to use short wires. Follow the diagram above, or build from your own logic, with your knowledge of multiplexing.
Refer to the coding section for programming basics of the prototype.
Step 4: Construction
Spread clear glue over a line of holes in the prototyping board and drop LEDs through them. Let the glue dry and hold the LEDs in place. This is important because once you flip the board over to start soldering, there is nothing to hold the LEDs in place to make sure it's facing front.
Once the glue is dry, bend the anode pins from the base in one direction. Solder them together in a line. Glue the next line of LEDs and continue the process. Test each line after soldering, by connecting power to the common end and touching a GND wire to the cathode pins. Once this is complete (the photo was taken at this point) apply insulating tape over each line. This will ensure that the cathode pins don't accidentally make contact with the anode pins and create a short circuit.
Now bend the cathode pins in the other direction over the anode pins, and bend them not from the base, but from half way up. This will avoid the pins from being folded over the anodes. Solder them together.
Once this is done, solder resistors to the end of the anode lines. Then take each end and solder them to the tip of an interconnecting pin.
When soldering, make sure you don't overheat the LED pins. This will oxidize the pin and increase the resistance or damage the LED, causing them to glow dimmer than normal. To avoid this, apply heat to the solder and let it drop onto the joints. Soldering to the interconnect will be difficult if you don't use a prototyping board that has copper leads on the bottom. Make sure to take out any extra solder to avoid shorts. Also, don't breathe in directly from the area of soldering. The solder contains lead. You should work in a well ventilated area, and take frequent breaks.
Depending on your soldering experience and size of the LED matrix, it could take anywhere from a few hours to 24+ hours. This was my first time soldering, and the 16 x 16 (512 solder points for just the LEDs + soldering to interconnect) took me around 20 hours to finish. I did them in 2 days, so I was exhausted and made several mistakes. For one, I bent the cathode pins directly over the anode pins instead of leaving some space above. This left the pins extremely close to each other and separated from contact only by the insulating tape. Some of the insulating tape will melt if soldered that close, so it was a close call. And secondly, I completely forgot to add the resistors in series with the anodes. This would probably have required a re-wiring, if it was not for my horrible soldering, that burnt the LED pins and increased resistance on each line. So it worked out to be okay in the end.
Once the glue is dry, bend the anode pins from the base in one direction. Solder them together in a line. Glue the next line of LEDs and continue the process. Test each line after soldering, by connecting power to the common end and touching a GND wire to the cathode pins. Once this is complete (the photo was taken at this point) apply insulating tape over each line. This will ensure that the cathode pins don't accidentally make contact with the anode pins and create a short circuit.
Now bend the cathode pins in the other direction over the anode pins, and bend them not from the base, but from half way up. This will avoid the pins from being folded over the anodes. Solder them together.
Once this is done, solder resistors to the end of the anode lines. Then take each end and solder them to the tip of an interconnecting pin.
When soldering, make sure you don't overheat the LED pins. This will oxidize the pin and increase the resistance or damage the LED, causing them to glow dimmer than normal. To avoid this, apply heat to the solder and let it drop onto the joints. Soldering to the interconnect will be difficult if you don't use a prototyping board that has copper leads on the bottom. Make sure to take out any extra solder to avoid shorts. Also, don't breathe in directly from the area of soldering. The solder contains lead. You should work in a well ventilated area, and take frequent breaks.
Depending on your soldering experience and size of the LED matrix, it could take anywhere from a few hours to 24+ hours. This was my first time soldering, and the 16 x 16 (512 solder points for just the LEDs + soldering to interconnect) took me around 20 hours to finish. I did them in 2 days, so I was exhausted and made several mistakes. For one, I bent the cathode pins directly over the anode pins instead of leaving some space above. This left the pins extremely close to each other and separated from contact only by the insulating tape. Some of the insulating tape will melt if soldered that close, so it was a close call. And secondly, I completely forgot to add the resistors in series with the anodes. This would probably have required a re-wiring, if it was not for my horrible soldering, that burnt the LED pins and increased resistance on each line. So it worked out to be okay in the end.
Step 5: Game Controller
The controller can be wired on the breadboard or soldered to a PCB. I used a breadboard because it felt better to hold than a PCB, and I could re-arrange the buttons to my liking at any time.
The Atmel chips that the Arduinos use have a built-in 20KOhm pull-up resistor. We will use this for our input ports. Use the input_pullup tutorial to learn how to use it. The pins are defined using pinMode as input_pullup, and set HIGH with digitalWrite. This input pin can be pulled low through a switch connected to GND. Therefore, in your controller, you should connect one side of each pushbutton to an input port. The other side should be connected to a GND. When the button is depressed, digitalRead(pin) will be pulled LOW, and return to HIGH when released.
The buttons also need to be debounced. Check the debouncing tutorial to learn how. The tutorial doesn't make use of the built-in pull-up resistor, and instead uses an external pull-down resistor.
The Atmel chips that the Arduinos use have a built-in 20KOhm pull-up resistor. We will use this for our input ports. Use the input_pullup tutorial to learn how to use it. The pins are defined using pinMode as input_pullup, and set HIGH with digitalWrite. This input pin can be pulled low through a switch connected to GND. Therefore, in your controller, you should connect one side of each pushbutton to an input port. The other side should be connected to a GND. When the button is depressed, digitalRead(pin) will be pulled LOW, and return to HIGH when released.
The buttons also need to be debounced. Check the debouncing tutorial to learn how. The tutorial doesn't make use of the built-in pull-up resistor, and instead uses an external pull-down resistor.
Step 6: Coding
The Arduino can be programmed through its own development environment and the language is very close to C++. Use Arduino's reference page and work through the examples before you start coding. Depending on your programming experience, balance coding efficiency vs. intuitiveness.
If you are new to programming, focus on making your code as intuitive as possible. Efficiency comes second. Here is a simple way to get started by displaying a static image. Try this on the prototype:
- This coding scheme assumes that all cathode lines are wired in sequence and anodes are wired in another sequence. The global variables are as follows:
int loop_delay = 1; // The time (in ms) each LED in a loop will be kept on to achieve POV.
int cathode_start; // The first cathode pin. The starting pin of the cathode sequence.
int anode_start; // The first anode pin. Starting pin of the anode sequence.
- Declare and initialize with global scope (i.e, outside void_loop() and void_setup()) a 4 x 4 array to display "J" like this. We will write the contents of this array to our prototype. Each 1 is an on and 0 is off.
int disp[4][4] = {
{1,1,1,1},
{0,0,1,0},
{1,0,1,0}.
{0,1,0,0}};
- Initialize the LEDs to all off in void_setup(), i.e, all anodes LOW and all cathodes HIGH.
void setup(){
for (int i=0; i<4; i++)
{
digitalWrite (i + cathode_start, HIGH);
digitalWrite (i + anode_start, LOW);
}
}
- Write a function that loops through the corresponding LEDs.
void lightup(){
for (int i=0; i<4; i++)
for (int j=0; j<4;j++)
if (disp[i][j] == 1)
{
digitalWrite (i + anode_start, HIGH); // Turn on LED (i,j)
digitalWrite (j + cathode_start, LOW);
delay (loop_delay); // Keep on for this length to achieve refresh rate
digitalWrite (i + anode_start, LOW);
digitalWrite (j + cathode_start, HIGH); // Turn off LED (i,j)
}
}
- Call the above function to display the contents of our 2 dimensional array (disp) in void_loop() to run continuously.
void_loop()
{
lightup();
}
Your prototype should be displaying our 4x4 array now.
If you want to make games on the display, the above can function as your "game engine". Think of your array as your game screen and write functions to update the array to display your game in void_loop(). Calling lightup() function at the end will then print the array's contents to screen.
If you are new to programming, focus on making your code as intuitive as possible. Efficiency comes second. Here is a simple way to get started by displaying a static image. Try this on the prototype:
- This coding scheme assumes that all cathode lines are wired in sequence and anodes are wired in another sequence. The global variables are as follows:
int loop_delay = 1; // The time (in ms) each LED in a loop will be kept on to achieve POV.
int cathode_start; // The first cathode pin. The starting pin of the cathode sequence.
int anode_start; // The first anode pin. Starting pin of the anode sequence.
- Declare and initialize with global scope (i.e, outside void_loop() and void_setup()) a 4 x 4 array to display "J" like this. We will write the contents of this array to our prototype. Each 1 is an on and 0 is off.
int disp[4][4] = {
{1,1,1,1},
{0,0,1,0},
{1,0,1,0}.
{0,1,0,0}};
- Initialize the LEDs to all off in void_setup(), i.e, all anodes LOW and all cathodes HIGH.
void setup(){
for (int i=0; i<4; i++)
{
digitalWrite (i + cathode_start, HIGH);
digitalWrite (i + anode_start, LOW);
}
}
- Write a function that loops through the corresponding LEDs.
void lightup(){
for (int i=0; i<4; i++)
for (int j=0; j<4;j++)
if (disp[i][j] == 1)
{
digitalWrite (i + anode_start, HIGH); // Turn on LED (i,j)
digitalWrite (j + cathode_start, LOW);
delay (loop_delay); // Keep on for this length to achieve refresh rate
digitalWrite (i + anode_start, LOW);
digitalWrite (j + cathode_start, HIGH); // Turn off LED (i,j)
}
}
- Call the above function to display the contents of our 2 dimensional array (disp) in void_loop() to run continuously.
void_loop()
{
lightup();
}
Your prototype should be displaying our 4x4 array now.
If you want to make games on the display, the above can function as your "game engine". Think of your array as your game screen and write functions to update the array to display your game in void_loop(). Calling lightup() function at the end will then print the array's contents to screen.
Step 7: Conclusion
If everything went well, you can now play around with the coding to make games and display patterns on your array.
If you didn't use them in this project, get a power supply and learn how to channel power to the circuit using transistors. Also get some shift registers and learn how to use shift_out to increase your number of output pins. Once you finish these, you are good to go for more complex projects involving LEDs.
For the PHYS 353 class - my project proposal was different from my final project. The original proposal was to build a 2 player racing game where a player would advance if he/she could press a button at the exact time a rolling LED light hit bottom. I changed it because the original proposal was too simple to build with the Arduino. However, I made an implementation without using a microcontroller in logisim and the sketch is uploaded here. From Simulation menu, set tick frequency to 32 and choose "ticks enabled". Use the 2 buttons to play the game. How can you cheat in this game? How would you fix it?
If you didn't use them in this project, get a power supply and learn how to channel power to the circuit using transistors. Also get some shift registers and learn how to use shift_out to increase your number of output pins. Once you finish these, you are good to go for more complex projects involving LEDs.
For the PHYS 353 class - my project proposal was different from my final project. The original proposal was to build a 2 player racing game where a player would advance if he/she could press a button at the exact time a rolling LED light hit bottom. I changed it because the original proposal was too simple to build with the Arduino. However, I made an implementation without using a microcontroller in logisim and the sketch is uploaded here. From Simulation menu, set tick frequency to 32 and choose "ticks enabled". Use the 2 buttons to play the game. How can you cheat in this game? How would you fix it?