From d9f11a5436c44265a85fba1ed7060a066be8cc18 Mon Sep 17 00:00:00 2001 From: Nicu Hodos Date: Sat, 9 May 2020 12:01:58 +0200 Subject: [PATCH] ESP8266 clock with Adafruit --- esp_clock.ino | 286 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 esp_clock.ino diff --git a/esp_clock.ino b/esp_clock.ino new file mode 100644 index 0000000..ab9330e --- /dev/null +++ b/esp_clock.ino @@ -0,0 +1,286 @@ +//########################################################################## +// 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 +#include // SPI interface API +#include // Wire support library +#include // UDP support +#include // Support for the Backpack FeatherWing +#include // Adafruit's graphics library + +#define VBATPIN 9 // Pin to read battery voltage + +//########################################################################## +// Globals +//########################################################################## + +#define TIME_24_HOUR true +#define DISPLAY_ADDRESS 0x70 + +// 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; + +#define STASSID "vulturul" +#define STAPSK "***REMOVED***" + +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); + + // 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; + } + } + } +}