← How-Tos
electronics Aug 7, 2026 ◑ 4 views ◯ 5 min read

Real-Time Clock Modules for Arduino and ESP32: DS3231 vs DS1307, Battery Backup, and NTP Sync

rtcds3231ds1307real time clocki2carduinoesp32ntpbattery backuptimekeeping

Any project that needs to know the actual time of day — a data logger stamping readings, a clock, an automated feeder, a security system logging events — needs a real-time clock that keeps ticking when the microcontroller is powered off, asleep, or mid-reflash. Arduino and ESP32 boards have no onboard RTC accurate enough for this (the ESP32's internal RTC drifts noticeably and resets its calendar entirely on power loss), so nearly every time-aware project reaches for an external RTC module. The two you'll run into constantly are the DS1307 and the DS3231. They look interchangeable on a breadboard and are not — this guide covers the real differences, wiring, battery backup, and how to keep one in sync with NTP when you have WiFi.

DS1307 vs DS3231: Not the Same Chip

DS1307DS3231 Timekeeping methodExternal 32.768kHz crystalIntegrated temperature-compensated crystal oscillator (TCXO) Accuracy±2 minutes/month typical (worse with cheap crystals or temperature swings)±2 minutes/year (±5ppm across 0-40°C) Backup batteryUsually CR2032 (some boards) or often a bare coin cell holderTypically CR2032 or a small rechargeable LIR2032 Voltage range5V native (needs level shifting on 3.3V-only boards like the ESP32 if not on a breakout with a regulator)2.3V-5.5V, works natively at 3.3V Extras56 bytes of battery-backed SRAMBuilt-in temperature sensor, two alarms, 32kHz output pin Typical priceCheaperA few dollars more

The practical takeaway: the DS1307's accuracy depends entirely on the external crystal's quality and stays fixed regardless of temperature, which is why cheap DS1307 boards drift badly — a few dollars' difference in crystal quality is the whole story. The DS3231's temperature-compensated oscillator is a genuinely different, more expensive design that holds accuracy across a temperature range without any external tuning. For anything logging real timestamps you'll actually rely on, use the DS3231 — the price difference is small and the accuracy difference is not.

Wiring (Both Chips)

Both modules use I2C, so wiring is identical regardless of which chip you choose:

RTC PinESP32 / Arduino VCC3.3V (DS3231) or 5V (DS1307 unless the breakout has an onboard regulator — most common breakout boards do) GNDGND SDAGPIO21 (ESP32) / A4 (Uno) SCLGPIO22 (ESP32) / A5 (Uno)

Both default to I2C address 0x68, which conflicts if you're also running an MPU6050 IMU on the same bus (it also defaults to 0x68) — use an I2C multiplexer or the IMU's AD0 pin to move it to 0x69 in that case.

Reading and Setting the Clock in Arduino

Using the widely-used RTClib library (works with both chips, auto-detecting which one is present):

#include <RTClib.h> RTC_DS3231 rtc; // or RTC_DS1307 rtc; void setup() { Serial.begin(115200); if (!rtc.begin()) { Serial.println("RTC not found"); while (1) delay(10); } if (rtc.lostPower()) { // First boot, or backup battery died — set from compile time rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); } } void loop() { DateTime now = rtc.now(); Serial.printf("%04d-%02d-%02d %02d:%02d:%02d\n", now.year(), now.month(), now.day(), now.hour(), now.minute(), now.second()); delay(1000); }

rtc.lostPower() is worth checking every boot — it tells you whether the backup battery successfully held the clock through a power cycle. If it returns true unexpectedly on a module that's been running for a while, the coin cell is dead or was never making contact.

Battery Backup: What Keeps It Running Without Power

The coin cell isn't optional if you need the clock to survive a power loss — without it, both chips reset to a default or frozen time the instant VCC drops. A few things that trip people up:

Syncing an RTC to NTP When You Have WiFi

If your ESP32 project has WiFi, the best practice is to let NTP correct the RTC's drift periodically rather than trust the RTC alone indefinitely — even a DS3231's ±2 minutes/year drift adds up on a project that runs for years untouched:

#include <WiFi.h> #include <time.h> void syncRTCFromNTP() { configTime(0, 0, "pool.ntp.org", "time.nist.gov"); struct tm timeinfo; if (getLocalTime(&timeinfo, 10000)) { rtc.adjust(DateTime(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec)); } }

Call this once at boot (after WiFi connects) and periodically thereafter — once a day is more than enough for either chip. This gives you the best of both: correct time immediately after any power loss even before WiFi connects (from the RTC), and long-term accuracy that doesn't depend on the RTC's crystal at all (from NTP).

When You Don't Need an External RTC

If a project is always connected to WiFi and never needs to know the time before that connection succeeds, you can skip the external RTC entirely and just call configTime() against an NTP server at boot — many ESPHome and Home Assistant sensor projects do exactly this. Reach for a DS3231 when the project needs correct timestamps immediately at boot (before WiFi associates), needs to keep time through extended power loss, or runs somewhere without reliable network access at all.