ESP32 ESP-IDF RTC Real Time Clock with DS3231

In this tutorial, we will learn to interface DS3231 RTC Module using ESP32 and ESP-IDF. This guide will include a brief description of the RTC module, connection with ESP32 board and then setting up a project in VS Code with ESP-IDF extension to acquire the current date/time along with the internal chip temperature and display it on the console.

Before we move ahead, make sure you have the latest version of VS Code installed on your system with the ESP-IDF extension configured.

DS3231 Precision RTC Module Introduction

The DS3231 RTC module is widely used to keep track of the current date and time. It is used in electronics projects for time keeping, data logging, building clocks, timers etc. This module runs on the DS3231S RTC chip and also contains the AT24C3 EEPRROM for accurate time keeping. The module consists of an integrated temperature compensated crystal oscillator and crystal resonator, whereby making it an extremely accurate RTC. Moreover, the accuracy of the time is maintained through a long time due to the presence of the crystal resonator.

On the backside of the RTC module, it possesses a 20mm 3V lithium coin cell holder which fits a CR2032 battery. This battery is extremely vital in tracking the time and making sure it stays correct in case the main power is cut off. Therefore, the CR2032 cell acts as a backup battery source incase the main power is lost.

The diagram below shows the front and the backside of the DS3231 RTC module. Note that the front side contains the DS3231 RTC chip and the 24C32 EEPROM whereas the backside consists of the CR2032 battery holder and the coin cell respectively.

DS3231 RTC Module

DS3231 RTC Module backside
Back of DS3231 RTC Module

DS3231 RTC Module Pinout

The diagram below shows the pinout of the DS3231 RTC module.

DS3231 RTC Module Pinout

As you can see in the diagram above, the DS3231 module consists of a total of ten pins but we only require the I2C communication pins (SCL and SDA) and the power supply pins (VCC and GND) to access the current time using a single module. The I2C pins and power pins on the other side of the module are given to connect to another module (daisy chain).

33KThis pin outputs the compensated temperature and the accurate reference clock.
SQWThis pin outputs square waves of different frequencies for example 1Hz, 4kHz, 8kHz, or 32kHz.
SCLThis is the serial clock pin which will produce the clock signal
SDAThis is the serial data pin which is used for sending and receiving data
VCCThis is the power supply pin which is connected with Vin (5V) or 3.3V of ESP32.
GNDThis is the ground pin.

Interfacing DS3231 RTC Module with ESP32

We will need the following components to connect our ESP32 board with the DS3231 Module.

  1. ESP32 board
  2. DS3231 Module
  3. Connecting Wires
  4. Breadboard

The connection of DS3231 Module with the ESP32 board is straightforward as it just involves the connection of the 4 pins (GND, VCC, SDA, and SCL) with ESP32. We have to connect the VCC terminal with Vin pin, ground with the ground (common ground), SCL of the sensor with SCL of ESP32 board, and SDA of the sensor with the SDA pin of the ESP32 board. We will use the default I2C pins of ESP32 board to connect with the sensor module.

In ESP32, the default I2C pin for SDA is GPIO21 and for SCL is GPIO22.

The connections between the devices are specified in the table below:

DS3231 ModuleESP32
GNDGND
VCC3.3V
SDAGPIO21(I2C SDA)
SCLGPIO22 (I2C SCL)
ESP32 with DS3231 RTC Module schematic diagram

Real Time Clock with ESP32 using DS3231 Module and ESP-IDF

We will build and create a project in VS Code with ESP-IDF extension. The code will output the current date/time and the internal chip temperature in the ESP-IDF terminal using the I2C device library, esp_idf_lib_helpers and ds3231.h. We will use ds3231 driver written by UncleRus.

Create Example Project

Open your VS Code and head over to View > Command Palette. Type ESP-IDF: New Project in the search bar and press enter.

Specify the project name and directory. We have named our project ‘DS3231_ESP32.’ For the ESP-IDF board, we have chosen the custom board option. For ESP-IDF target, we have chosen ESP32 module. Click ‘Choose Template’ button to proceed forward.

In the Extension, select ESP-IDF option:

ESP-IDF in VS Code New Project 2

We will click the ‘sample_project’ under the get-started tab. Now click ‘Create project using template sample_project.’

ESP-IDF in VS Code New Project 3

You will get a notification that the project has been created. To open the project in a new window, click ‘Yes.’

This opens the project that we created inside the EXPLORER tab. There are several folders inside our project folder. This is the same for every project which you will create through ESP-IDF Explorer.

  • First, let’s add the necessary header files for the libraries required for this project. Create a new folder called ‘components’ and add the following three sub-folders under it:
  1. esp_idf_lib_helpers
  2. i2cdev
  3. ds3231

Add ds3231, esp_idf_lib_helpers, and i2cdev folders in ‘components’ folder by copying from this link as listed below:

ESP32 ESP-IDF Real Time Clock using DS3231 Module Add components

ESP32 DS3231 Display Current Time and Date Code

Now head over to the main.c file. The main folder contains the source code meaning the main.c file will be found here. Go to main > main.c and open it. Copy the code given below in that file and save it.

This is an example sketch for the driver ds3231. It continuously prints the current date and time, along with the internal chip temperature in the ESP-IDF terminal.

#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <ds3231.h>
#include <string.h>

void ds3231_test(void *pvParameters)
{
    i2c_dev_t dev;
    memset(&dev, 0, sizeof(i2c_dev_t));

    ESP_ERROR_CHECK(ds3231_init_desc(&dev, 0, 21, 22));

    struct tm time = {
        .tm_year = 122, // (2022 - 1900)
        .tm_mon  = 11,  // 0-based
        .tm_mday = 15,
        .tm_hour = 0,
        .tm_min  = 50,
        .tm_sec  = 10
    };
    ESP_ERROR_CHECK(ds3231_set_time(&dev, &time));

    while (1)
    {
        float temp;

        vTaskDelay(pdMS_TO_TICKS(250));

        if (ds3231_get_temp_float(&dev, &temp) != ESP_OK)
        {
            printf("Could not get temperature\n");
            continue;
        }

        if (ds3231_get_time(&dev, &time) != ESP_OK)
        {
            printf("Could not get time\n");
            continue;
        }

        /* float is used in printf(). you need non-default configuration in
         * sdkconfig for ESP8266, which is enabled by default for this
         * example. see sdkconfig.defaults.esp8266
         */
        printf("%04d-%02d-%02d %02d:%02d:%02d, %.2f deg Cel\n", time.tm_year + 1900 /*Add 1900 for better readability*/, time.tm_mon + 1,
            time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec, temp);
    }
}

void app_main()
{
    ESP_ERROR_CHECK(i2cdev_init());
    xTaskCreate(ds3231_test, "ds3231_test", configMINIMAL_STACK_SIZE * 3, NULL, 5, NULL);
}

How the Code Works?

Firstly, we will start by including the necessary libraries for this project. This includes the ds3231 library to access the APIs for configuring and obtaining the current date/time and freertos libraries to create the task.

#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <ds3231.h>
#include <string.h>

ds3231_test()

Inside the ds3231_test() function, we first create an instance of i2c_dev_t struct which is used to configure and initialize the RTC module. After that we initialize all members of this struct to zero with memset.

  i2c_dev_t dev;
    memset(&dev, 0, sizeof(i2c_dev_t));
/**
 * I2C device descriptor
 */
typedef struct
{
    i2c_port_t port;         //!< I2C port number
    i2c_config_t cfg;        //!< I2C driver configuration
    uint8_t addr;            //!< Unshifted address
    SemaphoreHandle_t mutex; //!< Device mutex
    uint32_t timeout_ticks;  /*!< HW I2C bus timeout (stretch time), in ticks. 80MHz APB clock
                                  ticks for ESP-IDF, CPU ticks for ESP8266.
                                  When this value is 0, I2CDEV_MAX_STRETCH_TIME will be used */
} i2c_dev_t;

Next, the ds3231_init_desc() function initializes the ds3231 device descriptor by passing dev struct, I2C port, ESP32 SDA pin and ESP32 SCL pin respectively.

    ESP_ERROR_CHECK(ds3231_init_desc(&dev, 0, 21, 22));

Next we define the parameters of the tm struct which we define as ‘time.’ Inside it we specify the current year, month, date, hour, minute and second. After that, we call ds3231_set_time() to initialize the date and time as defined in the time structure. This function basically sets the time to RTC.

    struct tm time = {
        .tm_year = 122, // (2022 - 1900)
        .tm_mon  = 11,  // 0-based
        .tm_mday = 15,
        .tm_hour = 0,
        .tm_min  = 50,
        .tm_sec  = 10
    };
    ESP_ERROR_CHECK(ds3231_set_time(&dev, &time));

Inside the while loop, we access the current date and time using ds3231_get_time() function and the internal chip temperature using ds3231_get_temp_float() function. After every 0.25 second, we print the internal chip temperature and the current date and time on the ESP-IDF terminal by accessing each member of the time structure.

  while (1)
    {
        float temp;

        vTaskDelay(pdMS_TO_TICKS(250));

        if (ds3231_get_temp_float(&dev, &temp) != ESP_OK)
        {
            printf("Could not get temperature\n");
            continue;
        }

        if (ds3231_get_time(&dev, &time) != ESP_OK)
        {
            printf("Could not get time\n");
            continue;
        }


        printf("%04d-%02d-%02d %02d:%02d:%02d, %.2f deg Cel\n", time.tm_year + 1900 /*Add 1900 for better readability*/, time.tm_mon + 1,
            time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec, temp);
    }

app_main()

Inside the app_main() function, we will first initialize the i2c device by calling i2cdev_init(). Then we will create the ds3231 task using the xTaskCreate() function.

void app_main()
{
    ESP_ERROR_CHECK(i2cdev_init());
    xTaskCreate(ds3231_test, "ds3231_test", configMINIMAL_STACK_SIZE * 3, NULL, 5, NULL);
}

Compiling the Sketch

To flash your chip, type the following command in the serial terminal. Remember to replace the COM port with the one through which your board is connected.

idf.py -p COMX flash monitor

After the code is successfully flashed, you can view the current date and time along with the internal chip temperature on the console.

ESP32 ESP-IDF Real Time Clock using DS3231 Module Terminal

You may also like to read:

Leave a Comment