|
I am not sure if I understand correct what the problem is. Keep in mind that coils are actually digital outputs. Discrete inputs are digital inputs. So when you read coils you read back digital outputs.
Stephan
|
|
|
|
|
The issue is in WriteSyncData method, sending command function and received buffer function command is not matching. How do we ensure that the command we send is actually what we receive?
byte sendFunc = write_data[7];
tcpSynCl.Send(write_data, 0, write_data.Length, SocketFlags.None);
result = tcpSynCl.Receive(tcpSynClBuffer, 0, tcpSynClBuffer.Length, SocketFlags.None);
if (sendFunc != tcpSynClBuffer[7])
System.Diagnostics.Trace.WriteLine("Command and Response are not matching inside Master.WriteSyncData" + "Send Func : " + sendFunc + "Response : " + tcpSynClBuffer[7]);
It happens that we wanted to readsinglecoil but what we receive is previous call's(writesingleregister) output. It looks TCP synchronization issue. Is there any time delay to send the next command?
|
|
|
|
|
Hello..
I have a problem when trying to Read and Write address more than Integer limit (32767).
On function CreateReadHeader and CreateWriteHeader There is a problem when convert Start Address to byte, in this line of code :
(this is VB conversion code. from here: http://hadiscada.blogspot.com/2013/07/modbus-sample-vb-engllish-version.html[^])
Dim _adr As Byte() = BitConverter.GetBytes(CShort(IPAddress.HostToNetworkOrder(CShort(startAddress))))
data(8) = _adr(0)
data(9) = _adr(1)
I solved my problem with this edited code :
data(8) = startAddress \ 256
data(9) = startAddress Mod 256
Thanks..
modified 17-Sep-13 5:14am.
|
|
|
|
|
|
First of all, thanks for such a good working and kind sharing it with the community.
I had a quick investigation on the class file, saw some things that are needed to be considered
more precisely.
First one:
What MSDN says is "The asynchronous BeginReceive operation must be completed by calling the EndReceive method. Typically, the method is invoked by the callback delegate." Check it from http://msdn.microsoft.com/en-us/library/dxkwh6zw.aspx
There is no such call to EndReceive in OnReceive callback.
Second one:
This design pattern will work perfectly, if you receive the whole message from the slave at once (in one transaction).
But what if you don't get the whole message from the slave at once? This is very common situation in
tcp/ip communication that you can get your data, lets say 100 bytes data as 40,40 and 20 bytes, in two or three or more parts.
So in OnReceive, received length should be checked against to requested or sent length from slave then BegingReceive should be recalled
for the remaining part of the message. So you can be sure that received message is in completed status.
Or am I mistaken?
|
|
|
|
|
I agree with the first point and I added EndSend and EndReceive to the code. Fragmentation appears mostly when you exceed the Ethernet MTU size of 1500 bytes. Since modbusTCP only allows 255 bytes of data we should be well below this. I ran a quick test and after several hours and more than a thousand frames there was not a single fragmentation. I know its not 100% clean but I would have to change code quite a bit for this change.
Stephan
|
|
|
|
|
To make the class more flexible for all kind of physical connection types, not only for ethernet,
I still think that fragmentation should be considered too.
Please imagine that, if application would be required to gather information scattered on a very large field connected with
gprs based network or even through adsl line. I can not imagine all right now. But since it is a tcp then why not?
Cheer
|
|
|
|
|
Hi Stephan, so i got your example to work great and I am trying to use the dll for my own project.
I am trying to use asp.net and make a website I can go to that will essentially do exactly what your application does (just the read registers portion) but for the web. I have gotten to the point where it connects and I use the method ReadHoldingRegister which works fine but I have no idea how to get the data sent back. I'm not sure I can use your custom event handler to detect if data is sent back. I am pretty new to C# so maybe it is simple and I don't know yet but thought I would ask this question to see if you have a good idea on how I might resolve this issue.
Thanks so much!
Jeff
|
|
|
|
|
Jeff,
I am sorry but I dont know how to handle events in ASP. The issue is that you load the ASP page and then it just sits there. The modbus driver will send a callback when new data arrives but I dont know how an ASP web page can receive that.
Stephan
|
|
|
|
|
Hi, Stephan!
I'm not good in c#, but I need some programm for write/read data to my PLC Siemens Simatic S7 1200. Now I use your sample. But I need program, where I can just enter IP and read first two holding registers and write only first holding register (in "word" format). Can you help me?
Best regards!
p.s. Sorry for my english, I'm from Russia
|
|
|
|
|
|
Hi,
I am trying to read data from Modbus TCP simulator but it gives me an error like
Modbus says error: Illegal function! I am able to write data.
Please help.
Thank you,
Mayur Shah
|
|
|
|
|
This is a response from your slave device. You probably call a function to read coils to an address where you have registers or you read from an address that does not exist. If you can not find the issue please download wireshark and add some network traces so I can see the request and response.
Stephan
|
|
|
|
|
Hello,
Great example!
Do you think you could show an example on how to encapsulate Modbus RTU or Modbus ASCII messages over TCP?
I think the differences between RTU and ASCII are interesting code wise, and encapsulating those "older" Modbus variants into TCP is also interesting: using modern Ethernet to communicate with legacy serial devices.
Thank you in advance
--
Josep
|
|
|
|
|
Hi,
I have not seen a device that would use a modbusRTU frame embedded directly into TCP. I found the term when I search for it but I dont think that this is in any spec. ModbusTCP and modbusRTU are very close in their telegram structure. There are minor changes like for example the CRC does not exist in modbusTCP because the error detection is covered by the TCP frame. The function codes are exactly the same. Even function calls like read/write single coil still exist in modbusTCP also they dont make much sense. I guess for compatibility reasons it is nice to have all functions.
Best regards
Stephan
|
|
|
|
|
Hi there,
Legacy devices using Modbus RTU (or ASCII) are now being interfaced through Ethernet, as less and less computers come with COM ports. Thus using an Ethernet to Serial converter lets you interface in a modern way to a legacy device.
You are 100% right in the functions and CRC checks. A difference would be having as mandatory the node ID.
As per coding learning, the LRC check-sum method employed by Modbus ASCII makes an interesting and not too difficult exercise
Thank you for your time.
All the best,
Josep
|
|
|
|
|
Thanks for the very useful code!
This describes the difference between modbus TCP and RTU;
http://www.simplymodbus.ca/TCP.htm[^]
ModbusUtility.cs found here calculates the correct CRC;
http://code.google.com/p/nmodbus[^]
This will generate correct RTU MBAP header;
private byte[] CreateReadHeaderRTU(ushort id, byte unit, ushort startAddress, ushort length, byte function)
{
byte[] data = new byte[6];
byte[] crc = new byte[2];
data[0] = unit;
data[1] = function;
byte[] _adr = BitConverter.GetBytes((short) IPAddress.HostToNetworkOrder((short) startAddress));
data[2] = _adr[0];
data[3] = _adr[1];
byte[] _length = BitConverter.GetBytes((short) IPAddress.HostToNetworkOrder((short) length));
data[4] = _length[0];
data[5] = _length[1];
crc = ModbusUtil.CalculateCrc(data);
byte[] ret = new byte[data.Length + crc.Length];
Buffer.BlockCopy(data, 0, ret, 0, data.Length);
Buffer.BlockCopy(crc, 0, ret, data.Length, crc.Length);
return ret;
}
But I did not finish the response OnReceive() code
//TCP 00000000 | 00 00 00 00 00 07 01 03 04 42 A0 9B 1C | .........B...
//RTU 00000000 | 03 03 04 44 C2 41 88 5C C9 | ...D.A.\.
ushort rtu = BitConverter.ToUInt16(tcpAsyClBuffer, resultptr+2); // Zero for TCP, bytecount for RTU
because I realized ModbusRTU has no transaction ID, therefore it can't be done non-sequentially asynchronously since nothing in the response identifies the reqeust. But I did verify a single request/response was correct. I will eventually have to finish the synchronous RTU support.
I also added support for Enron (Modicon) and Omni extended ASCII read buffer (report) function 65
I created an Excel RTD (real-time-data) add-in using your code-base with a few modifications. I'll post it when I finish.
Ken
|
|
|
|
|
Thanks for the class, it's clean, simple and works very well.
However, I noticed that in "CreateReadHeader" and "CreateWriteHeader", you save the message ID/save Id with an inverted bit order, my device complained about this and I checked it with WireShark
byte[] _id = BitConverter.GetBytes((short)id);
data[0] = _id[0];
data[1] = _id[1];
Basically, _id[0] is your low-byte and vise-versa.
Also as a general note/recommendation some Modbus/TCP devices (i.e. Schneider) use the Slave Address for different messaging methods, so it is convenient to add the possibility of changing this as part of the read/write headers. The modifications are easy enough to make on your own but perhaps this can help some other potential users.
Thanks again, really like the code.
|
|
|
|
|
Hi,
thank you very much for the recommendations. I have updated the code and will post a new version shortly.
Stephan
|
|
|
|
|
Hi first of all thx a lot for this wonderfull applicatio but i have a question how to save values of message in case of coill inputs for further usage<
|
|
|
|
|
The MBmaster_OnResponseData returns the data for a read or write request. The ID identifies command you sent because you can send multiple request at a time and the ID tells you for what request you receive a response. The function value is just for reference and "values" is where you get your return data. You can transfer "values" into a global variable in case you want use the data somewhere else in your program. The sample program does this by simply using
data = values;
Stephan
|
|
|
|
|
Look stephan i'm gonna use ur code and try to develop scada system for data trending
Do u have any example for this application?
|
|
|
|
|
I am sorry but I dont have a sample for data trending.
Stephan
|
|
|
|
|
is it possible to open more than connection at a time
|
|
|
|
|
Yes, just create another instance of the master function block.
Stephan
|
|
|
|