3. Mesh协议栈架构:七层模型深度拆解

蓝牙Mesh的协议栈,说白了就是一套分层通信规则。我刚开始接触时也觉得层数太多,但实际调过几个项目后才发现——每一层都有它存在的理由。今天咱们就把这七层挨个捋一遍。

核心观点:Mesh协议栈从下到上共7层,每层只干一件事,层与层之间通过固定接口通信。这种分层设计让开发变得模块化,你改上层逻辑时不用动底层驱动。

蓝牙Mesh协议栈七层架构 承载层(Bearer Layer) 网络层(Network Layer) 底层传输层(Lower Transport Layer) 上层传输层(Upper Transport Layer) 接入层(Access Layer) 基础模型层(Foundation Model Layer) 模型层(Model Layer) 数据从上往下封装,从下往上解封装。每一层只处理自己的头部信息。

3.1 承载层(Bearer Layer)—— 数据怎么发出去

承载层是协议栈的最底层,它决定了数据通过什么物理通道传输。Mesh规范定义了两种承载方式:ADV承载和GATT承载。

承载类型 传输方式 典型场景 我个人的看法
ADV承载 BLE广播信道(37/38/39) 节点间直接通信、中继转发 主力承载,Mesh的核心通信方式
GATT承载 BLE连接 + Proxy协议 手机App与Mesh网络交互 主要用于配网和调试

我在项目中遇到过一个问题:ADV承载的广播包最大只有31字节,去掉Mesh头部后,实际有效载荷非常有限。所以设计消息时一定要控制长度,否则就得走分段传输。

实战建议:如果你在做网关类产品,GATT承载是必须支持的。手机通过Proxy协议接入Mesh网络时,底层走的就是GATT承载。我习惯在固件里同时开启两种承载,用状态机切换。

3.2 网络层(Network Layer)—— 消息怎么路由

网络层负责消息的中继和转发。它处理三个关键动作:消息解密、中继判断、重新加密转发。

你想想看,一个消息从节点A发到节点Z,中间可能经过5、6次中继。网络层每跳都要做一次加解密操作,这就是为什么Mesh对CPU性能有要求。

网络层的核心数据结构是网络PDU,格式如下:

网络PDU格式(从高位到低位):
| IVI(1bit) | NID(7bit) | CTL(1bit) | TTL(7bit) | SEQ(24bit) | SRC(16bit) | DST(16bit) | TransportPDU(可变) | NetMIC(32/64bit) |

这里有个坑——TTL字段。我曾经在调试一个大型网络时,发现消息总是到不了远端节点。查了半天,原来是TTL设成了默认值1,只跳一次就丢了。后来我改成3,问题解决。

注意:TTL值不是越大越好。每跳一次就多一次延迟,在密集网络中建议TTL设为3~5,既能覆盖范围又不至于广播风暴。

3.3 底层传输层(Lower Transport Layer)—— 大消息怎么切

底层传输层干的事很纯粹:分段与重组。当上层消息超过15字节时,底层传输层会把它切成多个Segment,每个Segment独立发送,接收端再拼回去。

分段消息的格式长这样:

分段消息Segment:
| SEG(1bit) | AKF(1bit) | AID(6bit) | SeqZero(13bit) | SegO(5bit) | SegN(5bit) | SegmentData(12-15字节) |

我记得第一次做OTA升级时,固件包有几十KB,底层传输层自动切成几百个Segment。接收端如果丢了一个Segment,整个消息就得重传。所以后来我加了一个选择性重传机制,只重传丢失的Segment,效率提升明显。

消息类型 最大长度 分段方式
非分段消息 15字节 单包发送
分段消息 理论无上限 每段12~15字节,最多64段

3.4 上层传输层(Upper Transport Layer)—— 消息怎么加密

上层传输层负责应用层加密和传输控制。它给消息加上传输层头部,然后用应用密钥(AppKey)加密。

这里有个关键概念——传输层PDU分为两种:

  • Access消息:携带应用数据,用AppKey加密
  • Control消息:携带传输控制信息,用设备密钥(DevKey)加密

为什么要分两层传输?我个人理解是:底层传输层管"怎么传",上层传输层管"传什么"。职责分离后,底层可以专注做分段和重传,上层可以专注做加密和认证。

关键点:上层传输层会计算一个传输层MIC(消息完整性校验码),用于接收端验证消息是否被篡改。MIC长度可以是4字节或8字节,安全等级要求高的场景建议用8字节。

3.5 接入层(Access Layer)—— 应用数据怎么格式化

接入层是应用层和传输层之间的桥梁。它负责:

  • 把应用数据打包成Access PDU
  • 添加操作码(Opcode)标识消息类型
  • 校验消息格式是否正确

Access PDU的结构很简单:

Access PDU:
| Opcode(1~3字节) | Parameters(0~379字节) |

操作码的设计很有意思。1字节操作码用于标准模型,2字节用于厂商自定义模型,3字节用于特殊场景。我习惯在项目初期就把操作码规划好,避免后期冲突。

3.6 基础模型层(Foundation Model Layer)—— 网络怎么管理

基础模型层是Mesh协议栈的"管理层"。它定义了5个标准模型,用于网络配置和管理:

模型名称 模型ID 功能
Configuration Server 0x0000 节点配置管理
Configuration Client 0x0001 发起配置请求
Health Server 0x0002 节点健康状态上报
Health Client 0x0003 查询健康状态
Remote Provisioning Server 0x0004 远程配网(Mesh 1.1新增)

基础模型层是每个节点都必须实现的。没有它,节点就无法加入网络、无法被配置。我曾经在一个项目中跳过了Health Server的实现,结果现场调试时完全不知道节点状态,后来老老实实补上了。

3.7 模型层(Model Layer)—— 业务逻辑怎么实现

模型层是协议栈的最顶层,也是开发者最常打交道的一层。它定义了具体的业务行为,比如开关灯、调色温、读传感器等。

模型分为三类:

  • Server Model:提供服务,响应请求
  • Client Model:发起请求,接收响应
  • Control Model:既有Server又有Client,用于复杂控制

举个例子,一个智能灯泡通常包含:

  • Generic OnOff Server(开关服务)
  • Light Lightness Server(亮度服务)
  • Light CTL Server(色温服务)

而手机App则实现对应的Client模型,发送开关、调光、调色温等命令。

我的经验:模型层设计时,尽量复用SIG定义的标准模型。标准模型兼容性好,不同厂家的设备可以互操作。只有标准模型满足不了需求时,才去定义Vendor Model。我见过一个项目用了20多个Vendor Model,结果联调时各种不兼容,教训深刻。

3.8 层间交互流程——一个消息的完整旅程

说了这么多层,咱们看一个实际例子:手机App发送"开灯"命令到Mesh灯泡。

  1. 模型层:App的Generic OnOff Client生成"开灯"消息
  2. 接入层:添加操作码0x8201(Generic OnOff Set),打包成Access PDU
  3. 上层传输层:用AppKey加密,添加传输层头部
  4. 底层传输层:检查消息长度,如果超过15字节则分段
  5. 网络层:添加网络头部(IVI、NID、TTL、SEQ等),用NetKey加密
  6. 承载层:通过ADV广播或GATT连接发送出去

灯泡收到后,从下往上逐层解封装,最终模型层执行开灯动作。整个过程在几十毫秒内完成。

总结一下:Mesh协议栈的七层设计,每一层都有明确的职责边界。理解了这个分层模型,你就能快速定位问题——消息发不出去查承载层,消息到不了查网络层,消息解密失败查传输层,业务逻辑不对查模型层。