libmodbus 是支持Linux,Mac OS X,QNX和win32 的一個modbus庫,支持modbus TCP和RTU 兩種模式。
libmodbus 的官網:https://www.libmodbus.org/
當前版本爲:v3.1.6
編譯
我在win10 下的wsl 下編譯的,相信在ubuntu linux 下也大同小異。
下載解壓 libmodbus 的源碼,F:lib modbus-3.1.6文件夾中。
進入wsl 的console,輸入下述命令完成編譯
cd /mnt/f/libmodbus-3.1.6
./configure
make
libmodbus 包中包含了tests 目錄,其中有一些測試程序,已經同時被編譯。
運行測試程序
./bandwidth_server_one
另外開啓一個wsl console 窗口
輸入 ./bandwidth_client
可以看到輸出信息
bandwidth_client /bandwidth_server是用來測試modbus TCP 速率的測試程序。相對比較簡單。下面列出了bandwidth_client 的程序源碼
/*
* Copyright © 2008-2014 Stéphane Raimbault <[email protected]>
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#ifndef _MSC_VER
#include <unistd.h>
#include <sys/time.h>
#endif
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <modbus.h>
#define G_MSEC_PER_SEC 1000
static uint32_t gettime_ms(void)
{
struct timeval tv;
#if !defined(_MSC_VER)
gettimeofday(&tv, NULL);
return (uint32_t) tv.tv_sec * 1000 + tv.tv_usec / 1000;
#else
return GetTickCount();
#endif
}
enum {
TCP,
RTU
};
/* Tests based on PI-MBUS-300 documentation */
int main(int argc, char *argv[])
{
uint8_t *tab_bit;
uint16_t *tab_reg;
modbus_t *ctx;
int i;
int nb_points;
double elapsed;
uint32_t start;
uint32_t end;
uint32_t bytes;
uint32_t rate;
int rc;
int n_loop;
int use_backend;
if (argc > 1) {
if (strcmp(argv[1], "tcp") == 0) {
use_backend = TCP;
n_loop = 100000;
} else if (strcmp(argv[1], "rtu") == 0) {
use_backend = RTU;
n_loop = 100;
} else {
printf("Usage:\n %s [tcp|rtu] - Modbus client to measure data bandwith\n\n", argv[0]);
exit(1);
}
} else {
/* By default */
use_backend = TCP;
n_loop = 100000;
}
if (use_backend == TCP) {
ctx = modbus_new_tcp("127.0.0.1", 1502);
} else {
ctx = modbus_new_rtu("/dev/ttyUSB1", 115200, 'N', 8, 1);
modbus_set_slave(ctx, 1);
}
if (modbus_connect(ctx) == -1) {
fprintf(stderr, "Connection failed: %s\n",
modbus_strerror(errno));
modbus_free(ctx);
return -1;
}
/* Allocate and initialize the memory to store the status */
tab_bit = (uint8_t *) malloc(MODBUS_MAX_READ_BITS * sizeof(uint8_t));
memset(tab_bit, 0, MODBUS_MAX_READ_BITS * sizeof(uint8_t));
/* Allocate and initialize the memory to store the registers */
tab_reg = (uint16_t *) malloc(MODBUS_MAX_READ_REGISTERS * sizeof(uint16_t));
memset(tab_reg, 0, MODBUS_MAX_READ_REGISTERS * sizeof(uint16_t));
printf("READ BITS\n\n");
nb_points = MODBUS_MAX_READ_BITS;
start = gettime_ms();
for (i=0; i<n_loop; i++) {
rc = modbus_read_bits(ctx, 0, nb_points, tab_bit);
if (rc == -1) {
fprintf(stderr, "%s\n", modbus_strerror(errno));
return -1;
}
}
end = gettime_ms();
elapsed = end - start;
rate = (n_loop * nb_points) * G_MSEC_PER_SEC / (end - start);
printf("Transfert rate in points/seconds:\n");
printf("* %d points/s\n", rate);
printf("\n");
bytes = n_loop * (nb_points / 8) + ((nb_points % 8) ? 1 : 0);
rate = bytes / 1024 * G_MSEC_PER_SEC / (end - start);
printf("Values:\n");
printf("* %d x %d values\n", n_loop, nb_points);
printf("* %.3f ms for %d bytes\n", elapsed, bytes);
printf("* %d KiB/s\n", rate);
printf("\n");
/* TCP: Query and reponse header and values */
bytes = 12 + 9 + (nb_points / 8) + ((nb_points % 8) ? 1 : 0);
printf("Values and TCP Modbus overhead:\n");
printf("* %d x %d bytes\n", n_loop, bytes);
bytes = n_loop * bytes;
rate = bytes / 1024 * G_MSEC_PER_SEC / (end - start);
printf("* %.3f ms for %d bytes\n", elapsed, bytes);
printf("* %d KiB/s\n", rate);
printf("\n\n");
printf("READ REGISTERS\n\n");
nb_points = MODBUS_MAX_READ_REGISTERS;
start = gettime_ms();
for (i=0; i<n_loop; i++) {
rc = modbus_read_registers(ctx, 0, nb_points, tab_reg);
if (rc == -1) {
fprintf(stderr, "%s\n", modbus_strerror(errno));
return -1;
}
}
end = gettime_ms();
elapsed = end - start;
rate = (n_loop * nb_points) * G_MSEC_PER_SEC / (end - start);
printf("Transfert rate in points/seconds:\n");
printf("* %d registers/s\n", rate);
printf("\n");
bytes = n_loop * nb_points * sizeof(uint16_t);
rate = bytes / 1024 * G_MSEC_PER_SEC / (end - start);
printf("Values:\n");
printf("* %d x %d values\n", n_loop, nb_points);
printf("* %.3f ms for %d bytes\n", elapsed, bytes);
printf("* %d KiB/s\n", rate);
printf("\n");
/* TCP:Query and reponse header and values */
bytes = 12 + 9 + (nb_points * sizeof(uint16_t));
printf("Values and TCP Modbus overhead:\n");
printf("* %d x %d bytes\n", n_loop, bytes);
bytes = n_loop * bytes;
rate = bytes / 1024 * G_MSEC_PER_SEC / (end - start);
printf("* %.3f ms for %d bytes\n", elapsed, bytes);
printf("* %d KiB/s\n", rate);
printf("\n\n");
printf("WRITE AND READ REGISTERS\n\n");
nb_points = MODBUS_MAX_WR_WRITE_REGISTERS;
start = gettime_ms();
for (i=0; i<n_loop; i++) {
rc = modbus_write_and_read_registers(ctx,
0, nb_points, tab_reg,
0, nb_points, tab_reg);
if (rc == -1) {
fprintf(stderr, "%s\n", modbus_strerror(errno));
return -1;
}
}
end = gettime_ms();
elapsed = end - start;
rate = (n_loop * nb_points) * G_MSEC_PER_SEC / (end - start);
printf("Transfert rate in points/seconds:\n");
printf("* %d registers/s\n", rate);
printf("\n");
bytes = n_loop * nb_points * sizeof(uint16_t);
rate = bytes / 1024 * G_MSEC_PER_SEC / (end - start);
printf("Values:\n");
printf("* %d x %d values\n", n_loop, nb_points);
printf("* %.3f ms for %d bytes\n", elapsed, bytes);
printf("* %d KiB/s\n", rate);
printf("\n");
/* TCP:Query and reponse header and values */
bytes = 12 + 9 + (nb_points * sizeof(uint16_t));
printf("Values and TCP Modbus overhead:\n");
printf("* %d x %d bytes\n", n_loop, bytes);
bytes = n_loop * bytes;
rate = bytes / 1024 * G_MSEC_PER_SEC / (end - start);
printf("* %.3f ms for %d bytes\n", elapsed, bytes);
printf("* %d KiB/s\n", rate);
printf("\n");
/* Free the memory */
free(tab_bit);
free(tab_reg);
/* Close the connection */
modbus_close(ctx);
modbus_free(ctx);
return 0;
}
上面的例子測試讀10萬個點,讀10萬個寄存器,讀寫十萬次的時間。
主要用法
上下文類型context
前面已經提到,libmodbus 支持TCP和RTU 兩種通信方式,前一種是支持TCP/IP 協議的,而RTU 是支持RS485 串口的協議。libmodbus 庫的設計將TCP/RTU 模式的實現細節在用戶端屏蔽掉了。modbus 的類型數據放置在一個結構類型中,當新建一個modbus 接口時,填入有關的參數,並將這個包含接口參數的結構指針返回給用戶,這個指針稱爲上下文類型。
例如:
在以後的各種操作中,用戶只要帶有contex 參數就可以了,不再關心是modbus TCP 還是modbus RTU 。這兩種模式的各種函數都是一樣的。
modbus_t *ctx;
ctx = modbus_new_tcp("127.0.0.1", 1502);
if (ctx == NULL) {
fprintf(stderr, "Unable to allocate libmodbus context\n");
return -1;
}
if (modbus_connect(ctx) == -1) {
fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno));
modbus_free(ctx);
return -1;
}
顯然,這是一種優雅的程序設計方式。
讀比特位
rc = modbus_write_bit(ctx, addr, tab_rq_bits[0]);
寫比特位
rc = modbus_read_bits(ctx, addr, 1, tab_rp_bits);
讀寄存器
int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
寫寄存器
int modbus_write_register(modbus_t *ctx, int addr, const uint16_t value);
讀寫寄存器
int modbus_write_and_read_registers(modbus_t *ctx, int write_addr, int write_nb, const uint16_t *src, int read_addr, int read_nb, const uint16_t *dest);