LoRaWAN协议解析 第4章 MAC帧格式
来源:互联网 发布:值得要源码交易 编辑:程序博客网 时间:2024/04/30 09:49
1 前言
我正在陆续对《LoRaWAN102》即LoRaWAN协议规范 V1.0.2 版本(2016年7月定稿)协议的各个章节进行翻译。译文之外还对LoRaWAN协议和源码进行了解析,可点此查看帖子LoRa学习笔记_汇总。
欢迎同行朋友们留言交流。
本文作者twowinter,转载请注明作者:http://blog.csdn.net/iotisan/
2 梳理解析
LoRaWAN第4章,主要讲述了MAC帧格式,对所有涉及的字段都做了解释。
千言万语汇成一句话,哦不,汇成一个表。
数据帧头 DevAddr FCtrl FCnt FOpts 数据帧 Preamble PHDR PHDR_CRC MHDR FHDR FPort FRMPayload MIC CRC MAC层 Preamble PHDR PHDR_CRC MHDR MACPayload MIC CRC PHY层 Preamble PHDR PHDR_CRC PHYPayload CRC好了,帧格式是大家随手都能看到的东西,本尊作为IoT小能手,如果不能提出一些稍有深度的信息增量,就对不起这个称号了。所以,有些协议设计层面的心得要分享下:
特别酷的ADR(速率自适应)机制
这个章节中最亮眼的莫过于速率自适应机制,简直是为LoRa网络量身定做的:一旦使能了FCtrl中的ADR位,距离近信号好的节点用高速率,距离远信号弱的节点用低速率,不小心被调高了速率,则自动降下来。这样,尽可能地提高了传输速率,也有效提高了网络容量。我已经见过不少厂家,拿这个协议的公知特点当产品卖点了。可同时携带数据和命令的MAC帧
一般来说,应用除了数据,出于管理需要,肯定还会涉及命令。比如基站要查询节点状态,或者节点要请求变更信道等。所以LoRaWAN协议设计上利用FOpts把数据和命令揉在一个MAC帧里,这样可以提高交互效率,有效地降低功耗。这在寸土寸金,哦不,寸库仑(电量单位)寸金的物联网应用中,是一个很有必要的设计。
3 源码解析
这章的处理基本都在 \src\mac\LoRaMac.c 中,下面按照MAC帧格式的字段逐个解析下。
3.1 MAC层MHDR
在LoRaWAN的数据API中处理了MHDR,这个字段内容比较少,就按需选择了消息类型是confirm还是unconfirm。
另外在管理API中的Join-Req的消息类型。
具体可见 LoRaMacMcpsRequest() 和 LoRaMacMlmeRequest() 这两个函数。
3.2 MACPayload
MACPayload 的组帧都在 PrepareFrame() 这个函数中处理,将macHdr和macPayload的fCtrl、FPort、FRMPayload都传递进去,完成整个MAC层的数据组帧。
LoRaMacBuffer就存放了MACPayload的数据,这个变量的组帧和协议字段定义是一一对应。MACPayload的组帧处理,在大流程上是对join和数据两种类型的帧分别处理,用两个case分开。
为了方便阅览,我把函数代码框架提炼了出来。
LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ){ switch( macHdr->Bits.MType ) { case FRAME_TYPE_JOIN_REQ: ...// 省略 break; case FRAME_TYPE_DATA_CONFIRMED_UP: NodeAckRequested = true; //Intentional falltrough case FRAME_TYPE_DATA_UNCONFIRMED_UP: ... fCtrl->Bits.AdrAckReq = AdrNextDr( fCtrl->Bits.Adr, true, &LoRaMacParams.ChannelsDatarate ); ... if( SrvAckRequested == true ) { SrvAckRequested = false; fCtrl->Bits.Ack = 1; } LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr ) & 0xFF; LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 8 ) & 0xFF; LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 16 ) & 0xFF; LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 24 ) & 0xFF; LoRaMacBuffer[pktHeaderLen++] = fCtrl->Value; LoRaMacBuffer[pktHeaderLen++] = UpLinkCounter & 0xFF; LoRaMacBuffer[pktHeaderLen++] = ( UpLinkCounter >> 8 ) & 0xFF; // Copy the MAC commands which must be re-send into the MAC command buffer memcpy1( &MacCommandsBuffer[MacCommandsBufferIndex], MacCommandsBufferToRepeat, MacCommandsBufferToRepeatIndex ); MacCommandsBufferIndex += MacCommandsBufferToRepeatIndex; if( ( payload != NULL ) && ( payloadSize > 0 ) ) { if( ( MacCommandsBufferIndex <= LORA_MAC_COMMAND_MAX_LENGTH ) && ( MacCommandsInNextTx == true ) ) { fCtrl->Bits.FOptsLen += MacCommandsBufferIndex; // Update FCtrl field with new value of OptionsLength LoRaMacBuffer[0x05] = fCtrl->Value; for( i = 0; i < MacCommandsBufferIndex; i++ ) { LoRaMacBuffer[pktHeaderLen++] = MacCommandsBuffer[i]; } } } else { if( ( MacCommandsBufferIndex > 0 ) && ( MacCommandsInNextTx ) ) { payloadSize = MacCommandsBufferIndex; payload = MacCommandsBuffer; framePort = 0; } } MacCommandsInNextTx = false; // Store MAC commands which must be re-send in case the device does not receive a downlink anymore MacCommandsBufferToRepeatIndex = ParseMacCommandsToRepeat( MacCommandsBuffer, MacCommandsBufferIndex, MacCommandsBufferToRepeat ); if( MacCommandsBufferToRepeatIndex > 0 ) { MacCommandsInNextTx = true; } MacCommandsBufferIndex = 0; if( ( payload != NULL ) && ( payloadSize > 0 ) ) { LoRaMacBuffer[pktHeaderLen++] = framePort; if( framePort == 0 ) { LoRaMacPayloadEncrypt( (uint8_t* ) payload, payloadSize, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, LoRaMacPayload ); } else { LoRaMacPayloadEncrypt( (uint8_t* ) payload, payloadSize, LoRaMacAppSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, LoRaMacPayload ); } memcpy1( LoRaMacBuffer + pktHeaderLen, LoRaMacPayload, payloadSize ); } LoRaMacBufferPktLen = pktHeaderLen + payloadSize; LoRaMacComputeMic( LoRaMacBuffer, LoRaMacBufferPktLen, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &mic ); LoRaMacBuffer[LoRaMacBufferPktLen + 0] = mic & 0xFF; LoRaMacBuffer[LoRaMacBufferPktLen + 1] = ( mic >> 8 ) & 0xFF; LoRaMacBuffer[LoRaMacBufferPktLen + 2] = ( mic >> 16 ) & 0xFF; LoRaMacBuffer[LoRaMacBufferPktLen + 3] = ( mic >> 24 ) & 0xFF; LoRaMacBufferPktLen += LORAMAC_MFR_LEN; break; case FRAME_TYPE_PROPRIETARY: ...// 省略 break; default: return LORAMAC_STATUS_SERVICE_UNKNOWN; } return LORAMAC_STATUS_OK;}
Join-request的组帧处理对应协议第6章 6.2.4 Join-request message。
数据帧的组帧处理则稍微复杂些,尤其是FHDR,下面逐个字段讲解下FHDR。
3.2.1 MACPayload中的FHDR
1.FHDR中的DevAddr
LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr ) & 0xFF;
LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 8 ) & 0xFF;
LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 16 ) & 0xFF;
LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 24 ) & 0xFF;2.FHDR中的FCtrl
首先 ADR 位段 是在传入 PrepareFrame() 之前,就做了处理。
fCtrl.Bits.Adr = AdrCtrlOn;
接着 AdrAckReq 位段,在长期失联情况下会发送AdrAckReq确认链路。
fCtrl->Bits.AdrAckReq = AdrNextDr( fCtrl->Bits.Adr, true, &LoRaMacParams.ChannelsDatarate );
最后 F0ptsLen 位段,会在下面计算完FOpts之后更新。
3.FHDR中的FCnt
LoRaMacBuffer[pktHeaderLen++] = UpLinkCounter & 0xFF;
LoRaMacBuffer[pktHeaderLen++] = ( UpLinkCounter >> 8 ) & 0xFF;
这个UpLinkCounter会在物理层发送完成后会按照协议进行累加。可以看到这是个32位计数器,按照协议规定,“如果采用32位帧计数,FCnt就对应计数器32位的16个低有效位”。
这是上行的,另外下行的也类似。
- 4.FHDR中的FOpts
把MAC命令放入F0pts中,并且更新F0ptsLen。MAC命令,要么使用非零的FPort来和数据一起传输,要么使用FPort0来单独传输。
// Copy the MAC commands which must be re-send into the MAC command buffermemcpy1( &MacCommandsBuffer[MacCommandsBufferIndex], MacCommandsBufferToRepeat, MacCommandsBufferToRepeatIndex );MacCommandsBufferIndex += MacCommandsBufferToRepeatIndex;if( ( payload != NULL ) && ( payloadSize > 0 ) ){ if( ( MacCommandsBufferIndex <= LORA_MAC_COMMAND_MAX_LENGTH ) && ( MacCommandsInNextTx == true ) ) { fCtrl->Bits.FOptsLen += MacCommandsBufferIndex; // Update FCtrl field with new value of OptionsLength LoRaMacBuffer[0x05] = fCtrl->Value; for( i = 0; i < MacCommandsBufferIndex; i++ ) { LoRaMacBuffer[pktHeaderLen++] = MacCommandsBuffer[i]; } }}else{ if( ( MacCommandsBufferIndex > 0 ) && ( MacCommandsInNextTx ) ) { payloadSize = MacCommandsBufferIndex; payload = MacCommandsBuffer; framePort = 0; }}
3.2.2 MACPayload中的FPort
这个是在应用层一直传递进去的,协议栈默认是用了端口2。这个是后期大家在应用时要调整的,类似于IP端口,不同的端口对应不同的服务。
3.3 MIC解析
在函数 PrepareFrame()的最后是调用LoRaMacComputeMic() 计算出整个MAC层的校验码。应用层这边基本不用改这边就暂时不细究了。
End
That’s all.
- LoRaWAN协议解析 第4章 MAC帧格式
- LoRaWAN协议解析 第3章 PHY帧格式
- LoRaWAN协议解析 第5章 MAC命令
- LoRaWAN协议中文版 第3章 PHY帧格式
- LoRaWAN协议1.0中文版_第4章_MAC帧格式
- LoRaWAN协议中文版_第5章 MAC命令
- LoRaWAN协议中文版 第11章 下行ping帧格式(仅Class B)
- LoRaWAN协议解析 第6章 终端激活
- LoRaWAN协议中文版 第2章 LoRaWAN Classes 类型介绍
- LoRaWAN协议中文版 第1章 介绍
- LoRaWAN协议中文版 第10章 Class B 模式的上行帧
- LoRaWAN协议中文版 第8章 Class B介绍
- LoRaWAN协议中文版 第17章 Class C
- LoRaWAN版本历史及协议格式说明
- LoRaWan协议
- LoRaWAN协议
- LoRaWAN协议1.0中文版_第6章_终端激活
- LoRaWAN协议中文版 第9章 下行同步网络的原理
- IO
- Java编程题练习2017-02-22
- 【C++ STL 温故而知新 002】Lists(链表)
- restful架构
- hiberfil.sys和pagefile.sys处理,为系统盘瘦身
- LoRaWAN协议解析 第4章 MAC帧格式
- 友盟崩溃信息定位
- struct kref
- 《从零开始搭建游戏服务器》自定义兼容多种Protobuf协议的编解码器
- 为什么I2C从机地址要左移一位
- 170223
- 表格里的某个超链接点击执行的函数参数传递json对象格式的数据
- 【lintcode笔记】在二叉查找树中插入节点
- [LeetCode] Submission Details(Java) 高票答案边界值对否?