一、简介
AP(Access Point)无线接入点
AP是无线接入点,是一个无线网络的创建者,是网络的中心节点。一般家庭或办公室使用的无线路由器就是一个AP。
STA(Station)站点
STA也可以理解为终端的意思,每一个连接到无线网络中的终端(例如笔记本电脑,手机等其他可以联网的设备)都可以成为一个STA站点。
SSID(Service Set Identifier)服务集标识符
每个无线AP都应该有一个SSID用于识别,就是通常所说的WIFI名
BSSID(Basic Service Set Identifier)基本服务集标识符
每一个网络设备都有其用于识别的物理地址,这个东西就是MAC地址。一般在出厂后会有一个默认MAC,可更改。MAC地址作为设备识别的标识符。BSSID是针对STA而言。对于STA来说,获取到AP接入点的MAC地址就是BSSID。
例如:某个AP释放一个名为A的WIFI热点,同一区域内另一个AP也释放一个名为A的WIFI热点,当手机连接A热点时,如何辨别连接的是哪个AP呢?此时就要用到BSSID。一般情况下BSSID可以理解为无线路由器的MAC地址,通过查看手机连接WIFI的MAC地址即可知道连接的是哪个AP。
ESSID(Extended Service Set Identifier)扩展服务集标识符
ESSID是一个比较抽象的概念,它实际上和SSID相同,只是如果有好几个AP热点名字相同,此时就相当于把这个SSID扩大了,这几个AP共同的名字就叫ESSID。
例如:某个AP释放一个名为A的WIFI热点,同一区域内另一个AP也释放一个名为A的WIFI热点,那么A就是ESSID。
二、API介绍
RegisterWifiEvent
函数功能:
为指定的WIFI事件注册回调函数。当wifiEvent中定义的wifi事件发生时,将调用已注册的回调函数。
函数原型:
WifiErrorCode RegisterWifiEvent(WifiEvent* event)
参数:
event:
事件类型。其类型为WifiEvent。看该类型的定义。
typedef struct {
/** Connection state change */
void (*OnWifiConnectionChanged)(int state, WifiLinkedInfo* info);
/** Scan state change */
void (*OnWifiScanStateChanged)(int state, int size);
/** Hotspot state change */
void (*OnHotspotStateChanged)(int state);
/** Station connected */
void (*OnHotspotStaJoin)(StationInfo* info);
/** Station disconnected */
void (*OnHotspotStaLeave)(StationInfo* info);
} WifiEvent;
OnWifiConnectionChanged:WIFI连接状态改变回调。针对STA。其参数state表示连接的状态。state>0表示连接成功,0 连接失败(密码错误或连接断开)。info 为更加详细的连接信息。看其对应的结构体。
typedef struct {
/** Service set ID (SSID). For its length, see {@link WIFI_MAX_SSID_LEN}. */
char ssid[WIFI_MAX_SSID_LEN];
/** Basic service set ID (BSSID). For its length, see {@link WIFI_MAC_LEN}. */
unsigned char bssid[WIFI_MAC_LEN];
/** Received signal strength indicator (RSSI) */
int rssi;
/** Wi-Fi connection state, which is defined in {@link WifiConnState} */
WifiConnState connState;
/** Reason for Wi-Fi disconnection */
unsigned short disconnectedReason;
} WifiLinkedInfo;
ssid:连接的SSID。只在连接成功时有用。
bssid:链接的路由器的MAC。只在连接成功时有用。
rssi:信号强度。只在连接成功时有用。
connState:连接状态。0 断开,1 连接。
typedef enum {
/** Disconnected */
WIFI_DISCONNECTED,
/** Connected */
WIFI_CONNECTED
} WifiConnState;
disconnectedReason:断开连接原因。
OnWifiScanStateChanged:扫描结果回调。针对STA。其参数有两个,state和size。state 表示扫描的结果,0 失败,1 成功。size表示扫描到的AP数量。
OnHotspotStateChanged:热点状态改变。针对AP。详细参数介绍见下一章。
OnHotspotStaJoin:有STA链接。针对AP。详细参数介绍见下一章。
OnHotspotStaLeave:有STA断开连接。针对AP。详细参数介绍见下一章。
返回值:
WIFI_SUCCESS:成功
其他值:失败
实例:
static WifiEvent g_wifiEventHandler = {0};
static WifiErrorCode error;
g_wifiEventHandler.OnWifiScanStateChanged = OnWifiScanStateChangedHandler; //扫描状态改变,扫描到了AP
g_wifiEventHandler.OnWifiConnectionChanged = OnWifiConnectionChangedHandler; //WIFI连接状态改变回调
g_wifiEventHandler.OnHotspotStaJoin = OnHotspotStaJoinHandler; //STA连接回调
g_wifiEventHandler.OnHotspotStaLeave = OnHotspotStaLeaveHandler; //STA断开回调
g_wifiEventHandler.OnHotspotStateChanged = OnHotspotStateChangedHandler; //热点状态改变的回调
error = RegisterWifiEvent(&g_wifiEventHandler); //注册事件
EnableWifi
函数功能:
使能WIFI,启用STA模式。
函数原型:
WifiErrorCode EnableWifi(void)
参数:
无
返回值:
WIFI_SUCCESS:成功
其他值:失败
实例:
if (EnableWifi() != WIFI_SUCCESS)
{
LOG_E("EnableWifi failed, error = %d\n", error);
}
IsWifiActive
函数功能:
检查STA模式是否启用
函数原型:
int IsWifiActive(void)
参数:
无
返回值:
#define WIFI_STA_NOT_ACTIVE 0 //STA未启动
#define WIFI_STA_ACTIVE 1 //STA启动
实例:
if (IsWifiActive() == 0)
{}
Scan
函数功能:
开启扫描热点信息。开启后,设备开始扫描设备。如果扫描完成,则会调用RegisterWifiEvent注册的OnWifiScanStateChanged事件。
函数原型:
WifiErrorCode Scan(void)
参数:
无
返回值:
WIFI_SUCCESS:成功
其他值:失败
实例:
Scan();
GetScanInfoList
函数功能:
获取所有扫描到的热点列表
函数原型:
WifiErrorCode GetScanInfoList(WifiScanInfo* result, unsigned int* size)
参数:
result:输出参数。扫描到的热点列表信息。其结构体信息为WifiScanInfo。
typedef struct {
/** Service set ID (SSID). For its length, see {@link WIFI_MAX_SSID_LEN}. */
char ssid[WIFI_MAX_SSID_LEN];
/** Basic service set ID (BSSID). For its length, see {@link WIFI_MAC_LEN}. */
unsigned char bssid[WIFI_MAC_LEN];
/** Security type. For details, see {@link WifiSecurityType}. */
int securityType;
/** Received signal strength indicator (RSSI) */
int rssi;
/** Frequency band */
int band;
/** Frequency in MHz */
int frequency;
} WifiScanInfo;
ssid:WIFI名
bssid:AP的MAC
securityType:加密类型
rssi:信号强度
band:2.4G还是5G
frequency:频率(信道)
size:扫描到的AP个数。
返回值:
WIFI_SUCCESS:成功
其他值:失败
实例:
WifiScanInfo *info = NULL;
unsigned int size = 0;
WifiErrorCode error;
error = GetScanInfoList(info, &size); //获取扫描列表
AddDeviceConfig
函数功能:
配置要连接的AP信息,此函数生成一个networkID
函数原型:
WifiErrorCode AddDeviceConfig(const WifiDeviceConfig* config, int* result)
参数:
config:配置参数,要连接的热点信息。结构体为WifiDeviceConfig。
typedef struct WifiDeviceConfig {
/** Service set ID (SSID). For its length, see {@link WIFI_MAX_SSID_LEN}. */
char ssid[WIFI_MAX_SSID_LEN];
/** Basic service set ID (BSSID). For its length, see {@link WIFI_MAC_LEN}. */
unsigned char bssid[WIFI_MAC_LEN];
/** Key. For its length, see {@link WIFI_MAX_KEY_LEN}. */
char preSharedKey[WIFI_MAX_KEY_LEN];
/** Security type. It is defined in {@link WifiSecurityType}. */
int securityType;
/** Allocated <b>networkId</b> */
int netId;
/** Frequency */
unsigned int freq;
/** PSK type, see {@link WifiPskType}. */
int wapiPskType;
} WifiDeviceConfig;
ssid:AP名。必填
bssid:AP MAC。非必填
preSharedKey:AP密码。必填
securityType:加密类型,必填
typedef enum {
/** Invalid security type */
WIFI_SEC_TYPE_INVALID = -1,
/** Open */
WIFI_SEC_TYPE_OPEN,
/** Wired Equivalent Privacy (WEP) */
WIFI_SEC_TYPE_WEP,
/** Pre-shared key (PSK) */
WIFI_SEC_TYPE_PSK,
/** Simultaneous Authentication of Equals (SAE) */
WIFI_SEC_TYPE_SAE,
} WifiSecurityType;
netId:网络ID,非必填
freq:频率,非必填
wapiPskType:psk 加密类型,非必填
result:输出,生成的networkID。每个网络ID匹配一个热点配置。
返回值:
WIFI_SUCCESS:成功
其他值:失败
实例:
WifiDeviceConfig select_ap_config = {0};
strcpy(select_ap_config.ssid,"harmony_test_ap");
strcpy(select_ap_config.preSharedKey, "123456789");
select_ap_config.securityType = WIFI_SEC_TYPE_PSK; //加密类型
if (AddDeviceConfig(&select_ap_config, &result) == WIFI_SUCCESS) //添加连接对象
{}
ConnectTo
函数功能:
连接到指定AP。连接结果会触发由RegisterWifiEvent注册的OnWifiConnectionChanged回调。
函数原型:
WifiErrorCode ConnectTo(int networkId)
参数:
networkId:网络ID。AddDeviceConfig 返回得到。
返回值:
WIFI_SUCCESS:成功
其他值:失败
实例:
int result;
if (ConnectTo(result) == WIFI_SUCCESS)
{}
netifapi_netif_find
函数功能:
获取网络接口用于IP操作
函数原型:
struct netif *netifapi_netif_find(const char *name);
参数:
name:网络接口名称。STA为“wlan0”,AP为“ap0”。为什么是这俩,还没搞清楚。搞清楚后会回来补充。
返回值:
NULL:失败
其他值:网络接口值。netif:LwIP网络接口
实例:
struct netif *g_lwip_netif = NULL;
g_lwip_netif = netifapi_netif_find("wlan0"); //获取网络接口
dhcp_start
函数功能:
启动DHCP,获取IP
函数原型:
err_t dhcp_start(struct netif *netif)
参数:
netif:网络接口值。netifapi_netif_find 获得。
返回值:
ERR_OK:成功
其他值:失败
实例:
struct netif *g_lwip_netif = NULL;
dhcp_start(g_lwip_netif);
dhcp_is_bound
函数功能:
查询DHCP状态,成功后返回OK。
函数原型:
err_t dhcp_is_bound(struct netif *netif)
参数:
netif:网络接口值。netifapi_netif_find 获得。
返回值:
ERR_OK:成功
其他值:失败
实例:
struct netif *g_lwip_netif = NULL;
if(dhcp_is_bound(g_lwip_netif) == ERR_OK)
{}
netifapi_netif_common
函数功能:
以线程安全的方式调用与netif相关的API
函数原型:
err_t netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc,
netifapi_errt_fn errtfunc)
参数:
netif:网络接口值。netifapi_netif_find 获得。
voidfunc:要调用的API。
errtfunc:失败时的回调。默认为NULL。
返回值:
ERR_OK:成功
其他值:失败
实例:
struct netif *g_lwip_netif = NULL;
netifapi_netif_common(g_lwip_netif, dhcp_clients_info_show, NULL);
dhcp_clients_info_show
函数功能:
打印DHCP客户端的相关信息。
函数原型:
void dhcp_clients_info_show(struct netif *netif_p);
参数:
netif:网络接口值。netifapi_netif_find 获得。
返回值:
无
实例:
netifapi_netif_common(g_lwip_netif, dhcp_clients_info_show, NULL);
四、实例
这里创建一个STA去连接指定的AP,并且打印相关链接的信息。
修改BUILD.gn文件如下:
include_dirs = [
"//utild/native/lite/include",
"//base/iot_hardware/interfaces/kits/wifiiot_lite",
"//utils/native/lite/include",
"//kernel/liteos_m/components/cmsis/2.0",
"//foundation/communication/interfaces/kits/wifi_lite/wifiservice",
"//vendor/hisi/hi3861/hi3861/third_party/lwip_sack/include/",
"src",
]
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "lwip/netif.h"
#include "lwip/netifapi.h"
#include "lwip/ip4_addr.h"
#include "lwip/api_shell.h"
#include "cmsis_os2.h"
#include "hos_types.h"
#include "wifi_device.h"
#include "wifiiot_errno.h"
#include "ohos_init.h"
#define LOG_I(fmt, args...) printf("<%8ld> - [STA]:"fmt"\r\n",osKernelGetTickCount(),##args);
#define LOG_E(fmt, args...) printf("<%8ld>-[STA_ERR]>>>>>>>>>>>>:"fmt"\r\n",osKernelGetTickCount(), ##args);
#define DEF_TIMEOUT 15
#define ONE_SECOND 1
static void WiFiInit(void);
static void WaitSacnResult(void);
static int WaitConnectResult(void);
static void OnWifiScanStateChangedHandler(int state, int size);
static void OnWifiConnectionChangedHandler(int state, WifiLinkedInfo *info);
static int g_staScanSuccess = 0;
static int g_ConnectSuccess = 0;
static int ssid_count = 0;
static WifiEvent g_wifiEventHandler = {0};
static WifiErrorCode error;
#define SELECT_WLAN_PORT "wlan0"
#define SELECT_WIFI_SSID "harmony_test_ap"
#define SELECT_WIFI_PASSWORD "123456789"
#define SELECT_WIFI_SECURITYTYPE WIFI_SEC_TYPE_PSK
/*扫描结果回调*/
static void OnWifiScanStateChangedHandler(int state, int size)
{
(void)state;
LOG_I("OnWifiScanStateChangedHandler,state:%d,size:%d",state,size);
if (size > 0)
{
ssid_count = size;
g_staScanSuccess = 1;
}
return;
}
/*WIFI连接状态改变*/
static void OnWifiConnectionChangedHandler(int state, WifiLinkedInfo *info)
{
(void)info;
LOG_I("bssid:%02x:%02x:%02x:%02x:%02x:%02x",info->bssid[0],info->bssid[1],info->bssid[2],info->bssid[3],info->bssid[4],info->bssid[5]);
LOG_I("OnWifiConnectionChangedHandler.state:%d,ssid:%s,rssi:%d,connstate:%d,disconnReason:%d",state,info->ssid,info->rssi,info->connState,info->disconnectedReason);
if (state > 0)
{
g_ConnectSuccess = 1;
LOG_I("callback function for wifi connect\r\n");
}
else
{
LOG_I("connect error,please check password\r\n");
}
return;
}
/*等待扫描结果,在扫描结果回调中置标志*/
static void WaitSacnResult(void)
{
int scanTimeout = DEF_TIMEOUT;
while (scanTimeout > 0)
{
sleep(ONE_SECOND);
scanTimeout--;
if (g_staScanSuccess == 1)
{
LOG_I("WaitSacnResult:wait success[%d]s\n", (DEF_TIMEOUT - scanTimeout));
break;
}
}
if (scanTimeout <= 0)
{
LOG_I("WaitSacnResult:timeout!\n");
}
}
/*等待连接结果*/
static int WaitConnectResult(void)
{
int ConnectTimeout = DEF_TIMEOUT;
while (ConnectTimeout > 0)
{
sleep(1);
ConnectTimeout--;
if (g_ConnectSuccess == 1)
{
LOG_I("WaitConnectResult:wait success[%d]s\n", (DEF_TIMEOUT - ConnectTimeout));
break;
}
}
if (ConnectTimeout <= 0)
{
LOG_I("WaitConnectResult:timeout!\n");
return 0;
}
return 1;
}
/*WIFI初始化*/
static void WiFiInit(void)
{
LOG_I("<--Wifi Init-->\r\n");
g_wifiEventHandler.OnWifiScanStateChanged = OnWifiScanStateChangedHandler; //扫描状态改变,扫描到了AP
g_wifiEventHandler.OnWifiConnectionChanged = OnWifiConnectionChangedHandler; //WIFI连接状态改变回调
error = RegisterWifiEvent(&g_wifiEventHandler); //注册事件
if (error != WIFI_SUCCESS)
{
LOG_I("register wifi event fail!\r\n");
}
else
{
LOG_I("register wifi event succeed!\r\n");
}
}
/*STA任务*/
static BOOL WifiSTATask(void)
{
WifiScanInfo *info = NULL;
unsigned int size = WIFI_SCAN_HOTSPOT_LIMIT;
static struct netif *g_lwip_netif = NULL;
WifiDeviceConfig select_ap_config = {0};
osDelay(200);
LOG_I("<--System Init-->\r\n");
//初始化WIFI
WiFiInit();
//使能WIFI
if (EnableWifi() != WIFI_SUCCESS)
{
LOG_E("EnableWifi failed, error = %d\n", error);
return -1;
}
//判断WIFI是否激活
if (IsWifiActive() == 0)
{
LOG_E("Wifi station is not actived.\n");
return -1;
}
//分配空间,保存WiFi信息
info = malloc(sizeof(WifiScanInfo) * WIFI_SCAN_HOTSPOT_LIMIT); //申请可扫描到热点个数
if (info == NULL)
{
return -1;
}
//轮询查找WiFi列表
do{
//重置标志位
ssid_count = 0;
g_staScanSuccess = 0;
//开始扫描
Scan();
//等待扫描结果
WaitSacnResult();
//获取扫描列表
error = GetScanInfoList(info, &size); //获取扫描列表
}while(g_staScanSuccess != 1);
//打印WiFi列表
LOG_I("********************\r\n");
for(uint8_t i = 0; i < ssid_count; i++)
{
LOG_I("no:%.3d, ssid:%-30s, rssi:%5d, freq:%d\r\n", i+1, info[i].ssid, info[i].rssi/100,info[i].frequency);
}
LOG_I("********************\r\n");
//连接指定的WiFi热点
for(uint8_t i = 0; i < ssid_count; i++)
{
if (strcmp(SELECT_WIFI_SSID, info[i].ssid) == 0)
{
int result;
LOG_I("Select:%3d wireless, Waiting...\r\n", i+1);
//拷贝要连接的热点信息
strcpy(select_ap_config.ssid, info[i].ssid);
strcpy(select_ap_config.preSharedKey, SELECT_WIFI_PASSWORD);
select_ap_config.securityType = SELECT_WIFI_SECURITYTYPE; //加密类型
if (AddDeviceConfig(&select_ap_config, &result) == WIFI_SUCCESS) //添加连接对象
{
if (ConnectTo(result) == WIFI_SUCCESS && WaitConnectResult() == 1) //开始连接
{
LOG_I("WiFi connect succeed!\r\n");
g_lwip_netif = netifapi_netif_find(SELECT_WLAN_PORT); //获取网络接口
break;
}
}
}
if(i == ssid_count-1)
{
LOG_I("ERROR: No wifi as expected\r\n");
while(1) osDelay(100);
}
}
//启动DHCP
if (g_lwip_netif)
{
dhcp_start(g_lwip_netif);
LOG_I("begain to dhcp\r\n");
}
//等待DHCP
for(;;)
{
if(dhcp_is_bound(g_lwip_netif) == ERR_OK)
{
LOG_I("<-- DHCP state:OK -->\r\n");
//打印获取到的IP信息
netifapi_netif_common(g_lwip_netif, dhcp_clients_info_show, NULL);
break;
}
LOG_I("<-- DHCP state:Inprogress -->\r\n");
osDelay(100);
}
//执行其他操作
for(;;)
{
osDelay(100);
}
}
/*STA任务初始化*/
void drv_sta_task_init(void)
{
osThreadAttr_t attr;
attr.name = "WifiSTATask";
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = 10240;
attr.priority = 24;
if (osThreadNew((osThreadFunc_t)WifiSTATask, NULL, &attr) == NULL)
{
LOG_I("Falied to create WifiSTATask!\n");
}
}
看结果:
可以看到,扫描结束后,触发了OnWifiScanStateChanged回调。STA连接上AP之后,触发了OnWifiConnectionChanged回调。且在回调中都打印了链接相关信息。
dhcp_clients_info_show会把server和client的相关信息都打印出来。包括MAC和IP地址等。