Home Arduino Projects Charlieplexing LEDs With The Arduino Board

# Charlieplexing LEDs With The Arduino Board

Adding LEDs to a project soon uses up valuable pins. What if you could control a row of 12 LEDs to show changing values with only 4 digital pins? There is a way and it is called Charlieplexing. It was first proposed in 1995 by Charlie Allen.

It relies on two properties that you may not have previously considered useful.

LEDs are Light Emitting Diodes and only pass electrical current in one direction. If you connect two of them together, in parallel but facing in opposite directions, you can use just 2 wires to control them.

If we connect the right-hand end of the circuit to 5V and the left-hand end to GND current will flow from right to left and the upper LED, #2, to light up and the lower LED will stay dark. If we reverse the connections, current will flow in the opposite direction and only the lower LED, #1, will be lit.

Here is the same setup on a breadboard. I’ve only used one resistor here, 660 Ohms, but it protects both LEDs. Notice that the long legs of the LEDs, the anodes, which are connected to the higher voltage, face in opposite directions.

## Project #1 – 6 LEDs on 3 wires

If we want more than two LEDs we can add additional pairs of parallel, opposite facing LEDs in different arrangements. Let’s try 6 LEDs in three pairs in the form of a triangle. The blue circle represents a pair of opposed parallel LEDs.

### Required materials

• Arduino – an UNO works perfectly
• LEDs – space constraints in project #2 make 3mm LEDs a better proposition
• 330 Ohm resistors
• Breadboard or stripboard
• Connecting wire

We can add the 330 Ohm resistors, the brown rectangles, to each of the corners, to limit the current, protect the LEDs and the Arduino pins. I’ve shown blue, yellow and green wires connected to the resistors.

The question marks indicate that the wire is not connected. How can we effectively disconnect a wire while it is physically attached to an Arduino pin?

The second property that we seldom think about is that the digital microcontroller pins are tri-state. They can be set HIGH, LOW or used for INPUT. When a pin is set to INPUT MODE it is high impedance. So high that almost no current can flow so it is effectively disconnected.

This means we can control the 6 LEDs with three pins connected to the blue, yellow and green wires and replace the ‘?’ with INPUT mode rather than the OUTPUT mode used for HIGH and LOW.

Normally we just set a pin’s mode at the start of the script and leave it alone for the rest of the program. Here we need to keep switching modes and values.

Here is one way of coding the previous table:

```// LED numbers    0,1,2,3,4,5
byte anodes[]  = {b,y,y,g,g,b};  // HIGH
byte caths[]   = {y,b,g,y,b,g};  // LOW
byte inputs[]  = {g,g,b,b,y,y};  // INPUT```

b, y and g are the wire colours, which have been allocated digital pin numbers. Reading vertically; for LED#0 to be switched ON – BLUE is set HIGH (anode), YELLOW is set LOW (cathode) and GREEN is set as an INPUT (disconnected).

The arrangement of LEDs in the previous diagram is logical but not very useful. It would be much more useful if the LEDs were in a straight line. This is quite a tricky topological transformation to keep all the connections correct. What you gain is free pins involves more complicated wiring.

Here is the breadboard version:

Notice that all the LEDs are facing the same way with their anode, the longer leg, to the right. Each pair is cross-connected, anode to cathode, with the pink and black links. The first pair are driven by the blue and yellow wires, the second pair by the yellow and green wires and the last pair by the green and blue wires. The coloured wire is connected directly to the anode of the LED it needs to turn ON.

Above each LED is its number (0…5) and the wire colours, anode – cathode. This is quite an effort to link up and the DuPont wires would probably get in the way when observing the flashing LEDs. Below is a photograph of a stripboard version, which once soldered up could easily be used in multiple projects. (There are no underside cuts of the copper strip on this board)

I connected the board to an Arduino UNO with the blue wire on pin 7, the yellow on pin 6, and green on pin 5. I also added a 10 K Ohm potentiometer with the wiper on A0 and the outside connections to  5V and GND to give a physical input to the program. You need to twist the pot knob once the LEDs stop flashing left to right.

```// CharliePlexing 6 LEDs with multiplexed bar graph
// Tony Goodhew 27 June 2020

#define b 7   // Blue wire
#define y 6   // Yellow wire
#define g 5   // Green wire

#define potPin A0  // 10 K Ohm potentiometer on A0, 5V and GND
int done = false;
int wait = 190;
unsigned long startTime;

// LED numbers    0,1,2,3,4,5
byte anodes[]  = {b,y,y,g,g,b};  // HIGH
byte caths[]   = {y,b,g,y,b,g};  // LOW
byte inputs[]  = {g,g,b,b,y,y};  // INPUT

void show(int p){  // Turn on a single LED ( p = 0...5)
pinMode(inputs[p], INPUT);   // Disconnect
pinMode(caths[p], OUTPUT);
digitalWrite(caths[p], 0);   // GND
pinMode(anodes[p], OUTPUT);
digitalWrite(anodes[p], 1);  // 5V
}

void show2(int q) {  // Multiple LEDs from left
for (int m = 0; m < q; m++)  {
show(m);
delay(3);
}
}

void setup() {
Serial.begin(115200);  // Ready for de-bugging
startTime = millis();
}

void loop() {
if (!done) {  // Basic left to right movement
for (int m = 0; m < 10; m++){  // Cycles
for (int i = 0; i <6; i++){  // LEDs
show(i);
delay(wait);
wait = wait - 3;           // Speed things up
}
}
}
done = true;  // Finished with counted display

// Twist the pot to change the LED lit up
int potValue = analogRead(potPin); // Read potentiometer
int pointer = potValue / 160; // values 0...6
if (pointer == 0 ) {  // Turn off all LEDs
pinMode(b, INPUT);  // Disconnect all wires
pinMode(y, INPUT);
pinMode(g, INPUT);
}
else{
if (millis() - startTime < 17000) { // 17 seconds
show(pointer - 1); // Single LED
}
else {
show2(pointer);    // Bar graph
}
}
}```

The function show() does most of the work. It resets pinModes and values to switch on a single LED. It is called multiple times by the function show2(), which turns a row of LEDs ON/OFF so quickly that persistence of vision does not allow our eyes to notice the flashing and produces the appearance of a steady a bar graph, if slightly dimmed.

In the main loop there are three stages of display:

1. A single lit LED appears  to move from left to right at faster and faster speeds.
2. A single LED is lit and its position in the row is controlled by the potentiometer.
3. A bar graph of lit LEDs is controlled by the potentiometer – 17 sec into the main loop.

Here is a second script which demonstrates generating patterns with this limited number of LEDs.

```// CharliePlexing 6 LEDs Patterns
// Tony Goodhew 28 June 2020

#define b 7   // Blue wire
#define y 6   // Yellow wire
#define g 5   // Green wire

// LED numbers    0,1,2,3,4,5
byte anodes[]  = {b,y,y,g,g,b};
byte caths[]   = {y,b,g,y,b,g};
byte inputs[]  = {g,g,b,b,y,y};

void show(int p){  // Turn on a single LED ( p = 0...5)
pinMode(inputs[p], INPUT);   // Disconnect
pinMode(caths[p], OUTPUT);
digitalWrite(caths[p], 0);   // GND
pinMode(anodes[p], OUTPUT);
digitalWrite(anodes[p], 1);  // 5V
}

void show2(int q) {  // Mulitple LEDs from left
for (int m = 0; m < q; m++)  {
show(m);
delay(3);
}
}

void setup() {
Serial.begin(115200);  // Ready for de-bugging
}

void loop() {
// Opposites
for (int m = 0; m < 7; m++){        // Cycles
for (int i = 0; i <6; i++){       // Move up
for (int t = 0; t <50; t++)  {  // Flashes 50 times
show(i);                      // Left LED
delay(3);                     // 3 millisecond flash
show(5-i);                    // Right LED
delay(3);
}
}
}

// Pair
for (int m = 0; m < 7; m++){         // Cycles
for (int i = 0; i < 4; i++){       // Move up
for (int t = 0; t <100; t++)  {  // Flashes
show(i);                       // Left LED
delay(3);
show(i + 1);                   // Right LED
delay(3);
}
}
for (int i = 0; i < 4; i++){      // Move down
int ii = 4 - i;
for (int t = 0; t <100; t++)  {  // Flashes 50 times
show(ii);                     // Left LED
delay(3);
show(ii + 1);                 // Right LED
delay(3);
}
}
}
}```

## Project #2 – 12 LEDs with 4 wires

We can arrange the 6 pairs of LEDs in a square with a resistor at each corner.

image

We can code this in a similar way to the previous script but we will need to set two lines as inputs.

```// LED numbers    0,1,2,3,4,5,6,7,8,9,10,11
byte anodes[]  = {b,y,y,g,g,r,r,b,y,r, b, g};
byte caths[]   = {y,b,g,y,r,g,b,r,r,y, g, b};
byte input1[]  = {g,g,b,b,b,b,y,y,b,b, r ,r};
byte input2[]  = {r,r,r,r,y,y,g,g,g,g, y, y};```

Here is a photograph of the stripboard version. The boards were 24 holes wide so there was just enough room to place twelve 3mm LEDs across the width of the board. (5mm LEDs are too wide, unfortunately, and need a single hole space between them).

Once again all the LEDs are facing the same way with the longer leg, anode, to the right. The copper strips on the underside of the board, underneath the resistors, are cut.

The board is well worth soldering up and keeping for use in other projects.

```// CharliePlexing 12 LEDs
// Tony Goodhew 27 June 2020

#define b 7   // Blue wire
#define y 6   // Yellow wire
#define g 5   // Green wire
#define r 4   // red wire
#define potPin A0  // 10 K Ohm potentiometer on A0

// pin numbers    0,1,2,3,4,5,6,7,8,9,10,11
byte anodes[]  = {b,y,y,g,g,r,r,b,y,r, b, g};
byte caths[]   = {y,b,g,y,r,g,b,r,r,y, g, b};
byte input1[]  = {g,g,b,b,b,b,y,y,b,b, r, r};
byte input2[]  = {r,r,r,r,y,y,g,g,g,g, y, y};

void show(int p) {  // Turn on a single LED ( p = 0...5)
pinMode(input1[p], INPUT);     // Disconnect
pinMode(input2[p], INPUT);
pinMode(caths[p], OUTPUT);
digitalWrite(caths[p], 0);   // GND
pinMode(anodes[p], OUTPUT);
digitalWrite(anodes[p], 1);  // 5V
}

void show2(int p) {
for (int m = 0; m < p; m++)  {
show(m);
delay(2);  // Reduced wait time to remove flicker
}
}

void setup() {
Serial.begin(115200);  // Ready for de-bugging
}

void loop() {
int potValue = analogRead(potPin); // Read potentiometer
int pointer = potValue / 80; // values 0...12
if (pointer == 0) {
pinMode(b, INPUT);  // Turn off all LEDs
pinMode(y, INPUT);  // Disconnect all wires

pinMode(g, INPUT);
pinMode(r, INPUT);
}
else{
show2(pointer);     // Bar graph display
}
}```

This is just an update of the previous code to allow for the extra LEDs and save you typing. This is just the minimum to check wiring. Note that the time delay in the show2() loop has been reduced to remove flicker. Try 3 or 4 milliseconds to see the difference.

## Things to try

Modify the code to extend the patterns supplied for the 6 LED board:

• One LED moving left and right at speeds controlled by the potentiometer
• 2 or 3 LEDs moving together left and right  or mirroring
• Bar graph with input from a different sensor such as an ultrasonic distance sensor (SR04) or temperature sensor (TMP36 or DS18B20).
• Wire up a compact 10 segment bar graph display and control it with 4 pins. I put an additional normal red LED at one end and a green LED at the other. If you try this remember to cut the tracks under the bar graph display. The display has cathodes on one side and anodes on the other with the LED between them. Using the resistors to make the connections is much quicker than stripping the insulation from wires. I suggest you test each LED as you build these boards, this is much easier than fault finding at the end.

## Conclusion

I hope you found the topic interesting and try building one of the boards. What we save on pins to drive these boards we pay for in the complexity of the wiring and code. We do not have to stop here with 12 LEDs. Each additional wire added increases the number of LEDs which can be controlled using the formula

L = n(n-1)

where L is the maximum number of LEDs controlled and n the number of pins used.

Another useful application would be on an interactive map or school project display with individual button switches on a menu lighting LEDs at different positions on the display. Examples include: naming the features of an ancient castle or tourist attractions on a map of the local area.