1. 项目缘起:为什么我要把九块电表塞进一块面板
家里装修的时候脑子一热,把整栋房子的用电分成了九个回路:照明、普通插座、厨房、空调、热水器、地暖、车库、庭院、还有一路备用。当时想的是"分路计量,精细管理",结果住进去才发现,九块电表分散在配电箱里,想看数据得拿手电筒一个个照,更别提做统计和自动化了。这种"数据孤岛"的状态持续了大半年,直到我把 Modbus 电表、ESP32 和 Home Assistant 串成了一条线,才算真正把九路电表的数据统一收进一块面板里。
这篇内容就是把这套方案的完整落地过程拆开讲清楚。核心关键词是Modbus、Home Assistant、ESP32、ESPHome、RS-485,涉及的是工业通讯协议和智能家居平台的结合。适合谁看?如果你家里或项目上有多个 Modbus 电表、温控器、变频器之类的 RS-485 设备,想统一接入 Home Assistant 做监控和自动化,那这套思路可以直接抄。如果你只是想接一块电表,也能从里面挑出有用的部分。哪怕你完全没碰过 Modbus,我也会把协议原理、接线、配置、排查都讲透,保证你能跟着做下来。
先说清楚这套方案能解决什么问题:把九路独立的 Modbus RTU 电表,通过一条 RS-485 总线接到一块 ESP32 上,由 ESPHome 固件轮询采集,再通过 Wi-Fi 上报给 Home Assistant,最终在一块面板上展示九路电压、电流、功率、电量等数据,并能基于这些数据做自动化。整套硬件成本大概两三百块,比买九块带联网功能的智能电表便宜太多,而且数据完全本地化,不依赖任何云服务。
2. 整体架构设计与选型思路
2.1 为什么是 Modbus + RS-485 这条路线
先说协议选型。市面上家用电表常见的通讯方式有几种:Wi-Fi 直连、Zigbee、RS-485 走 Modbus、还有脉冲输出。Wi-Fi 电表每块都要占一个 IP,九块就是九个设备,路由器压力大,而且很多便宜货固件封闭,数据拿不出来。Zigbee 电表生态窄,采样频率低,做功率监控不够用。脉冲电表只能算电量,拿不到实时功率和电压电流,做不了精细分析。
Modbus RTU over RS-485 是工业现场用了三十多年的老方案,稳定、抗干扰、成本低,而且几乎所有正经电表都支持。它的核心优势是总线式拓扑:一条双绞线可以挂几十个从站设备,每个设备一个地址,主站轮询。九块电表挂在一条总线上,只需要 ESP32 的一个串口,接线极其简洁。这就是我选它的根本原因——不是因为它新,而是因为它足够成熟可靠,出了问题有大量现成经验可以查。
提示:Modbus 有 RTU、ASCII、TCP 三种常见形态。RS-485 物理层上跑的是 RTU 或 ASCII,家用场景几乎全是 RTU。TCP 是跑在以太网上的,本文不涉及。
2.2 ESP32 在这里扮演什么角色
ESP32 是整个方案的大脑,它要做三件事:第一,通过 UART 转 RS-485 收发 Modbus 报文;第二,解析报文提取寄存器数据;第三,通过 Wi-Fi 把数据推给 Home Assistant。选 ESP32 而不是树莓派或者工控机,理由很实际:便宜(十几块一片)、功耗低、自带 Wi-Fi、有多个硬件串口、ESPHome 生态成熟。用 ESPHome 写配置,不用写一行 C++ 代码就能实现 Modbus 主站轮询,这是它最大的价值。
这里有个关键点要提前说:ESP32 的 UART 是 TTL 电平(0-3.3V),而 RS-485 是差分信号,两者之间必须加一个RS-485 收发器模块,常见的是 MAX485、SP3485 或者带隔离的 ADM2483。我一开始图便宜用了裸 MAX485,结果空调启动时干扰导致通讯频繁丢包,后来换成带光耦隔离的模块才稳定。这个坑后面会详细讲。
2.3 九路电表如何共用一条总线
九块电表并联在 A、B 两根差分线上,每块电表设置不同的从站地址(比如 1 到 9),波特率统一(我用 9600),校验位统一(8N1 或 8E1,看电表支持)。ESP32 作为主站,按地址逐个轮询。这里有个容易被忽略的点:总线两端要接 120 欧姆终端电阻,否则信号反射会导致通讯错误,尤其是线缆较长的时候。我家的总线拉了大概 15 米,两端各焊了一个 120 欧电阻,丢包率从 5% 降到几乎为零。
拓扑上建议用手拉手菊花链,不要用星型。星型分支会产生信号反射,短距离可能没事,长了必出问题。如果实在要分支,分支长度控制在 1 米以内。这是 RS-485 布线的铁律,很多人栽在这里。
2.4 数据流与刷新策略
九块电表,每块要读电压、电流、功率、电量等大概 6 到 8 个寄存器。如果每块都全量读,一轮下来报文数量不少。Modbus RTU 在 9600 波特率下,一个读 8 个寄存器的请求加响应大概 20 毫秒左右,九块就是 200 毫秒,加上间隔,一轮 500 毫秒到 1 秒比较合理。我实测下来,把轮询间隔设成 1 秒,Home Assistant 里的数据刷新足够流畅,也不会给总线太大压力。
这里要权衡的是刷新频率和总线负载。如果你追求秒级实时功率曲线,可以把间隔压到 500 毫秒,但要确保电表响应够快。如果只是看电量统计,5 秒甚至 10 秒一次都行。我的做法是:功率、电流这类实时量 1 秒一次,电量这类累积量 10 秒一次,分开轮询,减轻总线负担。
3. 硬件准备与接线实操
3.1 物料清单与选型要点
先把要买的东西列清楚,避免你走弯路。
| 物料 | 型号建议 | 数量 | 备注 |
|---|---|---|---|
| 主控 | ESP32 DevKitC 或 NodeMCU-32S | 1 | 要选带多个 UART 的版本 |
| RS-485 收发器 | 带光耦隔离的模块(如 ADM2483 方案) | 1 | 别用裸 MAX485 |
| Modbus 电表 | 支持 Modbus RTU 的导轨式电表 | 9 | 确认寄存器表 |
| 双绞线 | 屏蔽双绞线,0.5mm² 以上 | 按需 | 屏蔽层单端接地 |
| 终端电阻 | 120 欧 1/4W | 2 | 总线两端各一个 |
| 电源 | 5V 2A 适配器 | 1 | 给 ESP32 和收发器供电 |
电表选型是重点。我用的是一款常见的单相导轨电表,支持 Modbus RTU,寄存器表里 0x0000 是电压、0x0001 是电流、0x0003 是有功功率、0x0010 开始是电量。不同厂家寄存器地址不一样,买之前一定要拿到寄存器表,否则后面配置无从下手。有些电表还支持 04 功能码读输入寄存器,有些只支持 03 保持寄存器,这个也要确认。
3.2 RS-485 接线:A/B 线千万别接反
接线是整个项目最容易出错的地方。RS-485 有两根信号线,通常标 A 和 B,或者 D+ 和 D-。所有设备的 A 接 A,B 接 B,不能交叉。但坑在于:不同厂家对 A/B 的定义可能相反。有的厂家 A 是正、B 是负,有的反过来。如果你接完发现通讯不上,第一件事就是把 A/B 对调试试。
ESP32 到收发器模块的接线:
- ESP32 的 GPIO17(TX)接收发器的 DI(数据输入)
- ESP32 的 GPIO16(RX)接收发器的 RO(数据输出)
- ESP32 的 GPIO4 接收发器的 DE 和 RE(收发使能,两个脚短接)
- 收发器的 A、B 接总线
- 收发器 VCC 接 3.3V 或 5V(看模块),GND 共地
注意:DE 和 RE 是方向控制脚。发送时 DE 拉高,接收时拉低。ESPHome 的 Modbus 组件会自动控制这个脚,你只要在配置里指定 flow_control_pin 就行。如果模块没有自动方向控制,就得靠这个脚手动切换。
电表侧的接线:每块电表的 A、B 并联到总线,电源单独供 220V。九块电表的 A 全部拧在一起,B 全部拧在一起,然后接到收发器。屏蔽双绞线的屏蔽层只在 ESP32 这一端接地,另一端悬空,避免地环路干扰。
3.3 供电与隔离:别让干扰毁掉一切
供电这块我踩过坑。一开始 ESP32 和收发器共用电脑 USB 供电,结果电表一多,通讯就开始丢包。后来换成独立的 5V 2A 适配器,问题缓解。再后来发现空调启动瞬间还是会丢包,最终上了带光耦隔离的收发器模块,才算彻底稳定。
隔离为什么重要?因为 RS-485 总线上的电表和 ESP32 可能处于不同的"地"电位,尤其是电表接的是 220V 市电,地线上可能有各种干扰。光耦隔离把两边的电气连接断开,只传递信号,能有效阻断共模干扰和地环路。多花二三十块,省下无数排查时间,这笔账很划算。
另外,ESP32 的电源最好加一个 100uF 电解电容和 0.1uF 陶瓷电容做滤波,抑制电源纹波。这个细节很多人忽略,但在多设备总线上很关键。
4. ESPHome 配置与 Modbus 轮询实现
4.1 ESPHome 基础配置框架
ESPHome 的配置文件是 YAML 格式,结构清晰。先搭好基础框架:
esphome: name: modbus-meter-hub platform: ESP32 board: nodemcu-32s wifi: ssid: "你的WiFi名" password: "你的WiFi密码" logger: level: DEBUG api: encryption: key: "你的API密钥" ota: password: "你的OTA密码" uart: id: mod_uart tx_pin: GPIO17 rx_pin: GPIO16 baud_rate: 9600 parity: NONE stop_bits: 1 modbus: id: mod_bus uart_id: mod_uart flow_control_pin: GPIO4这段配置里,uart定义了串口参数,必须和电表一致。modbus组件挂在这个串口上,flow_control_pin指定方向控制脚。logger设成 DEBUG 方便排查,稳定后可以改成 INFO 减少日志量。
4.2 九路电表的 Modbus 从站定义
接下来为每块电表定义一个 modbus_controller。假设九块电表地址是 1 到 9:
modbus_controller: - id: meter1 address: 1 modbus_id: mod_bus update_interval: 1s - id: meter2 address: 2 modbus_id: mod_bus update_interval: 1s # ... 依次到 meter9update_interval控制轮询频率。九块都设 1 秒的话,总线会比较忙。我的做法是前几块设 1 秒,后几块设 2 秒,错开负载。ESPHome 会按顺序轮询,不会同时发请求,所以不会冲突。
4.3 寄存器映射与传感器定义
这是配置的核心。以第一块电表为例,定义电压、电流、功率、电量四个传感器:
sensor: - platform: modbus_controller modbus_controller_id: meter1 name: "Meter1 Voltage" address: 0x0000 register_type: holding value_type: U_WORD unit_of_measurement: "V" accuracy_decimals: 1 filters: - multiply: 0.1 - platform: modbus_controller modbus_controller_id: meter1 name: "Meter1 Current" address: 0x0001 register_type: holding value_type: U_WORD unit_of_measurement: "A" accuracy_decimals: 2 filters: - multiply: 0.01 - platform: modbus_controller modbus_controller_id: meter1 name: "Meter1 Power" address: 0x0003 register_type: holding value_type: U_WORD unit_of_measurement: "W" accuracy_decimals: 0 - platform: modbus_controller modbus_controller_id: meter1 name: "Meter1 Energy" address: 0x0010 register_type: holding value_type: U_DWORD unit_of_measurement: "kWh" accuracy_decimals: 2 filters: - multiply: 0.01这里有几个关键参数要解释。register_type指定读哪种寄存器,holding 对应 03 功能码,input 对应 04 功能码。value_type指定数据类型,U_WORD 是 16 位无符号,U_DWORD 是 32 位无符号(占两个寄存器)。filters里的 multiply 是缩放系数,因为电表返回的是整数,需要乘以系数才是真实值。比如电压返回 2201,乘以 0.1 就是 220.1V。
注意:缩放系数一定要查寄存器表确认。不同电表系数不同,有的电压是 0.1,有的是 0.01。搞错了数据就完全不对。我一开始没注意,功率显示成实际值的 10 倍,排查了半天。
4.4 用模板传感器做汇总计算
九路数据进来后,我做了几个汇总传感器:总功率、总电量、最大单路功率。用 template sensor 实现:
sensor: - platform: template name: "Total Power" unit_of_measurement: "W" lambda: |- return id(meter1_power).state + id(meter2_power).state + id(meter3_power).state + id(meter4_power).state + id(meter5_power).state + id(meter6_power).state + id(meter7_power).state + id(meter8_power).state + id(meter9_power).state; update_interval: 2s这样在 Home Assistant 里就能看到全屋总功率,配合电价还能算实时电费。lambda 里引用传感器需要给每个传感器加 id,这个别忘了。
5. Home Assistant 面板与自动化联动
5.1 数据接入与实体命名
ESPHome 设备通过 API 接入 Home Assistant 后,所有传感器会自动变成实体。命名建议用统一的规则,比如sensor.meter1_power、sensor.meter2_power,方便后续做面板和自动化。如果 ESPHome 里的 name 是 "Meter1 Power",HA 里会自动转成sensor.meter1_power。
接入后第一件事是检查数据是否正常。打开开发者工具,看每个实体的状态和单位。如果某个实体显示 unavailable,说明那块电表通讯有问题,回到 ESPHome 日志里看报错。
5.2 打造九路计量面板
Home Assistant 的面板我用的是 Lovelace 的 entities 卡片加 gauge 卡片组合。九路电表每路一个 gauge 显示实时功率,下面一个 entities 列表显示电压电流电量。布局上按房间分组,比如厨房、空调、照明各一组,视觉上更直观。
如果想更炫一点,可以用 mini-graph-card 画功率曲线,或者用 apexcharts-card 做堆叠柱状图看各回路占比。我个人的习惯是:主面板放总览(总功率、总电量、各回路占比饼图),子面板放每路的详细数据。这样一眼能看出哪个回路在耗电。
5.3 基于计量的自动化场景
数据进来后,自动化才是真正发挥价值的地方。我做了几个实用的:
- 功率超限告警:某路功率超过阈值(比如空调超过 3000W)持续 1 分钟,推送通知,提醒可能有问题。
- 待机功耗监测:夜间某路功率持续低于 5W 超过 2 小时,判断为待机,可以自动断电。
- 电量统计报表:每天凌晨统计各路电量,生成日报,通过通知发送。
- 异常检测:某路电流突然归零但电压正常,可能是设备故障或跳闸,立即告警。
这些自动化的核心是用对触发条件和持续时间。比如功率告警,如果不用for: minutes: 1,空调启动瞬间的冲击电流会误触发。这个细节很关键。
6. 常见问题与排查技巧实录
6.1 通讯丢包与超时排查
这是最常见的问题。表现是 ESPHome 日志里频繁出现 "Modbus timeout" 或 "CRC error"。排查顺序如下:
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 全部电表都超时 | A/B 接反、波特率不对 | 对调 A/B,核对波特率校验位 |
| 部分电表超时 | 地址冲突、该表故障 | 检查地址设置,单独测试该表 |
| 偶发 CRC 错误 | 干扰、终端电阻缺失 | 加终端电阻,换屏蔽线,加隔离 |
| 数据乱跳 | 缩放系数错、寄存器地址错 | 核对寄存器表 |
我的经验是:先排除接线,再排除参数,最后怀疑干扰。80% 的问题出在前两步。有一次我折腾了一晚上,最后发现是某块电表的地址被出厂设置成了 1,和另一块冲突了。
6.2 数据不准的几种典型情况
数据不准比通讯不上更隐蔽。常见的有:
- 数值放大或缩小整数倍:缩放系数错。查寄存器表确认。
- 电量不累积或跳变:32 位数据高低字顺序反了。试试 value_type 换成 U_DWORD 或 U_DWORD_R(字节序反转)。
- 功率为负:电表接线极性反了,或者电表本身支持双向计量。
- 电压电流正常但功率不对:功率因数问题,或者读错了寄存器。
提示:调试时可以用 Modbus Poll 这类上位机工具先单独连一块电表,确认寄存器和系数,再写进 ESPHome。这样能排除 ESPHome 配置的干扰,快速定位问题。
6.3 ESP32 与 RS-485 配合的避坑清单
最后整理一份避坑清单,都是我真金白银换来的:
- 别用裸 MAX485,干扰环境下必翻车,直接上隔离模块。
- 终端电阻不能省,尤其是线长超过 5 米。
- A/B 线用双绞,不要用普通平行线,抗干扰差很多。
- 屏蔽层单端接地,两端都接会形成地环路。
- ESP32 电源要干净,加滤波电容,别和电机类设备共用插座。
- 轮询间隔别太激进,9600 波特率下 1 秒一轮九块表已经接近极限。
- DE/RE 控制脚别接错,接错了根本发不出数据。
- 电表地址别重复,出厂默认可能都是 1。
- 寄存器表必须拿到,没有它寸步难行。
- 先单表调通再上总线,一次性接九块出问题很难定位。
这套方案跑了大半年,九路电表数据稳定,Home Assistant 面板一目了然,自动化也帮我省了不少电费。最深的体会是:Modbus 这东西看着老土,但在多设备计量场景下,它的稳定性和成本优势是其他方案比不了的。ESP32 加 ESPHome 的组合,把工业协议和消费级智能家居之间的门槛降到了最低,不用写代码就能搞定。如果你也在纠结怎么把一堆 RS-485 设备接进 Home Assistant,希望这篇能帮你少走点弯路。