It’s 3am but you are not going to bed until you squash this last bug. You sprinkle Serial.print() statements everywhere you can think of, and then that’s when all hell breaks loose:  Your code randomly locks up, the LEDs go crazy, and you’ve had it. What’s going on?  You’ve run out of RAM! All of those Serial.print() statements are composed of c-style strings. Which are “constant character arrays.”  In order for those arrays to work properly, they get loaded into the ATmega’s RAM before your code starts running. Which can be a problem because 2,048 bytes of RAM doesn’t allow for much.

Introducing the F() Macro

Search the entire Arduino Reference Page, and you won’t find a single mention of the F() macro. Which is unfortunate because it is one of the most powerful functions, which was added with the 1.0 release of the IDE. I keep mixing the terms “macro” and “function.”  F() really isn’t a function, it is a #define macro which lives in WString.h
WString.h:#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))
That long string of code tells the compiler to keep a string inside of PROGMEM and not allow it to consume RAM.

Using the F() Macro

Here’s an example of how you would use the F() macro with Serial.print() or Lcd.print().

Serial.println(F(“Hello World”));
Lcd.print(F(“W”));
That’s all there is to it. Simply wrap your string (const character array) with F().

Why is the F() Macro Needed?

Remember that the Arduino Uno (and it’s cousins) are based on the ATmega328. This microcontroller only offers 2,048 bytes of RAM. 2k, that’s it. Even the Uno’s big brother, the Mega2560, only has 8K of RAM.

What about using the “const” keyword?

From the #define vs const post, the const keyword will tell a compiler that a variable is a constant and can’t change. Depending on the optimizations used, the avr-gcc compiler will avoid putting that value into RAM since it knows it’ll never change. But, that technique won’t work with c-style strings or,well, arrays. Since arrays are based around pointers, the compiler needs to put the array into RAM so that pointers work correctly. Which means, all strings need to be put into RAM before they can be used.

What the F() macro Does

The F() macro tells the compiler to leave this particular array in PROGMEM. Then when it is time to access it, one byte of the data is copied to RAM at a time. There’s a small performance overhead for this extra work. However, printing strings over Serial or to a LCD is a really slow process, so a few extra clock cycles really won’t matter.

Tradeoffs to Consider

#1:  Can’t Change Data

The key tradeoff to using the F() Macro is that you can’t use it for data you want to change. Anything stored in PROGMEM cannot be changed by the running program. In fact, you would never want that. The only code that can change PROGMEM is code stored in the boot partition, which is where the bootloader lives.

#2:  Only works on Strings

Another tradeoff is that this macro only works for strings. While it is useful to use PROGMEM to store stuff like bit-patterns for characters, this macro isn’t going to help with that. If that’s the kind of thing you are trying to store, then you’ll need to use the traditional PROGMEM statements to do so. Which makes this reference on PROGMEM from the Arduino.cc site a great read.

#3:  Not good for big blocks of text (like HTML)

Storing HTML pages is another example where you probably want to keep the bulk of the text in PROGMEM. Unless that HTML is wrapped around Serial.print() or Client.print(), you aren’t going to be able to use the F() macro.

#4:  Optimization

There is no optimization of memory usage with the F() macro. If you use the same string over and over in your code, each instance will consume some PROGMEM. If you’re running short on PROGMEM, you might have to consider not using the F() macro .

Conclusion

Don’t wait until your code starts acting weird. Whenever you use strings in a print() method, get into the habit of wrapping the string with a F() macro. This way you don’t have to worry about RAM getting wasted on something that can’t be changed anyway.
Author

Fan of making things beep, blink and fly. Created AddOhms. Stream on Twitch. Video Host on element14 Presents and writing for Hackster.IO. Call sign KN6FGY.

28 Comments

  1. James This article is still very helpful even after their docs have been updated since they buried it in a place where only the most knowledgable users would think to look for it. it suffers from the same issue as lots of reference material, it’s only useful if one knows WHERE to look. Your article shows the symptoms and provides a way to fix an actual identifiable and SEARCHABLE problem.

    A search for my Arduino app crashes is likely to turn up your article and not the terse technical description of F() burried at the bottom of the PROGMEM reference page.

  2. Thank you, James. Using the F() macro was very helpful for me when I wrapped strings in my Arduino sketch with it. Doing so led to my Arduino Mega2560 R3 not crashing anymore. It was printing strings to an LCD screen as well as printing to the Serial Monitor in the void loop(). I had also increased the delay at the end of the loop from 100 ms to 125 ms.

  3. thank you this is very helpful!!
    what if we have many long different string variables.. are they affecting on the RAM ? if yes can we use PROGMEM for every variable?
    thank you again

    • i found the answer on the arduino references as you said … 👍🏼👍🏼👍🏼👍🏼

  4. Thanks for the great explanation. Can the F() macro be used with the SD card library .print/println functions?

    Thanks in advance.

  5. Alexandre Trevizoli Reply

    Is possible to add some macro operator to F() to it be converted automatically to String ?
    Let me explain…
    if we tried to use example, [code]Serial.println(F("xxxxxx")+String(myvar)+"yyyyyyyy");[/code] it will not compile….to correct we need to write:
    [code]Serial.println(String(F("xxxxxx"))+String(myvar)+"yyyyyyyyyy");[/code]

    Another ugly example:
    [arduino]
    for (int i = 0 ; i < EEPROM.length() ; i++) {
    EEPROM.write(i, 0);
    if (debug) {
    x=(i/EEPROM.length())*100;
    if (x%5==0) { Serial.println(String(x)+String(F("% completed")); }
    }
    }
    [/arduino]
    Thanks a lot!

    • There’s no reason to add or concatenate strings inside of the Serial.print() method. print() and println() are loading a serial transmit buffer. So the add/concat operate is wasting time and wrecking the stack. Just call serial.print() twice.

  6. And for the real newbies like me, be sure to use a capital F() as the macro name. f() didn’t work for me.

    Yes, this tip gives important insights! Thanks….

  7. Paul Boston Reply

    Well said!
    That’s clear even to a novice.
    Thanks!

    Paul Boston

  8. Pini Shachar Reply

    Thank you very much for the clear and detailed information of the macro F() . It should be realy helpfull for low capacity RAMs .

  9. carl. gifford Reply

    Thanks for this, I just recently saw this in a small chunk of code with a small comment // place in prog Mem. Your article nicely explains . Thanks.

  10. Oops, the comment shings that WString.h was a html tag or something. Since you seem to moderate the comments anyways maybe you can fix it because now the comment doesn’t make sense 🙂

  11. I had exact the same thing! I was pulling my hairs out to find out why all of the sudden my code wouldn’t work anymore. By commenting and uncommenting lines of code over and over again I finally found out that a Serial.print() was causing this issue. Then I found out about the F() macro.

    I couldn’t get it to work immediately though, but I suppose I need do use #include at the top of my code? Or is it part of Arduino.h?

    I eventually just removed the serial prints because the code was complete anyways. And boy did I miss a debugger during this process 🙁

    Nice to read I am not the only one who made this mistake.

  12. Many, many, many thanks for this article.

    My Arduino project (a Lasertag system) had ground to a halt over the weekend due to running out of memory, and I was considering upgrading to a Mega, as I could not find any other solution.

    I went through my code this morning and altered all my Serial.print statements to use the F macro and I recovered 60% of my available RAM.

    So your article saved the day 🙂

  13. stuart graham Reply

    Hello, Great piece of information.

    If i change an existing string such as:
    ” Serial.print(“Initializing SD card…”); ”
    and change this to read:
    ” Serial.print(F(“Initializing SD card…”)); ”

    The program compiles with no issues however neither the RAM or program storage used changes on my Uno.

    Do i need to initialise somewhere first?

    • This is a good example of how the RAM usage reported by the compiler isn’t always “right.” Character-const arrays don’t consume RAM until they are copied at run time. The RAM usage reported by the compiler only reports static usage, like how many variables are defined. The F() macro keeps the string from being copied.

      • matthijskooijman Reply

        I don’t think this is correct – the data is copied into RAM at startup, but the space for this is already reserved at compiletime and should be included in the IDE memory usage output. I just tried this with an empty sketch with just one serial print, and adding `F()` lowers the memory usage for me. Not sure why the original commenters code does not work like this, though.

  14. Mark Harrington Reply

    Thanks for sharing I learnt something new from your topic

  15. Any chance you could do a short piece on using progmem? The page on the Arduino site left me more confused then when I started lol

  16. StrangeTrousers Reply

    Thanks also. I found this page when looking for an explantion of the F() macro. Even after looking at the source for the macro I had no idea what it was for, yet it was used constantly in supplied examples. Baffling until now.

  17. Thnaks so much for your thorough explanation of the F() function/macro. I was also shocked for not being able to find any information about it anywhre in the “oficial” arduino pages.

    • Same here. I realized it when I went to link to a description for another post. It’s never been officially documented.

Write A Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.