3. Mesh协议栈架构:五层结构深度解析

蓝牙Mesh的协议栈,说白了就是一套分层规矩。每一层只管自己的事,层与层之间通过标准接口通信。我刚开始接触时也觉得层数多,但真正动手调过几次之后,你会发现这种设计其实非常巧妙——它让整个系统变得可拆解、可替换。

下面这张图是我自己画的分层结构,建议你先扫一眼,有个整体印象,我们再一层一层往下啃。

蓝牙Mesh协议栈分层架构 Model Layer(模型层) 定义设备行为:Generic OnOff、Light Lightness、Sensor等 Access Layer(接入层) 消息格式定义、Opcode分发、应用密钥管理 Transport Layer(传输层) 分段与重组(SAR)、可靠传输、心跳检测 Network Layer(网络层) 中继转发、网络密钥加密、TTL控制、序列号管理 Bearer Layer(承载层) ADV承载(广播)与GATT承载(代理协议) 数据流向:应用层 → 接入层 → 传输层 → 网络层 → 承载层(发送方向) 接收方向则相反:承载层 → 网络层 → 传输层 → 接入层 → 模型层

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

承载层是协议栈的最底层,它不关心你发的是什么内容,只关心怎么把数据包从天线送出去。蓝牙Mesh定义了两种承载方式:ADV承载和GATT承载。

核心要点:ADV承载是Mesh的主力通信方式,GATT承载主要用于手机App配网和调试。

ADV承载利用蓝牙4.0+的广播通道(37/38/39)来发送Mesh数据包。每个包最大31字节,所以上层数据大了就得拆。GATT承载则是通过标准BLE连接,走代理协议(Proxy Protocol),让不支持广播的设备也能加入Mesh网络。

我遇到过不少新手问:为什么我的设备连不上Mesh?查了半天发现是广播间隔配得太密,导致信道冲突。嗯,这里要注意——广播间隔建议在20ms到10.24s之间,别为了追求实时性把间隔压到10ms以下,否则丢包率会飙升。

承载类型 传输方式 典型场景 最大包长
ADV承载 BLE广播 Mesh节点间通信 31字节
GATT承载 BLE连接 手机配网、网关桥接 由MTU决定

3.2 Network Layer(网络层)—— 数据怎么传得远

网络层是Mesh的「交通枢纽」。它负责三件事:中继转发、网络密钥加密、TTL控制。

中继转发是Mesh能覆盖大范围的关键。一个节点收到网络层包后,检查TTL是否大于1,如果是,就把TTL减1,然后重新广播出去。你想想看,一个包经过多次中继,就能从客厅传到车库。

网络层使用Network Key(NetKey)对整个包进行加密和认证。每个Mesh网络至少有一个NetKey,不同子网可以用不同的NetKey隔离。我曾经在一个项目中,把灯光子网和传感器子网用了不同的NetKey,结果网关配置错了,灯光死活收不到传感器数据——排查了两天才发现是NetKey没对上。

个人经验:调试阶段建议先用同一个NetKey,等所有节点通信正常了,再按子网拆分密钥。否则你根本分不清是密钥问题还是路由问题。

序列号(SEQ)也是网络层管的。每个节点发送的每条消息都有一个递增的序列号,接收方用它来去重。如果序列号回绕了怎么办?嗯,Mesh协议规定序列号是24位,大约1600万条消息后才回绕,实际项目中基本不用担心。

3.3 Transport Layer(传输层)—— 数据怎么保证完整

传输层是承上启下的关键一层。它把上层的大消息拆成小段(分段),接收方再拼回去(重组)。这个机制叫SAR(Segmentation and Reassembly)。

为什么需要分段?因为网络层单包最大只有31字节,而一个Access消息可能长达380字节。传输层会把消息切成最多32个分段,每个分段加上传输层头,再交给网络层。

可靠传输也是传输层的职责。它使用两种机制:

  • 应答消息(Acknowledged Message):发送方等接收方回ACK,超时没收到就重传。重传次数默认3次,我建议在干扰大的环境中调到5次。
  • 非应答消息(Unacknowledged Message):发完就不管了,适合周期性广播数据,比如传感器上报。

我记得有一次做智能照明项目,灯控指令偶尔会丢失。抓包发现是传输层重传间隔设得太短(只有100ms),导致网络拥塞。后来把重传间隔调到400ms,问题就解决了。你想想看,Mesh网络里节点多了,每个节点都在转发,信道竞争很激烈,重传太快反而适得其反。

避坑指南:我曾经在密集部署场景(50+节点)中,把传输层重传次数设为7次,结果网络瞬间崩溃——大量重传包把信道占满了。建议重传次数不要超过5次,重传间隔不要低于200ms。

3.4 Access Layer(接入层)—— 数据怎么被理解

接入层是应用层和传输层之间的「翻译官」。它定义了两件事:消息格式和Opcode分发。

每个Access消息都有一个操作码(Opcode),用来标识这个消息是干什么的。比如0x8201表示Generic OnOff Get,0x8202表示Generic OnOff Set。接收方根据Opcode把消息分发给对应的Model处理。

接入层还负责应用密钥(AppKey)的管理。同一个Mesh网络里,不同的Model可以用不同的AppKey。比如灯光Model用一个AppKey,门锁Model用另一个,这样即使灯光被破解,门锁还是安全的。

Opcode范围 用途 示例
0x0000 - 0x3FFF 标准SIG Model Generic OnOff Set (0x8202)
0x4000 - 0xBFFF 供应商自定义 厂商私有指令
0xC000 - 0xFFFF 保留 协议内部使用

3.5 Model Layer(模型层)—— 数据怎么变成行为

模型层是离开发者最近的一层。它定义了设备的行为和状态。蓝牙Mesh SIG已经定义了几十种标准Model,覆盖了开关、调光、传感器、配置等常见场景。

每个Model可以包含一个或多个状态(State),以及操作这些状态的消息。比如Generic OnOff Server Model有一个布尔状态(On/Off),收到Set消息就改变状态,收到Get消息就回复状态。

Model之间还可以通过绑定(Binding)实现联动。比如把Sensor Model的输出绑定到Light Lightness Model的输入,传感器检测到人经过,灯就自动亮。我在做智能办公项目时就用过这个机制,省掉了网关的参与,响应速度比云端方案快了一个数量级。

关键概念:Model分为Server Model(提供服务)和Client Model(发起请求)。一个节点可以同时包含Server和Client,比如开关既是Client(发指令)又是Server(上报状态)。

3.6 各层协作:一条消息的完整旅程

说了这么多层,它们到底怎么配合?我拿一个最简单的例子:手机App把灯打开。

  1. Model Layer:App端的Generic OnOff Client Model构造一个Set消息,目标状态为On。
  2. Access Layer:加上Opcode(0x8202)和AppKey,打包成Access消息。
  3. Transport Layer:检查消息长度,如果超过15字节就分段。这里Set消息很短,不分段,直接加传输层头。
  4. Network Layer:加上NetKey加密、TTL(默认127)、序列号,打包成Network PDU。
  5. Bearer Layer:通过ADV承载广播出去。
  6. 中继节点:收到后,Network Layer检查TTL,减1后重新广播。
  7. 目标灯节点:Network Layer解密,Transport Layer重组(如果有分段),Access Layer根据Opcode分发给Generic OnOff Server Model。
  8. Model Layer:Server Model把状态改为On,驱动硬件点亮灯泡。

整个过程从App点击到灯亮,在优化好的Mesh网络里可以做到50ms以内。我实测过,如果中继跳数超过5跳,延迟会增加到150ms左右,但人眼基本感觉不到。

调试技巧:如果你用nRF52840 + Zephyr开发,可以在prj.conf里打开CONFIG_BT_MESH_DEBUG_NET=y和CONFIG_BT_MESH_DEBUG_TRANS=y,这样串口会打印每一层的收发包细节,排查问题非常方便。

好了,五层结构就讲到这里。你可能会问:这么多层,每层都加头,会不会太浪费?其实每层的头都很精简,Network Layer头只有9字节,Transport Layer头最少1字节。整个协议栈的开销控制在20字节以内,对于31字节的广播包来说,效率已经很高了。