ESP8266/ESP32 基礎篇: 時間同步 SNTP 介紹和使用

簡介

SNTP 協議是用來同步本地的時間到 unix 時間戳. 通常嵌入式設備上電, 連接 AP(access point), 獲取 IP 地址後, 就需要使用 SNTP 協議獲取全球時間. 以便於下一步的應用交互和使用.

SNTP 工作原理比較簡單, 通俗來說, 就是設備向 SNTP server 發送一包 SNTP 請求, 服務器收到請求後回覆一包 SNTP reply. 其中 SNTP reply 中就含有 unix 時間戳.

參考代碼

#include <stdio.h>
#include <time.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "lwip/apps/sntp.h"
#include "esp_log.h"

static const char *TAG = "sntp";

static void esp_initialize_sntp(void)
{
    ESP_LOGI(TAG, "Initializing SNTP");
    sntp_setoperatingmode(SNTP_OPMODE_POLL);
    sntp_setservername(0, "ntp1.aliyun.com");
    sntp_init();
}

void esp_wait_sntp_sync(void)
{
    char strftime_buf[64];
    esp_initialize_sntp();

    // wait for time to be set
    time_t now = 0;
    struct tm timeinfo = { 0 };
    int retry = 0;

    while (timeinfo.tm_year < (2019 - 1900)) {
        ESP_LOGD(TAG, "Waiting for system time to be set... (%d)", ++retry);
        vTaskDelay(100 / portTICK_PERIOD_MS);
        time(&now);
        localtime_r(&now, &timeinfo);
    }

    // set timezone to China Standard Time
    setenv("TZ", "CST-8", 1);
    tzset();

    strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
    ESP_LOGI(TAG, "The current date/time in Shanghai is: %s", strftime_buf);
}

// 應用層直接調用 esp_wait_sntp_sync() 即可.

使用說明

A). 代碼中 SNTP APIs 說明請參考 lwip 官方文檔.

B). sntp_setservername 除了可以設置域名, 也可以設置 IP 地址, 例如 sntp_setservername(0, "120.25.115.20");

C). 如果有必要, 請多設置幾個 SNTP server, 防止某個 SNTP server 暫時關閉服務而導致產品部分功能無法使用, 例如:

    sntp_setservername(0, "ntp1.aliyun.com");
    sntp_setservername(1, "210.72.145.44");		// 國家授時中心服務器 IP 地址
    sntp_setservername(2, "1.cn.pool.ntp.org");        

說明:
默認情況下, ESP8266/ESP32 只允許開啓一個 SNTP server(節省資源考慮), 如果用戶需開啓多個 SNTP server, 請配置:

  • ESP8266 請在 make menuconfig -> Component config -> LWIP -> DHCP -> Maximum bumber of NTP servers 修改爲 3
  • ESP32 請在 make menuconfig -> Component config -> LWIP -> SNTP -> Maximum bumber of NTP servers 修改爲 3

D). 配置多個 SNTP server 時, 不是同時發送多個 SNTP 請求報文, 而是輪循方式. 第一個處理超時後, 進行和第二個 SNTP server 交互, 這樣依次進行到最後一個, 最後一個處理超時後, 會再和第一個 SNTP server 交互.

E). 最好不在任何 callback 或中斷處理函數中調用 esp_wait_sntp_sync(), callback/中斷中, 調用任何阻塞的 API, 理論上都有死鎖的可能.

F). 任何有校驗服務器證書的 TLS 過程 (本地有 CA 證書), 請務必開啓 SNTP 功能, 否則會因爲校驗服務器證書有效期失敗而導致 TLS 握手失敗.

SNTP 調試

ESP8266 上, 可以在 make menuconfig -> Component config -> LWIP -> Enable lwip Debug -> Enable debugging for SNTP 開啓.
當然也可以把 UDP, 甚至 IP 層的 debug 打開.

SNTP 交互抓包展示

SNTP 請求報文:
在這裏插入圖片描述
SNTP 回覆報文:
在這裏插入圖片描述

SNTP 協議請參考 SNTP RFC 文檔.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章