ESP32通过HTTP及SNTP同步网络时间

news2024/9/29 17:22:01

1、获取毫秒级时间 和普通系统函数相同

int get_sys_time_ms(void)
{
    struct timeval tv_now;
    gettimeofday(&tv_now, NULL);
    int64_t time_us = (int64_t)tv_now.tv_sec * 1000000L + (int64_t)tv_now.tv_usec;
    return (int)(time_us/1000);
}

2、延时毫秒级时间

void my_delay_ms(u32 time_ms)
{
    vTaskDelay(time_ms / portTICK_RATE_MS);
}

SNTP校时

SNTP 指 简单网络时间协议(Simple Network Time Protocol),一个合格的物联网设备,少不了一个准确的钟。通过SNTP,可以使ESP32设备通过网络校准本地时间。使用起来也非常简单!

二、示例

1、场景一:最基础方式

最简单+基础的方式

sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, "ntp.aliyun.com");
sntp_init();

但此时,你的ESP32要已联网,否则肯定是获取不了的。

接下来,你可以通过sntp_get_sync_status()轮询检测同步是否完毕,官方示例提供了这样的代码。除此之外。我们也可以通过回调来完成。更新成功之后,我们就随时可以获取系统时间了:

首先设置时区方法(这只影响下文时间转换,不影响时间同步。如果你有自己的方法,则可以通过自己的方式设置):

设置时区---时区缩写:

标准时间代码

与GMT的偏移量

描述

NZDT

+13:00

新西兰夏令时

IDLE

+12:00

国际日期变更线,东边

NZST

+12:00

新西兰标准时间

NZT

+12:00

新西兰时间

AESST

+11:00

澳大利亚东部夏时制

CST(ACSST)

+10:30

中澳大利亚标准时间

CADT

+10:30

中澳大利亚夏时制

SADT

+10:30

南澳大利亚夏时制

EST(EAST)

+10:00

东澳大利亚标准时间

GST

+10:00

关岛标准时间

LIGT

+10:00

澳大利亚墨尔本时间

CAST

+9:30

中澳大利亚标准时间

SAT(SAST)

+9:30

南澳大利亚标准时间

WDT(AWSST)

+9:00

澳大利亚西部标准夏令时

JST

+9:00

日本标准时间,(USSR Zone 8)

KST

+9:00

韩国标准时间

MT

+8:30

毛里求斯时间

WST(AWST)

+8:00

澳大利亚西部标准时间

CCT

+8:00

中国沿海时间(北京时间)

JT

+7:30

爪哇时间

IT

+3:30

伊朗时间

BT

+3:00

巴格达时间

EETDST

+3:00

东欧夏时制

CETDST

+2:00

中欧夏时制

EET

+2:00

东欧,(USSR Zone 1)

FWT

+2:00

法国冬时制

IST

+2:00

以色列标准时间

MEST

+2:00

中欧夏时制

METDST

+2:00

中欧白昼时间

SST

+2:00

瑞典夏时制

BST

+1:00

英国夏时制

CET

+1:00

中欧时间

DNT

+1:00

Dansk Normal Tid

FST

+1:00

法国夏时制

MET

+1:00

中欧时间

MEWT

+1:00

中欧冬时制

MEZ

+1:00

中欧时区

NOR

+1:00

挪威标准时间

SET

+1:00

Seychelles Time

SWT

+1:00

瑞典冬时制

WETDST

+1:00

西欧光照利用时间(夏时制)

GMT

0:00

格林威治标准时间

WET

0:00

西欧

WAT

-1:00

西非时间

NDT

-2:30

纽芬兰(新大陆)白昼时间

ADT

-03:00

大西洋白昼时间

NFT

-3:30

纽芬兰(新大陆)标准时间

NST

-3:30

纽芬兰(新大陆)标准时间

AST

-4:00

大西洋标准时间(加拿大)

EDT

-4:00

(美国)东部夏令时

CDT

-5:00

(美国)中部夏令时

EST

-5:00

(美国)东部标准时间

CST

-6:00

(美国)中部标准时间

MDT

-6:00

(美国)山地夏令时

MST

-7:00

(美国)山地标准时间

PDT

-7:00

(美国)太平洋夏令时

PST

-8:00

(美国)太平洋标准时间

YDT

-8:00

Yukon夏令时

HDT

-9:00

夏威仪/阿拉斯加白昼时间

YST

-9:00

Yukon标准时

AHST

-10:00

夏威仪-阿拉斯加标准时间

CAT

-10:00

中阿拉斯加时间

NT

-11:00

州时间(Nome Time)

IDLW

-12:00

国际日期变更线,西边

//如果目标时区是在东区,则是负的,否则是正的。 setenv("TZ", "CST-8", 1); //东八区 tzset();

获取时间

// 获取系统时间戳
time_t now = 0;
time(&now);

// 结合设置的时区,转换为tm结构体
struct tm timeinfo = {0};
localtime_r(&now, &timeinfo);

// 转为字符串(方法随意,不一定要用strftime)
char str[64];
strftime(str, sizeof(str), "%c", &timeinfo);

2、场景二:使用回调

当sntp成功同步时间后,会有一个异步的回调通知应用做出相应更改,例如:在LVGL等UI框架上弹窗等。只需要下边的函数

sntp_set_time_sync_notification_cb(/* 需要的函数指针 /);
//需要的函数声明为形如:
void 函数名(struct timeval tv);

例如

static void initialize_sntp_cb(void)
{
    ESP_LOGI(TAG, "Initializing SNTP");
    sntp_setoperatingmode(SNTP_OPMODE_POLL);
    sntp_setservername(0, "cn.ntp.org.cn");     // 设置访问服务器
    sntp_setservername(1, "pool.ntp.org");
    sntp_setservername(2, "210.72.145.44");     // 国家授时中心服务器 IP 地址
    //开启一个 SNTP server(节省资源考虑), 如果用户需开启多个 SNTP server, 请配置menuconfig
    sntp_set_time_sync_notification_cb(time_sync_notification_cb);
    //设置时间同步模式
    sntp_set_sync_mode(SNTP_SYNC_MODE_SMOOTH);

    sntp_init();
} 
void time_sync_notification_cb(struct timeval *tv)
{
    time_t now = 0;
    struct tm timeinfo = {0};
    
    time(&now);
    localtime_r(&now, &timeinfo);

    char strftime_buf[64];

    setenv("TZ", "UTC-0", 1);
    tzset();
    localtime_r(&now, &timeinfo);
    strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
    ESP_LOGI(TAG,  "The time in time_zone:%s\r\n", strftime_buf);
    SntpFinishState = true;
    printf("===============set sntp config successfully==================\r\n");
}
//应用层只需调用
initialize_sntp_cb();

3、场景三:主动立刻触发时间同步

调用sntp_init()会立刻请求服务器同步一次时间。

因此,我们需要主动同步时:

先调用sntp_stop()、再调用sntp_init() 即可立刻同步一次时间。

经过测试:一定要先stop!不然不会发起同步

实例

static void esp_initialize_sntp(void)
{
    sntp_setoperatingmode(SNTP_OPMODE_POLL);    // 设置单播模式
    sntp_setservername(0, "cn.ntp.org.cn");     // 设置访问服务器
    sntp_setservername(1, "ntp1.aliyun.com");
    sntp_setservername(1, "pool.ntp.org");
    sntp_setservername(2, "210.72.145.44");     // 国家授时中心服务器 IP 地址
    setenv("TZ", "CST-8", 1);                   //东八区
    tzset();                                    // 更新本地C库时间
    sntp_init();                                //初始化
}


void sntp_task(void* args)
{
    esp_initialize_sntp();
    // 延时等待SNTP初始化完成
    do {
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        BLUFI_INFO("wait for wifi sntp sync time---------------------\n");
    } while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET);

    // 成功获取网络时间后停止NTP请求,不然设备重启后会造成获取网络时间失败的现象
    // 大概是服务器时根据心跳时间来删除客户端的,如果不是stop结束的客户端,下次连接服务器时就会出错
    sntp_stop();
    vTaskDelete(NULL);
}
void start_sntp(void)
{
    BLUFI_INFO("Start for sync standard time");
    xTaskCreate(&sntp_task, "sntp_task", 2048, NULL, 5, NULL);
}
struct tm* get_time(void)
{
    time_t now;
    time(&now);                         // 获取网络时间, 64bit的秒计数 
    
    localtime_r(&now, &timeinfo);       // 转换成具体的时间参数
    ESP_LOGI(TAG, "%4d-%02d-%02d %02d:%02d:%02d week:%d", timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, 
        timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, timeinfo.tm_wday); 
    return &timeinfo;
}

官网:pool.ntp.org: the internet cluster of ntp servers

pool.ntp.org 是一个以时间服务器的大虚拟集群为上百万的客户端提供可靠的 易用的 网络时间协议(NTP)服务的项目。

NTP池正在为世界各地成百上千万的系统提供服务。 它是绝大多数主流Linux发行版和许多网络设备的默认“时间服务器”

设置多个 SNTP server需要配置:国内可用的NTP 服务器地址 服务器介绍 NTP池的介绍

参考地址

//c标准库版本

#include <stdint.h>
#include <stdio.h>
#ifdef _WIN32
    #include <time.h>
    #include<windows.h>
#else
    #include <sys/time.h>
    #include <unistd.h>
#endif

uint64_t GetCurrentTimerMS(char* szTimer=NULL)
{
    uint64_t nTimer = 0;
#ifdef _WIN32
    SYSTEMTIME currentTime;
    GetLocalTime(&currentTime);
    tm temptm = { currentTime.wSecond,
        currentTime.wMinute,
        currentTime.wHour,
        currentTime.wDay,
        currentTime.wMonth - 1,
        currentTime.wYear - 1900
    };
    nTimer =  mktime(&temptm) * 1000 + currentTime.wMilliseconds;
#else
    struct timeval tv;
    gettimeofday(&tv,NULL);
    // printf("second:%ld\n",tv.tv_sec);  //秒
    nTimer = tv.tv_sec*1000 + tv.tv_usec/1000;
#endif
    if(szTimer != NULL)
        sprintf(szTimer, "%llu", nTimer);
    return nTimer;
}

int main()
{
    char szTimer[64];
    uint64_t nTimer=-1;
    GetCurrentTimerMS(szTimer);    //带参数
    nTimer = GetCurrentTimerMS(); //不带参数
    printf("millisecond:%s,\t%llu\n\n",szTimer,nTimer );  //毫秒
    return 0;
}

通过HTTP获取时间并更新


uint32_t get_net_time(void)
{
    esp_err_t ret = ESP_FAIL;
    int i = 0;
    net_time = 0;
    //获取网络时间并打印 配置网页域名和对应的参数
    esp_http_client_config_t config = {
        .url = "http://worldtimeapi.org/api/ip",
        .event_handler = http_event_handle,
        .buffer_size = NET_TIME_BUFF_LEN,
    };
    esp_http_client_handle_t time_client = esp_http_client_init(&config);//初始化http
    for(i=0;i<3;i++)
    {
        ret = esp_http_client_perform(time_client);//请求获取数据
        if (ret == ESP_OK) 
        {
            BLUFI_INFO("HTTP GET Status = %d, content_length = %lld",
                       esp_http_client_get_status_code(time_client),
                       esp_http_client_get_content_length(time_client));
            if (get_net_time_flag == 1) // 得到了平台下发
            {
                if (ESP_OK == wifi_analyse_time()) // 解析平台时间并存到对应变量中
                {
                    break;
                }
            }
        } 
        else 
        {
            BLUFI_ERROR("HTTP GET request failed: %s", esp_err_to_name(ret));
        }
        vTaskDelay(500 / portTICK_PERIOD_MS); // 延时约1000ms
    }
    esp_http_client_cleanup(time_client);
    BLUFI_ERROR("time: %lu", net_time);
    return net_time;
}


// 功能:http处理中断
static esp_err_t http_event_handle(esp_http_client_event_t *evt)
{
    switch (evt->event_id) {
    case HTTP_EVENT_ERROR:
        BLUFI_ERROR("HTTP_EVENT_ERROR");
        break;
    case HTTP_EVENT_ON_CONNECTED:
        BLUFI_INFO("HTTP_EVENT_ON_CONNECTED");
        break;
    case HTTP_EVENT_HEADER_SENT:
        BLUFI_INFO("HTTP_EVENT_HEADER_SENT");
        break;
    case HTTP_EVENT_ON_HEADER:
        // BLUFI_INFO(TAG, "HTTP_EVENT_ON_HEADER");
        break;
    case HTTP_EVENT_ON_DATA: //收到网址下发的时间数据将数据存放到buffer之后处理
        BLUFI_INFO("HTTP_EVENT_ON_DATA");
        get_net_time_flag = 1;
        memset(net_time_buf, 0, NET_TIME_BUFF_LEN);
        memcpy(net_time_buf, evt->data, evt->data_len);
        // BLUFI_INFO("len=(%d),data=(%s)",evt->data_len,evt->data);
        break;
    case HTTP_EVENT_ON_FINISH:
        BLUFI_INFO("HTTP_EVENT_ON_FINISH");
        break;
    case HTTP_EVENT_DISCONNECTED:
        BLUFI_INFO("HTTP_EVENT_DISCONNECTED");
        break;
    case HTTP_EVENT_REDIRECT:
        break;
    default:
        break;
    }
    return ESP_OK;
}

void update_net_time(const uint32_t time)
{
    net_time = time;
}

// 功能:解析网络时间
static esp_err_t wifi_analyse_time(void)
{
    cJSON *json_buff;
    cJSON *unixtime;
    cJSON *raw_offset;//东时区
    cJSON *dst_offset;//西时区
    // clr_link_create_certs();
    get_net_time_flag = 0;
    json_buff = cJSON_Parse(net_time_buf);//转化为json体
    if(json_buff == NULL)
    {
        BLUFI_INFO("time buffer not find\n");
        cJSON_Delete(json_buff);//释放json体
        return ESP_FAIL;
    }
    else
    {
        unixtime = cJSON_GetObjectItem(json_buff, "unixtime");//查找国际网络时间
        raw_offset = cJSON_GetObjectItem(json_buff, "raw_offset");//获取时区偏移量 可以采用电脑访问上述网址看网址得到的格式 postman模拟
        dst_offset = cJSON_GetObjectItem(json_buff, "dst_offset");//获取时区偏移量
        if((unixtime == NULL)||(raw_offset == NULL)||(dst_offset == NULL)) //没有对应字段
        {
            cJSON_Delete(json_buff);//释放json体
            BLUFI_INFO("unixtime not find\n");
            return ESP_FAIL;
        }
        else
        {
            update_net_time(unixtime->valueint); //将网络时间更新到本地存放网络时间变量里
            cJSON_Delete(json_buff);//释放json体
            return ESP_OK;
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/398544.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据分析师求职面试指南】必备编程技能整理之Hive SQL必备用法

文章目录熟悉Python懂R语言掌握SQL大数据基础数据库常用类型多表查询更多聚合函数distinctcase when窗口函数动态更新一行变多行调优内容整理自《拿下offer 数据分析师求职面试指南》—徐粼著 第四章编程技能考查熟悉Python 懂R语言 掌握SQL 大数据基础 Hive时Hadoop的一个…

C++基础了解-10-C++ 判断

C 判断 一、C 判断 判断结构要求程序员指定一个或多个要评估或测试的条件&#xff0c;以及条件为真时要执行的语句&#xff08;必需的&#xff09;和条件为假时要执行的语句&#xff08;可选的&#xff09;。 下面是大多数编程语言中典型的判断结构的一般形式&#xff1a; …

电商 SaaS 全渠道实时数据中台最佳实践

摘要&#xff1a;本文整理自聚水潭数据专家张成玉&#xff0c;聚水潭高级数据工程师应圣楚&#xff0c;在 FFA 2022 行业案例专场的分享。本篇内容主要分为四个部分&#xff1a;实时数仓的建设和发展数据中台的产品体系及架构实时计算的实践和优化对实时计算的未来展望Tips&…

2019年MathorCup数学建模B题环形穿梭车系统的设计与调度解题全过程文档及程序

2019年第九届MathorCup高校数学建模挑战赛 B题 环形穿梭车系统的设计与调度 原题再现&#xff1a; 整体求解过程概述(摘要) 环形穿梭车系统为集多种高新技术于一体的自动搬运设备&#xff0c;行驶和输送速度快、灵活性好、自动化程度高。但由于系统采用封闭式轨道&#xff0c…

成为AI架构师的三大能力

AI架构师的定义 “AI 架构师”是以深度学习为代表的第三次AI热潮所催生的新型复合型人才&#xff0c;它的产生最本质的驱动因素是AI产业化落地应用的蓬勃发展对人才的需求&#xff0c;深度学习突出的工程属性也特别需要复合型人才来驾驭。 从字面来看&#xff0c;AI架构师的“…

Pytorch深度学习实战3-8:详解数据可视化组件TensorBoard安装与使用

目录1 什么是Tensorboard&#xff1f;2 Tensorboard安装3 Tensorboard可视化流程4 Tensorboard可视化实例4.1 常量可视化4.2 特征图可视化1 什么是Tensorboard&#xff1f; 在深度学习领域&#xff0c;网络内部如同黑箱&#xff0c;其中包含大量的连接参数&#xff0c;这给人工…

续航乱标销量低迷! 零跑汽车短时“掉”电快 ?

【锋巢网】 进入3月&#xff0c;行业复苏的景象映入眼帘&#xff0c;但是新能源车企却有人欢喜有人愁。 近日&#xff0c;各大新能源车企公布了自家2月份的销量数据&#xff0c;整体来看&#xff0c;部分新能源车企在2月份的交付量战绩显著&#xff0c;涨幅颇高。其中&#x…

class01:VUE简介与实例挂载

目录一、VUE简介1. 介绍2. 学习内容3. 引入Vue4. 全局配置5. Vue Devtools安装二、挂载Vue实例一、VUE简介 1. 介绍 Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是&#xff0c;Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0c;不…

九、CSS3新特性三

文章目录一、逐帧动画二、flex弹性盒子三、少量元素侧轴对齐方式四、折行侧轴对齐方式五、项目属性六、网格布局七、网格布局的对齐方式八、网格布局的项目合并一、逐帧动画 一张背景图&#xff0c;改变back-position-x的位置让他动起来 step-start 逐帧动画 animation: play …

宝塔webhook自动化打包vue项目时,npm不生效问题

文章目录&#x1f4cb;前言&#x1f3af;查看webhook配置的代码&#x1f3af;测试代码&#xff0c;检查输出内容&#x1f3af;解决方法&#x1f4cb;前言 这篇文章主要是记录和解决在宝塔面板中&#xff0c;webhook自动化打包vue项目时&#xff0c;npm不生效问题。说来奇怪&am…

【DBC专题】-10-CAN DBC转换C语言代码Demo_接收Rx报文篇

案例背景(共15页精讲)&#xff1a; 该篇博文将告诉您&#xff0c;CAN DBC转换C语言代码Demo&#xff0c;只需传递对应CAN信号关联参数&#xff0c;无需每个信号"左移"和"右移"&#xff0c;并举例介绍&#xff1a;在CANoe/Canalyzer中CAPL中的应用&#xff…

【MIT 6.S081】Lab1: Xv6 and Unix utilities

Util概述sleeppingpongprimesfindxargs本Lab包括五个应用程序的实现&#xff0c;初步熟悉系统调用接口。用时约8h&#xff08;我太菜辣&#xff09;本Lab包括五个简单程序的实现&#xff0c;初步熟悉系统调用接口。 笔者用时约6h&#xff08;我太菜辣&#xff09; 概述 根据文…

mysql数据库之全局锁

锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中&#xff0c;除传统的计算资源&#xff08;CPU、RAM、I/O&#xff09;的争用以外&#xff0c;数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题&#x…

【Day2】Numpy简单入门基础

NumPy 简单入门基础 我的另一篇文章 &#xff1a; Numpy介绍-深度学习&#xff1a;Numpy介绍-深度学习&#xff08;Numpy介绍深度学习使用看这些足够了&#xff09; import numpy as npmy_array np.array([1, 2, 3, 4, 5]) print(my_array)[1 2 3 4 5]print(my_array.shape)…

Kafka 多线程消费者

Kafka 多线程消费者多线程方案Kafka 0.10.1.0 后&#xff0c;Kafka Consumer 变为双线程的设计 : 用户主线程 : 启动 Consumer 的 main心跳线程 (Heartbeat Thread) : 定期对 Broker 发送心跳请求&#xff0c;探测消费者的存活性 (liveness&#xff09;将心跳频率与主线程处理…

MQTT协议-取消订阅和取消订阅确认

MQTT协议-取消订阅和取消订阅确认 客户端向服务器取消订阅 取消订阅的前提是客户端已经通过CONNECT报文连接上服务器&#xff0c;并且订阅了一个主题 UNSUBSCRIBE—取消订阅 取消订阅的报文同样是由固定报头可变报头有效载荷组成 固定报头由两个字节组成&#xff0c;第一个…

2023年,当我们谈论架构时,我们要聊什么

架构是一个非常宽泛的话题&#xff0c;从组织结构上来说&#xff0c;涉及到前端、后端、运维&#xff1b;从软件设计上来说&#xff0c;涉及到需求分析、设计、编码、测试&#xff1b;从物理结构上来说&#xff0c;涉及到CDN、负载均衡、网关、服务器、数据库。当前一些架构方面…

奇淫技巧:阅读源码时基于一组快捷键,让我们知道身在何方!

一个十分蛋疼的问题 在我们阅读框架底层源码的时候&#xff0c;我们往往会一个方法一个方法的往下翻&#xff0c;翻了很久很快就会有这样的灵魂拷问&#xff1a;我从那个类&#xff08;方法&#xff09;来&#xff0c;我要到哪个&#xff08;类&#xff09;方法中去。这个时候…

RK3568平台开发系列讲解(显示篇) DRM显示系统组成分析

🚀返回专栏总目录 文章目录 一、DRM Framebuffer二、CRTC三、Planes四、Encoder五、Connector沉淀、分享、成长,让自己和他人都能有所收获!😄 📢让我们分析一下绿框中的五个部件,以及他们的联动。 一、DRM Framebuffer 与 framebuffer一样,是一片存放图像的内存区域,…

敏捷开发还需要PRD吗

一、PRD有什么用 prd提升与RD或者未来接手人的沟通效率 二、为什么会有PRD 首先来说说为什么会有PRD文档。 1、稍微大一点的团队产品经理未必能向每个人传达产品需求&#xff0c;这就需要有一个文档的形式来向项目的所有成员来传达需求&#xff0c;这就是文档的来源。 2、由…