Auto-BST for LED Clock
I’ve finally got around to adding British Summer Time detection to my LED matrix clock, I’ve also reduced flash wear and connection time by using the new persistent() method from the WiFi library. I’m still saving the UTC time to the RTC module, just displaying +/- the hour. The LiFePO4 battery is still putting out over 3.1v after 2 months.
The final code is below:
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include "LedControl.h"
#include <Wire.h>
#include <RTClib.h>
// ntp server pool
IPAddress timeServerIP;
// ntp time stamp is in the first 48 bytes of the message
const int NTP_PACKET_SIZE = 48;
// buffer to hold incoming and outgoing packets
byte packetBuffer[NTP_PACKET_SIZE];
// create a udp instance
WiFiUDP udp;
// data, clock, cs, numdevices
LedControl lc = LedControl(D7,D5,D6,4);
// ds3231 constuctor
RTC_DS3231 RTC;
const int num[10][8] = {
{0x00,0x78,0xcc,0xec,0xfc,0xdc,0xcc,0x78}, // zero
{0x00,0xfc,0x30,0x30,0x30,0x30,0xf0,0x30}, // one
{0x00,0xfc,0xcc,0x60,0x38,0x0c,0xcc,0x78}, // two
{0x00,0x78,0xcc,0x0c,0x38,0x0c,0xcc,0x78}, // three
{0x00,0x0c,0x0c,0xfe,0xcc,0x6c,0x3c,0x1c}, // four
{0x00,0x78,0xcc,0x0c,0x0c,0xf8,0xc0,0xfc}, // five
{0x00,0x78,0xcc,0xcc,0xf8,0xc0,0x60,0x38}, // six
{0x00,0x60,0x60,0x30,0x18,0x0c,0xcc,0xfc}, // seven
{0x00,0x78,0xcc,0xcc,0x78,0xcc,0xcc,0x78}, // eight
{0x00,0x70,0x18,0x0c,0x7c,0xcc,0xcc,0x78} // nine
};
void drawNum(int number, int display)
{
lc.setColumn(display, 0, num[number][0]);
lc.setColumn(display, 1, num[number][1]);
lc.setColumn(display, 2, num[number][2]);
lc.setColumn(display, 3, num[number][3]);
lc.setColumn(display, 4, num[number][4]);
lc.setColumn(display, 5, num[number][5]);
lc.setColumn(display, 6, num[number][6]);
lc.setColumn(display, 7, num[number][7]);
}
bool isBST(int year, int month, int day, int hour)
{
// bst begins at 01:00 gmt on the last sunday of march
// and ends at 01:00 gmt (02:00 bst) on the last sunday of october
// january, february, and november are out
if (month < 3 || month > 10) { return false; }
// april to september are in
if (month > 3 && month < 10) { return true; }
// last sunday of march
int lastMarSunday = (31 - (5* year /4 + 4) % 7);
// last sunday of october
int lastOctSunday = (31 - (5 * year /4 + 1) % 7);
// in march we are bst if its past 1am gmt on the last sunday in the month
if (month == 3)
{
if (day > lastMarSunday)
{
return true;
}
if (day < lastMarSunday)
{
return false;
}
if (hour < 1)
{
return false;
}
return true;
}
// in october we must be before 1am gmt (2am bst) on the last sunday to be bst
if (month == 10)
{
if (day < lastOctSunday)
{
return true;
}
if (day > lastOctSunday)
{
return false;
}
if (hour >= 1)
{
return false;
}
return true;
}
}
// send an ntp request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address)
{
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
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, send request
udp.beginPacket(address, 123);
udp.write(packetBuffer, NTP_PACKET_SIZE);
udp.endPacket();
}
void displayDate(unsigned long unixtime)
{
// power up led matrices
for (int i=0; i<4; i++)
{
lc.shutdown(i,false); // come out of powersaving
lc.setIntensity(i,5); // set brightness 0-15
lc.clearDisplay(i); // clear display
}
// handle british summer time
DateTime nowntp = unixtime;
int myhour;
if (isBST(nowntp.year(), nowntp.month(), nowntp.day(), nowntp.hour()))
{
myhour = ((unixtime % 86400L) / 3600) + 1; // bst
}
else
{
myhour = (unixtime % 86400L) / 3600; // utc
}
// print hour to led
if (myhour == 24)
{
drawNum(0,0);
drawNum(0,1);
}
else
{
drawNum(myhour/10,0);
drawNum(myhour%10,1);
}
// print minute to led
int myminute = (unixtime % 3600) / 60;
if (myminute < 10)
{
drawNum(0,2);
}
else
{
drawNum(myminute/10,2);
}
drawNum(myminute%10,3);
}
void gotoSleep()
{
for (int i=0; i<4; i++)
{
lc.clearDisplay(i);
lc.setIntensity(i,0);
lc.shutdown(i,true);
}
// latch cs pin and pause to work around display0 being stuck high on sleep problem
digitalWrite(D6, LOW);
delay(2000);
// sleep mcu forever
ESP.deepSleep(0);
}
void setup()
{
// setup ds3231
Wire.begin();
RTC.begin();
// display rtc time on led matrices
DateTime now = RTC.now();
displayDate(now.unixtime());
// connect to wifi network
if (WiFi.SSID() != "myssid") {
WiFi.begin("myssid", "mypassword");
WiFi.persistent(true);
WiFi.setAutoConnect(true);
WiFi.setAutoReconnect(true);
}
// static ip, gateway, netmask
WiFi.config(IPAddress(192, 168, 1, 2), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0));
// connect - could drain battery if never connects to wifi
int wifitries = 0;
while (WiFi.status() != WL_CONNECTED)
{
// give it a moment
delay(1000);
// only try 5 times before sleeping
wifitries++;
if (wifitries >5)
{
gotoSleep();
}
}
udp.begin(2390);
// get a random server from the pool
WiFi.hostByName("europe.pool.ntp.org", timeServerIP);
int cb = 0;
int ntptries = 0;
while (!cb)
{
// send an ntp packet to a time server
sendNTPpacket(timeServerIP);
// wait to see if a reply is available
delay(3000);
cb = udp.parsePacket();
// only try 5 times before sleeping
ntptries++;
if (ntptries >5)
{
gotoSleep();
}
}
// we've received a packet, read the data into the buffer
udp.read(packetBuffer, NTP_PACKET_SIZE);
// the timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. first, extract the two words:
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
// combine the four bytes (two words) into a long integer
// this is ntp time (seconds since jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
// unix time starts on jan 1 1970. in seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
// subtract seventy years:
unsigned long epoch = secsSince1900 - seventyYears;
// update ds3231 from ntp unixtime
RTC.adjust(DateTime(epoch));
// display ntp time on led matrices
displayDate(epoch);
// ntp has a dot rtc does not
lc.setLed(3, 7, 0, true);
// wait five seconds before shutting down
delay(5000);
gotoSleep();
}
void loop()
{
}