C# communicates with the ModbusTcp server of Siemens PLC1500 4--build ModbusTcp client

 1. Client selection

The client can be a program or a device. Here I use the C#WINFORM program to realize the communication between the client and the Modbustcp server of the PLC. The development environment is VS2019, and the .NET Framework version is 4.7.2

2. Create a winform program

 Create class library

 

Write a conversion library for various types of C#. The library is provided by me, don't worry, it is provided at the end of the article.

The project imports this class library 

 

3. Introduce Nmodbus4 protocol

Find the project, find the reference, right-click "Manage nuget program", and operate in the following dialog box

 4. The interface layout is as follows:

The layout uses the drop-down box combobox, textbox textbox, button button, label label

 This IP address and port number correspond to the

 

5. The form defines two variables and introduces the corresponding command space

        ModbusIpMaster master = null;//modbus object
        TcpClient tcpClient = null;//tcp client object

6. Connect button code

 private void btnOpen_Click(object sender, EventArgs e)
        {
            string ip = txtIPAddress.Text.Trim();
            bool t = IsIP(ip);
            if (t)
            {
                try
                {
                    int port = int.Parse(txtPort.Text.Trim());
                    tcpClient = new TcpClient();
                    tcpClient.Connect(ip, port);//连接到主机
                    master = ModbusIpMaster.CreateIp(tcpClient);//Ip 主站
                    master.Transport.ReadTimeout = 1000;//读超时
                    master.Transport.WriteTimeout = 1000;//写超时
                    master.Transport.Retries = 3;//尝试重复连接次数
                    master.Transport.WaitToRetryMilliseconds = 200;//尝试重复连接间隔
                    lblMessage.Text = "连接成功!";
                    btnOpen.Enabled = false;
                }
                catch (Exception ex)
                {
                    MessageBox.Show("连接失败," + ex.Message);
                }
            }
            else
            {
                MessageBox.Show("无效的ip地址!");
            }
        }

 7. The read code -- ushort type

In this example, only the function code of reading holding registers is used, that is, ReadHoldingRegisters (slave station address, start address, number of registers)

  /// <summary>
        /// 读取
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void myread_Click(object sender, EventArgs e)
        {
            //由于NModbus4读取到寄存器的数据都是ushort类型
            //功能码
            string readType = cboReadTypes.Text.Trim();
            //从站地址
            byte slaveAddr = byte.Parse(txtRSlaveId.Text.Trim());
            //开始地址
            ushort startAddr = ushort.Parse(txtRStartAddress.Text.Trim());
            //读取数量
            ushort readCount = ushort.Parse(txtRCount.Text.Trim());
            switch (readType)
            {
                case "读线圈":
                    bool[] blVals = master.ReadCoils(slaveAddr, startAddr, readCount);
                    txtReadDatas1.Text = string.Join(",", blVals.Select(b => b ? "1" : "0"));
                    break;
                case "读输入线圈":
                    bool[] blInputVals = master.ReadInputs(slaveAddr, startAddr, readCount);
                    txtReadDatas1.Text = string.Join(",", blInputVals.Select(b => b ? "1" : "0"));
                    break;
                case "读保持寄存器":
                    //情况1:ushort到ushort类型:即读取无符号的整数,如23,89,处理方法是:原封不动
                    //ushort[] uDatas = master.ReadHoldingRegisters(slaveAddr, startAddr, readCount);
                    //txtReadDatas.Text = string.Join(",", uDatas);

                    //功能码
                    string dataType = cmddatatype.Text.Trim();
                    switch (dataType)
                    {
                        case "ushort":
                            //利用token循环读取
                            ushortctsRead = new CancellationTokenSource();
                            Task.Run(new Action(() =>
                            {
                                ReadUshortFromPLC(slaveAddr, startAddr, readCount);
                            }), ushortctsRead.Token);
                            break;
                        case "short":
                            //利用token循环读取
                            shortctsRead = new CancellationTokenSource();
                            Task.Run(new Action(() =>
                            {
                                ReadShortFromPLC(slaveAddr, startAddr, readCount);
                            }), shortctsRead.Token);
                            break;
                        case "float":
                            //利用token循环读取
                            floatctsRead = new CancellationTokenSource();
                            Task.Run(new Action(() =>
                            {
                                ReadFloatFromPLC(slaveAddr, startAddr, readCount);
                            }), floatctsRead.Token);
                            break;
                    }  
                    break;
                case "读输入寄存器":
                    ushort[] uDatas1 = master.ReadInputRegisters(slaveAddr, startAddr, readCount);
                    txtReadDatas1.Text = string.Join(",", uDatas1);
                    break;
            }
        }

Note here,

The data read by NModbus4 into registers are of ushort type

The data read by NModbus4 into registers are of ushort type

The ReadUshortFromPLC method, the ReadShortFromPLC method, and the ReadFloatFromPLC method are used in the code in the link at the end of this article.

Run the program, connect successfully, and read data

 Note here that the slave station address is generally 1, unless you change it, the start address is 0, which means the start address of the register, and the number is 3, which means the number of read 3 registers, that is, the first 3 variables, m1-speed , m1-duaror, m1-level

 Why can’t the quantity here be 4, because the fourth variable is real, which occupies 2 registers, that is, 4 bytes, it is not ushort type, and the address here cannot be written as %DB3.DBW4, which is not the S7 protocol Reading variables is MODBUS reading registers, the two are different, don't be confused, elders.

8. Read code -- float type

 Many people are confused about the start address and quantity. The start address is the address of Modbus, and the Modbus address number starts from 0, so the addresses of the 8 variables are 0, 1, 2, 3, 4, 5, 6, 7, quantity It refers to the number of registers to be read, word occupies one, real occupies two, it is difficult to understand here, it is more confusing, one is the PLC address, and the other is the MODBUS address

The temperature we want to read is the third register, which is of real type and occupies 2 registers

If you want to read "Temperature of Motou 2", how do you read it?

 

 Think about it, why is the starting address 8?

9. The written code -- ushort type

 /// <summary>
        /// 写入
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnWrite_Click(object sender, EventArgs e)
        {
            //功能码
            string writeType = cboWriteTypes.Text.Trim();
            //从站地址
            byte slaveAddr = byte.Parse(txtWSlaveId.Text.Trim());
            //开始地址
            ushort startAddr = ushort.Parse(txtWStartAddress.Text.Trim());
            //数量
            //实际数量
            string objWriteVals = "";
            string dataType = cmddatatype2.Text.Trim();
            switch (dataType)
            {
                case "ushort":
                    objWriteVals = txtWriteDatas1.Text.Trim();
                    break;
                case "short":
                    objWriteVals = txtWriteDatas2.Text.Trim();
                    break;
                case "float":
                    objWriteVals = txtWriteDatas3.Text.Trim();
                    break;
            }
            ushort writeCount = ushort.Parse(txtWCount.Text.Trim()); 
            ushort objWCount = (ushort)objWriteVals.Split(',').Length;
            //实际数量与要求数量不一致,不允许操作
            if (writeCount != objWCount)
            {
                MessageBox.Show("写入值的数量不正确!");
                return;
            }
            string vals = objWriteVals;
            switch (writeType)
            {
                case "写单线圈":
                    bool blVal = vals == "1" ? true : false;
                    try
                    {
                        master.WriteSingleCoil(slaveAddr, startAddr, blVal);
                        MessageBox.Show("【单线圈】写入成功!");
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                    break;
                case "写单保持寄存器":
                    ushort uVal01 = ushort.Parse(vals);
                    try
                    {
                        master.WriteSingleRegister(slaveAddr, startAddr, uVal01);
                        MessageBox.Show("【单保持寄存器】写入成功!");
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                    break;
                case "写多线圈":
                    bool[] blVals = vals.Split(',').Select(s => s == "1" ? true : false).ToArray();//bool数组
                    try
                    {
                        master.WriteMultipleCoils(slaveAddr, startAddr, blVals);
                        MessageBox.Show("【多线圈】写入成功!");
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                    break;
                case "写多保持寄存器":
                    try
                    {
                        //功能码
                        //string dataType = cmddatatype2.Text.Trim();
                        switch (dataType)
                        {
                            case "ushort":
                                情况1:写入无符号的整数,即写入ushort数据,如写入33,44
                                ushort[] uVals01 = vals.Split(',').Select(s => ushort.Parse(s)).ToArray();
                                master.WriteMultipleRegisters(startAddr, uVals01);
                                break;
                            case "short":
                                //情况2:写入有符号的整数,即写入short数据,如写入-133,-65,98等,处理方法是:short[]=>byte[]=>ushort[],情况2包括了情况1 
                                short[] uVals02 = vals.Split(',').Select(s => short.Parse(s)).ToArray();
                                byte[] y2 = ByteArrayLib.GetByteArrayFromShortArray(uVals02);
                                ushort[] ushorts2 = UShortLib.GetUShortArrayFromByteArray(y2);
                                master.WriteMultipleRegisters(startAddr, ushorts2);
                                MessageBox.Show("【short类型数据】写入成功!");
                                break;
                            case "float":
                                //情况3:写入有符号的小数,即写入float数据,如写入-6.3,-2.65,56.893,51,-465等,处理方法是:float[]=>byte[]=>ushort[],情况3包括了情况2和情况1 
                                float[] uVals03 = vals.Split(',').Select(s => float.Parse(s)).ToArray();
                                byte[] y3 = ByteArrayLib.GetByteArrayFromFloatArray(uVals03);
                                ushort[] ushorts3 = UShortLib.GetUShortArrayFromByteArray(y3);
                                master.WriteMultipleRegisters(startAddr, ushorts3);
                                MessageBox.Show("【float类型数据】写入成功!");
                                break;
                        }



                        情况2:写入有符号的整数,即写入short数据,如写入-133,-65,98等,处理方法是:short[]=>byte[]=>ushort[],情况2包括了情况1 
                        //short[] uVals02 = vals.Split(',').Select(s => short.Parse(s)).ToArray();
                        //byte[] y = ByteArrayLib.GetByteArrayFromShortArray(uVals02);
                        //ushort[] ushorts = UShortLib.GetUShortArrayFromByteArray(y);
                        //master.WriteMultipleRegisters(slaveAddr, startAddr, ushorts);

                        情况3:写入有符号的小数,即写入float数据,如写入-6.3,-2.65,56.893,51,-465等,处理方法是:float[]=>byte[]=>ushort[],情况3包括了情况2和情况1 
                        //float[] uVals02 = vals.Split(',').Select(s => float.Parse(s)).ToArray();
                        //byte[] y = ByteArrayLib.GetByteArrayFromFloatArray(uVals02);
                        //ushort[] ushorts = UShortLib.GetUShortArrayFromByteArray(y);
                        //master.WriteMultipleRegisters(slaveAddr, startAddr, ushorts);

                        MessageBox.Show("【多保持寄存器】写入成功!");
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                    break;
            }
        }

 The writing is successful, and the value just written is also read at the same time, which can be seen in the monitoring table of Portal

10. The written code -- float type

 

write negative

 We write data to the register "Motor 2 Temperature"

Look at the data on the blog

 

11. Summary

The client creates a tcp client object, and then modbus uses the tcp object to create modbus communication, and then reads and writes PLC data through different data types, successfully

Code link:

Link: https://pan.baidu.com/s/1aCqv3eSX-7SXAdGtrGNpTw 
Extraction code: kyqo  

Guess you like

Origin blog.csdn.net/hqwest/article/details/132450229