RTC Driver
The RTC driver provides a clean API over the DS3231 module: init, read time, write time, and read temperature.
Library
Use the ds3231 component from UncleRus/esp-idf-lib.
This handles the DS3231's BCD register encoding for you.
Add it to your project:
# In your project root
git submodule add https://github.com/UncleRus/esp-idf-lib components/esp-idf-lib
Then add ds3231 to your component's REQUIRES:
idf_component_register(
SRCS "rtc.c"
INCLUDE_DIRS "include"
REQUIRES "ds3231" "driver"
)
Why Not Write Registers Directly?
The DS3231 stores time values in BCD (Binary-Coded Decimal) format. For example, 23:59:45
is stored as 0x23, 0x59, 0x45 — not 23, 59, 45. BCD encoding is easy to
get wrong and tedious to write. Use the library.
Implementation
// components/rtc/rtc.c
#include "ds3231.h"
#include "driver/i2c.h"
#include "rtc.h"
static i2c_dev_t ds3231_dev;
esp_err_t rtc_init(void) {
esp_err_t ret = i2cdev_init();
if (ret != ESP_OK) return ret;
return ds3231_init_desc(
&ds3231_dev,
I2C_NUM_0,
GPIO_NUM_21, // SDA
GPIO_NUM_22 // SCL
);
}
esp_err_t rtc_get_time(struct tm *out) {
return ds3231_get_time(&ds3231_dev, out);
}
esp_err_t rtc_set_time(const struct tm *in) {
return ds3231_set_time(&ds3231_dev, in);
}
float rtc_get_temperature(void) {
float temp = 0;
ds3231_get_temp_float(&ds3231_dev, &temp);
return temp;
}
Reading the Time
struct tm now;
if (rtc_get_time(&now) == ESP_OK) {
printf("Time: %02d:%02d:%02d\n",
now.tm_hour, now.tm_min, now.tm_sec);
}
struct tm is a standard C time structure (time.h). Fields: tm_year (years since 1900),
tm_mon (0–11), tm_mday (1–31), tm_hour, tm_min, tm_sec.
Syncing to System Time
After reading from the RTC, set the ESP32's POSIX system clock so all time() and
localtime() calls work correctly:
struct tm rtc_time;
rtc_get_time(&rtc_time);
time_t epoch = mktime(&rtc_time);
struct timeval tv = { .tv_sec = epoch, .tv_usec = 0 };
settimeofday(&tv, NULL);
After NTP sync (see the NTP feature doc), write the corrected time back to the RTC:
time_t now = time(NULL);
struct tm *tm_now = localtime(&now);
rtc_set_time(tm_now);
Time Zones
Store the timezone as a POSIX TZ string in NVS. Set it at boot with:
// Example: US Eastern time
setenv("TZ", "EST5EDT,M3.2.0,M11.1.0", 1);
tzset();
// Now localtime() returns local time correctly
time_t utc = time(NULL);
struct tm *local = localtime(&utc);
Common TZ strings:
UTC0— UTCEST5EDT,M3.2.0,M11.1.0— US EasternCET-1CEST,M3.5.0,M10.5.0/3— Central EuropeanAEST-10AEDT,M10.1.0,M4.1.0/3— Australian Eastern
Temperature Sensor
The DS3231 has a built-in temperature-compensated crystal and exposes the temperature reading (±3 °C accuracy). This is useful to display room temperature on the clock face as a bonus feature.
float celsius = rtc_get_temperature();
printf("Temperature: %.1f°C\n", celsius);