Structure Array EEPROM

Hi,

I am having some trouble with a structure array and EEPROM and am hoping someone can clarify what i’m doing wrong.

If I have the following structure:

int addr = 100;

typedef struct{
	char id[20] = "";
	int count = 0;
} idTagged;

which is a component in a larger structure, which I wish to keep in EEPROM.

int id_possibilities=50;

typedef struct{
	uint32_t magic;
	char email[40];
	idTagged idDict[id_possibilities];
	int id_tagged_bank_index;
} eepromConfig;

eepromConfig config_data;

Then I want to set values in the idTagged idDict structure

strcpy(config_data.idDict[config_data.id_tagged_bank_index].id, "8477_77777777777");
config_data.idDict[config_data.id_tagged_bank_index].count = 1;
EEPROM.put(addr, config_data);

It seems like I am wrecking my config_data variable by writing into a location on my structure array. Is this an address issue as my array of structures is growing in size?

Apologies if I missed the solution in another thread.

How did you get that to compile? id_possibilities must be a const expression because the value needs to be set at compile time and can’t be changed at runtime. I’d declare it like:

const size_t id_possibilities=50;

This is my modified test program:

#include "Particle.h"

SerialLogHandler logHandler;

SYSTEM_THREAD(ENABLED);

typedef struct{
	char id[20] = "";
	int count = 0;
} idTagged;

const size_t id_possibilities=50;

typedef struct{
	uint32_t magic;
	char email[40];
	idTagged idDict[id_possibilities];
	int id_tagged_bank_index;
} eepromConfig;

eepromConfig config_data;

void printConfig() {
	Log.info("magic=%08lx email=%s id_tagged_bank_index=%d", config_data.magic, config_data.email, config_data.id_tagged_bank_index);
	for(size_t ii = 0; ii < id_possibilities; ii++) {
		Log.info("ii=%u id=%s count=%d", ii, config_data.idDict[ii].id, config_data.idDict[ii].count);
	}
}

void setup() {
	waitFor(Serial.isConnected, 15000);
	delay(1000);
	
	printConfig();
	strcpy(config_data.idDict[config_data.id_tagged_bank_index].id, "8477_77777777777");
		config_data.idDict[config_data.id_tagged_bank_index].count = 1;

	// EEPROM.put(addr, config_data);

	printConfig();
}

void loop() {

}

After setting the value, it looks correct:

0000002383 [app] INFO: magic=00000000 email= id_tagged_bank_index=0
0000002385 [app] INFO: ii=0 id=8477_77777777777 count=1
0000002387 [app] INFO: ii=1 id= count=0
0000002389 [app] INFO: ii=2 id= count=0
0000002390 [app] INFO: ii=3 id= count=0
0000002391 [app] INFO: ii=4 id= count=0
0000002394 [app] INFO: ii=5 id= count=0

Be careful with id_tagged_bank_index. You probably should do some bounds checking to make sure it’s >=0 and < id_possibilities to avoid corrupting memory.

While it’s initialized to 0 as a global, if you read the struct from uninitialized EEPROM, that will likely be 0xffffffff, which as an int is -1, which will cause all sorts of memory corruption as an index into idDict.

Also you probably don’t want to use strcpy with things other than string constants because it does not protect against overflowing id which is 19 ASCII character + 1 trailing null.

1 Like

Thanks Rick, sorry you are correct, I actually have

const int id_possibilities=50;

I think you might have cracked it with id_tagged_bank_index I bet this wasn’t initialised correctly which resulted in some odd behaviour in EEPROM. I will add a bounds check to ensure I am getting expected behaviour.

The depths on my ignorance on chars and Strings deepens. Essentially I am trying to set one char array equal to another char array. What would your advice be in this scenario.

I think the advice coming will be to use strncpy:

Declaration

Following is the declaration for strncpy() function.

char *strncpy(char *dest, const char *src, size_t n)

Parameters

  • dest − This is the pointer to the destination array where the content is to be copied.
  • src − This is the string to be copied.
  • n − The number of characters to be copied from source.

Return Value

This function returns the pointer to the copied string.

source

The extra parameter n can help you copy at most n chars, protecting your code from overflows.

2 Likes

One word of caution tho’
strncpy() will not zero-terminate the string when the source string is longer than the target buffer.
To tackle that you can “force-terminate” the string like this

  strncpy(dest, source, sizeof(dest)-1);
  dest[sizeof(dest)-1] = '\0'; // as pointed out by @Muskie below

One side note too :wink:
How likely is it that you really need 50 idDict entries and how often may at least one of these change?
In case of a notable mutability of the data it may be worth to consider separating the configuration “header” and the tag “data” to only use that amount of flash you actually need.
For that you could store the actual number of stored tags in the config but store their individual data in a dedicated flash are.

e.g.

typedef struct{
	uint32_t magic;
	char     email[40];
	uint8_t  id_tagged_bank_index;
	uint8_t  savedTags;
} eepromConfig;
typedef struct{
	char     id[20] = "";
	uint8_t  count = 0;
} idTagged;

const int baseAddr = 100;
const int tagAddr  = baseAddr + sizeof(eepromConfig);

eepromConfig config;
...
  for(int i=0; i < asOftenAsNeeded; i++) {
    idTagged newTag;
    // ... populate newTag   
    EEPROM.put(tagAddr + config.savedTags++ * sizeof(newTag), newTag); // put new tag at next free place (and push index forward)
  }
  EEPROM.put(baseAddr, config);                                        // write config block

(you may want to add some check to keep the number of possible tags small enough to not outgrow the available flash memory, but you don’t need to hardcode that limit)

I also see you are using a magic number scheme to check data integrity. Whenever you read back the data from EEPROM and see a mismatch between your expected magic number and the read one, you should also reset explicitly reset id_tagged_bank_index and savedTags to 0

this would address this comment by Rick

2 Likes
strncpy(dest, source, sizeof(dest)-1);
dest[sizeof(dest) - 1] = '\0';

Very common mistake. Maximum array index is one less than the size of the array.

1 Like

Sorry, my bad.
I had the -1 in the size parameter but forgot to bring that idea forward :blush:

1 Like