简体   繁体   中英

Reading string from struct in Arduino PROGMEM

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.

struct commandCode {
  int code;
  const char name[12];
};

const commandCode commands[] PROGMEM = {
  { 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"}
}

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 the F() macro). This can be done by casting the pointer to a const __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.

struct commandCode {
  int code;
  const char name[12];
};

enum COMMANDS{
  CMD_DEMO,
  CMD_STOP,
  CMD_FORWARD,
  CMD_BACKWARD,
  CMD_TURN_LEFT,
  CMD_TURN_RIGHT,
  CMD_WAIT,
  CMD_WAIT_DONE,
};

const commandCode commands[] PROGMEM = {
  { 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"}
};

#define FIRST_COMMAND 0
#define LAST_COMMAND sizeof(commands) / sizeof(*commands)
#define FSH (const __FlashStringHelper*)  //A helper to allow printing the PROGMEM strings.

void show() {
    for (int i = FIRST_COMMAND; i < LAST_COMMAND; i++) {
        const commandCode &cmd = commands[i];
        showCommand(cmd);
    }
}

void showCommand(const commandCode &cmd) {
    if ( pgm_read_word( &cmd.code ) > FIRST_COMMAND) {
        Serial.print(F("["));
        Serial.print( pgm_read_word( &cmd.code ) );
        Serial.print(F("] "));
        Serial.println( FSH( cmd.name ) );
    }
}

const char* name(int code) {
    for (int i = FIRST_COMMAND; i < LAST_COMMAND; i++) {
        const commandCode &cmd = commands[i];
        if (pgm_read_word(&cmd.code) == code) {
            return cmd.name; //As cmd.name resolves to a pointer it can be passed back as is.
            //However to use the 'pointed to data' it will have to be accessed properly.
        }
    }
    return NULL;
}

void setup() {
  Serial.begin(9600);

  Serial.println("Show test");
  show();

  Serial.println("Name test");
  for (int i = FIRST_COMMAND; i < LAST_COMMAND; i++) {
    Serial.println( FSH( name(i) ) );
  }
  Serial.println("Done");
}

void loop() {}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM