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
- 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
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
| Pin | What it's for |
|---|---|
| Vin | supply 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) |
| GND | ground shared with the board |
| SCL, SDA | I²C bus, with 10 kΩ pull-ups to Vin already on the module |
| BAT | battery voltage, to read it or to use another battery instead of the CR1220 |
| 32K | 32.768 kHz square wave; open-drain output (it needs a pull-up) |
| SQW | alarms, or a square wave at 1 Hz, 1.024, 4.096 or 8.192 kHz; open-drain |
| RST | reset, 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 |
| Supply | VCC 2.3–5.5 V; battery 2.3–5.5 V |
| Current on battery | typical 0.84 µA, maximum 3 µA (at 3.63 V, I²C bus idle) |
| Temperature sensor | 0.25 °C resolution, ±3 °C accuracy; it is there for compensation, not to be used as a thermometer |
| Bus | I²C at 100 or 400 kHz, fixed address 0x68 |
| Alarms | two: the first down to the seconds, the second down to the minutes |
| Variants | DS3231M: MEMS oscillator, ±5 ppm, SQW at 1 Hz only |
How to wire 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.
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 happens | Why | Fix |
|---|---|---|
| “DS3231 not found” | SDA and SCL swapped, no shared ground, or on the ESP32 pins other than 21 and 22 were used | check the wires again; for other pins use Wire.begin(sda, scl) before rtc.begin() |
| After every reset the time goes back | rtc.adjust(...) called every time, not only when lostPower() is true | put the setting inside if (rtc.lostPower()), as in the example |
| Switched off and on again, it starts from 2000 | battery missing or flat | fit a new CR1220 and set the time again |
| The alarm doesn't fire | SQW still in square-wave mode: setAlarm1() returns false | first writeSqwPinMode(DS3231_OFF), then the alarm |
| The alarm fires only once | the alarm flag was not cleared | clearAlarm(1) after every alarm |
| Below zero the temperature is nonsense | in RTClib 2.1.4 getTemperature() reads negative values wrongly (open issue no. 267) | for room temperature use a dedicated sensor |
| Another module stops responding | two devices with the same address 0x68 on the same bus | change the other module's address, if possible, or use an I²C multiplexer |
Useful products
Other guides
- Resistor colour code and tolerances
- N-channel and P-channel MOSFETs
- Bipolar transistor (BJT) or MOSFET?
- Relays: how to drive them and how much current they draw
- THT and SMD/SMT components
- XIAO board pinouts
- LEDs: forward voltage and series resistor
- The multimeter: measuring voltage and current
- Wemos D1 mini: first steps
- MOSFET module with Arduino and ESP32
- Relay module with Arduino and ESP32
- 3D printing filaments: PLA, PETG and fibre-filled versions
- Analog and digital: the differences
- ADS1115: 16-bit converter with Arduino and ESP32
- From bits to real values: converting sensor readings
- Linear or switching regulator: which one to choose
- Step-down, step-up and buck-boost: choosing the right module
- Voltage divider: reading voltages higher than 5 V or 3.3 V
- Pull-up, pull-down and bounce: wiring a button properly
- 3.3 V and 5 V logic levels: getting an ESP32 and an Arduino to talk
- The I²C bus: addresses, pull-ups and the scanner
- Lithium batteries: voltages, charging and protection
- ESP32 power consumption and deep sleep: how long the battery lasts
- Temperature sensors compared
- ESP32: which pins to use and which to avoid
- Getting started with the ESP32 in Arduino IDE 2 and PlatformIO
- SSD1306 OLED display and 16×2 I²C LCD with Arduino and ESP32
- PIR motion sensor and HC-SR04 ultrasonic sensor
- Date and time from the Internet with the ESP32: NTP, Italian time zone and daylight saving time
- MicroSD data logger: saving measurements to CSV
- Solar-powered ESP32: panel, charger and the numbers for Italy
- Web server on the ESP32: control a relay from your phone
- Your first LED: wire it up and make it blink
- The button: let Arduino decide
- The potentiometer: turn a knob and read its value
- Soft light: adjusting the brightness of an LED
- The buzzer: playing notes and melodies
- The traffic light: three LEDs in sequence
- The thermometer: reading the temperature with the TMP36
- The servo motor: moving an arm with Arduino
- The night light: the photoresistor and your first decision
- The relay: switching on a light bulb with Arduino
- The LCD display: making Arduino write
- The electronic dice: your first complete project
- The RGB LED: every color from three lights
- The joystick: two-axis control with a shield
- The LED ring: twelve colors from a single pin
- The DC motor: driving it with a MOSFET
- Soil moisture: the plant that asks for water
- The stepper motor: turn exactly as far as you want
- The parking sensor: faster and faster beeps
- The 7-segment display: big, bright numbers
- Automatic watering: the plant that waters itself
- The keypad: a lock with a secret code
- Networked weather station: values and chart on your phone
- A Telegram bot on the ESP32: your home from your phone
- MQTT with the ESP32: the language of connected devices
- Updating the ESP32 over Wi-Fi (OTA)
- ESP-NOW: two ESP32 boards talking without a router
- WebSocket with the ESP32: the LED that follows the slider
- Readings on a Google sheet, with deep sleep
- WiFiManager: choose the Wi-Fi from your phone
- Bluetooth LE with the ESP32: the phone without a network
- The camera in your browser with the ESP32-S3
- A portable Bluetooth speaker you build yourself
- Bluetooth desktop speakers, 2 × 20 W
- Stereo MP3 player with two buttons
- The shelf that plays music: a Visaton exciter
- How to solder well
- How much does it draw? Current and power with the INA219
- Load-cell scale with the HX711
- CO2 traffic light: when to open the windows
- RFID card door opener
- GPS: track logger on microSD
- LoRa: how far does it go? A range test with two boards
- Battery weather sign with an e-paper display
- Web radio with an ESP32 and an I2S amplifier
- Getting started with the Raspberry Pi Pico 2 in MicroPython
- Your first amplifier: the LM386 and a speaker
- Talking Arduino: the DFR0760 speech synthesis module
- The spectrum of your music on an LED matrix
- Drawing with 64 LEDs: the 8×8 matrix with the MAX7219
- A real voltage: the 12-bit MCP4725 DAC
- The heartbeat on Arduino: an ECG with the AD8232