[co7io] Industrial integrations with Beckhoff ADS/Siemens S7

Are you looking for integration options of heavy duty stuff? Then you found your destination.
openHAB is very flexible and it is used in all kinds of installations. It is a battle proved software which allows to easily plug not only small sensors but also complex automation systems.

A while @chrisdutz asked about interest in Apache PLC4X bindings. Christofer, his and other’s companies been working really hard on the project and made great progress. We, as a ConnectorIO, started to make our very small contributions to that project. We look forward to do more and bridge a little both communities.

At current stage we provide openHAB 2.x integration via Apache PLC4X to below protocols:

  • Beckhoff ADS
  • Siemens S7

Other interesting protocols not yet available are:

  • Ethernet/IP
  • OPC UA (via Eclipse Milo)

There is also ongoing effort for other protocols (bacnet!, knx/ip) so thanks to this single library we are able to get Apache licensed (commercial friendly) artifacts which will link openhab closer to professional building automation. We provide a commercial support for supplied integrations. If you are installer, integrator or technician who love OH and would like to marry it with more expensive tools, we (ConnectorIO) are here to support you.

Here is a reference to a short post from December 2018 made by Chris.

Download kar, copy it to addons and look for openhab-binding-siemens-s7 and openhab-binding-beckhoff-ads.

As of beginning of May 2020 binding downloads are available through connectorio.com website where you can “purchase” item after leaving your contact details.

When you decide to pay, you buy a binding binary build with subsequent updates for openHAB 2.5 and 3.0. There are no license keys. Bought item have no data-point limit. You simply get an archive which you can install multiple times on any number of devices. We do ship VAT-EU documents and we do accept payments in USD.

You can use provided binaries for commercial deployments, even if you obtained them for free. Its open source. Do your own tests. If it works then its up to you if you want to pay for it. We collect cash to get real hardware, pay for further services, compensate our efforts and simply keep things up and running in a proper state.

Release history

  • 20201006 - downloads: Beckhoff ADS 2.5.1, Siemens S7 2.5.1. Free pdf manual + Beckhoff PLC Symbol Table Scanner 0.6.
    Fixes (dis)connection handling, fix Beckhoff numeric channels, updated Apache PLC4X library. Overall small steps geared towards stabilization of integrations.
  • 20200504 - Full discovery of Beckhoff ADS devices through network, automatic setup of AMS routes. Minor fixes in common logic used for also for Siemens PLCs.
  • 20200102 - Introduced support for contact items and numeric values, fixes in channel config descriptors for Beckhoff ADS.
  • 20191231 - Binding offers basic reading functionality of binary inputs without any discovery.

In case of troubles you can use this calendar to schedule a support call: https://calendly.com/connectorio.

Note 1 sources of bindings are publicly available. You can use available tools to check what is in bindings internals and build them yourself.
Note 2 binding sources will be currently hosted inside of ConnectorIO organization, anyone is free to fork and made use of them for free.
Note 3 in order to support further development and keep close touch with users from May 2020 we introduced registration. People who know how to compile sources are free to deploy their own builds.

1 Like

Hi, I would like to test the beckhoff ADS binding.
Is ther any more documentation?
By now I installed the addon on my 2.5 system. But there are errors in the log:

The XML document ‘/ESH-INF/thing/thing-types.xml’ in module ‘org.connectorio.binding.plc4x.beckhoff’ could not be parsed: No enum constant org.eclipse.smarthome.config.core.ConfigDescriptionParameter.Type.STRING : No enum constant org.eclipse.smarthome.config.core.ConfigDescriptionParameter.Type.STRING

Thank you. BR

Thank you very much for a test drive! Much appreciated given very short technical introduction.

The error you found is definitely my fault - I didn’t check configuration syntax despite of XML validation failures. I will be uploading a new version.

As I did not manage to get documentation yet, the easiest way is to click through inbox and adding devices via paper ui.
The configuration for Beckhoff in things file should look similar to this:

Bridge co7io-plc4x-ads:network:SomeNetworkInstance [host, port, targetAmsId, targetAmsPort, sourceAmsId, sourceAmsPort, refreshInterval*] {
  Thing co7io-plc4x-ads:ads:SomePlcInstance [refreshInterval*] {
     Type switch : input01 "Label for 01" [field, refreshInterval*]

Configurations with a star character are optional. Ams parameters are passed to connection string and are coming from protocol requirements. Currently only channels of switch type are supported.

The field value is depends on PLC kind, for Beckhoff it is:

  • 'symbolicAddress':'adsDataType'['numberOfElements']*, number of elements is optional
  • 0x'indexGroup'/0x'indexOffset':'adsDataType'['numberOfElements']* - if index is formatted in hex
  • 'indexGroup'/'indexOffset':'adsDataType'['numberOfElements']* - if index is formatted as decimal

You can mix hex notation at index group and offset, but for clarity of this little help I keep it in these three variants.

Supported ADS data types are:

  • BIT
  • BIT8
  • BITARR16
  • BITARR32
  • INT8
  • INT16
  • INT32
  • INT64
  • UINT8
  • UINT16
  • UINT32
  • UINT64
  • BOOL
  • BYTE
  • WORD
  • SINT
  • INT
  • DINT
  • LINT
  • REAL
  • TIME
  • DATE
  • ARRAY // not sure how this gets mapped
  • ENUM

Data types vary on each PLC (Siemens uses slightly different set), however there is an ongoing effort in the PLC4X to align some of the standard ones.

I will work on getting some VPN connection, so I will be able to test changes on Ads devices too.

Updated version 20200102 - 2.5.0-SNAPSHOT - Introduced support for contact items and numeric values, fixes in channel config descriptors for Beckhoff ADS.


I can not install the updated version of the binding.
It is not shown in the PaperUI. A restart does not solve this.
What can I do? Thank you. BR

If that’s not a problem please send me a copy of logs generated by your openhab instance (here or via private message), if there is any error in there. Usually when you drop KAR there is a delay in getting its contents to openHAB.
Also make sure you have only one KAR archive with beckhoff integration in addons/ directory.

I will also do a dry-run test on fresh 2.5 installation to determine if error is reproducible.

If you deployed same file logs should contain line such this:

2020-01-02 18:35:31.852 [WARN ] [af.deployer.kar.KarArtifactInstaller] - Karaf archive /home/splatch/Downloads/openhab-plc4x/addons/distribution-2.5.0-SNAPSHOT.kar' has been updated; redeploying.

Alternatively you can try getting into openhab console (ie. via start.sh, start.bat or ssh) and executing kar:list command which will should give you list of deployed addon archives, for me it prints out:

openhab> kar:list
KAR Name

Above means that distribution kar which you downloaded been pulled into runtime environment.
Second command which can be helpful is feature:list|grep openhab|grep ads, however in your case it won’t print anything as addon is completely missing (Paper UI shows subset of feature:list output).


Hi, thank you for your quick reply. I changed the extension of the file to something else and back to .kar. Now I could install the package. Maybe also I was not waiting long enough.
I con do some testing now. Looking forward. Thank you. BR

Hi, I can not connect to the PLC. I would like to create an Beckhoff ADS Network Bridge.

What I have: host, targetAmsId and targetAmsPort of the Beckhoff PLC as target.

What I don´t have: sourceAmsId, sourceAmsPort?
I don´t get it. What do I have to put in there? Normally there is no bidirektional authentification so I usually don´t need to know the information of the source?

I put in some values and the error message is like this:

[handler.BeckhoffNetworkBridgeHandler] - Could not obtain connection

org.apache.plc4x.java.api.exceptions.PlcConnectionException: Connection url ads:tcp:// doesn't match 'ads://{{host|ip}|serial:definition}/{targetAmsNetId}:{targetAmsPort}/{sourceAmsNetId}:{sourceAmsPort}' RAW:^ads:(tcp://(?<host>[\w.]+)(:(?<port>\d*))?|serial://(?<serialDefinition>((?!/\d).)*))/(?<targetAmsNetId>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(?<targetAmsPort>\d+)(/(?<sourceAmsNetId>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(?<sourceAmsPort>\d+))?(\?.*)?

Thank you. BR

Error you noticed comes from invalid connection string. This connection URL is generated from parameters given in configuration.

By default sourceAmsId is client machine IP address suffixed with .1.1. If computer on which you run openHAB is visible in network as then source ams can be set to, I would leave port same as you have on target machine eventually set it to 48898 if it doesn’t work.

By re-checking regular expression I noticed that source ams parameters are optional from plc4x. I will make them optional as well. Please try suggested values while I will cook new build. :slight_smile:

Thank you. Thats nice. Now I have a connection. :grinning:
Next thing where I struggle is the channel configuration. I try to set a bool variable. Field configuration looks like this:

But now I receive errors like this:

An error occurred while calling method ‘ThingHandler.handleCommand()’ on ‘org.connectorio.binding.plc4x.beckhoff.internal.handler.BeckhoffPlcHandler@1aee3eb’: No enum constant org.apache.plc4x.java.ads.model.AdsDataType.0x0

Btw: Refresh interval is in [ms]?

1 Like

Field might be 0x00004020:0x0:BOOL. You need data type and you can skip number of elements if you read single element/not an array. If you fetch 4 elements from an array (which is unsupported ATM) then you use 0x00004020:0x0:BOOL[4], however I will need to double check how plc4x actually handles that.

Yes, refresh interval is in ms.

When I am changing the value of the item in openHab there are exceptions in the log. On the side of PLC there is no change. Do you have an idea?
log.txt (3.4 KB)

It is communication error, possibly the network connection is not working as expected due to wrong ams id which breaks routing of answer back to caller. Lets see if newer version will work better.

Cause I can’t edit first post, attaching link here.

  • 20200103 - 2.5.0-SNAPSHOT - Beckhoff’s sourceAmsId/port parameters are now marked as optional. Added TCP port option for ADS network connections.

Hi @splatch and @Tuny, im in with testing to.

First of all, thanks to @splatch for the great effort so far.

I managed to get the ADS binding to work with the 20200103 Snapshot.

I have done it with a .things file that looks like this:

Bridge co7io-plc4x-ads:network:1dc63c66 [host="", port=48898, targetAmsId="", targetAmsPort=801, sourceAmsId="", sourceAmsPort=801, refreshInterval=1000] {
  Thing co7io-plc4x-ads:ads:1bb93999 [refreshInterval=1000] {
     Type switch : plc4XBoolTest1 "plc4XBoolTest1" [field="gTestPLC4XBOOL:BOOL", refreshInterval=1000]

My things look like this now:

This are my global test variables on the TwinCat side:


Unfortunately i’m getting this error now, when the binding tries to read the boolean:

2020-01-04 11:26:33.242 [me.event.ThingUpdatedEvent] - Thing 'co7io-plc4x-ads:ads:1bb93999' has been updated.

==> /logs/openhab.log <==

2020-01-04 11:26:33.224 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'plc4XTest.things', using it anyway:

Provide a thing type ID and a thing ID in this format:

 <thingTypeId> <thingId>

2020-01-04 11:26:33.227 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'plc4XTest.things'

2020-01-04 11:26:36.243 [WARN ] [mmon.WrappedScheduledExecutorService] - Scheduled runnable ended with an exception: 

org.apache.plc4x.java.api.exceptions.PlcRuntimeException: java.util.concurrent.TimeoutException

	at org.apache.plc4x.java.ads.connection.AdsAbstractPlcConnection.getFromFuture(AdsAbstractPlcConnection.java:236) ~[?:?]

	at org.apache.plc4x.java.ads.connection.AdsAbstractPlcConnection.lambda$mapFields$3(AdsAbstractPlcConnection.java:180) ~[?:?]

	at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660) ~[?:1.8.0_232]

	at org.apache.plc4x.java.ads.connection.AdsAbstractPlcConnection.mapFields(AdsAbstractPlcConnection.java:163) ~[?:?]

	at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183) ~[?:1.8.0_232]

	at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) ~[?:1.8.0_232]

	at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175) ~[?:1.8.0_232]

	at java.util.LinkedList$LLSpliterator.forEachRemaining(LinkedList.java:1235) ~[?:1.8.0_232]

	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482) ~[?:1.8.0_232]

	at java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:290) ~[?:1.8.0_232]

	at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731) ~[?:1.8.0_232]

	at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289) ~[?:1.8.0_232]

	at java.util.concurrent.ForkJoinTask.doInvoke(ForkJoinTask.java:401) ~[?:1.8.0_232]

	at java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:734) ~[?:1.8.0_232]

	at java.util.stream.ForEachOps$ForEachOp.evaluateParallel(ForEachOps.java:159) ~[?:1.8.0_232]

	at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateParallel(ForEachOps.java:173) ~[?:1.8.0_232]

	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233) ~[?:1.8.0_232]

	at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:485) ~[?:1.8.0_232]

	at org.apache.plc4x.java.ads.connection.AdsAbstractPlcConnection.mapFields(AdsAbstractPlcConnection.java:157) ~[?:?]

	at org.apache.plc4x.java.ads.connection.AdsAbstractPlcConnection.read(AdsAbstractPlcConnection.java:103) ~[?:?]

	at org.apache.plc4x.java.base.messages.DefaultPlcReadRequest.execute(DefaultPlcReadRequest.java:44) ~[?:?]

	at org.connectorio.binding.plc4x.shared.handler.task.ReadTask.run(ReadTask.java:47) ~[?:?]

	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[?:1.8.0_232]

	at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) ~[?:1.8.0_232]

	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) ~[?:1.8.0_232]

	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) ~[?:1.8.0_232]

	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_232]

	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_232]

	at java.lang.Thread.run(Thread.java:748) [?:1.8.0_232]

Caused by: java.util.concurrent.TimeoutException

	at java.util.concurrent.CompletableFuture.timedGet(CompletableFuture.java:1784) ~[?:1.8.0_232]

	at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1928) ~[?:1.8.0_232]

	at org.apache.plc4x.java.ads.connection.AdsAbstractPlcConnection.getFromFuture(AdsAbstractPlcConnection.java:230) ~[?:?]

	... 28 more

==> /logs/events.log <==

I think this is pretty much the same error as @Tuny reported allready here:

Any ideas how to fix this?

One concerne i’m thinking about in this case is:

When i connect to my PLC with Beckhoff tools, i must provide a username and a passwort to get connection to the ADS routing. How does PLC4X handle this? Shouldn’t we be able to provide credentials here to?

Might this probably chause the error i’m seeing now?

1 Like

I made a second attempt, using index decimal notation to declare the field value.

This time the .things file looks like this:

Bridge co7io-plc4x-ads:network:1dc63c66 [host="", port=48898, targetAmsId="", targetAmsPort=801, sourceAmsId="", sourceAmsPort=801, refreshInterval=1000] {
  Thing co7io-plc4x-ads:ads:1bb93999 [refreshInterval=1000] {
     Type switch : plc4XBoolTest1 "plc4XBoolTest1" [field="16448/1035447:BOOL", refreshInterval=5000]

With that approach i’m getting another error:

2020-01-04 13:58:25.822 [WARN ] [g.plc4x.shared.handler.task.ReadTask] - Could not fetch data from PLC

java.util.concurrent.ExecutionException: java.nio.channels.ClosedChannelException

	at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357) ~[?:1.8.0_232]

	at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1908) ~[?:1.8.0_232]

	at org.connectorio.binding.plc4x.shared.handler.task.ReadTask.run(ReadTask.java:52) [bundleFile:?]

	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:1.8.0_232]

	at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) [?:1.8.0_232]

	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) [?:1.8.0_232]

	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) [?:1.8.0_232]

	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_232]

	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_232]

	at java.lang.Thread.run(Thread.java:748) [?:1.8.0_232]

Caused by: java.nio.channels.ClosedChannelException

	at io.netty.channel.AbstractChannel$AbstractUnsafe.newClosedChannelException(AbstractChannel.java:955) ~[?:?]

	at io.netty.channel.AbstractChannel$AbstractUnsafe.write(AbstractChannel.java:863) ~[?:?]

	at io.netty.channel.DefaultChannelPipeline$HeadContext.write(DefaultChannelPipeline.java:1378) ~[?:?]

	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:716) ~[?:?]

	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:708) ~[?:?]

	at io.netty.channel.AbstractChannelHandlerContext.access$1700(AbstractChannelHandlerContext.java:56) ~[?:?]

	at io.netty.channel.AbstractChannelHandlerContext$AbstractWriteTask.write(AbstractChannelHandlerContext.java:1102) ~[?:?]

	at io.netty.channel.AbstractChannelHandlerContext$WriteAndFlushTask.write(AbstractChannelHandlerContext.java:1149) ~[?:?]

	at io.netty.channel.AbstractChannelHandlerContext$AbstractWriteTask.run(AbstractChannelHandlerContext.java:1073) ~[?:?]

	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163) ~[?:?]

	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:416) ~[?:?]

	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:515) ~[?:?]

	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:918) ~[?:?]

	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[?:?]

	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[?:?]

	... 1 more

==> /logs/events.log <==

Hey @seimenb, @Tuny I still couldn’t get device for direct tests thus I can not (yet) confirm if it is a bug. I hope to get VPN access to Apache IoT Lab in next days to do testing myself.
You can try to skip source ams parameters, if it doesn’t help you can also try to follow PLC4X instructions on setting up AMS routing:


I also installed it and i am currently working on it … some observations:

I tend to say that In ADS the “Target Port” is the Port for your Runtime on your SPS and you can see that in the TwinCat System Manager. SPS Configuration, for me 801 (on my Cx9xxx) for the first runtime on that SPS (811 would be second, 821,831,… just in case you have multiple runtimes)

Also i belive from this IOBroker thread https://forum.iobroker.net/topic/14285/adapter-beckhoff-ads/85 and my own local TWINCat Software i need to add a route for the openhab IP and Target System using the TwinCat System Manager. (Currently i am trying to fix that)

Source Port is kind off optional but mandatory field. Id did a quick check in the JAVA examples of Beckhoff https://infosys.beckhoff.de/english.php?content=../content/1033/tcsample_java/html/tcsample_java_intro.htm&id=
and i did not saw any of them using a Source Port to connect … so i tend to say it is only required if you have multiple ADS Device ID talking to a single destination. Like Multiple SPS on one AMS ID talking to another Target AMS id on another system.

I did a few packet captures and the only i see is when i add the Device that openhab connects (TCP SYN…) TCP gets established, does not transports anything (no DATA) and once fully established (just TCP layer) the SPS sends a FIN to disconnect the TCP immediate. Maybe i still miss the correct route.

Will continue testing this.

I’ve tried to avoid usage of port numbers beside amsId however current regular expression for parsing connection string requires it.
Today I’ve got my VPN access so I will play with it over weekend to see if I will go any further.

Hello Folks,

I also started testing this ADS Binding with my CX5120 and TwinCat 3.1.4027 (newest TC3 version)

I do get the same error as @Tuny and @seimenb.

I’m sure that the error happens, when the Binding trys to connect to the plc and I’m also pretty sure that this happens because the ADS Route is not established at this time. When I start the Bridge it says that it is online in the PaperUI, but on the target system no new route is added.

I think the route does not get established because the binding does not provide a username and a password for the route.

When you set up an ads route on the normal Beckhoff way, you will have to set it up with this window:


By adding the route I need to enter the login credentials for the PLC:


Only then, I’m able to add the route from openHAB to the PLC.
I tried to enter the credentials in my Bridige, but it didn’t solve the problem. I guess these parameters are not set up in this add-on:

Bridge co7io-plc4x-ads:network:1dc63c66 [host="", port=48898, targetAmsId="", targetAmsPort=801, sourceAmsId="", sourceAmsPort=801, refreshInterval=1000, username="Administrator", password="XXXXXX"] {


@splatch: Are you able to check my theory or send me a github link from the binding, so I can check it by myself?

Thanks, I would really like to get this binding running.


Hey @MarcelPayne,
Thanks for the info and test drive. Can you tell me if static route can be added upfront and left in persistent manner or lack of user/pw blocks entirely whole thing?
Binding attempts to open a connection to PLC and it does not support anything related to routing yet. In fact ams parameters set on connection are the only one thing exposed by underlying client library (plc4x) so far.
To me what you shown on above screens “add remote route” which requires additional password seems to be preflight call for opening the connection. I was trying to check Beckhoff documentation if there is any operation which requires username and password and found none beside routing.

Because we are currently limited by connection string ads://{{host|ip}|serial:definition}/{targetAmsNetId}:{targetAmsPort}/{sourceAmsNetId}:{sourceAmsPort} supported by plc4x (see AdsPlcDriver here) there is no way to get anything more than that.
We need to implement additionally routing part of ADS protocol in order to move forward. I checked with plc4x folks and they are interested to get that as part of the project up but first someone needs to donate code implementing that functionality. We still can experiment on our own with making binding aware of ads routing and its details.


For these who were interested in soruces, they are now available here: http://github.com/connectorio/connectorio-addons

I’ve been working on BACnet binding recently thus I didn’t move much forward with ADS yet.