Skip to main content
Added more explanations and example code.
Source Link
Nick Gammon
  • 38.9k
  • 13
  • 70
  • 126

There were some interesting subtleties to getting this working. :)

One of your major problems is this:

const char * const scriptTable[] PROGMEM = 

That will give you 2-byte pointers (const char *) which will not be large enough to index into 89400 characters.

So you need to have an array of 4-byte pointers like this:

uint_farptr_t  scriptTable [TABLE_SIZE] =

However you can't just initialize those like you tried to. That has to be done procedurally with a macro that pulls in the full address. My working code is below:

#include "code.cpp"

const int TABLE_SIZE = 3;
uint_farptr_t  scriptTable [TABLE_SIZE];  // values calculated at runtime
const int scriptLengths[TABLE_SIZE] 
  {
  sizeof (script1), 
  sizeof (script2), 
  sizeof (script3)
  };

void foo ()
{
  // for each of the three script strings
  for (byte tableIndex = 0; tableIndex < TABLE_SIZE; tableIndex++) 
    {
    // Chunk up large script strings in PROGMEM.
    const int CHUNKSIZE = 10; // CHUNKSIZE *must* be an even number!!
    char workingSpace[CHUNKSIZE + 1];
    long baseIndex = 0;

    Serial.println ("----------------");
    Serial.print("tableIndex: ");
    Serial.println(tableIndex);
    long strLength = scriptLengths[tableIndex];
    Serial.print("length script string: ");
    Serial.println(strLength);

    while (baseIndex < strLength) {
      int chunk;
      if (baseIndex + CHUNKSIZE > strLength) {
        chunk = strLength - baseIndex;
      }
      else {
        chunk = CHUNKSIZE;
      }
      memcpy_PF(workingSpace, scriptTable [tableIndex] + baseIndex, chunk);
      workingSpace[chunk] = '\0';

      // for debugging
      Serial.print("#");
      Serial.print(baseIndex);
      Serial.print("- Got a chunk [");
      Serial.print(workingSpace);
      Serial.println("]");
      delay (50);
      baseIndex += CHUNKSIZE;
    } // end of while
  } // end of for
} // end of foo


void setup() 
{
  // get proper pointers to the various pieces of progmem
  scriptTable [0] = pgm_get_far_address (script1);
  scriptTable [1] = pgm_get_far_address (script2);
  scriptTable [2] = pgm_get_far_address (script3);
  
  Serial.begin (115200);
  foo ();
}

void loop() 
{
}

The file "code.cpp" just has the actual data in it (generated at http://www.lipsum.com).

As in:

#include <Arduino.h>

const char script1[] PROGMEM =  { 
  
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.  ... " };

const char script2[] PROGMEM =  { 
  
"Aliquam erat volutpat. Praesent aliquet accumsan arcu,  ... " };
    
const char script3[] PROGMEM =  { 

"Ut neque dolor, condimentum ut odio iaculis,  ... " };

Output:

----------------
tableIndex: 0
length script string: 32767
#0- Got a chunk [Lorem ipsu]
#10- Got a chunk [m dolor si]
#20- Got a chunk [t amet, co]
#30- Got a chunk [nsectetur ]
#40- Got a chunk [adipiscing]
#50- Got a chunk [ elit. Pro]
#60- Got a chunk [in sit ame]
#70- Got a chunk [t purus cu]
#80- Got a chunk [rsus, dict]
#90- Got a chunk [um magna q]
#100- Got a chunk [uis, place]
#110- Got a chunk [rat dui. I]
#120- Got a chunk [nteger ali]

...

#32700- Got a chunk [, eget tem]
#32710- Got a chunk [por dui te]
#32720- Got a chunk [mpor a. Do]
#32730- Got a chunk [nec malesu]
#32740- Got a chunk [ada mi et ]
#32750- Got a chunk [dolor cras]
#32760- Got a chunk [ amet.]
----------------
tableIndex: 1
length script string: 32767
#0- Got a chunk [Aliquam er]
#10- Got a chunk [at volutpa]
#20- Got a chunk [t. Praesen]
#30- Got a chunk [t aliquet ]

It successfully transitioned from the first array to the second one.


There were some interesting subtleties to getting this working. :)

One of your major problems is this:

const char * const scriptTable[] PROGMEM = 

That will give you 2-byte pointers (const char *) which will not be large enough to index into 89400 characters.

So you need to have an array of 4-byte pointers like this:

uint_farptr_t  scriptTable [TABLE_SIZE] =

However you can't just initialize those like you tried to. That has to be done procedurally with a macro that pulls in the full address. My working code is below:

#include "code.cpp"

const int TABLE_SIZE = 3;
uint_farptr_t  scriptTable [TABLE_SIZE];  // values calculated at runtime
const int scriptLengths[TABLE_SIZE] 
  {
  sizeof (script1), 
  sizeof (script2), 
  sizeof (script3)
  };

void foo ()
{
  // for each of the three script strings
  for (byte tableIndex = 0; tableIndex < TABLE_SIZE; tableIndex++) 
    {
    // Chunk up large script strings in PROGMEM.
    const int CHUNKSIZE = 10; // CHUNKSIZE *must* be an even number!!
    char workingSpace[CHUNKSIZE + 1];
    long baseIndex = 0;

    Serial.println ("----------------");
    Serial.print("tableIndex: ");
    Serial.println(tableIndex);
    long strLength = scriptLengths[tableIndex];
    Serial.print("length script string: ");
    Serial.println(strLength);

    while (baseIndex < strLength) {
      int chunk;
      if (baseIndex + CHUNKSIZE > strLength) {
        chunk = strLength - baseIndex;
      }
      else {
        chunk = CHUNKSIZE;
      }
      memcpy_PF(workingSpace, scriptTable [tableIndex] + baseIndex, chunk);
      workingSpace[chunk] = '\0';

      // for debugging
      Serial.print("#");
      Serial.print(baseIndex);
      Serial.print("- Got a chunk [");
      Serial.print(workingSpace);
      Serial.println("]");
      delay (50);
      baseIndex += CHUNKSIZE;
    } // end of while
  } // end of for
} // end of foo


void setup() 
{
  // get proper pointers to the various pieces of progmem
  scriptTable [0] = pgm_get_far_address (script1);
  scriptTable [1] = pgm_get_far_address (script2);
  scriptTable [2] = pgm_get_far_address (script3);
  
  Serial.begin (115200);
  foo ();
}

void loop() 
{
}

The file "code.cpp" just has the actual data in it (generated at http://www.lipsum.com).

As in:

#include <Arduino.h>

const char script1[] PROGMEM =  { 
  
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.  ... " };

const char script2[] PROGMEM =  { 
  
"Aliquam erat volutpat. Praesent aliquet accumsan arcu,  ... " };
    
const char script3[] PROGMEM =  { 

"Ut neque dolor, condimentum ut odio iaculis,  ... " };

Output:

----------------
tableIndex: 0
length script string: 32767
#0- Got a chunk [Lorem ipsu]
#10- Got a chunk [m dolor si]
#20- Got a chunk [t amet, co]
#30- Got a chunk [nsectetur ]
#40- Got a chunk [adipiscing]
#50- Got a chunk [ elit. Pro]
#60- Got a chunk [in sit ame]
#70- Got a chunk [t purus cu]
#80- Got a chunk [rsus, dict]
#90- Got a chunk [um magna q]
#100- Got a chunk [uis, place]
#110- Got a chunk [rat dui. I]
#120- Got a chunk [nteger ali]

...

#32700- Got a chunk [, eget tem]
#32710- Got a chunk [por dui te]
#32720- Got a chunk [mpor a. Do]
#32730- Got a chunk [nec malesu]
#32740- Got a chunk [ada mi et ]
#32750- Got a chunk [dolor cras]
#32760- Got a chunk [ amet.]
----------------
tableIndex: 1
length script string: 32767
#0- Got a chunk [Aliquam er]
#10- Got a chunk [at volutpa]
#20- Got a chunk [t. Praesen]
#30- Got a chunk [t aliquet ]

It successfully transitioned from the first array to the second one.

Source Link
Nick Gammon
  • 38.9k
  • 13
  • 70
  • 126

The first thing you need to fix is this:

/home/nick/Arduino/Large_Progmem_test/Large_Progmem_test.ino: In function 'foo()':
/home/nick/Arduino/Large_Progmem_test/Large_Progmem_test.ino:42:27: warning: iteration 3276 invokes undefined behavior [-Waggressive-loop-optimizations]
     baseIndex += CHUNKSIZE;

You are adding 10 to 32760 hoping to be >= sizeof(code) however now baseIndex will become negative as an int goes from -32768 to +32767.