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小能手,如果不能提出一些稍有深度的信息增量,就对不起这个称号了。所以,有些协议设计层面的心得要分享下:

  1. 特别酷的ADR(速率自适应)机制
    这个章节中最亮眼的莫过于速率自适应机制,简直是为LoRa网络量身定做的:一旦使能了FCtrl中的ADR位,距离近信号好的节点用高速率,距离远信号弱的节点用低速率,不小心被调高了速率,则自动降下来。这样,尽可能地提高了传输速率,也有效提高了网络容量。我已经见过不少厂家,拿这个协议的公知特点当产品卖点了。

  2. 可同时携带数据和命令的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.


0 0
原创粉丝点击