LoRaMacStatus_t Send( LoRaMacHeader_t* macHdr, uint8_t fPort, void* fBuffer, uint16_t fBufferSize )
{
    LoRaMacFrameCtrl_t fCtrl;
    LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID;
    int8_t datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
    int8_t txPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower;
    uint32_t adrAckCounter = MacCtx.NvmCtx->AdrAckCounter;
    CalcNextAdrParams_t adrNext;

    // Check if we are joined
    if( MacCtx.NvmCtx->NetworkActivation == ACTIVATION_TYPE_NONE )
    {
        return LORAMAC_STATUS_NO_NETWORK_JOINED;
    }
    if( MacCtx.NvmCtx->MaxDCycle == 0 )
    {
        MacCtx.NvmCtx->AggregatedTimeOff = 0;
    }

    fCtrl.Value = 0;
    fCtrl.Bits.FOptsLen      = 0;
    fCtrl.Bits.Adr           = MacCtx.NvmCtx->AdrCtrlOn;

    // Check class b
    if( MacCtx.NvmCtx->DeviceClass == CLASS_B )
    {
        fCtrl.Bits.FPending      = 1;
    }
    else
    {
        fCtrl.Bits.FPending      = 0;
    }

    // Check server ack
    if( MacCtx.NvmCtx->SrvAckRequested == true )
    {
        fCtrl.Bits.Ack = 1;
    }

    // ADR next request
    adrNext.Version = MacCtx.NvmCtx->Version;
    adrNext.UpdateChanMask = true;
    adrNext.AdrEnabled = fCtrl.Bits.Adr;
    adrNext.AdrAckCounter = MacCtx.NvmCtx->AdrAckCounter;
    adrNext.AdrAckLimit = MacCtx.AdrAckLimit;
    adrNext.AdrAckDelay = MacCtx.AdrAckDelay;
    adrNext.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
    adrNext.TxPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower;
    adrNext.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime;
    adrNext.Region = MacCtx.NvmCtx->Region;

    fCtrl.Bits.AdrAckReq = LoRaMacAdrCalcNext( &adrNext, &MacCtx.NvmCtx->MacParams.ChannelsDatarate,
                                               &MacCtx.NvmCtx->MacParams.ChannelsTxPower, &adrAckCounter );

    // Prepare the frame
    status = PrepareFrame( macHdr, &fCtrl, fPort, fBuffer, fBufferSize );

    // Validate status
    if( ( status == LORAMAC_STATUS_OK ) || ( status == LORAMAC_STATUS_SKIPPED_APP_DATA ) )
    {
        // Schedule frame, do not allow delayed transmissions
        status = ScheduleTx( false );
    }

    // Post processing
    if( status != LORAMAC_STATUS_OK )
    {
        // Bad case - restore
        // Store local variables
        MacCtx.NvmCtx->MacParams.ChannelsDatarate = datarate;
        MacCtx.NvmCtx->MacParams.ChannelsTxPower = txPower;
    }
    else
    {
        // Good case
        MacCtx.NvmCtx->SrvAckRequested = false;
        MacCtx.NvmCtx->AdrAckCounter = adrAckCounter;
        // Remove all none sticky MAC commands
        if( LoRaMacCommandsRemoveNoneStickyCmds( ) != LORAMAC_COMMANDS_SUCCESS )
        {
            return LORAMAC_STATUS_MAC_COMMAD_ERROR;
        }
    }
    return status;
}

LoRaMacStatus_t Send( LoRaMacHeader_t* macHdr, uint8_t fPort, void* fBuffer, uint16_t fBufferSize )
{
    LoRaMacFrameCtrl_t fCtrl;
    LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID;
    int8_t datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
    int8_t txPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower;
    uint32_t adrAckCounter = MacCtx.NvmCtx->AdrAckCounter;
    CalcNextAdrParams_t adrNext;

     
    if( MacCtx.NvmCtx->NetworkActivation == ACTIVATION_TYPE_NONE )
    {
        return LORAMAC_STATUS_NO_NETWORK_JOINED;
    }
    if( MacCtx.NvmCtx->MaxDCycle == 0 )
    {
        MacCtx.NvmCtx->AggregatedTimeOff = 0;
    }

    fCtrl.Value = 0;
    fCtrl.Bits.FOptsLen      = 0;
    fCtrl.Bits.Adr           = MacCtx.NvmCtx->AdrCtrlOn;

     
    if( MacCtx.NvmCtx->DeviceClass == CLASS_B )
    {
        fCtrl.Bits.FPending      = 1;
    }
    else
    {
        fCtrl.Bits.FPending      = 0;
    }

     
    if( MacCtx.NvmCtx->SrvAckRequested == true )
    {
        fCtrl.Bits.Ack = 1;
    }

     
    adrNext.Version = MacCtx.NvmCtx->Version;
    adrNext.UpdateChanMask = true;
    adrNext.AdrEnabled = fCtrl.Bits.Adr;
    adrNext.AdrAckCounter = MacCtx.NvmCtx->AdrAckCounter;
    adrNext.AdrAckLimit = MacCtx.AdrAckLimit;
    adrNext.AdrAckDelay = MacCtx.AdrAckDelay;
    adrNext.Datarate = MacCtx.NvmCtx->MacParams.ChannelsDatarate;
    adrNext.TxPower = MacCtx.NvmCtx->MacParams.ChannelsTxPower;
    adrNext.UplinkDwellTime = MacCtx.NvmCtx->MacParams.UplinkDwellTime;
    adrNext.Region = MacCtx.NvmCtx->Region;

    fCtrl.Bits.AdrAckReq = LoRaMacAdrCalcNext( &adrNext, &MacCtx.NvmCtx->MacParams.ChannelsDatarate,
                                               &MacCtx.NvmCtx->MacParams.ChannelsTxPower, &adrAckCounter );

     
    status = PrepareFrame( macHdr, &fCtrl, fPort, fBuffer, fBufferSize );

     
    if( ( status == LORAMAC_STATUS_OK ) || ( status == LORAMAC_STATUS_SKIPPED_APP_DATA ) )
    {
         
        status = ScheduleTx( false );
    }

     
    if( status != LORAMAC_STATUS_OK )
    {
         
         
        MacCtx.NvmCtx->MacParams.ChannelsDatarate = datarate;
        MacCtx.NvmCtx->MacParams.ChannelsTxPower = txPower;
    }
    else
    {
         
        MacCtx.NvmCtx->SrvAckRequested = false;
        MacCtx.NvmCtx->AdrAckCounter = adrAckCounter;
         
        if( LoRaMacCommandsRemoveNoneStickyCmds( ) != LORAMAC_COMMANDS_SUCCESS )
        {
            return LORAMAC_STATUS_MAC_COMMAD_ERROR;
        }
    }
    return status;
}
