ESP32通过HTTP及SNTP同步网络时间 | 您所在的位置:网站首页 › localtime_r函数的用法 › ESP32通过HTTP及SNTP同步网络时间 |
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 #include #ifdef _WIN32 #include #include #else #include #include #endif uint64_t GetCurrentTimerMS(char* szTimer=NULL) { uint64_t nTimer = 0; #ifdef _WIN32 SYSTEMTIME currentTime; GetLocalTime(¤tTime); 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;ievent_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; } } } |
CopyRight 2018-2019 实验室设备网 版权所有 |