主题
modbus_tcp_server — Modbus TCP 从站服务器
模块名:modbus_tcp_server
让本机变成一台 Modbus TCP 从站,上位机 / 组态 / SCADA / 触摸屏可以直接读写本机 PLC 的软元件。协议解析和寄存器映射都在固件里做完了,脚本只需要 start 一下,不用注册任何回调。
函数总览
库级函数
| 函数 | 作用 |
|---|---|
modbus_tcp_server.start([port, max_sockets, buffer_size]) | 启动服务器 |
对象上只有 __gc,没有其它方法。被回收时自动停止服务。
modbus_tcp_server.start
lua
modbus_tcp_server.start([port, max_sockets, buffer_size]) --> server启动 Modbus TCP 从站并立即开始监听。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | port | integer | 否 | 502 | 监听端口 |
| 2 | max_sockets | integer | 否 | 1 | 最大并发主站连接数 |
| 3 | buffer_size | integer | 否 | 1024 | 每连接的收发缓冲区字节数 |
返回值
| 类型 | 说明 |
|---|---|
| userdata | 服务器对象。必须用全局变量接住,被 GC 回收会立刻停止服务 |
示例
lua
-- 默认 502 端口,允许 4 个主站同时连
server = modbus_tcp_server.start(502, 4, 1024)说明
- 没有停止方法。要停就让持有它的变量置
nil并等 GC,或重启脚本。 - 没有任何身份认证:任何能连到这个端口的客户端都可以读写本机 PLC 寄存器,包括写线圈、改 D 区。不要把它直接暴露在公网上,放在内网或用防火墙 / VPN 限制来源。
- 不校验从站单元号:报文里的 unit id 填任何值都会被响应,上位机侧填 1 即可。
支持的功能码
| 功能码 | 作用 |
|---|---|
| 01 | 读线圈 |
| 02 | 读离散输入 |
| 03 | 读保持寄存器(一次最多 125 个) |
| 04 | 读输入寄存器(一次最多 255 个) |
| 05 | 写单个线圈 |
| 06 | 写单个保持寄存器 |
| 15 (0x0F) | 写多个线圈 |
| 16 (0x10) | 写多个保持寄存器 |
| 17 (0x11) | 报告从站 ID |
| 23 (0x17) | 读写多个寄存器 |
其它功能码返回「非法功能码」异常。
地址映射表
表中的地址是协议线上的 0 基地址。按 5 位习惯写法(40001 = 保持寄存器 0)时,自己加 1。
保持寄存器 4x(功能码 03 / 06 / 16)
| 协议地址 | 对应软元件 |
|---|---|
| 0 – 7999 | D0 – D7999 |
| 8000 – 8255 | D8000 – D8255(特殊寄存器) |
| 8256 – 8511 | T0 – T255 当前值 |
| 8512 – 8711 | C0 – C199 当前值 |
| 8712 – 8823 | C200 – C255 当前值(32 位,每个占 2 个寄存器) |
| 9000 – 9099 | AO0 – AO99(模拟量输出) |
| 9100 – 9191 | AI0 – AI91(模拟量输入) |
| 其它 | 一律落到 D8030,不报错 |
输入寄存器 3x(功能码 04)
| 协议地址 | 对应软元件 |
|---|---|
| 0 – 255 | AI0 – AI255 |
| 其它 | 落到 AI0 |
线圈 0x(功能码 01 / 05 / 15)
| 协议地址 | 对应软元件 |
|---|---|
| 0 – 255 | Y 输出,第 0 – 255 点 |
| 256 – 511 | T0 – T255 触点(定时器到时位) |
| 512 – 767 | C0 – C255 触点(计数器到达位) |
| 1000 – 4071 | M0 – M3071 |
| 4072 – 4327 | M8000 – M8255(特殊继电器) |
| 5000 – 5999 | S0 – S999 |
| 8000 – 8255 | X 输入,第 0 – 255 点 |
| 其它 | 落到 M8255,不报错 |
离散输入 1x(功能码 02)
| 协议地址 | 对应软元件 |
|---|---|
| 0 – 255 | X 输入,第 0 – 255 点 |
| 其它 | 落到 X 第 0 点 |
说明
- X / Y 是按「点序号」映射,不是按八进制编号。PLC 里 X/Y 编号是八进制(X0…X7, X10…X17),映射时按顺序数:协议地址
8000= 第 0 点 =X0,8008= 第 8 点 =X10(八进制)。算错一位就读到相邻的点。 - 越界地址不报错,会静默落到 D8030 / M8255 / AI0。上位机读出恒定的怪值时先核对地址范围。
常见用法
给组态软件开一个 Modbus TCP 口
lua
server = modbus_tcp_server.start(502, 4)组态侧配置:协议 Modbus TCP、IP 填本机、端口 502、从站号 1,然后按上面的映射表填地址。读 D100 就填保持寄存器 40101(0 基地址 100)。
换个非标端口,避免和其它服务冲突
lua
server = modbus_tcp_server.start(1502, 2)注意事项
- 必须用全局变量持有返回值。
local server = ...在脚本主体里出作用域后服务就停了。 - 无认证、无加密。只在可信网络里开放,或用防火墙限制源 IP。
- 越界地址静默重定向,不会返回异常码,排查时优先核对映射表。
- X/Y 按点序号映射,八进制编号要自己换算。
- 同时想以主站身份采集别人的数据,用
modbus或pipe,两者可以并存。
例程
本模块只有
start一个入口,没有回调、没有其它方法。脚本start并全局持有返回对象即可,软元件映射由固件完成。统一模板「场景 / 完整代码 / 要点 / 验证方法 / 注意事项」。
例程 1:给组态软件开一个 Modbus TCP 口
适用:LP3201 / LP3202 / LP3203 / LP3222 / LP3232 / LP3288 / 软PLC模拟器 / 上位机 | 依赖模块:modbus_tcp_server | 难度:★☆☆
场景:上位机/组态/SCADA 想用标准 Modbus TCP 直接读写本机 PLC 的 D/Y/M。
完整代码(可直接下载运行):
lua
server = modbus_tcp_server.start(502, 4) -- 默认 502,最多 4 个主站同时连要点:
- 只有
start一个入口,无需回调;返回对象必须用全局变量接住。 - 组态侧:协议 Modbus TCP、IP 填本机、端口 502、从站号填 1。
- 读 D100 填保持寄存器
40101(协议线 0 基地址 100;5 位写法40001=0 基地址 0)。
验证方法:
- 用 Modbus 主站工具连
IP:502,读保持寄存器 100,应拿到 D100 的值。 - 写 D100 对应的寄存器,本机 D100 同步变化。
注意事项:
- 越界地址静默重定向、不报错:不在映射表的地址会落到
D8030/M8255/AI0,读出恒定怪值时先核对映射表。 - X/Y 按「点序号」映射,不是八进制编号:协议地址
8000=第 0 点=X0,8008=第 8 点=X10(八进制)。算错一位就读到相邻点。 - 无认证无加密,只在可信网络开放。
例程 2:换非标端口避免冲突
适用:同上 | 依赖模块:modbus_tcp_server | 难度:★☆☆
场景:502 端口已被别的 Modbus 服务占用,或想和内置服务分开。
完整代码(可直接下载运行):
lua
server = modbus_tcp_server.start(1502, 2) -- 端口 1502,最多 2 个主站要点:第 2 参 max_sockets 默认 1;第 3 参 buffer_size 默认 1024;返回对象仍须全局持有。
验证方法:主站连 IP:1502,读写正常。
注意事项:默认 max_sockets=1,第二台主站连不上要调大;local 持有会被 GC 停服。
例程 3:注意事项——越界地址与八进制换算
适用:同上 | 依赖模块:modbus_tcp_server | 难度:★★☆
场景:上位机读出一堆恒定怪值,或 Y/X 读到的不是预期点,多半是地址映射理解错了。
完整代码(可直接下载运行):
lua
server = modbus_tcp_server.start(502)
-- 映射要点(协议线 0 基地址):
-- 读 D100 → 保持寄存器 0x0000 起的 100 号 → 5 位写法 40101
-- 读 Y0 → 线圈 0x0000 → 写主站线圈地址 0
-- 读 X10 → 离散输入/线圈第 8 点(八进制 10 = 十进制 8)→ 协议地址 8008要点:
- 保持寄存器 4x:0–7999→D0–D7999,8256–8511→T0–T255,8712–8823→C200–C255(32 位占 2 寄存器)。
- 线圈 0x:0–255→Y,1000–4071→M0–M3071,5000–5999→S0–S999,8000–8255→X0–X255。
- 越界(如读 D99999)静默落到
D8030,不返回异常码。
验证方法:对照「地址映射表」填地址,读 D100 用 40101 能正确拿到 D100。
注意事项:越界不报错,先核对映射范围;X/Y 八进制编号要换算成点序号。