Using FLASH storage to save RAM - examples

For the past year, I have been using a simple method to make my programs compile and use FLASH (or not) just by altering a #define. This enables me to code normally, using RAM for speed, but then switch over to FLASH storage later if the project grows too big and I need the storage. I have shared my development/learning of this on my website, but thought I would post it here.

I welcome comments. Just because it seems to work great, doesn't mean it's being done the best way, or the correct way.

The key is a short block of #defines at the top, that create three macros - FLASHMEM, FLASHSTR and FLASHPTR. These alias the recommended ways for access FLASH storage, based on it being a string or a pointer. If USE_FLASH is defined, those macros are the Arduino IDE flash keywords/casting (PROGMEM, __FlashStringHelper*). If not defined, they are empty so it just works like RAM. Then, you just always use these macros in your code.

For instance, instead of declaring a normal string:

char string1[] = "This is a string that would be stored in RAM and chew up alot of memory";

...you would use the macro:

char string1[] FLASHMEM = "This is a string that would be stored in RAM or FLASH, depending on the macro.";

If #define USE_FLASH is set at the top, then it replaces FLASHMEM with the "PROGMEM" keyword and the string goes to flash.

Later, when accessing it, you get to it using FLASHSTR() like this:

Serial.println(FLASHSTR(string1));

If it's in RAM, then FLASHSTR is nothing, and it just does the normal print. If it's in FLASH, then that macro does the casting to a __FlashStringHelper*.

For arrays of pointers, like this:

const char *array[] FLASHMEM = {string1, string2, string3};

...those pointers (2 bytes each) are either in RAM, or in FLASH. Accessing these pointer is done by:

Serial.println(FLASHPTR(array[1]));

...and that is either just a normal RAM array print, or the macro makes it use pgm_read_word() to obtain the pointer from FLASH.

I have had to do this on many of my projects since I cram so much in to the tiny space of my various devices (Teensy 2.0, Arduino UNO, Arduino Leonardy, MEGA and some other clones).

Hope this helps someone...

//#include <avr/pgmspace.h>

//#define USE_FLASH
// USE_FLASH - 1839 bytes free
// without   - 1803 bytes free (36 bytes less - exactly as expected)

#ifdef USE_FLASH
#define FLASHMEM PROGMEM
#define FLASHSTR(x) (const __FlashStringHelper*)(x)
#define FLASHPTR(x) (const __FlashStringHelper*)pgm_read_word(&x)
#else
#define FLASHMEM
#define FLASHSTR(x) (x) //(const char *)(x)
#define FLASHPTR(x) (x) //(const char *)(x)
#endif

//#define PGMT(pgm_ptr) (reinterpret_cast<const __FlashStringHelper *>(pgm_ptr))

char string1[] FLASHMEM = "String #1"; // 9+NULL = 10 bytes
char string2[] FLASHMEM = "String #2"; // 
char string3[] FLASHMEM = "String #3"; // 30 bytes total here.

// 2 bytes for each pointer to string. 6 bytes total here.
//const char *flashArray[] FLASHMEM = { string1, string2, string3 }; // 1835
//const char * FLASHMEM flashArray[] = { string1, string2, string3 }; // 1829
const char *flashArray[] FLASHMEM = { string1, string2, string3 }; // 1835

void setup()
{
  int i;
  
  Serial.begin(9600);
  while(!Serial);

  Serial.print("X. ");
  Serial.println(FLASHSTR(string1));

  for (i=0; i<3; i++) {
    Serial.print(i);
    Serial.print(". ");
    Serial.println(FLASHPTR(flashArray[i]));
    //thisFails(i);
  }
  
  showFreeRam();
}

void thisFails(int i)
{
  //unsigned int addr = pgm_read_word(flashArray+i);
  //Serial.println(addr);
  //Serial.println((FLASHCAST)pgm_read_word(flashArray+i));
//  Serial.println((FLASHCAST)flashArray[i]);
}

void loop()
{
}

unsigned int freeRam()
{
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

void showFreeRam()
{
  Serial.print(F("Free RAM: "));
  Serial.println(freeRam());
}
// End of FlashTest