Mirko Pavleski
Published © GPL3+

DIY 3D Printed Single Digit Arduino Clock

One big digit, fully functional Arduino Nano clock.

IntermediateFull instructions provided2,742
DIY 3D Printed Single Digit Arduino Clock

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
DS3231M - ±5ppm, I2C Real-Time Clock
Maxim Integrated DS3231M - ±5ppm, I2C Real-Time Clock
×1
General Purpose Transistor NPN
General Purpose Transistor NPN
×8
Resistor 1k ohm
Resistor 1k ohm
×8
LED Strip, 500 mm
LED Strip, 500 mm
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Custom parts and enclosures

3D files

Schematics

Schematic

Code

Code

C/C++
// Clock Two
// tronixstuff.com/projects > Clock Two
// John Boxall - February 2012 - March 2013
// Use Arduino v23 | CC by-sa-nc

#include "Wire.h"
#define DS3231_I2C_ADDRESS 0x68 // the DS3231 RTC is 0x68

int timeDelay(3000); // duration in ms between displaying time

// digits' PORTD representation. 0~9, A~F, "-"
byte segments[]={
  B00111111,B00000110,B01011011,B01001111,B01100110,B01101101,B01111101,B00000111,B01111111,B01100111,B01110111,B01111100,B00111001,B01011110,B01111001,B01110001, B01000000};

// usual DS3231 time functions
// See tronixstuff.com/tutorials > chapter twenty  for more information
// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
  return ( (val/10*16) + (val%10) );
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}

// 1) Sets the date and time on the DS3231
// 2) Starts the clock
// 3) Sets hour mode to 24 hour clock

// Assumes you're passing in valid numbers

void setDateDS3231(byte second,        // 0-59
byte minute,        // 0-59
byte hour,          // 1-23
byte dayOfWeek,     // 1-7
byte dayOfMonth,    // 1-28/29/30/31
byte month,         // 1-12
byte year)          // 0-99
{
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0);
  Wire.write(decToBcd(second));    // 0 to bit 7 starts the clock
  Wire.write(decToBcd(minute));
  Wire.write(decToBcd(hour));     
  Wire.write(decToBcd(dayOfWeek));
  Wire.write(decToBcd(dayOfMonth));
  Wire.write(decToBcd(month));
  Wire.write(decToBcd(year));
  Wire.write(00010000); // sends 0x10 (hex) 00010000 (binary) to control register - turns on square wave
  Wire.endTransmission();
}

// Gets the date and time from the DS3231
void getDateDS3231(byte *second,
byte *minute,
byte *hour,
byte *dayOfWeek,
byte *dayOfMonth,
byte *month,
byte *year)
{
  // Reset the register pointer
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0);
  Wire.endTransmission();

  Wire.requestFrom(DS3231_I2C_ADDRESS, 7);

  // A few of these need masks because certain bits are control bits
  *second     = bcdToDec(Wire.read() & 0x7f);
  *minute     = bcdToDec(Wire.read());
  *hour       = bcdToDec(Wire.read() & 0x3f);  // Need to change this if 12 hour am/pm
  *dayOfWeek  = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month      = bcdToDec(Wire.read());
  *year       = bcdToDec(Wire.read());
}

void sqwOn() // turns on 1Hz output for decimal point blinking
{
 Wire.begin();   //TWI Bus is formed  
  Wire.beginTransmission(DS3231_I2C_ADDRESS); //device address and STSRT command are queued
  Wire.write(0x0E); //Control Register Address is queued  (or Wire.send)
  Wire.write(0x00); //Data for Control Register is queued (or Wire.send)
  Wire.endTransmission(); //queued information are transferred under ACK; STOP
}


// for information on "PORTD" etc see tronixstuff.com/tutorials > chapter 43

void loopNumbers() // for testing
{
  for (int a=0; a<16; a++)
  {
    PORTD = segments[a]; // digital 4~7 HIGH, digital 3~0 LOW
    delay(100);
  }
}

void blankLED()
// blanks LED display by setting all pins LOW
{
  PORTD = B00000000;
}


void displayTime1(int a, int b, int c, int d)
{
  int del=750; // delay between digits
  int bdel=100;
  PORTD = segments[a];
  delay(del);
  blankLED();
  delay(bdel);  
  PORTD = segments[b];
  delay(del);
  PORTD = segments[16]; // display hyphen
  delay(del);
  PORTD = segments[c];
  delay(del);
  blankLED();
  delay(bdel);  
  PORTD = segments[d];
  delay(del);
  blankLED(); // clear display
}

void setTime()
// user presses button A (digital 8) to enter 'set time' mode while time being displayed
// then user presses A to advance value, B to lock in. Repeat for four digits of time
{
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;  
  int h1=0;
  int h2=0;
  int m1=0;
  int m2=0;
  boolean set=false;
  blankLED();
  int debounce=200;

  do // get first digit of hours from user
  // press digital 8 button to change from 0>1>2>0>1...
  // press digital 9 to set value
  {
    PORTD = segments[h1];
    if (digitalRead(8)==HIGH)
    {
      delay(debounce);      
      h1++;
      if (h1>2)
      {
        h1=0;
      }
      PORTD = segments[h1];
    }
    if (digitalRead(9)==HIGH)
    {
      delay(debounce);      
      set=true;
    }
  } 
  while (set!=true);
  blankLED();
  set=false;  
  delay(500); 

  do // get second digit of hours from user
  // press digital 8 button to change from 0>1>2>...9>0...
  // press digital 9 to set value
  {
    PORTD = segments[h2];
    if (digitalRead(8)==HIGH)
    {
      delay(debounce);      
      h2++;
      if (h2>9)
      {
        h2=0;
      }
      PORTD = segments[h2];
    }
    if (digitalRead(9)==HIGH)
    {
      delay(debounce);      
      set=true;
    }
  } 
  while (set!=true);
  blankLED();
  set=false;    
  delay(500); 

  do // get first digit of minutes from user
  // press digital 8 button to change from 0>1>..5>0..
  // press digital 9 to set value
  {
    PORTD = segments[m1];
    if (digitalRead(8)==HIGH)
    {
      delay(debounce);      
      m1++;
      if (m1>5)
      {
        m1=0;
      }
      PORTD = segments[m1];
    }
    if (digitalRead(9)==HIGH)
    {
      set=true;
      delay(debounce);      
    }
  } 
  while (set!=true);
  blankLED();
  set=false;    
  delay(500); 

  do // get second digit of minutes from user
  // press digital 8 button to change from 0>1>..9>0..
  // press digital 9 to set value
  {
    PORTD = segments[m2];
    if (digitalRead(8)==HIGH)
    {
      delay(debounce);
      m2++;
      if (m2>9)
      {
        m2=0;
      }
      PORTD = segments[m2];
    }
    if (digitalRead(9)==HIGH)
    {
      set=true;
      delay(debounce);
    }
  } 
  while (set!=true);
  blankLED();
  set=false;    
  delay(500); 

  // now convert the user time data to variables to write to DS3231

  hour = (h1*10)+h2;
  minute = (m1*10)+m2;
  if (hour<24 && minute <60) // in case user enters invalid time e.g. 2659h
  {
    setDateDS3231(second, minute, hour, dayOfWeek, dayOfMonth, month, year);
    sqwOn(); // turn on blinking decimal point again (setDateDS3231 seems to turn it off...
    PORTD = segments[16]; // display hyphen
    delay(500);
    blankLED();
    delay(500);
    PORTD = segments[16]; // display hyphen
    delay(500);
    blankLED();  
  }
}

void setup()
{
  DDRB = B00000000; // set d13~d8 as inputs for buttons
  DDRD = B11111111; // set d7~d0 as outputs for LED display
  Wire.begin(); // fire up I2C bus  
  blankLED();  // blanks display. 
  sqwOn(); // turn on blinking decimal point 
}

void loop()
{
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  int h1, h2, m1, m2;
  getDateDS3231(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
  h1=int(hour/10);
  h2=hour%10;
  m1=int(minute/10);
  m2=minute%10;
  displayTime1(h1,h2,m1,m2); // displays the time on the display
  if (digitalRead(8)==HIGH)
  {
    setTime();
  }
  delay(timeDelay); // arbitrary delay until next time display
}

Credits

Mirko Pavleski

Mirko Pavleski

8 projects • 253 followers

Comments