C#基于NModbus实现MODBUSTCP字符串、浮点数读写

博客介绍了在C#中使用NModbus的相关操作。包括在NuGet搜索并添加NModbus引用,封装ModbusTcp类,进行连接测试、写入测试、读取测试等。还提到对ushort类型进行转换,以及字符串的写入和读取,同时给出了Modbus Slave的下载链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引用NModbus
在NuGet搜索NModbus,添加引用。
在这里插入图片描述

封装ModbusTcp类
public class ModbusTCP
{
private ModbusFactory modbusFactory;
private IModbusMaster master;
private TcpClient tcpClient;

    public string IPAdress { get; set; }
    public int Port { get; set; }

    public bool Connected
    {
        get => tcpClient.Connected;
    }

    public ModbusTCP(string ip, int port)
    {
        IPAdress = ip;
        Port = port;

        modbusFactory = new ModbusFactory();
        tcpClient = new TcpClient(IPAdress, Port);
        master = modbusFactory.CreateMaster(tcpClient);
        master.Transport.ReadTimeout = 2000;
        master.Transport.Retries = 10;
    }


    public bool[] ReadCoils(byte slaveAddress, ushort startAddress, ushort num)
    {
        return master.ReadCoils(slaveAddress, startAddress, num);
    }

    public bool[] ReadInputs(byte slaveAddress, ushort startAddress, ushort num)
    {
        return master.ReadInputs(slaveAddress, startAddress, num);
    }

    public ushort[] ReadHoldingRegisters(byte slaveAddress, ushort startAddress, ushort num)
    {
        return master.ReadHoldingRegisters(slaveAddress, startAddress, num);
    }

    public ushort[] ReadInputRegisters(byte slaveAddress, ushort startAddress, ushort num)
    {
        return master.ReadInputRegisters(slaveAddress, startAddress, num);
    }

    public void WriteSingleCoil(byte slaveAddress, ushort startAddress, bool value)
    {
        master.WriteSingleCoil(slaveAddress, startAddress, value);
    }

    public void WriteSingleRegister(byte slaveAddress, ushort startAddress, ushort value)
    {
        master.WriteSingleRegister(slaveAddress, startAddress, value);
    }

    public void WriteMultipleCoils(byte slaveAddress, ushort startAddress, bool[] value)
    {
        master.WriteMultipleCoils(slaveAddress, startAddress, value);
    }

    public void WriteMultipleRegisters(byte slaveAddress, ushort startAddress, ushort[] value)
    {
        master.WriteMultipleRegisters(slaveAddress, startAddress, value);
    }

}

连接测试
下载 Modbus Slave
链接:https://pan.baidu.com/s/1ydX_9KElkOTA7h-E3SIadw
提取码:7fh5
设置
在这里插入图片描述
在这里插入图片描述

连接
private ModbusTCP modbus;
private void ConnectExecute()
{
try
{
modbus = new ModbusTCP(IP, Port);
pollingTimer.Start();
}
catch (Exception ex)
{
Msg.Info(ex.Message);
}
}

在这里插入图片描述

写入测试
NModbus提供的对寄存器读写方法,只包括ushort类型,需要对ushort进行进行转换。
封装转换类型

    public class MODBUS
    {
        /// <summary>
        /// 赋值string
        /// </summary>
        /// <param name="src"></param>
        /// <param name="start"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static void SetString(ushort[] src, int start, string value)
        {
            byte[] bytesTemp = Encoding.UTF8.GetBytes(value);
            ushort[] dest = Bytes2Ushorts(bytesTemp);
            dest.CopyTo(src, start);
        }

        /// <summary>
        /// 获取string
        /// </summary>
        /// <param name="src"></param>
        /// <param name="start"></param>
        /// <param name="len"></param>
        /// <returns></returns>
        public static string GetString(ushort[] src, int start, int len)
        {
            ushort[] temp = new ushort[len];
            for (int i = 0; i < len; i++)
            {
                temp[i] = src[i + start];
            }
            byte[] bytesTemp = Ushorts2Bytes(temp);
            string res = Encoding.UTF8.GetString(bytesTemp).Trim(new char[] { '\0' });
            return res;
        }

        /// <summary>
        /// 赋值Real类型数据
        /// </summary>
        /// <param name="src"></param>
        /// <param name="start"></param>
        /// <param name="value"></param>
        public static void SetReal(ushort[] src, int start, float value)
        {
            byte[] bytes = BitConverter.GetBytes(value);

            ushort[] dest = Bytes2Ushorts(bytes);

            dest.CopyTo(src, start);
        }

        /// <summary>
        /// 获取float类型数据
        /// </summary>
        /// <param name="src"></param>
        /// <param name="start"></param>
        /// <returns></returns>
        public static float GetReal(ushort[] src, int start)
        {
            ushort[] temp = new ushort[2];
            for (int i = 0; i < 2; i++)
            {
                temp[i] = src[i + start];
            }
            byte[] bytesTemp = Ushorts2Bytes(temp);
            float res = BitConverter.ToSingle(bytesTemp, 0);
            return res;
        }

        /// <summary>
        /// 赋值Short类型数据
        /// </summary>
        /// <param name="src"></param>
        /// <param name="start"></param>
        /// <param name="value"></param>
        public static void SetShort(ushort[] src, int start, short value)
        {
            byte[] bytes = BitConverter.GetBytes(value);

            ushort[] dest = Bytes2Ushorts(bytes);

            dest.CopyTo(src, start);
        }

        /// <summary>
        /// 获取short类型数据
        /// </summary>
        /// <param name="src"></param>
        /// <param name="start"></param>
        /// <returns></returns>
        public static short GetShort(ushort[] src, int start)
        {
            ushort[] temp = new ushort[1];
            temp[0] = src[start];
            byte[] bytesTemp = Ushorts2Bytes(temp);
            short res = BitConverter.ToInt16(bytesTemp, 0);
            return res;
        }


        public static bool[] GetBools(ushort[] src, int start, int num)
        {
            ushort[] temp = new ushort[num];
            for (int i = start; i < start + num; i++)
            {
                temp[i] = src[i + start];
            }
            byte[] bytes = Ushorts2Bytes(temp);

            bool[] res = Bytes2Bools(bytes);

            return res;
        }

        private static bool[] Bytes2Bools(byte[] b)
        {
            bool[] array = new bool[8*b.Length];

            for (int i = 0; i < b.Length; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    array[i * 8 + j] = (b[i] & 1) == 1;//判定byte的最后一位是否为1,若为1,则是true;否则是false
                    b[i] = (byte)(b[i] >> 1);//将byte右移一位
                }
            }
            return array;
        }

        private static byte Bools2Byte(bool[] array)
        {
            if (array != null && array.Length > 0)
            {
                byte b = 0;
                for (int i = 0; i < 8; i++)
                {
                    if (array[i])
                    {
                        byte nn = (byte)(1 << i);//左移一位,相当于×2
                        b += nn;
                    }
                }
                return b;
            }
            return 0;
        }

        private static ushort[] Bytes2Ushorts(byte[] src, bool reverse = false)
        {
            int len = src.Length;

            byte[] srcPlus = new byte[len + 1];
            src.CopyTo(srcPlus, 0);
            int count = len >> 1;

            if (len % 2 != 0)
            {
                count += 1;
            }

            ushort[] dest = new ushort[count];
            if(reverse)
            {
                for (int i = 0; i < count; i++)
                {
                    dest[i] = (ushort)(srcPlus[i * 2] << 8 | srcPlus[2 * i + 1] & 0xff);
                }
            }
            else
            {
                for (int i = 0; i < count; i++)
                {
                    dest[i] = (ushort)(srcPlus[i * 2] & 0xff | srcPlus[2 * i + 1] << 8 );
                }
            }
         
            return dest;
        }

        private static byte[] Ushorts2Bytes(ushort[] src, bool reverse = false)
        {

            int count = src.Length;
            byte[] dest = new byte[count << 1];
            if(reverse)
            {
                for (int i = 0; i < count; i++)
                {
                    dest[i * 2] = (byte)(src[i] >> 8);
                    dest[i * 2 + 1] = (byte)(src[i] >> 0);
                }
            }
            else
            {
                for (int i = 0; i < count; i++)
                {
                    dest[i * 2] = (byte)(src[i] >> 0);
                    dest[i * 2 + 1] = (byte)(src[i] >> 8);
                }
            }
            return dest;
        }
    }

vb.net教程
c#教程
根据选择的类型写入

在这里插入图片描述
在这里插入图片描述

        private void WriteExecute()
        {
            try
            {
                if(VariableType == "real")
                {
                    ushort[] buff = new ushort[2];
                    float value = float.Parse(WriteValue);
                    MODBUS.SetReal(buff, 0, value);
                    modbus.WriteMultipleRegisters(SlaveID, WriteAddress, buff);
                }
                else if(VariableType == "string")
                {
                    ushort[] buff = new ushort[10];
                    MODBUS.SetString(buff, 0, WriteValue);
                    modbus.WriteMultipleRegisters(SlaveID, WriteAddress, buff);
                }
                else if(VariableType == "Int16")
                {
                    ushort[] buff = new ushort[1];
                    short value = short.Parse(WriteValue);
                    MODBUS.SetShort(buff, 0, value);
                    modbus.WriteMultipleRegisters(SlaveID, WriteAddress, buff);
                }
            }
            catch (Exception ex)
            {
                Msg.Info(ex.Message);
            }
        }

在这里插入图片描述

可以看到ModbusSlave上面的数据,一个浮点型数据占用两个寄存器,4个byte。按照下图设置后,可以看到,已经写入成功。

在这里插入图片描述

读取测试

private void ReadExecute()
{
    try
    {
        if (VariableType == "real")
        {
            ushort[] buff = modbus.ReadHoldingRegisters(SlaveID, ReadAddress, 2);
            float value = MODBUS.GetReal(buff, 0);
            ReadValue = value.ToString();
        }
        else if(VariableType == "string")
        {
            ushort[] buff = modbus.ReadHoldingRegisters(SlaveID, ReadAddress, 10);
            ReadValue = MODBUS.GetString(buff, 0, 10); 
        }
        else if(VariableType == "Int16")
        {
            ushort[] buff = modbus.ReadHoldingRegisters(SlaveID, ReadAddress, 1);
            short value = MODBUS.GetShort(buff, 0);
            ReadValue = value.ToString();
        }
    }
    catch (Exception ex)
    {
        Msg.Info(ex.Message);
    }
}

字符串写入和读取
参照VariableType=="string"部分代码。
在这里插入图片描述

转载请注明出处
https://www.cnblogs.com/Samberger/p/13039429.html

<think>好的,我现在需要帮助用户了解如何使用C#NModbus库进行通信读写。用户之前已经询问过NModbus的基本使用,现在可能想更具体地了解如何编写代码来实现读写操作。我需要回顾之前的回答,确保这次提供的信息更深入,但又不重复。 首先,用户的问题是关于使用C#NModbus进行读写,所以重点应该在代码示例和具体步骤上。之前的回答已经涵盖了环境准备、核心使用模式、API详解、异常处理等,但用户可能需要更详细的读写示例,特别是如何实现基本的读取和写入操作。 我需要考虑用户可能的背景,他们可能是刚开始接触NModbus的开发者,或者需要快速集成Modbus通信到他们的C#应用中。因此,步骤需要清晰,代码示例要简单易懂,并且包含必要的注释。 接下来,我应该分步骤介绍如何建立连接、读取数据、写入数据,以及异常处理和注意事项。可能需要区分不同的传输方式,比如TCP和RTU,但用户的问题没有特别指出,所以可以给出两种常见的例子。 另外,之前的回答中已经提到了TCP和串口通信的示例,但可能需要更详细的解释,比如参数的意义、功能码的选择,以及如何处理读取到的数据。例如,寄存器中的数据可能需要转换,如字节序的处理,这在之前的回答中提到过,但可能需要更详细的说明。 还需要注意异常处理的部分,确保用户知道如何处理通信中的常见错误,比如超时、从站异常等。此外,提供调试建议,比如使用工具验证通信,查看日志,这对用户实际开发中解决问题会有帮助。 用户可能需要了解如何初始化从站数据,这在之前的示例中提到了SlaveStorage,但可能需要更明确的步骤,说明如何设置寄存器和线圈的值,以便主站读取。 另外,高级配置如超时设置、日志集成也是重要的,但可能属于进阶内容。用户可能希望先看到基础读写操作,再考虑优化和配置,所以这部分可以放在后面,简要提及。 需要确保代码示例正确,符合C#语法,并且使用NModbus的最新版本。可能需要检查NuGet包的安装命令是否正确,命名空间是否引用正确。 最后,总结应用场景和注意事项,帮助用户理解何时使用这些代码,以及可能遇到的常见问题,比如设备厂商的特定实现差异,字节序的问题等。 综上所述,我需要结构清晰地分步骤,提供TCP和RTU两种示例,详细注释代码,强调异常处理和调试方法,并补充数据转换和实际应用的建议,确保用户能够顺利实现Modbus通信的读写功能。</think>以下是使用C#NModbus实现Modbus通信读写的详细指南,包含关键代码示例和最佳实践: --- ### 一、基础通信流程 $$ \text{通信流程} = \text{建立连接} \rightarrow \text{数据操作} \rightarrow \text{异常处理} \rightarrow \text{连接释放} $$ --- ### 二、TCP通信实现 #### 1. 主站连接配置 ```csharp using NModbus; using NModbus.Device; var factory = new ModbusFactory(); using var tcpClient = new TcpClient("192.168.1.10", 502); // 设备IP和端口 IModbusMaster master = factory.CreateMaster(tcpClient); ``` #### 2. 读取操作示例 ```csharp // 读取保持寄存器(功能码0x03) ushort[] registers = master.ReadHoldingRegisters( slaveAddress: 1, // 从站地址 startAddress: 40001, // 起始地址(Modbus地址需减1,实际对应40000) numberOfPoints: 10 // 读取数量 ); // 处理16位数据 int temperature = registers[0]; // 直接转换为整数 float voltage = registers[1] / 10.0f; // 带小数点处理 ``` #### 3. 写入操作示例 ```csharp // 写入单个线圈(功能码0x05) master.WriteSingleCoil( slaveAddress: 1, coilAddress: 00001, // 线圈地址(对应00000) value: true // 开关状态 ); // 批量写入寄存器(功能码0x10) ushort[] writeData = { 1234, 5678 }; master.WriteMultipleRegisters( slaveAddress: 1, startAddress: 40010, // 对应寄存器40011 data: writeData ); ``` --- ### 三、串口通信实现(RTU模式) ```csharp var serialPort = new SerialPort("COM3", 9600, Parity.None, 8, StopBits.One); serialPort.Open(); var adapter = new SerialPortAdapter(serialPort); IModbusMaster master = factory.CreateRtuMaster(adapter); // 读取输入寄存器(功能码0x04) ushort[] inputRegs = master.ReadInputRegisters(2, 30001, 5); ``` --- ### 四、数据转换技巧 #### 1. 32位浮点数处理 ```csharp // 大端序转换(常见于PLC) float value = BitConverter.ToSingle(new byte[] { (byte)(registers[0] >> 8), (byte)(registers[0] & 0xFF), (byte)(registers[1] >> 8), (byte)(registers[1] & 0xFF) }, 0); // 使用NModbus内置方法 float value = ModbusUtility.GetSingle( registers[0], // 高位寄存器 registers[1], // 低位寄存器 swapBytes: true // 是否需要字节交换 ); ``` #### 2. ASCII字符串解析 ```csharp // 从连续寄存器读取字符串(每个寄存器2字符) char[] chars = registers.SelectMany(r => new[] { (char)(r >> 8), (char)(r & 0xFF) }).ToArray(); string result = new string(chars).Trim('\0'); ``` --- ### 五、完整工作流程示例 ```csharp try { // 连接设备 using var client = new TcpClient("192.168.1.100", 502); IModbusMaster master = factory.CreateMaster(client); master.Transport.ReadTimeout = 1000; // 周期性读取 while(true) { ushort[] data = master.ReadHoldingRegisters(1, 40001, 2); Console.WriteLine($"温度: {data[0]}℃ 湿度: {data[1]}%"); Thread.Sleep(1000); } } catch (SlaveException ex) { Console.WriteLine($"Modbus错误: {ex.SlaveExceptionCode}"); } catch (IOException) { Console.WriteLine("连接中断"); } ``` --- ### 六、关键注意事项 1. **地址转换规则**: - Modbus协议地址 = 设备标注地址 - 1 - 示例:设备手册地址40001 → 实际使用40000 2. **通信参数匹配**: ```csharp // 必须与设备设置一致 new SerialPort("COM1", 19200, Parity.Even, 7, StopBits.Two); ``` 3. **性能优化**: - 单次读取寄存器数量 ≤ 125(RTU模式限制) - 使用异步方法避免阻塞:`ReadHoldingRegistersAsync` - 连接复用(TCP)减少握手开销 --- ### 七、调试工具推荐 1. **Modbus Poll**:验证主站通信 2. **Modbus Slave**:模拟从站设备 3. **Wireshark** + **Modbus协议过滤器**:分析原始报文 4. **NModbus日志**: ```csharp factory.UseLogger(LoggerFactory.Create(b => b.AddConsole()).CreateLogger<ModbusMaster>()); ``` --- ### 八、典型问题解决方案 1. **超时错误**: - 检查物理连接 - 确认从站地址正确 - 调整`ReadTimeout`参数 2. **数据错乱**: - 验证字节序设置 - 检查浮点数编码格式(IEEE754标准) - 确认寄存器映射表版本 3. **连接不稳定**: - 添加重试机制 ```csharp Retry.Do(() => master.ReadHoldingRegisters(...), TimeSpan.FromSeconds(1), retryCount: 3); ``` --- 通过以上实现,可完成以下工业场景开发: - PLC数据采集系统 - 智能电表数据监控 - 生产线设备控制 - 能源管理系统(EMS) - 楼宇自动化(BAS) 建议结合具体设备协议手册进行寄存器地址映射和数据类型转换。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值