Hi,
I'm having issues in optimising for the max SRam usage. Currently I'm hitting the ceiling and I've worked out what is causing the most usage but I don't know how to improve it. I'm using a nano so it has 2048 bytes of which I want to keep under 80% to reduce chance of runtime issues.
I've coupled together a few examples from FastLED libraries and modified a few to suit what I'm after. I'm using 4x 100 long strips all with individual LED arrays to individually address them.
I've noticed that when I reduce NUM_STRIPS it drops by 300bytes each increment. I would have thought this should only be 100bytes as it is only adding 100 more LEDs? Maybe a struct?
So this is taking up 1200bytes or 60% of my total, I would have though it would only take 400bytes? Is it possible to be able to reduce this by either moving to PROGMEM or recode so it's more optimised?
Note: I've commented out fire() as it was pushing it over.
Thanks for your help.
#include "FastLED.h"
#define LED_PIN0 5
#define LED_PIN1 6
#define LED_PIN2 7
#define LED_PIN3 8
const byte NUM_STRIPS = 4;
const byte NUM_LEDS = 100;
#define COLOR_ORDER GRB
#define CHIPSET WS2812B
#define BRIGHTNESS 100
#define GRAVITY -1 // Downward (negative) acceleration of gravity in m/s^2
#define h0 1.6 // Starting height, in meters, of the ball (strip length)
#define NUM_BALLS 8 // Number of bouncing balls you want (recommend < 7, but 20 is fun in its own way)
//BUTTON
const byte Button = 2;
byte ButtonActive = LOW;
byte ButtonHold;
byte ButtonLong = 0;
byte ButtonShort = 0;
//Bounce
float h[NUM_BALLS]; // An array of heights
float vImpact0 = sqrt(-2 * GRAVITY * h0); // Impact velocity of the ball when it hits the ground if "dropped" from the top of the strip
float vImpact[NUM_BALLS]; // As time goes on the impact velocity will change, so make an array to store those values
float tCycle[NUM_BALLS]; // The time since the last time the ball struck the ground
int pos[NUM_BALLS]; // The integer position of the dot on the strip (LED index)
long tLast[NUM_BALLS]; // The clock time of the last ground strike
float COR[NUM_BALLS];
//Fire
const byte COOLING = 70;
const byte SPARKING = 150;
bool gReverseDirection = false;
CRGB leds[NUM_STRIPS][NUM_LEDS];
struct colorstruct {
byte color;
byte sat;
byte val;
};
byte mode;
byte snakemode = 0;
unsigned long lastUpdate = 0; // for millis() when last update occoured
unsigned long lastUpdate2 = 0;
uint8_t gHue = 0; // rotating "base color" used by many of the patterns
void setup() {
Serial.begin(9600);
delay(2000); // sanity check delay - allows reprogramming if accidently blowing power w/leds
FastLED.addLeds<CHIPSET, LED_PIN0, COLOR_ORDER>(leds[0], NUM_LEDS);
FastLED.addLeds<CHIPSET, LED_PIN1, COLOR_ORDER>(leds[1], NUM_LEDS);
FastLED.addLeds<CHIPSET, LED_PIN2, COLOR_ORDER>(leds[2], NUM_LEDS);
FastLED.addLeds<CHIPSET, LED_PIN3, COLOR_ORDER>(leds[3], NUM_LEDS);
LEDS.setBrightness(BRIGHTNESS);
pinMode(Button, INPUT_PULLUP);
//Bounce
for (int i = 0; i < (NUM_BALLS); i++) { // Initialize variables
tLast[i] = millis();
h[i] = h0;
pos[i] = 0; // Balls start on the ground
vImpact[i] = vImpact0; // And "pop" up at vImpact0
tCycle[i] = 0;
COR[i] = 0.90 - float(i) / pow(NUM_BALLS, 2);
}
mode = 0;
}
void loop() {
static int animation = 0, lastReading;
int reading = digitalRead(Button);
if (lastReading == HIGH && reading == LOW) {
animation++; // change animation number
if (animation > 10) animation = 0; // wrap round if too big
FastLED.clear();
FastLED.show();
delay(50); // debounce delay
}
lastReading = reading; // save for next time
updateanimation(animation);
}
void updateanimation(int ani) {
switch (ani) {
case 0:
rainbow(30);
break;
case 1:
drip(random(200, 2000));
break;
case 2:
confetti(30, 300, 150, 200, 255); // Fade speed, New light Speed, Colour, Saturation, Brightness
break;
case 3:
lightning(random(10, 2000), 5, 0); //Frequency between, speed (number added each update), pixel update rate
break;
case 4:
snake(30);
break;
case 5:
confetti(0, 200, 50, 150, 255);
break;
case 6:
bounce();
break;
case 7:
rainbow(30);
addGlitter(80);
break;
case 8:
juggle();
break;
case 9:
bpm();
break;
case 10:
//fire(20);
break;
}
}
void rainbow(int wait) {
FastLED.show();
if (millis() - lastUpdate >= wait) {
gHue++;
lastUpdate = millis();
}
for (int i = 0; i < NUM_STRIPS; i++) {
fill_rainbow(leds[i], NUM_LEDS, gHue, 7);
}
}
void drip(int freq) {
static unsigned long lastUpdate;
static int dripmode;
int waitstart = 700;
static int wait;
int maxspeed = 20;
static byte nextstep;
if (nextstep < 0) {
wait = waitstart;
dripmode = random(0, 4);
nextstep = NUM_LEDS - 1;
}
int step = nextstep;
static int locked;
static int freqnext;
if (locked == 0) {
switch (dripmode) {
case 0:
if (millis() - lastUpdate >= wait) {
leds[0][step] = CHSV(uint8_t(150), 155, 150);
wait = (max(wait - sqrt(wait), maxspeed)) / 1.8;
lastUpdate = millis();
FastLED.show();
nextstep -= 1;
if (nextstep < 0) {
leds[0][0] = CRGB::Black;
FastLED.show();
}
leds[0][step] = CRGB::Black;
}
break;
case 1:
if (millis() - lastUpdate >= wait) {
leds[1][step] = CHSV(uint8_t(150), 155, 150);
wait = (max(wait - sqrt(wait), maxspeed)) / 1.8;
lastUpdate = millis();
FastLED.show();
nextstep -= 1;
if (nextstep < 0) {
leds[1][0] = CRGB::Black;
FastLED.show();
}
leds[1][step] = CRGB::Black;
}
break;
case 2:
if (millis() - lastUpdate >= wait) {
leds[2][step] = CHSV(uint8_t(150), 155, 150);
wait = (max(wait - sqrt(wait), maxspeed)) / 1.8;
lastUpdate = millis();
FastLED.show();
nextstep -= 1;
if (nextstep < 0) {
leds[2][0] = CRGB::Black;
FastLED.show();
}
leds[2][step] = CRGB::Black;
}
break;
case 3:
if (millis() - lastUpdate >= wait) {
leds[3][step] = CHSV(uint8_t(150), 155, 150);
wait = (max(wait - sqrt(wait), maxspeed)) / 1.8;
lastUpdate = millis();
FastLED.show();
nextstep -= 1;
if (nextstep < 0) {
leds[3][0] = CRGB::Black;
FastLED.show();
}
leds[3][step] = CRGB::Black;
}
break;
}
}
if (nextstep <= 0) {
locked = 1;
freqnext = freq;
}
if (locked == 1) {
if (millis() - lastUpdate >= freqnext) {
locked = 0;
}
}
}
void confetti(int wait, int wait2, int color, int sat, int val) {
if (millis() - lastUpdate >= wait) {
fadeToBlackBy(leds[0], NUM_LEDS, 5);
fadeToBlackBy(leds[1], NUM_LEDS, 5);
fadeToBlackBy(leds[2], NUM_LEDS, 5);
fadeToBlackBy(leds[3], NUM_LEDS, 5);
lastUpdate = millis();
}
if (millis() - lastUpdate2 >= wait2) {
int pos0 = random16(NUM_LEDS);
int pos1 = random16(NUM_LEDS);
int pos2 = random16(NUM_LEDS);
int pos3 = random16(NUM_LEDS);
leds[0][pos0] += CHSV(color, sat, val);
leds[1][pos1] += CHSV(color, sat, val);
leds[2][pos2] += CHSV(color, sat, val);
leds[3][pos3] += CHSV(color, sat, val);
lastUpdate2 = millis();
}
FastLED.show();
}
void lightning(int freq, int speed, int wait) {
static int lightmode;
int colormode;
static colorstruct color;
static int nextstep;
static int locked;
static int freqnext;
if (nextstep <= 0) {
FastLED.clear();
FastLED.show();
lightmode = random(0, 4);
colormode = random8(0, 100);
nextstep = NUM_LEDS - 1;
if (colormode < 55) {
color = { 202, 0, 255 }; //White
} else if (colormode < 70) {
color = { 160, 150, 255 }; //Blue
} else if (colormode < 85) {
color = { 64, 150, 255 }; //Yellow
} else if (colormode < 100) {
color = { 210, 150, 255 }; //Violet
}
}
if (locked == 0) {
switch (lightmode) {
case 0:
if (millis() - lastUpdate >= wait) {
for (int i = 0; i < speed; i++) {
leds[0][nextstep] = CHSV(color.color, color.sat, color.val);
nextstep -= 1;
}
lastUpdate = millis();
FastLED.show();
}
break;
case 1:
if (millis() - lastUpdate >= wait) {
for (int i = 0; i < speed; i++) {
leds[1][nextstep] = CHSV(color.color, color.sat, color.val);
nextstep -= 1;
}
lastUpdate = millis();
FastLED.show();
}
break;
case 2:
if (millis() - lastUpdate >= wait) {
for (int i = 0; i < speed; i++) {
leds[2][nextstep] = CHSV(color.color, color.sat, color.val);
nextstep -= 1;
}
lastUpdate = millis();
FastLED.show();
}
break;
case 3:
if (millis() - lastUpdate >= wait) {
for (int i = 0; i < speed; i++) {
leds[3][nextstep] = CHSV(color.color, color.sat, color.val);
nextstep -= 1;
}
lastUpdate = millis();
FastLED.show();
}
break;
}
}
if (nextstep <= 0) {
locked = 1;
freqnext = freq;
}
if (locked == 1) {
if (millis() - lastUpdate >= freqnext) {
locked = 0;
}
}
}
void snakedirection(int wait, int fade, int direction, int stripno) {
static int nextstep;
switch (direction) {
case 0:
if (millis() - lastUpdate >= wait) {
leds[stripno][nextstep] = CHSV(uint8_t(110), 255, 250);
for (int i = 0; i < NUM_STRIPS; i++) {
fadeToBlackBy(leds[i], NUM_LEDS, fade);
}
FastLED.show();
nextstep -= 1;
lastUpdate = millis();
if (nextstep < 0) {
direction = 1;
nextstep = 0;
snakemode += 1;
if (snakemode > 23) {
snakemode = 0;
}
}
}
break;
case 1:
if (millis() - lastUpdate >= wait) {
leds[stripno][nextstep] = CHSV(uint8_t(110), 255, 250);
for (int i = 0; i < NUM_STRIPS; i++) {
fadeToBlackBy(leds[i], NUM_LEDS, fade);
}
FastLED.show();
nextstep += 1;
lastUpdate = millis();
if (nextstep >= NUM_LEDS) {
direction = 0;
nextstep = 99;
snakemode += 1;
if (snakemode > 23) {
snakemode = 0;
}
}
}
}
}
void snake(int speed) {
//go down one strip then up the next
switch (snakemode) {
case 0:
snakedirection(speed, 110, 1, 0);
break;
case 1:
snakedirection(speed, 95, 0, 1);
break;
case 2:
snakedirection(speed, 80, 1, 2);
break;
case 3:
snakedirection(speed, 70, 0, 3);
break;
case 4:
snakedirection(speed, 60, 1, 2);
break;
case 5:
snakedirection(speed, 50, 0, 1);
break;
case 6:
snakedirection(speed, 40, 1, 0);
break;
case 7:
snakedirection(speed, 32, 0, 1);
break;
case 8:
snakedirection(speed, 25, 1, 2);
break;
case 9:
snakedirection(speed, 20, 0, 3);
break;
case 10:
snakedirection(speed, 15, 1, 2);
break;
case 11:
snakedirection(speed, 10, 0, 1);
break;
case 12:
snakedirection(speed, 10, 1, 0);
break;
case 13:
snakedirection(speed, 15, 0, 1);
break;
case 14:
snakedirection(speed, 20, 1, 2);
break;
case 15:
snakedirection(speed, 25, 0, 3);
break;
case 16:
snakedirection(speed, 32, 1, 2);
break;
case 17:
snakedirection(speed, 40, 0, 1);
break;
case 18:
snakedirection(speed, 50, 1, 0);
break;
case 19:
snakedirection(speed, 60, 0, 1);
break;
case 20:
snakedirection(speed, 70, 1, 2);
break;
case 21:
snakedirection(speed, 80, 0, 3);
break;
case 22:
snakedirection(speed, 95, 1, 2);
break;
case 23:
snakedirection(speed, 110, 0, 1);
break;
}
}
void bounce() {
for (int i = 0; i < NUM_BALLS; i++) {
tCycle[i] = millis() - tLast[i]; // Calculate the time since the last time the ball was on the ground
// A little kinematics equation calculates positon as a function of time, acceleration (gravity) and intial velocity
h[i] = 0.5 * GRAVITY * pow(tCycle[i] / 1000, 2.0) + vImpact[i] * tCycle[i] / 1000;
if (h[i] < 0) {
h[i] = 0; // If the ball crossed the threshold of the "ground," put it back on the ground
vImpact[i] = COR[i] * vImpact[i]; // and recalculate its new upward velocity as it's old velocity * COR
tLast[i] = millis();
if (vImpact[i] < 0.01) vImpact[i] = vImpact0; // If the ball is barely moving, "pop" it back up at vImpact0
}
pos[i] = round(h[i] * (NUM_LEDS - 1) / h0); // Map "h" to a "pos" integer index position on the LED strip
}
leds[0][pos[0]] = CHSV(uint8_t(0 * 40), 255, 255);
leds[0][pos[4]] = CHSV(uint8_t(4 * 40), 255, 255);
leds[1][pos[1]] = CHSV(uint8_t(1 * 40), 255, 255);
leds[1][pos[5]] = CHSV(uint8_t(5 * 40), 255, 255);
leds[2][pos[2]] = CHSV(uint8_t(2 * 40), 255, 255);
leds[2][pos[6]] = CHSV(uint8_t(6 * 40), 255, 255);
leds[3][pos[3]] = CHSV(uint8_t(3 * 40), 255, 255);
leds[3][pos[7]] = CHSV(uint8_t(7 * 40), 255, 255);
FastLED.show();
for (int x = 0; x < NUM_STRIPS; x++) {
for (int i = 0; i < NUM_BALLS; i++) {
leds[x][pos[i]] = CRGB::Black;
}
}
}
void addGlitter(fract8 chanceOfGlitter) {
if (random8() < chanceOfGlitter) {
for (int i = 0; i < NUM_STRIPS; i++) {
leds[i][random16(NUM_LEDS)] += CRGB::White;
}
}
FastLED.show();
}
void juggle() {
// eight colored dots, weaving in and out of sync with each other
for (int i = 0; i < NUM_STRIPS; i++) {
fadeToBlackBy(leds[i], NUM_LEDS, 20);
}
byte dothue = 0;
for (int i = 0; i < 8; i++) {
for (int a = 0; a < NUM_STRIPS; a++) {
leds[a][beatsin16(i + 7, 0, NUM_LEDS - 1)] |= CHSV(dothue, 200, 255);
dothue += 32;
}
}
FastLED.show();
}
void bpm() {
// colored stripes pulsing at a defined Beats-Per-Minute (BPM)
uint8_t BeatsPerMinute = 62;
CRGBPalette16 palette = PartyColors_p;
uint8_t beat = beatsin8(BeatsPerMinute, 64, 255);
for (int i = 0; i < NUM_LEDS; i++) { //9948
for (int a = 0; a < NUM_STRIPS; a++) {
leds[a][i] = ColorFromPalette(palette, gHue + (i * a), beat - gHue + (i * 10));
}
}
FastLED.show();
}
void fire(int wait) {
// Array of temperature readings at each simulation cell
static byte heat0[NUM_LEDS];
static byte heat1[NUM_LEDS];
static byte heat2[NUM_LEDS];
static byte heat3[NUM_LEDS];
//static unsigned long lastUpdate;
if (millis() - lastUpdate >= wait) {
// Step 1. Cool down every cell a little
for (int i = 0; i < NUM_LEDS; i++) {
heat0[i] = qsub8(heat0[i], random8(0, ((COOLING * 10) / NUM_LEDS) + 2));
heat1[i] = qsub8(heat1[i], random8(0, ((COOLING * 10) / NUM_LEDS) + 2));
heat2[i] = qsub8(heat2[i], random8(0, ((COOLING * 10) / NUM_LEDS) + 2));
heat3[i] = qsub8(heat3[i], random8(0, ((COOLING * 10) / NUM_LEDS) + 2));
}
// Step 2. Heat from each cell drifts 'up' and diffuses a little
for (int k = NUM_LEDS - 1; k >= 2; k--) {
heat0[k] = (heat0[k - 1] + heat0[k - 2] + heat0[k - 2]) / 3;
heat1[k] = (heat1[k - 1] + heat1[k - 2] + heat1[k - 2]) / 3;
heat2[k] = (heat2[k - 1] + heat2[k - 2] + heat2[k - 2]) / 3;
heat3[k] = (heat3[k - 1] + heat3[k - 2] + heat3[k - 2]) / 3;
}
// Step 3. Randomly ignite new 'sparks' of heat near the bottom
if (random8() < SPARKING) {
int y = random8(7);
heat0[y] = qadd8(heat0[y], random8(160, 255));
heat1[y] = qadd8(heat1[y], random8(160, 255));
heat2[y] = qadd8(heat2[y], random8(160, 255));
heat3[y] = qadd8(heat3[y], random8(160, 255));
}
// Step 4. Map from heat cells to LED colors
for (int j = 0; j < NUM_LEDS; j++) {
CRGB color0 = HeatColor(heat0[j]);
CRGB color1 = HeatColor(heat1[j]);
CRGB color2 = HeatColor(heat2[j]);
CRGB color3 = HeatColor(heat3[j]);
int pixelnumber;
if (gReverseDirection) {
pixelnumber = (NUM_LEDS - 1) - j;
} else {
pixelnumber = j;
}
leds[0][pixelnumber] = color0;
leds[1][pixelnumber] = color1;
leds[2][pixelnumber] = color2;
leds[3][pixelnumber] = color3;
}
lastUpdate = millis();
}
FastLED.show();
}