优秀的编程知识分享平台

网站首页 > 技术文章 正文

C#工控上位机系列(7)- Modbus TCP通信

nanyue 2025-04-30 18:37:11 技术文章 4 ℃

客户需要一个电子看板,类似一个电视大小的, 可以显示生产的型号,单号数量等信息. 电子看板是用Modbus TCP通讯的. 生产线每完成一件产品的测试,扫码打包后, 实际产量要增加1, 所以要和生产数据库连接起来

下载一个开源的C# Modbus的工具
https://github.com/stephan1827/modbusTCP-DotNET

里面关键代码就是构建Modbus TCP的Header

报文头 报文的序列号2字节, 00 00表示ModbusTCP协议,数据长度2字节,设备地址1字节,

功能码为1字节,寄存器地址2字节,读取长度2字节

Modbus的操作对象有四种:线圈、离散输入、保持寄存器、输入寄存器。

对象			
Coil线圈	可读可写
DiscreteInputs离散量				只读
InputRegister输入寄存器		只读
HoldingRegiste保持寄存器		可读可写

//功能码
				private const byte fctReadCoil = 1;
        private const byte fctReadDiscreteInputs = 2;
        private const byte fctReadHoldingRegister = 3;
        private const byte fctReadInputRegister = 4;
        private const byte fctWriteSingleCoil = 5;
        private const byte fctWriteSingleRegister = 6;
        private const byte fctWriteMultipleCoils = 15;
        private const byte fctWriteMultipleRegister = 16;
        private const byte fctReadWriteMultipleRegister = 23;
  // ------------------------------------------------------------------------
        // Create modbus header for read action
        private byte[] CreateReadHeader(ushort id, byte unit, ushort startAddress, ushort length, byte function)
        {
            byte[] data = new byte[12];

            byte[] _id = BitConverter.GetBytes((short)id);
            data[0] = _id[1];			    // Slave id high byte
            data[1] = _id[0];				// Slave id low byte
            data[5] = 6;					// Message size ,数据长度
            data[6] = unit;					// Slave address 设备地址
            data[7] = function;				// Function code 功能码
            byte[] _adr = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)startAddress));
            data[8] = _adr[0];				// Start address 寄存器地址
            data[9] = _adr[1];				// Start address
            byte[] _length = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)length));
            data[10] = _length[0];			// Number of data to read 读取长度
            data[11] = _length[1];			// Number of data to read
            return data;
        }

        // ------------------------------------------------------------------------
        // Create modbus header for write action
        private byte[] CreateWriteHeader(ushort id, byte unit, ushort startAddress, ushort numData, ushort numBytes, byte function)
        {
            byte[] data = new byte[numBytes + 11];

            byte[] _id = BitConverter.GetBytes((short)id);
            data[0] = _id[1];				// Slave id high byte
            data[1] = _id[0];				// Slave id low byte
            byte[] _size = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)(5 + numBytes)));
            data[4] = _size[0];				// Complete message size in bytes
            data[5] = _size[1];				// Complete message size in bytes
            data[6] = unit;					// Slave address
            data[7] = function;				// Function code
            byte[] _adr = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)startAddress));
            data[8] = _adr[0];				// Start address
            data[9] = _adr[1];				// Start address
            if (function >= fctWriteMultipleCoils)
            {
                byte[] _cnt = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)numData));
                data[10] = _cnt[0];			// Number of bytes
                data[11] = _cnt[1];			// Number of bytes
                data[12] = (byte)(numBytes - 2);
            }
            return data;
        }

了解了ModBus TCP协议后,我们看看怎么来读写寄存器的例子

 try
            {
                
                // Create new modbus master and add event functions
                MBmaster = new Master("192.168.8.1", 502, true);
                MBmaster.OnResponseData += new ModbusTCP.Master.ResponseData(MBmaster_OnResponseData);
                MBmaster.OnException += new ModbusTCP.Master.ExceptionData(MBmaster_OnException);
  
                ////取得工人人数等信息
                ReadHoldingRegister();
                lblMsg.Text = "连接成功";
                
            }
            catch (SystemException error)
            {
                MessageBox.Show(error.Message);
                lblMsg.Text = error.Message;

            }
  			// ------------------------------------------------------------------------
        // read holding register
        // 工人人数,计划数量,实际产量,已耗时长,已耗时长显示保留小数位数 存在地址1到6
        // 
        // ------------------------------------------------------------------------
        private void ReadHoldingRegister()
        {
            ushort ID = 3;  //自己定义的序号
            byte unit = Convert.ToByte("1"); //Modus 地址1
            ushort StartAddress = 0; //开始地址
            UInt16 Length =6; //长度
            if (MBmaster != null)
            {
                MBmaster.ReadHoldingRegister(ID, unit, StartAddress, Length); //异步方法
            } 

        }
 private void MBmaster_OnResponseData(ushort ID, byte unit, byte function, byte[] values)
        {
            // ------------------------------------------------------------------
            // Seperate calling threads
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new Master.ResponseData(MBmaster_OnResponseData), new object[] { ID, unit, function, values });
                return;
            }

            // ------------------------------------------------------------------------
            // Identify requested data
            switch (ID)
            {

                case 3:
                    //"Read holding register";
                    //根据返回值数组大小,来判断哪个数据
                    int[] word = ConvertBytesToWords(values);
                    if(word.Length==6)
                    {
                        Console.WriteLine("return ActualCnt=" + ActualCnt);
                        WorkerCnt = word[0];
                        PlanCnt = word[1];
                        ActualCnt = word[2];
                        txtWorkerCnt.Text = WorkerCnt.ToString();
                        txtActual.Text = ActualCnt.ToString();
                        txtPlanCnt.Text = PlanCnt.ToString();
                        txtLeftCnt.Text = (PlanCnt - ActualCnt).ToString();
                    }
                   
                    break;

                case 8:
                    //grpData.Text = "Write multiple register";
                    break;
            }
        }
最近发表
标签列表