Controlling WS2812B Addressable LEDs with Arduino

Whether you call them individually addressable RGB LEDs, WS2812B, or NeoPixels, there’s no denying that they are extremely popular and a must-have for any glowy and blinky project.

What sets these LEDs apart is their unique feature that allows you to individually address and control the color and brightness of each LED on a strip. Plus, you can (theoretically) chain as many LEDs as you want and control them all using a single microcontroller pin. The more LEDs you chain together, the more fancier your animations will be!

These LEDs are exceptionally bright, produce vibrant colors, and are easy to hookup. This tutorial explains everything you need to know about WS2812B addressable LEDs and how to use them with an Arduino.

WS2812B Hardware Overview

At first glance, the WS2812B LED may appear to be a standard 5050-sized (5x5mm) SMD RGB LED, but it is much more than that. It actually contains an integrated circuit inside. Below is a zoomed-in image of the WS2812B.

ws2812b close up image

If you look closely, you can see the tiny black chip, as well as the gold bonding wires that connect the chip to the red, green, and blue LED segments.

Did you know?

The popular WS2812 individually addressable RGB LED was first released by WorldSemi Corporation in late 2013. The company made a name for itself earlier with LED driver solutions, most notably the WS2811, an SOIC chip that turns a common anode RGB LED into a serially controllable LED.

When they stuffed the brains from the WS2811 into a compact package along with a few LEDs, they were able to create what is probably the most common programmable LED lighting solution available today.

Color-Depth

These are 8-bit RGB LEDs, which means that each LED segment can achieve 256 different levels of brightness, forming 24bit color palette, allowing 16,777,216 (256 x 256 x 256) different colors to be displayed. This means that you can produce almost any color; from teal to sienna to ghost white.

24bit color depth

Chain-able

The great thing about these LEDs is that they are super easy to chain together – the output of one LED can be connected to the input of another to create strips of hundreds of LEDs. Furthermore, only one pin on your microcontroller is required to control an entire strip of LEDs.

ws2812b strip data direction

The WS2812B LED Strip has arrows printed on it, showing the direction in which the data is flowing. When connecting the strip to the microcontroller, make sure the arrows are pointing away from the microcontroller.

Power

The WS2812B operates in the 3.3V to 5V range. The 5V output on an Arduino board, for example, is a perfect power source for these LEDs.

At full brightness, each Red, Green, and Blue LED segment draws approximately 20mA. In other words, each WS2812B LED can pull about 60mA. Even with only ten WS2812Bs connected in series, you’re looking at over 600mA.

So if you plan on stringing together a lot of these things, be sure your power source can handle the extra current draw. For more information, see the section on Estimating Power Requirements.

WS2812B Data Transfer Protocol

Although tens of thousands of WS2812Bs can be controlled through a single wire, the protocol is not like a standard, UART serial interface. So it’s interesting to talk about the WS2812B protocol because it’s so unique and extremely time-sensitive.

Both logic 0 and logic 1 require a square pulse, and the length of the pulse determines which it is: a short pulse (0.35 μs) represents a zero, while a long pulse (0.7 μs) represents a one. The time between consecutive rising edges should be 1.25 μs.

ws2812b data transfer protocol

The data is sent in a sequence of 24 bits followed by a 50µs long low “reset” pulse. This sequence has eight bits for each color, starting with green, then red, then blue.

ws2812b 24 bit grb sequence

The higher the value of a particular color, the brighter it will be. If each color is set to 0, the LED will be off. If each color is set to 255, the LED will be as bright and white as possible.

For example, a 24-bit sequence of 100101100000000000000000, which corresponds to a “10010110” value for green (150 when converted to decimal) and zeros for the remaining 16 bits, produces a medium brightness pure green color with no red or blue mixed in.

Although the WS2812Bs are said to be addressable, this is not technically accurate. The LEDs are actually accessed in a simple but effective cascading fashion. The microcontroller sends color data (24-bit sequence) for each LED in the strip sequentially. When an LED receives data, it has no idea where it is in the strip; it simply stores the first 24-bits of data it receives and sends the rest of the data down the wire. The next LED does the same thing, taking the first 24-bits of data it receives and passing the rest along, and so on.

To summarize, the protocol is highly time-sensitive, so you’ll need a real-time microcontroller like an Arduino, ESP32, AVR, or PIC to run the WS2812Bs; microprocessors such as those on the Raspberry Pi or pcDuino cannot provide a consistently timed pulse. A few microseconds of delay could cause the color of an LED to change from orange to yellow.

WS2812B Strip Pinout

Each LED strip has three connection points: the input connector, the auxiliary power wires, and the output connector. The strip uses 3-pin JST SM connectors.

ws2812b strip pinout

The Input Connector

The input connector is a 3-pin JST SM type female connector.

5V (Red) is the power supply wire. Should be connected to a regulated supply voltage between 3.3V and about 5V.

Data In (Green) transmits data into the strip.

GND (White) is the common ground.

The Output Connector

The output connector has three male pins inside of a plastic connector shroud, each separated by about 0.1″. It is designed to mate with the input connector of another LED strip so that you can connect multiple LED strips together.

5V (Red) is the power supply wire.

Data Out (Green) transmits data out of the strip.

GND (White) is the common ground.

The Auxiliary Power Wires

Both connectors include an extra 5V and GND auxiliary power wires. They allow you to add power to the LED strip at either end, or at multiple points if multiple strips are connected together, preventing excessive voltage drop along the strip(s).

If you’re not going to use these extra wires, clip off any exposed tips and/or insulate with a heat-shrink tube to prevent shorting out.

Estimating Power Requirements

Each WS2812B LED consumes approximately 60 milliamps when it is set to full brightness (bright white) and powered at 5 volts. This means that even with only 60 LEDs turned on, your LED strip could be drawing as much as 3.6 A.

In practice, however, it is uncommon for all LEDs to be turned on in this manner. When mixing colors and displaying animations, the current draw is much lower.

While it is impossible to provide an accurate estimate for every possible scenario, a rough rule of thumb is to use one-third of the total current draw (i.e. 20 mA per WS2812B LED) with no ill effects. However, if you are certain that every pixel must be turned on all the way, use the full 60 mA figure.

Power requirements can be roughly estimated by multiplying the total number of WS2812B LEDs by 20, and then dividing that number by 1000 to get a “rule of thumb” power supply rating (in Amps). If you want to ensure an absolute margin of safety in all situations, use 60 (rather than 20).

For example:

60 WS2812B LEDs × 20 mA ÷ 1000 = 1.2 Amps minimum

-or-

60 WS2812B LEDs × 60 mA ÷ 1000 = 3.6 Amps minimum

The choice of power supply is up to you, but an oversized power supply adds a layer of safety and reliability.

Protecting WS2812B LEDs

It’s important to note that the WS2812B LEDs are very sensitive. So it is highly recommended that you take the following precautions to protect it:

  • Add a large capacitor (100 to 1000 µF, 6.3V or higher) across the + and – terminals as close to your WS2812B as possible. This capacitor will help to smooth out your power supply. This protects the LEDs from the initial onrush of current. Additionally, the WS2812B’s current consumption varies considerably, so this capacitor will serve as a “power reservoir,” storing energy in case the supply dips.
  • Consider adding a 220 and 470 Ohm resistor between your microcontroller’s data output and the input to the LED strip to reduce the noise on that line. Some WS2812B products already include this resistor; if in doubt, add one; there’s no harm in doubling up!
  • Minimize the distance between the microcontroller and the LED strip, so the signal is clear. When the distance is only a meter or two, there is usually no problem, but when it gets much longer, the voltage can start to drop and cause problems.
  • Match the logic level for the data line (green wire) with the input voltage. So if you are powering the WS2812B strip with 5V, you need to connect the data line with a logic level of 5V.
  • Avoid making or modifying connections while the circuit is powered. If necessary, always connect the ground first, then +5V, then data, and disconnect in the reverse order.
  • When using a separate power supply for the LED strip, always apply power to the strip before powering the microcontroller. This prevents the LEDs from attempting to draw power through the data line, which could cause problems for the microcontroller.
  • Avoid touching the LED strip with bare hands. Static electricity from your body can harm the LEDs and lead to malfunctions.

Connecting a WS2812B LED Strip to an Arduino

The WS2812B LED strips come with a standardized JST-SM 3-pin connector and a male pigtail. This makes it easy to connect to a microcontroller as well as chain multiple LED strips together.

The hookup is fairly straightforward. Start by identifying the “input” end of the strip. There will be a solder pad labeled “DIN” or “DI” or just “I”(data input), as well as an arrow indicating the direction in which the data flows.

Connect the Red wire (+5V or +) of the addressable LED strip to the Arduino’s 5V output pin and the White/Yellow wire (GND or –) to the Arduino’s GND pin.

Finally, connect the Green wire (DIN or DI or I) of the LED strip to digital pin 6 on the Arduino, via a 330 Ohm resistor.

In addition, we recommend adding a large capacitor (100 to 1000 µF, 6.3V or higher) across the + and – terminals as close to your WS2812B as possible.

If you have few LEDs you can power the strip directly from the Arduino’s 5V pin. The Arduino can continuously supply only about 500 mA to the 5V pin. Each WS2812B LED can draw up to 60 mA at full brightness. So, as long as only a few LEDs are used and the overall brightness is kept low (around 25%), you can skip the separate DC supply and power directly from the Arduino. When in doubt, use a separate power supply.

wiring ws2812b strip to arduino

If you have a larger project that requires more LEDs, USB power won’t be enough. Instead you should inject power into the strip from an external source.

wiring ws2812b strip to arduino using external supply

Chaining and Distributing Power

Multiple LED strips can be chained together by connecting input connectors to output connectors. When the strips are chained this way, they can be controlled and powered as a single, continuous strip. However, as the chains get longer, the voltage starts to drop, causing the far end of the strip to dim, change color (redder), or stop working entirely.

ws2812b strip voltage drop issue

This voltage drop is proportional to the current flowing through the strip, meaning it increases when the LEDs are set to a higher brightness. The following results were observed when measuring the voltage drop and current draw of LED strips of different lengths with all LEDs set to maximum brightness:

  • The 30 LED 1 m strip drew 1.5 A and had a voltage drop of 0.2 V
  • The 60 LED 2 m strip drew 2.9 A and had a voltage drop of 0.8 V
  • The 120 LED 2 m strip drew 4.7 A and had a voltage drop of 1.4 V
  • The 150 LED 5 m strip drew 4.1 A and had a voltage drop of 2.0 V

To address this issue, it is recommended to power each set of 180 LEDs separately using the auxiliary power wires. Remember, if you are using different power supplies for each set, cut the power wires between sets to avoid shorting out the supplies.

Library Installation

Controlling WS2812B LEDs “from scratch” is quite a challenge, so we’ll be making use of Adafruit’s excellent NeoPixel library so that we can focus on the fun and interesting bits rather than messing around with the strange protocol.

To install the library, navigate to Sketch > Include Library > Manage Libraries… Wait for the Library Manager to download the libraries index and update the list of installed libraries.

manage libraries 2

Filter your search by entering ‘neopixel‘. Look for Adafruit NeoPixel by Adafruit. Click on that entry and then choose Install.

adafruit neopixel library installation

Adafruit NeoPixel is not the only library available; there are others, such as the FastLED library, which has more advanced features and supports other LED chipsets.

NeoPixel Example Sketches

The Adafruit NeoPixel library has a number of example sketches. You can use these example sketches as a basis for developing your own code.

To access the example sketches, navigate to the File > Examples > Adafruit NeoPixel. You will see a selection of example sketches.

adafruit neopixel library examples

Example 1 – Simple Color Wipe

The first example is very simple; all it does is fill strip LEDs one by one with green color. This example will give you some insight into how to use the NeoPixel library so you can create your own animations.

But, before you upload the sketch, you must make a minor change to make it work for you. The LED_PIN and LED_COUNT constants should be set, respectively, to the Arduino pin you’ve connected to the strip’s “DIN” pin and the total number of LEDs in the strip.

// Pin to use to send signals to WS2812B
#define LED_PIN 6

// Number of WS2812B LEDs attached to the Arduino
#define LED_COUNT 12

Once you are done, go ahead and try the sketch.

#include <Adafruit_NeoPixel.h>

// Pin to use to send signals to WS2812B
#define LED_PIN 6

// Number of WS2812B LEDs attached to the Arduino
#define LED_COUNT 12

// Setting up the NeoPixel library
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  strip.begin();           // Initialize NeoPixel object
  strip.setBrightness(10); // Set BRIGHTNESS to about 4% (max = 255)
}

void loop() {
  strip.clear(); // Set all pixel colors to 'off'

  // The first NeoPixel in a strand is #0, second is 1, all the way up
  // to the count of pixels minus one.
  for(int i=0; i<LED_COUNT; i++) {
    // Set the i-th LED to pure green:
    strip.setPixelColor(i, 0, 255, 0);
  
    strip.show();   // Send the updated pixel colors to the hardware.
  
    delay(500); // Pause before next pass through loop
  }
}

If the LEDs are properly connected and powered, you should see something like this.

ws2812b simple color wipe

The WS2812_Definitions.h file defines a huge list of standard colors. In there, you’ll find anything from teal to sienna to ghost white. Simply download the file, place it in your sketch folder before opening the sketch, import it into your sketch.

#include "WS2812_Definitions.h"

And use it to set the color of a pixel, like how the line below changes a pixel’s color to teal.

strip.setPixelColor(i, TEAL);

Code Explanation:

To learn how to write your own sketches, let’s start by dissecting this simple sketch.

The sketch begins by including the Adafruit_NeoPixel header file.

#include <Adafruit_NeoPixel.h>

In that same global area, two constants LED_PIN and LED_COUNT are defined, which specify the Arduino pin you’ve connected to the strip’s “DIN” pin and the total number of LEDs in the strip.

// Pin to use to send signals to WS2812B
#define LED_PIN 6

// Number of WS2812B LEDs attached to the Arduino
#define LED_COUNT 12

Next, an instance of the Adafruit_NeoPixel class called strip is created. That’s what you’ll be referring to from now on to control the strip. There are three arguments in parenthesis: LED_COUNT, LED_PIN and a value indicating the type of NeoPixels that are connected. In most cases you can leave this off and pass just two arguments.

// Setting up the NeoPixel library
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in the strip
// Argument 2 = Arduino pin number
// Argument 3 = Pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products)

In the setup() function, the begin() method is called to prepare the data pin for output.

strip.begin();

The overall brightness of all the LEDs is then adjusted using setBrightness(). This method accepts a single argument, a number between 0 (off) and 255 (maximum brightness). In this case, the brightness is set to 10 (about 4%).

strip.setBrightness(10); // Set BRIGHTNESS to about 4% (max = 255)

The first thing that happens in the loop() function is a call to clear(), which fills the strip with black or “off”.

strip.clear();

Finally, a for loop is used to fill the strip LEDs one by one.

Inside the for loop setPixelColor() is used to set the color of a pixel. The first argument passed to the method specifies the position of the LED along the strip, ranging from 0 to the number of pixels minus one. The next three arguments are the LED color, expressed as red, green and blue brightness levels, where 0 is dimmest (off) and 255 is maximum brightness.

setPixelColor() alone does nothing; you must use show() to “push” the color data to the strip.

for(int i=0; i<LED_COUNT; i++) {
  strip.setPixelColor(i, 0, 255, 0);
  strip.show();
  delay(500);
}

For your information, there are a couple different ways to call setPixelColor(). Each of the following lines can make the i-th LED glow a pure green.

strip.setPixelColor(i, 0, 255, 0);

strip.setPixelColor(i, 0x00FF00);

strip.setPixelColor(i, 0x00, 0xFF, 0x00);

uint32_t green = strip.Color(0, 255, 0);
strip.setPixelColor(i, green);

Example 2 – Rainbow Cycle

This is without a doubt the most popular animation. It scrolls through the entire rainbow of colors while evenly distributing the color spectrum across the LED strip.

#include <Adafruit_NeoPixel.h>

// Pin to use to send signals to WS2812B
#define LED_PIN 6

// Number of WS2812B LEDs attached to the Arduino
#define LED_COUNT 12

// Setting up the NeoPixel library
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  strip.begin();           // Initialize NeoPixel object
  strip.setBrightness(10); // Set BRIGHTNESS to about 4% (max = 255)
  strip.show();            // Initialize all pixels to 'off'
}

void loop() {
  rainbow(10);
}

// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(int wait) {
  // 5 cycles of all colors on wheel
  for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256) {
    strip.rainbow(firstPixelHue);
    strip.show(); // Update strip with new contents
    delay(wait);  // Pause for a moment
  }
}

Just upload the code and enjoy the show!

ws2812b rainbow cycle

Example 3 – Theater Marquee Chasing Lights

This is a mock-up of an old-time theater marquee. It actually causes the LEDs to blink in series, giving the impression that they are chasing around.

#include <Adafruit_NeoPixel.h>

// Pin to use to send signals to WS2812B
#define LED_PIN 6

// Number of WS2812B LEDs attached to the Arduino
#define LED_COUNT 12

// Setting up the NeoPixel library
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  strip.begin();           // Initialize NeoPixel object
  strip.setBrightness(10); // Set BRIGHTNESS to about 4% (max = 255)
  strip.show();            // Initialize all pixels to 'off'
}

void loop() {
  // Do a theater marquee effect in various colors...
  theaterChase(strip.Color(255, 255, 255), 50); // White
  theaterChase(strip.Color(255,   0,   0), 50); // Red
  theaterChase(strip.Color(  0,   0, 255), 50); // Blue
}

// Theater-marquee-style chasing lights. Pass in a color, and 
// a delay time (in ms) between frames.
void theaterChase(uint32_t color, int wait) {
  for(int a=0; a<10; a++) {  // Repeat 10 times...
    for(int b=0; b<3; b++) { //  'b' counts from 0 to 2...
      strip.clear();         //   Set all pixels in RAM to 0 (off)
      // 'c' counts up from 'b' to end of strip in steps of 3...
      for(int c=b; c<strip.numPixels(); c += 3) {
        strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
      }
      strip.show(); // Update strip with new contents
      delay(wait);  // Pause for a moment
    }
  }
}

This is what it looks like.

ws2812b theater marquee chasing lights

Example 4 – Snowflakes

This animation creates an array of randomly blinking white pixels that resembles snowfall.

#include <Adafruit_NeoPixel.h>

// Pin to use to send signals to WS2812B
#define LED_PIN 6

// Number of WS2812B LEDs attached to the Arduino
#define LED_COUNT 12

// Setting up the NeoPixel library
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  strip.begin();           // Initialize NeoPixel object
  strip.setBrightness(10); // Set BRIGHTNESS to about 4% (max = 255)
  strip.show();            // Initialize all pixels to 'off'
}

void loop() {
  snowflakes(100);
}

void snowflakes(uint8_t wait) {
  // Setup the pixel array
  int pixel[60];
  for(int p=0; p<60; p++){
    pixel[p] = random(0,255); 
  }
  
  // Run some snowflake cycles
  for (int j=0; j<200; j++) {
    // Every five cycles, light a new pixel
    if((j%5)==0){
      strip.setPixelColor(random(0,60), 255,255,255);
    }
    
    // Dim all pixels by 10
    for(int p=0; p<60; p++){
      strip.setPixelColor(p, pixel[p],pixel[p],pixel[p] );
      pixel[p] =  pixel[p] - 10;
    }
    strip.show();
    delay(wait);
  }
}

This is what it looks like.

ws2812b snowflakes