Printing from PROGMEM in simplest fashion?

I have a number of character arrays (strings) that I need to store in PROGMEM to save RAM. I can't use F() all the time because I need to pass a pointer to the string and then possibly print it inside a subroutine. I searched the forum and found several threads on the same general subject. From them I put together code which works, but I wonder if it can be made more simple.

For instance the defined PGMT statement uses a "reinterpret_cast" which I have never seen before and it seems clunky.

I have not been able to make the data types nor the cast less wordy. Is there a better way to do it and still have the function able to print the string too?

/*
Tests transfer of progmem "string" to a different function and then print it over the serial port.
*/
#include <avr/pgmspace.h>

#define PGMT( pgm_ptr ) ( reinterpret_cast< const __FlashStringHelper * >( pgm_ptr ) )

/**********************************************************
 *  While I try to avoid global variables, it 
 *  makes sense to have PROGMEM varialbe be global
 *  so that all subroutines and modules can use them
 * 
 *  Keyword "static" not required for global variables,
 *  But it is REQUIRED if it is defined in a function/subroutine
 **********************************************************/
static const char message_1[] PROGMEM = "THIS IS FLASH";
static const char message_2[] PROGMEM = "THIS IS FLASH ALSO";
   
void setup() {
  static const char message_1[] PROGMEM = "THIS IS FLASH";

  Serial.begin(9600);

     char message_0[] = "This is RAM";
     Serial.println(message_0);
     Serial.println(PGMT(message_1));
     printprogmem (PGMT(message_2));
}


void loop() {
}


void printprogmem ( const __FlashStringHelper* mess) {
    Serial.println (PGMT(mess));
}

-Tony

"Is there a better way to do it and still have the function able to print the string too?"

Make a second function with same name but take a different parameter like so:

void printprogmem (char *mess) {
    Serial.println (mess);
}

This is called function overloading. This is not necessary better (in fact this is frown upon in 5th gen languages). The better way to do this is polymorphism.

Make a second function with same name but take a different parameter like so:

Thanks for the reply. Actually, my question was not concerned about being able to print different datatypes with a function of apparently the same name.

Instead, I was not clear enough. I had to use what seems to me to be rather opaque and verbose casting to get the data to be passed in a manner recognizable to the compiler as being in PROGMEM space and also be able to be printed using the standard serial.print() and serial.println() functions from within my subroutine.

I'd like to see if I can get a less verbose and less opaque statement to achieve the exact same result as what I have in my code.

The defined cast, and the "flashstringhelper" seem rather clunky for the apparently simple task of telling the compiler that this pointer is to flash space rather than RAM. Oh, and as I said before, it needs to work with the serial.print() functions too.

-Tony

When something is stored in flash, it needs to be read byte-by-byte. That is what the flashstringhelper does. A simple, clean way of printing something from flash is to use the F macro:

Serial.println(F("This is coming from flash"));

The defined cast, and the "const __FlashStringHelper *" seem rather clunky for the apparently simple task of telling the compiler that this pointer is to flash space rather than RAM. Oh, and as I said before, it needs to work with the serial.print()

The fundamental problem is that these are the same types, as far as the compiler is concerned:

    const char string1[]         = "FLASH and RAM";
    const char string2[] PROGMEM = "FLASH only";

Unfortunately, the MCU cannot access these memory locations with the same instructions, due to the MCU's Harvard Architecture. Therefore, the compiler must generate different instructions to access these two strings.

So how can the coder distinguish these variables to the compiler?

1) With different types (via casting):

    Serial.print( string1 );                               // overloaded print( const char [] ) called
    Serial.print( (const __FlashStringHelper *) string2 ); // overloaded print( const __Flash... ) called

If you look at the implementation of these two ::print methods (here and here), you will see that they use two different techniques to get each character of these strings:

size_t Print::print(const __FlashStringHelper *ifsh)
{
  PGM_P p = reinterpret_cast<PGM_P>(ifsh);
  size_t n = 0;
  while (1) {
    unsigned char c = pgm_read_byte(p++);   <-- special MCU instructions to read FLASH
    if (c == 0) break;
    if (write(c)) n++;
    else break;
  }
  return n;
}

size_t Print::print(const char str[])
{
  return write(str);  <-- this is inline in Print.h
}
    size_t write(const char *str) {                  <-- RAM string...
      if (str == NULL) return 0;
      return write((const uint8_t *)str, strlen(str));  <-- uses RAM-oriented string functions
    }

2) By calling differently-named routines:

    Serial.print( strlen( string1 ) );   // prints length of a RAM string
    Serial.print( strlen_P( string2 ) ); // prints length of a FLASH string

Personally, I prefer to use a preprocessor macro to make things more readable:

#define CF(x) ((const __FlashStringHelper *)x)

    ...

  Serial.print( CF(string2) );

Cheers,
/dev