This library lets you control bi-colour (2-lead) LEDs using charlieplexing. That is a technique for minimizing the number of GPIO pins needed to activate a number of bidirectional devices.
Using N pins, you can control:
N * (N - 1) / 2
bi-colour LEDs.
- Works with 2 to 5 Arduino GPIO pins
- Supports RED / GREEN / YELLOW (time-multiplexed)
- Non-blocking (
update()loop, GPIOUtils-style) - Adjustable colour balance using
gamma
Each bi-colour LED is connected between a unique pair of pins; not to ground as is usual for LEDs.
- Current flows one way → RED
- Reverse direction → GREEN
- Rapid alternation → YELLOW
For N pins, you get:
| Pins | Bi-colour LEDs |
|---|---|
| 2 | 1 |
| 3 | 3 |
| 4 | 6 |
| 5 | 10 |
- Each LED is placed between two pins
- Each LED must have a series resistor (220Ω–470Ω)
- Resistor can be on either side of the LED
- Pins must be capable of INPUT (high-Z) mode
Assume Arduino pins:
2, 3, 4, 5, 6
| LED | Connect Between |
|---|---|
| 0 | 2 ↔ 3 |
| LED | Connect Between |
|---|---|
| 0 | 2 ↔ 3 |
| 1 | 2 ↔ 4 |
| 2 | 3 ↔ 4 |
| LED | Connect Between |
|---|---|
| 0 | 2 ↔ 3 |
| 1 | 2 ↔ 4 |
| 2 | 2 ↔ 5 |
| 3 | 3 ↔ 4 |
| 4 | 3 ↔ 5 |
| 5 | 4 ↔ 5 |
| LED | Connect Between |
|---|---|
| 0 | 2 ↔ 3 |
| 1 | 2 ↔ 4 |
| 2 | 2 ↔ 5 |
| 3 | 2 ↔ 6 |
| 4 | 3 ↔ 4 |
| 5 | 3 ↔ 5 |
| 6 | 3 ↔ 6 |
| 7 | 4 ↔ 5 |
| 8 | 4 ↔ 6 |
| 9 | 5 ↔ 6 |
Bi-colour LEDs contain two internal diodes.
So:
- One direction = RED
- Opposite direction = GREEN
If colours appear swapped:
- Flip the LED physically, or
- Swap RED/GREEN in software
To light an LED:
| Pin A | Pin B | Result |
|---|---|---|
| HIGH | LOW | One colour |
| LOW | HIGH | Other colour |
| INPUT | INPUT | Off |
All unused pins must be set to INPUT (high impedance).
The colour YELLOW is created by rapidly alternating between red and green.
However, in practice:
- Red LEDs are often brighter than green (or vice versa)
- Equal timing does not produce a visually balanced yellow
To compensate, this library provides:
setGamma(uint8_t gamma);Where:
| Gamma | Result |
|---|---|
| 0 | all green |
| 128 | equal time split |
| 255 | all red |
Internally, this controls the duty cycle split between red and green phases.
leds.setGamma(50);If red is brighter than green, a lower gamma value reduces red’s duty cycle, producing a more balanced yellow.
You can even adjust this dynamically:
leds.setGamma(analogRead(A0) >> 2);Charlieplexing achieves efficiency by driving only one LED at a time, rapidly cycling through all active LEDs. Your eyes integrate this into a steady image — but there’s an important consequence:
Brightness is shared across time.
If multiple LEDs are active simultaneously, each one receives only a fraction of the total “on” time.
If N LEDs are active:
| Active LEDs | Duty cycle per LED | Perceived brightness |
|---|---|---|
| 1 | 100% | full brightness |
| 2 | 50% | slightly dimmer |
| 3 | ~33% | noticeably dimmer |
| 6 | ~17% | dim |
| 10 | 10% | quite dim |
So:
- A single LED will appear brightest when turned on
- A group of LEDs will appear dimmer if they are turned on at once
- This is expected behavior, not a bug
For bi-colour LEDs, YELLOW is created by alternating:
- RED phase
- GREEN phase
So yellow LEDs are affected by:
- Multiplexing across LEDs
- Red/green duty splitting (
gamma)
This can make yellow appear dimmer or slightly biased in colour.
- Call
update()as frequently as possible (avoid delays) - Use lower resistor values carefully to increase brightness (within safe limits)
- If possible, avoid turning on all LEDs at once
Charlieplexing trades:
Fewer GPIO pins ↔ Lower brightness
This is the fundamental design constraint of the technique.
#include "CharlieLED.h"
CharlieLED leds(2, 3, 4);
void setup() {
leds.begin();
leds.setLED(0, CharlieLED::RED);
leds.setLED(1, CharlieLED::GREEN);
leds.setLED(2, CharlieLED::YELLOW);
leds.setGamma(50); // adjust yellow balance
}
void loop() {
leds.update();
}- Efficient use of GPIO pins
- Clean abstraction via the
CharlieLEDclass - Scales from 1 to 10 LEDs
- Fully non-blocking design
- Adjustable colour balance via
gamma
- Charlieplexing (Maxim / Analog Devices Application Note)
- Wikipedia: Charlieplexing
- Adafruit Guide to Charlieplexing
- Per-LED brightness (PWM duty cycle)
- Smarter time-sliced scheduler using
micros() - Gamma calibration per LED
- Animation patterns / effects layer

