Skip to content

概念先知道

  • Peripheral(从机):广播自己、等待连接的一方,相当于“服务器”。
  • GATT 服务:连接后对外提供的数据结构。例程注册了 ble_tp 测试服务(ble_tp_svc),内含多个特征,属性覆盖读/写/无应答写/通知/指示。
  • 广播参数:例程使用可连接广播(BT_LE_ADV_OPT_CONNECTABLE),带设备名与厂商数据(BL616)。
  • 断开重连:断开回调里重新开启广播(set_adv_enable(true)),方便再次连接。

例程功能简介

  • 本页对应博流官方 SDK 的 peripheral 例程(examples/btble/peripheral),演示 BLE **从机(Peripheral)**基本能力:
  • 初始化控制器与主机栈(btble_controller_inithci_driver_initbt_enable),注册连接回调,注册 ble_tp 测试服务(ble_tp_init),然后以可连接广播启动(bt_le_adv_start)。
  • 连接后对端可发现服务并读写/订阅;断开后自动恢复广播。配套BLE 主机(central 例程)可直接连接它做读写演示。

操作步骤

1
进入例程目录

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

cd examples/btble/peripheral
2
编译工程

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

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

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

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

串口助手波特率 2000000,日志打印 BD_ADDR:(MSB)...(LSB)。用手机上的 nRF Connect / LightBlue 等工具扫描并连接该设备,可以看到测试服务 07af27a5-9c22-11ea-9afe-02fcdc4e7412 下的读写/通知特征。

代码执行流程

例程调用的 API 介绍

btble_controller_init(prio)

初始化 BLE 控制器(射频与链路层)。

参数

  • prio:控制器任务优先级

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

bt_enable(bt_enable_cb)

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

参数

  • cb:完成回调

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

bt_conn_cb_register(&ble_conn_callbacks)

注册连接/断开回调,用于打印状态与断线后恢复广播。

参数

  • cbbt_conn_cb 回调集

返回值:无

ble_tp_init()

注册 ble_tp 测试 GATT 服务(读/写/无应答写/通知/指示特征)。

参数

返回值:无

bt_le_adv_start(&param, adv_data, n, adv_rsp, m)

开始可连接广播:携带标志、设备名(BT_LE_ADV_OPT_USE_NAME)与厂商数据 BL616

参数

  • param:广播参数(间隔/选项)
  • adv_data:广播数据
  • n:广播项数
  • adv_rsp:扫描响应
  • m:扫描响应项数

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

bt_get_local_public_address(&addr)

获取本机公共 BLE 地址,例程用于打印 BD_ADDR。

参数

  • addrbt_addr_le_t 输出

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

完整代码

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

📜 点击展开 peripheral/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(BL702) || defined(BL602)
#include "ble_lib_api.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_tp_svc.h"
#include "hci_driver.h"
#include "hci_core.h"

#include "bflb_mtd.h"
#include "easyflash.h"

#if defined(CONFIG_BT_OAD_SERVER)
#include "oad_main.h"
#include "oad_service.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,
};

static void ble_start_adv(void)
{
    struct bt_le_adv_param param;
    int err = -1;
    struct bt_data adv_data[1] = {
        BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_NO_BREDR | BT_LE_AD_GENERAL)
    };
    struct bt_data adv_rsp[1] = {
        BT_DATA_BYTES(BT_DATA_MANUFACTURER_DATA, "BL616")
    };

    memset(&param, 0, sizeof(param));
    // Set advertise interval
    param.interval_min = BT_GAP_ADV_FAST_INT_MIN_2;
    param.interval_max = BT_GAP_ADV_FAST_INT_MAX_2;
    /*Get adv type, 0:adv_ind,  1:adv_scan_ind, 2:adv_nonconn_ind 3: adv_direct_ind*/
    param.options = (BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_USE_NAME | BT_LE_ADV_OPT_ONE_TIME); 

    err = bt_le_adv_start(&param, adv_data, ARRAY_SIZE(adv_data), adv_rsp, ARRAY_SIZE(adv_rsp));
    if(err){
        printf("Failed to start advertising (err %d) \r\n", err);
    }
    printf("Start advertising success.\r\n");
}
bool ble_check_oad(u32_t cur_file_ver, u32_t new_file_ver)
{
    return true;
}
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_tp_init();
#if defined(CONFIG_BT_OAD_SERVER)
        oad_service_enable(ble_check_oad);
        // start advertising
        ble_start_adv();
#endif
    }
}

static TaskHandle_t app_start_handle;

static void app_start_task(void *pvParameters)
{
    // Initialize BLE controller
    #if defined(BL702) || 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);

    bflb_mtd_init();
    /* ble stack need easyflash kv */
    easyflash_init();

    /* 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) {
    }
}

FAQ

手机连上但看不到服务?

确认例程打印了 Start advertising success. 且连接的设备名是 BTBLE-DEV;部分手机 App 需要点“更多”刷新服务缓存(GATT 缓存)。

写入不成功?

特征属性必须包含 WRITE 或 WRITE_NO_RESP(例程都支持);写入长度不要超过 MTU-3(默认 20 字节)。

能改设备名吗?

可以:CONFIG_BT_DEVICE_NAME(默认 BTBLE-DEV)在 defconfig/Kconfig 中配置,改后重新编译;广播里的厂商数据在 ble_start_adv 中修改。

只广播不连接可以吗?

可以:把 param.options 里的 BT_LE_ADV_OPT_CONNECTABLE 去掉即变为不可连接广播(更省电,参考 iBeacon)。

遇到问题?

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

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