For boards like Feather RP2040 SCORPIO, use the adafruit_neopxl8
library to control the LEDs in CircuitPython, rather than the standard neopixel
library.
The library works much like the standard neopixel library, but it can control from 1 to 8 LED strands. Just import it, and then create an instance of the NeoPXL8 class:
import board from adafruit_neopxl8 import NeoPxl8 # Customize for your strands here num_strands = 8 strand_length = 30 first_led_pin = board.NEOPIXEL0 num_pixels = num_strands * strand_length pixels = NeoPxl8( first_led_pin, num_pixels, num_strands=num_strands, auto_write=False, brightness=0.50, )
"Background" writing
The adafruit_neopxl8
library is fast for two reasons. Not only does it send the data for up to 8 strands at once, it also sends the data "in the background", while your CircuitPython code can do things like check for button presses & update animations. With care in coding the rest of your CircuitPython program, you can get some really high update rates for your LED displays.
About pixel numbering
In CircuitPython's NeoPXL8, the pixels from all the strands are interleaved. In other words, say you have a 8 strands of 30 LEDs. The first 30 pixels are the pixels in the first strand; the next 30 pixels are the second pixel from each strand; and so on. Or, to put it another way, the pixels of the first strand are the numbers in range(0,30)
, the pixels of the second strand are range(30, 60)
, etc.
You can use the PixelMap feature of the LED Animation Library to build an object that lets you address each strand individually:
from adafruit_led_animation.helper import PixelMap def strand(n): return PixelMap( pixels, range(n * strand_length, (n + 1) * strand_length), individual_pixels=True, ) strands = [strand(i) for i in range(num_strands)]
Now, you can refer to strands[S][P]
to mean `strand #S, pixel #P`, or use `strand[S]` to refer to the whole strand—for instance, to call the fill
method or to use with an LED animation. Note that you want to call the strand function just once per strand and store the result. You can store them all in a list, like this example does; or you could give them names—for instance, if you you have 4 strands on the 4 sides of a window you might want to write top=strand(0)
and bottom=strand(1)
, for instance. Any of the other pixel mapping helpers from the LED Animation Library work equally well with NeoPXL8.
import rainbowio from adafruit_led_animation.animation.comet import Comet from adafruit_led_animation.group import AnimationGroup # For each strand, create a comet animation of a different color animations = [ Comet(strand, 0.02, rainbowio.colorwheel(3 * 32 * i), ring=True) for i, strand in enumerate(strands) ] # Group them so we can run them all at once animations = AnimationGroup(*animations) while True: animations.animate()
Here's the full example code. Use the 'download project bundle' link to get all the files you need to run it:
# SPDX-FileCopyrightText: 2022 Jeff Epler # # SPDX-License-Identifier: Unlicense import board import rainbowio import adafruit_ticks from adafruit_led_animation.animation.comet import Comet from adafruit_led_animation.group import AnimationGroup from adafruit_led_animation.helper import PixelMap from adafruit_neopxl8 import NeoPxl8 # Customize for your strands here num_strands = 8 strand_length = 30 first_led_pin = board.NEOPIXEL0 num_pixels = num_strands * strand_length # Make the object to control the pixels pixels = NeoPxl8( first_led_pin, num_pixels, num_strands=num_strands, auto_write=False, brightness=0.50, ) def strand(n): return PixelMap( pixels, range(n * strand_length, (n + 1) * strand_length), individual_pixels=True, ) # Create the 8 virtual strands strands = [strand(i) for i in range(num_strands)] # For each strand, create a comet animation of a different color animations = [ Comet(strand, 0.02, rainbowio.colorwheel(3 * 32 * i), ring=True) for i, strand in enumerate(strands) ] # Advance the animations by varying amounts so that they become staggered for i, animation in enumerate(animations): animation._tail_start = 30 * 5 * i // 8 # pylint: disable=protected-access # Group them so we can run them all at once animations = AnimationGroup(*animations) # Run the animations and report on the speed in frame per secodn t0 = adafruit_ticks.ticks_ms() frame_count = 0 while True: animations.animate() frame_count += 1 t1 = adafruit_ticks.ticks_ms() dt = adafruit_ticks.ticks_diff(t1, t0) if dt > 1000: print(f"{frame_count * 1000/dt:.1f}fps") t0 = t1 frame_count = 0
Text editor powered by tinymce.