· Engineering  · 29 min read

Why Pay for the OPC UA Server on a Beckhoff Controller?

Every Beckhoff controller already speaks ADS. OPC UA is a licensed server on top. Measured from the controller’s own figures: reading natively by address barely registered, up to 2500 values every 10 ms. A busy OPC UA server put its work in the PLC task. Resolving names in the runtime on every read cost up to 40% of its CPU. A look at what the OPC UA license buys, and where each way of reading puts its work.

Every Beckhoff controller already speaks ADS. OPC UA is a licensed server on top. Measured from the controller’s own figures: reading natively by address barely registered, up to 2500 values every 10 ms. A busy OPC UA server put its work in the PLC task. Resolving names in the runtime on every read cost up to 40% of its CPU. A look at what the OPC UA license buys, and where each way of reading puts its work.
Every Beckhoff controller already speaks ADS. OPC UA is an extra, licensed server on top. So what does the license buy you, and what does it cost the controller? We measured it from the controller’s own figures. Reading by address over ADS barely registered, up to 2500 values every 10 ms. Polled faster, the OPC UA server’s work landed inside the PLC task. Asked for 2500 variables every 10 ms, the OPC UA server answered 12 reads a second. ADS answered about 100, every read the client asked for.

1. The question

ADS is the protocol TwinCAT itself is built on. Engineering, the runtime and every Beckhoff tool talk to the controller over it. It is on every controller. It costs nothing extra.

OPC UA is something you add. Beckhoff’s server, TF6100, is a licensed TwinCAT function. Install it, license it, configure it, and any OPC UA client can browse the program’s variables by name. Standard protocol, standard security, standard tooling.

So when a project specifies OPC UA for a Beckhoff line, the question is worth asking out loud. Why pay for a second server on a controller that already has a native one?

The usual answer is interoperability. Often that answer is right. This article adds a question the license price does not show: what does each way of reading cost the controller itself?

A PLC runs its program in a fixed cycle and answers requests in the time that is left. Whatever a client asks for has to be paid for somewhere on that controller. Either inside the task that runs the machine or beside it. The interesting part is not only how much, but where the work lands and how it grows when you ask for more.

The Siemens article looked at the same kind of question from the client’s side. This one stays on the controller. A third article puts both vendors side by side with five more.

2. Before the first read

The license price is only part of what the OPC UA server costs. The rest is the effort of getting it running, and on Beckhoff hardware that effort is considerable.

On a Siemens controller, the OPC UA server is a setting in the device’s properties in TIA Portal. On our Linux-based Beckhoff controller it took four separate steps:

  1. Install it. Log in to the controller and install the server’s packages as root.

  2. Configure the server. This takes a separate configuration tool, outside the engineering environment.

  3. Configure the project. Back in TwinCAT engineering, enable the download of the symbol file in the PLC project’s properties. Then mark the variables the server should publish with an attribute in the PLC code. Anything unmarked stays invisible to OPC UA.

  4. License it. Generate a license request using the TAN Beckhoff sends on purchase. Email the request to an automated address. Receive a license file back, then install it manually in the engineering environment.

None of these steps is hard once you know it. Together they are a procedure someone has to document, and repeat on every controller.

ADS needs none of them. It is already running.

2.1. And after every change

The same difference shows up again when the program changes. Partway through this benchmark we added 2500 new variables to the running project and downloaded it to the CX9240.

Over ADS, the new variables were readable right after the download. No restart, no configuration. Our driver’s default mode, on-demand, keeps an address only until TwinCAT reports a program change, then looks the name up again.

Over OPC UA, they first had to be marked for publication in the PLC code. Even then, a browse of the server right after the download showed none of them, while the PLC was already running the new program. They appeared only after we restarted the controller. Beckhoff’s documentation describes the server reading the symbol file when TwinCAT starts. We did not find a setting that makes it reload after a download, though one may exist. We saw this once, on one controller with one TF6100 version.

Some integrators will call the first half of that a feature. With OPC UA, every published variable is a deliberate decision in the PLC project, and nothing leaves the controller by accident. With ADS, the PLC programmer does not have to think about the client at all. A variable can still be kept out of reach by marking it {attribute 'TcNoSymbol'}. OPC UA publishes nothing unless told to. ADS publishes everything unless told not to. Either can be what you want. What it costs is maintenance: with OPC UA, every new value a client needs is a change to the PLC project, and possibly a restart.

3. How I measured it

A client reads N integers from a TwinCAT 3 controller. N is 80, 500 or 2500. It either polls at a held rate (every 2 s, 500 ms, 200 ms, 50 ms, 20 ms or 10 ms) or subscribes to the values.

The values come in two shapes: elements of one array, and separately 2500 distinct variables. The array is never read as a whole. Each value is its own item in the read, addressing one element, so 2500 values are 2500 items either way. The difference turns out to matter a great deal for OPC UA.

While it does, I read the controller’s own figures beside the workload:

  • The PLC task’s execution time. The task runs every 10 ms, and the machine’s program lives here. Shown is how much the 95th percentile of its execution time grew, the slow cycles rather than the typical one.

  • TwinCAT’s CPU usage for work outside the task. Communication, symbol lookups, the OPC UA server’s own processing: anything the runtime does beside the program.

Every rate is held for 10 s and compared against an idle baseline. Every measurement runs three passes, in rotated order, with the driver exactly as it ships. Where the three passes disagree, the figure is not quoted. A positive control proves the figures can move at all before a flat row is read as "no cost". Without it, a quiet row could simply mean the probe was not looking.

Two controllers:

  • a CX9240, TwinCAT 3.1.2127, with the OPC UA server TF6100

  • a C6920, TwinCAT 3.1.1926, ADS only on this bench

So every OPC UA figure below comes from the CX9240. The C6920 shows how the ADS results change on a different machine.

4. Six ways to read a symbol over ADS

An ADS client names a variable by its symbol. The runtime serves memory by address. Every client has to turn one into the other somewhere. Where it does that turns out to matter a great deal.

The driver measured here can do it six ways (symbol-resolution=…​). Together they cover how ADS clients in general work. on-demand is our driver’s default. Other drivers might default to other modes:

Table 1. The six symbol-resolution modes.
ModeWhat happens on the wire

on-demand (ToddySoft default)

a symbol’s address is looked up the first time it is read and kept until TwinCAT reports a program change; reads go by address

table

the runtime’s whole symbol and type tables are fetched when connecting; reads go by address

per-request

the address is looked up again for every read, then read by address

by-name

the runtime is asked to read by symbol name, and resolves the name itself on every read

handle-on-demand

a handle is requested per symbol on first use and read through until the program changes

handle-per-request

for every read: request a handle, read through it, release it — what pyads’ `read_by_name does

Beside them I measured on-demand over Secure ADS, both with a pre-shared key and with certificates. On the OPC UA side, three security levels: None, Sign, and Sign and encrypt (Basic256Sha256). None of them made a measurable difference to the load on the controller. Secure ADS measured the same as plain ADS, and the three OPC UA levels measured the same as each other. For OPC UA that held on the client too: every security level read at the same rate.

5. Polling

5.1. ADS: reading by address costs next to nothing

on-demand, table and per-request all end in a read by address. On both controllers they kept the PLC task within about 3 µs of idle at its 95th percentile, wherever the passes agreed, and TwinCAT’s CPU figure at its idle level.

The heaviest case on the bench counts too: 2500 values every 10 ms. With on-demand, the CX9240 managed 99.7 reads a second there, at 7.7 ms per read.

These figures cover reading, not connecting. table does its work up front: it fetches the whole symbol and type tables every time it connects. On a large installation those tables can run to several hundred kilobytes. At one of our beta testers they were more than a megabyte. on-demand fetches only the symbols a client actually reads.

5.2. ADS: resolving names in the runtime costs its CPU

The other three make the runtime do more than find an address: read every value by name, or issue a handle for it and read through that. TwinCAT’s CPU figure shows it:

Table 2. TwinCAT CPU usage outside the PLC task: the largest figure where the passes agreed.
ModeCX9240C6920

by-name

40% (500 values, every 10 ms)

9% (2500 values, every 50 ms)

handle-per-request

10% (2500 values, every 500 ms)

12% (2500 values, every 50 ms)

handle-on-demand

7% (idle 5–8%)

about 1%

address-based modes

idle (4–7%)

idle (0%)

Same protocol. Same controller. Same values. Between the first and last rows, the only difference is how much work the runtime does for each value. At larger reads and faster rates the CX9240 went higher still, but its three passes disagreed by too much to quote a figure.

The two controllers put that work in different places. On the CX9240 the PLC task’s 95th percentile grows with it, by about 12 µs with a handle per read. On the C6920 it stays within 0.3 µs, and the work lands outside the task entirely.

handle-per-request deserves a second look. Request a handle, read, release it: a common Python library does exactly that in its read_by_name call. On the CX9240 it took 5.6 times as long as a read by address for 2500 values, and already at one read every 500 ms it kept 10% of TwinCAT’s CPU busy.

5.3. OPC UA: the task pays when the server works

OPC UA behaves differently from every ADS mode. Its cost shows up in the PLC task itself.

At slow rates the task does not notice it. Polled faster, wherever the three passes agreed, the task’s 95th percentile grew by 17 to 18 µs reading separate variables and by 25 to 37 µs reading array elements. Over the array, TwinCAT’s CPU outside the task rose to 15 to 17% at the fastest rates. ADS by address stayed within about 3 µs.

Many OPC UA cells disagreed between passes, more than for any ADS mode. The figures above are the stable ones, not the largest. The three security levels behaved alike.

5.4. ADS and OPC UA side by side

Same controller, the CX9240. ADS in its default mode against the OPC UA server, first for what polling costs the controller:

Table 3. What polling costs the controller. CX9240, growth of the PLC task’s 95th percentile. 🔒 encrypted and authenticated.
ADS, on-demandOPC UA

Slow rates

idle

idle

Fast rates

within about 3 µs

17–18 µs (separate variables), 25–37 µs (array elements)

Where the work lands

barely anywhere

in the PLC task

Security

Secure ADS 🔒 (TLS, signed and encrypted): no difference

OPC UA Sign, Sign and encrypt 🔒: no difference

🔒 marks the ways in that run with the channel encrypted and the client authenticated: Secure ADS, with certificates or a pre-shared key, and OPC UA with Sign and encrypt. Plain ADS and OPC UA with None run in the clear. Sign alone authenticates and protects against tampering but does not encrypt.

Then for what it delivers. Asked for a read every 10 ms:

Table 4. Reads per second achieved when asked for one every 10 ms, and the median time per read. CX9240, any OPC UA security level.
ValuesADS, on-demandOPC UA, separate variablesOPC UA, array elements, one item each

80

99.9/s, 2.4 ms

99.9/s, 5.1 ms

34/s, 29 ms

500

99.7/s, 4.5 ms

55/s, 18 ms

5.7/s, 174 ms

2500

99.8/s, 6.8 ms

12/s, 83 ms

1.1/s, 898 ms

The ADS figures are for separate variables. Over the array, ADS kept the same rate, at 7.7 ms per read of 2500.

ADS by address answers about 100 reads a second at every size. The test asked for exactly that many, so the figure is a limit of the test, not of ADS. A read took 2.4 ms to 6.8 ms, so the controller spent most of each 10 ms interval waiting for the next request.

The client sends one read at a time and waits for the answer. When a read takes longer than 10 ms, the next one starts as soon as it returns, so nothing is dropped or queued. Where the OPC UA figures fall short of 100, the server was answering back to back. They are real limits of the server.

For OPC UA, how the values are stored matters as much as the protocol. The same server answered 2500 separate variables about ten times as fast as 2500 array elements, each addressed on its own. Our first run read only the array, and put ADS more than sixty times ahead at 2500 values. Most of that gap came from the array, not from OPC UA as such.

Most systems on a shopfloor poll: historians, dashboards, SCADA. For them the fair comparison is the separate-variable column, and ADS still keeps a clear lead. At 80 values both kept up with a read every 10 ms. At 500 values ADS was about four times as fast. At 2500 it read the values in 6.8 ms, where the OPC UA server needed 83 ms, about twelve times as long. Asked once every 2 s instead of back to back, the gap at 2500 values was seven times: 13.5 against 92.5 ms.

6. Subscriptions

Subscribing moves the work into the controller. The client stops asking. The controller has to watch the values and report them.

A subscription is either cyclic, at an interval the client names, or on change. What a cyclic subscription does with its interval differs by protocol:

  • ADS sends every value, every interval, whether it changed or not.

  • OPC UA samples every value at the interval, then sends only the ones that changed.

The values did not change during the test. So it measures what watching costs and what a cyclic subscription delivers, not what delivering changes costs.

ADS delivers what it is asked, down to 10 ms. Every mode, Secure ADS included, delivered within about 1% of the requested rate on both controllers, up to 250,000 values a second for 2500 values every 10 ms.

OPC UA sent the initial values, then nothing. The server accepted all 2500 subscriptions. With static values it reports each value once and nothing after, at any requested rate. The protocol works that way; no server limit is involved.

What watching costs. For ADS in our driver’s default mode:

Table 5. What an ADS subscription costs the controller, on-demand, values not changing: growth of the PLC task’s 95th percentile and TwinCAT’s CPU outside the task. Idle CPU is 4–7% on the CX9240, 0% on the C6920 and 1% on the CX1010; the CX1010 was measured up to 500 values.
ValuesSubscriptionCX9240C6920CX1010

80

on change

+0.2 µs, CPU 5%

+0.0 µs, CPU 0%

+0.1 µs, CPU 1%

80

every 200 ms

not stable

+0.0 µs, CPU 0%

not stable

80

every 10 ms

not stable

+0.1 µs, CPU 0%

+0.3 µs, CPU 1%

500

on change

not stable

+0.0 µs, CPU 0%

not stable

500

every 200 ms

+3.5 µs, CPU 7%

+0.1 µs, CPU 0%

+0.7 µs, CPU 4%

500

every 10 ms

not stable

+0.1 µs, CPU 0%

not stable

2500

on change

not stable

+0.0 µs, CPU 1%

—

2500

every 200 ms

not stable

+0.1 µs, CPU 1%

—

2500

every 10 ms

not stable

+0.1 µs, CPU 1%

—

The C6920 does not notice any of it, even 2500 values every 10 ms. The CX1010 pays a little, and more CPU as the cycle tightens. The CX9240’s figures moved the most and disagreed the most between passes. Across all its ADS modes, the stable cells put 80 values at about 2 to 3 µs, 500 at 3 to 6 µs, and 2500 at around 9 to 12 µs, 27 µs with handles kept at 10 ms.

What delivering changes costs was not part of this run: the values held still throughout.

7. What it costs the network

So far every figure was about the controller. The same reads also cross the network, and there the protocol and the client’s habits show up in bytes.

A separate capture measured exactly the reads of the load runs on the wire: the same values, at 80, 500 and 2500 per read, every way of asking. The figures are whole Ethernet frames in both directions, acknowledgements included.

Table 6. Bytes on the wire per value, both directions. CX9240. 🔒 encrypted and authenticated.
How the client asks80 values500 values2500 values

ADS, on-demand, neighboring values combined into one read (our driver’s default)

5.7

2.6

2.2

  └ Secure ADS 🔒, certificates

7.0

2.8

2.3

  └ Secure ADS 🔒, pre-shared key

7.4

2.9

2.3

ADS, per-request

13.0

3.8

2.5

ADS, handle-on-demand

21.7

19.8

19.8

OPC UA, separate variables, None

44.9

42.6

42.0

ADS, by-name

52.1

52.2

52.1

OPC UA, array elements, None

54.4

51.4

51.3

  └ Sign

54.7

50.7

51.4

  └ Sign and encrypt 🔒

55.6

51.7

51.4

ADS, handle-per-request

103.1

97.6

96.1

Indented rows are the unindented row above them with security added. The two Secure ADS rows send the first row’s reads over TLS. Sign and Sign and encrypt are OPC UA’s two security levels on the same read.

An integer is two bytes. So the first row moves the data and almost nothing else. The client sees that the values sit next to each other and asks for the whole span at once.

OPC UA moves about 19 times the bytes ADS does for 2500 separate variables, 23 times over array elements, and eight to ten times for 80 values. Every OPC UA value travels with its node id and its own encoding. That overhead does not shrink as the read grows. ADS by address does: the fixed part of the request is spread over more values.

The ADS modes split the same way. per-request adds one lookup per read and ends up next to the default at large reads. The three modes that work by handle or by name carry something per value: handle-on-demand a small request of its own for every handle, by-name and handle-per-request the symbol name in every read.

Security adds little to either. Secure ADS adds about 105 to 145 bytes per read for its TLS framing, and nothing per value. OPC UA’s three security levels stay within a few percent of each other.

At the rates in this article that adds up, but not to a problem. 2500 values every 10 ms come to about 0.55 MB a second over ADS. Over OPC UA the same 2500 separate variables once a second come to about 0.1 MB a second. Those totals are our arithmetic from the bytes per read, not a separate measurement. Even the heavier one is a small fraction of the controller’s 100 Mbit/s port.

8. So is the OPC UA server worth it?

A disclosure first. I work on ToddySoft Connect. It provides both drivers measured here, the ADS one and the OPC UA one, and every number came out of its test suite. The OPC UA server on the controller is Beckhoff’s own. You should know that before you weigh my conclusions.

With that said, here is what the figures support.

If you need high-frequency data, the license buys you less of it. Polled every 10 ms, ADS delivered every read we asked for, at every size we tried. The OPC UA server kept up at 80 values. At 500 separate variables it managed 55 reads a second, at 2500 about 12. If your data needs to be fresh every few tens of milliseconds, that settles the question before the license price comes up.

If you poll, the native protocol is the one the controller barely notices. ADS by address kept the PLC task within about 3 µs of idle at every size and rate we tried. OPC UA left the task alone at slow rates. Polled faster, it cost the task 17 to 37 µs at its 95th percentile, depending on how the values were stored.

Skipping the license only helps if the ADS client reads well. Resolve once and read by address, and the controller stays idle. Ask the runtime to resolve on every read, and the same data can take up to 40% of TwinCAT’s CPU.

The mode you get is usually an implementation detail of your driver. The ToddySoft driver supports all six. Its default, on-demand, resolves each name once and then reads by address, which puts it in the rows that left the controller idle. If you are writing your own client, or choosing a library, find out which of the six it uses.

For subscriptions, the two do different jobs. ADS delivered every value at every cycle, down to 10 ms, and on the C6920 that cost nothing measurable. OPC UA reports only changes. What delivering them costs was not part of this run.

The license is not the whole bill. The setup is paid again on every controller: installed as root, configured in two places, licensed by email. Every new variable is then a change to the PLC code, and on our controller a restart. ADS is there from the start and picked up new variables as soon as they were downloaded.

ADS is also the lighter load on the network. Reading 2500 values, ADS moved 2.2 bytes per value and OPC UA 51, about 23 times the network volume. At 80 values it was 5.7 against 53.5, about nine times.

Security is not a reason to buy it. Secure ADS measured the same as plain ADS, with a pre-shared key and with certificates. The three OPC UA security levels measured the same as each other.

So the license buys neither speed nor a lighter load on the controller. What it buys is real all the same. Every other system on the floor can read OPC UA without knowing anything about Beckhoff. It brings an information model and security an auditor recognizes. Its cost in the task is measured in microseconds. If a system you cannot change only speaks OPC UA, that alone settles it.

If the client is yours, the choice is open. The Siemens article makes the longer case for putting the unified API in your application and letting each device speak what it speaks best. On a Beckhoff controller, what it speaks best is ADS.

The figures say the two ways in put their work in different places. A busy OPC UA server puts part of it in the task that runs your machine. ADS by address puts almost none anywhere. On a busy controller, weigh that before you weigh the license.

9. What to keep in mind

  • Two controllers, one of each. No figure here is a statement about TwinCAT in general. The two controllers already differ in where the work lands. On the CX9240, name resolution and ADS subscriptions both nudge the PLC task. On the C6920, neither does.

  • For OPC UA, how the values are stored matters. The same server answered 2500 separate variables about ten times as fast as 2500 array elements, each addressed on its own. Use the figure that matches how your project stores its values.

  • Many controller figures were not stable on the CX9240. Only cells where the three passes agreed are quoted. The CX9240’s largest figures often fell in the others.

  • The network figures come from a capture of the same reads as the load runs, at 80, 500 and 2500 values. The megabytes per second are our arithmetic from bytes per read, not a measurement of the network.

  • "Cyclic" does not mean the same on both sides. ADS cyclic subscriptions deliver every value at every interval. The OPC UA servers we tried delivered changes only, sampled at the interval.

  • Microseconds are small. The task’s cycle is 10 000 µs. The largest cost above, 37 µs, is under 0.4% of it. What these figures show is where a protocol’s work lands and how it scales, not that either one will stretch a 10 ms cycle on its own.

10. The point

The license is the cost you see. The other one lands on the controller.

Read natively by address, and the controller hardly knows you are there. Make the runtime resolve names on every read, and one of our two controllers spent up to 40% of its CPU doing it. Keep the OPC UA server busy, and the task that runs the machine pays for it.

None of those costs is large on its own. They are the kind that add up one dashboard at a time. So before you buy a second server for a controller that already has one, find out what the first one can do.

11. Version history

  • 2026-10-06: Link to the cross-vendor comparison. A lock marks the ways in that ran encrypted and authenticated.

  • 2026-10-05: Re-measured with the driver exactly as it ships. Controller cost now given as the 95th percentile of the task’s execution time. New figures for reads by name and for OPC UA. A subscription table for all three controllers. The comparison of changing values removed until it is measured the same way. Network figures updated, with OPC UA over separate variables added.

  • 2026-09-30: First published.

Back to Blog

Related Posts

View All Posts »
Ten PLC Protocols, Seven Vendors: How You Ask Beats What You Use

Ten PLC Protocols, Seven Vendors: How You Ask Beats What You Use

You cannot rank PLC protocols against each other: they run on different controllers, and the controller changes every number. What you can compare is a job. Ten protocols, seven vendors, the same values at the same rates: bytes on the wire, time per read and what each job costs the controller. How the client asked decided the cost more often than the protocol did.

Five Doors Into One Siemens Controller: What Each One Costs You

Five Doors Into One Siemens Controller: What Each One Costs You

How you ask a Siemens PLC for data matters more than which protocol you use. Reading the same hundred values off the same controller, a client that groups its requests is a hundred times faster on Modbus and five to fifteen times faster on S7Comm. That is larger than any difference between the protocols themselves. A measured look at what protocol and driver choice costs a PLC, a network and your budget.

Live chat is disabled. Cookie consent is required to use this feature. If you don't see a consent banner, an ad-blocker or privacy extension may be preventing it from appearing. Live-Chat ist deaktiviert. Cookie-Zustimmung ist erforderlich, um diese Funktion zu nutzen. Falls Sie kein Zustimmungsbanner sehen, verhindert möglicherweise ein Werbeblocker oder eine Datenschutz-Erweiterung dessen Anzeige.