Charlieplexing. Aside from being a fancy word it is a technique for driving multiple LEDs with relatively few pins on a microcontroller. It allows you to drive N*(N-1) LEDs with just N pins without requiring any additional hardware, making it a quick, cheap and easy way to add multiple LEDs to your arduino projects.

To play the simulation, click on “simulate” icon below circuit, wait for it to load and then press “start simulation” above the circuit. Please note that at the time of writing simulation tends to fail to run unless you are logged in due to a bug.
(If you are here just for the “made easy” part of this title, feel free to skip to step 3 or just jump over to step 5 and grab my charlieplexing generator code)
Step 1: Two LEDs

If you’re done playing with simulation or you just want a less interactive way of learning, read on.
Unlike standard light bulbs, LEDs conduct current only in one direction. That means that LED will emit light when you connect it “correctly” and act as open circuit when connected “wrong”. If you connect two LEDs in parallel with different orientations, one of the two will shine no matter the polarity. To select which LED shines, you can modulate which pin is + and which – (image 2). If you set booth to either + or -, current won’t have anywhere to flow, turning both LEDs off (image 3).
You have likely noticed that I used only one resistor for two LEDs. That can be done, because electricity “sees” only one LED no matter the polarity. To calculate the resistor’s value, use the standard equation or use this ONLINE CALCULATOR. Keep in mind that max current output from Arduino is 40mA (20mA recommended).
Step 2: Three LEDs

Fortunately it gets a lot better when you add the third pin. Using three pins for charlieplexing allows us to control 6 LEDs. If you intend to play with SIMULATION for this setup, please do it before reading on.
You have seen that as soon as you flip one switch, two LEDs turn on. No matter what you do, either none two LEDs are lit. That is because there are always two LEDs between + and – terminals (image 2).
Solution to this problem is shown in this SIMULATION. You will see that the only difference is that I added three more switches that disconnect terminals. I once again recommend you go through the simulation before reading on.
Newly added switch’s sole purpose is to prevent undesired current flow. Now you can turn on only the LED you want to, but all of a sudden we see ghosting. On arduino, which runs on 5V, ghosting is noticeable when charlieplexing red or green LEDs. That is due to the fact that voltage drop on those two is less than 2.5V and thus some minor current flows through the two LEDs between + and – (image 3). If you were to implement charlieplexing on a microcontroller that runs on 3.3 volts, no ghosting would occur.
Now a word on resistors: Charlieplexing is normally conducted with as many resistors as there are pins for driving LEDs. Value of each resistor is half of the required resistance for driving a single LED the “classic” way.
And to finally address the elephant in the room: I have used three symbols on my schematics: +, – and x. How do they translate to an arduino? + and – are simple. You configure Arduino’s pins as an OUTPUT and write HIGH(+) or LOW(-). Disconnected(x) state is a bit less intuitive. If you configure pin as an INPUT, it goes in so called high impedance mode (high-Z), which means that current can’t flow in or out of that pin, acting somewhat as a disconnected pin. More on that in the step where I talk about Arduino implementation.
Step 3: N LEDs

You could try to figure out how circuit in the schematic above works, or you could try out this SIMULATION. Note that I omitted switches and an Arduino instead.
You will see that LEDs in the schematic (image 1) are arranged in a NxN grid. Greyed out LEDs are omitted because those would be connected to same pin with both cathode and anode. Anodes (+) of all LEDs in first row are connected to pin1 and so are all cathodes in first column. Other diodes follow suit.
If you are wondering what you could possibly do with such arrangement of LEDs, fear not. This is just a schematic, which serves only for easy wiring. PCB layout can be very different. In this SIMULATION I took the exact same project as before and rearranged LEDs to form a circle (I also changed the code so that LEDs copy potentiometer’s rotation). One more very common use for charlieplexing is in LED cubes – 6 pins allow you to drive 30 LEDs, enough for a 3x3x3 cube while 9 pins are enough for a 4x4x4 version!
Step 4: The Code

As mentioned before, all pins need to be set to one of three possible states: HIGH (+), LOW (-) and HIGH-Z (unconnected). First two are easy. You configure pins as OUTPUT and use digitalWrite(pin, state) to choose between HIGH and LOW. HIGH-Z (high impedance) is a bit less intuitive, but no more difficult. Whenever a pin is configured as an input, it goes to HIGH-Z mode, which means that the pin can’t sink or source any current.
It is best to start the code by defining which Arduino pin corresponds to which charlieplexing pin. By default all pins are configured as digital input pins, which means they are in HIGH-Z mode.
Next comes the function that configures pins to turn the selected LED on – charlieWrite(led). It first sets all pins to HIGH-Z. That is followed by a SWITCH statement that configures correct pins as correct level outputs. When you configure a pin as a digital output, it is set to LOW by default, which is why I only write level value to the pin I want to be set to HIGH.
Last thing to do is to fill SETUP() and LOOP() functions. SETUP() can be left empty if you don’t need any additional functionality, while LOOP()’s contents depend on desired mode of operation.
In the first DEMO the code is set up to go through all LEDs with a for statement and turns them on sequentially.
Second DEMO is a little bit more advanced. It goes through all LEDs and fakes effect that any number of them are on simultaneously. LOOP() first measures potentiometer value. Through the FOR loop, that measurement is compared to a threshold that corresponds to each LED. If value is above threshold, LED is turned on, otherwise it is ignored. No matter what happened there, a same delay() is called. This ensures that LED brightness doesn’t vary with number of lit LEDs. Note that it would be a lot better to compare “while (micros() – time < 1000){}” would be a better way to go, but I won’t torture the simulator. Attached Arduino code is written properly.
Step 5: EAGLE CAD
Making circuits. Finally.
You could always follow te schematic from step 3 and place all LEDs and wires on schematic of your preferred circuit CAD program. That was the way I did it, until I wanted to make a 240 LED display (16 pins). To place 240 LEDs, rename them, wire them… lets just say it would be… time consuming…
Fortunately my go to PCB CAD program, Eagle CAD (free for up to 2 layers and 80cm2 area) saves it’s files in an XML format. That means, I can use python to make the file I want almost automagically!
My python code generates a grid of LEDs along with all the needed wiring. You need to put the output .sch file in your project folder and finish up the schematic the way you see fit. You will want to add at least resistors and connection terminals.
Read more: Charlieplexing Made Easy (and What It Even Means?!)