【普中Hi3861开发攻略--基于鸿蒙OS】-- 第 26 章 WIFI实验-AP 建立网络

(1)实验平台:

普中Hi3861鸿蒙物联网WIFI套件https://item.taobao.com/item.htm?id=829136021914(2)资料下载:普中科技-各型号产品资料下载链接


如今物联网市场异常火爆, WIFI 是物联网中非常重要的角色, 现在基本上家家户户都有 WIFI 网络, 通过 WIFI 接入到互联网, 成了智能家居产品普遍的选择。 Hi3861 内部已集成 WIFI 功能, 可以说它就是为 WIFI 无线连接而生的。 本章来学习 WIFI 的 AP 模式建立网络, 使用 Hi3861 开发 WIFI 是非常简单而美妙的,让大家在学习物联网中变的简单有趣。 本章分为如下几部分内容:

[26.1 实验介绍](#26.1 实验介绍)

[26.1.1 实验简介](#26.1.1 实验简介)

[26.1.1.1 网络协议简介](#26.1.1.1 网络协议简介)

[26.1.1.1.1 常用网络协议](#26.1.1.1.1 常用网络协议)

[26.1.1.1.2 网络协议的分层模型](#26.1.1.1.2 网络协议的分层模型)

[26.1.1.1.3 协议层报文间的封装与拆封](#26.1.1.1.3 协议层报文间的封装与拆封)

[26.1.1.2 LwIP 简介](#26.1.1.2 LwIP 简介)

[26.1.1.3 AP 模式简介](#26.1.1.3 AP 模式简介)

[26.1.2 实验目的](#26.1.2 实验目的)

[26.1.3 WIFI 函数使用](#26.1.3 WIFI 函数使用)

[26.1.3.1 RegisterWifiEvent 函数](#26.1.3.1 RegisterWifiEvent 函数)

[26.1.3.2 EnableWifi 函数](#26.1.3.2 EnableWifi 函数)

[26.1.3.3 IsWifiActive 函数](#26.1.3.3 IsWifiActive 函数)

[26.1.3.4 SetHotspotConfig 函数](#26.1.3.4 SetHotspotConfig 函数)

[26.1.3.5 EnableHotspot 函数](#26.1.3.5 EnableHotspot 函数)

[26.1.3.6 IsHotspotActive 函数](#26.1.3.6 IsHotspotActive 函数)

[26.2 硬件设计](#26.2 硬件设计)

[26.3 软件设计](#26.3 软件设计)

[26.4 实验现象](#26.4 实验现象)


26.1 实验介绍

26.1.1 实验简介

Hi3861 是一款集成了 Wi-Fi 功能的微控制器, 而 lwIP(轻量级 IP) 是一个为嵌入式系统设计的开源 TCP/IP 协议栈。 通过使用 lwIP 库, Hi3861 可以实现与外部网络的通信, 包括发送和接收数据包、 处理网络连接等。 因此, Hi3861是基于 lwIP 来实现网络功能的。

26.1.1.1 网络协议简介

26.1.1.1.1 常用网络协议

互联网对人类社会产生的巨大变革, 大家是有目共睹的, 它几乎改变了人类生活的方方面面。 互联网通信的本质是数字通信, 任何数字通信都离不开通信协议的制定, 通信设备只有按照约定的、 统一的方式去封装和解析信息, 才能实现通信。 互联网通信所要遵守的众多协议, 被统称为 TCP/IP。

TCP/IP 是一个协议族, 包含众多的协议。 但对于网络应用开发人员, 可能听到更多的是其中的应用层协议, 比如 HTTP、 FTP、 MQTT 等。

HTTP 协议是 Hyper Text Transfer Protocol(超文本传输协议) 的缩写, HTTP 的应用最为广泛。 比如大家日常使用电脑时的一个常规操作: 打开电脑,打开浏览器, 输入网址, 最后按下回车, 这一刻你就开启了 HTTP 通信。 HTTP协议工作于<客户端-服务端>架构之上, (服务端也称作为服务器端, 除非特别说明, 否则本书出现的"服务端" 即为"服务器端" ) , 浏览器作为 HTTP 客户端通过 URL 向 HTTP 服务端即 WEB 服务器发送所有请求。 Web 服务器根据接收到的请求后, 向客户端发送响应信息。 借助这种浏览器和服务器之间的 HTTP 通信,我们能够足不出户地获得来自世界各个角落的信息。 另外, 网页不仅仅是大型服务器的专利, 在物联网风潮盛行的今天, 许多随处可见的小型设备(空调、 冰箱、插座、 路由器等) 都内嵌网页, 在物理链路畅通的情况下, 用户可以用手机、 平板电脑上的浏览器随时随地监控这些设备。

FTP(File Transfer Protocol) 是文件传输协议的简称。 FTP 是工作在应用层的网络协议。 FTP 使得主机间可以共享文件, 用于在两台设备之间传输文件(双向传输) 。 它也是一个客户端-服务端框架系统。 用户可以通过一个支持 FTP 协议的客户端程序, 连接到在远程主机上的 FTP 服务端程序, 通过客户端程序向服务端程序发出命令, 服务端程序执行用户所发出的命令, 并将执行的结果返回到客户机。 FTP 除了基本的文件上传/下载功能外, 还有目录操作、 权限设置、身份验证机制, 许多网盘的文件传输功能都是基于 FTP 实现的。

在物联网发展的初期, 物联网场景中的设备使用何种应用层协议进行通信一直是备受争议的话题。 很多开发人员习惯了网页的开发模式, 于是经常选择 HTTP作为通信方式。 使用 HTTP 有以下不利因素: HTTP 是一种同步协议, 设备需要等待服务器的响应才可以进行下一步的工作, 然而在设备数量多、 网络不可靠的场景下, 实现同步通信很困难; HTTP 是单向的, 设备只能主动向服务器发出数据,无法被动的接收来自网络的数据, 这不适用于实时控制的场合; HTTP 是有许多帧头和规则的重量级协议, 实现在设备中需要耗费大量的系统资源。 基于上述的形势, MQTT 和 COAP 等轻量级、 异步的通信协议便得到了物联网设备开发商的宠爱, 尤其是 MQTT。 MQTT(消息队列遥测传输) 是 IBM 公司于 1990 年设计并推出的一款通信协议, 于 2014 年正式成为了一个 OASIS 开放标准。 近年来, MQTT的应用呈现出爆炸性的增长势头, 大有一统物联网的趋势。 另外, MQTT 在物联网以外的其他领域也得到了广泛的应用, 比如许多公司在制作手机 APP 时, 会使用 MQTT 来实现消息推送、 即时聊天等功能。

嵌入式设备接入互联网的需求越来越大, 有以下几点原因:

(1)近些年, 各种带网络接入功能的 MCU、 SoC 层出不穷, 开源轻量的 TCP/IP协议栈日趋成熟和完善, 云平台的市场越来越繁荣, 这些因素大大降低了嵌入式设备的入网成本, 也为许多资源受限的低端设备接入互联网提供了可能。

(2) "物联网+" 的风潮日渐盛行, 设备能够被远程监控, 这一点已经成为许多产品的技术要求。

(3) 人们对于设备"智能性" 的追求越来越高, 当今热门的大数据、 图像处理、 语音识别、 机器学习等功能都可以被集成在云端, 成为云平台能提供的服务。 终端设备大多是计算、 存储能力有限的设备, 这些设备如果想要获取"智能",最便捷的办法就是接入云平台, 利用各项云服务。

互联网的基础就是 TCP/IP。 TCP/IP 是一个非常复杂的协议族, 即便我们能把它的设计思想和实现原理都解释得清清楚楚, 你也不见得有时间和精力去学习它, 所以本书的写作重点不在于对 TCP/IP 的解读, 而在于对它的应用。 另外, TCP/IP 的复杂性也决定了它并不是那么简单就能用好的东西, 即便我们只关注应用开发, 也依然需要对它的许多概念和设计思想有所了解, 才能编写出正确、高效、 健壮性好的应用程序。

希望能借本攻略, 让嵌入式开发工程师们以浓厚的兴趣和清晰的视野, 搭上物联网发展的快车。

26.1.1.1.2 网络协议的分层模型

TCP/IP 是一个庞大的协议族, 它是众多网络协议的集合, 包括: ARP、 IP、ICMP、 UDP、 TCP、 DNS、 DHCP、 HTTP、 FTP、 MQTT 等等。 这些协议按照功能, 可以被划分为几个不同的层次, 如下图所示:

我们在上一节中介绍的 HTTP、 FTP、 MQTT, 它们隶属于应用层。 那么 TCP/IP为什么需要分层, 分层又是依靠什么依据呢?

TCP/IP 协议栈中不同协议所完成的功能是不一样的, 某些协议的实现要依赖于其它协议, 依据这种依赖关系, 可以将协议栈分层。 在上图中, 低层协议为相邻的上层协议提供服务, 是上层协议得以实现的基础。

其中, 物理层(PHY) 规定了传输信号所需要的物理电平、 介质特征; 链路层(MAC)规定了数据帧能被网卡接收的条件, 最常见的方式是利用网卡的 MAC 地址, 发送方会在欲发送的数据帧的首部加上接收方网卡的 MAC 地址信息, 接收方只有监听到属于自己的 MAC 地址信息后, 才会去接收并处理该数据; 每台网络设备都应该有自己的网络地址, 网络层规定了主机的网络地址该如何定义, 以及如何在网络地址和 MAC 地址之间进行映射, 即 ARP 协议; 网络层实现了数据包在主机之间的传递, 而一台主机内部可能运行着多个网络程序, 传输层可以区分数据包是属于哪一个应用程序的, 可以说传输层实现了数据包端到端的传递。另外, 数据包在传输过程中可能会出现丢包、 乱序和重复的现象, 网络层并没有提供应对这些错误的机制, 而传输层可以解决这些问题, 如 TCP 协议; 应用层以下的工作完成了数据的传递工作, 应用层则决定了你如何应用和处理这些数据,之所以会有许多的应用层协议, 是因为互联网中传递的数据种类很多、 差异很大、应用场景十分多样。

26.1.1.1.3 协议层报文间的封装与拆封

在这里, 我们以下图简单解释一下在数据的发送和接收过程中, TCP/IP 都做了哪些事。

当用户发送数据时, 将数据向下交给传输层, 这是处于应用层的操作, 应用层可以通过调用传输层的接口来编写特定的应用程序。 而 TCP/IP 协议一般也会包含一些简单的应用程序如 Telnet 远程登录、 FTP 文件传输、 SMTP 邮件传输协议等。 传输层会在数据前面加上传输层首部(此处以 TCP 协议为例, 上图中的传输层首部为 TCP 首部, 也可以是 UDP 首部) , 然后向下交给网络层。 同样地,网络层会在数据前面加上网络层首部(IP 首部) , 然后将数据向下交给链路层,链路层会对数据进行最后一次封装, 即在数据前面加上链路层首部(此处使用以太网接口为例) , 然后将数据交给网卡。 最后, 网卡将数据转换成物理链路上的电平信号, 数据就这样被发送到了网络中。 数据的发送过程, 可以概括为 TCP/IP的各层协议对数据进行封装的过程, 如上图所示。

当设备的网卡接收到某个数据包后, 它会将其放置在网卡的接收缓存中, 并告知 TCP/IP 内核。 然后 TCP/IP 内核就开始工作了, 它会将数据包从接收缓存中取出, 并逐层解析数据包中的协议首部信息, 并最终将数据交给某个应用程序。数据的接收过程与发送过程正好相反, 可以概括为 TCP/IP 的各层协议对数据进行解析的过程。

26.1.1.2 LwIP 简介

LwIP 全名: Light weight IP, 意思是轻量化的 TCP/IP 协议, 是瑞典计算机科学院(SICS)的 Adam Dunkels 开发的一个小型开源的 TCP/IP 协议栈。 LwIP的设计初衷是: 用少量的资源消耗实现一个较为完整的 TCP/IP 协议栈, 其中"完整" 主要指的是 TCP 协议的完整性, 实现的重点是在保持 TCP 协议主要功能的基础上减少对 RAM 的占用。 此外 LwIP 既可以移植到操作系统上运行, 也可以在无操作系统的情况下独立运行。

(1) LwIP 具有主要特性:

1.支持 ARP 协议(以太网地址解析协议) 。

2.支持 ICMP 协议(控制报文协议),用于网络的调试与维护。

3.支持 IGMP 协议(互联网组管理协议),可以实现多播数据的接收。

4.支持 UDP 协议(用户数据报协议)。

5.支持 TCP 协议(传输控制协议), 包括阻塞控制、 RTT 估算、 快速恢复和快速转发。

6.支持 PPP 协议(点对点通信协议) , 支持 PPPoE。

7.支持 DNS(域名解析) 。

8.支持 DHCP 协议, 动态分配 IP 地址。

9.支持 IP 协议, 包括 IPv4、 IPv6 协议, 支持 IP 分片与重装功能, 多网络接口下的数据包转发。

10.支持 SNMP 协议(简单网络管理协议) 。

11.支持 AUTOIP, 自动 IP 地址配置。

12.提供专门的内部回调接口(Raw API), 用于提高应用程序性能。

13.提供可选择的 Socket API、 NETCONN API (在多线程情况下使用) 。

(2) LwIP 在嵌入式中使用有以下优点:

1.资源开销低, 即轻量化。 LwIP 内核有自己的内存管理策略和数据包管理策略, 使得内核处理数据包的效率很高。 另外, LwIP 高度可剪裁, 一切不需要的功能都可以通过宏编译选项去掉。 LwIP 的流畅运行需要 40KB 的代码 ROM 和几十 KB 的 RAM, 这让它非常适合用在内存资源受限的嵌入式设备中。

2.支持的协议较为完整。 几乎支持 TCP/IP 中所有常见的协议, 这在嵌入式设备中早已够用。

3.实现了一些常见的应用程序: DHCP 客户端、 DNS 客户端、 HTTP 服务器、MQTT 客户端、 TFTP 服务器、 SNTP 客户端等等。

4.同时提供了三种编程接口: RAW API、 NETCONN API(注: NETCONN API 即为 Sequential API, 为了统一, 下文均采用 NETCONN API) 和 Socket API。 这三种 API 的执行效率、 易用性、 可移植性以及时空间的开销各不相同, 用户可以根据实际需要, 平衡利弊, 选择合适的 API 进行网络应用程序的开发。

5.高度可移植。 其源代码全部用 C 实现, 用户可以很方便地实现跨处理器、跨编译器的移植。 另外, 它对内核中会使用到操作系统功能的地方进行了抽象,使用了一套自定义的 API, 用户可以通过自己实现这些 API, 从而实现跨操作系统的移植工作。

6.开源、 免费, 用户可以不用承担任何商业风险地使用它。

7.相比于嵌入式领域其它的 TCP/IP 协议栈, 比如 uC-TCP/IP、FreeRTOS-TCP 等, LwIP 的发展历史要更悠久一些, 得到了更多的验证和测试。LwIP 被广泛用在嵌入式网络设备中, 国内一些物联网公司推出的物联网操作系统, 其 TCP/IP 核心就是 LwIP; 物联网知名的 WiFi 模块 ESP8266, 其 TCP/IP固件, 使用的就是 LwIP。

LwIP 尽管有如此多的优点, 但它毕竟是为嵌入式而生, 所以并没有很完整地实现 TCP/IP 协议栈。 相比于 Linux 和 Windows 系统自带的 TCP/IP 协议栈, LwIP 的功能不算完整和强大。 但对于大多数物联网领域的网络应用程序, LwIP已经足够了。

26.1.1.3 AP 模式简介

Hi3861 是一款带 MCU 的 WiFi 芯片, 支持 AP(Access Point) 模式和 Station模式。 在 AP 模式下, Hi3861 可以作为一个 WiFi 热点, 允许其他设备接入, 形成一个 WiFi 局域网。

26.1.2 实验目的

建立好 WiFi 热点之后, 手机可以连接这个节点, 然后在串口调试助手中打印出设备的连接信息。

26.1.3 WIFI 函数使用

26.1.3.1 RegisterWifiEvent 函数

用于注册特定 WiFi 事件回调函数的 API。 这个函数允许开发者为 WiFi 模块上发生的特定事件(如连接状态改变、 扫描状态改变、 热点状态改变等) 注册回调函数。 当这些事件发生时, 系统会自动调用相应的回调函数, 以便开发者能够处理这些事件。 函数原型:

cpp 复制代码
WifiErrorCode RegisterWifiEvent(WifiEvent *event);

参数:

event: 事件

需要定义三个回调函数, 用于处理热点状态变化、 站点加入和站点离开的事件。

返回值: 0 成功, 其他值错误信息;

26.1.3.2 EnableWifi 函数

使能 WIFI, 函数原型:

cpp 复制代码
WifiErrorCode EnableWifi(void);

参数: 无;

返回值: 0 成功, 其他值错误信息;

26.1.3.3 IsWifiActive 函数

判断 WIFI 是否激活, 函数原型:

cpp 复制代码
int IsWifiActive(void);

参数: 无;

返回值: WIFI_STA_ACTIVE 激活, WIFI_STA_NOT_ACTIVE 未激活;

26.1.3.4 SetHotspotConfig 函数

设置指定的热点配置, 函数原型:

cpp 复制代码
WifiErrorCode SetHotspotConfig(const HotspotConfig *config);

参数:

config: 热点配置参数; 包括 WIFI 热点名称、 密码、 频率安全类型等;

ssid: 为热点名称, 默认长度最大为 33;

securityType: 为 WIFI 安全类型,

WIFI_SEC_TYPE_OPEN, 即开放式 Wi-Fi 网络, 是指没有设置任何加密或安全验证措施的无线网络。 在这种类型的网络中, 任何设备都可以直接连接到 Wi-Fi网络, 而无需输入任何密码或进行身份验证。

WIFI_SEC_TYPE_WEP(Wired Equivalent Privacy) 是一种早期的无线网络安全协议, 旨在提供与有线网络相当的安全性。 然而, 随着时间的推移和加密技术的进步, WEP 已被证明存在严重的安全漏洞, 并且不再被推荐用于保护无线网络。

WIFI_SEC_TYPE_PSK(Pre-Shared Key) 是 Wi-Fi 网络中常用的一种安全类型, 它代表预共享密钥模式。 在这种模式下, 网络上的所有客户端都使用相同的密钥(密码) 来访问网络, 这个密钥在网络设置时由管理员设定, 并需要在客户端设备连接到网络时输入。

WIFI_SEC_TYPE_SAE(Simultaneous Authentication of Equals) 是 Wi-Fi Protected Access 3(WPA3) 标准中引入的一种安全类型, 它主要用于增强个人无线网络的安全性。

band: Wi-Fi 热点模式支持的频带,

channelNum: 2.4G 频段 wifi 信道可支持 14 个, 可选为 1-14;

preSharedKey: WIFI 共享密钥, 最大长度为 65;

返回值: 0 成功, 非 0 错误值;

26.1.3.5 EnableHotspot 函数

开启 WIFI 热点模式, 函数原型:

cpp 复制代码
WifiErrorCode EnableHotspot(void);

参数: 无;

返回值: 0 成功, 非 0 错误值;

26.1.3.6 IsHotspotActive 函数

检查 WIFI 热点是否激活, 函数原型:

cpp 复制代码
int IsHotspotActive(void);

参数: 无;

返回值: WIFI_HOTSPOT_ACTIVE 激活, WIFI_HOTSPOT_NOT_ACTIVE 未激活;

26.2 硬件设计

由于 Hi3861 内置 WIFI 功能, 所以直接在开发板上使用即可, 无需额外连接。

26.3 软件设计

将前面章节创建好的工程模板, 复制一份, 重命名为 22_wifi_ap, 如下所示:

然后在如下路径下新建 bsp_wifi.c 和 bsp_wifi.h 文件:

(1) 修改 demo 文件夹下的 BUILD.gn 文件, 如下所示:

(2) 添加工程编译文件路径, 如下所示

(3) 修改 bsp_wifi.c 文件, 代码如下:

cpp 复制代码
/**
 ****************************************************************************************************
 * @file        bsp_wifi.c
 * @author      普中科技
 * @version     V1.0
 * @date        2024-06-05
 * @brief       WIFI AP实验
 * @license     Copyright (c) 2024-2034, 深圳市普中科技有限公司
 ****************************************************************************************************
 * @attention
 *
 * 实验平台:普中-Hi3861
 * 在线视频:https://space.bilibili.com/2146492485
 * 公司网址:www.prechin.cn
 * 购买地址:
 *
 */

#include "bsp_wifi.h"
#include <unistd.h>

#include "wifi_device.h"
#include "wifi_hotspot.h"
#include "lwip/netifapi.h"
#include "lwip/netif.h"
#include "lwip/ip4_addr.h"
#include "lwip/api_shell.h"



#define DEF_TIMEOUT 15
static int g_ConnectSuccess = 0;
#define SELECT_WLAN_PORT "wlan0"
static struct netif *g_lwip_netif = NULL;
static char g_IP_Addr[20] = {"EC800M_4G"}; // 连接wifi热点之后,获取到的IP地址

/**
 * @brief  获取连接WiFi后的本地IP地址
 * @note
 * @retval IP地址-字符串
 */
char *WiFi_GetLocalIP(void)
{
    return g_IP_Addr;
}
/**
 * 获取WiFi的IP地址
 **/
void Sta_GetWiFiIP(struct netif *netif, char *ip)
{
    int ret;
    if (netif == NULL) {
        return;
    }

    ip4_addr_t ipAddr;
    ip4_addr_t netMask;
    ip4_addr_t gateWay;

    ret = netifapi_netif_get_addr(netif, &ipAddr, &netMask, &gateWay);
    if (ret == 0) {
        inet_ntop(AF_INET, &ipAddr, ip, INET_ADDRSTRLEN);
    }
}

#define WIFI_CHANNE 5 // WiFi通道

WifiErrorCode WiFi_createHotspots(const char *ssid, const char *psk)
{
    WifiErrorCode result;
    printf("Start initialization of WiFi hotspots\r\n");

    // 使能WiFi
    result = EnableWifi();
    if (result != WIFI_SUCCESS) {
        printf("Enable WiFi failed. result: %d\r\n", result);
        return result;
    }
    // 判断WiFi是否激活
    result = IsWifiActive();
    if (result != WIFI_STA_ACTIVE) {
        printf("WiFi activation failed. result: %d\r\n", result);
        return result;
    }

    // 设置指定的热点信息
    HotspotConfig hotspotConfig = {0};
    strcpy_s(hotspotConfig.ssid, strlen(ssid) + 1, ssid);
    strcpy_s(hotspotConfig.preSharedKey, strlen(psk) + 1, psk);
    hotspotConfig.securityType = WIFI_SEC_TYPE_PSK;
    hotspotConfig.band = HOTSPOT_BAND_TYPE_2G;
    hotspotConfig.channelNum = WIFI_CHANNE;
    result = SetHotspotConfig(&hotspotConfig);
    if (result != WIFI_SUCCESS) {
        printf("Failed to set WiFi hotspot information. result: %d\r\n", result);
        return result;
    }

    // 开启WiFi热点模式
    result = EnableHotspot();
    if (result != WIFI_SUCCESS) {
        printf("Failed to enable wifi hotspot mode. result: %d\r\n", result);
        return result;
    }

    // 检查WiFi热点是否激活
    result = IsHotspotActive();
    if (result != WIFI_HOTSPOT_ACTIVE) {
        printf("WiFi hotspot activation failed. result: %d\r\n", result);
        return result;
    }

    printf("WiFi hotspot initialized successfully\r\n");

    return WIFI_SUCCESS;
}

// 连接WiFi热点时的状态发生改变的回调函数
static void ConnectionWifiChangedHandler(int state, WifiLinkedInfo *info)
{
    if (info == NULL) {
        printf("WifiConnectionChanged:info is null.\r\n");
    } else {
        if (state == WIFI_STATE_AVALIABLE) {
            g_ConnectSuccess = 1;
        } else {
            g_ConnectSuccess = 0;
        }
    }
}
// 等待连接热点 默认15s的超时时间
static int WaitConnectResult(void)
{
    int ConnectTimeout = DEF_TIMEOUT;
    while (ConnectTimeout > 0) {
        sleep(1);
        ConnectTimeout--;
        printf(".");
        if (g_ConnectSuccess == 1) {
            printf("WaitConnectResult:wait success[%d]s.\r\n", (DEF_TIMEOUT - ConnectTimeout));
            break;
        }
    }
    if (ConnectTimeout <= 0) {
        printf("WaitConnectResult:timeout!.\r\n");
        return 0;
    }

    return 1;
}
WifiErrorCode WiFi_connectHotspots(const char *ssid, const char *psk)
{
    WifiErrorCode result;
    int Timeout = 10; // 超时时间 10s

    printf("Start Connect of WiFi hotspots.\r\n");

    // 使能WiFi
    result = EnableWifi();
    if (result != WIFI_SUCCESS) {
        printf("Enable WiFi failed.\r\n");
        return result;
    }
    // 判断WiFi是否激活
    result = IsWifiActive();
    if (result != WIFI_STA_ACTIVE) {
        printf("WiFi activation failed.\r\n");
        return result;
    }
    // 注册wifi的回调函数
    WifiEvent eventConfig = {0};
    eventConfig.OnWifiConnectionChanged = ConnectionWifiChangedHandler; // WiFi连接的状态改变
    result = RegisterWifiEvent(&eventConfig);
    if (result != WIFI_SUCCESS) {
        printf("Failed to register WiFi callback function.\r\n");
        return result;
    }

    // 连接指定的WiFi热点
    WifiDeviceConfig wifiDeviceConfig = {0};
    int wifiResult = 0;
    strcpy_s(wifiDeviceConfig.ssid, strlen(ssid) + 1, ssid);               // 连接WiFi的名称
    strcpy_s(wifiDeviceConfig.preSharedKey, strlen(psk) + 1, psk);        // WiFi的密码
    wifiDeviceConfig.securityType = WIFI_SEC_TYPE_PSK; // WiFi的安全性
    result = AddDeviceConfig(&wifiDeviceConfig, &wifiResult);
     if ((result == WIFI_SUCCESS) && (ConnectTo(wifiResult) == WIFI_SUCCESS)&& (WaitConnectResult() == 1)) {
        printf("wifi connect succeed!.\r\n");
        g_lwip_netif = netifapi_netif_find(SELECT_WLAN_PORT);
        //启动DHCP
        if (g_lwip_netif) {
            dhcp_start(g_lwip_netif);
        }

        //等待DHCP
        for (;;) {
            if (dhcp_is_bound(g_lwip_netif) == ERR_OK) {
                Sta_GetWiFiIP(g_lwip_netif, g_IP_Addr);
                printf("connect wifi IP addr: %s.\r\n", g_IP_Addr);
                break;
            }
            printf("#");
            Timeout--;
            if (Timeout == 0) {
                // 超时
                return ERROR_WIFI_BUSY;
            }
            sleep(1);
        }
    } else {
        return ERROR_WIFI_BUSY;
     }
    return WIFI_SUCCESS;
}

(4) 修改 bsp_wifi.h 文件, 代码如下:

cpp 复制代码
/**
 ****************************************************************************************************
 * @file        bsp_wifi.h
 * @author      普中科技
 * @version     V1.0
 * @date        2024-06-05
 * @brief       WIFI AP实验
 * @license     Copyright (c) 2024-2034, 深圳市普中科技有限公司
 ****************************************************************************************************
 * @attention
 *
 * 实验平台:普中-Hi3861
 * 在线视频:https://space.bilibili.com/2146492485
 * 公司网址:www.prechin.cn
 * 购买地址:
 *
 */

#ifndef BSP_WIFI_H
#define BSP_WIFI_H

#include "cmsis_os2.h"
#include "hi_io.h"
#include "hi_gpio.h"
#include "wifi_error_code.h"
#include "wifi_device.h"



//函数声明
WifiErrorCode WiFi_createHotspots(const char *ssid, const char *psk);
WifiErrorCode WiFi_connectHotspots(const char *ssid, const char *psk);
char* WiFi_GetLocalIP(void);

#endif

(5) 修改 template.c 文件, 代码如下:

cpp 复制代码
/**
 ****************************************************************************************************
 * @file        template.c
 * @author      普中科技
 * @version     V1.0
 * @date        2024-06-05
 * @brief       WIFI AP实验
 * @license     Copyright (c) 2024-2034, 深圳市普中科技有限公司
 ****************************************************************************************************
 * @attention
 *
 * 实验平台:普中-Hi3861
 * 在线视频:https://space.bilibili.com/2146492485
 * 公司网址:www.prechin.cn
 * 购买地址:
 *
 ****************************************************************************************************
 * 实验现象:使用手机连接开发板产生的热点信号,串口助手输出连接或断开结果信息
 *
 ****************************************************************************************************
 */

#include <stdio.h>
#include <unistd.h>

#include "ohos_init.h"
#include "cmsis_os2.h"

#include "bsp_led.h"
#include "bsp_wifi.h"


//LED任务
osThreadId_t LED_Task_ID; //led任务ID

void LED_Task(void)
{
    led_init();//LED初始化

    while (1) 
    {
        LED(1); 
        usleep(200*1000); //200ms
        LED(0);
        usleep(200*1000); //200ms
    }
}
//LED任务创建
void led_task_create(void)
{
    osThreadAttr_t taskOptions;
    taskOptions.name = "LEDTask";            // 任务的名字
    taskOptions.attr_bits = 0;               // 属性位
    taskOptions.cb_mem = NULL;               // 堆空间地址
    taskOptions.cb_size = 0;                 // 堆空间大小
    taskOptions.stack_mem = NULL;            // 栈空间地址
    taskOptions.stack_size = 1024;           // 栈空间大小 单位:字节
    taskOptions.priority = osPriorityNormal; // 任务的优先级

    LED_Task_ID = osThreadNew((osThreadFunc_t)LED_Task, NULL, &taskOptions); // 创建任务1
    if (LED_Task_ID != NULL)
    {
        printf("ID = %d, Create LED_Task_ID is OK!\n", LED_Task_ID);
    }
}

//控制任务
osThreadId_t WIFI_Task_ID; //任务ID


#define ARRAY_INDEX_0 0
#define ARRAY_INDEX_1 1
#define ARRAY_INDEX_2 2
#define ARRAY_INDEX_3 3
#define ARRAY_INDEX_4 4
#define ARRAY_INDEX_5 5

// 当 有设备连接热点时 触发的回调函数
static void connectingWiFihotspotsCallback(StationInfo *info)
{
    if (info == NULL) {
        printf("HotspotStaJoin:info is null.\n");
    } else {
        printf("New Sta Join.\n");
    }
}
// 当 有设备断开热点时 触发的回调函数
static void disconnectWiFihotspotsCallback(StationInfo *info)
{
    if (info == NULL) {
        printf(" HotspotStaLeave:info is null.\n");
    } else {
        // 打印mac地址
        printf("HotspotStaLeave: macAddress=%02X:%02X:%02X:%02X:%02X:%02X, reason=%d.\n",
               info->macAddress[ARRAY_INDEX_0],
               info->macAddress[ARRAY_INDEX_1],
               info->macAddress[ARRAY_INDEX_2],
               info->macAddress[ARRAY_INDEX_3],
               info->macAddress[ARRAY_INDEX_4],
               info->macAddress[ARRAY_INDEX_5],
               info->disconnectedReason);
    }
}
// 当 热点的状态发生改变时的回调函数
static void changeWiFiHotspotStateCallback(int state)
{
    printf("HotspotStateChanged:state is %d.", state);
    if (state == WIFI_HOTSPOT_ACTIVE) {
        printf("wifi hotspot active.\n");
    } else {
        printf("wifi hotspot noactive.\n");
    }
}

void WIFI_Task(void)
{
    WifiErrorCode result;

    // 注册WiFi事件的回调函数
    WifiEvent wifiEvent = {0};
    wifiEvent.OnHotspotStaJoin = connectingWiFihotspotsCallback;
    wifiEvent.OnHotspotStaLeave = disconnectWiFihotspotsCallback;
    wifiEvent.OnHotspotStateChanged = changeWiFiHotspotStateCallback;
    result = RegisterWifiEvent(&wifiEvent);
    if (result != WIFI_SUCCESS) 
    {
        printf(" Failed to register WiFi hotspot callback function.\n");
        return;
    }

    sleep(1); // 1 s
    
    WiFi_createHotspots("PZ_Hi3861_AP", "pz123456");
    while (1) 
    {
        sleep(1); // 1 s
    }
}
//任务创建
void wifi_task_create(void)
{
    osThreadAttr_t taskOptions;
    taskOptions.name = "wifiTask";       // 任务的名字
    taskOptions.attr_bits = 0;               // 属性位
    taskOptions.cb_mem = NULL;               // 堆空间地址
    taskOptions.cb_size = 0;                 // 堆空间大小
    taskOptions.stack_mem = NULL;            // 栈空间地址
    taskOptions.stack_size = 1024*10;           // 栈空间大小 单位:字节
    taskOptions.priority = osPriorityNormal; // 任务的优先级

    WIFI_Task_ID = osThreadNew((osThreadFunc_t)WIFI_Task, NULL, &taskOptions); // 创建任务
    if (WIFI_Task_ID != NULL)
    {
        printf("ID = %d, WIFI_Task_ID Create OK!\n", WIFI_Task_ID);
    }
}

/**
 * @description: 初始化并创建任务
 * @param {*}
 * @return {*}
 */
static void template_demo(void)
{
    printf("普中-Hi3861开发板--WIFI AP实验\r\n");
    led_task_create();
    wifi_task_create();//任务创建
}
SYS_RUN(template_demo);

26.4 实验现象

将程序下载到开发板内(可参考"2.2.5 程序下载运行"章节) , 打开串口调试助手"\5--开发工具\4-串口调试助手\UartAssist.exe" , 波特率设置为115200, 实验现象: 建立好 WiFi 热点之后, 手机可以连接这个节点, 然后在串口调试助手中打印出设备的连接信息。

相关推荐
普中科技6 小时前
【普中Hi3861开发攻略--基于鸿蒙OS】-- 第 24 章 OLED液晶显示实验
单片机·嵌入式硬件·oled·liteos·hi3861·普中科技
冻结的鱼7 小时前
STM32H5 的 PB14 引脚被意外拉低的问题解析
stm32·单片机·嵌入式硬件
小莞尔7 小时前
【51单片机】【protues仿真】基于51单片机彩灯控制器系统
单片机·嵌入式硬件
文火冰糖的硅基工坊7 小时前
[嵌入式系统-146]:五次工业革命对应的机器人形态的演进、主要功能的演进以及操作系统的演进
前端·网络·人工智能·嵌入式硬件·机器人
老六哥_AI助理指南7 小时前
为什么AI会改变单片机的未来?
人工智能·单片机·嵌入式硬件
点灯小铭7 小时前
基于单片机的智能家居多参数环境监测与联动报警系统设计
单片机·mongodb·毕业设计·智能家居·课程设计·期末大作业
点灯小铭8 小时前
基于单片机与上位机的智能宠物喂食管理系统设计
单片机·嵌入式硬件·毕业设计·课程设计·宠物
Lester_11018 小时前
嵌入式学习笔记 - 瑞萨单片机
单片机·嵌入式硬件
麻辣长颈鹿Sir8 小时前
单片机中的机器周期、指令周期、总线周期的联系和区别
单片机·嵌入式硬件·时钟周期·指令周期·机器周期·总线周期·嵌入式指令时间