One of the main applications for the Arduino board is reading and logging of sensor data. The data can be written to several devices. One of the devices I experimented with was an I2C EEPROM 24LC256 which was able to hold 32KB memory. That may not sound much but with a logging need of less than a KB per day it is enough for one month. 1KB /day is still 40 bytes / hour, e.g 40 single byte temp readings.
Besides for logging the I2C EEPROM can be used for configuration, serial number, license info, and (const) messages to be sent to a screen or so. In a way an EEPROM can be used as a sort of "slow RAM.". I found - http://www.arduino.cc/playground/Code/I2CEEPROM - very usefull but major drawback was that the Wire library to read/write to the I2C EEPROM only read and writes in relative small chunks of max. 32 bytes.
2011-05-18 lib works with 24LC512 too, thanks Ross for testing.
http://www.datasheetcatalog.org/datasheet2/7/0yuw0yc3x0278cpz78zge08qi13y.pdf
Arduino Analog pin 5 - SCL - EEPROM pin 6
Arduino 5V - VCC - EEPROM pin 8
Arduino GND - VSS - EEPROM pin 4
Pin 1,2,3 of the eeprom must be connect to GND too unless other address is used, see datasheet.
The library proposed here is inspired by the website mentioned earlier and is in fact a simple wrapper around the Wire library. It does writing to and reading from a I2C EEPROM, nothing more or less. What makes it special is that it's interface allows blocks of "any" size to be written or read. Internal the object to be written is chuncked to smaller pieces of 16 bytes, but the programmer only need to remember the address where the object is located.
The interface of the class supports functions for reading and writing a single byte and functions that write/read a block. Furthermore there is a constructor that needs the I2C address to identify the right EEPROM.
void writeByte(unsigned int, uint8_t ); // write a byte to address
uint8_t readByte(unsigned int); // read a byte from address
void writeBlock(unsigned int, uint8_t* , int ); // write a bunch of bytes
void readBlock(unsigned int, uint8_t*, int ); // read a bunch of bytes
//first param is the address.
//second param is pointer to object.
//third is the size of the object.
A small sketch shows how the class can be used, and it tests the 64 byte boundary bug.
* Example: I2C bus - EEPROM 24LC256
*/
#include <Wire.h> //I2C library
#include <I2C_eeprom.h>
I2C_eeprom ee(0x50);
void setup()
{
Serial.begin(115200);
Serial.print("Demo I2C eeprom library ");
Serial.print(I2C_EEPROM_VERSION);
Serial.println("\n");
}
void loop()
{
Serial.println("\nTEST: 64 byte page boundary writeBlock");
ee.setBlock(0,'0',100);
dumpEEPROM(0, 80);
char data[] = "11111111111111111111";
ee.writeBlock(61, (uint8_t*) data, 10);
dumpEEPROM(0, 80);
Serial.println("\nTEST: 64 byte page boundary setBlock");
ee.setBlock(0,'0',100);
dumpEEPROM(0, 80);
ee.setBlock(61, '1', 10);
dumpEEPROM(0, 80);
Serial.println("\nTEST: 64 byte page boundary readBlock");
ee.setBlock(0,'0',64);
ee.setBlock(64, '1', 64);
dumpEEPROM(0, 128);
char ar[100];
memset(ar,0,100);
ee.readBlock(60, (uint8_t*)ar, 10);
Serial.println(ar);
Serial.println("\nTEST: write large string readback in small steps");
ee.setBlock(0,'X',128);
char data2[] = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999A";
ee.writeBlock(10, (uint8_t *) &data2, 100);
dumpEEPROM(0, 256);
for (int i = 0; i<100; i++)
{
if (i%10 == 0 ) Serial.println();
Serial.print(ee.readByte(10+i));
}
Serial.println();
while(1);
}
void dumpEEPROM(unsigned int addr, unsigned int length)
{
// block to 10
addr = addr / 10 * 10;
length = (length + 9)/10 * 10;
byte b = ee.readByte(addr);
for (int i = 0; i < length; i++)
{
if (addr % 10 == 0)
{
Serial.println();
Serial.print(addr);
Serial.print(":\t");
}
Serial.print(b);
b = ee.readByte(++addr);
Serial.print(" ");
}
Serial.println();
}
//
// END OF FILE
//
In setup() a large string is written to EEPROM. The programmer only need to remember the length and the address it was written to.
In loop() the long string is fetched byte by byte in a loop, followed by how a block of data can be read from eeprom. Note that the you can start reading just anywhere.
To use the library, make a folder in your SKETCHBOOKPATH\libaries with the name I2C_EEPROM and put the .h and .cpp there. Optionally make a examples subdirectory to place the sample app.
The library does not validate anything (like address out of range), that is up to the programmer. Maybe in a next release ;)
24LC512 (64K) confirmed to work too; for larger EEPROMS the address variables should change from int to long.
2011-02-11 - Fixed bug with writing blocks over 64byte boundaries. Added a more elaborate test program to show it does work now.
Enjoy tinkering,
rob.tillaart@removethisgmail.com
#define I2C_EEPROM_H
//
// FILE: I2C_eeprom.h
// AUTHOR: Rob Tillaart
// PURPOSE: Simple I2C_eeprom library for Arduino with EEPROM 24LC256 et al.
// VERSION: 0.2.00
// URL: http://arduino.cc/playground/Main/LibraryForI2CEEPROM
// HISTORY: See I2C_eeprom.cpp
//
// Released to the public domain
//
#include <Wire.h>
// BLOCKSIZE must be 16
#define BLOCKSIZE 16
#define I2C_EEPROM_VERSION "0.2"
// interface
class I2C_eeprom
{
public:
// (I2C address)
I2C_eeprom(uint8_t);
// (mem_address, value)
void writeByte(unsigned int, uint8_t );
// (mem_address, buffer, length)
void writeBlock(unsigned int, uint8_t*, int );
// (mem_address, value, count)
void setBlock(unsigned int, uint8_t, int );
// (mem_address)
uint8_t readByte(unsigned int );
// (mem_address, buffer, length)
void readBlock(unsigned int, uint8_t*, int );
private:
uint8_t _Device;
// (address)
int endOfPage(unsigned int);
// (mem_address, buffer, length)
void _WriteBlock(unsigned int, uint8_t*, uint8_t );
void _ReadBlock(unsigned int, uint8_t*, uint8_t );
};
#endif
// END OF FILE
// FILE: I2C_eeprom.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.2.00
// PURPOSE: Simple I2C_eeprom library for Arduino with EEPROM 24LC256 et al.
//
// HISTORY:
// 0.1.00 - 2011-01-21 initial version
// 0.1.01 - 2011-02-07 added setBlock function
// 0.2.00 - 2011-02-11 fixed 64 bit boundary bug
//
// Released to the public domain
//
#include <I2C_eeprom.h>
#include "Wstring.h"
#include "Wiring.h"
////////////////////////////////////////////////////////////////////
//
// PUBLIC
//
I2C_eeprom::I2C_eeprom(uint8_t device)
{
_Device = device;
Wire.begin(); // initialise the connection
}
void I2C_eeprom::writeByte(unsigned int address, uint8_t data )
{
_WriteBlock(address, &data, 1);
}
void I2C_eeprom::writeBlock(unsigned int address, uint8_t* buffer, int length)
{
// determine length until end of page
int le = endOfPage(address);
if (le > 0)
{
_WriteBlock(address, buffer, le);
address += le;
buffer += le;
length -= le;
}
// write the rest at BLOCKSIZE (16) byte boundaries
while (length > 0)
{
_WriteBlock(address, buffer, min(length, BLOCKSIZE));
address += BLOCKSIZE;
buffer += BLOCKSIZE;
length -= BLOCKSIZE;
}
}
void I2C_eeprom::setBlock(unsigned int address, uint8_t data, int length)
{
uint8_t buffer[BLOCKSIZE];
for (uint8_t i =0; i< BLOCKSIZE; i++) buffer[i] = data;
// determine length until end of page
int le = endOfPage(address);
if (le > 0)
{
_WriteBlock(address, buffer, le);
address += le;
length -= le;
}
while (length > 0)
{
_WriteBlock(address, buffer, min(length, BLOCKSIZE));
address += BLOCKSIZE;
length -= BLOCKSIZE;
}
}
uint8_t I2C_eeprom::readByte(unsigned int address)
{
uint8_t rdata;
_ReadBlock(address, &rdata, 1);
return rdata;
}
// maybe let's not read more than 30 or 32 uint8_ts at a time!
void I2C_eeprom::readBlock(unsigned int address, uint8_t* buffer, int length)
{
while (length > 0)
{
_ReadBlock(address, buffer, min(length, BLOCKSIZE));
address += BLOCKSIZE;
buffer += BLOCKSIZE;
length -= BLOCKSIZE;
}
}
////////////////////////////////////////////////////////////////////
//
// PRIVATE
//
// detemines length until first multiple of 16 of an address
// so writing allways occurs up to 16 byte boundaries
// this is automatically 64 byte boundaries
int I2C_eeprom::endOfPage(unsigned int address)
{
const int m = BLOCKSIZE;
unsigned int eopAddr = ((address + m - 1) / m) * m; // "end of page" address
return eopAddr - address; // length until end of page
}
// pre: length < 32;
void I2C_eeprom::_WriteBlock(unsigned int address, uint8_t* buffer, uint8_t length)
{
Wire.beginTransmission(_Device);
Wire.send((int)(address >> 8));
Wire.send((int)(address & 0xFF));
for (uint8_t c = 0; c < length; c++)
Wire.send(buffer[c]);
Wire.endTransmission();
delay(5);
}
// pre: buffer is large enough to hold length bytes
void I2C_eeprom::_ReadBlock(unsigned int address, uint8_t* buffer, uint8_t length)
{
Wire.beginTransmission(_Device);
Wire.send((int)(address >> 8));
Wire.send((int)(address & 0xFF));
Wire.endTransmission();
Wire.requestFrom(_Device, length);
for (int c = 0; c < length; c++ )
if (Wire.available()) buffer[c] = Wire.receive();
}
//
// END OF FILE
//
# Syntax Coloring Map For I2C_EEPROM
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
I2C_eeprom KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
readByte KEYWORD2
writeByte KEYWORD2
setBlock KEYWORD2
readBlock KEYWORD2
writeBlock KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################