Skip to content

Concepts First

  • Provisioning: telling an IoT device (which has no keyboard or screen) the Wi-Fi credentials. BLE provisioning sends them from a phone over Bluetooth.
  • blesync: the Bouffalo SDK's BLE provisioning solution (bflb_blesync component); the smartconfig_ble example is based on it. This is not Espressif's blufi protocol — the BFLB SDK has no blufi example; for BLE provisioning use this page.
  • EasyFlash: the module stores the received Wi-Fi config in Flash (key-value), survives power loss and reconnects automatically after reboot.
  • Flow: module advertises (name bflb_blesync) → phone app connects → app scans Wi-Fi → user picks a network and enters the password → BLE sends it → module joins and saves.

Example Overview

This page covers the smartconfig_ble example in the official Bouffalo SDK (examples/wifi/sta/smartconfig_ble):

  • bflb_mtd_init + easyflash_init prepare config storage;
  • bflb_blesync_start() starts the BLE provisioning service and advertising;
  • after a Wi-Fi scan completes, bflb_blesync_scan_all_ap() sends the results to the phone app;
  • when the app sends SSID/password, the wifi_adapter (wifiprov_bl616.c) connects via the wifi_mgmr API;
  • on success the config is saved in EasyFlash and reused after reboot.
  • Related reference: web-based provisioning (SoftAP) in Soft-AP Mode; BLE basics in BLE Introduction.

Operation Steps

1
Enter the Example Directory

Open a terminal and enter the SDK BLE provisioning example directory (prerequisite: set up the environment as in Quick Start (Linux) or Windows):

cd examples/wifi/sta/smartconfig_ble
2
Build the Project

Both Ai-M61 and Ai-M62 use bl616:

make CHIP=bl616 BOARD=bl616dk
3
Flash the Firmware

Hold BOOT, briefly press EN/RST to enter download mode, then flash:

make flash CHIP=bl616 COMX=/dev/ttyUSB0
4
Start the BLE Provisioning Service

Serial tool at 2000000 baud. At boot, bflb_blesync_start() starts advertising as bflb_blesync. Install the bundled app (bflb_blesync/bleapp.apk) or another BLE provisioning tool on the phone.

5
Push Wi-Fi Credentials

Connect the phone to the module’s BLE advertisement (bflb_blesync), scan nearby Wi-Fi in the app (bflb_blesync_scan_all_ap), pick your router, enter the password, and confirm — the module automatically connects to that Wi-Fi.

6
Run and Verify

The module logs CODE_WIFI_ON_CONNECTED and CODE_WIFI_ON_GOT_IP — provisioning succeeded. The config is saved (EasyFlash); after reboot the module reconnects automatically, no need to provision again.

Code Execution Flow

The complete BLE provisioning flow from boot to Wi-Fi connection:

APIs Used by the Example

bflb_blesync_start()

Starts the BLE provisioning service: initializes BLE advertising and the GATT service (device name bflb_blesync), waiting for a phone connection.

Parameters: none

Return: none

bflb_blesync_scan_all_ap()

Triggers a Wi-Fi scan and sends the results to the phone app over BLE for the user to pick a network.

Parameters: none

Return: none

easyflash_init()

Initializes EasyFlash (Flash key-value storage) for saving Wi-Fi config that survives power loss.

Parameters: none

Return: 0 on success

wifi_mgmr(called in wifiprov_bl616.c)

The provisioning module uses the wifi_mgmr API (wifi_mgmr_sta_enable / wifi_mgmr_sta_connect etc.) to perform the actual connection; events (CODE_WIFI_ON_GOT_IP etc.) are handled by wifi_event_handler.

Parameters: SSID/password passed by the provisioning adapter

Return: 0 on success

Complete Code

The full main.c source, identical to the official example (examples/wifi/sta/smartconfig_ble; provisioning protocol in bflb_blesync/ in the same directory). Collapsed by default; click to expand:

📜 Click to expand smartconfig_ble/main.c full code
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

The phone cannot find bflb_blesync

Confirm the example flashed and booted without errors; BLE and Wi-Fi work simultaneously, so check the power supply (BLE+WiFi draws more current); on Android, location permission is required to scan BLE.

The router I want is not listed in the app

Confirm the router is on 2.4GHz (BLE provisioning can only configure 2.4G Wi-Fi); scan results are triggered by the CODE_WIFI_ON_SCAN_DONE event — wait a few seconds and ask the app to rescan.

Is the config saved? What if I change routers

The config is stored in EasyFlash (easyflash_init), survives power loss, and reconnects after reboot; to switch routers, reconnect to bflb_blesync in the app and send the new credentials to overwrite the old config.

Have questions?

For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

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