The habit of “copy-and-paste programming” — taking an existing project and modifying as little as possible to get it to do a new thing — yields quick results, but applied repeatedly it can result in sub-par code that doesn’t reflect current capabilities. Here’s a few things to watch out for when using the Adafruit_NeoPixel library for Arduino. Some of this applies to the Adafruit_DotStar library as well.
Newer Capabilities That See Little Use Because They’re Not Referenced In Old (Copied-and-Pasted) Code
TIRED: wheel()
WIRED: ColorHSV()
wheel()
is not actually part of the library itself, but was a small helper function in the first “strandtest” example that got copy-and-pasted out of control. This accepts a single unsigned 8-bit value (0-255) representing hue, returning a 32-bit fully-saturated color.
ColorHSV()
has been in the library for ages now but lacks wheel()
’s copy-paste momentum. Use it! This accepts three values: hue (unsigned 16-bit; 0 to 65535), saturation (0-255) and value (brightness) (0-255)…then, an optional fourth boolean argument, if “true,” applies gamma correction to the result. This function produces a much broader range of more nuanced colors than the old wheel()
. It’s documented here.
FUN FACT: recent versions of the NeoPixel and DotStar libraries also include the rainbow()
function, filling a whole strip with one or more cycles of color. As much as I’d like to believe we’ve moved past such clichés, the look remains super popular, almost the Hello World of addressable LEDs…so there it is, all in one function. The new function can also accept saturation and brightness. Some of the examples still implement their own rainbow()
functions and have not been updated for this new hotness yet.
Misunderstood Functions That You Might Want To Avoid Unless You Understand the Ramifications
TIRED: setBrightness()
and getPixelColor()
WIRED: Math.
A huge misunderstanding is trying to use setBrightness()
as an animation effect in itself, as when fading an LED strip to black. It’s really intended for one-time setup, say to tone down the overall brightness of a project, but even that is just a quick band-aid and not an ideal approach. 8-bit AVR microcontrollers were mainstream when Adafruit_NeoPixel was introduced, and doing brightness scaling as data is sent “down the wire” was too demanding…so a compromise was to “decimate” values as they’re passed to setPixelColor()
, meaning you’ll rarely get the same value back from getPixelColor()
. Newer 32-bit microcontrollers could handle it, but the old behavior is kept for consistency. Adafruit_NeoPixel_ZeroDMA and Adafruit_NeoPXL8 don’t suffer this because they’re exclusively for certain 32-bit chips.
setBrightness()
is fine for quick-and-dirty code. You want something less bright. Easy. If you want something more predictable and controllable, render the strip “manually,” perhaps using the fill()
function. Eventually I’ll write a whole guide on math-centric animation, but it’s a huge topic, too much to cover here.
Deprecated Functions Kept Around Only for Backwards Compatibility But Should Be Avoided
TIRED: updateLength()
and updateType()
WIRED: C++ “new
” operator.
These functions allow changing the length and type of a previously-declared NeoPixel strip, the use case being code that was reading configuration data from flash or SD, so the strip parameters aren’t known at compile time and can’t be declared globally. These were merged into the library with little forethought…and while technically they’ll work, it just replicates functionality better handled and already present in the language itself, and these run the risk of fragmenting memory and/or failing to reallocate. These are only present for a small amount of old code and will get deleted some day; they’re not even present in Adafruit_NeoPixel_ZeroDMA or Adafruit_NeoPXL8, and never will be.
Instead, the C++ “new
” operator and function pointers (->
) should be used.
Rather than:
Adafruit_NeoPixel strip(length, pin, type);
…
strip.begin();
strip.setPixelColor(...);
etc.
It would be:
Adafruit_NeoPixel *strip;
…
strip = new Adafruit_NeoPixel(length, pin, type);
strip->begin();
strip->setPixelColor(...);
etc.
The object pointer must be global, not declared in a function, due to the “scope” of variables and their storage.
This is pretty esoteric stuff and only needed if NeoPixel attributes aren’t known at compile-time. The “wrong” way came about because of some copy-and-paste code not wanting to change every “.
” to “->
” — but, sorry, that’s just the Good and Proper way it’s supposed to be done.
Interestingly, updatePin()
(NeoPixel) and updatePins()
(DotStar) remain with no plans to phase out. While not an ideal practice, what these can do is allow the same RAM to be re-used between multiple strips (rather than declaring multiple objects, each with their own buffer). Hacky, esoteric, and only in the most RAM-constrained situations…but valid.
Altogether Different Approaches to Consider
TIRED: Adafruit_NeoPixel? Maybe.
WIRED: FastLED, Adafruit_NeoPixel_ZeroDMA, Adafruit_NeoPXL8, Adafruit_NeoPXL8HDR, CircuitPython with the LED Animation Library.
Adafruit_NeoPixel is considered a starting point. It’s not sophisticated, not abstract…time, pixels and colors are discrete things, programmed brute-force-like. We picture folks using it for quick LED tests before graduating to something else; it’s not a whole “platform.”
FastLED conceptually works at a higher level; animation and color palettes are presented differently, with many details handled by the library and not needed in your code.
Adafruit_NeoPixel_ZeroDMA and Adafruit_NeoPXL8 & Adafruit_NeoPXL8HDR still follow NeoPixel’s non-abstract style, but take advantage of hardware in the latest microcontrollers to free up CPU cycles for better animation.
CircuitPython and the CircuitPython LED Animation Library break free of the write-compile-upload cycle…you can iterate super quick to develop ideas!