S7 1200 与 SQLSERVER 数据采集:协议转换与网关配置实战
简介这份资源面向工业自动化工程师、PLC编程学习者及需要打通产线数据链路的开发者聚焦西门子S7-1200与SQL Server数据库的集成方案。内容围绕PLC数据采集、通信接口配置、数据库表结构设计、通信方式选型及自定义应用程序编写等环节展开帮助读者理解如何将控制器数据稳定写入关系型数据库实现集中存储、查询与分析。资源包共19个文件约8.73MB以9个png截图、5个xml配置文件为主另含xsl、plf、pma、idx及ap13等工程与索引文件可用于还原项目结构、对照配置细节与排查通信问题。目前已有4841人学习下载适合希望掌握PLC与数据库对接思路、参考实际工程文件进行调试与优化的中高级读者。1. 从一条产线数据落库说起Siemens S7 1200 连 SQLSERVER 到底难在哪去年帮一个做装配检测的客户做数据采集现场是一台 Siemens S7 1200 PLC 控制整条线老板要求每件产品的扭矩值、检测结果、时间戳全部进 SQLSERVER方便后面做追溯报表。我第一反应是“PLC 走 OPC 再转数据库常规操作”结果到了现场才发现S7 1200 的以太网口只认 S7 协议SQLSERVER 那边只认 TDS 协议中间没有任何一层能直接对话。更坑的是客户 IT 部门给的 SQLSERVER 是 2019 Express默认只开了 TCP 1433Windows 防火墙还拦着PLC 侧连个 Ping 都过不去。那一次我前后折腾了三天翻车翻在协议转换和账号权限上最后用一台工控机做网关才跑通。这套环境在产线数据采集里太常见了S7 1200 便宜稳定SQLSERVER 又是工厂 MES 的标配但两者之间的桥怎么搭、参数怎么配、装坏了怎么救网上能照着走完的完整记录不多。这篇就把我踩过的坑和最终跑通的配置一步步写清楚适合正在做 PLC 数据入库的自动化工程师和运维新手能照着装熟手能直接看版本差异和参数取舍。2. 环境先列清S7 1200 与 SQLSERVER 的版本选择和依赖清单2.1 硬件与软件版本对照S7 1200 这边CPU 型号决定了你能用的通信方式。我手上这台是 CPU 1214C DC/DC/DC固件 V4.2带一个 PROFINET 口。如果你的固件低于 V4.0S7 通信的开放式指令TCON、TSEND、TRCV支持会受限建议先刷固件。软件侧TIA Portal 我用的 V16V15 也能用但 V16 对 S7-1200 的指令库更全。SQLSERVER 我选的是 2019 Developer 版Express 版也能跑但 Express 有 10GB 单库上限产线数据量大的话半年就得清库所以正式环境我一般用 Standard 或 Developer。操作系统这边网关机装的是 Windows 10 专业版 21H2SQLSERVER 2019 要求 .NET Framework 4.6 以上Win10 自带 4.8不用额外装。组件推荐版本最低要求备注S7 1200 CPU1214C / 1215C1211C固件 V4.0 以上TIA PortalV16V15需装 S7-1200 支持包SQLSERVER2019 Developer2016 ExpressExpress 有 10GB 上限网关机 OSWindows 10 专业版Windows 7 SP1需 .NET 4.6通信中间件KEPServerEX V6或 S7-1200 OPC 库也可用开源方案2.2 网络拓扑与端口规划S7 1200 的 PROFINET 口默认 IP 是 192.168.0.1网关机我设成 192.168.0.100SQLSERVER 如果装在另一台服务器上就再加一个网段。关键点是PLC 和网关机必须在同一子网否则 TCON 指令连不上。SQLSERVER 默认实例监听 TCP 1433如果你用命名实例端口是动态的得在 SQL Server Configuration Manager 里固定成 1433不然防火墙规则没法写。我一般会在网关机上开两个网卡一个连 PLC 网段一个连办公网段避免产线网络和办公网络混在一起。提示S7 1200 的 PROFINET 口不支持路由跨网段必须走网关机转发别想着在 PLC 里配默认网关就能连到另一个网段。2.3 依赖组件安装顺序顺序错了会出玄学问题。我一般按这个顺序来先装 TIA Portal再装 SQLSERVER最后装通信中间件。TIA Portal 装完会注册一些 S7 通信的 DLL如果先装中间件可能找不到这些库。SQLSERVER 装的时候记得勾选“数据库引擎服务”和“客户端工具连接”别只装个 Management Studio。中间件这边KEPServerEX 的 S7-1200 驱动需要单独授权如果不想花钱可以用开源的 Snap7 库自己写网关程序Python 和 C# 都有封装。# 检查 .NET Framework 版本SQLSERVER 2019 要求 4.6 以上 reg query HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full /v Release # 返回值 528040 以上对应 .NET 4.8394254 对应 4.6.1这段注册表查询用来确认系统 .NET 版本如果返回值低于 394254SQLSERVER 安装会直接报错退出。我遇到过一台 Win7 机器返回值是 378389对应 .NET 4.5装 SQLSERVER 2019 时卡在“正在加载安装程序”界面后来手动装了 4.8 才过。3. 装 SQLSERVER 2019实例配置和 TCP 端口固定3.1 安装选项与实例命名SQLSERVER 安装程序跑起来后第一个坑是“实例功能选择”。如果你只做数据采集选“数据库引擎服务”就够了Analysis Services 和 Reporting Services 不用勾省资源。实例名我一般用默认实例 MSSQLSERVER因为命名实例的端口是动态的后面配防火墙容易漏。排序规则选 Chinese_PRC_CI_AS不然中文产品名入库会乱码。身份验证模式必须选“混合模式”并设一个 sa 密码因为 PLC 侧网关程序用 Windows 身份验证跨网段很麻烦用 SQL 账号最省事。-- 安装完成后用 sa 登录创建专用数据库和账号 CREATE DATABASE PLCData; GO USE PLCData; GO CREATE TABLE TorqueLog ( Id INT IDENTITY(1,1) PRIMARY KEY, ProductSN NVARCHAR(50), TorqueValue DECIMAL(10,2), Result NVARCHAR(10), LogTime DATETIME DEFAULT GETDATE() ); GO CREATE LOGIN plc_user WITH PASSWORD Plc2024; GO USE PLCData; GO CREATE USER plc_user FOR LOGIN plc_user; GO ALTER ROLE db_datareader ADD MEMBER plc_user; ALTER ROLE db_datawriter ADD MEMBER plc_user; GO这段 SQL 先建库建表再建一个专用登录账号 plc_user只给读写权限不给 DDL 权限。参数上ProductSN 用 NVARCHAR 是为了存中文和特殊字符TorqueValue 用 DECIMAL(10,2) 保证精度LogTime 默认 GETDATE() 省得网关程序自己拼时间。注意密码要符合复杂度要求SQLSERVER 默认策略要求大小写加数字加符号我试过用纯数字密码安装阶段就被拒了。3.2 固定 TCP 端口并放行防火墙装完 SQLSERVER 后打开 SQL Server Configuration Manager找到“SQL Server 网络配置”下的“MSSQLSERVER 的协议”把 TCP/IP 启用。然后双击 TCP/IP在“IP 地址”标签页里把 IPAll 的 TCP 端口改成 1433TCP 动态端口清空。这一步不做命名实例每次重启端口都变网关程序连不上。改完重启 SQLSERVER 服务。# 用 PowerShell 添加入站防火墙规则放行 1433 New-NetFirewallRule -DisplayName SQLSERVER 1433 -Direction Inbound -Protocol TCP -LocalPort 1433 -Action Allow # 检查规则是否生效 Get-NetFirewallRule -DisplayName SQLSERVER 1433 | Select-Object Enabled, Direction, Action防火墙这块我踩过坑Windows 10 默认把 1433 归到“专用网络”配置文件如果网关机的网卡被识别成“公用网络”规则不生效。我一般会先把产线网卡的网络位置改成“专用”再执行上面的命令。另外如果 SQLSERVER 和网关程序不在同一台机器还要在 SQLSERVER 所在机器上开 1433 入站如果在同一台本地连接走共享内存但网关程序如果绑的是 IP 地址还是走 TCP所以规则照样要加。3.3 验证 SQLSERVER 可远程连接在网关机上用 telnet 或 Test-NetConnection 测 1433 通不通。如果通再用 sqlcmd 登录试试。# 测试 TCP 1433 连通性 Test-NetConnection -ComputerName 192.168.0.100 -Port 1433 # 用 sqlcmd 验证账号 sqlcmd -S 192.168.0.100,1433 -U plc_user -P Plc2024 -d PLCData -Q SELECT COUNT(*) FROM TorqueLogTest-NetConnection 返回 TcpTestSucceeded : True 才算通。如果 False先查防火墙再查 SQLSERVER 的 TCP/IP 是否启用最后查 SQLSERVER 错误日志里有没有“Server is listening on [ any 1433]”。sqlcmd 登录成功会返回行数如果报“登录失败”检查 sa 是否启用、plc_user 是否建对、密码有没有过期。4. 配 S7 1200 侧TCON、TSEND 和 TRCV 指令落地4.1 在 TIA Portal 里组态 PLC 和连接参数打开 TIA Portal V16新建项目添加 CPU 1214C DC/DC/DC固件选 V4.2。在“设备与网络”里给 PLC 的 PROFINET 口设 IP 192.168.0.1子网掩码 255.255.255.0。然后新建一个“连接”类型选“TCP”伙伴填网关机的 IP 192.168.0.100端口填 2000这个端口是网关程序监听的不是 SQLSERVER 的 1433。连接建好后TIA Portal 会自动生成一个连接 ID后面 TCON 指令要用。// OB1 里调用 TCON 建立连接 TCON_DB(REQ : TRUE, ID : 1, CONNECT : TCP_Connect_DB, DONE TCON_Done, BUSY TCON_Busy, ERROR TCON_Error, STATUS TCON_Status);TCON 的 ID 参数就是刚才连接组态里生成的 IDCONNECT 指向一个数据类型为 TCON_IP_v4 的 DB 块里面存了伙伴 IP 和端口。REQ 用上升沿触发别一直给 TRUE否则连接建好后会反复请求。DONE 置位表示连接建立成功ERROR 置位就要看 STATUS 代码常见 0x80C4 是连接被拒绝一般是网关程序没启动或端口不对。4.2 用 TSEND 发数据、TRCV 收结果连接建好后用 TSEND 把扭矩值发出去用 TRCV 收 SQLSERVER 那边的写入结果。TSEND 的 DATA 参数指向一个发送缓冲区我一般建一个 100 字节的数组前 4 字节放产品序列号长度后面放实际数据。// 发送数据 TSEND_DB(REQ : Send_Trigger, ID : 1, DATA : Send_Buffer, LEN : 100, DONE TSEND_Done, BUSY TSEND_Busy, ERROR TSEND_Error, STATUS TSEND_Status); // 接收结果 TRCV_DB(EN_R : TRUE, ID : 1, DATA : Recv_Buffer, LEN : 100, DONE TRCV_Done, BUSY TRCV_Busy, ERROR TRCV_Error, STATUS TRCV_Status, RCVD_LEN Recv_Len);TSEND 的 LEN 要和 DATA 缓冲区实际长度一致我试过填 200 但缓冲区只有 100结果 PLC 报 0x80B1长度错误。TRCV 的 EN_R 给 TRUE 表示一直准备接收DONE 置位表示收到一帧。注意 TSEND 和 TRCV 不能同时用同一个连接 ID 发和收如果网关程序是“收到数据后回一个 ACK”那 TRCV 要等 TSEND 完成后再触发否则会报 0x80C5连接被占用。4.3 数据格式约定与字节序处理S7 1200 默认是大端字节序SQLSERVER 和网关程序C#/Python是小端所以浮点数传输要转。我一般把扭矩值乘 100 转成整数再发接收端除以 100避免浮点精度问题。产品序列号用 ASCII 编码每个字符一个字节接收端按长度截取。# 网关程序侧解析 S7 发来的数据Python 示例 import struct def parse_s7_data(raw_bytes): # 前 4 字节是序列号长度大端 sn_len struct.unpack(I, raw_bytes[0:4])[0] # 序列号 ASCII sn raw_bytes[4:4sn_len].decode(ascii) # 扭矩值大端整数除以 100 torque struct.unpack(i, raw_bytes[4sn_len:8sn_len])[0] / 100.0 return sn, torquestruct.unpack 的 I 表示大端无符号整数i 表示大端有符号整数。如果 PLC 侧发的是 REAL 类型Python 侧要用 f但 REAL 有精度损失我一般不用。这段解析逻辑要和 PLC 侧的 TSEND 缓冲区布局严格对应错一个字节后面全乱。5. 搭网关程序把 S7 数据写进 SQLSERVER 的中间层5.1 网关程序选型与部署位置网关程序可以跑在工控机上也可以跑在 SQLSERVER 同一台服务器上。我一般放在工控机因为工控机离 PLC 近网络稳定而且工控机通常 24 小时开机。程序用 Python 写最省事pysnap7 库直接读 S7 1200 的数据块pyodbc 写 SQLSERVER。如果客户要求用 C#就用 S7.Net 加 SqlClient逻辑一样。# 安装 Python 依赖 pip install python-snap7 pyodbc # 注意 python-snap7 需要 snap7.dllWindows 下要手动放到 System32 或程序目录python-snap7 在 Windows 上装完不会自动带 snap7.dll得去官网下对应版本32 位/64 位要和 Python 一致放到 C:\Windows\System32 或者程序同目录。我踩过坑Python 是 64 位dll 下了 32 位import 时报“找不到指定模块”换了 64 位 dll 才过。5.2 连接 PLC 和 SQLSERVER 的代码骨架import snap7 import pyodbc import struct import time # 连接 PLC plc snap7.client.Client() plc.connect(192.168.0.1, 0, 1) # IP, rack, slot # 连接 SQLSERVER conn pyodbc.connect( DRIVER{ODBC Driver 17 for SQL Server}; SERVER192.168.0.100,1433; DATABASEPLCData; UIDplc_user; PWDPlc2024; TrustServerCertificateyes; ) cursor conn.cursor() while True: # 读 DB1 的前 100 字节 data plc.db_read(1, 0, 100) sn, torque parse_s7_data(data) cursor.execute( INSERT INTO TorqueLog (ProductSN, TorqueValue, Result) VALUES (?, ?, ?), sn, torque, OK ) conn.commit() time.sleep(0.5)plc.connect 的 rack 和 slot 参数S7 1200 一般是 0 和 1如果连不上试 0 和 0。db_read 的第三个参数是读取长度要和 PLC 侧 TSEND 的缓冲区一致。pyodbc 的连接字符串里 TrustServerCertificateyes 是为了跳过自签名证书验证SQLSERVER 2019 默认启用加密不加这个会报 SSL 错误。commit 要每插一条就提交不然断电丢数据。5.3 异常重连与断线续传产线环境网络抖动是常态网关程序必须能自动重连。我一般用 try-except 包住 plc.db_read 和 cursor.execute出错后关闭连接等 5 秒重连。def safe_read(plc, db_num, start, size): try: return plc.db_read(db_num, start, size) except Exception as e: print(fPLC read error: {e}, reconnecting...) plc.disconnect() time.sleep(5) plc.connect(192.168.0.1, 0, 1) return None断线续传这块如果 PLC 侧有缓存网关程序重连后从上次的偏移继续读如果没缓存就只能丢数据。我一般会在 PLC 侧用一个环形缓冲区存最近 1000 条记录网关程序重连后先读缓冲区再读实时数据。这个逻辑复杂一点但产线停线时能救回不少数据。6. 避坑与排查S7 1200 连 SQLSERVER 最常见的 5 个翻车点6.1 现象TCON 报 0x80C4连接被拒绝原因网关程序没启动或者监听的端口和 TIA Portal 里组态的端口不一致。我遇到过网关程序绑的是 127.0.0.1:2000PLC 连的是 192.168.0.100:2000结果 PLC 侧一直报 0x80C4。解决网关程序监听地址改成 0.0.0.0或者明确绑 192.168.0.100。另外检查 Windows 防火墙有没有放行 2000 端口。6.2 现象SQLSERVER 报“登录失败用户 plc_user”原因SQLSERVER 身份验证模式没选“混合模式”或者 plc_user 没建对。我试过在 SSMS 里建了登录名但没建数据库用户结果登录成功但访问 PLCData 时报“拒绝访问”。解决确认 SQLSERVER 属性里“安全性”是“SQL Server 和 Windows 身份验证模式”然后在 PLCData 库里执行 CREATE USER plc_user FOR LOGIN plc_user再给 db_datareader 和 db_datawriter 角色。6.3 现象TSEND 报 0x80B1长度错误原因TSEND 的 LEN 参数和 DATA 缓冲区实际长度不一致。我建了一个 100 字节的数组但 LEN 填了 200PLC 直接报错。解决LEN 必须小于等于 DATA 缓冲区的字节数而且要和接收端约定的长度一致。如果发送的是变长数据LEN 用实际数据长度别用缓冲区总长度。6.4 现象数据入库后中文乱码原因SQLSERVER 排序规则不是 Chinese_PRC_CI_AS或者网关程序编码不对。我遇到过 PLC 发的是 GBK 编码的中文Python 侧用 ascii 解码直接报错。解决PLC 侧统一用 ASCII 发序列号中文产品名在网关程序里转成 UTF-8 再入库SQLSERVER 字段用 NVARCHAR。如果排序规则已经错了只能重建库改排序规则很麻烦。6.5 现象网关程序跑几天后内存暴涨原因pyodbc 的 cursor 没关或者 snap7 的 client 没断。我写第一版时 while 循环里每次新建 cursor跑一天内存就满了。解决cursor 和 connection 在循环外建一次循环内复用plc 连接断了要 disconnect 再 connect别反复 new Client()。另外Python 的 gc 不会自动回收 snap7 的 C 层内存长时间跑建议每天定时重启一次网关程序。7. 装完怎么验证从一条模拟数据到端到端跑通验证这套环境我一般分三步走每一步都有明确的成功标志别跳步。第一步单独验 SQLSERVER。在网关机上用 sqlcmd 插入一条测试数据然后在 SSMS 里查出来。这一步只验数据库本身不涉及 PLC。INSERT INTO TorqueLog (ProductSN, TorqueValue, Result) VALUES (TEST001, 45.67, OK); SELECT * FROM TorqueLog WHERE ProductSN TEST001;如果查得到说明 SQLSERVER 的 TCP 1433、账号权限、表结构都没问题。查不到就回头查防火墙和登录名。第二步单独验 PLC 通信。在 TIA Portal 里监控 TCON_DB 的 DONE 位手动触发一次 TCON看 DONE 是否置位。如果 DONE 置位再用 TSEND 发一组固定数据在网关程序侧打印收到的原始字节。这一步不写数据库只看数据能不能从 PLC 到网关机。# 网关程序侧打印原始数据验证 PLC 到网关机的链路 data plc.db_read(1, 0, 100) print( .join(f{b:02X} for b in data))打印出来的十六进制要和 PLC 侧发送缓冲区的内容一致。如果全是 00说明 PLC 侧没触发 TSEND或者 DB 块偏移不对。第三步端到端联调。把网关程序的数据库写入逻辑打开在 PLC 侧手动触发一次发送然后在 SSMS 里查 TorqueLog 表有没有新记录。成功标志是PLC 侧 TSEND_Done 置位网关程序打印“Insert OK”SSMS 里查到对应序列号的数据。如果中间任何一环断了按前面的排查章节逐段定位。我自己的习惯是每次部署完这套环境都会先跑 100 条模拟数据用 Python 脚本批量插入然后核对 PLC 侧发送计数和数据库记录数是否一致。有一次发现差了 3 条查了半天是网关程序 commit 没加断电重启丢了 3 条缓存。从那以后我每次上线前都强制走一遍“100 条模拟数据 断电重启 核对记录数”的流程确认断线续传逻辑真的生效。希望这套步骤能帮你少走点弯路。本文还有配套的精品资源点击获取