Created
January 3, 2019 13:18
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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(); | |
} |
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
I have this issue: 'timer_create_default' was not declared in this scope
Can you helpme please?