0

I have a 2D array of uint16_t arrays (IR Remote Codes). Due to the size of this data in RAM I'm having all kinds of trouble when my array is larger than one code. Thus, I have been trying to move this whole structure into PROGMEM. Following various tutorials and this solution, I have come up with the code below for reading a single element out of the array and into a variable that I can feed to the IRLIB2 IRSendRaw function.

The variable that holds the code that is read out of PROGMEM is still huge and causing some problems. Is there a more direct way like a python list comprehension to access an arbitrary element and send it directly to the IRSender?

This PROGMEM_readAnything library looks promising, but I don't have enough C++ fu to make the mental leap to implement it in my code.

#include <IRLibSendBase.h>    //We need the base code
#include <IRLib_HashRaw.h>    //Only use raw sender

#define RAW_DATA_LEN 68  
const uint16_t sources[3][RAW_DATA_LEN] PROGMEM =
  {{8550, 4306, 530, 1606, 530, 566, 502, 1610, //power on/off
  530, 566, 502, 574, 506, 1630, 506, 566,
  506, 1630, 506, 566, 502, 1610, 530, 566,
  502, 1634, 506, 1606, 530, 570, 498, 1634,
  506, 570, 510, 562, 506, 566, 502, 1634,
  506, 1606, 530, 1610, 530, 562, 538, 538,
  530, 542, 538, 1598, 538, 1570, 558, 542,
  538, 538, 530, 542, 538, 1598, 530, 1578,
  558, 1578, 562, 1000},

  {8546, 4310, 558, 1578, 562, 538, 498, 1638,  //source CD
  530, 542, 506, 570, 502, 1634, 502, 570,
  498, 1638, 534, 538, 498, 1638, 530, 542,
  506, 1606, 554, 1582, 558, 538, 510, 1602,
  554, 546, 506, 566, 502, 570, 510, 1602,
  554, 1582, 558, 538, 510, 566, 502, 1606,
  554, 546, 502, 1610, 558, 1574, 554, 546,
  502, 570, 510, 1602, 526, 1610, 526, 574,
  506, 1602, 526, 1000},

  {8550, 4306, 530, 1606, 534, 566, 502, 1606,  //source CDR
  534, 566, 502, 570, 510, 1602, 526, 570,
  510, 1602, 534, 566, 502, 1606, 534, 566,
  506, 1602, 530, 1606, 534, 566, 502, 1610,
  530, 570, 498, 574, 506, 566, 502, 570,
  510, 1602, 526, 570, 510, 566, 502, 570,
  510, 1602, 526, 1610, 526, 1606, 534, 1602,
  534, 566, 502, 1610, 530, 1602, 534, 1602,
  526, 574, 506, 1000}};

//Create a sender
IRsendRaw mySender;


//Read out one code into a variable and send to the IR transmitter
uint16_t myCode[RAW_DATA_LEN];
for (int i = 0; i < RAW_DATA_LEN; i++) {
   myCode[i] = pgm_read_word_near(sources[0] + i); 
}
mySender.send(myCode,RAW_DATA_LEN,36);
  • you can't put a multidimensional array into PROGMEM. see "Array of Strings" in arduino.cc/reference/en/language/variables/utilities/progmem – Juraj Apr 4 at 19:40
  • 2
    @Juraj Sure you can. Strings are a special case though. There's no difference in memory between a 2D array, and a single large 1D array. – Majenko Apr 4 at 19:44
  • It is not problem to fill the rest of the flash memory of an arduino uno with progmem data. For an arduino mega 2560 it is better te keep the progmem data section below the 64k boundery. You copy the data into a buffer of 68 uint16_t, that is 136 bytes. Is that the problem? The arduino uno has 2kbyte ram. What else is using your sram? – Jot Apr 4 at 19:54
  • 1
    I had problems to read a two-dimensional array from PROGMEM. now I know. for one dimension the argument to pgm_read is arr + i. it is a pointer to the element. and for two dimensions the argument to pgm_read must be arr[i] + j – Juraj Apr 5 at 5:39
4

The library, as it stands, doesn't support direct sending from PROGMEM. However it's not too hard to get around that - if your C++ is up to it.

The IRsendRaw class is a child class that extends the IRsendBase with a single function. You just need to do the same thing to create a new class that implements the functions in a way that can read from PROGMEM.

This is the original:

class IRsendRaw: public virtual IRsendBase {
  public:
    void send(uint16_t *buf, uint8_t len, uint8_t khz) {
      enableIROut(khz);
      for (uint8_t i = 0; i < len; i++) {
        if (i & 1) {
          space(buf[i]);
        } 
        else {
          mark(buf[i]);
        }
      }
      space(0); // Just to be sure
    }
};

You just need to make your own one of those in your sketch, called something different, and which uses the progmem functions instead of accessing buf[i]:

class IRsendRawPGM: public virtual IRsendBase {
  public:
    void send(const uint16_t *buf, uint8_t len, uint8_t khz) {
      enableIROut(khz);
      for (uint8_t 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); // Just to be sure
    }
};

Now you can define your sender object as:

IRsendRawPGM mySender;

And send the data directly:

mySender.send(sources[0], RAW_DATA_LEN, 36);

(Note: this code is untested).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.