Like this?
I added msLastSparkle and msSparkleTime variables.
#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)
#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)
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, 255, 255, 255, 255, 255, 255, 255, 255
, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
, 255, 255, 255, 255, 255, 255, 255, 255, 245, 240
, 240, 240, 240, 235, 230, 230, 230, 230, 225, 220
, 220, 220, 220, 215, 210, 210, 210, 210, 205, 200
, 200, 200, 200, 200, 195, 195, 190, 190, 190, 190
, 190, 195, 180, 180, 180, 175, 170, 165, 160, 155
, 150, 145, 140, 135, 130, 125, 120, 115, 110, 105
, 100, 95, 90, 85, 80, 75, 70
};
const int cometLen = sizeof(brightAnim) / sizeof(brightAnim[0]);
const uint32_t msRefresh = 10;
const int stepSpeed = 5;
static int animationStep = 0;
const uint32_t msSparkleTime = 0;
uint32_t msLastSparkle = 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 = 31) {
uint32_t color = 0;
brightness = map(brightness, 0, 255, 0, 31); // only the bottom 5 bits available resuliting 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 - 1);
setAPA102Color(px, a->g, a->r, a->b, brightAnim[p]);
}
//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 + stepSpeed)
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;
}
if (millis() - msLastSparkle >= msSparkleTime) {
msLastSparkle = millis();
sparkleState = !sparkleState; // flip state
if (sparkleState)
{
sparklePixel = random(NUMPIXELS);
setAPA102Color(sparklePixel, random(255), random(255), random(255), 100);
}
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" , { 200, 200, 200 } } // 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;
}