[Python] Talking about the direct communication between python and PLC (deep analysis)


foreword

PLC (Programmable Logic Controller) Programmable logic controller can be understood as a microcomputer, which is widely used in industrial control, such as building intelligent control, precision machine tools, automotive electronics and so on.

With the rise of the Internet of Things, more and more traditional industrial equipment needs to communicate with the outside world. However, in many cases, microcontrollers like PLCs cannot directly communicate with the outside world due to their own hardware factors. The host computer such as PC acts as an intermediary bridge, opening a door for PLCs to communicate with the outside world.

As the most popular language at present, Python can be seen in many aspects such as AI and cloud computing. Of course, it cannot be missing in industrial control.

Here, just a novel about how to use Python to build communication between PC and PLC, it can also be regarded as a show of Python's demeanor in the field of industrial control.


提示:以下是本篇文章正文内容,下面案例可供参考

1. What exactly is Snap7?

The mainstream PLC communication methods in the current market are network communication and serial communication. The main protocols of network communication include profinet, modbus-tcp, etc. Serial communication is mainly based on RS232/485 modbus.

This time I came into contact with Siemens S7 series PLC, the communication method is network type, and Snap7 is an open source, 32/64 bit, multi-platform Ethernet communication library:

Support for multiple hardware architectures (i386/x86_64, ARM/ARM64, Sun Sparc, Mips)

Multi-system support (Windows, Linux, BSD, Solaris)

Multi-language support (C/C++, Phyton, Node.js, Pascal, C#, VB)

The official website is: http://snap7.sourceforge.net/

Python encapsulates it, for details, see:

https://github.com/gijzelaerr/python-snap7

2. Detailed steps to build the development environment

Here we mainly talk about how to build the Snap7 development environment in the Python environment from the two platforms of Windows and Linux (Ubuntu).

The installation of Python will not be repeated here. The environment construction is mainly the installation of the two libraries of Snap7 and python-snap7.

1. Install Snap7

Under Windows, you need to copy the downloaded Snap7 release library to the corresponding Python installation root directory according to the Python structural version (32-bit/64-bit).
As shown in the figure above, my python is 32bit, so I need to copy the files in the Win32 directory of Snap7 to the python installation root directory, as shown in the figure below:

insert image description here
Installation under Linux (Ubuntu) is relatively simple, just follow the command:

insert image description here

2. Install python-snap7

The python library installation of nap7 is much simpler, whether it is Windows or Linux, you can install it directly with pip.

$ pip install python-snap7

After the above two steps, even if the environment is set up, try a connection test code to judge whether the environment is set up normally.

import snap7
client = snap7.client.Client()
client.connect(‘192.168.0.1’, 0, 1)
client.disconnect()

If it is shown in the figure below, the environment is normal (the PLC of 192.168.0.1 does not exist)

4. Read and write PLC

insert image description here
After the environment is set up normally, the PLC still needs to do some configuration work before the formal establishment of communication, mainly to develop its own read and write permissions. For details, refer to the configuration in the figure below: Through the above configuration,
insert image description here
the PLC can communicate normally.

Combined with python-snap7's document API and source code analysis, the two important methods of python-sna7 are read_area and write_area, through which the corresponding storage address of PLC can be read and written.

def read_area(self, area, dbnumber, start, size):
       """This is the main function to read data from a PLC.
       With it you can read DB, Inputs, Outputs, Merkers, Timers and Counters.

       :param dbnumber: The DB number, only used when area= S7AreaDB
       :param start: offset to start writing
       :param size: number of units to read
       """
       assert area in snap7.snap7types.areas.values()
       wordlen = snap7.snap7types.S7WLByte
       type_ = snap7.snap7types.wordlen_to_ctypes[wordlen]
       logger.debug("reading area: %s dbnumber: %s start: %s: amount %s: "
                     "wordlen: %s" % (area, dbnumber, start, size, wordlen))
       data = (type_ * size)()
       result = self.library.Cli_ReadArea(self.pointer, area, dbnumber, start,
                               size, wordlen, byref(data))
       check_error(result, context="client")
       return bytearray(data)

   @error_wrap
   def write_area(self, area, dbnumber, start, data):
       """This is the main function to write data into a PLC. It's the
       complementary function of Cli_ReadArea(), the parameters and their
       meanings are the same. The only difference is that the data is
       transferred from the buffer pointed by pUsrData into PLC.

       :param dbnumber: The DB number, only used when area= S7AreaDB
       :param start: offset to start writing
       :param data: a bytearray containing the payload
       """
       wordlen = snap7.snap7types.S7WLByte
       type_ = snap7.snap7types.wordlen_to_ctypes[wordlen]
       size = len(data)
       logger.debug("writing area: %s dbnumber: %s start: %s: size %s: "
                     "type: %s" % (area, dbnumber, start, size, type_))
       cdata = (type_ * len(data)).from_buffer_copy(data)
       return self.library.Cli_WriteArea(self.pointer, area, dbnumber, start,
                              size, wordlen, byref(cdata))

It can be seen from the parameters that it is necessary to provide the PLC area address, start address, read and write data length.

What is the area address, PLC can provide the following information:
insert image description here
In the eyes of PLC programmers, there are only I, M, Q, DB,

Python programmers, panicked now, what is this?

How to see the beautiful scenery in the eyes of PLC programmers, you have to take another look at PLC.

By reading the PLC manual, the following information is obtained:
insert image description here

The data storage of the PLC is associated with the storage interval in the form of a tag, which is divided into input (I), output (O), bit storage (M) and data block (DB). When the program accesses the corresponding (I/O) tag, it accesses the Process Image Out of the CPU to operate on the corresponding address. The specific correspondence is as follows:

insert image description here

Here you can understand what the areas address defined in python-snap7 means.

areas = ADict({ 'PE': 0x81, #input 'PA': 0x82, #output 'MK': 0x83, #bit memory 'DB': 0x84, #DB 'CT': 0x1C, #counters 'TM': 0x1D, #Timers }) Now there is one last step before reading and writing PLC, how to determine the starting address?







insert image description here
It can be seen from the above that for M3.4, the corresponding is M (0x83), the starting address is 3, and the corresponding bit is 4.

Practical Operations (Key Points)

After careful preparation above, the next wave of actual combat will come.

Read and write PLC M10.1, MW201 to see how to read and write PLC.

import struct
import time

import snap7

def plc_connect(ip, rack=0, slot=1):
   """
   连接初始化
   :param ip:
   :param rack: 通常为0
   :param slot: 根据plc安装,一般为0或1
   :return:
   """
   client = snap7.client.Client()
   client.connect(ip, rack, slot)
   return client


def plc_con_close(client):
   """
   连接关闭
   :param client:
   :return:
   """
   client.disconnect()

def test_mk10_1(client):
   """
   测试M10.1
   :return:
   """
   area = snap7.snap7types.areas.MK
   dbnumber = 0
   amount = 1
   start = 10
   print(u'初始值')
   mk_data = client.read_area(area, dbnumber, start, amount)
   print(struct.unpack('!c', mk_data))

   print(u'置1')
   client.write_area(area, dbnumber, start, b'\x01')
   print(u'当前值')
   mk_cur = client.read_area(area, dbnumber, start, amount)
   print(struct.unpack('!c', mk_cur))

def test_mk_w201(client):
   """
   测试MW201,数据类型为word
   :param client:
   :return:
   """
   area = snap7.snap7types.areas.MK
   dbnumber = 0
   amount = 2
   start = 201
   print(u'初始值')
   mk_data = client.read_area(area, dbnumber, start, amount)
   print(struct.unpack('!h', mk_data))

   print(u'置12')
   client.write_area(area, dbnumber, start, b'\x00\x0C')
   print(u'当前值')
   mk_cur = client.read_area(area, dbnumber, start, amount)
   print(struct.unpack('!h', mk_cur))

   time.sleep(3)
   print(u'置3')
   client.write_area(area, dbnumber, start, b'\x00\x03')
   print(u'当前值')
   mk_cur = client.read_area(area, dbnumber, start, amount)
   print(struct.unpack('!h', mk_cur))

if __name__ == "__main__":
   client_fd = plc_connect('192.168.0.1')
   test_mk10_1(client_fd)
   test_mk10_1(client_fd)
   plc_con_close(client_fd)

It can be seen from the code that MW201 determines area as MK according to M, determines data amount as 2Btye according to W, determines start as 201 according to 201, unpacks the read data with struct according to the data length, and writes data corresponding to the pack of strcut.

The PLC variable type and size are given here, so that the corresponding amount for reading and writing is determined.

insert image description here
If you have any questions, please leave a message, thank you for one click and three links!

Guess you like

Origin blog.csdn.net/liaozp88/article/details/129708869