ESP32学习笔记(41)——SNTP接口使用

Posted Leung_ManWah

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ESP32学习笔记(41)——SNTP接口使用相关的知识,希望对你有一定的参考价值。

一、SNTP简介

简单网络时间协议(Simple Network Time Protocol),由 NTP 改编而来,主要用来同步因特网中的计算机时钟。

SNTP 协议是用来同步本地的时间到 unix 时间戳。通常嵌入式设备上电,连接 AP(access point),获取 IP 地址后,就需要使用 SNTP 协议获取全球时间。以便于下一步的应用交互和使用。
SNTP 工作原理比较简单, 通俗来说,就是设备向 SNTP server 发送一包 SNTP 请求,服务器收到请求后回复一包 SNTP reply。其中 SNTP reply 中就含有 unix 时间戳。

ESP-IDF 编程指南——SNTP 时间同步

二、API说明

以下 SNTP 接口位于 lwip/include/apps/esp_sntp.h

2.1 sntp_setoperatingmode

2.2 sntp_setservername

2.3 sntp_set_time_sync_notification_cb

2.4 sntp_init

2.5 sntp_get_sync_status

三、示例代码

根据 examples\\protocols\\sntp 中的例程修改

在 menuconfig 中配置 SSID 和密码

核心部分:

//设置单播模式
sntp_setoperatingmode(SNTP_OPMODE_POLL);
//设置访问服务器
sntp_setservername(0, "pool.ntp.org");
//初始化SNTP模块
sntp_init();

完整代码:

/* 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";

static void obtain_time(void);
static void initialize_sntp(void);

void time_sync_notification_cb(struct timeval *tv)
{
    ESP_LOGI(TAG, "Notification of a time synchronization event");
}

void app_main(void)
{
    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 < (2021 - 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);
    }

    char strftime_buf[64];

    while (1)
    {
        // update 'now' variable with current time
        time(&now);

        // 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);

        vTaskDelay(10000 / portTICK_PERIOD_MS);
    }
}

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);
    sntp_init();
}

查看打印:

四、注意事项

  1. sntp_setservername 除了可以设置域名, 也可以设置 IP 地址, 例如 sntp_setservername(0, "120.25.115.20");
  2. 如果有必要, 请多设置几个 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

配置多个 SNTP server 时, 不是同时发送多个 SNTP 请求报文, 而是轮循方式. 第一个处理超时后, 进行和第二个 SNTP server 交互, 这样依次进行到最后一个, 最后一个处理超时后, 会再和第一个 SNTP server 交互

  1. 更新时间请求间隔SNTP_UPDATE_DELAY,到下一次发起更新时间请求的间隔时间,单位是ms,最小不能小于15s,也是通过make menuconfig修改CONFIG_LWIP_SNTP_UPDATE_DELAY
    在 make menuconfig -> Component config -> LWIP -> SNTP -> Request interval to update time (ms).

  2. 最好不在任何 callback 或中断处理函数中调用 obtain_time(), callback/中断中, 调用任何阻塞的 API, 理论上都有死锁的可能.

  3. 任何有校验服务器证书的 TLS 过程 (本地有 CA 证书), 请务必开启 SNTP 功能, 否则会因为校验服务器证书有效期失败而导致 TLS 握手失败

  4. NTP.ORG.cn中国NTP授时快速域名服务提供商

新加坡sgp.ntp.org.cn韩国kr.ntp.org.cn
中国cn.ntp.org.cn中国教育网edu.ntp.org.cn
中国香港hk.ntp.org.cn中国台湾tw.ntp.org.cn
美国us.ntp.org.cn韩国kr.ntp.org.cn
日本jp.ntp.org.cn德国de.ntp.org.cn
印度尼西亚ina.ntp.org.cn
  1. 时区:
  • CST-8:这时区的表示有点混
    CST却同时可以代表如下 4 个不同的时区:
    Central Standard Time (USA) UT-6:00
    Central Standard Time (Australia) UT+9:30
    China Standard Time UT+8:00
    Cuba Standard Time UT-4:00
setenv("TZ", "CST-8", 1);
  • GMT:(Greenwich Mean Time)是格林尼治平时
    GMT+8正好是中国的标准时区
setenv("TZ", "GMT+8", 1);
  1. 在成功获取了网络时间后,必须调用 sntp_stop(); 停止NTP请求,不然设备重启后会造成获取网络时间失败的现象,大概是服务器时根据心跳时间来删除客户端的,如果不是stop结束的客户端,下次连接服务器时就会出错

• 由 Leung 写于 2021 年 7 月 30日

• 参考:ESP8266/ESP32 基础篇: 时间同步 SNTP 介绍和使用
    ESP32 SNTP配置
    ESP32的SDK开发之获取SNTP网络时间

以上是关于ESP32学习笔记(41)——SNTP接口使用的主要内容,如果未能解决你的问题,请参考以下文章

ESP32入门基础之SNTP时间显示

ESP32学习笔记(25)——OTA(空中升级)接口使用(简化API)

ESP32学习笔记(45)——DAC接口使用

ESP32学习笔记(36)——BluFi(蓝牙配网)接口使用

ESP32学习笔记(49)——ESP-WIFI-MESH接口使用

ESP32学习笔记(49)——ESP-WIFI-MESH接口使用