feat(esp_netif): Support posting event on SNTP time update

This commit is contained in:
David Cermak
2025-09-22 08:14:04 +02:00
committed by David Čermák
parent 6bfcd4b41c
commit 787bb39765
8 changed files with 131 additions and 4 deletions
@@ -17,6 +17,29 @@
extern "C" {
#endif
/**
* @brief SNTP event base for esp-netif
*/
ESP_EVENT_DECLARE_BASE(NETIF_SNTP_EVENT);
/**
* @brief Event IDs for NETIF_SNTP_EVENT
*/
typedef enum {
NETIF_SNTP_TIME_SYNC = 0, /**< System time synchronized via SNTP */
} esp_netif_sntp_event_t;
/**
* @brief Event payload for NETIF_SNTP_TIME_SYNC
*
* The event data passed to handlers registered for
* `NETIF_SNTP_EVENT`/`NETIF_SNTP_TIME_SYNC` is a pointer to this struct.
*/
typedef struct esp_netif_sntp_time_sync {
struct timeval tv; ///< Time of synchronization as reported by SNTP
} esp_netif_sntp_time_sync_t;
/**
* @brief Time sync notification function
*/
+11 -1
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -19,6 +19,8 @@
static const char *TAG = "esp_netif_sntp";
ESP_EVENT_DEFINE_BASE(NETIF_SNTP_EVENT);
typedef struct sntp_storage {
esp_sntp_time_cb_t sync_cb;
SemaphoreHandle_t sync_sem;
@@ -38,6 +40,14 @@ static void sync_time_cb(struct timeval *tv)
if (s_storage && s_storage->sync_cb) {
s_storage->sync_cb(tv);
}
// Post event with the timeval payload
esp_netif_sntp_time_sync_t evt = {0};
if (tv) {
evt.tv = *tv;
}
if (esp_event_post(NETIF_SNTP_EVENT, NETIF_SNTP_TIME_SYNC, &evt, sizeof(evt), 0) != ESP_OK) {
ESP_LOGE(TAG, "Error posting SNTP time sync");
}
}
static esp_err_t sntp_init_api(void *ctx)
@@ -9,6 +9,7 @@
#include "unity_fixture.h"
#include "esp_netif.h"
#include "esp_netif_sntp.h"
#include "esp_event.h"
#include "esp_netif_net_stack.h"
#include "esp_wifi.h"
#include "nvs_flash.h"
@@ -18,8 +19,9 @@
#include "memory_checks.h"
#include "lwip/netif.h"
#include "esp_netif_test.h"
#include "esp_event.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "sntp/sntp_get_set_time.h"
TEST_GROUP(esp_netif);
@@ -68,6 +70,44 @@ TEST(esp_netif, init_and_destroy_sntp)
esp_netif_sntp_deinit();
}
static SemaphoreHandle_t s_sntp_evt_sem;
static void sntp_event_handler(void *arg, esp_event_base_t base, int32_t id, void *data)
{
(void)arg; (void)base; (void)id; (void)data;
if (s_sntp_evt_sem) {
xSemaphoreGive(s_sntp_evt_sem);
}
}
TEST(esp_netif, sntp_posts_time_sync_event)
{
test_case_uses_tcpip();
TEST_ESP_OK(esp_event_loop_create_default());
// Register handler for NETIF_SNTP_EVENT
s_sntp_evt_sem = xSemaphoreCreateBinary();
TEST_ASSERT_NOT_NULL(s_sntp_evt_sem);
TEST_ESP_OK(esp_event_handler_register(NETIF_SNTP_EVENT, NETIF_SNTP_TIME_SYNC, &sntp_event_handler, NULL));
// Initialize esp-netif SNTP (no auto-start needed for this test)
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG("127.0.0.1");
config.start = false;
TEST_ESP_OK(esp_netif_sntp_init(&config));
// Trigger the SNTP time sync callback path artificially
sntp_set_system_time(1, 0);
// Wait for event to be posted
TEST_ASSERT_EQUAL(pdTRUE, xQueueSemaphoreTake(s_sntp_evt_sem, pdMS_TO_TICKS(1000)));
// Cleanup
esp_netif_sntp_deinit();
TEST_ESP_OK(esp_event_handler_unregister(NETIF_SNTP_EVENT, NETIF_SNTP_TIME_SYNC, &sntp_event_handler));
vSemaphoreDelete(s_sntp_evt_sem);
s_sntp_evt_sem = NULL;
TEST_ESP_OK(esp_event_loop_delete_default());
}
TEST(esp_netif, convert_ip_addresses)
{
const char *ipv4_src[] = {"127.168.1.1", "255.255.255.0", "305.500.721.801", "127.168.1..", "abc.def.***.ddd"};
@@ -666,6 +706,7 @@ TEST_GROUP_RUNNER(esp_netif)
*/
RUN_TEST_CASE(esp_netif, init_and_destroy)
RUN_TEST_CASE(esp_netif, init_and_destroy_sntp)
RUN_TEST_CASE(esp_netif, sntp_posts_time_sync_event)
RUN_TEST_CASE(esp_netif, convert_ip_addresses)
RUN_TEST_CASE(esp_netif, get_from_if_key)
RUN_TEST_CASE(esp_netif, create_delete_multiple_netifs)
@@ -40,6 +40,31 @@ This section provides more details on specific use cases for the SNTP service, s
3. Wait for the system time to synchronize using :cpp:func:`esp_netif_sntp_sync_wait()` (if required).
4. Stop and destroy the service using :cpp:func:`esp_netif_sntp_deinit()`.
Events
^^^^^^
The SNTP wrapper posts an event when the system time is synchronized:
- Event base: ``NETIF_SNTP_EVENT``
- Event ID: ``NETIF_SNTP_TIME_SYNC``
- Event data: pointer to :cpp:type:`esp_netif_sntp_time_sync_t` with the synchronized ``timeval``
Register a handler after creating the default event loop:
.. code-block:: c
static void sntp_evt_handler(void *arg, esp_event_base_t base, int32_t id, void *data)
{
const esp_netif_sntp_time_sync_t *evt = (const esp_netif_sntp_time_sync_t *)data;
if (evt) {
ESP_LOGI("sntp", "time synchronized: %ld.%06ld", (long)evt->tv.tv_sec, (long)evt->tv.tv_usec);
// Optionally convert to human-readable time using localtime_r() or gmtime_r()
}
}
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(esp_event_handler_register(NETIF_SNTP_EVENT, NETIF_SNTP_TIME_SYNC, &sntp_evt_handler, NULL));
Basic Mode with Statically Defined Server(s)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+1 -1
View File
@@ -105,7 +105,7 @@ To initialize a particular SNTP server and also start the SNTP service, simply c
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG("pool.ntp.org");
esp_netif_sntp_init(&config);
This code automatically performs time synchronization once a reply from the SNTP server is received. Sometimes it is useful to wait until the time gets synchronized, :cpp:func:`esp_netif_sntp_sync_wait()` can be used for this purpose:
This code automatically performs time synchronization once a reply from the SNTP server is received. Sometimes it is useful to wait until the time gets synchronized, :cpp:func:`esp_netif_sntp_sync_wait()` can be used for this purpose. Applications can also subscribe to an event posted when time is synchronized (``NETIF_SNTP_EVENT``, ID ``NETIF_SNTP_TIME_SYNC``). The event data is a pointer to :cpp:type:`esp_netif_sntp_time_sync_t` containing the synchronized ``timeval``:
.. code-block:: c
@@ -105,7 +105,7 @@ SNTP 时间同步
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG("pool.ntp.org");
esp_netif_sntp_init(&config);
一旦收到 SNTP 服务器的响应,此代码会自动执行时间同步。有时等待时间同步很有意义,调用 :cpp:func:`esp_netif_sntp_sync_wait()` 可实现此目的:
一旦收到 SNTP 服务器的响应,此代码会自动执行时间同步。有时等待时间同步很有意义,调用 :cpp:func:`esp_netif_sntp_sync_wait()` 可实现此目的。应用也可以订阅时间同步事件(事件基座 ``NETIF_SNTP_EVENT``,事件 ID ``NETIF_SNTP_TIME_SYNC``
.. code-block:: c
+9
View File
@@ -83,6 +83,15 @@ This example can use 3 time synchronization method:
- `sntp_get_sync_status()` and `sntp_set_sync_status()` - get/set time synchronization status.
- `sntp_get_sync_mode()` and `sntp_set_sync_mode()` - get/set the sync mode. Allowable two mode: `SNTP_SYNC_MODE_IMMED` and `SNTP_SYNC_MODE_SMOOTH`.
## Events
The esp-netif SNTP wrapper emits an event when the system time is synchronized:
- Event base: `NETIF_SNTP_EVENT`
- Event ID: `NETIF_SNTP_TIME_SYNC`
Register a handler after creating the default event loop (the event data is a pointer to `esp_netif_sntp_time_sync_t` containing the synchronized `timeval`). You can convert the timeval to human-readable local time or UTC if desired using `localtime_r()`/`gmtime_r()`.
## Mode of update time
`sntp_set_sync_mode()` - Set the sync mode of the system time. It has two mode:
@@ -34,6 +34,22 @@ RTC_DATA_ATTR static int boot_count = 0;
static void obtain_time(void);
static void sntp_event_handler(void *arg, esp_event_base_t base, int32_t id, void *data)
{
(void)arg; (void)base; (void)id;
const esp_netif_sntp_time_sync_t *evt = (const esp_netif_sntp_time_sync_t *)data;
if (evt) {
char ts[64];
time_t t = evt->tv.tv_sec;
struct tm tm_utc;
gmtime_r(&t, &tm_utc);
strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S", &tm_utc);
ESP_LOGI(TAG, "SNTP event: time synced (UTC): %s.%06ld", ts, (long)evt->tv.tv_usec);
} else {
ESP_LOGI(TAG, "SNTP event: time synced (no timeval provided)");
}
}
#ifdef CONFIG_SNTP_TIME_SYNC_METHOD_CUSTOM
void sntp_sync_time(struct timeval *tv)
{
@@ -140,6 +156,8 @@ 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() );
// Register handler to log SNTP event with timeval payload
ESP_ERROR_CHECK(esp_event_handler_register(NETIF_SNTP_EVENT, NETIF_SNTP_TIME_SYNC, &sntp_event_handler, NULL));
#if LWIP_DHCP_GET_NTP_SRV
/**
@@ -223,4 +241,5 @@ static void obtain_time(void)
ESP_ERROR_CHECK( example_disconnect() );
esp_netif_sntp_deinit();
ESP_ERROR_CHECK(esp_event_handler_unregister(NETIF_SNTP_EVENT, NETIF_SNTP_TIME_SYNC, &sntp_event_handler));
}