Access to an array of pointer in the flash memory

Hi everyone,

I’m currently doing a project which requires me to store a lot of data into a structure. So as to not fill completly the sRAM I have initialized the strucuture with PROGMEM.

struct Melodie
{
  unsigned int note[200];
  unsigned int* duree[200];
  int bpm;
  int nb_note;
};
 
typedef struct Melodie Melodie;

Whereas I have no problems to access to the value inside note, bpm and nb_note thanks to pgm_read_word(), I haven’t managed to do it with the array of pointer duree.

For instance like this with musique an array of the structure Melodie :

Serial.println(pgm_read_word(&musique[1].bpm));

I have tried to access the array of pointer the same way I did with the other field but I only got the ‘0’ value. :frowning:

I am quiet new to programmation so I might be missing something obvious. Can anyone help me with my problem or lead me to where I should look ?

Thanks a lot for your help :slight_smile:

Show us your full code. Snippets don't convey enough information.

I agree with OldSteve, however this might help: Gammon Forum : Electronics : Microprocessors : Putting constant data into program memory (PROGMEM)

You do not need this line:
typedef struct Melodie Melodie;

Just use:
Melodie
.

Thank you all for your interrest. I will try to describe my project to make my issue more understable.

My project is basicaly a memory game based on lights and sounds. A part of the project requires me to be able to play tunes on the buzzer. I wanted my program to make it easy to add more music to the board thanks to a music sheet. This is why I wanted my tunes to be described with two arrays, one for the note, and one for the time each note has to be held. All the tunes are declared in an array structure with PROGMEM so that they are stored in the flash memory and not sRAM ( which is too small for that kind of use I think)

The issue I quickly faced is that the time each note has to be held depends of the bpm (beat per minute, which means a crotchet doesn’t last the same amount of time in two different tunes) of the song.

To cope with this issue, I thought of using an array of pointer for the time so that before playing a song I can set (set_rythm function) the new duration of a crotchet, minim etc with the elements of the array pointing to these new values. I don’t know, though if it is the easiest way to proceed. Maybe I can do otherwise with something I didn’t think of (which is likely :slight_smile: ).

Here is so far the code I have made :

.c file

#include  "note.h"

#define HP_PIN      12
#define BAUD_RATE   9600
#define NB_LED      7


unsigned int   n ;      //last a crotchet
unsigned int   b= 2*n;  //       minim 
unsigned int   c = n/2;  //      quaver,eighth note
unsigned int   dc = n/4; //      semiquaver, sixteenth note
unsigned int    r = 4*n; //      semibreve 
unsigned int    t = n/3; //      tripler
unsigned int   duree_spe;
unsigned int   duree_spe2;
unsigned int   duree_spe3;

unsigned int   *N = &n;
unsigned int   *B= &b; 
unsigned int   *C = &c;   
unsigned int   *DC = &dc; 
unsigned int   *R = &r;
unsigned int   *T = &t;
unsigned int   *DUREE_SPE = &duree_spe;
unsigned int   *DUREE_SPE2 = &duree_spe2;
unsigned int   *DUREE_SPE3 = &duree_spe3;




const Melodie musique[2] PROGMEM= {
      {     {RE3, RE3, RE3, RE3, bSI2, DO3, //41 Note BPM 130
                       RE3, SIL, DO3, RE3,
                       LA2, SOL2, LA2, SOL2, DO3,
                        DO3, SI2, DO3, SI2, SI2,
                       LA2, SOL2, dFA2, SOL2, MI2,SIL,
                       LA2, SOL2, LA2, SOL2, DO3,
                       DO3, SI2, DO3, SI2, SI2,
                       LA2, SOL2, LA2, DO3, RE3, SIL},
      
              { T, T, T, N , N , N ,
                       T, T, T , point(B),
                       N, N, N, C, N,
                       C ,N ,C, N, C,
                       N, N, N, C , liaison(C,point(B)),N,
                       N , N, N, C, N ,
                       C, N, C, N, C,
                       N, N, N , C, liaison(C,point(B)), N},
                       
                       130,
                       41},

      { {DO4, dDO4, RE4, SIL, //17 notes BPM 200
                        SI3, FA4, SIL, FA4,
                        FA4, MI4, RE4, 
                        DO4, MI3, SIL, MI3,
                        DO3, SIL},

         {DC, DC, C, liaison(B,N),
                         N, N, N, N,
                         mult(R) , mult(R) ,mult(R),
                         N, N, N, N,
                         N, liaison(B,N) },

        200,
        17}
};

int led[NB_LED] = {3, 4, 5, 6, 7, 8, 9};

void setup() {
  
  for (int i = 0; i < 7; i++)
  {
    pinMode(led[i], OUTPUT);
  }

  pinMode(HP_PIN, OUTPUT);


  Serial.begin(BAUD_RATE);

}

void loop() {


  play_melody(1);

}


void play_melody(int num)
{
    int j = 0;

    set_rythm(num);
 
    for(int i = 0; i <pgm_read_word(&musique[num].nb_note);i++)
    {
       
        tone(HP_PIN, pgm_read_word(&musique[num].note[i]), *musique[num].duree[i]);

        
        digitalWrite(led[j],HIGH);
        
        delay(1+ *musique[num].duree[i]);

        digitalWrite(led[j], LOW);

        j++;

        if(j == NB_LED)
          j = 0;
    }


}

void set_rythm(int num)
{
    n = 60000/pgm_read_word(&musique[num].bpm);
    b= 2*n; 
    c = n/2;   
    dc = n/4; 
    r = 4*n;
    t = n/3; 
}

unsigned int* liaison(unsigned int *noteA, unsigned int *noteB)
{
    *DUREE_SPE = *noteA+ *noteB;
    return DUREE_SPE;
}

unsigned int* point(unsigned int *noteA)
{
    *DUREE_SPE2 = *noteA *1.5;
    return DUREE_SPE2;
}

unsigned int* mult(unsigned int *noteA)
{
    *DUREE_SPE3 = *noteA *0.33;
    return DUREE_SPE3;
}

.h file

#include <avr/pgmspace.h>
//1ER OCTAVE //

#define DO1    65.41
#define dDO1   69.3
#define RE1    73.42
#define MI1    82.41
#define FA1    87.31
#define dFA1   92.5
#define SOL1   98
#define LA1    110
#define bSI1   116.54
#define SI1    123.47

//2EME OCTAVE   //

#define DO2    130.81
#define dDO2   138.59
#define RE2    146.83
#define MI2    164.81
#define FA2    174.61
#define dFA2   185
#define SOL2   196
#define LA2    220
#define bSI2   233.08 
#define SI2    246.94

// 3EME OCTAVE //
#define DO3    261.63
#define dDO3   277.18
#define RE3    293.66
#define MI3    329.63
#define FA3    349.23
#define dFA3   369.99
#define SOL3   392
#define bLA3   415.3 
#define LA3    440
#define bSI3   466.16 
#define SI3    493.88

// 4EME OCTAVE //
#define DO4    523.25
#define dDO4   554.37
#define RE4    587.33
#define MI4    659.26
#define FA4    698.46 
#define dFA4   739.99
#define SOL4   783.99
#define LA4    880
#define bSI4   932.33 
#define SI4    987.77


// 5EME OCTAVE//
#define DO5    1046.5
#define dDO5   1108.73
#define RE5    1174.66
#define MI5    1318.51
#define FA5    1396.91
#define dFA5   1479.98
#define SOL5   1567.98
#define LA5    1760
#define bSI5   1864.66
#define SI5    1975.53

//6EME OCTAVE//

#define DO6    2093
#define dDO6   2217.46
#define RE6    2349.32
#define MI6    2637.02
#define FA6    2793.83
#define dFA6   2959.96
#define SOL6   3135.96
#define LA6    3520
#define bSI6   3279.31
#define SI6    3951.07


#define SIL     0


/*Structure*/

struct Melodie
{
  unsigned int note[200];
  unsigned int* duree[200];
  int bpm;
  int nb_note;
};
 
typedef struct Melodie Melodie;



/*Prototypes*/
unsigned int* point(unsigned int *noteA);
unsigned int* liaison(unsigned int *noteA, unsigned int *noteB);
unsigned int* mult(unsigned int *noteA);
void play_melody(int num);
void set_rythm(int num);

In the play_melody function, I call the tone function which has, as last parameter, the duration of the note. But it seems I can’t access this information. The way I tried doesn’t work because I think pgm_read_word can only returns an int type.

Does anyone know how I should proceed ? Thank you :slight_smile:

PS : Don’t pay attention to the liaison(), point(), mult() function, this is another issue I have to adress after this one. :slight_smile:

musicplayer.ino (3.13 KB)

note.h (5.6 KB)

The way I tried doesn’t work because I think pgm_read_word can only returns an int type.

But pgm_read_byte() and other functions read and return other types.

Thank you for your answer. :slight_smile:
Hmmm I have tried this code

int *test = (int*)pgm_read_byte(&musique[1].duree[1]);
 Serial.println(*test);

But in the console I only get “0”.

I have tried also without the “cast” (first time I am using this, maybe I am doing it wrong) for the same result. :frowning:

Why are you storing the byte in an integer pointer? Why are you casting the byte to an integer pointer?

byte crap = pgm_read_byte(someAddress);

Working code to show storing and using pointers to PROGMEM stored in PROGMEM.
I can’t say that none of it has been deprecated but it does work.

PROGMEM pointer is PGM_P.

#include <avr/io.h>
#include <avr/pgmspace.h>

const byte phrases = 16;
const char phrase00[] PROGMEM = "void * memccpy (void *, const void *, int, size_t)";
const char phrase01[] PROGMEM = "void * memchr (const void *, int, size_t) __ATTR_PURE__";
const char phrase02[] PROGMEM = "int memcmp (const void *, const void *, size_t) __ATTR_PURE__";
const char phrase03[] PROGMEM = "void * memcpy (void *, const void *, size_t)";
const char phrase04[] PROGMEM = "void * memmem (const void *, size_t, const void *, size_t) __ATTR_PURE__";
const char phrase05[] PROGMEM = "void * memmove (void *, const void *, size_t)";
const char phrase06[] PROGMEM = "void * memrchr (const void *, int, size_t) __ATTR_PURE__";
const char phrase07[] PROGMEM = "void * memset (void *, int, size_t)";
const char phrase08[] PROGMEM = "char* strcat( char* dest, const char* src ) adds src to dest";
const char phrase09[] PROGMEM = "char* strchr( const char *src, int val ) returns first val in src";
const char phrase10[] PROGMEM = "int strcmp( const char* s1, const char* s2 ) returns 1, 0, -1";
const char phrase11[] PROGMEM = "char* strcpy( char* dest, const char *src ) copies src to dest";
const char phrase12[] PROGMEM = "size_t strlen( const char* src ) returns the length of src";
const char phrase13[] PROGMEM = "char* strlwr( char* s	) converts s upper case alphas to lower case";
const char phrase14[] PROGMEM = "char* strstr( const char* s1, const char* s2 ) find s2 in s1";
const char phrase15[] PROGMEM = "char* strupr( char* s	) converts s lower case alphas to upper case";


PGM_P const phrase_list[ phrases ] PROGMEM =	   // change "string_table" name to suit
{
  phrase00, phrase01, phrase02, phrase03, phrase04,
  phrase05, phrase06, phrase07, phrase08, phrase09,
  phrase10, phrase11, phrase12, phrase13, phrase14, phrase15
};

byte data, idx;

void setup()
{
  Serial.begin( 115200 );
  Serial.println( F( "TEST\n" ));

  PGM_P flash;  

  for ( idx = 0; idx < 16; idx++ )
  {
    flash = ( PGM_P ) pgm_read_word( &( phrase_list[ idx ] ));
    do
    {
      if ( data = pgm_read_byte( flash++ ))
      {
        Serial.print(( char ) data );
        // Serial.print( " " );
      }
    }
    while ( data );

    Serial.println( );
  }

  Serial.println( "\n" );
}


void loop()
{
}
tone(HP_PIN, pgm_read_word(&musique[num].note[i]), *pgm_read_byte(&musique[num].duree[i]));

As PaulS said, dereferencing (as a pointer) a byte is certainly not going to work.

Don't just change pgm_read_word to pgm_read_byte to get rid of compiler errors. You just change the problem to a runtime error.