Learn how to use the DS1307 Real Time Clock Module with the ESP32. We’ll cover how to set and read the time. You’ll learn to synchronize the time with the ESP32 local time and with an NTP server periodically. We’ll also cover how to deal with time zones and daylight saving time. Finally, we’ll build an ESP32 digital clock using the DS1307 RTC module and an OLED display.
Note: this tutorial is also compatible with the DS3231 RTC just by changing one line of code in the examples.
Table of Contents
In this tutorials, we’ll cover the following topics:
- Introducing Real-Time Clock (RTC) Modules
- Introducing the DS1307 RTC Module
- Connecting the DS1307 RTC Module to the ESP32
- Working with the RTC
- Installing the RTCLib Library
- Setting and Reading the Time
- Syncing the RTC with an NTP Server
- Daylight Saving Time and Time Zone Adjustments
- ESP32 Clock with RTC Module and OLED Display (with time zone and daylight saving time)
Introducing Real-Time Clock (RTC) Modules
RTC modules, such as the DS3231 and DS1307, have their own tiny clock inside to keep track of time by themselves. Usually, they come with a battery holder to connect a battery so that they keep working even if the ESP32 resets or loses power.
The DS3231 and the DS1307 are some of the most popular choices to use with microcontrollers. Both are compatible with the ESP32 and communicate via I2C communication protocol. The DS3231 is more accurate because it comes with a temperature sensor and gives temperature-compensated results. Nonetheless, the DS1307 is also very accurate and suitable for most applications that need to keep track of time. In this tutorial, we’ll cover the DS1307 RTC module.
Introducing the DS1307 RTC Module
In this tutorial, we’ll be using the DS1307 RTC module, but if you have a DS3231, most of the information provided applies to both modules. Additionally, all the code should be compatible with both modules with just a small change. (In a near future, we’ll create a tutorial for the DS3231 RTC module).
The DS1307 RTC Module comes with the DS1307 chip (to keep track of time) and the AT24C32 EEPROM (to save data permanently). It can also be programmed to output square waves with different frequencies. We’ll only use the DS1307 chip features for timekeeping.
DS1307 Battery Holder
It comes with a battery holder to connect a CR2032 battery to keep accurate timekeeping. In the event of a power outage, it can still keep track of time accurately.
This module also comes with the option to connect a DS18B20 temperature. After connecting that sensor to the module, you can get the temperature from the module DS pin.
DS1307 RTC Module I2C Address
By default, the address of the DS1307 RTC is 0x68 and the EEPROM connected to the module is 0x50. You can run an I2C scanner sketch to double-check the addresses.
DS1307 RTC Module Pinout
The following table quickly describes the DS1307 RTC Module Pinout.
SQ | Output for square waves: 1 Hz, 4.096 kHz, 8.192 kHz, or 32.768 kHz (we won’t use)* |
DS | Output for temperature readings if DS18B20 is connected (we won’t use) |
SCL | SCL pin for I2C |
SDA | SDA pin for I2C |
VCC | Provides power to the module (3.3V or 5V) |
GND | GND |
BAT | Backup supply input (we won’t use) or use the default battery holder |
* the DS1307 SQ pin outputs a continuous square wave— it can’t directly wake the ESP32 at a specific time. For an RTC module with alarm and wake-up capabilities, the DS3231 is a better choice.
Connecting the DS1307 RTC Module to the ESP32
Here’s a list of the parts required for this tutorial:
- ESP32 Board – read Best ESP32 development boards
- DS1307 RTC Module or DS3231 Module
- OLED Display 0.96 inch (optional)
- Jumper wires
- Breadboard
We’ll only use the I2C and power pins to interface the RTC module with the ESP32. We’ll connect SCL to GPIO 22 and SDA to GPIO 21. You can use any other suitable I2C pins, as long as you change them on the code. You can use the following table as a reference or take a look at the schematic diagram.
RTC Module | ESP32 |
SCL | GPIO 22 |
SDA | GPIO 21 |
VCC | 3V3 |
GND | GND |
You may also like: Guide for I2C Communication with the ESP32
Working with the RTC
Using an RTC module in your projects always requires two important steps.
- Setting the current time: you can do it manually by inserting the current time (or a different desired time) on the code; the system’s local time; or get the time from an NTP server.
- Retaining the time: to make sure the RTC keeps the correct time, even if it loses power, it needs to be connected to a battery. RTC modules come with a battery holder, usually for a coin cell.
Installing the RTClib Library
There are several libraries to interface with the DS1307 RTC. We’ll use the RTClib from Adafruit that is compatible with DS1307, DS3231, and PCF8523 RTC modules. Additionally, this library is also compatible with ESP32 boards (even thought that’s not mentioned in the library page).
In the Arduino IDE, go to Sketch > Include Library > Manage Libraries. Search for RTClib and install the library by Adafruit. We’re using version 2.1.4.
Setting and Reading the Time
The following example sets the time on the RTC clock, and then reads the time in the loop every three seconds. This code shows two different ways to set the time: synchronizing the RTC with the system time (date and time the sketch was compiled), and setting a specific date and time manually by writing it yourself on the code.
/*
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete project details at https://RandomNerdTutorials.com/esp32-ds1307-real-time-clock-rtc-arduino/
Based on the RTClib Library examples: github.com/adafruit/RTClib/blob/master/examples
*/
// Date and time functions using a DS1307 RTC connected via I2C and Wire lib
#include "RTClib.h"
RTC_DS1307 rtc;
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
void setup () {
Serial.begin(115200);
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
Serial.flush();
while (1) delay(10);
}
if (! rtc.isrunning()) {
Serial.println("RTC is NOT running, let's set the time!");
// When time needs to be set on a new device, or after a power loss, the
// following line sets the RTC to the date & time this sketch was compiled
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// This line sets the RTC with an explicit date & time, for example to set
// January 21, 2014 at 3am you would call:
//rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}
// When time needs to be re-set on a previously configured device, the
// following line sets the RTC to the date & time this sketch was compiled
//rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// This line sets the RTC with an explicit date & time, for example to set
// January 21, 2014 at 3am you would call:
//rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}
void loop () {
// Get the current time from the RTC
DateTime now = rtc.now();
// Getting each time field in individual variables
// And adding a leading zero when needed;
String yearStr = String(now.year(), DEC);
String monthStr = (now.month() < 10 ? "0" : "") + String(now.month(), DEC);
String dayStr = (now.day() < 10 ? "0" : "") + String(now.day(), DEC);
String hourStr = (now.hour() < 10 ? "0" : "") + String(now.hour(), DEC);
String minuteStr = (now.minute() < 10 ? "0" : "") + String(now.minute(), DEC);
String secondStr = (now.second() < 10 ? "0" : "") + String(now.second(), DEC);
String dayOfWeek = daysOfTheWeek[now.dayOfTheWeek()];
// Complete time string
String formattedTime = dayOfWeek + ", " + yearStr + "-" + monthStr + "-" + dayStr + " " + hourStr + ":" + minuteStr + ":" + secondStr;
// Print the complete formatted time
Serial.println(formattedTime);
Serial.println();
delay(3000);
}
How Does the Code Work
Start by importing the RTCLib library.
#include "RTClib.h"
Then, create an RTC_DS1307 object called rtc.
RTC_DS1307 rtc;
If you’re using a DS3231 RTC module, use the following line instead.
RTC_DS3231 rtc;
Then, you create a char array with the days of the week.
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
You can access each day by its index. For example:
- daysOfTheWeek[0] will return “Sunday”.
- daysOfTheWeek[1] will return “Monday”.
In the setup(), initialize the Serial Monitor.
Serial.begin(115200);
Initialize the RTC module as follows:
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
Serial.flush();
while (1) delay(10);
}
Checking the RTC Status
Then, check if the RTC is keeping the time with the isrunning() function. If it is not running, because it is a new device or because the battery backup failed, we’ll print a message in the Serial Monitor and set the time.
if (! rtc.isrunning()) {
Serial.println("RTC is NOT running, let's set the time!");
Setting the Time
To set the time on thr RTC, we can use the adjust() method on our rtc object. The following line sets the RTC’s date and time to the current date and time when this sketch was last compiled.
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
__DATE__ and __TIME__ are macros that provide the current date and time at compilation.
Alternatively, you can set the date and time manually. Pass the time fields in this order: year, month, day, hour, minute, second. This line is commented on the code.
// January 21, 2014 at 3am you would call:
rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
If you need to re-set the time on a previously configured device, you can call one of the previous two lines to set the time without checking if it is running or not (this is commented on the code).
// When time needs to be re-set on a previously configured device, the
// following line sets the RTC to the date & time this sketch was compiled
//rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// This line sets the RTC with an explicit date & time, for example to set
// January 21, 2014 at 3am you would call:
//rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
Getting Date and Time
In the loop(), we get the date and time every three seconds, and we print it in the Serial Monitor.
We call rtc.now() to get the current date and time from the RTC module.
DateTime now = rtc.now();
It returns a DateTime object containing the values for the current year, month, day, hour, minute, and second.
To access each field of the date and time, we can use the following methods:
now.year() | Gets the current year (e.g., 2024) |
now.month() | Gets the current month (1–12) |
now.day() | Gets the current day of the month (1–31) |
now.dayOfTheWeek() | Gets the day of the week (0-6), where 0 is Sunday, and 6 is Saturday |
now.hour() | Gets the current hour (0–23) |
now.minute() | Gets the current minute (0–59) |
now.second() | Gets the current second (0–59) |
To convert the result into a string, we can use the String() method. We also pass DEC as a second argument to the string() method to get a decimal number.
String yearStr = String(now.year(), DEC);
In case of the month, day, hour, minute, and second, we add a leading zero when the number is smaller than 10. So, instead of having, for example: 3:5:6 (which is weird for a time format), you’ll get 03:05:06.
String monthStr = (now.month() < 10 ? "0" : "") + String(now.month(), DEC);
String dayStr = (now.day() < 10 ? "0" : "") + String(now.day(), DEC);
String hourStr = (now.hour() < 10 ? "0" : "") + String(now.hour(), DEC);
String minuteStr = (now.minute() < 10 ? "0" : "") + String(now.minute(), DEC);
String secondStr = (now.second() < 10 ? "0" : "") + String(now.second(), DEC);
To get the name of the day of the week, we use the daysOfTheWeek array we created at the beginning of the code.
String dayOfWeek = daysOfTheWeek[now.dayOfTheWeek()];
In the end, we concatenate all the time fields in a varaible and display it in the Serial Monitor.
// Complete time string
String formattedTime = dayOfWeek + ", " + yearStr + "-" + monthStr + "-" + dayStr + " " + hourStr + ":" + minuteStr + ":" + secondStr;
// Print the complete formatted time
Serial.println(formattedTime);
Testing the Example
With the RTC connected to the ESP32, upload the code to your board.
Open the Serial Monitor at a baud rate of 115200. The ESP32 will set the RTC time and will display the current time every three seconds.
Syncing the RTC with an NTP Server
The following example is similar to the previous one, but it syncs the RTC time with an NTP server after connecting the ESP32 to the internet. This method is useful if there’s a need to reset or adjust the time without manual input, for example, after changing the coin cell of the RTC, or if for some reason the RTC module crashes. In this particular example, we also add the option to synchronize the time with the NTP server every hour to prevent drifts along the time (the periodic sync should be adjusted depending on your project requirements).
/*
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete project details at https://RandomNerdTutorials.com/esp32-ds1307-real-time-clock-rtc-arduino/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <WiFi.h>
#include <time.h>
#include <RTClib.h>
// Enter your Wi-Fi credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
// NTP server details
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 0; // Offset for GMT in seconds
const int daylightOffset_sec = 3600; // Daylight savings time in seconds
// RTC object (for DS1307 or DS3231)
RTC_DS1307 rtc; // Change to RTC_DS1307 for DS1307 module
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
// Global timeinfo struct and last sync timestamp
struct tm timeinfo;
unsigned long lastSyncMillis = 0; // Last sync time in milliseconds
void setup() {
Serial.begin(115200);
setupWiFi();
// Initialize RTC
if (!rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1);
}
// Sync the RTC at startup
syncTime();
}
void loop() {
checkTimeAndSync(); // Check if 1 hour has passed and sync if necessary
// Get current time from RTC
DateTime now = rtc.now();
// Getting each time field in individual variables
String yearStr = String(now.year(), DEC);
String monthStr = (now.month() < 10 ? "0" : "") + String(now.month(), DEC);
String dayStr = (now.day() < 10 ? "0" : "") + String(now.day(), DEC);
String hourStr = (now.hour() < 10 ? "0" : "") + String(now.hour(), DEC);
String minuteStr = (now.minute() < 10 ? "0" : "") + String(now.minute(), DEC);
String secondStr = (now.second() < 10 ? "0" : "") + String(now.second(), DEC);
String dayOfWeek = daysOfTheWeek[now.dayOfTheWeek()];
// Complete time string
String formattedTime = dayOfWeek + ", " + yearStr + "-" + monthStr + "-" + dayStr + " " + hourStr + ":" + minuteStr + ":" + secondStr;
// Print the complete formatted time
Serial.println(formattedTime);
Serial.println();
delay(10000);
}
void setupWiFi() {
WiFi.begin(ssid, password); // Connect to WiFi
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("WiFi connected.");
}
void syncTime() {
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); // Configure time with NTP server
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain time");
return;
}
Serial.println("\nESP32 Time synchronized with NTP server.");
Serial.print("Current time: ");
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
// Sync the RTC with the NTP time
rtc.adjust(DateTime(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec));
lastSyncMillis = millis(); // Record the last sync time in milliseconds
}
void checkTimeAndSync() {
// Check if 1 hour has passed since the last sync (1 hour = 3600000 milliseconds)
if (millis() - lastSyncMillis >= 3600000) {
Serial.println("Synchronizing time with NTP...");
syncTime();
}
}
How Does the Code Work?
This code is similar to the previous one, but uses an NTP server to sync the time. To get time from an NTP server, the ESP32 needs to be connected to the internet.
First, you need to include the following libraries.
#include <WiFi.h>
#include <time.h>
#include <RTClib.h>
Then, you need to insert your network connections in the following lines.
// WiFi credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
Next we have the NTP server details. The NTP server returns the time in GMT. If you’re on a different time zone, you can add the time zone offset on the gmtOffset_sec variable in seconds. Also add daylight saving time in seconds in the daylightOffset_sec variable if needed.
// NTP server details
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 0; // Offset for GMT in seconds
const int daylightOffset_sec = 3600; // Daylight savings time in seconds
We create a tm structure called timeinfo to save the time from the NTP server.
struct tm timeinfo;
The tm structure contains a calendar date and time broken down into its components:
- tm_sec: seconds after the minute;
- tm_min: minutes after the hour;
- tm_hour: hours since midnight;
- tm_mday: day of the month;
- tm_year: years since 1900;
- tm_wday: days since Sunday;
- tm_yday: days since January 1;
- tm_isdst: Daylight Saving Time flag;
- tm structure documentation.
To learn more about getting time from an NTP server with the ESP32, check out this tutorial: ESP32 NTP Client-Server: Get Date and Time (Arduino IDE).
We create a variable to save the time that has passed since we synchronized the time.
unsigned long lastSyncMillis = 0; // Last sync time in milliseconds
In the setup(), we intialize the Serial Monitor and connect the ESP32 to Wi-Fi.
void setup() {
Serial.begin(115200);
initWiFi();
We initialize the RTC module.
// Initialize RTC
if (!rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1);
}
And finally, we call the syncTime() function, declared at the end of the code, to synchronize the time.
// Sync the RTC at startup
syncTime();
The syncTime() function
On the syncTime() function, we get the time from the NTP server and save it on the timeinfo variable.
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); // Configure time with NTP server
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain time");
return;
}
Then, we call the rtc.adjust() method and pass as arguments the time fields from the timeinfo variable. This will synchronize the RTC time with the time from the NTP server.
// Sync the RTC with the NTP time
rtc.adjust(DateTime(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec));
Note: timeinfo.tm_year holds the number of years since 1900. So, we need to add 1900 to get the current year.
Finally, we update the lastSyncMillis() with the current time.
// Sync the RTC at startup
syncTime();
loop()
In the loop(), we start by calling the checkTimeAndSync() function.
checkTimeAndSync(); // Check if 1 hour has passed and sync if necessary
This function will check if one hour has passed since the last sync, and synchronize the time if needed.
void checkTimeAndSync() {
// Check if 1 hour has passed since the last sync (1 hour = 3600000 milliseconds)
if (millis() - lastSyncMillis >= 3600000) {
Serial.println("Synchronizing time with NTP...");
syncTime();
}
}
Still in the loop(), after synchronizing the time, we get the time from the RTC module and print the time in the Serial Monitor every 10 seconds.
// Get current time from RTC
DateTime now = rtc.now();
// Getting each time field in individual variables
String yearStr = String(now.year(), DEC);
String monthStr = (now.month() < 10 ? "0" : "") + String(now.month(), DEC);
String dayStr = (now.day() < 10 ? "0" : "") + String(now.day(), DEC);
String hourStr = (now.hour() < 10 ? "0" : "") + String(now.hour(), DEC);
String minuteStr = (now.minute() < 10 ? "0" : "") + String(now.minute(), DEC);
String secondStr = (now.second() < 10 ? "0" : "") + String(now.second(), DEC);
String dayOfWeek = daysOfTheWeek[now.dayOfTheWeek()];
// Complete time string
String formattedTime = dayOfWeek + ", " + yearStr + "-" + monthStr + "-" + dayStr + " " + hourStr + ":" + minuteStr + ":" + secondStr;
// Print the complete formatted time
Serial.println(formattedTime);
Serial.println();
delay(10000);
Testing the Code
Upload the previous code to the ESP32. It will connect to the internet, get the time from the NTP server and synchronize the time of the RTC module.
After that, it will display the date and time every 10 seconds.
Every hour, it will resynchronize the time.
Daylight Saving Time and Time Zone Adjustments
Even if you adjust the offset to get the time for your time zone, it will not take into account when daylight saving time is active or not (when the hour changes). There is a way to get the time for you specific time zone and adjusted with daylight saving time using the ESP32 by setting the TZ environment variable to the correct value. We have a detailed tutorial about it in the following link:
The following example is similar to the previous one, but you’ll always get the correct time taking into account your time zone (and daylight saving time).
/*
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete project details at https://RandomNerdTutorials.com/esp32-ds1307-real-time-clock-rtc-arduino/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <WiFi.h>
#include <time.h>
#include <RTClib.h> // For DS3231 or DS1307 RTC module
// Enter your Wi-Fi credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
// NTP server and timezone details
const char* ntpServer = "pool.ntp.org";
const char* timezone = "AST4"; // Example for Western Europe with DST rules - https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
// RTC object (change to RTC_DS1307 for DS1307 module)
RTC_DS3231 rtc;
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
// Track last sync time
struct tm timeinfo;
unsigned long lastSyncMillis = 0; // Last sync time in milliseconds
void setup() {
Serial.begin(115200);
setupWiFi();
// Initialize RTC
if (!rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1); // Stop if RTC is not found
}
// Sync the RTC at startup
syncTime();
}
void loop() {
checkTimeAndSync(); // Check if 1 hour has passed and sync if necessary
// Get current time from RTC
DateTime now = rtc.now();
// Format the time as strings for easier readability
String yearStr = String(now.year());
String monthStr = (now.month() < 10 ? "0" : "") + String(now.month());
String dayStr = (now.day() < 10 ? "0" : "") + String(now.day());
String hourStr = (now.hour() < 10 ? "0" : "") + String(now.hour());
String minuteStr = (now.minute() < 10 ? "0" : "") + String(now.minute());
String secondStr = (now.second() < 10 ? "0" : "") + String(now.second());
String dayOfWeek = daysOfTheWeek[now.dayOfTheWeek()];
// Construct and print the formatted time
String formattedTime = dayOfWeek + ", " + yearStr + "-" + monthStr + "-" + dayStr + " " + hourStr + ":" + minuteStr + ":" + secondStr;
Serial.println(formattedTime);
delay(10000); // Wait 10 seconds before printing again
}
void setupWiFi() {
Serial.print("Connecting to WiFi");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected.");
}
void syncTime() {
// Configure time with NTP server and get time info
configTime(0, 0, ntpServer);
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain time");
return;
}
// Configure timezone for DST using POSIX rule
Serial.println("Configuring timezone and dailight saving time");
setenv("TZ", timezone, 1);
tzset();
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain adjusted time");
return;
}
Serial.println("\n Time synchronized with NTP server with timezone and DST.");
Serial.print("Current time: ");
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
// Update the RTC with NTP time
rtc.adjust(DateTime(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec));
lastSyncMillis = millis(); // Update last sync time
}
void checkTimeAndSync() {
// Sync with NTP server every 1 hour (3600000 ms)
if (millis() - lastSyncMillis >= 3600000) {
Serial.println("Synchronizing time with NTP...");
syncTime();
}
}
Basically, we get the time from the NTP server, convert it to your Time zone by setting the TZ environment variable, and then, we set the RTC module with that time. Every hour, the RTC module resynchronizes its time (you can change this to a longer period).
How Does the Code Work?
This code is quite similar to the previous example, let’s just take a look at the relevant parts to adjust the time zone.
Set your time zone string on the following variable. A list of time zone strings can be found here. Ours is Europe/Lisbon.
const char* timezone = "WET0WEST,M3.5.0/1,M10.5.0";
In the syncTime() function, we first get the time from the NTP server.
void syncTime() {
// Configure time with NTP server and get time info
configTime(0, 0, ntpServer);
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain time");
return;
}
To convert the time to a specific time zone, use the setenv() function to set the TZ (timezone) environment variable as follows, in which timezone is your timezone string.
setenv("TZ", timezone, 1);
After that, call the tzset() function for the changes to take effect.
tzset();
After that, call the getLocalTime() function again to get the time adjusted for your time zone.
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain adjusted time");
return;
}
Now that you have the time adjusted, you can sync the time of the RTC module using rtc.adjust().
rtc.adjust(DateTime(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec));
The rest of the code works exactly like the previous example.
Testing the Example
Now, after uploading the code, you should get the correct time for your timezone and adjusted with daylight saving time.
ESP32 Clock with RTC Module and OLED Display (with time zone and daylight saving time)
Now, we can easily build an ESP32 digital clock that is always with the correct time synchronized with your time zone and taking into account daylight saving time.
Wiring the Circuit
Add an OLED display to your circuit. Wire it as shown in the table below.
OLED Display | ESP32 |
SDA | GPIO 21 |
SCL | GPIO 22 |
VCC | 3V3 |
GND | GND |
ESP32 Clock with RTC Module and OLED Display – Code
Upload the following code to your board. Adjust the timezone variable with your time zone string and insert your network credentials.
/*
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete project details at https://RandomNerdTutorials.com/esp32-ds1307-real-time-clock-rtc-arduino/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <WiFi.h>
#include <time.h>
#include <RTClib.h> // For DS3231 or DS1307 RTC module
#include <Adafruit_SSD1306.h>
// Enter your Wi-Fi credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
// NTP server and timezone details
const char* ntpServer = "pool.ntp.org";
const char* timezone = "WET0WEST,M3.5.0/1,M10.5.0";
// Check list of timezones here: https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
// RTC object (change to RTC_DS3231 for DS3231 module)
RTC_DS1307 rtc;
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
// OLED display setup
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// Track last sync time
struct tm timeinfo;
unsigned long lastSyncMillis = 0;
void setup() {
Serial.begin(115200);
initWiFi();
// Initialize the OLED display
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
while (1); // Stop if display initialization fails
}
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE); // Draw white text
display.setCursor(0, 0); // Start at top-left corner
display.display();
// Initialize RTC
if (!rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1);
}
// Sync the RTC at startup
syncTime();
}
void loop() {
checkTimeAndSync(); // Check if 1 hour has passed and sync if necessary
// Get current time from RTC
DateTime now = rtc.now();
// Format the time as strings
String yearStr = String(now.year());
String monthStr = (now.month() < 10 ? "0" : "") + String(now.month());
String dayStr = (now.day() < 10 ? "0" : "") + String(now.day());
String hourStr = (now.hour() < 10 ? "0" : "") + String(now.hour());
String minuteStr = (now.minute() < 10 ? "0" : "") + String(now.minute());
String secondStr = (now.second() < 10 ? "0" : "") + String(now.second());
String dayOfWeek = daysOfTheWeek[now.dayOfTheWeek()];
// Construct the formatted time
String formattedTime = dayOfWeek + "\n" + yearStr + "-" + monthStr + "-" + dayStr +
"\n" + hourStr + ":" + minuteStr + ":" + secondStr;
// Display the formatted time on the OLED
display.clearDisplay();
display.setCursor(0, 0);
display.println(formattedTime);
display.display();
delay(1000); // Update every second
}
void initWiFi() {
Serial.print("Connecting to WiFi");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected.");
}
void syncTime() {
// Configure time with NTP server and get time info
configTime(0, 0, ntpServer);
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain time");
return;
}
// Configure timezone for DST using POSIX rule
Serial.println("Configuring timezone and daylight saving time");
setenv("TZ", timezone, 1);
tzset();
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain adjusted time");
return;
}
Serial.println("\nTime synchronized with NTP server with timezone and DST.");
Serial.print("Current time: ");
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
// Update the RTC with NTP time
rtc.adjust(DateTime(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec));
lastSyncMillis = millis(); // Update last sync time
}
void checkTimeAndSync() {
// Sync with NTP server every 1 hour (3600000 ms)
if (millis() - lastSyncMillis >= 3600000) {
Serial.println("Synchronizing time with NTP...");
syncTime();
}
}
We won’t explain how this code works. Most of it was explained in previous examples. To learn how to interface the OLED display with the ESP32, you can use the following tutorial as a reference.
Demonstration
After uploading the code, the digital clock is ready. Now, you can build a nice case for a more finished look.
Wrapping Up
In this tutorial, you learned how to use the DS1307 RTC module with the ESP32. You learned how to set and read its time. You also learned how to synchronize the time with an NTP server periodically, so that the RTC is always on time. Additionally, we also took a look at how to adjust the time to your time zone and take into account daylight saving time.
Finally, we created a digital clock using an OLED display. Alternatively, you may also want to use an LCD, for example.
We have tutorials and guides for more than 20 modules with the ESP32 that you may find useful:
Other projects you may liked:
- ESP32/ESP8266: DHT Temperature and Humidity Readings in OLED Display
- ESP32 CYD with LVGL: Digital Clock with Time and Date
- How to Use I2C LCD with ESP32 on Arduino IDE (ESP8266 compatible)
We hope you’ve found this guide useful. Learn more about the ESP32 with our resources:
Thanks for reading.