In this step, we'll create a program to be uploaded onto the ATmega microcontroller on the Arduino Uno board. The program will power the 13 required LEDs to display a 24-hour clock in binary. In later steps, we'll worry about setting the time, but for now it will always start at 00:00.
Arduino programs have 2 main methods; a method that is run once at the beginning where initialization is done (called setup), and another method which is called continuously (called loop).
In setup(), we'll set the time to 00:00, and set up which pins are going to drive the 13 LEDs.
In loop() we'll see if more than a second has ellapsed (and if so, increment the time), then display the time by powering the proper LEDs. The millis() method is instrumental to keeping time. It returns the number of milliseconds that have ellapsed since the circuit was powered as an unsigned long. "Unsigned" means that it will not be negative, and long refers to how many bits (32 to be exact) are used to keep track of this number (bits are the number of binary digits).
The Quirk with millis()
Since there is a finite number of bits in an unsigned long, at some point we're going run out of digits! According to the Arduino documentation on millis()
, it will wrap around (ie, reset back to zero) after approximately 50 days. How annoying would that be to have to reset your clock every 50 days? As one of the goals states, we're striving for functional perfection
and this distruptive behavior is unnacceptable. Thus, the logic in our tick() method is used to see when we wrap around and continue without anyone being the wiser.
Before we jump right in and start assigning digital pins, we'll required a PWM (pulse width modulated) pin to display the seconds on the analog meter in a later step. On the Arduino Uno, pins 3, 5, 6, 9, 10, and 11 are enabled for PWM (as signified by the "~"). Thus, I'm saving pin 11 for the analog display, and using pins 0 - 10, 12 & 13 for the binary LED display.
Cwik Clock v1.0 - Prototyping the Display
Author: Dennis Cwik
Date: July 23, 2012
This program is the controller for a binary clock, with LEDs
attached to digital pins 0 through 10, 12, and 13.
This example code is in the public domain.
// This can be modified for debug purposes to make a minute go by quicker.
int ONE_SECOND = 1000; // measured in milliseconds
int DELAY_BETWEEN_LOOP_CALLS = 200; // measured in milliseconds
// I didn't come up with this, it's from the arduino documentation
unsigned long MAX_UNSIGNED_LONG = 4294967295; // = (2 ^ 32) - 1
// 1st column of LEDs
int PIN_MIN1 = 0;
int PIN_MIN2 = 1;
int PIN_MIN4 = 2;
int PIN_MIN8 = 3;
// 2nd column of LEDs
int PIN_MIN10 = 4;
int PIN_MIN20 = 5;
int PIN_MIN40 = 6;
// 3rd column of LEDs
int PIN_HOUR1 = 7;
int PIN_HOUR2 = 8;
int PIN_HOUR4 = 9;
int PIN_HOUR8 = 10;
// 4th column of LEDs
int PIN_HOUR10 = 12;
int PIN_HOUR20 = 13;
// the last time the seconds in the time were incremented
unsigned long m_lastTick;
// used to tell us if we're setting the time or not
boolean m_inTimeSetMode = false;
// the time
// the setup routine runs once when you press reset
// initialize the pins used for outputting time as OUTPUT
// initialize clock variables
m_lastTick = 0;
setTime(0, 0, 0);
// the loop routine runs over and over again forever
// see if we're setting the time, or letting time flow normally
// now that the time has been updated, show the time
// arbitrary delay so that we're not processing away 100% of the time,
// an act of power saving
* A helper method to set m_second, m_minute, and m_hour.
void setTime(byte newHour, byte newMinute, byte newSecond)
m_second = newSecond;
m_minute = newMinute;
m_hour = newHour;
* This method keeps track of the logical flow of time. If enough time has
* passed since the last time it was called, m_second, m_minute, and m_hour
* will be updated appropriate. This takes into account that millis() rolls
* over roughly every 50 days.
unsigned long now = millis();
unsigned long msElapsed;
// first we need to find out how much time has passed since the last time we
// called tick()
if (now < m_lastTick)
// gasp, either we've succeeded in travelling back in time, or millis() wrapped around!
msElapsed = (MAX_UNSIGNED_LONG - m_lastTick) + now;
msElapsed = now - m_lastTick;
// for each second that has passed (hopefully just 1, unless our code is really laggy),
// add 1 second to the time, and increase the minutes & hours if necessary.
for (int i = 0; i < msElapsed / ONE_SECOND; ++i)
m_lastTick = m_lastTick + ONE_SECOND;
if (m_second == 60)
m_second = 0;
if (m_minute == 60)
m_minute = 0;
if (m_hour == 24)
m_hour = 0;
// TODO control the analog display
* This method reads the variable m_minute, converts it to binary, and displays
* it on the appropriate LEDs (those being PIN_MIN*).
byte ones = m_minute % 10;
digitalWrite(PIN_MIN1, ones & B1);
digitalWrite(PIN_MIN2, ones & B10);
digitalWrite(PIN_MIN4, ones & B100);
digitalWrite(PIN_MIN8, ones & B1000);
// division is kind of expensive, but we'll assume the compile optimizes this for us :)
byte tens = m_minute / 10;
digitalWrite(PIN_MIN10, tens & B1);
digitalWrite(PIN_MIN20, tens & B10);
digitalWrite(PIN_MIN40, tens & B100);
* This method reads the variable m_hour, converts it to binary, and displays
* it on the appropriate LEDs (those being PIN_HOUR*).
byte ones = m_hour % 10;
digitalWrite(PIN_HOUR1, ones & B1);
digitalWrite(PIN_HOUR2, ones & B10);
digitalWrite(PIN_HOUR4, ones & B100);
digitalWrite(PIN_HOUR8, ones & B1000);
byte tens = m_hour / 10;
digitalWrite(PIN_HOUR10, tens & B1);
digitalWrite(PIN_HOUR20, tens & B10);
* This method reads the values from the 2 potentiometers, converts them to
* mimnutes and hours, and sets m_minute and m_hour to the associated values.
// TODO read the potentiometers, set the hour and minutes