ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Python通过snap7库与西门子S7-1200建立S7通信,读写存储器数据,顺便写个流水灯

2022-04-01 22:35:29  阅读:323  来源: 互联网

标签:dbnumber snap7 area Python S7 start client data


1.snap7 简介

snap7 是一个基于以太网与S7系列的西门子PLC通讯的开源库。

支持包括S7系列的S7-200、S7-200 Smart、S7-300、S7-400、S7-1200以及S7-1500的以太网通信。

适用系统
支持32/64位英特尔/ AMD的所有平台。
例如:Windows ( 除了 windows Me和95);Linux和类Linux(树莓派,UBeagleBone Black,DOO 等);BSD;Oracle Solaris ;Apple OSX

支持语言
Pascal;C#;C++;C;LabVIEW;Python;Node.js;Java,其中介绍比较多的是Python。

snap7官方网站

http://snap7.sourceforge.net/

https://pypi.org/project/python-snap7/

https://python-snap7.readthedocs.io/en/latest/

2.S7通信

西门子S7系列PLC采用以下两种通讯方式:
1) 开放式的TCP\IP,可以用于连接PLC与其他非西门子硬件
2) 西门子自己开发的S7 Protocol以太网通讯协议,用于西门子内部硬件通讯

这两者的传输报文是不一样的,如下图:

西门子数存储到二进制时方式是大端模式(BIG-Endian),而我们的普通电脑常常为小端模式(Liitle-Endian)。
大端模式是指数据的位保存在内存的地址中,而数据的高位保存在内存的低地址中.
小端模式是指数据的位保存在内存的地址中,而数据的高位保存在内存的高地址中。
例如:双字 DWORD 0X2F11214C
PLC

PC

所以数据需要进行转换。

3.安装snap7库

pip install python-snap7

笔者使用的是64位Python3.6.4和python-snap7 1.1.0,安装完成后,环境就算搭建好了。

对于32位Python,需要将Snap7官网下载的Win32目录下的文件,复制到Python的安装根目录下,如下图所示:

https://sourceforge.net/projects/snap7/files/1.4.2/snap7-full-1.4.2.7z/download

通过一个连接测试代码试试,判断下环境是否搭建正常。

注意自己新建的文件名不能是snap7,会和库文件冲突!

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

如果是下图提示,则环境正常(192.168.0.1的PLC不存在)。

如何使用 Python 构建 PC 通信?

如果是下图提示,则环境异常(snap7库安装不正确)。

如何使用 Python 构建 PC 通信?

4.读写PLC

4.1配置S7-1200

环境搭建正常后,在正式建立通信前PLC还需做些配置工作,主要是开发自身的读写权限。具体参照下图配置:

设置访问级别 设置DB块的属性

通过上述配置,PLC可以正常通信了。

4.2使用snap7读写存储器

python-snap7重要的两个方法是read_area和write_area,通过这两个方法就能读和写PLC的对应存储地址。

摘自client.py

复制代码
def read_area(self, area: Areas, dbnumber: int, start: int, size: int) -> bytearray:
    """Reads a data area from a PLC
    With it you can read DB, Inputs, Outputs, Merkers, Timers and Counters.

    Args:
        area: area to be read from.
        dbnumber: number of the db to be read from. In case of Inputs, Marks or Outputs, this should be equal to 0.
        start: byte index to start reading.
        size: number of bytes to read.

    Returns:
        Buffer with the data read.

    Raises:
        :obj:`ValueError`: if the area is not defined in the `Areas`

    Example:
        >>> import snap7
        >>> client = snap7.client.Client()
        >>> client.connect("192.168.0.1", 0, 0)
        >>> buffer = client.read_area(snap7.types.Areas.DB, 1, 10, 4)  # Reads the DB number 1 from the byte 10 to the byte 14.
        >>> buffer
        bytearray(b'\\x00\\x00')
    """
    if area not in Areas:
        raise ValueError(f"{area} is not implemented in snap7.types")
    elif area == Areas.TM:
        wordlen = WordLen.Timer
    elif area == Areas.CT:
        wordlen = WordLen.Counter
    else:
        wordlen = WordLen.Byte
    type_ = snap7.types.wordlen_to_ctypes[wordlen.value]
    logger.debug(
        f"reading area: {area.name} dbnumber: {dbnumber} start: {start}: amount {size}: wordlen: {wordlen.name}={wordlen.value}")
    data = (type_ * size)()
    result = self._library.Cli_ReadArea(self._pointer, area.value, dbnumber, start,
                                        size, wordlen.value, byref(data))
    check_error(result, context="client")
    return bytearray(data)


@error_wrap
def write_area(self, area: Areas, dbnumber: int, start: int, data: bytearray) -> int:
    """Writes a data area into a PLC.

    Args:
        area: area to be write.
        dbnumber: number of the db to be write to. In case of Inputs, Marks or Outputs, this should be equal to 0.
        start: byte index to start writting.
        data: buffer to be write.

    Returns:
        Snap7 error code.

    Exmaple:
        >>> import snap7
        >>> client = snap7.client.Client()
        >>> client.connect("192.168.0.1", 0, 0)
        >>> buffer = bytearray([0b00000001])
        >>> client.write_area(snap7.types.Areas.DB, 1, 10, buffer)  # Writes the bit 0 of the byte 10 from the DB number 1 to TRUE.
    """
    if area == Areas.TM:
        wordlen = WordLen.Timer
    elif area == Areas.CT:
        wordlen = WordLen.Counter
    else:
        wordlen = WordLen.Byte
    type_ = snap7.types.wordlen_to_ctypes[WordLen.Byte.value]
    size = len(data)
    logger.debug(f"writing area: {area.name} dbnumber: {dbnumber} start: {start}: size {size}: "
                 f"wordlen {wordlen.name}={wordlen.value} type: {type_}")
    cdata = (type_ * len(data)).from_buffer_copy(data)
    return self._library.Cli_WriteArea(self._pointer, area.value, dbnumber, start,
                                       size, wordlen.value, byref(cdata))
复制代码

《SIMATIC S7-1200 可编程控制器系统手册》节4.2.1有如下描述:

PLC的数据存储通过“变量”的形式与存储区间关联,分为输入(I)、输出(O)、位存储(M)和数据块(DB)。程序在访问对应(I/O)存储区时,是通过访问CPU的过程映像对相应地址进行操作的。具体对应关系如下:

故python-snap7中定义的Areas含义为

复制代码

'PE': 0x81, #input

'PA': 0x82, #output

'MK': 0x83, #bit memory

'DB': 0x84, #DB

'CT': 0x1C, #counters

'TM': 0x1D, #Timers

复制代码

现在离读写PLC还差最后一步,就是起始地址如何确定呢?

对于M3.4,对应的就是M(0x83),起始地址是3,对应bit位是4。

4.3数据存储地址

https://support.industry.siemens.com/cs/document/57374718

1、BIT        :位是存储空间的最小单位;
2、BYTE    :字节,由 8 个位组成;
3、WORD  :字,由2个字节组成,共16个位。
4、DWORD:双字,由2个字组成,共32个位。
第二:绝对地址寻址(同一存储空间)
M0.0 位   :     M            0         .    0
                   存储区  字节地址     位号
MB0 字节:     M            B              0
                    存储区  字节寻地     字节起始地址
                    含 M0.0-M0.7 共 8个位
MW0   字:      M           W              0
                    存储区    字寻地      字节起始地址
                    含MB0、MB1, 即M0.0-M0.7以及 M1.0-M1.7 共 16个位
MD0 双字:     M           D              0
                    存储区    双字寻地      字节起始地址
                    含MB0、MB1、MB2、MB3, 即M0.0-M3.7共 32 个位

4.4读写示例

复制代码
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.types.Areas.MK
    dbnumber = 0
    start = 10
    amount = 1
    print('初始值',end='')
    mk_data = client.read_area(area, dbnumber, start, amount)
    print(mk_data)#struct.unpack('!c', mk_data)
    print('置1')
    client.write_area(area, dbnumber, start, b'\x01')
    print('当前值',end='')
    mk_cur = client.read_area(area, dbnumber, start, amount)
    print(mk_cur)

def test_mk_w201(client):
    """
    测试MW201,数据类型为word
    :param client:
    :return:
    """
    area = snap7.types.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'\x12')
    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'\x02')
    print(u'当前值')
    mk_cur = client.read_area(area, dbnumber, start, amount)
    print(struct.unpack('!h', mk_cur))

def test_q0_0(client):
    """
    测试Q0.0,会使其输出高电平
    :return:
    """
    area = snap7.types.Areas.PA
    dbnumber = 0
    start = 0
    amount = 1
    print('初始值',end='')
    mk_data = client.read_area(area, dbnumber, start, amount)
    print(mk_data)#struct.unpack('!c', mk_data)
    print('置1')
    client.write_area(area, dbnumber, start, b'\x01')
    print('当前值',end='')
    mk_cur = client.read_area(area, dbnumber, start, amount)
    print(mk_cur)

def test_MD1012(client):
"""
测试MD1012,数据类型为real
:param client:
:return:
"""
area = snap7.types.Areas.MK
dbnumber = 0 # 读取的DB块序号。当读取的地址为I/Q/M时,序号为0
amount = 4
start = 1012
mk_data = client.read_area(area, dbnumber, start, amount)
print(struct.unpack('>f', mk_data)[0])
if __name__ == "__main__":
    client_fd = plc_connect('192.168.0.2')
    # test_mk10_1(client_fd)
    # test_mk_w201(client_fd)
    test_q0_0(client_fd)
    plc_con_close(client_fd)


'''
'PE': 0x81, #input, I
'PA': 0x82, #output, Q
'MK': 0x83, #bit memory, M
'DB': 0x84, #DB, DBX
'CT': 0x1C, #counters
'TM': 0x1D, #Timers
'''
复制代码

也可通过db_read()和db_write()读写DB块

复制代码
import snap7
client = snap7.client.Client()
client.connect('192.168.0.2', 0, 1)
plc_db1 = client.db_read(1, 0, 3)  # 读取数据块db1,起始字节,读取长度
print(plc_db1) # hex(plc_db1[0])
client.db_write(1, 0, b'\x11') # 写入数据块db1,起始字节,数据hex
plc_db1 = client.db_read(1, 0, 3)  # 读取数据块db1,起始字节,读取长度
print(plc_db1)
client.disconnect()
复制代码

读写MW变量时,要在变量表中先创建变量,并烧录进S7-1200

5.流水灯

复制代码
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 ledtrip(client):
    """
    跑马灯,使Q0.0~5循环亮起
    :return:
    """
    area = snap7.types.Areas.PA
    dbnumber = 0
    start = 0
    delayTime = 0.5
    for i in range(10):
        client.write_area(area, dbnumber, start, bytearray([0b00000001]))
        time.sleep(delayTime)
        client.write_area(area, dbnumber, start, bytearray([0b00000010]))
        time.sleep(delayTime)
        client.write_area(area, dbnumber, start, bytearray([0b00000100]))
        time.sleep(delayTime)
        client.write_area(area, dbnumber, start, bytearray([0b00001000]))
        time.sleep(delayTime)
        client.write_area(area, dbnumber, start, bytearray([0b00010000]))
        time.sleep(delayTime)
        client.write_area(area, dbnumber, start, bytearray([0b00100000]))
        time.sleep(delayTime)
    client.write_area(area, dbnumber, start, b'\x00')


if __name__ == "__main__":
    client_fd = plc_connect('192.168.0.2')
    ledtrip(client_fd)
    plc_con_close(client_fd)
复制代码

参考

https://pypi.org/project/python-snap7/

https://python-snap7.readthedocs.io/en/latest/

https://blog.csdn.net/zxpbuct/article/details/80079698

https://blog.csdn.net/lcb411/article/details/101147181

https://www.toutiao.com/a6589203413941092868

http://www.6dm.club/index.php/2018/04/07/

 

标签:dbnumber,snap7,area,Python,S7,start,client,data
来源: https://www.cnblogs.com/natlqs/p/16089459.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有