Introduction: Controlling 20 Led's From 5 Arduino Pins Using Charlieplexing
Today, I'll show you how to control 20 LED's from just 5 Arduino pins. I'm working on a project where I need to control 15 LED's, 3 buttons, 3 seven segment displays and an RGB orb from one Arduino. Now if you work it out, you would see that without some clever multiplexing, I would need 15 Digital outputs for the LED's, 3 inputs for the buttons, 21 digital outputs for the seven segment displays and 3 PWM outputs for the RGB orb.
My Arduino doesn't have 40(15+21+3) outputs and 3 inputs, so I either need to drop features (Which doesn't sound like the fun thing to do), buy an Arduino Mega (Where's the fun in that) or I need to find a clever way to make it work. While I'm usually lazy, doing the impossible sounds like a lot more fun (Ok, the improbable then...)
I've already managed to hook 5 buttons to one analog input and documented that here:
https://www.instructables.com/id/How-to-access-5-buttons-through-1-Arduino-input/
By using Charlieplexing, you can hook up n*(n-1) LED's to n microcontroller pins. I'll go through the theory in the next step, but from this you can see that I can hook up 20 LED's on 5 pins or 12 LED's on 4 pins, which would be a great start for actually making this all work on 1 Arduino.
If you search Instructables (and Google for that matter), You'll find lots of theoretical explanations of how Charlieplexing work, and you'll even find some examples of Charlieplexed LED's running on Arduino's. The problem is that the code on these examples are generally not explained very well, and are usually very tightly tied to the exact hardware setup that the author used. This makes it an exercise in futility and frustration to try and make it work on your own projects.
I will show you exactly how this works, how to set it up with as many LED's as you would like in as painless a manner as possible (Although it gets horribly complicated if you go to more than about 30 LED's), how to code it so it works for you, and which problems I had to overcome in the process.
My Arduino doesn't have 40(15+21+3) outputs and 3 inputs, so I either need to drop features (Which doesn't sound like the fun thing to do), buy an Arduino Mega (Where's the fun in that) or I need to find a clever way to make it work. While I'm usually lazy, doing the impossible sounds like a lot more fun (Ok, the improbable then...)
I've already managed to hook 5 buttons to one analog input and documented that here:
https://www.instructables.com/id/How-to-access-5-buttons-through-1-Arduino-input/
By using Charlieplexing, you can hook up n*(n-1) LED's to n microcontroller pins. I'll go through the theory in the next step, but from this you can see that I can hook up 20 LED's on 5 pins or 12 LED's on 4 pins, which would be a great start for actually making this all work on 1 Arduino.
If you search Instructables (and Google for that matter), You'll find lots of theoretical explanations of how Charlieplexing work, and you'll even find some examples of Charlieplexed LED's running on Arduino's. The problem is that the code on these examples are generally not explained very well, and are usually very tightly tied to the exact hardware setup that the author used. This makes it an exercise in futility and frustration to try and make it work on your own projects.
I will show you exactly how this works, how to set it up with as many LED's as you would like in as painless a manner as possible (Although it gets horribly complicated if you go to more than about 30 LED's), how to code it so it works for you, and which problems I had to overcome in the process.
Step 1: The Parts List
20 x LED's - These need to have the same forward voltage and current requirements to work properly. If you use different types, more than one will probably turn on at the same time. In my case I used 3mm green and red LED's.
5 Resistors - I used 100 Ohm resistors (Brown Black Brown). The value depends on your resistors, but will probably be somewhere between about 75 and 150 Ohm. If the value is too big, your LED's will be dim. If they are too small, more than one LED will turn on at the same time.
An Arduino. This is the brains of the project. Well, actually, I'm the brains, but you get the idea... I'm using a Diecimila for this project.
Some wire to hook it all together (Actually, LOTS of wire).
5 Resistors - I used 100 Ohm resistors (Brown Black Brown). The value depends on your resistors, but will probably be somewhere between about 75 and 150 Ohm. If the value is too big, your LED's will be dim. If they are too small, more than one LED will turn on at the same time.
An Arduino. This is the brains of the project. Well, actually, I'm the brains, but you get the idea... I'm using a Diecimila for this project.
Some wire to hook it all together (Actually, LOTS of wire).
Step 2: Charlieplexing - the Theory
Ok, the principle behind Charlieplexing is fairly simple. If you hook up 2 LED's between 2 micro-controller pins with 1 anode towards pin 1 and one anode towards pin 2, you turn on LED1 by setting pin 1 HIGH and pin 2 LOW. To turn LED2 on you reverse it - Set pin 1 LOW and pin 2 HIGH. See the first image below.
This of course means that only one of the LED's can be on at any time. Microcontrollers operate so quickly that you can just alternate their on states very quickly and it will look like both are on all the time.
Now at this point, you'll probably be saying "Well, so what? I could turn 2 LED's on using 2 pins without all this extra complexity..."
If you add a third micro-controller pin to the equation it starts getting interesting. Now you can turn on 6 LED's. Basically, you have the same setup as with 2 pins, but you also have the same setup between pin 2 and 3 and between pin 1 and 3. See the second image below.
To turn on the LED's connected to pin 1 and 3, you need to disconnect pin 2 from the circuit, and then just set pin 1 and 3 HIGH and LOW based on which LED you want to turn on. Luckily, the Arduino allows you to do this. If you change a pin to an input, it basically turns into a big resistance, which is close enough to being disconnected for this to work.
Now if you look at the third image, you can see what this looks like for 4 pins (12 LED's) and the fourth image shows you what it looks like for 5 pins (20 LED's).
You'll notice that it becomes rather interesting to wire it up by the time you get to 30 LED's with 6 pins. This for me seems like the limiting factor when building this.
I generated the images from the following link (I see the link is dead at the moment... I'm not sure if this is temporary):
http://hosting.alexanderbrevig.com/arduino/help/charlieplex
This of course means that only one of the LED's can be on at any time. Microcontrollers operate so quickly that you can just alternate their on states very quickly and it will look like both are on all the time.
Now at this point, you'll probably be saying "Well, so what? I could turn 2 LED's on using 2 pins without all this extra complexity..."
If you add a third micro-controller pin to the equation it starts getting interesting. Now you can turn on 6 LED's. Basically, you have the same setup as with 2 pins, but you also have the same setup between pin 2 and 3 and between pin 1 and 3. See the second image below.
To turn on the LED's connected to pin 1 and 3, you need to disconnect pin 2 from the circuit, and then just set pin 1 and 3 HIGH and LOW based on which LED you want to turn on. Luckily, the Arduino allows you to do this. If you change a pin to an input, it basically turns into a big resistance, which is close enough to being disconnected for this to work.
Now if you look at the third image, you can see what this looks like for 4 pins (12 LED's) and the fourth image shows you what it looks like for 5 pins (20 LED's).
You'll notice that it becomes rather interesting to wire it up by the time you get to 30 LED's with 6 pins. This for me seems like the limiting factor when building this.
I generated the images from the following link (I see the link is dead at the moment... I'm not sure if this is temporary):
http://hosting.alexanderbrevig.com/arduino/help/charlieplex
Step 3: The First Problem - Wiring It Properly
I don't joke when I say this is not as simple as it seems. If you make a mistake, it could be hard to find and correct. When I built this circuit, I pulled it apart and re-did it 3 times before it worked (mostly). Eventually I had to pull out 3 LED's before it worked properly... The diffusion on them are slightly different, so I suspect their current requirements are probably different. Once I pulled these 3 out, everything works fine.
Also, make sure you test every LED you are going to use before you put it in the circuit. It might seem like a waste of time, but once you start, it could be very hard finding the reason why you sometimes have 4 LED's lighting up at the same time. This was what was happening with me.
When wiring Charlieplexed LED's, planning is extremely important. Wire it the easiest way and just reassign the LED numbering in the software if necessary. It makes the initial programming a bit harder, but that's probably easier than trying to wire it in a specific sequence.
In my case, I wanted the LED's in batches of 3, so I designed it like the first image. I eventually pulled out LED 18, 19 and 20 to make it work. While they were in the circuit, 4 LED's would light up at the same time. I ended up having to buy new LED's to get this working properly.
The easiest way to wire it up would be as an n x (n-1) matrix, where n is the number of pins you are using. So for my 5 pin example, it would be easier to wire up as a 5 x 4 matrix. The second image shows you the simplified view of this. As you can see, the cathodes of the LED's are connected in batches of (n -1), and the anodes of each 4 LED batch goes to every other pin. I eventually went this route.
The third image shows my breadboard with the 20 LED's set up.Notice the rats nest of wires... If you screw up, it is probably easier to start from scratch, so make sure that you double check every wire before putting it in. Something else that makes it easier is to use different color hookup wire for each pin. so every connection to pin 1 is blue, pin 2 is green, pin 3 is yellow, etc.
Also, make sure you test every LED you are going to use before you put it in the circuit. It might seem like a waste of time, but once you start, it could be very hard finding the reason why you sometimes have 4 LED's lighting up at the same time. This was what was happening with me.
When wiring Charlieplexed LED's, planning is extremely important. Wire it the easiest way and just reassign the LED numbering in the software if necessary. It makes the initial programming a bit harder, but that's probably easier than trying to wire it in a specific sequence.
In my case, I wanted the LED's in batches of 3, so I designed it like the first image. I eventually pulled out LED 18, 19 and 20 to make it work. While they were in the circuit, 4 LED's would light up at the same time. I ended up having to buy new LED's to get this working properly.
The easiest way to wire it up would be as an n x (n-1) matrix, where n is the number of pins you are using. So for my 5 pin example, it would be easier to wire up as a 5 x 4 matrix. The second image shows you the simplified view of this. As you can see, the cathodes of the LED's are connected in batches of (n -1), and the anodes of each 4 LED batch goes to every other pin. I eventually went this route.
The third image shows my breadboard with the 20 LED's set up.Notice the rats nest of wires... If you screw up, it is probably easier to start from scratch, so make sure that you double check every wire before putting it in. Something else that makes it easier is to use different color hookup wire for each pin. so every connection to pin 1 is blue, pin 2 is green, pin 3 is yellow, etc.
Step 4: Testing What You Have So Far.
Do this! If you don't test now before you connect the Arduino, you can't be sure whether the problem is in the software or if the problem is hardware related if (when?) you have issues once you start programming it.
I tested mine and it worked fine, but then I tried using a library I found and some LED's just wouldn't light up. I pulled it apart and rebuilt the circuit again and got the same result. Turns out the problem was with the library (Or the way I was trying to use it - Not that I could see any issue).
To test it, connect a 5V power supply to your breadboard, and connect the ground wire to the pin 1 connection. Then touch the 5v power to pin 2,3,4,5 in turn. For every wire one (and only one) LED should light up. If 2 LED's light up for every single wire, make your resistors bigger by about 25% and try again. Repeat until only 1 light up at a time. If the LED's are dim, make your resistors smaller by about 25% until they are bright enough.
If no LED's light up, 2 LED's light up on some pins or more than 2 LED's light up on some pins, you need to check your LED's to make sure that they work and that they are all the same type. If they are, you probably made a mistake with the wiring.
Once you've connected the 5V power to every pin in turn, move the ground wire to pin 2 and do it again (Touching the 5V wire to pin 1,3,4,5). Do this with the ground wire connected to every pin in turn and make sure that only one LED turn on every time you touch the 5V wire to the other pins.
I tested mine and it worked fine, but then I tried using a library I found and some LED's just wouldn't light up. I pulled it apart and rebuilt the circuit again and got the same result. Turns out the problem was with the library (Or the way I was trying to use it - Not that I could see any issue).
To test it, connect a 5V power supply to your breadboard, and connect the ground wire to the pin 1 connection. Then touch the 5v power to pin 2,3,4,5 in turn. For every wire one (and only one) LED should light up. If 2 LED's light up for every single wire, make your resistors bigger by about 25% and try again. Repeat until only 1 light up at a time. If the LED's are dim, make your resistors smaller by about 25% until they are bright enough.
If no LED's light up, 2 LED's light up on some pins or more than 2 LED's light up on some pins, you need to check your LED's to make sure that they work and that they are all the same type. If they are, you probably made a mistake with the wiring.
Once you've connected the 5V power to every pin in turn, move the ground wire to pin 2 and do it again (Touching the 5V wire to pin 1,3,4,5). Do this with the ground wire connected to every pin in turn and make sure that only one LED turn on every time you touch the 5V wire to the other pins.
Step 5: Preparing to Code It.
Now before we actually get to coding it, it would be a good idea to have a bit of a plan before just jumping in.
The principle is actually quite simple.
When the Arduino starts, change all pins connected to the Charlieplex to INPUT.
In the main loop:
- Turn off all LED's by setting all pins as INPUTS
- For each LED you want to turn on, look up its two pins:
Set both pins as output.
Set the pin connected to the anode HIGH
Set the pin connected to the cathode LOW
Wait a certain amount of time
- Turn off all LED's
- Turn on the next LED
- Repeat until the program ends
Let's assume I want to just sequence through all the LED's turning them on one at a time. For each LED, I need to set 5 pins to INPUT, then I need to set 2 pins to OUTPUT, do two digitalWrite() calls and then finally wait for a specified period. We need to do this 20 times.... As you can see, this gets very confusing and complex very quickly. Unless we can create some form of library, this will be another exercise in futility.
From the library, we would need the following:
- A list of all LED's in the matrix. Each LED should also specify what pins its anode and cathode is connected to.
- A function to turn off all LED's
- A function to turn on all LED's
- A function that will turn on all LED's defined by some form of mask for a specific period.
I'll explain the actual code, but I'm not going to explain how to create a library. If you are interested in that, there is a pretty good tutorial on the Arduino site:
http://arduino.cc/en/Hacking/LibraryTutorial
Before you do a library, you should always get your code working as a normal sketch first in any case, so lets do that.
The principle is actually quite simple.
When the Arduino starts, change all pins connected to the Charlieplex to INPUT.
In the main loop:
- Turn off all LED's by setting all pins as INPUTS
- For each LED you want to turn on, look up its two pins:
Set both pins as output.
Set the pin connected to the anode HIGH
Set the pin connected to the cathode LOW
Wait a certain amount of time
- Turn off all LED's
- Turn on the next LED
- Repeat until the program ends
Let's assume I want to just sequence through all the LED's turning them on one at a time. For each LED, I need to set 5 pins to INPUT, then I need to set 2 pins to OUTPUT, do two digitalWrite() calls and then finally wait for a specified period. We need to do this 20 times.... As you can see, this gets very confusing and complex very quickly. Unless we can create some form of library, this will be another exercise in futility.
From the library, we would need the following:
- A list of all LED's in the matrix. Each LED should also specify what pins its anode and cathode is connected to.
- A function to turn off all LED's
- A function to turn on all LED's
- A function that will turn on all LED's defined by some form of mask for a specific period.
I'll explain the actual code, but I'm not going to explain how to create a library. If you are interested in that, there is a pretty good tutorial on the Arduino site:
http://arduino.cc/en/Hacking/LibraryTutorial
Before you do a library, you should always get your code working as a normal sketch first in any case, so lets do that.
Step 6: Coding It - a Simple Test.
The first thing we do is define the pins that the Charlieplex will be on:
#define A 8
#define B 9
#define C 10
#define D 11
#define E 12
Then we define the actual LED connections. You can see that I define them as they are set up in the simplified circuit. The first row is every LED with it's cathode connected to pin 1:
int c[5][4][2] = {
{ {A, B}, {A, C}, {A, D}, {A, E} },
{ {B, A}, {B, C}, {B, D}, {B, E} },
{ {C, A}, {C, B}, {C, D}, {C, E} },
{ {D, A}, {D, B}, {D, C}, {D, E} },
{ {E, A}, {E, B}, {E, C}, {E, D} }
};
In setup() we just set all the pins to INPUT's.
The light() method is just a convenient way to set the relevant pins to outputs and set them high and low as required:
void light( int pins[2] ){
pinMode( pins[0], OUTPUT );
digitalWrite( pins[0], HIGH );
pinMode( pins[1], OUTPUT );
digitalWrite( pins[1], LOW );
}
The test_loop() method just use the test() method to light every LED for half a second:
And finally, the loop()method just calls the test_loop() method.
The only tricky thing here is to actually define the LED array properly.
#define A 8
#define B 9
#define C 10
#define D 11
#define E 12
Then we define the actual LED connections. You can see that I define them as they are set up in the simplified circuit. The first row is every LED with it's cathode connected to pin 1:
int c[5][4][2] = {
{ {A, B}, {A, C}, {A, D}, {A, E} },
{ {B, A}, {B, C}, {B, D}, {B, E} },
{ {C, A}, {C, B}, {C, D}, {C, E} },
{ {D, A}, {D, B}, {D, C}, {D, E} },
{ {E, A}, {E, B}, {E, C}, {E, D} }
};
In setup() we just set all the pins to INPUT's.
The light() method is just a convenient way to set the relevant pins to outputs and set them high and low as required:
void light( int pins[2] ){
pinMode( pins[0], OUTPUT );
digitalWrite( pins[0], HIGH );
pinMode( pins[1], OUTPUT );
digitalWrite( pins[1], LOW );
}
The test_loop() method just use the test() method to light every LED for half a second:
And finally, the loop()method just calls the test_loop() method.
The only tricky thing here is to actually define the LED array properly.
Attachments
Step 7: Coding It - a Better Example.
While cycling through all LED's is a good indication that it works, it's somewhat boring...
The attached sketch shows how you can set up the code to display specific frames (Where each frame is defined as a bunch of LED's that are on or off).
The first difference is that I re-arranged the variables in my Array of LED's to resemble the layout on my breadboard. Unfortunately, there's no trick here. It's just trial and error. Watch the sequence of LED's and swop two at a time to try and sort it out.
The main difference however is the display(int frame[5], int duration) method. Most of it is pretty self-evident. There's a lot of looping to get all the relevant values from the different arrays, but the only part that's likely to be confusing is here:
if( frame[y] & (0b1000 >> x) ){
light( c[y][x] );
This is essentially using a bitwise AND on a Bitshifted byte to determine whether a column in the frame is on. When you right shift, the 1 in 0B1000 will move right, so what you are doing is this:
Assume X = 0 and frame[y]=0b0101:
0b1000 >> 0 is 0B1000
by doing a bitwise AND on
0B1000 and
0B0101
you end up with 0B0000 which means no LED's will turn on.
If you then go on to X = 1 and frame[y]=0b0101:
0b1000 >> 1 is 0B0100
by doing a bitwise AND on
0B0100 and
0B0101
you end up with 0B0100 which means turn on the second LED (And only the second LED) in row Y.
Essentially, it's just a clever bit of maths to make the code cleaner. You can read more about bitwise math here:
http://www.arduino.cc/playground/Code/BitMath
The video shows what it looks like when running.
Step 8: Final Thoughts
While this was actually quite fun to build, it is a lot of complexity for a rather limited solution. In the video on the previous step you can see how the LED's get dimmer the more you have on at any specific time. Couple that to the fact that the wiring is quite complex and the code is pretty convoluted, and it just doesn't strike me as a good way to go.
Personally, I'm waiting for a couple of 74HC595 shift registers (You can see more info here ). There is also a stable Arduino library for the 595, and after you've added the first, adding more doesn't take any extra pins. This makes it fairly simple to for example control 64 LED's from 3 pins.
If anything is unclear, feel free to ask and I'll answer and update the Instructable as necessary.
Thanks for taking the time to read this, It's been a fun little side project for me. Any feedback or ratings (wink, wink) would be much appreciated. Also, Please take the time to vote for me in the Microcontroller contest if you found this helpful.
This was supposed to be part of a pretty big project, but I just don't think it's worth the effort. Look out for my next Instructables on creating a clock that displays multiple timezones, an RGB Orb, 7 segment displays and interfacing this all with an online app that will update statuses (If at all possible on a single Arduino).
Personally, I'm waiting for a couple of 74HC595 shift registers (You can see more info here ). There is also a stable Arduino library for the 595, and after you've added the first, adding more doesn't take any extra pins. This makes it fairly simple to for example control 64 LED's from 3 pins.
If anything is unclear, feel free to ask and I'll answer and update the Instructable as necessary.
Thanks for taking the time to read this, It's been a fun little side project for me. Any feedback or ratings (wink, wink) would be much appreciated. Also, Please take the time to vote for me in the Microcontroller contest if you found this helpful.
This was supposed to be part of a pretty big project, but I just don't think it's worth the effort. Look out for my next Instructables on creating a clock that displays multiple timezones, an RGB Orb, 7 segment displays and interfacing this all with an online app that will update statuses (If at all possible on a single Arduino).