描述
本文来源电子发烧友社区,作者:李元江, 帖子地址:https://bbs.elecfans.com/jishu_2025428_1_1.html
元旦好冷,哪也不想去,那就趁着有空,写写帖子吧。今天的帖子我写的是关于如何从网络获取天气数据,以及如何解析出我们需要的天气数据。
一、天气数据获取
首先说说怎么获取天气数据。现在可以从很多平台获取到天气数据,我使用的平台是心知天气,如何只是获取实时天气情况和最近几天天气情况是免费的,只要注册账号就可以使用。地址为:https://www.seniverse.com/。心知天气里面不仅支持天气数据获取,还支持其他数据获取。这里我们需要获取天气实况和逐日天气情况。每个API有一个请求示例地址,逐日天气请求示例地址如下,每个参数都会有相关说明。
在浏览器地址栏输入请求示例地址后,可以查看返回数据内容情况。
有了这个数据获取接口,接下来以下步骤来获取数据。
二、软件设计1、添加cJson功能
从网络上直接获取到的天气数据是Json格式的,需要进行解析才能得到所需天气数据。解析Json格式数据我借助第三方软件包cJson,通过cJson解析出数据。其实在wifiiot的例程源代码中,已经添加有cJson了。
但是要使用cJson功能,还需要下面操作。在OLED下的BUILD.gn文件中include_dirs加入 "//third_party/cJSON",
2、Json数据解析
在OLED下面新建从cjsonparse.c和cjsonparse.h文件,主要是关于Json数据解析的函数。这里我们获取得天气数据有两个:实时天气情况和未来三天天气数据情况,所以需要解析实时天气Json数据和未来三天天气Json数据。
解析实时天气,主要是为了获取现在的温度和天气情况代码。
-
int cJSON_NowWeatherParse(char *JSON,weather *Weather)
-
{
-
cJSON *json,*arrayItem,*object,*subobject,*item;
-
-
json = cJSON_Parse(JSON); //解析JSON数据包
-
if(json == NULL) //检测JSON数据包是否存在语法上的错误,返回NULL表示数据包无效
-
{
-
printf("Error before: [%s]n",cJSON_GetErrorPtr()); //打印数据包语法错误的位置
-
return 1;
-
}
-
else
-
{
-
if((arrayItem = cJSON_GetObjectItem(json,"results")) != NULL) //匹配字符串"results",获取数组内容
-
{
-
cJSON_GetArraySize(arrayItem); //获取数组中对象个数
-
//printf("cJSON_GetArraySize: size=%dn",size);
-
-
if((object = cJSON_GetArrayItem(arrayItem,0)) != NULL)//获取父对象内容
-
{
-
/* 匹配子对象1 */
-
if((subobject = cJSON_GetObjectItem(object,"location")) != NULL)
-
{
-
-
}
-
/* 匹配子对象2 */
-
if((subobject = cJSON_GetObjectItem(object,"now")) != NULL)
-
{
-
printf("---------------------------------now-------------------------------n");
-
//匹配子对象2成员"text"
-
if((item = cJSON_GetObjectItem(subobject,"text")) != NULL)
-
{
-
printf("%s : %sn",item->string,item->valuestring);
-
}
-
//匹配子对象2成员"code"
-
if((item = cJSON_GetObjectItem(subobject,"code")) != NULL)
-
{
-
printf("%s : %sn",item->string,item->valuestring);
-
Weather->nowcode = str2int(item->valuestring);
-
}
-
//匹配子对象2成员"temperature"
-
if((item = cJSON_GetObjectItem(subobject,"temperature")) != NULL)
-
{
-
printf("%s : %sn",item->string,item->valuestring);
-
Weather->nowtemp = str2int(item->valuestring);
-
}
-
}
-
/* 匹配子对象last_update */
-
if((subobject = cJSON_GetObjectItem(object,"last_update")) != NULL)
-
{
-
printf("----------------------------last_update----------------------------n");
-
printf("%s : %snn",subobject->string,subobject->valuestring);
-
}
-
}
-
}
-
}
-
cJSON_Delete(json); //释放cJSON_Parse()分配出来的内存空间
-
return 0;
-
}
复制代码
解析未来三天天气情况数据,主要为了获取今天、明天、后天的最高、最低温度、天气情况代码、湿度情况。当然也可以解析获取更改天气情况数据,但是这里我只解析获取那么多。
-
//解析三天天气
-
int cJSON_TayWeatherParse(char *JSON,weather *weather)
-
{
-
cJSON *root;
-
cJSON *pSub;
-
cJSON *arrayItem;
-
cJSON *pItem;
-
cJSON *pSubItem;
-
cJSON *pChildItem;
-
cJSON *pLastItem;
-
char *pr;
-
root = cJSON_Parse((const char*)JSON);
-
if(root != NULL)
-
{
-
pSub = cJSON_GetObjectItem(root,"results");
-
if(pSub != NULL)
-
{
-
arrayItem = cJSON_GetArrayItem(pSub,0);
-
pr = cJSON_Print(arrayItem);
-
pItem = cJSON_Parse(pr);
-
if(pItem != NULL)
-
{
-
pSubItem = cJSON_GetObjectItem(pItem,"daily");
-
if(pSubItem != NULL)
-
{
-
int size = cJSON_GetArraySize(pSubItem);
-
for(int i=0;i;i++)
-
{
-
if(i==3)break;
-
arrayItem = cJSON_GetArrayItem(pSubItem,i);
-
pr = cJSON_Print(arrayItem);
-
pLastItem = cJSON_Parse(pr);
-
if(pLastItem != NULL)
-
{
-
if((pChildItem = cJSON_GetObjectItem(pLastItem,"high")) != NULL)
-
{
-
printf("%s : %sn",pChildItem->string,pChildItem->valuestring);
-
weather->high[i] = str2int(pChildItem->valuestring);
-
}
-
-
if((pChildItem = cJSON_GetObjectItem(pLastItem,"low")) != NULL)
-
{
-
printf("%s : %sn",pChildItem->string,pChildItem->valuestring);
-
weather->low[i] = str2int(pChildItem->valuestring);
-
}
-
-
if((pChildItem = cJSON_GetObjectItem(pLastItem,"code_day"))!=NULL)
-
{
-
printf("%s : %sn",pChildItem->string,pChildItem->valuestring);
-
weather->code[i] = str2int(pChildItem->valuestring);
-
}
-
if((pChildItem = cJSON_GetObjectItem(pLastItem,"humidity"))!=NULL)
-
{
-
printf("%s : %sn",pChildItem->string,pChildItem->valuestring);
-
weather->humi[i] = str2int(pChildItem->valuestring);
-
}
-
}
-
cJSON_Delete(pLastItem);
-
}
-
}
-
}
-
cJSON_Delete(pItem);
-
}
-
}
-
cJSON_Delete(root);
-
-
return 0;
-
}
复制代码
3、 获取天气数据
新建getweather.c文件,主要获取天气数据的功能函数。设置的代码中主要经过如下步骤获取和解析天气情况数据。
-
2、 连接到服务器
-
3、发送近三天天气情况请求
-
4、接收数据
-
5、解析近三天天气Json数据
-
6、关闭与服务器连接
-
7、从新连接到服务器
-
8、发送实时天气情况请求
-
9、接收数据
-
10、解析实时天气Json数据
-
11、关闭与服务器连接
-
12、断开与网络的连接
-
#include
-
#include
-
#include
-
#include
-
-
#include "net_demo.h"
-
#include "net_common.h"
-
#include "net_params.h"
-
#include "wifi_connecter.h"
-
#include "ohos_init.h"
-
#include "cmsis_os2.h"
-
#include "cjsonparse.h"
-
-
#define WEATHERIPADDR "116.62.81.138"
-
#define WEATHERPORT 80
-
-
static char requestday[] = "GET https://api.seniverse.com/v3/weather/daily.json?key=SgJs9V9ghopE5WSBe&location=shenzhen&language=zh-Hans&unit=c&start=0&days=3rnrn";
-
static char requestnow[] = "GET https://api.seniverse.com/v3/weather/now.json?key=SgJs9V9ghopE5WSBe&location=shenzhen&language=zh-Hans&unit=crnrn";
-
-
static char response[1000] = "";
-
-
weather weatherValue;
-
-
bool getWeather(void){
-
bool sucflag = false;
-
WifiDeviceConfig config = {0};
-
-
// 准备AP的配置参数
-
strcpy(config.ssid, PARAM_HOTSPOT_SSID);
-
strcpy(config.preSharedKey, PARAM_HOTSPOT_PSK);
-
config.securityType = PARAM_HOTSPOT_TYPE;
-
osDelay(10);
-
int netId = ConnectToHotspot(&config);
-
-
/*获取最近三天天气情况*/
-
int32_t retval = 0;
-
int sockfd = socket(AF_INET, SOCK_STREAM, 0); // TCP socket
-
struct sockaddr_in serverAddr = {0};
-
serverAddr.sin_family = AF_INET; // AF_INET表示IPv4协议
-
serverAddr.sin_port = htons(WEATHERPORT); // 端口号,从主机字节序转为网络字节序
-
if (inet_pton(AF_INET, WEATHERIPADDR, &serverAddr.sin_addr) <= 0) { // 将主机IP地址从“点分十进制”字符串 转化为 标准格式(32位整数)
-
printf("inet_pton failed!rn");
-
goto do_cleanup;
-
}
-
// 尝试和目标主机建立连接,连接成功会返回0 ,失败返回 -1
-
if (connect(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) {
-
printf("connect failed!rn");
-
goto do_cleanup;
-
}
-
printf("connect to server %s success!rn", WEATHERIPADDR);
-
-
// 建立连接成功之后,这个TCP socket描述符 —— sockfd 就具有了 “连接状态”,发送、接收 对端都是 connect 参数指定的目标主机和端口
-
//retval = send(sockfd, requestnow, sizeof(requestnow), 0);
-
retval = send(sockfd, requestday, sizeof(requestday), 0);
-
if (retval < 0) {
-
printf("send request failed!rn");
-
goto do_cleanup;
-
}
-
printf("send request{%s} %ld to server done!rn", requestday, retval);
-
retval = recv(sockfd, &response, sizeof(response), 0);
-
if (retval <= 0) {
-
printf("send response from server failed or done, %ld!rn", retval);
-
goto do_cleanup;
-
}
-
response[retval] = '';
-
int i = 0;
-
/*打印接收到数据*/
-
while(i)
-
{
-
printf("%c",response[i]);
-
i++;
-
}
-
cJSON_TayWeatherParse(response,&weatherValue);
-
close(sockfd);
-
-
/*获取现在的天气情况*/
-
sockfd = socket(AF_INET, SOCK_STREAM, 0); // TCP socket
-
if (inet_pton(AF_INET, WEATHERIPADDR, &serverAddr.sin_addr) <= 0) { // 将主机IP地址从“点分十进制”字符串 转化为 标准格式(32位整数)
-
printf("inet_pton failed!rn");
-
goto do_cleanup;
-
}
-
// 尝试和目标主机建立连接,连接成功会返回0 ,失败返回 -1
-
if (connect(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) {
-
printf("connect failed!rn");
-
goto do_cleanup;
-
}
-
-
retval = send(sockfd, requestnow, sizeof(requestnow), 0);
-
if (retval < 0) {
-
printf("send request failed!rn");
-
goto do_cleanup;
-
}
-
printf("send request{%s} %ld to server done!rn", requestnow, retval);
-
retval = recv(sockfd, &response, sizeof(response), 0);
-
if (retval <= 0) {
-
printf("send response from server failed or done, %ld!rn", retval);
-
goto do_cleanup;
-
}
-
response[retval] = '';
-
i = 0;
-
/*打印接收到数据*/
-
while(i)
-
{
-
printf("%c",response[i]);
-
i++;
-
}
-
cJSON_NowWeatherParse(response,&weatherValue);
-
sucflag=true;
-
do_cleanup:
-
close(sockfd);
-
DisconnectWithHotspot(netId);
-
if(sucflag)
-
return true;
-
else
-
return false;
-
}
复制代码
4、加入任务中
把获取天气数据功能增加到任务中。在oled_demo.c中static void OledTask(void *arg)函数增加以下代码。
-
AdcRead(ANALOG_KEY_CHAN_NAME, &data, WIFI_IOT_ADC_EQU_MODEL_4, WIFI_IOT_ADC_CUR_BAIS_DEFAULT, 0);
-
float voltage = ConvertToVoltage(data);
-
-
if(voltage>0.45 && voltage<0.65)
-
{
-
OledShowString(16,7,"Sync time...",1);
-
getNtpTime();
-
OledFillScreen(0);
-
}
-
else if(voltage>0.9 && voltage<1)
-
{
-
-
OledShowString(0,7,"Get Weather...",1);
-
if(getWeather())
-
OledFillScreen(0);
-
else
-
{
-
OledShowString(0,7,"Get fail...",1);
-
}
-
-
}
复制代码
按下oled显示板的右边按钮,会进入获取天气情况功能。现在我这里只是通过串口打印出来的数据,观察数据获取和解析情况,还没有把解析后的天气数据显示到oled上。
5、修改BUILD.gn
修改OLED文件夹下的BUILD.gn文件,sources中加入getweather.c和cjsonparse.c
-
sources = [
-
"oled_demo.c",
-
"oled_ssd1306.c",
-
"timeconv.c",
-
"envrionment_demo.c",
-
"aht20.c",
-
"wifi_connecter.c",
-
"getNTP.c",
-
"getweather.c",
-
"cjsonparse.c",
-
]
复制代码
三、结果演示
按下OLED显示板右边按键,会进入天气数据功能,之后显示“Get Weather....”提示。天气数据获取失败后,会显示“Get fail...”提示。
可以从串口打印输出的信息,观察到获取的Json数据情况和解析后的数据情况。
四、总结
网络天气数据的获取主要经过如下步骤
2021年第一篇帖子,先写到这里。下一篇是关于通过TCP连接与手机APP进行数据交互的帖子。当然手机APP是我之前做好的。
打开APP阅读更多精彩内容