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模块。
有兴趣的可以私我