简介:这份源码资源面向欧姆龙PLC通信开发的新手与有一定经验的工程师,解决Modbus TCP/IP协议下PLC连接与数据收发的实际问题。包内提供完整的VB.NET工程方案,包含OmronComm解决方案文件、主窗体与通信核心模块,涉及HostLink、FINS指令及串口通信等关键实现,可直接编译运行用于测试和学习。资源共47个文件,以vb源码为主,辅以exe可执行程序、xml配置、dll动态库及resx资源文件等,压缩包约199KB,结构紧凑便于快速上手。目前已有2772人学习下载,说明其参考价值得到一定验证。读者可从中获取欧姆龙PLC与Modbus TCP/IP通信的完整代码框架、数据收发逻辑与工程组织方式,适合作为入门练手或项目开发的参考模板。
1. 欧姆龙 PLC 走 Modbus TCP/IP:一份 ASP 源码能省掉多少联调时间
车间里一台欧姆龙 CP2E 通过以太网口对外提供数据,上位机却是一套跑在 IIS 上的老 ASP 系统,这种组合在设备改造和产线数据采集里并不少见。问题往往不在 PLC 侧,而在上位机怎么把 Modbus TCP/IP 的寄存器读出来、再塞进网页里刷新。这份「欧姆龙 PLC 通 Modbus TCP/IP 通信实例源码 ASP」就是冲着这个场景来的:它把 PLC 侧的通信配置和 ASP 侧的读写逻辑打包在一起,让做设备联网、SCADA 对接、产线看板的人能直接照着改。适合手里有欧姆龙 PLC、需要快速验证以太网通信、又不想从零啃协议栈的工程师,也适合刚接触 PLC 编程入门、想拿一个能跑通的实例理解 Modbus 报文长什么样的新手。
2. 先搞懂欧姆龙侧到底开了什么:Modbus TCP/IP 的地址映射与功能码
2.1 为什么欧姆龙要用 Modbus TCP/IP 而不是私有协议
欧姆龙自家有 FINS 协议,功能强、地址表达灵活,但上位机要对接就得装欧姆龙组件或自己实现 FINS 报文,跨平台和跨语言都麻烦。Modbus TCP/IP 是公开标准,几乎所有组态软件、SCADA、甚至 Python 和 C# 的现成库都支持,调试时还能用通用工具抓包看报文。常见做法是:PLC 侧启用 Modbus TCP 从站功能,上位机当主站去轮询。这样上位机不用关心欧姆龙内部地址体系,只按 Modbus 的寄存器编号读写,换 PLC 型号时改动也小。代价是 Modbus 的数据模型比较扁平,位、字、双字的表达要靠约定,映射没规划好后期会乱。
2.2 欧姆龙 Modbus 地址与 PLC 软元件的对应关系
欧姆龙带以太网口的机型(如 CP2E、NJ 系列配合通信模块)做 Modbus TCP 从站时,会把 PLC 的软元件映射到 Modbus 的四个数据区。常见映射思路是:线圈对应输出位,输入状态对应输入位,保持寄存器对应数据寄存器 DM 区,输入寄存器对应输入通道。具体映射表要看机型手册,不同型号的起始地址和数量不一样。下面这张表是排查时最常用的对照,实际以你手上设备的说明书为准。
| Modbus 数据区 | 功能码 | 典型对应欧姆龙软元件 | 读写方向 |
|---|---|---|---|
| 线圈 Coil | 01 读 / 05 写单个 / 15 写多个 | 输出位、内部辅助位 | 可读可写 |
| 离散输入 | 02 读 | 输入位 | 只读 |
| 保持寄存器 | 03 读 / 06 写单个 / 16 写多个 | DM 数据寄存器 | 可读可写 |
| 输入寄存器 | 04 读 | 输入通道 | 只读 |
提示:地址编号有「0 基」和「1 基」两种习惯,组态软件里填的 40001 和报文里的 0 往往差一位,联调时先确认这一点,能省掉大量「读出来全是 0」的排查。
2.3 用通用工具先验证 PLC 从站是否活着
在动 ASP 代码之前,先用一个 Modbus 主站工具确认 PLC 侧通了。这一步是分界线:工具能读到数,问题就在 ASP;工具读不到,别去改网页代码。常见做法是用 Modbus Poll 这类工具,连接参数按下面填。
# 连接参数(以常见默认值为例,实际按 PLC 设置改) IP 地址: 192.168.1.10 # PLC 以太网口地址 端口: 502 # Modbus TCP 标准端口 从站地址: 1 # PLC 侧设定的单元号 功能码: 03 # 读保持寄存器 起始地址: 0 # 对应 DM 区起始 寄存器数量: 10 # 一次读 10 个字参数说明:端口 502 是 Modbus TCP 的约定端口,改之前确认 PLC 侧没占用别的服务;从站地址在单台 PLC 直连时通常为 1,多台走网关时才需要区分;起始地址和数量决定一次请求的报文长度,读太多会被从站拒绝或分帧。如果工具能稳定读出数值,说明网络、端口、从站配置都没问题,接下来才轮到 ASP 侧。
3. ASP 侧怎么把 Modbus 报文发出去:Socket 连接与寄存器解析
3.1 ASP 里做 TCP 通信的现实约束
ASP 是服务端脚本,跑在 IIS 上,本身没有现成的 Modbus 库,常见做法是用服务器端的 Socket 组件或调用 COM 组件来发 TCP 报文。老系统里常用的是微软自带的 Winsock 控件封装,或者第三方 Socket 组件。这里要有个心理准备:ASP 的 Socket 编程比现代语言啰嗦,连接、发送、接收、关闭都要手动管,异常处理也得自己写。所以这份源码的价值不在于「优雅」,而在于把一整套能跑的流程固定下来,你改 IP 和地址就能用。
3.2 手工拼 Modbus TCP 报文
Modbus TCP 的报文结构是固定的:前面 7 个字节是 MBAP 头,后面是 PDU。读保持寄存器的请求报文可以手工拼出来,不依赖任何库。
<% ' 拼接 Modbus TCP 读保持寄存器请求报文(功能码 03) Function BuildReadRequest(slaveId, startAddr, regCount) Dim tid, pid, len, pdu tid = Chr(0) & Chr(1) ' 事务标识,随便给,响应会原样带回 pid = Chr(0) & Chr(0) ' 协议标识,Modbus 固定为 0 len = Chr(0) & Chr(6) ' 后续字节数:单元号1 + 功能码1 + 地址2 + 数量2 pdu = Chr(slaveId) ' 从站地址 pdu = pdu & Chr(3) ' 功能码 03 pdu = pdu & Chr(Int(startAddr / 256)) & Chr(startAddr Mod 256) ' 起始地址,高字节在前 pdu = pdu & Chr(Int(regCount / 256)) & Chr(regCount Mod 256) ' 寄存器数量 BuildReadRequest = tid & pid & len & pdu End Function %>逻辑说明:MBAP 头里事务标识用来匹配请求和响应,协议标识固定 0,长度字段是从单元号开始算的字节数。PDU 里功能码 03 表示读保持寄存器,地址和数量都是大端序,所以要先高字节后低字节。参数上,startAddr 是 0 基地址,regCount 一次别超过 125,否则从站可能直接拒绝。
3.3 发送请求并解析响应
发出去之后要收响应,响应里第 9 个字节开始才是数据,每个寄存器两个字节,同样是大端。
<% ' 发送请求并解析响应中的寄存器值 Function ReadRegisters(ip, port, slaveId, startAddr, regCount) Dim sock, req, resp, i, val, result Set sock = Server.CreateObject("MSWinsock.Winsock") ' 按实际组件名替换 sock.Connect ip, port req = BuildReadRequest(slaveId, startAddr, regCount) sock.SendData req ' 实际使用需等待并读取返回,这里示意解析逻辑 resp = sock.GetData result = "" For i = 0 To regCount - 1 ' 数据从第 9 字节开始(0 基为第 9 位),每寄存器 2 字节 val = Asc(Mid(resp, 10 + i * 2, 1)) * 256 + Asc(Mid(resp, 11 + i * 2, 1)) result = result & val & "," Next sock.Close Set sock = Nothing ReadRegisters = result End Function %>逻辑说明:连接后发送请求,收到响应先跳过 MBAP 头和功能码、字节数,从数据段开始按两字节一组还原成整数。参数上,ip 和 port 要和 PLC 侧一致,slaveId 对应从站地址。注意响应长度要校验,如果返回的功能码最高位是 1,说明是异常响应,后面一个字节是错误码,这时不能按正常数据解析。
3.4 把读到的值渲染到页面
拿到寄存器值之后,ASP 直接输出到 HTML 里就行,配合定时刷新或前端轮询。
<% Dim values values = ReadRegisters("192.168.1.10", 502, 1, 0, 4) Response.Write "<p>当前寄存器值:" & values & "</p>" %>逻辑说明:这里把四个寄存器的值拼成字符串输出。实际项目里通常会把每个寄存器映射成有意义的变量名,比如温度、速度、产量,再套进页面模板。参数上,刷新频率别设太高,Modbus 从站处理能力有限,几百毫秒一次已经算频繁,再快容易丢包。
4. 联调时最容易翻车的几个点:避坑与排查
4.1 现象:工具能读,ASP 读出来全是 0
原因:ASP 里拼报文时地址用了 1 基,而 PLC 侧按 0 基解析,或者字节序搞反了。解决:先用工具确认 0 基地址下的值,再对照 ASP 代码里的 startAddr,确认没有多加 1;字节序上 Modbus 规定大端,如果 PLC 侧数据是双字,还要考虑字交换。
4.2 现象:连接直接失败,报端口拒绝
原因:PLC 侧 Modbus TCP 从站功能没启用,或者端口不是 502,或者 IIS 服务器和 PLC 不在同一网段。解决:先在服务器上 ping PLC 地址,再用 telnet 测端口,确认网络可达;然后进 PLC 设置确认从站已开启、端口和单元号正确。
4.3 现象:读几个寄存器正常,读多了就超时
原因:一次请求的寄存器数量超过从站限制,或者轮询间隔太短,从站来不及响应。解决:把单次读取数量降到 100 以内,读多段就分多次请求;轮询间隔根据从站能力调整,别用死循环狂发。
4.4 现象:页面偶尔卡死,IIS 里 ASP 线程堆积
原因:Socket 连接没有正确关闭,异常路径下对象没释放,连接越积越多。解决:在错误处理里也保证 Close 和 Set Nothing 执行,给 Socket 操作加超时,别让一个请求无限等下去。
4.5 现象:写入寄存器后 PLC 没反应
原因:功能码用错,写单个和写多个混了,或者写到了只读区。解决:写单个寄存器用 06,写多个用 16,先确认目标地址在 PLC 侧是可写的 DM 区,别往输入区写。
5. 把这套源码用成自己的:改地址、加轮询、做数据缓存
拿到源码之后,别急着全盘照抄,先按自己的设备把几个关键参数替换掉。第一步是改连接参数,把 IP、端口、从站地址换成现场实际值,这一步在代码里通常集中在几个常量或函数入口,改完先用工具验证一遍。第二步是规划寄存器映射,把要采集的每个量对应到具体的 Modbus 地址,做成一张表贴在代码注释里,后期维护时不用再翻手册。第三步是处理轮询节奏,如果页面访问量不大,可以在每次请求时实时读;如果访问频繁,建议加一层缓存,比如把最近一次读到的值存到 Application 或数据库,页面读缓存,后台定时刷新,这样既减轻 PLC 压力,也避免多个用户同时触发 Socket 操作。
<% ' 简单缓存示例:定时刷新,页面读缓存 Dim lastValues, lastTime Application.Lock If IsEmpty(Application("plcValues")) Or DateDiff("s", Application("plcTime"), Now) > 2 Then Application("plcValues") = ReadRegisters("192.168.1.10", 502, 1, 0, 10) Application("plcTime") = Now End If lastValues = Application("plcValues") Application.Unlock Response.Write lastValues %>逻辑说明:用 Application 存最近一次读取结果,超过 2 秒才真正去读 PLC,其余请求直接返回缓存。参数上,2 秒是经验值,产线节奏快可以缩短,但别低于从站能承受的轮询下限。验证方法很简单:打开页面看数值是否随 PLC 变化更新,同时用工具观察请求频率是否被压下来。从那以后我每次接这类老 ASP 加 PLC 的项目,都会先把寄存器映射表和轮询策略写清楚再动代码,不然联调时全是玄学问题。希望帮到你。
本文还有配套的精品资源,点击获取