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.mzph.cn/news/795231.shtml

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

相关文章

Kubernetes 高可用性入门:初学者指南

Kubernetes 高可用性解释 引言一、需要 Kubernetes 高可用性二、Kubernetes 控制平面的高可用性2.1、etcd2.2、API 服务器2.3、Kube 调度器2.4、Kube 控制器管理器2.5、云控制器管理器 三、工作节点的高可用性四、Kubernetes 集群可用性度量五、Kubernetes 可用性常见问题六、总…

(学习日记)2024.04.07:UCOSIII第三十五节:互斥量实验

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

前端学习<三>CSS进阶——0102-CSS布局样式

前言 css 进阶的主要内容如下。 1、css 非布局样式 html 元素的分类和特性 css 选择器 css 常见属性&#xff08;非布局样式&#xff09; 2、css 布局相关 css 布局属性和组合解析 常见布局方案 三栏布局案例 3、动画和效果 属于 css 中最出彩的内容。 多背景多投影特…

如何用putty通过ssh连接ubuntu

1. 下载和安装PuTTY 访问PuTTY官网下载PuTTY的最新版本。 2. 打开PuTTY 解压下载的文件后&#xff0c;找到PuTTY文件并双击打开。 3. 配置SSH连接 在ubuntu下安装ssh服务在安装ssh时&#xff0c;我一直遇到一个问题&#xff0c;原因是我的虚拟机连不上网&#xff0c;反复实…

深入理解 SQL 中的数据集合和数据关联

引言 在数据库管理系统中&#xff0c;数据集合和数据关联是 SQL 查询中常见的概念。它们是构建复杂查询和分析数据的基石。本文将深入探讨 SQL 中的数据集合和数据关联&#xff0c;包括它们的概念、常见用途以及实际示例。 首先引入一下数学中的集合 集合的基本概念&#x…

加速度:电子元器件营销网站的功能和开发周期

据工信部预计&#xff0c;到2023年&#xff0c;我国电子元器件销售总额将达到2.1万亿元。随着资本的涌入&#xff0c;在这个万亿级赛道&#xff0c;市场竞争变得更加激烈的同时&#xff0c;行业数字化发展已是大势所趋。电子元器件B2B商城平台提升数据化驱动能力&#xff0c;扩…

Flutter Don‘t use ‘BuildContext‘s across async gaps.

Flutter提示Don‘t use ‘BuildContext‘s across async gaps.的解决办法—flutter里state的mounted属性

[蓝桥杯 2019 国 C] 数正方形

[蓝桥杯 2019 国 C] 数正方形 题目描述 在一个 N N N \times N NN 的点阵上&#xff0c;取其中 4 4 4 个点恰好组成一个正方形的 4 4 4 个顶点&#xff0c;一共有多少种不同的取法&#xff1f; 由于结果可能非常大&#xff0c;你只需要输出模 1 0 9 7 10^9 7 1097 的…

吴恩达机器学习笔记:第 6 周-11机器学习系统的设计(Machine Learning System Design)11.1-11.5

目录 第 6 周 11、 机器学习系统的设计(Machine Learning System Design)11.1 首先要做什么11.2 误差分析11.3 类偏斜的误差度量11.4 查准率和查全率之间的权衡11.5 机器学习的数据 第 6 周 11、 机器学习系统的设计(Machine Learning System Design) 11.1 首先要做什么 在接…

leetcode.24. 两两交换链表中的节点

题目 给定一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后的链表。 你不能只是单纯的改变节点内部的值&#xff0c;而是需要实际的进行节点交换。 思路 创建虚拟头节点&#xff0c;画图&#xff0c;确认步骤。 实现 /*** Definition for singly-li…

docker安装、调试qsign签名服务器

go-cqhttp 在 Docker 里早就部署好了&#xff0c;由于没有搭建 qsign 签名服务器&#xff0c;所以迟迟不敢上线。今天终于搞定了在 Docker 下安装 qsign 签名服务器了。这次用的docker市场里找到的镜像&#xff0c;下次找时间制作一个自己的镜像。 1 拉取和运行镜像&#xff1a…

C# MES通信从入门到精通(9)——c#使用visual studio2019社区版部署webservice服务

前言 在上位机软件开发领域,我们经常使用调用webservice接口来和mes系统进行交互,我们在开发Mes程序之前也可以自己先模拟发布客户的webservice接口进行调用从而加快软件开发进度,本文就是详细介绍如何自己发布webservice接口。 1、创建 参考这篇博文:webservice服务创建…

图像处理与视觉感知---期末复习重点(6)

文章目录 一、图像分割二、间断检测2.1 概述2.2 点检测2.3 线检测2.4 边缘检测 三、边缘连接3.1 概述3.2 Hough变换3.3 例子3.4 Hough变换的具体步骤3.5 Hough变换的法线表示形式3.6 Hough变换的扩展 四、阈值处理4.1 概述4.2 计算基本全局阈值算法4.3 自适应阈值 五、基于区域…

LeetCode-131. 分割回文串【字符串 动态规划 回溯】

LeetCode-131. 分割回文串【字符串 动态规划 回溯】 题目描述&#xff1a;解题思路一&#xff1a;回溯&#xff0c; 回溯三部曲解题思路二&#xff1a;0解题思路三&#xff1a;0 题目描述&#xff1a; 给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个…

常用软件架构模式优缺点及应用场景

1、分层架构模式 最常见的架构模式就是分层架构或者称为 n 层架构。大部分软件架构师、设计师和开发者都对这个架构模式非常熟悉。尽管对于层的数量和类型没有具体限制&#xff0c;但大部分分层架构主要由四层组成&#xff1a;展现层、业务层、持久层和数据库层&#xff0c;如…

Peter算法小课堂—树状数组

大家好&#xff0c;我是人见人爱&#xff0c;花见花开&#xff0c;车见车爆胎的树状数组Peter Pan&#xff0c;hhh 讲正文前&#xff0c;先来一个长文警告⚠很重要的知识点&#xff1a;L SB&#xff08;SB&#xff1f;&#xff09; LSB 怎么算呢&#xff1f; 哦……懂了&…

LeetCode-79. 单词搜索【数组 字符串 回溯 矩阵】

LeetCode-79. 单词搜索【数组 字符串 回溯 矩阵】 题目描述&#xff1a;解题思路一&#xff1a;回溯 回溯三部曲。这里比较关键的是给board做标记&#xff0c;防止之后搜索时重复访问。解题思路二&#xff1a;回溯算法 dfs,直接看代码,很容易理解。visited哈希&#xff0c;防止…

计算机网络——33多点访问协议

多点访问协议 多路访问链路和协议 两种类型的链路&#xff08;一个子网内部链路连接形式&#xff09; 点对点 拨号访问的PPP以太网交换机和主机之间的点对点链路 广播 传统以太网HFC上行链路802.11无线局域网 多路访问协议 单个共享的广播型链路 2个过更多结点同时传送&am…

c# 指数搜索(Exponential Search)

该搜索算法的名称可能会产生误导&#xff0c;因为它的工作时间为 O(Log n)。该名称来自于它搜索元素的方式。 给定一个已排序的数组和要 搜索的元素 x&#xff0c;找到 x 在数组中的位置。 输入&#xff1a;arr[] {10, 20, 40, 45, 55} x 45 输出&#xff1a;在索…

Aurora8b10b(1)IP核介绍并基于IP核进行设计

文章目录 前言一、IP核设置二、基于IP核进行设计2.1、设计框图2.2、aurora_8b10b_0模块2.3、aurora_8b10b_0_CLOCK_MODULE2.4、aurora_8b10b_0_SUPPORT_RESET_LOGIC2.5、aurora8b10b_channel模块2.6、IBUFDS_GTE2模块2.7、aurora_8b10b_0_gt_common_wrapper模块2.8、aurora8b10…