1. 为什么要在FX5UJ上折腾Socket通信
三菱FX5UJ这台PLC,玩过的人都知道,它自带以太网口,支持MC协议、SLMP、Socket通信这些功能。但很多人拿到手之后,翻遍手册也搞不清楚Socket通信到底怎么配置,尤其是做TCP客户端的时候,各种参数设置让人头大。我刚开始接触FX5UJ做Socket通信那会儿,也是踩了不少坑,GX Works3里那些设置项看着简单,实际连起来就是不通,排查半天发现是端口号搞错了或者连接目标IP写反了。
这篇内容就是把我自己在FX5UJ上实现Socket TCP客户端的完整过程整理出来,从基础概念到参数配置,再到实际编程和调试,全部走一遍。不管你是刚接触PLC编程的新手,还是从FX3U、Q系列转过来的老手,只要涉及到FX5UJ的以太网Socket通信,这里面的内容都能直接参考。特别是那些需要跟上位机、视觉系统、机器人控制器做TCP通信的场景,FX5UJ作为客户端主动连接对方服务器的需求非常常见。
先说清楚一个概念:Socket通信本质上就是TCP/IP协议栈上面的一层编程接口。你可以把它理解成打电话——客户端就是主动拨号的一方,服务端是接听的一方。FX5UJ做TCP客户端,就是让PLC主动去连接指定的IP地址和端口号,建立连接之后双方就可以互相发数据了。这个过程中,PLC不需要等别人来连它,而是它去找别人,这在工业现场很实用,比如PLC要把数据推给MES系统、要把结果发给视觉控制器,都是PLC做客户端更顺手。
FX5UJ的Socket通信支持TCP和UDP两种协议,TCP又分客户端和服务器两种角色。TCP客户端模式下,PLC主动发起连接请求,经过三次握手建立连接,然后就可以收发数据。这里有个关键点:FX5UJ的Socket通信是通过内置以太网口实现的,不需要额外加通信模块,但需要用到专用的Socket通信指令,比如SP.SOCOPEN、SP.SOCRCV、SP.SOCSND这些。这些指令跟普通的FROM/TO指令不一样,它们是专门为Socket通信设计的,用起来有固定的套路。
我见过很多人卡在第一步——不知道FX5UJ初始IP是多少。出厂默认是没有IP的,需要通过GX Works3或者SD卡来设置。一般来说,我们会把PLC的IP设成跟目标设备同一个网段,比如目标服务器是192.168.1.100,那PLC就设成192.168.1.10这种。子网掩码通常255.255.255.0,默认网关看情况,如果跨网段通信就需要设,同网段直连可以不设。这些参数在GX Works3的“模块参数”里面配置,具体路径是:导航窗口→参数→FX5UJCPU→模块参数→以太网端口→基本设置→IP地址设置。
还有一个容易混淆的地方:Socket通信的端口号。FX5UJ做客户端时,需要指定目标服务器的端口号,同时PLC自己也会用一个本地端口。本地端口可以固定,也可以设为0让系统自动分配。我一般建议固定本地端口,方便排查问题,比如设成5000、6000这种不常用的端口。目标端口就要看对方服务器开的是什么端口了,常见的有502(Modbus TCP)、9600、8080这些。这里要特别注意,本地端口和目标端口不能搞混,我见过有人把目标端口填成了PLC自己的端口,结果怎么都连不上。
2. FX5UJ Socket TCP客户端的核心配置拆解
2.1 以太网基本参数设置
打开GX Works3,在导航窗口找到“参数”→ “FX5UJCPU”→ “模块参数”→ “以太网端口”。这里有几个关键页面需要配置。
第一个是“基本设置”。IP地址设置里面,选择“直接设置”,然后填入PLC的IP地址、子网掩码、默认网关。比如:
- IP地址:192.168.1.10
- 子网掩码:255.255.255.0
- 默认网关:192.168.1.1(同网段通信可以不填)
通信速度可以选“自动检测”,或者强制100Mbps全双工。如果现场干扰大,建议强制100M全双工,避免自动协商出问题。
第二个是“打开设置”。这里就是配置Socket通信的关键地方。FX5UJ支持最多8个连接(同时打开8个Socket),每个连接可以独立配置协议、角色、端口号等。我们做TCP客户端,就选一个连接来配置:
- 协议:TCP
- 打开方式:Active(主动)——这就是客户端模式
- 本地端口号:比如6000
- 目标IP地址:比如192.168.1.100
- 目标端口号:比如9600
这里有个细节:目标IP地址可以填IP或者主机名,但工业现场一般直接用IP,避免DNS解析出问题。目标端口号一定要跟服务器端监听的端口一致,差一个数字都连不上。
2.2 Socket通信指令体系
FX5UJ的Socket通信指令主要有这几个:
| 指令 | 功能 | 使用场景 |
|---|---|---|
| SP.SOCOPEN | 打开Socket连接 | 建立TCP连接 |
| SP.SOCCLOSE | 关闭Socket连接 | 断开TCP连接 |
| SP.SOCRCV | 接收数据 | 从对方读取数据 |
| SP.SOCSND | 发送数据 | 向对方发送数据 |
| SP.SOCINF | 获取Socket信息 | 查询连接状态 |
这些指令都是带完成标志和错误标志的,编程的时候需要配合触点使用。比如SP.SOCOPEN指令,需要指定连接编号、执行方式(首次执行还是连续执行),然后等待完成标志ON。
SP.SOCOPEN的参数设置:
- 连接编号:1~8,对应打开设置里的连接号
- 执行方式:0=首次执行,1=连续执行
- 完成标志:指令执行完成后置ON
- 错误标志:执行出错时置ON
我一般用首次执行方式,用一个上升沿触发,然后等待完成标志。如果出错,错误标志ON,这时候需要查错误代码来定位问题。
2.3 数据收发缓冲区规划
Socket通信收发数据都需要缓冲区,FX5UJ用的是数据寄存器D或者文件寄存器R。发送的时候,把要发的数据放到连续的D寄存器里,然后指定起始地址和长度;接收的时候,指定接收缓冲区的起始地址和最大长度。
比如我规划:
- 发送缓冲区:D100~D199,共100个字(200字节)
- 接收缓冲区:D200~D299,共100个字(200字节)
发送长度和接收长度都是按字节算的,不是按字数。比如要发10个字节,长度就填10。接收的时候,SP.SOCRCV指令会返回实际接收到的字节数,放在指定的寄存器里。
这里有个坑:FX5UJ的Socket通信数据是以字节为单位传输的,但PLC内部是16位寄存器。所以发送的时候,如果数据长度是奇数,最后一个字节的高8位会被忽略或者补零。接收的时候也一样,如果收到奇数个字节,最后一个寄存器的高8位可能是无效数据。处理ASCII字符串的时候要特别注意这个问题。
3. 手把手搭建TCP客户端通信程序
3.1 连接建立与断开逻辑
先写连接建立的部分。用一个启动按钮X0触发,上升沿执行SP.SOCOPEN指令。
X0上升沿 ├─ SP.SOCOPEN 连接编号=1,执行方式=0 ├─ 完成标志M100 └─ 错误标志M101M100 ON表示连接建立成功,M101 ON表示连接失败。连接失败的时候,需要查错误代码。错误代码放在SP.SOCOPEN指令的最后一个参数里,比如D500。
连接成功之后,就可以进行数据收发了。断开连接用SP.SOCCLOSE指令,同样指定连接编号和执行方式。
X1上升沿 ├─ SP.SOCCLOSE 连接编号=1,执行方式=0 ├─ 完成标志M110 └─ 错误标志M111实际项目中,我一般会在PLC上电后自动尝试连接,连接失败后延时重试。重试间隔设个3~5秒,避免频繁重连把网络搞拥塞。
3.2 数据发送的完整流程
发送数据用SP.SOCSND指令。假设我要把D100开始的10个字节发出去:
M100(连接已建立)AND X2上升沿 ├─ SP.SOCSND 连接编号=1,发送起始地址=D100,发送长度=10 ├─ 完成标志M120 └─ 错误标志M121发送完成标志M120 ON表示数据已经成功发出去了。注意,这里只是表示数据从PLC发出去了,不表示对方已经收到。TCP协议本身有确认机制,但应用层不知道对方有没有处理。
发送的数据需要提前准备好。比如要发字符串“HELLO”,就把ASCII码放到D100、D101、D102:
- D100 = 0x4845('H'和'E')
- D101 = 0x4C4C('L'和'L')
- D102 = 0x4F00('O'和填充)
或者用$MOV指令直接写字符串,FX5UJ支持字符串常量。
3.3 数据接收与超时处理
接收数据用SP.SOCRCV指令。这个指令有点特殊,它是非等待的,执行后立即返回,如果有数据就接收,没有数据就等下次扫描。
M100(连接已建立)AND M200(接收使能) ├─ SP.SOCRCV 连接编号=1,接收起始地址=D200,最大长度=100 ├─ 完成标志M130 └─ 错误标志M131接收完成标志M130 ON表示收到了数据,实际接收长度放在指定的寄存器里,比如D300。然后根据D300的值来处理接收到的数据。
这里有个关键点:SP.SOCRCV指令需要在每次扫描都执行,或者用连续执行方式。如果只用上升沿触发一次,可能错过后续的数据。我一般用一个常ON触点来驱动,或者用定时器周期性触发。
接收超时怎么处理?FX5UJ本身没有内置接收超时功能,需要自己用定时器做。比如启动接收后,如果500ms内没有收到数据,就认为超时,做相应处理。这个逻辑用定时器T0来实现:
M100 AND NOT M130 ├─ T0 K50(5秒定时器) └─ T0 ON时,置位超时标志M1403.4 完整通信状态机设计
实际项目中,Socket通信不能只是简单的发和收,需要有一个状态机来管理整个通信过程。我一般设计这几个状态:
| 状态 | 说明 | 转移条件 |
|---|---|---|
| 0 | 初始状态 | 上电或复位 |
| 1 | 连接中 | 执行SOCOPEN |
| 2 | 已连接 | 连接成功,可收发 |
| 3 | 发送中 | 执行SOCSND |
| 4 | 接收中 | 执行SOCRCV |
| 5 | 错误处理 | 出错后重试或报警 |
状态机用D寄存器或者M寄存器来存当前状态,然后根据状态和事件来转移。这种写法比一堆触点和线圈清晰得多,后期维护也方便。
4. 调试过程中那些让人抓狂的问题
4.1 连接不上怎么办
连接不上是最常见的问题。排查顺序我一般是这样:
- 先ping一下目标IP,看网络通不通。如果ping不通,检查网线、交换机、IP设置。
- 用电脑上的网络调试工具(比如TCP测试工具)模拟服务器,看PLC能不能连上。如果能连上,说明PLC配置没问题,问题在目标服务器。
- 检查端口号。目标端口号是不是服务器实际监听的端口?本地端口号有没有被占用?
- 检查防火墙。目标服务器或者中间网络设备有没有拦截这个端口?
- 看错误代码。SP.SOCOPEN的错误代码会告诉你具体原因,比如超时、拒绝连接、网络不可达等。
常见错误代码:
| 错误代码 | 含义 | 处理方式 |
|---|---|---|
| 0x0000 | 正常 | - |
| 0x0001 | 连接超时 | 检查网络和目标设备 |
| 0x0002 | 连接被拒绝 | 目标端口未监听 |
| 0x0003 | 网络不可达 | 检查IP和路由 |
| 0x0004 | 连接已存在 | 先关闭再打开 |
4.2 数据收发异常排查
数据发出去了但对方没收到,或者收到的是乱码,这种问题也很常见。
先确认发送长度对不对。长度是按字节算的,不是字数。比如发“ABCD”4个字符,长度填4,不是2。
再确认字节序。FX5UJ的寄存器是低字节在前还是高字节在前?发送的时候,D100的低8位先发还是高8位先发?这个跟对方服务器的解析方式有关。如果对方是按大端序解析,而PLC发的是小端序,就会乱码。解决办法是在PLC端做字节交换,或者跟对方约定好字节序。
接收的时候也一样。如果收到的数据长度是奇数,最后一个字节可能有问题。我一般会在接收缓冲区后面多留几个寄存器,避免越界。
4.3 连接断开与重连策略
TCP连接可能因为各种原因断开:网络抖动、对方服务器重启、超时等。PLC需要能检测到断开并自动重连。
检测断开的方法:SP.SOCRCV指令如果返回错误,或者SP.SOCINF查询到连接状态不是已连接,就认为断开了。然后关闭连接,延时后重新执行SP.SOCOPEN。
重连策略我一般这样设计:
- 首次重连延时1秒
- 第二次重连延时3秒
- 第三次重连延时5秒
- 之后每次重连延时10秒
避免频繁重连把网络搞崩。同时记录重连次数,超过一定次数(比如10次)就报警,提示人工检查。
4.4 性能优化与注意事项
FX5UJ的Socket通信性能跟扫描周期有关。SP.SOCRCV和SP.SOCSND指令的执行需要时间,如果扫描周期太短,可能指令还没执行完就进入下一次扫描了。我一般会把Socket通信相关的程序放在扫描周期的后半段,或者用定时器中断来执行。
发送数据不要太频繁。TCP协议本身有开销,每次发送都有包头包尾。如果数据量小但发送频率高,网络效率很低。我一般会做数据缓冲,攒够一定长度或者间隔一定时间再发。
接收缓冲区要足够大。如果对方发得快,PLC接收不及时,数据可能会丢失。FX5UJ的Socket接收缓冲区大小有限,具体看手册。如果数据量大,建议用文件寄存器R来存接收数据。
还有一个容易忽略的点:PLC断电重启后,Socket连接会断开,需要重新建立。所以程序里要有上电自动连接的逻辑,不能只靠手动按钮触发。
5. 实际项目中的经验总结
5.1 与上位机通信的实战案例
我之前做过一个项目,FX5UJ作为TCP客户端,连接一台工控机上的MES系统。MES系统开了一个TCP服务器,端口9600,PLC每生产一个产品就发一条数据过去,包含产品编号、检测结果、时间戳。
数据格式是ASCII字符串,用逗号分隔,比如“SN12345,OK,20240101120000”。PLC这边用$MOV指令把字符串拼好放到D寄存器,然后SP.SOCSND发出去。MES收到后解析入库。
这个项目里遇到一个问题:MES服务器有时候会重启,PLC检测到连接断开后需要自动重连。我用了前面说的重连策略,效果不错。另外,MES服务器对数据格式要求很严格,多一个空格都会解析失败。所以PLC发送之前一定要确认数据格式完全正确。
5.2 与视觉系统通信的注意事项
另一个项目是FX5UJ连接视觉控制器。视觉控制器作为TCP服务器,PLC作为客户端。PLC触发拍照后,视觉控制器返回检测结果,比如“OK”或“NG”。
这个场景下,实时性要求比较高。PLC发触发信号后,要在100ms内收到结果。我用了SP.SOCRCV的连续执行方式,配合定时器做超时检测。如果超时没收到结果,就认为视觉系统故障,报警停机。
这里有个细节:视觉控制器返回的数据可能分多次到达。比如先发“O”,再发“K”。PLC需要能处理这种分包情况。我的做法是接收缓冲区不清零,每次收到数据就追加,直到收到结束符(比如换行符)才认为一帧数据完整。
5.3 多连接并发处理
FX5UJ支持最多8个Socket连接同时打开。如果需要同时跟多个设备通信,可以配置多个连接。比如连接1跟MES通信,连接2跟视觉控制器通信,连接3跟机器人控制器通信。
每个连接独立配置、独立编程。但要注意,所有连接共享同一个以太网口,带宽是有限的。如果数据量大,需要考虑网络负载。我一般会把实时性要求高的连接设成高优先级,数据量大的连接做流量控制。
多连接编程的时候,指令的连接编号一定要对应好。我见过有人把连接1的发送指令写成了连接2的编号,结果数据发到错误的设备上去了。这种错误很隐蔽,调试的时候要特别小心。
5.4 常见问题速查表
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 连接超时 | IP或端口错误 | ping测试,检查配置 |
| 连接被拒绝 | 目标端口未监听 | 用电脑测试目标端口 |
| 发送成功但对方没收到 | 字节序或格式问题 | 抓包分析,检查数据格式 |
| 接收乱码 | 字节序不匹配 | 做字节交换测试 |
| 频繁断连 | 网络不稳定或对方重启 | 检查网络质量,看重连日志 |
| 扫描周期变长 | Socket指令耗时 | 优化程序结构,用中断 |
| 数据丢失 | 接收缓冲区太小 | 增大缓冲区,降低发送频率 |
5.5 编程习惯与代码规范
最后说几个编程习惯上的建议。Socket通信程序一定要加注释,每个指令的作用、参数含义、完成标志和错误标志的地址都要写清楚。不然过几个月自己都看不懂。
错误处理要完善。每个Socket指令都要有错误标志判断,出错后要有相应的处理逻辑,不能不管。我一般会把错误代码存到连续的寄存器里,方便后期排查。
程序结构要清晰。连接管理、数据发送、数据接收、错误处理分开写,用不同的程序块或者FB。这样后期修改的时候不会牵一发而动全身。
还有,调试的时候善用GX Works3的监控功能。可以实时看Socket指令的执行状态、完成标志、错误代码,比猜要快得多。网络调试工具也是必备的,Wireshark抓包能看到底层的数据流,定位问题很有效。
我在实际项目中最大的体会是:Socket通信本身不复杂,复杂的是各种边界情况和异常处理。把异常处理做好了,程序就稳了。另外,跟对方设备的开发者保持沟通很重要,数据格式、通信协议、超时时间这些都要提前约定好,不然联调的时候会浪费很多时间。