1. 项目概述:当智能家居遇上水患预警
家里没人时,水管突然爆了、水槽溢水了,或者只是忘记关水龙头——这种场景光是想想就让人头皮发麻。传统的解决方案是购买商业化的漏水传感器,但它们往往功能单一,报警方式有限(比如只能本地蜂鸣),而且一旦你不在家,听到报警也无济于事。作为一名折腾智能家居多年的玩家,我一直在寻找一个更主动、更智能的解决方案:它不仅要能第一时间发现漏水,更要能在我收到警报的瞬间,自动执行一系列止损操作,比如关闭家里的总水阀。
这个想法催生了今天要分享的项目:一个基于ESP32和Telegram的智能洪水防护系统。它的核心逻辑非常简单:ESP32连接一个或多个漏水传感器,实时监测状态。一旦检测到漏水,它不会只是傻傻地叫唤,而是会立刻通过Wi-Fi向你的Telegram账号发送警报,包含位置、时间等关键信息。更重要的是,你可以通过Telegram Bot直接远程控制一个继电器,进而驱动电磁阀切断水源。整个过程,从检测到响应,完全自动化,且不受地理距离限制。ESP32的低功耗和强大网络能力,加上Telegram Bot即时、免费、跨平台的消息推送,构成了这个低成本、高可靠性DIY项目的基石。
2. 核心组件选型与设计思路
2.1 主控芯片:为什么是ESP32?
在众多微控制器中,选择ESP32几乎是这个项目的必然。首先,它内置了Wi-Fi和蓝牙模块,这意味着我们无需额外添加网络模块,简化了硬件设计和成本。其次,ESP32拥有双核处理器和丰富的外设接口(GPIO、ADC、I2C等),性能足以轻松处理传感器数据读取、网络通信和逻辑判断。最后,其庞大的社区和Arduino核心支持,使得开发门槛极低,有海量的库和示例可供参考。
注意:市面上有ESP32-S2、ESP32-C3等多种变体。对于本项目,选择最经典的ESP32 DevKit V1或NodeMCU-32S开发板即可,它们引脚引出完善,便于调试。
2.2 传感部分:漏水检测的几种方案
漏水传感器的原理通常是检测两个探针之间的电阻变化。当没有水时,空气是绝缘体,电阻极大;当有水连接两个探针时,水的导电性使得电阻急剧下降,从而触发信号。
- 商用漏水传感器:最简单直接的选择。通常是一个带有探针的塑料模块,输出数字信号(高电平或低电平)。优点是即插即用,防水性好。你可以选择常开(NO)或常闭(NC)型,根据你的电路逻辑选择。
- 自制探针:成本最低的方案。用两根不锈钢丝、铜线甚至PCB上的覆铜条作为探针,间隔几毫米放置。你需要自己设计一个简单的比较器电路(例如使用LM393)将电阻变化转换为数字信号,再输入ESP32。这种方式更灵活,可以制作成任意形状,但需要一定的电路知识,并且要做好探针的防腐蚀处理(不锈钢是较好的选择)。
对于大多数DIYer,我推荐直接使用现成的数字输出漏水传感器模块,省心省力。
2.3 执行部分:如何远程关闭水阀?
这是系统的“手”。我们需要一个执行器来转动家里的球阀或截止阀。常见方案有:
- 电机驱动球阀:直接替换原有的手动球阀,安装电动执行器。扭矩大,可靠性高,但价格昂贵,安装需要一定的管道工技能。
- 电磁阀:在主管道上串联一个常开型电磁阀。通电时关闭,断电时打开。搭配UPS(不间断电源)使用,可以确保断电时水路依然畅通。这是性价比很高的方案,但需要确认家中水压与电磁阀规格匹配。
- 舵机+夹具:最具DIY精神的方案。用一个大扭矩舵机(如MG996R)配合3D打印的夹具,夹在现有的手动阀门手柄上,通过旋转手柄来开关水。成本低,通用性强,但结构强度和长期可靠性需要仔细设计。
考虑到易用性和可靠性,本项目采用“继电器 + 常开型电磁阀”的方案。ESP32通过一个继电器模块控制电磁阀的通断电。
2.4 通信与交互核心:Telegram Bot的优势
为什么选择Telegram Bot而不是微信、邮件或短信?
- 即时性与可靠性:Telegram消息推送几乎无延迟,且不依赖手机品牌和后台管理策略,到达率远高于国内某些需要常驻后台的App。
- 强大的Bot API:Telegram为机器人提供了极其完善和易用的HTTP API,可以轻松实现发送消息、接收命令、创建交互式键盘等功能。
- 完全免费:发送消息、创建Bot没有任何费用。
- 跨平台:手机、电脑、网页端均可使用,随时随地查看和控制。
我们将创建一个Telegram Bot,它将成为你和ESP32设备之间的智能中介。ESP32通过Wi-Fi向Bot发送警报,你通过向Bot发送命令(如/close_valve)来控制继电器。
3. 硬件搭建与电路连接详解
3.1 物料清单
| 组件 | 型号/规格 | 数量 | 备注 |
|---|---|---|---|
| 主控板 | ESP32 DevKit V1 | 1 | 或其他ESP32开发板 |
| 漏水传感器 | 数字输出模块 | 1-多个 | 建议常开(NO)型 |
| 继电器模块 | 5V 单路 | 1 | 用于控制电磁阀 |
| 电磁阀 | DN15/20,常开型,DC12V/24V | 1 | 电压根据电源定,口径匹配家用水管 |
| 电源 | 12V/24V直流电源适配器 | 1 | 为电磁阀供电 |
| 电源(降压) | DC-DC降压模块 (如LM2596) | 1 | 将12V/24V降至5V为ESP32和继电器供电 |
| 导线、杜邦线 | 若干 | ||
| 水管接头 | 与电磁阀匹配 | 2 | 用于将电磁阀接入管道 |
3.2 电路连接图与原理
整个系统的供电与控制逻辑如下:
- 市电通过电源适配器转换为直流电(例如12V)。
- 该直流电一路直接供给电磁阀(注意正负极)。
- 另一路通过DC-DC降压模块降至稳定的5V,为ESP32开发板和继电器模块的控制端供电。
- ESP32的某个GPIO口(如GPIO4)连接漏水传感器的数字输出引脚。
- ESP32的另一个GPIO口(如GPIO5)连接继电器模块的IN(信号)引脚。
- 继电器模块的常开(NO)触点串联在电磁阀的供电回路中。当ESP32给继电器信号使其吸合时,电磁阀的电路接通,电磁阀动作(关闭);断开信号,则电磁阀断电恢复打开状态。
接线步骤:
- 电源部分:将12V电源正极接降压模块的
Vin+,负极接GND。降压模块的Vout+(5V)接ESP32的VIN或5V引脚(如果板载稳压器支持),Vout-接ESP32的GND。同时,将这5V和GND也接到继电器模块的VCC和GND。 - 传感器部分:漏水传感器模块通常有三根线:
VCC(接3.3V或5V,看模块说明)、GND、DO(数字输出)。将DO线连接到ESP32的GPIO4。VCC和GND对应连接。 - 继电器控制:将ESP32的
GPIO5连接到继电器模块的IN引脚。 - 电磁阀驱动:将12V电源的正极接到继电器模块的
COM(公共端)端子。将电磁阀的正极线接到继电器模块的NO(常开端)端子。电磁阀的负极直接接回12V电源的负极。这样,只有当继电器吸合(GPIO5输出高电平)时,电磁阀才会得电关闭。
重要安全提示:电磁阀和继电器的接线涉及市电转换后的直流电,务必在断电情况下操作。确保所有接线牢固,裸露部分用绝缘胶带包好。水管安装部分,如果自己不熟悉,请务必咨询专业水管工,防止安装不当导致漏水。
4. 软件部分:从创建Bot到编写代码
4.1 创建你的Telegram Bot
- 在Telegram中搜索
@BotFather并开始对话。 - 发送
/newbot命令。 - 根据提示,为你的Bot起一个名字(如
MyFloodGuardBot)和一个唯一的用户名(必须以bot结尾,如my_flood_guard_bot)。 - 创建成功后,
BotFather会给你一个HTTP API Token,格式类似123456789:ABCdefGHIjklMnOprSTUvWxyZ-abcDEFgh4。务必妥善保存这个Token,它是你的Bot与ESP32通信的钥匙。
4.2 Arduino IDE环境配置
- 安装ESP32开发板支持:打开Arduino IDE,进入
文件->首选项,在“附加开发板管理器网址”中添加:https://espressif.github.io/arduino-esp32/package_esp32_index.json。然后进入工具->开发板->开发板管理器,搜索“esp32”,安装“Espressif Systems”的ESP32平台。 - 安装所需库:本项目需要两个关键库:
- Universal Telegram Bot Library:用于与Telegram Bot交互。在
项目->加载库->管理库中搜索“Universal Telegram Bot”并安装。 - WiFi和WiFiClientSecure:ESP32核心自带,无需额外安装。
- Universal Telegram Bot Library:用于与Telegram Bot交互。在
4.3 核心代码逻辑解析与编写
代码的核心是循环执行以下任务:1. 检查传感器状态;2. 处理来自Telegram的消息;3. 根据状态和命令执行动作。
#include <WiFi.h> #include <WiFiClientSecure.h> #include <UniversalTelegramBot.h> // 配置你的网络和Bot const char* ssid = "你的Wi-Fi名称"; const char* password = "你的Wi-Fi密码"; const char* botToken = "你的BotFather给的Token"; // 例如 "123456789:ABCdef..." // 初始化Telegram Bot WiFiClientSecure client; UniversalTelegramBot bot(botToken, client); // 定义引脚 const int floodSensorPin = 4; // 漏水传感器连接的GPIO const int relayPin = 5; // 继电器控制引脚 const int valveCloseDuration = 10000; // 电磁阀关闭后保持的时间(毫秒),10秒 // 变量定义 bool floodDetected = false; bool valveClosed = false; unsigned long valveCloseTime = 0; String chatId; // 用于存储授权用户的Chat ID void setup() { Serial.begin(115200); pinMode(floodSensorPin, INPUT_PULLUP); // 使用内部上拉,传感器输出低电平有效 pinMode(relayPin, OUTPUT); digitalWrite(relayPin, LOW); // 初始化继电器为断开(电磁阀打开) // 连接Wi-Fi WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\nWiFi connected."); Serial.print("IP address: "); Serial.println(WiFi.localIP()); // 设置SSL证书(Telegram需要) client.setCACert(TELEGRAM_CERTIFICATE_ROOT); // 发送启动消息到第一个联系Bot的用户(即你自己) sendStartupMessage(); } void loop() { // 任务1:检查漏水传感器 checkFloodSensor(); // 任务2:处理Telegram消息 int numNewMessages = bot.getUpdates(bot.last_message_received + 1); while (numNewMessages) { handleNewMessages(numNewMessages); numNewMessages = bot.getUpdates(bot.last_message_received + 1); } // 任务3:管理电磁阀状态(例如,关闭10秒后自动打开) manageValveState(); delay(100); // 短延时,避免过于频繁的查询 } void checkFloodSensor() { // 假设传感器在检测到水时输出低电平(LOW) if (digitalRead(floodSensorPin) == LOW && !floodDetected) { floodDetected = true; Serial.println("警报!检测到漏水!"); if (chatId != "") { bot.sendMessage(chatId, "🚨 *洪水警报!* 检测到漏水!", "Markdown"); // 可以附加更多信息,如从网络获取的时间 } // 自动关闭水阀 closeWaterValve(); } else if (digitalRead(floodSensorPin) == HIGH) { floodDetected = false; // 水干后重置状态 } } void handleNewMessages(int numNewMessages) { for (int i = 0; i < numNewMessages; i++) { String text = bot.messages[i].text; chatId = bot.messages[i].chat_id; // 记录第一个联系Bot的用户ID if (text == "/start") { String welcome = "欢迎使用智能洪水防护系统!\n"; welcome += "可用命令:\n"; welcome += "/status - 查看当前状态\n"; welcome += "/close_valve - 手动关闭水阀\n"; welcome += "/open_valve - 手动打开水阀\n"; bot.sendMessage(chatId, welcome, ""); } if (text == "/status") { String statusMsg = "系统状态报告:\n"; statusMsg += "🔧 网络: " + String(WiFi.status() == WL_CONNECTED ? "已连接" : "断开") + "\n"; statusMsg += "💧 传感器: " + String(floodDetected ? "检测到漏水!" : "正常") + "\n"; statusMsg += "🚰 水阀: " + String(valveClosed ? "已关闭" : "已打开") + "\n"; statusMsg += "📶 IP: " + WiFi.localIP().toString(); bot.sendMessage(chatId, statusMsg, ""); } if (text == "/close_valve") { closeWaterValve(); bot.sendMessage(chatId, "指令已执行,水阀正在关闭。", ""); } if (text == "/open_valve") { openWaterValve(); bot.sendMessage(chatId, "指令已执行,水阀已打开。", ""); } } } void closeWaterValve() { digitalWrite(relayPin, HIGH); // 继电器吸合,电磁阀通电关闭 valveClosed = true; valveCloseTime = millis(); // 记录关闭开始时间 Serial.println("水阀已关闭。"); } void openWaterValve() { digitalWrite(relayPin, LOW); // 继电器断开,电磁阀断电打开 valveClosed = false; Serial.println("水阀已打开。"); } void manageValveState() { // 如果水阀因漏水而关闭,持续一段时间后自动打开(假设漏水已处理) if (valveClosed && floodDetected && (millis() - valveCloseTime > valveCloseDuration)) { // 这里可以添加再次检查传感器状态的逻辑 // 如果依然检测到水,可以再次报警,或者保持关闭 // 本例中,我们简单地在10秒后重新打开,并发送提示 openWaterValve(); if (chatId != "") { bot.sendMessage(chatId, "⚠️ 自动安全检查:水阀已重新打开。请确认漏水情况是否已解决。", ""); } } } void sendStartupMessage() { // 这个函数需要Bot先收到一条消息(如/start)才能获得chatId。 // 一种做法是在loop()初期等待第一条消息,或者将chatId硬编码(不推荐,不安全)。 // 更优做法:将chatId存储在EEPROM或文件系统中,启动时读取并发送消息。 // 此处为简化,仅在loop中处理/start命令时获取chatId。 }代码关键点解析:
- 内部上拉电阻:
INPUT_PULLUP模式让传感器在无水时引脚读数为高电平(HIGH),有水时被拉低为低电平(LOW),这样连接更稳定。 - 消息处理循环:
bot.getUpdates是轮询获取新消息的方式。bot.last_message_received + 1确保不会重复处理旧消息。 - 阀状态管理:
manageValveState函数实现了一个简单的安全逻辑:关闭阀门一段时间后自动打开,防止因误报或临时情况导致长期停水。在实际应用中,这个逻辑可以根据需要变得更复杂,比如只有在传感器恢复干燥后才打开。 - Chat ID管理:代码中简单地将第一个与Bot交互的用户ID存为
chatId。在生产环境中,你应该实现一个简单的认证机制,比如在代码中预定义一个密码,用户首次使用时需要发送密码进行绑定,然后将授权的chatId保存到ESP32的EEPROM或SPIFFS文件系统中。
5. 系统部署、调试与优化
5.1 硬件安装与注意事项
- 电磁阀安装:选择家中主进水管道的一个方便操作的位置(如橱柜下方)。务必先关闭总水阀,排空管道余水后再进行安装。按照电磁阀箭头指示的水流方向安装,使用生料带密封螺纹接口。安装后,缓慢打开总水阀,检查各接口是否渗漏。
- 传感器布置:将漏水传感器放置在风险最高的区域,如洗衣机底部、热水器下方、厨房水槽柜内。可以购买多个传感器并联到同一个数字输入口(都触发低电平),或者使用ESP32的多个GPIO口分别监测不同房间。
- 设备固定与防水:将ESP32、继电器模块、电源等电路部分放入一个防水配电盒中,并固定在干燥、通风的位置。所有外部接线口用防水胶泥或密封圈处理。
5.2 软件烧录与初次配置
- 用USB线将ESP32连接至电脑。
- 在Arduino IDE中选择正确的开发板(如
ESP32 Dev Module)和端口。 - 将代码中的
ssid、password和botToken替换为你自己的信息。 - 点击上传。上传成功后,打开串口监视器(波特率115200),观察启动日志,确认Wi-Fi连接成功,并获取设备的IP地址。
5.3 功能测试流程
- 通信测试:在Telegram中找到你的Bot(通过
@你的Bot用户名),发送/start。你应该能收到欢迎信息。发送/status,检查返回的系统状态是否正常。 - 传感器测试:用湿手指或蘸水的棉签触碰漏水传感器的探针。观察串口监视器是否打印警报信息,同时Telegram是否收到警报消息。
- 手动控制测试:在Telegram中发送
/close_valve,应能听到继电器“咔嗒”一声吸合,电磁阀动作(可能有轻微振动和声音)。发送/open_valve,阀门应恢复。 - 自动联动测试:触发传感器,系统应自动发送警报并关闭电磁阀。等待设定的时间(如10秒)后,检查阀门是否按逻辑自动重新打开(或根据你的设计保持关闭)。
5.4 常见问题与排查技巧
| 问题现象 | 可能原因 | 排查步骤 |
|---|---|---|
| ESP32无法连接Wi-Fi | SSID/密码错误;信号太弱;路由器设置限制(如MAC过滤) | 检查代码中的凭据;将设备靠近路由器;检查路由器后台;查看串口日志。 |
| Telegram Bot无响应 | Bot Token错误;网络问题;chatId未正确设置 | 重新向@BotFather索取Token;确保ESP32能访问外网(可尝试Ping);先给Bot发条消息再测试。 |
| 漏水警报不触发 | 传感器接线错误;传感器类型(NO/NC)与代码逻辑不匹配;上拉电阻未启用 | 用万用表测量传感器输出端状态变化;尝试将代码中的LOW改为HIGH;确认使用了INPUT_PULLUP。 |
| 继电器吸合但电磁阀不动作 | 电磁阀供电不足或接反;继电器触点未正确串联在电路中;电磁阀本身故障 | 用万用表测量电磁阀两端电压;检查继电器COM和NO端接线;单独给电磁阀通电测试。 |
| 系统运行一段时间后重启 | 电源功率不足;代码中有内存泄漏或看门狗超时 | 使用电流表测试总功耗,确保电源有足够余量(建议2倍以上);检查循环中是否有长时间阻塞的代码(如delay过长)。 |
| 自动重新开阀逻辑不工作 | millis()溢出或逻辑判断有误 | millis()约50天溢出一次,对于本项目影响不大。检查valveCloseTime的赋值时机和比较逻辑。可添加串口打印调试。 |
实操心得:
- 电源是关键:电磁阀,尤其是DC12V/24V的,启动瞬间电流可能较大(是稳态电流的3-5倍)。务必选择一个额定电流足够(例如电磁阀稳态电流300mA,则电源至少选1.5A)的优质开关电源,并确保DC-DC降压模块也能提供ESP32和继电器所需的电流(约500mA)。电源不稳定是大多数奇怪故障的根源。
- 防误报设计:漏水传感器非常敏感,冷凝水、灰尘潮湿都可能误触发。可以在软件中加入“延时确认”逻辑,例如连续检测到水位超过2秒才判定为漏水,瞬间的触发则忽略。
- 增加本地冗余:虽然网络控制是核心,但增加一个本地蜂鸣器或LED作为辅助报警是很好的备份。万一网络中断,本地仍能发出声光警报。
- 定期自检:可以编写一个每周自动运行的测试例程,比如每周末自动关闭并打开阀门一次,同时发送测试消息,确保系统各个部分工作正常。
这个项目将开源硬件、物联网和即时通讯软件巧妙地结合在一起,实现了一个成本可控、功能强大的主动式家庭防水解决方案。它不仅仅是一个简单的报警器,更是一个具备远程干预能力的自动化系统。你可以在此基础上继续扩展,比如集成温湿度传感器判断冷凝水风险,添加流量计监测异常用水,甚至与其他智能家居平台(如Home Assistant)联动,打造更全面的家庭安全网络。动手搭建的过程,也是深入理解物联网系统从感知、联网到控制全流程的绝佳实践。