1. 開發(fā)環(huán)境搭建
本次評測開發(fā)環(huán)境搭建在windows11的WSL2的Ubuntu20.04中,
(1) 在WSL的Ubuntu20.04下安裝必要的工具的.
- 安裝git:
sudo apt-get install git
- 安裝gcc編譯器套件:
sudo apt-get install build-essential
sudo apt-get install bear
- 安裝ncurses庫,使用make menuconfig配置工程時依賴該庫.
sudo apt-get install libncurses5-dev
(2)安裝windows下必要的工具
我們編譯的固件要通過串口燒錄到XR806,由于WSL2下不能直接使用windows的串口,所以需要在windows下使用工具 usbipd共享串口給WSL2使用,
(3)搭建XR806編譯開發(fā)環(huán)境
需要注意的是文章中提供的編譯工具鏈gcc-arm-none-eabi-8-2019-q3-update下載鏈接無效,
2. 創(chuàng)建工程
我們在終端中進入工程跟目錄,按如下步驟來配置工程和編譯代碼生成鏡像:
# 復制默認配置文件到頂層目錄(不切換工程可不要此步驟)
$ make PRJ=demo/wifi_sta defconfig
# 檢查SDK 基礎配置,如工程名、芯片型號、高頻晶振、板級配置是否正確
$ make menuconfig
# 清理,切換工程時需要
$ make build_clean
# 編譯代碼并生成鏡像文件,生成的鏡像文件為“out/xr_system.img”
$ bear make build -j 12
依次執(zhí)行上述命令后,在工程根目錄執(zhí)行如下命令,使用vscode打開工程code .
在vscode中打開工程目錄后,敲擊F1鍵,彈出如下選擇項,我們選擇C/C++:編輯配置(UI)
在編譯器路徑輸入框中輸入XR806交叉編譯器完整路徑,如下圖所示:
在高級設置下的編譯命令輸入框中輸入編譯數(shù)據(jù)庫文件compile_commands.json的路徑,如下圖所示:
3. 編輯工程代碼
想要從心知天氣網(wǎng)獲取天氣預報,首先需要注冊該網(wǎng)站賬號. 注冊賬號并登陸,打開獲取天氣預報相關的API文檔頁
其中參數(shù)"your_api_key"是你獲取天氣預報信息的API密鑰,該API密鑰可以從心知天氣網(wǎng)主頁進入控制臺頁面,然后點擊左側的"免費版",即可看到自己的私鑰,該私鑰即為API 密鑰,如下圖所示:
接下來我們編寫代碼實現(xiàn)通過http請求從心知天氣網(wǎng)獲取未來3天的天氣信息.
首先在main.c中添加必要的頭文件:
#include < stdio.h > #include < string.h > #include "kernel/os/os.h" #include "net/wlan/wlan.h" #include "net/wlan/wlan_defs.h" #include "common/framework/net_ctrl.h" #include "common/framework/platform_init.h" #include < errno.h > #include "lwip/err.h" #include "lwip/sockets.h" #include "lwip/sys.h" #include "lwip/inet.h" #include "lwip/netdb.h" #include "sys/select.h" #include "cjson/cJSON.h"
然后定義關于天氣信息的結構體類型:
/* 天氣數(shù)據(jù)結構體 */ typedef struct tagWeather { /* 實況天氣數(shù)據(jù) */ char id[32]; //id char name[32]; //地名 char country[32]; //國家 char path[32]; //完整地名路徑 char timezone[32]; //時區(qū) char timezone_offset[32]; //時差 char text[32]; //天氣預報文字 char code[32]; //天氣預報代碼 char temperature[32]; //氣溫 char last_update[32]; //最后一次更新的時間 /* 今天、明天、后天天氣數(shù)據(jù) */ char date[3][32]; //日期 char text_day[3][64]; //白天天氣現(xiàn)象文字 char code_day[3][32]; //白天天氣現(xiàn)象代碼 char code_night[3][64]; //晚間天氣現(xiàn)象代碼 char high[3][32]; //最高溫 char low[3][32]; //最低溫 char wind_direction[3][64]; //風向 char wind_speed[3][32]; //風速,單位km/h(當unit=c時) char wind_scale[3][32]; //風力等級 } Weather_T;
再定義通過http的GET請求方式獲取天氣預報的請求頭部:
#define WEB_SERVER "api.seniverse.com" // 天氣預報網(wǎng)服務器地址 #define WEB_PORT "80" // 天氣預報網(wǎng)服務器端口號 #define CONFIG_API_KEY "xxxxxxxxxxxxxx" // 你的API密鑰,從心知天氣網(wǎng)控制臺頁面獲取 /* 獲取天氣預報信息的http請求頭部 */ #define GET_REQUEST_PACKAGE "GET https://api.seniverse.com/v3/weather/daily.json?key=" CONFIG_API_KEY "&location=%s&language=zh-Hans&unit=crnrn" #define HTTPC_DEMO_THREAD_STACK_SIZE (8 * 1024) /* 任務棧大小 */
定義WiFi的ssid和password:
char *sta_ssid = "xxxxxx"; // 你要連接的WiFi名 char *sta_psk = "xxxxxxxx"; // 你要連接的WiFi密碼 char httpc_response_buf[2048]; //用于保存獲取到的天氣信息的原始數(shù)據(jù) int write_idx = 0; // 寫數(shù)據(jù)到httpc_response_buf的數(shù)組下標 static OS_Thread_t httpc_demo_thread; // 獲取天氣的線程ID
編寫WiFi聯(lián)網(wǎng)初始化函數(shù):
void sta_start(void) { /* switch to sta mode */ net_switch_mode(WLAN_MODE_STA); #if STA_MODE_USE_WPA2_ONLY /* set ssid and password to wlan, only use WPA2 mode to connect AP. */ wlan_sta_config((uint8_t *)sta_ssid, strlen(sta_ssid), (uint8_t *)sta_psk, 0); #else /* set ssid and password to wlan, use WPA2|WPA3 compatible mode to connect AP. */ wlan_sta_set((uint8_t *)sta_ssid, strlen(sta_ssid), (uint8_t *)sta_psk); #endif /* start scan and connect to ap automatically */ wlan_sta_enable(); }
在mian函數(shù)中添加如下代碼
int main(void) { observer_base *net_ob; platform_init(); /* create an observer to monitor the net work state */ net_ob = sys_callback_observer_create(CTRL_MSG_TYPE_NETWORK, NET_CTRL_MSG_ALL, net_cb, NULL); if (net_ob == NULL) { return -1; } if (sys_ctrl_attach(net_ob) != 0) { return -1; } sta_start(); return 0; }
其中,函數(shù)sys_callback_observer_create創(chuàng)建一個事件監(jiān)聽器,當函數(shù)第1個參數(shù)CTRL_MSG_TYPE_NETWORK和第2個參數(shù)NET_CTRL_MSG_ALL所指定的事件發(fā)生時自動調用回調函數(shù)net_cb,此處表示所有的網(wǎng)絡事件發(fā)生時均會調用回調函數(shù)net_cb,net_cb定義如下:
static void net_cb(uint32_t event, uint32_t data, void *arg) { uint16_t type = EVENT_SUBTYPE(event); switch (type) { case NET_CTRL_MSG_NETWORK_UP: // WiFi sta連接AP成功并自動分配了ip地址 { /* 打印本機的IP地址,網(wǎng)關,子網(wǎng)掩碼 */ struct netif *nif = wlan_netif_get(WLAN_MODE_STA); while (!NET_IS_IP4_VALID(nif)) { OS_MSleep(100); } printf("local ip: %sn", ipaddr_ntoa(&nif- >ip_addr)); printf("gw: %sn", ipaddr_ntoa(&nif- >gw)); printf("netmask: %sn", ipaddr_ntoa(&nif- >netmask)); } /*創(chuàng)建線程,通過http請求獲取天氣預報信息*/ if (!OS_ThreadIsValid(&httpc_demo_thread)) { OS_ThreadCreate(&httpc_demo_thread, "httpc_demo_thread", httpc_demo_fun, (void *)NULL, OS_THREAD_PRIO_APP, HTTPC_DEMO_THREAD_STACK_SIZE); } break; case NET_CTRL_MSG_NETWORK_DOWN: //WiFi連接斷開事件 break; default: break; } }
httpc_demo_fun函數(shù)的定義如下:
static void httpc_demo_fun(void *arg) { http_get_weather("beijing"); // 獲取天氣預報信息結束后,刪除本線程 OS_ThreadDelete(&httpc_demo_thread); }
其中http_get_weather函數(shù)的參數(shù)即為想要獲取天氣預報的城市的漢語拼音名,定義如下:
static void http_get_weather(char *city) { int32_t ret; char request_head[sizeof(REQUEST) + 64]; const struct addrinfo hints = { .ai_family = AF_INET, .ai_socktype = SOCK_STREAM, }; struct addrinfo *res; struct in_addr *addr; int s, r; Weather_T weather_data = {0}; bzero(httpc_response_buf, sizeof(httpc_response_buf)); /* 通過服務器域名和端口獲取服務器的IP地址相關信息 */ ret = getaddrinfo(WEB_SERVER, WEB_PORT, &hints, &res); if (ret != 0 || res == NULL) { printf("DNS lookup failed ret=%d res=%pn", ret, res); return; } // Note: inet_ntoa is non-reentrant, look at ipaddr_ntoa_r for "real" code */ addr = &((struct sockaddr_in *)res- >ai_addr)- >sin_addr; printf("DNS lookup succeeded. IP=%sn", inet_ntoa(*addr)); /* 創(chuàng)建soocket */ s = socket(res- >ai_family, res- >ai_socktype, 0); if(s < 0) { printf("... Failed to allocate socket.n"); freeaddrinfo(res); return; } printf("... allocated socketn"); /* 使用第1步獲取的服務器IP地址連接服務器 */ if(connect(s, res- >ai_addr, res- >ai_addrlen) != 0) { printf("... socket connect failed errno=%dn", errno); close(s); freeaddrinfo(res); return; } printf("... connectedn"); freeaddrinfo(res); /* 使用城市名生成完整的http的GET請求頭部并發(fā)送到服務器*/ snprintf(request_head, sizeof(request_head), GET_REQUEST_PACKAGE, city); if (write(s, request_head, strlen(request_head)) < 0) { printf("... socket send failedn"); close(s); return; } printf("... socket send successn"); /* 讀取服務器返回的應答數(shù)據(jù) */ /* Read HTTP response */ do { r = read(s, &httpc_response_buf[write_idx], sizeof(httpc_response_buf) - write_idx -1); if (r > 0) { write_idx += r; } } while(r > 0 && write_idx < (sizeof(httpc_response_buf) - 1)); printf("... done reading from socket. Last read return=%d write_idx=%u errno=%d.n", r, write_idx, errno); /* 打印服務器返回的數(shù)據(jù) */ for (int i = 0; i < write_idx; ++i) { putchar(httpc_response_buf[i]); } puts(""); /* 解析天氣預報數(shù)據(jù) */ ret = cJSON_DailyWeatherParse(httpc_response_buf, &weather_data); if (ret == 0) { /* 格式化打印天氣預報信息 */ DisplayWeather(&weather_data); } close(s); }
由于服務器返回的天氣預報數(shù)據(jù)為json字符串, 我們編寫函數(shù)cJSON_DailyWeatherParse解析天氣預報json數(shù)據(jù)并保存到結構體變量weather_data中,cJSON_DailyWeatherParse函數(shù)和DisplayWeather函數(shù)的定義如下:
static int cJSON_DailyWeatherParse(char *JSON, Weather_T *result) { cJSON *json,*arrayItem,*object,*subobject,*item,*sub_child_object,*child_Item; json = cJSON_Parse(JSON); //解析JSON數(shù)據(jù)包 if(json == NULL) //檢測JSON數(shù)據(jù)包是否存在語法上的錯誤,返回NULL表示數(shù)據(jù)包無效 { printf("Error before: [%s]n",cJSON_GetErrorPtr()); //打印數(shù)據(jù)包語法錯誤的位置 return 1; } else { if ((arrayItem = cJSON_GetObjectItem(json,"results")) != NULL) //匹配字符串"results",獲取數(shù)組內容 { // int size = cJSON_GetArraySize(arrayItem); //獲取數(shù)組中對象個數(shù) #if DEBUG printf("Get Array Size: size=%dn",size); #endif if((object = cJSON_GetArrayItem(arrayItem,0)) != NULL)//獲取父對象內容 { /* 匹配子對象1------結構體location */ if((subobject = cJSON_GetObjectItem(object,"location")) != NULL) { if((item = cJSON_GetObjectItem(subobject,"name")) != NULL) //匹配子對象1成員"name" { memcpy(result- >name, item- >valuestring,strlen(item- >valuestring)); // 保存數(shù)據(jù)供外部調用 } } /* 匹配子對象2------數(shù)組daily */ if((subobject = cJSON_GetObjectItem(object,"daily")) != NULL) { int sub_array_size = cJSON_GetArraySize(subobject); #if DEBUG printf("Get Sub Array Size: sub_array_size=%dn",sub_array_size); #endif for(int i = 0; i < sub_array_size; i++) { if((sub_child_object = cJSON_GetArrayItem(subobject,i))!=NULL) { // 匹配日期 if((child_Item = cJSON_GetObjectItem(sub_child_object,"date")) != NULL) { memcpy(result- >date[i], child_Item- >valuestring,strlen(child_Item- >valuestring)); // 保存數(shù)據(jù) } // 匹配白天天氣現(xiàn)象文字 if((child_Item = cJSON_GetObjectItem(sub_child_object,"text_day")) != NULL) { memcpy(result- >text_day[i], child_Item- >valuestring,strlen(child_Item- >valuestring)); // 保存數(shù)據(jù) } // 匹配白天天氣現(xiàn)象代碼 if((child_Item = cJSON_GetObjectItem(sub_child_object,"code_day")) != NULL) { memcpy(result- >code_day[i], child_Item- >valuestring,strlen(child_Item- >valuestring)); // 保存數(shù)據(jù) } // 匹配夜間天氣現(xiàn)象代碼 if((child_Item = cJSON_GetObjectItem(sub_child_object,"code_night")) != NULL) { memcpy(result- >code_night[i], child_Item- >valuestring,strlen(child_Item- >valuestring)); // 保存數(shù)據(jù) } // 匹配最高溫度 if((child_Item = cJSON_GetObjectItem(sub_child_object,"high")) != NULL) { memcpy(result- >high[i], child_Item- >valuestring,strlen(child_Item- >valuestring)); //保存數(shù)據(jù) } // 匹配最低溫度 if((child_Item = cJSON_GetObjectItem(sub_child_object,"low")) != NULL) { memcpy(result- >low[i], child_Item- >valuestring,strlen(child_Item- >valuestring)); // 保存數(shù)據(jù) } // 匹配風向 if((child_Item = cJSON_GetObjectItem(sub_child_object,"wind_direction")) != NULL) { memcpy(result- >wind_direction[i],child_Item- >valuestring,strlen(child_Item- >valuestring)); //保存數(shù)據(jù) } // 匹配風速,單位km/h(當unit=c時) if((child_Item = cJSON_GetObjectItem(sub_child_object,"wind_speed")) != NULL) { memcpy(result- >wind_speed[i], child_Item- >valuestring,strlen(child_Item- >valuestring)); // 保存數(shù)據(jù) } // 匹配風力等級 if((child_Item = cJSON_GetObjectItem(sub_child_object,"wind_scale")) != NULL) { memcpy(result- >wind_scale[i], child_Item- >valuestring,strlen(child_Item- >valuestring)); // 保存數(shù)據(jù) } } } } /* 匹配子對象3------最后一次更新的時間 */ if((subobject = cJSON_GetObjectItem(object,"last_update")) != NULL) { //printf("%s:%sn",subobject- >string,subobject- >valuestring); } } } } cJSON_Delete(json); //釋放cJSON_Parse()分配出來的內存空間 return 0; } /******************************************************************************************************* ** 函數(shù): DisplayWeather,顯示天氣數(shù)據(jù) **------------------------------------------------------------------------------------------------------ ** 參數(shù): weather_data:天氣數(shù)據(jù) ** 返回: void ********************************************************************************************************/ static void DisplayWeather(Weather_T *weather_data) { printf("===========%s近三天的天氣情況如下===========n",weather_data- >name); printf("【%s】n",weather_data- >date[0]); printf("天氣:%sn",weather_data- >text_day[0]); printf("最高溫:%s℃n",weather_data- >high[0]); printf("最低溫:%s℃n",weather_data- >low[0]); printf("風向:%sn",weather_data- >wind_direction[0]); printf("風速:%skm/hn",weather_data- >wind_speed[0]); printf("風力等級:%sn",weather_data- >wind_scale[0]); printf("n"); printf("【%s】n",weather_data- >date[1]); printf("天氣:%sn",weather_data- >text_day[1]); printf("最高溫:%s℃n",weather_data- >high[1]); printf("最低溫:%s℃n",weather_data- >low[1]); printf("風向:%sn",weather_data- >wind_direction[1]); printf("風速:%skm/hn",weather_data- >wind_speed[1]); printf("風力等級:%sn",weather_data- >wind_scale[1]); printf("n"); printf("【%s】n",weather_data- >date[2]); printf("天氣:%sn",weather_data- >text_day[2]); printf("最高溫:%s℃n",weather_data- >high[2]); printf("最低溫:%s℃n",weather_data- >low[2]); printf("風向:%sn",weather_data- >wind_direction[2]); printf("風速:%skm/hn",weather_data- >wind_speed[2]); printf("風力等級:%sn",weather_data- >wind_scale[2]); }
編譯工程
bear make build -j 8
燒錄鏡像到xr806
首先使用USB線將開發(fā)板連上電腦,可能需要重新安裝CP2102驅動,在Windows中打開powershell,輸入如下命令:PS C:Users30751Desktop > usbipd wsl list BUSID VID:PID DEVICE STATE 2-1 10c4:ea60 Silicon Labs CP210x USB to UART Bridge (COM3) Not attached 2-3 248a:8367 USB 輸入設備 Not attached 2-6 0c45:6a1b Integrated Webcam Not attached 2-10 8087:0026 英特爾(R) 無線 Bluetooth(R) Not attached
我們可以看到開發(fā)板連接的USB端口的的BUSID為2-1,接著使用如下命令將該USB串口共享給WSL:
PS C:Users30751Desktop > usbipd wsl attach --busid 2-1 usbipd: info: Using default WSL distribution 'Ubuntu-20.04'; specify the '--distribution' option to select a different one.
接下來我們在Ubuntu終端中進入工程源碼目錄下的tools目錄下,開啟固件燒錄USB串口的讀寫權限:
sudo chmod 666 /dev/ttyUSB0
使用如下命令燒錄鏡像到xr806./phoenixMC
燒錄成功信息如下:我們再次在Windows中打開powershell,輸入如下命令將開發(fā)板連接到windows:
usbipd wsl detach --busid 2-1
我們在windows中打開終端軟件Tera Term,連上開發(fā)板串口,復位開發(fā)板將會看到如下信息:use default flash chip mJedec 0x0 [FD I]: mode: 0x10, freq: 96000000Hz, drv: 0 [FD I]: jedec: 0x0, suspend_support: 1 mode select:e wlan information =================================================== firmware: version : R0-XR_C07.08.52.67_ULP_R_02.132 Jan 10 2023 19:14:11-Y02.132 buffer : 8 driver: version : XR_V02.06.10 mac address: in use : 8c:6d:08:3d:14:01 in use : 8c:6d:08:3d:14:02 ==================================================================== wlan mode:a platform information =============================================== XR806 SDK v1.2.2 Oct 21 2023 23:46:57 62800400 heap space [0x217098, 0x24bc00), size 215912 cpu clock 160000000 Hz HF clock 40000000 Hz sdk option: XIP : enable INT LF OSC : enable INT LDO : select INT LDO / EXT PWR: enable SIP flash : enable mac address: efuse : 80:74:84:05:b9:ca in use : 8c:6d:08:3d:14:01 ==================================================================== [net INF] no need to switch wlan mode 0 [net INF] msg < wlan scan success > en1: Trying to associate with 8c:ab:8e:fd:c3:58 (SSID='302' freq=2412 MHz) en1: Associated with 8c:ab:8e:fd:c3:58 en1: WPA: Key negotiation completed with 8c:ab:8e:fd:c3:58 [PTK=CCMP GTK=TKIP] en1: CTRL-EVENT-CONNECTED - Connection to 8c:ab:8e:fd:c3:58 completed [id=0 id_str=] [net INF] msg < wlan connected > [net INF] netif is link up [net INF] start DHCP... WAR drop=1135, fctl=0x00d0. [net INF] netif (IPv4) is up [net INF] address: 192.168.2.107 [net INF] gateway: 192.168.2.1 [net INF] netmask: 255.255.255.0 [net INF] msg < network up > local ip: 192.168.2.107 gw: 192.168.2.1 netmask: 255.255.255.0 DNS lookup succeeded. IP=116.62.81.138 ... allocated socket ... connected ... socket send success ... done reading from socket. Last read return=0 write_idx=995 errno=107. {"results":[{"location":{"id":"WX4FBXXFKE4F","name":"北京","country":"CN","path":"北京,北京,中國","timezone":"Asia/Shanghai","timezone_offset":"+08:00"},"daily":[{"date":"2023-10-22","text_day":"晴","code_day":"0","text_night":"晴","code_night":"1","high":"21","low":"6","rainfall":"0.00","precip":"0.00","wind_direction":"無持續(xù)風向","wind_direction_degree":"","wind_speed":"8.4","wind_scale":"2","humidity":"61"},{"date":"2023-10-23","text_day":"晴","code_day":"0","text_night":"晴","code_night":"1","high":"21","low":"7","rainfall":"0.00","precip":"0.00","wind_direction":"無持續(xù)風向","wind_direction_degree":"","wind_speed":"3.0","wind_scale":"1","humidity":"75"},{"date":"2023-10-24","text_day":"晴","code_day":"0","text_night":"晴","code_night":"1","high":"23","low":"9","rainfall":"0.00","precip":"0.00","wind_direction":"無持續(xù)風向","wind_direction_degree":"","wind_speed":"3.0","wind_scale":"1","humidity":"75"}],"last_update":"2023-10-20T08:00:00+08:00"}]} ===========北京近三天的天氣情況如下=========== 【2023-10-22】 天氣:晴 最高溫:21℃ 最低溫:6℃ 風向:無持續(xù)風向 風速:8.4km/h 風力等級:2 【2023-10-23】 天氣:晴 最高溫:21℃ 最低溫:7℃ 風向:無持續(xù)風向 風速:3.0km/h 風力等級:1 【2023-10-24】 天氣:晴 最高溫:23℃ 最低溫:9℃ 風向:無持續(xù)風向 風速:3.0km/h 風力等級:1
可以看到我們已經(jīng)成功獲取了北京的未來三天的天氣情況.
4. 總結
通過本次開發(fā)板評測,掌握了XR806的WiFi相關API的使用,系統(tǒng)事件監(jiān)聽API的使用掌,握了sokect網(wǎng)絡編程相關知識,掌握了cJSON的使用.
-
回調函數(shù)
+關注
關注
0文章
87瀏覽量
11622 -
CP2102
+關注
關注
2文章
20瀏覽量
12471 -
Ubuntu系統(tǒng)
+關注
關注
0文章
91瀏覽量
4034 -
gcc編譯器
+關注
關注
0文章
78瀏覽量
3426 -
vscode
+關注
關注
1文章
157瀏覽量
7853
發(fā)布評論請先 登錄
相關推薦
評論