LoRa自组网络设计 6

1 深入了解LoRaWan

1.1 LoRaWan概述

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

LoRa联盟是2015年3月Semtech牵头成立的一个开放的、非盈利的组织,发起成员还有法国Actility,中国AUGTEK和荷兰皇家电信kpn等企业。至2016年4月,联盟已经发展成员公司290余家,其中不乏IBM、思科、法国Orange等重量级产商

1.2 LoRaWan通信协议

128AES加密功能

节点与server之间的加密通信:
HAL驱动SPI驱动物理层phy,物理层通过lora或者fsk协议再与网关通信,网关可以通过spi或usb与网关模块通信
网关也可以通过3Gwifi与我们网络服务器通讯

1.3 LoRaWAN与其他组网协议对比 

zigbee属于mesh

1.4 LoRawan终端 

1.4.1 LoRaWAN网关SX1301

拓扑图
大容量的网络规模高速度的通信机制
8通道Lora
IF9 1个FSk
IF8 1个网关间通信
非常适合做网关 200元左右,需要Arm9高速处理器,我们只需要了解,我们学习节点

1.4.2 LoRaWAN终端Class A

LoRaWAN Server选择最佳Gateway下行通信,都是通过LoRaServer选择,
开启两个接收窗口,平时处于休眠模式,当他需要工作的时候才会去发送数据包,功耗最低,实时性不高,比如1小时才能发送1次信息,控制不太合适,采集信息用classA最合适

 

1.4.3 LoRaWAN终端Class B

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

 

1.4.4 LoRaWAN终端Class C 

既保持实时性,也保证了数据收发,一直会打开接收窗口,缺点能耗高

1.5 LoRawan服务器 

1.5.1 LoRaWAN服务器框架

与ABCclass建立了Star通讯
网络服务器与网关建立通讯
控制网络服务器协议算法是通过控制服务器做得,服务器如何决定通过那个网关进行通讯
应用服务器根据行业需求,布置不同应用,使用接口比较单一
客户服务器 二次开发,人机交互等在此开发

1.5.2 不同server的通信

 tcp udp
客户服务网、应用服务器、网络服务器、控制服务器都是通过TCP进行通讯,可靠性
网关、节点和网络服务器是通过UDP,保证实时性

1.5.3 LoRaWAN服务器通信协议 

TCP、UDP、网关通过JSON字符串进行交互的

思考 如何设计Class C终端进行私有组网

1.只能联网内才开发loraWan
2.loraWan网关成本高
3.要具备Sever开发能力

2 LoRa自组网络架构设计

2.1 MAC协议的重要性

类似交通信号灯,无线信道只有1个,不设计会产生冲突
解决信号冲突的问题
尽可能地节省电能
保证通信的健壮和稳定性

2.2 MAC协议种类

 

设计要基于3种协议 

2.3 时分复用

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

时分多路复用是将时间划分为一段段等长的时分复用帧(TDM帧),每个用户在每个TDM帧中占用固定序号的时隙。
每个用户所占用的时隙是周期性出现(其周期就是TDM帧的长度)。
时分复用的所有用户是在不同的时间占用相同的频带宽度。 

2.4 频分复用

1301 芯片

频分多路复用的个用户占用不同的带宽资源(这里的“带宽”是频率带宽(单位:Hz)而不是数据的发送速率)。
用户在分配到一定的频带后,在通信过程中自始至终都占用这个频道。
应用:有线电视网络。

2.5 码分复用

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

SF扩频,SF不同,进行不同的通信

LoRa 中的码分复用通过以下方式实现:

  1. 唯一的扩频因子(Spreading Factor): 在 LoRa 中,每个终端设备使用唯一的扩频因子,这个扩频因子决定了数据信号的频带扩展程度。不同的扩频因子对应着不同的码片序列。每个终端设备在发送数据时,使用其唯一的扩频因子进行调制,因此即使在相同的频率上,不同的终端设备也可以同时发送数据而不会相互干扰。

  2. 自适应数据速率(Adaptive Data Rate,ADR): LoRa 网络可以根据终端设备的距离和环境条件动态调整扩频因子和发送功率,以最大程度地提高通信的可靠性和覆盖范围。

  3. 碰撞避免技术: LoRa 中还采用了碰撞避免技术,通过随机选取发送时间和采用随机退避算法来减少终端设备之间的碰撞,进一步提高了网络的性能。

总的来说,LoRa 中的码分复用技术使得多个终端设备可以在同一时间和频率上进行通信,从而实现了低功耗、远距离和大规模连接的物联网应用场景。

2.6 轮询访问

modbus只有一个主机,节点可以是1-247,只允许主机发送,从机应答。实时性差 

2.7 我们的设计

时间 随机访问,竞争入网
信号(协调器)
节点1 发送收到
节点2 先判断网络是否冲突,再延时发送 接收
节点3 先判断网络是否冲突,再延时发送 接收

时间槽分配、或者时间片,在规定时间内进行收发rx、tx,从而实现整个网络的通信
同时设置冗余、revered slot和空闲任务used idle 

2.7.1 LoRa自组网协调器设计

串口:我们需要牧场监控内的信息,控制器要与协调器进行数据通信
无线数据:入网没完成等待,是否有新节点加入,断电是否有旧节点加入
解决时钟飘移缺点,时钟同步

 2.7.2 LoRa自组网节点设计

思考 基于时分复用MAC协议如何开发协调器与节点程序

3 LoRa自组网集中器程序开发(网关)

3.1 制定通信协议

入网请求

从机->入网请求
名称字节数描述举例
帧头10x3C0x3C
长度1最长126字节;长度范围内不检测包头;计算整个帧长度0x0c
网络类型1字符<J>代表入网请求J
网络标识符2PANID,用于网络区分,只有PANID一样才可以组网通信0x0102
设备地址2设备唯一地址标识0x1235
CRC8校验1数据包校验,整个数据包,除校验位0x08
主机->入网应答
名称字节数描述举例
帧头10x3C0x3C
长度1最长126字节;长度范围内不检测包头;计算整个帧长度0x0c
网络类型1字符<A>代表入网成功A
网络标识符2PANID,用于网络区分,只有PANID一样才可以组网通信0x0102
设备地址2设备唯一地址标识0x1235
设备序号1设备是第几个入网1
CRC8校验1数据包校验,整个数据包,除校验位0x08

        设备标识符 PANID和zigbee组网一样,相同才能在一个网通信 

时间同步

        设备序号:第几个入网的,同时给节点分时间片

网络数据包

网络数据包
名称字节数描述举例
帧头1字符<N>代表网络数据包N
网络标识符2PANID,用于网络区分,只有PANID一样才可以组网通信0x0102
数据包包头10x210x21
包长1数据包长度,代表数据域数据长度1
数据类型10x00:数据、0x01:命令0x00
设备地址2设备标识符0x0001
传感器类型10x01:温湿度、0x02:三轴、0x03:风机、0x04:水表、0x05:地磁 0x06:灌溉0x01
数据4每种传感器数值用一个字节标识,比如温湿度占两个字节0x01298113
CRC8校验1数据包校验,整个数据包,除校验位0x08

3.2 工程模板修改

 

HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7|GPIO_PIN_15, GPIO_PIN_SET); 

3.2.1 Cubmx RTC外设配置

修改RTC时钟源为外部高速时钟
配置RTC分频系数
初始化日期和时间
配置Alarm参数
使能RTC全局中断

1s为单位.rtc时钟为250hz

 

3.2.2 Cubmx 定时器外设配置

配置TIM2分频系数
使能TIM2定时器中断 

1s为单位 

3.2.3 Cubmx 串口和ADC外设配置

 

把main函数的功能移到task文件夹下,数据解析、网络解析、协议等 

如果是从机增加一个ADC,用于生成随机数

配置ADC为连续采集
配置DMA通道
配置ADC标签

 

 

3.2.4 RTC任务

主要任务:RTC 提供实时时钟,用于时钟同步,开启闹钟中断

sTime的初值和sAlarm的初值都为0

void MX_RTC_Init(void)
{RTC_TimeTypeDef sTime;RTC_DateTypeDef sDate;RTC_AlarmTypeDef sAlarm;/**Initialize RTC Only */hrtc.Instance = RTC;hrtc.Init.HourFormat = RTC_HOURFORMAT_24;hrtc.Init.AsynchPrediv = 125-1;hrtc.Init.SynchPrediv = 2000-1;hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;if (HAL_RTC_Init(&hrtc) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}/**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;if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}sDate.WeekDay = RTC_WEEKDAY_MONDAY;sDate.Month = RTC_MONTH_APRIL;sDate.Date = 0x1;sDate.Year = 0x18;if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BCD) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}if(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0) != 0x32F2){HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR0,0x32F2);}/**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));if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN) != HAL_OK)  //设置闹钟中断{_Error_Handler(__FILE__, __LINE__);}}

闹钟事件回调函数,触发事件用于处理任务

协调器(主机):
        同步时钟标志(因为每加入的设备都需要同步时钟)
        获取时间,设置下次闹钟时间+5小时
节点(从机):
        发送更新数据标志
        闹钟发送数据的时间赋值,使能闹钟中断

毫秒单位转换为时分秒

//**********************************//
//函数名称: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;  //+5hourgAlarm.AlarmTime.Minutes = masterTime.Minutes;gAlarm.AlarmTime.Seconds = masterTime.Seconds;gAlarm.AlarmTime.SubSeconds = masterTime.SubSeconds;#else //SLAVERsendUpDataFlag = 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;
#endifif (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;
}

 3.2.5 定时器任务

定时器 用来节点超时的判断

定时器初始化

开启定时器中断

void MX_TIM2_Init(void)
{TIM_ClockConfigTypeDef sClockSourceConfig;TIM_MasterConfigTypeDef sMasterConfig;htim2.Instance = TIM2;htim2.Init.Prescaler = 480-1;htim2.Init.CounterMode = TIM_COUNTERMODE_UP;htim2.Init.Period = 100*1000-1;htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;if (HAL_TIM_Base_Init(&htim2) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}}

定时器定时事件任务

定时器中断溢出,要在里面做一些处理,判断节点是否入网超时

//**********************************//
//函数名称:   HAL_TIM_PeriodElapsedCallback
//函数描述:   定时器2溢出中断回调函数
//函数参数:   TIM_HandleTypeDef *htim
//返回值:     无
//*******************************//void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
//判断是否为定时器2中断
//累加全局计数值if(htim->Instance == htim2.Instance){JionNodeTimeCount++;}
}

3.2.5 CRC校验码及通信协议宏定义

protocol.c
1生成crc8校验码
2判断crc8校验码是否正确

#include "protocol.h"/******************************************************************************
* Name:    CRC-8               x8+x2+x+1
* Poly:    0x07
* Init:    0x00
* Refin:   False
* Refout:  False
* Xorout:  0x00
* Note:
*****************************************************************************/
uint8_t crc8(uint8_t *data, uint8_t length)
{uint8_t i;uint8_t crc = 0;        // Initial valuewhile(length--){crc ^= *data++;        // crc ^= *data; data++;for ( i = 0; i < 8; i++ ){if ( crc & 0x80 )crc = (crc << 1) ^ 0x07;elsecrc <<= 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;}
}
#ifndef _PROTOCOL_H
#define _PROTOCOL_H#include "stm32f0xx.h"#define JIONREQUEST      0x3C
#define NETDATA          'N'
#define DATAHEAD         0x21
#define  PAN_ID                 0x1010#ifdef MASTER
#define  ADDR                   0xFFFF  
#else
#define  ADDR                   0x1201   //0x1202 0x1203 ...
#endif#define HI_UINT16(a) (((a) >> 8) & 0xFF)
#define LO_UINT16(a) ((a) & 0xFF)uint8_t DataCrcVerify(uint8_t * buff, uint8_t len);
uint8_t crc8(uint8_t *data, uint8_t length);#endif

3.2.6 数据处理任务

dataprocess.c

主机

从机 

3.2.6.1 串口数据获取并无线发出去
//**********************************//
//函数名称:UartDmaGet   
//函数描述:串口数据获取   
//函数参数:   无
//返回值:     无
//*******************************//void UartDmaGet(void)
{if(UsartType1.receive_flag == 1)//如果过新的数据,在串口中断回调函数中实现{//串口接收到的数据原封发给SX1278Radio->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++;elseSlave_RxNumber++;
}//**********************************//
//函数名称:   TxDataPacketNum
//函数描述:   发送数据包计数
//函数参数:  无 
//返回值:     无
//*******************************//
void TxDataPacketNum(void)
{if(EnableMaster == true)Master_TxNumber++;elseSlave_TxNumber++;
}
3.2.6.2 读取无线射频数据
//**********************************//
//函数名称:  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);elseprintf("slave Rx Len = %d\n",BufferSize);      if( BufferSize > 0 )//&& (BufferSize == strlen((char*)Buffer))){//接收数据闪烁LedBlink( LED_RX );//计算接收数据的个数RxDataPacketNum();//清空sx127x接收缓冲区
#ifdef MASTERstatus = MasterProtocolAnalysis(Buffer,BufferSize);  //主机协议解析
#else      status = SlaveProtocolAnalysis(Buffer, BufferSize);  //从机协议解析
#endifmemset(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;
}

根据radio.h process的不同枚举类型进行业务处理

typedef enum
{RF_IDLE,     //空闲RF_BUSY,RF_RX_DONE,RF_RX_TIMEOUT,  RF_TX_DONE,RF_TX_TIMEOUT,RF_LEN_ERROR,RF_CHANNEL_EMPTY,RF_CHANNEL_ACTIVITY_DETECTED,
}tRFProcessReturnCodes;
3.2.6.3 主机协议解析
//**********************************//
//函数名称:   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)  //'N'{if((buff[1] == HI_UINT16(PAN_ID))&&(buff[2] == LO_UINT16(PAN_ID)))  // 0x10   0x10{Crc8Data = crc8(&buff[0], len - 1); //减去校验if(Crc8Data != buff[len - 1]){memset(buff,0,len);//清空缓存区return 0;}if(buff[3] == DATAHEAD)   //0x21{NetDataProtocolAnalysis(&buff[3], len - 3);   //网络数据包解析}}elsereturn 0;}else if(buff[0] == JIONREQUEST)        //0x3C{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;
}

入网协议解析(新设备添加入网表)

/************************************************************************/
/* 入网协议分析状态                                                                 */
/************************************************************************/
typedef enum 
{JION_HEADER = 0,JION_LENGHT,JION_TYPE,JION_PANID,JION_ADDR,JION_CRC
}JionProtocol//**********************************//
//函数名称:   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:  //0if (buff[status] == JIONREQUEST) //0x3C{status = JION_LENGHT;  } else{goto ERR;}break;case JION_LENGHT:  //1if(buff[status] == 0x06) //6个字节{status = JION_TYPE; } else{goto ERR;}break;case JION_TYPE:if (buff[status] == 'J') //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;  //0printf("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;
}

网络数据包解析

//**********************************//
//函数名称:   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");
}
3.2.6.4 从机协议解析
//**********************************//
//函数名称:   SendJionNetPacke
//函数描述:   从机入网数据发送
//函数参数:   无
//返回值:     无
//*******************************//void SendJionNetPacke(void)
{uint16_t addr = ADDR;  //0xFFFF 主机地址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)  //'N'  网络数据包{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] == 0x21)  //DATAHEAD{printf("Slave_NETDATA\n");if(buff[5] == 0x1)  //0x01 命令  0x00数据{if (buff[6] == HI_UINT16(ADDR) && buff[7] == LO_UINT16(ADDR)){if(buff[8] == 0x3)  //传感器类型{
#if defined(FAN)                if(buff[9] == true){FanOn();}else{FanOff();           }
#endif}}}}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);//使能RTCMX_RTC_Init();  //RTC同步return 0xFF;}}return 1;
}
3.2.6.5 从机数据上传 
//**********************************//
//函数名称:   SendSensorDataUP
//函数描述:   上传节点传感器数据
//函数参数:   无
//返回值:     无
//*******************************//void SendSensorDataUP(void)
{printf("SendSensorDataUP\n");
#if defined(MPU6050)mpu6050_ReadData(&Mx,&My,&Mz);  printf("Mx = %.3f\n",Mx);printf("My = %3f\n",My);printf("Mz = %3f\n",Mz);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 = 0x08;DataPacke_t.dataType = 0;DataPacke_t.deviceAddr[0] = HI_UINT16(ADDR);DataPacke_t.deviceAddr[1] = LO_UINT16(ADDR);DataPacke_t.sensorType  = 0x2;DataPacke_t.buff[0]  = (int8_t)(Mx*10);DataPacke_t.buff[1]  = (int8_t)(My*10);DataPacke_t.buff[2]  = (int8_t)(Mz*10);//校验码DataPacke_t.crcCheck = crc8((uint8_t *)&DataPacke_t,DataPacke_t.dataLength + 4);//发送数据包Radio->SetTxPacket((uint8_t *)&DataPacke_t, DataPacke_t.dataLength + 5);
#elif defined(DHT11)    DHT11_TEST();printf("TEMP = %d\n",ucharT_data_H);printf("HUM = %d\n",ucharRH_data_H);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 = 0x07;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]  = ucharT_data_H;DataPacke_t.buff[1]  = ucharRH_data_H;//校验码DataPacke_t.crcCheck = crc8((uint8_t *)&DataPacke_t,DataPacke_t.dataLength + 4);//发送数据包Radio->SetTxPacket((uint8_t *)&DataPacke_t, DataPacke_t.dataLength + 5);#elif defined(FAN)FanStaus = FanReadStaus();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 = 0x06;DataPacke_t.dataType = 0;DataPacke_t.deviceAddr[0] = HI_UINT16(ADDR);DataPacke_t.deviceAddr[1] = LO_UINT16(ADDR);DataPacke_t.sensorType  = 0x3;DataPacke_t.buff[0]  = FanStaus;//校验码DataPacke_t.crcCheck = crc8((uint8_t *)&DataPacke_t,DataPacke_t.dataLength + 4);//发送数据包Radio->SetTxPacket((uint8_t *)&DataPacke_t, DataPacke_t.dataLength + 5);#endif   }

3.2.7 网络处理任务

netprocess.c

主机

从机

 

 netprocess.h

#ifndef _NETPROCESS_H
#define _NETPROCESS_H#include "stm32f0xx.h"
#include "stdbool.h"#define NodeNumber	20extern volatile  uint16_t currentDeviceNumber;  //当前设备数量
extern volatile  uint16_t oldNodeNumber;
extern volatile uint32_t DataUpTime;extern uint8_t startUpTimeHours;  //启动时间
extern uint8_t startUpTimeMinute;
extern uint8_t startUpTimeSeconds;
extern uint32_t startUpTimeSubSeconds;extern uint8_t DataUpTimeHours;  //更新时间
extern uint8_t DataUpTimeMinute;
extern uint8_t DataUpTimeSeconds;
extern uint32_t DataUpTimeSubSeconds;
/************************************************************************/
/* 定义设备入网时的状态                                                 */
/************************************************************************/
typedef enum
{NO_JION = 0,  //未加入网络JIONING,	  //正在加入网络JIONTIMEOUT,  //入网超时JIONDONE,     //入网完成AGAIN_JION_NET
}DeviceJionStatus;/************************************************************************/
/* 入网协议分析状态                                                                 */
/************************************************************************/
typedef enum 
{JION_HEADER = 0,JION_LENGHT,JION_TYPE,JION_PANID,JION_ADDR,JION_CRC
}JionProtocol;/************************************************************************/
/* 定义设备节点加入标志                                                 */
/************************************************************************/
typedef enum
{No_Node_Jion_Flag = 0,Node_Jion_Finish_Flag,Node_Jion_No_Finish_Flag,New_Node_Jion_Flag
}DeviceJionFlag;/************************************************************************/
/* 设备入网,发送的消息体                                               */
/************************************************************************/
typedef struct 
{uint8_t	msgHead;	//入网消息头0x3Cuint8_t dataLength;	//数据长度 type~crcuint8_t netType;	//模块的网络类型uint8_t netPanid[2];	//设备的PANIDuint8_t deviceAddr[2];	//模块的设备地址uint8_t crcCheck;	//数据校验(整个数据包,除校验位)
}SlaveJionNet;/************************************************************************/
/* 设备数据,发送的消息体                                               */
/************************************************************************/
typedef struct 
{uint8_t	netmsgHead;	//入网消息头0x3Cuint8_t netPanid[2];	//设备的PANIDuint8_t	msgHead;	//数据消息头0x21uint8_t dataLength;	//数据长度 type~crcuint8_t dataType;	//模块的数据类型uint8_t deviceAddr[2];	//模块的设备地址uint8_t sensorType;	//模块的传感器类型uint8_t buff[4];        //每种传感器数值用两个字节标识,比如温湿度占四个字节uint8_t crcCheck;	//数据校验(整个数据包,除校验位)
}SlaveDataNet;/************************************************************************/
/* 更新RTC,发送的消息体                                               */
/************************************************************************/
typedef struct
{uint8_t	msgHead;	//入网消息头0x3Cuint8_t dataLength;	//数据长度 type~crcuint8_t netType;	//模块的网络类型uint8_t netPanid[2];	//设备的PANIDuint8_t timeData[5];	//模块的设备地址uint8_t crcCheck;	//数据校验(整个数据包,除校验位)
}SlaveRtcSync;/************************************************************************/
/* 设备信息                                                             */
/************************************************************************/
typedef struct  
{uint8_t deviceType;	//模块的设备类型DeviceJionStatus deviceNetStatus;	//设备的网络状态uint8_t deviceAddr[2];	//模块的设备地址uint8_t deviceId;	//表示在网表中加入第几个设备uint8_t deviceData[20];
}SlaveInfo;uint16_t RandomNumber(void);
uint8_t SlaveJionNetFuction(void);
void SlaveGetSendTime(void);
DeviceJionFlag WaitJionNetFinish(uint8_t timout);
void MasterSendClockData(void);#endif

netprocess.c

//所有节点的更新周期(在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;//时钟同步
SlaveRtcSync rtcSync_t;//初始化网络状态
volatile DeviceJionStatus NetStatus = NO_JION;extern tRadioDriver *Radio;
 3.2.7.1 主机等待从机入网完成
//**********************************//
//函数名称:  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;}}//等待加入网络}
}
 3.2.7.2 主机发送同步时钟
//**********************************//
//函数名称:   MasterSendClockData
//函数描述:   主机发送同步时钟
//函数参数:   无
//返回值:     无
//*******************************//void MasterSendClockData(void)
{RTC_TimeTypeDef thisTime;rtcSync_t.msgHead = JIONREQUEST;  //0x3crtcSync_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);}

 3.2.7.3 生成随机数
//**********************************//
//函数名称:   RandomNumber
//函数描述:   生成随机数
//函数参数:   无
//返回值:     随机数
//*******************************//uint16_t RandomNumber(void)
{uint16_t randNumber = 0;float adcValue = 0;uint32_t u32adcValue = 0;//开启DMA转换ADCHAL_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;
}

库函数 放到时间数里面,循环读10次,转换为uint8 ,最大是2550,返回(0-2.5s) 

 

3.2.7.4 从机加入网络
//**********************************//
//函数名称:   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;
}
3.2.7.5 从机获取时间片
//**********************************//
//函数名称:   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);
}

3.2.8  获取数据发送时长

lora获取时间片的同时需要考虑数据发送的时长

 

//**********************************//
//函数名称: SX1276LoRaGetTransferTime  
//函数描述: 获取数据发送时长
//函数参数:   无
//返回值:     float
//*******************************//
float SX1276LoRaGetTransferTime( void )
{uint16_t PayloadSymNb_Ceil,SF, PayloadSymNb_Ceil_DenoMinator;float Tsym = 0,Tpreamble = 0,PayloadSymNb = 0,Tpayload=0,Tpacket=0;bool H;uint8_t DE = 0;//计算符号速率SF = (2 << LoRaSettings.SpreadingFactor-1);Tsym = (SF*1000 / (float)SignalBw[LoRaSettings.SignalBw]);
//	PRINTF2("Tsym:%0.3f\n", Tsym);//前导码时间Tpreamble = (SX1276LoRaGetPreambleLength() + 4.25)*Tsym;
//	PRINTF2("Tpreamble:%0.3f\n", Tpreamble);//有效负载符号数H = !LoRaSettings.ImplicitHeaderOn;DE = SX1276LoRaGetLowDatarateOptimize();PayloadSymNb_Ceil_DenoMinator = (((8 * LoRaSettings.PayloadLength) - (4 * LoRaSettings.SpreadingFactor) + 28 + 16 - (20 * H)));
//	PRINTF2("PayloadSymNb_Ceil_DenoMinator:%d\n", PayloadSymNb_Ceil_DenoMinator);PayloadSymNb_Ceil = (uint16_t)ceil(((double)PayloadSymNb_Ceil_DenoMinator) / (4 * (LoRaSettings.SpreadingFactor - 2 * DE)));
//	PRINTF2("PayloadSymNb_Ceil:%d\n", PayloadSymNb_Ceil);PayloadSymNb = 8 + max((PayloadSymNb_Ceil)*(LoRaSettings.ErrorCoding + 4), 0);Tpayload = PayloadSymNb * Tsym;
//	PRINTF2("Tpayload:%0.3f\n", Tpayload);//计算传输时间Tpacket = (Tpreamble + Tpayload)/1000;return Tpacket;
}//**********************************//
//函数名称:   Sx127xGetSendTime
//函数描述:   获取节点发送时间片
//函数参数:   uint8_t num, float timeUp, uint32_t dataUpTimePeriod
//返回值:     时间片
//*******************************//uint16_t Sx127xGetSendTime(uint8_t num, float timeUp, uint32_t dataUpTimePeriod)
{uint16_t startTime = 0;/* 连个节点之间数据传输间隔最小为500Ms */if ( ((timeUp + 500) * num ) > dataUpTimePeriod){}/* 每个节点的所占间隙的时间长度,已经包含发送时间和空闲时间 */startTime = dataUpTimePeriod / num;/* 获得计算数据的整数部分,向上取整*/return ((uint16_t)ceil((double)startTime * slaveNativeInfo_t.deviceId));
}

 

3.2.8 主程序

主机:

等待入网
是否是新节点,如是改变节点入网状态未完成
是否旧节点,如是改变节点入网状态未完成
时钟同步时间是否到,到了时钟同步

从机:

节点都已经在上面部分实现,只需要等待接收数据,数据解析
判断是否到达定时发送时间,发送数据,并清空标志位。

1.adc读取,返回随机时间
2.打印地址
3.发送完后收到应答包,加入完成
4.时间片是0,因为第一个
5.一分钟一个rtc闹钟
6网络同步
7收到同步包
8设置了时间
9 1分钟传一个数据,上传一次数据
10 还有另一个设备发送的,节点也会收到。

int main(void)
{uint8_t RegVersion = 0;uint8_t str[20] = {0};uint16_t addr = ADDR;HAL_Init();uint32_t DelayTime = 0;SystemClock_Config();MX_GPIO_Init();MX_DMA_Init();MX_ADC_Init();MX_USART1_UART_Init();MX_SPI1_Init();MX_RTC_Init();MX_TIM2_Init();#if defined (MPU6050)//初始化三轴传感器  MX_I2C1_Init();InitMpu6050();  #elif defined (FAN)D1_OUT_GPIO_Init();
#endifLcd_Init();// showimage(gImage_logo);HAL_Delay(500);Lcd_Clear(YELLOW);Gui_DrawFont_GBK16(0,0,RED,GREEN,"  LoRa Topology  ");
#if defined (SLAVE)  Gui_DrawFont_GBK16(0,16,RED,GREEN,"     Slave      ");#if defined (MPU6050)  //三轴传感器显示Gui_DrawFont_GBK16(0,48,BLACK,YELLOW,"X:");Gui_DrawFont_GBK16(0,64,BLACK,YELLOW,"Y:");Gui_DrawFont_GBK16(0,80,BLACK,YELLOW,"Z:");
#elif defined (DHT11)//空气温湿度传感器显示Gui_DrawFont_GBK16(0,48,BLACK,YELLOW,"TEMP:");Gui_DrawFont_GBK16(0,64,BLACK,YELLOW,"HUM:");#elif defined (FAN)//风扇传感器显示Gui_DrawFont_GBK16(0,48,BLACK,YELLOW,"FAN:");#endif#elif defined (MASTER)Gui_DrawFont_GBK16(0,16,RED,GREEN,"     Master     ");Gui_DrawFont_GBK16(0,48,BLACK,YELLOW,"RX:");Gui_DrawFont_GBK16(0,64,BLACK,YELLOW,"TX:");#endifGui_DrawFont_GBK16(0,32,BLACK,YELLOW,"ADDR:");sprintf((char*)str,"%x",addr);Gui_DrawFont_GBK16(64,32,BLACK,YELLOW,str);HAL_SPI_DeInit(&hspi1);MX_SPI1_Init();//启动串口1,使能串口空闲中断  __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE); HAL_UART_Receive_DMA(&huart1,a_Usart1_RxBuffer,RXLENGHT); SX1276Read( REG_LR_VERSION, &RegVersion );if(RegVersion != 0x12){printf("LoRa read Error!\r\n");printf("LoRa RegVersion = %d!\r\n",RegVersion);}else{printf("LoRa read Ok!\r\n");printf("LoRa RegVersion = %d!\r\n",RegVersion);}//读到版本号后,关闭3种灯LedOff(LED_RX);LedOff(LED_TX);LedOff(LED_NT);Radio = RadioDriverInit();Radio->Init();printf("systerm init ok!\n");Radio->StartRx( );  #if SLAVE//获取随机入网时间DelayTime = RandomNumber();printf("JionTime = %d\n",DelayTime);HAL_Delay(DelayTime);//等待入网成功while (SlaveJionNetFuction());//获取节点发送时间片SlaveGetSendTime();#else//主机直接初始化RTCMX_RTC_Init();HAL_TIM_Base_Start_IT(&htim2);#endifwhile (1){Sx127xDataGet();#if SLAVEif(sendUpDataFlag == 1){SendSensorDataUP();sendUpDataFlag = 0;}#elseUartDmaGet();//等待节点入网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;}}
#endifif(EnableMaster == true){MLCD_Show();}else{SLCD_Show();}}
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/302884.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FL Studio21水果软件有哪些新功能?如何下载破解版

FL Studio 21是一款由Image-Line公司开发的专业的音乐制作软件&#xff0c;它提供了音乐编曲、录音、编辑、混音等多种功能&#xff0c;非常适合专业音乐制作人、DJ及音乐爱好者使用。这款软件不仅具有高级的音频编辑功能&#xff0c;如切片、时间伸缩、音高调整&#xff0c;还…

场景文本检测识别学习 day02(AlexNet论文阅读、ResNet论文精读)

怎么读论文 在第一遍阅读的时候&#xff0c;只需要看题目&#xff0c;摘要和结论&#xff0c;先看题目是不是跟我的方向有关&#xff0c;看摘要是不是用到了我感兴趣的方法&#xff0c;看结论他是怎么解决摘要中提出的问题&#xff0c;或者怎么实现摘要中的方法&#xff0c;然…

机器学习(五) -- 监督学习(2) -- k近邻

系列文章目录及链接 目录 前言 一、K近邻通俗理解及定义 二、原理理解及公式 1、距离度量 四、接口实现 1、鸢尾花数据集介绍 2、API 3、流程 3.1、获取数据 3.2、数据预处理 3.3、特征工程 3.4、knn模型训练 3.5、模型评估 3.6、结果预测 4、超参数搜索-网格搜…

QT drawPixmap和drawImage处理图片模糊问题

drawPixmap和drawImage显示图片时&#xff0c;如果图片存在缩放时&#xff0c;会出现模糊现象&#xff0c;例如将一个100x100 的图片显示到30x30的区域&#xff0c;这个时候就会出现模糊。如下&#xff1a; 实际图片&#xff1a; 这个问题就是大图显示成小图造成的像素失真。 当…

【stm32】I2C通信协议

【stm32】I2C通信协议 概念及原理 如果我们想要读写寄存器来控制硬件电路&#xff0c;就至少需要定义两个字节数据 一个字节是我们要读写哪个寄存器&#xff0c;也就是指定寄存器的地址 另一个字节就是这个地址下存储寄存器的内容 写入内容就是控制电路&#xff0c;读出内容就…

利用IP地址判断羊毛用户:IP数据云提供IP风险画像

在当今数字化社会&#xff0c;互联网已经成为人们日常生活和商业活动中不可或缺的一部分。然而&#xff0c;随着网络的普及&#xff0c;网络欺诈行为也日益猖獗&#xff0c;其中包括了羊毛党这一群体。羊毛党指的是利用各种手段获取利益、奖励或者优惠而频繁刷取优惠券、注册账…

FME学习之旅---day21

我们付出一些成本&#xff0c;时间的或者其他&#xff0c;最终总能收获一些什么。 教程&#xff1a;AutoCAD 变换 相关的文章 为您的 DWG 赋予一些样式&#xff1a;使用 DWGStyler、模板文件、块等 FME数据检查器在显示行的方式上受到限制。它只能显示线条颜色&#xff0c;而…

Java NIO Selector选择器源码分析

文章目录 前言Selector类结构Selector抽象类AbstractSelectorSelectorImplWindowsSelectorImpl三种SelectionKey集合 前言 Java NIO&#xff08;New I/O&#xff09;的Selector选择器是一个用于多路复用&#xff08;Multiplexing&#xff09;的I/O操作的关键组件。它允许一个单…

弹幕功能1

今天看pure-admin的时候发现有个弹幕功能 GitHub - hellodigua/vue-danmaku: 基于 Vue 的弹幕交互组件 | A danmaku component for Vue

使用VPN时,Java程序无法访问远程网络的解决办法

应用场景&#xff1a; 电脑连接VPN之后&#xff0c;Java程序无法连接远程服务&#xff0c;比如第三方接口、远程数据库连接、远程微服务等。我个人遇到的情况有连接海康威视SDK&#xff0c;influxdb以及一些微服务。 解决办法&#xff1a; 启动Java时加入参数&#xff1a;-D…

Webots常用的执行器(Python版)

文章目录 1. RotationalMotor2. LinearMotor3. Brake4. Propeller5. Pen6. LED 1. RotationalMotor # -*- coding: utf-8 -*- """motor_controller controller."""from controller import Robot# 实例化机器人 robot Robot()# 获取基本仿真步长…

LeetCode 热题 100 题解(二):双指针部分(1)

题目一&#xff1a;移动零&#xff08;No. 283&#xff09; 题目链接&#xff1a;https://leetcode.cn/problems/move-zeroes/description/?envTypestudy-plan-v2&envIdtop-100-liked 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同…

惠海 H4029 同步整流降压芯片IC 支持24V/36V转12V/5V/3.3V5A方案 大电流温度低

同步整流降压芯片IC是一种高效能的电源管理方案&#xff0c;用于将较高的输入电压&#xff08;如24V或36V&#xff09;转换为较低的输出电压&#xff08;如12V、5V或3.3V&#xff09;&#xff0c;同时提供高达5A的大电流输出。这种芯片采用同步整流技术&#xff0c;相比传统的线…

自动驾驶基础技术-无迹卡尔曼滤波UKF

自动驾驶基础技术-无迹卡尔曼滤波UKF Unscented Kalman Filter是解决非线性卡尔曼滤波的另一种思路&#xff0c;它利用Unscented Transform来解决概率分布非线性变换的问题。UnScented Kalman Filter不需要像Extended Kalman Filter一样计算Jacobin矩阵&#xff0c;在计算量大…

Vue通过自定义指令实现元素平滑上升的动画效果。没一句废话

1、演示 2、介绍 这个指令不是原生自带的&#xff0c;需要手动去书写&#xff0c;但是这辈子只需要编写这一次就好了&#xff0c;后边可以反复利用。 用到的API&#xff1a;IntersectionObserver 这里有详细介绍 3、Vue文件代码 <template><div class"container&…

软件测试面试入职了,背完这写轻松上岸

全网首发-涵盖16个技术栈 第一部分&#xff0c;测试理论&#xff08;测试基础需求分析测试模型测试计划测试策略测试案例等等&#xff09; 第二部分&#xff0c;Linux&#xff08; Linux基础Linux练习题&#xff09; 第三部分&#xff0c;MySQL&#xff08;基础知识查询练习…

AI技术创业有哪些机会?

引言 在当今数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;技术正不断地推动着各行各业的创新和变革。AI作为一项具有巨大潜力的技术&#xff0c;正在为创业者带来许多新的机会。本文将探讨AI技术创业领域中的机会&#xff0c;并通过具体的例子来说明它们。 1. 智…

学习操作系统之多道批处理系统

1964年IBM生产了第一台小规模集成电路计算机IBM System/360&#xff08;第三代计算机&#xff09;&#xff0c;并为该计算机开发了OS/360操作系统&#xff0c;是第一个多道批处理系统。 多道批处理的运行机制&#xff1a; 多道批处理系统同样要求事先将多道作业存放到外存上并…

lora微调过程

import os import pickle from transformers import AutoModelForCausalLM from peft import get_peft_config, get_peft_model, get_peft_model_state_dict, LoraConfig, TaskTypedevice "cuda:0"#1.创建lora微调基本的配置 peft_config LoraConfig(task_typeTask…

Fecify站点斗篷cloak

斗篷cloak站点斗篷模式功能发布&#xff01;全新的应用场景&#xff0c;该模式是针对推广不用GMC&#xff0c;而是通过facebook&#xff0c;或者其他的一些平台/工具推广&#xff0c;这些推广方式的特点是&#xff1a;不需要商品的图片&#xff0c;或者说不会排查商品图片的侵权…