概述
本文主要介绍使用Renesa Version Board中WIFI功能,该模块基于RW007模块设计,RT-Thread软件架构已经实现该硬件相关的驱动接口。笔者基于该模块的相关接口在LWIP软件框架的基础上实现Client功能。实现数据的发送和接收。
1 WLAN 框架简介
参考文档:
https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/wlan/wlan?id=wlan-%E6%89%AB%E6%8F%8F
WLAN 框架是 RT-Thread 开发的一套用于管理 WIFI 的中间件。对下连接具体的 WIFI 驱动,控制 WIFI 的连接断开,扫描等操作。对上承载不同的应用,为应用提供 WIFI 控制,事件,数据导流等操作,为上层应用提供统一的 WIFI 控制接口。WLAN 框架主要由三个部分组成。DEV 驱动接口层,为 WLAN 框架提供统一的调用接口。Manage 管理层为用户提供 WIFI 扫描,连接,断线重连等具体功能。Protocol 协议负责处理 WIFI 上产生的数据流,可根据不同的使用场景挂载不同通讯协议,如 LWIP 等。具有使用简单,功能齐全,对接方便,兼容性强等特点。
第一部分 app 为应用层。是基于 WLAN 框架的具体应用,如 WiFi 相关的 Shell 命令。
第二部分 airkiss、voice 为配网层。提供无线配网和声波配网等功能。
第三部分 WLAN manager 为 WLAN 管理层。能够对 WLAN 设备进行控制和管理。具备设置模式、连接热点、断开热点、启动热点、扫描热点等 WLAN 控制相关的功能。还提供断线重连,自动切换热点等管理功能。
第四部分 WLAN protocol 为协议层。将数据流递交给具体协议进行解析,用户可以指定使用不同的协议进行通信。
第五部分 WLAN config 为参数管理层。管理连接成功的热点信息及密码,并写入非易失的存储介质中。
第六部分 WLAN dev 为驱动接口层。对接具体 WLAN 硬件,为管理层提供统一的调用接口。
2 RT-Thread创建WIFI项目
2.1 RT-Thread中创建项目
使用ST-Thread创建项目,选择WIFI类型的Demo,创建完成后。查看配置参数
点击RT-Thread Setting,可以看见如下两个软件包已经被引用到项目中。
2.2 测试WIFI功能
编译代码下载到板卡中:
使用命令可以扫描当前环境中的热点:
wifi scan
热点信息如下:
3 WIFI功能应用
3.1 WIFI接口
WLAN 连接接口函数
函数 | 描述 |
---|---|
rt_wlan_connect() | 连接热点 |
rt_wlan_connect_adv() | 无阻塞连接热点 |
rt_wlan_disconnect() | 断开热点 |
rt_wlan_is_connected() | 获取连接标志 |
rt_wlan_is_ready() | 获取就绪标志 |
rt_wlan_get_info() | 获取连接信息 |
rt_wlan_get_rssi() | 获取信号强度 |
连接热点函数:
rt_err_t rt_wlan_connect(const char *ssid, const char *password)
阻塞式连接热点。此 API调用的时间会比较长,连接成功或失败后才会返回。
参数 描述 ssid
热点的名字 password
热点密码,无密码传空 返回 描述 RT_EOK 连接成功 -RT_ERROR 连接失败 WLAN 连接成功,还不能进行数据通讯,需要等待连接就绪才能通讯。
设置模式函数:
rt_err_t rt_wlan_set_mode(const char *dev_name, rt_wlan_mode_t mode)
设置 WLAN 设备的工作模。同一个设备,切换相同的模式无效,一种模式,只能存在一个设备,不能两个设备设置同一个模式。一般的,一个设备只支持一种模式。
参数 描述 dev_name
设备名字 mode
工作模式 返回值 描述 RT_EOK 设置成功 -RT_ERROR 设置失败
使用Demo:
struct rt_wlan_info info;
INVALID_INFO(&info); /* 初始化 info */
SSID_SET(&info, "test_ap"); /* 设置热点名字 */
info.security = SECURITY_WPA2_AES_PSK; /* 指定安全类型 */
rt_wlan_connect_adv(&info, "12345678"); /* 执行连接动作 */
while (rt_wlan_is_connected() == RT_FALSE); /* 等待连接成功 */
3.2 WLAN 设备使用示例
#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.);
4 WIFI 创建Client
4.1 使能WIFI接口函数
1) 头文件和端口定义
代码10~11行: 添加lwip的头文件
代码15~17行:添加wifi相关的头文件
代码19~20行: 添加wifi用户名和密码
代码25~26行:server 和client的端口号
2)wifi相关的回调函数
3)连接函数
代码74行:设置WIFI的模式
代码77~85行: 注册回调函数
代码88行:
4.2 LWIP接口函数
代码201行:配置服务器IP
代码204行:创建控制块
代码211行: 连接服务器
接收数据回调函数
注册回调函数
4.3 主函数
代码129行: 调用连接wifi函数
代码131行: 创建tcp client
4.4 详细源代码文件
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-08-25 Administrator the first version
*/
#include "lwip/memp.h"
#include "lwip/tcp.h"
#include <rthw.h>
#include <rtthread.h>
#include <wlan_mgnt.h>
#include <wlan_prot.h>
#include <wlan_cfg.h>
#define WLAN_SSID wifi_user
#define WLAN_PASSWORD wifi_pd
#define NET_READY_TIME_OUT (rt_tick_from_millisecond(15 * 1000))
#define TCP_REMOTE_PORT 19999 /* 远端端口 */
#define TCP_LOCAL_PORT 2980 /* 本地端口 */
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_PRIO);
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 wifi_client(void)
{
wifi_connect();
/* Initilaize the LwIP stack */
tcp_client_init();
}
static err_t tcp_client_recv(void *arg, struct tcp_pcb *tpcb,
struct pbuf *p, err_t err)
{
uint32_t i;
/* 数据回传 */
//tcp_write(tpcb, p->payload, p->len, 1);
if (p != NULL)
{
struct pbuf *ptmp = p;
/* 打印接收到的数据 */
rt_kprintf("get msg from %d:%d:%d:%d port:%d:\r\n",
*((uint8_t *)&tpcb->remote_ip.addr),
*((uint8_t *)&tpcb->remote_ip.addr + 1),
*((uint8_t *)&tpcb->remote_ip.addr + 2),
*((uint8_t *)&tpcb->remote_ip.addr + 3),
tpcb->remote_port);
while(ptmp != NULL)
{
for (i = 0; i < p->len; i++)
{
rt_kprintf("%c", *((char *)p->payload + i));
}
ptmp = p->next;
}
rt_kprintf("\r\n");
tcp_recved(tpcb, p->tot_len);
/* 释放缓冲区数据 */
pbuf_free(p);
}
else if (err == ERR_OK)
{
rt_kprintf("tcp client closed\r\n");
tcp_recved(tpcb, p->tot_len);
return tcp_close(tpcb);
}
return ERR_OK;
}
static err_t tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
{
rt_kprintf("tcp client connected \r\n");
tcp_write(tpcb, "tcp client connected", strlen("tcp client connected"), 0);
/* 注册接收回调函数 */
tcp_recv(tpcb, tcp_client_recv);
return ERR_OK;
}
void tcp_client_init(void)
{
struct tcp_pcb *tpcb;
ip_addr_t serverIp;
/* 服务器IP */
IP4_ADDR(&serverIp, 192, 168, 1, 5);
/* 创建tcp控制块 */
tpcb = tcp_new();
if (tpcb != NULL)
{
err_t err;
/* 绑定本地端号和IP地址 */
err = tcp_bind(tpcb, IP_ADDR_ANY, TCP_LOCAL_PORT);
if (err == ERR_OK)
{
/* 连接服务器 */
tcp_connect(tpcb, &serverIp, TCP_REMOTE_PORT, tcp_client_connected);
}
else
{
memp_free(MEMP_TCP_PCB, tpcb);
rt_kprintf("can not bind pcb \r\n");
}
}
}
5 测试
5.1 创建测试环境
1)使用网络调试工具创建一个server
2)编译代码下载到板卡中运行代码,调试信息显示,系统已经正常连接到wifi,且服务器联网也成功。
在网络调试助手上看见:
5.2 功能测试
服务器上发送数据至Client
Client上接收到的数据