探索Modbus通信库:libmodbus版本检测与功能探索

freeFree Technical Resource

This content is free to read, suitable for basic learning and search traffic.

探索Modbus通信库:libmodbus版本检测与功能探索

libmodbus是C语言生态中最流行、最成熟的Modbusプロトコル库,由Stéphane Raimbault开发维护,广泛应用于工业自動化、エンベデッド·イン系统、IoTゲートウェイ等领域。本記事详细介绍libmodbus库的版本检测方法、核心功能、API使用、编译インストール以及完全的サンプルコード,帮助開発者たちはスピードマスター。libmodbus的使用方法。

一、libmodbus库概述

1.1 核心特性

  • 多协议サポート:Modbus RTU、Modbus TCP、Modbus ASCII三种协议
  • 主从兼备:既可作为クライアント側は(master),也可作为サーバー側。(slave)
  • 跨プラットフォーム:サポートLinux、Windows、Mac OS、FreeBSD、embeddedLinux等
  • 功能完全:サポート01-16すべての標準機能コード,以及部分自定义機能コード
  • 高いパフォーマンスを:C语言実装,リソース占用小,适合エンベデッド·イン环境
  • 线程セキュリティ:每个modbus_tContextより独立,多线程使用需各自作成实例
  • 开源無料:LGPL 2.1协议,可無料用于商业项目
  • ドキュメント完善:官方ドキュメント详细,サンプル丰富

1.2 版本历史

版本Published主要更新
3.1.102023年最新稳定版,修复多个bug
3.1.02020年增加Modbus TCPサーバー·サーバーサポート,改进RTU
3.0.02018年重大重构,API变化
2.9.32017年2.x系列最后版本
2.0.02012年增加TCPサポート,改进RTU

二、版本检测方法

2.1 编译时版本检测

#include <stdio.h>
#include <modbus.h>

int main() {
    // libmodbus提供了版本宏
    printf("libmodbus版本:%s\n", LIBMODBUS_VERSION_STRING);
    printf("主版本号:%d\n", LIBMODBUS_VERSION_MAJOR);
    printf("次版本号:%d\n", LIBMODBUS_VERSION_MINOR);
    printf("补丁版本号:%d\n", LIBMODBUS_VERSION_MICRO);
    
    // 版本号数值形式,便于比较(部分版本可能未定义此宏)
#ifdef LIBMODBUS_VERSION
    printf("版本数值:%d\n", LIBMODBUS_VERSION);
#endif
    
    // サンプル出力:
    // libmodbus版本:3.1.10
    // 主版本号:3
    // 次版本号:1
    // 补丁版本号:10
    // 版本数值:3.1.10
    
    return 0;
}

2.2 运行时版本检测

#include <stdio.h>
#include <modbus.h>
#include <dlfcn.h>

/* 运行时检测libmodbus版本(用于动态链接库) */
void check_libmodbus_version() {
    // 方法1:使用版本宏(编译时确定)
    printf("编译时版本:%s\n", LIBMODBUS_VERSION_STRING);
    
    // 方法2:経由pkg-config查询(命令行)
    // pkg-config --modversion libmodbus
    
    // 方法3:チェック特定函数存在するか
    // modbus_new_tcp_pi是3.0+版本的函数
    void *handle = dlopen("libmodbus.so", RTLD_LAZY);
    if (handle) {
        void *func = dlsym(handle, "modbus_new_tcp_pi");
        if (func) {
            printf("检测到modbus_new_tcp_pi函数,版本>=3.0\n");
        } else {
            printf("未检测到modbus_new_tcp_pi函数,版本<3.0\n");
        }
        dlclose(handle);
    }
}

int main() {
    check_libmodbus_version();
    return 0;
}

2.3 命令行版本检测

# 方法1:pkg-config
pkg-config --modversion libmodbus
# 出力:3.1.10

# 方法2:表示库文件
ls -la /usr/lib/x86_64-linux-gnu/libmodbus*
# 出力:libmodbus.so.5 -> libmodbus.so.5.1.0

# 方法3:dpkg(Debian/Ubuntu)
dpkg -l | grep libmodbus
# 出力:ii  libmodbus-dev  3.1.10-1  amd64

# 方法4:rpm(CentOS/RHEL)
rpm -qa | grep modbus
# 出力:libmodbus-3.1.10-1.el8.x86_64

# 方法5:编译测试程序
cat > test_version.c << 'EOF'
#include <stdio.h>
#include <modbus.h>
int main() { printf("%s\n", LIBMODBUS_VERSION_STRING); return 0; }
EOF
gcc test_version.c -o test_version `pkg-config --cflags --libs libmodbus`
./test_version
# 出力:3.1.10

三、编译インストール

3.1 Linux源码编译

# インストール依赖
sudo apt-get install build-essential autoconf automake libtool

# 下载源码
wget https://github.com/stephane/libmodbus/archive/refs/tags/v3.1.10.tar.gz
tar xzf v3.1.10.tar.gz
cd libmodbus-3.1.10

# 编译インストール
./autogen.sh
./configure --prefix=/usr/local
make -j4
sudo make install
sudo ldconfig

# 验证インストール
pkg-config --modversion libmodbus
# 出力:3.1.10

3.2 包管理器インストール

# Debian/Ubuntu
sudo apt-get install libmodbus-dev

# CentOS/RHEL
sudo yum install libmodbus-devel

# Fedora
sudo dnf install libmodbus-devel

# Arch Linux
sudo pacman -S libmodbus

# Mac OS (Modbus中国語ネットワークbrew)
brew install libmodbus

3.3 Windows编译

# 使用MSYS2/MinGW编译
pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-libmodbus

# 或者使用CMake
mkdir build
cd build
cmake .. -G "MinGW Makefiles"
mingw32-make
mingw32-make install

四、核心API详解

4.1 Contextより作成与连接

#include <stdio.h>
#include <stdlib.h>
#include <modbus.h>
#include <errno.h>

int main() {
    modbus_t *ctx;
    int rc;

    /* === 作成TCPContextより === */
    ctx = modbus_new_tcp("192.168.1.100", 502);
    if (ctx == NULL) {
        fprintf(stderr, "作成TCPContextより失敗:%s\n", modbus_strerror(errno));
        return -1;
    }

    /* === 作成RTUContextより === */
    // ctx = modbus_new_rtu("/dev/ttyUSB0", 9600, 'N', 8, 1);
    // modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485);

    /* === 設定駅からの住所 === */
    modbus_set_slave(ctx, 1);

    /* === 設定デバッグモード(打印通讯データ) === */
    modbus_set_debug(ctx, TRUE);

    /* === 設定レスポンス·タイムアウト === */
    struct timeval response_timeout;
    response_timeout.tv_sec = 3;
    response_timeout.tv_usec = 0;
    modbus_set_response_timeout(ctx, &response_timeout);

    /* === 建立连接 === */
    rc = modbus_connect(ctx);
    if (rc == -1) {
        fprintf(stderr, "接続の失敗:%s\n", modbus_strerror(errno));
        modbus_free(ctx);
        return -1;
    }
    printf("接続成功です。!\n");

    /* === 业务逻辑 === */
    // ...

    /* === 接続を閉じる === */
    modbus_close(ctx);
    modbus_free(ctx);
    return 0;
}

4.2 読み取り操作。

/* function code01:リードコイルを読む */
uint8_t tab_coils[MODBUS_MAX_READ_BITS];  // 最多2000位
rc = modbus_read_bits(ctx, 0, 8, tab_coils);
if (rc == -1) {
    fprintf(stderr, "リードコイルを読む失敗:%s\n", modbus_strerror(errno));
} else {
    printf("読み取り到%d个コイルの状態:\n", rc);
    for (int i = 0; i < rc; i++) {
        printf("  コイル%d:%s\n", i, tab_coils[i] ? "吸合" : "断开");
    }
}

/* function code02:read discrete inputs */
uint8_t tab_inputs[MODBUS_MAX_READ_BITS];
rc = modbus_read_input_bits(ctx, 0, 4, tab_inputs);

/* function code03:read holding registers */
uint16_t tab_reg[MODBUS_MAX_READ_REGISTERS];  // 最多125个
rc = modbus_read_registers(ctx, 0, 10, tab_reg);
if (rc == -1) {
    fprintf(stderr, "读レジスタ失敗:%s\n", modbus_strerror(errno));
} else {
    printf("読み取り到%dRegisters:\n", rc);
    for (int i = 0; i < rc; i++) {
        printf("  register%d:%d\n", i, tab_reg[i]);
    }
}

/* function code04:read input registers */
uint16_t tab_input_reg[MODBUS_MAX_READ_REGISTERS];
rc = modbus_read_input_registers(ctx, 0, 2, tab_input_reg);
if (rc != -1) {
    float temperature = tab_input_reg[0] / 10.0;
    float humidity = tab_input_reg[1] / 10.0;
    printf("temperature:%.1f℃,湿度:%.1f%%\n", temperature, humidity);
}

4.3 書き込み操作。

/* function code05:write single coil */
rc = modbus_write_bit(ctx, 0, TRUE);   // 吸合
if (rc == -1) {
    fprintf(stderr, "写コイル失敗:%s\n", modbus_strerror(errno));
}
modbus_write_bit(ctx, 0, FALSE);  // 断开

/* function code06:write single register */
rc = modbus_write_register(ctx, 0, 1234);
if (rc == -1) {
    fprintf(stderr, "写レジスタ失敗:%s\n", modbus_strerror(errno));
}

/* function code15:write multiple coils */
uint8_t tab_write_coils[] = {TRUE, FALSE, TRUE, FALSE};
rc = modbus_write_bits(ctx, 0, 4, tab_write_coils);

/* function code16:write multiple registers */
uint16_t tab_write_reg[] = {100, 200, 300};
rc = modbus_write_registers(ctx, 0, 3, tab_write_reg);

4.4 浮動小数点数処理

/* read32ビット浮動小数点数(ABCDビッグエンディアン) */
uint16_t regs[2];
modbus_read_registers(ctx, 10, 2, regs);

// 方法1:使用modbus_get_float
float value = modbus_get_float(regs);
printf("浮動小数点数值:%.2f\n", value);

// 方法2:手动转换
uint32_t bits = ((uint32_t)regs[0] << 16) | regs[1];
float value2;
memcpy(&value2, &bits, sizeof(float));
printf("浮動小数点数值:%.2f\n", value2);

/* 写入32ビット浮動小数点数 */
float write_value = 25.5;
uint16_t write_regs[2];
modbus_set_float(write_value, write_regs);
modbus_write_registers(ctx, 10, 2, write_regs);

/* 不同バイトオーダー的浮動小数点数処理(libmodbus 3.1+) */
// modbus_get_float_abcd  → ABCD(ビッグエンディアン,高字ハイバイト)
// modbus_get_float_cdab  → CDAB(Word exchange,低字ハイバイト)
// modbus_get_float_dcba  → DCBA(リトルエンディアン,低字低バイト数)
// modbus_get_float_badc  → BADC(バイトスワップ,高字低バイト数)
// modbus_get_float       → デフォルト等同于 abcd(ビッグエンディアン)
//
// バイトオーダー説明(以32位值0x41420000为例,A=0x41,B=0x42,C=0x00,D=0x00):
//   ABCD: reg[0]=0x4142, reg[1]=0x0000  (標準ビッグエンディアン,高字ハイバイト)
//   CDAB: reg[0]=0x0000, reg[1]=0x4142  (Word exchange,前に低い言葉。)
//   DCBA: reg[0]=0x0000, reg[1]=0x4241  (リトルエンディアン,低字低バイト数)
//   BADC: reg[0]=0x4241, reg[1]=0x0000  (バイトスワップ,高字低バイト数)

五、Modbus RTUシリアルポート通讯

#include <modbus.h>
#include <modbus-rtu.h>

int main() {
    modbus_t *ctx;
    uint16_t regs[10];

    // 作成RTUContextより
    ctx = modbus_new_rtu("/dev/ttyUSB0",  // Serial Port Device
                         9600,            // Baud rate
                         'N',             // check digit(N/E/O)
                         8,               // data bit
                         1);              // stop bit
    if (ctx == NULL) {
        fprintf(stderr, "作成RTUContextより失敗\n");
        return -1;
    }

    // 設定RS485モード
    modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485);
    // MODBUS_RTU_RS232 = RS232モード
    // MODBUS_RTU_RS485 = RS485モード

    // 設定RTSモード(部分RS485変換器です。需要)
    modbus_rtu_set_rts(ctx, MODBUS_RTU_RTS_UP);
    // MODBUS_RTU_RTS_NONE = 不控制RTS
    // MODBUS_RTU_RTS_UP = 送信时RTS高电平
    // MODBUS_RTU_RTS_DOWN = 送信时RTS低电平

    // 設定駅からの住所
    modbus_set_slave(ctx, 1);

    // connect
    if (modbus_connect(ctx) == -1) {
        fprintf(stderr, "RTU接続の失敗:%s\n", modbus_strerror(errno));
        modbus_free(ctx);
        return -1;
    }

    // Read and hold register
    int rc = modbus_read_registers(ctx, 0, 10, regs);
    if (rc != -1) {
        for (int i = 0; i < rc; i++) {
            printf("register%d:%d\n", i, regs[i]);
        }
    }

    modbus_close(ctx);
    modbus_free(ctx);
    return 0;
}

六、Modbus TCPServer(slave)実装

#include <stdio.h>
#include <stdlib.h>
#include <modbus.h>
#include <modbus-tcp.h>
#include <unistd.h>

#define NB_COILS     1024
#define NB_INPUTS    1024
#define NB_REGISTERS 1024

int main() {
    modbus_t *ctx;
    modbus_mapping_t *mb_mapping;
    int server_socket;
    uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
    int rc;

    // 作成TCPContextより(监听すべてのアドレス)
    ctx = modbus_new_tcp("0.0.0.0", 502);
    if (ctx == NULL) {
        fprintf(stderr, "作成Contextより失敗\n");
        return -1;
    }

    // 作成データ·マッピング
    mb_mapping = modbus_mapping_new(
        NB_COILS,      // コイルの数
        NB_INPUTS,     // ショップ型入力数量
        NB_REGISTERS,  // レジスタを保持する。数量
        NB_REGISTERS   // 入力レジスタ。数量
    );
    if (mb_mapping == NULL) {
        fprintf(stderr, "作成データ·マッピング失敗:%s\n", modbus_strerror(errno));
        modbus_free(ctx);
        return -1;
    }

    // 設定初期値は
    mb_mapping->tab_registers[0] = 100;  // temperature
    mb_mapping->tab_registers[1] = 50;   // 湿度
    mb_mapping->tab_bits[0] = TRUE;      // Relay1

    // 启动TCP监听
    server_socket = modbus_tcp_listen(ctx, 5);  // 最大5个连接
    if (server_socket == -1) {
        fprintf(stderr, "リスニングの失敗:%s\n", modbus_strerror(errno));
        modbus_mapping_free(mb_mapping);
        modbus_free(ctx);
        return -1;
    }
    printf("Modbus TCPサーバー·サーバー已启动,监听ポート:502\n");

    // メイン·サイクル
    for (;;) {
        // 接受クライアント側は连接
        int client_socket = modbus_tcp_accept(ctx, &server_socket);
        if (client_socket == -1) {
            continue;
        }
        printf("クライアント側は连接\n");

        // 処理クライアント側はリクエスト
        for (;;) {
            rc = modbus_receive(ctx, query);
            if (rc > 0) {
                // 回复リクエスト
                modbus_reply(ctx, query, rc, mb_mapping);
            } else {
                // 连接断开或エラー
                break;
            }
        }

        printf("クライアント側は断开\n");
        close(client_socket);
    }

    close(server_socket);
    modbus_mapping_free(mb_mapping);
    modbus_free(ctx);
    return 0;
}

七、完全工具类封装

/* modbus_util.h */
#ifndef MODBUS_UTIL_H
#define MODBUS_UTIL_H

#include <modbus.h>

typedef struct {
    modbus_t *ctx;
    const char *host;
    int port;
    int slave_id;
} ModbusUtil;

/* 作成と接続。 */
ModbusUtil* modbus_util_connect(const char *host, int port, int slave_id);

/* 切断する */
void modbus_util_disconnect(ModbusUtil *mu);

/* read holding registers(带重试) */
int modbus_util_read_regs(ModbusUtil *mu, int addr, int count, uint16_t *regs);

/* 读32ビット浮動小数点数 */
float modbus_util_read_float(ModbusUtil *mu, int addr);

/* 写32ビット浮動小数点数 */
int modbus_util_write_float(ModbusUtil *mu, int addr, float value);

/* write single register */
int modbus_util_write_reg(ModbusUtil *mu, int addr, uint16_t value);

/* write single coil */
int modbus_util_write_coil(ModbusUtil *mu, int addr, int value);

/* リードコイルを読む */
int modbus_util_read_coils(ModbusUtil *mu, int addr, int count, uint8_t *coils);

#endif

/* modbus_util.c */
#include "modbus_util.h"
#include <stdio.h>
#include <unistd.h>

ModbusUtil* modbus_util_connect(const char *host, int port, int slave_id) {
    ModbusUtil *mu = malloc(sizeof(ModbusUtil));
    mu->host = host;
    mu->port = port;
    mu->slave_id = slave_id;

    for (int attempt = 0; attempt < 3; attempt++) {
        mu->ctx = modbus_new_tcp(host, port);
        if (mu->ctx == NULL) continue;

        modbus_set_slave(mu->ctx, slave_id);
        struct timeval tv = {3, 0};
        modbus_set_response_timeout(mu->ctx, &tv);

        if (modbus_connect(mu->ctx) != -1) {
            printf("接続成功です。:%s:%d\n", host, port);
            return mu;
        }
        modbus_free(mu->ctx);
        sleep(1);
    }
    free(mu);
    return NULL;
}

void modbus_util_disconnect(ModbusUtil *mu) {
    if (mu) {
        modbus_close(mu->ctx);
        modbus_free(mu->ctx);
        free(mu);
    }
}

int modbus_util_read_regs(ModbusUtil *mu, int addr, int count, uint16_t *regs) {
    for (int attempt = 0; attempt < 3; attempt++) {
        int rc = modbus_read_registers(mu->ctx, addr, count, regs);
        if (rc != -1) return rc;
        fprintf(stderr, "Read failed(No.%d次):%s\n", attempt+1, modbus_strerror(errno));
        modbus_close(mu->ctx);
        modbus_connect(mu->ctx);
    }
    return -1;
}

float modbus_util_read_float(ModbusUtil *mu, int addr) {
    uint16_t regs[2];
    if (modbus_util_read_regs(mu, addr, 2, regs) != 2) return 0;
    return modbus_get_float(regs);
}

int modbus_util_write_float(ModbusUtil *mu, int addr, float value) {
    uint16_t regs[2];
    modbus_set_float(value, regs);
    return modbus_write_registers(mu->ctx, addr, 2, regs);
}

int modbus_util_write_reg(ModbusUtil *mu, int addr, uint16_t value) {
    return modbus_write_register(mu->ctx, addr, value);
}

int modbus_util_write_coil(ModbusUtil *mu, int addr, int value) {
    return modbus_write_bit(mu->ctx, addr, value);
}

int modbus_util_read_coils(ModbusUtil *mu, int addr, int count, uint8_t *coils) {
    return modbus_read_bits(mu->ctx, addr, count, coils);
}

/* 使用例の例 */
int main() {
    ModbusUtil *mb = modbus_util_connect("192.168.1.100", 502, 1);
    if (!mb) {
        fprintf(stderr, "接続の失敗\n");
        return -1;
    }

    // 読み取り温度
    float temp = modbus_util_read_float(mb, 10);
    printf("temperature:%.2f℃\n", temp);

    // read101つのレジスタ
    uint16_t regs[10];
    modbus_util_read_regs(mb, 0, 10, regs);

    // 控制继电器
    modbus_util_write_coil(mb, 0, TRUE);

    modbus_util_disconnect(mb);
    return 0;
}

八、编译与链接

# 简单编译
gcc main.c -o main `pkg-config --cflags --libs libmodbus`

# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(modbus_demo)

find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBMODBUS REQUIRED libmodbus)

add_executable(main main.c)
target_include_directories(main PRIVATE ${LIBMODBUS_INCLUDE_DIRS})
target_link_libraries(main ${LIBMODBUS_LIBRARIES})

# Makefile
CC = gcc
CFLAGS = `pkg-config --cflags libmodbus` -Wall
LDFLAGS = `pkg-config --libs libmodbus`

main: main.c
	$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)

clean:
	rm -f main

九、よくある質問与ベストプラクティス

9.1 common problems

  • 接続タイムアウト:チェックIPaddress、ポート、ファイアウォール,確認デバイスオンライン
  • 例外コードを返す:チェック駅からの住所、function code、アドレス範囲是否正确
  • RTUシリアルポート打不开:チェックシリアルデバイス名,確認权限(加入dialout组)
  • CRCError:RTUモード下libmodbus自动処理CRC,不要手动追加
  • 浮動小数点数解析エラー:機器の確認バイトオーダー,使用对应的函数(modbus_get_float_abcd/cdab/dcba/badc)
  • 多线程問題:每个线程使用独立的modbus_tContextより,不要共享
  • 内存泄漏:使用modbus_mapping_free释放マッピング,modbus_free释放Contextより

9.2 ベストプラクティス

  • 使用pkg-config:経由pkg-config取得编译参数,避免路径問題
  • バッチ読み取り:尽量一次読み取り多个连续レジスタ,减少通讯次数
  • 合理タイムアウト:局域网1-3秒,RTUserial port3-5秒
  • 追加重试:网络不稳定时自動再試行2-3次
  • エラー処理:每次调用チェック戻り値は,使用modbus_strerror取得エラー説明
  • デバッグモード:开发阶段开启modbus_set_debug,表示元の通讯データ
  • リソース释放:程序退出前调用modbus_close和modbus_free
  • 版本兼容:使用LIBMODBUS_VERSION_CHECK宏检测版本,确保API兼容

十、libmodbus与其他C语言Modbus库对比

库名协议サポートサーバー側。サポート活跃度適用可能なシーン
libmodbusRTU/TCP/ASCIIサポート大多数项目首选
freemodbusRTU/ASCII仅スレーブ埋め込みスレーブ駅デバイス
modbus-arduinoRTU/TCPサポートArduinoプラットフォーム
mbusRTU/TCP仅マスター简单マスター应用

libmodbus是C语言Modbus开发的首选库,其完善的功能和活跃的社区使其在工业自動化、エンベデッド·イン系统、IoTゲートウェイ等领域得到广泛应用。本記事提供的版本检测方法、API详解和工具类封装可直接使用する。于项目开发,建议先使用Modbus Poll機器の確認通信は正常,再将C代码集成到实际项目中。

Related Tags
Put this resource to use in a real project?

Go to the Tool Center for message parsing, CRC verification and device debugging, or submit your requirements for selection and integration advice.

Engineer Membership

Turn this article into actionable debugging resources

After activation, you can use advanced message parsing, resource pack downloads, code examples, engineering cases and priority technical support, suitable for real project delivery.

Unlimited Advanced Tools
Resource & Code Packs
Complete Engineering Case Library
Priority Technical Support

Leave a Reply

Your email address will not be published. Required fields are marked *.