Modbus Read Task
Learn how to acquire data from Modbus TCP devices with Synnax.
For task lifecycle management, see the Task Basics page.
Task Configuration Reference
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Yes | - | Human-readable task name |
device | string | Yes | - | Key of the Modbus server device |
sample_rate | number | Yes | - | Samples per second (Hz) |
stream_rate | number | No | sample_rate | Rate data is streamed to Synnax (Hz), must be ≤ sample_rate |
data_saving | boolean | No | false | Enable permanent storage in Synnax |
auto_start | boolean | No | false | Automatically start task after configuration |
channels | array | Yes | - | List of input channel configurations |
Register Types Reference
Holding Register Input (holding_register_input)
holding_register_input)Reads from 16-bit read/write registers (Function Code 03). Typically used for configuration parameters and analog outputs that can also be read back.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
channel | number | Yes | - | Synnax channel key |
address | number | Yes | - | Modbus register address (0-65535) |
data_type | enum | No | float32 | Data type: int16, uint16, int32, uint32, float32 |
swap_bytes | boolean | No | false | Swap byte order within 16-bit words |
swap_words | boolean | No | false | Swap word order for 32-bit+ values |
Data Type Sizes:
int16/uint16: 1 register (2 bytes)int32/uint32/float32: 2 registers (4 bytes)
Input Register (register_input)
register_input)Reads from 16-bit read-only registers (Function Code 04). Typically used for analog sensor values like temperature, pressure, or flow.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
channel | number | Yes | - | Synnax channel key |
address | number | Yes | - | Modbus register address (0-65535) |
data_type | enum | No | float32 | Data type: int16, uint16, int32, uint32, float32 |
swap_bytes | boolean | No | false | Swap byte order within 16-bit words |
swap_words | boolean | No | false | Swap word order for 32-bit+ values |
Data Type Sizes:
int16/uint16: 1 register (2 bytes)int32/uint32/float32: 2 registers (4 bytes)
Coil Input (coil_input)
coil_input)Reads from 1-bit read/write coils (Function Code 01). Typically used for discrete output states that can be read back (e.g., relay states, valve positions).
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
channel | number | Yes | - | Synnax channel key |
address | number | Yes | - | Modbus coil address (0-65535) |
Data Type: Always boolean (0/1)
Discrete Input (discrete_input)
discrete_input)Reads from 1-bit read-only discrete inputs (Function Code 02). Typically used for binary sensor inputs (e.g., limit switches, proximity sensors).
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
channel | number | Yes | - | Synnax channel key |
address | number | Yes | - | Modbus discrete input address (0-65535) |
Data Type: Always boolean (0/1)
Important Rules
- Sample rates: All channels in a task sample at the same rate. Create separate tasks for different rates.
- Software timing: Modbus tasks use software timing with ~100 μs precision (may degrade under heavy load).
- One running task per channel: A channel can only receive live data from one task at a time.
- Stream rate optimization: For low-rate tasks (< 50 Hz), set the stream rate to the sample rate. For high-rate tasks, keep the stream rate less than 50 Hz for better performance.
- Byte/word order: Ensure swap settings match your server configuration for multi-register data types.
How-To
Configure and run task
import synnax as sy
from synnax.hardware import modbus
client = sy.Synnax()
# Retrieve device
dev = client.hardware.devices.retrieve(name="Modbus Server")
# Create index channel
modbus_time = client.channels.create(
name="modbus_time",
is_index=True,
data_type=sy.DataType.TIMESTAMP,
retrieve_if_name_exists=True,
)
# Create data channels
input_reg_0 = client.channels.create(
name="input_register_0",
index=modbus_time.key,
data_type=sy.DataType.UINT8,
retrieve_if_name_exists=True,
)
input_reg_1 = client.channels.create(
name="input_register_1",
index=modbus_time.key,
data_type=sy.DataType.UINT8,
retrieve_if_name_exists=True,
)
# Create and configure task
task = modbus.ReadTask(
name="Modbus Read Task",
device=dev.key,
sample_rate=sy.Rate.HZ * 10,
stream_rate=sy.Rate.HZ * 10,
data_saving=True,
channels=[
modbus.InputRegisterChan(
channel=input_reg_0.key,
address=0,
data_type="uint8",
),
modbus.InputRegisterChan(
channel=input_reg_1.key,
address=1,
data_type="uint8",
),
],
)
client.hardware.tasks.configure(task)
# Start task and read data
with task.run():
with client.open_streamer(["input_register_0", "input_register_1"]) as streamer:
for _ in range(10):
frame = streamer.read()
print(frame) Edit task configuration
# Retrieve existing task
task = client.hardware.tasks.retrieve(name="Modbus Read Task")
task = modbus.ReadTask(internal=task)
# Update task-level configuration
task.config.auto_start = True
task.config.stream_rate = int(sy.Rate.HZ * 5)
# Update first channel configuration
task.config.channels[0].address = 10
task.config.channels[0].data_type = "uint16"
# Update second channel configuration
task.config.channels[1].address = 11
task.config.channels[1].data_type = "uint16"
# Apply changes
client.hardware.tasks.configure(task) Configure and run task
import { Synnax } from "@synnaxlabs/client";
const client = new Synnax();
// Retrieve device
const dev = await client.hardware.devices.retrieve({ name: "Modbus Server" });
// Create index channel
const modbusTime = await client.channels.create({
name: "modbus_time",
isIndex: true,
dataType: "timestamp",
retrieveIfNameExists: true,
});
// Create data channels
const inputReg0 = await client.channels.create({
name: "input_register_0",
index: modbusTime.key,
dataType: "uint8",
retrieveIfNameExists: true,
});
const inputReg1 = await client.channels.create({
name: "input_register_1",
index: modbusTime.key,
dataType: "uint8",
retrieveIfNameExists: true,
});
// Create and configure task
const task = await client.hardware.tasks.create({
name: "Modbus Read Task",
type: "modbus_read",
config: JSON.stringify({
device: dev.key,
sample_rate: 10,
stream_rate: 10,
data_saving: true,
channels: [
{
type: "register_input",
channel: inputReg0.key,
address: 0,
data_type: "uint8",
},
{
type: "register_input",
channel: inputReg1.key,
address: 1,
data_type: "uint8",
},
],
}),
});
// Start task
await task.executeCommandSync("start");
// Read data
const streamer = await client.openStreamer(["input_register_0", "input_register_1"]);
for (let i = 0; i < 10; i++) {
const frame = await streamer.read();
console.log(frame);
}
// Stop task
await task.executeCommandSync("stop");
await streamer.close(); Edit task configuration
// Retrieve existing task
const task = await client.hardware.tasks.retrieve({ name: "Modbus Read Task" });
// Parse and update configuration
const config = JSON.parse(task.config);
// Update task-level configuration
config.auto_start = true;
config.stream_rate = 5;
// Update first channel configuration
config.channels[0].address = 10;
config.channels[0].data_type = "uint16";
// Update second channel configuration
config.channels[1].address = 11;
config.channels[1].data_type = "uint16";
// Apply changes
await client.hardware.tasks.create({
key: task.key,
name: task.name,
type: task.type,
config: JSON.stringify(config),
});