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.
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.
- 1 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Half breadboard
- 1 × I2C EEPROM, 24LC256
- 1 × Real-time clock, DS1307
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 pin | Connects to |
|---|---|
| I2C EEPROM, 24LC256 pin VCC | Arduino Uno R3 pin 5V |
| Real-time clock, DS1307 pin VCC | Arduino Uno R3 pin 5V |
| I2C EEPROM, 24LC256 pin VSS | Arduino Uno R3 pin GND |
| Real-time clock, DS1307 pin GND | Arduino Uno R3 pin GND |
| I2C EEPROM, 24LC256 pin SDA | Arduino Uno R3 pin A4 |
| Real-time clock, DS1307 pin SDA | Arduino Uno R3 pin A4 |
| I2C EEPROM, 24LC256 pin SCL | Arduino Uno R3 pin A5 |
| Real-time clock, DS1307 pin SCL | Arduino 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.
// 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 editorTroubleshooting
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 inwriteRecordcan 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.