Progmem won't work with variable for index; only static values

I have a problem with progmem using variables for its indexes. When I insert static values for x and y in the code below (e.g. byte var = pgm_read_byte(&(progbytes[2][10])) );, it accesses the array correctly. But when I revert to the variable values, it retrieves garbage.

#include <avr/pgmspace.h>

PROGMEM const char string_0[] = ":1000000005C1000022C1000020C100001EC1000087";
PROGMEM const char string_1[] = ":100010001CC100001AC1000018C1000016C1000078";
PROGMEM const char string_2[] = ":1000200014C1000012C1000010C100000EC1000088";
PROGMEM const char string_3[] = ":100030000CC100000AC1000008C1000006C1000098";

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

void setup()
{
  delay(1000);
  Serial.begin(19200);  
  Serial.println("Starting");  
    
  Test();  
}

void loop()
{    
}

void Test()
{
  for(int x = 0; x < 3; x++)
  {    
    for(int y = 0; y < 10; y++)
    {      
      byte var = pgm_read_byte(&(progbytes[x][y]));
      Serial.print("x: ");
      Serial.print(x);
      Serial.print("    y: ");
      Serial.print(y);
      Serial.print("     ");
      Serial.println((char)var);
    }
  }
}

Does anyone have a fix?

You’ll have to read from flash twice. Something like:

void Test()
{
  char *p;

  for(int x = 0; x < 3; x++)
  { 
    p = pgm_read_word(&(progbytes[x]));
    for(int y = 0; y < 10; y++)
    {      
      byte var = pgm_read_byte(&(p[y]));
      Serial.print("x: ");
      Serial.print(x);
      Serial.print("    y: ");
      Serial.print(y);
      Serial.print("     ");
      Serial.println((char)var);
    }
  }
}

Thanks gardner for the workaround.
However why is it that I do not have to read twice when I use, say:

 byte var = pgm_read_byte(&(progbytes[2][10]));

but I have to read twice when I use :

int x = 2;
int y = 10;
 byte var = pgm_read_byte(&(progbytes[x][y]));

Is this a bug in the Arduino IDE?
The AVR-Libc documentation does not state that you have to use pgm_read_word prior to using pgm_read_byte.

lemming:
Thanks gardner for the workaround.

What @gardner posted is not a work-around. It is correct.

However why is it that I do not have to read twice when I use, say:

There are two possibilities... 1. dumb luck; 2. optimization (the compiler may recognize what you are trying to do and insert the constant in place of the array reference).

Is this a bug in the Arduino IDE?

No. It's a bug in your code. Use the snippet provided by @gardner.

The AVR-Libc documentation does not state that you have to use pgm_read_word prior to using pgm_read_byte.

http://www.nongnu.org/avr-libc/user-manual/pgmspace.html
...read over the "Storing and Retrieving Strings in the Program Space" section.

Thanks Coding Badly.

What @gardner posted is not a work-around. It is correct.

I can’t find any other examples on the net that require one to use pgm_read_word() before using pgm_read_byte().

I had read that documentation that you refer to (avr-libc: Data in Program Space) several times prior to posting this thread.

The documentation does not suggest that I use both routines to extract bytes. In relation to reading strings it states:

“You probably don’t want to pull the string out of Program Space, byte by byte, using the pgm_read_byte() macro.”

It so happens that I do, so the pgm_read_byte() macro is for me. It does not state that I also need to use the pgm_read_word() first.

The caveat at the bottom of the AVR-Libc documentation states that there is extra overhead incurred by these routines. So it would be best to run just one instead of two. Of course if this is the only way to do it, then one has to live with the overhead. But if it isn’t, then i’d rather not.

The fact that the pgm_read_byte() runs fine with the static values seems to prove that one does not have to use the pgm_read_word() prior to using the pgm_read_byte().

No. It’s a bug in your code.

Please would you point out specifically where the bug is in my code?

EDIT:
I’ve played around a bit more and have the following alternative code working fine (which shows that you don’t have to use pgm_read_word() prior to using the pgm_read_byte()) but it still doesn’t explain why the original code does not work.

#include <avr/pgmspace.h>

PROGMEM const char BitMap1[] = "abcdefgh";
PROGMEM const char BitMap2[] = "ijklmnop";

PROGMEM const char* const progbytes[] = 
{   
  BitMap1,
  BitMap2
};

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

void loop()
{
  for (int j = 0; j < 2; j++)
  {
    for (int i = 0; i < 8; i++)
    {
      byte var = pgm_read_byte(&progbytes[j][i]);
      Serial.println((char)var);
    }
  }
  while (true);  // loop forever  
}

Now I’m just comparing it to my code in my first post in this thread to see why this one works and the other one doesn’t.
Still can’t see it, can you?

lemming:
The documentation does not suggest that I use both routines to extract bytes.

This code is from the link I provided earlier (from the AVR Libc documentation)…

void foo(void)
{
    char buffer[10];
    
    for (unsigned char i = 0; i < 5; i++)
    {
        strcpy_P(buffer, (PGM_P)pgm_read_word(&(string_table[i])));
        
        // Display buffer on LCD.
    }
    return;
}

strcpy_P is functionally equivalent to pgm_read_byte. The difference being strcpy_P extracts all the bytes up to a null terminator and pgm_read_byte extracts just the one byte you specify. The code reads from Flash twice. Once to extract the pointer. A second time to read the actual bytes of the string.

The way you have structured your data, you have to read from Flash twice. In your case, because you are reading individual bytes, you will be using pgm_read_byte.

lemming:

No. It's a bug in your code.

Please would you point out specifically where the bug is in my code?

Done. Three times now. Once by @gardner. Twice by me.

Try this version…

#include <avr/pgmspace.h>

PROGMEM const char BitMap1[] = "abcdefgh";
PROGMEM const char BitMap2[] = "ijklmnop";

PROGMEM const char* const progbytes[] = 
{   
  BitMap1,
  BitMap2
};

 
void setup()
{
  Serial.begin(9600);
  Serial.println( F( "Print an alphabet..." ) );
}

void loop()
{
  for (int j = 0; j < 2; j++)
  {
    for (int i = 0; i < 8; i++)
    {
      byte var = pgm_read_byte(&progbytes[j][i]);
      Serial.println((char)var);
    }
  }
  while (true);  // loop forever  
}

lemming:
I can't find any other examples on the net that require one to use pgm_read_word() before using pgm_read_byte().

The problem is that you haven't declared a two dimensional array - what you've done is declare an array of pointers. This is why you need to do a word read first (to read the pointer) followed by a byte read (to read the byte that is pointed to).