DS3231 and LCD 1602 Clock: Accurate Time Without the Internet
The DS3231 real-time clock keeps time for years on its battery, and an LCD 1602 with an I2C backpack shows it on two lines. Two devices on one I2C bus, setting the time automatically at upload, and the built-in thermometer.
About the project
The ESP32 can get the time from the internet over NTP, but without Wi-Fi it starts from zero after every reset. The DS3231 module is a real-time clock with a temperature-compensated crystal: it drifts about ±2 min a year, and a CR2032 cell keeps it running for years. We show the time, date and temperature on an LCD 1602.
What you need
- ESP32 DevKit
- DS3231 module (ZS-042 or the mini version) + a CR2032 cell
- LCD 1602 with an I2C backpack (PCF8574, address 0x27 or 0x3F)
- The RTClib (Adafruit) and LiquidCrystal_I2C libraries
Wiring
Both modules share one I2C bus, each at its own address (DS3231 is 0x68, the LCD 0x27):
| Module | VCC | GND | SDA | SCL |
|---|---|---|---|---|
| DS3231 | 3V3 | GND | GPIO21 | GPIO22 |
| LCD 1602 | 3V3 (or 5V) | GND | GPIO21 | GPIO22 |
The display works from 3.3 V but with lower contrast: turn the blue trimmer on the I2C board. If you power the LCD from 5 V, remember that its pull-up resistors pull SDA/SCL to 5 V. The ESP32 usually tolerates it, but it is out of spec.
ZS-042 modules have a "charging" circuit for the cell. With a plain CR2032 (not a rechargeable LIR2032) it is better disabled by removing the resistor or diode next to the holder.
How it works
rtc.lostPower()returnstrueif the clock stopped, for example because the cell ran flat or was removed.DateTime(F(__DATE__), F(__TIME__))is the date and time the sketch was compiled. Upload the sketch and the clock sets itself (a few seconds behind, because of the upload time).rtc.getTemperature()is the built-in temperature sensor, in 0.25 °C steps. It measures the chip itself, so inside a case it reads 1–2 °C above the air.snprintfbuilds a fixed-width line, so the digits do not jump around and leftovers of old text are overwritten.
The HD44780 (the LCD controller) has no Cyrillic in its built-in font, so the weekdays are written in Latin letters.
Code
// Clock: DS3231 (time) + LCD 1602 over I2C
#include <Wire.h>
#include <RTClib.h>
#include <LiquidCrystal_I2C.h>
RTC_DS3231 rtc;
LiquidCrystal_I2C lcd(0x27, 16, 2);
const char* DAYS[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
void setup() {
lcd.init();
lcd.backlight();
if (!rtc.begin()) {
lcd.print("RTC not found");
while (true) delay(1000);
}
if (rtc.lostPower()) {
// the cell was removed: set the clock to the moment the sketch was compiled
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
}
void loop() {
DateTime now = rtc.now();
char line[17]; // 16 characters + the terminating zero
snprintf(line, sizeof line, "%02d:%02d:%02d %s", now.hour(), now.minute(), now.second(), DAYS[now.dayOfTheWeek()]);
lcd.setCursor(0, 0);
lcd.print(line);
snprintf(line, sizeof line, "%02d.%02d.%04d %3.0fC", now.day(), now.month(), now.year(), rtc.getTemperature());
lcd.setCursor(0, 1);
lcd.print(line);
delay(200);
}
Common mistakes
- Blank screen or a row of blocks. Wrong address (try 0x3F) or the contrast is not set. An I2C scanner sketch shows the addresses of every device on the bus.
- The time resets after power-off. The cell is flat, or the module is "charging" a plain CR2032.
- The clock is a few seconds slow after an upload. That is expected:
__TIME__is the moment of compilation, not of the upload.
What next
Add buttons to set the time, or sync the DS3231 with NTP once a day: the ESP32 connects to Wi-Fi, gets the exact time and writes it into the RTC.