【蓝牙精品系列文档】【04_blemesh model】01_访问层

Access Layer 访问层

章节定位

Week3 已覆盖Bearer 承载层 → Network 网络层 → Lower Transport 底层传输层 → Upper Transport 上层传输层(应用安全层)

本章从Access Layer开始,向上衔接 Foundation Model 与业务 Model。Access 层是传输层与模型层之间的中间层:不负责加解密(Upper Transport 已解密),也不执行业务逻辑(Model 层负责),核心工作是解析 Access PDU、按地址与 Opcode 路由到正确的 Model 实例

┌─────────────────────────────────────────────────────────┐ │ Model Layer 业务模型层(OnOff、Lightness…) │ ├─────────────────────────────────────────────────────────┤ │ Foundation Model 基础模型层(Config、Health…) │ ├─────────────────────────────────────────────────────────┤ │ Access Layer ◀──── 本章:Opcode 解析、Element/Model 路由│ ├─────────────────────────────────────────────────────────┤ │ Upper Transport AppKey/DevKey 加密、TransMIC │ ├─────────────────────────────────────────────────────────┤ │ Lower Transport 分段重组、Segment ACK │ ├─────────────────────────────────────────────────────────┤ │ Network Layer NetKey、Relay、IV Index │ ├─────────────────────────────────────────────────────────┤ │ Bearer Layer ADV / GATT │ └─────────────────────────────────────────────────────────┘

Access 层一句话

Upper Transport 解密后得到明文 Access PDU;Access 层按DST 地址 + OpcodeElement → Model表,把消息交给对应 Model 的 handler 处理。


一、Access PDU 格式

Access PDU 是 Upper Transport 解密后的应用层可见明文,结构固定:

Access PDU ┌─────────────┬──────────────────────────────────┐ │ Opcode │ Parameters(可选) │ │ 1 ~ 3 字节 │ 0 ~ 多字节,由消息定义决定 │ └─────────────┴──────────────────────────────────┘
字段作用
Opcode消息类型 ID;Access 层据此匹配 Model 的 handler
Parameters载荷;Set 写入 State,Get 通常为空,Status 从 State 读出

Opcode 编码规则

长度取值范围归属进 Access 层?
1 字节0x00~0x7F由 Network PDU 的 CTL 位决定归属:CTL=1 → Transport Control;CTL=0 → Foundation 层 Access 消息CTL=1不进;CTL=0
2 字节0x8000~0xBFFFAccess 层 SIG 消息(业务 Model 全部 + Foundation 大部分),CTL=0
3 字节0xC0xxxxAccess 层厂商自定义:0xC0+ Vendor Opcode + Company ID,CTL=0进(查vnd_models[]

关键区分区分 Control vs Access 的不是 Opcode 长度,是 Network PDU 的 CTL 位。1 字节 Opcode 既可能是 Transport Control(CTL=1,Transport 层自处理),也可能是 Foundation 层 Access 消息(CTL=0,进 Access 层查 Model handler)。Access 层会收到 1/2/3 字节 Opcode 都可能——只有 CTL=1 的 Control 消息才不进 Access。

1 字节 Opcode 的两种归属

1 字节 Opcode 空间(0x00~0x7F)被 SIG分给两类完全不同的消息,靠 CTL 位区分:

第一类:Transport Control(CTL=1)—— 不进 Access 层

源码transport.h:23

#define TRANS_CTL_OP_ACK 0x00 #define TRANS_CTL_OP_FRIEND_POLL 0x01 #define TRANS_CTL_OP_FRIEND_UPDATE 0x02 #define TRANS_CTL_OP_FRIEND_REQ 0x03 #define TRANS_CTL_OP_FRIEND_OFFER 0x04 #define TRANS_CTL_OP_FRIEND_CLEAR 0x05 #define TRANS_CTL_OP_FRIEND_CLEAR_CFM 0x06 #define TRANS_CTL_OP_FRIEND_SUB_ADD 0x07 #define TRANS_CTL_OP_FRIEND_SUB_REM 0x08 #define TRANS_CTL_OP_FRIEND_SUB_CFM 0x09 #define TRANS_CTL_OP_HEARTBEAT 0x0a
Opcode名称方向用途
0x00Segment ACK接收方 → 发送方分段消息确认,携带 BlockAck 位图
0x01Friend PollLPN → Friend低功耗节点轮询 Friend 是否有缓存消息
0x02Friend UpdateFriend → LPN回复 Poll,告知是否有消息 + 当前 IV Index/Flags
0x03Friend RequestLPN → 周边广播找 Friend,携带 ReceiveWindow/QueueSize 等参数
0x04Friend Offer候选 Friend → LPN应答 Request,告知自身缓存能力
0x05Friend Clear新 Friend → 旧 FriendLPN 换 Friend 时,新 Friend 通知旧 Friend 释放
0x06Friend Clear Confirm旧 Friend → 新 Friend确认释放,握手完成
0x07Friend Subscription AddLPN → FriendLPN 让 Friend 帮它监听新增的 Group/Virtual 地址
0x08Friend Subscription RemoveLPN → FriendLPN 让 Friend 取消监听某些地址
0x09Friend Subscription ConfirmFriend → LPN确认订阅列表已更新
0x0AHeartbeat节点 → 周边周期心跳,携带 Active Relay/Proxy/Feature 等位图 + TTL

第二类:Foundation 层 Access 消息(CTL=0)—— 进 Access 层

源码foundation.h:12

#define OP_APP_KEY_ADD BT_MESH_MODEL_OP_1(0x00) #define OP_APP_KEY_UPDATE BT_MESH_MODEL_OP_1(0x01) #define OP_DEV_COMP_DATA_STATUS BT_MESH_MODEL_OP_1(0x02) #define OP_MOD_PUB_SET BT_MESH_MODEL_OP_1(0x03) #define OP_HEALTH_CURRENT_STATUS BT_MESH_MODEL_OP_1(0x04) #define OP_HEALTH_FAULT_STATUS BT_MESH_MODEL_OP_1(0x05) #define OP_HEARTBEAT_PUB_STATUS BT_MESH_MODEL_OP_1(0x06)
Opcode名称方向类型用途
0x00Config AppKey AddClient → Server命令添加 AppKey(最高频 Config 命令,故给短 Opcode)
0x01Config AppKey UpdateClient → Server命令更新 AppKey(Key Refresh 用)
0x02Config Composition Data StatusServer → ClientStatus回复 Composition Data Get,携带完整 Composition
0x03Config Model Publication SetClient → Server命令设置 Model Publish 参数
0x04Health Current StatusServer → ClientStatus上报当前 Health Fault 列表
0x05Health Fault StatusServer → ClientStatus回复 Health Fault Get/Test,携带 Fault 列表
0x06Config Heartbeat Publication StatusServer → ClientStatus回复 Heartbeat Pub Get/Set,携带发布配置

重要观察

  • 这 7 个 1 字节 Access Opcode全部属于 Foundation 层(Config + Health + Heartbeat),业务 Model(Generic/Lighting/Sensor 等)没有任何 1 字节 Opcode——业务消息全部用 2 字节(0x82xx 系列)
  • Foundation 层也不是全部用 1 字节:只有这 7 个高频消息用短 Opcode,其余(Composition Data Get 0x8008、AppKey Delete 0x8000、Beacon Get 0x8009 等)仍用 2 字节
  • 1 字节空间混合了命令和 Status:0x00/0x01/0x03 是命令,0x02/0x04/0x05/0x06 是 Status
三类 Opcode 归属对比(修正版)
维度1 字节(CTL=1)Control1 字节(CTL=0)Foundation2 字节(CTL=0)SIG 业务3 字节(CTL=0)Vendor
归属层Transport 层 ControlAccess 层 Foundation ModelAccess 层 SIG 业务 ModelAccess 层厂商 Model
加密NetKey 加密即可,不加密 AppKeyAppKey/DevKey 加密 + TransMICAppKey/DevKey 加密 + TransMICAppKey/DevKey 加密 + TransMIC
是否进 Access 层不进,Transport 层自己处理,查 Config/Health Server 的op[]进,查业务 Model 的op[]进,查vnd_models[]
谁定义Mesh Profile Spec §3.4.xMesh Profile Spec §5.4(Foundation)Mesh Model Spec(各 Model 章节)厂商自定义(Company ID 区分)
消息类型ACK/Friendship/HeartbeatConfig/Health/Heartbeat Pub 的高频消息OnOff Set、Lightness Get 等业务厂商私有业务
路由方式Transport 层按ctl_opswitch,不经 Model handlerAccess 层find_op()→ Config/Health Server handlerAccess 层find_op()→ 业务 Model handler同左,但查vnd_models[]
例子0x00 ACK、0x01 Friend Poll0x00 AppKey Add、0x02 Comp Data Status0x8202 OnOff Set、0x8008 Comp Data Get0xC0xxxx 厂商私有

源码证据:CTL=1 的 Control 消息在 Transport 层直接按ctl_opswitch 分发,根本不调用bt_mesh_model_recv()

case TRANS_CTL_OP_ACK: /* ... 处理 Segment ACK ... */ break; /* ... */ switch (ctl_op) { case TRANS_CTL_OP_FRIEND_POLL: /* ... */ case TRANS_CTL_OP_FRIEND_REQ: /* ... */ case TRANS_CTL_OP_FRIEND_CLEAR: /* ... */ case TRANS_CTL_OP_FRIEND_CLEAR_CFM: /* ... */ case TRANS_CTL_OP_FRIEND_SUB_ADD: /* ... */ case TRANS_CTL_OP_