因此,现在,详细介绍了如何构造Modbus TCP消息: 交易ID -这是一个2字节的字段,可以是您想要的任意数字.通常,每次发送消息时,此值将增加1.您发送给的设备将在响应时重复发送此号码给您,因此您可以确定要为特定请求解析数据.如果响应上的事务ID与您发送的ID不匹配,则应丢弃数据. 协议-这是一个2字节字段,应为全零,以指示Modbus TCP.非常简单. 剩余字节数-这是一个2字节的字段,用于说明邮件中剩余的字节数.只需添加所有字节;那就是这里的数字. 从站ID -这是一个1字节的字段.我发现这个值起初有点令人困惑,但是请考虑一下:您正在使用Modbus TCP -您已经已直接连接到从站,因为您正在连接到您在上面指定的IP地址.从站ID的用途是,如果您使用的是Modbus TCP-RTU路由器",则您的消息将在RTU网络上重新广播.在这种情况下,您要尝试在RTU网络上指定路由器后面的设备的地址.如果您要通信的设备与您为其指定IP地址的设备相同,则您无需使用从设备ID.如果您不使用该值,则应将其设置为255.因此,tl; dr-如果您未使用Modbus做复杂的事情,请将该值输入255. 功能ID -这是一个1字节的字段.这是一个与特定功能代码对应的数字,该数字指示设备执行操作.就我而言,我想做的就是读取寄存器,因此每次我的功能ID都是4. 数据-您可以配置设备执行的任何操作,但是我将解释一些默认"情况,因为它们应该非常通用.通常,当您向设备(从站)发送消息时,数据将是两个2字节的值,其中第一个2字节的值是特定寄存器的地址,第二个2字节的值是其中一个的值.要读取的寄存器或要写入该寄存器的值. 所以,让我们尝试一个例子.我希望事务代码为3(同样,我选择一个任意数字,并在每次传输中递增,以使我能够将响应匹配到特定传输).我希望功能代码为4(读取寄存器),我想从寄存器0开始,我想读取12个寄存器.为了记录,一个寄存器通常为2个字节,因此这意味着我要从寄存器0开始并准备好24个字节的信息. 该消息如下所示:message = [... %*** TRANSACTION ID ***% uint8(0); ... % uint8(3); ... % Two byte transaction ID %*** PROTOCOL ***% uint8(0); ... % uint8(0); ... % Two byte protocol ID - all zeros means Modbus TCP %*** BYTES REMAINING ***% uint8(0); ... % uint8(6); ... % Two byte number of bytes for everything after this %*** SLAVE ID ***% uint8(255); ... % Slave ID - use if end device is after a modbus tcp/rtu router, otherwise use 255 %*** FUNCTION ID ***% uint8(4); ... % 4 - read input registers %*** DATA ***% %***** Starting Register *****% uint8(0); ... % uint8(0); ... % Two byte number that gives the starting register to read %***** Number of Registers to Read *****% uint8(0); ... % uint8(12)]; % Two byte number that gives how many registers to read现在,将此消息写入设备:fwrite(tcpip_pipe, message,'int8');然后,等待响应:while ~tcpip_pipe.BytesAvailable,end然后读取返回的数据:response = fread(tcpip_pipe,tcpip_pipe.BytesAvailable);标头信息应全部符合预期.同样,如果交易ID与您发送的ID不匹配,则应丢弃该消息.功能代码应与您发送的代码相同.如果不是,则可能是错误-错误功能代码与您所使用的相同发送,加上128.因此,在我的示例中,我想读取输入寄存器(功能代码4),如果出现错误(例如,我的消息格式不正确,请指定无效的寄存器号,等等),然后该函数我将在响应中得到的代码将为4 + 128 =132.最后一点,我要补充一点:对读命令的响应中的第一个字节是响应中的寄存器数(不是字节!一个寄存器是两个字节),这对我来说似乎是多余的因为Modbus TCP标头中已经有一个有效载荷长度,但是我没有制定协议. 希望这是有关Modbus TCP工作原理的一个很好的入门,并且所提供的所有代码都是直接来自我正在运行的脚本的Matlab代码,因此它应该对您有用.如果您想了解有关应发送和接收的确切消息的更多信息,请查看 Wikipedia页面在Modbus上.一旦理解,一切都非常简单. I have this MATLAB Code:function [s] = serialstart(opt) % Function for initializing a serial interface in matlab for interfacing % Functions using the serial port must be passed the serial port object % s in order for the serial port to be accessible. port = 502; s = tcpip('192.168.2.177',port); %????? set(s, 'InputBufferSize', 3000000); % Initialize serial port on specified com port date_addr = 40001; date_num=1; date_addr_high = floor(date_addr/100); date_addr_low = mod(date_addr,100); date_num_high = floor(date_num/100); date_num_low = mod(date_num,100); %Open serial connection fopen(s); % Specify Terminator s.terminator='CR/LF'; fwrite(s,0,'char') %Transactio identifier 0x00 fwrite(s,0,'char') % Transactio identifier 0x00 fwrite(s,0,'char') % Protokol identifier 0x00 fwrite(s,0,'char') % Protokol identifier 0x00 fwrite(s,0,'char') % Data Bytes 0x00 fwrite(s,1,'char') % Data Bytes 0x06 fwrite(s,255,'char') % unit identifier 0xff fwrite(s,3,'uint8') % Function 0x03 fwrite(s,date_addr_high,'uint8') %Register High Byte fwrite(s,date_addr_low,'uint8') %Register Low Byte fwrite(s,date_num_high,'uint8') %How many Register Low Byte fwrite(s,date_num_low,'uint8') %How many Register High Byte out = fread(s,1,'char'); fclose(s); but I get the following response:Here are the settings for the TCPIP object:TCPIP Object : TCPIP-192.168.2.177 Communication Settings RemotePort: 502 RemoteHost: 192.168.2.177 Terminator: 'CR/LF' NetworkRole: client Communication State Status: closed RecordStatus: off Read/Write State TransferStatus: idle BytesAvailable: 0 ValuesReceived: 0 ValuesSent: 12The connection was successful, but I don't receive any data. I don't know how to receive any Date.EDIT:I added this at the end:while ~s.BytesAvailableends.BytesAvailableres=fread(s,s.BytesAvailable) fclose(s);Now i get no response. 解决方案 I know this is an old post, so I'm reluctant to bump it, but I've got Modbus TCP working with Matlab and this is one of the posts I came across when I was trying to get it to work, so I figured I'd post an answer here about it. I'll preface this by saying that this is done with the Instrument Control Toolbox for Matlab, as that's required for the tcpip() command. I'll work on getting this to work without the toolbox later, as getting the whole toolbox just for Modbus TCP is overkill, but for rapid development purposes a trial version of the toolbox is enough to get you by for a bit. So, first configure the port:IPADDR='192.168.0.1';PORT=502;tcpip_pipe=tcpip(IPADDR, PORT);set(tcpip_pipe, 'InputBufferSize', 512); tcpip_pipe.ByteOrder='bigEndian';Then, open the port:try if ~strcmp(tcpip_pipe.Status,'open') fopen(tcpip_pipe); end disp('TCP/IP Open'); catch err disp('Error: Can''t open TCP/IP'); return;endNow, what I've found to work is to prepare the entire message and then write the entire thing at once. This helps, I think, because you specify the endianness with te .ByteOrder above, so you don't really need to worry about how it's setup beyond that or if you're writing the messages in the correct order, etc. So now, a breakdown of how you structure a Modbus TCP message:Transaction ID - This is a 2-byte field that can be any arbitrary number you want. Typically you would increment this value by 1 every time you send a message. The device you transmit to will repeat this number back to you when it responds so you can be sure that you're parsing the data for a particular request. If the transaction ID on the response doesn't match what you sent then you should discard the data. Protocol - This is a 2-byte field that should be all zeros to indicate Modbus TCP. Pretty straightforward. Bytes Remaining - This is a 2-byte field that states how many bytes remain in the message. Just add all the bytes; that's the number that goes here. Slave ID - This is a 1-byte field. I found this value to be a little confusing at first, but consider this: You are using Modbus TCP - you already have a direct connection to the slave because you are connecting to the IP address that you specify above. The purpose of Slave ID here is if you're using a Modbus TCP-RTU "router" where your message will get re-broadcast on an RTU network. In that case, you're trying to specify the address of the device behind the router, on the RTU network. If the device you're trying to communicate is the same as the one that you've specified the IP address for, then you don't need to use the Slave ID. The value you should put down if you're not using it is 255. So, tl;dr - if you're not doing something complex with Modbus, put 255 for this value. Function ID - This is a 1-byte field. This is a number that corresponds to a specific function code that instructs the device what to do. In my case, all I wanted to do was read registers, so my function ID was 4 every time.Data - This could be anything that your device is configured to do, but there's a couple "default" cases that I'll explain as they should be pretty universal. Typically, when you send a message to the device (slave), the data will be two 2-byte values, where the first 2-byte value is the address of a particular register and the second 2-byte value is either the number of registers to read or the value to be written to that register. So, let's try an example. I want the transaction code to be 3 (again, an arbitrary number I choose and increment with every transmission to allow me to match a response to a particular transmission). I want the function code to be 4 (read registers), I want to start at register 0, and I want to read 12 registers. For the record, a register is typically 2 bytes, so this means that I want to start at register 0 and ready 24 bytes of information. That message looks like the following:message = [... %*** TRANSACTION ID ***% uint8(0); ... % uint8(3); ... % Two byte transaction ID %*** PROTOCOL ***% uint8(0); ... % uint8(0); ... % Two byte protocol ID - all zeros means Modbus TCP %*** BYTES REMAINING ***% uint8(0); ... % uint8(6); ... % Two byte number of bytes for everything after this %*** SLAVE ID ***% uint8(255); ... % Slave ID - use if end device is after a modbus tcp/rtu router, otherwise use 255 %*** FUNCTION ID ***% uint8(4); ... % 4 - read input registers %*** DATA ***% %***** Starting Register *****% uint8(0); ... % uint8(0); ... % Two byte number that gives the starting register to read %***** Number of Registers to Read *****% uint8(0); ... % uint8(12)]; % Two byte number that gives how many registers to readNow, write this message to the device:fwrite(tcpip_pipe, message,'int8');Then, wait for a response:while ~tcpip_pipe.BytesAvailable,endAnd then read the returned data:response = fread(tcpip_pipe,tcpip_pipe.BytesAvailable);The header information should all be as expected. Again, if the transaction ID doesn't match what you sent then you should discard the message. The function code should be the same as what you sent. If it's not, it's probably an error - the error function code is the same as what you sent, plus 128. So, in my example, I wanted to read input registers (function code 4), and if there's an error (like my message isn't formatted correctly, specify an invalid register number, etc.) then the function code I'll get in the response will be 4+128 = 132. As a final note, I'll add that the first byte in the response to a read command would be the number of registers (not bytes! a register is two bytes) that follow in the response, which seems superfluous to me because there's already a payload length in the Modbus TCP header, but I didn't make the protocol. Hopefully this is a pretty good primer on how Modbus TCP works, and all the code provided is Matlab code straight from my functioning script, so it should work for you. If you would like more information on the exact messages you should be sending and receiving, check out the Wikipedia page on Modbus. It's all pretty straightforward once you understand it. 这篇关于Modbus TCP和MATLAB的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
10-20 23:45