Modbus 协议通信库
libmodbus 是一个免费的开源软件库,用于与遵守 Modbus 协议的设备发送/接收数据。该库可以使用串口或以太网连接。库中的函数源自 Modicon Modbus 协议参考指南。
sudo apt-get update
sudo apt-get install libmodbus-dev
brew install libmodbus
# 安装依赖
sudo apt-get install autoconf automake libtool
# 克隆仓库
git clone https://github.com/stephane/libmodbus.git
cd libmodbus
# 生成配置脚本
./autogen.sh
# 配置、编译和安装
./configure --prefix=/usr/local
make
sudo make install
# 更新库缓存
sudo ldconfig
#include <iostream>
#include <modbus/modbus.h>
int main() {
modbus_t *ctx = NULL;
uint16_t tab_reg[32];
int rc;
int i;
// 创建 Modbus TCP 上下文
ctx = modbus_new_tcp("127.0.0.1", 502);
if (ctx == NULL) {
std::cerr << "无法创建 Modbus 上下文" << std::endl;
return -1;
}
// 连接到设备
if (modbus_connect(ctx) == -1) {
std::cerr << "连接失败: " << modbus_strerror(errno) << std::endl;
modbus_free(ctx);
return -1;
}
std::cout << "已连接到 Modbus 设备" << std::endl;
// 读取保持寄存器
rc = modbus_read_registers(ctx, 0, 0, 10, tab_reg);
if (rc == -1) {
std::cerr << "读取寄存器失败" << std::endl;
} else {
std::cout << "读取到 " << rc << " 个寄存器:" << std::endl;
for (i = 0; i < rc; i++) {
std::cout << "[" << i << "]: " << tab_reg[i] << std::endl;
}
}
// 关闭连接和释放资源
modbus_close(ctx);
modbus_free(ctx);
return 0;
}
#include <iostream>
#include <modbus/modbus.h>
int main() {
modbus_t *ctx = NULL;
uint16_t tab_reg[32];
int rc;
// 创建 Modbus RTU 上下文(串口)
ctx = modbus_new_rtu("/dev/ttyUSB0", 19200, 'N', 8, 1);
if (ctx == NULL) {
std::cerr << "无法创建 RTU 上下文" << std::endl;
return -1;
}
// 设置 RTU 模式
modbus_set_slave(ctx, 1); // 从站地址
modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS232);
modbus_rtu_set_rts(ctx, MODBUS_RTU_RTS_UP);
// 连接到设备
if (modbus_connect(ctx) == -1) {
std::cerr << "连接失败" << std::endl;
modbus_free(ctx);
return -1;
}
std::cout << "已连接到 RTU 设备" << std::endl;
// 读取输入寄存器
rc = modbus_read_input_registers(ctx, 0, 0, 10, tab_reg);
if (rc == -1) {
std::cerr << "读取失败" << std::endl;
} else {
for (int i = 0; i < rc; i++) {
std::cout << "输入寄存器 " << i << ": " << tab_reg[i] << std::endl;
}
}
modbus_close(ctx);
modbus_free(ctx);
return 0;
}
modbus_t *ctx = modbus_new_tcp("192.168.1.100", 502);
if (modbus_connect(ctx) == -1) {
std::cerr << "连接失败" << std::endl;
modbus_free(ctx);
return -1;
}
// 设置从站地址
modbus_set_slave(ctx, 1);
// 写入单个保持寄存器
uint16_t value = 1234;
int rc = modbus_write_register(ctx, 0, value);
if (rc == -1) {
std::cerr << "写入失败: " << modbus_strerror(errno) << std::endl;
} else {
std::cout << "写入成功,值: " << value << std::endl;
}
modbus_close(ctx);
modbus_free(ctx);
modbus_t *ctx = modbus_new_tcp("192.168.1.100", 502);
modbus_connect(ctx);
modbus_set_slave(ctx, 1);
// 准备数据
uint16_t tab_reg[10];
for (int i = 0; i < 10; i++) {
tab_reg[i] = i * 100;
}
// 写入多个保持寄存器
int rc = modbus_write_registers(ctx, 0, 10, tab_reg);
if (rc == -1) {
std::cerr << "批量写入失败" << std::endl;
} else {
std::cout << "成功写入 " << rc << " 个寄存器" << std::endl;
}
modbus_close(ctx);
modbus_free(ctx);
modbus_t *ctx = modbus_new_tcp("192.168.1.100", 502);
modbus_connect(ctx);
modbus_set_slave(ctx, 1);
// 读取线圈
uint8_t tab_bits[32];
int rc = modbus_read_bits(ctx, 0, 10, tab_bits);
if (rc == -1) {
std::cerr << "读取线圈失败" << std::endl;
} else {
std::cout << "读取到 " << rc << " 个线圈:" << std::endl;
for (int i = 0; i < rc; i++) {
std::cout << "线圈 " << i << ": " << (tab_bits[i] ? "ON" : "OFF") << std::endl;
}
}
modbus_close(ctx);
modbus_free(ctx);
modbus_t *ctx = modbus_new_tcp("192.168.1.100", 502);
modbus_connect(ctx);
modbus_set_slave(ctx, 1);
// 写入单个线圈
int rc = modbus_write_bit(ctx, 0, 1); // 地址 0,值 1 (ON)
if (rc == -1) {
std::cerr << "写入线圈失败" << std::endl;
} else {
std::cout << "线圈设置成功" << std::endl;
}
// 写入多个线圈
uint8_t tab_bits[2] = {1, 0};
rc = modbus_write_bits(ctx, 0, 2, tab_bits);
modbus_close(ctx);
modbus_free(ctx);
#include <iostream>
#include <modbus/modbus.h>
#include <modbus/modbus-tcp.h>
#include <unistd.h>
#define MODBUS_TCP_PORT 502
#define SERVER_ADDRESS "0.0.0.0"
int main() {
int socket;
modbus_t *ctx;
modbus_mapping_t *mb_mapping;
uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
int rc;
// 创建 Modbus TCP 上下文
ctx = modbus_new_tcp(NULL, MODBUS_TCP_PORT);
if (ctx == NULL) {
std::cerr << "无法创建上下文" << std::endl;
return -1;
}
// 创建映射表
mb_mapping = modbus_mapping_new(500, 500, 500, 500);
if (mb_mapping == NULL) {
std::cerr << "无法创建映射" << std::endl;
modbus_free(ctx);
return -1;
}
// 设置初始数据
for (int i = 0; i < 500; i++) {
mb_mapping->tab_registers[i] = i;
}
// 启动服务器
socket = modbus_tcp_listen(ctx, 1);
if (socket == -1) {
std::cerr << "无法启动服务器" << std::endl;
modbus_mapping_free(mb_mapping);
modbus_free(ctx);
return -1;
}
std::cout << "Modbus TCP 服务器运行中..." << std::endl;
// 接受连接
while (1) {
int client_socket = modbus_tcp_accept(ctx, &socket);
if (client_socket == -1) {
continue;
}
std::cout << "客户端已连接" << std::endl;
// 处理请求
while (1) {
rc = modbus_receive(ctx, query);
if (rc > 0) {
// 处理查询
rc = modbus_reply(ctx, query, rc, mb_mapping);
} else if (rc == -1) {
break;
}
}
close(client_socket);
}
modbus_mapping_free(mb_mapping);
modbus_free(ctx);
return 0;
}
modbus_t *ctx = modbus_new_tcp("192.168.1.100", 502);
// 设置响应超时(毫秒)
struct timeval response_timeout;
response_timeout.tv_sec = 1; // 1秒
response_timeout.tv_usec = 0;
modbus_set_response_timeout(ctx, &response_timeout);
// 设置字节间超时
struct timeval byte_timeout;
byte_timeout.tv_sec = 0;
byte_timeout.tv_usec = 500000; // 500毫秒
modbus_set_byte_timeout(ctx, &byte_timeout);
// 调试模式(打印 Modbus 帧)
modbus_set_debug(ctx, TRUE);
// 设置重试次数
int retries = 3;
while (retries-- > 0) {
int rc = modbus_read_registers(ctx, 0, 0, 10, tab_reg);
if (rc != -1) break;
std::cout << "重试... 剩余: " << retries << std::endl;
usleep(100000); // 等待 100ms
}
| 功能 | 功能码 | libmodbus 函数 |
|---|---|---|
| 读取线圈 | 0x01 | modbus_read_bits() |
| 读取离散输入 | 0x02 | modbus_read_input_bits() |
| 读取保持寄存器 | 0x03 | modbus_read_registers() |
| 读取输入寄存器 | 0x04 | modbus_read_input_registers() |
| 写入单个线圈 | 0x05 | modbus_write_bit() |
| 写入单个寄存器 | 0x06 | modbus_write_register() |
| 写入多个寄存器 | 0x10 | modbus_write_registers() |