Skip to content

综合例程:串口透传(本地双串口 / 远程 / 网口) ​

「透传」就是让本设备的串口变成一根透明的线:数据不解析、不加工,从 A 口进来就从 B 口出去。 典型用途是给手上没有接口的那台设备编程 —— 触摸屏、上位机软件、下载线都接不到现场那台仪表, 就借用本机设备的串口把它"搬"过来。 三种透传用的是同一对 set_transparent 接口,区别只在两端各是什么,见第一节。


一、三种透传怎么选 ​

想要的效果怎么接调什么
一台 485 总线上,触摸屏和 PLC 采集共存串口 1 接从站、串口 2 接触摸屏com1:set_transparent(com2)(带参数)
人在外地,用上位机工具给现场设备编程串口接从站,走云com1:set_transparent()(不带参数)
现场有网络,远端 PC 直接跟串口设备说话串口接从站 + 一条 TCP 连接com1:set_transparent() 并且 tcp:set_transparent()

核心接口只有两个,都在本手册里已有完整说明:

透传是「两边登记」的事

除了串口到串口(一个调用就描述了两端),另外两种都需要成对登记: 云透传要靠上位机工具侧发起,网口透传必须 TCP 侧和串口侧各调一次。 只设一边,现象就是"一边通、一边没反应",而且不报任何错。


二、透传一:串口 ↔ 串口(本地共享一条总线) ​

场景:COM1 上挂着一台 Modbus 仪表,PLC 要周期性读它;现场还有一台触摸屏也要读同一台仪表, 但触摸屏只有 COM2。把 COM2 设为 COM1 的透传口,触摸屏就"仿佛"直接接在仪表上。

完整代码(可直接下载运行):

lua
-- ============================================================
-- 串口 1 正常采集 + 串口 2 透传到串口 1
-- 触摸屏接串口 2,实际访问的是挂在串口 1 上的设备
-- ============================================================

-- ---------- 1. 串口 1 上的正常采集业务 ----------
local modbus_state = data.creat(0)                             -- 通信状态
local meter = com_modbus.creat(com_modbus.RTU, 2, modbus_state)
meter:set_float_abcd(com_modbus.ABCD)                          -- 浮点字序按仪表手册
meter:set_time_recv(100)                                       -- 单次应答等 100ms
meter:set_time_error(1000)                                     -- 出错后 1 秒恢复
meter:add_channel(400001, com_modbus.FLOAT, 100, com_modbus.READ)  -- 400001 -> D100/D101

local serial_slave = com.setup(1, 9600, com.NONE, 8, 1)
if not serial_slave then
    print("串口 1 打开失败")
    return
end
serial_slave:add_device(meter)

-- ---------- 2. 串口 2 作为透传口 ----------
local serial_master = com.setup(2, 9600, com.NONE, 8, 1)       -- 参数与触摸屏一致
if not serial_master then
    print("串口 2 打开失败")
    return
end

-- ---------- 3. 建立透传关系后再启动 ----------
serial_slave:set_transparent(serial_master)                    -- 串口 1 <-> 串口 2
serial_slave:set_time_lunxun(100)                              -- 采集轮询 100ms 一轮
serial_slave:start()

逐段要点:

  1. set_transparent 要写在 start 之前:轮询线程一起来就开始发包, 透传关系晚建一步,触摸屏的头几帧就丢了。
  2. 两个串口的通讯格式必须和挂在总线上的设备一致(波特率、校验、数据位)。 触摸屏是 9600-8-N-1,串口 2 就得填 9600-8-N-1。
  3. 透传是双向原样搬运,固件不改一个字节,所以触摸屏的协议它不需要懂。
  4. 轮询间隔要留出总线空隙:set_time_lunxun 太小(比如 1ms)时, 本机采集和触摸屏的请求会挤在同一段总线上冲突,表现为两边都偶发超时。
  5. 采集业务和透传并存时,串口 1 上不要再用脚本手写 send / recv, 见串口裸报文例程。

验证方法:

  1. 下载运行,触摸屏上组态读仪表的寄存器,画面数值应正常刷新。
  2. 同时看设备日志中 modbus_state 为 0(本机采集也正常)—— 两边同时通才算透传成功,只有一边通说明参数或顺序有问题。
  3. 把 set_transparent 那行注释掉再下载:触摸屏立刻读不到,本机采集照常 —— 这样能确认透传关系确实是由这一行建立的。

三、透传二:串口 → 云(远程给现场设备编程) ​

场景:设备装在客户现场,工程师在本地要用 GX Works / 编程软件连它下面的 PLC。 把设备的串口设为云透传口,上位机工具选「远程透传」即可打通。

完整代码(可直接下载运行):

lua
-- ============================================================
-- 云透传:把串口 1 登记为远程透传口
-- 上位机工具与 PLC 需登录同一账户;局域网内也可直接透传
-- ============================================================
local serial_slave = com.setup(1, 9600, com.NONE, 8, 1)        -- 参数按现场设备
if not serial_slave then
    print("串口打开失败")
    return
end
serial_slave:set_transparent()                                 -- 不传参数 = 设为云透传口
serial_slave:set_timeout(1000)                                 -- 远程链路延迟大,接收窗口放宽

逐段要点:

  1. 不带参数的 set_transparent 会自动启动轮询线程,不需要再调 com.start()。
  2. set_timeout 在这里很重要:远程链路的往返比本地慢得多, 默认 100ms 会把对方的应答判成"没收到"。给到 1000ms 起步。
  3. 上位机工具侧的操作(选菜单、挑端口、波特率要对齐)见 远程串口透传; 跟着做一遍的图文流程见 串口透传给第三方设备编程。
  4. 这台设备的串口在透传期间就交给远程了,本地触摸屏/脚本不要同时抢它。

验证方法:

  1. 现场设备上电、云服务已连(D8005.F 为 ON,见 特殊软元件详细说明)。
  2. 本地打开上位机工具 → 远程透传 → 选到这台设备 → 打开本地串口。
  3. 用厂商编程软件(如 GX Works)通过该串口连现场 PLC,能上线下载即成功。
  4. 现场侧观察:编程软件每次读写,串口对应指示灯/日志有报文往来。

注意事项:

  • 忘了 set_timeout:表现为"连得上、一读写就超时"。
  • 上位机工具和 PLC 不是同一账户:找不到设备。
  • 现场那一路串口同时还挂着采集脚本:两边抢总线。

四、透传三:串口 ↔ TCP(网口搬到远端) ​

场景:现场有以太网,远端 PC 上跑一个自己的软件,要直接跟挂在串口上的仪表说话。

完整代码(可直接下载运行):

lua
-- ============================================================
-- 串口 1 <-> TCP 双向透传
-- 关键:TCP 侧和串口侧各登记一次,两边都不能省
-- ============================================================
local serial_slave = com.setup(1, 9600, com.NONE, 8, 1)
if not serial_slave then
    print("串口打开失败")
    return
end

tcpc = tcp_client.creat("192.168.0.238", 6000, plc.eth_ip())   -- 第 3 参指定走以太网口
tcpc:reg(tcp_client.CONNECTED, function()
    print("透传通道已建立")
end)
tcpc:reg(tcp_client.DISCONNECTED, function()
    print("透传通道断开,内部会自动重连")
end)

tcpc:start()
tcpc:set_transparent()                                         -- ① TCP 侧登记
serial_slave:set_transparent()                                 -- ② 串口侧登记(不传参数)

-- 可选:把连接状态镜像到 M,给画面当指示灯
timer.start(2000, function()
    plc.m(300, tcpc:is_linked() == 1 and 1 or 0)
end)

逐段要点:

  1. 两侧都要 set_transparent,顺序无所谓,缺一个就不通。 串口侧这里不传参数 —— 表示"这个串口参与透传",对端由 TCP 侧那条连接决定。
  2. tcp_client.creat(ip, port, ip_self) 的第 3 参 plc.eth_ip() 用来指定出口网卡。 WiFi + 以太网同时在线的机型上不写这一参,连接可能从 WiFi 出去,走不到您要的网段。
  3. 不要为了"等网口就绪"写一个循环卡在最前面(这是本节要重点纠正的写法,见第五节)。 tcp_client:start() 自带自动重连,网口还没起来时它自己会重试, 脚本照常往下走完、进入事件循环即可。
  4. 一旦登记为透传,不要在脚本里再对同一串口 send / recv,也不用注册 RECVDATA, 数据由固件直接搬运。
  5. is_linked() 返回整数,判连接必须写 == 1。

五、错误写法 vs 正确写法对照 ​

下面这段是真实客户脚本的形态,它不报错,但会把设备卡住:

lua
-- 错误:用阻塞循环等待网口就绪
while (plc.bite(plc.ds(5), 6) == 0) do
    system.sleep(1000)
    print("正在等待网口就绪")
end
client = tcp_client.creat("192.168.0.238", 6000, plc.eth_ip())
client:set_transparent()
问题后果正确做法
脚本顶层的 while + system.sleep顶层就是主线程,sleep 在这里是真阻塞、不泵消息队列:循环期间所有回调(串口、MQTT、梯形图交互、其它定时器)全都冻住,只有这一行 print 在动删掉等待循环:tcp_client 的 start 内部会自动重连,不需要您等
网线没插时这个循环永远出不来设备看起来"在跑",实际整台机器被一个 while 锁死;只能重新下载脚本状态检查放定时器里,当作观测而不是门槛
只调了 client:set_transparent()只有 TCP 一侧登记了,串口侧不知道,表现为"一边通、一边没反应"补上 serial:set_transparent()
用 plc.ds(5) 却不说它是 D8005plc.ds(n) 是 D8000 起的偏移,plc.ds(5) → D8005;写成人看不懂的 5 极容易被后面的人改错加注释写明是 D8005,或直接引用位定义表

改成事件驱动,等待这件事就消失了:

lua
-- 正确:不等,直接建对象;网口好不好用定时器去"看"
local serial_slave = com.setup(1, 9600, com.NONE, 8, 1)
if not serial_slave then return end

tcpc = tcp_client.creat("192.168.0.238", 6000, plc.eth_ip())
tcpc:reg(tcp_client.CONNECTED, function() print("透传通道已建立") end)
tcpc:start()
tcpc:set_transparent()
serial_slave:set_transparent()

-- 只做观测:网线没插就在日志里提示一句,不拦任何流程
timer.start(5000, function()
    if plc.bite(plc.ds(5), 6) == 0 then                 -- D8005.6 = 网线已连接
        print("提示:D8005.6 未置位,检查网线")
    end
end)

六、注意事项汇总 ​

  • set_time_lunxun(0) 不会变成 0:小于 1 的值被强制按 1ms 处理。 轮询压到 1ms 会让总线寸步难让,透传对端几乎必然超时 —— 这不是"最快",是"最慢"。
  • 透传期间同一串口不要混用其它用法:挂了子设备轮询、又手写 send、又开透传, 三者会抢同一条总线,现象是随机丢包,很难查。
  • 透传不做任何协议转换:它只搬字节。远端软件必须自己按现场的通讯格式(含波特率、校验)收发。
  • 云透传要求云服务在线:D8005.F(bit F)没置位就先查云连接,不要在串口上找原因。
  • 多网卡机型要显式指定出口:tcp_client.creat 的第 3 参不填, 连接可能从另一个网卡出去,到达不了目标网段。
  • 参数对不上时不要怀疑透传:串口两侧的设备如果波特率/校验不一致, 透传只会忠实地把乱码搬到对面 —— 先各自单独验证设备通讯正常,再开透传。
  • 能免脚本就免脚本:只是要远程编程,用上位机工具的远程透传配置即可, 不必专门下载一段 Lua,见远程串口透传。

七、相关文档 ​

LPPLC 工业物联网云平台 · 软硬件与云平台全栈自研