SimpleModbus 简介
SimpleModbus 是一套为 Arduino 平台开发的 Modbus RTU 协议库,托管在 Google Code Archive(项目已被归档)。它由 Juan Bester 开发,包含 SimpleModbusMaster(主站库)和 SimpleModbusSlave(从站库)两个独立的 Arduino 库。虽然项目名称里有”Simple”,但它提供的功能覆盖了 Modbus RTU 通信中最常用的功能码,非常适合 Arduino 初学者和快速原型开发。
⚠️ 注意:SimpleModbus 项目已不再活跃更新。对于新项目,推荐使用更现代化的 Modbus RTU for Arduino 库(库名为 “Modbus”)或 esp-modbus(ESP32 平台)。但 SimpleModbus 的代码简洁易懂,作为学习 Modbus 协议实现的入门资料仍然很有价值。
SimpleModbus 支持的功能
- SimpleModbusMaster:支持 FC01(读线圈)、FC02(读离散输入)、FC03(读保持寄存器)、FC04(读输入寄存器)、FC15(写多线圈)、FC16(写多寄存器)
- SimpleModbusSlave:支持 FC03(读保持寄存器)和 FC16(写多寄存器)
- 硬件要求:Arduino + RS-485 转换模块(如 MAX485)
硬件连接
Arduino 通过 MAX485 模块连接 RS-485 总线。典型接线方式:
| MAX485 引脚 | Arduino Uno | 说明 |
|---|---|---|
| VCC | 5V | 电源 |
| GND | GND | 地 |
| RO | RX (D0) | 接收数据 → Arduino 串口 RX |
| DI | TX (D1) | 发送数据 ← Arduino 串口 TX |
| RE | D2 | 接收使能(低有效) |
| DE | D2 | 发送使能(高有效),与 RE 并联到同一 IO |
| A | A (总线+) | RS-485 A 线(差分+) |
| B | B (总线-) | RS-485 B 线(差分-) |
⚠️ 在 RS-485 总线的两端必须加 120Ω 终端电阻(A-B 之间),否则长距离通信会出现信号反射导致数据错误。
SimpleModbusSlave 示例:Arduino 作为 Modbus RTU 从站
#include <SimpleModbusSlave.h>
// 定义保持寄存器数组(10个寄存器)
enum {
TEMP_REG = 0, // 温度(x10)
HUMIDITY_REG, // 湿度(x10)
COIL_STATUS, // 线圈状态
SETPOINT, // 设定值
// ... 更多寄存器
TOTAL_REGS = 10
};
unsigned int holdingRegs[TOTAL_REGS];
void setup() {
// 设置 RS-485 方向控制引脚
pinMode(2, OUTPUT);
// SimpleModbusSlave 参数:
// 1. 寄存器数组地址
// 2. 寄存器数量
// 3. 发送使能引脚
// 4. 发送使能有效电平
// 5. 从站地址
// 6. 波特率
// 7. 校验(0=无校验)
modbus_configure(holdingRegs, TOTAL_REGS,
2, // TXEN 引脚
HIGH, // TXEN 高电平时发送
1, // 从站地址
9600, // 波特率
0); // 校验
}
void loop() {
// 更新传感器数据(模拟)
holdingRegs[TEMP_REG] = analogRead(A0) / 2; // 温度(模拟)
holdingRegs[HUMIDITY_REG] = analogRead(A1) / 3;
holdingRegs[COIL_STATUS] = digitalRead(3);
// 处理 Modbus 请求
modbus_update();
}
SimpleModbusMaster 示例:读取从站数据
#include <SimpleModbusMaster.h>
#define TOTAL_NO_OF_REGISTERS 2
unsigned int holdingRegs[TOTAL_NO_OF_REGISTERS];
void setup() {
Serial.begin(9600);
// SimpleModbusMaster 参数:
// 1. 本地寄存器数组
// 2. 读取/写入的寄存器数量
// 3. 发送使能引脚
// 4. 发送使能有效电平
modbus_configure(holdingRegs, TOTAL_NO_OF_REGISTERS,
2, // TXEN 引脚
HIGH); // TXEN 高电平时发送
}
void loop() {
// 从站地址 1,起始地址 0,读取 2 个保持寄存器
unsigned char result = modbus_read_holding_registers(1, 0, 2);
if (result == 0) {
Serial.print("温度: ");
Serial.print(holdingRegs[0] / 10.0);
Serial.print("°C, 湿度: ");
Serial.print(holdingRegs[1] / 10.0);
Serial.println("%");
} else {
Serial.print("Modbus 读取失败,错误码: ");
Serial.println(result);
}
delay(1000);
}
SimpleModbus vs 现代替代方案
SimpleModbus 项目已经在数年前停止更新,现代 Arduino 项目推荐以下替代方案:
| 库名 | 平台 | 特点 |
|---|---|---|
| ArduinoModbus | Arduino(官方) | Arduino 官方维护,支持 RTU 和 TCP,Arduino IDE 库管理器一键安装 |
| Modbus-Master-Slave-for-Arduino | Arduino | 同时支持主站和从站,功能码覆盖全,GitHub 活跃更新 |
| esp-modbus | ESP32 / ESP8266 | Espressif 官方库,支持 Modbus RTU/TCP/ASCII,性能优异 |
| SimpleModbus | Arduino | 简单易懂,适合学习,但已停更 |
ESP32 上实现 Modbus RTU 通信(现代方案)
如果你使用 ESP32 平台,推荐 Espressif 官方的 esp-modbus 库。以下是一个最小示例:
// ESP32 使用 HardwareSerial + MAX485
#include <Arduino.h>
// 定义 RS-485 控制引脚
#define RXD2 16 // ESP32 RX2
#define TXD2 17 // ESP32 TX2
#define DE_RE 4 // RS-485 方向控制
HardwareSerial modbusSerial(2);
void setup() {
Serial.begin(115200);
modbusSerial.begin(9600, SERIAL_8N1, RXD2, TXD2);
pinMode(DE_RE, OUTPUT);
digitalWrite(DE_RE, LOW); // 默认接收模式
}
void loop() {
// 发送 Modbus RTU 请求(手动构造帧)
uint8_t request[] = {
0x01, // 从站地址
0x03, // 功能码(读保持寄存器)
0x00, 0x00, // 起始地址
0x00, 0x02 // 读取 2 个寄存器
};
uint16_t crc = calculateCRC(request, 6);
request[6] = crc & 0xFF;
request[7] = crc >> 8;
// 切换到发送模式
digitalWrite(DE_RE, HIGH);
delayMicroseconds(100);
modbusSerial.write(request, 8);
modbusSerial.flush();
// 切换回接收模式
digitalWrite(DE_RE, LOW);
// 等待响应
delay(500);
while (modbusSerial.available()) {
Serial.printf("%02X ", modbusSerial.read());
}
Serial.println();
delay(2000);
}
Arduino 官方 ArduinoModbus 库
在 Arduino IDE 中打开库管理器(Sketch → Include Library → Manage Libraries),搜索 “ArduinoModbus”,安装即可。ArduinoModbus 支持 Modbus RTU(RS-485)和 Modbus TCP(WiFi/以太网),主站和从站模式均可。
#include <ArduinoRS485.h>
#include <ArduinoModbus.h>
void setup() {
Serial.begin(9600);
// 初始化 RS-485
RS485.begin(9600);
// 启动 Modbus RTU 从站(地址 1)
ModbusRTUServer.begin(1, 9600);
// 配置保持寄存器
ModbusRTUServer.configureHoldingRegisters(0, 10);
}
void loop() {
// 轮询 Modbus 请求
ModbusRTUServer.poll();
// 更新寄存器值
int sensorValue = analogRead(A0);
ModbusRTUServer.holdingRegisterWrite(0, sensorValue);
}
常见问题排查
通信完全没有响应
- 检查 MAX485 DE/RE 引脚的方向切换逻辑是否正确(发送时 HIGH、接收时 LOW)
- 确认波特率和从站地址与主站配置一致
- 用 USB 转 RS-485 模块 + 串口助手监控总线数据,确认信号是否存在
- 检查 A/B 线是否接反(用万用表测差分电压:A 对 B 应为正电压时总线空闲)
偶尔丢数据或 CRC 错误
- 检查 RS-485 总线两端是否加了 120Ω 终端电阻
- 降低波特率尝试(4800 或 2400),排除信号完整性因素
- 缩短总线长度或加中继器
- 确认发送完成到接收切换之间留有足够的延时(至少 1 个字符时间)
总结
SimpleModbus 作为早期 Arduino Modbus RTU 库,其代码简洁、功能明确,适合初学者理解 Modbus 协议的实现原理。但在实际项目中,推荐使用维护更活跃、功能更完善的替代方案:Arduino 官方库 ArduinoModbus(通用 Arduino 平台)或 esp-modbus(ESP32/ESP8266 平台)。无论选择哪个库,核心的 Modbus RTU 通信原理——从站地址、功能码、寄存器映射、CRC 校验——都是一致的。
发表回复