Нашел и отладил утилиты синхронизации времени с NTP сервером, конвертации времени из UNIX формата (число секунд с 01.01.1970) в обычный и обратно. Что очень полезно для программирования событий на ESP 8266. Естественно, чтобы использовать их, необходимо, чтобы ESP8266 была подключена к сети с выходом в интернет. До того, как произойдет синхронизация (может не произойти с первого раза), она пытается это делать каждые 10 секунд, после этого - раз в сутки, т.к. есть встроенные часы по таймеру Tcker.h
Также прикладываю утилиты конвертации IP адреса в/из строкового формата.
Также прикладываю утилиты конвертации IP адреса в/из строкового формата.
Код:
#include <WiFiUdp.h>
#include <Ticker.h>
/*******************настройки для NTP-синхронизации времени****************/
#define GMT 3
bool AskNTPTime=false;
unsigned int udpPort = 2390; // local port to listen for UDP packets
IPAddress timeServerIP; // time.nist.gov NTP server address
const char* ntpServerName = "time.nist.gov";
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
WiFiUDP udp;
/*******************настройки времени и даты*******************************/
uint8_t blink_loop = 0;
uint16_t frequency, blink_mode = 0;
uint8_t hour, minute, second, day, month, year, weekday;
uint8_t monthDays[12]={31,28,31,30,31,30,31,31,30,31,30,31};
unsigned long secsUnix;
unsigned long Tm_blink;
Ticker timer;
IPAddress ip, gateway, subnet;
void setup(void)
{
Serial.begin(115200);
Serial.println();
timer.attach(1, timerTime);
Serial.println("Starting UDP");
udp.begin(udpPort);
Serial.print("Local port: ");
Serial.println(udp.localPort());
}
void loop() {
if ( WiFi.status() == WL_CONNECTED)
{
if(AskNTPTime==true)
{
if(hour==0 && minute==0 && second<2) AskNTPTime==false;
}
else if(second%10<2) Ask_NTP_Time();
}
}
void timerTime()
{
if(AskNTPTime==true) secsUnix++; // если время синхронизировано, то отсчитываем время в секундах
second++; // ведем реальный отсчет времени
if (second > 59)
{
second = 0;
minute++;
if (minute > 59)
{
minute = 0;
hour++;
if (hour > 23)
{
hour = 0;
day++;
if(month>1)
if(day>monthDays[month-1])
{
day=1;
month++;
if(month>12) { year++; month=1; }
}
}
}
}
}
String IP_to_String(IPAddress ip)
{
return(String(ip[0])+"."+String(ip[1])+"."+String(ip[2])+"."+String(ip[3]));
}
IPAddress String_to_IP(String strIP)
{
int Parts[4] = {0,0,0,0};
int Part = 0;
for ( byte i=0; i<strIP.length(); i++ )
{
char c = strIP.charAt(i);
if ( c == '.' )
{
Part++;
continue;
}
if ( c<48 || c>57 ) continue; // не цифровой символ
Parts[Part] *= 10;
Parts[Part] += c - '0';
}
IPAddress ip_str( Parts[0], Parts[1], Parts[2], Parts[3] );
return(ip_str);
}
bool Ask_NTP_Time()
{
int cb;
while (udp.parsePacket() > 0) {delay(1);}
WiFi.hostByName(ntpServerName, timeServerIP);
sendNTPpacket(timeServerIP); // send an NTP packet to a time server
// wait to see if a reply is available
unsigned long beginWait = millis();
while (millis() - beginWait < 2000)
{
cb = udp.parsePacket();
if (cb >= NTP_PACKET_SIZE)
{
Serial.print("packet received, length=");
Serial.println(cb);
// 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 secsSince1900;
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
secsSince1900 |= (unsigned long)packetBuffer[43];
Serial.print("Seconds since Jan 1 1900 = " );
Serial.println(secsSince1900);
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
// subtract seventy years:
secsUnix = secsSince1900 - seventyYears;
// print Unix time:
Serial.print("Unix time = ");
Serial.println(secsUnix);
Unix_to_GMT(secsUnix);
AskNTPTime=true; // признак того, что время синхронизировано
// выдерживаем паузу
while (millis() - beginWait < 3000) {delay(10);}
Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT)
Serial.println(Time_to_String());
return(true);
}
}
Serial.println("No packet yet");
while (millis() - beginWait < 1000) {delay(10);}
return(false);
// wait ten seconds before asking for the time again
}
// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress & address)
{
Serial.println("sending NTP packet...IP="+IP_to_String(address));
// 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)
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(address, 123); //NTP requests are to port 123
udp.write(packetBuffer, NTP_PACKET_SIZE);
udp.endPacket();
}
void Unix_to_GMT(unsigned long epoch)
{
// корректировка часового пояса и синхронизация
epoch = epoch + GMT * 3600;
second=epoch%60;
epoch/=60; // now it is minutes
minute=epoch%60;
epoch/=60; // now it is hours
hour=epoch%24;
epoch/=24; // now it is days
weekday=(epoch+4)%7; // day week, 0-sunday
year=70;
int days=0;
while(days + ((year % 4) ? 365 : 366) <= epoch) {
days += (year % 4) ? 365 : 366;
year++;
}
epoch -= days; // now it is days in this year, starting at 0
days=0;
month=0;
byte monthLength=0;
for (month=0; month<12; month++) {
if (month==1) { // february
if (year%4) monthLength=28;
else monthLength=29;
}
else monthLength = monthDays[month];
if (epoch>=monthLength) epoch-=monthLength;
else break;
}
month++; // jan is month 1
day=epoch+1; // day of month
}
String Time_to_String()
{
String Tm,wd;
int k,n;
switch(weekday)
{
case 0:
wd="su";;
break;
case 1:
wd="mo";;
break;
case 2:
wd="tu";;
break;
case 4:
wd="th";;
break;
case 5:
wd="fr";;
break;
case 6:
wd="sf";;
break;
}
k=day/10;
n=day%10;
Tm=String(k)+String(n)+".";
k=month/10;
n=month%10;
Tm=Tm+String(k)+String(n)+".";
k=1900+year;
Tm=Tm+String(k)+","+wd+" ";
k=hour/10;
n=hour%10;
Tm=Tm+String(k)+String(n)+":";
k=minute/10;
n=minute%10;
Tm=Tm+String(k)+String(n)+":";
k=second/10;
n=second%10;
Tm=Tm+String(k)+String(n);
return(Tm);
}
unsigned long GMT_to_Unix(byte y,byte mon,byte d,byte h,byte mt,byte s)
{
// считает количество секунд от текущего времени до времени d.mon.y h:mt:s
unsigned long epoch=0;
epoch+=s;
epoch+=mt*60;
epoch+=h*3600;
d--;
epoch+=d*24*3600;
mon--;
byte monthLength;
for (int m=mon; m>=1; m--) {
if (m==2) { // february
if (year%4) monthLength=28;
else monthLength=29;
}
else monthLength = monthDays[m-1];
epoch+=monthLength*24*3600;
}
y--;
int days=0;
while(y>=70) {
days += (y % 4) ? 365 : 366;
y--;
}
epoch+=days*24*3600;
epoch = epoch - GMT * 3600;
return(epoch);
}