1. 九路电表一块屏这个项目到底在解决什么问题家里装了光伏或者租了个小厂房分表计费再或者像我一样纯粹爱折腾早晚会碰到同一个需求手头有好几路电想在一个界面上同时看到每一路的电压、电流、功率、累计电量。市面上的成品多路电表要么贵得离谱要么数据锁在厂商云平台里想接进自己的自动化系统处处受限。这个项目的思路就很直接——用九台支持 Modbus 的导轨式电表通过 RS-485 总线串起来由一块 ESP32 跑 ESPHome 做协议转换最终在 Home Assistant 里呈现为一个统一的能源面板。关键词里出现的 Modbus、Home Assistant、ESP32、ESPHome、RS-485基本就是这个方案的五大支柱。Modbus 是工业现场用了四十多年的通信协议简单、开放、生态成熟RS-485 是它的物理层载体一条双绞线能挂几十个从站抗干扰能力在低压场景里足够用ESP32 负责把串口上的 Modbus 帧翻译成 Wi-Fi 上的数据ESPHome 把翻译逻辑封装成声明式配置不用写一行 C 代码Home Assistant 则是最终的展示和自动化中枢。适合谁来参考这篇内容如果你已经有一点点 Home Assistant 使用经验会改 configuration.yaml知道什么是实体和仪表盘那读起来会很顺。如果你完全没接触过 Modbus也没关系我会把协议帧、寄存器、CRC 这些概念用生活化的方式讲清楚。真正需要提前准备的是硬件层面的东西电表、USB 转 RS-485 调试工具、终端电阻、屏蔽双绞线以及一块带串口的 ESP32 开发板。我自己的场景是九路负载市电总进、光伏逆变器输出、储能充放电、空调专线、厨房专线、车库充电桩、照明回路、插座回路、备用回路。九台电表挂在同一条总线上地址从 1 排到 9。整套东西跑了大半年中间踩过的坑足够写一篇避坑指南所以这篇不只是配置教程更多是把那些文档里不会写的细节摊开讲。2. 先搞懂 Modbus 在电表上到底怎么跑2.1 从站地址、功能码、寄存器三个必须刻进脑子里的概念Modbus 的本质是主问从答。总线上只有一个主站这里是 ESP32其余全是从站九台电表。主站发一帧请求帧里带着目标从站地址只有地址匹配的那台电表会回应其他电表装聋作哑。这就是为什么一条总线能挂九台甚至更多设备——靠地址区分不靠物理线路区分。从站地址在电表出厂时通常是 1需要通过面板按键或厂商软件改成唯一值。我见过有人偷懒九台全留默认地址 1结果总线上九台同时应答数据帧互相碰撞读回来的数值乱七八糟。这个坑后面还会细说。功能码决定读什么类型的寄存器。电表场景里最常用的是这几个功能码名称用途电表里的典型数据0x03读保持寄存器读可读写的配置和测量值电压、电流、功率、电量0x04读输入寄存器读只读的测量值部分电表把实时量放这里0x06写单个寄存器改配置修改从站地址、波特率0x10写多个寄存器批量改配置批量设置参数寄存器是 Modbus 的数据容器每个寄存器 16 位。但电表里的电压、功率这些量往往超过 16 位能表示的范围所以厂商会把一个 32 位浮点数拆成两个连续的寄存器这就是所谓的双寄存器或32 位数据。读取时功能码写 0x03起始地址写低字或高字数量写 2拿回来两个 16 位值再拼成一个浮点数。拼的顺序高字在前还是低字在前因厂商而异这是调试阶段最容易卡住的地方。2.2 RS-485 物理层为什么一条线能挂九台表RS-485 用差分信号传输A、B 两根线之间的电压差表示逻辑 0 和 1。差分的好处是共模干扰会被接收端抵消掉所以几十米甚至上百米的距离都能稳定通信。九台电表并联在 A、B 两根线上每台表的 A 接总线 AB 接总线 B不能交叉。总线两端需要各接一个 120 欧姆的终端电阻作用是吸收信号反射。九台表挂在中间两端分别是 ESP32 侧和最后一台电表侧。如果距离很短比如都在一个配电箱里总线不到两米终端电阻不接往往也能跑但一旦通信距离拉长或者波特率提高不接终端电阻就会出现间歇性丢包。我自己的配电箱里总线大概三米一开始没接终端电阻9600 波特率下偶尔丢一帧接上之后彻底稳定。屏蔽双绞线是首选。屏蔽层单端接地接在配电箱的接地排上不要两端都接否则会形成地环路反而引入干扰。A、B 线尽量远离变频器输出线和大电流动力线如果必须交叉走直角交叉而不是平行走线。2.3 波特率、校验位、停止位三件套必须和电表一致串口通信的三件套是波特率、数据位、校验位、停止位Modbus RTU 里数据位固定 8 位所以实际要匹配的是波特率、校验位、停止位。电表出厂常见配置是 9600、无校验、1 位停止位写作 9600 8N1。也有电表默认 9600 8E1偶校验。ESP32 侧的配置必须和电表完全一致差一个校验位就是全部读不到数据。九台电表如果来自不同批次出厂配置可能不一样务必逐台确认。我遇到过一台表是 8E1其他八台是 8N1结果那一台怎么都读不出来查了半天才发现是校验位的问题。波特率的选择上9600 是最稳的九台表轮询一遍大概一两秒。如果想更快可以上 19200 或 38400但前提是总线质量够好、终端电阻到位。我实测 19200 在九台表场景下轮询周期能压到一秒以内对能源监控来说完全够用没必要追求更高。3. ESP32 侧ESPHome 配置的完整拆解3.1 硬件接线ESP32 的哪个串口给 RS-485ESP32 一般有三个硬件串口UART0 被 USB 调试占用UART1 和 UART2 可以自由映射到任意 GPIO。我习惯用 UART1TX 接 GPIO17RX 接 GPIO16这两个脚在大多数 ESP32 开发板上都引出来了。RS-485 是半双工收发共用一对差分线所以需要一个收发方向控制引脚。常见的 MAX485 模块有一个 DE/RE 引脚高电平发送、低电平接收。ESP32 用一个 GPIO 控制它ESPHome 的 modbus 组件里通过 flow_control_pin 指定。我用的 GPIO4。接线顺序是这样的ESP32 的 GPIO17TX接 MAX485 模块的 DIGPIO16RX接 ROGPIO4 接 DE 和 RE这两个脚通常短接在一起。MAX485 的 A、B 接总线VCC 接 3.3V 或 5V看模块规格GND 和 ESP32 共地。注意MAX485 模块有的是 5V 供电但逻辑电平是 5V直接接 ESP32 的 3.3V GPIO 可能损坏芯片。买模块时认准 3.3V 兼容的型号或者在 TX/RX 线上加电平转换。我一开始用了个老模块ESP32 的 RX 脚被 5V 拉高了几个月居然没坏但这是运气不要学。3.2 ESPHome 的 modbus_controller一个从站一个控制器ESPHome 从 2021 年开始提供 modbus_controller 组件专门用来管理 Modbus 从站。它的设计逻辑是一个 modbus_controller 对应一个从站地址下面挂若干 sensor每个 sensor 对应一个寄存器地址。九台电表就是九个 modbus_controller每个的 address 不同但共用同一个 uart 和 flow_control_pin。配置结构大概是这样uart: id: mod_bus tx_pin: GPIO17 rx_pin: GPIO16 baud_rate: 9600 stop_bits: 1 parity: NONE modbus: id: modbus1 uart_id: mod_bus flow_control_pin: GPIO4 modbus_controller: - id: meter1 address: 1 modbus_id: modbus1 setup_priority: -10 update_interval: 5s - id: meter2 address: 2 modbus_id: modbus1 setup_priority: -10 update_interval: 5s # ... 一直到 meter9update_interval 是轮询周期。九台表如果都设 5 秒ESP32 会依次轮询实际每台表的刷新间隔是 45 秒。如果想让总进线表刷新快一点可以给它单独设 2 秒其他表设 10 秒这样总进线数据更实时支路数据慢一点无所谓。setup_priority 设为 -10 是为了让 modbus 组件在传感器之前初始化这个值在 ESPHome 文档里有说明照抄即可。3.3 寄存器映射把电表的说明书翻译成 YAML每台电表的寄存器地址表在厂商说明书里通常是一张表格列出参数名称、寄存器地址、数据类型、单位、读写属性。以我用的某款单相导轨表为例参数寄存器地址数据类型单位电压0x000016 位无符号0.1V电流0x000116 位无符号0.01A有功功率0x000216 位有符号1W累计电量0x000432 位无符号0.01kWh电压寄存器地址 0x0000读回来是 2301乘以 0.1 就是 230.1V。ESPHome 里这样写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累计电量是 32 位value_type 写 U_DWORDESPHome 会自动读两个连续寄存器并拼成 32 位值。但拼的顺序有讲究如果读回来数值明显不对比如应该是 1234.56 却读成 3.4e38就要换 value_type 为 U_DWORD_RR 表示寄存器顺序反转。提示调试阶段不要一上来就配九台表。先配一台确认电压、电流、功率、电量四个值都对得上再复制八份改地址。我见过有人一口气配完九台结果全读不出来排查起来要一台台试反而更慢。4. 九台表轮询的坑从地址冲突到 CRC 超时4.1 地址冲突九台表同时应答的灾难现场前面提过电表出厂地址都是 1。如果你不逐台改地址就挂上总线主站发地址 1 的请求九台表同时应答RS-485 是差分总线多个设备同时驱动总线会导致信号电平混乱主站收到的是乱码CRC 校验必然失败。改地址的方法有两种。一种是用电表面板按键进菜单改适合只有一两台的情况。另一种是用 Modbus 调试工具通过串口发写寄存器命令批量改适合九台这种规模。我用的方法是每次只接一台表到 USB 转 RS-485 工具上用调试软件把地址改成目标值改完拔下来接下一台。九台表大概花二十分钟比在总线上排查冲突快得多。改完地址后建议在电表外壳上贴个标签写明地址和用途配电箱里九台表长得一模一样不贴标签后期维护会疯。4.2 CRC 校验失败先查线再查参数最后查干扰Modbus RTU 每帧末尾有两个字节的 CRC 校验主站收到数据后会重新计算 CRC 并比对不一致就丢弃。ESPHome 的日志里会打印 CRC error 或 Modbus error看到这个不要慌按顺序排查。第一步查接线。A、B 有没有接反这是最常见的。RS-485 的 A、B 定义在不同厂商那里有时是反的有的标 A、B-有的标 D、D-。如果接反了通信完全不通日志里全是超时。判断方法很简单把 A、B 对调一下试试如果通了就是接反了。第二步查参数。波特率、校验位、停止位、从站地址四个参数逐一核对。特别是校验位8N1 和 8E1 在日志上的表现都是 CRC 错误很难区分。我的做法是拿一台表单独接调试工具用调试软件扫一遍常见参数组合确认电表的真实配置。第三步查干扰。如果接线和参数都对但偶尔丢帧那就是干扰问题。检查屏蔽层是否单端接地总线是否远离动力线终端电阻是否接好。我遇到过一次九台表里有一台总是丢帧最后发现是那台表的 RS-485 接线端子螺丝没拧紧接触电阻过大。拧紧之后问题消失。4.3 轮询超时九台表的时间预算怎么算Modbus RTU 一帧请求加一帧应答在 9600 波特率下大概 20 到 30 毫秒。九台表轮询一遍加上主站处理时间大概 300 毫秒。如果某台表没应答主站会等超时ESPHome 默认超时是 200 毫秒左右一台表超时就会拖慢整个轮询周期。如果日志里频繁出现某台表超时先确认那台表是否在线。电表的 RS-485 接口有的需要单独供电有的从测量回路取电如果测量回路没电通信接口也不工作。我有一路备用回路平时是断开的那台表就经常超时后来把它的 update_interval 设长一点减少无效轮询。九台表全部在线的情况下ESP32 的 CPU 占用很低Wi-Fi 和 Modbus 可以稳定共存。有人担心 ESP32 同时跑 Wi-Fi 和串口会冲突实测下来只要不跑蓝牙Wi-Fi 加 Modbus 完全没问题。如果非要同时开蓝牙建议把 Modbus 轮询周期放长给蓝牙留出时间片。5. Home Assistant 侧的能源面板怎么搭5.1 实体命名规范九台表不能乱起名ESPHome 把每个 sensor 暴露成 Home Assistant 里的实体实体 ID 由 name 字段决定。九台表如果都叫 VoltageHome Assistant 里会出现九个同名实体自动化里根本没法引用。命名规范必须在配置阶段就定好。我的命名方式是 Meter{编号}_{参数}比如 Meter1_Voltage、Meter1_Current、Meter1_Power、Meter1_Energy。编号和电表地址对应1 号表就是总进线2 号表是光伏依此类推。这样在 Home Assistant 的实体列表里一眼就能找到。如果想让中文显示更友好可以在 ESPHome 的 name 里写中文比如 总进线电压但实体 ID 会自动转成拼音或英文引用时还是用英文 ID。我的建议是 name 用英文在 Home Assistant 里通过 customize 或者仪表盘的显示名称改成中文这样兼顾可读性和可维护性。5.2 能源仪表盘把九路电量汇总成一张图Home Assistant 从 2021.8 开始提供能源仪表盘专门用来展示发电、用电、储能、电网交互。九台电表的数据接进来后在能源仪表盘里配置电网计量选总进线表的累计电量太阳能发电选光伏表的累计电量电池储能选储能表的充放电电量单个设备选各支路表的累计电量能源仪表盘会自动计算自消费率、电网依赖度这些指标还能按日、周、月、年查看。九路数据汇总在一张图上哪路负载在什么时段用电多一目了然。需要注意的是能源仪表盘要求电量实体有正确的 device_class 和 state_class。ESPHome 里这样写- platform: modbus_controller modbus_controller_id: meter1 name: Meter1_Energy address: 0x0004 register_type: holding value_type: U_DWORD unit_of_measurement: kWh device_class: energy state_class: total_increasing accuracy_decimals: 2 filters: - multiply: 0.01state_class 必须是 total_increasing能源仪表盘才能正确计算增量。如果写成 measurement仪表盘会报错说实体不支持。5.3 自动化联动电量超限告警和负载调度数据接进来只是第一步真正有用的是基于这些数据做自动化。我配了几条规则第一条总进线功率超过 8kW 持续 5 分钟手机推送告警。这条规则帮我发现过一次空调和充电桩同时启动导致的过载。第二条光伏发电功率超过 3kW 且储能电量低于 80%自动开启储能充电。这条规则让光伏的自消费率从 40% 提到了 70%。第三条某路支路电量日增量超过阈值推送提醒。这条规则用来监控那些不该有大功耗的设备比如冰箱如果一天用了 5 度电肯定有问题。自动化的触发条件用 numeric_state 或者 template动作里可以调用 notify 服务推送也可以调用 switch 服务控制继电器。九台表的数据在 Home Assistant 里就是普通实体和任何其他传感器一样用没有特殊之处。6. 调试工具与实战心得6.1 调试阶段必备的三件工具第一件是 USB 转 RS-485 调试器。这东西几十块钱一头插电脑 USB一头接 A、B 线配合调试软件可以直接和电表对话。它的价值在于把 ESP32 和 Home Assistant 从排查链路里摘出去单独验证电表和总线是否正常。我习惯先用调试器确认九台表都能读到数据再接 ESP32这样出问题时能快速定位是总线问题还是 ESP32 配置问题。第二件是 Modbus 调试软件。电脑端的调试软件可以手动发帧、看应答、解析寄存器值是理解电表寄存器映射的最佳工具。用它可以直观地看到发出去的请求帧长什么样电表回的应答帧长什么样CRC 对不对。调试阶段花半小时用软件摸清电表的脾气比在 ESPHome 日志里猜半天高效得多。第三件是万用表。用来测 A、B 之间的电压差正常空闲时应该是几百毫伏的偏置电压通信时会有波动。如果 A、B 之间电压是 0说明总线没供电或者接线断了。这个检查在排查完全不通的问题时特别有用。6.2 那些说明书不会写的经验经验一电表的 RS-485 接口有的需要独立供电。我用的某款表测量回路有电时通信正常测量回路断电后通信也断。如果你的某路表经常离线先确认那路是不是经常断电。经验二九台表不要用同一个 update_interval。如果九台表都设 1 秒ESP32 会疲于奔命Wi-Fi 都可能受影响。我的配置是总进线表 2 秒光伏和储能表 5 秒其他支路表 15 秒。这样关键数据实时次要数据够用ESP32 负载也低。经验三ESPHome 的 modbus_controller 在从站离线时会重试重试次数和超时时间可以在 modbus 组件里配。默认值在九台表场景下够用但如果某台表经常离线可以单独给它设更长的超时避免拖慢其他表。经验四电表累计电量寄存器有的是 32 位无符号有的是 32 位有符号有的是两个 16 位寄存器拼成 32 位。读回来数值不对时先换 value_type 试试U_DWORD、S_DWORD、U_DWORD_R、S_DWORD_R 四种组合总有一个对的。经验五Home Assistant 的能源仪表盘对时区敏感。如果电表数据的时间戳和 Home Assistant 的时区不一致日增量计算会出错。确保 ESPHome 和 Home Assistant 的时区配置一致国内都设 Asia/Shanghai。6.3 从九台表扩展到更多路的思路九台表跑通之后如果想扩展到更多路有几个方向。一是换用多路电表一台表测多路减少总线上的设备数量。二是用 Modbus TCP 网关把 RS-485 转成以太网ESP32 只负责 Wi-Fi 部分Modbus 轮询交给网关。三是换用支持 Modbus 的 PLC 或者工业网关稳定性更高但成本也更高。我自己的九路已经够用短期内不打算扩展。但如果你的场景是几十路甚至上百路RS-485 轮询周期会变得很长这时候 Modbus TCP 或者多主站方案更合适。ESP32 单主站轮询几十台表每台表刷新间隔可能到几分钟对实时性要求高的场景就不够了。最后分享一个我踩过的坑ESP32 的 Wi-Fi 和 Modbus 共用天线资源如果 Wi-Fi 信号弱ESP32 会频繁重连重连期间 Modbus 轮询会暂停。我的配电箱在车库角落Wi-Fi 信号只有两格后来加了一个 Wi-Fi 扩展器信号满格后 Modbus 轮询再没断过。所以如果你的 ESP32 放在金属配电箱里务必先确认 Wi-Fi 信号强度信号弱的话再好的 Modbus 配置也白搭。
