APA102 DotStar strips animation is slowing down

I have a continuous strip of 4,320 LEDs (30m). Upon an event occurring in the cloud, a ‘comet’ will run the full length of the strip. However, when there are a lot of comets running, the entire animation begins to drastically slow down. Is there any way for me to keep this animation from slowing down?

Here is my code:

#define NUMPIXELS 4320 // Number of LEDs in strip


//-------------------------------------------------------------------
// Here's how to control the LEDs from SPI pins (Hardware SPI):
//-------------------------------------------------------------------
// Hardware SPI is a little faster, but must be wired to specific pins
// (Core/Photon/P1/Electron = pin A5 for data, A3 for clock)
// Yellow: clock   Green: data

#define DATAPIN   A5
#define CLOCKPIN  A3
uint32_t pxBuffer[1 + NUMPIXELS + NUMPIXELS/16 + 1]; // element 0 (leadin) ... LEDs ... 1 leadout clock per 2 LEDs (ceil)


// Compensate for frequency variation among strips {r, g, b}
#define STRIP_COUNT NUMPIXELS/144
float gamma_s0[ 3 ] = { 1.0, 1.0, 1.0 }; //no change
float gamma_s1[ 3 ] = { 0.4, 0.9, 1.0 }; //less green
float gamma_s2[ 3 ] = { 0.8, 0.2, 0.2 }; //less red and blue
float gamma_s3[ 3 ] = { 0.6, 0.4, 0.3 }; // ,g,


float* gamma[ STRIP_COUNT ] = {
    gamma_s0, gamma_s0, gamma_s0, gamma_s0, gamma_s0, gamma_s0, gamma_s0, gamma_s0, gamma_s0, gamma_s0,
    gamma_s0, gamma_s0, gamma_s0, gamma_s0, gamma_s0, gamma_s0, gamma_s0, gamma_s0, gamma_s0, gamma_s0,
    gamma_s0, gamma_s0, gamma_s0, gamma_s0, gamma_s0, gamma_s0, gamma_s0, gamma_s0, gamma_s0, gamma_s0
};

//ADD MORE VALUES IF LENGTH OF STRIP INCREASES
int strip_position[ STRIP_COUNT ] = {
       0,  144,  288,  432,  576,  720,  864, 1008, 1152, 1296,
    1440, 1584, 1728, 1872, 2016, 2160, 2304, 2448, 2592, 2736,
    2880, 3024, 3168, 3312, 3456, 3600, 3744, 3888, 4032, 4176
};

uint8_t red_for_pixel ( int p, int n ) {
float* ga;
  for (int i = 0; i < STRIP_COUNT; i++) {
    if (p >= strip_position[i] && p < strip_position[i+1])  {
      ga = gamma[ i ];
    }
  }
  
  int r = n * ga[ 0 ];
  
  return r;
}

uint8_t green_for_pixel ( int p, int n ) {
    int i;
    for ( i=0 ; p < strip_position[ i ] ; i++ ) {}
    float* ga = gamma[ i ];
    
    int g = n * ga[ 1 ];
    
    return g;
}

uint8_t blue_for_pixel ( int p, int n ) {
    int i;
    for ( i=0 ; p < strip_position[ i ] ; i++ ) {}
    float* ga = gamma[ i ];
    
    int b = n * ga[ 2 ];
    
    return b;
}


struct ANIMATION_DATA
{
  uint8_t g;
  uint8_t b;
  uint8_t r;
  int     pos;
  int     len;
};

// circular buffer with 100 slots will overwrite unfinished animations when full
const int      maxAnim  = 100;
int            animHead = 0;
int            animTail = 0;
ANIMATION_DATA anim[maxAnim];


const uint8_t  brightAnim[] = 
{ 255, 255, 255 
, 255, 255, 255
, 255, 255, 255
, 255, 255, 255
, 240, 240, 240
, 220, 220, 220
, 200, 200, 190
, 180, 165, 140
, 120, 100,  90
,  80,  70,  50
};

const int      cometLen = sizeof(brightAnim) / sizeof(brightAnim[0]);


const uint32_t msRefresh = 10;
const int stepSpeed = 2;
const float bright_percent = .1; //percent in decimal form


static int animationStep = 0;


//Set Color of pixel at position px, including brightness level
uint32_t setAPA102Color(int px, uint8_t g, uint8_t b, uint8_t r, uint8_t brightness = 255) {
  uint32_t color = 0;

  brightness = map(brightness, 0, 255, 0, 31);  // only the bottom 5 bits available resulting in 32 discrete levels
  
  // due to big endianness in revers order 
  color |= (brightness <<   0) | 0xE0;  //top 3 bits must be 111
  color |= (r          <<   8);
  color |= (g          <<  16);
  color |= (b          <<  24);

  if (0 <= px && px < NUMPIXELS) {
    return pxBuffer[px] = color;
  }

  return -1;
}

bool needRefresh = true;
volatile bool canRefresh = true;

void refreshDone() {
  canRefresh = true;
}


void setup()
{
  memset(anim, 0, sizeof(anim));
  memset(pxBuffer, 0xE0, NUMPIXELS*4);
  SPI.begin();
  SPI.setClockSpeed(8, MHZ);
  SPI.setDataMode(0);
  SPI.setBitOrder(MSBFIRST);
  
  Particle.function("led", addComet);
}

void loop()
{
  static uint32_t msDelay = 0;
  if (millis() - msDelay < msRefresh) return;
  msDelay = millis();
  
  static bool sparkleState = false;
  static int  sparklePixel = 0;
  
  if (animHead != animTail)
  {
    ANIMATION_DATA *a;
    for (int animationStep = animTail; animationStep != animHead; ) 
    {
      a = &anim[animationStep];
      if (a->len)
      {
        int px;

        //Draw the Comet
        for (int p = a->len - 1; p >= 0; p--)
        {
          px = constrain(a->pos - p, 0, NUMPIXELS + cometLen);

          if (p < 1) {
            setAPA102Color(px, green_for_pixel(px, 255), red_for_pixel(px, 255), blue_for_pixel(px, 255), brightAnim[p]*bright_percent);
          } else if (p < 3) {
            setAPA102Color(px, green_for_pixel(px, a->g) + ((green_for_pixel(px, 255)-green_for_pixel(px, a->g))*.5), red_for_pixel(px, a->r) + ((red_for_pixel(px, 255)-red_for_pixel(px, a->r))*.5), blue_for_pixel(px, a->b) + ((blue_for_pixel(px, 255)-blue_for_pixel(px, a->b))*.5), brightAnim[p]*bright_percent);
          } else if (p < 5) {
            setAPA102Color(px, green_for_pixel(px, a->g) + ((green_for_pixel(px, 255)-green_for_pixel(px, a->g))*.1), red_for_pixel(px, a->r) + ((red_for_pixel(px, 255)-red_for_pixel(px, a->r))*.1), blue_for_pixel(px, a->b) + ((blue_for_pixel(px, 255)-blue_for_pixel(px, a->b))*.1), brightAnim[p]*bright_percent);
          } else {
            setAPA102Color(px, green_for_pixel(px, a->g), red_for_pixel(px, a->r), blue_for_pixel(px, a->b), brightAnim[p]*bright_percent);
          }
          
        }

        //Erase the pixels behind comet as it moves forward  
        if (a->pos - a->len >= 0)
          for (int k = 0; k <= stepSpeed; k++)
            setAPA102Color(a->pos - k - a->len, 0, 0, 0, 0);

        //Make comet jump forward stepSpeed pixels at a time to make comet run seemingly faster
        if (a->pos < NUMPIXELS + cometLen)
          a->pos += stepSpeed;
        else
          a->len -= stepSpeed;

        if (a->len <= 0)
        {
          *a = { 0, 0, 0, 0, 0 };
          animTail++;
          animTail %= maxAnim;
        }
      }

      animationStep++;
      animationStep %= maxAnim;
    }
    needRefresh = true;
  }

  sparkleState = !sparkleState;  // flip state
  if (sparkleState)
  {
    sparklePixel = random(NUMPIXELS);
    setAPA102Color(sparklePixel, random(255), random(255), random(255), 50);
    needRefresh = true;
  }
  else
  {
    setAPA102Color(sparklePixel, 0, 0, 0, 0);
    needRefresh = true;
  }
    
  
  if (needRefresh && canRefresh) {
      canRefresh =
      needRefresh = false;
      SPI.transfer(pxBuffer, NULL, sizeof(pxBuffer), refreshDone);
  }

}

struct topic {
  char    title[16];
  uint8_t rgb[3];
};

const topic topicList[] = 
{ { "login"  , { 255, 255, 255 } }  // white
, { "idea"   , { 255,   0, 255 } }  // yellow
, { "comment", {   0, 255,   0 } }  // blue
, { "outcome", { 255,   0,   0 } }  // green
, { "project", { 255,   0,   0 } }  // green
, { "status" , { 100,   0, 255 } }  // orange
, { "step"   , {   0, 255,  75 } }  // purple
, { "vote"   , {   0,   0, 255 } }  // red
, { "view"   , { 105, 200, 255 } }  // pink
};

const int   topics = sizeof(topicList)/sizeof(topicList[0]);


//Add a comet to the strip whenever initated by cloud event
int addComet(String command)
{
  int retVal = -1;

  for (int i = 0; i < topics; i++)
  {
    if (strcmp((const char*)command, topicList[i].title) == 0)
    {
      anim[animHead] = { topicList[i].rgb[0], topicList[i].rgb[1], topicList[i].rgb[2], 0, cometLen };
      Particle.publish(command);
      animHead++;
      animHead %= maxAnim;
      retVal = i;
      break;
    }
  }
  return retVal;
}

A post was merged into an existing topic: When I connect chain of LED strips to power from both ends, do I need to make the power line disconnect in the middle?

Please don’t create duplicate posts…