0

I'm running a sketch whith a quite big, constant three dimensional char array (or simply, a table of strings). As this takes lots of RAM I'd like to store it in programm memory in order to keep the RAM free. This is the Sketch I'd like to run (my first idea):

const char *rules [16][5] PROGMEM ={ 
 {"1Lorem ipsum dolor sit amet, consetetur sadipscin0g elitr", "sed di7am nonumy 8eirmod tempor invidunt"},
 {"2Lorem ipsum dolor sit amet, consetetur sadipscing2 elitr", "sed diam nonumy 9eirmod tempo5r invidunt"},
 {"3Lorem ipsum dolor sit amet, consetetur sadipscing 4elitr", "sed0 diam nonumy 1eirmod tempor invidunt"},
 {"4Lorem ipsum dolor sit amet, consetetur sadips3cing elitr", "sed diam nonumy 2eirmod tempor in5vidunt"},
 {"5Lorem ipsum dolor sit amet, consetetur 3sadipscing elitr", "sed 7diam nonumy 3eirmod tempor invidunt"},
 {"6Lorem ipsum dolor sit amet, conset9etur sadipscing elitr", "sed diam n8onumy 4eirmod tempor invidunt"},
 {"7Lorem ipsum dolor sit amet, consetetur sadipsc3ing elitr", "sed diam nonumy 5eirmod tempor inv4idunt"},
};

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(115200);
  for(int i = 0; i < 16; i++){
    for(int j = 0; j < 5; j++){
     Serial.println(rules[i][j]); 
    }
  }
}

void loop() {
}

But this fails with the following error:

variable 'rules' must be const in order to be put into read-only section by means of '__attribute__((progmem))'

According to Arduino's FAQ this can be solved by changing the array's declaration to

const char * const rules [16][5] PROGMEM ={

The sketch compiles now without errors, but the global variables (here only the array) still need lots of RAM:

Sketch uses 2484 bytes (7%) of program storage space. Maximum is 32256 bytes.
Global variables use 880 bytes (42%) of dynamic memory, leaving 1168 bytes for local variables. Maximum is 2048 bytes.

I'm not sure what causes this, but is it possible that only the pointers to the strings are stored in program memory, while the strings theirselves are still copied to RAM?

How do I put the whole arary in programm memory?

There has already been a question like this on StackOverflow, but the answer recommends using prog_chars, which are according to the AVR documentation deprecated.

  • See nongnu.org/avr-libc/user-manual/pgmspace.html and also use the special program memory access functions to get the string pointers. – Mikael Patel Nov 1 '17 at 10:46
  • Can you use fixed length data ? That would make it very easy. In that case, don't use a pointer, declare the array with data completely in PROGMEM with "const char rules[16][5][50] PROGMEM = {". The Arduino can not run code from ram and can not read data from program memory. As @MikaelPatel wrote, you need special functions. Read this arduino.cc/en/Reference/PROGMEM and this gammon.com.au/progmem Don't rush through those pages, read them. They explain how to use data and a table with pointers in PROGMEM to optimize memory usage. – Jot Nov 1 '17 at 11:03
  • Fixed data length would be wasting space as the data I really want to store has different lengths. What causes this issue? Is what I described in the question (pointers in PROGMEM, strings in RAM) correct? – tr01 Nov 1 '17 at 11:32
  • 1
    Yes, you describe in the question pointers in PROGMEM and strings in RAM. Actually the strings are loaded from PROGMEM to RAM by the program startup code (init). – Mikael Patel Nov 1 '17 at 15:02
  • Maybe wasting a few hundred bytes is acceptable. – Jot Nov 2 '17 at 1:41
1

Here you have:

const char * const rules [16][5] PROGMEM ={ 

One const refers to the char* part. It means the pointer value doesn't change.

The other const refers to what it is pointed by. It means the thing this pointer points doesn't change (but the pointer itself can change).

In your sketch the thing that get put into PROGMEM is the pointer array; the texts themselves are still in RAM. Have a look at PROGMEM page and study this example:

const char string_0[] PROGMEM = "String 0";   // "String 0" etc are strings to store - change to suit.
const char string_1[] PROGMEM = "String 1";
const char string_2[] PROGMEM = "String 2";
const char string_3[] PROGMEM = "String 3";
const char string_4[] PROGMEM = "String 4";
const char string_5[] PROGMEM = "String 5";


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

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

You need to declare each string by itself, and later put them in an array.

As you can see, pointer are inescapable.

Edit: The F() Macro

Here is a proof of concept of how to manage an array of string (lowercase "s") and strings constants in PROGMEM:

const char uno[] PROGMEM = "1Lorem ipsum dolor sit amet, consetetur sadipscin0g elitr";
const char dos[] PROGMEM = "2Lorem ipsum dolor sit amet, consetetur sadipscin0g elitr";
const char tre[] PROGMEM = "3Lorem ipsum dolor sit amet, consetetur sadipscin0g elitr";
const char cua[] PROGMEM = "4Lorem ipsum dolor sit amet, consetetur sadipscin0g elitr";
const char cin[] PROGMEM = "5Lorem ipsum dolor sit amet, consetetur sadipscin0g elitr";

const char * const rules [3][2] = { 
 {uno, dos},
 {tre, cua},
 {cua, cin}
};

void setup() {
  Serial.begin(115200);
  Serial.println(F("PROGMEM string test"));
  Serial.println(F("-------------------"));
  for(int i = 0; i < 3; i++){
    for(int j = 0; j < 2; j++){
      Serial.print(i); Serial.print(" "); Serial.print(j); Serial.print(" ");
     Serial.println((const __FlashStringHelper *) rules[i][j]); 
    }
  }
}

void loop() {
}

Use the F() macro to put constant strings in PROGMEM. Otherwise, the strings will live in RAM. Are running short of RAM? Put the F() macro around those pesky strings and send them out of your RAM.

  • So if I want a three dimensional array, I would change the last line like this?: const char* const string_table[2][2] PROGMEM = { {string_0, string_1}, {string_2, string_3}};` Would that be correct? – tr01 Nov 1 '17 at 11:49
  • Yes; that's the correct syntax. BTW, yours is a two-dimensional array. – user31481 Nov 1 '17 at 11:54
  • Yes, it's a two-dimensional array containing (pointers to) one-dimensional arrays, right? – tr01 Nov 1 '17 at 11:58
  • @LookAlterno Might need to update the answer with how to print the strings. This is tricky as a special form (helper class) is used for the F() macro. – Mikael Patel Nov 1 '17 at 15:05
  • How do I declare the strings? Should I use const char string_0[] PROGMEM = "String 0"; (as in the exapmle on the Arduino page) or const char* const uno PROGMEM = "1Lorem.."; as in your second example? I'm not sure where the difference is. The first one creates an array while the second one creates a pointer, right? Why is it possible to store multiple chars in one char pointer? – tr01 Nov 1 '17 at 16:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.