前言
上篇学习了ESP32C3使用http来获取天气数据,本次将用http来获取网络时间。获取指定时区的网络时间,并转化为年月日:时分秒这类时间格式。
一、获取时间API
1.了解相关API
进入“世界时间API”了解该网站提供的时间服务(http://worldtimeapi.org)
阅读网站提供的请求示例
API请求格式的参数有几种,可以输入对应的城市,也可以输入时区等,可以通过该API“查看这个网站所支持的地区和时区参数”查看所支持的地区和时区参数(http://worldtimeapi.org/api/timezone)
我使用使用的API参数是时区合成的http请求:https://worldtimeapi.org/api/timezone/Etc/GMT-8,用电脑浏览器测试返回的数据格式为:
2.ESP32C3编程流程:
二、ESP32C3获取时间
1.连接可访问网络的热点AP:
在前面有博文有介绍怎么连接热点AP,链接在这里“连接手机热点AP博客”
2.创建http协议客户端:
提供时间服务的网站是http,不需要加密,所以只要创建http客户端就可以了。
1)头文件包含
2)相关宏定义
3)创建http客户端
创建http客户端任务函数:连接到“世界时间”的http服务器上,并通过GET请求获取天气信息,代码实现如下:
void http_get_time_task(void *pvParameters)
{
const struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
};
struct addrinfo *res;
struct in_addr *addr;
int s, r;
char recv_buf[64];
while(1) {
int err = getaddrinfo(WEB_SERVER, WEB_PORT, &hints, &res);
if(err != 0 || res == NULL) {
ESP_LOGE(TAG, "DNS lookup failed err=%d res=%p", err, res);
vTaskDelay(1000 / portTICK_PERIOD_MS);
continue;
}
/* Code to print the resolved IP.
Note: inet_ntoa is non-reentrant, look at ipaddr_ntoa_r for "real" code */
addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", inet_ntoa(*addr));
s = socket(res->ai_family, res->ai_socktype, 0);
if(s < 0) {
ESP_LOGE(TAG, "... Failed to allocate socket.");
freeaddrinfo(res);
vTaskDelay(1000 / portTICK_PERIOD_MS);
continue;
}
ESP_LOGI(TAG, "... allocated socket");
if(connect(s, res->ai_addr, res->ai_addrlen) != 0) {
ESP_LOGE(TAG, "... socket connect failed errno=%d", errno);
close(s);
freeaddrinfo(res);
vTaskDelay(4000 / portTICK_PERIOD_MS);
continue;
}
ESP_LOGI(TAG, "... connected");
freeaddrinfo(res);
if (write(s, REQUEST, strlen(REQUEST)) < 0) {
ESP_LOGE(TAG, "... socket send failed");
close(s);
vTaskDelay(4000 / portTICK_PERIOD_MS);
continue;
}
ESP_LOGI(TAG, "... socket send success");
struct timeval receiving_timeout;
receiving_timeout.tv_sec = 5;
receiving_timeout.tv_usec = 0;
if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &receiving_timeout,
sizeof(receiving_timeout)) < 0) {
ESP_LOGE(TAG, "... failed to set socket receiving timeout");
close(s);
vTaskDelay(4000 / portTICK_PERIOD_MS);
continue;
}
ESP_LOGI(TAG, "... set socket receiving timeout success");
/* Read HTTP response */
do {
bzero(recv_buf, sizeof(recv_buf));
r = read(s, recv_buf, sizeof(recv_buf)-1);
for(int i = 0; i < r; i++) {
putchar(recv_buf[i]);
}
} while(r > 0);
ESP_LOGI(TAG, "... done reading from socket. Last read return=%d errno=%d.", r, errno);
close(s);
for(int countdown = 10; countdown >= 0; countdown--) {
ESP_LOGI(TAG, "%d... ", countdown);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
ESP_LOGI(TAG, "Starting again!");
}
}
http协议的详细知识点在我“ESP32C3获取天气数据”的博文中有介绍,有关该任务函数在热点AP连接成功的事件返回中调用。
4)主函数
与“连接手机热点AP博文”的这篇博文一样:
void app_main(void)
{
/* 初始化NVS Flash */
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
/* 初始化基础TCP/IP堆 */
ESP_ERROR_CHECK(esp_netif_init());
/* 创建一个默认的事件循环(同一个esp32C3程序中可以有多个event_loop,这里使用默认的事件循环)*/
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_LOGI(TAG, "ESP_WIFI_STA");
wifi_init_sta();
}
我这里暂时就不做cjson解析了,后面会加上cjson解析,有需要的朋友可以自己解析一下。
3.调试现象
总结
本次主要实现的是如何使用ESP32C3来获取网络时间的功能,使用了ESP32C3的联网功能和http协议相关功能。