There are more example animations on this page that you can look at in order to get ideas about how you can manipulate the LEDs from your custom Animation. Another really great place to look is the existing built-in Animations from the adafruit_led_animation library. The examples in this guide are commented thoroughly detailing the purpose of each line of code. If you'd like to try these on your own device simply click the 'Download Project Bundle' button at the top of the code files. It will download the custom Animation class, a code.py file that uses it, and all required libraries it needs to run.
Starting from existing animations
When you'd like to create your own custom Animation it's helpful if you can find an existing animation that is similar in some way to the animation you have in mind. If there is one with any similarities then you can copy it's code to start yours from instead of needing to start with an absolute "blank canvas". For instance, if you know that you want a rainbow animation you can re-use the internal color wheel generator from one of the built-in animations.
Sweep Animation
The SweepAnimation will sweep across the strand, turning 1 pixel on with each animation frame. Once all of the pixels are on it will then sweep across again the same direction turning them back off. It uses custom variables to keep track of whether it's currently sweeping on or off, as well as the current index within the strand.
# SPDX-FileCopyrightText: 2024 Tim Cocks
#
# SPDX-License-Identifier: MIT
"""
SweepAnimation helper class
"""
from adafruit_led_animation.animation import Animation
class SweepAnimation(Animation):
def __init__(self, pixel_object, speed, color):
"""
Sweeps across the strand lighting up one pixel at a time.
Once the full strand is lit, sweeps across again turning off
each pixel one at a time.
:param pixel_object: The initialized pixel object
:param speed: The speed to run the animation
:param color: The color the pixels will be lit up.
"""
# Call super class initialization
super().__init__(pixel_object, speed, color)
# custom variable to store the current step of the animation
self.current_step = 0
# one step per pixel
self.last_step = len(pixel_object)
# boolean indicating whether we're currently sweeping LEDs on or off
self.sweeping_on = True
self.cycle_complete = False
# This animation supports the cycle complete callback
on_cycle_complete_supported = True
def draw(self):
"""
Display the current frame of the animation
:return: None
"""
if self.sweeping_on:
# Turn on the next LED
self.pixel_object[self.current_step] = self.color
else: # sweeping off
# Turn off the next LED
self.pixel_object[self.current_step] = 0x000000
# increment the current step variable
self.current_step += 1
# if we've reached the last step
if self.current_step >= self.last_step:
# if we are currently sweeping off
if not self.sweeping_on:
# signal that the cycle is complete
self.cycle_complete = True
# reset the step variable to 0
self.current_step = 0
# flop sweeping on/off indicator variable
self.sweeping_on = not self.sweeping_on
Zipper Animation
The ZipperAnimation will start lighting up every other LED from both ends of the strand, passing each other in the middle and resulting in the full strand being lit at the end of the cycle. It uses custom variables to keep track of the current step within the animation. The reset() is used to turn off the LEDs and reset the variables back to their initial values.
# SPDX-FileCopyrightText: 2024 Tim Cocks
#
# SPDX-License-Identifier: MIT
"""
ZipperAnimation helper class
"""
from adafruit_led_animation.animation import Animation
class ZipperAnimation(Animation):
def __init__(self, pixel_object, speed, color, alternate_color=None):
"""
Lights up every other LED from each ends of the strand, passing each
other in the middle and resulting in the full strand being lit at the
end of the cycle.
:param pixel_object: The initialized pixel object
:param speed: The speed to run the animation
:param color: The color the pixels will be lit up.
"""
# Call super class initialization
super().__init__(pixel_object, speed, color)
# if alternate color is None then use single color
if alternate_color is None:
self.alternate_color = color
else:
self.alternate_color = alternate_color
# custom variable to store the current step of the animation
self.current_step = 0
# We're lighting up every other LED, so we have half the strand
# length in steps.
self.last_step = len(pixel_object) // 2
self.cycle_complete = False
# This animation supports the cycle complete callback
on_cycle_complete_supported = True
def draw(self):
"""
Display the current frame of the animation
:return: None
"""
# Use try/except to ignore indexes outside the strand
try:
# Turn on 1 even indexed pixel starting from the start of the strand
self.pixel_object[self.current_step * 2] = self.color
# Turn on 1 odd indexed pixel starting from the end of the strand
self.pixel_object[-(self.current_step * 2) - 1] = self.alternate_color
except IndexError:
pass
# increment the current step variable
self.current_step += 1
# if we've reached the last step
if self.current_step > self.last_step:
# signal that the cycle is complete
self.cycle_complete = True
# call internal reset() function
self.reset()
def reset(self):
"""
Turns all the LEDs off and resets the current step variable to 0
:return: None
"""
# turn LEDs off
self.pixel_object.fill(0x000000)
# reset current step variable
self.current_step = 0
RainbowSweep Animation
The RainbowSweepAnimation shimmers the whole strand with a rainbow and then sweeps across it with another specified color. You can use BLACK or 0x000000 to turn LEDs off during the sweep. This animation uses a generator function internally to iterate through colors of the rainbow with help some colorwheel helpers. Many of the built-in Rainbow animation use this technique.
# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
Adapted From `adafruit_led_animation.animation.rainbow`
"""
from adafruit_led_animation.animation import Animation
from adafruit_led_animation.color import colorwheel
from adafruit_led_animation import MS_PER_SECOND, monotonic_ms
class RainbowSweepAnimation(Animation):
"""
The classic rainbow color wheel that gets swept across by another specified color.
:param pixel_object: The initialised LED object.
:param float speed: Animation refresh rate in seconds, e.g. ``0.1``.
:param float sweep_speed: How long in seconds to wait between sweep steps
:param float period: Period to cycle the rainbow over in seconds. Default 1.
:param sweep_direction: which way to sweep across the rainbow. Must be one of
DIRECTION_START_TO_END or DIRECTION_END_TO_START
:param str name: Name of animation (optional, useful for sequences and debugging).
"""
# constants to represent the different directions
DIRECTION_START_TO_END = 0
DIRECTION_END_TO_START = 1
# pylint: disable=too-many-arguments
def __init__(
self, pixel_object, speed, color, sweep_speed=0.3, period=1,
name=None, sweep_direction=DIRECTION_START_TO_END
):
super().__init__(pixel_object, speed, color, name=name)
self._period = period
# internal var step used inside of color generator
self._step = 256 // len(pixel_object)
# internal var wheel_index used inside of color generator
self._wheel_index = 0
# instance of the generator
self._generator = self._color_wheel_generator()
# convert swap speed from seconds to ms and store it
self._sweep_speed = sweep_speed * 1000
# set the initial sweep index
self.sweep_index = len(pixel_object)
# internal variable to store the timestamp of when a sweep step occurs
self._last_sweep_time = 0
# store the direction argument
self.direction = sweep_direction
# this animation supports on cycle complete callbacks
on_cycle_complete_supported = True
def _color_wheel_generator(self):
# convert period to ms
period = int(self._period * MS_PER_SECOND)
# how many pixels in the strand
num_pixels = len(self.pixel_object)
# current timestamp
last_update = monotonic_ms()
cycle_position = 0
last_pos = 0
while True:
cycle_completed = False
# time vars
now = monotonic_ms()
time_since_last_draw = now - last_update
last_update = now
# cycle position vars
pos = cycle_position = (cycle_position + time_since_last_draw) % period
# if it's time to signal cycle complete
if pos < last_pos:
cycle_completed = True
# update position var for next iteration
last_pos = pos
# calculate wheel_index
wheel_index = int((pos / period) * 256)
# set all pixels to their color based on the wheel color and step
self.pixel_object[:] = [
colorwheel(((i * self._step) + wheel_index) % 255) for i in range(num_pixels)
]
# if it's time for a sweep step
if self._last_sweep_time + self._sweep_speed <= now:
# udpate sweep timestamp
self._last_sweep_time = now
# decrement the sweep index
self.sweep_index -= 1
# if it's finished the last step
if self.sweep_index == -1:
# reset it to the number of pixels in the strand
self.sweep_index = len(self.pixel_object)
# if end to start direction
if self.direction == self.DIRECTION_END_TO_START:
# set the current pixels at the end of the strand to the specified color
self.pixel_object[self.sweep_index:] = (
[self.color] * (len(self.pixel_object) - self.sweep_index))
# if start to end direction
elif self.direction == self.DIRECTION_START_TO_END:
# set the pixels at the begining of the strand to the specified color
inverse_index = len(self.pixel_object) - self.sweep_index
self.pixel_object[:inverse_index] = [self.color] * (inverse_index)
# update the wheel index
self._wheel_index = wheel_index
# signal cycle complete if it's time
if cycle_completed:
self.cycle_complete = True
yield
def draw(self):
"""
draw the current frame of the animation
:return:
"""
next(self._generator)
def reset(self):
"""
Resets the animation.
"""
self._generator = self._color_wheel_generator()
All in Sequence
The following code.py file will play all 3 animations one after another in an AnimationSequence. Click the download project button to try it out. Remember to update the pin referenced by the code to match the pin your Neopixels are connected to.Â
# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import board
import neopixel
from adafruit_led_animation.color import PINK, JADE
from adafruit_led_animation.sequence import AnimationSequence
from rainbowsweep import RainbowSweepAnimation
from sweep import SweepAnimation
from zipper import ZipperAnimation
# Update to match the pin connected to your NeoPixels
pixel_pin = board.A1
# Update to match the number of NeoPixels you have connected
pixel_num = 30
# initialize the neopixels. Change out for dotstars if needed.
pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.02, auto_write=False)
# initialize the animations
sweep = SweepAnimation(pixels, speed=0.05, color=PINK)
zipper = ZipperAnimation(pixels, speed=0.1, color=PINK, alternate_color=JADE)
rainbowsweep = RainbowSweepAnimation(pixels, speed=0.05, color=0x000000, sweep_speed=0.1,
sweep_direction=RainbowSweepAnimation.DIRECTION_END_TO_START)
# sequence to play them all one after another
animations = AnimationSequence(
sweep, zipper, rainbowsweep, advance_interval=6, auto_clear=True
)
while True:
animations.animate()
Page last edited January 22, 2025
Text editor powered by tinymce.