1. Snap7 简介Snap7 是一个开源的西门子 S7 PLC 通信库支持 S7-200/300/400/1200/1500 全系列。它原生支持 C但提供了 Python、C#、Node.js 等语言的绑定。优点开源免费无需额外授权支持多平台Windows/Linux/macOS性能好支持多 PLC 并发连接协议层兼容不依赖 TIA Portal2. 环境准备2.1 安装 python-snap7pipinstallpython-snap72.2 S7-1200/1500 PLC 端设置在 TIA Portal 中进行以下配置才能让上位机通过 S7 协议通信允许 PUT/GET 通信S7-1500 需特别注意在设备组态 → 保护与安全 → 编译块时支持仿真/PUT/GET 通信 → 勾选优化块访问 → 标准访问DB 块属性中取消优化的块访问否则直接读写需要按符号名Snap7 读写按地址偏移防火墙设置确保 PLC 的以太网口防火墙允许 S7 通信端口 102特别注意S7-1200 固件 4.0 默认关闭 PUT/GET需在 TIA Portal 中显式开启。3. 快速开始连接与读取3.1 创建客户端并连接importsnap7# 创建客户端clientsnap7.client.Client()# 参数PLC 的 IP 地址机架号槽号# S7-1200 通常 rack0, slot1# S7-1500 通常 rack0, slot0client.connect(192.168.0.1,0,1)# 检查连接状态ifclient.get_connected():print(f已连接到 PLC:{client.get_cpu_state()})else:print(连接失败)3.2 读取 DB 块数据DB 块是 S7 中最常用的数据存储区上位机和 PLC 通过 DB 交换数据。defread_db_block(client,db_number,start_offset,byte_count): 读取 DB 块数据 :param client: snap7 客户端 :param db_number: DB 块编号 :param start_offset: 起始字节偏移 :param byte_count: 读取的字节数 :return: bytes 对象 try:dataclient.db_read(db_number,start_offset,byte_count)returndataexceptsnap7.snap7exceptions.Snap7Exceptionase:print(f读取 DB{db_number}失败:{e})returnNone# 示例读取 DB1 从偏移 0 开始的 10 个字节dataread_db_block(client,1,0,10)ifdata:print(f原始字节:{data.hex()})3.3 字节数据解析工具函数PLC 中常见的数据类型及其字节长度类型长度(字节)Python 解析方法Bool1 (bit)位运算提取Byte1int.from_bytes()Int2int.from_bytes()DInt4int.from_bytes()Real4struct.unpack(‘f’)String可变bytes.decode()importstructdefparse_int(data,offset0):解析 S7 Int (有符号 16 位)returnint.from_bytes(data[offset:offset2],byteorderbig,signedTrue)defparse_dint(data,offset0):解析 S7 DInt (有符号 32 位)returnint.from_bytes(data[offset:offset4],byteorderbig,signedTrue)defparse_real(data,offset0):解析 S7 Real (32 位浮点数)returnstruct.unpack(f,data[offset:offset4])[0]defparse_word(data,offset0):解析 S7 Word (无符号 16 位)returnint.from_bytes(data[offset:offset2],byteorderbig)defparse_dword(data,offset0):解析 S7 DWord (无符号 32 位)returnint.from_bytes(data[offset:offset4],byteorderbig)defparse_byte_array(data,offset0,length1):解析 Byte 数组returnlist(data[offset:offsetlength])# 使用示例rawclient.db_read(1,0,40)# 一次读取 40 字节temp_valueparse_real(raw,0)# Real 类型温度值偏移 0pressureparse_int(raw,4)# Int 类型压力值偏移 4status_flagsparse_dword(raw,6)# DWord 状态标志偏移 63.4 完整示例读取 PLC 中的设备运行数据importsnap7importstructimporttimeclassS7Client:西门子 PLC 数据采集客户端封装def__init__(self,ip,rack0,slot1):self.clientsnap7.client.Client()self.ipip self.rackrack self.slotslotdefconnect(self):try:self.client.connect(self.ip,self.rack,self.slot)print(f[] 已连接到{self.ip})returnTrueexceptExceptionase:print(f[-] 连接失败:{e})returnFalsedefdisconnect(self):self.client.disconnect()defread_device_data(self): 从 DB10 读取设备运行数据 字节分配与 PLC 程序员约定好的 Offset 0-3: Real 温度 Offset 4-7: Real 压力 Offset 8-9: Int 转速 Offset 10-13: DInt 累计运行时间(秒) Offset 14: Byte 设备状态(0停止,1运行,2故障) Offset 15: Byte 报警代码 try:dataself.client.db_read(10,0,16)return{temperature:struct.unpack(f,data[0:4])[0],pressure:struct.unpack(f,data[4:8])[0],speed:int.from_bytes(data[8:10],big,signedTrue),run_time:int.from_bytes(data[10:14],big,signedTrue),status:data[14],alarm_code:data[15],}exceptsnap7.snap7exceptions.Snap7Exceptionase:print(f读取设备数据失败:{e})returnNonedefread_multi_db(self,db_configs): 批量读取多个 DB 块 db_configs: [(db_num, start, size), ...] results{}fordb_num,start,sizeindb_configs:dataself.client.db_read(db_num,start,size)results[db_num]datareturnresults# 使用示例 if__name____main__:plcS7Client(192.168.0.1,rack0,slot1)ifplc.connect():try:whileTrue:dataplc.read_device_data()ifdata:print(f温度:{data[temperature]:.1f}°C | f压力:{data[pressure]:.2f}MPa | f转速:{data[speed]}RPM | f状态:{data[status]})time.sleep(1)# 每秒采集一次exceptKeyboardInterrupt:print(\n停止采集)finally:plc.disconnect()4. 写入数据到 PLC除了读取Snap7 同样支持写入操作。defwrite_real(client,db_number,offset,value):向 PLC DB 块写入 Real 类型数据datastruct.pack(f,value)client.db_write(db_number,offset,data)defwrite_int(client,db_number,offset,value):向 PLC DB 块写入 Int 类型数据dataint.to_bytes(value,2,byteorderbig,signedTrue)client.db_write(db_number,offset,data)defwrite_bool(client,db_number,byte_offset,bit_offset,value):向 PLC DB 块写入 Bool 类型数据# 先读取当前字节currentclient.db_read(db_number,byte_offset,1)byte_valcurrent[0]ifvalue:byte_val|(1bit_offset)else:byte_val~(1bit_offset)client.db_write(db_number,byte_offset,bytes([byte_val]))# 写入示例write_real(client,10,0,25.5)# DB10.0 写温度write_int(client,10,8,1500)# DB10.8 写转速write_bool(client,10,20,3,True)# DB10.20.3 写一个启停位5. 读取 I/O 区数据除了 DB 块S7 还有 I/Q/M 等存储区。# 读取输入映像区 PIW过程映像输入# 从字节偏移 0 开始读 4 个字节input_dataclient.ab_read(0,4)# 读取输出映像区 PQW过程映像输出output_dataclient.as_read(0,4)# 读取 M 存储区中间变量m_dataclient.read_area(snap7.types.Areas.MK,0,0,10)6. 常见问题与排查问题原因解决方案连接超时IP/机架/槽号配置错误确认 PLC IPPing 测试核对 rack/slotDB block not foundDB 号不存在或未下载检查 TIA Portal 中的 DB 编号wrong DB length读取长度超出 DB 实际大小缩小读取范围或增大 DB 大小读出的数值不对字节序或数据类型解析错误S7 是大端 (f)确认数据类型长度无法连接 S7-1200 v4未开启 PUT/GET 通信在 TIA Portal 保护设置中勾选允许7. 完整代码获取本文所有代码已整理为可直接运行的 Python 脚本。
Python + Snap7 实现西门子 S7-1200/1500 数据采集
1. Snap7 简介Snap7 是一个开源的西门子 S7 PLC 通信库支持 S7-200/300/400/1200/1500 全系列。它原生支持 C但提供了 Python、C#、Node.js 等语言的绑定。优点开源免费无需额外授权支持多平台Windows/Linux/macOS性能好支持多 PLC 并发连接协议层兼容不依赖 TIA Portal2. 环境准备2.1 安装 python-snap7pipinstallpython-snap72.2 S7-1200/1500 PLC 端设置在 TIA Portal 中进行以下配置才能让上位机通过 S7 协议通信允许 PUT/GET 通信S7-1500 需特别注意在设备组态 → 保护与安全 → 编译块时支持仿真/PUT/GET 通信 → 勾选优化块访问 → 标准访问DB 块属性中取消优化的块访问否则直接读写需要按符号名Snap7 读写按地址偏移防火墙设置确保 PLC 的以太网口防火墙允许 S7 通信端口 102特别注意S7-1200 固件 4.0 默认关闭 PUT/GET需在 TIA Portal 中显式开启。3. 快速开始连接与读取3.1 创建客户端并连接importsnap7# 创建客户端clientsnap7.client.Client()# 参数PLC 的 IP 地址机架号槽号# S7-1200 通常 rack0, slot1# S7-1500 通常 rack0, slot0client.connect(192.168.0.1,0,1)# 检查连接状态ifclient.get_connected():print(f已连接到 PLC:{client.get_cpu_state()})else:print(连接失败)3.2 读取 DB 块数据DB 块是 S7 中最常用的数据存储区上位机和 PLC 通过 DB 交换数据。defread_db_block(client,db_number,start_offset,byte_count): 读取 DB 块数据 :param client: snap7 客户端 :param db_number: DB 块编号 :param start_offset: 起始字节偏移 :param byte_count: 读取的字节数 :return: bytes 对象 try:dataclient.db_read(db_number,start_offset,byte_count)returndataexceptsnap7.snap7exceptions.Snap7Exceptionase:print(f读取 DB{db_number}失败:{e})returnNone# 示例读取 DB1 从偏移 0 开始的 10 个字节dataread_db_block(client,1,0,10)ifdata:print(f原始字节:{data.hex()})3.3 字节数据解析工具函数PLC 中常见的数据类型及其字节长度类型长度(字节)Python 解析方法Bool1 (bit)位运算提取Byte1int.from_bytes()Int2int.from_bytes()DInt4int.from_bytes()Real4struct.unpack(‘f’)String可变bytes.decode()importstructdefparse_int(data,offset0):解析 S7 Int (有符号 16 位)returnint.from_bytes(data[offset:offset2],byteorderbig,signedTrue)defparse_dint(data,offset0):解析 S7 DInt (有符号 32 位)returnint.from_bytes(data[offset:offset4],byteorderbig,signedTrue)defparse_real(data,offset0):解析 S7 Real (32 位浮点数)returnstruct.unpack(f,data[offset:offset4])[0]defparse_word(data,offset0):解析 S7 Word (无符号 16 位)returnint.from_bytes(data[offset:offset2],byteorderbig)defparse_dword(data,offset0):解析 S7 DWord (无符号 32 位)returnint.from_bytes(data[offset:offset4],byteorderbig)defparse_byte_array(data,offset0,length1):解析 Byte 数组returnlist(data[offset:offsetlength])# 使用示例rawclient.db_read(1,0,40)# 一次读取 40 字节temp_valueparse_real(raw,0)# Real 类型温度值偏移 0pressureparse_int(raw,4)# Int 类型压力值偏移 4status_flagsparse_dword(raw,6)# DWord 状态标志偏移 63.4 完整示例读取 PLC 中的设备运行数据importsnap7importstructimporttimeclassS7Client:西门子 PLC 数据采集客户端封装def__init__(self,ip,rack0,slot1):self.clientsnap7.client.Client()self.ipip self.rackrack self.slotslotdefconnect(self):try:self.client.connect(self.ip,self.rack,self.slot)print(f[] 已连接到{self.ip})returnTrueexceptExceptionase:print(f[-] 连接失败:{e})returnFalsedefdisconnect(self):self.client.disconnect()defread_device_data(self): 从 DB10 读取设备运行数据 字节分配与 PLC 程序员约定好的 Offset 0-3: Real 温度 Offset 4-7: Real 压力 Offset 8-9: Int 转速 Offset 10-13: DInt 累计运行时间(秒) Offset 14: Byte 设备状态(0停止,1运行,2故障) Offset 15: Byte 报警代码 try:dataself.client.db_read(10,0,16)return{temperature:struct.unpack(f,data[0:4])[0],pressure:struct.unpack(f,data[4:8])[0],speed:int.from_bytes(data[8:10],big,signedTrue),run_time:int.from_bytes(data[10:14],big,signedTrue),status:data[14],alarm_code:data[15],}exceptsnap7.snap7exceptions.Snap7Exceptionase:print(f读取设备数据失败:{e})returnNonedefread_multi_db(self,db_configs): 批量读取多个 DB 块 db_configs: [(db_num, start, size), ...] results{}fordb_num,start,sizeindb_configs:dataself.client.db_read(db_num,start,size)results[db_num]datareturnresults# 使用示例 if__name____main__:plcS7Client(192.168.0.1,rack0,slot1)ifplc.connect():try:whileTrue:dataplc.read_device_data()ifdata:print(f温度:{data[temperature]:.1f}°C | f压力:{data[pressure]:.2f}MPa | f转速:{data[speed]}RPM | f状态:{data[status]})time.sleep(1)# 每秒采集一次exceptKeyboardInterrupt:print(\n停止采集)finally:plc.disconnect()4. 写入数据到 PLC除了读取Snap7 同样支持写入操作。defwrite_real(client,db_number,offset,value):向 PLC DB 块写入 Real 类型数据datastruct.pack(f,value)client.db_write(db_number,offset,data)defwrite_int(client,db_number,offset,value):向 PLC DB 块写入 Int 类型数据dataint.to_bytes(value,2,byteorderbig,signedTrue)client.db_write(db_number,offset,data)defwrite_bool(client,db_number,byte_offset,bit_offset,value):向 PLC DB 块写入 Bool 类型数据# 先读取当前字节currentclient.db_read(db_number,byte_offset,1)byte_valcurrent[0]ifvalue:byte_val|(1bit_offset)else:byte_val~(1bit_offset)client.db_write(db_number,byte_offset,bytes([byte_val]))# 写入示例write_real(client,10,0,25.5)# DB10.0 写温度write_int(client,10,8,1500)# DB10.8 写转速write_bool(client,10,20,3,True)# DB10.20.3 写一个启停位5. 读取 I/O 区数据除了 DB 块S7 还有 I/Q/M 等存储区。# 读取输入映像区 PIW过程映像输入# 从字节偏移 0 开始读 4 个字节input_dataclient.ab_read(0,4)# 读取输出映像区 PQW过程映像输出output_dataclient.as_read(0,4)# 读取 M 存储区中间变量m_dataclient.read_area(snap7.types.Areas.MK,0,0,10)6. 常见问题与排查问题原因解决方案连接超时IP/机架/槽号配置错误确认 PLC IPPing 测试核对 rack/slotDB block not foundDB 号不存在或未下载检查 TIA Portal 中的 DB 编号wrong DB length读取长度超出 DB 实际大小缩小读取范围或增大 DB 大小读出的数值不对字节序或数据类型解析错误S7 是大端 (f)确认数据类型长度无法连接 S7-1200 v4未开启 PUT/GET 通信在 TIA Portal 保护设置中勾选允许7. 完整代码获取本文所有代码已整理为可直接运行的 Python 脚本。