It’s assumed at this point that you have the Adafruit_NeoPixel library for Arduino installed and have run the strandtest example sketch successfully. If not, return to the prior page for directions to set that up.
To learn about writing your own NeoPixel sketches, let’s begin by dissecting the strandtest sketch…
All NeoPixel sketches begin by including the header file:
#include <Adafruit_NeoPixel.h>
The block of code that follows is mostly descriptive comments. Only a couple lines are really doing any work:
// Which pin on the Arduino is connected to the NeoPixels? // On a Trinket or Gemma we suggest changing this to 1: #define LED_PIN 6 // How many NeoPixels are attached to the Arduino? #define LED_COUNT 60 // Declare our NeoPixel strip object: Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800); // Argument 1 = Number of pixels in NeoPixel strip // Argument 2 = Arduino pin number (most are valid) // 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)
The first few lines assign numbers to the symbols “LED_PIN” and “LED_COUNT” for later reference. It doesn’t need to be done this way, but makes it easier to change the pin and length where the NeoPixels are connected without digging deeper into the code.
The last line declares a NeoPixel object. We’ll refer to this by name later to control the strip of pixels. There are three parameters or arguments in parenthesis:
- The number of sequential NeoPixels in the strip. In the example this is set to LED_COUNT, which was defined as 60 above, equal to 1 meter of medium-density strip. Change this to match the actual number you’re using.
- The pin number to which the NeoPixel strip (or other device) is connected. Normally this would be a number, but we previously declared the symbol LED_PIN to refer to it by name here.
- A value indicating the type of NeoPixels that are connected. In most cases you can leave this off and pass just two arguments; the example code is just being extra descriptive. If you have a supply of classic “V1” Flora pixels, those require NEO_KHZ400 + NEO_RGB to be passed here. RGBW NeoPixels also require a different value here: NEO_RGBW.
void setup() { strip.begin(); strip.show(); // Initialize all pixels to 'off' }
In the strandtest example, loop() doesn’t set any pixel colors on its own — it calls other functions that create animated effects. So let’s ignore it for now and look ahead, inside the individual functions, to see how the strip is controlled.
There are a couple different ways to set the color of a pixel. The first is:
strip.setPixelColor(n, red, green, blue);
or, if you're using RGBW strips:
strip.setPixelColor(n, red, green, blue, white);
The first argument — n in this example — is the pixel number along the strip, starting from 0 closest to the Arduino. If you have a strip of 30 pixels, they’re numbered 0 through 29. It’s a computer thing. You’ll see various places in the code using a for loop, passing the loop counter variable as the pixel number to this function, to set the values of multiple pixels.
The next three arguments are the pixel color, expressed as red, green and blue brightness levels, where 0 is dimmest (off) and 255 is maximum brightness. The last optional argument is for white, which will only be used if the strip was defined during creation as an RGBW type and the strip actually is RGBW type.
To set the 12th pixel (#11, counting from 0) to magenta (red + blue), you could write:
strip.setPixelColor(11, 255, 0, 255);
to set the 8th pixel (#7 counting from 0) to half-brightness white (with an RGBW strip), with no light from red/green/blue, use:
strip.setPixelColor(7, 0, 0, 0, 127);
strip.setPixelColor(n, color);
You can also convert separate red, green and blue values into a single 32-bit type for later use:
uint32_t magenta = strip.Color(255, 0, 255);
Then later you can just pass “magenta” as an argument to setPixelColor rather than the separate red, green and blue numbers every time.
You can also (optionally) add a white component to the color at the end, like this:
uint32_t greenishwhite = strip.Color(0, 64, 0, 64);
setPixelColor() does not have an immediate effect on the LEDs. To “push” the color data to the strip, call show():
strip.show();
Multiple pixels can be set to the same color using the fill() function, which accepts one to three arguments. Typically it’s called like this:
strip.fill(color, first, count);
“color” is a packed 32-bit RGB (or RGBW) color value, as might be returned by strip.Color(). There is no option here for separate red, green and blue, so call the Color() function to pack these into one value.
“first” is the index of the first pixel to fill, where 0 is the first pixel in the strip, and strip.numPixels() - 1 is the last. Must be a positive value or 0.
“count” is the number of pixels to fill. Must be a positive value.
If called without a count argument (only color and first), this will from first to the end of the strip.
If called without first or count arguments (only color), the full strip will be set to the requested color.
If called with no arguments, the strip will be filled with black or “off,” but there’s also a different syntax which might be easier to read:
strip.clear();
uint32_t color = strip.getPixelColor(11);
This returns a 32-bit merged RGB color value. This is always RGB, even if the “ColorHSV()” function (described below) was used.
uint16_t n = strip.numPixels();
strip.setBrightness(64);
Just like setPixel(), this does not have an immediate effect. You need to follow this with a call to show().
setBrightness() was intended to be called once, in setup(), to limit the current/brightness of the LEDs throughout the life of the sketch. It is not intended as an animation effect itself! The operation of this function is “lossy” — it modifies the current pixel data in RAM, not in the show() call — in order to meet NeoPixels’ strict timing requirements. Certain animation effects are better served by leaving the brightness setting at the default maximum, modulating pixel brightness in your own sketch logic and redrawing the full strip with setPixel().
HSV (Hue-Saturation-Value) Colors…
The NeoPixel library has some support for colors in the “HSV” (hue-saturation-value) color space. This is a different way of specifying colors than the usual RGB (red-green-blue). Some folks find it easier or more “natural” to think about…or quite often it’s just easier for certain color effects (the popular rainbow cycle and such).
In the NeoPixel library, hue is expressed as a 16-bit number. Starting from 0 for red, this increments first toward yellow (around 65536/6, or 10922 give or take a bit), and on through green, cyan (at the halfway point of 32768), blue, magenta and back to red. In your own code, you can allow any hue-related variables to overflow or underflow and they’ll “wrap around” and do the correct and expected thing, it’s really nice.
Saturation determines the intensity or purity of the color…this is an 8-bit number ranging from 0 (no saturation, just grayscale) to 255 (maximum saturation, pure hue). In the middle, you’ll start to get sort of pastel tones.
Value determines the brightness of a color…it’s also an 8-bit number ranging from 0 (black, regardless of hue or saturation) to 255 (maximum brightness).
setPixelColor() and fill() both still want RGB values though, so we convert to these from HSV by using the ColorHSV() function:
uint32_t rgbcolor = strip.ColorHSV(hue, saturation, value);
If you just want a “pure color” (fully saturated and full brightness), the latter two arguments can be left off:
uint32_t rgbcolor = strip.ColorHSV(hue);
In either case, the resulting RGB value can then be passed to a pixel-setting function, e.g.:
strip.fill(rgbcolor);
There is no corresponding function to go the other way, from RGB to HSV. This is on purpose and by design, because conversion in that direction is often ambiguous — there may be multiple valid possibilities for a given input. If you look at some of the example sketches you’ll see they keep track of their own hues…they don’t assign colors to pixels and then try to read them back out again.
…and Gamma Correction
Something you might observe when working with more nuanced color changes is that things may appear overly bright or washed-out. It’s generally not a problem with simple primary and secondary colors, but becomes more an issue with blends, transitions, and the sorts of pastel colors you might get from the ColorHSV() function. Numerically the color values are correct, but perceptually our eyes make something different of it, as explained in this guide.
The gamma32() function takes a packed RGB value (as you might get out of Color() or ColorHSV()) and filters the result to look more perceptually correct.
uint32_t rgbcolor = strip.gamma32(strip.ColorHSV(hue, sat, val));
You might notice in strandtest and other example sketches that we never use ColorHSV() without passing the result through gamma32() before setting a pixel’s color. It’s that desirable.
However, the gamma32 operation is not built in to ColorHSV() — it must be called as a separate operation — for a few reasons, including that advanced programmers might want to provide a more specific color-correction function of their own design (gamma32() is a “one size fits most” approximation) or may need to keep around the original “numerically but not perceptually correct” numbers.
There is no corresponding reverse operation. When you set a pixel to a color filtered through gamma32(), reading back the pixel value yields that filtered color, not the original RGB value. It’s precisely because of this sort of decimation that advanced NeoPixel programs often treat the pixel buffer as a write-only resource…they generate each full frame of animation based on their own program state, not as a series of read-modify-write operations.
- forgetting to call strip.begin() in setup().
- forgetting to call strip.show() after setting pixel colors.
Adafruit_NeoPixel strip_a = Adafruit_NeoPixel(16, 5); Adafruit_NeoPixel strip_b = Adafruit_NeoPixel(16, 6);
See note above; setBrightness() is designed as a one-time setup function, not an animation effect.
Also see the “Advanced Coding” page — there’s an alternative library that includes “nondestructive” brightness adjustment, among other features!
Pixels Gobble RAM
Each NeoPixel requires about 3 bytes of RAM. This doesn’t sound like very much, but when you start using dozens or even hundreds of pixels, and consider that the mainstream Arduino Uno only has 2 kilobytes of RAM (often much less after other libraries stake their claim), this can be a real problem!For using really large numbers of LEDs, you might need to step up to a more potent board like the Arduino Mega or Due. But if you’re close and need just a little extra space, you can sometimes tweak your code to be more RAM-efficient. This tutorial has some pointers on memory usage.
Arduino Library Best Practices
This blog post has suggestions on writing “modern” NeoPixel code to avoid old copy-and-paste bad habits and deprecated techniques.
Text editor powered by tinymce.