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

本文主要结合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 commandsmcpsReq.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 variableAppData.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 FramePendingif( 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 RxSlotif( ComplianceTest.Running == true ){ComplianceTest.DownLinkCounter++;}if( mcpsIndication->RxData == true ){switch( mcpsIndication->Port ){case 1: // The application LED can be controlled on port 1 or 2case 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 = MIB_ADR;mibReq.Param.AdrEnable = true;LoRaMacMibSetRequestConfirm( &mibReq );#if defined( REGION_EU868 ) || defined( REGION_RU864 ) || defined( REGION_CN779 ) || defined( REGION_EU433 )LoRaMacTestSetDutyCycleOn( false );
#endif}}else{ComplianceTest.State = mcpsIndication->Buffer[0];switch( ComplianceTest.State ){case 0: // Check compliance test disable command (ii)IsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON;AppPort = LORAWAN_APP_PORT;AppDataSize = AppDataSizeBackup;ComplianceTest.DownLinkCounter = 0;ComplianceTest.Running = false;MibRequestConfirm_t mibReq;mibReq.Type = MIB_ADR;mibReq.Param.AdrEnable = LORAWAN_ADR_ON;LoRaMacMibSetRequestConfirm( &mibReq );
#if defined( REGION_EU868 ) || defined( REGION_RU864 ) || defined( REGION_CN779 ) || defined( REGION_EU433 )LoRaMacTestSetDutyCycleOn( LORAWAN_DUTYCYCLE_ON );
#endifbreak;case 1: // (iii, iv)AppDataSize = 2;break;case 2: // Enable confirmed messages (v)IsTxConfirmed = true;ComplianceTest.State = 1;break;case 3:  // Disable confirmed messages (vi)IsTxConfirmed = false;ComplianceTest.State = 1;break;case 4: // (vii)AppDataSize = mcpsIndication->BufferSize;AppDataBuffer[0] = 4;for( uint8_t i = 1; i < MIN( AppDataSize, LORAWAN_APP_DATA_MAX_SIZE ); i++ ){AppDataBuffer[i] = mcpsIndication->Buffer[i] + 1;}break;case 5: // (viii){MlmeReq_t mlmeReq;mlmeReq.Type = MLME_LINK_CHECK;LoRaMacStatus_t status = LoRaMacMlmeRequest( &mlmeReq );printf( "\r\n###### ===== MLME-Request - MLME_LINK_CHECK ==== ######\r\n" );printf( "STATUS      : %s\r\n", MacStatusStrings[status] );}break;case 6: // (ix){// Disable TestMode and revert back to normal operationIsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON;AppPort = LORAWAN_APP_PORT;AppDataSize = AppDataSizeBackup;ComplianceTest.DownLinkCounter = 0;ComplianceTest.Running = false;MibRequestConfirm_t mibReq;mibReq.Type = MIB_ADR;mibReq.Param.AdrEnable = LORAWAN_ADR_ON;LoRaMacMibSetRequestConfirm( &mibReq );
#if defined( REGION_EU868 ) || defined( REGION_RU864 ) || defined( REGION_CN779 ) || defined( REGION_EU433 )LoRaMacTestSetDutyCycleOn( LORAWAN_DUTYCYCLE_ON );
#endifJoinNetwork( );}break;case 7: // (x){if( mcpsIndication->BufferSize == 3 ){MlmeReq_t mlmeReq;mlmeReq.Type = MLME_TXCW;mlmeReq.Req.TxCw.Timeout = ( uint16_t )( ( mcpsIndication->Buffer[1] << 8 ) | mcpsIndication->Buffer[2] );LoRaMacStatus_t status = LoRaMacMlmeRequest( &mlmeReq );printf( "\r\n###### ===== MLME-Request - MLME_TXCW ==== ######\r\n" );printf( "STATUS      : %s\r\n", MacStatusStrings[status] );}else if( mcpsIndication->BufferSize == 7 ){MlmeReq_t mlmeReq;mlmeReq.Type = MLME_TXCW_1;mlmeReq.Req.TxCw.Timeout = ( uint16_t )( ( mcpsIndication->Buffer[1] << 8 ) | mcpsIndication->Buffer[2] );mlmeReq.Req.TxCw.Frequency = ( uint32_t )( ( mcpsIndication->Buffer[3] << 16 ) | ( mcpsIndication->Buffer[4] << 8 ) | mcpsIndication->Buffer[5] ) * 100;mlmeReq.Req.TxCw.Power = mcpsIndication->Buffer[6];LoRaMacStatus_t status = LoRaMacMlmeRequest( &mlmeReq );printf( "\r\n###### ===== MLME-Request - MLME_TXCW1 ==== ######\r\n" );printf( "STATUS      : %s\r\n", MacStatusStrings[status] );}ComplianceTest.State = 1;}break;case 8: // Send DeviceTimeReq{MlmeReq_t mlmeReq;mlmeReq.Type = MLME_DEVICE_TIME;LoRaMacStatus_t status = LoRaMacMlmeRequest( &mlmeReq );printf( "\r\n###### ===== MLME-Request - MLME_DEVICE_TIME ==== ######\r\n" );printf( "STATUS      : %s\r\n", MacStatusStrings[status] );}break;default:break;}}break;default:break;}}// Switch LED 2 ON for each received downlinkGpioWrite( &Led2, 1 );TimerStart( &Led2Timer );const char *slotStrings[] = { "1", "2", "C", "C Multicast", "B Ping-Slot", "B Multicast Ping-Slot" };printf( "\r\n###### ===== DOWNLINK FRAME %lu ==== ######\r\n", mcpsIndication->DownLinkCounter );printf( "RX WINDOW   : %s\r\n", slotStrings[mcpsIndication->RxSlot] );printf( "RX PORT     : %d\r\n", mcpsIndication->Port );if( mcpsIndication->BufferSize != 0 ){printf( "RX DATA     : \r\n" );PrintHexBuffer( mcpsIndication->Buffer, mcpsIndication->BufferSize );}printf( "\r\n" );printf( "DATA RATE   : DR_%d\r\n", mcpsIndication->RxDatarate );printf( "RX RSSI     : %d\r\n", mcpsIndication->Rssi );printf( "RX SNR      : %d\r\n", mcpsIndication->Snr );printf( "\r\n" );
}

应用可以在这里获取服务器下发的数据,也可以获取到下发信号的RSSI和SNR等。

至此,如何上报数据,下发接收数据分析完成。

                                    ——————END———————

推荐阅读:
LoRa节点开发——初识SDK
LoRa节点开发——构建keil工程
LoRa节点开发——SDK整体设计思路
LoRa节点开发——SDK整体设计思路

欢迎关注公众号:“物联网思考”,获取更多开发资料、经验。

LoRa节点开发:5、代码详解LoRaWAN中的几种数据包(发送与接收数据)相关推荐

  1. java 代码块_详解java中的四种代码块

    在java中用{}括起来的称为代码块,代码块可分为以下四种: 一.简介 1.普通代码块: 类中方法的方法体 2.构造代码块: 构造块会在创建对象时被调用,每次创建时都会被调用,优先于类构造函数执行. ...

  2. LoRa节点开发:4、代码详解 LoRaWAN节点入网

    本文主要结合LoRaNode SDK v4.4.2和LoRaWAN规范1.0.3来展开. 1.入网(激活)方式 可以看出,两种入网(激活)方式: OTAA(Over-The-Air Activatio ...

  3. linux 查redis状态_干货:用案例代码详解Redis中的事件驱动模型

    Redis 是一个事件驱动的内存数据库,服务器需要处理两种类型的事件. 文件事件 时间事件 下面就会介绍这两种事件的实现原理. 推荐阅读:我凭借这份pdf拿下了蚂蚁金服.字节跳动.小米等大厂的offe ...

  4. 详解 Java 中的三种代理模式

    代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. 这里使用 ...

  5. 详解shell中的几种标准输出重定向方式

    shell重定向介绍 无论是用什麽语言开发的程序,都会处理外部的输入,然后将运算结果输出到指定的位置.在交互式的程序中,输入来自用户的键盘和鼠标,结果输出到用户的屏幕,甚至到其他播放设备中.而对于某些 ...

  6. 详解美股中的几种交易单-限价单、市价单、止损单、止损限价单、跟踪止损单

    1.限价单: 1.1.介绍: 是一种以等于或低于指定价格买进特定数量股票的委托单,或一种以等同或高于指定价格(称为限定价格)卖出股票的委托单. 1.2.例子: 假设我们在市场交易价为$2.45时递交了 ...

  7. 【美股】详解美股中的几种交易单-限价单、市价单、止损单、止损限价单、跟踪止损单

    1.限价单: 1.1.介绍: 是一种以等于或低于指定价格买进特定数量股票的委托单,或一种以等同或高于指定价格(称为限定价格)卖出股票的委托单. 1.2.例子: 假设我们在市场交易价为$2.45时递交了 ...

  8. DeepLearning tutorial(4)CNN卷积神经网络原理简介+代码详解

    FROM: http://blog.csdn.net/u012162613/article/details/43225445 DeepLearning tutorial(4)CNN卷积神经网络原理简介 ...

  9. 后缀数组模板及代码详解

    后缀数组代码详解 上图中存在直边和斜边,下文会用到. #include <cstdio> #include <cstring> #include <iostream> ...

最新文章

  1. oracle共享时监听,Oracle监听---共享连接参数配置介绍
  2. UA MATH564 概率论V 中心极限定理
  3. LeetCode - Convert Sorted Array to Binary Search Tree
  4. safari的一些问题
  5. Jmater参数说明
  6. 二十八种未授权访问漏洞合集(暂时最全)
  7. 在VC++中使用Tab Control控件
  8. 拼多多九鼎:信息披露无瑕疵 “注水”的是竞争对手
  9. (剑指Offer)面试题55:字符流中第一个不重复的字符
  10. 测试 REST API,到底应该选择什么样的 VS Code 插件?
  11. 利用lxml爬取豆瓣小组内容文档并保存
  12. 刚接触Cisco认证:CCNA学习经验
  13. android java静态库,Android NDK开发相关知识集合
  14. MATLAB入门教程之MATLAB的基本知识
  15. android checkbox点击,android的CheckBox点击全选信息反馈不正常,该如何解决
  16. mapbox之点击图斑更换图斑图片
  17. 关于flash player的问题
  18. wordpress插件_5个最佳WordPress企业目录插件
  19. 高频故障-桌面图标变成白纸图标的恢复方案
  20. VTK中oberver 和 command 的信息机制详解

热门文章

  1. 使用STM8单片机+NTC热敏电阻自制简易温度巡检仪
  2. 使用three.js + geojson 完成广西地图的绘制(上篇)
  3. 来看看生词:CVBS、S-Video、YPbPr、模拟RGB、DVI和HDMI
  4. mysql 过程if语句,mysql存储过程之if语句用法实例详解
  5. Log4j2的MDC详解
  6. windows10 安装
  7. 电动车电池放电口能冲电吗充电口和放电口是同一个吗
  8. predict函数在回归分析中的应用
  9. 渭城曲 / 送元二使安西
  10. 元气骑士icloud共享吧_如何禁用OS X的iCloud照片和视频共享