跳转到内容

Modbus 服务器

modbus_server 组件创建一条 RS485 连接,让你的 ESPHome 节点充当 Modbus 服务器,从而允许 ModBUS 客户端从你的 ESPHome 节点读取数据(如传感器值)。

你必须将 Modbus 的 role 属性设置为 server。

你需要一个 RS485 收发器模块:

有关更多细节,请参阅 stackexchange 上的这个 RS485 模块是如何工作的?。

收发器连接到 MCU 的 UART。对于 ESP32,默认将引脚 16 连接到 TXD、引脚 17 连接到 RXD,但也可以使用任何其他引脚。将 3.3V 连接到 VCC,当然还有 GND 连接到 GND。

在总线侧,根据 Modbus 标准,你需要在总线电缆的两端各放置一个 120 欧姆的终端电阻。有些收发器已经将其焊接到板上,而一些从站设备可能通过跳线或 DIP 开关提供。

NOTE

如果你使用的是 ESP8266,串行日志记录可能会导致从 UART 读取出现问题。为获得最佳效果,建议使用硬件串行。如果其他组件在 loop() 中花费大量时间,软件串行可能无法读取所有接收到的数据。

对于硬件串行,只能使用一组有限的引脚。使用 tx_pin: GPIO1 和 rx_pin: GPIO3,或 tx_pin: GPIO15 和 rx_pin: GPIO13。

使用硬件 UART 的缺点是不能再使用串行日志记录,因为串行日志会被发送到 Modbus 设备(们),从而导致错误。

可以通过设置 baud_rate: 0 来禁用串行日志记录。

参见 Logger 了解更多细节。

logger:
level: <level>
baud_rate: 0
  • modbus_id (可选, ID): 手动指定 modbus hub 的 ID。

  • address (必选, integer): 服务器设备的 Modbus 地址。

  • courtesy_response (可选): 配置块,用于在设备充当 Modbus 服务器时启用友好响应(courtesy response)功能。

    • enabled (可选, boolean): 是否启用友好响应功能。 默认为 false。
    • register_last_address (可选, integer): 最高的 Modbus 寄存器地址(含),在该范围内允许读取未定义的寄存器,并将用默认值填充。 任何包含此范围内的未定义寄存器的读取请求都将返回 register_value 指定的值,而不是触发异常。 默认为 65535。
    • register_value (可选, integer): 在 register_last_address 定义的地址范围内,为未定义寄存器返回的 16 位值(范围:0–65535)。 默认为 0。
  • registers (可选): 充当服务器时响应的寄存器列表。

    • address (必选, integer): 一个范围内第一个寄存器的起始地址。

    • value_type (可选): mod_bus 寄存器数据的数据类型。ModBUS 的默认数据类型是 big endian(网络字节序,最高字节在前)格式的 16 位整数。

      • U_WORD :无符号 16 位整数,1 个寄存器,uint16_t
      • S_WORD :有符号 16 位整数,1 个寄存器,int16_t
      • U_DWORD :无符号 32 位整数,2 个寄存器,uint32_t
      • S_DWORD :有符号 32 位整数,2 个寄存器,int32_t
      • U_DWORD_R :little endian 无符号 32 位整数,2 个寄存器,uint32_t
      • S_DWORD_R :little endian 有符号 32 位整数,2 个寄存器,int32_t
      • U_QWORD :无符号 64 位整数,4 个寄存器,uint64_t
      • S_QWORD :有符号 64 位整数,4 个寄存器,int64_t
      • U_QWORD_R :little endian 无符号 64 位整数,4 个寄存器,uint64_t
      • S_QWORD_R :little endian 有符号 64 位整数,4 个寄存器,int64_t
      • FP32 :32 位 IEEE 754 浮点数,2 个寄存器,float
      • FP32_R :little endian 32 位 IEEE 754 浮点数,2 个寄存器,float

      默认为 U_WORD。

    • read_lambda (必选, lambda): 返回此寄存器值的 Lambda。

    • write_lambda (可选, lambda): 设置此寄存器值的 Lambda。系统会提供一个适当类型(uint16_t、int32_t 等,见上文)的变量 x 作为值, 同时还提供包含此寄存器地址的 address 变量。如果操作成功,你必须返回 true,否则返回 false; 返回 false 时,将向客户端发送 ModBUS 异常码 4。

    • allow_partial_read (可选, boolean): 对于多寄存器值类型 (U_DWORD、S_QWORD、FP32 等),允许读取请求仅覆盖 该值的部分范围——从中间开始或在其结束前停止——并且 只返回所请求的寄存器。当为 false 时,该值被视为 原子的,任何未覆盖整个值的读取都会以 ILLEGAL_DATA_ADDRESS 异常被拒绝。对单寄存器类型 (U_WORD/S_WORD)没有影响。默认为 false。

      NOTE

      即使启用了 allow_partial_read,read_lambda 仍然 每个请求计算一次,因此单个请求内返回的寄存器 始终彼此一致。在分开的请求中读取某个值的各个部分 的客户端,可能会观察到值在两次读取之间发生了变化——这是按寄存器逐项访问 Modbus 所固有的特性。

      此选项仅影响读取。写入始终是原子的:写入必须 覆盖整个值,因为无法为 write_lambda 重构部分值。

NOTE

每个寄存器条目根据其 value_type 占据多个连续的地址 (*_WORD 占 1 个,*_DWORD/FP32 占 2 个,*_QWORD 占 4 个)。 同一个 modbus_server 内的寄存器地址范围不得重叠, 且必须适合 16 位地址空间(0x0000–0xFFFF); 否则配置将被拒绝。

以下代码允许 ModBUS 客户端从你的 ESPHome 节点读取传感器值,而该节点本身是从某个本地传感器读取的。

uart:
- id: uart_modbus_server
tx_pin: 25
rx_pin: 35
modbus:
- uart_id: uart_modbus_server
id: modbus_server
role: server
modbus_server:
- modbus_id: modbus_server
address: 0x4
registers:
- address: 0x0002
value_type: U_DWORD_R
read_lambda: |-
return id(uptime_sens).state;
sensor:
- platform: uptime
id: uptime_sens