內容概要:算法
一、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服務器通訊協議:
內容概要:
一、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自組網協調器設計:
根據協調器業務流程須要在以前工程裏添加兩個外設:定時器(用於節點入網超時的判斷,後面有配置說明)、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函數相關代碼分析:
//全部設備加入網絡的當前狀況 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自組網節點設計:
根據節點業務流程須要在以前工程裏添加一個外設用於隨機數發生:ADC
ADC外設配置:
一、配置ADC爲連續採集
二、配置DMA通道
三、配置ADC標籤
搭建框架:
一、網絡處理任務
二、數據處理任務
數據處理任務:
數據解析任務
-->從機數據解析
-->網絡數據包解析
-->網絡應答包解析
-->時間同步包解析
數據上傳任務
-->入網信息上傳
-->數據信息上傳
網絡處理任務:
一、入網隨機時間獲取
二、無線加入網絡
三、獲取數據包發送時長
四、獲取節點時間片
硬件準備:LoRa設備X三、STlinkX一、USBmini線X3
程序燒寫:
一、燒寫Master程序
二、燒寫Slave程序:配置從機設備地址,分別燒錄
實驗現象:
一、從機入網請求
二、主機入網應答
三、從機1分鐘定時上傳數據