51单片机漏水检测系统实战:从DHT11传感器到上位机监控
发布时间:2026/9/16 9:45:21 作者:尧图编辑部 阅读量:1,286

简介一份基于51单片机的智能化漏水检测系统课程设计资料包面向电子、嵌入式专业学生及开发者用于档案馆库房渗漏监控场景。系统以51系列单片机为核心完成温湿度与浸水信号采集、液晶屏实时显示、超限声光报警并可通过串口将数据上送至LabVIEW上位机实现远程监测预警体现智能化、自动化、集成化的库房漏水告警思路。压缩包共51个文件大小约5.61MB既包含C语言源程序、烧录文件与编译辅助文件也配有Proteus仿真、原理图、上位机工程、流程图和元件清单表覆盖从硬件设计、软件编写到仿真验证的完整链路目录结构便于按模块查阅和二次开发。已有445人学习/下载适合课程设计、毕业设计或综合实践参考借助Proteus仿真可在无实物条件下先行调试控制逻辑与通信流程元件清单和原理图也可为电路板制作与实物搭建提供直接依据。1. 从漏水报警到上位机管理51单片机智能化漏水检测在做什么机房地板下渗出几毫米积水时传统漏水报警器只会狂响维护人员到场后还要沿着支路逐一排查。如果系统能同时显示当前温度、湿度并把漏水状态通过串口上报到上位机这种排查就会变成一次有据可查的故障处理。这就是基于51单片机做智能化漏水检测的核心价值本地可视化、远程可监控、事件可留痕。整套硬件并不复杂STC89C52RC 做控制器DHT11 负责温湿度采集LM393 水浸模块输出泄漏信号LCD1602 在本地实时展示数据通过 USB 转 TTL 串口与上位机通信。真正麻烦的是传感器时序、LCD1602 初始化流程、串口帧格式这三件事之间的协调。在 Proteus 中先仿真验证逻辑、再决定是否打样是这套方案最实用的落地点也正是标题里原理图、流程图、仿真图、物料清单要解决的完整交付链路。这篇文章适合两类人正在做课程设计或毕业设计的学生可以按章节一步步搭出可演示的实物物联网或自动化领域的工程师可以把这里的水浸检测替换成压差、液位等传感器换成新的通信协议后直接复用主控框架。2. 硬件选型与信号采集51单片机怎么接温湿度和浸水传感器2.1 传感器选型与测量原理为什么选用DHT11和LM393水浸模块DHT11 是极常见的单总线数字温湿度传感器内部包含一个电阻式湿度元件和一个 NTC 测温元件每次通讯输出 40 位数据其中湿度整数、湿度小数、温度整数、温度小数各 8 位最后 8 位是校验和。它只需要一根数据线5V 供电但数据线必须外接 4.7kΩ 上拉电阻。51 单片机主频低、I/O 资源少DHT11 的单总线时序恰好能在几十微秒级别内被精准控制。它的缺点是采样间隔要求不小于 1 秒且湿度精度只有 ±5%RH不适合高湿冷凝环境。对我们这个漏水检测场景来说DHT11 的作用是判断环境是否已经接近露点而不是精确计量湿度所以精度完全够用。水浸部分我习惯用基于 LM393 比较器的水浸传感器模块而不是自制电极。模块上两根探测针裸露遇到水时电阻下降比较器翻转输出一个高或低电平。板载电位器可以调节触发阈值高灵敏模式用于漏水预警低灵敏模式用于电解电容测试等场景。仿真时 Proteus 里没有真实“水”所以在原理图里用一只普通开关或电位器代替探测针电阻变化开关闭合模拟浸水断开模拟正常。这是 Proteus 仿真中比较高效的做法也避免为了传感器模型去折腾元件库。采用 51 单片机而不是 STM32 或 Arduino是因为这个任务的计算量和 I/O 占用都很小51 的 256B RAM 和 8KB Flash 足够装下状态机、显示缓冲和串口协议。STC89C52RC 是 51 增强型的代表型号12T 模式下工作频率可以到 12MHzKeil C51 工程直接支持。Proteus 里没有 STC 系列模型统一用 AT89C51 或 AT89C52 代替仿真引脚兼容、时序相同运行效果一致。2.2 引脚分配与接口电路设计LCD1602和串口怎么接LCD1602 有 16 个引脚实际控制最少需要 6 个 I/ORS、E 和 4 条数据线。我采用 4 位数据模式把 R/W 直接接地固定为写模式省掉 R/W 控制脚。DHT11 占用一个 I/O水浸模块占用一个 I/O再加上串口收发一共只需要 10 个 I/O。下面是我常用的一组引脚分配方便后续代码直接对应。器件引脚单片机引脚说明DHT11 DATAP1.2单总线数据需 4.7kΩ 上拉至 VCC水浸模块 SIGP1.3数字量输出高电平有效或低电平有效需看模块型号LCD1602 RSP1.0寄存器选择0指令 1数据LCD1602 EP1.1使能信号下降沿锁存数据LCD1602 DB4P2.04 位数据最高位起始LCD1602 DB5P2.1数据位LCD1602 DB6P2.2数据位LCD1602 DB7P2.3数据位单片机 TXDP3.1接上位机 RXD 或 COMPIM RXD单片机 RXDP3.0接上位机 TXD 或 COMPIM TXD以上接法避开 P3.2 和 P3.3 的外部中断引脚如果你后续要用外部中断做浸水紧急唤醒可以把水浸模块改接到 P3.2但要同步调整代码里的位定义。串口部分实物中我通常用带 CH340 的 USB 转 TTL 模块直接连接单片机的 TXD/RXD必须与单片机的 GND 共地如果接 MAX232 做 RS232 电平则要注意 DB9 的 2、3 脚交叉。这一节的引脚定义在代码里直接用 sbit 和宏固定下来方便后续阅读和维护// hardware_pin.h - 51单片机漏水检测硬件引脚定义 #include reg52.h // 传感器 sbit DHT_DATA P1^2; // DHT11 单总线数据 sbit WATER_ALM P1^3; // 水浸模块信号输入 // LCD1602 控制线 sbit LCD_RS P1^0; // 寄存器选择 sbit LCD_E P1^1; // 使能信号 // LCD1602 4位数据线DB4-DB7 接到 P2.0-P2.3 #define DATAPORT P2注意WATER_ALM的有效电平取决于具体模块。有的模块正常输出高电平、漏水时拉低有的正好相反。在程序里我用连续采样加去抖来适配只要修改一个宏即可。DATAPORT定义成 P2是为了能用整字节操作低 4 位在 LCD 驱动中对高低半字节做移位处理。2.3 Proteus原理图绘制步骤与物料清单进入 Proteus 8 Professional 后新建一个 Schematic 工程从库中搜索并添加以下器件AT89C51仿真用代替 STC89C52RC、12MHz 晶振、两个 30pF 瓷片电容、10uF 电解电容、10kΩ 电阻、LM016LProteus 中 LCD1602 的标准模型、DHT11、SWITCH模拟水浸、COMPIM串口通信组件。如果只是想先跑通显示逻辑也可以用 VIRTUAL TERMINAL 直接看串口数据。原理图连接顺序按照 2.2 节的引脚表。先把单片机最小系统画好XTAL1 接晶振一侧与 30pF 电容XTAL2 接晶振另一侧与另一个 30pF 电容电容另一端接到 GNDRST 引脚接 10kΩ 电阻到 GND 和 10uF 电容到 VCC这样上电时 RST 能维持高电平几十毫秒完成复位。DHT11 的数据线加 4.7kΩ 上拉电阻到 VCC。COMPIM 模块的 RXD 接单片机 TXDP3.1TXD 接单片机 RXDP3.0注意是交叉连接不是直连。绘制完成后可以用下面的网络连接清单快速核对Y1: 1 - XTAL1, 2 - XTAL2 C1: 1 - XTAL1, 2 - GND C2: 1 - XTAL2, 2 - GND C3: 1 - RST, 2 - VCC R1: 1 - RST, 2 - GND R2: 1 - VCC, 2 - DHT_DATA U2(DHT11): DATA - P1.2, VCC - VCC, GND - GND S1: 一端 - VCC, 另一端 - WATER_ALM(P1.3) LCD1(LM016L): RS - P1.0, E - P1.1, DB4-DB7 - P2.0-P2.3, VSS-GND, VDD-VCC, V0-可调电阻中轴 COMPIM: RXD - P3.1, TXD - P3.0这里水浸传感器用开关 S1 替代闭合表示浸水如果想模拟渐进式漏水可以用电位器把它接到比较器电路但通常开关已经够验证逻辑。COMPIM 的波特率要在属性里设置成 96008 数据位、无校验、1 停止位。V0 引脚接一个 10kΩ 电位器用于调节 LCD 对比度这在仿真里容易被漏掉漏掉后屏幕永远不显示内容。硬件层面的物料清单如下按此采购可以直接装配。序号名称型号/规格数量备注1单片机STC89C52RC / AT89C511DIP40 或贴片2晶振12MHz1直插3瓷片电容30pF2晶振负载4电解电容10uF/16V1复位电路5电阻10kΩ1复位下拉6电阻4.7kΩ1DHT11 上拉7温湿度传感器DHT111单总线8水浸传感器模块LM393 比较器模块1带电平输出9液晶屏LCD160214 位模式10USB 转 TTL 模块CH3401与上位机通信11电源5V/1A USB 供电1可同时给单片机供电12杜邦线母对母/公对母若干连接使用3. 主控程序与LCD1602显示漏水检测状态机如何跑起来3.1 软件架构主循环、定时器与事件状态机整个 MCU 软件用裸机时间片轮询实现不用 RTOS因为任务量太小。主循环固定做三件事读取传感器、刷新 LCD、检查串口发送标志。为了避免某一步阻塞影响其他任务我把不同操作的执行周期分开。DHT11 的最短采样间隔是 1 秒我设置成 2 秒读取一次既满足传感器要求也让 LCD 数字跳动不要过于频繁。水浸检测每 20ms 扫描一次连续 3 次确认才认为是真的漏水滤掉水流溅射造成的瞬态抖动。状态机只保留三个状态NORMAL、ALARM、FAULT。NORMAL 下温湿度正常、水浸无信号ALARM 表示检测到漏水FAULT 表示 DHT11 连续多次读取失败或数据校验未通过。FAULT 状态要求 LCD 显示 Err 字样同时串口上报故障码方便上位机端区分“没漏水”和“传感器坏了”。主流程可以用下面的文本表示main(): 关中断配置系统时钟和外设 初始化LCD显示开机画面 初始化串口波特率9600 开中断 进入while(1): 若2秒时间到启动一次DHT11读取 若20ms时间到扫描水浸引脚并去抖 若LCD刷新标志置位更新LCD内容 若串口发送标志置位发送一帧数据这个结构的好处是任何单一传感器故障都不会导致系统完全瘫痪。比如 DHT11 长时间无响应程序不会卡死等待而是超时后把状态置为 FAULT水浸检测和串口发送仍然继续工作。3.2 DHT11单总线读取与浸水检测代码到底怎么写DHT11 的时序有几个关键点起始信号要拉低至少 18ms然后在主机的 20~40μs 拉高后释放总线从机会拉低 80μs 作为响应再拉高 80μμs 准备发送数据。每一位数据都是低电平 50μs 后跟一个高电平高电平持续时间 26~28μs 表示“0”持续 70μs 表示“1”。程序里用延时 30μs 的方式判断高电平是否超过半个位周期。下面是一个可用的读取函数骨架省略了校验和与超时处理// dht11.c - 读取DHT11温湿度数据格式tempx/1, humx/1 #include reg52.h #include hardware_pin.h void delay_us(unsigned int us) { while (us--) { _nop_(); _nop_(); // 12MHz下约1us/次 } } void dht11_start(void) { DHT_DATA 0; delay_us(22000); // 拉低 18ms DHT_DATA 1; delay_us(40); // 释放总线 } unsigned char dht11_read_byte(void) { unsigned char i, code 0; for (i 0; i 8; i) { while (DHT_DATA 0); // 等待低电平结束 delay_us(30); // 30us后采样 if (DHT_DATA) { code | (0x80 i); while (DHT_DATA); // 等待高电平结束 } } return code; } bit dht11_read(unsigned char *h, unsigned char *t) { unsigned char data[4]; // 发送起始信号后等待响应 DHT_DATA 1; delay_us(40); if (DHT_DATA 0) { // 检查响应低电平 while (DHT_DATA 0); while (DHT_DATA 1); // 跳过80us高电平 data[0] dht11_read_byte(); data[1] dht11_read_byte(); data[2] dht11_read_byte(); data[3] dht11_read_byte(); // 校验省略实际项目必须判断 data[0]data[1]data[2]data[3] data[4] *h data[0]; *t data[2]; return 1; } return 0; }这段代码中delay_us(22000)在最差情况下会阻塞主循环约 22ms但 DHT11 每 2 秒才读一次阻塞不是问题。dht11_read_byte()内对每一位都等待低电平结束所以万一传感器断开程序可能卡死在while (DHT_DATA 0)。实际产品中应该给等待加超时计数这里为了突出重点先把超时逻辑略去你在自己的工程里一定要加一个计数器比如循环超过 1000 次就退出。水浸检测相对简单但也不能直接读一次电平就判定。因为水浸模块在溅水时信号可能悬空或抖动连续采样是最廉价可靠的滤波办法。我的实现如下// water_detect.c - 水浸检测带连续确认 #include hardware_pin.h #define LEAK_LEVEL 0 // 假设模块输出低电平表示漏水 unsigned char water_scan(void) { static unsigned char count 0; if (WATER_ALM LEAK_LEVEL) { if (count 3) count; if (count 3) return 1; } else { count 0; } return 0; }count变量是 static在连续 3 次采样后才返回漏水状态返回后不会立刻清除直到电平恢复才归零。这样可以防止一次误触发电平触发报警。如果模块是输出高电平表示漏水只需把LEAK_LEVEL改为 1。3.3 LCD1602 4位模式驱动与显示布局LCD1602 在 4 位模式下的初始化有一个固定套路先把模块当 8 位模式发送三次 0x03然后再发送 0x02 切换到 4 位模式。这个序列是标准步骤缺一不可。初始化完成后写数据和写命令都要先发高 4 位再发低 4 位中间让 E 引脚产生一个下降沿。初始化代码如下// lcd1602.c - 4位模式驱动 #include reg52.h #include hardware_pin.h #define LCD_DATA_PORT P2 void lcd_strobe() { LCD_E 1; _nop_(); _nop_(); LCD_E 0; } void lcd_write_nibble(unsigned char nibble) { LCD_DATA_PORT 0xF0; // 清低4位 LCD_DATA_PORT | (nibble 0x0F); // 写入低4位 lcd_strobe(); } void lcd_write_cmd(unsigned char cmd) { LCD_RS 0; lcd_write_nibble(cmd 4); lcd_write_nibble(cmd); delay_us(200); // 指令执行时间 } void lcd_init() { LCD_E 0; delay_us(15000); lcd_write_nibble(0x03); delay_us(5000); lcd_write_nibble(0x03); delay_us(1000); lcd_write_nibble(0x03); delay_us(1000); lcd_write_nibble(0x02); delay_us(1000); lcd_write_cmd(0x28); // 4位、2行、5x7点阵 lcd_write_cmd(0x0C); // 显示开、光标关 lcd_write_cmd(0x06); // 光标增量、不移位 lcd_write_cmd(0x01); // 清屏 }lcd_write_nibble里对LCD_DATA_PORT执行 0xF0因为低 4 位是 DB4-DB7这样既不影响高位的其他引脚又能保证低 4 位精确输出。lcd_write_cmd先发高 4 位再发低 4 位两次 strobe 之间要留至少 40μs 的指令周期这里用delay_us(200)保守处理。数据写入函数只需把 RS 置 1其余流程一样。显示布局我固定为第一行显示温度和湿度第二行显示漏水状态。切换状态时整行刷新避免残留旧字符。LCD 行实际显示示例第 1 行T:xxC RH:xx%T:25C RH:60%第 2 行Leak:xxxLeak:No/Leak:YES3.4 串口发送协议设计上位机怎么解析上位机不需要知道单片机内部变量名只要按帧解析即可。我给这个项目定了一个最简单的协议帧头0xAA、设备地址0x01、温度、湿度、漏水状态、帧尾0xBB共 6 字节。温度与湿度直接用无符号整数表示单位分别为 ℃ 和 %RH漏水状态 0 代表正常、1 代表漏水。字节索引内容值域0帧头0xAA1设备地址0x012温度0-993湿度0-994漏水状态0-15帧尾0xBB该协议虽然原始但防止了串口粘包接收端只要在数据流里找到0xAA 0x01且第 6 个字节是0xBB就认为收到一个有效帧。发送代码// uart.c - 串口发送一个数据帧 #include reg52.h void uart_send_byte(unsigned char dat) { SBUF dat; while (!TI); TI 0; } void send_status(unsigned char temp, unsigned char hum, unsigned char leak) { uart_send_byte(0xAA); uart_send_byte(0x01); uart_send_byte(temp); uart_send_byte(hum); uart_send_byte(leak); uart_send_byte(0xBB); }这里使用查询式发送因为数据量很小每次发送 6 字节在 9600 波特率下消耗不到 6ms不影响主循环。如果你将来增加心跳包或历史数据上报可以改成中断发送但对当前场景没必要。4. 上位机开发与Proteus仿真联调远程监控和可视化4.1 上位机技术选型用C#还是Python上位机的作用是接收 51 单片机通过串口发来的数据帧解析后实时显示并记录时间戳。选型上常见两种C# WinForms 和 Python pyserial。C# 的优势在于 VS2019/VS2022 环境下创建串口程序非常快SerialPort 控件拖拽即用界面轮询和图表控件比较成熟Python 的优势是跨平台和部署灵活但需要额外安装依赖包界面需要 PyQt 或 Tkinter。这个项目用 C# WinForms 最直接因为网上大量源码都是 C# 写的教程也最密集遇到编译问题容易搜到答案。注意用 VS2019 创建的 C# 工程在 VS2015 里打开会提示需要升级解决方案如果你只有 VS2015建议在工程属性里把目标框架改成 .NET Framework 4.0再保存一次即可。4.2 串口通信上位机代码帧解析与界面显示WinForms 里放置一个SerialPort控件设置端口号、波特率 9600、数据位 8、停止位 1、无校验。用DataReceived事件回调读取缓冲区。注意这个事件发生在非 UI 线程所以更新 Label 时必须用Invoke或者BeginInvoke否则会抛跨线程访问异常。下面的代码是核心解析部分// MainForm.cs - 串口数据接收与帧解析 private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) { int bytes serialPort1.BytesToRead; byte[] buffer new byte[bytes]; int n serialPort1.Read(buffer, 0, bytes); for (int i 0; i n; i) { if (buffer[i] 0xAA i 5 n buffer[i 1] 0x01 buffer[i 5] 0xBB) { int temp buffer[i 2]; int hum buffer[i 3]; int leak buffer[i 4]; this.Invoke(new Action(() { labelTemp.Text temp ℃; labelHum.Text hum %RH; labelLeak.Text leak 1 ? 漏水 : 正常; listBox1.Items.Add( DateTime.Now.ToString(HH:mm:ss) Temp: temp Hum: hum Leak: (leak 1 ? YES : NO)); })); break; // 一次只处理一个帧 } } }解析逻辑先找 0xAA随后确认设备地址 0x01 和帧尾 0xBB三个条件同时满足才认为数据有效。i 5 n保证不越界。每条有效帧追加到 ListBox形成时间线记录这比单纯在 Label 上跳数字更有价值。4.3 Proteus虚拟串口设置与联调步骤Proteus 中的 COMPIM 组件仿真串口需要依赖 PC 上的虚拟串口工具。常见做法是先安装 VSPD创建一对虚拟串口比如 COM2 和 COM3这两个端口在系统层面互联。然后在 Proteus 里双击 COMPIM将 Physical Virtual Port 设为 COM2在上位机程序中将 SerialPort 端口号设为 COM3。这样单片机通过 P3.1/P3.0 送出的数据会从 COM2 进入系统再被 COM3 读出等同于接了一个真实 USB 转 TTL 模块。联调步骤为先打开 VSPD 创建串口对再启动 Proteus 仿真最后启动 C# 上位机程序。如果上位机打开串口时报“端口被占用”说明 VSPD 创建失败或 Proteus 还在占用 COM2重新创建即可。仿真运行后把 Proteus 里的水浸开关闭合LCD1602 第二行应立即显示 Leak:YES同时上位机列表出现漏水事件。整个过程中串口帧每个周期发送一次上位机端即使偶发丢帧也不影响最终状态显示。联调参数如下表必须三处保持一致否则解析必然失败。位置波特率数据位停止位校验位51 单片机串口初始化960081无Proteus COMPIM 属性960081无C# SerialPort 控件960081无4.4 流程图在项目里的实际画法标题里要求流程图目的不是画个漂亮图而是让你在写代码前把事件分支固定下来。用 Proteus 画仿真图的同时我会在 Visio 或 Draw.io 里画一张系统级流程图。如果你不擅长图形工具也可以先用文本流程图表达逻辑再转录成图形。下面是一个可直接参照的版本[上电] - [初始化LCD与串口] - [读取DHT11] - 是否成功? 否 - [FAULT状态LCD显示Err串口发故障帧] - 延时2s后重读 是 - [读取水浸状态] - 有漏水 是 - [漏水标志置位控制LED/蜂鸣器] 否 - [清除漏水标志] - [刷新LCD] - [串口发送温湿度漏水状态] - 延时20ms - 回到读取DHT11注意流程图中加入故障分支这是很多课程设计里容易漏掉的关键路径。真实上位机收到的数据里2 秒一帧如果上位机超过 10 秒没收到任何帧说明通信链路断了或是单片机卡死在某个循环里。一个好的流程图要把这种“超时”分支也画出来才能让程序有容错能力。5. 掉坑与验证把漏水检测系统调稳的5个关键技巧5.1 LCD1602不显示或乱码先查对比度再查时序启动仿真后屏幕全黑或只有一排方块90% 是对比度电位器的问题。Proteus 里的 LM016L 在 V0 引脚悬空时显示对比度可能过高全屏变成实心方块实物中同样需要在 V0 接一个 10kΩ 电位器到地调节到能清晰看到字符。如果屏幕有显示但是乱码检查初始化序列是否漏发了 0x03 三次或 0x02 切换 4 位模式的指令。仿真时还容易犯一个错误把 DB4-DB7 接到 P0 口但没加上拉电阻导致数据漂移。把数据线改到 P2 或 P1 口是最省事的方案。5.2 DHT11连续读取失败间隔必须大于1秒DHT11 数据手册明确要求两次读取间隔必须大于 1 秒实际测试发现间隔在 1.2 秒以内时数据错误率显著上升。很多人把它放在主循环里每几十毫秒读一次必然导致传感器无响应。解决办法是加一个软件计时标志比如用定时器中断做个 2 秒的基准时间。另外start 信号拉低时间必须是 18ms 以上但也不能太长超过 30ms 会导致部分 DHT11 模块反应异常。在 12MHz 晶振下我通常用 22ms 的延时。如果你发现仿真和实物的行为不一致先看 DHT11 模型版本Proteus 的老版本模型对时序要求更严格把延时调到 25ms 往往能解决。5.3 串口出现雪花或丢帧用虚拟终端先验证串口问题一半是信号线接反一半是波特率不对。Proteus 里 VIRTUAL TERMINAL 可以直接挂在单片机 TXD 引脚上不用 COMPIM就能以 ASCII 模式看到原始串口输出。先把上位机放到一边用 VIRTUAL TERMINAL 验证 51 单片机是否真的在发数据、发的内容是不是 6 字节正确帧。如果虚拟终端全是乱码优先检查波特率、晶振频率和定时器重装值。12MHz 晶振与 9600 波特率的组合在 12T 模式下误差约为 0.17%可以接受但如果换成 11.0592MHz 晶振误差几乎为零这在上位机长时间运行时能明显降低累计丢帧概率。修改晶振后Keil 里的定时器初值也要同步更新否则波特率会偏到无法通信。本文还有配套的精品资源点击获取