I'm afraid I have to post a lengthy bit of code, because I can't pinpoint what's causing my issues – in short, it appears repeatedly adding float position + float speed
in a struct ends up borking the whole thing.
The purpose of the code is to animate at most MAX_PARTICLES
particles ("pixels") traveling at random speeds across the LED stripe.
The array named particles
stores the current list of particles being managed. Each particle in the array is stored as a struct, as defined at the beginning of the code. Particles are added to the array using function addParticle()
at the very end of the code. Particles which reach the end of the stripe are killed in the second part of the loop()
function (after FastLED.show()
).
Here's the code:
#define FASTLED_INTERNAL // Eliminates a compiler warning
#include <FastLED.h>
#include <colorutils.h>
#define NUM_LEDS 149
#define DATA_PIN 6
#define MAX_PARTICLES 5 // This uses up the memory, mostly
CRGB leds[NUM_LEDS];
struct particle {
float position;
CRGB color;
float speed;
int id;
};
typedef struct particle Particle;
Particle particles[MAX_PARTICLES];
int numParticles = 0;
int maxId = 0;
// Gramma correction tables (Defalt Gamma = 2.8)
const uint8_t PROGMEM gammaR[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5,
5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9,
9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 14, 14, 14,
15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33,
33, 34, 35, 36, 36, 37, 38, 39, 40, 40, 41, 42, 43, 44, 45, 46,
46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
62, 63, 65, 66, 67, 68, 69, 70, 71, 73, 74, 75, 76, 78, 79, 80,
81, 83, 84, 85, 87, 88, 89, 91, 92, 94, 95, 97, 98, 99,101,102,
104,105,107,109,110,112,113,115,116,118,120,121,123,125,127,128,
130,132,134,135,137,139,141,143,145,146,148,150,152,154,156,158,
160,162,164,166,168,170,172,174,177,179,181,183,185,187,190,192,
194,196,199,201,203,206,208,210,213,215,218,220,223,225,227,230 };
const uint8_t PROGMEM gammaG[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };
const uint8_t PROGMEM gammaB[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4,
4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 8,
8, 8, 8, 9, 9, 9, 10, 10, 10, 10, 11, 11, 12, 12, 12, 13,
13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 19,
20, 20, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 27, 27, 28, 28,
29, 30, 30, 31, 32, 32, 33, 34, 34, 35, 36, 37, 37, 38, 39, 40,
40, 41, 42, 43, 44, 44, 45, 46, 47, 48, 49, 50, 51, 51, 52, 53,
54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 69, 70,
71, 72, 73, 74, 75, 77, 78, 79, 80, 81, 83, 84, 85, 86, 88, 89,
90, 92, 93, 94, 96, 97, 98,100,101,103,104,106,107,109,110,112,
113,115,116,118,119,121,122,124,126,127,129,131,132,134,136,137,
139,141,143,144,146,148,150,152,153,155,157,159,161,163,165,167,
169,171,173,175,177,179,181,183,185,187,189,191,193,196,198,200 };
void setup() {
FastLED.addLeds<WS2812B, DATA_PIN, BGR>(leds, NUM_LEDS);
randomSeed(analogRead(0));
Serial.begin(115200);
}
void loop() {
if (numParticles < MAX_PARTICLES && random(10)>8) {
addParticle();
}
for (int i=0; i<numParticles; i++) {
// Antialiasing
int basePosition = floor(particles[i].position);
byte percentOnePos = round(255 * (particles[i].position-basePosition));
byte percentBasePos = 255 - percentOnePos;
CRGB color0 = particles[i].color % percentOnePos;
CRGB color1 = particles[i].color % percentBasePos;
leds[basePosition] += color0;
leds[basePosition+1] += color1;
}
// Apply gamma correction
for (int i=0; i<NUM_LEDS; i++) {
//leds[i] = applyGamma_video(leds[i], 2.2);
// *** applyGamma_video(2.2) pros and cons:
// PROs: smoother color transitions
// CONs: much slower; much worse color calibration
// TODO: pre-render custom, color calibrated gamma tables
leds[i].red = pgm_read_byte(&gammaR[leds[i].red]);
leds[i].green = pgm_read_byte(&gammaG[leds[i].green]);
leds[i].blue = pgm_read_byte(&gammaB[leds[i].blue]);
}
FastLED.show();
for (int i=0; i<numParticles; i++) {
int basePosition = floor(particles[i].position);
leds[basePosition] = CRGB::Black;
leds[basePosition+1] = CRGB::Black;
particles[i].position += particles[i].speed;
if (particles[i].position > (NUM_LEDS+1)) { // We want to render it fading out
for (int j = i; j<numParticles-1; j++) {
particles[j] = particles[j+1];
}
numParticles--;
i--; // We want the for() cycle to process the particle we shifted down
}
}
debugParticles();
}
void debugParticles()
{
for (int i = 0; i<MAX_PARTICLES; i++) {
if (i>0) {
Serial.print(", ");
}
Serial.print(i);
Serial.print(",");
Serial.print(particles[i].id);
Serial.print(",");
Serial.print(particles[i].position);
}
Serial.println("");
}
void addParticle()
{
particles[numParticles].color = CHSV(random8(),255,150);
particles[numParticles].position = 0;
particles[numParticles].speed = (0.0+random(10))/100+0.01;
particles[numParticles].id = maxId++;
numParticles++;
}
For debugging purposes, each unique particle ever created has a unique ID. The debugging output contains the following fields:
- [0] the array index for the first entry in the array (always 0)
- [1] the unique ID of the particle stored in the first record of the array
- [2] the position of the particle stored in the first record of the array
- [3] the array index for the second entry in the array (always 1)
- [4] the unique ID of the particle stored in the second record of the array
- [5] the position of the particle stored in the second record of the array
- (and so on – three fields for each record in the particles array)
Finally, here's an interesting excerpt from the output, after ~1,500 records:
0 0 30.36 1 1 30.28 2 2 147.70 3 3 59.04 4 4 73.15
0 0 30.38 1 1 30.30 2 2 147.80 3 3 59.08 4 4 73.20
0 0 30.40 1 1 30.32 2 2 147.90 3 3 59.12 4 4 73.25
0 0 30.42 1 1 30.34 2 2 148.00 3 3 59.16 4 4 73.30
0 0 30.44 1 1 30.36 2 2 148.10 3 3 59.20 4 4 73.35
0 0 8.00 1 1 30.38 2 2 148.20 3 3 59.24 4 4 73.40
0 0 8.00 1 1 30.40 2 2 148.30 3 3 59.28 4 4 73.45
0 0 8.00 1 1 30.42 2 2 148.40 3 3 59.32 4 4 73.50
0 0 8.00 1 1 30.44 2 2 148.50 3 3 59.36 4 4 73.55
0 0 8.00 1 1 30.46 2 2 148.60 3 3 59.40 4 4 73.60
0 0 8.00 1 1 30.48 2 2 148.70 3 3 59.44 4 4 73.65
0 0 8.00 1 1 30.50 2 2 148.80 3 3 59.48 4 4 73.70
0 0 8.00 1 1 30.52 2 2 148.90 3 3 59.52 4 4 73.75
0 0 8.00 1 1 30.54 2 2 149.00 3 3 59.56 4 4 73.80
0 0 8.00 1 1 30.56 2 2 149.10 3 3 59.60 4 4 73.85
0 1 0.00 1 2 149.20 2 3 59.64 3 4 73.90 4 4 73.85
0 1 0.00 1 2 149.30 2 3 59.68 3 4 73.95 4 4 73.85
0 1 0.00 1 2 149.40 2 3 59.72 3 4 74.00 4 4 73.85
Notice how in all but the last three lines, the array index and the particle ID are still in sync (0=>0, 1=>1, 2=>2, 3=>3, 4=>4), which means no particles have died yet (new particles would have larger IDs). I'm trying to say that no particle re-assignment magic has happened yet – for now, we're just running the boring code which moves particles along the strip.
And yet, on the sixth line in the output, the first particle's position becomes mangled – notice how on the first five lines the first particle's position has steadily increased by 0.02 (presumably that particle's speed) up to 30.44, whereas on the sixth line it inexplicably becomes 8.00 (other times I experimented, it becomes 32.00). Not only that, but that 8.00 (the particle's position) never increases again, and three lines from the bottom the code also decides to kill the first particle altogether (notice that the second column becomes 1, which means the first record in the particles array now stores the particle with unique ID 1, instead of unique ID 0).
I have no idea what I'm doing wrong – I've been experimenting with this over and over, killing off all the bugs and warnings I could find, and still no luck.
leds
(which is variableparticles
). – KIIV Jan 12 '18 at 8:49leds[basePosition+1] = ...
and if the base position is 148, it means you are writing at the position 149. That means AFTER the space allocated for theCRGB leds[149]
as you have 149 elements starting with index 0 and ending by index 148 – KIIV Jan 12 '18 at 8:57