PROGMEM and struct/class members

I would like to put a class in program space and be able to access members but I am having difficulty doing that in the simplest of examples. Here is my code:

// fun with PROGMEM

#include <avr/pgmspace.h>

#define line {Serial.print(l++);Serial.print(" ");}
typedef unsigned const int prog_uint16_t;

int iRAM=11;
struct A {
public:
  prog_uint16_t    a;
  A():a(33){};
  prog_uint16_t * getAaddr() const { return &a; }
};

PROGMEM  prog_uint16_t iFLASH=22;

A const PROGMEM a;


void setup() {
  int l=1;
  Serial.begin(115200);
  delay(2000); //alow some time to open serial console
  
  line; Serial.print(F("single  int/RAM value    ")); Serial.println(iRAM);
  line; Serial.print(F("single  int/RAM address  ")); Serial.println((int)&iRAM);
  Serial.println("");
  line; Serial.print(F("single  int/PROGMEM value/RAM   ")); Serial.println(iFLASH);
  line; Serial.print(F("single  int/PROGMEM address     ")); Serial.println((int)&iFLASH);
  line; Serial.print(F("single  int/PROGMEM value/Flash ")); Serial.println(pgm_read_word(&iFLASH));
  Serial.println("");
  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());
  line; Serial.print(F("class  int/PROGMEM value/Flash ")); Serial.println(pgm_read_word(&a.a));
  line; Serial.print(F("class  int/PROGMEM value/Flash ")); Serial.println(pgm_read_word(a.getAaddr()));

}

void loop() {

}

And it produces the following output:

1 single  int/RAM value    11
2 single  int/RAM address  256

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

6 class  int/PROGMEM value/RAM   17967
7 class  int/PROGMEM address     417
8 class  int/PROGMEM address     417
9 class  int/PROGMEM value/Flash 0
10 class  int/PROGMEM value/Flash 0

Line 3 looks unusual, but I'm supposing that for small enough programs, some flash locations are accessible using the instructions that normally read RAM. (I see a similar result in line 6 of I eliminate some print() statements.)

Lines 9 and 10 are the ones that trouble me. I cannot seem to retrieve the value of the member of A from flash. I searched the forum for discussions on this and there were several that showed how to put objects into flash but I found none that demonstrated how to retrieve them.

This is built using the 1.5.8 IDE as the 1.0.6 IDE produces the following error (after I clean up the complaints relating to the typedef):

progmem:18: error: a causes a section type conflict

Any help on retrieving member variables from flash is most appreciated! My first real project and I've exceeded RAM capacity!

I don't understand why you want to do this.

Text or tables with data can be created in PROGMEM. What is so big in that class that you want everything in PROGMEM ?

What else do you have in RAM ?
If you really need many buffers for Ethernet and SD card and a pixel display and many more, you are going to need the Arduino Mega 2560 someday anyway.

Hi Peter,
At present I have no plans for Ethernet, storage or advanced display for this app. It is a game/busy box for my grandson consisting of four buttons, eight LEDs and a speaker. When he presses the correct sequence of buttons "something" happens. One response is to play music.

Notes are encoded as a frequency and duration for each note. Musical phrases consist of arrays of notes. Compositions are made up of one or more sequences of phrases (to reduce storage by taking advantage of the redundancy in music.) A couple Christmas carols, Ode to Joy and some interesting sound effects and I have less than 100 bytes of RAM left.

I had been declaring all of these const thinking they were going into flash and was surprised to find they were not. I was happy to see a mechanism to do so and need to get it working. Flash usage is about 10K so there is plenty of room to do more if it doesn't all have to reside in RAM.

I suppose I can reduce the RAM footprint by indexing into tables of notes and durations using byte values but that means I have to re-encode all of the music. It would be easier to implement PROGMEM and would remove the music entirely from RAM vs. cutting less than half.

thanks,
hank

I would go by using a progmem based struct for notes and have progmem stored phrases pointing at the struct items. Would that do?

I have made that a week ago. Both the notes and the duration and an array with pointers to them in PROGMEM. It's on my other computer :cry:
I made it to play a melody in the background, and I don't know if I can extract it for a working sketch.

100 bytes of RAM is not enough, so you have to fix that one way or the other. Do you use String Objects ?
Which Arduino board are you using ?

Do you know the Mozzi Library ?

Do you know toneAC() ?
https://code.google.com/p/arduino-tone-ac/

rlogiacco:
I would go by using a progmem based struct for notes and have progmem stored phrases pointing at the struct items. Would that do?

Yes, that's exactly what I would like to do. But if I cannot access a struct/class member for an object stored in progmem I cannot do that.

Peter_n:
I have made that a week ago. Both the notes and the duration and an array with pointers to them in PROGMEM. It's on my other computer :cry:
I made it to play a melody in the background, and I don't know if I can extract it for a working sketch.

100 bytes of RAM is not enough, so you have to fix that one way or the other. Do you use String Objects ?
Which Arduino board are you using ?

Do you know the Mozzi Library ?
Mozzi

Do you know toneAC() ?
https://code.google.com/p/arduino-tone-ac/

No need for string objects. I've looked at Mozzi and toneAC and settled on the "arduino tone library" found on github as most appropriate for my needs. I'm using the Uno for development and the target is a custom board with the same chip (socketed, so I can place it in an Uno for S/W updates.)

Can anyone tell me why my sketch in the OP does not do what I expect it to do?

thanks,
hank

HankB:
Can anyone tell me why my sketch in the OP does not do what I expect it to do?

No.

I'm on my other computer now. I took a part of my code, so it is only a rough layout of the code. It is working with the rest of my sketch on my Mega board with Arduino 1.5.8, but the example below is not tested.

#include "pitches.h"

const int pinPiezo = 9;

// Some commands, a melody could have something special.
#define NO_GAP 100


// Array with data.
// A pause is with the frequency 0
// An end is with the duration at 0


// Declare many melodies and noteDurations.
// Every list ends with a 0

const int melody0[] PROGMEM = {
  NOTE_E1, ....
  0
};

const int noteDurations0[] PROGMEM = {
  4, ...
  0
 };

// Create a list for the melodies, also an extra code for a command.

struct MELODY_STRUCT
{
  const int PROGMEM * pMelody;
  const int PROGMEM * pNoteDurations;
  int code;
};

const MELODY_STRUCT melodyList[] PROGMEM = {
  { melody0, noteDurations0, 0 },
  { melody1, noteDurations1, 0 },
  { melody2, noteDurations2, 0 },
  { melody3, noteDurations3, 0 },
  { melody4, noteDurations4, 0 },
  { melody5, noteDurations5, NO_GAP },
};


void setup()
{
}

void loop()
{
  // Do here the normal things that needs to be done.
  ...


  if( button == LOW)
    Beep(3);        // play melody3 in the background

  // At the end of loop(), call BeepUpdate().
  BeepUpdate();
}

// The beepNext (interval) and beepPrevMillis are both needed, to avoid the rollover problem.
static bool beepRunning = false;
static int beepMelody;
static int beepIndex;
static unsigned int beepNext;
static unsigned long beepPrevMillis;

void Beep( int melody)
{
  beepRunning = true;
  beepIndex = 0;
  beepNext = 0;
  beepMelody = melody;
  beepPrevMillis = millis();
  
  // start immediately with the first note
  BeepUpdate();
}


void BeepUpdate()
{
  if( !beepRunning)
    return;
    
  unsigned long beepCurrentMillis = millis();
  
  if( beepCurrentMillis - beepPrevMillis >= beepNext)
  {
    // Set the previous value to the current value.
    // If the current value was delayed by the sketch,
    // so will the tune be delayed.
    // This prevents that the tune is truncated.
    beepPrevMillis = beepCurrentMillis;
    
    const int PROGMEM * pMel = (const int PROGMEM *) pgm_read_word( &melodyList[beepMelody].pMelody);
    const int PROGMEM * pDur = (const int PROGMEM *) pgm_read_word( &melodyList[beepMelody].pNoteDurations);
    int code = pgm_read_word( &melodyList[beepMelody].code);
    int f = pgm_read_word(&pMel[beepIndex]);
    int t = pgm_read_word(&pDur[beepIndex]);
    
    if( t == 0)
    {
      beepRunning = false;
      noTone( pinPiezo);
    }
    else
    {
      // The tone is played for the duration.
      // But a new tone is started with an extra delay.
      // This creates a silent gap between the tones.
      int duration = 1000 / t;
      
      if( code = NO_GAP)
      {
        // No gap, not for a melody, but for alarm sound for example.
        // The tone is played without duration.
        tone( pinPiezo, f);
        beepNext = duration;
      }
      else
      {
        tone( pinPiezo, f, duration);
        beepNext = duration + (duration / 4);
      }
      beepIndex++;
    }
  }
}

Hi Peter,
Thanks for taking the time to put together that example. It encompassed something of great interest to me: accessing struct members from PROGMEM. I copied it and (with a little editing) ran it. I didn't play any music but I printed out the information that was read from PROGMEM.

The modified code is as follows:

//#include "pitches.h"

#define NOTE_E1 567

const int pinPiezo = 9;

// Some commands, a melody could have something special.
#define NO_GAP 100


// Array with data.
// A pause is with the frequency 0
// An end is with the duration at 0


// Declare many melodies and noteDurations.
// Every list ends with a 0

const int melody0[] PROGMEM = {
  NOTE_E1,
  0
};

const int noteDurations0[] PROGMEM = {
  4,
  0
 };

// Create a list for the melodies, also an extra code for a command.

struct MELODY_STRUCT
{
  const int PROGMEM * pMelody;
  const int PROGMEM * pNoteDurations;
  int code;
};

const MELODY_STRUCT melodyList[] PROGMEM = {
  { melody0, noteDurations0, 0 },
//  { melody1, noteDurations1, 0 },
//  { melody2, noteDurations2, 0 },
//  { melody3, noteDurations3, 0 },
//  { melody4, noteDurations4, 0 },
//  { melody5, noteDurations5, NO_GAP },
};


void setup()
{
  Serial.begin(115200);
}

void loop()
{
  // Do here the normal things that needs to be done.
  // ...


 // if( button == LOW)
    Beep(3);        // play melody3 in the background

  // At the end of loop(), call BeepUpdate().
  BeepUpdate();
}

// The beepNext (interval) and beepPrevMillis are both needed, to avoid the rollover problem.
static bool beepRunning = false;
static int beepMelody;
static int beepIndex;
static unsigned int beepNext;
static unsigned long beepPrevMillis;

void Beep( int melody)
{
  beepRunning = true;
  beepIndex = 0;
  beepNext = 0;
  beepMelody = melody;
  beepPrevMillis = millis();
  
  // start immediately with the first note
  BeepUpdate();
}


void BeepUpdate()
{
  if( !beepRunning)
    return;
    
  unsigned long beepCurrentMillis = millis();
  
  if( beepCurrentMillis - beepPrevMillis >= beepNext)
  {
    // Set the previous value to the current value.
    // If the current value was delayed by the sketch,
    // so will the tune be delayed.
    // This prevents that the tune is truncated.
    beepPrevMillis = beepCurrentMillis;
    
    const int PROGMEM * pMel = (const int PROGMEM *) pgm_read_word( &melodyList[beepMelody].pMelody);
    const int PROGMEM * pDur = (const int PROGMEM *) pgm_read_word( &melodyList[beepMelody].pNoteDurations);
    int code = pgm_read_word( &melodyList[beepMelody].code);
    int f = pgm_read_word(&pMel[beepIndex]);
    int t = pgm_read_word(&pDur[beepIndex]);
    Serial.print("pMel "); Serial.print((unsigned int)pMel);
    Serial.print(" pDur "); Serial.print((unsigned int)pDur);
    Serial.print(" code "); Serial.print(code);
    Serial.print(" f "); Serial.print(f);
    Serial.print(" t "); Serial.println(t);
    
    if( t == 0)
    {
      beepRunning = false;
      noTone( pinPiezo);
    }
    else
    {
      // The tone is played for the duration.
      // But a new tone is started with an extra delay.
      // This creates a silent gap between the tones.
      int duration = 1000 / t;
      
      if( code = NO_GAP)
      {
        // No gap, not for a melody, but for alarm sound for example.
        // The tone is played without duration.
        tone( pinPiezo, f);
        beepNext = duration;
      }
      else
      {
        tone( pinPiezo, f, duration);
        beepNext = duration + (duration / 4);
      }
      delay(20000);
      beepIndex++;
    }
  }
}
pMel 8 pDur 258 code 0 f -27636 t -16380

The pMel value looks questionable. That's in the vector space for the processor. The values for tone and duration are not what they should be. It seems that this does not work as expected either.

thanks,
hank

melody3 does not exist, and number 3 is played. Play melody0 and it will work.

A pointer to PROGMEM does not have a very high address, since PROGMEM starts at zero. It is a different memory than ram. So a pointer with value 110 to PROGMEM points to something else than a pointer with value 110 to ram.

This is a working sketch:

//#include "pitches.h"

#define NOTE_E1 567

const int pinPiezo = 9;

// Some commands, a melody could have something special.
#define NO_GAP 100


// Array with data.
// A pause is with the frequency 0
// An end is with the duration at 0


// Declare many melodies and noteDurations.
// Every list ends with a 0

const int melody0[] PROGMEM = {
  NOTE_E1,
  0
};

const int noteDurations0[] PROGMEM = {
  4,
  0
 };

// Create a list for the melodies, also an extra code for a command.

struct MELODY_STRUCT
{
  const int PROGMEM * pMelody;
  const int PROGMEM * pNoteDurations;
  int code;
};

const MELODY_STRUCT melodyList[] PROGMEM = {
  { melody0, noteDurations0, 0 },
//  { melody1, noteDurations1, 0 },
//  { melody2, noteDurations2, 0 },
//  { melody3, noteDurations3, 0 },
//  { melody4, noteDurations4, 0 },
//  { melody5, noteDurations5, NO_GAP },
};


void setup()
{
  Serial.begin(115200);
  
}

void loop()
{
  // Do here the normal things that needs to be done.
  // ...


  // Start melody 0
  Beep(0);

  // At the end of loop(), call BeepUpdate().
  BeepUpdate();
}

// The beepNext (interval) and beepPrevMillis are both needed, to avoid the rollover problem.
static bool beepRunning = false;
static int beepMelody;
static int beepIndex;
static unsigned int beepNext;
static unsigned long beepPrevMillis;

void Beep( int melody)
{
  beepRunning = true;
  beepIndex = 0;
  beepNext = 0;
  beepMelody = melody;
  beepPrevMillis = millis();
  
  // start immediately with the first note
  BeepUpdate();
}


void BeepUpdate()
{
  if( !beepRunning)
    return;
    
  unsigned long beepCurrentMillis = millis();
  
  if( beepCurrentMillis - beepPrevMillis >= beepNext)
  {
    // Set the previous value to the current value.
    // If the current value was delayed by the sketch,
    // so will the tune be delayed.
    // This prevents that the tune is truncated.
    beepPrevMillis = beepCurrentMillis;
    
    const int PROGMEM * pMel = (const int PROGMEM *) pgm_read_word( &melodyList[beepMelody].pMelody);
    const int PROGMEM * pDur = (const int PROGMEM *) pgm_read_word( &melodyList[beepMelody].pNoteDurations);
    int code = pgm_read_word( &melodyList[beepMelody].code);
    int f = pgm_read_word(&pMel[beepIndex]);
    int t = pgm_read_word(&pDur[beepIndex]);
    Serial.print("pMel "); Serial.print((unsigned int)pMel);
    Serial.print(" pDur "); Serial.print((unsigned int)pDur);
    Serial.print(" code "); Serial.print(code);
    Serial.print(" f "); Serial.print(f);
    Serial.print(" t "); Serial.println(t);
    
    if( t == 0)
    {
      beepRunning = false;
      noTone( pinPiezo);
    }
    else
    {
      // The tone is played for the duration.
      // But a new tone is started with an extra delay.
      // This creates a silent gap between the tones.
      int duration = 1000 / t;
      
      if( code = NO_GAP)
      {
        // No gap, not for a melody, but for alarm sound for example.
        // The tone is played without duration.
        tone( pinPiezo, f);
        beepNext = duration;
      }
      else
      {
        tone( pinPiezo, f, duration);
        beepNext = duration + (duration / 4);
      }
      beepIndex++;
    }
  }

  delay(1000);
}

Thanks! I see that the error in my version of the sketch had nothing to do with PROGMEM. (Array bounds...)

Now to apply that to my sketch.

Edit: the problem seems to relate to the constructor. If I comment out my constructors and initilaize the way you did, I get )more or less) the result I expect. It still makes me a bit queasy that I can access the variables directly as well as using pgm_read_() but as long as the latter works, I'm happy.

Edit.2: 50807 – [avr] Constructor writing to RAM for variable in Flash

hbarta@cypress:~$ arduino-1.5.8/hardware/tools/avr/bin/avr-g++ --version
avr-g++ (GCC) 4.8.1
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

hbarta@cypress:~$

Did you read this : Gammon Forum : Electronics : Microprocessors : Putting constant data into program memory (PROGMEM)

I don't understand that "Constructor writing to RAM for variable in Flash" bug. I use Arduino 1.5.8 and I think that everything is okay with my sketch.

Peter_n:
Did you read this : Gammon Forum : Electronics : Microprocessors : Putting constant data into program memory (PROGMEM)

Yes I did - Mr. Gammon has several pages that I find very helpful.

I don't understand that "Constructor writing to RAM for variable in Flash" bug. I use Arduino 1.5.8 and I think that everything is okay with my sketch.

I can't say fully understand it either. What I do see is that a constructor initializer for an object that is destined for PROGMEM puts the value in RAM instead. That would seem to be consistent with the behavior I saw too (reading back zeroes.) And when I commented out the constructors, I observed correct behavior. Your sketch did not use constructors either and did not provoke that bug.