Arduino + WS2812

By harald, November 2, 2016

Description

You can purchase long flexible strips of small surface mounted LEDs that come on a spool.  You can cut the strip to length at the cut marks.  You can solder strips together by using the solder pads.

arduino_neopixel001

There are a number of different type of SMD (surface mounted device) LEDs available:

arduino_neopixel002

The most common type of SMD LED is the ‘5050’ square LED light. A single ‘5050’ LED consists of 3 individual miniature LEDs.  These miniature LEDs can be either pure white, all of the same color or red-green-blue (tri-color).  As a result, ‘5050’ LEDs are brighter than ‘3528’ LEDs, but ‘3528’ LEDs used in high density strips will be able to provide a similar brightness whilst being more cost effective.

You can order LED strips:

  • in optional IP65 waterproof silicone tubing.
  • in cool white or warm white.
  • with 60° degrees or 120° degrees viewing angle LEDs.
  • running on 5Vdc or 12Vdc power.
  • 30 LEDs per meter or 60 LEDs per meter.
  • with or without adhesive backing.
  • with an optional white colored rubberized strip.

When choosing an RGB LED strip you may want to pay attention to the brightness in lumens:

Accent Lighting and Mood Lighting ~ 156 – 350
Under cabinet Lighting ~ 175 – 525
Task Lighting with low distance from light source ~ 280 – 437
Task Lighting with higher distance from light source ~ 344 – 687
Indirect lighting in a bedroom/ hotel / vehicle / lobby ~ 375 – 562
Industrial lighting / signage / tube replacements ~ 500 – 950

Analog LED strips

Analog LED strips come in singlecolor strips and multi-color strips.  Analog multi-color strips are also known as ‘analog RGB LED strips’.

Analog single-color LED strips simply have a “+” and “GND” terminal.  You can use PWM to change the brightness.  You can order these strips in pure white (cool / warm) or a single color.  Each individual LED is a single color.

The multi-color strips use “tri-LEDs” consisting of 3 miniature Red-Green-Blue (RGB) LEDs inside a single ‘5050’ LED.

arduino_neopixel010
Single color analog LED strip (top) and multi-color analog LED strip (bottom).

Analog multi-color strips have all the LEDs connected in parallel and so they acts like one huge tri-color LED.  You can set the entire strip to any color you want, but you can’t set a different color each individual LED. They are very easy to use and fairly inexpensive.  Analog multi-color LED strips have a +12V terminal and separate Red, Green and Blue terminals for the miniature RGB LEDs inside the (tri-color) ‘5050’ LEDs.  Using pulse width modulation (PWM) we can change the intensity of the Red, Green or Blue miniature LED from 0V to 12V and generate the desired overall color and brightness.

arduino_neopixel003

You can use a sharp knife to remove some of the protective tubing and solder wires to the solder tabs.

arduino_neopixel004

Use heatshrink tubing to make the LED strip IP65 waterproof if required.

arduino_neopixel005

Transistor

An Arduino digital I/O pin can output a maximum of 40mA.  The total amount of current across all pins should not exceed 500mA.  A single ‘5050’ LED will consume up to 60 mA.  It is good practice not to power a LED strip directly from a digital I/O pin.  A 1 meter LED strip with 60 ‘5050’ LEDs per meter =  60 * 60 mA = 3600 mA = 3.6 A max.

The easiest way to power a LED strip is by using an external power supply.  We can use transistors to switch the LED strip on or off.  A transistor uses a small current flowing from the Base to the Emitter to create a conductive path between the Collector and Emitter.  It is a bit like how the first small patch of loose snow moving down a mountain slope causes an avalanche.  A TIP120 transistor (darlington pair) can handle 60V 5A across the Collector-Emitter junction and only needs 2mA applied to the Base-Emitter junction to create a short between Collector and Emitter, just like a switch.

Hardware Required

  • Arduino Board
  • Analog RGB LED strip
  • External 12V power adapter
  • TIP120 transistor
  • 10kOhm resistor

Circuit

arduino_neopixel006

Code

    // Color swirl!

    // Connect an RGB LED to the PWM pins as indicated
     
    #define REDPIN 9
    #define GREENPIN 10
    #define BLUEPIN 11
     
    #define FADESPEED 5     // make this higher to slow down
     
    void setup() {
      pinMode(REDPIN, OUTPUT);
      pinMode(GREENPIN, OUTPUT);
      pinMode(BLUEPIN, OUTPUT);
    }
     
     
    void loop() {
      int r, g, b;
     
      // fade from blue to violet
      for (r = 0; r < 256; r++) { 
        analogWrite(REDPIN, r);
        delay(FADESPEED);
      } 
      // fade from violet to red
      for (b = 255; b > 0; b--) { 
        analogWrite(BLUEPIN, b);
        delay(FADESPEED);
      } 
      // fade from red to yellow
      for (g = 0; g < 256; g++) { 
        analogWrite(GREENPIN, g);
        delay(FADESPEED);
      } 
      // fade from yellow to green
      for (r = 255; r > 0; r--) { 
        analogWrite(REDPIN, r);
        delay(FADESPEED);
      } 
      // fade from green to teal
      for (b = 0; b < 256; b++) { 
        analogWrite(BLUEPIN, b);
        delay(FADESPEED);
      } 
      // fade from teal to blue
      for (g = 255; g > 0; g--) { 
        analogWrite(GREENPIN, g);
        delay(FADESPEED);
      } 
    }

Control Unit

Analog RGB strips sold on the internet often come with a 28 or 44 function control unit.

arduino_neopixel007

This control unit uses pulse width modulation to change the brightness of each of he Red, Green and Blue channels of the analog LED strip similar to our code.

Digital RGB LED strip

Digital RGB LED strips are also called pixel (addressable) RGB LED strips or Neopixel LED strips.  Each LED in the strip is connected to a small chip.  This chip allow you to control the brightness and color of each individual(!) LED.  The chips used are either WS2801, WS2811 or WS2812.

(!) Unlike the analog RGB LED strips most Digital RGB LED strips operate on 5 Volts!

Differences between WS2801, WS2811 and WS2812

Before we start, we should probably identify the differences between the WS2801, WS2811 and WS2812 chips.  Most projects and descriptions out there discus these sometimes mixed, and for one who dives into LED strips for the first time, these models numbers might be confusing.

The model numbers WS2801, WS2811 and WS2812 actually refer to different “things”:

  • The WS2801 and WS2811 are LED driver chips.  These IC’s can control up to 3 LEDs, typically Red, Green and Blue. Positioned close together, so you as a viewer will see the mixed color result.  The WS2801 used to be quite popular but is no longer.
  • The WS2812 however is a WS2811 placed inside a 5050 LED package.  The 5050 LED is a very common 3 LED (Red, Green, Blue) package, in one 5mm x 5mm case.

In the illustration below you’ll see the difference:

arduino_neopixel009
On the left a 5050 RGB LED, on the right a WS2812 which combines a 5050 RGB LED with a WS2811 chip.
Note how the layout of the “silver” tracks are almost identical in both images, yet the black (IC) block and the tiny wires are different (right).  Where the WS2801 strips needed 4 wires, the WS2811/WS2812 strips only needs 3 wires. The WS2801 uses a separate clock line, which can be seen as an advantage, whereas the WS2811/WS2812 does not. The WS2811/WS2812 depends on sending data matching a very tight timing. The advantage of the WS2812 though, is that production of these combo’s in strips is easier and therefor cheaper, and each RGB LED takes much less space on strips.

We will be using the WS2812 digital LED strips.

Hardware Required

  • Arduino Board
  • Digital RGB LED strip (using WS2812 chips and 5050 tri-color LEDs).
  • External 5V power adapter
  • 470 Ohm resistor
  • 1000uF 60V+ electrolytic capacitor

The resistor is used to protect against voltage spikes and large currents.  As the resistor value is very low and these resistors are only 0.25W, they basically act like a fuse and will protect your precious digital RGB LED strip data line (WS2812 chips) from power surges.

A 1000uF electrolytic capacitor prevents the initial onrush of current from damaging the pixels. The capacitor is not needed for a few (say 1-3) pixels, but definitely recommended for anything more than that, otherwise you risk to damage the pixels!

Circuit

arduino_neopixel008

It’s important to pay attention to the arrow on the digital RGB LED strip; if you use your strip in the wrong “direction”, it will not work.  Simply connecting +5V and GND will at best flash up your strip for a fraction of a second.  The LEDs need to be “told” to be ON, so without data signal your LEDs will remain OFF.

Code

As the WS2812 does not use both a clock and data line but only a data line, timing has to be very precise.  There are two libraries that are often used to drive digital RGB LED strips:

Example 1 (Neopixel)

#include <Adafruit_NeoPixel.h>
#define PIN 7
// Parameter 1 = number of pixels in strip
// Parameter 2 = pin number (most are valid)
// Parameter 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)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800);
void setup() {
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
}

void loop() {
  // Some example procedures showing how to display to the pixels:
  colorWipe(strip.Color(255, 0, 0), 50); // Red
  colorWipe(strip.Color(0, 255, 0), 50); // Green
  colorWipe(strip.Color(0, 0, 255), 50); // Blue
  // Send a theater pixel chase in...
  theaterChase(strip.Color(127, 127, 127), 50); // White
  theaterChase(strip.Color(127,   0,   0), 50); // Red
  theaterChase(strip.Color(  0,   0, 127), 50); // Blue
  rainbow(20);
  rainbowCycle(20);
  theaterChaseRainbow(50);
}

// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, c);
      strip.show();
      delay(wait);
  }
}
void rainbow(uint8_t wait) {
  uint16_t i, j;
  for(j=0; j<256; j++) {
    for(i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel((i+j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
  uint16_t i, j;
  for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
    for(i=0; i< strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
  }
}
//Theatre-style crawling lights.

void theaterChase(uint32_t c, uint8_t wait) {
  for (int j=0; j<10; j++) {  //do 10 cycles of chasing
    for (int q=0; q < 3; q++) {
      for (int i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, c);    //turn every third pixel on
      }
      strip.show();
      delay(wait);
      for (int i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }

}

//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
  for (int j=0; j < 256; j++) {     // cycle all 256 colors in the wheel
    for (int q=0; q < 3; q++) {
        for (int i=0; i < strip.numPixels(); i=i+3) {
          strip.setPixelColor(i+q, Wheel( (i+j) % 255));    //turn every third pixel on
        }
        strip.show();
        delay(wait);
        for (int i=0; i < strip.numPixels(); i=i+3) {
          strip.setPixelColor(i+q, 0);        //turn every third pixel off
        }
    }
  }
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  if(WheelPos < 85) {
   return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } else if(WheelPos < 170) {
   WheelPos -= 85;
   return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } else {
   WheelPos -= 170;
   return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
}

Example 2 (FastLED)

#include "FastLED.h"
// Number of RGB LEDs in the strand
#define NUM_LEDS 60 
// Define the array of leds
CRGB leds[NUM_LEDS];
// Arduino pin used for Data
#define PIN 7

void setup()
{
  FastLED.addLeds<NEOPIXEL, PIN, RGB>(leds, NUM_LEDS);
}

void loop() { 
  // one at a time
  for(int j = 0; j < 3; j++) { 
    for(int i = 0 ; i < NUM_LEDS; i++ ) {
      memset(leds, 0, NUM_LEDS * 3);
      switch(j) { 
        case 0: leds[i].r = 255; break;
        case 1: leds[i].g = 255; break;
        case 2: leds[i].b = 255; break;
      }
      FastLED.show();
      delay(10);
    }
  }

  // growing/receding bars
  for(int j = 0; j < 3; j++) { 
    memset(leds, 0, NUM_LEDS * 3);
    for(int i = 0 ; i < NUM_LEDS; i++ ) {
      switch(j) { 
        case 0: leds[i].r = 255; break;
        case 1: leds[i].g = 255; break;
        case 2: leds[i].b = 255; break;
      }
      FastLED.show();
      delay(10);
    }
    for(int i = NUM_LEDS-1 ; i >= 0; i-- ) {
      switch(j) { 
        case 0: leds[i].r = 0; break;
        case 1: leds[i].g = 0; break;
        case 2: leds[i].b = 0; break;
      }
      FastSPI_LED.show();
      delay(1);
    }
  }

  // Fade in/fade out
  for(int j = 0; j < 3; j++ ) { 
    memset(leds, 0, NUM_LEDS * 3);
    for(int k = 0; k < 256; k++) { 
      for(int i = 0; i < NUM_LEDS; i++ ) {
        switch(j) { 
          case 0: leds[i].r = k; break;
          case 1: leds[i].g = k; break;
          case 2: leds[i].b = k; break;
        }
      }
      FastLED.show();
      delay(3);
    }
    for(int k = 255; k >= 0; k--) { 
      for(int i = 0; i < NUM_LEDS; i++ ) {
        switch(j) { 
          case 0: leds[i].r = k; break;
          case 1: leds[i].g = k; break;
          case 2: leds[i].b = k; break;
        }
      }
      FastLED.show();
      delay(3);
    }
  }
}

Links

Additional information:

https://learn.adafruit.com/adafruit-neopixel-uberguide/

https://github.com/FastLED/FastLED/wiki/Basic-usage