当前位置: 首页 > news >正文

【STM32项目】水质检测

✌️✌️大家好,这里是5132单片机毕设设计项目分享,今天给大家分享的是基于《基于STM32的水质监测设计》。

目录

1、系统功能

2.1、硬件清单

2.2、功能介绍

2.3、控制模式

2、演示视频和实物

3、系统设计框图

4、软件设计流程图

5、原理图

6、主程序

7、总结


1、系统功能

2.1、硬件清单

STM32+OLED+PH+电导率+温度+浊度+水位+补水水泵+排水水泵+加热器+WIFI

2.2、功能介绍

            基于STM32F103C8T6为主控制器的水质检测系统的设计与实现。其中包括基于STM32F103C8T6单片机的最小系统的搭建,温度传感器、浑浊度传感器、水位传感器、PH传感器、电导率传感器等相关器件模块的选型和原理图设计,在上述原理图的基础上搭建硬件平台并验证其功能的完善性。
(1)ESP8266-01Wifi模块。通过本模块单片机系统可以连接到机智云,手机APP就可以远程查看水质参数。
(2)浊度感器器。用来检测水的浊度。
(3)温度传感器,温度传感器用来检测水的水温,使水保持恒温。
(4)PH传感器,用来检测水的PH值。
(5)电导率传感器,用来检测水的电导率.
(6)水位传感器,检测水位。
(5)继电器模块,用继电器来驱动两个水泵和加热器。
(7)OLED显示模块。OLED显示模块采用0.96寸液晶显示屏,该模块用来实时显示传感器数据及工作模式。
(8)按键模块。按键模块用来控制模式选择。
(9)蜂鸣器模块。设备出现故障用来报警。

2.3、控制模式

(1)远程模式
通过手机 APP 实现对补水泵、排水泵、加热棒的开关控制,实时查看温度、水位、pH 值、电导率、浊度等参数,并可设置各参数的预警阈值(如温度阈值、水位阈值、浊度阈值等)。WiFi 模块支持自动配网,未连接时可通过复位键重新配对。
(2)手动模式
按下第 1 个按键切换至手动模式,通过后 3 个按键直接控制设备:
第 2 个按键:控制排水泵启停;
第 3 个按键:控制补水泵启停;
第 4 个按键:控制加热棒启停。
该模式下设备状态实时显示在 OLED 屏,不受阈值自动控制逻辑影响。
(3)自动模式
按下第 1 个按键切换至自动模式,系统根据预设阈值自动运行:
水位控制:当水位低于设定阈值时,自动启动补水泵补水;水位达到阈值后自动关闭补水泵。
温度控制:当水温低于温度阈值时,自动启动加热棒加热;水温达到阈值后自动关闭加热棒。
报警机制:浊度、pH 值、电导率任一参数超过预设报警阈值时,蜂鸣器触发声光报警,同时 APP 同步显示报警信息。


2、演示视频和实物

基于STM32的水质检测系统设计-(PH+电导率+水位+浊度+水位监测)-(远程+自动+手动模式)

 


3、软件设计流程图


4、原理图


5、主程序

#include "main.h"short temperature;
uint8_t KeyNum;// 存储按键值
u8 t = 0;// 传感器读取时间间隔
uint16_t RTC_Time[] = {0, 0, 0};// RTC时间
uint16_t RTC_Time1[] = {7, 0, 0};// 定时时间---开
uint16_t RTC_Time2[] = {19, 0, 0};// 定时时间---关
u8 S_Mode; // 按键状态标志
u8 S_Shou_1 ;     //手动模式控制设备1
u8 S_Shou_2;     //手动模式控制设备2
u8 S_Shou_3;     //手动模式控制设备3
u8 S_YuZhi;       //阈值设置里用的,用来切换阈值
// 其他状态标志
u8 qingping = 1;   //清屏标志
u8 S_ShiShi_Time_1;     //设置实时时间用,用来切换时间显示和设置时间
u8 S_ShiShi_Time_2;    //设置实时时间用,用来切换设置的时分秒
u8 S_DingShi_Switch;    //设置定时时间里用到,切换时分秒
uint16_t AD0, AD1, AD2, AD3;             //存储5路ADC值
float PH;
// 定义传感器数据和阈值结构体变量
SensorDataAndThreshold sensorData;// 初始化相关硬件和机智云
void System_Init()
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设置NVIC中断分组2:2位抢占优先级,2位响应优先级uart_init(9600);       // 串口初始化为9600delay_init();      // 延时函数初始化// 初始化传感器阈值sensorData.tempYu = 20;sensorData.ZhuoDuYu = 60;sensorData.DianYu = 100;sensorData.PHYu = 9;sensorData.ShuiWeiYu = 50;LED_Init();            // 初始化与LED连接的硬件接口Buzzer_Init();             // 外设初始化OLED_Init();Key_Init();AD_Init();MyRTC_Init();TDS_Init();MY_Gizwits_Init();   // 机智云初始化while (DS18B20_Init()) //DS18B20初始化{printf("ds18b20 success!");delay_ms(200);}}u16 Get_Adc_Average(u8 ADC_CHx, u8 times)
{u32 temp_val = 0;u8 t;for (t = 0; t < times; t++){temp_val += AD_GetValue(ADC_CHx);//SYSTICK_DelayMs(5);}return temp_val / times;
}int main(void)
{System_Init();while (1){userHandle();    // 数据上传gizwitsHandle((dataPoint_t *)&currentDataPoint);   // 后台处理,必须放在while里ReadSensorData();HandleModes();}
}// 读取传感器数据
void ReadSensorData()
{if (t % 10 == 0)    //每100ms读取一次{temperature = DS18B20_Get_Temp();sensorData.temp = temperature;}delay_ms(10);t++;sensorData.ShuiWei = 100 - TS_GetData(ADC_Channel_0);  // 水位传感器     PA0sensorData.ZhuoDu = TS_GetData(ADC_Channel_1);    // 浊度传感器     PA1AD2 = Get_Adc_Average(ADC_Channel_2, 10);PH = (float)AD2 * (3.3 / 4096); //读取ADC通道4的值PH = -5.7541 * PH + 16.654; //输出电压范围0~3V3      因为STM32的ADC参考电压是3.3V//将ADC的原始值(adcx)转换为电压值。这里假设ADC的参考电压是3.3V,并且ADC的位数是12位(即最大值为4096)。sensorData.PH = PH * 10;sensorData.Dian = TDS_GetData_PPM();}// 处理不同模式
void HandleModes()
{KeyNum = Key_GetNum();if (KeyNum == 1 && DebounceKey(1)){qingping = 0;BU_OFF();Buzzer_OFF();PAI_OFF();RE_OFF();S_Mode = (S_Mode + 1) % 4;}switch (S_Mode){case 0: // 远程模式if (qingping == 0){OLED_Clear();qingping = 1;}TimeRead();ChuangGan();OLED_ShowChinese(1, 7, 49);OLED_ShowChinese(1, 8, 50);break;case 2: // 自动模式OLED_ShowChinese(1, 7, 51);OLED_ShowChinese(1, 8, 52);TimeRead();zhidong();ChuangGan();break;case 1: // 手动模式OLED_ShowChinese(1, 7, 18);OLED_ShowChinese(1, 8, 52);TimeRead();ChuangGan();shoudong();break;case 3: // 阈值设置if (qingping == 0){OLED_Clear();qingping = 1;}YuZhiSet();break;}
}// 手动模式函数
void shoudong()
{if (qingping == 0){OLED_Clear();qingping = 1;}if (KeyNum == 2 && DebounceKey(2)){S_Shou_1 = (S_Shou_1 + 1) % 2;}if (S_Shou_1 == 0){PAI_OFF(); // 外设操作}if (S_Shou_1 == 1){PAI_ON(); // 外设操作}if (KeyNum == 3 && DebounceKey(3)){S_Shou_2 = (S_Shou_2 + 1) % 2;}if (S_Shou_2 == 1){BU_ON(); // 外设操作}if (S_Shou_2 == 0){BU_OFF(); // 外设操作}if (KeyNum == 4 && DebounceKey(4)){S_Shou_3 = (S_Shou_3 + 1) % 2;}if (S_Shou_3 == 1){RE_ON(); // 外设操作}if (S_Shou_3 == 0){RE_OFF(); // 外设操作}
}// 自动模式函数
void zhidong()
{if (qingping == 0){OLED_Clear();qingping = 1;}if (temperature / 10 < sensorData.tempYu){RE_ON();}else{RE_OFF();}if (sensorData.ShuiWei < sensorData.ShuiWeiYu){BU_ON();}else{BU_OFF();}if ((sensorData.PH / 10 > sensorData.PHYu) || (sensorData.Dian > sensorData.DianYu) || (sensorData.ZhuoDu > sensorData.ZhuoDuYu)){Buzzer_Turn();}else{Buzzer_OFF();}}// 设置阈值函数
void YuZhiSet()
{if (qingping == 0){OLED_Clear();qingping = 1;}zhidong();OLED_ShowChinese(1, 3, 72);OLED_ShowChinese(1, 4, 73);OLED_ShowChinese(1, 5, 74);OLED_ShowChinese(1, 6, 75);OLED_ShowString(2, 1, "WD:");OLED_ShowNum(2, 4, sensorData.tempYu, 2);OLED_ShowString(2, 10, "SW:");OLED_ShowNum(2, 13, sensorData.ShuiWeiYu, 2);OLED_ShowString(3, 1, "ZD:");OLED_ShowNum(3, 4, sensorData.ZhuoDuYu, 2);OLED_ShowString(3, 10, "PH:");OLED_ShowNum(3, 13, sensorData.PHYu, 2);OLED_ShowString(4, 1, "DD:");OLED_ShowNum(4, 4, sensorData.DianYu, 3);if (KeyNum == 2 && DebounceKey(2)){S_YuZhi = (S_YuZhi + 1) % 5;}switch (S_YuZhi){case 0:if (KeyNum == 3) sensorData.tempYu++;if (KeyNum == 4) sensorData.tempYu--;break;case 1:if (KeyNum == 3) sensorData.ShuiWeiYu++;if (KeyNum == 4) sensorData.ShuiWeiYu--;break;case 2:if (KeyNum == 3) sensorData.ZhuoDuYu ++;if (KeyNum == 4) sensorData.ZhuoDuYu --;break;case 3:if (KeyNum == 3) sensorData.PHYu ++;if (KeyNum == 4) sensorData.PHYu --;break;case 4:if (KeyNum == 3) sensorData.DianYu ++;if (KeyNum == 4) sensorData.DianYu --;break;}
}// 定时模式函数
void DingShiMoShi()
{if (qingping == 0){OLED_Clear();qingping = 1;}// 定时模式判断if ((MyRTC_Time[3] == RTC_Time1[0]) && (MyRTC_Time[4] == RTC_Time1[1]) && (MyRTC_Time[5] == RTC_Time1[2])){// 外设操作}if ((MyRTC_Time[3] == RTC_Time2[0]) && (MyRTC_Time[4] == RTC_Time2[1]) && (MyRTC_Time[5] == RTC_Time2[2])){// 外设操作}// 显示定时时间OLED_ShowChinese(3, 1, 31);OLED_ShowString(3, 3, ":");OLED_ShowNum(3, 5, RTC_Time1[0], 2);OLED_ShowString(3, 7, ":");OLED_ShowNum(3, 8, RTC_Time1[1], 2);OLED_ShowString(3, 10, ":");OLED_ShowNum(3, 11, RTC_Time1[2], 2);OLED_ShowChinese(4, 1, 32);OLED_ShowString(4, 3, ":");OLED_ShowNum(4, 5, RTC_Time2[0], 2);OLED_ShowString(4, 7, ":");OLED_ShowNum(4, 8, RTC_Time2[1], 2);OLED_ShowString(4, 10, ":");OLED_ShowNum(4, 11, RTC_Time2[2], 2);// 修改定时时间操作if (KeyNum == 2 && DebounceKey(2)){S_DingShi_Switch = (S_DingShi_Switch + 1) % 6;}switch (S_DingShi_Switch){case 0: // 时if (KeyNum == 3) RTC_Time2[0]++;if (KeyNum == 4) RTC_Time2[0]--;break;case 1: // 分if (KeyNum == 3) RTC_Time2[1]++;if (KeyNum == 4) RTC_Time2[1]--;break;case 2: // 秒if (KeyNum == 3) RTC_Time2[2]++;if (KeyNum == 4) RTC_Time2[2]--;break;case 3: // 时if (KeyNum == 3) RTC_Time1[0]++;if (KeyNum == 4) RTC_Time1[0]--;break;case 4: // 分if (KeyNum == 3) RTC_Time1[1]++;if (KeyNum == 4) RTC_Time1[1]--;break;case 5: // 秒if (KeyNum == 3) RTC_Time1[2]++;if (KeyNum == 4) RTC_Time1[2]--;break;}
}void YuYingMode()   //先说小杰唤醒,然后说打开窗户和关闭窗户
{if (qingping == 0){OLED_Clear();qingping = 1;}
//    if (Serial2_RxFlag == 1)        //串口接收到数据包的标志位,若是收到数据包,会置1
//    {
//        if (strcmp(Serial2_RxPacket, "JIASHI_ON") == 0)
//        {
//            // 外设操作
//        }
//        else if (strcmp(Serial2_RxPacket, "JIASHI_OFF") == 0)
//        {
//            // 外设操作
//        }
//        Serial2_RxFlag = 0; //将标志位清零,不清零就接收不到下一个数据包了
//    }
}// 机智云初始化函数
void MY_Gizwits_Init(void)
{TIM3_Int_Init(9, 7199); // 1MS系统定时usart3_init(9600); // WIFI初始化memset((uint8_t *)&currentDataPoint, 0, sizeof(dataPoint_t)); // 设备状态结构体初始化gizwitsInit(); // 环形缓冲区初始化gizwitsSetMode(2);   // 设置模式userInit();
}// 按键消抖函数
uint8_t DebounceKey(uint8_t key)
{delay_ms(20);return KeyNum == key;
}// 设置时间函数
void TimeSet()
{if (KeyNum == 2 && DebounceKey(2)){S_ShiShi_Time_1 = (S_ShiShi_Time_1 + 1) % 3;}if (S_ShiShi_Time_1 == 0)   // 时间显示模式{MyRTC_ReadTime();OLED_ShowNum(1, 5, MyRTC_Time[3], 2);    // 时OLED_ShowString(1, 7, ":");OLED_ShowNum(1, 8, MyRTC_Time[4], 2);    // 分OLED_ShowString(1, 10, ":");OLED_ShowNum(1, 11, MyRTC_Time[5], 2);   // 秒RTC_Time[0] = MyRTC_Time[3];RTC_Time[1] = MyRTC_Time[4];RTC_Time[2] = MyRTC_Time[5];}else if (S_ShiShi_Time_1 == 1)     // 修改时间{if (KeyNum == 5 && DebounceKey(5)){S_ShiShi_Time_2 = (S_ShiShi_Time_2 + 1) % 3;}switch (S_ShiShi_Time_2){case 0: // 修改时if (KeyNum == 4){RTC_Time[0] = (RTC_Time[0] + 1) % 24; // 加 1 后取模 24,确保在 0 - 23 范围内}if (KeyNum == 3){if (RTC_Time[0] == 0){RTC_Time[0] = 23; // 当为 0 时,减操作变为 23}else{RTC_Time[0]--;}}OLED_ShowNum(1, 5, RTC_Time[0], 2);  // 时break;case 1: // 修改分if (KeyNum == 4){RTC_Time[1] = (RTC_Time[1] + 1) % 60; // 加 1 后取模 60,确保在 0 - 59 范围内}if (KeyNum == 3){if (RTC_Time[1] == 0){RTC_Time[1] = 59; // 当为 0 时,减操作变为 59}else{RTC_Time[1]--;}}OLED_ShowNum(1, 8, RTC_Time[1], 2);  // 分break;case 2: // 修改秒if (KeyNum == 4){RTC_Time[2] = (RTC_Time[2] + 1) % 60; // 加 1 后取模 60,确保在 0 - 59 范围内}if (KeyNum == 3){if (RTC_Time[2] == 0){RTC_Time[2] = 59; // 当为 0 时,减操作变为 59}else{RTC_Time[2]--;}}OLED_ShowNum(1, 11, RTC_Time[2], 2);  // 秒break;}}else if (S_ShiShi_Time_1 == 2){MyRTC_Time[3] = RTC_Time[0];MyRTC_Time[4] = RTC_Time[1];MyRTC_Time[5] = RTC_Time[2];MyRTC_SetTime();S_ShiShi_Time_1 = 0;}
}// 读取时间函数
void TimeRead()
{MyRTC_ReadTime();OLED_ShowNum(1, 5, MyRTC_Time[3], 2);    // 时OLED_ShowString(1, 7, ":");OLED_ShowNum(1, 8, MyRTC_Time[4], 2);    // 分OLED_ShowString(1, 10, ":");OLED_ShowNum(1, 11, MyRTC_Time[5], 2);   // 秒
}// 显示传感器信息
void ChuangGan()
{OLED_ShowChinese(2, 1, 26);OLED_ShowChinese(2, 2, 28);OLED_ShowString(2, 5, ":");if (temperature < 0){OLED_ShowString(2, 6, "-");     //显示负号temperature = -temperature;                 //转为正数}else OLED_ShowString(2, 6, " ");        //去掉负号OLED_ShowNum(2, 7, temperature / 10, 2);OLED_ShowString(2, 9, ".");OLED_ShowNum(2, 10, temperature % 10, 1);OLED_ShowChinese(3, 1, 39);OLED_ShowChinese(3, 2, 40);OLED_ShowString(3, 5, ":");OLED_ShowNum(3, 6, sensorData.ShuiWei, 2);OLED_ShowChinese(3, 5, 78);OLED_ShowChinese(3, 6, 79);OLED_ShowString(3, 13, ":");OLED_ShowNum(3, 14, sensorData.ZhuoDu, 2);OLED_ShowString(4, 1, "PH:");OLED_ShowNum(4, 4, sensorData.PH / 10, 1);OLED_ShowString(4, 5, ".");OLED_ShowNum(4, 6, sensorData.PH % 10, 1);OLED_ShowString(4, 10, "D:");OLED_ShowNum(4, 12, sensorData.Dian, 3);}

6、总结

本文设计了一种基于STM32F103C8T6的水质监测系统,可实时检测水温、水位、pH值、电导率和浊度等参数。系统采用模块化设计,包含传感器采集、OLED显示、WiFi远程监控(通过机智云平台实现手机APP控制)三大功能模块,支持远程、自动和手动三种控制模式。自动模式下可根据预设阈值实现水位自动调节、温度恒温控制及异常报警功能;硬件部分包含多种传感器、继电器控制的水泵和加热器等设备。软件设计采用C语言编程,通过主程序循环处理传感器数据和控制逻辑。该系统具有功能完善、操作灵活等特点,适用于水产养殖、污水处理等

http://www.lryc.cn/news/599025.html

相关文章:

  • 【数组的定义与使用】
  • 使用Python采集招聘网站数据并智能分析求职信息
  • AI大模型各类概念扫盲
  • 【C++】标准模板库(STL)—— 学习算法的利器
  • 算法题(179):单调栈
  • C++抽象类完全指南
  • ARM汇编常见伪指令及其用法示例
  • Datawhale AI数据分析 作业2
  • linux入门 相关linux系统操作命令(二)--文件管理系统 ubuntu22.04
  • DS18B20扩展:在数码管上显示温度时包含小数部分
  • MPI并行梯形积分法:原理、实现与优化指南
  • 【PyTorch】图像二分类项目-部署
  • 从零开始学 Pandas:数据处理核心操作指南
  • 清除浮动以及原理
  • cri-docker部署高版本k8s
  • 闲庭信步使用图像验证平台加速FPGA的开发:第三十四课——车牌识别的FPGA实现(6)叠加车牌识别的信息
  • 5.7 input子系统
  • RocketMQ集群高级特性
  • 洛谷刷题7.24
  • 办公自动化入门:如何高效将图片整合为PDF文档
  • 精通Python PDF裁剪:从入门到专业的三重境界
  • 读书笔记(黄帝内经)
  • 【CMake】CMake 常用语法总结
  • 【STM32】FreeRTOS 任务的创建(二)
  • Bright Data 实战指南:从竞品数据抓取到电商策略优化全流程
  • 深度分析Java类加载机制
  • 【C# 找最大值、最小值和平均值及大于个数和值】2022-9-23
  • 行为型模式-协作与交互机制
  • 基于Matlab图像处理的水果分级系统
  • OpenCV(03)插值方法,边缘填充,透视变换,水印制作,噪点消除