A school bell, a timed irrigation pump or a shop’s sign light needs to switch at a real time of day, even after a power cut. The DS3231 keeps that time on its coin cell and can also wake the Mega with an alarm signal.
The I2C lines go to SDA/SCL (pins 20/21) and the alarm output SQW to pin 2, an interrupt pin. The sketch sets a daily alarm for 06:30 and prints a message when it fires.
Wiring
| Arduino Mega 2560 | DS3231 real-time clock (RTC) module | Wire | Notes |
|---|---|---|---|
| 5V | VCC | red | |
| GND | GND | black | |
| SDA | SDA | blue | Same signal as pin 20. |
| SCL | SCL | yellow | Same signal as pin 21. |
| D2 | SQW | green | Open-drain alarm output; the internal pull-up is enabled in the sketch. |
2.3–5.5 V part, 5 V board. The module pulls SDA/SCL up to its own VCC. On a 3.3 V board, power it from 3.3 V — the DS3231 keeps excellent time at 3.3 V.
Board quirks
- I2C on the Mega is pins 20/21 (or SDA/SCL by AREF), not A4/A5.
- SQW is open-drain: it only pulls LOW.
INPUT_PULLUPon pin 2 gives it a high level. Pin 2 is one of the Mega’s six external-interrupt pins (2, 3, 18, 19, 20, 21). - The alarm keeps SQW LOW until
clearAlarm(1)is called — forget it and the next alarm never fires. - On ZS-042 modules with a CR2032, disable the coin-cell charging circuit (remove the resistor near the diode) or use an LIR2032.
- Pumps and bells on 230 V go through a contactor or suitable relay, wired by an electrician.
Code
// DS3231 RTC on an Arduino Mega 2560 with a daily alarm on the SQW pin (school bell, pump timer).
// VCC → 5V, GND → GND, SDA → SDA (20), SCL → SCL (21), SQW → D2 (interrupt, open-drain, pulled up).
#include <Wire.h>
#include <RTClib.h>
RTC_DS3231 rtc;
const uint8_t SQW_PIN = 2;
const uint8_t ALARM_HOUR = 6, ALARM_MIN = 30; // 06:30 every day
volatile bool alarmFlag = false;
void onAlarm() { alarmFlag = true; }
void setup() {
Serial.begin(115200);
if (!rtc.begin()) {
Serial.println(F("DS3231 not found - check SDA/SCL (20/21)"));
for (;;) {}
}
if (rtc.lostPower()) rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
rtc.disable32K();
rtc.writeSqwPinMode(DS3231_OFF); // SQW becomes the alarm interrupt output
rtc.clearAlarm(1);
rtc.clearAlarm(2);
rtc.disableAlarm(2);
rtc.setAlarm1(DateTime(2000, 1, 1, ALARM_HOUR, ALARM_MIN, 0), DS3231_A1_Hour); // hh:mm:ss match
pinMode(SQW_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(SQW_PIN), onAlarm, FALLING);
Serial.print(F("Alarm set for "));
Serial.print(ALARM_HOUR);
Serial.print(':');
Serial.println(ALARM_MIN);
}
void loop() {
if (alarmFlag) {
alarmFlag = false;
rtc.clearAlarm(1); // releases SQW so the next alarm can fire
Serial.println(F("*** ALARM - switch the bell / pump here ***"));
}
static unsigned long last = 0;
if (millis() - last >= 10000) {
last = millis();
char buf[] = "DD-MM-YYYY hh:mm:ss";
Serial.println(rtc.now().toString(buf));
}
}// DS3231 RTC on an Arduino Mega 2560 with a daily alarm on the SQW pin (school bell, pump timer).
// VCC → 5V, GND → GND, SDA → SDA (20), SCL → SCL (21), SQW → D2 (interrupt, open-drain, pulled up).
#include <Wire.h>
#include <RTClib.h>
RTC_DS3231 rtc;
const uint8_t SQW_PIN = 2;
const uint8_t ALARM_HOUR = 6, ALARM_MIN = 30; // 06:30 every day
volatile bool alarmFlag = false;
void onAlarm() { alarmFlag = true; }
void setup() {
Serial.begin(115200);
if (!rtc.begin()) {
Serial.println(F("DS3231 not found - check SDA/SCL (20/21)"));
for (;;) {}
}
if (rtc.lostPower()) rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
rtc.disable32K();
rtc.writeSqwPinMode(DS3231_OFF); // SQW becomes the alarm interrupt output
rtc.clearAlarm(1);
rtc.clearAlarm(2);
rtc.disableAlarm(2);
rtc.setAlarm1(DateTime(2000, 1, 1, ALARM_HOUR, ALARM_MIN, 0), DS3231_A1_Hour); // hh:mm:ss match
pinMode(SQW_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(SQW_PIN), onAlarm, FALLING);
Serial.print(F("Alarm set for "));
Serial.print(ALARM_HOUR);
Serial.print(':');
Serial.println(ALARM_MIN);
}
void loop() {
if (alarmFlag) {
alarmFlag = false;
rtc.clearAlarm(1); // releases SQW so the next alarm can fire
Serial.println(F("*** ALARM - switch the bell / pump here ***"));
}
static unsigned long last = 0;
if (millis() - last >= 10000) {
last = millis();
char buf[] = "DD-MM-YYYY hh:mm:ss";
Serial.println(rtc.now().toString(buf));
}
}Library: RTClib (Adafruit) — compiled with RTClib 2.1.4.
Expected serial output
Alarm set for 6:30
03-11-2026 06:29:52
*** ALARM - switch the bell / pump here ***Alarm set for 6:30 03-11-2026 06:29:52 *** ALARM - switch the bell / pump here ***
Common errors
Alarm never fires
SQW not set to alarm mode (writeSqwPinMode(DS3231_OFF)), no pull-up on pin 2, or the previous alarm was not cleared.
DS3231 not found
Module wired to A4/A5 instead of 20/21. Full fix.