00. 目录
文章目录
- 00. 目录
- 01. LwIP简介
- 02. STA模式简介
- 03. API描述
- 3.1 RegisterWifiEvent
- 3.2 EnableWifi
- 3.3 DisableWifi
- 3.4 IsWifiActive
- 3.5 Scan
- 3.6 GetScanInfoList
- 3.7 AddDeviceConfig
- 3.8 ConnectTo
- 3.9 Disconnect
- 3.10 GetLinkedInfo
- 3.11 GetDeviceMacAddress
- 3.12 netifapi_netif_find
- 3.13 netifapi_netif_set_addr
- 3.14 netifapi_netif_get_addr
- 3.15 netifapi_dhcps_start
- 3.16 netifapi_netif_common
- 3.17 netifapi_dhcp_start
- 3.18 netifapi_dhcp_is_bound
- 04. 硬件设计
- 05. 模块参考
- 06. 软件设计
- 07. 实验现象
- 08. 附录
01. 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已经足够了。
02. STA模式简介
在 Hi3861 中,STA(Station)模式,即客户端模式,是指 Hi3861 设备作为无线网络中的一个终端节点,连接到接入点(AP,Access Point)上,从而接入到无线网络中。在 STA 模式下,Hi3861 设备可以访问互联网、与其他设备共享数据等,实现各种物联网应用。
Wi-Fi 是我们平时接触最多的网络,本章我们讲解开发板如何接入到到 Wi-Fi网络中。STA:全称 Station,工作站,所有的 wifi 设备都被称为 station,比如手机、电脑等,具有接入点功能的 wifi 设备也是一个 station。
03. API描述
foundation\communication\interfaces\kits\wifi_lite\wifiservice\wifi_device.h
vendor\hisi\hi3861\hi3861\third_party\lwip_sack\include\lwip/netifapi.h
vendor\hisi\hi3861\hi3861\third_party\lwip_sack\include\lwip/dhcp.h
3.1 RegisterWifiEvent
/**
* @brief Registers a callback for a specified Wi-Fi event.
*
* The registered callback will be invoked when the Wi-Fi event defined in {@link WifiEvent} occurs. \n
*
* @param event Indicates the event for which the callback is to be registered.
* @return Returns {@link WIFI_SUCCESS} if the callback is registered successfully; returns an error code defined
* in {@link WifiErrorCode} otherwise.
* @since 7
*/
WifiErrorCode RegisterWifiEvent(WifiEvent *event)
功能:
用于注册特定 WiFi 事件回调函数的 API。这个函数允许开发者为 WiFi 模块上发生的特定事件(如连接状态改变、扫描状态改变、热点状态改变等)注册回调函数。当这些事件发生时,系统会自动调用相应的回调函数,以便开发者能够处理这些事件。
参数:
event:事件
返回值:
0 成功,其他值错误信息
WifiEvent类型
/**
* @brief Represents the pointer to a Wi-Fi event callback for station and hotspot connection, disconnection, or scan.
*
*
* If you do not need a callback, set the value of its pointer to <b>NULL</b>. \n
*
* @since 7
*/
typedef struct {
/** Connection state change */
void (*OnWifiConnectionChanged)(int state, WifiLinkedInfo *info);
/** Scan state change */
void (*OnWifiScanStateChanged)(int state, int size);
/** Hotspot state change */
void (*OnHotspotStateChanged)(int state);
/** Station connected */
void (*OnHotspotStaJoin)(StationInfo *info);
/** Station disconnected */
void (*OnHotspotStaLeave)(StationInfo *info);
} WifiEvent;
3.2 EnableWifi
/**
* @brief Enables the station mode.
*
* @return Returns {@link WIFI_SUCCESS} if the station mode is enabled; returns an error code defined in
* {@link WifiErrorCode} otherwise.
* @since 7
*/
WifiErrorCode EnableWifi(void)
功能:
启用STA模式
参数:
返回值:
0 成功,其他值错误信息
3.3 DisableWifi
/**
* @brief Disables the station mode.
*
* @return Returns {@link WIFI_SUCCESS} if the station mode is disabled; returns an error code defined in
* {@link WifiErrorCode} otherwise.
* @since 7
*/
WifiErrorCode DisableWifi(void)
功能:
禁用STA模式
参数:
返回值:
0 成功,其他值错误信息
3.4 IsWifiActive
/**
* @brief Checks whether the station mode is enabled.
*
* @return Returns {@link WIFI_STA_ACTIVE} if the station mode is enabled; returns {@link WIFI_STA_NOT_ACTIVE}
* otherwise.
* @since 7
*/
int IsWifiActive(void)
功能:
检查Wifi STA模式是否启用
参数:
返回值:
WIFI_STA_ACTIVE - 已启用;WIFI_STA_NOT_ACTIVE - 未启用
3.5 Scan
/**
* @brief Starts a Wi-Fi scan.
*
* @return Returns {@link WIFI_SUCCESS} if the Wi-Fi scan is started; returns an error code defined in
* {@link WifiErrorCode} otherwise.
* @since 7
*/
WifiErrorCode Scan(void)
功能:
开启扫描热点信息
参数:
返回值:
WIFI_SUCCESS 成功,非 0 错误值
3.6 GetScanInfoList
/**
* @brief Obtains an array of hotspots detected in a Wi-Fi scan.
*
* The array of hotspots can be obtained only after the Wi-Fi scan is complete. \n
*
* @param result Indicates the array of hotspots detected in a Wi-Fi scan. The array is requested and released by the
* caller. The value must be greater than or equal to {@link WIFI_SCAN_HOTSPOT_LIMIT}.
* @param size Indicates the size of the array.
* @return Returns {@link WIFI_SUCCESS} if the array of hotspots detected in the Wi-Fi scan is obtained; returns an
* error code defined in {@link WifiErrorCode} otherwise.
* @since 7
*/
WifiErrorCode GetScanInfoList(WifiScanInfo *result, unsigned int *size)
功能:
获取所有扫描到的热点列表
参数:
result:被扫描到的热点数组
size:热点数组大小
返回值:
0 成功,非 0 错误值
3.7 AddDeviceConfig
/**
* @brief Adds a specified hotspot configuration for connecting to a hotspot.
*
* This function generates a <b>networkId</b>. \n
*
* @param config Indicates the hotspot configuration to add.
* @param result Indicates the generated <b>networkId</b>. Each <b>networkId</b> matches a hotspot configuration.
* @return Returns {@link WIFI_SUCCESS} if the specified hotspot configuration is added; returns an error code defined
* in {@link WifiErrorCode} otherwise.
* @since 7
*/
WifiErrorCode AddDeviceConfig(const WifiDeviceConfig *config, int *result)
功能:
配置连接到热点信息
参数:
config:热点配置参数;包括 WIFI 热点名称、密码、频率安全类型等
result:表示生成的networkId。每个networkId匹配一个热点配置
返回值:
0 成功,非 0 错误值
WifiDeviceConfig类型
/**
* @brief Represents the Wi-Fi station configuration used to connect to a specified Wi-Fi device.
*
* @since 7
*/
typedef struct WifiDeviceConfig {
/** Service set ID (SSID). For its length, see {@link WIFI_MAX_SSID_LEN}. */
char ssid[WIFI_MAX_SSID_LEN];
/** Basic service set ID (BSSID). For its length, see {@link WIFI_MAC_LEN}. */
unsigned char bssid[WIFI_MAC_LEN];
/** Key. For its length, see {@link WIFI_MAX_KEY_LEN}. */
char preSharedKey[WIFI_MAX_KEY_LEN];
/** Security type. It is defined in {@link WifiSecurityType}. */
int securityType;
/** Allocated <b>networkId</b> */
int netId;
/** Frequency */
unsigned int freq;
/** PSK type, see {@link WifiPskType}. */
int wapiPskType;
/** IP address type */
IpType ipType;
/** Static IP address */
IpConfig staticIp;
/* 1 for hidden config */
int isHiddenSsid;
} WifiDeviceConfig;
ssid:为热点名称,默认长度最大为 33;
bssid:基本服务集标识符(BSSID,Basic Service Set Identifier)是一个 48 位的 MAC 地址,用于在 IEEE 802.11 无线网络中唯一标识一个基本服务集(BSS)。在 Wi-Fi 网络中,BSSID 通常指的是接入点(AP,Access Point)的MAC 地址,但也可以是由 AP 为特定网络或虚拟接入点(VAP)分配的独特 MAC 地址。
preSharedKey:WIFI 共享密钥,最大长度为 65;
securityType:为 WIFI 安全类型
/**
* @brief Enumerates Wi-Fi security types.
*
* @since 7
*/
typedef enum {
/** Invalid security type */
WIFI_SEC_TYPE_INVALID = -1,
/** Open */
WIFI_SEC_TYPE_OPEN,
/** Wired Equivalent Privacy (WEP) */
WIFI_SEC_TYPE_WEP,
/** Pre-shared key (PSK) */
WIFI_SEC_TYPE_PSK,
/** Simultaneous Authentication of Equals (SAE) */
WIFI_SEC_TYPE_SAE,
} WifiSecurityType;
netId:分配的网络 ID;
freq:频率,指定了 Wi-Fi 网络使用的无线频道频率。这对于选择或设置Wi-Fi 网络的工作频道很重要。
wapiPskType:PSK 类型,与 securityType 类似,但更具体地针对 WAPI(无线局域网鉴别和保密基础结构)安全协议中的 PSK 类型。WifiPskType 是一个枚举类型,定义了不同类型的 PSK。
/**
* @brief Enumerates psk encryption types.
*
* @since 7
*/
typedef enum {
/** Indicates that the ascii type of psk encryption type */
WIFI_PSK_TYPE_ASCII = 0,
/** Indicates that the hex type of psk encryption type */
WIFI_PSK_TYPE_HEX,
} WifiPskType;
ipType:IP 地址类型,指定了设备在网络中使用的 IP 地址类型。
/**
* @brief Enumerates IP address types for the Wi-Fi device.
*
* @since 3
*/
typedef enum {
/** Static IP address */
STATIC_IP,
/** IP address dynamically assigned by DHCP */
DHCP,
/** Unknown IP address type */
UNKNOWN
} IpType;
staticIp:静态 IP 配置,包含设备的静态 IP 地址、子网掩码、默认网关和DNS 服务器等信息。这个结构体(IpConfig)用于在需要静态 IP 配置的场景中。
/**
* @brief Defines the IP configuration of the Wi-Fi device.
*
* The IP configuration is mainly used for connecting to the device. \n
*
* @since 3
*/
typedef struct {
/** IP address of the Wi-Fi device */
unsigned int ipAddress;
/** Gateway of the Wi-Fi device */
unsigned int gateway;
/** DNS server addresses for the Wi-Fi device */
unsigned int dnsServers[WIFI_MAX_DNS_NUM];
/** Subnet mask of the Wi-Fi device */
unsigned int netmask;
} IpConfig;
isHiddenSsid:隐藏 SSID 标志,一个整数。如果设置为 1,则表示这个 Wi-Fi网络的SSID 是隐藏的,不会在无线扫描结果中显示,客户端需要知道确切的 SSID才能连接。
3.8 ConnectTo
/**
* @brief Connects to a hotspot matching a specified <b>networkId</b>.
*
* Before calling this function, call {@link AddDeviceConfig} to add a hotspot configuration. \n
*
* @param networkId Indicates the <b>networkId</b> matching the target hotspot.
* @return Returns {@link WIFI_SUCCESS} if the hotspot is connected; returns an error code defined in
* {@link WifiErrorCode} otherwise.
* @since 7
*/
WifiErrorCode ConnectTo(int networkId)
功能:
获取指定的热点配置
参数:
networkId:表示与目标热点匹配的网络id
返回值:
0 成功,非 0 错误值
3.9 Disconnect
/**
* @brief Disconnects this Wi-Fi connection.
*
* @return Returns {@link WIFI_SUCCESS} if this Wi-Fi connection is disconnected; returns an error code defined
* in {@link WifiErrorCode} otherwise.
* @since 7
*/
WifiErrorCode Disconnect(void)
功能:
断开Wifi连接
参数:
返回值:
0 成功,非 0 错误值
3.10 GetLinkedInfo
/**
* @brief Obtains information about the connected hotspot.
*
* @param result Indicates the information about the connected hotspot.
* @return Returns {@link WIFI_SUCCESS} if the information about the connected hotspot is obtained; returns an error
* code defined in {@link WifiErrorCode} otherwise.
* @since 7
*/
WifiErrorCode GetLinkedInfo(WifiLinkedInfo *result)
功能:
获取连接热点信息
参数:
result:表示目前连接中的热点信息
返回值:
0 成功,非 0 错误值
3.11 GetDeviceMacAddress
/**
* @brief Obtains the MAC address of this device.
*
* @param result Indicates the MAC address of this device. It is a char array whose length is 6.
* @return Returns {@link WIFI_SUCCESS} if the MAC address of this device is obtained; returns an error code defined
* in {@link WifiErrorCode} otherwise.
* @since 7
*/
WifiErrorCode GetDeviceMacAddress(unsigned char *result)
功能:
获取设备的MAC地址
参数:
result:设备的MAC地址,6字节长度数组
返回值:
0 成功,非 0 错误值
3.12 netifapi_netif_find
/**
* @ingroup Threadsafe_Network_Interfaces
* @brief
* This thread-safe interface is called when to find interface
*/
struct netif *netifapi_netif_find(const char *name);
功能:
获取网络接口用于IP操作
参数:
name:网络接口名称
返回值:
网络接口
3.13 netifapi_netif_set_addr
/*
* Func Name: netifapi_netif_set_addr
*/
/**
* @ingroup Threadsafe_Network_Interfaces
* This is a thread safe API, used to change IP_add configuration for a
* network interface (including netmask and default gateway).
* It is recommended to use this API instead of netif_set_addr().
* @param[in] netif Indicates the network interface to change.
* @param[in] ipaddr Indicates the new IP address.
* @param[in] netmask Indicates the new network mask.
* @param[in] gw Indicates the new default gateway IP address.
* @returns
* 0 : On success. \n
* Negative value : On failure.
*
* @par Related Topics
* netif_set_addr()
*
* @note
* - If NULL is passed to ipaddr/netmask/gw, then 0.0.0.0 will be set as the corresponding address on the netif
*/
err_t netifapi_netif_set_addr(struct netif *netif,
const ip4_addr_t *ipaddr,
const ip4_addr_t *netmask,
const ip4_addr_t *gw)
功能:
设置SoftAp的DHCP服务器的IP地址、子网掩码和网关参数
参数:
netif:被改变的网络接口
ipaddr:新的IP地址
netmask:新的子网掩码
gw:新的网关地址
返回值:
0 成功,非 0 错误值
3.14 netifapi_netif_get_addr
/*
* Func Name: netifapi_netif_get_addr
*/
/**
* @ingroup Threadsafe_Network_Interfaces
*
* @brief
*
* This is a thread safe API, used to get IP_add configuration for a network interface
* (including netmask and default gateway).
* It is recommended to use this API instead of netif_get_addr()
*
* @param[in] netif Indicates the network interface to get.
* @param[in] ipaddr Indicates the IP address.
* @param[in] netmask Indicates the network mask.
* @param[in] gw Indicates the default gateway IP address.
*
* @returns
* 0 : On success \n
* Negative value : On failure \n
*
* @par Related Topics
* netif_get_addr()
*
* @note
* - netmask and/or gw can be passed NULL, if these details about the netif are not needed
*/
err_t netifapi_netif_get_addr(struct netif *netif,
ip4_addr_t *ipaddr,
ip4_addr_t *netmask,
ip4_addr_t *gw)
功能:
获取网络接口的IP地址、子网掩码和网关参数
参数:
netif:改变的网络接口
ipaddr:IP地址
netmask:子网掩码
gw:网关地址
返回值:
0 成功,非 0 错误值
3.15 netifapi_dhcps_start
/*
Func Name: netifapi_dhcps_start
*/
/**
* @ingroup Threadsafe_DHCP_Interfaces
* @brief
* This API is used to start DHCPv4 Server on the netif. This should be called only
once when the DHCPv4 Server needs to be started on the netif.
*
* @param[in] netif Indicates the lwIP network interface.
* @param[in] start_ip Indicates the starting IP address of the DHCPv4 address pool.
* @param[in] ip_num Indicates the number of IP addresses that need to be in the pool.
*
* @par Return values
* ERR_OK: On success \n
* ERR_ARG: On passing invalid arguments. \n
* ERR_MEM: On trying to start a DHCPv4 server on a netif where DHCPv4 server is already running \n
*
* @par Note
* - If the DHCPv4 scope (Address Pool) use the default configuration, then both
* start_ip and ip_num must be NULL.
* - If the DHCPv4 scope (Address Pool) is manually configured, then both start_ip And
* ip_num must not be NULL.
* - In case of default DHCPv4 scope (Address Pool) configuration, the default start IP is ((netif_ip & netif_mask) + 2).
* - In case of manual DHCPv4 scope (Address Pool) configuration, start_ip should be in the same subnet of the netif.
* - The total number of addresses in the DHCPv4 Scope will be the minimum of non-zero ip_num & LWIP_DHCPS_MAX_LEASE.
* - No matter in case of default configuration or manual configuration, if the IP address of the DHCPv4 server lies in
* the range of address pool, then the total number of addresses available in the pool for clients will be decreased
* one, since one of the addresses in the pool has been taken up by the DHCPv4 Server and only others will be available
* to the DHCPv4 Clients. \n
* - In case of manual DHCPv4 scope configuration, if ip_start+ip_num goes beyond the maximum host IP, then the total
* number of addresses in the pool will be from [ip_start, maximum host IP] only.
* e.g. netmask is 255.255.255.0 and so maximum host IP is x.y.z.254, if ip_start+ip_num goes beyond x.y.z.254,
* then the total number of addresses in the pool will be from [ip_start, x.y.z.254] only.
*/
err_t netifapi_dhcps_start(struct netif *netif, char *start_ip, u16_t ip_num)
功能:
启动SoftAp的DHCP服务器
参数:
netif:LwIP网络接口
start_ip:DHCP起始地址
ip_num:IP池数量
返回值:
0 成功,非 0 错误值
3.16 netifapi_netif_common
/**
* @ingroup Threadsafe_Network_Interfaces
* @brief
* This API is used to call all netif related APIs in a thread safe manner. The netif related APIs must be
* of prototype to receive only struct netif* as argument and return type can of type err_t or void. You
* must pass either viodfunc or errtfunc.
*
* @param[in] netif Indicates the network interface to be passed as argument.
* @param[in] voidfunc Callback with return type of void, will be called if errtfunc is NULL.
* @param[in] errtfunc Callback with return type of err_t.
*
* @returns
* 0 : On success. \n
* Negative value : On failure.
*
* @note
* The prototype for netifapi_void_fn and netifapi_errt_fn are as follows:\n
* typedef void (*netifapi_void_fn)(struct netif *netif); \n
* typedef err_t (*netifapi_errt_fn)(struct netif *netif); \n
*/
err_t netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc,
netifapi_errt_fn errtfunc)
功能:
用于以线程安全的方式调用所有与netif相关的API
参数:
netif:LwIP网络接口
voidfunc:如果errtfunc为NULL,将调用返回类型为void的回调
errtfunc:返回类型为err_t的回调
返回值:
0 成功,非 0 错误值
3.17 netifapi_dhcp_start
/** @ingroup Threadsafe_DHCP_Interfaces
* @brief
* This interface is used to start DHCP negotiation for a network interface. If no DHCP client instance
* is attached to this interface, a new client is created first. If a DHCP client instance is already
* present, it restarts negotiation. It is the thread-safe way for calling dhcp_start in the user space.
* */
err_t netifapi_dhcp_start(struct netif *netif);
功能:
启动DHCP, 获取IP
参数:
netif:LwIP网络接口
返回值:
0 成功,非 0 错误值
3.18 netifapi_dhcp_is_bound
/** @ingroup Threadsafe_DHCP_Interfaces
* @brief
* This interface is used to get the DHCP negotiation status for a network interface.
* It is the thread-safe way for calling dhcp_is_bound() in the user space.
*/
err_t netifapi_dhcp_is_bound(struct netif *netif)
功能:
检查指定的网络接口(netif)是否已经通过 DHCP成功绑定了 IP 地址
参数:
netif:LwIP网络接口
返回值:
0 成功,非 0 错误值
04. 硬件设计
由于 Hi3861 内置 WIFI 功能,所以直接在开发板上使用即可,无需额外连接。
05. 模块参考
5.1 注册wifi事件的回调函数
通过 RegisterWifiEvent
接口向系统注册扫描状态监听函数,用于接收扫描状态通知,如扫描动作是否完成等
printf("<--Wifi Init-->\r\n");
g_wifiEventHandler.OnWifiScanStateChanged = OnWifiScanStateChangedHandler;
g_wifiEventHandler.OnWifiConnectionChanged = OnWifiConnectionChangedHandler;
g_wifiEventHandler.OnHotspotStaJoin = OnHotspotStaJoinHandler;
g_wifiEventHandler.OnHotspotStaLeave = OnHotspotStaLeaveHandler;
g_wifiEventHandler.OnHotspotStateChanged = OnHotspotStateChangedHandler;
error = RegisterWifiEvent(&g_wifiEventHandler);
if (error != WIFI_SUCCESS)
{
printf("register wifi event fail!\r\n");
}
else
{
printf("register wifi event succeed!\r\n");
}
5.2 绑定连接状态监听
OnWifiConnectionChangedHandler
用于绑定连接状态监听函数,该回调函数有两个参数 state 和 info。
- state表示扫描状态,取值为0和1,1表示热点连接成功;
- info表示Wi-Fi连接信息,包含以下参数;
名字 | 描述 |
---|---|
ssid [WIFI_MAX_SSID_LEN] | 连接的热点名称. |
bssid [WIFI_MAC_LEN] | MAC地址. |
rssi | 接收信号强度(RSSI). |
connState | Wifi连接状态. |
disconnectedReason | Wi-Fi断开的原因. |
static void OnWifiConnectionChangedHandler(int state, WifiLinkedInfo *info)
{
(void)info;
if (state > 0)
{
g_ConnectSuccess = 1;
printf("callback function for wifi connect\r\n");
}
else
{
printf("connect error,please check password\r\n");
}
return;
}
5.3 Wi-Fi启动阶段
调用 EnableWifi
接口,使能 Wifi。
调用 AddDeviceConfig
接口,配置连接的热点信息。
//使能WIFI
if (EnableWifi() != WIFI_SUCCESS)
{
printf("EnableWifi failed, error = %d\n", error);
return -1;
}
//判断WIFI是否激活
if (IsWifiActive() == 0)
{
printf("Wifi station is not actived.\n");
return -1;
}
5.4 Wi-Fi连接阶段
调用 ConnectTo
接口,连接到指定networkId的热点。
调用 WaitConnectResult
接口等待,该函数中会有15s的时间去轮询连接成功标志位
g_ConnectSuccess
,当 g_ConnectSuccess
为 1 时退出等待。
//连接指定的WiFi热点
for(uint8_t i = 0; i < ssid_count; i++)
{
if (strcmp(SELECT_WIFI_SSID, info[i].ssid) == 0)
{
int result;
printf("Select:%3d wireless, Waiting...\r\n", i+1);
//拷贝要连接的热点信息
strcpy(select_ap_config.ssid, info[i].ssid);
strcpy(select_ap_config.preSharedKey, SELECT_WIFI_PASSWORD);
select_ap_config.securityType = SELECT_WIFI_SECURITYTYPE;
if (AddDeviceConfig(&select_ap_config, &result) == WIFI_SUCCESS)
{
if (ConnectTo(result) == WIFI_SUCCESS && WaitConnectResult() == 1)
{
printf("WiFi connect succeed!\r\n");
g_lwip_netif = netifapi_netif_find(SELECT_WLAN_PORT);
break;
}
}
}
if(i == ssid_count-1)
{
printf("ERROR: No wifi as expected\r\n");
while(1) osDelay(100);
}
}
5.5 Wi-Fi“Got IP”阶段
调用 netifapi_netif_find
接口,获取 netif 用于 IP 操作
调用 dhcp_start
接口,启动 DHCP, 获取 IP
//启动DHCP
if (g_lwip_netif)
{
dhcp_start(g_lwip_netif);
printf("begain to dhcp");
}
//等待DHCP
for(;;)
{
if(dhcp_is_bound(g_lwip_netif) == ERR_OK)
{
printf("<-- DHCP state:OK -->\r\n");
//打印获取到的IP信息
netifapi_netif_common(g_lwip_netif, dhcp_clients_info_show, NULL);
break;
}
printf("<-- DHCP state:Inprogress -->\r\n");
osDelay(100);
}
5.6 完整代码
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "lwip/netif.h"
#include "lwip/netifapi.h"
#include "lwip/ip4_addr.h"
#include "lwip/api_shell.h"
#include "cmsis_os2.h"
#include "hos_types.h"
#include "wifi_device.h"
#include "wifiiot_errno.h"
#include "ohos_init.h"
#define DEF_TIMEOUT 15
static void WiFiInit(void);
static int WaitConnectResult(void);
static void OnWifiConnectionChangedHandler(int state, WifiLinkedInfo *info);
static void OnHotspotStaJoinHandler(StationInfo *info);
static void OnHotspotStateChangedHandler(int state);
static void OnHotspotStaLeaveHandler(StationInfo *info);
static int g_ConnectSuccess = 0;
WifiEvent g_wifiEventHandler = {0};
WifiErrorCode error;
#define SELECT_WLAN_PORT "wlan0"
#define SELECT_WIFI_SSID "uplooking"
#define SELECT_WIFI_PASSWORD "88888888"
#define SELECT_WIFI_SECURITYTYPE WIFI_SEC_TYPE_PSK
static BOOL WifiSTATask(void)
{
WifiScanInfo *info = NULL;
unsigned int size = WIFI_SCAN_HOTSPOT_LIMIT;
static struct netif *g_lwip_netif = NULL;
WifiDeviceConfig select_ap_config = {0};
osDelay(200);
printf("<--System Init-->\r\n");
//初始化WIFI
WiFiInit();
//使能WIFI
if (EnableWifi() != WIFI_SUCCESS)
{
printf("EnableWifi failed, error = %d\n", error);
return -1;
}
//判断WIFI是否激活
if (IsWifiActive() == 0)
{
printf("Wifi station is not actived.\n");
return -1;
}
int result;
//拷贝要连接的热点信息
strcpy(select_ap_config.ssid, SELECT_WIFI_SSID);
strcpy(select_ap_config.preSharedKey, SELECT_WIFI_PASSWORD);
select_ap_config.securityType = SELECT_WIFI_SECURITYTYPE;
if (AddDeviceConfig(&select_ap_config, &result) == WIFI_SUCCESS)
{
if (ConnectTo(result) == 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);
printf("begain to dhcp");
}
//等待DHCP
for(;;)
{
if(dhcp_is_bound(g_lwip_netif) == ERR_OK)
{
printf("<-- DHCP state:OK -->\r\n");
//打印获取到的IP信息
netifapi_netif_common(g_lwip_netif, dhcp_clients_info_show, NULL);
break;
}
printf("<-- DHCP state:Inprogress -->\r\n");
osDelay(100);
}
//执行其他操作
for(;;)
{
osDelay(100);
}
}
static void WiFiInit(void)
{
printf("<--Wifi Init-->\r\n");
g_wifiEventHandler.OnWifiConnectionChanged = OnWifiConnectionChangedHandler;
g_wifiEventHandler.OnHotspotStaJoin = OnHotspotStaJoinHandler;
g_wifiEventHandler.OnHotspotStaLeave = OnHotspotStaLeaveHandler;
g_wifiEventHandler.OnHotspotStateChanged = OnHotspotStateChangedHandler;
error = RegisterWifiEvent(&g_wifiEventHandler);
if (error != WIFI_SUCCESS)
{
printf("register wifi event fail!\r\n");
}
else
{
printf("register wifi event succeed!\r\n");
}
}
static void OnWifiConnectionChangedHandler(int state, WifiLinkedInfo *info)
{
(void)info;
if (state > 0)
{
g_ConnectSuccess = 1;
printf("callback function for wifi connect\r\n");
}
else
{
printf("connect error,please check password\r\n");
}
return;
}
static void OnHotspotStaJoinHandler(StationInfo *info)
{
(void)info;
printf("STA join AP\n");
return;
}
static void OnHotspotStaLeaveHandler(StationInfo *info)
{
(void)info;
printf("HotspotStaLeave:info is null.\n");
return;
}
static void OnHotspotStateChangedHandler(int state)
{
printf("HotspotStateChanged:state is %d.\n", state);
return;
}
static int WaitConnectResult(void)
{
int ConnectTimeout = DEF_TIMEOUT;
while (ConnectTimeout > 0)
{
sleep(1);
ConnectTimeout--;
if (g_ConnectSuccess == 1)
{
printf("WaitConnectResult:wait success[%d]s\n", (DEF_TIMEOUT - ConnectTimeout));
break;
}
}
if (ConnectTimeout <= 0)
{
printf("WaitConnectResult:timeout!\n");
return 0;
}
return 1;
}
static void WifiClientSTA(void)
{
osThreadAttr_t attr;
attr.name = "WifiSTATask";
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = 10240;
attr.priority = 24;
if (osThreadNew((osThreadFunc_t)WifiSTATask, NULL, &attr) == NULL)
{
printf("Falied to create WifiSTATask!\n");
}
}
APP_FEATURE_INIT(WifiClientSTA);
06. 软件设计
bsp_wifi.h
#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);
#endif /* __BSP_WIFI_H__ */
bsp_wifi.c
#include <unistd.h>
#include <stdio.h>
#include <string.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"
#include "bsp_wifi.h"
//WIFI通道
#define WIFI_CHANNEL 5
#define DEF_TIMEOUT 15
#define SELECT_WLAN_PORT "wlan0"
//STA 连接状态结果
int g_ConnectState = 0;
struct netif *g_lwip_netif = NULL;
//----------------------------WIFI AP----------------------------------
/** Hotspot state change */
void OnHotspotStateChangedCallbak(int state)
{
printf("OnHotspotStateChangedCallbak: state is %d.\n", state);
if (WIFI_HOTSPOT_ACTIVE == state)
{
printf("wifi hotspot active\n");
}
else
{
printf("wifi hotspot noactive\n");
}
}
/** Station connected */
void OnHotspotStaJoinCallbak(StationInfo *info)
{
static char macAddr[32] = {0};
static unsigned char *mac = NULL;
if (NULL == info)
{
printf("OnHotspotStaJoinCallbak is NULL\n");
}
else
{
mac = info->macAddress;
snprintf(macAddr, sizeof(macAddr), "%02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
printf("OnHotspotStaJoinCallbak: mac: %s reason: %d\n", macAddr, info->disconnectedReason);
}
}
/** Station disconnected */
void OnHotspotStaLeaveCallbak(StationInfo *info)
{
static char macAddr[32] = {0};
static unsigned char *mac = NULL;
if (NULL == info)
{
printf("OnHotspotStaLeaveCallbak is NULL\n");
}
else
{
mac = info->macAddress;
snprintf(macAddr, sizeof(macAddr), "%02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
printf("OnHotspotStaLeaveCallbak: mac: %s reason: %d\n", macAddr, info->disconnectedReason);
}
}
//创建Wifi热点
WifiErrorCode WiFi_createHotSpots(const char *ssid, const char *psk)
{
WifiErrorCode ret;
static WifiEvent event;
static HotspotConfig config;
printf("Start Initialization of WiFI AP Mode\r\n");
//注册WIFI事件的回调函数
event.OnHotspotStaJoin = OnHotspotStaJoinCallbak;
event.OnHotspotStaLeave = OnHotspotStaLeaveCallbak;
event.OnHotspotStateChanged =OnHotspotStateChangedCallbak;
ret = RegisterWifiEvent(&event);
if (WIFI_SUCCESS != ret)
{
printf("RegisterWifiEvent failed....\n");
return -1;
}
printf("RegisterWifiEvent OK .....\n");
//设置热点
strcpy(config.ssid, ssid);
strcpy(config.preSharedKey, psk);
config.band = HOTSPOT_BAND_TYPE_2G;
config.channelNum = WIFI_CHANNEL;
config.securityType = WIFI_SEC_TYPE_PSK;
ret = SetHotspotConfig(&config);
if (WIFI_SUCCESS != ret)
{
printf("SetHotspotConfig failed....\n");
return -1;
}
printf("SetHotspotConfig OK....\n");
//启动WIFI AP模式
ret = EnableHotspot();
if (WIFI_SUCCESS != ret)
{
printf("EnableHotspot failed...\n");
return -1;
}
printf("EnableHotspot OK ....\n");
//检查热点模式是否使能
if (WIFI_HOTSPOT_ACTIVE != IsHotspotActive())
{
printf("IsHotspotActive failed....\n");
return -1;
}
printf("IsHotspotActive OK .....\n");
}
//----------------------------WIFI STA----------------------------------
/** Connection state change */
void staOnWifiConnectionChanged(int state, WifiLinkedInfo *info)
{
if (state > 0)
{
g_ConnectState = 1;
printf("staOnWifiConnectionChanged state: %d\n", state);
}
else
{
printf("staOnWifiConnectionChanged failed state: %d\n", state);
}
}
/** Scan state change */
void staOnWifiScanStateChanged(int state, int size)
{
printf("staOnWifiScanStateChanged state: %d size: %d\n", state, size);
}
/** Hotspot state change */
void staOnHotspotStateChanged(int state)
{
printf("staOnHotspotStateChanged state: %d\n", state);
}
/** Station connected */
void staOnHotspotStaJoin(StationInfo *info)
{
printf("staOnHotspotStaJoin STA Join AP\n");
}
/** Station disconnected */
void staOnHotspotStaLeave(StationInfo *info)
{
printf("staOnHotspotStaLeave..\n");
}
//STA模式 连接WIFI
WifiErrorCode WiFi_connectHotspots(const char *ssid, const char *psk)
{
WifiErrorCode ret;
static WifiEvent event;
static WifiDeviceConfig config;
int result;
int timeout;
printf("---------------WIFI STA Mode------------\n");
//1. 注册WIFI事件
event.OnHotspotStaJoin = staOnHotspotStaJoin;
event.OnHotspotStaLeave = staOnHotspotStaLeave;
event.OnHotspotStateChanged = staOnWifiScanStateChanged;
event.OnWifiConnectionChanged = staOnWifiConnectionChanged;
event.OnWifiScanStateChanged = staOnWifiScanStateChanged;
ret = RegisterWifiEvent(&event);
if (WIFI_SUCCESS != ret)
{
printf("RegisterWifiEvent failed....\n");
return -1;
}
else
{
printf("RegisterWifiEvent OK....\n");
}
//2. 使能WIFI
ret = EnableWifi();
if (WIFI_SUCCESS != ret)
{
printf("EnableWifi failed...\n");
return -1;
}
//3. 判断WIFI是否激活
if (WIFI_STA_ACTIVE != IsWifiActive())
{
printf("IsWifiActive is not actived..\n");
return -1;
}
//4. 配置连接热点信息
strcpy(config.ssid, ssid);
strcpy(config.preSharedKey, psk);
config.securityType = WIFI_SEC_TYPE_PSK;
ret = AddDeviceConfig(&config, &result);
if (WIFI_SUCCESS != ret)
{
printf("AddDeviceConfig failed....\n");
return -1;
}
//5. 连接到热点
if (WIFI_SUCCESS == ConnectTo(result))
{
printf("ConnectTo OK.....\n");
}
else
{
printf("ConnectTo failed....\n");
return -1;
}
//6. 等待连接结果
timeout = DEF_TIMEOUT;
while(timeout > 0)
{
sleep(1);
timeout--;
if (1 == g_ConnectState)
{
printf("Connect to %s OK ....\n", ssid);
break;
}
}
if (timeout <= 0)
{
printf("Connect to %s timeout.....\n", ssid);
return -1;
}
//7. 获取网络接口
g_lwip_netif = netifapi_netif_find(SELECT_WLAN_PORT);
//8. 启动DHCP
if (NULL != g_lwip_netif)
{
dhcp_start(g_lwip_netif);
printf("dhcp_start begin dhcp....\n");
}
//9. 等待DHCP
for (;;)
{
if (dhcp_is_bound(g_lwip_netif) == ERR_OK)
{
//打印获取到的IP信息
netifapi_netif_common(g_lwip_netif, dhcp_clients_info_show, NULL);
break;
}
printf("DHCP IP InProgress.....\n");
sleep(1);
}
//10. 执行其它操作
}
template.c
#include <stdio.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "bsp_wifi.h"
#define TASK_STACK_SIZE 1024
//任务1ID
osThreadId_t task1_id;
osThreadId_t task2_id;
//线程回调入口函数
void task1 (void *argument)
{
//WiFi_createHotSpots("IOT2", "12345678");
WiFi_connectHotspots("IOT", "iot12345678");
while(1)
{
sleep(1);
}
}
/**
* @description: 初始化并创建任务
* @param {*}
* @return {*}
*/
static void template_demo(void)
{
osThreadAttr_t attr;
attr.name = "task1"; //任务名称
attr.attr_bits = osThreadDetached; //分离状态
attr.cb_mem = NULL;
attr.cb_size = 0;
attr.stack_mem = NULL;
attr.stack_size = TASK_STACK_SIZE * 10;
attr.priority = osPriorityNormal;
//创建任务1
task1_id = osThreadNew(task1, NULL, &attr);
if (NULL != task1_id)
{
printf("任务1创建OK task1_id = %d\n", task1_id);
}
}
SYS_RUN(template_demo);
07. 实验现象
实验现象:连接外部 WIFI 热点信号,串口输出对应状态信息