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

    • SDK 总览
    • 支持芯片与蓝牙认证
    • 工程结构导航
  • 开发环境与构建

    • 环境搭建与工具链安装
    • 编译指南与工程选择
    • 烧录与生产工具
  • BLE 透传/数传应用

    • 透传应用框架与处理模块
    • 透传与数传示例
    • 多连接与自定义服务示例
    • FindMy 与查找网络示例
  • HID 人机交互应用

    • 键盘与按键设备示例
    • 鼠标设备示例
    • 遥控器示例
    • HID 蓝牙应用模块
  • 公共 BSP 模块

    • 按键、编码器与红外输入
    • 传感器驱动
    • LED 与显示控制
    • 串口与 USB 通信
    • 存储、参数与时钟
    • 电源与温度管理
    • 消息、内存与系统配置
    • OTA 升级框架
  • 蓝牙协议栈与库

    • BLE 控制器与协议栈适配
    • 经典蓝牙 BR/EDR 支持
    • 第三方蓝牙协议
    • 设备管理框架
    • DUT 测试与射频认证
  • 构建系统与开发工具

    • Makefile 构建系统
    • 固件后处理与配置工具
    • 辅助脚本与库合并
  • 文档与硬件资料

    • AT 命令参考
    • 硬件参考资料
    • SDK 文档与在线资源

FindMy 与查找网络示例

本页介绍 AW333N BLE SDK 中 FindMy(Apple 查找网络 / FMNA)与定位器示例的实现:demo app 的接入方式、FMNA 预编译库的集成、产测模块 findmy_product_test.c 的 HID 透传协议、SN/UUID/Token 管理以及双 Bank OTA 升级流程。

Purpose and Scope

本页覆盖 apps/demo/transfer 工程中与 FindMy / 查找网络 相关的完整能力:

  • demo app 如何通过 CONFIG_APP_FINDMY 接入(app_main.c);
  • FMNA(Find My Network Accessory)预编译库的内存布局与链接脚本约定;
  • 产测模块 findmy_product_test.c 的通道、命令、帧格式与加解密;
  • SN / UUID / Token 的读取与写入、Flash UUID 读取;
  • 基于 Dongle(USB HID)中转 BLE 的产测链路与 OTA 远端升级。

不覆盖(属于其他页面的话题):Google FHN 例子(app_main.c 中提及的另一个定位器方案)、通用 BLE 透传/数传、AT 模组指令、HID 人机交互等。这些能力各有独立的示例与文档,本页只做交叉引用。

Overview

FindMy 是 Apple 的查找网络(Find My Network),配件端需要实现 FMNA 协议(广播密钥轮换、附近设备上报等)。在 AW333N SDK 中,FMNA 协议栈以预编译固件库形式提供:产测模块通过 fmna_api.h 调用其接口,链接脚本则为其预留独立的 _fmna_data_begin / _fmna_const_begin 内存段,避免与用户代码的数据区冲突。

示例工程 apps/demo/transfer 是一个多 demo 复用工程,通过编译宏选择当前 app:当定义 CONFIG_APP_FINDMY 时,app 名称为 "findmy",动作分发到 ACTION_FINDMY_MAIN(产测主循环)。配套的产测模块在 FMY_PRODUCT_TEST_MODE 下编译,实现与 PC 产测工具 的完整通信:

  • 通信拓扑:PC 产测工具 ↔ USB HID Dongle ↔ BLE 空中链路 ↔ AW333N 设备;
  • 支持产测数据帧的分包(64 字节 HID 报告)、CRC16 校验与可选的逐包加密(K 值由设备 MAC 与产测下发数据联合计算);
  • 支持 Flash UUID 读取、SN/UUID/Token 读写、UARP 信息透传(查询/停止/取消)以及 1KB 分块的双 Bank OTA 升级;
  • 产测完成后可配置无动作、关机(POFF)或复位(RESET),便于产线自动化。

硬件参考:AW333A FindMy 参考原理图见 doc/硬件资料/原理图/AW333A Findmy参考原理图V1.0.pdf;整体适用场景(定位器 Findmy & Find Hub)见 README.md。

Architecture

下图展示 FindMy 示例的整体架构与数据通路(所有节点均对应真实源码/组件):

flowchart TD
    subgraph sg_PC["PC 产测端"]
        PCTool["PC 产测工具"]
    end

    subgraph sg_Dongle["中转层"]
        Dongle["BLE Dongle 固件<br/>USB HID 64B 帧"]
    end

    subgraph sg_Device["AW333N 设备端"]
        AppMain["app_main.c<br/>CONFIG_APP_FINDMY → ACTION_FINDMY_MAIN"]
        ProdTest["findmy_product_test.c<br/>产测模块 (FMY_PRODUCT_TEST_MODE)"]
        FMNALib["FMNA 预编译库<br/>fmna_api.h / fmy_state_idle_set_active"]
        BLEStack["BLE 协议栈<br/>GATT 特征 0x0029"]
        Flash["Flash 存储<br/>SN / UUID / Token / Flash UUID"]
        OTA["双 Bank OTA<br/>dual_bank_updata_api_v2"]
    end

    PCTool -->|"HID 帧(0x4A5E + channel + CRC + 0xBD)"| Dongle
    Dongle -->|"BLE 空中链路"| BLEStack
    BLEStack --> ProdTest
    AppMain -->|"动作分发"| ProdTest
    ProdTest --> FMNALib
    ProdTest --> Flash
    ProdTest --> OTA
    OTA -->|"dual_bank_updata_api_v2.h"| Flash
    FMNALib --> BLEStack

各组件职责:

组件源码位置职责
App 入口app_main.c通过 CONFIG_APP_FINDMY 选择 findmy app,设置名称与动作
产测模块findmy_product_test.c命令解析、SN/UUID/Token 管理、帧封装、OTA 状态机
FMNA 库fmna_api.h(闭源库)实现 Apple 查找网络协议;提供 doe() 加解密、fmy_state_idle_set_active() 空闲控制
链接脚本btstack_lib.ld 等为 FMNA 库预留 _fmna_data_begin / _fmna_const_begin 数据段
存储tzflash_read_flash_uuid() / Flash 写接口产测数据持久化
OTAdual_bank_updata_api_v2.h(BD57 平台)双 Bank 升级,START → WRITE → VERIFY

设计意图:FMNA 协议栈闭源交付,SDK 通过链接脚本段符号(_fmna_data_begin、_fmna_const_begin)把库的全局数据固定到专属内存区域,用户程序只需链接即可使用,无需了解协议细节;产测链路则刻意与协议栈解耦——所有产测数据统一走“HID 帧 → Dongle → BLE → GATT 特征”的透传管道,使产测工具与设备端只需关心帧协议,不依赖 FMNA 内部实现。

Demo App 接入与启动流程

示例工程 apps/demo/transfer 采用“多 app 复用”结构,通过编译宏决定当前运行的 demo。app_main.c 中通过 CONFIG_APP_FINDMY 将 findmy 注册为可选 app:

#elif CONFIG_APP_FINDMY
    it->name = "findmy";
    it->action = ACTION_FINDMY_MAIN;

Source: app_main.c

该 app 条目包含两个关键字段:

  • name:app 名称 "findmy",用于日志与 UI 展示;
  • action:ACTION_FINDMY_MAIN,动作 ID,由框架消息循环分发到产测模块的主处理函数。

app_main.c 还在 app 切换逻辑中再次出现 findmy 分支(case 0 时默认选择 findmy),并在注释中明确说明移植方式:

//示例只修改findmy和google fhn切换例子,其他app参考移植
switch (next_app) {
...
    case 0:
#if CONFIG_APP_FINDMY
        it->name = "findmy";
        it->action = ACTION_FINDMY_MAIN;
#endif

Source: app_main.c

设计意图:name + action 的抽象把“app 是什么”和“app 做什么”分离——不同 demo(findmy、google FHN、透传等)共用同一套 app 框架,切换只需改编译宏与少量 case 分支,其余代码参考移植即可。

FMNA 库与链接脚本约定

FMNA 协议栈以预编译库形式链接进固件。为确保库的内部全局变量与常量不被用户代码的段布局破坏,三个链接脚本都为 findmy 预留了专属段:

/*findmy*/
. = ALIGN(4);
_fmna_data_begin = .;

Source: btstack_lib.ld

同样的 /*findmy*/ 段标记还出现在:

  • btstack_lib.ld#L53-L54 与 _fmna_const_begin(btstack_lib.ld#L130-L131);
  • btstack_lib_data.ld#L21-L22(data 段 _fmna_data_begin);
  • btstack_lib_bss.ld#L18-L19(bss 段对齐)。

产测模块通过 fmna_api.h 访问库接口,并声明了三个外部符号(findmy_product_test.c#L150-L152):

extern void doe(u16 k, void *pBuf, u32 lenIn, u32 addr);        // 加解密
extern void fmy_state_idle_set_active(uint8_t active);          // FMNA 空闲状态控制
extern void tzflash_read_flash_uuid(u8 *uuid_buf);              // 读 Flash UUID

设计意图:_fmna_*_begin 段符号是“库 ↔ 链接脚本”的契约——库内部用这些符号定位自己的数据区起点,因此用户不得删除或移动这些段标记,否则 FMNA 库的全局状态会错乱;而 doe() 之类的符号则说明加解密能力由 FMNA 库提供,产测模块只负责计算 K 值并调用。

产测模块总览

findmy_product_test.c 整个文件由 #if FMY_PRODUCT_TEST_MODE 保护,即仅在产测固件中编译。它的核心职责是把 PC 产测工具的指令通过 Dongle 透传到设备,并返回结果。

通讯通道

数据在“PC ↔ Dongle ↔ 设备”之间按通道区分用途:

enum {
    HID_RX_HANDLER_CHANNEL_COMMAND = 0x00 + DONGLE_OTA_VERSION,
    HID_RX_HANDLER_CHANNEL_RESPONSE = 0x10 + DONGLE_OTA_VERSION,
    HID_RX_HANDLER_CHANNEL_USB = 0x20 + DONGLE_OTA_VERSION,
    HID_RX_HANDLER_CHANNEL_REMOTE1 = 0x30 + DONGLE_OTA_VERSION,
    HID_RX_HANDLER_CHANNEL_REMOTE2 = 0x40 + DONGLE_OTA_VERSION,
    ...
    HID_RX_HANDLER_CHANNEL_REMOTE8 = 0xA0 + DONGLE_OTA_VERSION,
};

Source: findmy_product_test.c

通道用途
0x00PC → Dongle 命令通道
0x10Dongle → PC 响应通道
0x20PC → USB 透传
0x30 ~ 0xA0远端升级透传(8 路,channel_3 ~ channel_9)

产测命令集

enum {
    APP_CMD_GET_FMY_PRODUCT_FLASH_UUID = 0x51,   // 读 Flash UUID
    APP_CMD_SET_FMY_PRODUCT_TEST_STATUS,          // 0x52 设置产测状态
    APP_CMD_GET_FMY_PRODUCT_INFO,                 // 0x53 获取产测信息
    APP_CMD_GET_FMY_SN_AND_TOKEN,                 // 0x54 读 SN 与 Token
    APP_CMD_SET_FMY_SN_AND_TOKEN,                 // 0x55 写 SN 与 Token
    APP_CMD_TRANS_FMY_UARP_INFO = 0x5A,           // UARP 信息透传
    APP_CMD_QUERY_FMY_UARP_DATA,                  // 0x5B 查询 UARP 数据
    APP_CMD_STOP_TRANS_FMY_UARP,                  // 0x5C 停止透传
    APP_CMD_CANCEL_TRANS_FMY_UARP,                // 0x5D 取消透传
};

Source: findmy_product_test.c

命令 0x51~0x55 负责产测基础项(Flash UUID、产测状态、产品信息、SN/Token 读写);0x5A~0x5D 负责 UARP(FMNA 相关的上行/下行透传数据)的传输控制,用于产测工具与设备间搬运 FMNA 协议数据。

产测状态机

//findmy产测状态
#define FINDMY_PRODUCT_TEST_DONE            0x00
#define FINDMY_PRODUCT_TEST_BEGIN           0x01
#define FINDMY_PRODUCT_TEST_FAIL            0x02

//产测完成后的动作
#define FINDMY_PRODUCT_TEST_DONE_NO_ACTION  0x00
#define FINDMY_PRODUCT_TEST_DONE_POFF       0x01
#define FINDMY_PRODUCT_TEST_DONE_RESET      0x02

Source: findmy_product_test.c

stateDiagram-v2
    [*] --> BEGIN: 产测开始
    BEGIN --> DONE: 全部项目通过
    BEGIN --> FAIL: 任一项失败
    DONE --> POFF: 配置关机
    DONE --> RESET: 配置复位
    DONE --> [*]: 无动作
    FAIL --> BEGIN: 重新测试

命令处理结果使用独立状态码:FINDMY_CMD_HANDLE_SUCCESS 0x00 / FINDMY_CMD_HANDLE_FAIL 0x01,未处理状态为 FINDMY_CMD_STATUS_NULL 0xFF;读 SN/UUID/Token 时另有 READ_SN_CMD_HANDLE_NULL 0x02 表示数据为空(findmy_product_test.c#L50-L59)。

HID 帧协议(Dongle ↔ PC)

产测数据在 PC 与 Dongle 之间以 USB HID 报告承载。帧封装由 fmy_send_ble_data_to_dongle() 实现(设备端 BLE 收到的数据,组帧后发给 Dongle,由 Dongle 直接转发 PC):

static uint8_t fmy_send_ble_data_to_dongle(u16 channel, u8 *data, u16 len, u8 cmd_type)
{
    u16 calc_crc;

    len = len + 1;//加上cmd_type长度
    u8 send_data_tag[HID_SEND_DATA_TAG_LONG - 2 - 1] = {0x4A, 0x5E, channel, 0x00, len / 256, len % 256, cmd_type};
    len = len - 1;//去除cmd_type长度
    u8 send_data[((len + HID_SEND_DATA_TAG_LONG + 1) / HID_USB_SEND_MAX + 1) * HID_USB_SEND_MAX]; // 包括crc 2bytes

    memset(send_data, 0x00, sizeof(send_data));
    memcpy(&send_data, &send_data_tag, HID_SEND_DATA_TAG_LONG - 2 - 1);
    memcpy(&send_data[HID_SEND_DATA_TAG_LONG - 2 - 1], data, len);

    calc_crc = CRC16(&send_data[2], (5 + len)); //crc: config(2)+length(2)+cmd type(1)+payload(len)
    send_data[HID_SEND_DATA_TAG_LONG + len - 2 - 1] = (calc_crc >> 8) & 0xFF;
    send_data[HID_SEND_DATA_TAG_LONG + len - 2] = calc_crc  & 0xFF;
    send_data[HID_SEND_DATA_TAG_LONG + len - 1] = HID_RX_HANDLER_TAIL_TAG;

    //发送数据分包为每包64bytes发包,包含填包
    u8 i = (len + HID_SEND_DATA_TAG_LONG) / HID_USB_SEND_MAX;
    i = ((len + HID_SEND_DATA_TAG_LONG) % HID_USB_SEND_MAX) ? (i + 1) : (i);
    for (u8 j = 1; j <= i; j++) {
        log_info("fmy send ble data to dongle %d", j);
        put_buf(&send_data[(j - 1) * 64], HID_USB_SEND_MAX);

        //加密
        if (crypt_k_value) {
            doe(crypt_k_value, &send_data[(j - 1) * 64], HID_USB_SEND_MAX, 0);
        }
    }
    ...
}

Source: findmy_product_test.c

帧格式(HID_SEND_DATA_TAG_LONG = 10 为除 payload 外的包长度,HID_USB_SEND_MAX = 64):

偏移内容长度说明
0~1帧头 0x4A 0x5E2HID_RX_HANDLER_HEND_TAG
2channel1命令/响应/透传/OTA 通道
3保留10x00(HID_SEND_DATA_TAG_LONG - 2 - 1 后的高位长度位)
4~5len2cmd_type + payload 长度
6cmd_type1命令类型(如 0x55 写 SN/Token)
7~Npayloadlen有效数据
N+1~N+2CRC162覆盖 config(2) + length(2) + cmd(1) + payload
N+3帧尾 0xBD1HID_RX_HANDLER_TAIL_TAG

设计意图:

  • 64 字节分包:USB HID 中断传输的报告长度固定为 64B,超长数据必须切包,接收端通过帧头/长度字段重组;
  • CRC16 完整性:校验范围覆盖配置、长度、命令与负载,保证产测数据不被 USB 传输干扰破坏;
  • 逐包加密:当 crypt_k_value 非 0 时,每个 64B 包在发送前调用 doe(k, buf, 64, 0) 加密,防止空中/总线上产测数据(如 Token)被窃取;K 值为 0 时明文发送,兼容尚未配对/未下发密钥的阶段。

K 值计算

K 值是加解密的会话密钥,由 产测工具下发的 6 字节数据 + 设备 BLE MAC 6 字节 拼接成 12 字节后做 CRC16 得到:

void fmy_product_test_caculate_k_value(u8 *data, u8 len)
{
    u8 tmp_addr12[12] = {0};

    memcpy(&tmp_addr12[0], data, len);
    memcpy(&tmp_addr12[6], &findmy_ble_mac[0], 6);

    log_info_hexdump(tmp_addr12, 12);

    crypt_k_value = CRC16(tmp_addr12, 12);
    log_info("crypt_k_value: 0x%x", crypt_k_value);
}

Source: findmy_product_test.c

设备 BLE MAC 由 fmy_product_test_add_dev_mac() 写入:

void fmy_product_test_add_dev_mac(u8 *data, u8 len)
{
    memcpy(&findmy_ble_mac[0], data, len);
}

Source: findmy_product_test.c

密钥绑定设备 MAC 的设计,使同一产测工具的会话密钥无法跨设备复用,提升产测数据(尤其 Token)的保密性。

SN / UUID / Token 管理

产测的核心数据是 SN(序列号)、UUID(查找网络标识)与 Token(认证令牌)。文件用一条宏定义了它们组合后的最大长度:

//定义ret\SN\UUID\TOKEN的固定长度
#define RET_SN_UUID_TOKEN_DATA_LEN   1 + 3 + 16 + 3 + 1024 + 3 + 16

Source: findmy_product_test.c

布局为:状态(1B) + SN[长度(3B) + 内容(16B)] + UUID[长度(3B) + 内容(1024B)] + Token[长度(3B) + 内容(16B)]。每段都带 3 字节长度前缀,采用 TLV 风格,接收端可精确切分变长字段;UUID 预留 1024B,说明产测允许写入较大体积的查找网络标识数据。

配套的产测信息变量(findmy_product_test.c#L136-L138):

static uint8_t FMY_ManufacturerName[64];
static uint8_t FMY_ModelName[64];
static uint8_t fmy_product_data[8];

这些变量由 APP_CMD_GET_FMY_PRODUCT_INFO(0x53)返回给产测工具。设备侧 Flash UUID 通过 tzflash_read_flash_uuid() 读取,由 APP_CMD_GET_FMY_PRODUCT_FLASH_UUID(0x51)命令触发——Flash UUID 用于在产测端唯一标识芯片,防止 SN 与芯片绑定错位。

BLE 数据通路

设备端 BLE 接收/发送路径的关键常量:

#define BLE_DATA_SEND_MAX                   240
#define FMY_DEBUG_RX_ATT_HANDLE             0x0029

Source: findmy_product_test.c

  • BLE_DATA_SEND_MAX = 240:BLE 单包最大数据长度(长包/MTU 相关的发送上限);
  • FMY_DEBUG_RX_ATT_HANDLE = 0x0029:调试接收特征 ATT_CHARACTERISTIC_4F860005-943B-49EF-BED4-2F730304427A_01 的 value handle,产测命令经该特征值写入设备;
  • 接收侧维护 ble_recv_long_buf 与 ota_buff_len,用于重组跨包到达的长数据;
  • 数据从 BLE 收到后,经 fmy_send_ble_data_to_dongle() 组帧并转发给 Dongle → PC(见上文 HID 帧协议)。

OTA 升级(双 Bank)

产测还承担固件升级能力,允许产线通过同一链路刷写固件。关键定义:

#define FMY_REQ_OTA_DATA_MAX                1024    //一次申请1K升级数据

enum {
    APP_OTA_UPDATE_SUCCESS,
    APP_OTA_TRANS_FAILED,
    APP_OTA_UPDATE_FAILED,
};

typedef enum {
    DUAL_OTA_STEP_START = 0,
    DUAL_OTA_STEP_WRITE,
    DUAL_OTA_STEP_VERIFY,
} DUAL_OTA_STEP;

struct file_parameter_t {
    uint16_t file_version;
    uint32_t file_size;
    uint16_t crc;
} __attribute__((packed));

struct file_parameter_t file_parameter;
static uint32_t global_file_offset = 0;

Source: findmy_product_test.c

要点:

  • 分块传输:每次申请 1KB(FMY_REQ_OTA_DATA_MAX)升级数据,global_file_offset 记录写入偏移,配合 file_parameter_t(版本、大小、CRC)做完整性校验;
  • 三步状态机:DUAL_OTA_STEP_START → WRITE → VERIFY,升级包先写入 Bank 1,校验通过后再切换启动 Bank;
  • 双 Bank API:在 CONFIG_CPU_BD57 平台包含 dual_bank_updata_api_v2.h(findmy_product_test.c#L24-L26),升级失败不损坏当前固件;
  • 远端透传通道:升级数据走 0x30~0xA0 的 8 路远端升级通道,与普通命令/响应通道(0x00/0x10)隔离,避免升级流量阻塞产测命令。

Core Flow(核心流程)

产测主流程:写 SN / UUID / Token

以 APP_CMD_SET_FMY_SN_AND_TOKEN(0x55)为例,展示从 PC 产测工具到设备 Flash 的完整链路:

sequenceDiagram
    participant PC as PC 产测工具
    participant DG as Dongle (USB HID)
    participant DV as AW333N 设备
    participant FL as Flash / FMNA 库

    PC->>DG: HID 帧(0x51 读 Flash UUID)
    DG->>DV: BLE 透传 (GATT 0x0029)
    DV->>DV: 解析命令 & 调 tzflash_read_flash_uuid()
    DV-->>DG: 响应(0x10 通道, 组帧+CRC)
    DG-->>PC: HID 帧返回 UUID

    PC->>DG: HID 帧(0x55 写 SN/UUID/Token, 含 K 值数据)
    DG->>DV: BLE 透传
    DV->>DV: fmy_product_test_caculate_k_value() 计算 K
    DV->>FL: 写入 SN / UUID / Token (TLV 布局)
    FL-->>DV: 写结果
    DV-->>DG: 0x00 成功 / 0x01 失败
    DG-->>PC: 响应帧

流程要点:

  1. 发现阶段:PC 先发 0x51 读取芯片 Flash UUID,用于绑定芯片与 SN;
  2. 密钥协商:PC 下发 6 字节随机数据,设备拼接自身 BLE MAC 得到 12 字节,CRC16 生成 crypt_k_value——此后双向数据按包加密;
  3. 数据写入:0x55 携带 TLV 格式的 SN/UUID/Token,设备校验并持久化;
  4. 结果回传:设备以 0x10 响应通道回 FINDMY_CMD_HANDLE_SUCCESS(0x00) 或 FAIL(0x01),产测工具据此判定 PASS/FAIL,并按配置触发关机/复位。

产测结束动作

产测完成(FINDMY_PRODUCT_TEST_DONE)后,设备按产测工具下发的配置执行收尾:

flowchart TD
    Done["产测完成<br/>FINDMY_PRODUCT_TEST_DONE (0x00)"] --> Action{"结束动作配置"}
    Action -->|"0x00"| NoAction["无动作, 继续运行"]
    Action -->|"0x01"| Poff["关机<br/>FINDMY_PRODUCT_TEST_DONE_POFF"]
    Action -->|"0x02"| Reset["复位<br/>FINDMY_PRODUCT_TEST_DONE_RESET"]
    Poff --> End([产线流转])
    Reset --> End
    NoAction --> End

Usage Examples(源码示例)

示例 1:注册 findmy demo app

在 app_main.c 中通过编译宏选择 findmy app,设置名称与动作:

#elif CONFIG_APP_FINDMY
    it->name = "findmy";
    it->action = ACTION_FINDMY_MAIN;

Source: app_main.c

示例 2:计算会话密钥 K 值

设备端把产测工具下发的 6 字节与 BLE MAC 拼成 12 字节,CRC16 得到加解密 K 值:

void fmy_product_test_caculate_k_value(u8 *data, u8 len)
{
    u8 tmp_addr12[12] = {0};

    memcpy(&tmp_addr12[0], data, len);
    memcpy(&tmp_addr12[6], &findmy_ble_mac[0], 6);

    log_info_hexdump(tmp_addr12, 12);

    crypt_k_value = CRC16(tmp_addr12, 12);
    log_info("crypt_k_value: 0x%x", crypt_k_value);
}

Source: findmy_product_test.c

示例 3:设置设备 BLE MAC

产测模块记录设备 BLE MAC,供 K 值计算使用:

void fmy_product_test_add_dev_mac(u8 *data, u8 len)
{
    memcpy(&findmy_ble_mac[0], data, len);
}

Source: findmy_product_test.c

示例 4:HID 帧组包与加密发送

设备把 BLE 收到的产测数据封装成 HID 帧(帧头 0x4A5E + channel + 长度 + cmd_type + payload + CRC16 + 帧尾 0xBD),按 64B 分包并逐包加密:

u8 send_data_tag[HID_SEND_DATA_TAG_LONG - 2 - 1] = {0x4A, 0x5E, channel, 0x00, len / 256, len % 256, cmd_type};
...
calc_crc = CRC16(&send_data[2], (5 + len)); //crc: config(2)+length(2)+cmd type(1)+payload(len)
send_data[HID_SEND_DATA_TAG_LONG + len - 2 - 1] = (calc_crc >> 8) & 0xFF;
send_data[HID_SEND_DATA_TAG_LONG + len - 2] = calc_crc  & 0xFF;
send_data[HID_SEND_DATA_TAG_LONG + len - 1] = HID_RX_HANDLER_TAIL_TAG;

u8 i = (len + HID_SEND_DATA_TAG_LONG) / HID_USB_SEND_MAX;
i = ((len + HID_SEND_DATA_TAG_LONG) % HID_USB_SEND_MAX) ? (i + 1) : (i);
for (u8 j = 1; j <= i; j++) {
    put_buf(&send_data[(j - 1) * 64], HID_USB_SEND_MAX);
    if (crypt_k_value) {
        doe(crypt_k_value, &send_data[(j - 1) * 64], HID_USB_SEND_MAX, 0);
    }
}

Source: findmy_product_test.c

Configuration Options(配置项)

FindMy 示例由编译宏与源码常量共同控制,汇总如下:

选项类型默认说明
CONFIG_APP_FINDMY编译宏关闭使能 findmy demo app(app_main.c 中选择 "findmy" / ACTION_FINDMY_MAIN)
FMY_PRODUCT_TEST_MODE编译宏关闭使能产测模块编译(findmy_product_test.c 整体由 #if 保护)
DONGLE_OTA_VERSION常量0Dongle OTA 版本,参与通道基址计算
HID_USB_SEND_MAX常量64USB HID 单包长度,产测帧分包粒度
HID_SEND_DATA_TAG_LONG常量10除 payload 外的帧头+CRC+帧尾长度
BLE_DATA_SEND_MAX常量240BLE 单包最大发送长度
FMY_REQ_OTA_DATA_MAX常量1024OTA 单次申请升级数据量(1KB)
RET_SN_UUID_TOKEN_DATA_LEN常量1+3+16+3+1024+3+16SN/UUID/Token 组合数据最大长度(TLV)
FINDMY_CMD_STATUS_NULL常量0xFF命令未处理状态
FINDMY_CMD_HANDLE_SUCCESS/FAIL常量0x00/0x01命令处理结果
READ_SN_CMD_HANDLE_NULL常量0x02读 SN/UUID/Token 结果为空
FINDMY_PRODUCT_TEST_DONE/BEGIN/FAIL常量0x00/0x01/0x02产测状态
FINDMY_PRODUCT_TEST_DONE_*常量0x00/0x01/0x02产测完成动作:无动作/关机/复位

API Reference(接口参考)

void fmy_product_test_add_dev_mac(u8 *data, u8 len)

记录设备 BLE MAC(6 字节),供会话密钥 K 值计算使用。

参数:

  • data (u8*):MAC 数据指针
  • len (u8):MAC 长度

返回: 无

Source: findmy_product_test.c#L165-L168

void fmy_product_test_caculate_k_value(u8 *data, u8 len)

将产测工具下发的 6 字节与设备 BLE MAC 拼成 12 字节,CRC16 计算加解密 K 值并存入全局 crypt_k_value。

参数:

  • data (u8*):产测工具下发的数据(6 字节)
  • len (u8):数据长度

返回: 无;副作用:更新 crypt_k_value,为 0 时后续发送不加密。

Source: findmy_product_test.c#L181-L192

static uint8_t fmy_send_ble_data_to_dongle(u16 channel, u8 *data, u16 len, u8 cmd_type)

将数据封装为 HID 帧(帧头 0x4A5E + channel + 长度 + cmd_type + payload + CRC16 + 0xBD 帧尾),按 64B 分包发送,crypt_k_value 非 0 时逐包调用 doe() 加密。

参数:

  • channel (u16):通讯通道(命令 0x00 / 响应 0x10 / USB 透传 0x20 / 远端升级 0x30~0xA0)
  • data (u8*):payload
  • len (u16):payload 长度
  • cmd_type (u8):命令类型(0x51~0x5D)

返回: 发送状态(uint8_t)。

Source: findmy_product_test.c#L205-L240

外部库接口(由 FMNA 库 / 平台提供)

签名说明
extern void doe(u16 k, void *pBuf, u32 lenIn, u32 addr)FMNA 库加解密函数,k 为 K 值
extern void fmy_state_idle_set_active(uint8_t active)控制 FMNA 空闲状态激活
extern void tzflash_read_flash_uuid(u8 *uuid_buf)读取芯片 Flash UUID

Source: findmy_product_test.c#L150-L152

Failure Modes, Edge Cases & Concurrency(故障模式与边界)

  • 命令未处理:FINDMY_CMD_STATUS_NULL (0xFF) 表示命令尚未进入处理流程,产测工具侧应将其视为超时/重试信号。
  • 读数据为空:READ_SN_CMD_HANDLE_NULL (0x02) 区分“读取失败”与“数据不存在”,产线可据此决定是否先写 SN 再校验。
  • CRC 校验失败:帧尾与 CRC16 覆盖整帧关键字段,任一字节被 USB 传输破坏都会导致接收端丢弃该帧;分包重组的计数变量 all_packet / remain_packet 用于恢复同步。
  • 加密降级:crypt_k_value == 0 时明文发送——必须保证在写入 Token 之前完成 K 值协商,否则 Token 会明文暴露。
  • OTA 失败分类:APP_OTA_TRANS_FAILED(传输层失败,可重传)与 APP_OTA_UPDATE_FAILED(写入/校验失败)分离,配合双 Bank 机制保证升级失败不损坏当前固件;DUAL_OTA_STEP(START→WRITE→VERIFY)任一阶段失败都会中止。
  • 并发/连接管理:8 路远端升级透传通道(0x30~0xA0)可并行;模块用全局 fmy_conn_handle 记录当前 BLE 连接句柄,产测期间应保证单连接,断连时需复位 fmy_product_test_begined 等状态标志。
  • 产测中断恢复:global_file_offset 与 file_parameter(版本/大小/CRC)保证 OTA 可断点续传或重传;产测状态标志 fmy_product_test_begined 防止重复进入产测流程。

Performance & Operational Notes(性能与运维)

  • 链路瓶颈在 HID 分包:每 64B 一包,长数据(如 1024B UUID)会拆成多包,HID_SEND_DATA_TAG_LONG(10) 的帧开销占比约 15%,产线吞吐由 USB 轮询周期决定。
  • OTA 分块粒度:单次申请 1KB,global_file_offset 顺序写入,配合 VERIFY 阶段的 CRC 校验,兼顾速度与可靠性。
  • 加密开销:doe() 逐包(64B)执行,K 值协商一次后全程复用,产测周期内开销可忽略。
  • 产线自动化:产测完成后可配置 POFF(关机)或 RESET(复位),便于流水线流转;FINDMY_PRODUCT_TEST_DONE_NO_ACTION 供调试。
  • 日志:模块启用 LOG_ERROR/DEBUG/INFO/DUMP/CLI(LOG_TAG "[FMY_PROD_TEST]"),关键路径(K 值、分包序号、hexdump)均有打印,产线问题可据此定位。

Extension Points(扩展点)

  • 新增产测命令:在命令枚举(0x51~0x5D)后追加新命令号,并在接收分发处增加对应处理分支即可;响应统一走 0x10 通道与同一帧封装函数。
  • 新增通讯通道:通道枚举(0x00~0xA0)按 0x10 步进扩展,远端升级通道已预留 8 路,可仿照增加。
  • 移植到其他 app:app_main.c 注释明确说明“示例只修改 findmy 和 google fhn 切换例子,其他 app 参考移植”——新定位器方案只需提供 name 与 action 并在 switch 中注册。
  • FMNA 库能力:通过 fmna_api.h 暴露的接口(如 fmy_state_idle_set_active())控制查找网络行为;库为闭源,接口以 extern 声明为准。
  • 硬件平台:CONFIG_CPU_BD57 时启用 dual_bank_updata_api_v2.h 双 Bank 升级;其他平台的 OTA 实现需按平台 API 替换。

Tests(测试与验证)

仓库中未提供独立的单元测试文件;FindMy 示例的验证主要依赖:

  1. 产测固件自检:findmy_product_test.c 本身即产线测试逻辑,通过 PC 产测工具逐项下发命令并核对响应状态码;
  2. 硬件验证:参考原理图 AW333A Findmy参考原理图V1.0.pdf 搭建定位器硬件;
  3. 端到端联调:PC 工具 ↔ Dongle ↔ 设备链路,覆盖 Flash UUID 读取、SN/Token 读写、UARP 透传与 OTA 升级四个环节。

Related Links

  • README.md — BLE 透传/数传与定位器场景
  • findmy_product_test.c — 产测模块实现
  • app_main.c — demo app 注册与切换
  • btstack_lib.ld — FMNA 库链接段
  • btstack_lib_data.ld — FMNA data 段
  • btstack_lib_bss.ld — FMNA bss 段
  • AW333A Findmy 参考原理图

相关主题:BLE 透传/数传示例、AT 模组指令、Google FHN 定位器示例(见 app_main.c 中的 app 切换逻辑)属于独立页面,本页不再展开。

Prev
多连接与自定义服务示例