4、LoRaWAN协议栈:MAC层帧结构、Class A/B/C 设备类型、Join流程(OTAA/ABP)
好,咱们今天聊聊LoRaWAN协议栈里最核心的几个东西。说实话,很多刚入门的同学一看到协议栈就头大,觉得这玩意儿太抽象。其实你把它拆开看,无非就是三件事:数据怎么打包(帧结构)、设备怎么工作(Class类型)、怎么入网(Join流程)。搞懂了这三块,LoRaWAN就算入门了。
4.1 MAC层帧结构:数据是怎么装进信封的?
LoRaWAN的MAC层帧结构,说白了就是给数据包套了个信封。这个信封有固定的格式,网关和终端都得按这个规矩来解析。我刚开始做项目时,有一次终端死活连不上服务器,查了半天,发现是帧头里的版本号写错了——这种低级错误,嗯,犯过一次就再也不会忘了。
一个标准的LoRaWAN上行帧长这样:
| 前导码 | PHDR | PHDR_CRC | PHYPayload | CRC |
咱们重点关注的是PHYPayload,它里面又分三层:
- MAC头(MHDR):1字节,告诉你这是哪类消息(Join请求、数据上行、数据下行等)
- MAC载荷(MACPayload):真正的数据区,包含帧头(FHDR)、端口(FPort)和加密后的数据(FRMPayload)
- 消息完整性校验码(MIC):4字节,用来验证消息有没有被篡改
这里有个细节——FHDR里包含的设备地址(DevAddr)、帧计数器(FCnt)和控制位(FCtrl)。帧计数器特别重要,它是个递增的数值,用来防止重放攻击。我在项目里遇到过一个问题:设备重启后帧计数器归零了,服务器那边还记着旧值,结果所有上行数据都被丢弃了。后来我养成了习惯,每次设备OTA升级后,都会在服务器端手动重置一下帧计数器。
重点提醒:LoRaWAN的帧结构里,数据载荷(FRMPayload)是加密的,但帧头(FHDR)是明文传输的。这意味着设备地址、帧计数器这些信息是暴露在空中的。所以,千万别在帧头里放敏感信息。
4.2 Class A/B/C 设备类型:三种不同的“作息时间”
LoRaWAN定义了三种设备类型,区别在于它们什么时候“听”服务器说话。你想想看,这就像三个人用对讲机:一个人随时在听(Class C),一个人只在特定时间听(Class B),还有一个人只在说完话后听一会儿(Class A)。
Class A:最省电,但最被动
Class A是所有LoRaWAN设备都必须支持的基础类型。它的工作流程是:
- 设备主动发送上行数据
- 发送结束后,打开两个短暂的接收窗口(RX1和RX2)
- 如果服务器有下行数据,就在这两个窗口里发下来
- 窗口关闭后,设备进入休眠,直到下一次上行
说白了,Class A设备就像个“发完就跑”的家伙。服务器想给它发消息?对不起,得等它下次主动找你。这种模式最适合电池供电的传感器——比如水表、气表,一天上报几次数据就够了。
我的经验:Class A设备的RX1和RX2窗口时间是可以配置的。RX1通常在上行结束后1秒打开,RX2在2秒后打开。如果你想让设备更快收到下行确认,可以把RX1的延迟调短一点。但别太短,网关处理也需要时间。
Class B:定时“收听”
Class B在Class A的基础上,增加了一个定时接收窗口。设备会定期打开接收窗口,不管它有没有发过上行的数据。这个“定期”是通过网关发送的信标(Beacon)来同步的。
Class B适合那些需要服务器主动下发指令的场景,比如远程控制阀门开关。但代价是功耗比Class A高——毕竟要定时醒来听信标。我在一个智能灌溉项目里用过Class B,设备每10分钟醒来一次接收指令,电池大概能撑半年。
Class C:随时在线
Class C设备几乎一直在接收状态,只有在发送数据时才短暂关闭接收。这种模式功耗最高,但延迟最低——服务器随时可以下发数据。
Class C适合那些需要实时控制的设备,比如电动阀门、报警器。但说实话,在电池供电的场景里很少用Class C,一般都是外接电源的设备才会这么干。
| 类型 | 接收方式 | 功耗 | 下行延迟 | 典型场景 |
|---|---|---|---|---|
| Class A | 上行后打开两个窗口 | 极低 | 高(需等待上行) | 传感器、水表 |
| Class B | 定时打开接收窗口 | 中等 | 中(取决于信标周期) | 远程控制、阀门 |
| Class C | 持续接收 | 高 | 低(实时) | 报警器、执行器 |
4.3 Join流程:设备怎么“注册”进网络?
设备要加入LoRaWAN网络,得先完成一个“注册”流程。这个流程有两种方式:OTAA和ABP。我刚开始做LoRa项目时,一直搞不清这两个的区别,后来在项目里各踩了一次坑,才算真正弄明白。
OTAA:空中激活(推荐方式)
OTAA流程就像你去办一张新的SIM卡——你得提供身份信息(DevEUI和AppKey),网络侧审核通过后,给你分配一个临时号码(DevAddr)和会话密钥(NwkSKey、AppSKey)。
具体流程:
- 设备发送Join Request消息,包含DevEUI(设备唯一标识)、AppEUI(应用标识)和DevNonce(随机数)
- 网络服务器验证DevNonce是否重复(防止重放攻击)
- 验证通过后,服务器回复Join Accept消息,包含DevAddr、NwkSKey、AppSKey等
- 设备用预置的AppKey解密Join Accept,拿到会话密钥
- 从此以后,设备就用这些密钥加密通信
避坑指南:我曾经在一个项目里,设备每次重启都重新执行OTAA流程。结果服务器端发现DevNonce重复了,直接拒绝了Join请求。后来我才知道,DevNonce必须是一个递增的随机数,不能重复使用。解决办法是在设备里保存一个递增的计数器,每次重启后从上次的值继续。
ABP:预配置激活(简单但风险高)
ABP就简单多了——设备出厂时就把DevAddr、NwkSKey、AppSKey写死在固件里。设备上电后直接发数据,不需要走Join流程。
ABP的好处是速度快、省电。但坏处也很明显:
- 密钥是固定的,一旦泄露,整个网络都不安全
- 设备换到另一个网络时,得重新烧录固件
- 帧计数器无法同步,容易出问题
我记得有个项目,客户非要图省事用ABP。结果设备在测试时频繁掉线,查了半天发现是帧计数器溢出了——设备重启后计数器归零,服务器那边还记着旧值,所有数据都被当成重放攻击丢弃了。后来我建议他们改成OTAA,问题就解决了。
我的建议:除非你的设备是纯单向上报、且网络环境完全可控(比如工厂内部网络),否则尽量用OTAA。虽然多了一次握手流程,但安全性高得多。尤其是做物联网产品,一旦密钥泄露,后果很严重。
4.4 小结
好了,这一章的内容就这些。总结一下:
- 帧结构:数据包怎么打包、加密、校验,记住MHDR、FHDR、FRMPayload和MIC这四个部分就行
- Class类型:A省电但被动,B定时收听,C随时在线——根据你的功耗和延迟需求选
- Join流程:OTAA安全但多一次握手,ABP简单但有风险——我推荐OTAA
下一章咱们聊聊LoRaWAN的速率自适应(ADR)和信道规划,这可是优化网络性能的关键。到时候我会分享一个我在城市级物联网项目里踩过的坑——嗯,那个坑让我加班了整整一周。