Python使用python-snap7实现西门子PLC通讯

article/2025/7/17 3:06:03

Python简介

Python是开源的高级编程语言之一,广泛应用于人工智能、数据分析、爬虫等领域。由于它拥有大量的开源库和标准库,以及简单且贴近自然语言的语法,所以即便是从未接触过编程的人,也能快速上手。2021年10月,Python登顶Tiobe,成为全世界最热门的编程语言之一。

以下摘自百度百科:

Python由荷兰数学和计算机科学研究学会的吉多·范罗苏姆 于1990 年代初设计,作为一门叫做ABC语言的替代品。Python提供了高效的高级数据结构,还能简单有效地面向对象编程。Python语法和动态类型,以及解释型语言的本质,使它成为多数平台上写脚本和快速开发应用的编程语言,随着版本的不断更新和语言新功能的添加,逐渐被用于独立的、大型项目的开发。  

需要注意的是,Python存在2.x版本和3.x版本,本文使用的是3.x的版本。

本文代码已上传至GitHub,项目地址如下:

https://github.com/XMNHCAS/Snap7PythonDemo


Snap7简介

snap7是一个由国外程序员开发的基于以太网与西门子S7系列PLC的通讯的开源库,类似于C#的S7.Net,但是它不单只支持Python,还支持Java、C/C++、C#等语言。

以下是snap7的官网:

http://snap7.sourceforge.net/

官网包含了使用文档、源码、历史版本等说明,可以根据需要自行查阅。

而python-snap7则是snap7的python版本,有单独的文档以及使用说明,只能用于python,以下是官方文档及GitHub链接:

python-snap7官方文档

GitHub


导入python-snap7

首先需要安装python-snap7,打开cmd,输入以下命令,安装python-snap7

pip3 install python-snap7

 

打开vscode,新建一个py文件,然后在最上面导入snap7

如果不知道如何安装python和使用vscode进行python代码编辑,可以百度一下,此处不赘述如何配置Python开发环境

import snap7

创建连接

snap7实现通讯的时候,是将PLC作为服务端,PC以客户端的身份主动连接的,所以最开始的时候,我们应该创建通讯需要使用的客户端

# 创建通讯客户端实例
plcObj = snap7.client.Client()# 连接至PLC
plcObj.connect('192.168.10.230', 0, 1)

如果手头没有PLC,可以参考我写的这篇文章:C#使用S7NetPlus以及PLCSIM Advanced V3.0实现西门子PLC仿真通讯

使用PLCSIM Advanced可以仿真出PLC来进行通讯测试

测试连接功能是否正常:

import snap7# 创建通讯客户端实例
plcObj = snap7.client.Client()# 连接至PLC
plcObj.connect('192.168.10.230', 0, 1)# 打印连接状态
print(f"连接状态:{plcObj.get_connected()}")# 关闭连接
plcObj.disconnect()# 打印连接状态
print(f"连接状态:{plcObj.get_connected()}")


读取数据

python-snap7并未集成像S7.Net那样的读取即刻解析数据的功能,所以无论是读还是写,都是需要进行字节转换的。

以读取DB10的以下的五个变量为例:

打开TIA Protal,创建DB块,编号为10,并添加如下图所示的变量并赋初值,下载到仿真的PLC后打开打开数值监控:

首先我们需要计算需要读取的总字节数,也就是最后一个变量的地址(即偏移量)加上它的数据长度。可以看到,上面的六个变量中,最后一个地址是264,WString为512字节,所以需要读取的总字节数为264+512=776个。

所以第一步,把这776个字节都读取上来:

data = plcObj.db_read(10, 0, 776)

其中,plcObj是我们刚刚创建的通讯客户端对象,db_read是它的读取DB块的方法,第一个参数是DB号,第二个是要读取的字节起始的起始地址,第三个参数是要读取的字节总数,用data这个变量来接收这些数据。

当然也可以写成这种形式:

data = plcObj.read_area(snap7.client.Areas.DB, 10, 0, 776)

read_area是读取任意区域的方法,通过第一个参数的枚举来区分读取的区域,如input、output、DB等,后面三个参数与db_read一致。

当我们需要的数据以字节的形式读取上来以后,我们就可以进行解析了。


解析数据

这里解析的方法有两种,第一种是使用python自身的数据类型转换的方法进行解析,第二种是使用python-snap7提供的转换方法进行解析。

Python自身函数

首先我们介绍第一种方法,使用python自身的数据类型转换的方法进行解析。

在python中,bool、int两个类型都有一个from_bytes的方法,可以通过这个方法来将字节数组转换为对应的数据。但是需要注意的是,在PLC中,数据是大端存储的,而PC中一般是小端存储,所以在这样进行转换的时候,需要加上byteorder='big',来声明读取上来的字节是大端存储的方式。故前面的bool和int变量都可以以这种方式进行解析:

# 读取bool的值
bool.from_bytes(data[0:1], byteorder='big')
# 读取int的值
int.from_bytes(data[2:4], byteorder='big')

其中[ 数字 : 数字 ]是python截取数组的语法,左边的数字是截取的起始索引号,右边的是截取的停止的索引号,需要注意的是,右边的索引是不会被截取的,所以像上面代码的data[ 0 : 1 ],实际上截取的只有data[0],而data[ 2 : 4 ],截取的则是data[2]和data[3]。如果左边的数字不填,则默认从索引0开始截取;而如果右边的数字不填,则默认截取剩余的所有元素。

而对于字符串,python提供了decode方法,可以将字符串的字节数组按照指定的编码格式来转换为字符串。不过PLC中的字符串的头字节是字符串的变量长度和字符串的实际长度,所以需要跳过这些字节来读取实际的数据。string类型是单字节存储的ASCII编码的字符串,所以跳过前两个字节。而wstring类型则是双字节存储的UTF-16BE编码的字符串,所以需要跳过前四个字节。

# 读取string的值
data[10:264].decode(encoding="ascii")
# 读取wstring的值
data[268:].decode(encoding="utf-16be")

在python中,float类型没有像bool和int一样的from_bytes方法,这个时候我们就需要使用struct来进行解析。struct是python的模块之一,可以用以解析字节,刚刚说到的bool、int和字符串都可以用它来解析,当然float也是可以的,不过要讲的话东西就会比较多,此次就不深入讲解了。

struct有pack和unpack两个方法,分别用以将数据转换为字节流和将字节流解析为对应的数据,如下所示,即可将real数值转换为python的float类型:

# 读取real的值
struct.unpack('>f', data[4:8])[0]

第一个参数是指定需要转换成的数据类型,“f”代表float类型,前面加的“>”代表这个字节数组是大端存储的方式,第二个参数是需要转换的字节流。由于unpack是以元组的形式返回的数据,所以需要加[0]来获取它返回的第一个数据。

接下来运行一下我们的代码,来查看读取结果:

import snap7
import struct# 创建通讯客户端实例
plcObj = snap7.client.Client()# 连接至PLC
plcObj.connect('192.168.10.230', 0, 1)# 读取数据
data = plcObj.db_read(10, 0, 776)# 关闭连接
plcObj.disconnect()# python解析
selfBool = bool.from_bytes(data[0:1], byteorder='big')
selfInt = int.from_bytes(data[2:4], byteorder='big')
selfReal = struct.unpack('>f', data[4:8])[0]
selfString = data[10:264].decode(encoding="ascii")
selfWString = data[268:].decode(encoding="utf-16be")
print("python自身函数解析:")
print(f"bool:{selfBool}; int:{selfInt}; real:{selfReal}; string:{selfString}; wstring:{selfWString}"
)

 

对比我们上面的PLC中的数值,基本上是没什么问题的。至于为什么real里的30.1,读上来之后会变成30.100000381469727,是因为计算机中的浮点型的存储规则决定了浮点数在部分情况下只能是个近似值,并非是python的问题,由于误差是比较小,实际应用时一般可以忽略不计。

通过上述方法,我们可以知道python自身就足以解析PLC读取上来的字节,但是这种方法对于部分人来说可能不是那么好理解,使用起来也不是那么简单直观,而且更重要的是,这种方法解析bool值存在一点小问题,毕竟python对二进制的位操作支持得并没有C#那么好。

python-snap7函数

接下来我们来介绍第二种方法,使用python-snap7提供的转换方法进行解析。

首先查看文档,可以看到snap7中有一个util的模块,它提供了多种数据类型的转换方法,可以将从PLC读取上来的字节直接解析为python可识别的数据类型。

首先导入util,这样我们可以少写个snap7,提高代码的简明性。 

from snap7 import util

根据文档,我们写出以下转换代码:

# Bool的值
util.get_bool(data, 0, 0)
# Int的值
util.get_int(data, 2)
# Real的值
util.get_real(data, 4)
# String的值
util.get_string(data, 8, 256)

可以看到,我们在使用snap7的转换方法的时候,只需要把我们读取到的字节数组以及数据的起始索引传进去即可,比起使用python自身的方法会更加简单。

get_bool方法的第三个参数为该字节的第几个bool量,因为一个bool量只需要一个位来表示,而一个字节是包含八个位的,也就是说这个字节可以表示八个bool量,在这里对应的DB10里地址为0.0~0.7的八个bool量,由于我们要读取的是地址0上的第一个bool量,所以第二个参数和第三个参数分别为0,0。

get_string方法的第三个参数为该字符串的最大长度,由于string类型共有256个字节,所以此处填256。

打印一下两种方法的结果:

import snap7
import struct
from snap7 import util# 创建通讯客户端实例
plcObj = snap7.client.Client()# 连接至PLC
plcObj.connect('192.168.10.230', 0, 1)# 读取数据
data = plcObj.db_read(10, 0, 776)# 关闭连接
plcObj.disconnect()# python解析
selfBool = bool.from_bytes(data[0:1], byteorder='big')
selfInt = int.from_bytes(data[2:4], byteorder='big')
selfReal = struct.unpack('>f', data[4:8])[0]
selfString = data[10:264].decode(encoding="ascii")
selfWString = data[268:].decode(encoding="utf-16be")
print("python自身函数解析:")
print(f"bool:{selfBool}; int:{selfInt}; real:{selfReal}; string:{selfString}; wstring:{selfWString}"
)# snap7解析
snap7Bool = util.get_bool(data, 0, 0)
snap7Int = util.get_int(data, 2)
snap7Real = util.get_real(data, 4)
snap7String = util.get_string(data, 8, 256)
snap7WString = util.get_string(data, 264, 508)
print("snap7函数解析:")
print(f"bool:{snap7Bool}; int:{selfInt}; real:{snap7Real}; string:{snap7String}; wstring:{snap7WString}"
)

 

可以看到,wstring读取出来的结果是乱码,这是因为python-snap7的头字节解析及编码格式问题,所以当我们读取wstring的时候,最好还是用python自己的decode方法


写入数据

与读取数据一样,写入数据也有两种方式,但是区别也仅仅是生成写入数据的字节方式不用。

写入数据可以调用db_write方法,也可以调用write_area,与读取数据一样,前者只能用以写入DB块,后者可以写入任意区域。

plcObj.write_area(snap7.client.Areas.DB,10,0,data)
plcObj.db_write(10,0,data)

 data是需要写入的数据的字节形式。

Python转换

我们在解析读取数据的时候提到,bool和int有from_bytes,同样的它们也有to_bytes方法,用以将数据转换成对应的字节数组的形式:

# bool的字节
bool.to_bytes(True, 1, 'big')# int的字节数组(双字节)
int.to_bytes(200, 2, 'big')

同样的,字符串类型有解码的decode方法,也有转码的encode方法:

# string的字节数组
str = 'hello python'
str.encode(encoding='ascii')# wstring的字节数组
str = '中国北京市'
str.encode(encoding='utf-16be')

而对于float类型要用的struct,我们解析数据时用到了它的unpack方法,而转换为字节的时候,我们就需要调用它的pack方法:

# float的字节数组
struct.pack(">f", 10.1)

所以我们能很轻易地写出写入数据的代码:

import snap7
import struct# 创建通讯客户端实例
plcObj = snap7.client.Client()# 连接至PLC
plcObj.connect('192.168.10.230', 0, 1)# 写入DB10.0 —— bool值
plcObj.write_area(snap7.client.Areas.DB, 10, 0, bool.to_bytes(False, 1, 'big'))# 写入DB10.2
plcObj.write_area(snap7.client.Areas.DB, 10, 2, int.to_bytes(200, 2, 'big'))
# plcObj.write_area(snap7.client.Areas.DB, 10, 2, struct.pack(">h", 112))# 写入DB10.4 —— real值
plcObj.write_area(snap7.client.Areas.DB, 10, 4, struct.pack(">f", 10.1))# 写入DB10.8 —— string值
str = 'hello python'
data = int.to_bytes(254, 1, 'big') + int.to_bytes(len(str), 1, 'big') + str.encode(encoding='ascii')
plcObj.write_area(snap7.client.Areas.DB, 10, 8, data)# 写入DB10.264 —— wstring值
str = '中国北京市'
data = int.to_bytes(508, 2, 'big') + int.to_bytes(len(str), 2, 'big') + str.encode(encoding='utf-16be')
plcObj.write_area(snap7.client.Areas.DB, 10, 264, data)# 关闭连接
plcObj.disconnect()

运行程序后,回到TIA Protal的监视界面可以看到,数值已经被更改了:

python-snap7转换

python-snap7提供了不同数据类型的转换方法,如下图所示:

 所以根据我们这里的五种变量类型,可以写出对应的转换代码:

# bool的字节数组
boolData = bytearray(1)
util.set_bool(boolData, 0, 0, True)# int的字节数组
intData = bytearray(2)
util.set_int(intData, 0, 100)# real的字节数组
realData = bytearray(4)
util.set_real(realData, 0, 20.5)# string的字节数组
str = "hello snap7"
stringData = bytearray(len(str) + 2)
util.set_string(stringData, 0, str, 256)
stringData[0] = 254

需要注意,string类型在调用了set_string方法后,首字节的字符最大值依旧是0,可能是python-snap7目前仍然存在的小bug,所以需要手动修改为真实的最大值。

由于目前并没有set_wstring方法,而且set_string方法也不支持wstring,所以wstring依然使用python的decode方法进行写入。

写入代码如下:

import snap7
from snap7 import util# 创建通讯客户端实例
plcObj = snap7.client.Client()# 连接至PLC
plcObj.connect('192.168.10.230', 0, 1)# 写入bool
boolData = bytearray(1)
util.set_bool(boolData, 0, 0, True)
plcObj.db_write(10, 0, boolData)# 写入int
intData = bytearray(2)
util.set_int(intData, 0, 100)
plcObj.db_write(10, 2, intData)# 写入real
realData = bytearray(4)
util.set_real(realData, 0, 20.5)
plcObj.db_write(10, 4, realData)# 写入string
str = "hello snap7"
stringData = bytearray(len(str) + 2)
util.set_string(stringData, 0, str, 256)
stringData[0] = 254
plcObj.db_write(10, 8, stringData)# 写入wstring
str = '中国广州市'
data = int.to_bytes(508, 2, 'big') + int.to_bytes(len(str), 2, 'big') + str.encode(encoding='utf-16be')
plcObj.db_write(10, 264, data)plcObj.disconnect()

再打开TIA Protal,可以看到数据已经成功写入。


结尾

本文介绍了怎么使用python实现西门子PLC的通讯,如果会C#的写法的话,可以很明显感觉到python的代码量会比C#少,但是相对的,数据解析也会比C#稍微麻烦一点。不过底层的通讯原理都是一样的,只是具体实现方式不同而已。


http://chatgpt.dhexx.cn/article/e3ZF07tl.shtml

相关文章

手把手教你强化学习 (三)马尔可夫决策过程与贝尔曼方程

马尔可夫决策过程 (Markov Decision Process,MDP)是序贯决策(sequential decision)的数学模型,一般用于具备马尔可夫性的环境中。最早的研究可以追溯到最优控制 (optimal control)问题上,1957年,美国学者Richard Bellman通过离散随…

数学建模,8月学习感想

数学建模概览 Matlab入门常用的操作指令数据类型 建模流程分析问题建立模型&求解模型数据建模技术优化技术连续模型求解评价模型求解机理建模方法 撰写论文前情提示正文部分配套说明排版工具 写在最后 长文预警!!! Matlab入门 Matlab可谓…

「从零入门数据分析」01_ 数据分析概述

作者 | gongyouliu 编辑 | auroral-L 本篇文章共8628字,预计阅读时间35分钟。 大家好,我是强哥。一个热爱暴走、读书、写作的人! 本章目录 一、数据分析的发展简史 二、数据分析的应用场景 三、常用的数据分析工具与技术 四、数据分析的思维…

数学建模心得分享(校赛)——2021.6.7

一、个人感受 总结为:找队友->讲方法->找知识->善讨论->勤实践 基础能力(个人见解): 第一次比赛完,还有这刚刚实习完的余温,工作中的思维的惯性还时刻在影响着我,身边的人几乎都为了自己的生活而奋斗。有幸…

matlab在管理学中的应用简述【一】

1、问题研究与描述 1.1 问题与模型 (1)问题 问题与人们的日常生活和工作有着密切的联系,人们每天都要面对各种各样的问题需要处理和解决。 如:资源配置问题,救灾抢险问题,时间安排问题,生产组…

大数据和人工智能属于什么专业 - 学大数据和人工智能出来做什么

人工智能专业主要从事什么工作? 1、算法工程师。进行人工智能相关前沿算法的研究,包括机器学习、知识应用、智能决策等技术的应用。以机器学习的过程为例,涉及到数据收集、数据整理、算法设计、算法训练、算法验证、算法应用等步骤&#xff0…

方差分析 anova一般指方差分析

方差分析(Analysis of Variance,简称ANOVA),又称“ 变异数分析”或“F检验”,是R.A.Fisher发明的,用于两个及两个以上 样本均数差别的 显著性检验。 由于各种因素的影响,研究所得的数据呈现波动状。造成波动的原因可分成两类,一是不可控的随机因素,另一是研究中施加的对…

继续无题

企业分为:决策层、管理层、执行层。 所以企业也会有:决策软件、管理软件、执行软件。 (1) 啥叫分析?分析就是根据现状,思考原因,这就是典型的归因:从现在往过去捋,这就叫…

数学建模经验分享及比赛时间汇总

一、浅谈数学建模1.1 什么是数学建模 先来看看官方的解释:数学建模是运用数学的语言和方法,通过抽象,简化建立能近似刻画“解决”实际问题的一种强有力的数学手段。数学建模就是用数学语言描述实际现象的过程。这里的实际现象既包涵具体的自然…

数模论文写作入门——正文篇

提示:数模论文写作入门还有“首页篇”和“排版篇”,可查看专栏补充。 目录 前言 一、问题重述 1、改词降重 水论文方法 2、 查阅相关背景资料 引用高大上 二、问题分析 三、模型假设 1.题目中明确给出假设条件 2.排除生活中小概率时间&#xff08…

SPSS统计分析过程包括描述性统计、均值比较、一般线性模型、相关分析、回归分析、对数线性模型、聚类分析、数据简化、生存分析、时间序列分析、多重响应等几大类...

https://www.zhihu.com/topic/19582125/top-answershttps://wenku.baidu.com/search?wordspss&ieutf-8&lm0&od0 SPSS 18.0由17个功能模组组成: Base System 基础程式 Advanced Models 高等统计模组(GEE/GLM/存活分析) Regression Models 进阶回归模组 …

数学建模论文写作

声明:本文为本人在b站学习清风数学建模的学习笔记 数学建模清风——论文写作方法教程(国赛和美赛) 文章目录 论文写作方法国赛摘要摘要的开头段摘要的中间段摘要的结尾段摘要中常见的废话完整摘要参考关键词 (正文部分&#xff09…

机器学习中的 Shapley 值怎么理解?

导语:在集体合作中,如何公平分配奖励? 图片来源:https://unsplash.com/photos/TL5Vy1IM-uA 我第一次听说 Shapley 值是在学习模型可解释性的时候。我知道了 SHAP,它是一个框架,可以更好地理解为什么机器学…

做数据分析如何从囚徒困境到合作的进化

小飞象交流会 即使拿到一手烂牌,也要打出一串王炸。这是场无限游戏,努力成为牌桌上最后一名玩家。 内部交流│7期 做数据分析如何 从囚徒困境到合作的进化 data analysis ●●●● 分享人:周海鹏 ‍ ‍‍ 在推进业务线各种项目的过程中&#…

博弈论学习(二)——完全信息静态博弈

这一篇主要介绍非合作博弈中,完全信息静态博弈的相关知识。 切记非合作博弈的条件:参与人具有足够智力且极端个人主义,即以自己的效用作为唯一的衡量标准。 要理解完全信息静态博弈,首先要了解以下2个概念: 完全信息…

Nabla算子

Nabla算子的定义: 梯度: 散度: 旋度:

Marr-Hildreth边缘检测器

用于边缘检测的算子应该有两个显著的特点: 1.它应该能够计算图中每一个点处的一阶导数或者二阶导数的数字近似的微分算子。 2.它能够被“调整”以便在任何期望的尺寸上其作用。因此,大的算子也可以用于检测模糊边缘,小的算子可以用来检测锐度…

5-3-BP神经网络

文章目录 引入名词BP神经网络NaN独热码Nabla算子 1神经网络1.1 神经元模型1.2 神经元激活函数1.2.1 为什么要使用激活函数?1.2.2为什么激活函数需要非线性函数?1.2.3常用的激活函数sigmoid 激活函数tanh激活函数Relu激活函数 1.3 神经网络结构 2.损失函数…

图像梯度——Sobel算子和Laplacian算子

一、Sobel算子 1、定义 Sobel算子是一种离散的微分算子,结合了高斯平滑和微分求导运算,利用局部拆分寻找边缘,计算所得的是一个梯度的近似值。 Sobel算子|左-右|/|下-上| Scharr算子|左-右|/|下-上| 2、原理 滤波器指由一幅图根据像素点&…

sobel算子 拉普拉斯算子以及散度与梯度的概念

在ECBSR论文的代码研究中,我发现关于ECBSR提出的多分支重参数化模型中,代码用到了sobel算子与laplace算子,很难判断这两个算子是为了论文的创新点还是真的有用,这块只能等待后续的对比实验。 1、拉普拉斯算子 首先是散度与梯度的…