第三章 MAC层帧结构:上行帧与下行帧

聊到LoRaWAN的MAC层,我第一个想说的就是帧结构。这东西看着像一堆二进制位,其实背后藏着不少设计智慧。你想想看,一个传感器节点可能只有几KB的RAM,电池还得撑好几年,帧结构设计得不够精巧,那根本跑不起来。

3.1 上行帧与下行帧:谁先开口说话?

LoRaWAN里,终端设备永远是主动方。上行帧(Uplink)是节点发给网关的,下行帧(Downlink)是网关回给节点的。这个顺序不能乱——节点不发,网关就不吭声。为什么?省电啊!

我在做智慧农业项目时,有个同事非要在下行链路里主动推送数据,结果节点功耗直接翻了三倍。嗯,后来他老老实实改回了标准流程。

上行帧结构

一个上行帧,说白了就是三部分:

  • 前导码(Preamble):让接收方锁定信号,长度可配,我一般用8个符号
  • PHDR + PHDR_CRC:物理层头,告诉接收方这包数据多长
  • PHYPayload:真正的MAC层数据,里面藏着帧头、帧载荷和MIC

这里有个坑。PHDR_CRC只有2个字节,我曾经在强干扰环境下遇到过误判——物理层头校验通过了,但实际数据是错的。后来我加了一层应用层校验,才算踏实。

下行帧结构

下行帧和上行帧长得差不多,但有个关键区别:

  • 前导码长度固定,不能改
  • RX1和RX2两个接收窗口,时序卡得很死
  • 下行帧里可以带MAC命令,这个后面细说

核心区别:上行帧的PHYPayload最大可以到51字节(DR0),下行帧最大只有51字节(DR0)或更多(取决于区域参数)。别小看这个限制,我见过有人把传感器数据塞到50字节,结果MAC命令没地方放了,整包被网关丢弃。

3.2 帧头格式详解:FHDR里藏着什么?

帧头(FHDR)是MAC层的核心。它只有7到12个字节,但信息量巨大。我拆开给你看:

字段 长度 说明
DevAddr 4字节 终端设备短地址,网络里唯一标识
FCtrl 1字节 控制标志位,比如确认请求、ADR开关
FCnt 2字节 帧计数器,防重放攻击的关键
FOpts 0-15字节 内联MAC命令,不占额外空间

FCtrl这个字节很有意思。它的第5位是ADR(自适应数据速率),第6位是ACK(确认标志)。我调试时经常抓这个位——如果节点一直发但网关不回复ACK,那八成是信号太差或者干扰严重。

个人经验:FCnt用2字节,意味着最大65535。超过这个数怎么办?协议里有个FCnt32扩展机制,但我建议你直接做OTAA重新入网。我在一个长期运行的监测项目里,节点跑了两年多,FCnt溢出过一次,重新入网后一切正常。

3.3 MAC命令帧:看不见的握手

MAC命令是LoRaWAN的隐形通信通道。它不占应用数据的位置,而是藏在FOpts或者帧载荷里。常见的MAC命令有:

  • LinkCheckReq/Ans:节点问网关“你还能听到我吗?”
  • LinkADRReq/Ans:网关告诉节点“你功率太大了,降一降”
  • DutyCycleReq/Ans:限制节点的占空比,防止它霸占信道
  • RXParamSetupReq/Ans:配置接收窗口参数

我曾经踩过一个坑。LinkCheckReq发出去后,网关回复了LinkCheckAns,但我的代码没处理这个应答。结果节点一直以为信号很好,实际上已经快掉线了。嗯,从那以后我养成了习惯——所有MAC命令的应答都得在代码里显式处理。

MAC命令的两种携带方式

MAC命令可以放在两个地方:

  1. FOpts字段:不加密,不占额外开销,但最多15字节
  2. 帧载荷(FRMPayload):加密传输,但会占用应用数据空间

我个人习惯把短命令(比如LinkCheckReq)放FOpts,长命令(比如RXParamSetupReq)放FRMPayload。这样既灵活又高效。

注意:FOpts里的MAC命令不加密,但会被MIC保护。如果有人篡改,接收方会发现MIC校验失败。但如果你对安全性要求极高,建议把敏感命令放到FRMPayload里。

3.4 帧校验与重传机制:丢了怎么办?

无线通信嘛,丢包是常态。LoRaWAN用两种方式保证可靠性:

MIC校验

MIC(消息完整性码)是4个字节,放在帧尾。它用AES-CMAC算法计算,密钥是NwkSKey。接收方收到帧后,重新计算MIC,如果对不上,直接丢弃。

我记得有一次,客户反馈说节点数据经常收不到。我抓包一看,MIC全是错的。查了半天,发现是NwkSKey配置错了——节点用的是AppKey,网关用的是NwkSKey,两个密钥不匹配。这种低级错误,排查起来最费时间。

确认帧与重传

LoRaWAN支持两种模式:

  • 未确认模式(UNCONFIRMED):发完就走,不关心收没收到
  • 确认模式(CONFIRMED):发完后等ACK,超时重传

确认模式的重传逻辑是这样的:

  1. 节点发送上行帧,FCtrl里的ACK位设为1
  2. 网关收到后,在RX1或RX2窗口回复ACK
  3. 节点如果在接收窗口没等到ACK,就重传
  4. 重传次数有限制(默认8次),超过后放弃

避坑指南:我曾经在一个项目中,把重传次数设成了255。结果节点在弱信号区疯狂重传,电池两天就耗光了。后来我改成3次重传,配合ADR自动调整速率,电池撑了两年多。记住:重传不是越多越好,够用就行。

重传的退避策略

LoRaWAN的重传不是立即重发,而是有一个退避机制:

  • 第一次重传:等待一个随机时间(0-2秒)
  • 第二次重传:等待时间加倍
  • 以此类推,直到最大重传次数

这个设计是为了避免多个节点同时重传造成信道拥塞。你想想看,如果100个节点同时丢包,然后同时重传,那信道直接就炸了。随机退避就是解决这个问题的。

好了,帧结构这块就聊到这儿。下一章我们讲MAC层的核心流程——入网和会话管理。那个更有意思,到时候见。