元旦好冷,哪也不想去,那就趁着有空,写写帖子吧。今天的帖子我写的是关于如何从网络获取天气数据,以及如何解析出我们需要的天气数据。
一、天气数据获取 首先说说怎么获取天气数据。现在可以从很多平台获取到天气数据,我使用的平台是心知天气,如何只是获取实时天气情况和最近几天天气情况是免费的,只要注册账号就可以使用。地址为:https://www.seniverse.com/。心知天气里面不仅支持天气数据获取,还支持其他数据获取。这里我们需要获取天气实况和逐日天气情况。每个API有一个请求示例地址,逐日天气请求示例地址如下,每个参数都会有相关说明。
在浏览器地址栏输入请求示例地址后,可以查看返回数据内容情况。
有了这个数据获取接口,接下来以下步骤来获取数据。
连接心知天气服务器心知天气服务器地址为116.62.81.138。端口号为80,连接方式为TCP
发送Get请求成功连接到心知天气服务器后,需要发送Get请求才能获取到数据。我要获取最近三天的天气预报情况,请求地址为https://api.seniverse.com/v3/weather/daily.json?key=SgJs9V9ghopE5WSBe&location=shenzhen&language=zh-Hans&unit=c&start=0&days=3则我们需要发送的数据为“Get https://api.seniverse.com/v3/weather/daily.json?key=SgJs9V9ghopE5WSBe&location=shenzhen&language=zh-Hans&unit=c&start=0&days=3rnrn”注意:最后为两个回车换行。之后会返回天气数据包,但是该数据包是Json格式的数据,需要解析才能得到我们真正需要的数据。下面是我使用调试助手获取到的数据情况。
二、软件设计
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<size;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 <errno.h>
- #include <stdio.h>
- #include <string.h>
- #include <unistd.h>
-
- #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] = '\0';
- int i = 0;
- /*打印接收到数据*/
- while(i<retval)
- {
- 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] = '\0';
- i = 0;
- /*打印接收到数据*/
- while(i<retval)
- {
- 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是我之前做好的。