Black Lives Matter - Action and Equality. ... Adafruit is open and shipping.
0

Multiple Animations on a single Adafruit'sb NeoPixel strip
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Multiple Animations on a single Adafruit'sb NeoPixel strip

by prudhvychoudary15 on Tue Apr 07, 2020 6:03 am

For a school project, I am trying to show multiple animations at the same time on a single strip of Adafruit's Neopixels strip with the help of one of your code libraries.


My setup;

RPi zero + 5v power supply for strip + data pin on GPIO 18 + Adafruit_NeoPixel (strandtest.py) library from Github + Python3

Problem:

I am successful to get all the animations and light up the entire strip. But, I would like to play multiple animations at the same time by separating the strip into different parts in the code by setting the range.
I am pretty new to Pi and python so, I am unable to figure out where exactly I can give the range to divide the strip. I could find some kind of resources to achieve this using Arduino but I must do it with Pi.

I found an image online that I exactly looking to achieve. That explains my question better.

I really appreciate your work.
Thanks and greeting from Berlin.
Attachments
18031310432823652415610793.png
18031310432823652415610793.png (206.07 KiB) Viewed 71 times

prudhvychoudary15
 
Posts: 5
Joined: Tue Apr 07, 2020 5:58 am

Re: Multiple Animations on a single Adafruit'sb NeoPixel str

by kattni on Tue Apr 07, 2020 2:42 pm

You'll want to use slicing. Here are a couple of examples that use slicing with LEDs:

https://learn.adafruit.com/circuitpytho ... -code-13-7

https://learn.adafruit.com/circuitpytho ... half-10-14

https://learn.adafruit.com/burning-fire ... -files-3-4

These examples should get you going. You'll want to identify slices, and then apply each animation to a different slice.

The second example is followed by a brief explanation of using slicing.

The third example is a lot more complicated but has more advanced usage of slices.

kattni
 
Posts: 72
Joined: Fri Aug 18, 2017 6:33 pm

Re: Multiple Animations on a single Adafruit'sb NeoPixel str

by prudhvychoudary15 on Tue Apr 14, 2020 10:10 am

Hello Kattni,
Thanks for your informative response. Now i have understood how the slice function works.But as a beginner in python, I can not figure out where can i indicate in my code to separate the strip in to three parts. And to apply my animations individually at the same time.this is the code i trying to work with.
Sorry i feel stupid to ask you to write me my code. but i have been trying to make it work almost 2 months for now without any success.

Thanks once again.

Code: Select all | TOGGLE FULL SIZE
import time
from neopixel import *
import argparse

# LED strip configuration:
LED_COUNT      = 50      # Number of LED pixels.
LED_PIN        = 18      # GPIO pin connected to the pixels (18 uses PWM!).
#LED_PIN        = 10      # GPIO pin connected to the pixels (10 uses SPI /dev/spidev0.0).
LED_FREQ_HZ    = 800000  # LED signal frequency in hertz (usually 800khz)
LED_DMA        = 10      # DMA channel to use for generating signal (try 10)
LED_BRIGHTNESS = 255     # Set to 0 for darkest and 255 for brightest
LED_INVERT     = False   # True to invert the signal (when using NPN transistor level shift)
LED_CHANNEL    = 0       # set to '1' for GPIOs 13, 19, 41, 45 or 53

# Define functions which animate LEDs in various ways.
def colorWipe(strip, color, wait_ms=50):
    """Wipe color across display a pixel at a time."""
    for i in range(strip.numPixels()):
        strip.setPixelColor(i, color)
        strip.show()
        time.sleep(wait_ms/1000.0)
def theaterChase(strip, color, wait_ms=50, iterations=10):
    """Movie theater light style chaser animation."""
    for j in range(iterations):
        for q in range(3):
            for i in range(0, strip.numPixels(), 3):
                strip.setPixelColor(i+q, color)
            strip.show()
            time.sleep(wait_ms/1000.0)
            for i in range(0, strip.numPixels(), 3):
                strip.setPixelColor(i+q, 0)

def wheel(pos):
    """Generate rainbow colors across 0-255 positions."""
    if pos < 85:
        return Color(pos * 3, 255 - pos * 3, 0)
    elif pos < 170:
        pos -= 85
        return Color(255 - pos * 3, 0, pos * 3)
    else:
        pos -= 170
        return Color(0, pos * 3, 255 - pos * 3)

def rainbow(strip, wait_ms=20, iterations=1):
    """Draw rainbow that fades across all pixels at once."""
    for j in range(256*iterations):
        for i in range(strip.numPixels()):
            strip.setPixelColor(i, wheel((i+j) & 255))
        strip.show()
        time.sleep(wait_ms/1000.0)

def rainbowCycle(strip, wait_ms=20, iterations=5):
    """Draw rainbow that uniformly distributes itself across all pixels."""
    for j in range(256*iterations):
        for i in range(strip.numPixels()):
            strip.setPixelColor(i, wheel((int(i * 256 / strip.numPixels()) + j) & 255))
        strip.show()
        time.sleep(wait_ms/1000.0)

def theaterChaseRainbow(strip, wait_ms=50):
    """Rainbow movie theater light style chaser animation."""
    for j in range(256):
        for q in range(3):
            for i in range(0, strip.numPixels(), 3):
                strip.setPixelColor(i+q, wheel((i+j) % 255))
            strip.show()
            time.sleep(wait_ms/1000.0)
            for i in range(0, strip.numPixels(), 3):
                strip.setPixelColor(i+q, 0)

# Main program logic follows:
if __name__ == '__main__':
    # Process arguments
    parser = argparse.ArgumentParser()
    parser.add_argument('-c', '--clear', action='store_true', help='clear the display on exit')
    args = parser.parse_args()

    # Create NeoPixel object with appropriate configuration.
    strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL)
    # Intialize the library (must be called once before other functions).
    strip.begin()

 
  def off (strip):
    for j in range(256):
        for i in range(strip.numPixels()):
            strip.setPixelColor(i, Color(0,0,0))
        strip.show()
        time.sleep(.001)#off for 1 sec.Change this number to change the
                        #duration.Remove the number to completely off the leds
    print ('Press Ctrl-C to quit.')
    if not args.clear:
        print('Use "-c" argument to clear LEDs on exit')

   try:

        while True:       
            print ('Color wipe animations.')                         
            colorWipe(strip(0,LED_COUNT), Color(255, 0, 0))  # Red wipe
            colorWipe(strip, Color(0, 255, 0))  # Blue wipe
            colorWipe(strip, Color(0, 0, 255))  # Green wipe   
            print ('Theater chase animations.')
            theaterChase(strip, Color(127, 127, 127))  # White theater chase
            theaterChase(strip, Color(127,   0,   0))  # Red theater chase
            theaterChase(strip, Color(  0,   0, 127))  # Blue theater chase           
            print ('Rainbow animations.')
            rainbow(strip)
            print ('off.')
            off(strip)
            #rainbowCycle(strip)
            #theaterChaseRainbow(strip)
      
    except KeyboardInterrupt:
        if args.clear:
            colorWipe(strip, Color(0,0,0), 10)



kattni wrote:You'll want to use slicing. Here are a couple of examples that use slicing with LEDs:

https://learn.adafruit.com/circuitpytho ... -code-13-7

https://learn.adafruit.com/circuitpytho ... half-10-14

https://learn.adafruit.com/burning-fire ... -files-3-4

These examples should get you going. You'll want to identify slices, and then apply each animation to a different slice.

The second example is followed by a brief explanation of using slicing.

The third example is a lot more complicated but has more advanced usage of slices.

prudhvychoudary15
 
Posts: 5
Joined: Tue Apr 07, 2020 5:58 am

Re: Multiple Animations on a single Adafruit'sb NeoPixel str

by kattni on Thu Apr 16, 2020 4:11 pm

I'm not certain how to use slicing with rainbow code. I've only ever used it with other types of animations. Looking at your code, I'm not sure where to put the slicing either. My understanding of using slicing is limited to having the strip variable available and setting it to a particular color. However, in your code, all of the animations are handled by functions. There are two likely places for it. Either you need to change how the functions work, or you need to change how they are called in your while True: loop.

First try changing
Code: Select all | TOGGLE FULL SIZE
            colorWipe(strip(0,LED_COUNT), Color(255, 0, 0))  # Red wipe

To something like:
Code: Select all | TOGGLE FULL SIZE
            colorWipe(strip[0:5], Color([(255, 0, 0)] * 5))  # Red wipe

And see if it displays the colorWipe on only the first five pixels.

Beyond that, I would be guessing as well.

kattni
 
Posts: 72
Joined: Fri Aug 18, 2017 6:33 pm

Re: Multiple Animations on a single Adafruit'sb NeoPixel str

by Foamyguy on Thu Apr 16, 2020 5:17 pm

I think there are two main things that would need to be done with this code to make it work like you are asking:

1) The animation functions need to be modified to accept the range of pixels that they will take effect on. Right now they all assume they are acting on the whole strip because they all use for loops similar to this:
Code: Select all | TOGGLE FULL SIZE
for i in range(strip.numPixels()):

Which is making it loop over all of the pixels instead of just a subset.

2) Right now the animation functions run through the entire animation when you call them. They won't return until they have finished entirely. This means you can't really do anything else (i.e. run animations on other pixels) until they complete and return. I would probably try to modify the functions to only "do" one step of the animation. They would need an extra parameter for passing the "index" of the step that you want to run. They would make the changes required only for one step of the animation and then return. Then inside your main loop you can have a for loop that counts through indexes and you'll pass the current index to each of the animation functions they will each do one step and then it'll go to the next iteration and the index will increase...

For most of the animation functions the first one of the changes is probably a little bit easier to do than the second. The theater chase one is a bit more tricky because it splits the strip into thirds first, but it could still work similarly if it were passed a range to effect.

The second change is perhaps more difficult to make because it requires thinking about the animation loop a little differently in single steps at a time instead of the whole loop. In some ways it's a bit like the idea of recursive functions: "do one step of the iteration and then return".

I can try to help you modify the code with these changes but I will likely not have the time to make much progress on it until tomorrow evening.

Foamyguy
 
Posts: 34
Joined: Mon May 26, 2014 4:24 pm

Re: Multiple Animations on a single Adafruit'sb NeoPixel str

by prudhvychoudary15 on Fri Apr 17, 2020 6:00 am

I have already tried it after looking into the example you have mention earlier. But it is giving the following error.


colorWipe(strip[0:5], Color([{255, 0, 0}]*5)) # Red wipe
TypeError: 'Adafruit_NeoPixel' object has no attribute '__getitem__'


But thanks anyway for your help.

kattni wrote:I'm not certain how to use slicing with rainbow code. I've only ever used it with other types of animations. Looking at your code, I'm not sure where to put the slicing either. My understanding of using slicing is limited to having the strip variable available and setting it to a particular color. However, in your code, all of the animations are handled by functions. There are two likely places for it. Either you need to change how the functions work, or you need to change how they are called in your while True: loop.

First try changing
Code: Select all | TOGGLE FULL SIZE
            colorWipe(strip(0,LED_COUNT), Color(255, 0, 0))  # Red wipe

To something like:
Code: Select all | TOGGLE FULL SIZE
            colorWipe(strip[0:5], Color([(255, 0, 0)] * 5))  # Red wipe

And see if it displays the colorWipe on only the first five pixels.

Beyond that, I would be guessing as well.

prudhvychoudary15
 
Posts: 5
Joined: Tue Apr 07, 2020 5:58 am

Re: Multiple Animations on a single Adafruit'sb NeoPixel str

by prudhvychoudary15 on Fri Apr 17, 2020 10:57 am

Hi Foamguy,

I have tried changing the range in all the positions and tried everything but it is showing the error.

Traceback (most recent call last):
File "strandtest.py", line 105, in <module>
colorWipe(strip, Color(255, 0, 0)) # Red wipe
File "strandtest.py", line 25, in colorWipe
for i in range(strip.numPixels([0,10]*10)):
TypeError: numPixels() takes exactly 1 argument (2 given)

2nd try;
I have even tried giving just 10 as a range and it is still showing that there are two arguments given. which I do not understand, why it is reading as two inputs.


any three animations are fine for me. rainbow is not important. I have succeeded in creating two parts of the same animation with different properties using the following code, but its same animation on two parts.


Code: Select all | TOGGLE FULL SIZE
import time
import _rpi_ws281x as ws
from neopixel import *

# LED configuration.
LED_CHANNEL    = 0
LED_COUNT     = 60       # How many LEDs to light.
LED_FREQ_HZ    = 800000     # Frequency of the LED signal.  Should be 800khz or 400khz.
LED_DMA_NUM    = 10         # DMA channel to use, can be 0-14.
LED_GPIO       = 18         # GPIO connected to the LED signal line.  Must support PWM!
LED_BRIGHTNESS = 50        # Set to 0 for darkest and 255 for brightest
LED_INVERT     = 0          # Set to 1 to invert the LED signal, good if using NPN
                     # transistor as a 3.3V->5V level converter.  Keep at 0
                     # for a normal/non-inverted signal.

# Define colors which will be used by the example.  Each color is an unsigned
# 32-bit value where the lower 24 bits define the red, green, blue data (each
# being 8 bits long).
DOT_COLORS = [  0x000020,   # red
            0x002020,   # orange
            0x001520,   # yellow
            0x001020,   # green
            0x000520,   # lightblue
            0x000020,   # blue
            0x000015,   # purple
            0x002015 ]  # pink
DOT_COLORS2 = [  0x200000,   # red
            0x000020,   # BLUE
            0x200010,   # PINK
            0x002000,   # green
            0x201000,   # ORANGE
            0x000020,   # blue
            0x100010,   # purple
            0x200010 ]  # pinK


# Create a ws2811_t structure from the LED configuration.
# Note that this structure will be created on the heap so you need to be careful
# that you delete its memory by calling delete_ws2811_t when it's not needed.
leds = ws.new_ws2811_t()

# Initialize all channels to off
for channum in range(0):
    channel = ws.ws2811_channel_get(leds, channum)
    ws.ws2811_channel_t_count_set(channel, 0)
    ws.ws2811_channel_t_gpionum_set(channel, 0)
    ws.ws2811_channel_t_invert_set(channel, 0)
    ws.ws2811_channel_t_brightness_set(channel, 0)

channel = ws.ws2811_channel_get(leds, LED_CHANNEL)

ws.ws2811_channel_t_count_set(channel, LED_COUNT)
ws.ws2811_channel_t_gpionum_set(channel, LED_GPIO)
ws.ws2811_channel_t_invert_set(channel, LED_INVERT)
ws.ws2811_channel_t_brightness_set(channel, LED_BRIGHTNESS)

ws.ws2811_t_freq_set(leds, LED_FREQ_HZ)
ws.ws2811_t_dmanum_set(leds, LED_DMA_NUM)

# Initialize library with LED configuration.
resp = ws.ws2811_init(leds)
if resp != ws.WS2811_SUCCESS:
   message = ws.ws2811_get_return_t_str(resp)
   raise RuntimeError('ws2811_init failed with code {0} ({1})'.format(resp, message))

# Wrap following code in a try/finally to ensure cleanup functions are called
# after library is initialized.
try:
   offset = 2
   while True:
      # Update each LED color in the buffer.
      for i in range(LED_COUNT):
                         # Pick a color based on LED position and an offset for animation.
         color = DOT_COLORS[(i+1 + offset) % len(DOT_COLORS)]

         # Set the LED color buffer value.
         ws.ws2811_led_set(channel, i, color)

      # Send the LED color data to the hardware.
      resp = ws.ws2811_render(leds)
      if resp != ws.WS2811_SUCCESS:
         message = ws.ws2811_get_return_t_str(resp)
         raise RuntimeError('ws2811_render failed with code {0} ({1})'.format(resp, message))

      # Delay for a small period of time.
      time.sleep(1)

      # Increase offset to animate colors moving.  Will eventually overflow, which
      # is fine.
      offset += 2
      
      # Update each LED color in the buffer.
      for i in range(LED_COUNT):
         # Pick a color based on LED position and an offset for animation.
         color = DOT_COLORS2[(i+5 + offset) % len(DOT_COLORS2)]

         # Set the LED color buffer value.
         ws.ws2811_led_set(channel, i, color)

      # Send the LED color data to the hardware.
      resp = ws.ws2811_render(leds)
      if resp != ws.WS2811_SUCCESS:
         message = ws.ws2811_get_return_t_str(resp)
         raise RuntimeError('ws2811_render failed with code {0} ({1})'.format(resp, message))

      #Delay for a small period of time.
#           time.sleep(2)

      # Increase offset to animate colors moving.  Will eventually overflow, which
      # is fine.
#      offset += 1

finally:
   # Ensure ws2811_fini is called before the program quits.
        for i in range(0,60):
                color = 0x000000
        ws.ws2811_led_set(channel, i, color)
       
        ws.ws2811_fini(leds)
   # Example of calling delete function to clean up structure memory.  Isn't
   # strictly necessary at the end of the program execution here, but is good practice.
   ws.delete_ws2811_t(leds)



I must have the following animations
1. blink effect
2.theater chase
3.wheel



Thanks a ton for your wonderful explanation.
Greetings from Berlin

Foamyguy wrote:I think there are two main things that would need to be done with this code to make it work like you are asking:

1) The animation functions need to be modified to accept the range of pixels that they will take effect on. Right now they all assume they are acting on the whole strip because they all use for loops similar to this:
Code: Select all | TOGGLE FULL SIZE
for i in range(strip.numPixels()):

Which is making it loop over all of the pixels instead of just a subset.

2) Right now the animation functions run through the entire animation when you call them. They won't return until they have finished entirely. This means you can't really do anything else (i.e. run animations on other pixels) until they complete and return. I would probably try to modify the functions to only "do" one step of the animation. They would need an extra parameter for passing the "index" of the step that you want to run. They would make the changes required only for one step of the animation and then return. Then inside your main loop you can have a for loop that counts through indexes and you'll pass the current index to each of the animation functions they will each do one step and then it'll go to the next iteration and the index will increase...

For most of the animation functions the first one of the changes is probably a little bit easier to do than the second. The theater chase one is a bit more tricky because it splits the strip into thirds first, but it could still work similarly if it were passed a range to effect.

The second change is perhaps more difficult to make because it requires thinking about the animation loop a little differently in single steps at a time instead of the whole loop. In some ways it's a bit like the idea of recursive functions: "do one step of the iteration and then return".

I can try to help you modify the code with these changes but I will likely not have the time to make much progress on it until tomorrow evening.

prudhvychoudary15
 
Posts: 5
Joined: Tue Apr 07, 2020 5:58 am

Re: Multiple Animations on a single Adafruit'sb NeoPixel str

by Foamyguy on Sat Apr 18, 2020 10:08 am

I have made the first of the 2 modifications I mentioned to the strandtest program I added range_start and range_end parameters to the animation functions. You can use these to pass in a beginning index and ending index that you want the animation to take effect on. My strip is 30 pixels and right now it's set to divide the strip into thirds and play different animations on them. But you could tweak the values to suite your needs.

However this does not yet play the animations simultaneously, they still happen one after another.

I will work on it some more today to see if I can make the animations use the "one step at a time" strategy that I mentioned in the previous post.

Code: Select all | TOGGLE FULL SIZE
#!/usr/bin/env python3
# NeoPixel library strandtest example
# Author: Tony DiCola (tony@tonydicola.com) edited by foamyguy
#
# Direct port of the Arduino NeoPixel library strandtest example.  Showcases
# various animations on a strip of NeoPixels.

import time
from neopixel import *
import argparse
PIXELS = 30
# LED strip configuration:
LED_COUNT      = PIXELS   # Number of LED pixels.
LED_PIN        = 18      # GPIO pin connected to the pixels (18 uses PWM!).
#LED_PIN        = 10      # GPIO pin connected to the pixels (10 uses SPI /dev/spidev0.0).
LED_FREQ_HZ    = 800000  # LED signal frequency in hertz (usually 800khz)
LED_DMA        = 10      # DMA channel to use for generating signal (try 10)
LED_BRIGHTNESS = 10     # Set to 0 for darkest and 255 for brightest
LED_INVERT     = False   # True to invert the signal (when using NPN transistor level shift)
LED_CHANNEL    = 0       # set to '1' for GPIOs 13, 19, 41, 45 or 53



# Define functions which animate LEDs in various ways.
def colorWipe(strip, color, wait_ms=50, range_begin=0, range_end=-1):
    if range_end ==-1:
        range_end = strip.numPixels()
    """Wipe color across display a pixel at a time."""
    for i in range(range_begin, range_end):
        strip.setPixelColor(i, color)
        strip.show()
        time.sleep(wait_ms/1000.0)

def theaterChase(strip, color, wait_ms=50, iterations=10, range_begin=0, range_end=-1):
    if range_end ==-1:
        range_end = strip.numPixels()
    """Movie theater light style chaser animation."""
    for j in range(iterations):
        for q in range(3):
            for i in range(range_begin, range_end, 3):
                if i+q > range_end-1:
                    break
                strip.setPixelColor(min(range_end-1,i+q), color)
            strip.show()
            time.sleep(wait_ms/1000.0)
            for i in range(range_begin, range_end, 3):
                if i+q > range_end-1:
                    break
                strip.setPixelColor(min(range_end-1,i+q), 0)

def wheel(pos):
    """Generate rainbow colors across 0-255 positions."""
    if pos < 85:
        return Color(pos * 3, 255 - pos * 3, 0)
    elif pos < 170:
        pos -= 85
        return Color(255 - pos * 3, 0, pos * 3)
    else:
        pos -= 170
        return Color(0, pos * 3, 255 - pos * 3)

def rainbow(strip, wait_ms=20, iterations=1, range_begin=0, range_end=-1):
    if range_end ==-1:
        range_end = strip.numPixels()
    """Draw rainbow that fades across all pixels at once."""
    for j in range(256*iterations):
        for i in range(range_begin, range_end):
            strip.setPixelColor(i, wheel((i+j) & 255))
        strip.show()
        time.sleep(wait_ms/1000.0)

def rainbowCycle(strip, wait_ms=20, iterations=5, range_begin=0, range_end=-1):
    if range_end ==-1:
        range_end = strip.numPixels()
    """Draw rainbow that uniformly distributes itself across all pixels."""
    for j in range(256*iterations):
        for i in range(range_begin, range_end):
            strip.setPixelColor(i, wheel((int(i * 256 / range_end-range_begin) + j) & 255))
        strip.show()
        time.sleep(wait_ms/1000.0)

def theaterChaseRainbow(strip, wait_ms=50, range_begin=0, range_end=-1):
    if range_end ==-1:
        range_end = strip.numPixels()
    """Rainbow movie theater light style chaser animation."""
    for j in range(256):
        for q in range(3):
            for i in range(range_begin, range_end, 3):
                if i+q > range_end-1:
                    break
                strip.setPixelColor(min(range_end-1,i+q), wheel((i+j) % 255))
            strip.show()
            time.sleep(wait_ms/1000.0)
            for i in range(range_begin, range_end, 3):
                if i+q > range_end-1:
                    break
                strip.setPixelColor(min(range_end-1,i+q), 0)

# Main program logic follows:
if __name__ == '__main__':
    # Process arguments
    parser = argparse.ArgumentParser()
    parser.add_argument('-c', '--clear', action='store_true', help='clear the display on exit')
    args = parser.parse_args()

    # Create NeoPixel object with appropriate configuration.
    strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL)
    # Intialize the library (must be called once before other functions).
    strip.begin()

    print ('Press Ctrl-C to quit.')
    if not args.clear:
        print('Use "-c" argument to clear LEDs on exit')

    try:

        while True:
            print ('Color wipe animations.')
            colorWipe(strip, Color(255, 0, 0), range_begin=0, range_end=PIXELS//3)  # Red wipe
            colorWipe(strip, Color(0, 255, 0), range_begin=PIXELS//3, range_end=PIXELS//3*2)  # Blue wipe
            colorWipe(strip, Color(0, 0, 255), range_begin=PIXELS//3, range_end=PIXELS//3*2)  # Green wipe
            print ('Theater chase animations.')
            theaterChase(strip, Color(127, 127, 127), range_begin=0, range_end=PIXELS//3)  # White theater chase
            theaterChase(strip, Color(127,   0,   0), range_begin=0, range_end=PIXELS//3)  # Red theater chase
            theaterChase(strip, Color(  0,   0, 127), range_begin=0, range_end=PIXELS//3)  # Blue theater chase
            print ('Rainbow animations.')
            #rainbow(strip, range_begin=PIXELS//3, range_end=PIXELS//3*2)
            #rainbowCycle(strip, range_begin=PIXELS//3*2, range_end=PIXELS)
            theaterChaseRainbow(strip, range_begin=PIXELS//3, range_end=PIXELS//3*2)

    except KeyboardInterrupt:
        if args.clear:
            colorWipe(strip, Color(0,0,0), 10)


Foamyguy
 
Posts: 34
Joined: Mon May 26, 2014 4:24 pm

Re: Multiple Animations on a single Adafruit'sb NeoPixel str

by Foamyguy on Sat Apr 18, 2020 1:30 pm

I've modified the script further to use the "one step at a time" approach I mentioned. This version of the strandtest script still allows you specify ranges like the first one I posted. But in this version the loop is moved outside of the animation functions and the current loop iterator is passed in to a new parameter iterator_step that must be passed to all of the functions now.

This makes it so each time you call the function it only executes one step of the animation which means you can then move on and execute one step of a different animation (on a different part of the strip) afterward rather than having to wait until the first animation is entirely complete to do anything else.

The code still works mostly the same as before but some of the loops within the animation functions have been replaced by modulus division of the iterator_step parameter. I tried to add comments for the main things that changed. Let me know if it does not make sense how it works and I can try help you understand more.

Code: Select all | TOGGLE FULL SIZE
#!/usr/bin/env python3
# NeoPixel library strandtest example
# Author: Tony DiCola (tony@tonydicola.com) edited by foamyguy
#
# Direct port of the Arduino NeoPixel library strandtest example.  Showcases
# various animations on a strip of NeoPixels.

import time
from neopixel import *
import argparse

# set the variable equal to size of your neopixel strip
PIXELS = 30
# LED strip configuration:
LED_COUNT      = PIXELS   # Number of LED pixels.
LED_PIN        = 18      # GPIO pin connected to the pixels (18 uses PWM!).
#LED_PIN        = 10      # GPIO pin connected to the pixels (10 uses SPI /dev/spidev0.0).
LED_FREQ_HZ    = 800000  # LED signal frequency in hertz (usually 800khz)
LED_DMA        = 10      # DMA channel to use for generating signal (try 10)
LED_BRIGHTNESS = 10     # Set to 0 for darkest and 255 for brightest
LED_INVERT     = False   # True to invert the signal (when using NPN transistor level shift)
LED_CHANNEL    = 0       # set to '1' for GPIOs 13, 19, 41, 45 or 53



# All animations will take 256 total iteration steps to complete


# Define functions which animate LEDs in various ways.
def colorWipe(strip, color, wait_ms=50, range_begin=0, range_end=-1, iteration_step=-1):
    """Wipe color across display a pixel at a time."""
    if range_end ==-1:
        range_end = strip.numPixels()

    # using modulous division and our range_begin offset to find which pixel
    # we need to change this step
    pixel_to_change = iteration_step % (range_end - range_begin) + range_begin

    # if it's the first pixel wipe the range clear
    if pixel_to_change - range_begin  == 0:
        for i in range(range_begin, range_end):
            strip.setPixelColor(i, Color(0,0,0))

    strip.setPixelColor(int(pixel_to_change), color)
    strip.show()
    if wait_ms > 0:
        time.sleep(wait_ms/1000.0)

def theaterChase(strip, color, wait_ms=50, range_begin=0, range_end=-1, iteration_step=-1):
    """Movie theater light style chaser animation."""
    if range_end ==-1:
        range_end = strip.numPixels()

    # using modulous division to affect every 3rd pixel
    q = iteration_step % 3
    # loop over pixles and turn them on
    for i in range(range_begin, range_end, 3):
        # if the pixel is outside of our range then break out
        if i+q > range_end-1:
            break
        strip.setPixelColor(min(range_end-1,i+q), color)
    strip.show()
    time.sleep(wait_ms/1000.0)

    # loop over pixels and turn them off
    for i in range(range_begin, range_end, 3):
        # if the pixel is outside of our range then break out
        if i+q > range_end-1:
            break
        strip.setPixelColor(min(range_end-1,i+q), 0)

def wheel(pos):
    """Generate rainbow colors across 0-255 positions."""
    if pos < 85:
        return Color(pos * 3, 255 - pos * 3, 0)
    elif pos < 170:
        pos -= 85
        return Color(255 - pos * 3, 0, pos * 3)
    else:
        pos -= 170
        return Color(0, pos * 3, 255 - pos * 3)

def rainbow(strip, wait_ms=20, range_begin=0, range_end=-1, iteration_step=-1):
    """Draw rainbow that fades across all pixels at once."""
    if range_end ==-1:
        range_end = strip.numPixels()

    # one color per iteration step
    j = iteration_step

    # loop over pixels in range and turn them on to current color from wheel
    for i in range(range_begin, range_end):
        strip.setPixelColor(i, wheel((i+j) & 255))
    strip.show()
    time.sleep(wait_ms/1000.0)

def rainbowCycle(strip, wait_ms=20, range_begin=0, range_end=-1, iteration_step=-1):
    """Draw rainbow that uniformly distributes itself across all pixels."""
    if range_end ==-1:
        range_end = strip.numPixels()

    # one color per iteration step
    j = iteration_step

    # loop over pixels in range and turn them on to respective colors from wheel
    for i in range(range_begin, range_end):
        strip.setPixelColor(i, wheel((int(i * 256 / range_end-range_begin) + j) & 255))
    strip.show()
    time.sleep(wait_ms/1000.0)

def theaterChaseRainbow(strip, wait_ms=50, range_begin=0, range_end=-1, iteration_step=-1):
    """Rainbow movie theater light style chaser animation."""
    if range_end ==-1:
        range_end = strip.numPixels()

    # one color per iteration step
    j = iteration_step

    # using modulous division to affect every 3rd pixel
    q = iteration_step % 3

    # loop over pixels and turn them on
    for i in range(range_begin, range_end, 3):
        # if the pixel is outside of our range then break out
        if i+q > range_end-1:
            break
        strip.setPixelColor(min(range_end-1,i+q), wheel((i+j) % 255))
    strip.show()
    time.sleep(wait_ms/1000.0)
    # loop over pixels and turn them off
    for i in range(range_begin, range_end, 3):
        # if the pixel is outside of our range then break out
        if i+q > range_end-1:
            break
        strip.setPixelColor(min(range_end-1,i+q), 0)

# Main program logic follows:
if __name__ == '__main__':
    # Process arguments
    parser = argparse.ArgumentParser()
    parser.add_argument('-c', '--clear', action='store_true', help='clear the display on exit')
    args = parser.parse_args()

    # Create NeoPixel object with appropriate configuration.
    strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL)
    # Intialize the library (must be called once before other functions).
    strip.begin()

    print ('Press Ctrl-C to quit.')
    if not args.clear:
        print('Use "-c" argument to clear LEDs on exit')

    try:
        while True:
            for i in range(256):

                #colorWipe(strip, Color(255, 0, 0), wait_ms=10, range_begin=0, range_end=PIXELS//3, iteration_step=i)  # Red wipe
                #colorWipe(strip, Color(0, 255, 0), wait_ms=10, range_begin=PIXELS//3, range_end=PIXELS//3*2, iteration_step=i)  # Blue wipe
                #colorWipe(strip, Color(0, 0, 255), wait_ms=10, range_begin=PIXELS//3*2, range_end=PIXELS, iteration_step=i)  # Green wipe
                rainbowCycle(strip, range_begin=0, range_end=PIXELS//3, iteration_step=i)
                #theaterChase(strip, Color(127, 127, 127), range_begin=PIXELS//3, range_end=PIXELS//3*2, iteration_step=i)
                theaterChaseRainbow(strip, range_begin=PIXELS//3, range_end=PIXELS//3*2, iteration_step=i)
                rainbow(strip, range_begin=PIXELS//3*2, range_end=PIXELS, iteration_step=i)

            # clear pixels
            for i in range(30):
                colorWipe(strip, Color(0,0,0), wait_ms=20, iteration_step=i)

    except KeyboardInterrupt:
        if args.clear:
            for i in range(30):
                colorWipe(strip, Color(0,0,0), wait_ms=10, iteration_step=i)


Foamyguy
 
Posts: 34
Joined: Mon May 26, 2014 4:24 pm

Re: Multiple Animations on a single Adafruit'sb NeoPixel str

by prudhvychoudary15 on Sun Apr 19, 2020 11:12 am

Foamguy , I can't thank you enough. Everything works exactly the way i want it to be. I can't imagine that i have spent almost 2 months trying to figure it on my own.

I have only one issue, as an example, I just tried to run only color wipe animation.it works flawlessly. But, after a certain time, the animation is restarting. I tried modifying all the values in the clear pixel step, but no use. I couldn't even able to figure out how much duration is the animation playing before it's going to the Clear pixel step.

If you don't have time, i can understand. Thanks once again.


Foamyguy wrote:I've modified the script further to use the "one step at a time" approach I mentioned. This version of the strandtest script still allows you specify ranges like the first one I posted. But in this version the loop is moved outside of the animation functions and the current loop iterator is passed in to a new parameter iterator_step that must be passed to all of the functions now.

This makes it so each time you call the function it only executes one step of the animation which means you can then move on and execute one step of a different animation (on a different part of the strip) afterward rather than having to wait until the first animation is entirely complete to do anything else.

The code still works mostly the same as before but some of the loops within the animation functions have been replaced by modulus division of the iterator_step parameter. I tried to add comments for the main things that changed. Let me know if it does not make sense how it works and I can try help you understand more.

Code: Select all | TOGGLE FULL SIZE
#!/usr/bin/env python3
# NeoPixel library strandtest example
# Author: Tony DiCola (tony@tonydicola.com) edited by foamyguy
#
# Direct port of the Arduino NeoPixel library strandtest example.  Showcases
# various animations on a strip of NeoPixels.

import time
from neopixel import *
import argparse

# set the variable equal to size of your neopixel strip
PIXELS = 30
# LED strip configuration:
LED_COUNT      = PIXELS   # Number of LED pixels.
LED_PIN        = 18      # GPIO pin connected to the pixels (18 uses PWM!).
#LED_PIN        = 10      # GPIO pin connected to the pixels (10 uses SPI /dev/spidev0.0).
LED_FREQ_HZ    = 800000  # LED signal frequency in hertz (usually 800khz)
LED_DMA        = 10      # DMA channel to use for generating signal (try 10)
LED_BRIGHTNESS = 10     # Set to 0 for darkest and 255 for brightest
LED_INVERT     = False   # True to invert the signal (when using NPN transistor level shift)
LED_CHANNEL    = 0       # set to '1' for GPIOs 13, 19, 41, 45 or 53



# All animations will take 256 total iteration steps to complete


# Define functions which animate LEDs in various ways.
def colorWipe(strip, color, wait_ms=50, range_begin=0, range_end=-1, iteration_step=-1):
    """Wipe color across display a pixel at a time."""
    if range_end ==-1:
        range_end = strip.numPixels()

    # using modulous division and our range_begin offset to find which pixel
    # we need to change this step
    pixel_to_change = iteration_step % (range_end - range_begin) + range_begin

    # if it's the first pixel wipe the range clear
    if pixel_to_change - range_begin  == 0:
        for i in range(range_begin, range_end):
            strip.setPixelColor(i, Color(0,0,0))

    strip.setPixelColor(int(pixel_to_change), color)
    strip.show()
    if wait_ms > 0:
        time.sleep(wait_ms/1000.0)

def theaterChase(strip, color, wait_ms=50, range_begin=0, range_end=-1, iteration_step=-1):
    """Movie theater light style chaser animation."""
    if range_end ==-1:
        range_end = strip.numPixels()

    # using modulous division to affect every 3rd pixel
    q = iteration_step % 3
    # loop over pixles and turn them on
    for i in range(range_begin, range_end, 3):
        # if the pixel is outside of our range then break out
        if i+q > range_end-1:
            break
        strip.setPixelColor(min(range_end-1,i+q), color)
    strip.show()
    time.sleep(wait_ms/1000.0)

    # loop over pixels and turn them off
    for i in range(range_begin, range_end, 3):
        # if the pixel is outside of our range then break out
        if i+q > range_end-1:
            break
        strip.setPixelColor(min(range_end-1,i+q), 0)

def wheel(pos):
    """Generate rainbow colors across 0-255 positions."""
    if pos < 85:
        return Color(pos * 3, 255 - pos * 3, 0)
    elif pos < 170:
        pos -= 85
        return Color(255 - pos * 3, 0, pos * 3)
    else:
        pos -= 170
        return Color(0, pos * 3, 255 - pos * 3)

def rainbow(strip, wait_ms=20, range_begin=0, range_end=-1, iteration_step=-1):
    """Draw rainbow that fades across all pixels at once."""
    if range_end ==-1:
        range_end = strip.numPixels()

    # one color per iteration step
    j = iteration_step

    # loop over pixels in range and turn them on to current color from wheel
    for i in range(range_begin, range_end):
        strip.setPixelColor(i, wheel((i+j) & 255))
    strip.show()
    time.sleep(wait_ms/1000.0)

def rainbowCycle(strip, wait_ms=20, range_begin=0, range_end=-1, iteration_step=-1):
    """Draw rainbow that uniformly distributes itself across all pixels."""
    if range_end ==-1:
        range_end = strip.numPixels()

    # one color per iteration step
    j = iteration_step

    # loop over pixels in range and turn them on to respective colors from wheel
    for i in range(range_begin, range_end):
        strip.setPixelColor(i, wheel((int(i * 256 / range_end-range_begin) + j) & 255))
    strip.show()
    time.sleep(wait_ms/1000.0)

def theaterChaseRainbow(strip, wait_ms=50, range_begin=0, range_end=-1, iteration_step=-1):
    """Rainbow movie theater light style chaser animation."""
    if range_end ==-1:
        range_end = strip.numPixels()

    # one color per iteration step
    j = iteration_step

    # using modulous division to affect every 3rd pixel
    q = iteration_step % 3

    # loop over pixels and turn them on
    for i in range(range_begin, range_end, 3):
        # if the pixel is outside of our range then break out
        if i+q > range_end-1:
            break
        strip.setPixelColor(min(range_end-1,i+q), wheel((i+j) % 255))
    strip.show()
    time.sleep(wait_ms/1000.0)
    # loop over pixels and turn them off
    for i in range(range_begin, range_end, 3):
        # if the pixel is outside of our range then break out
        if i+q > range_end-1:
            break
        strip.setPixelColor(min(range_end-1,i+q), 0)

# Main program logic follows:
if __name__ == '__main__':
    # Process arguments
    parser = argparse.ArgumentParser()
    parser.add_argument('-c', '--clear', action='store_true', help='clear the display on exit')
    args = parser.parse_args()

    # Create NeoPixel object with appropriate configuration.
    strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL)
    # Intialize the library (must be called once before other functions).
    strip.begin()

    print ('Press Ctrl-C to quit.')
    if not args.clear:
        print('Use "-c" argument to clear LEDs on exit')

    try:
        while True:
            for i in range(256):

                #colorWipe(strip, Color(255, 0, 0), wait_ms=10, range_begin=0, range_end=PIXELS//3, iteration_step=i)  # Red wipe
                #colorWipe(strip, Color(0, 255, 0), wait_ms=10, range_begin=PIXELS//3, range_end=PIXELS//3*2, iteration_step=i)  # Blue wipe
                #colorWipe(strip, Color(0, 0, 255), wait_ms=10, range_begin=PIXELS//3*2, range_end=PIXELS, iteration_step=i)  # Green wipe
                rainbowCycle(strip, range_begin=0, range_end=PIXELS//3, iteration_step=i)
                #theaterChase(strip, Color(127, 127, 127), range_begin=PIXELS//3, range_end=PIXELS//3*2, iteration_step=i)
                theaterChaseRainbow(strip, range_begin=PIXELS//3, range_end=PIXELS//3*2, iteration_step=i)
                rainbow(strip, range_begin=PIXELS//3*2, range_end=PIXELS, iteration_step=i)

            # clear pixels
            for i in range(30):
                colorWipe(strip, Color(0,0,0), wait_ms=20, iteration_step=i)

    except KeyboardInterrupt:
        if args.clear:
            for i in range(30):
                colorWipe(strip, Color(0,0,0), wait_ms=10, iteration_step=i)


prudhvychoudary15
 
Posts: 5
Joined: Tue Apr 07, 2020 5:58 am

Re: Multiple Animations on a single Adafruit'sb NeoPixel str

by Foamyguy on Sun Apr 19, 2020 11:54 am

You are welcome. I'm glad it's working how you wanted. Don't be too hard on yourself... This is fairly complex task to achieve. It definitely took me a long time to get to the point of fully understanding it enough to use it to solve a practical problem like the one you presented.

The duration of the animation is going to be a product of the number of iterations from the main 'for' loop and the wait_ms parameter that you pass to the animations within the loop. For instance if you set up your main loop like this:
Code: Select all | TOGGLE FULL SIZE
for i in range(256):
    colorWipe(strip, Color(255, 0, 0), wait_ms=10, range_begin=0, range_end=PIXELS//3, iteration_step=i)  # Red wipe


This would make the total duration be 256 iterations times 10ms wait value so it should be 2560ms total duration.

But the way the colorWipe animations works is that every 10 iterations completes one full visual "cycle" through the animation, so within those 256 iterations it's going to repeat the wipe effect 25.6 times clearing the pixels in between each one. And each one of those effects would take 10 iterations times 10ms wait time so 100ms which is very fast.

But the duration can be affected by having more animations running like if you have a main loop like this:
Code: Select all | TOGGLE FULL SIZE
for i in range(256):
    colorWipe(strip, Color(255, 0, 0), wait_ms=10, range_begin=0, range_end=PIXELS//3, iteration_step=i)  # Red wipe
    colorWipe(strip, Color(0, 255, 0), wait_ms=10, range_begin=PIXELS//3, range_end=PIXELS//3*2, iteration_step=i)  # Blue wipe

Now the total duration is 256 iterations times 20ms (10 for each animation call) which doubles the total time it takes up to 5120ms and increases the time for each visual cycle of the colorWipe to 200ms.

Continuing to add more animations into the loop will further increase the total duration of each animation as it's waiting for others to complete their steps.

Foamyguy
 
Posts: 34
Joined: Mon May 26, 2014 4:24 pm

Please be positive and constructive with your questions and comments.