Cannot Save and Delete Data from Preferences in the Same Function

I am having an issue storing/removing data in ESP32S3 Preferences. I took the bits of code to save/delete/read data to/from the Preferences db from my project and created a test sketch that I have attached and uploaded.
sketch_aug28b.ino (10.3 KB)

I believe the issue is one of timing in the Preferences library. I am saving launcher_id/MAC address pairs in Preferences. The rule is there can only be one launcher_id per MAC address and one MAC address per launcher_id.

The error occurs in the saveToPreferences() function. In that function, I am saving new data to Preferences, and also insuring the rule of launcher_id/MAC address is preserved as well as ignoring duplicates. The code behaves correctly if I (1) save the data first, (2) create a delay by printing out the Preferences, and (3) remove any entries that violate the launcher_id/MAC address rule. The keys to making the code work are
(1) the delay introduced between saving the new data and deleting the old data, and
(2) saving the data first before deleting the data.
Remove that delay or deleting the data before saving it, and the code breaks.

At the top of the code, you will see a const bool BREAK_THE_CODE variable. Setting it to true will break the code a shown in the output, and setting it to false will make the code behave. The variable adds in the printPreferences() delay between saving the data and deleting the data in the saveToPreferences() function. I have included a set of test cases in the setup() function, and you can see the contents of the Preferences db as the code progresses in the serial output.

The correct final output, with BREAK_THE_CODE=false:

print keys: 
	number of keys = 4
	key=1
	key=3
	key=8
	key=9
print preferences: 
	{1: 01:02:03:04:05:06}, {010203040506: 1}
	{3: 03:05:04:03:02:01}, {030504030201: 3}
	{8: 08:05:04:03:02:01}, {080504030201: 8}
	{9: 02:05:04:03:02:01}, {020504030201: 9}

The broken final output if BREAK_THE_CODE=true. Note that the intervening steps of saving data and ignoring duplicates works in both cases. The failure occurs when I try to save and delete data.

print keys: 
	number of keys = 4
	key=1
	key=3
	key=8
	key=9
print preferences: 
	{1: 01:02:03:04:05:06}, {010203040506: 1}
	{3: 03:05:04:03:02:01}, {030504030201: 3}
	{8: 08:05:04:03:02:01}, {080504030201: 8}
	{9: }, {080504030201: 8}

The error occurs in the last test case, where I try to add a MAC address with a new launcher_id. The result should be to delete the old launcher_id/MAC address and add thew new one, as shown in the correct output above. The incorrect output shows that launcher_id does not have an associated MAC address, and the second entry is totally wrong.

I am curious if anyone has run into a situation like this where (1) the order of saving and deleting data from Preferences matters and (2) a delay is needed between saving and deleting data from Preferences.

Is there a better way to achieve what I want to achieve without the artificial delay of printing the Preferences data in the middle of the saveToPreferences() function?

If the delay is necessary, is there a better way to introduce a delay in the code?

Thanks!

#include <Preferences.h>
#include <string.h>
#include <cstring> // For strcpy
#include <stdbool.h>

/* Preference Data 
* launcher_id_str: macAddrStr -  note launcher_id is an int, so have to convert to a String to use as a key
* "keys": [launcher-1_id, launcher-2_id, launcher-3_id,] - an int array of the launcher_ids using the key "keys"
*/

Preferences preferences;
const char PREFERENCES_NAME_SPACE[] = "multiLauncher";
const bool CLEAR_PREFERENCES = true;
const bool TEST_PREFERENCES = true;

const bool BREAK_THE_CODE = true;

/*
  Testing different scenarios.
  * Add a launcher_id MAC address pair
  * Add a duplicate launcher_id and MAC address
  * Add a duplicate launcher_id with a different MAC address
  In each case print out the contents of the preferences and the keys
  Start the process each time with the preferences db empty
*/
void setup() {
  Serial.begin(115200); 
  delay(1000);     

  Serial.printf("Storage Setup*****");
  if (CLEAR_PREFERENCES) {
    preferences.begin(PREFERENCES_NAME_SPACE, false);
    preferences.clear();
    preferences.end();
    
  }
  if (TEST_PREFERENCES) {
    Serial.println("Testing Preferences - START");
    printKeys();
    printPreferences();

    // Add the first set of data
    char mac_str[] = "01:02:03:04:05:06";
    saveToPreferences(1, mac_str);
    printKeys();
    printPreferences();
    // Add a second set
    saveToPreferences(3, "03:05:04:03:02:01");
    printKeys();
    printPreferences();
    // Add a third set
    saveToPreferences(2, "02:05:04:03:02:01");
    printKeys();
    printPreferences();
    // Add a fourth set
    saveToPreferences(8, "08:05:04:03:02:01");
    printKeys();
    printPreferences();
    // Add a duplicate launcher_id MAC address pair
    saveToPreferences(3, "03:05:04:03:02:01");
    printKeys();
    printPreferences();
    // Update the launcher_id=2 a new MAC address
    // This should replace the launcher_id=2 with launcher_id=9
    // for MAC address 02:05:04:03:02:01
    saveToPreferences(9, "02:05:04:03:02:01");
    printKeys();
    printPreferences();

    Serial.println("Testing Preferences - DONE");
  }
  Serial.println("Storage setup done");
}

/*
  Save a launcher_id/MAC address pair to the preferences DB. 
  * Ignore duplicates
  * If the MAC address for a particular launcher_id changes, 
      then update the MAC address and remove the old entries
*/
void saveToPreferences(int launcher_number, char* macStr) {
  Serial.printf("Saving to preferences: launcher_number=%d, macStr=%s\n", launcher_number, macStr);
  preferences.begin(PREFERENCES_NAME_SPACE, false);
  char macStr_short[13];
  macToShort(macStr_short, macStr);
  int key = preferences.getInt(macStr_short);

  // deleting the data before saving always breaks the code
  // so I moved the deleting part to after the saving part
  // if (key != launcher_number && key != 0) {
  //   Serial.printf("\tremoving mac=%s, mac_short=%s, launcher_id=%d\n", macStr, macStr_short, key);
  //   char key_str[6];
  //   keyToString(launcher_number, key_str);
  //   preferences.remove(key_str);
  //   removeKey(key);
  // }

  char new_key_str[6];
  keyToString(launcher_number, new_key_str);
  preferences.putString(new_key_str, macStr);
  Serial.printf("\tSaving %s: %s\n", new_key_str, macStr);
  preferences.putInt(macStr_short, launcher_number);
  Serial.printf("\tSaving %s: %d\n", macStr_short, launcher_number);

  if (!BREAK_THE_CODE) {
    printPreferences();     // remove this line, and the code breaks as shown in the output!
  }

  if (key != launcher_number && key != 0) {
    Serial.printf("\tremoving mac=%s, mac_short=%s, launcher_id=%d\n", macStr, macStr_short, key);
    char key_str[6];
    keyToString(launcher_number, key_str);
    preferences.remove(key_str);
    removeKey(key);
  }

  preferences.end();
  saveKey(launcher_number);
}

/*
  Save a key to Preferneces. Check for duplicates and ignore them.
  Create the "keys" array if needed.
*/
void saveKey(int launcher_number) {
  Serial.printf("Save Key: launcher_number=%d\n", launcher_number);
  preferences.begin(PREFERENCES_NAME_SPACE, false);
  size_t storedSize = preferences.getBytesLength("keys");
  int arraySize = storedSize / sizeof(int);
  //Serial.printf("\t#1 storedSize=%d\n", storedSize);
  if (storedSize > 0) {
    int retrievedArray[arraySize + 1];
    preferences.getBytes("keys", retrievedArray, storedSize + sizeof(int));
    bool duplicate = false;
    for (int i = 0; i < arraySize; i++) {
      if (launcher_number == retrievedArray[i]) {
        duplicate = true;
        //Serial.printf("Found a duplicate: launcher_number=%d\n", launcher_number);
        // ignore the duplicate, just don't add another.
      }
    }
    if (!duplicate) {
      retrievedArray[arraySize] = launcher_number;
      preferences.putBytes("keys", retrievedArray, sizeof(retrievedArray));
    }
  }
  else {
    // storing first key, so create a new array and save it
    int keys[] = {launcher_number};
    preferences.putBytes("keys", keys, sizeof(keys));
  }
  // storedSize = preferences.getBytesLength("keys");
  // Serial.printf("\t#2 stored_size=%d\n", storedSize);
  preferences.end();
}

/* Copy the keys into a new array, one element shorter than the 
   original array and leave out the one we want to remove. 
*/
void removeKey(int key) {
  Serial.printf("Remove Key: key=%d\n", key);
  preferences.begin(PREFERENCES_NAME_SPACE, false);
  size_t storedSize = preferences.getBytesLength("keys");
  int arraySize = storedSize / sizeof(int);
  //Serial.printf("#1 stored_size=%zu, arraySize=%d\n", storedSize, arraySize);
  if (arraySize > 1) {
    //Serial.println("here");
    int retrievedArray[arraySize];
    int newArray[arraySize - 1];
    preferences.getBytes("keys", retrievedArray, storedSize);
    for (int i = 0; i < arraySize-1; i++) {
      //Serial.printf("key=%d, i=%d, array_value=%d\n", key, i, retrievedArray[i]);
      if (key != retrievedArray[i]) {
        newArray[i] = retrievedArray[i];
      }
      else {
        newArray[i] = retrievedArray[i+1];
      }
      //Serial.printf("newArray[i]=%d\n", newArray[i]);
    }
    preferences.putBytes("keys", newArray, sizeof(newArray));
  }
  else {
    preferences.remove("keys");
  }
  storedSize = preferences.getBytesLength("keys");
  //Serial.printf("#2 new stored_size=%zu\n", storedSize);
  preferences.end();
}

/*
  Print out each key on one line from preferences.
*/
void printKeys() {
  Serial.println("print keys: ");
  preferences.begin(PREFERENCES_NAME_SPACE, false);
  size_t storedSize = preferences.getBytesLength("keys");
  //Serial.printf("#1 stored_size=%d\n", storedSize);
  if (storedSize > 0) {
    int arraySize = storedSize / sizeof(int);
    Serial.printf("\tnumber of keys = %d\n", arraySize);
    int retrievedArray[arraySize];
    preferences.getBytes("keys", retrievedArray, storedSize);
    for (int i = 0; i < arraySize; i++) {
      Serial.printf("\tkey=%d\n", retrievedArray[i]);
    }
  }
  else {
    Serial.printf("\tNo keys in the store\n");
  }
  preferences.end();
}

/*
  Print out the preferences. Preferences consist of three types:
  launcher_id_str : macString
  macString: launcher)id_int
  "keys": [launcher_id_int_1, launcher_id_int_2, ...]
  Use print keys to print the keys preferences
*/
void printPreferences() {
  Serial.println("print preferences: ");
  preferences.begin(PREFERENCES_NAME_SPACE, false);
  size_t storedSize = preferences.getBytesLength("keys");
  if (storedSize > 0) {
    int arraySize = storedSize / sizeof(int);
    //Serial.printf("\tnumber of keys = %d\n", arraySize);
    int retrievedArray[arraySize];
    preferences.getBytes("keys", retrievedArray, storedSize);
    for (int i=0; i < arraySize; i++) {
      int key = retrievedArray[i];
      //Serial.printf("\tkey=%d\n", key);
      char key_str[6];
      keyToString(key, key_str);
      String cma = preferences.getString(key_str);
      //Serial.printf("\tcma=%s\n", cma.c_str());
      uint8_t mac_array[6];
      macToArray((char*)cma.c_str(), mac_array);
      char macStr_short[13];
      macToString(mac_array, macStr_short, 13, true);
      //Serial.printf("\tmacStr_short=%s\n", macStr_short);
      int same_key = preferences.getInt(macStr_short);
      Serial.printf("\t{%s: %s}, {%s: %d}\n", key_str, cma.c_str(), macStr_short, same_key);
    }
  }
  else {
    Serial.printf("\tNo data stored in Preferences\n");
  }
  preferences.end();
}

/*
  Use to convert a launcher_id_int to a launcher_id_string, so it can 
  be used as a key in preferences.
*/
void keyToString(int launcher_number, char* launcher_number_str) {
  char buffer[6];
  sprintf(launcher_number_str, "%d", launcher_number); 
  //Serial.printf("launcher_number=%d, launcher_number_str=%s\n", launcher_number, launcher_number_str);
}

/*
  MAC addresses for ESP_NOW need to be an array, whereas it is easier to store the MAC address as
  a string in preferences. However, keys used in preferences cannot exceed 15 characters, so a formatted
  MAC address as a string (18 characters) is too long for a preference key. Therefore, we nned a third MAC address
  form as a stirng without the ':' to keep it at 12 characters.
*/
// Formats MAC Address from uint8_t array to a string for printing and saving to Preferences, short format removes ':'
void macToString(const uint8_t *macArray, char *macString, int maxLength, bool short_format) {
  if (short_format) {
    snprintf(macString, maxLength, "%02x%02x%02x%02x%02x%02x", macArray[0], macArray[1], macArray[2], macArray[3], macArray[4], macArray[5]);
  }
  else {
    snprintf(macString, maxLength, "%02x:%02x:%02x:%02x:%02x:%02x", macArray[0], macArray[1], macArray[2], macArray[3], macArray[4], macArray[5]);
  }
}

/*
  The long string version (with ':') of the MAC address is passed as function arguments, so when saving
  the MAC address to preferences it has to be trimmed down to the short version.
*/
void macToShort(char* macShort, char* macLong) {
  int i, j;
  Serial.printf("size macShort=%d, size macLong=%d\n", strlen(macShort), strlen(macLong));
  for (i = 0, j = 0; i < strlen(macLong); i++) {
    if (macLong[i] != ':') {
      macShort[j++] = macLong[i];
    }
  }
  macShort[j] = '\0';
}

/* 
  Converts a string version of the mac address back into a uint8_t array used by esp-now
*/
void macToArray(char *macString, uint8_t *macArray) {
    sscanf(macString, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &macArray[0], &macArray[1], &macArray[2], &macArray[3], &macArray[4], &macArray[5]);
}

/*
  Not needed for this excercise.
*/
void loop() {

}

I am having difficulty following your description, but I think your problem is in how to do the 'dupe elim'. When you receive a new pair to add, first check if it is valid (not a dupe MAC or launcher_id). If it is a dupe, just ignore that entry and go to the next. You should not need to delete any data, just ignore the new data that is a dupe.

My apologies for the confusion. Duplicates are just ignored. They are not the problem.

The problem occurs when a new launcher_id is assigned to an existing MAC address in the database. In this case, the entries in the data base need to be updated so the new launcher_id is associated with the existing MAC address, and the old launcher_id is removed.

Removing data and adding data to Preferences in the same function seems to be a problem, which is why I think it is a timing issue within the library, or just an artifact of how long it takes the hardware to add data.

In the test cases, this data is added:
saveToPreferences(2, "02:05:04:03:02:01");

At the end of the test cases, this data is added:
saveToPreferences(9, "02:05:04:03:02:01");

Note the MAC addresses are the same, but the first digit, the launcher_id is different. So, I need to update the Preferences so launcher_id=9 has MAC address 02:05:04:03:02:01 associated with it, and launcher_id=2 no longer exists in the database.

Right before the addition of 'launcher_id=9`, Preferences should look like this, based on the test cases:

    {1: 01:02:03:04:05:06}, {010203040506: 1}
	{3: 03:05:04:03:02:01}, {030504030201: 3}
	{2: 02:05:04:03:02:01}, {020504030201: 9}
	{8: 08:05:04:03:02:01}, {080504030201: 8}

After the addition of launcher_id=9, Preferences should look like this:

    {1: 01:02:03:04:05:06}, {010203040506: 1}
	{3: 03:05:04:03:02:01}, {030504030201: 3}
	{8: 08:05:04:03:02:01}, {080504030201: 8}
	{9: 02:05:04:03:02:01}, {020504030201: 9}

I hope that helps!

Thank you, it does help. I am surprised that timing is an issue; you are reading/writing Flash memory, so in human terms, it is instantaneous.
You showed me correct operation, what actually happens (NOT your interpretation, just evidence like serial log)

I have not used Preferences, so I had to look at the .h file. It appears to be 'keyed' data. I assume the MAC is the key. Since the API does not contain an update, it would appear you need to do a remove by key, followed by a putString, perhaps?

Sorry, I will have to drop out, I would never use this functionality since I wrote my own many years ago so I am sort of blind to the preferences API.
Good luck.

if I’m following the code properly..

key is old launcher number..

launcher_number is the new one sent in to the function..

I’m thinking keyToString should have been called with key not launcher_number??

looks like you try to remove the new before actually adding the new..

good luck.. ~q

If you set BREAK_THE_CODE = true (e.g. removing the delay caused by the printPreferences() call, this is the contents of Preferences after the addition of launcher_id=9:

    {1: 01:02:03:04:05:06}, {010203040506: 1}
	{3: 03:05:04:03:02:01}, {030504030201: 3}
	{8: 08:05:04:03:02:01}, {080504030201: 8}
	{9: }, {080504030201: 8}

Note that the MAC address string is not saved for the first entry, and the second entry has totally incorrect data.

Yup...that fixed it. Darn, I should have caught that one. :face_with_spiral_eyes:

However, your absolutely correct fix ONLY works IF the delete happens AFTER the save.

If you comment out the delete section after the save, and remove the comments on the delete section before the save (and replace launcher_id with key as you so astutely pointed out), the code still breaks in that the Preferences db is messed up (in the same way) as before.

post revised code and I’ll look again..

~q

Here it is.

If BREAK_THE_CODE = true, the deletes are done BEFORE the saves, and the code fails in that the db is corrupted as before. If BREAK_THE_CODE = false, the deletes are done AFTER the saves, and the code functions as expected.

BREAK_THE_CODE = false leaves the Preferences db in this state after all the tests:

	{1: 01:02:03:04:05:06}, {010203040506: 1}
	{3: 03:05:04:03:02:01}, {030504030201: 3}
	{8: 08:05:04:03:02:01}, {080504030201: 8}
	{9: 02:05:04:03:02:01}, {020504030201: 9}

BREAK_THE_CODE = true leaves the Preferences db in this state after all the tests:

    {1: 01:02:03:04:05:06}, {010203040506: 1}
	{3: 03:05:04:03:02:01}, {030504030201: 3}
	{8: 08:05:04:03:02:01}, {080504030201: 8}
	{9: }, {080504030201: 8}
#include <Preferences.h>
#include <string.h>
#include <cstring> // For strcpy
#include <stdbool.h>

/* Preference Data 
* launcher_id_str: macAddrStr -  note launcher_id is an int, so have to convert to a String to use as a key
* "keys": [launcher-1_id, launcher-2_id, launcher-3_id,] - an int array of the launcher_ids using the key "keys"
*/

Preferences preferences;
const char PREFERENCES_NAME_SPACE[] = "multiLauncher";
const bool CLEAR_PREFERENCES = true;
const bool TEST_PREFERENCES = true;

const bool BREAK_THE_CODE = false;

/*
  Testing different scenarios.
  * Add a launcher_id MAC address pair
  * Add a duplicate launcher_id and MAC address
  * Add a duplicate launcher_id with a different MAC address
  In each case print out the contents of the preferences and the keys
  Start the process each time with the preferences db empty
*/
void setup() {
  Serial.begin(115200); 
  delay(1000);     

  Serial.printf("Storage Setup*****");
  if (CLEAR_PREFERENCES) {
    preferences.begin(PREFERENCES_NAME_SPACE, false);
    preferences.clear();
    preferences.end();
    
  }
  if (TEST_PREFERENCES) {
    Serial.println("Testing Preferences - START");
    printKeys();
    printPreferences();

    // Add the first set of data
    char mac_str[] = "01:02:03:04:05:06";
    saveToPreferences(1, mac_str);
    printKeys();
    printPreferences();
    // Add a second set
    saveToPreferences(3, "03:05:04:03:02:01");
    printKeys();
    printPreferences();
    // Add a third set
    saveToPreferences(2, "02:05:04:03:02:01");
    printKeys();
    printPreferences();
    // Add a fourth set
    saveToPreferences(8, "08:05:04:03:02:01");
    printKeys();
    printPreferences();
    // Add a duplicate launcher_id MAC address pair
    saveToPreferences(3, "03:05:04:03:02:01");
    printKeys();
    printPreferences();
    // Update the launcher_id=2 a new MAC address
    // This should replace the launcher_id=2 with launcher_id=9
    // for MAC address 02:05:04:03:02:01
    saveToPreferences(9, "02:05:04:03:02:01");
    printKeys();
    printPreferences();

    Serial.println("Testing Preferences - DONE");
  }
  Serial.println("Storage setup done");
}

/*
  Save a launcher_id/MAC address pair to the preferences DB. 
  * Ignore duplicates
  * If the MAC address for a particular launcher_id changes, 
      then update the MAC address and remove the old entries
*/
void saveToPreferences(int launcher_number, char* macStr) {
  Serial.printf("Saving to preferences: launcher_number=%d, macStr=%s\n", launcher_number, macStr);
  preferences.begin(PREFERENCES_NAME_SPACE, false);
  char macStr_short[13];
  macToShort(macStr_short, macStr);
  int key = preferences.getInt(macStr_short);

/*******************************************************************************************/
  if (BREAK_THE_CODE) {
    // deleting the data before saving always breaks the code
    // so I moved the deleting part to after the saving part
    if (key != launcher_number && key != 0) {
      Serial.printf("\tremoving mac=%s, mac_short=%s, launcher_id=%d\n", macStr, macStr_short, key);
      char key_str[6];
      keyToString(key, key_str);
      preferences.remove(key_str);
      removeKey(key);
    }
  }
/*************************************************************************************************/
  char new_key_str[6];
  keyToString(launcher_number, new_key_str);
  preferences.putString(new_key_str, macStr);
  Serial.printf("\tSaving %s: %s\n", new_key_str, macStr);
  preferences.putInt(macStr_short, launcher_number);
  Serial.printf("\tSaving %s: %d\n", macStr_short, launcher_number);
  
  /********************************************************************************/
  if (!BREAK_THE_CODE) {
    if (key != launcher_number && key != 0) {
      Serial.printf("\tremoving mac=%s, mac_short=%s, launcher_id=%d\n", macStr, macStr_short, key);
      char key_str[6];
      keyToString(key, key_str);
      preferences.remove(key_str);
      removeKey(key);
    }
  }
  /************************************************************************************/

  preferences.end();
  saveKey(launcher_number);
}

/*
  Save a key to Preferneces. Check for duplicates and ignore them.
  Create the "keys" array if needed.
*/
void saveKey(int launcher_number) {
  Serial.printf("Save Key: launcher_number=%d\n", launcher_number);
  preferences.begin(PREFERENCES_NAME_SPACE, false);
  size_t storedSize = preferences.getBytesLength("keys");
  int arraySize = storedSize / sizeof(int);
  //Serial.printf("\t#1 storedSize=%d\n", storedSize);
  if (storedSize > 0) {
    int retrievedArray[arraySize + 1];
    preferences.getBytes("keys", retrievedArray, storedSize + sizeof(int));
    bool duplicate = false;
    for (int i = 0; i < arraySize; i++) {
      if (launcher_number == retrievedArray[i]) {
        duplicate = true;
        //Serial.printf("Found a duplicate: launcher_number=%d\n", launcher_number);
        // ignore the duplicate, just don't add another.
      }
    }
    if (!duplicate) {
      retrievedArray[arraySize] = launcher_number;
      preferences.putBytes("keys", retrievedArray, sizeof(retrievedArray));
    }
  }
  else {
    // storing first key, so create a new array and save it
    int keys[] = {launcher_number};
    preferences.putBytes("keys", keys, sizeof(keys));
  }
  // storedSize = preferences.getBytesLength("keys");
  // Serial.printf("\t#2 stored_size=%d\n", storedSize);
  preferences.end();
}

/* Copy the keys into a new array, one element shorter than the 
   original array and leave out the one we want to remove. 
*/
void removeKey(int key) {
  Serial.printf("Remove Key: key=%d\n", key);
  preferences.begin(PREFERENCES_NAME_SPACE, false);
  size_t storedSize = preferences.getBytesLength("keys");
  int arraySize = storedSize / sizeof(int);
  //Serial.printf("#1 stored_size=%zu, arraySize=%d\n", storedSize, arraySize);
  if (arraySize > 1) {
    //Serial.println("here");
    int retrievedArray[arraySize];
    int newArray[arraySize - 1];
    preferences.getBytes("keys", retrievedArray, storedSize);
    for (int i = 0; i < arraySize-1; i++) {
      //Serial.printf("key=%d, i=%d, array_value=%d\n", key, i, retrievedArray[i]);
      if (key != retrievedArray[i]) {
        newArray[i] = retrievedArray[i];
      }
      else {
        newArray[i] = retrievedArray[i+1];
      }
      //Serial.printf("newArray[i]=%d\n", newArray[i]);
    }
    preferences.putBytes("keys", newArray, sizeof(newArray));
  }
  else {
    preferences.remove("keys");
  }
  storedSize = preferences.getBytesLength("keys");
  //Serial.printf("#2 new stored_size=%zu\n", storedSize);
  preferences.end();
}

/*
  Print out each key on one line from preferences.
*/
void printKeys() {
  Serial.println("print keys: ");
  preferences.begin(PREFERENCES_NAME_SPACE, false);
  size_t storedSize = preferences.getBytesLength("keys");
  //Serial.printf("#1 stored_size=%d\n", storedSize);
  if (storedSize > 0) {
    int arraySize = storedSize / sizeof(int);
    Serial.printf("\tnumber of keys = %d\n", arraySize);
    int retrievedArray[arraySize];
    preferences.getBytes("keys", retrievedArray, storedSize);
    for (int i = 0; i < arraySize; i++) {
      Serial.printf("\tkey=%d\n", retrievedArray[i]);
    }
  }
  else {
    Serial.printf("\tNo keys in the store\n");
  }
  preferences.end();
}

/*
  Print out the preferences. Preferences consist of three types:
  launcher_id_str : macString
  macString: launcher)id_int
  "keys": [launcher_id_int_1, launcher_id_int_2, ...]
  Use print keys to print the keys preferences
*/
void printPreferences() {
  Serial.println("print preferences: ");
  preferences.begin(PREFERENCES_NAME_SPACE, false);
  size_t storedSize = preferences.getBytesLength("keys");
  if (storedSize > 0) {
    int arraySize = storedSize / sizeof(int);
    //Serial.printf("\tnumber of keys = %d\n", arraySize);
    int retrievedArray[arraySize];
    preferences.getBytes("keys", retrievedArray, storedSize);
    for (int i=0; i < arraySize; i++) {
      int key = retrievedArray[i];
      //Serial.printf("\tkey=%d\n", key);
      char key_str[6];
      keyToString(key, key_str);
      String cma = preferences.getString(key_str);
      //Serial.printf("\tcma=%s\n", cma.c_str());
      uint8_t mac_array[6];
      macToArray((char*)cma.c_str(), mac_array);
      char macStr_short[13];
      macToString(mac_array, macStr_short, 13, true);
      //Serial.printf("\tmacStr_short=%s\n", macStr_short);
      int same_key = preferences.getInt(macStr_short);
      Serial.printf("\t{%s: %s}, {%s: %d}\n", key_str, cma.c_str(), macStr_short, same_key);
    }
  }
  else {
    Serial.printf("\tNo data stored in Preferences\n");
  }
  preferences.end();
}

/*
  Use to convert a launcher_id_int to a launcher_id_string, so it can 
  be used as a key in preferences.
*/
void keyToString(int launcher_number, char* launcher_number_str) {
  char buffer[6];
  sprintf(launcher_number_str, "%d", launcher_number); 
  //Serial.printf("launcher_number=%d, launcher_number_str=%s\n", launcher_number, launcher_number_str);
}

/*
  MAC addresses for ESP_NOW need to be an array, whereas it is easier to store the MAC address as
  a string in preferences. However, keys used in preferences cannot exceed 15 characters, so a formatted
  MAC address as a string (18 characters) is too long for a preference key. Therefore, we nned a third MAC address
  form as a stirng without the ':' to keep it at 12 characters.
*/
// Formats MAC Address from uint8_t array to a string for printing and saving to Preferences, short format removes ':'
void macToString(const uint8_t *macArray, char *macString, int maxLength, bool short_format) {
  if (short_format) {
    snprintf(macString, maxLength, "%02x%02x%02x%02x%02x%02x", macArray[0], macArray[1], macArray[2], macArray[3], macArray[4], macArray[5]);
  }
  else {
    snprintf(macString, maxLength, "%02x:%02x:%02x:%02x:%02x:%02x", macArray[0], macArray[1], macArray[2], macArray[3], macArray[4], macArray[5]);
  }
}

/*
  The long string version (with ':') of the MAC address is passed as function arguments, so when saving
  the MAC address to preferences it has to be trimmed down to the short version.
*/
void macToShort(char* macShort, char* macLong) {
  int i, j;
  Serial.printf("size macShort=%d, size macLong=%d\n", strlen(macShort), strlen(macLong));
  for (i = 0, j = 0; i < strlen(macLong); i++) {
    if (macLong[i] != ':') {
      macShort[j++] = macLong[i];
    }
  }
  macShort[j] = '\0';
}

/* 
  Converts a string version of the mac address back into a uint8_t array used by esp-now
*/
void macToArray(char *macString, uint8_t *macArray) {
    sscanf(macString, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &macArray[0], &macArray[1], &macArray[2], &macArray[3], &macArray[4], &macArray[5]);
}

/*
  Not needed for this excercise.
*/
void loop() {

}

removeKey() ends the preferences..

and you can’t begin it twice..

if it’s already started it exits begin..

bool Preferences::begin(const char *name, bool readOnly, const char *partition_label) {
  if (_started) {
    return false;
  }

source code..

~q

Another great save!!

If I move removeKey() to after preferences.end() and before saveKey(), it should work! I am not able to test now, but I will later and report back.

Thanks again!!!!!

UPDATE:

It works as designed! No more bad data in Preferences! Thanks again to all who contributed!

1 Like