A few days ago I received an email from Sergio. He turned my attention towards a WebVisu project developed on SourceForge net by Frank Benkert (https://sourceforge.net/projects/webvisu/). The author managed to decode the protocol of communication between a PLC and the standard, web and JAVA based visualizations. Having the protocol he prepared a code, which re-creates CoDeSys visualization in a web browser using ONLY JavaScript. Sounds fascinating? Oh, because it is!
Here is a short illustration of what it is all about:
In order to see it all in action you need to:
I find the findings of Frank Benkert so exciting for 2 reasons:
Communication with the PLC controller is done through POST request sent to /plc/webvisu.htm.
In order to read variables (or physical ins and outs) the request should be structured as follows:
| 0 | number-of-addresses-to-read
| counter-starting-from-zero | address | address | number-of-bytes | variable-type |
Reading two first physical outputs on my controller goes as follows:
|0|2|
0|2|48|0|0|
1|2|29|0|0|
The PLC replies:|0|0|
One must not know the address, byte-length or variable type. This information is published by the controller in a plc_visu.xml file. I write about it in the 2nd part of this article.
The change of a variable’s value is done as follows:
| 1 | number-of-addresses-to-write
| counter-starting-from-zero | address | address | number-of-bytes | variable-type | new-value |
In order to change a BYTE variable addressed at %MB6 to 7 I send:
|1|1|0|0|6|1|2|7|
For an amateur like myself, who does not like MODBUS, such a protocol is simple and transparent. It is sufficient to use the following PYTHON code to read first 100 outputs:
#!/usr/bin/python import requests #you might need to install requests separatelly req = "|0|100" for num in range (0,99): req+= "|"+str(num)+"|2|"+str(num)+"|1|2" req+= "|" r = requests.post('http://192.168.1.3/PLC/webvisu.htm', data=req) print r.text
The PLC replies:
|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|….
The time of executing this script on my RPi is 0.2 sec.
In javascriptcie (with jQuery) one could use the following code:
<script> req = "|0|100"; for (var i=0; i<100; i++) { req+="|"+i+"|2|"+i+"|1|2"; } req += "|" console.log(req); $.ajax({ type: "POST", url: "http://192.168.1.3/plc/webvisu.htm", data: req, success: function (data) { console.log(data); } }); </script>
The advantage of the presented protocol over the READPI/WRITEPI request is quite obvious. One can read a large number of variables very quickly. It is a great simplification in comparison to constructing a „READPI?ADR=…..&FORMAT=%d” request for each individual variable. In fact, knowing what I know now, I should rewrite all my code including the jQuery plugin…
You can easily skip the next 4 tech-oriented paragraphs and go directly to the words „One, however, does not need to know all of that”.
In the communication each of the variables is presented as:
| address | address | number-of-bytes | variable-type |
It seems that the first address number is a group. On my controller all variables, which received specific addresses (by using AT %MB…) are placed in group 0. The inputs – 1, outputs – 2, other variables in the program – 4.
My first output DO, has the address %QX3.0. First 3 WORDS were taken by the RS232 communication module. 3 words are 3x16 bits = 48 bits, counting from 0 it is 0-47. That is why the address of %QX3.0 is 2, 48.
The number of bytes depends on the variable type. For physical inputs and outputs it is 0 (?), for BOOL and BYTE it is 1, WORD -2, DWORD – 4, STRING – length+1.
Variable types are: BOOL – 0, INT – 1, BYTE – 2, WORD – 3, DINT – 4, DWORD – 5, REAL – 6, TIME – 7, STRING – 8, ARRAY – 9. Setting the right variable type seems not to be critical. One can read the whole memory in 1-byte pieces. A correct definition of the type gives correctly formatted reply. A 4-char string can be returned as “abcd” if the variable type 8 is requested, or 47821 if the type is 5.
One, however, does not need to know all of that. Another thing described by Frank Benkert is how the variable address used in a visualization are published by the PLC.
Let’s imagine a simple program with the following variables:
PROGRAM PLC_PRG VAR AddressedVariable AT %MB0 : BOOL; StringVariable : STRING(5); ByteVariable : BYTE; WordVariable: WORD; END_VAR
Now, let’s add a visualization named “PLC_VISU” with 4 elements presenting the values of the above-mentioned variables and with a fifth element showing the state of the 1st physical output of the controller. When the program is uploaded (assuming that the option Target Settings->Visualization->Web visualization is checked), in the “PLC” directory of your PLC a file named “plc_visu.xml” will be generated.
At the very bottom of the file you will find:
<variablelist> <variable name="PLC_PRG.AddressedVariable">0,0,1,0</variable> <variable name="PLC_PRG.StringVariable">4,0,6,8</variable> <variable name="PLC_PRG.ByteVariable">4,6,1,2</variable> <variable name="PLC_PRG.WordVariable">4,7,2,3</variable> <variable name=".OUT0">2,0,0,0</variable> </variablelist>
Here are the addresses we were looking for! We can now read their values with the “|0|…” POST request, with the traditional READPI queries and with MODBUS. For me it is like a break-through! I have access to all the data without the laborious addressing made in the code. I can read the value of StringVariable sending"|0|1|0|4|0|6|8|" or of all the above-mentioned addresses with: "|0|5|0|0|0|1|0|1|4|0|6|8|2|4|6|1|2|3|4|7|2|3|4|2|0|0|0|".
Instead of assigning an address (using VAR variable AT %M…) to each variable, which I want to read or change from the outside , I can create a visualization with an element on it, which displays the variable’s value. When the code is uploaded to the controller, the address of that variable will be available in the visualization_name.xml.
An important remark: the location of a variable or a function block in the controllers memory is set by the compiler when the code is generated. Each change of the program might lead to a change of all those addresses. They are not permanent. Every external program willing to use the “un-addressed” variables should each time go through the xml file and search for the current address of the desired variable.
Sergio - many thanks for showing this to me!