API Modbus

Exposes OpenEMS channels as a Modbus/TCP or Modbus/RTU slave interface with read-only or read-write access. This enables external systems to monitor and control OpenEMS Edge via the widely-used Modbus industrial protocol.

Overview

The Modbus API Controller provides a Modbus slave server that dynamically exposes OpenEMS components and channels to external Modbus clients (SCADA, PLC, HMI systems).

Key Capabilities
  • Dynamic Protocol Generation: Automatically structures selected components into Modbus register blocks

  • Self-Describing Layout: Register headers allow clients to parse protocol without predefined knowledge

  • Multiple Transports: TCP (Ethernet), RTU (serial) support

  • Access Control: Read-only or read-write modes per controller instance

  • Flexible Component Selection: Choose which components to expose via configuration

  • Timeout Management: Automatic timeout for writes from external systems

  • Excel Export: Generate component/channel mappings for documentation

Supported Protocols

  • Modbus/TCP: Over Ethernet, port configurable

  • Modbus/RTU: Over serial connections (COM1, COM2, etc.)

Components

Controller.Api.ModbusTcp.ReadWrite

Description

Modbus/TCP slave server with read-write access to configured components. External clients can read all channels and write to channels designated in the writeChannels configuration.

Factory-PID

Controller.Api.ModbusTcp.ReadWrite

Service Binding

Implements:

  • io.openems.edge.controller.api.modbus.readwrite.tcp.ControllerApiModbusTcpReadWrite

  • io.openems.edge.controller.api.Controller

  • io.openems.edge.common.component.OpenemsComponent

  • io.openems.edge.common.jsonapi.ComponentJsonApi

  • io.openems.edge.timedata.api.TimedataProvider

Table 1. Channels
Channel ID Description Type

overrideStatus

Status of channel overrides (ACTIVE/INACTIVE)

Enum (Status)

cumulatedActiveTime

Total time with active overrides

Long (seconds)

cumulatedInactiveTime

Total time without active overrides

Long (seconds)

api_workerLog

Logs write-command execution

String

Table 2. Configuration Parameters
Parameter Description Type Default

id

Unique component identifier

String

ctrlApiModbusTcp0

alias

Human-readable component name

String

ModbusTcp Read-Write

enabled

Enable/disable the controller

Boolean

true

port

Port for TCP server to listen on

Integer

502

component_ids

List of component IDs to expose (e.g., ["_sum", "ess0", "meter0"])

String[]

["_sum"]

writeChannels

List of channels that can be written via Modbus (format: componentId/channelId)

String[]

(empty)

apiTimeout

Timeout for write operations (seconds)

Integer

60

maxConcurrentConnections

Maximum simultaneous client connections

Integer

20

logVerbosity

Log level (NONE, TRACE, DEBUG)

Enum

NONE

Controller.Api.ModbusTcp.ReadOnly

Description

Modbus/TCP slave server with read-only access to configured components. External clients can only read channels; writes are rejected.

Factory-PID

Controller.Api.ModbusTcp.ReadOnly

Configuration Parameters

Same as ReadWrite, except no writeChannels parameter.

Controller.Api.ModbusRtu.ReadWrite

Description

Modbus/RTU slave server over serial connection with read-write access. Communicates with serial-connected devices (industrial controllers, gateways).

Factory-PID

Controller.Api.ModbusRtu.ReadWrite

Configuration Parameters

Same as TCP ReadWrite, plus:

Parameter Description Type Default

port

Serial port device name (e.g., /dev/ttyUSB0, COM1)

String

/dev/ttyUSB0

baudRate

Serial connection speed

Integer

9600

stopBits

Number of stop bits (1 or 2)

Integer

1

Controller.Api.ModbusRtu.ReadOnly

Description

Modbus/RTU slave server over serial connection with read-only access.

Factory-PID

Controller.Api.ModbusRtu.ReadOnly

Register Layout

The Modbus protocol is self-describing through dynamic register headers:

Protocol Structure

  1. Register 0: OpenEMS identifier hash (0x17ed6201)

  2. Register 1: Length of first block header

    Adding length to current address gives next block start address.

  3. Register 200 (example block): 16-character fixed-length Component-ID string

  4. Register 216 (example block): Block length (register count)

  5. Register 220 (example block): Sub-block nature identifier (OpenemsComponent, State, Power)

  6. Register 221 (example block): Sub-block length

This structure allows Modbus clients to: 1. Detect OpenEMS by hash in register 0 2. Parse all available components by following block headers 3. Map registers to channels dynamically

Example Parsing

For a controller configured with component _sum:

  • Register 0: 0x17ed6201 (OpenEMS magic number)

  • Register 1: 199 (first block length)

  • Registers 1-199: Component _sum data

  • Register 200: Start of next component (if configured)

Channel Address Calculation

To read/write specific channels:

  1. Locate component in register map by parsing headers

  2. Find channel offset within component block

  3. Read/write to register address = component_start + channel_offset

Example: * Read register 302 to get _sum/EssSoc (total ESS state of charge) * Write to register 806 to set ess0/SetActivePowerEquals (control charging/discharging)

Write Channel Configuration

The writeChannels parameter controls which channels external systems can modify. Syntax: componentId/channelId

writeChannels = [
  "ess0/SetActivePowerEquals",
  "ess0/SetReactivePowerEquals",
  "charger0/SetChargingPower",
  "heatpump0/SetTargetTemperature"
]

Only channels explicitly listed can be written. All other channels are read-only.

Data Export

Instead of parsing the Modbus protocol manually, download the component/channel mapping from OpenEMS UI:

  1. Open UI → System → System Profile

  2. Export as Excel file

  3. Maps all registers to components and channels

  4. Example file included in bundle’s doc folder

Timeout Behavior

When an external system writes to a channel via Modbus:

  1. Value is set in OpenEMS with a write timestamp

  2. If no new write received within apiTimeout seconds, the previous value expires

  3. Channel reverts to default or last cycled value

  4. Prevents "stuck" values if Modbus client crashes or loses connection