sondahub / OPC UA test server
OPC UA test server
A public OPC UA server with a bottling line behind it — values that move with the plant, methods that start the pump, events when the conveyor jams, seven days of history — over the WebSocket mapping of OPC UA, free and open.
SecurityPolicy None, anonymous or any user. One connection, one server: nothing you write reaches anyone else.
Connect
- Endpoint
- opc.wss://api.sondahub.com/opcua
- Transport
- WebSocket, subprotocol opcua+uacp (OPC UA Part 6)
- Security
- SecurityPolicy None, MessageSecurityMode None
- Sign-in
- anonymous, or any user name and password (viewer: read-only; denied: refused)
- Namespaces
- 0 http://opcfoundation.org/UA/
1 urn:sondahub:opcua:server
2 http://sondahub.com/UA/Plant/ - Node ids
- ns=2;s=Line1.Tank101.Level — the path, readable
Each WebSocket message carries one UA Connection Protocol chunk — HEL, OPN, MSG, CLO — exactly as they travel over TCP. Chunks up to 64 KB, messages up to 4 MB, and answers chunked to the buffer your client announced in its Hello. One connection, one server: the plant, your writes and your subscriptions live as long as the connection does.
The plant
Under Objects/Plant: bottling line 1 — a mixing tank that fills and drains on a 25-minute cycle, a transfer pump that runs about 70 % of every 15 minutes, a twelve-head filler, a conveyor that jams for 40 seconds every 20 minutes — the utilities, and a Simulation folder for testing a client’s handling of every type. Analog variables are AnalogItems with EURange and EngineeringUnits (UNECE codes); valves are TwoStateDiscretes; the conveyor state is a MultiStateDiscrete.
Line 1
Bottling line 1: mix, pump, fill, convey.
| ns=2;s= | Access |
|---|---|
| Line1.Tank101 Object | Mixing tank, 5 m tall. |
| Line1.Tank101.Level Double | read, history Fills while the inlet valve is open, drains through the outlet. Write the valves and it follows them. |
| Line1.Tank101.Temperature Double | read, history |
| Line1.Tank101.Pressure Double | read |
| Line1.Tank101.InletValve Boolean | read, write Write true to open, false to close. Once you touch a valve, the tank is yours: the level follows the two valves from then on. |
| Line1.Tank101.OutletValve Boolean | read, write Write true to open, false to close. Once you touch a valve, the tank is yours: the level follows the two valves from then on. |
| Line1.Tank101.HighLevelAlarm Boolean | read Above 4.5 m. Raises an event on the Server object. |
| Line1.Tank101.Agitator Object | |
| Line1.Tank101.Agitator.Running Boolean | read |
| Line1.Tank101.Agitator.Speed Double | read |
| Line1.Tank101.Agitator.SpeedSetpoint Double | read, write 0–300 rpm; outside that, BadOutOfRange. |
| Line1.Tank101.Agitator.Start Method | call () |
| Line1.Tank101.Agitator.Stop Method | call () |
| Line1.Pump101 Object | Transfer pump, tank to filler. Runs about 70 % of every 15 minutes on its own. |
| Line1.Pump101.Running Boolean | read |
| Line1.Pump101.Speed Double | read, history Ramps over six seconds after a start or a stop. |
| Line1.Pump101.SpeedSetpoint Double | read, write 0–1800 rpm. Flow, current and vibration follow the speed. |
| Line1.Pump101.Flow Double | read, history |
| Line1.Pump101.MotorCurrent Double | read |
| Line1.Pump101.Vibration Double | read Spikes above 5 mm/s for a minute every hour while running — an event says so. |
| Line1.Pump101.RunHours Double | read |
| Line1.Pump101.Starts UInt32 | read |
| Line1.Pump101.Start Method | call () Starts the pump and keeps it running: from now on the pump is yours, not the schedule’s. |
| Line1.Pump101.Stop Method | call () |
| Line1.Pump101.SetSpeed Method | call (Speed: Double) → Accepted: Boolean Sets the speed setpoint, the way a method does it: arguments checked one by one. |
| Line1.Filler Object | Twelve-head filler. |
| Line1.Filler.FillSetpoint Double | read, write 100–2000 mL. |
| Line1.Filler.ActualFill Double | read |
| Line1.Filler.Heads Double[] | read What each of the twelve heads filled last, in mL. |
| Line1.Filler.Recipe String | read, write |
| Line1.Filler.BottlesFilled UInt32 | read |
| Line1.Filler.Rejects UInt32 | read |
| Line1.Conveyor Object | Jams for 40 seconds every 20 minutes. |
| Line1.Conveyor.State UInt32 | read, write 0 Stopped, 1 Running, 2 Jammed, 3 Maintenance. Write 0, 1 or 3; a jam is not something you can ask for. |
| Line1.Conveyor.Speed Double | read |
| Line1.Conveyor.BottlesPerMinute Double | read |
| Line1.Conveyor.JamDetected Boolean | read |
| Line1.Conveyor.ResetCounters Method | call () Zeroes the filler’s BottlesFilled and Rejects. |
Utilities
| ns=2;s= | Access |
|---|---|
| Utilities.PowerMeter Object | |
| Utilities.PowerMeter.ActivePower Double | read, history |
| Utilities.PowerMeter.Energy Double | read |
| Utilities.PowerMeter.PowerFactor Double | read |
| Utilities.PowerMeter.Frequency Double | read |
| Utilities.PowerMeter.Voltages Double[] | read L1–L2, L2–L3, L3–L1 in volts. |
| Utilities.PowerMeter.Currents Double[] | read L1, L2, L3 in amperes. |
| Utilities.CompressedAir Object | |
| Utilities.CompressedAir.Pressure Double | read |
| Utilities.CompressedAir.CompressorRunning Boolean | read |
| Utilities.CompressedAir.DewPoint Double | read |
Simulation
Test variables: signals, every built-in type to read and write, arrays, methods.
| ns=2;s= | Access |
|---|---|
| Simulation.Dynamic Object | |
| Simulation.Dynamic.Sinusoid Double | read, history ±100, one cycle a minute. |
| Simulation.Dynamic.Sawtooth Double | read, history 0 to 100 every 30 seconds. |
| Simulation.Dynamic.Triangle Double | read 0 to 100 and back every 40 seconds. |
| Simulation.Dynamic.Square Boolean | read Flips every 10 seconds. |
| Simulation.Dynamic.Random Double | read A new value every second. |
| Simulation.Dynamic.RandomInt32 Int32 | read |
| Simulation.Dynamic.Counter UInt32 | read Seconds since 1970: one more every second. |
| Simulation.Dynamic.StepUp Int32 | read |
| Simulation.Dynamic.Timestamp UtcTime | read |
| Simulation.Static Object | One writable variable per built-in type. What you write is yours for the connection. |
| Simulation.Static.Boolean Boolean | read, write |
| Simulation.Static.SByte SByte | read, write |
| Simulation.Static.Byte Byte | read, write |
| Simulation.Static.Int16 Int16 | read, write |
| Simulation.Static.UInt16 UInt16 | read, write |
| Simulation.Static.Int32 Int32 | read, write |
| Simulation.Static.UInt32 UInt32 | read, write |
| Simulation.Static.Int64 Int64 | read, write |
| Simulation.Static.UInt64 UInt64 | read, write |
| Simulation.Static.Float Float | read, write |
| Simulation.Static.Double Double | read, write |
| Simulation.Static.String String | read, write |
| Simulation.Static.DateTime DateTime | read, write |
| Simulation.Static.Guid Guid | read, write |
| Simulation.Static.ByteString ByteString | read, write |
| Simulation.Static.XmlElement XmlElement | read, write |
| Simulation.Static.NodeId NodeId | read, write |
| Simulation.Static.StatusCode StatusCode | read, write |
| Simulation.Static.QualifiedName QualifiedName | read, write |
| Simulation.Static.LocalizedText LocalizedText | read, write |
| Simulation.Arrays Object | Writable arrays, and a 2 × 3 matrix. |
| Simulation.Arrays.BooleanArray Boolean[] | read, write |
| Simulation.Arrays.ByteArray Byte[] | read, write |
| Simulation.Arrays.Int32Array Int32[] | read, write |
| Simulation.Arrays.UInt32Array UInt32[] | read, write |
| Simulation.Arrays.DoubleArray Double[] | read, write |
| Simulation.Arrays.StringArray String[] | read, write |
| Simulation.Arrays.Matrix Int32[,] | read, write |
| Simulation.Methods Object | Methods to call: arguments checked one by one, failures on purpose, a slow one. |
| Simulation.Methods.Add Method | call (A: Double, B: Double) → Sum: Double A + B, two Doubles. |
| Simulation.Methods.Echo Method | call (Text: String) → Text: String |
| Simulation.Methods.Fail Method | call () Always fails with BadInternalError, for testing how a client reports it. |
| Simulation.Methods.Sleep Method | call (Milliseconds: UInt32) → Slept: UInt32 Answers after the time you give: for testing timeouts and requests that overlap. |
The Server object (ns=0;i=2253) has ServerStatus, NamespaceArray, ServerArray, ServiceLevel and ServerCapabilities, and the type trees under Types hold the object, variable, data and reference types the address space uses.
Services
| Service set | What answers |
|---|---|
| Discovery | GetEndpoints and FindServers, before or after a session; the endpoint offers anonymous and user name tokens. |
| Session | CreateSession, ActivateSession, CloseSession, Cancel. Up to ten sessions per connection, timeouts from 10 seconds to an hour. |
| View | Browse with every direction, reference type filter (with subtypes), node class mask and result mask; continuation points (ten per session) and BrowseNext; TranslateBrowsePathsToNodeIds; RegisterNodes and UnregisterNodes. |
| Attribute | Read of every attribute a node class has, with IndexRange on arrays and strings and the timestamps you ask for. Write of Value, type-checked strictly: a Double variable takes a Double. HistoryRead, raw and at-time. |
| Method | Call, with arguments checked one by one: inputArgumentResults say which one was wrong. |
| Subscription | CreateSubscription, ModifySubscription, SetPublishingMode, Publish, Republish, TransferSubscriptions (between sessions of one connection), DeleteSubscriptions. |
| MonitoredItem | CreateMonitoredItems, ModifyMonitoredItems, SetMonitoringMode, SetTriggering, DeleteMonitoredItems. |
Anything else — Query, AddNodes, HistoryUpdate — answers BadServiceUnsupported.
Subscriptions and events
Publishing intervals from 100 ms to a minute, sampling from 100 ms, queues up to 1,000 values. DataChangeFilter with the status, value and timestamp triggers and absolute or percent deadbands (percent on AnalogItems, against their EURange). Keep-alives, lifetimes, late publish requests, 32 messages kept for Republish, triggering links between items.
The Server object is an event notifier. Monitor its EventNotifier attribute with an EventFilter and the plant reports on its own: a conveyor jam and its end, the tank above 4.5 m and back, pump vibration above 5 mm/s (a minute every hour, while it runs), the pump starting and stopping, and a quality check every 30 seconds. Select clauses take the BaseEventType fields — EventId, EventType, SourceNode, SourceName, Time, ReceiveTime, Message, Severity; a where clause is accepted and not applied.
History
Variables marked history above keep seven days: Sinusoid and Sawtooth one value a second, the tank, the pump and the active power one every five or ten seconds. ReadRawModifiedDetails forward or backward (start after end), with numValuesPerNode as the page size and continuation points between pages — at most 1,000 values per call; ReadAtTimeDetails answers for each time asked. Processed and event history answer BadHistoryOperationUnsupported.
From a tool that only speaks TCP
UaExpert, asyncua, node-opcua, open62541 and most OPC UA tools speak opc.tcp:// only. The bridge is a small Node script that runs on your computer, listens on the usual TCP port and carries each message to the hub over WebSocket — nothing else happens in it, and nothing is installed:
curl -O https://sondahub.com/industrial/bridge.mjs node bridge.mjs opcua # then point the tool at opc.tcp://localhost:4840
Errors to test against
BadTypeMismatch | Write an Int64 to Simulation.Static.Int32. |
BadOutOfRange | Write 5000 to Line1.Pump101.SpeedSetpoint (0–1800). |
BadNotWritable | Write to Line1.Pump101.Speed. |
BadUserAccessDenied | Write or call as the user viewer. |
BadInvalidArgument | Call Add with a String: the second input argument result is BadTypeMismatch. |
BadInternalError | Call Simulation.Methods.Fail. |
| (your timeout) | Call Simulation.Methods.Sleep with up to 10,000 ms: the answer comes that late, and your client’s own timeout is what gets tested. |
BadIdentityTokenRejected | Sign in as denied. |
Questions
Which OPC UA clients can connect?
Any client with the WebSocket transport of OPC UA Part 6 (opc.wss://, subprotocol opcua+uacp) connects with the address alone. Most desktop tools and libraries — UaExpert, asyncua, node-opcua, open62541 — speak only opc.tcp://; run the bridge and point them at opc.tcp://localhost:4840.
Is there security?
SecurityPolicy None only: no certificates, no signing, no encryption — it is a public playground, and the endpoint says so. Sign in anonymously, or with any user name and password; the user viewer may only read, and the user denied is refused, so both paths of a client can be tried.
What happens to what I write?
It belongs to your connection: setpoints, valves, the pump you started, the static variables, your subscriptions. A second connection starts from the same plant you started from, and a reconnect starts fresh — a client that tries to re-activate its old session gets BadSessionIdInvalid and creates a new one, which is what a server restart looks like.
How far back does history go?
Seven days, for the variables marked history below: the plant’s own signals, replayed for any moment you ask. Raw reads go forward or backward with continuation points (at most 1,000 values per call), and reads at given times answer for each time. Writes do not enter history: it is the plant as it runs on its own.