Skip to content

Modbus / PLC integration

This gateway ships Node-RED with node-red-contrib-modbus pre-installed — the node set for talking to PLCs and other devices over Modbus (serial or TCP). Filter the palette for "modbus" to see the full set:

Modbus nodes in the palette

Node Role
Modbus - Read Reads one or more registers/coils on a schedule or trigger
Modbus - Write Writes a value to a register/coil
Modbus - Getter Reads a register/coil in response to an incoming message
Modbus - Flex - Getter Like Getter, with more flexible addressing per-message
Modbus - Flex - Write Like Write, with more flexible addressing per-message
Modbus - Response Formats/handles the raw response from a read
Modbus - Response - Filter Filters responses, e.g. only passing on changed values
Modbus - Server Runs this gateway as a Modbus server other devices can query
Modbus - Queue - Info Reports on the internal request queue for a Modbus connection
Modbus - Flex - Connector A configurable low-level connector for custom polling patterns
Modbus - Flex - Sequencer Sequences multiple Modbus operations
Modbus - Flex - FC Sends a specific Modbus function code directly

Typical pattern

A basic PLC-read flow looks like: an inject node (or a timestamp/timer) triggers on an interval → a Modbus - Read node polls a register range from the PLC → a function or change node reshapes the result → an output node (an http request, mqtt out, or a write into SCADash via its API) sends the value onward.

Double-click a Modbus node to configure:

  • Connection — the PLC's address, port (502 for Modbus TCP), and unit ID. Connections are shared Configuration Nodes — set one up once and reuse it across multiple Modbus nodes.
  • Register type and address — which Modbus data area (coil, discrete input, holding register, input register) and the starting address to read or write.
  • Polling / trigger behavior — varies by node; Read nodes typically poll on an interval, Getter nodes respond to an incoming message instead.

Note

The exact addresses and register types depend entirely on your PLC's own Modbus map — consult the PLC's documentation for what's available at which address.

Connecting to a Modbus RTU device

Every Modbus node's Server field points at a shared connection (modbus-client) config node. Click the pencil icon next to Server to edit an existing one, or + to add a new one, then set Type to Serial or Serial Expert:

modbus-client config node, Type set to Serial Expert

Field Notes
Serial port The gateway's serial device path, e.g. /dev/ttyUSB0
Serial type RTU, RTU-BUFFERD, or ASCII
Baud rate 115200 down to 75 — must match the device
Data Bits 8, 7, 6, or 5
Stop Bits 1, 1.5, or 2
Parity None, Even, Mark, Odd, or Space
Connection delay (ms) Serial Expert only — a pause after opening the port before communicating, useful for RS-485 converters that need time to switch direction
Unit-Id Default slave address for nodes that don't override it per-message
Timeout (ms) How long to wait for a response before failing
Reconnect on timeout Automatically reopen the port after a timeout

For a TCP-connected device (or a Modbus TCP-to-RTU gateway), use Type: TCP instead — Host, Port (502 by default), and a TCP Type of RTU-BUFFERED if it's actually bridging to RTU devices on the other side:

modbus-client config node, Type set to TCP

Every serial RTU device sharing one port must use the same modbus-client config node — that's what makes the connection shared and serializes access to the bus, instead of two nodes fighting over the same serial port.

Example: reading and writing by function code with the Flex nodes

The Flex - Getter and Flex - Write nodes don't have fixed register/function-code settings in their edit dialog at all — they read the Modbus function code, address, and quantity from msg.payload on every incoming message instead. That makes them the right choice when you want to pick the function code dynamically (different registers per poll, a generic "read anything" flow, etc.) rather than hard-coding one read per node.

Reading (Flex - Getter)

A function node builds the request, a Modbus - Flex - Getter reads it, and a debug node shows the result:

function → Modbus Flex Getter → debug, wired together

The function node's code:

msg.payload = {
    fc: 3,        // function code: 3 = Read Holding Registers
    unitid: 5,    // RTU slave address of the device on the bus
    address: 0,   // starting register address
    quantity: 10  // number of registers to read
};
return msg;

Function codes accepted by Flex - Getter (all read operations):

fc Meaning
1 Read Coil Status
2 Read Input Status
3 Read Holding Registers
4 Read Input Registers

Double-click the Flex - Getter node itself and set its Server to the modbus-client connection for your RTU device (see above) — the node only needs that connection plus whatever comes in on msg.payload.

Writing (Flex - Write)

Same pattern, with a Modbus - Flex - Write node instead:

function → Modbus Flex Write → debug, wired together

// Single coil (FC5) — value must be true/false or 1/0
msg.payload = { fc: 5, unitid: 5, address: 0, quantity: 1, value: true };

// Single register (FC6) — value is one number, 0-65535
// msg.payload = { fc: 6, unitid: 5, address: 0, quantity: 1, value: 1234 };

// Multiple registers (FC16) — value is an array of numbers, 0-65535 each
// msg.payload = { fc: 16, unitid: 5, address: 0, quantity: 3, value: [100, 200, 300] };

return msg;

Function codes accepted by Flex - Write (all write operations):

fc Meaning value
5 Force Single Coil true/false or 1/0
6 Preset Single Register one number, 0–65535
15 Force Multiple Coils array of booleans
16 Preset Multiple Registers array of numbers, each 0–65535

As with Flex - Getter, point the node's Server field at your device's modbus-client connection, then trigger the function node (an inject node, an incoming value from elsewhere in the flow, etc.) whenever you need to write.

Note

unitid in the message overrides the connection's default Unit-Id — handy when several RTU devices share the same serial port and you're picking which one to talk to per-message.