Zephyr网络使用-Wifi控制

Creative Commons
本作品采用知识共享署名

本文基于esp32c3说明如何配置使用wifi. 可以使用zephyr自带的esp32c3_devkitm进行验证.

esp32c3自带wifi和蓝牙,zephyr中wifi的数据通路通过ethernet的L2接入TCP/IP, wifi的控制通路通过wifi L2接入net_mgmt. 本文主要说明如何通过配置启用esp32c3,并演示如果使用net_mgmt进行wifi扫描,连接等操作。

配置

增加配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 启用WIFI
CONFIG_WIFI=y
CONFIG_WIFI_ESP32=y

# 启用网络
CONFIG_NETWORKING=y
CONFIG_NET_IPV4=y

# esp32c3的wifi数据通路是走ethernet,因此需要打开ethernet L2支持
CONFIG_NET_L2_ETHERNET=y

# esp32c3的wifi控制通路由net_mgmt管理,需要打开
CONFIG_NET_MGMT=y
CONFIG_NET_MGMT_EVENT=y

在设备树中启用wifi

1
2
3
&wifi {
status = "okay";
};

Zephyr-3.2.0下 esp32c3使用的wifi节点没有实际的物理参数可配置,仅仅是用来标识启用该设备,如果不添加构建时会被Kconfig检查到报错

1
warning: WIFI_ESP32 (defined at drivers/wifi/esp32/Kconfig.esp32:3) has direct dependencies DT_HAS_ESPRESSIF_ESP32_WIFI_ENABLED && !SMP && WIFI with value n, but is currently being y-selected by the following symbols:

代码

Zephyr对wifi的控制操作封装得非常简练,通过net_mgmt进行控制请求, 结果直接返回或者是通过net_mgmt_init_event_callback/net_mgmt_add_event_callback注册callback,在callback中处理控制请求的结果。

wifi控制请求

wifi控制的请求全部定义在include/zephyr/net/wifi_mgmt.h中,有下面6种

  • NET_REQUEST_WIFI_SCAN 扫描AP节点
  • NET_REQUEST_WIFI_CONNECT 连接AP节点
  • NET_REQUEST_WIFI_DISCONNECT 断开连接
  • NET_REQUEST_WIFI_AP_ENABLE 启用AP
  • NET_REQUEST_WIFI_AP_DISABLE 禁用AP
  • NET_REQUEST_WIFI_IFACE_STATUS 获取wifi接口状态,例如收发数据量的大小。注意这里获取不到信号强度的。

响应事件

对于请求,会有一些响应事件定义在include/zephyr/net/wifi_mgmt.h中,有下面5种

  • NET_EVENT_WIFI_SCAN_RESULT 通知wifi扫描的结果
  • NET_EVENT_WIFI_SCAN_DONE 通知wifi扫描已完成
  • NET_EVENT_WIFI_CONNECT_RESULT 通知连接的结果
  • NET_EVENT_WIFI_DISCONNECT_RESULT 通知断开连接的结果
  • NET_EVENT_WIFI_IFACE_STATUS 通知wifi接口的状态

使用方法

扫描AP节点

1
2
struct net_if *iface = net_if_get_default();
int ret = net_mgmt(NET_REQUEST_WIFI_SCAN, iface, NULL, 0)

net_mgmt执行NET_REQUEST_WIFI_SCAN用于发动扫描AP,返回为0表示成功启动扫描,返回标准的errno表示失败。每扫到一个AP节点就有一次NET_EVENT_WIFI_SCAN_RESULT,此时可以从callback获取到AP的信息, 本次扫描结束后产生NET_EVENT_WIFI_SCAN_DONE.

连接和断开AP节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct net_if *iface = net_if_get_default();
struct wifi_connect_req_params params;

//配置SSID
params->ssid = "AP_NAME"
params->ssid_length = strlen(params->ssid);

//配置channel
params->channel = WIFI_CHANNEL_ANY;

//配置连接密码
params->psk = "AP_PASSWORD"
params->psk_length = strlen(argv[idx]);

//密码验证方式
params->security = WIFI_SECURITY_TYPE_PSK;

//管理帧保护
params->mfp = WIFI_MFP_OPTIONAL;

//执行连接
int ret = net_mgmt(NET_REQUEST_WIFI_CONNECT, iface,
&params, sizeof(struct wifi_connect_req_params))

net_mgmt执行NET_REQUEST_WIFI_CONNECT用于执行连接动作,无论连接成功还是失败都会收到NET_EVENT_WIFI_CONNECT_RESULT, 在callback中可以判断连接的结果。

1
2
struct net_if *iface = net_if_get_default();
int status = net_mgmt(NET_REQUEST_WIFI_DISCONNECT, iface, NULL, 0);

net_mgmt执行NET_REQUEST_WIFI_DISCONNECT用于执行断开连接动作,如果返回的状态为-EALREADY表示已经断开。断开连接时会收到NET_EVENT_WIFI_DISCONNECT_RESULT, 注意:即便没有执行NET_REQUEST_WIFI_DISCONNECT而是因为AP主动断开,也会有NET_EVENT_WIFI_DISCONNECT_RESULT产生

启用和禁用AP模式

通过设置可以让esp32c3处于AP模式

1
2
3
4
5
6
static struct wifi_connect_req_params ap_params;

// 设置ap_params和连接的参数方式一样,这里不再列出

int ret net_mgmt(NET_REQUEST_WIFI_AP_ENABLE, iface,
&ap_params, sizeof(struct wifi_connect_req_params))

net_mgmt执行NET_REQUEST_WIFI_AP_ENABLE用于启用AP模式,不会有事件产生

1
2
int ret net_mgmt(NET_REQUEST_WIFI_AP_DISABLE, iface,
&ap_params, sizeof(struct wifi_connect_req_params))

net_mgmt执行NET_REQUEST_WIFI_AP_DISABLE用于启用AP模式,不会有事件产生

获取WIFI的接口状态

只有在配置了CONFIG_NET_STATISTICS_WIFI=yCONFIG_NET_STATISTICS_USER_API=y的情况下才能获取状态

1
2
3
4
5
6
struct net_if *iface = net_if_get_default();
struct net_stats_wifi stats = { 0 };
int ret;

ret = net_mgmt(NET_REQUEST_STATS_GET_WIFI, iface,
&stats, sizeof(stats));

抓取的状态是接口收到的包统计而不是信号强度

1
2
3
4
5
6
7
8
struct net_stats_wifi {
struct net_stats_sta_mgmt sta_mgmt;
struct net_stats_bytes bytes;
struct net_stats_pkts pkts;
struct net_stats_pkts broadcast;
struct net_stats_pkts multicast;
struct net_stats_pkts errors;
};

由于状态可以及时返回,我们不需要再去处理NET_EVENT_WIFI_IFACE_STATUS

注册事件处理回调

事件处理回调用于处理响应事件, 首先注册回调

1
2
3
4
5
6
7
8
9
10
11
static struct net_mgmt_event_callback wifi_mgmt_cb;

//初始化,可以指定要响应那些事件
net_mgmt_init_event_callback(&wifi_mgmt_cb,
wifi_mgmt_event_handler,
(NET_EVENT_WIFI_SCAN_RESULT | \
NET_EVENT_WIFI_SCAN_DONE | \
NET_EVENT_WIFI_CONNECT_RESULT | \
NET_EVENT_WIFI_DISCONNECT_RESULT));

net_mgmt_add_event_callback(&wifi_mgmt_cb);

回调内依照event的不同分别处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
static void wifi_mgmt_event_handler(struct net_mgmt_event_callback *cb,
uint32_t mgmt_event, struct net_if *iface)
{
switch (mgmt_event) {
case NET_EVENT_WIFI_SCAN_RESULT:
{
//CB中的信息就是scan的结果
const struct wifi_scan_result *entry = (const struct wifi_scan_result *)cb->info;
}
break;
case NET_EVENT_WIFI_SCAN_DONE:
{
//CB中的信息是wifi状态
const struct wifi_status *status = (const struct wifi_status *)cb->info;
}
break;
case NET_EVENT_WIFI_CONNECT_RESULT:
{
//CB中的信息是wifi状态
const struct wifi_status *status = (const struct wifi_status *)cb->info;
}
break;
case NET_EVENT_WIFI_DISCONNECT_RESULT:
{
//CB中的信息是wifi状态
const struct wifi_status *status = (const struct wifi_status *)cb->info;
}
break;
default:
break;
}
}

不同的事件按照不同的结构来解析cb->info, NET_EVENT_WIFI_SCAN_RESULT按照struct wifi_scan_result进行解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct wifi_scan_result {
//SSID
uint8_t ssid[WIFI_SSID_MAX_LEN];
uint8_t ssid_length;

// AP带宽
uint8_t band;

// AP的工作channel
uint8_t channel;

//密码验证方式
enum wifi_security_type security;

// 管理帧保护
enum wifi_mfp_options mfp;

//信号强度
int8_t rssi;

//BSSID
uint8_t mac[WIFI_MAC_ADDR_LEN];
uint8_t mac_length;
};

wifi的状态按照如下结构解析

1
2
3
struct wifi_status {
int status;
};

不同的事件status含义不一样:

  • NET_EVENT_WIFI_SCAN_DONE 0表示扫描正常结束,其它为不正常结束
  • NET_EVENT_WIFI_CONNECT_RESULT 0表示连接成功,其它为连接失败
  • NET_EVENT_WIFI_DISCONNECT_RESULT 0表示断开连接成功,其它为断开连接失败

代码分析指南

目前zephyr中wifi的控制都是通过驱动注册的struct net_wifi_mgmt_offload的API来进行,因此wifi的管理实现并不复杂,主要实现在subsys/net/l2/wifi/wifi_mgmt.c中,例如:
由mgmt的宏包装实现函数

1
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_WIFI_CONNECT, wifi_connect);

再由实现函数调用offload API

1
2
3
4
5
6
7
8
9
10
11
12
static int wifi_connect(uint32_t mgmt_request, struct net_if *iface,
void *data, size_t len)
{
//找到驱动的device
const struct device *dev = net_if_get_device(iface);
//加载驱动的offload API
struct net_wifi_mgmt_offload *off_api =
(struct net_wifi_mgmt_offload *) dev->api

//调用驱动的offload API
return off_api->connect(dev, params);
}

offload api再向下就对接到驱动中,esp32c3对接到的是drivers/wifi/esp32/src/esp_wifi_drv.cesp32_wifi_scan

1
2
3
4
5
6
7
8
9
10
static const struct net_wifi_mgmt_offload esp32_api = {
.wifi_iface.iface_api.init = esp32_wifi_init,
.wifi_iface.send = esp32_wifi_send,
.wifi_iface.get_stats = esp32_wifi_stats,
.scan = esp32_wifi_scan,
.connect = esp32_wifi_connect,
.disconnect = esp32_wifi_disconnect,
.ap_enable = esp32_wifi_ap_enable,
.ap_disable = esp32_wifi_ap_disable,
};

对上net_mgmt(NET_REQUEST_WIFI_CONNECT, iface, &cnx_params, sizeof(struct wifi_connect_req_params))如何调用到wifi_connect可以参考Zephyr网络管理模块分析-注册请求机制一文.