How to use const class objects in PROGMEM?

Status
Not open for further replies.

wolfv

Well-known member
My Teensy 2.0 project is running low on RAM.
The project contains many arrays of const objects.
And there is more than enough flash on Teensy 2.0 to use PROGMEM.

The following example sketch compiles in Arduino 1.6.5-r5 with C++11, and runs on Teensy 2.0
(How I added C++11 to Arduino for Teensy 2.0 is explained in: https://forum.pjrc.com/threads/30474-User-Name )

When USE_PROGMEM is defined, the sketch compiles with the objects in PROGMEM.
The sketch was compiled and run twice; once with PROGMEM, and once without PROGMEM.
Using PROGMEM saves 24 bytes of RAM, which seems about right.
But the const PROGMEM objects prints the wrong values.

The example sketch:
Code:
//comment or uncomment the #define to toggle PROGMEM
#define USE_PROGMEM

// return amount of free RAM, code copied from http://web-engineering.info/node/30
extern unsigned int __bss_end;
extern unsigned int __heap_start;
extern void *__brkval;

//compute and return amount of free RAM
uint16_t getFreeRAM()
{
    uint8_t newVariable;
    // if heap is empty, use bss as start memory address
    if ((uint16_t)__brkval == 0)
    {
        return (((uint16_t)&newVariable) - ((uint16_t)&__bss_end));
    }
    // else use heap end as the start of the memory address
    else
    {
        return (((uint16_t)&newVariable) - ((uint16_t)__brkval));
    }
};

class Num
{
    private:
        const uint16_t num;

    public:
        //"constexpr" to evaluate the value at compile time
        constexpr Num( const uint16_t n ) : num(n) {}

        void print() const
        {
            Keyboard.print("num=");
            Keyboard.println( num );
        }
};

#ifdef USE_PROGMEM
const PROGMEM Num n_1(1);
const PROGMEM Num n_2(2);
const PROGMEM Num n_3(3);
const PROGMEM Num n_4(4);
const PROGMEM Num n_5(5);
#else
const Num n_1(1);
const Num n_2(2);
const Num n_3(3);
const Num n_4(4);
const Num n_5(5);
#endif

void setup()
{
    Keyboard.begin();
    delay(1000);

//print heading
#ifdef USE_PROGMEM
    Keyboard.println("\nUsing PROGMEM");
#else
    Keyboard.println("\nNot using PROGMEM");
#endif

    Keyboard.print("Free RAM = ");
    Keyboard.println( getFreeRAM() );

    //print the object values
    n_1.print();
    n_2.print();
    n_3.print();
    n_4.print();
    n_5.print();
}

void loop() {}

Using PROGMEM, the objects output the wrong values:
Code:
Using PROGMEM
Free RAM = 2467
num=0
num=0
num=0
num=0
num=0

Not using PROGMEM, the objects output the correct values:
Code:
Not using PROGMEM
Free RAM = 2453
num=1
num=2
num=3
num=4
num=5

I tried putting the object in arrays, but the problem was not with the array.
The problem was with getting the objects to work in PROGMEM.

I am using Teensy 2.0 because the PCB for the project was already done for Teensy 2.0 before Teensy LC came out.
The project doesn't use strings, so the F("string") macro wouldn't help.

How to to use class objects in PROGMEM?:confused:

Compiler verbose output when USE_PROGMEM is defined:
Code:
/opt/arduino-1.6.5-r5/hardware/tools/avr/bin/avr-g++ -c -Os -g -Wall -ffunction-sections -fdata-sections -MMD -fno-exceptions -felide-constructors -std=c++11 -mmcu=atmega32u4 -DTEENSYDUINO=125 -DARDUINO=10605 -DF_CPU=16000000L -DARDUINO_ARCH_AVR -DUSB_HID -DLAYOUT_US_ENGLISH -I/opt/arduino-1.6.5-r5/hardware/teensy/avr/cores/teensy /tmp/build7492478963666243005.tmp/PROGMEM_objects.cpp -o /tmp/build7492478963666243005.tmp/PROGMEM_objects.cpp.o 
Using previously compiled file: /tmp/build7492478963666243005.tmp/malloc.c.o
Using previously compiled file: /tmp/build7492478963666243005.tmp/pins_teensy.c.o
Using previously compiled file: /tmp/build7492478963666243005.tmp/keylayouts.c.o
Using previously compiled file: /tmp/build7492478963666243005.tmp/usb.c.o
Using previously compiled file: /tmp/build7492478963666243005.tmp/wiring.c.o
Using previously compiled file: /tmp/build7492478963666243005.tmp/WInterrupts.c.o
Using previously compiled file: /tmp/build7492478963666243005.tmp/Stream.cpp.o
Using previously compiled file: /tmp/build7492478963666243005.tmp/usb_api.cpp.o
Using previously compiled file: /tmp/build7492478963666243005.tmp/IPAddress.cpp.o
Using previously compiled file: /tmp/build7492478963666243005.tmp/new.cpp.o
Using previously compiled file: /tmp/build7492478963666243005.tmp/HardwareSerial.cpp.o
Using previously compiled file: /tmp/build7492478963666243005.tmp/WMath.cpp.o
Using previously compiled file: /tmp/build7492478963666243005.tmp/Print.cpp.o
Using previously compiled file: /tmp/build7492478963666243005.tmp/Tone.cpp.o
Using previously compiled file: /tmp/build7492478963666243005.tmp/WString.cpp.o
Using previously compiled file: /tmp/build7492478963666243005.tmp/yield.cpp.o
Using previously compiled file: /tmp/build7492478963666243005.tmp/main.cpp.o
Using previously compiled file: /tmp/build7492478963666243005.tmp/core.a
/opt/arduino-1.6.5-r5/hardware/tools/avr/bin/avr-gcc -Os -Wl,--gc-sections,--relax -mmcu=atmega32u4 -o /tmp/build7492478963666243005.tmp/PROGMEM_objects.cpp.elf /tmp/build7492478963666243005.tmp/PROGMEM_objects.cpp.o /tmp/build7492478963666243005.tmp/core.a -L/tmp/build7492478963666243005.tmp -lm 
/opt/arduino-1.6.5-r5/hardware/tools/avr/bin/avr-objcopy -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0 /tmp/build7492478963666243005.tmp/PROGMEM_objects.cpp.elf /tmp/build7492478963666243005.tmp/PROGMEM_objects.cpp.eep 
/opt/arduino-1.6.5-r5/hardware/tools/avr/bin/avr-objcopy -O ihex -R .eeprom /tmp/build7492478963666243005.tmp/PROGMEM_objects.cpp.elf /tmp/build7492478963666243005.tmp/PROGMEM_objects.cpp.hex 
/opt/arduino-1.6.5-r5/hardware/tools/teensy_post_compile -file=PROGMEM_objects.cpp -path=/tmp/build7492478963666243005.tmp -tools=/opt/arduino-1.6.5-r5/hardware/tools -board=TEENSY2 

Sketch uses 4,218 bytes (13%) of program storage space. Maximum is 32,256 bytes.
Global variables use 113 bytes (4%) of dynamic memory, leaving 2,447 bytes for local variables. Maximum is 2,560 bytes.
 
Last edited:
As noted in your other thread that memory is in a different space - and AVR specific so I can't try it with what I have on my desk.

The Linked page gives a complete sample on storing arrays and doing the needed operation to transfer the data between memory spaces.
 
It seems just that the print() method will not work directly with the constants in the PROG_MEM. You most probably have to "bring them home" first by assigning them to a local variable using pgm_read_byte_near(pointer towards the variable in PROG_MEM).

Why not port your project over to a reasonable ARM processor? I think that the good old AVR times are ending.
 
Last edited:
Thanks defragster and Theremingenieur,

Here is my attempt at using pgm_read_word() in the class function:
Code:
#include <avr/pgmspace.h>

//comment or uncomment the #define to toggle PROGMEM
#define USE_PROGMEM

// return amount of free RAM, code copied from http://web-engineering.info/node/30
extern unsigned int __bss_end;
extern unsigned int __heap_start;
extern void *__brkval;

//compute and return amount of free RAM
uint16_t getFreeRAM()
{
    uint8_t newVariable;
    // if heap is empty, use bss as start memory address
    if ((uint16_t)__brkval == 0)
    {
        return (((uint16_t)&newVariable) - ((uint16_t)&__bss_end));
    }
    // else use heap end as the start of the memory address
    else
    {
        return (((uint16_t)&newVariable) - ((uint16_t)__brkval));
    }
};

uint16_t* ptrNum;

class Num
{
    private:
#ifdef USE_PROGMEM
        const PROGMEM uint16_t num;
#else
        const uint16_t num;
#endif

    public:
        //"constexpr" to evaluate the value at compile time
        constexpr Num( const uint16_t n ) : num(n) {}

        void print() const
        {
            Keyboard.print("num=");
#ifdef USE_PROGMEM
            ptrNum = (uint16_t*) pgm_read_word(&num);
            Keyboard.println( *ptrNum );
#else
            Keyboard.println( num );
#endif
        }
};

const Num n_1(1);
const Num n_2(2);
const Num n_3(3);
const Num n_4(4);
const Num n_5(5);

void setup()
{
    Keyboard.begin();
    delay(1000);

//print heading
#ifdef USE_PROGMEM
    Keyboard.println("\nUsing PROGMEM");
#else
    Keyboard.println("\nNot using PROGMEM");
#endif

    Keyboard.print("Free RAM = ");
    Keyboard.println( getFreeRAM() );

    //print the object values
    n_1.print();
    n_2.print();
    n_3.print();
    n_4.print();
    n_5.print();
}

void loop() {}
But the output is still wrong:
Code:
Using PROGMEM
Free RAM = 2455
num=161
num=0
num=0
num=0
num=0
The only example of class objects (struct with a function) that I found is on http://www.avrfreaks.net/forum/can-i-put-classstruct-objects-progmem
 
Last edited:
Can you skip the class stuff and use the example code from the link and confirm it works for you.

The way it is set up to print is not properly calling the access function transfer the values to the usable memory space.

Having a single array of numbers will be more efficient with RAM at run time too. It seems to me the pointers and overhead of instantiating the pointer and class object will take more room than the numbers themselves.

The C example on the web relied on arrays and if you had 10 or 1000 numbers in the array the overhead would be the same and straightforward.
 
defragster,

I ran the sketch from http://www.avrfreaks.net/forum/can-i-put-classstruct-objects-progmem and got similar output:
Code:
1 single  int/RAM value    11
2 single  int/RAM address  256

3 single  int/PROGMEM value/RAM   22
4 single  int/PROGMEM address     483
5 single  int/PROGMEM value/Flash 22

6 class  int/PROGMEM value/RAM   33
7 class  int/PROGMEM address     485
8 class  int/PROGMEM address     485
9 class  int/PROGMEM value/Flash 0
10 class  int/PROGMEM value/Flash 0
Then I simplified the sketch, added function A.print(), and added function getFreeRAM():
Code:
#include <avr/pgmspace.h>

//comment or uncomment the #define to toggle PROGMEM
#define USE_PROGMEM

// return amount of free RAM, code copied from http://web-engineering.info/node/30
extern unsigned int __bss_end;
extern unsigned int __heap_start;
extern void *__brkval;

//compute and return amount of free RAM
uint16_t getFreeRAM()
{
    uint8_t newVariable;
    // if heap is empty, use bss as start memory address
    if ((uint16_t)__brkval == 0)
    {
        return (((uint16_t)&newVariable) - ((uint16_t)&__bss_end));
    }
    // else use heap end as the start of the memory address
    else
    {
        return (((uint16_t)&newVariable) - ((uint16_t)__brkval));
    }
};

#define line {Serial.print(l++);Serial.print(" ");}

#ifdef USE_PROGMEM
typedef const PROGMEM int prog_uint16_t;
#else
typedef const int prog_uint16_t;
#endif

class A
{
    public:
        prog_uint16_t    a;
        A(int x):a(x){};
#ifdef USE_PROGMEM
        const prog_uint16_t PROGMEM * getAaddr() const
#else
        const prog_uint16_t * getAaddr() const
#endif
        {
            return &a;
        }
        void print() const
        {
            Serial.print(F("class int/PROGMEM value/RAM   ")); Serial.println(a);
        }
};

#ifdef USE_PROGMEM
//warning: uninitialized variable 'a' put into program memory area [-Wuninitialized]
A const PROGMEM a(33);
#else
A const a(33);
#endif

void setup()
{
    int l=1;
    Serial.begin(115200);
    delay(2000); //allow some time to open serial console

//print heading
#ifdef USE_PROGMEM
    Serial.println(F("\nUsing PROGMEM"));
#else
    Serial.println(F("\nNot using PROGMEM"));
#endif

    Serial.print(F("Free RAM = "));
    Serial.println( getFreeRAM() );

    a.print();

    line; Serial.print(F("class int/PROGMEM value/RAM   ")); Serial.println(a.a);
    line; Serial.print(F("class int/PROGMEM address     ")); Serial.println((int)&a.a);
    line; Serial.print(F("class int/PROGMEM address     ")); Serial.println((int)a.getAaddr());
}

void loop() { }
The sketch was compiled and run twice; once with PROGMEM, and once without PROGMEM.
Using PROGMEM on this sketch didn't save any RAM.

Output using PROGMEM:
Code:
Using PROGMEM
Free RAM = 2501
class int/PROGMEM value/RAM   33
1 class int/PROGMEM value/RAM   33
2 class int/PROGMEM address     323
3 class int/PROGMEM address     323

Output not using PROGMEM uses 2 bytes less RAM:
Code:
Not using PROGMEM
Free RAM = 2499
class int/PROGMEM value/RAM   33
1 class int/PROGMEM value/RAM   33
2 class int/PROGMEM address     282
3 class int/PROGMEM address     282
 
Last edited:
I did a bit on ATtiny before moving to the T_3.1 so where I worried about this before and played with it minimally it isn't something I did a lot of or want to dig one out now to try that I won stop using a T_3.1 or T_3.2 until PJRC releases the upgrade version.

If I were to do it - I'd start with the linked sample from the Arduino.cc site and understand that and see if and how it works first.

I finally see one value of 33 stored in a() - for a single value you won't save anything - tried to cover that in post#6 - as well as the fact that class overhead adds even more to dynamic memory usage when there is behind the scenes 'glue' for making those concepts come to life at run time. There is overhead and memory for every pointer and reference - just like the number itself. Long text strings quickly can make up for that when used that way. But unless you try it with a large group of numbers - an array where the ONE pointer maps to multiple values you'll be lucky to break even and waste code space in the process as well as add the over head of the time to copy it and the buffer space needed to make that transition between memory pools.
 
defragster,

I just noticed an error in my previous post. PROGMEM saved 2 bytes. I have corrected the post.

The original sketch from http://www.avrfreaks.net/forum/can-i-put-classstruct-objects-progmem only has one class A object.
This is not a good example.
It gets a compile time warning: uninitialized variable 'a' put into program memory area [-Wuninitialized]
When I add 4 more class A objects the output is wrong.

Example sketch with 5 class A objects:
Code:
#include <avr/pgmspace.h>

//comment or uncomment the #define to toggle PROGMEM
#define USE_PROGMEM

// return amount of free RAM, code copied from http://web-engineering.info/node/30
extern unsigned int __bss_end;
extern unsigned int __heap_start;
extern void *__brkval;

//compute and return amount of free RAM
uint16_t getFreeRAM()
{
    uint8_t newVariable;
    // if heap is empty, use bss as start memory address
    if ((uint16_t)__brkval == 0)
    {
        return (((uint16_t)&newVariable) - ((uint16_t)&__bss_end));
    }
    // else use heap end as the start of the memory address
    else
    {
        return (((uint16_t)&newVariable) - ((uint16_t)__brkval));
    }
};

#define line {Serial.print(l++);Serial.print(" ");}

#ifdef USE_PROGMEM
typedef const PROGMEM int prog_uint16_t;
#else
typedef const int prog_uint16_t;
#endif

class A
{
    public:
        prog_uint16_t    a;
        A(int x):a(x){};
        void print() const
        {
            Serial.print(F("a=")); Serial.println(a);
        }
};

#ifdef USE_PROGMEM
//warning: uninitialized variable 'a' put into program memory area [-Wuninitialized]
A const PROGMEM a_1(1);
A const PROGMEM a_2(2);
A const PROGMEM a_3(3);
A const PROGMEM a_4(4);
A const PROGMEM a_5(5);
#else
A const a_1(1);
A const a_2(2);
A const a_3(3);
A const a_4(4);
A const a_5(5);
#endif

void setup()
{
    Serial.begin(115200);
    delay(2000); //allow some time to open serial console

//print heading
#ifdef USE_PROGMEM
    Serial.println(F("\nUsing PROGMEM"));
#else
    Serial.println(F("\nNot using PROGMEM"));
#endif

    Serial.print(F("Free RAM = "));
    Serial.println( getFreeRAM() );

    a_1.print();
    a_2.print();
    a_3.print();
    a_4.print();
    a_5.print();
}

void loop() { }

The sketch was compiled and run twice; once with PROGMEM, and once without PROGMEM.

Using PROGMEM the output is wrong:
Code:
Using PROGMEM
Free RAM = 2503
a=1
a=770
a=0
a=4
a=5

Not using PROGMEM uses more RAM:
Code:
Not using PROGMEM
Free RAM = 2493
a=1
a=2
a=3
a=4
a=5

So PROGMEM saves RAM:), if it can be made to work:(.
 
Indeed the point of PROGMEM is to allow 'offline storage' and when done right it will save memory. You need to store in arrays to maximize the ratio of 'needed' pointers to useful data.

I don't know what is attractive about the class stuff. If you rewrote that in 'C' using equivalent functions and storage you might gain even more.

Please try the same comparison using the Linked PROGMEM Arduino.cc sample and not only will you get correct results, but indexing an arrayed group will multiply the savings, any overhead for addressing the array would be in code space and cost you nothing.

Code:
#include <avr/pgmspace.h>


// save some unsigned ints
const PROGMEM  uint16_t charSet[]  = { 65000, 32796, 16843, 10, 11234};

// save some chars
const char signMessage[] PROGMEM  = {"I AM PREDATOR,  UNSEEN COMBATANT. CREATED BY THE UNITED STATES DEPART"};

unsigned int displayInt;
int k;    // counter variable
char myChar;


void setup() {
  Serial.begin(9600);
  while (!Serial);

  // put your setup code here, to run once:
  // read back a 2-byte int
  for (k = 0; k < 5; k++)
  {
    displayInt = pgm_read_word_near(charSet + k);
    Serial.println(displayInt);
  }
  Serial.println();

  // read back a char
  int len = strlen_P(signMessage);
  for (k = 0; k < len; k++)
  {
    myChar =  pgm_read_byte_near(signMessage + k);
    Serial.print(myChar);
  }

  Serial.println();
}

void loop() {
  // put your main code here, to run repeatedly:

}
 
defragster,

I understand what you are saying about arrays in PROGMEM.
I learned PROGMEM on string arrays from these two tutorials, which are similar to your example:
http://www.gammon.com.au/progmem
https://www.arduino.cc/en/Reference/PROGMEM

Your suggestion to write code for arrays is a good one, and if I had to write the firmware over again that's how I would do it.
But the project's firmware is almost done, it runs, and it's been tested. It just needs a few more features.

The classes are needed for polymorphism.
The project's firmware depends on polymorphism.
Arrays of polymorphic objects is fundamental to the sketch's architecture.
It would be easier to redesign the PCB for Teensy LC than to start over on the firmware.
 
That's all interesting. But trying that example will show you it works on your board. So far you have not seen that WRT the problem at hand. Once that moment has been reached you should then be able to embrace and extend the concept.

The problem maybe compile time versus runtime - AFAIK at compile time some part of "A const PROGMEM a_5(5);" is turned into a run time instantiation. Which would mean this cannot work as you want.

A solution might be to use something like the example provided and make the numbers a "C" code resource linking in the PROGMEM data that you tie into your classes 'under the covers'.
 
AVR and PROGMEM (Harvard architecture) used to be a PITA.
Now, accustomed to low cost ARMs with flash and RAM in the same address space, I think of AVRs as a bad sunburn.
 
It works now!
My sketch in post #1 puts a "PROGMEM" macro on every object.
But that isn't necessary, because the class's function is code, which gets put in code space anyways.
Only the class's data needs the PROGMEM macro.

I also put the objects' pointers array in PROGMEM:
Code:
#include <avr/pgmspace.h>

//comment or uncomment the #define to toggle PROGMEM
#define USE_PROGMEM

//return amount of free RAM, code copied from http://web-engineering.info/node/30
extern unsigned int __bss_end;
extern unsigned int __heap_start;
extern void *__brkval;

//compute and return amount of free RAM
uint16_t getFreeRAM()
{
    uint8_t newVariable;
    // if heap is empty, use bss as start memory address
    if ((uint16_t)__brkval == 0)
    {
        return (((uint16_t)&newVariable) - ((uint16_t)&__bss_end));
    }
    // else use heap end as the start of the memory address
    else
    {
        return (((uint16_t)&newVariable) - ((uint16_t)__brkval));
    }
};

class Num
{
    private:
#ifdef USE_PROGMEM
        //warning: '__progmem__' attribute ignored [-Wattributes]
        const PROGMEM int num;
#else
        const int num;
#endif

    public:
        //"constexpr" to evaluate the value at compile time
        constexpr Num( const int n ) : num(n) {}

        void print() const
        {
            Keyboard.print("num=");
            Keyboard.println( num );
        }
};

Num const n_1(1);
Num const n_2(2);
Num const n_3(3);
Num const n_4(4);
Num const n_5(5);

const int numCount = 5;

const
#ifdef USE_PROGMEM
PROGMEM
#endif
Num *const ptrsNums[numCount] = { &n_1, &n_2, &n_3, &n_4, &n_5 };

void setup()
{
    const Num* ptrNum;

    Keyboard.begin();
    delay(1000);

//print heading
#ifdef USE_PROGMEM
    Keyboard.println("\nUsing PROGMEM");
#else
    Keyboard.println("\nNot using PROGMEM");
#endif

    Keyboard.print("Free RAM = ");
    Keyboard.println( getFreeRAM() );

    //print the object values
    for (uint8_t i=0; i<numCount; i++)
    {
#ifdef USE_PROGMEM
        ptrNum = (const Num*) pgm_read_ptr_near (ptrsNums + i);
        ptrNum->print();
#else
        ptrsNums[i]->print();
#endif
    }
}

void loop() {}
Output using PROGMEM:
Code:
Using PROGMEM
Free RAM = 2453
num=1
num=2
num=3
num=4
num=5
Output not using PROGMEM:
Code:
Not using PROGMEM
Free RAM = 2439
num=1
num=2
num=3
num=4
num=5

Thank you for guiding me through the discovery process.
The discovery process will be useful for future problems as well.
 
AVR and PROGMEM (Harvard architecture) used to be a PITA.
Now, accustomed to low cost ARMs with flash and RAM in the same address space, I think of AVRs as a bad sunburn.
I am using Teensy 2.0 because the PCB for the project was already done for Teensy 2.0 before Teensy LC came out.
 
Awesome you stuck with it and found the problem and that it will hopefully save you the bytes you need to finish the project.
 
Oh no! The PROGMEM on class data didn't save any RAM.
All the apparent RAM savings came from the pointers array in PROGMEM, and me forgetting to use F().

So it's back to basics. I try to put data into PROGMEM without an array:
Code:
//comment or uncomment to toggle PROGMEM
#define USE_PROGMEM

// return amount of free RAM, code from http://web-engineering.info/node/30
extern unsigned int __bss_end;
extern unsigned int __heap_start;
extern void *__brkval;

//measure and return amount of free RAM
uint16_t getFreeSRAM()
{
    uint8_t newVariable;
    // if heap is empty, use bss as start memory address
    if ((uint16_t)__brkval == 0)
    {
        return (((uint16_t)&newVariable) - ((uint16_t)&__bss_end));
    }
    // else use heap end as the start of the memory address
    else
    {
        return (((uint16_t)&newVariable) - ((uint16_t)__brkval));
    }
};

void print(const long unsigned num)
{
    Keyboard.print("num=");
    Keyboard.println( num );
}

#ifdef USE_PROGMEM
const PROGMEM long unsigned n_1=1;
const PROGMEM long unsigned n_2=2;
const PROGMEM long unsigned n_3=3;
const PROGMEM long unsigned n_4=4;
const PROGMEM long unsigned n_5=5;
#else
const long unsigned n_1=1;
const long unsigned n_2=2;
const long unsigned n_3=3;
const long unsigned n_4=4;
const long unsigned n_5=5;
#endif

void setup()
{
    Keyboard.begin();
    delay(1000);

//print heading
#ifdef USE_PROGMEM
    Keyboard.println(F("\nUsing PROGMEM"));
#else
    Keyboard.println(F("\nNot using PROGMEM"));
#endif

    Keyboard.print(F("Free SRAM = "));
    Keyboard.println( getFreeSRAM() );

    print(n_1);
    print(n_2);
    print(n_3);
    print(n_4);
    print(n_5);
}

void loop() {}
Output using PROGEM:
Code:
Using PROGMEM
Free SRAM = 2493
num=1
num=2
num=3
num=4
num=5
Output not using PROGEM:
Code:
Not using PROGMEM
Free SRAM = 2493
num=1
num=2
num=3
num=4
num=5
PROGMEM didn't save any RAM.
Is there a way to put data in PROGMEM without an array?
 
Last edited:
Indeed - that was what I expected per my previous post(s). Unless you have your class pull the data from an array, you may as well use native RAM values. If you try it with BYTES [ const PROGMEM byte n_1=1; ], you'll actually lose RAM I suspect.

And the failure to use F() on he debug string affected both cases equally as that was common code. You would still save 14 bytes for the five numbers when stored as an array.
 
I also tried it with uint8_t, uint32_t, and got the exact same output every time:
Code:
Using PROGMEM
Free SRAM = 2493
num=1
num=2
num=3
num=4
num=5

Not using PROGMEM
Free SRAM = 2493
num=1
num=2
num=3
num=4
num=5
 
If the same change was made in both halves of the '#ifdef USE_PROGMEM' then the native RAM 'Not using PROGMEM' version usage would have gone DOWN and the PROGMEM as indicated would have stayed the same.

Trading pointers for native values will gain no ground unless you incorporate arrays and use enough data space to make it worth the effort. Using PROGMEM like F() requires a temp buffer to hold the data on the transfer. That buffer should be transitory or shared - but it has to sit somewhere while it makes a copy to use - i.e. the heap might grow and crash if RAM is that tight.
 
If the same change was made in both halves of the '#ifdef USE_PROGMEM' then the native RAM 'Not using PROGMEM' version usage would have gone DOWN and the PROGMEM as indicated would have stayed the same.
That's what I was expecting. There is something fishy going on here.
Output shows 'Not using PROGMEM' uses the same amount of RAM for uint8_t and uint16_t; that's not right.

The sketch with uint8_t:
Code:
//comment or uncomment to toggle PROGMEM
//#define USE_PROGMEM

// return amount of free RAM, code from http://web-engineering.info/node/30
extern unsigned int __bss_end;
extern unsigned int __heap_start;
extern void *__brkval;

//measure and return amount of free RAM
uint16_t getFreeSRAM()
{
    uint8_t newVariable;
    // if heap is empty, use bss as start memory address
    if ((uint16_t)__brkval == 0)
    {
        return (((uint16_t)&newVariable) - ((uint16_t)&__bss_end));
    }
    // else use heap end as the start of the memory address
    else
    {
        return (((uint16_t)&newVariable) - ((uint16_t)__brkval));
    }
};

void print(const uint8_t num)
{
    Keyboard.print("num=");
    Keyboard.println( num );
}

#ifdef USE_PROGMEM
const PROGMEM uint8_t n_1=1;
const PROGMEM uint8_t n_2=2;
const PROGMEM uint8_t n_3=3;
const PROGMEM uint8_t n_4=4;
const PROGMEM uint8_t n_5=5;
#else
const uint8_t n_1=1;
const uint8_t n_2=2;
const uint8_t n_3=3;
const uint8_t n_4=4;
const uint8_t n_5=5;
#endif

void setup()
{
    Keyboard.begin();
    delay(1000);

//print heading
#ifdef USE_PROGMEM
    Keyboard.println(F("\nUsing PROGMEM"));
#else
    Keyboard.println(F("\nNot using PROGMEM"));
#endif

    Keyboard.print(F("Free SRAM = "));
    Keyboard.println( getFreeSRAM() );

    print(n_1);
    print(n_2);
    print(n_3);
    print(n_4);
    print(n_5);
}

void loop() {}
Output with uint8_t:
Code:
Using PROGMEM
Free SRAM = 2493
num=1
num=2
num=3
num=4
num=5

Not using PROGMEM
Free SRAM = 2493
num=1
num=2
num=3
num=4
num=5
The sketch with uint32_t:
Code:
//comment or uncomment to toggle PROGMEM
//#define USE_PROGMEM

// return amount of free RAM, code from http://web-engineering.info/node/30
extern unsigned int __bss_end;
extern unsigned int __heap_start;
extern void *__brkval;

//measure and return amount of free RAM
uint16_t getFreeSRAM()
{
    uint8_t newVariable;
    // if heap is empty, use bss as start memory address
    if ((uint16_t)__brkval == 0)
    {
        return (((uint16_t)&newVariable) - ((uint16_t)&__bss_end));
    }
    // else use heap end as the start of the memory address
    else
    {
        return (((uint16_t)&newVariable) - ((uint16_t)__brkval));
    }
};

void print(const uint32_t num)
{
    Keyboard.print("num=");
    Keyboard.println( num );
}

#ifdef USE_PROGMEM
const PROGMEM uint32_t n_1=1;
const PROGMEM uint32_t n_2=2;
const PROGMEM uint32_t n_3=3;
const PROGMEM uint32_t n_4=4;
const PROGMEM uint32_t n_5=5;
#else
const uint32_t n_1=1;
const uint32_t n_2=2;
const uint32_t n_3=3;
const uint32_t n_4=4;
const uint32_t n_5=5;
#endif

void setup()
{
    Keyboard.begin();
    delay(1000);

//print heading
#ifdef USE_PROGMEM
    Keyboard.println(F("\nUsing PROGMEM"));
#else
    Keyboard.println(F("\nNot using PROGMEM"));
#endif

    Keyboard.print(F("Free SRAM = "));
    Keyboard.println( getFreeSRAM() );

    print(n_1);
    print(n_2);
    print(n_3);
    print(n_4);
    print(n_5);
}

void loop() {}
Output with uint32_t:
Code:
Using PROGMEM
Free SRAM = 2493
num=1
num=2
num=3
num=4
num=5

Not using PROGMEM
Free SRAM = 2493
num=1
num=2
num=3
num=4
num=5
 
Last edited:
May not be so Odd. Since these are 'const uint32_t n_1=1;' these numbers themselves may be compiled into the code stored in flash as they cannot change. ReRun that sample with the 'const' removed.

Make a function with the local vars on the stack and return the value of 'getFreeSRAM()'.
Pass in a value or two and assign them to the local vars to use them so they don't compiled away or make warnings - then check that the 'getFreeSRAM()' returns what you expect for the size of the variables on the stack.
 
Your right. Free SRAM is as expected after removing "const".
If n_* variables not const, uint32_t consumes 14 bytes more RAM ((4-1)*5=15 bytes) compared to uint8_t.
If n_* variables are const, uint8_t and uint32_t consume the same amount of RAM because these const numbers are compiled into the code and stored in flash.
 
If adding and removing PROGMEM results in the same amount of free RAM, how does one know if PROGMEM was ineffective or the const was compiled into code space?

Answer:
If adding PROGMEM increases free RAM, then PROGMEM is effective.
If adding PROGMEM results in the same amount of free RAM, then remove the const.
If removing the const decreases free RAM,
then const value was compiled into code space,
else PROGMEM is ineffective.​
 
Last edited:
Status
Not open for further replies.
Back
Top