Skip to content

Instantly share code, notes, and snippets.

@jeffskinnerbox
Created January 3, 2019 13:18
Show Gist options
  • Save jeffskinnerbox/d74f518157c28dc2dac3297095417447 to your computer and use it in GitHub Desktop.
Save jeffskinnerbox/d74f518157c28dc2dac3297095417447 to your computer and use it in GitHub Desktop.
Test program for ESP8266 NodeMCU + 7-segment display + backpack from Adafruit. NodeMCU will drive the display as a clock synchronized, via NTP, with a NIST time server.
/*
Maintainer: jeffskinnerbox@yahoo.com / www.jeffskinnerbox.me
Version: 0.1.0
DESCRIPTION:
Test program for ESP8266 NodeMCU + 7-segment display + backpack from Adafruit.
NodeMCU will drive the display as a clock synchronized, via NTP, with a NIST time server.
PHYSICAL DESIGN:
Hardware
Adafruit 1.2" 4-Digit 7-Segment Display w/I2C Backpack - https://www.adafruit.com/product/1270
HiLetgo New Version ESP8266 NodeMCU LUA CP2102 ESP-12E - https://www.amazon.com/gp/product/B010O1G1ES
Wiring
Connect display's backpack I2C clock pin "C" (SCL) with NodeMCU D1 pin
Connect display's backpack I2C data pin "D" (SDA) pin with NodeMCU D2 pin
Connect display's backpack GND pin "-" with with NodeMCU GND
Connect display's backpack VCC pin "+" with NodeMCU Vin (5V)
Connect display's backpack pin "IO" with with NodeMCU 3.3V
MONITOR:
screen /dev/ttyUSB0 9600,cs8
to terminate Cntr-a :quit
TESTING:
To do more frequent calls to NTP, modify REFRESH to a value like 5000UL
SOURCES:
https://learn.adafruit.com/adafruit-led-backpack?view=all
https://www.arduinoslovakia.eu/blog/2017/7/esp8266---ntp-klient-a-letny-cas?lang=en
https://github.com/adafruit/Adafruit_LED_Backpack/blob/master/examples/clock_sevenseg_ds1307/clock_sevenseg_ds1307.ino
*/
#include <Wire.h>
#include <timer.h>
#include <Adafruit_GFX.h>
#include <Adafruit_LEDBackpack.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <TimeLib.h>
#include <Timezone.h>
#define TICKTOCK 999 // number of esp8266 milliseconds in a tick-tock second (ideally 1000)
#define REFRESH 3600000UL // number of milliseconds between npt time refresh (one hour)
#define WIFITIME 10000 // attempt to connect with wifi for approx. 10 seconds then abandon
#define DISPLAY_ADDRESS 0x70 // I2C address of the display
#define HEARTBEAT false // print '.' at each ticktock of the clock
#define ONE_SECOND 1000UL // milliseconds in one second
#define ONE_MINUTE 60000UL // milliseconds in one minute
#define ONE_HOUR 3600000UL // milliseconds in one hour
#define ONE_DAY 85400000UL // milliseconds in one day
#define INITIALIZING 0xAAAA // display code meaning clock is initializing
#define NOWIFI 0xCCCC // display code meaning can't get wifi
#define NOREFRESH 0xFFFF // display code meaning too long in not getting ntp time
#define NONTPSERVER 0xEEEE // display code meaning can't get ntp time to start clock
#define SEVENTYYEARS 2208988800UL // seventy years of seconds
// create display object
Adafruit_7segment clockDisplay = Adafruit_7segment();
unsigned long lastNTPResponse = 0UL; // time of last successful ntp update
unsigned long newUNIXepoch = 0UL;
unsigned long oldUNIXepoch = 0UL;
// credentials for wifi network
const char *ssid = "my-wifi-ssid";
const char *pass = "my-wifi-password";
// create timers with default settings
auto timer1 = timer_create_default(); // timer to update clock display
auto timer2 = timer_create_default(); // timer to blink colon on clock display
auto timer3 = timer_create_default(); // timer to internal clock with ntp time
int Brightness = 6; // set brightness of the display (value 1 to 15)
bool blinkColon = false; // turn on the colon on the display
bool displayPM = false; // is the current time in the pm?
int displayValue = 0; // value written to the display
int displayHours = 0; // hours value written to the display
int displayMinutes = 0; // minutes value written to the display
int displaySeconds = 0; // seconds value written to the display
// A UDP object to send/receive packets over UDP
WiFiUDP udp;
unsigned int localPort = 2390; // local port to listen for UDP packets
// time.nist.gov NTP server address
IPAddress timeServerIP;
const char* ntpServerName = "time.nist.gov"; // NIST time server
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets
//US Eastern Time Zone (New York, Detroit)
TimeChangeRule usEDT = {"EDT", Second, Sun, Mar, 2, -240}; //Eastern Daylight Time = UTC - 4 hours
TimeChangeRule usEST = {"EST", First, Sun, Nov, 2, -300}; //Eastern Standard Time = UTC - 5 hours
Timezone usET(usEDT, usEST);
//------------------------------------------------------------------------------
// connect to wifi
bool wifiConnect(const char *ssid, const char *password, unsigned long timeout) {
unsigned long tout;
// attempt first connect to a WiFi network
Serial.print("\nAttempting connection to WiFi SSID ");
Serial.println(ssid);
WiFi.begin(ssid, password);
// make subsequent connection attempts to wifi
tout = timeout + millis(); // milliseconds of time given to making connection attempt
while(WiFi.status() != WL_CONNECTED) {
Serial.print(".");
if (millis() > tout) {
Serial.print("Failed to connect to WiFi! ");
Serial.print("WiFi status exit code is ");
Serial.println(WiFi.status());
return false;
}
delay(500);
}
Serial.print("Successfully connected to WiFi! ");
Serial.print("IP address is ");
Serial.println(WiFi.localIP());
return true;
}
// terminate the wifi connect
void wifiTerminate() {
Serial.print("\nDisconnecting from WiFi with SSID ");
Serial.println(WiFi.SSID());
WiFi.disconnect();
Serial.println("\n-------------------------------------------------------");
}
// scan for nearby networks
void scanNetworks() {
Serial.println("\nStarting Network Scan");
byte numSsid = WiFi.scanNetworks();
// print the list of networks seen
Serial.print("SSID List:");
Serial.println(numSsid);
// print the network number and name for each network found
for (int thisNet = 0; thisNet<numSsid; thisNet++) {
Serial.print(" ");
Serial.print(thisNet);
Serial.print(") Network: ");
Serial.println(WiFi.SSID(thisNet));
}
Serial.println("Network Scan Completed");
Serial.println("\n-------------------------------------------------------");
}
// start listening for UDP messages on port localPort
void startUDP() {
Serial.print("Starting UDP ");
udp.begin(localPort);
Serial.print("using local port ");
Serial.println(udp.localPort());
Serial.print("\n");
}
//------------------------------------------------------------------------------
// push colon status to 7-segment display
bool UpdateColon(void *) {
clockDisplay.drawColon(blinkColon); // print the colon status
clockDisplay.writeDisplay(); // now push out to the display
blinkColon = !blinkColon; // flipping value to blink colon
return true; // repeat? true
}
// push time to 7-segment display
bool UpdateTime(void *) {
displaySeconds += 1; // increment seconds
if (displaySeconds >= 60) {
displaySeconds = 0;
displayMinutes += 1;
}
if (displayMinutes >= 60) { // increment minutes
displayMinutes = 0;
displayHours += 1; // increment hours
}
// convert from 24 to 12 hour clock
if (displayHours == 0) {
displayHours = 12;
displayPM = false;
}
if (displayHours >= 13) {
displayHours -= 12;
displayPM = true;
} else
displayPM = false;
//displayValue = 10000 * displayHours + 100 * displayMinutes + displaySeconds;
displayValue = 100 * displayHours + displayMinutes;
clockDisplay.print(displayValue, DEC); // print the time value
clockDisplay.drawColon(blinkColon); // print the colon status
clockDisplay.writeDisplay(); // now push out to the display
// print heart beat
if (HEARTBEAT) {
if (displaySeconds == 59)
Serial.print(".\n\r");
else
Serial.print(".");
}
return true; // repeat? true
}
// check for clock accuracy
bool TimeDriftCheck(void *) {
bool rtn;
int h, m, s;
time_t utc;
unsigned long epoch;
TimeChangeRule *tcr;
wifiConnect(ssid, pass, WIFITIME); // connect to wifi, attempt for 10 seconds
startUDP(); // start listening for UDP messages
epoch = getNTPTime();
utc = epoch;
Serial.println("Check for time drift:");
if (utc != NULL) {
// new time based on ntp server
h = hour(usET.toLocal(utc, &tcr));
m = minute(usET.toLocal(utc, &tcr));
s = second(usET.toLocal(utc, &tcr));
// convert from 24 to 12 hour clock
if (h == 0) h = 12;
if (h >= 13) {
h -= 12;
}
printTimeDateLoc(utc, "UTC", "Universal Coordinated Time");
printTimeDateLoc(usET.toLocal(utc, &tcr), tcr->abbrev, "New York");
Serial.print("\nNTP time = ");
printTime(h, m, s);
Serial.println();
rtn = true;
} else {
Serial.println("\nNTP server could not be reached. NTP Time unknown.");
rtn = false;
}
Serial.print("Current clock time = ");
printTime(displayHours, displayMinutes, displaySeconds);
Serial.println();
wifiTerminate(); // disconnecting from wifi
return rtn;
}
// periodically refresh the time with a query to ntp server
bool TimeRefresh(void *) {
int pre_t, post_t;
wifiConnect(ssid, pass, WIFITIME); // connect to wifi, attempt for 10 seconds
startUDP(); // start listening for UDP messages
pre_t = displayHours * 10000 + displayMinutes * 100 + displaySeconds;
Serial.print("Display time pre-NTP refresh = ");
Serial.print(pre_t);
Serial.print(" == ");
printTime(displayHours, displayMinutes, displaySeconds);
Serial.println();
SetTime(getNTPTime()); // get current time from ntp server and set the clock
post_t = displayHours * 10000 + displayMinutes * 100 + displaySeconds;
Serial.print("Display time post-NTP refresh = ");
Serial.print(post_t);
Serial.print(" == ");
printTime(displayHours, displayMinutes, displaySeconds);
Serial.println();
// pre_t will be zero for the first refreash
if (pre_t > 0) {
Serial.print("Clock is out of sync with NTP server by ");
Serial.print(newUNIXepoch - oldUNIXepoch - REFRESH / 1000UL);
Serial.println(" seconds");
}
wifiTerminate(); // disconnecting from wifi
}
// set the 7-segment display to a specific value
bool SetTime(unsigned long time) {
time_t utc;
TimeChangeRule *tcr;
if (time != NULL) {
utc = time;
oldUNIXepoch = newUNIXepoch;
newUNIXepoch = time;
// new time based on ntp server
displayHours = hour(usET.toLocal(utc, &tcr));
displayMinutes = minute(usET.toLocal(utc, &tcr));
displaySeconds = second(usET.toLocal(utc, &tcr));
// convert from 24 to 12 hour clock
if (displayHours == 0) {
displayHours = 12;
displayPM = false;
}
if (displayHours >= 13) {
displayHours -= 12;
displayPM = true;
} else
displayPM = false;
Serial.print("Clock time set to = ");
printTime(displayHours, displayMinutes, displaySeconds);
if (displayPM)
Serial.println("pm");
else
Serial.println("am");
return true;
}
Serial.println("New time could not be set. Continuing with old time.");
return false;
}
//------------------------------------------------------------------------------
// handle errors by displaying a code and then restart
void errorHandler(int error) {
clockDisplay.print(error, HEX); // error code to be displayed
clockDisplay.writeDisplay(); // now push out to the display
delay(ONE_MINUTE); // delay so the error code can be read
Serial.flush();
ESP.reset(); // nothing can be done so restart
}
// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address) {
Serial.print("Sending NTP packet...");
memset(packetBuffer, 0, NTP_PACKET_SIZE); // set all bytes in the buffer to 0
// 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 time stamp:
udp.beginPacket(address, 123); // ntp requests are to port 123
udp.write(packetBuffer, NTP_PACKET_SIZE);
udp.endPacket();
}
// query the ntp server and update the current time
// NOTE: you corrupting the time via the "delay" and other things
unsigned long getNTPTime() {
TimeChangeRule *tcr;
WiFi.hostByName(ntpServerName, timeServerIP); //get a random server from the pool
sendNTPpacket(timeServerIP); // send an NTP packet to a time server
delay(1000); // wait one second before checking for reply
int cb = udp.parsePacket();
if (!cb) {
Serial.println("No packet from NTP server yet.");
if ((millis() - lastNTPResponse) > ONE_DAY) {
Serial.println("\n\nMore than 24 hours since last NTP response. Rebooting...");
errorHandler(NOREFRESH);
}
} else {
Serial.print("NTP packet received, length= ");
Serial.println(cb);
lastNTPResponse = millis();
udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
//the time stamp 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 giving you the NTP time
// which is the seconds since Jan 1 1900 at UTC
unsigned long secsSince1900 = highWord << 16 | lowWord; // this is time (seconds since Jan 1 1900)
Serial.print("NTP time (seconds since Jan 1 1900 UTC) = " );
Serial.println(secsSince1900);
// now convert NTP time into unix time
// which is the seconds since Jan 1 1970 UTC
Serial.print("Unix time (seconds since Jan 1 1970 UTC) = ");
unsigned long epoch = secsSince1900 - SEVENTYYEARS; // subtract seventy years
Serial.println(epoch); // print Unix time (seconds since Jan 1 1970)
// now print the full time, date, timezone
printTimeDateLoc(epoch, "UTC", "Universal Coordinated Time");
printTimeDateLoc(usET.toLocal(epoch, &tcr), tcr->abbrev, "New York");
return epoch; // successful
}
return NULL; // can't get updated time via ntp
}
//------------------------------------------------------------------------------
//Function to print time with time zone
void printTimeDateLoc(time_t t, char *tz, char *loc) {
printTime(hour(t), minute(t), second(t));
Serial.print(' ');
Serial.print(dayShortStr(weekday(t)));
Serial.print(' ');
sPrintI00(day(t));
Serial.print(' ');
Serial.print(monthShortStr(month(t)));
Serial.print(' ');
Serial.print(year(t));
Serial.print(' ');
Serial.print(tz);
Serial.print(' ');
Serial.print(loc);
Serial.println();
}
void printTime(int hour, int minute, int second) {
sPrintI00(hour);
sPrintDigits(minute);
sPrintDigits(second);
}
// print an integer in "00" format (with leading zero).
// input value assumed to be between 0 and 99.
void sPrintI00(int val) {
if (val < 10) Serial.print('0');
Serial.print(val, DEC);
return;
}
// print an integer in ":00" format (with leading zero).
// input value assumed to be between 0 and 99.
void sPrintDigits(int val) {
Serial.print(':');
if (val < 10) Serial.print('0');
Serial.print(val, DEC);
}
//------------------------------------------------------------------------------
void setup() {
unsigned long epoch;
// setup serial port to print debug output
Serial.begin(9600);
while (!Serial) {} // wait for serial port to connect
Serial.println("NTP-Clock starting!");
clockDisplay.begin(DISPLAY_ADDRESS); // Setup the display
clockDisplay.setBrightness(Brightness); // set brightness of the display
clockDisplay.print(INITIALIZING, HEX); // print initial value
clockDisplay.writeDisplay(); // now push out to the display
scanNetworks(); // scan for wifi access points
if (wifiConnect(ssid, pass, WIFITIME)) { // connect to wifi
startUDP(); // start listening for UDP messages
epoch = getNTPTime(); // get current time from ntp server
SetTime(epoch); // set the clock
oldUNIXepoch = epoch; // make equal for initialization
// you need to stop if you can't get your first ntp time request
if (epoch == NULL) {
Serial.println("Can't go on without NTP time. Press reset to try again.");
errorHandler(NONTPSERVER);
}
// set the callback print_message function for execution every 1000 millis (1 second)
timer1.every(TICKTOCK, UpdateTime); // timer to update clock display
timer2.every(TICKTOCK, UpdateColon); // timer to blink colon on clock display
timer3.every(REFRESH, TimeRefresh); // timer to internal clock with ntp time
wifiTerminate(); // disconnecting from wifi
} else {
Serial.println("Can't go on without WiFi connection. Press reset twice to fix.");
errorHandler(NOWIFI);
}
}
void loop() {
// refresh the time at 2:01:01am with a query to ntp server
// this is to make sure you capture day light savings time changes
if (displayHours == 2 & displayMinutes == 1 & displaySeconds == 1 & displayPM == false) {
TimeRefresh(NULL);
}
// tick the timers
timer1.tick();
timer2.tick();
timer3.tick();
}
@ahutec
Copy link

ahutec commented Jul 6, 2021

I have this issue: 'timer_create_default' was not declared in this scope
Can you helpme please?

@jeffskinnerbox
Copy link
Author

See this ... https://github.com/contrem/arduino-timer. I believe you find it declared here - #include <arduino-timer.h>

Also, consider using the latest version - https://github.com/jeffskinnerbox/ntp-clock

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment