杰理 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)
  • 文档与开发资源

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

低功耗蓝牙 Mesh 方案(llsync_mesh)

llsync_mesh 是杰理 AC63 蓝牙 SDK 中集成的腾讯云 IoT(qiot)低功耗蓝牙 Mesh 方案(LLSync)适配模块,提供基于 BLE 广播/扫描与厂商 Model 的轻量级配网、认证、数据下发与上报能力,位于 apps/common/third_party_profile/llsync_mesh/ 目录。

Purpose and Scope

本页面向负责集成、移植与二次开发 llsync_mesh 的嵌入式工程师,完整说明:

  • 模块目录结构与分层职责(公开 API / HAL 适配 / 数据模板 / SDK 内核 / 配置)
  • 公开 API(ble_qiot_export.h)每个接口的语义、实现细节与设计意图
  • 厂商数据模板机制(ble_property_t 属性注册与 set/get 回调)
  • 设备 UUID 广播格式、扫描响应数据与 AuthValue 安全认证算法
  • 未入网广播 / 静默广播状态机的配置语义
  • 配置宏(ble_qiot_mesh_cfg.h)的完整清单、默认值及调优含义

本页不覆盖:BLE 协议栈本身的广播/扫描/连接实现、其他第三方集成方案(如微信小程序、涂鸦等)以及应用层业务逻辑。这些内容属于 SDK 协议栈或 9-third-party-integrations 目录下相邻条目。

Overview

LLSync(Low Latency Sync)是腾讯云 IoT 面向低功耗设备的 BLE Mesh 通信方案。其核心思想是:设备在未入网阶段通过厂商自定义广播(Vendor ADV)携带 UUID 供 Provisioner(配网器/小程序)发现;配网成功后通过厂商 Model 的 Opcode(SET / GET / SET_UNACK / CONFIRMATION)完成属性读写与确认;设备侧使用 HMAC-SHA256 + 异或折叠算法基于设备 PSK 计算 AuthValue,完成配网过程中的双向认证。

llsync_mesh 在 AC63 SDK 中的角色是一个可移植的中间件:它不直接操作芯片寄存器,而是通过 ble_qiot_import(导入/适配接口)调用协议栈能力(如 llsync_mesh_dev_info_get 获取设备信息、llsync_mesh_vendor_data_report 触发上报等),因此可以跨杰理不同芯片平台复用。

关键设计意图

  1. 分层解耦:export 侧(对外实现)与 import 侧(平台适配)分离,中间件不感知具体协议栈 API,平台移植只改 hal/ble_qiot_import.c。
  2. 模板化数据模型:属性(如开关、HSL 颜色、色温)以 ble_property_t 数组注册,SDK 内核按 type 匹配属性并回调应用层,业务无需关心帧格式解析细节。
  3. 安全优先:AuthValue 由随机数 + 产品 ID + 设备名的 HMAC-SHA256 签名再异或折叠生成,且 ble_qiot_utils_hmac256、ble_qiot_utils_sha256、ble_qiot_utils_base64 均为 SDK 自带实现,避免依赖平台加密库。
  4. 低功耗广播策略:未入网广播高密度(150ms 时长 / 500ms 间隔)持续 10 分钟用于快速被发现;超时未配网则退化为 60s 间隔的静默广播,仅保留被 Provisioner 发现的最小可见性,显著降低功耗。

架构

flowchart TD
    subgraph sg_App["应用层 / 蓝牙协议栈"]
        BTStack["BLE 协议栈事件<br/>扫描/广播/接收"]
        AppLogic["应用业务逻辑"]
    end

    subgraph sg_Public["公开 API (include)"]
        ExportH["ble_qiot_export.h"]
    end

    subgraph sg_HAL["HAL 层 (hal)"]
        ExportC["ble_qiot_export.c"]
        ImportC["ble_qiot_import.c"]
        ImportH["ble_qiot_import.h"]
    end

    subgraph sg_Cfg["配置 (cfg)"]
        CfgH["ble_qiot_mesh_cfg.h"]
    end

    subgraph sg_Tpl["数据模板 (data_template)"]
        TplH["ble_qiot_template.h"]
        TplC["ble_qiot_template.c"]
    end

    subgraph sg_Sdk["SDK 内核 (sdk_src)"]
        MeshCore["core/ble_qiot_utils_mesh.c"]
        Utils["utils: sha256 / hmac256 / base64 / log"]
    end

    BTStack -->|"接收/扫描事件"| ExportC
    ExportC --> ExportH
    ExportC -->|"调用平台适配函数"| ImportC
    ImportC --> ImportH
    ExportC --> CfgH
    ExportC --> TplC
    TplC --> TplH
    ExportC --> MeshCore
    MeshCore --> Utils
    AppLogic -->|"注册属性回调"| TplC

架构说明:

  • include/ble_qiot_export.h:对协议栈/应用层暴露的 6 个公共接口,是模块的"门面"。
  • hal/ble_qiot_export.c:公共接口的具体实现,包括 Opcode 分发、扫描响应拼装、UUID 构造、AuthValue 计算。
  • hal/ble_qiot_import.c / ble_qiot_import.h:平台适配层,声明并实现 llsync_mesh_dev_info_get 等由平台提供的函数(设备名、产品 ID、PSK 等),export.c 通过它获取设备凭据。
  • data_template/ble_qiot_template.{c,h}:厂商属性模板——定义属性类型枚举、负载结构体与 ble_property_t 注册表,是业务与 SDK 之间的"翻译层"。
  • sdk_src/core/ble_qiot_utils_mesh.c:Mesh 核心工具,实现 vendor data 的 set/get/set_unack/confirmation 处理与配网状态管理。
  • sdk_src/utils/:纯软件算法库(SHA-256、HMAC-SHA256、Base64、日志),无平台依赖。
  • cfg/ble_qiot_mesh_cfg.h:所有可调参数(Flash 地址、广播时长/间隔、日志宏),集中配置。

数据模板机制(data_template)

数据模板是 llsync_mesh 连接"协议帧"与"业务属性"的桥梁。SDK 不要求业务直接解析字节流,而是把属性抽象为"类型 + set/get 回调",由模板层统一翻译。

属性注册表

核心类型定义如下:

typedef int (*property_set_cb)(const char *data, uint16_t len);
typedef int (*property_get_cb)(char *buf, uint16_t buf_len);

typedef struct {
    property_set_cb set_cb;  //set callback
    property_get_cb get_cb;  //get callback
    uint16_t type;           //attr type
    uint16_t data_len;
} ble_property_t;

Source: ble_qiot_template.h

每个 ble_property_t 描述一个可读写的设备属性:type 是厂商属性类型标识,set_cb 负责把下发的负载写入业务状态,get_cb 负责把业务状态序列化供上报,data_len 声明负载长度。

厂商属性类型

模板预定义了通用属性与电工照明属性两类:

typedef enum {
    // General Properties
    LLSYNC_MESH_VENDOR_ONOFF_TYPE            = 0xF000,
    LLSYNC_MESH_VENDOR_UNIX_TIME_TYPE        = 0xF001,
    LLSYNC_MESH_VENDOR_POWER_PERCENT_TYPE    = 0xF002,

    // Electrician Lighting
    LLSYNC_MESH_VENDOR_HSL_TYPE              = 0xF100,
    LLSYNC_MESH_VENDOR_LIGHTNESS_LEVEL_TYPE  = 0xF101,
    LLYNSC_MESH_VENDOR_COLOR_TEMPRETURE_TYPE = 0xF102,

    LLSYNC_MESH_VENDOR_STRUCT_TEST_TYPE = 0xF103,
} mesh_vendor_type_e;

Source: ble_qiot_template.h

类型编码采用 0xF0xx(通用)/ 0xF1xx(电工照明)分段,便于 Provisioner 端按行业分类解析;业务可在同一枚举空间内追加自定义类型。配套的负载结构体均为 __attribute__((packed)) 紧凑布局,避免编译器对齐填充导致线上帧格式与本地结构不一致:

typedef struct {
    uint8_t onoff;
} __attribute__((packed)) llsync_mesh_onoff;

typedef struct {
    uint16_t HSL[3];
} __attribute__((packed)) llsync_mesh_HSL;

typedef struct {
    uint32_t testa;
    uint32_t testb;
} __attribute__((packed)) llsync_mesh_struct_test;

Source: ble_qiot_template.h

模板同时暴露两个查询接口:llsync_mesh_vendor_size_get() 返回属性总数,llsync_mesh_property_array_get() 返回 ble_property_t 数组指针,供 SDK 内核(utils_mesh)在收到 vendor data 时遍历匹配 type。

设备信息与广播载荷

设备信息获取

export.c 中所有涉及设备凭据的逻辑都通过 llsync_mesh_dev_info_get() 一次性取得 ble_device_info_t(设备名、产品 ID、PSK),该函数由平台适配侧(import)实现,使 SDK 内核不直接持有设备凭据:

memset(&dev_info, 0, sizeof(ble_device_info_t));
llsync_mesh_dev_info_get(&dev_info);

memcpy(&scan_resp_data[offset], dev_info.device_name, strlen(dev_info.device_name));

Source: ble_qiot_export.c

扫描响应数据

llsync_mesh_scan_data_get() 拼装 BLE 扫描响应包,固定前缀 0x02 0x01 0x06(Flags:可发现 + 不支持 BR/EDR)与 0x0d 0x09(完整本地名,长度 13),随后填充设备名:

uint8_t scan_resp_data[LLSYNC_MESH_ADV_MAX_LEN] = {0x02, 0x01, 0x06, 0x0d, 0x09};
uint8_t offset                           = 5;
ble_device_info_t dev_info;

memset(&dev_info, 0, sizeof(ble_device_info_t));
llsync_mesh_dev_info_get(&dev_info);

memcpy(&scan_resp_data[offset], dev_info.device_name, strlen(dev_info.device_name));
offset += strlen(dev_info.device_name);

memcpy(data, scan_resp_data, offset);
*data_len = offset;

Source: ble_qiot_export.c

设计意图:扫描响应不携带产品 ID,只携带可读设备名,供手机端扫描列表直接展示;入网相关的身份信息放在厂商广播 UUID 中,减少扫描响应长度、提高低功耗扫描效率。

设备 UUID 广播格式

llsync_mesh_dev_uuid_get() 构造厂商广播 UUID,字节序为:CID 低字节、CID 高字节、协议版本 | 广播类型位、RFU 标志、产品 ID(LLSYNC_MESH_PRODUCT_ID_LEN 字节)、2 字节 RFU、结束符:

data[index++] = LLSYNC_MESH_CID_VENDOR_LOW;
data[index++] = LLSYNC_MESH_CID_VENDOR_HIGH;
if (type == LLSYNC_MESH_UNNET_ADV_BIT) {
    data[index++] = LLSYNC_MESH_PROTOCOL_VERSION | LLSYNC_MESH_UNNET_ADV_BIT;
} else {
    data[index++] = LLSYNC_MESH_PROTOCOL_VERSION | LLSYNC_MESH_SILENCE_ADV_BIT;
}
data[index++] = LLSYNC_MESH_ADV_RFU_FLAG2;

memset(&dev_info, 0, sizeof(ble_device_info_t));
llsync_mesh_dev_info_get(&dev_info);
memcpy(&data[index], dev_info.product_id, LLSYNC_MESH_PRODUCT_ID_LEN);

Source: ble_qiot_export.c

type 参数(LLSYNC_MESH_UNNET_ADV_BIT / LLSYNC_MESH_SILENCE_ADV_BIT)直接决定广播类型位,Provisioner 据此区分"待配网设备"与"静默待发现设备"。函数在缓冲区不足(LLSYNC_MESH_DEVICE_UUID_LEN > data_len)时打印错误日志并返回 LLSYNC_MESH_RS_ERR_PARA,防止越界写。

安全认证(AuthValue)

配网认证的核心是 llsync_mesh_auth_clac():输入 16 字节随机数,输出 16 字节 AuthValue。算法分为三步:

  1. 拼接待签名数据:随机数的十六进制字符串 + 产品 ID + 设备名;
  2. HMAC-SHA256 签名:以设备 PSK 为密钥;
  3. 异或折叠:将 32 字节摘要前后 16 字节逐字节异或,压缩为 16 字节 AuthValue。
uint8_t temp_data[LLSYNC_MESH_RAMDOM_DATA_LEN] = {0};
uint8_t sign[HMAC_SHA256_DIGEST_SIZE]       = {0};
uint8_t buf[128]                            = {0};
uint8_t buf_len                             = 0;
uint8_t i                                   = 0;
ble_device_info_t dev_info;

memset(&dev_info, 0, sizeof(ble_device_info_t));
llsync_mesh_dev_info_get(&dev_info);
buf_len += llsync_mesh_hex(random, LLSYNC_MESH_RAMDOM_DATA_LEN, buf);
memcpy(buf + buf_len, dev_info.product_id, LLSYNC_MESH_PRODUCT_ID_LEN);
buf_len += LLSYNC_MESH_PRODUCT_ID_LEN;
memcpy(buf + buf_len, dev_info.device_name, strlen(dev_info.device_name));
buf_len += strlen(dev_info.device_name);

llsync_hmac_sha256(sign, (const uint8_t *)buf, buf_len,
                   (const uint8_t *)dev_info.psk, sizeof(dev_info.psk));

for (i = 0; i < HMAC_SHA256_DIGEST_SIZE / 2; i++) {
    temp_data[i] = sign[i] ^ sign[i + HMAC_SHA256_DIGEST_SIZE / 2];
}

memcpy(auth_data, temp_data, LLSYNC_MESH_RAMDOM_DATA_LEN);

Source: ble_qiot_export.c

设计意图:随机数以十六进制文本(llsync_mesh_hex,每字节转 2 个 ASCII 字符)参与签名,保证挑战值不可预测,防重放;PSK 不出设备、不进广播帧,只有持有相同 PSK 的 Provisioner 才能计算出匹配的 AuthValue,从而完成"设备证明自己持有 PSK"的鉴权。异或折叠将 32 字节摘要压缩为 16 字节,适配 LLSync 短帧载荷并增加破解难度(无法从 AuthValue 反推完整摘要)。签名/摘要算法全部使用 SDK 自带实现(ble_qiot_utils_hmac256.c、ble_qiot_utils_sha256.c),不依赖平台加密硬件,保证可移植性。

广播状态机

配置宏共同定义了两阶段广播策略,对应 LLSync 配网流程:

flowchart TD
    Start([设备上电]) --> Unnet{"已入网?"}
    Unnet -->|"否:未入网"| Adv1["未入网广播 UNNET_ADV<br/>时长 150ms / 间隔 500ms"]
    Adv1 --> Total{"累计超过<br/>UNNET_ADV_TOTAL_TIME<br/>10 分钟?"}
    Total -->|"否"| Adv1
    Total -->|"是"| Silent{"SILENCE_ADV_ENABLE<br/>== 1 ?"}
    Silent -->|"是"| Adv2["静默广播 SILENCE_ADV<br/>间隔 60s,仅被发现"]
    Silent -->|"否"| Stop["停止广播"]
    Adv1 -->|"配网成功"| Net["入网状态<br/>正常组网通信"]
    Adv2 -->|"被 Provisioner 配网"| Net
    Net --> Report["llsync_mesh_vendor_data_report<br/>主动上报 / net_status_inform 通知"]
    Adv2 --> Wait["等待 Provisioner 发现"]
    Wait --> Adv2
  • 未入网广播(UNNET_ADV):单次时长 150ms、间隔 500ms,总时长上限 10 分钟——高占空比保证配网器扫到即连;
  • 静默广播(SILENCE_ADV):超时后进入,间隔拉长到 60s,仅维持最低可发现性以省电;由 LLSYNC_MESH_SILENCE_ADV_ENABLE 控制是否启用(默认 1);
  • 入网后:llsync_mesh_net_status_inform(net_status) 通知 SDK 广播/Flash 写入切换,llsync_mesh_vendor_data_report() 触发厂商数据主动上报。

配网状态与网络配置(如密钥、组网地址)持久化在 Flash 的 LLSYNC_MESH_RECORD_FLASH_ADDR(默认 0xFE000),页大小 4096 字节,保证掉电重启后仍保持入网状态。

核心数据流:厂商数据接收与处理

协议栈收到 Mesh 厂商帧后,最终会调用 llsync_mesh_recv_data_handle()。该函数是数据下发的唯一入口,先做指针校验,再按 Opcode 分发到内核处理函数:

ble_qiot_ret_status_t llsync_mesh_recv_data_handle(uint32_t Opcode, uint8_t *data, uint16_t data_len)
{
    LLSYNC_MESH_POINTER_CHECK(data, LLSYNC_MESH_RS_ERR_PARA);

    switch (Opcode) {
    case LLSYNC_MESH_VND_MODEL_OP_SET: {
        llsync_mesh_vendor_data_set(data, data_len);
        break;
    }

    case LLSYNC_MESH_VND_MODEL_OP_GET: {
        llsync_mesh_vendor_data_get(data, data_len);
        break;
    }

    case LLSYNC_MESH_VND_MODEL_OP_SET_UNACK: {
        llsync_mesh_vendor_data_set_unack(data, data_len);
        break;
    }

    case LLSYNC_MESH_VND_MODEL_OP_CONFIRMATION: {
        llsync_mesh_vendor_op_confirmation(data, data_len);
        break;
    }

    default:
        break;
    }

    return LLSYNC_MESH_RS_OK;
}

Source: ble_qiot_export.c

sequenceDiagram
    participant Stack as BLE 协议栈
    participant Export as llsync_mesh_recv_data_handle<br/>hal/ble_qiot_export.c
    participant Core as SDK 内核<br/>ble_qiot_utils_mesh.c
    participant Tpl as 数据模板<br/>ble_property_t 注册表
    participant App as 应用属性回调

    Stack->>Export: Mesh 接收事件 (Opcode, data, data_len)
    Export->>Export: LLSYNC_MESH_POINTER_CHECK 指针校验
    alt Opcode == VND_MODEL_OP_SET
        Export->>Core: llsync_mesh_vendor_data_set(data, len)
        Core->>Tpl: 按属性 type 匹配 ble_property_t
        Tpl->>App: 调用 set_cb 写入业务状态
    else Opcode == VND_MODEL_OP_GET
        Export->>Core: llsync_mesh_vendor_data_get(data, len)
        Core->>Tpl: 按属性 type 匹配 ble_property_t
        Tpl->>App: 调用 get_cb 读取业务状态
    else Opcode == VND_MODEL_OP_SET_UNACK
        Export->>Core: llsync_mesh_vendor_data_set_unack(data, len)
        Core->>Tpl: 设置属性(无需应答)
    else Opcode == VND_MODEL_OP_CONFIRMATION
        Export->>Core: llsync_mesh_vendor_op_confirmation(data, len)
        Core->>Core: 配网确认处理 / 状态持久化
    end
    Export-->>Stack: 返回 LLSYNC_MESH_RS_OK

流程要点:

  • 未知 Opcode 走 default 分支静默忽略,但函数仍返回 LLSYNC_MESH_RS_OK,避免协议栈因单帧异常终止整个连接——这是"尽力而为"的 Mesh 语义设计。
  • SET(带应答)与 SET_UNACK(不带应答)走同一数据处理路径,区别在于是否要求 Provisioner 应答,适配不同 QoS 场景(如灯光调色可容忍丢帧,不产生重传拥塞)。
  • 属性匹配发生在模板层:内核解析出属性 type 后,遍历 llsync_mesh_property_array_get() 返回的注册表,命中则回调 set_cb/get_cb,业务只关心自己注册的属性。

使用示例

1. 接收 Mesh 下发数据(协议栈事件回调内)

在协议栈的 Mesh 接收回调中调用导出接口,按 Opcode 交给 SDK 处理:

ble_qiot_ret_status_t ret = llsync_mesh_recv_data_handle(opcode, data, data_len);
if (ret != LLSYNC_MESH_RS_OK) {
    // 参数非法(data 为空)等错误处理
}

Source: ble_qiot_export.h

2. 构造配网广播 UUID

Provisioner 扫描阶段,把 llsync_mesh_dev_uuid_get() 的输出填入厂商广播:

uint8_t uuid_buf[LLSYNC_MESH_DEVICE_UUID_LEN];
uint8_t len = llsync_mesh_dev_uuid_get(LLSYNC_MESH_UNNET_ADV_BIT, uuid_buf, sizeof(uuid_buf));
if (len <= 0) {
    // 缓冲区过短等错误处理
}

Source: ble_qiot_export.h

3. 配网认证:计算 AuthValue

收到配网器下发的随机数挑战后,计算并回传 AuthValue:

uint8_t random[LLSYNC_MESH_RAMDOM_DATA_LEN] = {0};   // 来自配网器挑战
uint8_t auth_data[LLSYNC_MESH_RAMDOM_DATA_LEN] = {0};
llsync_mesh_auth_clac(random, auth_data);
// 将 auth_data 回传给 Provisioner 完成认证

Source: ble_qiot_export.h

4. 注册自定义属性

业务按模板约定,把设备能力注册进属性表,SDK 内核即可自动路由 set/get:

static ble_property_t g_property_table[] = {
    { my_onoff_set, my_onoff_get, LLSYNC_MESH_VENDOR_ONOFF_TYPE,
      sizeof(llsync_mesh_onoff) },
    { my_hsl_set,   my_hsl_get,   LLSYNC_MESH_VENDOR_HSL_TYPE,
      sizeof(llsync_mesh_HSL) },
};
// 供 llsync_mesh_property_array_get() 返回

Source: ble_qiot_template.h

配置选项(ble_qiot_mesh_cfg.h)

所有可调参数集中定义在 ble_qiot_mesh_cfg.h,按"持久化 / 广播策略 / 平台"分组:

宏类型默认值说明
LLSYNC_MESH_RECORD_FLASH_ADDR地址0xFE000配网状态/网络配置的 Flash 存储起始地址,需避开固件与文件系统分区
LLSYNC_MESH_RECORD_FLASH_PAGESIZE整数4096Flash 页大小(见芯片数据手册),决定擦写粒度
LLSYNC_MESH_UNNET_ADV_TOTAL_TIME时长(ms)10 * 60 * 1000未入网广播总时长上限,10 分钟后进入静默广播或停止
LLSYNC_MESH_SILENCE_ADV_ENABLE布尔1超时未配网是否进入静默广播(仅用于让 Provisioner 发现设备)
LLSYNC_MESH_UNNET_ADV_DURATION时长(ms)150未入网广播单次广播时长
LLSYNC_MESH_SILENCE_ADV_DURATION时长(ms)150静默广播单次广播时长
LLSYNC_MESH_UNNET_ADV_INTERVAL时长(ms)500未入网广播间隔
LLSYNC_MESH_SILENCE_ADV_INTERVAL时长(ms)60 * 1000静默广播间隔(60s,低占空比省电)
MESH_LOG_PRINT(...)宏printf(...)日志输出口,可重定向到自有日志系统
BLE_QIOT_USER_DEFINE_HEXDUMP布尔0是否由用户自定义 hexdump 打印(配合日志)

Source: ble_qiot_mesh_cfg.h

调优指引:

  • 更快被发现:缩短 LLSYNC_MESH_UNNET_ADV_INTERVAL 或延长 LLSYNC_MESH_UNNET_ADV_DURATION,代价是配网阶段功耗上升;
  • 更省电:延长 LLSYNC_MESH_UNNET_ADV_TOTAL_TIME 会让设备在未配网状态停留更久;配网成功率高的场景可缩短该值更快进入静默态;
  • Flash 分区冲突:LLSYNC_MESH_RECORD_FLASH_ADDR = 0xFE000 与页面大小需与链接脚本、升级区对齐,改址前务必核对内存布局。

API 参考(ble_qiot_export.h)

以下 6 个接口构成 llsync_mesh 的对外契约,由协议栈/应用层调用,hal/ble_qiot_export.c 实现。

ble_qiot_ret_status_t llsync_mesh_recv_data_handle(uint32_t Opcode, uint8_t *data, uint16_t data_len)

处理设备收到的 Mesh 厂商数据。按 Opcode 分发:LLSYNC_MESH_VND_MODEL_OP_SET → vendor set;LLSYNC_MESH_VND_MODEL_OP_GET → vendor get;LLSYNC_MESH_VND_MODEL_OP_SET_UNACK → 无应答 set;LLSYNC_MESH_VND_MODEL_OP_CONFIRMATION → 配网确认。

参数:

  • Opcode (uint32_t):设备厂商 Model 操作码;
  • data (uint8_t*):数据内容;不可为空;
  • data_len (uint16_t):数据长度。

返回:成功返回 LLSYNC_MESH_RS_OK;data 为空返回 LLSYNC_MESH_RS_ERR_PARA。

Source: ble_qiot_export.h

ble_qiot_ret_status_t llsync_mesh_scan_data_get(uint8_t *data, uint8_t *data_len)

获取扫描响应数据(Flags + 完整本地名 + 设备名)。

参数:

  • data (uint8_t*):输出缓冲区;
  • data_len (uint8_t*):输出长度。

返回:成功返回 LLSYNC_MESH_RS_OK;参数为空返回 LLSYNC_MESH_RS_ERR_PARA。

Source: ble_qiot_export.h

int llsync_mesh_dev_uuid_get(uint8_t type, uint8_t *data, uint8_t data_len)

构造设备 UUID(厂商广播载荷)。

参数:

  • type (uint8_t):LLSYNC_MESH_UNNET_ADV_BIT(未入网广播)或 LLSYNC_MESH_SILENCE_ADV_BIT(静默广播);
  • data (uint8_t*):输出缓冲区;
  • data_len (uint8_t):缓冲区容量。

返回:成功返回有效数据长度;data 为空或 data_len 小于 LLSYNC_MESH_DEVICE_UUID_LEN 返回错误码(LLSYNC_MESH_RS_ERR_PARA)。

Source: ble_qiot_export.h

ble_qiot_ret_status_t llsync_mesh_auth_clac(uint8_t *random, uint8_t *auth_data)

配网认证:输入 16 字节随机数,输出 16 字节 AuthValue(HMAC-SHA256 + 异或折叠,见上文算法)。

参数:

  • random (uint8_t*):16 字节随机数(配网器挑战值);
  • auth_data (uint8_t*):输出 16 字节认证值。

返回:成功返回 LLSYNC_MESH_RS_OK;任一参数为空返回 LLSYNC_MESH_RS_ERR_PARA。

Source: ble_qiot_export.h

void llsync_mesh_vendor_data_report(void)

触发设备主动上报厂商数据(如状态变化、定时上报)。无参数、无返回值;内部读取各属性 get_cb 组织上报帧。

Source: ble_qiot_export.h

void llsync_mesh_net_status_inform(uint8_t net_status)

通知 SDK 网络状态变化(入网/退网),SDK 据此切换广播模式或写 Flash 持久化状态。

参数:

  • net_status (uint8_t):网络状态(入网/未入网)。

Source: ble_qiot_export.h

模板查询接口(data_template)

函数说明
uint16_t llsync_mesh_vendor_size_get(void)返回已注册属性数量
ble_property_t *llsync_mesh_property_array_get(void)返回属性注册表数组指针

Source: ble_qiot_template.h

故障模式、边界情况与并发

空指针与参数校验

llsync_mesh_recv_data_handle、llsync_mesh_scan_data_get、llsync_mesh_dev_uuid_get、llsync_mesh_auth_clac 均以 LLSYNC_MESH_POINTER_CHECK(ptr, ret) 宏做入口防御,空指针立即返回 LLSYNC_MESH_RS_ERR_PARA,避免协议栈事件回调传入非法指针导致整机崩溃。dev_uuid_get 还对输出缓冲区长度做了上限检查,并在过短时打印错误日志(ble_qiot_log_e)。

未知 Opcode 的容错

recv_data_handle 对未识别的 Opcode 走 default 分支静默忽略并仍返回 LLSYNC_MESH_RS_OK。这是有意为之:Mesh 广播链路天然存在帧丢失与乱序,把未知帧视为"非本设备可处理"而不是错误,可避免 Provisioner 侧因单帧异常而中断交互。代价是无效帧不会留下任何日志,排查问题时需在协议栈侧另行抓包。

广播缓冲区溢出防护

llsync_mesh_hex 内部将待转换长度钳制在 (sizeof(hexbufs) - 1) / 2(137 字节缓冲),确保任意长度输入都不会写穿栈上缓冲;dev_uuid_get 的构造过程同样受 LLSYNC_MESH_DEVICE_UUID_LEN 约束,若产品 ID 超长会导致错误返回而不是溢出。

认证随机数与重放

AuthValue 依赖配网器下发的随机数,因此协议本身抗重放的前提是随机数每次不同。若平台侧的随机数源质量差(如固定值),认证将形同虚设——移植时务必确保 random 来自真随机源。此外 llsync_mesh_hex 的十六进制文本拼接待签名数据,任何一侧拼接规则不一致(大小写、分隔符)都会导致 HMAC 校验失败,协议两端必须严格一致。

并发与中断上下文

本模块 API 均为同步、无锁实现,没有内部全局状态机上的互斥保护。BLE 协议栈的事件回调(接收、扫描、定时广播)通常运行在中断/任务上下文中,若应用层在多个任务同时调用 llsync_mesh_vendor_data_report() 或读写属性,需由集成方在应用层保证互斥(如关中断或任务锁)。Flash 写入(配网状态持久化)耗时较长,若在低优先级任务中执行可能阻塞扫描/广播时序,建议放到专门的任务或使用芯片的 Flash 管理接口。

性能与运维注意事项

  • 认证计算开销:llsync_mesh_auth_clac 含一次 HMAC-SHA256(对 ≤ 约 64 字节输入),在 AC63 这类 MCU 上耗时在毫秒级且计算期间占用 CPU;配网流程中仅需调用一次,可接受,但不要在广播回调中反复计算。
  • 广播占空比:未入网阶段 150ms/500ms 的广播占空比约 30%,是配网期功耗的主要来源;进入静默广播(60s 间隔)后功耗显著下降。量产前应根据目标功耗与配网成功率调优 cfg 中的时长宏。
  • Flash 寿命:LLSYNC_MESH_RECORD_FLASH_ADDR = 0xFE000、页大小 4096 字节。反复配网/退网会反复擦写该页,需评估 Flash 擦写寿命与产品生命周期匹配;多次配网失败导致的频繁写 Flash 应加节流。
  • 日志:MESH_LOG_PRINT 默认 printf,调试时可临时打开;量产固件建议关闭或降级,避免日志输出挤占广播时序(尤其 UART 阻塞式输出)。
  • 未读源码说明:sdk_src/core/ble_qiot_utils_mesh.c 与 hal/ble_qiot_import.c 的内部实现未在本页逐一展开,其对外行为(vendor set/get/set_unack/confirmation 处理、llsync_mesh_dev_info_get 设备信息适配)已通过 export.c 的调用关系确认;如需深入了解组网状态机细节,请直接阅读这两个文件。

扩展点

  1. 新增厂商属性:在 mesh_vendor_type_e 追加类型(沿用 0xF1xx 或自定义段),定义 packed 负载结构体,并在 ble_property_t 注册表中登记 set_cb/get_cb 与 data_len。SDK 内核自动完成路由,无需改动协议栈。
  2. 平台移植(换芯片/换协议栈):只需实现 hal/ble_qiot_import.{c,h} 侧声明的适配函数(如 llsync_mesh_dev_info_get 返回设备名/产品 ID/PSK),并保证 export.c 依赖的常量(CID、协议版本、广播位)与 Provisioner 端一致。
  3. 自定义安全策略:llsync_mesh_auth_clac 算法可整体替换(如改用 SM4/国密),只要在 export.h 保持同一签名、两端约定一致即可;ble_qiot_utils_sha256/hmac256/base64 为独立实现,亦可替换为硬件加速版本。
  4. 日志与告警接入:重定义 MESH_LOG_PRINT 与 BLE_QIOT_USER_DEFINE_HEXDUMP 可将 SDK 日志接入自有日志系统。

测试与验证建议

  • 单元级:验证 llsync_mesh_hex 的边界(0 长度、超长输入钳制);用固定随机数与 PSK 比对 llsync_mesh_auth_clac 输出与上位机计算结果是否一致(大小端敏感)。
  • 集成级:Provisioner 分别下发 SET / GET / SET_UNACK / CONFIRMATION 四种 Opcode,确认 set_cb/get_cb 被正确调用、未知 Opcode 不影响后续帧;扫描验证 UNNET 与 SILENCE 两种 UUID 广播位。
  • 异常场景:模拟配网超时进入静默广播、Flash 写失败、配网中掉电重启(验证状态从 Flash 恢复)。

Related Links

  • ble_qiot_export.h(公开 API)
  • ble_qiot_export.c(HAL 实现)
  • ble_qiot_mesh_cfg.h(配置项)
  • ble_qiot_template.h(数据模板)
  • ble_qiot_import.h(平台适配接口)
  • ble_qiot_utils_mesh.h(Mesh 内核工具)
  • ble_qiot_common.h(公共定义)
  • 其他第三方集成方案见 9-third-party-integrations 目录下的相邻条目。
Prev
杰理 RCSP 私有协议
Next
Sig Mesh 方案