[SOLVED] How to pass Arrays stored in PROGMEM?

Hello,

I have a boat load of static arrays. They are raw IR codes. There are so many of them, I am unable to compile because of memory consumption, so I need to store them in PROGMEM.

The problem is, I cannot seem to get them out of flash and use them.

For example, if I don't store them in flash, and use the function to send the code out the IR LED, it works (in this case, it will mute the TV)

const unsigned int TvMute[67] = {4450,4350,600,1550,650,1550,650,1550,600,500,650,450,650,500,600,500,600,500,600,1600,600,1550,650,1550,650,450,650,500,600,500,600,500,600,500,600,1600,600,1600,600,1550,650,1550,600,500,650,450,650,500,600,500,600,500,600,500,600,500,600,500,650,1550,600,1600,600,1600,600,1550,6500};
void  sendRaw (const unsigned int buf[],  unsigned int len,  unsigned int hz)
{
   // Set IR carrier frequency
   enableIROut(hz);

   for (unsigned int i = 0;  i < len;  i++) {
      if (i & 1)  space(buf[i]) ;
      else        mark (buf[i]) ;
}

   space(0);  // Always end with the LED off
}

void sendCommand() {
      sendRaw(TvMute, sizeof(TvMute)-1, hz);
}

Doing it this way, produces no results ... now the IRLED does blink (I look at it using a cell phone camera) ... but it doesn't send anything that the television understands, so I'm not sure what's happening, but I am sure that the array is not being represented as it was defined.

const unsigned int TvMute[67]       PROGMEM = {4450,4350,600,1550,650,1550,650,1550,600,500,650,450,650,500,600,500,600,500,600,1600,600,1550,650,1550,650,450,650,500,600,500,600,500,600,500,600,1600,600,1600,600,1550,650,1550,600,500,650,450,650,500,600,500,600,500,600,500,600,500,600,500,650,1550,600,1600,600,1600,600,1550,6500};

void  sendRaw (const unsigned int buf[] PROGMEM,  unsigned int len,  unsigned int hz)
{
    // Set IR carrier frequency
    enableIROut(hz);
    int max = sizeof(buf);
    for (unsigned int i = 0;  i < max;  i++) {
        if (i & 1)  space(pgm_read_word_near(buf[i]));
        else        mark (pgm_read_word_near(buf[i]));
    }
    space(0);  // Always end with the LED off
}

void sendCommand() {
     sendRaw(TvMute, sizeof(TvMute)-1, hz);
}

What is the right way to accomplish what I'm trying to accomplish?

Maybe this untested:

const unsigned int TvMute[67]       PROGMEM = {4450,4350,600,1550,650,1550,650,1550,600,500,650,450,650,500,600,500,600,500,600,1600,600,1550,650,1550,650,450,650,500,600,500,600,500,600,500,600,1600,600,1600,600,1550,650,1550,600,500,650,450,650,500,600,500,600,500,600,500,600,500,600,500,650,1550,600,1600,600,1600,600,1550,6500};

void  sendRaw (const unsigned int buf[],  unsigned int len,  unsigned int hz)
{
    // Set IR carrier frequency
    enableIROut(hz);
    for (unsigned int i = 0;  i < len;  i++) {
        if (i & 1)  space(pgm_read_word_near(&(buf[i])));
        else        mark (pgm_read_word_near(&(buf[i])));
    }
    space(0);  // Always end with the LED off
}

void sendCommand() {
     sendRaw(TvMute, sizeof(TvMute)/sizeof(TvMute[0]), hz);
}

The sizeof operator returns the number of bytes of space required to store the array, not the number of elements.
When you pass an array into a function, the sizeof information for the array is lost and you just get the size of a pointer instead.

arduarn:
Maybe this untested:

const unsigned int TvMute[67]       PROGMEM = {4450,4350,600,1550,650,1550,650,1550,600,500,650,450,650,500,600,500,600,500,600,1600,600,1550,650,1550,650,450,650,500,600,500,600,500,600,500,600,1600,600,1600,600,1550,650,1550,600,500,650,450,650,500,600,500,600,500,600,500,600,500,600,500,650,1550,600,1600,600,1600,600,1550,6500};

void  sendRaw (const unsigned int buf[],  unsigned int len,  unsigned int hz)
{
   // Set IR carrier frequency
   enableIROut(hz);
   for (unsigned int i = 0;  i < len;  i++) {
       if (i & 1)  space(pgm_read_word_near(&(buf[i])));
       else        mark (pgm_read_word_near(&(buf[i])));
   }
   space(0);  // Always end with the LED off
}

void sendCommand() {
    sendRaw(TvMute, sizeof(TvMute)/sizeof(TvMute[0]), hz);
}

Well, that didn't work... I even tried adding back in the PROGMEM statement in the function declaration. I also tried it by taking the array out of flash and calling the other function but still using your sizeof(TvMute)/sizeof(TvMute[0]) in the function call, and it still worked... but cant pull this thing out of flash for some reason, and I don't get it.

Is there a way to just pull the array out of flash and put it into a new array? If so, I can just do that and pass that array to the function.

EasyGoing1:
Well, that didn't work...

Perhaps what you meant to say was "Thanks for trying, but I'm afraid that suggestion didn't work out. But, I really appreciate you making the effort"

Just saying.....

What do the mark() and space() functions look like? They may not be able to handle the progmem arguments.

Try this:

void  sendRaw (const unsigned int buf[],  unsigned int len,  unsigned int hz)
{
    // Set IR carrier frequency
    enableIROut(hz);
    for (unsigned int i = 0;  i < len;  i++) {
        unsigned int temp = pgm_read_word_near(&(buf[i]));
        if (i & 1)  space(temp);
        else        mark (temp);
    }
    space(0);  // Always end with the LED off
}

EasyGoing1:
Well, that didn't work... I even tried adding back in the PROGMEM statement in the function declaration. I also tried it by taking the array out of flash and calling the other function but still using your sizeof(TvMute)/sizeof(TvMute[0]) in the function call, and it still worked... but cant pull this thing out of flash for some reason, and I don't get it.

Did you see the ampersands he added to the pgm_read_word_near lines? Did you add those too? They're pretty important.
And he is right, your sizeof was incorrect, and his version fixed that bug, even if it's not the reason your PROGMEM isn't working.

Pieter

One other important thing, which arduino are you doing this on?

gfvalvo:
Perhaps what you meant to say was "Thanks for trying, but I'm afraid that suggestion didn't work out. But, I really appreciate you making the effort"

Just saying.....

Yeah ... unfortunately vocal inflection doesn't pass into text either by reference or by value - lol ...

david_2018:
One other important thing, which arduino are you doing this on?

A Nano

Instead of mark & space, just print the values of the array to serial to check if they are being read back correctly.

PieterP:
Did you see the ampersands he added to the pgm_read_word_near lines? Did you add those too? They're pretty important.
And he is right, your sizeof was incorrect, and his version fixed that bug, even if it's not the reason your PROGMEM isn't working.

Pieter

GOOD CALL! I totally missed that, and after putting it in, IT WORKS!
Thank you Arduan, you did solve my problem!

EasyGoing1:
A Nano

Ok, was concerned you might be using a mega - once you get outside the lower page of memory the progmem access is a bit different.

Just to be thorough for anyone who might be reading this in the future, the problem was solved by making two changes to the original code.

This code:

 pgm_read_word_near(buf[i])

Is using the p****gm_read_word_near macro but the macro needs the address of the array element, which can be accessed by preceding the array reference with an ampersand like this:

pgm_read_word_near(&(buf[i]))

The other issue involved passing the correct value of the number of elements in the array into the function, so in my function call, passing this as the size of the array works - although I don't know why - I only know that if I don't do it this way, it doesn't work:

sizeof(myArray) / sizeof(myArray[0])
1 Like

unsigned int is 2 bytes.
sizeof(myArray) returns number of bytes
sizeof(myArray)/sizeof(myArray[0]) will return the number of bytes/2

noweare:
unsigned int is 2 bytes.
sizeof(myArray) returns number of bytes
sizeof(myArray)/sizeof(myArray[0]) will return the number of bytes/2

Why does myArray[0] always contain the number 2, and why not just divide by 2?

EasyGoing1:
Why does myArray[0] always contain the number 2, and why not just divide by 2?

You declared const unsigned int TvMute[67]. If you take an element of that array, like TvMute[0], then that element is of type unsigned int. On a basic Arduino, sizeof(unsigned int) is 2. The compiler knows this at compile time and so it hard-codes the value of the sizeof on your behalf.

You could of course hard-code the constant yourself, but then if you ever change the type of TvMute you would need to remember to change the hard-coded constant everywhere it appears. Likewise, if you ever wanted to compile your sketch for a device where the size of an unsigned int is not 2, you would need to manually change all those constants too. For a small personal sketch maybe not a big deal, for larger project it's just a maintenance nightmare.

EasyGoing1:
Just to be thorough for anyone who might be reading this in the future, the problem was solved by making two changes to the original code.

For completeness it would be helpful, too to add a notation to the thread title in the opening post indicating "[SOLVED]" or some such.

noweare:
unsigned int is 2 bytes.
sizeof(myArray) returns number of bytes
sizeof(myArray)/sizeof(myArray[0]) will return the number of bytes/2

EasyGoing1:
Why does myArray[0] always contain the number 2, and why not just divide by 2?

myArray[0] does not always contain the number 2.
sizeof(myArray[0]) is always the number of bytes occupied by myArray[0], regardless of the value stored in myArray[0], and happens to be 2 on most arduino's, but on a few boards is 4 (the DUE and SAMD based boards).

sizeof(myArray)/ sizeof(myArray[0])

is an easy, generic way to tell how many elements are in an array, and is equivalent to

(total number of bytes needed to store an array) / (number of bytes needed to store each element in the array)

doesn't matter what type variable is stored in the array, as long as each element is the same size, so generally it is not a good idea to hard-code the divisor, just adds extra work in having to calculate the correct number (particularly when using a multi-dimensional array or a struct as the type), and makes code less portable.

dougp:
For completeness it would be helpful, too to add a notation to the thread title in the opening post indicating "[SOLVED]" or some such.

Thats a great idea! I was not aware that I could do that.