Binding for Beckhoff PLC - ADS Protocol

@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. Actions | openHAB

  • 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