A collection LED control examples for Arduino
This project is maintained by tigoe
Electronic candles are common in restaurants and bars these days, and they’re usually pretty low-fidelity representations of the behavior of real candles. It’s possible to make them with more complex behaviors, and it’s a good way to learn about addressable, LEDs. What follows is an introduction to addressable LEDs, using electronic candle-making as an application through which to learn. In the process, you’ll learn a bit about how colors are reproduced using LEDs and computation, and hopefully a few things about diffusion and reflection as well.
In order to make the most of this tutorial, you should know what a microcontroller is, what the Arduino microcontroller programming environment is, and the basics of how to use it. You should download and install the Arduino IDE (integrated development environment) on your computer to follow along with the programming below. The ITP Intro to Physical Computing Topics and Video Links offer helpful background if you’re new to electronics and microcontrollers.
To follow along with this tutorial, you’ll need:
Figures 1-5 show the parts you’ll need for this exercise. Click on any image for a larger view.
Figure 1. Arduino board. Shown here is the Nano 33 IoT.
An Arduino-compatible microcontroller. Everything you’ll see here works on all models including the Uno, Nano 33 IoT and Nano Every, and the MKR boards.
Figure 2. Addressable LED Ring or module
A set of WorldSemi addressable LEDs, the WS2812/SK6812 types. You can buy these from many retailers. Adafruit’s NeoPixel line are all compatible, as are SparkFun’s LilyPad Pixel Board Seeedstudio’s WS2812 offerings, and many others. Here is a quickstart guide to the NeoPixel library.
Figure 3. Solderless Breadboard
Figure 3. Hookup wires
Figure 5. microUSB cable
A solderless breadboard, some jumper wires, and a USB cable to match your Arduino.
You’ll also need a personal computer with the Arduino software installed.
When you start making a housing for your candle, some material for diffusing and reflecting the light will be useful as well: paper, cloth, glass, plastic, whatever strikes your fancy.
If you watch a candle flame over time, you’ll see many colors in it: pale yellow-orange in the tip, fading to orange towards the base, with hints of blue or green near the wick; perhaps an orange or reddish color at the top of the wax of the candle. All of these blend together in the flame, more so when you see them reflected through a frosted candle base or on a nearby surface. A slight breeze will change the mixture of the colors and the rhythm of their change. These colors are made of different wavelengths of light, determined by the material that’s burning and the material through which it’s refracted or off of which it’s reflected. Re-creating this feeling using artificial light sources requires multiple sources mixed together. It can be done by controlling the color and intensity of multiple light-emitting diodes, or LEDs, using a microcontroller, a tiny, simple computer used to control physical devices.
To get started, download and install the Arduino microcontroller integrated development environment (IDE) on your computer. From the IDE’s Tools menu, choose Manage Libraries… then search for and install the Adafruit NeoPixel library. If you’re using the Nano 33 or any of the MKR boards you can use the Adafruit NeoPixel DMA library instead. The library manager will automatically install the library into the IDE.
There are many different models of Arduino and compatible microcontroller boards. Any of them will work for this exercise. Figures 6 through 8 show breadboard layouts for the MKR boards, the Nano boards, and the Uno.
All circuit board images made with Fritzing
Figure 6. Arduino Uno next to a breadboard. Pin function numbers can be found at this link. Wires extend from the 5V and Ground pins of the Uno to the breadboard’s vertical rows on the left hand side. These form voltage and ground buses. Wires connect the vertical rows on the left side with those on the right side so that both sides of the breadboard have voltage and ground buses.
If you’re using a MKR or Nano board or plug it into a solderless breadboard as shown in Figures 7 and 8. Don’t leave it in the black foam in which it’s shipped. This foam is conductive foam, to protect the board during shipping, but it will damage the board if you power it while it’s in the foam. You can find typical breadboard layouts for the Uno, Nano, and MKR boards at this link.
Figure 7. Arduino Nano 33 IoT mounted on a breadboard. Physical pins are numbered in a u shape from top left. Pin function numbers for the Nano 33 IoT can be found at this link, and for the Nano Every at this link. The pin arrangement are the same for all Nanos. Wires extend from the 3.3V and Ground pins of the Nano (physical pins 2 and 14, respectively) to the breadboard’s vertical rows on the left hand side. These form voltage and ground buses. Wires connect the vertical rows on the left side with those on the right side so that both sides of the breadboard have voltage and ground buses.
Figure 8. Arduino MKR Zero mounted on a breadboard. Physical pins are numbered in a u shape from top left. Pin function numbers for the MKR Zero can be found at this link. Wires extend from the 3.3V and Ground pins of the Nano (physical pins 26 and 25, respectively) to the breadboard’s vertical rows on the right hand side. These form voltage and ground buses. Wires connect the vertical rows on the right side with those on the left side so that both sides of the breadboard have voltage and ground buses.
Once you’ve arranged your Arduino with a solderless breadboard, plug it into your computer’s USB port.
Now it’s time to light your first LED. Most Arduino and Arduino-compatible boards come with a built-in LED. Click the File menu, then Examples, then 01. Basics, then Blink. This will open a new file that looks something like this:
// the setup function runs once when you press reset or power the board
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
Click the Tools menu, then click Board: and choose the name corresponding to your board. The MKRZero and Nano 33 IoT boards can be found under the Arduino SAMD Boards submenu. The Nano Every is under Arduino MegaAVR boards. If your board is not in the menu, choose the Boards Manager from that same menu and search for the type of board you’re using and install it.
When you’ve got the board selected, click the Tools menu again, then click Port: and choose the port with the name corresponding to your board. For example, the one shown in Figure 9 is called /dev/cu.usbmodem14141 (Arduino MKRZERO).
Note: in the Arduino IDE version 2.0.0 and later, the board and port should show up in the board menu of the Sketch window as long as the IDE recognizes your board. Figure 10 shows the 2.0 Board/Port menu in the sketch window.
Figure 9. Tools menu of the Arduino IDE
Figure 10. Board and port menu in the Sketch window of the Arduino IDE 2.0 and later.
When you’ve picked the right board and the right port, click the Upload button on the toolbar as shown in Figure 11. You can also type command-U (MacOS) or control-U (Windows):
Figure 11. Upload button on the Arduino tool bar
The IDE will now compile your Blink program to a binary file, transfer it to the Arduino, and start running it. You should see the builtin LED on your board blinking, as seen in Video 1 below. Congratulations!
Video 1: The LED Blinking on a MKRZero
Controlling a few LEDs at a time on an Arduino is simple. You attach one leg of the LED (the longer leg, called the anode) to one of the input-output (I/O) pins through a 220-ohm resistor, attach the other leg (the shorter leg, or cathode) and apply voltage to the pin to turn the LED on. The digitalWrite()
command does this. digitalWrite(pinNumber, HIGH)
applies voltage, and digitalWrite(pinNumber, LOW)
turns it off. Each LED is just one color, though. If you want to change the color of your light, you need multiple LEDs. To mix red, green, and blue into a range of colors, for example, you need three LEDs. Fortunately, you can buy components that have multiple LEDs in one package. A typical RGB LED might look like those in Figure 12:
Figure 12. Four LED components. The one on the right is an RGB LED. Note that it has four legs. It contains three LEDs in the one package. The long one is a common cathode. The three others are the anodes for the red, green, and blue LEDs in the package.
To control an RGB LED like the one shown on the right above, you need three I/O pins. The common cathode is attached to ground. As you can imagine, you run out of I/O pins fast if every LED needs its own pin. This is where addressable LEDs come in handy. Addressable LEDs or programmable LEDs as they’re also called, are components containing an LED and a very limited processor to control them. They’re chained together so that you can control many of them from one I/O pin. Your microcontroller sends a series of electronic pulses on the I/O pin, and the string of addressable LEDs interprets the pulses to know which LED to turn on, and how bright. Each LED in the chain (for example, the ring shown in Figure 4 above) gets its own address, and you send pulses indicating the address, then the levels for each color channel at that address. This communication is a form of serial communication, a common way that computers talk to each other. You can think of each LED as its own tiny computer, listening for messages from your master computer (your Arduino).
The addressable LEDs you’re using are a variant of WorldSemi’s WS2812 LEDs. They listen for a specific protocol set by the manufacturer, and you can send it from your microcontroller using the Adafruit NeoPixel library which you installed earlier. Disconnect your board from the computer (Always disconnect your microcontroller from power before changing the circuit!). Then connect your addressable LED ring to the board as shown in Figures 13-15 below. The voltage input pin of the LEDs is attached to the Arduino’s Vcc, the GND pin is attached to ground, and the Data In (DI) pin attached to digital pin 5. These LEDs ideally run on 5V, but for smaller numbers of LEDs (less than 10), you can run them on 3.3V. The current available from the microcontroller’s Vcc pin isn’t much, but it’s enough to supply less than 10 addressable LEDs. You can use this same arrangement (addressable LED voltage, ground, and control on pin 5) arrangement on all Arduino models.
Figure 13. MKRZero with addressable LED ring
Figure 14. Nano 33 IoT with addressable LED ring
Figure 15. Arduino Uno with addressable LED ring
Now open a new file in the IDE, and enter the following program:
#include <Adafruit_NeoPixel.h>
const int neoPixelPin = 5; // control pin
const int pixelCount = 8; // number of pixels
// set up strip:
Adafruit_NeoPixel strip = Adafruit_NeoPixel(pixelCount, neoPixelPin, NEO_GRB + NEO_KHZ800);
void setup() {
strip.begin(); // initialize pixel strip
strip.clear(); // turn all LEDs off
}
void loop() {
int red = 255; // set colors
int green = 0;
int blue = 0;
// loop over all the pixels:
for (int pixel = 0; pixel < pixelCount; pixel++) {
strip.setPixelColor(pixel, red, green, blue);// set the color for this pixel
delay(500);
strip.show(); // refresh the strip
}
delay(1000);
strip.clear();
}
If you want to test each of the LEDs and colors in a NeoPixel module, here is a link to an example that cycles through red, green, blue, and white, one pixel at a time.
Video 2 below shows the LEDs of an addressable ring turning on in red, one at a time. The Arduino in that video is running the code above.
Video 2: Addressable LEDs turning on one at a time
This program contains the basic elements of any program for controlling neoPixel addressable LEDs:
strip
in this case) and set its parameterssetup()
functionstrip.setPixelColor()
to change the color of any given LED. Each channel’s brightness ranges from 0-255.strip.show()
Although the LEDs you’re using are likely just RGB LEDs, they’re not your only option. Addressable LEDs come in other options. RGB+white and white-white-amber (WWA), which have a cool white, a warm white, and an amber LED, are available as well. And the NeoPixel library isn’t your only option for programming them. More experienced coders may want to look at the FastLED library, or the light_WS2812 library. Most of the libraries will follow the same pattern of control shown above.
The magic in creating movement, color change, and animation lies in how you time the changes between colors of each given LED. Play around with these parameters in the previous program and try a few variations of your own, to see what you can do. Try making your LED fade from orange (which is a combination of red and green and blue, like 191, 104, 38) to a yellow (something like 205, 206, 36). Try writing a program to turn each of the LEDs in the ring a different color found in the candle flame.
Note: the RGB color scheme used to set colors in web pages is identical to the scheme you use to set colors for these LEDs. Red, green, and blue are often encoded as hexadecimal numbers, so a string like 205, 206, 36 would be 0xCD, 0xCE, 0x24, usually written as #CDCE24 in HTML.
Now that you can control your LEDs, it’s time to think about the colors in those candles again. How can you make the best fade from red to orange to yellow, with occasional flashes of blue or green?
LEDs emit different colors depending on the material from which they’re made. LEDs are made from materials like gallium arsenide, silicon carbide, gallium indium nitride, and others, mixed together in different ratios. Electronics Tutorials has a nice chart showing the common materials for different colors. What this means is that to recreate the flame colors, you need to find LEDs that produce the right wavelengths as the materials in your original candle, or you need to find LEDs that can mix together to give more or less the colors you want. You can enhance the look by using reflectors and diffusers that highlight the colors you want, but you can’t reflect a color that’s not there to begin with. So the color range of your sources is important. In fact, artificial sources are rated by their color rendering index (CRI), which is a measure of how well a given light source renders the colors of an object.
Most LED sources re-create a wide color spectrum by combining color sources that are spread across the color spectrum like the ones you have here: red, green and blue. Some sources will include a white LED as well. Some specialty sources will include more than just these colors. For example, white LED lights commonly come in multiple variants of white to support fading the color temperature of the light. Color temperature refers to the warmth or coolness of a light source. The Kelvin scale for color temperature ranges from 1,500k– 2,000k for warm reddish sources to 2,500k– 3,000k for amber and yellowish sources to 3,000– 4,000k for so-called “natural” white sources (analogous to incandescent light blulbs) to 4,000k– 6,500k for cool blue whites and 6,500k and higher for sky blues. The higher the Kelvin temperature, the cooler the hue.
The ability to render a wide range of colors is a competitive advantage for lighting manufacturers. For example, ETC Theatre Lighting makes sources with seven LED colors, to create as wide a range of color rendering as possible. They know that stage lighting designers are used to working with a range of color, from the subtle warm glow of an incandescent source as it fades out to the harsh greenish-white glare of an arc lamp, and they design their lights to re-create these conditions.
When you think about mixing colors, you need a way to organize the colors so you can think about how to move from one color to another. Rune Madsen has an excellent chapter on color models and color spaces in his book Programming Design Systems which I’ll borrow from here. A color model is a representation of the color spectrum in multiple dimensions, depending on the parameters that you have to represent the spectrum. For example the RGB color model represents the color spectrum on three axes: red, green blue. This is the most commonly used model for lighting and for computer screens, which are made of lighting pixels. See Madsen’s interactive model for RGB to visualize this. Printers often use a CMYK color model, in which color is represented on four axes, cyan, magenta, yellow, and black, for the ink colors often used in printing.
The RGB color model makes sense when you’re mixing red, green, and blue sources to make a color, but it can be challenging when you want to describe the changing hues of a source like a candle. For example, fading from yellow to orange to red and back requires you to mix red, green, and blue simultaneously to stay in the red-to-orange-to-yellow range. When changing between different hues, the Hue-Saturation-Intensity (HSI) is easier to work with. The HSI model maps hue on a color cylinder. Saturation of the color is mapped from no saturation (white) at the center of the circle to full saturation at the edge, and intensity, or brightness, is mapped in a third dimension.HSI is a variation on another model, Hue-Saturation-Lightness (HSL). The Hue-Saturation-Value (HSV) color model is also similar, but the height of the cylinder is intensity of color, not intensity. When you’re thinking about changing colors of light, you often describe what you want using an HSL color model, as the description of the candle above does.
When your light sources have RGB controls but you want to describe your program using HSI, you have to convert from one to the other. This is what computers are good for. To convert between HSI and RGB, you could do the math yourself, or use a color conversion calculator, or you could use the color conversion functions in the NeoPixel library. These functions do the math for you on the Arduino.
Here’s an example that uses the color conversion functions to fade from red to orange. Note how it requires changing only one number, the hue, even though that results in a change to red, green, and blue simultaneously. The code can also be found in this gitHub repository:
#include <Adafruit_NeoPixel.h>
const int neoPixelPin = 5; // control pin
const int pixelCount = 7; // number of pixels
int change = 10; // increment to change hue by
// set up strip:
Adafruit_NeoPixel strip = Adafruit_NeoPixel(pixelCount, neoPixelPin, NEO_GRBW + NEO_KHZ800);
int h = 1000; // hue
int s = 255; // saturation
int i = 255; // intensity
void setup() {
strip.begin(); // initialize pixel strip
strip.clear(); // turn all LEDs off
strip.show(); // update strip
}
void loop() {
// create a single color from hue, sat, intensity:
long color = strip.ColorHSV(h, s, i);
// loop over all the pixels:
for (int pixel = 0; pixel < pixelCount; pixel++) {
strip.setPixelColor(pixel, color);
strip.show(); // update the strip
delay(100);
}
// increment hue to fade from red (0) to reddish orange (15) and back:
h = h + change;
if (h < 0 || h > 2400) {
change = -change;
}
}
The HSV color space makes it much easier to change individual colors across a range of hues without having to work out the color mixing to RGB yourself. Video 3 shows the program in action.
Video 3. A ring of addressable LEDs fading from red to orange
Try modifying the program above to create different hues, and then write your own program to fade between them.
Making a light is more than just turning on and off the LEDs. It’s about the surfaces through which the light is refracted, and off which it’s reflected. What makes any light interesting, in the end, is what it illuminates, and how it does it. There are a few constraints that can be useful in thinking about this:
The first two videos belows give you some ideas for what’s possible with addressable LEDs. Video 4 shows one of these addressable pixel rings inside a hand-crafted tea candle holder made from glazed ceramic. The glaze of the ceramic makes a nice reflection.
Video 4. A NeoPixel ring fading in a candle holder. Video by Denise Hand.
In the videos 6 and 7, notice how the bristles of the paint brush blur the light and spread it out so that, in the second video, you don’t notice the LEDs at all, only the candle flame shape that it forms. Strong lines in a diffusion spread the light perpendicular to the direction of the lines, so the bristles in these videos help to spread the light here horizontally, blurring the distinction between one pixel and the next.
Video 6. A paintbrush diffusing LEDs. Video by Hayeon Hwang
Video 8 uses dichroic filters, thin films which selectively pass a small range of colors of light while reflecting other colors. The contrast of the deep purple light passed through the filter and reflected off the wall with the bright white light in the center creates drama.
In video 9, a thick sheet of paper with holes blocks most (though not all) of the light, and allows some through to be further diffused by a lightweight silk material. The combination creates a sense of depth, and the light coming through the silk feels ephemeral compared the the more solid light of the inner polka-dot cylinder. This soft, out-of-focus bokeh light is a popular photographic trope.
Video 9. Bokeh light candle. Video by Hayeon Hwang
Video 10 is a candle that uses a wooden holder with holes, and creates a nice movement through variable changes in the LEDs. Since they change at varying speeds, the candle feels more “flickery” and lively than some of the other candles: