Usage guide
Available nodes
| Node | Purpose |
|---|---|
slmp-connection |
Shared MELSEC SLMP connection profile for slmp-read and slmp-write. |
slmp-read |
Reads one or more device addresses and writes the result to msg.payload. |
slmp-write |
Writes one or more device values from static configuration or incoming messages. |
slmp-connection config node
| Config field | Description |
|---|---|
| Name | Optional display-only label. Empty/whitespace/non-string values mean no custom label; duplicate labels are allowed and never identify a connection or PLC route. |
| Host | PLC host name or IP address. For the examples, use 192.168.250.100. |
| Port | TCP or UDP port. Use 1025 for TCP examples and 1035 for UDP examples. |
| Transport | tcp or udp. |
| Timeout ms | Communication timeout in milliseconds. |
| PLC profile | Required canonical PLC profile. The current editor options are melsec:iq-f, melsec:iq-r, melsec:iq-r:rj71en71, melsec:iq-l, melsec:mx-f, melsec:mx-r, melsec:mx-r:rj71en71, melsec:lcpu, melsec:lcpu:lj71e71-100, melsec:qcpu:qj71e71-100, melsec:qnu, melsec:qnu:qj71e71-100, melsec:qnudv, and melsec:qnudv:qj71e71-100. |
| Use remote password | Explicitly enables the remote-password lifecycle. Leave unchecked when the PLC route does not use it. |
| Remote password | Required and non-empty when Use remote password is checked. Disabled otherwise. |
| Monitor timer | SLMP monitoring timer field in 250 ms units, 0 to 65535. A new node starts at 16 (four seconds). An explicit 0 requests an indefinite PLC-side processing wait. |
| Network | Target network number, 0 to 255. |
| Station | Target station number, 0 to 255. |
| Module I/O | Target module I/O number, entered as hexadecimal such as 03FF. |
| Multidrop | Target multidrop station number, 0 to 255. |
Requests that share one slmp-connection are sent in FIFO order. The next
request waits until the previous request has received a response, timed out, or
failed. This is intentional for SLMP compatibility because PLCs have
model-dependent limits for commands sent before earlier responses arrive.
Disconnect rejects both active and queued work and prevents queued work from
reconnecting after that close. A later, newly admitted operation may open a new
transport generation.
Concurrent disconnect/close() callers share one close flight. Managed
password locking and local transport close run at most once for that flight,
all callers observe the same result, and new connects or operations remain
blocked until the complete close has settled. A later sequential close remains
safe and idempotent.
TCP connections enable keepalive after 30 seconds idle. UDP timeouts discard the timed-out socket generation before a later request can open a new one. A UDP socket error also detaches and closes that generation so its local port and listeners are released; late events from it cannot affect a replacement socket. A UDP request completes only after both the socket send callback has succeeded and the matching response has arrived. A response received first is held provisionally. A later send failure discards it and invalidates the transport; request and byte counters are not published while the response is provisional. A missing callback or response remains bounded by the same absolute deadline.
The monitor timer and Timeout ms control different waits. The monitor timer
is sent to the PLC. Timeout ms is one local absolute transaction deadline
covering lazy connection, send completion, response framing/correlation, and
decode. Therefore,
even when the monitor timer is explicitly 0, the client can still end the
request when its communication timeout expires. Missing monitor timer settings
use 16; null, blank, Boolean, fractional, negative, non-finite, and
out-of-range values are configuration errors rather than defaults.
For parallel communication, use separate slmp-connection config nodes. Each
config node owns its own client connection and therefore its own request queue.
Remote password
Node-RED is the only SLMP package here with a connection-field remote password lifecycle.
When Use remote password is checked and a non-empty credential is set, the node unlocks after opening
and, when idle, tries to lock before disconnecting. If work is active or queued,
disconnect prioritizes deterministic cancellation and local close, so the PLC
lock state is unknown and disconnect reports SLMP_OPERATION_OUTCOME_UNKNOWN
with reason closed. The local transport is still closed. The field is disabled and not forwarded when the checkbox is
off. When it is on, iQ-R credentials must be 6–32 printable ASCII characters and Q/L credentials must
be exactly 4 printable ASCII characters.
If a concurrent disconnect occurs only after a response has already decoded,
the decoded success, write acknowledgement, or PLC end code remains the
operation result. The same result remains definitive if the local transaction
deadline passes afterward. An incomplete read reports SLMP_CLOSED. An incomplete
state-changing operation that may have been sent reports
SLMP_OPERATION_OUTCOME_UNKNOWN with reason closed.
Authentication belongs to one concrete TCP/UDP connection. A reconnect unlocks again before the first normal command; a failed normal command is never replayed automatically. Disconnect always closes the local transport. If the PLC rejects the final lock or the lock times out, the disconnect operation reports that failure and does not claim that the PLC is locked.
For C200-series password end codes, see the shared
SLMP Troubleshooting & Codes
page.
Routing / target station
Every saved connection explicitly contains all four route fields. The editor initializes a new connection for the directly connected own station. Change those values only when your PLC network is configured for another station, multi-CPU module I/O, or multidrop access.
Route fields control the SLMP destination header. They are not device family
selectors; routed devices such as Un\Gn and Jn\... still need their own
address syntax.
Per-request routing can be supplied from a message:
{
"target": {
"network": 1,
"station": 2,
"moduleIO": 1023,
"multidrop": 0
}
}
The same object can be placed in msg.slmp.target, or configured through the
Route source on slmp-read and slmp-write.
Runtime route objects use primitive integer Numbers for all four fields. String,
Boolean, boxed, and coercible values are rejected. Saved connection fields and
literal Route JSON are editor configuration: the runtime converts their decimal
fields and hexadecimal moduleIO field before calling the client, so the visible
own-station default remains 03FF. Dynamic msg, flow, global, and environment
route objects must already use Numbers.
Route priority is msg.target, msg.slmp.target, configured Route source, then
the connection route when no override is present. If a higher-priority property
or configured source exists but is invalid or missing its referenced value, the
operation fails; it does not continue to a different PLC route.
JavaScript code that calls the low-level client can use ModuleIONo constants
for moduleIO, for example ModuleIONo.MULTIPLE_CPU_2. Low-level clients must
receive a complete route; a missing or partial route is rejected.
For low-level iQ-R multi-CPU U3En\HG... access, the qualified device never
changes the request target. The application must explicitly select the target
CPU when a write must be reflected there. A write can return a normal end code
without changing the intended CPU buffer when the selected request target
identifies a different CPU or Own Station; cross-CPU reads remain valid. No
automatic target fallback, resend, readback, or retry is performed. See the
shared iQ-R target guidance.
Low-level SlmpClient users can register Word/DWord monitor devices with
registerMonitorDevices or registerMonitorDevicesExt, then execute one cycle
with explicit wordPoints and dwordPoints. selfTestLoopback(Buffer) and
clearError() provide fixed semantic system commands without raw command
numbers. The combined monitor count must be nonzero and cannot exceed the
selected profile's monitor-registration limit. These low-level operations are
not additional Node-RED node types.
slmp-read and slmp-write use the public high-level address parser for normal
device addresses. They do not expose Un\G, Un\HG, or Jn\... extended
device access as user-facing address forms.
slmp-read node
| Config field | Description |
|---|---|
| Name | Optional display-only label; it is not sent, emitted as metadata, or used as the connection identity. |
| Connection | The slmp-connection config node to use. |
| Source | Where to read the address list from: literal text, msg, flow, global, or env. |
| Addresses | Literal address list when Source is literal text. Use one address per line for clarity. |
| Route | Optional per-request route source: literal JSON, msg, flow, global, or env. |
| Route JSON | Literal route object with network, station, moduleIO, and multidrop. |
| Output | object always returns an address-keyed object, array always returns an array, and value requires exactly one address. |
| Metadata | full, minimal, or off for msg.slmp output. |
| Errors | Throw, attach to msg.error, or send the failed message to a second output. |
| Msg field | Description |
|---|---|
msg.addresses |
When present, a non-empty string or an array containing only non-empty address strings. Invalid input fails and never falls back to the configured Source. |
msg.target |
Per-request route override object. |
msg.slmp.target |
Per-request route override when msg.target is not set. |
msg.topic |
connect, disconnect, or reinitialize controls the shared connection instead of reading. |
msg.connect |
When true, opens the shared connection. |
msg.disconnect |
When true, closes the shared connection. |
msg.reinitialize |
When true, closes and reconnects the shared connection. |
| Output field | Description |
|---|---|
msg.payload |
Read result. Object mode is keyed by normalized address, array mode follows address order, and value mode returns a scalar for one address. |
msg.slmp.addresses |
Full metadata mode only: normalized address list. |
msg.slmp.connection |
Full metadata mode only: effective connection profile, frame type, target, and remote password status. |
msg.slmp.target |
Full and minimal metadata modes: effective route target. |
msg.slmp.targetSource |
Full and minimal metadata modes: msg.target, msg.slmp.target, configured.<type>, or connection. |
msg.slmp.itemCount |
Minimal metadata mode only: number of requested addresses. |
msg.error |
Error object when Errors is msg.error, or on the second output when Errors is second output. |
slmp-write node
| Config field | Description |
|---|---|
| Name | Optional display-only label; it is not sent, emitted as metadata, or used as the connection identity. |
| Connection | The slmp-connection config node to use. |
| Source | Where to read updates from: literal text, msg, flow, global, or env. |
| Static updates | Literal JSON object when Source is literal text. |
| Route | Optional per-request route source: literal JSON, msg, flow, global, or env. |
| Route JSON | Literal route object with network, station, moduleIO, and multidrop. |
| Metadata | full, minimal, or off for msg.slmp output. |
| Errors | Throw, attach to msg.error, or send the failed message to a second output. |
| Msg field | Description |
|---|---|
msg.updates |
Runtime update object, for example { "D300:U": 123, "M1000:BIT": true }. |
msg.address |
Single-address write path. |
msg.dtype |
Required for a bare single-write address. Use exactly BIT, U, S, D, L, F, or STR. Omit it when the address already contains a complete dtype or word-bit selector; specifying both is an error. |
msg.value |
Single-address write value. Required when msg.address is used. |
msg.target |
Per-request route override object. |
msg.slmp.target |
Per-request route override when msg.target is not set. |
msg.topic |
connect, disconnect, or reinitialize controls the shared connection instead of writing. |
msg.connect |
When true, opens the shared connection. |
msg.disconnect |
When true, closes the shared connection. |
msg.reinitialize |
When true, closes and reconnects the shared connection. |
Runtime write fields are authoritative when present. msg.updates and
msg.address are mutually exclusive, and msg.value/msg.dtype are valid only
with msg.address. Invalid, empty, conflicting, or isolated runtime fields fail;
the node does not execute configured updates as a fallback.
Single-write dtype must come from exactly one source. A colon or period in
msg.address must form a complete supported dtype/count or word-bit selector;
an incomplete or conflicting selector is not completed from msg.dtype.
Every write changes PLC state. Use controlled test addresses, save the original value before a reversible test, and restore only after confirmed acknowledgements. A timeout or transport failure after send can mean the outcome is unknown; do not retry or assume restoration succeeded. Stop the automatic sequence and reconcile the target value manually. The maintained write flows demonstrate manual opt-in, random format-valid test values, and best-effort snapshot restoration.
| Output field | Description |
|---|---|
msg.payload |
The incoming payload is preserved unless your flow changes it before the write. |
msg.slmp.updates |
Full metadata mode only: normalized update object. |
msg.slmp.connection |
Full metadata mode only: effective connection profile, frame type, target, and remote password status. |
msg.slmp.target |
Full and minimal metadata modes: effective route target. |
msg.slmp.targetSource |
Full and minimal metadata modes: selected route source. |
msg.slmp.itemCount |
Minimal metadata mode only: number of update addresses. |
msg.error |
Error object when Errors is msg.error, or on the second output when Errors is second output. |
Address syntax
| Form | Example | Meaning |
|---|---|---|
| Unsigned word | D100:U |
Unsigned 16-bit word. |
| Signed word | D100:S |
Signed 16-bit word. |
| Unsigned dword | D100:D |
Unsigned 32-bit value. |
| Signed dword | D100:L |
Signed 32-bit value. |
| Float | D100:F |
32-bit float. |
| String | D100:STR,10 |
UTF-8 string with a 10-byte maximum, packed two bytes per word. |
| Bit in word | D50.3 |
One bit inside a word device. |
| Direct bit | M1000:BIT |
One bit device. |
| Counted bit | M1000:BIT,8 |
One contiguous bit request. |
| Counted word | D100:U,4 |
Four word entries inside one Random Read request. |
Named addresses must include the intended type suffix, for example D100:U or M1000:BIT. The .bit form, such as D50.3, already declares bit-in-word access.
A textual ,count suffix contains ASCII decimal digits only and must represent
an exact positive JavaScript safe integer. Signs, embedded whitespace,
fractions, exponent notation, non-ASCII digits, suffix junk, zero, and values
above Number.MAX_SAFE_INTEGER are rejected instead of being rounded or
partially parsed. This syntax rule is separate from the lower command/profile
point limit, which is applied afterward. Code calling formatParsedAddress
with a hand-built parsed object must likewise provide a primitive positive
safe-integer Number when hasCount is true; numeric strings and coercible
objects are not accepted.
The complete accessed span must fit the selected SLMP device-number field:
24 bits for Q/L or 32 bits for ordinary iQ-R entries. A link-direct
J-qualified device always uses the 24-bit Q/L device specification, including
when the client profile is iQ-R. Ordinary DWord and float values consume
two consecutive word addresses. Low-level packed word access to a bit-device
family, and each Block bit point, consumes 16 consecutive bit-device numbers;
a low-level packed DWord consumes 32. Crossing the wire boundary is rejected
before communication. This check does
not impose the profile catalog's practical device ranges; applications may
enforce those separately when required.
Use only BIT, U, S, D, L, F, and STR. The removed compatibility
spellings :I, :STRING, and DSTR... are rejected. :BIT and counted bit
forms require a canonical bit device. Numeric and string forms require a
canonical word device; use .0 through .F for a bit inside a word device.
Explicit low-level word-unit access may still address a packed 16-bit word in a
bit-device family.
readNamed emits exactly one Random Read request or rejects the complete plan
before transport. Counted words, strings, DWord arrays, and packable bit entries
are expanded inside that one request and must fit the selected profile limit.
It never hides a long-timer Direct Read fallback; use readTyped or an explicit
long-timer helper for those routes. writeNamed must also fit one protocol
request and rejects mixed command families before transport.
For repeated reads with the same client, addresses, and request options, create
one prepareReadNamed(client, addresses, options) plan and call
await plan.execute({ signal }) each cycle. Preparation owns and validates the
Random Read payload and decode indexes once; each execution still uses normal
FIFO admission, a fresh serial and deadline, and current open/close state. The
plan is bound to that exact client configuration and must not be passed as the
addresses argument to readNamed. Call plan.dispose() when finished; it
releases retained planning references and rejects later executions without
cancelling an execution that is already active.
compileReadPlan is the separate inspectable structural expansion API and is
not directly executable. The Node-RED read node automatically retains no more
than one exact-match prepared plan.
Options such as a per-request target are forwarded by the high-level helpers.
Their compiled device/value lists, block lists, point counts, and bit/word route
remain authoritative; same-named caller option fields cannot redirect the
operation or replace its values.
Bit-in-word read-modify-write is not hidden inside writeNamed. The explicit
writeBitInWord helper snapshots and validates its arguments before queue
admission, prepares both request routes once, then holds one ordinary-client FIFO
turn and one absolute procedure deadline across its word read and word write.
It always sends both requests, including when the selected bit already has the
requested value. That prevents same-client interleaving only. The two requests are
not atomic at the PLC: another connection or PLC program logic can update the
word between them, and they can observe different PLC scans. If the write may
have been sent, its outcome is unknown after timeout, close, or transport
failure. The helper does not retry automatically; verify PLC state first.
Direct words use Direct Read/Write. Qualified U module-buffer and J link-direct
words use the already-supported Extended Random Read/Write route. The selected
route and qualification remain identical for both requests, and an unsupported
profile/route combination fails before the read.
Direct write values are not coerced: word/DWord values must be exact in-range
native JavaScript Numbers, float values must be finite and remain finite after
Float32 conversion, and bits must be native JavaScript Booleans. Numeric
strings such as "123", "1.5", and "1e3", boxed Numbers, BigInt,
Booleans, null, arrays, and coercible objects are rejected before queue
admission. STR still requires a string and BIT still requires a Boolean.
Convert configuration text explicitly before calling a numeric write helper.
Named writes also reject overlapping destinations. Extended random-read result
keys append +Zn, +LZn, or +INDIRECT when a modifier is present.
remoteReset confirms that the request frame was transmitted, closes the
current transport generation, and does not confirm PLC execution. Reconnect
and verify PLC state before issuing another operation.
Long device families
Use explicit 32-bit forms for long current-value families:
| Family | Use |
|---|---|
LTN |
Low-level readTyped/writeTyped with LTN0 and D or L |
LSTN |
Low-level readTyped/writeTyped with LSTN0 and D or L |
LCN |
LCN0:D or LCN0:L |
LZ |
LZ0:D or LZ0:L |
These are 32-bit families. Do not use direct plain word access for them; the
lower-level direct word commands reject that shape. readNamed and the
Node-RED read node accept Random-Read-compatible LCN and LZ entries, but
reject LTN and LSTN because they would require a hidden Direct Read route.
Node status and diagnosis
Node status is a concise progress indicator. Use the selected error route and its Error object for diagnosis; do not parse status text as an error code.
| Node / state | Fill | Shape | Exact text |
|---|---|---|---|
| Connection created | grey | ring | ready |
| Connection opening | yellow | ring | connecting |
| Connection open | green | dot | connected |
| Connection closing | yellow | ring | disconnecting |
| Connection closed by a disconnect operation | red | ring | disconnected |
| Connection closing and opening again | yellow | ring | reinitializing |
| Config node removed or runtime stopped | grey | ring | closed |
| Read in progress | blue | dot | reading |
| Write in progress | blue | dot | writing |
| Successful read or write | green | dot | N item(s) |
| Failed read, write, or control action | red | ring | The actual error.message |
A connect, disconnect, or reinitialize control message first shows a
yellow ring with that exact action name. Success then shows a dot with the same
text: green for connect and reinitialize, red for disconnect.
The failure status is deliberately dynamic. Timeout, operation-outcome-unknown,
and profile-capability failures are error classifications, not promised status
strings. Depending on the configured Errors mode, inspect the Error passed to
done(error) or the Error in msg.error on output 1 or output 2. Its
JavaScript type and structured fields such as code, reason, endCode,
errorInfo, and cause provide the available diagnosis.
A profile-capability rejection is local pre-transport validation; it does not prove that a PLC rejected a request. If a state-changing request may have been sent and its outcome is unknown, the node does not retry it automatically. Confirm PLC/application state with an appropriate read or operator procedure before deciding whether to issue another write.
Connection control messages
Send any of these fields to slmp-read or slmp-write:
| Msg field | Effect |
|---|---|
msg.topic = "connect" |
Opens the shared connection. |
msg.topic = "disconnect" |
Closes the shared connection. |
msg.topic = "reinitialize" |
Closes and opens the shared connection. |
msg.connect = true |
Same as connect. |
msg.disconnect = true |
Same as disconnect. |
msg.reinitialize = true |
Same as reinitialize. |
Operational recipes
The examples/flows/slmp-multi-plc-monitor.json flow is a read-only multi-PLC monitor. It polls D100:U, emits long-form rows shaped as timestamp,plc,tag,value, and uses connected, lost, reconnecting, and recovered state transitions with a 1 second to 30 second backoff.
For config-driven polling, keep a JSON config in an Inject or Function node and feed msg.addresses into slmp-read; no extra node type is required.
To persist CSV-equivalent rows, route the long-form row messages through a CSV node with timestamp, plc, tag, and value columns, then into a File node in append mode.
Metadata output
When Metadata is full, msg.slmp includes:
| Field | Description |
|---|---|
msg.slmp.addresses |
Normalized read addresses. Present on slmp-read. |
msg.slmp.updates |
Normalized write updates. Present on slmp-write. |
msg.slmp.connection |
Connection profile with host, port, transport, PLC profile, frame type, series, target, and remote password status. |
msg.slmp.target |
Effective request target after route overrides. |
msg.slmp.targetSource |
Selected route source and therefore why that target was used. |
When Metadata is minimal, msg.slmp includes operation, target,
targetSource, itemCount, and metadataMode.
When Metadata is off, the node leaves msg.slmp unchanged. Any pre-existing
value is not guaranteed to describe the current operation or result.
Error handling
Success is always sent through output 1. The selected error mode determines the
only failure route and therefore the saved terminal count: throw and msg
have one terminal, while output2 has two. A flow whose saved count conflicts
with the selected mode is rejected for migration review.
| Mode | Behavior |
|---|---|
| Throw | Calls Node-RED done(error) and lets the runtime route the error. |
msg.error |
Adds the error object to msg.error and sends the message on the normal output. |
| Second output | Sends the failed message with msg.error on output 2. |
For PLC response errors, read msg.error.endCode. When the PLC returned the
structured error-information block, msg.error.errorInfo includes command
and subcommand. Present structured error information must identify the active
request's route, command, and subcommand. A mismatch is
reported as a malformed SlmpError, invalidates the connection, and is never reported as
a definite PLC rejection. If the affected operation may have changed PLC state,
the public result is SLMP_OPERATION_OUTCOME_UNKNOWN with reason
malformed-response; verify state before considering another write. Additional
PLC data following a matching nine-byte error-information prefix remains
available on the structured PLC error.
Standard write, monitor-registration, remote-control, clear-error, password,
memory, extend-unit, and label APIs require an empty successful
acknowledgement. Any data after end code zero is malformed and produces the
same outcome-unknown classification and connection invalidation. The
maintainer-level rawCommand() API remains the explicit surface for commands
whose successful response may contain arbitrary data.
if (msg.error && msg.error.endCode !== undefined) {
node.warn(`SLMP end_code=0x${msg.error.endCode.toString(16).padStart(4, "0").toUpperCase()}`);
if (msg.error.errorInfo) {
node.warn(`command=0x${msg.error.errorInfo.command.toString(16).padStart(4, "0").toUpperCase()}`);
node.warn(`subcommand=0x${msg.error.errorInfo.subcommand.toString(16).padStart(4, "0").toUpperCase()}`);
}
}
Profile capability failures use SlmpProfileFeatureError. Select the exact PLC
profile and use a supported operation. Normal public APIs do not provide a
profile-check bypass or an unsupported-device skip switch.
Older Function nodes may still add msg.slmpSkipUnsupported or
msg.slmp.skipUnsupported. These inputs have been removed and produce a runtime
migration warning. They never change the selected error route. To continue a
flow after a specific capability error, select msg.error or the second output
and make that decision explicitly in the application flow.
Request payload limits
One SLMP request can carry at most 65,529 command-payload bytes over TCP. UDP must also fit one complete datagram, so the command-payload maximum is 65,492 bytes for 3E and 65,488 bytes for 4E. Array and random label requests use even-sized payloads and therefore have a largest protocol-representable payload of 65,528 bytes before the lower UDP limit is applied.
An oversized request raises ValueError before connection, send, traffic counters, trace state,
or 4E serial allocation. The client never truncates or automatically splits it; applications that
need several requests must define ordering, partial-success, and write-atomicity behavior explicitly.
Traffic statistics
The low-level SlmpClient.trafficStats() method returns a frozen client-lifetime snapshot with
requestCount, txBytes, and rxBytes. Complete sends and complete received frames are counted;
close and reconnect do not reset the snapshot.