esp_clock: refactor to use ntp client library and multi wifi

This commit is contained in:
Nicu Hodos 2020-05-10 22:16:07 +02:00
parent 180320593f
commit d2b0bc4fd6

View File

@ -1,45 +1,27 @@
//##########################################################################
// 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 <ESP8266WiFiMulti.h>
#include <NTPClient.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <Adafruit_LEDBackpack.h> // Support for the Backpack FeatherWing
#include <Adafruit_GFX.h> // Adafruit's graphics library
//##########################################################################
// Globals
//##########################################################################
#define STASSID "Miracle"
#define STAPSK "***REMOVED***"
#define TIME_24_HOUR true
#define TIME_24_HOUR
#define DISPLAY_ADDRESS 0x70
#define BRIGHTNESS 7
//##########################################################################
// Globals
//##########################################################################
// Create display object
Adafruit_7segment clockDisplay = Adafruit_7segment();
@ -48,49 +30,131 @@ int minutes = 0; // Track minutes
int seconds = 0; // Track seconds
int tzOffset = +2; // Time zone offset (-4 = US Eastern time)
#ifdef TIME_24_HOUR
byte displayHours[24] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23};
#else
byte displayHours[24] = {12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
#endif
bool blinkColon = false; // Track colon status to blink once/sec
int status = WL_IDLE_STATUS;
bool shouldUpdate = true;
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
ESP8266WiFiMulti wifiMulti;
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", tzOffset*3600);
WiFiUDP Udp; // Set up a WINC1500 client session
void setup() {
Serial.begin(9600); // Start the serial console
Serial.println("Clock starting!"); // Start the clock message.
//##########################################################################
// Functions
//##########################################################################
wifiMulti.addAP("vulturul", "***REMOVED***");
wifiMulti.addAP("Miracle", "***REMOVED***");
Serial.println("Connecting to WiFi netowrk.");
while (wifiMulti.run() != WL_CONNECTED) {
delay(500);
}
Serial.println("Connected to network.");
printWiFiStatus(); // Display WiFi status data
// 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;
setupOTA();
//Serial.println("3");
clockDisplay.begin(DISPLAY_ADDRESS);
clockDisplay.setBrightness(BRIGHTNESS);
// 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");
timeClient.begin();
}
void loop() {
ArduinoOTA.handle();
if (shouldUpdate && timeClient.forceUpdate()) {
shouldUpdate = false;
hours = timeClient.getHours();
minutes = timeClient.getMinutes();
seconds = timeClient.getSeconds();
Serial.println(timeClient.getFormattedTime());
} else {
incrementTime();
}
displayTime();
delay(1000);
}
void incrementTime() {
seconds++;
if (seconds == 60) {
seconds = 0;
minutes++;
if (((minutes % 5) == 0)) shouldUpdate = true;
if (minutes == 60) {
minutes = 0;
Serial.println("Minutes set to zero - should query NTP on next loop()");
hours = ++hours % 24;
}
}
}
void displayTime() {
int displayHour = displayHours[hours];
int displayValue = displayHour*100 + minutes;
// 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 (displayHour == 0) {
clockDisplay.writeDigitNum(1, 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();
}
void setupOTA() {
ArduinoOTA.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "sketch";
} else { // U_FS
type = "filesystem";
}
// NOTE: if updating FS this would be the place to unmount FS using FS.end()
Serial.println("Start updating " + type);
});
ArduinoOTA.onEnd([]() {
Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) {
Serial.println("Auth Failed");
} else if (error == OTA_BEGIN_ERROR) {
Serial.println("Begin Failed");
} else if (error == OTA_CONNECT_ERROR) {
Serial.println("Connect Failed");
} else if (error == OTA_RECEIVE_ERROR) {
Serial.println("Receive Failed");
} else if (error == OTA_END_ERROR) {
Serial.println("End Failed");
}
});
ArduinoOTA.begin();
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void printWiFiStatus() {
@ -109,178 +173,3 @@ void printWiFiStatus() {
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 - 24;
}
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;
}
}
}
}