foreword
This article will use a component technology disclosed by NuGet to implement a ModBus RTU client, which can easily read and write to a Modbus rtu server. This server can be designed in C# on the computer, or implemented by PLC, or other Any server that supports this communication protocol.
github address: https://github.com/dathlin/HslCommunication If you like, you can star or fork, and you can also give a reward for support.
You can download and install it in the NuGet manager in Visual Studio, or you can directly enter the following command in the NuGet console to install:
Install-Package HslCommunication
NuGet installation tutorial http://www.cnblogs.com/dathlin/p/7705014.html
Technical support QQ group: 592132877 (The version update details of the component will also be released in the group as soon as possible) Component API address: http://www.cnblogs.com/dathlin/p/7703805.html
Special thanks to
- Netizen: Chen Enfu 's reading test of float and int data fixed the bug that the weights were reversed.
- Netizen: U4 happy snail found a wrong method name on the blog, updated on January 8, 2018 13:34:39. And feedback the bug of reading data of some special devices (modbus tcp server). already fixed.
just chit chat
The design pattern here is that the client actively requests the server data, and then receives the server's feedback data, supports native command sending and receiving, and supports some other convenient API sending and receiving. The special function code needs to use the native transceiver API. This component supports the following functional operations:
- 0x01 read coil operation,
- 0x02 read discrete operations,
- 0x03 reads the value of the register,
- 0x05 write a coil operation,
- 0x06 writes a register value,
- 0x0F Batch write coil operation,
- 0x10 batch write register value,
If your device needs data beyond these functions, you can use the native API method, but the premise of this method is that you are very clear about the MODBUS protocol. If you do not understand this protocol, you can refer to the following blog instructions:
http://blog.csdn.net/thebestleo/article/details/52269999
If you need to build your own ModBus server, you can refer to this article: http://www.cnblogs.com/dathlin/p/7782315.html
Access the test project
Need to download a serial port virtual software
Virtual Serial Port Driver
Download address: https://virtual-serial-port-driver.en.softonic.com/
Then virtualize the two serial ports, COM4 and COM5 are connected together by default. So we can do local testing
Before you develop your own client program, you can use the MODBUS test tool to test it. An open source project at the following address is a Modbus rtu test tool developed based on this component, which can be directly used for read and write tests.
ModbusTcpServer.zip starts the service first, then starts the serial port
The following item is the access test item of this component, you can conduct preliminary access test, save you the trouble of writing test program, this item is written together with the access of Mitsubishi and Siemens PLC. can be referenced at the same time.
The download address is: HslCommunicationDemo.zip
Reference
All functional classes of ModBus components are in the HslCommunication.ModBus namespace, so add them before using them
using HslCommunication.ModBus; using HslCommunication;
How to Use
Instantiate:
It must be instantiated before using the read and write functions:
private ModbusRtu busRtuClient = new ModbusRtu( station );
Note: In the devices of the Modbus server, most devices start from address 0, and some special devices start from address 1, so in this component, the default starts from address 0, if you want to start from address 1 , then the following configuration is required:
busRtuClient.AddressStartWithZero = False;
Then you need to initialize the parameters. For serial ports, the usual parameters include serial port name, baud rate, data bits, stop bits, and parity bits, which provide a way to entrust setting.
try { busRtuClient.SerialPortInni( sp => { sp.PortName = "COM5"; sp.BaudRate = 9600; sp.DataBits = 8; sp.StopBits = System.IO.Ports.StopBits.One; sp.Parity = System.IO.Ports.Parity.None; } ); busRtuClient.Open( ); // open } catch (Exception ex) { MessageBox.Show( ex.Message ); }
If it is closed, call the following method
busRtuClient.Close( );
The following code demonstrates common read and write operations. For the sake of convenience, IsSuccess is no longer judged, and it is generally successful:
private void userButton30_Click(object sender, EventArgs e) { // read operation bool coil100 = busRtuClient.ReadCoil("100").Content; // Read the on-off of coil 100 short short100 = busRtuClient.ReadInt16("100").Content; // Read the short value of register 100 ushort ushort100 = busRtuClient.ReadUInt16("100").Content; // Read the ushort value of register 100 int int100 = busRtuClient.ReadInt32("100").Content; // read the int value of registers 100-101 uint uint100 = busRtuClient.ReadUInt32("100").Content; // Read the uint value of registers 100-101 float float100 = busRtuClient.ReadFloat("100").Content; // read the float value of registers 100-101 long long100 = busRtuClient.ReadInt64("100").Content; // read the long value of registers 100-103 ulong ulong100 = busRtuClient.ReadUInt64("100").Content; // Read the ulong value of registers 100-103 double double100 = busRtuClient.ReadDouble("100").Content; // read the double value of registers 100-103 string str100 = busRtuClient.ReadString("100", 5).Content;// Read a string of 10 characters from 100 to 104 // write operation busRtuClient.WriteCoil("100", true);// Write coil 100 to pass busRtuClient.Write("100", (short)12345);//Write register 100 to 12345 busRtuClient.Write("100", (ushort)45678);//Write register 100 to 45678 busRtuClient.Write("100", 123456789);//Write register 100-101 to 123456789 busRtuClient.Write("100", (uint)123456778);//Write register 100-101 to 123456778 busRtuClient.Write("100", 123.456);//Write register 100-101 to 123.456 busRtuClient.Write("100", 12312312312414L);//Write registers 100-103 as a big data busRtuClient.Write("100", 12634534534543656UL);// Write registers 100-103 as a large data busRtuClient.Write("100", 123.456d);//Write register 100-103 as a double-precision data busRtuClient.Write("100", "K123456789"); }
Strict operations and batched complex read and write operations will be explained below. Suppose you want to read 1,000 M, and it may take 3 seconds to read 1,000 times in a loop. If the following batch read is used, only It takes 50ms, but you need to be familiar with the principle of bytes to handle it easily
Read coil API:
Here is an example to read the number of coils with an address of 0 and a length of 10. The read data has been automatically converted into a bool array, which is convenient for secondary processing:
private void userButton8_Click(object sender,EventArgs e) { HslCommunication.OperateResult<bool[]> read = busRtuClient.ReadCoil("0", 10); if(read.IsSuccess) { bool coil_0 = read.Content[0]; bool coil_1 = read.Content[1]; bool coil_2 = read.Content[2]; bool coil_3 = read.Content[3]; bool coil_4 = read.Content[4]; bool coil_5 = read.Content[5]; bool coil_6 = read.Content[6]; bool coil_7 = read.Content[7]; bool coil_8 = read.Content[8]; bool coil_9 = read.Content[9]; } else { MessageBox.Show(read.ToMessageShowString()); } }
Of course, you can also use the data conversion API provided by the component to achieve data extraction:
Read discrete data:
The codes for reading discrete data and reading coils are almost the same, and the processing methods are also the same, except that the method name is changed to:
private void userButton8_Click(object sender,EventArgs e) { HslCommunication.OperateResult<bool[]> read = busRtuClient.ReadDiscrete("0", 10); if(read.IsSuccess) { bool coil_0 = read.Content[0]; bool coil_1 = read.Content[1]; bool coil_2 = read.Content[2]; bool coil_3 = read.Content[3]; bool coil_4 = read.Content[4]; bool coil_5 = read.Content[5]; bool coil_6 = read.Content[6]; bool coil_7 = read.Content[7]; bool coil_8 = read.Content[8]; bool coil_9 = read.Content[9]; } else { MessageBox.Show(read.ToMessageShowString()); } }
Read register data:
Suppose we need to read data with address 0 and length 10, that is, 10 data, 2 bytes each, for a total of 20 bytes of data. Before parsing the data below, assumptions are made first. You can refer to the following parsing before parsing your own data.
private void userButton10_Click(object sender, EventArgs e) { HslCommunication.OperateResult<byte[]> read = busRtuClient.Read("0", 10); if (read.IsSuccess) { // A total of 20 bytes are returned, 2 bytes for each data, high order first, low order last // Before data parsing, you need to know what type of data is stored in it, so you need to make some assumptions: // The first two bytes are of the short data type short value1 = busTcpClient.ByteTransform.TransInt16(read.Content, 0); // The next 2 bytes are of type ushort ushort value2 = busTcpClient.ByteTransform.TransUInt16(read.Content, 2); // The next 4 bytes are of type int int value3 = busTcpClient.ByteTransform.TransInt32(read.Content, 4); // The next 4 bytes are of type float float value4 = busTcpClient.ByteTransform.TransFloat(read.Content, 8); // All the next bytes, a total of 8 bytes are specification information string speci = Encoding.ASCII.GetString(read.Content, 12, 8); // All data has been fetched } else { MessageBox.Show(read.ToMessageShowString()); } }
Write a coil:
Write a coil, this is relatively simple, assuming we need to write coil 0, for the pass
private void userButton11_Click(object sender, EventArgs e) { HslCommunication.OperateResult write = busRtuClient.WriteCoil("0", true); if (write.IsSuccess) { // write successfully textBox1.Text = "Write successful"; } else { MessageBox.Show(write.ToMessageShowString()); } }
Write a register:
The operation of writing a register is also very convenient. Three overloaded methods are provided here, allowing three ways to write: write, short, ushort, byte respectively:
private void userButton12_Click(object sender, EventArgs e) { short value = -1234; HslCommunication.OperateResult write = busRtuClient.WriteOneRegister("0", value); if (write.IsSuccess) { // write successfully textBox1.Text = "Write successful"; } else { MessageBox.Show(write.ToMessageShowString()); } }
private void userButton12_Click(object sender, EventArgs e) { ushort value = 56713; HslCommunication.OperateResult write = busRtuClient.WriteOneRegister("0", value); if (write.IsSuccess) { // write successfully textBox1.Text = "Write successful"; } else { MessageBox.Show(write.ToMessageShowString()); } }
private void userButton12_Click(object sender, EventArgs e) { // 0x00 is high, 0x10 is low HslCommunication.OperateResult write = busRtuClient.WriteOneRegister("0", 0x00, 0x10); if (write.IsSuccess) { // write successfully textBox1.Text = "Write successful"; } else { MessageBox.Show(write.ToMessageShowString()); } }
Bulk write to coils:
private void userButton13_Click(object sender, EventArgs e) { // Coil 0 is True, Coil 1 is false, Coil 2 is true... and so on, and so on, as many coils are written as the array length is bool[] value = new bool[] { true, false, true, true, false, false }; HslCommunication.OperateResult write = busRtuClient.WriteCoil("0", value); if (write.IsSuccess) { // write successfully textBox1.Text = "Write successful"; } else { MessageBox.Show(write.ToMessageShowString()); } }
Bulk write registers:
The first case writes a string of short arrays. This case is relatively simple:
private void userButton14_Click(object sender, EventArgs e) { short[] value = new short[] { -1234, 467, 12345 }; HslCommunication.OperateResult write = busRtuClient.Write("0", value); if (write.IsSuccess) { // write successfully textBox1.Text = "Write successful"; } else { MessageBox.Show(write.ToMessageShowString()); } }
The second case is to write a string of ushort arrays, which is also relatively simple:
private void userButton14_Click(object sender, EventArgs e) { ushort[] value = new ushort[] { 46789, 467, 12345 }; HslCommunication.OperateResult write = busRtuClient.Write("0", value); if (write.IsSuccess) { // write successfully textBox1.Text = "Write successful"; } else { MessageBox.Show(write.ToMessageShowString()); } }
The more complicated thing is to write custom data, read registers according to the above, for example, I need to write an int data composed of register 0 and register 1, then we write as follows:
private void userButton15_Click(object sender, EventArgs e) { int value = 12345678;//A data waiting to be written HslCommunication.OperateResult write = busRtuClient.Write("0", value); if (write.IsSuccess) { // write successfully textBox1.Text = "Write successful"; } else { MessageBox.Show(write.ToMessageShowString()); } }
You can refer to this for other data. If you don’t understand anything, you can contact the QQ group above.
Ultimate data manipulation, using native packets to manipulate data:
Pass in a byte array, the data content is consistent with the original data, for example, if I want to read the data whose register address is 0 and the length is 3 through the native API, then the HEX identification form of the byte is 01 03 00 00 00 03 excluding CRC check code
private void button26_Click( object sender, EventArgs e ) { try { OperateResult<byte[]> read = busRtuClient.ReadBase( HslCommunication.Serial.SoftCRC16.CRC16(HslCommunication.BasicFramework.SoftBasic.HexStringToBytes( "01 03 00 00 00 03" )) ); if (read.IsSuccess) { textBox11.Text = "结果:" + HslCommunication.BasicFramework.SoftBasic.ByteToHexString( read.Content,' ' ); } else { MessageBox.Show( "Failed to read: " + read.ToMessageShowString( ) ); } } catch (Exception ex) { MessageBox.Show( "Failed to read: " + ex.Message ); } }
The above code uses a conversion mechanism during operation. The input is hexadecimal text and converted into byte[] data. The separator in the middle can be a space, can be '-', or can be ',', '_ 'Wait and so on, the data conversion function of the component base is called.