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;
}