Built-in project · Arduino Uno R3

Alarm clock with a timer on a four-digit display, on an Arduino Uno

A DS1307 keeps the time on a four-digit display driven by a 74HC595 (8, 13, 12). Four buttons set a timer and an alarm kept in EEPROM; a DHT11 on 11 adds the weather. The whole build, an Arduino Uno and 16 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Advanced Runs in your browser. Free, and no account needed.

The parts retail for about $43. On Mokxi they're free.

Alarm clock with a timer on a four-digit display · Arduino Uno R3live0.000 s 0.00x
Press the left button for a minute, the middle one to start
The circuit itself, running here on the simulator. Press what can be pressed; click anything else to open it in the editor.

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Uno R3
// Alarm clock with a kitchen timer, a calendar and a thermometer, all on one
// four-digit display.
//
//   74HC595 DS -> pin 8    SHCP -> pin 13    STCP -> pin 12
//   Q0 to Q7 -> segments a to g and the dot; digit commons -> pins 2, 4, A3, 7
//   buttons, each to GND (INPUT_PULLUP): "+" -> A0, "set" -> A1,
//   "++" -> A2, "view" -> pin 10
//   DS1307 clock SDA -> A4, SCL -> A5     DHT11 DATA -> pin 11
//   active buzzer -> pin 3     RGB LED red -> pin 6, green -> pin 9
//
// The display shows the time. "view" steps through the year, the month and
// day, the temperature (ending in C) and the humidity (ending in H), and
// after five seconds it comes back to the time.
//
// Timer: press "+" for a minute or "++" for a second, then "set" to start.
// "set" again cancels it.
//
// Alarm: on the time, with no timer set, press "set". The display shows the
// alarm time; "+" adds an hour, "++" a minute, and "set" saves it. It is kept
// in the EEPROM, so it survives a power cut. The RGB LED is yellow while an
// alarm is set and green when none is. To turn the alarm off, set it to
// 00:00.
//
// When the timer runs out or the alarm time comes, the buzzer beeps and the
// LED flashes red and green until you press "set".

#include <Wire.h>
#include <RTClib.h>
#include <DHT.h>
#include <EEPROM.h>

const int DATA_PIN = 8;
const int LATCH_PIN = 12;
const int CLOCK_PIN = 13;
const int DIGIT_PINS[4] = {2, 4, A3, 7};
const int BUZZER = 3;
const int RED_PIN = 6;
const int GREEN_PIN = 9;

RTC_DS1307 rtc;
DHT dht(11, DHT11);

//                       gfedcba
const byte NUMBERS[10] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
const byte LETTER_C = 0x39;
const byte LETTER_H = 0x76;
const byte DOT = 0x80;

struct Button {
  int pin;
  bool down;
  unsigned long changedAt;
};
Button plus = {A0, false, 0};
Button setKey = {A1, false, 0};
Button plusPlus = {A2, false, 0};
Button view = {10, false, 0};

enum Mode { CLOCK, TIMER_SET, TIMER_RUN, ALARM_SET, RINGING };
Mode mode = CLOCK;
int page = 0;  // 0 time, 1 year, 2 month and day, 3 temperature, 4 humidity
unsigned long pageAt = 0;
long timerLeft = 0;  // seconds
unsigned long lastTick = 0;
int alarmMinutes = 0;  // minutes after midnight; 0 is no alarm
int lastRungMinute = -1;
byte shown[4];
int digit = 0;
float temperature = 0;
float humidity = 0;
unsigned long lastReading = 0;

bool pressed(Button &b) {
  bool now = digitalRead(b.pin) == LOW;
  if (now != b.down && millis() - b.changedAt > 30) {
    b.down = now;
    b.changedAt = millis();
    return now;
  }
  return false;
}

void color(int r, int g) {
  analogWrite(RED_PIN, r);
  analogWrite(GREEN_PIN, g);
}

void restingColor() {
  if (alarmMinutes > 0) color(160, 100);
  else color(0, 160);
}

void showNumber(int value, bool colon) {
  shown[0] = NUMBERS[value / 1000 % 10];
  shown[1] = NUMBERS[value / 100 % 10] | (colon ? DOT : 0);
  shown[2] = NUMBERS[value / 10 % 10];
  shown[3] = NUMBERS[value % 10];
}

void showReading(int value, byte letter) {
  shown[0] = 0;
  shown[1] = NUMBERS[value / 10 % 10];
  shown[2] = NUMBERS[value % 10];
  shown[3] = letter;
}

void refresh() {
  for (int i = 0; i < 4; i++) digitalWrite(DIGIT_PINS[i], HIGH);
  digitalWrite(LATCH_PIN, LOW);
  shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, shown[digit]);
  digitalWrite(LATCH_PIN, HIGH);
  digitalWrite(DIGIT_PINS[digit], LOW);
  digit = (digit + 1) % 4;
}

void saveAlarm() {
  EEPROM.put(0, alarmMinutes);
  Serial.print("alarm saved: ");
  Serial.println(alarmMinutes > 0 ? alarmMinutes : 0);
}

void startRinging(const char *why) {
  mode = RINGING;
  Serial.println(why);
}

void updateDisplay(const DateTime &now) {
  switch (mode) {
    case TIMER_SET:
    case TIMER_RUN:
      showNumber(timerLeft / 60 * 100 + timerLeft % 60, true);
      return;
    case ALARM_SET:
      showNumber(alarmMinutes / 60 * 100 + alarmMinutes % 60, true);
      return;
    default:
      break;
  }
  if (page == 1) showNumber(now.year(), false);
  else if (page == 2) showNumber(now.month() * 100 + now.day(), true);
  else if (page == 3) showReading((int)temperature, LETTER_C);
  else if (page == 4) showReading((int)humidity, LETTER_H);
  else showNumber(now.hour() * 100 + now.minute(), now.second() % 2 == 0);
}

void setup() {
  pinMode(DATA_PIN, OUTPUT);
  pinMode(LATCH_PIN, OUTPUT);
  pinMode(CLOCK_PIN, OUTPUT);
  for (int i = 0; i < 4; i++) pinMode(DIGIT_PINS[i], OUTPUT);
  pinMode(plus.pin, INPUT_PULLUP);
  pinMode(setKey.pin, INPUT_PULLUP);
  pinMode(plusPlus.pin, INPUT_PULLUP);
  pinMode(view.pin, INPUT_PULLUP);
  pinMode(BUZZER, OUTPUT);
  pinMode(RED_PIN, OUTPUT);
  pinMode(GREEN_PIN, OUTPUT);
  Serial.begin(9600);
  Serial.println("Mokxi Uno: alarm clock. view (10), + (A0), ++ (A2), set (A1).");
  if (!rtc.begin()) Serial.println("no clock found: check SDA and SCL");
  if (!rtc.isrunning()) rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  dht.begin();
  EEPROM.get(0, alarmMinutes);
  if (alarmMinutes < 0 || alarmMinutes >= 24 * 60) alarmMinutes = 0;  // a fresh EEPROM reads 0xFF
  restingColor();
}

void loop() {
  DateTime now = rtc.now();
  bool p = pressed(plus);
  bool s = pressed(setKey);
  bool pp = pressed(plusPlus);
  bool v = pressed(view);

  if (millis() - lastReading > 2000) {
    lastReading = millis();
    float t = dht.readTemperature();
    float h = dht.readHumidity();
    if (!isnan(t)) temperature = t;
    if (!isnan(h)) humidity = h;
  }

  switch (mode) {
    case CLOCK:
      if (v) {
        page = (page + 1) % 5;
        pageAt = millis();
      }
      if (page != 0 && millis() - pageAt > 5000) page = 0;
      if (p || pp) {
        mode = TIMER_SET;
        timerLeft = p ? 60 : 1;
        Serial.println("timer: set it, then press set");
      } else if (s) {
        mode = ALARM_SET;
        color(160, 100);
        Serial.println("alarm: set the time, then press set");
      }
      break;
    case TIMER_SET:
      if (p && timerLeft < 99L * 60) timerLeft += 60;
      if (pp && timerLeft < 99L * 60 + 59) timerLeft += 1;
      if (s) {
        mode = TIMER_RUN;
        lastTick = millis();
        Serial.println("timer started");
      }
      break;
    case TIMER_RUN:
      if (s) {
        mode = CLOCK;
        Serial.println("timer cancelled");
      } else if (millis() - lastTick >= 1000) {
        lastTick += 1000;
        if (--timerLeft <= 0) startRinging("time is up");
      }
      break;
    case ALARM_SET:
      if (p) alarmMinutes = (alarmMinutes + 60) % (24 * 60);
      if (pp) alarmMinutes = alarmMinutes / 60 * 60 + (alarmMinutes % 60 + 1) % 60;
      if (s) {
        saveAlarm();
        mode = CLOCK;
        restingColor();
      }
      break;
    case RINGING: {
      bool on = (millis() / 250) % 2 == 0;
      digitalWrite(BUZZER, on ? HIGH : LOW);
      color(on ? 160 : 0, on ? 0 : 160);
      if (s) {
        digitalWrite(BUZZER, LOW);
        mode = CLOCK;
        restingColor();
        Serial.println("quiet");
      }
      break;
    }
  }

  int minuteOfDay = now.hour() * 60 + now.minute();
  if (mode == CLOCK && alarmMinutes > 0 && minuteOfDay == alarmMinutes && lastRungMinute != minuteOfDay) {
    lastRungMinute = minuteOfDay;
    startRinging("alarm!");
  }

  updateDisplay(now);
  refresh();
  delay(3);
}

Parts list

19 parts, plus the jumper wires. They retail for about $43; on Mokxi they're free.

How it is wired

36 connections, pin by pin, read from the circuit itself. Each line is one set of pins joined together, by a jumper wire or a breadboard strip.

  • Ground: Arduino Uno R3 pin GND; 74HC595 shift register pin GND; 74HC595 shift register pin OE; Pushbutton (1) pin 2b; Pushbutton (2) pin 2b; Pushbutton (3) pin 2a; Temperature and humidity, DHT22 pin GND
  • Arduino Uno R3 pin 13; 74HC595 shift register pin SHCP
  • Arduino Uno R3 pin 12; 74HC595 shift register pin STCP
  • Arduino Uno R3 pin 11; Temperature and humidity, DHT22 pin DATA
  • Arduino Uno R3 pin 10; Pushbutton (4) pin 1b
  • Arduino Uno R3 pin 9; Resistor, 220 Ω (6) pin 2
  • Arduino Uno R3 pin 8; 74HC595 shift register pin DS
  • Arduino Uno R3 pin 7; Resistor, 220 Ω (4) pin 1
  • Arduino Uno R3 pin 6; Resistor, 220 Ω (5) pin 1
  • Arduino Uno R3 pin 4; Resistor, 220 Ω (2) pin 2
  • Arduino Uno R3 pin 3; Buzzer pin 1
  • Arduino Uno R3 pin 2; Resistor, 220 Ω (1) pin 2
  • Arduino Uno R3 pin IOREF; Real-time clock, DS1307 pin VCC
  • Arduino Uno R3 pin 5V; 74HC595 shift register pin MR; 74HC595 shift register pin VCC; Temperature and humidity, DHT22 pin VCC
  • Ground: Arduino Uno R3 pin GND; RGB LED pin C; Buzzer pin 2; Pushbutton (4) pin 2b
  • Ground: Arduino Uno R3 pin GND; Real-time clock, DS1307 pin GND
  • Arduino Uno R3 pin A0; Pushbutton (1) pin 1b
  • Arduino Uno R3 pin A1; Pushbutton (2) pin 1b
  • Arduino Uno R3 pin A2; Pushbutton (3) pin 1a
  • Arduino Uno R3 pin A3; Resistor, 220 Ω (3) pin 2
  • Arduino Uno R3 pin A4; Real-time clock, DS1307 pin SDA
  • Arduino Uno R3 pin A5; Real-time clock, DS1307 pin SCL
  • 74HC595 shift register pin Q1; Four-digit seven segment, multiplexed pin B
  • 74HC595 shift register pin Q2; Four-digit seven segment, multiplexed pin C
  • 74HC595 shift register pin Q3; Four-digit seven segment, multiplexed pin D
  • 74HC595 shift register pin Q4; Four-digit seven segment, multiplexed pin E
  • 74HC595 shift register pin Q5; Four-digit seven segment, multiplexed pin F
  • 74HC595 shift register pin Q6; Four-digit seven segment, multiplexed pin G
  • 74HC595 shift register pin Q7; Four-digit seven segment, multiplexed pin DP
  • 74HC595 shift register pin Q0; Four-digit seven segment, multiplexed pin A
  • Four-digit seven segment, multiplexed pin DIG4; Resistor, 220 Ω (4) pin 2
  • Four-digit seven segment, multiplexed pin DIG3; Resistor, 220 Ω (3) pin 1
  • Four-digit seven segment, multiplexed pin DIG2; Resistor, 220 Ω (2) pin 1
  • Four-digit seven segment, multiplexed pin DIG1; Resistor, 220 Ω (1) pin 1
  • RGB LED pin R; Resistor, 220 Ω (5) pin 2
  • RGB LED pin G; Resistor, 220 Ω (6) pin 1

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.