Arduino Clock

Article thumbnail

Published

arduino rtc projects

A simple alarm clock with an Arduino Nano

Overview

The Arduino Clock v2 is an open-source, easy-to-use alarm clock running on an Arduino Nano. It features a bright 4-digit seven-segment display with decimal points, large snooze button, and two ways to be powered - USB or battery.

Specifications

AttributeValue
MicrocontrollerATmega328P (on Arduino Nano)
Input Voltage5V DC via USB or 7-12V DC via input pins
Clock Speed (crystal)16MHz microcontroller, 32.768kHz DS1302
PCB Size13.3 cm × 8.3 cm (5.2 in × 3.2 in)
Clock ChipDS1302
Clock Chip Backup Power Supply3V DC via coin cell battery
Num. of Digits4
Mounting Holes4

Schematic

Arduino Clock schematic

List of Parts

All IC sockets have 300 mil pin spacing.

Download the .fzz file

PartSymbolQuantity
Arduino Nano v3Arduino Nano1
4-Digit 7-Segment Display1
MAX7219IC11
DS1302IC21
32.768kHz crystalY11
Pushbutton (normally open)S1-88
BuzzerBUZZER1
DIP24 IC socket1
DIP8 IC socket1
Green LEDD11
Female header, 15 pins1
Rounded female header, 7 pins2
47k resistorR11
220R resistorR2-32
3V coin cell holder1
9V battery snap (optional)1
SPST slide switch (optional)1
9V battery (optional)1
3V coin cell (CR2032)1
Fabricated PCB1

Installation

Solder on both IC sockets. Pay attention to the orientation and direction of the notch.
Solder on both rounded female headers (7 pins).
Solder on the battery holder, R2, and R3 (both 220R).
Solder on both female headers (15 pins).
R1 is mounted vertically to save space. Bend one of the leads 180 degrees to make it fit. Then, put it on the PCB with the actual resistor in the circle. Next, be sure to leave at least 2mm of lead space on Y1 at the top of the PCB. Solder on R1 and Y1.
Solder on D1. Make sure that the anode goes into the square pad.
Solder on S1-8 and the buzzer. Make sure that the "+" on the buzzer is on the left.
If using 9V battery: Solder the 9V battery snap to the back of the PCB. The black wire should be in the square pad. Then, attach the wires to the board with tape.
If using 9V battery: Solder the SPST slide switch to the two holes labelled "SWITCH".
Bend Y1 downward. Then, insert the Arduino Nano into the two long female headers. The USB port should be over the word "USB" on the PCB.
Insert the two ICs into their corresponding sockets. Pay attention to their orientation.
Note the small "1" on the PCB. This indicates where pin 1 of the display should be connected. Insert the display into the rounded female headers. Then, insert a 3V coin cell into the coin cell holder. The side marked with a "+" should be on top.
Use super glue to stick a 7.5 mm x 31 mm (0.3 in x 1.2 in) piece of black, rigid plastic to S6 and S7, forming the snooze bar.
If using 9V battery: Insert the 9V battery into the snap and use double-sided tape to stick it on the PCB.

Solder on both IC sockets. Pay attention to the orientation and direction of the notch.

Code

note:

You will need the DS130X and LedControl libraries for the code to compile and run properly.

// Keyboard shortcuts:
// TIME+ and ALARM+  loads alarm time
// TIME- and ALARM-  saves alarm time
// TIME+ and SLEEP   clears alarm time

#include <DS130X.h>     // RTC
#include <LedControl.h> // MAX7219
#include <EEPROM.h>     // EEPROM

// Pin definitions

// RTC Pins
#define RTC_CLK 2 // CLK pin on DS1302
#define RTC_DAT 3 // DAT pin on DS1302
#define RTC_RST 4 // RST pin on DS1302

// LED Pins
#define LED_DIN    5 // DIN pin on MAX7219
#define LED_CS     6 // LOAD pin on MAX7219
#define LED_CLK    7 // CLK pin on MAX7219
#define COLON      8 // Pin 8 on LED display
#define INDICATOR 10 // LED to indicate if alarm is on

// Button pins
#define TIME_PLUS    11
#define TIME_MINUS   12
#define SNOOZE_1     14
#define SNOOZE_2     15
#define SLEEP        16
#define ALARM_ON_OFF 17
#define ALARM_MINUS  18
#define ALARM_PLUS   19

#define BUZZER  9 // Buzzer pin

// Variables
DS1302 rtc(RTC_DAT, RTC_CLK, RTC_RST);
LedControl lc(LED_DIN, LED_CLK, LED_CS, 1);

int h, m, s; // Variables for time
int alarm_hr = 12;
int alarm_min = 0;
boolean alarm = false;
boolean pressed = false;
boolean prev = false;
boolean done = false;
int del = 250; // Milliseconds between time changes if the TIME +/- button is held down
int snooze_time = 5; // Minutes in between pressing snooze and alarm going off again

void setup() {
  // Configure digital pins
  // The pins A6/7 do not have internal pullup resistors; therefore, they are not used
  pinMode(COLON, OUTPUT);
  pinMode(INDICATOR, OUTPUT);
  pinMode(TIME_PLUS, INPUT_PULLUP);
  pinMode(TIME_MINUS, INPUT_PULLUP);
  pinMode(SNOOZE_1, INPUT_PULLUP);
  pinMode(SNOOZE_2, INPUT_PULLUP);
  pinMode(SLEEP, INPUT_PULLUP);
  pinMode(ALARM_ON_OFF, INPUT_PULLUP);
  pinMode(ALARM_MINUS, INPUT_PULLUP);
  pinMode(ALARM_PLUS, INPUT_PULLUP);
  pinMode(BUZZER, OUTPUT);

  // RTC setup
  rtc.setHalt(false); // Set the RTC to run mode
  rtc.setWriteProtect(false); // Disable write protection

  // MAX7219 setup
  const int displayBrightness = 8;
  lc.shutdown(0, false); // Turn off power saving and enable display
  lc.setIntensity(0, displayBrightness); // Set brightness (Possible values 0 to 15)
  lc.clearDisplay(0); // Clear the display
}

void loop() {
  // Get h, m, and s
  h = rtc.read(HOURS);
  m = rtc.read(MINUTES);
  s = rtc.read(SECONDS);

  check12h(); // Check the current time and adjust if necessary
  displayNum(getTens(h), getOnes(h), getTens(m), getOnes(m)); // Display the time

  if (not(digitalRead(SLEEP)) && done) {
    alarm = false; // no alarm
    digitalWrite(BUZZER, LOW);
  }

  if (((not(digitalRead(SNOOZE_1))) || (not(digitalRead(SNOOZE_2)))) && done) {
    // Snooze pressed, add 5 minutes
    alarm_min += snooze_time;
    check12hAlarm();
    digitalWrite(BUZZER, LOW); // turn buzzer off
    done = false; // not done yet
  }

  // Check if the Alarm On/Off button has been pressed
  pressed = !digitalRead(ALARM_ON_OFF);
  // Only adtivate/deactivate alarm once button is released
  if (pressed != prev) {
    if (pressed) {
      alarm = !alarm;
    }
  }
  prev = pressed;
  digitalWrite(INDICATOR, alarm); // Indicator to tell if alarm is on

  // Alarm setting/checking
  setAlarm();
  checkAlarm();

  // Flash colon
  if ((s % 2) == 0) {
    // Turn on the colon if the amount of seconds is even
    digitalWrite(COLON, HIGH);
  } else {
    // Turn off the colon if the amount of seconds is odd
    digitalWrite(COLON, LOW);
  }

  if (not(digitalRead(TIME_PLUS))) {
    // Time+ button is pressed
    // Check for keyboard shortcut
    if (not(digitalRead(ALARM_PLUS))) {
      // Load alarm time
      loadAlarmTime();
    }
    else if (not(digitalRead(SLEEP))) {
      // Clear alarm time (these two buttons are far apart so no accidental clearing)
      clearAlarmTime();
    }
    else {
      // Increment minutes
      m += 1; // Increment m
      adjustTime();
      rtc.setTime(h, m, 0); // Set the new time
      delay(del); // Delay between changes if the button is held down
    }
  }

  if (not(digitalRead(TIME_MINUS))) {
    // Time- button is pressed
    // Check for keyboard shortcut
    if (not(digitalRead(ALARM_MINUS))) {
      // Save alarm time
      saveAlarmTime();
    }
    else {
      m -= 1; // Decrement m
      adjustTime();
      rtc.setTime(h, m, 0); // Set the new time
      delay(del); // Delay between changes if the button is held down
    }
  }
}

int getTens(int num) {
  return int(num / 10);
}

int getOnes(int num) {
  return num % 10;
}

void displayNum(int d1, int d2, int d3, int d4) {
  lc.setDigit(0, 0, d1, false);
  lc.setDigit(0, 1, d2, false);
  lc.setDigit(0, 2, d3, false);
  lc.setDigit(0, 3, d4, false);
}

void check12h() {
  if (h > 12) {
    h -= 12;
  }
  else if (h == 0) {
    h = 12;
  }
}

void check12hAlarm() {
  if (alarm_hr > 12) {
    alarm_hr -= 12;
  }
  else if (alarm_hr == 0) {
    alarm_hr = 12;
  }
}

void setAlarm() {
  if (alarm) {
    if (!digitalRead(ALARM_PLUS) && !done) {
      alarm_min++;
      adjustAlarm();
      displayNum(getTens(alarm_hr), getOnes(alarm_hr), getTens(alarm_min), getOnes(alarm_min));
      delay(del + 150); // more time to see the set alarm before displaying current time
    }

    if (!digitalRead(ALARM_MINUS) && !done) {
      alarm_min--;
      adjustAlarm();
      displayNum(getTens(alarm_hr), getOnes(alarm_hr), getTens(alarm_min), getOnes(alarm_min));
      delay(del + 150);
    }
  }
}

void checkAlarm() {
  if ((((h == alarm_hr) && (m == alarm_min)) || done) && (alarm && !checkAlarmButtonPress())) {
    done = true;
    if ((s % 2) == 0) { // Turn on the alarm if the amount of seconds is even
      digitalWrite(BUZZER, HIGH);
    }
    else { // Turn off the alarm if the amount of seconds is odd
      digitalWrite(BUZZER, LOW);
    }
  }
}

boolean checkAlarmButtonPress() {
  return !digitalRead(ALARM_PLUS) || !digitalRead(ALARM_MINUS);
}

void adjustTime() {
  if (m == 60) {
    // Overflow in minutes, set minutes to 0 and increment hour
    m = 0;
    h++;
  }
  if (m < 0) {
    // Underflow in minutes, set minutes to 59 and decrement hour
    m = 59;
    h--;
  }
  check12h(); // adjust if necessary
}

void adjustAlarm() {
  if (alarm_min == 60) {
    // Overflow in minutes, set minutes to 0 and increment hour
    alarm_min = 0;
    alarm_hr++;
  }
  if (alarm_min == -1) {
    // Underflow in minutes, set minutes to 59 and decrement hour
    alarm_min = 59;
    alarm_hr--;
  }
  check12hAlarm(); // adjust if necessary
}

void saveAlarmTime() {
  // Index 0 is hour
  // Index 1 is minute
  EEPROM.update(0, alarm_hr); // Only write if it is a new time
  EEPROM.update(1, alarm_min);
}

void loadAlarmTime() {
  alarm_hr = EEPROM.read(0);
  alarm_min = EEPROM.read(1);
}

void clearAlarmTime() {
  // Reset alarm values to default (12:00)
  EEPROM.update(0, 12); // hour
  EEPROM.update(1, 0);  // minute
}

Usage

Power-On: Turn the clock on. If you are using a 9V battery, slide the switch to the "ON" position. If you are using USB power, connect the USB port to a 5V USB supply (ex. a computer's USB port) via a mini-USB cable.

Time Adjustments: To adjust the displayed time, use the TIME+/- buttons (S1 and S2). TIME+ will add 1 to the minutes, and TIME- will subtract 1 from the minutes. The clock uses 12-hour time. Even when you turn it off, the clock will still be recording the time because the DS1302 has the coin cell as a backup power supply.

Setting an Alarm: To set an alarm, press the ALARM ON/OFF button (S8). D1 will turn on. The default alarm time is 12:00. Adjusting the alarm time is similar to adjusting the normal time. Use the ALARM+/- buttons (S3 and S4).

Turning the Alarm Off: When the set alarm time is reached, the buzzer will beep continuously. Use the SLEEP button (S5) to turn the alarm off, or use the snooze bar (S6 and S7) to make the clock wait 5 minutes before beeping again. The snooze bar will work if either button is pressed. It will also work if both buttons are pressed.

Replacing the Coin Cell: To replace the 3V coin cell, first turn the clock off. Then, use pliers to bend the small metal tab on the left of the holder to release the coin cell. After removing the coin cell, put a new one in by putting the right edge in first and then pushing the left end down to secure it.