Skip to content

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_inithci_driver_initbt_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, and ble_subscribe commands 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):
CommandPurposeExample
ble_initInitialize the BLE stackble_init
ble_enableEnable BLE functionalityble_enable
ble_start_advStart advertising (type/mode/min interval/max interval)ble_start_adv 0 0 0x80 0x80
ble_stop_advStop advertisingble_stop_adv
ble_start_scanStart scanning (type/filtering/interval/window)ble_start_scan 1 1 0x0080 0x0050
ble_connectConnect to a remote addressble_connect 0 112233AABBCC
ble_disconnectDisconnectble_disconnect 0 112233AABBCC
ble_discoverDiscover GATT services/characteristicsble_discover 0 1800 0001 ffff
ble_readRead a characteristic (handle/offset)ble_read 0001 0000
ble_writeWrite a characteristicble_write 0002 0000 0003 414243
ble_subscribeSubscribe to notifications/indications (CCC handle/value handle/type)ble_subscribe 0004 0003 1
ble_exchange_mtuExchange MTUble_exchange_mtu
ble_tp_startStart throughput testingble_tp_start 1

Operation Steps

1
Enter the example directory

Enter the SDK’s btble_cli example directory (prerequisite: set up the environment with Quick Start (Linux) or Windows):

cd examples/btble/btble_cli
2
Build the project

Ai-M61 and Ai-M62 both use bl616:

make CHIP=bl616 BOARD=bl616dk
3
Flash the firmware

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

make flash CHIP=bl616 COMX=/dev/ttyUSB0
4
Run and verify

Open 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 passes configMAX_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: true to advertise; false to 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
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 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

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