主题
pipe — 统一管道通信
模块名:pipe
用一套统一的读写接口访问不同品牌 PLC 和仪表。pipe.setup 按协议名创建管道对象,之后不管对面是三菱、西门子还是 Modbus 从站,都用同样的 read_u16 / write_float 等方法收发数据。
本页是通用参考:管道对象的全部方法、返回值约定、错误码和数量限制都在这里。各协议的地址格式和差异见第 8 章后续小节。
ESP32 固件常用的协议
下面是 ESP32 机型内置、可直接 pipe.setup 的常用协议(固件持续增补,以您手上版本的补全提示为准):
| 协议名 | 第二参数类型 | 说明 |
|---|---|---|
"plc" | 无 | 访问本机 PLC 软元件 |
"modbus.tcp" | table | Modbus TCP 主站 |
"modbus.rtu" | table | Modbus RTU 主站(串口) |
"modbus.ascii" | table | Modbus ASCII 主站(串口) |
"fx" | table | 三菱 FX 编程口(串口或以太网) |
"s71200" | string | 西门子 S7-1200 |
"dlt645" | table | DL/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) | 2000 | 1968 |
16 位(s16/u16) | 125 | 123 |
32 位(s32/u32/float) | 62 | 61 |
双精度(double) | 31 | 30 |
pipe.setup
lua
pipe.setup(protocol, [config]) --> pipe | nil按协议名创建一个管道对象。第二个参数的类型按协议不同,有 table 和 string 两派,写错会创建失败。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | protocol | string | 是 | — | 协议名,见上面的协议表。"plc" 也可以直接传 plc 模块对象 |
| 2 | config | table | string | 视协议 | — | 连接参数,见下面各协议的字段表 |
返回值
| 类型 | 说明 |
|---|---|
| userdata | 管道对象。必须用变量长期持有,被 GC 回收会断开连接 |
| nil | 协议名不认识、当前机型不支持该协议、或参数不合法 |
modbus.tcp 的 config 字段
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
ip | string | 是 | — | 从站 IP |
port | integer | 否 | 502 | 端口 |
slave | integer | 否 | 1 | 从站单元号 |
format | string | 否 | "CDAB" | 32/64 位数据的寄存器与字节顺序,可取 ABCD/CDAB/BADC/DCBA(大小写均可) |
modbus.rtu / modbus.ascii 的 config 字段
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
com | integer | 否 | 1 | 串口号(数字),不是 "COM1" 这种字符串 |
baud | integer | 否 | 9600 | 波特率 |
data_bits | integer | 否 | 8 | 数据位 |
stop_bits | integer | 否 | 1 | 停止位 |
parity | string | 否 | 'N' | 校验:'N' 无、'E' 偶、'O' 奇 |
slave | integer | 否 | 1 | 从站地址 |
format | string | 否 | "CDAB" | 同上 |
fx 的 config 字段
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
com | integer | 否 | 1 | 串口号。串口方式用这个 |
baud | integer | 否 | 9600 | 波特率。帧格式固定 7 数据位 + 偶校验 + 2 停止位,不可配 |
tcp | string | 否 | — | 对方 IP。给了它就走以太网方式,端口固定 5551 |
dlt645 的 config 字段(注意字段名是大写)
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
COM | integer | 否 | 1 | 串口号 |
BAUDRATE | integer | 否 | 2400 | 波特率。帧格式固定 7 数据位 + 偶校验 + 1 停止位 |
ADDRESS | string | 否 | "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 }读取位(线圈 / 离散输入)。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | address | string | 是 | — | 地址字符串,格式随协议而定(Modbus 用 "00001" / "10001") |
| 2 | count | integer | 否 | 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])
endp:write_bite
lua
p:write_bite(address, value) --> errcode写位。value 可以是单个布尔/数字,也可以是数组一次写多个。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | address | string | 是 | — | 位地址(Modbus 只能写线圈区 0xxxx) |
| 2 | value | boolean | 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 位有符号整数。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | address | string | 是 | — | 寄存器地址 |
| 2 | count | integer | 否 | 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]) endp:write_s16
lua
p:write_s16(address, value) --> errcode写 16 位有符号整数。value 为单个数字或数组(上限 123)。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | address | string | 是 | — | 寄存器地址(Modbus 只能写 0xxxx 或 4xxxx) |
| 2 | value | number | 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("写入成功") endp: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]) endp: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]。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | address | string | 是 | — | 起始寄存器地址 |
| 2 | count | integer | 否 | 8 | 寄存器个数(默认 8 个 = 16 字节) |
lua
local ret = mb:read_string("40001", 10) -- 读 20 字节
if ret.errno == 0 then print(ret.data[1]) endp: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 用它改从站号和字节序。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | params | table | 是 | — | 要修改的字段,如 { 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,不在主线程阻塞。
验证方法:
- 下载重启,本机 D100~D109 出现从站 40001~40010 的值。
- 拔从站网线,
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" 协议主要用于"统一写法"场景。