3. Mesh协议栈架构:七层模型深度拆解
蓝牙Mesh的协议栈,说白了就是一套分层通信规则。我刚开始接触时也觉得层数太多,但实际调过几个项目后才发现——每一层都有它存在的理由。今天咱们就把这七层挨个捋一遍。
核心观点:Mesh协议栈从下到上共7层,每层只干一件事,层与层之间通过固定接口通信。这种分层设计让开发变得模块化,你改上层逻辑时不用动底层驱动。
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灯泡。
- 模型层:App的Generic OnOff Client生成"开灯"消息
- 接入层:添加操作码0x8201(Generic OnOff Set),打包成Access PDU
- 上层传输层:用AppKey加密,添加传输层头部
- 底层传输层:检查消息长度,如果超过15字节则分段
- 网络层:添加网络头部(IVI、NID、TTL、SEQ等),用NetKey加密
- 承载层:通过ADV广播或GATT连接发送出去
灯泡收到后,从下往上逐层解封装,最终模型层执行开灯动作。整个过程在几十毫秒内完成。
总结一下:Mesh协议栈的七层设计,每一层都有明确的职责边界。理解了这个分层模型,你就能快速定位问题——消息发不出去查承载层,消息到不了查网络层,消息解密失败查传输层,业务逻辑不对查模型层。