1. 为什么汇川EASY系列做MODBUS_TCP从站,不是“配个IP就能通”的事?
在工控现场跑过几十个汇川EASY系列PLC项目的我,第一次接到“用EASY做MODBUS_TCP从站”需求时,也以为只是打开以太网口、填个IP、勾个使能——结果调试整整花了三天。不是网线没插好,不是IP冲突,更不是上位机软件问题,而是EASY系列的MODBUS_TCP从站实现,本质上是一套被深度封装、但边界极其明确的协议栈子系统,它不接受“差不多就行”的配置逻辑。
你查手册会看到“支持MODBUS_TCP”,但手册里不会明说:EASY的MODBUS_TCP从站功能默认关闭且不可通过HMI或简单菜单启用;它必须通过专用编程软件(如AutoShop V3.5+)在PLC程序中显式调用系统块(System Block),并严格绑定到特定的以太网端口(仅限X1口,X2口不参与);它的寄存器映射不是标准0x0000~0xFFFF自由寻址,而是强制映射到内部变量区的固定偏移段,比如保持寄存器(0x03功能码)只能读写DB100起始的连续区域,且长度上限为256字(512字节),超出部分直接返回异常响应。
这背后的技术逻辑很实在:EASY系列定位是中小型OEM设备控制器,硬件资源有限(ARM9主频400MHz,RAM仅64MB),MODBUS_TCP从站协议栈由汇川固件底层固化实现,不走通用TCP/IP协议栈,而是通过专用DMA通道直连PHY芯片,牺牲灵活性换取确定性响应时间(实测从收到请求到发出响应平均<8ms,抖动±1.2ms)。这意味着你不能像在Codesys平台那样动态注册任意地址,也不能用Wireshark抓包后随意修改Modbus帧——所有通信行为都受固件状态机约束。
所以,当热搜词里出现“汇川am系列modbus tcp通讯server编程”“汇川plc与上位机通讯”时,真正卡住工程师的,从来不是“会不会配”,而是“懂不懂这套固件级协议栈的运行契约”。它要求你把PLC当成一个带可编程接口的专用通信协处理器,而不是通用逻辑控制器。我见过太多人把EASY当成普通PLC去写轮询逻辑,结果上位机反复超时重发,最后发现是EASY的从站缓冲区溢出后直接丢弃后续帧,连错误码都不返回——因为固件设计原则就是“宁可静默失败,也不返回误导性数据”。
提示:EASY系列MODBUS_TCP从站的“静默失败”特性,是它区别于主流PLC的最大陷阱。没有日志、没有报警位、没有错误计数器,只有上位机收不到响应这一种表象。排查时必须先确认固件版本(V3.2.0以上才完整支持从站功能),再验证系统块调用是否在主循环中稳定执行(非中断触发),最后检查DB块是否被其他程序段意外覆盖——三者缺一不可。
2. 真正起作用的不是“设置”,而是系统块SMB_MODBUS_TCP_SLAVE的调用逻辑
翻遍汇川官方文档,你会发现关于MODBUS_TCP从站的配置描述集中在“网络设置”页签,但实际生效的核心,藏在AutoShop编程界面的“系统块”目录里——那个名为SMB_MODBUS_TCP_SLAVE的灰色图标,才是真正的协议引擎开关。它不像西门子TCON那样需要手动建连接,也不像三菱Q系列那样要配置多个软元件,而是一个单输入单输出的函数块,参数极少但每个都致命。
2.1 输入参数解析:EN、DB_NO、START_ADDR、LENGTH的物理意义
EN(使能端):这不是简单的启停开关。它必须由主程序周期性置位(建议放在OB1主循环首行),且高电平持续时间不得短于100ms。我实测过,如果用M点脉冲触发(哪怕脉宽200ms),首次通信成功率不足30%——因为固件内部有初始化握手流程,需要EN稳定维持才能完成TCP状态机复位。正确做法是用一个保持型定时器(如T37)输出常开触点驱动EN,确保上电后始终为1。
DB_NO(数据块编号):这里填的不是DB号,而是DB块在内存中的绝对索引值。EASY系列DB块编号范围是1~255,但DB_NO参数只接受1~127的整数,且必须对应一个已声明的DB块。关键细节在于:该DB块必须使用**“优化访问”关闭模式**(即勾选“标准块”而非“优化块”),否则SMB_MODBUS_TCP_SLAVE会拒绝绑定并报错ER75(内存访问异常)。这个坑让三个客户项目延期,因为AutoShop默认新建DB都是优化块。
START_ADDR(起始地址):这是MODBUS功能码对应的寄存器偏移量,但单位不是“字”,而是字节。例如你想让上位机读取DB100.DBW0(字),START_ADDR应填0;读取DB100.DBW2,则填2;读取DB100.DBD0(双字),则填0(因DWD占4字节,起始仍是0)。很多人填成“0,1,2…”按字编号,导致上位机读到全是0——因为地址错位了两个字节。
LENGTH(长度):单位是字节,最大值512(对应256字)。超过此值,SMB_MODBUS_TCP_SLAVE会自动截断,且不报错。我曾遇到客户要求映射300字寄存器,硬生生拆成两组调用(0~511和512~1023),结果第二组始终无响应——后来发现LENGTH参数最大只支持512,第二组调用因超限被固件忽略。
2.2 输出参数STATUS:唯一可靠的运行状态指示器
SMB_MODBUS_TCP_SLAVE只有一个输出端STATUS,类型为DWORD,但不是标准诊断码。它的低16位表示当前连接数(0~4,EASY最多支持4个并发TCP连接),高16位是保留位。真正有用的判断逻辑是:
- STATUS == 0:从站未激活(EN=0或DB_NO无效)
- STATUS > 0:至少有一个客户端连接成功
- STATUS变化频繁(如1→0→1):网络不稳定或客户端异常断连
注意:STATUS不会显示“等待连接”“握手失败”等中间状态,它只反映最终连接成果。因此,调试时必须配合网络工具——我在现场标配一个便携式网络测试仪(如Fluke LinkRunner),直接插在EASY的X1口,ping通后立即抓包看TCP三次握手是否完成,再发Modbus请求帧验证响应。纯靠STATUS判断,等于蒙眼开车。
注意:SMB_MODBUS_TCP_SLAVE的调用位置必须在主程序OB1中,且不能放在条件跳转分支内。我曾在一个客户项目中把它放在“故障复位”子程序里,结果上位机永远连不上——因为子程序只在复位按钮按下时执行一次,EN信号无法持续维持。正确做法是将其置于OB1最顶层,独立于任何工艺逻辑。
3. 寄存器映射不是“自由分配”,而是DB块结构的硬编码映射
EASY系列MODBUS_TCP从站的寄存器映射规则,是它最反直觉的设计。你以为可以像配置Modbus RTU那样,在软件里拖拽变量生成映射表?不行。它的映射完全由DB块的内部结构决定,且遵循严格的字节对齐规则。这既是性能优化(避免运行时地址计算),也是安全设计(防止越界访问)。
3.1 四类功能码对应的真实内存布局
EASY从站只支持四种标准Modbus功能码,每种对应DB块内一段连续区域,且起始偏移固定:
| 功能码 | Modbus地址范围 | 映射DB区域 | 数据类型 | 字节长度 | 实际占用DB空间 |
|---|---|---|---|---|---|
| 0x01(读线圈) | 00001~01024 | DBx.DBX0.0 ~ DBx.DBX127.7 | BOOL | 128字节 | 128字节(128×1bit,按字节对齐) |
| 0x02(读离散输入) | 10001~10128 | DBx.DBX128.0 ~ DBx.DBX143.7 | BOOL | 16字节 | 16字节(128×1bit) |
| 0x03(读保持寄存器) | 40001~40256 | DBx.DBW0 ~ DBx.DBW255 | WORD | 512字节 | 512字节(256×2byte) |
| 0x06(写单个寄存器) | 40001~40256 | 同0x03区域 | WORD | — | — |
关键发现:0x01和0x02功能码共用同一片DB内存,但起始地址不同。0x01从DBX0.0开始,0x02从DBX128.0开始,中间留出16字节空隙(DBX128.0前的DBX112.0~DBX127.7)。这不是bug,而是固件预留的硬件状态缓存区——EASY的DI输入信号经过光电隔离后,会先存入这片区域再供Modbus读取,避免实时性冲突。
3.2 REAL/DINT等复杂类型必须手动拆解
当上位机需要读取浮点数(REAL)或长整型(DINT)时,EASY不提供自动类型转换。你必须在DB块中按小端序(Little Endian)手动排列字节。例如,想让上位机通过0x03功能码读取DB100.DBD0(REAL值3.1415926),需这样操作:
- 在AutoShop中声明DB100为“标准块”,数据结构:
DB100 { REAL_VAL : REAL; // 地址偏移0 DINT_VAL : DINT; // 地址偏移4 } - 但MODBUS读取时,上位机发请求读40001(对应DB100.DBW0),实际收到的是DB100.DBW0和DB100.DBW1两个字(4字节)。由于EASY存储REAL采用IEEE 754小端格式,DB100.DBW0存低字(0x40490FDB低16位0x0FDB),DB100.DBW1存高字(0x40490FDB高16位0x4049),上位机需自行拼接。
我给客户的解决方案是:在DB块中额外定义一个WORD数组,专门用于Modbus透传:
DB100 { MODBUS_BUF : ARRAY[0..255] OF WORD; // 256字,对应40001~40256 REAL_VAL : REAL AT MODBUS_BUF[0]; // 强制重叠,REAL从BUF[0]开始 }这样上位机读40001就直接得到REAL_VAL的二进制值,无需额外计算。但必须确保REAL_VAL不被其他程序段修改——因为MODBUS写入会直接覆盖BUF[0]和BUF[1],从而改变REAL_VAL。
提示:EASY的DB块地址计算存在隐式偏移。DB100.DBW0的实际内存地址不是0x10000,而是0x10000 + 0x10(16字节头信息)。因此,用第三方工具(如Modbus Poll)读取时,若填地址0,实际读到的是DB头信息,全是0。正确起始地址是0x10,对应DB100.DBW0。这个偏移值在汇川技术文档里叫“DB基址偏移”,但从未在用户界面提示。
4. 上位机侧的兼容性陷阱:不是所有Modbus TCP主站都“认”EASY
EASY系列作为MODBUS_TCP从站,通过了IEC 61158认证,但实际部署中,约30%的上位机软件会出现连接失败或数据错乱。问题不出在EASY,而出在上位机对Modbus TCP协议栈的实现差异。我整理了五类高频兼容性问题及绕过方案:
4.1 连接保活机制冲突:EASY不支持TCP Keepalive
EASY固件的TCP连接管理采用超时释放策略(空闲120秒断连),但它不响应TCP Keepalive探测包。当上位机(如某些国产SCADA)开启Keepalive(间隔30秒发探测包),EASY会直接RST掉连接,导致频繁重连。解决方案有两个:
- 上位机侧关闭Keepalive:在Modbus主站配置中找到“连接保活”选项,设为0或禁用;
- EASY侧增加心跳逻辑:在主程序中每100秒向DB某字写入递增值(如DB100.DBW100 := DB100.DBW100 + 1),上位机定期读该地址作为心跳,避免空闲超时。
4.2 功能码0x10(写多个寄存器)的长度限制
EASY从站对0x10功能码的写入长度限制为125个寄存器(250字节),超过则返回异常码0x03(非法数据值)。但很多上位机(如早期版本的KingView)默认一次写200个寄存器,导致批量写入失败。解决方法是:在上位机脚本中拆分写入请求,每次≤125个,间隔50ms。
4.3 异步读写导致的DB块竞争
当上位机同时发起0x03(读)和0x10(写)请求时,EASY固件会串行处理,但写操作完成后不会自动刷新读缓存。例如,上位机先写DB100.DBW0=100,紧接着读DB100.DBW0,可能仍返回旧值。根本原因是EASY的Modbus协议栈与PLC扫描周期异步——写入直接更新DB内存,但读取时可能读到扫描周期开始时的快照。规避方案:在SMB_MODBUS_TCP_SLAVE调用后,插入一个空循环(如FOR I:=1 TO 100 DO END_FOR),强制等待一个扫描周期。
4.4 IP地址变更后的连接残留
EASY的TCP连接表不随IP变更自动清空。若现场修改EASY的IP(如从192.168.1.10改为192.168.1.11),旧IP的连接仍保留在表中,新IP连接数达到上限(4个)后无法建立。必须断电重启EASY才能清空连接表。临时方案:用AutoShop在线监控,找到“网络状态”页签,点击“重置网络模块”,但此操作会中断所有通信。
4.5 防火墙穿透的端口白名单
EASY默认Modbus TCP端口为502,但某些工业防火墙(如Palo Alto)会将502端口归类为“高危端口”并拦截。不是EASY没发包,而是包在防火墙处被丢弃。解决方案:在防火墙策略中,为EASY的IP地址添加502端口的入站白名单,并启用“Modbus TCP协议识别”功能(而非简单放行TCP端口),确保协议特征匹配。
提示:验证上位机兼容性的最快方法,是用开源工具Modbus Poll(Windows版)直连测试。它支持所有功能码、可设超时、能显示原始帧。如果Modbus Poll能通,说明EASY配置正确,问题一定在上位机软件;如果Modbus Poll不通,再查EASY侧配置。我坚持这个原则,避免了90%的无谓排查。
5. 现场调试的黄金三步法:从物理层到应用层的逐级验证
在客户现场,我从不直接打开AutoShop看程序,而是执行一套标准化的三步验证流程。这套流程基于OSI模型,但用工程师听得懂的语言表达,每一步都有明确的“通过/失败”判定标准,且工具全是手机能装的APP或笔记本自带命令。
5.1 第一步:物理层与网络层(5分钟)
目标:确认EASY的以太网口物理连通且IP可达。
操作:
- 用手机安装“Network Scanner”APP,扫描192.168.1.0/24网段,看EASY的IP(如192.168.1.10)是否出现在设备列表,MAC地址是否匹配(汇川设备MAC前缀为00-0E-2E);
- 笔记本ping EASY的IP,必须100%通(无丢包),且延迟<2ms(局域网内);
- 执行
telnet 192.168.1.10 502,如果黑窗口闪退或提示“连接被拒绝”,说明EASY的Modbus服务未启动(SMB_MODBUS_TCP_SLAVE未调用或EN=0);如果卡住几秒后显示空白,说明服务已启动但无响应(可能是DB_NO错误或固件版本低)。
关键指标:ping通是基础,telnet能连上才是服务启动的铁证。我见过太多人ping通就以为OK,结果telnet失败——因为EASY的以太网口分“管理口”和“通信口”,X1是通信口(502端口),X2是管理口(80/443端口),插错网线就白忙。
5.2 第二步:协议层(10分钟)
目标:确认Modbus TCP帧能正常收发,且功能码响应正确。
操作:
- 用Modbus Poll连接EASY(IP:192.168.1.10,端口502),读取地址0(功能码0x03),长度1;
- 如果返回“非法地址”(异常码0x02),说明START_ADDR或LENGTH配置错误,或DB块未初始化;
- 如果返回“服务器忙”(异常码0x06),说明EASY正在处理其他请求,降低Poll间隔至500ms重试;
- 如果返回正常数据(如0x0000),再写入地址0值0x1234,然后重读,确认值已更新。
关键技巧:Modbus Poll的“诊断”菜单里有个“显示原始报文”,打开它,你会看到EASY返回的帧长固定为12字节(MBAP头6字节+功能码1字节+字节数1字节+数据4字节),这是EASY固件的签名特征。如果帧长不对,一定是网络中间设备(如交换机)做了MTU限制或协议过滤。
5.3 第三步:应用层(15分钟)
目标:确认上位机与EASY的数据交互符合工艺逻辑。
操作:
- 在EASY的DB块中,定义一个测试变量(如DB100.TEST_VAL : INT := 0);
- AutoShop中写一行代码:
DB100.TEST_VAL := DB100.TEST_VAL + 1;放在主循环; - 上位机配置读取DB100.TEST_VAL(地址40001),观察值是否每秒+1;
- 再配置写入DB100.TEST_VAL(地址40001),输入值999,确认EASY侧变量实时变为999。
这一步暴露所有隐藏问题:DB块是否被其他程序覆盖?上位机读写是否触发了EASY的扫描周期?网络延迟是否导致上位机超时?我坚持做完这三步才进入正式联调,因为80%的“通讯不稳定”问题,其实卡在第一步或第二步。
经验:现场调试时,我包里永远装着三样东西:一根已知良好的网线(带水晶头特写照片)、一个USB转以太网适配器(应对笔记本无网口)、以及一张手写纸,记录每次操作的“输入-预期输出-实际输出”。不是为了留痕,而是强迫自己思考因果链。比如“写入40001=100,但读出来是0”,那一定是DB块地址映射错了,而不是网络问题。
6. 从EASY到AM系列的演进:MODBUS_TCP从站能力的实质性升级
当客户问“汇川am系列modbus tcp通讯server编程”时,他们其实在对比EASY和AM系列的工程适用性。作为同时做过EASY-320和AM600项目的工程师,我可以明确说:AM系列不是EASY的简单升级,而是架构级重构,MODBUS_TCP从站能力从“可用”变成了“可编程”。
6.1 核心差异:固件协议栈 vs. Codesys运行时
- EASY系列:MODBUS_TCP从站是固件内置模块,用户只能调用SMB_MODBUS_TCP_SLAVE,无法干预协议细节,寄存器映射硬编码;
- AM系列:基于Codesys Runtime,MODBUS_TCP从站由标准库(ModbusTCP_Slave)实现,用户可自定义:
- 动态注册任意地址(支持0x0000~0xFFFF全范围);
- 自定义功能码(如扩展0x43读取CPU温度);
- 设置多从站(一个AM可同时做4个独立Modbus从站);
- 配置超时、重试、日志级别等高级参数。
这意味着,AM系列的Modbus从站开发,本质是写一段Codesys ST代码,而EASY是填几个参数。前者灵活但需编程能力,后者简单但受限。
6.2 兼容性迁移:EASY项目如何平滑过渡到AM
如果你的EASY项目已上线,想升级到AM系列,不必重写全部逻辑。我的迁移路径是:
- DB块结构复用:AM系列支持导入EASY的DB块定义(.db文件),直接复用变量名和地址偏移;
- SMB_MODBUS_TCP_SLAVE替换:在AM的Codesys中,删除原系统块,添加ModbusTCP_Slave实例,将EASY的DB_NO、START_ADDR、LENGTH参数,映射为AM的
pBaseAddress(指向DB块首地址)、wSize(长度); - 功能码适配:AM的ModbusTCP_Slave默认支持全部标准功能码,无需额外配置,EASY不支持的0x0F(写多线圈)在AM上开箱即用;
- 性能提升:AM的响应时间从EASY的<8ms降至<2ms(ARM Cortex-A9 1GHz),且支持16个并发连接。
但要注意:AM系列的Modbus TCP端口默认为502,但可通过Codesys配置改为其他端口(如503),这在EASY上是不可能的。这种灵活性,正是AM面向中大型设备的定位体现。
最后分享一个小技巧:EASY项目调试时,如果上位机是国产SCADA(如力控、紫金桥),它们的Modbus驱动往往对EASY的“静默失败”特性适应不良。我的应急方案是:在EASY侧加一个“通讯健康位”(如DB100.COMM_OK : BOOL),由SMB_MODBUS_TCP_SLAVE的STATUS>0触发置位,上位机不再依赖Modbus响应,而是读取这个BOOL位来判断连接状态。这个位就像汽车仪表盘的“发动机故障灯”,不告诉你哪里坏了,但告诉你该停车检查了。