物联网 工业数据采集器实战:Modbus RTU 转 MQTT 协议桥接
工业现场有海量的老设备还在用 Modbus RTU 协议通过 RS485 总线通信,这些设备本身没有联网能力。但你想把它们的数据推上云端做远程监控怎么办?最实际的方案就是搭一个协议桥接网关——把 Modbus RTU 数据读出来,转成 MQTT 发到云平台。
今天我们就用一块 ESP32 加一个 RS485 模块,从零搭建一个工业数据采集器。不需要 PLC,不需要昂贵的工业网关,成本不到一百块。
为什么需要 Modbus RTU 转 MQTT
Modbus RTU 是工业领域最基础的通信协议之一,大量传感器、仪表、变频器都支持它。但它有个致命缺点:只能在 RS485 总线上跑,传输距离有限,也没法直接上互联网。
MQTT 恰恰反过来——轻量、基于 TCP/IP、天然适合云端。把两者打通,等于给传统工业设备装上了”翅膀”。
这个方案特别适合以下场景:
-
工厂车间温度/压力/流量数据远程采集
-
楼宇自控系统(BAS)数据上云
-
农业大棚环境传感器联网
-
旧设备改造,不想更换整套系统
硬件清单
| 部件 | 型号 | 数量 | 参考价格 |
|---|---|---|---|
| 主控板 | ESP32-WROOM-32 开发板 | 1 | ¥25 |
| RS485 模块 | MAX485 TTL 转 RS485 | 1 | ¥5 |
| 调试用 Modbus 设备 | 温湿度变送器 RS485 输出 | 1 | ¥35 |
| 连接线 | 杜邦线若干 | 1 套 | ¥3 |
| 电源 | 5V/1A 适配器 | 1 | ¥8 |
| 合计 | 约 ¥76 |
如果你已经有支持 Modbus RTU 的现场设备,温湿度变送器可以省掉。
接线说明
ESP32 到 MAX485 模块的接线很简单:
| MAX485 引脚 | ESP32 引脚 | 说明 |
|---|---|---|
| DI | GPIO17 (TX) | 数据输入 |
| RO | GPIO16 (RX) | 数据输出 |
| DE + RE | GPIO4 | 收发控制 |
| VCC | 5V | 供电 |
| GND | GND | 地 |
RS485 总线端接线:
-
A+ 接所有设备的 A 端(正端)
-
B- 接所有设备的 B 端(负端)
-
总线两端各接一个 120Ω 终端电阻(线长超过 100 米时必须加)
⚠️ 注意事项:
-
所有设备必须共地,否则通信不稳定
-
RS485 是差分信号,A/B 不能接反,接反会导致完全读不到数据
-
DE 和 RE 短接后接到同一个 GPIO,高电平发送,低电平接收
软件实现
我们使用 Arduino IDE + 两个关键库:
-
ModbusMaster— Modbus RTU 主站协议栈 -
PubSubClient— MQTT 客户端
先安装库,然后在 Arduino IDE 中搜索安装即可。
完整代码
#include
#include
#include
// WiFi 配置
const char* ssid = "YourWiFi";
const char* password = "YourPassword";
// MQTT 配置
const char* mqtt_server = "broker.emqx.io";
const int mqtt_port = 1883;
const char* mqtt_client_id = "modbus-gateway-001";
const char* mqtt_topic = "factory/sensor/data";
// RS485 引脚
#define RX_PIN 16
#define TX_PIN 17
#define DE_RE_PIN 4
ModbusMaster node;
WiFiClient espClient;
PubSubClient mqtt(espClient);
// 控制 RS485 收发
void preTransmission() {
digitalWrite(DE_RE_PIN, HIGH);
}
void postTransmission() {
digitalWrite(DE_RE_PIN, LOW);
}
void setup() {
Serial.begin(115200);
// 初始化 RS485 控制引脚
pinMode(DE_RE_PIN, OUTPUT);
digitalWrite(DE_RE_PIN, LOW);
// 初始化 Modbus 主站
node.begin(1, Serial2); // 从站地址 1
node.preTransmission(preTransmission);
node.postTransmission(postTransmission);
// 配置 Serial2 的 RX/TX
Serial2.begin(9600, SERIAL_8N1, RX_PIN, TX_PIN);
// 连接 WiFi
WiFi.begin(ssid, password);
Serial.print("Connecting WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected: " + WiFi.localIP().toString());
// 连接 MQTT
mqtt.setServer(mqtt_server, mqtt_port);
mqtt_connect();
}
void mqtt_connect() {
while (!mqtt.connected()) {
if (mqtt.connect(mqtt_client_id)) {
Serial.println("MQTT connected");
} else {
Serial.print("MQTT connect failed: ");
Serial.println(mqtt.state());
delay(3000);
}
}
}
void loop() {
if (!mqtt.connected()) {
mqtt_connect();
}
mqtt.loop();
// 读取 Modbus 寄存器(保持寄存器 0x0000 开始,读 2 个寄存器)
uint8_t result = node.readHoldingRegisters(0x0000, 2);
if (result == node.ku8MBSuccess) {
// 假设寄存器 0 = 温度(×10), 寄存器 1 = 湿度(×10)
float temperature = node.getResponseBuffer(0x00) / 10.0;
float humidity = node.getResponseBuffer(0x01) / 10.0;
// 构建 JSON 消息
String payload = "{";
payload += "\"temperature\":" + String(temperature, 1) + ",";
payload += "\"humidity\":" + String(humidity, 1) + ",";
payload += "\"timestamp\":" + String(millis());
payload += "}";
// 发布到 MQTT
if (mqtt.publish(mqtt_topic, payload.c_str())) {
Serial.println("Published: " + payload);
}
} else {
Serial.print("Modbus error: ");
Serial.println(result, HEX);
}
delay(5000); // 每 5 秒采集一次
}
代码要点说明
-
$1
-
$1
-
$1
MQTT 消息格式
采集器每 5 秒发布一条 JSON 消息到 factory/sensor/data:
{
"temperature": 23.5,
"humidity": 65.2,
"timestamp": 123456789
}
在云端你可以用 Node-RED、InfluxDB + Grafana,甚至直接用 Home Assistant 订阅这个 topic 来做数据可视化和告警。
常见问题排查
问题 1:Modbus 读取返回错误码
这是最常见的坑。错误码对照表:
| 错误码 | 含义 | 排查方向 |
|---|---|---|
| 0xE2 | 无效应答 | 检查波特率、从站地址是否正确 |
| 0xE4 | 超时 | 检查 A/B 线是否接反,终端电阻是否到位 |
| 0xE6 | 数据校验失败 | 检查串口参数(8N1),线缆质量 |
排查步骤:
-
$1
-
$1
-
$1
问题 2:MQTT 连接反复断开
-
确认 ESP32 的 WiFi 信号强度足够(-70dBm 以上)
-
检查 MQTT broker 是否开启了匿名连接(broker.emqx.io 支持匿名,但企业环境可能需要用户名密码)
-
如果数据量大,减小
mqtt.setBufferSize()的值或增加mqtt.setKeepAlive()时间
问题 3:数据跳变严重
-
RS485 总线过长时,信号反射会导致数据异常,检查终端电阻(120Ω)是否在总线两端正确安装
-
确认所有设备的共地连接可靠
-
软件层面可以加一个简单滤波:连续读 3 次取中值,或者用移动平均
问题 4:ESP32 频繁重启
可能是看门狗触发的。常见原因:
-
loop()中有长时间阻塞操作(比如 WiFi 连接超时太久) -
堆栈溢出,尝试在
setup()中增加ESP.getFreeHeap()监控内存 -
建议在 Modbus 读取失败时增加重试次数限制(比如最多重试 3 次)
进阶扩展
完成基本采集后,你还可以继续扩展:
-
多从站轮询:修改
node.begin()的从站地址,用数组轮询多个设备 -
断线缓存:添加 SD 卡模块,网络断开时本地缓存数据
-
OTA 升级:启用 ESP32 OTA 功能,远程更新固件
-
TLS 加密:MQTT 连接启用 TLS,确保数据传输安全
总结
用 ESP32 + RS485 模块搭建 Modbus RTU 转 MQTT 网关,成本低、部署快、维护简单。对于中小规模的工业物联网改造项目,这比买商业工业网关划算得多。核心思路就是:用 ModbusMaster 读寄存器数据,用 PubSubClient 发 MQTT,中间的协议转换逻辑其实就这么几行代码。
希望这篇博客文章对您有所帮助!