Skip to content

概念先知道

  • 配网(Provisioning):把 Wi-Fi 账号密码“告诉”没有键盘屏幕的物联网设备的过程。BLE 配网就是让手机通过蓝牙把信息发给模组。
  • blesync:博流 SDK 的 BLE 配网方案(bflb_blesync 组件),例程 smartconfig_ble 即基于它;它不是乐鑫的 blufi 协议——BFLB SDK 没有 blufi 例程,BLE 配网请以本页为准。
  • EasyFlash:模组把收到的 Wi-Fi 配置存进 Flash(键值对),掉电不丢,重启自动使用。
  • 流程:模组广播(名字 bflb_blesync)→ 手机 App 连接 → App 扫描 Wi-Fi → 用户选网填密码 → 蓝牙下发 → 模组连网并保存。

例程功能简介

本页对应博流官方 SDK 的 smartconfig_ble 例程(examples/wifi/sta/smartconfig_ble):

  • bflb_mtd_init + easyflash_init 准备配置存储;
  • bflb_blesync_start() 启动 BLE 配网服务并开始广播;
  • Wi-Fi 扫描完成后调用 bflb_blesync_scan_all_ap() 把结果同步给手机 App;
  • 收到 App 下发的 SSID/密码后,通过 wifi_adapterwifiprov_bl616.c)调用 wifi_mgmr API 连接路由器;
  • 连接成功后配置保存在 EasyFlash,重启自动重连。
  • 同族参考:网页配网版(SoftAP 配网)见 路由模式(Soft-AP);BLE 基础能力见 BLE 简介

操作步骤

1
进入例程目录

在终端进入 SDK 的 BLE 配网例程目录(前提:环境已按快速开始(Linux)Windows搭好):

cd examples/wifi/sta/smartconfig_ble
2
编译工程

统一填写 bl616

make CHIP=bl616 BOARD=bl616dk
3
烧录固件

按住 BOOT 键短按 EN/RST 进入下载模式后烧录:

make flash CHIP=bl616 COMX=/dev/ttyUSB0
4
启动 BLE 配网服务

串口助手波特率 2000000,例程启动后 bflb_blesync_start() 会自动以设备名 bflb_blesync 广播。在手机上安装例程自带的 App(bflb_blesync/bleapp.apk)或其他 BLE 配网工具。

5
推送 Wi-Fi 账号密码

手机连接模组的 BLE 广播(bflb_blesync),在 App 里扫描周围 Wi-Fi(bflb_blesync_scan_all_ap),选择你的路由器并填入密码,确认后模组自动用该账号连接 Wi-Fi。

6
运行验证

模组日志出现 CODE_WIFI_ON_CONNECTEDCODE_WIFI_ON_GOT_IP 即配网成功;设备会保存配置(EasyFlash),重启后自动重连,无需再次配网。

代码执行流程

BLE 配网从启动到连接 Wi-Fi 的完整流程如下:

例程调用的 API 介绍

bflb_blesync_start()

启动 BLE 配网服务:初始化 BLE 广播与 GATT 服务(设备名 bflb_blesync),等待手机连接。

参数:无

返回值:无

bflb_blesync_scan_all_ap()

触发一次 Wi-Fi 扫描,并把扫描结果通过 BLE 发给手机 App 显示,供用户选择网络。

参数:无

返回值:无

easyflash_init()

初始化 EasyFlash(Flash 键值存储),用于保存 Wi-Fi 配置,掉电不丢。

参数:无

返回值0 成功

wifi_mgmr(wifiprov_bl616.c 内调用)

配网模块通过 wifi_mgmr API(wifi_mgmr_sta_enable / wifi_mgmr_sta_connect 等)执行实际连接,连接过程的事件(CODE_WIFI_ON_GOT_IP 等)由 wifi_event_handler 处理。

参数:由配网适配层传入 SSID / 密码

返回值0 成功

完整代码

以下为 main.c 完整源码,与官方示例(examples/wifi/sta/smartconfig_ble)一致(配网协议实现在同目录 bflb_blesync/),默认折叠,点击展开:

📜 点击展开 smartconfig_ble/main.c 完整代码
c
/****************************************************************************
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you 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.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"

#include <lwip/tcpip.h>
#include <lwip/sockets.h>
#include <lwip/netdb.h>

#include "wifi_mgmr_ext.h"

#include "bflb_irq.h"
#include "bflb_uart.h"

#include "rfparam_adapter.h"

#include "board.h"
#include "shell.h"

#include "bflb_mtd.h"
#include "easyflash.h"

#include "bflb_blesync_app.h"

#include "fhost_api.h"
#include "wifi_mgmr.h"
#include "async_event.h"
#include "mm.h"

#define DBG_TAG "MAIN"
#include "log.h"

struct bflb_device_s *gpio;

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#define WIFI_STACK_SIZE  (1536)
#define TASK_PRIORITY_FW (16)

/****************************************************************************
 * Private Types
 ****************************************************************************/

/****************************************************************************
 * Private Data
 ****************************************************************************/

static struct bflb_device_s *uart0;


extern void shell_init_with_task(struct bflb_device_s *shell);
extern void wifi_event_handler(async_input_event_t ev, void *priv);

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/****************************************************************************
 * Functions
 ****************************************************************************/

void wifi_start_firmware_task(void *param)
{
    LOG_I("Starting wifi ...\r\n");

    async_register_event_filter(EV_WIFI, wifi_event_handler, NULL);


    wifi_task_create();

    LOG_I("Starting fhost ...\r\n");
    fhost_init();

    vTaskDelete(NULL);
}

void wifi_event_handler(async_input_event_t ev, void *priv)
{
    uint32_t code = ev->code;

    switch (code) {
        case CODE_WIFI_ON_INIT_DONE: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_INIT_DONE\r\n", __func__);
            wifi_mgmr_task_start();
        } break;
        case CODE_WIFI_ON_MGMR_DONE: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_MGMR_DONE\r\n", __func__);
        } break;
        case CODE_WIFI_ON_SCAN_DONE: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_SCAN_DONE\r\n", __func__);
            //wifi_mgmr_sta_scanlist();
            bflb_blesync_scan_all_ap();
        } break;
        case CODE_WIFI_ON_CONNECTED: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_CONNECTED\r\n", __func__);
            void mm_sec_keydump();
            mm_sec_keydump();
        } break;
        #ifdef CODE_WIFI_ON_GOT_IP_ABORT
        case CODE_WIFI_ON_GOT_IP_ABORT: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_GOT_IP_ABORT\r\n", __func__);
        } break;
        #endif
        #ifdef CODE_WIFI_ON_GOT_IP_TIMEOUT
        case CODE_WIFI_ON_GOT_IP_TIMEOUT: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_GOT_IP_TIMEOUT\r\n", __func__);
        } break;
        #endif
        case CODE_WIFI_ON_GOT_IP: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_GOT_IP\r\n", __func__);
            LOG_I("[SYS] Memory left is %d Bytes\r\n", kfree_size(0));
        } break;
        case CODE_WIFI_ON_DISCONNECT: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_DISCONNECT\r\n", __func__);
        } break;
        case CODE_WIFI_ON_AP_STARTED: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_AP_STARTED\r\n", __func__);
        } break;
        case CODE_WIFI_ON_AP_STOPPED: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_AP_STOPPED\r\n", __func__);
        } break;
        case CODE_WIFI_ON_AP_STA_ADD: {
            LOG_I("[APP] [EVT] [AP] [ADD] %lld\r\n", xTaskGetTickCount());
        } break;
        case CODE_WIFI_ON_AP_STA_DEL: {
            LOG_I("[APP] [EVT] [AP] [DEL] %lld\r\n", xTaskGetTickCount());
        } break;
        default: {
            LOG_I("[APP] [EVT] Unknown code %u \r\n", code);
        }
    }
}

int main(void)
{
    board_init();

    uart0 = bflb_device_get_by_name("uart0");
    shell_init_with_task(uart0);

    bflb_mtd_init();
    /* ble stack need easyflash kv */
    easyflash_init();

    if (0 != rfparam_init(0, NULL, 0)) {
        LOG_I("PHY RF init failed!\r\n");
        return 0;
    }

    bflb_blesync_start();

    tcpip_init(NULL, NULL);
    xTaskCreate(wifi_start_firmware_task, "wifi init", 1024, NULL, 10, NULL);

    vTaskStartScheduler();

    while (1) {
    }
}

FAQ

手机搜不到 bflb_blesync 设备

确认例程已烧录并启动(日志无报错);蓝牙与 Wi-Fi 需要同时工作,注意供电是否足够(BLE+WiFi 同时开电流较大);Android 需要开启定位权限才能扫描 BLE。

App 连上后找不到要配的路由器

确认路由器在 2.4GHz 频段(BLE 配网只能配 2.4G Wi-Fi);扫描结果通过 CODE_WIFI_ON_SCAN_DONE 事件触发,等待几秒后让 App 重新请求扫描。

配置保存了吗?换路由器怎么办

配置保存在 EasyFlash(easyflash_init),掉电不丢、重启自动重连;要换路由器时,重新用 App 连上 bflb_blesync 下发新账号即可覆盖旧配置。

遇到问题?

如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

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