modbus-utils
Modbus client and server command line tools based on libmodbus.
代码仓库
https://github.com/Krzysztow/modbus-utils
编译
for ubuntu:
sudo apt install libmodbus-dev
for buildroot:
BR2_PACKAGE_LIBMODBUS=y
编译:
$ git clone https://github.com/Krzysztow/modbus-utils
$ cd modbus-utils
$ git submodule update --init
$ mkdir build
$ cd build
$ cmake ..
$ make
modbus协议分析
modbus
协议是一种PLC
协议,目的当然是为了方便主控制器与从PLC
外设之间的通信。
为了避免重复编写,以下链接详细介绍了modbus rtu
和modbus tcp
协议报文的格式,有需要请自行阅读。
modbus tcp : IoTClient开发3 - ModBusTcp协议客户端实现
modbus rtu : IoTClient开发5 - ModBusRtu协议
调试modbus_client
功能选项:
-m : rtu / tcp
-a : slave id
-r : 写入或读取的开始地址
-t : 功能码,
(0x01) Read Coils, (0x02) Read Discrete Inputs, (0x05) Write Single Coil
(0x03) Read Holding Registers, (0x04) Read Input Registers, (0x06) WriteSingle Register
(0x0F) WriteMultipleCoils, (0x10) Write Multiple register
-b : 串口波特率
-d : 数据位
-s : 停止位
-p : odd 、 even 、none(奇偶校验)
-c : 读取的数目
示例命令:
./modbus_client --debug -mrtu -a1 -r1 -t0x10 -b38400 -d8 -s1 -peven /dev/ttyUSB0 0x1234 0x4321
注意点:
(1)专业词汇扫盲:
线圈寄存器:开关输出信号,读写一个寄存器的单位是1个字节,取值1或0,比如我想写入slave id
为1,地址为80开始的3个线圈
$ ./modbus_client --debug -mrtu -a1 -r80 -t0x0f -b38400 -d8 -s1 -peven /dev/ttyUSB0 0x1 0x1 0x1
Data to write: 0x01 0x01 0x01
Opening /dev/ttyUSB0 at 38400 bauds (E, 8, 1)
[01][0F][00][50][00][03][01][07][0E][99]
Waiting for a confirmation...
<01><0F><00><50><00><03><15><DB>
SUCCESS: written 3 elements!
写入线圈后,读取出来:
$ ./modbus_client --debug -mrtu -a1 -r80 -c3 -t0x01 -b38400 -d8 -s1 -peven /dev/ttyUSB0
Opening /dev/ttyUSB0 at 38400 bauds (E, 8, 1)
[01][01][00][50][00][03][7C][1A]
Waiting for a confirmation...
<01><01><01><07><10><4A>
SUCCESS: read 3 of elements:
Data: 0x01 0x01 0x01
离散输入寄存器:相当于线圈寄存器的只读模式,只能读不能写
保持寄存器:可读可写,读写一个寄存器的单位是2个字节,如下是写入的命令,
$ ./modbus_client --debug -mrtu -a1 -r1 -t0x10 -b38400 -d8 -s1 -peven /dev/ttyUSB0 0x1234 0x4321
Data to write: 0x1234 0x4321
Opening /dev/ttyUSB0 at 38400 bauds (E, 8, 1)
[01][10][00][01][00][02][04][12][34][43][21][87][FD]
Waiting for a confirmation...
<01><10><00><01><00><02><10><08>
SUCCESS: written 2 elements!
写入之后读取出来:
$ ./modbus_client --debug -mrtu -a1 -r1 -c2 -t0x03 -b38400 -d8 -s1 -peven /dev/ttyUSB0
Opening /dev/ttyUSB0 at 38400 bauds (E, 8, 1)
[01][03][00][01][00][02][95][CB]
Waiting for a confirmation...
<01><03><04><12><34><43><21><4F><AD>
SUCCESS: read 2 of elements:
Data: 0x1234 0x4321
输入寄存器:相当于保持寄存器的只读模式,只能读不能写。
(2)-r选项设定地址范围为0-99,不能超过该值,如果地址超过该范围,需要在命令行指定寄存器地址范围,参数使用示例如下:
--di 0xffff --co 0xffff --ir 0xffff --hr 0xffff
调试modbus_server
功能选项:
-m : rtu / tcp
-a : slave id
#若是rtu,有如下选项
-b : 串口波特率
-d : 数据位
-s : 停止位
-p : odd 、 even 、none(奇偶校验)
#若是tcp,有如下选项
-p:端口号
modbus_server
用来模拟PLC
设备
示例命令:
–di 0xffff --co 0xffff–ir 0xffff --hr 0xffff用来设定寄存器的范围
./modbus_server --di 0xffff --co 0xffff --ir 0xffff --hr 0xffff \
-mrtu -a0x1 -b38400 -d8 -s1 -peven /dev/ttyAMA2 --debug
./modbus_server --di 0xffff --co 0xffff --ir 0xffff --hr 0xffff \
-mtcp -a1 -p503 127.0.0.1 --debug
常见问题
Unable to listen TCP connection
这个问题是权限问题,参考代码:modbus-utils/libmodbus/src/modbus-tcp.c
端口号如果小于1024,运行modbus_server时,需要用root权限运行。
详细可以阅读下面代码,不做赘述。
int modbus_tcp_listen(modbus_t *ctx, int nb_connection)
{
int new_s;
int yes;
struct sockaddr_in addr;
modbus_tcp_t *ctx_tcp;
if (ctx == NULL) {
errno = EINVAL;
return -1;
}
ctx_tcp = ctx->backend_data;
#ifdef OS_WIN32
if (_modbus_tcp_init_win32() == -1) {
return -1;
}
#endif
new_s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (new_s == -1) {
return -1;
}
yes = 1;
if (setsockopt(new_s, SOL_SOCKET, SO_REUSEADDR,
(char *) &yes, sizeof(yes)) == -1) {
close(new_s);
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
//端口号小于1024的,必须以root权限运行
/* If the modbus port is < to 1024, we need the setuid root. */
addr.sin_port = htons(ctx_tcp->port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(new_s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
close(new_s);
return -1;
}
if (listen(new_s, nb_connection) == -1) {
close(new_s);
return -1;
}
return new_s;
}
阅读代码
上述阐述如有不清晰的地方,请直接在评论区下留言,或者自行阅读代码。