当前位置 博文首页 > freemote的博客:LoRa节点开发:5、代码详解LoRaWAN中的几种数据

    freemote的博客:LoRa节点开发:5、代码详解LoRaWAN中的几种数据

    作者:[db:作者] 时间:2021-07-18 19:11

    本文来源微信公众号【物联网思考】

    本文主要结合LoRaNode SDK v4.4.2和LoRaWAN规范1.0.3来展开。

    1、数据包类型

    LoRaWAN规范中有不同的数据包,通过MType字段区分,MType是3位的,总共可以表示8种不同类型的数据,其中前六种是不同的数据包,分别是“入网请求”、“入网回复”、“不需要确认上行数据包”、“需要确认上行数据包”、“不需要确认下行数据包”、“需要确认下行数据包”,后面两个一个是预留(RFU),一个开放给用户自定义(Proprietary)。
    在这里插入图片描述
    其中“入网请求”、“入网回复”,主要是用于OTAA入网的,在前面的LoRa节点开发——代码详解 LoRaWAN节点入网文章已经分析过了。

    “不需要确认上行数据包”、“需要确认上行数据包”:主要用于用户上报数据。这里说一下不需要确认和需要确认,“需要确认”:就是发送数据后需要服务器回复一个ack,表明已经收到数据了,如果没有回复ack,那么还会重复发,一般用于紧急重要的数据上报;“不需要确认”:就是不管服务器有没有收到数据,发一次就不管了,一般用于非紧急不重要数据上报。

    “不需要确认下行数据包”、“需要确认下行数据包”:主要服务器下发数据。不需要确认和需要确认同上面。服务器发送“需要确认”数据包时,需要节点回复ack给服务器。

    2、源码分析

    2.1上行数据

    /*!
     * \brief   Prepares the payload of the frame
     */
    static void PrepareTxFrame( uint8_t port )
    {
        switch( port )
        {
        case 2:
            {
                AppDataSizeBackup = AppDataSize = 1;
                AppDataBuffer[0] = AppLedStateOn;
            }
            break;
        case 224:
            if( ComplianceTest.LinkCheck == true )
            {
                ComplianceTest.LinkCheck = false;
                AppDataSize = 3;
                AppDataBuffer[0] = 5;
                AppDataBuffer[1] = ComplianceTest.DemodMargin;
                AppDataBuffer[2] = ComplianceTest.NbGateways;
                ComplianceTest.State = 1;
            }
            else
            {
                switch( ComplianceTest.State )
                {
                case 4:
                    ComplianceTest.State = 1;
                    break;
                case 1:
                    AppDataSize = 2;
                    AppDataBuffer[0] = ComplianceTest.DownLinkCounter >> 8;
                    AppDataBuffer[1] = ComplianceTest.DownLinkCounter;
                    break;
                }
            }
            break;
        default:
            break;
        }
    }
    

    可以看到,在PrepareTxFrame这个函数中,应用只需要在AppDataBuffer中填充相应的数据以及设置数据长度AppDataSize即可。

    static bool SendFrame( void )
    {
        McpsReq_t mcpsReq;
        LoRaMacTxInfo_t txInfo;
    
        if( LoRaMacQueryTxPossible( AppDataSize, &txInfo ) != LORAMAC_STATUS_OK )
        {
            // Send empty frame in order to flush MAC commands
            mcpsReq.Type = MCPS_UNCONFIRMED;
            mcpsReq.Req.Unconfirmed.fBuffer = NULL;
            mcpsReq.Req.Unconfirmed.fBufferSize = 0;
            mcpsReq.Req.Unconfirmed.Datarate = LORAWAN_DEFAULT_DATARATE;
        }
        else
        {
            if( IsTxConfirmed == false )
            {
                mcpsReq.Type = MCPS_UNCONFIRMED;
                mcpsReq.Req.Unconfirmed.fPort = AppPort;
                mcpsReq.Req.Unconfirmed.fBuffer = AppDataBuffer;
                mcpsReq.Req.Unconfirmed.fBufferSize = AppDataSize;
                mcpsReq.Req.Unconfirmed.Datarate = LORAWAN_DEFAULT_DATARATE;
            }
            else
            {
                mcpsReq.Type = MCPS_CONFIRMED;
                mcpsReq.Req.Confirmed.fPort = AppPort;
                mcpsReq.Req.Confirmed.fBuffer = AppDataBuffer;
                mcpsReq.Req.Confirmed.fBufferSize = AppDataSize;
                mcpsReq.Req.Confirmed.NbTrials = 8;
                mcpsReq.Req.Confirmed.Datarate = LORAWAN_DEFAULT_DATARATE;
            }
        }
    
        // Update global variable
        AppData.MsgType = ( mcpsReq.Type == MCPS_CONFIRMED ) ? LORAMAC_HANDLER_CONFIRMED_MSG : LORAMAC_HANDLER_UNCONFIRMED_MSG;
        AppData.Port = mcpsReq.Req.Unconfirmed.fPort;
        AppData.Buffer = mcpsReq.Req.Unconfirmed.fBuffer;
        AppData.BufferSize = mcpsReq.Req.Unconfirmed.fBufferSize;
    
        LoRaMacStatus_t status;
        status = LoRaMacMcpsRequest( &mcpsReq );
        printf( "\r\n###### ===== MCPS-Request ==== ######\r\n" );
        printf( "STATUS      : %s\r\n", MacStatusStrings[status] );
    
        if( status == LORAMAC_STATUS_OK )
        {
            return false;
        }
        return true;
    }
    

    一些紧急重要数据可以发送“需要确认数据包”,从SendFrame这个函数中,可以看出需要发送“需要确认数据包”的时候,只需把IsTxConfirmed这个参数设置true即可。

    应用只需设置以上3个参数即可发送,数据准备好之后,就是协议栈组包了,LoRaMacMcpsRequest( &mcpsReq )这个函数正是发送数据组包的函数,组包之后就是加密,最后就是射频发送了。

    2.2下行数据

    过程刚好和发送数据相反(上行数据),先是射频接收,接收到数据之后解密,用户应用数据处理。

    查看static void ProcessRadioRxDone( void )函数,可以看到使用switch case语句,通过macHdr.Bits.MType字段对接收到的数据包进行了区分,FRAME_TYPE_DATA_CONFIRMED_DOWNFRAME_TYPE_DATA_UNCONFIRMED_DOWN正是服务器的下行数据。

    LoRaWAN协议栈在处理的时候,使用了设置标志位,然后回调函数的方法来处理。若有下发数据,则将 MacCtx.MacFlags.Bits.McpsInd 设置为1,如下:

                // Provide always an indication, skip the callback to the user application,
                // in case of a confirmed downlink retransmission.
                MacCtx.MacFlags.Bits.McpsInd = 1;
    

    协议栈中,也给了英文注释,跳转到应用回调函数。

    在LoRaWAN协议栈初始化的时候,注册了几个函数,然后在满足条件的时候回调。

    int main( void )
    {
       …………//代码过长,部分代码未截取
        macPrimitives.MacMcpsConfirm = McpsConfirm;
        macPrimitives.MacMcpsIndication = McpsIndication;
        macPrimitives.MacMlmeConfirm = MlmeConfirm;
        macPrimitives.MacMlmeIndication = MlmeIndication;
        macCallbacks.GetBatteryLevel = BoardGetBatteryLevel;
        macCallbacks.GetTemperatureLevel = NULL;
        macCallbacks.NvmContextChange = NvmCtxMgmtEvent;
        macCallbacks.MacProcessNotify = OnMacProcessNotify;
    
        LoRaMacInitialization( &macPrimitives, &macCallbacks, ACTIVE_REGION );
        …………//代码过长,部分代码未截取
    }
    

    其中,MlmeIndication就是下发回调函数。

    查看MlmeIndication函数,如下:

    static void McpsIndication( McpsIndication_t *mcpsIndication )
    {
        printf( "\r\n###### ===== MCPS-Indication ==== ######\r\n" );
        printf( "STATUS      : %s\r\n", EventInfoStatusStrings[mcpsIndication->Status] );
        if( mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK )
        {
            return;
        }
    
        switch( mcpsIndication->McpsIndication )
        {
            case MCPS_UNCONFIRMED:
            {
                break;
            }
            case MCPS_CONFIRMED:
            {
                break;
            }
            case MCPS_PROPRIETARY:
            {
                break;
            }
            case MCPS_MULTICAST:
            {
                break;
            }
            default:
                break;
        }
    
        // Check Multicast
        // Check Port
        // Check Datarate
        // Check FramePending
        if( mcpsIndication->FramePending == true )
        {
            // The server signals that it has pending data to be sent.
            // We schedule an uplink as soon as possible to flush the server.
            OnTxNextPacketTimerEvent( NULL );
        }
        // Check Buffer
        // Check BufferSize
        // Check Rssi
        // Check Snr
        // Check RxSlot
    
        if( ComplianceTest.Running == true )
        {
            ComplianceTest.DownLinkCounter++;
        }
    
        if( mcpsIndication->RxData == true )
        {
            switch( mcpsIndication->Port )
            {
            case 1: // The application LED can be controlled on port 1 or 2
            case 2:
                if( mcpsIndication->BufferSize == 1 )
                {
                    AppLedStateOn = mcpsIndication->Buffer[0] & 0x01;
                }
                break;
            case 224:
                if( ComplianceTest.Running == false )
                {
                    // Check compliance test enable command (i)
                    if( ( mcpsIndication->BufferSize == 4 ) &&
                        ( mcpsIndication->Buffer[0] == 0x01 ) &&
                        ( mcpsIndication->Buffer[1] == 0x01 ) &&
                        ( mcpsIndication->Buffer[2] == 0x01 ) &&
                        ( mcpsIndication->Buffer[3] == 0x01 ) )
                    {
                        IsTxConfirmed = false;
                        AppPort = 224;
                        AppDataSizeBackup = AppDataSize;
                        AppDataSize = 2;
                        ComplianceTest.DownLinkCounter = 0;
                        ComplianceTest.LinkCheck = false;
                        ComplianceTest.DemodMargin = 0;
                        ComplianceTest.NbGateways = 0;
                        ComplianceTest.Running = true;
                        ComplianceTest.State = 1;
    
                        MibRequestConfirm_t mibReq;
                        mibReq.Type =