Arduino:Примеры/TimeNTP

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.


Синхронизация времени с помощью NTP-сервера[1]

Это скетч для библиотеки Time и платы Arduino, который демонстрирует, как синхронизировать время при помощи NTP-сервера.

Код

/*

Синхронизация времени с помощью NTP-сервера

Этот пример показывает, как синхронизировать время 
с помощью NTP-сервера.

В скетче используется библиотека Ethernet.

*/
 
#include <TimeLib.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <SPI.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 
// NTP-серверы:
IPAddress timeServer(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov
// IPAddress timeServer(132, 163, 4, 102); // time-b.timefreq.bldrdoc.gov
// IPAddress timeServer(132, 163, 4, 103); // time-c.timefreq.bldrdoc.gov


const int timeZone = 1;     // центрально-европейское время
//const int timeZone = -5;  // восточное время (США)
//const int timeZone = -4;  // восточное дневное время (США)
//const int timeZone = -8;  // тихоокеанское время (США)
//const int timeZone = -7;  // тихоокеанское дневное время (США)


EthernetUDP Udp;
unsigned int localPort = 8888;  // локальный порт для прослушивания UDP-пакетов

void setup() 
{
  Serial.begin(9600);
  while (!Serial) ; // нужно только для Leonardo
  delay(250);
  Serial.println("TimeNTP Example");  //  "Синхронизация с помощью NTP"
  if (Ethernet.begin(mac) == 0) {
    // продолжать смысла нет, поэтому дальше ничего не делаем:
    while (1) {
      Serial.println("Failed to configure Ethernet using DHCP");  //  "Не удалось сконфигурировать Ethernet при помощи UDP"
      delay(10000);
    }
  }
  Serial.print("IP number assigned by DHCP is ");  //  "IP, присвоенный DHCP: "

  Serial.println(Ethernet.localIP());
  Udp.begin(localPort);
  Serial.println("waiting for sync");  //  "ждем синхронизации"

  setSyncProvider(getNtpTime);
}

time_t prevDisplay = 0; // когда будут показаны цифровые часы

void loop()
{  
  if (timeStatus() != timeNotSet) {
    if (now() != prevDisplay) { // обновляем дисплей только если время поменялось
      prevDisplay = now();
      digitalClockDisplay();  
    }
  }
}

void digitalClockDisplay(){
  // показываем цифровые часы:
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(day());
  Serial.print(" ");
  Serial.print(month());
  Serial.print(" ");
  Serial.print(year()); 
  Serial.println(); 
}

void printDigits(int digits){
  // вспомогательная функция для печати данных о времени 
  // на монитор порта; добавляет в начале двоеточие и ноль:
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

/*-------- Код для NTP ----------*/

const int NTP_PACKET_SIZE = 48; // NTP-время – в первых 48 байтах сообщения
byte packetBuffer[NTP_PACKET_SIZE]; // буфер для хранения входящих и исходящих пакетов

time_t getNtpTime()
{
  while (Udp.parsePacket() > 0) ; // отбраковываем все пакеты, полученные ранее
  Serial.println("Transmit NTP Request");  //  "Передача NTP-запроса"
  sendNTPpacket(timeServer);
  uint32_t beginWait = millis();
  while (millis() - beginWait < 1500) {
    int size = Udp.parsePacket();
    if (size >= NTP_PACKET_SIZE) {
      Serial.println("Receive NTP Response");  //  "Получение NTP-ответа"
      Udp.read(packetBuffer, NTP_PACKET_SIZE);  // считываем пакет в буфер
      unsigned long secsSince1900;
      // конвертируем 4 байта (начиная с позиции 40) в длинное целое число:
      secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
      secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
      secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
      secsSince1900 |= (unsigned long)packetBuffer[43];
      return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
    }
  }
  Serial.println("No NTP Response :-(");  //  "Нет NTP-ответа :("
  return 0; // если время получить не удалось, возвращаема «0»
}

// отправляем NTP-запрос серверу времени по указанному адресу:
void sendNTPpacket(IPAddress &address)
{
  // задаем все байты в буфере на «0»:
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // инициализируем значения для создания NTP-запроса
  // (подробнее о пакетах смотрите по ссылке выше)
  packetBuffer[0] = 0b11100011;   // LI (от «leap indicator», т.е. «индикатор перехода»), версия, режим работы
  packetBuffer[1] = 0;     // слой (или тип часов)
  packetBuffer[2] = 6;     // интервал запросов
  packetBuffer[3] = 0xEC;  // точность
  // 8 байтов с нулями, обозначающие базовую задержку и базовую дисперсию:
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;
  // После заполнения всех указанных полей
  // вы сможете отправлять пакет с запросом о временной метке:    
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

См.также

Внешние ссылки