Skip to content

概念先知道

  • Shell(命令行):通过串口输入文本命令来操控设备的方式。本页例程把 BLE 的几乎所有能力(扫描、广播、连接、GATT 读写、安全配对、吞吐测试等)都封装成了命令,适合做调试和功能验证,不用反复改代码重烧。
  • BLE 主机栈(Host):负责 GAP/GATT/L2CAP 的软件协议栈,例程通过 btble_controller_inithci_driver_initbt_enable 三步启动。
  • GATT 客户端操作:连接建立后,主机作为客户端去发现服务、读写特征、订阅通知,这些在例程里对应 ble_discoverble_readble_writeble_subscribe 命令。
  • 注意:这个例程的固件还编译了 BLE Mesh 命令(blemesh_*),但 Mesh 功能官方尚未提供完整验证例程,本页只介绍 BLE 常规(非 Mesh)命令。

例程功能简介

  • 本页对应博流官方 SDK 的 btble_cli 例程(examples/btble/btble_cli),把 BLE 协议栈的调试命令(ble_*)注册进串口 Shell。
  • 支持芯片:BL602 / BL616 / BL618 / BL618DG / BL616CL;Ai-M61(BL618)与 Ai-M62(BL616)统一按 bl616 编译。
  • 常用命令速查(完整命令见 SDK 文档 docs/en/api_reference/ble.rst):
命令作用示例
ble_init初始化 BLE 协议栈ble_init
ble_enable启用 BLE 功能ble_enable
ble_start_adv开始广播(类型/模式/最小间隔/最大间隔)ble_start_adv 0 0 0x80 0x80
ble_stop_adv停止广播ble_stop_adv
ble_start_scan开始扫描(类型/去重/间隔/窗口)ble_start_scan 1 1 0x0080 0x0050
ble_connect连接指定地址的设备ble_connect 0 112233AABBCC
ble_disconnect断开连接ble_disconnect 0 112233AABBCC
ble_discover发现 GATT 服务/特征ble_discover 0 1800 0001 ffff
ble_read读特征值(句柄/偏移)ble_read 0001 0000
ble_write写特征值ble_write 0002 0000 0003 414243
ble_subscribe订阅通知/指示(CCC 句柄/值句柄/类型)ble_subscribe 0004 0003 1
ble_exchange_mtu协商 MTUble_exchange_mtu
ble_tp_start开启吞吐量测试ble_tp_start 1

操作步骤

1
进入例程目录

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

cd examples/btble/btble_cli
2
编译工程

Ai-M61 与 Ai-M62 统一填写 bl616

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

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

make flash CHIP=bl616 COMX=/dev/ttyUSB0
4
运行验证

串口助手波特率 2000000。上电后日志会打印本机 BLE 地址 BD_ADDR。在命令行依次输入 ble_initble_enable 初始化并启用 BLE,再输入 ble_start_adv 0 0 0x80 0x80 开启可连接广播,用手机上的 nRF Connect 就能扫到并连接。

代码执行流程

例程调用的 API 介绍

btble_controller_init(prio)

初始化 BLE 控制器(射频与链路层),创建控制器任务。

参数

  • prio:控制器任务优先级,例程传入 configMAX_PRIORITIES - 1

返回值:成功返回 0;失败返回负值

hci_driver_init()

初始化 HCI 驱动,连接控制器与主机协议栈。

参数:无

返回值:无

bt_enable(bt_enable_cb)

启动 BLE 主机协议栈,完成后回调 bt_enable_cb

参数

  • cb:启动完成回调

返回值:成功返回 0;失败返回负值

ble_cli_register()

ble_* 系列命令注册进串口 Shell(由 ble_cli_cmds 组件提供)。

参数:无

返回值:无

set_adv_enable(enable)

开启/关闭广播。例程在断开回调里调用 set_adv_enable(true) 自动恢复广播。

参数

  • enabletrue 开启广播;false 关闭

返回值:成功返回 0;失败返回负值

完整代码

以下为 btble_cli 例程 main.c 完整源码,与官方示例一致,默认折叠,点击展开:

📜 点击展开 btble_cli/main.c 完整代码
c
#include "shell.h"
#include <FreeRTOS.h>
#include "task.h"
#include "board.h"

#include "bluetooth.h"
#include "conn.h"
#include "conn_internal.h"
#if defined(BL602)
#include "ble_lib_api.h"
#include "bl602_glb.h"
#include "rfparam_adapter.h"
#elif defined(BL616)
#include "btble_lib_api.h"
#include "bl616_glb.h"
#include "rfparam_adapter.h"
#elif defined(BL618DG)
#include "btble_lib_api.h"
#include "bl618dg_glb.h"
#include "rfparam_adapter.h"
#elif defined(BL616CL)
#include "btble_lib_api.h"
#include "bl616cl_glb.h"
#include "rfparam_adapter.h"
#endif

#include "ble_cli_cmds.h"
#include "hci_driver.h"
#include "hci_core.h"

#if defined(CONFIG_BT_BREDR)
extern int bredr_cli_register(void);
#endif

#if defined(CONFIG_BT_SETTINGS)
#include "bflb_mtd.h"
#include "easyflash.h"
#endif

#if defined(CONFIG_BLE_TP_SERVER)
#include "ble_tp_svc.h"
#endif

static struct bflb_device_s *uart0;

extern void shell_init_with_task(struct bflb_device_s *shell);

static void ble_connected(struct bt_conn *conn, u8_t err)
{
    if(err || conn->type != BT_CONN_TYPE_LE)
    {
        return;
    }
    printf("%s",__func__);
}

static void ble_disconnected(struct bt_conn *conn, u8_t reason)
{ 
    int ret;

    if(conn->type != BT_CONN_TYPE_LE)
    {
        return;
    }

    printf("%s",__func__);

    // enable adv
    ret = set_adv_enable(true);
    if(ret) {
        printf("Restart adv fail. \r\n");
    }
}

static struct bt_conn_cb ble_conn_callbacks = {
	.connected	=   ble_connected,
	.disconnected	=   ble_disconnected,
};

void bt_enable_cb(int err)
{
    if (!err) {
        bt_addr_le_t bt_addr;
        bt_get_local_public_address(&bt_addr);
        printf("BD_ADDR:(MSB)%02x:%02x:%02x:%02x:%02x:%02x(LSB) \r\n",
               bt_addr.a.val[5], bt_addr.a.val[4], bt_addr.a.val[3], bt_addr.a.val[2], bt_addr.a.val[1], bt_addr.a.val[0]);
        bt_conn_cb_register(&ble_conn_callbacks);
        ble_cli_register();

        #if defined(CONFIG_BLE_TP_SERVER)
        ble_tp_init();
        #endif

        #if defined(CONFIG_BT_TP_CLI)
        extern int ble_tp_cli_register(void);
        ble_tp_cli_register();
        #endif

        #if defined(CONFIG_BT_BREDR)
        extern int bredr_cli_register(void);
        bredr_cli_register();
        #endif
    }
}

static TaskHandle_t app_start_handle;

static void app_start_task(void *pvParameters)
{
    // Initialize BLE controller
    #if defined(BL602)
    ble_controller_init(configMAX_PRIORITIES - 1);
    #else
    btble_controller_init(configMAX_PRIORITIES - 1);
    #endif
    // Initialize BLE Host stack
    hci_driver_init();
    bt_enable(bt_enable_cb);

    vTaskDelete(NULL);
}

int main(void)
{
    board_init();

    configASSERT((configMAX_PRIORITIES > 4));

    uart0 = bflb_device_get_by_name("uart0");
    shell_init_with_task(uart0);
#if defined(CONFIG_BT_SETTINGS)
    bflb_mtd_init();
    /* ble stack need easyflash kv */
    easyflash_init();
#endif

    /* Init rf */
    if (0 != rfparam_init(0, NULL, 0)) {
        printf("PHY RF init failed!\r\n");
        return 0;
    }

    #if defined(BL618DG)
    #if defined(CONFIG_BTBLE_USE_STANDALONE_PATH)
    printf("cmd_set_btble_standalone\r\n");
    extern void cmd_set_btble_standalone(int argc, char **argv);
    cmd_set_btble_standalone(0, 0);
    #else
    printf("cmd_set_btble_combo\r\n");
    extern void cmd_set_btble_combo(int argc, char **argv);
    cmd_set_btble_combo(0, 0);
    #endif
    #endif

    xTaskCreate(app_start_task, (char *)"app_start", 1024, NULL, configMAX_PRIORITIES - 2, &app_start_handle);

    vTaskStartScheduler();

    while (1) {
    }
}
#if defined(BL616CL)
#include "mm.h"
#include "bl616cl_glb.h"

int heap_add_em(int argc, char **argv)
{
    extern uint8_t __LD_CONFIG_EM_SEL;
    volatile uint32_t em_size;
    em_size = (uint32_t)&__LD_CONFIG_EM_SEL;

    if (em_size > 0) {
        uint32_t em_heap_addr = 0x21020000 - em_size;
        GLB_Set_EM_Sel(GLB_WRAM160KB_EM0KB);
        mm_register_heap(MM_HEAP_EM_0, "EM", MM_ALLOCATOR_HEAP5,
                         (void *)em_heap_addr, em_size);
    }
    return 0;
}
SHELL_CMD_EXPORT_ALIAS(heap_add_em, heap_add_em, heap add EM.);
#endif /* BL616CL */

FAQ

输入 ble_init 提示 command not found

确认编译时开启了 BLE 命令行功能:例程 defconfig 中需包含 CONFIG_BT_STACK_CLI =y;同时 BLE 命令要等上电日志打印出 BD_ADDR 之后才可用(主机栈启动完成才注册命令)。

扫描不到任何设备

确认命令参数:ble_start_scan 1 1 0x0080 0x0050(主动扫描、过滤重复、间隔 0x0080、窗口 0x0050);手机端扫描需要开启定位权限;另外确认对端设备正在广播。

连接后无法发现服务或读写失败

先用 ble_discover 0 1800 0001 ffff 找到服务/特征句柄,再按句柄执行 ble_read / ble_write;如果特征需要先订阅通知,用 ble_subscribe <ccc句柄> <值句柄> 1

例程支持经典蓝牙(BR/EDR)吗

defconfig 中非 bl616cl 分支开启了 CONFIG_BT_BREDR =y 等选项,启动时也会注册经典蓝牙命令;本页只演示 BLE(LE)命令,经典蓝牙相关能力未在文档中展开。

遇到问题?

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

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