Friday, March 8, 2013

Use ATtiny85 with I2C EEPROM


This is a simple setup for using an I2C EEPROM with the ATtiny85. There are many sites which outline the use of I2C eeprom, but there are a few nuances when using an ATtiny85 that arise because of the lack of true I2C. I have compiled them here and think that they may be helpful to anyone else trying to do this.


The major components are the ATtiny85, 24LC256, and the USIic2 library.  The ATtiny85 (and others) does not have true I2C, but instead has a USI (universal serial interface).  The USIic2 library takes care of the I2C communication.

I will be using a generic Serial enabled LCD screen for testing purposes.  This requires the SoftwareSerial library.  You must burn the 8MHz internal clock bootloader to use this library.

Additionally, you will need a programmer.  Arduino's can be used as programmers for the ATtiny.


The pull-up resistors are necessary when using the ATtiny85. They are not necessary on the arduino because the arduino has internal pull-ups enabled when using the I2C interface.

The A0, A1, and A2 pins on the EEPROM are chip select inputs. They determine the device address. When all three are low (tied to ground) the device address is 0x50 and when all three are high (tied to Vcc) the device address is 0x57. Possible combinations allow for 8 devices on the same bus. For this example I left mine at 0x50.

The WP pin on the EEPROM is for write protection. When this pin is high you cannot write to the EEPROM (though you can still read).

EEPROM Datasheet

Here is the completed circuit:

Arduino as ISP

NOTE: It has been brought to my attention that my wording may be misleading.  For many this will not make a difference to you because it does not really effect the actions being taken, but has to do with what happens internally when you "Burn bootloader." When I say XXMHz internal/external clock bootloader I am referring to the bootloader that is for whatever setup you want (1MHz internal clock, 8MHz internal clock, or 20MHz external clock).  The bootloader is dependent on the clock for certain functions.  When you "Burn Bootloader" in the arduino environment you unlock the bootloader section on the chip, set fuses, upload the bootloader to the chip, and lock the bootloader section on the chip.  Your clock configuration is handled by setting fuses; it is in no way dependent on the bootloader (bootloader is dependent on the clock).

The ATtiny85 can be programmed using an Arduino.  You must download the ATtiny hardware list, which contains all of the bootloader information.  After downloading the hardware list extract all of the files.  Copy the "attiny" folder to %yoursketchbook%/hardware/ (%yoursketchbook% is whatever directory your sketchbook is).  I burned the wrong bootloader once recently, so I have started editing the boards list so that only the boards that I use show up in the menu.  Access the yoursketchbook/hardware/attiny/boards.txt and edit it so that the only boards in there are the ones you need access to. Make a backup of the original in case you need to add more boards in the future. The file was one single line for me, but each board starts with " (some clock info)". I took out all but attiny85 1MHz and attiny85 8MHz.  Restart the Arduino environment.

Connect your ATtiny85 to the Arduino like so:

The capacitors and crystal are not necessary if you are burning the 1MHz or 8MHz bootloader.  I had managed to screw this up by unintentionally selecting the 20MHz external clock bootloader.  It will not work and you will not be able to burn another bootloader without having connected a crystal and caps first.  The crystal frequency for this does not necessarily matter.  Here is a little shield that I rigged up with a status light, just to make it a little easier:

Upload the sketch "ArduinoISP" to the arduino.  Then go to tools and select the ATtiny85 from  "Board" and Arduino as ISP from "Programmer".  Now you can burn bootloaders and upload sketches.  The ATtiny can be connected up to the arduino while you are setting it up as a programmer.
NOTE: In older versions of arduino you must edit the "ArduinoISP" sketch so that the delay function in the hearbeat() function is:

You need to burn the 8MHz bootloader if you are going to be using the SoftwareSerial library.

You will also need to download the USIi2c library.  Extract these files and add them to the libraries folder in your sketchbook.
NOTE: If your Serial lcd has a library that uses SoftwareSerial or you are using SoftwareSerial to connect to your lcd you must burn the 8MHz bootloader. You will also have to change the values defined in the top of USI_TWI_Master.cpp and USI_TWI_Master.h to account for the different clock rate.
    #define F_CPU 8000000UL // was 1000000UL  
in the .cpp file
    #define SYS_CLK   8000.0 // was 1000.0  
in the .h file
NOTE: You may get a message "avrdude: please define PAGEL and BS2 signals in the configuration file for part ATtiny85". This can be ignored.

The Sketch

Most of configuration I got from this site. I tweaked it because it was originally designed for the arduino.  The tutorial in that link goes over, in detail, how the read and write functions work if you are interested.  They are pretty simple.

#include <TinyWireM.h> // equivalent of Wire library
#include <SoftwareSerial.h>  // for serial lcd, must be 8MHz clock

#define eeprom        0x50              // address for eeprom

SoftwareSerial lcd(4, 3); //define lcd serial device
char abcs[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'};  // array for example purposes

void setup(){
  lcd.begin(9600);  // initialize serial 
  TinyWireM.begin(); // initialize I2C lib
  lcd.print("EEPROM Test 1");  // test lcd
  lcd.print("array object: ");

void loop(){
  // cycle through character array, write first object to
  // address 0, print object from address zero to lcd
  // repeat and cylce through
  for (int i = 0; i < 10; i++){
    writeEEPROM(eeprom, i, abcs[i]); // write object at index i to eeprom address i   
    setcursor(2, 1);
    lcd.write(readEEPROM(eeprom, i));  // print object from eeprom address i

void writeEEPROM(int deviceaddress, unsigned int eeaddress, byte data)
  int rdata = data;
  TinyWireM.send((int)(eeaddress >> 8));
  TinyWireM.send((int)(eeaddress & 0xFF));

byte readEEPROM(int deviceaddress, unsigned int eeaddress)
  byte rdata = 0xFF;
  TinyWireM.send((int)(eeaddress >> 8));
  TinyWireM.send((int)(eeaddress & 0xFF));
  if (TinyWireM.available())
    rdata = TinyWireM.receive();
  return rdata;

void clearlcd()

void setcursor(unsigned int line, unsigned int pos)
  if (line == 1){
    pos = 0x00 + pos;
    pos = 0x40 + pos;
  lcd.write(0xfe); // These 3 lines
  lcd.write(0x45); // change my lcd
  lcd.write(pos); // cursor location

void lcdbckspace()

The final result:


  1. This comment has been removed by the author.

  2. How can I get this to work for ATtiny 84's? It gives me a bunch of errors which are all very similair.

    D:\Arduino\libraries\TinyWireM\USI_TWI_Master.cpp:321: error: 'PORT_USI' was not declared in this scope

    Can I just add
    | defined(__AVR_ATtiny84__)
    to the list of defined devices or do I have to change something?

    Thanks in advance!

  3. doesnt work (