主题
pipe "fins" — 欧姆龙 FINS 协议
协议名:"fins"
欧姆龙 CP / CJ / CS 系列 PLC 的 FINS 协议(以太网)。管道对象的读写方法、返回值约定和错误码都和 7.1 Pipe 通信概述 一致,本页只讲 FINS 特有的创建方式和地址格式。
平台限制
本协议只在上位机版本和 Linux 网关版本里提供,ESP32 机型不支持,在 ESP32 上 pipe.setup("fins", ...) 会返回 nil。
ESP32 上访问欧姆龙 PLC 可以走 Modbus TCP / RTU(多数机型支持),用 "modbus.tcp"。
创建管道
lua
pipe.setup("fins", "ip[:port]") --> pipe | nil第二个参数是字符串,不是 table。
| 形式 | 说明 |
|---|---|
"192.168.1.100:9600" | 指定 IP 和端口 |
"192.168.1.100" | 用默认端口 9600 |
lua
fins = pipe.setup("fins", "192.168.1.100:9600")说明
- 默认端口
9600,这也是欧姆龙 FINS/TCP 的标准端口。 - 目标节点号固定
1、目标单元号固定0、源节点号固定1,不能从 Lua 侧配置。PLC 侧的 FINS 节点号需要与之对应(通常是 IP 末位或参数里设定的节点号)。
地址格式
区域前缀 + 十进制编号。位地址用小数点形式 区域字号.位号,不带小数点就是字地址。
| 前缀 | 区域 | 字地址示例 | 位地址示例 |
|---|---|---|---|
CIO | I/O 区 | CIO100 | CIO0.00 |
W | 工作区 | W100 | W0.00 |
H | 保持区 | H100 | H0.00 |
A | 辅助区 | A100 | A0.00 |
DM | 数据存储区 | DM1000 | DM1000.00 |
EM | 扩展数据区 | EM100 | EM0.00 |
lua
fins:read_bite("CIO0.00") -- 读 CIO 0 通道第 0 位
fins:write_bite("W0.00", true)
fins:read_u16("CIO100") -- 读 CIO100 字
fins:read_u16("DM1000", 10) -- 读 DM1000 起 10 个字
fins:write_float("DM2000", 25.5)说明
- 位方法只能用位地址,字方法只能用字地址。用
read_bite("CIO100")(无小数点)或read_u16("CIO0.00")(有小数点)都会被拒绝,返回参数错误8002。 - 位号按欧姆龙习惯写两位(
.00~.15),代码用十进制解析,写.0也能识别。 A辅助区在欧姆龙里有一部分是只读的,但代码不做只读检查,写这些地址会被 PLC 侧拒绝。- 代码里不做地址范围检查。
可用方法
16 个类型化读写方法全部可用,用法见 7.1 的对象方法:
read_bite write_bite read_s16 write_s16 read_u16 write_u16 read_s32 write_s32 read_u32 write_u32 read_float write_float read_double write_double read_string write_string
不可用:p:read() 和 p:write() 在 FINS 里没有实现。
read_string 的第 2 个参数是字节数,默认 10。
常见用法
lua
fins = pipe.setup("fins", "192.168.1.100:9600")
function poll()
-- 读 10 个 DM 字到本机 D 区
local ret = fins:read_u16("DM1000", 10)
if ret.errno == 0 then
for i = 1, 10 do plc.d(i, ret.data[i]) end
end
-- 按位控制
if plc.m(0) == 1 then
fins:write_bite("W0.00", true)
end
end
t = timer.start(500, poll)注意事项
- ESP32 固件不支持本协议,
pipe.setup直接返回nil。 - 位地址必须带小数点,字地址必须不带,这是最常见的报错来源。
- 节点号 / 单元号写死在代码里,PLC 侧参数要配成对应值,否则 PLC 不响应(
errno为1007或8109)。 - 管道对象要用全局变量长期持有。
例程
每个例程都是可直接下载运行的完整脚本(仅上位机 / Linux 网关版本,ESP32 上
pipe.setup("fins", ...)返回nil)。通用接口见 8.1 概述。统一模板「场景 / 完整代码 / 要点 / 验证方法 / 注意事项」。
例程 1:轮询 DM 区 + 按位控制
适用:LP3232 / LP3288 / 软PLC模拟器 / 上位机(不含 ESP32)| 依赖模块:pipe timer plc | 难度:★☆☆
场景:欧姆龙 CP/CJ/CS 系列,每 500ms 把 DM1000 起 10 个字读到本机 D 区,本机 M0 置位时把 W0.00 置 1。
完整代码(可直接下载运行):
lua
fins = pipe.setup("fins", "192.168.1.100:9600") -- 第二参字符串,默认端口 9600
if not fins then
print("创建失败(ESP32 不支持 fins,改用 modbus.tcp)")
return
end
function poll()
local ret = fins:read_u16("DM1000", 10)
if ret.errno == 0 then
for i = 1, 10 do plc.d(i, ret.data[i]) end
end
if plc.m(0) == 1 then fins:write_bite("W0.00", true) end
end
t = timer.start(500, poll)要点:
pipe.setup("fins", "ip:port")—— 第二参是字符串,默认端口9600(不是 502)。- 位地址带小数点(
W0.00),字地址不带(CIO100)——位/字类型必须与方法匹配。 - ESP32 上创建返回
nil,必须判空。
验证方法:在上位机 / Linux 网关版本运行,本机 D100~D109 出现 DM1000~DM1009 的值。
注意事项:位方法用位地址(read_bite("CIO0.00"))、字方法用字地址(read_u16("CIO100"));混用会被拒绝返回 8002。
例程 2:写浮点 + 写位(写方法返回整数错误码)
适用:同上 | 依赖模块:pipe | 难度:★☆☆
场景:把浮点 25.5 写到 DM2000,并把 W0.00 置位。
完整代码(可直接下载运行):
lua
local fins = pipe.setup("fins", "192.168.1.100:9600")
if not fins then return end
local e1 = fins:write_float("DM2000", 25.5)
print("写浮点:", e1 == 0 and "成功" or "失败")
local e2 = fins:write_bite("W0.00", true)
print("写位:", e2 == 0 and "成功" or "失败")要点:写方法返回整数错误码,0 成功;浮点用 write_float,位用带小数点地址。
验证方法:欧姆龙侧 DM2000 = 25.5、W0.00 = 1。
注意事项:把写结果当 table 去查 result.errno,整数无该字段、判断恒真;务必 e1 == 0。
例程 3:注意事项——位/字地址不分
适用:同上 | 依赖模块:pipe | 难度:★☆☆
场景:最易出现的参数错误:位方法用了字地址、字方法用了位地址。
完整代码(可直接下载运行):
lua
local fins = pipe.setup("fins", "192.168.1.100:9600")
if not fins then return end
-- ❌ 错误:位方法用了字地址、字方法用了位地址 → 返回 8002
fins:read_bite("CIO100") -- 无小数点 = 字地址,应给 read_u16
fins:read_u16("CIO0.00") -- 有小数点 = 位地址,应给 read_bite
-- ✅ 正确
fins:read_bite("CIO0.00") -- 位
fins:read_u16("CIO100") -- 字要点:FINS 位地址带小数点(CIO0.00),字地址不带(CIO100);read_bite 用位、read_u16 用字。
验证方法:正确写法拿到值,错误写法 errno = 8002。
注意事项:位/字混用是 FINS 最常见的 8002 来源;ESP32 上 fins 一定返回 nil。