Printing char array from array of structs in progmem to serial (I apologize if this is a c++ question, I'm too green to know if this is unique to the Arduino IDE)

I've moved an array of structs into my PROGMEM because I was running into memory issues:

#define HU_ADDR                 0x190
#define ALL_AUDIO_ADDR          0x1FF
#define MY_ADDR                 0x360
#define UNKNOWN_ADDR            0xFFF

typedef byte MessageID;
enum {
  ACT_UNKNOWN,
  ACT_POWERON,
  ACT_POWEROFF,
  ACT_PASSIVEOFF_0,
  ACT_PASSIVEOFFRARE_0,
  ACT_PASSIVEONRARE,
  ACT_PASSIVEON_0,
  ACT_KEYTURN,
  ACT_PRESSDISC,
  ACT_PRESSAM,
  ACT_CD,
  ACT_STATIONFOUND,
  ACT_ENABLECD,
  ACT_CD2
};

struct MessageDef {
  MessageID ID;
  word MasterAddr;
  word SlaveAddr;
  byte PayloadLength;
  word Payload[24];
  char Description[20];
};

const MessageDef MessageTable[] PROGMEM = {
  {ACT_POWERON,           HU_ADDR, ALL_AUDIO_ADDR, 13,  {0x00, 0x00, 0x00, 0x00, 0x100, 0x100, 0x00, 0x81, 0x100, 0x01, 0xF1, 0x31, 0x60},                    "Tune Knob"},
  {ACT_POWEROFF,          HU_ADDR, UNKNOWN_ADDR, 13,    {0x00, 0x60, 0x31, 0xF1, 0x00, 0x00, 0x81, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00},                       "off?"},
  {ACT_PASSIVEOFF_0,      HU_ADDR, UNKNOWN_ADDR, 3,     {0x46, 0x01, 0x11},                                                                                   "Passive: Weak"},
  {ACT_PASSIVEOFFRARE_0,  HU_ADDR, UNKNOWN_ADDR, 4,     {0x60, 0x45, 0x01, 0x11},                                                                             "Passive: Strong"}, 
  {ACT_PASSIVEONRARE,     HU_ADDR, UNKNOWN_ADDR, 4,     {0x100, 0x20, 0x01, 0x11},                                                                            "Passive: Fierce"},
  {ACT_PASSIVEON_0,       HU_ADDR, ALL_AUDIO_ADDR, 16,  {0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x100, 0x80, 0xF1, 0x31, 0x74},    "Vol Knob"},
  {ACT_KEYTURN,           HU_ADDR, UNKNOWN_ADDR, 11,    {0x31, 0x62, 0x31, 0xF1, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0x00},                                   "Key turned"}, 
  {ACT_PRESSDISC,         HU_ADDR, ALL_AUDIO_ADDR, 4,   {0x00, 0x9F, 0x31, 0x62},                                                                             "No DISC available"},
  {ACT_PRESSAM,           HU_ADDR, ALL_AUDIO_ADDR, 24,  {0x00, 0x60, 0x31, 0xF3, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x30, 0x00, 0x58, 0x00, 0x6C, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, "AM/FM Switch"},
  {ACT_CD,                HU_ADDR, ALL_AUDIO_ADDR, 11,  {0x31, 0x62, 0x31, 0xF1, 0x01, 0x30, 0x01, 0xFF, 0xFF, 0xFF, 0x00},                                   "CD switch?"},
  {ACT_STATIONFOUND,      HU_ADDR, ALL_AUDIO_ADDR, 13,  {0x00, 0x00, 0x00, 0x04, 0x00, 0x100, 0x00, 0x81, 0x100, 0x01, 0xF1, 0x31, 0x60},                     "FM station found"},
  {ACT_ENABLECD,          HU_ADDR, ALL_AUDIO_ADDR, 13,  {0x00, 0x00, 0x00, 0x04, 0x00, 0x77, 0x00, 0x81, 0x00, 0x00, 0xF1, 0x31, 0x60},                       "Enter CD mode"},
  {ACT_CD2,               HU_ADDR, ALL_AUDIO_ADDR, 4,   {0x62, 0x45, 0x01, 0x11},                                                                             "CD message 2?"}
};

const byte MessageTableSize = sizeof(MessageTable)/sizeof(MessageDef);


I've adjusted accordingly in my code segments that reference this data using pgm_read_byte_near() for example. However, I'm not really sure how to get the Description field anymore. When MessageTable was in dynamic memory I could just use:

Serial.println(pgm_read_word(MessageTable[msgid].Description))


But now that seems to be printing... maybe the addresses of the data? Instead of printing out the character array I need to see. How do I parse this more correctly?
You can use the strncpy_P(char* dest, const char* src, size_t maxLen); function which will copy a zero-terminated string of at max maxLen bytes from PROGMEM into RAM. Then print the string from RAM. 

Example code:

//We have global buffer in RAM. +1 for 0 termination
char descBuf[20 + 1];

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

    //Copy the description string of the first message in the buffer
    strncpy_P(descBuf, MessageTable[0].Description, 20);
    //Print it
    Serial.print("Description: ");
    Serial.println(descBuf);
}


Together with your code above, that prints on my Arduino Nano as:

Description: Tune Knob


If you don't want to have a 21 byte big buffer in RAM (or there are larger strings you wish to print), you can also use the pgm_read_byte function to read and print the string characters byte-wise. Example:

Serial.print("Description: ");
char c = 0;
for(size_t i=0; i &lt; strlen_P(MessageTable[0].Description); i++) {
    c = pgm_read_byte(MessageTable[0].Description + i);
    Serial.print(c);
}
Serial.println();


For further reference, see the Arduino documentation on PROGMEM: https://www.arduino.cc/reference/en/language/variables/utilities/progmem/