PROGMEM instruction and compiling problem..

Hi Everyone,
I am a newbie on Arduino world and would like to seek support from community on this.
I made a sketch with following code. I made PROGMEM following instruction to have the menu data saved on flash, however, when i verify, IDE complained that I have low dynamic memory (compiler message below)

Sketch uses 9946 bytes (30%) of program storage space. Maximum is 32256 bytes.
Global variables use 1538 bytes (75%) of dynamic memory, leaving 510 bytes for local variables. Maximum is 2048 bytes.
Low memory available, stability problems may occur.

When I resize the array to 2 elements, the size of dynamic memory consumption reduced as below.

Sketch uses 8512 bytes (26%) of program storage space. Maximum is 32256 bytes.
Global variables use 248 bytes (12%) of dynamic memory, leaving 1800 bytes for local variables. Maximum is 2048 bytes.

This should mean that PROGMEM does not work. Please help me to find out the problem with thanks.
Code for the troublesome menu part as below.

typedef boolean (*Menu_Function)();

typedef struct MenuItem
{
  char* Caption;
  char* Data;
  bool Type;
  Menu_Function Function;
  //unsigned int Address = 0;// EEPROM Address, do later
};

static const PROGMEM MenuItem Menu[22] PROGMEM  = 
{  
  {"Set Voltage 1","[Value,2]",1,SetBrightness},
  {"Set Voltage 2","[Value,4]",1,SetPowerLevel},
  {"Power level 1","[Min,5],[Max,6]",0,SetPowerLevel},
  {"Power level 2","[On:Hour,7],[On:Minute,8],[Off:Hour,9],[Off:Minute,10],[Level,11]",0,SetPowerLevel},
  {"Dev 1: Alarm MON","[On:Hour,12],[On:Minute,13],[Off:Hour,14],[Off:Minute,15],[Level,16]",0,SetPowerLevel},
  {"Dev 1: Alarm TUE","[On:Hour,17],[On:Minute,18],[Off:Hour,19],[Off:Minute,20],[Level,21]",0,SetPowerLevel},
  {"Dev 1: Alarm WED","[On:Hour,22],[On:Minute,23],[Off:Hour,24],[Off:Minute,25],[Level,26]",0,SetPowerLevel},
  {"Dev 1: Alarm THU","[On:Hour,27],[On:Minute,28],[Off:Hour,29],[Off:Minute,30],[Level,31]",0,SetPowerLevel},
  {"Dev 1: Alarm SAT","[On:Hour,32],[On:Minute,33],[Off:Hour,34],[Off:Minute,35],[Level,36]",0,SetPowerLevel},
  {"Dev 1: Alarm SUN","[On:Hour,37],[On:Minute,38],[Off:Hour,39],[Off:Minute,40],[Level,41]",0,SetPowerLevel},
  {"Dev 2: Alarm MON","[On:Hour,42],[On:Minute,43],[Off:Hour,44],[Off:Minute,45],[Level,46]",0,SetPowerLevel},
  {"Dev 2: Alarm TUE","[On:Hour,47],[On:Minute,48],[Off:Hour,49],[Off:Minute,50],[Level,51]",0,SetPowerLevel},
  {"Dev 2: Alarm WED","[On:Hour,52],[On:Minute,53],[Off:Hour,54],[Off:Minute,55],[Level,56]",0,SetPowerLevel},
  {"Dev 2: Alarm THU","[On:Hour,57],[On:Minute,58],[Off:Hour,59],[Off:Minute,60],[Level,61]",0,SetPowerLevel},
  {"Dev 2: Alarm SAT","[On:Hour,62],[On:Minute,63],[Off:Hour,64],[Off:Minute,65],[Level,66]",0,SetPowerLevel},
  {"Dev 2: Alarm SUN","[On:Hour,67],[On:Minute,68],[Off:Hour,69],[Off:Minute,70],[Level,71]",0,SetPowerLevel},
  {"Set Date","[Day,72],[Month,73],[Year,74]",0,SetPowerLevel},
  {"Set Time","[Hour,75],[Minute,76]",0,SetPowerLevel},
  {"Contrast","[Value,77]",0,SetContrast},
  {"Brightness","[Value,78]",1,SetBrightness},
  {"Time out","[Value,79]",0,SetPowerLevel},
  {"Sleep","[Value,80]",0,SetPowerLevel}  
};

Thank you very much for your support

when you do this

static const PROGMEM MenuItem Menu[22] PROGMEM  = 
{  
  {[color=red]"Set Voltage 1"[/color],[color=red]"[Value,2]"[/color],1,SetBrightness},

the cStrings in Red are in RAM, not in PROGMEM. what ends up in PROGMEM is the pointer to the ram

unfortunately you need to declare all your strings as PROGMEM individual elements, and then use that in the struct (declare them as
** **const char *** **
)

here is an example I had shared previously

#include <avr/pgmspace.h>

enum situation_t : uint8_t {single, couple, RIP, Arduinist};

const char s0[] PROGMEM = "single";
const char s1[] PROGMEM = "couple";
const char s2[] PROGMEM = "rest in peace";
const char s3[] PROGMEM = "discovering PROGMEM";

const char* const descriptionSituation[] PROGMEM = {s0, s1, s2, s3};

struct person_t {
  const char * firstName;
  const uint8_t d;
  const uint8_t m;
  const uint16_t y;
  situation_t situation;
};


// for char array, you define them first somewhere in flash memory
const char auguste[] PROGMEM = "Auguste";
const char camille[] PROGMEM = "Camille";
const char victor[] PROGMEM = "Victor";
const char juliette[] PROGMEM = "Juliette";
const char someone[] PROGMEM = "Paul Steigel";

const person_t population[] PROGMEM =
{
  {auguste, 12, 11, 1840, RIP}, // the structure holds a pointer in memory for the first name
  {camille, 8, 12, 1864, RIP},
  {victor, 26, 2, 1802, RIP},
  {juliette, 10, 4, 1806, RIP},
  {someone, 16, 11, 2020, Arduinist}
};

const uint16_t nbElements = sizeof(population) / sizeof(population[0]);

void setup() {
  char buffer[30]; // Needs to be large enough!

  Serial.begin(115200);
  for (size_t i = 0; i < nbElements; i++)
  {
    Serial.print(F("------------  ")); Serial.print(i); Serial.println(F("  ------------"));
    strcpy_P(buffer, (char*)pgm_read_word(&(population[i].firstName)));
    Serial.println( buffer );
    // or if you don't need it in RAM
    // Serial.println( (__FlashStringHelper*) pgm_read_word(&(population[i].firstName)));

    Serial.print( (uint8_t) pgm_read_byte_near(&(population[i].d)) ); Serial.print("/");
    Serial.print( (uint8_t) pgm_read_byte_near(&(population[i].m)) ); Serial.print("/");
    Serial.println( (uint16_t) pgm_read_word_near(&(population[i].y)) );

    strcpy_P(buffer, (char*)pgm_read_word(&(descriptionSituation[(uint8_t) pgm_read_byte_near(&(population[i].situation))])));
    Serial.println( buffer );
    // or if you don't need it in RAM
    // Serial.println( (__FlashStringHelper*)  pgm_read_word(&(descriptionSituation[(uint8_t) pgm_read_byte_near(&(population[i].situation))])));

  }
  Serial.println(F("------------------------------"));
}

void loop() {}

Serial Monitor (@ 115200 bauds) will show

[color=purple]
------------  0  ------------
Auguste
12/11/1840
rest in peace
------------  1  ------------
Camille
8/12/1864
rest in peace
------------  2  ------------
Victor
26/2/1802
rest in peace
------------  3  ------------
Juliette
10/4/1806
rest in peace
------------  4  ------------
[color=green]Paul Steigel
16/11/2020
discovering PROGMEM[/color]
------------------------------
[/color]

:slight_smile:

You'll see it's not direct either to extract the associated String, you need to go through indirection and pgm_read_word_near()

J-M-L:
You'll see it's not direct either to extract the associated String, you need to go through indirection and pgm_read_word_near()

It is not necessary to copy the text from PROGMEM to a buffer for printing, just cast to __FlashStringHelper*. A pointer can also be read from PROGMEM with pgm_read_ptr() or pgm_read_ptr_near(). There is also the _far version, but that only applies to a mega when you exceed the 64k byte boundary, and is a bit more involved.

enum Menu_Function:byte {SetBrightness, SetPowerLevel, SetContrast};
const char menu_function[3][14] PROGMEM = {
  "SetBrightness",
  "SetPowerLevel",
  "SetContrast"
};

struct MenuItem
{
  const char* Caption;
  const char* Data;
  const bool Type;
  Menu_Function Function;
  //unsigned int Address = 0;// EEPROM Address, do later
};

const char caption00[] PROGMEM = "Set Voltage 1";
const char caption01[] PROGMEM = "Set Voltage 2";
const char caption02[] PROGMEM = "Power level 1";
const char caption03[] PROGMEM = "Power level 2";
const char caption04[] PROGMEM = "Dev 1: Alarm MON";
const char caption05[] PROGMEM = "Dev 1: Alarm TUE";
const char caption06[] PROGMEM = "Dev 1: Alarm WED";
const char caption07[] PROGMEM = "Dev 1: Alarm THU";
const char caption08[] PROGMEM = "Dev 1: Alarm SAT";
const char caption09[] PROGMEM = "Dev 1: Alarm SUN";
const char caption10[] PROGMEM = "Dev 2: Alarm MON";
const char caption11[] PROGMEM = "Dev 2: Alarm TUE";
const char caption12[] PROGMEM = "Dev 2: Alarm WED";
const char caption13[] PROGMEM = "Dev 2: Alarm THU";
const char caption14[] PROGMEM = "Dev 2: Alarm SAT";
const char caption15[] PROGMEM = "Dev 2: Alarm SUN";
const char caption16[] PROGMEM = "Set Date";
const char caption17[] PROGMEM = "Set Time";
const char caption18[] PROGMEM = "Contrast";
const char caption19[] PROGMEM = "Brightness";
const char caption20[] PROGMEM = "Time out";
const char caption21[] PROGMEM = "Sleep";

const char data00[] PROGMEM = "[Value,2]";
const char data01[] PROGMEM = "[Value,4]";
const char data02[] PROGMEM = "[Min,5],[Max,6]";
const char data03[] PROGMEM = "[On:Hour,7],[On:Minute,8],[Off:Hour,9],[Off:Minute,10],[Level,11]";
const char data04[] PROGMEM = "[On:Hour,12],[On:Minute,13],[Off:Hour,14],[Off:Minute,15],[Level,16]";
const char data05[] PROGMEM = "[On:Hour,17],[On:Minute,18],[Off:Hour,19],[Off:Minute,20],[Level,21]";
const char data06[] PROGMEM = "[On:Hour,22],[On:Minute,23],[Off:Hour,24],[Off:Minute,25],[Level,26]";
const char data07[] PROGMEM = "[On:Hour,27],[On:Minute,28],[Off:Hour,29],[Off:Minute,30],[Level,31]";
const char data08[] PROGMEM = "[On:Hour,32],[On:Minute,33],[Off:Hour,34],[Off:Minute,35],[Level,36]";
const char data09[] PROGMEM = "[On:Hour,37],[On:Minute,38],[Off:Hour,39],[Off:Minute,40],[Level,41]";
const char data10[] PROGMEM = "[On:Hour,42],[On:Minute,43],[Off:Hour,44],[Off:Minute,45],[Level,46]";
const char data11[] PROGMEM = "[On:Hour,47],[On:Minute,48],[Off:Hour,49],[Off:Minute,50],[Level,51]";
const char data12[] PROGMEM = "[On:Hour,52],[On:Minute,53],[Off:Hour,54],[Off:Minute,55],[Level,56]";
const char data13[] PROGMEM = "[On:Hour,57],[On:Minute,58],[Off:Hour,59],[Off:Minute,60],[Level,61]";
const char data14[] PROGMEM = "[On:Hour,62],[On:Minute,63],[Off:Hour,64],[Off:Minute,65],[Level,66]";
const char data15[] PROGMEM = "[On:Hour,67],[On:Minute,68],[Off:Hour,69],[Off:Minute,70],[Level,71]";
const char data16[] PROGMEM = "[Day,72],[Month,73],[Year,74]";
const char data17[] PROGMEM = "[Hour,75],[Minute,76]";
const char data18[] PROGMEM = "[Value,77]";
const char data19[] PROGMEM = "[Value,78]";
const char data20[] PROGMEM = "[Value,79]";
const char data21[] PROGMEM = "[Value,80]";


const PROGMEM MenuItem Menu[22] PROGMEM  =
{
  {caption00, data00, 1, SetBrightness},
  {caption01, data01, 1, SetPowerLevel},
  {caption02, data02, 0, SetPowerLevel},
  {caption03, data03, 0, SetPowerLevel},
  {caption04, data04, 0, SetPowerLevel},
  {caption05, data05, 0, SetPowerLevel},
  {caption06, data06, 0, SetPowerLevel},
  {caption07, data07, 0, SetPowerLevel},
  {caption08, data08, 0, SetPowerLevel},
  {caption09, data09, 0, SetPowerLevel},
  {caption10, data10, 0, SetPowerLevel},
  {caption11, data11, 0, SetPowerLevel},
  {caption12, data12, 0, SetPowerLevel},
  {caption13, data13, 0, SetPowerLevel},
  {caption14, data14, 0, SetPowerLevel},
  {caption15, data15, 0, SetPowerLevel},
  {caption16, data16, 0, SetPowerLevel},
  {caption17, data17, 0, SetPowerLevel},
  {caption18, data18, 0, SetContrast},
  {caption19, data19, 1, SetBrightness},
  {caption20, data20, 0, SetPowerLevel},
  {caption21, data21, 0, SetPowerLevel}
};
void setup ()
{
  Serial.begin (115200);
  for (byte i = 0; i < 22; i++) {
    Serial.println(i);
    Serial.println((__FlashStringHelper*)pgm_read_ptr(&Menu[i].Caption));
    Serial.println((__FlashStringHelper*)pgm_read_ptr(&Menu[i].Data));
    Serial.println((pgm_read_byte(&Menu[i].Type) == 1) ? F("True") : F("False"));
    Serial.println((__FlashStringHelper*)menu_function[pgm_read_byte(&Menu[i].Function)]);
  }
}

void loop () {}

It is not necessary to copy the text from PROGMEM to a buffer for printing

yes that's correct

added

    // or if you don't need it in RAM
    // Serial.println( (__FlashStringHelper*)  pgm_read_word(&(descriptionSituation[(uint8_t) pgm_read_byte_near(&(population[i].situation))])));

pgm_read_ptr(address) returns a void* so you need to cast the result depending how you want t to use it.

Great thank to JML and David for your quick responses. Following your sharing, unfortunately, i can not be lazy by putting all things in one place but to declare them separately. Actually, I am looking for some other ways to put things in a single place for sharing a simple approach of doing LCD menu rather than using menu library!
To have all menu strings in PROGMEM, can I store menu string tables ([menu caption], menu item parameter and instruction for EEROM addres], [menu type], [function name]) in a separate include file?
Regarding David sample, many thanks for your effort in separating all things into separated structure (would cost a bit of your time). I make it in excel for quick copy and paste. Functions in the provided code were just sample but in my application they are different (equal to count of menu item - 22) so I would think that an array will be needed!
Please advice me with thanks.

I finally have things be on the PROGMEM with following revisions (but putting the max length of char in struct) and the compiler says ok, no more low memory. The Array still kept intact and in one place!

typedef struct MenuItem
{
  char Caption[16];
  char Data[80];
  bool Type;
  Menu_Function Function;
  //unsigned int Address = 0;// EEPROM Address, do later
};
Sketch uses 11916 bytes (36%) of program storage space. Maximum is 32256 bytes.
Global variables use 476 bytes (23%) of dynamic memory, leaving 1572 bytes for local variables. Maximum is 2048 bytes.

Many thanks to JML and David for your help!

Using a fixed-size char array for the text will work, but can be very wasteful of memory if all the text strings are not close to the same length.

david_2018:
Using a fixed-size char array for the text will work, but can be very wasteful of memory if all the text strings are not close to the same length.

That's absolutely right David. The only things I tried to make is to make it easy to follow for some lazy reason. I am a VBA hobbyist so moving to C++ was a bit too hard! I will share a simple LCD menu for controlling devices using rotary encoder in an easy way so some newbie can make use of it without too much reading!
The Data attribute of the struct will be broken down to array so that user can go to sub menu and edit each of them with a press on the button of the rotary encoder and change its value rather than making the menu be too complicated to handle. On finish the SetAlarm function will fire and set the power level for output device at certain time.

{"Dev 1: Alarm MON","[On:Hour,9],[On:Minute,10],[Off:Hour,11],[Off:Minute,12],[Level,13]",0,SetAlarm},

If what you provide to fill those in are text strings between quotes and you don’t neee to modify the arrays, then Use const char* instead of fixed size char arrays.

 typedef struct MenuItem
{
  const char* Caption;
  const char* Data;
  bool Type;
  Menu_Function Function;
  //unsigned int Address = 0;// EEPROM Address, do later
};

Ans you can still statically define the content as

...
{"Dev 1: Alarm MON","[On:Hour,9],[On:Minute,10],[Off:Hour,11],[Off:Minute,12],[Level,13]",0,SetAlarm},
...

but not using more bytes than necessary

J-M-L:
If what you provide to fill those in are text strings between quotes and you don’t neee to modify the arrays, then Use const char* instead of fixed size char arrays.

 typedef struct MenuItem

{
 const char* Caption;
 const char* Data;
 bool Type;
 Menu_Function Function;
 //unsigned int Address = 0;// EEPROM Address, do later
};



Ans you can still statically define the content as


...
{"Dev 1: Alarm MON","[On:Hour,9],[On:Minute,10],[Off:Hour,11],[Off:Minute,12],[Level,13]",0,SetAlarm},
...



but not using more bytes than necessary

Thanks J-M-L. That was also what i tried to do (see below), similar to your option

typedef struct MenuItem
{
  const char* Caption;
  const char* Data;
  bool Type;
  Menu_Function Function;
};

but the compiler complained of having low dynamic menu!

Sketch uses 10854 bytes (33%) of program storage space. Maximum is 32256 bytes.
Global variables use 1711 bytes (83%) of dynamic memory, leaving 337 bytes for local variables. Maximum is 2048 bytes.
Low memory available, stability problems may occur.

So the fixed size length idea came in!

That does not make any sense at all.

The fixed size arrays by definition will use up more memory than the pointer version which allocates exactly the right amount of bytes.

when you doconst char* data = "Hello";the compiler allocates 5 bytes for the letters H e l l o and one extra byte for a trailing null character, so that's 6. (plus space for the pointer variable itself 2).

when you dochar data[80] = "Hello"; the compiler allocates 80 bytes and copy the letters H e l l o and one extra byte for a trailing null character at the beginning of the array. You wasted 72 bytes if the only thing you do is read and print that cString.

Can you post the code with your definition?

( sometimes the compiler is smart enough to get rid of unused variables).

J-M-L:
That does not make any sense at all.

The fixed size arrays by definition will use up more memory than the pointer version which allocates exactly the right amount of bytes.

when you do

const char* data = "Hello";

the compiler allocates 5 bytes for the letters H e l l o and one extra byte for a trailing null character, so that's 6. (plus space for the pointer variable itself 2).

when you do

char data[80] = "Hello";

the compiler allocates 80 bytes and copy the letters H e l l o and one extra byte for a trailing null character at the beginning of the array. You wasted 72 bytes if the only thing you do is read and print that cString.

Can you post the code with your definition?

( sometimes the compiler is smart enough to get rid of unused variables).

Thank JML, the code is attached, there are quite alot of things . This is not completed yet. The main thing caused low memory is the menu array. Thanks for your help!

LCDMenuRotary.ino (20.2 KB)

Some good findings and some stuff to fix..

get started with a minimal sketch. Try to compile this which is based on your code:

typedef boolean (*Menu_Function)(unsigned int);

boolean SetPowerLevel(unsigned int PowerPtr);
boolean SetBrightness(unsigned int Level = 0);
boolean SetContrast(unsigned int Level = 0);

struct t_MenuItem
{
  char Caption[16];
  char Data[80];
  bool Type;
  Menu_Function Function;
};

static const t_MenuItem Menu[]  PROGMEM =
{
  {"Set Voltage 1", "[Value,0]", 1, SetBrightness},
  {"Set Voltage 2", "[Value,1]", 1, SetPowerLevel},
  {"Power level 1", "[Min,2],[Max,3]", 0, SetPowerLevel},
  {"Power level 2", "[On:Hour,4],[On:Minute,5],[Off:Hour,6],[Off:Minute,7],[Level,8]", 0, SetPowerLevel},
  {"Dev 1: Alarm MON", "[On:Hour,9],[On:Minute,10],[Off:Hour,11],[Off:Minute,12],[Level,13]", 0, SetPowerLevel},
  {"Dev 1: Alarm TUE", "[On:Hour,14],[On:Minute,15],[Off:Hour,16],[Off:Minute,17],[Level,18]", 0, SetPowerLevel},
  {"Dev 1: Alarm WED", "[On:Hour,19],[On:Minute,20],[Off:Hour,21],[Off:Minute,22],[Level,23]", 0, SetPowerLevel},
  {"Dev 1: Alarm THU", "[On:Hour,24],[On:Minute,25],[Off:Hour,26],[Off:Minute,27],[Level,28]", 0, SetPowerLevel},
  {"Dev 1: Alarm SAT", "[On:Hour,29],[On:Minute,30],[Off:Hour,31],[Off:Minute,32],[Level,33]", 0, SetPowerLevel},
  {"Dev 1: Alarm SUN", "[On:Hour,34],[On:Minute,35],[Off:Hour,36],[Off:Minute,37],[Level,38]", 0, SetPowerLevel},
  {"Dev 2: Alarm MON", "[On:Hour,39],[On:Minute,40],[Off:Hour,41],[Off:Minute,42],[Level,43]", 0, SetPowerLevel},
  {"Dev 2: Alarm TUE", "[On:Hour,44],[On:Minute,45],[Off:Hour,46],[Off:Minute,47],[Level,48]", 0, SetPowerLevel},
  {"Dev 2: Alarm WED", "[On:Hour,49],[On:Minute,50],[Off:Hour,51],[Off:Minute,52],[Level,53]", 0, SetPowerLevel},
  {"Dev 2: Alarm THU", "[On:Hour,54],[On:Minute,55],[Off:Hour,56],[Off:Minute,57],[Level,58]", 0, SetPowerLevel},
  {"Dev 2: Alarm SAT", "[On:Hour,59],[On:Minute,60],[Off:Hour,61],[Off:Minute,62],[Level,63]", 0, SetPowerLevel},
  {"Dev 2: Alarm SUN", "[On:Hour,64],[On:Minute,65],[Off:Hour,66],[Off:Minute,67],[Level,68]", 0, SetPowerLevel},
  {"Set Date", "[Day,69],[Month,70],[Year,71]", 0, SetPowerLevel},
  {"Set Time", "[Hour,72],[Minute,73]", 0, SetPowerLevel},
  {"Contrast", "[Value,74]", 0, SetContrast},
  {"Brightness", "[Value,75]", 1, SetBrightness},
  {"Time out", "[Value,76]", 0, SetPowerLevel},
  {"Sleep", "[Value,77]", 0, SetPowerLevel},
};

const size_t MenuElementsCount = sizeof Menu / sizeof Menu[0];

boolean SetPowerLevel(unsigned int PowerPtr) {
  Serial.println(PowerPtr);
  return true;
}

boolean SetBrightness(unsigned int Level) {
  Serial.println(Level);
  return true;
}

boolean SetContrast(unsigned int Level) {
  Serial.println(Level);
  return true;
}

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

void loop() {}

(I fixed the typedef for the function pointer as you forgot to mention there was an unsigned int parameter expected in the callback.)

You'll see the compiler complaining

[color=orange]
.../sketch_nov17b.ino:39:1: warning: initializer-string for array of chars is too long [-fpermissive]
 };
 ^
.../sketch_nov17b.ino:39:1: warning: initializer-string for array of chars is too long [-fpermissive]
.../sketch_nov17b.ino:39:1: warning: initializer-string for array of chars is too long [-fpermissive]
.../sketch_nov17b.ino:39:1: warning: initializer-string for array of chars is too long [-fpermissive]
.../sketch_nov17b.ino:39:1: warning: initializer-string for array of chars is too long [-fpermissive]
.../sketch_nov17b.ino:39:1: warning: initializer-string for array of chars is too long [-fpermissive]
.../sketch_nov17b.ino:39:1: warning: initializer-string for array of chars is too long [-fpermissive]
.../sketch_nov17b.ino:39:1: warning: initializer-string for array of chars is too long [-fpermissive]
.../sketch_nov17b.ino:39:1: warning: initializer-string for array of chars is too long [-fpermissive]
.../sketch_nov17b.ino:39:1: warning: initializer-string for array of chars is too long [-fpermissive]
.../sketch_nov17b.ino:39:1: warning: initializer-string for array of chars is too long [-fpermissive]
.../sketch_nov17b.ino:39:1: warning: initializer-string for array of chars is too long [-fpermissive]
[/color]

that's because 16 is not enough for many of your Captions because you miss space for the trailing null char.
Change 16 into 17 will remove the warnings

struct t_MenuItem
{
  char Caption[17]; // <<==== HERE USE 17
  char Data[80];
  bool Type;
  Menu_Function Function;
};

other note: you don't need to pre-declare the number of elements in your menu array. Let the compiler do the work for you. The array is statically defined, so the compiler can guess the number of elements you want and to get that number, use const size_t MenuElementsCount = sizeof Menu / sizeof Menu[0];

Now the good findings: it seems that the definition with static and PROGMEM does put everything into Flash memory.

try to compile this:

typedef boolean (*Menu_Function)(unsigned int);

boolean SetPowerLevel(unsigned int PowerPtr);
boolean SetBrightness(unsigned int Level = 0);
boolean SetContrast(unsigned int Level = 0);

struct t_MenuItem
{
  char Caption[17];
  char Data[80];
  bool Type;
  Menu_Function Function;
};

static const t_MenuItem Menu[]  PROGMEM =
{
  {"Set Voltage 1", "[Value,0]", 1, SetBrightness},
  {"Set Voltage 2", "[Value,1]", 1, SetPowerLevel},
  {"Power level 1", "[Min,2],[Max,3]", 0, SetPowerLevel},
  {"Power level 2", "[On:Hour,4],[On:Minute,5],[Off:Hour,6],[Off:Minute,7],[Level,8]", 0, SetPowerLevel},
  {"Dev 1: Alarm MON", "[On:Hour,9],[On:Minute,10],[Off:Hour,11],[Off:Minute,12],[Level,13]", 0, SetPowerLevel},
  {"Dev 1: Alarm TUE", "[On:Hour,14],[On:Minute,15],[Off:Hour,16],[Off:Minute,17],[Level,18]", 0, SetPowerLevel},
  {"Dev 1: Alarm WED", "[On:Hour,19],[On:Minute,20],[Off:Hour,21],[Off:Minute,22],[Level,23]", 0, SetPowerLevel},
  {"Dev 1: Alarm THU", "[On:Hour,24],[On:Minute,25],[Off:Hour,26],[Off:Minute,27],[Level,28]", 0, SetPowerLevel},
  {"Dev 1: Alarm SAT", "[On:Hour,29],[On:Minute,30],[Off:Hour,31],[Off:Minute,32],[Level,33]", 0, SetPowerLevel},
  {"Dev 1: Alarm SUN", "[On:Hour,34],[On:Minute,35],[Off:Hour,36],[Off:Minute,37],[Level,38]", 0, SetPowerLevel},
  {"Dev 2: Alarm MON", "[On:Hour,39],[On:Minute,40],[Off:Hour,41],[Off:Minute,42],[Level,43]", 0, SetPowerLevel},
  {"Dev 2: Alarm TUE", "[On:Hour,44],[On:Minute,45],[Off:Hour,46],[Off:Minute,47],[Level,48]", 0, SetPowerLevel},
  {"Dev 2: Alarm WED", "[On:Hour,49],[On:Minute,50],[Off:Hour,51],[Off:Minute,52],[Level,53]", 0, SetPowerLevel},
  {"Dev 2: Alarm THU", "[On:Hour,54],[On:Minute,55],[Off:Hour,56],[Off:Minute,57],[Level,58]", 0, SetPowerLevel},
  {"Dev 2: Alarm SAT", "[On:Hour,59],[On:Minute,60],[Off:Hour,61],[Off:Minute,62],[Level,63]", 0, SetPowerLevel},
  {"Dev 2: Alarm SUN", "[On:Hour,64],[On:Minute,65],[Off:Hour,66],[Off:Minute,67],[Level,68]", 0, SetPowerLevel},
  {"Set Date", "[Day,69],[Month,70],[Year,71]", 0, SetPowerLevel},
  {"Set Time", "[Hour,72],[Minute,73]", 0, SetPowerLevel},
  {"Contrast", "[Value,74]", 0, SetContrast},
  {"Brightness", "[Value,75]", 1, SetBrightness},
  {"Time out", "[Value,76]", 0, SetPowerLevel},
  {"Sleep", "[Value,77]", 0, SetPowerLevel},
};

const size_t MenuElementsCount = sizeof Menu / sizeof Menu[0];

boolean SetPowerLevel(unsigned int PowerPtr) {
  Serial.println(PowerPtr);
  return true;
}

boolean SetBrightness(unsigned int Level) {
  Serial.println(Level);
  return true;
}

boolean SetContrast(unsigned int Level) {
  Serial.println(Level);
  return true;
}

void setup() {
  Serial.begin(115200);
  Serial.print(F("The menu holds ")); Serial.print(MenuElementsCount); Serial.println(F(" elements"));
  for (size_t i = 0; i < MenuElementsCount; i++) {
    Serial.print("Menu #"); Serial.println(i);
    Serial.write('\t'); Serial.println((__FlashStringHelper*) Menu[i].Caption);
    Serial.write('\t'); Serial.println((__FlashStringHelper*) Menu[i].Data);
    Serial.write('\t'); Serial.println(pgm_read_byte(&Menu[i].Type) ? F("TRUE") : F("FALSE"));
  }
}

void loop() {}

the serial monitor at 115200 bauds will show you all your menu entries and RAM seems to be not impacted by the size of the menu. (something might have changed in the way PROGMEM is handled)

I think a reasonable compromise is to keep the char strings in PROGMEM and the structs (containing only pointers and numeric values) in RAM.

This avoids the "PROGMEM Gymnastics" of double indirection.

The strings can all be specified in one place (rather than once to initialize the string and once to initialize the struct's pointers), as desired by OP, by using the F() macro. But, this must be done in a function as F() won't work outside of executable code.

Here's an example. I didn't recreate OP's structs in all their gory as it's too much clutter. The essence of the technique is easy to see here:

void printEntries();

struct Entry {
  Entry(uint8_t i, const __FlashStringHelper* s) : index(i), string(s) {}

  uint8_t index;
  const __FlashStringHelper* string;
};

constexpr size_t numEntries = 10;

Entry *entries[numEntries];

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

  entries[0] = new Entry(0, F("Hello World 0"));
  entries[1] = new Entry(1, F("Hello World 1"));
  entries[2] = new Entry(2, F("Hello World 2"));
  entries[3] = new Entry(3, F("Hello World 3"));
  entries[4] = new Entry(4, F("Hello World 4"));
  entries[5] = new Entry(5, F("Hello World 5"));
  entries[6] = new Entry(6, F("Hello World 6"));
  entries[7] = new Entry(7, F("Hello World 7"));
  entries[8] = new Entry(8, F("Hello World 8"));
  entries[9] = new Entry(9, F("Hello World 9"));

  printEntries();

}

void loop() {
}

void printEntries() {
  for (size_t i = 0; i < numEntries; i++) {
    Serial.print(entries[i]->index);
    Serial.print(": ");
    Serial.println(entries[i]->string);
  }
}

Many thanks to JML for your time dealing with my lengthy stuff. Your works have helped me much to get on with C++.
I will take your approach to my project and will soon share a little work with people!
Again, thanks for all your helps and attention!
The more we talk, the more I learn!
Cheer!

gfvalvo:
I think a reasonable compromise is to keep the char strings in PROGMEM and the structs (containing only pointers and numeric values) in RAM.

This avoids the "PROGMEM Gymnastics" of double indirection.

The strings can all be specified in one place (rather than once to initialize the string and once to initialize the struct's pointers), as desired by OP, by using the F() macro. But, this must be done in a function as F() won't work outside of executable code.

Here's an example. I didn't recreate OP's structs in all their gory as it's too much clutter. The essence of the technique is easy to see here:

void printEntries();

struct Entry {
  Entry(uint8_t i, const __FlashStringHelper* s) : index(i), string(s) {}

uint8_t index;
  const __FlashStringHelper* string;
};

constexpr size_t numEntries = 10;

Entry *entries[numEntries];

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

entries[0] = new Entry(0, F("Hello World 0"));
  entries[1] = new Entry(1, F("Hello World 1"));
  entries[2] = new Entry(2, F("Hello World 2"));
  entries[3] = new Entry(3, F("Hello World 3"));
  entries[4] = new Entry(4, F("Hello World 4"));
  entries[5] = new Entry(5, F("Hello World 5"));
  entries[6] = new Entry(6, F("Hello World 6"));
  entries[7] = new Entry(7, F("Hello World 7"));
  entries[8] = new Entry(8, F("Hello World 8"));
  entries[9] = new Entry(9, F("Hello World 9"));

printEntries();

}

void loop() {
}

void printEntries() {
  for (size_t i = 0; i < numEntries; i++) {
    Serial.print(entries[i]->index);
    Serial.print(": ");
    Serial.println(entries[i]->string);
  }
}

Many thanks to gfvalvo for your sharing! All I want to do is to find out why PROGMEM was provided correctly but it does not store on Flash if fixed size char was not provided in the struct. Also, Keep things be condensed in one place (e.g. menu declaration) so that lazy guys like me and newbie can gain some benefits out of it! I actually do not want to use menulibs in github as they are all too much to put in a small sketch!
I am learning C++ and trying to understand the case for better steps forward then!
Again, many thanks for your sharing!

paulsteigel:
All I want to do it to find out why PROGMEM was provided correctly but it does not store on Flash if fixed size char was not provided in the struct.

Because, as already noted, if all your struct has is a pointer to the string, then what you tried will only keep the struct (and hence just the pointer) in PROGMEM. The string itself will be transferred from PROGMEM to RAM upon initialization. If you want to keep the string in PROGMEM, you need to use some of the ideas presented here.

I really am confused ....about how i can control my Flash and sram

Is this allowed and efficient?

const __FlashStringHelper* somePointerThatIwillCallManyTimes(){
       return F("Hope Only storing once");
}

jimakoskx:
I really am confused ....about how i can control my Flash and sram

Is this allowed and efficient?

const __FlashStringHelper* somePointerThatIwillCallManyTimes(){

return F("Hope Only storing once");
}

If that's all you want to do, just create a global pointer that you can use anywhere:

void printFromPointer(const __FlashStringHelper *p);

const __FlashStringHelper *ptr;

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

  ptr = F("This string will only be created once and will stay in PROGMEM. The the ptr pointer is global, it can be used anywhere");
  Serial.println(ptr);
  delay(500);
  printFromPointer(ptr);
  delay(500);
}

void loop() {
  Serial.println(ptr);
  delay(500);
}

void printFromPointer(const __FlashStringHelper *p) {
  Serial.println(p);
}

jimakoskx:
I really am confused ....about how i can control my Flash and sram

Is this allowed and efficient?

const __FlashStringHelper* somePointerThatIwillCallManyTimes(){

return F("Hope Only storing once");
}

That brings up one of the disadvantage of the F() macro. When text literals are stored in RAM, the compiler will recognize when there are identical text strings and only save a single copy in RAM. Unfortunately, this is not done when using F(), resulting in multiple copies of the same text being stored in PROGMEM. The usual way around this is to store the text in a char array in PROGMEM, then reference the array whenever it needs to be used, although that usually requires casting the reference to __FlashStringHelper*, which your technique of declaring a separate pointer avoids.

gfvalvo:
Because, as already noted, if all your struct has is a pointer to the string, then what you tried will only keep the struct (and hence just the pointer) in PROGMEM. The string itself will be transferred from PROGMEM to RAM upon initialization. If you want to keep the string in PROGMEM, you need to use some of the ideas presented here.

Many thanks to gfvalvo, David and other I understood the case for now, but one of a following up question:
Why if I made these only changes to struct declaration (with fixed size of string) the followed string array was successfully stored in PROGMEM:
This worked: and of course the fixed length can be any number greater than the max length of the following array element.

typedef struct MenuItem
{
  char Caption[16];
  char Data[70];
  bool Type;
  Menu_Function Function;
};

not this

typedef struct MenuItem
{
  char* Caption;
  char* Data;
  bool Type;
  Menu_Function Function;
};

and not this:

typedef struct MenuItem
{
  const char* Caption;
  const char* Data;
  bool Type;
  Menu_Function Function;
};

The following array was kept intact in all three cases:

const MenuItem Menu[]  PROGMEM = 
{  
  {"Set Voltage 1","[Value,1]",1,SetBrightness},
  {"Set Voltage 2","[Value,2]",1,SetPowerLevel},
  {"Power level 1","[Min,3],[Max,4]",0,SetPowerLevel},
  {"Power level 2","[Min,5],[Max,6]",0,SetPowerLevel},
  {"Dev 1: Alarm 1","[Week day:DDD,7],[On:HH,8],[On:MM,9],[Off:HH,10],[Off:MM,11]",0,SetPowerLevel},
  {"Dev 1: Alarm 2","[Week day:DDD,13],[On:HH,14],[On:MM,15],[Off:HH,16],[Off:MM,17]",0,SetPowerLevel},
  {"Dev 1: Alarm 3","[Week day:DDD,19],[On:HH,20],[On:MM,21],[Off:HH,22],[Off:MM,23]",0,SetPowerLevel},
  {"Dev 1: Alarm 4","[Week day:DDD,25],[On:HH,26],[On:MM,27],[Off:HH,28],[Off:MM,29]",0,SetPowerLevel},
  {"Dev 1: Alarm 5","[Week day:DDD,31],[On:HH,32],[On:MM,33],[Off:HH,34],[Off:MM,35]",0,SetPowerLevel},
  {"Dev 1: Alarm 6","[Week day:DDD,37],[On:HH,38],[On:MM,39],[Off:HH,40],[Off:MM,41]",0,SetPowerLevel},
  {"Dev 1: Alarm 7","[Week day:DDD,43],[On:HH,44],[On:MM,45],[Off:HH,46],[Off:MM,47]",0,SetPowerLevel},
  {"Dev 2: Alarm 1","[Week day:DDD,49],[On:HH,50],[On:MM,51],[Off:HH,52],[Off:MM,53]",0,SetPowerLevel},
  {"Dev 2: Alarm 2","[Week day:DDD,55],[On:HH,56],[On:MM,57],[Off:HH,58],[Off:MM,59]",0,SetPowerLevel},
  {"Dev 2: Alarm 3","[Week day:DDD,61],[On:HH,62],[On:MM,63],[Off:HH,64],[Off:MM,65]",0,SetPowerLevel},
  {"Dev 2: Alarm 4","[Week day:DDD,67],[On:HH,68],[On:MM,69],[Off:HH,70],[Off:MM,71]",0,SetPowerLevel},
  {"Dev 2: Alarm 5","[Week day:DDD,73],[On:HH,74],[On:MM,75],[Off:HH,76],[Off:MM,77]",0,SetPowerLevel},
  {"Dev 2: Alarm 6","[Week day:DDD,79],[On:HH,80],[On:MM,81],[Off:HH,82],[Off:MM,83]",0,SetPowerLevel},
  {"Dev 2: Alarm 7","[Week day:DDD,85],[On:HH,86],[On:MM,87],[Off:HH,88],[Off:MM,89]",0,SetPowerLevel},
  {"Set Date","[Day,91],[Month,92],[Year,93]",0,SetPowerLevel},
  {"Set Time","[Hour,94],[Minute,95]",0,SetPowerLevel},
  {"Contrast","[Value,96]",0,SetContrast},
  {"Brightness","[Value,97]",1,SetBrightness},
  {"Time out","[Value,98]",0,SetPowerLevel},
  {"Sleep","[Value,99]",0,SetPowerLevel},
};

Procedure for reading variable from PROGMEM is:

template <typename T> void Progmem_Read (const T * sce, T& dest){ memcpy_P (&dest, sce, sizeof (T)); }

Thank you for your time!