ESP8266 синхронизация времени с NTP сервером. UnixTime и конвертация времени.

Paul_B
Offline
Зарегистрирован: 05.12.2016
Нашел и отладил утилиты синхронизации времени с NTP сервером, конвертации времени из UNIX формата (число секунд с 01.01.1970) в обычный и обратно. Что очень полезно для программирования событий на ESP 8266. Естественно, чтобы использовать их, необходимо, чтобы ESP8266 была подключена к сети с выходом в интернет. До того, как произойдет синхронизация (может не произойти с первого раза), она пытается это делать каждые 10 секунд, после этого - раз в сутки, т.к. есть встроенные часы по таймеру Tcker.h
 
Также прикладываю утилиты конвертации IP адреса в/из строкового формата.
 
[code]
#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); 
 
[/code]

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Так вроде ж в ардуиновской среде вполне себе существует time.h - к чему велосипед? Просто для упражнения?

Paul_B
Offline
Зарегистрирован: 05.12.2016

ЕвгенийП пишет:

Так вроде ж в ардуиновской среде вполне себе существует time.h - к чему велосипед? Просто для упражнения?

Возможно. Не знаю насколько она может синхронизировать время с интернета. Я не нашел, поупражнялся.

b707
Offline
Зарегистрирован: 26.05.2017

Paul_B пишет:

Не знаю насколько она может синхронизировать время с интернета. Я не нашел, поупражнялся.

"дурная голова ногам покоя не дает" :)

В time.h есть синхронизация по NTP, так что это и правда "велосипед". Но на самом деле сделать самому всегда полезнее, чем тупо использовать чужой код - хоть поймешь, как это работает.  Код посмотрел,  вроде неплохо,

negavoid
Offline
Зарегистрирован: 09.07.2016

File -> Examples -> ESP8266WiFi -> NTPClient, впрочем, пример и был тут использован.

И ещё, по поводу написать самому полезнее, чем взять готовое - так не всегда, и если уж взялись, то идти до конца:

IPAddress ip = String_to_IP("1476066034"); // упс...