基于STM32+LoRa+OneNET+MQTT+安卓小程序的水质监测系统

1.系统背景:

在我国环境发展进程中,水污染问题一直是一个大问题。比如说生活垃圾乱丢乱,工业污水随意排放,农业生产过程中产生的农药化肥残留等等,这些都很容易造成水质污染。同时,日本在今年多次排放核废水,导致污染海洋水资源。而水质监测作为治理水污染的重要手段,通过专业的数据对比,问题分析,能够充分了解水污染的源头,水污染的现状、扩张速度以及可能造成的危害,为治理水污染问题提供数据资料和经验,帮助专业人员做出正确的判断,从而设计制定合理的治理方案,最终有效改善水质问题。

2.系统架构图:

1.硬件材料:PH传感器、TDS(浊度传感器)、DS18B20、STM32F103C8T6、ESP8266、LoRa三个

2.硬件原理图:

PCB布线铺铜后效果图:

3.方案设计:

本次系统主要是三个STM32进行组网,以LoRa为网关,其中两个STM32当中节点,另外一个STM32作为协调器,两个节点采集数据,通过LoRa将数据传输到协调器,协调器在通过ESP8266将数据发布到OneNET,协议走的是MQTT,本系统没有下发控制,所有使用HTTP也是可以的。最后是在安卓APP进行展示数据。

4.软件设计

软件主要是分为几大部分,第一部分即为传感器采集数据,第二部分数据网关传输,第三部分数据发布到OneNET,最后一部分即为android小程序获取数据并展示数据。

1.传感器采集数据程序

DS18B20采集程序:

#include "ds18b20.h"
#include "stm32f10x.h"


//
float TEMP_Value=0;

 /**************************************************************************************
 * 描  述 : 配置DS18B20用到的I/O口
 * 入  参 : 无
 * 返回值 : 无
 **************************************************************************************/
static void DS18B20_GPIO_Config(void)
{		
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(DS18B20_CLK, ENABLE); 
															   
  GPIO_InitStructure.GPIO_Pin = DS18B20_PIN;	
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽输出  
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
  GPIO_Init(DS18B20_PORT, &GPIO_InitStructure);
	
	GPIO_SetBits(DS18B20_PORT, DS18B20_PIN);	 //DS18B20数据引脚初始化配置为高电平输出
}

 /**************************************************************************************
 * 描  述 : 配置使DS18B20-DATA引脚变为输入模式
 * 入  参 : 无
 * 返回值 : 无
 **************************************************************************************/
static void DS18B20_Mode_IPU(void)
{
 	  GPIO_InitTypeDef GPIO_InitStructure;
	
	  GPIO_InitStructure.GPIO_Pin = DS18B20_PIN; 
	  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;	  //上拉输入
	  GPIO_Init(DS18B20_PORT, &GPIO_InitStructure);
}

 /**************************************************************************************
 * 描  述 : 配置使DS18B20-DATA引脚变为输出模式
 * 入  参 : 无
 * 返回值 : 无
 **************************************************************************************/
static void DS18B20_Mode_Out_PP(void)
{
 	GPIO_InitTypeDef GPIO_InitStructure;
															   
  GPIO_InitStructure.GPIO_Pin = DS18B20_PIN;	

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   //推挽输出   
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(DS18B20_PORT, &GPIO_InitStructure);
}

 /**************************************************************************************
 * 描  述 : 主机给从机发送复位脉冲
 * 入  参 : 无
 * 返回值 : 无
 **************************************************************************************/
static void DS18B20_Rst(void)
{
	DS18B20_Mode_Out_PP();     //主机设置为推挽输出 
	
	DS18B20_DATA_OUT(LOW);     //主机至少产生480us的低电平复位信号
	delay_us(750);
	DS18B20_DATA_OUT(HIGH);   //主机在产生复位信号后,需将总线拉高
	delay_us(15);   //从机接收到主机的复位信号后,会在15~60us后给主机发一个存在脉冲
}

 /**************************************************************************************
 * 描  述 : 检测从机给主机返回的存在脉冲
 * 入  参 : 无
 * 返回值 : 0:成功   1:失败  
 **************************************************************************************/
static u8 DS18B20_Presence(void)
{
	u8 pulse_time = 0;
	
	DS18B20_Mode_IPU();    //主机设置为上拉输入
	
	/* 等待存在脉冲的到来,存在脉冲为一个60~240us的低电平信号 
	 * 如果存在脉冲没有来则做超时处理,从机接收到主机的复位信号后,会在15~60us后给主机发一个存在脉冲
	 */
	while( DS18B20_DATA_IN() && pulse_time<100 )
	{
		pulse_time++;
		delay_us(1);
	}	

	if( pulse_time >=100 )  //经过100us后,存在脉冲都还没有到来
		return 1;             //读取失败
	else                 //经过100us后,存在脉冲到来
		pulse_time = 0;    //清零计时变量
	
	while( !DS18B20_DATA_IN() && pulse_time<240 )  // 存在脉冲到来,且存在的时间不能超过240us
	{
		pulse_time++;
		delay_us(1);
	}	
	if( pulse_time >=240 ) // 存在脉冲到来,且存在的时间超过了240us
		return 1;        //读取失败
	else
		return 0;
}

 /**************************************************************************************
 * 描  述 : 从DS18B20读取一个bit
 * 入  参 : 无
 * 返回值 : u8 
 **************************************************************************************/
static u8 DS18B20_Read_Bit(void)
{
	u8 dat;
	
	/* 读0和读1的时间至少要大于60us */	
	DS18B20_Mode_Out_PP();
	/* 读时间的起始:必须由主机产生 >1us <15us 的低电平信号 */
	DS18B20_DATA_OUT(LOW);
	delay_us(10);
	
	/* 设置成输入,释放总线,由外部上拉电阻将总线拉高 */
	DS18B20_Mode_IPU();
	
	if( DS18B20_DATA_IN() == SET )
		dat = 1;
	else
		dat = 0;
	
	/* 这个延时参数请参考时序图 */
	delay_us(45);
	
	return dat;
}

 /**************************************************************************************
 * 描  述 : 从DS18B20读一个字节,低位先行
 * 入  参 : 无
 * 返回值 : u8  
 **************************************************************************************/
u8 DS18B20_Read_Byte(void)
{
	u8 i, j, dat = 0;	
	
	for(i=0; i<8; i++) 
	{
		j = DS18B20_Read_Bit();		//从DS18B20读取一个bit
		dat = (dat) | (j<<i);
	}
	
	return dat;																																																																																
}

 /**************************************************************************************
 * 描  述 : 写一个字节到DS18B20,低位先行
 * 入  参 : u8
 * 返回值 : 无  
 **************************************************************************************/
void DS18B20_Write_Byte(u8 dat)
{
	u8 i, testb;
	DS18B20_Mode_Out_PP();
	
	for( i=0; i<8; i++ )
	{
		testb = dat&0x01;
		dat = dat>>1;		
		/* 写0和写1的时间至少要大于60us */
		if (testb)
		{			
			DS18B20_DATA_OUT(LOW);
			delay_us(8);   //1us < 这个延时 < 15us
			
			DS18B20_DATA_OUT(HIGH);
			delay_us(58);    //58us+8us>60us
		}		
		else
		{			
			DS18B20_DATA_OUT(LOW);  
			/* 60us < Tx 0 < 120us */
			delay_us(70);
			
			DS18B20_DATA_OUT(HIGH);			
			/* 1us < Trec(恢复时间) < 无穷大*/
			delay_us(2);
		}
	}
}

 /**************************************************************************************
 * 描  述 : 起始DS18B20
 * 入  参 : 无
 * 返回值 : 无  
 **************************************************************************************/
void DS18B20_Start(void)
{
	DS18B20_Rst();	           //主机给从机发送复位脉冲
	DS18B20_Presence();	       //检测从机给主机返回的存在脉冲
	DS18B20_Write_Byte(0XCC);		 // 跳过 ROM 
	DS18B20_Write_Byte(0X44);		 // 开始转换 
}

 /**************************************************************************************
 * 描  述 : DS18B20初始化函数
 * 入  参 : 无
 * 返回值 : u8
 **************************************************************************************/
u8 DS18B20_Init(void)
{
	DS18B20_GPIO_Config();   
	DS18B20_Rst();
	
	return DS18B20_Presence();
}

 /**************************************************************************************
 * 描  述 : 从DS18B20读取温度值
 * 入  参 : 无  
 * 返回值 : float 
 **************************************************************************************/
float DS18B20_Get_Temp(void)
{
	u8 temp_H, temp_L;
	short s_tem;
	float f_tem;
	
//	DS18B20_Init();
	
	DS18B20_Rst();	   
	DS18B20_Presence();	 
	DS18B20_Write_Byte(0XCC);				/* 跳过 ROM */
	DS18B20_Write_Byte(0X44);				/* 开始转换 */
	
	DS18B20_Rst();
  DS18B20_Presence();
	DS18B20_Write_Byte(0XCC);				/* 跳过 ROM */
  DS18B20_Write_Byte(0XBE);				/* 读温度值 */
	
	temp_L = DS18B20_Read_Byte();		 
	temp_H = DS18B20_Read_Byte(); 
	
	s_tem = temp_H<<8;
	s_tem = s_tem | temp_L;
	
	if( s_tem < 0 )		/* 负温度 */
		f_tem = (~s_tem+1) * 0.0625;	
	else
		f_tem = (s_tem * 0.0625);
	  
																	//这样做的目的将小数点后第一位也转换为可显示数字
																	//同时进行一个四舍五入操作。
	  
	return f_tem; 	
}

TDS浊度传感器:

#include "adc.h"
#include "SysTick.h"
#include "ds18b20.h"


//#define ARRY_LENGTH 10

// 用于保存转换计算后的电压值 	 
float ADC_ConvertedValueLocal[4]; 
//温度校准系数
float compensationCoefficient=1.0;
//温度ds18b20值
extern float TEMP_Value;

float compensationVolatge;



float kValue=1.67;


/*******************************************************************************
* 函 数 名         : ADCx_Init
* 函数功能		   : ADC初始化	
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void ADCx_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量	
	ADC_InitTypeDef       ADC_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;//ADC
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;	//模拟输入
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;//非扫描模式	
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//关闭连续转换
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//禁止触发检测,使用软件触发
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐	
	ADC_InitStructure.ADC_NbrOfChannel = 1;//1个转换在规则序列中 也就是只转换规则序列1 
	ADC_Init(ADC1, &ADC_InitStructure);//ADC初始化
	
	ADC_Cmd(ADC1, ENABLE);//开启AD转换器
	
	ADC_ResetCalibration(ADC1);//重置指定的ADC的校准寄存器
	while(ADC_GetResetCalibrationStatus(ADC1));//获取ADC重置校准寄存器的状态
	
	ADC_StartCalibration(ADC1);//开始指定ADC的校准状态
	while(ADC_GetCalibrationStatus(ADC1));//获取指定ADC的校准程序

	ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能或者失能指定的ADC的软件转换启动功能
}



/*******************************************************************************
* 函 数 名         : Get_ADC_Value
* 函数功能		     : 获取通道ch的转换值,取times次,然后平均 	
* 输入 ch          : 通道编号
* times            : 获取次数
* 输    出         : 通道ch的times次转换结果平均值
*******************************************************************************/
float Get_ADC_Value_TDS(u8 ch,u8 times)
{
	u32 temp_val=0;
	float tds_value=0.0;
	u8 t;
	//设置指定ADC的规则组通道,一个序列,采样时间
	ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5);	//ADC1,ADC通道,239.5个周期,提高采样时间可以提高精确度			    
	
	for(t=0;t<times;t++)
	{
		ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能指定的ADC1的软件转换启动功能	
		while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
		temp_val+=ADC_GetConversionValue(ADC1);
		delay_ms(5);
	}	
	
	ADC_ConvertedValueLocal[0]=(float)((temp_val/times)/4096*3.3); //AD转换
	compensationCoefficient=1.0+0.02*((TEMP_Value/10)-25.0); 
	compensationVolatge=ADC_ConvertedValueLocal[1]/compensationCoefficient;
	
	if((ADC_ConvertedValueLocal[2]>=0)&&(ADC_ConvertedValueLocal[1]<0.1))
	{compensationVolatge=0;}
	
	tds_value=(133.42*compensationVolatge*compensationVolatge*compensationVolatge - 
	255.86*compensationVolatge*compensationVolatge + 857.39*compensationVolatge)*0.5*kValue;
	
	if(tds_value<=0) tds_value=0;
	if(tds_value>1400) tds_value=1400;
	return tds_value;	
} 

PH传感器程序就不展示了,因为PH也是用的ADC进行采集的,只是数据处理不一样而已,具体的可以看看卖家发的开发手册,根据开发手册进行编写,同时网上源码也有很多,所有这里就不展示了。

2.LoRa初始化及发送数据程序

LoRa发送数据在本系统采用的是点对点的方式,根据接收的LoRa节点的信道和地址设计的点对点的形式,并不是采用的广播,这一点大家需要注意,不要整错了。

节点1

#include "lora.h"
#include "usart.h"
#include "usart3.h"
#include "SysTick.h"


//Lora接收数据数组
char lora_rx_data[100];


void MD0_Config(void)	
{
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE); // 使能PC端口时钟  
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;	//选择对应的引脚
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;       
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOB, &GPIO_InitStructure);  //初始化PC端口
  GPIO_ResetBits(GPIOB, GPIO_Pin_0 );	 //拉低
}


void AUX_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //开启按键端口PA的时钟
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //端口配置为下拉输入
	GPIO_Init(GPIOB, &GPIO_InitStructure);	//初始化端口
	GPIO_ResetBits(GPIOB, GPIO_Pin_1 );	 // 关闭所有LED
}

int AUX(void) 
{
		if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 1)
			return 1;
		else
			return 0;
}					
void lora_check(void)
{
	int OK_state = 0;
	while(AUX())//检测模块是否在忙
	{
		 UART1TX("Lora正忙1");
		 delay_ms(500);
	}
	MD0(1);
	delay_ms(40);
	UART1TX("LORA检测中......");
	while(!OK_state)//模块检测成功
	{
		UART3TX("AT");
		UART3Test(&OK_state);	
	}
	UART1TX("LORA检测成功");
	UART1TX("   ");
}

void lora_set(void)
{
	/*################################设置地址###############################*/
	int OK_state = 0;
	char addr[20] = {0};int t1 = 0,t2 = 0;
	unsigned char i = 0;
	while(AUX())//等待模块空闲
	{
		 UART1TX("Lora正忙2");
	}
	MD0(1);
	delay_ms(40);
	while(!OK_state)//模块检测成功
	{
		UART3TX("AT+ADDR=00,02");  //地址:0x02
		UART3Test(&OK_state);
		UART1TX("地址设置中......");
	}
	UART1TX("地址为:");
	UART3TX("AT+ADDR?");
	while(1)
	{
			UART3GetByte(&i);
			if(i==':')
			{
				while(1)
				{
					if(UART3GetByte(&i) == 1 && i!='O')
					{
						addr[t1++]=i;
					}
					else if(i=='O')
						break;	 
				}
				while(1)
				{
						UART1SendByte(addr[t2++]);
						if(t1 == t2)
							break;
				}
				break;
			} 
	}
	UART1TX("  ");
	/*##############################设置信道和速率################################*/
	OK_state = 0; 
	t1 = 0;
	t2 = 0;
	
	while(!OK_state)//模块检测成功
	{
		UART3TX("AT+WLRATE=23,5");  //信道23
		UART3Test(&OK_state);
		UART1TX("信道速率设置中......");
	}
	UART1TX("信道,速率为:");
	UART3TX("AT+WLRATE?");
	while(1)
	{
			UART3GetByte(&i);
			if(i==':')
			{
				while(1)
				{
					if(UART3GetByte(&i) == 1 && i!='O')
					{
						addr[t1++]=i;
					}
					else if(i=='O')
						break;	 
				}
				while(1)
				{
						UART1SendByte(addr[t2++]);
						if(t1 == t2)
							break;
				}
				break;
			} 
	}
	UART1TX("  ");	
/*##############################发射功率################################*/
	OK_state = 0; 
	t1 = 0;
	t2 = 0;
	
	while(!OK_state)//模块检测成功
	{
		UART3TX("AT+TPOWER=3");
		UART3Test(&OK_state);
		UART1TX("发射功率设置中......");
	}
	UART1TX("发射功率为:");
	UART3TX("AT+TPOWER?");
	while(1)
	{
			UART3GetByte(&i);
			if(i==':')
			{
				while(1)
				{
					if(UART3GetByte(&i) == 1 && i!='O')
					{
						addr[t1++]=i;
					}
					else if(i=='O')
						break;	 
				}
				while(1)
				{
						UART1SendByte(addr[t2++]);
						if(t1 == t2)
							break;
				}
				break;
			} 
	}
	UART1TX("  ");	
/*##############################工作模式################################*/
	OK_state = 0; 
	t1 = 0;
	t2 = 0;
	
	while(!OK_state)//模块检测成功
	{
		UART3TX("AT+CWMODE=0");
		UART3Test(&OK_state);
		UART1TX("工作模式设置中......");
	}
	UART1TX("工作模式为:");
	UART3TX("AT+CWMODE?");
	while(1)
	{
			UART3GetByte(&i);
			if(i==':')
			{
				while(1)
				{
					if(UART3GetByte(&i) == 1 && i!='O')
					{
						addr[t1++]=i;
					}
					else if(i=='O')
						break;	 
				}
				while(1)
				{
						UART1SendByte(addr[t2++]);
						if(t1 == t2)
							break;
				}
				break;
			} 
	}
	UART1TX("  ");		
/*##############################发送状态################################*/
	OK_state = 0; 
	t1 = 0;
	t2 = 0;
	
	while(!OK_state)//模块检测成功
	{
		UART3TX("AT+TMODE=1");//0:透明传输,1:定向传输
		UART3Test(&OK_state);
		UART1TX("发送状态设置中......");
	}
	UART1TX("发送状态为:");
	UART3TX("AT+TMODE?");
	while(1)
	{
			UART3GetByte(&i);
			if(i==':')
			{
				while(1)
				{
					if(UART3GetByte(&i) == 1 && i!='O')
					{
						addr[t1++]=i;
					}
					else if(i=='O')
						break;	 
				}
				while(1)
				{
						UART1SendByte(addr[t2++]);
						if(t1 == t2)
							break;
				}
				break;
			} 
	}
	UART1TX("  ");	
/*##############################睡眠时间################################*/
	OK_state = 0; 
	t1 = 0;
	t2 = 0;
	
	while(!OK_state)//模块检测成功
	{
		UART3TX("AT+WLTIME=0");
		UART3Test(&OK_state);
		UART1TX("睡眠时间设置中......");
	}
	UART1TX("睡眠时间为:");
	UART3TX("AT+WLTIME?");
	while(1)
	{
			UART3GetByte(&i);
			if(i==':')
			{
				while(1)
				{
					if(UART3GetByte(&i) == 1 && i!='O')
					{
						addr[t1++]=i;
					}
					else if(i=='O')
						break;	 
				}
				while(1)
				{
						UART1SendByte(addr[t2++]);
						if(t1 == t2)
							break;
				}
				break;
			} 
	}
	UART1TX("  ");	
/*##############################波特率,数据校验位################################*/
	OK_state = 0; 
	t1 = 0;
	t2 = 0;
	
	while(!OK_state)//模块检测成功
	{
		UART3TX("AT+UART=3,0");
		UART3Test(&OK_state);
		UART1TX("波特率,数据校验位设置中......");
	}
	UART1TX("波特率,数据校验位为:");
	UART3TX("AT+UART?");
	while(1)
	{
			UART3GetByte(&i);
			if(i==':')
			{
				while(1)
				{
					if(UART3GetByte(&i) == 1 && i!='O')
					{
						addr[t1++]=i;
					}
					else if(i=='O')
						break;	 
				}
				while(1)
				{
						UART1SendByte(addr[t2++]);
						if(t1 == t2)
							break;
				}
				break;
			} 
	}
	UART1TX("  ");	
	MD0(0);
	delay_ms(40);
	while(AUX());
	USART3_Init(9600); //USART3 配置 
	
	UART1TX("LORA配置完成######################");
}



void lora_transmit(char* a)
{
		int i = 0;

	  //身份码
		UART3SendByte(0x00);
	  UART3SendByte(0x01);
	  UART3SendByte(0x19);
	  //起始符
	  UART3SendByte('#');
	
		while(a[i] != '$')
		{
				UART3SendByte(a[i]);
				i++;
		}
		UART3SendByte('$');//结束符
		UART3SendByte('\r');
		UART3SendByte('\n');
		
}



int lora_receive(void)
{
	unsigned char i = 0;
	
	UART3GetByte(&i);
	if(i=='#') return 0;
	else return 1;
				
}


void LORA_Init(void)
{
		MD0_Config(); //初始化MD0 
		AUX_Config();
		lora_check();
		lora_set();   //配置LORA
}





节点2跟节点1也是类似的,只是地址和信道设计的不一样和发送的数据不一样,其他的基本上是一样的,这里也就不在展示了。

现在就看看协调器解析数据程序吧!

下面这段代码就是向节点1和节点2通过轮询的方式获取数据,然后就是解析接收到的数据,最后存储到对应的变量当中。

void lora_transmit_node_1(void)
{
	  //起始符
		UART3SendByte(0x00);
	  UART3SendByte(0x02);
	  UART3SendByte(0x17);
	
		UART3SendByte('#');//身份码
	
		UART3SendByte('\r');
		UART3SendByte('\n');	
}

void lora_transmit_node_2(void)
{
	  //起始符
		UART3SendByte(0x00);
	  UART3SendByte(0x03);
	  UART3SendByte(0x18);
	
	
		UART3SendByte('*');//结束符
	
		UART3SendByte('\r');
		UART3SendByte('\n');
		
}


void lora_receive1(void)
{
	unsigned char ii = 0;
	int t1 = 0,t2 = 0;
	
	//向节点1发送获取数据指令
	lora_transmit_node_1();
	
	//接收节点1发送的数据
	while(1)
	{
		UART3GetByte(&ii);
		if(ii=='#')
		{
			while(1)
			{
				if(UART3GetByte(&ii) == 1 && ii!='$' && ii!='#')
				{
					lora_rx_data[t1++]=ii;
				}
				else if(ii=='$')
					break;	 
			}
				
			while(1)
			{
				UART1SendByte(lora_rx_data[t2++]);
				flag=1;
				if(t1 == t2)
				{
					UART1SendByte('\r');
					UART1SendByte('\n');
					break;
				}
		  }
			break;
		}
		else break;
	}
	
}



void lora_receive2(void)
{
	unsigned char i = 0;
	int t1 = 0,t2 = 0;
	
	//向节点2发送获取数据指令
	lora_transmit_node_2();
	
	//接收节点2发送的数据
	while(1)
	{
		UART3GetByte(&i);
		if(i=='x')
		{
			while(1)
			{
				if(UART3GetByte(&i) == 1 && i!='$' && i!='x')
				{
					lora_rx_data2[t1++]=i;
				}
				else if(i=='$')
					break;	 
			}
				
			while(1)
			{
				UART1SendByte(lora_rx_data2[t2++]);
				flag=0;
				if(t1 == t2)
				{
					UART1SendByte('\r');
					UART1SendByte('\n');
					break;
				}
		  }
			break;
		}
		else break;
	}
	
}




char temp_receive[20]={0};
char tds_receive[20]={0};
void Lora_Get_Data(void)
{
	int i,j=0,jj=0;
	
	//lora接收节点1数据
    if(flag==0)
		{
			lora_receive1();
			//数据分解
			for(i=0;i<20;i++)
			{
				if(lora_rx_data[i] != 'y' && j==0)
				{
					temp_receive[i]=lora_rx_data[i];
				}
				else if(lora_rx_data[i] == 'y') j=1;
				
				if(j==1) tds_receive[jj++]=lora_rx_data[i+1];
			}
		  Temp=strtod(temp_receive,NULL);
		  TDS=strtod(tds_receive,NULL);
		}
		
		//lora接收节点2数据
		if(flag==1)
		{
			lora_receive2();
		  //数据分解
		  PH=strtod(lora_rx_data2,NULL);
		}
}

3.ESP8266初始化和接入OneNET(MQTT协议)

#define PROID		"615311"

#define AUTH_INFO	"test03"

#define DEVID		"1143067484"


void ESP8266_Init(void)
{
	
	GPIO_InitTypeDef GPIO_Initure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

	//ESP8266复位引脚
	GPIO_Initure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Initure.GPIO_Pin = GPIO_Pin_14;					//GPIOC14-复位
	GPIO_Initure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_Initure);
	
	GPIO_WriteBit(GPIOC, GPIO_Pin_14, Bit_RESET);
	delay_ms(250);
	GPIO_WriteBit(GPIOC, GPIO_Pin_14, Bit_SET);
	delay_ms(500);
	
	ESP8266_Clear();
	
	UsartPrintf(USART_DEBUG, "00. AT\r\n");
	while(ESP8266_SendCmd("AT\r\n", "OK"))
		 delay_ms(500);
	
	UsartPrintf(USART_DEBUG, "01. RST\r\n");
	ESP8266_SendCmd("AT+RST\r\n", "");
		delay_ms(500);
	ESP8266_SendCmd("AT+CIPCLOSE\r\n", "");
		delay_ms(500);
	
	UsartPrintf(USART_DEBUG, "02. CWMODE\r\n");
	while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"))
		delay_ms(500);
	
	UsartPrintf(USART_DEBUG, "03. AT+CWDHCP\r\n");
	while(ESP8266_SendCmd("AT+CWDHCP=1,1\r\n", "OK"))
		delay_ms(500);
	
	UsartPrintf(USART_DEBUG, "04. CWJAP\r\n");
	while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "GOT IP"))
		delay_ms(500);
	
	UsartPrintf(USART_DEBUG, "05. CIPSTART\r\n");
	while(ESP8266_SendCmd(ESP8266_ONENET_INFO, "CONNECT"))
		delay_ms(500);
	
	UsartPrintf(USART_DEBUG, "06. ESP8266 Init OK\r\n");

}

_Bool OneNet_DevLink(void)
{
	
	MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};					//协议包

	unsigned char *dataPtr;
	
	_Bool status = 1;
	
 	UsartPrintf(USART_DEBUG, "OneNet_DevLink\r\n"
							"PROID: %s,	AUIF: %s,	DEVID:%s\r\n"
                        , PROID, AUTH_INFO, DEVID);
	
	if(MQTT_PacketConnect(PROID, AUTH_INFO, DEVID, 256, 0, MQTT_QOS_LEVEL0, NULL, NULL, 0, &mqttPacket) == 0)
	{
		ESP8266_SendData(mqttPacket._data, mqttPacket._len);			//上传平台
		
		dataPtr = ESP8266_GetIPD(250);									//等待平台响应
		if(dataPtr != NULL)
		{
			if(MQTT_UnPacketRecv(dataPtr) == MQTT_PKT_CONNACK)
			{
				switch(MQTT_UnPacketConnectAck(dataPtr))
				{
					case 0:UsartPrintf(USART_DEBUG, "Tips:	连接成功\r\n");status = 0;break;
					
					case 1:UsartPrintf(USART_DEBUG, "WARN:	连接失败:协议错误\r\n");break;
					case 2:UsartPrintf(USART_DEBUG, "WARN:	连接失败:非法的clientid\r\n");break;
					case 3:UsartPrintf(USART_DEBUG, "WARN:	连接失败:服务器失败\r\n");break;
					case 4:UsartPrintf(USART_DEBUG, "WARN:	连接失败:用户名或密码错误\r\n");break;
					case 5:UsartPrintf(USART_DEBUG, "WARN:	连接失败:非法链接(比如token非法)\r\n");break;
					
					default:UsartPrintf(USART_DEBUG, "ERR:	连接失败:未知错误\r\n");break;
				}
			}
		}
		
		MQTT_DeleteBuffer(&mqttPacket);								//删包
	}
	else
		UsartPrintf(USART_DEBUG, "WARN:	MQTT_PacketConnect Failed\r\n");
	
	return status;
	
}

4.发布数据程序

char myDataFmt1[] = "{\"temp\":%.1f,\"ph\":%.1f,\"tds\":%.1f}";
char myDataFmt2[] = "temp:%.1f,ph:%.1f,tds:%.1f";

char mqtt_pubtopic1[15]="$dp";
char mqtt_pubtopic2[15]="data/xyy";


char PUB_BUF1[80];
char PUB_BUF2[80];

void sprintf_json_data(float temp,float ph,float tds)
{
	memset(PUB_BUF1,0,80);
	memset(PUB_BUF2,0,80);

	sprintf(PUB_BUF1,myDataFmt2,temp,ph,tds);
	sprintf(PUB_BUF2,myDataFmt1,temp,ph,tds);
}


void Sample_Upload_Fun (float temp,float ph,float tds)
{
	sprintf_json_data(temp,ph,tds);
	
  OneNet_Publish2 ( mqtt_pubtopic2, PUB_BUF1 ); 
  delay_ms ( 200 ); 
	
	OneNet_Publish ( mqtt_pubtopic1, PUB_BUF2 ); 
  delay_ms ( 1000 );
}



_Bool OneNet_Publish ( char *topic, const char *msg )
{

    short body_len = 0, i = 0;
    MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};

    printf ( "Publish Topic: %s, Msg: %s\r\n", topic, msg );

    body_len = strlen ( msg );

    if ( MQTT_PacketSaveData2 ( topic,  body_len, NULL,  &mqttPacket ) == 0 )							
    {
        for ( i = 0; i < body_len; i++ )
        {
            mqttPacket._data[mqttPacket._len++] = msg[i];
        }

        ESP8266_SendData ( mqttPacket._data, mqttPacket._len );									


        MQTT_DeleteBuffer ( &mqttPacket );															
    }





    return 0;

}

效果图:

5.最后就是android小程序,这里只展示APP连接OneNET和订阅数据及处理数据,APP登陆系统就不展示了。

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.widget.EditText;
import android.widget.TextView;

import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;


public class MainActivity2 extends AppCompatActivity {

    EditText ed_temp;
    EditText ed_ph;
    EditText ed_tds;
    TextView show;

    private MqttAndroidClient mqttClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);


        ed_temp=findViewById(R.id.ed_temp);
        ed_ph=findViewById(R.id.ed_ph);
        ed_tds=findViewById(R.id.ed_tds);

        show=findViewById(R.id.show);

        Intent intent=getIntent();
        String string1=intent.getStringExtra("denglu");


        //连接服务器
        connectToMQTT();
    }


    private void connectToMQTT() {
        String serverUri = "tcp://183.230.40.39:6002";
        String clientId = "1143096583";
        String username = "615311";
        String password = "watch01";

        mqttClient = new MqttAndroidClient(this, serverUri, clientId);

        MqttConnectOptions options = new MqttConnectOptions();
        options.setUserName(username);
        options.setPassword(password.toCharArray());

        try {
            mqttClient.connect(options, null, new IMqttActionListener() {
                @Override
                public void onSuccess(IMqttToken asyncActionToken) {
                    // 连接成功
                    subscribeToTopic();
                }

                @Override
                public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                    // 连接失败
                    exception.printStackTrace();
                }
            });
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }


    private void subscribeToTopic() {
        String topic = "data/#";
        int qos = 1;

        try {
            mqttClient.subscribe(topic, qos, null, new IMqttActionListener() {
                @Override
                public void onSuccess(IMqttToken asyncActionToken) {
                    // 订阅成功
                }

                @Override
                public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                    // 订阅失败
                    exception.printStackTrace();
                }
            });

            mqttClient.setCallback(new MqttCallback() {
                @Override
                public void connectionLost(Throwable cause) {
                    // 处理连接丢失的情况
                }

                @Override
                public void messageArrived(String topic, MqttMessage message) throws Exception {
                    // 处理收到的消息
                    String data = new String(message.getPayload());
                    if(data.indexOf("T")!=-1)
                    {
                        String[] temp_data=data.split(":");
                        ed_temp.setText("" + temp_data[1]);
                    }
                    else if(data.indexOf("tds")!=-1)
                    {
                        String[] tds_data=data.split(":");
                        ed_tds.setText(""+tds_data[1]);
                    }
                    else if(data.indexOf("PH")!=-1)
                    {
                        String[] ph_data=data.split(":");
                        ed_ph.setText(""+ph_data[1]);
                    }

                    show.setText("" + data);

                }

                @Override
                public void deliveryComplete(IMqttDeliveryToken token) {
                    // 处理消息发送完成的情况
                }
            });
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }

}

效果图:

最后经过测试,本系统也是成功的完成了,但是经过后续研究,发现系统有个错误,这个错误就是ESP8266模块的使用有点不符合场景,因为有很多地方并没有WIFI,所以这一模块后续我会重新更换,使用SIM900A模块。

有兴趣的可以私我

  • 5
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
这里是一个简单的 STM32+LORA 芯片 SX1278 主从通信的程序设计参考,供您参考: 1. 硬件准备: - STM32F4开发板 - LoRa 模块 SX1278 - 杜邦线若干 - 5V 2A电源 - USB转串口线 2. 程序设计: - 安装 Keil MDK 开发环境,并创建一个新的 STM32 工程。 - 在工程中新建一个 main.c 文件,并编写代码实现 LoRa 模块的初始化、主从通信等操作。 - 在 main.c 文件中引入相应的头文件,例如:#include "stm32f4xx.h"、#include "sx1278.h" 等。 - 在 main 函数中初始化串口、LoRa 模块、GPIO 等基础配置,例如: ```c int main(void) { // 初始化串口 USART_Init(); // 初始化 LoRa 模块 SX1278_Init(); // 配置 GPIO GPIO_Configuration(); // ... } ``` - 在主从通信前,需要设置 LoRa 模块的工作模式(主/从),例如: ```c void SX1278_SetMode(uint8_t mode) { SX1278_Write(REG_LR_OPMODE, (SX1278_Read(REG_LR_OPMODE) & RFLR_OPMODE_MASK) | mode); } ``` - 主从通信时,需要设置 LoRa 模块的频率、数据速率、功率等参数,例如: ```c void SX1278_SetTxConfig(uint32_t freq, uint32_t bw, uint32_t sf, uint32_t cr, uint32_t power) { uint8_t bw_sf_reg = 0; switch (bw) { case 125000: bw_sf_reg |= 0x70; break; // ... } switch (sf) { case 6: bw_sf_reg |= 0x60; break; // ... } SX1278_Write(REG_LR_MODEMCONFIG1, bw_sf_reg); SX1278_Write(REG_LR_MODEMCONFIG2, (SX1278_Read(REG_LR_MODEMCONFIG2) & RFLR_MODEMCONFIG2_SF_MASK) | (sf << 4)); SX1278_Write(REG_LR_MODEMCONFIG3, 0x04); SX1278_Write(REG_LR_PACONFIG, power); SX1278_Write(REG_LR_FRFMSB, (uint8_t)((freq >> 16) & 0xFF)); SX1278_Write(REG_LR_FRFMID, (uint8_t)((freq >> 8) & 0xFF)); SX1278_Write(REG_LR_FRFLSB, (uint8_t)(freq & 0xFF)); } ``` - 主从通信时,需要发送和接收数据,例如: ```c void SX1278_SendPacket(uint8_t *buf, uint8_t size) { SX1278_SetMode(RFLR_OPMODE_STANDBY); SX1278_Write(REG_LR_FIFOADDRPTR, SX1278_Read(REG_LR_FIFOTXBASEADDR)); SX1278_Write(REG_LR_PAYLOADLENGTH, size); SX1278_WriteFifo(buf, size); SX1278_SetMode(RFLR_OPMODE_TRANSMITTER); while ((GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)) == Bit_RESET); while ((GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)) == Bit_SET); } uint8_t SX1278_ReceivePacket(uint8_t *buf, uint8_t size) { uint8_t rx_len = 0; if (SX1278_Read(REG_LR_IRQFLAGS) & RFLR_IRQFLAGS_PAYLOADCRCERROR_MASK) { SX1278_Write(REG_LR_IRQFLAGS, RFLR_IRQFLAGS_PAYLOADCRCERROR_MASK); } else { rx_len = SX1278_Read(REG_LR_RXNBBYTES); SX1278_Write(REG_LR_FIFOADDRPTR, SX1278_Read(REG_LR_FIFORXCURRENTADDR)); SX1278_ReadFifo(buf, rx_len); } return rx_len; } ``` 3. 编译和下载程序: - 在 Keil MDK 中编译程序,生成可执行文件。 - 使用 ST-Link 下载程序到 STM32 开发板中进行调试。 以上是一个简单的 STM32+LORA 芯片 SX1278 主从通信的程序设计参考,可以根据您的实际需求进行修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值