There are two kinds of memory your program must use. The program code goes into flash memory and a 328 will hold a program of 30K+, so it's not your 19K+ code size that is the problem. The second memory is the SRAM memory. This is where all your variables, arrays and the stack data lives.
This SRAM is the memory you are most likely exceeding. The IDE does not report how much sram your program is using up, nor can it accurately tell at compile time as stack usage is a run-time accumulation. The 328p chip has just 2K or SRAM, and the 1280 and 2560 chips have 8K of SRAM space.
There are ways to save on SRAM space. All the string constant characters and other static data can be forced to be located into flash code memory instead of sram memory. See Flash | Arduiniana
There is nothing in the Flash library for detecting memory leaks. The purpose of the Flash library is to manage constants (large arrays of constants, string constants). If your Sketch includes many / large constants, the Flash library will help reduce the amount of SRAM your Sketch uses.
UnaClocker:
Is there a way to make the sketch output the current SRAM usage to the serial port for debugging?
I use the following. I have no idea if it works correctly when the heap is activated (malloc and its ilk). Ideally, the value from the "maximum stack depth" should be used to determine if there is an SRAM overrun (heap and stack overlap).
extern unsigned int __bss_end;
extern void *__brkval;
int freeMemory()
{
int free_memory;
if((int)__brkval == 0)
free_memory = ((int)&free_memory) - ((int)&__bss_end);
else
free_memory = ((int)&free_memory) - ((int)__brkval);
return free_memory;
}
That function works quite well. It will return a point-in-time number of bytes between the top of the stack and the end of the heap. __brkval starts at zero and moves up towards the stack as heap memory is allocated. Using AVR_STACK_POINTER_REG (found in avr/io.h) would be better than taking the address of a local variable near the end of the stack if you wanted to improve that function.
Using the actual stack pointer address is arguably better than taking the address of an automatic variable as an approximation, but the difference will be minor.
The way that AVR stack frames are built, the automatic variable "free_memory" is in fact at the top of the stack -- I think. But that presumes a bit about the layout of the function activation record. Looking at the actual stack pointer, rather than some offset from the frame pointer, is a more sure-fire way to know where the stack has grown to.
If the code were:
{
char buffer[SOME_SIZE];
int free_memory;
int otherbuffer[SOMEOTHERSIZE];
/* some code here that uses malloc() */
if((int)__brkval == 0)
free_memory = ((int)&free_memory) - ((int)&__bss_end);
else
free_memory = ((int)&free_memory) - ((int)__brkval);
}
/* use the value of free_memory to decide what to do */
}
Then the size of the other automatics would affect the calculation, where inspecting the actual stack pointer value would not have this effect. None of this matters that much as long as you do the calculation in a function with a simple stack frame, but if you go in-lining it into bigger modules, it could make a difference.