WS2812 sequencing too slow

Adafruit's tiny microcontroller platform. Please tell us which board you are using.
For CircuitPython issues, ask in the Adafruit CircuitPython forum.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
BykeCommuter
 
Posts: 35
Joined: Tue Sep 06, 2016 9:46 am

WS2812 sequencing too slow

Post by BykeCommuter »

Using a Trinket M0 for this latest development I'm trying to get a knight rider or sweep or chase effect to work but without putting a time.sleep() or delay in the loop, the rate of 'chase' is noticeably not lightning fast even when modifying the code so that only every other pixel lights up.

Also, because of the nature of the intended application, I am feeding two strips from pins 3 and 4. The data forward is oriented away from the middle. Are there strip commands that would work better than writing LEDs individually? Maybe I need to try another library or go back to the Arduino IDE and away from CircuitPython?

Karma tipjar bonus for comments to have a better clean exit for the green sweep or to debug the Strobe1 subroutine.

video: https://www.youtube.com/watch?v=PP0eOQTY0cM

Code: Select all

import time
import neopixel
import board
NUMPIXELS = 48
npl = neopixel.NeoPixel(board.D3, NUMPIXELS, brightness=0.2, auto_write=False)
npr = neopixel.NeoPixel(board.D4, NUMPIXELS, brightness=0.2, auto_write=False)
pixbright=40

def Strobe1(red, green, blue, StrobeCount, FlashDelay, EndPause):
    for C in range(StrobeCount):
        for i in range(NUMPIXELS):
            i=0
            i=i+1
            npl[i] = (pixbright, 0, 0)
            npr[i] = (pixbright, 0, 0)
            npl.show()
            npr.show()
            time.sleep(FlashDelay)
        for i in range (NUMPIXELS):
            i=0
            i=i+1
            npl[i] = npr[i] = (0, 0, 0)
            #npr[i] = (0, 0, 0)
            npl.show()
            npr.show()
        time.sleep(EndPause)
def ClearStripL():
    i = 0
    while i <= 47:
        npr[i] = (0, 0, 0)
      #  np.show()
      #  time.sleep(0.0025)
        i=i+1
    npr.show()
    
def ClearStripR():
    i = 0
    while i <= 47:
        npl[i] = (0, 0, 0)
      #  np.show()
      #  time.sleep(0.0025)
        i = i+1
    npl.show()

def TestChase():
    p = 0
    while p <= 47:
        npr[p] = (10, 0, 0)
        npl[p-24] = (10, 0, 0)
      #  q=p-1
      #  np[p-1] = (0,0,0)
      #  np[q] = (0,0,0)
      #  np.show()
      #  time.sleep(0.01/2)
        p = p+1
    npr.show()
      
def LeftSignal():
    i = 24
    while i <= 47:
        npl[i] = (10, 0, 0)
        i = i+1
    npl.show()
    
def wig():
    x = 0
    for x in range(NUMPIXELS):
        #x = x+1
        npl[x] = (0, 0, pixbright)
        npr[x] = (pixbright, 0, 0)
        x = x+1
    npl.show()
    npr.show()
    time.sleep(0.4)
    
def wag():
    x=0
    for x in range(NUMPIXELS):
        npl[x] = (pixbright, 0, 0)
        npr[x] = (0, 0, pixbright)
        x=x+1
    npl.show()
    npr.show()
    time.sleep(0.4)

def BouncyBall():
    n = 0
    for n in range(0, 24):
        npl[45 - 2 * n] = (0, pixbright, 0)
        npl[45 - 2 * abs(n - 3)] = (0, 0, 0)
        n = n+1
        npl.show()
    n = 0
    for n in range(NUMPIXELS / 2):
        npr[2 * n] = (0, pixbright, 0)
        npr[2 * (n - 3)] = (0, 0, 0)
        n = n+1
        npr.show()
        
def OutFill():
    n = 0
    for n in range(0, 47, 2):
        npl[n] = (pixbright, 0, 0)
        npr[n] = (pixbright, 0, 0)
        npl.show()
        npr.show()
        n=n+1
        
def InFill():
    n=0
    for n in range(0, 47, 2):
        npl[47-n] = (pixbright, 0, 0)
        npr[47-n] = (pixbright, 0, 0)
        npl.show()
        npr.show()
        n=n+1
        
while True:
    InFill()
    #time.sleep(0.5)
    ClearStripR()
    ClearStripL()
    OutFill()
    ClearStripR()
    ClearStripL()
    #Strobe1(0, 85, 0, 20, 25, 100)
    BouncyBall()
    ClearStripR()
    ClearStripL()
    wig()
    wag()
    ClearStripR()
    ClearStripL()
    #TestChase()
    #LeftSignal()

User avatar
BrickBeau
 
Posts: 6
Joined: Mon Apr 16, 2018 12:36 am

Re: WS2812 sequencing too slow

Post by BrickBeau »

Your code looks quite complex. I do not know Circuitpython yet so I am still using the Arduino IDE. Try this test. I am using it with a Tricket M0 and it is lightning fast when delay is set to 0. This code just goes in one direction. I think that an additional counter to decrement will give you what you want. I am powering a 144 string of skinny NeoPixels from the 3.3v pin. My Trinket is powered by USB.

Code: Select all

// Simple NeoPixel test.  Lights just a few pixels at a time so a
// 1m strip can safely be powered from Arduino 5V pin.  Arduino
// may nonetheless hiccup when LEDs are first connected and not
// accept code.  So upload code first, unplug USB, connect pixels
// to GND FIRST, then +5V and digital pin 6, then re-plug USB.
// A working strip will show a few pixels moving down the line,
// cycling between red, green and blue.  If you get no response,
// might be connected to wrong end of strip (the end wires, if
// any, are no indication -- look instead for the data direction
// arrows printed on the strip).

#include <Adafruit_NeoPixel.h>

#define PIN      4
#define N_LEDS 144

Adafruit_NeoPixel strip = Adafruit_NeoPixel(N_LEDS, PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  strip.begin();
}

void loop() {
  chase(strip.Color(255, 0, 0)); // Red
  chase(strip.Color(0, 255, 0)); // Green
  chase(strip.Color(0, 0, 255)); // Blue
}

static void chase(uint32_t c) {
  for(uint16_t i=144; i<strip.numPixels()+4; i--) {
      strip.setPixelColor(i  , c); // Draw new pixel
      strip.setPixelColor(i+4, 0); // Erase pixel a few steps back
      strip.show();
      delay(25);
  }
}

User avatar
BykeCommuter
 
Posts: 35
Joined: Tue Sep 06, 2016 9:46 am

Re: WS2812 sequencing too slow

Post by BykeCommuter »

I had to shelf this project for a couple of months. I came back to it tonight and tried some things on just a basic strobe effect subroutine. For 8-16 pixels, the effect is OK. I have 2 strips wired up btw, and oscillating 16 pixels on just one or 8 on each using 2 different pins yields the same strobe rate. The only noticeable difference is adding more and more pixels to the code, which slows the rate down, but it doesn't seem to matter if all the pixels are PWM'd across one pin or two. This makes me think it is a limitation of the chip (less likely) or that the CircuiPython code is compiled in a more complex and slower to run manner than using the Arduino IDE (more likely?)

My earlier version strobed 96 pixels very effectively, but that was using the Pro Trinket and the Arduino IDE.

I'm suspecting that using CircuitPython does not support the same speedy PWM rate as the Arduino IDE compiler. Can someone confirm this? Is there a CircuitPython workaround? That would be the most ideal for my end goal -- to have a Web interface that would update the MyPython file with newly selected light pattern options without having to compile through the Duino IDE.

User avatar
BykeCommuter
 
Posts: 35
Joined: Tue Sep 06, 2016 9:46 am

Re: WS2812 sequencing too slow

Post by BykeCommuter »

It's been a while since I assembled the Pro Trinket and 96 pixel setup. I should clarify that I was running 48 pixels really, but as doubles. Blame it on my memory ;)

User avatar
adafruit_support_bill
 
Posts: 88141
Joined: Sat Feb 07, 2009 10:11 am

Re: WS2812 sequencing too slow

Post by adafruit_support_bill »

Calling show() takes some time. The more pixels you have the longer it takes.

For faster performance, move your calls to "show()" outside of the loops. That way you are only calling once after you set all the pixel colors in the strip - instead of calling it after each pixel change.

User avatar
BykeCommuter
 
Posts: 35
Joined: Tue Sep 06, 2016 9:46 am

Re: WS2812 sequencing too slow

Post by BykeCommuter »

Gotcha. I can see how that would speed up a blink or strobe effect where every pixel was being altered at the same time. The chase speed of a night rider effect would still be rather limited wouldn't it?

User avatar
adafruit_support_bill
 
Posts: 88141
Joined: Sat Feb 07, 2009 10:11 am

Re: WS2812 sequencing too slow

Post by adafruit_support_bill »

It should be possible to make a pretty speedy chase. But at some point you will be limited by the overhead of the python interpreter.

User avatar
aaronaverill
 
Posts: 84
Joined: Sun Jun 03, 2018 10:45 am

Re: WS2812 sequencing too slow

Post by aaronaverill »

You're piling up a massive amount of inefficiencies in your code. I've got a trinket M0 and have seen refresh rates in the 2000 pixels per second and beyond rate in Circuit Python.

If you're doing simple chase stuff, not algorithmic, you may want to use my library that drives pixels from bitmap files stored on your flash drive. Here:

https://github.com/aaronaverill/CircuitPython_NeoSprite

If you want to keep tuning your own code, some things to keep in mind.

1. Using brightness on the pixel strip results in a floating point operation for every RGB tuple as well as copying the entire bytearray in memory. Take a look at this line of code:

https://github.com/adafruit/Adafruit_Ci ... el.py#L227

That's a big NOPE for fast animations. Instead you should compute RGB values from 0 to [brightness]*255. So if you're using 0.2, set your RGB values from 0 to 51 directly and set the brightness to 1 to avoid a BUNCH of slow code.

2. (as mentioned) Get your show() method calls under control - each show() refreshes the pixels. If you're doing things like clearing and then setting them to a color there is no need to call show in between. Your show should be placed AFTER you set all the pixel colors, outside the loop. This alone should fix all your problems.

3. Advanced performance: If you're comfortable hardcoding some things, figuring out rgb order, and hacking byte arrays you can directly access the neopixel.buf element instead of calling neopixel = (r, g, b). Take a look at all the code in the index setter and figure out if you need or use any of this, moreover, you avoid a function call for every pixel.

https://github.com/adafruit/Adafruit_Ci ... el.py#L123

In particular for zeroing out a pixel strip, you shouldn't be looping and calling npr = (0, 0, 0), just set all the buf to 0

4. More Advanced performance: You can create pre-allocated and pre-computed bytearrays for your animations, shift things around, and call neopixel_write(buffer) when needed. Your code will be blazingly fast if you can manage the memory - there's ways such as feezing to optimize this. Basically this is what my neosprite library does.

More advices:

You're also doing some basic python performance mistakes - you should use the for .. in range operator instead of while loops which are less efficient.

All those instances of 47 should be replaced by NUMPIXELS-1 or access the values directly on the neopixel object. An example to get you started (haven't tested, but should work or be close)

Code: Select all

def ClearStripL():
  for i in range(0, bpr.n * bpr.bpp):
    bpr.buf[i] = b'0x00'


EDIT: I just looked at your video. I think wiring this as a single strip and using the library I wrote to animate .BMP files is a super easy solution if you just want to get it working and aren't doing this to learn about coding pixels efficiently. Here's a video of my recent project, I think this is animating at about 1/20th of max speed.

https://www.youtube.com/watch?v=XaCqrlE20PE


have fun!

User avatar
aaronaverill
 
Posts: 84
Joined: Sun Jun 03, 2018 10:45 am

Re: WS2812 sequencing too slow

Post by aaronaverill »

Update. Here is a video of my project running at half speed on the trinket m0 in circuit python. I'm not 100% sure the video posted above is a trinket, it might have been my feather m0 express, so I decided to shoot another video and post it up. It's the same chip but there are different options in compile such as long int support on feather but not on the trinket. So...

There are six pixels around, and each animation frame is refreshed twice per cycle with zero sleep calls. So you can essentially expect 10x faster speed on a single neopixel strip as you're seeing the vertical animation. You're seeing a 6x8 array of pixels.

https://www.youtube.com/watch?v=zBWRE_DQU84

TL;DR: CP is plenty fast for animations up to several hundred pixels.

Locked
Please be positive and constructive with your questions and comments.

Return to “Trinket ATTiny, Trinket M0”