Renesa Version Board开发RT-Thread 之WIFI创建Client

news2024/11/14 20:41:15

概述

本文主要介绍使用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上接收到的数据

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2074418.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java】—— Java面向对象基础:使用Java创建和打印员工对象信息

在Java中&#xff0c;类的定义和使用是面向对象编程的核心。本文将通过一个简单的例子来展示如何定义一个员工类&#xff08;Employee&#xff09;&#xff0c;并在测试类中创建员工对象&#xff0c;为这些对象的属性赋值&#xff0c;并打印出它们的信息。 定义员工类&#xff…

大模型微调

文章目录 前言一、使用的库二、数据预处理1.引入库2.读入数据3.对数据进行预处理4.转换为json格式文件 三&#xff0c;使用算子分析数据并进行数据处理四&#xff0c;划分训练集和测试集五&#xff0c;编写训练脚本开始训练六&#xff0c;进行模型推理人工评估总结 前言 这是使…

网络优化|单源最短路|Dijkstra|Floyd|Matlab

图和网络可以用来描述集合元素和元素之间关系。大量的最优化问题都可以抽象为网络模型加以解释&#xff0c;描述和求解。 图与网络模型在建模时具有直观、易理解、适应性强等&#xff0c;广泛应用在管理科学、物理学、化学、计算机科学、信息论、控制论、社会科学以及军事科学等…

C# 循环访问目录树详解与示例

文章目录 一、目录树遍历的概念二、使用System.IO命名空间三、DirectoryInfo和FileInfo类四、递归遍历目录树五、示例&#xff1a;列出目录树中的所有文件和文件夹六、异常处理七、迭代方法八、总结 在C#中&#xff0c;访问文件系统是常见的需求之一。有时我们需要遍历目录树以…

嵌入式开发技术进步带来新机遇

嵌入式开发作为信息技术领域的重要分支&#xff0c;随着科技的不断进步&#xff0c;正迎来新的机遇。本文将从人工智能、物联网、边缘计算等方面探讨嵌入式开发技术的进步如何带来新的发展机遇&#xff0c;并展望未来的发展趋势。 一、引言 嵌入式系统是一种特殊的计算机系统&…

unreal engine5中character角色和怪物交互时发生穿模

UE5系列文章目录 文章目录 UE5系列文章目录前言一、原因定位二、解决方法 前言 在 Unreal Engine 5 中&#xff0c;角色“穿模”通常指的是角色模型与其他物体&#xff08;如墙壁、地面或其他对象&#xff09;发生不正确的穿透或重叠现象。这可能是由多种原因造成的&#xff0…

C++ | Leetcode C++题解之第372题超级次方

题目&#xff1a; 题解&#xff1a; class Solution {const int MOD 1337;int pow(int x, int n) {int res 1;while (n) {if (n % 2) {res (long) res * x % MOD;}x (long) x * x % MOD;n / 2;}return res;}public:int superPow(int a, vector<int> &b) {int an…

C# 泛型类型的约束详解与示例

文章目录 一、泛型约束概述二、泛型约束详解与示例1. 类约束2. 接口约束3. 引用类型约束4. 值类型约束5. 无参数构造函数约束6、多重约束7、默认构造函数约束8、基类和接口的组合约束 三、总结 在C#编程语言中&#xff0c;泛型是一种非常强大的特性&#xff0c;它允许我们编写可…

NetSuite 2024.2 学习笔记

NetSuite一年两次的发版&#xff0c;每次都会带来一些新的东西。这对于顾问来说&#xff0c;应该成为必修课。 每个版本发版内容的学习时长&#xff0c;大约在20小时左右。包括&#xff1a; •Release Notes通读 •Release Preview环境申请 •热点功能验证 •New Release学习笔…

vue中video视频路径改变,dom不更新问题

效果展示 视频切换前 视频切换后 完整代码 <template><!-- 设置v-if只在路径有值时&#xff0c;标签才存在 --><video v-if"state.videoSrc ! null" controls><source :src"state.videoSrc" type"video/mp4"></…

Java | Leetcode Java题解之第372题超级次方

题目&#xff1a; 题解&#xff1a; class Solution {static final int MOD 1337;public int superPow(int a, int[] b) {int ans 1;for (int e : b) {ans (int) ((long) pow(ans, 10) * pow(a, e) % MOD);}return ans;}public int pow(int x, int n) {int res 1;while (n…

Pytorch实现CIFAR10训练模型

文章目录 简述模型结构模型参数、优化器、损失函数参数初始化优化器损失函数 模型训练、测试集预测、模型保存、日志记录训练测试集测试模型保存模型训练完整代码 tensorboard训练可视化结果train_loss测试准确率测试集loss 模型应用模型独立应用代码api.py预测结果 简述 使用…

leetcode1232一点小问题

解法 a x 2 − x 1 y 2 − y 1 &#xff0c; b y 1 − a x 1 a\frac{x_{2}-x_{1}}{y_{2}-y_{1}} &#xff0c;by_{1}-ax_{1} ay2​−y1​x2​−x1​​&#xff0c;by1​−ax1​ d y n − y n − 1 x n − x n − 1 d\frac{y_{n}-y_{n-1}}{x_{n}-x_{n-1}} dxn​−xn−1​yn​…

【初阶数据结构】链表题的证明

环形链表题目方法的证明 证明1&#xff1a;为什么快指针每次⾛两步&#xff0c;慢指针⾛⼀步可以相遇&#xff0c;有没有可能遇不上&#xff0c;请推理证明&#xff01; 证明二&#xff1a;为什么相遇点&#xff08;meet&#xff09;和头结点&#xff08;head&#xff09;到入环…

sql server导入mysql,使用工具SQLyog

概述 需要将sql server的数据导入到mysql中&#xff0c;由于2种数据库存在各种差异&#xff0c;比如表字段类型就有很多不同&#xff0c;因此需要工具来实现。 这里使用SQLyog来实现。 SQLyog安装 安装过程参考文档&#xff1a;https://blog.csdn.net/Sunshine_liang1/article/…

USART之串口发送+接收应用案例

文章目录 前言一、电路接线图二、应用案例代码三、应用案例分析3.1 USART模块初始化3.1.1 RCC开启时钟3.1.2 GPIO初始化3.1.3 配置USART3.1.4 开启中断、配置NVIC3.1.5 开启USART 3.2 USART串口收发模块3.2.1 Serial_SendByte&#xff08;发送一个字节数据&#xff09;3.2.2 US…

JVM对象创建和内存分配机制深度解析

一、对象创建方式 1、new关键字 这是最常见的创建对象的方式。通过调用类的构造方法&#xff08;constructor&#xff09;来创建对象。如&#xff1a;MyClass obj new MyClass()。这种方式会触发类的加载、链接、初始化过程&#xff08;如果类还未被加载过的话&#xff09;&…

递归搜索与回溯专题篇一

目录 组合 目标和 组合总和 字母大小全排列 组合 题目 思路 解决这道题利用DFS&#xff0c;决策树是怎样的&#xff1f;以n4&#xff0c;k3为例&#xff1a; 因为每个数只用到一次&#xff0c;因此需要剪枝&#xff0c;将出现重复数字的枝剪掉&#xff0c;因为组合中元素的…

Vue中的this.$emit()方法详解【父子组件传值常用】

​在Vue中&#xff0c;this.$emit()方法用于触发自定义事件。它是Vue实例的一个方法&#xff0c;可以在组件内部使用。 使用this.$emit()方法&#xff0c;你可以向父组件发送自定义事件&#xff0c;并传递数据给父组件。父组件可以通过监听这个自定义事件来执行相应的逻辑。 …

【PyQt6 应用程序】QTDesigner生成ui文件转成py源码并执行

要使用Qt Designer设计的UI界面生成Python代码并执行需要遵循几个步骤。确保已经安装了PyQt6和Qt Designer。Qt Designer是一个强大的工具,允许通过拖放组件来设计GUI界面,而不需要手写所有的代码。安装PyQt6时 Qt Designer通常会一起被安装。 文章目录 使用Qt Designer设计U…