FastLED vs Adafruit_NeoPixel for RGBW LEDs

Multicolor LED strips generally come in two flavors – solid color and addressable.  Solid color strips, as the name implies, have all of their LEDs display the same color.  Addressable strips on the other hand allow you to control the color value of every individual LED in the strip.  The most popular individually-addressable LEDs on the market at the moment are Adafruit Industries’ NeoPixels, based on the WS2182 chipset.  They come in a variety of physical packages including strips, rings, and matrices.

Each “NeoPixel” is a self-contained 5050 LED with red, green, blue, and optionally white LEDs with an embedded controller.  To set a pixel’s color you need to send a control signal containing color information.  The pixel’s controller interprets the digital signal to determine its own color and then passes the data stream along to the next pixel in line.  This allows you to string many pixels together and control all of them with a single microcontroller pin.

To start displaying colors on our LEDs we first need to convert our properly formatted color information into a precise datastream for embedded LED controllers.  Fortunately others have already done most of the heavy lifting for us.  Instead of writing the rather difficult assembly code from scratch we can use an Arduino library, a set of prewritten routines for a given project or device – in this case addressable LEDs.

As of this writing there are two main libraries for addressable LEDs: FastLED and Adafruit_NeoPixel.  In almost every way FastLED is the superior choice.  It deals with the stored color data more logically, has better scaling capabilities, and has many more functions for manipulating color information (including color temperature).

But unfortunately FastLED does not support RGBW strips!

The creator of FastLED has said that he’s working on RGBW implementation, but that he’ll basically need to rewrite a large portion of the library from scratch.  There is no ETA at the moment, and I don’t want to wait to get this project going.  The Adafruit_NeoPixel library has almost no color manipulation functions, but it does work perfectly for sending formatted color information to RGBW strips.

I spent a couple of days brainstorming how to mix the color functions of FastLED with the bit bang function of the NeoPixel library (i.e. its ability to output to RGBW strips) without modifying either, and the best implementation I came up with was 3x slower than using either library alone.  The key sticking point is because the color information is formatted differently between the two libraries, you have to either store all color information twice (a CRGB array and a *pixels GRBW array), or convert the GRBW information to a temporary CRGB object if you need to call a FastLED function.  I’d imagine that the best option would be to rewrite how the NeoPixel library stores its color information to include a CRGB object, but for the time being I’m going to use the Adafruit_NeoPixel library vanilla.

If you have a good solution for mixing the two libraries together, please let me know!

11 thoughts on “FastLED vs Adafruit_NeoPixel for RGBW LEDs”

  1. Dang, those RGBWs would be perfect in my project, but only if I can use all my FastLED algorithms to generate the RGB patterns. Glad I did a quick search before clicking “add to cart”. Thanks for saving me some time and money!

  2. For a recent project, I came up with a solution that doesn’t slow things down or require double writes. Essentially, I’m using type casting so I can treat a CRGBW array as a CRGB array for the native FastLED calls that require CRGB.

    In my header, I define a CRGBW struture and some support functions:
    struct CRGBW {
    union {
    struct {
    union {
    uint8_t g;
    uint8_t green;
    };
    union {
    uint8_t r;
    uint8_t red;
    };
    union {
    uint8_t b;
    uint8_t blue;
    };
    union {
    uint8_t w;
    uint8_t white;
    };
    };
    uint8_t raw[4];
    };

    inline CRGBW& setHSVW (uint8_t hue, uint8_t sat, uint8_t val, uint8_t white) __attribute__((always_inline))
    {
    hsv2rgb_rainbow( CHSV(hue, sat, val), (CRGB &) *this);
    this->white = white;
    return *this;
    }

    inline CRGBW& setRGBW (uint8_t red, uint8_t grn, uint8_t blu, uint8_t white) __attribute__((always_inline))
    {
    this->r = red;
    this->g = grn;
    this->b = blu;
    this->white = white;
    return *this;
    }

    };

    Then, in my code, I allocate my LED Strip as CRGBW:

    CRGBW allLights[NUM_LEDS_TOTAL];

    I then use typecasting to create a CRGB pointer to the same data:

    CRGB *allLightsRGB = (CRGB *) &allLights[0];

    I use this pointer only in the addLEDs call that needs a CRGB structure.

    When setting up the LEDs, I use an adjusted LED account, that takes into account the increased size caused by the white components.

    FastLED.addLeds(allLightsRGB, NUM_LEDS_TOTAL_adj);

    NUM_LEDS_TOTAL_adj is essentially a rounded up version of NUM_LEDS_TOTAL*4/3. I typically hard code it. e.g.

    int NUM_LEDS_TOTAL = 30;
    int NUM_LEDS_TOTAL_adj = 40;

    When modifying my colors, I modify my allLights values directly, like so:

    allLights[idx].setRGBW(r,g,b,w);
    (or setHSVW(h,s,v,w) )

    and call FastLED.show() as usual.

    1. Wow! Nice work, I’ll have to give that a try later. What strips are you using? Are you using a stock FastLED controller, or did you add your own?

      1. A friend provided the strips, so not sure which ones, but they were fairly tightly spaced (we used them to make a Unicorn horn that pulsated to music). The controller was a Teensy.

        1. What I mean is, FastLED has a number of controller types to manage different LED strips and protocols. Do you remember which LED type you were using? Or did you use the NeoPixel library to talk to the LEDs?

          1. Oh whoops, WS2812B. Was actually using four strips, so the code looked like this:

            FastLED.addLeds(strip1, NUM_LEDS_1_adj);
            FastLED.addLeds(strip2, NUM_LEDS_2_adj);
            FastLED.addLeds(strip3, NUM_LEDS_3_adj);
            FastLED.addLeds(strip4, NUM_LEDS_4_adj);

  3. But these were all typecast to point to the same long array, so I could encode color values in the same loop…

    CRGBW allLights[NUM_LEDS_TOTAL];
    CRGB *strip1 = (CRGB *) &allLights[0];
    CRGB *strip2 = (CRGB *) &allLights[26];
    CRGB *strip3 = (CRGB *) &allLights[41];
    CRGB *strip4 = (CRGB *) &allLights[65];

    1. Thanks again for sharing your method Jim, this is great work! I’ve modified your code slightly and created a new post explaining how it works, which you can find here. Let me know if you’d like me to tweak your acknowledgements in any way!

Leave a Reply

Your email address will not be published. Required fields are marked *