So now I want to understand the loop to retrieve all strings in that table and printing them
for (int i = 0; i < 6; i++) {
strcpy_P(buffer, (char *)pgm_read_word(&(string_table[i]))); // Necessary casts and dereferencing, just copy.
Serial.println(buffer);
delay(500);
}
Buffer is char table as large as the longest string we try to retreive.
strcpy_P()) function returns a pointer to the destination string dest. Questions:
I am not sure why this function was called here. I would expect a function to load the buffer not a function to return the pointer
pgm_read_byte() is a macro that read a word of data stored in a specified address(PROGMEM area).
So pgm_read_word(&(string_table[i])) seems clear. It is reading a word (2bytes) from the pointer defined by string_table[i]. But few questions here:
what is the role of (char *) before the call of pgm_read_byte()
what is the role of "&"
Any string in a table is longer then 2 bytes . Buffer is 30 bytes where in this command we identify how many bytes should we read for selected string.
Any clarification here would be greatly appreciated
Do you mean not using strcpy_P?
Sure, just use pgm_read_byte until you encounter the terminator.
(But obviously, you'd really want to use strcpy_P, because that's already been written and debugged)
strcpy_P copies the string in PROGMEM into the buffer, then returns a pointer to the buffer. Most of the str functions return pointers to the string destination, in case you want to use the function embedded within another function.
pgm_read_word() returns a uint16_t, the cast to char* is needed to match the function parameter for strcpy_P.
The & indicate the address of string_table[i], instead of the value stored there.
The number of bytes to read in the function is determined by the length of the string in PROGMEM, strcpy_P copies until it encounters the terminating null character.
It is possible to print the string directly from PROGMEM, without needing to copy to a buffer.
for (int i = 0; i < 6; i++) {
Serial.println((__FlashStringHelper*)pgm_read_ptr(&(string_table[i])));
delay(500);
}
pgm_read_ptr() is similar to pgm_read_word, except that it does not require that you know the size of a pointer on the processor you are using.
__FlashStringHelper* specifies a pointer to a string in PROGMEM, as opposed to a pointer in ram. There is an overload of print() that is specifically coded for strings in PROGMEM.
Ok. So both points you commented on make sense. So I provide word long address to the memory and the strcpy_P reads data to a buffer until the null terminator
Thank you for a comprehensive analysis:)
That pretty much calrifies things.
Is there a function that allows me to read and show the content of progmem regardless of the individual strings saved to it.
So in my example strcpy_P starts loading buffer with data until it ancounters terminating null character.
Is there a function that would allow me to load the whole buffer until it is full so I can display it.
This is just out of curiosity.
Which other functions form that library are worth getting familiar with. There are loads of them..
The following sketch and the associated comments may provide some clarification.
const char string_0[] PROGMEM = "String 0"; //string goes into flash because of PROGMEM
void setup()
{
Serial.begin(9600);
byte charLen = strlen_P((const char*)string_0); //strlen_P() is variant of strlen()
for (int i = 0; i <charLen; i++)
{
char y = pgm_read_byte_near(&string_0[i]);
Serial.print(y); //shows: String 0
}
Serial.println();
}
void loop()
{
}
1. The strlen_P() function deals with string stored in Program Memory (the flash). It takes a pointer (which holds the base address of the string stored in flash) as argument. Using the pointer, the function scans the memory and counts the number of characters/bytes present in the string until the null-character ('\0') is encountered.
2. The cast (const char*) is there to indicate that the pointer refers to a string whose elements are characters and cannot be modified.
3. The pgm_read_byte_near() function takes the address of the string as argument and hence the ampersand (&) operator.
Not sure what you are asking, what would be the point in printing random data that exists beyond the end of the string? print() prints string data until it encounters the terminating null, then stops. The null is what indicates the end of a string stored as a char array.
If you really want to fill the entire buffer, you can use memcpy_P and give the size of the buffer, but then printing will need to be done differently, print() will still stop at the null.
If all the strings are the same length, or nearly the same length, then they can be stored as a two-dimensional array to avoid the table of char*. Accessing the string is then a bit less convoluted.