主题
com_fx — 串口三菱 FX 协议
模块名:com_fx
通过三菱 FX 系列 PLC 的编程口协议采集数据。和 com_modbus 一样,本模块只描述「要读写哪些软元件」,必须用 com.add_device 挂到一个 com 对象上,由 com.start 的轮询线程驱动。
软元件用字符串指定(如 "D0"、"M100"、"Y0"),不像 Modbus 那样用数字地址。
除 creat 之外的函数在库表和对象元表里都注册了,com_fx.add_channel(dev, ...) 与 dev:add_channel(...) 等价。
函数总览
库级函数
| 函数 | 作用 |
|---|---|
com_fx.creat([data_state]) | 创建一个 FX 设备对象 |
对象方法
| 方法 | 作用 |
|---|---|
dev:add_channel | 增加一条采集/写入通道 |
dev:set_time_recv | 设置应答等待超时 |
dev:set_time_error | 设置出错后的重试延时 |
常量
读写属性
| 常量 | 说明 |
|---|---|
com_fx.READ | 只读:FX → 本地 |
com_fx.READ_WRITE | 读写双向 |
数据类型
| 常量 | 占用 | 说明 |
|---|---|---|
com_fx.BITE | — | 位软元件(M/X/Y/S 等) |
com_fx.S16 | 1 个 D | 16 位有符号 |
com_fx.U16 | 1 个 D | 16 位无符号 |
com_fx.S32 | 2 个 D | 32 位有符号 |
com_fx.U32 | 2 个 D | 32 位无符号 |
com_fx.FLOAT | 2 个 D | 单精度浮点 |
字节序(模块里注册了这些常量,供与 com_modbus 保持一致的写法使用)
com_fx.AB、com_fx.BA、com_fx.ABCD、com_fx.CDAB、com_fx.BADC、com_fx.DCBA
模式:com_fx.RTU、com_fx.ASCII(常量存在,creat 不接受模式参数,FX 编程口帧格式固定)
com_fx.creat
lua
com_fx.creat([status]) --> device创建一个 FX 设备对象。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | status | integer | data | 否 | — | 通信状态回写位置。数字 = D 寄存器编号,data 对象 = 写到该对象。省略则不回写 |
返回值
| 类型 | 说明 |
|---|---|
| userdata | FX 设备对象。必须用变量接住 |
示例
lua
local st = data.creat(0)
local fx = com_fx.creat(st)说明
- FX 编程口是点对点协议,没有站号,所以
creat不需要设备地址参数。 - 建议传
status,用来判断 PLC 是否在线。
dev:add_channel
lua
dev:add_channel(device_name, data_type, target, rw) --> 0 | 1增加一条通道,把对方 PLC 的一个软元件绑定到本机 D 寄存器或 data 对象。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | device_name | string | 是 | — | 对方软元件名,如 "D0"、"D100"、"M50"、"Y0"、"X0" |
| 2 | data_type | integer | 是 | — | com_fx.BITE / S16 / U16 / S32 / U32 / FLOAT |
| 3 | target | integer | data | 是 | — | 本地落点编号。落点由数据类型决定:BITE 写进 M 区,字类型(S16/U16/S32/U32/FLOAT)写进 D 区;传 data 对象则与 D/M 无关 |
| 4 | rw | integer | 是 | — | com_fx.READ 或 com_fx.READ_WRITE |
返回值
| 类型 | 说明 |
|---|---|
| integer | 1 添加成功,0 添加失败(软元件名无法解析等) |
示例
lua
-- 把对方 D0 读到本机 D100
fx:add_channel("D0", com_fx.S16, 100, com_fx.READ)
-- 把对方 D10-D11 的浮点读到 data 对象
local temp = data.creat(0)
fx:add_channel("D10", com_fx.FLOAT, temp, com_fx.READ)
-- 双向同步:本机 M200 <-> 对方 M0
fx:add_channel("M0", com_fx.BITE, 200, com_fx.READ_WRITE)说明
- 软元件名大小写都能识别,编号按对方 PLC 的编址习惯写(X/Y 按三菱的八进制丝印编号)。
BITE只能用于位软元件(M/X/Y/S、T/C 触点),字类型只能用于 D 等字软元件。- 本地落点跟着数据类型走:上面这行把对方
M0存进本机的M200(不是D200), 读取要写plc.m(200);而"D0"那两行落的是D100,用plc.d(100)读。 梯形图侧也照这个规则接:位类通道去取 M,字类通道去取 D。
dev:set_time_recv
lua
dev:set_time_recv(ms)设置本设备的应答等待超时。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | ms | integer | 是 | — | 超时毫秒数 |
返回值
无返回值。
dev:set_time_error
lua
dev:set_time_error(ms)设置通信出错后,下一次重试前的延时。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | ms | integer | 是 | — | 延时毫秒数 |
返回值
无返回值。
说明
- 设大一点可避免对方掉线时反复超时拖慢轮询。
常见用法
把一台 FX3U 的数据搬到本机 D 区,梯形图直接用
lua
local st = data.creat(0)
local fx = com_fx.creat(st)
fx:set_time_recv(500)
fx:set_time_error(2000)
fx:add_channel("D0", com_fx.S16, 100, com_fx.READ) -- 对方 D0 → D100
fx:add_channel("D10", com_fx.FLOAT, 102, com_fx.READ) -- 对方 D10 → D102-D103
fx:add_channel("M0", com_fx.BITE, 200, com_fx.READ_WRITE) -- 对方 M0 <-> M200
fx:add_channel("Y0", com_fx.BITE, 201, com_fx.READ) -- 对方 Y0 → M201
local port = com.setup(1, 9600, com.EVEN, 7, 1) -- FX 编程口常用 9600-7-E-1
port:add_device(fx)
port:set_time_lunxun(500)
port:start()
timer.start(1000, function()
print("对方 D0 =", plc.d(100), "在线:", st:get_num() == 0)
end)注意事项
- FX 编程口的串口参数与 Modbus 不同,常见是 9600、7 数据位、偶校验、1 停止位,接不上先核对这里。
- FX 编程口是点对点,一条串口上只能接一台 FX PLC,不要挂多个
com_fx子设备。 - X/Y 软元件在三菱侧是八进制编号,写通道名时按 PLC 面板上的丝印写。
- 设备对象和
data对象都要长期持有,被 GC 回收后采集失效。 - 需要通过网口而不是串口连三菱 PLC,用
pipe的fx或mc协议。
例程
每个例程都是可直接下载运行的完整脚本:
com_fx只描述「要读写哪些软元件」,挂到com并start后由后台线程轮询,脚本注册完即结束。 API 只用本页已文档化的函数。统一模板「场景 / 完整代码 / 要点 / 验证方法 / 注意事项」。
例程 1:把一台 FX3U 的数据搬到本机 D 区
适用:LP3201 / LP3202 / LP3203 / LP3222 / LP3232 / LP3288 / 软PLC模拟器 | 依赖模块:com_fx com data timer plc | 难度:★☆☆
场景:现场一台三菱 FX3U,要把它的 D0(16 位)、D10(浮点)、M0、Y0 自动搬运到本机 D 区,让梯形图/上位机直接拿来用。
完整代码(可直接下载运行):
lua
local st = data.creat(0)
local fx = com_fx.creat(st)
fx:set_time_recv(500)
fx:set_time_error(2000)
fx:add_channel("D0", com_fx.S16, 100, com_fx.READ) -- 对方 D0 → D100
fx:add_channel("D10", com_fx.FLOAT, 102, com_fx.READ) -- 对方 D10 → D102-D103
fx:add_channel("M0", com_fx.BITE, 200, com_fx.READ_WRITE) -- 对方 M0 <-> M200
fx:add_channel("Y0", com_fx.BITE, 201, com_fx.READ) -- 对方 Y0 → M201
local port = com.setup(1, 9600, com.EVEN, 7, 1) -- FX 编程口常用 9600-7-E-1
port:add_device(fx)
port:set_time_lunxun(500)
port:start()
timer.start(1000, function()
print("对方 D0 =", plc.d(100), "在线:", st:get_num() == 0)
end)要点:
- FX 编程口是点对点协议,没有站号,所以
com_fx.creat不需要设备地址参数。 - 软元件名用字符串(
"D0"、"M50"、"Y0"),不是数字地址。 - 串口参数和 Modbus 不同,常见 9600 / 7 数据位 / 偶校验 / 1 停止位。
验证方法:
- 下载重启,D100 出现对方 D0 的值,D102-D103 出现 D10 浮点,
st:get_num()= 0(通讯成功)。 通信状态码:0=成功、1=报文异常、2=通信超时, 所以判在线要写st:get_num() == 0,不是== 1。 - 改变对方 PLC 的 D0,本机 D100 同步变化。
注意事项:
- X/Y 在三菱侧是八进制编号,写通道名按 PLC 面板丝印(如
Y0、Y10)。 - 接不上先核对串口参数(9600-7-E-1),这是 FX 编程口最常见的坑。
- FX 编程口一条串口上只能挂一台 FX PLC,不要建多个
com_fx子设备。
例程 2:本机写对方 M0(READ_WRITE 双向控制)
适用:同上 | 依赖模块:com_fx com data plc | 难度:★☆☆
场景:想用本机 M200 来控制对方 PLC 的 M0(比如远程启动电机)。用 READ_WRITE 通道,本机写 M200 会被自动下发,对方写 M0 也会被自动回读。注意 BITE 通道的落点是 M 区而不是 D 区。
完整代码(可直接下载运行):
lua
-- 本机 M200 置 1 时,轮询线程自动把对方 M0 置 1(READ_WRITE 双向)
local st = data.creat(0)
local fx = com_fx.creat(st)
fx:add_channel("M0", com_fx.BITE, 200, com_fx.READ_WRITE) -- 本机 M200 <-> 对方 M0
local port = com.setup(1, 9600, com.EVEN, 7, 1)
port:add_device(fx)
port:set_time_lunxun(500)
port:start()
-- 现场要动作:梯形图/上位机把 M200 置 1 即可,无需脚本再写逻辑要点:
com_fx只有READ和READ_WRITE两种,没有独立的WRITE常量;要下发就选READ_WRITE。- 双向通道既能下发也能回读,不必为「下发」和「上读」各建一条。
验证方法:
- 把本机 M200 置 1,用 GX Works 之类看对方 M0 = 1;复位 M200 后 M0 也复位。
- 在对方把 M0 手动置 1,本机 M200 应同步变 1(用
plc.m(200)读,plc.d(200)永远读不到)。
注意事项:
- 只配
READ的话,本机写 M200 不会下发到对方——要下发必须READ_WRITE。 - FX 编程口点对点,一条串口一台设备。
例程 3:把本机计算结果写到对方 D 区(下发设定值)
适用:同上 | 依赖模块:com_fx com data plc | 难度:★☆☆
场景:本机算好变频器频率给定放在 D300,希望自动下发到对方 D100。
完整代码(可直接下载运行):
lua
-- 本机 D300 = 频率给定,自动下发到对方 D100(双向,也能回读)
local st = data.creat(0)
local fx = com_fx.creat(st)
fx:add_channel("D100", com_fx.S16, 300, com_fx.READ_WRITE) -- 本机 D300 <-> 对方 D100
local port = com.setup(1, 9600, com.EVEN, 7, 1)
port:add_device(fx)
port:set_time_lunxun(500)
port:start()要点:
- 用
READ_WRITE既能下发(本机 D300 变 → 对方 D100 变)也能回读(对方改 D100 → 本机 D300 同步)。 - 脚本只需配置一次,下发由轮询线程自动完成。
验证方法:
- 把本机 D300 改成 50,用三菱软件读对方 D100 应同步成 50。
- 在对方直接改 D100,本机 D300 也跟着变(证明是双向)。
注意事项:
com_fx无独立WRITE,下发务必用READ_WRITE。- 串口参数错(非 9600-7-E-1)会导致完全通信不上。
例程 4:现场常用脚本 com_fx.lua(原文)
适用:同上 | 依赖模块:com_fx com data timer plc | 难度:★☆☆
场景:读对方 FX 的 D0/D1/D2,分别落到本机 D100、数据对象 d1、数据对象 d2, 10 秒打印一次,并把 d1 自增、d2 超 100 时回写 88。这份是三菱编程口最常用的入门脚本 (文件 com_fx.lua),当前固件上可直接运行,原文收录如下。
完整代码(com_fx.lua 原文,可直接下载运行):
lua
fx_state=data.creat(0);
fx_device=com_fx.creat(fx_state);--建立三菱编程口设备
fx_device:add_channel("D0",com_fx.S16,100,com_fx.READ_WRITE)--加入通道将设备D0与本机D100绑定
d1=data.creat(0)
d2=data.creat(0)
fx_device:add_channel("D1",com_fx.S16,d1,com_fx.READ_WRITE)--加入通道将D1与数据对象d1绑定
fx_device:add_channel("D2",com_fx.S16,d2,com_fx.READ_WRITE)--加入通道将D2与数据对象d2绑定
--建立父设备
com1=com.setup(1,9600,com.EVEN,7,1)--打开串口父设备,注意这里要与子设备通讯格式相同,比如ascii设备应该设定为7位数据
com1:add_device(fx_device)--在串口父设备中加入子设备
com1:start()--启动串口父设备
x=timer.start(10000,function()
--输出读取到的值
print("D0=".. plc.d(100).." D1=".. d1:get_num().." D2=".. d2:get_num().." state=".. fx_state:get_num())
--数据改变时会自动写入到设备
if d2:get_num()>100 then
d2:set(88)
end
d1:set(1+d1:get_num())--自动写入到D1
end)要点:
- 一份脚本把三种落点写法都给全了:填数字
100落本机D100(用plc.d(100)读); 填data对象(d1/d2)不占寄存器;com_fx.creat(fx_state)里的对象用来收通讯状态。 - 软元件名是字符串(
"D0"),与本机落点(第 3 个参数)不要弄反。 d1:set(...)写完不用手动下发,轮询线程检测到对象值变化自动写回对方D1。- 串口父设备参数必须是
9600, com.EVEN, 7, 1(编程口格式 7-E-1),写成 8 位数据就完全连不上。
验证方法:对方 PLC 里把 D0 改成 123,日志里 D0=123 跟随;看 D1= 每 10 秒 +1(证明反向写通); 把对方 D2 改成 150,下一轮应变回 88。
注意事项:fx_device 与 d1/d2 都要用全局或长期变量接住,被 GC 回收后对应通道就断了; fx_state:get_num() 一直非 0 说明链路或编程口格式不对;把 100 当成对方软元件号写会采错点。