From b534504831b4a9645c4e33435a245cb7b9d74260 Mon Sep 17 00:00:00 2001 From: Wouter Voorberg <119453250+Hackerberg43@users.noreply.github.com> Date: Wed, 26 Nov 2025 18:04:19 +0100 Subject: [PATCH 1/3] Enhance Modbus agent documentation Updated Modbus documentation to clarify device configuration and data types. Added details on batched requests and addressing. --- docs/user-guide/agents-protocols/modbus.md | 138 +++++++++++++-------- 1 file changed, 87 insertions(+), 51 deletions(-) diff --git a/docs/user-guide/agents-protocols/modbus.md b/docs/user-guide/agents-protocols/modbus.md index 82dccfc1..9fe6f655 100644 --- a/docs/user-guide/agents-protocols/modbus.md +++ b/docs/user-guide/agents-protocols/modbus.md @@ -8,8 +8,7 @@ The Modbus TCP and RTU agents allow integration of Modbus devices into OpenRemot ## How It Works -In OpenRemote, Modbus agents act as Modbus **masters**, communicating directly with Modbus **slave** devices. The agent manages the connection parameters (TCP or Serial) and facilitates reading and writing data to specified registers or coils of the connected Modbus devices. - +In OpenRemote, Modbus agents act as Modbus **masters**, communicating directly with Modbus **slave** devices. The agent manages the connection parameters (TCP or Serial) and facilitates reading and writing data to specified registers or coils of the connected Modbus devices. The agents can optimize their read requests by batching multiple registers in one call. This is disabled by default (see enabling batched reading) ## Configuring a Modbus Agent You can configure either **Modbus TCP** or **Modbus RTU** agents: @@ -20,32 +19,54 @@ You can configure either **Modbus TCP** or **Modbus RTU** agents: | --------- | ------------------------------------------- | -------- | ------- | | **host** | IP address or hostname of the Modbus device | Yes | - | | **port** | TCP port number of the Modbus device | Yes | `502` | -| **Unit ID** | Modbus device address (1–247 typically) | Yes | - - -**Example:** +| **deviceConfig** | Device (unitID) specific settings, applies "default" setting unless a specific UnitID config is added | Yes | automatically loaded | -```yaml -host: 192.168.1.50 -port: 502 -unitId: 1 -``` ### Modbus RTU Agent | Parameter | Description | Required | Default | | ------------------ |----------------------------------------------| -------- | ------- | | **serialPort** | Serial port identifier (e.g., `/dev/ttyUSB0`) | Yes | - | -| **Unit ID** | Modbus device address (1–247 typically) | Yes | -| **serialBaudrate** | Baud rate for serial communication | No | `38400` | +| **serialBaudrate** | Baud rate for serial communication | No | `9600` | +| **dataBits** | Amount of databits setting on serial port, most often 8 | Yes | - | +| **parity** | Parity/check bit setting of the serial port, | Yes | - | +| **stopBits** | Amount of stop bits on the serial port | Yes | - | +| **deviceConfig** | Device (unitID) specific settings, applies "default" setting unless a specific UnitID config is added | Yes | automatically loaded | + +### Device Config +While the above settings are applicable for all devices on the bus port, some settings can vary per device (unitId / modbus device address). +The "default" setting is applied to all unitID's unless specified otherwise. If you have a unitID on the bus requiring a different config, add an extra property to the config with that unitID as name. +For example, a specific config for unitID 1 would be: + +**Device Config Example:** + +```json +{ + "default": { + "endianFormat": "BIG_ENDIAN", + "illegalRegisters": "", + "maxRegisterLength": 1 + } + "1": { + "endianFormat": "LITTLE_ENDIAN", + "illegalRegisters": "23,24,100-200", + "maxRegisterLength": 40 + } +} +``` +Valid parameters are: -**Example:** +| Parameter | Description | +| ------------------ |----------------------------------------------| +| **endianFormat** | Byte endianness; Big/Little and with or without byte swap | +| **illegalRegisters** | Registers not supported by the device | +| **maxRegisterLength** | Maximum amount of registers in one request | -```yaml -serialPort: /dev/ttyUSB0 -serialBaudrate: 9600 -unitId: 1 -``` +### Batched requests +The settings **illegalRegisters** and **maxRegisterLength** are used to setup combined registers read into a single request. This can significantly reduce traffic on the bus and reduce response times. +By default, maxRegisterlength is set to 1, resulting in no batching. When raising maxRegisterLength, illegalRegister must be used to take into account register ranges that are invalid for that specific device. +For example, when agentlinks are setup to register 1 and 20, this will be combined in one call if maxRegisterLength > 20. However, if a call to register 19 results in an exception response by the device, this will fail, so you would then need to add 19 to illegalRegisters. +The agent forms groups of batches based on **identical requestInterval and unitID**. ## Linking Attributes via Agent Link @@ -53,14 +74,14 @@ Each asset attribute can link to the Modbus agent via an **Agent Link**. Below a | Parameter | Description | Required | |---------------------------|--------------------------------------------------------|----------------| -| **Read Type** | Register type (`COIL`, `DISCRETE`, `HOLDING`, `INPUT`) | Yes (for read) | | **Read Address** | Register or coil address | Yes (for read) | -| **Read Value Type** | Data type (`BOOL`, `INT`, `UINT`, `DINT`, `REAL`, etc.) | Yes (for read) | -| **Write Type** | Register type for write (`COIL`, `HOLDING`) | Optional | -| **Write Address** | Register or coil address for write | Optional | -| **Write Value Type** | Data type for writing | Optional | -| **Polling Interval** | Interval between reads in milliseconds | Optional | -| **Read Registers Amount** | Amount of registers to read | Optional | +| **Read Memory Area** | Register type (`COIL`, `DISCRETE`, `HOLDING`, `INPUT`) | Yes (for read) | +| **Read Value Type** | Data type (`BOOL`, `INT`, `UINT`, `DINT`, `REAL`, etc.) | Yes (for read)| +| **Write Address** | Register or coil address for write | Yes (for write)| +| **Write Memory Area** | Register type for write (`COIL`, `HOLDING`) | Yes (for write)| +| **Unit Id** | Modbus device address (1–247 typically) | Yes (for serial) | +| **Request Interval** | Interval between reads in milliseconds | Optional | +| **Registers Amount** | Amount of registers to read (automatically determined if not set | Optional | **Important:** Modbus addressing in OpenRemote is zero-based. @@ -78,13 +99,16 @@ Polling Interval: 5000 ## Data Types -OpenRemote Modbus integration supports various data types through Apache PLC4X: +OpenRemote Modbus integration supports various data type: -* **BOOL:** Boolean (single-bit) -* **INT/UINT:** 16-bit integer (signed/unsigned) -* **DINT/UDINT:** 32-bit integer (signed/unsigned) -* **REAL:** 32-bit floating-point -* **LREAL:** 64-bit floating-point +* **BOOL:** Boolean (single-bit) +* **SINT/USINT/BYTE:** 8-bit integer (signed/unsigned) +* **INT/UINT/WORD:** 16-bit integer (signed/unsigned) +* **DINT/UDINT/DWORD:** 32-bit integer (signed/unsigned) +* **LINT/ULINT/LWORD:** 64-bit integer (signed/unsigned, terms as `long` or `BigInteger` may be used for very large values) +* **REAL:** 32-bit floating-point +* **LREAL:** 64-bit floating-point +* **CHAR/WCHAR:** Character (single or wide, represented as `char` or `String`) ## Example Configuration @@ -95,36 +119,48 @@ Here’s a full example configuration for a Modbus TCP temperature sensor: ```yaml host: 192.168.1.50 port: 502 -unitId: 1 +deviceConfig: "default": { + "endianFormat": "BIG_ENDIAN", + "illegalRegisters": "", + "maxRegisterLength": 1 + } ``` **Agent Link Configuration:** ```yaml -Read Type: HOLDING -Read Address: 0 -Read Value Type: UINT -Write Type: HOLDING -Write Address: 0 -Write Value Type: UINT -Polling Interval: 10000 - + readAddress: 23, + readMemoryArea: "INPUT", + readValueType: "LINT", + registersAmount: 2, + requestInterval: 60000, + unitId: 223, + writeAddress: 22, + writeMemoryArea: "HOLDING" + unitId: 1 ``` -## Notes +## Under the Hood -* The Modbus agent polls device data periodically based on the configured interval. -* Write operations occur only when attribute values are explicitly changed in OpenRemote. -* Double-check zero-based addressing, especially if your device documentation uses one-based indexing. +The Modbus agent is written for openremote natively. This enables thorough integration into the openremote attribute event model. +When you configure a linked attribute with a Read setting along with a request interval, the agent schedules periodic read requests to the device (using the specified function code based on Read Type, e.g. function 0x03 for holding registers or 0x01 for coils, etc.). The response is parsed according to the Read Value Type (e.g. UINT, BOOL, etc.) and then the attribute's value is updated in OpenRemote. +With only writing active, the request acknowledgement will be used to determine whether the write was succesfull. However, it may be the case that you write a setpoint register but want to update the value from another register which holds the actual value (the device may have limits for example). In that case, enable both reading and writing. You can do that either on the same or a different register, depending how the device works. When both reading and writing are active, instead of using the request acknowledgement, the value is immediately read back with a read request after the write request. -## Under the Hood +### Read/Write Interval & Combinations -The Modbus agent uses a Modbus client library (Apache PLC4X for parsing Modbus data types) to handle communication. When you configure a linked attribute with a Read setting, the agent schedules periodic read requests to the device (using the specified function code based on Read Type, e.g. function 0x03 for holding registers or 0x01 for coils, etc.). The response is parsed according to the Read Value Type (e.g. UINT, BOOL, etc.) and then the attribute's value is updated in OpenRemote. Likewise, when a write is triggered (either on a schedule or when you change an attribute's value), the agent sends the appropriate write command (e.g. 0x06 to write a single register, or 0x05 for a coil) with the value formatted according to the Write Value Type. The use of the ModbusDataType enum for the value types ensures that the byte order and size are handled correctly by the PLC4X library, so you don't have to manually deal with big-endian/little-endian or register ordering concerns - you simply select the correct data type and the agent does the rest. +| Read Configured | Write Configured | Request Interval Set| Resulting Behavior | +| --------- | ------------------------------------------- | -------- | ------- | +| yes | no | no | one time read after agent connects | +| no | yes | no | only writes after attribute is manipulated | +| yes | yes | no | one time read after agent connects, only writes after attribute is manipulated, then reads back. | +| yes | no | yes | cyclic reads | +| yes | no | yes | cyclic writes | +| yes | yes | yes | cyclic read/write, updates value on attribute manipulation and reads | -Note on addressing: Remember that OpenRemote (and PLC4X) use zero-based addressing for Modbus. This is a common source of confusion. Always confirm whether your device documentation lists register addresses starting at 1 or 0, and adjust accordingly. For instance, if a device documentation refers to "Register 100" (but is zero-based internally), you might actually need to use 100 as is. However, if it refers to "Register 40001" (Modbus convention for first holding register), you should use 0. The OpenRemote Modbus agent ultimately converts your given address into the proper Modbus PDU address (e.g. address 0 will be sent as 0x0000 in the request). -Note on data types: The list of supported data types (for Read/Write Value Type) comes from PLC4X's Modbus support. Most standard types are available. For example, if you need to read a 32-bit integer split across two registers (as many devices use), you can choose DINT or UDINT. If you need a 32-bit float, choose REAL. The agent will automatically read the necessary number of registers (two for 32-bit, four for 64-bit, etc.) in one request. If you select a 64-bit type (LINT, ULINT, LREAL), note that it will read four consecutive 16-bit registers (since Modbus registers are 16-bit each) to assemble the 64-bit value. Ensure that adjacent registers are free or belong to the same value in your device, otherwise you might get incorrect data. Booleans (BOOL) correspond to single bits (coils or bits within a register). If you use BOOL on a register type, it will interpret the least significant bit of that register as the boolean value. +**Note on addressing:** +Remember that OpenRemote uses 1-based addressing for Modbus. This is a common source of confusion. Always confirm whether your device documentation lists register addresses starting at 1 or 0, and adjust accordingly. For instance, if a device documentation refers to "Register 100" (but is zero-based), you might actually need to use 101. However, if it refers to "Register 40001" (Modbus convention for first holding register), you should use 1. The OpenRemote Modbus agent ultimately converts your given address into the proper Modbus PDU address (e.g. address 1 will be sent as 0x0000 in the request). -Note on write operations: OpenRemote will only perform a write when the attribute value is explicitly set or changed (through the UI, an automation rule, or the API). Simply linking an attribute with write parameters does not continuously write; it only enables writing. The reading (polling) is what happens continuously. If an attribute has both read and write configured, it is effectively read-write: the agent will poll it regularly, and you can also send a new value to the device. If you want write-only (seldom needed), you could configure write parameters and perhaps disable polling (set polling interval to 0 or very high) so it doesn't try to read a non-existent value. +**Note on data types:** Most standard types are available. For example, if you need to read a 32-bit integer split across two registers (as many devices use), you can choose DINT or UDINT. If you need a 32-bit float, choose REAL. The agent will automatically read the necessary number of registers (two for 32-bit, four for 64-bit, etc.) in one request. If you select a 64-bit type (LINT, ULINT, LREAL), note that it will read four consecutive 16-bit registers (since Modbus registers are 16-bit each) to assemble the 64-bit value. Ensure that adjacent registers are free or belong to the same value in your device, otherwise you might get incorrect data. Booleans (BOOL) correspond to single bits (coils or bits within a register). If you use BOOL on a register type, it will interpret the least significant bit of that register as the boolean value. -In summary, the Modbus agent allows you to bring data from PLCs, power meters, sensors, and other Modbus-based devices into OpenRemote, and to control those devices, using the Modbus protocol. By setting up the agent with the correct connection parameters and linking attributes with the proper register type, address, and data type, you can integrate Modbus data seamlessly into your OpenRemote project. The flexible configuration (separating read and write settings) accommodates devices that use distinct registers for status and control, and the use of standard data type definitions makes it easier to handle multi-register values. The ongoing documentation efforts and community feedback (see OpenRemote forums) are refining this feature , but with the information above, you should be able to get started with Modbus integration in OpenRemote. +**In summary,** the Modbus agent allows you to bring data from PLCs, power meters, sensors, and other Modbus-based devices into OpenRemote, and to control those devices, using the Modbus protocol. By setting up the agent with the correct connection parameters and linking attributes with the proper register type, address, and data type, you can integrate Modbus data seamlessly into your OpenRemote project. The flexible configuration (separating read and write settings) accommodates devices that use distinct registers for status and control, and the use of standard data type definitions makes it easier to handle multi-register values. The ongoing documentation efforts and community feedback (see OpenRemote forums) are refining this feature , but with the information above, you should be able to get started with Modbus integration in OpenRemote. From 01b387754aa77d3df75776efdaf16e60e020c89f Mon Sep 17 00:00:00 2001 From: Wouter Voorberg <119453250+Hackerberg43@users.noreply.github.com> Date: Mon, 16 Mar 2026 11:17:15 +0100 Subject: [PATCH 2/3] update docs based on later changes the the modbus implementation --- docs/user-guide/agents-protocols/modbus.md | 106 +++++++++++---------- 1 file changed, 56 insertions(+), 50 deletions(-) diff --git a/docs/user-guide/agents-protocols/modbus.md b/docs/user-guide/agents-protocols/modbus.md index fa8539ab..041e730e 100644 --- a/docs/user-guide/agents-protocols/modbus.md +++ b/docs/user-guide/agents-protocols/modbus.md @@ -8,7 +8,8 @@ The Modbus TCP and RTU agents allow integration of Modbus devices into OpenRemot ## How It Works -In OpenRemote, Modbus agents act as Modbus **masters**, communicating directly with Modbus **slave** devices. The agent manages the connection parameters (TCP or Serial) and facilitates reading and writing data to specified registers or coils of the connected Modbus devices. The agents can optimize their read requests by batching multiple registers in one call. This is disabled by default (see enabling batched reading) +In OpenRemote, Modbus agents act as Modbus **masters**, communicating directly with Modbus **slave** devices. The agent manages the connection parameters (TCP or Serial) and facilitates reading and writing data to specified registers or coils of the connected Modbus devices. The agents can optimize their read requests by batching multiple registers in one call. This is disabled by default (see [batched requests](#batched-requests)). + ## Configuring a Modbus Agent You can configure either **Modbus TCP** or **Modbus RTU** agents: @@ -18,8 +19,8 @@ You can configure either **Modbus TCP** or **Modbus RTU** agents: | Parameter | Description | Required | Default | | --------- | ------------------------------------------- | -------- | ------- | | **host** | IP address or hostname of the Modbus device | Yes | - | -| **port** | TCP port number of the Modbus device | Yes | `502` | -| **deviceConfig** | Device (unitID) specific settings, applies "default" setting unless a specific UnitID config is added | Yes | automatically loaded | +| **port** | TCP port number of the Modbus device | Yes | - | +| **deviceConfig** | Device (unitID) specific settings, applies "default" setting unless a specific UnitID config is added | No | auto-populated with defaults | ### Modbus RTU Agent @@ -28,10 +29,10 @@ You can configure either **Modbus TCP** or **Modbus RTU** agents: | ------------------ |----------------------------------------------| -------- | ------- | | **serialPort** | Serial port identifier (e.g., `/dev/ttyUSB0`) | Yes | - | | **serialBaudrate** | Baud rate for serial communication | No | `9600` | -| **dataBits** | Amount of databits setting on serial port, most often 8 | Yes | - | -| **parity** | Parity/check bit setting of the serial port, | Yes | - | -| **stopBits** | Amount of stop bits on the serial port | Yes | - | -| **deviceConfig** | Device (unitID) specific settings, applies "default" setting unless a specific UnitID config is added | Yes | automatically loaded | +| **dataBits** | Number of data bits per frame | No | `8` | +| **parity** | Parity bit setting (`NONE`, `ODD`, `EVEN`) | No | `EVEN` | +| **stopbits** | Number of stop bits (`STOPBITS_1`, `STOPBITS_2`) | No | `STOPBITS_1` | +| **deviceConfig** | Device (unitID) specific settings, applies "default" setting unless a specific UnitID config is added | No | auto-populated with defaults | ### Device Config While the above settings are applicable for all devices on the bus port, some settings can vary per device (unitId / modbus device address). @@ -46,8 +47,8 @@ For example, a specific config for unitID 1 would be: "endianFormat": "BIG_ENDIAN", "illegalRegisters": "", "maxRegisterLength": 1 - } - "1": { + }, + "1": { "endianFormat": "LITTLE_ENDIAN", "illegalRegisters": "23,24,100-200", "maxRegisterLength": 40 @@ -58,15 +59,15 @@ Valid parameters are: | Parameter | Description | | ------------------ |----------------------------------------------| -| **endianFormat** | Byte endianness; Big/Little and with or without byte swap | -| **illegalRegisters** | Registers not supported by the device | -| **maxRegisterLength** | Maximum amount of registers in one request | +| **endianFormat** | Byte endianness: `BIG_ENDIAN` (ABCD), `LITTLE_ENDIAN` (DCBA), `BIG_ENDIAN_BYTE_SWAP` (BADC), `LITTLE_ENDIAN_BYTE_SWAP` (CDAB) | +| **illegalRegisters** | Registers not supported by the device (e.g. `"23,24,100-200"`) | +| **maxRegisterLength** | Maximum amount of registers in one request (default: `1`) | ### Batched requests The settings **illegalRegisters** and **maxRegisterLength** are used to setup combined registers read into a single request. This can significantly reduce traffic on the bus and reduce response times. By default, maxRegisterlength is set to 1, resulting in no batching. When raising maxRegisterLength, illegalRegister must be used to take into account register ranges that are invalid for that specific device. For example, when agentlinks are setup to register 1 and 20, this will be combined in one call if maxRegisterLength > 20. However, if a call to register 19 results in an exception response by the device, this will fail, so you would then need to add 19 to illegalRegisters. -The agent forms groups of batches based on **identical requestInterval and unitID**. +The agent forms groups of batches based on **identical requestInterval, unitID, and readMemoryArea**. ## Linking Attributes via Agent Link @@ -76,30 +77,32 @@ Each asset attribute can link to the Modbus agent via an **Agent Link**. Below a |---------------------------|--------------------------------------------------------|----------------| | **Read Address** | Register or coil address | Yes (for read) | | **Read Memory Area** | Register type (`COIL`, `DISCRETE`, `HOLDING`, `INPUT`) | Yes (for read) | -| **Read Value Type** | Data type (`BOOL`, `INT`, `UINT`, `DINT`, `REAL`, etc.) | Yes (for read)| +| **Read Value Type** | Data type (`BOOL`, `INT`, `UINT`, `DINT`, `REAL`, etc.). Also used for multi-register write conversions. | Yes (for read)| | **Write Address** | Register or coil address for write | Yes (for write)| | **Write Memory Area** | Register type for write (`COIL`, `HOLDING`) | Yes (for write)| -| **Unit Id** | Modbus device address (1–247 typically) | Yes (for serial) | -| **Request Interval** | Interval between reads in milliseconds | Optional | -| **Registers Amount** | Amount of registers to read (automatically determined if not set | Optional | +| **Unit Id** | Modbus device address (1–255). Required for RTU, optional for TCP (defaults to `1`). Set when connecting through a Modbus gateway. | Yes (RTU) / No (TCP) | +| **Request Interval** | Interval between reads/writes in milliseconds (minimum: 1000). If not set, reads execute once on connection and writes execute on demand only. | Optional | +| **Registers Amount** | Amount of registers to read/write (automatically determined from data type if not set) | Optional | -**Important:** Modbus addressing in OpenRemote is zero-based. +**Important:** Modbus addressing in OpenRemote is **1-based**. Address 1 maps to PDU address 0x0000. **Example Attribute Link:** -```yaml -Read Type: HOLDING -Read Address: 24 -Read Value Type: UINT -Write Type: HOLDING -Write Address: 0 -Write Value Type: UINT -Polling Interval: 5000 +```json +{ + "readAddress": 24, + "readMemoryArea": "HOLDING", + "readValueType": "UINT", + "writeAddress": 24, + "writeMemoryArea": "HOLDING", + "unitId": 1, + "requestInterval": 5000 +} ``` ## Data Types -OpenRemote Modbus integration supports various data type: +OpenRemote Modbus integration supports various data types: * **BOOL:** Boolean (single-bit) * **SINT/USINT/BYTE:** 8-bit integer (signed/unsigned) @@ -116,35 +119,38 @@ Here’s a full example configuration for a Modbus TCP temperature sensor: **Agent Configuration:** -```yaml -host: 192.168.1.50 -port: 502 -deviceConfig: "default": { - "endianFormat": "BIG_ENDIAN", - "illegalRegisters": "", - "maxRegisterLength": 1 - } +```json +{ + "host": "192.168.1.50", + "port": 502, + "deviceConfig": { + "default": { + "endianFormat": "BIG_ENDIAN", + "illegalRegisters": "", + "maxRegisterLength": 1 + } + } +} ``` **Agent Link Configuration:** -```yaml - readAddress: 23, - readMemoryArea: "INPUT", - readValueType: "LINT", - registersAmount: 2, - requestInterval: 60000, - unitId: 223, - writeAddress: 22, - writeMemoryArea: "HOLDING" - unitId: 1 +```json +{ + "readAddress": 23, + "readMemoryArea": "INPUT", + "readValueType": "REAL", + "registersAmount": 2, + "requestInterval": 60000, + "unitId": 1 +} ``` ## Under the Hood The Modbus agent is written for openremote natively. This enables thorough integration into the openremote attribute event model. When you configure a linked attribute with a Read setting along with a request interval, the agent schedules periodic read requests to the device (using the specified function code based on Read Type, e.g. function 0x03 for holding registers or 0x01 for coils, etc.). The response is parsed according to the Read Value Type (e.g. UINT, BOOL, etc.) and then the attribute's value is updated in OpenRemote. -With only writing active, the request acknowledgement will be used to determine whether the write was succesfull. However, it may be the case that you write a setpoint register but want to update the value from another register which holds the actual value (the device may have limits for example). In that case, enable both reading and writing. You can do that either on the same or a different register, depending how the device works. When both reading and writing are active, instead of using the request acknowledgement, the value is immediately read back with a read request after the write request. +With only writing active, the request acknowledgement will be used to determine whether the write was successful, and the attribute value is updated locally. However, it may be the case that you write a setpoint register but want to update the value from another register which holds the actual value (the device may have limits for example). In that case, enable both reading and writing. You can do that either on the same or a different register, depending how the device works. When both reading and writing are active, instead of using the request acknowledgement, the value is immediately read back with a read request after the write request. ### Read/Write Interval & Combinations @@ -154,13 +160,13 @@ With only writing active, the request acknowledgement will be used to determine | no | yes | no | only writes after attribute is manipulated | | yes | yes | no | one time read after agent connects, only writes after attribute is manipulated, then reads back. | | yes | no | yes | cyclic reads | -| yes | no | yes | cyclic writes | -| yes | yes | yes | cyclic read/write, updates value on attribute manipulation and reads | +| no | yes | yes | cyclic writes (re-sends the current attribute value each interval) | +| yes | yes | yes | cyclic reads; writes are on-demand when the attribute value changes, followed by a verification read | **Note on addressing:** Remember that OpenRemote uses 1-based addressing for Modbus. This is a common source of confusion. Always confirm whether your device documentation lists register addresses starting at 1 or 0, and adjust accordingly. For instance, if a device documentation refers to "Register 100" (but is zero-based), you might actually need to use 101. However, if it refers to "Register 40001" (Modbus convention for first holding register), you should use 1. The OpenRemote Modbus agent ultimately converts your given address into the proper Modbus PDU address (e.g. address 1 will be sent as 0x0000 in the request). -**Note on data types:** Most standard types are available. For example, if you need to read a 32-bit integer split across two registers (as many devices use), you can choose DINT or UDINT. If you need a 32-bit float, choose REAL. The agent will automatically read the necessary number of registers (two for 32-bit, four for 64-bit, etc.) in one request. If you select a 64-bit type (LINT, ULINT, LREAL), note that it will read four consecutive 16-bit registers (since Modbus registers are 16-bit each) to assemble the 64-bit value. Ensure that adjacent registers are free or belong to the same value in your device, otherwise you might get incorrect data. Booleans (BOOL) correspond to single bits (coils or bits within a register). If you use BOOL on a register type, it will interpret the least significant bit of that register as the boolean value. +**Note on data types:** Most standard types are available. For example, if you need to read a 32-bit integer split across two registers (as many devices use), you can choose DINT or UDINT. If you need a 32-bit float, choose REAL. The agent will automatically read the necessary number of registers (two for 32-bit, four for 64-bit, etc.) in one request. If you select a 64-bit type (LINT, ULINT, LREAL), note that it will read four consecutive 16-bit registers (since Modbus registers are 16-bit each) to assemble the 64-bit value. Ensure that adjacent registers are free or belong to the same value in your device, otherwise you might get incorrect data. Booleans (BOOL) correspond to single bits when used with coils or discrete inputs. If you use BOOL on a register type (holding/input), any non-zero register value is interpreted as true. Note that there is no separate write value type; multi-register writes use the read value type for data conversion. -**In summary,** the Modbus agent allows you to bring data from PLCs, power meters, sensors, and other Modbus-based devices into OpenRemote, and to control those devices, using the Modbus protocol. By setting up the agent with the correct connection parameters and linking attributes with the proper register type, address, and data type, you can integrate Modbus data seamlessly into your OpenRemote project. The flexible configuration (separating read and write settings) accommodates devices that use distinct registers for status and control, and the use of standard data type definitions makes it easier to handle multi-register values. The ongoing documentation efforts and community feedback (see OpenRemote forums) are refining this feature , but with the information above, you should be able to get started with Modbus integration in OpenRemote. +**In summary,** the Modbus agent allows you to bring data from PLCs, power meters, sensors, and other Modbus-based devices into OpenRemote, and to control those devices, using the Modbus protocol. By setting up the agent with the correct connection parameters and linking attributes with the proper register type, address, and data type, you can integrate Modbus data seamlessly into your OpenRemote project. The flexible configuration (separating read and write settings) accommodates devices that use distinct registers for status and control, and the use of standard data type definitions makes it easier to handle multi-register values. The ongoing documentation efforts and community feedback (see OpenRemote forums) are refining this feature, but with the information above, you should be able to get started with Modbus integration in OpenRemote. Modbus requests have a 3-second timeout. From 7a45edc4f32e215db6df415a70f5b65561fa4813 Mon Sep 17 00:00:00 2001 From: Wouter Voorberg <119453250+Hackerberg43@users.noreply.github.com> Date: Mon, 16 Mar 2026 11:23:55 +0100 Subject: [PATCH 3/3] update --- docs/user-guide/agents-protocols/modbus.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user-guide/agents-protocols/modbus.md b/docs/user-guide/agents-protocols/modbus.md index 041e730e..fa4f84f8 100644 --- a/docs/user-guide/agents-protocols/modbus.md +++ b/docs/user-guide/agents-protocols/modbus.md @@ -80,7 +80,7 @@ Each asset attribute can link to the Modbus agent via an **Agent Link**. Below a | **Read Value Type** | Data type (`BOOL`, `INT`, `UINT`, `DINT`, `REAL`, etc.). Also used for multi-register write conversions. | Yes (for read)| | **Write Address** | Register or coil address for write | Yes (for write)| | **Write Memory Area** | Register type for write (`COIL`, `HOLDING`) | Yes (for write)| -| **Unit Id** | Modbus device address (1–255). Required for RTU, optional for TCP (defaults to `1`). Set when connecting through a Modbus gateway. | Yes (RTU) / No (TCP) | +| **Unit Id** | Modbus device address (1–255). Required for reading. Writes default to `1` if omitted. For TCP, this is typically `1` unless connecting through a Modbus gateway. | Yes (for read) | | **Request Interval** | Interval between reads/writes in milliseconds (minimum: 1000). If not set, reads execute once on connection and writes execute on demand only. | Optional | | **Registers Amount** | Amount of registers to read/write (automatically determined from data type if not set) | Optional |