先说个真实的场景:某天晚上我在现场调一台设备,上位机用串口和欧姆龙PLC通信,面板上的通信指示灯明明在一闪一闪,数据却没一个是对的。我在那个机柜前蹲了三个多小时,换了线、换了串口工具、换了电脑,最后才意识到问题出在通信参数和FCS校验上。那晚之后我跟自己说,欧姆龙PLC通信协议这个事,值得好好写一篇笔记。
这篇文章聊的是欧姆龙PLC通信协议,主要覆盖HostLink、FINS、无协议通信、Modbus-RTU这几种常见玩法,以及我在实际项目中踩过的坑和最后搞明白的原理。适合这几类人看:用上位机、触摸屏、工控机跟欧姆龙PLC读写数据,发现通信不上、数据不对、时断时续,想搞明白底层逻辑的工程师和电气调试人员。我会把踩坑过程和排查思路都交代清楚,而不是只丢一个结论。
1. 先分清欧姆龙PLC的几套通信协议,别拿错钥匙开错锁
我最早接触欧姆龙的时候,以为PLC通信就是一个串口协议,后来发现完全不是这么回事。欧姆龙PLC的通信体系比很多品牌更杂,同一个CPU上有好几个通信口,每个口能跑的协议还不一样。选错协议这件事,占了我遇到的通信问题里至少三成。
1.1 欧姆龙PLC通信协议的版图
我按自己实际用过的场景,把常用的几种协议整理成一张表:
| 协议名称 | 物理载体 | 典型用途 | 常见于 |
|---|---|---|---|
| HostLink | RS-232C / RS-422 / RS-485 | 上位机、触摸屏与PLC串口通信 | CP系列、CJ系列、CS系列 |
| FINS/UDP | 以太网 | 上位机通过网口读写PLC数据,无需建立连接,报文效率高 | CJ2、CS1、NJ/NX等带以太网口型号 |
| FINS/TCP | 以太网 | 网口通信,TCP保证可靠传输,适合跨路由或对稳定性要求高的场景 | 同上 |
| Modbus-RTU | RS-485 | PLC与仪表、变频器、触摸屏等第三方设备通信 | 部分型号串口支持,或用无协议方式转发 |
| 无协议通信 | RS-232C / RS-485 | 用TXD/RXD指令自定义收发任意帧,接扫码枪、打印机、非标仪表 | CP1H、CP1L、CJ2M等 |
| EtherNet/IP | 以太网 | NJ/NX与AB等支持CIP协议的设备互联 | NJ/NX系列,Sysmac Studio配置 |
这张表不用背,但心里要有这个概念:欧姆龙PLC的同一个串口,可以通过编程软件把它配置成不同协议模式。我见过不少同行在软件里把串口模式从HostLink改成"无协议"之后,上位机立刻读不到数据,就是因为他不知道这个口已经变了属性。
1.2 按PLC型号和通信口判断该用什么协议
选协议的大原则是三步:先看CPU上有什么物理口,再看这套系统里的上位机或触摸屏想怎么连,最后看数据量大小。
串口又是RS-232C又是RS-485的时候,优先确认插件型号和DIP开关位置。例如CP1H自带一个RS-232C口,CP1L自带RS-485口,如果要用RS-485接多个从站,得把终端电阻和方向控制搞清楚。我踩过的一个低级坑:把CP1W-CIF11上的终端电阻拨码全拨到ON,结果整个485总线上一个设备都收不到数据,逐个排查了几个小时才发现是终端电阻多了。
1.3 很多人把“通信口属性”和“程序里的指令”搞混
这一点我想单独拿出来说。HostLink、无协议通信、Modbus-RTU这些,本质上是串口通道被配置成某种工作模式,它决定了PLC用什么样的规则去解析串口收到的一串字节。而TXD/RXD、MBUS这些指令,是在程序里主动往通信口发数据用的。
有个典型的反面案例:有人想让CP1H和一台Modbus-RTU从站的仪表通信,于是把上位机软件里的Modbus地址都填好,但PLC串口模式还停留在HostLink,程序里也没写TXD指令,结果就是上位机发出去的数据石沉大海。记住一句话:串口模式决定通道规则,程序指令决定主动发送的帧内容,两者不是一回事。
2. 串口HostLink的坑:参数、FCS校验、帧格式一个都不能错
HostLink是欧姆龙上位机链接协议,相当于欧姆龙设备之间和设备与上位机之间的串口“官方语言”。我最初就是从HostLink开始踩坑的,现在回头看,几乎所有问题都集中在三处:通信参数、FCS校验、帧格式细节。
2.1 默认通信参数看着简单,错一个字符就全是乱码
欧姆龙PLC串口的HostLink模式,出厂常见参数是波特率9600、8位数据位、偶校验、1位停止位。这个组合一般写作9600, 8, E, 1。问题在于,很多串口调试助手和上位机编程组态软件的默认设置是9600, 8, N, 1,也就是无校验。
我遇到过的情况是:上位机显示发送成功,PLC的RX指示灯也闪,但CPU不响应。用串口监听一看,PLC回的根本不是正常帧,因为它在用偶校验去解读收到的字节,全乱套了。排查方式很朴素:把上位机软件参数改成偶校验,重启通信测试,马上就好了。
改参数的位置在编程软件里:CX-Programmer或CX-One中打开PLC属性,找到内置串口设置,把通信设置为HostLink,再核对单元号、波特率、校验位。这里有个容易漏的点——单元号。HostLink帧里的单元号默认是0,如果PLC侧设置成1,而上位机发的是0,PLC会认为这不是发给自己的帧,连响应都不给。这个现象和参数错误几乎一样:纯无响应,没有任何错误提示。
2.2 HostLink帧结构:从@到CR一个都不能少
搞明白参数只是第一步,第二步是理解HostLink帧本身长什么样。我记了很久的帧结构是这样:
@ 单元号 命令 参数 校验符 终止符 @ 00 RD 0000000002 FCS *CR拆开来看:
@:帧起始符,所有HostLink命令以这个开头。00:单元号,PLC CPU单元一般0,扩展串口板可能是1,看PLC侧设置。RD:读命令,表示读取数据。0000000002:参数区。前四位是起始地址,后四位是读取长度。比如读DM0000开始的2个字,起始地址填0000,长度填0002。FCS:帧校验,2个ASCII字符,这是最多人出错的地方。*CR:终止符,一个星号加回车。很多初学者在调试助手里只发了@00RD0000...FCS就回车了,没发星号,PLC当然不认。
正常响应也是类似结构,PLC会回一条以@开头的帧,里面带着读到的数据。如果PLC判断命令有错误,响应帧可能以!开头,后面跟错误码。具体到自己的型号,还是要以欧姆龙对应系列的通信手册为准,但熟悉这套结构后,看手册会快很多。
2.3 FCS校验的正确算法:异或前面所有的ASCII字符
这是HostLink里最容易翻车的地方,我自己就在这栽过两次。FCS的计算方法是把从@开始到参数区结束的所有字符的ASCII码,逐字节做异或,得到一个十六进制数值,再把高四位和低四位分别转成ASCII码字符,拼成两个字符放在FCS位置。
关键点有两个:
第一,起始符@也要参与异或计算。我见过很多示例代码里漏掉@,直接对后面的"00RD0000..."做异或,结果算出来的校验码永远和PLC对不上。第二,异或结果是十六进制数值,比如结果是0x4E,要转换成ASCII字符'4'和'E'放到帧里,而不是发送4E这两个十六进制字节。这个转换思路想通了,校验就没那么玄了。
我给自己的土办法是:先在Excel里写一列ASCII码,用BITXOR逐步算一遍,再用串口调试助手实测;等这个方法验证过一条帧之后,再上程序代码。
2.4 实操示例:HostLink读DM区的一次完整验证
我拿CP1H为例,假设PLC侧串口已配置成HostLink,单元号0,波特率9600偶校验,上位机发下面这条帧去读DM0000开始的2个字:
@00RD0000000002FCS*CRPLC如果正常响应,返回的帧里会包含那2个字的当前值,每个字以4个十六进制ASCII字符表示。比如DM0000里是1234,DM0001里是ABCD,响应就类似:
@00RD1234ABCDxxxxxxxxFCS*CR我先在PLC程序里用内存监视窗口确认DM0000的实时值,再和HostLink读回来的值比对,就能立刻判断地址映射和数据格式对不对。这个方法比直接跑到现场对着屏猜要有效得多。
2.5 我踩过的串口通信其他坑
除了参数和FCS,串口通信还有一个隐蔽坑:USB转串口线的质量。我曾在台式机上用一根普通的CH340转接线,通信时好时坏,还以为是PLC侧设置问题,用示波器量了下波形,发现TX引脚电平毛刺一堆,明显是转换器驱动能力不足。换了一根带隔离的USB转RS-422/485线之后彻底消停。现场有条件的话,尽量用带隔离的串口线,省得莫名其妙被干扰坑一次。
3. FINS协议:以太网通信绕不过去的坎
串口HostLink搞定之后,我很快遇到了第二个门槛——以太网通信。欧姆龙在网口上主推的不是HostLink,而是FINS协议。它比HostLink灵活,报文能承载更长的数据块,对批量读写设备区非常方便,但对报文结构的严谨程度也高,一个字节错位,轻则响应错误,重则直接超时。
3.1 FINS到底是什么
FINS全称是Factory Interface Network Service,欧姆龙自定义的一套应用层协议。它不关心底层是串口、以太网还是Controller Link,只管“在欧姆龙设备之间传递命令和响应”。
理解FINS最好的方式,是把它当成一台设备的“网络信封”。信封上写着收件人是谁、发件人是谁、这封信是什么服务的,里面装着的才是真正的读命令、写命令和参数。以太网环境下,这封信通常承载在UDP或TCP之上,端口号都是9600。
3.2 读懂FINS头这10个字节
FINS报文开头有10个字节的帧头,这10个字节我建议每个做通信的人都认一遍:
| 字节 | 名称 | 含义 |
|---|---|---|
| ICF | 信息控制字段 | 决定是命令还是响应,是否需要确认 |
| RSV | 保留 | 通常填0 |
| GCT | 网关数 | 一般填0 |
| DNA | 目标网络号 | 本地网络通常1或0 |
| DA1 | 目标节点号 | 接收方PLC节点号 |
| DA2 | 目标单元号 | PLC CPU单元通常0 |
| SNA | 源网络号 | 发送方所在网络 |
| SA1 | 源节点号 | 发送方节点号 |
| SA2 | 源单元号 | 发送方单元号 |
| SID | 服务标识 | 用于命令和响应配对 |
我踩过的一个坑就是SID。上位机连续发了多帧FINS命令,每一帧的SID全都一样,PLC虽然能响应,但上位机很难区分返回的是哪条命令的结果。后来我把SID设计成自增序号,问题就清晰了。
3.3 内存区读命令的地址映射逻辑
FINS里最常用的命令是内存区读,命令码是01 01。它的参数包含区域代码、起始地址和数据长度。这个“起始地址”不是PLC编程软件里的绝对地址,而是在该区域内的相对地址偏移。
Debug的时候最容易出问题的是十进制和十六进制混用。比如读DM100,如果填了十进制100转十六进制0x0064,那FINS地址就要写成64,而不是直接写100。这个我栽过很多次,因为PLC程序里看到的是“DM100”,下意识就把01 01后面的地址填成100,响应帧返回错误码。
更绕人的是位区域和字区域。读位状态要用位单位的区域代码,读一个字要用字单位。如果想监控一个开关量是ON还是OFF,别拿字单位命令去读整个字再自己移位,直接按位读,返回数据干净得多。
3.4 自己拼FINS帧还是用现成库
自己拼FINS帧的好处是能掌握底层逻辑,坏处是容易在字节序和区域代码这些细节上浪费大量时间。我自己的路线是:先看一遍协议手册理解帧结构,然后直接参考成熟的开源实现,比如HslCommunication里的欧姆龙FINS实现,把它的帧生成逻辑读一遍,再对照Wireshark抓包里的真实报文,最后按项目需求改成自己的代码。
这里想强调一个观点:用现成库之前,最好先抓一次包,眼睁睁看看库里发出去的命令长什么样。不然等到现场协议出问题,你会完全没有方向。
4. 以太网通信的节点号、端口与连接管理,坑得最深的三个点
串口搞定了,以太网我也跑通了基础读写,但接下来踩的坑比串口更隐蔽。这三个点分别是节点号、端口、TCP连接管理。
4.1 FINS帧里的节点号不是IP地址
FINS头里的DA1和SA1是节点号,不是IP地址。这是很多初次用欧姆龙以太网通信的人栽跟头的地方。你在电脑上配好PLC的IP地址,PING一下也通,但上位机发FINS命令就是得到错误响应,原因常常是节点号不匹配。
我经手的机器里,有些CPU内置以太网口的节点号在设置页里是手动指定的。如果PLC里节点号配的是10,那FINS头里的DA1必须填10,而不是填IP地址最后一段。这个设置和IP地址是独立的两项,千万别想当然认为IP是192.168.1.10节点号就是10。更稳妥的做法是打开CX-One里PLC的以太网设置页,把节点号看清,再在上位机里填成同一个值。
4.2 端口9600和连接数限制
FINS/UDP和FINS/TCP都走9600端口。很多IT部门会顺手把9600当成普通端口屏蔽掉或做了隔离,结果现场通信不通,费半天劲才找到问题。我后来有个习惯:凡涉及欧姆龙FINS通信,先确认网络里9600端口没有被防火墙或交换机策略过滤。
TCP连接数这个问题,我印象特别深。某个项目里有三台工控机同时连一台PLC,用的是FINS/TCP,跑了一晚上之后新加的一台上位机死活连不上。后来发现PLC内置以太网口的TCP连接数是有上限的,老连接空闲不关,新连接就进不来。解决办法是让上位机通信程序在空闲时主动断开FINS/TCP,或者换成FINS/UDP这种无连接方式。
4.3 UDP和TCP怎么选
我的经验是:同一台设备在同一个局域网里,优先用UDP。FINS/UDP省去了建立连接的握手过程,报文一发一收,逻辑简单,丢包概率在普通厂房网络里低到可以忽略。跨网段、跨路由、或者现场网络质量差的环境,才需要FINS/TCP,因为TCP负责重传和排序,不会因为一个丢包把整个通信流程卡死。
还有一点要注意,FINS/TCP建立连接后,通常需要先做一次节点地址交换或者说“注册”,这个过程是欧姆龙特定的握手流程。如果只按普通TCP套接字连接上就急着发读命令,PLC可能不回。这里提醒一下,具体帧序列务必对照你所用型号的FINS/TCP通信手册。
5. 变频器、温控表和触摸屏:别把协议用串了
搞定了PC和PLC之间的几种通信之后,更多的坑来自外围设备。这个主题下最多人搜的是“1200PLC与欧姆龙变频器的485通讯程序”“欧姆龙温控表E5CC说明书”这类跨品牌、跨设备的需求,说明实际问题里协议种类远比想象多。
5.1 无协议通信:PLC自己当主站去拼帧
和变频器、温控表这类非欧姆龙设备通信时,PLC往往得放下HostLink和FINS,改用串口的无协议模式。所谓无协议,其实就是把串口当成一个透明通道,你想发什么字节都行,PLC程序里用TXD指令发出去,再用RXD指令收回来。
这时候协议就得你手动拼了。比如某台变频器支持Modbus-RTU协议,从站地址是1,功能码03是读寄存器,寄存器地址是0x2000,那你要发的一帧就是:
01 03 20 00 00 01 CRC高 CRC低这串字节通过TXD发出,然后等变频器回一帧。拼这种帧的时候,CRC16计算和字节顺序最容易错。尤其是高低字节顺序,不同手册表述习惯不一样,我看过有人把CRC高低字节写反,从站直接不响应。
5.2 Modbus-RTU在欧姆龙串口上的使用注意
并不是所有欧姆龙PLC串口都能直接选“Modbus-RTU主站”模式。就算你的CPU本身支持,也要先看CX-P或CX-One里这个口的设置列表里有没有这个选项。有的型号串口只支持HostLink和无协议,这时想和Modbus从站通信,只能用无协议模式手动拼帧。
另外,如果PLC本身是Modbus-RTU从站,被第三方触摸屏或上位机读,需要关注Modbus地址和PLC内存区之间的映射关系。比如4X区对应什么内存区、3X区对应什么内存区,不同系列哪怕是同系列的CPU固件,映射也可能不一样。我一般先用触摸屏自带的通信诊断功能读PLC一个已知值,验证映射正确了再往下写程序。
5.3 跨品牌组合的一个通用经验
“西门子S7-1200PLC和欧姆龙变频器做485通信”这种需求,本质是两台设备各说各话。这个问题没有捷径:拿到双方手册,确定变频器的从站地址、寄存器地址、数据格式、CRC校验方式,然后从PLC侧发标准Modbus帧。
我的顺序是:先不写PLC程序,拿电脑上的串口调试工具加上Modbus计算工具手动发一帧,能把这个变频器的寄存器的确读到,说明链路和协议是对的,然后再把这套验证过的帧搬到PLC的TXD/RXD逻辑里。多数现场问题,到最后发现不是PLC程序错,而是变频器侧的从站地址或波特率没设对。
5.4 触摸屏驱动选型别想当然
威纶通、昆仑通态、Proface这些触摸屏,做欧姆龙驱动时经常有一长串型号选项。选HostLink还是选FINS Ethernet,直接决定了触摸屏把地址解释成什么。
举个例子:同样一个“D100”地址,在HostLink驱动里的地址空间和FINS Ethernet驱动里的地址空间可能就不是同一个区。我用过一台屏,选成FINS Ethernet之后发现怎么都读不到程序里的DM区数据,后来查驱动说明才确认这个型号的以太网驱动走的是NJ系列的标签/DB方式,不是CP系列的DM区方式。
选驱动还有一个小技巧:看触摸屏的驱动里有没有“Omron FINS/UDP”和“Omron FINS/TCP”的区分。选UDP的驱动时,触摸屏软件界面里通常只需要填IP地址、节点号;选TCP的驱动有时会多出端口和连接保持时间设置。这两个不一样,填错了也连不上。
6. 三步排障法:从“完全没反应”到“快速定位”
最后分享一套我总结的通信排障方法,适用于上面所有协议和场景。这三步走完,大部分通信问题都能定位到具体原因。
6.1 第一步:看灯,确认物理层
先看PLC通信口的状态灯和以太网口的LINK/ACT灯。串口灯完全不闪,说明上位机的收发引线根本没有到达PLC,问题大概率在接线、串口线、USB转换头或者接口定义上。以太网LINK灯不亮,说明网线或交换机端口有问题。灯在闪,至少说明物理层有信号,可以往下排查。
我自己排障时很少迷信“灯在闪就是没问题”,但用它来做第一道判断题非常高效。
6.2 第二步:发一条已知帧,直接问PLC“在不在”
物理层没问题后,用串口调试助手或网络调试助手,手动发一条已知的、最简单的命令帧。比如HostLink发一条读一个字的命令,FINS/UDP发一条读一个字的命令。
这一步的目的很简单:跳过你的上位机程序,用最原始的手段验证PLC到底能不能正常响应一条标准帧。如果调试助手发出去,PLC照常响应,那问题就在上位机软件配置上;如果调试助手发出去也没响应,那问题就在通信参数、协议选择和报文结构上,和你的业务程序无关。
这个方法帮我把“上位机程序的问题”和“通信链路的问题”快速切开,不然容易两边的代码改来改去,最后一查其实是个参数错误。
6.3 第三步:抓包对比,看响应码
如果调试助手发帧还是不通,就要进入抓包环节。
串口可以用带监听功能的USB转串口线,或者用虚拟串口软件把上位机发的和PLC回的分别记录下来。以太网直接用Wireshark抓,过滤条件填写port 9600,就能看到FINS/UDP或FINS/TCP报文。抓包的好处是能把请求和响应原封不动地展示出来,哪个字节多了少了,一对比就出来了。
如果PLC有响应,但响应帧里带了错误代码,那就去对应的通信手册里查这个错误码的含义。常见几类错误码大致都指向这些问题:区域代码不对、地址越界、数据长度超限、命令代码不存在。记住这些错误码不代表什么,真正有用的是你的项目里响应码出现哪些值,然后根据手册回查。
6.4 一个完整的复盘案例:FCS错误导致的“假死”
我在这里复述一个最典型的排障过程,帮你把三步法串起来。
当时现象:上位机读PLC内存,一直提示通信超时,PLC通信指示灯有闪烁。第一步看灯,物理层正常。第二步用串口调试助手发了一条HostLink读命令,无响应。到这里基本能断定问题在帧本身。
然后用监听线抓包,发现上位机发出的帧和我们预期的手册帧差了一个字符——FCS校验位算错了。我一开始以为是转换函数写错,后来发现是FCS计算时把起始符@漏了。改掉之后,调试助手再发同样一条帧,PLC立刻给出正常响应。整个过程不到半小时,但如果没有经历前面那些半夜排障,我不会养成“抓包对比”这个习惯。
最后记录几件搞明白之后的事
踩完这些坑,我给自己定了几个规矩,也分享给正在和欧姆龙PLC通信较劲的朋友:
第一,任何通信项目的第一个调试动作,一定是拿调试助手发一条最基础的读指令,而不是直接跑上位机程序。链路通不通、帧对不对,这一步就能筛掉一大半问题。
第二,所有通信地址都要先在PLC编程软件的内存监视里确认一遍,再把对应关系映射到通信命令里。这个习惯帮我避开了很多“数据读出来但对应不上”的尴尬。
第三,协议手册要下载对应具体型号的通信手册,而不是看网上零散笔记。我第一次查的时候偷懒,看的帖子里的FINS区域代码和实际CPU型号对不上,白白浪费了半天。
欧姆龙PLC通信协议从原理上讲并不复杂,但对细节的要求非常苛刻。只要把物理层、帧格式、地址映射、校验这四件事盯住,这台PLC的通信基本就能被拿捏。希望这篇踩坑记录,能让你少走一些我走过的夜路。