1. 两台S7-1200做TCP通信我踩过的坑和最终跑通的完整路径车间里两台S7-1200要互相传数据这个需求在产线改造里太常见了。可能是主站要读副站的压力值可能是包装机要把计数发给码垛机也可能是两台设备要做一个简单的主从联动。很多人第一反应是走PROFINET做智能从站配置简单、周期稳定但实际现场经常遇到一个尴尬情况两台PLC分属不同的小系统程序由不同的人写甚至中间隔了一段网络这时候用开放式TCP通信反而是最省事、耦合度最低的方案。TCP通信不依赖双方的程序结构和硬件组态只要网络通、端口对、报文格式约定好数据就能走起来。这篇内容我打算把两台S7-1200之间做TCP通信的完整步骤拆开讲清楚。核心关键词就是S7-1200、TCP通信、TSEND_C、TRCV_C、连接参数、IP地址与端口。适合谁看刚接触西门子开放式通信的电气工程师、需要快速把两台设备数据打通的一线调试人员以及那些被连接建立不起来数据收不到一断电就断连折腾过的朋友。我会从方案选型讲起把指令块的关键参数一个个掰开再给出一套可以直接照着做的配置流程最后把我实际调试中遇到的典型问题和排查思路整理出来。整个过程不依赖任何特殊工具TIA Portal里就能全部完成。先说清楚一个前提S7-1200的开放式TCP通信本质上是让PLC作为一个普通的TCP端点去收发字节流。它不像PROFINET那样有周期性的IO数据交换也没有硬件层面的实时保证数据是一段一段的字节收多少、什么时候收全靠程序自己判断。理解这一点非常关键因为后面所有的参数设置和编程思路都是围绕字节流这个特性展开的。你把它想成两台电脑用网线聊天而不是两个设备共享一块内存思路就对了。2. 方案选型为什么是TCP而不是别的2.1 三种常见通信方式的取舍逻辑两台S7-1200之间传数据摆在面前的路其实有好几条我先把它们摆出来对比一下你就明白为什么TCP在这个场景里经常是首选。通信方式配置复杂度实时性耦合程度适用场景PROFINET智能从站中等需组态GSD高周期IO高依赖硬件组态同一项目、强实时联动S7通信PUT/GET低需勾选访问权限中等中等同品牌、少量数据开放式TCP低纯程序实现一般取决于扫描低只依赖网络跨系统、跨程序、灵活报文PROFINET智能从站的方式确实稳定但它要求两台PLC在同一个TIA项目里做硬件组态或者至少一方能拿到另一方的GSD文件。现场经常是两台设备来自不同的集成商程序各管各的你很难去动对方的硬件组态。S7通信的PUT/GET用起来简单但它需要在对方CPU的属性里勾选允许来自远程对象的PUT/GET通信访问这个操作意味着你要改对方的硬件配置并重新下载协调成本不低。开放式TCP的优势就在这里它只要求两边网络能通剩下的全部在用户程序里完成。你不需要动对方的硬件组态不需要GSD文件甚至对方是不是西门子都不重要——只要它支持标准TCP报文格式约定好就能通信。这种低耦合特性在多方协作的现场就是最大的便利。2.2 TCP通信的代价你必须自己管好字节流选了TCP就要接受它的脾气。它给你的是纯粹的字节流没有消息边界。什么意思发送方调一次发送指令发了10个字节接收方可能一次收到10个字节也可能分两次收到6个和4个甚至可能和下一批数据粘在一起。这就像寄信你投进去的是一封封独立的信但对方拆开信封看到的可能是一整卷连续的纸带得自己按约定去切分。所以用TCP通信报文格式的设计和接收逻辑的健壮性比指令块本身更重要。我后面会专门讲怎么用固定长度或者带长度头的报文来规避粘包问题。这一点是很多新手最容易忽略的地方指令块配置得再对接收逻辑写得不严谨数据照样会错位。提示如果你的数据量很小、格式固定用固定长度的报文是最省心的做法。双方约定好每次就发N个字节接收方凑够N个字节再解析逻辑简单不容易出错。2.3 连接角色怎么分主动方与被动方TCP通信里一定有一方是主动建立连接的客户端角色另一方是被动等待连接的服务器角色。在S7-1200里这个角色由指令块决定用TSEND_C并设置连接参数为主动连接的一方是客户端用TRCV_C并设置为被动连接的一方是服务器。实际项目里怎么分我的习惯是让数据需求方做主动方。比如A机要读B机的数据那就让A机主动去连B机B机被动监听。这样连接的发起点在需求方逻辑上更顺出问题时也容易定位——需求方连不上问题大概率在网络或者对方没准备好。反过来如果让被动方去连主动方排查方向就绕了。还有一个细节主动方需要知道被动方的IP和端口被动方只需要开放一个本地端口等待连接。所以端口号在被动方这边定义主动方去填。这个端口号建议避开常用端口选一个双方约定好的、不冲突的值比如2000以上的数值。3. 核心指令块拆解TSEND_C与TRCV_C的关键参数3.1 TSEND_C主动方发送与连接管理一肩挑TSEND_C这个块很有意思它不只是发送还顺带把连接的建立和维护都管了。你把它理解成一个带连接管理的发送器更准确。它的关键引脚我逐个说。REQ是发送触发上升沿有效。这里有个坑很多人以为REQ给个持续的高电平就会一直发其实不是它是边沿触发的。你要周期性发送就得自己做一个脉冲比如用一个定时器或者用扫描周期的上升沿去触发。我一般用一个周期性的时钟存储器位或者干脆在OB1里用一个自复位逻辑产生脉冲。CONT是连接控制这个引脚必须保持为1连接才会维持。如果你把它置0连接会被断开。有些朋友调试时发现连接老是断最后查出来是CONT被某个逻辑误置0了。记住CONT是连接使能不是发送使能它得一直亮着。CONNECT是一个指向连接描述数据结构的引用这个数据结构TCON_IP_v4类型里放着对方的IP地址、端口号、本地端口、连接类型等信息。这个结构体是核心配错了连接就建不起来。我后面单独讲怎么填。DATA指向你要发送的数据区可以是M区、DB块或者位存储器。注意它发的是字节流长度由LEN决定。LEN是本次发送的字节数。这里要特别注意LEN不能超过DATA区实际可用的长度否则会报错。我一般把DATA指向一个足够大的数组LEN按实际有效数据填。DONE、BUSY、ERROR、STATUS是状态输出。DONE表示本次发送完成ERROR表示出错STATUS给出具体错误码。调试阶段一定要盯着STATUS看它是定位问题的第一手信息。3.2 TRCV_C被动方的接收与连接维持TRCV_C负责接收同时也能管理连接。它的引脚逻辑和TSEND_C有相似之处但接收行为有自己的特点。EN_R是接收使能保持为1才允许接收。这个引脚和TSEND_C的CONT有点像都得常亮。如果你把它置0接收就停了。CONT同样是连接控制保持为1维持连接。CONNECT同样指向连接描述结构但被动方的这个结构里本地端口是重点对方IP可以填也可以不填取决于你是否要限定只接受特定来源。DATA指向接收缓冲区LEN是期望接收的最大字节数。这里的关键是TRCV_C每次收到数据后会在RCVD_LEN里告诉你实际收到了多少字节。你解析数据时要用RCVD_LEN而不是想当然地认为收满了LEN。RCVD_LEN是实际接收长度这个输出非常重要是处理字节流的核心依据。NDR是新数据就绪每收到一批新数据它会给出一个脉冲。我一般用NDR的上升沿去触发数据解析逻辑这样能保证每次都是处理新到的数据。3.3 连接描述结构TCON_IP_v4怎么填这个结构体是连接能不能建起来的关键我把它拆开说。在TIA Portal里你可以在DB块里定义一个TCON_IP_v4类型的变量然后逐项赋值。参数含义主动方填法被动方填法InterfaceId本地接口硬件标识填CPU的PROFINET接口ID同左ID连接ID双方约定一个唯一值与主动方一致ConnectionType连接类型16#0BTCP16#0BTCPActiveEstablished是否主动建立TRUEFALSERemoteAddress对方IP填被动方IP可填0.0.0.0或不限定RemotePort对方端口填被动方监听端口一般填0LocalPort本地端口可填0由系统分配填约定的监听端口InterfaceId这个值很多人不知道怎么找。它其实就是CPU上那个PROFINET接口的硬件标识符在设备组态里点开CPU看属性里的硬件标识符就能找到通常是一个比较大的数值。填错了连接直接建不起来STATUS会给你报错。ID这个连接ID双方必须一致它是标识这条连接的编号。我一般习惯用1或者100这种好记的值但要注意如果一台PLC上有多条TCP连接每个连接的ID必须不同。ActiveEstablished这个布尔量直接决定角色TRUE就是主动方客户端FALSE就是被动方服务器。这一位填反了两边都在等对方连连接永远建不起来。这是新手最常见的错误之一。4. 完整实操流程从零把两台PLC的TCP通道打通4.1 第一步网络基础配置在动任何指令块之前先把网络打通。两台S7-1200各自在设备组态里设置好IP地址和子网掩码确保它们在同一网段。比如一台设192.168.0.10另一台设192.168.0.11掩码255.255.255.0。用网线直连或者经过交换机都行直连的话现在的网口都支持自动翻转普通网线就能用。配完IP下载硬件组态后先别急着写程序用电脑ping一下两台PLC确认网络层是通的。这一步看着简单但现场很多通信不通最后查出来就是IP设错、网段不对、或者网线接在了不同的隔离网口上。先把这层确认了后面排查能省一大半时间。注意S7-1200有些型号有两个网口但它们是同一个接口的两个物理端口不是两个独立网段。别指望一个口接一个网络、另一个口接另一个网络来做隔离它们共享同一个IP。4.2 第二步定义报文格式和连接参数在写程序前先把两件事约定清楚写在一张纸上双方确认。第一是报文格式。我推荐用固定长度报文比如双方约定每次传16个字节前2个字节是数据标识后面是具体数据。固定长度的好处是接收方凑够16字节就解析逻辑简单。如果数据长度会变那就用长度头数据的格式前2个字节表示后续数据长度接收方先读长度再读数据。这两种方式都能规避粘包问题选一种双方都认可的就行。第二是连接参数。约定好谁主动谁被动、被动方监听哪个端口、连接ID用多少。我一般做个简单的约定表项目约定值主动方A机数据需求方被动方B机数据提供方被动方监听端口2000连接ID1报文长度固定16字节把这些定下来后面两边各填各的参数不会打架。4.3 第三步被动方服务器程序实现先写被动方因为它是等待连接的一方先让它准备好主动方再去连调试顺序更顺。在B机的程序里新建一个DB块定义一个TCON_IP_v4类型的变量按被动方填法赋值ActiveEstablished设为FALSELocalPort设为2000ConnectionType设为16#0BID设为1InterfaceId填B机CPU的接口硬件标识。RemoteAddress和RemotePort被动方一般不用填具体值。然后调用TRCV_C块。EN_R接一个常TRUE的变量或者直接接TRUECONT也接TRUECONNECT接刚才那个结构体变量DATA指向一个16字节的接收数组LEN填16。NDR、RCVD_LEN、ERROR、STATUS接到对应的变量上方便监控。被动方程序下载后CPU会开始在2000端口上监听。这时候你用电脑telnet一下B机的2000端口如果电脑上有telnet客户端能连上就说明监听成功了。当然更直接的办法是等主动方来连看STATUS的变化。4.4 第四步主动方客户端程序实现A机这边同样新建DB块定义TCON_IP_v4变量按主动方填法ActiveEstablished设为TRUERemoteAddress填B机IP192.168.0.11RemotePort填2000ConnectionType填16#0BID填1InterfaceId填A机自己的接口标识LocalPort可以填0让系统分配。调用TSEND_C块。REQ接一个周期性脉冲CONT接TRUECONNECT接结构体DATA指向发送数组LEN填16。这里有个顺序问题连接建立和发送是两回事。TSEND_C会先建连接连接建好后才能发数据。所以你第一次触发REQ时如果连接还没建好这次发送可能会失败或者被忽略。我的做法是等STATUS显示连接已建立通常是16#7002或者DONE有输出之后再开始周期性触发REQ。发送的脉冲频率别太高我一般用500ms到1s的周期。TCP通信本身有开销发太快意义不大还容易让BUSY一直挂着。用时钟存储器的一个位比如1Hz做触发就挺合适。4.5 第五步联调验证与数据核对两边程序都下载后先看被动方的STATUS。如果主动方连上来了被动方的TRCV_C状态会从等待变成已连接。然后看主动方的TSEND_CDONE应该有脉冲输出ERROR为0。数据核对这一步别偷懒。我在A机的发送数组里预先填一组有规律的值比如1、2、3、4……然后在B机的接收数组里在线监控看收到的值是不是和发的一致、位置对不对。如果收到的数据错位了八成是报文长度或者接收逻辑的问题。如果RCVD_LEN每次都是16数据也对得上那这条通道就算通了。联调时我习惯做一个心跳机制主动方周期性发送被动方如果超过一定时间没收到新数据NDR长时间无脉冲就置一个通信故障标志。这样通信断了能及时发现而不是等用到数据时才发现是旧值。5. 常见问题与排查技巧实录5.1 连接建不起来的几种典型情况调试TCP通信十有八九的问题卡在连接建不起来。我把遇到过的典型情况整理成一张速查表按STATUS错误码和现象对照排查。现象可能原因排查方向STATUS报16#80C8连接ID冲突或连接参数不一致检查双方ID、端口、连接类型是否一致STATUS报16#80A1接口标识符错误核对InterfaceId是否为CPU实际接口ID一直BUSY无DONE对方未监听或网络不通ping对方、确认被动方程序已运行连接建立后立即断开CONT被置0或对方主动断开检查CONT逻辑、对方程序状态主动方报连接被拒绝端口填错或对方端口未开放核对RemotePort与被动方LocalPort16#80C8这个错误我遇到最多基本都是双方连接参数对不上。有一次是主动方ID填了1被动方填了2两边各自觉得没问题连了半天连不上。还有一次是连接类型一边填了TCP一边填了别的也是白折腾。所以联调前那张约定表真的有用双方照着填能避免一大半的低级错误。5.2 数据收不到或收错位的排查思路连接通了但数据不对问题就转移到报文和接收逻辑上。先看被动方的RCVD_LEN如果它一直是0说明根本没收到数据那问题在发送侧——检查主动方的REQ有没有脉冲、LEN填的对不对、DATA区是不是有效。如果RCVD_LEN有值但数据错位那就是报文格式或者解析逻辑的问题。粘包是这里最隐蔽的坑。假设主动方每500ms发16字节被动方如果处理不及时可能一次收到32字节两批粘在一起。这时候如果你还按固定16字节去解析第二批数据就错位了。解决办法有两个一是接收缓冲区开大一点用RCVD_LEN判断实际收到多少按16字节循环切分二是报文里带长度头先读头再读体。我倾向于第一种逻辑简单只要接收逻辑写成循环处理就行。还有一种情况是数据看起来对但偶尔跳变。这往往是主动方发送频率和被动方解析频率不匹配或者发送数组在发送过程中被其他逻辑改写了。发送数组最好用独立的DB块发送前先组好数据别让别的逻辑在发送瞬间去动它。5.3 断电重启后连接恢复的处理现场设备难免断电重启TCP连接断了之后能不能自动恢复是衡量程序健壮性的一个点。TSEND_C和TRCV_C本身有一定的重连能力但前提是CONT保持为1、连接参数没变。实际测试下来主动方断电重启后只要程序重新运行、CONT为1它会自动尝试重连被动方。被动方这边如果先重启主动方可能需要等一个重连周期。我的经验是加一个连接状态监控用TSEND_C的STATUS判断连接是否正常如果连续一段时间STATUS显示断开就复位一下REQ触发让块重新走一遍连接建立流程。有些版本的固件在异常断开后需要重新触发才能恢复加这个兜底逻辑能省不少现场跑腿。提示调试阶段把双方的STATUS和RCVD_LEN都做到监控表里联调时盯着看比猜问题快得多。STATUS的十六进制码对照手册查基本能定位到具体原因。5.4 几个容易被忽略的实操细节第一个细节是接收缓冲区的初始化。TRCV_C的DATA区在上电时最好清零否则如果第一次收到的数据比预期短缓冲区里残留的旧值可能被误当成有效数据。我一般在上电初始化逻辑里把接收数组清一遍。第二个细节是发送数据的字节序。如果双方传的是多字节数值比如INT、REAL要注意字节序问题。S7-1200内部是大端但如果你和第三方设备通信对方可能是小端这时候需要在发送前或接收后做字节交换。两台S7-1200之间通信一般不用管但跨品牌就要留意。第三个细节是端口号的选择。避开102S7通信、80、443这些常用端口选2000以上的。另外有些网络设备或安全策略会限制特定端口现场如果发现端口不通换个端口试试也是排查手段之一。第四个细节是扫描周期的影响。TRCV_C和TSEND_C是异步执行的它们的完成不占用扫描周期的等待但BUSY状态会持续若干个周期。如果你在OB1里高频调用注意别让REQ触发得太密给块留出处理时间。我一般用定时器或者时钟位控制触发节奏不追求极致频率。6. 我在这套方案上的一些实际体会这套TCP通信方案我在好几个现场用过从简单的两机数据对传到多台设备组网稳定性总体是可靠的。它的价值在于解耦——不用动对方硬件组态不用协调GSD文件双方只要把IP、端口、报文格式三样东西对齐剩下的各写各的程序。对于多方协作、程序归属复杂的现场这种低耦合特性省下的协调成本远比多写几行接收逻辑划算。真正需要花心思的地方不在指令块配置而在报文设计和接收逻辑的健壮性。指令块填参数是死功夫照着约定填就行但字节流的切分、粘包的处理、断线重连的兜底这些才是决定这套通信能不能长期稳定跑的关键。我见过太多配置全对但数据偶尔错位的案例最后都是接收逻辑没处理好RCVD_LEN和粘包。如果后续要扩展这套结构很容易加东西。比如加一个心跳报文做链路检测加一个序号字段做丢包判断或者把固定长度报文改成带长度头的变长报文来传更灵活的数据。基础通道搭好了上层怎么设计报文就看具体业务需求了。我个人建议初期先用最简单的固定长度报文把通道跑通确认稳定后再逐步加复杂度别一上来就设计一套复杂的协议调试起来反而找不到北。