概念先知道
- iBeacon:苹果定义的蓝牙广播格式,把 UUID(16 字节)+ Major(2 字节)+ Minor(2 字节)+ 信号强度 打包进广播包的厂商自定义数据段(Company ID 为
0x004C)。 - UUID / Major / Minor:UUID 标识“是哪一组信标”(如某个连锁店的店铺组),Major/Minor 进一步细分(如哪家分店、哪个货架)。
- 广播生命周期:例程演示
controller_init → adv_init → set_parameter → set_data → adv_start → adv_stop → adv_deinit → controller_deinit的完整循环,并检查堆内存是否有泄漏。 - 不可连接广播(ADV_NONCONN_IND):iBeacon 只“喊话”不建立连接,扫描者只能收到广播,不能连接它。
例程功能简介
本页对应博流官方 SDK 的 adv 例程(examples/btble/adv,README 标注 Chip support: BL616CL only):
- 用
CONFIG_BLE_BEACON_ONLY直接控制器 API(绕过 HCI 传输层),内存占用极低; build_ibeacon_data()按 iBeacon 格式组装广播数据:Flags + 厂商数据(0x004C/0x0215/ UUID / Major / Minor / TX Power);- 每轮广播 3 秒(间隔 100~150ms),共 3 轮,打印每步堆内存变化,验证无循环泄漏;
- 例程参数(UUID、Major、Minor、TX Power)都在
main.c开头的宏里,可自行修改。 - 同族参考:需要“可连接 + 服务/特征”的从机场景见 BLE 从机;普通 BL616/BL618 上做广播可参考
examples/btble/peripheral的ble_start_adv()。
操作步骤
⚠️ 本页例程(examples/btble/adv)只支持 BL616CL(BLE-only 芯片),需要 CONFIG_BLE_BEACON_ONLY=y,不支持 Ai-M61(BL618)/ Ai-M62(BL616)的普通版本。这是 SDK README 明确标注的限制,请先在对应型号的板子上操作。
在终端进入 SDK 的 beacon 例程目录(前提:环境已按快速开始(Linux)或Windows搭好):
cd examples/btble/adv用 bl616cl 编译:
make CHIP=bl616cl BOARD=bl616cldk按住 BOOT 键短按 EN/RST 进入下载模式后烧录:
make flash CHIP=bl616cl COMX=/dev/ttyUSB0串口助手波特率 2000000。程序自动执行 3 轮“初始化→广播→停止→反初始化”生命周期,每轮广播 3 秒(iBeacon 格式,不可连接);用手机上的 nRF Connect / LightBlue / Beacon Scope 扫描,能看到厂商数据为 0x004C(Apple)的 iBeacon 广播。
代码执行流程
beacon 例程单轮广播的完整流程如下:
例程调用的 API 介绍
btble_controller_init / btble_controller_deinit(priority) / ()
初始化/反初始化 BLE 控制器(BEACON_ONLY 模式下堆占用极低,初始化约 244 字节,反初始化完全回收)。
参数:
priority:控制器任务优先级(例程configMAX_PRIORITIES - 1)
返回值:0 成功;负值错误码
btble_adv_set_parameter(¶ms)
设置广播参数:间隔(100~150ms)、类型(BTBLE_ADV_TYPE_NON_CONNECTABLE_UNDIRECTED 不可连接)、信道(全部)等。
参数:
params:struct btble_adv_params
返回值:0 成功;负值错误码
btble_adv_set_data / btble_adv_start / btble_adv_stop(data, len) / () / ()
设置广播数据(iBeacon 包)、开始广播、停止广播。
参数:
data/len:广播数据与长度
返回值:0 成功;负值错误码
完整代码
以下为 main.c 完整源码,与官方示例(examples/btble/adv)一致,默认折叠,点击展开:
📜 点击展开 adv/main.c 完整代码
/**
* @file main.c
* @brief BLE Beacon Demo - full lifecycle cycle test
*
* Runs 3 cycles of:
* btble_controller_init -> btble_adv_init -> btble_adv_set_parameter ->
* btble_adv_set_data -> btble_adv_start -> (wait) ->
* btble_adv_stop -> btble_adv_deinit -> btble_controller_deinit
*
* Requires CONFIG_BLE_BEACON_ONLY=y and BL616CL chip.
*/
#include <stdio.h>
#include <string.h>
#include <FreeRTOS.h>
#include "task.h"
#include "board.h"
#include "mm.h"
#include "btble_lib_api.h"
#include "bl616cl_glb.h"
#include "rfparam_adapter.h"
#include "bflb_mtd.h"
#include "easyflash.h"
/**************************************************************************************************
* Beacon Configuration
**************************************************************************************************/
#define BEACON_COMPANY_ID 0x004C /* Apple, Inc. */
#define BEACON_TYPE 0x0215 /* iBeacon type */
#define BEACON_MAJOR 0x0001
#define BEACON_MINOR 0x0002
#define BEACON_TX_POWER 0xC5 /* -59 dBm */
static const uint8_t beacon_uuid[16] = {
0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78,
0x89, 0x9A, 0xAB, 0xBC, 0xCD, 0xDE, 0xEF, 0xF0
};
/**************************************************************************************************
* Beacon Data Builder
**************************************************************************************************/
static int build_ibeacon_data(uint8_t *buf, size_t buf_size)
{
uint8_t *p = buf;
if (buf_size < 28) {
return -1;
}
/* Flags */
*p++ = 0x02;
*p++ = 0x01;
*p++ = 0x06;
/* Manufacturer Specific Data */
*p++ = 26;
*p++ = 0xFF;
*p++ = (BEACON_COMPANY_ID & 0xFF);
*p++ = ((BEACON_COMPANY_ID >> 8) & 0xFF);
*p++ = ((BEACON_TYPE >> 8) & 0xFF);
*p++ = (BEACON_TYPE & 0xFF);
*p++ = 0x15;
memcpy(p, beacon_uuid, 16);
p += 16;
*p++ = ((BEACON_MAJOR >> 8) & 0xFF);
*p++ = (BEACON_MAJOR & 0xFF);
*p++ = ((BEACON_MINOR >> 8) & 0xFF);
*p++ = (BEACON_MINOR & 0xFF);
*p++ = BEACON_TX_POWER;
return (int)(p - buf);
}
/**************************************************************************************************
* Heap Helper
**************************************************************************************************/
static size_t heap_free(void)
{
return kfree_size(0);
}
/**************************************************************************************************
* Beacon Cycle Test Task
**************************************************************************************************/
static void beacon_cycle_task(void *pvParameters)
{
const int NUM_CYCLES = 3;
const int ADV_DURATION_MS = 3000;
uint8_t adv_data[BTBLE_ADV_DATA_MAX_LEN];
int adv_data_len;
struct btble_adv_params params;
int err;
printf("\r\n========================================\r\n");
printf(" BLE Beacon Lifecycle Cycle Test\r\n");
printf(" Sequence: ctrl_init -> adv_init -> set_params\r\n");
printf(" -> set_data -> adv_start -> adv_stop\r\n");
printf(" -> adv_deinit -> ctrl_deinit\r\n");
printf(" Cycles: %d, ADV duration: %dms\r\n", NUM_CYCLES, ADV_DURATION_MS);
printf("========================================\r\n\r\n");
/* Build beacon data once (same for all cycles) */
adv_data_len = build_ibeacon_data(adv_data, sizeof(adv_data));
if (adv_data_len < 0) {
printf("[ERR] Failed to build beacon data\r\n");
vTaskDelete(NULL);
return;
}
memset(¶ms, 0, sizeof(params));
params.adv_interval_min = 0x00A0; /* 100 ms */
params.adv_interval_max = 0x00F0; /* 150 ms */
params.adv_type = BTBLE_ADV_TYPE_NON_CONNECTABLE_UNDIRECTED;
params.own_addr_type = BTBLE_ADDR_TYPE_PUBLIC;
params.adv_channel_map = BTBLE_ADV_CHANNEL_ALL;
params.adv_filter_policy = BTBLE_ADV_FILTER_ALLOW_ALL;
size_t heap_baseline = heap_free();
printf("[BASELINE] heap free: %u bytes\r\n\r\n", (unsigned)heap_baseline);
for (int cycle = 1; cycle <= NUM_CYCLES; cycle++) {
size_t h_prev, h_cur;
printf("--- Cycle %d/%d ---\r\n", cycle, NUM_CYCLES);
/* Step 1: controller init */
h_prev = heap_free();
btble_controller_init(configMAX_PRIORITIES - 1);
h_cur = heap_free();
printf(" [ctrl_init] heap used: %u free: %u\r\n",
(unsigned)(h_prev - h_cur), (unsigned)h_cur);
{
uint8_t *em_addr = NULL;
int em_size = 0;
btble_controller_remaining_mem(&em_addr, &em_size);
printf(" [EM] remaining: %d bytes\r\n", em_size);
}
/* Step 2: adv init (HCI_Reset + Set_Event_Mask + queue alloc) */
h_prev = h_cur;
err = btble_adv_init();
h_cur = heap_free();
printf(" [adv_init] err=%d heap used: %u free: %u\r\n",
err, (unsigned)(h_prev - h_cur), (unsigned)h_cur);
if (err != 0) {
printf(" [ERR] adv_init failed, skipping cycle\r\n");
btble_controller_deinit();
continue;
}
/* Step 3: set parameters */
err = btble_adv_set_parameter(¶ms);
printf(" [set_params] err=%d\r\n", err);
/* Step 4: set adv data */
err = btble_adv_set_data(adv_data, (uint8_t)adv_data_len);
printf(" [set_data] err=%d len=%d\r\n", err, adv_data_len);
/* Step 5: start advertising */
err = btble_adv_start();
printf(" [adv_start] err=%d -> %s\r\n",
err, err == 0 ? "OK" : "FAILED");
/* Advertise for ADV_DURATION_MS */
vTaskDelay(pdMS_TO_TICKS(ADV_DURATION_MS));
/* Step 6: stop advertising */
err = btble_adv_stop();
printf(" [adv_stop] err=%d\r\n", err);
/* Step 7: adv deinit (queue free + state reset) */
h_prev = h_cur;
btble_adv_deinit();
h_cur = heap_free();
printf(" [adv_deinit] heap recovered: %u free: %u\r\n",
(unsigned)(h_cur - h_prev), (unsigned)h_cur);
/* Step 8: controller deinit */
h_prev = h_cur;
btble_controller_deinit();
h_cur = heap_free();
printf(" [ctrl_deinit] heap recovered: %u free: %u\r\n",
(unsigned)(h_cur - h_prev), (unsigned)h_cur);
/* Check for leaks */
if (h_cur >= heap_baseline) {
printf(" [CHECK] heap fully recovered\r\n");
} else {
printf(" [CHECK] heap leak: %u bytes (baseline=%u, now=%u)\r\n",
(unsigned)(heap_baseline - h_cur),
(unsigned)heap_baseline, (unsigned)h_cur);
}
printf("\r\n");
vTaskDelay(pdMS_TO_TICKS(200));
}
printf("========================================\r\n");
printf(" Beacon lifecycle test DONE (%d cycles)\r\n", NUM_CYCLES);
printf(" Final heap free: %u bytes\r\n", (unsigned)heap_free());
printf("========================================\r\n");
vTaskDelete(NULL);
}
/**************************************************************************************************
* Main
**************************************************************************************************/
int main(void)
{
board_init();
bflb_mtd_init();
easyflash_init();
if (0 != rfparam_init(0, NULL, 0)) {
printf("PHY RF init failed!\r\n");
return 0;
}
xTaskCreate(beacon_cycle_task, "beacon_cycle", 1024, NULL,
configMAX_PRIORITIES - 2, NULL);
vTaskStartScheduler();
while (1) {}
}FAQ
用 bl616 编译报错 / 无法运行
例程依赖 CONFIG_BLE_BEACON_ONLY,该选项只支持 BL616CL(BLE-only 芯片)。Ai-M61/Ai-M62 的普通 BL618/BL616 不支持,需要用其他 BLE 例程(如 BLE 从机)。
手机扫描不到 iBeacon
确认广播周期内(每轮 3 秒)手机正在扫描;iBeacon 是不可连接广播,部分 App 默认只显示可连接设备,需打开“显示所有设备/beacon”选项(nRF Connect 的扫描结果里会显示厂商数据)。
遇到问题?
如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

