A digital clock using one hundred 5 volt WS2811 addressable RGB LEDs on a 20X5 grid, an Arduino Uno to control everything and a DS1302 RTC real time clock with it's own battery to keep track of the time.
The Arduino talks to the clock module and the 2004 LCD screen using I2C (two wire communication) on Analogue pins 4 and 5, and the addressable LEDs using only one data line on pin 3.
With different programming, this would make a great text scrolling display.
Watch the video for more information.
Parts list -
WS2811 5V addressable RGB LEDs 100 off.
Arduino Uno.
DS1302 RTC (Real Time Clock) Module.
5V Buck step down power converter.
4 Line LCD 2004 Display using I2C.
Barrel Jack Power Socket.
Various wire.
Bits of wood.
// ***** LED Clock by Richard Creese 2017 *****
//
// LEDS 100 RGB WS2811 in a 20 x 5 grid, Needs own 5v 5A power surply and with Gnd and Data Pin 3 to Arduino
// LCD 20 x 4 display using I2C
// RTC DS3231 Real Time Clock using I2C
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "FastLED.h"
#define DS3231_I2C_ADDRESS 0x68
#define NUM_LEDS 100
#define DATA_PIN 3
CRGB leds[NUM_LEDS];
byte BRIGHT = 64;
CHSV LEDCOLOUR( 1,255,BRIGHT );// (Colour-Hue,Saturation,Brightness)
LiquidCrystal_I2C lcd(0x27, 20, 4);
//User defined chars for LCD
byte N0[8] = {
B11111,
B10001,
B10001,
B10001,
B10001,
B10001,
B10001,
B10001,
};
byte N1[8] = {
B10001,
B10001,
B10001,
B10001,
B10001,
B10001,
B10001,
B11111,
};
byte N2[8] = {
B00001,
B00001,
B00001,
B00001,
B00001,
B00001,
B00001,
B00001,
};
byte N3[8] = {
B11111,
B00001,
B00001,
B00001,
B00001,
B00001,
B00001,
B00001,
};
byte N4[8] = {
B11111,
B00001,
B00001,
B00001,
B00001,
B00001,
B00001,
B11111,
};
byte N5[8] = {
B11111,
B10000,
B10000,
B10000,
B10000,
B10000,
B10000,
B11111,
};
byte N6[8] = {
B11111,
B10001,
B10001,
B10001,
B10001,
B10001,
B10001,
B11111,
};
byte N7[8] = {
B00001,
B00001,
B00001,
B00001,
B00001,
B00001,
B00001,
B11111,
};
// Letter 'C' For LEDs
boolean C[5][6]={
{1,0,0,1,1,0},
{0,0,1,0,0,1},
{0,0,1,0,0,0},
{0,0,1,0,0,1},
{0,0,0,1,1,0}
};
// Numbers for LEDs
boolean NN[10][5][3] = {
{
{1,1,1},// Zero
{1,0,1},
{1,0,1},
{1,0,1},
{1,1,1}
},
{
{1,1,0},// One
{0,1,0},
{0,1,0},
{0,1,0},
{1,1,1}
},
{
{1,1,1},// Two
{0,0,1},
{0,1,1},
{1,0,0},
{1,1,1}
},
{
{1,1,1},// Three
{0,0,1},
{1,1,1},
{0,0,1},
{1,1,1}
},
{
{1,0,1},// Four
{1,0,1},
{1,1,1},
{0,0,1},
{0,0,1}
},
{
{1,1,1},// Five
{1,0,0},
{1,1,1},
{0,0,1},
{1,1,1}
},
{
{1,1,1},// Six
{1,0,0},
{1,1,1},
{1,0,1},
{1,1,1}
},
{
{1,1,1},// Seven
{0,0,1},
{0,0,1},
{0,0,1},
{0,0,1}
},
{
{1,1,1},// Eight
{1,0,1},
{1,1,1},
{1,0,1},
{1,1,1}
},
{
{1,1,1},// Nine
{1,0,1},
{1,1,1},
{0,0,1},
{0,0,1}
}
};
int LEFT[14] = { 0,359,328,300,269,239,208,178,147,116,86,55,25,380};
int DAYSLEFT = 365;
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
unsigned long TIMER;
byte tMSB, tLSB;
float TEMP;
byte M = 0;
// 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) );
}
void setup()
{
Wire.begin();
//Serial.begin(9600);
lcd.begin();
lcd.print("Loading...");
delay(2000);
lcd.setCursor(0,1);
lcd.print("LED Test ...");
lcd.createChar(0, N0);
lcd.createChar(1, N1);
lcd.createChar(2, N2);
lcd.createChar(3, N3);
lcd.createChar(4, N4);
lcd.createChar(5, N5);
lcd.createChar(6, N6);
lcd.createChar(7, N7);
FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
for(int i = 0; i < NUM_LEDS; i = i + 1) {// Visual check all LEDS working
leds[i] = CRGB::White;
FastLED.show();
delay(50);
leds[i] = CRGB::Black;
}// Next i
lcd.begin();
// ********************************************************
// set the initial time here:
// DS3231 seconds, minutes, hours, day, date, month, year
// setDS3231time(0,40,15,1,28,1,18);
// ********************************************************
}
void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte
dayOfMonth, byte month, byte year)
{
// sets time and date data to DS3231
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0); // set next input to start at the seconds register
Wire.write(decToBcd(second)); // set seconds
Wire.write(decToBcd(minute)); // set minutes
Wire.write(decToBcd(hour)); // set hours
Wire.write(decToBcd(dayOfWeek)); // set day of week (1=Sunday, 7=Saturday)
Wire.write(decToBcd(dayOfMonth)); // set date (1 to 31)
Wire.write(decToBcd(month)); // set month
Wire.write(decToBcd(year)); // set year (0 to 99)
Wire.endTransmission();
}
void readDS3231time(byte *second,
byte *minute,
byte *hour,
byte *dayOfWeek,
byte *dayOfMonth,
byte *month,
byte *year)
{
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0); // set DS3231 register pointer to 00h
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
// request seven bytes of data from DS3231 starting from register 00h
*second = bcdToDec(Wire.read() & 0x7f);
*minute = bcdToDec(Wire.read());
*hour = bcdToDec(Wire.read() & 0x3f);
*dayOfWeek = bcdToDec(Wire.read());
*dayOfMonth = bcdToDec(Wire.read());
*month = bcdToDec(Wire.read());
*year = bcdToDec(Wire.read());
}
void loop()
{
int T = 0 ; // Temp Tempreture value
TIMER=millis();// Start timer
// **** Get Time from RTC ****
readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
DAYSLEFT = LEFT[month] - dayOfMonth;
if ( DAYSLEFT <= -1 ){ DAYSLEFT = LEFT[13] + DAYSLEFT; }
if ( second == 15 ){ // Display tempreture at 15 second on LEDS
CLEARLEDS();
T = READTEMP();
//T=35;// Testing Tempreture colour
LEDCOLOUR.hue = int( 160 + ( T * 2.8 ));
LEDCOLOUR.val = BRIGHT;
LEDNUMBER( int( T ),3);
for (byte Y=0; Y<=4; Y=Y+1){// Display letter 'C'
for (byte X=0; X<=5; X=X+1){
if(C[Y][X]==1){
leds[11+X+(Y*20)] = LEDCOLOUR ;
};
}// Next X
}// Next Y
FastLED.show();// Display Tempreture on LEDS
while(millis() < TIMER + 5000)// Wait total of five seconds
{LCDSCREEN();// Keep dsplaying time and date on LCD
delay(999);// wait one second
readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);//Check RTC clock
}
M = 61; // Force it to show time when finished
}// End if
if ( second == 45 ){ // Display days left until Christmas at 45 second on LEDS
CLEARLEDS();
LEDCOLOUR.hue = 128;
LEDCOLOUR.val = BRIGHT;
//DAYSLEFT = LEFT[month] - dayOfMonth;
//if ( DAYSLEFT <= -1 ){ DAYSLEFT = LEFT[13] + DAYSLEFT; }
LEDNUMBER( DAYSLEFT,8 );
FastLED.show();// Display days left on LEDS
while(millis() < TIMER + 5000)// Wait total of five seconds
{LCDSCREEN();// Keep dsplaying time and date on LCD
delay(999);// wait one second
readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);//Check RTC clock
}
M = 61; // Force it to show time when finished
}// End if
if ( second == 30 ){ // Display date at 30 second on LEDS
CLEARLEDS();
LEDCOLOUR.hue = 128;
LEDCOLOUR.val = BRIGHT;
LEDNUMBER( dayOfMonth,0 );
LEDNUMBER( month,13);
leds[10] = LEDCOLOUR;
leds[30] = LEDCOLOUR;
leds[49] = LEDCOLOUR;
leds[68] = LEDCOLOUR;
leds[88] = LEDCOLOUR;
FastLED.show();// Display days left on LEDS
while(millis() < TIMER + 5000)// Wait total of five seconds
{LCDSCREEN();// Keep dsplaying time and date on LCD
delay(999);// wait one second
readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);//Check RTC clock
}
CLEARLEDS();// Display year on LEDS
LEDNUMBER( 20, 3 );
LEDNUMBER( year,11 );
FastLED.show();// Display days left on LEDS
while(millis() < TIMER + 10000)// Wait total of five seconds
{LCDSCREEN();// Keep dsplaying time and date on LCD
delay(999);// wait one second
readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);//Check RTC clock
}
M = 61; // Force it to show time when finished
}// End if
TIME();// Display time on LEDS
LCDSCREEN();// Display time and date on LCD
leds[29]=LEDCOLOUR;// Display dots
leds[69]=LEDCOLOUR;
FastLED.show();
while(millis() < TIMER + 500){ delay(10); }// Half second from timer start
leds[29]=CRGB(0,0,0);// Erase dots;
leds[69]=CRGB(0,0,0);;
FastLED.show();
delay(500); // every Half second
}
float READTEMP()
{
//temp registers (11h-12h) get updated automatically every 64s
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0x11);
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 2);
if(Wire.available()) {
tMSB = Wire.read(); //2's complement int portion
tLSB = Wire.read(); //fraction portion
TEMP = (tMSB & B01111111); //do 2's math on Tmsb
TEMP += ( (tLSB >> 6) * 0.25 ); //only care about bits 7 & 8
}
else {
//ERROR
}
return TEMP;
}
void BIGNUMBER(byte V, byte P){
int TENS = 0;
int UNITS = 0;
byte H = 0; // Height on display
TENS = V / 10;
UNITS = V % 10;
lcd.setCursor(P,H);
switch(TENS){
case 0:
lcd.write(byte(0));
lcd.setCursor(P,H+1);
lcd.write(byte(1));
break;
case 1:
lcd.write(byte(2));
lcd.setCursor(P,H+1);
lcd.write(byte(2));
break;
case 2:
lcd.write(byte(3));
lcd.setCursor(P,H+1);
lcd.write(byte(5));
break;
case 3:
lcd.write(byte(3));
lcd.setCursor(P,H+1);
lcd.write(byte(4));
break;
case 4:
lcd.write(byte(1));
lcd.setCursor(P,H+1);
lcd.write(byte(2));
break;
case 5:
lcd.write(byte(5));
lcd.setCursor(P,H+1);
lcd.write(byte(7));
break;
case 6:
lcd.write(byte(5));
lcd.setCursor(P,H+1);
lcd.write(byte(1));
break;
case 7:
lcd.write(byte(3));
lcd.setCursor(P,H+1);
lcd.write(byte(2));
break;
case 8:
lcd.write(byte(6));
lcd.setCursor(P,H+1);
lcd.write(byte(1));
break;
case 9:
lcd.write(byte(6));
lcd.setCursor(P,H+1);
lcd.write(byte(7));
break;
}
P=P+1;
lcd.setCursor(P,H);
switch(UNITS){
case 0:
lcd.write(byte(0));
lcd.setCursor(P,H+1);
lcd.write(byte(1));
break;
case 1:
lcd.write(byte(2));
lcd.setCursor(P,H+1);
lcd.write(byte(2));
break;
case 2:
lcd.write(byte(3));
lcd.setCursor(P,H+1);
lcd.write(byte(5));
break;
case 3:
lcd.write(byte(3));
lcd.setCursor(P,H+1);
lcd.write(byte(4));
break;
case 4:
lcd.write(byte(1));
lcd.setCursor(P,H+1);
lcd.write(byte(2));
break;
case 5:
lcd.write(byte(5));
lcd.setCursor(P,H+1);
lcd.write(byte(7));
break;
case 6:
lcd.write(byte(5));
lcd.setCursor(P,H+1);
lcd.write(byte(1));
break;
case 7:
lcd.write(byte(3));
lcd.setCursor(P,H+1);
lcd.write(byte(2));
break;
case 8:
lcd.write(byte(6));
lcd.setCursor(P,H+1);
lcd.write(byte(1));
break;
case 9:
lcd.write(byte(6));
lcd.setCursor(P,H+1);
lcd.write(byte(7));
break;
}
}
void LCDSCREEN(){
BIGNUMBER(hour,0);
BIGNUMBER(minute,3);
BIGNUMBER(second,6);
// **** Display Date LCD ****
lcd.setCursor(10,1);
if (dayOfMonth<10) { lcd.print("0"); }
lcd.print(dayOfMonth, DEC);
lcd.print(" ");
if (month<10) { lcd.print("0"); }
lcd.print(month, DEC);
// **** Display Year LCD ****
lcd.setCursor(16,1);
lcd.print("20");
lcd.print(year);
// **** Display Day LCD ****
lcd.setCursor(10,0);
switch(dayOfWeek){
case 1:;
lcd.print("Sunday ");
break;
case 2:
lcd.print("Monday ");
break;
case 3:
lcd.print("Tuesday ");
break;
case 4:
lcd.print("Wednesday");
break;
case 5:
lcd.print("Thrsday ");
break;
case 6:
lcd.print("Friday ");
break;
case 7:
lcd.print("Saturday ");;
break;
}
// **** Display Temprature LCD ****
lcd.setCursor(0,3);
lcd.print("Temprature ");
lcd.print(READTEMP());
lcd.print("C ");
}
void TIME(){
int hh = 0;
if( M != minute){ // Only Update LEDS if Minutes Change
//if(hour>12){ hour=hour-12;}// Change to 12 hour clock
CLEARLEDS(); // Clear LED Display
//LEDCOLOUR.hue=int((255/59)*minute); //Old maths
hh = int( (minute*60 + second) / 14.06 );
LEDCOLOUR.hue = hh;
lcd.setCursor(0,2);
//lcd.print ("Colour : ");
//lcd.print ( hh );// Display current colour
//DAYSLEFT = LEFT[month] - dayOfMonth;
//if ( DAYSLEFT <= -1 ){ DAYSLEFT = LEFT[13] + DAYSLEFT; }
lcd.print (DAYSLEFT);
lcd.print (" Days until xmas");
LEDCOLOUR.val = BRIGHT;
LEDNUMBER(hour,1);
LEDNUMBER(minute,11);
M = minute;
FastLED.show(); // Display it
}// End If
}// End TIME
void LEDNUMBER(int V, byte P){
int HUNDRADS =0;
int TENS = 0;
int UNITS = 0;
HUNDRADS = V / 100;
V = V - (HUNDRADS * 100);
TENS = V / 10;
UNITS = V % 10;
// ***** Display TENS as LEDS *****
for (byte Y=0; Y<=4; Y=Y+1){
for (byte X=0; X<=2; X=X+1){
if(NN[TENS][Y][X]==1){
leds[P+X+(Y*20)] = LEDCOLOUR ;
};
}// Next X
}// Next Y
// ***** Display UNITS as LEDS *****
for (byte Y=0; Y<=4; Y=Y+1){
for (byte X=0; X<=2; X=X+1){
if(NN[UNITS][Y][X]==1){
leds[P+4+X+(Y*20)] = LEDCOLOUR ;
};
}// Next X
}// Next Y
// ****** Display HUNDRADS if any *****
if (HUNDRADS >= 1 ){
for (byte Y=0; Y<=4; Y=Y+1){
for (byte X=0; X<=2; X=X+1){
if(NN[HUNDRADS][Y][X]==1){
leds[P-4+X+(Y*20)] = LEDCOLOUR ;
};// End if
}// Next X
}// Next Y
}// End if
}
void CLEARLEDS(){ // ***** All LEDS to black *******
for (int i=0; i <= 99; i=i+1){
leds[i] = CRGB::Black;
}// Next i
FastLED.show();
}
Downloadable Ardiuno code and wiring digram for the LED Clock project. Click links below. These are stored on Google Drive and I think you might need an account.
CNC program for drilling holes G-Code.txt