概念先知道
- Shell(命令行):通过串口输入文本命令来操控设备的方式。本页例程把 BLE 的几乎所有能力(扫描、广播、连接、GATT 读写、安全配对、吞吐测试等)都封装成了命令,适合做调试和功能验证,不用反复改代码重烧。
- BLE 主机栈(Host):负责 GAP/GATT/L2CAP 的软件协议栈,例程通过
btble_controller_init→hci_driver_init→bt_enable三步启动。 - GATT 客户端操作:连接建立后,主机作为客户端去发现服务、读写特征、订阅通知,这些在例程里对应
ble_discover、ble_read、ble_write、ble_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 | 协商 MTU | ble_exchange_mtu |
ble_tp_start | 开启吞吐量测试 | ble_tp_start 1 |
操作步骤
在终端进入 SDK 的 btble_cli 例程目录(前提:环境已按快速开始(Linux)或Windows搭好):
cd examples/btble/btble_cliAi-M61 与 Ai-M62 统一填写 bl616:
make CHIP=bl616 BOARD=bl616dk按住 BOOT 键不放、短按 EN/RST 进入下载模式后烧录:
make flash CHIP=bl616 COMX=/dev/ttyUSB0串口助手波特率 2000000。上电后日志会打印本机 BLE 地址 BD_ADDR。在命令行依次输入 ble_init 和 ble_enable 初始化并启用 BLE,再输入 ble_start_adv 0 0 0x80 0x80 开启可连接广播,用手机上的 nRF Connect 就能扫到并连接。
代码执行流程
例程调用的 API 介绍
btble_controller_init(prio)
初始化 BLE 控制器(射频与链路层),创建控制器任务。
参数:
prio:控制器任务优先级,例程传入configMAX_PRIORITIES - 1
返回值:成功返回 0;失败返回负值
set_adv_enable(enable)
开启/关闭广播。例程在断开回调里调用 set_adv_enable(true) 自动恢复广播。
参数:
enable:true开启广播;false关闭
返回值:成功返回 0;失败返回负值
完整代码
以下为 btble_cli 例程 main.c 完整源码,与官方示例一致,默认折叠,点击展开:
📜 点击展开 btble_cli/main.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

