|
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
|
|
|
|
|
Hello Stephen,
I tried using the example in a different context, Instead of havin btnReadHoldReg_Click(..) invoke the callbacks I have an example that works on timers. Lets call this function ReadHoldReg_Click().
It works for a while, but then the "values" array becomes NULL and there is nothing to copy and an exception is thrown. Since my example is too big to debug I am trying to modify your example to work on "ticks" rather than "clicks" to see if the same undesired behavior can be somehow provoked.
Originally I tried using a Modbus driver from a different source, but this never worked. The connection dropped after a while and the reconnection sequence did not work for me.
https://groups.google.com/forum/#!topic/nmodbus-discuss/Tb834C5wnpU[^]
Has anyone experienced the "values" array in the frmStart.cs module going NULL?
....
grpData.Text = "Read holding register";
globaldata = values;
regards
Colm
modified 26-Jul-13 9:38am.
|
|
|
|
|
For connecting to siemens S1214 I had to change port from 501 to 102 but now the fild below remain white ever.
In this way I (hope) have estabilished the connection, now i'd like read and write!
pls help me thanks!
|
|
|
|
|
The standard port for modbusTCP is 502. Where did you change that port (Windows or Siemens) and what field exactly remains white?
Stephan
|
|
|
|
|
I've resolved everything and i've also build a solution for read , bool, int, real and string.
For develop this .dll i used the LibNoDave.dll library.
Soon i hope to post my release.
PS: the port that I use is 102
|
|
|
|
|
This is what I'm looking for,
Thanks
|
|
|
|
|
|
Hi,
I am trying communication between my PC and Siemens PLC with MODBUS TCP. PLC is a server, and I set "P#M100.0 BYTE 200" to holding register. I am using first 100 bytes for reading with no problem, but writing to the second 100 bytes doesn't work. I am using WriteMultipleRegister(6, 100, data), everything looks OK and no error msg, but value didn't change on the PLC side.
Could anyone please help?
Thanks a lot in advance.
Anne
|
|
|
|
|
Anne,
Can you please download wireshark and add some traces? This will tell us what data is sent and the response from the plc.
Stephan
|
|
|
|
|
Hi Stephen,
Thanks for your reply.
As you suggested, I tried wireshark, and was using your MODBUS tester to test the communication. It seems that for writing multiple registers, the start address couldn't go over 84. If start address is bigger than 84, for example 85, PLC will give out exception.
I saved the file and screen copy, but not sure how to attach it to this message.
and I am new to it, maybe did something silly.
Thanks,
Anne
|
|
|
|
|
Anne,
somewhere in the Siemens PLC there must be a configuration where you define the size of your Modbus buffer. It seems that you buffer is 84 plus the amount of data you try write. When you write to addresses below 84 do you see the values in the Siemens PLC?
Stephan
|
|
|
|
|
I am kind of get it. In my case, if the second 100 bytes are for writing, then the start address should be 50 instead of 100.
Is it right?
thanks,
Anne
|
|
|
|
|
did you resolve the problem!?
If yes could you contact me at andrea.feduzzi@gmail.com or post below.
I use siemens s1200 series
thanks
|
|
|
|
|
Hi thanks for the code but i am having a slight problem
Connection to the PLC is succesful but i cant seem to write coils.
I am using your test application.
Problem:
The Start address is at 0
The Size is 8 Bytes
I can write the first coil but when i try to write the second one it does not work
I have tried writing multiple coils but still it does not work. May I no why this is happening and how i can solve it
|
|
|
|