Skip to content

由 WildboarG 贡献,Ai-Thinker 进行整理

AI-WB2 配网【上】

AI-WB2 html网页配网

引言


早期的Wi-Fi设备大多都用于带屏幕和输入设备的(如嵌入式设备/笔记本/手机等),配网只需要用户连接路由器的SSID输入password即可。

但是随着物联网的兴起,Wi-Fi被大量应用于没有人机交互方式的设备中,如智能插座等物联网终端产品中,而Wi-Fi(IEEE802.11)标准在设计之初是没有考虑这种无人机交互方式的配网的,也就是没有官方标准。这也就导致了市面上配网的方法多种多样,且各家玩各家的,鱼龙混杂,互不相通,整体体验偏低。

简化示意图如下:

常见的配网方式有:

  • STA+AP配网
  • STA配网
  • 蓝牙配网
  • 一键配网(SMART CONFIG)
    • 广播包长方式
    • 组播地址方式
  • 其他

两个基本概念:

STA : STA称为“客户端模式”,是指设备(如手机、笔记本、单片机等)作为 Wi-Fi 客户端,连接到无线 AP(路由器或热点)以访问网络。STA 模式的特点:

  • 设备需要连接到 AP 才能访问互联网或局域网。
  • 典型应用:手机连接 Wi-Fi、电脑连接无线路由器等。

AP : AP 模式也称为“热点模式”或“SoftAP(软件AP)模式”,用于创建一个无线网络,允许其他 STA 设备连接。

AP 模式的特点:

  • 设备自身变成 Wi-Fi 热点,其他设备可以连接到它。
  • 适用于 IoT 设备配网、临时网络等应用。

STA & STA+AP配网


  • STA STA配网原理就和你手机连接路由器一样简单。预先将路由器或者路由设备(能够产生热点的设备)开启,然后需要配网的设备(例如WB2/M61/ESP8266等)内部写入与之匹配固定的SSID与PASSWORD,当配网设备运行后,会扫描附近热点,一旦匹配到对应的SIID,将尝试连接,成功即可返回。

    通常sta有动态获取和静态两种选项:

    动态:由路由器DHCP随机分配未使用的IP地址

    静态:预先将我想使用的ip地址写入程序中,连接后设置我要使用的ip地址。更方便些。

  • STA+AP

  1. 设备端(WB2/M61/ESP8266等)进入softAP模式,然后建立一个约定好的AP热点。
  2. 手机APP端/电脑 用户去连接步骤1中建立的热点。
  3. 连接后,手机与设备端建立局域网通信。
  4. 手机APP/电脑 通过UDP/TCP(常见的有静态网页)将配网信息(常见的是路由器)传递给设备。
  5. 设备端拿到配网信息之后切换为STA模式,用STA模式连接。

示例代码

  • STA
📜 点击展开完整代码
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
);
// 删除当前任务

}

设置静态ip

📜 点击展开完整代码
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

用AP开启一个静态网页去连接WIFI

  1. 启用ap模式,ssid: anthinker password:12345678 ip地址为192.168.169.1
  2. 手机或电脑访问<http://192.168.169.1>
  3. 输入要配置的SSID 和 PASSWORD
  4. 点击连接,成功后静态页面就不可用了。
📜 点击展开完整代码
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
);
}

效果

ap+sta效果

附件

附件:Apstaconf.zip

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