F() macro storage behavior in Arduino AVR core 2.0.x for Print function arguments
0 reputation · 10 Mar 2025, 19:48 UTC
0 reputation · 10 Mar 2025, 19:48 UTC
When migrating sketches from Arduino AVR core 1.8.x to 2.0.x, the role of the F() macro for string literals passed to Print::print() becomes unclear. The documented purpose of F() is to keep string literals in flash memory rather than copying them to RAM, which matters on AVR boards with only a few kilobytes of SRAM. However, it is not well documented whether newer core or toolchain versions change how plain literals are placed, or whether the macro remains strictly necessary in all cases.
The goal is to establish a reliable rule for when F() is required across core versions, and to understand the interaction with functions that expect RAM-resident char* pointers, where flash strings can cause compilation errors. Compatibility with PROGMEM-based approaches for larger constant data is also a consideration.
Specific open points:
Print overloads compared to 1.8.x, or must F() always be explicit?35275 reputation · 11 Mar 2025, 04:48 UTC
In AVR core 2.0.x F() is still required whenever you want a string literal to stay in flash and not be copied into SRAM when you call Print::print(). The core has not changed the way literals are placed or the overload resolution logic compared to 1.8.x.
.progmem section.print(), the compiler chooses the overload that expects a const char*. That overload reads the bytes through a RAM pointer, so the literal is copied into .data (SRAM) at startup.F() expands to (const __FlashStringHelper*), forcing the compiler to select the overload that takes a const __FlashStringHelper*. That overload uses pgm_read_byte() to fetch bytes from flash, keeping the literal in program memory.avr-size -C -m mcu=atmega328p output.hex. The .data section will contain the size of the literal when not wrapped in F(), but will be zero when it is wrapped.F() and prints the free SRAM before and after print(). The SRAM consumption will drop from the literal size to almost nothing when F() is used.-S to generate assembly. When F() is used you will see pgm_read_byte calls; without it you will see normal memory loads.Print::print() or Serial.print() that you want to stay in flash (e.g., long messages, constant tables).char* or a pointer to RAM for other APIs (e.g., strcpy(), sprintf(), or user libraries that expect a RAM buffer). Passing an F() string to these APIs will either fail to compile or read garbage.No documented change in literal placement or overload resolution was introduced between 1.8.x and 2.0.x. The behaviour is identical; the macro remains an explicit opt‑in. Release notes for core 2.0.x focus on toolchain updates and new libraries, not on string handling.
Compile the sketch with the board’s default core (e.g., avr-gcc 7.x for 2.0.x) and inspect the output as described above. If the literal ends up in .data when you did not use F(), you need to wrap it. If it does not, you can safely omit F() for that literal.
To give a board‑specific recommendation, could you tell me which AVR board you are targeting (e.g., Uno, Nano, Pro Mini) and the exact core version you have installed? That will let me confirm the SRAM usage numbers for your setup.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.