
??想了解更多關(guān)于開(kāi)源的內(nèi)容,請(qǐng)?jiān)L問(wèn):??
??51CTO 開(kāi)源基礎(chǔ)軟件社區(qū)??
??https://ost.51cto.com??
概述
ANY功能是一種華為私有的短數(shù)據(jù)通信功能,允許處于同一信道的2個(gè)Wi-Fi設(shè)備進(jìn)行直接的點(diǎn)對(duì)點(diǎn)無(wú)連接通信。
ANY可以應(yīng)用于智能開(kāi)關(guān)控制燈泡、傳感器數(shù)據(jù)采集、遙控器控制家用電器等無(wú)線控制場(chǎng)景。
ANY功能特點(diǎn)
- 每個(gè)設(shè)備可以選擇一個(gè)接口(例如:wlan0或ap0)用于ANY報(bào)文的收發(fā)。
- ANY報(bào)文采用接口當(dāng)前所在信道進(jìn)行收發(fā),和通信對(duì)端需要處于同一信道。
- 單個(gè)ANY報(bào)文最多可以支持250byte的用戶層數(shù)據(jù)。
- 單個(gè)ANY設(shè)備最多支持同時(shí)和16個(gè)ANY對(duì)端設(shè)備進(jìn)行通信,其中最多允許和6個(gè)對(duì)端進(jìn)行加密通信。
- ANY設(shè)備可以收發(fā)ANY單播報(bào)文和ANY廣播報(bào)文,不支持組播報(bào)文。
- ANY設(shè)備可以掃描發(fā)現(xiàn)附近的其他ANY設(shè)備。
環(huán)境
- OpenHarmony-3.1
- 潤(rùn)和hispark_pegasus Hi3861開(kāi)發(fā)板
- DevEco Device Tool
- 串口調(diào)試助手
總體流程
總體流程為:
- ANY功能初始化。
- 注冊(cè)收發(fā)回調(diào)函數(shù)。
- 創(chuàng)建收發(fā)線程T1處理收發(fā)回調(diào)(將收發(fā)回調(diào)函數(shù)用此線程完成,避免長(zhǎng)期占用主線程)。
- 進(jìn)行ANY設(shè)備掃描。
- 調(diào)用hi_wifi_any_deinit,去初始化ANY。
1、初始化
首先設(shè)備需要完成STA或者SoftAP的初始化;再以此作為ANY收發(fā)通信的接口,初始化ANY功能。初始化ANY功能之后,ANY會(huì)采用其所在的信道進(jìn)行通信,并采用MAC地址作為發(fā)送接收的地址。
- 對(duì)于SoftAP模式下,所在的信道就是創(chuàng)建的時(shí)候指定的信道。
- 對(duì)于STA模式,若已連接到了SoftAP,所在信道為該SoftAP指定的信道。
- 對(duì)于STA模式,若未連接,可以采用設(shè)置信道的API函數(shù)hi_wifi_set_channel給STA指定一個(gè)信道。
對(duì)于ANY功能初始化,步驟為:?jiǎn)?dòng)STA或SoftAP調(diào)用?hi_wifi_any_init,選擇STA接口(wlan0)或AP接口(ap0)作為ANY通信接口。
調(diào)用hi_wifi_any_deinit,去初始化ANY(關(guān)閉WiFi會(huì)自動(dòng)關(guān)閉ANY)。
對(duì)于初始化函數(shù),返回值為0即執(zhí)行成功,1則為執(zhí)行失敗。
/* STA接口的信息 */
hi_char ifname[16 + 1] = {0};
hi_s32 len = sizeof(ifname);
/*WIFI_STA功能啟動(dòng)*/
hi_wifi_sta_start(ifname, &len);
/* 初始化并注冊(cè)發(fā)送完成和接收回調(diào)函數(shù) */
hi_wifi_any_init(ifname);
/* AP接口的信息 */
hi_wifi_softap_config hapd_conf = {
"my_wifi", "", 1, 0, HI_WIFI_SECURITY_OPEN, HI_WIFI_PARIWISE_UNKNOWN};
hi_char ifname[16 + 1] = {0}; /* 創(chuàng)建的SoftAP接口名 */
hi_s32 len = sizeof(ifname); /* SoftAP接口名長(zhǎng)度 */
struct netif *netif_p = HI_NULL;
ip4_addr_t st_gw;
ip4_addr_t st_ipaddr;
ip4_addr_t st_netmask;
/* 這里用戶配置自己的網(wǎng)關(guān)、IP、掩碼 */
IP4_ADDR(&st_gw, 0, 0, 0, 0);
IP4_ADDR(&st_ipaddr, 0, 0, 0, 0);
IP4_ADDR(&st_netmask, 0, 0, 0, 0);
hi_u8 ap_msg[] = "msg from my_wifi";
/* 配置SoftAP網(wǎng)絡(luò)參數(shù),beacon周期修改為200ms */
hi_wifi_softap_set_beacon_period(200);
/* 啟動(dòng)SoftAP接口 */
hi_wifi_softap_start(&hapd_conf, ifname, &len);
/* 配置DHCP服務(wù)器 */
netif_p = netif_find(ifname);
if (netif_p == HI_NULL)
{
(hi_void) hi_wifi_softap_stop();
return HISI_FAIL;
}
if (netifapi_netif_set_addr(netif_p, &st_ipaddr, &st_netmask, &st_gw) != HISI_OK)
{
(hi_void) hi_wifi_softap_stop();
return HISI_FAIL;
}
if (netifapi_dhcps_start(netif_p, NULL, 0) != HISI_OK)
{
(hi_void) hi_wifi_softap_stop();
return HISI_FAIL;
}
/* 初始化并注冊(cè)發(fā)送完成和接收回調(diào)函數(shù) */
hi_wifi_any_init(ifname);
2、注冊(cè)ANY收發(fā)回調(diào)函數(shù)
ANY設(shè)備能夠與同信道的其他ANY設(shè)備進(jìn)行通信。其中單個(gè)ANY報(bào)文最多支持250Byte的用戶數(shù)據(jù)。支持加密通信或不加密通信,加密通信需要要求雙方提前配置同樣的16Byte長(zhǎng)度密鑰。
在通信前需要確保:ANY功能初始化,以及通信雙方處于同一個(gè)信道。
接受和發(fā)送回調(diào)函數(shù)同樣是運(yùn)行于驅(qū)動(dòng)線程,建議新建一個(gè)線程T1(第三步驟)處理收發(fā)報(bào)文,所以回調(diào)函數(shù)內(nèi)的任務(wù)只是將msg數(shù)據(jù)通過(guò)消息隊(duì)列傳到收發(fā)線程T1。
- 實(shí)現(xiàn)接收回調(diào)函數(shù)&& 發(fā)送完成回調(diào)函數(shù),hi_wifi_any_recv_cb? && hi_wifi_any_send_complete_cb。
- 回調(diào)函數(shù)內(nèi)調(diào)用write_any_msg函數(shù),msg數(shù)據(jù)將會(huì)在收發(fā)線程T1里面處理。
- 調(diào)用hi_wifi_any_set_callback向驅(qū)動(dòng)注?冊(cè)回調(diào)函數(shù),其中發(fā)送回調(diào)函數(shù)反饋發(fā)送結(jié)果(成功為1),若不需要該函數(shù),注冊(cè)時(shí)可以設(shè)置為HI_NULL。
ANY只在當(dāng)前信道通信,想要與其他信道設(shè)備通信,需要下調(diào)用切信道API
一個(gè)ANY設(shè)備支持和16個(gè)對(duì)端通信,最多6個(gè)對(duì)端加密通信,并且需要雙方配置相同密鑰。
接收回調(diào)函數(shù)的數(shù)據(jù)內(nèi)存由驅(qū)動(dòng)自行管理。
hi_wifi_any_set_callback(wifi_any_send_cb, wifi_any_recv_cb);
/*發(fā)送完成回調(diào)函數(shù)*/
void wifi_any_send_cb(unsigned char *mac, unsigned char status, unsigned char seqnum)
{
any_msg_stru msg = {0};
memcpy_s(msg.mac, sizeof(msg.mac), mac, sizeof(msg.mac));
msg.type = ANY_SEND_COMPLETE_CALLBACK;
msg.status = status;
msg.seqnum = seqnum;
//調(diào)用write_any_msg函數(shù)
write_any_msg(&msg);
}
/* 接收回調(diào)函數(shù)*/
void wifi_any_recv_cb(unsigned char *mac, unsigned char *data, unsigned short len, unsigned char seqnum)
{
any_msg_stru msg = {0};
msg.type = ANY_RECV_CALLBACK;
memcpy_s(msg.mac, sizeof(msg.mac), mac, sizeof(msg.mac));
msg.len = len;
msg.seqnum = seqnum;
msg.data = (unsigned char *)hi_malloc(HI_MOD_ID_WIFI_DRV, len);
if (msg.data == NULL)
return;
if (memcpy_s(msg.data, len, data, len) != EOK)
{
hi_free(HI_MOD_ID_WIFI_DRV, msg.data);
return;
}
//調(diào)用write_any_msg函數(shù)
if (write_any_msg(&msg) != HI_ERR_SUCCESS)
hi_free(HI_MOD_ID_WIFI_DRV, msg.data);
}
/* 向收發(fā)線程T1發(fā)送msg */
hi_u32 write_any_msg(any_msg_stru *msg)
{
hi_u32 ret;
if ((g_any_msg_queue == 0) || (g_task_running == 0))
{
printf("msg queue or task is not working!\r\n");
return HI_ERR_FAILURE;
}
//將msg消息發(fā)送到消息隊(duì)列
ret = hi_msg_queue_send(g_any_msg_queue, msg, 0, sizeof(any_msg_stru));
return ret;
}
3、處理子線程T1
由于接收回調(diào)、發(fā)送回調(diào)、掃描回調(diào)都不易過(guò)多占用主線程的運(yùn)行,所以我們需要?jiǎng)?chuàng)建一個(gè)新的線程用于實(shí)現(xiàn)接收回調(diào)、發(fā)送回調(diào)、掃描回調(diào)的活。
創(chuàng)建線程&&線程實(shí)現(xiàn):
/*創(chuàng)建處理子線程T1*/
hi_u32 any_start_callback_task(hi_void)
{
hi_u32 ret;
if (g_any_msg_task_id != 0)
{
return HI_ERR_FAILURE;
}
hi_task_attr task_init = {0};
task_init.task_prio = ANY_TASK_PRIORITY;
task_init.stack_size = ANY_TASK_STACK_SIZE;
task_init.task_name = ANY_TASK_NAME;
g_task_running = 1;
ret = hi_task_create(&g_any_msg_task_id, &task_init, handle_any_msg, HI_NULL);
if (ret != HI_ERR_SUCCESS)
{
printf("create any msg task ret:%d\r\n", ret);
return ret;
}
printf("create any msg task success!\r\n");
return ret;
}
hi_u32 any_destory_callback_task(hi_void)
{
printf("destory any callback task\r\n");
g_task_running = 0;
return HI_ERR_FAILURE;
}
/*處理子線程T1*/
static hi_void *handle_any_msg(hi_void *data)
{
(hi_void) data;
int ret;
any_msg_stru msg = {0};
if (g_any_msg_queue == 0)
{
ret = hi_msg_queue_create(&g_any_msg_queue, ANY_MSG_QUEUE_MAX_LEN, sizeof(any_msg_stru));
if (ret != HI_ERR_SUCCESS)
{
printf("create any message queue ret:%d\r\n", ret);
g_any_msg_task_id = 0;
return HI_NULL;
}
}
while (1)
{
hi_u32 msg_size = sizeof(any_msg_stru);
ret = hi_msg_queue_wait(g_any_msg_queue, (hi_pvoid)&msg, ANY_TASK_SLEEP_TIME,&msg_size);
if (ret == HI_ERR_SUCCESS)
{
switch (msg.type)
{
case ANY_SCAN_CALLBACK:
/*掃描回調(diào)*/
wifi_any_scan_success_proc(msg.mac, msg.channel);
break;
case ANY_RECV_CALLBACK:
/*接收回調(diào)*/
wifi_any_recv_proc(msg.mac, msg.data, msg.len, msg.seqnum);
hi_free(HI_MOD_ID_WIFI_DRV, msg.data);
break;
case ANY_SEND_COMPLETE_CALLBACK:
/*發(fā)送完成回調(diào)*/
wifi_any_send_complete_proc(msg.mac, msg.status, msg.seqnum);
break;
default:
break;
}
}
else
{
if (g_task_running == 0)
{
break;
}
}
}
g_any_msg_task_id = 0;
hi_u8 trycount = 3;
while (trycount > 0)
{
if (hi_msg_queue_delete(g_any_msg_queue) == HI_ERR_SUCCESS)
{
g_any_msg_queue = 0;
return HI_NULL;
}
trycount--;
}
printf("delete any msg queue failed!\r\n");
return HI_NULL;
}
真正的接收回調(diào)、發(fā)送回調(diào)、掃描回調(diào)(可參考文章末尾的源碼):
/* 接收回調(diào) */
void wifi_any_recv_proc(const unsigned char *mac, unsigned char *data, unsigned short len,unsigned char seqnum)
{}
/* 發(fā)送完成回調(diào) */
void wifi_any_send_complete_proc(const unsigned char *mac, unsigned char status, unsigned char seqnum)
{}
/* 掃描回調(diào) */
void wifi_any_scan_success_proc(const unsigned char *mac, unsigned char channel)
{}
4、ANY設(shè)備掃描
完成ANY初始化后,STA端可以進(jìn)行設(shè)備的掃描:
- 首先實(shí)現(xiàn)hi_wifi_any_scan_result_cb掃描回調(diào)函數(shù),用于處理掃描完成之后的結(jié)果。對(duì)于該回調(diào)函數(shù),驅(qū)動(dòng)傳入的輸入?yún)?shù)為發(fā)現(xiàn)的ANY設(shè)備的指針數(shù)組和數(shù)組元素個(gè)數(shù),每個(gè)元素指向一個(gè)發(fā)現(xiàn)的ANY設(shè)備相關(guān)信息。
- 調(diào)用hi_wifi_any_discover_peer,向驅(qū)動(dòng)注冊(cè)回調(diào)函數(shù),并**啟動(dòng)一次ANY掃描。
掃描過(guò)程中,發(fā)現(xiàn)的設(shè)備是SoftAP,則設(shè)備信息會(huì)包括SSID信息,STA則為空字符串。
回調(diào)函數(shù)運(yùn)行于驅(qū)動(dòng)線程,不能阻塞或長(zhǎng)時(shí)間?等待,使用第三步驟的處理子線程T1進(jìn)行處理。
單次掃描最多通過(guò)回調(diào)函數(shù)返回32個(gè)對(duì)端設(shè)備信息,回調(diào)函數(shù)傳入的數(shù)組內(nèi)存由驅(qū)動(dòng)自行管理,在回調(diào)函數(shù)中不應(yīng)釋放。
//g_find_any_device初始值為0,完成掃描回調(diào)函數(shù)退出whil循環(huán)
while (!g_find_any_device)
{
/* wifi_any_scan_result_cb : 掃描回調(diào)函數(shù) */
hi_wifi_any_discover_peer(wifi_any_scan_result_cb);
hi_sleep(2000); //延時(shí)2s
}
/*掃描回調(diào)函數(shù)*/
void wifi_any_scan_result_cb(hi_wifi_any_device *devices[], unsigned char num)
{
unsigned char target_ssid[] = "my_wifi";
any_msg_stru msg = {0};
unsigned char loop;
if ((devices == NULL) || (num == 0))
{
printf("Total scanned ANY dev num: 0\r\n");
return;
}
for (loop = 0; (loop < num) && (devices[loop] != NULL); loop++)
{
if (strcmp((char *)devices[loop]->ssid, (char *)target_ssid) != 0)
{
continue;
}
g_find_any_device = 1;
msg.type = ANY_SCAN_CALLBACK;
msg.channel = devices[loop]->channel;
memcpy_s(msg.mac, WIFI_ANY_MAC_LEN, devices[loop]->bssid, WIFI_ANY_MAC_LEN);
if (write_any_msg(&msg) != HI_ERR_SUCCESS)
{
printf("write scan result failed\r\n");
}
break;
}
printf("Total scanned ANY dev num: %d\r\n", num);
return;
}
5、加密
ANY設(shè)備支持加密通信,要求通信雙方采用同樣的16byte長(zhǎng)度密鑰進(jìn)行加解密,一個(gè)密鑰對(duì)應(yīng)一個(gè)對(duì)端MAC地址,即采用該密鑰加解密來(lái)自該MAC地址的ANY報(bào)文。用戶應(yīng)根據(jù)產(chǎn)品應(yīng)用場(chǎng)景確定ANY設(shè)備之間如何產(chǎn)生和共享同樣的密鑰。
設(shè)備1和設(shè)備2的配對(duì)協(xié)商消息均是采用ANY報(bào)文。協(xié)商請(qǐng)求消息中攜帶了隨機(jī)數(shù)R1,協(xié)商應(yīng)答消息攜帶了隨機(jī)數(shù)R2和Cookie值。參與協(xié)商的2個(gè)設(shè)備需要采用同樣的預(yù)共享密鑰PSK,設(shè)備獲取到隨機(jī)數(shù)R1和R2之后結(jié)合PSK生成一個(gè)臨時(shí)密鑰用于加密密鑰協(xié)商消息。設(shè)備1采用該臨時(shí)密鑰加密密鑰請(qǐng)求消息,密鑰請(qǐng)求消息中包括來(lái)自設(shè)備2的Cookie和隨機(jī)生成的密鑰因子K1。設(shè)備2采用臨時(shí)密鑰解密消息之后驗(yàn)證Cookie是否正確,驗(yàn)證通過(guò)則回復(fù)密鑰應(yīng)答消息。密鑰應(yīng)答消息中包含隨機(jī)生成密鑰因子K2,并采用臨時(shí)密鑰加密。雙方完成上述交互之后,通過(guò)隨機(jī)數(shù)R1和R2、密鑰因子K1和K2采用同樣的算法生成用于通信的最終的密鑰。?

6、發(fā)送函數(shù)
向指定MAC地址的設(shè)備發(fā)送ANY數(shù)據(jù)。
int hi_wifi_any_send(const unsigned char *mac, unsigned char mac_len, unsigned char *data,unsigned short data_len, unsigned char seq);
- mac:6字節(jié)長(zhǎng)度目的MAC地址, 可為單播或者廣播地址, 不支持組播地址。
- mac_len:MAC地址長(zhǎng)度, 需為6字節(jié)。
- data:待發(fā)送數(shù)據(jù)的緩存地址。
- len:待發(fā)送的數(shù)據(jù)長(zhǎng)度, 最大為250字節(jié)。
- seqnum:待發(fā)送的ANY幀的序列號(hào),范圍0-255。
效果
此樣例連接后互相發(fā)送信息。
這里可以看到AP端的mac地址為0x8e,STA端的mac地址為0x32。
STA端掃描發(fā)現(xiàn)ANY設(shè)備:

互相發(fā)送信息:

文章相關(guān)附件可以點(diǎn)擊下面的原文鏈接前往下載:
https://ost.51cto.com/resource/2570。
??想了解更多關(guān)于開(kāi)源的內(nèi)容,請(qǐng)?jiān)L問(wèn):??
??51CTO 開(kāi)源基礎(chǔ)軟件社區(qū)??
??https://ost.51cto.com??