PROGMEM problems.

My project has several files which I maintain with RCS. I want to be able to display the file names and versions on a LCD or the serial console.

Each file is headed with (typically):

const char menu_rcs[] PROGMEM = {"$Id: file1.pde,v 1.2 2012/03/22 17:40:06 jim Exp $"};

and each RCS constant pointed to in an array:

const char* rcs_strings[] PROGMEM = {file1, file2, file3 etc.};

I then want to copy just the file name and version to a buffer:

char buffer[100];

with lines like:

buffer[buffer_index++] = pgm_read_byte(rcs_strings[array_index][string_index++]);

and then print the buffer.

But all I get is rubbish in the buffer[]!

I've got the logic of the program right, as when I remove the PROGMEM stuff and do it all in flash it works fine. Can anyone suggest where it's going wrong, please.

Also:

I've seen in a PROGMEM tutorial the PSTR macro, to print a string directly from program memory. It was demonstrated by:

LCD_puts ( PSTR (" Program Memory String ") );

This looks useful, but doesn't work with:

Serial.println( PSTR (" Program Memory String ") );

Why is this, please?

I'de be grateful for some help here.

Jim

jimford: Each file is headed with (typically):

const char menu_rcs[] PROGMEM = {"$Id: file1.pde,v 1.2 2012/03/22 17:40:06 jim Exp $"};

You've made an array with one item in it there. Maybe:

const char menu_rcs[] PROGMEM = "$Id: file1.pde,v 1.2 2012/03/22 17:40:06 jim Exp $";

Serial.println( PSTR (" Program Memory String ") );

Under version 1.0 of the IDE just do:

Serial.println( F (" Program Memory String ") );

Thanks for the replies.

I seem to be totally missing something regarding PROGMEM, and can’t get the most simple operation to work.

Whilst the following code compiles without a snivel, it just doesn’t work - all I get printed from the following is rubbish:

#include <avr/pgmspace.h>

void setup(){

Serial.begin(9600);

const char greet[] PROGMEM = "Hi There!";

char character = char(pgm_read_byte(greet[0]));

Serial.println(character); // I'm expecting an 'H' here, but all I get is a square!
}

void loop(){}

I’d be grateful to anyone who can indicate where I’m going wrong, please!

Jim

In my opinion the string greet should be static (or define it globally). I have no idea how the compiler will treat your version. And maybe you could use strcpy_P() to get your strings.

In pgmspace.h it is defined as

extern char *strcpy_P(char *, PGM_P);

but it might be your Arduino version doesn't know this function, I am using a newer toolchain at the moment, just give it a try.

You are dereferencing too soon. So you read the “H” and then try to get something from PROGMEM at address H, not the address where H is. Also don’t put the PROGMEM inside a function. This prints H:

#include <avr/pgmspace.h>

const char greet[] PROGMEM = "Hi There!";

void setup(){
  Serial.begin(115200);
  char character = pgm_read_byte(greet);
  Serial.println(character); 
}

void loop(){}

And this prints the whole thing:

#include <avr/pgmspace.h>

const char greet[] PROGMEM = "Hi There!";
  
/* Print a string from Program Memory directly to save RAM */
void printProgStr (const char * str)
{
  char c;
  if(!str) return;
  while((c = pgm_read_byte(str++)))
    Serial.print(c);
} // end of printProgStr

void setup(){
  Serial.begin(115200);
  printProgStr (greet);
}

void loop(){}

Thanks a lot Nick.

My main problem was that I had the PROGMEM string inside setup().

I quoted the array subscript because I wanted to access individual characters, so it really should have had:

char character = pgm_read_byte(&(greet[j])); where j is the subscript for the character I’m interested in. I’d omitted the reference operator!

All this is to get a handle on what I really want to do - that is to access individual characters in an array of strings - this sort of thing:

#include <avr/pgmspace.h>

/* Program to extract just the file name and version number from RCS strings
and display in the form of:

menu.pde:1.2
meter.pde:1.1
current.pde:1.3
temp.pde:1.3 

*/

char buffer[100];

const char menu_rcs[] PROGMEM = \
  "$Id: menu.pde,v 1.2 2012/03/22 17:40:06 jim Exp $";
const char meter_rcs[] PROGMEM = \
  "$Id: meter.pde,v 1.1 2012/03/22 17:40:06 jim Exp $";
const char current_rcs[] PROGMEM = \
  "$Id: current.pde,v 1.3 2012/03/22 17:40:06 jim Exp $";
const char temp_rcs[] PROGMEM = \
  "$Id: temp.pde,v 1.3 2012/03/22 17:40:06 jim Exp $";

/* Array of pointers to above, also in progmem. 
NULL to stop from falling off the end of the array.
*/
const char* rcs_strings[] PROGMEM = {menu_rcs, meter_rcs, current_rcs, temp_rcs, NULL};

void setup() {

  Serial.begin(9600);

  int array_index = 0; // Counter for rcs_strings array.
  int buffer_index = 0;  
  int i = 0;

  while (pgm_read_word(&rcs_strings[i++])) { // Do for each rcs string.

    int string_index = 5; // Index of first file name char.
    char c; 
    while((c = pgm_read_byte(pgm_read_word(&rcs_strings[array_index][string_index++]))) \
	  != ',') buffer[buffer_index++] = c; // Copy string up until ','.
   
    string_index += 3; // Skip ",v " and point to 1st char of version number.
    buffer[buffer_index++] = ':'; // Write a separating colon.
    // String_index now pointing to first digit in version number

    while((c = pgm_read_byte(pgm_read_word(&rcs_strings[array_index][string_index++]))) \
	  != ' ') buffer[buffer_index++] = c; // Copy string until next space.
    
    buffer[buffer_index++] = '\n';  // write newline and increment to next location
    array_index++; // Select next version string.
  }
    buffer[buffer_index] = '\0'; // All done - write terminating null.
    // Show what we've done.
    Serial.println(buffer);
}

void loop() {}

It’s the inner ‘while’ loops that have got me stumped:

“while((c = pgm_read_byte(pgm_read_word(&rcs_strings[array_index][string_index++])))”

I’m hoping to get the address of the string character with “pgm_read_word(&rcs_strings[array_index][string_index++]” and use that to read the character at that location with the outer “pgm_read_byte” - but its not working. It’s pretty ugly and the referencing and de-referencing has got my head swimming!

I’m going to have a lie down now!

Jim

Do the pgm_read_word once - it is just too complicated with two subscripts like that.

Thanks again Nick.

It's still got me beat - the lie down didn't help!

You mean from:

"while((c = pgm_read_byte(pgm_read_word(&rcs_strings[array_index][string_index++])))"

to:

"while((c = pgm_read_word(&rcs_strings[array_index][string_index++]))"

which doesn't work.

I'm starting to get desperate and using every combination of pgm_read_word, pgm_read_byte, &, (char*) and brackets that I can think of!

Jim

Hang on!

while((c = pgm_read_word(&rcs_strings[array_index][string_index++]))" Does work - 'c' returns the characters in the strings OK, but I've got some extreme weirdness going on in the outer loop!

Embedding it into a loop breaks it. Basically I can hard wire any numbers I like into the array subscript and get sensible results, but as soon as I set up an outer loop to step through the array of strings it falls over.

I'll try to set up an example stripped own to the bare bones to illustrate it!

Jim

I’ve stripped my problem down to the bare bones and it appears to be related to a simple loop as shown in this example:

The following program just prints out character by character parts of the strings. I want to be able to do this rather than do a string copy, because I need to select just the elements that I need.

With the outer loop commented out, the following works and I can ‘hard-wire’ various indexes (within range) for the string_table, and it works fine. If I uncomment the outer loop I just get rubbish printed - not even sensible values for the index ‘i’.

I can’t understand it - it’s as if the outer loop somehow corrupts the inner one.

#include <avr/pgmspace.h>

const char string_0[] PROGMEM = "String 0";
const char string_1[] PROGMEM = "String 1";
const char string_2[] PROGMEM = "String 2";
const char string_3[] PROGMEM = "String 3";

const char *string_table[] PROGMEM =  \
{   
  string_0,
  string_1,
  string_2,
  string_3
};

char buffer[40];

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

  int i = 0; // Counter for string_table.
  
  // while(i < 4) {

    Serial.println(i); // Show position in string_table.

    int j = 2; // Counter for characters in strings (starting at 'r').
   
    char c; 
    
    while((c = pgm_read_word(&string_table[i][j++])))
      Serial.print(c); // Show string character by character, starting at 'r'.
    
    //i++;
  //}
}

void loop() {}

I’m not sure where to go to next. I might just have to copy each string into a buffer in flash memory in turn, and do the substring stuff there, if it’s a restriction in using PROGMEM.

Jim

As I was saying, it’s too complicated trying to get the address, and the byte within the string all in one line. This breaks it up, and works:

#include <avr/pgmspace.h>

const char string_0[] PROGMEM = "String 0";
const char string_1[] PROGMEM = "String 1";
const char string_2[] PROGMEM = "String 2";
const char string_3[] PROGMEM = "String 3";

const char *string_table[] PROGMEM =  
{   
  string_0,
  string_1,
  string_2,
  string_3
};

/* Print a string from Program Memory directly to save RAM */
void printProgStr (const char * str)
{
  char c;
  if(!str) return;
  while((c = pgm_read_byte(str++)))
    Serial.print(c);
} // end of printProgStr


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

  for (int i = 0; i < 4; i++)
    {
    Serial.println(i); // Show position in string_table.
    
    // get the string address in PROGMEM
    const char * p = (const char *) pgm_read_word (&string_table[i]);
    
    // print the string from PROGMEM
    printProgStr (p);
    
    // new line
    Serial.println ();
    } // end of for
}  // end of setup

void loop() {}

Output:

0
String 0
1
String 1
2
String 2
3
String 3

Thanks again Nick, you've been a great help.

As you demonstrated, the answer was to break the code down into smaller chunks.

Jim