adafruit / adafruit_circuitpython_led_animation Goto Github PK
View Code? Open in Web Editor NEWCircuitPython helper library for LED colors and animations
License: MIT License
CircuitPython helper library for LED colors and animations
License: MIT License
Rainbow and other animations with a period property don't let you change the cycle period after the animation is created.
A user commented on this: https://forums.adafruit.com/viewtopic.php?f=60&t=186732
This (simplified) example, after the first round, does the red and blue animations in random order. Each Pulse
animation is correct, but, successive Pulse
s do not strictly alternate between red and blue. Note the AnimateOnce
inside AnimationSequence
.
import board
import neopixel
from adafruit_led_animation.animation.pulse import Pulse
from adafruit_led_animation.sequence import AnimationSequence
from adafruit_led_animation.sequence import AnimateOnce
from adafruit_led_animation.color import RED, BLUE
num_pixels = 24
pixels = neopixel.NeoPixel(board.A0, num_pixels, auto_write=False)
pixels.brightness = 0.2
pulse0 = Pulse(pixels, speed=.05, color=RED, period=1)
pulse1 = Pulse(pixels, speed=.05, color=BLUE, period=1)
animations = AnimationSequence(
AnimateOnce(pulse0,pulse1,),
auto_clear=True,
auto_reset=True,
advance_interval=5,
)
while True:
animations.animate()
Removing the nesting works fine. Red and blue alternate, as expected.
import board
import neopixel
from adafruit_led_animation.animation.pulse import Pulse
from adafruit_led_animation.sequence import AnimateOnce
from adafruit_led_animation.color import RED, BLUE
num_pixels = 24
pixels = neopixel.NeoPixel(board.A0, num_pixels, auto_write=False)
pixels.brightness = 0.2
pulse0 = Pulse(pixels, speed=.05, color=RED, period=1)
pulse1 = Pulse(pixels, speed=.05, color=BLUE, period=1)
animations = AnimateOnce(pulse0,pulse1,
auto_clear=True,
auto_reset=True,
advance_interval=5,
)
while True:
animations.animate()
There are a number of cases that assume RGB tuples.
Requires adafruit/circuitpython#2953 to be fixed.
It would be useful to add a concept of “neighboring” pixels when you’ve established a logical re-mapping of the physical pixels. For example, if you have a typical RGB gamer keyboard, there’s a bunch of lighting modes. One is often a radiating color explosion that starts with a keypress. If you press H on the keyboard it’ll change color and then a radiating color wave will move next to T Y U G J B N M and so on.
Python 3.7.3
Library version 2.1.1+
PixelSubset numbering currently requires a value one higher than the last pixel you want in a slice. This is how it currently works:
pixel_ringA = PixelSubset(pixels, 0, 24) # 24 pixel ring
pixel_stripA = PixelSubset(pixels, 24, 40) # 16 pixel strip
pixel_stripB = PixelSubset(pixels, 40, 56) # 16 pixel strip
I would expect it to work like this:
pixel_ringA = PixelSubset(pixels, 0, 23) # 24 pixel ring
pixel_stripA = PixelSubset(pixels, 24, 39) # 16 pixel strip
pixel_stripB = PixelSubset(pixels, 40, 55) # 16 pixel strip
I'm trying to import the Pulse animation module, but getting error. Other animation modules work.
I'm using circuitpython 8.2.9 on a raspi pico, and on v2.8.0 of the animation lib.
I'm using the CircuitPython VSCode extension, this is my config:
{
"python.languageServer": "Pylance",
"python.analysis.diagnosticSeverityOverrides": {
"reportMissingModuleSource": "none",
"reportShadowedImports": "none"
},
"python.analysis.extraPaths": [
"/Users/sam/.vscode/extensions/joedevivo.vscode-circuitpython-0.2.0-darwin-arm64/boards/0x239A/0x80F4",
"/Users/sam/.vscode/extensions/joedevivo.vscode-circuitpython-0.2.0-darwin-arm64/stubs",
"/Users/sam/Library/Application Support/Code/User/globalStorage/joedevivo.vscode-circuitpython/bundle/20240130/adafruit-circuitpython-bundle-py-20240130/lib"
],
"circuitpython.board.version": "8.2.9",
"circuitpython.board.vid": "0x239A",
"circuitpython.board.pid": "0x80F4"
}
When I try to import pulse.py
manually, I get an error at this line:
I tried fiddling with it, doing top level import, removing it, but every time something else breaks...
Several colors are used in examples, however the full list is not available anywhere other than in the code.
It would be great if these could be documented so users know what is available to use.
Hardware: Feather M4 Express ATSAMD51, 74AHCT125 Level-Shifter,168 NeoPixels [zig-zag configuration],
Software: adafruit-circuitpython-feather_m4_express-en_US-5.3.0, adafruit-circuitpython-bundle-5.x-mpy-20200527
Animation types that work with PixelMap range of pixels:
Solid, Blink, ColorCycle, Pulse,
Animation types that display as a single color at a time - for the whole PixelMap range of pixels:
Rainbow, Chase, RainbowChase, RainbowSparkle
Animation types that error out with PixelMap range of pixels:
Comet, RainbowComet (ValueError: Slice and input sequence size do not match.)
Sparkle, SparklePulse (ValueError: Sparkle needs at least 2 pixels)
Test Code:
import board
import neopixel
from adafruit_led_animation.sequence import AnimationSequence
from adafruit_led_animation.helper import PixelMap
from adafruit_led_animation.animation.solid import Solid
from adafruit_led_animation.animation.colorcycle import ColorCycle
from adafruit_led_animation.animation.blink import Blink
from adafruit_led_animation.animation.comet import Comet
from adafruit_led_animation.animation.chase import Chase
from adafruit_led_animation.animation.pulse import Pulse
from adafruit_led_animation.animation.rainbow import Rainbow
from adafruit_led_animation.animation.rainbowchase import RainbowChase
from adafruit_led_animation.animation.rainbowcomet import RainbowComet
from adafruit_led_animation.animation.rainbowsparkle import RainbowSparkle
from adafruit_led_animation.animation.sparkle import Sparkle
from adafruit_led_animation.animation.sparklepulse import SparklePulse
from adafruit_led_animation.color import PURPLE, AMBER, TEAL, PINK, MAGENTA
pixels = neopixel.NeoPixel(board.D6, 168, brightness = .1, auto_write=False)
section_a = PixelMap(pixels, [(0, 56)])
section_b = PixelMap(pixels, [(56, 112)])
section_c = PixelMap(pixels, [(112, 168)])
solid = Solid(section_a, color=PINK)
blink = Blink(section_b, speed=0.5, color=TEAL)
colorCycle = ColorCycle(section_c, speed=0.4, colors=[MAGENTA, PURPLE, TEAL])
pulse = Pulse(section_a, speed=0.1, color=TEAL, period=3)
rainbow = Rainbow(section_b, speed=0.1, period=1)
chase = Chase(section_c, speed=0.1, color=PURPLE, size=3, spacing=6)
rainbowChase = RainbowChase(section_a, speed=0.1, size=5, spacing=3)
rainbowSparkle = RainbowSparkle(section_b, speed=0.1, num_sparkles=15)
# comet = Comet(section_c, speed=0.01, color=PURPLE, tail_length=10, bounce=True)
# rainbowComet = RainbowComet(section_a, speed=0.01, tail_length=7, bounce=True)
# sparkle = Sparkle(section_b, speed=0.05, color=AMBER, num_sparkles=10)
# sparklePulse = SparklePulse(section_c, speed=0.001, period=3, color=AMBER)
animations = AnimationSequence(solid, blink, colorCycle, pulse, rainbow, chase, rainbowChase, rainbowSparkle,
# comet, rainbowComet, sparkle, sparklePulse,
advance_interval=3, auto_clear=True)
while True:
animations.animate()
it seems only animations derived from ColorCycle currently support the callback used by sequence when they complete. I added this code
`def onComplete(sequence):
print("completed sequence", animations.current_animation, sequence)
animations.add_cycle_complete_receiver(onComplete)
`
to the all-animations example, and it is invoked only when the CustomColorChase ends its cycle. outputting:
"completed sequence <CustomColorChase: None> <AnimationSequence: None>"
Like the ability to use Custom Colors with "chase" animation
There are missing type annotations for some functions in this library.
The typing
module does not exist on CircuitPython devices so the import needs to be wrapped in try/except to catch the error for missing import. There is an example of how that is done here:
try:
from typing import List, Tuple
except ImportError:
pass
Once imported the typing annotations for the argument type(s), and return type(s) can be added to the function signature. Here is an example of a function that has had this done already:
def wrap_text_to_pixels(
string: str, max_width: int, font=None, indent0: str = "", indent1: str = ""
) -> List[str]:
If you are new to Git or Github we have a guide about contributing to our projects here: https://learn.adafruit.com/contribute-to-circuitpython-with-git-and-github
There is also a guide that covers our CI utilities and how to run them locally to ensure they will pass in Github Actions here: https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/check-your-code In particular the pages: Sharing docs on ReadTheDocs
and Check your code with pre-commit
contain the tools to install and commands to run locally to run the checks.
If you are attempting to resolve this issue and need help, you can post a comment on this issue and tag both @FoamyGuy and @kattni or reach out to us on Discord: https://adafru.it/discord in the #circuitpython-dev
channel.
The following locations are reported by mypy to be missing type annotations:
Any idea how difficult it would be to make it easier to update the brightness of the animation once it has been created? I'm using a RainbowComet
animation with a PixelSubset
. While that's running I also have a customized Pulse
animation running on another subset of pixels. As pulse.draw()
is called I keep track of the current brightness level so that my other LEDs can change as well.
I'd like the brightness of the RainbowComet
animation to be in sync with that of my customized Pulse
animation. To do that, I created a slightly customized RainbowComet
class that overrides the draw
method to use the current brightness:
def draw(self):
"""
Same exact implementation as the base class except adding in current brightness
"""
# super().draw()
colors = self._comet_colors
if self.reverse:
colors = reversed(colors)
for pixel_no, color in enumerate(colors):
# The next three lines were added to add in brightness
if len(color) == 3:
color = list(color)
color.append(self.displayer.current_brightness / 100)
draw_at = self._tail_start + pixel_no
if draw_at < 0 or draw_at >= self._num_pixels:
if not self._ring:
continue
draw_at = draw_at % self._num_pixels
self.pixel_object[draw_at] = color
self._tail_start += self._direction
if self._tail_start < self._left_side or self._tail_start >= self._right_side:
if self.bounce:
self.reverse = not self.reverse
self._direction = -self._direction
elif self._ring:
self._tail_start = self._tail_start % self._num_pixels
else:
self.reset()
if self.reverse == self._initial_reverse and self.draw_count > 0:
self.cycle_complete = True
I tried animation._pixel_object.brightness
but that ended poorly.
I'd love a gradient animation. I want to be able to select my colors (maybe 3-4, or maybe more for a whole palette). The colors should fade into each other seamlessly, and I want to be able to choose the period / number of LEDs per section. Then I'd want the whole thing to scroll along the strip and be able to choose the speed.
Gradient (pixels, speed=0.1, colors=(color.RED, color.BLUE, (0,200,10), 0XFF0000), period=5)
It would be nice if the animation library supported RGBW LEDs
I noticed the following issues with Sparkle animations:
Simple SparklePulse example that will only light up 4 pixels, not 5:
import board
import neopixel
from adafruit_led_animation.animation.sparklepulse import SparklePulse
from adafruit_led_animation.color import RED
pixels = neopixel.NeoPixel(board.D12, 5, brightness=0.5, auto_write=False)
animation = SparklePulse(pixels, speed=0.1, period=3, color=RED)
while True:
animation.animate()
The same issue does not affect the regular Pulse animation.
Simple Sparkle example that doesn't sparkle the first or last pixel:
import board
import neopixel
from adafruit_led_animation.animation.sparkle import Sparkle
from adafruit_led_animation.color import RED
pixels = neopixel.NeoPixel(board.D12, 5, brightness=0.5, auto_write=False)
animation = Sparkle(pixels, speed=0.2, color=RED, num_sparkles=10)
while True:
animation.animate()
I tried to fix this one myself but wasn't able to narrow down the cause of the issue.
After upgrading to the latest Circuit Python Bundle (circuitpython-bundle-5.x-mpy-20200621), I'm getting the following error with the adafruit_led_animation library on the Feather nRF52840 Express:
File "code.py", line 7, in TypeError: 'module' object is not callable
Here is a sample program to reproduce the error:
from adafruit_led_animation.animation import Pulse, Solid
import adafruit_led_animation.color as color
from neopixel import NeoPixel
from time import sleep
from board import NEOPIXEL
pixel = NeoPixel(NEOPIXEL, 1)
solid = Solid(pixel, color.GREEN) # THIS LINE THROWS THE ERROR
solid.animate()
sleep(4)
The code works with circuitpython-bundle-5.x-mpy-20200516
The code stops working with circuitpython-bundle-5.x-mpy-20200518
I'm running CircuitPython 5.4.0-beta.1-43-g1504d9005 on 2020-06-22; on Feather nRF52840 Express.
I noticed that some effect do not look "perfect" when your row of RGB LED make a loop or is circular, like this product: https://www.adafruit.com/product/1586
When you get to the last pixel, the next pixel is the first one.
With current library, if you run an effect multiple time, the effect will end on last pixel and a new effect will start on the first pixel. This break the "motion". For Meteor, this mean a new meteor will be launched, rather than to have one meteor that start and rotate again and again.
I believe some effects might benefit from beeing continuous from one run to the other.
Ahoi! I'm just starting out with circuitpython and have to say thank you for those amazing libraries.
Easy to use and setup and looks great.
Now I'm hitting a wall. I want to change the period of a Rainbow on the fly. The property _period isn't the way to go.
speed
could be used but this isn't what I want as I want to speed up the color transitions.
I've found a way to use the cycle complete receiver callback logic I.e. I create a new rainbow with the new period on a complete cycle.
But this leads to quicker period changes for quicker cycles...erm.
OK, what is my goal here: using the feather nRF52840 express, I create a webserver on a raspi4 which conencts via bluetooth to the feather and provides a WebUI to control brightness and speed of my WS2811 running the rainbow animation.
The webserver will receive POST requests and translate them into some UART packets.
This works fine for brightness as I can directly access pixels.brightness
mid-animation but I see no way to change the period.
What am I missing? How should I go about it?
group.py line 155 of method animate()
return any(item.animate(show) for item in self._members)
consider replacing the "any()" with "all()" so it doesn't short circuit.
If the speed of animations is fast (0.01 for example), then the first animation in an animation group succeeds, returns true, and shortcuts without executing the remaining item.animate() commands. Thus, none of the other animations ever run. Slowing down the speed allows for more animations to run because the first few animations may skip if they haven't reached their self._next_update. So it seems sporadic, but is actually just based on timing.
#100 submitted to fix
This repository still uses master
instead of main
There's lots of room for performance fixes. For example, enumerate()
is much faster than range()
is much faster than a while
loop.
Chase, and Comet, default to a (0, 0, 0) background color. It would be nice to be able to specify a background color or fade-to color, besides the default (0, 0, 0) color. This would let the user differentiate their display giving it a less generic look. And with Grouped (layered) animations, when the top animation overides the bottom animation, without using (0, 0, 0) colored pixels, it might be more pleasing look.
you may need to use the universal use time.monotonic() instead
https://forums.adafruit.com/viewtopic.php?f=52&t=168249&p=823612#p823612
The rainbow comet will be a single red color comet when tail_length is > 256 and step=0. I believe this may be caused by line 63 in the source:
if step == 0: self._colorwheel_step = int(256 / tail_length)
consider tail_length = > 256 and that int() statement can yield 0. When that happens the RainbowComet will be all solid red
perhaps a simple fix is:
if step == 0: self._colorwheel_step = max(int(256 / tail_length), 1)
Maybe someone fill it with some content - maybe me? We'll see.
Recent issues like #108, which seem to be the result of API changes in a base class, might be mitigated if the less-used parameters for animation classes like Comet
were declared as keyword-only.
Travis can't run repo this because the pylint command is wrong.
AnimationGroup is powerful - but it needs more examples to help people understand how and when to use it, and how/when to use the sync=True
setting.
Pretty new at this - please be gentle if I have this wrong. When I run the Comet animation (using MagTag from ADABOX017), and set the Reverse option, the LED's seem to stay dark:
Comet(strip, strip_comet_speed, comet_one_color, tail_length=strip_comet_tail, reverse=True)
The draw() function is where the action is:
def draw(self):
colors = self._comet_colors
if self.reverse:
colors = reversed(colors)
for pixel_no, color in enumerate(colors):
draw_at = self._tail_start + pixel_no
print(draw_at, self._tail_start, pixel_no)
if draw_at < 0 or draw_at >= self._num_pixels:
if not self._ring:
continue
draw_at = draw_at % self._num_pixels
self.pixel_object[draw_at] = color
self._tail_start += self._direction
print("tail_start", self._tail_start)
if self._tail_start < self._left_side or self._tail_start >= self._right_side:
if self.bounce:
self.reverse = not self.reverse
self._direction = -self._direction
elif self._ring:
self._tail_start = self._tail_start % self._num_pixels
else:
self.reset()
if self.reverse == self._initial_reverse and self.draw_count > 0:
self.cycle_complete = True
Bounce works using reverse. Reverse (alone) does not. Debug print statements show normal numbers for Comet with no options. With reverse set, _tail_start is being set to 45 (30 LED's plus 15 pixel 'tail'), decremented to 44 by <self._tail_start += self._direction> and reset to 45 elsewhere before draw() is called again. It appears the last IF statement is the cuplrit. With no support for simple 'reverse' in the root IF block (last ten lines), the last IF resets the animation. If reverse is simply there to be a flag for bounce, it should be removed from the public settable parameters. If it's meant to function on it's own, this root IF block needs fixing to support it.
We're lacking a simple example here or in a Learn Guide of cycling through animatiosn or interrupting animations with button pushes. Sometimes people think they need threads or interrupts to do this, but it would be good to show simple ways to accomplish that.
Just following the README.md and I get the error:
ImportError: cannot import name 'Blink' from 'adafruit_led_animation.animation'
sparkle = Sparkle(pixels, speed=0.05, color=AMBER, num_sparkles=10)
This code should look like this (code and video taken from the adafruit guide):
But it does actually look like this:
However, the rainbowsparkle.mpy animation does indeed work (with the same code):
Tested with CircuitPython 8 on a PICO RP2040
Is this animation currently broken?
Rainbow initializes self.colors = None
_color_wheel_generator
then tries to get the length
When using precompute_rainbow=False
this will cause an error:
TypeError: object of type 'NoneType' has no len()
Initializing self.colors = []
should still work in the truthy test or moving the wheel_index
calculation into the if self.colors
block could fix it too.
Better examples or a tutorial or guide would help for more advanced animation usages. Common questions include how to manually advance (eg from buttons), how to adjust speed, and how to pause animations.
There is a bug in Comet and RainbowComet where the comet length is short by 1 because of the black pixel in the color list.
The comet also cannot be longer than the strip.
Hi, is there a way to address multiple 8x8 matrixes with this into a framebuffer for scrolling text?
I can talk to all just fine, but I am trying to use the library to draw scrolling text. I have 6 total panels (all work fine).
Layout is like this: 0-63 in an 8x8, 0-7 first row, then it continues and they're all in the same orientation.
Thanks
Recently changes were made in how anim.draw_count is updated in animation/init.py . Near line 102/105 anim.draw_count is incremented. Thus it is incremented 2 times.
The issue is that in chase.py at line 119 a check is done on draw_count . This check does a % with "num of pixels" . This test is never true now that draw_count is incremented twice and thus cycle_complete is never set.
Solutions are to remove the anim.draw_count increment at line 105 in animation/init.py or modify chase.py to handle this change of draw_count .
I can provide PR for either way, which would you prefer or something else. Both comet and sparkle work with the suggested change to animation/init.py .
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.