物聯網之LoRa開發與應用六(LoRa自組網絡設計)

深刻了解LoRaWAN

內容概要算法

一、LoRaWAN概述緩存

二、LoRaWAN終端(重點掌握安全

三、LoRaWAN服務器服務器

LoRaWAN是什麼網絡

LoRaWAN採用星型無線拓撲:End Nodes(節點)、Gateway(網關)、Network Server(網絡服務器)、Application Server(應用服務器)數據結構

LoRaWAN通訊協議架構

低功耗、可擴展、高服務質量、安全的長距離無線網絡框架

LoRaWAN通訊機制dom

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自組網架構設計

內容概要

一、MAC協議設計

二、LoRa自組網協調器設計

三、LoRa自組網節點設計

MAC協議重要性:解決信號衝突的問題、儘量地節省電能、保證通訊的健壯和穩定性

MAC協議種類

一、信道劃分的MAC協議:時分(TDMA)、頻分(FDMA)、碼分(CDMA)

二、隨機訪問MAC協議:

     ALOHA,S-ALOHA,CSMA,CSMA/CD

     CSMA/CD應用於以太網

     CSMA/CA應用於802.11無線局域網

三、輪訊訪問MAC協議:

     主節點輪詢

     工業Modbus通訊協議

時分複用:(在必定的事件內去分配時間槽,每一個時間槽分給一個節點,使節點在這個時間槽裏通訊,若是不在這個時間槽是不能通訊的。和電腦CPU的時間片是一個道理

優勢:節省電能、最大化使用帶寬

缺點:全部節點須要精確的時鐘源,而且須要週期性校時;

          向網絡中添加和刪除節點都要有時隙分配和回收算法。

頻分複用:(CPU是多核,多任務同時進行:不一樣頻率的通訊能夠同時進行

優勢:增長通訊容量、提升通訊可靠性

缺點:物理通道增長,成本增長

輪詢訪問

優勢:協議簡單,易開發

缺點:通信效率低、網絡規模小(只能接入1-247個節點)

基於時分複用LoRa自組網設計

入網機制:(隨機訪問,競爭入網)

時分複用:(每一個節點在規定的時間槽內通訊)

 LoRa自組網協調器設計

LoRa自組網節點設計

LoRa自組網集中器程序開發

內容概要

一、通訊協議

二、工程修改

三、搭建框架

四、源碼分析

通訊協議

 

LoRa自組網協調器設計

根據協調器業務流程須要在以前工程裏添加兩個外設:定時器(用於節點入網超時的判斷,後面有配置說明)、RTC(實時時鐘,用於時鐘同步,後面有配置說明)

IAR工程修改

添加外設須要修改STM32CubeMX工程,須要把咱們編寫的代碼放在BEGIN和END中間

RTC外設配置

一、修改RTC時鐘源爲外部高速時鐘

二、配置RTC分頻係數

三、初始化日期和時間

四、配置Alarm參數

五、使能RTC全局中斷

定時器外設配置

一、配置TIM2分頻係數

二、使能TIM2定時器中斷

搭建框架

一、RTC任務

二、定時器任務

三、通訊協議

四、數據處理任務

五、網絡處理任務

RTC任務

一、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));

二、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;
}

定時器任務

一、定時器初始化:CubeMX重初始化已經完成,這裏不須要修改

二、定時器中斷任務

//**********************************//
//
//函數名稱:   HAL_TIM_PeriodElapsedCallback
//
//函數描述:   定時器2溢出中斷回調函數
//
//函數參數:   TIM_HandleTypeDef *htim
//
//返回值:     無
//
//建立者:     
//*******************************//

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
//判斷是否爲定時器2中斷
//累加全局計數值
  if(htim->Instance == htim2.Instance)
  {
  
    JionNodeTimeCount++;
  }
}

通訊協議

一、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;
	}
}

二、協議數據結構

數據處理任務:(先了解大體框架,後面具體分析

串口任務

    -->串口接收

無線任務

    -->無線接收

        -->主機協議解析

            -->網絡數據包解析

            -->入網請求解析

/* 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");
}

網絡處理任務:(先了解大體框架,後面具體分析

一、等待入網完成

二、主機發送時鐘同步數據包

/* 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自組網節點程序開發

內容概要

一、工程修改

二、搭建框架

三、源碼分析

四、組網實驗

LoRa自組網節點設計

根據節點業務流程須要在以前工程裏添加一個外設用於隨機數發生:ADC

ADC外設配置

一、配置ADC爲連續採集

二、配置DMA通道

三、配置ADC標籤

搭建框架

一、網絡處理任務

二、數據處理任務

數據處理任務

數據解析任務

    -->從機數據解析

        -->網絡數據包解析

        -->網絡應答包解析

        -->時間同步包解析

數據上傳任務

    -->入網信息上傳

    -->數據信息上傳

網絡處理任務

一、入網隨機時間獲取

二、無線加入網絡

三、獲取數據包發送時長

四、獲取節點時間片

硬件準備:LoRa設備X三、STlinkX一、USBmini線X3

程序燒寫

一、燒寫Master程序

二、燒寫Slave程序:配置從機設備地址,分別燒錄

實驗現象

一、從機入網請求

二、主機入網應答

三、從機1分鐘定時上傳數據