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

 

 

深入了解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初始化

 
  1. /**Initialize RTC and set the Time and Date

  2. */

  3. sTime.Hours = startUpDateHours;

  4. sTime.Minutes = startUpDateMinute;

  5. sTime.Seconds = startUpDateSeconds;

  6. sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;

  7. sTime.StoreOperation = RTC_STOREOPERATION_RESET;

 
  1. /**Enable the Alarm A

  2. */

  3. sAlarm.AlarmTime.Hours = DataUpTimeHours;

  4. sAlarm.AlarmTime.Minutes = DataUpTimeMinute;

  5. sAlarm.AlarmTime.Seconds = DataUpTimeSeconds;

  6. sAlarm.AlarmTime.SubSeconds = DataUpTimeSubSeconds;

  7. sAlarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;

  8. sAlarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET;

  9. sAlarm.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY;

  10. sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;

  11. sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;

  12. sAlarm.AlarmDateWeekDay = 0x1;

  13. sAlarm.Alarm = RTC_ALARM_A;

  14. memcpy(&gAlarm, &sAlarm, sizeof(sAlarm));

2、Alarm中断任务

 
  1. //**********************************//

  2. //

  3. //函数名称:HAL_RTC_AlarmAEventCallback

  4. //

  5. //函数描述: 闹钟事件回调函数

  6. //

  7. //函数参数: RTC_HandleTypeDef *hrtc

  8. //

  9. //返回值: 无

  10. //

  11. //创建者:

  12. //*******************************//

  13.  
  14. void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)

  15. {

  16.  
  17. RTC_TimeTypeDef masterTime;

  18. RTC_TimeTypeDef SlaveTime;

  19. RTC_DateTypeDef masterDate;

  20.  
  21. #if MASTER

  22. //置位同步时钟标志

  23. SendClockFlag = 0;

  24. //获取下次闹钟时间

  25. HAL_RTC_GetTime(hrtc, &masterTime, RTC_FORMAT_BIN);

  26. HAL_RTC_GetDate(hrtc, &masterDate, RTC_FORMAT_BIN);

  27. gAlarm.AlarmTime.Hours = masterTime.Hours + CLOCKHOURS;

  28. gAlarm.AlarmTime.Minutes = masterTime.Minutes;

  29. gAlarm.AlarmTime.Seconds = masterTime.Seconds;

  30. gAlarm.AlarmTime.SubSeconds = masterTime.SubSeconds;

  31.  
  32. #else

  33. sendUpDataFlag = 1;

  34. HAL_RTC_GetTime(hrtc, &SlaveTime, RTC_FORMAT_BIN);

  35. HAL_RTC_GetDate(hrtc, &masterDate, RTC_FORMAT_BIN);

  36. gAlarm.AlarmTime.Hours = SlaveTime.Hours + DataUpTimeHours;

  37. gAlarm.AlarmTime.Minutes = SlaveTime.Minutes + DataUpTimeMinute;

  38. gAlarm.AlarmTime.Seconds = SlaveTime.Seconds + DataUpTimeSeconds;

  39. gAlarm.AlarmTime.SubSeconds = SlaveTime.SubSeconds + DataUpTimeSubSeconds;

  40. #endif

  41.  
  42.  
  43. if (gAlarm.AlarmTime.Seconds > 59)

  44. {

  45. gAlarm.AlarmTime.Seconds -= 60;

  46. gAlarm.AlarmTime.Minutes += 1;

  47. }

  48.  
  49. if ( gAlarm.AlarmTime.Minutes >59)

  50. {

  51. gAlarm.AlarmTime.Minutes -= 60;

  52. gAlarm.AlarmTime.Hours += 1;

  53. }

  54. if (gAlarm.AlarmTime.Hours > 23)

  55. {

  56. gAlarm.AlarmTime.Hours -= 24;

  57. }

  58.  
  59. printf("RTC\n");

  60. //使能闹钟中断

  61. if (HAL_RTC_SetAlarm_IT(hrtc, &gAlarm, RTC_FORMAT_BIN) != HAL_OK)

  62. {

  63. _Error_Handler(__FILE__, __LINE__);

  64. }

  65. }

 
  1. //**********************************//

  2. //

  3. //函数名称: GetTimeHMS

  4. //

  5. //函数描述: 时分秒转换

  6. //

  7. //函数参数: uint32_t timeData,uint8_t *hours,uint8_t *minute,uint8_t *seconds,uint32_t *subSeconds

  8. //

  9. //返回值: 无

  10. //

  11. //创建者:

  12. //*******************************//

  13.  
  14. void GetTimeHMS(uint32_t timeData,uint8_t *hours,uint8_t *minute,uint8_t *seconds,uint32_t *subSeconds)

  15. {

  16. /* 获得亚秒 */

  17. *subSeconds = timeData % 1000;

  18. /* 获得秒钟*/

  19. timeData = timeData / 1000;

  20. *seconds = timeData % 60;

  21. /* 获得分钟*/

  22. timeData = timeData / 60;

  23. *minute = timeData % 60;

  24. /* 获得小时 */

  25. *hours = timeData / 60;

  26. }

定时器任务

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

2、定时器中断任务

 
  1. //**********************************//

  2. //

  3. //函数名称: HAL_TIM_PeriodElapsedCallback

  4. //

  5. //函数描述: 定时器2溢出中断回调函数

  6. //

  7. //函数参数: TIM_HandleTypeDef *htim

  8. //

  9. //返回值: 无

  10. //

  11. //创建者:

  12. //*******************************//

  13.  
  14. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

  15. {

  16. //判断是否为定时器2中断

  17. //累加全局计数值

  18. if(htim->Instance == htim2.Instance)

  19. {

  20.  
  21. JionNodeTimeCount++;

  22. }

  23. }

通信协议

1、CRC8校验函数

 
  1. /* protocol.c */

  2.  
  3. #include "protocol.h"

  4.  
  5. /******************************************************************************

  6. * Name: CRC-8 x8+x2+x+1

  7. * Poly: 0x07

  8. * Init: 0x00

  9. * Refin: False

  10. * Refout: False 函数功能:生成CRC代码

  11. * Xorout: 0x00

  12. * Note:

  13. *****************************************************************************/

  14. uint8_t crc8(uint8_t *data, uint8_t length)

  15. {

  16. uint8_t i;

  17. uint8_t crc = 0; // Initial value

  18. while(length--)

  19. {

  20. crc ^= *data++; // crc ^= *data; data++;

  21. for ( i = 0; i < 8; i++ )

  22. {

  23. if ( crc & 0x80 )

  24. crc = (crc << 1) ^ 0x07;

  25. else

  26. crc <<= 1;

  27. }

  28. }

  29. return crc;

  30. }

  31.  
  32. //**********************************//

  33. //

  34. //函数名称: DataCrcVerify

  35. //

  36. //函数描述: CRC8校验

  37. //

  38. //函数参数: uint8_t * buff, uint8_t len

  39. //

  40. //返回值: uint8_t

  41. //

  42. //创建者:

  43. //*******************************//

  44.  
  45. uint8_t DataCrcVerify(uint8_t * buff, uint8_t len)

  46. {

  47. uint8_t Crc8Data = 0;

  48.  
  49. //验证数据是否正确

  50. Crc8Data = crc8(buff, len - 1);

  51.  
  52. if (Crc8Data == buff[len - 1])

  53. {

  54. // PRINTF1("CRC8 Success!\n");

  55. return 1;

  56. }

  57. else

  58. {

  59. // PRINTF1("CRC8 Failed!\n");

  60. return 0;

  61. }

  62. }

2、协议数据结构

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

串口任务

    -->串口接收

无线任务

    -->无线接收

        -->主机协议解析

            -->网络数据包解析

            -->入网请求解析

 
  1. /* dataprocess.c */

  2.  
  3. #include "dataprocess.h"

  4. #include "usart.h"

  5. #include "led.h"

  6. #include "protocol.h"

  7. #include "rtc.h"

  8.  
  9. #include "string.h"

  10. #include "stdio.h"

  11. //sx1278

  12. #include "platform.h"

  13. #include "radio.h"

  14. #include "sx1276-Hal.h"

  15. #include "sx1276-LoRa.h"

  16. #include "sx1276-LoRaMisc.h"

  17.  
  18. extern uint16_t BufferSize;

  19. extern uint8_t Buffer[BUFFER_SIZE];

  20.  
  21. #if defined(MASTER)

  22. extern uint8_t EnableMaster;

  23.  
  24. #elif defined(SLAVE)

  25.  
  26. extern uint8_t EnableMaster;

  27.  
  28. #endif

  29.  
  30. extern tRadioDriver *Radio;

  31.  
  32. extern uint32_t Master_RxNumber;

  33. extern uint32_t Master_TxNumber;

  34.  
  35. extern uint32_t Slave_RxNumber;

  36. extern uint32_t Slave_TxNumber;

  37.  
  38. extern volatile uint8_t SendDataOkFlag;

  39.  
  40. uint8_t startUpDateHours = 0;

  41. uint8_t startUpDateMinute = 0;

  42. uint8_t startUpDateSeconds = 0;

  43. uint16_t startUpDateSubSeconds = 0;

  44.  
  45. //Master存储入网的设备信息

  46. SlaveInfo slaveNetInfo_t[NodeNumber];

  47.  
  48. //Salve入网信息包

  49. SlaveJionNet jionPacke_t;

  50.  
  51. //Salve保存自己的地址

  52. SlaveInfo slaveNativeInfo_t;

  53. //节点数据

  54. SlaveDataNet DataPacke_t;

  55.  
  56.  
  57. //**********************************//

  58. //

  59. //函数名称:UartDmaGet

  60. //

  61. //函数描述:串口数据获取

  62. //

  63. //函数参数: 无

  64. //

  65. //返回值: 无

  66. //

  67. //创建者:

  68. //*******************************//

  69.  
  70. void UartDmaGet(void)

  71. {

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

  73. {

  74.  
  75. //串口接收到的数据原封发给SX1278

  76. Radio->SetTxPacket(UsartType1.usartDMA_rxBuf, UsartType1.Usart_rx_len);

  77.  
  78. memset(UsartType1.usartDMA_rxBuf,0,UsartType1.Usart_rx_len);

  79. UsartType1.receive_flag = 0; //接收数据标志清零,

  80. }

  81. }

  82.  
  83. //**********************************//

  84. //

  85. //函数名称: RxDataPacketNum

  86. //

  87. //函数描述: 接收数据包计数

  88. //

  89. //函数参数: 无

  90. //

  91. //返回值: 无

  92. //

  93. //创建者:

  94. //*******************************//

  95.  
  96. void RxDataPacketNum(void)

  97. {

  98. if(EnableMaster == true)

  99. Master_RxNumber++;

  100. else

  101. Slave_RxNumber++;

  102. }

  103. //**********************************//

  104. //

  105. //函数名称: TxDataPacketNum

  106. //

  107. //函数描述: 发送数据包计数

  108. //

  109. //函数参数: 无

  110. //

  111. //返回值: 无

  112. //

  113. //创建者:

  114. //*******************************//

  115.  
  116. void TxDataPacketNum(void)

  117. {

  118. if(EnableMaster == true)

  119. Master_TxNumber++;

  120. else

  121. Slave_TxNumber++;

  122. }

  123. //**********************************//

  124. //

  125. //函数名称: Sx127xDataGet

  126. //

  127. //函数描述: 读取sx127x射频射频数据

  128. //

  129. //函数参数: 无

  130. //

  131. //返回值: 无

  132. //

  133. //创建者:

  134. //*******************************//

  135.  
  136. uint8_t Sx127xDataGet(void)

  137. {

  138. uint8_t status = 0;

  139. switch( Radio->Process( ) )

  140. {

  141. case RF_RX_TIMEOUT:

  142. printf("RF_RX_TIMEOUT\n");

  143. break;

  144. case RF_RX_DONE:

  145. Radio->GetRxPacket( Buffer, ( uint16_t* )&BufferSize );

  146. if(EnableMaster == true)

  147. printf("master Rx Len = %d\n",BufferSize);

  148. else

  149. printf("slave Rx Len = %d\n",BufferSize);

  150. if( BufferSize > 0 )//&& (BufferSize == strlen((char*)Buffer)))

  151. {

  152. //接收数据闪烁

  153. LedBlink( LED_RX );

  154. //计算接收数据的个数

  155. RxDataPacketNum();

  156.  
  157. //清空sx127x接收缓冲区

  158. #ifdef MASTER

  159. status = MasterProtocolAnalysis(Buffer,BufferSize);

  160. #else

  161. status = SlaveProtocolAnalysis(Buffer, BufferSize);

  162. #endif

  163. memset(Buffer,0,BufferSize);

  164. }

  165. break;

  166. case RF_TX_DONE:

  167. //发送闪烁

  168. LedBlink( LED_TX );

  169. //计算发送数据的个数

  170. TxDataPacketNum();

  171. Radio->StartRx( );

  172. SendDataOkFlag = 1;

  173. break;

  174. case RF_TX_TIMEOUT:

  175. printf("RF_TX_TIMEOUT\n");

  176. break;

  177. default:

  178. break;

  179. }

  180. return status;

  181. }

  182.  
  183.  
  184. //**************从**************//

  185. //**************机**************//

  186.  
  187.  
  188.  
  189. //**********************************//

  190. //

  191. //函数名称: SendJionNetPacke

  192. //

  193. //函数描述: 从机入网数据发送

  194. //

  195. //函数参数: 无

  196. //

  197. //返回值: 无

  198. //

  199. //创建者:

  200. //*******************************//

  201.  
  202. void SendJionNetPacke(void)

  203. {

  204. uint16_t addr = ADDR;

  205. jionPacke_t.msgHead = 0x3C;

  206. jionPacke_t.dataLength = 0x06;

  207. jionPacke_t.netType = 'J';

  208. jionPacke_t.netPanid[0] = HI_UINT16(PAN_ID);

  209. jionPacke_t.netPanid[1] = LO_UINT16(PAN_ID);

  210. jionPacke_t.deviceAddr[0] = HI_UINT16(ADDR);

  211. jionPacke_t.deviceAddr[1] = LO_UINT16(ADDR);

  212. //校验码

  213. jionPacke_t.crcCheck = crc8((uint8_t *)&jionPacke_t,jionPacke_t.dataLength + 1);

  214.  
  215. printf("SendJionNetPacke addr = %d\n",addr);

  216. //发送数据包

  217. Radio->SetTxPacket((uint8_t *)&jionPacke_t, jionPacke_t.dataLength + 2);

  218. }

  219.  
  220. //**********************************//

  221. //

  222. //函数名称: SlaveProtocolAnalysis

  223. //

  224. //函数描述: 从机协议解析

  225. //

  226. //函数参数: uint8_t *buff,uint8_t len

  227. //

  228. //返回值: uint8_t

  229. //

  230. //创建者:

  231. //*******************************//

  232.  
  233. uint8_t SlaveProtocolAnalysis(uint8_t *buff,uint8_t len)

  234. {

  235. uint8_t Crc8Data;

  236.  
  237. printf("SlaveProtocolAnalysis\n");

  238. for (int i = 0; i < len; i++)

  239. {

  240. printf("0x%x ",buff[i]);

  241. }

  242. printf("\n");

  243.  
  244. if (buff[0] == NETDATA)

  245. {

  246. if (buff[1] == HI_UINT16(PAN_ID) && buff[2] == LO_UINT16(PAN_ID))

  247. {

  248. Crc8Data = crc8(&buff[3], len - 4);

  249.  
  250. if (Crc8Data != buff[len - 1])

  251. {

  252. memset(buff, 0, len);

  253. return 0;

  254. }

  255. if (buff[3] == 0x21)

  256. {

  257. printf("Slave_NETDATA\n");

  258. }

  259. return 0;

  260. }

  261. }

  262. else if((buff[0] == 0x3C) && (buff[2] == 'A'))

  263. {

  264. if (DataCrcVerify(buff, len) == 0)

  265. {

  266. return 0;

  267. }

  268. if (buff[3] == HI_UINT16(PAN_ID) && buff[4] == LO_UINT16(PAN_ID))

  269. {

  270. if (buff[5] == jionPacke_t.deviceAddr[0] && buff[6] == jionPacke_t.deviceAddr[1])

  271. {

  272. slaveNativeInfo_t.deviceId = buff[7];

  273. printf("Slave_ACK\n");

  274. return 0xFF;

  275. }

  276. }

  277. }

  278. else if((buff[0] == 0x3C) && (buff[2] == 'T'))

  279. {

  280. if (DataCrcVerify(buff, len) == 0)

  281. {

  282. return 0;

  283. }

  284. if (buff[3] == HI_UINT16(PAN_ID) && buff[4] == LO_UINT16(PAN_ID))

  285. {

  286. uint32_t alarmTime = 0;

  287. startUpTimeHours = buff[5];

  288. startUpTimeMinute = buff[6];

  289. startUpTimeSeconds = buff[7];

  290. startUpTimeSubSeconds = buff[8] <<8 | buff[9];

  291. printf("Slave_CLOCK\n");

  292. printf("H:%d,M:%d,S:%d,SUB:%d\n", startUpTimeHours, startUpTimeMinute, startUpTimeSeconds, startUpTimeSubSeconds);

  293. alarmTime = ((DataUpTimeHours * 60 + DataUpTimeMinute) * 60

  294. + DataUpTimeSeconds) * 1000 + (DataUpTimeSubSeconds / 2) + DataUpTime;

  295. GetTimeHMS(alarmTime, &DataUpTimeHours, &DataUpTimeMinute, &DataUpTimeSeconds, &DataUpTimeSubSeconds);

  296. printf("DataUpTime->H:%d,M:%d,S:%d,SUB:%d\n", DataUpTimeHours, DataUpTimeMinute, DataUpTimeSeconds, DataUpTimeSubSeconds);

  297. //使能RTC

  298. MX_RTC_Init();

  299. return 0xFF;

  300. }

  301. }

  302. return 1;

  303. }

  304.  
  305. //**********************************//

  306. //

  307. //函数名称: SendSensorDataUP

  308. //

  309. //函数描述: 上传节点传感器数据

  310. //

  311. //函数参数: 无

  312. //

  313. //返回值: 无

  314. //

  315. //创建者:

  316. //*******************************//

  317.  
  318. void SendSensorDataUP(void)

  319. {

  320.  
  321. printf("SendSensorDataUP\n");

  322. DataPacke_t.netmsgHead = 'N';

  323. DataPacke_t.netPanid[0] = HI_UINT16(PAN_ID);

  324. DataPacke_t.netPanid[1] = LO_UINT16(PAN_ID);

  325. DataPacke_t.msgHead = 0x21;

  326. DataPacke_t.dataLength = 0x09;

  327. DataPacke_t.dataType = 0;

  328. DataPacke_t.deviceAddr[0] = HI_UINT16(ADDR);

  329. DataPacke_t.deviceAddr[1] = LO_UINT16(ADDR);

  330. DataPacke_t.sensorType = 0x1;

  331. DataPacke_t.buff[0] = 0x1;

  332. DataPacke_t.buff[1] = 0x2;

  333. DataPacke_t.buff[2] = 0x3;

  334. DataPacke_t.buff[3] = 0x4;

  335.  
  336. //校验码

  337. DataPacke_t.crcCheck = crc8((uint8_t *)&DataPacke_t,DataPacke_t.dataLength + 4);

  338. //发送数据包

  339. Radio->SetTxPacket((uint8_t *)&DataPacke_t, DataPacke_t.dataLength + 5);

  340. }

  341.  
  342.  
  343. //**************主**************//

  344. //**************机**************//

  345.  
  346.  
  347.  
  348. //**********************************//

  349. //

  350. //函数名称: MasterProtocolAnalysis

  351. //

  352. //函数描述: 主机协议解析

  353. //

  354. //函数参数: uint8_t *buff,uint8_t len

  355. //

  356. //返回值: uint8_t

  357. //

  358. //创建者:

  359. //*******************************//

  360.  
  361. uint8_t MasterProtocolAnalysis(uint8_t *buff,uint8_t len)

  362. {

  363. uint8_t Crc8Data,deviceID;

  364.  
  365. uint8_t SendAck[12];

  366.  
  367. printf("MasterProtocolAnalysis\n");

  368. for (int i = 0; i < len; i++)

  369. {

  370. printf("0x%x ",buff[i]);

  371. }

  372. printf("\n");

  373.  
  374. if(buff[0] == NETDATA)

  375. {

  376. if((buff[1] == HI_UINT16(PAN_ID))&&(buff[2] == LO_UINT16(PAN_ID)))

  377. {

  378. Crc8Data = crc8(&buff[0], len - 1); //减去校验

  379. if(Crc8Data != buff[len - 1])

  380. {

  381. memset(buff,0,len);//清空缓存区

  382.  
  383. return 0;

  384. }

  385.  
  386. if(buff[3] == DATAHEAD)

  387. {

  388.  
  389. NetDataProtocolAnalysis(&buff[3], len - 3);

  390. }

  391. }

  392. else

  393. return 0;

  394. }

  395. else if(buff[0] == JIONREQUEST)

  396. {

  397. deviceID = JionNetProtocolAnalysis(buff, len);

  398. printf("deviceID = %d\n",deviceID);

  399.  
  400. if(deviceID >= 0)

  401. {

  402. SendAck[0] = JIONREQUEST;

  403. SendAck[1] = 1;

  404. SendAck[2] = 'A';

  405. SendAck[3] = HI_UINT16(PAN_ID);

  406. SendAck[4] = LO_UINT16(PAN_ID);

  407. SendAck[5] = slaveNetInfo_t[deviceID].deviceAddr[0];

  408. SendAck[6] = slaveNetInfo_t[deviceID].deviceAddr[1];

  409. SendAck[7] = deviceID;

  410. SendAck[8] = crc8(SendAck, 8);

  411. Radio->SetTxPacket(SendAck, 9);

  412. printf("MasterAck\n");

  413. for (int i = 0; i < 9; i++)

  414. {

  415. printf("0x%x ",SendAck[i]);

  416. }

  417. printf("\n");

  418. }

  419. }

  420. return 1;

  421. }

  422.  
  423.  
  424.  
  425. //**********************************//

  426. //

  427. //函数名称: JionNetProtocolAnalysis

  428. //

  429. //函数描述: 入网协议解析

  430. //

  431. //函数参数: uint8_t *buff,uint8_t len

  432. //

  433. //返回值: uint8_t

  434. //

  435. //创建者:

  436. //*******************************//

  437.  
  438. uint8_t JionNetProtocolAnalysis(uint8_t *buff,uint8_t len)

  439. {

  440. uint8_t i = 0, dataLen = 0;

  441. uint8_t status = 0, lenOld = len;

  442.  
  443. printf("JionNetProtocolAnalysis\n");

  444. for (int i = 0; i < len; i++)

  445. {

  446. printf("0x%x ",buff[i]);

  447. }

  448. printf("\n");

  449. while(len--)

  450. {

  451. switch(status)

  452. {

  453. case JION_HEADER:

  454. if (buff[status] == JIONREQUEST)

  455. {

  456. status = JION_LENGHT;

  457. }

  458. else

  459. {

  460. goto ERR;

  461. }

  462. break;

  463. case JION_LENGHT:

  464. if (buff[status] == 0x06)

  465. {

  466. status = JION_TYPE;

  467. }

  468. else

  469. {

  470. goto ERR;

  471. }

  472. break;

  473. case JION_TYPE:

  474. if (buff[status] == 'J')

  475. {

  476. status = JION_PANID;

  477. }

  478. else

  479. {

  480. goto ERR;

  481. }

  482. break;

  483. case JION_PANID:

  484. if (buff[status] == HI_UINT16(PAN_ID) && buff[status + 1] == LO_UINT16(PAN_ID))

  485. {

  486. status = JION_ADDR;

  487. }

  488. else

  489. {

  490. goto ERR;

  491. }

  492. break;

  493. case JION_ADDR:

  494. //旧节点加入

  495. for (i = 0; i < currentDeviceNumber; i++)

  496. {

  497. if ((slaveNetInfo_t[i].deviceAddr[0] == buff[status + 1]) &&

  498. (slaveNetInfo_t[i].deviceAddr[1] == buff[status + 2]))

  499. {

  500. slaveNetInfo_t[i].deviceNetStatus = AGAIN_JION_NET;

  501. status = JION_CRC;

  502. printf("AGAIN_JION_NET i = %d\n",i);

  503. printf("deviceId=%x\n",slaveNetInfo_t[i].deviceId);

  504. printf("deviceAddr[0]=%x\n",slaveNetInfo_t[i].deviceAddr[0]);

  505. printf("deviceAddr[1]=%x\n",slaveNetInfo_t[i].deviceAddr[1]);

  506. break;

  507. }

  508. }

  509. //新节点加入

  510. if(i == currentDeviceNumber)

  511. {

  512. currentDeviceNumber++;//新增加入节点

  513. slaveNetInfo_t[i].deviceId = i;

  514. slaveNetInfo_t[i].deviceAddr[0] = buff[status + 1];

  515. slaveNetInfo_t[i].deviceAddr[1] = buff[status + 2];

  516. status = JION_CRC;

  517. printf("CURRENT_JION_NET i = %d\n",i);

  518. printf("deviceId=%x\n",slaveNetInfo_t[i].deviceId);

  519. printf("deviceAddr[0]=%x\n",slaveNetInfo_t[i].deviceAddr[0]);

  520. printf("deviceAddr[1]=%x\n",slaveNetInfo_t[i].deviceAddr[1]);

  521. }

  522. break;

  523. case JION_CRC:

  524. //更新节点入网状态

  525. if (slaveNetInfo_t[i].deviceNetStatus != AGAIN_JION_NET)

  526. {

  527. slaveNetInfo_t[i].deviceNetStatus = JIONDONE;

  528. status = JION_HEADER;

  529. printf("JIONDONE i = %d\n",i);

  530. printf("deviceId=%x\n",slaveNetInfo_t[i].deviceId);

  531. printf("deviceAddr[0]=%x\n",slaveNetInfo_t[i].deviceAddr[0]);

  532. printf("deviceAddr[1]=%x\n",slaveNetInfo_t[i].deviceAddr[1]);

  533.  
  534. }

  535. break;

  536. default:

  537. break;

  538. }

  539. }

  540. return i;

  541.  
  542. ERR:

  543. memset(buff, 0, lenOld);

  544. status = JION_HEADER;

  545. return -1;

  546. }

  547.  
  548. //**********************************//

  549. //

  550. //函数名称: NetDataProtocolAnalysis

  551. //

  552. //函数描述: 网络数据包解析

  553. //

  554. //函数参数: uint8_t *buff,uint8_t len

  555. //

  556. //返回值: 无

  557. //

  558. //创建者:

  559. //*******************************//

  560.  
  561. void NetDataProtocolAnalysis(uint8_t *buff,uint8_t len)

  562. {

  563. printf("NetDataProtocolAnalysis\n");

  564. for (int i = 0; i < len; i++)

  565. {

  566. printf("0x%x ",buff[i]);

  567. }

  568. printf("\n");

  569. }

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

1、等待入网完成

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

 
  1. /* netprocess.c */

  2.  
  3. #include "netprocess.h"

  4. #include "dataprocess.h"

  5. #include "tim.h"

  6. #include "rtc.h"

  7. #include "adc.h"

  8. #include "protocol.h"

  9.  
  10. #include "math.h"

  11. #include "stdlib.h"

  12. #include "string.h"

  13. #include "stdio.h"

  14. //sx1278

  15. #include "platform.h"

  16. #include "radio.h"

  17. #include "sx1276-Hal.h"

  18. #include "sx1276-LoRa.h"

  19. #include "sx1276-LoRaMisc.h"

  20.  
  21. //所有节点的更新周期(在Time内上传所有数据) 单位Ms

  22. volatile uint32_t DataUpTimePeriod = 1000 * 60 * 1; //1分钟

  23.  
  24. volatile static uint32_t currentTime = 0;

  25. //当前加入设个的个数

  26. volatile uint16_t currentDeviceNumber = 0;

  27. //保存当前加入节点

  28. volatile uint16_t oldNodeNumber = 0;

  29. //节点时间片

  30. volatile uint32_t DataUpTime = 0;

  31.  
  32. //节点入网状态

  33. volatile DeviceJionFlag JionNodeTimeOutFlag = No_Node_Jion_Flag;

  34.  
  35. uint8_t startUpTimeHours = 0;

  36. uint8_t startUpTimeMinute = 0;

  37. uint8_t startUpTimeSeconds = 0;

  38. uint32_t startUpTimeSubSeconds = 0;

  39.  
  40. uint8_t DataUpTimeHours = 0;

  41. uint8_t DataUpTimeMinute = 0;

  42. uint8_t DataUpTimeSeconds = 0;

  43. uint32_t DataUpTimeSubSeconds = 0;

  44.  
  45. //时钟同步

  46. SlaveRtcSync rtcSync_t;

  47.  
  48. //初始化网络状态

  49. volatile DeviceJionStatus NetStatus = NO_JION;

  50.  
  51. extern tRadioDriver *Radio;

  52.  
  53.  
  54. //**************从**************//

  55. //**************机**************//

  56.  
  57.  
  58. //**********************************//

  59. //

  60. //函数名称: RandomNumber

  61. //

  62. //函数描述: 生成随机数

  63. //

  64. //函数参数: 无

  65. //

  66. //返回值: 随机数

  67. //

  68. //创建者:

  69. //*******************************//

  70.  
  71. uint16_t RandomNumber(void)

  72. {

  73. uint16_t randNumber = 0;

  74. float adcValue = 0;

  75. uint32_t u32adcValue = 0;

  76.  
  77. //开启DMA转换ADC

  78. HAL_ADC_Start_DMA(&hadc, (uint32_t*)ADC_DMA_Value, ADC_NUM);

  79.  
  80. HAL_Delay(100);

  81. // printf("ADC_DMA_Value[0] = %d\n",ADC_DMA_Value[0]);

  82. // printf("ADC_DMA_Value[1] = %d\n",ADC_DMA_Value[1]);

  83. // printf("ADC_DMA_Value[2] = %d\n",ADC_DMA_Value[2]);

  84. // printf("ADC_DMA_Value[3] = %d\n",ADC_DMA_Value[3]);

  85. // printf("ADC_DMA_Value[4] = %d\n",ADC_DMA_Value[4]);

  86. //转换为mv值

  87. adcValue = ADC_DMA_Value[ADC_IN5];

  88. adcValue = (adcValue * 3300) / 4096;

  89.  
  90. printf("adcValue = %f\n",adcValue);

  91.  
  92. u32adcValue = (uint32_t)((adcValue-floor(adcValue))*1000000);

  93. printf("u32adcValue = %d\n",u32adcValue);

  94. //获取随机数

  95. srand(u32adcValue);

  96. for(int i = 0;i< 10;i++)

  97. randNumber += (uint8_t)rand();

  98. return randNumber;

  99. }

  100.  
  101. //**********************************//

  102. //

  103. //函数名称: SlaveJionNetFuction

  104. //

  105. //函数描述: 从机加入网络

  106. //

  107. //函数参数: 无

  108. //

  109. //返回值: 入网状态

  110. //

  111. //创建者:

  112. //*******************************//

  113.  
  114. uint8_t SlaveJionNetFuction(void)

  115. {

  116. switch(NetStatus)

  117. {

  118. case NO_JION:

  119. SendJionNetPacke();

  120. //if(Radio->Process( ) == RF_TX_DONE)

  121. NetStatus = JIONING;

  122. currentTime = HAL_GetTick();

  123. break;

  124. case JIONING:

  125. if(Sx127xDataGet() == 0xFF)

  126. {

  127. NetStatus = JIONDONE;

  128. printf("Slave_JIONDONE\n");

  129. }

  130. else

  131. {

  132. if ((HAL_GetTick() - currentTime) > 6000)

  133. NetStatus = JIONTIMEOUT;

  134. }

  135. break;

  136. case JIONTIMEOUT:

  137. NetStatus = NO_JION;

  138. break;

  139. case JIONDONE:

  140. Radio->StartRx();

  141. return 0;

  142. break;

  143. default:

  144. break;

  145. }

  146. return 1;

  147.  
  148. }

  149. //**********************************//

  150. //

  151. //函数名称: SlaveGetSendTime

  152. //

  153. //函数描述: 节点获取时间片

  154. //

  155. //函数参数: 无

  156. //

  157. //返回值: 无

  158. //

  159. //创建者:

  160. //*******************************//

  161.  
  162. void SlaveGetSendTime(void)

  163. {

  164. float TransTimeUP = 0; //数据传输时间

  165. TransTimeUP = SX1276LoRaGetTransferTime();

  166. DataUpTime = Sx127xGetSendTime(NodeNumber,TransTimeUP, DataUpTimePeriod);

  167. printf("DataUpTime = %d\n",DataUpTime);

  168. if (DataUpTime == 0)

  169. {

  170. startUpTimeHours = startUpTimeMinute = 0;

  171. startUpTimeSeconds = startUpTimeSubSeconds = 0;

  172. }

  173. else

  174. {

  175. GetTimeHMS(DataUpTime, &startUpTimeHours, &startUpTimeMinute, &startUpTimeSeconds, &startUpTimeSubSeconds);

  176. printf("DataUpTime->H:%d,M:%d,S:%d,SUB:%d\n", startUpTimeHours, startUpTimeMinute, startUpTimeSeconds, startUpTimeSubSeconds);

  177. }

  178. GetTimeHMS(DataUpTimePeriod, &DataUpTimeHours, &DataUpTimeMinute, &DataUpTimeSeconds, &DataUpTimeSubSeconds);

  179. printf("DataUpTimePeriod->H:%d,M:%d,S:%d,SUB:%d\n", DataUpTimeHours, DataUpTimeMinute, DataUpTimeSeconds, DataUpTimeSubSeconds);

  180. }

  181.  
  182.  
  183. //**************主**************//

  184. //**************机**************//

  185.  
  186.  
  187. //**********************************//

  188. //

  189. //函数名称: WaiitJionNetFinish

  190. //

  191. //函数描述: 等待入网完成

  192. //

  193. //函数参数: 超时时间

  194. //

  195. //返回值: 无

  196. //

  197. //创建者:

  198. //*******************************//

  199.  
  200. DeviceJionFlag WaitJionNetFinish(uint8_t timout)

  201. {

  202. JionNodeTimeCount = 0;

  203. while(1)

  204. {

  205. Sx127xDataGet();

  206. if (JionNodeTimeCount > timout)

  207. {

  208. if (oldNodeNumber == currentDeviceNumber)

  209. {

  210. printf("无新节点加入\r\n");

  211. //无新节点加入

  212. JionNodeTimeOutFlag = Node_Jion_Finish_Flag;

  213. //停止定时器

  214. HAL_TIM_Base_Stop_IT(&htim2);

  215. return JionNodeTimeOutFlag;

  216. }

  217. else

  218. {

  219. //有新节点加入

  220. printf("有新节点加入\r\n");

  221. JionNodeTimeOutFlag = Node_Jion_No_Finish_Flag;

  222. //保存当前节点数量

  223. oldNodeNumber = currentDeviceNumber;

  224. }

  225.  
  226. }//等待加入网络

  227. }

  228. }

  229. //**********************************//

  230. //

  231. //函数名称: MasterSendClockData

  232. //

  233. //函数描述: 主机发送同步时钟

  234. //

  235. //函数参数: 无

  236. //

  237. //返回值: 无

  238. //

  239. //创建者:

  240. //*******************************//

  241.  
  242. void MasterSendClockData(void)

  243. {

  244. RTC_TimeTypeDef thisTime;

  245.  
  246. rtcSync_t.msgHead = JIONREQUEST;

  247. rtcSync_t.dataLength = 0x09;

  248. rtcSync_t.netType = 'T';

  249. rtcSync_t.netPanid[0] = HI_UINT16(PAN_ID);

  250. rtcSync_t.netPanid[1] = LO_UINT16(PAN_ID);

  251.  
  252. //获取当前时间

  253. HAL_RTC_GetTime(&hrtc, &thisTime, RTC_FORMAT_BIN);

  254.  
  255. rtcSync_t.timeData[0] = thisTime.Hours;

  256. rtcSync_t.timeData[1] = thisTime.Minutes;

  257. rtcSync_t.timeData[2] = thisTime.Seconds;

  258. rtcSync_t.timeData[3] = (thisTime.SubSeconds >> 8) & 0xFF;

  259. rtcSync_t.timeData[4] = thisTime.SubSeconds & 0xFF;

  260. //计算校验码

  261. rtcSync_t.crcCheck = crc8((uint8_t *)&rtcSync_t, rtcSync_t.dataLength + 1);

  262. //发送数据包

  263. Radio->SetTxPacket((uint8_t *)&rtcSync_t, rtcSync_t.dataLength + 2);

  264. }

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

main函数相关代码分析

 
  1. //所有设备加入网络的当前情况

  2. DeviceJionFlag JionDeviceStatu = No_Node_Jion_Flag;

  3.  
  4. //时间同步标志

  5. volatile uint8_t MasterSendTimeSliceFlag = 0;

  6.  
  7. volatile uint8_t SendDataOkFlag = 0;

  8.  
  9. extern volatile uint8_t SendClockFlag;

  10.  
  11.  
  12. /* main */

  13.  
  14. #if SLAVE

  15. //获取随机入网时间

  16. DelayTime = RandomNumber();

  17. printf("JionTime = %d\n",DelayTime);

  18. HAL_Delay(DelayTime);

  19. //等待入网成功

  20. while (SlaveJionNetFuction());

  21. //获取节点发送时间片

  22. SlaveGetSendTime();

  23.  
  24. #else

  25. //主机直接初始化RTC

  26. MX_RTC_Init();--------------------------------------------->第一步:主机初始化RTC

  27. HAL_TIM_Base_Start_IT(&htim2);----------------------------->第二步:主机初始化定时器(包括中断)

  28. #endif

  29.  
  30. while (1)

  31. {

  32. /* USER CODE END WHILE */

  33.  
  34. /* USER CODE BEGIN 3 */

  35.  
  36. Sx127xDataGet();

  37.  
  38. #if SLAVE

  39. if(sendUpDataFlag == 1)

  40. {

  41. SendSensorDataUP();

  42. sendUpDataFlag = 0;

  43. }

  44. #else

  45. UartDmaGet();------------------------------------------->第三步:串口数据获取,并把数据发送出去

  46. //等待节点入网

  47. if (JionDeviceStatu != Node_Jion_Finish_Flag)----------->第四步:如果没有入网完成

  48. {

  49. printf("main 等待加入网络\n");

  50. JionDeviceStatu = WaitJionNetFinish(10);

  51. }

  52.  
  53. /* 有新节点加入 */

  54. if (currentDeviceNumber != oldNodeNumber)

  55. {

  56. printf("main 新节点加入网络\n");

  57. HAL_TIM_Base_Start_IT(&htim2);

  58. JionDeviceStatu = New_Node_Jion_Flag;

  59. SendClockFlag = 0; //发送分时时间片

  60. }

  61. /* 有旧节点加入 */

  62. for (int i = 0; i < currentDeviceNumber;i++)

  63. {

  64. /* 查询是否有旧节点重新加入*/

  65. if (slaveNetInfo_t[i].deviceNetStatus == AGAIN_JION_NET)

  66. {

  67. printf("main 旧节点加入网络\n");

  68. slaveNetInfo_t[i].deviceNetStatus = JIONDONE;

  69. JionDeviceStatu = New_Node_Jion_Flag;

  70. SendClockFlag = 0; //发送分时时间片

  71. HAL_TIM_Base_Start_IT(&htim2);

  72. }

  73. }

  74.  
  75. /* 给从机分发时间片 */

  76. if ((JionDeviceStatu == Node_Jion_Finish_Flag)&&(SendClockFlag == 0)

  77. &&(currentDeviceNumber != 0))

  78. {

  79. if (SendDataOkFlag == 1) {

  80. SendDataOkFlag = 0;

  81. printf("main 发送时钟同步\n");

  82. //告诉所有节点开始上传数据

  83. MasterSendClockData();

  84. SendClockFlag = 1;

  85. while(!SendDataOkFlag) //等待发送完成

  86. {

  87. Sx127xDataGet();

  88. }

  89. SendDataOkFlag = 1;

  90. }

  91. }

  92. #endif

  93. if(EnableMaster == true)

  94. {

  95. MLCD_Show();

  96. }

  97. else

  98. {

  99. SLCD_Show();

  100. }

  101. }

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分钟定时上传数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值