"Accelerare" effetti dei pixel

Buondì, allora, ho un wemos d1 mini che controlla dei led indirizzabili, il problema è che quando ci sono tanti pixel, le animazioni vanno a rilento, mentre tipo con 300 led vanno molto fluide e veloci

Nella mia configurazione ne ho 1365 tutti in serie, sono dei WS2815 (dei WS2813 a 12v), ho provato anche con la più base delle animazioni, un semplice effetto di accensione e spegnimento sequenziale dei pixel ma niente da fare

Suggerimenti?

Questo è il codice che ho usato come prova:

#include <FastLED.h>

#define NUM_LEDS 1365
#define DATA_PIN 5

CRGB leds[NUM_LEDS];

void setup() {
   	delay(2000);
    FastLED.addLeds<WS2813, DATA_PIN, GRB>(leds, NUM_LEDS);
}

void loop() {
   for(int pixel = 0; pixel < NUM_LEDS; pixel = pixel + 1) {
      leds[pixel] = CRGB::White;
      FastLED.show();
      delay(10);
      leds[pixel] = CRGB::Black;
   }
}

C'è poco da fare ... tu lavori pixel a pixel mentre, di solito, si programmano prima TUTTI i pixel e poi si fa un unico show() per trasmettere i dati.

Ogni volta che fai show() sia che tu abbia cambiato solo 1 pixel, sia che tu li abbia cambiati tutti, vengono trasmessi comunque TUTTI ... ti redi conto da solo che la tua strada è lentissima ... ::slight_smile:

Guglielmo

gpb01:
C'è poco da fare ... tu lavori pixel a pixel mentre, di solito, si programmano prima TUTTI i pixel e poi si fa un unico show() per trasmettere i dati.

Ogni volta che fai show() sia che tu abbia cambiato solo 1 pixel, sia che tu li abbia cambiati tutti, vengono trasmessi comunque TUTTI ... ti redi conto da solo che la tua strada è lentissima ... ::slight_smile:

Guglielmo

Ma come faccio a programmarli che si spengano se non faccio lo show?
Cioè, se gli do il colore bianco e poi gli do il nero senza visualizzarlo, quando vado a chiamare FastLED.show() è come se fossero tutti spenti

Capisco ... e allora hai scelto i LED sbagliati ... invece dei NeoPixel (WS2812 o quello che è) dovevi usare gli APA102 che hanno un pin dedicato al clock, NON hanno problematiche di tempistiche, sono di un oridne di grandezza ( e forse più) più veloci e che, usati tramite il bus SPI, raggiungono velocità dell'ordine di svariati MHz (i NeoPixel viaggiano nelle centiania di KHz).

Guglielmo

Io adesso dico una grossa corbellerie

1365 è 5 per 273

Fare 5 linee da 273 led?

Non ho idea se la FastLED che lui usa (... e che io NON conosco e che NON uso) possa essere istanziata più volte ... una per ogni pin a cui colleghi i 5 DIN ... ::slight_smile:

Guglielmo

L'unica mi sa che è provare

E io aggiungo una corbelleria maggiore a quella del socio per farlo funzionare a prescindere anche con un'istanza sola :wink:
E se nel mezzo ci si mette un multiplexer?

Maurizio

Devo escludere eventuali modifiche hardware, in quanto ho i profili di alluminio che sono già saturi di cavi

Oggi ho fatto diverse prove, e c'è uno sketch di esempio con la libreria neopixelbus che va come una scheggia e non capisco il perchè:

// NeoPixelCylon
// This example will move a Cylon Red Eye back and forth across the 
// the full collection of pixels on the strip.
// 
// This will demonstrate the use of the NeoEase animation ease methods; that provide
// simulated acceleration to the animations.
//
//

#include <NeoPixelBus.h>
#include <NeoPixelAnimator.h>

const uint16_t PixelCount = 1365; // make sure to set this to the number of pixels in your strip
const uint8_t PixelPin = 2;  // make sure to set this to the correct pin, ignored for Esp8266
const RgbColor CylonEyeColor(HtmlColor(0x7f0000));

NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin);
// for esp8266 omit the pin
//NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount);

NeoPixelAnimator animations(2); // only ever need 2 animations

uint16_t lastPixel = 0; // track the eye position
int8_t moveDir = 1; // track the direction of movement

// uncomment one of the lines below to see the effects of
// changing the ease function on the movement animation
AnimEaseFunction moveEase =
//      NeoEase::Linear;
//      NeoEase::QuadraticInOut;
//      NeoEase::CubicInOut;
        NeoEase::QuarticInOut;
//      NeoEase::QuinticInOut;
//      NeoEase::SinusoidalInOut;
//      NeoEase::ExponentialInOut;
//      NeoEase::CircularInOut;

void FadeAll(uint8_t darkenBy)
{
    RgbColor color;
    for (uint16_t indexPixel = 0; indexPixel < strip.PixelCount(); indexPixel++)
    {
        color = strip.GetPixelColor(indexPixel);
        color.Darken(darkenBy);
        strip.SetPixelColor(indexPixel, color);
    }
}

void FadeAnimUpdate(const AnimationParam& param)
{
    if (param.state == AnimationState_Completed)
    {
        FadeAll(10);
        animations.RestartAnimation(param.index);
    }
}

void MoveAnimUpdate(const AnimationParam& param)
{
    // apply the movement animation curve
    float progress = moveEase(param.progress);

    // use the curved progress to calculate the pixel to effect
    uint16_t nextPixel;
    if (moveDir > 0)
    {
        nextPixel = progress * PixelCount;
    }
    else
    {
        nextPixel = (1.0f - progress) * PixelCount;
    }

    // if progress moves fast enough, we may move more than
    // one pixel, so we update all between the calculated and
    // the last
    if (lastPixel != nextPixel)
    {
        for (uint16_t i = lastPixel + moveDir; i != nextPixel; i += moveDir)
        {
            strip.SetPixelColor(i, CylonEyeColor);
        }
    }
    strip.SetPixelColor(nextPixel, CylonEyeColor);

    lastPixel = nextPixel;

    if (param.state == AnimationState_Completed)
    {
        // reverse direction of movement
        moveDir *= -1;

        // done, time to restart this position tracking animation/timer
        animations.RestartAnimation(param.index);
    }
}

void SetupAnimations()
{
    // fade all pixels providing a tail that is longer the faster
    // the pixel moves.
    animations.StartAnimation(0, 5, FadeAnimUpdate);

    // take several seconds to move eye fron one side to the other
    animations.StartAnimation(1, 2000, MoveAnimUpdate);
}

void setup()
{
    strip.Begin();
    strip.Show();

    SetupAnimations();
}

void loop()
{
    // this is all that is needed to keep it running
    // and avoiding using delay() is always a good thing for
    // any timing related routines
    animations.UpdateAnimations();
    strip.Show();
}

Mentre questo se metto tutti i led va lento, ma se ne metto 300 va veloce

// NeoPixelFunLoop
// This example will move a trail of light around a series of pixels.  
// A ring formation of pixels looks best.  
// The trail will have a slowly fading tail.
// 
// This will demonstrate the use of the NeoPixelAnimator.
// It shows the advanced use an animation to control the modification and 
// starting of other animations.
// It also shows the normal use of animating colors.
// It also demonstrates the ability to share an animation channel rather than
// hard code them to pixels.
//

#include <NeoPixelBus.h>
#include <NeoPixelAnimator.h>


const uint16_t PixelCount = 1365; // make sure to set this to the number of pixels in your strip
const uint16_t PixelPin = 2;  // make sure to set this to the correct pin, ignored for Esp8266
const uint16_t AnimCount = PixelCount / 5 * 2 + 1; // we only need enough animations for the tail and one extra

const uint16_t PixelFadeDuration = 300; // third of a second
// one second divide by the number of pixels = loop once a second
const uint16_t NextPixelMoveDuration = 1000 / PixelCount; // how fast we move through the pixels

NeoGamma<NeoGammaTableMethod> colorGamma; // for any fade animations, best to correct gamma

NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin);
// For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.  
// There are other Esp8266 alternative methods that provide more pin options, but also have
// other side effects.
// for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods 

// what is stored for state is specific to the need, in this case, the colors and
// the pixel to animate;
// basically what ever you need inside the animation update function
struct MyAnimationState
{
    RgbColor StartingColor;
    RgbColor EndingColor;
    uint16_t IndexPixel; // which pixel this animation is effecting
};

NeoPixelAnimator animations(AnimCount); // NeoPixel animation management object
MyAnimationState animationState[AnimCount];
uint16_t frontPixel = 0;  // the front of the loop
RgbColor frontColor;  // the color at the front of the loop

void SetRandomSeed()
{
    uint32_t seed;

    // random works best with a seed that can use 31 bits
    // analogRead on a unconnected pin tends toward less than four bits
    seed = analogRead(0);
    delay(1);

    for (int shifts = 3; shifts < 31; shifts += 3)
    {
        seed ^= analogRead(0) << shifts;
        delay(1);
    }

    // Serial.println(seed);
    randomSeed(seed);
}

void FadeOutAnimUpdate(const AnimationParam& param)
{
    // this gets called for each animation on every time step
    // progress will start at 0.0 and end at 1.0
    // we use the blend function on the RgbColor to mix
    // color based on the progress given to us in the animation
    RgbColor updatedColor = RgbColor::LinearBlend(
        animationState[param.index].StartingColor,
        animationState[param.index].EndingColor,
        param.progress);
    // apply the color to the strip
    strip.SetPixelColor(animationState[param.index].IndexPixel, 
        colorGamma.Correct(updatedColor));
}

void LoopAnimUpdate(const AnimationParam& param)
{
    // wait for this animation to complete,
    // we are using it as a timer of sorts
    if (param.state == AnimationState_Completed)
    {
        // done, time to restart this position tracking animation/timer
        animations.RestartAnimation(param.index);

        // pick the next pixel inline to start animating
        // 
        frontPixel = (frontPixel + 1) % PixelCount; // increment and wrap
        if (frontPixel == 0)
        {
            // we looped, lets pick a new front color
            frontColor = HslColor(random(360) / 360.0f, 1.0f, 0.25f);
        }

        uint16_t indexAnim;
        // do we have an animation available to use to animate the next front pixel?
        // if you see skipping, then either you are going to fast or need to increase
        // the number of animation channels
        if (animations.NextAvailableAnimation(&indexAnim, 1))
        {
            animationState[indexAnim].StartingColor = frontColor;
            animationState[indexAnim].EndingColor = RgbColor(0, 0, 0);
            animationState[indexAnim].IndexPixel = frontPixel;

            animations.StartAnimation(indexAnim, PixelFadeDuration, FadeOutAnimUpdate);
        }
    }
}

void setup()
{
    strip.Begin();
    strip.Show();

    SetRandomSeed();

    // we use the index 0 animation to time how often we move to the next
    // pixel in the strip
    animations.StartAnimation(0, NextPixelMoveDuration, LoopAnimUpdate);
}


void loop()
{
    // this is all that is needed to keep it running
    // and avoiding using delay() is always a good thing for
    // any timing related routines
    animations.UpdateAnimations();
    strip.Show();
}

Non conosco la libreria che hai usato, ma dando una rapida occhiata, nel primo caso, quello lento, vedo:

animations.StartAnimation(1, 2000, MoveAnimUpdate);

il 2000 intuisco sia un valore che determina la tempistica dell'animazione.

Nel secondo caso, quello veloce, invece, vedo:

animations.StartAnimation(0, NextPixelMoveDuration, LoopAnimUpdate);

Dove, però:

const uint16_t NextPixelMoveDuration = 1000 / PixelCount; // how fast we move through the pixels

e:

const uint16_t PixelCount = 1365;

Quindi invece di 2000 usa 1000 / PixelCount, quindi un valore mooooolto più piccolo.

...

Maurizio

maubarzi:
Non conosco la libreria che hai usato, ma dando una rapida occhiata, nel primo caso, quello lento, vedo:

animations.StartAnimation(1, 2000, MoveAnimUpdate);

E' proprio quello il "problema", paradossalmente il primo esempio è quello che mi va 20 volte più veloce del secondo, ho provato a variare anche nel secondo sketch ma non c'è verso di far andare più veloce l'animazione

Il numero di led è sempre lo stesso?

Maurizio

maubarzi:
Il numero di led è sempre lo stesso?

Maurizio

Si, 1365 su entrambi gli sketch, il primo va veloce e posso anche variare i parametri e funziona come ci si aspetta, cioè gli metto il tempo che deve impiegare per fare il giro completo dell'animazione, e al variare dei valori, varia anche la velocità di visualizzazione dell'effetto (funziona perfettamente anche a 500 o inferiore, solo che è troppo veloce)
    animations.StartAnimation(1, 2000, MoveAnimUpdate);

Sul secondo sketch (e anche su altri esempi della stessa libreria, o utilizzando la libreria Fastled) invece non sono stato capace di trovare la soluzione