modbus-gateway是由ffutop开源的高いパフォーマンスをModbusゲートウェイ,使用Go语言编写,サポートTCP、RTU、RTU-over-TCP三种协议的任意桥接,基于Slave ID的多スレーブ路由,内置本地虚拟スレーブ,以及多マスター并发队列。本記事从架构原理、インストール编译、設定详解到実践部署,完全介绍如何用modbus-gateway搭建工业级Modbusプロトコル转换ゲートウェイ。
一、modbus-gateway概述
1.1 什么是modbus-gateway
modbus-gateway是一个構成ドライバ的高いパフォーマンスをModbusProtocol Gateway,核心功能是在不同Modbus传输协议之间进行桥接转换。典型场景是:トップマシン(SCADA/PLC)経由Modbus TCP连接ゲートウェイ,ゲートウェイ再経由RS485シリアルポート(Modbus RTU)サイトをつなぐデバイス,実装TCPtoRTU的プロトコル変換。它还サポート基于スレーブID(Slave ID)的路由,可以将不同駅からの住所的リクエスト分发到不同的下游デバイス。
1.2 核心特性
- 全协议桥接:TCP、RTU、RTU-over-TCP三种协议在上游和下游可任意组合
- Slave ID路由:按駅からの住所将リクエスト分发到不同下游,サポート範囲(1-10)和リスト(1,2,3)
- 本地虚拟スレーブ:内置虚拟Modbusslave,サポートmemory/file/mmap三种持久化
- 多マスター并发:多个上游マスター同时连接,每个下游有独立序列化队列防止バス冲突
- RS485 RTS控制:完全的RTS信号时序控制,サポート工业级RS485変換器です。
- 多ゲートウェイ实例:单个进程可运行多个独立ゲートウェイ实例
- pprof性能分析:内置Go pprof端点,可实时诊断CPU/内存/协程
- 高いパフォーマンスを:Go原生并发,单实例可処理数千リクエスト/秒
- 零依赖部署:编译为单个二进制文件,无需运行时环境
1.3 典型アプリケーションシナリオ。
- TCP to RTU:イーサネット(イーサネット)トップマシンアクセスRS485シリアルデバイス(最も一般的な)
- RTU to TCP:シリアルポートマスターアクセスイーサネット(イーサネット)Modbus TCPdevice
- 多スレーブ集約:多条RS485バス的デバイス集約到一个TCPポート
- 协议中继:RTU-over-TCP与標準TCP/RTU之间转换
- デバイス模拟:用本地虚拟ステーションからのシミュレーション不存在的Modbusdevice
- 远程アクセス:将现场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 to RTUゲートウェイ
最も一般的な的场景:トップマシン経由Modbus TCP连接ゲートウェイ,ゲートウェイ通過RS485シリアルポートサイトをつなぐRTUdevice。
# config-tcp2rtu.yaml - TCP to 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" # slaveID 1-10的リクエスト路由到此シリアルポート
serial:
device: "/dev/ttyUSB0" # USB转RS485デバイス
baud_rate: 9600 # Baud rate
data_bits: 8 # data bit
parity: "N" # check digit N=无 E=偶 O=奇
stop_bits: 1 # stop bit
timeout: "500ms" # 読み取りタイムアウト
rqst_pause: "100ms" # リクエスト间最小間隔
log:
level: "info" # 日志级别 debug/info/warn/error
file: "" # 日志文件路径,空=出力到stdout
# 启动ゲートウェイ
./modbus-gateway -config config-tcp2rtu.yaml
# 测试:用pymodbus或QModMaster连接ゲートウェイ
# 连接アドレス:ゲートウェイIP:502
# slaveID:1-10(会被路由到RS485シリアルポート)
# function code: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 - Temperature & Humidity Sensor(slave1-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 - パワーメーター(slave11-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 TCPdevice - 周波数変換器(slave21)
- type: "tcp"
slave_ids: "21"
tcp:
address: "192.168.1.50:502"
# 下游4:本地虚拟スレーブ - ゲートウェイステータス(slave100)
- 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内置了本地虚拟スレーブ功能,可以在没実際のデバイス的情况下レスポンスModbusRequest,サポート三种持久化方式。
# 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 Head),常用于某些シリアルサーバーデバイス。modbus-gatewayサポートRTU-over-TCP作为上游或下游。
# config-rtu-over-tcp.yaml - RTU-over-TCP設定
gateways:
- name: "rtu-over-tcp-gateway"
upstreams:
# 上游1:標準Modbus TCP(带MBAP Head)
- 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:
# 下游:標準RTUSerial Port Device
- type: "rtu"
slave_ids: "1-10"
serial:
device: "/dev/ttyUSB0"
baud_rate: 9600
data_bits: 8
parity: "N"
stop_bits: 1
# 下游:RTU-over-TCPdevice(如某些シリアルサーバー)
- 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 # EnableRTS控制
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芯片的极性
- commonRS485芯片如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统一接入SCADAsystem。
# factory-energy.yaml - プラントエネルギー監視ゲートウェイ
gateways:
- name: "factory-energy-gateway"
upstreams:
# SCADA系统経由TCPconnect
- type: "tcp"
tcp:
address: "0.0.0.0:502"
downstreams:
# 1号RS485バス:パワーメーター(slave1-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バス:Temperature & Humidity Sensor(slave9-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(slave17-20)
- type: "rtu"
slave_ids: "17-20"
serial:
device: "/dev/ttyUSB2"
baud_rate: 19200
data_bits: 8
parity: "N"
stop_bits: 1
timeout: "300ms"
# イーサネット(イーサネット)デバイス:スマートメーター(slave21)
- type: "tcp"
slave_ids: "21"
tcp:
address: "192.168.1.50:502"
# 本地虚拟スレーブ:ゲートウェイ自身ステータス(slave100)
- 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ポート,统一アクセス全装備。
- slave1-8的リクエスト路由到USB0(パワーメーター,9600ダブルチェック)
- slave9-16的リクエスト路由到USB1(Temperature & Humidity Sensor,4800検証なし。)
- slave17-20的リクエスト路由到USB2(PLC,19200検証なし。)
- slave21的リクエスト转发到イーサネット(イーサネット)スマートメーター
- slave100由ゲートウェイ本地虚拟スレーブレスポンス,可写入ゲートウェイ実行状態の状態
- 每个下游有独立序列化队列,不会因为一条バス慢而影响其他バス
十二、パフォーマンス最適化与モニタリング
12.1 pprof性能分析
# Enablepprof后,アクセス以下端点:
# 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 - ViewCPU占用前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)
- 分批読み取り:トップマシン应尽量バッチ読み取り连续アドレス,减少リクエスト次数
- 连接复用:トップマシン保持长连接,避免频繁建立TCPconnect
- slaveID规划:同一バス上的デバイス使用连续的スレーブID,便于路由設定
十三、よくある質問与トラブルシューティング
| 問題 | 原因 | 解決方法 |
|---|---|---|
| 上游接続成功です。但応答なし。 | slaveID未マッチング到任何下游 | チェックslave_ids設定,确保リクエスト的スレーブID在路由範囲内 |
| RTUデバイスが応答しません | シリアルパラメータの不一致 | 核对ボーレート、data bit、check digit、ストップ·ビット与デバイス一致 |
| 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分析,チェック上游连接是否正常关闭 |
十四、設定参照クイックチェック·テーブル
| 設定项 | Type | デフォルト値 | 説明 |
|---|---|---|---|
| gateways[].name | string | - | ゲートウェイ名称,用于日志标识 |
| upstreams[].type | enum | - | tcp / rtu / rtu-over-tcp |
| upstreams[].tcp.address | string | - | TCPListen address |
| downstreams[].type | enum | - | tcp / rtu / rtu-over-tcp / local |
| downstreams[].slave_ids | string | "" | 路由ルール,空=マッチングすべての |
| serial.device | string | - | シリアルデバイス路径 |
| serial.baud_rate | int | - | Baud rate |
| serial.data_bits | int | 8 | data bit |
| serial.parity | string | "N" | N/E/O |
| serial.stop_bits | int | 1 | stop bit |
| serial.timeout | duration | "500ms" | 読み取りタイムアウト |
| serial.rqst_pause | duration | "100ms" | リクエスト间最小間隔 |
| serial.rs485 | bool | false | EnableRTS控制 |
| 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 to RTU到多ゲートウェイ实例、RS485エキスパート控制、Docker部署和完全工厂能源モニタリングの実践設定,可直接使用する。于生产环境部署。建议在部署前先用本地虚拟スレーブ测试路由ルール,再逐步接入真实デバイス。
VIP专属:Go语言Modbusゲートウェイ完全なコードパッケージ
ffutop/modbus-gateway YAML構成ドライバ,TCP/RTU/ローカルマルチプロトコル変換,工場エネルギー監視を含む。
Activate VIP即可下载完全代码,同时解锁 30+ 工程実践パッケージの内容:スクリプトのデバッグ、クイックチェック·テーブル、项目模板、トラブルシューティング案例……
前往VIP资料库下载 → 月费仅9.9元 / 年费199元