Learn

An Arduino alarm clock with a DS1302

Intermediate

Or see every lesson.

  • 465parts on the bench
  • 43boards running now
  • 1.00xreal time, on every board
Arduino Uno: a DS1302 alarm clocklive0.000 s 0.00x
Press Run, wait for 7:00, then press the button
A DS1302 on pins 4, 5 and 2, a TM1637, a piezo and a button, on a simulated ATmega328P. The alarm goes off ten seconds after Run.

An Arduino forgets the time every time it loses power. The DS1302 is a cheap clock chip that does not: it counts seconds on its own watch crystal, keeps going on a coin cell, and gives the time back over three wires. It is the clock module in many starter kits, and a good first real-time clock.

The circuit above is an Uno with a DS1302, a TM1637 display, a piezo and a button: an alarm clock. The clock module starts at 6:59:50, so ten seconds after you press Run it reaches 7:00 and the alarm beeps until you press the button. It runs in your browser on the sketch below.

What you need

  • An Arduino Uno
  • A DS1302 clock module with its coin cell
  • A 4-digit TM1637 display
  • A passive piezo and a pushbutton
  • A half-size breadboard and jumper wires

Wiring

Part and pinGoes toNote
DS1302 DATPin 4The data line, both ways
DS1302 CLKPin 5The clock
DS1302 RSTPin 2The enable, sometimes labeled CE
DS1302 VCC and GND5 V and GND
TM1637 CLK and DIOPins 7 and 6
PiezoPin 8Other leg to GND
ButtonPin 3Other side to GND

Three wires, but not SPI or I2C

The DS1302 has its own three-wire bus: RST goes high to start a transfer, CLK clocks the bits, and DAT carries them in both directions. It is not I2C, so it does not go on A4 and A5; any three digital pins work. The Rtc by Makuna library handles the protocol, through a small helper called ThreeWire.

ThreeWire myWire(4, 5, 2) names the pins in a fixed order: data, clock, then enable. Getting that order wrong is the most common reason a DS1302 reads nonsense.

Starting the clock, and setting the time

Rtc.Begin() sets up the pins. A new chip can come up write-protected or with its clock halted, so the sketch checks both: GetIsWriteProtected() and SetIsWriteProtected(false), then GetIsRunning() and SetIsRunning(true). If the clock was halted, the serial monitor says "clock was stopped: starting it".

To set the time on a real module, the library’s example uses the moment the sketch was compiled: RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__), then Rtc.SetDateTime(compiled) if the clock is behind it. Do that once, or only when the clock is behind, never on every start-up, or every reset puts the clock back to the compile time.

In Mokxi the module starts at the time in its start property every run, here 6:59:50, and has no coin cell. Press S on the module to set it to your computer’s clock.

The alarm, step by step

loop() calls Rtc.GetDateTime() and, when the second changes, shows the hours and minutes on the TM1637 with the colon on every even second, and prints the time. Then it checks for 7:00. At 7:00:00 it prints "alarm!" and starts ringing: four 80 millisecond beeps at 2000 Hz, a rest, and again, checking the button between beeps. Press it and the sketch prints "alarm off".

doneToday stops the alarm starting again in the same minute after you turn it off. It is cleared once the minute is over, so the alarm is ready for the next day.

The alarm clock sketch
#include <ThreeWire.h>
#include <RtcDS1302.h>
#include <TM1637Display.h>

const int PIEZO = 8;
const int BUTTON = 3;
const uint8_t ALARM_HOUR = 7;
const uint8_t ALARM_MINUTE = 0;

ThreeWire myWire(4, 5, 2);        // DAT, CLK, RST
RtcDS1302<ThreeWire> Rtc(myWire);
TM1637Display display(7, 6);      // CLK, DIO

bool ringing = false;
bool doneToday = false;
uint8_t lastSecond = 99;

void setup() {
  Serial.begin(115200);
  pinMode(BUTTON, INPUT_PULLUP);
  display.setBrightness(0x0f);
  Rtc.Begin();
  if (Rtc.GetIsWriteProtected()) Rtc.SetIsWriteProtected(false);
  if (!Rtc.GetIsRunning()) {
    Serial.println("clock was stopped: starting it");
    Rtc.SetIsRunning(true);
  }
}

void loop() {
  RtcDateTime now = Rtc.GetDateTime();
  if (now.Second() != lastSecond) {
    lastSecond = now.Second();
    uint8_t colon = (now.Second() % 2 == 0) ? 0b01000000 : 0;
    display.showNumberDecEx(now.Hour() * 100 + now.Minute(), colon, true);
    Serial.print(now.Hour());
    Serial.print(':');
    if (now.Minute() < 10) Serial.print('0');
    Serial.print(now.Minute());
    Serial.print(':');
    if (now.Second() < 10) Serial.print('0');
    Serial.println(now.Second());
  }

  bool alarmTime = now.Hour() == ALARM_HOUR && now.Minute() == ALARM_MINUTE;
  if (alarmTime && !doneToday && !ringing) {
    ringing = true;
    Serial.println("alarm!");
  }
  if (!alarmTime) doneToday = false;

  if (ringing) {
    // Four short beeps, then a rest, until the button.
    for (int i = 0; i < 4 && ringing; i++) {
      tone(PIEZO, 2000, 80);
      delay(150);
      if (digitalRead(BUTTON) == LOW) ringing = false;
    }
    delay(300);
    if (digitalRead(BUTTON) == LOW) ringing = false;
    if (!ringing) {
      noTone(PIEZO);
      doneToday = true;
      Serial.println("alarm off");
    }
  } else {
    delay(50);
  }
}

DS1302 or DS3231?

The DS1302 runs on an ordinary watch crystal, so its accuracy depends on the crystal and the temperature; drift of a minute or so a month is common. The DS3231 corrects its crystal for temperature and stays within about a minute a year, and it talks I2C. For a clock you set once and forget, the DS3231 is worth the extra cost. For learning, or a clock you can reset now and then, the DS1302 is fine.

The DS1302 has one extra: 31 bytes of RAM that the coin cell keeps alive, which the library reads and writes with GetMemory() and SetMemory().

Common mistakes

The pins in the wrong order in ThreeWire. It is data, clock, enable. Swap the DAT and CLK wires in this circuit and the clock reads "0:00:00" and never moves.

Wiring it to the I2C pins. Any three pins work, but they have to match ThreeWire.

Setting the time in setup() on every start-up, which resets the clock whenever the board restarts.

A halted or write-protected chip. A new DS1302 may not be running; check GetIsRunning() and start it, as this sketch does.

Turning on the trickle charger with an ordinary CR2032 in the module. Only a rechargeable cell should be charged.

Questions

How do I connect a DS1302 to an Arduino Uno?

VCC to 5 V, GND to GND, and DAT, CLK and RST to any three digital pins, here 4, 5 and 2. Name them in the same order in ThreeWire myWire(DAT, CLK, RST).

How do I set the time on a DS1302?

With the Rtc by Makuna library, build an RtcDateTime and call Rtc.SetDateTime(). The usual trick is RtcDateTime(__DATE__, __TIME__), the moment the sketch was compiled, set only when the clock is behind it.

Why does my DS1302 show the wrong time or not count?

Usually the pin order in ThreeWire is wrong, the clock is halted (call SetIsRunning(true)), or it is write-protected (call SetIsWriteProtected(false)). A flat coin cell also loses the time when the power is off.

Which is better, the DS1302 or the DS3231?

The DS3231 is far more accurate because it corrects for temperature, and it uses I2C. The DS1302 is cheaper and fine for learning or for a clock you reset now and then.

Build this for real

Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.