Binding for Beckhoff PLC - ADS Protocol

Hi Toni

This might help you.
Using XPATH to parse the resolved xml

Thanks for the reply
My xpath is //SOAP-ENV:Body/ns1:ReadResponse/ns1:RItemList/ns1:Items[@ItemName=“PLC1.arrLampProcessOutputData[17].bData”]/ns1:Value@xsi:type=“xsd:boolean”

and with this I get the status of my lamp (true or false)

Should I use some item like String Info “Info [%s]” {http="<[http://192.168.178.30/data/Dynamic.xml:10:XPATH(MYXPATH_BELOW)]"} ?

Hi Jan @herrJones

Are you the author of this?
https://github.com/herrJones/beckhoffads

Is this a working solution? Can you elaborate a little about that beckhoffads project as well?

Thanks,

Simon

Hey Simon,

Unfortunately, it is not…

My knowledge of java is not what it should be.

I decided to use the node.js node-ams module.

One you get the hang of it, it is quite straightforward to use.

I now have a working setup based on oh2, node-ams and influx db for logging.

Success,

Jan

HI Simon My CX9010 is running my home for the last 12 years. I have built a html app on top of the embedded IIS using TCScriptCE. Now I need to renovate the app or just find a new Interface like OH.

I found this (https://github.com/tomcx/tame4) solution using WebService and JS. I have the feeling that this could be a good base for binding.

You are reading the SPS every second, which would generate a huge traffic in my case with over 200 variables. If I understood the concept of OH right (I’m new to it) a binding would allow to read and write the things related variables (channels) as needed.

I’m a Beckhoff SPS fan and asked the Beckhoff support if they have any planes to support OH but they denied.

I’m more than interested to find / build a good working solution.

In the meantime, could you give me some guidance on how you implemented it? Items, things, bindings?

Thanks for some guidance

Hi all,

I’ve put my own code online in github (https://github.com/herrJones/openhab-node) as a reference for anyone in need :slight_smile:
(because I searched long enough myself)

As I mentioned above, I abandoned OpenHAB addons and most of the rules scripting in favor of NodeJS.
This because I have more experience with javascript than java…
Not to mention the hugely important fact that I found a working Beckhoff solution in NodeJS.
Not to mention the fact that you can actually debug code with NodeJS, without trial and error…

Features:

  • Beckhoff CX9020 integration via node-ads-api
    (probably other models will work too)
  • timers (week-rules)(in development :slight_smile: )
  • pushing and fetching data to InfluxDB via HTTP API
  • has not crashed in the last 2 months :sunglasses:

Hope this helps!

Jan

Hey @herrJones,
Because of my earlier bacnet work I was approached by some members of Apache plc4x project who told me that their project have support for most popular PLCs using appropriate protocols - these include S7 (Siemens) and ADS (Beckhoff). According to project lead there is more goodness to come.

This means that there are client libraries available which have compatible source code license and been tested with real hardware in industry projects. Most importantly these are done in proper way, without hacks or necessity for additional middleman hardware.
I didn’t go over plc4x code base yet and I am not sure if there is a chance to have a full “discovery” of things/items from s7/ads data stream, however at API level there are few interesting concepts such subscription which might get implemented according to underlying protocol.

Maybe at some day, once we find someone stubborn enough, we will get integration for these? I leave links here so everyone who is up - can follow them up!

Cheers,
Łukasz

1 Like

@seimenb
Im new in openhab. I worked with the standard bindings and items.
But i need Help with your Beckhoff communicatio solution.

So here are my questions:

  • does this work even if openhab is running on a Raspberry?
  • Are all my sps variables available when the server is running or do I have to create or share them specifically?
  • sendHttpPostRequest: create or install something for this ?
  • rule? do I create a separate file for each varianble in the folder rule? with name cX_Light_Sunblind_read like you?
  • “AL_Entre_Treppenhaus_Licht_EG.postUpdate” is the counterpart on openhab site? where or how do I use that?

Did I understand correctly that the rule files will go through every second automatically without further call and the values ​​are written in the openhab variables?

  • how can I use this variable? that would have to be a Chanel or item so that I can hang it graphically on an item?

As you can see, I’m a total beginner. But maybe you can help me sort something so I can use the interface. That would be nice.

Hi @jamhacker

Welcome to openHAB.

  • does this work even if openHAB is running on a Raspberry?

I’m running it myself on an Raspberry Pi 3.

Please be aware that this is not a very nice solution due to the polling of many SPS variables for every cycle (a second or so), what generates huge traffic and overhead, as already criticized by @Hoode63 and @stm.
Another limitation is also the capability of the Pi for all the XPATH parsing needed for my solution.

But it works well for me with around 160 PLC variables and a cycle time of 1 second. I have it running since 8 months without any issues and it’s stable. I’m using it daily in my productive system and I’m satisfied with it for the moment.

As long as there is no MQTT support for TwinCat2 and no better openHAB binding possibility around, I definitely go with this.

For me, this is the solution with as less pain as possible for the moment. Without any third party technology or server. Just Beckhoff’s CX with the already there OPC server and the Pi with openHAB. All the complexity is programmatically expressed in a handful of openHAB rule files, that can be easily expanded and managed.

  • Are all my sps variables available when the server is running or do I have to create or share them specifically?

All PLC variables should be available for the OPC server without doing something special.
On my CX there was everithing allready there and running. I did not have to change anything on the CX.

  • sendHttpPostRequest: create or install something for this ?

This is just an ordinary openHAB command, that can be used in rules and allows to send a http post request. https://www.openhab.org/docs/configuration/actions.html#http-actions

  • rule? do I create a separate file for each varianble in the folder rule? with name cX_Light_Sunblind_read like you?

I have one rule for all variables that will be read in the same cycle. For more variables, just add them to the content string of the post request and the xpath parsing as shown in my excample.
I’have created a second rule for variables with another, slower cycle. For example variables for room temperature or a weather station, where a cycle of 5 minutes is far enough to poll.

  • “AL_Entre_Treppenhaus_Licht_EG.postUpdate” is the counterpart on openhab site? where or how do I use that?

AL_Entre_Treppenhaus_Licht_EG is just an ordinary openHAB switch item.
Here is how i defined that in an .items file:

Switch AL_Entre_Treppenhaus_Licht_EG "Treppenhaus EG" <light> (AL_Entre_Treppenhaus) {autoupdate="false"}

Did I understand correctly that the rule files will go through every second automatically without further call and the values ​​are written in the openhab variables?

The rules file from my example executes every second. The cycle time can be adjusted with the crone command.

  • how can I use this variable? that would have to be a Chanel or item so that I can hang it graphically on an item?

I have no channels or bindings defined for my Beckhoff variables. Just items.
And I have installed and use the openHAB XPath, Scale and Map transformations.
The postUpdate command updates the item state if it has changed in the XPath’ed xml stream from the plc’s service. The rules do all the job that channels and bindings would do.

I have managed another rule to write the values to the plc.
It listens to changes of the items from the openHab side and passes an equivalent OPC call to the plc.
Here an example:

rule "<CX_Schreiben>"
when
    Item AL_Entre_Treppenhaus_Licht_EG received command or
    Item AL_Entre_Treppenhaus_Licht_OG received command or
    
    Item EG_KuecheEssen_Storre_Kueche received command or
    Item EG_KuecheEssen_Storre_Essen received
    
then
    
    var String url = "http://<yoursCxIpAddress>/TcPlcDataServiceDa.dll"
    var String contentType = "text/xml"
    var String contentWriteBeginning = '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><Write xmlns="http://opcfoundation.org/webservices/XMLDA/1.0/"><Options ClientRequestHandle="1" /><ItemList>'
    var String contentWriteEnd = '</ItemList></Write></soap:Body></soap:Envelope>'
    
    var String content
   
    if (triggeringItem.name == AL_Entre_Treppenhaus_Licht_EG.name)
    {
        if (AL_Entre_Treppenhaus_Licht_EG.state == OFF)
        {
            content = '<Items ItemName="PLC1.arrLightingCommands[1].bStart"><Value xsi:type="xsd:boolean">true</Value></Items>'
        }
        else
        {
            content = '<Items ItemName="PLC1.arrLightingCommands[1].bOff"><Value xsi:type="xsd:boolean">true</Value></Items>'
        }        
        sendHttpPostRequest(url, contentType, contentWriteBeginning  + content + contentWriteEnd)    
    }

    if (triggeringItem.name == AL_Entre_Treppenhaus_Licht_OG.name)
    {
        if (AL_Entre_Treppenhaus_Licht_OG.state == OFF)
        {
            content = '<Items ItemName="PLC1.arrLightingCommands[2].bStart"><Value xsi:type="xsd:boolean">true</Value></Items>'
        }
        else
        {
            content = '<Items ItemName="PLC1.arrLightingCommands[2].bOff"><Value xsi:type="xsd:boolean">true</Value></Items>'
        }        
        sendHttpPostRequest(url, contentType, contentWriteBeginning  + content + contentWriteEnd)    
    }

    
    if (triggeringItem.name == EG_KuecheEssen_Storre_Kueche.name)
    {
        if (receivedCommand.toString == 'DOWN')
        {
            content = '<Items ItemName="PLC1.arrSunblindCommands[1].bDown"><Value xsi:type="xsd:boolean">true</Value></Items>'
            sendHttpPostRequest(url, contentType, contentWriteBeginning  + content + contentWriteEnd)    
        }
        if (receivedCommand.toString == 'STOP')
        {
            content = '<Items ItemName="PLC1.arrSunblindCommands[1].bStop"><Value xsi:type="xsd:boolean">true</Value></Items>'
            sendHttpPostRequest(url, contentType, contentWriteBeginning  + content + contentWriteEnd)    
        }
        if (receivedCommand.toString == 'UP')
        {
            content = '<Items ItemName="PLC1.arrSunblindCommands[1].bUp"><Value xsi:type="xsd:boolean">true</Value></Items>'
            sendHttpPostRequest(url, contentType, contentWriteBeginning  + content + contentWriteEnd)    
        }      
    }
    if (triggeringItem.name == EG_KuecheEssen_Storre_Essen.name)
    {
        if (receivedCommand.toString == 'DOWN')
        {
            content = '<Items ItemName="PLC1.arrSunblindCommands[2].bDown"><Value xsi:type="xsd:boolean">true</Value></Items>'
            sendHttpPostRequest(url, contentType, contentWriteBeginning  + content + contentWriteEnd)    
        }
        if (receivedCommand.toString == 'STOP')
        {
            content = '<Items ItemName="PLC1.arrSunblindCommands[2].bStop"><Value xsi:type="xsd:boolean">true</Value></Items>'
            sendHttpPostRequest(url, contentType, contentWriteBeginning  + content + contentWriteEnd)    
        }
        if (receivedCommand.toString == 'UP')
        {
            content = '<Items ItemName="PLC1.arrSunblindCommands[2].bUp"><Value xsi:type="xsd:boolean">true</Value></Items>'
            sendHttpPostRequest(url, contentType, contentWriteBeginning  + content + contentWriteEnd)    
        }      
    }  
end

Hi, you can also give it a try while using the TwinCat 2 Modbus TCP Communication.
I did some Tests with the Modbus TCP Binding on Openhab 2.4 and it seems to work pretty well.
You can Download the Supplement (30 days trial) from the Beckhoff Homepage for your PLC.
But I don’t know the price for the supplement.

One you installed it you can read and write global variables in your Twincat System over Openhab. If you are interested I can give you more information about the configuration in OH later.

@Tuny

I read more About openhub.
I think Modbus is easier for me because i use it at the moment between Beckhoff plc and Raspberry Pi with codesys. So i think i have all i nead.

But i find online the discription of the Modbus bindungs. I don’t understand all.
Maybe you or someting else can help me?

What I have done with openhab 2.4:

  • install Modbus Binding (Paper UI)
  • create Modbus TCP Slave (Paper UI) , IP of the Modbus Master and the ID = 2 because m Raspberry with codesys is a Modus slave , too. (with ID 1)
  • create Modus Poll for Inputs (Paper UI), brigde to Modbus TCP Slave, TYPE: discrete Input, Length ? i dont know , i set it to 6
  • create Modbus Poll for Outputs (Paper UI), brigde to Modbus TCP Slave, TYPE:coil , Length ? i dont know , i set it to 6

All this 3 Things are online.

But i dont know how I have configurate the Modbus data Thing(s).
I Need a data Thing for each poll?
The Bridge Selection is the poll and not the Modbus TCP Thing?
And what is with the Read/write value Type,Write type, what is the Right value?

I read allways About “Modbus.cfg” but i don*'t find it. Maybe in new versions ist not necessary?

I create a File “Modbus.items” with:

Number Dimmer1 "Dimmer1 [%d]" (ALL) {modbus="slave1:0"}
Number Dimmer2 "Dimmer2 [%d]" (ALL) {modbus="slave1:1"}
Number Dimmer3 "Dimmer3 [%d]" (ALL) {modbus="slave1:2"}
Number Dimmer4 "Dimmer4 [%d]" (ALL) {modbus="slave1:3"}

But is “slave1” correctly? which string i must use?
“Number” is correctly for digital I/Os and Modbus?

Yes, just install the modbus binding. There is no more confiuration needed. Just create the things:
Then you can try it with this configuration (just paste it in the things configurations directory):

Bridge modbus:tcp:SPS "SPS" @ "Modbus" [ host="192.168.0.107", port=502, id=1 ]{
 Bridge poller holding "holding" @ "Modbus" [ start=12288, length=2, refresh=600, type="holding" ] {
    Thing data holding12288 "Byte1" @ "Modbus" [ readStart="12288", readValueType="int16", writeStart="12288", writeValueType="int16", writeType="holding" ]
    Thing data holding12289 "Byte2" @ "Modbus" [ readStart="12289", readValueType="int16", writeStart="12289", writeValueType="int16", writeType="holding" ]
}}

Items can look like this:

Number  TEST1    "TEST1 [%.1f °C]"  <heating> { channel="modbus:data:SPS:holding:holding12288:number" }
Number  TEST2    "TEST2 [%.1f °C]"  <heating> { channel="modbus:data:SPS:holding:holding12289:number" }

After that you should be able to change the values in Twincat and the other way:

I hope this will help you for the beginning.
BR Sebastian

1 Like

Thank you, now it works with your example.

At the moment i dont use the memory area of the modbus. but i want do it in the next days.
But how i must change the things if i want use the default Inputs, Outputs and the memory area together?

I think only the start and the type is different.
But if i do that i dont see anything things:

Bridge modbus:tcp:SPS “SPS” @ “Modbus” [ host=“192.168.178.24”, port=502, id=2 ]{

Bridge poller holding “holding” @ “Modbus” [ start=12288, length=2, refresh=600, type=“holding” ] {

Thing data holding12288 "Byte1" @ "Modbus" [ readStart="12288", readValueType="int16", writeStart="12288", writeValueType="int16", writeType="holding" ]

Thing data holding12289 "Byte2" @ "Modbus" [ readStart="12289", readValueType="int16", writeStart="12289", writeValueType="int16", writeType="holding" ]

}

Bridge poller modbusOutput “modbusOutput” @ “Modbus” [ start=0, length=2, refresh=600, type=“coil” ] {

Thing data modbusOutput0 "Byte1" @ "Modbus" [ readStart="0", readValueType="int16", writeStart="0", writeValueType="int16", writeType="coil" ]

Thing data modbusOutput1 "Byte2" @ "Modbus" [ readStart="1", readValueType="int16", writeStart="1", writeValueType="int16", writeType="coil" ]

}

What have i do to use all three Parts (input, output, memory)?

Regards, Jens

Hi, I am happy to hear it works.
So now you want to use the different Modbus-Areas how it is described here:
Modbus Memory

This I never tried.
Your thing configuration looks good. You want to read a varible which is declared as output in the SPS?
What about the information in the log? I might test this on my system later.

I use the Input and Output area between plc and Raspberry. So i know the modbus configuration.
But how can i build the different things.
But if you dont use it, test it tomorrow.

I think its not a big Problem.

This is an realy interesting approach:

Interest in an Apache PLC4X binding (Universal Industrial PLC API)

Reactivating the topic as I managed to get a first version of Beckhoff ADS integration working with openHAB. It works from Linux thanks to Apache PLC4X. This is java-native integration. It requires no DLLs and no license (let say) to connect if you know your PLC symbol table.

Please follow below topic if you find it interesting:

Currently I lack Beckhoff device for testing thus anyone who is capable of handling this integration is welcome.

Hi all,

I’ve implemented a binding for a Beckhoff PLC and a special lib for home automation. The solution is running stable for almost three years in my house, and for more than two years in several buildings of family and friends.

My Beckhoff Binding uses the ADS protocol for real time communication and it is optimized to run with a special LIB for Beckhoff PLCs. This lib has everything needed for building the Smart Home automation logic. For example if you want to have a room temperature controller you just need to call a function module instance like this:

VAR fbTempWO: FB_OHThing_TempControl;
...
fbTempWO(
	sOHThingLabel:='Temperatur Wohnzimmer', 
	sOHThingLocation:='Wohnzimmer', 
	aProg:=fbSetTimesFBH.aProgTimes, // optional array of heating times for day/night
	bExtEco:=bOpenWindowWO // optional link to a window reed contact
);

openHAB will detect this as a Thing and you get all(!) this automagically:

On openHAB side everything works via plug&play: the Binding is able to automatically analyze the PLC program, generate things (with all the channels) - and even all the configuration files for openHAB will be created automatically (i.e. a sitemap, a persistence file, etc.).

In this example the 5-6 lines is everything needed for a ready to use room controller - the whole logic is included within the special Beckhoff PLC LIB. You see the actual room temperature, the history and a graph, two setpoints (day and night temperature), automatic mode switch based on a time array, an optional connection to the window reed contact (hence it shows EXT_ECO in my screenshot), etc.
All settings changed on the UI are persisted on PLC side - so there are no issues if openHAB is not availible for some reason.

The Lib includes function modules for controlling lights, blinds, dimmers, motion detectors, alarm, solar water heating and many more things.

If you do not need the built-in logic but want to use your own, then you can have simple switches, sliders, etc. and bind them to your variables.

The software works on TwinCAT 3 PLCs, I’m running the software in my projects on a CX9020 but it should work on all Beckhoff devices.

The official home page of my start up is http://bashatec.de

If you have any question you can send me a message, reply here or drop me a mail at osman[at]bashatec.de

Best regards,
Osman

1 Like

Hello @monnimeter

Do you have binding for Beckhoff ADS <-> OpenHab in TwinCat2?

Hello @Bagunda,

the software package works only with TwinCat 3.

Best regards,
Osman