蓝牙 Mesh 网络
AC79 AIoT SDK 基于 SIG Bluetooth Mesh(低功耗蓝牙网状网络)协议栈,为设备提供多对多组网通信能力。本文档介绍该协议栈在 SDK 中的整体架构、节点角色、配网(Provisioning)流程、核心 API、参数配置与故障处理机制,覆盖从应用层到预编译协议栈库(lib_sig_mesh.a)的完整链路。
目的与范围
本文档面向需要基于 AC79 平台开发蓝牙 Mesh 应用的工程师,内容涵盖:
- Mesh 协议栈在 SDK 中的代码组织与模块划分(API 头文件、应用配置、预编译库);
- 节点角色:Provisioner、Node、LPN(低功耗节点)、Friend(好友节点)及其参数语义;
- 配网(Provisioning)与消息收发的核心流程;
- 公共 API、地址模型、错误码与 buffer 工具函数;
- 可配置参数(广播间隔、超时、缓冲等)及默认值;
- 故障模式、并发/资源约束与扩展方式。
以下主题属于兄弟页面,不在本文展开:经典蓝牙(BR/EDR)与音频、普通 BLE GATT 外设/主机应用、Wi-Fi 协议栈。与本文相关的详细 API 用法可参考 include_lib/utils/btmesh/api/ 下各模块头文件。
概述
蓝牙 Mesh(SIG Mesh)是一种基于 BLE 广播与扫描机制的组网协议:节点间通过 ADV(不可连接广播)转发消息,支持中继(Relay)、代理(Proxy)、低功耗(LPN)与好友(Friend)等特性,从而在无中心路由器的条件下构建大规模设备网络。
在 AC79 SDK 中,Mesh 协议栈以预编译静态库 + 公开头文件 + 应用参数配置三种形态交付:
| 形态 | 位置 | 作用 |
|---|---|---|
| 预编译库 | cpu/wl82/liba/lib_sig_mesh.a | 协议栈核心实现(网络层、传输层、加密、模型框架) |
| 公开 API 头 | include_lib/utils/btmesh/api/*.h | 应用可见的接口定义与类型 |
| 内部模块头 | include_lib/utils/btmesh/*.h | 协议栈内部数据结构说明 |
| 应用参数配置 | apps/common/ble/mesh/mesh_config_common.c | 广播、配网、LPN/Friend、beacon 等运行参数 |
| 链接/内存布局 | cpu/wl82/sdk_ld_sdram.c、sdk_ld_sfc.c | Mesh 代码段、RAM 占用统计与库版本符号 |
协议栈代码通过 #pragma 段指令映射到专用的 .ble_mesh_* 链接段(见 sig_mesh_api.c),便于在链接脚本中统计 Mesh 功能的 RAM/Flash 占用,并支持按需裁剪。
架构
下图展示了 Mesh 子系统从应用到控制器的分层结构,以及配置与 API 的依赖关系(节点与连接均取自实际源码中的模块/文件):
flowchart TD
subgraph sg_App["应用层 (apps)"]
App["应用代码 (业务 task / app_main)"]
end
subgraph sg_Config["配置层"]
CfgCommon["mesh_config_common.c<br/>广播/配网/LPN/Friend 参数"]
CfgModel["CONFIG_MESH_MODEL<br/>SIG_MESH_PROVISIONER / NODE"]
end
subgraph sg_API["API 层 (include_lib/utils/btmesh/api)"]
SigApi["sig_mesh_api.h / .c"]
MainApi["mesh_main.h<br/>Provisioning & Profile API"]
AccessApi["access.h 模型访问"]
CfgApi["cfg_cli.h / cfg_srv.h"]
HealthApi["health_cli.h / health_srv.h"]
ProxyApi["proxy.h"]
CdbApi["cdb.h 配置数据库"]
end
subgraph sg_Stack["Mesh 协议栈 (lib_sig_mesh.a)"]
Net["mesh_net.h 网络层"]
Trans["mesh_transport.h 传输层"]
Crypto["mesh_crypto.h 加密"]
Settings["mesh_settings.h 持久化"]
end
subgraph sg_BT["蓝牙控制器 (wl82)"]
HCI["HCI / 链路层"]
end
App -->|"调用"| SigApi
App -->|"编译期选择"| CfgModel
CfgModel --> CfgCommon
CfgCommon -->|"参数生效"| SigApi
SigApi --> MainApi
SigApi --> AccessApi
SigApi --> CfgApi
SigApi --> HealthApi
SigApi --> ProxyApi
SigApi --> CdbApi
MainApi --> Net
Net --> Trans
Trans --> Crypto
Settings -->|"读写配置"| Net
Net -->|"ADV / 扫描 / GATT"| HCI
各层职责说明:
- 应用层:业务代码通过
sig_mesh_api.h聚合暴露的接口完成节点初始化、模型注册与消息收发;不直接触碰协议栈内部结构。 - 配置层:
mesh_config_common.c以全局常量(多数为_WEAK_弱符号)形式定义运行参数,应用可覆盖;CONFIG_MESH_MODEL决定设备编译为 Provisioner 还是普通 Node,并影响默认广播参数。 - API 层:
sig_mesh_api.h是总入口,聚合了 access、mesh_main、proxy、cfg_cli/srv、health_cli/srv、cdb 等子模块头文件;sig_mesh_api.c提供对net_buf_simple的 buffer 封装与 OpCode 构造工具。 - 协议栈层:网络层(
mesh_net)、传输层(mesh_transport)、加密(mesh_crypto)与设置持久化(mesh_settings)被编译进lib_sig_mesh.a,应用只通过 API 头文件交互。 - 控制器层:最终通过 wl82 蓝牙控制器以 ADV/扫描/连接方式承载 Mesh 广播与 GATT Proxy 通道。
协议栈模块与节点角色
API 聚合与子模块
sig_mesh_api.h 作为唯一公共入口,按 #include 聚合以下子模块(见 sig_mesh_api.h):
| 头文件 | 职责 |
|---|---|
api/basic_depend.h | 基础类型与依赖定义 |
api/mesh_config.h | Mesh 编译期配置开关 |
kernel/atomic_h.h | 原子操作 |
api/access.h | 元素(Element)、模型(Model)注册与消息分发 |
api/mesh_main.h | Provisioning 与 Profile 主 API |
api/proxy.h | GATT Proxy 服务(手机 App 通过 GATT 接入 Mesh) |
api/cfg_cli.h / api/cfg_srv.h | 配置客户端 / 服务端模型(Composition Data、AppKey 等) |
api/health_cli.h / api/health_srv.h | 健康检测模型(Health) |
api/cdb.h | 配置数据库(Provisioner 侧记录节点信息) |
这种"单一入口聚合头 + 预编译库"的设计让应用只需包含一个头文件即可获得全部 Mesh 能力,同时把协议栈内部符号隔离在 lib_sig_mesh.a 中,减少应用编译耦合。
节点角色与编译期选择
SDK 通过 CONFIG_MESH_MODEL 宏区分设备角色,并在 mesh_config_common.c 中为不同角色提供不同的默认广播参数(见 mesh_config_common.c):
#if (CONFIG_MESH_MODEL == SIG_MESH_PROVISIONER)
const u16 config_bt_mesh_node_msg_adv_interval = ADV_SCAN_UNIT(15); // unit: ms
const u16 config_bt_mesh_node_msg_adv_duration = 60; // 40unit: ms
const u8 config_bt_mesh_is_provisioner = 1;
#else
const u16 config_bt_mesh_node_msg_adv_interval = ADV_SCAN_UNIT(10); // unit: ms
const u16 config_bt_mesh_node_msg_adv_duration = 100; // unit: ms
const u8 config_bt_mesh_is_provisioner = 0;
#endif
Source: mesh_config_common.c
- Provisioner(配网器):负责发现未配网设备并为其分配单播地址、下发 NetKey/AppKey。其节点消息广播间隔更大(15ms vs 10ms),以让出更多射频时间用于配网流程。
- Node(节点):被配网器纳入网络的普通设备,仅作为消息收发与中继实体。
此外协议栈还支持两种资源受限特性,相关参数在 mesh_config_common.c 中集中定义:
- LPN(Low Power Node,低功耗节点):通过降低扫描占空比省电,依赖 Friend 缓存消息。参数包括
config_bt_mesh_lpn_*系列(自动超时、重试、扫描延迟、Poll 超时、接收延迟、RSSI/接收窗口因子、最小队列长度)。 - Friend(好友节点):为 LPN 代收并缓存消息。参数包括
config_bt_mesh_friend_*系列(可服务 LPN 数量、接收窗口、订阅列表大小、消息队列大小)。
LPN 与 Friend 通过 Friend Request/Offer/Update 等 PDU 建立关系,config_bt_mesh_lpn_powerup_add_sub_list 控制节点上电后是否自动把订阅地址加入列表,config_bt_mesh_lpn_auto_timeout 控制 LPN 自动进入低功耗模式的延时(0 表示不自动进入)。
配网 Bearer 与 OOB 能力
mesh_main.h 定义了配网使用的两种承载方式与输入/输出 OOB 能力(见 mesh_main.h):
typedef enum {
BT_MESH_PROV_ADV = BIT(0),
BT_MESH_PROV_GATT = BIT(1),
} bt_mesh_prov_bearer_t;
typedef enum {
BT_MESH_NO_OUTPUT = 0,
BT_MESH_BLINK = BIT(0),
BT_MESH_BEEP = BIT(1),
BT_MESH_VIBRATE = BIT(2),
BT_MESH_DISPLAY_NUMBER = BIT(3),
BT_MESH_DISPLAY_STRING = BIT(4),
} bt_mesh_output_action_t;
Source: mesh_main.h
BT_MESH_PROV_ADV:基于不可连接广播的配网(PB-ADV),适合设备间直连配网;BT_MESH_PROV_GATT:基于 GATT 连接的配网(PB-GATT),适合手机 App 通过 Proxy 协议配网。
struct bt_mesh_prov 聚合了设备配网时的身份信息(uuid、uri、OOB 信息)以及输出/输入能力回调(output_number、output_string、输入回调等),应用通过实现这些回调完成人机交互确认。
核心流程
配网与配置流程
sequenceDiagram
participant P as Provisioner
participant N as 未配网节点
participant S as Mesh 协议栈 (lib_sig_mesh.a)
participant C as 蓝牙控制器
Note over N,C: 未配网设备广播 Unprovisioned Device Beacon<br/>(config_bt_mesh_unprov_beacon_interval)
N->>C: 周期广播未配网信标
C-->>P: 扫描发现设备 (UUID)
Note over P,N: PB-ADV / PB-GATT 配网协议
P->>N: Invite (attention duration)
N-->>P: Capabilities
P->>N: Start (OOB 选择)
N-->>P: Public Key (ECDH)
P->>N: Provisioning Data (NetKey, IV Index, 单播地址)
Note over P,N: 失败超时由 config_bt_mesh_prov_protocol_timeout 控制<br/>重传由 config_bt_mesh_prov_retransmit_timeout 控制
N-->>P: Complete
Note over P,N: 配置阶段 (Config Client 模型)
P->>N: Config Composition Data Get
N-->>P: 元素与模型清单
P->>N: AppKey Add / Model App Bind
N-->>P: Success
Note over P,N: 应用消息 (Access → Transport → Network)
P->>S: 发送应用消息 (OpCode + payload)
S->>S: mesh_crypto 加密 + mesh_transport 分片
S->>C: 广播或经 GATT Proxy 发送
C-->>N: 中继至目标地址 (单播/组播/虚拟)
配网过程的每个阶段都依赖 mesh_config_common.c 中的定时参数:PB-ADV 广播间隔/时长(config_bt_mesh_pb_adv_interval/duration)、配网 PDU 重传超时(config_bt_mesh_prov_retransmit_timeout)、事务超时(config_bt_mesh_prov_transaction_timeout)、链路关闭超时(config_bt_mesh_prov_link_close_timeout)与协议总超时(config_bt_mesh_prov_protocol_timeout),保证配网过程对射频占用与丢包具有容错能力。
消息发送路径
应用消息从 Access 层出发,经传输层分片/重组与网络层加密,最终由控制器广播。sig_mesh_api.c 中的 buffer 工具负责按大小端序组装网络 PDU(见下文"使用示例")。消息可发往三种目标地址,由 sig_mesh_api.h 的地址宏判定:
#define BT_MESH_ADDR_IS_UNICAST(addr) ((addr) && (addr) < 0x8000)
#define BT_MESH_ADDR_IS_GROUP(addr) ((addr) >= 0xc000 && (addr) <= 0xff00)
#define BT_MESH_ADDR_IS_VIRTUAL(addr) ((addr) >= 0x8000 && (addr) < 0xc000)
Source: sig_mesh_api.h
单播(Unicast)定向到单个元素,组播(Group)面向同一订阅组,虚拟(Virtual)基于 128 位标签地址,为多供应商模型提供更灵活的寻址空间。
配置参数详解
以下参数均定义于 mesh_config_common.c,除标注 _WEAK_ 的符号外均为强定义。ADV_SCAN_UNIT(_ms) 将毫秒换算为广播单元(1 单元 = 0.625ms,即 (_ms) * 8 / 5)。
节点消息广播
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
config_bt_mesh_node_msg_adv_interval | u16 | Provisioner: ADV_SCAN_UNIT(15);Node: ADV_SCAN_UNIT(10) | 节点发送消息时的广播间隔 |
config_bt_mesh_node_msg_adv_duration | u16 | Provisioner: 60;Node: 100 | 消息广播持续时长(单元) |
config_bt_mesh_is_provisioner | u8 | 1 / 0(随角色) | 当前设备是否为配网器 |
Proxy 广播
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
config_bt_mesh_proxy_unprovision_adv_interval | u16 | ADV_SCAN_UNIT(30) | 未配网状态下的 Proxy 可连接广播间隔 |
config_bt_mesh_proxy_pre_node_adv_interval | u16 | Provisioner: ADV_SCAN_UNIT(300);Node: ADV_SCAN_UNIT(10) | 入网前(pre-node)广播间隔 |
config_bt_mesh_proxy_node_adv_interval | u16 | ADV_SCAN_UNIT(300)(_WEAK_) | 入网后节点 Proxy 广播间隔,应用可覆盖 |
LPN(低功耗节点)
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
config_bt_mesh_lpn_auto_timeout | u8 | 0 | 自动进入低功耗模式的延时(秒),0 表示不自动进入 |
config_bt_mesh_lpn_retry_timeout | u8 | 10 | Friend 请求重试超时(秒) |
config_bt_mesh_lpn_scan_latency | int | 10 | 扫描延迟(毫秒) |
config_bt_mesh_lpn_init_poll_timeout | u32 | 300 | 初始 Poll 超时(单位 100ms) |
config_bt_mesh_lpn_powerup_add_sub_list | u8 | 1 | 上电后是否自动添加订阅列表 |
config_bt_mesh_lpn_recv_delay | u8 | 100 | 接收延迟(毫秒,对应 3.6.5.3 Friend Request) |
config_bt_mesh_lpn_poll_timeout | u32 | 200 | 正常 Poll 超时(单位 100ms) |
config_bt_mesh_lpn_rssi_factor | u8 | 0 | RSSI 因子 |
config_bt_mesh_lpn_recv_win_factor | u8 | 0 | 接收窗口因子 |
config_bt_mesh_lpn_min_queue_size | u8 | 1 | Friend 最小队列长度要求 |
Friend(好友节点)
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
config_bt_mesh_friend_lpn_count | u8 | 1 | 可服务的 LPN 数量 |
config_bt_mesh_friend_recv_win | u8 | 250 | Friend 接收窗口(对应 3.6.5.4 Friend Offer) |
config_bt_mesh_friend_sub_list_size | u8 | 2 | 订阅列表大小 |
config_bt_mesh_friend_queue_size | u8 | 2 | 消息队列大小 |
广播缓冲与配网、Beacon
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
config_bt_mesh_adv_buf_count | u8 | 20(_WEAK_) | 广播缓存个数,编译期强制 >= 3,否则 #error |
config_bt_mesh_pb_adv_interval | u16 | ADV_SCAN_UNIT(15) | PB-ADV 配网广播间隔 |
config_bt_mesh_pb_adv_duration | u16 | 60 | PB-ADV 广播时长 |
config_bt_mesh_prov_retransmit_timeout | u32 | 300 | 配网 PDU 重传超时(毫秒) |
config_bt_mesh_prov_transaction_timeout | u8 | 14 | 配网事务超时(秒) |
config_bt_mesh_prov_link_close_timeout | u8 | 3 | 配网链路关闭超时(秒) |
config_bt_mesh_prov_protocol_timeout | u8 | 15 | 配网协议总超时(秒) |
config_bt_mesh_unprov_beacon_interval | u32 | 200 | 未配网信标广播间隔(毫秒) |
config_bt_mesh_secure_beacon_interval | u16 | 10 | 安全信标广播间隔(秒) |
MESH_ADV_BUFFER_COUNT 的编译期检查(#error " MESH_ADV_BUFFER_COUNT must >= 3 ")体现了设计约束:Mesh 依赖广播承载,至少需要 3 个缓存槽位支撑"当前广播 + 排队广播 + 扫描响应"的最小工作集,防止广播饥饿。
使用示例
示例一:按角色配置广播参数
下面的代码展示 SDK 如何根据设备角色(Provisioner / Node)在编译期选择不同的广播参数——这是应用无需改动业务代码即可适配两种角色的关键机制:
#if (CONFIG_MESH_MODEL == SIG_MESH_PROVISIONER)
const u16 config_bt_mesh_node_msg_adv_interval = ADV_SCAN_UNIT(15); // unit: ms
const u16 config_bt_mesh_node_msg_adv_duration = 60; // 40unit: ms
const u8 config_bt_mesh_is_provisioner = 1;
#else
const u16 config_bt_mesh_node_msg_adv_interval = ADV_SCAN_UNIT(10); // unit: ms
const u16 config_bt_mesh_node_msg_adv_duration = 100; // unit: ms
const u8 config_bt_mesh_is_provisioner = 0;
#endif
Source: mesh_config_common.c
示例二:地址类型判定宏
Mesh 应用在收发消息前需要判断目标地址类型,SDK 提供如下位域宏(单播 < 0x8000,虚拟 0x8000~0xBFFF,组播 0xC000~0xFEFF,保留 0xFF00~0xFFFB):
#define BT_MESH_ADDR_IS_UNICAST(addr) ((addr) && (addr) < 0x8000)
#define BT_MESH_ADDR_IS_GROUP(addr) ((addr) >= 0xc000 && (addr) <= 0xff00)
#define BT_MESH_ADDR_IS_VIRTUAL(addr) ((addr) >= 0x8000 && (addr) < 0xc000)
#define BT_MESH_ADDR_IS_RFU(addr) ((addr) >= 0xff00 && (addr) <= 0xfffb)
Source: sig_mesh_api.h
示例三:OpCode 头部构造
协议栈发送消息时需按 SIG 规范将 OpCode 编码为 1/2/3 字节(字节序与 OpCode 长度相关),buffer_head_init 封装了该逻辑:
u32 buffer_head_init(u32 opcode)
{
if (opcode < 0x100) {
/* 1-byte OpCode */
return opcode;
}
if (opcode < 0x10000) {
/* 2-byte OpCode */
return sys_cpu_to_be16(opcode);
}
/* 3-byte OpCode */
u8 b0 = (opcode >> 16) & 0xff;
...
}
Source: sig_mesh_api.c
该函数体现了 Mesh 协议对 OpCode 的分层设计:0x00~0xFF 使用 1 字节 OpCode,0x0100~0xFFFF 使用 2 字节(网络序),更大的厂商自定义 OpCode 使用 3 字节(含 Company ID),以此在短报文(广播信道资源紧张)与地址空间扩展之间取得平衡。
示例四:配网能力枚举
应用通过 struct bt_mesh_prov 声明设备配网能力,其中输出动作枚举如下:
typedef enum {
BT_MESH_NO_OUTPUT = 0,
BT_MESH_BLINK = BIT(0),
BT_MESH_BEEP = BIT(1),
BT_MESH_VIBRATE = BIT(2),
BT_MESH_DISPLAY_NUMBER = BIT(3),
BT_MESH_DISPLAY_STRING = BIT(4),
} bt_mesh_output_action_t;
Source: mesh_main.h
OOB(带外)能力使用位掩码组合,配网器与设备据此协商认证方式(数字/字符串输出、按键/输入等),确保配网过程的安全性。
示例五:buffer 工具封装
sig_mesh_api.c 将 Zephyr 风格 net_buf_simple 的读写操作封装为面向 Mesh 报文的工具函数,统一处理大小端:
u8 *buffer_add_u8_at_tail(void *buf, u8 val)
{
return net_buf_simple_add_u8((struct net_buf_simple *)buf, val);
}
void buffer_add_le16_at_tail(void *buf, u16 val)
{
net_buf_simple_add_le16((struct net_buf_simple *)buf, val);
}
void buffer_add_be32_at_tail(void *buf, u32 val)
{
net_buf_simple_add_be32((struct net_buf_simple *)buf, val);
}
Source: sig_mesh_api.c
Mesh 协议字段混用小端(地址、计数器)与大端(OpCode、部分控制字段),这些封装让上层组装报文时无需关心具体字节序。
API 参考
地址判定宏(sig_mesh_api.h)
| 宏 | 参数 | 返回值 | 语义 |
|---|---|---|---|
BT_MESH_ADDR_IS_UNICAST(addr) | u16 地址 | 布尔 | 单播地址(addr != 0 && addr < 0x8000) |
BT_MESH_ADDR_IS_GROUP(addr) | u16 地址 | 布尔 | 组播地址(0xC000~0xFF00) |
BT_MESH_ADDR_IS_VIRTUAL(addr) | u16 地址 | 布尔 | 虚拟地址哈希(0x8000~0xBFFF) |
BT_MESH_ADDR_IS_RFU(addr) | u16 地址 | 布尔 | 保留地址(0xFF00~0xFFFB) |
buffer 工具函数(sig_mesh_api.c)
| 函数 | 语义 |
|---|---|
u8 *buffer_add_u8_at_tail(void *buf, u8 val) | 尾部追加 1 字节 |
void buffer_add_le16_at_tail(void *buf, u16 val) | 尾部追加小端 2 字节 |
void buffer_add_be16_at_tail(void *buf, u16 val) | 尾部追加大端 2 字节 |
void buffer_add_le32_at_tail(void *buf, u32 val) | 尾部追加小端 4 字节 |
void buffer_add_be32_at_tail(void *buf, u32 val) | 尾部追加大端 4 字节 |
u8 buffer_pull_u8_from_head(void *buf) | 头部取出 1 字节 |
u16 buffer_pull_le16_from_head(void *buf) | 头部取出小端 2 字节 |
u16 buffer_pull_be16_from_head(void *buf) | 头部取出大端 2 字节 |
u32 buffer_pull_le32_from_head(void *buf) | 头部取出小端 4 字节 |
u32 buffer_pull_be32_from_head(void *buf) | 头部取出大端 4 字节 |
void *buffer_memcpy(void *buf, const void *mem, u32 len) | 尾部追加原始内存块 |
void *buffer_memset(struct net_buf_simple *buf, u8 val, u32 len) | 尾部追加填充字节 |
u32 buffer_head_init(u32 opcode) | 将 OpCode 编码为 1/2/3 字节网络序头部 |
错误码约定(sig_mesh_api.h)
协议栈复用类 POSIX 错误码,Mesh 特有扩展包括:
| 错误码 | 值 | 语义 |
|---|---|---|
ENOTSUP | 35 | 不支持的操作/特性 |
EMSGSIZE | 36 | 消息过长(超过传输层可承载尺寸) |
ENOBUFS | 55 | 无可用缓冲区(如广播缓存耗尽) |
ENOTCONN | 57 | 未建立连接(如 Proxy 未连接) |
ETIMEDOUT | 60 | 操作超时(如配网事务超时) |
EALREADY | 69 | 操作已在进行(如重复发起配网) |
ECANCELED | 72 | 操作被取消 |
EBADMSG | 77 | 报文格式非法 |
Provisioning 枚举(mesh_main.h)
bt_mesh_output_action_t:设备可执行输出动作(闪烁/蜂鸣/振动/显示数字/显示字符串),BT_MESH_NO_OUTPUT表示无输出能力;bt_mesh_input_action_t:设备可接受输入动作(按压/旋转/输入数字/输入字符串),BT_MESH_NO_INPUT表示无输入能力;bt_mesh_prov_bearer_t:配网承载(BT_MESH_PROV_ADV/BT_MESH_PROV_GATT,可组合);bt_mesh_prov_oob_info_t:OOB 信息来源描述(URI、二维码、NFC、设备内外标签等),用于配网流程的安全提示;struct bt_mesh_prov:配网属性与回调集合,包含uuid、uri、oob_info、static_val、输出/输入能力与output_number等回调。
故障模式、边界情况与并发
广播缓存耗尽(ENOBUFS)
Mesh 消息依赖广播承载,config_bt_mesh_adv_buf_count(默认 20,弱符号可覆盖)是并发消息的硬上限。当多模型同时发送消息或中继负载突增时,缓存耗尽将返回 ENOBUFS(错误码 55)。设计上:
- 编译期强制
MESH_ADV_BUFFER_COUNT >= 3,低于 3 直接#error,杜绝"广播饥饿"; - 运行时通过增大广播间隔(如 Provisioner 使用 15ms 而非 Node 的 10ms)让出射频时间,降低缓存竞争概率;
- 应用侧应避免在短时间窗口内突发大量消息,必要时按业务优先级排队。
配网超时与重传
配网协议对丢包敏感,SDK 用四层超时保障流程收敛:
config_bt_mesh_prov_retransmit_timeout(300ms)——同一 PDU 的重传间隔;config_bt_mesh_prov_transaction_timeout(14s)——单事务(如 Invite→Capabilities)最大等待;config_bt_mesh_prov_link_close_timeout(3s)——链路关闭确认等待;config_bt_mesh_prov_protocol_timeout(15s)——整个配网协议的最长时限。
超时后协议栈以 ETIMEDOUT 通知应用,应用可重新触发配网。并发配网(多个未配网设备同时入网)受 EALREADY 保护:同一时刻仅允许一个配网事务。
地址边界
地址宏对 addr = 0 的处理(BT_MESH_ADDR_IS_UNICAST 要求 addr 非零)防止将"未分配地址"误判为合法单播地址;BT_MESH_ADDR_IS_RFU 覆盖 0xFF00~0xFFFB 保留段,避免把保留地址当作组播使用。
并发与资源占用
协议栈以专用链接段(.ble_mesh_* / .ble_mesh_joint_*)承载代码与数据,在 sdk_ld_sdram.c / sdk_ld_sfc.c 中统计 BTSTACK_LE_HOST_MESH_RAM_TOTAL(Data+BSS)与 BTSTACK_LE_HOST_MESH_FLASH_TOTAL(Code),并导出 lib_btmesh_version 符号用于版本核验。这意味着:
- Mesh 功能的 RAM/Flash 占用是静态可预算的,利于在资源受限的 wl82 平台规划内存;
- 打开
MESH_RAM_AND_CODE_MAP_DETAIL可将 Mesh 代码/数据进一步拆分为独立段(joint 段),便于按模块裁剪。
性能与运维注意事项
- 广播间隔权衡:较小的
config_bt_mesh_node_msg_adv_interval提升消息时延表现,但增加射频占用与缓存竞争;Provisioner 默认 15ms、Node 默认 10ms 的差异即是对"配网占用"与"普通通信"不同场景的折中。 - LPN 省电:
config_bt_mesh_lpn_scan_latency与config_bt_mesh_lpn_poll_timeout决定 LPN 的休眠深度,直接关系电池寿命;同时必须与 Friend 的config_bt_mesh_friend_recv_win、config_bt_mesh_friend_queue_size匹配,否则消息会因窗口/队列不足而丢失。 - Proxy 广播:入网后 Proxy 节点默认以 300ms 间隔广播可连接事件(
config_bt_mesh_proxy_node_adv_interval为弱符号,可覆盖),该参数影响手机 App 发现节点的速度与功耗。 - 日志分级:
MESH_HCI、MESH_GATT两组日志 tag 均以CONFIG_DEBUG_LIB控制(默认关闭),生产构建可整体关闭以减小代码体积与日志开销。
扩展点
- 弱符号参数覆盖:
config_bt_mesh_adv_buf_count、config_bt_mesh_proxy_node_adv_interval声明为_WEAK_,应用可重新定义同名强符号覆盖默认值,无需改动 SDK 源码。 - 角色编译开关:
CONFIG_MESH_MODEL(SIG_MESH_PROVISIONER/ 其他)在编译期决定设备角色及相应默认参数。 - 配网回调:
struct bt_mesh_prov的output_number/output_string/ 输入回调允许应用接入自定义的人机交互(LED、蜂鸣器、屏幕、按键矩阵)。 - 模型框架:
access.h提供的元素/模型注册机制支持应用自定义模型(含 3 字节厂商 OpCode),配合BT_MESH_ADDR_IS_VIRTUAL虚拟地址可实现多供应商互操作。 - 内存布局:
MESH_RAM_AND_CODE_MAP_DETAIL开启后可对 Mesh 各模块做更细粒度的段映射,为资源裁剪提供入口。
相关链接
- sig_mesh_api.h(公共 API 总入口)
- sig_mesh_api.c(buffer 工具实现)
- mesh_main.h(Provisioning & Profile API)
- mesh_config_common.c(Mesh 运行参数配置)
- sdk_ld_sdram.c(Mesh 内存/Flash 统计与库版本)
- 兄弟页面:经典蓝牙与音频(4.1)、BLE GATT(4.2)、Wi-Fi 联网(4.4)