Program crashes when button if pushed if i use more than 21 pixels

Hello guys so i was working on a project recently and i was putting in the finishing touches when i decided it would be fun to add a fire pattern to my project, now im using a custom pcb running off of an attiny85 and when i compiled the code it only uses 95% of the program storage space. i tested it on a strip of 16 pixels and everything worked fine, i was able to switch through all the patterns without any problems. i then decided to test it on another strip that was 23 pixels long, the strip i had worked on this code for in the very first place, it uploaded fine and everything but as soon as i pushed the button everything stopped, i turned it off and back on and same thing, i reuploaded the code several times and same thing. i decided to play with the number of pixels starting from 16 which i knew worked and going up by 1 pixel and testing to make sure the program runs fine, as soon as i hit 21 pixels i ran into the same error. Now when i tested the fire pattern i ran it on a 23 pixel strip and it ran fine by itself, tested it on a 30 pixel strip and it also ran fine, but if i try yo use a 23 pixel strip with the code that i can switch though patterns it simply stops responding, i dont see how this makes any sense. can someone please help me! :o :confused:

attaching a link to the code

Please post the code here to avoid the need to download it.

Program storage is not the problem; RAM usage might be.

OP's code

#include "FastLED.h"
 
#define DATA_PIN 0
#define MIC_PIN A1
#define DC_OFFSET 0
#define NOISE 50
#define SAMPLES 60
#define TOP       (NUM_LEDS +2) // Allow dot to go slightly off scale
#define PEAK_FALL 20
#define BUTTON_LEAD 4
#define NUMBER_OF_MODES 6
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
#define NUM_LEDS 21
CRGB leds[NUM_LEDS];
 
 
#define BRIGHTNESS 64
#define FRAMES_PER_SECOND 120
 
//The following constant is the delay between each pixel flash
const int interval = 25;
unsigned long previousMillis = millis();
int mode = 1; //Default mode is one.
 
byte
  peak      = 0,      // Used for falling dot
  dotCount  = 0,      // Frame counter for delaying dot-falling speed
  volCount  = 0;      // Frame counter for storing past volume data
 
int
  vol[SAMPLES],       // Collection of prior volume samples
  lvl       = 10,     // Current "dampened" audio level
  minLvlAvg = 0,      // For dynamic adjustment of graph low & high
  maxLvlAvg = 512;
 
bool buttonState = LOW;
bool lastButtonState = LOW;
bool gReverseDirection = false;
 
CRGBPalette16 gPal;
 
 
void setup(){
  pinMode(BUTTON_LEAD, INPUT_PULLUP);
  memset(vol,0,sizeof(int)*SAMPLES);//Thanks Neil!
  FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);
  gPal = CRGBPalette16( CRGB::Black, CRGB::Blue, CRGB::Aqua, CRGB::White);
}
 
uint8_t gHue = 0;
 
void loop() {
 
  switch (mode) {
    case 1:
      sinelon();
      break;
 
    case 2:
      vuscanner();
      break;
 
    case 3:
      confetti();
      break;
 
    case 4:
       juggle();
       break;
 
     case 5:
      Fire2012WithPalette();
      break;
 
    default:
      mode = 1;
      break;
  }
  FastLED.show();
  EVERY_N_MILLISECONDS( 20 ) { gHue++; }
}
 
//COOLING: How much does the air cool as it rises?
//Less cooling = taller flames. More cooling = shorter flames.
//Default 55, suggested range 20-100.
#define COOLING 55
 
//SPARKING: What chance (out of 255) is there that a new spark will be lit?
//Higher chance = more roaring fire. Lower chance = more flickery fire.
//Default 120, suggested range 50-200.
#define SPARKING 120
 
/* monitor button press */
bool buttonListener() {
  bool modeChanged = false;
 
  buttonState = digitalRead(BUTTON_LEAD);
 
  if (buttonState != lastButtonState) {
    if (buttonState == LOW) {
      mode++;
      modeChanged = true;
      delay(250);             // Debounce delay. I checked the best parameter and 250 was it.
    }
  }
  lastButtonState = buttonState;
 
  return modeChanged;
}
 
void sinelon() {
  if(buttonListener()) { return; }
  if(millis() - previousMillis >= interval) {
      previousMillis = millis();
  fadeToBlackBy(leds, NUM_LEDS, 20);
  int pos = beatsin16(13,0,NUM_LEDS);
  leds[pos] += CHSV(gHue, 255, 192);
}
}
 
void confetti() {
  if(buttonListener()) { return; }
  if(millis() - previousMillis >= interval) {
      previousMillis = millis();
  fadeToBlackBy(leds, NUM_LEDS, 10);
  int pos = random16(NUM_LEDS);
  leds[pos] += CHSV( gHue + random8(64), 200, 255);
}
}
 
void juggle() {
  if(buttonListener()) { return; }
  if(millis() - previousMillis >= interval) {
      previousMillis = millis();
  fadeToBlackBy(leds, NUM_LEDS, 20);
  byte dothue = 0;
  for(int i = 0; i < 8; i++) {
    leds[beatsin16(i+7,0,NUM_LEDS)] |= CHSV(dothue, 200, 255);
    dothue += 32;
  }
}
}
 
 
void vuscanner() {
  if(buttonListener()) { return; }
   uint8_t  i;
  uint16_t minLvl, maxLvl;
  int      n, height;
   
  n = analogRead(MIC_PIN);                                    // Raw reading from mic
  n = abs(n - 512 - DC_OFFSET);                               // Center on zero
 
  n = (n <= NOISE) ? 0 : (n - NOISE);                         // Remove noise/hum
  lvl = ((lvl * 7) + n) >> 3;                                 // "Dampened" reading (else looks twitchy)
 
  // Calculate bar height based on dynamic min/max levels (fixed point):
  height = TOP * (lvl - minLvlAvg) / (long)(maxLvlAvg - minLvlAvg);
 
  if (height < 0L)       height = 0;                          // Clip output
  else if (height > TOP) height = TOP;
  if (height > peak)     peak   = height;                     // Keep 'peak' dot at top
 
 
  // Color pixels based on rainbow gradient
  for (i=0; i<NUM_LEDS; i++) {
    if (i >= height)   leds[i].setRGB( 0, 0,0);
    //uncomment one of the following lines to set strip effect
    //else leds[i] = CHSV(map(i,0,NUM_LEDS-1,0,255), 255, 255);  //CHSV values set for rainbow
    else leds[i] = CHSV(gHue, 255, 255);                      //CHSV values set to fade rgb
  }
 
  // Draw peak dot  
  if (peak > 0 && peak <= NUM_LEDS-1) leds[peak] = CHSV(map(peak,0,NUM_LEDS-1,0,255), 255, 255);
 
// Every few frames, make the peak pixel drop by 1:
 
    if (++dotCount >= PEAK_FALL) {                            // fall rate
      if(peak > 0) peak--;
      dotCount = 0;
    }
 
  vol[volCount] = n;                                          // Save sample for dynamic leveling
  if (++volCount >= SAMPLES) volCount = 0;                    // Advance/rollover sample counter
 
  // Get volume range of prior frames
  minLvl = maxLvl = vol[0];
  for (i=1; i<SAMPLES; i++) {
    if (vol[i] < minLvl)      minLvl = vol[i];
    else if (vol[i] > maxLvl) maxLvl = vol[i];
  }
  // minLvl and maxLvl indicate the volume range over prior frames, used
  // for vertically scaling the output graph (so it looks interesting
  // regardless of volume level).  If they're too close together though
  // (e.g. at very low volume levels) the graph becomes super coarse
  // and 'jumpy'...so keep some minimum distance between them (this
  // also lets the graph go to zero when no sound is playing):
  if((maxLvl - minLvl) < TOP) maxLvl = minLvl + TOP;
  minLvlAvg = (minLvlAvg * 63 + minLvl) >> 6;                 // Dampen min/max levels
  maxLvlAvg = (maxLvlAvg * 63 + maxLvl) >> 6;                 // (fake rolling average)
}
 
void Fire2012WithPalette(){
  if(buttonListener()) { return; }
  if(millis() - previousMillis >= interval) {
      previousMillis = millis();
// Array of temperature readings at each simulation cell
  static byte heat[NUM_LEDS];
 
  // Step 1.  Cool down every cell a little
    for( int i = 0; i < NUM_LEDS; i++) {
      heat[i] = qsub8( heat[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--) {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
    }
   
    // Step 3.  Randomly ignite new 'sparks' of heat near the bottom
    if( random8() < SPARKING ) {
      int y = random8(7);
      heat[y] = qadd8( heat[y], random8(160,255) );
    }
 
    // Step 4.  Map from heat cells to LED colors
    for( int j = 0; j < NUM_LEDS; j++) {
      // Scale the heat value from 0-255 down to 0-240
      // for best results with color palettes.
      byte colorindex = scale8( heat[j], 240);
      CRGB color = ColorFromPalette( gPal, colorindex);
      int pixelnumber;
      if( gReverseDirection ) {
        pixelnumber = (NUM_LEDS-1) - j;
      } else {
        pixelnumber = j;
      }
      leds[pixelnumber] = color;
    }
}
}

I compiled for an Uno and the code uses 407 bytes RAM. You only have 512 bytes.

The IDE will warn you (when compiling for the Uno) that stability issues might occur when using around 1500 bytes or more of RAM); that is at about 75%.

You are using around 80%. Calls to functions and local variables in the functions in the libraries need to be added to that. You might be out of memory,

void setup(){
...
  memset(vol,0,sizeof(int)*SAMPLES);//Thanks Neil!

...is useless. Remove it.

unsigned long previousMillis = millis();

Because init has not yet been called when previousMillis is initialized this is equivalent...

unsigned long previousMillis = 0;

Because previousMillis is global this is equivalent...

unsigned long previousMillis;

So remove the memset and replace
unsigned long previousMillis = millis();

With

Unsigned long previousMillis;

And that should help it if its a RAM thing? And sorry i couldnt post the code just the link. Usually i get an error when i try to post code (exceeds maximum number of characters) so i didnt even try this time.

I doubt it. It must be fixed in loop() and the functions that are called in loop() and the functions that are called from those and so on.

It's not easy to determine if it's a low memory issue. If you have e.g. an Uno, run the code on there; if it works, you know.

And sometimes one must be realistic in what can be done with 512 bytes of RAM.

Well i tried that and i think i broke it. It wont do anything now when i upload it... and its not the board because i can run other programs on it just fine... im not sure what went wrong O.o

Ok so i removed the fire pattern readded the memset and set previousMillis = millis(); and everything works again. I dont understand why it wont work when i add the fire pattern...

Chrisrocks23:
And that should help it if its a RAM thing?

No.

But removing troublesome code is always worth doing.

Chrisrocks23:
And sorry i couldnt post the code just the link.

• Click Reply.

• Click Attachments and other options.

• Click Browse.

• Select the file(s).

• Click Open.

• Click Post.

Chrisrocks23:
I dont understand why it wont work when i add the fire pattern...

The straw that broke the camel's back...

void Fire2012WithPalette(){
...
  static byte heat[NUM_LEDS];

Lol the straw that broke the camels back. Love that. But i even removed one of the other patterns. The juggle one to try and make some room. And thats when the whole thing stopped working. Nothing would come up. I actually thought i fried the darn thing for a moment there. Gonna keep trying different things hopefully i can get it working. My little brother really wants that fire pattern lol.

For what it's worth I cannot see any other problems with the code (e.g. buffer overrun). I suspect @sterretje is correct.

It appears that is was a memory issue, i was able to run the program successfully on my uno without any issues, i then kept experimenting with it and found that if i only use the fire pattern and the vumeter pattern they can both run smoothly, but if i try to add a 3rd pattern to the list the program crashes as soon as i try to switch to vumeter. oh well im working on a board design with the atmege32u4 anyways, i guess ill save this pattern for then lol, thank you all for all of the help. I love being able to ask a question here and get help. Hope this thread helps anyone else having a similar issue.

Thank you for the follow-up.

How are you powering your project?

Hey sorry for the late reply. Im powering my project from a 3.7V lipo battery.

Chrisrocks23:
Hey sorry for the late reply. Im powering my project from a 3.7V lipo battery.

Well, try temporarily plugging it into something chunkier, and see if it stops crashing when you light up all the pixels.

It looks like the problem was running out of SRAM. FastLED really eats up SRAM which is why 23 was the max. at 24 it went over the 512 bytes available in an attiny85.