Contributed by WildboarG, organized by Ai-Thinker
AI-WB2 Provisioning [Part 1]
AI-WB2 Web Page Provisioning
Introduction
Early Wi-Fi devices mostly had screens and input devices (embedded devices, laptops, phones, etc.), so provisioning only required the user to connect to the router's SSID and enter the password.
However, with the rise of the IoT, Wi-Fi is now widely used in devices without any human-machine interface, such as smart plugs and other IoT terminal products. The Wi-Fi (IEEE802.11) standard was never designed with provisioning for such headless devices in mind — there is no official standard. This has led to a wide variety of provisioning methods on the market, each vendor doing its own thing, resulting in fragmentation and incompatibility, and a generally poor experience.
A simplified diagram is shown below:

Common provisioning methods include:
- STA+AP provisioning
- STA provisioning
- Bluetooth provisioning
- One-key provisioning (SMART CONFIG)
- Broadcast packet length method
- Multicast address method
- Others
Two basic concepts:
STA: STA, also called "client mode", means the device (phone, laptop, MCU, etc.) acts as a Wi-Fi client, connecting to a wireless AP (router or hotspot) to access the network. STA mode features:
- The device must connect to an AP to access the internet or LAN.
- Typical applications: a phone connecting to Wi-Fi, a computer connecting to a wireless router, etc.
AP: AP mode, also called "hotspot mode" or "SoftAP (software AP) mode", creates a wireless network that allows other STA devices to connect.
AP mode features:
- The device itself becomes a Wi-Fi hotspot that other devices can connect to.
- Suitable for IoT device provisioning, temporary networks, and similar applications.
STA & STA+AP Provisioning
STA STA provisioning is as simple as connecting your phone to a router. Turn on the router or routing device (a device that can create a hotspot) in advance, then write the matching fixed SSID and PASSWORD into the device to be provisioned (e.g. WB2/M61/ESP8266). When the device runs, it scans nearby hotspots; once it matches the corresponding SSID, it attempts to connect and returns once successful.
STA usually offers dynamic and static options:
Dynamic: the router's DHCP randomly assigns an unused IP address
Static: write the IP address you want to use into the program in advance; after connecting, set the IP address to use. It's a bit more convenient.
STA+AP
- The device (WB2/M61/ESP8266, etc.) enters softAP mode and creates an agreed AP hotspot.
- The user connects the phone APP / computer to the hotspot created in step 1.
- Once connected, the phone and the device establish LAN communication.
- The phone APP / computer sends the provisioning information (usually the router information) to the device over UDP/TCP (commonly via a static web page).
- After receiving the provisioning information, the device switches to STA mode and connects in STA mode.
Example Code
- STA
Click to expand full code
#
define
ROUTER_SSID
"your ssid"
// 定义WiFi SSID
#
define
ROUTER_PWD
"your password"
// 定义WiFi密码
// WiFi 配置结构体,指定国家代码
static
wifi_conf_t
conf =
{
.country_code =
"CN"
,
};
// WiFi 连接函数,连接到指定的SSID和密码
static
void
wifi_sta_connect
(
char
* ssid,
char
* password)
{
wifi_interface_t
wifi_interface;
wifi_interface = wifi_mgmr_sta_enable();
// 启用 WiFi STA 模式
wifi_mgmr_sta_connect(wifi_interface, ssid, password,
NULL
,
NULL
,
0
,
0
);
// 连接WiFi
}
// WiFi 事件回调函数,处理各种WiFi事件
static
void
event_cb_wifi_event
(
input_event_t
* event,
void
* private_data)
{
static
char
* ssid;
static
char
* password;
switch
(event->code)
{
case
CODE_WIFI_ON_INIT_DONE:
// WiFi初始化完成
{
blog_info(
"[APP] [EVT] INIT DONE %lld"
, aos_now_ms());
wifi_mgmr_start_background(&conf);
// 启动 WiFi 管理器
}
break
;
case
CODE_WIFI_ON_MGMR_DONE:
// WiFi管理器初始化完成
{
blog_info(
"[APP] [EVT] MGMR DONE %lld"
, aos_now_ms());
wifi_sta_connect(ROUTER_SSID, ROUTER_PWD);
// 连接WiFi
}
break
;
case
CODE_WIFI_ON_SCAN_DONE:
// WiFi扫描完成
{
blog_info(
"[APP] [EVT] SCAN Done %lld"
, aos_now_ms());
}
break
;
case
CODE_WIFI_ON_DISCONNECT:
// WiFi断开连接
{
blog_info(
"[APP] [EVT] disconnect %lld"
, aos_now_ms());
}
break
;
case
CODE_WIFI_ON_CONNECTING:
// WiFi正在连接
{
blog_info(
"[APP] [EVT] Connecting %lld"
, aos_now_ms());
}
break
;
case
CODE_WIFI_CMD_RECONNECT:
// WiFi重连命令
{
blog_info(
"[APP] [EVT] Reconnect %lld"
, aos_now_ms());
}
break
;
case
CODE_WIFI_ON_CONNECTED:
// WiFi已连接
{
blog_info(
"[APP] [EVT] connected %lld"
, aos_now_ms());
}
break
;
case
CODE_WIFI_ON_PRE_GOT_IP:
// WiFi连接成功,准备获取IP
{
blog_info(
"[APP] [EVT] connected %lld"
, aos_now_ms());
}
break
;
case
CODE_WIFI_ON_GOT_IP:
// WiFi成功获取IP地址
{
blog_info(
"[APP] [EVT] GOT IP %lld"
, aos_now_ms());
blog_info(
"[SYS] Memory left is %d Bytes"
, xPortGetFreeHeapSize());
// 记录剩余内存
//获取到ip地址之后就可以开别的任务。
}
break
;
case
CODE_WIFI_ON_PROV_SSID:
// 接收到配网的SSID
{
blog_info(
"[APP] [EVT] [PROV] [SSID] %lld: %s"
, aos_now_ms(), event->value ? (
const
char
*)event->value :
"UNKNOWN"
);
if
(ssid)
{
vPortFree(ssid);
ssid =
NULL
;
}
ssid = (
char
*)event->value;
}
break
;
case
CODE_WIFI_ON_PROV_BSSID:
// 接收到配网的BSSID
{
blog_info(
"[APP] [EVT] [PROV] [BSSID] %lld: %s"
, aos_now_ms(), event->value ? (
const
char
*)event->value :
"UNKNOWN"
);
if
(event->value)
{
vPortFree((
void
*)event->value);
}
}
break
;
case
CODE_WIFI_ON_PROV_PASSWD:
// 接收到配网的密码
{
blog_info(
"[APP] [EVT] [PROV] [PASSWD] %lld: %s"
, aos_now_ms(), event->value ? (
const
char
*)event->value :
"UNKNOWN"
);
if
(password)
{
vPortFree(password);
password =
NULL
;
}
password = (
char
*)event->value;
}
break
;
case
CODE_WIFI_ON_PROV_CONNECT:
// 触发配网连接
{
blog_info(
"[APP] [EVT] [PROV] [CONNECT] %lld"
, aos_now_ms());
blog_info(
"connecting to %s:%s..."
, ssid, password);
wifi_sta_connect(ssid, password);
}
break
;
case
CODE_WIFI_ON_PROV_DISCONNECT:
// 配网断开
{
blog_info(
"[APP] [EVT] [PROV] [DISCONNECT] %lld"
, aos_now_ms());
}
break
;
default
:
// 处理未知WiFi事件
{
blog_info(
"[APP] [EVT] Unknown code %u, %lld"
, event->code, aos_now_ms());
}
}
}
// WiFi 主任务入口
static
void
proc_main_entry
(
void
* pvParameters)
{
aos_register_event_filter(EV_WIFI, event_cb_wifi_event,
NULL
);
// 注册WiFi事件回调函数
hal_wifi_start_firmware_task();
// 启动WiFi固件任务
aos_post_event(EV_WIFI, CODE_WIFI_ON_INIT_DONE,
0
);
// 发送WiFi初始化完成事件
vTaskDelete(
NULL
);
// 删除当前任务
}Set a static IP
Click to expand full code
// 定义WiFi站点的SSID
#
define
STA_SSID
"123"
// 定义WiFi站点的密码
#
define
STA_PASSWORD
"12345678"
// 定义WiFi站点的最大连接数
#
define
STA_MAX_CONN_cCOUNT 15
// 定义WiFi站点的静态IP地址
#
define
STA_STATIC_IP
"192.168.43.133"
// 定义一个宏,用于将四个字节的IP地址转换为32位无符号整数
#
define
IP_SET_ADDR(a, b, c, d) (((uint32_t)((a)&0xff)) | \
((uint32_t)((b)&0xff) << 8) | \
((uint32_t)((c)&0xff) << 16) | \
((uint32_t)((d)&0xff) << 24))
// 定义静态WiFi配置结构体,初始化国家代码为"CN"(中国)
static
wifi_conf_t
conf = {
.country_code =
"CN"
,
};
// 定义国家代码类型变量,初始化为中国
country_code_type country_code = WIFI_COUNTRY_CODE_CN;
// 定义WiFi IP参数结构体并初始化为0
wifi_ip_params_t
sta_ip_params = {
0
};
// 定义全局WiFi站点接口指针,初始化为空
static
wifi_interface_t
g_wifi_sta_interface =
NULL
;
// 定义全局WiFi站点连接状态标志,0表示未连接
static
int
g_wifi_sta_is_connected =
0
;
// WiFi后台初始化函数,参数为国家代码
void
wifi_background_init
(country_code_type country_code)
{
// 定义国家代码字符串数组
char
* country_code_string[WIFI_COUNTRY_CODE_MAX] = {
"CN"
,
"JP"
,
"US"
,
"EU"
};
/* 初始化WiFi后台 */
// 将传入的国家代码复制到配置结构体中
strcpy
(conf.country_code, country_code_string[country_code]);
// 启动WiFi后台管理
wifi_mgmr_start_background(&conf);
/* 启用扫描隐藏SSID功能 */
wifi_mgmr_scan_filter_hidden_ssid(
0
);
}
// 获取站点IP信息的函数
static
int
wifi_get_station_ip_str
(
void
)
{
int
state = WIFI_STATE_IDLE;
// WiFi状态变量,初始化为空闲状态
ip4_addr_t
ipaddr, gwaddr, maskaddr;
// 定义IP地址、网关和子网掩码变量
wifi_mgmr_state_get(&state);
// 获取当前WiFi状态
// 如果状态为已连接并获取IP
if
(state == WIFI_STATE_CONNECTED_IP_GOT || state == WIFI_STATE_WITH_AP_CONNECTED_IP_GOT || state == CODE_WIFI_ON_GOT_IP)
{
// 获取站点IP参数
wifi_mgmr_sta_ip_get(&sta_ip_params.ip, &sta_ip_params.gateway, &sta_ip_params.netmask);
ipaddr.addr = sta_ip_params.ip;
// 设置IP地址
gwaddr.addr = sta_ip_params.gateway;
// 设置网关地址
maskaddr.addr = sta_ip_params.netmask;
// 设置子网掩码
}
else
{
// 如果未连接,IP参数置0
ipaddr.addr =
0
;
gwaddr.addr =
0
;
maskaddr.addr =
0
;
}
// 打印IP地址信息
blog_info(
"GOT %s:\"%s\""
,
"ip"
, ip4addr_ntoa(&ipaddr));
blog_info(
"GOT %s:\"%s\""
,
"gateway"
, ip4addr_ntoa(&gwaddr));
blog_info(
"GOT %s:\"%s\""
,
"netmask"
, ip4addr_ntoa(&maskaddr));
return
0
;
}
// WiFi站点连接函数,参数为静态IP地址字符串
int
wifi_sta_connect
(
const
char
* sta_ip)
{
struct
ap_connect_adv
ext_param
=
{
0
};
// 定义高级连接参数结构体并初始化
g_wifi_sta_interface = wifi_mgmr_sta_enable();
// 启用WiFi站点模式并获取接口
uint32_t
flags =
0
;
// 定义标志变量
uint8_t
ip[
4
] = {
0
}, j =
0
;
// 定义IP地址字节数组和计数器
char
* temp_arg = (
char
*)
malloc
(
6
);
// 分配临时字符串缓冲区
// 清空IP数组和参数结构体
memset
(ip,
0
,
sizeof
(ip));
memset
(&sta_ip_params,
0
,
sizeof
(sta_ip_params));
memset
(temp_arg,
0
,
sizeof
(temp_arg));
// 解析静态IP地址字符串到字节数组
for
(
int
i =
0
; i <
4
; i++)
{
temp_arg = sta_ip;
while
(*(sta_ip++) !=
'.'
)
{
j++;
}
temp_arg[j] =
'\0'
;
ip[i] = atoi(temp_arg);
j =
0
;
}
// 设置IP参数
sta_ip_params.ip = IP_SET_ADDR(ip[
0
], ip[
1
], ip[
2
], ip[
3
]);
// 设置IP地址
sta_ip_params.gateway = IP_SET_ADDR(ip[
0
], ip[
1
], ip[
2
],
1
);
// 设置网关地址
sta_ip_params.netmask = IP_SET_ADDR(
255
,
255
,
255
,
0
);
// 设置子网掩码
ext_param.psk =
NULL
;
// 设置PSK为空
ext_param.ap_info.type = AP_INFO_TYPE_PRESIST;
// 设置AP信息类型为持久
ext_param.ap_info.time_to_live =
5
;
// 设置存活时间
ext_param.ap_info.band =
0
;
// 设置频段
flags |= WIFI_CONNECT_PMF_CAPABLE;
// 设置支持PMF标志
ext_param.flags = flags;
// 应用标志
// 如果已经连接
if
(g_wifi_sta_is_connected ==
1
)
{
blog_info(
"sta has connect"
);
// 打印已连接信息
return
0
;
}
else
{
wifi_mgmr_sta_autoconnect_enable();
// 启用自动连接
// 设置站点IP参数
wifi_mgmr_sta_ip_set(sta_ip_params.ip, sta_ip_params.netmask, sta_ip_params.gateway, sta_ip_params.gateway,
0
);
blog_info(
"connect to wifi %s"
, STA_SSID);
// 打印连接信息
// 执行站点连接
return
wifi_mgmr_sta_connect_ext(g_wifi_sta_interface, STA_SSID, STA_PASSWORD, &ext_param);
}
free
(temp_arg);
// 释放临时缓冲区
}
// WiFi事件回调函数
static
void
wifi_event_cb
(
input_event_t
* event,
void
* private_data)
{
static
char
* ssid;
// 静态SSID变量
static
char
* password;
// 静态密码变量
blog_info(
"[APP] [EVT] event->code %d"
, event->code);
// 打印事件代码
blog_info(
"[SYS] Memory left is %d Bytes"
, xPortGetFreeHeapSize());
// 打印剩余内存
// 根据事件代码处理不同情况
switch
(event->code)
{
case
CODE_WIFI_ON_AP_STARTED:
// AP启动事件
{
blog_info(
"[APP] [EVT] AP INIT DONE %lld"
, aos_now_ms());
}
break
;
case
CODE_WIFI_ON_AP_STOPPED:
// AP停止事件
{
blog_info(
"[APP] [EVT] AP STOP DONE %lld"
, aos_now_ms());
}
break
;
case
CODE_WIFI_ON_INIT_DONE:
// 初始化完成事件
{
blog_info(
"[APP] [EVT] INIT DONE %lld"
, aos_now_ms());
wifi_mgmr_start_background(&conf);
// 启动WiFi后台
wifi_sta_connect(STA_STATIC_IP);
// 连接WiFi站点
}
break
;
case
CODE_WIFI_ON_MGMR_DONE:
// 管理器完成事件
{
blog_info(
"[APP] [EVT] MGMR DONE %lld"
, aos_now_ms());
}
break
;
case
CODE_WIFI_ON_SCAN_DONE:
// 扫描完成事件
{
blog_info(
"[APP] [EVT] SCAN Done %lld"
, aos_now_ms());
}
break
;
case
CODE_WIFI_ON_DISCONNECT:
// 断开连接事件
{
g_wifi_sta_is_connected =
0
;
// 设置未连接状态
blog_info(
"wifi sta disconnected"
);
// 打印断开信息
blog_info(
"[APP] [EVT] disconnect %lld"
, aos_now_ms());
}
break
;
case
CODE_WIFI_ON_CONNECTING:
// 正在连接事件
{
blog_info(
"[APP] [EVT] Connecting %lld"
, aos_now_ms());
}
break
;
case
CODE_WIFI_CMD_RECONNECT:
// 重连事件
{
blog_info(
"[APP] [EVT] Reconnect %lld"
, aos_now_ms());
}
break
;
case
CODE_WIFI_ON_CONNECTED:
// 连接成功事件
{
blog_info(
"wifi sta connected"
);
// 打印连接成功信息
blog_info(
"[APP] [EVT] connected %lld"
, aos_now_ms());
}
break
;
case
CODE_WIFI_ON_PRE_GOT_IP:
// 获取IP前事件
{
blog_info(
"[APP] [EVT] connected %lld"
, aos_now_ms());
}
break
;
case
CODE_WIFI_ON_GOT_IP:
// 获取IP事件
{
blog_info(
"WIFI STA GOT IP"
);
// 打印获取IP信息
blog_info(
"[APP] [EVT] GOT IP %lld"
, aos_now_ms());
g_wifi_sta_is_connected =
1
;
// 设置已连接状态
wifi_get_station_ip_str();
// 获取并打印IP信息
}
break
;
case
CODE_WIFI_ON_PROV_SSID:
// 提供SSID事件
{
blog_info(
"[APP] [EVT] [PROV] [SSID] %lld: %s"
,
aos_now_ms(),
event->value ? (
const
char
*)event->value :
"UNKNOWN"
);
if
(ssid)
{
vPortFree(ssid);
// 释放旧的SSID
ssid =
NULL
;
}
ssid = (
char
*)event->value;
// 设置新的SSID
}
break
;
case
CODE_WIFI_ON_PROV_PASSWD:
// 提供密码事件
{
blog_info(
"[APP] [EVT] [PROV] [PASSWD] %lld: %s"
, aos_now_ms(),
event->value ? (
const
char
*)event->value :
"UNKNOWN"
);
if
(password)
{
vPortFree(password);
// 释放旧的密码
password =
NULL
;
}
password = (
char
*)event->value;
// 设置新的密码
}
break
;
case
CODE_WIFI_ON_PROV_CONNECT:
// 提供连接事件
{
blog_info(
"connecting to %s:%s..."
, ssid, password);
// 打印连接信息
}
break
;
case
CODE_WIFI_ON_PROV_DISCONNECT:
// 提供断开事件
{
blog_info(
"[APP] [EVT] [PROV] [DISCONNECT] %lld"
, aos_now_ms());
}
break
;
default
:
// 未知事件
{
blog_info(
"[APP] [EVT] Unknown code %u, %lld"
, event->code, aos_now_ms());
/* 无操作 */
}
}
}
// WiFi执行函数
void
wifi_execute
(
void
* pvParameters)
{
// 注册WiFi事件过滤器
aos_register_event_filter(EV_WIFI, wifi_event_cb,
NULL
);
static
uint8_t
stack_wifi_init =
0
;
// WiFi栈初始化标志
// 如果已经初始化
if
(
1
== stack_wifi_init)
{
blog_info(
"Wi-Fi Stack Started already!!!"
);
// 打印已启动信息
return
;
}
stack_wifi_init =
1
;
// 设置已初始化
blog_info(
"Wi-Fi init successful"
);
// 打印初始化成功信息
hal_wifi_start_firmware_task();
// 启动WiFi固件任务
/* 触发启动WiFi */
aos_post_event(EV_WIFI, CODE_WIFI_ON_INIT_DONE,
0
);
// 发送初始化完成事件
vTaskDelete(
NULL
);
// 删除当前任务
}- AP+STA
Use the AP to host a static web page for connecting to Wi-Fi
- Enable AP mode, ssid: anthinker password:12345678, IP address 192.168.169.1
- Access <http://192.168.169.1> from a phone or computer
- Enter the SSID and PASSWORD to configure
- Tap Connect; once successful, the static page is no longer available.
Click to expand full code
#
include
<FreeRTOS.h>
#
include
<aos/kernel.h>
#
include
<aos/yloop.h>
#
include
<blog.h>
#
include
<hal_wifi.h>
#
include
<lwip/inet.h>
#
include
<lwip/netif.h>
#
include
<lwip/tcpip.h>
#
include
<stdio.h>
#
include
<string.h>
#
include
<task.h>
#
include
<wifi_mgmr_ext.h>
#
include
"lwip/api.h"
#
include
"lwip/err.h"
#
include
"lwip/netdb.h"
#
include
"lwip/sys.h"
#
include
"portmacro.h"
#
include
"string.h"
#
include
"web_server.h"
#
include
<cJSON.h>
#
include
<stdint.h>
#
define
SERVER_PORT 80
#
define
AP_SSID
"ai-thinker"
#
define
AP_PWD
"12345678"
#
define
TAG
"softAP"
TaskHandle_t ap_task_handle =
NULL
;
char
*ssid =
NULL
;
//等带从网页中配置
char
*password =
NULL
;
const
char
html_page[] =
"<!DOCTYPE html>\r\n"
"<html lang=\"zh\">\r\n"
"<head>\r\n"
" <meta charset=\"UTF-8\">\r\n"
" <meta name=\"viewport\" content=\"width=device-width, "
"initial-scale=1.0\">\r\n"
" <title>WiFi 配网</title>\r\n"
" <style>\r\n"
" body { font-family: Arial, sans-serif; text-align: center; "
"margin: 50px; }\r\n"
" input, button { padding: 10px; font-size: 16px; margin: 5px; }\r\n"
" </style>\r\n"
" <script>\r\n"
" function connectWiFi() {\r\n"
" let ssid = document.getElementById(\"wifi-ssid\").value;\r\n"
" let password = "
"document.getElementById(\"wifi-password\").value;\r\n"
" if (!ssid) { alert(\"请输入 WiFi 名称(SSID)\"); return; "
"}\r\n"
" fetch(\"/connect_wifi\", {\r\n"
" method: \"POST\",\r\n"
" headers: { \"Content-Type\": \"application/json\" },\r\n"
" body: JSON.stringify({ ssid, password })\r\n"
" })\r\n"
" .then(response => response.text())\r\n"
" .then(result => alert(\"WiFi 连接结果: \" + result))\r\n"
" .catch(error => alert(\"WiFi 连接失败: \" + error));\r\n"
" }\r\n"
" </script>\r\n"
"</head>\r\n"
"<body>\r\n"
" <h2>WiFi 配网</h2>\r\n"
" <label>WiFi 名称 (SSID):</label>\r\n"
" <input type=\"text\" id=\"wifi-ssid\" placeholder=\"输入 WiFi "
"SSID\">\r\n"
" <br>\r\n"
" <label>WiFi 密码:</label>\r\n"
" <input type=\"password\" id=\"wifi-password\" placeholder=\"输入 WiFi "
"密码\">\r\n"
" <br>\r\n"
" <button onclick=\"connectWiFi()\">连接 WiFi</button>\r\n"
"</body>\r\n"
"</html>\r\n"
;
static
wifi_conf_t
ap_conf = {
.country_code =
"CN"
,
};
static
wifi_interface_t
ap_interface;
/**
* @brief wifi_ap_ip_set
* Set the IP address of soft AP
* @param ip_addr IPV4 addr
* @param netmask netmask
* @param gw DNS
*/
static
void
wifi_ap_ip_set
(
char
*ip_addr,
char
*netmask,
char
*gw)
{
struct
netif
*
ap_netif
=
netif_find(
"ap1"
);
int
i =
0
;
int
ap_ipaddr[
4
] = {
0
};
int
ap_netmask[
4
] = {
255
,
255
,
255
,
0
};
int
ap_gw_arry[
4
] = {
0
,
0
,
0
,
0
};
ap_ipaddr[
0
] = atoi(strtok(ip_addr,
"."
));
for
(i =
1
; i <
4
; i++) {
ap_ipaddr[i] = atoi(strtok(
NULL
,
"."
));
}
if
(netmask) {
ap_netmask[
0
] = atoi(strtok(netmask,
"."
));
for
(i =
1
; i <
4
; i++)
ap_netmask[i] = atoi(strtok(
NULL
,
"."
));
}
if
(gw) {
ap_gw_arry[
0
] = atoi(strtok(gw,
"."
));
for
(i =
1
; i <
4
; i++)
ap_gw_arry[i] = atoi(strtok(
NULL
,
"."
));
}
if
(ap_netif) {
ip_addr_t
ap_ip;
ip_addr_t
ap_mask;
ip_addr_t
ap_gw;
IP4_ADDR(&ap_ip, ap_ipaddr[
0
], ap_ipaddr[
1
], ap_ipaddr[
2
], ap_ipaddr[
3
]);
IP4_ADDR(&ap_mask, ap_netmask[
0
], ap_netmask[
1
], ap_netmask[
2
],
ap_netmask[
3
]);
IP4_ADDR(&ap_gw, ap_gw_arry[
0
], ap_gw_arry[
1
], ap_gw_arry[
2
],
ap_gw_arry[
3
]);
netif_set_down(ap_netif);
netif_set_ipaddr(ap_netif, &ap_ip);
netif_set_netmask(ap_netif, &ap_mask);
netif_set_gw(ap_netif, &ap_gw);
netif_set_up(ap_netif);
blog_info(
"[softAP]:SSID:%s,PASSWORD:%s,IP addr:%s"
, AP_SSID, AP_PWD,
ip4addr_ntoa(netif_ip4_addr(ap_netif)));
}
else
blog_info(
"no find netif ap1 "
);
}
/**
* @brief wifi_ap_start
*
*/
static
void
wifi_ap_start
()
{
ap_interface = wifi_mgmr_ap_enable();
wifi_mgmr_conf_max_sta(
4
);
wifi_mgmr_ap_start(ap_interface, AP_SSID,
0
, AP_PWD,
6
);
wifi_ap_ip_set(
"192.168.169.1"
,
"255.255.255.0"
,
"192.168.169.1"
);
// defaut gateway ip is "192.168.169.1",if you want usb
// other gateway ip ,please change
// components/network/lwip_dhcpd/dhcp_server_raw.c:42
// DHCPD_SERVER_IP for example, gateway ip:"192.168.4.1"
// , change DHCPD_SERVER_IP to "192.168.4.1" :
// wifi_ap_ip_set("192.168.4.1", "255.255.255.0",
// "192.168.4.1");
// components/network/lwip_dhcpd/dhcp_server_raw.c:42
// #define DHCPD_SERVER_IP "192.168.4.1"
}
// WiFi 连接函数,STA连接到指定的SSID和密码
int
wifi_sta_connect
(
char
*ssid,
char
*password)
{
wifi_interface_t
wifi_interface;
wifi_interface = wifi_mgmr_sta_enable();
// 启用 WiFi STA 模式
return
wifi_mgmr_sta_connect(wifi_interface, ssid, password,
NULL
,
NULL
,
0
,
0
);
// 连接WiFi
//
}
// 处理 WiFi 连接请求
static
void
handle_connect_wifi
(
struct
netconn *conn,
char
*buf,
uint16_t
buflen)
{
printf
(
"📡 收到 WiFi 连接请求...\n"
);
// 1️⃣ 查找 HTTP 请求体(JSON 数据)
char
*json_start =
strstr
(buf,
"\r\n\r\n"
);
// 找到 HTTP 头部结束位置
if
(!json_start) {
printf
(
"❌ 无法找到 HTTP 请求体\n"
);
netconn_write(conn,
"HTTP/1.1 400 Bad Request\r\nContent-Type: "
"text/plain\r\n\r\n请求格式错误"
,
62
, NETCONN_NOCOPY);
return
;
}
json_start +=
4
;
// 跳过 "\r\n\r\n"
printf
(
"🌍 提取的 JSON 数据: %s\n"
, json_start);
// 调试信息
// 2️⃣ 解析 JSON 数据
cJSON *json = cJSON_Parse(json_start);
if
(!json) {
printf
(
"❌ JSON 解析失败\n"
);
netconn_write(conn,
"HTTP/1.1 400 Bad Request\r\nContent-Type: "
"text/plain\r\n\r\nJSON 解析失败"
,
62
, NETCONN_NOCOPY);
return
;
}
// 3️⃣ 获取 WiFi SSID 和密码
cJSON *ssid_item = cJSON_GetObjectItem(json,
"ssid"
);
cJSON *password_item = cJSON_GetObjectItem(json,
"password"
);
if
(!ssid_item || !password_item) {
printf
(
"❌ 缺少 SSID 或密码字段\n"
);
cJSON_Delete(json);
netconn_write(conn,
"HTTP/1.1 400 Bad Request\r\nContent-Type: "
"text/plain\r\n\r\n缺少必要参数"
,
62
, NETCONN_NOCOPY);
return
;
}
char
*ssid = ssid_item->valuestring;
char
*password = password_item->valuestring;
printf
(
"📶 解析 WiFi SSID: %s\n"
, ssid);
printf
(
"🔑 解析 WiFi 密码: %s\n"
, password);
cJSON_Delete(json);
// 4️⃣ 切换到 STA 模式并连接 WiFi
printf
(
"🔄 切换到 STA 模式...\n"
);
int
ret = wifi_sta_connect(ssid, password);
if
(ret ==
0
) {
printf
(
"✅ WiFi 连接请求已发送\n"
);
netconn_write(
conn,
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nWiFi 连接中..."
,
59
,
NETCONN_NOCOPY);
}
else
{
printf
(
"❌ WiFi 连接失败,错误码: %d\n"
, ret);
netconn_write(conn,
"HTTP/1.1 500 Internal Server Error\r\nContent-Type: "
"text/plain\r\n\r\nWiFi 连接失败"
,
67
, NETCONN_NOCOPY);
}
}
// 处理 HTTP 请求
static
void
web_httpserver
(
struct
netconn *conn)
{
struct
netbuf
*
inputbuf
;
char
*buf;
uint16_t
buflen;
err_t
err;
err = netconn_recv(conn, &inputbuf);
if
(err != ERR_OK) {
printf
(
"❌ HTTP 请求接收失败\n"
);
netconn_close(conn);
return
;
}
netbuf_data(inputbuf, (
void
**)&buf, &buflen);
printf
(
"🌐 收到 HTTP 请求: %s\n"
, buf);
// 访问主页(返回 HTML)
if
(
strncmp
(buf,
"GET / "
,
6
) ==
0
) {
printf
(
"📄 发送 HTML 页面\n"
);
netconn_write(conn, html_page,
strlen
(html_page), NETCONN_NOCOPY);
}
// 处理 WiFi 连接请求
else
if
(
strncmp
(buf,
"POST /connect_wifi"
,
18
) ==
0
) {
printf
(
"🔄 处理 WiFi 连接请求...\n"
);
handle_connect_wifi(conn, buf, buflen);
}
else
{
printf
(
"🚫 未知请求\n"
);
}
netbuf_delete(inputbuf);
netconn_close(conn);
}
// 启动 HTTP 服务器
void
start_ap_http_server
(
void
*pvParameters)
{
struct
netconn
*
conn
, *
newconn
;
err_t
err;
conn = netconn_new(NETCONN_TCP);
netconn_bind(conn,
NULL
,
80
);
netconn_listen(conn);
while
(
1
) {
err = netconn_accept(conn, &newconn);
if
(err == ERR_OK) {
web_httpserver(newconn);
netconn_delete(newconn);
}
}
}
static
void
event_cb_wifi_event
(
input_event_t
*event,
void
*private_data)
{
switch
(event->code) {
case
CODE_WIFI_ON_INIT_DONE:
blog_info(
"<<<<<<<<< init wifi done <<<<<<<<<<"
);
wifi_mgmr_start_background(&ap_conf);
break
;
case
CODE_WIFI_ON_MGMR_DONE:
blog_info(
"<<<<<<<<< startting soft ap <<<<<<<<<<<"
);
wifi_ap_start();
break
;
case
CODE_WIFI_ON_AP_STARTED:
// ap成功启动
blog_info(
"<<<<<<<<< startt soft ap OK<<<<<<<<<<<"
);
// 开启一个配网HTML页面
xTaskCreate(&start_ap_http_server,
"server_task"
,
1024
*
10
,
NULL
,
5
, &ap_task_handle);
break
;
case
CODE_WIFI_ON_AP_STOPPED:
break
;
case
CODE_WIFI_ON_AP_STA_ADD:
// 从设备连接到wb2的ap
blog_info(
"<<<<<<<<< station connent ap <<<<<<<<<<<"
);
break
;
case
CODE_WIFI_ON_AP_STA_DEL:
blog_info(
"<<<<<<<<< station disconnet ap <<<<<<<<<<<"
);
break
;
case
CODE_WIFI_ON_SCAN_DONE: {
printf
(
"[APP] [EVT] SCAN Done %lld\r\n"
, aos_now_ms());
// wifi_mgmr_cli_scanlist();
}
break
;
case
CODE_WIFI_ON_DISCONNECT: {
printf
(
"[APP] [EVT] disconnect %lld\r\n"
, aos_now_ms());
}
break
;
case
CODE_WIFI_ON_CONNECTING: {
printf
(
"[APP] [EVT] Connecting %lld\r\n"
, aos_now_ms());
}
break
;
case
CODE_WIFI_CMD_RECONNECT: {
printf
(
"[APP] [EVT] Reconnect %lld\r\n"
, aos_now_ms());
}
break
;
case
CODE_WIFI_ON_CONNECTED: {
printf
(
"[APP] [EVT] connected %lld\r\n"
, aos_now_ms());
}
break
;
case
CODE_WIFI_ON_PRE_GOT_IP: {
printf
(
"[APP] [EVT] connected %lld\r\n"
, aos_now_ms());
}
break
;
case
CODE_WIFI_ON_GOT_IP: {
printf
(
"[APP] [EVT] GOT IP %lld\r\n"
, aos_now_ms());
printf
(
"[SYS] Memory left is %d Bytes\r\n"
, xPortGetFreeHeapSize());
// wifi connection succeeded, create tcp server task
//
printf
(
"successfuly connect!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\r\n"
);
//到这一步就已经配网成功了,删不删除原来的AP任务都行
if
(ap_task_handle !=
NULL
) {
vTaskDelete(ap_task_handle);
ap_task_handle =
NULL
;
// 避免悬空指针
}
// 在这里添加配网后的任务吧
}
break
;
case
CODE_WIFI_ON_PROV_SSID: {
printf
(
"[APP] [EVT] [PROV] [SSID] %lld: %s\r\n"
, aos_now_ms(),
event->value ? (
const
char
*)event->value :
"UNKNOWN"
);
if
(ssid) {
vPortFree(ssid);
ssid =
NULL
;
}
ssid = (
char
*)event->value;
}
break
;
case
CODE_WIFI_ON_PROV_BSSID: {
printf
(
"[APP] [EVT] [PROV] [BSSID] %lld: %s\r\n"
, aos_now_ms(),
event->value ? (
const
char
*)event->value :
"UNKNOWN"
);
if
(event->value) {
vPortFree((
void
*)event->value);
}
}
break
;
case
CODE_WIFI_ON_PROV_PASSWD: {
printf
(
"[APP] [EVT] [PROV] [PASSWD] %lld: %s\r\n"
, aos_now_ms(),
event->value ? (
const
char
*)event->value :
"UNKNOWN"
);
if
(password) {
vPortFree(password);
password =
NULL
;
}
password = (
char
*)event->value;
}
break
;
case
CODE_WIFI_ON_PROV_CONNECT: {
printf
(
"[APP] [EVT] [PROV] [CONNECT] %lld\r\n"
, aos_now_ms());
printf
(
"connecting to %s:%s...\r\n"
, ssid, password);
wifi_sta_connect(ssid, password);
}
break
;
case
CODE_WIFI_ON_PROV_DISCONNECT: {
printf
(
"[APP] [EVT] [PROV] [DISCONNECT] %lld\r\n"
, aos_now_ms());
}
break
;
default
:
break
;
}
}
static
void
proc_main_entry
(
void
*pvParameters)
{
aos_register_event_filter(EV_WIFI, event_cb_wifi_event,
NULL
);
hal_wifi_start_firmware_task();
aos_post_event(EV_WIFI, CODE_WIFI_ON_INIT_DONE,
0
);
vTaskDelete(
NULL
);
}
static
void
system_thread_init
()
{
/*nothing here*/
}
void
main
()
{
system_thread_init();
puts
(
"[OS] Starting TCP/IP Stack..."
);
tcpip_init(
NULL
,
NULL
);
puts
(
"[OS] proc_main_entry task..."
);
xTaskCreate(proc_main_entry, (
char
*)
"main_entry"
,
1024
,
NULL
,
15
,
NULL
);
}Results
AP+STA results



