Home

Guide no. 16

Date and time always right, even with the board switched off: how to wire it, how to set it and how to use it as an alarm clock. Examples on the Adafruit 3013, redrawn from the official design files.

By Ada Intelisano · informaticait.it technical team

Download the PDF (A4, 6 pages) Free to print and share, without changes — CC BY-ND 4.0
In short
  • Arduino and the ESP32 don't know what time it is: when they are switched off they start again from zero. The DS3231 keeps time on its own and, with its backup battery, keeps going even with the board off.
  • It is one of the most accurate in its class: crystal and temperature compensation are inside the chip, ±2 ppm between 0 and 40 °C, which is about one minute a year.
  • It connects with four wires on the I²C bus (address 0x68) and you use it with the Adafruit RTClib library.
  • With the SQW output it becomes an alarm clock: at a set time it pulls a pin low and the board notices.

Why you need an external clock

Boards count the milliseconds since you switched them on (millis()) and that's all: a data logger, an irrigation timer or a clock with a display needs to know the real date and time. The ESP32 can get them from the internet, but only if Wi-Fi is available. The DS3231 keeps them in a chip with its own battery: seconds, minutes, hours, day, month and year, with leap years already taken care of (the datasheet guarantees it up to 2100).

How accurate it is

Cheap clocks use an external crystal whose speed changes with temperature. The DS3231 has the crystal and temperature compensation in the same package: it measures the temperature every 64 seconds and corrects itself. 2 ppm means 2 seconds per million: do the math and it comes to about 5 seconds a month and a little over a minute a year. Over the whole range from −40 to +85 °C, the datasheet gives ±2 minutes a year. On top of that comes crystal aging, ±1 ppm in the first year.

What the Adafruit 3013 module looks like

DS3231DS3231VinGNDSCLSDABAT32KSQWRSTPrecision I2C RTCCR1220+component sidebottom side: CR1220 batteryVinGNDSCLSDABAT32KSQWRSTinformaticait.it

Drawn to scale from the Adafruit design files: 22.9 × 17.8 mm, eight pins at 2.54 mm pitch. The holder for the CR1220 battery (not included) is on the underside of the board. The 10 kΩ pull-up resistors on SDA and SCL are already fitted.

The pins

PinWhat it's for
Vinsupply from 2.3 to 5.5 V: 5 V with the Arduino UNO, 3.3 V with the ESP32 (there is no regulator on the module)
GNDground shared with the board
SCL, SDAI²C bus, with 10 kΩ pull-ups to Vin already on the module
BATbattery voltage, to read it or to use another battery instead of the CR1220
32K32.768 kHz square wave; open-drain output (it needs a pull-up)
SQWalarms, or a square wave at 1 Hz, 1.024, 4.096 or 8.192 kHz; open-drain
RSTreset, active low; it already has its own 50 kΩ pull-up inside the chip

DS3231 figures (datasheet)

Value
Accuracy±2 ppm from 0 to +40 °C; ±3.5 ppm outside that range (up to +70 °C for the DS3231S, from −40 to +85 °C for the DS3231SN)
Aging±1 ppm the first year, ±5 ppm over ten years
SupplyVCC 2.3–5.5 V; battery 2.3–5.5 V
Current on batterytypical 0.84 µA, maximum 3 µA (at 3.63 V, I²C bus idle)
Temperature sensor0.25 °C resolution, ±3 °C accuracy; it is there for compensation, not to be used as a thermometer
BusI²C at 100 or 400 kHz, fixed address 0x68
Alarmstwo: the first down to the seconds, the second down to the minutes
VariantsDS3231M: MEMS oscillator, ±5 ppm, SQW at 1 Hz only

How to wire it

Arduino UNO + DS3231 (Adafruit 3013)SCLSDAAREFGND131211109876543210IOREFRESET3.3V5VGNDGNDVINDIGITAL (PWM ~)POWERANALOG INUNODS3231DS3231VinGNDSCLSDABAT32KSQWRSTPrecision I2C RTCAdafruit 3013Vin → 5VGND → GNDSCL → SCLSDA → SDASQW → 2 (alarm)informaticait.it

With the Arduino UNO you can take I²C from the SCL and SDA pins next to AREF (they are the same as A5 and A4). The purple wire on SQW is only needed for the alarm in the second example.

ESP32-DevKitC + DS3231 (Adafruit 3013)ESP32-WROOM-323V3GNDEN23VP22VNTX34RX352132GND3319251826527171416124GND0132D215D3D1CMDD05VCLKDS3231DS3231VinGNDSCLSDABAT32KSQWRSTPrecision I2C RTCAdafruit 3013Vin → 3V3GND → GNDSCL → GPIO 22SDA → GPIO 21SQW → GPIO 27 (alarm)informaticait.it

With the ESP32 the module must be powered at 3.3 V: the module's pull-ups bring SDA and SCL up to Vin, and the ESP32 pins do not accept 5 V. The default I²C pins are GPIO 21 (SDA) and GPIO 22 (SCL).

Example 1: clock with the chip temperature

// RTC DS3231: on first power-up (or if the battery has run flat) it sets the computer's time,
// then every second it prints the date, the time and the chip temperature.
// Same sketch for Arduino UNO (SDA = A4, SCL = A5) and ESP32 (SDA = GPIO 21, SCL = GPIO 22).
// Library: Adafruit RTClib.
#include <RTClib.h>

RTC_DS3231 rtc;

void setup() {
  Serial.begin(115200);
  if (!rtc.begin()) {
    Serial.println("DS3231 not found: check VCC, GND, SDA and SCL");
    while (true) delay(1000);
  }
  if (rtc.lostPower()) {
    // the clock has stopped: set it to the time the sketch was compiled
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    Serial.println("Time set from the computer");
  }
}

void loop() {
  DateTime t = rtc.now();
  char line[24];
  snprintf(line, sizeof(line), "%02d/%02d/%04d %02d:%02d:%02d",
           t.day(), t.month(), t.year(), t.hour(), t.minute(), t.second());
  Serial.print(line);
  Serial.print("   chip temperature: ");
  Serial.print(rtc.getTemperature(), 2);
  Serial.println(" C");
  delay(1000);
}

The first time, the clock takes the time when you compiled the sketch (so it lags by the few seconds it takes to upload it). After that it doesn't touch it again: lostPower() is true only if the oscillator has stopped, for example at first power-up or with a flat or missing battery.

Example 2: alarm once a minute

// RTC DS3231 as an alarm clock: alarm 1 fires every minute (when the seconds go back to 0)
// and pulls the SQW/INT pin low; the pin is connected to an interrupt input on the board.
// Same sketch for Arduino UNO and ESP32. Library: Adafruit RTClib.
#include <RTClib.h>

#ifdef ARDUINO_ARCH_ESP32
const int PIN_SQW = 27;        // ESP32: any GPIO that accepts interrupts
#else
const int PIN_SQW = 2;         // UNO: external interrupt INT0
#endif

RTC_DS3231 rtc;
volatile bool wakeUp = false;

#ifdef ARDUINO_ARCH_ESP32
void IRAM_ATTR onAlarm() { wakeUp = true; }
#else
void onAlarm() { wakeUp = true; }
#endif

void setup() {
  Serial.begin(115200);
  if (!rtc.begin()) {
    Serial.println("DS3231 not found");
    while (true) delay(1000);
  }
  if (rtc.lostPower()) rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

  pinMode(PIN_SQW, INPUT_PULLUP);              // SQW/INT is open-drain: it needs the pull-up
  attachInterrupt(digitalPinToInterrupt(PIN_SQW), onAlarm, FALLING);

  rtc.writeSqwPinMode(DS3231_OFF);             // SQW/INT used as the alarm output
  rtc.disableAlarm(2);
  rtc.clearAlarm(1);
  rtc.clearAlarm(2);
  // fires when the seconds are 0, that is once a minute
  rtc.setAlarm1(DateTime(0, 0, 0, 0, 0, 0), DS3231_A1_Second);
  Serial.println("Alarm set: one message at the start of every minute");
}

void loop() {
  if (wakeUp) {
    wakeUp = false;
    rtc.clearAlarm(1);                          // releases SQW/INT, ready for the next minute
    DateTime now = rtc.now();
    Serial.print("Wake up! It's ");
    Serial.print(now.hour());
    Serial.print(':');
    if (now.minute() < 10) Serial.print('0');
    Serial.println(now.minute());
  }
}

SQW is open-drain: it pulls the pin low when the alarm fires and then lets it go. That is why you need a pull-up (here the board's internal one, INPUT_PULLUP) and you have to call clearAlarm(1), otherwise the pin stays low. With DS3231_A1_Hour or DS3231_A1_Date the alarm becomes daily or monthly.

The battery

The Adafruit 3013 takes a 3 V lithium CR1220; Adafruit estimates it lasts five years or more. CR2032 and CR1220 cells are not rechargeable: Energizer gives a maximum reverse current of 1 µA, and Panasonic asks for a diode and a series resistor if there is any risk of something charging them. Some cheap modules with a CR2032 holder have a circuit on the board that charges it from Vin: before fitting a CR2032 to a module you don't know, check the seller's schematic. The rechargeable versions (LIR2032) are a different thing: 3.7 V and charged at 4.2 V.

Common mistakes

What happensWhyFix
“DS3231 not found”SDA and SCL swapped, no shared ground, or on the ESP32 pins other than 21 and 22 were usedcheck the wires again; for other pins use Wire.begin(sda, scl) before rtc.begin()
After every reset the time goes backrtc.adjust(...) called every time, not only when lostPower() is trueput the setting inside if (rtc.lostPower()), as in the example
Switched off and on again, it starts from 2000battery missing or flatfit a new CR1220 and set the time again
The alarm doesn't fireSQW still in square-wave mode: setAlarm1() returns falsefirst writeSqwPinMode(DS3231_OFF), then the alarm
The alarm fires only oncethe alarm flag was not clearedclearAlarm(1) after every alarm
Below zero the temperature is nonsensein RTClib 2.1.4 getTemperature() reads negative values wrongly (open issue no. 267)for room temperature use a dedicated sensor
Another module stops respondingtwo devices with the same address 0x68 on the same buschange the other module's address, if possible, or use an I²C multiplexer
Use cases. Data logger with date and time on an SD card · irrigation at set times · clock with a display · battery-powered board that sleeps and is woken by the alarm on the SQW pin · backup time for an ESP32 when there is no Wi-Fi.
Sources: Analog Devices, DS3231 datasheet (19-5170 Rev 10) and DS3231M datasheet (19-5312 Rev 7) · Adafruit, guide “Adafruit DS3231 Precision RTC Breakout” and Eagle design files (GitHub, Adafruit-DS3231-Precision-RTC-Breakout-PCB) · Adafruit, RTClib library 2.1.4 and the ds3231 and DS3231_alarm examples · Espressif, Arduino-ESP32 documentation, I2C (default pins) · Energizer, CR2032 data sheet and lithium coin cell handbook; Panasonic, lithium battery technical handbook

Useful products

Other guides

All our guides →
Loading...
Back to top