Zookeeper 国外镜像 软件测试工程师 jquery ios7 jackson Modernizr Validator HammerJS vue中文 vue表单提交 change事件 mysql安装后怎么使用 python编程练习题 html好看的字体 mysql查询结果拼接 mysql更新 python的str python的open函数 python或运算 python中的map函数 java数据 java运行环境配置 java开发教程 java课程 java文档 linux系统命令大全 linux硬盘 服务器系统下载 忧思华光玉 图解深度学习 wine模拟器 eml文件阅读器下载 hexworkshop 英雄联盟体验服转换器 dnf男柔道加点 野德天赋 mathcad15 ip地址转换器 批量插入数据
当前位置: 首页 > 学习教程  > 

物联网之LoRa开发与应用六(LoRa自组网络设计)

2020/10/16 17:46:05 文章标签: lora开发

深入了解LoRaWAN 内容概要: 1、LoRaWAN概述 2、LoRaWAN终端(重点掌握) 3、LoRaWAN服务器 LoRaWAN是什么: LoRaWAN采用星型无线拓扑:End Nodes(节点)、Gateway(网关)…

深入了解LoRaWAN

内容概要

1、LoRaWAN概述

2、LoRaWAN终端(重点掌握

3、LoRaWAN服务器

LoRaWAN是什么

LoRaWAN采用星型无线拓扑:End Nodes(节点)、Gateway(网关)、Network Server(网络服务器)、Application Server(应用服务器)

LoRaWAN通信协议

低功耗、可扩展、高服务质量、安全的长距离无线网络

LoRaWAN通信机制

LoRaWAN与其他组网协议对比

LoRaWAN网关SX1301:(8通道的LoRa接口用于LoRa节点的接入、一个FSK接口用于FSK节点的接入、1个LoRa网关间通讯的接口

大容量的网络规模、高速度的通信机制

有三种节点类型:Class A、Class B、Class C

LoRaWAN终端Class A:(平时处于休眠模式,当他需要工作的时候才会去发送数据包,所以功耗比较低。但是实时性较差,间隔一段时间才能下行通信

LoRaWAN终端Class B:(当需要节点去响应实时性问题的时候,首先网关会发送一个信标,告诉节点要加快通讯,快速工作,节点收到信标之后,会在128秒内去打开多个事件窗口,每个窗口在3-160ms,在128秒内可以实时对节点进行监控

LoRaWAN终端Class C:(如果不发送数据的情况下,节点一直打开接收窗口,既保证了实时性,也保证了数据的收发,但是功耗非常高

LoRaWAN服务器框架

LoRaWAN服务器通信接口

LoRaWAN服务器通信协议

LoRa自组网架构设计

内容概要

1、MAC协议设计

2、LoRa自组网协调器设计

3、LoRa自组网节点设计

MAC协议重要性:解决信号冲突的问题、尽可能地节省电能、保证通信的健壮和稳定性

MAC协议种类

1、信道划分的MAC协议:时分(TDMA)、频分(FDMA)、码分(CDMA)

2、随机访问MAC协议:

     ALOHA,S-ALOHA,CSMA,CSMA/CD

     CSMA/CD应用于以太网

     CSMA/CA应用于802.11无线局域网

3、轮讯访问MAC协议:

     主节点轮询

     工业Modbus通信协议

时分复用:(在一定的事件内去分配时间槽,每个时间槽分给一个节点,使节点在这个时间槽里通信,如果不在这个时间槽是不能通信的。和电脑CPU的时间片是一个道理

优点:节省电能、最大化使用带宽

缺点:所有节点需要精确的时钟源,并且需要周期性校时;

          向网络中添加和删除节点都要有时隙分配和回收算法。

频分复用:(CPU是多核,多任务同时进行:不同频率的通信可以同时进行

优点:增加通信容量、提高通信可靠性

缺点:物理通道增加,成本增加

轮询访问

优点:协议简单,易开发

缺点:通讯效率低、网络规模小(只能接入1-247个节点)

基于时分复用LoRa自组网设计

入网机制:(随机访问,竞争入网)

时分复用:(每个节点在规定的时间槽内通信)

 LoRa自组网协调器设计

LoRa自组网节点设计

LoRa自组网集中器程序开发

内容概要

1、通信协议

2、工程修改

3、搭建框架

4、源码分析

通信协议

 

LoRa自组网协调器设计

根据协调器业务流程需要在之前工程里添加两个外设:定时器(用于节点入网超时的判断,后面有配置说明)、RTC(实时时钟,用于时钟同步,后面有配置说明)

IAR工程修改

添加外设需要修改STM32CubeMX工程,需要把我们编写的代码放在BEGIN和END中间

RTC外设配置

1、修改RTC时钟源为外部高速时钟

2、配置RTC分频系数

3、初始化日期和时间

4、配置Alarm参数

5、使能RTC全局中断

定时器外设配置

1、配置TIM2分频系数

2、使能TIM2定时器中断

搭建框架

1、RTC任务

2、定时器任务

3、通信协议

4、数据处理任务

5、网络处理任务

RTC任务

1、RTC初始化

    /**Initialize RTC and set the Time and Date 
    */
  sTime.Hours = startUpDateHours;
  sTime.Minutes = startUpDateMinute;
  sTime.Seconds = startUpDateSeconds;
  sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  sTime.StoreOperation = RTC_STOREOPERATION_RESET;

 /**Enable the Alarm A 
    */
  sAlarm.AlarmTime.Hours = DataUpTimeHours;
  sAlarm.AlarmTime.Minutes = DataUpTimeMinute;
  sAlarm.AlarmTime.Seconds = DataUpTimeSeconds;
  sAlarm.AlarmTime.SubSeconds = DataUpTimeSubSeconds;
  sAlarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  sAlarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET;
  sAlarm.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY;
  sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;
  sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
  sAlarm.AlarmDateWeekDay = 0x1;
  sAlarm.Alarm = RTC_ALARM_A;
  memcpy(&gAlarm, &sAlarm, sizeof(sAlarm));

2、Alarm中断任务

//**********************************//
//
//函数名称:HAL_RTC_AlarmAEventCallback   
//
//函数描述: 闹钟事件回调函数  
//
//函数参数:   RTC_HandleTypeDef *hrtc
//
//返回值:    无 
//
//创建者:    
//*******************************//

void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
  
  RTC_TimeTypeDef masterTime;
  RTC_TimeTypeDef SlaveTime;
  RTC_DateTypeDef masterDate;
  
#if MASTER  
  //置位同步时钟标志
    SendClockFlag = 0;
  //获取下次闹钟时间
    HAL_RTC_GetTime(hrtc, &masterTime, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(hrtc, &masterDate, RTC_FORMAT_BIN);
    gAlarm.AlarmTime.Hours = masterTime.Hours + CLOCKHOURS;
    gAlarm.AlarmTime.Minutes = masterTime.Minutes;
    gAlarm.AlarmTime.Seconds = masterTime.Seconds;
    gAlarm.AlarmTime.SubSeconds = masterTime.SubSeconds;
    
#else
    sendUpDataFlag = 1;
    HAL_RTC_GetTime(hrtc, &SlaveTime, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(hrtc, &masterDate, RTC_FORMAT_BIN);
    gAlarm.AlarmTime.Hours = SlaveTime.Hours + DataUpTimeHours;
    gAlarm.AlarmTime.Minutes = SlaveTime.Minutes + DataUpTimeMinute;
    gAlarm.AlarmTime.Seconds = SlaveTime.Seconds + DataUpTimeSeconds;
    gAlarm.AlarmTime.SubSeconds = SlaveTime.SubSeconds + DataUpTimeSubSeconds;
#endif
    
    
    if (gAlarm.AlarmTime.Seconds > 59)
    {
	   gAlarm.AlarmTime.Seconds -= 60;
	   gAlarm.AlarmTime.Minutes += 1;
    }

   if ( gAlarm.AlarmTime.Minutes >59)
   {
	   gAlarm.AlarmTime.Minutes -= 60;
	   gAlarm.AlarmTime.Hours += 1;
   }
   if (gAlarm.AlarmTime.Hours > 23)
   {
	   gAlarm.AlarmTime.Hours -= 24;
   }
    
   printf("RTC\n");
  //使能闹钟中断
    if (HAL_RTC_SetAlarm_IT(hrtc, &gAlarm, RTC_FORMAT_BIN) != HAL_OK)
    {
      _Error_Handler(__FILE__, __LINE__);
    }
}

//**********************************//
//
//函数名称:   GetTimeHMS
//
//函数描述:   时分秒转换
//
//函数参数:   uint32_t timeData,uint8_t *hours,uint8_t *minute,uint8_t *seconds,uint32_t *subSeconds
//
//返回值:     无
//
//创建者:     
//*******************************//

void GetTimeHMS(uint32_t timeData,uint8_t *hours,uint8_t *minute,uint8_t *seconds,uint32_t *subSeconds) 
{
	/* 获得亚秒 */
	*subSeconds = timeData % 1000;
	/* 获得秒钟*/
	timeData = timeData / 1000;
	*seconds = timeData % 60;
	/* 获得分钟*/
	timeData = timeData / 60;
	*minute = timeData % 60;
	/* 获得小时 */
	*hours = timeData / 60;
}

定时器任务

1、定时器初始化:CubeMX重初始化已经完成,这里不需要修改

2、定时器中断任务

//**********************************//
//
//函数名称:   HAL_TIM_PeriodElapsedCallback
//
//函数描述:   定时器2溢出中断回调函数
//
//函数参数:   TIM_HandleTypeDef *htim
//
//返回值:     无
//
//创建者:     
//*******************************//

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
//判断是否为定时器2中断
//累加全局计数值
  if(htim->Instance == htim2.Instance)
  {
  
    JionNodeTimeCount++;
  }
}

通信协议

1、CRC8校验函数

/* protocol.c */

#include "protocol.h"

/******************************************************************************
* Name:    CRC-8               x8+x2+x+1
* Poly:    0x07
* Init:    0x00
* Refin:   False
* Refout:  False          函数功能:生成CRC代码
* Xorout:  0x00
* Note:
*****************************************************************************/
uint8_t crc8(uint8_t *data, uint8_t length)
{
  uint8_t i;
  uint8_t crc = 0;        // Initial value
  while(length--)
  {
    crc ^= *data++;        // crc ^= *data; data++;
    for ( i = 0; i < 8; i++ )
    {
      if ( crc & 0x80 )
        crc = (crc << 1) ^ 0x07;
      else
        crc <<= 1;
    }
  }
  return crc;
}

//**********************************//
//
//函数名称:   DataCrcVerify
//
//函数描述:   CRC8校验
//
//函数参数:   uint8_t * buff, uint8_t len
//
//返回值:     uint8_t
//
//创建者:   
//*******************************//

uint8_t DataCrcVerify(uint8_t * buff, uint8_t len)
{
	uint8_t Crc8Data = 0;

	//验证数据是否正确 
	Crc8Data = crc8(buff, len - 1);

	if (Crc8Data == buff[len - 1])
	{
// 		PRINTF1("CRC8 Success!\n");
		return 1;
	}
	else
	{
//		PRINTF1("CRC8 Failed!\n");
		return 0;
	}
}

2、协议数据结构

数据处理任务:(先了解大致框架,后面具体分析

串口任务

    -->串口接收

无线任务

    -->无线接收

        -->主机协议解析

            -->网络数据包解析

            -->入网请求解析

/* dataprocess.c */

#include "dataprocess.h"
#include "usart.h"
#include "led.h"
#include "protocol.h"
#include "rtc.h"

#include "string.h"
#include "stdio.h"
//sx1278
#include "platform.h"
#include "radio.h"
#include "sx1276-Hal.h"
#include "sx1276-LoRa.h"
#include "sx1276-LoRaMisc.h"

extern uint16_t BufferSize;
extern uint8_t Buffer[BUFFER_SIZE];

#if defined(MASTER)
extern uint8_t EnableMaster;

#elif defined(SLAVE)

extern uint8_t EnableMaster;

#endif

extern tRadioDriver *Radio;

extern uint32_t Master_RxNumber;
extern uint32_t Master_TxNumber;

extern uint32_t Slave_RxNumber;
extern uint32_t Slave_TxNumber;

extern volatile uint8_t SendDataOkFlag;

uint8_t startUpDateHours = 0;
uint8_t startUpDateMinute = 0;
uint8_t startUpDateSeconds = 0;
uint16_t startUpDateSubSeconds = 0;

//Master存储入网的设备信息
SlaveInfo slaveNetInfo_t[NodeNumber];

//Salve入网信息包
SlaveJionNet jionPacke_t;

//Salve保存自己的地址
SlaveInfo slaveNativeInfo_t;
//节点数据
SlaveDataNet DataPacke_t;


//**********************************//
//
//函数名称:UartDmaGet   
//
//函数描述:串口数据获取   
//
//函数参数:   无
//
//返回值:     无
//
//创建者:     
//*******************************//

void UartDmaGet(void)
{
  if(UsartType1.receive_flag == 1)//如果过新的数据
  {

    //串口接收到的数据原封发给SX1278
    Radio->SetTxPacket(UsartType1.usartDMA_rxBuf, UsartType1.Usart_rx_len);
    
    memset(UsartType1.usartDMA_rxBuf,0,UsartType1.Usart_rx_len);
    UsartType1.receive_flag = 0; //接收数据标志清零,
  }
}

//**********************************//
//
//函数名称:  RxDataPacketNum 
//
//函数描述:  接收数据包计数 
//
//函数参数:   无
//
//返回值:     无
//
//创建者:     
//*******************************//

void RxDataPacketNum(void)
{
  if(EnableMaster == true)
    Master_RxNumber++;
  else
    Slave_RxNumber++;
}
//**********************************//
//
//函数名称:   TxDataPacketNum
//
//函数描述:   发送数据包计数
//
//函数参数:  无 
//
//返回值:     无
//
//创建者:    
//*******************************//

void TxDataPacketNum(void)
{
  if(EnableMaster == true)
    Master_TxNumber++;
  else
    Slave_TxNumber++;
}
//**********************************//
//
//函数名称:  Sx127xDataGet 
//
//函数描述:   读取sx127x射频射频数据
//
//函数参数:   无
//
//返回值:     无
//
//创建者:  
//*******************************//

uint8_t Sx127xDataGet(void)
{
  uint8_t status = 0;
  switch( Radio->Process( ) )
  {
  case RF_RX_TIMEOUT:
    printf("RF_RX_TIMEOUT\n");
    break;
  case RF_RX_DONE:
    Radio->GetRxPacket( Buffer, ( uint16_t* )&BufferSize );
    if(EnableMaster == true)
      printf("master Rx Len = %d\n",BufferSize);
    else
      printf("slave Rx Len = %d\n",BufferSize);      
    if( BufferSize > 0 )//&& (BufferSize == strlen((char*)Buffer)))
    {
      //接收数据闪烁
      LedBlink( LED_RX );
      //计算接收数据的个数
      RxDataPacketNum();

      //清空sx127x接收缓冲区
#ifdef MASTER
      status = MasterProtocolAnalysis(Buffer,BufferSize);
#else      
      status = SlaveProtocolAnalysis(Buffer, BufferSize);
#endif
      memset(Buffer,0,BufferSize);
    }            
    break;
  case RF_TX_DONE:
    //发送闪烁
    LedBlink( LED_TX );
    //计算发送数据的个数
    TxDataPacketNum();
    Radio->StartRx( );
    SendDataOkFlag = 1;
    break;
  case RF_TX_TIMEOUT:
    printf("RF_TX_TIMEOUT\n");
    break; 
  default:
    break;
  }
  return status;
}


//**************从**************//
//**************机**************//



//**********************************//
//
//函数名称:   SendJionNetPacke
//
//函数描述:   从机入网数据发送
//
//函数参数:   无
//
//返回值:     无
//
//创建者:     
//*******************************//

void SendJionNetPacke(void)
{
  uint16_t addr = ADDR;
    jionPacke_t.msgHead = 0x3C;
    jionPacke_t.dataLength = 0x06;
    jionPacke_t.netType = 'J';
    jionPacke_t.netPanid[0] = HI_UINT16(PAN_ID);
    jionPacke_t.netPanid[1] = LO_UINT16(PAN_ID);
    jionPacke_t.deviceAddr[0] = HI_UINT16(ADDR);
    jionPacke_t.deviceAddr[1] = LO_UINT16(ADDR);
    //校验码
    jionPacke_t.crcCheck = crc8((uint8_t *)&jionPacke_t,jionPacke_t.dataLength + 1);
    
    printf("SendJionNetPacke addr = %d\n",addr);
    //发送数据包
    Radio->SetTxPacket((uint8_t *)&jionPacke_t, jionPacke_t.dataLength + 2);
}

//**********************************//
//
//函数名称:   SlaveProtocolAnalysis
//
//函数描述:   从机协议解析
//
//函数参数:   uint8_t *buff,uint8_t len
//
//返回值:     uint8_t
//
//创建者:    
//*******************************//

uint8_t SlaveProtocolAnalysis(uint8_t *buff,uint8_t len)
{
  uint8_t Crc8Data;
  
  printf("SlaveProtocolAnalysis\n");
  for (int i = 0; i < len; i++)
  {
    printf("0x%x  ",buff[i]);
  }
  printf("\n");
  
  if (buff[0] == NETDATA)
  {
    if (buff[1] == HI_UINT16(PAN_ID) && buff[2] == LO_UINT16(PAN_ID))
    {
      Crc8Data = crc8(&buff[3], len - 4);
      
      if (Crc8Data != buff[len - 1])
      {
        memset(buff, 0, len);
        return 0;
      }
      if (buff[3] == 0x21)
      {
        printf("Slave_NETDATA\n");
      }
      return 0;
    }
  }
  else if((buff[0] == 0x3C) && (buff[2] == 'A'))
  {
    if (DataCrcVerify(buff, len) == 0)
    {
      return 0;
    }
    if (buff[3] == HI_UINT16(PAN_ID) && buff[4] == LO_UINT16(PAN_ID))
    {
      if (buff[5] == jionPacke_t.deviceAddr[0] && buff[6] == jionPacke_t.deviceAddr[1])
      {
        slaveNativeInfo_t.deviceId = buff[7];
        printf("Slave_ACK\n");
        return 0xFF;
      }
    }
  }
  else if((buff[0] == 0x3C) && (buff[2] == 'T'))
  {
    if (DataCrcVerify(buff, len) == 0)
    {
      return 0;
    }
    if (buff[3] == HI_UINT16(PAN_ID) && buff[4] == LO_UINT16(PAN_ID))
    {
      uint32_t alarmTime = 0;
      startUpTimeHours = buff[5];
      startUpTimeMinute = buff[6];
      startUpTimeSeconds = buff[7];
      startUpTimeSubSeconds = buff[8] <<8 | buff[9];
      printf("Slave_CLOCK\n");
      printf("H:%d,M:%d,S:%d,SUB:%d\n", startUpTimeHours, startUpTimeMinute, startUpTimeSeconds, startUpTimeSubSeconds);
      alarmTime = ((DataUpTimeHours * 60 + DataUpTimeMinute) * 60 
                   + DataUpTimeSeconds) * 1000 + (DataUpTimeSubSeconds / 2) + DataUpTime;
      GetTimeHMS(alarmTime, &DataUpTimeHours, &DataUpTimeMinute, &DataUpTimeSeconds, &DataUpTimeSubSeconds);
      printf("DataUpTime->H:%d,M:%d,S:%d,SUB:%d\n", DataUpTimeHours, DataUpTimeMinute, DataUpTimeSeconds, DataUpTimeSubSeconds);
      //使能RTC
      MX_RTC_Init();
     return 0xFF;
    }
  }
  return 1;
}

//**********************************//
//
//函数名称:   SendSensorDataUP
//
//函数描述:   上传节点传感器数据
//
//函数参数:   无
//
//返回值:     无
//
//创建者:     
//*******************************//

void SendSensorDataUP(void)
{
    
  printf("SendSensorDataUP\n");
  DataPacke_t.netmsgHead = 'N';
  DataPacke_t.netPanid[0] = HI_UINT16(PAN_ID);
  DataPacke_t.netPanid[1] = LO_UINT16(PAN_ID);
  DataPacke_t.msgHead = 0x21;
  DataPacke_t.dataLength = 0x09;
  DataPacke_t.dataType = 0;
  DataPacke_t.deviceAddr[0] = HI_UINT16(ADDR);
  DataPacke_t.deviceAddr[1] = LO_UINT16(ADDR);
  DataPacke_t.sensorType  = 0x1;
  DataPacke_t.buff[0]  = 0x1;
  DataPacke_t.buff[1]  = 0x2;
  DataPacke_t.buff[2]  = 0x3;
  DataPacke_t.buff[3]  = 0x4;
    
  //校验码
  DataPacke_t.crcCheck = crc8((uint8_t *)&DataPacke_t,DataPacke_t.dataLength + 4);
  //发送数据包
  Radio->SetTxPacket((uint8_t *)&DataPacke_t, DataPacke_t.dataLength + 5);
}


//**************主**************//
//**************机**************//



//**********************************//
//
//函数名称:   MasterProtocolAnalysis
//
//函数描述:   主机协议解析
//
//函数参数:   uint8_t *buff,uint8_t len
//
//返回值:     uint8_t
//
//创建者:    
//*******************************//

uint8_t MasterProtocolAnalysis(uint8_t *buff,uint8_t len)
{
  uint8_t Crc8Data,deviceID;
  
  uint8_t SendAck[12];
  
  printf("MasterProtocolAnalysis\n");
  for (int i = 0; i < len; i++)
  {
    printf("0x%x  ",buff[i]);
  }
  printf("\n");
  
  if(buff[0] == NETDATA)
  {
    if((buff[1] == HI_UINT16(PAN_ID))&&(buff[2] == LO_UINT16(PAN_ID)))
    {
      Crc8Data = crc8(&buff[0], len - 1); //减去校验
      if(Crc8Data != buff[len - 1])
      {
        memset(buff,0,len);//清空缓存区
        
        return 0;
      }
      
      if(buff[3] == DATAHEAD)
      {
      
        NetDataProtocolAnalysis(&buff[3], len - 3);
      }
    }
    else
      return 0;
  }
  else if(buff[0] == JIONREQUEST)
  {
      deviceID = JionNetProtocolAnalysis(buff, len);
      printf("deviceID = %d\n",deviceID);
      
      if(deviceID >= 0)
      {
        SendAck[0] = JIONREQUEST;
        SendAck[1] = 1;
        SendAck[2] = 'A';
        SendAck[3] = HI_UINT16(PAN_ID);
        SendAck[4] = LO_UINT16(PAN_ID);
        SendAck[5] = slaveNetInfo_t[deviceID].deviceAddr[0];
        SendAck[6] = slaveNetInfo_t[deviceID].deviceAddr[1];
        SendAck[7] = deviceID;
        SendAck[8] = crc8(SendAck, 8);
        Radio->SetTxPacket(SendAck, 9);
        printf("MasterAck\n");
        for (int i = 0; i < 9; i++)
        {
          printf("0x%x  ",SendAck[i]);
        }
        printf("\n");
      }
  }
  return 1;
}



//**********************************//
//
//函数名称:   JionNetProtocolAnalysis
//
//函数描述:   入网协议解析
//
//函数参数:   uint8_t *buff,uint8_t len
//
//返回值:     uint8_t
//
//创建者:    
//*******************************//

uint8_t JionNetProtocolAnalysis(uint8_t *buff,uint8_t len)
{
  uint8_t i = 0, dataLen = 0;
  uint8_t status = 0, lenOld = len;
  
  printf("JionNetProtocolAnalysis\n");
  for (int i = 0; i < len; i++)
  {
    printf("0x%x  ",buff[i]);
  }
  printf("\n");
  while(len--)
  {
    switch(status)
    {
      case JION_HEADER:
        if (buff[status] == JIONREQUEST)
        {
          status = JION_LENGHT;
        } 
        else
        {
          goto ERR;
        }
      break;
      case JION_LENGHT:
       if (buff[status] == 0x06)
        {
          status = JION_TYPE;
        } 
        else
        {
          goto ERR;
        }
      break;
      case JION_TYPE:
        if (buff[status] == 'J')
        {
          status = JION_PANID;
        } 
        else
        {
          goto ERR;
        }
      break;
      case JION_PANID:
        if (buff[status] == HI_UINT16(PAN_ID) && buff[status + 1] == LO_UINT16(PAN_ID))
        {
          status = JION_ADDR;
        } 
        else
        {
          goto ERR;
        }
      break;
      case JION_ADDR:
      //旧节点加入
        for (i = 0; i < currentDeviceNumber; i++)
        {
          if ((slaveNetInfo_t[i].deviceAddr[0] == buff[status + 1]) &&
                (slaveNetInfo_t[i].deviceAddr[1] == buff[status + 2]))
          {
            slaveNetInfo_t[i].deviceNetStatus = AGAIN_JION_NET;
            status = JION_CRC;  
            printf("AGAIN_JION_NET i = %d\n",i);
            printf("deviceId=%x\n",slaveNetInfo_t[i].deviceId);
            printf("deviceAddr[0]=%x\n",slaveNetInfo_t[i].deviceAddr[0]);
            printf("deviceAddr[1]=%x\n",slaveNetInfo_t[i].deviceAddr[1]);
            break;
          }
        }
        //新节点加入
        if(i == currentDeviceNumber)
        {
          currentDeviceNumber++;//新增加入节点
          slaveNetInfo_t[i].deviceId = i;
          slaveNetInfo_t[i].deviceAddr[0] = buff[status + 1];
          slaveNetInfo_t[i].deviceAddr[1] = buff[status + 2];
          status = JION_CRC;
          printf("CURRENT_JION_NET i = %d\n",i);
          printf("deviceId=%x\n",slaveNetInfo_t[i].deviceId);
          printf("deviceAddr[0]=%x\n",slaveNetInfo_t[i].deviceAddr[0]);
          printf("deviceAddr[1]=%x\n",slaveNetInfo_t[i].deviceAddr[1]);
        }
      break;
      case JION_CRC:
      //更新节点入网状态
            if (slaveNetInfo_t[i].deviceNetStatus != AGAIN_JION_NET)  
            {
              slaveNetInfo_t[i].deviceNetStatus = JIONDONE;
              status = JION_HEADER;
              printf("JIONDONE i = %d\n",i);
              printf("deviceId=%x\n",slaveNetInfo_t[i].deviceId);
              printf("deviceAddr[0]=%x\n",slaveNetInfo_t[i].deviceAddr[0]);
              printf("deviceAddr[1]=%x\n",slaveNetInfo_t[i].deviceAddr[1]);
            
            }
      break;
      default:
      break;
    }
  }
  return i;

ERR:
  memset(buff, 0, lenOld);
  status = JION_HEADER;
  return -1;
}

//**********************************//
//
//函数名称:   NetDataProtocolAnalysis
//
//函数描述:   网络数据包解析
//
//函数参数:   uint8_t *buff,uint8_t len
//
//返回值:   无  
//
//创建者:    
//*******************************//

void NetDataProtocolAnalysis(uint8_t *buff,uint8_t len)
{
  printf("NetDataProtocolAnalysis\n");
  for (int i = 0; i < len; i++)
  {
    printf("0x%x  ",buff[i]);
  }
  printf("\n");
}

网络处理任务:(先了解大致框架,后面具体分析

1、等待入网完成

2、主机发送时钟同步数据包

/* netprocess.c */

#include "netprocess.h"
#include "dataprocess.h"
#include "tim.h"
#include "rtc.h"
#include "adc.h"
#include "protocol.h"

#include "math.h"
#include "stdlib.h"
#include "string.h"
#include "stdio.h"
//sx1278
#include "platform.h"
#include "radio.h"
#include "sx1276-Hal.h"
#include "sx1276-LoRa.h"
#include "sx1276-LoRaMisc.h"

//所有节点的更新周期(在Time内上传所有数据) 单位Ms
volatile uint32_t DataUpTimePeriod = 1000 *  60 * 1;	//1分钟

volatile static uint32_t currentTime = 0;
//当前加入设个的个数
volatile  uint16_t currentDeviceNumber = 0;
//保存当前加入节点
volatile  uint16_t oldNodeNumber = 0;
//节点时间片
volatile uint32_t DataUpTime = 0;

//节点入网状态
volatile DeviceJionFlag JionNodeTimeOutFlag = No_Node_Jion_Flag;

uint8_t startUpTimeHours = 0;
uint8_t startUpTimeMinute = 0;
uint8_t startUpTimeSeconds = 0;
uint32_t startUpTimeSubSeconds = 0;

uint8_t DataUpTimeHours = 0;
uint8_t DataUpTimeMinute = 0;
uint8_t DataUpTimeSeconds = 0;
uint32_t DataUpTimeSubSeconds = 0;

//时钟同步
SlaveRtcSync rtcSync_t;

//初始化网络状态
volatile DeviceJionStatus NetStatus = NO_JION;

extern tRadioDriver *Radio;


//**************从**************//
//**************机**************//


//**********************************//
//
//函数名称:   RandomNumber
//
//函数描述:   生成随机数
//
//函数参数:   无
//
//返回值:     随机数
//
//创建者:    
//*******************************//

uint16_t RandomNumber(void)
{
    uint16_t randNumber = 0;
    float adcValue = 0;
    uint32_t u32adcValue = 0;
    
    //开启DMA转换ADC
    HAL_ADC_Start_DMA(&hadc, (uint32_t*)ADC_DMA_Value, ADC_NUM);
    
    HAL_Delay(100);
//    printf("ADC_DMA_Value[0] = %d\n",ADC_DMA_Value[0]);
//    printf("ADC_DMA_Value[1] = %d\n",ADC_DMA_Value[1]);
//    printf("ADC_DMA_Value[2] = %d\n",ADC_DMA_Value[2]);
//    printf("ADC_DMA_Value[3] = %d\n",ADC_DMA_Value[3]);
//    printf("ADC_DMA_Value[4] = %d\n",ADC_DMA_Value[4]);
    //转换为mv值
    adcValue = ADC_DMA_Value[ADC_IN5];
    adcValue = (adcValue * 3300) / 4096;  
    
    printf("adcValue = %f\n",adcValue);
    
    u32adcValue = (uint32_t)((adcValue-floor(adcValue))*1000000);
    printf("u32adcValue = %d\n",u32adcValue);
    //获取随机数
    srand(u32adcValue);
    for(int i = 0;i< 10;i++)
    randNumber += (uint8_t)rand();
    return randNumber;
}

//**********************************//
//
//函数名称:   SlaveJionNetFuction
//
//函数描述:   从机加入网络
//
//函数参数:   无
//
//返回值:     入网状态
//
//创建者: 
//*******************************//

uint8_t SlaveJionNetFuction(void)
{
  switch(NetStatus)
  {
    case NO_JION:
          SendJionNetPacke();
          //if(Radio->Process( ) == RF_TX_DONE)
          NetStatus = JIONING;
          currentTime = HAL_GetTick();
    break;
    case JIONING:
          if(Sx127xDataGet() == 0xFF)
          {
            NetStatus = JIONDONE;
            printf("Slave_JIONDONE\n");
          }
          else
          {
            if ((HAL_GetTick() - currentTime) > 6000)
            NetStatus = JIONTIMEOUT;
          }
    break;
    case JIONTIMEOUT:
        NetStatus = NO_JION;
    break;
    case JIONDONE:
          Radio->StartRx();
          return 0;
    break;
    default:
    break;
  }
return 1;

}
//**********************************//
//
//函数名称:   SlaveGetSendTime
//
//函数描述:   节点获取时间片
//
//函数参数:   无
//      
//返回值:     无     
//
//创建者:   
//*******************************//

void SlaveGetSendTime(void)
{
  float TransTimeUP = 0;		//数据传输时间
  TransTimeUP = SX1276LoRaGetTransferTime();
  DataUpTime  = Sx127xGetSendTime(NodeNumber,TransTimeUP, DataUpTimePeriod);
  printf("DataUpTime = %d\n",DataUpTime);
  if (DataUpTime == 0)
  {
    startUpTimeHours = startUpTimeMinute = 0;
    startUpTimeSeconds = startUpTimeSubSeconds = 0;
  }
  else
  {
    GetTimeHMS(DataUpTime, &startUpTimeHours, &startUpTimeMinute, &startUpTimeSeconds, &startUpTimeSubSeconds);
    printf("DataUpTime->H:%d,M:%d,S:%d,SUB:%d\n", startUpTimeHours, startUpTimeMinute, startUpTimeSeconds, startUpTimeSubSeconds);
  }
  GetTimeHMS(DataUpTimePeriod, &DataUpTimeHours, &DataUpTimeMinute, &DataUpTimeSeconds, &DataUpTimeSubSeconds);
  printf("DataUpTimePeriod->H:%d,M:%d,S:%d,SUB:%d\n", DataUpTimeHours, DataUpTimeMinute, DataUpTimeSeconds, DataUpTimeSubSeconds);
}


//**************主**************//
//**************机**************//


//**********************************//
//
//函数名称:  WaiitJionNetFinish 
//
//函数描述:  等待入网完成 
//
//函数参数:  超时时间
//
//返回值:     无
//
//创建者:   
//*******************************//

DeviceJionFlag WaitJionNetFinish(uint8_t timout)
{
  JionNodeTimeCount = 0;
  while(1)
  {
    Sx127xDataGet();
    if (JionNodeTimeCount > timout)
    {
      if (oldNodeNumber == currentDeviceNumber)
      {
          printf("无新节点加入\r\n");
          //无新节点加入
          JionNodeTimeOutFlag = Node_Jion_Finish_Flag;
          //停止定时器
          HAL_TIM_Base_Stop_IT(&htim2);
          return JionNodeTimeOutFlag;
      }
      else
      {
          //有新节点加入
          printf("有新节点加入\r\n");
          JionNodeTimeOutFlag = Node_Jion_No_Finish_Flag;
          //保存当前节点数量
          oldNodeNumber = currentDeviceNumber;
      }

    }//等待加入网络
    }
  }
//**********************************//
//
//函数名称:   MasterSendClockData
//
//函数描述:   主机发送同步时钟
//
//函数参数:   无
//
//返回值:     无
//
//创建者:  
//*******************************//

void MasterSendClockData(void)
{
  RTC_TimeTypeDef thisTime;
  
  rtcSync_t.msgHead = JIONREQUEST;
  rtcSync_t.dataLength = 0x09;
  rtcSync_t.netType = 'T';
  rtcSync_t.netPanid[0] = HI_UINT16(PAN_ID);
  rtcSync_t.netPanid[1] = LO_UINT16(PAN_ID);
  
  //获取当前时间
  HAL_RTC_GetTime(&hrtc, &thisTime, RTC_FORMAT_BIN);
  
  rtcSync_t.timeData[0] = thisTime.Hours;
  rtcSync_t.timeData[1] = thisTime.Minutes;
  rtcSync_t.timeData[2] = thisTime.Seconds;
  rtcSync_t.timeData[3] = (thisTime.SubSeconds >> 8) & 0xFF;
  rtcSync_t.timeData[4] = thisTime.SubSeconds & 0xFF;
  //计算校验码
  rtcSync_t.crcCheck = crc8((uint8_t *)&rtcSync_t, rtcSync_t.dataLength + 1);
  //发送数据包
  Radio->SetTxPacket((uint8_t *)&rtcSync_t, rtcSync_t.dataLength + 2);
}

源码分析(具体分析:该源码涵盖了集中器 和 节点 的源代码,可以从main函数开始分析,先屏蔽掉节点的代码,只分析集中器的相关代码,然后屏蔽掉集中器的代码,只分析节点代码。分析过程中有遇到没见过的或者不知道的函数,直接追入分析即可)

main函数相关代码分析

//所有设备加入网络的当前情况
DeviceJionFlag	JionDeviceStatu = No_Node_Jion_Flag;

//时间同步标志
volatile uint8_t MasterSendTimeSliceFlag = 0;

volatile uint8_t SendDataOkFlag = 0;

extern volatile uint8_t SendClockFlag;


/* main */

#if SLAVE
  //获取随机入网时间
  DelayTime = RandomNumber();
  printf("JionTime = %d\n",DelayTime);
  HAL_Delay(DelayTime);
  //等待入网成功
  while (SlaveJionNetFuction());
  //获取节点发送时间片
  SlaveGetSendTime();
  
#else
    //主机直接初始化RTC
    MX_RTC_Init();--------------------------------------------->第一步:主机初始化RTC
    HAL_TIM_Base_Start_IT(&htim2);----------------------------->第二步:主机初始化定时器(包括中断)
#endif

 while (1)
  {
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
    
    Sx127xDataGet();
    
#if SLAVE
    if(sendUpDataFlag == 1)
    {
      SendSensorDataUP();
      sendUpDataFlag = 0;
    }
#else
    UartDmaGet();------------------------------------------->第三步:串口数据获取,并把数据发送出去
    //等待节点入网
    if (JionDeviceStatu != Node_Jion_Finish_Flag)----------->第四步:如果没有入网完成
    {
      printf("main 等待加入网络\n");
      JionDeviceStatu = WaitJionNetFinish(10);
    }
    
        /* 有新节点加入 */
    if (currentDeviceNumber != oldNodeNumber)
    {
      printf("main 新节点加入网络\n");
      HAL_TIM_Base_Start_IT(&htim2);
      JionDeviceStatu = New_Node_Jion_Flag;
      SendClockFlag = 0; //发送分时时间片
    }
       /* 有旧节点加入 */
    for (int i = 0; i < currentDeviceNumber;i++)
    {
      /* 查询是否有旧节点重新加入*/
      if (slaveNetInfo_t[i].deviceNetStatus == AGAIN_JION_NET)
      {
        printf("main 旧节点加入网络\n");
        slaveNetInfo_t[i].deviceNetStatus = JIONDONE;
        JionDeviceStatu = New_Node_Jion_Flag;
		SendClockFlag = 0; //发送分时时间片
        HAL_TIM_Base_Start_IT(&htim2);
      }
    }
    
        /* 给从机分发时间片 */
    if ((JionDeviceStatu == Node_Jion_Finish_Flag)&&(SendClockFlag == 0)
        &&(currentDeviceNumber != 0))
    {
		if (SendDataOkFlag == 1) {
			SendDataOkFlag = 0;
                        printf("main 发送时钟同步\n");
			//告诉所有节点开始上传数据
			MasterSendClockData();
			SendClockFlag = 1;
			while(!SendDataOkFlag)  //等待发送完成
			{
				Sx127xDataGet();
			}
			SendDataOkFlag = 1;
		}
    }
#endif
    if(EnableMaster == true)
    {
     MLCD_Show();
    }
    else
    {
      SLCD_Show();
    }
  }

LoRa自组网节点程序开发

内容概要

1、工程修改

2、搭建框架

3、源码分析

4、组网实验

LoRa自组网节点设计

根据节点业务流程需要在之前工程里添加一个外设用于随机数发生:ADC

ADC外设配置

1、配置ADC为连续采集

2、配置DMA通道

3、配置ADC标签

搭建框架

1、网络处理任务

2、数据处理任务

数据处理任务

数据解析任务

    -->从机数据解析

        -->网络数据包解析

        -->网络应答包解析

        -->时间同步包解析

数据上传任务

    -->入网信息上传

    -->数据信息上传

网络处理任务

1、入网随机时间获取

2、无线加入网络

3、获取数据包发送时长

4、获取节点时间片

硬件准备:LoRa设备X3、STlinkX1、USBmini线X3

程序烧写

1、烧写Master程序

2、烧写Slave程序:配置从机设备地址,分别烧录

实验现象

1、从机入网请求

2、主机入网应答

3、从机1分钟定时上传数据

 


本文链接: http://www.dtmao.cc/news_show_300101.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?