It’s assumed at this point that you have the Adafruit_DotStar 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 DotStar sketches, let’s begin by dissecting the strandtest sketch…
All DotStar sketches begin by including the header file:
#include <Adafruit_DotStar.h>
Just below this in the strandtest example are some extra lines that are sometimes needed…
Most microcontrollers have some kind of SPI bus (a high-speed serial interface to devices). If so, there’s an SPI-related header file that must be included. But a few microcontrollers (such as the diminutive Adafruit Gemma and Trinket) don’t have SPI, in which case that line should be commented out (preceded with a “//”) or simply deleted.
Speaking of Gemma and Trinket…the next line, normally commented out, should be enabled (remove the “//”) if using one of those boards.
// Because conditional #includes don't work w/Arduino sketches... #include <SPI.h> // COMMENT OUT THIS LINE FOR GEMMA OR TRINKET //#include <avr/power.h> // ENABLE THIS LINE FOR GEMMA OR TRINKET
The next few lines define the length of the strip (30 pixels in our example, but you can change this to more or less to match your setup) and which pins to use for the data and clock signals. The wiring diagrams previously shown had these on 4 and 5…but they can usually be any two pins, whatever works best for you and matches your wiring:
#define NUMPIXELS 30 // Number of LEDs in strip // Here's how to control the LEDs from any two pins: #define DATAPIN 4 #define CLOCKPIN 5 Adafruit_DotStar strip(NUMPIXELS, DATAPIN, CLOCKPIN, DOTSTAR_BRG);
This is how we declare a DotStar object. We’ll refer to this by name later to control the strip of pixels.
The last parameter is optional — this is the color data order of the DotStar strip, which has changed over time in different production runs. Default is DOTSTAR_BRG, so change this if you have an earlier strip. If the LEDs are basically working but coming up the wrong colors, this is usually the reason why…swap around the R, G and B until things look right.
If using an SPI-capable microcontroller, this usually provides better performance. However, this must be wired to specific pins, and it varies among microcontrollers. For example, on the Arduino Uno the SPI MOSI and SCK signals are on pins 11 and 13 respectively. You’ll need a pinout diagram for other boards, look for the SPI MOSI and SCK pins!
The object declaration in this case is a little different…simply leave out the data and clock pin numbers:
Adafruit_DotStar strip(NUMPIXELS, DOTSTAR_BRG);
The SPI-or-not decision affects the wiring and strip declaration, but after that everything is the same regardless.
Now, in the setup()
function, call begin()
to prepare the data and clock pins for DotStar output:
void setup() { strip.begin(); strip.show(); // Initialize all pixels to 'off' }
The second line, strip.show()
, isn’t absolutely necessary, it’s just there to be thorough. That function pushes data out to the pixels…since no colors have been set yet, this initializes all the DotStars to an initial “off” state in case some were left lit by a prior program.
To set the color of an individual DotStar LED, use the setPixelColor()
function, which can work a couple of different ways. Easiest usually involves passing a pixel number and red, green and blue color values:
strip.setPixelColor(index, red, green, blue);
index
is the pixel number, starting at 0 (first pixel), then 1 (second pixel), up to the number of pixels in the strip minus one. Pixel indices outside this range will simply be ignored.
red
, green
, blue
specify the color, each in a range from 0 (off) to 255 (maximum brightness).
For example, here’s how we’d set the first pixel (index 0) to an orangey color (100% red, 50% green, 0% blue):
strip.setPixelColor(0, 255, 127, 0);
An alternate syntax takes just two arguments, a pixel index (same as before) and a single color value as a “packed” 24-bit integer (often in hexadecimal notation…something advanced programers may be more comfortable working in):
strip.setPixelColor(index, color);
You can “pack” 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.
setPixelColor()
does not have an immediate effect on the LEDs. To “push” the color data to the strip, call show()
:
strip.show();
This updates the whole strip at once, and despite the extra step is actually a good thing. If every call to setPixelColor()
had an immediate effect, animation would appear jumpy rather than buttery smooth.
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();
You can query the color of a previously-set pixel using getPixelColor()
:
uint32_t color = strip.getPixelColor(11);
This returns a 32-bit merged color value (only the least 24 bits are used).
The number of pixels in a previously-declared strip can be queried using numPixels()
:
uint16_t n = strip.numPixels();
The overall brightness of all the LEDs can be adjusted using setBrightness()
. This takes a single argument, a number in the range 0 (off) to 255 (max brightness). For example, to set a strip to 1/4 brightness:
strip.setBrightness(64);
Just like setPixel()
, this does not have an immediate effect.You need to follow this with a call to show()
.
HSV (Hue-Saturation-Value) Colors…
The DotStar 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 DotStar 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 some 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 DotStar 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.
There are two main culprits for this:
- forgetting to call
strip.begin()
insetup()
. - forgetting to call
strip.show()
after setting pixel colors.
Another (less common) possibility is running out of RAM — see the last section below. If the program sort of works but has unpredictable results, consider that.
Certainly! Each requires its own declaration with a unique name:
Adafruit_DotStar strip_a(16, 3, 4); Adafruit_DotStar strip_b(16, 5, 6);
The above declares two distinct DotStar objects, one with data and clock on pins 3 and 4, the other on pins 5 and 5. Each contains 16 pixels and is using the default color order.
In many cases, yes. All the strips will then show exactly the same thing. This only works up to a point though…four strips wired to the same two pins is a good and reliable number. More than that starts to get “iffy.”
Different versions of DotStar LEDs expect to receive color data in a different order…and occasionally it may change if it improves production efficiency or yield.
The last argument to the Adafruit_DotStar()
constructor lets you try different color orders. Default if unspecified is DOTSTAR_BRG
.
We don’t change this in each release of the library because it’s just an endless game of Whack-a-Mole…it’s only a matter of time before the manufacturers use a different order again. Find what works with the hardware you have.
Most NeoPixel Code Adapts Easily to DotStars
Nearly any NeoPixel code should compile and run with DotStars, just changing the library #include and the strip declaration…the remaining functions are roughly the same. There may be a few exceptions, but this is usually esoteric code that’s doing NeoPixel-specific hardware trickery.
Pixels Gobble RAM
Each DotStar 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 one of our M0- or M4-equipped Metro Express or Feather boards. 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.
Page last edited March 08, 2024
Text editor powered by tinymce.