This code demonstrates how to read and write any data type, such as strings to an external I2C EEPROM. I am sharing this code as I wasn’t happy with the other examples I found on the Internet, which I found to be too complicated.
#include <Wire.h>
#include "i2ceeprom.h"
// struct containing a text array for storage in the EEPROM
struct text_t {
char text[100];
};
int address = 0;
void setup() {
Wire.begin();
Serial.begin(115200);
}
void loop() {
text_t input;
while (Serial.available() < 1) {
delay(100);
}
// obtain array of characters from Serial port
uint8_t i = 0;
while (Serial.available() > 0) {
input.text[i++] = (char) Serial.read();
}
input.text[i++] = '\0';
// Write the input object (which contains a text array)
// to the i2C EEPROM. This does not work with an array directly
// as the array must be within a struct.
address += eeWrite(address, input);
config.count++;
eeWrite(0, config);
// loop through all of the output text
unsigned int output_address = config.start;
text_t output;
while (output_address < config.start+(sizeof(output)*config.count)) {
Serial.print(output_address);
Serial.print(" ");
output_address += eeRead(output_address, output);
Serial.print(output.text);
}
}
Here is the header file to include that includes the related functions.
#include <Arduino.h> // for type definitions
#define DEVICE 0x50
// Read one byte
byte eeReadByte(unsigned int address) {
byte read_data = 0xFF;
Wire.beginTransmission(DEVICE);
Wire.write((int)(address >> 8)); // MSB
Wire.write((int)(address & 0xFF)); // LSB
Wire.endTransmission();
// Request 1 byte from device
Wire.requestFrom(DEVICE, 1);
// if successful return
if (Wire.available()) read_data = Wire.read();
return read_data;
}
// Write one byte (could be made faster by using page write)
// but I've decided not to implement this as speed is not a consideration
// for my projects (I can wait for the data to be written while I do other things)
void eeWriteByte(unsigned int address, byte data) {
if (data == eeReadByte(address)) return; // minimize data writes for speed
// and wear on memory
Wire.beginTransmission(DEVICE);
Wire.write((int)(address >> 8)); // MSB
Wire.write((int)(address & 0xFF)); // LSB
Wire.write(data); // Write byte
Wire.endTransmission();
// Writing in I2C DEVICE takes ~5ms (even if I2C writing already done)
delay(5);
}
// eeWrite is the user function that converts an object into
// a byte stream and then calls eeWriteByte to write the data
//
// int ee = Address to write
// const T& = Any type of object (EXCEPT ARRAY) to write, such as
// byte, int, char, struct. If you want to save
// an array of any type such as a character array,
// you should put the array within in a struct
// or use another non-array type like the string class.
template <class T> int eeWrite(int ee, const T& obj) {
// cast object to a void then to a byte
const byte* p = (const byte*)(const void*)&obj;
// for each byte, write it
for (unsigned int i = 0; i < sizeof(obj); i++)
eeWriteByte(ee++, *p++);
// return the size of data written so we can increment
// our address variable to know the location of the next address
return sizeof(obj);
}
// eeRead is the user function that converts an object into
// a byte stream and then calls eeReadByte to write the data
//
// int ee = Address to start of reading
// const T& = Any type of object (EXCEPT ARRAY) to read, such as
// byte, int, char, struct. That number of bytes will
// be fetched from the eeprom, with data converted back
// into the proper data type.
template <class T> int eeRead(int ee, T& obj) {
byte* p = (byte*)(void*)&obj;
for (unsigned int i = 0; i < sizeof(obj); i++)
*p++ = eeReadByte(ee++);
return sizeof(obj);
}