Insights

Some Finer Points of NeoPixels

  • The “60 milliamps” rule of thumb is just that…a rule of thumb, not hard science. Actual peak use is slightly less. But for napkin calculations and mental math, 60 is much easier than fifty-whatever. (The mini NeoPixels on Circuit Playground and “Mini Skinny” NeoPixel strips are more like 35 mA each, max. And RGBW NeoPixels, with a fourth LED element inside, may draw closer to 80 mA each.)
  • Also, 60 (or 35, or 80) milliamps is a worst case. When animating and mixing colors, current draw will be less. Sometimes much less. That’s what this guide is all about.
  • Even when set to color 0 (no LED elements lit), the driver logic inside each NeoPixel still uses a tiny bit of current. It’s under 1 milliamp per pixel…but with lots of pixels this could add up.

Strategies

Most of these tips are demonstrated in the Arduino sketch on the last page. That code is designed for Circuit Playground. If you don’t have one, that’s okay! The concepts explained here are applicable to any NeoPixel circuit.

If you do have a Circuit Playground board…upload the sketch and follow along! Use the left and right buttons to switch between different NeoPixel display modes. The Serial Monitor window shows a rough estimate of the required current in the active mode (this is just a software-based estimate, it’s not actually measuring power use).

Million-pixels-all-on-white oddly reminds me of the Swensen’s ice cream sundae bar of my youth. Liking each of the toppings individually, I’d heap everything on my sundae with no regard to whether the flavors mingled well, ending up with a sickeningly sweet mess. NeoPixels are like that sometimes. A tasteful result stems from showing some restraint.

Strategy: Use. Fewer. Pixels.

I can’t emphasize this one enough. That’s why it’s first.

Don’t use 144-LED-per-meter NeoPixel strip where 60/m will do. Don’t use 60/m where 30/m will do. Don’t encrust an entire thing in NeoPixels when some individually-placed pixels or shapes will make a good impression. A candy red sports car is dazzling, but a well-placed racing stripe can be just as eye-catching.

Careful NeoPixel placement not only saves power, it also requires less RAM on the microcontroller, can improve frame rates, and with fewer points of failure can make a project more robust.

Consider using “Mini Skinny” NeoPixel strips. Not as bright as their full-size brethren, but need only about 60% as much current.

Put some extra effort into everything around the pixels; don’t make them a gimmick. Design a prop or a character with strong appeal even if it had no NeoPixels at all! Make them part of a bigger thing.

Strategy: Tone Down the Brightness

When you first run the demo sketch, the NeoPixels all start in an “off” state. Tap the right button once to turn them all on, white at maximum brightness.

Immediately, you’ll notice something: this really hurts to look at. Please don’t stare directly at it.

It’s a novice impulse to want “MOAR!” of everything. But really, it’s okay, perfectly legal, to light some or all NeoPixels to less than full brightness. In fact, by default the Circuit Playground board runs them under 10 percent…anything brighter must be specifically unlocked (the “setBrightness(255)” call in the demo sketch).

NeoPixels use a technique called pulse width modulation (PWM) to control brightness and color. The LED elements inside are switched on and off very quickly (about 400 to 1000 times a second, depending on the generation of NeoPixel). The duty cycle — a measure of the “on” vs “off” time — determines the perceived brightness. Our brains smooth this out and see a continuous brightness level rather than all that flickering.

Duty cycle bears a direct relationship to power use. Run a NeoPixel at half the duty cycle, and it will use half the current.

Let me repeat that: Duty cycle bears a direct relationship to power use.

USELESS TRIVIA: even at their maximum setting (255), NeoPixels never quite have a 100% duty cycle…there’s always a tiny off “blip” in the cycle. A minimum setting (0) does correspond to a 0% duty cycle though. Also, you can never have exactly one half the maximum duty cycle. 255÷2 = 127.5, but NeoPixels expect an 8-bit integer. So there’s 127 (49.8% of longest duty cycle) or 128 (50.2%). These are tiny differences and perceptually will make no difference, but it’s fun to contemplate.

Strategy: Gamma Correction

I really wanted to save this as a grand finale…the power savings can be quite dramatic…but it’s a vital concept in subsequent examples.

From the “max brightness” mode, tap the right button twice to cycle through 50% and 0% duty cycle, then the left button twice to return through 50% and 100%. Go back and forth a few times.

You’ll notice something odd here. The 50% duty cycle state seems disproportionately bright. You can verify in the code though, could even hook up instruments to measure it. It really is a 50% duty cycle (or very close to it, per “useless trivia” above).

This means you can get most of the brightness from NeoPixels using only half the power. This is hugely important.

This is a real thing, a biological phenomenon of how our eyes and brains work. And we can exploit this to save a ton of power! In fact we’ve written an entire guide on just this one idea:

LED Tricks: Gamma Correction

Gamma correction is a process of compensating for this disparity between numerical brightness and perceived brightness. To make something appear half as bright, we use much less than half the duty cycle…in fact it’s about 20%. So it’s not just a 2:1 reduction in power use in this case, but a tremendous 5:1!

Following the 100/50/0% modes, tap back and forth through the next three modes. You’ll see the middle state now looks more “correct” — it seems closer to half-as-bright as the maximum.

Open the Serial Monitor window and look at the difference!

This adjustment (and some corresponding power savings) works on any brightness value in-between the minimum and maximum. There’s a PROGMEM table in the example sketch for this…look up an index from 0 to 255, it contains an adjusted value (also 0–255) to assign a pixel’s brightness:

out = pgm_read_byte(&gammaTable[in]);

For colors, you’d repeat this for red, green and blue (and white, if using RGBW NeoPixels).

Strategy: Color Selection

Look at a NeoPixel under a microscope and you’ll see something fascinating: tiny, separate LED elements for red, green and blue. Any color a NeoPixel shows is made by mixing these three primary colors using PWM. And if an element isn’t being used as part of the color mix, it’s not using any current.

Therefore: primary colors (red, green, blue) use about 1/3 the current of white. Secondary colors (cyan magenta, yellow) use about 2/3 the current. Favor these colors over white and you can reduce your battery needs considerably!

Tap the right button to get to this RGB pinwheel.

There’s color, there’s motion, and every single pixel is lit. But as it’s all primary colors, this steadily uses about 1/3 the amount of current of the all-white mode.

Because the colors are at maximum brightness, this mode doesn’t benefit from gamma correction, but we’ll return to that idea in a moment.

Tap again for this rainbow swirl, a staple of NeoPixel demos! Again every pixel is lit, this time a mix of primary and secondary colors and everything in-between.

An interesting artifact of the math behind this pattern is that the average duty cycle is close to 50%.

Tapping once more shows an almost, but not quite identical rainbow. Now it’s using gamma correction. But since only a fraction of the colors are “in-between” (most are 0 or 100%), this effect doesn’t benefit much. In the Serial Monitor you’ll see there’s some modest power savings, and the colors are just a tiny bit dimmer and more “true.”

Strategy: Light Fewer Pixels

Simple idea. Light the least amount of NeoPixels while still doing something interesting. The fewer the better.

Tap right again. This red spinner has half the NeoPixels off at any moment. And because it’s a primary color, that’s another 3-fold reduction vs. white. 1/2 × 1/3 = 1/6 (about 17%) of our initial “rule of thumb” current.

Tap again. This sparkle effect takes the least-pixels idea to its extreme, with only one NeoPixel lit at a time. This can be quite dazzling done in white, but any bright color will do…we’ll use blue here just to emphasize the primary color trick again, and because folks are still obsessed with blue LEDs. Take a look in the Serial Monitor and see how little this needs.

Combo Effects

Any or all of the above strategies can be used in combination…the energy-saving effects are cumulative.

Let’s begin with this green spinner (tap right, you know the drill)…

On average, half the NeoPixels are off (it varies from 4 to 6 per frame…but on average, half).

Green was chosen because it’s a primary color. So already, we’re combining two strategies. The current draw is about 1/6 the worst-case value. And we’re just getting started.

Tapping again switches to this sine wave spinner. Rather than on-or-off, the brightness of each pixel is determined by a moving sine wave. I really like effects like this, they give electronic projects a more “living” feel than the usual blippy animations. But I digress…

The sin() function is avoided because it’s slow. Instead, a precomputed PROGMEM array — sineTable[] — holds 256 values, varying from 0 to 255 in proportion through one cycle of the wave.

Interestingly, because the average value in the sine table is about one half of the maximum value, this effect uses roughly the same amount of current as the simple on/off green spinner above, despite the fact that more pixels are lit (though varying in brightness) at any given time. Averaging out all the duty cycles comes close to 50%. So we get a way cooler effect and are still at the same 1/6 current draw just as before.

The sine table lookup in the demo sketch works a bit like this:

uint8_t y = pgm_read_byte(&sineTable[x]);  // x = 0 to 255, y = 0 to 255

Tapping right again, a slight variation on the sine wave spinner. Now we’ve added gamma correction. Something like this:

uint8_t y = pgm_read_byte(&sineTable[x]);  // x = 0 to 255, y = 0 to 255
uint8_t z = pgm_read_byte(&gammaTable[y]); // z = 0 to 255 (gamma-corrected sine)

The sine wave is a bit dimmer but also more “defined” — the “ons“ and “offs” appear more balanced. It looks better and also the power consumption has dropped by about a third.

One more tap…the last mode refers back to one of the first strategies. Maybe we don’t need the sine wave peaks at full brightness. Maybe half the perceived brightness would suffice.

uint8_t y = pgm_read_byte(&sineTable[x]) / 2; // x = 0 to 255, y = 0 to 127
uint8_t z = pgm_read_byte(&gammaTable[y]);    // z = 0 to 39

We’ve still got that lovely organic sine wave effect, just less blinding now. The power consumption though…

Combining several strategies — using primary colors, limiting brightness, and applying gamma correction — this animation needs only about 7 milliamps over the base current (about 10 mA) used by the microcontroller, NeoPixels and other components on the board. 17 mA total.

17 milliamps. Down from an initial 340 mA with everything running full-blast. That’s a 20-fold reduction from the rule-of-thumb estimate.

Last updated on 2017-05-03 at 07.11.09 PM Published on 2017-05-03 at 02.45.38 PM