Reading and Writing an External I2C 24LC256 EEPROM

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

1 Comment

  • Hi Andy?
    Thanks for sharing this code.
    Being Dutch I stumbled searching for more info about this code on your site on the Dutch settlement state forest. The name must be very old.

    I was very happy to find a example to write more than 32 bytes to EEPROM after many attemps to write it myself.
    Unfortunately when compiling for an Arduino Uno I get an error.
    The use of templates in C++ like:
    template int eeRead(int ee, T& obj) { byte* p = (byte*)(void*)&obj;
    is just too much for me to debug.
    Where is the config. coming from? Or is is an old Wire command

    Can you help me with this error?

    All the best
    Ed

    C:\Users\ednie\Documents\Files\Arduino\DS3231_EEPROM\RWEEPROM\RWEEPROM.ino: In function ‘void loop()’:
    RWEEPROM:35:3: error: ‘config’ was not declared in this scope
    config.count++;
    ^~~~~~
    C:\Users\ednie\Documents\Files\Arduino\DS3231_EEPROM\RWEEPROM\RWEEPROM.ino:35:3: note: suggested alternative: ‘cosf’
    config.count++;
    ^~~~~~
    cosf
    exit status 1
    ‘config’ was not declared in this scope

Leave a Reply

Your email address will not be published. Required fields are marked *