C# ModbusTCP命令功能码

1. Modbus概述

Modbus原先是施耐德电气(Schneider Electric)为PLC(可编程逻辑控制器)通信而研发的一种串行通信协议,现在它已经成为工业领域通信协议的业界标准,并且现在是工业电子、工业控制、电力等设备之间常用的连接方式。Modbus协议使用的是主从通讯技术,即由主设备主动查询和操作从设备。
另外,我们也常听说另外一种协议BACnet,它是智能建筑的通信协议,主要是针对智能建筑和控制系统,用于照明、暖气、空调、门禁、火警等相关设备。通常在智能建筑方面,我们会看到支持两种协议共存的设备,它们有一定的映射关系,可以相互转换。

2.Modbus RTU、ASCII和TCP

Modbus协议目前应用于串口、以太网以及其他支持互联网的网络,它有多种协议,但是常使用的是两个,即Modbus RTU和Modbus TCP。
在串行总线上运行时,Modbus 协议又分为RTU和ASCII两种模式。RTU是Remote Terminal Unit的缩写,意思是远程终端单元。ASCII是American Standard Code for Information Interchanged的缩写,意思是美国信息交换标准代码,是将文字编辑符号、大小写字母、数字和一些不可见的控制字符进行编码的一种字符表示形式,除了空字符外,共127个字符编码。
Modbus tcp/ip协议应用于以太网链接,ModbusTCP的数据帧去掉了ModbusRTU中CRC校验,增加了MBAP报文头,可分为两部分:ADU = MBAP + PDU,即 MBAP + 功能码 + 数据域,其中MBAP是7个字节,功能码1byte,数据域长度不确定,是由其具体功能而决定的。
至于ModbusRTU、ModbusASCII和ModbusTCP具体内容,可参考小姚同学的文章https://blog.csdn.net/YiWangJiuShiXingFu/article/details/105557082,另外对于ModbusRTU详解专题可参考仰望星空e的https://blog.csdn.net/qq_36339249/article/details/90664839,对于ModbusTCP的专题可参考sgmcumt的https://blog.csdn.net/sgmcumt/article/details/87435191

3.ModbusTCP的八个主要功能码命令

功能码作 用
0x01读线圈
0x02读离散量输入
0x03读保持寄存器
0x04读输入寄存器
0x05写单个线圈
0x06写单个寄存器
0x0F写多个线圈
0x10写多个寄存器

4.ModbusTCP的八条命令生成代码

前面讲的其实都些Modbus的基本概念,如果大概了解其含义后,就可利用下面代码生成这八条功能码命令了,返回的是二进制数组,可直接通过TCP客户端(Sockets.TcpClient)发送到ModbusTCP设备。
本程序参数id=-40000是命令行的MBAP报文头1-2字节,其默认值-40000表示采用自动增量,也可以自己修改其值,表示自定义的报文编号。同时,程序异常处理程序Action<string, Exception> ModbusException同样也给程序员外暴异常处理方法,可根据需求去定义处理异常的代码。

#region Modbus TCP

    /// <summary>
    /// ModbusTCP的0x01-0x04读取线圈或寄存器的4个命令方法
    /// 以及0x05-0x10写入线圈或寄存器的4个命令方法
    /// </summary>
    public class ModbusTCP
    {
        #region 报文序号
        private ushort _head { get; set; } = 0;
        private object OBJ = 0;
        //报文号从1开始递增到65530后,再复位至1重新计数
        private byte[] head()
        {
            lock (OBJ)
            {
                if (_head >= 65530)
                {
                    _head = 0;
                }
                _head++;

                return BitConverter.GetBytes(_head);
            }
        }
        #endregion

        #region MBAP报文头
        /// <summary>
        /// MBAP报文头1-2字节,报文序号
        /// </summary>
        private byte[] Head { get; set; } = { 0x00, 0x00 };
        /// <summary>
        /// MBAP报文头5-6字节,从第7字节(含第7字节)至报文结束总共长度
        /// </summary>
        private byte[] MBAPTotalLength { get; set; } = { 0x00, 0x00 };
        /// <summary>
        /// MBAP报文第7字节:从站地址(站号/点名)
        /// </summary>
        private byte slaveAddress { get; set; } = 0x00;
        /// <summary>
        /// MBAP报文头
        /// </summary>
        private byte[] MBAP
        {
            get
            {
                byte[] _MBAP = new byte[7] { 0, 0, 0, 0, 0, 0, 0 };
                Array.Copy(Head, 0, _MBAP, 0, 2);
                Array.Copy(MBAPTotalLength, 0, _MBAP, 4, 2);
                _MBAP[6] = slaveAddress;
                return _MBAP;
            }
        }
        #endregion

        #region 读取线圈,功能码0x01
        /// <summary>
        /// 读取线圈命令行,功能码0x01
        /// </summary>
        /// <param name="SlaveAddress">从站地址(站点)</param>
        /// <param name="StartAddress">开始地址</param>
        /// <param name="NumberOfPoints">线圈数量</param>
        /// <param name="id">MBAP报文头(事务元标识符)【1-2字节】,默认自动进行增量编号</param>
        /// <returns>0x01命令行</returns>
        public byte[] ReadCoilStatus_0x01(int SlaveAddress, int StartAddress, int NumberOfPoints, int id = -40000)
        {

            //命令行
            byte[] command = new byte[12];

            try
            {
                //数量限制
                if (NumberOfPoints > 2000)
                {
                    throw new Exception("读取线圈数量不能超过2000");
                }
                //
                ushort mStartAddress = (ushort)IPAddress.HostToNetworkOrder((short)StartAddress);
                ushort mNumberOfPoints = (ushort)IPAddress.HostToNetworkOrder((short)NumberOfPoints);

                //MBAP报文头组合,高低字节换位(与主机字节相反)
                if (id == -40000)
                {
                    Head = head().Reverse().ToArray();
                }
                else
                {
                    Head = BitConverter.GetBytes((ushort)id).Reverse().ToArray();
                }
                MBAPTotalLength = BitConverter.GetBytes((ushort)(command.Length - 6)).Reverse().ToArray();
                slaveAddress = Convert.ToByte(SlaveAddress);
                //线圈起始地址,读取长度
                byte[] start = BitConverter.GetBytes(mStartAddress);
                byte[] length = BitConverter.GetBytes(mNumberOfPoints);

                //命令行加载报文头
                Array.Copy(MBAP, 0, command, 0, 7);

                //功能码
                command[7] = 0x01;//功能码

                //命令行加载起始地址
                Array.Copy(start, 0, command, 8, 2);
                //命令行加载读取数量
                Array.Copy(length, 0, command, 10, 2);


            }
            catch (Exception ex)
            {
                ModbusException?.BeginInvoke("功能码0x01", ex, null, null);
            }

            return command;
        }

        #endregion

        #region 读取离散量,功能码0x02
        /// <summary>
        /// 读取离散量命令行,功能码0x02
        /// </summary>
        /// <param name="SlaveAddress">从站地址(站点)</param>
        /// <param name="StartAddress">开始地址</param>
        /// <param name="NumberOfPoints">线圈数量</param>
        /// <param name="id">MBAP报文头(事务元标识符)【1-2字节】,默认自动进行增量编号</param>
        /// <returns>0x02命令行</returns>
        public byte[] ReadInputStatus_0x02(int SlaveAddress, int StartAddress, int NumberOfPoints, int id = -40000)
        {
            //命令行
            byte[] command = new byte[12];

            try
            {
                //数量限制
                if (NumberOfPoints > 2000)
                {
                    throw new Exception("读取离散量不能超过2000");
                }
                //
                //ushort mhead = (ushort)IPAddress.HostToNetworkOrder((short)head);
                ushort mStartAddress = (ushort)IPAddress.HostToNetworkOrder((short)StartAddress);
                ushort mNumberOfPoints = (ushort)IPAddress.HostToNetworkOrder((short)NumberOfPoints);

                //MBAP报文头组合
                if (id == -40000)
                {
                    Head = head().Reverse().ToArray();
                }
                else
                {
                    Head = BitConverter.GetBytes((ushort)id).Reverse().ToArray();
                }

                MBAPTotalLength = BitConverter.GetBytes((ushort)(command.Length - 6)).Reverse().ToArray();
                slaveAddress = Convert.ToByte(SlaveAddress);
                //离散量起始地址,读取长度
                byte[] start = BitConverter.GetBytes(mStartAddress);
                byte[] length = BitConverter.GetBytes(mNumberOfPoints);

                //命令行加载报文头
                Array.Copy(MBAP, 0, command, 0, 7);

                //功能码
                command[7] = 0x02;//功能码

                //命令行加载起始地址
                Array.Copy(start, 0, command, 8, 2);
                //命令行加载读取数量
                Array.Copy(length, 0, command, 10, 2);


            }
            catch (Exception ex)
            {
                ModbusException?.BeginInvoke("功能码0x02", ex, null, null);
            }

            return command;
        }
        #endregion

        #region 读取保持寄存器,功能码0x03
        /// <summary>
        /// 读取保持寄存器命令行,功能码0x03
        /// </summary>
        /// <param name="SlaveAddress">从站地址(站点)</param>
        /// <param name="StartAddress">开始地址</param>
        /// <param name="NumberOfPoints">寄存器数量</param>
        /// <param name="id">MBAP报文头(事务元标识符)【1-2字节】,默认自动进行增量编号</param>
        /// <returns>0x03命令行</returns>
        public byte[] ReadHoldingRegister_0x03(int SlaveAddress, int StartAddress, int NumberOfPoints, int id = -40000)
        {
            //命令行
            byte[] command = new byte[12];

            try
            {
                //数量限制
                if (NumberOfPoints > 125)
                {
                    throw new Exception("读取保持寄存器数量不能超过125");
                }

                //MBAP报文头组合,高低字节换位(与主机字节相反)
                if (id == -40000)
                {
                    Head = head().Reverse().ToArray();
                }
                else
                {
                    Head = BitConverter.GetBytes((ushort)id).Reverse().ToArray();
                }
                MBAPTotalLength = BitConverter.GetBytes((ushort)(command.Length - 6)).Reverse().ToArray();
                slaveAddress = Convert.ToByte(SlaveAddress);
                //保持寄存器起始地址,读取长度
                byte[] start = BitConverter.GetBytes((ushort)StartAddress).Reverse().ToArray();
                byte[] length = BitConverter.GetBytes((ushort)NumberOfPoints).Reverse().ToArray();

                //命令行加载报文头
                Array.Copy(MBAP, 0, command, 0, 7);

                //功能码
                command[7] = 0x03;//功能码

                //命令行加载起始地址
                Array.Copy(start, 0, command, 8, 2);
                //命令行加载读取数量
                Array.Copy(length, 0, command, 10, 2);


            }
            catch (Exception ex)
            {
                ModbusException?.BeginInvoke("功能码0x03", ex, null, null);
            }

            return command;
        }
        #endregion

        #region 读取输入寄存器,功能码0x04
        /// <summary>
        /// 读取输入寄存器命令行,功能码0x04
        /// </summary>
        /// <param name="head">报文序号</param>
        /// <param name="SlaveAddress">从站地址(站点)</param>
        /// <param name="StartAddress">开始地址</param>
        /// <param name="NumberOfPoints">寄存器数量</param>
        /// <param name="id">MBAP报文头(事务元标识符)【1-2字节】,默认自动进行增量编号</param>
        /// <returns>0x04命令行</returns>
        public byte[] ReadInputRegister_0x04(int SlaveAddress, int StartAddress, int NumberOfPoints, int id = -40000)
        {
            //命令行
            byte[] command = new byte[12];

            try
            {
                //数量限制
                if (NumberOfPoints > 125)
                {
                    throw new Exception("读取输入寄存器数量不能超过125");
                }

                //MBAP报文头组合,高低字节换位(与主机字节相反)
                if (id == -40000)
                {
                    Head = head().Reverse().ToArray();
                }
                else
                {
                    Head = BitConverter.GetBytes((ushort)id).Reverse().ToArray();
                }
                MBAPTotalLength = BitConverter.GetBytes((ushort)(command.Length - 6)).Reverse().ToArray();
                slaveAddress = Convert.ToByte(SlaveAddress);
                //输入寄存器起始地址,读取长度
                byte[] start = BitConverter.GetBytes((ushort)StartAddress).Reverse().ToArray();
                byte[] length = BitConverter.GetBytes((ushort)NumberOfPoints).Reverse().ToArray();

                //命令行加载报文头
                Array.Copy(MBAP, 0, command, 0, 7);

                //功能码
                command[7] = 0x04;//功能码

                //命令行加载起始地址
                Array.Copy(start, 0, command, 8, 2);
                //命令行加载读取数量
                Array.Copy(length, 0, command, 10, 2);


            }
            catch (Exception ex)
            {
                ModbusException?.BeginInvoke("功能码0x04", ex, null, null);
            }

            return command;
        }
        #endregion

        #region 写单个线圈,功能码0x05
        /// <summary>
        /// 写单个线圈命令行,功能码0x05
        /// </summary>
        /// <param name="head">报文序号</param>
        /// <param name="SlaveAddress">从站地址(站点)</param>
        /// <param name="StartAddress">开始地址</param>
        /// <param name="Switch">线圈的值</param>
        /// <param name="id">MBAP报文头(事务元标识符)【1-2字节】,默认自动进行增量编号</param>
        /// <returns>0x05命令行</returns>
        public byte[] WriteSingleCoil_0x05(int SlaveAddress, int StartAddress, bool Switch, int id = -40000)
        {
            //命令行
            byte[] command = new byte[12];

            try
            {
                //MBAP报文头组合,高低字节换位(与主机字节相反)
                if (id == -40000)
                {
                    Head = head().Reverse().ToArray();
                }
                else
                {
                    Head = BitConverter.GetBytes((ushort)id).Reverse().ToArray();
                }
                MBAPTotalLength = BitConverter.GetBytes((ushort)(command.Length - 6)).Reverse().ToArray();
                slaveAddress = Convert.ToByte(SlaveAddress);
                //单个线圈起始地址
                byte[] start = BitConverter.GetBytes((ushort)StartAddress).Reverse().ToArray();
                //单个线圈写入的值
                byte[] power = new byte[2];
                //如果线圈写入开,则为0xFF00
                if (Switch)
                {
                    power = new byte[2] { 0xFF, 0x00 };
                }

                //命令行加载报文头
                Array.Copy(MBAP, 0, command, 0, 7);

                //功能码
                command[7] = 0x05;//功能码

                //命令行加载起始地址
                Array.Copy(start, 0, command, 8, 2);
                //命令行加载读取数量
                Array.Copy(power, 0, command, 10, 2);


            }
            catch (Exception ex)
            {
                ModbusException?.BeginInvoke("功能码0x05", ex, null, null);
            }

            return command;
        }
        #endregion

        #region 写单个保持寄存器,功能码0x06
        /// <summary>
        /// 写单个保持寄存器命令行,功能码0x06
        /// </summary>
        /// <param name="SlaveAddress">从站地址(站点)</param>
        /// <param name="StartAddress">开始地址</param>
        /// <param name="Values">单个保持寄存器值(16位,byte[0]低位,btye[1]高位)</param>
        /// <param name="id">MBAP报文头(事务元标识符)【1-2字节】,默认自动进行增量编号</param>
        /// <returns>0x06命令行</returns>
        public byte[] WriteSingleRegister_0x06(int SlaveAddress, int StartAddress, byte[] Values, int id = -40000)
        {
            //命令行
            byte[] command = new byte[12];

            try
            {
                //数量限制
                if (Values.Length != 2)
                {
                    throw new Exception("写单个保持寄存器的值必须是16位");
                }

                //MBAP报文头组合,高低字节换位(与主机字节相反)
                if (id == -40000)
                {
                    Head = head().Reverse().ToArray();
                }
                else
                {
                    Head = BitConverter.GetBytes((ushort)id).Reverse().ToArray();
                }
                MBAPTotalLength = BitConverter.GetBytes((ushort)(command.Length - 6)).Reverse().ToArray();
                slaveAddress = Convert.ToByte(SlaveAddress);
                //保持寄存器起始地址,读取长度
                byte[] start = BitConverter.GetBytes((ushort)StartAddress).Reverse().ToArray();
                byte[] values = Values.Reverse().ToArray();

                //命令行加载报文头
                Array.Copy(MBAP, 0, command, 0, 7);

                //功能码
                command[7] = 0x06;//功能码

                //命令行加载起始地址
                Array.Copy(start, 0, command, 8, 2);
                //命令行加载读取数量
                Array.Copy(values, 0, command, 10, 2);


            }
            catch (Exception ex)
            {
                ModbusException?.BeginInvoke("功能码0x06", ex, null, null);
            }

            return command;
        }
        #endregion

        #region 写多个线圈,功能码0x0F
        /// <summary>
        /// 写多个线圈,功能码0x0F,数值是从低到高的bool的List序列
        /// </summary>
        /// <param name="SlaveAddress">从站地址(站点)</param>
        /// <param name="StartAddress">开始地址</param>
        /// <param name="data">线圈写入数值(bool表示),从低位到高位的List序列</param>
        /// <param name="id">MBAP报文头(事务元标识符)【1-2字节】,默认自动进行增量编号</param>
        /// <returns>0x0F命令行</returns>
        public byte[] WriteMultipleCoil_0x0F(int SlaveAddress, int StartAddress, List<bool> data, int id = -40000)
        {
            if (data.Count > 1968)
            {
                throw new Exception("写多个线圈的数量最多为246个(1968位)");
            }

            //线圈数值长度(按字节算),报文【第13个字节(索引12)】
            byte dataByteCount = Convert.ToByte(Math.Ceiling((double)data.Count / 8));
            //命令行
            byte[] command = new byte[13 + dataByteCount];

            try
            {
                //MBAP报文头组合,高低字节换位(与主机字节相反)
                if (id == -40000)
                {
                    Head = head().Reverse().ToArray();
                }
                else
                {
                    Head = BitConverter.GetBytes((ushort)id).Reverse().ToArray();
                }
                MBAPTotalLength = BitConverter.GetBytes((ushort)(command.Length - 6)).Reverse().ToArray();
                slaveAddress = Convert.ToByte(SlaveAddress);
                //保持寄存器起始地址,读取长度
                byte[] start = BitConverter.GetBytes((ushort)StartAddress).Reverse().ToArray();

                //命令行加载报文头【1-7字节】
                Array.Copy(MBAP, 0, command, 0, 7);

                //功能码
                command[7] = 0x0F;//功能码【第8字节】

                //命令行加载起始地址
                Array.Copy(start, 0, command, 8, 2);//【9-10字节】

                //线圈数值长度(接位数算),报文【第11,12字节(索引10,11)】
                byte[] dataNumCount = BitConverter.GetBytes((ushort)data.Count).Reverse().ToArray();
                //命令行加载线圈数值数量(按位数算)
                Array.Copy(dataNumCount, 0, command, 10, 2);//【11-12字节】
                //命令行加载线圈数值长度(按字节算)【13字节】
                command[12] = dataByteCount;

                //**线圈数据转换成字节表示
                int mod = data.Count % 8;//data数值放在最后一个字节里,总共有多少位
                byte[] dataValues = new byte[dataByteCount];//data数据所用的字节

                for (int i = 0; i < dataByteCount; i++)//data数值分配到每个字节
                {
                    //最后一个字节分配的data不满8位
                    if (mod != 0 && i == dataByteCount - 1)
                    {
                        for (int j = 0; j < mod; j++)
                        {
                            if (data[8 * i + j])
                            {
                                dataValues[i] = (byte)(dataValues[i] | (1 << j));
                            }
                        }
                    }
                    else//data数值正好能分配到每个字节,即data的最后8位正好能满一个字节
                    {
                        for (int j = 0; j < 8; j++)
                        {
                            if (data[8 * i + j])
                            {
                                dataValues[i] = (byte)(dataValues[i] | (1 << j));
                            }

                        }
                    }

                }

                //将data数值加载到命令行中
                Array.Copy(dataValues, 0, command, 13, dataByteCount);//【14-dataByteCount字节】

            }
            catch (Exception ex)
            {
                ModbusException?.BeginInvoke("功能码0x0F", ex, null, null);
            }

            return command;
        }
        #endregion

        #region 写多个保持寄存器,功能码0x10
        /// <summary>
        /// 写多个保持寄存器命令行,功能码0x10,数值是单个16位寄存器byte[2]的List序列(byte[0]低位,byte[1]高位)
        /// </summary>
        /// <param name="SlaveAddress">从站地址(站点)</param>
        /// <param name="StartAddress">开始地址</param>
        /// <param name="data">单个保持寄存器值(16位,byte[0]低位,btye[1]高位)的List序列</param>
        /// <param name="id">MBAP报文头(事务元标识符)【1-2字节】,默认自动进行增量编号</param>
        /// <returns>0x10命令行</returns>
        public byte[] WriteMultipleRegister_0x10(int SlaveAddress, int StartAddress, List<byte[]> data, int id = -40000)
        {
            if (data.Count > 120)
            {
                throw new Exception("写多个保持寄存器的数量最多为120个");
            }

            if (data.Where(u => u.Length != 2).Count() > 0)
            {
                throw new Exception("写多个保持寄存器的数值中含有不是16位的值");
            }
            //保持寄存器数值长度,每个数值是2个字节【第13个字节,索引是12】
            byte dataByteCount = Convert.ToByte(data.Count * 2);
            //命令行
            byte[] command = new byte[13 + dataByteCount];

            try
            {
                //MBAP报文头组合,高低字节换位(与主机字节相反)
                if (id == -40000)
                {
                    Head = head().Reverse().ToArray();
                }
                else
                {
                    Head = BitConverter.GetBytes((ushort)id).Reverse().ToArray();
                }
                MBAPTotalLength = BitConverter.GetBytes((ushort)(command.Length - 6)).Reverse().ToArray();
                slaveAddress = Convert.ToByte(SlaveAddress);

                //命令行加载报文头【1-7字节】
                Array.Copy(MBAP, 0, command, 0, 7);

                //功能码
                command[7] = 0x10;//功能码【第8字节】

                //保持寄存器起始地址
                byte[] start = BitConverter.GetBytes((ushort)StartAddress).Reverse().ToArray();
                //命令行加载起始地址
                Array.Copy(start, 0, command, 8, 2);//【9-10字节】

                //保持寄存器数值数量,报文【第11,12字节(索引10,11)】
                byte[] dataNumCount = BitConverter.GetBytes((ushort)data.Count).Reverse().ToArray();
                //命令行加载保持寄存器数值数量
                Array.Copy(dataNumCount, 0, command, 10, 2);//【11-12字节】
                //命令行加载保持寄存器数值长度(按字节算)【13字节】
                command[12] = dataByteCount;

                //data数值加载到命令行
                for (int i = 0; i < data.Count; i++)
                {
                    Array.Copy(data[i].Reverse().ToArray(), 0, command, 13 + 2 * i, 2);
                }

            }
            catch (Exception ex)
            {
                ModbusException?.BeginInvoke("功能码0x10", ex, null, null);
            }

            return command;
        }
        #endregion

        #region 异常处理程序
        public Action<string, Exception> ModbusException { get; set; }
        #endregion

    }
   
    #endregion //Modbus TCP

5.总结

Modbus协议具体含义非本文重点,可以通过上面所说别的博主文章进行参考学习,也非常感谢上述博主的文章,让我学习到很多,知识是积累而来的,此处代码也算是自己的学习笔记。

  • 3
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
C# ModbusTCP是基于C#语言实现的ModbusTCP通信协议。ModbusTCP是一种工业现场总线协议标准,用于实现设备之间的通信。在C#中,可以使用第三方库或者自行编写代来实现ModbusTCP通信。 通常,使用C#实现ModbusTCP通信需要以下步骤: 1. 导入适当的库或包,例如NModbus库,以便在C#中使用ModbusTCP功能。 2. 创建一个ModbusTCP客户端实例,使用指定的IP地址和端口连接到ModbusTCP服务器。 3. 根据需要,通过客户端实例来读取或写入数据,使用相应的功能和寄存器地址。 4. 处理返回的数据,根据需要进行解析和处理。 以下是一个简单的C#示例,演示了如何使用NModbus库实现ModbusTCP通信: ```csharp using System; using System.Net; using System.Net.Sockets; using Modbus; using Modbus.Data; using Modbus.Device; namespace ModbusExample { class Program { static void Main(string[] args) { // 创建一个ModbusTCP客户端实例,连接到ModbusTCP服务器 TcpClient client = new TcpClient(); client.Connect(IPAddress.Parse("192.168.0.1"), 502); ModbusIpMaster master = ModbusIpMaster.CreateIp(client); // 读取保持寄存器的值 ushort[] values = master.ReadHoldingRegisters(1, 0, 10); // 处理返回的数据 foreach (ushort value in values) { Console.WriteLine(value); } // 关闭连接 client.Close(); } } } ``` 请注意,上述代只是一个简单示例,并且仅用于说明如何使用C#和NModbus库进行ModbusTCP通信。实际上,你可能需要根据具体的需求和设备进行适当的配置和调整。 希望这个示例可以帮助你理解如何在C#中使用ModbusTCP通信协议。如果有任何进一步的问题,请随时提问。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [C#实现MODBUS TCP 通信基础理论](https://blog.csdn.net/sharenfish/article/details/121691952)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值