Saving memory with macros (put strings from RAM to PROGMEM)

Started by VdesmedT, July 18, 2015, 04:44:55 PM

VdesmedT

Hey there,  I wondered if someone had a less chatty version of the Wireless69Hex library which apparently consumes 330bytes of memory, of which I suspect most are strings used for debugging. I can of course just remove those if(DEBUG) Serial.println... statements (or at least shorten them) but I dont wanna mess with the code and miss the next updates. Shall I start a branch for that ? Any other suggestion ?

Felix

You can move them in the flash by using PROGMEM. I should have done that in the first place ... If I ever refactor the libs I will put the strings in flash instead of ram.

VdesmedT

That's indeed a good option but we will then need to initialise the flash and the initializing code will take the same strings along...  So we will need a separate sketch to initialise the flash that needs to be run first on every node..  I'm I missing something ?

TomWS

Quote from: VdesmedT on July 19, 2015, 05:04:26 AM
That's indeed a good option but we will then need to initialise the flash and the initializing code will take the same strings along...  So we will need a separate sketch to initialise the flash that needs to be run first on every node..  I'm I missing something ?
You are correct IF the flash Felix was referring to was the outboard flash memory.  Fortunately, he was referring to the internal flash memory.  There is an easy way to move string constants to this space without doing any 'programming'...

Say you have the following code:
   Serial.println("I really don't like this being in data memory!");


If you use the 'F()' macro, you will move the string to PROGMEM as Felix suggested:
   Serial.println( F("I really don't like this being in data memory!") );
   Serial.println( F("Ahhh, this is MUCH better!") );

Note that the entire string is contained within:  F(...) so you'll have the double closing parenthesis at the end.  Also note that this will ONLY work with string constants that are retrieved as a block.  Consequently this will NOT work with sprintf() function, as in:

  Serial.print( F("This will work!") );
  sprintf(buf, F("This will NOT work!") );
  Serial.print(buf);


Tom

VdesmedT

I was not aware of that macro. Thanks !

EloyP

Quote from: TomWS on July 19, 2015, 08:25:35 AM
Also note that this will ONLY work with string constants that are retrieved as a block.  Consequently this will NOT work with sprintf() function, as in:

  Serial.print( F("This will work!") );
  sprintf(buf, F("This will NOT work!") );
  Serial.print(buf);


It should work if sprintf_P() is used instead. I say *should* because I am probably the only person here that does not use Arduino libraries or "language" to program his Moteinos/ATmegas, so I am not 100% sure. But, in the AVR libc world (which Arduino uses, I believe), if one #includes <avr/pgmspace.h> and uses sprintf_P() then the format string is expected to be in program memory. (An additional nice addition is that %S in the format string fetches the vararg string from program memory, unlike %s which fetches it from RAM.

Cheers!

TomWS

Quote from: EloyP on July 19, 2015, 11:38:29 PM
<snip>

It should work if sprintf_P() is used instead. I say *should* because I am probably the only person here that does not use Arduino libraries or "language" to program his Moteinos/ATmegas, so I am not 100% sure. But, in the AVR libc world (which Arduino uses, I believe), if one #includes <avr/pgmspace.h> and uses sprintf_P() then the format string is expected to be in program memory. (An additional nice addition is that %S in the format string fetches the vararg string from program memory, unlike %s which fetches it from RAM.

Cheers!
Thanks, Eloy!  That's useful information, especially with the constrained data memory in the 328P.

Tom

Felix


TomWS

Quote from: TomWS on July 20, 2015, 07:41:16 AM
Quote from: EloyP on July 19, 2015, 11:38:29 PM
<snip>

It should work if sprintf_P() is used instead. I say *should* because I am probably the only person here that does not use Arduino libraries or "language" to program his Moteinos/ATmegas, so I am not 100% sure. But, in the AVR libc world (which Arduino uses, I believe), if one #includes <avr/pgmspace.h> and uses sprintf_P() then the format string is expected to be in program memory. (An additional nice addition is that %S in the format string fetches the vararg string from program memory, unlike %s which fetches it from RAM.

Cheers!
Thanks, Eloy!  That's useful information, especially with the constrained data memory in the 328P.

Tom
I tried this and sprintf_P does work from PROGMEM but you can't use the F() macro to define the format string.  You have to declare the format string as:
PROGMEM const char fmt[]="BBB Power is low! Current reading is:%d, should be %d\n";

...
    sprintf_P(buf, fmt, sysResetADC, SYS_RESET_POWER);


Thanks again for the tip!
Tom

EloyP

Hi Tom,

Quote from: TomWS on July 20, 2015, 08:49:28 AM
I tried this and sprintf_P does work from PROGMEM but you can't use the F() macro to define the format string.  You have to declare the format string as:
PROGMEM const char fmt[]="BBB Power is low! Current reading is:%d, should be %d\n";

...
    sprintf_P(buf, fmt, sysResetADC, SYS_RESET_POWER);


As far as I can tell, the F() macro, which I understand is provided by the Arduino include file(s), is similar (though apparently not interchangeable based on your testing) to the AVR libc PSTR() macro. You use it in a way similar to how you showed the F() macro being used:

printf_P(PSTR("I am in program memory\n") );

sprintf_P(buffer, PSTR("%S and myself are in program memory\n"), PSTR("this string") );

I put all my strings in program memory as putting them in RAM consumes both RAM and program space memory; a total waste of resources.

Cheers!

Eloy Paris.-