3 simple lines of code use 7% of memory

Hi all,

I have 3 very simple lines of code that when compiled uses 7% of SRAM. The funny thing is that those same 3 lines of code exist else where in the program, but don't use up any memory.

Here's the problem spot:

// get RSSI values
  int rssiValue = 0;          // RSSI value from FONA
  int dBmValue = 0;           // decibel milliwatts value, calculated from RSSI
  getRssi(&rssiValue, &dBmValue);     // pass variables by reference - this function will change them

  char RssiMsg[140] = " RSSI = ";          // store the result here to build upon and print
  char strRssiValue[5] = "";
  itoa(rssiValue, strRssiValue, 10);
  char strDecibValue[7] = "";
  itoa(dBmValue, strDecibValue, 10);
  
  // this code hogs memory, not sure why....
  
  strcat(RssiMsg, strRssiValue);  strcat(RssiMsg, ": ");
  
  strcat(RssiMsg, strDecibValue);  strcat(RssiMsg, " dBm");
  
  Serial.print(F(" signal strength - "));  Serial.println(RssiMsg); Serial.println();

As is, the sketch compiles at 63% of dynamic memory. if I comment out the last three lines of code, it compiles at 56% dynamic memory. The above code is at line 875 of my program.

At around line 505 of the program, I have this bit of code:

  // get RSSI values
  int rssiValue = 0;          // RSSI value from FONA
  int dBmValue = 0;           // decibel milliwatts value, calculated from RSSI
  getRssi(&rssiValue, &dBmValue);     // pass variables by reference - this function will change them

  char strRssiValue[5] = "";
  itoa(rssiValue, strRssiValue, 10);
  char strDecibValue[7] = "";
  itoa(dBmValue, strDecibValue, 10);

  strcat(response, "\nRSSI = ");  strcat(response, strRssiValue);  strcat(response, ": ");
  strcat(response, strDecibValue);  strcat(response, " dBm");
  
  Serial.print(F("response - "));  Serial.println(response); Serial.println();

The last 3 lines of this code are preforming the same action as the last 3 lines of the other code, building a character array. These last 3 lines of code don't use any dynamic memory.

Any idea what's going on?

If anyone wants to see the entire code to help me, I'll post it, but it's ~950 lines...

Thanks for any help
Randy

If " RSSI = " is a constant string, why not treat it as such, and leave it in flash memory, where it belongs?

Why bother with strcat?

in the first part you have 140 bytes allocated to your buffer  char RssiMsg[140] = " RSSI = ";          // store the result here to build upon and print

in the second you stuff things into response

  strcat(response, "\nRSSI = ");  strcat(response, strRssiValue);  strcat(response, ": ");
  strcat(response, strDecibValue);  strcat(response, " dBm");

but we don't know what response is... does it have 140 bytes too? is it a local variable or a global? (local would not be reported by the compiler)

in any case, why do you bother building a string at all, just send to Serial the elements one by one...

  Serial.print(F("response - \nRSSI = "));
  Serial.print(rssiValue);
  Serial.print(F(":"));
  Serial.print(dBmValue);
  Serial.println(F(" dBm\r\n"));

that will save you memory and make the code faster

without those 3 lines RssiMsg[140] is optimized away

revolt_randy:

  char RssiMsg[140] = " RSSI = ";          // store the result here to build upon and print

// this code hogs memory, not sure why....
 strcat(RssiMsg, strRssiValue);  strcat(RssiMsg, ": ");
 strcat(RssiMsg, strDecibValue);  strcat(RssiMsg, " dBm");
 Serial.print(F(" signal strength - "));  Serial.println(RssiMsg); Serial.println();




As is, the sketch compiles at 63% of dynamic memory. if I comment out the last three lines of code, it compiles at 56% dynamic memory. The above code is at line 875 of my program.

I think that when you take out those three lines the compiler realizes that you aren't using 'RssiMsg' anywhere so it doesn't allocate space for the 140 characters or the three string constants you eliminates.

Instead of showing the percentage it would be more informative to compare the number of bytes used. On an UNO with 2048 bytes of RAM a 7% change is about 143 bytes. I would have guessed a savings of about 165 bytes between 'RssiMsg' and the three strings.

TheMemberFormerlyKnownAsAWOL:
If " RSSI = " is a constant string, why not treat it as such, and leave it in flash memory, where it belongs?

When flash/code memory is intended to hold program codes, why are you insisting to place data into there?

GolamMostafa:
When flash/code memory is intended to hold program codes, why are you insisting to place data into there?

Because it is already there (yes, really), and because it is a constant string.

Hey, thanks for the replies!

J-M-L:
in the first part you have 140 bytes allocated to your buffer  char RssiMsg[140] = " RSSI = ";          // store the result here to build upon and print

in the second you stuff things into response

  strcat(response, "\nRSSI = ");  strcat(response, strRssiValue);  strcat(response, ": ");

strcat(response, strDecibValue);  strcat(response, " dBm");


but we don't know what response is... does it have 140 bytes too? is it a local variable or a global? (local would not be reported by the compiler)

in any case, why do you bother building a string at all, just send to Serial the elements one by one... 


Serial.print(F("response - \nRSSI = "));
  Serial.print(rssiValue);
  Serial.print(F(":"));
  Serial.print(dBmValue);
  Serial.println(F(" dBm\r\n"));


that will save you memory and make the code faster

response is a character array of 150 bytes. It is defined in the same function where it is used. It is a local variable. In both cases, char response[150]; and char RssiMsg[140]; are both local variables in functions.

I guess that is what I am really wondering, why the one code example, the first one, increase global variable usage, while the second example doesn't.

To answer your question, in both cases I build a character array to pass to another function that sends the data via a SMS text message.

johnwasser:
I think that when you take out those three lines the compiler realizes that you aren't using 'RssiMsg' anywhere so it doesn't allocate space for the 140 characters or the three string constants you eliminates.

Instead of showing the percentage it would be more informative to compare the number of bytes used. On an UNO with 2048 bytes of RAM a 7% change is about 143 bytes. I would have guessed a savings of about 165 bytes between 'RssiMsg' and the three strings.

In both cases, the character arrays that are being built are local to the function they are in, the arrays are then passed to another function that sends them via a SMS text message. Since they are local, they should count towards global variable usage should they?

GolamMostafa:
When flash/code memory is intended to hold program codes, why are you insisting to place data into there?

Because I have too many string constants that are used to build character arrays to send to another function. If they weren't stored in program space, I would have ran out of SRAM a long time ago.

Thanks everyone for your answers, but I'm still not understanding why using local variables is increasing my global variable useage for one example and not the other.

Randy

It's hard to tell the scope of the variables from your snippets.
Local variables won't normally be reported in the memory requirements.

can you post the whole code ? or a smaller program exhibiting the issue?

  char RssiMsg[140] = " RSSI = ";          // store the result here to build upon and print

The initializer for your local array, all 140 bytes, is saved in FLASH and counts against your global variables.

To save global space you could initialize the array at run time:

  char RssiMsg[140] ;          // store the result here to build upon and print
  strcpy(RssiMsg, " RSSI = ");

That will use about 8 bytes of FLASH and RAM for the string plus a little FLASH for the function call.

To save a little RAM space you could store the initializer in PROGMEM:

  const static char RSSI_MSG[] PROGMEM = " RSSI = ";
  char RssiMsg[140] ;          // store the result here to build upon and print
  strcpy_P(RssiMsg, RSSI_MSG);

That will use about 8 bytes of FLASH for the PROGMEM string plus a little FLASH for the function call.

Good catch John. (Assuming you tested this)

I would have thought the compiler was smarter and not allocate in flash the 140 bytes since only a few are really needed for initialization...

Looks like TEST4 (see sketch below) uses the least PROGMEM but TEST5 saves 8 bytes of RAM and only uses 2 more bytes of PROGMEM.

TEST1 (Can't use strcat() because the array is in PROGMEM)
Sketch uses 1684 bytes (5%) of program storage space.
Global variables use 188 bytes (9%) of dynamic memory, leaving 1860 bytes for local variables.

TEST2
Sketch uses 1688 bytes (5%) of program storage space.
Global variables use 388 bytes (18%) of dynamic memory, leaving 1660 bytes for local variables.

TEST3
Sketch uses 1742 bytes (5%) of program storage space.
Global variables use 388 bytes (18%) of dynamic memory, leaving 1720 bytes for local variables.

TEST4
Sketch uses 1546 bytes (4%) of program storage space. Maximum is 32256 bytes.
Global variables use 196 bytes (9%) of dynamic memory, leaving 1852 bytes for local variables.

TEST5
Sketch uses 1548 bytes (4%) of program storage space. Maximum is 32256 bytes.
Global variables use 188 bytes (9%) of dynamic memory, leaving 1860 bytes for local variables.

#define TEST1


#ifdef TEST1
const char RssiMsg[200] PROGMEM = " RSSI = ";          // store the result here to build upon and print
#endif


#ifdef TEST2
char RssiMsg[200] = " RSSI = ";          // store the result here to build upon and print
#endif


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


#ifdef TEST3
  char RssiMsg[200] = " RSSI = ";          // store the result here to build upon and print
#endif




#ifdef TEST4
  char RssiMsg[200] ;          // store the result here to build upon and print
  strcpy(RssiMsg, " RSSI = ");
#endif


#ifdef TEST5
  const static char RSSI_MSG[] PROGMEM = " RSSI = ";
  char RssiMsg[200] ;          // store the result here to build upon and print
  strcpy_P(RssiMsg, RSSI_MSG);
#endif


#ifdef TEST1
  Serial.println((__FlashStringHelper *)RssiMsg);
#else
  Serial.println(RssiMsg);
#endif
}


void loop() {}

thanks John!

Thanks again for the replies!

TheMemberFormerlyKnownAsAWOL:
It's hard to tell the scope of the variables from your snippets.
Local variables won't normally be reported in the memory requirements.

Ok, I got it. The scope of the variables was unknown because I didn't post the complete functions. Lesson learned, but after reading the replies here, I have a few questions about scope of variables.

johnwasser:

  char RssiMsg[140] = " RSSI = ";          // store the result here to build upon and print

The initializer for your local array, all 140 bytes, is saved in FLASH and counts against your global variables.

To save global space you could initialize the array at run time:

  char RssiMsg[140] ;          // store the result here to build upon and print

strcpy(RssiMsg, " RSSI = ");



That will use about 8 bytes of FLASH and RAM for the string plus a little FLASH for the function call.

To save a little RAM space you could store the initializer in PROGMEM:


const static char RSSI_MSG[] PROGMEM = " RSSI = ";
  char RssiMsg[140] ;          // store the result here to build upon and print
  strcpy_P(RssiMsg, RSSI_MSG);



That will use about 8 bytes of FLASH for the PROGMEM string plus a little FLASH for the function call.

So basically, it was because I was defining and initializing the variable at the same time caused the FLASH usage? Indeed, the char response[150] was just defined, not initialized. Thank you for the code example, that's what made it all click for me.

But that leads me to other questions about variable's scope. Global variables defined before the setup() are available to the whole program. Local variables defined in functions are local to the function. Variables define in the setup() have what scope?

To save global space you could initialize the array at run time:

What is the scope of a run time variable?

I will look into this more myself, but everyone here so far has taught me something, why stop here? Teach me more.....

Thanks,
Randy

PS - @ johnwasser - if you pm me a paypal address, I'll buy you a cup of coffee....

Local variables defined in functions are local to the function. Variables define in the setup() have what scope?

setup() is just a function like any other., including loop()

What is the scope of a run time variable?

It depends on your code. The symbol can be local in scope even if the memory is surviving the duration of the function and You can free up the allocated memory (malloc/free or object instantiation/deletion) and you can pass pointers to location in memory to other functions so they can use the data even if that function is not in the scope of the variable name