Large 6 Digit LED Clock 1.8" + 0.8" High Digits Using Arduino and DS3231 RTC

by adrian-smith31 in Circuits > Clocks

528 Views, 5 Favorites, 0 Comments

Large 6 Digit LED Clock 1.8" + 0.8" High Digits Using Arduino and DS3231 RTC

20231124_124143.jpg
20231103_110553_exported_3.jpg
Build a large LED clock with 1.8" digits plus 0.8" for seconds. Uses RTC for accuracy. ATMEGA168P

A 6 digit LED clock using large 1.8" high digits for the hours and minutes and 0.8" high digits for the seconds. It uses an ATMEGA168P or ATMEGA328P microcontroller and programmed in the Arduino IDE. A DS3231 RTC module keeps accurate time. In my example I used salvaged LED displays which light up in a orange / red colour however they are a standard pinout so you can use equivalents; an example of which are listed in the schematic.

The displays do not use multiplexing and a TPIC6B595 shift register runs each LED display. The 1.8" high displays have 3 LED chips per segment and need a forward voltage of around 6.3V. Hence I used 12V to power these via current limiting resistors. A reverse polarity protection diode on the input of the circuit drops the LED supply voltage to around 11.4V. The chips run off 5V via a regulator.

The clock uses PWM on the Output Enable pin of the shift registers to control the brightness of the display. It will dim between the hours of 23:00 and 7:00. The colons will also dim by switching in an additional resistor to dim them at night.

The clock is ideal for large rooms and workplaces where the display needs to be seen from a distance.

A suggested option for the case is listed in this instructable.

Supplies

Wooden case Tea Box Wooden 8 Section Organiser Storage Caddy With Clear Lid Kitchen Storage | eBay

Light grey window tint TRANSPARENT COLOURED WINDOW FILM STAINED GLASS SELF ADHESIVE VINYL FABLON | eBay

Custom made PCB My Ebay listings – Adrian's electronics blog (adrian-smith31.co.uk)

ATMEGA168P or ATMEGA328P with Arduino bootloader pre-installed

DS3231 RTC module.

Electronic components (see attached BOM (Bill of Materials) file for full details.

Program the ATmega168P / 328P

The simplest way of programming the microcontroller is to put it into an Arduino Uno and upload the code (below) however you can program the .hex file with an external programmer such as the TL866 or any of Atmel / Microchip's tools. After the chip is programmed it can be removed from it's socket and used in the project.

If you want to program a bare chip without bootloader make sure the fuses need to be set to as below:-

  • Fuse Low Byte (none selected)
  • Fuse High Byte
  • SPIEN=0
  • BOOTRST=0
  • Extended Fuse Byte
  • BODLEVEL1=0
  • Lock Bit Byte (none selected)

Code for the clock - make sure you install the libraries listed in #include. These can be installed via the Arduino IDE.

Pre-compiled .hex file attached to this step.

// 6 Digit clock using TPIC6B595 shift registers
// Leftmost digit is digit 0
// Data will be fed in to the rightmost register first so digit 0 needs shifting out first.
// The clock will have 3 buttons - hour set, min set and enter / exit set mode.
// To set the time press the set button and the clock will go to the set time function - this is detected in main loop.
// To exit time set function press set button again, this will also zero the seconds and write new values to the rtc.
// A 4th button could be used to display temperature - not sure about this yet.


// 7-segment digits 0-9
// {Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7} --> {g, f, e, d, c, b, a, DP}


#include <SPI.h>
#include <SoftPWM.h>
#include <Wire.h>                        
#include <RTClib.h>
#include "Button.h"  // Button library by Alexander Brevig                      


const int reg_clock = 13; // SRCK
const int reg_latch = 10; // RCLK
const int reg_data = 11; // SER_IN
const int reg_OE = 9; // output enable; Active LOW. Can be used to control brightness with PWM.
const int colon_pin = 8; // used to dim the colon LED's when night mode enabled.


Button button1 = Button(2,BUTTON_PULLUP_INTERNAL);       // Setup button A (using button library)
Button button2 = Button(3,BUTTON_PULLUP_INTERNAL);       // Setup button B (using button library)
Button button3 = Button(4,BUTTON_PULLUP_INTERNAL);       // Setup button A (using button library)
Button button4 = Button(5,BUTTON_PULLUP_INTERNAL);       // Setup button B (using button library)


int brightness = 4; // set display brightness. Values 1-5. Set to level 4 as LED's I chose were eye searingly bright at level 5.
int brt = 0; // value for storing reversed PWM percentage
int set_mode = 0; // value for storing timeset mode true or false


#define DS3231_I2C_ADDR 0x68
#define DS3231_TEMPERATURE_ADDR 0x11


RTC_DS3231 rtc;


byte ssddigits[10] = // array without decimal points on
{
  B01111110,  // 0
  B00001100,  // 1
  B10110110,  // 2
  B10011110,  // 3
  B11001100,  // 4
  B11011010,  // 5
  B11111010,  // 6
  B01001110,  // 7
  B11111110,  // 8
  B11011110,  // 9
};


byte ssddigitsDP[10] = // array with decimal points on
{
  B01111111,  // 0
  B00001101,  // 1
  B10110111,  // 2
  B10011111,  // 3
  B11001101,  // 4
  B11011011,  // 5
  B11111011,  // 6
  B01001111,  // 7
  B11111111,  // 8
  B11011111,  // 9
};


byte tempC[2] = // C and degrees symbol
{
  B01110010,
  B11000110,
};


void setup() // runs once at powerup
{
 
  pinMode (reg_clock, OUTPUT);
  pinMode (reg_latch, OUTPUT);
  pinMode (reg_data, OUTPUT);
  pinMode (reg_OE,OUTPUT);
  pinMode (colon_pin, OUTPUT);
  digitalWrite(reg_OE,LOW); // enable output of shift register chain; an external pullup resistor on the OE pin prevents garbage being displayed at power on.
  digitalWrite(colon_pin, HIGH); // set colons to bright initially
  setBrt();
  SoftPWMBegin();
  SoftPWMSet(9, 0); // init software PWM on pin 9. This pulses the OE pin on and off to control brightness.
  SoftPWMSetPercent(9, brt);
  Wire.begin(); // start i2c
  rtc.begin(); //start RTC Clock
  set_mode = 0; // set timeset mode to off
             
  //rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // comment out once clock is initialy set
   
}


void loop() // main program loop
{
   if (set_mode == 1)


   {
     setTime();
   }


   else
   {
     clockDisplay();
   }
   
   if (button1.uniquePress())
   {
    SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0)); // display "set" for 2 seconds
    digitalWrite(reg_latch,LOW);
    SPI.transfer(B11011010); // S
    SPI.transfer(B11110010); // E
    SPI.transfer(B11110000); // t
    SPI.transfer(B00000000); // blank digit 3
    SPI.transfer(B00000000); // blank seconds display 10s
    SPI.transfer(B00000000); // blank seconds display 1s
    digitalWrite(reg_latch,HIGH);
    SPI.endTransaction();
    delay(2000);
    set_mode = 1; // enter timeset mode
   }


  setBrt();
  SoftPWMSetPercent(9, brt);
}


/// functions ///


void clockDisplay() // gets the time from the rtc and sends data to the shift registers.
{


  DateTime now = rtc.now();


  int hours = now.hour();  
  int minutes = now.minute();
  int seconds = now.second();  


  int h1, h2, m1, m2, s1, s2; // split the numbers into seperate digits using mod math
  h2 = hours % 10;
  h1 = ((hours % 100) - h1) / 10;
  m2 = minutes % 10;
  m1 = ((minutes % 100) - m1) / 10;
  s2 = seconds % 10;
  s1 = ((seconds % 100) - s1) / 10;


  // get digit 0
  int dig0 = (h1); // 10h
  // get digit 1
  int dig1 = (h2); // 1h
  // get digit 2
  int dig2 = (m1); // 10m
  // get digit 3
  int dig3 = (m2); // 1m
  // get digit 4
  int dig4 = (s1); // 10s
  // get digit 5
  int dig5 = (s2); // 1s


  SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0)); // send data to shift registers
  digitalWrite(reg_latch,LOW);
  SPI.transfer(ssddigits[dig0]);
  SPI.transfer(ssddigits[dig1]);
  SPI.transfer(ssddigits[dig2]);
  SPI.transfer(ssddigits[dig3]);
  SPI.transfer(ssddigits[dig4]);
  SPI.transfer(ssddigits[dig5]);
  digitalWrite(reg_latch,HIGH);
  SPI.endTransaction();


 if (now.hour() == 23 && now.minute() == 0 && now.second() == 0) // dim display at 23:00
    {
       brightness = 1;
       digitalWrite(colon_pin, LOW); // set colon to dim
    }


    if (now.hour() == 7 && now.minute() == 0 && now.second() == 0) // resume normal brightness at 7:00
    {
       brightness = 4;
       digitalWrite(colon_pin, HIGH); // set colon to bright.
    }


}


void setTime() // this will run if hour or minute set button is pressed


{
   
  DateTime now = rtc.now(); // this gets the current time from the RTC so it updates after button presses.
  int set_min = now.minute();
  int set_hour  = now.hour();
 
  int h1, h2, m1, m2; // split the numbers into seperate digits using mod math
  h2 = set_hour % 10;
  h1 = ((set_hour % 100) - h1) / 10;
  m2 = set_min % 10;
  m1 = ((set_min % 100) - m1) / 10;


  // get digit 0
  int dig0 = (h1); // 10h
  // get digit 1
  int dig1 = (h2); // 1h
  // get digit 2
  int dig2 = (m1); // 10m
  // get digit 3
  int dig3 = (m2); // 1m


  SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0)); // send data to shift registers
  digitalWrite(reg_latch,LOW);
  SPI.transfer(ssddigits[dig0]);
  SPI.transfer(ssddigits[dig1]);
  SPI.transfer(ssddigits[dig2]);
  SPI.transfer(ssddigits[dig3]);
  SPI.transfer(B00000000); // blank seconds display 10s
  SPI.transfer(B00000001); // blank seconds display 1s & light last decimal point to indicate set mode
  digitalWrite(reg_latch,HIGH);
  SPI.endTransaction();
     
  // code here will increment values above if hour and minute buttons are pressed also sets button_pressed flag to true


  while (button2.uniquePress())
    {
      set_hour++;
      if (set_hour == 24)
      {
        set_hour = 0;
      }
      delay(10); // 10 millisecond delay to avoid spamming the RTC
      rtc.adjust(DateTime(2023, 10, 23, set_hour, set_min)); // write the time to the rtc, dont zero seconds
    }


  while (button3.uniquePress())
    {
      set_min++;


      if (set_min == 60)
      {
        set_min = 0;
      }
      delay(10); // 10 millisecond delay to avoid spamming the RTC
      rtc.adjust(DateTime(2023, 10, 23, set_hour, set_min)); // write the time to the rtc, don't zero seconds
    }  
   
   if (button1.uniquePress())
    {
      DateTime now = rtc.now(); // this gets the current time from the RTC so it updates after button presses.
      int set_min = now.minute();
      int set_hour  = now.hour();
      rtc.adjust(DateTime(2023, 10, 23, set_hour, set_min,0)); // zero seconds when set button is pressed again
      set_mode = 0; // this happens if timeset button is pressed after adjusting the time
      clockDisplay(); // exit function and return to clock display
    }


}


void setBrt() // this changes the brightness logarithmic reversed scale to a more linear one


{
  if (brightness == 1)
  brt = 90;


  else if (brightness == 2)
  brt = 75;


  else if (brightness == 3)
  brt = 50;


  else if (brightness == 4)
  brt = 30;


  else if (brightness == 5)
  brt = 0;
}




Assemble the PCB

20231103_111222.jpg
PCB.jpg
6 digit schematic.jpg
20231125_142100.jpg

Fit the components to the PCB using the schematic as a guide. I would recommend using sockets for the IC's - especially the microcontroller. The PCB can either be bought from my Ebay store (link in supplies) or you can make your own.

*** Addendum to the schematic; the 330ohm resistors should, ideally be 390 ohms instead as this would bring the segment current of the hours and minutes displays to within 1mA of the seconds displays which is around 16mA. With 330ohm resistors there is about 3mA difference which could on some LED displays make the segments displays slightly dimmer than the rest. It wasn't noticeable on mine but I would use 390 ohm resistors instead. ***

For the RTC module check the battery you have fitted as these come with a LIR2032 (rechargeable) or CR2032 (non rechargeable) battery and the charging circuit is always active. It overcharges the lithium cell if LIR2032 fitted and will cause a CR2032 to explode eventually.

The RTC module is fitted to the back of the board with the battery facing outwards as shown.

It is important to either

  • A) disable the battery charging circuit and throw away the LIR2032 and replace it with a CR2032
  • B) replace the battery with a supercapacitor.

To disable the battery charging circuit remove either the diode or resistor as shown in this guide Modifying the RTC module - Camper Control

Power Up and Set the Time

Large Arduino LED Clock Update - time set demo. This wasn't shown in the project video.

To set the time on this clock you press the “set mode button” and the clock will show “SEt” for 2 seconds and blank the seconds display. The decimal point will light on the last digit to indicate the clock is in time set mode. Then use the hour button and minute button till the correct time is shown then finally press set mode again to return to normal operation.

Note that as soon as you press set time to exit setting mode it will zero the seconds. This way the clock can be synchronised to another time source. For example if the current time is 13:56 set the clock to 13:57, wait till the other time source changes to 13:57 then press set mode button.

I will attach a short video showing you how to set the time.


Assemble Into a Case

20231124_120046.jpg
20231124_120058.jpg
20231124_120105.jpg
20231124_120140.jpg
20231124_123140.jpg

Now that you have verified that he clock works it's time to assemble into a case. I got a tea bag selection case from eBay and removed the internal compartments then attached the PCB to a piece of skirting board to add weight and to bring the PCB closer to the glass. The extra weight prevents the case from falling forward as the wight is at the back. Standoffs raised the height to the required level so the LED displays are just below the glass when the lid is closed.

Light grey window tint was applied to the glass to improve readability of the 7 segment displays and to hide the PCB. Holes were drilled to accommodate the power connector which I used a 5.5x2.1mm DC jack so it can be powered from a 12V router wall adaptor.

I mounted the 4 buttons onto a separate PCB and fitted them inside the case. It means opening the lid to set the time but this only need to be done twice a year. Magnets salvaged from an old hard drive and glued in place keeps the lid shut.


Display in Your Room!

20231124_124547.jpg

Now that the clock is complete display it somewhere!