Help optimizing global variables - not enough memory

Hi all, I'm unable to compile my code due to a lack of SRAM on my Uno - my global variables use 117% of dynamic memory, and the IDE says that I'm 358 bytes over.

I'm a beginner when it comes to different data types and optimizing memory usage, so any immediate changes I can make to my code would be appreciated.

#define LIN_OUT 1 // use the log output function
#define FFT_N 256 // set to 256 point fft
#define DC_OFFSET 511

#include <FFT.h>  //FFT library
#include <FastLED.h>  //FastLED library

#define LED_DT 6  //LED data pin
#define COLOR_ORDER GRB //WS2812b specific
#define LED_TYPE WS2812B
#define NUM_LEDS 288  //Number of LEDs in project
#define COL_HEIGHT 9  //height of column

int adc_val;

struct CRGB leds[NUM_LEDS];

void setup() {
  Serial.begin(9600); // use the serial port
  delay(1000);  //soft startup to ease flow of 

  LEDS.addLeds<LED_TYPE, LED_DT, COLOR_ORDER>(leds, NUM_LEDS);

  TIMSK0 = 0; // turn off timer0 for lower jitter - delay() and millis() killed
  ADCSRA = 0xe5; // set the adc to free running mode
  ADMUX = 0x40; // use adc0
  DIDR0 = 0x01; // turn off the digital input for adc0
}

void loop() {
  getFFT();
  fftDisplay();
  FastLED.show();
}

void getSound() {
    while (1) { // reduces jitter
      cli();  // UDRE interrupt slows this way down on arduino1.0
      for (int i = 0 ; i < FFT_N * 2 ; i += 2) { // save 256 samples
        while (!(ADCSRA & 0x10)); // wait for adc to be ready
        ADCSRA = 0xf5; // restart adc
        byte m = ADCL; // fetch adc data
        byte j = ADCH;
        int k = (j << 8) | m; // form into an int
        k -= DC_OFFSET;
        adc_val = k<<6;
        fft_input[i] = adc_val; // put real data into even bins
        fft_input[i + 1] = 0; // set odd bins to 0
      }
      sei(); // turn interrupts back on
   }
}

void getFFT() {
   // window data, then reorder, then run, then take output
   getSound(); //High speed sound sampling
   fft_window(); // window the data for better frequency response
   fft_reorder(); // reorder the data before doing the fft
   fft_run(); // process the data in the fft
   fft_mag_lin(); // take the output of the fft
}

void fftDisplay() {
  int i, j, colHeight, startIndex;
  for(i = 1; i < 33; i+=2)  //traverse odd index bins of FFT output starting from 2nd bin (index 1), 'i' also represents col #
  {
    colHeight = (unsigned char)map(fft_lin_out[i], 0, 255, 0, 9);  //map FFT output from 0-255 (value of unsigned char) to 0-9
    startIndex = 9 * (i-1); //starting LED index of ith column
    for(j = startIndex; j < (startIndex + COL_HEIGHT); j++) //traverse LEDs in ith column (for odd # columns)
    {
      if(j < (startIndex + colHeight))  //turn on colHeight specified # of LEDs in ith column
      {
        leds[j] = CRGB::White;
      } 
      else  //turn off all other LEDs in ith column
      {
        leds[j] = CRGB::Black;
      }
    }
  }
  for(i = 2; i < 33; i += 2)  //traverse even index bins of FFT output starting from 3rd bin (index 2), 'i' also represents col #
  {
    colHeight = (unsigned char)map(fft_lin_out[i], 0, 255, 0, 9);
    startIndex = 9 * i - 1;
    for(j = startIndex; j > (startIndex - COL_HEIGHT); j--)
    {
      if(j > (startIndex - colHeight))
      {
        leds[j] = CRGB::White;
      }
      else
      {
        leds[j] = CRGB::Black;
      }
    }
  }
    
}

You are trying to run too many LEDs. Grab a different flavor of Arduino/compatible that has more RAM.

-jim lee

jimLee:
You are trying to run too many LEDs. Grab a different flavor of Arduino/compatible that has more RAM.

-jim lee

I'll order a Mega as a last resort, but I assumed it had something to do with my code. With just the FastLED library installed and some basic code, I was able to light up all 288 LEDs as a test (and since each LED takes 3 bytes, which means 864 bytes total). Isn't there anything I can do to adjust my code such that I can free up some SRAM?

The problem is that you are using a double buffer,

was able to light up all 288 LEDs as a test (and since each LED takes 3 bytes, which means 864 bytes total).

exactly so the rest is taken up at the input side,

void getFFT() {
   // window data, then reorder, then run, then take output
   getSound(); //High speed sound sampling
   fft_window(); // window the data for better frequency response
   fft_reorder(); // reorder the data before doing the fft
   fft_run(); // process the data in the fft
   fft_mag_lin(); // take the output of the fft
}

will need to see how these function use their variables, and if there is a buffer that you can re-use as an output buffer then you should be fine. The basic process is : Input => Processing => Output => Input => etc. so any variables that are no longer required to hold data during (or straight after processing) basically, anything that is free during this :

void fftDisplay() {
  int i, j, colHeight, startIndex;
  for(i = 1; i < 33; i+=2)  //traverse odd index bins of FFT output starting from 2nd bin (index 1), 'i' also represents col #
  {
    colHeight = (unsigned char)map(fft_lin_out[i], 0, 255, 0, 9);  //map FFT output from 0-255 (value of unsigned char) to 0-9
    startIndex = 9 * (i-1); //starting LED index of ith column
    for(j = startIndex; j < (startIndex + COL_HEIGHT); j++) //traverse LEDs in ith column (for odd # columns)
    {
      if(j < (startIndex + colHeight))  //turn on colHeight specified # of LEDs in ith column
      {
        leds[j] = CRGB::White;
      }
      else  //turn off all other LEDs in ith column
      {
        leds[j] = CRGB::Black;
      }
    }
  }
  for(i = 2; i < 33; i += 2)  //traverse even index bins of FFT output starting from 3rd bin (index 2), 'i' also represents col #
  {
    colHeight = (unsigned char)map(fft_lin_out[i], 0, 255, 0, 9);
    startIndex = 9 * i - 1;
    for(j = startIndex; j > (startIndex - COL_HEIGHT); j--)
    {
      if(j > (startIndex - colHeight))
      {
        leds[j] = CRGB::White;
      }
      else
      {
        leds[j] = CRGB::Black;
      }
    }
  }
   
}

Can be used. You will need to be careful of course but really for this final bit of processing you only needfft_lin_out[i] the fft output buffer, and i figure the input buffer can be temporarily used to store the LED data that need to be in the output buffer. With Neopixel.h it is quite straight forward to retrieve and/or modify the pointer to the buffer, i am quite sure that it is not hugely different using FastLED. You pass the pointer to the buffer when you declare the object, so if you know what area you are going to use, you can pass that pointer, and all you need to do is write to the LED array differently. (probably using memcpy or something, there are a few ways to do this.)

Deva_Rishi:
The problem is that you are using a double buffer, exactly so the rest is taken up at the input side,

void getFFT() {

// window data, then reorder, then run, then take output
  getSound(); //High speed sound sampling
  fft_window(); // window the data for better frequency response
  fft_reorder(); // reorder the data before doing the fft
  fft_run(); // process the data in the fft
  fft_mag_lin(); // take the output of the fft
}


will need to see how these function use their variables, and if there is a buffer that you can re-use as an output buffer then you should be fine. The basic process is : Input => Processing => Output => Input => etc. so any variables that are no longer required to hold data during (or straight after processing) basically, anything that is free during this :

void fftDisplay() {
  int i, j, colHeight, startIndex;
  for(i = 1; i < 33; i+=2)  //traverse odd index bins of FFT output starting from 2nd bin (index 1), 'i' also represents col #
  {
    colHeight = (unsigned char)map(fft_lin_out[i], 0, 255, 0, 9);  //map FFT output from 0-255 (value of unsigned char) to 0-9
    startIndex = 9 * (i-1); //starting LED index of ith column
    for(j = startIndex; j < (startIndex + COL_HEIGHT); j++) //traverse LEDs in ith column (for odd # columns)
    {
      if(j < (startIndex + colHeight))  //turn on colHeight specified # of LEDs in ith column
      {
        leds[j] = CRGB::White;
      }
      else  //turn off all other LEDs in ith column
      {
        leds[j] = CRGB::Black;
      }
    }
  }
  for(i = 2; i < 33; i += 2)  //traverse even index bins of FFT output starting from 3rd bin (index 2), 'i' also represents col #
  {
    colHeight = (unsigned char)map(fft_lin_out[i], 0, 255, 0, 9);
    startIndex = 9 * i - 1;
    for(j = startIndex; j > (startIndex - COL_HEIGHT); j--)
    {
      if(j > (startIndex - colHeight))
      {
        leds[j] = CRGB::White;
      }
      else
      {
        leds[j] = CRGB::Black;
      }
    }
  }
 
}


Can be used. You will need to be careful of course but really for this final bit of processing you only need`fft_lin_out[i]` the fft output buffer, and i figure the input buffer can be temporarily used to store the LED data that need to be in the output buffer. With Neopixel.h it is quite straight forward to retrieve and/or modify the pointer to the buffer, i am quite sure that it is not hugely different using FastLED. You pass the pointer to the buffer when you declare the object, so if you know what area you are going to use, you can pass that pointer, and all you need to do is write to the LED array differently. (probably using memcpy or something, there are a few ways to do this.)

Thanks for the detailed response. Will do research into all of the points that you brought up