Arduino:Примеры/UdpNtpClient

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

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


Клиент, запрашивающий время через NTP[1]

В этом примере мы воспользуемся Arduino и Ethernet Shield, чтобы делать запросы к серверу Network Time Protocol (NTP). Благодаря этому Arduino сможет получать из интернета информацию о времени.

Необходимое оборудование

  • Сетевой модуль Arduino Ethernet Shield;
  • Плата Arduino, совместимая с Ethernet Shield;

Цепь

Ethernet Shield позволяет подключить плату Arduino к Ethernet-контроллеру WizNet (через SPI). Это подключение осуществляется при помощи 10-ого, 11-ого, 12-ого и 13-ого контактов. У более поздних моделей Ethernet Shield имеется встроенный слот для SD-карты. В этом примере мы будем управлять SS-контактом этой SD-карты через 4-ый цифровой контакт.

Ethernet Shield должен быть подключен к сети при помощи Ethernet-кабеля. Также вам надо поменять сетевые настройки в скетче, чтобы они соответствовали настройкам вашей сети.

Ethernet Shield устанавливается поверх платы Arduino.

Схема

Код

/*
Клиент, запрашивающий время через NTP

Получает информацию о времени от NTP-сервера времени.
Демонстрирует отправку и передачу пакетов через UDP.
Более подробно о время-серверах NTP и сообщениях,
необходимых для их коммуникации, можно прочесть тут:
http://en.wikipedia.org/wiki/Network_Time_Protocol.
 
Внимание: NTP-сервера подвержены временным сбоям и имеют склонность менять IP-адреса.
Если время-сервер, используемый для этого примера, не работает,
пожалуйста, пройдите сюда – http://tf.nist.gov/tf-cgi/servers.cgi.

Создан 4 сентября 2010 Майклом Марголисом (Michael Margolis),
модифицирован 9 апреля 2012 Томом Иго (Tom Igoe).
 
Этот код не защищен авторским правом.
*/

#include <SPI.h>         
#include <Ethernet.h>
#include <EthernetUdp.h>

// Ниже введите MAC-адрес вашего контроллера.
// У модулей Ethernet Shield самых последних моделей MAC-адрес напечатан на стикере.
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

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

IPAddress timeServer(132, 163, 4, 101); // NTP-сервер time-a.timefreq.bldrdoc.gov 
// IPAddress timeServer(132, 163, 4, 102); // NTP-сервер time-b.timefreq.bldrdoc.gov
// IPAddress timeServer(132, 163, 4, 103); // NTP-сервер time-c.timefreq.bldrdoc.gov NTP

const int NTP_PACKET_SIZE= 48; // временная отметка NTP находится в первых 48 байтах сообщения

byte packetBuffer[ NTP_PACKET_SIZE]; // буфер, в котором будут храниться входящие и исходящие пакеты 

// Создаем экземпляр класса EthernetUDP, который позволит нам отправлять и получать пакеты через UDP:
EthernetUDP Udp;

void setup() 
{
 // Инициализируем последовательную передачу данных и ждем открытия порта:
  Serial.begin(9600);
   while (!Serial) {
    ; // ждем подключения последовательного порта (нужно только для Leonardo)
  }

  // Запускаем Ethernet и UDP:
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");  //  "Не удалось сконфигурировать Ethernet при помощи UDP"
    // Продолжать дальше смысла нет, поэтому дальше ничего не делаем:
    for(;;)
      ;
  }
  Udp.begin(localPort);
}

void loop()
{
  sendNTPpacket(timeServer); // Отсылаем время-серверу NTP-пакет 

    // Ждем, чтобы увидеть, доступен ли ответ:
  delay(1000);  
  if ( Udp.parsePacket() ) {  
    // Пакет получен, значит считываем данные оттуда:
    Udp.read(packetBuffer,NTP_PACKET_SIZE);  // считываем содержимое пакета в буфер

    // Временная отметка начинается с 40 байта полученного пакета
    // и его длина составляет четыре байта или два слова.
    // Для начала извлекаем два этих слова:
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);  
    // Совмещаем четыре байта (два слова) в длинное целое. 
    // Это и будет NTP-временем (секунды начиная с 1 января 1990 года):
    unsigned long secsSince1900 = highWord << 16 | lowWord;  
    Serial.print("Seconds since Jan 1 1900 = " );  //  "Секунды, прошедшие с 1 января 1990 = "
    Serial.println(secsSince1900);      

    // Теперь конвертируем NTP-время в обычное время:
    Serial.print("Unix time = ");  //  "Unix-время = "
    // Время Unix стартует с 1 января 1970 года. В секундах это 2208988800:
    const unsigned long seventyYears = 2208988800UL;     
    // Вычитаем 70 лет:
    unsigned long epoch = secsSince1900 - seventyYears;  
    // Выводим на Serial Monitor Unix-время:
    Serial.println(epoch);                               


    // Выводим на Serial Monitor информацию о часах, минутах и секундах:
    Serial.print("The UTC time is ");       // "Среднее время по Гринвичу = ",  // UTC – это среднее время по Гринвичу (GMT):
    Serial.print((epoch  % 86400L) / 3600); // выводим часы (86400 эквивалентно количеству секунд в день):
    Serial.print(':');  
    if ( ((epoch % 3600) / 60) < 10 ) {
      // У первых десяти минут каждого часа должна спереди стоять цифра «0»:
      Serial.print('0');
    }
    Serial.print((epoch  % 3600) / 60); // выводим минуты (3600 эквивалентно количеству секунд в минуте):
    Serial.print(':'); 
    if ( (epoch % 60) < 10 ) {
      // У первых десяти секунд каждой минуты спереди должна стоять цифра «0»:
      Serial.print('0');
    }
    Serial.println(epoch %60); // выводим секунды
  }
  // Ждем 10 секунд перед новым запросом о времени:
  delay(10000); 
}

// Отправляем NTP-запрос к время-серверу по указанному адресу: 
unsigned long sendNTPpacket(IPAddress& address)
{
  // Отправляем все байты (вплоть до 0-ого) в буфер:
  memset(packetBuffer, 0, NTP_PACKET_SIZE); 
  // Инициализируем значения, необходимые для формирования NTP-запроса
  // (подробнее о пакетах см. в URL выше):
  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-запрос идет на порт 123
  Udp.write(packetBuffer,NTP_PACKET_SIZE);
  Udp.endPacket(); 
}

См.также

  1. Arduino Ethernet Shield
  2. Getting started with the ethernet shield
  3. Ethernet library
  4. ChatServer
  5. ChatClient
  6. WebClient
  7. WebServer
  8. PachubeClient?
  9. PachubeClientString?
  10. BarometricPressureWebServer
  11. UDPSendReceiveString
  12. UdpNtpClient

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