杰理 SDK 文档中心
首页
首页
  • 概述

    • SDK 概览与产品定位
    • 支持芯片平台与蓝牙认证
    • SDK 架构与目录分层
  • 快速开始

    • 环境搭建与编译工具链
    • 编译构建系统
    • 板级工程与配置
    • 烧录与固件升级工具
  • 应用工程

    • 应用选择与工程总览
    • SPP + BLE 数传应用框架
    • 透传与 AT 指令示例
    • BLE 广播/中心与定位示例
    • 2.4G 私有协议与 Dongle 示例
    • 云平台接入示例
    • HID 人机交互应用框架
    • HID 示例工程(键盘/鼠标/遥控器/手柄)
    • Bluetooth Mesh 应用框架
    • Mesh 模型与 Mesh DFU 固件升级
    • Mesh 音频编解码演示
  • 芯片平台与硬件抽象

    • 芯片平台总览与差异
    • 音频编解码与时钟管理
    • 外设驱动接口(ADC/IIC/SPI/PWM/LED/充电)
    • 芯片配置工具与下载支持
  • 蓝牙协议栈

    • 蓝牙控制器层(btctrler)
    • 蓝牙协议栈与 Profile(btstack)
    • 蓝牙模块选择与配置
  • 媒体与音频框架

    • 音频流框架
    • 音频编解码与 A2DP 媒体
    • 音频效果处理(EQ/频谱/变调/环绕/超低音)
    • 本地 TWS 与音频同步
  • 系统服务与运行时

    • 实时操作系统与任务调度
    • 消息事件机制
    • 电源管理与低功耗
    • 存储与配置系统
    • 设备驱动框架(USB/RTC)
  • 应用公共组件

    • 音频应用组件
    • 设备外设抽象(按键/触摸/传感器/存储)
    • 蓝牙公共模块与消息联动
    • 调试与配置组件
    • 杰理关键词唤醒(jl_kws)
  • 第三方协议与云平台接入

    • 杰理 RCSP 私有协议
    • 低功耗蓝牙 Mesh 方案(llsync_mesh)
    • Sig Mesh 方案
    • 涂鸦协议接入
    • 腾讯连连接入
    • 华为 HiLink 接入
  • 固件升级与维护

    • OTA 升级机制
    • 升级补丁与版本维护
    • 升级工具链(BLE OTA / USB Dongle OTA)
  • 文档与开发资源

    • 数据手册与架构文档
    • 协议与云平台开发文档
    • 常见问题与技术支持

BLE 广播/中心与定位示例

本文档介绍 AC63_BT_SDK 中 spp_and_le 工程内的 Find My(苹果查找网络)示例应用:设备作为 BLE 外设(广播者/Peripheral)实现 Apple FMNA 配件协议,可被 iOS 设备(中心/Central)发现、配对并执行播放声音、SN 查询、固件升级等定位与查找操作。

Purpose and Scope

本页面向 apps/spp_and_le/examples/findmy/ 目录下的完整示例应用,覆盖:

  • 应用的注册方式与启动流程(app_main.c → app_fmy.c 的状态机与 intent 分发);
  • BLE 协议栈的初始化、广播(Advertising/Broadcaster)与连接(Central/Peripheral)角色配置;
  • Find My 配件所必需的 GATT 服务与 FMNA 协议实现(ble_fmy.c、ble_fmy_fmna.c);
  • 配对安全(SM)、低功耗与软关机、VM 配置存储、调试与日志开关。

本文档不展开的内容(请参见各自文档页):经典蓝牙(EDR/AVCTP/RCSP)相关能力、通用 LE 透传(le_trans)示例、OTA 升级的底层协议细节、以及 third_party/fmna 库的内部算法。这些属于兄弟主题,此处仅在涉及调用边界时提及。

Overview

什么是"定位示例"

findmy 示例在 SDK 中对应 CONFIG_APP_FINDMY 配置,注册的应用名为 findmy、动作值为 ACTION_FINDMY。它把一个杰理 AC63 系列蓝牙 SoC 模拟为苹果 Find My 网络中的"配件(Accessory)":

  • 设备开机后进入 BLE 广播(Broadcast),广播内容符合 FMNA 规范,可被 iPhone/iPad 的"查找"App 发现;
  • iOS 设备作为 BLE 中心(Central)发起连接,通过 FMNA 控制点特征完成配对、绑定(Owner)与各类操作;
  • 设备侧通过 ATT 读写回调响应控制点命令:播放声音(FMY_CAPABILITY_SUPPORTS_PLAY_SOUND)、SN 查询(FMY_CAPABILITY_SUPPORTS_SN_LOOKUP_BY_BLE)、固件升级(FMY_OTA_SUPPORT_CONFIG 时使能 FMY_CAPABILITY_SUPPORTS_FW_UPDATE_SERVICE)、运动检测(TCFG_GSENSOR_ENABLE 时使能 FMY_CAPABILITY_SUPPORTS_MOTION_DETECTOR_UT)。

广播/中心两种角色在本示例中的体现

角色本示例中的实现说明
广播者 Broadcaster(Peripheral)fmy_set_adv_enable() / fmy_set_adv_mode(),广播周期 ADV_INTERVAL_MIN设备默认行为:持续广播 Find My 可发现包
中心 Central(Scanner)协议栈 btstack 的 LE 扫描/连接能力,config_btctler_le_rx_nums 按 CONFIG_APP_FINDMY 放大 RX 缓冲本示例以被动响应为主;中心侧能力由协议栈通用接口提供

关键源文件

文件职责
apps/spp_and_le/examples/findmy/app_fmy.c应用入口:启动、状态机、HCI 事件转发、软关机、电源事件
apps/spp_and_le/examples/findmy/ble_fmy.cBLE 主实现:ATT 回调、FMNA 属性表、广播/连接控制、GATT 控制块
apps/spp_and_le/examples/findmy/ble_fmy.h全局结构 fmy_glb_t、VM 结构 fmy_vm_t、Profile 模式枚举与导出 API
apps/spp_and_le/examples/findmy/ble_fmy_fmna.c/.hFMNA 配件框架集成(第三方 fmna_api.h 的桥接)
apps/spp_and_le/examples/findmy/ble_fmy_ota.c/.h固件升级服务(94110001-6D9B-4225-A4F1-6A4A7F01B0DE)
apps/spp_and_le/examples/findmy/ble_fmy_sensor_uart.c/.h调试用 SENSOR UART 输出
apps/spp_and_le/examples/findmy/ble_fmy_cfg.h示例级配置项汇总
apps/spp_and_le/examples/findmy/ble_fmy_profile.hGATT Profile 特征句柄定义
apps/spp_and_le/app_main.c应用任务表注册(task_info_table、ACTION_FINDMY)

Architecture

flowchart TD
    subgraph sg_App["应用层 (app_fmy.c)"]
        AppStart["fmy_app_start()"]
        StateMachine["fmy_state_machine()"]
        HciHandler["fmy_bt_hci_event_handler()"]
        PowerOff["fmy_set_soft_poweroff()"]
    end

    subgraph sg_Ble["BLE 协议层 (ble_fmy.c)"]
        GattCb["fmy_att_read_callback / fmy_att_write_callback"]
        EventHandler["fmy_event_packet_handler()"]
        Adv["fmy_set_adv_enable() / fmy_set_adv_mode()"]
        SmCfg["fmy_sm_init_config (SM 安全)"]
    end

    subgraph sg_Fmna["FMNA 配件框架"]
        FmnaApi["third_party/fmna/fmna_api.h"]
        FmnaAtt["fmna_att_handle_table (4F8600xx UUID)"]
        Ota["ble_fmy_ota.c (固件升级)"]
    end

    subgraph sg_Ext["外设 / 存储 / 调试"]
        Gsensor["gSensor/fmy/gsensor_api.h"]
        Uart["ble_fmy_sensor_uart.c"]
        Vm["VM 存储 (fmy_vm_info)"]
    end

    AppStart --> StateMachine
    AppStart --> HciHandler
    StateMachine --> AppStart
    HciHandler --> PowerOff
    GattCb --> FmnaAtt
    FmnaAtt --> FmnaApi
    EventHandler --> Adv
    EventHandler --> GattCb
    SmCfg --> GattCb
    GattCb --> Ota
    GattCb --> Gsensor
    GattCb --> Uart
    GattCb --> Vm

架构说明:

  • 应用层:fmy_app_start() 是唯一入口,负责系统时钟、看门狗、BT 协议栈与外围(UART、按键)初始化;fmy_state_machine() 是应用生命周期状态机,在 APP_STA_START 收到 ACTION_FINDMY 后启动;HCI 事件统一由 fmy_bt_hci_event_handler() 接收并转发给公共处理函数。
  • BLE 协议层:ble_fmy.c 是核心,向协议栈注册三个回调(ATT 读、ATT 写、事件包),并持有 GATT 控制块(MTU、发送缓冲、服务配置)与 SM 安全配置。广播使能/模式、静态 MAC 读写、主动断开等操作均在此层。
  • FMNA 框架:fmna_att_handle_table 把 FMNA 的控制点特征句柄(4F860001~4F860005 与 OTA 的 94110001)映射到第三方 FMNA 库,实现配对控制、Owner/Non-Owner 控制、调试控制与固件升级。
  • 外围/存储:G-Sensor 提供运动检测能力位,SENSOR UART 提供调试输出,VM 保存配对/重置标志等掉电不丢失数据。

应用注册与启动流程

应用任务注册

示例应用通过 task_info_table 与 REGISTER_APPLICATION 宏挂接到系统。app_main.c 中在 CONFIG_APP_FINDMY 下注册 app_core 任务:

#if CONFIG_APP_FINDMY
    {"app_core",            1,     0,   640 * 2,   128  },

Source: app_main.c

intent 携带 ACTION_FINDMY,应用名为 findmy:

#elif CONFIG_APP_FINDMY
    it.name = "findmy";
    it.action = ACTION_FINDMY;

Source: app_main.c

app_fmy.c 末尾通过 REGISTER_APPLICATION(app_fmy_action) 将名称与动作绑定:

REGISTER_APPLICATION(app_fmy_action) = {
    .name 	= "findmy",
    .action	= ACTION_FINDMY,

Source: app_fmy.c

应用启动函数 fmy_app_start()

启动函数承担三件事:硬件加速(看门狗与主频)、BT 协议栈初始化(EDR/BLE 双模)、外围使能(按键、调试 UART)。设计意图:Find My 的 Apple 算法(AL)对 CPU 频率敏感,因此把系统主频固定提到 96 MHz,并把看门狗放宽到 32 秒以容纳较长的配对/加密流程。

static void fmy_app_start()
{
    log_info("=======================================");
    log_info("-----------findmy demo-------------");
    log_info("=======================================");
    log_info("app_file: %s", __FILE__);

    wdt_init(WDT_32S);//need set long time
    clk_set("sys", 96 * 1000000L);//need run fast for AL
    log_info("vm start address: 0x%8x", CONFIG_VM_ADDR);

    if (__fydata->enter_btstack_num == 0) {
        __fydata->enter_btstack_num = 1;

//有蓝牙
#if (TCFG_USER_EDR_ENABLE || TCFG_USER_BLE_ENABLE)
        uint32_t sys_clk =  clk_get("sys");
        bt_pll_para(TCFG_CLOCK_OSC_HZ, sys_clk, 0, 0);

#if TCFG_USER_EDR_ENABLE
        btstack_edr_start_before_init(NULL, 0);
#if DOUBLE_BT_SAME_MAC
        //手机自带搜索界面,默认搜索到EDR
        __change_hci_class_type(BD_CLASS_TRANSFER_HEALTH);//
#endif
#endif

#if TCFG_USER_BLE_ENABLE
        btstack_ble_start_before_init(&fmy_data_ble_config, 0);
#endif

        btstack_init();

#else
//no bt,to for test
        sys_timer_add(NULL, fmy_timer_handle_test, 1000);
#endif
    }
    /* 按键消息使能 */
    sys_key_event_enable();

    /* SENSOR UART使能 */
#if FMY_DEBUG_SENSOR_TO_UART_ENBALE
    sensor_uart_init();
#endif

    fmy_state_idle_set_active(false);
}

Source: app_fmy.c

要点解读:

  • enter_btstack_num 防止重复进入应用时重复初始化协议栈(幂等保护)。
  • fmy_data_ble_config 配置 same_address = 0、appearance = 0,即 BLE 使用独立地址、不声明外观类型,避免与 EDR 的发现行为冲突:
    static const ble_init_cfg_t fmy_data_ble_config = {
        .same_address = 0,
        .appearance = 0,
    };
    

    Source: app_fmy.c

  • DOUBLE_BT_SAME_MAC 时修改 EDR 的 class of device 为 BD_CLASS_TRANSFER_HEALTH,让手机搜索界面默认命中 EDR,避免与 Find My 的 BLE 广播互相干扰。

应用状态机 fmy_state_machine()

应用生命周期由系统按 APP_STA_* 状态驱动,示例只在 APP_STA_START 分支按 intent 动作分发到 fmy_app_start(),其余状态保持空实现(可扩展):

static int fmy_state_machine(struct application *app, enum app_state state, struct intent *it)
{
    switch (state) {
    case APP_STA_CREATE:
        break;
    case APP_STA_START:
        if (!it) {
            break;
        }
        switch (it->action) {
        case ACTION_FINDMY:
            fmy_app_start();
            break;
        }
        break;
    case APP_STA_PAUSE:
        break;
    case APP_STA_RESUME:
        break;
    case APP_STA_STOP:
        break;
    case APP_STA_DESTROY:
        log_info("APP_STA_DESTROY\n");
        break;
    }

    return 0;
}

Source: app_fmy.c

HCI 事件转发

所有蓝牙连接/断开事件统一进入 fmy_bt_hci_event_handler(),再按 EDR/BLE 使能情况转发给 bt_comm_* 公共处理,保证上层状态一致:

static int fmy_bt_hci_event_handler(struct bt_event *bt)
{
    //对应原来的蓝牙连接上断开处理函数  ,bt->value=reason
    log_info("----%s reason %x %x", __FUNCTION__, bt->event, bt->value);

#if TCFG_USER_EDR_ENABLE
    bt_comm_edr_hci_event_handler(bt);
#endif

#if TCFG_USER_BLE_ENABLE
    bt_comm_ble_hci_event_handler(bt);
#endif
    return 0;
}

Source: app_fmy.c

BLE 协议层与 FMNA 实现(ble_fmy.c)

ATT/GATT 运行参数

ble_fmy.c 顶部用宏固定 ATT 参数,这些参数直接影响吞吐与内存占用:

宏值设计意图
PASSKEY_ENABLE0默认免输入;置 1 后 SM 改为 IO_CAPABILITY_DISPLAY_ONLY 并启用 passkey 加密
ATT_MTU_REQUEST_ENALBE0设备端不主动发起 MTU 交换;置 1 可主动发起
ATT_LOCAL_MTU_SIZE247本端 ATT 发送包长,23 ≤ 值 ≥ MTU,支持大包
ATT_PACKET_NUMS_MAX4ATT 缓存支持的包个数(约需 1 KB)
ATT_SEND_CBUF_SIZE4 * (ATT_PACKET_HEAD_SIZE + 247)发送环形缓冲总大小
ADV_INTERVAL_MIN160 * 5(单位 0.625 ms,约 500 ms)广播周期,兼顾低功耗与可发现性
FMY_MAX_CONNECTIONS2Find My 允许的最大连接数,不可修改
//广播周期 (unit:0.625ms)
#define ADV_INTERVAL_MIN          (160 * 5)//
// FMY最大连接数,不可修改
#define FMY_MAX_CONNECTIONS       2

Source: ble_fmy.c

配件设备信息与能力位

设备名称、制造商、型号、配件类别、发射功率与电池类型都是 FMNA 规范要求暴露的静态信息。发射功率被固定声明为 +4 dBm(规范要求配件 ≥ +4 dBm):

static const char the_original_Name[] = "Find My Accessory";//"Accessory- Find My.";
static const char the_suffix_Name[] = "Acce- Find My.";
static const char FMY_ManufacturerName[64] = "Zhuhai Jieli Technology Co.,Ltd.";
static const char FMY_ModelName[64] = "JLtag";
static const uint8_t FMY_AccessoryCategory[8] = {FMY_CATEGORY_Finder, 0, 0, 0, 0, 0, 0, 0};
static const uint8_t read_tx_power_level = 4;//The Bluetooth LE transmit power level of the accessory shall be fixed at ≥ +4dBm

static const uint8_t fmy_battery_type = FMNA_BAT_NON_RECHARGEABLE;

Source: ble_fmy.c

能力位(Capabilities)通过位或组合,条件编译决定是否暴露某项能力,FMY_CHECK_CAPABILITIES 宏供上层查询:

static const uint8_t FMY_AccessoryCapabilities[4] = {
    (FMY_CAPABILITY_SUPPORTS_PLAY_SOUND

#if FMY_OTA_SUPPORT_CONFIG
    | FMY_CAPABILITY_SUPPORTS_FW_UPDATE_SERVICE
#endif

#if TCFG_GSENSOR_ENABLE
    | FMY_CAPABILITY_SUPPORTS_MOTION_DETECTOR_UT
#endif

    | FMY_CAPABILITY_SUPPORTS_SN_LOOKUP_BY_BLE),
    0x00, 0x00, 0x00
};

#define FMY_CHECK_CAPABILITIES(bit)   ((FMY_AccessoryCapabilities[0] & bit) != 0)

Source: ble_fmy.c

FMNA 属性句柄映射

FMNA 控制点特征使用厂商专用 UUID 4F860001-943B-49EF-BED4-2F730304427A(配对控制点、Owner 配置控制点、Non-Owner 控制点、Owner 信息点、调试控制点),固件升级使用 94110001-6D9B-4225-A4F1-6A4A7F01B0DE。句柄来自 ble_fmy_profile.h 生成的 Profile 表:

static const fmna_att_handle_t fmna_att_handle_table = {
    .pairing_control_point_handle = ATT_CHARACTERISTIC_4F860001_943B_49EF_BED4_2F730304427A_01_VALUE_HANDLE,
    .owner_cfg_control_point_handle = ATT_CHARACTERISTIC_4F860002_943B_49EF_BED4_2F730304427A_01_VALUE_HANDLE,
    .owner_info_porint_handle = ATT_CHARACTERISTIC_4F860004_943B_49EF_BED4_2F730304427A_01_VALUE_HANDLE,
    .non_owner_control_point_handle = ATT_CHARACTERISTIC_4F860003_943B_49EF_BED4_2F730304427A_01_VALUE_HANDLE,
    .debug_control_point_handle = ATT_CHARACTERISTIC_4F860005_943B_49EF_BED4_2F730304427A_01_VALUE_HANDLE,
    .firmware_update_handle = ATT_CHARACTERISTIC_94110001_6D9B_4225_A4F1_6A4A7F01B0DE_01_VALUE_HANDLE,
};

Source: ble_fmy.c

配对安全(SM)配置

默认免输入免输出、要求绑定(BONDING),密钥长度 7~16 字节;启用 PASSKEY_ENABLE 后改为 Display Only(设备显示 passkey):

static const sm_cfg_t fmy_sm_init_config = {
    .slave_security_auto_req = 0,
    .slave_set_wait_security = 0,

#if PASSKEY_ENABLE
    .io_capabilities = IO_CAPABILITY_DISPLAY_ONLY,
#else
    .io_capabilities = IO_CAPABILITY_NO_INPUT_NO_OUTPUT,
#endif

    .authentication_req_flags = SM_AUTHREQ_BONDING,// | SM_AUTHREQ_MITM_PROTECTION,
    .min_key_size = 7,
    .max_key_size = 16,
    .sm_cb_packet_handler = NULL,
};

Source: ble_fmy.c

GATT 服务控制块

把 ATT 读/写回调与事件包回调绑定到协议栈,并指定 MTU 与发送缓冲:

static const gatt_server_cfg_t fmy_server_init_cfg = {
    .att_read_cb = &fmy_att_read_callback,
    .att_write_cb = &fmy_att_write_callback,
    .event_packet_handler = &fmy_event_packet_handler,
};

static gatt_ctrl_t fmy_gatt_control_block = {
    //public
    .mtu_size = ATT_LOCAL_MTU_SIZE,
    .cbuffer_size = ATT_SEND_CBUF_SIZE,
    .multi_dev_flag	= 0,

#if CONFIG_BT_GATT_SERVER_NUM
    .server_config = &fmy_server_init_cfg,
#else
    .server_config = NULL,
#endif

Source: ble_fmy.c

全局状态与 Profile 模式

ble_fmy.h 定义全局状态结构 fmy_glb_t 与 Profile 模式枚举。Profile 模式描述配件与 iOS 设备的关系状态,是 Find My 行为(广播内容、可执行命令)的决策依据:

enum {
    PROFILE_MODE_UNPAIR = 0,
    PROFILE_MODE_OWNER,
    PROFILE_MODE_NON_OWNER,
    PROFILE_MODE_NEARBY,
    PROFILE_MODE_SEPARATED,
};

typedef struct {
    uint16_t cur_con_handle;//记录最新的连接handle
    uint16_t sound_ctrl_timer_id;//蜂鸣器定时器
    uint16_t pairing_mode_timer_id;//配对模式定时器
    uint8_t  is_app_fmy_active;//控制是否进入低功耗,0-yes,1-no
    uint8_t  enter_btstack_num;//是否重复进入应用
    uint8_t  battery_level;// init BAT_STATE_FULL
    uint8_t  adv_fmna_state;//fmna adv 状态
    uint8_t  profile_mode; //profile 配置模式
    uint8_t  sound_onoff;  //蜂鸣器状态
    uint8_t  pairing_mode_enable; //配对模式使能
    uint8_t  testbox_mode_enable; //测试频偏校准模式
} fmy_glb_t;

Source: ble_fmy.h

核心流程

启动与连接时序

sequenceDiagram
    participant AM as "app_main (task_info_table)"
    participant SM as "fmy_state_machine()"
    participant ST as "fmy_app_start()"
    participant BT as "btstack 协议栈"
    participant GA as "GATT 服务 (ble_fmy.c)"
    participant FM as "FMNA 框架 (fmna_api)"

    AM->>SM: intent { action = ACTION_FINDMY }
    SM->>ST: fmy_app_start()
    ST->>BT: wdt_init + clk_set(sys, 96MHz)
    ST->>BT: bt_pll_para / btstack_ble_start_before_init
    ST->>BT: btstack_init()
    BT-->>GA: 注册 att_read/write/event 回调
    Note over GA,BT: 进入广播 (ADV_INTERVAL_MIN ≈ 500ms)
    BT->>GA: 连接建立事件 (HCI_EVENT)
    GA->>FM: 读取控制点 / 写入控制点 (4F8600xx)
    FM-->>GA: 命令处理结果 (声音/SN/OTA)
    GA-->>BT: ATT 响应

启动路径从 app_main.c 的任务表出发:系统创建 app_core 任务后按 intent 分发到 ACTION_FINDMY,fmy_state_machine() 在 APP_STA_START 调用 fmy_app_start();后者完成主频/看门狗/协议栈初始化,随后 GATT 服务注册回调并开始广播。iOS 中心设备连接后,所有 FMNA 操作都经由 ATT 读写回调进入 fmna_api 处理,结果再以 ATT 响应返回。

Profile 模式状态机

Profile 模式决定配件处于"未配对 / 已绑定 Owner / 其他设备 / 附近 / 分离"中的哪种状态,各模式下的广播内容与允许命令不同:

stateDiagram-v2
    [*] --> UNPAIR
    UNPAIR --> OWNER: 配对绑定 (owner 控制点)
    UNPAIR --> NON_OWNER: 其他 iOS 设备连接
    OWNER --> SEPARATED: 断开/超范围
    NON_OWNER --> NEARBY: 靠近
    NEARBY --> SEPARATED: 远离
    SEPARATED --> OWNER: Owner 重新连接
    OWNER --> UNPAIR: 解绑/恢复出厂
    NON_OWNER --> OWNER: 成为新 Owner

低功耗与软关机

fmy_state_idle_set_active() 控制 is_app_fmy_active 标志,决定系统是否允许进入低功耗(0 = 允许);fmy_set_soft_poweroff() 是软关机路径——必须先主动断开蓝牙链路,否则要等链路超时断开,会显著拖慢关机:

static void fmy_set_soft_poweroff(void)
{
    log_info("set_soft_poweroff\n");
    fmy_state_idle_set_active(true);
    //必须先主动断开蓝牙链路,否则要等链路超时断开

#if TCFG_USER_BLE_ENABLE
    btstack_ble_exit(0);
#endif

#if TCFG_USER_EDR_ENABLE
    btstack_edr_exit(0);
#endif

#if (TCFG_USER_EDR_ENABLE || TCFG_USER_BLE_ENABLE)
    //延时300ms,确保BT退出链路断开
    sys_timeout_add(NULL, power_set_soft_poweroff, WAIT_DISCONN_TIME_MS);
#else
    power_set_soft_poweroff();
#endif
}

Source: app_fmy.c

电源事件通过系统事件机制上报(fmy_power_event_to_user),把内部电源事件包装为 SYS_DEVICE_EVENT / DEVICE_EVENT_FROM_POWER,供上层 UI/策略统一处理:

void fmy_power_event_to_user(uint8_t event)
{
    struct sys_event e;
    e.type = SYS_DEVICE_EVENT;
    e.arg  = (void *)DEVICE_EVENT_FROM_POWER;
    e.u.dev.event = event;
    e.u.dev.value = 0;
    sys_event_notify(&e);
}

Source: app_fmy.c

配置持久化(VM)

配对/重置标志存放在 fmy_vm_t,通过 fmy_vm_deal() 读写 VM:

typedef struct {
    uint8_t  head_tag;
    uint8_t  reset_config;//flag
} fmy_vm_t;

extern fmy_vm_t fmy_vm_info;
#define __fy_vm  (&fmy_vm_info)

Source: ble_fmy.h

head_tag 用作校验头:读取时先校验标记合法性,非法则按默认值重置并回写,避免 VM 区被破坏时误用脏数据。

使用示例

示例 1:广播周期与连接数约束

Find My 规范对最大连接数有硬性约束,示例在 ble_fmy.c 中直接以宏固化,防止上层误改:

// 广播周期 (unit:0.625ms)
#define ADV_INTERVAL_MIN          (160 * 5)//
// FMY最大连接数,不可修改
#define FMY_MAX_CONNECTIONS       2

Source: ble_fmy.c

示例 2:能力位条件编译

能力位通过编译开关组合,产品定制时只需调整 FMY_OTA_SUPPORT_CONFIG / TCFG_GSENSOR_ENABLE 即可决定是否对外暴露"固件升级/运动检测"能力:

static const uint8_t FMY_AccessoryCapabilities[4] = {
    (FMY_CAPABILITY_SUPPORTS_PLAY_SOUND

#if FMY_OTA_SUPPORT_CONFIG
    | FMY_CAPABILITY_SUPPORTS_FW_UPDATE_SERVICE
#endif

#if TCFG_GSENSOR_ENABLE
    | FMY_CAPABILITY_SUPPORTS_MOTION_DETECTOR_UT
#endif

    | FMY_CAPABILITY_SUPPORTS_SN_LOOKUP_BY_BLE),
    0x00, 0x00, 0x00
};

Source: ble_fmy.c

示例 3:全局状态访问宏

所有模块通过 __fydata 宏访问 fmy_glb_t,fmy_state_idle_set_active() 就是其典型用法(低功耗开关):

void fmy_state_idle_set_active(uint8_t active)
{
    __fydata->is_app_fmy_active = active;
}

Source: app_fmy.c

示例 4:协议栈 RX 缓冲放大

lib_btctler_config.c 在 CONFIG_APP_FINDMY 下放大 LE RX 通道数(按连接数与扩展广播配置 × 3),保证多连接 + 扩展广播场景下收包不丢:

#if CONFIG_APP_FINDMY
const int config_btctler_le_rx_nums = ((CONFIG_BT_GATT_CONNECTION_NUM + EXT_ADV_CFG_HW) * 3);

Source: lib_btctler_config.c

示例 5:日志分级开关

log_config.c 中 FINDMY 标签按 v/i/d/w/e 五级单独开关,便于量产时裁剪日志:

const char log_tag_const_v_FINDMY AT(.LOG_TAG_CONST) = 0;
const char log_tag_const_i_FINDMY AT(.LOG_TAG_CONST) = 1;
const char log_tag_const_d_FINDMY AT(.LOG_TAG_CONST) = 1;
const char log_tag_const_w_FINDMY AT(.LOG_TAG_CONST) = 1;
const char log_tag_const_e_FINDMY AT(.LOG_TAG_CONST) = 1;

Source: log_config.c

配置选项

应用级开关

选项类型默认/典型值说明
CONFIG_APP_FINDMY编译宏0/1是否编译 Find My 示例应用(同时影响 task_info_table、RX 通道数与日志标签)
TCFG_USER_BLE_ENABLE编译宏1使能 BLE 协议栈(广播/连接的前提)
TCFG_USER_EDR_ENABLE编译宏0/1使能经典蓝牙(HCI 事件会转发给 bt_comm_edr_hci_event_handler)
DOUBLE_BT_SAME_MAC编译宏0/1EDR/BLE 同 MAC 时,把 EDR class 改为 BD_CLASS_TRANSFER_HEALTH 避免与 BLE 广播冲突
CONFIG_BT_GATT_CONNECTION_NUM编译宏按工程GATT 连接数,参与 RX 通道数计算
EXT_ADV_CFG_HW编译宏按工程扩展广播硬件配置数

示例内部配置(ble_fmy.c / ble_fmy_cfg.h)

选项类型默认值说明
PASSKEY_ENABLE宏01 时启用 passkey 加密,SM IO 能力改为 Display Only
ATT_MTU_REQUEST_ENALBE宏01 时设备端主动发起 MTU 交换
ATT_LOCAL_MTU_SIZE宏247本端 ATT 发送包长
ATT_PACKET_NUMS_MAX宏4ATT 缓存包个数(约 1 KB 内存)
ATT_SEND_CBUF_SIZE宏4*(ATT_PACKET_HEAD_SIZE+247)发送环形缓冲大小
ADV_INTERVAL_MIN宏160*5(≈500 ms)广播周期,单位 0.625 ms
FMY_MAX_CONNECTIONS宏2Find My 最大连接数(不可修改)
FMY_OTA_SUPPORT_CONFIG宏0/1使能固件升级能力位与 OTA 服务
TCFG_GSENSOR_ENABLE宏0/1使能运动检测能力位(G-Sensor)
FMY_DEBUG_SENSOR_TO_UART_ENBALE宏0/1使能 SENSOR UART 调试输出
WAIT_DISCONN_TIME_MS宏300软关机前等待蓝牙断开的时间

运行时行为参数(ble_fmy.c 常量)

常量值说明
read_tx_power_level4对外声明的发射功率(规范要求 ≥ +4 dBm)
fmy_battery_typeFMNA_BAT_NON_RECHARGEABLE电池类型(不可充电)
FMY_AccessoryCategoryFMY_CATEGORY_Finder配件类别(查找器)

API 参考

void fmy_state_idle_set_active(uint8_t active)

设置应用活跃标志 is_app_fmy_active,控制系统是否可进入低功耗。active=1 表示应用处于关键流程(如软关机),禁止低功耗。

参数:

  • active(uint8_t):1 禁止低功耗,0 允许低功耗。

Source: app_fmy.c

void fmy_power_event_to_user(uint8_t event)

把内部电源事件封装为 SYS_DEVICE_EVENT + DEVICE_EVENT_FROM_POWER 系统事件并通知上层。

参数:

  • event(uint8_t):电源事件码。

Source: app_fmy.c

bool fmy_vm_deal(fmy_vm_t *info, uint8_t rw_flag)

读写 Find My 的 VM 配置(配对/重置标志)。读取时校验 head_tag,非法则重置为默认值。

参数:

  • info(fmy_vm_t *):VM 数据结构指针。
  • rw_flag(uint8_t):读/写方向标志。

返回: 操作成功返回 true。

Source: ble_fmy.h

bool fmy_check_capabilities_is_enalbe(uint8_t cap)

查询能力位(FMY_CAPABILITY_*)是否使能。

参数:

  • cap(uint8_t):能力位。

返回: 使能返回 true。

Source: ble_fmy.h

bool att_set_handle_enable(hci_con_handle_t con_handle, uint16_t start_handle, uint16_t end_handle, uint8_t enable)

按句柄区间使能/禁止某连接上的 ATT 特征可访问性,用于按 Profile 模式(Owner/Non-Owner)动态开放或收紧特征权限。

参数:

  • con_handle(hci_con_handle_t):连接句柄。
  • start_handle / end_handle(uint16_t):ATT 句柄区间。
  • enable(uint8_t):1 使能,0 禁止。

Source: ble_fmy.h

内部回调(ble_fmy.c,注册到 GATT 服务)

回调签名职责
fmy_att_read_callbackuint16_t (hci_con_handle_t, uint16_t att_handle, uint16_t offset, uint8_t *buffer, uint16_t buffer_size)响应特征读取(设备信息、SN 等)
fmy_att_write_callbackint (hci_con_handle_t, uint16_t att_handle, uint16_t mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size)处理控制点写入(配对/声音/OTA 等)
fmy_event_packet_handlerint (int event, uint8_t *packet, uint16_t size, uint8_t *ext_param)处理协议栈事件(连接、MTU、加密等)
fmy_set_adv_enableint (uint8_t enable)广播使能/停止
fmy_set_adv_modeint (uint8_t mode)切换广播模式(可发现性)
fmy_get_static_mac / fmy_set_static_macint (uint8_t *mac)读写静态 MAC(FMNA 要求固定地址)
fmy_disconnectint (uint16_t conn_handle, uint8_t reason)主动断开指定连接

回调声明见 ble_fmy.c,注册见 ble_fmy.c。

失败模式、边界情况与并发

重复进入应用(幂等保护)

fmy_app_start() 以 __fydata->enter_btstack_num 标志保证协议栈只初始化一次;若应用被反复 START 而标志未复位,btstack_init() 不会被二次调用,避免协议栈状态错乱。

软关机前必须断链

fmy_set_soft_poweroff() 先调用 btstack_ble_exit() / btstack_edr_exit() 主动断开链路,再延时 WAIT_DISCONN_TIME_MS(300 ms)执行 power_set_soft_poweroff()。若省略断链步骤,关机将被链路超时(可达数秒)阻塞;is_app_fmy_active=true 同时阻止低功耗进入,确保关机流程不被中断。

连接数上限与多连接

FMY_MAX_CONNECTIONS = 2 是 FMNA 规范的硬约束。多连接时通过 att_set_handle_enable() 按连接句柄管理特征权限,防止非 Owner 设备访问 Owner 专属控制点。config_btctler_le_rx_nums 按 (CONN_NUM + EXT_ADV_CFG_HW) * 3 放大 RX 通道,正是为多连接 + 扩展广播并发收包预留余量;若裁剪该值,高负载下可能丢包导致 ATT 超时。

未使能蓝牙的回退路径

当 TCFG_USER_EDR_ENABLE 与 TCFG_USER_BLE_ENABLE 均关闭时,fmy_app_start() 不再初始化协议栈,而是挂一个 1 秒周期的 fmy_timer_handle_test 定时器(仅打印 not_bt),用于无蓝牙环境下的内存/功耗测试。

广播周期与可发现性的权衡

ADV_INTERVAL_MIN = 160 * 5(约 500 ms)是功耗与可发现延迟的折中:周期过长则 iOS 端发现慢,过短则耗电。产品若需要更快的"查找"响应,可缩短该值,但需评估平均电流。

VM 数据损坏

fmy_vm_t.head_tag 作为校验头:读取时校验失败即视为脏数据并按默认值重置回写,避免配对/重置标志错乱导致配件行为异常(例如误判已绑定)。

并发访问

ATT 回调(读/写/事件)运行在协议栈上下文,与应用层任务并发;示例通过单一全局结构 fmy_glb_t(__fydata)集中共享状态,且关键流程(如软关机)用 is_app_fmy_active 标志与电源管理互斥,避免低功耗打断 ATT 事务。定制时新增共享字段需注意原子性。

性能与运维考虑

  • 主频:fmy_app_start() 将 sys 时钟提到 96 MHz 并调用 bt_pll_para() 同步 PLL,保证 FMNA 的 Apple 算法(AL)在时限内完成;降低主频可能导致配对/加密超时。
  • 看门狗:wdt_init(WDT_32S) 使用 32 秒长窗口,因为配对绑定与 OTA 存在长阻塞路径;量产固件应评估是否回调。
  • 内存:ATT_SEND_CBUF_SIZE = 4 * (ATT_PACKET_HEAD_SIZE + 247) 约 1 KB 专用于 ATT 发送缓冲;ATT_PACKET_NUMS_MAX=4 决定突发写命令的排队深度,增大可提升吞吐但增加内存。
  • MTU:ATT_LOCAL_MTU_SIZE = 247 支持大包传输(OTA 数据特征),前提是对端也协商到相同 MTU;ATT_MTU_REQUEST_ENALBE=1 可让设备端主动发起交换。
  • 日志裁剪:log_config.c 中 FINDMY 标签的 v/i/d/w/e 五级开关可独立关闭,量产时关闭 v 级可显著减少串口输出与内存占用。

扩展点

  • 能力位:修改 FMY_AccessoryCapabilities[4](配合 FMY_OTA_SUPPORT_CONFIG、TCFG_GSENSOR_ENABLE)即可对外增删能力,无需改动协议栈。
  • 新增特征/服务:在 ble_fmy_profile.h 增加特征句柄定义,并在 fmy_att_read_callback / fmy_att_write_callback 中按 att_handle 分发即可扩展私有服务。
  • FMNA 桥接:ble_fmy_fmna.c 是 third_party/fmna/fmna_api.h 的桥接层;替换或升级 FMNA 库只需更新该文件与 fmna_att_handle_table 的句柄映射。
  • 调试通道:FMY_DEBUG_SENSOR_TO_UART_ENBALE 打开 sensor_uart_init(),可把 FMNA 状态、Profile 模式等通过 UART 导出,方便产测。
  • G-Sensor 运动检测:TCFG_GSENSOR_ENABLE 使能 FMY_CAPABILITY_SUPPORTS_MOTION_DETECTOR_UT,接入 gSensor/fmy/gsensor_api.h 可实现防丢/移动告警类功能。

测试与验证

示例工程未在 examples/findmy 内提供独立单元测试;验证依赖实机与产测工具:

  • 配对/绑定链路:使用 iPhone"查找"App 验证广播可见、配对、Owner/Non-Owner 控制点行为;
  • 产测工具:testbox_mode_enable 字段支持测试盒模式(频偏校准),配合 SENSOR UART 输出验证 RF 指标;
  • OTA 验证:FMY_OTA_SUPPORT_CONFIG 下通过 94110001-6D9B-4225-A4F1-6A4A7F01B0DE 特征验证固件升级流程;
  • 功耗验证:确认 is_app_fmy_active=0 时系统可进入低功耗,广播/连接状态下的电流符合产品规格。

Related Links

  • app_fmy.c(应用入口)
  • ble_fmy.h(全局状态/API)
  • ble_fmy.c(BLE/FMNA 主实现)
  • ble_fmy_fmna.c、ble_fmy_ota.c、ble_fmy_sensor_uart.c
  • ble_fmy_cfg.h(示例配置)
  • app_main.c(任务注册)
  • 相关目录:third_party/fmna/(FMNA 库)、gSensor/fmy/(运动检测)、gatt_common/(GATT 公共层)
  • 兄弟主题:BLE 通用透传示例(LE 中心/外设数据通道)、OTA 升级示例(固件升级完整流程)
Prev
透传与 AT 指令示例
Next
2.4G 私有协议与 Dongle 示例