F() macro and PROGMEM

I found in Arduino Playground - Memory

  Serial.println(F("This string will be stored in flash memory"));

to be supported since Arduino 1.0

Now I have some other strings declared like

prog_char MyString[] PROGMEM = "This should be in flash as well";  
...
    Serial.println(MyString);

compiles well, and I guess ( comparing sketch size and mem_check() results ), that both texts really reside in flash,
but it does not produce the expected output.
I understand the Serial.println() Method has a variant for prog_char* data type, which hides pgm_read_char and avoids strcpy_P. ?
How can I use this with my globally defined PROGMEM texts ?

I found a workaround using locally defined texts:

void setup() {
  static __FlashStringHelper* HEADER = 
   F("-- -- ---- -- -- -- ---------  ------  ------") ;

   // ...

   Serial.println(HEADER); 
}

but the F( ) macro is not allowed globally:

statement-expressions are not allowed outside functions

Edit: This does not solve the problem...

This works:

const char* PROGMEM test = "Globally declared in Flash mem";

void setup() {
  Serial.begin(19200);
}

void loop() {
  Serial.println(F("Inline Flash mem"));
  Serial.println(test);
 delay (1000); 
}

Thanks.
I'm not sure about how serious the warning in PROGMEM - Arduino Reference is, to avoid
data type PROGMEM variable[] = {};   syntax

... It works, but I fear your "Globally declared in Flash mem" is eating RAM space. Could it be that just the pointer is in PROGMEM ?
With different text lengths check_mem() shows me different sizes of the heap.

If anybody else is interested: http://arduino.cc/forum/index.php/topic,91314.0.html is very informative.
My solution currently is:

#define PGMSTR(x) (__FlashStringHelper*)(x)

const char HEADER[]  PROGMEM  = { "-- -- ---- -- -- -- ---------  ------  ------"};

void setup() {
// ...
    Serial.println (PGMSTR(HEADER));
}

which is saving to type a few characters ... :wink:

Edit: adding test results of TRex' solution

You are right, I had the PROGMEM in the wrong place.

Your solution works, you can also add another pointer for each global string, making the prints even simpler, but then adding another pointer in memory:

const char HEADER[]  PROGMEM  = { "-- -- ---- -- -- -- ---------  ------  ------"};
__FlashStringHelper* HEADERPTR = (__FlashStringHelper*)HEADER;

void setup() {
// ...
    Serial.println (HEADERPTR);
}

I agree.

My concern was that the F() macro does not work outside a function,
and that it requires a literal constant and won't work on a char[] or char*.

BTW: Why don't I need prog_char instead of char and why doesn't that trigger the print() behavior,
instead of this obscure pointer to class __FlashStringHelper ?

I'll just reduce my macro name to fewer characters and have nearly got what I was looking for. :wink:

#define FS(x) (__FlashStringHelper*)(x)
const char MyText[]  PROGMEM  = { "My flash based text" };

void setup() {
  Serial.begin(57600):
  Serial.println(F("inline flash based text"));
  Serial.println(FS(MyText));
}

Thanks for your feedback.

Hello,

thanks for the nice and short Code on global Flash-based strings!
A have copied michael_x's code and changed it al little to my needs. All works fine.

Now i would like to save some strings stored in an array (e.g. LCD-Menu or Serial-Menu ect., in this case Commands CmdAR[] ) also to Flash but that does not work. It compiles, but the Ram and Flash compiler massages shows, it is not in flash. Is there an option to put Array based strings to Flash (and get them back for comparison to a, by serial read-in-sting and printing)?

Thanks in advance!

// Demonstrate easy use of global saved, Flash based strings
// Source: http://forum.arduino.cc/index.php?topic=110307.0 



//### Defines for Flash saved Text #############################################
#define FPr(x);   Serial.print(FP(x));              // FlashPrint(FT)
#define FPrL(x);  Serial.println(FP(x));            // FlasPrintLine(FT)
#define SPFT(x);  Serial.print(FP(FT_##x));         // shorter version (ID)
#define SPFTL(x); Serial.println(FP(FT_##x));       // shorter version (ID)

#define SFP(x);   Serial.print(FP(x));

#define FT(x,y);  const char FT_##x[] PROGMEM = {y};// generate FlashText(ID,FT)
#define FP(x)     (__FlashStringHelper*)(x)         // Helper
//******************************************************************************



//### Shortening standard Serial.Print F() #####################################
#define SPr(x);   Serial.print(x);                  // short for Serial.print
#define SPrL(x);  Serial.println(x);                // short for Serial.println
#define SPrF(x);  SPr(F(x));                        // short for F-Macro Serial.print
#define SPrLF(x); SPrL(F(x));                       // short for F-Macro Serial.println
//******************************************************************************



//### Generate global Flash saved strings ######################################
FT(0,"Text 0");                                               // Flash Text FT_0
FT(1,"This is a global saved and Flash "
     "saved Text!");                                          // FT_1
FT(2,"File: " __FILE__ " Date: " __DATE__ " Time: " __TIME__);// FT_2
FT(3,"***************************************************");  // FT_3
FT(B4,"*\t RS232 Menu\t\t\t\t  *");                            // FT_4
FT(5,"###################################################");  // FT_3
//******************************************************************************



//### Typedefs ################################################################
typedef void(*pF)(void);                        // Pointer to void FCN(void)
//*****************************************************************************

//### Struct RS232 Commands ###################################################
struct Commands
{ 
  //char    cmd1[10];                           // Command 1
  const char cmd1[10] PROGMEM;

  //char    cmd2[10];                           // Command 2
  const char cmd2[10] PROGMEM;
  pF      callback;                           // Callback-FCN
  //String  info;                               // Info on Command
  //char    info[60];
  const char info[60] PROGMEM;
  
};
//*****************************************************************************

#define CMD_CNT (sizeof(CmdAR)/sizeof(CmdAR[0])) // Number of Commands

//### Define RS232-Command-Array ##############################################
Commands CmdAR[] = {
// cmd1,    cmd2,    callback, info
{"TASK",   "t  ",   cTask,   "set Task Parameter (ID, Periode, Set, CNT, Offset)"},
{"Set",    "s  ",   cTask,   "set Task Parameter (ID, Periode, Set, CNT, Offset)"},
{"LCDBL",  "lcdb",  cLcdBL,  "set LCD BackLight (off/Auto-Off Periode)"          }
};


void cTask(){
  SPrLF("cTask");
}

void cLcdBL(){
  SPrLF("cLcdBL");
}

void printComands(){
  SPrL();
  //SPrLF("**************************************************");
  SPFTL(3);
  //SPrLF("*\tRS232 Command List\t\t\t *");
  SPFTL(B4);
  //SPrLF("**************************************************");
  SPFTL(3);
  SPrLF("Cmd1\t, Cmd2\t, Info (Parameter)");
  for(byte i=0; i<CMD_CNT; i++){
    SPrF(" ");
    //SPr(FP(CmdAR[i].cmd1)); SPrF("\t, ");
    SFP(CmdAR[i].cmd1); SPrF("\t, ");
    SFP(CmdAR[i].cmd2); SPrF("\t, ");
    SFP(CmdAR[i].info); 
    SPrL();
  }
  SPrL();
  //SPrLF("**************************************************");
  SPFTL(3);
  SPrLF("Type: < Command1/2, Val1, Val2, Val3, Val4, Val5");
  SPrL();
}


//### Setup() #####################################################
void setup() {
  Serial.begin(230400);
  SPrL();
  Serial.println(F("Macro-Based, only single "
                   "In-Function usable Flash saved Text."));
  SPrLF("...the same, but shorter command ");

  SPrL();                   // Newline
  
  FPrL(FT_0);               // Serial Print Line Flash saved Text
  FPrL(FT_1);
  SPFTL(2);                 // Shortest Version SerialPrintFlashText(ID)
  SPFTL(5);
  
  
}//void setup()
//*****************************************************************



//### Main loop() #################################################
void loop(){

  // reuse same string anywere in the Code
  //SPrL();                     // Newline
  //SPFTL(3);                   // Headder, SerialPrintFlashTextLine
  //SPFTL(B4);             
  //SPFTL(3);

  SPrL();
  printComands();

  delay(10000);  
}// void loop() 
//*****************************************************************

SOLVED :slight_smile:

I needed to add PROGMEM const Commands CmdAR[] = { ...to my Array and
included a function char* fStr(const char* str) to read Flash-based strings to Ram-based buffer and returns it (found at: http://linux.dd.com.au/wiki/Arduino_Static_Strings).

So for all that are happy to find some Example Code on the web, like me, here it is (Note: I am not a programmer, it might not be the best way to code it. Suggestions are welcome! :slight_smile: )

Thanks to all!

Code:

// Demonstrate easy use of global saved, Flash based strings
// Source: http://forum.arduino.cc/index.php?topic=110307.0 



//### Defines for Flash saved Text #############################################
#define SPFT(x);  Serial.print(FP(FT_##x));         // SerialPrintFlashText(ID)
#define SPFTL(x); Serial.println(FP(FT_##x));       // SerialPrintlnFlashText(ID)

#define SFP(x);   Serial.print(fStr(x));            // SerialFlashPrint()
#define SFPL(x);  Serial.println(fStr(x));          // SerialFlashPrintln()

#define FT(x,y);  const char FT_##x[] PROGMEM = {y};// generate FlashText(ID,FT)
#define FP(x)     (__FlashStringHelper*)(x)         // Helper
//******************************************************************************



//### Shortening standard Serial.Print F() #####################################
#define SPr(x);   Serial.print(x);                  // short for Serial.print
#define SPrL(x);  Serial.println(x);                // short for Serial.println
#define SPrF(x);  SPr(F(x));                        // short for F-Macro Serial.print
#define SPrLF(x); SPrL(F(x));                       // short for F-Macro Serial.println
//******************************************************************************



//### Generate global Flash saved strings ######################################
FT(2,"File: " __FILE__ "\nDate: " __DATE__ "\nTime: " __TIME__);// FT_2
FT(3,"***************************************************");  // FT_3
FT(B4,"*\t RS232 Menu\t\t\t\t  *");                            // FT_4
FT(5,"---------------------------------------------------");  // FT_5

//******************************************************************************



//### Typedefs ################################################################
typedef void(*pF)(void);                        // Pointer to void FCN(void)
//*****************************************************************************



//### Struct RS232 Commands ###################################################
struct Commands
{ 
  const char cmd1[10] PROGMEM;                  // Command 1
  const char cmd2[10] PROGMEM;                  // Command 2
  pF      callback;                             // Callback-FCN
  const char info[61] PROGMEM;                  // Info Text
};
//*****************************************************************************



//### Define RS232-Command-Array ##############################################
#define CMD_CNT (sizeof(CmdAR)/sizeof(CmdAR[0])) // Number of Commands
PROGMEM const Commands CmdAR[] = {
//Cmd1,    Cmd2,    Callback, Info
{"TASK",   "t  ",   cTask,   "set Task Parameter (ID, Periode, Set, CNT, Offset)"},
{"Set",    "s  ",   cTask,   "set Task Parameter (ID, Periode, Set, CNT, Offset)"},
{"Get",    "g  ",   cTask,   "12345678901234567890123456789012345678901234567890123456789)"},
{"LCDBL",  "lcdb",  cLcdBL,  "set LCD BackLight (off/Auto-Off Periode)"          }
};
//*****************************************************************************



//### Flash String Conversion #################################################
#define MAX_STRING 60
char* fStr(const char* str) {                   // Return Ram bufferd String
  char stringBuffer[MAX_STRING+1];
  strcpy_P(stringBuffer, (char*)str);
  return stringBuffer;
}
//*****************************************************************************



//### Dummy Callback 1 ########################################################
void cTask(){
  SPrLF("cTask");
}
//*****************************************************************************
//### Dummy Callback 2 ########################################################
void cLcdBL(){
  SPrLF("cLcdBL");
}
//*****************************************************************************



//### Print all Commands (or. LCD-Menu etc.) ##################################
void printComands(){
  SPrL();                                     // Newline
  SPFTL(3);                                   // ******
  SPFTL(B4);                                  // * Rs232 Command
  SPFTL(3);                                   // ******
  SPrLF("Cmd1\t, Cmd2\t, Info (Parameter)");
  SPFTL(5);                                   // --------
  
  for(byte i=0; i<CMD_CNT; i++){              // Array-Content
    SPrF(" ");
    SFP(CmdAR[i].cmd1); SPrF("\t, ");         // Command 1
    SFP(CmdAR[i].cmd2); SPrF("\t, ");         // Command 2
    SFP(CmdAR[i].info);                       // Info Text
    SPrL();
  }
  
  SPrL();
  SPFTL(3);                                   // ******
  SPrLF("Type: < Command1/2, Val1, Val2, Val3, Val4, Val5");
  SPrL();
}
//*****************************************************************************



//### Setup() #################################################################
void setup() {
  Serial.begin(230400);
  SPrL();
  SPFTL(2);                 // Shortest Version SerialPrintFlashText(ID)
   
}//void setup()
//*****************************************************************************



//### Main loop() #############################################################
void loop(){
 
  SPrL();
  printComands();

  delay(60000);  
}// void loop() 
//*****************************************************************************

//END OF FILE

There is one more thing, I need to compare a flash based string to one other:

const byte    cmdSize           = 10;       // Serial recive Command size [Byte]
char          comandFromPC[cmdSize] = {0}; // Command buffer

//...

if( (strcmp(comandFromPC, fStr(CmdAR[i].cmd1)) == 0)){ //...}

//### Flash String Conversion #################################################
#define MAX_STRING 60
char* fStr(const char* str) {                   // Return Ram bufferd String
  char stringBuffer[MAX_STRING+1];
  strcpy_P(stringBuffer, (char*)str);
  return stringBuffer;
}
//*****************************************************************************

I am happy to get some hints! :slight_smile:

here is one more thing, I need to compare a flash based string to one other:

You put them there - why would you need to compare them?

I would like to compare the flash based strings to the serial rxed command of the Serial port.

You mean, like with "strcmp_P" ?

Hey, I am a programmer and I think this is broken:

char* fStr(const char* str) {                   // Return Ram bufferd String
  char stringBuffer[MAX_STRING+1];
  strcpy_P(stringBuffer, (char*)str);
  return stringBuffer;

stringBuffer is a local variable to fStr function, you should not return a pointer to it as that memory is fred once the function returns and you have a pointer to memory that does not belong to you and might get overriten or something. You may reserve memory with 'malloc' and free it with 'free' after use but it is not recomended on arduino due to tiny memory and fragmentarion. Better use an external buffer that may be a local variable to the outter function but hey, you would be making strcpy_P function itself.

You are coding like in java or python. In those languages there is a garbage collector that free objects no longer used.

as that memory is fred

Are you sure about that?

PaulS:
Are you sure about that?

I am. Automatic variables are automatically allocated when they become in scope and freed when they go out of scope.

PaulS, I love your jokes.
I particularly like the whooshing sound as they go over other people's heads.

I was hoping PaulS's comment was not about the typo itself (fred vs freed). Otherwise I just look goofy for not getting the joke. :-/

Sorry, the Quote does not work on my tablet.

You mean, like with "strcmp_P" ?
That might be the solution for my problem. I didn't knew/know that function.

I can't test it befor sunday, is it as simple as change
from:
if( (strcmp(comandFromPC, CmdAR*.cmd1) == 0)){ //...}*
to:
if( (strcmp_P(comandFromPC, CmdAR*.cmd1) == 0)){ //...} ??
_
Hey, I am a programmer and I think this is broken: ... *_
Thank you very much for that hint and explanation. Now I clearly see my mistake and understand why my controller was resetting if called that function. I will try strcmp_P instead.

A further question I have:

Is it possible to put a const char PROGMEM string and a int variable in a struct and generate an Array of that struct, where the strings are constant and in Flash and the int variables are in ram and can be changed at runtime?

Would it look like this?:
struct myStruct
{
const char myFlashString[10] PROGMEM;
Int myInt;
};

PROGMEM const myStruct myArray[] = { // or witout const PROGMEM?
{"Flashtext 1", 200},
{"Set", -32000}
};

"Flashtext 1"

How many characters are there?

const char myFlashString[10] PROGMEM;

How many fit?

Is it possible to put a const char PROGMEM string and a int variable in a struct and generate an Array of that struct, where the strings are constant and in Flash and the int variables are in ram and can be changed at runtime?

Yes, though the idea seems goofy. If you have an array of structs containing an int and a string, is is really that difficult to deal with an array of ints and an array of strings, instead?

PaulS:

as that memory is fred

Are you sure about that?

I think he meant "fried" - which can happen if you put four million volts through it.