Network variables get all the attention in LonWorks. They’re the clean part: bind an output to an input and values flow. But a lot of the traffic on a real channel isn’t network variables. It’s the commissioning tool installing nodes, a gateway tunnelling another protocol, a SCADA master reading status, and nodes answering all of it.
That traffic is explicit messages, network management and diagnostics. All of it is decoded from one byte. Once you can read that byte, a LonTalk capture stops being a wall of hex.
Everything here comes from ISO/IEC 14908-1. For the data model side, see the network variables article. For the big picture, the LonWorks protocol pillar.
Table of Contents
The first byte decides everything
Every application-layer packet (APDU) starts with a one-byte header the standard calls destin_type. Its top bits tell the receiver what kind of message this is.
| Bit pattern | Hex range | Message type | Codes |
|---|---|---|---|
00xxxxxx | 0x00–0x3F | Generic application message (explicit message) | 64 |
0100xxxx | 0x40–0x4F | Foreign frame | 16 |
0101xxxx | 0x50–0x5F | Network diagnostic | 16 |
011xxxxx | 0x60–0x7F | Network management | 32 |
1dxxxxxx | 0x80–0xFF | Network variable | — |
For network variables, d is the direction bit: 1 for outgoing, 0 for incoming. The other six bits join the next byte to make the 14-bit selector.
That’s the whole map. If the first byte is 0x80 or above, it’s a network variable. Below that, the next bits tell you which family it belongs to.
One catch: responses to network management and diagnostic commands also land in the 0x00–0x3F range. More on that below, because it’s the thing that confuses people reading captures.
Explicit messages: the application’s free channel
A generic application message is anything the application builds itself. The protocol doesn’t care what’s inside. The first byte is a message code from 0x00 to 0x3F, and the rest is data the two applications agree on.
What you get with explicit messages:
- Any addressing mode: subnet/node, group, broadcast, or Unique_Node_ID.
- Any service: acknowledged, request/response, unacknowledged, or unacknowledged repeated.
- Your own data format. Use this for things that don’t fit a network variable: block reads, commands with parameters, files sent in pieces.
What you don’t get: typing, binding by a tool, or any interoperability. Two devices only understand each other’s explicit messages if their vendors agreed on the format. That’s why network variables exist.
How big can a message be?
It depends on the buffers in the receiving node, not on the protocol. Each node reports its buffer sizes in its read-only data, encoded in four bits:
| Code | Bytes | Code | Bytes |
|---|---|---|---|
| 2 | 20 | 9 | 42 |
| 3 | 21 | 10 | 50 |
| 4 | 22 | 11 | 66 |
| 5 | 24 | 12 | 82 |
| 6 | 26 | 13 | 114 |
| 7 | 30 | 14 | 146 |
| 8 | 34 | 15 | 210 |
| 0 | 255 |
The floor is set by the standard: every compliant node must accept at least 16 bytes of data plus the destin_type byte. If you build an explicit message for a mixed network, size it for the smallest receiver, or split it.
Two more bits in the read-only data are worth checking when a node ignores your messages:
msg_process: set when the application can process explicit messages at all.explicit_addr: set when the application uses explicit addressing. Its application buffers then carry an extra 11-byte address field, which eats into the space for data.
Foreign frames: tunnelling other protocols
The 0x40–0x4F range is for foreign frames. These are messages that start and end outside the LonTalk world. The protocol carries them as a plain array of bytes and does no special processing. The standard names two uses: running LonTalk as a gateway between two outside devices, and tunnelling another protocol through LonTalk.
You’ll see this in the field. ABB’s substation LON uses this range for its own formats, including SPA-bus messages carried inside code 0x41. See the LonWorks in substations article for that case.
When the node’s application is offline
If you send a request to a node whose application is offline, the node still answers, but with a special code:
| Request type | What comes back |
|---|---|
| Explicit application message | destin_type = 63 (0x3F) |
| Foreign frame | destin_type = 79 (0x4F) |
| Network variable poll | The selector, with no data |
The network variable case is ambiguous. The same no-data response also means “no such selector on this node” or “the authenticated poll failed.” Three different faults look the same on the wire. If you need certainty, use NV Fetch (below), which works by index and doesn’t care whether the node is online.
Reading a response byte
This is where most capture confusion comes from.
Network management commands go out with the 011xxxxx header, so the five low bits are the command code. The response comes back as:
00pxxxxx
p= 1 if the command succeeded, 0 if it failed.xxxxxechoes the command code.
Diagnostic commands go out as 0101xxxx and come back as 00p1xxxx, same idea.
So for Query ID (command code 1):
| Byte | Meaning |
|---|---|
| 0x61 | Query ID request |
| 0x21 | Query ID response, success |
| 0x01 | Query ID response, failed |
And for Query Status (diagnostic code 1):
| Byte | Meaning |
|---|---|
| 0x51 | Query Status request |
| 0x31 | Query Status response, success |
| 0x11 | Query Status response, failed |
Notice that responses fall inside 0x00–0x3F, the same range as generic application messages. That’s on purpose. All NM and diagnostic requests are handled by the protocol’s network management layer. All responses are handed to the application, which is the network management tool. So the first byte alone doesn’t tell you if you’re looking at a response or a new application message. Look at the request it answers.
A node that doesn’t understand a network management request must return a failed response. Failures are usually table range errors or non-volatile memory write failures.
The network management commands
Here’s the full set, with the hex byte you’ll see on the wire.
| Hex | Command | What it does |
|---|---|---|
| 0x61 | Query ID | Asks nodes for their Unique_Node_ID and program ID |
| 0x62 | Respond to Query | Sets or clears a node’s “respond to query” bit |
| 0x63 | Update Domain | Writes a domain entry: domain ID, subnet, node, auth key |
| 0x64 | Leave Domain | Removes the node from a domain |
| 0x65 | Update Key | Changes the authentication key by increment |
| 0x66 | Update Address | Writes an address table entry by index |
| 0x67 | Query Address | Reads an address table entry |
| 0x68 | Query NV Config | Reads a network variable config entry (or an alias) |
| 0x69 | Update Group Address | Updates a group entry, addressed by group |
| 0x6A | Query Domain | Reads one of the node’s domains |
| 0x6B | Update NV Config | Writes a network variable config entry (or an alias) |
| 0x6C | Set Node Mode | Offline, online, reset, or change state |
| 0x6D | Read Memory | Reads node memory |
| 0x6E | Write Memory | Writes node memory |
| 0x6F | Checksum Recalculate | Rebuilds the config or application checksum |
| 0x70 | Install | Wink, dynamic NVs, self-documentation, extended commands |
| 0x71 | Memory Refresh | Rewrites non-volatile memory to extend retention |
| 0x72 | Query SI | Reads self-identification data held outside the protocol processor |
| 0x73 | NV Fetch | Reads a network variable’s value by index |
| 0x74–0x7C | Router commands | Mode, table clear/download/report, forward flags, status |
| 0x7D | NM escape | Vendor-specific commands, including Product Query |
| 0x7E | Router half escape | Passes the next command to the other side of a router |
| 0x7F | Manual Service Request | Unsolicited: “here’s my ID”, sent by the node |
The diagnostic set is short:
| Hex | Command |
|---|---|
| 0x51 | Query Status |
| 0x52 | Proxy |
| 0x53 | Clear Status |
| 0x54 | Query Transceiver Status |
How a node gets installed
Put the standard’s commands in order and a typical install looks like this. Tools differ in the details, but the building blocks are the same.
1. Find the node. Either someone presses the service pin and the node broadcasts a Manual Service Request (0x7F), or the tool sends Query ID (0x61) as a broadcast. Both carry the 48-bit Unique_Node_ID and the 8-byte program ID.
The Manual Service Request is the only unsolicited network management message. It goes out as a domain-wide broadcast on a zero-length domain, from subnet 0, node 0. That’s how an unconfigured node can be heard by a tool that knows nothing about it.
Query ID has a selector byte: 0 asks unconfigured nodes, 1 asks nodes with “respond to query” set, 2 asks nodes that are both.
2. Give it a domain. Update Domain (0x63) writes the domain ID, subnet, node number and authentication key.
3. Write the address table. Update Address (0x66), one entry per index. A standard node has up to 15 entries. Nodes that support the extended command set can have many more. No duplicate check is done, so the tool has to get it right.
4. Bind the network variables. Update NV Config (0x6B) sets each variable’s selector and which address table entry it uses. Address index 15 means “no address table entry.” A selector of 0x3FFF minus the variable’s index means “not bound.”
5. Fix the checksum. The address and domain commands update the configuration checksum themselves. For anything written by Write Memory, finish with Checksum Recalculate (0x6F).
6. Put it online. Set Node Mode (0x6C) to change the state to configured and then online. Reset the node if you changed communication parameters and want them to take effect.
Timing that catches tools out
Every write to non-volatile memory takes time, and the node recalculates its checksum after each addressing command. The delay can be as long as 20 ms per byte written. For an Update Address command, which changes 5 bytes, the transmit transaction timeout should be at least 20 × 5 + 30 = 130 ms. A tool with a short timeout will retry commands that actually succeeded.
When you address a node by Unique_Node_ID, the node stretches its receive transaction timer to about 8 seconds for that exchange. That lets slow configuration writes finish without tuning the normal timers for them.
Unique_Node_ID addressing has another advantage. If a node loses its configuration, for example from a power cut during a write, a tool using Unique_Node_ID can still reach it. Its old subnet/node address no longer means anything to the node.
Set Node Mode quirks
- Offline and online never return a response. The node accepts a request/response message, but the state change stops it from answering. Confirm with Query Status.
- Reset goes unacknowledged (or acknowledged if authentication is required). The standard sequence is: Clear Status, then reset, then Query Status. If the status doesn’t show a reset, the reset message was probably lost. Do it again.
- Routers ignore broadcast Set Node Mode commands. That stops a broadcast restart from taking down the router before the command reaches the far side. Restart or offline each router individually.
Reading the service LED
The node status indicator, the service LED, tells you the node state without a tool. The standard lists it as an optional diagnostic aid, with yellow as the usual colour.
| LED | State | Meaning |
|---|---|---|
| On steady | Applicationless | No valid application loaded |
| Flashing, 1 s rate | Unconfigured | Application loaded, no valid network config |
| Off | Configured or hard-offline | Normal |
A steady LED after a firmware download means the application is missing, still loading, or failed its checksum. A flashing LED on a device that used to work means its network configuration is missing or failed its checksum, or the node left its last domain. Either way, it has to be configured again.
Query Status: the node’s health report
Query Status (0x51) is the most useful diagnostic command. It never needs authentication. The response holds:
| Field | What it counts |
|---|---|
| transmission_errors | CRC errors and packets shorter than 8 bytes. Collisions, noise, attenuation. |
| transaction_timeouts | Acknowledged or request/response transactions that ran out of retries |
| receive_transaction_full | Incoming messages lost for lack of a receive transaction record |
| lost_messages | Messages addressed to the node, dropped for lack of an application buffer |
| missed_messages | Packets on the wire the node couldn’t take, for lack of a network buffer or one too small |
| reset_cause | Source of the last reset |
| node_state | State plus online/offline |
| version | Protocol firmware version |
| error_log | Last error logged |
| model_number | Protocol processor model |
How to read the counters:
- transmission_errors climbing on several nodes: wiring, termination or noise. On one node only: suspect that node’s transceiver or its drop first.
- transaction_timeouts climbing: the node is sending to someone who isn’t answering. Check bindings to removed devices first.
- lost_messages: the application can’t keep up. More application input buffers, or less traffic to that node.
- missed_messages: network buffers too few or too small. Often a node receiving bigger messages than it was built for.
The counters reset with Clear Status (0x53) and at every node reset. If you poll status regularly, clear after each read so the numbers mean “since last check.”
Reset cause
| Value (binary) | Cause |
|---|---|
XXXXXXX1 | Power-up |
XXXXXX10 | External reset |
XXXX1100 | Watchdog |
XXX10100 | Software |
Repeated watchdog resets usually point at the application rather than the wiring.
Node state byte
| Byte | Meaning |
|---|---|
| 0x02 | Unconfigured |
| 0x03 | Applicationless |
| 0x04 | Configured, online |
| 0x06 | Hard-offline |
| 0x0C | Configured, offline |
| 0x8C | Configured, offline in bypass mode |
Only the configured state receives application messages. In every other state they’re thrown away, but network management and diagnostics still work. A node at 0x0C is configured but offline: it answers requests with the offline codes covered earlier. A node at 0x06 is hard-offline: its application isn’t running at all.
Error log
0 means no error. 1 to 127 are application-specific. 128 to 255 are system errors. The ones you’ll actually meet:
| Code | Name | What it means on site |
|---|---|---|
| 0x82 | NV Length | An incoming update had the wrong length for the input. Usually a binding between mismatched types. The update is dropped. |
| 0x83 | NV Short | An update arrived missing the second selector byte |
| 0x84 | EE Write Fail | A non-volatile write didn’t read back correctly |
| 0x85 | Bad Address Type | Illegal type in the address table |
| 0x88 | Sync NV | A synchronous variable was overwritten before it could be sent |
| 0x8A | Invalid Domain | Message sent with a bad domain table entry. Discarded. |
| 0x8D | Invalid Address Index | Out-of-range address table index |
| 0x93 | Invalid NV Index | Out-of-range NV config or alias table index |
0x82 is the one to remember. If a bound input never updates and the source is clearly sending, read the error log on the receiver.
Proxy, NV Fetch and the other useful oddities
Proxy (0x52). Sends a status request or an unconfigured Query ID through another node, the agent. The agent relays it to the target and passes back the answer. Useful when the target is on a channel the tool can’t reach directly. Best when agent and target share a channel. Expect more failures than with direct requests, since the agent may be busy. The agent needs at least two application input buffers.
NV Fetch (0x73). Reads a network variable by its index instead of its selector, so it doesn’t depend on how selectors were assigned. It also returns the value whether the node is online or offline. When you’re debugging bindings, this is better than a poll.
Read Memory and Write Memory (0x6D, 0x6E). Keep reads to 16 bytes and writes to 11 bytes, unless you know the target has bigger buffers. If the node is read/write protected, only some areas can be read and only the configuration structure can be written. A read of absolute address 0 with a count of 1 returns the original system image version.
Memory Refresh (0x71). Rewrites non-volatile memory to extend how long it holds its data. Keep the count to 8 bytes when the target is online, up to 38 when it’s offline. A reset during a refresh can corrupt the configuration or the application, so only run it on stable power.
NM escape (0x7D). Opens vendor-specific commands. Any node that supports it must answer Product Query, which returns a product byte and, in the long form, the model number, firmware version, mode and transceiver type. Success shows as 0x3D, failure as 0x1D.
Keys and network management security
Two commands touch the authentication key, and they’re not equal.
- Update Domain sends the full 48-bit key in the clear. Anyone capturing the channel at that moment has it.
- Update Key sends an increment. The node adds it to the current key byte by byte, without carry. The real key never crosses the wire.
Use Update Key to change keys on a live network. Use Update Domain only when the wire is physically secure.
Authentication on network management itself is controlled by a bit in the node’s configuration. It’s honoured only in the configured and hard-offline states. Query ID, Respond to Query and Query Status never need authentication, whatever that bit says. If a tool loses track of which key a node has, the standard advice is to try the current key, then the previous one, until one works.
Leave Domain always clears the key for that domain. If it’s the node’s last domain, the node also goes to the unconfigured state and resets.
FAQ
What is the difference between an explicit message and a network variable?
A network variable is a typed data point that a tool binds between devices. An explicit message is a free-form packet the application builds itself. Network variables give interoperability. Explicit messages give flexibility but only work between devices that agree on the format.
How do I tell a network management response from an application message?
By where it sits in the transaction, not by its first byte. Both use 0x00–0x3F. A response comes back answering a request. For network management, the low five bits echo the command code and bit 5 is the success flag. So 0x21 is a successful Query ID response, and 0x01 is a failed one.
What does the LonWorks service pin do?
It makes the node send a Manual Service Request (0x7F). That’s a broadcast carrying the node’s 48-bit Unique_Node_ID and program ID. The commissioning tool uses it to identify and install the node.
Why does my LonWorks node’s service LED flash?
A flash at about one second means the node is unconfigured. Its application is loaded, but its network configuration is missing or failed its checksum. It needs to be reinstalled by the commissioning tool.
Which LonTalk command shows communication errors on a node?
Query Status (diagnostic code 0x51). It returns transmission errors, transaction timeouts, lost and missed messages, the last reset cause, the node state and the last logged error. Clear Status (0x53) resets the counters.
Why don’t I get a response when I set a node offline?
That’s by design. Set Node Mode with offline or online never returns a response, because the state change stops the node from answering. Check the result with Query Status.
Is it safe to change LonWorks authentication keys over the network?
Use the Update Key command. It sends only an increment, so the real key isn’t exposed. Update Domain sends the full key in the clear and should only be used on a physically secure network.
