diff --git a/examples/Example9_SaveChangedSettings/Example9_SaveChangedSettings.ino b/examples/Example9_SaveChangedSettings/Example9_SaveChangedSettings.ino index c3c70d8..26bc258 100644 --- a/examples/Example9_SaveChangedSettings/Example9_SaveChangedSettings.ino +++ b/examples/Example9_SaveChangedSettings/Example9_SaveChangedSettings.ino @@ -8,205 +8,282 @@ Feel like supporting our work? Buy a board from SparkFun! https://www.sparkfun.com/products/18355 - This example demonstrates how to record various user settings easily to EEPROM, - only writing settings that changed in order to preserve the life of the memory. - Use the ExternalEEPROM::putChanged() method instead of External_EEPROM::put() to do this. + This example demonstrates how to record various user settings easily to + EEPROM, only writing settings that changed in order to preserve the life of + the memory. Use the ExternalEEPROM::putChanged() method instead of + External_EEPROM::put() to do this. The I2C EEPROM should have all its ADR pins set to GND (0). This is default on the Qwiic board. Hardware Connections: - Plug the SparkFun Qwiic EEPROM to an Uno, Artemis, or other Qwiic equipped board - Load this sketch - Open output window at 115200bps + Plug the SparkFun Qwiic EEPROM to an Uno, Artemis, or other Qwiic equipped + board. + Load this sketch. + Open serial monitor window with baud rate at 115200 and line ending set to + Line Feed. */ +// Include the necessary libraries #include -#include "SparkFun_External_EEPROM.h" // Click here to get the library: http://librarymanager/All#SparkFun_External_EEPROM -ExternalEEPROM myMem; +// Click here to get the library: +// http://librarymanager/All#SparkFun_External_EEPROM +// or download the zip here: +// https://github.com/sparkfun/SparkFun_External_EEPROM_Arduino_Library/archive/master.zip +#include "SparkFun_External_EEPROM.h" -#define LOCATION_SETTINGS 0 //Position in EEPROM to store the user setting structure +// Declare the EEPROM object +ExternalEEPROM myMem; -//This is the struct that contains all the user settings. Add as many vars as you want. -struct struct_userSettings { +// This is the position in EEPROM to store the settings struct +#define LOCATION_SETTINGS 0 + +// This is the data that we use to validate the EEPROM data. +// The __TIME__ macro is the time the sketch was compiled, in HH:MM:SS format. +// This means that if the sketch is recompiled, the EEPROM data will be +// considered invalid, +#define VALID_DATA __TIME__ + +// This is the struct that contains all the settings. +// Add as many vars as you want. +typedef struct { + // This first buffer is used to determine if the EEPROM has + // been written to with valid data. If the bytes match the VALID_DATA + // constant, then we can assume the data is valid. + char memoryIsValid[sizeof(VALID_DATA)]; + // A baud rate setting (doesn't actually do anything in this example) unsigned long baudRate; - bool logDate; - bool enableIMU; - float calValue; -}; - -//These are the default settings for each variable. They will be written if the EEPROM is blank. -struct_userSettings settings = { - .baudRate = 115200, - .logDate = false, - .enableIMU = true, - .calValue = -5.17, -}; - -void setup() -{ + // A setting to enable or disable the built-in LED + bool enableLED; +} Settings; + +// This is the settings object. +Settings settings; + +void setup() { + // Set up serial communication Serial.begin(115200); - //delay(250); //Often needed for ESP based platforms - Serial.println(F("Qwiic EEPROM example")); + // delay(250); // Often needed for ESP based platforms + while (!Serial.available()) { + Serial.println(F("Press any key to begin.")); + delay(1000); + } + Serial.println(F("EEPROM load/save example")); + + // Initialize I2C Wire.begin(); - // Default to the Qwiic 24xx512 EEPROM: https://www.sparkfun.com/products/18355 - myMem.setMemoryType(512); // Valid types: 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1025, 2048 + // Configure the EEPROM + // Default to the Qwiic 24xx512 EEPROM: + // https://www.sparkfun.com/products/18355 + // Valid types: 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1025, 2048 + // For example, to use a 24C02 EEPROM, set the memory type to 2 + // Make sure to set this correctly or this example won't work! + myMem.setMemoryType(2); - if (myMem.begin() == false) - { + // Initialize the EEPROM + if (myMem.begin() == false) { Serial.println(F("No memory detected. Freezing.")); while (true); } Serial.println(F("Memory detected!")); - Serial.print("Size of user settings (bytes): "); + // Print out the size of the user settings struct + Serial.print("Size of user settings in bytes: "); Serial.println(sizeof(settings)); - loadUserSettings(); - - Serial.print("Baud rate: "); - Serial.println(settings.baudRate); - - Serial.print("logDate: "); - if (settings.logDate == true) Serial.println("True"); - else Serial.println("False"); - - Serial.print("calValue: "); - Serial.println(settings.calValue, 2); - - //Now we can change something - settings.baudRate = 57600; + Serial.println("Settings are not initialized or loaded from EEPROM."); - //Now we can save it - recordUserSettings(); - - //And we never have to worry about byte alignment or EEPROM locations! - - Serial.println("Press any key to get menu"); + // Present user menu + mainMenu(); } -void loop() -{ - if (Serial.available()) mainMenu(); //Present user menu +void loop() { + if (Serial.available()) mainMenu(); // Present user menu } -void mainMenu() -{ - while (1) - { +void mainMenu() { + int number; + while (1) { Serial.println(); - Serial.println("Menu: Main Menu"); - - Serial.println("1) Set Baud Rate"); - Serial.println("x) Exit"); - - byte incoming = getByteChoice(); //Timeout after 10 seconds - - if (incoming == '1') - { - Serial.print("Enter baud rate (1200 to 115200): "); - int newBaud = getNumber(); - if (newBaud < 1200 || newBaud > 115200) - { - Serial.println("Error: baud rate out of range"); - } - else - { - settings.baudRate = newBaud; - recordUserSettings(); - } - } - else if (incoming == 'x') - break; - else - { - Serial.print("Unknown choice: "); - Serial.write(incoming); - Serial.println(); + Serial.println("Main menu:"); + Serial.println(); + Serial.println("P: Print Settings"); + Serial.println("L: Load Settings"); + Serial.println("S: Save Settings"); + Serial.println("R: Reset Settings to Default"); + Serial.println("B: Set Baud Rate"); + Serial.println("T: Toggle LED Setting"); + Serial.println(); + Serial.print("Enter choice: "); + + byte selection = readByte(); + + switch (selection) { + case 'p': + case 'P': + printSettings(); + break; + case 'l': + case 'L': + loadSettings(); + break; + case 's': + case 'S': + saveSettings(); + break; + case 'r': + case 'R': + resetSettings(); + break; + case 'b': + case 'B': + Serial.print("Enter baud rate (1200 to 115200): "); + number = readNumber(); + if (number < 1200 || number > 115200) { + Serial.println("Error: baud rate out of range"); + } else { + settings.baudRate = number; + } + break; + case 't': + case 'T': + settings.enableLED = !settings.enableLED; + syncLED(); + Serial.print("LED is now "); + Serial.println(settings.enableLED ? "on" : "off"); + break; + default: + Serial.print("Unknown choice: "); + Serial.write(selection); + Serial.println(); } } } -//Blocking wait for user input -void waitForInput() -{ - delay(10); //Wait for any incoming chars to hit buffer - while (Serial.available() > 0) Serial.read(); //Clear buffer +// Blocking wait for user input +void waitForInput() { + delay(10); // Wait for any incoming chars to hit buffer + while (Serial.available() > 0) Serial.read(); // Clear buffer while (Serial.available() == 0); } -//Get single byte from user -//Waits for and returns the character that the user provides -byte getByteChoice() -{ - waitForInput(); //Wait for user to send a value +// Get single byte from user. +// Waits for and returns the character that the user provides +byte readByte() { + waitForInput(); return (Serial.read()); } -//Get a string/value from user, remove all non-numeric values -int getNumber() -{ - waitForInput(); //Wait for user to send a value +// Get a string/value from user, remove all non-numeric values +int readNumber() { + waitForInput(); - //Get input from user - char cleansed[20]; + char numberString[20 + 1]; // Room for 20 digits plus terminating \0 - int spot = 0; - while (spot < 20 - 1) //Leave room for terminating \0 - { - while (Serial.available() == 0) ; //Wait for user input + int index = 0; + while (index < 20) { + // Wait for user input + while (Serial.available() == 0); + // Read the incoming byte byte incoming = Serial.read(); - if (incoming == '\n' || incoming == '\r') - { + + // If the user hits enter, we're done + if (incoming == '\n' || incoming == '\r') { Serial.println(); break; } - if (isDigit(incoming) == true) - { - Serial.write(incoming); //Echo user's typing - cleansed[spot++] = (char)incoming; + // Check for a digit + if (isDigit(incoming) == true) { + // Echo the digit + Serial.write(incoming); + // Add the digit to the string + numberString[index] = (char)incoming; + index++; } } - cleansed[spot] = '\0'; + // Null terminate the string + numberString[index] = '\0'; - String tempValue = cleansed; + // Convert the string to an integer and return it + String tempValue = numberString; return (tempValue.toInt()); } +// This function is called to sync the LED state with the settings +// (On my board at least), the LED is active low, so we invert the setting +void syncLED() { digitalWrite(PIN_LED, !settings.enableLED); } + +// Reset the settings struct to default values (without writing to EEPROM) +void resetSettings() { + // Set the first 8 bytes of the settings struct to the VALID_DATA constant + memcpy(settings.memoryIsValid, VALID_DATA, sizeof(settings.memoryIsValid)); + // Set the baud rate to 9600 + settings.baudRate = 9600; + // Set the LED to off + settings.enableLED = false; + syncLED(); + + Serial.println("Settings reset to default."); +} + +// Load the current settings from EEPROM into the settings struct +void loadSettings() { + // Call the get() method with the location and the struct to load into + myMem.get(LOCATION_SETTINGS, settings); + + // Sync the LED with the settings + syncLED(); + + Serial.println("Settings loaded from EEPROM."); + + if (memcmp(settings.memoryIsValid, VALID_DATA, + sizeof(settings.memoryIsValid)) != 0) { + Serial.println( + "Memory is not valid. You should reset the settings and save them."); + // In a real application, you would do this here: + // resetSettings(); + // saveSettings(); + } else { + Serial.println("Memory is valid!"); + } +} -//Load the current settings from EEPROM into the settings struct -void loadUserSettings() -{ - //Uncomment these lines to forcibly erase the EEPROM and see how the defaults are set - //Serial.println("Erasing EEPROM"); - //myMem.erase(); +// Record the current settings into EEPROM +void saveSettings() { + // The putChanged() method writes the data to the EEPROM + // (the same as the put() method except it only writes data that changed). + myMem.put(LOCATION_SETTINGS, settings); + + Serial.println("Settings saved to EEPROM."); +} - //Check to see if EEPROM is blank. If the first four spots are zeros then we can assume the EEPROM is blank. - uint32_t testRead = 0; - if (myMem.get(LOCATION_SETTINGS, testRead) == 0) //EEPROM address to read, thing to read into - { - //At power on, settings are set to defaults within the struct. - //So go record the struct as it currently exists so that defaults are set. - recordUserSettings(); +// Print the current settings to the serial monitor +void printSettings() { + Serial.println("Current settings:"); + Serial.println(); - Serial.println("Default settings applied"); + Serial.print("Memory valid: "); + for (int i = 0; i < sizeof(settings.memoryIsValid); i++) { + Serial.print(settings.memoryIsValid[i], 16); + Serial.print(' '); } - else - { - //Read current settings - myMem.get(LOCATION_SETTINGS, settings); + Serial.println(); + Serial.print(" VALID_DATA: "); + for (int i = 0; i < sizeof(settings.memoryIsValid); i++) { + Serial.print(VALID_DATA[i], 16); + Serial.print(' '); } -} + Serial.println(); + + Serial.print("Baud rate: "); + Serial.println(settings.baudRate); -//Record the current settings into EEPROM -void recordUserSettings() -{ - // The putChanged() method does the same as put() except it only writes data that changed. - myMem.putChanged(LOCATION_SETTINGS, settings); // That's it! + Serial.print("LED: "); + Serial.println(settings.enableLED ? "on" : "off"); }