Library for easy handling data in flash memory

Problem
Many AVRs have limited amount of RAM but may have more Flash space available. With constant data we can avoid using RAM. Constant data should use flash memory instead of RAM but it doesn’t. We need the library pgmspace.h to deal with this. It is a complicated technique, see the section “Data in Program Space” in the user manual: avr-libc: Data in Program Space.

Unfortunately is not easy to use the library pgmspace.h so I made some effort to simplify it.

Solution
There are different functions to read data from program memory such as pgm_read_word() or pgm_read_dword() which depends type which we use. This is not handy. Therefore I created a function readFlash() which can be used for all different kinds of data types and pointers.

Example
int i PROGMEM = 32767; // write data to program memory
int x = readFlash(i); // read data from program memory

Library AVDFlash.h

#ifndef AVDFlash_h
#define AVDFlash_h

#define readFlash(ProgmemValue) _readFlash(& ProgmemValue)

char _readFlash(const char* ch) // or const char* PROGMEM ch 
{ return pgm_read_byte(ch);
}

byte _readFlash(const byte* b)
{ return pgm_read_byte(b);
}

int _readFlash(const int* i)
{ return pgm_read_word(i);
}

unsigned int _readFlash(const unsigned int* ui)
{ return pgm_read_word(ui);
}

long _readFlash(const long* l)
{ return pgm_read_dword(l);
}

unsigned long _readFlash(const unsigned long* ul)
{ return pgm_read_dword(ul);
}

char PROGMEM * _readFlash(char** ch) 
{ return (char*) pgm_read_word(ch);
}

char PROGMEM * _readFlash(const char** ch) 
{ return (char*) pgm_read_word(ch);
}

#endif

Place AVDFlash.h in a folder named AVDFlash and place it in: Arduino\hardware\libraries.

Demo
AVDFlashDemo.pde

#include <avr/pgmspace.h>
#include <assert.h>
#include <Streaming.h>
#include <AVDFlash.h>

void setup()
{ Serial.begin(9600);
  Serial << "RAM: " << availableMemory() << endl;
  dataTypesDemo();
  arraysDemo();    
  Serial << "test OK";
}

void loop() {}
 
int availableMemory() 
{ int size = 2048; 
  byte *buf;
  while ((buf = (byte *) malloc(--size)) == NULL);
  free(buf);
  return size;
}

DataTypesDemo.pde

// All PROGMEM constants must be global
char ch PROGMEM = 'x'; 
byte b PROGMEM = 255; 
int i PROGMEM = 32767; 
unsigned int ui PROGMEM = 65535;
long l PROGMEM = 2147483647;
unsigned long ul PROGMEM = 4294967295;

char progmemString[] PROGMEM = "progmemString\n"; // do not use: char* progmemString PROGMEM = "progmemString\n"

void dataTypesDemo() // use readFlash() for all types
{ assert(readFlash(ch) == 'x');   
  assert(readFlash(b) == 255);         
  assert(readFlash(i) == 32767);            
  assert(readFlash(ui) == 65535);            
  assert(readFlash(l) == 2147483647);            
  assert(readFlash(ul) == 4294967295);  
    
  // String manipulation. See avr-libc-user-manual.pdf section 22.18.4.6 page 255 for more stringfunctions
  assert(strcmp_P("progmemString\n", progmemString) == 0); // read the string
  assert(strlen_P(progmemString) == 14); // string length
  assert(readFlash(progmemString[3]) == 'g'); // access a character from the string
  
  int i=0; char c; // print progmemString
  while(c = readFlash(progmemString[i++]), c) Serial << c;
}

ArraysDemo.pde

// All PROGMEM constants must be global
char charArray[2][3] PROGMEM = {{'a', 'b', 'c'}, {'d', 'e', 'f'}};
int intArray[2][3] PROGMEM = {{1, 2, 3}, {4, 5, 6}};

char string1[] PROGMEM = "String 1\n"; 
char string2[] PROGMEM = "String 2\n"; 
char string3[] PROGMEM = "String 3\n"; 

char* stringTableA[] PROGMEM = {string1, string2, string3};

const char* stringTableB[] PROGMEM = {string1, string2, string3}; // test also const char*

void arraysDemo()
{ assert(readFlash(charArray[1][2]) == 'f'); // get an array element 
  assert(readFlash(intArray[1][2]) == 6); // get an array element 

  // String manipulation. See avr-libc-user-manual.pdf section 22.18.4.6 page 255 for more stringfunctions
  assert(strcmp_P("String 3\n", stringTableA[2]) == 0); // get string3
  assert(strlen_P(stringTableA[2]) == 9); // string length
  assert(readFlash(stringTableA[2][7]) == '3'); // access a character from string3

  int i=0; char c; // print string3
  while(c = readFlash(stringTableA[2][i++]), c) Serial << c;

  assert(strcmp_P("String 3\n", stringTableB[2]) == 0); // test also const char*
}

We use streaming, you can get the Streaming library at: http://arduiniana.org. Place the streaming folder in Arduino\hardware\libraries.

Arduiniana
There is also another approach of the same issue, see Flash | Arduiniana
Here you can find a powerful library flash.h for handling constant data in flash memory.

By the way, readFlash doesn't generate extra code.