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()
Text editor powered by tinymce.