- 1. 一、modbus-gateway概述
- 2. 二、架构原理
- 3. 三、安装与编译
- 4. 四、基础配置:TCP转RTU网关
- 5. 五、多从站路由配置
- 6. 六、本地虚拟从站
- 7. 七、RTU-over-TCP配置
- 8. 八、多网关实例
- 9. 九、RS485高级控制
- 10. 十、Docker部署
- 11. 十一、完整实战:工厂能源监控网关
- 12. 十二、性能优化与监控
- 13. 十三、常见问题与排查
- 14. 十四、配置参考速查表
- 15. 十五、学习资源
- 16. VIP专属:Go语言Modbus网关完整代码包
modbus-gateway是由ffutop开源的高性能Modbus网关,使用Go语言编写,支持TCP、RTU、RTU-over-TCP三种协议的任意桥接,基于Slave ID的多从站路由,内置本地虚拟从站,以及多主站并发队列。本文从架构原理、安装编译、配置详解到实战部署,完整介绍如何用modbus-gateway搭建工业级Modbus协议转换网关。
一、modbus-gateway概述
1.1 什么是modbus-gateway
modbus-gateway是一个配置驱动的高性能ModbusVereinbarung Gateway,核心功能是在不同Modbus传输协议之间进行桥接转换。典型场景是:上位机(SCADA/PLC)通过Modbus TCP连接网关,网关再通过RS485串口(Modbus RTU)连接现场设备,实现TCPzuRTU的协议转换。它还支持基于从站ID(Slave ID)的路由,可以将不同从站地址的请求分发到不同的下游设备。
1.2 核心特性
- 全协议桥接:TCP、RTU、RTU-over-TCP三种协议在上游和下游可任意组合
- Slave ID路由:按从站地址将请求分发到不同下游,支持范围(1-10)和列表(1,2,3)
- 本地虚拟从站:内置虚拟ModbusVon der Station,支持memory/file/mmap三种持久化
- 多主站并发:多个上游主站同时连接,每个下游有独立序列化队列防止总线冲突
- RS485 RTS控制:完整的RTS信号时序控制,支持工业级RS485转换器
- 多网关实例:单个进程可运行多个独立网关实例
- pprof性能分析:内置Go pprof端点,可实时诊断CPU/内存/协程
- 高性能:Go原生并发,单实例可处理数千请求/秒
- 零依赖部署:编译为单个二进制文件,无需运行时环境
1.3 典型应用场景
- TCP转RTU:以太网上位机访问RS485串口设备(最常见)
- RTU转TCP:串口主站访问以太网Modbus TCPAusrüstung
- 多从站聚合:多条RS485总线的设备聚合到一个TCP端口
- 协议中继:RTU-over-TCP与标准TCP/RTU之间转换
- 设备模拟:用本地虚拟从站模拟不存在的ModbusAusrüstung
- 远程访问:将现场RS485设备暴露为TCP服务,支持远程调试
二、架构原理
modbus-gateway的架构分为三层:上游(Upstream)、网关核心(Gateway)、下游(Downstream)。
┌─────────────────────────────────────────────────────────────┐
│ Upstreams(上游) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ TCP Master │ │ RTU-over-TCP │ │ RTU Master │ │
│ │ 0.0.0.0:502 │ │ 0.0.0.0:503 │ │ /dev/ttyS0 │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
└─────────┼─────────────────┼─────────────────┼──────────────┘
│ │ │
┌─────────▼─────────────────▼─────────────────▼──────────────┐
│ Gateway Core(网关核心) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Queue │→│ Buffer │→│Serializer│→│SlaveID │ │
│ │ 队列缓冲 │ │ 缓冲区 │ │ 序列化 │ │Router │ │
│ └──────────┘ └──────────┘ └──────────┘ └────┬─────┘ │
│ ┌───────▼───────┐ │
│ │ Local Slave │ │
│ │ 本地虚拟从站 │ │
│ └───────────────┘ │
└──────────────────────────────┬──────────────────────────────┘
│
┌──────────────────────────────▼──────────────────────────────┐
│ Downstreams(下游) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ RTU Slave │ │ TCP Slave │ │ Local Slave │ │
│ │ slave_ids: │ │ slave_ids: │ │ slave_ids: │ │
│ │ 1-10 │ │ 11-20 │ │ 100 │ │
│ │ /dev/ttyUSB0 │ │ 192.168.1.50 │ │ mmap存储 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
2.1 关键机制说明
- 队列缓冲(Queue Buffer):每个上游连接的请求先进入队列,避免请求丢失
- 序列化(Serializer):同一下游的请求被串行化执行,防止RS485总线冲突
- Slave ID路由:根据请求中的从站地址,查表决定发往哪个下游
- 本地虚拟从站:路由到local类型的请求由网关内部直接响应,无需真实设备
三、安装与编译
# 方式一:源码编译(推荐)
git clone https://github.com/ffutop/modbus-gateway.git
cd modbus-gateway
go build -o modbus-gateway
# 交叉编译(在Linux上编译Windows版本)
GOOS=windows GOARCH=amd64 go build -o modbus-gateway.exe
# 交叉编译ARM版本(Raspberry Pi/嵌入式设备)
GOOS=linux GOARCH=arm GOARM=7 go build -o modbus-gateway-arm
# 方式二:下载预编译二进制
# 从GitHub Releases页面下载对应平台的二进制
# https://github.com/ffutop/modbus-gateway/releases
# 验证安装
./modbus-gateway -help
# 输出命令行参数说明
# 查看版本
./modbus-gateway -version
四、基础配置:TCP转RTU网关
最常见的场景:上位机通过Modbus TCP连接网关,网关通过RS485串口连接现场RTUAusrüstung。
# config-tcp2rtu.yaml - TCP转RTU基础配置
gateways:
- name: "tcp-to-rtu-gateway"
# 上游:监听TCP端口,接受上位机连接
upstreams:
- type: "tcp"
tcp:
address: "0.0.0.0:502" # 监听所有网卡的502端口
# 下游:通过RS485串口连接现场设备
downstreams:
- type: "rtu"
slave_ids: "1-10" # Von der StationID 1-10的请求路由到此串口
serial:
device: "/dev/ttyUSB0" # USB转RS485设备
baud_rate: 9600 # Porter Rate
data_bits: 8 # Datenplatz
parity: "N" # Prüfstand. N=无 E=偶 O=奇
stop_bits: 1 # Stoppen Sie Platz
timeout: "500ms" # 读取超时
rqst_pause: "100ms" # 请求间最小间隔
log:
level: "info" # 日志级别 debug/info/warn/error
file: "" # 日志文件路径,空=输出到stdout
# 启动网关
./modbus-gateway -config config-tcp2rtu.yaml
# 测试:用pymodbus或QModMaster连接网关
# 连接地址:网关IP:502
# Von der StationID:1-10(会被路由到RS485串口)
# Funktionscode:01/02/03/04/05/06/15/16全部支持
# 查看日志确认请求转发
# INFO tcp master accepted conn from 192.168.1.100:54321
# INFO routing slave_id=1 to downstream rtu:/dev/ttyUSB0
# INFO request FC=03 addr=0 count=10 -> response ok
五、多从站路由配置
当现场有多条RS485总线或多种类型设备时,可以通过Slave ID路由将请求分发到不同下游。
# config-multi-slave.yaml - 多从站路由
gateways:
- name: "multi-slave-gateway"
upstreams:
- type: "tcp"
tcp:
address: "0.0.0.0:502"
downstreams:
# 下游1:RS485总线A - Temperatur- & Feuchtigkeitssensor(Von der Station1-10)
- type: "rtu"
slave_ids: "1-10"
serial:
device: "/dev/ttyUSB0"
baud_rate: 9600
data_bits: 8
parity: "N"
stop_bits: 1
timeout: "500ms"
# 下游2:RS485总线B - 电力仪表(Von der Station11-20)
- type: "rtu"
slave_ids: "11-20"
serial:
device: "/dev/ttyUSB1"
baud_rate: 19200
data_bits: 8
parity: "E" # 电力仪表常用偶校验
stop_bits: 1
timeout: "300ms"
# 下游3:Modbus TCPAusrüstung - 变频器(Von der Station21)
- type: "tcp"
slave_ids: "21"
tcp:
address: "192.168.1.50:502"
# 下游4:本地虚拟从站 - 网关状态(Von der Station100)
- type: "local"
slave_ids: "100"
local:
persistence:
type: "memory" # 内存存储,重启后丢失
log:
level: "info"
路由规则说明:
- slave_ids: “1-10”:匹配从站ID 1到10的所有请求
- slave_ids: “1,2,3”:匹配从站ID 1、2、3
- slave_ids: “1-5,10,20-25”:混合范围和列表
- slave_ids: “”(空):匹配所有未被其他下游匹配的请求(默认路由)
- 如果一个从站ID被多个下游匹配,按配置顺序第一个匹配的下游生效
六、本地虚拟从站
modbus-gateway内置了本地虚拟从站功能,可以在没有真实设备的情况下响应ModbusBitte,支持三种持久化方式。
# config-local-slave.yaml - 本地虚拟从站配置
gateways:
- name: "local-slave-demo"
upstreams:
- type: "tcp"
tcp:
address: "0.0.0.0:502"
downstreams:
# 虚拟从站1:内存模式(重启数据丢失,适合测试)
- type: "local"
slave_ids: "100"
local:
persistence:
type: "memory"
# 虚拟从站2:文件模式(数据持久化到文件)
- type: "local"
slave_ids: "101"
local:
persistence:
type: "file"
path: "/data/modbus-slave-101/"
# 虚拟从站3:mmap模式(内存映射文件,高性能+持久化)
- type: "local"
slave_ids: "102"
local:
persistence:
type: "mmap"
path: "/data/modbus-slave-102/"
log:
level: "debug"
6.1 三种持久化方式对比
| 方式 | 速度 | 持久化 | 适用场景 |
|---|---|---|---|
| memory | 最快 | 否(重启丢失) | 测试、临时模拟 |
| file | 较慢 | 是 | 需要持久化但写入不频繁 |
| mmap | 快 | 是 | 生产环境,高频读写 |
七、RTU-over-TCP配置
RTU-over-TCP是将Modbus RTU帧直接封装在TCP中传输(不加MBAP Kopf),常用于某些串口服务器设备。modbus-gateway支持RTU-over-TCP作为上游或下游。
# config-rtu-over-tcp.yaml - RTU-over-TCP配置
gateways:
- name: "rtu-over-tcp-gateway"
upstreams:
# 上游1:标准Modbus TCP(带MBAP Kopf)
- type: "tcp"
tcp:
address: "0.0.0.0:502"
# 上游2:RTU-over-TCP(裸RTU帧)
- type: "rtu-over-tcp"
tcp:
address: "0.0.0.0:503" # 用不同端口区分
downstreams:
# 下游:标准RTUSerielle Ausrüstung
- type: "rtu"
slave_ids: "1-10"
serial:
device: "/dev/ttyUSB0"
baud_rate: 9600
data_bits: 8
parity: "N"
stop_bits: 1
# 下游:RTU-over-TCPAusrüstung(如某些串口服务器)
- type: "rtu-over-tcp"
slave_ids: "11-20"
tcp:
address: "192.168.1.100:4001" # 串口服务器地址
log:
level: "info"
八、多网关实例
单个modbus-gateway进程可以运行多个独立的网关实例,每个实例有自己的上游监听和下游连接,互不干扰。
# config-multi-gateway.yaml - 多网关实例
gateways:
# 实例1:车间A网关
- name: "workshop-a"
upstreams:
- type: "tcp"
tcp:
address: "0.0.0.0:502"
downstreams:
- type: "rtu"
slave_ids: "1-20"
serial:
device: "/dev/ttyUSB0"
baud_rate: 9600
data_bits: 8
parity: "N"
stop_bits: 1
# 实例2:车间B网关
- name: "workshop-b"
upstreams:
- type: "tcp"
tcp:
address: "0.0.0.0:503" # 不同端口
downstreams:
- type: "rtu"
slave_ids: "1-20"
serial:
device: "/dev/ttyUSB1"
baud_rate: 19200
data_bits: 8
parity: "E"
stop_bits: 1
# 实例3:能源监控网关
- name: "energy-monitor"
upstreams:
- type: "tcp"
tcp:
address: "0.0.0.0:504"
downstreams:
- type: "tcp"
slave_ids: "1-5"
tcp:
address: "192.168.1.50:502"
# 性能分析(可选)
pprof:
enabled: true
address: "localhost:6060"
log:
level: "info"
file: "/var/log/modbus-gateway.log"
九、RS485高级控制
对于需要精确控制RS485收发切换的工业场景,modbus-gateway提供了完整的RTS信号时序控制。
# config-rs485.yaml - RS485高级控制
gateways:
- name: "rs485-industrial"
upstreams:
- type: "tcp"
tcp:
address: "0.0.0.0:502"
downstreams:
- type: "rtu"
slave_ids: "1-10"
serial:
device: "/dev/ttyUSB0"
baud_rate: 115200
data_bits: 8
parity: "N"
stop_bits: 1
timeout: "300ms"
rqst_pause: "50ms"
# RS485 RTS控制
rs485: true # aktiviert.RTS控制
delay_rts_before_send: "1ms" # 发送前RTS置位延迟
delay_rts_after_send: "1ms" # 发送后RTS复位延迟
rts_high_during_send: true # 发送期间RTS保持高电平
rts_high_after_send: false # 发送后RTS低电平
log:
level: "debug"
9.1 RTS时序说明
- rs485: true:启用硬件RTS流控,自动切换收发方向
- delay_rts_before_send:RTS置位后等待多久再发送数据,确保RS485芯片切换到发送模式
- delay_rts_after_send:数据发送完成后等待多久再复位RTS,确保最后一个字节发送完毕
- rts_high_during_send:发送期间RTS电平(true=高电平,false=低电平),取决于RS485芯片的极性
- HäufigRS485芯片如MAX485:DE/RE引脚通常高电平发送、低电平接收
十、Docker部署
# Dockerfile
FROM golang:1.22-alpine AS builder
WORKDIR /app
RUN apk add --no-cache git
RUN git clone https://github.com/ffutop/modbus-gateway.git .
RUN go build -o modbus-gateway
FROM alpine:3.19
RUN apk add --no-cache ca-certificates
COPY --from=builder /app/modbus-gateway /usr/local/bin/
ENTRYPOINT ["modbus-gateway"]
CMD ["-config", "/etc/modbus-gateway/config.yaml"]
# docker-compose.yaml
version: "3.8"
services:
modbus-gateway:
build: .
container_name: modbus-gateway
restart: unless-stopped
ports:
- "502:502" # Modbus TCP
- "503:503" # RTU-over-TCP
- "6060:6060" # pprof
devices:
- "/dev/ttyUSB0:/dev/ttyUSB0" # RS485串口
- "/dev/ttyUSB1:/dev/ttyUSB1"
volumes:
- ./config.yaml:/etc/modbus-gateway/config.yaml:ro
- ./data:/data
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# 构建并启动
docker-compose up -d
# 查看日志
docker-compose logs -f
# 重启
docker-compose restart
十一、完整实战:工厂能源监控网关
以下是一个完整的生产环境配置案例:工厂能源监控系统,通过modbus-gateway将车间的电力仪表、温湿度传感器和PLC统一接入SCADADas System。
# factory-energy.yaml - 工厂能源监控网关
gateways:
- name: "factory-energy-gateway"
upstreams:
# SCADA系统通过TCPVerbindung
- type: "tcp"
tcp:
address: "0.0.0.0:502"
downstreams:
# 1号RS485总线:电力仪表(Von der Station1-8)
- type: "rtu"
slave_ids: "1-8"
serial:
device: "/dev/ttyUSB0"
baud_rate: 9600
data_bits: 8
parity: "E"
stop_bits: 1
timeout: "500ms"
rqst_pause: "100ms"
rs485: true
delay_rts_before_send: "1ms"
delay_rts_after_send: "2ms"
# 2号RS485总线:Temperatur- & Feuchtigkeitssensor(Von der Station9-16)
- type: "rtu"
slave_ids: "9-16"
serial:
device: "/dev/ttyUSB1"
baud_rate: 4800
data_bits: 8
parity: "N"
stop_bits: 1
timeout: "1000ms"
# 3号RS485总线:PLC(Von der Station17-20)
- type: "rtu"
slave_ids: "17-20"
serial:
device: "/dev/ttyUSB2"
baud_rate: 19200
data_bits: 8
parity: "N"
stop_bits: 1
timeout: "300ms"
# 以太网设备:智能电表(Von der Station21)
- type: "tcp"
slave_ids: "21"
tcp:
address: "192.168.1.50:502"
# 本地虚拟从站:网关自身状态(Von der Station100)
- type: "local"
slave_ids: "100"
local:
persistence:
type: "mmap"
path: "/data/gateway-status/"
# 性能监控
pprof:
enabled: true
address: "localhost:6060"
log:
level: "info"
file: "/var/log/modbus-gateway/factory-energy.log"
11.1 系统架构说明
- SCADA系统连接网关TCP 502端口,统一访问所有设备
- Von der Station1-8的请求路由到USB0(电力仪表,9600偶校验)
- Von der Station9-16的请求路由到USB1(Temperatur- & Feuchtigkeitssensor,4800无校验)
- Von der Station17-20的请求路由到USB2(PLC,19200无校验)
- Von der Station21的请求转发到以太网智能电表
- Von der Station100由网关本地虚拟从站响应,可写入网关运行状态
- 每个下游有独立序列化队列,不会因为一条总线慢而影响其他总线
十二、性能优化与监控
12.1 pprof性能分析
# aktiviert.pprof后,访问以下端点:
# http://localhost:6060/debug/pprof/ - 概览
# http://localhost:6060/debug/pprof/heap - 内存分析
# http://localhost:6060/debug/pprof/profile - CPU分析(30秒)
# http://localhost:6060/debug/pprof/goroutine - 协程分析
# 用go tool分析CPU
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
# 进入pprof交互界面后:
# top10 - ÜberprüfenCPU占用前10的函数
# web - 生成调用图(需要graphviz)
# list function - 查看函数级别的耗时
# 分析内存
go tool pprof http://localhost:6060/debug/pprof/heap
# inuse_space - 当前内存占用
# alloc_space - 累计分配
12.2 性能优化建议
- 合理设置timeout:超时时间应略大于设备最大响应时间,过短导致丢包,过长降低效率
- rqst_pause调优:请求间隔根据设备响应速度调整,慢速设备建议100-200ms
- 波特率最大化:在通信稳定的前提下尽量使用高波特率(115200)
- 分批读取:上位机应尽量批量读取连续地址,减少请求次数
- 连接复用:上位机保持长连接,避免频繁建立TCPVerbindung
- Von der StationID规划:同一总线上的设备使用连续的从站ID,便于路由配置
十三、常见问题与排查
| 问题 | 原因 | 解决方法 |
|---|---|---|
| 上游连接成功但无响应 | Von der StationID未匹配到任何下游 | 检查slave_ids配置,确保请求的从站ID在路由范围内 |
| RTU设备无响应 | 串口参数不匹配 | 核对波特率、Datenplatz、Prüfstand.、停止位与设备一致 |
| RS485通信不稳定 | RTS时序不正确 | 调整delay_rts_before/after_send,检查接线和终端电阻 |
| 多主站时数据错乱 | 序列化队列未生效 | 确认每个下游只有一个序列化队列,检查日志 |
| TCP连接被拒绝 | 端口被占用或防火墙 | 检查502端口是否被其他程序占用,开放防火墙 |
| 虚拟从站数据丢失 | 使用了memory模式 | 改用file或mmap模式持久化 |
| 高延迟 | timeout设置过大 | 根据设备实际响应时间调小timeout |
| 串口权限不足 | 用户无串口访问权限 | 将用户加入dialout组:sudo usermod -aG dialout $USER |
| RTU-over-TCP不工作 | 对端是标准TCP而非RTU-over-TCP | 确认设备协议类型,标准TCP用type: tcp |
| 内存持续增长 | 连接泄漏或数据累积 | 用pprof分析,检查上游连接是否正常关闭 |
十四、配置参考速查表
| 配置项 | Typus | 默认值 | 说明 |
|---|---|---|---|
| gateways[].name | string | – | 网关名称,用于日志标识 |
| upstreams[].type | enum | – | tcp / rtu / rtu-over-tcp |
| upstreams[].tcp.address | string | – | TCPAdresse überwachen |
| downstreams[].type | enum | – | tcp / rtu / rtu-over-tcp / local |
| downstreams[].slave_ids | string | “” | 路由规则,空=匹配所有 |
| serial.device | string | – | 串口设备路径 |
| serial.baud_rate | int | – | Porter Rate |
| serial.data_bits | int | 8 | Datenplatz |
| serial.parity | string | “N” | N/E/O |
| serial.stop_bits | int | 1 | Stoppen Sie Platz |
| serial.timeout | duration | “500ms” | 读取超时 |
| serial.rqst_pause | duration | “100ms” | 请求间最小间隔 |
| serial.rs485 | bool | false | aktiviert.RTS控制 |
| local.persistence.type | enum | – | memory / file / mmap |
| local.persistence.path | string | – | 持久化目录 |
| pprof.enabled | bool | false | 启用性能分析 |
| log.level | string | “info” | debug/info/warn/error |
| log.file | string | “” | 日志文件路径 |
十五、学习资源
- 项目地址:https://github.com/ffutop/modbus-gateway
- 官方文档:http://modbus-gateway.ffutop.com/
- Modbus协议规范:https://modbus.org/specs.php
- Go语言官网:https://go.dev/
modbus-gateway作为Go语言编写的高性能Modbus网关,以配置驱动的方式实现了TCP/RTU/RTU-over-TCP的任意协议桥接,基于Slave ID的多从站路由和本地虚拟从站功能使其能够应对复杂的工业现场需求。本文提供的从基础TCP转RTU到多网关实例、RS485高级控制、Docker部署和完整工厂能源监控的实战配置,可直接用于生产环境部署。建议在部署前先用本地虚拟从站测试路由规则,再逐步接入真实设备。
VIP专属:Go语言Modbus网关完整代码包
ffutop/modbus-gateway YAML配置驱动,TCP/RTU/本地多协议转换,含工厂能源监控配置。
Öffnen Sie VIP即可下载完整代码,同时解锁 30+ 工程实战资料包:调试脚本、速查表、项目模板、排查案例……
前往VIP资料库下载 → 月费仅9.9元 / 年费199元