【HarmonyOS HiSpark Wi-Fi IoT 套件试用连载】七、获取网络天气数据

描述

本文来源电子发烧友社区,作者:李元江, 帖子地址:https://bbs.elecfans.com/jishu_2025428_1_1.html

元旦好冷,哪也不想去,那就趁着有空,写写帖子吧。今天的帖子我写的是关于如何从网络获取天气数据,以及如何解析出我们需要的天气数据。

 
一、天气数据获取
       首先说说怎么获取天气数据。现在可以从很多平台获取到天气数据,我使用的平台是心知天气,如何只是获取实时天气情况和最近几天天气情况是免费的,只要注册账号就可以使用。地址为:https://www.seniverse.com/。心知天气里面不仅支持天气数据获取,还支持其他数据获取。这里我们需要获取天气实况和逐日天气情况。每个API有一个请求示例地址,逐日天气请求示例地址如下,每个参数都会有相关说明。
HiSpark
在浏览器地址栏输入请求示例地址后,可以查看返回数据内容情况。
HiSpark
有了这个数据获取接口,接下来以下步骤来获取数据。
  • 连接心知天气服务器心知天气服务器地址为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格式的数据,需要解析才能得到我们真正需要的数据。下面是我使用调试助手获取到的数据情况。

HiSpark

二、软件设计
1、添加cJson功能

从网络上直接获取到的天气数据是Json格式的,需要进行解析才能得到所需天气数据。解析Json格式数据我借助第三方软件包cJson,通过cJson解析出数据。其实在wifiiot的例程源代码中,已经添加有cJson了。

HiSpark

 

但是要使用cJson功能,还需要下面操作。在OLED下的BUILD.gn文件中include_dirs加入 "//third_party/cJSON",

HiSpark

 

2、Json数据解析

在OLED下面新建从cjsonparse.c和cjsonparse.h文件,主要是关于Json数据解析的函数。这里我们获取得天气数据有两个:实时天气情况和未来三天天气数据情况,所以需要解析实时天气Json数据和未来三天天气Json数据。

解析实时天气,主要是为了获取现在的温度和天气情况代码。

  1. int cJSON_NowWeatherParse(char *JSON,weather *Weather)
  2. {
  3. cJSON *json,*arrayItem,*object,*subobject,*item;
  4. json = cJSON_Parse(JSON); //解析JSON数据包
  5. if(json == NULL)      //检测JSON数据包是否存在语法上的错误,返回NULL表示数据包无效
  6. {
  7.    printf("Error before: [%s]n",cJSON_GetErrorPtr()); //打印数据包语法错误的位置
  8.    return 1;
  9. }
  10. else
  11. {
  12.    if((arrayItem = cJSON_GetObjectItem(json,"results")) != NULL) //匹配字符串"results",获取数组内容
  13.    {
  14.      cJSON_GetArraySize(arrayItem);     //获取数组中对象个数
  15.      //printf("cJSON_GetArraySize: size=%dn",size);
  16.      
  17.      if((object = cJSON_GetArrayItem(arrayItem,0)) != NULL)//获取父对象内容
  18.      {
  19.        /* 匹配子对象1 */
  20.        if((subobject = cJSON_GetObjectItem(object,"location")) != NULL)
  21.        {
  22.        }
  23.         /* 匹配子对象2 */
  24.        if((subobject = cJSON_GetObjectItem(object,"now")) != NULL)
  25.        {
  26.          printf("---------------------------------now-------------------------------n");
  27.          //匹配子对象2成员"text"
  28.          if((item = cJSON_GetObjectItem(subobject,"text")) != NULL)
  29.          {
  30.            printf("%s : %sn",item->string,item->valuestring);
  31.          }
  32.          //匹配子对象2成员"code"
  33.          if((item = cJSON_GetObjectItem(subobject,"code")) != NULL)
  34.          {
  35.            printf("%s : %sn",item->string,item->valuestring);
  36.            Weather->nowcode = str2int(item->valuestring);
  37.          }
  38.          //匹配子对象2成员"temperature"
  39.          if((item = cJSON_GetObjectItem(subobject,"temperature")) != NULL)
  40.          {
  41.            printf("%s : %sn",item->string,item->valuestring);
  42.            Weather->nowtemp = str2int(item->valuestring);
  43.          }
  44.        }
  45.        /* 匹配子对象last_update */
  46.        if((subobject = cJSON_GetObjectItem(object,"last_update")) != NULL)
  47.        {
  48.          printf("----------------------------last_update----------------------------n");
  49.          printf("%s : %snn",subobject->string,subobject->valuestring);
  50.        }
  51.      }
  52.    }
  53. }
  54. cJSON_Delete(json); //释放cJSON_Parse()分配出来的内存空间
  55. return 0;
  56. }
复制代码

解析未来三天天气情况数据,主要为了获取今天、明天、后天的最高、最低温度、天气情况代码、湿度情况。当然也可以解析获取更改天气情况数据,但是这里我只解析获取那么多。

  1. //解析三天天气
  2. int cJSON_TayWeatherParse(char *JSON,weather *weather)
  3. {
  4.     cJSON *root;
  5.     cJSON *pSub;
  6.     cJSON *arrayItem;
  7.     cJSON *pItem;
  8.     cJSON *pSubItem;
  9.     cJSON *pChildItem;
  10.     cJSON *pLastItem;
  11.     char *pr;
  12.     root = cJSON_Parse((const char*)JSON);
  13.     if(root != NULL)
  14.     {
  15.         pSub = cJSON_GetObjectItem(root,"results");
  16.         if(pSub != NULL)
  17.         {
  18.             arrayItem = cJSON_GetArrayItem(pSub,0);
  19.             pr = cJSON_Print(arrayItem);
  20.             pItem = cJSON_Parse(pr);
  21.             if(pItem != NULL)
  22.             {
  23.                 pSubItem = cJSON_GetObjectItem(pItem,"daily");
  24.                 if(pSubItem != NULL)
  25.                 {
  26.                     int size = cJSON_GetArraySize(pSubItem);
  27.                     for(int i=0;i;i++)
  28.                     {
  29.                         if(i==3)break;
  30.                         arrayItem = cJSON_GetArrayItem(pSubItem,i);
  31.                         pr = cJSON_Print(arrayItem);
  32.                         pLastItem = cJSON_Parse(pr);
  33.                         if(pLastItem != NULL)
  34.                         {
  35.                             if((pChildItem =  cJSON_GetObjectItem(pLastItem,"high")) != NULL)
  36.                             {
  37.                                 printf("%s : %sn",pChildItem->string,pChildItem->valuestring);
  38.                                 weather->high[i] = str2int(pChildItem->valuestring);
  39.                             }
  40.                             if((pChildItem =  cJSON_GetObjectItem(pLastItem,"low")) != NULL)
  41.                             {
  42.                                 printf("%s : %sn",pChildItem->string,pChildItem->valuestring);
  43.                                 weather->low[i] = str2int(pChildItem->valuestring);
  44.                             }
  45.                                 
  46.                             if((pChildItem =  cJSON_GetObjectItem(pLastItem,"code_day"))!=NULL)
  47.                             {
  48.                                 printf("%s : %sn",pChildItem->string,pChildItem->valuestring);
  49.                                 weather->code[i] = str2int(pChildItem->valuestring);
  50.                             }                                       
  51.                             if((pChildItem =  cJSON_GetObjectItem(pLastItem,"humidity"))!=NULL)                                             
  52.                             {
  53.                                 printf("%s : %sn",pChildItem->string,pChildItem->valuestring);
  54.                                 weather->humi[i] = str2int(pChildItem->valuestring);
  55.                             }
  56.                         }
  57.                         cJSON_Delete(pLastItem);
  58.                     }
  59.                 }
  60.             }
  61.             cJSON_Delete(pItem);
  62.         }
  63.     }
  64.     cJSON_Delete(root);
  65.     return 0;
  66. }
复制代码
3、 获取天气数据

新建getweather.c文件,主要获取天气数据的功能函数。设置的代码中主要经过如下步骤获取和解析天气情况数据。

  • 1、连接网络
  • 2、 连接到服务器
  • 3、发送近三天天气情况请求
  • 4、接收数据
  • 5、解析近三天天气Json数据
  • 6、关闭与服务器连接
  • 7、从新连接到服务器
  • 8、发送实时天气情况请求
  • 9、接收数据
  • 10、解析实时天气Json数据
  • 11、关闭与服务器连接
  • 12、断开与网络的连接
  1. #include
  2. #include
  3. #include
  4. #include
  5. #include "net_demo.h"
  6. #include "net_common.h"
  7. #include "net_params.h"
  8. #include "wifi_connecter.h"
  9. #include "ohos_init.h"
  10. #include "cmsis_os2.h"
  11. #include "cjsonparse.h"
  12. #define     WEATHERIPADDR       "116.62.81.138"
  13. #define     WEATHERPORT          80
  14. 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";
  15. static char requestnow[] = "GET https://api.seniverse.com/v3/weather/now.json?key=SgJs9V9ghopE5WSBe&location=shenzhen&language=zh-Hans&unit=crnrn";
  16. static char response[1000] = "";
  17. weather weatherValue;
  18. bool getWeather(void){
  19.     bool sucflag = false;
  20.     WifiDeviceConfig config = {0};
  21.     // 准备AP的配置参数
  22.     strcpy(config.ssid, PARAM_HOTSPOT_SSID);
  23.     strcpy(config.preSharedKey, PARAM_HOTSPOT_PSK);
  24.     config.securityType = PARAM_HOTSPOT_TYPE;
  25.     osDelay(10);
  26.     int netId = ConnectToHotspot(&config);
  27.     /*获取最近三天天气情况*/
  28.     int32_t retval = 0;
  29.     int sockfd = socket(AF_INET, SOCK_STREAM, 0); // TCP socket
  30.     struct sockaddr_in serverAddr = {0};
  31.     serverAddr.sin_family = AF_INET;  // AF_INET表示IPv4协议
  32.     serverAddr.sin_port = htons(WEATHERPORT);  // 端口号,从主机字节序转为网络字节序
  33.     if (inet_pton(AF_INET, WEATHERIPADDR, &serverAddr.sin_addr) <= 0) {  // 将主机IP地址从“点分十进制”字符串 转化为 标准格式(32位整数)
  34.         printf("inet_pton failed!rn");
  35.         goto do_cleanup;
  36.     }
  37.     // 尝试和目标主机建立连接,连接成功会返回0 ,失败返回 -1
  38.     if (connect(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) {
  39.         printf("connect failed!rn");
  40.         goto do_cleanup;
  41.     }
  42.     printf("connect to server %s success!rn", WEATHERIPADDR);
  43.     // 建立连接成功之后,这个TCP socket描述符 —— sockfd 就具有了 “连接状态”,发送、接收 对端都是 connect 参数指定的目标主机和端口
  44.     //retval = send(sockfd, requestnow, sizeof(requestnow), 0);
  45.      retval = send(sockfd, requestday, sizeof(requestday), 0);
  46.     if (retval < 0) {
  47.         printf("send request failed!rn");
  48.         goto do_cleanup;
  49.     }
  50.     printf("send request{%s} %ld to server done!rn", requestday, retval);
  51.     retval = recv(sockfd, &response, sizeof(response), 0);
  52.     if (retval <= 0) {
  53.         printf("send response from server failed or done, %ld!rn", retval);
  54.         goto do_cleanup;
  55.     }
  56.     response[retval] = '';
  57.     int i = 0;
  58.     /*打印接收到数据*/
  59.     while(i)
  60.     {
  61.         printf("%c",response[i]);
  62.         i++;
  63.     }
  64.      cJSON_TayWeatherParse(response,&weatherValue);
  65.     close(sockfd);
  66.     /*获取现在的天气情况*/
  67.     sockfd = socket(AF_INET, SOCK_STREAM, 0); // TCP socket
  68.     if (inet_pton(AF_INET, WEATHERIPADDR, &serverAddr.sin_addr) <= 0) {  // 将主机IP地址从“点分十进制”字符串 转化为 标准格式(32位整数)
  69.         printf("inet_pton failed!rn");
  70.         goto do_cleanup;
  71.     }
  72.     // 尝试和目标主机建立连接,连接成功会返回0 ,失败返回 -1
  73.     if (connect(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) {
  74.         printf("connect failed!rn");
  75.         goto do_cleanup;
  76.     }
  77.     retval = send(sockfd, requestnow, sizeof(requestnow), 0);
  78.     if (retval < 0) {
  79.         printf("send request failed!rn");
  80.         goto do_cleanup;
  81.     }
  82.     printf("send request{%s} %ld to server done!rn", requestnow, retval);
  83.     retval = recv(sockfd, &response, sizeof(response), 0);
  84.     if (retval <= 0) {
  85.         printf("send response from server failed or done, %ld!rn", retval);
  86.         goto do_cleanup;
  87.     }
  88.     response[retval] = '';
  89.     i = 0;
  90.      /*打印接收到数据*/
  91.     while(i)
  92.     {
  93.         printf("%c",response[i]);
  94.         i++;
  95.     }
  96.     cJSON_NowWeatherParse(response,&weatherValue);
  97.     sucflag=true;
  98. do_cleanup:
  99.     close(sockfd);
  100.     DisconnectWithHotspot(netId);
  101.     if(sucflag)
  102.     return true;
  103.     else
  104.     return false;
  105. }
复制代码
4、加入任务中

把获取天气数据功能增加到任务中。在oled_demo.c中static void OledTask(void *arg)函数增加以下代码。

  1. AdcRead(ANALOG_KEY_CHAN_NAME, &data, WIFI_IOT_ADC_EQU_MODEL_4, WIFI_IOT_ADC_CUR_BAIS_DEFAULT, 0);
  2.         float voltage = ConvertToVoltage(data);
  3.         if(voltage>0.45 && voltage<0.65)
  4.         {
  5.             OledShowString(16,7,"Sync time...",1);
  6.             getNtpTime();
  7.             OledFillScreen(0);
  8.         }
  9.         else if(voltage>0.9 && voltage<1)
  10.         {
  11.             OledShowString(0,7,"Get Weather...",1);
  12.             if(getWeather())
  13.             OledFillScreen(0);
  14.             else
  15.             {
  16.             OledShowString(0,7,"Get fail...",1);
  17.             }
  18.             
  19.         }
复制代码

按下oled显示板的右边按钮,会进入获取天气情况功能。现在我这里只是通过串口打印出来的数据,观察数据获取和解析情况,还没有把解析后的天气数据显示到oled上。

5、修改BUILD.gn

修改OLED文件夹下的BUILD.gn文件,sources中加入getweather.c和cjsonparse.c

  1.     sources = [
  2.         "oled_demo.c",
  3.         "oled_ssd1306.c",
  4.         "timeconv.c",
  5.         "envrionment_demo.c",
  6.         "aht20.c",
  7.         "wifi_connecter.c",
  8.         "getNTP.c",
  9.         "getweather.c",
  10.         "cjsonparse.c",
  11.     ]
复制代码

三、结果演示

按下OLED显示板右边按键,会进入天气数据功能,之后显示“Get Weather....”提示。天气数据获取失败后,会显示“Get fail...”提示。

HiSpark

 

可以从串口打印输出的信息,观察到获取的Json数据情况和解析后的数据情况。

HiSpark

 


四、总结

网络天气数据的获取主要经过如下步骤

  • 连接网络
  • 连接服务器
  • 请求数据
  • 解析Json数据

2021年第一篇帖子,先写到这里。下一篇是关于通过TCP连接与手机APP进行数据交互的帖子。当然手机APP是我之前做好的。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分