В данной статье мы научимся использовать модуль LwIP SNTP на плате ESP32 и функции работы со временем для получения времени с интернет-серверов с помощью ESP-IDF. SNTP (Simple Network Time Protocol), также известный как «Простой сетевой протокол времени», — это протокол, используемый для получения текущего времени и даты. Библиотека LwIP SNTP, доступная в esp-idf, помогает получать текущее время и синхронизировать таймер ESP32 с NTP-сервером. Это очень полезно в приложениях для регистрации данных, где необходимо записывать значения данных вместе с правильными метками времени через определенный интервал времени.
Также на нашем сайте вы можете посмотреть статью про интернет часы на ESP32 с получением времени по протоколу NTP (с помощью Arduino IDE).
API ESP32 для работы с SNTP с помощью ESP-IDF
Теперь давайте обсудим API библиотеки LwIP SNTP, предоставляемые ESP-IDF для получения текущего времени.
Это заголовочный файл, необходимый для подключения библиотеки:
|
1 |
#include "esp_sntp.h" |
Функция gettimeofday обычно используется для доступа к текущей метке времени. Аналогично, функция settimeofday используется для установки времени. Именно эта функция используется библиотекой LwIP для установки текущего времени при получении ответа от NTP-сервера.
Однако стандартная библиотека C предоставляет еще несколько функций, связанных со временем. Некоторые из них перечислены ниже:
|
1 2 3 4 5 6 7 8 9 10 |
gettimeofday time asctime clock ctime difftime gmtime localtime mktime strftime |
Функция sntp_get_sync_status() используется для получения статуса времени. Если время синхронизировано, она возвращает ответ SNTP_SYNC_RETURN_COMPLETED. Если статус сброшен, она возвращает SNTP_SYC_STATUS_RESET. Кроме того, если происходит плавная синхронизация времени, она возвращает ответ SNTP_SYNC_STATUS_IN_PROGRESS.
|
1 |
sntp_sync_status_t sntp_get_sync_status(void) |
Функция sntp_set_sync_status() используется для установки статуса синхронизации времени. Она принимает один параметр — ‘sync_status’, обозначающий статус синхронизации времени.
|
1 |
void sntp_set_sync_status(sntp_sync_status_t sync_status) |
Далее у нас есть функция sntp_get_sync_mode() . Она используется для получения режима синхронизации времени. Эта функция возвращает ответ SNTP_SYNC_MODE_IMMED, если синхронизация настроена на немедленное обновление времени. В противном случае она возвращает ответ SNTP_SYNC_MODE_SMOOTH, если включено плавное обновление времени.
|
1 |
sntp_sync_mode_t sntp_get_sync_mode(void) |
Функция sntp_set_sync_mode() используется для установки режима синхронизации времени. Она принимает один параметр ‘sync_mode’, который обозначает режим синхронизации. Режим может быть либо SNTP_SYNC_MODE_IMMED, либо SNTP_SYNC_MODE_SMOOTH. При выборе режима SNTP_SYNC_MODE_IMMED системное время обновляется сразу после получения от SNTP-сервера. При выборе режима SNTP_SYNC_MODE_SMOOTH устанавливается плавное обновление времени. В этом случае для уведомления о времени используется функция adjtime.
|
1 |
void sntp_set_sync_mode(sntp_sync_mode_t sync_mode) |
Функция sntp_set_time_sync_notification_cb() используется для установки функции обратного вызова для уведомления о синхронизации времени. Она принимает один параметр — функцию обратного вызова.
|
1 |
void sntp_set_time_sync_notification_cb(sntp_sync_time_cb_t callback) |
Пример ESP-IDF для работы с SNTP
В этом разделе мы создадим и протестируем пример, используя API LwIP SNTP, как описано ранее. Мы будем использовать пример SNTP, предоставленный ESP-IDF. На примере мы покажем, как получить текущее время с помощью SNTP.
Создание примера проекта
Откройте VS Code и перейдите в менюView > Command Palette («Вид» > «Палитра команд»). Введите в строке поиска ESP-IDF: New Project и нажмите Enter.
Укажите название проекта и каталог. Мы назвали наш проект «ESP_IDF_SNTP». Для платы ESP-IDF мы выбрали опцию custom board («Пользовательская плата»). В качестве целевой платформы ESP-IDF мы выбрали модуль ESP32. Нажмите кнопку ‘Choose Template’ («Выбрать шаблон»), чтобы продолжить.
В разделе Extension («Расширение») выберите опцию ESP-IDF:
На вкладке Protocols («Протоколы») мы щелкнем по пункту «sntp». Затем нажмем ‘Create project using template sntp’ («Создать проект, используя шаблон sntp»).
Вы получите уведомление о создании проекта. Чтобы открыть проект в новом окне, нажмите «Да».
Это откроет созданный нами проект ESP_IDF_SNTP во вкладке «Проводник». Внутри папки проекта находится несколько папок. Это справедливо для всех проектов, которые вы будете создавать с помощью ESP-IDF Explorer.
Редактор конфигурации ESP-IDF SDK
Для начала перейдем в меню menuconfig. Щелкните значок, показанный ниже. Откроется редактор конфигурации ESP-IDF SDK.
Прокрутите вниз и откройте Example Connection Configuration (пример конфигурации подключения). Здесь нам нужно указать SSID и пароль нашей сети Wi-Fi. Это необходимо, потому что в примере с SNTP при первой загрузке ESP32 подключается к интернету и использует SNTP для получения текущего времени. Мы укажем параметры подключения. Также есть возможность подключения через интерфейс Ethernet.
Далее прокрутите вниз до раздела Example Configuration («Пример конфигурации») и выберите метод синхронизации времени. В этом примере представлены три метода синхронизации времени.
- Обновление времени сразу после получения: это метод, выбранный по умолчанию. В этом случае время обновляется немедленно после получения от SNTP-сервера.
- Обновление времени с помощью плавного метода (adjtime): в этом методе функция adjtime используется для уведомления о времени.
- Пользовательская реализация: в этом методе встроенная синхронизация времени не используется.
Мы используем опцию ‘Update Time immediately when received’ («Обновлять время сразу после получения»).
Далее перейдите к настройкам, специфичным для ESP32 (ESP32-specific). Убедитесь, что для параметра “Timers used for gettimeofday function” («Таймеры, используемые для функции gettimeofday») выбрана опция “RTC and high-resolution timer” («RTC и таймер высокого разрешения») или «RTC». Это гарантирует, что после синхронизации с SNTP-сервером отсчет времени будет выполняться с помощью этих таймеров. Когда плата ESP32 находится в режиме глубокого сна, для отслеживания времени будет использоваться встроенный таймер RTC. Аналогично, таймер высокого разрешения FRC1 обеспечит точность времени до микросекунд, пока плата находится в активном режиме.
Пример кода ESP-IDF для работы с SNTP
Перейдём к файлу main.c. В папке main находится исходный код, поэтому файл main.c будет расположен именно там.
Теперь перейдите в файл main > sntp_example_main.c и откройте его.
Ниже приведён код, который вы увидите в исходном коде. Этот пример кода демонстрирует синхронизацию платы ESP32 с SNTP-сервером для получения текущего времени. После первоначальной загрузки плата ESP32 подключается к Wi-Fi, синхронизируется с SNTP-сервером и получает текущее время. Затем плата ESP32 переходит в режим глубокого сна. В это время таймер RTC отслеживает текущее время. После пробуждения ESP32 выводит текущее время, не подключаясь к Wi-Fi.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
/* LwIP SNTP example This example code is in the Public Domain (or CC0 licensed, at your option.) Unless required by applicable law or agreed to in writing, this software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ #include <string.h> #include <time.h> #include <sys/time.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" #include "esp_system.h" #include "esp_event.h" #include "esp_log.h" #include "esp_attr.h" #include "esp_sleep.h" #include "nvs_flash.h" #include "protocol_examples_common.h" #include "esp_sntp.h" static const char *TAG = "example"; /* Variable holding number of times ESP32 restarted since first boot. * It is placed into RTC memory using RTC_DATA_ATTR and * maintains its value when ESP32 wakes from deep sleep. */ RTC_DATA_ATTR static int boot_count = 0; static void obtain_time(void); static void initialize_sntp(void); #ifdef CONFIG_SNTP_TIME_SYNC_METHOD_CUSTOM void sntp_sync_time(struct timeval *tv) { settimeofday(tv, NULL); ESP_LOGI(TAG, "Time is synchronized from custom code"); sntp_set_sync_status(SNTP_SYNC_STATUS_COMPLETED); } #endif void time_sync_notification_cb(struct timeval *tv) { ESP_LOGI(TAG, "Notification of a time synchronization event"); } void app_main(void) { ++boot_count; ESP_LOGI(TAG, "Boot count: %d", boot_count); time_t now; struct tm timeinfo; time(&now); localtime_r(&now, &timeinfo); // Is time set? If not, tm_year will be (1970 - 1900). if (timeinfo.tm_year < (2016 - 1900)) { ESP_LOGI(TAG, "Time is not set yet. Connecting to WiFi and getting time over NTP."); obtain_time(); // update 'now' variable with current time time(&now); } #ifdef CONFIG_SNTP_TIME_SYNC_METHOD_SMOOTH else { // add 500 ms error to the current system time. // Only to demonstrate a work of adjusting method! { ESP_LOGI(TAG, "Add a error for test adjtime"); struct timeval tv_now; gettimeofday(&tv_now, NULL); int64_t cpu_time = (int64_t)tv_now.tv_sec * 1000000L + (int64_t)tv_now.tv_usec; int64_t error_time = cpu_time + 500 * 1000L; struct timeval tv_error = { .tv_sec = error_time / 1000000L, .tv_usec = error_time % 1000000L }; settimeofday(&tv_error, NULL); } ESP_LOGI(TAG, "Time was set, now just adjusting it. Use SMOOTH SYNC method."); obtain_time(); // update 'now' variable with current time time(&now); } #endif char strftime_buf[64]; // Set timezone to Eastern Standard Time and print local time setenv("TZ", "EST5EDT,M3.2.0/2,M11.1.0", 1); tzset(); localtime_r(&now, &timeinfo); strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo); ESP_LOGI(TAG, "The current date/time in New York is: %s", strftime_buf); // Set timezone to China Standard Time setenv("TZ", "CST-8", 1); tzset(); localtime_r(&now, &timeinfo); strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo); ESP_LOGI(TAG, "The current date/time in Shanghai is: %s", strftime_buf); if (sntp_get_sync_mode() == SNTP_SYNC_MODE_SMOOTH) { struct timeval outdelta; while (sntp_get_sync_status() == SNTP_SYNC_STATUS_IN_PROGRESS) { adjtime(NULL, &outdelta); ESP_LOGI(TAG, "Waiting for adjusting time ... outdelta = %li sec: %li ms: %li us", (long)outdelta.tv_sec, outdelta.tv_usec/1000, outdelta.tv_usec%1000); vTaskDelay(2000 / portTICK_PERIOD_MS); } } const int deep_sleep_sec = 10; ESP_LOGI(TAG, "Entering deep sleep for %d seconds", deep_sleep_sec); esp_deep_sleep(1000000LL * deep_sleep_sec); } static void obtain_time(void) { ESP_ERROR_CHECK( nvs_flash_init() ); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK( esp_event_loop_create_default() ); /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. * Read "Establishing Wi-Fi or Ethernet Connection" section in * examples/protocols/README.md for more information about this function. */ ESP_ERROR_CHECK(example_connect()); initialize_sntp(); // wait for time to be set time_t now = 0; struct tm timeinfo = { 0 }; int retry = 0; const int retry_count = 10; while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET && ++retry < retry_count) { ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count); vTaskDelay(2000 / portTICK_PERIOD_MS); } time(&now); localtime_r(&now, &timeinfo); ESP_ERROR_CHECK( example_disconnect() ); } static void initialize_sntp(void) { ESP_LOGI(TAG, "Initializing SNTP"); sntp_setoperatingmode(SNTP_OPMODE_POLL); sntp_setservername(0, "pool.ntp.org"); sntp_set_time_sync_notification_cb(time_sync_notification_cb); #ifdef CONFIG_SNTP_TIME_SYNC_METHOD_SMOOTH sntp_set_sync_mode(SNTP_SYNC_MODE_SMOOTH); #endif sntp_init(); } |
Как работает код?
Для начала мы подключим необходимые библиотеки, включая библиотеки FreeRTOS для генерации задержек и создания задач, sys/time.h и esp_sntp.h для доступа ко времени, esp_log.h для отладки и вывода информационных логов, esp_sleep.h для перевода платы ESP32 в режим глубокого сна и т.д.
статья в разработке
