ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

三菱FX5U以太网通信实战:SLMP协议读写与避坑指南

三菱FX5U以太网通信实战:SLMP协议读写与避坑指南 简介本资源面向工业自动化与上位机开发人员提供一套基于C#与WinForm实现三菱FX5U PLC以太网通信的完整工程源码重点解决3E帧报文构造、连接建立与数据读写等核心问题适用于自动化立体仓库等场景下的上位机开发学习与二次开发。压缩包共35个文件约79KB以cs源码文件为主辅以config配置、resx与resources资源、csproj与sln工程文件及exe可执行程序结构完整可直接在Visual Studio中打开运行。目前已有465人学习下载。读者可从中获取3E帧报文封装、RawSocket与TcpClient通信、错误重试机制以及WinForm界面交互等实现思路并参考其工程组织方式快速搭建与FX5U PLC通信的上位机框架理解工业现场通信协议与业务逻辑的结合方式。1. 三菱FX5U上位机以太网通信从一根网线到稳定读写车间里一台 FX5U 跑着产线逻辑你想在工控机上实时看 D 寄存器的值、改几个配方参数、把报警日志拉出来存库。最省事的做法不是加串口板、不是换 PLC而是直接用 FX5U 自带的以太网口让上位机通过 SLMPSeamLess Message Protocol三菱对 MC 协议的以太网封装跟它对话。这个标题讲的就是这件事上位机怎么用 C#、Qt、Python 或 LabVIEW 这类工具通过以太网读写 FX5U 的软元件。它解决的是数据采集、参数下发、设备联网监控这类需求适合做非标自动化、产线数据看板、MES 对接的工程师。下面按“协议怎么立住 → 环境怎么搭 → 代码怎么写 → 坑在哪 → 怎么验证”的顺序讲透。2. 先把 SLMP/MC 协议和 FX5U 的以太网口立住2.1 FX5U 以太网通信到底走的是什么协议FX5U 的以太网口支持多种通信方式上位机开发最常用的是 SLMP。它本质上是三菱 MC 协议在 TCP/UDP 上的承载报文分二进制和 ASCII 两种格式。二进制格式字节数少、解析快是上位机首选ASCII 格式可读性好调试阶段用来抓包看报文更直观。SLMP 的请求帧结构大致是帧头子头 网络号 PLC 号 请求目标模块 IO 号 请求目标模块站号 请求数据长度 监视定时器→ 指令如批量读 0x0401、批量写 0x1401→ 子指令 → 软元件代码 → 起始地址 → 点数。响应帧在帧头后跟结束代码和数据区。理解这个结构后面写代码时才知道每个字节在干什么而不是抄一段就完事。2.2 软元件代码和地址换算D、M、X、Y 怎么对应上位机读写 FX5U 时最容易翻车的地方是软元件代码和地址换算。D 寄存器是 0xA8M 是 0x90X 是 0x9CY 是 0x9DB 是 0xA0W 是 0xB4R 是 0xAF。地址方面D 和 M 这类软元件直接用十进制编号比如 D100 就是 100X 和 Y 是十六进制编号X10 对应 0x10写代码时要按十六进制解析。位软元件按位读写时点数单位是位字软元件按字读写时点数单位是字。批量读 D100 开始 10 个字请求里点数写 10响应里就是 20 字节数据。这个换算关系如果搞错读出来的值会整体偏移现象是“数据看着像但不对”排查起来很费时间。2.3 TCP 还是 UDP连接方式怎么选FX5U 的 SLMP 支持 TCP 和 UDP。TCP 有连接状态适合上位机长期在线、需要可靠传输的场景比如数据采集和配方下发UDP 无连接适合高频短报文、对实时性要求高但能容忍少量丢包的场景。实际项目里我一般用 TCP因为上位机通常要维持长连接断线重连逻辑也好写。FX5U 作为服务器时默认端口是 5000但可以在 GX Works3 的以太网端口设置里改。如果上位机做客户端PLC 做服务器上位机主动连 PLC 的 IP 和端口如果 PLC 做客户端主动上报上位机做服务器那就要在 PLC 侧配置主动打开设置。多数采集场景是上位机做客户端连 PLC 的 5000 端口。2.4 用 GX Works3 把 FX5U 的以太网参数配好动手写代码前先在 GX Works3 里把 PLC 侧参数配好。步骤是打开工程 → 导航窗口里找到“模块配置图”或“以太网端口设置” → 设置 IP 地址和子网掩码比如 192.168.1.10 / 255.255.255.0 → 在“对象设备连接配置设置”里添加 SLMP 连接指定端口号 5000、通信方式 TCP → 如果需要 PLC 主动打开勾选“主动打开”并设置目标 IP 和端口 → 写入 PLC 并复位。配置完成后用 ping 确认网络通再用调试工具发一帧读 D100 的请求能收到响应就说明 PLC 侧没问题。这一步不做后面代码写得再对也连不上。3. 用 C# 写一个能跑通的 FX5U 读写最小程序3.1 环境准备和工程结构我一般用 C# 写上位机因为工控机上 .NET 环境好部署WinForms 或 WPF 做界面都快。新建一个 .NET Framework 4.7.2 或 .NET 6 的控制台/窗体项目不需要额外 NuGet 包直接用 System.Net.Sockets 里的 TcpClient 就行。工程里建三个类SlmpClient 负责连接和收发SlmpFrame 负责组帧和解析Program 或主窗体负责调用。这样分层的好处是协议逻辑和界面逻辑分开后面换 Qt 或 Python 也能照着搬。先写一个控制台版本跑通再套界面比一上来就拖控件稳。3.2 组帧批量读 D 寄存器的请求怎么拼// 构造 SLMP 二进制批量读请求帧读 D 寄存器 // 参数startAddress 起始地址十进制points 点数字 public static byte[] BuildReadDRequest(ushort startAddress, ushort points) { var frame new Listbyte(); // 子头5000 表示请求二进制格式 frame.AddRange(new byte[] { 0x50, 0x00 }); // 网络号0x00 表示当前网络 frame.Add(0x00); // PLC 号0xFF 表示当前 PLC frame.Add(0xFF); // 请求目标模块 IO 号0x03FF 表示 CPU 模块 frame.AddRange(new byte[] { 0xFF, 0x03 }); // 请求目标模块站号0x00 表示当前站 frame.Add(0x00); // 请求数据长度从指令开始到数据结束的字节数 // 指令 2 子指令 2 软元件代码 1 起始地址 3 点数 2 10 frame.AddRange(new byte[] { 0x0A, 0x00 }); // 监视定时器0x0010 表示 4 秒 frame.AddRange(new byte[] { 0x10, 0x00 }); // 指令0x0401 批量读 frame.AddRange(new byte[] { 0x01, 0x04 }); // 子指令0x0000 按字读取 frame.AddRange(new byte[] { 0x00, 0x00 }); // 软元件代码D 寄存器 0xA8 frame.Add(0xA8); // 起始地址3 字节小端 frame.Add((byte)(startAddress 0xFF)); frame.Add((byte)((startAddress 8) 0xFF)); frame.Add(0x00); // 点数2 字节小端 frame.Add((byte)(points 0xFF)); frame.Add((byte)((points 8) 0xFF)); return frame.ToArray(); }这段代码的关键是请求数据长度的计算。它统计的是从指令开始到数据结束的字节数不是整帧长度。指令 2 字节、子指令 2 字节、软元件代码 1 字节、起始地址 3 字节、点数 2 字节合计 10 字节所以写 0x0A、0x00。起始地址和点数都是小端低字节在前。软元件代码 D 是 0xA8如果读 M 就改成 0x90读 X 改成 0x9C。监视定时器 0x0010 对应 4 秒如果网络延迟大可以适当加大但不要超过 PLC 侧设置的上限。3.3 解析响应结束代码和数据区怎么取// 解析 SLMP 批量读响应返回 D 寄存器的字数组 public static ushort[] ParseReadResponse(byte[] response, int points) { // 响应帧头长度子头 2 网络号 1 PLC 号 1 IO 号 2 站号 1 数据长度 2 结束代码 2 11 int headerLen 11; // 结束代码在偏移 9、10 两个字节小端 ushort endCode (ushort)(response[9] | (response[10] 8)); if (endCode ! 0) { throw new Exception($SLMP 错误结束代码0x{endCode:X4}); } // 数据区从偏移 11 开始每个字 2 字节小端 var values new ushort[points]; for (int i 0; i points; i) { int offset headerLen i * 2; values[i] (ushort)(response[offset] | (response[offset 1] 8)); } return values; }响应帧的结束代码是排查问题的第一手信息。0x0000 表示正常非零就是出错常见的有 0xC059指令错误、0xC05B软元件指定错误、0xC05C请求内容错误。数据区紧跟在结束代码后面按字小端排列。如果读的是位软元件数据区是按位打包的解析方式不同这里先讲字读取。实际项目里我会把结束代码做成枚举出错时直接提示“软元件地址超范围”而不是丢一个十六进制数给现场人员。3.4 连接、发送、接收的完整调用流程// 完整调用示例连接 FX5U读 D100 开始的 10 个字 using System.Net.Sockets; var client new TcpClient(); client.Connect(192.168.1.10, 5000); // PLC 的 IP 和端口 client.ReceiveTimeout 3000; client.SendTimeout 3000; var stream client.GetStream(); var request BuildReadDRequest(100, 10); stream.Write(request, 0, request.Length); // 先读帧头 11 字节拿到数据长度后再读数据区 var header new byte[11]; int read 0; while (read 11) { int n stream.Read(header, read, 11 - read); if (n 0) throw new Exception(连接被关闭); read n; } // 数据长度在偏移 7、8小端表示从结束代码开始到数据结束的字节数 int dataLen header[7] | (header[8] 8); var body new byte[dataLen]; read 0; while (read dataLen) { int n stream.Read(body, read, dataLen - read); if (n 0) throw new Exception(连接被关闭); read n; } // 拼成完整响应 var response new byte[11 dataLen]; Array.Copy(header, 0, response, 0, 11); Array.Copy(body, 0, response, 11, dataLen); var values ParseReadResponse(response, 10); Console.WriteLine(string.Join(,, values)); client.Close();这里有个细节TCP 是流式协议不能假设一次 Read 就能拿到完整帧。先读固定长度的帧头从帧头里解析出数据长度再按长度读剩余部分这是工控通信里最稳的收包方式。ReceiveTimeout 设 3 秒避免网络异常时线程卡死。发送前确认连接状态发送后如果超时先检查 IP 和端口再检查 PLC 侧 SLMP 连接是否配好。写寄存器用 0x1401 指令帧结构和读类似只是数据区要带上要写的值这里不展开照着读的帧改指令和数据区即可。4. 避坑与排查FX5U 以太网通信最常见的 5 个翻车点4.1 现象ping 得通但连不上 5000 端口原因通常是 PLC 侧没有配置 SLMP 连接或者端口号不是 5000。FX5U 的以太网口默认不一定开放 SLMP 服务需要在 GX Works3 的“对象设备连接配置设置”里手动添加。解决方法是打开 GX Works3确认以太网端口设置里已添加 SLMP 连接、通信方式选 TCP、端口号与上位机一致写入后复位 PLC。如果改过端口上位机也要同步改。另外注意防火墙工控机上的安全软件有时会拦 5000 端口。4.2 现象读回来的数据整体偏移或全是 0原因多半是软元件代码或地址换算错了。D 寄存器代码是 0xA8M 是 0x90X 是 0x9CY 是 0x9D。如果读 X 寄存器却按十进制解析地址X10 会被当成 10 而不是 16数据自然不对。解决方法是确认软元件类型和编号进制X/Y 按十六进制D/M 按十进制。另外检查起始地址是否超出 PLC 实际范围FX5U 的 D 寄存器范围有限超范围会返回错误码而不是 0。4.3 现象偶尔超时重连后又正常这是 TCP 长连接在工业现场最常见的问题。原因可能是网络抖动、交换机负载高、PLC 扫描周期忙导致响应慢。解决方法是把监视定时器适当加大ReceiveTimeout 设 3 到 5 秒并在上位机里做断线重连捕获 SocketException 后关闭旧连接延时 1 秒重新 Connect重连成功后重新初始化。不要每次读写都新建连接频繁握手会增加 PLC 负担。如果现场干扰大考虑用屏蔽网线并远离变频器。4.4 现象写 D 寄存器后读回来没变先确认写指令用的是 0x1401 而不是 0x0401子指令按字写是 0x0000。然后确认写入的数据区字节序SLMP 二进制格式是小端低字节在前。如果写的是位软元件数据区是按位打包不是按字。还有一个容易忽略的点PLC 程序里可能在同一扫描周期内把该寄存器重新赋值导致你写进去的值立刻被覆盖。解决方法是检查 PLC 程序里有没有对该地址的写操作必要时换一个未被程序占用的寄存器测试。4.5 现象多台上位机同时连有的连不上FX5U 的 SLMP 连接数有上限具体数量取决于型号和固件超了就会拒绝新连接。原因是每台上位机都占一个连接资源如果程序里没有正常关闭连接连接会一直挂着。解决方法是确保每次通信结束后 Close 连接或者用连接池控制并发数。如果确实需要多台上位机可以在 PLC 侧增加 SLMP 连接配置或者用一台中间网关做汇聚。调试阶段可以用 netstat 看工控机上的 5000 端口连接状态确认有没有僵尸连接。5. 进阶用批量读和心跳包把采集做稳5.1 批量读代替逐点读减少往返次数很多新手写采集是一个地址发一帧读 100 个 D 寄存器就发 100 帧效率低还容易超时。SLMP 支持一次读连续地址的多个点比如一次读 D100 到 D199 共 100 个字一帧就够。如果地址不连续可以按块分组把相邻地址合并成一次请求。我一般会把采集点按地址排序分成若干连续块每块一次批量读。这样往返次数从上百次降到几次PLC 的通信负载也小。注意单帧点数不要太大FX5U 对单次请求的点数有上限超了会返回错误具体上限查对应型号的通信手册一般几百个字以内是安全的。5.2 心跳包和断线重连的判断逻辑长连接需要心跳来确认链路还活着。我的做法是每隔 1 秒发一帧读固定寄存器的请求比如读 D0 一个点能正常收到响应就认为链路正常连续 3 次超时就判定断线触发重连。心跳不要发太频繁否则占用 PLC 通信资源也不要太稀疏否则断线发现不及时。重连逻辑里要加退避第一次延时 1 秒第二次 2 秒最多到 10 秒避免网络还没恢复就疯狂重连把 PLC 打挂。重连成功后要重新初始化所有采集状态不要假设之前的连接上下文还在。5.3 用 Wireshark 抓包验证 SLMP 报文代码跑不通时抓包是最快的定位手段。在工控机上装 Wireshark选对网卡过滤条件写tcp.port 5000然后运行上位机程序。看请求帧的子头是不是 0x5000数据长度字段和实际字节数是否一致结束代码是不是 0x0000。如果请求发出去了但 PLC 没响应看是不是 IP 或端口错了如果响应回来了但解析不对对照报文逐字节检查偏移。抓包能看到最原始的字节比在代码里打日志更直接。注意抓包时不要同时开多个通信程序否则报文混在一起不好分析。5.4 一个我常用的验证习惯每次新项目上手我不会直接写完整采集程序而是先写一个最小测试连上 PLC读一个已知值的 D 寄存器打印出来再写一个值进去读回来确认。这个最小闭环跑通后再往上加批量读、心跳、界面。这样出问题时范围小容易定位。另外我会把 PLC 的 IP、端口、软元件地址做成配置文件现场调试时不用改代码重新编译。这个习惯帮我省了很多来回跑现场的时间。希望帮到你。本文还有配套的精品资源点击获取
返回列表