diff --git a/README.md b/README.md index 45b9f279..a6a16b7d 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ | TW105 | I2C | i2c_sht30 | [I2C温湿度传感器采集](applications/app/TW105_I2C_sht30/README.md) | 核心板 + 马达扩展板| | TW106 | UART | uart | [UART串口自发自收](applications/app/TW106_UART/README.md) | 核心板 | | TW201 | Module | oled | [OLED显示屏驱动](applications/app/TW201_Module_oled/README.md) | 核心板 + OLED扩展板| +| TW301 | Network | wifista | [WiFi-STA连接演示](applications/app/TW301_Network_wifista/README.md) | 核心板 | ## 五、源码目录简介 diff --git a/applications/app/BUILD.gn b/applications/app/BUILD.gn index c1e3eb97..72e09471 100644 --- a/applications/app/BUILD.gn +++ b/applications/app/BUILD.gn @@ -29,5 +29,6 @@ lite_component("app") { # "TW105_I2C_sht30:i2c_sht30", # "TW106_UART:uart_example", # "TW201_Module_oled:module_oled_example", + # "TW301_Network_wifista:network_wifista_example", ] } diff --git a/applications/app/README.md b/applications/app/README.md index 4a6efe55..9fc5344e 100644 --- a/applications/app/README.md +++ b/applications/app/README.md @@ -27,4 +27,5 @@ | TW104 | ADC | adc_voltage | [ADC电压采集](applications/app/TW104_ADC_voltage/README.md) | 核心板 + OLED扩展板| | TW105 | I2C | i2c_sht30 | [I2C温湿度传感器采集](applications/app/TW105_I2C_sht30/README.md) | 核心板 + 马达扩展板| | TW106 | UART | uart | [UART串口自发自收](applications/app/TW106_UART/README.md) | 核心板 | -| TW201 | Module | oled | [OLED显示屏驱动](applications/app/TW201_Module_oled/README.md) | 核心板 + OLED扩展板| \ No newline at end of file +| TW201 | Module | oled | [OLED显示屏驱动](applications/app/TW201_Module_oled/README.md) | 核心板 + OLED扩展板| +| TW301 | Network | wifista | [WiFi-STA连接演示](applications/app/TW301_Network_wifista/README.md) | 核心板 | \ No newline at end of file diff --git a/applications/app/TW106_UART/BUILD.gn b/applications/app/TW106_UART/BUILD.gn index dda34c21..4ddd7c08 100644 --- a/applications/app/TW106_UART/BUILD.gn +++ b/applications/app/TW106_UART/BUILD.gn @@ -11,7 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -static_library("uart_test"){ +static_library("uart_example"){ sources =[ "uart_example.c" ] diff --git a/applications/app/TW301_Network_wifista/BUILD.gn b/applications/app/TW301_Network_wifista/BUILD.gn new file mode 100644 index 00000000..f6ee018e --- /dev/null +++ b/applications/app/TW301_Network_wifista/BUILD.gn @@ -0,0 +1,25 @@ +# Copyright (c) 2021 Talkweb Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +static_library("network_wifista_example") { + sources = [ + "wifi_connect_demo.c", + ] + + include_dirs = [ + "//utils/native/lite/include", + "//kernel/liteos_m/kal/cmsis", + "//foundation/communication/wifi_lite/interfaces/wifiservice", + "//device/talkweb/niobe/sdk_liteos/third_party/lwip_sack/include", + ] +} \ No newline at end of file diff --git a/applications/app/TW301_Network_wifista/README.md b/applications/app/TW301_Network_wifista/README.md new file mode 100644 index 00000000..daefe99f --- /dev/null +++ b/applications/app/TW301_Network_wifista/README.md @@ -0,0 +1,322 @@ +# Niobe开发板WiFi-STA联网演示 + +本案例程序将演示怎么在拓维Niobe WiFi IoT Core开发板上编写一个连接Wifi热点的业务程序,实现开发板联网效果。 + +![image-20210924115053858](figure/image-20210924115053858.png) + +## Wifi API分析 + +本案例主要使用了以下几个API完成Wifi联网 + +## RegisterWifiEvent() + +``` +WifiErrorCode RegisterWifiEvent (WifiEvent * event) +``` + +**描述:** 为指定的Wi-Fi事件注册回调函数。当WifiEvent中定义的Wi-Fi事件发生时,将调用已注册的回调函数 + +**参数:** + +| 名字 | 描述 | +| ----- | --------------------- | +| event | 表示要注册回调的事件. | + +## UnRegisterWifiEvent() + +``` +WifiErrorCode UnRegisterWifiEvent (WifiEvent * event) +``` + +**描述:** 为指定的Wi-Fi事件取消已经注册的回调函数。 + +**参数:** + +| 名字 | 描述 | +| ----- | ------------------------- | +| event | 表示已经注册的回调的事件. | + +## EnableWifi() + +``` +WifiErrorCode EnableWifi (void ) +``` + +**描述:** + +启用STA模式 + +## DisableWifi() + +``` +WifiErrorCode DisableWifi (void ) +``` + +**描述:** + +禁用STA模式 + +## AddDeviceConfig() + +``` +WifiErrorCode AddDeviceConfig (const WifiDeviceConfig * config, int * result ) +``` + +**描述:** + +添加用于配置连接到热点信息,此函数生成一个networkId + +**参数:** + +| 名字 | 描述 | +| ------ | -------------------------------------------------- | +| config | 表示要连接的热点信息. | +| result | 表示生成的networkId。每个networkId匹配一个热点配置 | + +## ConnectTo() + +``` +WifiErrorCode ConnectTo (int networkId) +``` + +**描述:** + +连接到指定networkId的热点 + +**参数:** + +| 名字 | 描述 | +| --------- | --------------------------- | +| networkId | 表示与目标热点匹配的网络id. | + +## Disconnect() + +``` +WifiErrorCode Disconnect (void) +``` + +**描述:** + +断开已连接到的热点 + +## netifapi_netif_find() + +``` +struct netif *netifapi_netif_find(const char *name); +``` + +**描述:** + +获取netif用于IP操作 + +## netifapi_dhcp_start() + +``` +err_t netifapi_dhcp_start(struct netif *netif); +``` + +**描述:** + +启动DHCP, 获取IP + +## dhcp_clients_info_show() + +``` +void dhcp_clients_info_show(struct netif *netif_p); +``` + +**描述:** + +格式化DHCP信息到控制台 + +## netifapi_netif_common() + +``` +err_t netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc, netifapi_errt_fn errtfunc); +``` + +**描述:** + +此函数主要是保证voidfunc和errtfunc在安全线程中执行。 + +## 软件设计 + +**主要代码分析** + +完成Wifi热点的连接需要以下几步 + +1. 通过 `RegisterWifiEvent` 接口向系统注册扫描状态监听函数,用于接收扫描状态通知,如扫描动作是否完成等。 + `OnWifiConnectionChanged` 用于绑定连接状态监听函数,该回调函数有两个参数 `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断开的原因. | + | ipAddress | 连接的特点IP地址 | + +2. 调用 `EnableWifi` 接口,启动 Wifi STA模式。 + +3. 调用 `AddDeviceConfig` 接口,配置连接的热点信息。 + +4. 调用 `ConnectTo` 接口,连接到指定networkId的热点。 + +5. while循环等待,该过程中会有几秒钟的时间去轮询WiFi连接成功标志位 `g_wifiState`,当`g_wifiState` 为 1 时退出等待。 + +6. 调用 `netifapi_netif_find` 接口,获取 netif 用于 IP 操作。 + +7. 调用 `netifapi_dhcp_start` 接口,启动 DHCP, 获取 IP。 + +8. 调用`netifapi_netif_common`接口,打印HDCP信息。 + +9. 如果DHCP IP获取失败,断开WiFi连接,重新启动。 + +```c +static void IotWifiConnectTask(void *arg) +{ + (void)arg; + + WifiErrorCode errCode; + + WifiEvent eventListener = { + .OnWifiConnectionChanged = OnWifiConnectionChanged, + .OnWifiScanStateChanged = OnWifiScanStateChanged}; + + WifiDeviceConfig apConfig = { + .ssid = DEFAILT_WIFI_SSID, + .preSharedKey = DEFAILT_WIFI_PASSWORD, + .securityType = WIFI_SEC_TYPE_PSK}; + + Delay(10); + + errCode = RegisterWifiEvent(&eventListener); + printf("RegisterWifiEvent errCode: %d\r\n", errCode); + + while (1) + { + int networkId = -1; + //启动WiFi STA模式 + errCode = EnableWifi(); + printf("EnableWifi errCode: %d\r\n", errCode); + Delay(100); + + errCode = AddDeviceConfig(&apConfig, &networkId); + printf("AddDeviceConfig errCode: %d\r\n", errCode); + + g_wifiState = 0; + errCode = ConnectTo(networkId); + printf("ConnectTo(%d) errCode: %d\r\n", networkId, errCode); + + while (!g_wifiState) + { + Delay(10); + } + printf("g_wifiState: %d\r\n", g_wifiState); + Delay(3000); + + // 联网业务开始 + // 这里是网络业务代码... + struct netif *iface = netifapi_netif_find("wlan0"); + if (iface) + { + err_t ret = netifapi_dhcp_start(iface); + printf("netifapi_dhcp_start: %d\r\n", ret); + + Delay(2000); // 等待DHCP服务分配IP地址 + + ret = netifapi_netif_common(iface, dhcp_clients_info_show, NULL); + printf("netifapi_netif_common: %d\r\n", ret); + + g_wifiState = WIFI_STATE_AVALIABLE; + + break; //联网成功,退出循环 + } + else + { + // 联网业务结束,断开和AP的连接 + Disconnect(); + RemoveDevice(networkId); + + //关闭WiFi STA模式 + errCode = DisableWifi(); + printf("DisableWifi errCode: %d\r\n", errCode); + Delay(500); + } + } + + errCode = UnRegisterWifiEvent(&eventListener); + printf("UnRegisterWifiEvent errCode: %d\r\n", errCode); +} +``` + +## 编译调试 + +### 修改对接热点的账号密码 + +修改`wifi_connect_demo.h`第20行和21行的WiFi热点SSID和密码,改成自己环境中的WiFi热点。 + +``` +//此处是默认测试WiFi连接热点,具体使用时,请修改WiFi的ssid和password +#define DEFAILT_WIFI_SSID "DESKTOP-TALKWEB" +#define DEFAILT_WIFI_PASSWORD "ai123456789" +``` + +### 修改 BUILD.gn 文件 + +修改 `applications/app/BUILD.gn` 路径中的 BUILD.gn 文件,指定 `network_wifista_example` 参与编译。 + +``` +# "TW208_Module_ds1307:module_ds1307_example", +# "TW209_Module_gps:module_gps_example", +"TW301_Network_wifista:network_wifista_example", +# "TW302_Network_wifiap:network_wifiap_example", +# "TW303_Network_mqttclient:network_mqttclient_example", +# "TW304_Network_httpclient:network_httpclient_example", +# "TW305_Network_ntpclient:network_ntpclient_example", +``` + +### 运行结果 + +示例代码编译烧录代码后,按下开发板的RESET按键,通过串口助手查看日志,会打印连接到的Wifi热点信息。 + +``` +entry wifi connect demo. + +RegisterWifiEvent errCode: 0 + +EnableWifi errCode: 0 + +AddDeviceConfig errCode: 0 + +ConnectTo(1) errCode: 0 + +No crash dump found! + ++NOTICE:SCANFINISH ++NOTICE:CONNECTED +OnWifiConnectionChanged 50, state = 1 + +info: bssid: BA:81:98:01:A9:A5, rssi: 0, connState: 0, reason: 0, ssid: DESKTOP-TALKWEB + +g_wifiState: 1 + +netifapi_dhcp_start: 0 + +server : + server_id : 192.168.137.1 + mask : 255.255.255.0, 1 + gw : 192.168.137.1 + T0 : 604800 + T1 : 302400 + T2 : 453600 +clients <1> : + mac_idx mac addr state lease tries rto + 0 20579e6185a4 192.168.137.176 10 0 1 2 +netifapi_netif_common: 0 + +UnRegisterWifiEvent errCode: 0 +``` \ No newline at end of file diff --git a/applications/app/TW301_Network_wifista/figure/image-20210924115053858.png b/applications/app/TW301_Network_wifista/figure/image-20210924115053858.png new file mode 100644 index 00000000..1c894364 Binary files /dev/null and b/applications/app/TW301_Network_wifista/figure/image-20210924115053858.png differ diff --git a/applications/app/TW301_Network_wifista/wifi_connect_demo.c b/applications/app/TW301_Network_wifista/wifi_connect_demo.c new file mode 100644 index 00000000..5668bbe6 --- /dev/null +++ b/applications/app/TW301_Network_wifista/wifi_connect_demo.c @@ -0,0 +1,190 @@ +/* +* Copyright (c) 2021 Talkweb Co.,ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include +#include +#include + +#include "ohos_init.h" +#include "cmsis_os2.h" +#include "wifi_device.h" + +#include "lwip/netifapi.h" +#include "lwip/api_shell.h" + +#include "wifi_connect_demo.h" + +int g_wifiState = 0; + +/** + * @brief 打印WifiLinkedInfo信息 + * @param info 需要打印信息的WifiLinkedInfo指针 + */ +static void PrintWifiLinkedInfo(WifiLinkedInfo *info) +{ + if (!info) + return; + + static char macAddress[32] = {0}; + unsigned char *mac = info->bssid; + snprintf(macAddress, sizeof(macAddress), "%02X:%02X:%02X:%02X:%02X:%02X", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + printf("info: bssid: %s, rssi: %d, connState: %d, reason: %d, ssid: %s\r\n", + macAddress, info->rssi, info->connState, info->disconnectedReason, info->ssid); +} + +/** + * @brief WiFi连接状态改变回调 + * @param state WiFi连接状态,参考WifiEventState + * @param info WiFi连接信息 + */ +static void OnWifiConnectionChanged(int state, WifiLinkedInfo *info) +{ + if (!info) + return; + + printf("%s %d, state = %d \r\n", __FUNCTION__, __LINE__, state); + PrintWifiLinkedInfo(info); + + g_wifiState = state; +} + +/** + * @brief WiFi扫描状态改变回调 + * @param state WiFi连接状态,参考WifiEventState + * @param size + */ +static void OnWifiScanStateChanged(int state, int size) +{ + printf("%s %d, state = %X, size = %d\r\n", __FUNCTION__, __LINE__, state, size); +} + +/** + * @brief 毫秒级延时 + * @param ms 需要延迟的毫秒数 + */ +static void Delay(uint32_t ms) +{ + uint32_t usPerTicks = (1000 * 1000) / osKernelGetTickFreq(); + osDelay((ms * 1000) / usPerTicks); + usleep((ms * 1000) % usPerTicks); +} + +/** + * @brief WiFi连接程序,CMSIS线程回调函数 + * @param arg 线程参数 + */ +static void IotWifiConnectTask(void *arg) +{ + (void)arg; + + WifiErrorCode errCode; + + WifiEvent eventListener = { + .OnWifiConnectionChanged = OnWifiConnectionChanged, + .OnWifiScanStateChanged = OnWifiScanStateChanged}; + + WifiDeviceConfig apConfig = { + .ssid = DEFAILT_WIFI_SSID, + .preSharedKey = DEFAILT_WIFI_PASSWORD, + .securityType = WIFI_SEC_TYPE_PSK}; + + Delay(10); + + errCode = RegisterWifiEvent(&eventListener); + printf("RegisterWifiEvent errCode: %d\r\n", errCode); + + while (1) + { + int networkId = -1; + //启动WiFi STA模式 + errCode = EnableWifi(); + printf("EnableWifi errCode: %d\r\n", errCode); + Delay(100); + + errCode = AddDeviceConfig(&apConfig, &networkId); + printf("AddDeviceConfig errCode: %d\r\n", errCode); + + g_wifiState = 0; + errCode = ConnectTo(networkId); + printf("ConnectTo(%d) errCode: %d\r\n", networkId, errCode); + + while (!g_wifiState) + { + Delay(10); + } + printf("g_wifiState: %d\r\n", g_wifiState); + Delay(3000); + + // 联网业务开始 + // 这里是网络业务代码... + struct netif *iface = netifapi_netif_find("wlan0"); + if (iface) + { + err_t ret = netifapi_dhcp_start(iface); + printf("netifapi_dhcp_start: %d\r\n", ret); + + Delay(2000); // 等待DHCP服务分配IP地址 + + ret = netifapi_netif_common(iface, dhcp_clients_info_show, NULL); + printf("netifapi_netif_common: %d\r\n", ret); + + g_wifiState = WIFI_STATE_AVALIABLE; + + break; //联网成功,退出循环 + } + else + { + // 联网业务结束,断开和AP的连接 + Disconnect(); + RemoveDevice(networkId); + + //关闭WiFi STA模式 + errCode = DisableWifi(); + printf("DisableWifi errCode: %d\r\n", errCode); + Delay(500); + } + } + + errCode = UnRegisterWifiEvent(&eventListener); + printf("UnRegisterWifiEvent errCode: %d\r\n", errCode); +} + +/** + * @brief WiFi STA模式案例入口函数 + */ +void IotWifiConnect(void) +{ + + printf("\n\n entry wifi connect demo.\r\n"); + + osThreadAttr_t attr; + + attr.name = "IotWifiConnectTask"; + attr.attr_bits = 0U; + attr.cb_mem = NULL; + attr.cb_size = 0U; + attr.stack_mem = NULL; + attr.stack_size = 10240; + attr.priority = osPriorityNormal; + + if (osThreadNew(IotWifiConnectTask, NULL, &attr) == NULL) + { + printf("[Talkweb Niobe] Falied to create IotWifiConnectTask!\n"); + } +} + +APP_FEATURE_INIT(IotWifiConnect); diff --git a/applications/app/TW301_Network_wifista/wifi_connect_demo.h b/applications/app/TW301_Network_wifista/wifi_connect_demo.h new file mode 100644 index 00000000..90502290 --- /dev/null +++ b/applications/app/TW301_Network_wifista/wifi_connect_demo.h @@ -0,0 +1,28 @@ +/* +* Copyright (c) 2021 Talkweb Co.,ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef WIFI_CONNECT_DEMO_H +#define WIFI_CONNECT_DEMO_H + +//此处是默认测试WiFi连接热点,具体使用时,请修改WiFi的ssid和password +#define DEFAILT_WIFI_SSID "DESKTOP-TALKWEB" +#define DEFAILT_WIFI_PASSWORD "ai123456789" + +extern int g_wifiState; + +void IotWifiConnect(void); + +#endif +