4. LoRaWAN消息类型与帧结构:MAC层帧格式

好,咱们今天聊点硬核的——LoRaWAN的MAC层帧结构。说实话,我刚入行那会儿,看协议栈文档看得头大,一堆字段、标志位,感觉像在读天书。后来做认证测试,踩了不少坑,才慢慢摸清楚门道。今天我就把这些经验掰开揉碎,跟你讲讲。

4.1 帧结构总览:从MHDR到FRMPayload

一个完整的LoRaWAN MAC帧,说白了就是一段精心打包的数据。它长这样:

| MHDR (1字节) | MACPayload (可变) | MIC (4字节) |

嗯,就这么简单?别急,咱们一层层剥开看。

MHDR(MAC Header)——帧头,1个字节。它告诉接收方:这条消息是什么类型,用的是哪个版本协议。我习惯把它看成快递单上的“加急”或“普通”标签。

MACPayload——这才是真正的“货”。它里面又套了一层:

| FHDR (7~22字节) | FPort (1字节,可选) | FRMPayload (可变) |

MIC(Message Integrity Code)——4字节的消息完整性校验码。说白了就是防篡改的。我在项目里见过有人把MIC算错,结果服务器死活不认,折腾了一整天。

4.2 FHDR:帧头里的门道

FHDR是MACPayload的头部,结构如下:

| DevAddr (4字节) | FCtrl (1字节) | FCnt (2字节) | FOpts (0~15字节) |

DevAddr——设备地址。每个节点在入网后都会分配一个32位的地址。注意,这不是全球唯一的,只是网络内唯一。我遇到过有人把DevAddr和DevEUI搞混,结果测试一直不过。

FCtrl——控制字节。这个字段很有意思,它里面藏了好几个标志位:

  • ADR(Adaptive Data Rate):自适应速率开关。开了之后网络可以帮你调速率,省电。我建议在固定场景下关掉它,不然调试时数据速率忽高忽低,你根本搞不清问题出在哪。
  • ACK:确认标志。收到Confirmed消息后,这个位要置1。
  • FPending:这个咱们后面细说,是个坑。
  • FOptsLen:FOpts字段的长度,单位字节。

FCnt——帧计数器。每个方向(上行/下行)各有一个16位的计数器。从0开始,每发一帧加1。为什么要有这个?防重放攻击。你想想看,如果没有计数器,坏人截获了一条“开锁”指令,反复重放怎么办?

FOpts——可选字段,最多15字节。用来携带MAC命令。注意,如果FOptsLen不为0,那FPort就不能是0。这个规则我当年背了好久才记住。

4.3 FPort与FRMPayload:数据怎么装

FPort——端口号,1字节。取值范围:

  • 0:表示FRMPayload里只有MAC命令,没有应用数据。
  • 1~223:应用数据端口。具体用哪个,看你的应用层协议怎么定。
  • 224~255:保留给将来扩展用。我建议你别碰,除非你知道自己在干什么。

FRMPayload——真正的数据载荷。如果FPort=0,这里就是MAC命令列表。如果FPort非0,这里就是加密后的应用数据。

加密用的是AES-128,密钥是网络会话密钥(NwkSKey)或应用会话密钥(AppSKey)。具体用哪个,取决于FPort的值。嗯,这里有个细节:上行数据用AppSKey加密,下行数据用NwkSKey加密。为什么不一样?为了安全隔离。网络层和应用层的密钥分开,就算网络被攻破,应用数据还是安全的。

4.4 Confirmed与Unconfirmed消息

这两种消息的区别,说白了就是一个要回执,一个不要。

Unconfirmed消息:发出去就不管了。适合传感器上报温度、湿度这种非关键数据。丢了就丢了,下次再报。优点是省电、省带宽。

Confirmed消息:发出去后,接收方必须回复一个ACK。如果没收到ACK,发送方会在下一个接收窗口重传。适合控制指令,比如“打开阀门”、“关闭电源”。

我在做智能路灯项目时,就吃过亏。当时用Unconfirmed消息发控制指令,结果路灯时亮时不亮。排查了半天,发现是丢包了。后来改成Confirmed消息,问题解决。但代价是电池消耗快了30%。所以,选哪种,得权衡。

我的建议:数据上报用Unconfirmed,控制指令用Confirmed。别为了省电把关键指令也设成Unconfirmed,否则你会后悔的。

4.5 FOpts与FPending机制

FOpts——这个字段我前面提过,用来捎带MAC命令。为什么要有它?因为LoRaWAN的带宽太珍贵了。你想想看,一个上行帧可能只有几十字节,如果专门发一条MAC命令,太浪费了。所以协议设计者搞了个“捎带”机制:在数据帧里顺带把MAC命令塞进去。

FOpts最多15字节,可以塞多个MAC命令。每个命令格式是:

| CID (1字节) | 参数 (可变) |

CID是命令标识符,比如LinkCheckReq的CID是0x02,LinkADRReq的CID是0x03。

FPending——这个标志位只在下行帧里出现。它告诉节点:服务器还有数据要发,你赶紧再开一个接收窗口。

为什么会这样?因为LoRaWAN的接收窗口是有限的。节点发完数据后,会打开两个接收窗口(RX1和RX2)。如果服务器数据太多,一个窗口发不完,就会把FPending置1。节点收到后,会继续打开额外的接收窗口,直到FPending被清零。

注意:FPending机制在Class A设备里用得比较多。Class C设备因为一直在监听,基本用不上。我曾经在Class C设备里错误地处理了FPending,导致节点不断开窗口,功耗飙升。嗯,这个坑我踩过,你别再踩了。

4.6 实战中的常见问题

说了这么多理论,咱们来点实际的。我在认证测试中遇到过几个典型问题:

  1. MIC计算错误:最常见的问题。原因往往是密钥搞错了,或者计算时包含了错误的字段。记住,MIC计算范围是MHDR+MACPayload,不包括MIC本身。
  2. FCnt回绕:16位计数器,最大65535。到了之后会回绕到0。但服务器端如果没处理好,会认为是重放攻击。解决办法是使用32位扩展计数器(FCnt32),或者服务器端做特殊处理。
  3. FOpts长度超限:FOpts最多15字节。如果你塞了16字节,接收方会直接丢弃。我见过有人把MAC命令和FRMPayload都塞满了,结果帧长度超了,发不出去。
  4. FPort=0时FRMPayload非空:FPort=0表示FRMPayload里只能有MAC命令。如果你放了应用数据,接收方会解析失败。
避坑指南:我曾经在调试一个网关时,发现下行数据总是发不下去。查了两天,最后发现是FPending标志位设置错了。服务器明明还有数据要发,但FPending没置1,节点收到一条就关了接收窗口。从那以后,我每次调试都会先抓包看FCtrl字段,确认每个标志位都正确。

4.7 小结

好了,这一章的内容就这些。咱们回顾一下重点:

  • LoRaWAN帧结构:MHDR + MACPayload + MIC
  • FHDR里藏着DevAddr、FCtrl、FCnt、FOpts
  • Confirmed消息要ACK,Unconfirmed不要
  • FOpts用来捎带MAC命令,FPending告诉节点“还有数据”

下一章咱们会讲MAC命令的具体实现,包括LinkCheck、LinkADR这些常用的。到时候我会拿实际项目里的抓包数据给你看,保证比干讲有意思。

记住,协议这东西,光看文档是不够的。你得动手抓包、分析、调试,才能真正理解。我当年也是这么过来的,踩的坑多了,自然就熟了。