至此,我们已经搭建好LoRa终端的软硬件开发环境,接下来,将解构和实现LoRa终端的应用程序。
LoRa终端的应用程序,(1)可以直接基于LoRa的物理层协议(LoRa芯片实现);(2)也可以基于LoRa MAC协议。
本文将介绍LoRa的终端--基于物理层协议的PingPong应用程序的软件架构
目录:
1. PingPong应用程序的模型
2. PingPong应用程序的协议栈
3. PingPong应用程序软件架构
4. PingPong软件模块的来源
5. PingPong的工程文件解读
6. PingPong应用程序的主流程图解读
7. PingPong的Master/Slave的状态机切换解读
8. LED灯控制的优化
1. PingPong应用程序的模型
上图LoRa终端PingPong应用程序的模型。
在此模型中,有两个LoRa终端节点,我们称为NodeA和NodeB;
每个节点是两个角色中的一个:Master和Slave角色。
Master负责发送或广播Ping消息,当Slave收到Ping消息后,延时10ms后,发送Pong消息作为回复。
Master在收到Slave的Pong回复消息后,延时1s,再发送下一个Ping消息。
就这样周而复始,master不断的发送Ping消息,slave不断的回复Pong消息。
Master和Slave的角色,是在消息交互过程中自动协商出来的。
当然,为了可视化PingPong的消息交互流程,在发送消息时,点亮LED, 发送完消息后,关闭LED.
由于master和slave发送消息的延时不同,因此LED灯亮的时间也不相同。
详细参考源代码。
备注:
在本案例中,只有一个灯,用于展示数据的发送和接收:在收到数据后,打开LED, 在发送完数据后,关闭LED.
2. PingPong应用程序的协议栈
在上图中,
(1)PingPong应用程序:
负责发送和接收Ping和Pong消息,Ping和Pong是两个不同的字符串消息,一个是"Ping”字符串,一个是"Pong”字符串。
同时负责根据收到Ping和Pong消息后,进行Master和Slave角色的切换。
(2)LoMAC:在该应用中,并没有实现LoRa MAC层协议,本文主要关注:如何直接通过LoRa的物理层和射频层芯片SX126X发送特定的数据:ping和pong字符串。
(3)MCU驱动程序:应用程序通过MCU的驱动程序来访问LoRa SX126X芯片,用于发送和接收Ping和Pong消息。
(4)MCU单片机:所有应用程序的执行体。
(5)LoRa物理层芯片Sx126X:芯片实现LoRa协议的最底层(RF射频层和物理层协议)。
如下是LoRa物理层协议的帧结构,ping和pong字符串消息是承载在物理层的Payloa中的。
更详细的软件模块见如下:
3. PingPong应用程序软件架构
在上图中,灰色的部分不在本文讨论。
4. PingPong软件模块的来源
上述所有的软件模块,都可以由开发板供应商一次性提供,包括IAR或STDP集成开发工具的工程文件。
但我们需要知道,开发板供应商只是这些程序的集成者,他们并非所有程序的创建者,程序主要来源如下几方:
(1)STM8 MCU的芯片商:ST公司
(2)LoRa射频芯片SX1262的芯片供应商:semtech
(3)IAR和STDP集成开发环境:IAR公司或ST公司
(4)LoRa模组供应商:翱捷科技,该公司是模组代码的主要的集成者,他们集成了STM8 外设驱动 + SX126x驱动
(5)开发板提供商:成都芯域矩阵,是最终代码继承者,但95%以上的代码都不是其实现的,他们只对于开发板相关的代码进行了部分的适配。
代码模块类型 | 代码位置 | 代码的提供者 |
PingPong应用程序 | ..\LoRaMac-node\src\apps\ping-pong\ASR6505\main.c | semtech提供样本, 可以semtech官网获取 模组和开发板厂家只提供模板和开发板相关的适配 |
LoRa射频芯片驱动 | ..\LoRaMac-node\Src\Radio\SX1626x\*.* | semtech提供样本, 模组厂家提供适配版本 |
操作系统级接口驱动程序 | ..\LoRaMac-node\src\system\*.* | semtech提供,屏蔽不同MCU和不同目标的差别 |
C语言标准库:静态库 | ..\LoRaMac-node\src\boards\*.* | IAR或ST集成工具公司提供 |
STM8标准外设接口驱动程序 | ..\Libraries\STM8L15x_StdPeriph_Driver\*.* | ST公司提供,可以ST官网获取 |
IRA软件工程或STDP | ..\Projects\PingPong\EWSTM8 | 开发板供应商 |
5. PingPong的工程文件解读
User: 用户应用程序,LoRa Ping Pong应用程序和MAC class A, C都在此。
STM815x_StdPeriph_Driver:MCU外设接口,如SPI, I2C,timer等接口驱动。
LoRaNode: LoRa协议栈程序,包括物理层+射频层芯片Sx126x驱动程序与MAC协议程序。
Ouput:编译输出。
6. PingPong应用程序的主流程图解读
在上图汇总可以看出,
在master模式下,如果收到ping消息,说明这里有两个master,则收到Ping消息的节点,主动让出master模式,进入Slave模式;
在slave模式下,如果收到pong消息,说明这里有两个Slave,则收到Pong消息的节点,主动退出Slave模式,进入master模式。
在master模式下,如果超时没有收到slave的发来的pong响应,则master认为,slave可能没有收到master的ping,则重发ping。
在slave模式下,如果超时没有收到slave的发来的ping消息,由于ping pong应用程序是请求、响应模式,因此slave则一直等待。
这种设计有一个缺陷:
就是Master掉线之后,Slave也无法响应了,因此在测试过程中,会发现一个奇怪的现象,就是master断线后,slave也没有反应了,且此时,Slave无法切换到master模式,这是ping Pong应用程序的缺陷之一。
解决办法:
(1)重启master节点。
7. PingPong的Master/Slave的状态机切换解读
上电初始状态,每个节点默认是master模式,在启动的过程中,先发送ping的节点,就可能保持在master模式,而后启动慢一点的节点,会收到启动快一点的节点的ping消息,然后切换到slave模式。这样,就完成在启动过程中,master和slave角色的自动协商。
但这样的设计和代码实现,也有一个大的缺陷,就是如果两个节点的启动时间基本相同,发送ping报文的时间也基本相同,则两个节点在发送ping报文的过程中,都无法处于接收状态,因此无法收到对方的ping报文,导致一个很严重的测试后果:即两个节点都master状态,超时之后,持续发送ping报文,始终无法进入一个master,一个slave的ping pong效果。
下图展现了这个特殊的异常过程:
在上图中,master1和master2的启动时间相差无几,Master1正在发送ping消息的时候,master2也在发送ping消息,而RFIC收和发是不能同时进行的,在发送的时候,不能接收,在接收的时候,不能发送。
这样导致master1和master2始终处于5分钟接收Rx超时状态,超时后,两个节点,同时发送ping,同时进入接收状态,因此,任何一个节点,都无法进入slave模式。现象是:两个节点的接收LED灯都一直不闪烁,误以为程序没有执行。
错误的根本原因:
(1)设计的缺陷,未能考虑到两个节点同时发送和同时接收这样的小概率事件。
(2)发送消息时间过长,到2-3s中。ping和pong消息只有4个字节,但在代码实现是,消息的长度是64字节,后续的60字节都是填充数据。
解决的办法:
(1)重启其中一个节点,确保两个节点的发送消息的时间是错开的,即一个节点在发送,一个节点在接收。如果一次不成,多重启几次。达到如下的效果:
master2的滞后,可以使得Master1和Master2在接受和发送时是错开的,即master1在发送,master2在接受,在master2发送时,master1处于接收状态。
这样错开的结果是,master2先收到master1发送过来的ping消息,然后切换到slave模式下,然后被动等待master1的ping消息,然后被动的发送响应消息pong消息。
(2)减少发送数据包的长度,数据包的长度越长,发送的窗口的时间越长,这样master1和master2发送窗口重叠的可能就越大。减少发送数据包的长度,降低发送窗口的长度,降低问题出现的概率,但不能根除。
(3)加大接收超时定时器,目前的接收超时定时器是5分钟,加大该定时器,实际上就是加大接收窗口的长度。这样可以降低问题出现的概率,但不能根除。
(4)设计上解决:在接收超时后,采用随机的延时发送ping报文,而不是固定的延时,或立即发送。但这需要MCU提供随机延时的功能。
8. LED显示的优化
为了展示RFIC发送64字节的数据需要较长的时间,我们可以优化LED的显示。
(1)当前的LED显示的代码逻辑:
case RX:
if( isMaster == true ) //master
{
if( BufferSize > 0 )
{
if( strncmp( ( const char* )Buffer, ( const char* )PongMsg, 4 ) == 0 )
{
printf("Master Received: PONG\r\n");
// Send the next PING frame
/* LED TX ON */
GPIO_LOW(LED_TX_PORT,LED_TX_PIN); //打开LED
Buffer[0] = 'P';
Buffer[1] = 'I';
Buffer[2] = 'N';
Buffer[3] = 'G';
// We fill the buffer with numbers for the payload
for( i = 4; i < BufferSize; i++ )
{
Buffer[i] = i - 4;
}
DelayMs(1000);
printf("Master Sent: PING\r\n");
Radio.Send( Buffer, BufferSize ); //请求射频芯片发送ping消息。
/* LED TX OFF */
GPIO_HIGH(LED_TX_PORT,LED_TX_PIN); //关闭LED
}
}
else //Slave模式
{
if( BufferSize > 0 )
{
if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 )
{
printf("Slave Received: PING\r\n");
printf("Slave Rssi: %d\r\n", RssiValue);
printf("Slave Snr: %d\r\n", SnrValue);
// Send the reply to the PONG string
/* LED TX ON */
GPIO_LOW(LED_TX_PORT,LED_TX_PIN); //打开LED
Buffer[0] = 'P';
Buffer[1] = 'O';
Buffer[2] = 'N';
Buffer[3] = 'G';
// We fill the buffer with numbers for the payload
for( i = 4; i < BufferSize; i++ )
{
Buffer[i] = i - 4;
}
DelayMs(1);
Radio.Send( Buffer, BufferSize ); //请求射频芯片发送ping消息。
printf("Slave Sent: PONG\r\n");
/* LED TX OFF */
GPIO_HIGH(LED_TX_PORT,LED_TX_PIN); //关闭LED
}
(2)当前的LED显示的优化逻辑
把LED灯的关闭时间点,推迟到射频芯片发送完完整的消息之后。
case TX: //射频芯片发送完成
GPIO_HIGH(LED_TX_PORT,LED_TX_PIN); //关闭LED
//turn Rx on as soon as possible
Radio.Rx( RX_TIMEOUT_VALUE );
case TX_TIMEOUT: //射频芯片发送出错,或发送被打断。
GPIO_HIGH(LED_TX_PORT,LED_TX_PIN); //关闭LED
通过LED等的亮度时间,就可以清晰地知道:
射频芯片什么时候处于发送状态,发送数据。
什么时候,处于接收状态,等待接受消息。
结束语:
至此,我们可以利用开发板提供商提供的软件工程代码,能够进行两个LoRa节点间,点对点通信。
两个LoRa节点直接利用的是LoRa芯片提供的物理层的功能。ping和pong消息是直接承载在LoRa的物理成的帧结构中。
后续会进一步深入拆解LoRa物理层的功能和原理:包括物理层的编码、调制解调、天线、电磁波频频等概念与原理。