libmodbus tcp/ip client

68 篇文章 15 订阅

文章目录

      • 1.modbus概况
      • 2.各语言的推荐 Modbus 库
      • 3. 我对modbus程序的理解
      • 4.tcp/ip 报文解析
        • 4.1 功能码(libmodbus 程序中的定义)
        • 4.2 client向server发送 的request报文
          • 4.2.1 client发送报文,各个功能码对应的数据部分
          • 4.2.2 报文举例
        • 4.3 server的response 报文
      • 5.写个代码(libmodbus) tcp/ip client

1.modbus概况

搜索有大把的概括,感兴趣就搜,我就精简的写。

  • 是一种通讯协议,分客户端/主机(client/master)和服务端/从机(server/slave)。
  • 客户端/主机 向 服务器/从机 发送报文读写数据。
  • 服务器/从机 只能在客户端/主机查询时返回数据,不能主动向客户端/主机发送数据(当然可以改协议)。

2.各语言的推荐 Modbus 库

3. 我对modbus程序的理解

废话不多说,直接看程序。

server设备一直维护四个table:

  • Coils: 保存二进制数据,可读可写
  • Discrete inputs: 保存二进制数据,只可读
  • input registers: 保存16进制数据,只可读
  • holding registers: 保存16进制数据,可读可写

client设备发送报文读/写server数据,报文中需要包含的内容:

  • server地址(因为一个client可以连接多个server,需要指定哪个server)
  • function功能码(对哪个寄存器表,进行读或写)
  • 寄存器具体地址
  • 和function功能码对应的具体数据

4.tcp/ip 报文解析

其实如果不打算做处理报文的程序,单纯只维护寄存器表时,这部分不用看,因为各个开源程序已经封装好处理报文的程序,调用函数就行。

4.1 功能码(libmodbus 程序中的定义)

  • 0x01 :MODBUS_FC_READ_COILS ,读cols
  • 0x02 :MODBUS_FC_READ_DISCRETE_INPUTS,读discrete inputs
  • 0x03 :MODBUS_FC_READ_HOLDING_REGISTERS,读holding register
  • 0x04 :MODBUS_FC_READ_INPUT_REGISTERS, 读input register
  • 0x05 :MODBUS_FC_WRITE_SINGLE_COIL, 写一个 coil
  • 0x06 :MODBUS_FC_WRITE_SINGLE_REGISTER, 写一个register
  • 0x07 :MODBUS_FC_READ_EXCEPTION_STATUS, 读exception status
  • 0x0F :MODBUS_FC_WRITE_MULTIPLE_COILS ,写多个coils
  • 0x10 :MODBUS_FC_WRITE_MULTIPLE_REGISTERS ,写多个register
  • 0x11 :MODBUS_FC_REPORT_SLAVE_ID, 上报地址
  • 0x16 :MODBUS_FC_MASK_WRITE_REGISTER ,对一个数据进行处理
  • 0x17 :MODBUS_FC_WRITE_AND_READ_REGISTERS ,读写registers

4.2 client向server发送 的request报文

报文构成:

帧头tcp/ip协议表示该字节以后的长度设备地址功能码数据
2字节0x00 002字节1字节1字根据功能码确定

4.2.1 client发送报文,各个功能码对应的数据部分

  • 读操作: 0x01, 0x02, 0x03, 0x04
2字节2字节
寄存器起始地址读寄存器的数目
  • 0x05: 写一个coil
2字节2字节
寄存器起始地址数据(0x0000 ,代表0; 0xff00,代表1)
  • 0x06,写一个register
2字节2字节
寄存器起始地址数据
  • 0x0f,写多个coils
2字节2字节一个字节写数据个数/8 个字节
寄存器起始地址写数据个数写数据个数/8具体算法看代码即可

例如写9个数据,第三部分数值为0x02

  • 0x10 :写多个register
2字节2字节一个字节2*n 个字节
寄存器起始地址写数据个数后面的字节数目写每个数据两个字节
  • 0x11 : 上报地址
2字节
寄存器起始地址
  • 0x07, 读exception status
2字节
寄存器起始地址
  • 0x16 : 处理数据
2字节2个字节2个字节
寄存器起始地址andor

最终数据为

data = (data & and) | (or & (~and));
  • 1
  • 0x17 : 读写
2字节2个字节2个字节2字节1字节2*(写寄存器数目n) 字节
读寄存器起始地址读寄存器数目写寄存器起始地址写寄存器数目之后的字节数写寄存器数值

4.2.2 报文举例

举例:读holding register

03 6D00 0000 06010300 0000 0A
主机发送的检验信息(client发送的request的次序)表示tcp/ip的协议是modbus协议表示该字节以后的长度设备地址功能码寄存器起始地址读寄存器数目

4.3 server的response 报文

报文构成

帧头tcp/ip协议表示长度设备地址功能码数据长度数据
2字节0x00 002字节1字节1字节1字节由具体功能决定

举例:返回holding register数据

03 6D00 0000 1701031400 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
检验信息tcp/ip为modbus协议表示该字节后的长度设备地址功能码该字节后的字节数

具体返回的数据部分看代码,懒得写了

5.写个代码(libmodbus) tcp/ip client

了解完就开干。 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

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在VB.NET中,可以使用TCP/IP协议来进行客户端的发送和接收操作。 要创建一个TCP/IP客户端,首先我们需要导入`System.Net.Sockets`命名空间,并创建一个`TcpClient`实例,这个实例可以用来与服务器进行通信。 假设我们已经知道服务器IP地址和端口号,我们可以使用`TcpClient`实例的`Connect`方法连接到服务器。例如: ```vb Dim client As New TcpClient() client.Connect("服务器IP地址", 端口号) ``` 接下来,我们可以使用`TcpClient`实例的`GetStream`方法获得一个`NetworkStream`实例。`NetworkStream`类用于在网络上发送和接收数据。 ```vb Dim stream As NetworkStream = client.GetStream() ``` 现在我们可以使用`NetworkStream`实例的`Write`方法发送数据到服务器。例如,发送一个字符串: ```vb Dim data As String = "Hello, server!" Dim bytes As Byte() = Encoding.ASCII.GetBytes(data) stream.Write(bytes, 0, bytes.Length) ``` 同样,我们也可以使用`NetworkStream`实例的`Read`方法从服务器接收数据。例如,接收服务器发来的回应: ```vb Dim buffer As Byte() = New Byte(client.ReceiveBufferSize - 1) {} Dim bytesRead As Integer = stream.Read(buffer, 0, buffer.Length) Dim responseData As String = Encoding.ASCII.GetString(buffer, 0, bytesRead) ``` 最后,我们可以关闭`NetworkStream`和`TcpClient`实例,释放资源: ```vb stream.Close() client.Close() ``` 这样,我们就完成了使用VB.NET进行TCP/IP客户端的发送和接收操作。请注意,在实际应用中,可能需要处理更多的异常和错误情况,并进行适当的错误处理和重试。 ### 回答2: TCP/IP是一种广泛应用于计算机网络通信的协议。在VB.NET中,我们可以使用TcpClient类来创建一个TCP/IP的客户端,并进行发送和接收数据的操作。 首先,我们需要在VB.NET项目中引入System.Net命名空间,以便使用TcpClient类。然后,我们可以通过实例化TcpClient类来创建一个TCP/IP客户端对象。 创建对象的代码如下: Dim client As New TcpClient 然后,我们可以使用TcpClient类的Connect方法来连接到服务器。Connect方法接受一个IP地址和端口号作为参数。 连接服务器的代码示例如下: client.Connect("服务器IP地址", 端口号) 连接成功后,我们就可以使用TcpClient类的GetStream方法来获取数据流。数据流提供了发送和接收数据的功能。 获取数据流的代码示例如下: Dim stream As NetworkStream = client.GetStream() 发送数据非常简单,我们可以使用数据流的Write方法将数据发送到服务器。Write方法接受一个字节数组作为参数,将字节数组中的数据发送出去。 发送数据的代码示例如下: Dim data As Byte() = Encoding.ASCII.GetBytes("需要发送的数据") stream.Write(data, 0, data.Length) 接收数据也很简单,我们可以使用数据流的Read方法从服务器接收数据。Read方法接受一个字节数组作为参数,并将接收到的数据存储在字节数组中。 接收数据的代码示例如下: Dim buffer As Byte() = New Byte(1023) {} Dim bytesRead As Integer = stream.Read(buffer, 0, buffer.Length) Dim receivedData As String = Encoding.ASCII.GetString(buffer, 0, bytesRead) 最后,我们可以使用TcpClient类的Close方法关闭TCP/IP客户端连接。 关闭连接的代码示例如下: client.Close() 通过以上步骤,我们可以在VB.NET中创建一个TCP/IP客户端,并进行发送和接收数据的操作。 ### 回答3: VB.NET中实现TCP/IP客户端的发送和接收可以通过使用Socket类来完成。下面是一个简单的示例: 首先,在VB.NET中引入System.Net.Sockets命名空间。 ``` Imports System.Net.Sockets ``` 然后,创建一个TcpClient对象来建立与服务器的连接。 ``` Dim client As TcpClient = New TcpClient() client.Connect("服务器IP地址", 服务器端口号) ``` 接下来,可以使用网络流来发送和接收数据。 发送数据: ``` Dim data As String = "要发送的数据" Dim stream As NetworkStream = client.GetStream() Dim bytes As Byte() = Encoding.UTF8.GetBytes(data) stream.Write(bytes, 0, bytes.Length) ``` 接收数据: ``` Dim buffer(1024) As Byte Dim bytesRead As Integer = stream.Read(buffer, 0, buffer.Length) Dim receivedData As String = Encoding.UTF8.GetString(buffer, 0, bytesRead) ``` 最后,关闭连接。 ``` stream.Close() client.Close() ``` 这就是用VB.NET实现TCP/IP客户端的发送和接收的基本步骤。当然,在实际应用中可能需要添加一些错误处理和数据处理的逻辑,以上仅为简单示例。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值