Contributed by WildboarG, organized by Ai-Thinker
AI-WB2 Provisioning [Part 3]
AI-WB2 Bluetooth Provisioning
Bluetooth Provisioning
Principle: phone App → via Bluetooth → sends Wi-Fi SSID & password → Wi-Fi device receives & connects to the network
1. Device Enters Bluetooth Provisioning Mode
- The device (e.g. AI-WB2, ESP32) enables BLE and acts as a ****Bluetooth Peripheral
- The device broadcasts the BLE service UUID and waits for a phone or PC to connect
2. Phone App Connects via Bluetooth
- The phone turns on Bluetooth and scans for nearby BLE devices (can filter by a specific UUID)
- After connecting to the target device, discover the custom BLE service & characteristics
3. Phone Sends Wi-Fi Information
- Write to the device through BLE characteristics:
- SSID (Wi-Fi name)
- Password
- Encryption method (optional)
- The device parses the received data and stores it in Flash or RAM.
4. Device Attempts to Connect to Wi-Fi
- The device disconnects the Bluetooth link and tries to connect to Wi-Fi with the received SSID & password.
- Once connected, it can return a success status to the phone.

Code Example
Click to expand full code
#
include
<FreeRTOS.h>
#
include
<stdint.h>
#
include
<stdio.h>
#
include
<string.h>
#
include
<task.h>
#
include
<timers.h>
#
include
<lwip/tcpip.h>
#
include
<bl602_glb.h>
#
include
<bl_sys.h>
#
include
<bl_uart.h>
#
include
<cli.h>
//////////////////////////////////
#
include
"axk_blufi.h"
#
include
"ble_interface.h"
#
include
"blufi.h"
#
include
"blufi_api.h"
#
include
"blufi_hal.h"
#
include
"blufi_init.h"
#
include
"blufi_security.h"
#
include
"wifi_interface.h"
#
include
<../wifi_mgmr.h>
//////////////////////////////////
static
int
scan_counter;
static
bool
ble_is_connected =
false
;
static
bool
gl_sta_connected =
false
;
blufi_config_t
g_blufi_config = {
0
};
static
void
blufi_wifi_event
(
int
event,
void
*param)
{
//wifi事件回调
switch
(event) {
case
BLUFI_STATION_CONNECTED:
gl_sta_connected =
true
;
break
;
case
BLUFI_STATION_DISCONNECTED:
gl_sta_connected =
false
;
break
;
case
BLUFI_STATION_GOT_IP: {
axk_blufi_extra_info_t
info;
memset
(&info,
0
,
sizeof
(
axk_blufi_extra_info_t
));
wifi_conn_ap_info_get(&g_blufi_config.wifi.sta);
memcpy
(info.sta_bssid, g_blufi_config.wifi.sta.cwjap_param.bssid,
6
);
info.sta_bssid_set =
true
;
info.sta_ssid = (
uint8_t
*)g_blufi_config.wifi.sta.cwjap_param.ssid;
info.sta_ssid_len =
strlen
(g_blufi_config.wifi.sta.cwjap_param.ssid);
if
(ble_is_connected ==
true
) {
axk_blufi_send_wifi_conn_report(g_blufi_config.wifi.cwmode,
_BLUFI_STA_CONN_SUCCESS,
0
, &info);
}
else
{
printf
(
"BLUFI BLE is not connected yet\r\n"
);
}
printf
(
"BLUFI save ssid&&pwd \r\n"
);
printf
(
"START YOUR FOUTION:\r\n"
);
//一般联网成功这里开始写用户自定义任务
g_blufi_config.wifi.cwmode = WIFIMODE_STA;
}
break
;
default
:
break
;
}
}
static
void
example_event_callback
(
_blufi_cb_event_t
event,
_blufi_cb_param_t
*param)
{
//蓝牙事件回调
switch
(event) {
case
AXK_BLUFI_EVENT_INIT_FINISH:
//BLUFI初始化完成开启蓝牙广播
printf
(
"BLUFI init finish\n"
);
axk_blufi_adv_start();
break
;
case
AXK_BLUFI_EVENT_DEINIT_FINISH:
//反初始化
printf
(
"BLUFI deinit finish\n"
);
break
;
case
AXK_BLUFI_EVENT_BLE_CONNECT:
//蓝牙已连接
printf
(
"BLUFI ble connect\n"
);
ble_is_connected =
true
;
axk_blufi_adv_stop();
blufi_security_init();
break
;
case
AXK_BLUFI_EVENT_BLE_DISCONNECT:
//蓝牙断开连接
printf
(
"BLUFI ble disconnect\n"
);
ble_is_connected =
false
;
blufi_security_deinit();
axk_blufi_adv_start();
break
;
case
AXK_BLUFI_EVENT_SET_WIFI_OPMODE:
//设置WIFI工作模式,一般都是配网,也就是用wb2连接别人,那就是STA
printf
(
"BLUFI Set WIFI opmode %d\n"
, param->wifi_mode.op_mode);
g_blufi_config.wifi.cwmode = WIFIMODE_STA;
break
;
case
AXK_BLUFI_EVENT_REQ_CONNECT_TO_AP: {
//蓝牙端获取WIFI信息并请求连接ap
cwjap_param_t
cwjap_param = {
0
};
printf
(
"BLUFI requset wifi connect to AP\n"
);
cwjap_param = g_blufi_config.wifi.sta.cwjap_param;
if
(axk_hal_conn_ap_info_set(&cwjap_param) != BLUFI_ERR_SUCCESS) {
printf
(
"BLUFI axk_hal_conn_ap_info_set fail\r\n"
);
break
;
}
g_blufi_config.wifi.sta.state = BLUFI_WIFI_STATE_CONNECTING;
}
break
;
case
AXK_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP:
//请求设备断开 Wi-Fi
printf
(
"BLUFI requset wifi disconnect from AP\n"
);
axk_hal_disconn_ap();
break
;
case
AXK_BLUFI_EVENT_REPORT_ERROR:
//Wi-Fi 连接或 BLUFI 发生错误
printf
(
"BLUFI report error, error code %d\n"
, param->report_error.state);
axk_blufi_send_error_info(param->report_error.state);
break
;
case
AXK_BLUFI_EVENT_GET_WIFI_STATUS: {
//查询 Wi-Fi 连接状态
wifi_mode_t
mode;
mode = g_blufi_config.wifi.cwmode;
if
(gl_sta_connected) {
axk_blufi_extra_info_t
info;
memset
(&info,
0
,
sizeof
(
axk_blufi_extra_info_t
));
wifi_conn_ap_info_get(&g_blufi_config.wifi.sta);
memcpy
(info.sta_bssid, g_blufi_config.wifi.sta.cwjap_param.bssid,
6
);
info.sta_bssid_set =
true
;
info.sta_ssid = (
uint8_t
*)g_blufi_config.wifi.sta.cwjap_param.ssid;
info.sta_ssid_len =
strlen
(g_blufi_config.wifi.sta.cwjap_param.ssid);
axk_blufi_send_wifi_conn_report(mode, _BLUFI_STA_CONN_SUCCESS,
0
, &info);
}
else
{
axk_blufi_send_wifi_conn_report(mode, _BLUFI_STA_CONN_FAIL,
0
,
NULL
);
}
printf
(
"BLUFI get wifi status from AP\n"
);
break
;
}
case
AXK_BLUFI_EVENT_RECV_SLAVE_DISCONNECT_BLE:
//设备主动断开 BLE 连接
printf
(
"blufi close a gatt connection\r\n"
);
axk_blufi_disconnect();
break
;
case
AXK_BLUFI_EVENT_RECV_STA_BSSID:
// 接收 Wi-Fi 路由器的 BSSID
memset
(g_blufi_config.wifi.sta.cwjap_param.bssid,
0
,
6
);
memcpy
(g_blufi_config.wifi.sta.cwjap_param.bssid, param->sta_bssid.bssid,
6
);
// sta_config.sta.bssid_set = 1;
// esp_wifi_set_config(WIFI_IF_STA, &sta_config);
printf
(
"Recv STA BSSID %s\r\n"
, param->sta_bssid.bssid);
break
;
case
AXK_BLUFI_EVENT_RECV_STA_SSID:
//接收 Wi-Fi SSID
memset
(g_blufi_config.wifi.sta.cwjap_param.ssid,
0
,
33
);
strncpy
(g_blufi_config.wifi.sta.cwjap_param.ssid,
(
char
*)param->sta_ssid.ssid, param->sta_ssid.ssid_len);
printf
(
"Recv STA SSID %s\r\n"
,
(
char
*)g_blufi_config.wifi.sta.cwjap_param.ssid);
break
;
case
AXK_BLUFI_EVENT_RECV_STA_PASSWD:
//接收 Wi-Fi 密码
// 先清空 Wi-Fi 密码存储区域,避免旧数据干扰
memset
(g_blufi_config.wifi.sta.cwjap_param.pwd,
0
,
64
);
// 这里 `param->sta_ssid.ssid` 实际上是 Wi-Fi 密码,虽然名字容易误导
// 由于结构体内存复用,所以它存的是密码,而非 SSID
strncpy
(g_blufi_config.wifi.sta.cwjap_param.pwd,
(
char
*)param->sta_ssid.ssid, param->sta_ssid.ssid_len);
// 打印收到的 Wi-Fi 密码,确保传输正确
printf
(
"Recv STA PASSWORD %s\r\n"
,
(
char
*)g_blufi_config.wifi.sta.cwjap_param.pwd);
break
;
default
:
break
;
}
}
static
_blufi_callbacks_t
example_callbacks = {
.event_cb = example_event_callback,
.negotiate_data_handler = blufi_dh_negotiate_data_handler,
.encrypt_func = blufi_aes_encrypt,
.decrypt_func = blufi_aes_decrypt,
.checksum_func = blufi_crc_checksum,
};
int
at_blufi_start
(
void
)
{
int
ret =
-1
;
axk_hal_blufi_init();
ret = _blufi_host_and_cb_init(&example_callbacks);
if
(ret) {
printf
(
"%s initialise failed: %d\n"
, __func__, ret);
}
return
ret;
}
static
void
proc_main_entry
(
void
*pvParameters)
{
wifi_interface_init(blufi_wifi_event);
at_blufi_start();
vTaskDelete(
NULL
);
}
void
main
()
{
static
TaskHandle_t proc_main_task;
bl_sys_init();
xTaskCreate(proc_main_entry, (
char
*)
"main_entry"
,
1024
,
NULL
,
15
,
&proc_main_task);
tcpip_init(
NULL
,
NULL
);
printf
(
"blufi demo test\r\n"
);
}Flow Analysis
Let's analyze the connection flow with the logs;
- First, I flash the program and open the serial port to check the logs
- In the provisioning section of the Ai-Thinker IoT WeChat mini program, there is a BLUFI provisioning option, which is Bluetooth provisioning. Remember to turn on Bluetooth and grant the mini program Bluetooth permission.
- Scan for devices

- The name is
AXK_BLUFI; tap to connect

- Make sure the phone is connected to 2.4G Wi-Fi, enter the password, then tap "Configure Wi-Fi"

- Then look at the logs printed on our serial port

Pay attention to the [blufi] lines in the output; these lines cover the process from the mini program connecting to Bluetooth all the way to successful provisioning
Two functions appear in the messages:
btc_transfer_context: used to transfer BLE tasks across threads, ensuring safe data transfer.
btc_thread_handler: executes BLE events (such as Wi-Fi connection) in the BTC thread
One is the event flag issued by the Bluetooth side; the other is the task that executes the corresponding event. In short, a task needs to be executed.
For example, the third line BLUFI ble connect — based on the Bluetooth event callback function, this means the Bluetooth connection is complete. So the tasks passed in the first two lines are to execute the Bluetooth connection, i.e., the task initiated from the phone side after I scanned and tapped AXK_BLUFI in the mini program.
[blufi]btc_transfer_context msg 1 3 0x42023634 [blufi] btc_thread_handler msg 1 3 0x4201bad0
//act=3 <====> AXK_BLUFI_EVENT_BLE_CONNECT
BLUFI ble connect
[BLE] connected [BLE] conn param updated: int 0x0006 lat 0 to 500 [BLE] conn param updated: int 0x0027 lat 0 to 500 [BLE] mtu updated:247
[BLUFI] ccc change 1 客户端已启用通知(用于接收 Wi-Fi 连接状态)
[BLE] conn param updated: int 0x0020 lat 0 to 400 [blufi]btc_transfer_context msg 1 10 0x42023584 [blufi] btc_thread_handler msg 1 10 0x4201bad0
//act=10 <====>AXK_BLUFI_EVENT_RECV_STA_SSID
Recv STA SSID CU_5ZPu
[blufi]btc_transfer_context msg 1 11 0x42023584 [blufi] btc_thread_handler msg 1 11 0x4201bad0
//act=11<====>AXK_BLUFI_EVENT_RECV_STA_PASSWD
Recv STA PASSWORD HYGS3305
[blufi]btc_transfer_context msg 1 5 0x00000000 [blufi] btc_thread_handler msg 1 5 0x00000000
//act=5 <====>AXK_BLUFI_EVENT_REQ_CONNECT_TO_AP
BLUFI requset wifi connect to AP
// 往下隔了wifi连接的日志还有几行
...
[blufi]btc_transfer_context msg 0 2 0x4201989c [blufi] btc_thread_handler msg 0 2 0x4201b780
//act=2 <====> AXK_BLUFI_EVENT_REQ_CONNECT_TO_AP
BLUFI save ssid&&pwd START YOUR FOUTION // 这里就表示已经连接好了wifi,后面写自己的逻辑代码
To verify the guess, find in the code where these logs are printed.
Search the whole project for this function btc_transfer_context — it is located in ble_btc.c
Inside, we find:
Click to expand full code
printf
(
"[blufi]%s msg %u %u %p\n"
, __func__, msg->sig, msg->act, arg);Doesn't it exactly match the first line?
Click to expand full code
[blufi]函数名 msg msg->sig msg->act ,argsig: the message type. Taking the first line as an example, sig=1 means the protocol stack is responding to the application layer.
Click to expand full code
typedef
enum
{
BTC_SIG_API_CALL =
0
,
// APP TO STACK
BTC_SIG_API_CB,
// STACK TO APP
BTC_SIG_NUM,
}
btc_sig_t
;
//btc message typeact: the specific response event number. Again taking the first line as an example, the event number is 3, which exactly corresponds to AXK_BLUFI_EVENT_BLE_CONNECT, the Bluetooth connection event — the guess is verified

- Connection successful


After that, you can write your own task functions.

