Introduction: Clock Project for Ahmed (Arduino-Compatible)

This is not an Instructable how to recreate the clock project that got Ahmed Mohamed in trouble in September 2015, nor do I want this to be a discussion about what happened. What I do want to do is lay out a simple clock project using an Arduino to help inspire people like Ahmed to keep hacking, making, and programming.

At it's most fundamental, a digital clock is a computer with input and output and with behavior controlled through logic. Using an Arduino, we can program and test the behaviors step by step. This can lead us in to some insights as to why the basic digital clock hasn't changed in several decades. It can also let us creatively explore some alternatives.

Summary of Steps:

  1. Using Arduino's internal counter to keep time.
  2. Adding visual output using a 7-segment display.
  3. Adding an alarm using a piezo speaker.
  4. Using a button for input to silence the alarm.
  5. Adding a real-time clock (RTC) for accuracy.
  6. Adding battery power.

Parts & Supplies:

All you really need to start you exploration is an Arduino. However, to complete this project, some other optional items are needed:

Ready to begin?

Step 1: An Arduino Makes a Poor Clock - Simulating a Clock in Software

When I tackle a complex project, I like to break it into discrete steps based on the functionality and wiring. Then, when something goes wrong, I can always fall back to the previous "known good" state to help figure out what went wrong.

We will start by just writing some simple code to handle seconds, minutes, and hours using the Arduino's internal counter which tracks the time, in milliseconds, since the board was powered-on or reset.

Here is our starting sketch, with no libraries or wiring. Time is simply displayed using the serial console:

  // time variables<br>  long lastMillis;
  int hours = 0;
  int minutes = 0;
  int seconds = 0;
  
  
  void setup() {
    Serial.begin(9600);  // Open channel to serial console
    Serial.println("Arduino Clock Step 1");
  }
  
  
  void loop() {
    long curMillis = millis();              // get current counter in milliseconds
    if (curMillis > (lastMillis + 1000)){   // If one second has passed:
      lastMillis = lastMillis + 1000;
      seconds = seconds + 1;                // increment seconds
      if (seconds > 59){
        seconds = 0;
        minutes = minutes + 1;              // increment minutes
        if (minutes > 59){
          minutes = 0;
          hours = hours + 1;                // increment hours
          if (hours > 23){
            hours = 0;
          }
        }
      }
    
      // Display time on serial console
      Serial.print(hours);
      Serial.print(":");
      Serial.print(minutes);
      Serial.print(":");
      Serial.println(seconds);
    }
  }

You can set the time variables before you upload the sketch, and the Arduino will keep reasonable time (within a minute per day) until a reset or power cycle occurs.

Time to Explore:

  • Can you fix the output so if hours or minutes are less than 10, a leading "0" is shown?
  • How accurate is your Arduino? Could you calibrate it to run more accurately?
  • Could you use the Digital Pin 13 LED to indicate the passing of seconds?

Step 2: Adding Some Output - 7 Segment Display

Seeing time click off on the serial console is cool, but hey, you already have an accurate digital clock down in the corner of your computer screen. We need some real output, so I'm going to add a 7-segment display. Adafruit and SparkFun have a number of different display options, but I'm going to use Adafruit's I2C one (https://www.adafruit.com/products/881).

Both the display and the RTC module (Step 5) have software libraries that must be installed to give us the functionality to easily program them. Libraries abstract all the low-level hardware code so we can just focus on the task at hand.

We are eventually going to need three libraries, so lets install them all now. Start with the display library instructions at https://learn.adafruit.com/adafruit-led-backpack/0... and install the libraries from:

Also, using the instructions from https://learn.adafruit.com/ds1307-real-time-clock-... install the library from:

Wire the display up per the instructions and diagram, and test it using the library's example "sevenseg". Once you are satisfied it is functioning, we are ready for some clock code:

  #include "Wire.h" <br>  #include "RTClib.h"

  #include "Adafruit_LEDBackpack.h"
  #include "Adafruit_GFX.h"
  Adafruit_7segment matrix = Adafruit_7segment();
  
  // time variables
  long lastMillis;
  int hours = 0;
  int minutes = 0;
  int seconds = 0;
  
  void setup() {
    Serial.begin(9600);  // Open channel to serial console
    Serial.println("Arduino Clock Step 2");
    matrix.begin(0x70);  // Start display
  }
  
  void loop() {
    long curMillis = millis();              // get current counter in milliseconds
    if (curMillis > (lastMillis + 1000)){   // If one second has passed:
      lastMillis = lastMillis + 1000;
      seconds = seconds + 1;                // increment seconds
      if (seconds > 59){
        seconds = 0;
        minutes = minutes + 1;              // increment minutes
        if (minutes > 59){
          minutes = 0;
          hours = hours + 1;                // increment hours
          if (hours > 23){
            hours = 0;
          }
        }
      }
      
      // Display time on serial console
      if (hours < 10){
        Serial.print(" ");    // pad hours
      }
      Serial.print(hours);
      Serial.print(":");
      if (minutes < 10){
        Serial.print("0");    // pad minutes}
      }
      Serial.print(minutes);
      Serial.print(":");
      if (seconds < 10){
        Serial.print("0");    // pad seconds
      }
      Serial.println(seconds);
      
      // Break time into individual digits for display
      int h1 = hours / 10;
      int h2 = hours - h1 * 10;
      int m1 = minutes / 10;
      int m2 = minutes - m1 * 10;
      int s1 = seconds / 10;
      int s2 = seconds - s1 * 10;
      
      // Send data to display
      if (h1){
        matrix.writeDigitNum(0, h1, false); }
      else{
        matrix.writeDigitRaw(0, 0);       // blank instead of zero
      }
      matrix.writeDigitNum(1, h2, false);
      matrix.drawColon(seconds % 2);       // on if second is odd
      matrix.writeDigitNum(3, m1, false);
      matrix.writeDigitNum(4, m2, false);
      matrix.writeDisplay();    
    }
  }

Depending on your start time, you may just see "0 00". Pretty boring. If you want to see some number move, try substituting in the second digits (s1, s2) for the minutes digits (m1, m2) for digits # 3 & 4.

Also, most digital clocks give the indication of seconds passing by flashing the colon in between hours and minutes. You can do that using a modulo test on seconds to flash on if it is odd, off if it is even. Try adding:

matrix.drawColon(seconds % 2);<br>

With the RTC library available (even without the actual module installed), we can use some tricks to get the time the sketch was saved and set the time.

// Get upload date time<br>  DateTime uploadDateTime = DateTime(__DATE__, __TIME__);
  
  int hours = uploadDateTime.hour();
  int minutes = uploadDateTime.minute();
  int seconds = uploadDateTime.second();

Now you should have a decent display of current time until you reset or power-cycle the board. Just re-upload the sketch to set the correct time again!

Time to explore:

  • How would you make the clock run faster or slower?
  • Can you make it run backwards?
  • How could you display AM/PM instead of 24 hours?

Step 3: Alarm! Adding Some Sound With a Piezo Speaker

Alarm clocks alarm. I'm going to add a piezo speaker so our Arduino clock can signal us audibly. That counts as another output.

Hook up a piezo speaker to Digital Pin 8 and ground. Next, we need some variables to help track things in the beginning of our sketch:

// set alarm here or it gets +1 minute in setup<br>  int alarm_hour = 0;
  int alarm_minute = 0;
  boolean alarming = false;
  boolean alarmSet = true;
  int buzzerPin = 8;

In setup(), we can set the alarm if it wasn't set before:

// set the alarm one minute in the future<br>    if(!alarm_hour){ // not set above
      alarm_hour = hours;
      alarm_minute = minutes + 1;
    }

And in loop(), we need to figure out if it is time to alarm or not:

      // check for alarm<br>      if ((hours == alarm_hour) && (minutes == alarm_minute)){
        alarming = true;}
      else{
        alarming = false;
        alarmSet = true; // reset alarm
      }
      
      if (alarming && alarmSet){
        Serial.print("Alarm! ");
        if (seconds % 2){
          tone(buzzerPin, 880);
        }
        else{
          noTone(buzzerPin);
        }
      }
      else{
        noTone(buzzerPin);
      }

Wow. That sounds just like the clock next to my bed. See how I'm using modulo again to alternate the speaker on and off. My clock by my bed also beeps in one second intervals and will alarm for a minute if not reset or "snoozed". Coincidence?

Time to explore:

  • Change the pitch from 880. Can you come up with a pattern or play notes?
  • Add the Digital Pin 13 LED in the party. You could use it as a visual indication of alarming.
  • What if you want the alarm to be shorter than one minute?

Step 4: Silence That Alarm. Adding Input With a Button

You usually don't let your alarm clock beep for an entire minute hoping it will shut up. You either hit the snooze button or turn the alarm off. Right now, all we have is the reset button which will silence the alarm, but only for another minute since it will reset the time. Time to add an input button we can use to silence the alarm.

Lets add our button by connecting one side to Analong Pin 0, which we will treat as a Digital Pin, and the other side to Ground.

int buttonPin = A0;

In setup(), we need to set the pin as OUTPUT, and then engage the internal pull-up resistor. This means a digitalRead() will return HIGH (5V) unless pushed.

    // input button setup<br>    pinMode(buttonPin, INPUT);
    digitalWrite(buttonPin, HIGH); // engage internal pullup

Now we need to monitor the button and silence the alarm if pressed. Our previous use of the alarmSet variable makes this quite easy:

    // Check button. If pushed reset alarm.<br>    boolean buttonState = digitalRead(buttonPin);
    if(buttonState == LOW){
      alarmSet = false;
    }

Time to Explore:

  • What if you didn't want the alarm to go off to start with? Could you use the Pin 13 LED to indicate status?
  • A short beep when the button is pushed would give some user feedback. How would you do that?

Step 5: Getting Real With Time - Adding a Real Time Clock

By now, you must realize the limitations of a software simulated clock. How does a watch or phone keep accurate time? First, you need a continuous power supply, and secondly, you need an accurate crystal. A Real Time Clock module takes care of this for us. Once it is set, as long as it's small battery has power, it will keep time accurately to several seconds per day.

Adafruit's RTC module uses the same I2C bus that the display uses. How does this work? Each item has a unique address, and the libraries handle the rest for us.

  // Start the Real Time Clock<br>  RTC_DS1307 rtc = RTC_DS1307();

In setup(), we need to check if the clock is running and set if it is not. Then we can initialize the variable using the current time.

    // start and set clock if not already running<br>    rtc.begin();
    bool setClockTime = !rtc.isrunning();
    Serial.print("rtc.isrunning() =");
    Serial.println(!setClockTime);
    if (setClockTime) {
      rtc.adjust(DateTime(uploadDateTime));
    }
    
    // initalize time variables
    DateTime time = rtc.now();
    hours = time.hour();
    minutes = time.minute();
    seconds = time.second();
    lastMillis = millis();

We need to check the RTC time every so often since we are still relying on the internal counter to increment seconds. How often? How about once an hour:

    // Check RTC at top of hour and reset variables<br>    if (minutes == 0 && seconds == 0){
      DateTime time = rtc.now();
      hours = time.hour();
      minutes = time.minute();
      seconds = time.second();
    }

Time to Explore:

  • The RTC modules also provides information about the date. How would you set an alarm for weekdays only?
  • How accurate is your RTC module?
  • The code for setting time variables in setup() and loop() are nearly identical. Could we create a function used by both?

Step 6: Going Portable - Adding Battery Power

So by now, we have a fully functioning clock. If you are like Ahmed, and want to take it somewhere to show someone, we are going to need a bigger battery than the one keeping the RTC alive. Arduinos thrive on 5V, and can run as low as 3V given the right conditions. Unfortunately, the Adafruit RTC needs a minimum of 4.5V to operate correctly, so we need something regulated correctly and not just hit-n-miss with for instance 3xAA.

There are a number of options, but they all add cost. A recharable lithium-ion battery requires a boost circuit to get to 5V and a recharging circuit, so we would need something like adafruit.com/products/1944. I'm trying to keep this simple, so I'm going to opt for 4XAA fed to the Arduino's Vin, letting it handle the 5V regulation. The holder is cheap, as are the batteries.

Time to Explore:

  • How much current is your Arduino drawing with and without the display running?
  • How long would you expect your batteries to last.
  • What could you do to lower the current draw and extend the run time?

Step 7: Packing It Up - Adding a Project Box

You have accurate time, a clock that behaves the way your programmed it, and it is now battery powered. It is time for the final step: putting it in a project box.

An enclosure can be just about anything, and instead of buying something off the shelf, I like to look through thrift-stores or keep my eye on the recycle bin. For this project, I wanted something with a clear lid so people could see the inner-workings and not be worried that it had a nefarious purpose.

I cut openings for USB and external power to the Arduino. I hot glued the Arduino, breadboard, and battery pack to the bottom after roughing it up with some sand paper. I then marked and cut openings in the lid to hot glue the various components in place. Even if you have followed this Instructable to the letter, here is your chance to make it a creation all your own! I'd love to see photos if you do.

A couple of notes to help you out:

  • Male-female jumpers work great for getting the 7-segment display off the breadboard.
  • Solid core hookup wire can help making the longer connections so that you can still open the lid to work on it.
  • Heat shrink makes things look professional and helps avoid shorts.

Step 8: Some Final Thoughts

Our Arduino turned in to a pretty expensive clock, but the fact that you made it and programmed it makes it priceless. And, you can always incorporate the parts into your next, bigger and better project!

If you still have clocks on the brain, here are some interesting clock projects with unique displays:

Happy Making!