read a structs array from flash memory with PROGMEM

Hello. I try to read line by line after pressing a button a registered structure table in the flash memory.
Here is my sketch.

#include <avr/pgmspace.h>

#define TAILLE_MAX 47
#define LABEL_SIZE 20

typedef struct TabCom {
  char label[LABEL_SIZE];
  unsigned int value;
} TabCom;

const TabCom tabCom[TAILLE_MAX] PROGMEM = {
    {"KEY_POWER", 0x17BD}, {"KEY_TV", 0x179C}, {"KEY_VIDEO", 0x1798}, {"KEY_AUDIO", 0x1799},
    {"KEY_IMAGES", 0x179A}, {"KEY_INFO", 0x179B}, {"KEY_RADIO", 0x178C}, {"KEY_EXIT", 0x179F}, 
    {"KEY_MENU", 0x178D}, {"KEY_CHANNELDOWN", 0x1792}, {"KEY_MUTE", 0x178F}, {"KEY_UP", 0x1794}, 
    {"KEY_DOWN", 0x1795}, {"KEY_LEFT", 0x1796}, {"KEY_RIGHT", 0x1797}, {"KEY_OK", 0x17A5}, 
    {"KEY_VOLUMEUP", 0x1790}, {"KEY_VOLUMEDOWN", 0x1791}, {"KEY_CHANNELUP", 0x17A0}, 
    {"KEY_CHANNELDOWN", 0x17A1}, {"KEY_RECORD", 0x17B7}, {"KEY_STOP", 0x17B6}, {"KEY_REWIND", 0x17B2}, 
    {"KEY_FASTFORWARD", 0x17B4}, {"KEY_PLAY", 0x17B5}, {"KEY_AGAIN", 0x17A4}, {"KEY_NEXT", 0x179E}, 
    {"KEY_PAUSE ", 0x17B0}, {"KEY_1", 0x1781}, {"KEY_2", 0x1782}, {"KEY_3", 0x1783}, {"KEY_4", 0x1784}, 
    {"KEY_5", 0x1785}, {"KEY_6", 0x1786}, {"KEY_7", 0x1787}, {"KEY_8", 0x1788}, {"KEY_9", 0x1789}, 
    {"KEY_NUMERIC_STAR", 0x178A}, {"KEY_0", 0x1780}, {"KEY_NUMERIC_POUND", 0x178E}, {"KEY_RED", 0x178B}, 
    {"KEY_GREEN", 0x17AE}, {"KEY_YELLOW", 0x17B8}, {"KEY_BLUE", 0x17A9}, {"KEY_SUBTITLE", 0x178E}, 
    {"KEY_TEXT", 0x178A}, {"KEY_HOME", 0x17BB}
  };

char buffer[30];    // make sure this is large enough for the largest string it must hold

const byte buttonPin = 2;           // Bouton-poussoir en broche 2
byte buttonState;
byte buttonOldState;
byte bits;
byte i; // counter variable

void setup() {
  Serial.begin(9600);
  i = 0;
  //bits = 32;
  
}

void loop() {

  while (i<TAILLE_MAX){
      
    buttonState = digitalRead(buttonPin); // Interrogation du bouton-poussoir
   
    if ((buttonState != buttonOldState) && (buttonState == HIGH)) {
      
      for (byte i = 0; i < TAILLE_MAX; ++i) {
 
 strcpy_P(buffer, tabCom[i].label);
 
 Serial.println(buffer);
   
 i++;
  
 delay(200);
 
      }
      
      if (i == TAILLE_MAX) {
 
 i=0;
 
      }
      
    }
    
  }
 
  buttonOldState = buttonState;  
}

The result of this code is that when pressing the button, the array is read in its entirety from the SRAM as I understood it. This is not what I want to get.
After pressing the button, a single row of the table should be read.
When I wrote the sketch with just a few lines of the table, so in SRAM, it worked perfectly.

Thanks for your help.

Hello. I try to read line by line after pressing a button a registered structure table in the flash memory.

You are going to have to explain what "line by line" means for a structure that has no relationship to lines.

I compile and download your sketch to my Arduino MEGA, and the result was:

KEY_POWER
KEY_VIDEO
KEY_IMAGES
KEY_RADIO
KEY_MENU
KEY_MUTE
KEY_DOWN
KEY_RIGHT
KEY_VOLUMEUP
KEY_CHANNELUP
KEY_RECORD
KEY_REWIND
KEY_PLAY
KEY_NEXT
KEY_1
KEY_3
KEY_5
KEY_7
KEY_9
KEY_0
KEY_RED
KEY_YELLOW
KEY_SUBTITLE
KEY_HOME
KEY_POWER
KEY_VIDEO
KEY_IMAGES
KEY_RADIO
KEY_MENU
KEY_MUTE
KEY_DOWN
KEY_RIGHT
KEY_VOLUMEUP
KEY_CHANNELUP
KEY_RECORD
KEY_REWIND
KEY_PLAY
KEY_NEXT
KEY_1
KEY_3
KEY_5
KEY_7
KEY_9
KEY_0
KEY_RED
KEY_YELLOW
KEY_SUBTITLE
(...)

So, I think it's working, don't?

This link may help. http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_rom_array

I think you are in a similar situation where the pointers are in ROM, but the actual strings themselves are still in RAM. You should define the strings separately so they will be put in ROM as well.

moricef:
The result of this code is that when pressing the button, the picture is read in its entirety from the SRAM as I understood it. This is not what I want to get.

But something similar is what your program says:

  • if the button is pressed,
  • start a for-loop
  • loop through all entries of the array
  • print each entry label name
  • increment the loop counter once more within the loop (so only every second entry is printed)
  • block the program for 200 milliseconds before next looping
if ((buttonState != buttonOldState) && (buttonState == HIGH)) {

      for (byte i = 0; i < TAILLE_MAX; ++i) {

        strcpy_P(buffer, tabCom[i].label);

        Serial.println(buffer);

        i++;

        delay(200);

      }
  ...

moricef:
When I wrote the sketch with just a few lines of the table, so in SRAM, it worked perfectly.

Perhaps you should use the “Tools - Automatic Formatting” function to format your code for better readability. So perhaps can have a better look on the actual programming logic in your code.

Hi, I have rewritten your code. Could you compare it line by line ?

I have used the auto-format, and removed some empty lines. The variable 'i' was decared twice. I changed the way buttonState and buttonOldState are used. The name TabCom was used twice in a different way. I have remove the 'typestruct' keyword. It is possible to create a typestruct without that keyword. The TAILLE_MAX is no longer used, it is now automatic with the sizeof(). The button could bounce, that would result in a few text lines, instead of just one. You might have to add a delay to avoid that. Or use the Bounce2 library. Because of the fixed size of the string inside the struct, everything is in PROGMEM without the trouble of using pointers. The fixed size is of course not very memory efficient, but is will only use PROGMEM (code memory), no ram.

#define TAILLE_MAX 47             // not used
#define LABEL_SIZE 20

struct TabCom_STRUCT {
  char label[LABEL_SIZE];
  unsigned int value;
};

const TabCom_STRUCT tabCom[] PROGMEM = {
  {"KEY_POWER", 0x17BD}, {"KEY_TV", 0x179C}, {"KEY_VIDEO", 0x1798}, {"KEY_AUDIO", 0x1799},
  {"KEY_IMAGES", 0x179A}, {"KEY_INFO", 0x179B}, {"KEY_RADIO", 0x178C}, {"KEY_EXIT", 0x179F},
  {"KEY_MENU", 0x178D}, {"KEY_CHANNELDOWN", 0x1792}, {"KEY_MUTE", 0x178F}, {"KEY_UP", 0x1794},
  {"KEY_DOWN", 0x1795}, {"KEY_LEFT", 0x1796}, {"KEY_RIGHT", 0x1797}, {"KEY_OK", 0x17A5},
  {"KEY_VOLUMEUP", 0x1790}, {"KEY_VOLUMEDOWN", 0x1791}, {"KEY_CHANNELUP", 0x17A0},
  {"KEY_CHANNELDOWN", 0x17A1}, {"KEY_RECORD", 0x17B7}, {"KEY_STOP", 0x17B6}, {"KEY_REWIND", 0x17B2},
  {"KEY_FASTFORWARD", 0x17B4}, {"KEY_PLAY", 0x17B5}, {"KEY_AGAIN", 0x17A4}, {"KEY_NEXT", 0x179E},
  {"KEY_PAUSE ", 0x17B0}, {"KEY_1", 0x1781}, {"KEY_2", 0x1782}, {"KEY_3", 0x1783}, {"KEY_4", 0x1784},
  {"KEY_5", 0x1785}, {"KEY_6", 0x1786}, {"KEY_7", 0x1787}, {"KEY_8", 0x1788}, {"KEY_9", 0x1789},
  {"KEY_NUMERIC_STAR", 0x178A}, {"KEY_0", 0x1780}, {"KEY_NUMERIC_POUND", 0x178E}, {"KEY_RED", 0x178B},
  {"KEY_GREEN", 0x17AE}, {"KEY_YELLOW", 0x17B8}, {"KEY_BLUE", 0x17A9}, {"KEY_SUBTITLE", 0x178E},
  {"KEY_TEXT", 0x178A}, {"KEY_HOME", 0x17BB}
};

char buffer[30];    // make sure this is large enough for the largest string it must hold

const byte buttonPin = 2;           // Bouton-poussoir en broche 2
byte buttonState;
byte buttonOldState;
byte bits;
byte counter; // counter variable

void setup() {
  Serial.begin(9600);
  counter = 0;
  //bits = 32;
  
  pinMode( buttonPin, INPUT_PULLUP);
}

void loop() {
  buttonState = digitalRead(buttonPin); // Interrogation du bouton-poussoir
  
  if (buttonState != buttonOldState) {
    buttonOldState = buttonState;
    
    if( buttonState == HIGH)
    {
      strcpy_P(buffer, tabCom[counter].label);
      Serial.println(buffer);
      counter++;
      if (counter >= sizeof(tabCom) / sizeof( TabCom_STRUCT))
        counter = 0;
    }
  }
}

Thanks Peter_n , I’ve tried your sketch and it runs as well as i want it. But i don’t understand what it is doing with this part of code : if (counter >= sizeof(tabCom) / sizeof(TabCom_STRUCT)){counter=0;}
the counter equal zero if (the counter is upper or equal to the size of the array) divided by (the size of the structure)

While the strings are by far the most common reason for using the attribute PROGMEM
I do not know if it’s interresting to keep the unsigned int data as they are. Perhaps it is better to put them in the flash memory knowing as strings. These unsigned int data is to be sent as an infrared code to program a module.
Otherwise I had thought use pgm_read_word(&tabCom[1].value);, but with that there is no results in the serial console

PaulS: line after line by push on the button to read the line of the struct array which is in the program space memory

jurs: i’m not an expert in C code but i think i know what my code is doing. And i think the formatting of my code is in fact really readable even if it is not the Arduino IDE formatting but the kate editor C formatting. Now this is my ancient sketch with the results i want to achieve, before I put in all the data I want to use and before it is necessary to use the flash memory :

#include <IRremote.h>

#define TAILLE_MAX 4

typedef struct TabCom {
 char label[20];
 unsigned long value;
} TabCom;

IRsend irsend;

const byte buttonPin = 2; // Bouton-poussoir en broche 2
byte buttonState; // Variable pour enregistrer l'état du bouton-poussoir
byte buttonOldState; // Variable pour enregistrer l'état du bouton-poussoir
//byte bits = 32;
byte i;

TabCom tabCom[4] = {
    {"KEY_POWER", 0x17BD}, {"KEY_TV", 0x179C}, {"KEY_VIDEO", 0x1798}, {"KEY_AUDIO", 0x1799}
};

void setup() {
  Serial.begin(9600);
  pinMode(buttonPin, INPUT); // Broche bouton-poussoir comme entrée
  i = 0;
}

void loop() {
  
    while (i<TAILLE_MAX){
      
      buttonState = digitalRead(buttonPin); // Interrogation du bouton-poussoir
   
      if ((buttonState != buttonOldState) && (buttonState == HIGH)) {
 
 Serial.println(tabCom[i].label);
     
 for (int n = 0; n < 3; n++) {
 
  Serial.println(tabCom[i].value, HEX);
 }
  
 i++;
 
 delay(500);
      }
      
      if (i == TAILLE_MAX) {
 
 i = 0;
      }

      buttonOldState = buttonState;
  }
}

The sizeof() is solved by the compiler. It can be used for almost anything. The number of items in an array (or an array of struct) is the total size divided by the size of one element.

Thanks a lot Peter_n.

Now i have the results i want to achieve. Maybe it can be improved, but it’s fine now :

#include <avr/pgmspace.h>
#define TAILLE_MAX 47             // not used
#define LABEL_SIZE 20

struct TabCom_STRUCT {
  char label[LABEL_SIZE];
  unsigned int value;
};

const TabCom_STRUCT tabCom_tab[] PROGMEM = {
  {"KEY_POWER", 0x17BD}, {"KEY_TV", 0x179C}, {"KEY_VIDEO", 0x1798}, {"KEY_AUDIO", 0x1799},
  {"KEY_IMAGES", 0x179A}, {"KEY_INFO", 0x179B}, {"KEY_RADIO", 0x178C}, {"KEY_EXIT", 0x179F},
  {"KEY_MENU", 0x178D}, {"KEY_CHANNELDOWN", 0x1792}, {"KEY_MUTE", 0x178F}, {"KEY_UP", 0x1794},
  {"KEY_DOWN", 0x1795}, {"KEY_LEFT", 0x1796}, {"KEY_RIGHT", 0x1797}, {"KEY_OK", 0x17A5},
  {"KEY_VOLUMEUP", 0x1790}, {"KEY_VOLUMEDOWN", 0x1791}, {"KEY_CHANNELUP", 0x17A0},
  {"KEY_CHANNELDOWN", 0x17A1}, {"KEY_RECORD", 0x17B7}, {"KEY_STOP", 0x17B6}, {"KEY_REWIND", 0x17B2},
  {"KEY_FASTFORWARD", 0x17B4}, {"KEY_PLAY", 0x17B5}, {"KEY_AGAIN", 0x17A4}, {"KEY_NEXT", 0x179E},
  {"KEY_PAUSE ", 0x17B0}, {"KEY_1", 0x1781}, {"KEY_2", 0x1782}, {"KEY_3", 0x1783}, {"KEY_4", 0x1784},
  {"KEY_5", 0x1785}, {"KEY_6", 0x1786}, {"KEY_7", 0x1787}, {"KEY_8", 0x1788}, {"KEY_9", 0x1789},
  {"KEY_NUMERIC_STAR", 0x178A}, {"KEY_0", 0x1780}, {"KEY_NUMERIC_POUND", 0x178E}, {"KEY_RED", 0x178B},
  {"KEY_GREEN", 0x17AE}, {"KEY_YELLOW", 0x17B8}, {"KEY_BLUE", 0x17A9}, {"KEY_SUBTITLE", 0x178E},
  {"KEY_TEXT", 0x178A}, {"KEY_HOME", 0x17BB}
};

char buffer_label[30];    // make sure this is large enough for the largest string it must hold

const byte buttonPin = 2;           // Bouton-poussoir en broche 2
byte buttonState;
byte buttonOldState;
byte bits;
byte counter; // counter variable
unsigned long readValue;

void setup() {
  Serial.begin(9600);
  counter = 0;
  //bits = 32;
  pinMode( buttonPin, INPUT_PULLUP);
}

void loop() {
  buttonState = digitalRead(buttonPin); // Interrogation du bouton-poussoir
  
  if (buttonState != buttonOldState) {
    buttonOldState = buttonState;
    
    if( buttonState == HIGH)
    {
      strcpy_P(buffer_label, tabCom_tab[counter].label);
      Serial.println(buffer_label);
    
      for (int n = 0; n < 3; n++) {

      readValue= pgm_read_word(&tabCom_tab[counter].value);
      Serial.println(readValue, HEX);
	
      }
      counter++;
      delay(200);
      if (counter >= sizeof(tabCom_tab) / sizeof(TabCom_STRUCT)) {
        counter = 0;
	
      }
    }
  }
}

I do not know how to mark it resolved?

You can remove this : #include <avr/pgmspace.h>
It was perhaps needed in the past, but it is no longer needed.

Mark as solved ? Most of us don’t do that, but some do this: go to your first post of this topic. Click “Modify”, and add “[SOLVED]” in front of the subject.