I want to read a string from a struct stored in Arduino PROGMEM:
struct commandCode {
int code;
const char *name;
};
const PROGMEM commandCode commands[LAST_COMMAND] = {
{ CMD_DEMO, "DEMO" } ,
{ CMD_STOP, "STOP"} ,
{ CMD_FORWARD, "FORWARD"},
{ CMD_BACKWARD, "BACKWARD"},
{ CMD_TURN_LEFT, "TURN LEFT"},
{ CMD_TURN_RIGHT, "TURN RIGHT"},
{ CMD_WAIT, "WAIT"},
{ CMD_WAIT_DONE, "WAIT DONE"},
};
This code prints the string just fine:
void CommandCodes::show() {
Serial.print(LAST_COMMAND);
Serial.println(" Comands Defined:");
for (int i = FIRST_COMMAND; i < LAST_COMMAND; i++) {
CommandCodes::commandCode cmd = commands[i];
showCommand(cmd);
}
}
void CommandCodes::showCommand(commandCode cmd) {
if (cmd.code > FIRST_COMMAND) {
Serial.print(F("["));
Serial.print(cmd.code);
Serial.print(F("] "));
Serial.println(cmd.name);
}
}
This code bombs and restarts the program:
const char* CommandCodes::name(int code) {
for (int i = FIRST_COMMAND; i < LAST_COMMAND; i++) {
CommandCodes::commandCode cmd = commands[i];
if (cmd.code == code) {
return cmd.name;
}
}
return NULL;
}
What is the code to return a pointer to cmd.name
?
As the structure only contains a pointer, not the string data, the strings are still stored in RAM.
Also you aren't reading from PROGMEM when you access the data, the fact it's working in certain situations is just luck, but it's still incorrect.
To place all the data in PROGMEM, you'll need to allocate space inside the struct for it. As the largest string is 11 chars + null you can make the length 12.
As the internals of each struct are in PROGMEM you need to read them using special functions. You cannot read them directly.
This also means you cannot copy an item like you have done:
CommandCodes::commandCode cmd = commands[i];
But you can use a reference.
const commandCode &cmd = commands[i];
However, like I mentioned above, the elements of the referenced struct still need to be accessed properly.
For an integer, you need to use
pgm_read_word
. For the strings, you can trick the Serial class into printing it for you as it handles flash strings (like where you use theF()
macro). This can be done by casting the pointer to aconst __FlashStringHelper*
.Here is a working sketch showing how to access each part properly. Give it a test and try and work out what I've done. I'm sure you'll have some questions, so just add them to the comments of this answer and I'll update my answer for you.