Arduino library 路 Data and time

I2C_EEPROM: an external 24LC256 or AT24C EEPROM on I2C

An external I2C EEPROM, such as Microchip鈥檚 24LC256 (32 KB) or the AT24C32 found on DS3231 clock modules, keeps data with the power off, like the Uno鈥檚 own EEPROM but much bigger. Rob Tillaart鈥檚 I2C_EEPROM library handles the details that make these chips awkward to drive with Wire alone.

Built in on Mokxi: #include <I2C_eeprom.h> and it builds, with nothing to install. Free, and no account needed.

What I2C_EEPROM does

The awkward details are pages and write time. The chip writes in pages (64 bytes on a 24LC256), and a write that runs past a page boundary wraps around and overwrites the start of the page. After every write the chip spends about 5 ms programming and ignores the bus. writeBlock() splits writes at page edges and waits out each write cycle, so a sketch can write a struct in one call.

The chip鈥檚 address is 0x50 plus its three address pins, so up to eight can share a bus. Tell the constructor the size so the library uses the right address width: one address byte on the small 24C01 to 24C16, two bytes from the 24C32 up.

Library Manager nameI2C_EEPROM
AuthorRob Tillaart
Include#include <I2C_eeprom.h>
#include <I2C_eeprom_cyclic_store.h>
Boardsevery board Mokxi compiles for but ATtiny85
In the simulatorSimulated: it runs against the part the way it does on the bench.
Parts it drivesI2C EEPROM, 24LC256

Install it

It builds on every board Mokxi compiles for but ATtiny85.

In Mokxi it is built in: #include <I2C_eeprom.h> and the sketch compiles, with no download, no zip and no Library Manager.

On a real board, install "I2C_EEPROM" by Rob Tillaart from the Arduino IDE鈥檚 Library Manager (Sketch, Include Library, Manage Libraries), or add I2C_EEPROM to lib_deps in PlatformIO. Mokxi follows the calls of release 1.9.4, so a sketch written here builds there too.

ESP32-C3 路 Arduino Uno 路 Arduino Nano 路 Arduino Mega 2560 路 Arduino Leonardo 路 SparkFun Pro Micro 路 Arduino Micro 路 ESP32-C6 路 Raspberry Pi Pico 路 Raspberry Pi Pico W 路 Seeed XIAO SAMD21 路 STM32 路 STM32 Nucleo-F411RE 路 STM32 Blue Pill 路 micro:bit V2 路 Arduino Uno R4 路 ESP32 DevKit 路 ESP32-S3-DevKitC-1 路 ESP8266 NodeMCU 路 Arduino Pro Mini 3.3 V 路 Seeed XIAO ESP32-C3 路 ESP32-C3 SuperMini 路 Seeed XIAO ESP32-C6 路 ESP32-DevKitC V4 路 LOLIN32 Lite 路 Wemos D1 R32 路 Cheap Yellow Display 路 TTGO T-Display 路 Seeed XIAO ESP32-S3 路 Arduino Nano ESP32 路 Arduino Nano RP2040 Connect 路 Seeed XIAO RP2040 路 Waveshare RP2040-Zero 路 Arduino Uno R4 WiFi

The calls you will use

In the order a sketch meets them.

I2C_eeprom ee(0x50, I2C_DEVICESIZE_24LC256)

Creates the object for the chip at address 0x50 with its size in bytes. The size decides the page size and address width.

ee.begin()

Prepares the library. Call Wire.begin() first, as the examples do.

ee.isConnected()

True when a chip acknowledges at the address, the check to make before trusting reads.

ee.writeByte(0, 42)

Writes one byte at a memory address and returns 0 on success, or Wire鈥檚 error code.

ee.readByte(0)

Reads one byte from a memory address.

ee.writeBlock(100, (uint8_t *)&settings, sizeof(settings))

Writes a run of bytes, here a whole struct, splitting it at page boundaries and waiting for each write cycle.

struct Settings { int offset; float scale; } settings = {12, 1.05};
ee.writeBlock(100, (uint8_t *)&settings, sizeof(settings));
ee.readBlock(100, (uint8_t *)&settings, sizeof(settings));

ee.readBlock(100, buffer, 32)

Reads a run of bytes into a buffer, in chunks that fit Wire鈥檚 buffer.

ee.updateByte(0, 42)

Writes only if the stored byte differs, which saves write cycles; each cell lasts about a million writes.

Example: Keep a counter in a 24LC256

On the Arduino Uno R3. It runs in the browser exactly as printed.

sketch.ino 路 Arduino Uno R3
#include <Wire.h>
#include <I2C_eeprom.h>

I2C_eeprom ee(0x50, I2C_DEVICESIZE_24LC256);

struct Settings {
  uint16_t boots;
  char name[8];
};

void setup() {
  Serial.begin(9600);
  Wire.begin();
  ee.begin();
  if (!ee.isConnected()) {
    Serial.println("ERROR: Can't find eeprom");
    while (1);
  }
  Serial.print("size ");
  Serial.println(ee.getDeviceSize());

  // 100 bytes from address 60: across the page boundary at 64.
  uint8_t out[100];
  for (int i = 0; i < 100; i++) out[i] = i * 3;
  ee.writeBlock(60, out, 100);
  uint8_t in[100];
  ee.readBlock(60, in, 100);
  Serial.println(memcmp(in, out, 100) == 0 ? "block ok" : "block differs");

  Settings s = {7, "mokxi"};
  ee.writeObject(1000, s);
  Settings back;
  ee.readObject(1000, back);
  Serial.print("boots ");
  Serial.print(back.boots);
  Serial.print(" name ");
  Serial.println(back.name);
}

void loop() {}

Wiring

  • Arduino Uno R3 A4 to I2C EEPROM, 24LC256 SDA
  • Arduino Uno R3 A5 to I2C EEPROM, 24LC256 SCL
  • Arduino Uno R3 5V to I2C EEPROM, 24LC256 VCC
  • Arduino Uno R3 GND to I2C EEPROM, 24LC256 VSS
  • Arduino Uno R3 A4 to Resistor 1
  • Resistor 2 to Arduino Uno R3 5V
  • Arduino Uno R3 A5 to Resistor 1
  • Resistor 2 to Arduino Uno R3 5V

Open it in the editor, wired and ready to run

Common errors and how to fix them

isConnected() is false, or "ERROR: Can't find eeprom"

Run an I2C scanner to find the real address: the A0 to A2 pins change it from 0x50 up to 0x57, and on DS3231 modules the EEPROM is often at 0x57. Check SDA, SCL and the pull-up resistors too.

Bytes written with Wire alone come back in the wrong places

A write that crossed a page boundary wrapped to the start of the page. Use writeBlock(), which splits writes at page edges, and give the constructor the right chip size so it knows the page length.

Reads right after a write return 0xFF or fail

The chip ignores the bus for about 5 ms while it programs. The library polls until it answers; code that bypasses it with Wire needs a delay(5) after each write.

Try I2C_EEPROM without installing anything

Open the example, change the sketch, and keep your version in a free account.