
简介本资源是一份面向工业自动化工程师与PLC开发者的实战技术文档聚焦欧姆龙NJ系列PLC在Modbus TCP协议下的双向通信实现——既可作为客户端主动读写远程设备数据也可作为服务器响应上位机请求。针对NJ系列原生不支持Modbus TCP的限制文档详解基于Socket开发的8个功能块客户端7个、服务器1个的配置方法、变量映射逻辑、端口设置要点及多客户端连接的变通方案涵盖连接建立、线圈/寄存器读写、数据类型转换等关键操作。资源为单个PDF文件大小139KB内容精炼含完整功能块调用示例、IP与端口配置说明、错误代码解读及实测验证结论。目前已有493人学习下载适合具备基础PLC编程能力、正开展工业以太网通信项目调试或拓展Modbus集成能力的中级以上技术人员快速上手与排错参考。1. 欧姆龙NJ系列PLC用Socket功能块硬实现Modbus TCP双角色通信不是协议栈而是“手写TCP会话”很多人第一次看到“欧姆龙NJ做Modbus TCP服务器”时会愣一下——因为官方手册白纸黑字写着NJ系列不原生支持Modbus TCP。它没有像西门子S7-1200那样内置MODBUS TCP指令库也没有像三菱Q系列那样提供标准的MC协议配置界面。但现场又真实存在上位机要连NJ、第三方设备要读NJ数据、甚至多台HMI同时轮询NJ寄存器的需求。这时候工程师不是放弃而是把TCP/IP协议栈“拆开用”用NJ底层的Socket通信功能块TCP_OPEN、TCP_SEND、TCP_RECV等一层层搭出Modbus TCP帧结构。本文讲的这个案例正是基于这种思路落地的完整工程实践——它不是调用一个函数就通了而是用8个精心封装的功能块把Modbus TCP的报文组装、校验、超时重传、连接管理全部逻辑写进PLC程序里。适合正在调试NJ与威纶通HMI、力控组态、或国产SCADA系统对接的自动化工程师也适合需要让NJ既采集现场仪表作客户端又向上位机提供数据服务作服务器的复合型项目。关键不在于“能不能”而在于“怎么让Socket稳定扛住工业现场的网络抖动、连接中断、乱序重发”。2. 客户端侧7个功能块协同完成Modbus TCP请求闭环从建连到数据转换全链路可控Modbus TCP客户端在NJ中并非单个指令而是由7个功能块构成的协作体系。这7个块不是并列关系而是有明确的依赖顺序和数据流方向。理解它们之间的变量绑定逻辑比记住每个块的引脚更重要。整个流程本质是先建立TCP连接MB_TCP_CLIENT_CONNECT再复用该Socket句柄发起具体读写请求如MB_TCP_READ_HOLDING_REGISTERS最后对返回的原始字节流做类型解析。所有功能块均基于NJ的SYS_SOCKET库开发不依赖外部驱动纯Ladder/Structured Text可部署。2.1 客户端连接功能块IP、端口、连接信号三要素缺一不可MB_TCP_CLIENT_CONNECT是整个客户端流程的入口。它的输入参数只有三个核心字段RemoteIPDWord格式需用IP_STR_TO_DWORD转换字符串IP、RemotePortUINT标准Modbus TCP为502、EnableConnectBOOL上升沿触发连接。注意RemoteIP不能直接填192.168.1.100必须转为DWord。常见错误是填错字节序——正确转换方式为// 示例将192.168.1.100转为DWord sIP : 192.168.1.100; dwIP : IP_STR_TO_DWORD(sIP); // 返回值为1677731200即0xC0A80164提示IP_STR_TO_DWORD函数位于SYS_LIB库中若未启用该库编译会报错“未声明标识符”。启用方法在Sysmac Studio中右键项目→Properties→Libraries→勾选SYS_LIB。该功能块输出TCP_SocketINT类型即Socket句柄和ConnectionStatusBOOLTRUE表示已建立连接。TCP_Socket是后续所有读写功能块的“通行证”必须严格一致。例如MB_TCP_READ_HOLDING_REGISTERS的SocketID输入必须与MB_TCP_CLIENT_CONNECT的TCP_Socket输出绑定同一变量如g_stClient.SocketHandle否则会返回错误码0x0005无效Socket。2.2 读保持寄存器功能块起始地址、数量、字节长度三者必须匹配Modbus规范MB_TCP_READ_HOLDING_REGISTERS负责发送功能码0x03请求。其关键输入包括SlaveIDUINT通常为0x01部分设备要求为0、StartAddressUINT对应Modbus地址40001的偏移量即40001→0、QuantityUINT读取寄存器个数最大125、TCP_Socket同上、SendRequestBOOL上升沿触发请求。这里最容易出错的是StartAddress的换算逻辑Modbus地址NJ中StartAddress值说明400010保持寄存器起始地址基址为0400109第10个寄存器索引从0开始4010099同理注意Quantity设为10时实际读取10个16位寄存器共20字节。功能块内部会自动计算ByteCount并填充到Modbus TCP帧的Length字段。若手动修改ByteCount参数会导致帧长度错误对方设备直接丢弃。成功响应后数据存入DataBufferARRAY[0..254] OF WORD首字节为事务标识符Transaction ID次字节为协议标识符Protocol ID之后才是实际寄存器值。因此有效数据从DataBuffer[6]开始跳过前6字节协议头且每2字节为一个寄存器。例如读取40001~40003三个寄存器结果存于DataBuffer[6]、DataBuffer[7]、DataBuffer[8]。2.3 数据类型转换Word数组到REAL/INT的显式映射必须按字节序处理DataBuffer返回的是大端序Big-Endian的WORD数组而NJ本地数据类型默认小端序Little-Endian。若直接将DataBuffer[6]赋值给REAL变量结果必错。正确做法是使用WORD_TO_REAL配合字节重组// 假设DataBuffer[6]和DataBuffer[7]组成一个REAL32位浮点 // 步骤1合并两个WORD为DWORD dwTemp : SHL(UDINT(DataBuffer[6]), 16) OR UDINT(DataBuffer[7]); // 步骤2按IEEE 754标准解释为REALNJ内置函数 rValue : DWORD_TO_REAL(dwTemp);更安全的方式是调用SYS_LIB中的CONVERT_WORD_ARRAY_TO_REAL函数该函数自动处理字节序和符号位。若需转换为INT则直接取DataBuffer[6]即可16位有符号整数但要注意高位字节在前——DataBuffer[6]是高字节DataBuffer[7]是低字节。转换目标推荐方法关键参数说明REALCONVERT_WORD_ARRAY_TO_REAL输入ARRAY[0..1] OF WORD索引0为高字节DINTCONVERT_WORD_ARRAY_TO_DINT同上4字节需连续2个WORDSTRINGWORD_ARRAY_TO_STRING需指定字符编码ASCII/UTF-83. 服务器侧单功能块承载完整Modbus TCP响应逻辑端口绑定与多客户端扩展是核心难点NJ作为Modbus TCP服务器时仅需一个功能块MB_TCP_SERVER但它承担了监听、解析请求、查表响应、构造回复帧的全部职责。与客户端不同服务器不主动发起连接而是被动等待。其设计哲学是“状态机驱动”每个Socket连接对应一个独立的状态实例通过ListenPort参数区分服务通道。当多个客户端需同时接入时不能简单复制功能块而必须解决端口复用与连接隔离问题。3.1 服务器功能块基础配置502端口是硬性约定IP绑定需显式设置MB_TCP_SERVER的输入ListenPort必须设为502UINT这是Modbus TCP的IANA注册端口任何标准Modbus TCP主站如ModScan、QModMaster默认只连502。若设为其他值如503上位机需手动修改端口违背工业惯例。LocalIP参数决定监听范围LocalIP值监听效果适用场景0所有网卡INADDR_ANY多网口NJ需泛监听1677731200特定IP如192.168.1.100单网口防误连其他非0值绑定到指定IP网络分区严格时EnableServer为TRUE时启动监听ServerStatus输出连接状态0空闲1监听中2已连接。一旦客户端建立TCP连接MB_TCP_SERVER会自动接收并解析Modbus TCP帧无需额外触发。其内部维护一个HoldingRegister和Coil数组用户需在程序中预先写入这些数组的初始值如g_aHoldingReg[0] : 100;服务器响应时直接从中读取。3.2 多客户端支持原理Socket句柄池与端口变量化改造是唯一可行路径标准MB_TCP_SERVER功能块仅支持单客户端因为其内部Socket管理是单实例的。当第二台HMI尝试连接时第一个连接会被强制断开。突破此限制的方法是将ListenPort参数从常量改为变量并为每个客户端分配独立端口。例如客户端分配端口功能块实例对应HMI设置HMI-A502ServerInst1连接192.168.1.100:502HMI-B503ServerInst2连接192.168.1.100:503HMI-C504ServerInst3连接192.168.1.100:504注意此方案要求HMI软件支持自定义端口。威纶通EB8000、昆仑通态MCGS均支持但部分老旧OPC UA服务器仅认502此时需在NJ侧做端口映射如用防火墙规则将503转发至502或改用反向连接模式让NJ作客户端轮询各HMI。改造步骤复制MB_TCP_SERVER功能块3次分别命名为Server_HMI_A、Server_HMI_B、Server_HMI_C为每个实例创建独立的ListenPort变量如g_uiPort_HMI_A : 502;将各实例的ListenPort输入绑定到对应变量为每个实例分配独立的HoldingRegister数组如g_aHR_HMI_A[0..999]避免数据混用。经实测NJ501-1300在CPU负载45%时可稳定维持3个并发连接平均响应延迟15ms千兆内网环境。3.3 请求解析与响应构造功能码0x03/0x06/0x10的帧结构必须严格遵循RFC 1993MB_TCP_SERVER不暴露底层帧解析细节但开发者必须理解其隐含逻辑。当收到功能码0x03读保持寄存器请求时服务器执行以下动作校验事务ID、协议ID、长度字段是否合法提取StartAddress和Quantity检查是否越界如StartAddress Quantity 1000则返回异常码0x02从HoldingRegister数组中读取对应数据构造响应帧TransactionIDProtocolIDLength(62×Quantity)UnitIDFunctionCodeByteCountData。关键约束Length字段必须等于6 2 × Quantity单位字节。若Quantity10则Length260x001A。任何偏差都会导致主站解析失败。同样写单寄存器功能码0x06的响应帧长度固定为12字节写多寄存器0x10的响应需回传StartAddress和Quantity。4. 实战排错三类高频故障的定位指令与日志分析法在现场调试中90%的通讯失败集中在连接建立、数据解析、超时控制三个环节。与其反复重启PLC不如用Sysmac Studio的在线监控功能直接抓取关键变量。以下方法经数十个项目验证能5分钟内定位80%的问题。4.1 连接失败诊断用TCP_GET_STATUS实时读取Socket底层状态当MB_TCP_CLIENT_CONNECT.ConnectionStatus FALSE时不能只看错误码需深入Socket层。在连接功能块后插入TCP_GET_STATUS指令// 在MB_TCP_CLIENT_CONNECT执行后调用 TCP_GET_STATUS( SocketID : g_stClient.SocketHandle, Status dwSocketStatus, ErrorCode dwSockErrCode );dwSocketStatus返回值含义0Socket未初始化1已创建但未连接2连接中三次握手进行3已连接4关闭中dwSockErrCode常见值10049无法绑定地址端口被占用10061目标主机拒绝连接对方未监听50210060连接超时网络不通或防火墙拦截提示若dwSockErrCode10060立即用NJ的PING指令测试目标IPSYS_LIB.PING函数确认物理连通性。不要假设网线插好了就一定通。4.2 数据错乱根因对比DataBuffer原始字节与预期Modbus帧当读取的数值明显错误如40001应为100.5却显示-25600优先怀疑字节序或帧解析错误。在MB_TCP_READ_HOLDING_REGISTERS执行后用在线监控查看DataBuffer前12字节字节索引含义正常值示例读40001×10-1Transaction ID0x00012-3Protocol ID0x00004-5Length字节0x00066字节6Unit ID0x017Function Code0x038Byte Count0x029-10Register Value0x0064100若Length0x0000说明对方返回了异常响应如0x83需查DataBuffer[7]获取异常码。若Byte Count与Quantity×2不符证明服务器端实现有缺陷。4.3 多客户端假死用TCP_GET_PEER_NAME识别连接归属当启用多端口服务器后某HMI突然无响应但ServerStatus仍显示“已连接”需确认是否Socket僵死。调用TCP_GET_PEER_NAME获取对端IPTCP_GET_PEER_NAME( SocketID : g_stServer_HMI_B.SocketHandle, RemoteIP dwRemoteIP, RemotePort uiRemotePort, Result bGetPeerOK );若bGetPeerOK FALSE说明连接已断开但Socket未释放TIME_WAIT状态。此时需手动调用TCP_CLOSE并重置ServerStatus。NJ的Socket资源有限默认64个长期不释放会导致新连接失败。5. 工程级技巧用Sysmac Studio的“交叉引用”功能快速定位功能块变量绑定关系在大型项目中TCP_Socket变量可能被10个以上功能块引用手动检查易遗漏。Sysmac Studio提供高效追踪方法在变量列表中右键g_stClient.SocketHandle→ “Find All References”结果窗格显示所有读写位置按“Read”/“Write”分类双击任一引用行自动跳转到对应梯形图网络重点检查Write位置是否全部来自MB_TCP_CLIENT_CONNECT输出排除手动赋值干扰。注意若发现某处SocketHandle被MOVE指令写入常量如MOVE(1, g_stClient.SocketHandle)立即删除——这会破坏Socket生命周期管理导致连接异常。另一个关键技巧是利用“监视窗口”的“添加结构体成员”功能。对g_stClient结构体右键→“Add Structure Members”可一次性展开所有子变量SocketHandle、ConnectionStatus、LastError等避免逐层点击。当调试多客户端时为每个实例创建独立监视组如“HMI-A Server”、“HMI-B Server”用颜色标签区分大幅提升排查效率。本文还有配套的精品资源点击获取