Hello-
I'm using an ESP8266 to collect data with timestamps which are sent to a server via HTTPS. I'm using certs rather than fingerprints, so system time needs to be fairly accurate. configTime() does this well, except if an internet connection is unavailable at startup. Then HTTPS doesn't work when the internet connection is restored.
So I'm using a DS3231. However, the DS3231 is not updating time() so I still need to call a second NTP request to get x.509 to work.
Simplified code is below, with the typical serial output, running on a ESP-12E.
At startup, time() is 0, as expected. After the RTC NTP update, time() is still 0 even though setTime() has been called. Next shows that the RTC is available and accurate. time() is only updated with configTime().
I'd like to eliminate configTime(). How can I set time() from the RTC? If configTime() can do it, I assume it can be done.
Thanks for any suggestions.
Code:
#include <Arduino.h>
#include <ESP8266WiFi.h> // https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/readme.html
#define CFG_WIFI_ESSID "ssid"
#define CFG_WIFI_PASSWORD "password"
// ------------------------ Begin RTC
#include <WiFiUdp.h>
#include <DS323x_Generic.h>
DS323x rtc;
char timeServer[] = "time.nist.gov"; // NTP server
unsigned int NTPlocalPort = 2390; // local port to listen for UDP packets
const int NTP_PACKET_SIZE = 48; // NTP timestamp is in the first 48 bytes of the message
const int UDP_TIMEOUT = 2000; // timeout in miliseconds to wait for an UDP packet to arrive
byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets
// A UDP instance to let us send and receive packets over UDP
WiFiUDP Udp;
// ------------------------ End RTC
void setup() {
Wire.begin(0, 2); // join i2c bus (SDA, SCL)
Serial.begin(115200);
Serial.println(__FILE__);
WiFi.mode(WIFI_STA);
WiFi.begin(CFG_WIFI_ESSID, CFG_WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
Serial.println("WiFi Connected.");
time_t timeNowUTC = time(nullptr);
struct tm * timeInfo;
timeInfo = localtime(&timeNowUTC);
Serial.print("Start: ");
Serial.println(asctime(timeInfo));
// ------------------------ Begin NTP/RTC
Udp.begin(NTPlocalPort);
rtc.attach(Wire);
// Get time from NTP once, then update RTC
getNTPTime();
// Give time for getNTPTime() to work. This doesn't help.
delay(5000);
// ------------------------ End NTP/RTC
timeNowUTC = time(nullptr);
timeInfo = localtime(&timeNowUTC);
Serial.print("getNTPTime: ");
Serial.println(asctime(timeInfo));
DateTime now = rtc.now();
time_t utc = now.get_time_t();
struct tm * UTCtimeInfo = localtime(&utc);
Serial.print("RTC Time: ");
Serial.println(asctime(UTCtimeInfo));
timeNowUTC = time(nullptr);
timeInfo = localtime(&timeNowUTC);
Serial.print("Time: ");
Serial.println(asctime(timeInfo));
// needed for x.509
configTime("GMT", "time.nist.gov");
timeNowUTC = time(nullptr);
while (timeNowUTC < 8 * 3600 * 2) {
delay(100);
timeNowUTC = time(nullptr);
}
timeNowUTC = time(nullptr);
timeInfo = localtime(&timeNowUTC);
Serial.print("configTime: ");
Serial.println(asctime(timeInfo));
}
void loop() {
}
// RTC Functions
// send an NTP request to the time server at the given address
void sendNTPpacket(char *ntpSrv)
{
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
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 timestamp:
Udp.beginPacket(ntpSrv, 123); //NTP requests are to port 123
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
}
void getNTPTime(void)
{
static bool gotCurrentTime = false;
// Just get the correct time once
if (!gotCurrentTime)
{
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(F("Packet received"));
// We've received a packet, read the data from it
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the 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;
// now convert NTP time into everyday 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:
// Get the time_t from epoch
time_t epoch_t = epoch;
// set the system time to UTC
// warning: assumes that compileTime() returns US EDT
// adjust the following line accordingly if you're in another time zone
setTime(epoch_t);
// Update RTC
// Can use either one of these functions
// 1) DateTime(tmElements_t). Must create tmElements_t if not present
//tmElements_t tm;
//breakTime(epoch_t, tm);
//rtc.now( DateTime(tm) );
// 2) DateTime(year, month, day, hour, min, sec)
//rtc.now( DateTime(year(epoch_t), month(epoch_t), day(epoch_t), hour(epoch_t), minute(epoch_t), second(epoch_t) ) );
// 3) DateTime (time_t)
//rtc.now( DateTime(epoch_t) );
// 4) DateTime(unsigned long epoch). The best and easiest way
rtc.now( DateTime((uint32_t) epoch) );
gotCurrentTime = true;
// set previousTimeUpdate
}
else
{
// wait ten seconds before asking for the time again
// No. Just return.
// delay(10000);
return;
}
}
}
Output:
WiFi Connected.
Start: Thu Jan 1 00:00:06 1970
getNTPTime: Thu Jan 1 00:00:12 1970
RTC Time: Tue Feb 22 15:58:09 2022
Time: Thu Jan 1 00:00:12 1970
configTime: Tue Feb 22 15:58:10 2022