Skip to content

NTP 的使用方法

1. NTP与RTC的关系

NTP(Network Time Protocol)和RTC(Real-Time Clock)是两个不同的概念。

RTC 功能定位
RTC 是一个独立的硬件模块,一般配备备用电源(如纽扣电池)。当主系统断电,RTC 依靠备用电源持续工作,持续对时间进行计数。等系统再次上电,就可以从 RTC 读取保存的时间,并将其更新到系统时间里,保证系统时间的连续性和准确性。比如在一些嵌入式设备、智能手表等产品中,即便设备长时间关机,再次开机时仍能显示准确时间,这就是 RTC 在发挥作用。

2. NTP与SNTP

NTP(Network Time Protocol)是一种用于网络时间同步的协议,它允许计算机通过网络获取准确的时间。NTP使用UDP协议进行通信,通常使用端口号123。NTP协议的工作原理是通过发送请求消息到NTP服务器,然后接收服务器返回的响应消息,从而获取准确的时间。 SNTP(Simple Network Time Protocol)是NTP的简化版本,它是NTP协议的一个子集,只包含了NTP协议的基本功能。SNTP协议的工作原理与NTP协议相同,只是在网络传输过程中使用了TCP协议进行通信。SNTP协议通常使用端口号123。 ESP - IDF 主要用于开发基于 ESP 系列芯片的嵌入式系统,这些芯片通常资源有限。SNTP 由于其简单性和低资源消耗的特点,非常适合在这类设备上运行。在大多数物联网应用场景中,几十毫秒到几百毫秒的时间同步精度已经足够满足需求,因此 ESP - IDF 选择了 SNTP 作为时间同步协议的实现。

3. NTP 的使用方法

在 ESP - IDF 中使用 NTP 进行时间同步非常简单,只需要使用 ESP - IDF 提供的库函数即可。以下是使用 NTP 进行时间同步的步骤:

  1. 引入 ESP - IDF 的库文件:
    #include "esp_netif_sntp.h"
    与时间相关的头文件
    #include "time.h"
    #include "sys/time.h"
  2. 保证网络连接正常,可以使用 Wi-Fi 或以太网连接。ESP_ERROR_CHECK(example_connect());
  3. 设置 ntp 配置参数:
    esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG('ntp1.aliyun.com');
  4. 设置回调函数:
    config.sync_cb = time_sync_notification_cb;
  5. 调用
    esp_netif_sntp_init(&config);
    初始化 NTP。
  6. 设置时区:
    setenv("TZ", "CST-8", 1); tzset();
  7. 调用
    esp_netif_sntp_sync_wait(2000 / portTICK_PERIOD_MS)
    等待 NTP 同步完成。
  8. 调用
    time(&now); localtime_r(&now, &timeinfo);
    获取当前时间。

特别注意
在进行同步之前,也就是在调用esp_netif_sntp_sync_wait(2000 / portTICK_PERIOD_MS)之前,一定要先设置时区,否则获取到的时间是UTC时间。

c
    setenv("TZ", "CST-8", 1);
    tzset();
    esp_netif_sntp_sync_wait(2000 / portTICK_PERIOD_MS);

4. 与时区的相关问题

在ESP-IDF中,如果需要设置时区,可以使用setenv("TZ", "UTC-8")来设置时区。如果没有设置时区,ESP-IDF是没有时区概念的。

1. 设置时区的时间

只要在设置时间时,指定了所在的时区,那么设置的时间,就是当前时区时间。

c

    setenv("TZ", "EST5EDT,M3.2.0/2,M11.1.0", 1);
    tzset();

    // 设置本地时区的时间,假如现在的时间是 2023-09-10 10:30:17
    struct tm time_to_set = {
        .tm_sec = 17,
        .tm_min = 30,
        .tm_hour = 10,
        .tm_mday = 10,
        .tm_mon = 8,  // 月份从 0 开始计数,所以 9 月是 8
        .tm_year = 2023 - 1900,  // 年份从 1900 开始计数
        .tm_isdst = -1  // 自动判断是否为夏令时
    };

    time_t timestamp = mktime(&time_to_set);
    struct timeval tv = {
        .tv_sec = (long)timestamp, // 秒数
        .tv_usec = 0         // 微秒数
    };

    settimeofday(&tv, NULL);

mktime函数会根据当前的时区,将时间转换为UTC时间。

2. 获取时区的时间

获取时区的时间,需要先获取UTC时间,然后再转换为当前时区的时间。

c
    time_t now;
    struct tm timeinfo;
    time(&now);
    localtime_r(&now, &timeinfo);
    printf("Current time: %02d:%02d:%02d\n", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);