基于单片机的智能水质监测系统设计与实现

收藏和点赞,您的关注是我创作的动力

文章目录

  • 概要
  • 一、系统总体设计
  • 1功能需求分析
  • 2原理图设计
  • 二、程序流程图
  • 仿真电路图
  • 三、 主要代码
  • 四、 内容截图
  • 五、 文章目录
  • 概要

      水资源一直以来都是人类最重要的财富之一,由于工农业的发展人
    们对水资源的污染变得越来越严重,保护水资源应势在必行。在这个科学技术日新月异的时代,嵌入式技术和物联网技术一直在发展未曾被淘汰便足以证明这些技术的优秀,结合现在的时代背景对水质检测的需求,水质检测对世界各国家的发展以及人们的生活至关重要。本文在以上基础上本文利用嵌入式技术和物联网技术设计一款基于 STM32F103C8T6单片机实现水质检测的功能,单片机外接 E-201 系列的 PH 传感器和TSW-30 浑浊度传感器采集数据然后通过单片机计算具体水质数据,再将水质数据显示到 LCD1602 液晶显示器上,并外接 ESP8266WIFI 模块通过单片机将水质数据实时传输到手机应用上显示,该设计与传统的水质检测相比有着采集效率高,实时性好,成本低等优点。同时,水资检测是保护水资源的重要手段之一,对人们的身体健康有着良好的保障,对国家的发展有着重要意义。

    一、系统总体设计

    1功能需求分析

    本设计以单片机为基础对水质进行检测,需要实现显示功能、水质数据采集功能、无线通信功能,实现一个自动采集水质数据并显示出来的系统。
    显示需求分析:
    单片机较为常用的液晶显示屏主流有 LCD1602 和 LCD12864,这两款液晶显示器区别不大,显示内容上 LCD12864 比 LCD1602 能够呈现的信息更多,LCD12864能够显示中文,LCD1602 显示数字和英文更为方便,后者比前者相对便宜,且本设计用于显示大小对 LCD1602 已经足够,所以显示方面选择性价比更高的 LCD1602。
    水质传感器需求分析:
    水质可以检测的常见参数有 COD、氨氮、色度、浑浊度、PH 值等,但由于成本和技术上的问题,只能选取少量参数进行检测。
    (1)COD 是水中的还原物被氧化需要的量,是一种化学检测方法,但一般用在工业级别的检测上,且会因使用的氧化剂会导致结果有所区别,故不选取。
    (2)氨氮是检测由 NH3 和 NH4+ 在水中结合而成的化合物含量,这中化合物会导致水富营养化,这种检测仪器较为昂贵,设计成本太高所以不适用于本设计。
    (3)色度是天然水或者对水中的悬浮物进行处理,然后进行检测其颜色的一种检测方式,但由于检测原理较为复杂,应用于单片机检测上难度较大,故也不选用
    (4)PH 是指水中氢离子浓度,这种检测方式虽然也在工业上使用,且市面上的 PH 检测仪器无法进行二次开发,但却有成熟的 PH 检测模块可以配合 ph 测量电极使用,价格便宜,使用起来简单方便,可采用。
    (5)浑浊度指水中悬浮物质的含量,检测原理是根据水对光线的穿透量的大小,虽然工业级的检测仪器价格高昂,但可采用洗衣机、洗碗机所使用的浑浊度传感器,这种传感器价格低廉,适合用于开发,可采用。
    无线通信需求分析:
    目前嵌入式使用比较多的无线通信技术有红外遥控、蓝牙、WIFI。红外遥控技术需要端口对接,受障碍物影响较大,而且传输距离较短;而蓝牙技术虽然没有了障碍物的影响,能够全方位地传输信息,但缺点是传输距离仍然较短,传输速率较慢;WIFI 相对以上两者,没有了那些缺点,传输距离和传输速率都有了明显的提升,以上三种技术中显然 WIFI是最适用的。

    2原理图设计

    STM32 最小系统由 STM32 芯片、晶振电路和复位电路组成,如图 3.5 所示。晶振电路的作用是为单片机提供合适的时钟信号流,就像人的心脏提供心跳,单片机才能运行,电路中两个电容是为了满足谐振要求,要想晶振电路能够工作正常,需要接上适当大小的电容。复位电路是上电复位电路,为低电平复位,电压升高,让系统恢复到初始状态。具体原理图如图 3.9 所示。

    图3-4 STM32 最小系统
    图 3.7 为稳压电路,AMS1117 是一款稳压芯片,用于转换电压。由于 USB 供电通常为 5V,而 STM32 需要 3.3V 供电,所以需要通过稳压电路将 5V 转换为 3.3V 给单片机提供电源,D3 二极管是肖特基二极管,它的主要作用是让电流只能从一个方向通过,保障电路正常工作。

    二、程序流程图

    本设计以 STM32 为核心,外接液晶显示模块、PH 传感器、浑浊度传感器、WIFI模块实现检测水质 PH 值和浑浊度,STM32 单片机通过 PH 传感器以及浑浊度传感器获取电压信号,然后单片机获取信号再将具体数据计算结果,将结果通过程序显示在 LCD1602,同时单片机利用 WIFI 模块发送水质的 PH 值和浑浊度数据,手机应用获取到具体数据并显示出来,具体程序流程图如图 4.1。开始时需要时钟初始化、IO 口初始化、串口初始化、液晶初始化等。其中获取 PH 值和浑浊度参数是通过单片机的采集电压而来的,然后算出实际数据

    图4-1 程序流程图
    图4.1为液晶显示的部分程序内容,其中调用函数 LCD_DisStr(0,0,“”)表示从第一行第一列开始显示数据,LCDW_Dat()显示水质数据的各位(个位、十位等)数据。

    图4-2 液晶显示
    STM32 单片机通过 WIFI 模块发送数据方式如图 4.3 所示,sendchar()为发送一段字符串,字符串中 S 表示发送起点,009 表示九个字节的数据,后面跟着的即为发送的数据,E 表示发送结束。

    图4-3WIFI 发送

    仿真电路图

    如图5.1所示。

    图5-1仿真电路图

    三、 主要代码

    主程序:
    源代码:
    #include "sys.h"
    #include "delay.h"
    #include "gpio.h"
    #include "OLED_I2C.h"
    #include "ds18b20.h"
    #include "usart1.h"
    #include "esp8266.h"
    #include "adc.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define STM32_RX1_BUF       Usart1RecBuf 
    #define STM32_Rx1Counter    RxCounter
    #define STM32_RX1BUFF_SIZE  USART1_RXBUFF_SIZE
    
    #define Offset 0.00	//deviation compensate
    #define RATIO  4.51/4.08
    short temperature=0;  //温度
    u8    tempSetVal=40;  //温度上限
    u8 shuaxin = 0;  //刷新标志
    u8 setFlag = 0;  //设置标志
    float PH  = 0.0;
    u16 Ph_min = 300,Ph_max = 900;   //PH下限上限
    u16 Turbidity=0;
    u16 TurSetMax=1000;  //浊度上限
    unsigned long int avgValue;	//Store the average value of the sensor feedback 
    
    char display[16];
    
    void Usart1RxBufClear(void) //清除串口接收缓存
    {
    	  memset(STM32_RX1_BUF, 0, STM32_RX1BUFF_SIZE);//清除缓存
    		STM32_Rx1Counter = 0; 
    }
    
    void InitDisplay(void)   //初始化显示
    {
    	  unsigned char i=0;
    	  
    	  OLED_ShowStr(0, 2, "PH:", 2,0);
    	  for(i=0;i<2;i++)OLED_ShowCN(i*16,4,i+0,0);//显示中文:温度
    	  for(i=0;i<2;i++)OLED_ShowCN(i*16,6,i+2,0);//显示中文:浊度
    	  OLED_ShowChar(32,4,':',2,0);
    	  OLED_ShowChar(32,6,':',2,0);
    }
    
    void displaySetValue(void)  //显示设置的值
    {
    	  if(setFlag == 1 ||setFlag == 2)
    		{
    				sprintf(display,"%5.2f",(float)Ph_min/100); 
    				OLED_ShowStr(40, 4,(u8 *)display, 2,setFlag+1-1);
    			
    				sprintf(display,"%5.2f",(float)Ph_max/100); 
    				OLED_ShowStr(40, 6,(u8 *)display, 2,setFlag+1-2);
    		}
    		if(setFlag == 3)
    		{
    				sprintf(display,"%02d",tempSetVal); 
    				OLED_ShowStr(40, 4,(u8 *)display, 2,setFlag+1-3);
    		}
    		if(setFlag == 4)
    		{
    				sprintf(display,"%04d",TurSetMax); 
    				OLED_ShowStr(40, 4,(u8 *)display, 2,setFlag+1-4);
    		}
    }
    
    void keyscan(void)   //按键扫描
    {
    	 unsigned char i=0;
    	
    	 if(KEY1 == 0) //设置键
    	 {
    			delay_ms(20);
    		  if(KEY1 == 0)
    			{
    					while(KEY1 == 0);
    				  BEEP=0;
    				  setFlag ++;
    				  if(setFlag == 1)
    					{
    							OLED_CLS();    //清屏
    						  for(i=0;i<2;i++)OLED_ShowCN(i*16+32,0,i+8,0);//显示中文:设置
    						  OLED_ShowStr(62, 0, " PH", 2,0);
    						  for(i=0;i<2;i++)OLED_ShowCN(i*16,4,i+4,0);//显示中文:下限
    							for(i=0;i<2;i++)OLED_ShowCN(i*16,6,i+6,0);//显示中文:上限
    						  OLED_ShowChar(32,4,':',2,0);
    						  OLED_ShowChar(32,6,':',2,0);
    					}
    					if(setFlag == 3)
    					{
    							for(i=0;i<2;i++)OLED_ShowCN(i*16+64,0,i+0,0);//显示中文:温度
    						  OLED_ShowStr(56, 4, "    ", 2,0);
    						  OLED_ShowCentigrade(56, 4);    //℃
    						  OLED_ShowStr(0, 6, "                ", 2,0);
    					}
    					if(setFlag == 4)
    					{
    							for(i=0;i<2;i++)OLED_ShowCN(i*16+64,0,i+2,0);//显示中文:浊度
    						  OLED_ShowStr(72, 4, "NTU", 2,0);
    					}
    					if(setFlag >= 5)
    					{
    						  setFlag = 0;
    							OLED_CLS();    //清屏
    						  InitDisplay();
    						  
    					}
    					displaySetValue();
    			}
    	 }
    	 if(KEY2 == 0) //加键
    	 {
    			delay_ms(100);
    		  if(KEY2 == 0)
    			{
    					if(setFlag == 1)
    					{
    						  if(Ph_max-Ph_min > 10)Ph_min+=10;
    					}
    					if(setFlag == 2)
    					{
    							if(Ph_max < 1400)Ph_max+=10;
    					}
    					if(setFlag == 3)
    					{
    							if(tempSetVal<99)tempSetVal++;
    					}
    					if(setFlag == 4)
    					{
    						  if(TurSetMax<3000)TurSetMax+=10;
    					}
    					displaySetValue();   //显示没有设置值
    			}
    	 }
    	 if(KEY3 == 0) //减键
    	 {
    			delay_ms(100);
    		  if(KEY3 == 0)
    			{
    					if(setFlag == 1)
    					{
    						  if(Ph_min >= 10)Ph_min-=10;
    					}
    					if(setFlag == 2)
    					{
    							if(Ph_max-Ph_min > 10)Ph_max-=10;
    					}
    					if(setFlag == 3)
    					{
    							if(tempSetVal>0)tempSetVal--;
    					}
    					if(setFlag == 4)
    					{
    						  if(TurSetMax>=10)TurSetMax-=10;
    					}
    					displaySetValue();   //显示没有设置值
    			}
    	 }
    }
    
    void Get_PH(void)    //获取PH
    {
    	  u16 buf[10];//buffer for read analog
    	  u8 i,j;	
    	  float phValue=0.0;
    	
    		for(i=0;i<10;i++)	//Get 10 sample value from the sensor for smooth the value
    		{
    			buf[i]=Get_Adc_Average(ADC_Channel_8,10); 
    		}
    		for(i=0;i<9;i++)	//sort the analog from small to large
    		{
    				for(j=i+1;j<10;j++)
    				{
    						if(buf[i]>buf[j])
    						{
    							int temp=buf[i];
    							buf[i]=buf[j]; 
    							buf[j]=temp;
    						}
    				}
    		}
    		avgValue=0;
    		for(i=2;i<8;i++)	//take the average value of 6 center sample 
    		avgValue+=buf[i];
        phValue=((float)avgValue*5.0/4095/6)*RATIO; //convert the analog into millivolt
    		PH=(phValue*(-5.290))+23.053;	//convert the millivolt into pH value 
    		if(PH<0)PH=0;
    		if(PH>14.0)PH=14.0;
    }
    
    void Get_Turbidity(void)   //获取浑浊度
    {
    		float T;
    	  u16 adcx = 0;
    	
    	  adcx = Get_Adc_Average(ADC_Channel_9,20);//读取AD值
    		T = adcx;
    		T = T*(3.3/4096)+1.72;
    		if(T < 2.5)
    		{
    				T = 3000;	
    		}
    		else 
    		{
    				T = (-1120.4*T*T+5742.3*T-4352.9);	//Tul是AD值
    		}
    		if(T < 0)
    		{
    			T = 0;
    		}
    		Turbidity = (u16)T;
    		
    		if(Turbidity > 3000)Turbidity = 3000;
    }
    
    void UsartSendData(void)    //串口发送数据
    {
    	  char SEND_BUF[100];
    	  char BUF[50];
    	 
    	  memset(SEND_BUF,0,sizeof(SEND_BUF));      //清空缓冲区
    	  sprintf(SEND_BUF,"PH:%5.2f\r\n",PH); 
    	
    	  memset(BUF,0,sizeof(BUF));      //清空缓冲区	
    	  sprintf(BUF,"温度:%d℃\r\n",temperature);
        strcat(SEND_BUF,BUF);	
    	
    	  memset(BUF,0,sizeof(BUF));      //清空缓冲区	
    	  sprintf(BUF,"浊度:%dNTU\r\n",Turbidity);
        strcat(SEND_BUF,BUF);	
    	
    	  strcat(SEND_BUF,"\r\n");
    	  
    	  ESP8266_SendData((u8 *)SEND_BUF, strlen(SEND_BUF));   //ESP8266发送数据
    }
    
    int main(void)
    {
    	  u16 timeCount1 = 300;
    	  u16 timeCount2 = 10;
    	  u8 shanshuo=0;
    	
    		delay_init();	           //延时函数初始化	 
        NVIC_Configuration();	   //中断优先级配置
    	  I2C_Configuration();     //IIC初始化
    	  delay_ms(200); 
    	  Adc_Init();		  	      	//ADC初始化	
    	  OLED_Init();             //OLED液晶初始化
    	  OLED_CLS();              //清屏
    	  OLED_ShowStr(0, 2,"   Loading...   ", 2,0);
    	  ESP8266_Init();
    	  OLED_CLS();              //清屏
    	  InitDisplay();
    		KEY_GPIO_Init();        //按键引脚初始化
    	  DS18B20_GPIO_Init();
    	  DS18B20_Init();         //初始化显示
    	  uart1_Init(9600);
    		while(1)
    		{ 
    			   keyscan();  //按键扫描
             timeCount1 ++;
    			   if(timeCount1 >= 300 && !setFlag)   //延时一段时间读取
    				 {
    						timeCount1 = 0;
    					 
    					  shanshuo=!shanshuo;
    					 
    					  Get_PH() ;      //获取PH
    					  /*超限的时候闪烁显示*/
    					  if(((PH*100)<=Ph_min || (PH*100)>=Ph_max) && shanshuo)
    						{
    								OLED_ShowStr(32, 2,"     ", 2,0);
    						}
    						else
    						{
    								sprintf(display,"%5.2f ",(float)PH); 
    								OLED_ShowStr(32, 2,(u8 *)display, 2,0);
    						}
    					 
    					 
    					  temperature = ReadTemperature();//读取温度
    						/*超限的时候闪烁显示*/
    						if((temperature>=tempSetVal) && shanshuo)
    						{
    								OLED_ShowStr(40, 4,"    ", 2,0);
    						}
    						else
    						{
    								sprintf(display,"%02d",temperature); 
    								OLED_ShowStr(40, 4,(u8 *)display, 2,0);
    							  OLED_ShowCentigrade(56, 4);    //℃
    						}
    					 
    					  Get_Turbidity() ;
    						/*超限的时候闪烁显示*/
    						if((Turbidity>=TurSetMax) && shanshuo)
    						{
    								OLED_ShowStr(40, 6,"       ", 2,0);
    						}
    						else
    						{
    								sprintf(display,"%dNTU   ",Turbidity); 
    								OLED_ShowStr(40, 6,(u8 *)display, 2,0);
    						}
    						
    						if(((PH*100)<=Ph_min || (PH*100)>=Ph_max)||(temperature>=tempSetVal)||(Turbidity>=TurSetMax))BEEP= 1;else BEEP=0;  //超限蜂鸣器报警 
    						timeCount2 ++;
    						if(timeCount2 >= 3)
    						{
    								timeCount2 = 0;
    							  UsartSendData();     //串口发送数据
    						}
    				 }
    			   delay_ms(1);
    		}
    }
    
    
    
    
    
    

    四、 内容截图


    请添加图片描述
    请添加图片描述

    五、 文章目录

    目 录

    1 绪论………………………………………………………………………………1
    1.1 研究背景…………………………………………………………………Y
    1.2 国内外研究现状………………………………………………………………Y
    1.3选题背景和研究内容……………………………………………………………Y
    1.4本章小结………………………………………………………………Y
    2 方案论证……………………………………………………………………Y
    2.1 功能需求分析…………………………………………………………………Y
    2.2技术应用概况…………………………………………………………………Y
    2.3本章小结…………………………………………………………………Y
    3 硬件设计………………………………………………………………………Y
    3.1 芯片选型及系统框架……………………………………………………………Y
    3.2原理图设计…………………………………………………………………Y
    4 软件开发………………………………………………………………………Y
    4.1 开发软件应用……………………………………………………………Y
    4.2程序流程图…………………………………………………………………Y
    5 综合测试………………………………………………………………………Y
    5.1硬件测试……………………………………………………………Y
    5.2功能测试…………………………………………………………………Y
    6总结与展望………………………………………………………………………Y
    致谢 …………………………………………………………………………………Y
    参考文献……………………………………………………………………………Y
    附录…………………………………………………………………………………Y

    物联沃分享整理
    物联沃-IOTWORD物联网 » 基于单片机的智能水质监测系统设计与实现

    发表评论