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>
ESP32 IDF 获取天气信息的更多相关文章
- 半吊子学习Swift--天气预报程序-获取天气信息
昨天申请的彩云天气Api开发者今天上午已审核通过  饭后运动过后就马不停蹄的来测试接口,接口是采用经纬度的方式来获取天气信息,接口地址如下 https://api.caiyunapp.com/v2/ ...
- 内网公告牌获取天气信息解决方案(C# WebForm)
需求:内网公告牌能够正确显示未来三天的天气信息 本文关键字:C#/WebForm/Web定时任务/Ajax跨域 规划: 1.天定时读取百度接口获取天气信息并存储至Txt文档: 2.示牌开启时请求Web ...
- C#调用WebService获取天气信息
概述 本文使用C#开发Winform应用程序,通过调用<WebXml/>(URL:http://www.webxml.com.cn)的WebService服务WeatherWS来获取天气预 ...
- java获取天气信息
通过天气信息接口获取天气信息,首先要给项目导入程序所需要的包,具体需要如下几个包: json-lib-2.4.jar ezmorph-1.0.6.jar commons-beanutils-1.8.3 ...
- Kettle通过Webservice获取天气信息
Kettle通过Webservice获取天气信息 需求: 通过kettle工具,通过webservice获取天气信息,写成xml格式文件. 思路: Kettle可通过两种选择获取webservic ...
- Java通过webservice接口获取天气信息
通过SOAP请求的方式获取天气信息并解析返回的XML文件. 参考: http://www.webxml.com.cn/WebServices/WeatherWS.asmx import java.io ...
- ajax无刷新获取天气信息
浏览器由于安全方面的问题,禁止ajax跨域请求其他网站的数据,但是可以再本地的服务器上获取其他服务器的信息,在通过ajax请求本地服务来实现: <?php header("conten ...
- Android实现自动定位城市并获取天气信息
定位实现代码: <span style="font-size:14px;">import java.io.IOException; import java.util.L ...
- java解析xml实例——获取天气信息
获取xml并解析其中的数据: package getweather.xml; import java.io.IOException; import java.util.HashMap; import ...
随机推荐
- 常见docker命令(二)-容器生命周期相关
docker run 命令主要参数-d 后台运行,返回容器id-i 以交互模式运行,通常与-t连用-t 为容器重新分配一个伪输入终端,通常与-i连用-P(大写) 随机端口映射,容器内部端口随机映射到主 ...
- HC32L110 系列 M0 MCU 的介绍和Win10下DAP-Link, ST-Link, J-Link的烧录
HC32L110 系列 Cortex M0 MCU Hackaday 在三月份的时候介绍了一款最小的MCU NEW PART DAY: SMALLEST ARM MCU UPROOTS COMPETI ...
- 我开源了一个Go学习仓库|笔记预览
前言 大半个月前我参与了字节后端面试,未通过第四面,面试总结写在了这篇文章: https://juejin.cn/post/7132712873351970823 在此文的末尾,我写到为了全面回顾Go ...
- PerfView专题 (第十篇):洞察 C# 终结队列引发的内存泄漏
一:背景 C# 程序内存泄漏的诱发因素有很多,但从顶层原理上来说,就是该销毁的 用户根 对象没有被销毁,从而导致内存中意料之外的对象无限堆积,导致内存暴涨,最终崩溃,这其中的一个用户根就是 终结器队列 ...
- 【SwiftUI】学习笔记1-创建第一个iOS应用
本系列将会开发大量实际的项目. 系列为本人学习笔记,资料:<SwiftUI自学成长笔记>-刘铭 资源源代码下载资源:可以在gitee上下载,搜索刘铭即可. 第一章:创建项目 也可以在菜单栏 ...
- windows系统-不能打印问题:PDF打印软件正常打开PDF文件,点击打印后软件卡死并提示未响应(No response)
电脑突然出现PDF软件卡死问题,导致无法打印:初步思路记录: 导致问题出现的原因可能为文件问题(文件过大,打印机容量小).打印机问题(打印机未连接.故障等).电脑驱动问题(打印机驱动损坏).电脑补丁问 ...
- 基于ASP.NET Core 6.0的整洁架构
大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享学习心得,希望我的文章能成为你成长路上的垫脚石,让我们一起精进. 本节将介绍基于ASP.NET Core的整洁架构的设计理念,同时基于理论落地的代码 ...
- 实时降噪(Real-time Denoising):Nvidia Real-time Denoisers 源码剖析
目录 Nvidia Real-time Denoisers(NRD) v3.x ReBLUR 前置知识 空间滤波(Spatial Filtering):Diffuse & Specular 泊 ...
- GB/T 28181联网系统通信协议结构和技术实现
技术回顾 在本文开头,我们先一起回顾下GB/T28181联网系统通信协议结构: 联网系统在进行视音频传输及控制时应建立两个传输通道:会话通道和媒体流通道. 会话通道用于在设备之间建立会话并传输系统控制 ...
- KingbaseES 中可以通过构造一个聚集函数来实现mysql的any_value功能。
示例 创建函数 create function coalesce_("anyelement","anyelement") returns "anyel ...