Concepts First
- Shell (command line): the way to control the device by typing text commands over UART. This example exposes almost every BLE capability (scanning, advertising, connection, GATT read/write, security pairing, throughput testing, etc.) as commands — great for debugging and verification without recompiling firmware.
- BLE Host stack: the software protocol stack that handles GAP/GATT/L2CAP. The example starts it in three steps:
btble_controller_init→hci_driver_init→bt_enable. - GATT client operations: after a connection is established, the central acts as a GATT client to discover services, read/write characteristics, and subscribe to notifications — mapped to
ble_discover,ble_read,ble_write, andble_subscribecommands here. - Note: this firmware also compiles BLE Mesh commands (
blemesh_*), but Mesh has no fully verified official example yet; this page only covers regular (non-Mesh) BLE commands.
Example Overview
- This page covers the official btble_cli example (
examples/btble/btble_cli), which registers the BLE stack debugging commands (ble_*) into the serial Shell. - Supported chips: BL602 / BL616 / BL618 / BL618DG / BL616CL; Ai-M61 (BL618) and Ai-M62 (BL616) both build with
bl616. - Quick command reference (the full command list is in the SDK docs
docs/en/api_reference/ble.rst):
| Command | Purpose | Example |
|---|---|---|
ble_init | Initialize the BLE stack | ble_init |
ble_enable | Enable BLE functionality | ble_enable |
ble_start_adv | Start advertising (type/mode/min interval/max interval) | ble_start_adv 0 0 0x80 0x80 |
ble_stop_adv | Stop advertising | ble_stop_adv |
ble_start_scan | Start scanning (type/filtering/interval/window) | ble_start_scan 1 1 0x0080 0x0050 |
ble_connect | Connect to a remote address | ble_connect 0 112233AABBCC |
ble_disconnect | Disconnect | ble_disconnect 0 112233AABBCC |
ble_discover | Discover GATT services/characteristics | ble_discover 0 1800 0001 ffff |
ble_read | Read a characteristic (handle/offset) | ble_read 0001 0000 |
ble_write | Write a characteristic | ble_write 0002 0000 0003 414243 |
ble_subscribe | Subscribe to notifications/indications (CCC handle/value handle/type) | ble_subscribe 0004 0003 1 |
ble_exchange_mtu | Exchange MTU | ble_exchange_mtu |
ble_tp_start | Start throughput testing | ble_tp_start 1 |
Operation Steps
Enter the SDK’s btble_cli example directory (prerequisite: set up the environment with Quick Start (Linux) or Windows):
cd examples/btble/btble_cliAi-M61 and Ai-M62 both use bl616:
make CHIP=bl616 BOARD=bl616dkHold BOOT, tap EN/RST to enter download mode, then flash:
make flash CHIP=bl616 COMX=/dev/ttyUSB0Open the serial terminal at 2000000 baud. After boot, the log prints the local BLE address BD_ADDR. Type ble_init then ble_enable to initialize and enable BLE, then ble_start_adv 0 0 0x80 0x80 to start connectable advertising — a phone with nRF Connect can scan and connect.
Code Execution Flow
APIs Used by the Example
btble_controller_init(prio)
Initialize the BLE controller (radio and link layer) and create its task.
Parameters:
prio: controller task priority; the example passesconfigMAX_PRIORITIES - 1
Return value: 0 on success; negative on failure
hci_driver_init()
Initialize the HCI driver that links the controller and the host stack.
Parameters: none
Return value: none
bt_enable(bt_enable_cb)
Start the BLE host stack; bt_enable_cb is called when ready.
Parameters:
cb: completion callback
Return value: 0 on success; negative on failure
ble_cli_register()
Register all ble_* commands into the serial Shell (provided by the ble_cli_cmds component).
Parameters: none
Return value: none
set_adv_enable(enable)
Enable or disable advertising. The example calls set_adv_enable(true) in the disconnect callback to resume advertising automatically.
Parameters:
enable:trueto advertise;falseto stop
Return value: 0 on success; negative on failure
Complete Code
The full main.c of the btble_cli example, identical to the official SDK, collapsed by default — click to expand:
📜 Click to expand btble_cli/main.c full code
#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 says command not found
Make sure the BLE CLI feature is enabled at build time: the example defconfig must include CONFIG_BT_STACK_CLI =y. Also, ble_* commands only work after the boot log prints BD_ADDR (they are registered once the host stack is up).
No devices found while scanning
Check the parameters: ble_start_scan 1 1 0x0080 0x0050 (active scan, duplicate filtering, interval 0x0080, window 0x0050). On the phone side, location permission is required for BLE scanning; also confirm the peer device is actually advertising.
Cannot discover services or read/write after connecting
First run ble_discover 0 1800 0001 ffff to find the service/characteristic handles, then use ble_read / ble_write with those handles. If the characteristic requires notifications, subscribe first with ble_subscribe <ccc-handle> <value-handle> 1.
Does this example support classic Bluetooth (BR/EDR)
The defconfig enables options like CONFIG_BT_BREDR =y on non-bl616cl builds, so classic Bluetooth commands are registered too; this page only demonstrates BLE (LE) commands, and classic Bluetooth is not covered here.
Have questions?
For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

