资讯中心

三菱MC协议3E帧二进制实战:PLC以太网配置、组帧与排错

📅 2026/10/29 12:19:40
三菱MC协议3E帧二进制实战:PLC以太网配置、组帧与排错
简介《三菱MC协议配置和使用简析》是一份面向PLC通信初学者与工控调试人员的入门文档重点解决外部设备通过MC协议读写三菱PLC寄存器时配置思路不清、报文格式难理解等问题。资源包共1个docx文件大小约1.12MB围绕TCP/IP通信展开涵盖MC协议开放寄存器的目的、3E帧二进制收发格式、PLC内置以太网参数设置、向D7000写入H000C及连续读取5个软元件的示例并整理软元件代码对应关系、指令与子指令列表和错误返回帧排查方法。已有4092人学习适合希望快速掌握MC协议配置与报文解析、对照实操查漏补缺的读者。1. 网线通了不等于协议通了三菱 MC 协议最容易翻车的地方网线插好、IP 配完、ping 得通、端口也 telnet 得开不少人到这一步就认定三菱 MC 协议已经跑通了结果第一次发帧收回来的是一串看不懂的十六进制或者干脆一个字节都不回。这套协议本身不复杂难的是三件套必须同时对上PLC 侧内置以太网的打开设置要开 TCP 加 MC 协议并指定端口上位机发的帧必须是 3E 帧二进制且两字节字段低位在前软元件代码和起始地址要按手册的二进制表算。三样错一样PLC 要么回错误帧要么静默。这篇笔记按我实际配过的一套流程走先定环境和帧格式再配 PLC 参数然后手搓帧验证读写最后把踩过的坑和验证方法摊开写清楚。适合第一次对接 PLC 采集、手上只有一份手册和一根网线的人。2. 环境先定死3E 帧、二进制与 4999 端口的三件套动手之前先把「用什么帧、传什么格式、连哪个端口」这三件事写进纸面后面所有配置都围着它们转。工控现场最常见的时间浪费不是配置难而是配到一半才发现上位机按 ASCII 组帧、PLC 侧选的是二进制或者两边端口号只差一位。这一章把选型说透一次配对的概率能高很多。2.1 帧格式选型3E、3C、4C、4E 到底选哪个MC 协议的帧格式有 3E、3C、4C、4E 几种。3C 属于早期 A 兼容系列的格式头部字段宽度和 3E 不一样新一点的设备基本不再用它4C 与 3C 同源差别在支持的能力范围4E 可以理解为 3E 的扩展把请求数据长度这类字段加宽了一帧能带更多数据适合大批量读写。3E 是当下最通用的一档头部结构规整各家上位机库、示例代码基本都以它为主。我一般默认选 3E理由不是它先进而是排错资料最多。帧一旦发错能对着手册一条一条比对字段用 4C 这种冷门格式出了问题连搜都不好搜。只有在单次要读写的软元件点数特别大、3E 一帧装不下需要频繁分包时才值得切到 4E。具体差异以《MELSEC 通讯协议参考手册》的帧结构章节为准手册里每种格式的发送帧、响应帧都画了字段图比任何二手资料都准。选型时还有一个容易被忽略的点帧格式和「通信数据代码」是两件事。前者决定头部字段怎么排后者决定字段值以二进制还是 ASCII 字符呈现。两者组合起来才是完整的一套配 PLC 的时候要一起对。2.2 二进制还是 ASCII为什么推荐二进制通讯内容分二进制和 ASCII 文本两种形式传输的内容一致只是表现形式不同。ASCII 形式下帧里的每个字节都写成两个可读字符用抓包工具一看就懂调试时确实直观。代价是一帧长度明显更长而且上位机发之前要做一次编码、收到之后要做一次解码多两道转换就多两个出错点。二进制形式一帧的数据长度更短数据不需要转换直接按字节发、按字节收通讯效率更高。对周期采集的场景比如 100ms 采一轮、每轮读几十个寄存器这个差距会实打实地体现在轮询周期上。所以默认推荐二进制我自己所有现场也都是二进制。提示手册里软元件代码表、指令代码表通常都分 ASCII 和二进制两张。查表前先确认自己翻的是哪一张二进制表里 D 是 A8ASCII 表里是「D*」这种两字符形式拿错表算出来的地址一定是错的。2.3 网络与端口预置清单配置 PLC 之前先把下面这张表填满。表中每一项都要能写出来写不出来的先补别急着开软件。项目本次取值说明帧格式3E 帧通用度最高排错资料最多数据形式二进制帧短、无需转换、效率高传输层TCP/IP一发一收上位机主动发起PLC 侧 IP与工控机同网段例如 192.168.1.10避开网关冲突地址端口号4999打开设置里自选注意别撞已占用端口通信数据代码二进制必须与上位机一致允许 RUN 中写入勾选不勾只能读写入会回错误端口号在 1024 以下的低段位很多被系统服务占着选 4999 这种高位端口比较省事。真正的硬约束是PLC 打开设置里填的端口号、上位机 socket 连接的端口号、现场防火墙放行的端口号三个必须是同一个。这一条听起来废话但我见过不止一次因为中间一台交换机做了端口过滤排查了一下午。2.4 上位机调试环境Python 标准库就够调试环境不用装什么重型工具Python 3.x 加标准库的 socket 就能把收发链路跑通。这样做的好处是可控每一个字节都是自己拼的出错时能精确知道错在第几个字段而不是被某个封装库的黑匣子挡在外面。等链路跑通、帧结构吃透了再换成现成的通讯库提效也不迟。先做一步连通性确认只验证 TCP 能不能建起来# 确认工控机到 PLC 的 TCP 通路nc 返回成功即端口可达 # -v 输出详细过程-z 只扫不发送数据-w 2 超时 2 秒 nc -vz 192.168.1.10 4999这条命令只说明「网络层和传输层通了」不代表 MC 协议配对了。很多现场就卡在这个误判上nc 显示 succeeded就以为大功告成其实 PLC 的打开设置里可能压根没开 MC 协议只是端口被别的东西占着应答了。真正的验证必须发一帧真数据出去、收到一帧正确响应这一步放到第 4 章做。再准备一个最小的 socket 连接脚本后面所有帧都往这个骨架里塞import socket PLC_IP 192.168.1.10 # PLC 内置以太网口地址与打开设置里一致 PLC_PORT 4999 # 打开设置里配置的端口号 sock socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(3) # 一发一收模式必须设超时否则收不到会一直阻塞 sock.connect((PLC_IP, PLC_PORT)) print(connected) # 这里后续填入具体的 3E 二进制帧 sock.close()settimeout是这套脚本里最关键的一行。MC 协议是一发一收发出去之后如果 PLC 没回没有超时的 socket 会一直挂着程序看起来像假死现场排查时极容易被误判成 PLC 死机。3. PLC 侧配置内置以太网端口设置到烧写生效PLC 这边不需要写任何程序只在工程软件的参数里改几处设置然后把配置烧写进去。听起来简单但每一步都有个默认值在等着坑人。这一章按操作顺序走一遍顺带说明每个参数背后的含义。3.1 从工程栏进入 PLC 参数打开工程软件在右侧工程栏中双击「PLC 参数」弹出参数设置对话框。整个入口只有这一层找不到多半是因为当前打开的是智能功能模块的参数页不是 CPU 的参数页。进入后先确认一件事这台 PLC 的 CPU 型号带不带内置以太网口。带口的比如常见的 Q 系列带内置以太网的型号、L 系列、iQ-R 系列直接在内置以太网端口设置里配就行不带口的看 3.4 节挂模块的做法。这一步判断错了后面怎么配都不会通因为参数根本没有生效的物理通道。3.2 内置以太网端口设置IP、通信数据代码、允许 RUN 中写入在弹框里选中「内置以太网端口设置」这里要改三项第一项是 IP 地址。填一个和工控机同网段、且没被占用的地址。别用网段里的 .1 或 .254那是网关和交换机的常驻位置撞上了会出现「能 ping 通但不是 PLC 在应答」的玄学现象。第二项是通信数据代码设置。这里选二进制和上位机保持一致。这一项一旦选错帧发过去 PLC 能收到、也会回但回的是错误帧很容易被误判成地址算错。第三项是勾选「允许 RUN 中写入」。不勾这一项PLC 在运行状态下所有写操作都会被拒读操作则正常。表现就是「能读不能写」很多人第一反应是帧格式错了其实是权限没开。3.3 打开设置TCP 协议、MC 协议、端口号点「打开设置」在弹框第一栏里配置连接方式。这里有两个必选项协议选 TCP通信方式选 MC 协议。选完再填端口号按自己需求来比如 4999。这三个值里协议和通信方式选错是最致命的。选了 UDP上位机用 TCP 连握手阶段就直接失败协议选了 TCP 但通信方式没选 MC 协议选成其他开放方式端口能连上发 MC 帧过去对方不认回的东西完全对不上。这种错最难查因为 TCP 层一切正常只有应用层在默默失败。填完点「设置结束」回到上一级再点一次「设置结束」。两层弹框都要确认只关掉一层的话参数并没有真正落到工程里下一次编译烧写还是旧配置。3.4 没有内置网卡时网络参数里挂模块如果 PLC 本身没有自带网卡模块参数项里就不会有内置以太网端口设置需要到「网络参数」里选择对应的以太网模块按模块占用的槽位和起始 I/O 号添加然后在该模块的设置里做同样的配置IP、通信数据代码、打开设置里的 TCP 加 MC 协议加端口号。流程和内置口基本一致唯一要额外留心的是模块的 I/O 号。多模块系统里挂错槽位的情况不少见配置写进去了但写到了另一个空槽上现象是参数看着都对、就是不通。添加模块后核对一下起始 I/O 号和机架上实际插的位置对得上再往下走。3.5 烧写与生效确认配置改完必须烧写到 PLC 才生效。烧写的内容是参数区不是程序区操作时注意别把整个工程连程序一起覆盖掉——现场设备在跑的时候这么干后果不用多说。烧写完成后做两步确认一是重新打开 PLC 参数看刚才填的 IP 和端口还在不在有些情况烧写失败但软件不报错重开一次就能发现二是按 2.4 节的 nc 命令扫一次端口能通说明以太网口已经按新配置工作了。到这里 PLC 侧就算配完了接下来的问题全部在上位机的帧上。4. 组帧3E 帧二进制从副头部到软元件代码协议对接的核心就一件事把要读写的软元件、地址、点数按 3E 帧二进制的规则拼成一串字节。这一章拿两个真实用得到的例子走完向 D7000 写 H000C从 D7000 起连续读 5 个软元件。拼完再逐字段拆开讲。4.1 发送帧骨架头部由 TCP/IP 自动添加先说清楚边界。发送帧的最前面一部分是 TCP/IP 协议栈自己加的写代码时不用管我们要拼的是 TCP 载荷里的应用层部分。3E 帧二进制下这部分从副头部开始依次是网络号、PLC 号、请求目标模块 I/O 编号、请求目标模块站号、请求数据长度、监视定时器然后才是指令、子指令和软元件信息。这里有一条贯穿全帧的硬规则所有占两个字节的字段低位字节在前、高位字节在后。软元件的起始地址占三个字节同样是低位在前。这条规则记不住后面每一帧都会算错而且错得很隐蔽——地址 7000 的十六进制是 1B58写成「1B 58 00」和「58 1B 00」在抓包工具里看起来都挺像那么回事。监视定时器我一般填 0010也就是 16单位是 250ms折算下来 4 秒。这个值决定 PLC 等上位机后续数据的时间太长会拖慢异常发现太短在网络抖动时会误超时4 秒在产线环境里够用。4.2 写 D7000 H000C一帧 24 字节指令是向软元件 D7000 写入值 H000C发送帧内容50 00 00 FF FF 03 00 0E 00 10 00 01 14 00 00 58 1B 00 A8 01 00 0C 00逐字段拆开看字节位置内容含义1–250 00副头部请求帧固定 5000300网络号4FFPLC 号FF 表示不指定5–6FF 03请求目标模块 I/O 编号即 03FF700请求目标模块站号8–90E 00请求数据长度0x000E 即 14 字节10–1110 00监视定时器16 × 250ms 4 秒12–1301 14指令 0x1401批量写14–1500 00子指令 0000按字16 位为单位16–1858 1B 00起始软元件地址0x1B58 7000三字节低位在前19A8软元件代码D 寄存器二进制代码 A820–2101 00软元件点数1 个22–230C 00写入数据 H000C16 位低位在前请求数据长度 0x000E 是从监视定时器开始数到帧尾的字节数监视定时器 2 字节 指令 2 字节 子指令 2 字节 起始地址 3 字节 软元件代码 1 字节 点数 2 字节 数据 2 字节正好 14。这个长度字段是自己算的不是固定的改点数、改数据长度都要重算算错了 PLC 会直接回错误帧。用 2.4 节的 socket 骨架把这帧发出去# 写 D7000 H000C3E 帧二进制 frame bytes.fromhex( 500000FFFF03000E00100001 140000581B00A801000C00.replace( , ) ) sock.sendall(frame) # 一发一收发完立刻等回 resp sock.recv(1024) # 一帧回一帧1024 字节足够容纳 print(resp.hex( ).upper()) # 打成大写十六进制方便和手册对照sendall而不是send是因为大帧可能被拆成多次发送send不保证一次发完。recv(1024)一次读到的通常是完整一帧但这只是经验值生产代码里应该先读头部两字节、再按长度字段读剩余部分避免粘包。4.3 响应帧D0 00 00 FF FF 03 00 02 00 00 00 怎么读写入成功后的响应帧D0 00 00 FF FF 03 00 02 00 00 00和请求帧结构对称副头部变成 D000 表示响应网络号、PLC 号、I/O 编号、站号原样返回然后是响应数据长度和结束代码。这里的 02 00 表示响应数据长度 2 字节紧接着的 00 00 就是结束代码0000 代表正常结束。也就是说判断一次写入是否成功只看最后两个字节是不是 00 00 就够了前面的字段基本是回显。这条判据要记牢因为它是后面所有排错的分水岭。很多人看到「有响应」就认为通了实际上响应帧长度字段不是 2、结束代码不是 0000 的全都是失败。失败时结束代码的具体含义要去对应型号的 PLC 手册里查比如 L 系列手册里对每个错误码都有明确的描述对着查比猜快得多。4.4 批量读 5 个软元件点数与响应数据长度读取 D7000 开始的连续 5 个软元件发送帧50 00 00 FF FF 03 00 0C 00 10 00 01 04 00 00 58 1B 00 A8 05 00和写入帧比变化有三处指令从 1401 变成 0401批量读请求数据长度从 0E 变成 0C14 变 12因为读请求不带写入数据点数从 01 变成 05。起始地址和软元件代码完全不变这也是实际开发中最省事的写法——读和写共用同一套地址计算逻辑。响应帧D0 00 00 FF FF 03 00 0C 00 00 00 0C 00 00 00 00 00 00 00 00 00响应数据长度 0C 00 即 12 字节其中结束代码占 2 字节剩下 10 字节是 5 个软元件的值每个 2 字节。按低位在前还原0C 00 是 000C也就是上一次写进去的那个数后面四个都是 0000。这条响应同时验证了写入和读取两条链路是我现场调试时必跑的一次闭环。解析时注意5 个软元件的值是紧挨着排的没有分隔符。按 2 字节一组切分再用int.from_bytes(chunk, little)转成数值切错一位后面全错。32 位指令比如 DWORD 双字占 4 字节低字在前高字在后和 16 位是同一个方向。4.5 软元件代码与指令子指令对照软元件代码是组帧里最容易查错表的一项。基本型 QCPU 里软元件名称和代码在 ASCII 与二进制两种形式下是两套完全不同的值手册里通常给两张表。下面这几个是二进制表里高频用到的软元件二进制代码说明DA8数据寄存器最常用M90内部继电器X9C输入继电器Y9D输出继电器BA0链接继电器WB4链接寄存器其他型号L 系列、iQ-R 系列等的代码表不一定完全一致换型号时按手册的二进制表重新核一遍别照抄。指令方面批量读是 0401、批量写是 1401子指令 0000 表示按字16 位处理、0001 表示按位处理。读写位软元件M、X、Y时必须把子指令改成 0001否则 PLC 会按字去解析地址返回的要么是错误帧要么是错位的数据。随机读、多块批量读这些指令等真的需要优化轮询次数时再查手册引入一开始用批量读写足够。5. 避坑与排查错误帧、字节序和那根看着没问题的网线现场大部分「配不通」最后都能归到几类固定原因上。这一章按我遇到的顺序列每条按现象、原因、解决写方便对照。5.1 先看响应帧再看别的排错顺序错了会比错误本身更耗时间。我习惯的第一步永远是把发出的帧和收到的帧都打成十六进制打印出来对照手册逐字段比对。先看副头部是不是 D000再看响应数据长度是不是预期值最后看结束代码是不是 0000。这三步能在两分钟内把问题范围缩到「帧格式」「地址」「权限」三者之一。反过来上来就怀疑网线、交换机、防火墙往往查了两小时发现帧里地址少写了一字节。链路层的问题特征很明确连不上、连上就断、超时无响应。只要收到了任何一帧数据说明链路是通的问题一定在帧内容或 PLC 参数上。5.2 五条踩坑记录现象一nc 扫描端口成功但发帧后完全无响应超时断开。原因端口被 PLC 上别的服务占着或者打开设置里通信方式没选 MC 协议PLC 收到帧后不认识直接丢弃不回。 解决回 PLC 参数里逐层核对打开设置的两层弹框是否都点了「设置结束」协议是否 TCP、通信方式是否 MC 协议改完重新烧写。别在 nc 通就以为万事大吉。现象二能正常读数据写操作一律返回非 0000 的结束代码。原因「允许 RUN 中写入」没勾选PLC 在运行状态下拒绝所有写请求或者子指令写成了 0000 但目标是位软元件。 解决勾上 RUN 中写入并重新烧写写 M、X、Y 这类位软元件时把子指令改成 0001并按位地址换算不要直接用字地址。现象三写入的值和读回来的值对不上比如写 H000C 读回来 H0C00。原因字节序搞反了。两字节字段低位在前写成高位在前在十六进制上看不出问题但数值全错。 解决把起始地址、点数、写入数据这三处的字节顺序统一改成低位在前。地址 7000 必须是 58 1B 00不是 00 1B 58。这个错属于血泪经验级别的常见写完帧先自己按字节倒推一遍再发。现象四小批量读写正常一次读几十个点就报错或数据截断。原因请求数据长度字段没跟着点数重算PLC 按长度字段解析长度对不上就判定帧格式错误或者一帧数据量超过了 3E 帧的承载范围。 解决每次改点数都重新算一遍请求数据长度公式是固定字段字节数加数据字节数点数确实很大时拆成多次批量读或者评估切到 4E 帧。现象五换了一台同型号 PLC参数照抄过去却读不到数。原因软元件代码或起始地址沿用了上一台的算法但型号或系列不同二进制代码表不一样。 解决换设备时把软元件代码表重新查一遍别复制粘贴。我一般的做法是把型号、软元件代码、地址算法写在同一段注释里换设备时强制自己看一遍。6. 装完怎么验证三条命令跑通读写闭环配置和组帧都完成后别急着往上叠业务代码先用一段最小脚本把「写—读—比对」这个闭环跑通。跑通了后面所有采集逻辑都是在这上面加软元件和轮询跑不通加再多代码也是把错误放大。验证逻辑很直接先向 D7000 写一个已知值再读回来比对数值是否一致。选 D7000 是因为它是数据寄存器不参与设备实际动作写坏了也不影响产线如果你手上的 PLC 这个地址已被占用换一个空闲的 D 区地址改帧里的地址三个字节即可。import socket PLC_IP, PLC_PORT 192.168.1.10, 4999 def send_frame(frame_hex): 一发一收发一帧、收一帧返回十六进制字符串 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.settimeout(3) # 无响应时快速失败不阻塞 s.connect((PLC_IP, PLC_PORT)) s.sendall(bytes.fromhex(frame_hex)) return s.recv(1024).hex( ).upper() # 第一步写入 D7000 H000C w send_frame(500000FFFF03000E00100001 140000581B00A801000C00.replace( , )) print(write:, w) assert w.endswith(00 00), 写入失败结束代码非 0000 # 第二步从 D7000 起读 5 个软元件 r send_frame(500000FFFF03000C00100001 040000581B00A80500.replace( , )) print(read :, r) # 第三步按 2 字节一组、低位在前解析比对第一个值 data bytes.fromhex(r.replace( , ))[11:] # 跳过 11 字节头部与结束代码 vals [int.from_bytes(data[i:i2], little) for i in range(0, 10, 2)] assert vals[0] 0x000C, f回读不一致{vals[0]:#06x} print(closed loop ok:, [hex(v) for v in vals])assert这两行是这段脚本的价值所在。很多人验证时只 print 不判断看到有响应就收工结果后面业务代码里跑了两周才发现读出来的一直是零。把判断写死在验证脚本里跑一次就知道通不通。几个验证项的期望现象和不通过时的排查方向验证项期望现象不通过先看哪里TCP 连接秒连不超时IP、端口、打开设置里的协议写入响应响应帧以 00 00 结尾RUN 中写入、指令 1401、子指令读取响应响应数据长度 0C 00共 12 字节帧长度字段、点数换算回读数值与写入值一致低位在前字节序、软元件代码表连续轮询多次收发稳定无粘包错位recv 读取策略、监视定时器有一条容易被忽略验证不要只跑一次。连发二十次看有没有偶发超时或数据错位。产线上真正让人头疼的从来不是一次性配不通而是跑几小时才出一次的数据漂移而这类问题的种子往往在第一次验证时就埋下了。从那以后我每次对接新设备都强制先跑这套「写一个已知值、读回来比对、连发二十次」的闭环通过了才动业务代码。省下来的不是调试时间是后面被叫到现场排查「数据偶尔不对」的时间。希望帮到你。本文还有配套的精品资源点击获取

看完文章,想为自己的企业也做一次专业网站诊断?

尧图顾问免费为您评估现有网站,并给出建站/改版建议与报价方案。

免费获取方案