ESP32+MQTT改造除湿机:接入Home Assistant的IoT实战
说实话,把除湿机接入 IoT 网络这件事,我一直觉得比折腾智能音箱、智能灯值得多。你就想这么一个问题:南方回南天,你人在公司,家里湿度 85%,木地板、衣柜、钢琴、相机全都在默默吸水,而你完全不知道。等周末回去看到墙壁挂水珠、柜子发霉,再开除湿机已经晚了。普通除湿机只能靠面板上的按键和旋钮,出门在外就是个“睁眼瞎”,它连“今天抽了多少杯水”都不会告诉你。给除湿机加一个 IoT 大脑,成本不算高,解决的全是刚需问题:远程看温湿度、自动维持目标湿度、漏水报警、和新风系统联动。
这篇文章我会完整记录我从拆机、接线、写固件到接入 Home Assistant 的全过程。适合有一定动手能力、想把普通家电变智能的朋友参考。项目整体思路是用 ESP32 做边缘控制节点,用 SHT30 数字温湿度传感器做环境感知,用继电器模拟按键控制除湿机启停,再用 MQTT 接入本地智能家居平台。整个过程走下来,你会发现所谓 IoT 改造并不神秘,它其实就是“传感器采集 -> 边缘决策 -> 网络上报 -> 平台联动”这条链路的实体化。
1. 改造思路与方案选型:为什么是 ESP32 + MQTT
1.1 这台除湿机到底缺什么
先说我手头这台除湿机,压缩机式,标称每天 12L 除湿量,面板上就三个东西:一个电源键、一个湿度设定旋钮、一个水箱满指示灯。它的内部控制逻辑其实很简单:旋钮设定目标湿度,机器内部自带的湿度传感器检测到湿度高于设定值就启动压缩机,低于设定值就停机。这就是一个典型的本地闭环系统。
但问题恰恰出在这。第一,它的传感器在机器内部,检测的是机身周围局部湿度,不是房间真实平均湿度;第二,它没有数据记录能力,你根本不知道家里湿度是缓慢上升还是持续波动,等发现问题时往往已经晚了;第三,它没有网络,人不在家就无法主动开机除湿。所以我给这台机器定的改造目标很明确:
- 增加独立的数字温湿度传感器,把它放到回风口或房间代表点位,而不是只依赖机器内部那枚老式传感器。
- 将温湿度数据实时上传到本地 IoT 平台,并保存历史曲线。
- 把原机的“旋钮设定”逻辑升级成可远程配置、可联动场景的逻辑。
- 增加漏水检测、设备异常告警等保护功能。
这套改造做完,除湿机就从“被动响应机器”变成了“主动维护环境湿度”的 IoT 节点。
1.2 硬件选型:ESP32 几乎是唯一答案
如果你在网上搜“智能除湿机改装”,很多人会用 ESP8266 配 DHT22,我也这么干过,但后来全部升级成 ESP32 + SHT30。为什么?有四个很实际的理由:
- ESP32 双核 + 足够的 GPIO:除了跑传感器读取和 MQTT,还能同时处理本地控制逻辑、OTA、看门狗,不会出现资源不够导致卡顿。ESP8266 在同时开 WiFi、读传感器、处理控制逻辑时,偶尔会出现云台阻塞或断流问题。
- ESP32 原生支持蓝牙和 WiFi:虽然本项目只用 WiFi,但如果后续想接蓝牙温湿度计、或做配网二维码,底子就在这儿了。
- SHT30 是 I2C 数字接口:精度高、长期稳定性好、出厂校准。DHT22 虽然便宜,但靠单总线时序通信,对线长和干扰敏感,用半年后飘移明显。
- Arduino / ESPHome / PlatformIO 生态成熟:社区资料多,遇到问题一搜就能找到答案。
传感器方面,我最终选的是 SHT30。它的典型精度是 ±2% RH(相对湿度),温度精度 ±0.3°C,这个级别对除湿控制已经绰绰有余。如果你手头已经有 DHT22 也能用,但建议把它放在离主机稍远一点的地方,用短线连接,并且每隔一段时间用标准湿度计对比校准一下。
继电器模块我也踩过几个坑。普通 5V 继电器模块便宜,十几块钱,但要注意两点:一是上电瞬间 GPIO 默认电平是否会导致继电器误动作,二是触点容量够不够。压缩机启动瞬间电流远大于稳态电流,如果你改造的是大功率除湿机,最好选 16A 触点容量的继电器模块,或者再加一个交流接触器做中间级。我在这个项目里用的是一路 5V 继电器模块,光耦隔离,触点额定 10A/250VAC,对 12L 的机器够用。如果你要控制 20L 以上的大机器,强烈建议再加接触器,别省这个钱。
1.3 通讯方案:MQTT 而不是私有云
很多所谓“智能除湿机”出厂就带 Wi-Fi,但 App 体验做得一言难尽:第一个问题是数据走厂商私有云,断网就全瞎;第二个问题是 App 只给一个开关和定时功能,没有数据曲线,也不支持和 Home Assistant 这类本地平台联动;第三个问题是厂商一旦停止维护,设备就变成“电子垃圾”。所以我从一开始就决定:通讯协议用 MQTT,平台用 Home Assistant,所有核心逻辑走本地优先。
MQTT 的优势在于它是物联网行业的事实标准,基于发布/订阅模型,非常适合这种“传感器定时上报 + 控制命令下行”的场景。它的 QoS 机制可以保证消息不丢,retain 标志可以让新订阅的设备立刻拿到当前状态,遗嘱消息(Last Will and Testament)还能在设备掉线时通知平台。这些能力用 HTTP 轮询实现起来会很别扭。
在这个项目里,数据流是这样的:
SHT30 传感器 -> ESP32 本地逻辑 -> MQTT Broker(Home Assistant 自带 Mosquitto) | +-> Home Assistant 仪表盘 +-> Node-RED 自动化 +-> 手机 App 推送控制流是反过来的:手机/App -> MQTT Broker -> ESP32 -> 继电器 -> 除湿机电源键。
2. 硬件改装:从拆机到接线
2.1 拆机与定位关键检测点
动手前必须先断电,这点没有任何商量余地。除湿机内部有压缩机、启动电容、控制板,特别是启动电容在断电后可能还存着电,需要等几分钟或者用放电电阻放电后再碰。我拆开外壳后,第一件事是拍照记录内部结构,特别是控制板上的线序,方便后面复原。
拆开后要找到控制面板上面板按钮对应的接线位置。大多数除湿机面板上是轻触开关,一端接控制板的地/信号端,另一端接按键扫描端口。你不需要理解它的扫描原理,只要能找到“按下电源键时,哪两个焊点会导通”,就可以把继电器触点并联上去。用万用表二极管档或通断档,在按下按键时听蜂鸣,一般都能很快定位。
这里有一个非常关键的选择:建议采用“继电器并联按键触点”的方式,而不是直接控制总电源。直接控制总电源意味着压缩机、风扇、控制板会同时断电,下次上电时机器未必会自动开机,而且频繁通断总电源对压缩机寿命很不利。继电器并联按键触点,本质上等于“模拟人按了一下电源键”,机器自身的保护逻辑仍然在场,风险低很多。
2.2 硬件清单与接线方案
我最终的硬件清单如下:
| 器件 | 规格/型号 | 数量 | 作用 |
|---|---|---|---|
| ESP32 开发板 | ESP32 DevKitC 或 NodeMCU-32S | 1 | 主控,WiFi/MQTT/本地逻辑 |
| 温湿度传感器 | SHT30 模块(I2C 接口) | 1 | 采集环境温湿度 |
| 继电器模块 | 一路 5V 光耦隔离继电器,10A/250VAC | 1 | 模拟按键控制除湿机 |
| 水浸传感器 | 简易触点式漏水探头 | 1(可选) | 检测底部漏水 |
| DC-DC 降压模块 | 220V 转 5V 或使用 USB 适配器 | 1 | 为 ESP32 供电 |
| 杜邦线/硅胶线 | 若干 | - | 接线 |
| 绝缘盒/热缩管 | 若干 | - | 安全防护 |
接线示意图大概长这样:
SHT30模块 ESP32 继电器模块 VCC ---------------- 3.3V VCC ---------------- 5V GND ---------------- GND GND ---------------- GND SCL ---------------- GPIO 22 (I2C SCL) IN ---------------- GPIO 16 SDA ---------------- GPIO 21 (I2C SDA) 继电器触点NO/COM ---- 并联到除湿机电源键两端注意继电器模块的 VCC 我用的是 ESP32 的 5V 引脚,前提是你的 ESP32 开发板从 USB 或 5V 输入供电。如果继电器模块是 3.3V 版本,也可以直接用 3.3V,但驱动电流会略紧张,最好选带光耦的模块。
除湿机按键处接线时,把原来的轻触开关引脚用杜邦线或硅胶线引出来,并联到继电器模块的常开(NO)和公共(COM)端。这样继电器一吸合,等效于按键按下。常开接法是最安全的,平时不会影响原机按键功能。
2.3 安装位置与安全细节
传感器放哪里非常影响控制效果。我踩过的坑是:第一次把 SHT30 直接贴在除湿机出风口附近,结果读数低得离谱,因为出风口吹出来的是干燥空气,但不代表房间整体湿度已经降下来了。后来我把传感器移到了除湿机侧面回风口的旁边,离机身 5 到 10 厘米,并且不让它正对出风口或冷凝器。这个位置测到的是“即将被除湿机抽进去的空气”,和机器内部传感器相比,代表的是房间空气状态,控制逻辑更合理。
另外要注意 ESP32 和继电器模块不能裸奔。除湿机内部是水汽环境,一旦冷凝水流到控制板上,220V 和 3.3V 混在一起就不是闹着玩的了。我建议把 ESP32 和继电器模块放进一个绝缘密封盒,固定到远离冷凝器和排水槽的位置。如果条件允许,给电路板喷一层三防漆,能极大提高在潮湿环境下的可靠性。
供电方式也值得多说一句:不要直接从除湿机控制板上取 3.3V 或 5V 电源,因为压缩机启动时电压跌落很厉害。我用的是一路独立的 5V/2A USB 适配器,从外部供电给 ESP32 和继电器。这样既安全,又不会因为电源问题导致 WiFi 掉线或复位。
3. 固件设计与核心逻辑:除湿机的“大脑”怎么造
3.1 固件框架:数据采集、状态机与控制逻辑
如果你编程经验不多,可以直接用 ESPHome,配置 YAML 就能把传感器、继电器、MQTT 接入 Home Assistant。但如果你和我一样,想精细控制压缩机保护、异常阈值、回差逻辑,那还是写自定义固件更舒服。我用的是 PlatformIO + Arduino 框架,核心就三个模块:
- 数据采集模块:每 2 秒读一次 SHT30,做简单滤波后更新全局变量。
- 控制状态机:维护 Standby、Running、DelayStart、Alarm 四个状态,根据当前湿度、目标湿度、压缩机启动时间决定状态迁移。
- 通讯模块:连接 WiFi 和 MQTT,定时上报温湿度、设备状态,订阅控制主题。
核心代码骨架如下:
#include <WiFi.h> #include <PubSubClient.h> #include <Wire.h> #include "Adafruit_SHT31.h" Adafruit_SHT31 sht30 = Adafruit_SHT31(); WiFiClient espClient; PubSubClient mqtt(espClient); const char* mqttServer = "192.168.1.10"; // Home Assistant / Mosquitto 地址 const int relayPin = 16; // 目标湿度与回差值 float targetHum = 55.0; float hysteresis = 5.0; // 压缩机保护延时(毫秒) unsigned long compDelay = 300000; // 二次启动至少等 5 分钟 unsigned long lastCompStopTime = 0; bool compRunning = false; void setup() { pinMode(relayPin, OUTPUT); digitalWrite(relayPin, LOW); Wire.begin(21, 22); sht30.begin(0x44); connectWiFi(); connectMQTT(); } void loop() { mqtt.loop(); // 1. 读取传感器 float temp = sht30.readTemperature(); float hum = sht30.readHumidity(); // 2. 控制决策 if (hum > targetHum + hysteresis && !compRunning) { // 满足启动条件,且已经过了压缩机保护延时 if (millis() - lastCompStopTime > compDelay) { digitalWrite(relayPin, HIGH); // 模拟按键,开机 compRunning = true; delay(200); digitalWrite(relayPin, LOW); } } else if (hum < targetHum - hysteresis && compRunning) { digitalWrite(relayPin, HIGH); // 模拟按键,关机 compRunning = false; lastCompStopTime = millis(); delay(200); digitalWrite(relayPin, LOW); } // 3. 上报 MQTT publishSensorData(temp, hum); publishStatus(compRunning); delay(2000); }这段代码是简化版,实际项目里还加了看门狗、异常数据过滤、OTA 和配置管理。但核心思路就这么简单:读数据、做判断、控制继电器。
3.2 关键算法:湿度回差、压缩机保护与失败模式
除湿控制最忌讳的就是“湿度一到 55% 就停,一到 55.1% 就开”,这种频繁启停会让压缩机死得很快。所以要引入回差(hysteresis)机制。我这里的设置是:目标湿度 55%,回差 5%,那实际控制逻辑就是“湿度高于 60% 启动,低于 50% 停机”。中间的 10% 区间就是死区,避免设备在目标值附近反复横跳。
压缩机保护延时同样重要。压缩机从停止到再次启动,至少需要等 3 到 5 分钟,因为制冷系统高低压侧需要平衡,否则启动负载过大,容易闷机甚至烧毁。我在状态机里专门加了一个DelayStart状态:每次压缩机停机后记录时间戳,如果收到新的启动指令但还没到 5 分钟,就进入等待状态,直到延时结束才真正给继电器发脉冲。
传感器失效保护很多人会忽略。SHT30 如果接线松动或受到干扰,可能读到 NaN 或者明显离谱的数值(比如湿度 150%)。如果不加保护,设备会把错误数据当成“环境湿度”,做出错误决策。我加的逻辑是:连续 5 次读取失败,或者数值超出物理合理范围,就发布alarm/sensor_failure消息,并且立即停止压缩机,保持现状等待人工处理。
漏水检测我建议有条件一定要加。除湿机最大的隐患不是“抽不到水”,而是“水箱满/管路堵塞导致内部积水溢出”。我后来在水箱附近和机器底部各贴了一个触点式水浸探头,接到 ESP32 的某个 GPIO 上。一旦监测到漏水,第一时间发布告警并且强制关闭压缩机,避免二次事故。这类保护功能没有技术含量,但关键时刻能救命。
3.3 OTA 升级与配置管理
固件写完不可能一次就完美,OTA 升级几乎是刚需。我在固件里内置了 ArduinoOTA 库,并采用双分区(ota_data + app0 + app1)的方式,这样刷机失败后还能自动回滚。
OTA 这块的思路其实是借鉴了生产环境的用户策略:升级不能“一把梭”,得有个灰度过程。早期原型阶段,我每次改了代码都是直接串口刷,后来设备装到墙上,不想频繁拆机,才开始用 OTA。我的做法是:
- 新固件启动后立刻上报一条
status/boot消息,并在 60 秒内持续上报心跳。 - 如果 Home Assistant 侧没有收到新固件的心跳,判定启动失败,设备在下次重启时回滚到旧分区。
- MQTT 收到
command/ota/prepare后才允许发送升级包,防止误触。
当然,这是家庭项目,不需要真的搞什么复杂的灰度系统,但“失败回滚”这个意识一定要有。否则你远程更新固件,万一断电或者固件有 bug,设备就变砖了。生产级 IoT 平台的 OTA 用户策略本质上也是这套:分阶段推送、设备健康度确认、失败自动回滚。
配置管理方面,我用了 NVS(Non-Volatile Storage)保存目标湿度、回差、MQTT 服务器地址等参数。这样不需要重新编译固件就能改配置。最开始我用的是硬编码常量,每次调参都要重新刷机,后来改成 NVS 后省了太多事。
4. 云端接入与可视化:让除湿机“开口说话”
4.1 本地 MQTT Broker 与 Home Assistant 自动发现
我家的 IoT 中枢是 Home Assistant,它内置了 Mosquitto MQTT Broker 插件。设备端要做的只是连接 WiFi、连上 Broker、发布消息。主题设计我用了统一前缀,方便后续扩展:
home/dehumidifier/humidity home/dehumidifier/temperature home/dehumidifier/status/power home/dehumidifier/command/setpoint home/dehumidifier/alarm/waterHome Assistant 最方便的一点是支持 MQTT Discovery,设备端发一条特殊的配置消息,HA 就会自动创建对应的实体。比如温湿度传感器配置主题长这样:
homeassistant/sensor/dehumidifier_humidity/configpayload 是一个 JSON:
{ "name": "Dehumidifier Humidity", "state_topic": "home/dehumidifier/humidity", "unit_of_measurement": "%", "device_class": "humidity", "unique_id": "dehumidifier_humidity_01" }这条消息发出去,HA 侧就自动多了一个湿度传感器实体,非常解手。想在手机 App 上看数据,只要配好 HA 的远程访问,或者用灵动通知组件推送到手机就行。
4.2 Node-RED 做自动化策略:从命令到场景
数据接入 HA 之后,能做的事情就不只是“看个数字”了。我在 Node-RED 里搭了一套场景自动化,和手动控制逻辑互补:
- 当房间湿度高于 70% 且持续时间超过 10 分钟,自动开启除湿机,同时打开卫生间排气扇辅助排湿。
- 当湿度降到 50% 以下,自动关闭除湿机,并向手机推送一条摘要消息:“本次除湿持续 3 小时 20 分,从 75% 降至 48%。”
- 如果除湿机运行超过 6 小时但湿度下降不到 10%,大概率是机器故障或门窗一直开着,推送告警让我检查。
- 如果水浸传感器触发,HA 立刻推送紧急通知,并通过自动化关闭除湿机电源。
这些自动化用 Node-RED 的 MQTT 输入节点 + Function 节点就能搞定。如果你不想再装 Node-RED,直接用 HA 自带的自动化编辑器写 YAML 也可以,效果一样。重点是:设备端只负责稳定地采集和执行,真正的“智能场景”放在平台侧,这样改策略不用重新刷固件。
4.3 数据采集与远程告警:从“能看”到“能判断”
很多玩智能家居的朋友,做到“能看温度湿度曲线”就停了。但数据采集的真正价值在于通过趋势判断设备健康度。把 History 插件打开,保存一周的数据后,你会发现:
- 正常除湿机工作时,湿度曲线应该是一个接一个的“锯齿波”:工作段快速下降,停止段缓慢回升。
- 如果锯齿波消失,湿度一直平在高位,说明除湿机大概率没在好好工作。
- 如果湿度一天内剧烈波动,可能是有窗户没关、或门经常进出。
我在 Home Assistant 里定了几条告警规则,其中最实用的一条是“相对湿度持续偏高且设备多次尝试启动失败”。有一次,我因为水箱浮球卡住,机器一直报满水保护,我没注意,是这条告警提醒我检查的。
这套链路放到更大的场景里,其实就是物联网海量数据采集场景的缩小版:数据源产生数据 -> 边缘节点初步处理 -> 网关汇聚 -> 平台存储与规则引擎 -> 异常告警。家里虽然只有一台除湿机,但把这个链路跑通之后,再去做多设备、多传感器项目,思路就是现成的了。
5. 常见问题与排查实录
5.1 传感器数据异常或飘移
SHT30 用 I2C 接线,最常见的故障是读到的温度为负数、湿度为 0 或数据频繁跳变。我在调试过程中遇到过三次:第一次是杜邦线接触不良,用手一碰传感器读数就乱跳,后来把杜邦线换成焊接的硅胶线才稳定;第二次是 I2C 地址冲突,SHT30 模块默认地址是 0x44,有些模块把地址引脚拉高变成 0x45,如果你同时挂了两个设备就要注意;第三次是线太长,超过 30 厘米后信号完整性变差,我把传感器放到离 ESP32 较远的位置后加了一个 4.7kΩ 上拉电阻才解决。
建议你在正式固定传感器前先用 MQTT Explorer 观察半小时数据,确认曲线平稳再封装。如果读数跳变超过 ±5% RH,大概率是接线或供电问题,不要着急用软件滤波掩盖,先把硬件问题解决。
5.2 继电器不吸合或一直吸合
这个问题的根源通常不在继电器本身,而在单片机上电瞬间的 GPIO 默认电平。ESP32 的 GPIO 在复位期间可能是高阻态或高电平,如果继电器控制引脚没有做下拉,模块可能会在上电瞬间吸合一下,导致除湿机无故开机。解决方法是:在继电器 IN 引脚和 GND 之间加一个 10kΩ 下拉电阻,并在固件初始化时先把引脚设为 LOW,再延迟 1 秒后进入正常逻辑。
另外,不要用 ESP32 的 3.3V GPIO 直接驱动没有光耦的继电器模块,驱动能力不足会产生“能亮但吸合不了”的怪问题。选带光耦隔离的模块,或者加一级 NPN 三极管/ULN2003 放大驱动。
5.3 OTA 刷机失败与回滚
家庭环境 OTA 失败的主要原因是 WiFi 信号弱、电源供电不稳、或者刷机过程中断电。我的教训是:不要以为 OTA 成功就万事大吉,必须在固件里预留串口刷机引导。如果设备变砖,最简单的方式是按住 ESP32 的 BOOT 键,用串口工具重新刷一次引导程序。我在固件里把这个模式做成了“5 秒内连续按 3 次按键进入串口恢复模式”,这样哪怕设备网络配置全乱,也能通过串口救回来。
5.4 断网后设备“失智”?
我最开始的设计里,控制逻辑完全依赖 MQTT 指令,结果有一次路由器重启,设备连着 WiFi 但连不上 MQTT,就在那一动不动,除湿机也不自动开。后来我把本地控制逻辑放回了 ESP32 固件里:就算 MQTT 断线,设备仍然依照本地目标湿度和回差自动运行,只是不再上报数据。这其实是 IoT 项目里必须想清楚的一点:平台是辅助决策层,设备本身必须有本地独立的保底逻辑。把自动化全部放在云端看似方便,但网络抖动一次就全盘崩掉。
如果后续你做的设备需要本地逻辑和云端指令同时存在,还要定义清楚“谁优先级更高”。我的策略是:云端指令可以立即改变设备目标状态,但如果云端失联超过 2 分钟,设备恢复本地自动模式。
6. 进一步的扩展和折腾方向
6.1 从单机改造到多房间集控
一台除湿机改完,你会发现思路很快就想复制到第二台、第三台。如果有楼上楼下多个房间,可以在每台除湿机里部署同样的 ESP32 方案,只要给每台设备一个唯一 ID,配置不同的主题前缀,Home Assistant 里就能自动生成多个实体。我后来在地下室、衣帽间各加了一台,用 HA 的 group 把两个湿度传感器和两台除湿机绑定到一张卡片上,一眼就能看到整个房子的除湿状态。
多设备部署后,有一点要注意:每个设备必须设置独立的 MQTT client_id,否则 Broker 会互相踢下线。这也是批量部署设备时最容易踩的坑。
6.2 小型项目里学到的生产级 IoT 经验
虽然这只是一台几百块钱的除湿机改造,但整个项目的架构和一个正经的物联网产品已经很像了。设备端要考虑:传感器选型、数据采集频率、本地决策与保护、异常上报、OTA 升级与回滚、设备唯一标识、配置可修改。平台端要考虑:设备发现、状态存储、可视化和告警、远程控制。和那些处理海量数据采集场景的生产系统相比,差的只是规模,不是逻辑。
如果你打算做更正式的 IoT 产品,我建议在文档里固定好这套主题规范、消息格式、设备状态枚举,然后用模拟器跑一遍端到端流程。这些细节在家庭项目里看不出差别,但一旦数量上到几十台设备,就是灾难和救命的区别了。
6.3 折腾完这台除湿机,我学到的真正东西
改造完这台除湿机后,我最大的感受不是“我多了个远程开关”,而是“我终于知道房间湿度到底在发生什么”。你以为家里发潮是下雨天的事,打开历史曲线才发现,阴天、多云、甚至冬天暖气的日子,湿度都可能偷偷爬到 70% 以上。没有数据,你永远靠猜;有了 IoT 化改造,你靠的是证据。
另一个收获是:这套方案的每一个环节都可以迁移。传感器换成空气质量传感器,继电器换成智能插座,MQTT 主题从除湿机换成新风机,整个架构几乎不用大改。这大概就是物联网项目最有意思的地方——你第一眼看只是在改造一台家电,但其实是在搭一个通用的感知与控制底座。
