Built-in project · Arduino Uno R3

Clock and memory on an Arduino Uno

A DS1307 and a 24LC256 on one I2C bus: the time written to EEPROM once a second, acknowledge polling through every write cycle, and the log read back. The whole build, an Arduino Uno and 2 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Intermediate About 20 minutes to build and run. Runs in your browser. Free, and no account needed.

The parts retail for about $32. On Mokxi they're free.

The circuit runs here once the page has loaded.

What you'll learn

  • Sharing one I2C bus between two devices
  • Reading time from a real-time clock
  • Writing and reading bytes in an EEPROM
  • Acknowledge polling after a slow write
  • Storing a small circular log
  • Difficulty: Intermediate
  • Time: About 20 minutes
  • Board: Arduino Uno R3
  • Parts: 4

How it works

A DS1307 clock and a 24LC256 memory chip share the same two wires, A4 and A5. This is I2C. Each chip has its own address, 0x68 for the clock and 0x50 for the memory, so the Uno can talk to one at a time.

Once a second the sketch reads the time and writes a four-byte record to the memory: hours, minutes, seconds and a count. The records go into eight slots, and the count wraps around them.

After each write the memory chip is busy for a few milliseconds and does not answer. The sketch keeps asking until it does, and prints how many tries that took. Every eight records it reads the whole log back and prints it.

Parts list

4 parts, plus the jumper wires. They retail for about $32; on Mokxi they're free.

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
I2C EEPROM, 24LC256 pin VCCArduino Uno R3 pin 5V
Real-time clock, DS1307 pin VCCArduino Uno R3 pin 5V
I2C EEPROM, 24LC256 pin VSSArduino Uno R3 pin GND
Real-time clock, DS1307 pin GNDArduino Uno R3 pin GND
I2C EEPROM, 24LC256 pin SDAArduino Uno R3 pin A4
Real-time clock, DS1307 pin SDAArduino Uno R3 pin A4
I2C EEPROM, 24LC256 pin SCLArduino Uno R3 pin A5
Real-time clock, DS1307 pin SCLArduino Uno R3 pin A5

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Uno R3
// A DS1307 clock and a 24LC256 EEPROM on one bus: a time-stamped log.
//
// Both chips are on A4 and A5. The clock is at 0x68 and the memory at 0x50,
// which is the whole reason two devices can share two wires.
//
// Once a second the sketch reads the time and writes a four-byte record to
// the EEPROM: hours, minutes, seconds and a count. A write to the 24LC256 is
// only a request; the chip then goes away for up to 5 ms to do it, and does
// not answer its own address until it is done. So after each write the sketch
// polls the address until it is acknowledged again, and prints how many polls
// that took. Every eight records it reads the whole log back.
//
// A new DS1307 is stopped, at 2000-01-01, until something starts it. The
// clock here has been running on its coin cell; tick the part's `halted`
// property to see the sketch find it stopped and start it.

#include <Wire.h>
#include "RTClib.h"

const uint8_t EEPROM_ADDRESS = 0x50;
RTC_DS1307 rtc;
uint8_t count = 0;

void writeRecord(unsigned int at, const uint8_t *record) {
  Wire.beginTransmission(EEPROM_ADDRESS);
  Wire.write((uint8_t)(at >> 8));
  Wire.write((uint8_t)at);
  Wire.write(record, 4);
  Wire.endTransmission();
  // Acknowledge polling: an empty write, until the chip answers again.
  int polls = 0;
  do {
    Wire.beginTransmission(EEPROM_ADDRESS);
    polls++;
  } while (Wire.endTransmission() != 0);
  Serial.print("  (busy for ");
  Serial.print(polls);
  Serial.println(" polls)");
}

void print2(uint8_t value) {
  if (value < 10) Serial.print('0');
  Serial.print(value);
}

void setup() {
  Serial.begin(9600);
  Wire.begin();
  if (!rtc.begin()) {
    Serial.println("no clock at 0x68");
    while (1) delay(10);
  }
  if (!rtc.isrunning()) {
    Serial.println("the clock is halted: starting it");
    rtc.adjust(DateTime(2026, 9, 23, 12, 0, 0));
  }
}

void loop() {
  DateTime now = rtc.now();
  uint8_t record[4] = {now.hour(), now.minute(), now.second(), count};
  print2(record[0]);
  Serial.print(':');
  print2(record[1]);
  Serial.print(':');
  print2(record[2]);
  Serial.print("  record ");
  Serial.print(count);
  writeRecord((unsigned int)(count % 8) * 4, record);
  count++;

  if (count % 8 == 0) {
    Serial.println("the log, read back:");
    Wire.beginTransmission(EEPROM_ADDRESS);
    Wire.write(0);
    Wire.write(0);
    Wire.endTransmission(false);
    Wire.requestFrom(EEPROM_ADDRESS, 32);
    for (int i = 0; i < 8; i++) {
      uint8_t r[4];
      for (int j = 0; j < 4; j++) r[j] = Wire.read();
      Serial.print("  ");
      print2(r[0]);
      Serial.print(':');
      print2(r[1]);
      Serial.print(':');
      print2(r[2]);
      Serial.print(" #");
      Serial.println(r[3]);
    }
  }
  delay(1000);
}

Code walkthrough

The key functions of the sketch, and what each one does.

writeRecord sketch.ino

Sends the memory address in two bytes, then the four record bytes. That only starts the write. The chip then goes busy, so the function sends empty transmissions in a loop until endTransmission returns 0, meaning the chip answers again. It prints how many polls that took.

void writeRecord(unsigned int at, const uint8_t *record) {
  Wire.beginTransmission(EEPROM_ADDRESS);
  Wire.write((uint8_t)(at >> 8));
  Wire.write((uint8_t)at);
  Wire.write(record, 4);
  Wire.endTransmission();
  // Acknowledge polling: an empty write, until the chip answers again.
  int polls = 0;
  do {
    Wire.beginTransmission(EEPROM_ADDRESS);
    polls++;
  } while (Wire.endTransmission() != 0);
  Serial.print("  (busy for ");
  Serial.print(polls);
  Serial.println(" polls)");
}

print2 sketch.ino

A small helper that prints a number with a leading zero when it is below 10, so the time reads 09:05:03 and not 9:5:3.

void print2(uint8_t value) {
  if (value < 10) Serial.print('0');
  Serial.print(value);
}

setup sketch.ino

Starts the serial port and I2C, then looks for the clock. If none answers at 0x68 it prints a message and stops. If the clock is halted, it prints that and sets it to a fixed date and time, which starts it running.

void setup() {
  Serial.begin(9600);
  Wire.begin();
  if (!rtc.begin()) {
    Serial.println("no clock at 0x68");
    while (1) delay(10);
  }
  if (!rtc.isrunning()) {
    Serial.println("the clock is halted: starting it");
    rtc.adjust(DateTime(2026, 9, 23, 12, 0, 0));
  }
}

loop sketch.ino

Reads the time, builds the four-byte record, prints it and writes it to slot count % 8. When count reaches a multiple of 8, it reads all 32 bytes back from address 0 and prints the eight records. Then it waits one second.

void loop() {
  DateTime now = rtc.now();
  uint8_t record[4] = {now.hour(), now.minute(), now.second(), count};
  print2(record[0]);
  Serial.print(':');
  print2(record[1]);
  Serial.print(':');
  print2(record[2]);
  Serial.print("  record ");
  Serial.print(count);
  writeRecord((unsigned int)(count % 8) * 4, record);
  count++;

  if (count % 8 == 0) {
    Serial.println("the log, read back:");
    Wire.beginTransmission(EEPROM_ADDRESS);
    Wire.write(0);
    Wire.write(0);
    Wire.endTransmission(false);
    Wire.requestFrom(EEPROM_ADDRESS, 32);
    for (int i = 0; i < 8; i++) {
      uint8_t r[4];
      for (int j = 0; j < 4; j++) r[j] = Wire.read();
      Serial.print("  ");
      print2(r[0]);
      Serial.print(':');
      print2(r[1]);
      Serial.print(':');
  // ... 7 more lines in the editor

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • The serial monitor says "no clock at 0x68" and nothing else happens This project
    Check SDA on A4 and SCL on A5, and that the clock has 5 V and ground.
  • The log read back shows odd values This project
    Make sure the sketch waits for the write to finish. Skipping the polling loop in writeRecord can lose records.
  • Writes seem to do nothing, or reads return old data. I2C EEPROM
    Wait about 5 ms after each write before the next access. Send two address bytes, high first, then low. Check the A0 to A2 pins set the address, base 0x50.
  • Data wraps around or overwrites earlier bytes. I2C EEPROM
    A write must stay within one 64 byte page. If it passes the page end, it wraps to the page start. Split longer writes at page boundaries. Pull WP low to allow writing.
  • The time is wrong, or stays at the same value. Real-time clock
    A new chip starts halted. Clear the clock halt bit (bit 7 of the seconds register) to start it, and set the time. Most libraries do this when you call the set time function.
  • The clock reads garbage, or the I2C scan finds nothing. Real-time clock
    The DS1307 runs from 5 V, not 3.3 V. It answers at I2C address 0x68. SDA and SCL need pull-up resistors, about 4.7 k. Many modules already have them on board.
  • Upload fails or nothing runs after wiring pins 0 and 1 to a module. Arduino Uno R3
    Pins 0 and 1 are the USB serial port. Move the module to other pins, or unplug it while uploading. Also check the right board is selected.
  • Pins misbehave or the board resets when you power motors or servos from it. Arduino Uno R3
    Each I/O pin gives about 20 mA, 40 mA max. Power motors and servos from their own supply and join its ground to the Uno's GND.

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.