概念先知道
- 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_init→hci_driver_init→bt_enable),注册连接回调,注册ble_tp测试服务(ble_tp_init),然后以可连接广播启动(bt_le_adv_start)。 - 连接后对端可发现服务并读写/订阅;断开后自动恢复广播。配套:BLE 主机(central 例程)可直接连接它做读写演示。
操作步骤
在终端进入 SDK 的 BLE peripheral(从机)例程目录(前提:环境已按快速开始(Linux)或Windows搭好):
cd examples/btble/peripheralAi-M61 与 Ai-M62 统一填写 bl616:
make CHIP=bl616 BOARD=bl616dk按住 BOOT 键不放、短按 EN/RST 进入下载模式后烧录:
make flash CHIP=bl616 COMX=/dev/ttyUSB0串口助手波特率 2000000,日志打印 BD_ADDR:(MSB)...(LSB)。用手机上的 nRF Connect / LightBlue 等工具扫描并连接该设备,可以看到测试服务 07af27a5-9c22-11ea-9afe-02fcdc4e7412 下的读写/通知特征。
代码执行流程
例程调用的 API 介绍
bt_le_adv_start(¶m, 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。
参数:
addr:bt_addr_le_t输出
返回值:成功返回 0;失败返回负值
完整代码
以下为 peripheral 例程完整源码,与官方示例一致,默认折叠,点击展开:
📜 点击展开 peripheral/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(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(¶m, 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(¶m, 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

