Modbus TCP和MATLAB [英] Modbus TCP and MATLAB

查看:296
本文介绍了Modbus TCP和MATLAB的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个MATLAB代码:

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); 

但是我得到以下回应:

警告:读取失败:未在指定的范围内返回指定的数据量 超时时间.

以下是TCPIP对象的设置:

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: 12

连接成功,但是我没有收到任何数据.我不知道如何接收日期.

我在末尾添加了此内容:

while ~s.BytesAvailable
end
s.BytesAvailable
res=fread(s,s.BytesAvailable)                 
fclose(s);

现在我没有回应.

解决方案

我知道这是一篇老文章,所以我不愿意碰它,但是我让Modbus TCP与Matlab一起工作,这是其中之一我在尝试使其工作时遇到的帖子,所以我想在这里发布有关此问题的答案.

我将通过使用Matlab的仪器控制工具箱来完成此操作,这是tcpip()命令所必需的.我将在以后不使用工具箱的情况下继续进行工作,因为仅将整个工具箱用于Modbus TCP是过大的,但是出于快速开发的目的,工具箱的试用版足以使您满意.

因此,首先配置端口:

IPADDR='192.168.0.1';
PORT=502;
tcpip_pipe=tcpip(IPADDR, PORT);
set(tcpip_pipe, 'InputBufferSize', 512); 
tcpip_pipe.ByteOrder='bigEndian';

然后,打开端口:

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;
end

现在,我发现可以工作的是准备整个消息,然后立即编写整个内容.我认为这会有所帮助,因为您在上面用te .ByteOrder指定了字节序,所以您实际上不必担心它的设置方式是否超出设置范围,或者您是否以正确的顺序编写消息,等等.

因此,现在,详细介绍了如何构造Modbus TCP消息:

  1. 交易ID -这是一个2字节的字段,可以是您想要的任意数字.通常,每次发送消息时,此值将增加1.您发送给的设备将在响应时重复发送此号码给您,因此您可以确定要为特定请求解析数据.如果响应上的事务ID与您发送的ID不匹配,则应丢弃数据.
  2. 协议-这是一个2字节字段,应为全零,以指示Modbus TCP.非常简单.
  3. 剩余字节数-这是一个2字节的字段,用于说明邮件中剩余的字节数.只需添加所有字节;那就是这里的数字.
  4. 从站ID -这是一个1字节的字段.我发现这个值起初有点令人困惑,但是请考虑一下:您正在使用Modbus TCP -您已经已直接连接到从站,因为您正在连接到您在上面指定的IP地址.从站ID的用途是,如果您使用的是Modbus TCP-RTU路由器",则您的消息将在RTU网络上重新广播.在这种情况下,您要尝试在RTU网络上指定路由器后面的设备的地址.如果您要通信的设备与您为其指定IP地址的设备相同,则您无需使用从设备ID.如果您不使用该值,则应将其设置为255.因此,tl; dr-如果您未使用Modbus做复杂的事情,请将该值输入255.
  5. 功能ID -这是一个1字节的字段.这是一个与特定功能代码对应的数字,该数字指示设备执行操作.就我而言,我想做的就是读取寄存器,因此每次我的功能ID都是4.
  6. 数据-您可以配置设备执行的任何操作,但是我将解释一些默认"情况,因为它们应该非常通用.通常,当您向设备(从站)发送消息时,数据将是两个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:

Warning: Unsuccessful read: The specified amount of data was not returned within the Timeout period.

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: 12

The 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.BytesAvailable
end
s.BytesAvailable
res=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;
end

Now, 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:

  1. 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.
  2. Protocol - This is a 2-byte field that should be all zeros to indicate Modbus TCP. Pretty straightforward.
  3. 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.
  4. 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.
  5. 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.
  6. 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 read

Now, write this message to the device:

fwrite(tcpip_pipe, message,'int8');

Then, wait for a response:

while ~tcpip_pipe.BytesAvailable,end

And 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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆