
简介这份资源面向需要用 Python 与西门子 S7-300、S7-400 系列 PLC 通信的工程师与学习者通过 TCP/IP 直连方式实现数据读写省去依赖专用组态软件的麻烦。压缩包共 12 个文件约 910KB包含 3 个 py 脚本与 2 个 pyc 编译文件承载通信与业务逻辑5 个 xls 用于 PLC 点位标签与校验数据管理另有 1 个 ui 界面文件和 1 个 png 示意图整体结构紧凑。作者已亲测跑通并附上自制的 DEMO 工程其中演示了 Python 对 Excel 文件的读写处理界面用 QT 简单搭建方便对照理解通信流程与数据落盘方式。目前已有 3616 人学习下载适合希望快速上手西门子 PLC 数据采集、点位表管理与上位机交互的读者参考借鉴。1. 用 Python 打通西门子 S7-300/400为什么我最后选了 python-snap7车间里那台跑了七八年的 S7-300 还在稳定干活但上位机那边要加个数据看板或者把产量、报警记录导进 Excel 做日报找原厂改一次程序报价够买台新电脑。这种场景下用 Python 直接读写 PLC 的 DB 块、M 区、I/Q 区是最省事的路径。我试过 OPC 服务器转一道、也试过组态软件脚本最后稳定跑下来的还是 python-snap7 这条路——它直接封装了西门子 S7 通信协议基于 ISO-TCP 的 RFC1006不依赖西门子官方驱动Windows、Linux、树莓派都能装。这篇笔记面向的是手上有 S7-300 或 S7-400、想用 Python 做数据采集或轻量控制的工程师。S7-1200/1500 也能连但需要额外开启访问权限本文重点放在 300/400 这两个老系列上因为它们的坑更集中、资料更零散。读完你能自己搭起连接、读写 DB 块、把数据落进 Excel并且知道连不上时该查哪几个地方。2. 环境搭建与连接参数从装包到第一次握手2.1 python-snap7 的安装与那个绕不开的 DLLpython-snap7 本身是纯 Python 封装但它底层调用的是 snap7 的 C 库snap7.dll / libsnap7.so。很多人pip install python-snap7之后一运行就报RuntimeError: cant find snap7 library就是这一步没处理。Windows 下的常见做法是去 snap7 官方源下载对应版本的压缩包把snap7.dll放到C:\Windows\System32或者 Python 安装目录的DLLs文件夹里。Linux 下更省事直接装系统包# Ubuntu/Debian 系 sudo apt-get install snap7 # 或者用 pip 装完 python 包后手动把 libsnap7.so 放到 /usr/lib/ pip install python-snap7装完先验证库能不能加载这一步能省掉后面一半的玄学问题import snap7 from snap7.util import * # 打印 snap7 底层库版本能打印出来说明 DLL/SO 加载成功 print(snap7.__version__) client snap7.client.Client() print(Client 对象创建成功)逻辑说明snap7.client.Client()是所有操作的入口对象创建它本身不建立网络连接只是初始化。如果这一行就报错问题一定在库文件路径不在网络。参数上没什么可调的重点是把库文件放对位置。2.2 连接 S7-300/400 的四个关键参数连接 PLC 用的是client.connect(address, rack, slot, tcpport)四个参数里前三个最容易填错。import snap7 client snap7.client.Client() # 典型 S7-300/400 连接参数 # address: PLC 的 IP # rack: 机架号S7-300/400 通常是 0 # slot: 槽号S7-300 的 CPU 一般在 2S7-400 视电源和接口模块位置而定 # tcpport: ISO-TCP 端口默认 102 client.connect(192.168.0.10, 0, 2, 102) # 确认连接状态 print(client.get_connected()) # True 表示握手成功 # 读一下 CPU 信息验证通信正常 cpu_info client.get_cpu_info() print(cpu_info.ModuleTypeName)逻辑说明rack和slot是 S7 协议里定位 CPU 的坐标。S7-300 一般是 rack0、slot2S7-400 如果机架上先插了电源模块和接口模块CPU 的 slot 可能是 2 或 3得看硬件组态。tcpport默认 102除非网络管理员改过否则不用动。参数怎么改连不上时先把slot在 2 和 3 之间试一遍这是最常见的翻车点。另外确认 PLC 的 IP 和电脑在同一网段用ping先通一下。S7-300 老 CPU 有的不支持 ISO-TCP需要确认 CPU 型号带 PN 或已配 CP343-1 以太网模块。提示连接前先在 STEP7 或博途的硬件组态里确认 CPU 的以太网接口已启用并且没有勾选禁止来自远端的通信之类的保护选项。3. 读写 DB 块与 M 区地址换算和数据类型处理3.1 DB 块读取从字节流到真实数值PLC 里的数据是一段连续的字节Python 拿到的是bytearray需要按数据类型解析。这是整个流程里最需要细心的地方偏移量算错一位读出来的数就是乱的。import snap7 from snap7.util import get_real, get_int, get_bool client snap7.client.Client() client.connect(192.168.0.10, 0, 2, 102) # 读取 DB1从偏移 0 开始读 12 个字节 # db_read(db_number, start, size) data client.db_read(1, 0, 12) # 按偏移解析假设 DB1.DBD0 是 RealDB1.DBW4 是 IntDB1.DBX6.0 是 Bool temperature get_real(data, 0) # 偏移 04 字节 Real count get_int(data, 4) # 偏移 42 字节 Int running get_bool(data, 6, 0) # 偏移 6第 0 位 Bool print(f温度{temperature}, 计数{count}, 运行{running})逻辑说明db_read的start是字节偏移size是要读的字节数。get_real、get_int、get_bool这些工具函数负责把字节流按西门子的字节序大端解析成 Python 值。注意get_bool多一个 bit 参数因为布尔量是按位寻址的。参数怎么改偏移量必须和 PLC 程序里的变量地址严格对应。DB1.DBD0 对应偏移 0DB1.DBD4 对应偏移 4以此类推。如果一次读多个不连续的变量要么分多次读要么一次读一大段再按偏移切。读的 size 别超过 DB 块实际长度否则会报错。3.2 写入与 M 区、I/Q 区操作写操作和读对称用db_write。M 区标志位、I 区输入、Q 区输出用另外的接口。from snap7.util import set_real, set_int # 写 DB1.DBD8 一个 Real 值 write_buf bytearray(4) set_real(write_buf, 0, 25.6) client.db_write(1, 8, write_buf) # 读 M 区从 MB0 开始读 4 个字节 m_data client.read_area(snap7.types.Areas.MK, 0, 0, 4) # 读 Q 区从 QB0 开始读 2 个字节 q_data client.read_area(snap7.types.Areas.PA, 0, 0, 2) # 读 I 区从 IB0 开始读 2 个字节 i_data client.read_area(snap7.types.Areas.PE, 0, 0, 2)逻辑说明read_area的第一个参数是区域代码Areas.MK是 M 区Areas.PA是输出区QAreas.PE是输入区IAreas.DB是数据块。第二个参数是 DB 号非 DB 区填 0第三个是起始字节第四个是长度。参数怎么改写 DB 时db_write的start同样是字节偏移写入的bytearray长度要和数据类型匹配——Real 是 4 字节Int 是 2 字节。写 M 区用write_area区域代码用Areas.MK。注意写 Q 区会直接影响现场输出调试阶段务必先确认没有接实际执行机构或者用仿真环境。注意对运行中的产线 PLC 做写操作前一定确认目标地址不是安全联锁或急停相关的变量。我一般先在离线环境把地址表核对三遍再上现场。3.3 把采集数据落进 Excel采集回来的数据最终要给人看Excel 是最通用的载体。用 openpyxl 或 pandas 都行我一般用 pandas 直接写代码短。import pandas as pd from datetime import datetime records [] for i in range(10): data client.db_read(1, 0, 12) records.append({ 时间: datetime.now().strftime(%Y-%m-%d %H:%M:%S), 温度: get_real(data, 0), 计数: get_int(data, 4), 运行: get_bool(data, 6, 0), }) time.sleep(1) df pd.DataFrame(records) df.to_excel(plc_data.xlsx, indexFalse)逻辑说明循环采集时每次重新db_read把解析后的值存进字典列表最后一次性写 Excel。to_excel的indexFalse去掉行号列表格更干净。参数怎么改采集周期用time.sleep控制但注意别设太短S7-300 的通信资源有限100ms 以下的高频采集容易丢包。数据量大时建议先写 CSV最后再转 Excel避免内存里堆太多 DataFrame。4. 避坑与排查连不上、读出错、写不进的五条血泪经验4.1 现象connect 超时ping 得通但连不上原因最常见的是 rack/slot 填错其次是 PLC 的 ISO-TCP 端口被防火墙挡了或者 CPU 本身不支持以太网通信老款 S7-300 只有 MPI 口必须加 CP343-1 模块。解决先用 STEP7 在线看硬件组态确认 rack/slot用telnet 192.168.0.10 102测端口通不通确认 CPU 型号带 PN 或已装以太网 CP 模块。三条都过了还连不上换根网线试试我遇到过网线水晶头氧化导致间歇性断连的。4.2 现象db_read 报 Address out of range原因读的起始偏移加长度超过了 DB 块的实际定义长度。比如 DB1 只定义了 10 个字节你从偏移 8 读 4 个字节就越界了。解决在 STEP7 里打开 DB 块看实际长度或者用client.db_get(1)拿到 DB 信息里的 Size 字段。读之前先判断start size db_size。4.3 现象读出来的 Real 值是个天文数字或极小值原因字节序搞反了或者偏移量差了一位。西门子是大端序python-snap7 的get_real已经处理了大端但如果偏移量错了解析出来的就是相邻变量的字节。解决对照 PLC 变量表逐个核对偏移。DB1.DBD0 是偏移 0DB1.DBD4 是偏移 4别把 DBD 的编号当成偏移量。用get_real读出来不对时先print(data.hex())看原始字节手动算一下。4.4 现象db_write 返回成功但 PLC 里值没变原因写到了错误的 DB 号或偏移或者 PLC 程序在下一个扫描周期把该地址覆盖了。还有一种情况是 DB 块被设置为只读或处于优化访问模式S7-1200/1500 常见。解决确认 DB 号和偏移无误检查 PLC 程序里是否有对该地址的写操作S7-300/400 的 DB 块默认是非优化访问一般不存在这个问题但如果是从 1200/1500 移植过来的程序要注意。4.5 现象长时间运行后连接断开报 ISO : An error occurred during recv原因网络抖动、PLC 侧通信资源耗尽、或者电脑休眠导致 TCP 连接被回收。S7-300 的通信连接数有限多个客户端同时连容易挤掉。解决加心跳和重连机制捕获异常后client.disconnect()再client.connect()。采集程序里加个try/except包住读写操作断线后等几秒重连。下面这段是我常用的重连模板def safe_read(client, db, start, size, retry3): for i in range(retry): try: if not client.get_connected(): client.connect(192.168.0.10, 0, 2, 102) return client.db_read(db, start, size) except Exception as e: print(f读取失败第 {i1} 次重试: {e}) time.sleep(2) return None逻辑说明每次读之前先检查连接状态断了就重连最多重试retry次。参数retry按现场网络质量调一般 3 次够用网络差的可以加到 5 次。5. 进阶技巧批量采集、地址表驱动与一个验证习惯5.1 用地址表驱动采集别把偏移写死在代码里项目一多DB 块地址经常变把偏移量硬编码在 Python 里改一次就要动代码。我现在的做法是维护一张 Excel 或 CSV 地址表程序启动时读进来按表采集。变量名区域DB号偏移类型说明温度DB10Real反应釜温度计数DB14Int当日产量运行DB16.0Bool设备运行状态报警M010Bool综合报警位import pandas as pd from snap7.util import get_real, get_int, get_bool # 读取地址表 tags pd.read_excel(tags.xlsx) def read_tag(client, row): if row[区域] DB: data client.db_read(int(row[DB号]), int(row[偏移]), 4) else: data client.read_area(snap7.types.Areas.MK, 0, int(row[偏移]), 4) if row[类型] Real: return get_real(data, 0) elif row[类型] Int: return get_int(data, 0) elif row[类型] Bool: return get_bool(data, 0, 0) for _, row in tags.iterrows(): print(row[变量名], read_tag(client, row))逻辑说明地址表把变量名-区域-DB号-偏移-类型解耦出来程序只负责按表解析。参数上偏移列对 Bool 类型要写成6.0这种带位号的格式解析时拆开用。5.2 一个验证习惯先读已知值再上生产每次新项目接线完成我不会直接跑采集程序而是先手动在 PLC 里给几个测试变量赋已知值——比如温度写 25.0、计数写 100、运行位置 1——然后用 Python 读一遍确认读出来的和写进去的完全一致。这一步能同时验证 rack/slot、偏移量、数据类型三件事比逐个排查快得多。# 验证脚本读三个已知值和预期对比 data client.db_read(1, 0, 12) assert abs(get_real(data, 0) - 25.0) 0.01, 温度读取异常 assert get_int(data, 4) 100, 计数读取异常 assert get_bool(data, 6, 0) True, 运行状态读取异常 print(验证通过可以上生产采集)逻辑说明用assert做硬校验任何一项不匹配直接抛异常避免带着错误配置上生产。Real 值比较用容差因为浮点传输可能有微小误差。从那以后我每次新接一个 S7-300/400 的采集项目都强制先跑一遍这个已知值验证再开正式采集。希望帮到你。本文还有配套的精品资源点击获取