基于sx1276lora模块,进行多个模块之间自组网,组网形式为1个集中器加多个终端。模块之间距离较远时,集中器无法直接与某个终端进行通信,其他终端本身可作为中继给该终端作为中继与集中器通信。lora调制方式,发送数据为星型通信方式,为自组网提供了便利。
终端接收心跳存储:
typedef struct
{
uint32_t Id;//接收的ID
uint8_t Rssi;//信号强度
}RECV_TERMINAL_T;
RECV_TERMINAL_T RecvTerminal[RECV_NUM];
集中器接收心跳存储、发送路径存储:
typedef struct
{
uint32_t Id;//ID设备号
uint8_t index;//
uint8_t relay;//
}TERMINAL_ID_LOCATION_T;
typedef struct
{
uint32_t Id[ROUTING_LAYERS];//接收的ID
uint8_t Rssi[ROUTING_LAYERS];//信号强度
}RECV_TERMINAL_T;
TERMINAL_ID_LOCATION_T TerminalId[REGISTER_ID_NUM]; // 注册的终端ID 由配置传入
RECV_TERMINAL_T RecvTerminal[ ID_ROUTE_NUM];
1.1、路径存储形式:
二维数组RecvTerminal[i].Id[j];使用i表示所有不同的路径信息,j=0表示是直连、j=1即1级中继、j=2即2级中继。
集中器上的注册ID填写至结构体TerminalId;TerminalId.Id为注册ID,TerminalId.index为要发送到改地址的路径信息位置,TerminalId.relay为需要发送到该地址所需经过的中继层数。
1.2、终端处理流程:
终端定义每隔5分钟(时间根据测试修改)发送一次心跳报文。
在接收数据时,判断若是接收到心跳报文,则进行填本终端所接收到的心跳结构体RecvTerminal[n];
判断若是终端发送的非心跳报文,则进行判断,若报文目的地址非本地地址,则进行中继转发判断,判断本地地址是否为该报文中继,若是则进行转发判断到下一中继或者转发到目的地址;
判断若是集中器下发的报文,若目的地址为本地地址,则直接进行解包处理。若目的地址不为本地地址,则进行中继转发判断到下一中继或转发到目的地址。
终端回复集中器报文时,自动根据集中器下发的路径来巡回路径。
1.3、集中器端:
1.3.1 直连通信:
集中器接收到终端的心跳报文,则进行填表处理RecvTerminal[i++].Id[0],TerminalId.index=i,TerminalId.relay=0。若在集中器注册的ID存在接收不到心跳,则需要进行中继召唤处理,若能接收到全部心跳报文,则不进行一下处理。
1.3.2 一级中继:
集中器下发召唤命令到已填表的IDrecv=RecvTerminal[i++].Id[0],要求该IDrecv上送其所能接收到的心跳终端(RecvTerminal[n])。查询RecvTerminal里是否有存在集中器无接收到心跳的Idnorecv,若存在,则填表RecvTerminal[i++].Id[0]=IDrecv,RecvTerminal[i].Id[1]=Idnorecv;该Idnorecv的TerminalId.index=i,TerminalId.relay=1;表示集中器需要与Idnorecv通信时,需要通过终端IDrecv作为中继才能发送成功。
1.3.3 二级中继:
一级中继召唤命令完成后,依旧存在集中器注的IDnorecv接收不到心跳报文也无路径信息,即IDnorecv对应的结构体的index==0。则集中器下发召唤命令到已填表中TerminalId.relay为1的Idrecv,由于Idrecv的relay为1,则该终端本身需要通过一个中继终端RelayId作为中继来进行通信。集中器要求该Idrecv上送其所能接收到的心跳终端(RecvTerminal[n]),再根据终端回应的报文查询RecvTerminal里是否有存在集中器无接收到的心跳IDnorecv,若存在则填表RecvTerminal[i++].Id[0]=RelayId,RecvTerminal[i].Id[1]=Idrecv,
RecvTerminal[i].Id[2]=Idnorecv;该Idnorecv的TerminalId.index=i,TerminalId.relay=2。表示当集中器要与Idnorecv通信时,需要通过RelayId、Idrecv两级作为该终端中继才能通信成功。
1.3.4 集中器下发报文路径:
报文中包含relaynum中继个数,relayID具体的中继ID,即路径。例如需要通信ID=0x0001,0x0001对应的结构体TerminalId.Id=0x0001,TerminalId.index=5;
TerminalId.relay=2. 则集中器下发的报文中relaynum=2,relayID1=RecvTerminal[5].Id[0],
relayID2=RecvTerminal[5].Id[1]。如此集中器即能与0x0001通信。
终端部分代码:
#define ROUTING_LAYERS 3
#define REGISTER_ID_NUM 5
#define RECV_NUM 10
typedef enum
{
UPLOAD_TERMINAL_RELAY = 0x01,
}COMMAND_OPERATION_TYPE_E; // 命令操作类型
typedef enum
{
UPLOAD_MESSAGE_FIRST_RELAY,
UPLOAD_MESSAGE_SECOND_RELAY,
}COMMAND_PROCESSING_E; // 命令处理
typedef struct
{
uint8_t DevType; /* 设备类型*/
uint32_t DevAddr; /* 设备地址 */
uint8_t TypeId; /* 操作类型 例如遥控 遥测*/
uint8_t Func; /* 功能码,例如具体遥控什么*/
uint8_t RelayNum;//经过中继个数
uint32_t RfAddr; /* RF 地址*/
uint8_t Data[16]; /* 数据域 */
uint8_t Len; /* data数据报文长度 */
}MESSAGE_FORMAT_T; // message format 报文格式
typedef struct
{
uint8_t Buf[64];
uint8_t Len; /* 数据长度*/
uint32_t Addr; /* 远方地址*/
}MESSAGE_BUFFER;
typedef struct
{
BYTE Cache[MACRO_RFPROC_RECV_CACHE_SIZE];
CircleQueue Queue;
}MESSAGE_QUEUE;
typedef struct
{
uint32_t Id;//接收的ID
uint8_t Rssi;//信号强度
}RECV_TERMINAL_T;
typedef struct
{
uint8_t Num; // 注册终端ID个数 由配置传入
uint8_t index; // 检测注册终端第index个是否收到心跳
uint8_t Route;
uint8_t NetworkFlag;// 开启组网标志
}REGISTER_TERMINAL_T;
typedef struct
{
RECV_TERMINAL_T RecvTerminal[RECV_NUM];
uint8_t n;MESSAGE_FORMAT_T SendFrame; /* 发送帧数据*/
MESSAGE_FORMAT_T RecvFrame; /* 接收存放帧*/
MESSAGE_BUFFER SendBuf;
MESSAGE_QUEUE RecvQueue;
}PROTOCOL_T;
extern __no_init PROTOCOL_T g_ProData;
/*
* 函数名称 : HeartBeatPackets
* 函数功能 : 心跳
* 输入参数 : 无
* 输出参数 : 无
* 返 回 值 : 无
* 其它说明 : 5s发送一次
* */
static void HeartBeatPackets(void)
{
uint8_t index = 0;
uint8_t i = 0;
uint8_t cs = 0;
uint8_t TypeId = 0x55;
g_ProData.SendBuf.Buf[index++] = 0x10;
g_ProData.SendBuf.Buf[index++] = TypeId;
uint8_t *pAddr = (uint8_t *)&g_ProData.SendFrame.DevAddr;
for(i = 0;i < 3;i++)
{
g_ProData.SendBuf.Buf[index++] = pAddr[3-i];
}
for(i = 1;i < index;i++)
{
cs += g_ProData.SendBuf.Buf[i];
}
g_ProData.SendBuf.Buf[index++] = cs;
g_ProData.SendBuf.Buf[index++] = 0x16;
g_ProData.SendBuf.Addr = 0x00;
g_ProData.SendBuf.Len = index;
SX1276_Data->G_LoRaConfig.PayloadLength = g_ProData.SendBuf.Len;
Send();
}
/*
* 函数名称 : RF436_Unpack10
* 函数功能 : 解包
* 输入参数 : 无
* 输出参数 : 无
* 返 回 值 : 成功返回true 失败返回FALSE
* 其它说明 : 无
* */
static bool Unpack10(void)
{
uint8_t cs = 0;
uint32_t RfAddr = 0;
static uint8_t i=0;
..........
RfAddr = RF436_GetULongFromCache(&g_ProData.RecvQueue.Queue,2,3);
if(i!=0)
{
for(int j=0;j<i;j++)
{
if(RfAddr == g_ProData.RecvTerminal[j].Id) // 已存在 刷新数据
{
g_ProData.RecvTerminal[j].Id = RfAddr;
g_ProData.RecvTerminal[j].Rssi = g_Sx1276.LoraParam.Rssi;
CircleQueue_Remove(&g_ProData.RecvQueue.Queue,7);/* 读取完成一包,下移一包*/
return TRUE;
}
}
// 不存在 增加填表
g_ProData.RecvTerminal[i++].Id = RfAddr;
g_ProData.RecvTerminal[i].Rssi = g_Sx1276.LoraParam.Rssi;
g_ProData.n = i;
}
else
{
g_ProData.RecvTerminal[0].Id = RfAddr;
g_ProData.RecvTerminal[0].Rssi = g_Sx1276.LoraParam.Rssi;
i++;
g_ProData.n = 1;
}
CircleQueue_Remove(&g_ProData.RecvQueue.Queue,7);/* 读取完成一包,下移一包*/
return TRUE;
}
/*
* 函数名称 : SendHeartBeatId
* 函数功能 : 发送接收的心跳终端地址
* 输入参数 : 无
* 输出参数 : 无
* 返 回 值 : 无
* 其它说明 : 集中器召唤时发送
* */
static void SendHeartBeatId(uint8_t TypeId,uint8_t Func)
{
uint8_t index = 0;
uint8_t i = 0;
uint8_t cs = 0;
g_ProData.SendFrame.DevType = 0x5e;
g_ProData.SendFrame.TypeId = UPLOAD_TERMINAL_RELAY;
g_ProData.SendFrame.Func = UPLOAD_MESSAGE_FIRST_RELAY;
g_ProData.SendFrame.RelayNum = 0;
g_ProData.SendFrame.Len = (g_ProData.n)*(sizeof(RECV_TERMINAL_T));
g_ProData.SendBuf.Buf[index++] = 0x68;
index+=2;
g_ProData.SendBuf.Buf[index++] = 0x68;
g_ProData.SendBuf.Buf[index++] = g_ProData.SendFrame.DevType;
index += RF436_GetBytesFromOther(&g_ProData.SendFrame.DevAddr,sizeof(ULONG), g_ProData.SendBuf.Buf + index,3);
g_ProData.SendBuf.Buf[index++] = g_ProData.SendFrame.TypeId;
g_ProData.SendBuf.Buf[index++] = g_ProData.SendFrame.Func;
g_ProData.SendBuf.Buf[index++] = g_ProData.SendFrame.RelayNum;
index += RF436_GetBytesFromOther(&g_ProData.SendFrame.RfAddr,sizeof(uint32_t),g_ProData.SendBuf.Buf + index, 3);
memcpy(g_ProData.SendFrame.Data,&g_ProData.RecvTerminal,(g_ProData.n)*(sizeof(RECV_TERMINAL_T)));
for(i = 0;i < g_ProData.SendFrame.Len;i++)
{
g_ProData.SendBuf.Buf[index++] = g_ProData.SendFrame.Data[i];/* pframedata[i];,..... */
}
for(i = 4;i < index;i++)
{
cs += g_ProData.SendBuf.Buf[i];
}
g_ProData.SendBuf.Buf[index++] = cs;
g_ProData.SendBuf.Buf[index++] = 0x16;
g_ProData.SendBuf.Buf[1] = index-6;
g_ProData.SendBuf.Buf[2] = index-6;
g_ProData.SendBuf.Len = index;
g_ProData.SendBuf.Addr = g_ProData.SendFrame.RfAddr;
Send();
}
///
集中器部分代码
#define ID_ROUTE_NUM 30
#define ROUTING_LAYERS 3
#define REGISTER_ID_NUM 5
typedef enum
{
UPLOAD_TERMINAL_RELAY = 0x01;
}COMMAND_OPERATION_TYPE_E; // 命令操作类型
typedef enum
{
UPLOAD_MESSAGE_FIRST_RELAY = 0x10,
UPLOAD_MESSAGE_SECOND_RELAY = 0x11,
}COMMAND_PROCESSING_E; // 命令处理
typedef enum
{
COMMUNICATION_NONE,
COMMUNICATION_NETWORK,
}COMMUNICATION_STATE_E; //
typedef struct
{
uint8_t DevType; /* 设备类型*/
uint32_t DevAddr; /* 设备地址 */
uint8_t TypeId; /* 操作类型 例如遥控 遥测*/
uint8_t Func; /* 功能码,例如具体遥控什么*/
uint8_t RelayNum;//经过中继个数
uint32_t RfAddr; /* RF 地址*/
uint8_t Data[16]; /* 数据域 */
uint8_t Len; /* data数据报文长度 */
}MESSAGE_FORMAT_T; // message format 报文格式
typedef struct
{
uint8_t Buf[64];
uint8_t Len; /* 数据长度*/
uint32_t Addr; /* 远方地址*/
}MESSAGE_BUFFER;
typedef struct
{
BYTE Cache[MACRO_RFPROC_RECV_CACHE_SIZE];
CircleQueue Queue;
}MESSAGE_QUEUE;
typedef struct
{
uint32_t Id[ROUTING_LAYERS];//接收的ID
uint8_t Rssi[ROUTING_LAYERS];//信号强度
}RECV_TERMINAL_T;
typedef struct
{
uint32_t Id;//ID设备号
uint8_t index;//
uint8_t relay;//
}TERMINAL_ID_LOCATION_T;
typedef struct
{
TERMINAL_ID_LOCATION_T TerminalId[REGISTER_ID_NUM]; // 注册的终端ID 由配置传入
uint8_t Num; // 注册终端ID个数 由配置传入
uint8_t index; // 检测注册终端第index个是否收到心跳
uint8_t Route;
uint8_t NetworkFlag;// 开启组网标志
}REGISTER_TERMINAL_T;
typedef struct
{
RECV_TERMINAL_T RecvTerminal[ ID_ROUTE_NUM];
REGISTER_TERMINAL_T Register;
COMMUNICATION_STATE_E CommunicationState;
MESSAGE_FORMAT_T SendFrame; /* 发送帧数据*/
MESSAGE_FORMAT_T RecvFrame; /* 接收存放帧*/
MESSAGE_BUFFER SendBuf;
MESSAGE_QUEUE RecvQueue;
}PROTOCOL_T;
extern __no_init PROTOCOL_T g_ProData;
/*
* 函数名称 : Unpack10
* 函数功能 : 解包
* 输入参数 : 无
* 输出参数 : 无
* 返 回 值 : 成功返回true 失败返回FALSE
* 其它说明 : 解包与终端填充方式不同
* */
static bool Unpack10(void)
{
uint8_t cs = 0;
uint32_t RfAddr = 0;
........................
RfAddr = RF436_GetULongFromCache(&g_ProData.RecvQueue.Queue,2,3);
SX1276_GetRecvRSSI();
for(int j = 1;j < g_ProData.Register.Route;j++)
{
if(RfAddr == g_ProData.RecvTerminal[j].Id[0]) // 已存在 刷新数据
{
g_ProData.RecvTerminal[j].Id[0] = RfAddr;
g_ProData.RecvTerminal[j].Rssi[0] = g_Sx1276.LoraParam.Rssi;
CircleQueue_Remove(&g_ProData.RecvQueue.Queue,7);/* 读取完成一包,下移一包*/
return TRUE;
}
}
// 不存在 增加填表
g_ProData.RecvTerminal[g_ProData.Register.Route].Id[0] = RfAddr;
g_ProData.RecvTerminal[g_ProData.Register.Route].Rssi[0] = g_Sx1276.LoraParam.Rssi;
for(int j = 0;j < g_ProData.Register.Num;j++) // 填写直连通信路径
{
if(RfAddr == g_ProData.Register.TerminalId[j].Id )
{
g_ProData.Register.TerminalId[j].index = g_ProData.Register.Route;
g_ProData.Register.TerminalId[j].relay = 0;
}
}
g_ProData.Register.Route++;
CircleQueue_Remove(&g_ProData.RecvQueue.Queue,7);/* 读取完成一包,下移一包*/
return TRUE;
}
/*
* 函数名称 :
* 函数功能 : 写入绑定终端ID
* 输入参数 : 无
* 输出参数 : 无
* 返 回 值 :
* 其它说明 : 无
* */
static void RegisterId(void)
{
g_ProData.Register.Num = 5;
g_ProData.Register.TerminalId[0].Id = 0xA00001;
g_ProData.Register.TerminalId[1].Id = 0xA00002;
g_ProData.Register.TerminalId[2].Id = 0xA00003;
g_ProData.Register.TerminalId[3].Id = 0xA00004;
g_ProData.Register.TerminalId[4].Id = 0xA00005;
}
void StartNetwork(void)
{
g_Data.Run.NetWorkTime = 120000; //120s
g_Data.Run.Section1time = 30000;
}
/*
* 函数名称 : NetworkContinueTime
* 函数功能 : 组网持续时间
* 输入参数 : 无
* 输出参数 : 无
* 返 回 值 :
* 其它说明 :
* */
static void NetworkContinueTime(void)
{
if(g_Data.Run.NetWorkTime)
{
g_ProData.Register.NetworkFlag = true;
}
else
{
g_ProData.Register.NetworkFlag = false;
}
}
/*
* 函数名称 :
* 函数功能 :
* 输入参数 : 无
* 输出参数 : 无
* 返 回 值 :
* 其它说明 :
* */
static void CheckAndCallTerminal(uint8_t i)
{
if(g_ProData.Register.TerminalId[i].index == 0)// 判断是否存在有注册ID没收到心跳
{
if(g_Data.Run.Section1time)
{
Level1relay(); // 发送一级中继召唤
}
else
{
g_Data.Run.Section2time = 30000;
}
// 等待30s 后进行二级中继召唤
if(g_Data.Run.Section2time)// 阶段二
{
Level2relay(); // 发送二级中继召唤
}
}
}
/*
* 函数名称 : NetWorking
* 函数功能 : 组网
* 输入参数 : 无
* 输出参数 : 无
* 返 回 值 :
* 其它说明 : 间隔5分钟,或者在长时间无接收到某终端数据后进行调用
组网时,阶段时间切换 3个时间,1、一级中继召唤时间 2、二级中继召唤时间 3、组网共花费时间
* */
static void NetWorking(void)
{
if(g_Data.Run.SystemTick < 15000) // 开机后接收15s心跳后在进行组网
{
return;
}
NetworkContinueTime();
if((!g_Data.Run.DetectionTime) && (g_ProData.Register.NetworkFlag))
{
if(g_ProData.Register.index < g_ProData.Register.Num)
{
CheckAndCallTerminal(g_ProData.Register.index);
g_ProData.Register.index++;
}
else
{
g_ProData.Register.index = 0;
}
g_Data.Run.DetectionTime = 3000; // 开启组网后 3s一次进入检测组网
}
}