fenghuang:
F() might be a bit above my pay grade at the moment. 
Well, you have a bit of a problem, then. Because that's the way you consume less RAM.
Essentially, any initialised constant data becomes part of your binary image, because of course it does: how else would your code have access to it? All of these initialised constants get copied from the binary image into RAM, so that your program can work with them.
But what you can do is to tell the compiler to not copy those blocks of data into RAM, to leave them in read-only "program memory". And this is fine for blocks of data that don't change: blocks of text. Chunks of HTML. pre-computed lookup tables for hardware.
But, but, if they are not in RAM your program can't get at them. So, what's the use of that? Well, the arduino libraries give you a way to pull them out of program memory one byte at a time. Each of these chunks of data has an address that looks like a regular pointer, "memory address 12345", but address 12345 in RAM will contain garbage if you pull it out directly. You have to use pgm_read_X_near
, where 'X' is 'byte' or 'word'. This function has machine code in it that accesses progmem rather than regular RAM.
Of course, there are utility functions to make this a bit easier.
C++ supports overloaded functions. you can make two different functions with the same name but different argument types, and the compiler will distinguish between them. The arduino libraries use this to distinguish between pointers that are regular RAM pointers, and pointers that are references into program memory. The arduino defines a type named __FlashStringHelper
.
So there are two functions in Serial named println.
void println(const char *);
void println(const __FlashStringHelper *);
The second one knows that when it is passed a pointer to a string, it has to fetch each character of the string from program memory.
The F() macro is defined like so:
#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))
PSTR wraps your string constant, telling the loader to leave it in program memory. The cast gooses the type of the pointer, informing the compiler that when println is called, it needs to use the "other" println.
TL;DR: learn to use F().