Skip to content

概念先知道

  • 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/peripheralble_start_adv()

操作步骤

1
确认芯片型号

⚠️ 本页例程(examples/btble/adv只支持 BL616CL(BLE-only 芯片),需要 CONFIG_BLE_BEACON_ONLY=y,不支持 Ai-M61(BL618)/ Ai-M62(BL616)的普通版本。这是 SDK README 明确标注的限制,请先在对应型号的板子上操作。

2
进入例程目录

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

cd examples/btble/adv
3
编译工程

bl616cl 编译:

make CHIP=bl616cl BOARD=bl616cldk
4
烧录固件

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

make flash CHIP=bl616cl COMX=/dev/ttyUSB0
5
运行验证

串口助手波特率 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_init / btble_adv_deinit()

初始化/反初始化广播模块(HCI Reset + 事件掩码 + 队列分配)。

参数:无

返回值0 成功;负值错误码

btble_adv_set_parameter(&params)

设置广播参数:间隔(100~150ms)、类型(BTBLE_ADV_TYPE_NON_CONNECTABLE_UNDIRECTED 不可连接)、信道(全部)等。

参数

  • paramsstruct 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 完整代码
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(&params, 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(&params);
        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

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