[Solved] Snprintf char array member

Hi there, Merry Christmas to all of you.
I'm making a birthday calendar and have problems with three lines of code inside a for loop.
First line prints the right name, but I can't make it into a file name for getting it from flash.

  const char *const birthdayName[] PROGMEM = { "Miranda" }; // one of many
  char fileName[13];

for(...
  Serial.println(birthdayName[i]); // prints Miranda
  snprintf(fileName, 12, "/%c.bin", birthdayName[i]);
  Serial.println(fileName); // prints /x.bin instead of "/Miranda.bin"

Where x shows a random single character.
Any pointers?
Leo..

Shouldn't at %c format specifier be a %s as you are printing a string rather than a single character

Thank you. That could be it.
Copied the formatter from another part of the code without thinking.
Too much Christmas port wine could be the cause.
Will post the results tomorrow.
Leo..

Please post a complete example that will compile.

This is creating a single element array of const char*, birthdayName, which is stored in PROGMEM, but the actual text "Miranda" is stored in ram.

This is wrong, but likely works because the compiler is seeing i as a constant, and is optimizing away the array in PROGMEM.

As for the snprintf(), in light of the error pointed by @UKHeliBob, not sure how the compiler is handling it.

Do you need to add the suffix \0 to the array ("Miranda\0")?

Add a second element to the array, for example, "Wawa" or "Wawa\0" and try to print out just the first element ("Miranda\0"). The size difference of the elements might also give clues to what is needed.

The Arduino reference pages show how PROGMEM is correctly used on AVR type Arduinos.

The PROGMEM keyword is ignored (concept not implemented) on some other types of Arduinos, like ESP32.

Post a complete code that people can download, modify, and test before proposing solutions.

const char birthdayName[] [9] = { "Samantha", "Tim", "Miranda"...etc. }; // compiles
const char *const birthdayName[] = { "Samantha"... etc.}; // compiles too

Not sure what the difference is and what to use.
No access to the hardware until tomorrow. (XIAO ESP32-S3).
Leo..

PROGMEM is not applicable to that processor.

The first creates a 2-dimensional array, with each element of the array being composed of an array of 9 char.
The second creates an array of pointers to individual char arrays.

The first is somewhat easier to use, particularly when PROGMEM is needed, but other than that not a lot of difference in your application. The first tends to waste a lot of memory if the texts are different lengths, with only a few very long and the majority very short. The second can waste memory if all or nearly all of the texts are the same length, since storage is needed for the pointer as well as the text. Not really a concert with a processor with as much memory as an ESP32.

Thanks for the explanation.
Names are maximum 8 charcters long, to stay within the 8.3 file name scheme.
Maybe 50 entries or so for now. I will see how much is actually wasted.
The ESP32-S3 used has 8MB flash, but I am going to Use a custom partition scheme, to store more images. Best not to waste program flash.
Leo..

I would use the 2-dimensional array, since an ESP32 pointer is 4 bytes. A 50 element array of char* would be 200 byte, in addition to what is needed for the names themselves. The two-dimensional array would be 50 * 9 = 450 bytes total.

The two-dimensional array reports 12 bytes less with 25 names.
964671 vs. 964683. A drop less in a bucket...
Leo..

If you're always going to loop through all of them in sequence (instead of doing random access by array index) you can take advantage of the fact that the compiler with mush all contiguous literal strings together. It's more readable than one giant string, and you can use multiple lines. Putting NUL at the end of each name will result in double-NUL at the end of the resulting C-string.

const char *names = "Samantha\0" "Tim\0" "Miranda\0"
  "Dancer\0" "Prancer\0" "Donner\0" "Blitzen\0";

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

void loop() {
  delay(1200);
  if (*names) {
    char buf[20];
    int len = snprintf(buf, sizeof(buf), "/%s.bin", names);
    len -= 5;  // "/" and ".bin"
    Serial.print(len);
    Serial.print('\t');
    Serial.println(buf);
    names += len + 1; // skip NUL separator
  }
}

I guess this is most likely not what you wanted to do. This line of code would put Mirnada into RAM and a pointer to it into a PROGMEM. If you rewrite it to

const char birthdayName[] PROGMEM = "Miranda";

an array containing Miranda would be placed in a PORGEMEM and you can still use birthdayName as a pointer if you need.

But PROGMEM only works on AVR boards. WIth XIAO ESP32-S3 I would mind putting an additional 450 bytes in RAM if you are not having problems with it. Besides that accessing RAM is faster than accessing PROGMEM.

To achieve the similar behaviour on ESP32 you can use String s = F("Miranda"); but I personally do not like Strings particularly when running low on memory since there is a chance they are not being constructed and this is something that your code should take into account.

As long as you have plenty of memory I'd rather focus on the speed and clarity of the code than complicate it with memory management.

F() is just not doing anything on ESP32. String literals (const char *) are stored in flash memory.

Indeed. Thank you for letting me know.

The problem has been solved.
And I can now store about 50 240x240 .bin (113k each) images on an XIAO ESP32-S3.
Custom partition, 1.3MB program, 6.6MB LittleFS.
Leo..