本文來源電子發燒友社區,作者:李元江, 帖子地址:https://bbs.elecfans.com/jishu_2025428_1_1.html
元旦好冷,哪也不想去,那就趁著有空,寫寫帖子吧。今天的帖子我寫的是關于如何從網絡獲取天氣數據,以及如何解析出我們需要的天氣數據。
-
連接心知天氣服務器心知天氣服務器地址為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加入 "http://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;
- }
新建getweather.c文件,主要獲取天氣數據的功能函數。設置的代碼中主要經過如下步驟獲取和解析天氣情況數據。
-
1、連接網絡
-
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;
- }
把獲取天氣數據功能增加到任務中。在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顯示板的右邊按鈕,會進入獲取天氣情況功能?,F在我這里只是通過串口打印出來的數據,觀察數據獲取和解析情況,還沒有把解析后的天氣數據顯示到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數據情況和解析后的數據情況。
四、總結
網絡天氣數據的獲取主要經過如下步驟
-
連接網絡
-
連接服務器
-
請求數據
-
解析Json數據
2021年第一篇帖子,先寫到這里。下一篇是關于通過TCP連接與手機APP進行數據交互的帖子。當然手機APP是我之前做好的。
-
wi-fi
+關注
關注
14文章
2112瀏覽量
124330 -
HarmonyOS
+關注
關注
79文章
1967瀏覽量
30025 -
HiSpark
+關注
關注
1文章
156瀏覽量
6904
發布評論請先 登錄
相關推薦
評論