Modbus Adapter
The Modbus adapter connects Conduit to Modbus TCP and Modbus RTU devices, enabling data collection from PLCs, sensors, meters, and other field instruments.
Overview
The Modbus adapter supports:
- Modbus TCP: Ethernet-based Modbus communication
- Modbus RTU over TCP: Serial encapsulation over TCP
- All Register Types: Coils, discrete inputs, holding registers, input registers
- Multiple Data Types: Integer, float, string, BCD
- Polling: Configurable intervals per register group
Prerequisites
- Network Access: TCP connectivity to Modbus device
- Device Documentation: Register map for your device
- Unit ID: Modbus unit/slave address
Configuration
Basic TCP Configuration
adapter:
type: modbus
name: modbus-plc-1
connection:
type: tcp
host: 192.168.1.50
port: 502
unitId: 1
polling:
defaultInterval: 1000 # ms
RTU over TCP Configuration
adapter:
type: modbus
name: modbus-rtu-gateway
connection:
type: rtu-tcp
host: 192.168.1.60
port: 4001
unitId: 5
Register Configuration
Register Types
| Code | Type | Access | Address Range | |------|------|--------|---------------| | 0 | Coils | Read/Write | 00001-09999 | | 1 | Discrete Inputs | Read Only | 10001-19999 | | 3 | Input Registers | Read Only | 30001-39999 | | 4 | Holding Registers | Read/Write | 40001-49999 |
Defining Registers
registers:
- name: Tank1_Temperature
address: 40001
type: holding
dataType: float32
byteOrder: big-endian
unit: "°F"
description: "Tank 1 Process Temperature"
- name: Tank1_Level
address: 40003
type: holding
dataType: uint16
scale: 0.1
offset: 0
unit: "%"
- name: Pump1_Running
address: 00001
type: coil
dataType: bool
- name: Alarm_Status
address: 10001
type: discrete
dataType: bool
Data Types
| Type | Registers | Description |
|------|-----------|-------------|
| bool | 1 | Boolean (coils/discrete) |
| int16 | 1 | Signed 16-bit integer |
| uint16 | 1 | Unsigned 16-bit integer |
| int32 | 2 | Signed 32-bit integer |
| uint32 | 2 | Unsigned 32-bit integer |
| float32 | 2 | 32-bit float (IEEE 754) |
| float64 | 4 | 64-bit float (IEEE 754) |
| string | N | ASCII string |
| bcd | N | Binary-coded decimal |
Byte Order
Configure byte ordering for multi-register values:
registers:
- name: Temperature
address: 40001
dataType: float32
byteOrder: big-endian # AB CD (default)
# byteOrder: little-endian # CD AB
# byteOrder: mid-big # BA DC
# byteOrder: mid-little # DC BA
Polling Configuration
Group-Based Polling
pollGroups:
- name: fast
interval: 100 # ms
registers:
- Tank1_Temperature
- Tank1_Pressure
- Pump1_Running
- name: normal
interval: 1000
registers:
- Tank1_Level
- Flow_Rate
- name: slow
interval: 10000
registers:
- Daily_Production
- Runtime_Hours
Contiguous Read Optimization
optimization:
coalesceReads: true
maxGap: 10 # Combine reads up to 10 register gap
Scaling & Engineering Units
Linear Scaling
registers:
- name: Pressure
address: 40010
dataType: uint16
rawMin: 0
rawMax: 65535
engMin: 0
engMax: 100
unit: "PSI"
Formula: engValue = (rawValue - rawMin) / (rawMax - rawMin) * (engMax - engMin) + engMin
Custom Expression
registers:
- name: Temperature_C
address: 40020
dataType: int16
expression: "value * 0.1 - 40"
unit: "°C"
Connection Management
Reconnection
connection:
reconnect:
enabled: true
interval: 5000 # ms
maxAttempts: -1 # Infinite
timeout:
connect: 5000 # ms
read: 3000
write: 3000
Connection Pooling
connection:
pool:
enabled: true
size: 3
waitTimeout: 10000
Quality Handling
Communication Errors
quality:
onError: stale # Mark as stale on comm error
staleTimeout: 30 # seconds before marking bad
substitution:
enabled: false
value: 0
Value Range Validation
registers:
- name: Temperature
address: 40001
dataType: float32
validation:
min: -40
max: 200
onInvalid: bad # Mark quality as bad
Writing Values
Enable Writes
writes:
enabled: true
confirmation: true # Read back after write
timeout: 5000
Write Protection
writes:
protected:
- Pump1_Running # Require explicit unlock
- Setpoint_*
locks:
timeout: 60 # Auto-unlock after 60s
Multiple Devices
Device Groups
adapter:
type: modbus
name: modbus-building-1
devices:
- name: chiller-1
host: 192.168.1.50
port: 502
unitId: 1
registers: *chiller-registers
- name: chiller-2
host: 192.168.1.51
port: 502
unitId: 1
registers: *chiller-registers
registerTemplates:
chiller-registers: &chiller-registers
- name: Supply_Temp
address: 40001
dataType: float32
- name: Return_Temp
address: 40003
dataType: float32
Troubleshooting
Connection Issues
Connection Refused
- Verify IP and port
- Check firewall rules
- Confirm device is powered on
Timeout
- Reduce timeout values
- Check network latency
- Verify unit ID is correct
Data Issues
Incorrect Values
- Check byte order configuration
- Verify data type matches device
- Check scaling configuration
All Zeros
- Register address might be wrong
- Check register type (holding vs input)
- Some devices use 0-based addressing
Performance Issues
High Latency
- Enable contiguous read optimization
- Reduce polling frequency for slow-changing values
- Use multiple poll groups
Example: VFD (Variable Frequency Drive)
adapter:
type: modbus
name: vfd-pump-room
connection:
type: tcp
host: 192.168.1.100
port: 502
unitId: 1
registers:
- name: VFD1_Speed_Hz
address: 40001
dataType: uint16
scale: 0.01
unit: "Hz"
- name: VFD1_Current_A
address: 40002
dataType: uint16
scale: 0.01
unit: "A"
- name: VFD1_Power_kW
address: 40003
dataType: uint16
scale: 0.1
unit: "kW"
- name: VFD1_Running
address: 40010
dataType: uint16
expression: "(value & 0x01) != 0"
- name: VFD1_Fault
address: 40010
dataType: uint16
expression: "(value & 0x02) != 0"
pollGroups:
- name: status
interval: 500
registers: [VFD1_Running, VFD1_Fault]
- name: values
interval: 2000
registers: [VFD1_Speed_Hz, VFD1_Current_A, VFD1_Power_kW]
Next Steps
- MQTT Adapter - Connect to MQTT brokers
- OPC-UA Adapter - Higher-level protocol
- Architecture - How adapters work