找回密码
 注册
关于网站域名变更的通知
查看: 267|回复: 1
打印 上一主题 下一主题

LORA 射频自组网 两级中继

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2021-9-24 15:25 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
基于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.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.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.Id[1]=Idrecv,


RecvTerminal.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;
    }
    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.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;/* pframedata;,..... */
    }
    for(i = 4;i < index;i++)
    {
        cs += g_ProData.SendBuf.Buf;
    }   
    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.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一次进入检测组网
    }
}


该用户从未签到

2#
发表于 2021-9-24 15:42 | 只看该作者
lora调制方式,发送数据为星型通信方式
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

EDA365公众号

关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

GMT+8, 2025-6-20 15:37 , Processed in 0.093750 second(s), 23 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表