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() {
}