赞
踩
搜索有大把的概括,感兴趣就搜,我就精简的写。
废话不多说,直接看程序。
server设备一直维护四个table:
client设备发送报文读/写server数据,报文中需要包含的内容:
其实如果不打算做处理报文的程序,单纯只维护寄存器表时,这部分不用看,因为各个开源程序已经封装好处理报文的程序,调用函数就行。
4.1 功能码(libmodbus 程序中的定义)
4.2 client向server发送 的request报文
报文构成:
帧头 | tcp/ip协议表示 | 该字节以后的长度 | 设备地址 | 功能码 | 数据 |
---|---|---|---|---|---|
2字节 | 0x00 00 | 2字节 | 1字节 | 1字 | 根据功能码确定 |
2字节 | 2字节 |
---|---|
寄存器起始地址 | 读寄存器的数目 |
2字节 | 2字节 |
---|---|
寄存器起始地址 | 数据(0x0000 ,代表0; 0xff00,代表1) |
2字节 | 2字节 |
---|---|
寄存器起始地址 | 数据 |
2字节 | 2字节 | 一个字节 | 写数据个数/8 个字节 |
---|---|---|---|
寄存器起始地址 | 写数据个数 | 写数据个数/8 | 具体算法看代码即可 |
例如写9个数据,第三部分数值为0x02
2字节 | 2字节 | 一个字节 | 2*n 个字节 |
---|---|---|---|
寄存器起始地址 | 写数据个数 | 后面的字节数目 | 写每个数据两个字节 |
2字节 |
---|
寄存器起始地址 |
2字节 |
---|
寄存器起始地址 |
2字节 | 2个字节 | 2个字节 |
---|---|---|
寄存器起始地址 | and | or |
最终数据为
data = (data & and) | (or & (~and));
2字节 | 2个字节 | 2个字节 | 2字节 | 1字节 | 2*(写寄存器数目n) 字节 |
---|---|---|---|---|---|
读寄存器起始地址 | 读寄存器数目 | 写寄存器起始地址 | 写寄存器数目 | 之后的字节数 | 写寄存器数值 |
举例:读holding register
03 6D | 00 00 | 00 06 | 01 | 03 | 00 00 | 00 0A |
---|---|---|---|---|---|---|
主机发送的检验信息(client发送的request的次序) | 表示tcp/ip的协议是modbus协议 | 表示该字节以后的长度 | 设备地址 | 功能码 | 寄存器起始地址 | 读寄存器数目 |
报文构成
帧头 | tcp/ip协议表示 | 长度 | 设备地址 | 功能码 | 数据长度 | 数据 |
---|---|---|---|---|---|---|
2字节 | 0x00 00 | 2字节 | 1字节 | 1字节 | 1字节 | 由具体功能决定 |
举例:返回holding register数据
03 6D | 00 00 | 00 17 | 01 | 03 | 14 | 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
---|---|---|---|---|---|---|
检验信息 | tcp/ip为modbus协议 | 表示该字节后的长度 | 设备地址 | 功能码 | 该字节后的字节数 |
具体返回的数据部分看代码,懒得写了
了解完就开干。 github搞到源代码,其中还有test例程可以参考。
- #include "modbus/modbus.h"
- class myclass:{
- public:
- myclass();
- virtual ~myclass();
- void listenDevice();
- private:
- std::string ip_;
- int port_;
- int server_address_;
- modbus_t* ctx_;
- modbus_mapping_t* mb_mapping_;
- std::shared_ptr<std::thread> revThread_;
- bool timer_run_;
- }
- myclass::myclass()
- {
- ip_ = "127.0.0.1"; //ip
- port_ = 502; //端口号
- server_address_ = 1; //地址
- timer_run_ = true;
- ctx_ = modbus_new_tcp(ip_.c_str(),port_); //初始化
- mb_mapping_ = NULL; //数据字典
- if(ctx_ == NULL){
- printf("Unable to allocate libmodbus context :%s",modbus_strerror(errno));
- }
- printf("finished_init");
- revThread_ = std::make_shared<std::thread>(std::bind(&myclass::listenDevice,this));
- }
-
- myclass::~myclass() {
- timer_run_ = false;
- if(ctx_ != NULL){
- modbus_free(ctx_);
- }
- if(mb_mapping_ !=NULL){
- modbus_mapping_free(mb_mapping_);
- }
- }
-
- void myclass::listenDevice(){
- while(1){
- printf("begin listen device");
- if(!timer_run_){
- return;
- }
- if(ctx_ == NULL){
- printf("Unable to allocate libmodbus context");
- return;
- }
- modbus_set_debug(ctx_,true);
-
- //server需要listen
- int server_socket = modbus_tcp_listen(ctx_,10);
- if(server_socket == -1){
- printf("Unable to listen TCP: %s",modbus_strerror(errno));
- continue;
- }
-
- modbus_tcp_accept(ctx_, &server_socket);
- //初始数据字典
- mb_mapping_ = modbus_mapping_new(MODBUS_MAX_READ_BITS,
- MODBUS_MAX_WRITE_BITS,
- MODBUS_MAX_READ_REGISTERS,
- MODBUS_MAX_WRITE_REGISTERS);
- if (mb_mapping_ == NULL) {
- printf("Failed to allocate the mapping: %s",modbus_strerror(errno));
- continue;
- }
- //设置字典的数据
- for (int i=0;i<20;i++)
- {
- mb_mapping_->tab_input_registers[i] = i;
- mb_mapping_->tab_registers[i] = i;
- }
-
- while(1){
- uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
- //接受client报文
- int rc = modbus_receive(ctx_, query);
- if (rc > 0) {
- //处理client报文
- modbus_reply(ctx_, query, rc, mb_mapping_);
- } else{
- printf("error %s\n", modbus_strerror(errno));
- break;
- }
- if(!timer_run_){
- return;
- }
- }
- close(server_socket);
- }
- }

来自client的报文解析在modbus_reply中。源代码看一下就好,结构很清晰,想处理报文就改这部分。
(118条消息) libmodbus tcp/ip client_虎哥的铲屎员-CSDN博客_libmodbus tcp
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。