Error in 2D String array

I am coding for an Arduino Nano. The program is much longer, but I have isolated this bit of code for initializing a 2-dimensional String Array for a menuing system, which is consistently giving me the following error:

Arduino: 1.8.13 (Linux), Board: "Arduino Nano, ATmega328P (Old Bootloader)"

sketch_apr05b:17:15: error: too many initializers for 'const String [3][15]'
};
^
exit status 1
too many initializers for 'const String [3][15]'

const String Menu[3][15] PROGMEM = 
              {{"Set Clocks", "", ""},
               {"", "Clk0", ""},
               {"", "Clk1", ""},
               {"", "Clk2", ""},
               {"", "Save", ""},
               {"Set Sweep", "", ""},
               {"", "Start freq", ""},
               {"", "Stop freq", ""},
               {"", "Step size", ""},
               {"", "Sweep", ""},
               {"", "", "Single"},
               {"", "", "Continuous"},
               {"", "Time (ms)", ""},
               {"", "Save", ""},
               {"Reset", "", ""},
              };

void setup() { 
}

void loop() {
}

What am I doing wrong in initializing this array?

Copy and paste error by the look of it. You have an extra comma right at the end.

I changed this line
const String Menu[15][3] =
and it compiles ok, so I think that's part of it - 15 elements of 3 elements each.
Do you need to add PROGMEM when it's already a const?

(IDE 1.8.13)

I changed it as you say, but now I get the following error:

Arduino: 1.8.13 (Linux), Board: "Arduino Nano, ATmega328P (Old Bootloader)"

/home/rajiv/Apps/Arduino/sketches/TestStringArray/sketch_apr05b/sketch_apr05b.ino: In function '_GLOBAL__sub_D_setup':
sketch_apr05b:1:14: error: variable 'Menu' with dynamic initialization put into program memory area
const String Menu[15][3] PROGMEM =
^
lto-wrapper: fatal error: /home/rajiv/Apps/Arduino/arduino-1.8.13/hardware/tools/avr/bin/avr-gcc returned 1 exit status
compilation terminated.
/home/rajiv/Apps/Arduino/arduino-1.8.13/hardware/tools/avr/bin/../lib/gcc/avr/7.3.0/../../../../avr/bin/ld: error: lto-wrapper failed
collect2: error: ld returned 1 exit status
exit status 1
variable 'Menu' with dynamic initialization put into program memory area

wildbill:
Copy and paste error by the look of it. You have an extra comma right at the end.

It comes as a surprise, but C and a few other languages allow for that comma. It is ignored.

There is logic to the decision, google if you care.

"weird extra comma array initialize"

I've appreciated the latitude it affords from time to time.

a7

Mine did not have PROGMEM in it. As I said, I don't think it is needed when you have already declared them as const.

This seems to do what you are after, if you get the [11] wrong (max strlen) the compiler will let you know

const char Menu[15][3][11] PROGMEM = 
 { {"Set Clocks", "", ""},
  {"", "Clk0", ""},
  {"", "Clk1", ""},
  {"", "Clk2", ""},
  {"", "Save", ""},
  {"Set Sweep", "", ""},
  {"", "Start freq", ""},
  {"", "Stop freq", ""},
  {"", "Step size", ""},
  {"", "Sweep", ""},
  {"", "", "Single"},
  {"", "", "Continuous"},
  {"", "Time (ms)", ""},
  {"", "Save", ""},
  {"Reset", "", ""},
};

void setup() {
  Serial.begin(115200);
  for (int i = 10; i > 0; i--) {
    Serial.print(' '); Serial.print(i);
    delay(500);
  }
  Serial.println();
  for (size_t i = 0; i < 15; i++) {
    String element0(reinterpret_cast<const __FlashStringHelper *>(Menu[i][0]));
    String element1(reinterpret_cast<const __FlashStringHelper *>(Menu[i][1]));
    String element2(reinterpret_cast<const __FlashStringHelper *>(Menu[i][2]));
    Serial.print(element0); Serial.print(" , "); Serial.print(element1); Serial.print(" , "); Serial.print(element2); Serial.println();
  }
}
void loop() {
}

the output is

Set Clocks ,  , 
 , Clk0 , 
 , Clk1 , 
 , Clk2 , 
 , Save , 
Set Sweep ,  , 
 , Start freq , 
 , Stop freq , 
 , Step size , 
 , Sweep , 
 ,  , Single
 ,  , Continuous
 , Time (ms) , 
 , Save , 
Reset ,  ,

PROGMEM .. I don't think it is needed when you have already declared them as const.

I tried removing the PROGMEM and the compile stats when from

with PROGMEM
Sketch uses 3780 bytes (11%) of program storage space. Maximum is 32256 bytes.
Global variables use 202 bytes (9%) of dynamic memory, leaving 1846 bytes for local variables. Maximum is 2048 bytes.

to without PROGMEM
Sketch uses 3778 bytes (11%) of program storage space. Maximum is 32256 bytes.
Global variables use 696 bytes (33%) of dynamic memory, leaving 1352 bytes for local variables. Maximum is 2048 bytes.

@Crossroads, it is the RAM available that gets affected by PROGMEM, as @drmpf shows above.

@drmpf, thanks for the lead. Yes, this appears to work. But why won't it work with the String class, like I was doing, I wonder...

Thank you, all!

RT

Has the String library been updated to allow the string part of a String to stay in flash memory?

Seems a bit pointless - the supposed advantage of a String is to make manipulation easier.

@drmpf how did you print out the array? I'm having a problem with Serial.print() - absolutely nothing prints out.

const char Menu[15][3][11] PROGMEM = 
              {{"Set Clocks", "", ""},
               {"", "Clk0", ""},
               {"", "Clk1", ""},
               {"", "Clk2", ""},
               {"", "Save", ""},
               {"Set Sweep", "", ""},
               {"", "Start freq", ""},
               {"", "Stop freq", ""},
               {"", "Step size", ""},
               {"", "Sweep", ""},
               {"", "", "Single"},
               {"", "", "Continuous"},
               {"", "Time (ms)", ""},
               {"", "Save", ""},
               {"Reset", "", ""}
              };

void setup() {
  Serial.begin(115200);
  delay(10);
  for (int i=1; i>15; i++){
    for (int k=1; k>3; k++){
      Serial.print(*Menu[i, k]);
    }
    Serial.println();
  }
}

void loop() {
 
}

Print expects a char* to point to RAM, not PROGMEM. For PROGMEM, you need to use __FlashStringHelper*.

Your for loops are written incorrectly, i > 15 evaluates to FALSE, which causes the for loop to never be executed, the same applies to k > 3.

const char Menu[15][3][11] PROGMEM =
              {{"Set Clocks", "", ""},
               {"", "Clk0", ""},
               {"", "Clk1", ""},
               {"", "Clk2", ""},
               {"", "Save", ""},
               {"Set Sweep", "", ""},
               {"", "Start freq", ""},
               {"", "Stop freq", ""},
               {"", "Step size", ""},
               {"", "Sweep", ""},
               {"", "", "Single"},
               {"", "", "Continuous"},
               {"", "Time (ms)", ""},
               {"", "Save", ""},
               {"Reset", "", ""}
              };

void setup() {
  Serial.begin(115200);
  delay(10);
  for (int i=0; i<15; i++){
    for (int k=0; k<3; k++){
      Serial.print((__FlashStringHelper*)Menu[i][k]);
    }
    Serial.println();
  }
}

void loop() {
 
}

Using the three dimensional array is very wasteful of memory, you are allocating 11 bytes of memory for each string, including the empty string "" . Better method is to use an array of char*, but that requires a bit more work to actually put the string text into PROGMEM, and I don't have time this morning to make an example for that.

what's the purpose of the empty text "" if this is meant to be hardwired in flash memory?

do you actually mean to have only the label in PROGMEM and the rest of the text can vary ? in that case you could define it this way

const char s0[] PROGMEM = "Set Clocks";
const char s1[] PROGMEM = "Clk0";
const char s2[] PROGMEM = "Clk1";
const char s3[] PROGMEM = "Clk2";
const char s4[] PROGMEM = "Save";

struct t_data {
  String firstString;
  const char * label;
  String secondString;
} menu[] = {
  {"", s0, ""},
  {"", s1, ""},
  {"", s2, ""},
  {"", s3, ""},
  {"", s4, ""},
};
const byte nbEntries = sizeof menu / sizeof menu[0];

void setup() {
  Serial.begin(115200);
  for (byte i = 0; i < nbEntries; i++) {
    Serial.println((__FlashStringHelper*) menu[i].label);
  }

  // assigning some value to the dynamic part (would be best to reserve some memory for the Strings)
  menu[0].firstString = "Hello World";
  Serial.print(menu[0].firstString);
}

void loop() {}

(was lazy and only took the first few entries, you get the idea)

Serial Monitor (@ 115200 bauds) will show

[color=purple]
Set Clocks
Clk0
Clk1
Clk2
Save
Hello World
[/color]

@J-M-L the array is a hierarchical menu structure for a 16x2 LCD display...

is the menu dynamic or you know about it at compile time?

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.