赞
踩
关于LibModbus驱动库的安装推荐参考:
[LibModbus驱动库的使用]([https://blog.csdn.net/whik1194/article/details/119010616]
代码如下(示例):
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "modbus.h" int main(int argc, char *argv[]){ int socket=-1; uint8_t *query; modbus_t *mb; int ret; _modbus_mapping_t *mb_mapping; mb=modbus_new_tcp("127.0.0.1", 1502); query = (uint8_t *)malloc(MODBUS_TCP_MAX_ADU_LENGTH); if((mb_mapping=modbus_mapping_new_start_address(0,0,0,0,15,3,0,0))==NULL){ modbus_free(mb); printf("new map failed: %s\n", modbus_strerror(errno)); return 0; } mb_mapping->tab_registers[0]=0x1001; mb_mapping->tab_registers[1]=0x1002; mb_mapping->tab_registers[2]=0x1003; socket = modbus_tcp_listen(mb, 1); modbus_tcp_accept(mb, &socket); printf("create modbus slave success\n"); while(1){ do{ /*轮询串口数据*/ ret=modbus_receive(mb,query); }while(ret==0); if(ret>0){ printf("len=%02d\t",ret); /*%02d格式限定符,其中0表示变量宽度不足时以0作为填充,2表示显示宽度至少为2,d表示十进制整数*/ for(int i=0; i<ret; ++i) printf("%02x", query[i]); printf("\n"); modbus_reply(mb,query,ret,mb_mapping); } else{ printf("quit the loop: %s\n", modbus_strerror(errno)); break; } } modbus_mapping_free(mb_mapping); modbus_close(mb); free(query); modbus_free(mb); return 0; }
代码如下(示例):
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "modbus.h" int main(int argc, char *argv[]){ uint16_t table[3]; modbus_t *mt; mt = modbus_new_tcp("127.0.0.1", 1502); if(modbus_connect(mt)==-1){ modbus_free(mt); printf("connect failed: %s\n", modbus_strerror(errno)); return 0; } while(1){ if(modbus_read_registers(mt, 0X0F, 3, table)==3) printf("read success: 0x%04x 0x%04x 0x%04x \n",table[0],table[1],table[2]);/*%04x格式限定符 ,其中0表示变量宽度不足时以0作为填充,4表示显示宽度至少为4,x表示十六进制整数*/ else{ printf("read error: %s\n", modbus_strerror(errno)); break; } for(int i=0; i<3; ++i) table[i]++; if(modbus_write_registers(mt, 0X0F, 3, table)==3) printf("write success: 0x%04x 0x%04x 0x%04x \n",table[0],table[1],table[2]); else{ printf("write error: %s\n", modbus_strerror(errno)); break; } Sleep(1000); } modbus_close(mt); modbus_free(mt); system("pause"); return 0; }
先运行从机使之进入监听状态,再运行主机两者即进入通讯状态。
./master_demo.exe
read success: 0x1001 0x1002 0x1003 #主机读取从机对应寄存器的值
write success: 0x1002 0x1003 0x1004 #主机将对应寄存器的值处理后再写入从机的寄存器
read success: 0x1002 0x1003 0x1004
write success: 0x1003 0x1004 0x1005
read success: 0x1003 0x1004 0x1005
write success: 0x1004 0x1005 0x1006
read success: 0x1004 0x1005 0x1006
write success: 0x1005 0x1006 0x1007
#保存寄存器既可以读也可以写
./slave_demo.exe
create modbus slave success
len=12 000100000006ff03000f0003
len=19 00020000000dff10000f000306100210031004
len=12 000300000006ff03000f0003
len=19 00040000000dff10000f000306100310041005
len=12 000500000006ff03000f0003
len=19 00060000000dff10000f000306100410051006
len=12 000700000006ff03000f0003
len=19 00080000000dff10000f000306100510061007
quit the loop: No error
Demo中的读保持寄存器的报文格式(以从站第1条报文为例):
事务处理标识符 | 协议标识符 | 长度(字节) | 单元标识符 | 功能码 | 起始寄存器高位 | 起始寄存器低位 | 寄存器数量高位 | 寄存器数量低位 |
---|---|---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 |
0001 | 0000 | 0006 | ff | 03 | 00 | 0f | 00 | 03 |
释义:读取服务器(标识符0xff)保持寄存器(功能码0x03),从0x000f(起始寄存器高位+起始寄存器低位)开始的0x0003(寄存器数量高位+寄存器数量低位)个寄存器的值。
Demo中的写保持寄存器的报文格式(以从站第2条报文为例):
事务处理标识符 | 协议标识符 | 长度(字节) | 单元标识符 | 功能码 | 起始寄存器高位 | 起始寄存器低位 | 寄存器数量高位 | 寄存器数量低位 | 字节计数 | 字节的值 |
---|---|---|---|---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 | 字节计数 |
0002 | 0000 | 000d | ff | 10 | 00 | 0f | 00 | 03 | 06 | 100210031004 |
释义:写入服务器(标识符0xff)保持寄存器(功能码0x10),从0x000f(起始寄存器高位+起始寄存器低位)开始的0x0003(寄存器数量高位+寄存器数量低位)个寄存器,写入数值为0x1002、0x1003、0x1004(每个保持寄存器占2字节)。
在2.1示例中作如下修改:
//增加输入寄存器的映射
if((mb_mapping=modbus_mapping_new_start_address(0,0,0,0,0x0f,0x03,0x20,0x03))==NULL){
modbus_free(mb);
printf("new map failed: %s\n", modbus_strerror(errno));
return 0;
}
mb_mapping->tab_registers[0]=0x1001;
mb_mapping->tab_registers[1]=0x1002;
mb_mapping->tab_registers[2]=0x1003;
#增加输入寄存器赋值
mb_mapping->tab_input_registers[0]=0x1001;
mb_mapping->tab_input_registers[1]=0x1002;
mb_mapping->tab_input_registers[2]=0x1003;
在2.2示例中作如下修改:
//将while(1)循环体内部代码段作如下修改
if(modbus_read_input_registers(mt, 0X20, 3, table)==3) #读取寄存器的函数需要改为modbus_read_input_registers(),若不修改将显示“Illegal data address”(非法数据地址),在LibModbus中每种寄存器的读写都有对应的方法,使用时它会检查寄存器地址是否合法
printf("read success: 0x%04x 0x%04x 0x%04x \n",table[0],table[1],table[2]);/*%04x格式限定符
,其中0表示变量宽度不足时以0作为填充,4表示显示宽度至少为4,x表示十六进制整数*/
else{
printf("read error: %s\n", modbus_strerror(errno));
break;
}
Sleep(1000);
先运行从机使之进入监听状态,再运行主机两者即进入通讯状态。
./master_demo.exe
read success: 0x1001 0x1002 0x1003
read success: 0x1001 0x1002 0x1003
read success: 0x1001 0x1002 0x1003
read success: 0x1001 0x1002 0x1003
read success: 0x1001 0x1002 0x1003
read success: 0x1001 0x1002 0x1003
read success: 0x1001 0x1002 0x1003
read success: 0x1001 0x1002 0x1003
#输入寄存器只可读不可写
./slave_demo.exe
create modbus slave success
len=12 000100000006ff0400200003
len=12 000200000006ff0400200003
len=12 000300000006ff0400200003
len=12 000400000006ff0400200003
len=12 000500000006ff0400200003
len=12 000600000006ff0400200003
len=12 000700000006ff0400200003
len=12 000800000006ff0400200003
Demo中的读保持寄存器的报文格式(以从站第1条报文为例):
事务处理标识符 | 协议标识符 | 长度(字节) | 单元标识符 | 功能码 | 起始寄存器高位 | 起始寄存器低位 | 寄存器数量高位 | 寄存器数量低位 |
---|---|---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 |
0001 | 0000 | 0006 | ff | 04 | 00 | 20 | 00 | 03 |
释义:读取服务器(标识符0xff)输入寄存器(功能码0x04)从0x0020(起始寄存器高位+起始寄存器低位)开始的0x0003(寄存器数量高位+寄存器数量低位)个寄存器的值。
在3.1示例中作如下修改:
//增加线圈状态寄存器的映射
if((mb_mapping=modbus_mapping_new_start_address(0x30,0x03,0,0,0x0f,0x03,0x20,0x03))==NULL){
modbus_free(mb);
printf("new map failed: %s\n", modbus_strerror(errno));
return 0;
}
mb_mapping->tab_bits[0]=true;
mb_mapping->tab_bits[1]=false;
mb_mapping->tab_bits[2]=false;
在3.2示例中作如下修改:
uint8_t table[3] //更改table数组的声明,libModbus中每个线圈寄存器占1字节 //将while(1)循环体内部代码段作如下修改 if(modbus_read_bits(mt, 0x30, 3, table)==3){ //读线圈寄存器状态 printf("read success:\n"); } else{ printf("read error: %s\n", modbus_strerror(errno)); break; } for(int i=0; i<3; ++i) printf("Line circuit %d: %s\n", i+1, table[i]?"ON":"OFF"); //将线圈寄存器值置反 table[0]=table[0]?false:true; table[1]=table[1]?false:true; table[2]=table[2]?false:true; if(modbus_write_bits(mt, 0X30, 3, table)==3) //写线圈寄存器状态 printf("write success:\n"); else{ printf("write error: %s\n", modbus_strerror(errno)); break; } printf("----------------------------------\n"); Sleep(1000);
先运行从机使之进入监听状态,再运行主机两者即进入通讯状态。
./master_demo.exe read success: Line circuit 1: ON Line circuit 2: OFF Line circuit 3: OFF write success: ---------------------------------- read success: Line circuit 1: OFF Line circuit 2: ON Line circuit 3: ON write success: ---------------------------------- read success: Line circuit 1: ON Line circuit 2: OFF Line circuit 3: OFF write success: #线圈寄存器支持读和写 ----------------------------------
./slave_demo.exe
create modbus slave success
len=12 000100000006ff0100300003
len=14 000200000008ff0f003000030106
len=12 000300000006ff0100300003
len=14 000400000008ff0f003000030101
len=12 000500000006ff0100300003
len=14 000600000008ff0f003000030106
Demo中的读线圈寄存器的报文格式(以从站第1条报文为例):
事务处理标识符 | 协议标识符 | 长度(字节) | 单元标识符 | 功能码 | 起始寄存器高位 | 起始寄存器低位 | 寄存器数量高位 | 寄存器数量低位 |
---|---|---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 |
0001 | 0000 | 0006 | ff | 01 | 00 | 30 | 00 | 03 |
释义:读取服务器(标识符0xff)线圈寄存器(功能码0x01)从0x0030(起始寄存器高位+起始寄存器低位)开始的0x0003(寄存器数量高位+寄存器数量低位)个线圈寄存器的状态值(开true或关false)。
Demo中的写线圈寄存器的报文格式(以从站第2条报文为例):
事务处理标识符 | 协议标识符 | 长度(字节) | 单元标识符 | 功能码 | 起始寄存器高位 | 起始寄存器低位 | 寄存器数量高位 | 寄存器数量低位 | 字节计数 | 字节的值 |
---|---|---|---|---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 | 字节计数 |
0002 | 0000 | 0008 | ff | 0f | 00 | 30 | 00 | 03 | 01 | 06 |
释义:写入服务器(标识符0xff)保持寄存器(功能码0x10),从0x000f(起始寄存器高位+起始寄存器低位)开始的0x0003(寄存器数量高位+寄存器数量低位)个寄存器,写入寄存器状态0x06(00000110,由低位开始算第一个线圈0,第二个线圈1,第3个线圈1)。
注意!Modbus报文中线圈是以bit位为存储单元的,而在libmodbus从机镜像中线圈是以byte字节为存储单元的。
在4.1示例中作如下修改:
//增加离散输入线圈状态寄存器的映射
if((mb_mapping=modbus_mapping_new_start_address(0x30,0x08,0x00,0x08,0x0f,0x03,0x20,0x03))==NULL){
modbus_free(mb);
printf("new map failed: %s\n", modbus_strerror(errno));
return 0;
}
mb_mapping->tab_input_bits[0]=true;
mb_mapping->tab_input_bits[1]=false;
mb_mapping->tab_input_bits[2]=false;
mb_mapping->tab_input_bits[3]=true;
mb_mapping->tab_input_bits[4]=false;
mb_mapping->tab_input_bits[5]=false;
mb_mapping->tab_input_bits[6]=false;
mb_mapping->tab_input_bits[7]=false;
在4.2示例中作如下修改:
//离散输入线圈的读取使用modbus_read_input_bits()函数
if(modbus_read_input_bits(mt, 0x00, 8, table)==8){
printf("read success:\n");
}
先运行从机使之进入监听状态,再运行主机两者即进入通讯状态。
./master_demo.exe
read success:
Line circuit 1: ON
Line circuit 2: OFF
Line circuit 3: OFF
Line circuit 4: ON
Line circuit 5: OFF
Line circuit 6: OFF
Line circuit 7: OFF
Line circuit 8: OFF
./slave_demo.exe
create modbus slave success
len=12 000100000006ff0200000008
len=12 000200000006ff0200000008
len=12 000300000006ff0200000008
len=12 000400000006ff0200000008
Demo中的读离散输入线圈寄存器的报文格式(以从站第1条报文为例):
事务处理标识符 | 协议标识符 | 长度(字节) | 单元标识符 | 功能码 | 起始寄存器高位 | 起始寄存器低位 | 寄存器数量高位 | 寄存器数量低位 |
---|---|---|---|---|---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 | 1字节 |
0001 | 0000 | 0006 | ff | 02 | 00 | 00 | 00 | 08 |
释义:读取服务器(标识符0xff)的离散输入线圈寄存器(功能码0x02)从0x0000(起始寄存器高位+起始寄存器低位)开始的0x0008(寄存器数量高位+寄存器数量低位)个线圈寄存器的状态值(开true或关false)。
主从机的定义、通讯建立、从机寄存器镜像的存储结构、线圈寄存器读取和写入、离散输入寄存器的读取、保持寄存器的读取和写入、输入寄存器的读取是Modbus通讯开发的基础,本文以ModbusTCP示例演示了libmodbus库相关接口的使用,希望对看到这篇博文的大家学习Modbus有所帮助。在这篇博文编辑的过程中发现LibModbus库中RTU从机无法进入监听,运行至connect()函数直接跳出,并无errno,希望路过的大神有了解的指正一下。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。