Получение даты и времени с NTP-сервера с помощью ESP32

Получение даты и времени с NTP-сервера с помощью ESP32

Представьте, что вы создаёте умное устройство, которое включает лампу в спальне ровно в 7:00 утра, или метеостанцию, которая записывает данные о температуре каждые 15 минут. Может быть, вы создаёте свои собственные интернет-часы, которые всегда показывают точное время с точностью до секунды. Во всех этих проектах одно становится совершенно очевидным — поддержание точного времени абсолютно необходимо.

Именно здесь на помощь приходит NTP, или Network Time Protocol (протокол сетевого времени). NTP — это интернет-протокол, который позволяет вашему ESP32 (или любому компьютеру или устройству) синхронизировать свои часы с чрезвычайно точными серверами времени. И самое лучшее в этом то, что это совершенно бесплатно, и вам не нужно подключать никакое дополнительное оборудование. Всё, что вам нужно — это подключение к интернету.

В этом руководстве вы узнаете, что такое NTP, как он работает и как использовать его с вашим ESP32 для получения точного текущего времени.

Давайте начнём!

Получение даты и времени с NTP-сервера с помощью ESP32

Подождите, разве в ESP32 нет встроенных часов?

Вы можете задаться вопросом: «В ESP32 есть встроенный RTC, так зачем мне вообще NTP?» Это отличный вопрос!

Да, ESP32 действительно поставляется с внутренним модулем RTC. Он может отслеживать часы, минуты и секунды, даже пока выполняет другие задачи. Но есть подвох.

  • Когда ваш ESP32 впервые включается, его внутренние часы не имеют представления о том, какое сейчас реальное время. Они просто начинают отсчёт с нуля, если вы сами не зададите правильное время.

  • Также, в отличие от некоторых внешних модулей RTC (таких как DS1307 или DS3231), которые имеют резервное питание от батареи, RTC ESP32 не запоминает время при потере питания или перезагрузке.

  • И даже если вы будете держать ESP32 постоянно включённым, его внутренние часы могут постепенно отставать и становиться менее точными со временем. Через несколько дней или недель они могут отставать на несколько секунд или даже минут.

Вот почему NTP так полезен. Он позволяет ESP32 получать реальное время со специальных NTP-серверов в интернете, которые синхронизированы с атомными часами — одними из самых точных часов в мире. Если ваш ESP32 перезагрузится или потеряет питание, он может просто снова запросить правильное время у NTP-сервера. И если его внутренние часы начнут отставать, ESP32 может время от времени повторно синхронизироваться с NTP-сервером, чтобы скорректировать их и оставаться точным.

Как на самом деле работает NTP?

NTP использует иерархическую систему, называемую «Стратами» (множественное число от stratum — «слой»). Каждый страт имеет связанное с ним значение, которое используется для определения точности часов. Эти значения варьируются от 0 до 15, где 0 — наиболее точный, а 15 — наименее точный. Всё, что выше 15, означает, что часам нельзя доверять.

Иерархическая архитектура NTP со стратами

На вершине иерархии находится Страт 0. Это не компьютеры, а высокоточные устройства хранения времени, такие как атомные часы, спутники GPS или радиочасы. Устройства Страта 0 не подключаются напрямую к интернету. Вместо этого они подключены к серверам Страта 1. Это первичные серверы времени, которые получают время непосредственно от устройств Страта 0.

Серверы Страта 2 получают время от серверов Страта 1, а серверы Страта 3 получают время от Страта 2, и так далее. По мере продвижения вниз по иерархии время становится немного менее точным из-за сетевых задержек, но оно всё ещё достаточно точно для большинства повседневных задач.

Ваш персональный компьютер или типичный веб-сервер обычно подключается к серверу Страта 2 или Страта 3 для получения времени.

Как ESP32 получает время с помощью NTP?

Давайте рассмотрим реальный пример того, как микроконтроллер ESP32 получает текущее время и дату с помощью NTP.

Во-первых, нам нужен источник времени. Для небольших проектов этим источником обычно является NTP-сервер в интернете. Один из самых популярных вариантов — pool.ntp.org. Это бесплатный открытый проект, который подключает ваше устройство к сети серверов времени со всего мира.

Также существуют региональные версии пула NTP, предназначенные для разных частей мира, например:

Регион

Имя хоста

Весь мир

pool.ntp.org

Азия

asia.pool.ntp.org

Европа

europe.pool.ntp.org

Северная Америка

north-america.pool.ntp.org

Океания

oceania.pool.ntp.org

Южная Америка

south-america.pool.ntp.org

Когда ваше устройство подключается к pool.ntp.org, оно автоматически пытается найти сервер, который является одновременно надёжным и быстрым. Однако, если вы знаете, где расположено ваше устройство, использование регионального сервера может дать лучшие результаты, потому что эти серверы географически ближе к вам, а значит, они могут отвечать быстрее. Это помогает вашему ESP32 синхронизировать время быстрее и точнее.

Когда ESP32 (который выступает в роли клиента) хочет получить время, он отправляет запрос на NTP-сервер, используя так называемый протокол пользовательских датаграмм (UDP). В отличие от TCP, который постоянно проверяет, что данные доставлены корректно, UDP быстрее и легче, что идеально подходит для быстрых проверок времени. NTP конкретно использует UDP-порт 123.

Работа NTP-сервера — передача пакетов с запросом временной метки

Сервер отвечает, отправляя обратно пакет данных, содержащий текущее время в формате координированного всемирного времени (UTC). UTC — это глобальный стандарт времени, очень похожий на среднее время по Гринвичу (GMT). Важная особенность UTC заключается в том, что оно не меняется в зависимости от часовых поясов или перехода на летнее время; оно остаётся одинаковым во всём мире.

После того как ESP32 получает время UTC, он корректирует свои собственные внутренние часы. Затем, при необходимости, он применяет смещение местного часового пояса (например, преобразование в восточное или тихоокеанское время) или корректировку перехода на летнее время.

После синхронизации ESP32 с NTP-сервером он отслеживает время, используя свои внутренние часы реального времени (RTC). Ему не нужно запрашивать время каждую секунду. Вместо этого он периодически повторно синхронизируется, возможно, раз в час или раз в день, чтобы предотвратить слишком сильное отклонение часов.

Настройка Arduino IDE

Теперь, когда вы понимаете, как работает NTP и как ESP32 может его использовать, пришло время посмотреть, как мы можем запрограммировать ESP32 для фактического получения текущей даты и времени с NTP-сервера.

Мы будем использовать Arduino IDE для программирования ESP32, поэтому, пожалуйста, убедитесь, что у вас установлено дополнение ESP32, прежде чем продолжить:

Установка платы ESP32 в Arduino IDE

Микроконтроллер ESP32 быстро стал одной из самых популярных плат среди любителей, инженеров и людей, интересующихся Интернетом вещей (IoT)…

/lastminuteengineers/esp32-arduino-ide-tutorial/index

Пример кода

Приведённый ниже пример показывает, как получить текущую дату и время с NTP-сервера с помощью вашего ESP32. Скопируйте код в Arduino IDE, но пока не загружайте его! Сначала нужно внести несколько важных изменений, чтобы всё работало правильно для вашей конфигурации.

  1. Во-первых, вам нужно указать ESP32, как подключиться к вашей сети Wi-Fi. Найдите эти две строки в коде и замените текст-заглушку на ваше реальное имя сети и пароль:

// Replace with your network credentials
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
  1. Далее вам нужно установить правильное смещение UTC для вашего часового пояса. Помните, NTP-серверы предоставляют время в UTC. Это стандартное время, используемое во всём мире, и оно не зависит от местоположения. Поскольку вы, вероятно, не находитесь в часовом поясе UTC, вам нужно указать ESP32 смещение, чтобы помочь ему настроиться на ваше местное время. Ваш местный часовой пояс находится на определённое количество часов впереди или позади UTC.

    Смещение должно быть записано в секундах. Вот как его рассчитать: умножьте количество часов на 60 (минуты), а затем снова на 60 (секунды). Например:

    • Если вы находитесь в восточном стандартном времени (UTC -5), ваше смещение: -5 x 60 x 60 = -18000

    • Если вы находитесь в центральноевропейском времени (UTC +1), ваше смещение: 1 x 60 x 60 = 3600

    • Если вы находитесь в UTC +0 (как Лондон), ваше смещение: 0 x 60 x 60 = 0

const long gmtOffset_sec = -18000;

Вы можете найти полный список смещений UTC здесь, чтобы найти ваш конкретный часовой пояс.

  1. Вам также нужно установить смещение для перехода на летнее время. Если ваша страна или регион соблюдают переход на летнее время, установите это значение равным 3600 (что равно 1 часу). Если ваш регион не использует переход на летнее время, просто установите его на 0.

const int daylightOffset_sec = 3600;

После внесения всех этих изменений вы готовы загрузить код в ваш ESP32!

// Libraries to get time from NTP Server
#include <WiFi.h>
#include <time.h>

// Replace with your network credentials
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";

// NTP server setup
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = -18000;    // Adjust this for your timezone
const int daylightOffset_sec = 3600;  // Adjust if DST is in effect

// Function that prints formatted date and time
void printDateTime() {
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
  }
  char formattedTime[80];  // Buffer to store the formatted string
  strftime(formattedTime, sizeof(formattedTime), "%A, %B %d %Y %H:%M:%S", &timeinfo);
  Serial.println(formattedTime);
}

void setup() {
  Serial.begin(115200);

  // Connect to WiFi
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println("\nConnected to WiFi!");

  // Configure NTP
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  Serial.println("NTP time configured.");
}

void loop() {
  // Print formatted date and time
  printDateTime();
  delay(1000);
}

Демонстрация

После загрузки кода откройте монитор порта в Arduino IDE и убедитесь, что скорость передачи установлена на 115200. Затем нажмите кнопку EN (сброс) на вашем ESP32. Если всё настроено правильно, вы должны увидеть текущую дату и время, печатаемые каждую секунду в мониторе порта.

ESP32 считывает дату и время с NTP-сервера — вывод в мониторе порта

Объяснение кода

Давайте кратко рассмотрим код, чтобы понять, как он работает.

Код начинается с подключения двух важных библиотек. Библиотека WiFi.h помогает ESP32 подключаться к Wi-Fi. Библиотека time.h позволяет ESP32 обмениваться данными с NTP-сервером и форматировать время в читаемый формат.

#include <WiFi.h>
#include "time.h"

Далее мы определяем две переменные для вашего Wi-Fi-соединения: одну для имени сети (SSID) и одну для пароля. Этот шаг очень важен, потому что ESP32 нуждается в доступе к интернету для связи с NTP-сервером и получения правильного времени. Вот почему вам нужно было заменить текст-заглушку на ваши реальные учётные данные сети ранее.

const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASS";

После этого мы настраиваем параметры NTP. Мы выбираем сервер времени, в данном случае «pool.ntp.org», и указываем ESP32, как настроить смещение от UTC к вашему местному часовому поясу.

Мы используем gmtOffset_sec для сдвига времени к вашему часовому поясу и daylightOffset_sec для добавления дополнительного часа, если ваше местоположение соблюдает переход на летнее время. Например, gmtOffset_sec равный -18000 представляет UTC-5 (восточное стандартное время), а daylightOffset_sec равный 3600 добавляет один час для перехода на летнее время.

Очень важно правильно установить эти значения! Если они неверны, ваши временные метки будут неправильными, даже если NTP-соединение работает идеально.

const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = -18000;
const int daylightOffset_sec = 3600;

Как упоминалось ранее, «pool.ntp.org» — хороший сервер по умолчанию, потому что он автоматически подключает вас к быстрому и надёжному серверу времени. Однако, если вы предпочитаете, вы можете использовать региональный сервер, такой как «europe.pool.ntp.org» или «asia.pool.ntp.org», в зависимости от вашего местоположения.

В секции setup мы сначала устанавливаем последовательную связь с компьютером, а затем пытаемся подключить ESP32 к сети Wi-Fi. Мы используем функцию WiFi.mode() для перевода радиомодуля Wi-Fi в режим станции, а функцию WiFi.begin() для фактического запуска процесса подключения. Пока идёт подключение, вы будете видеть точки, появляющиеся в мониторе порта. После подключения вы увидите сообщение «Connected to WiFi!».

Serial.begin(115200);

// Connect to WiFi
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi ..");
while (WiFi.status() != WL_CONNECTED) {
  Serial.print('.');
  delay(1000);
}
Serial.println("\nConnected to WiFi!");

После того как ESP32 подключён к сети, мы вызываем функцию configTime(). Эта функция синхронизирует внутренние часы ESP32 с NTP-сервером, используя смещение часового пояса, настройку перехода на летнее время и адрес NTP-сервера, которые мы настроили ранее.

Как только это сделано, ESP32 знает правильную дату и время и будет отслеживать их даже при отключении Wi-Fi, используя свои внутренние часы.

// Configure NTP
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
Serial.println("NTP time configured.");

Затем у нас есть пользовательская функция под названием printDateTime(). Эта функция выводит текущую дату и время в монитор порта в красивом, читаемом формате. Сначала она запрашивает у внутренних часов ESP32 текущее время (которое было установлено ранее с помощью NTP-сервера). Затем она форматирует эту информацию в удобочитаемую строку вроде «Wednesday, December 03 2025 15:42:01», используя встроенную функцию strftime(). Если что-то идёт не так и она не может получить время, вместо этого выводится сообщение об ошибке.

void printDateTime() {
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
  }
  char formattedTime[80];  // Buffer to store the formatted string
  strftime(formattedTime, sizeof(formattedTime), "%A, %B %d %Y %H:%M:%S", &timeinfo);
  Serial.println(formattedTime);
}

Строка формата использует различные коды (называемые спецификаторами), начинающиеся с символа %, для представления различных частей даты и времени.

Вот список спецификаторов, которые вы можете использовать для настройки отображения даты и времени. Например:

%a

Сокращённое название дня недели (например, «Sun»)

%A

Полное название дня недели (например, «Sunday»)

%b

Сокращённое название месяца (например, «Jan»)

%B

Полное название месяца (например, «January»)

%d

День месяца в виде десятичного числа (01-31)

%H

Час (24-часовой формат) в виде десятичного числа (00-23)

%I

Час (12-часовой формат) в виде десятичного числа (01-12)

%M

Минута в виде десятичного числа (00-59)

%S

Секунда в виде десятичного числа (00-59)

%Y

Год с указанием века в виде десятичного числа (например, 2025)

%y

Год без указания века в виде десятичного числа (00-99)

%c

Представление даты и времени, соответствующее локали

%x

Представление даты, соответствующее локали

%X

Представление времени, соответствующее локали

%p

Индикатор AM/PM для 12-часового формата

%Z

Название часового пояса

%%

Символ «%» (литерал)

Наконец, в секции loop мы просто вызываем printDateTime() каждую секунду для вывода текущей даты и времени в монитор порта.

void loop() {
  // Print formatted date and time
  printDateTime();
  delay(1000);
}