libmodbus是一个基于C语言实现的modbus驱动库,支持
Linux
、Mac OS X
、Win32
等操作系统。支持如下功能:
- 支持
Modbus-RTU
。- 支持
Modbus-TCP
。- 支持常用功能码(01/02/03/04/05/06/07/0F/10/11/16/17)。
- 支持线圈类型读写、寄存器读写、离散量读取等。
- 支持广播地址
0
,从机地址1-247
。- 支持浮点数和整型数据转换,大小端等多种模式。
官网最新版本:libmodbus-3.1.7.tar.gz
开源仓库地址:https://github.com/stephane/libmodbus/
Linux终端下执行如下命令完成解压:
sudo tar zxvf libmodbus-3.1.7.tar.gz
解压后的libmodbus-3.1.7目录结构如下:
.
├── acinclude.m4
├── aclocal.m4
├── AUTHORS
├── build-aux/
├── config.h.in
├── configure*
├── configure.ac
├── COPYING.LESSER
├── doc/
├── libmodbus.pc.in
├── m4/
├── Makefile.am
├── Makefile.in
├── MIGRATION
├── NEWS
├── README.md
├── src/
└── tests/
Linux终端下执行如下命令生成config.h配置文件:
./configure
生成后提示如下:
libmodbus 3.1.7
===============
prefix: /usr/local
sysconfdir: ${prefix}/etc
libdir: ${exec_prefix}/lib
includedir: ${prefix}/include
compiler: gcc
cflags: -g -O2
ldflags:
documentation: no
tests: yes
使用Modbus Slave模拟从站设备,libmodbus作为主站读写从站保持寄存器。
(1)Modbus Slave连接配置
点击Connection,选择Connect,弹出Connection Setup对话框,Connection选择Modbus TCP/IP
,IP Address填写为192.168.3.100
(电脑本机地址),Port填写为502
。
(2)Modbus Slave从机配置
点击Setup,选择Slave Definition,Slave ID填写为1
,Function选择为03 Holding Register(4x)
,Address填写为0
,Quantity填写为10
。
(3)poll_wr_regs_demo读写例程
/**
* @file poll_wr_regs_demo.c
* @author 李云亮 ([email protected])
* @brief modbus主站读写多个保持寄存器
* @version 1.0.0
* @date 2022-08-19
*
* @copyright Copyright (c) 2022
*
*/
#include
#include
#include
#include "modbus.h"
#define MODBUS_TCP_SERVER_IP "192.168.3.100" // TCP IP地址
#define MODBUS_TCP_SERVER_PORT 502 // TCP 端口号
#define REG_ADDR_START 0 // 寄存器起始地址
#define REG_ADDR_END 9 // 寄存器结束地址
#define REG_NUM ((REG_ADDR_END) - (REG_ADDR_START) + 1) // 寄存器个数
int main(int argc, char const *argv[])
{
/* code */
int i = 0;
int ret = 0;
modbus_t* ctx = NULL;
uint16_t* sendBuf = NULL;
uint16_t* recvBuf = NULL;
// 1.创建TCP
ctx = modbus_new_tcp(MODBUS_TCP_SERVER_IP, MODBUS_TCP_SERVER_PORT);
if (NULL == ctx)
{
printf("Set modbus TCP failed!\n");
return -1;
}
// 2.设置调试模式
ret = modbus_set_debug(ctx, TRUE);
if (-1 == ret)
{
printf("Set modbus debug mode failed!\n");
}
// 3.连接Server
ret = modbus_connect(ctx);
if (-1 == ret)
{
printf("Connect modbus server failed!\n");
modbus_free(ctx);
return -1;
}
// 4.设置从机地址
ret = modbus_set_slave(ctx, 1);
// 5.申请内存
sendBuf = (uint16_t*)malloc(REG_NUM * (sizeof(uint16_t)));
if (NULL == sendBuf)
{
printf("modbus sendBuf malloc failed!\n");
free(sendBuf);
return -1;
}
recvBuf = (uint16_t*)malloc(REG_NUM * (sizeof(uint16_t)));
if (NULL == recvBuf)
{
printf("modbus recvBuf malloc failed!\n");
free(recvBuf);
return -1;
}
memset(sendBuf, 0, REG_NUM);
memset(recvBuf, 0, REG_NUM);
// 6.写入多个保持寄存器
for (i = 0; i < REG_NUM; i++)
{
sendBuf[i] = i;
}
ret = modbus_write_registers(ctx, REG_ADDR_START, REG_NUM, sendBuf);
if (REG_NUM != ret)
{
printf("modbus write regs failed!\n");
return -1;
}
else
{
// 7.读多个保持寄存器
ret = modbus_read_registers(ctx, REG_ADDR_START, REG_NUM, recvBuf);
if (REG_NUM != ret)
{
printf("modbus read regs failed!\n");
return -1;
}
else
{
printf("result data:\n");
for (i = 0; i < REG_NUM; i++)
{
printf("%d ", recvBuf[i]);
}
printf("\n");
}
}
// 8.释放内存
free(sendBuf);
free(recvBuf);
// 9.断开连接
modbus_close(ctx);
modbus_free(ctx);
return 0;
}
(4)编译与执行
lyl@ubuntu18:~/Desktop/gitee/libmodbus-demo/libmodbusUse$ make
+ Linking output/demo/poll_wr_regs_demo ...
lyl@ubuntu18:~/Desktop/gitee/libmodbus-demo/libmodbusUse$ ./output/demo/poll_wr_regs_demo
Connecting to 192.168.3.100:502
[00][01][00][00][00][1B][01][10][00][00][00][0A][14][00][00][00][01][00][02][00][03][00][04][00][05][00][06][00][07][00][08][00][09]
Waiting for a confirmation...
<00><01><00><00><00><06><01><10><00><00><00><0A>
[00][02][00][00][00][06][01][03][00][00][00][0A]
Waiting for a confirmation...
<00><02><00><00><00><17><01><03><14><00><00><00><01><00><02><00><03><00><04><00><05><00><06><00><07><00><08><00><09>
result data:
0 1 2 3 4 5 6 7 8 9
(5)数据帧分析
主站写寄存器分析
# 主站写多个寄存器
[00][01][00][00][00][1B][01][10][00][00][00][0A][14][00][00][00][01][00][02][00][03][00][04][00][05][00][06][00][07][00][08][00][09]
通信标识符 | 协议标识符 | 数据长度 | 从机地址 | 功能码 | 起始地址 | 寄存器数量 | 数据长度 | 数据 |
---|---|---|---|---|---|---|---|---|
00 01 | 00 00 | 00 1B | 01 | 0x10(16) | 00 00 | 00 0A(10) | 0x14(20) | 0-9 |
# 从站返回写结果
<00><01><00><00><00><06><01><10><00><00><00><0A>
通信标识符 | 协议标识符 | 数据长度 | 从机地址 | 功能码 | 起始地址 | 寄存器数量 |
---|---|---|---|---|---|---|
00 01 | 00 00 | 00 06 | 01 | 0x10(16) | 00 00 | 00 0A(10) |
主站读寄存器分析
# 主站读多个寄存器
[00][02][00][00][00][06][01][03][00][00][00][0A]
通信标识符 | 协议标识符 | 数据长度 | 从机地址 | 功能码 | 起始地址 | 寄存器数量 |
---|---|---|---|---|---|---|
00 02 | 00 00 | 00 06 | 01 | 03 | 00 00 | 00 0A(10) |
# 从站返回读结果
<00><02><00><00><00><17><01><03><14><00><00><00><01><00><02><00><03><00><04><00><05><00><06><00><07><00><08><00><09>
通信标识符 | 协议标识符 | 数据长度 | 从机地址 | 功能码 | 数据长度 | 数据 |
---|---|---|---|---|---|---|
00 02 | 00 00 | 00 17(23) | 01 | 03 | 0x14(20) | 0-9 |
使用Modbus Poll模拟主站设备,libmodbus作为从站。
(1)Modbus Poll连接配置
点击Connection,选择Connect,弹出Connection Setup对话框,Connection选择Modbus TCP/IP
,IP Address填写为192.168.3.102
(Ubuntu从机地址),Port填写为1502
。
(2)Modbus Slave主机配置
点击Setup,选择Read/Write Definition,Slave ID填写为1
,Function选择为03 Holding Register(4x)
,Address填写为0
,Quantity填写为10
。
(3)slave_wr_regs_demo读写例程
/**
* @file slave_wr_regs_demo.c
* @author 李云亮 ([email protected])
* @brief modbus从站读写例程
* @version 1.0.0
* @date 2022-08-25
*
* @copyright Copyright (c) 2022
*
*/
#include
#include
#include
#include
#include "modbus.h"
#define MODBUS_SLAVE_TCP_SERVER_IP "192.168.3.102"
#define MODBUS_SLAVE_TCP_SERVER_PORT 1502
#define MAPPING_SIZE 10
#define MODBUS_POLL_CONNECT_MAX 1
int main(int argc, char const *argv[])
{
/* code */
int socket = -1;
modbus_t *ctx;
modbus_mapping_t *mb_mapping;
uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH] = {0};
int rc = 0;
int i = 0;
// 1.创建从机TCP
ctx = modbus_new_tcp(MODBUS_SLAVE_TCP_SERVER_IP, MODBUS_SLAVE_TCP_SERVER_PORT); //开发板ip自行修改
if (NULL == ctx)
{
printf("Set modbus TCP failed!\n");
return -1;
}
// 2.设置调试模式
modbus_set_debug(ctx, TRUE);
// 3.初始化寄存器
mb_mapping = modbus_mapping_new(MAPPING_SIZE, MAPPING_SIZE, MAPPING_SIZE, MAPPING_SIZE);
if (mb_mapping == NULL) {
printf("Failed to allocate the mapping!\n");
modbus_free(ctx);
return -1;
}
for (i = 0; i < MAPPING_SIZE; i++)
{
mb_mapping->tab_registers[i] = i;
}
// 4.侦听主站连接
socket = modbus_tcp_listen(ctx, MODBUS_POLL_CONNECT_MAX);
if (-1 == socket)
{
printf("Unable to listen TCP!\n");
modbus_free(ctx);
return -1;
}
// 5.创建连接
modbus_tcp_accept(ctx, &socket);
while (1)
{
// 6.接收请求
rc = modbus_receive(ctx, query);
if (rc > 1) {
// 7.发送响应
modbus_reply(ctx, query, rc, mb_mapping);
printf("In the loop \n");
}
else
{
// Connection closed by the client or error
modbus_mapping_free(mb_mapping);
modbus_close(ctx);
modbus_free(ctx);
modbus_tcp_accept(ctx, &socket);
break;
}
}
return 0;
}