On [Arduino](https://www.ampheo.com/c/development-board-arduino), EEPROM.put() / EEPROM.get() are designed for the internal EEPROM (on AVR boards like [Arduino Uno](https://www.ampheo.com/product/a000046-25542493)/Nano/Mega). For an external EEPROM (typically I²C parts like [24LC256](https://www.onzuu.com/search/24LC256)/[24LC512](https://www.onzuu.com/search/24LC512)/[AT24Cxx](https://www.onzuu.com/search/AT24C)), you have two good options:
1. Use an external-EEPROM library that already provides get/put-like helpers, or
2. Write your own “put/get” wrappers that read/write arbitrary types over I²C.
Below is a practical, copy-paste friendly approach (option 2), plus notes to avoid the classic EEPROM pitfalls.

**Typical external EEPROM wiring (I²C)**
* EEPROM SDA → Arduino SDA
* EEPROM SCL → Arduino SCL
* Pull-ups on SDA/SCL (often 4.7k to 10k to VCC)
* EEPROM A0/A1/A2 set device address (commonly all GND → base address)
* EEPROM WP (write protect) to GND to allow writes (or VCC to lock)
Most 24xx EEPROMs show up at I²C address 0x50–0x57 depending on A0–A2.
**“put/get” for external I²C EEPROM (works with structs, floats, etc.)**
Example assumes a 16-bit address EEPROM like 24LC256 / 24LC512 (most common).
**Core byte read/write + ACK polling (recommended)**
```
#include <Wire.h>
static const uint8_t EEPROM_I2C_ADDR = 0x50; // 0x50 + (A2 A1 A0)
static const uint16_t EEPROM_PAGE_SIZE = 64; // 24LC256 often 64-byte pages
// Wait until EEPROM finishes its internal write cycle (ACK polling).
void eepromWaitReady() {
while (true) {
Wire.beginTransmission(EEPROM_I2C_ADDR);
uint8_t err = Wire.endTransmission();
if (err == 0) break; // device ACKed
delay(1);
}
}
void eepromWriteByte(uint16_t memAddr, uint8_t data) {
Wire.beginTransmission(EEPROM_I2C_ADDR);
Wire.write((uint8_t)(memAddr >> 8)); // high byte
Wire.write((uint8_t)(memAddr & 0xFF)); // low byte
Wire.write(data);
Wire.endTransmission();
eepromWaitReady();
}
uint8_t eepromReadByte(uint16_t memAddr) {
Wire.beginTransmission(EEPROM_I2C_ADDR);
Wire.write((uint8_t)(memAddr >> 8));
Wire.write((uint8_t)(memAddr & 0xFF));
Wire.endTransmission(false); // repeated start
Wire.requestFrom(EEPROM_I2C_ADDR, (uint8_t)1);
return Wire.available() ? Wire.read() : 0xFF;
}
```
**Page-safe block write + sequential read**
External EEPROMs write in pages. If you cross a page boundary in a single write, data can wrap and corrupt.
```
void eepromWriteBlock(uint16_t memAddr, const uint8_t* data, size_t len) {
while (len > 0) {
uint16_t pageOffset = memAddr % EEPROM_PAGE_SIZE;
uint16_t spaceInPage = EEPROM_PAGE_SIZE - pageOffset;
uint16_t chunk = (len < spaceInPage) ? len : spaceInPage;
Wire.beginTransmission(EEPROM_I2C_ADDR);
Wire.write((uint8_t)(memAddr >> 8));
Wire.write((uint8_t)(memAddr & 0xFF));
for (uint16_t i = 0; i < chunk; i++) {
Wire.write(data[i]);
}
Wire.endTransmission();
eepromWaitReady();
memAddr += chunk;
data += chunk;
len -= chunk;
}
}
void eepromReadBlock(uint16_t memAddr, uint8_t* out, size_t len) {
Wire.beginTransmission(EEPROM_I2C_ADDR);
Wire.write((uint8_t)(memAddr >> 8));
Wire.write((uint8_t)(memAddr & 0xFF));
Wire.endTransmission(false);
size_t idx = 0;
while (idx < len) {
uint8_t chunk = (len - idx > 32) ? 32 : (uint8_t)(len - idx); // Wire buffer
Wire.requestFrom(EEPROM_I2C_ADDR, chunk);
while (Wire.available() && idx < len) {
out[idx++] = Wire.read();
}
}
}
```
**Now the “put/get” templates (drop-in replacements)**
```
template <typename T>
void eepromPut(uint16_t addr, const T& value) {
const uint8_t* p = (const uint8_t*)&value;
eepromWriteBlock(addr, p, sizeof(T));
}
template <typename T>
void eepromGet(uint16_t addr, T& value) {
uint8_t* p = (uint8_t*)&value;
eepromReadBlock(addr, p, sizeof(T));
}
```
**Example: store and load a struct**
```
struct Calibration {
float offset;
float scale;
uint32_t magic;
};
static const uint16_t CAL_ADDR = 0x0000;
void setup() {
Serial.begin(115200);
Wire.begin();
// Write
Calibration calWrite { 1.25f, 0.98f, 0xC0A1B123 };
eepromPut(CAL_ADDR, calWrite);
// Read back
Calibration calRead;
eepromGet(CAL_ADDR, calRead);
Serial.println(calRead.offset, 6);
Serial.println(calRead.scale, 6);
Serial.println(calRead.magic, HEX);
}
void loop() {}
```
**Important notes (so it works every time)**
* Write cycle time: external EEPROM needs a few ms to internally commit data. That’s why ACK polling (eepromWaitReady()) matters.
* Page boundaries: always write in page-safe chunks (done above).
* Wire buffer: many Arduino cores limit I²C TX/RX to ~32 bytes at a time.
* Wear: EEPROM has limited write endurance. Avoid writing the same address constantly; consider writing only when values change, or implement simple wear-leveling.
**If you prefer a library**
Search [Arduino](https://www.ampheoelec.de/c/development-board-arduino) Library Manager for:
* extEEPROM (popular for 24xx I²C EEPROM)
* I2C_eeprom
Many of these libraries provide .read()/.write() and sometimes .get()/.put() style helpers.