资讯详情

欧姆龙PLC以太网通信实战:FinsTCP协议详解与C#/Python代码实现

📅 2026/10/4 15:50:51 | 华诺云谱 👁 阅读
欧姆龙PLC以太网通信实战:FinsTCP协议详解与C#/Python代码实现
FinsTCP是欧姆龙PLC设备联网绕不开的一个协议。不管你是做上位机软件、MES数据采集还是给老设备做数字化改造只要面对的是欧姆龙CP、CJ、CS、NX/NJ系列PLC大概率都要跟这个协议打交道。我最早接触它是在一个车间数据采集项目里要实时读几十台CP1H的当前温度、压力和节拍数据当时方案选型绕了一大圈最后就是靠FinsTCP把问题解决的。这篇文章不是教科书翻译而是我从实际项目里踩出来的经验包括帧格式、节点号设置、C#和Python通信代码以及几个非常容易栽跟头的细节。不管你是刚入门的电气工程师还是准备做上位机开发的程序员按着这篇内容走一遍基本就能把欧姆龙PLC的以太网通信跑通。1. FinsTCP是什么为什么搞懂这一个协议就够用1.1 从串口时代到以太网时代FINS协议的家底FINS的全称是Factory Interface Network Service是欧姆龙定义的工厂接口网络服务协议。这个协议比很多人想象的更老早期跑在串口线路上用来实现电脑或触摸屏与PLC之间的数据交换。后来工厂开始普及以太网欧姆龙把FINS报文原封不动地封装到TCP/IP或者UDP/IP之上就变成了我们今天看到的FinsTCP和FinsUDP。这里的关键词是“原封不动”——FINS报文本身的格式和规则没有大变变的只是传输载体。所以如果你以前用过HOSTLINK或者串口FINS切换到FinsTCP时只需要理解怎么把FINS报文塞进TCP流几乎零学习成本。为什么这个老协议到现在还在大量使用核心原因是欧姆龙PLC的内置以太网口基本都原生支持FinsTCP不需要额外购买任何通信模块。在CP1H、CJ2M、CS1D这些常见型号上打开以太网口设置填一个IP地址和一个FINS节点号上位机就能通过FinsTCP直接读写PLC的CIO区、DM区、HR区等所有数据区。对于系统集成商来说这意味着设备联网改造的技术门槛和硬件成本都被压得很低。1.2 选型对比FinsTCP、FinsUDP、Modbus TCP到底选谁我见过不少人在项目里纠结到底用FinsTCP还是Modbus TCP先说结论如果PLC是欧姆龙中大型系列且没有特殊的跨平台约束我倾向于直接用FinsTCP。下面是几个主流方案的对比。对比项FinsTCPModbus TCPFinsUDP连接方式TCP长连接TCP长连接UDP无连接默认端口96005029600报文格式FINSModbusFINS地址映射直接访问CIO/DM/HR等原生态地址需要先把数据区映射成保持寄存器或线圈同FinsTCP适合场景欧姆龙PLC一对一或多对一采集跨品牌设备统一接入低时延轮询、组播等特殊场景为什么TCP而不是UDP这个问题我当时也仔细考虑过。UDP看起来更快但FinsUDP没有连接状态丢了包不会自动重传响应和请求是不是配对的全靠SID字段匹配。在车间这种电磁环境比较复杂的地方偶尔丢一个UDP包如果没有应用层重试数据就会出现空洞。FinsTCP虽然也有TCP的开销但对大多数采集场景来说数据量根本到不了带宽瓶颈多出来的那几毫秒延迟完全无所谓换来的却是稳定可靠的长连接。所以除非你需要在极低延迟的环境里做高频轮询否则我建议优先上FinsTCP。另外FinsTCP和Modbus TCP还有一个很实际的区别地址概念。Modbus TCP里你需要把PLC的数据区映射成保持寄存器、线圈这些抽象地址如果PLC本身不支持Modbus从站功能还得选一个支持Modbus-TCP的通信模块非常麻烦。而FinsTCP天生就直接操作CIO、WR、HR、DM这些欧姆龙原生态地址读D区就是命令码加内存区代码0x82读W区就是0x31省掉了映射这层环节。这也是为什么但凡碰到欧姆龙PLC懂行的人会直接建议用FinsTCP。3. FINS帧结构拆解这10个字节必须是送分题3.1 帧头字段逐个说ICF、DNA、DA1、SA1分别管什么FINS命令帧的帧头一共10个字节字段含义如下表所示。很多初学者看到这一堆缩写就头大其实真正需要自己填的只有4个目标节点号DA1、源节点号SA1、SID以及ICF。字段占用字节含义请求帧常用值ICF1信息控制字段表示请求还是响应、是否需要响应等0x80RSV1保留字段0x00GCT1网关计数0x02DNA1目标网络号0x00表示本地网络DA11目标节点号即PLC的FINS节点地址根据PLC实际设置DA21目标单元号CPU单元一般为00x00SNA1源网络号0x00SA11源节点号即上位机自己的FINS节点地址1到254之间的值SA21源单元号0x00SID1服务标识请求和响应用同一个值配对建议从0开始递增这里最容易被搞混的是DA1和IP地址。DA1是FINS层的节点标识跟IP地址最后一字节不是完全等价的关系。欧姆龙大多数内嵌以太网口的默认配置里FINS节点号默认值就等于IP地址最后一字节所以很多人想当然地把节点号当IP末位来填也确实能用。但如果你手动改过PLC里的FINS节点号还按照IP末位去填就会碰到“网络通了但FINS就是没反应”的诡异现象。排查方法后面第四章会详细说。SA1这个字段也是高频问题因为热门搜索里总有人问“欧姆龙中的FINS中的SA1是什么意思”。它其实就是源节点号代表发起通信的那一端在整个FINS网络里的身份标识。上位机软件作为一个FINS节点也要给自己分配一个节点号可以选1到254之间的任意值建议选一个不会被其他站占用的值比如0x01。我做项目时喜欢把PC的IP末位当作SA1这样组网时一眼就能看出哪个IP、哪个节点在哪台设备上排故方便。3.2 命令码与内存区代码读D区、刷CIO区的核心命令读数据和写数据最常用的命令码是0101内存区读取和0102内存区写入。命令码占两字节高字节在前这一点在字节序部分还会强调。除了读写还有2301强制置位、2302强制复位这类命令用于远程控制某个位为ON或OFF但日常数据采集用得不多。内存区代码决定了你操作的是哪个物理区域。最常用的几个如下表所示。内存区字访问时代码典型地址换算CIO区0x30CIO 0对应0x0000CIO 100对应0x0064WR区0x31W0对应0x0000W5对应0x0005HR区0x32H0对应0x0000H10对应0x000ADM区0x82D0对应0x0000D100对应0x0064注意表里这些内存区代码是针对“字访问”的。如果你要做的是读某个位、强置某个位情况会再复杂一点。位地址的线性换算规则在不同型号手册里存在细节差异实操时我一般是先查手册再在设备上实测一次不建议拍脑袋。本篇里先把字访问的基础链路跑通能覆盖90%的数据采集场景。3.3 报文实例读D100的请求帧和响应帧长什么样先看一条最简单的读D100开始的10个字的请求帧十六进制如下。80 00 02 00 00 0B 00 00 01 00 01 01 82 00 64 00 0A逐段拆一下80是ICF00是RSV02是GCT00是目标网络号0B是目标节点号也就是PLC的FINS节点号这里假设是1100是目标单元号第三个00是源网络号01是源节点号最后的00是SID。这10个字节是帧头。然后是命令码01 01表示读命令。82表示DM区。00 64是起始地址高字节在前换算成十进制就是100对应D100。最后的00 0A表示读取10个字。对应的正常响应帧大概长这样C0 00 02 00 01 00 00 0B 00 00 00 00 [10个字的数据共20字节]开头的C0是ICFbit6被置位表示这是一个响应帧。帧头之后紧跟两个字节00 00这是命令执行状态码0x0000表示正常。如果状态码不是0000说明PLC拒绝执行这条命令具体含义对应第四章表格里的状态码说明。状态码之后才是真正的数据区。所以解析响应帧的套路非常固定读前12个字节跳过帧头和状态码剩下的就是数据。再看一条写命令。要把0x1234写入D100、0x5678写入D101请求帧如下80 00 02 00 00 0B 00 00 01 00 01 02 82 00 64 00 02 12 34 56 78命令码换成了0102地址和位数量照旧后面跟着要写入的数据按高字节在前排列。写命令的响应帧比较短只有帧头和状态码没有数据区。4. 实操上手从PLC设置到上位机连通的完整路径4.1 PLC侧准备Sysmac Studio里要改的三个参数先在Sysmac Studio里把PLC的以太网参数配好。打开软件连接PLC后找到“控制器设置”下的“内置以太网端口设置”。主要改三个地方IP地址、子网掩码、FINS节点号。IP地址按你规划好的网段填子网掩码一般填255.255.255.0。往下找“FINS节点号”或者“FINS/TCP设置”把节点号改成你规划好的值。这里有几个容易踩的坑。第一FINS节点号默认值通常是1但如果你手动把它改成和IP末位不一致的值上位机就很容易填错。我做网络规划时会直接让FINS节点号等于IP地址末位省去后面所有麻烦。第二改完IP或节点号后PLC必须重新上电才能生效不是软件里点一下下载按钮就完事。第三如果PLC和电脑之间隔了路由器和交换机要确认PLC的CPU单元型号支持FinsTCP有些老型号需要额外选购以太网模块。Sysmac Studio里还提供一个通信测试功能在“通信设置”里可以测试电脑和PLC之间的以太网连接。不过我实际用得不多因为那个界面只能确认TCP通不通没法精确验证FINS报文是否正确。我更习惯用Python脚本做联调后面详说。4.2 C#实现FinsTCP读取一个可以直接套用的基础类下面这段C#代码我写得很精简但足够用于生产环境。它做的事就是建立TCP连接、拼装FINS读命令、发送、接收并解析响应。using System; using System.Net.Sockets; public class FinsTcpClient { private TcpClient _client; private NetworkStream _stream; private byte _sid 0; private byte _destNode; public bool Connect(string ip, byte destNode, int port 9600) { _client new TcpClient(); _client.Connect(ip, port); _stream _client.GetStream(); _destNode destNode; return _client.Connected; } // 读DM区 public byte[] ReadDm(ushort startAddr, ushort count) { byte[] cmd new byte[17]; cmd[0] 0x80; // ICF请求帧 cmd[1] 0x00; // RSV cmd[2] 0x02; // GCT cmd[3] 0x00; // DNA目标网络号 cmd[4] _destNode; // DA1目标节点号 cmd[5] 0x00; // DA2目标单元号 cmd[6] 0x00; // SNA源网络号 cmd[7] 0x01; // SA1源节点号 cmd[8] 0x00; // SA2源单元号 cmd[9] _sid; // SID cmd[10] 0x01; // 命令码 0101 cmd[11] 0x01; cmd[12] 0x82; // DM区 cmd[13] (byte)(startAddr 8); cmd[14] (byte)(startAddr 0xFF); cmd[15] (byte)(count 8); cmd[16] (byte)(count 0xFF); _stream.Write(cmd, 0, cmd.Length); // 接收响应实际项目建议做粘包拆包处理 byte[] resp new byte[1024]; int len _stream.Read(resp, 0, resp.Length); ushort status (ushort)((resp[10] 8) | resp[11]); if (status ! 0) throw new Exception($FINS状态码异常: 0x{status:X4}); byte[] data new byte[len - 12]; Array.Copy(resp, 12, data, 0, data.Length); return data; } public void Close() { _stream?.Close(); _client?.Close(); } }代码里有两个地方值得说明。第一响应帧的帧头也是10字节后面2字节是状态码所以数据区从第12字节开始。第二我特意没有处理TCP粘包拆包只在能确定响应一次性返回的情况下简化实现。真要上生产别偷懒建议用MemoryStream缓冲起来解析出完整一帧再处理。使用方式很简单先new一个FinsTcpClientConnect传入PLC的IP和节点号然后ReadDm传入起始地址和读取字数拿到的byte数组就是按大端排好序的原始字数据。要转成float、int、short自己再按规则转换即可。注意C#默认的BitConverter是小端直接转int会出错后面专门说这个问题。4.3 用Python快速验证通信链路有时候不想打开Visual Studio只想快速验证PLC通不通或者临时读几个点排故用Python最顺手。下面这段脚本是我平时排查通信问题的标配工具。import socket def read_dm(ip, dest_node, start_addr, count, port9600): cmd bytes([ 0x80, 0x00, 0x02, 0x00, dest_node, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, # 0101 读命令 0x82, # DM区 (start_addr 8) 0xFF, start_addr 0xFF, (count 8) 0xFF, count 0xFF ]) with socket.create_connection((ip, port), timeout3) as s: s.send(cmd) resp s.recv(2048) status (resp[10] 8) | resp[11] if status ! 0: raise RuntimeError(fFINS状态码异常: 0x{status:04X}) return resp[12:] if __name__ __main__: data read_dm(192.168.1.10, dest_node10, start_addr100, count5) for i in range(5): val (data[i * 2] 8) | data[i * 2 1] print(fD{100 i} 0x{val:04X} {val})注意socket.recv是一次性接收不保证服务端一帧能全部到达所以这个脚本只适用于通信测试和生产环境里报文不超过MTU的场景。正式做采集程序还是要处理分片和粘包和C#版本是一个道理。另外强烈建议学一下Wireshark抓包。FinsTCP的默认端口是9600Wireshark自带FINS协议的解析器抓包后能直接看到帧头各个字段的解析结果。我排查地址对不对、节点号错没错一半时间是在看抓包而不是断点调试。5. 常见问题排查实测中遇到的坑和解决办法5.1 网络通但FINS无响应先查节点号这个现象太经典了。上位机ping PLC能通TCP也能正常连接9600端口但发出去的FINS请求就是等不到响应。90%的情况是DA1目标节点号填得和PLC里的FINS节点号不一致。因为TCP连接是IP层的事跟FINS层的节点号没有绑定关系IP通了不代表FINS就能对上。解决办法很直接先在Sysmac Studio的控制器设置里查看实际的FINS节点号再检查上位机代码里destNode填的是不是同一个值。如果PLC侧设置改过记得重新上电否则节点号还是旧的。还有一个容易被忽略的点是源节点号SA1。有些上位机把自己配成0x00这在FINS规则里是非法的因为0x00是保留值。结果就是PLC收到请求后不知道往哪里回表现为“请求发出去了但一直等不到响应”。SA1改成1到254之间的值问题立刻消失。5.2 响应帧里的状态码怎么看FINS响应帧里的状态码是排查问题的钥匙。遇到错误时先别急着改代码对着表格看含义。状态码含义常见原因0x0000正常完成无0x0101本地节点错误源节点号或网络号配置异常0x0103目标节点不存在目标节点号错误或该节点未连接0x0105目标节点忙碌PLC正在执行编程调试操作稍后重试0x0201目标节点错误命令格式内存区代码或地址格式错误0x0202目标节点错误命令长度请求帧长度与命令不匹配0x0401服务未执行命令码不支持或PLC禁止该操作遇到0201和0202基本就是报文拼错了重点检查内存区代码、起始地址字节序、位数长度。遇到0101和0103优先检查节点号和网络号。0105比较有迷惑性它通常表示PLC正忙常见于有人正用编程软件在线连接PLC时你对PLC发起大量读写请求。等对方断开在线连接或者把轮询频率降下来一般就能恢复。5.3 字节序、数据类型、连接占满这些隐藏的坑FINS报文里多字节数据一律大端序也就是高字节在前。这个约定和网络字节序一致但如果你的上位机程序跑在x86上直接用BitConverter读short、int就会翻车。举个例子PLC里D100存的值是0x1234我们读到的byte[]是[0x12, 0x34]在C#里直接BitConverter.ToInt16会得到0x3412完全错位。正确做法是用(Buffer[0] 8) | Buffer[1]手动拼或者用BinaryPrimitives.ReadUInt16BigEndian。32位整数和浮点数同理。浮点还要额外注意PLC里存的是不是IEEE 754标准欧姆龙大多数型号是但个别CJ系列老固件表现不太一样。我每次写完通信程序都会先做一个回环测试往PLC写入一组已知值读回来对比确认字节序和浮点格式都没问题再正式启用。连接占满也是个低频但麻烦的问题。欧姆龙PLC对同时建立的FinsTCP连接数是有限制的不同型号上限不同常见是4个或8个。调试时开了好几个上位机窗口老窗口没释放新连接要么连不上要么响应变慢就是连接数被占满了。排查方法关掉所有测试程序等几十秒再用单个客户端试。写正式程序时记得用using或者try/finally保证TcpClient一定会被关闭别让连接泄漏到PLC重启才能释放。FinsTCP这个协议本身不复杂或者说它的复杂点非常集中——帧头10字节、内存区代码、字节序这三大块。把这几块弄明白欧姆龙PLC的以太网通信基本就是手到擒来的事。最后分享一个我自己的习惯任何欧姆龙FinsTCP项目我都会先写一个只有几十行的Python验证脚本把PLC侧的IP、节点号、目标地址确认好再开始写正式的上位机程序。这一步省掉了我大量来回调试的时间。另外就是SID一定不要图省事固定写成一个值每次请求递增一下排查网络丢包和指令乱序时能省不少事。这些经验都是实打实踩坑换来的希望能帮你少走弯路。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑