OPC UA Sampling vs Publishing: Why Connected Doesn't Mean Good Data
How sampling interval, publishing interval, queue size, deadband and the KeepAlive/Lifetime pair decide whether your client and historian see the process.
The client shows a green connect icon, the endpoint is right, the certificate got trusted months ago — and the trip review still can't tell you which pump lost pressure first. The data was there. The subscription threw most of it away before the historian ever saw it.
That is the failure I keep finding, and it almost never shows up as a connection error. It shows up as a flat trend, a value that lags the panel by two seconds, or a historian full of noise on the wrong tags. All of it traces back to five subscription parameters that people leave at whatever the client stack defaulted to.
Sampling and publishing are two different clocks
This is the one that trips everyone, so it goes first. Per OPC UA Part 4, a monitored item has a SamplingInterval — how often the server checks the source value — and its subscription has a PublishingInterval — how often the server ships accumulated notifications to the client. They are not the same number and they do not have to be.
Say the server samples a pressure tag at 250 ms and publishes at 1000 ms. In that one-second window the server saw four samples. What the client gets depends entirely on QueueSize:
- QueueSize 1: the client gets the newest value. The other three are gone. Fine for an HMI faceplate.
- QueueSize 4 (or more): the client gets all four in one Publish response, each with its own source timestamp. That's what you want for the trip review.
Two settings people forget exist: SamplingInterval -1 means "use the publishing interval" and 0 means "as fast as the server supports." If you never set it, you're often quietly on one of those. And DiscardOldest (default true) decides which end of a full queue gets dropped — leave it true for trending, because losing the oldest sample of a ramp hurts less than losing the newest.
For an operator screen, latest-value-wins is correct and QueueSize 1 saves bandwidth. For sequence-of-events reconstruction, the queue matters more than the sample rate.
The server can quietly ignore what you asked for
You request 100 ms sampling. The server writes back RevisedSamplingInterval: 1000, because its MinSupportedSampleRate is 1 s, or because the underlying PLC scan is 1 s and there is no point sampling faster. Same story for RevisedPublishingInterval and RevisedQueueSize.
A lot of clients accept the revised value silently. So the first commissioning check is always: after CreateMonitoredItems, read the revised values back and confirm they match the request. If the server floored your 100 ms to 1 s, no amount of client-side tuning fixes it — the data does not exist that fast. Asking anyway just buys you false confidence.
Choose intervals by what the data is for, not by habit
Do not drop every tag into one 1-second subscription. Group by use:
| Signal class | Examples | Starting point |
|---|---|---|
| Fast status | Motor run feedback, valve travel, sequence step | 250–500 ms sampling, queue ≥ 4 if transitions matter |
| Operator analog | Pressure, flow, temperature on the main HMI | 500 ms–2 s, publish near display refresh |
| Slow analog | Tank level, room temp, utility total | 5–30 s unless control or alarm needs faster |
| Diagnostic | Device temperature, firmware state, port counters | 30–300 s, keep off the operator subscription |
| Event-like flag | Batch phase done, permissive dropped | Fast enough to catch the pulse — or latch it in the PLC |
The rule I hold to: a fast subscription has to name its reason — an operator need, an alarm, a historian analysis, or a commissioning test. When someone asks you to cut server load a year later, the tag without a reason is the first one you slow down.
Deadband in engineering units, not percent
DataChangeFilter cuts traffic, but a wide deadband hides real movement. I set DeadbandType Absolute over Percent almost every time, because I can compare an absolute number to the instrument's actual noise and the process tolerance. "5 mm" I can defend; "2%" I have to go compute.
- Tank level with ±2 mm of transmitter noise → 5 mm absolute deadband for the operator display.
- Pressure feeding a pump-trip analysis → much tighter, or no deadband at all during commissioning.
- A totalizer creeps up slowly, so a change-magnitude filter alone drops it for hours — force a periodic report on that one.
- Valve position feedback with a deadband wide enough that a drifting actuator still looks parked is worse than no filter.
And if OPC UA feeds a historian that also does compression, you now have two deadbands in series. Two layers strip more detail than either one looks like it should. Line them up on purpose.
KeepAlive and Lifetime: how fast a dead link is noticed
These decide whether operators stare at a frozen value thinking it's live. When nothing changes, the server still sends an empty keep-alive every MaxKeepAliveCount publishing intervals. If the client stops acknowledging, the server tears down the subscription after LifetimeCount intervals.
Part 4 says LifetimeCount must be at least 3× MaxKeepAliveCount, and clients should honor that. Concretely, with PublishingInterval 1 s and MaxKeepAliveCount 10, you get a keep-alive every 10 s and the subscription dies after LifetimeCount × 1 s of silence. Set those too long and a broken link looks healthy for a minute or more; set the lifetime too short relative to keep-alive and the server rejects the values on you. The number that matters operationally is the keep-alive period — that's your ceiling on how long a stale value can masquerade as current.
Check the timestamp and quality, not just the number
A moving number on the screen does not prove the data is fit for operations. Every value carries a source timestamp, a server timestamp, and a quality status — read all three during commissioning:
- Does the source timestamp advance when the PLC value changes, or is the server just stamping arrival time?
- Does the HMI make Bad and Uncertain quality obvious, or does it render the last good value as if it were current?
- Which timestamp does the historian store — source, server, or collector clock?
For sequence work, a timestamp rounded to 1 s is too coarse. For a slow utility total it's fine. Write the expected resolution into the tag standard instead of discovering it mid fault-review.
The mistakes that show up on every project
- One giant subscription with thousands of mixed-rate items.
- Sampling faster than the PLC or gateway can actually refresh — see the revised-value check above.
- QueueSize 1 on values later needed for event reconstruction.
- A deadband copied from another site without checking engineering units.
- Reconnect succeeds but monitored items return Bad after a namespace or NodeId change.
- KeepAlive and Lifetime so long that stale reads look normal.
- HMI and historian sharing one client config despite needing opposite things.
If the server exposes diagnostics, enable them: rejected monitored items, revised sampling intervals, publish request backlog, queue overflows, session reconnect count. Those numbers name your problem before an operator does.
A commissioning test you can run without lab gear
Pick a few representative tags and exercise them while watching the client diagnostics and the HMI/historian output.
- After creating the items, confirm the server did not revise sampling to something slower than you asked.
- Toggle a discrete faster than the publishing interval — does the client see every transition, or only the last?
- Ramp an analog and compare the raw source against what the historian actually stored.
- Force Bad quality or pull a device offline, and confirm the HMI does not keep showing the last good value as current.
- Restart the client and confirm subscriptions rebuild with the same intervals, queues, filters, and quality handling.
If a tag fails step 2 or 4, you found a subscription that lies about the process — better to find it on a bench toggle than during the next trip investigation.