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