Skip to content

Modbus Server Split Out of modbus_controller

Modbus server mode has been split out of modbus_controller into a new dedicated modbus_server component. Configurations that used modbus_controller as a Modbus server (the role: server setting lives on the modbus: bus, not on modbus_controller) must move their server_registers: and server_courtesy_response: blocks under a new top-level modbus_server: entry.

This is a breaking change for YAML configurations using server mode in ESPHome 2026.5.0 and later.

PR #15509: Split modbus_server from modbus_controller

Server-mode register handling was wedged into modbus_controller alongside its client-mode polling. The two roles share a transport (the modbus: bus) but almost nothing else: server mode dispatches inbound requests to user lambdas, while client mode polls outbound register reads on a schedule. Keeping both in the same component made the controller heavier than it needed to be, complicated review of server-side fixes, and prevented the server logic from sharing helpers with future modbus-based components.

modbus_server is a thin Component that implements modbus::ModbusDevice and owns the register table plus the optional courtesy response, with no client-mode machinery. The migration in this PR is intentionally close to a copy/paste — functional improvements to server mode (out-of-range handling, multi-register transactions, parser fixes, etc.) come in follow-up PRs.

ConfigurationBeforeAfter
Pure servermodbus_controller ~4.5 KBmodbus_server ~1.8 KB
Pure clientmodbus_controller ~6.4 KBmodbus_controller ~3.9 KB

Devices that previously combined client and server in one modbus_controller will now have two separate components, but each is much smaller.

Old (under modbus_controller)New (under modbus_server)
server_registers:registers:
server_courtesy_response:courtesy_response:

role: server on the bus is still supported — that selects the transport-layer role. What changed is that the register table is no longer accepted on modbus_controller. Put it on modbus_server instead.

YAML configurations that used modbus_controller as a Modbus server — typically battery management bridges (BMS protocol bridges, yamBMS forks), EVSE bridges, energy/inverter proxies, and any “act as a register slave” project. Pure-client modbus_controller users (the more common case — reading from third-party Modbus devices) are not affected.

# Before
modbus:
role: server
modbus_controller:
- address: 1
server_registers:
- address: 0x0001
value_type: U_WORD
read_lambda: "return id(my_sensor).state;"
server_courtesy_response:
enabled: true
register_value: 0
# After
modbus:
role: server
modbus_server:
- address: 1
registers:
- address: 0x0001
value_type: U_WORD
read_lambda: "return id(my_sensor).state;"
courtesy_response:
enabled: true
register_value: 0

A modbus: bus has a single role and your device occupies one side of it: modbus_controller requires role: client, modbus_server requires role: server, and the bus schema enforces this — you cannot mix the two on the same modbus: instance. Modbus only allows one client per bus, so the “both on one device” pattern was never valid; if a previous config had server_registers: under a client-mode modbus_controller, those registers were silently inactive. Run a second modbus: bus on a different UART if you need a device that’s both a server on one wire and a client on another.

  • 2026.5.0: Server-mode keys removed from modbus_controller; new modbus_server component active.

This is a clean break — there is no deprecation period or migration shim. modbus_controller configs that still contain server_registers: or server_courtesy_response: will fail validation on 2026.5.0. (For contrast, the earlier modbus_controllermodbus::helpers namespace refactor in 2026.4.0 kept deprecation shims through 2026.10.0 — that pattern doesn’t apply here because there’s no in-place rename to forward, and the affected configs are caught at config-validation time with a precise error message rather than at runtime.)

Terminal window
# YAML — find server-mode keys still under modbus_controller.
# -E (extended regex) keeps the alternation portable across GNU and BSD/macOS grep.
grep -rEn 'server_registers:|server_courtesy_response:' your_configs/

If you have questions about migrating your configuration, please ask in:

Comments

Feel free to leave a comment here to discuss this post with others. You can ask questions, share your experience, or suggest improvements. If you have a question about a specific feature or issue, please consider using the ESPHome Discord. Stick to English and follow ESPHome's code of conduct. These comments exist on a discussion on GitHub, so you can also comment there directly if you prefer.