Python通过snap7库与西门子S7-1200建立S7通信,读写存储器数据,顺便写个流水灯
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不存在)。
如果是下图提示,则环境异常(snap7库安装不正确)。
4.读写PLC
4.1配置S7-1200
环境搭建正常后,在正式建立通信前PLC还需做些配置工作,主要是开发自身的读写权限。具体参照下图配置:



通过上述配置,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/
Python通过snap7库与西门子S7-1200建立S7通信,读写存储器数据,顺便写个流水灯的更多相关文章
- Python底层socket库
Python底层socket库将Unix关于网络通信的系统调用对象化处理,是底层函数的高级封装,socket()函数返回一个套接字,它的方法实现了各种套接字系统调用.read与write与Python ...
- 【C++实现python字符串函数库】strip、lstrip、rstrip方法
[C++实现python字符串函数库]strip.lstrip.rstrip方法 这三个方法用于删除字符串首尾处指定的字符,默认删除空白符(包括'\n', '\r', '\t', ' '). s.st ...
- 【C++实现python字符串函数库】二:字符串匹配函数startswith与endswith
[C++实现python字符串函数库]字符串匹配函数startswith与endswith 这两个函数用于匹配字符串的开头或末尾,判断是否包含另一个字符串,它们返回bool值.startswith() ...
- 【C++实现python字符串函数库】一:分割函数:split、rsplit
[C++实现python字符串函数库]split()与rsplit()方法 前言 本系列文章将介绍python提供的字符串函数,并尝试使用C++来实现这些函数.这些C++函数在这里做单独的分析,最后我 ...
- python使用cookielib库示例分享
Python中cookielib库(python3中为http.cookiejar)为存储和管理cookie提供客户端支持,下面是使用示例 该模块主要功能是提供可存储cookie的对象.使用此模块捕获 ...
- Python virtualenv安装库报错SSL: CERTIFICATE_VERIFY_FAILED
Python virtualenv安装库报错SSL: CERTIFICATE_VERIFY_FAILED 问题描述 使用pip按照virtualenv报错,如下: pip install virtua ...
- 【python】标准库的大致认识
正如那句 Python 社区中很有名的话所说的:“battery included”,Python 的一大好处在于它有一套很有用的标准库(standard library).标准库是随着 Python ...
- Python 图形 GUI 库 pyqtgraph
原文 Python 图形 GUI 库 pyqtgraph pyqtgraph 是纯 Python 图形 GUI 库,基于PyQT4 /pyside和NumPy.它主要目的用于在数学/科学/工程中.M ...
- python的urllib2库详细使用说明
一直以来技术群里会有新入行的同学提问关于urllib和urllib2以及cookielib相关的问题.所以我打算在这里总结一下,避免大家反复回答同样的问题浪费资源. 这篇属于教程类的文字,如果你已经非 ...
随机推荐
- 274-基于XC7V690T的3U VPX信号处理板
一.板卡概述 本板卡系我司自主研发的基于3U VPX导冷架构的信号处理板,适用于高速图像处理,雷达信号处理等.芯片采用工业级设计.该处理板包含1片Xilinx公司的Virtex7系列FPGA-XC7V ...
- 「BUAA OO Pre」Git生成多个ssh key并连接GitLab仓库
「BUAA OO Pre」Git生成多个ssh key并连接GitLab仓库 Part 0 前言 写作背景 笔者在配置学校GitLab的ssh key时遇到一些问题,原因应为曾经配置过GitHub的s ...
- 【第二十三期】春招实习阶段性总结(阿里云已OC)
本人算是一个半路出家找工作的菜鸡了,现在手中阿里云云原生offer.百度度小满offer.腾讯PCG二面环节.美团点评等offer环节.希望我的经历分享能对各位或之后准备春招的同学有帮助. 个人背景 ...
- VUE3 之 动画与过渡的实现 - 这个系列的教程通俗易懂,适合新手
1. 概述 光环效应告诉我们: 当一个人在某一方面取得了巨大的成功,人们就会给他贴上正面的标签,这个人从此就被"优秀"的光环所笼罩,他做的一切,人们都认为是正确的. 例如:越是名气 ...
- node(s) didn‘t match node selector.
k8s集群中,有pod出现了 Pending ,通过 kubectl describe pod 命令,发现了如下报错 0/4 nodes are available: 1 node(s) had ta ...
- TensorFlow优化器浅析
本文基于tensorflow-v1.15分支,简单分析下TensorFlow中的优化器. optimizer = tf.train.GradientDescentOptimizer(learning_ ...
- 简述对CT,IT,ICT,OT的认识
今天碰到一个关键词:CT.CT领域,所以给自己做一个科普. 网络:简述对CT,IT,ICT,OT的认识 一.通信技术-CT(Communication Technology) 最早的CT业被称为电信业 ...
- Redis_RDB持久化之写时复制技术的应用
背景: 最近生产环境中某个Set的Redis集群经常出现短暂的内存降低现象,经过查看日志是因为在RDB持久化所造成的内存突降(日志中:RDB: 4929 MB of memory used by co ...
- elasticsearch算法之搜索模型(一)
面对海量的信息,我们很容易被淹没在信息的海洋中:当我们需要查找某个信息的时候,我们就会输入能够体现我们意图的关键字,搜索引擎会通过解析我们的关键字从而构造相应的查询表示方法:然后搜索引擎通过构造的查询 ...
- 【C# .Net GC】GC初始化设置 和GcSetting
相关的类 GcSetting 类 GCLargeObjectHeapCompactionMode 枚举 GCLargeObjectHeapCompactionMode 枚举 属性的值 GCSettin ...