← 返回库列表

📡 libmodbus 使用指南

Modbus 协议通信库

项目地址:https://github.com/stephane/libmodbus

官方文档:https://libmodbus.org/documentation/

📖 什么是 libmodbus?

libmodbus 是一个免费的开源软件库,用于与遵守 Modbus 协议的设备发送/接收数据。该库可以使用串口或以太网连接。库中的函数源自 Modicon Modbus 协议参考指南。

🚀 安装方式

Linux (Ubuntu/Debian)

sudo apt-get update
sudo apt-get install libmodbus-dev

macOS (Homebrew)

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

💡 基础使用

Modbus TCP 客户端 - 读取寄存器

#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;
}

Modbus RTU 客户端 - 串口通信

#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);

Modbus TCP 服务器

#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
}

🎯 Modbus 功能码速查

功能 功能码 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()

📚 更多资源