Go Down

Topic: F() macro and PROGMEM (Read 52432 times) previous topic - next topic

michael_x

I found in http://www.arduino.cc/playground/Learning/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:
Code: [Select]
void setup() {
  static __FlashStringHelper* HEADER =
   F("-- -- ---- -- -- -- ---------  ------  ------") ;

   // ...

   Serial.println(HEADER);
}
but the F( ) macro is not allowed globally:
statement-expressions are not allowed outside functions

TRex

#1
Jun 16, 2012, 12:38 pm Last Edit: Jun 16, 2012, 02:47 pm by TRex Reason: 1
Edit: This does not solve the problem...

This works:

Code: [Select]

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);
}

michael_x

#2
Jun 16, 2012, 01:07 pm Last Edit: Jun 16, 2012, 01:19 pm by michael_x Reason: 1
Thanks.
I'm not sure about how serious the warning in http://www.arduino.cc/en/Reference/PROGMEM 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:

Code: [Select]
#define PGMSTR(x) (__FlashStringHelper*)(x)

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

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


which is saving to type a few characters ... ;)

Edit: adding test results of TRex' solution

TRex

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:

Code: [Select]

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

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



michael_x

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.  ;)


Code: [Select]
#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.


jim_beam

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!

Code: [Select]
// 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()
//*****************************************************************


jim_beam

SOLVED  :)

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!  :) )

Thanks to all!

Code:
Code: [Select]
// 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

jim_beam

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

Code: [Select]

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!  :)


AWOL

Quote
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?
"Pete, it's a fool (who) looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.
I speak for myself, not Arduino.

jim_beam

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

AWOL

You mean, like with "strcmp_P" ?
"Pete, it's a fool (who) looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.
I speak for myself, not Arduino.

aalku

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

Code: [Select]

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.

PaulS

Quote
as that memory is fred
Are you sure about that?
The art of getting good answers lies in asking good questions.

christop

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.

AWOL

PaulS, I love your jokes.
I particularly like the whooshing sound as they go over other people's heads.
"Pete, it's a fool (who) looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.
I speak for myself, not Arduino.

Go Up