-----
We overheard in a conversation recently something like: "Now that I'm retired I really only focus on what day of the week it is and the general time of day." We then discovered this awful looking Day of the Week Clock and the vision of a more modern version came to mind.
-----
The rig is based around this MakerFocus ESP32 Development Board mainly because it works with the Arduino IDE and has WiFi with a display on board. The code (source below) gets it's time/date from a NTP server and then simply figures out and displays the day of the week and what percentage of the day has past. Evidently this is all the critical information needed to guide the non-working through their day.
-----
To dress up the look a case was 3D printed. After seeing the result we needed to celebrate with a beer. As shown in the video below it was a bit early in the day for a drink, but as the ol' saying goes "It's 71% somewhere!".
-----
Retirement Clock Source Code:
/*
* HelTec Automation(TM) ESP32 Series Dev boards OLED "Retirement Clock"
* Adruino IDE Board Setting: WiFi Kit32, Disabled, 240MHz, 921600, None
*
* "Retirement Clock" shows only Day of Week and percentage time left in the day.
*
* FEB 2022
* Search whiskeytangohotel.com for project details.
*
*/
#include "Arduino.h"
#include "heltec.h"
#include <TimeLib.h>
#include <WiFi.h>
#include <WiFiUdp.h>
const char ssid[] = "xxxxxxx"; // your network SSID (name)
const char pass[] = "xxxxxxx"; // your network password
long Sync_Delay = 60000; // 60000 is sync every ten minutes, We aren't going for precision here. LOL
int DOWNUM = 0; // DOWNUM from NTP (expect 0 - 6)
String DOW = "DOW"; // DOWNUM to a Day of Week String
int hourNUM = 0; // hour() value from NTP
float minuteNUM = 0; // minute() value from NPT
// NTP Servers:
static const char ntpServerName[] = "pool.ntp.org";
const int timeZone = 0; // leave as 0 for UTC. We do the offset math in void setup
WiFiUDP Udp;
unsigned int localPort = 8888; // local port to listen for UDP packets
time_t getNtpTime();
void digitalClockDisplay();
void printDigits(int digits);
void sendNTPpacket(IPAddress &address);
void setup() {
//NTP setup
Serial.begin(115200);
while (!Serial) ; // Needed for Leonardo only
delay(250);
Serial.println("TimeNTP");
Serial.print("Connecting to... ");
Serial.println(ssid);
WiFi.disconnect();
WiFi.mode(WIFI_MODE_STA);
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.print("IP number assigned by DHCP is: ");
Serial.println(WiFi.localIP());
Serial.println("Starting UDP");
Udp.begin(localPort);
Serial.print("Local port: ");
// Serial.println(Udp.localPort());
Serial.println("waiting for sync... ");
setSyncProvider(getNtpTime);
setSyncInterval(Sync_Delay); // defines how often to check NTP time
// Hetec setup
Heltec.begin(true /*DisplayEnable Enable*/, false /*LoRa Disable*/, true /*Serial Enable*/);
Heltec.display->flipScreenVertically();
Heltec.display->setFont(ArialMT_Plain_24);
} // end setup link
time_t prevDisplay = 0; // when the digital clock was last displayed timer
void loop() { // main program loop
if (timeStatus() != timeNotSet) {
if (now() != prevDisplay) { //update the display only if time has changed
prevDisplay = now();
}
}
// Parse time info and send info to serial monitor (for debug)
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(".");
Serial.print(month());
Serial.print(".");
Serial.print(year());
Serial.print(" : ");
if (int(year()) < 2022) {
Serial.print(String(int(year())) + " is the NTP server year. That aint right, so try again!!!");
Serial.println();
setup();
} // the NTP server return something 'wierd' (probably 1970) so try again
//Covert Time in percent of day completed. Midnight Local = 0%, Noon Local = 50%
minuteNUM = minute();
hourNUM = int(hour());
// manually adjust hourNUM and minuteNUM for debug
// hourNUM = 5; // valid values are INTERGERS 0 thru 24
// minuteNUM = 49; // valid values are INTEGERS 0 thru 60
minuteNUM = minuteNUM / 60; // covert minutes to 'fraction'
hourNUM = hourNUM - 6; // hourNUM is in UTC. Adjust to local. -6 = CST, -5 = CDT, 0 = UTC
DOWNUM = int(weekday());
// Calculate percentage of the day pasted into 'progress'
int progress = round( ( ((hourNUM + minuteNUM)) / 24 ) * 100 );
if (progress < 0) { // it's past UTC midnight and the timezone offset created a negative %
progress = 100 + progress;
DOWNUM = DOWNUM - 1;
} //end progress < 0
// Convert DOWNUM into printable String. Sunday is 1
if (DOWNUM == 1) { DOW = "SUN"; }
if (DOWNUM == 2) { DOW = "MON"; }
if (DOWNUM == 3) { DOW = "TUE"; }
if (DOWNUM == 4) { DOW = "WED"; }
if (DOWNUM == 5) { DOW = "THU"; }
if (DOWNUM == 6) { DOW = "FRI"; }
if (DOWNUM == 7) { DOW = "SAT"; }
Serial.print(String(int(weekday())) + " is " + DOW);
Serial.println();
Serial.print(String(hourNUM) + " + " + String(minuteNUM) + " = " + String(progress) + "%");
Serial.println();
// clear the display
Heltec.display->clear();
// Draw the Precent of Day pasted onto progress bar
Heltec.display->drawProgressBar(0, 0, 127, 20, progress);
// Label the Day Of Week and percentage as String
Heltec.display->setTextAlignment(TEXT_ALIGN_CENTER);
Heltec.display->drawString(60, 31, DOW + " " + String(progress) + "%");
// Write buffer to Heltec display
Heltec.display->display();
delay(5000); // wait some. we are in no hurry to update the screen
} // end void loop main program
void printDigits(int digits)
{
// utility for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if (digits < 10)
Serial.print('0');
Serial.print(digits);
}
/*-------- NTP code ----------*/
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
time_t getNtpTime()
{
IPAddress ntpServerIP; // NTP server's ip address
while (Udp.parsePacket() > 0) ; // discard any previously received packets
Serial.println("Transmit NTP Request");
// get a random server from the pool
WiFi.hostByName(ntpServerName, ntpServerIP);
Serial.print(ntpServerName);
Serial.print(": ");
Serial.println(ntpServerIP);
sendNTPpacket(ntpServerIP);
uint32_t beginWait = millis();
while (millis() - beginWait < 1500) {
int size = Udp.parsePacket();
if (size >= NTP_PACKET_SIZE) {
Serial.println("Receive NTP Response");
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
secsSince1900 |= (unsigned long)packetBuffer[43];
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
}
}
Serial.println("No NTP Response :-(");
return 0; // return 0 if unable to get the time
}
// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address)
{
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket(address, 123); //NTP requests are to port 123
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
}
-----