Friday, December 31, 2010

Simple way how to use PROGMEM

If you write large Arduino program with a lot of strings, RAM can be huge issue.

When reading something about Arduino Memory organization you might notice that all strings are being put in to RAM. And Arduino has "only" 2kB of the RAM. So if you have a program with lots of these:

Serial.prinln("Some very long string...");
Serial.prinln("Some another very long string...");

RAM runs out very quickly. In my case it was quite talkative telnet server that sometimes unexpectedly crashed because of this issue.

Note: Free RAM could be determined by function ATS_GetFreeMemory() form ArduinoTestSuite.

The solution is to put strings into program memory (Arduino has 32kB of program memory). However that is not as simple as it sounds. Details can be found in PROGMEM section in Arduino reference. You will probably end with something like this:

#include <avr/pgmspace.h>

const char message[] PROGMEM = "Some very long string...";
char buffer[30];

strcpy_P(buffer, message);
Serial.println(buffer);

And this is a lot of code for one println, isn't it. Ideal would be something like

Serial.prinln(P("Some very long string..."));

And surprise, it's possible. Solution is to declare a macro.

#include <avr/pgmspace.h>
 
char p_buffer[80];
#define P(str) (strcpy_P(p_buffer, PSTR(str)), p_buffer)

That’s all!

Principle is simple. String is stored in the program memory and processor copies it to the buffer and returns this buffer instead of the string. Key is macro PSTR from pgmspace.h. PSTR allows you to declare string inline into the program memory without PROGMEM keyword and variables.

In my telnet server, I've saved more then 1kB (more then 50%!). And it only requires to surround all strings with P("...").