ESP32 IDF 获取天气信息
一、注册天气获取账号
我使用的知心天气,没有获取天气账号的小伙伴可以去注册一下,知心天气官网:https://www.seniverse.com/
取得天气获取的API后,可以直接在浏览器中访问测试一下,如下图所示:
这里我就不赘述了,稍微花点信息就可以明白天气是怎么获取的了。
二、天气信息
获取到的天气格式是JSON的数据,直接在浏览器中不好观察,所以我将它整理了一下,如下所示:
{ "results":[ { "location":{ "id":"WKEZD7MXE04F", "name":"贵阳", "country":"CN", "path":"贵阳,贵阳,贵州,中国", "timezone":"Asia/Shanghai", "timezone_offset":"+08:00" }, "daily":[ { "date":"2022-10-24", "text_day":"多云", "code_day":"4", "text_night":"多云", "code_night":"4", "high":"24", "low":"12", "rainfall":"0.00", "precip":"0.00", "wind_direction":"东南", "wind_direction_degree":"135", "wind_speed":"8.4", "wind_scale":"2", "humidity":"57" }, { "date":"2022-10-25", "text_day":"多云", "code_day":"4", "text_night":"多云", "code_night":"4", "high":"24", "low":"14", "rainfall":"0.00", "precip":"0.00", "wind_direction":"南", "wind_direction_degree":"180", "wind_speed":"8.4", "wind_scale":"2", "humidity":"62" }, { "date":"2022-10-26", "text_day":"阴", "code_day":"9", "text_night":"阵雨", "code_night":"10", "high":"24", "low":"13", "rainfall":"4.63", "precip":"0.94", "wind_direction":"南", "wind_direction_degree":"180", "wind_speed":"3.0", "wind_scale":"1", "humidity":"87" } ], "last_update":"2022-10-24T08:00:00+08:00" } ] }
其中有些格式可能看不知道什么意思,不要怕,看官方的注释,如下所示:
{ "results": [ { "location": { "id": "C23NB62W20TF", "name": "西雅图", "country": "US", "path": "西雅图,华盛顿州,美国", "timezone": "America/Los_Angeles", "timezone_offset": "-07:00" }, "now": { "text": "多云", //天气现象文字 "code": "4", //天气现象代码 "temperature": "14", //温度,单位为c摄氏度或f华氏度 "feels_like": "14", //体感温度,单位为c摄氏度或f华氏度 "pressure": "1018", //气压,单位为mb百帕或in英寸 "humidity": "76", //相对湿度,0~100,单位为百分比 "visibility": "16.09", //能见度,单位为km公里或mi英里 "wind_direction": "西北", //风向文字 "wind_direction_degree": "340", //风向角度,范围0~360,0为正北,90为正东,180为正南,270为正西 "wind_speed": "8.05", //风速,单位为km/h公里每小时或mph英里每小时 "wind_scale": "2", //风力等级,请参考:http://baike.baidu.com/view/465076.htm "clouds": "90", //云量,单位%,范围0~100,天空被云覆盖的百分比 #目前不支持中国城市# "dew_point": "-12" //露点温度,请参考:http://baike.baidu.com/view/118348.htm #目前不支持中国城市# }, "last_update": "2015-09-25T22:45:00-07:00" //数据更新时间(该城市的本地时间) } ] }
三、ESP32获取天气信息
这里我使用的是ESP-IDF环境,并且是通过 socket 的方式进行获取
-
socket 通信思路如下图所示:
-
创建socket连接
函数 int socket(int domain, int type, int protocol) 含义 函数socket()为通信创建一个端点,并为该套接字返回一个文件描述符。 返回值 int,若发生错误则返回-1 domain 表示需要创建的协议。
如:AF_INET表示IPv4,
AF_INET6表示IPv6,
AF_UNIX表示本地套接字type 创建时,选择需要的通行方式,如:
SOCK_STREAM表示TCP,
SOCK_DGRAM表示UDP,
SOCK_SEQPACKET表示可靠的顺序包服务,
SOCK_RAW表示网络层上的原始协议protocol 表示指定要使用的实际传输协议
最常见的有IPPROTO_TCP, IPPROTO_SCTP, IPPROTO_UDP, IPPROTO_DCCP等。
如果填0(IPPRORO_IP)则根据前两个参数自动选择协议/* 创建套接字 */ socket_handle = socket(dns_info->ai_family, dns_info->ai_socktype, 0); // 0(IPPROTO_IP)可以用来表示选择一个默认的协议。 if(socket_handle < 0) { ESP_LOGE(TAG, "... Failed to allocate socket"); close(socket_handle); freeaddrinfo(dns_info); false; } -
连接 connect
连接时需要用到服务器的信息,而获取天气信息是通过域名的方式获取的,在连接之前,我们需要使用getaddrinfo()函数进行DNS解析/* 域名解析 */ int err = getaddrinfo(WEB_SERVER, WEB_PORT, &hints, &dns_info); if(err != 0 || dns_info == NULL) { ESP_LOGE(TAG, "DNS lookup failed err=%d dns_info=%p", err, dns_info); return false; } /* 连接服务器 */ if(connect(socket_handle, dns_info->ai_addr, dns_info->ai_addrlen) != 0) { ESP_LOGE(TAG, "... socket connect failed errno=%d", errno); close(socket_handle); freeaddrinfo(dns_info); false; } -
通过写数据,发送get请求
/* 想缓冲区中写入服务请求信息 */ if (write(socket_handle, REQUEST, strlen(REQUEST)) < 0) { ESP_LOGE(TAG, "... socket send failed"); close(socket_handle); false; } -
设置请求超时
/* 设置请求超时 */ struct timeval receiving_timeout; receiving_timeout.tv_sec = 5; receiving_timeout.tv_usec = 0; if (setsockopt(socket_handle, SOL_SOCKET, SO_RCVTIMEO, &receiving_timeout, sizeof(receiving_timeout)) < 0) { ESP_LOGE(TAG, "... failed to set socket receiving timeout"); close(socket_handle); false; } -
通过读取数据,获取get响应数据
bzero(weather_buf, buf_size); // 将内存 weather_buf 前的 sizeof(weather_buf) 全部设置为0 int read_size = read(socket_handle, weather_buf, buf_size-1); // 从缓冲区中读取指定长度的数据,当缓冲区中内容小于指定长度时,read() 返回实际读取的数据长度, ESP_LOGI(TAG, "get weather is: %s", weather_buf); // 打印获取的天气信息
四、天气获取案例
#include "lvgl_weather_view.h" #include "cJSON.h" #include "../../wifi/wifi.h" #include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_system.h" #include "esp_wifi.h" #include "esp_event.h" #include "esp_log.h" #include "nvs_flash.h" #include "lwip/err.h" #include "lwip/sockets.h" #include "lwip/sys.h" #include "lwip/netdb.h" #include "lwip/dns.h" /* 获取天气的地址 */ #define WEB_SERVER "api.seniverse.com" // 服务器域名 #define WEB_PORT "80" // 服务器端口 #define WEB_PATH "https://api.seniverse.com/v3/weather/daily.json?key=xxxxxx=Guiyang&language=zh-Hans" // 天气获取路径 /* 存放json解析后的天气信息 */ static lvgl_user_weather_info_t user_weather_info = {0}; static const char *REQUEST = "GET " WEB_PATH " HTTP/1.0\r\n" "Host: "WEB_SERVER":"WEB_PORT"\r\n" "User-Agent: esp-idf/1.0 esp32\r\n" "\r\n"; /** * @brief 获取天气数据 * * @param weather_buf 天气数据的存储空间 * @param buf_size 存储空间的大小 * @return true 获取成功 * @return false 获取失败 */ static bool get_weather_buf(char *weather_buf, size_t buf_size) { const struct addrinfo hints = { .ai_family = AF_INET, // AF_INET表示IPv4,AF_INET6表示IPv6 .ai_socktype = SOCK_STREAM, // SOCK_STREAM表示TCP、SOCK_DGRAM表示UDP、SOCK_RAW表示RAW }; struct addrinfo *dns_info; // DNS 解析信息 int socket_handle; // socket句柄 /* 域名解析 */ int err = getaddrinfo(WEB_SERVER, WEB_PORT, &hints, &dns_info); if(err != 0 || dns_info == NULL) { ESP_LOGE(TAG, "DNS lookup failed err=%d dns_info=%p", err, dns_info); return false; } /* 打印解析的服务器 IP */ struct in_addr *service_IP = &((struct sockaddr_in *)dns_info->ai_addr)->sin_addr; ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", inet_ntoa(*service_IP)); /* 创建套接字 */ socket_handle = socket(dns_info->ai_family, dns_info->ai_socktype, 0); // 0(IPPROTO_IP)可以用来表示选择一个默认的协议。 if(socket_handle < 0) { ESP_LOGE(TAG, "... Failed to allocate socket"); close(socket_handle); freeaddrinfo(dns_info); false; } // ESP_LOGI(TAG, "allocated socket... "); /* 连接服务器 */ if(connect(socket_handle, dns_info->ai_addr, dns_info->ai_addrlen) != 0) { ESP_LOGE(TAG, "... socket connect failed errno=%d", errno); close(socket_handle); freeaddrinfo(dns_info); false; } // ESP_LOGI(TAG, "... connected"); /* 释放 dns_info 指向的空间 */ freeaddrinfo(dns_info); /* 想缓冲区中写入服务请求信息 */ if (write(socket_handle, REQUEST, strlen(REQUEST)) < 0) { ESP_LOGE(TAG, "... socket send failed"); close(socket_handle); false; } // ESP_LOGI(TAG, "... socket send success"); /* 设置请求超时 */ struct timeval receiving_timeout; receiving_timeout.tv_sec = 5; receiving_timeout.tv_usec = 0; if (setsockopt(socket_handle, SOL_SOCKET, SO_RCVTIMEO, &receiving_timeout, sizeof(receiving_timeout)) < 0) { ESP_LOGE(TAG, "... failed to set socket receiving timeout"); close(socket_handle); false; } // ESP_LOGI(TAG, "... set socket receiving timeout success"); /* 从缓冲区中读取天气信息 */ bzero(weather_buf, buf_size); // 将内存 weather_buf 前的 sizeof(weather_buf) 全部设置为0 int read_size = read(socket_handle, weather_buf, buf_size-1); // 从缓冲区中读取指定长度的数据,当缓冲区中内容小于指定长度时,read() 返回实际读取的数据长度, ESP_LOGI(TAG, "get weather is: %s", weather_buf); // 打印获取的天气信息 ESP_LOGI(TAG, "... done reading from socket. Last read return=%d errno=%d.", read_size, errno); // 打印读取到的数据长度 close(socket_handle); return true; }
五、JSON数据解析
/** * @brief 解析天气数据(JSON) * * @param analysis_buf 数据的存储空间 * @return true 解析成功 * @return false 解析失败 */ static bool parse_json_data(const char *analysis_buf) { cJSON *json_data = NULL; /* 截取有效json */ char *index = strchr(analysis_buf, '{'); // strcpy(weather_buf, index); json_data = cJSON_Parse(index); if( json_data == NULL ) // 判断字段是否json格式 { return false; } // ESP_LOGI(TAG, "Start parsing data"); cJSON* cjson_item =cJSON_GetObjectItem(json_data,"results"); cJSON* cjson_results = cJSON_GetArrayItem(cjson_item,0); /* 获取天气的地址 */ cJSON* cjson_location = cJSON_GetObjectItem(cjson_results,"location"); cJSON* cjson_temperature_name = cJSON_GetObjectItem(cjson_location,"name"); strcpy(user_weather_info.location_name,cjson_temperature_name->valuestring); /* 天气信息 */ cJSON* cjson_daily = cJSON_GetObjectItem(cjson_results,"daily"); /* 当天的天气信息 */ cJSON* cjson_daily_1 = cJSON_GetArrayItem(cjson_daily,0); ESP_LOGI(TAG, "day_one_code is: %s", cJSON_GetObjectItem(cjson_daily_1,"code_day")->valuestring); ESP_LOGI(TAG, "day_one_temp_high is: %s", cJSON_GetObjectItem(cjson_daily_1,"high")->valuestring); ESP_LOGI(TAG, "day_three_temp_low is: %s", cJSON_GetObjectItem(cjson_daily_1,"low")->valuestring); ESP_LOGI(TAG, "day_one_humi is: %s", cJSON_GetObjectItem(cjson_daily_1,"humidity")->valuestring); ESP_LOGI(TAG, "day_one_windspeed is: %s", cJSON_GetObjectItem(cjson_daily_1,"wind_speed")->valuestring);
注意:解析JSON数据时,使用的都是 valuestring
数据类型,否则会出现无法解析的现象
参考文献
ESP32学习笔记(12)——JSON接口使用:https://blog.csdn.net/qq_36347513/article/details/116481167
ESP32学习笔记(14)——HTTP服务器 - 简书:https://www.jianshu.com/p/aa865ff71b05
ESP32_IDF学习8【HTTP服务器】 - redlightASl - 博客园:https://www.cnblogs.com/redlightASl/p/15542579.html
ESP32 之 ESP-IDF 教学(十二)WiFi篇—— LwIP 之 TCP 通信:https://blog.csdn.net/m0_50064262/article/details/120265731>
本文来自博客园,作者:浇筑菜鸟,转载请注明原文链接:https://www.cnblogs.com/jzcn/p/16827037.html
如本博客的内容侵犯了你的权益,请与以下地址联系,本人获知后,马上删除。同时本人深表歉意,并致以崇高的谢意! cn_jiaozhu@qq.com
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异