WinCAT3与TCP/IP通讯:PLC以太网通讯架构与Socket实战

📅 发布时间:2026/9/30 4:03:54
WinCAT3与TCP/IP通讯:PLC以太网通讯架构与Socket实战
简介这是一份由Beckhoff工程师整理并分享的TwinCAT 3 TCP/IP通讯技术文档面向使用倍福控制器与第三方TCP/IP设备联调的自动化工程师、PLC程序员。文档以CX5020嵌入式控制器为例系统讲解Socket Tool调试助手的使用、FB_SocketConnect/FB_SocketSend/FB_SocketReceive/FB_SocketClose四个功能块的调用方法并区分Client与Server两种通讯场景给出实验步骤便于读者快速理解并应用到实际项目中。资源包共1个docx文件大小672KB内容以原理介绍、接线拓扑示意、功能块代码片段和调试流程为主适合入门后进阶对照学习。目前已有194人浏览学习可作为TwinCAT 3网络通讯开发的参考资料。1. 这个标题背后WinCAT3 与 TCP/IP 通讯到底解决什么“WinCAT3 TCP IP 通讯”这个标题第一眼容易让人误以为是笔误倍福官方的产品名是 TwinCAT 3但很多现场工程师在文档、程序注释和快捷方式里都会顺手写成 WinCAT3。它要解决的是 Windows 平台上 PLC 与外部系统之间的以太网数据交换上位机读状态、MES 写配方、视觉相机把坐标发过来、机器人控制器和 PLC 握手基本都是靠 TCP/IP 这个最基础也最通用的通道。下面按我落地这类通讯的习惯把协议栈分工、最小可运行代码、最容易翻车的点和验证方法串一遍适合第一次在 TwinCAT 3 里做以太网通讯的电气工程师也适合被 TCP/IP 调试折腾过的上位机开发。2. WinCAT3 的 TCP/IP 通讯架构先分清实时上下文、Socket 和 ADS2.1 TCP/IP 协议栈为什么能扛住工业通讯要把 WinCAT3 里的 TCP/IP 通讯做好第一步不是急着拖功能块而是把协议栈的分层理解清楚。常见的工业串口如 RS485、CAN 和 Modbus RTU都是面向字节或帧的总线协议TCP/IP 则是一套完整的网络协议栈从上到下依次是应用层、传输层、网络层、网络接口层。应用层的数据每往下一层就被加上一个协议头TCP 头里带源端口和目的端口IP 头里带源地址和目的地址最后在网卡上变成以太网帧。这就是数据在 TCP/IP 模型中传输的过程图也是理解“为什么抓包能看到多层头部”的基础。TCP 和 UDP 的差别在这里也要拎清。UDP 只管把数据报发出去不保证顺序也不重传适合广播、组播或强实时场景TCP 则是面向连接的可靠传输要经过三次握手建立连接数据带序号接收方确认超时则重传。WinCAT3 控制现场要做“通讯”绝大多数上位机交互都选 TCP因为丢失一个字节都可能造成工艺事故。代价是 TCP 协议本身有建立连接和数据确认的开销实时性不如 EtherCAT这决定了它不能替代现场总线只能作为上位机和外设的通讯手段。2.2 WinCAT3 的实时上下文与 Windows 网络栈WinCAT3 的实时核和普通 Windows 程序是共存在一台主机里的。PLC 程序跑在实时任务里EtherCAT 等现场总线直接通过独立网卡和实时驱动收发而 TCP/IP 功能块在 PLC 任务里被调用时底层仍然由 Windows 的 TCP/IP 协议栈处理再经过 Winsock 接口转发。这意味着TCP/IP 通讯不可能做到像 EtherCAT 那样微秒级确定性它只能保证“数据最终会到”不能保证“这一毫秒一定到”。所以在 WinCAT3 的项目规划阶段就要把网卡任务分开。EtherCAT 主站占用一张物理网卡TCP/IP 通讯占用另一张物理网卡否则实时帧和普通以太网包挤在同一张网卡上重载时丢帧几乎必然发生。对于通讯速度也要有一个合理预期一次 TCP 读写从 PLC 发起到底层网卡发出通常需要几十微秒到几毫秒不等具体看 Windows 调度和网卡负载。设计上位机通讯逻辑时不建议在 1ms 或 250μs 的高速任务里做阻塞式 Socket 读写而应该把通讯放到后台任务或独立周期任务中去轮询。2.3 三条落地路径原生 Socket、ADS 与 Modbus TCP 怎么选在 WinCAT3 里做 TCP/IP 通讯常见做法是三条路径并行考虑而不是一条路走到黑。第一条是 PLC 侧调用原生 Socket 功能块。倍福提供的 Tc2_TcpIp 库或 TF6310 TCP/IP 功能包里封装了创建、绑定、监听、连接、发送、接收、关闭等标准 Socket 动作。这条路适合和任意支持 TCP/IP 的设备对接比如视觉系统、C# 上位机、西门子 1200 PLC、扫码枪等因为协议完全开放你可以自定义帧格式通讯行为和普通 PC 编程里的 Socket 几乎一致。第二条是 ADSAutomation Device Specification。ADS 是倍福自己的设备通讯规范用于 TwinCAT 系统内部以及倍福生态的上位机软件之间交换数据。它的优势是几乎不需要写网络代码把变量映射好上位机通过 ADS 库就能读写。但第三方设备不支持 ADS所以它只适合和倍福自身的 HMI、VB/C# 程序或 TwinCAT 路由连接。第三条是直接上 Modbus TCP。如果对方是西门子、三菱、欧姆龙这类第三方 PLC或是变频器、仪表Modbus TCP 是工业领域最通用的应用层协议之一。WinCAT3 的 PLC 可以作为一个 Modbus TCP 客户端去轮询设备寄存器也可以作为服务器让别人来读写。它的好处是跨厂商、资料多坏处是数据建模受限于寄存器、线圈和保持寄存器复杂结构要自己映射。这三条路径的选择逻辑我一般这样排对方是倍福生态用 ADS对方是任意网络设备用原生 Socket对方是第三方 PLC 或存量仪表用 Modbus TCP。如果只是做项目交付原生 Socket 加自定义应用层协议是覆盖面最广的做法下面章节就围绕这条路展开。路径典型载体适用对象优点主要风险原生 SocketTc2_TcpIp/TF6310 的 FB_Socket*任意 TCP/IP 设备开放、可定制PLC 侧代码量较大ADSTwinCAT ADS倍福上位机/HMI开发快、映射透明第三方设备不支持Modbus TCP标准 Modbus 功能块第三方 PLC、仪表跨厂商通用数据结构受寄存器模型限制3. 在 WinCAT3 里跑通 TCP/IP 通讯PLC 当服务器C# 上门连接3.1 建工程和引用库先把 Tc2_TcpIp 加进去在 TwinCAT 3 里建一个 TCP/IP 通讯项目前提是要把 Socket 功能库引用进来。打开 TwinCAT XAE 的解决方案在 PLC 项目下右击 References选 Add Library搜索框里输入 Tc2_TcpIp。如果你找到的是 Tf6310 之类的功能包名称说明这台机器装了倍福的 TCP/IP 功能包效果一致。添加成功后项目里会出现 T_HSOCKET、FB_SocketCreate、FB_SocketConnect、FB_SocketSend 等类型和功能块。如果搜索不到这个库先不要急着写代码。打开 TwinCAT 3 的 TwinCAT Communication 功能选单找到 TCP/IP 或 TF6310确认是否安装了对应授权。没有授权时倍福通常提供试用版激活后重启 TwinCAT 才生效。端口方面建议选一个大于 1024 的固定端口例如 8500避开常见软件默认占用的端口。上位机和 PLC 在同一网段IP 固定不要用 DHCP。3.2 PLC 侧状态机从 Create 到 Accept 的最小服务器下面这段代码是 PLC 作为 TCP Server 的最小骨架用状态机把套接字的生命周期串起来。以 Tc2_TcpIp 库的功能块为例TwinCAT 的 Socket 功能块几乎都是脉冲触发Execute 为 TRUE 后开始动作Done 输出为 TRUE 代表完成下一次再触发前必须把 Execute 拉回 FALSE。PROGRAM Main_TcpServer VAR step : INT : 0; fbCreate : FB_SocketCreate; fbBind : FB_SocketBind; fbListen : FB_SocketListen; fbAccept : FB_SocketAccept; fbReceive : FB_SocketReceive; hServer : T_HSOCKET; hClient : T_HSOCKET; nPort : WORD : 8500; rxData : ARRAY[0..1023] OF BYTE; nRxLen : UDINT; bAccepted : BOOL; END_VAR CASE step OF 0: // 创建监听套接字 fbCreate(Execute : TRUE, eSocketType : TcpServer, hSocket hServer); IF fbCreate.Done THEN fbCreate(Execute : FALSE); step : 1; END_IF 1: // 绑定端口 fbBind(Execute : TRUE, hSocket : hServer, nPort : nPort); IF fbBind.Done THEN fbBind(Execute : FALSE); step : 2; END_IF 2: // 开始监听 fbListen(Execute : TRUE, hSocket : hServer, nBacklog : 5); IF fbListen.Done THEN fbListen(Execute : FALSE); step : 3; END_IF 3: // 接受客户端连接 fbAccept(Execute : TRUE, hSocket : hServer, hClient hClient); IF fbAccept.Done THEN fbAccept(Execute : FALSE); bAccepted : TRUE; step : 4; END_IF 4: // 接收数据 fbReceive( Execute : TRUE, hSocket : hClient, pData : ADR(rxData), nMaxLen : SIZEOF(rxData), nRecvLen nRxLen ); IF fbReceive.Done THEN // 对 rxData[0..nRxLen-1] 做应用层解析 fbReceive(Execute : FALSE); END_IF END_CASE这段代码的关键是状态机的步进逻辑。step 0 到 3 只在 PLC 启动时执行一次把监听套接字准备好step 4 在客户端接入后循环调用。注意 fbReceive 的 pData 参数用 ADR(rxData) 取缓冲区地址nMaxLen 是缓冲区能容纳的最大字节数不能超过数组大小否则会越界写坏内存。nRecvLen 输出的是实际收到的字节数若为 0通常代表客户端已经关闭连接此时应该回到 step 3重新等待下一个客户端接入。TwinCAT 的 Socket 接收是异步方式不是阻塞在任务里等数据所以主循环仍然可以干别的事。看到这里你可能会有个疑问为什么不是直接在程序里调用一个“连接”功能块因为 TCP/IP 是面向连接的服务器必须先创建套接字、绑定端口、进入监听状态才有资格等待客户端。这个流程和 Windows 下用 C 写 Socket 服务端完全一致只是换成了 PLC 的功能块语言。3.3 上位机 C# 客户端的最小代码PLC 侧服务器就绪后用 C# 写一个最简单的 TcpClient 客户端验证通讯链路。下面这段代码可以作为 WinForms、WPF 或控制台程序里的核心片段。要注意 TcpClient 的 ReceiveTimeout 和 SendTimeout 都需要手动设置否则默认情况下可能长时间阻塞。using System.Net.Sockets; var client new TcpClient(); client.ReceiveTimeout 3000; client.SendTimeout 3000; string ip 192.168.0.10; int port 8500; await client.ConnectAsync(ip, port); NetworkStream ns client.GetStream(); byte[] request { 0x01, 0x03, 0x00, 0x00, 0x00, 0x10 }; await ns.WriteAsync(request, 0, request.Length); byte[] buffer new byte[256]; int received await ns.ReadAsync(buffer, 0, buffer.Length); Console.WriteLine($recv {received} bytes); client.Close();这段代码是最朴素的“连一次、发一包、读一包”流程。ConnectAsync 会完成 TCP 三次握手WriteAsync 把应用层请求发过去ReadAsync 在没有数据时会一直等直到 ReceiveTimeout 超时抛出异常。实际交付时不能只发一次就关闭连接需要在上位机里维护一个长连接对象并处理断线重连。PLC 侧那个状态机里如果 nRxLen 为 0也要主动调用 FB_SocketClose 关闭 hClient否则资源释放不及时会导致第二次连接失败。4. WinCAT3 TCP IP 通讯避坑五个踩下去就让人头疼的问题4.1 连接超时Windows 防火墙悄悄把端口拒了现象是 C# 客户端调用 ConnectAsync 后抛 SocketException提示超时或被拒而 PLC 侧已经成功绑定端口并监听了。用 netstat -ano 查端口发现 8500 确实在 LISTEN但外部设备就是连不上。原因是 Windows 防火墙默认阻止入站连接。TwinCAT 机器上的 PLC 绑定了端口但外部 IP 的数据包在到达 Winsock 之前就被防火墙拦截了。解决方法是把所用端口加进防火墙入站规则打开“高级安全 Windows 防火墙”新建入站规则选择端口填 TCP 8500允许连接。如果是开发调试阶段可以先用“允许所有连接”的规则验证确认能连通后再收紧规则。排查时不要只在本机 ping 自己要用另一台设备 telnet 测试端口通不通。4.2 客户端第一次连接成功断开后第二次连不上现象是最开始调试时上位机连上 PLC 一次正常通讯几秒后关闭连接再重新连接PLC 侧 accept 功能块一直不 Done上位机报超时。原因是 FB_SocketAccept 功能块完成一次接受后如果代码没有把它复位也没有关闭已断开的 hClient套接字内部状态还停留在“已连接且未释放”上导致新的连接请求进不来。解决办法是在收到 nRxLen 0 或检测到接收超时后主动调用 FB_SocketClose 关闭 hClient并把 step 退回 3重新进入 accept 状态。同时要给 fbAccept 的 Execute 一个下降沿让它能再次触发不然功能块不会进入新的接受流程。4.3 收到的数据对不上TCP 流没有消息边界现象是上位机连续发送两包数据PLC 一次把所有字节都收完或者上位机发了一包 200 字节的数据PLC 分成两次收到第一次 128 字节第二次 72 字节。原因是 TCP 是字节流协议它只保证字节顺序不保证一次 WriteAsync 对应一次 Receive。应用层数据完全可能被内核拼接或拆分。解决办法是必须自定义应用层帧格式比如固定帧头、长度字段、命令字段、数据、CRC。接收端先收固定长度的帧头解析出数据区长度再按长度收满剩余字节。这个过程叫“拆包”是所有 TCP 通讯开发绕不开的功课后面第 5 章会详细给出一个可用的帧格式。4.4 PLC 任务卡顿把阻塞收发放进了高速任务现象是 EtherCAT 任务周期偶尔超限运动控制出现抖动同时 TCP 通讯响应延迟变大有时上位机要用到几百毫秒才收到结果。原因是 Socket 功能块虽然本身是异步调用但它需要与 Windows 网络栈交互这个交互过程会被 Windows 的调度、网卡驱动的延迟影响。放在 1ms 的高速任务里一旦底层出现阻塞或重传就会把任务时间拉长。解决办法是把 TCP/IP 通讯代码从运动控制任务里挪出去放到 TwinCAT 的 Background 任务里或者单独建一个 WaitableTimerTask周期设为 10ms 甚至 50ms。通讯任务的优先级不要超过实时任务给运动控制留足处理余量。4.5 EtherCAT 和 TCP/IP 共用一张网卡导致丢包现象是现场只有一张物理网卡又跑 EtherCAT 又跑上位机 TCP设备运行一段时间后 EtherCAT 从站报丢帧上位机 ping 网关也有掉包。原因是一个物理网卡同时处理实时帧和普通以太网包网卡驱动和协议栈之间会产生竞争实时帧在 TCP/IP 流量大的时候被延迟或丢弃这是 EtherCAT 最怕的情况。解决办法是至少准备两块物理网卡EtherCAT 主站独占一张TCP/IP 通讯走另一张。如果机台只有一个网口宁可在外部加一个工业交换机把上位机和 EtherCAT 从站分开也不要让上位机流量直接跑到同一张 EtherCAT 网卡上。这个坑在项目选型和装配阶段就要规避软件层很难完全补救。5. 把 WinCAT3 通讯做成可交付接口帧格式、心跳与 C# 数据解析5.1 自定义应用层协议的完整定义上面避坑章节提到TCP 是字节流没有消息边界。所以真正能交付的 WinCAT3 通讯程序一定要有一个“应用层协议”。我常用的帧格式很简单却足够覆盖大多数工业场景。字段长度字节内容帧头2固定为 0xAA 0x55用于识别报文起始长度2数据区字节数不含帧头、长度和 CRC命令10x01 读参数、0x02 写参数、0x03 心跳等数据区N实际负载CRC162从长度字段到数据区的校验值低位在前这里的“长度”我一般用大端序存储因为和西门子 1200 PLC、Modbus TCP 的标准字节序一致。CRC16 可以选 Modbus CRC16也可以选 CCITT关键是上位机和 PLC 的计算规则必须一致。调试初期如果不想写 CRC可以先用一个字节的异或校验代替但正式项目一定要用 CRC否则数据被干扰后无法及时发现。5.2 PLC 侧打包函数把字节流拼进发送缓冲区在 TwinCAT 3 的 ST 语言里把数据拼成上面这种帧格式推荐独立写一个函数不把拼包逻辑散落在主程序里。下面这串代码展示核心思想传入命令字和数据区输出一个完整的发送缓冲区。FUNCTION F_BuildFrame : UINT VAR_INPUT cmd : BYTE; pData : POINTER TO BYTE; dataLen : UINT; pOutBuf : POINTER TO BYTE; END_VAR VAR idx : UINT; crcVal : WORD; END_VAR // 写帧头 pOutBuf[idx] : 16#AA; pOutBuf[idx 1] : 16#55; idx : idx 2; // 写长度大端序 pOutBuf[idx] : UINT_TO_BYTE(SHR(dataLen, 8)); pOutBuf[idx 1] : UINT_TO_BYTE(dataLen AND 16#FF); idx : idx 2; // 写命令 pOutBuf[idx] : cmd; idx : idx 1; // 写数据区 FOR i : 0 TO dataLen - 1 DO pOutBuf[idx] : pData[i]; idx : idx 1; END_FOR // 计算 CRC 并写入 crcVal : F_CalcCrc16(pOutBuf, idx); pOutBuf[idx] : BYTE(crcVal AND 16#FF); pOutBuf[idx 1] : BYTE(SHR(crcVal, 8)); F_BuildFrame : idx 2; // 返回总帧长度这个函数把“组帧”和“发送”分开了返回值是整帧字节数便于调用方直接传给 FB_SocketSend。如果用 POINTER TO BYTE要保证调用时传入的缓冲区足够大不然会越界。另一种更简单的做法是在 C# 上位机里把帧格式完全做好PLC 只负责填写数据区这样可以把协议维护工作集中在上位机PLC 侧只要预留一块字节数组的接口。5.3 和西门子 1200 PLC、变频器等第三方设备互通的实践如果 WinCAT3 是要作为 TCP 客户端去连西门子 1200 PLC最常见的做法不是自己发明私有的收发协议而是用西门子的 S7 协议或 Modbus TCP。做过 1200PLC 与欧姆龙变频器 485 通讯的人会知道串行链路本身很脆弱还要处理地址映射、波特率和 CRC把同样一台变频器换到 TCP/IP 网关后本质上还是 Modbus 寄存器读写只是传输介质从 RS485 变成了以太网。这种场景下WinCAT3 里的通讯代码要区分两层底层是 Socket 连接负责建立 TCP 链路上层是协议解析把对方的 Modbus 或 S7 报文解出来。很多工程师把精力放在底层连接上结果连上了却发现数据全是乱码因为应用层协议没有按对方规范实现。我的习惯是拿到对方设备手册时先翻清楚“报文结构”和“寄存器映射表”再写一行 PLC 代码。帧格式、心跳、通讯超时这些设计都是在协议确定之后自然长出来的。6. 用 WireShark 验证通讯顺手补一个断线重连调试 TCP/IP 通讯时不要靠猜抓包是最快的方式。在 PLC 或上位机上打开 Wireshark设置过滤表达式tcp.port 8500就能看到每个连接对应的包。如果只有 SYN 没有 SYN-ACK多半是防火墙把入站请求拦了如果连接建立后立刻出现 RST说明对端主动拒绝了连接要检查端口是否被其他程序占用或是 PLC 侧的 accept 没有就绪。看包时重点确认三次握手过程以及数据段里的 PSH 标志它能帮你判断一包数据是从哪里开始分割的。断线重连是 TCP 通讯在工业现场必备的保底功能。我一般会在 PLC 侧加一个“最近一次接收时间戳”变量在上位机侧每 1 秒发一次心跳帧PLC 超过 5 秒没收到心跳就主动关闭旧连接把状态机退回监听状态同时把故障标志置位。重连逻辑不要放在运动控制任务里用后台任务轮询。连不上的时候要做一个可复现的恢复流程不要只在 PLC 里反复创建套接字否则端口会被 TIME_WAIT 状态占满越重连越连不上。我现在做 TCP/IP 通讯项目会先把帧格式表画出来再把超时和重连时序画在纸上最后才写代码。带着这个习惯大多数“连不上、断线、乱码”的问题都能在合闸之前解决。希望帮到你。本文还有配套的精品资源点击获取