Combine Char with number to point to PROGMEM uint8_t

I'm making a maze game where the maps are saved in PROGMEM as an uint8_t 2d array.
Example:

static const uint8_t PROGMEM
  map1[]=              
  { B00000000,
    B01111100,
    B01000000,
    B01011111,
    B01000000,
    B01111110,
    B00000010,
    B11111000 };
map2[]=              
  { B00000000,
    B01000000,
    B01000000,
    B01011001,
    B01000000,
    B01100110,
    B00000010,
    B11011000 };

I'm stuck on figuring out how to dynamically change the map by pointing to a different map number such as "map1" or "map2" to use when calling Adafruit_GFX with matrix.drawBitmap(0,0,mapHolder,8,8,LED_RED); I have success making a pointer called uint8_t *mapHolder and then making mapHolder=map1; but I want to build up the name "map" and the "number" with two separate variables.

I figure I have to break the problem down in two steps: The first being to combine the word "map" with the number.

char mapName[1];
char mapNum[1];

void setup(){
  mapNum[1]=1;
  strcpy(mapName,"map");
  strcat(mapName,mapNum[1]);
  Serial.println(mapName);
 
}

This code only works if I put "1" where "mapNum[1]" is.

Then the second step would be to convert that char or somehow just cast it into (uint8_t *)mapName?

mapHolder=(uint8_t *)mapName;

Thanks for any help in advance!

Once you have this string in the form of “map1” or “map3”, what are you going to do with it at run time? Not much, I suspect.

If I understand what you’re trying to do, I think you want to create an array of addresses of the maps and select the desired map by indexing into the table and extract the map address based on the index.

You're correct that I wont be doing much work on the "map1" or "map3" variable once its set, but throughout the program I need to reference that map to redraw the board due to having other things drawn over the map. So I want just that one string/ variable with that map I can reference over and over and over again (matrix.drawBitmap(0,0,mapHolder,8,8,LED_RED):wink: without having to have massive waste of program space every time there's a map change.

I think you do understand me correctly regarding the array of addresses of the maps. Similar to this program I found elsewhere on the form. But it doesn't compile correctly so I struggled with understanding how it could help me. Working with array of strings, PROGMEM question - Programming Questions - Arduino Forum

prog_char string_0[] PROGMEM = "MARK";   // "String 0" etc are strings to store - change to suit.
prog_char string_1[] PROGMEM = "FRANCIS";
prog_char string_2[] PROGMEM = "JOHN";
prog_char string_3[] PROGMEM = "ANDREW";
prog_char string_4[] PROGMEM = "ROBERT";
prog_char string_5[] PROGMEM = "SEAN";

prog_char string_6[] PROGMEM = "HAYES";   // "String 0" etc are strings to store - change to suit.
prog_char string_7[] PROGMEM = "LUDWIG";
prog_char string_8[] PROGMEM = "HARRISON";
prog_char string_9[] PROGMEM = "GRAND";
prog_char string_10[] PROGMEM = "SALTER";
prog_char string_11[] PROGMEM = "BOND";

// Then set up a table to refer to your strings.

PROGMEM const char *string_table1[] =  // change "string_table" name to suit
{  
string_0,
string_1,
string_2,
string_3,
string_4,
string_5 };

PROGMEM const char *string_table2[] =  // change "string_table" name to suit
{  
string_6,
string_7,
string_8,
string_9,
string_10,
string_11};

char buffer[30];    // make sure this is large enough for the largest string it must hold


int randNames; // random word taken from the group names
int randLastnames; // random last name
char finalWord[40];  // enough room for all strings together
int myLenght; // lenght of the final word
//int myCentre; // coordinates to place the final word in the centre

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

void loop(){
randNames = random (6); //picks a synonym
randLastnames = random (6); //picks an adjective

finalWord[0] = '\0';          // start with a null string:
strcpy_P(buffer, (char*)pgm_read_word(&(string_table1[randNames])));
strcat (finalWord, buffer); //add third string
strcat(finalWord, " ");   // add second string, the empty space string
strcpy_P(buffer, (char*)pgm_read_word(&(string_table2[randLastnames])));
strcat(finalWord, buffer);   // add first string

myLenght = strlen(finalWord);

Serial.println(finalWord);

delay(1000);
}

2D arrays

+1 karma for doing the research, you're on the correct path. Here is the demo code that will compile, complete with typos ::slight_smile: It appears to work correctly on an Uno.

const char string_0[] PROGMEM = "MARK";   // "String 0" etc are strings to store - change to suit.
const char string_1[] PROGMEM = "FRANCIS";
const char string_2[] PROGMEM = "JOHN";
const char string_3[] PROGMEM = "ANDREW";
const char string_4[] PROGMEM = "ROBERT";
const char string_5[] PROGMEM = "SEAN";

const char string_6[] PROGMEM = "HAYES";   // "String 0" etc are strings to store - change to suit.
const char string_7[] PROGMEM = "LUDWIG";
const char string_8[] PROGMEM = "HARRISON";
const char string_9[] PROGMEM = "GRAND";
const char string_10[] PROGMEM = "SALTER";
const char string_11[] PROGMEM = "BOND";

// Then set up a table to refer to your strings.

char * const string_table1[] PROGMEM =  {
  string_0,
  string_1,
  string_2,
  string_3,
  string_4,
  string_5
};

char * const string_table2[] PROGMEM = // change "string_table" name to suit
{
  string_6,
  string_7,
  string_8,
  string_9,
  string_10,
  string_11
};

char buffer[30];    // make sure this is large enough for the largest string it must hold

int randNames; // random word taken from the group names
int randLastnames; // random last name
char finalWord[40];  // enough room for all strings together
int myLenght; // lenght of the final word
//int myCentre; // coordinates to place the final word in the centre

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

void loop() {
  randNames = random (6); //picks a synonym
  randLastnames = random (6); //picks an adjective

  finalWord[0] = '\0';          // start with a null string:
  strcpy_P(buffer, (char*)pgm_read_word(&(string_table1[randNames])));
  strcat (finalWord, buffer); //add third string
  strcat(finalWord, " ");   // add second string, the empty space string
  strcpy_P(buffer, (char*)pgm_read_word(&(string_table2[randLastnames])));
  strcat(finalWord, buffer);   // add first string

  myLenght = strlen(finalWord);

  Serial.println(finalWord);

  delay(1000);
}

+1 karma right back at you fred. Thanks for fixing that example!
That was the ticket to push me forward a bit.

Here's the final line I came up with:

mapHolder=(uint8_t*)pgm_read_byte(&(map_table[randMap]));

I changed from char to uint8_t, pgm_read_word to pgm_read_byte and of course the variables that point to my table of map names and the randmap numbers.

That pulls the correct data from my PROGMEM and saves it as the "mapHolder" I needed to cleanly change maps in roughly a few dozen places per level every time I try to do a:

matrix.drawBitmap(0,0,mapHolder,8,8,LED_RED);

I'll definitely be sending you a follow up PM when my project is all done to show what you've helped me accomplish.

I thought I had put reading from PROGMEM to bed. After adding more of the maps as seen from my first post:

static const uint8_t PROGMEM
    map_0[]=
    {B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B11011011},
    map_1[]=
    {B00001000,
    B00000011,
    B00010000,
    B00010000,
    B00010000,
    B00010000,
    B00010000,
    B00010000},
    map_2[]=
    {B00000000,
    B01111100,
    B01000000,
    B01011111,
    B01000000,
    B01111110,
    B00000010,
    B11111000},
    map_3[]=
    {B00000000,
    B01111110,
    B01000010,
    B01011010,
    B01011010,
    B01000010,
    B01111110,
    B00000000},
    map_4[]=
    {B00001000,
    B01101010,
    B00101010,
    B10101010,
    B00101010,
    B01101010,
    B00101010,
    B10100010},
    map_5[]=
    {B01000000,
    B10100000,
    B01011111,
    B00100001,
    B00011101,
    B00000001,
    B01111111,
    B00000000},
    map_6[]=
    {B01000101,
    B10010101,
    B11100101,
    B00001100,
    B01111010,
    B00010110,
    B11100110,
    B00001110};

char * const map_table[] PROGMEM =  {
  map_0,
  map_1,
  map_2,
  map_3,
  map_4,
  map_5,
  map_6
};

It ends up corrupting (displaying random pixels on) the LED matrix when running this line:

uint8_t *mapHolder;
byte mapNum=0;

mapHolder=(uint8_t*)pgm_read_byte(&(map_table[mapNum]));
matrix.drawBitmap(0,0,mapHolder,8,8,LED_RED);
matrix.writeDisplay();

I can delete one map at a time from the map_table and its corresponding map array until everything starts working normally again. I thought maybe I could be running out of SRAM. But after running the freeMemory() example shown here: Measuring Memory Usage | Memories of an Arduino | Adafruit Learning System It shows that I have 2295(bits?) free the entire way through my sketch.

This post: Memory and variable corruption issues - Project Guidance - Arduino Forum was my best find at a comparable problem. But they recommend what I thought I was already doing.

I thought I would be able to add dozens of these array "maps".

Any thoughts?

My semi-educated guess: I think it breaks when your table entries wrap a 256 byte boundary. Why? Because you’re only reading a byte, not a word, which only gets the LSB of the table address. The MSB is accidentally the correct starting value.

mapHolder=(uint8_t*)pgm_read_byte(&(map_table[mapNum])); Should be

mapHolder=(uint8_t*)pgm_read_word(&map_table[mapNum]);

Edit:

Not sure if you need the double parens for the & operator. I see it mentioned in the library reference, not sure why it is needed. Better to use it and not need it than need it and...

My semi-educated guess

How can you expect that using a function to read 16 bits, and storing them in an 8 bit variable will produce better results than using a function to read 8 bits and store them in an 8 bit variable?

@coptig: How much memory are you saving by storing the pointers in PROGMEM, instead of SRAM? Is that savings worth the aggravation?

+2 Karma Fred! I switched from pgm_read_byte to pgm_read_word and it worked!! Also tried with and without parens but it still worked either way.

Reading a word and stuffing it into a byte is way over my head and I truly don't understand what you meant about the table addresses, but I'm stoked either way. On to making hundreds of these maps!

You’re not reading a word and stuffing it into a byte, you’re creating a pointer to a table of bytes and the size of that pointer is a word, not a byte.