Skip to content

Implement putChanged method #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 212 additions & 0 deletions examples/Example9_SaveChangedSettings/Example9_SaveChangedSettings.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
/*
Read and write data to an external I2C EEPROM
By: Nathan Seidle, Merlin Z
SparkFun Electronics
Date: July 11, 2024
License: This code is public domain but you buy me a coffee if you use this
and we meet someday (Beerware license).
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.

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
*/

#include <Wire.h>

#include "SparkFun_External_EEPROM.h" // Click here to get the library: http://librarymanager/All#SparkFun_External_EEPROM
ExternalEEPROM myMem;

#define LOCATION_SETTINGS 0 //Position in EEPROM to store the user setting structure

//This is the struct that contains all the user settings. Add as many vars as you want.
struct struct_userSettings {
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()
{
Serial.begin(115200);
//delay(250); //Often needed for ESP based platforms
Serial.println(F("Qwiic EEPROM example"));

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

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): ");
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;

//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");
}

void loop()
{
if (Serial.available()) mainMenu(); //Present user menu
}

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

//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
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 input from user
char cleansed[20];

int spot = 0;
while (spot < 20 - 1) //Leave room for terminating \0
{
while (Serial.available() == 0) ; //Wait for user input

byte incoming = Serial.read();
if (incoming == '\n' || incoming == '\r')
{
Serial.println();
break;
}

if (isDigit(incoming) == true)
{
Serial.write(incoming); //Echo user's typing
cleansed[spot++] = (char)incoming;
}
}

cleansed[spot] = '\0';

String tempValue = cleansed;
return (tempValue.toInt());
}


//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();

//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();

Serial.println("Default settings applied");
}
else
{
//Read current settings
myMem.get(LOCATION_SETTINGS, settings);
}
}

//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!
}
13 changes: 13 additions & 0 deletions src/SparkFun_External_EEPROM.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,19 @@ class ExternalEEPROM
return t;
}

template <typename T> const T &putChanged(uint32_t idx, const T &t) // Address, data
{
const uint8_t *newData = (const uint8_t *)&t;
uint8_t oldData[sizeof(T)];
read(idx, oldData, sizeof(T)); // Address, data, sizeOfData
for (uint16_t i = 0; i < sizeof(T); i++) {
if (oldData[i] != newData[i]) {
write(idx + i, newData[i]);
}
}
return t;
}

uint32_t putString(uint32_t eepromLocation, String &strToWrite);
void getString(uint32_t eepromLocation, String &strToRead);

Expand Down