"String field" inside a struct that goes into PROGMEM ...

Hi all,

While i was creating a DYNAMIC LCD MENU, i encountered some problems when storing my values table inside the FLASH (to free some RAM ...)

I messed around with some "section type conflict" when initializing the table when storing it into FLASH (PROGMEM option) because it contained a String field (which appears to create some erratic problems with the code compilation).

I couldn't find any simple workaround to change the String field into any other data type (that can contains TEXT) without experiencing lot of problems when it is stored inside FLASH.
The only way i found for the code to compiles when i use the PROGMEM function, was to change my "String" field into a "char *" field.
But it seems that some big problems happens when i try to read the table from FLASH during code execution.

Have you guys an idea of what could be the problem ?

Thanks !

But it seems that some big problems happens when i try to read the table from FLASH during code execution.

And those problems are..?

Have you thought about using a char array instead?
Or adding a PROGMEM qualifier to the char*?

I just got an idea ...

When some table/variable are inside the FLASH memory (with help of the "avr/pgmspace.h" library), is there a way to read them on a different way than when they are stored inside RAM memory ?

I can remember i've read somewhere on a topic that we must use a "pgm read something" ?! is that the answer to my problem ?

PS : for the big problems that happens :
When i try to enumerate the contents of my table (stored in FLASH with PROGMEM), using the same way to read them as if they were in RAM memory, the values are everything except the ones that are initialized.

You can ONLY access data in PROGMEM using the appropriate pgm_readxxx (or derivatives) routines

Haha ... OK, now i understand my mistake :slight_smile:

By the way, could you please explain to me, the way to read a value because i have some doubts about the example in "Arduino Reference - PROGMEM".
It contains this code :

#include <avr/pgmspace.h>
// save some unsigned ints
PROGMEM  prog_uint16_t charSet[]  = { 65000, 32796, 16843, 10, 11234};

// save some chars
prog_uchar signMessage[] PROGMEM  = {"I AM PREDATOR,  UNSEEN COMBATANT. CREATED BY THE UNITED STATES DEPART"};

unsigned int displayInt;
int k;    // counter variable
char myChar;  

// read back a 2-byte int
 displayInt = pgm_read_word_near(charSet + k)

// read back a char 
myChar =  pgm_read_byte_near(signMessage + k);

Why does there is (charSet + k) and (signMessage + k) ?!
I just don't understand.

I hope that i will not have to rewrite a big part of my code because like it is actually, it works fine ... but, in RAM.

myChar =  pgm_read_byte_near(signMessage + k);

Think of it like

myChar =  pgm_read_byte_near(signMessage [ k]);

Does that help?

(the example isn't helpful because "k" is uninitialised, but say "k" was zero, then the example would fetch the first character in the string - signMessage is effectively a pointer into program memory.)

I still have problems to understand ...

Let say i have my table with different types of data :

byte, int, char *, boolean
{25, 6543, "Hello world !", true},
{65, 8756, "Bye bye world !!!", false}
...

If i successfully use PROGMEM on this table, how will i be able to read on LINE 1 (the one that begins with 65), the value on COLUMN 2 (the one with data type char *) ?

The problem you've got there is that you can easily access the pointer "char*", but then you have to use (dereference) that to access the data "Hello world", which is why I suggested using a fixed-length char array in the struct, rather than a pointer.

Remember, whether you use PROGMEM or RAM, the string "Hello world" is NOT stored within the structure.

For telling the truth, on start i wanted to use a "char [20]" because i will never need more than this.

If i use this as a struct, and then, initialize my table values, how could i read the value that will be stored inside the FLASH with "pgm_read_byte/word" ?

Will i need to use strcpy or something like this ?

Could you please give me some code example please ?
I just need to know how to access a line/column value.
If it needs a cast() or something else like enumerating the chars to form a String ... no problem for me.

Right now, i'm really lost on this pgm_read_xxx() function.

Thanks !

I don't want to give you code because, although I can compile code, I don't have an Arduino where I am right now, so can't test it, and if I can't test it, I could just be leading you further away for what may prove to be a very simple bug.
There is, however, a strcpy_P function you may want to look at.
There should be plenty of examples - try the Playground.

Erf, i already tried, but i still have problems to understand the syntax.

Please someone give me an example !!! hahaha :grin:

simkard,
I assume this is related to your glcd menu stuff.

Have a read of the HTML documentation that is supplied with the GLCD v3 library
and you will see that it includes API calls that know how to process strings stored in PROGMEM.

--- bill

Ok, so i managed to get "pgm_read_byte_near" and "pgm_read_word_near" working fine.

But, i still have problems when reading a prog_uchar[30] defined.
When i try to read it, it shows crazy characters and won't stop. (i'm using serial.print for my tests ...)

For now, i use this code :
MY TABLE with its struct initialization

struct STRUCT_MENU {
  byte STRUCT_MENU_MENU_LEVEL;
  byte STRUCT_MENU_MENU_REF;
  byte STRUCT_MENU_SUBMENU_LEVEL;
  prog_uchar STRUCT_MENU_MENU_TEXT[20];
  boolean STRUCT_MENU_EXECUTABLE;
  byte STRUCT_MENU_VALUE_TYPE;
  int STRUCT_MENU_VALUE;
};
const STRUCT_MENU LCD_MENU_TREE [] PROGMEM = {
  // MAIN MENU TEXT
  {0, 1, 1, "Main Menu", false, 0, 0},
  // 1st LEVEL MENUs
  {1, 1, 11, "Configuration", false, 0, 0},
  {1, 2, 12, "Tests", false, 0, 0},
  {1, 3, 0, "About ...", true, 0, 101},
  // 2nd LEVEL MENUs
  {11, 1, 0, "Mode AUTO", false, 0, 1101},
  {11, 2, 22, "Conditions", false, 0, 0},
  {11, 3, 23, "System", false, 0, 0},
  {12, 1, 0, "Light", true, 0, 1201},
  {12, 2, 0, "Fans", true, 0, 1202},
  {12, 3, 0, "CO2 valve", true, 0, 1203},

The code i'm using for reading /&&/ printing values from my table :

      for (int i = 0; i < 21; i++) {
        int BYTE_TEMP = pgm_read_word_near(&LCD_MENU_TREE[i].STRUCT_MENU_VALUE);
        Serial.print("BYTE (");
        Serial.print(i);
        Serial.print(") - VALUE : ");
        Serial.print(BYTE_TEMP);
        Serial.print(" - TEXT : ");
        char buffer[30];
        strcpy_P(buffer, (char*)pgm_read_word(&(LCD_MENU_TREE[i].STRUCT_MENU_MENU_TEXT)));
        Serial.println(buffer);
      }

The outputs looks like this :

     -> READ : TABLE from FLASH
BYTE (0) - VALUE : 0 - TEXT : g·ñøÏúô»ô`ÿoOOOÀ#ðéÀw#!ðè/v/Àf#qðè/pà`à*ðfwÚ÷ù
BYTE (1) - VALUE : 0 - TEXT :            :
BYTE (2) - VALUE : 0 - TEXT : UT : DISABLED
BYTE (3) - VALUE : 101 - TEXT : ð_?qðGûað?yUòÏFñÀñÏè»'f'w'Ëôï
                                                            ÐÀϱßSSSSSSSSSSSSSSSSSSSSSSSS<ETC ETC ETC ......................................>

It seems that some code from a previous program i had flashed a while ago is showing on the output !!! :grin:
I must have some kind of problems while reading the value.
Perhaps the "strcpy_P" function needs to be changed by another one ?

Thanks for your help !

strcpy_P accepts a pointer to flash memory, so pgm_read... is not required:

strcpy_P(buffer, LCD_MENU_TREE[i].STRUCT_MENU_MENU_TEXT);

Oliver

olikraus:
strcpy_P accepts a pointer to flash memory, so pgm_read... is not required:

strcpy_P(buffer, LCD_MENU_TREE[i].STRUCT_MENU_MENU_TEXT);

Oliver

Heheeeeeee !!!

A BIG THANKS !
It works like a charm !

I will free quite 2,5 KB of RAM with your precious help !!!