Netduino Controlled LED Cube
- Posted: Nov 07, 2011 at 2:50 PM
- 50,728 Views
- 25 Comments
Loading User Information from Channel 9
Something went wrong getting user information from Channel 9
Loading User Information from MSDN
Something went wrong getting user information from MSDN
Loading Visual Studio Achievements
Something went wrong getting the Visual Studio Achievements
Commercial LED cubes have been around for a while now and can be quite large and capable of some interesting displays. This project is on a smaller scale and goes through the steps required to build a Netduino Mini-controlled 512 LED cube (8 x 8 x 8).
Clearly, the Mini does not have 512 pins and so we use Persistence of Vision to control the LEDs in the cube. The result is that seven pins on the Netduino Mini can control 512 LEDs.
At the end of this article you should be able to create a cube capable of the following:
Description | Quantity |
LEDs (I chose blue) | 512 |
74HC595 Shift Registers | 8 |
74HC238 3 to 8 line decoder | 1 |
Netduino Mini | 1 |
16 Pin DIL Socket (0.3") | 9 |
24 Pin DIL Socket (0.6") | 1 |
TIP122 NPN Transistor | 8 |
100nF Ceramic Capacitor | 10 |
2.2K Resistor | 8 |
68 Ohm 0.25W Resistor (you may need to change these depending upon the LED you choose) | 64 |
8 Way Single Row Socket | 9 |
36 Way Header Strip (Straight) | 3 |
2 Way Single Row Socket | 2 |
2 Way PCB Mount Terminal Connector | 1 |
8 Way Cable (ribbon or alarm) | 2.5m |
Wire | Miscellaneous |
Pad board 160 x 115 Hole | 1 |
Hex PCB spacer and screw (M3 threaded) | 4 |
The following items are also required:
You may need to shop around for the LEDs. I went to my preferred supplier and was quoted 18.5 pence each. By going on eBay I managed to negotiate the price down to 2.5 pence each including delivery. You are buying in bulk so don’t be afraid to ask for a good bulk price. Also, buy a few more than you need in case you have a faulty part or break a few.
This is probably the most time consuming part of the project requiring a lot of patience and testing. The aim of the project is to convert these 512 blue LEDs:
Into this:
The exact dimensions of the cube depend upon the length of the legs on the LEDs. The legs are soldered together with the cathodes of the LEDs forming a horizontal plane and the anodes vertically connecting the layers. Each of the layers are configured into a common cathode display.
Take one of the LEDs and bend the cathode at a point as close to the body of the LED as possible. The cathode should be at 90 degrees to the anode and parallel to the flat base of the LED. This allows the horizontal legs to be soldered together to form the common cathode.
Shaping the anode is a little more difficult. The anodes connect the horizontal layers and so need to be shaped to ensure that each LED in the layer is directly above the corresponding LED in the layer below. Otherwise, each layer will be slightly offset from the one beneath it.
To achieve this, bend the anode in the opposite direction as described for the cathode. Then, still working with the anode, start about 2mm along the leg. Finally, bend the top 2-3mm through and fashion an angle at 90 degrees pointing back to the LED body. The end result should look like this:
The horizontal leg is the cathode and the vertical leg is the anode. When these are connected it should look like this:
Notice how the shaped anode allows the LEDs to be placed above each other.
Now repeat this process for the remaining 511 LEDs.
Our next task is to connect the LEDs together. This is best achieved by using a template for the individual layers. Use a piece of wood with a grid of holes drilled into it as the template.
Measure the distance from the center of the now horizontal cathode to the end of the leg and subtract about 2mm. This will tell you how far apart the LEDs are in the horizontal plane. The 2mm will be used to overlap with the neighboring LED and will connect the cathodes. Now drill an 8 x 8grid of holes, each of which are distance of your choosing apart. Here they are 20mm apart.
The holes need to be large enough to hold the LEDs securely but should not be so small that the LEDs are wedged into the hole and difficult to remove.
Take eight of the LEDs and place them along the top row with the cathodes all pointing to either the right or the left. If all are pointing to the right, the cathodes of seven LEDs will overlap slightly with the next LED to the right. The rightmost cathode will go off into space. Solder the cathodes together.
Now let’s add the remaining LEDs in the layer. I started on the left because I hold the soldering iron in my right hand. Take another seven LEDs and place these under the top row and down the far left column. The cathode of each first LED should overlap the cathode of the LED in the top layer. Solder these together.
Repeat with the remaining columns. At this point, you should have a horizontal row of LEDs connected together with eight strings of seven LEDs hanging from it. Eventually you should have something looking like this:
Now test the layer using a power supply and current limiting resistor. A 5V supply and a 68 Ohm resistor are adequate. Ground the cathode of the LED pointing off into space. Now touch each leg of the LEDs in turn with the positive output of the supply (through the current limiting resistor, of course). Each LED should light up and as you touch the anode.
One final bit of soldering is needed to add a stiffening wire to the layer. Cut and strip a piece of wire. The wire should be long enough to cross the entire layer. Place the stripped wire on the cathodes of the LEDs at the bottom of the string and solder it on to each.
At this point you will have one complete layer. Remove the layer from the template. This should be done carefully so that you do not put too much stress on the joints. Gently lifting it up with a screwdriver should help. Don’t hurry. Put this layer to one side and repeat another seven times.
Now that all of the layers are built, test them again. This repeated test may save you future pain. Just imagine how difficult it will be to fix a bad joint in the middle of the cube. Check that the LEDs are still connected and none of the joints were broken when layers were removed from the template.
Now drop one of the layers back into the template. What we now need to do is to place a second layer on top of the first so that the anodes of the layer in the template touch the anodes of the LEDs in the upper layer. Once in place, we need to solder the anodes together, and so need a way of supporting the top layer whilst connecting the LEDs. A strip of cardboard can accomplish this. Cut the cardboard into strips (making sure the cardboard is the height needed to support the layer) and bind the strips together with tape. Two of these strips should be enough to support the layer. Here’s how it looked when several layers had already been connected:
Now that you have the layer supported, solder the anode of each LED in the top layer to the anode of the corresponding LED in the layer directly beneath it. Once this has been done, test each LED. Connect the cathode of the bottom layer to ground and touch each of the legs on the top layer in turn with the positive supply (going through the current limiting resistor). The LED on the bottom layer should light up. Repeat for each LED in the layer. Move the cathode to the top layer and repeat the test—this time the LED on the top layer should light. Again, repeat for each LED in the layer.
Now add the remaining layers. Just for safety, test every layer in the cube as it is built up. This repeated testing sounds like a big pain, but trust me it's worth it. At this point you will have a cube of LEDs looking something like this:
Now trim the cathodes that are still flying off into space.
The controller board will allow any of the 512 LEDs in the cube to be turned on by a Netduino Mini using only seven pins.
Connecting the anode to a positive supply while grounding the layer in which the LED is located can turn on any LED. You can also do this with more than one LED in a layer, and so within a layer can turn on 0 to 64 LEDs. To light the entire cube, we need to switch on each layer in turn while doing so fast enough to give the impression of static image. This is where the principle of Persistence of Vision comes into play.
The basic algorithm is as follows:
If we do this fast enough, the human eye will see a single image as when watching TV or a movie.
Our basic building blocks for the controller are as follows:
The shift registers determine which LEDs are turned on. The board has a series of eight cascaded 74HC595 registers. This allows us to control 64 LEDs (8 chips x 8 outputs). The following schematic shows how two of these registers should be wired together:
The above should be repeated until you have eight shift registers cascaded.
The output from the register is 5V and will give more than enough power to burn out an LED, so we need to put a current limiting resistor in the circuit. A 68 Ohm resistor is required for the LEDs in this project—make sure you verify the value you choose against the LEDs used.
Putting this together gives the following layout:
Each socket holds a 74HC595 shift register. The connections are identical for each register with the data cascaded into the next register. So if we look at the bottom left socket you will see the following:
The following colors have been used:
Note that the connectors and current limiting resistor are slightly offset as output 0 is on pin 15 and this is routed on the underside of the board. The remaining connections are a direct one to one path from the pin to the resistor/connector.
The microcontroller uses the Serial Peripheral Interface (SPI) bus to tell the shift registers which LED to turn on. The data from the registers is cascaded and so we can store 64 bits of information (1 bit for each LED in a layer). With logic 1 turning a particular LED on and 0 turns the LED off.
The layer switching logic allows the controller to connect any one of the layers to ground (using a common cathode). Coupling this with the LED selection logic above allows us to turn on any one of the LEDs in the cube. This is achieved by using a transistor as a switch. The TIP122 was selected because it is capable of sinking 2A. This may seem like a lot considering the 25mA for each LED, but remember that we potentially have 64 LEDs being turned on at once. This means we may be drawing 1.6A of current. If you use a different LED, you will need to verify that the shift registers, power supply, and the transistor are capable of dealing with the amount of power you will be drawing.
The schematic for the layer switching looks like this:
The 74HC238 has three input signals. These represent a binary number 0-7. The chip converts this number into eight output lines. 0 turns on line 0, 1 turns on line 1 etc. The output from each line (0 through 7) is then fed in the base of a TIP122 transistor. This turns on the appropriate layer by connecting the layer through to ground.
One line for the Netduino Mini controls enable line on the 72HC238 chip. This allows us to turn all of the outputs off whilst a new value is being loaded into the chip. This line is used to make sure that the transitions between the states are “invisible.” Without this there is the chance that the viewer may see a flickering effect when new values are loaded into both the 74HC595s and the 74HC238.
The completed controller board looks something like this:
Note that there are a pair of connectors to the top right and bottom left of the Netduino Mini. The pair at the top right breakout the COM1 port. The two at the bottom left allow grounding of an FTDI lead (connected to the controller board) and also one socket that is not connected. This allows the 5V lead on the FTDI connector to have a place to live and not be in danger of touching something it shouldn’t.
And on the underside:
The final task (from a hardware point of view) is to connect the cube to the controller board. I tried both ribbon cable and alarm cable. The alarm cable was a little more difficult to connect but was flexible. The ribbon cable was easier to work with but was not as flexible. The principle is the same whichever you chose.
Place the cube on a flat surface (the anodes touching the surface) with one face of the cube facing you. The connections should be made so that the lower back left corner is co-ordinate (0, 0, 0). The co-ordinates increase moving to the right, towards you and up. So looking at the controller board above, shift register 0 connects to the LEDs farthest away from you with output 0 from the register connecting to the LED to the far left. Cut and make the 8 cables according to this pattern varying the lengths to suit the location of the controller board with respect to the cube. Each cable will need a single eight-way socket on one end with the other end connected to the appropriate LED.
The layer selection logic should be connected using a similar cable. Each layer should be connected to a TIP122 with layer 0 being the bottom layer.
The cube will then look like this:
Connecting it up to the controller:
I found it easier to be consistent and wire each plug and LED identically, and so all of the connections above have a black wire to the right of the connector. It helps with connecting things up later.
If we have everything connected then we only need one more thing...
The software running the cube needs to perform two main tasks:
These two tasks need to be performed at the same time (or so fast that they appear to run at the same time). Luckily the .NET Micro Framework has a built in mechanism to allow us to do this—threading. Threading allows us to do this by running two tasks interleaved. So task 1 will run for a while, the system will then switch and run task 2 for a while, then back to task 1 and so on.
To do this, the software is split into two parts, the main program that decides what to display and a separate class that runs the display.
This class has only one purpose in life, to output data to the controller board and so “run” the display. It is a relatively simple class containing the following methods:
The constructor sets everything up by instantiating an instance of the SPI class and setting the buffer (which contains the data to be displayed) to be empty, effectively clearing the cube:
public LEDCube()
{
config = new SPI.Configuration(SPI_mod: SPI.SPI_module.SPI1,
ChipSelect_Port: Pins.GPIO_PIN_20,
ChipSelect_ActiveState: false,
ChipSelect_SetupTime: 0,
ChipSelect_HoldTime: 0,
Clock_IdleState: true,
Clock_Edge: true,
Clock_RateKHz: 100); spi = new SPI(config);
buffer = new byte[64];
for (int index = 0; index < buffer.Length; index++)
{
buffer[index] = 0;
}
}The buffer is 64 bytes of data, 8 rows of 8 bytes. Each byte corresponds to a vertical layer in the cube.
The UpdateBuffer method allows the calling program to change what is displayed in the cube. A little control is needed here to ensure that the pattern displayed in the cube is consistent, and so locking is used to ensure that the buffer cannot be updated part way through a display cycle:
public void UpdateBuffer(byte[] newValues)
{
lock (buffer)
{
for (int index = 0; index < buffer.Length; index++)
{
buffer[index] = newValues[index];
}
}
} The final method in this class is the method spawned off into it’s own thread:
public void DisplayBuffer()
{
while (true)
{
lock (buffer)
{
byte[] displayData = new byte[8]; for (int row = 0; row < 8; row++)
{
int offset = row * 8; for (int index = 0; index < 8; index++)
{
displayData[index] = buffer[offset + index];
} enable.Write(true);
bit0.Write((row & 1) != 0);
bit1.Write((row & 2) != 0);
bit2.Write((row & 4) != 0);
spi.Write(displayData);
enable.Write(false);
}
}
}
}This method again uses the lock statement to lock the buffer. This means we cannot update the buffer until a full cube of data has been displayed. The method takes a block of eight bytes representing a layer and then writes this to the shift registers using SPI. Note that the spi.Write is embedded in the write to the enable line. This ensures that all of the layers are turned off whilst we are updating the shift registers.
Wrapping all of this in the LEDCube class means that we now have a very simple class where we can spawn the DislpayBuffer method into its own thread.
The main program sets the cube up and then controls what is actually shown in the cube. The first thing we need to do is to set up a buffer to store the cube display and then spawn off the DisplayBuffer into its own thread.
private static LEDCube cube = new LEDCube(); private static byte[] newFrame = new byte[64]; ClearCube(); Cube.UpdateBuffer(newFrame); Thread display = new Thread(new ThreadStart(cube.DisplayBuffer)); display.Start();
The ClearCube method is trivial and simply sets all of the bytes in newFrame to zero. At this point we have a cube with a whole lot of nothing going on. The next thing we need to do is to add some effects.
Now that we have the mechanism to control the cube, we simply need to work out what we want to display. The rain effect in the opening video illustrates the majority of the techniques used to control the cube.
The rain algorithm is as follows:
To do this we will create two methods:
private static void AddDrops(int count, int plane = -1)
{
for (int drops = 0; drops < count; drops++)
{
bool findingSpace = true;
while (findingSpace)
{
int x = rand.Next() % 8;
int y = rand.Next() % 8;
int z; if (plane == -1)
{
z = rand.Next() % 8;
}
else
{
z = plane;
} int position = (z * 8) + y;
byte value = (byte) ((1 << x) & 0xff);
if ((newFrame[position] & value) == 0)
{
newFrame[position] |= value;
findingSpace = false;
}
}
}
}AddDrops simply adds a specified number of rain drops to the buffer. It also makes sure that if asked for 10 it will always add 10 by checking to see if one exists in the location it wanted to use (check out the findingSpace variable):
private static void Rain(int noDrops, int cycles)
{
ClearCube();
AddDrops(noDrops);
cube.UpdateBuffer(newFrame);
for (int currentCycle = 0; currentCycle < cycles; currentCycle++)
{
int bitCount = 0;
for (int plane = 0; plane < 8; plane++)
{
byte value = 1;
for (int bit = 0; bit < 8; bit++)
{
if ((newFrame[56 + plane] & value) > 0)
{
bitCount++;
}
value <<= 1;
}
}
for (int plane = 7; plane > 0; plane--)
{
for (int currentByte = 0; currentByte < 8; currentByte++)
{
newFrame[(plane * 8) + currentByte] = newFrame[((plane - 1) * 8) + currentByte];
}
}
for (int b = 0; b < 8; b++)
{
newFrame[b] = 0;
}
AddDrops(bitCount, 0);
cube.UpdateBuffer(newFrame);
Thread.Sleep(75);
}
}Rain adds the specified number of drops to the newFrame, shows the drops, and then moves them all down by one horizontal plane. Before doing this, it counts how many drops are going to fall out of the cube (the number in the bottom horizontal plane). After all the drops have moved down one plane it adds the drops which have disappeared out of the bottom of the cube back in at the top in random positions.
We can display this by adding the following to the main program:
while (true)
{
Rain(10, 60000);
} Full source code at http://netduinoledcontrol.codeplex.com/.
If you add your own effects, remember to consider the rating of the equipment and parts used. Turning on a full horizontal layer consumes 1.6A for the LEDs alone. Things can get warm/hot if every LED in the cube is turned on at the same time so avoid such effects.
When I started out on this project I was not sure how fast the Netduino Mini could control the cube. It has proven that it is up to the task of keeping the display running whilst also being able to work on the upcoming “frame.” If you have gone this far and built a cube then how about expanding it:
Where will you take it?
Mark Stevens is a software engineer and self-confessed technology addict. Mark started in the computer industry in the 1980s and at the age of 16 wrote a disk operating system for a then state of the art 8-bit microprocessor (despite the fact the machine already had one). He did it for fun and has been hooked on IT ever since then. Mark currently writes line of business applications for Win32, ASP.NET, and Silverlight platforms. He has recently taken up microelectronics as a hobby and has since discovered that his wife does not like the smell of solder. Marks’ current exploits, thoughts, and contact information can be found on his blog.
Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation,
please create a new thread in our Forums,
or
Contact Us and let us know.
Follow the Discussion
Oops, something didn't work.
What does this mean?
Following an item on Channel 9 allows you to watch for new content and comments that you are interested in. You need to be signed in to Channel 9 to use this feature.What does this mean?
Following an item on Channel 9 allows you to watch for new content and comments that you are interested in and view them all on your notifications page.sign up for email notifications?
That's amazing! I love this thing! Will you sell one?
@ilija injac: we have all the instructions to build it yourself!
I have to tell you, I really like this one!
Great work Mark.
Long, long time ago (when I still had a lot of hair ...) I made a 10x10 matrix of LED's, but sir, you pwned! Gread work!
Impressive. Most impressive.
Thanks for all of the positive comments. It really was fun to do.
Regards,
Mark
This so cool I started building one of these 2 weeks ago and figured I would build the cube then look for a way to get it working great timing THANKS!
Great work! What size LEDs did you use - 5mm or 8mm? What did the total size of your cube end up being? I built a 3x3x3 last week using 3mm LEDs I had in stock. Now I really want something bigger!
BTW, You have my dream job...
Now imagine the cube loaded in the trunk of a car and connected to a BBS Amplifier. That would be like so extreme <a href="http://www.inchirieriauto.com">:)</a>
@Ian: I used 5mm LEDs and this gave me a cube which was 16 cm along each edge. There is a little bit of play in the dimensions as you are manually shaping the legs.
Hola, muy buen proyecto, me pregunto si funcionará igual con una tarjeta netduino de tamaño normal y en que varían las terminales, ojalá pueda contestarme, Gracias
[quote]
3 minutes ago
Hello, very good project, I wonder if it will work like a normal sizedcard netduino and varying terminals, hopefully I can answer, Thanks
@PatyTo: Yes it will work with both the standard Netduino and the Netduino Plus. In fact the original code was developed on the Netduino Plus. I moved to the Mini as the Mini could be embedded in the project easily.
Regards,
Mark
Sorry but i am about to build the led cube myself and just looked at your circuit and noticed that you connected the active low pin to VCC, the clock to ground and didnt use a lot of the neccessary pins at all????
Inspring project! I am in the process of building the cube myself and I got confused by the Netduino mini pinning names as they do not match the pinning I got from the netduino mini schematics. The diagram I got from the open source project lists names such as PWM0 for the IO. You are using GPIO7. Is PWM0 the same as GPIO7?
Genius!!!!! ^.^
Sell one? You dumbass, this explains how to make it!
Good Job n Amazing work ,... I really like this one!
Its cool. I am jealous.
Can you give me a prat no from http://in.rsdelivers.com
This is an awesome project! I have made the cube and working on the controller board. I am planning on hooking it up to a netduino, not the mini. Is there anything that needs to be changed in order for the program to work? After looking at the schematics, the pins do not match up exactly. Could you tell me which pins on the mini you connected the 74HC595s and the 74HC238 to? Thank you!
these great job done by u, very nice, i like it.
please send me complete program code of 8x8x8 led cube in speedy. because my in a some error occurs. which type of software you used in led cube? plz tell me. fast reply......
Nice. But 74HC595 max rated at 70mA, isn't it? So, 70*8=0.56A per layer, 8.75mA per led. But you have made with 25mA per led. Hmm?!. Where am I wrong?
Can I used 74HC154 instead of 74HC238?
Remove this comment
Remove this thread
close