Assigning Colors to LEDs

So we can store and retrieve colors, and mix between them, but how to we actually get those colors to the LEDs?

FancyLED doesn’t speak any addressable LED protocols on its own. We import an existing library (like NeoPixel or DotStar), then assign colors (from FancyLED) to LEDs (using the corresponding LED library), usually in a loop of some sort.

As explained on the Install page, for NeoPixels we need to import the “board” and “neopixel” libraries:

import board
import neopixel

Then create a NeoPixel instance, passing the board pin number, number of LEDs and optionally auto_write=False so the LEDs only refresh when we say so:

strip = neopixel.NeoPixel(board.D1, 10, auto_write=False)

Or for the built-in NeoPixels on Circuit Playground Express:

from import cpx

The NeoPixel and DotStar libraries can both accept RGB packed integers as inputs, so we’ll use FancyLED’s pack() function to hand off the data.

Here’s a complete CircuitPython script for some FancyLED animation on Circuit Playground Express. It creates a color palette with 4 entries, then loops forever, moving data from the color palette to each NeoPixel, with a slight offset each time around to make the palette “spin”:

from import cpx
import adafruit_fancyled.adafruit_fancyled as fancy

cpx.pixels.auto_write = False  # Update only when we say
cpx.pixels.brightness = 0.25   # make less blinding

palette = [fancy.CRGB(255, 255, 255),  # White
           fancy.CRGB(255, 255, 0),    # Yellow
           fancy.CRGB(255, 0, 0),      # Red
           fancy.CRGB(0,0,0)]          # Black

offset = 0  # Position offset into palette to make it "spin"

while True:
    for i in range(10):
        color = fancy.palette_lookup(palette, offset + i / 9)
        cpx.pixels[i] = color.pack()

    offset += 0.033  # Bigger number = faster spin

Secret Ingredient: Gamma Correction

There’s an extra optional step we can perform if we want the LED colors to appear more “true” or if we’d like a global color balance adjustment…

Gamma correction is a process of adjusting brightness to better match the response curve of our eyes. Without it, mid-range levels appear unreasonably bright (50% brightness appears to our eyes more like 80%). We’ve written a whole guide if you need more details.

FancyLED provides the gamma_adjust() function to perform this operation. This can operate on a single CRGB or CHSV color (but not RGB packed integer) or a whole list of colors — the first argument to this function, which is required, is the color to adjust, or a list of colors.

An optional named argument — gamma_value — is the exponent that’s applied to the color(s)…either a single value (which is applied equally to red, green and blue), or a 3-element tuple of floats if you need separate exponents for red, green and blue. If nothing is specified, the default exponent is 2.7 which works reasonably well in most cases.

A second optional named argument — brightness — is a brightness adjustment that’s applied in the same operation. Like gamma_value, this can be a single value (0.0 to 1.0) that’s applied to red, green and blue, or a 3-element tuple of floats to adjust red, green and blue separately…pretty handy for adjusting the color balance on LEDs, which are seldom a neutral white. Default brightness is 1.0 (no dimming performed).

If converting a list of colors, one more optional named argument — inplace — decides whether a new gamma-adjusted color list is returned (keeping the original intact), or if the library may replace the contents of the list you passed in. By default this is False and a new list is returned.

Gamma adjustment should be the last step before colors are issued to the LEDs, and applied “on the way out.” You probably don’t want to keep the adjusted value around…repeated gamma adjustments over the same data will destroy your hard work!

Here’s how we might add gamma and brightness adjustments to the example program above. First, after this line…

offset = 0  # Position offset into palette to make it "spin"

Add a second line, consisting of a 3-element tuple of floats. These are brightness levels for red, green and blue, respectively. Aside from bringing the LEDs down to a less eye-searing brightness level, this also tries to balance the colors better so white doesn’t appear blue-tinged:

offset = 0  # Position offset into palette to make it "spin"
levels = (0.25, 0.3, 0.15)

Then, between these two lines in the pixel-setting loop:

color = fancy.palette_lookup(palette, offset + i / 9)
        cpx.pixels[i] = color.pack()

Insert a new line that processes “color” through the gamma_adjust() function, using our “levels” setting and putting the result back into the “color” variable:

color = fancy.palette_lookup(palette, offset + i / 9)
        color = fancy.gamma_adjust(color, brightness=levels)
        cpx.pixels[i] = color.pack()

Run the change and you’ll see the colors are a bit more mellow now.

Here are some different ways gamma_adjust() might be invoked, from the simple to the complex:

color = fancy.gamma_adjust(CRGB(0.6, 0.1, 0.9))

color1 = CHSV(0.5, 0.6, 0.3)
color2 = fancy.gamma_adjust(color1, gamma_value=2.7)

colorlist1 = [fancy.CRGB(128, 32, 117),
              fancy.CRGB(88, 85, 223),
              fancy.CRGB(0, 187, 30)]
levels = (0.25, 0.3, 0.15)
gammas = (2.6, 2.6, 2.7)
colorlist2 = fancy.gamma_adjust(colorlist1, gamma_value=gammas, brightness=levels)

Note that gamma_adjust() always returns a CRGB type (or list of CRGBs), even when the input(s) are CHSV. It’s an operation inherently based in RGB color space.

Whatever LED library you’re using — NeoPixel, DotStar or other — probably has its own brightness-setting function. Use one or the other, or the effect will compound and LEDs will be very dim. We recommend setting the NeoPixel/DotStar/other brightness to the maximum and handling this adjustment in FancyLED, since it provides color balance control and gamma correction.

Last updated on 2018-02-06 at 04.40.35 PM Published on 2018-02-01 at 12.49.42 AM