287 lines
10 KiB
C++
287 lines
10 KiB
C++
//##########################################################################
|
|
// FeatherClock - Use the Adafruit seven segment LED FeatherWing to display
|
|
// the current time, which we fetch from the Internet using NTP. This
|
|
// program assumes the seven segment display is attached to a Feather M0
|
|
// WiFi (WINC1500).
|
|
//
|
|
// At the top of every hour, re-sync with NTP to correct for any drift.
|
|
//
|
|
// Credit: This is based heavily on the work by Tony DiCola from Adafruit.
|
|
// Suggest (strongly) you go to adafruit.com and buy some product from
|
|
// them. WINC1500 code modified from example code provided by Michael
|
|
// Margolis and Tom Igoe.
|
|
//
|
|
// Philip R. Moyer
|
|
// Adafruit
|
|
//
|
|
// This source code is released under the BSD license.
|
|
// Any further redistribution must include this header.
|
|
//##########################################################################
|
|
|
|
//##########################################################################
|
|
// Includes and defines
|
|
//##########################################################################
|
|
|
|
#include <ESP8266WiFi.h>
|
|
#include <SPI.h> // SPI interface API
|
|
#include <Wire.h> // Wire support library
|
|
#include <WiFiUdp.h> // UDP support
|
|
#include <Adafruit_LEDBackpack.h> // Support for the Backpack FeatherWing
|
|
#include <Adafruit_GFX.h> // Adafruit's graphics library
|
|
|
|
//##########################################################################
|
|
// Globals
|
|
//##########################################################################
|
|
|
|
#define STASSID "vulturul"
|
|
#define STAPSK "***REMOVED***"
|
|
|
|
#define TIME_24_HOUR true
|
|
#define DISPLAY_ADDRESS 0x70
|
|
#define BRIGHTNESS 7
|
|
|
|
// Create display object
|
|
Adafruit_7segment clockDisplay = Adafruit_7segment();
|
|
|
|
int hours = 0; // Track hours
|
|
int minutes = 0; // Track minutes
|
|
int seconds = 0; // Track seconds
|
|
int tzOffset = +2; // Time zone offset (-4 = US Eastern time)
|
|
|
|
bool blinkColon = false; // Track colon status to blink once/sec
|
|
int status = WL_IDLE_STATUS;
|
|
|
|
unsigned int localPort = 2390; // Local port to listen for UDP packets
|
|
IPAddress timeServer(129,6,15,28); // time.nist.gov NTP server
|
|
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes
|
|
byte packetBuffer[NTP_PACKET_SIZE]; // Buffer for incoming and outgoing UDP packets
|
|
|
|
WiFiUDP Udp; // Set up a WINC1500 client session
|
|
|
|
//##########################################################################
|
|
// Functions
|
|
//##########################################################################
|
|
|
|
// send an NTP request to the time server at the given address
|
|
unsigned long sendNTPpacket(IPAddress& address)
|
|
{
|
|
//Serial.println("1");
|
|
// 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)
|
|
//Serial.println("2");
|
|
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;
|
|
|
|
//Serial.println("3");
|
|
|
|
// 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
|
|
//Serial.println("4");
|
|
Udp.write(packetBuffer, NTP_PACKET_SIZE);
|
|
//Serial.println("5");
|
|
Udp.endPacket();
|
|
//Serial.println("6");
|
|
}
|
|
|
|
void printWiFiStatus() {
|
|
// print the SSID of the network you're attached to:
|
|
Serial.print("SSID: ");
|
|
Serial.println(WiFi.SSID());
|
|
|
|
// print your WiFi shield's IP address:
|
|
IPAddress ip = WiFi.localIP();
|
|
Serial.print("IP Address: ");
|
|
Serial.println(ip);
|
|
|
|
// print the received signal strength:
|
|
long rssi = WiFi.RSSI();
|
|
Serial.print("signal strength (RSSI):");
|
|
Serial.print(rssi);
|
|
Serial.println(" dBm");
|
|
}
|
|
|
|
//##########################################################################
|
|
// Setup - main
|
|
//##########################################################################
|
|
|
|
// This is one of the two standard functions for every Arduino program,
|
|
// with the other being loop(). This one runs once at the beginning of the
|
|
// program execution and is ordinarily used to initialize hardware and
|
|
// variables.
|
|
|
|
void setup() {
|
|
#ifdef WINC_EN
|
|
pinMode(WINC_EN, OUTPUT);
|
|
digitalWrite(WINC_EN, HIGH);
|
|
#endif
|
|
|
|
Serial.begin(115200); // Start the serial console
|
|
Serial.println("Clock starting!"); // Start the clock message.
|
|
|
|
WiFi.persistent(false);
|
|
WiFi.mode(WIFI_STA);
|
|
WiFi.begin(STASSID, STAPSK);
|
|
|
|
// Now back to the setup
|
|
|
|
// Set up the display.
|
|
clockDisplay.begin(DISPLAY_ADDRESS);
|
|
clockDisplay.setBrightness(BRIGHTNESS);
|
|
|
|
// Attempt to conect to the WiFi network.
|
|
Serial.println("Connecting to WiFi netowrk.");
|
|
while (WiFi.status() != WL_CONNECTED) {
|
|
status = WiFi.begin(STASSID, STAPSK); // Connect to WPA2 network
|
|
uint8_t timeout = 10; // Set a timeout variable
|
|
while (timeout && (WiFi.status() != WL_CONNECTED)) {
|
|
timeout--; // Decrement timeout
|
|
delay(1000); // Delay for one second
|
|
}
|
|
}
|
|
|
|
Serial.println("Connected to network.");
|
|
printWiFiStatus(); // Display WiFi status data
|
|
|
|
Udp.begin(localPort); // Open the UDP port for comms
|
|
}
|
|
|
|
//##########################################################################
|
|
// Loop - main
|
|
//##########################################################################
|
|
|
|
// This is one of the two standard functions for every Arduino program,
|
|
// with the other being setup(). This one runs continuously, forever, and
|
|
// executes the Arduino program code.
|
|
|
|
void loop() {
|
|
// Refresh the time at the top of every hour, or every five minutes
|
|
// because the clock drift on the bare Feather M0 is pretty wicked.
|
|
if ((minutes == 0) || ((minutes % 5) == 0)) {
|
|
sendNTPpacket(timeServer); // send an NTP packet to a time server
|
|
// wait to see if a reply is available
|
|
delay(1000);
|
|
if ( Udp.parsePacket() ) {
|
|
Serial.println("packet received");
|
|
// We've received a packet, read the data from it
|
|
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into buffer
|
|
|
|
//the timestamp starts at byte 40 of the received packet and is four
|
|
// bytes, or two words, long. First, esxtract 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;
|
|
Serial.print("Seconds since Jan 1 1900 = " );
|
|
Serial.println(secsSince1900);
|
|
|
|
// now convert NTP time into everyday time:
|
|
Serial.print("Unix time = ");
|
|
// 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;
|
|
// print Unix time:
|
|
Serial.println(epoch);
|
|
|
|
// print the hour, minute and second:
|
|
Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT)
|
|
hours = ((epoch % 86400L) / 3600); // print the hour (86400 equals secs per day)
|
|
hours += tzOffset; // Calculate the time zone offset
|
|
if (hours < 0) {
|
|
hours = 24 + hours;
|
|
}
|
|
if (hours > 23) {
|
|
hours = hours - 23;
|
|
}
|
|
Serial.print(hours); // print the hour
|
|
Serial.print(':');
|
|
minutes = ((epoch % 3600) / 60);
|
|
if (minutes < 10 ) {
|
|
// In the first 10 minutes of each hour, we'll want a leading '0'
|
|
Serial.print('0');
|
|
}
|
|
Serial.print(minutes); // print the minute (3600 equals secs per minute)
|
|
Serial.print(':');
|
|
seconds = (epoch % 60); // print the second
|
|
if ( seconds < 10 ) {
|
|
// In the first 10 seconds of each minute, we'll want a leading '0'
|
|
Serial.print('0');
|
|
}
|
|
Serial.println(seconds);
|
|
}
|
|
}
|
|
// Display the time
|
|
int displayValue = hours*100 + minutes;
|
|
|
|
if (!TIME_24_HOUR) {
|
|
if (hours > 12) {
|
|
displayValue -= 1200;
|
|
}
|
|
else if (hours == 0) {
|
|
displayValue += 1200;
|
|
}
|
|
}
|
|
|
|
// Print the time on the display
|
|
clockDisplay.print(displayValue, DEC);
|
|
|
|
// Add zero padding when in 24 hour mode and it's midnight.
|
|
// In this case the print function above won't have leading 0's
|
|
// which can look confusing. Go in and explicitly add these zeros.
|
|
if (TIME_24_HOUR && hours == 0) {
|
|
// Pad hour 0.
|
|
clockDisplay.writeDigitNum(1, 0);
|
|
// Also pad when the 10's minute is 0 and should be padded.
|
|
if (minutes < 10) {
|
|
clockDisplay.writeDigitNum(2, 0);
|
|
}
|
|
}
|
|
|
|
// Blink the colon by flipping its value every loop iteration
|
|
// (which happens every second).
|
|
blinkColon = !blinkColon;
|
|
clockDisplay.drawColon(blinkColon);
|
|
|
|
// Now push out to the display the new values that were set above.
|
|
clockDisplay.writeDisplay();
|
|
|
|
// Pause for a second for time to elapse. This value is in milliseconds
|
|
// so 1000 milliseconds = 1 second.
|
|
delay(1000);
|
|
|
|
// Now increase the seconds by one.
|
|
seconds += 1;
|
|
// If the seconds go above 59 then the minutes should increase and
|
|
// the seconds should wrap back to 0.
|
|
if (seconds > 59) {
|
|
seconds = 0;
|
|
minutes += 1;
|
|
// Again if the minutes go above 59 then the hour should increase and
|
|
// the minutes should wrap back to 0.
|
|
if (minutes > 59) {
|
|
minutes = 0;
|
|
Serial.println("Minutes set to zero - should query NTP on next loop()");
|
|
hours += 1;
|
|
// Note that when the minutes are 0 (i.e. it's the top of a new hour)
|
|
// then the start of the loop will read the actual time from NTP
|
|
// again. Just to be safe though we'll also increment the hour and wrap
|
|
// back to 0 if it goes above 23 (i.e. past midnight).
|
|
if (hours > 23) {
|
|
hours = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|