Other options for variables that never change?

Hi, I'm starting to run into SRAM issues as in its getting full. :astonished: So I've been going through my sketch trying to do what I can to free memory up. The sketch includes a large GUI which is the reason for my problems, lots of images, text, etc...

I have the following variables declared as global which are using 556 bytes of memory, that's enough to possible solve my issues. These variables are images and text that never change, is there a way to store them on the SD card or flash memory if it doesn't take to much space, I'm also getting short on that as well. I've been reading about this and it seems it can be done but everything I see doesn't really apply to my project and is over my head.

What would be the best method with these types of variables? They are images that change through out the GUI, the images are located on the SD card and I'm using the SDFat library.

// neutral lights mode buttons for lights screen
char *lightMode[] = {
  "schHS.raw","schMS.raw","schLS.raw","schM.raw"};

// large power buttons for the power screen and the feeding configuration screen, off and on
char *pwrLightIcon[] = {
  "pwLiteLF.raw","pwLiteLO.raw"};
char *pwrFilterIcon[] = {
  "pwFiltLF.raw","pwFiltLO.raw"};
char *pwrCircIcon[] = {
  "pwPumpLF.raw","pwPumpLO.raw"};
char *pwrHeatIcon[] = {
  "pwHeatLF.raw","pwHeatLO.raw"};
char *pwrCO2Icon[] = {
  "pwCo2LF.raw","pwCo2LO.raw"};
char *pwrAux1Icon[] = {
  "pwAux1LF.raw","pwAux1LO.raw"};
char *pwrAux2Icon[] = {
  "pwAux2LF.raw","pwAux2LO.raw"};

// on off power dot under each power button on the power screen and feeding config screen
char *pwrDot[] = {
  "pwDotOff.raw","pwDotOn.raw"};

// small power icons for the home screen, off and on
char *pwrLightIconS[] = {
  "pwLiteSF.raw","pwLiteSO.raw"};
char *pwrFilterIconS[] = {
  "pwFiltSF.raw","pwFiltSO.raw"};
char *pwrCircIconS[] = {
  "pwPumpSF.raw","pwPumpSO.raw"};
char *pwrHeatIconS[] = {
  "pwHeatSF.raw","pwHeatSO.raw"};
char *pwrCO2IconS[] = {
  "pwCo2SF.raw","pwCo2SO.raw"};
char *pwrAux1IconS[] = {
  "pwAux1SF.raw","pwAux1SO.raw"};
char *pwrAux2IconS[] = {
  "pwAux2SF.raw","pwAux2SO.raw"};

// small light mode icons for home screen
char *lightModeFade[] = {
  "schHS.raw","schMS.raw","schLS.raw","schM.raw"};

// enabled or not enabled small check boxes for the power schedule screen
char *schedActive[] = {
  "11dis.raw","11enab.raw"};

// days and month character strings for displaing at the top of the screen
char *Day[] = {
  "","Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
char *Mon[] = {
  "","Jan","Feb","March","April","May","June","July","Aug","Sept","Oct","Nov","Dec"};

As all filenames end on ".raw" you could just add that part at the end.
The extra code might be smaller than all those extensions

Looking at the names, these can easily be generated.

pwLiteLF.raw","pwLiteLO.raw" just one char different

"pwDotOff.raw","pwDotOn.raw" slightly more difficult

you can rename all the files to "1" ... "n" having optimal compression...

I now switched to the F() macro which works great and may be all I need. I'm adding serial communications between 2 arduinos and the serial() tags were taking up memory as I added them, now its like they aren't even there. :slight_smile: To bad I can't use it on text printed to the screen. If I need more I'll try some of the other stuff in your examples Nick.

That's a good idea on shortening the names of the files, I will try that and see how it works.

Do I understand properly?

Using the F() macro I can have as many serial prints as I want without affecting memory as long as they are strings?

The F macro, used with string literals, avoids consuming RAM. They still consume PROGMEM.

So something like this won't consume RAM but consume flash memory? Not sure what you mean by "They still consume PROGMEM"

      Serial1.println(F("123"));

Well, the "123" has to come from somewhere, right?

Using the F() macro I can have as many serial prints as I want ...

No, you can't have a billion of them. The things you print are in in flash memory (PROGMEM). So you can't have "as many as you want". You can have as many as will fit into flash.

Thanks Nick, I understand now. And I shouldn't have said as many as you want, I did mean as many as the flash would hold, I realize it takes that space.

robtillaart, I tried your idea of shortening the file names, I changed the code I posted above, minus the dates, to numbers, started a 1.raw and ended at 40.raw. Doing that saved me 324 bytes, which is awesome!

Thought I would post the results of that if anyone else comes across this. I don't know how to add the .raw to the end so that's still part of the variables, I'm sure if I did that I would save another 300 bytes as that's a lot of text removed.

I don't know how to add the .raw to the end so that's still part of the variables, I'm sure if I did that I would save another 300 bytes as that's a lot of text removed.

Show how you are using the file names (or the whole sketch). That can be easily fixed.

Here's an example, they all work the same.

Here's what the global variables looks like.

char *pwrLightIcon[] = {
  "5.raw","6.raw"};

// used for storing power states of relays
struct PWR
{  
  byte pwrLight1;
  byte pwrLight2;
  byte pwrFilter;
  byte pwrCirc;
  byte pwrHeat;
  byte pwrCO2;
  byte pwrAux1;
  byte pwrAux2;
} 
feedPower, preFeedPower, globalPower;

and this is the code throughout the sketch loading one of those images.

  myFiles.loadBitmap(58, 105, 96, 96, pwrLightIcon[globalPower.pwrLight1]);

The actual file name "5.raw" is never seen again in the sketch, only in the global variable.

Here is a method you can use. It means your global RAM usage for the file names can be decreased to 8 bytes. No need to store file names if they are essentially numbers.

char *makeFileName( unsigned char id ){
  static char text[ 8 ];            //Storage for file name
  char *ptr = itoa( id, text, 10 ); //Convert ID to a string.
  while( *ptr ) ++ptr;              //Move to end of ID string (null char)
  memcpy_P( ptr, F( ".raw" ), 5 );  //Copy in file path
  return text;
}

It would allow you to do something like this:

#define PWRLIGHT_ICONS 5

//...


myFiles.loadBitmap(58, 105, 96, 96, makeFileName(PWRLIGHT_ICONS + globalPower.pwrLight1) );

You can test the function using this short sketch:

void setup() {
  Serial.begin( 9600 );

  for( unsigned char c = 0 ; c < 255 ; ++c ){
    Serial.println( makeFileName(c) );
  }
}

void loop(){}

The function is not thread/interrupt safe, so you’ll need to ensure your main program flow has finished using the return value before using the function in an interrupt (if you happen to do that).