I've posted some complicated clocks that chime, provide time, temp, event notification, etc., but wanted a simple clock that would set itself and just provide the time in HH:MM format but very accurately. This clock just connects to your Wi-Fi network, gets the time and then sets the clock. There is one button that forces a reconnect and update of the time on demand, but I've found the Adafruit DS3231 to be so accurate that there's no discernible 'drift'. The processor chip is the ESP 8266, so the whole clock including processor, RTC and Display (1.2" seven segment LED with I2C backpack) costs less than $50. The enclosure is a 4" plastic 'stand' from the Container Store costing $9.
There's some interesting code for Daylight Savings time detection and adjustment as well use of the colons, blinking, etc. for status indication
/*
Simple NTP clock with autoset of RTC DS3231 -- uses 8266 and 7 Segment panel
Sets itself using NTP time and then uses RTC for time updates, display
Adjusts for Daylight Savings time
On Power up will revert to saved RTC time unless battery is removed
[Note: the RTC3231 is so accurate that I've found no 'true up' is required even after months
but you could add one either with a pushbutton or a check for change of month or whatever]
Indicators:
1. Shows all "8" to indicate power, working, etc.
2. Show UTC offset (e.g., -8 for Pacific timezone)
3. Blinks whole display including 8888, colons, while waiting for wifi
4. Normal display with middle colon blinking to show time updating
5. Rapid blink of two left hand colons while regetting and reloading wifi time (either power lost or button pushed)
6. Upper left hand colon dot becomes "PM" marker
adapted from EasyNTPClient example: NodeMCU
For more details see: https://github.com/aharshac/EasyNTPClient
*/
#include <Wire.h>
#include <ESP8266WiFi.h>
#include <EasyNTPClient.h>
#include <WiFiUdp.h>
#include <TimeLib.h>
#include "RTClib.h"
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
const char *SSID_7912 = "xxxxx"; // WiFi SSID goes here
const char *Password_7912 = "yyyyyyyyy"; // Password goes here
const int MyOffset = -8; // correction to UTC for your timezone goes here (e.g., Pacific TZ is -8)
#define onesecond 1000 // milliseconds to wait
void DDTv(String st, int vt) { // Print descriptor and value
Serial.print(" ");
Serial.print(st);
Serial.print(" ");
Serial.print(vt);}
// debugging tools
void DDTl(String st, int vt) { // Print descriptor and value and new line
DDTv(st, vt);
Serial.println(" ");}
void DDTs(String st) { // Print string
Serial.print(st);}
void DDTt(String st) { // Print string and new line
Serial.println(st);}
void DDTf(String st, float fi) { // Print descriptor and floating point value and new line
Serial.print(" ");
Serial.print(st);
Serial.print(" ");
Serial.println(fi,4);}
void DDTsl(String st, String vt) { // Print descriptor and string value and new line
Serial.print(st);
Serial.println(vt);}
unsigned long t_unix_date;
int MyYear;
int MyMonth;
int MyDay;
int MyHour;
int MyMinute;
int MySecond;
int MyWeekDay;
char MyDaysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
bool BlinkFlag = true;
byte Colon = 0; // mask for colons
const byte Colon_UL = 0x04; // Upper left dot
const byte Colon_LL = 0x08; // Lower left dot
const byte Colon_Middle = 0x02; // middle colon
const byte Colon_Blank = 0x00; //no colon
int Print_Hour = 0; //printed hours without am/pm
#define sunday 0 // used for the daylight savings time routine
#define monday 1 // just for readability
#define tuesday 2 // Note well that the 8266 routines return 1-7, NOT 0-6 like the arduino rtc logic
#define wednesday 3 // but since we use we load the RTC from the 8266 and don't use the 8266's "day of week",
#define thursday 4 // and instead we use the RTC3231 time and date for the DST logic, and must use 0-6
#define friday 5 // to match its 0-6 week for returned day of week
#define saturday 6
#define january 1
#define february 2
#define march 3
#define april 4
#define may 5
#define june 6
#define july 7
#define august 8
#define september 9
#define october 10
#define november 11
#define december 12
RTC_DS3231 rtc; // Initialize for Adafruit DS3231 RTC real time clock
//ESP 8266 pin analogs
/*
D0 = GPIO16;
D1 = GPIO5;
D2 = GPIO4;
D3 = GPIO0;
D4 = GPIO2;
D5 = GPIO14;
D6 = GPIO12;
D7 = GPIO13;
D8 = GPIO15;
D9 = GPIO3;
D10 = GPIO1;
LED_BUILTIN = GPIO16 (auxiliary constant for the board LED, not a board pin);
*/
#define SDA 4 // 8266 pin D2
#define SCL 5 // 8266 pin D1
#define Reset_Pin 0 // 8266 pin D3 Used to force a wifi NTP time update
WiFiUDP udp; // start wifi
EasyNTPClient ntpClient(udp, "pool.ntp.org", ((MyOffset*60*60)+(0))); // initialize wifi/NTP routine
Adafruit_7segment matrix = Adafruit_7segment(); // set up seven segment LED
void setup(){
Serial.begin(9600);
matrix.begin(0x70);
Wire.begin(SDA, SCL); // join i2c bus with address defined (int sda, int scl);
matrix.setBrightness(15);
matrix.print(8888, DEC); // (1) first show the full display (Indicates Power)
matrix.drawColon(false);
matrix.writeDisplay();
delay(onesecond*2); // wait two seconds
if (! rtc.begin()) { // check that clock is there
while (1); }
matrix.print(MyOffset); //(2) second show UTC offset (LED Indicates RTC is there)
matrix.writeDisplay();
delay(onesecond*2); // and wait (indicator the clock IS there)
// if(true) { //un-comment this line and comment the next to force a reload upon startup
if (rtc.lostPower()) { // if power lost force wifi NTP setup, else load 'current' values and go
ConnectWiFi(); // did lose power, so connect to wifi network (LED Indicates while blinking that not connected)
Load_DateTime(); // Note: to force a wifi NTP check each time, just leave out the battery!
} // end of reset time rather than just use last time
pinMode(Reset_Pin, INPUT_PULLUP); // initialize pin to listen to for forced reset
}// end of setup
void loop() {
DateTime now = rtc.now(); //load time from the RTC
MyYear= now.year();
MyMonth = now.month();
MyDay = now.day();
MyHour = now.hour();
MyMinute= now.minute();
MySecond = now.second();
MyWeekDay= now.dayOfTheWeek();
/* // uncomment for debugging
Serial.print("Today's Date and Time: ");
Serial.print(MyDaysOfTheWeek[MyWeekDay]);
Serial.print(" ");
Serial.print(MyWeekDay);
Serial.print(" ");
Serial.print(MyMonth);
Serial.print("/");
Serial.print(MyDay);
Serial.print("/");
Serial.print(MyYear);
Serial.print(" ");
Serial.print(MyHour);
Serial.print(":");
if (MyMinute < 10) {Serial.print("0");}
Serial.print(MyMinute);
Serial.print(":");
if (MySecond < 10) {Serial.print("0");}
Serial.print(MySecond);
Serial.println(" ");
if (DSTInEffect()) {Serial.println("DST On");}
*/
//Print hh:mm to LED
if (DSTInEffect()){MyHour = MyHour + 1;} // Adjust for DST (this only works because we don't care about hour vs. date skew at midnight
if (MyHour > 12) {Print_Hour = MyHour - 12;} else {Print_Hour = MyHour;} // Adjust for am,pm
if (Print_Hour == 0) {Print_Hour = 12;} // and print "12" for "00" (midnight)
if (MySecond%2) {Colon = Colon | Colon_Middle;} else {Colon = Colon_Blank;} // center colon only on even seconds else clear it
if (MyHour >=12) {Colon = Colon | Colon_UL;} // if its PM then light upper left colon
matrix.print((Print_Hour*100)+MyMinute, DEC);
matrix.drawColon(true); // we want the colon
matrix.writeDigitRaw(2, Colon); // draw whatver colons are needed
matrix.writeDisplay(); // update display
if ( !digitalRead(Reset_Pin)) { // read button
ConnectWiFi(); // Reset button pushed, so connect to wifi network (LED Indicates while blinking that not connected)
Load_DateTime(); // and reload the time
delay(onesecond);} // give time to get finger off the button
delay(onesecond); // wait constant one second between calls for display (mostly for flashing colon)
} // end of loop
bool DSTInEffect(){ // returns true if DST in effect
// by examing month,day and hour (first Sunday in March at 2am, 2nd Sunday in November at 2am)
switch (MyMonth){
case april: // April thru October, we are in DST if its used
case may:
case june:
case july:
case august:
case september:
case october:
return true;
case december: // December thru February always NOT in DST if used
case january:
case february:
return false;
case march: // in March, we ARE in DST on 2nd Sunday
if (MyDay < 8) {return false;} // days 1 thru 7 can't be 2nd sunday
if (MyDay >13){return true;} // days 14 and beyond always WILL be DST
switch (MyWeekDay){
case sunday:
if ((MyDay >= 8 && MyDay <= 13) && (MyHour >= 2) ) {return true;} else return false; //Sunday AND after 2:00am
case monday: if (MyDay >=9 && MyDay <= 13) {return true;} else return false;
case tuesday: if (MyDay >=10 && MyDay <=13) {return true;} else return false;
case wednesday: if (MyDay >=11 && MyDay <=13) {return true;} else return false;
case thursday: if (MyDay >=12 && MyDay <=13) {return true;} else return false;
case friday: if (MyDay == 13) {return true;} else return false;
case saturday: return false;
} // end of case on the day for March
case november: // in November, switch off of DST on first Sunday
if (MyDay > 7) {return false;} // Days 8 through end of month have to be non-DST
switch (MyWeekDay){ // use day of week and day of month for days 1-7
case sunday: if (MyHour <2 ) {return true;} else return false; // if its Sunday and its after 2:00am, then adjust
case monday: if (MyDay >=2) {return false;} else return true;
case tuesday: if (MyDay >=3 ) {return false;} else return true;
case wednesday: if (MyDay >=4) {return false;} else return true;
case thursday: if (MyDay >=5 ) {return false;} else return true;
case friday: if (MyDay >=6) {return false;} else return true;
case saturday: if (MyDay ==7){return false;} else return true;
} // end of case on the day for November
} // end of case on month
} // end of DSTInEffect
void ConnectWiFi(){ // routine to connect to wifi network
while (WiFi.status() != WL_CONNECTED) { //(3) wait til connected and indicate with blink
matrix.blinkRate(2); // 0 = none, 1-3 = rate
matrix.print(8888, DEC); // and also nonsensical display til we have one
if (BlinkFlag) {matrix.writeDigitRaw(2,Colon_UL | Colon_LL | Colon_Middle); //lower, upper and middle colon on
} else {matrix.writeDigitRaw(2,Colon_Blank);} // and off in sync with middle
matrix.writeDisplay();
BlinkFlag = !BlinkFlag;
delay(onesecond);
} // end of 'not connected'
matrix.blinkRate(0); //(4) stop blinking when we've made it and go to actual time display
} // end of connect to Wifi
void Load_DateTime(){
t_unix_date = ntpClient.getUnixTime(); // get the internet time
MyYear = year(t_unix_date); // load all the date and time stuff up
MyMonth = month(t_unix_date);
MyDay = day(t_unix_date);
MyHour = hour(t_unix_date);
MyMinute = minute(t_unix_date);
MySecond = second(t_unix_date);
MyWeekDay = weekday(t_unix_date);
rtc.adjust(DateTime(MyYear, MyMonth, MyDay, MyHour, MyMinute, MySecond)); // and reset RTC
for (int i=0; i<12; i++) { //(5) blink two left colons to indicate reloaded time
if (BlinkFlag) {matrix.writeDigitRaw(2,Colon_UL | Colon_LL ); //lower, upper and middle colon on
} else {matrix.writeDigitRaw(2,Colon_Blank);} // and off in sync with middle
matrix.writeDisplay();
DDTv("BlinkFlag",BlinkFlag);
BlinkFlag = !BlinkFlag;
delay(onesecond/3);}
} // end of Load_DateTime
Comments
Please log in or sign up to comment.