Usage guide
Available nodes
| Node | Purpose |
|---|---|
kvhostlink-connection |
Shared KEYENCE KV Host Link TCP/UDP connection config. |
kvhostlink-read |
Reads one or more high-level addresses into msg.payload. |
kvhostlink-write |
Writes one or more high-level address/value updates. |
kvhostlink-connection config node
| Field | Required | Default | Description |
|---|---|---|---|
| Name | No | Empty | Display-only label. Empty/whitespace/non-string values mean no custom label; duplicates are allowed and never identify a connection or PLC route. |
| Host | Yes | Empty | IPv4 PLC address, or a host name that resolves to IPv4. IPv6 is unsupported. |
| Port | Yes | 8501 |
TCP or UDP port. |
| Transport | Yes | tcp |
tcp or udp. |
| Timeout ms | Yes | 3000 |
One absolute active connection/request deadline in milliseconds. |
| PLC Profile | Yes | keyence:kv-x500 |
Canonical lowercase profile value. |
Accepted profile values are listed in PLC profiles.
The values shown in a newly created editor node are initial form values. A
saved flow must retain explicit port and transport fields; the runtime does not
repair missing values.
Port and timeout must be decimal integer text within their documented range.
The connection node converts that form text once at the Node-RED boundary;
direct HostLinkClient construction requires actual safe JavaScript integers
and never coerces numeric strings.
The profile must equal one canonical lowercase identifier exactly; aliases,
case changes, surrounding whitespace, and object/string coercion are rejected.
IPv6 literals and host names without an IPv4 result are rejected. When a host
name has several IPv4 results, the first resolver result is used.
Bracketed IPv4 input such as [127.0.0.1] is invalid and is rejected during
client construction before DNS or socket work. Remove the brackets and pass
127.0.0.1; host strings are not URI authority syntax.
The direct client's maintainer sendRaw API represents exactly one non-empty
ASCII command. A body containing CR, LF, or CRLF would represent more than one
frame and is therefore a protocol input error. The body is limited to 65,506
ASCII bytes, producing at most a 65,507-byte frame after the library appends its
single terminating CR. Empty, newline-containing, and oversized bodies are
rejected before FIFO admission, connection-state checks, DNS, socket creation,
connect, or send. Migrate bracketed IPv4 by removing the brackets; split a
multi-command raw string into separate sendRaw calls; and split an oversized
operation according to the PLC command's smaller command-specific limit rather
than bypassing this absolute frame boundary.
Performance notes
For stable local networks, UDP usually has the lowest latency. TCP is the safer default for remote or less predictable networks because the OS handles retransmission.
Reuse one kvhostlink-connection config node for repeated reads and writes.
Prefer reading one address list or one array address over many separate
single-address messages when one application result set can be read together.
Connection reuse and concurrent requests
Share one kvhostlink-connection config node between read and write nodes that
talk to the same PLC endpoint. The ordinary client places concurrent calls in a
strict FIFO, snapshots all effective inputs at admission, and holds a compound
read for one complete queue turn. Different connection clients progress
independently. A waiting call does not start its timeout until it becomes active.
Use the connection control messages connect, disconnect, and reinitialize
for deliberate connection control. Create separate connection config nodes only
when you intentionally want separate PLC sessions.
Once active, one monotonic timeout covers sending, response framing/receive, and
decoding. Partial writes, trickled response bytes, and phase changes do not
restart it. Most JavaScript client/helper calls accept a final { signal }
option. Canceling a waiting call removes it without sending; canceling active
I/O retires that connection generation. close() rejects active and waiting
work immediately, and callers must explicitly reconnect and submit new work.
Each TCP request exclusively owns one non-empty response line. Stale partial
data, an unsolicited line, or an additional line invalidates that socket rather
than becoming a later request's response. UDP reuses a successful socket and
local endpoint. Timeout, cancellation, malformed/additional response data,
unowned datagrams, close, or socket failure retire it; later work creates a
replacement from the already resolved IPv4 address and never resends the failed
request. Malformed decoder
output and mode responses other than exact 0 or 1 also invalidate the
supplying generation. PLC command errors E0 through E9 remain reusable.
Host Link TCP responses contain no request identifier. The client checks for already visible input before sending, but a non-conforming peer can still deliver an unrelated line after that check and before the current response; the library cannot prove that such a line belongs to another request. Opening and closing TCP for every healthy request would not add an identifier, but would repeat connection setup and teardown latency. The supported contract therefore reuses a healthy TCP stream, serializes requests, and retires it immediately when stale, additional, malformed, timed-out, cancelled, or failed input becomes observable.
kvhostlink-read node
| Config field | Description |
|---|---|
| Name | Optional display-only label; it is not sent, emitted as metadata, or used as the connection identity. |
| Connection | kvhostlink-connection config node. |
| Source | Literal text, msg, flow, global, or env. |
| Addresses | Literal address list when Source is str. |
| Output | object always returns an address-keyed object, array always returns an array, and value requires exactly one address. |
| RDC comment | Required when the resolved plan contains :COMMENT: select decoded text or raw buffer. |
| Encoding | Required only for text comments: exact utf8 or cp932. |
| Metadata | full, minimal, or off. |
| Errors | throw, msg.error, or second output. |
| Input 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.topic |
connect, disconnect, or reinitialize for connection control. |
msg.connect |
Set to true to connect. |
msg.disconnect |
Set to true to disconnect. |
msg.reinitialize |
Set to true to close and reconnect. |
| Output msg field | Description |
|---|---|
msg.payload |
Read result as an object, array, or scalar value. |
msg.kvhostlink |
Metadata when enabled. |
msg.error |
Error object when Errors is msg.error. |
The configured Source type is required. If a msg, flow, global, or env
reference cannot be evaluated, the operation fails before connecting; the
reference name is never treated as a literal PLC address or update.
A multi-entry read is completely prevalidated before its one aggregate FIFO turn. Compatible device groups run by first input appearance; addresses inside each group are sorted ascending, contiguous ranges are merged, and only protocol limits split a range. Result keys and values retain input order even though wire order is optimized. A scalar, dword, float, array, or other declared entry is never torn between requests. The operation stops on first failure and returns no partial object. Multiple requests are not an atomic/coherent PLC snapshot because the PLC may change between them. Use one protocol request or a PLC-side sequence/handshake when values must describe one instant. Polling compiles this plan once, holds one FIFO turn per complete cycle, and waits the interval after releasing that turn.
RDC comment bytes and text
Every RDC response is first an exact byte body without CR/LF. Raw buffer
output returns those bytes unchanged, including trailing ASCII padding spaces.
Text output requires the exact saved encoding utf8 or cp932; cp932 is the
Windows-31J-compatible mapping commonly described by KEYENCE as Shift_JIS.
There is no separate strict Shift_JIS selection and aliases such as utf-8,
shift_jis, windows-31j, or auto are invalid public settings.
The node preflights the complete resolved address plan. Missing text encoding,
an encoding combined with raw Buffer output, or any unsupported value fails
before connection or send. Decoding is fatal: malformed bytes raise a protocol
error, invalidate the connection generation that supplied them, and never fall
back to another codec or insert replacement characters. Ambiguous bytes are
decoded only by the selected codec.
A leading UTF-8 BOM is payload data and decodes to U+FEFF; it is not silently
removed. The same bytes selected as cp932 are decoded only as CP932 and fail
when they are not a valid CP932 sequence.
For cp932, bytes 00 through 7F retain the identical ASCII code points;
80, A0, and FD through FF are invalid. Half-width and double-byte code
units are decoded strictly, including Windows-31J extension characters.
kvhostlink-write node
| Config field | Description |
|---|---|
| Name | Optional display-only label; it is not sent, emitted as metadata, or used as the connection identity. |
| Connection | kvhostlink-connection config node. |
| Source | Literal text, msg, flow, global, or env. |
| Static updates | JSON object when Source is str. |
| Metadata | full, minimal, or off. |
| Errors | throw, msg.error, or second output. |
| Input msg field | Description |
|---|---|
msg.updates |
Object or JSON string updates. |
msg.address |
Single address for one write. |
msg.dtype |
Required for a bare single-write address. Use exactly BIT, U, S, D, L, F, or H. Omit it when the address already contains a dtype or word-bit selector; specifying both is an error. COMMENT is read-only. |
msg.value |
Single write value. Required when msg.address is used. |
msg.topic |
connect, disconnect, or reinitialize for connection control. |
msg.connect |
Set to true to connect. |
msg.disconnect |
Set to true to disconnect. |
msg.reinitialize |
Set to true to close and reconnect. |
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.
Every update is validated before the first PLC request. Numeric values must be
finite JavaScript numbers in the selected format's exact range; strings,
Booleans, fractional integers, wraparound values, and Float32 overflow are
rejected. Float32 is valid only for ordinary one-word .U device families
that support consecutive two-word access. Direct-bit and special-response
families such as R, T, C, and AT reject it before FIFO admission or
transport. Direct-bit write values must be
actual JavaScript Booleans; numeric/string 0, 1, ON, and OFF are not
coerced. An empty update object performs no write and is rejected.
A complete writeNamed update set is snapshotted, compiled, and checked before
connecting. It is accepted only when it can be sent as one wire request within
the applicable limit: 1000 word points, 500 dword/Float32 points, or 120
timer/counter points. A plan requiring multiple requests fails as a whole before
transport. State-changing operations are never auto-split or auto-retried.
writeNamed never selects a bit-in-word read-modify-write implicitly. Library
consumers that deliberately require that policy can call the explicit helper:
await writeBitInWord(client, "DM150", 3, true);
The value must be a JavaScript Boolean, the index is 0..15, and the target
must be an ordinary 16-bit word device. Invalid plans fail before FIFO
admission. After activation, one absolute transaction deadline covers exactly
one word read followed by one word write in one FIFO turn; queue wait is outside
that deadline. The write is sent even if the requested state is unchanged.
There is no fallback, resend, success readback, or automatic retry.
The helper is not PLC-atomic. PLC logic or another connection can update the word between requests and its change can be lost. Use PLC-side coordination, a handshake, or exclusive complete-word ownership for stronger guarantees. Cancellation before the write begins sends no write; failure after write transmission may have started is outcome-unknown and requires deliberate reopen and PLC-state reconciliation. A complete PLC error is definitive and does not by itself retire a healthy connection.
Expansion-unit buffer memory uses a separate explicit route helper:
await writeBitInExpansionUnitBuffer(client, 1, 100, 3, true);
The unit/address and one .U word remain fixed across exactly one URD point
and one UWR point. Ordinary-device and expansion-unit routes never fall back
to one another. The shared-deadline, cancellation, outcome-unknown,
no-readback, and non-PLC-atomic rules are otherwise identical.
For R, MR, LR, and CR direct BIT updates, consecutive planning uses
the logical sixteen-bit bank order rather than the displayed decimal number.
For example, insertion-ordered R115:BIT then R200:BIT is one consecutive
request starting at R115; the two values retain that order. writeNamed does
not sort, deduplicate, fill a gap, or merge another device family or dtype.
Duplicates, reverse order, and non-consecutive logical bits therefore remain a
complete pre-transport rejection. Existing 00 through 15 display-bit
validation and the 1000-bit request limit still apply.
| Output msg field | Description |
|---|---|
msg.payload |
Original payload is passed through. |
msg.kvhostlink |
Metadata when enabled. |
msg.error |
Error object when Errors is msg.error. |
Success is always sent through output 1. throw reports failure through
done(error) without a message, msg.error sends the failed message on output
1, and Second output sends it only on output 2. The saved terminal count must
match the selected mode exactly; conflicting old flows are rejected for review.
Address syntax
| Form | Example | Meaning |
|---|---|---|
| Word value | DM100:U |
Read or write an unsigned word value. |
| Signed 16-bit | DM100:S |
Interpret one word as signed 16-bit. |
| Unsigned 32-bit | DM120:D |
Interpret two words as unsigned 32-bit. |
| Signed 32-bit | DM130:L |
Interpret two words as signed 32-bit. |
| Float32 | DM130:F |
Interpret two words as a 32-bit float. |
| Hex word | DM140:H |
Read or write a word as uppercase hexadecimal text. |
| Comment read | DM145:COMMENT |
Read explicit-codec text or the exact raw RDC body Buffer. |
| Bit in word | DM150.3 |
Read bit 3 in DM150; writing requires the explicit writeBitInWord(client, "DM150", 3, bool) API. |
| Word array | DM160:U,4 |
Read or write four consecutive unsigned word values. |
| Bit array | R200:BIT,4 |
Read or write four consecutive relay bits. |
| Timer preset | T10:D |
Read timer preset value. |
| Counter preset | C10:D |
Read counter preset value. |
Use : for data types and .0 through .F for bit-in-word access.
DM100.D means bit D inside DM100; use DM100:D for a 32-bit value.
High-level read/write addresses must specify the data type explicitly, such as :U, :D, or :BIT.
Each address contains exactly one complete selector: one dtype, or one word-bit
selector, followed by an optional positive safe-integer count only where that
form supports a count. BIT is limited to direct-bit device families.
Float32 F is limited to ordinary one-word .U families with consecutive
two-word access; direct-bit and special-response families such as R, T,
C, and AT are invalid. COMMENT is limited to devices supported by RDC.
Comment and word-bit forms do not accept a count.
The editor and runtime use this same grammar. Extra selectors or text before,
between, or after addresses is an error rather than being ignored.
The public parser, normalizer, formatter, JavaScript-array input, JSON-array
input, and delimited-list input apply the same device/data-type rules. Valid
list entries retain their trimmed spelling for result keys.
Named reads require one entry per semantic address. Case and leading-zero
variants, including an omitted versus explicit count of one, are duplicates.
Different dtypes or bit indexes and non-identical overlapping ranges remain
valid. Polling requires a positive integer interval of at most 2147483647
milliseconds; use at least 1, never zero.
Timer and counter
T10:D and C10:D use the high-level timer/counter behavior.
Reads return the preset value for compatibility with ordinary scalar reads.
Composite responses require exactly three fields: status, current, and preset.
Status is a structural field validated from the raw first token as exact 0 or
1; it remains numeric 0 or 1 and is never interpreted using the selected
numeric format. The requested .U, .S, .H, .D, or .L parser and bounds
apply only to current and preset. For example, T0.H may return
0,270F,270F, which decodes as [0, "270F", "270F"]. A non-exact status,
wrong field count, malformed current or preset, or out-of-range value is a
protocol error that retires the supplying transport generation.
Timer/counter preset writes use Host Link WS and WSS, which are supported only on KV-8000/7000-series CPU units.
Other CPU units may return PLC error E1.
Use TC, TS, CC, and CS when you want the timer/counter current/contact device families directly.
Low-level monitor words
The low-level registerMonitorWords API accepts a bare direct-bit target such
as "R5000". It sends the exact registration MWS R5000; the matching MWR
field is the unsigned 16-bit value packed from R5000 through R5015.
One through five ASCII decimal digits are accepted because the manual does not
guarantee fixed-width padding. 00013, for example, is returned as JavaScript
number 13. This special word
monitor behavior does not apply to bare scalar RD or MBS/MBR, whose
responses remain strict individual bits.
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 and
operation-outcome-unknown 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, and
cause provide the available diagnosis.
PLC profile selection and unsupported local input are validated before transport; such a failure 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
Both read and write nodes accept connection control messages.
| Message | Action |
|---|---|
msg.topic = "connect" |
Connect the shared connection. |
msg.topic = "disconnect" |
Disconnect the shared connection. |
msg.topic = "reinitialize" |
Disconnect, then connect again. |
msg.connect = true |
Connect the shared connection. |
msg.disconnect = true |
Disconnect the shared connection. |
msg.reinitialize = true |
Disconnect, then connect again. |
Operational recipes
The examples/flows/kvhostlink-multi-plc-monitor.json flow is a read-only multi-PLC monitor. It polls DM100: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.
The basic, typed, and array write buttons are manual controlled-test paths. Each saves the original value or snapshot, generates format-valid random test values, writes once, restores the saved state, and reads again. Restoration is best effort after communication failure. The device-matrix flow is read-only; its write controls and write router are deliberately disabled.
For config-driven polling, keep a JSON config in an Inject or Function node and feed msg.addresses into kvhostlink-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
| Metadata mode | msg.kvhostlink fields |
|---|---|
full |
Adds current operation, itemCount, metadataMode, connection, and current read addresses or write updates. |
minimal |
Adds current operation, itemCount, and metadataMode; owned full-mode fields are removed. |
off |
Leaves msg.kvhostlink unchanged. |
With off, a pre-existing msg.kvhostlink value is not guaranteed to describe
the current operation or result.
The connection metadata contains host, port, transport, and timeout.
Error handling
| Errors setting | Behavior |
|---|---|
Throw |
Calls Node-RED done(error). |
msg.error |
Adds the error object to msg.error and sends the message on output 1. |
Second output |
Sends normal messages on output 1 and error messages on output 2. |
JavaScript callers can classify failures by exported type/code:
| Error | Caller meaning |
|---|---|
ValueError |
Local input/configuration error; no send. |
HostLinkNotConnectedError |
Call connect() explicitly. |
HostLinkCanceledError |
Caller cancellation. |
HostLinkTimeoutError |
The absolute active deadline expired. |
HostLinkClosedError |
Explicit close rejected the operation. |
HostLinkConnectionError |
DNS/socket/transport failure. |
HostLinkProtocolError |
Invalid framing, bytes, tokens, or response shape. |
HostLinkError |
Complete PLC E0 through E9; inspect code. |
HostLinkOperationOutcomeUnknownError |
A state change may have reached the PLC; inspect reason and cause. |
An application may reconnect and retry a read according to its own policy. Do not automatically retry an outcome-unknown write: reconcile PLC/application state first, because repeating it can duplicate a state change.
Traffic statistics
Call client.trafficStats() to inspect completed framed sends and responses. Close/reconnect does not reset it.
For TCP, a received line counts its body plus the first CR/LF terminator; extra CR/LF separators
are consumed but not counted. For UDP, the complete response datagram is counted.