2

Using Nano's for small programmes I get compilation reports about the amount of space taken by global variables.

On a typical progamme it says something like Sketch uses 16316 bytes (53%) of program storage space. Maximum is 30720 bytes. Global variables use 1798 bytes (87%) of dynamic memory, leaving 250 bytes for local variables. Maximum is 2048 bytes. Low memory available, stability problems may occur.

If I count up the number of bytes taken by the declared global variables it is 126 bytes, depending on how bool's take up memory ( one bit or one byte ).

Can I get the compiler to give me a dump of what it is actually assigning as global variable space to reconcile the difference between 126 bytes and 1798 bytes. Then I could change the programme to get round the memory problem and keep using the Nano's.

Thanks in advance for any useful insights.

Thanks to those who commented for the help.

Checking the memory requirement for the global variables, includes and begins still only used 34% so I realised that something in the main loop was using extra memory. Then the penny dropped, I had print messages left over from tracing problems and these become stored constants when compiled. Moved these to a conditional compile section so they are only compiled during tracing for bugs. Problem solved. Once again thanks for the helpful hints.

4
  • 1
    Start with an empty sketch and see its RAM requirements. Then add Serial.begin(9600); and notice the difference. Your 1798 bytes are certainly used by some library. And yes, there`s a tool avrdump , but not that easy to use. Dec 17, 2021 at 23:04
  • If you dive into command line levels, you can ask the linker to generate a map file. However, this is not trivial for the beginner. Dec 18, 2021 at 13:29
  • Here's a hint for future use: this form (note the 'F' macro) Serial.print(F("Your message here.\n")); will store the message text in flash instead of RAM. That will work for any object that uses Print library. Though your concern wasn't (yet) running out of RAM, in a larger program, adding text messages can cause various kinds of out-of-RAM crashes.
    – JRobert
    Dec 18, 2021 at 17:01
  • Please do not answer questions in the comments. There is a nice big box underneath where answers go. @Harry - if you find the answer yourself (as you did it seems) feel free to make an answer yourself.
    – Nick Gammon
    Dec 18, 2021 at 22:08

1 Answer 1

1

RAM is precious on a microcontroller, PROGMEM (program memory) not quite as much so. On the Arduino Uno and the Nano (using an Atmega328P) you have:

  • 1 kB of EEPROM
  • 2 kB of RAM
  • 32 kB of PROGMEM

Clearly, there is a lot more program memory than RAM.

Variables are copied into RAM at program startup

The problem with constants in general (including string constants like "hello") is that the compiler generates code to copy them into RAM at program startup, because most C functions do not expect to find data in PROGMEM. (It has to generate different instructions to access PROGMEM).

Benchmark memory use

To test memory use, I'll start off with a sketch that simply returns how much memory is free, without doing anything else. Tested on IDE 1.0.6, Arduino Uno, on Ubuntu.

#include "memdebug.h"

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  Serial.print (F("Free memory = "));
  Serial.println (getFreeMemory ());
  }  // end of setup

void loop () { } 

Output:

Free memory = 1702

Since we started with 2048 bytes of RAM, already we have used 2048 - 1702 = 346 bytes. These are used as follows:

34 bytes for the HardwareSerial instance (Serial)
64 bytes for the Serial transmit buffer
64 bytes for the Serial receive buffer
4 bytes for the Serial transmit buffer head and tail pointers
4 bytes for the Serial receive buffer head and tail pointers
9 bytes for keeping track of millis / micros
4 bytes for memory allocation (__malloc_heap_start, __malloc_margin)
128 bytes for the heap safety margin
6 bytes for a few nested function calls (main -> setup -> getFreeMemory)
16 bytes for the compiler vtable for HardwareSerial
4 bytes for variables __brkval and __flp (used in memdebug)
2 bytes pushed onto the stack in main (to save registers)
2 bytes pushed onto the stack in setup (to save registers)
4 bytes pushed onto the stack in getFreeMemory (to save registers)
1 byte because the stack pointer starts at 0x8FF rather than 0x900

(That's 346 bytes accounted for)

Tips

The stack starts at the highest possible address and grows downwards. Each function call would add 2 bytes to the stack (the return address) plus any local (auto) variables would be allocated there.

The heap safety margin is a buffer between the top of the heap and the bottom of the stack. Without it, if you did a "malloc" and got all available memory, there would be no memory left for function calls and local variables in functions.

The vtable is used by the compiler to generate late bindings for virtual functions used by classes.

The compiler generates code to push registers onto the stack when you call a function (the registers used inside that function) so that if the same register was used by the calling function, it would not be corrupted.

Printing constants

Let's print a constant by adding this line:

  Serial.println ("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmo");

Output is now:

Free memory = 1630
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmo

So the free RAM has gone down by 72 bytes, which happens to be be how long that string is (allowing one byte for the 0x00 terminator at the end).

Why? Because the string was copied from PROGMEM, where it must be when the Arduino is turned off, into RAM.

The first thing we can do is use the F() macro, which cunningly expands out to print directly from PROGMEM, thus saving RAM. So we change the line above to read:

  Serial.println (F("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmo"));

Output:

Free memory = 1702
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmo

Back to 1702 bytes of free memory. So, lesson #1 is to use the F() macro as illustrated above whenever printing constants.

More information

I have more information on my page about RAM usage which discusses putting arrays of constants into PROGMEM as well as what I mention above.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.