Skip to content

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

  1. The device (WB2/M61/ESP8266, etc.) enters softAP mode and creates an agreed AP hotspot.
  2. The user connects the phone APP / computer to the hotspot created in step 1.
  3. Once connected, the phone and the device establish LAN communication.
  4. The phone APP / computer sends the provisioning information (usually the router information) to the device over UDP/TCP (commonly via a static web page).
  5. After receiving the provisioning information, the device switches to STA mode and connects in STA mode.

Example Code

  • STA
Click to expand full code
C
#
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
C
// 定义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

  1. Enable AP mode, ssid: anthinker password:12345678, IP address 192.168.169.1
  2. Access <http://192.168.169.1> from a phone or computer
  3. Enter the SSID and PASSWORD to configure
  4. Tap Connect; once successful, the static page is no longer available.
Click to expand full code
C
#
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

Attachment

Attachment: Apstaconf.zip

Released under the MIT License. Build Time 2026-09-11 14:52:23