Skip to content

Commit f452e46

Browse files
authored
Update modbus docs (#150)
1 parent d52c7c2 commit f452e46

File tree

1 file changed

+106
-64
lines changed

1 file changed

+106
-64
lines changed

docs/user-guide/agents-protocols/modbus.md

Lines changed: 106 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ The Modbus TCP and RTU agents allow integration of Modbus devices into OpenRemot
88

99
## How It Works
1010

11-
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.
11+
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)).
1212

1313
## Configuring a Modbus Agent
1414

@@ -19,112 +19,154 @@ You can configure either **Modbus TCP** or **Modbus RTU** agents:
1919
| Parameter | Description | Required | Default |
2020
| --------- | ------------------------------------------- | -------- | ------- |
2121
| **host** | IP address or hostname of the Modbus device | Yes | - |
22-
| **port** | TCP port number of the Modbus device | Yes | `502` |
23-
| **Unit ID** | Modbus device address (1–247 typically) | Yes |
22+
| **port** | TCP port number of the Modbus device | Yes | - |
23+
| **deviceConfig** | Device (unitID) specific settings, applies "default" setting unless a specific UnitID config is added | No | auto-populated with defaults |
2424

2525

26-
**Example:**
27-
28-
```yaml
29-
host: 192.168.1.50
30-
port: 502
31-
unitId: 1
32-
```
33-
3426
### Modbus RTU Agent
3527

3628
| Parameter | Description | Required | Default |
3729
| ------------------ |----------------------------------------------| -------- | ------- |
3830
| **serialPort** | Serial port identifier (e.g., `/dev/ttyUSB0`) | Yes | - |
39-
| **Unit ID** | Modbus device address (1–247 typically) | Yes |
40-
| **serialBaudrate** | Baud rate for serial communication | No | `38400` |
31+
| **serialBaudrate** | Baud rate for serial communication | No | `9600` |
32+
| **dataBits** | Number of data bits per frame | No | `8` |
33+
| **parity** | Parity bit setting (`NONE`, `ODD`, `EVEN`) | No | `EVEN` |
34+
| **stopbits** | Number of stop bits (`STOPBITS_1`, `STOPBITS_2`) | No | `STOPBITS_1` |
35+
| **deviceConfig** | Device (unitID) specific settings, applies "default" setting unless a specific UnitID config is added | No | auto-populated with defaults |
36+
37+
### Device Config
38+
While the above settings are applicable for all devices on the bus port, some settings can vary per device (unitId / modbus device address).
39+
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.
40+
For example, a specific config for unitID 1 would be:
41+
42+
**Device Config Example:**
43+
44+
```json
45+
{
46+
"default": {
47+
"endianFormat": "BIG_ENDIAN",
48+
"illegalRegisters": "",
49+
"maxRegisterLength": 1
50+
},
51+
"1": {
52+
"endianFormat": "LITTLE_ENDIAN",
53+
"illegalRegisters": "23,24,100-200",
54+
"maxRegisterLength": 40
55+
}
56+
}
57+
```
58+
Valid parameters are:
4159

42-
**Example:**
60+
| Parameter | Description |
61+
| ------------------ |----------------------------------------------|
62+
| **endianFormat** | Byte endianness: `BIG_ENDIAN` (ABCD), `LITTLE_ENDIAN` (DCBA), `BIG_ENDIAN_BYTE_SWAP` (BADC), `LITTLE_ENDIAN_BYTE_SWAP` (CDAB) |
63+
| **illegalRegisters** | Registers not supported by the device (e.g. `"23,24,100-200"`) |
64+
| **maxRegisterLength** | Maximum amount of registers in one request (default: `1`) |
4365

44-
```yaml
45-
serialPort: /dev/ttyUSB0
46-
serialBaudrate: 9600
47-
unitId: 1
48-
```
66+
### Batched requests
67+
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.
68+
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.
69+
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.
70+
The agent forms groups of batches based on **identical requestInterval, unitID, and readMemoryArea**.
4971

5072
## Linking Attributes via Agent Link
5173

5274
Each asset attribute can link to the Modbus agent via an **Agent Link**. Below are the parameters required:
5375

5476
| Parameter | Description | Required |
5577
|---------------------------|--------------------------------------------------------|----------------|
56-
| **Read Type** | Register type (`COIL`, `DISCRETE`, `HOLDING`, `INPUT`) | Yes (for read) |
5778
| **Read Address** | Register or coil address | Yes (for read) |
58-
| **Read Value Type** | Data type (`BOOL`, `INT`, `UINT`, `DINT`, `REAL`, etc.) | Yes (for read) |
59-
| **Write Type** | Register type for write (`COIL`, `HOLDING`) | Optional |
60-
| **Write Address** | Register or coil address for write | Optional |
61-
| **Write Value Type** | Data type for writing | Optional |
62-
| **Polling Interval** | Interval between reads in milliseconds | Optional |
63-
| **Read Registers Amount** | Amount of registers to read | Optional |
79+
| **Read Memory Area** | Register type (`COIL`, `DISCRETE`, `HOLDING`, `INPUT`) | Yes (for read) |
80+
| **Read Value Type** | Data type (`BOOL`, `INT`, `UINT`, `DINT`, `REAL`, etc.). Also used for multi-register write conversions. | Yes (for read)|
81+
| **Write Address** | Register or coil address for write | Yes (for write)|
82+
| **Write Memory Area** | Register type for write (`COIL`, `HOLDING`) | Yes (for write)|
83+
| **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) |
84+
| **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 |
85+
| **Registers Amount** | Amount of registers to read/write (automatically determined from data type if not set) | Optional |
6486

65-
**Important:** Modbus addressing in OpenRemote is zero-based.
87+
**Important:** Modbus addressing in OpenRemote is **1-based**. Address 1 maps to PDU address 0x0000.
6688

6789
**Example Attribute Link:**
6890

69-
```yaml
70-
Read Type: HOLDING
71-
Read Address: 24
72-
Read Value Type: UINT
73-
Write Type: HOLDING
74-
Write Address: 0
75-
Write Value Type: UINT
76-
Polling Interval: 5000
91+
```json
92+
{
93+
"readAddress": 24,
94+
"readMemoryArea": "HOLDING",
95+
"readValueType": "UINT",
96+
"writeAddress": 24,
97+
"writeMemoryArea": "HOLDING",
98+
"unitId": 1,
99+
"requestInterval": 5000
100+
}
77101
```
78102

79103
## Data Types
80104

81-
OpenRemote Modbus integration supports various data types through Apache PLC4X:
105+
OpenRemote Modbus integration supports various data types:
82106

83-
* **BOOL:** Boolean (single-bit)
84-
* **INT/UINT:** 16-bit integer (signed/unsigned)
85-
* **DINT/UDINT:** 32-bit integer (signed/unsigned)
86-
* **REAL:** 32-bit floating-point
87-
* **LREAL:** 64-bit floating-point
107+
* **BOOL:** Boolean (single-bit)
108+
* **SINT/USINT/BYTE:** 8-bit integer (signed/unsigned)
109+
* **INT/UINT/WORD:** 16-bit integer (signed/unsigned)
110+
* **DINT/UDINT/DWORD:** 32-bit integer (signed/unsigned)
111+
* **LINT/ULINT/LWORD:** 64-bit integer (signed/unsigned, terms as `long` or `BigInteger` may be used for very large values)
112+
* **REAL:** 32-bit floating-point
113+
* **LREAL:** 64-bit floating-point
114+
* **CHAR/WCHAR:** Character (single or wide, represented as `char` or `String`)
88115

89116
## Example Configuration
90117

91118
Here’s a full example configuration for a Modbus TCP temperature sensor:
92119

93120
**Agent Configuration:**
94121

95-
```yaml
96-
host: 192.168.1.50
97-
port: 502
98-
unitId: 1
122+
```json
123+
{
124+
"host": "192.168.1.50",
125+
"port": 502,
126+
"deviceConfig": {
127+
"default": {
128+
"endianFormat": "BIG_ENDIAN",
129+
"illegalRegisters": "",
130+
"maxRegisterLength": 1
131+
}
132+
}
133+
}
99134
```
100135

101136
**Agent Link Configuration:**
102137

103-
```yaml
104-
Read Type: HOLDING
105-
Read Address: 0
106-
Read Value Type: UINT
107-
Write Type: HOLDING
108-
Write Address: 0
109-
Write Value Type: UINT
110-
Polling Interval: 10000
111-
138+
```json
139+
{
140+
"readAddress": 23,
141+
"readMemoryArea": "INPUT",
142+
"readValueType": "REAL",
143+
"registersAmount": 2,
144+
"requestInterval": 60000,
145+
"unitId": 1
146+
}
112147
```
113148

114-
## Notes
149+
## Under the Hood
115150

116-
* The Modbus agent polls device data periodically based on the configured interval.
117-
* Write operations occur only when attribute values are explicitly changed in OpenRemote.
118-
* Double-check zero-based addressing, especially if your device documentation uses one-based indexing.
151+
The Modbus agent is written for openremote natively. This enables thorough integration into the openremote attribute event model.
152+
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.
153+
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.
119154

120-
## Under the Hood
155+
### Read/Write Interval & Combinations
121156

122-
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.
157+
| Read Configured | Write Configured | Request Interval Set| Resulting Behavior |
158+
| --------- | ------------------------------------------- | -------- | ------- |
159+
| yes | no | no | one time read after agent connects |
160+
| no | yes | no | only writes after attribute is manipulated |
161+
| yes | yes | no | one time read after agent connects, only writes after attribute is manipulated, then reads back. |
162+
| yes | no | yes | cyclic reads |
163+
| no | yes | yes | cyclic writes (re-sends the current attribute value each interval) |
164+
| yes | yes | yes | cyclic reads; writes are on-demand when the attribute value changes, followed by a verification read |
123165

124-
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).
125166

126-
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.
167+
**Note on addressing:**
168+
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).
127169

128-
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.
170+
**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.
129171

130-
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.
172+
**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.

0 commit comments

Comments
 (0)