Skip to content

pipe — 统一管道通信 ​

模块名:pipe

用一套统一的读写接口访问不同品牌 PLC 和仪表。pipe.setup 按协议名创建管道对象,之后不管对面是三菱、西门子还是 Modbus 从站,都用同样的 read_u16 / write_float 等方法收发数据。

本页是通用参考:管道对象的全部方法、返回值约定、错误码和数量限制都在这里。各协议的地址格式和差异见第 8 章后续小节。

Pipe 统一接口架构与协议分类

ESP32 固件常用的协议 ​

下面是 ESP32 机型内置、可直接 pipe.setup 的常用协议(固件持续增补,以您手上版本的补全提示为准):

协议名第二参数类型说明
"plc"无访问本机 PLC 软元件
"modbus.tcp"tableModbus TCP 主站
"modbus.rtu"tableModbus RTU 主站(串口)
"modbus.ascii"tableModbus ASCII 主站(串口)
"fx"table三菱 FX 编程口(串口或以太网)
"s71200"string西门子 S7-1200
"dlt645"tableDL/T 645 电表(只读)

平台差异:三菱 MC、三菱 A 系列、欧姆龙 FINS、西门子 SMART/S7-200/S7-300、台达、信捷、汇川等协议只在上位机版本和 Linux 网关版本里提供,ESP32 机型暂不支持。在 ESP32 上对这些协议调用 pipe.setup 会返回 nil——是否支持某个协议,最稳妥的办法是在编辑器补全里查一下,或直接试调 pipe.setup 看返回值。

函数总览 ​

库级函数 ​

函数作用
pipe.setup按协议名创建管道对象

对象方法:位读写 ​

方法作用
p:read_bite读位
p:write_bite写位

对象方法:整数读写 ​

方法作用
p:read_s16 / p:read_u16读 16 位有符号 / 无符号
p:write_s16 / p:write_u16写 16 位有符号 / 无符号
p:read_s32 / p:read_u32读 32 位有符号 / 无符号
p:write_s32 / p:write_u32写 32 位有符号 / 无符号

对象方法:浮点与字符串 ​

方法作用
p:read_float / p:write_float读写单精度浮点
p:read_double / p:write_double读写双精度浮点
p:read_string / p:write_string读写字符串

对象方法:参数存取 ​

方法作用
p:read读管道自身参数(各协议含义不同)
p:write改管道自身参数或执行协议命令

返回值约定 ​

读方法返回一个 table:

lua
{ errno = 0, data = { 值1, 值2, ... } }

errno 为 0 表示成功,值从 data[1] 开始。出错时 data 里可能带 request / response 的十六进制报文,便于排查。

写方法返回一个整数错误码,不是 table:

lua
local err = p:write_u16("40001", 1234)
if err == 0 then print("写入成功") end

此处容易出错:写方法不能写成 if result.errno == 0。

错误码 ​

值含义
0成功
1007连接错误(对方不可达、端口不通、串口未打开)
5555该协议不支持此操作
8002参数错误(地址非法、数量超限、区域不允许该操作)
8003功能不支持
8105发送错误
8109接收或解析错误

数量限制 ​

一次读写的点数上限(超限返回 8002):

数据类型读上限写上限
位(bite)20001968
16 位(s16/u16)125123
32 位(s32/u32/float)6261
双精度(double)3130

pipe.setup ​

lua
pipe.setup(protocol, [config]) --> pipe | nil

按协议名创建一个管道对象。第二个参数的类型按协议不同,有 table 和 string 两派,写错会创建失败。

参数

#参数类型必填默认值说明
1protocolstring是—协议名,见上面的协议表。"plc" 也可以直接传 plc 模块对象
2configtable | string视协议—连接参数,见下面各协议的字段表

返回值

类型说明
userdata管道对象。必须用变量长期持有,被 GC 回收会断开连接
nil协议名不认识、当前机型不支持该协议、或参数不合法

modbus.tcp 的 config 字段

字段类型必填默认值说明
ipstring是—从站 IP
portinteger否502端口
slaveinteger否1从站单元号
formatstring否"CDAB"32/64 位数据的寄存器与字节顺序,可取 ABCD/CDAB/BADC/DCBA(大小写均可)

modbus.rtu / modbus.ascii 的 config 字段

字段类型必填默认值说明
cominteger否1串口号(数字),不是 "COM1" 这种字符串
baudinteger否9600波特率
data_bitsinteger否8数据位
stop_bitsinteger否1停止位
paritystring否'N'校验:'N' 无、'E' 偶、'O' 奇
slaveinteger否1从站地址
formatstring否"CDAB"同上

fx 的 config 字段

字段类型必填默认值说明
cominteger否1串口号。串口方式用这个
baudinteger否9600波特率。帧格式固定 7 数据位 + 偶校验 + 2 停止位,不可配
tcpstring否—对方 IP。给了它就走以太网方式,端口固定 5551

dlt645 的 config 字段(注意字段名是大写)

字段类型必填默认值说明
COMinteger否1串口号
BAUDRATEinteger否2400波特率。帧格式固定 7 数据位 + 偶校验 + 1 停止位
ADDRESSstring否"999999999999"表号,默认值是通配地址

s71200 的 config

字符串形式:"ip" 或 "ip:port"。默认端口 102,rack 0,slot 1。

示例

lua
-- Modbus TCP
mb = pipe.setup("modbus.tcp", { ip = "192.168.1.100", port = 502, slave = 1, format = "CDAB" })

-- Modbus RTU(串口 1,9600,8N1,从站 1)
mbr = pipe.setup("modbus.rtu", { com = 1, baud = 9600, slave = 1 })

-- 三菱 FX,以太网
fx = pipe.setup("fx", { tcp = "192.168.1.50" })

-- 西门子 S7-1200(字符串参数)
s7 = pipe.setup("s71200", "192.168.1.10:102")

-- 本机 PLC
lp = pipe.setup("plc")

if not mb then print("管道创建失败") end

说明

  • 返回 nil 时先确认三件事:协议名拼写、第二参数类型(table 还是 string)、这个协议在当前固件里是否编译。
  • 串口类协议的字段名是 com(数字)和 baud,写成 port="COM1" / baudrate=9600 会被静默忽略并落回默认值。
  • 32 位数据读出的数值明显偏离量程时,通常是 format 未设置正确。多数国产设备使用 CDAB。

p:read_bite ​

lua
p:read_bite(address, [count]) --> { errno, data }

读取位(线圈 / 离散输入)。

参数

#参数类型必填默认值说明
1addressstring是—地址字符串,格式随协议而定(Modbus 用 "00001" / "10001")
2countinteger否1读取位数,上限 2000

返回值

类型说明
table{ errno=0, data={0/1, ...} },值从 data[1] 开始
lua
local ret = mb:read_bite("00001", 8)
if ret.errno == 0 then
  print(ret.data[1])
end

p:write_bite ​

lua
p:write_bite(address, value) --> errcode

写位。value 可以是单个布尔/数字,也可以是数组一次写多个。

参数

#参数类型必填默认值说明
1addressstring是—位地址(Modbus 只能写线圈区 0xxxx)
2valueboolean | number | table是—单个值或数组,数组上限 1968

返回值

类型说明
integer错误码,0 成功
lua
local err = mb:write_bite("00001", true)
local err2 = mb:write_bite("00001", { 1, 0, 1, 1 })

p:read_s16 ​

lua
p:read_s16(address, [count]) --> { errno, data }

读 16 位有符号整数。

参数

#参数类型必填默认值说明
1addressstring是—寄存器地址
2countinteger否1数量,上限 125

返回值

类型说明
table{ errno=0, data={...} }
lua
local ret = mb:read_s16("40001", 2)

p:read_u16 ​

lua
p:read_u16(address, [count]) --> { errno, data }

读 16 位无符号整数。参数与返回值同 p:read_s16,count 上限 125。

lua
local ret = mb:read_u16("40001")
if ret.errno == 0 then print(ret.data[1]) end

p:write_s16 ​

lua
p:write_s16(address, value) --> errcode

写 16 位有符号整数。value 为单个数字或数组(上限 123)。

参数

#参数类型必填默认值说明
1addressstring是—寄存器地址(Modbus 只能写 0xxxx 或 4xxxx)
2valuenumber | table是—单个值或数组

返回值

类型说明
integer错误码,0 成功
lua
local err = mb:write_s16("40001", -1234)
local err2 = mb:write_s16("40001", { 10, 20, 30 })

p:write_u16 ​

lua
p:write_u16(address, value) --> errcode

写 16 位无符号整数。参数与返回值同 p:write_s16。

lua
local err = mb:write_u16("40001", 1234)
if err == 0 then print("写入成功") end

p:read_s32 ​

lua
p:read_s32(address, [count]) --> { errno, data }

读 32 位有符号整数。每个值占 2 个寄存器,count 上限 62,字节序由创建时的 format 决定。

lua
local ret = mb:read_s32("40001", 1)

p:read_u32 ​

lua
p:read_u32(address, [count]) --> { errno, data }

读 32 位无符号整数。规则同 p:read_s32。

p:write_s32 ​

lua
p:write_s32(address, value) --> errcode

写 32 位有符号整数。value 单个或数组(数组上限 61)。

lua
local err = mb:write_s32("40001", 100000)

p:write_u32 ​

lua
p:write_u32(address, value) --> errcode

写 32 位无符号整数。规则同 p:write_s32。

p:read_float ​

lua
p:read_float(address, [count]) --> { errno, data }

读单精度浮点。每个值占 2 个寄存器,count 上限 62,字节序由 format 决定。

lua
local ret = mb:read_float("40001")
if ret.errno == 0 then print(ret.data[1]) end

p:write_float ​

lua
p:write_float(address, value) --> errcode

写单精度浮点。value 单个或数组(上限 61)。

lua
local err = mb:write_float("40001", 3.14)

p:read_double ​

lua
p:read_double(address, [count]) --> { errno, data }

读双精度浮点。每个值占 4 个寄存器,count 上限 31。

p:write_double ​

lua
p:write_double(address, value) --> errcode

写双精度浮点。value 单个或数组(上限 30)。

lua
local err = mb:write_double("40001", 3.1415926535)

p:read_string ​

lua
p:read_string(address, [count]) --> { errno, data }

读字符串。count 是寄存器个数(每个寄存器 2 字节),字符串在 data[1]。

参数

#参数类型必填默认值说明
1addressstring是—起始寄存器地址
2countinteger否8寄存器个数(默认 8 个 = 16 字节)
lua
local ret = mb:read_string("40001", 10)  -- 读 20 字节
if ret.errno == 0 then print(ret.data[1]) end

p:write_string ​

lua
p:write_string(address, value) --> errcode

写字符串(Modbus 只能写保持寄存器区 4xxxx)。

lua
local err = mb:write_string("40001", "HELLO")

p:read ​

lua
p:read() --> table

读管道自身参数,不是设备数据。返回内容各协议不同。Modbus 返回 { slave, format }。

lua
local params = mb:read()
print("从站:", params.slave, "字节序:", params.format)

说明 这个方法读的是管道对象的配置,不要与 p:read_u16 之类的设备读取混淆。

p:write ​

lua
p:write(params) --> errcode

改管道自身参数或执行协议命令。Modbus 用它改从站号和字节序。

参数

#参数类型必填默认值说明
1paramstable是—要修改的字段,如 { slave=2, format="ABCD" }

返回值

类型说明
integer错误码,0 成功
lua
local err = mb:write({ slave = 2, format = "ABCD" })

常见用法 ​

周期性轮询一批寄存器

lua
mb = pipe.setup("modbus.tcp", { ip = "192.168.1.100", slave = 1, format = "CDAB" })

function poll()
  local ret = mb:read_u16("40001", 10)
  if ret.errno == 0 then
    for i = 1, 10 do
      plc.d(i, ret.data[i])   -- 存进本机 D 寄存器
    end
  else
    print("读取失败 errno=", ret.errno)
  end
end

-- 每 500ms 轮询一次(timer.start 创建即启动,没有 timer.creat)
t = timer.start(500, poll)

读一个浮点温度并做判断

lua
local ret = mb:read_float("40010")
if ret.errno == 0 and ret.data[1] > 80.0 then
  mb:write_bite("00001", true)   -- 超温报警
end

注意事项 ​

  • 管道对象必须长期持有。存进局部变量、函数返回后被 GC 回收,连接就断了。用全局变量或模块级变量。
  • 读方法判 ret.errno,写方法判返回的整数。两者返回结构不同,是最常见的错误来源。
  • 32/64 位数值不对先查 format。国产设备多为 CDAB。
  • 地址区域和方法要匹配。Modbus 位操作只能用在 0xxxx/1xxxx,字操作用在 3xxxx(只读)/4xxxx;区域不匹配返回 8002。
  • 单次读写不能超过上限,超限直接返回 8002,不会自动分帧。

例程 ​

每个例程都是可直接下载运行的完整脚本。先把 pipe.setup 的对象长期持有,再用统一的 read_* / write_* 存取,脚本注册完即结束。统一模板「场景 / 完整代码 / 要点 / 验证方法 / 注意事项」。

例程 1:周期性轮询 Modbus TCP 一批寄存器到本机 D 区 ​

适用:LP3201 / LP3202 / LP3203 / LP3222 / LP3232 / LP3288 / 软PLC模拟器 / 上位机 | 依赖模块:pipe timer plc | 难度:★☆☆

场景:一台 Modbus TCP 从站(192.168.1.100:502,单元号 1,浮点 CDAB),每 500ms 把保持寄存器 40001 起的 10 个字轮询到本机 D100~D109。

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

lua
mb = pipe.setup("modbus.tcp", { ip = "192.168.1.100", slave = 1, format = "CDAB" })
if not mb then
    print("管道创建失败(协议不支持或参数错)")
    return
end

function poll()
    local ret = mb:read_u16("40001", 10)
    if ret.errno == 0 then
        for i = 1, 10 do
            plc.d(i, ret.data[i])   -- 存进本机 D 寄存器
        end
    else
        print("读取失败 errno=", ret.errno)
    end
end

t = timer.start(500, poll)   -- timer.start 创建即启动,没有 timer.creat

要点:

  • pipe.setup 失败返回 nil,必须判空(ESP32 机型对部分协议不支持也会返回 nil)。
  • 读方法返回 { errno=0, data={...} },值从 data[1] 开始;用 ret.errno == 0 判断成功。
  • 周期轮询用 timer.start,不在主线程阻塞。

验证方法:

  1. 下载重启,本机 D100~D109 出现从站 40001~40010 的值。
  2. 拔从站网线,errno 变非 0 并打印"读取失败"。

注意事项:

  • 写方法的返回值不是 table,是单个整数错误码——local err = mb:write_u16("40001", 1234) 要判 err == 0,写成 if result.errno == 0 永远为真(整数没有 errno 字段)。
  • 32/64 位数值不对先查 format;国产设备多为 CDAB。
  • 地址区域和方法要匹配:位操作只能 0xxxx/1xxxx,字操作 3xxxx(只读)/4xxxx,区域错返回 8002。
  • 单次上限:16 位读 125 / 写 123,超限直接 8002。

例程 2:读浮点温度 + 超温报警(读返回 table 与写返回整数) ​

适用:同上 | 依赖模块:pipe plc | 难度:★☆☆

场景:读从站 40010 的单精度浮点温度,超过 80℃ 时把线圈 00001 置 1。

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

lua
local mb = pipe.setup("modbus.tcp", { ip = "192.168.1.100", format = "CDAB" })
if not mb then return end

local ret = mb:read_float("40010")
if ret.errno == 0 then
    print("温度 =", ret.data[1])
    if ret.data[1] > 80.0 then
        local err = mb:write_bite("00001", true)   -- 写方法返回整数错误码
        if err == 0 then print("已触发超温报警") end
    end
end

要点:

  • 读浮点:read_float("40010") → ret.data[1] 是数值;字节序由 setup 的 format 决定。
  • 写位:write_bite("00001", true) 返回 整数 0 表示成功;value 可以是布尔/数字/数组。

验证方法:从站 40010 温度 > 80 时,线圈 00001 被置位(可用 Modbus 工具确认)。

注意事项:把读/写两套返回值搞混是最常见错误——读 = table 看 errno;写 = 整数看返回码。


例程 3:读写本机 PLC 软元件("plc" 协议) ​

适用:同上 | 依赖模块:pipe | 难度:★☆☆

场景:脚本内部想用统一的 pipe 接口读写本机软元件(和读写外部设备写法一致)。

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

lua
local lp = pipe.setup("plc")   -- 第二参数省略
if not lp then return end

local r = lp:read_u16("D100", 2)
if r.errno == 0 then
    print("D100=", r.data[1], "D101=", r.data[2])
end
local err = lp:write_u16("D200", 1234)
print("写 D200:", err == 0 and "成功" or "失败")

要点:"plc" 协议无需配置参数;地址用 "D100" 之类。

验证方法:本机 D100/D101 有值则读出;改 D200 看写结果。

注意事项:本机读写其实直接用 plc.d() / plc.y() 更高效;pipe 的 "plc" 协议主要用于"统一写法"场景。

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