跳转至

WLAN框架应用笔记

本文的目的和结构

本文的目的和背景

随着物联网快速发展,越来越多的嵌入式设备上搭载了WIFI无线网络设备。为了能够管理WIFI网络设备,RT-Thread引入了WLAN设备管理框架。这套框架具备控制和管理WIFI的众多功能,为开发者使用WIFI设备提供许多便利。

本文将帮助开发者学会使用这套框架控制管理WIFI。将从概念,示例等多方面介绍WLAN框架的相关知识,通过Shell及代码两种方式展示WIFI相关的功能,如控制WIFI扫描、连接、断开等。

本文的结构

  • WLAN框架介绍

  • WLAN框架配置

  • WLAN框架使用

问题阐述

本应用笔记主要围绕下面几个问题来介绍WLAN框架

  • WLAN框架是什么,具有什么功能?

  • 如何配置WLAN框架?

  • 如何使用WLAN框架?

想要解决上诉问题,就要了解WLAN框架的组成原理,学会使用WLAN框架中的各种功能,下面将逐步开始介绍WLAN框架的组成及相关功能的使用。

问题解决

WLAN框架介绍

WLAN框架是RT-Thread开发的一套用于管理WIFI的中间件。对下连接具体的WIFI驱动,控制WIFI的连接断开,扫描等操作。对上承载不同的应用,为应用提供WIFI控制,事件,数据导流等操作,为上层应用提供统一的WIFI控制接口。WLAN框架主要由三个部分组成。DEV驱动接口层,为WLAN框架提供统一的调用接口。Manage管理层为用户提供WIFI扫描,连接,断线重连等具体功能。Protocol协议负责处理WIFI上产生的数据流,可根据不同的使用场景挂载不同通讯协议,如LWIP等。具有使用简单,功能齐全,对接方便,兼容性强等特点。

WLAN框架组成

下图是WIFI框架的结构框架

WIFI框架

第一部分app为应用层。是基于WLAN框架的具体应用,如WiFi相关的Shell命令。

第二部分airkiss、voice为配网层。提供无线配网和声波配网等功能。

第三部分WLAN manager为WLAN管理层。能够对WLAN设备进行控制和管理。具备设置模式、连接热点、断开热点、启动热点,、扫描热点等WLAN控制相关的功能。还提供断线重连,自动切换热点等管理功能。

第四部分WLAN protocol为协议层。将数据流递交给具体协议进行解析,用户可以指定使用不同的协议进行通信。

第五部分WLAN config为参数管理层。管理连接成功的热点信息及密码,并写入非易失的存储介质中。

第六部分WLAN dev为驱动接口层。对接具体WLAN硬件,为管理层提供统一的调用接口。

WLAN框架功能

  • 自动连接 打开自动连接功能后,只要WIFI处在断线状态,就会自动读取之前连接成功的热点信息,连接热点。如果一个热点连接失败,则切换下一个热点信息进行连接,直到连接成功为止。自动连接使用的热点信息,按连接成功的时间顺序,依次尝试,优先使用最近连接成功的热点信息。连接成功后,将热点信息缓存在最前面,下次断线优先使用。

  • 参数存储 存储连接成功的WIFI参数,WIFI参数会在内存中缓存一份,如果配置外部非易失存储接口,则会在外部存储介质中存储一份。用户可根据自己的实际情况,实现 struct rt_wlan_cfg_ops 这个结构体,将参数保存任何地方。缓存的参数主要给自动连接提供热点信息,wifi处在未连接状态时,会读取缓存的参数,尝试连接。

  • WIFI控制 提供完备的WIFI控制接口,扫描,连接,热点等。提供WIFI相关状态回调事件,断开,连接,连接失败等。为用户提供简单易用的WIFI管理接口。

  • Shell命令 可在Msh中输入命令控制WIFI执行扫描,连接,断开等动作。打印WIFI状态等调试信息。

WLAN框架配置及初始化

本文将基于正点原子 STML4 IOT board 开发板,该开发板板载一颗AP6181 WiFi芯片,且WiFi驱动已经实现。适合用来学习WLAN管理框架。

WIFI框架

WLAN框架配置主要包括以下几个方面

  • 开启WLAN框架,并配置

  • 初始化WLAN设备,并指定使用的协议

WLAN框架配置

这里将介绍WIFI框架相关的配置,使用env工具进入 IOT board 目录,在env的命令行中输入 menuconfig 命令,打开图形配置界面

  • menuconfig 配置界面依次选择 ,RT-Thread Components -> Device Drivers -> Using WiFi -> 如下图所示

WLAN配置

下面将介绍这些配置项

  • Using Wi-Fi framework : 使用WLAN管理框架

  • the WiFi device name for station : Station设备默认名字

  • the WiFi device name for ap : ap设备默认名字

  • Default transport protocol : 默认协议

  • Scan timeout time : 扫描结果超时时间

  • connect timeout time : 连接超时时间

  • SSID name maximum length : SSID最大长度

  • Maximum password length : 密码最大长度

  • Automatic sorting of scan results : 扫描结果自动排序

  • Maximum number of WiFi information automatically saved : 自动保存最多条目数

  • WiFi work queue thread name : WIFI后台线程名字

  • wifi work queue thread size : WIFI后台线程栈大小

  • WiFi work queue thread priority : WIFI后台线程优先级

  • Maximum number of driver events : dev层一种事件最大注册数

  • Forced use of PBUF transmission : 强行使用PBUF交换数据

  • Enable WLAN Debugging Options : 打开调试log日志

按照上图配置好后,保存然后退出

WLAN设备初始化

WLAN框架需要指定一个初始化WLAN设备的工作模式,这些需要写代码实现,所以需要以下代码进行初始化。代码如下:

int wifi_init(void)
{
    rt_wlan_set_mode(RT_WLAN_DEVICE_STA_NAME, RT_WLAN_STATION);      //配置WLAN设备工作模式
    return 0;
}

编写好上面的代码,一定要调用执行一次,即可使用使用WLAN框架管理设备了。

WLAN使用

注:进行下面操作之前,一定要先执行WLAN设备初始化,详查看 WLAN设备初始化 部分

Shell操作WiFi

使用shell命令,可以帮助我们快速调试WiFi相关功能。只需要在msh中输入相应的命令,WiFi就执行相应的动作。下面将通过三条命令,展现使用shell命令操作WiFi。

wifi相关的shell命令如下:

wifi                                       :打印帮助
wifi help                                  :查看帮助
wifi join SSID [PASSWORD]                  :连接wifi,SSDI为空,使用配置自动连接
wifi ap   SSID [PASSWORD]                  :建立热点
wifi scan                                  :扫描全部热点
wifi disc                                  :断开连接
wifi ap_stop                               :停止热点
wifi status                                :打印wifi状态 sta + ap
wifi smartconfig                           :启动配网功能
WiFi扫描

执行wifi扫描命令后,会将周围的热点信息打印在终端上。通过打印的热点信息,可以看到SSID,MAC地址等多项属性。

  • wifi 扫描命令格式如下
wifi scan

命令说明

字段 描述
wifi 有关wifi命令都以wifi开头
scan wifi执行扫描动作

在msh中输入该命令,即可进行 wifi 命令扫描,扫描结果如下图所示

wifi scan
SSID                                   MAC            security    rssi chn Mbps
------------------------------- -----------------  -------------- ---- --- ----
rtt_test_ssid_1                 c0:3d:46:00:3e:aa  OPEN           -14    8  300
test_ssid                       3c:f5:91:8e:4c:79  WPA2_AES_PSK   -18    6   72
rtt_test_ssid_2                 ec:88:8f:88:aa:9a  WPA2_MIXED_PSK -47    6  144
rtt_test_ssid_3                 c0:3d:46:00:41:ca  WPA2_MIXED_PSK -48    3  300
WiFi连接

执行WiFi连接命令后,如果热点存在,且密码正确,开发板会连接上热点,并获得IP地址。网络连接成功后,可使用socket套接字进行网络通讯。

  • wifi 连接命令格式如下
wifi join rtt-SSID0 12345678
  • 命令解析
字段 描述
wifi 有关wifi命令都以wifi开头
join wifi执行连接动作
ssid 热点的名字
123456789 热点的密码,没有密码可不输入这一项
  • 连接成功后,将在终端上打印获得的IP地址,如下图所示
wifi join ssid_test 12345678
[I/WLAN.mgnt] wifi connect success ssid:ssid_test
[I/WLAN.lwip] Got IP address : 192.168.1.110
WiFi断开

执行WiFi断开命令后,开发板将断开与热点的连接。

  • wifi 断开命令格式如下
wifi disc
  • 命令解析
字段 描述
wifi 有关wifi命令都以wifi开头
disc wifi执行断开动作
  • 断开成功后,将在终端上打印如下信息,如下图所示
wifi disc
[I/WLAN.mgnt] disconnect success!

WiFi扫描

下面这段代码将展示WiFi同步扫描,然后我们将结果打印在终端上。 先需要执行WIFI初始化,然后执行WIFI扫描函数 rt_wlan_scan_sync,这个函数是同步的,函数返回的扫描的数量和结果。在这个示例中,会将扫描的热点名字打印出来。

#include <rthw.h>
#include <rtthread.h>

#include <wlan_mgnt.h>
#include <wlan_prot.h>
#include <wlan_cfg.h>

void wifi_scan(void)
{
    struct rt_wlan_scan_result *result;
    int i = 0;

    /* Configuring WLAN device working mode */
    rt_wlan_set_mode(RT_WLAN_DEVICE_STA_NAME, RT_WLAN_STATION);
    /* WiFi scan */
    result = rt_wlan_scan_sync();
    /* Print scan results */
    rt_kprintf("scan num:%d\n", result->num);
    for (i = 0; i < result->num; i++)
    {
        rt_kprintf("ssid:%s\n", result->info[i].ssid.val);
    }
}

int scan(int argc, char *argv[])
{
    wifi_scan();
    return 0;
}
MSH_CMD_EXPORT(scan, scan test.);

运行结果如下:

扫描

WiFi连接与断开

下面这段代码将展示WiFi同步连接。

先需要执行WIFI初始化,然后常见一个用于等待 RT_WLAN_EVT_READY 事件的信号量。注册需要关注的事件的回调函数,执行 rt_wlan_connect wifi连接函数,函数返回表示是否已经连接成功。但是连接成功还不能进行通信,还需要等待网络获取IP。使用事先创建的信号量等待网络准备好,网络准备好后,就能正常通信了。

连接上WIFI后,等待一段时间后,执行 rt_wlan_disconnect 函数断开连接。断开操作是阻塞的,返回值表示是否断开成功。

#include <rthw.h>
#include <rtthread.h>

#include <wlan_mgnt.h>
#include <wlan_prot.h>
#include <wlan_cfg.h>

#define WLAN_SSID               "SSID-A"
#define WLAN_PASSWORD           "12345678"
#define NET_READY_TIME_OUT       (rt_tick_from_millisecond(15 * 1000))

static rt_sem_t net_ready = RT_NULL;

static void
wifi_ready_callback(int event, struct rt_wlan_buff *buff, void *parameter)
{
    rt_kprintf("%s\n", __FUNCTION__);
    rt_sem_release(net_ready);
}

static void
wifi_connect_callback(int event, struct rt_wlan_buff *buff, void *parameter)
{
    rt_kprintf("%s\n", __FUNCTION__);
    if ((buff != RT_NULL) && (buff->len == sizeof(struct rt_wlan_info)))
    {
        rt_kprintf("ssid : %s \n", ((struct rt_wlan_info *)buff->data)->ssid.val);
    }
}

static void
wifi_disconnect_callback(int event, struct rt_wlan_buff *buff, void *parameter)
{
    rt_kprintf("%s\n", __FUNCTION__);
    if ((buff != RT_NULL) && (buff->len == sizeof(struct rt_wlan_info)))
    {
        rt_kprintf("ssid : %s \n", ((struct rt_wlan_info *)buff->data)->ssid.val);
    }
}

static void
wifi_connect_fail_callback(int event, struct rt_wlan_buff *buff, void *parameter)
{
    rt_kprintf("%s\n", __FUNCTION__);
    if ((buff != RT_NULL) && (buff->len == sizeof(struct rt_wlan_info)))
    {
        rt_kprintf("ssid : %s \n", ((struct rt_wlan_info *)buff->data)->ssid.val);
    }
}

rt_err_t wifi_connect(void)
{
    rt_err_t result = RT_EOK;

    /* Configuring WLAN device working mode */
    rt_wlan_set_mode(RT_WLAN_DEVICE_STA_NAME, RT_WLAN_STATION);
    /* station connect */
    rt_kprintf("start to connect ap ...\n");
    net_ready = rt_sem_create("net_ready", 0, RT_IPC_FLAG_FIFO);
    rt_wlan_register_event_handler(RT_WLAN_EVT_READY,
            wifi_ready_callback, RT_NULL);
    rt_wlan_register_event_handler(RT_WLAN_EVT_STA_CONNECTED,
            wifi_connect_callback, RT_NULL);
    rt_wlan_register_event_handler(RT_WLAN_EVT_STA_DISCONNECTED,
            wifi_disconnect_callback, RT_NULL);
    rt_wlan_register_event_handler(RT_WLAN_EVT_STA_CONNECTED_FAIL,
            wifi_connect_fail_callback, RT_NULL);

    /* connect wifi */
    result = rt_wlan_connect(WLAN_SSID, WLAN_PASSWORD);

    if (result == RT_EOK)
    {
        /* waiting for IP to be got successfully  */
        result = rt_sem_take(net_ready, NET_READY_TIME_OUT);
        if (result == RT_EOK)
        {
            rt_kprintf("networking ready!\n");
        }
        else
        {
            rt_kprintf("wait ip got timeout!\n");
        }
        rt_wlan_unregister_event_handler(RT_WLAN_EVT_READY);
        rt_sem_delete(net_ready);

        rt_thread_delay(rt_tick_from_millisecond(5 * 1000));
        rt_kprintf("wifi disconnect test!\n");
        /* disconnect */
        result = rt_wlan_disconnect();
        if (result != RT_EOK)
        {
            rt_kprintf("disconnect failed\n");
            return result;
        }
        rt_kprintf("disconnect success\n");
    }
    else
    {
        rt_kprintf("connect failed!\n");
    }
    return result;
}

int connect(int argc, char *argv[])
{
    wifi_connect();
    return 0;
}
MSH_CMD_EXPORT(connect, connect test.);

运行结果如下

连接断开

WiFi开启自动重连

先开启自动重连功能,使用命令行连接上一个热点A后,在连接上另一个热点B。等待几秒后,将热点B断电,系统会自动重试连接B热点,此时B热点连接不上,系统自动切换热点A进行连接。连接成功A后,系统停止连接。

#include <rthw.h>
#include <rtthread.h>

#include <wlan_mgnt.h>
#include <wlan_prot.h>
#include <wlan_cfg.h>

static void
wifi_ready_callback(int event, struct rt_wlan_buff *buff, void *parameter)
{
    rt_kprintf("%s\n", __FUNCTION__);
}

static void
wifi_connect_callback(int event, struct rt_wlan_buff *buff, void *parameter)
{
    rt_kprintf("%s\n", __FUNCTION__);
    if ((buff != RT_NULL) && (buff->len == sizeof(struct rt_wlan_info)))
    {
        rt_kprintf("ssid : %s \n", ((struct rt_wlan_info *)buff->data)->ssid.val);
    }
}

static void
wifi_disconnect_callback(int event, struct rt_wlan_buff *buff, void *parameter)
{
    rt_kprintf("%s\n", __FUNCTION__);
    if ((buff != RT_NULL) && (buff->len == sizeof(struct rt_wlan_info)))
    {
        rt_kprintf("ssid : %s \n", ((struct rt_wlan_info *)buff->data)->ssid.val);
    }
}

static void
wifi_connect_fail_callback(int event, struct rt_wlan_buff *buff, void *parameter)
{
    rt_kprintf("%s\n", __FUNCTION__);
    if ((buff != RT_NULL) && (buff->len == sizeof(struct rt_wlan_info)))
    {
        rt_kprintf("ssid : %s \n", ((struct rt_wlan_info *)buff->data)->ssid.val);
    }
}

int wifi_autoconnect(void)
{
    /* Configuring WLAN device working mode */
    rt_wlan_set_mode(RT_WLAN_DEVICE_STA_NAME, RT_WLAN_STATION);
    /* Start automatic connection */
    rt_wlan_config_autoreconnect(RT_TRUE);
    /* register event */
    rt_wlan_register_event_handler(RT_WLAN_EVT_READY,
            wifi_ready_callback, RT_NULL);
    rt_wlan_register_event_handler(RT_WLAN_EVT_STA_CONNECTED,
            wifi_connect_callback, RT_NULL);
    rt_wlan_register_event_handler(RT_WLAN_EVT_STA_DISCONNECTED,
            wifi_disconnect_callback, RT_NULL);
    rt_wlan_register_event_handler(RT_WLAN_EVT_STA_CONNECTED_FAIL,
            wifi_connect_fail_callback, RT_NULL);
    return 0;
}

int auto_connect(int argc, char *argv[])
{
    wifi_autoconnect();
    return 0;
}
MSH_CMD_EXPORT(auto_connect, auto connect test.);

运行结果如下:

自动连接

参考

本文相关API

API列表

API 位置
rt_wlan_set_mode wlan_mgnt.c
rt_wlan_prot_attach wlan_prot.c
rt_wlan_scan_sync wlan_mgnt.c
rt_wlan_connect wlan_mgnt.c
rt_wlan_disconnect wlan_mgnt.c
rt_wlan_config_autoreconnect wlan_mgnt.c

核心API详解

rt_wlan_set_mode()

函数功能

注册WLAN设备到WLAN设备框架

函数原型

rt_err_t rt_wlan_set_mode(const char *dev_name, rt_wlan_mode_t mode);

函数参数

参数 描述
dev_name WLAN设备名
mode WLAN设备工作模式

返回值

返回值 描述
-RT_EINVAL 参数错误
-RT_EIO 设备未找到
-RT_ERROR 执行失败
RT_EOK 执行成功

wlan mode 可取以下值之一 RT_WLAN_NONE 清空工作模式 RT_WLAN_STATION 工作在STATION模式 RT_WLAN_AP 工作在AP模式

rt_wlan_prot_attach()

函数功能

指定WLAN设备使用的协议

函数原型

rt_err_t rt_wlan_prot_attach(const char *dev_name, const char *prot_name);

函数参数

参数 描述
name WLAN设备名
prot_name 协议名

返回值

返回值 描述
-RT_ERROR 执行失败
RT_EOK 执行成功

type 可取以下值之一 RT_WLAN_PROT_LWIP 协议类型为LWIP

rt_wlan_scan_sync()

函数功能

同步扫描热点

函数原型

struct rt_wlan_scan_result *rt_wlan_scan_sync(void);

函数参数

返回值

返回值 描述
rt_wlan_scan_result 扫描结果

扫描结果是一个结构体,成员如下

num : info数量 info : info指针

struct rt_wlan_scan_result
{
    rt_int32_t num;
    struct rt_wlan_info *info;
};

rt_wlan_connect()

函数功能

同步连接热点

函数原型

rt_err_t rt_wlan_connect(const char *ssid, const char *password);

函数参数

参数 描述
ssid WIFI名字
password WIFI密码

返回值

返回值 描述
-RT_EINVAL 参数错误
-RT_EIO 未注册设备
-RT_ERROR 连接失败
RT_EOK 连接成功

rt_wlan_disconnect()

函数功能

同步断开热点

函数原型

rt_err_t rt_wlan_disconnect(void);

函数参数

返回值

返回值 描述
-RT_EIO 未注册设备
-RT_ENOMEM 内存不足
-RT_ERROR 断开失败
RT_EOK 断开成功

rt_wlan_config_autoreconnect()

函数功能

配置自动重连模式

函数原型

void rt_wlan_config_autoreconnect(rt_bool_t enable);

函数参数

参数 描述
enable enanle/disable自动重连

返回值

评论