//global:
char *menuText[] = { // pointers to Menu options
(char*)"SET DATE/TIME", // set RTC date and time
(char*)"SET CHKIN HOURS", // set one to three daily checkin hours
(char*)"AUTO ADJUST DST", // enable auto adjust for daylight saving
(char*)"SET BUZZER TIME", // set alarm buzzer time if check-in missed
(char*)"SET QUICK TRIES", // number of immediate tries to send PUSH
(char*)"ONE-HOUR RETRIES", // number of retries on successive hours if PUSH still fails
(char*)"SET WIFI & KEYS", // set WiFi and Pushover keys
(char*)"TESTPUSH TO ME", // send test PUSH notification to my phone
(char*)"TESTPUSH TO ALL", // send to group
(char*)"TRIM RTC SPEED", // adjust RTC oscillation speed
(char*)"COINCELL VOLTAGE", // check voltage of RTC backup coin cell
(char*)"EXIT MENU"
};
// and then later:
lcd.print(menuText[menuIndex]);
I don't remember where I got that, but it works fine. But it puts all the strings in ram. I would like to keep them in flash, and print them from there. So far, I've concluded I need to make everything const, but I don't know how to set up the array as PROGMEM, or do the printing. Any suggestions would be appreciated.
const type array[] PROGMEM = {
"one",
"two",
"tre"
};
I used "flat" arrays, so inside the braces, the commas go away, and it's just one long string, but my strings in one array were all the same length so I can calculate where I need to read.
Then, I read the stored data out in bytes using: pgm_read_byte()
void sequencer(char list[], int columns, int loops) {
for (int lop = 0; lop < loops; lop++) { // count through loops
loopTime(columnSpacing, columns, loops); // display Loop Time
for (int col = 0; col < columns; col++) { // count through row length
for (int row = 0; row < ledCount; row++) { // count through number of LEDs
switch (pgm_read_byte(&list[row * columns + col])) { // locate element row and column location
case '*': digitalWrite(ledPin[row], HIGH); break; // turn LED ON
case '.': digitalWrite(ledPin[row], LOW); break; // turn LED OFF
default: break;
}
}
delay(columnSpacing); // regulate the speed of the sequence
}
}
firstTime = 1; // set first time flag for LoopTime display
}
If all the char arrays are the same length, or nearly the same length, then a 2-dimensional array can be used:
const char menuText[][17] PROGMEM = { // pointers to Menu options
"SET DATE/TIME", // set RTC date and time
"SET CHKIN HOURS", // set one to three daily checkin hours
"AUTO ADJUST DST", // enable auto adjust for daylight saving
"SET BUZZER TIME", // set alarm buzzer time if check-in missed
"SET QUICK TRIES", // number of immediate tries to send PUSH
"ONE-HOUR RETRIES", // number of retries on successive hours if PUSH still fails
"SET WIFI & KEYS", // set WiFi and Pushover keys
"TESTPUSH TO ME", // send test PUSH notification to my phone
"TESTPUSH TO ALL", // send to group
"TRIM RTC SPEED", // adjust RTC oscillation speed
"COINCELL VOLTAGE", // check voltage of RTC backup coin cell
"EXIT MENU"
};
lcd.print((__FlashStringHelper*)&menuText[menuIndex])
If the char arrays vary in length, it is more efficient to have an array of pointers to the char arrays:
const char text00[] PROGMEM = "SET DATE/TIME"; // set RTC date and time
const char text01[] PROGMEM = "SET CHKIN HOURS"; // set one to three daily checkin hours
const char text02[] PROGMEM = "AUTO ADJUST DST"; // enable auto adjust for daylight saving
const char text03[] PROGMEM = "SET BUZZER TIME"; // set alarm buzzer time if check-in missed
const char text04[] PROGMEM = "SET QUICK TRIES"; // number of immediate tries to send PUSH
const char text05[] PROGMEM = "ONE-HOUR RETRIES"; // number of retries on successive hours if PUSH still fails
const char text06[] PROGMEM = "SET WIFI & KEYS"; // set WiFi and Pushover keys
const char text07[] PROGMEM = "TESTPUSH TO ME"; // send test PUSH notification to my phone
const char text08[] PROGMEM = "TESTPUSH TO ALL"; // send to group
const char text09[] PROGMEM = "TRIM RTC SPEED"; // adjust RTC oscillation speed
const char text10[] PROGMEM = "COINCELL VOLTAGE"; // check voltage of RTC backup coin cell
const char text11[] PROGMEM = "EXIT MENU";
const char* const menuText[] PROGMEM = { // pointers to Menu options
text00,
text01,
text02,
text03,
text04,
text05,
text06,
text07,
text08,
text09,
text10,
text11,
};
lcd.print((__FlashStringHelper*)pgm_read_ptr(&menuText[menuIndex]));
Note that this does rely on the lcd library inheriting from print, which has an overload of the print() function that uses __FlashStringHelper* to signify that a char* points to PROGMEM instead of ram.
Yes, if it compiles then the library has a function that takes the __FlashStringHelper*. All the lcd libraries I'm familiar with will work, but there is always the chance someone is using there own code or a more obscure library.
Too many dimensions cause pointer/reference issues.
That is why I (in my link) removed all the commas after the quotes to make the data one-dimension. Only needed a little row/col math to get the right data.
Not as well that casting as char * a string literal is not a good practice - that should be const char * but it’s implicit .
As you usually are not short for flash, I find the option of using fixed size arrays (to the longest string thus)) the easy way forward to define and use the array.
You do have to cast the pointer to __FlashStringHelper* when using it though as the compiler won’t know.
Agreed. But if you really had your heart set on a jagged array in PROGMEM, the below technique works. It's kind of slick, but it does require the pointer array to be in RAM. So, it may not be worth the trouble.
SET DATE/TIME
SET CHKIN HOURS
AUTO ADJUST DST
SET BUZZER TIME
SET QUICK TRIES
ONE-HOUR RETRIES
SET WIFI & KEYS
TESTPUSH TO ME
TESTPUSH TO ALL
TRIM RTC SPEED
COINCELL VOLTAGE
EXIT MENU
Excatly. Actually the String literal (the thing between the double quotes) does have a hidden null terminator and if that does not fill in all the bytes of the array then the compiler (it's a global variable) will pad that with 0.