帮助朋友调试 MODBUS 通信设备,MODBUS 协议栈以前写过,但是这次也不想自己从头写一个 MODBUS 协议栈,太烦了。所以在 GitHub 上找到了 libModbus。
官网地址为 https://www.libmodbus.org/。提供一个标准 C 实现的 Modbus 协议栈。支持 TCP 和 RTU。
该协议栈支持 Linux, Mac OS X, Win32 等系统。
机器:Mac Book Pro。
GCC版本:4.2.1。机器显示如下:
% gcc --version
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/4.2.1
Apple clang version 12.0.0 (clang-1200.0.32.2)
Target: x86_64-apple-darwin19.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
libModbus 版本:使用最新的 3.1.6。
MODBUS设备:两个485接口的温湿度传感器,一个地址为 0x02,一个地址为 0x03。
我使用了一个 USB 转 485,内部芯片为 CH341。该芯片提供 Mac 的驱动。对应的下载地址为:http://www.wch.cn/download/CH341SER_MAC_ZIP.html。
下载后,安装对应驱动。插入 USB 转 485 设备。
% ls /dev/tty.
tty.Bluetooth-Incoming-Port% tty.usbserial-1410%
如上所示,tty.usbserial-1410 就是我机器上的串口。
在苹果商店搜索“串口调试助手”,下载安装即可。
上图就是在 MBP 上运行和设备通信的情况。
需要安装 autoconf、automake、libtool。可以使用命令行:
brew install autoconf automake libtool
进行安装。
解压文件,进入到对应的目录。使用命令行
./configure && make install
一般情况下,不会出现任何错误。
这样对应的库文件会安装在 /usr/local/lib 下。
% ls /usr/local/lib/libmo
libmodbus.5.dylib* libmodbus.la* libmongoose.2.dylib@ libmongoose.dylib@
libmodbus.dylib@ libmongoose.2.0.4.dylib@ libmongoose.a@ libmongoose_dbg.a@
如上所示,这样 libModbus 就编译好了。
头文件将安装在 /usr/local/include/modbus 目录下。
% ls /usr/local/include/modbus/
modbus-rtu.h modbus-tcp.h modbus-version.h modbus.h
本次设备为温湿度传感器。
发送:02 04 00 01 00 01 60 39。
其中 02 表示设备地址,04 表示 Modbus 功能码,00 01 表示寄存器地址为 1,00 01 表示数据长度为 1,60 39 是 CRC 校验。
将获得:02 04 02 01 2B BC BF
其中 02 表示设备地址,04 表示 Modbus 功能码,02 表示数据长度,01 2B 表示数据内容,BC BF 是 CRC 校验。
0x012B 对应的 10 进制数据为 299,也就是说温度为 29.9 摄氏度。
发送:02 04 00 02 00 01 90 39。
将获得:02 04 02 01 D5 3D 3F。
对应的湿度是 0x01D5,转换成十进制为 469,对应的湿度为 46.9%。
测试代码如下,使用标准 C,代码中都有注释。
#include
#include
#include
#include
#include
int main(int argc, char *argv[]) {
modbus_t *ctx;
int rc;
uint16_t tab_reg[64];
//打开
ctx = modbus_new_rtu("/dev/tty.usbserial-1410", 9600, 'N', 8, 1);
if (modbus_connect(ctx) == -1) {
fprintf(stderr, "Connection failed: %s\n",
modbus_strerror(errno));
modbus_free(ctx);
}
//打开调试模式
modbus_set_debug(ctx, 1);
modbus_set_response_timeout(ctx, 1, 0);//设置响应时间
//设置从机地址
modbus_set_slave(ctx, 2);
/* 从 1 寄存器读取 1 个地址,这样获得温度*/
rc = modbus_read_input_registers(ctx, 1, 1, tab_reg);
if (rc == -1) {
fprintf(stderr, "%s\n", modbus_strerror(errno));
return -1;
}
//输出结果
for (int i=0; i < rc; i++) {
printf("reg[%d]=%d (0x%X)\n", i, tab_reg[i], tab_reg[i]);
}
usleep(20000);
/* 从 2 寄存器读取 1 个地址,这样获得湿度*/
rc = modbus_read_input_registers(ctx, 2, 1, tab_reg);
if (rc == -1) {
fprintf(stderr, "%s\n", modbus_strerror(errno));
return -1;
}
//输出结果
for (int i=0; i < rc; i++) {
printf("reg[%d]=%d (0x%X)\n", i, tab_reg[i], tab_reg[i]);
}
usleep(20000);
//设置从机地址
modbus_set_slave(ctx, 3);
/* 从 1 寄存器读取 1 个地址,这样获得温度*/
rc = modbus_read_input_registers(ctx, 1, 1, tab_reg);
if (rc == -1) {
fprintf(stderr, "%s\n", modbus_strerror(errno));
return -1;
}
//输出结果
for (int i=0; i < rc; i++) {
printf("reg[%d]=%d (0x%X)\n", i, tab_reg[i], tab_reg[i]);
}
usleep(20000);
/* 从 2 寄存器读取 1 个地址,这样获得湿度*/
rc = modbus_read_input_registers(ctx, 2, 1, tab_reg);
if (rc == -1) {
fprintf(stderr, "%s\n", modbus_strerror(errno));
return -1;
}
//输出结果
for (int i=0; i < rc; i++) {
printf("reg[%d]=%d (0x%X)\n", i, tab_reg[i], tab_reg[i]);
}
usleep(20000);
//关闭设备
modbus_close(ctx);
modbus_free(ctx);
return 0;
}
% gcc mytest.c -o mytest `pkg-config --libs --cflags libmodbus`
这样将得到可执行文件 mytest。
% ./mytest
[02][04][00][01][00][01][60][39]
Waiting for a confirmation...
<02><04><02><01><3A><7C><B3>
reg[0]=314 (0x13A)
[02][04][00][02][00][01][90][39]
Waiting for a confirmation...
<02><04><02><01><B9><3D><12>
reg[0]=441 (0x1B9)
[03][04][00][01][00][01][61][E8]
Waiting for a confirmation...
<03><04><02><01><34><C0><B7>
reg[0]=308 (0x134)
[03][04][00][02][00][01][91][E8]
Waiting for a confirmation...
<03><04><02><01><C3><81><31>
reg[0]=451 (0x1C3)
可以看到获得的数据和上面解析的数据一致。说明已经可以使用 libModbus 进行 Modbus RTU 通信。
测试代码已经完成,下面开始写正式的工程代码。