Last Updated: 06/04/2024

Arduino UNO 24LC256 External EEPROM

Components >> Arduino UNO 24LC256 External EEPROM

Arduino UNO with 24LC256 External EEPROM

EEPROM (electrically erasable programmable read-only memory) is a very cheap chip that allows you to store data and works a bit like a small primitive hard drive.

24LC256 EEPROM

When I say small, I mean very small. The 24LC256 chip can store 256kbits of data, but this works out at 32,768 bytes of data, or if you are storing Integers you could store 16,384 and if you want to store a Long you are reduced to 8,192. So if you are planning to do some intensive data logging, EEPROM is probably not the best choice and you should look at an SD card instead.

Instead EEPROM tends to be used for storing internal settings for a program, so in my projects that are using DCC++ for model railways I use EEPROM in my controllers to store loco data, DCC addresses and descriptions as well as function settings while in my accessory Decoders, for example my turntable, it will store the locations of the various exit points and update every time the adjustment system is used. In my projects I have only stored bytes and Integers although in the example I have included code for reading and writing Longs as well.

Why not use the Arduino built in EEPROM?

The Arduino UNO only has 1024 bytes of built in EEPROM compared to the 32,768 bytes this chip offers so external EEPROM offers a lot more space. However, there are other reasons to use external EEPROM. Firstly not all boards have built in EEPROM, boards such as the Arduino Due and the ESP32 boards do not have internal EEPROM although it is possible to store values in flash memory. However, in my projects I like to use external EEPROM because if the Arduino/ESP32 fails, my sketch can be uploaded to a new board and continue to use my stored settings on the EEPROM chip.

Another factor for using external EEPROM rather than the internal EEPROM is it has 10x the lifecycle. Internal EEPROM can only have a byte reset 100,000 times while external is 1,000,000. This is the number of writes, both have have as many reads as you want. 100,000 may sound a lot but if you have a project that is constantly updating it's settings it's not as many times as you would imagine. Updating a location once a second would wear out the internal EEPROM in around 27 hours!

This means that if you are doing a lot of updates you may need to write something into your code to change the storage location of your data so that it gets moved around the chip, evening out the wear.

The chip does have a means of changing it's address from the default value of 0x50 so you can have multiple chips attached at the same time if you need more storage.

 

I2C Interface

The 24LC256 and it's bigger brother the 24LC512 (512Kbits) use the I2C interface so requires the Wire library.

More information on this library is available at https://www.arduino.cc/en/Reference/Wire , however one of the most important items is often overlooked and that is the read system can read a maximum of 32 bytes of data in one call. It is therefore worth spending some times thinking about how you will store your data.

The chip is wired up to the UNO as seen below (Alternate pins for other Arduino boards are listed at https://www.arduino.cc/en/Reference/Wire ).

The only extra component required is a 10K pull up resistor.

UNO and 24LC256 EEPROM

ESP32 EEPROM circuit

Data Structure

Bytes are stored in addresses from 0 - 32,768 and so it's worth taking some time to think about your data structure.

In my DCC Loco controller I store data for the current controller settings as well as loco data such as it's Decoder Address and the state of all the loco functions.
As I select locos by name rather than Decoder Address I therefore have to store the descriptions of my locos as well.
I therefore use the structure below.

System data starts at address 0

/*
* 0 = controlledlocos[0]...left controller.........limits the system to 255 locos if data stored in single byte

* 1 = controlledlocos[1]....right controller

* 2 = Remote...which controllers loco linked to remote...RESERVED

* 3 = numlocos on system

* 4 = numpoints on system

* 5 = layout year...following for time facility

* 6 = layout month

* 7 = layout day

* 8 = layout dayofweek

* 9 = layout seconds

* 10 = layout minutes

* 11 = layout hours

* 12 = reserved

* 13 = reserved

* 14 = reserved

* 15 = reserved

* 16 = reserved

* 17 = reserved

* 18 = reserved

* 19 = reserved

*/


Loco data starts at address 1000, 100 locos would require 3200 bytes

/* loco data

* 0 = locoid

* 1-2 Decoder address

* 3-31 function

*/


Loco descriptions starts at 10000, 100 locos would require 3200 bytes

/* loco name

*

* 0 = locoid

* 1-20 loco name

* 21-31 Reserved

*

*/


It is important to work out just how much space you require for each set of data so that you do not overwrite a memory location with different bytes of data.
Special thought needs to be put into this when storing Integers and Longs. This is the reason that in my loco data the decoder address requires 2 bytes as it's an integer.

Example 1: EEPROM24LC256v1.ino

Basic script to read and write to EEPROM

Click to Download code: EEPROM24LC256v1.ino

 

 
/*  EEPROM24LC256v1
 *  16/03/2022
 *  Checks 24LC256 external EEPROM is connected
 *  Reads and writes bytes to and from EEPROM
 */
//Wire library required as EEPROM run on I2C
#include "Wire.h"
#define disk1 0x50    //Default Address of 24LC256 eeprom chip in Board

//I2C scanner used on start up to mkake sure eeprom is alive and well
 void scani2c(){
  byte error, address;
    int nDevices = 0;

  Serial.println("Scanning...");

  for (byte address = 1; address < 127; ++address) {
    // The i2c_scanner uses the return value of
    // the Wire.endTransmission to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    byte error = Wire.endTransmission();

    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address < 16) {
        Serial.print("0");
      }
      Serial.print(address, HEX);
      Serial.println("  !");

      ++nDevices;
    } else if (error == 4) {
      Serial.print("Unknown error at address 0x");
      if (address < 16) {
        Serial.print("0");
      }
      Serial.println(address, HEX);
    }
  }
  if (nDevices == 0) {
    Serial.println("No I2C devices found\n");
  } else {
    Serial.println("done\n");
  }
}

//this function writes to an eeprom and location with some passed data.
void EEPROMWrite(int disk, int eepromaddress, byte eepromdata){
  Wire.beginTransmission(disk);
  Wire.write((eepromaddress >> 8));   // MSBthese two lines make sure the address gets sent correctly
  Wire.write((eepromaddress & 0xFF)); // LSB
  Wire.write(eepromdata);//data
  Wire.endTransmission();
  delay(10);//required to give it time to write
}

//function to get data from eeprom
//returns 1 for good command, 0 for error
int EEPROMRead(int disk, int startdatareadaddress, int numbytes){//numbytes cannot be bigger than 32
  if(numbytes < 33){
    Wire.beginTransmission(disk1);
    //splits the address into 2 bytes for transmission
    Wire.write(startdatareadaddress >> 8);   // MSBthese two lines make sure the address gets sent correctly
    Wire.write(startdatareadaddress & 0xFF); // LSB
    Wire.endTransmission();
    Wire.requestFrom(disk, numbytes);    // request bytes from slave device 
    return 1;
  }else{
    Serial.println("EEPROM Read error, too many bytes requested");
    return 0;  
  }
}



void setup() {  
  Serial.begin(9600);
  Serial.println("EEPROM24LC256v1");
  //start the I2C wire library
  Wire.begin();
  //scans I2C and should return an address of 0x50 if all is well
  scani2c();

  int q;
  byte nextByte;
  for(q=0;q < 10;q++){
    Serial.println("q =: "+String(q));
    EEPROMRead(disk1, q * 32, 32); //Read from disk1 starting at an address and read 32 bytes of data 

    //work through the bytes recieved
    while(Wire.available()){    // slave may send less than requested
      nextByte = Wire.read();
      Serial.println(nextByte);;
    }
  }

  Serial.println("Writing first 5 bytes");
  for(q=0;q < 5;q++){
    EEPROMWrite(disk1, q, q);  
  }
  Serial.println("Reading back first 5 bytes");
  EEPROMRead(disk1, 0, 5); //Read from disk1 starting at an address and read 32 bytes of data 

    //work through the bytes recieved
    while(Wire.available()){    // slave may send less than requested
      nextByte = Wire.read();
      Serial.println(nextByte);;
    }

}

void loop() {
  

}

Example 2: EEPROM24LC256v2.ino

Adds the ability to write Integers and Longs to EEROM and read them back

Click to Download code: EEPROM24LC256v2.ino

 
/*  EEPROM24LC256v2
 *  16/03/2022
 *  Reading and Writing Integers and Longs to EEPROM
 */
//Wire library required as EEPROM run on I2C
#include "Wire.h"
#define disk1 0x50    //Default Address of 24LC256 eeprom chip in Board

//I2C scanner used on start up to mkake sure eeprom is alive and well
 void scani2c(){
  byte error, address;
    int nDevices = 0;

  Serial.println("Scanning...");

  for (byte address = 1; address < 127; ++address) {
    // The i2c_scanner uses the return value of
    // the Wire.endTransmission to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    byte error = Wire.endTransmission();

    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address < 16) {
        Serial.print("0");
      }
      Serial.print(address, HEX);
      Serial.println("  !");

      ++nDevices;
    } else if (error == 4) {
      Serial.print("Unknown error at address 0x");
      if (address < 16) {
        Serial.print("0");
      }
      Serial.println(address, HEX);
    }
  }
  if (nDevices == 0) {
    Serial.println("No I2C devices found\n");
  } else {
    Serial.println("done\n");
  }
}

//this function writes to an eeprom and location with some passed data.
void EEPROMWrite(int disk, int eepromaddress, byte eepromdata){
  Wire.beginTransmission(disk);
  Wire.write((eepromaddress >> 8));   // MSBthese two lines make sure the address gets sent correctly
  Wire.write((eepromaddress & 0xFF)); // LSB
  Wire.write(eepromdata);//data
  Wire.endTransmission();
  delay(10);//required to give it time to write
}

void EEPROMWriteInt(int disk, int eepromaddress, int eepromdata){
  byte firstByte;
  byte secondByte;
  firstByte = eepromdata >> 8;
  secondByte = eepromdata & 0xFF;
  Serial.println("firstByte : " + String(firstByte)); 
  Serial.println("secondByte : " + String(secondByte));
  //firstByte * 256 + secondByte = eepromdata 
  EEPROMWrite(disk, eepromaddress, firstByte);
  EEPROMWrite(disk, eepromaddress + 1, secondByte);
}

void EEPROMWriteLong(int disk, int eepromaddress, long eepromdata){
  byte firstByte;
  byte secondByte;
  byte thirdByte;
  byte forthByte;
  Serial.println("eepromdata: ");
  Serial.println(eepromdata);
  firstByte = eepromdata >> 24;
  secondByte = eepromdata >> 16;
  thirdByte = eepromdata >> 8;
  forthByte = eepromdata & 0xFF;
  Serial.println("firstByte : " + String(firstByte)); 
  Serial.println("secondByte : " + String(secondByte));
  Serial.println("thirdByte : " + String(thirdByte)); 
  Serial.println("forthByte : " + String(forthByte));
  
  EEPROMWrite(disk, eepromaddress, firstByte);
  EEPROMWrite(disk, eepromaddress + 1, secondByte);
  EEPROMWrite(disk, eepromaddress + 2, thirdByte);
  EEPROMWrite(disk, eepromaddress + 3, forthByte);
}

//function to get data from eeprom
//returns 1 for good command, 0 for error
int EEPROMRead(int disk, int startdatareadaddress, int numbytes){//numbytes cannot be bigger than 32
  if(numbytes < 33){
    Wire.beginTransmission(disk1);
    //splits the address into 2 bytes for transmission
    Wire.write(startdatareadaddress >> 8);   // MSBthese two lines make sure the address gets sent correctly
    Wire.write(startdatareadaddress & 0xFF); // LSB
    Wire.endTransmission();
    Wire.requestFrom(disk, numbytes);    // request bytes from slave device 
    return 1;
  }else{
    Serial.println("EEPROM Read error, too many bytes requested");
    return 0;  
  }
}

int EEPROMReadInt(int disk, int startdatareadaddress){
  byte firstByte;
  byte secondByte;
  int returnInt;
    Wire.beginTransmission(disk1);
    //splits the address into 2 bytes for transmission
    Wire.write(startdatareadaddress >> 8);   // MSBthese two lines make sure the address gets sent correctly
    Wire.write(startdatareadaddress & 0xFF); // LSB
    Wire.endTransmission();
    Wire.requestFrom(disk, 2);    // request 2 bytes 
    firstByte = Wire.read();
    Serial.println(firstByte);
    secondByte = Wire.read();
    Serial.println(secondByte);
    returnInt =  firstByte * 256 + secondByte;
    Serial.println(returnInt);
    return returnInt;
}

long EEPROMReadLong(int disk, int startdatareadaddress){
  byte firstByte;
  byte secondByte;
  byte thirdByte;
  byte forthByte;
  long returnLong;
    Wire.beginTransmission(disk1);
    //splits the address into 2 bytes for transmission
    Wire.write(startdatareadaddress >> 8);   // MSBthese two lines make sure the address gets sent correctly
    Wire.write(startdatareadaddress & 0xFF); // LSB
    Wire.endTransmission();
    Wire.requestFrom(disk, 4);    // request 4 bytes 
    firstByte = Wire.read();
    Serial.println(firstByte);
    secondByte = Wire.read();
    Serial.println(secondByte);
    thirdByte = Wire.read();
    Serial.println(thirdByte);
    forthByte = Wire.read();
    Serial.println(forthByte);
    returnLong =  (firstByte * 16777216) +(secondByte * 65536) +(thirdByte * 256) + forthByte;
    Serial.println(returnLong);
    return returnLong;
}



void setup() {  
  Serial.begin(9600);
  Serial.println("EEPROM24LC256v2");
  //start the I2C wire library
  Wire.begin();
  //scans I2C and should return an address of 0x50 if all is well
  scani2c();

  int q;
  byte nextByte;
  for(q=0;q < 10;q++){
    Serial.println("q =: "+String(q));
    EEPROMRead(disk1, q * 32, 32); //Read from disk1 starting at an address and read 32 bytes of data 

    //work through the bytes recieved
    while(Wire.available()){    // slave may send less than requested
      nextByte = Wire.read();
      Serial.println(nextByte);;
    }
  }

  Serial.println("Writing first 5 bytes");
  for(q=0;q < 5;q++){
    EEPROMWrite(disk1, q, q);  
  }
  Serial.println("Reading back first 5 bytes");
  EEPROMRead(disk1, 0, 5); //Read from disk1 starting at an address and read 32 bytes of data 

    //work through the bytes recieved
    while(Wire.available()){    // slave may send less than requested
      nextByte = Wire.read();
      Serial.println(nextByte);
    }



    Serial.println("Writing an integer to address 10");
    
    //IMPORTANT: as this is an integer it will use 2 bytes so address 10 and 11 will get used.
    int myAddress = 10;
    int myInt = 12345;
    EEPROMWriteInt(disk1, myAddress, myInt);

    //we now read back our stored value
    myInt = EEPROMReadInt(disk1, myAddress); 
    Serial.println("myInt : " + String(myInt));

    //IMPORTANT: long require 4 bytes so will use myAddress to myAddress + 3
    myAddress = 20;
    long myLong = 1234567890;
    Serial.println("Writing a long to address 20");
    EEPROMWriteLong(disk1, myAddress, myLong);
    myLong = EEPROMReadLong(disk1, myAddress); 
    Serial.println("myLong : " + String(myLong));
}

void loop() {
}

Additional Resource Links

Wite Library reference

Lesson 6: Basic Numeric variables, boolean, byte, int, unsigned int, long, unsigned long 23/07/2021

Comments


This site has been designed to be child friendly, this means that comments cannot be added to videos or directly to the site.
To add a comment or ask a question please email the address in this image: and use Arduino UNO 24LC256 External EEPROM as a reference.