NModbus库
in 默认分类 with 0 comment

记录一下基本用法
NModbus版本 最新稳定版 3.91.2024.1031
NModbus.Serial 最新稳定版 3.0.81
英文文档

做主站

Modbus TCP

// 连接本机的Modbus从站
using var client = new TcpClient("127.0.0.1", 502);
var factory = new ModbusFactory();
var master = factory.CreateMaster(client);

byte slaveId = 1; // 从站地址
ushort startAddress = 0; // 起始地址
ushort numInputs = 5; // 读取5个寄存器

ushort[] inputs = master.ReadHoldingRegisters(slaveId, startAddress, numInputs);

Console.WriteLine("读取的输入寄存器数据:");
for (int i = 0; i < inputs.Length; i++) {
  Console.WriteLine($"寄存器 {startAddress + i} = {inputs[i]}");
  Thread.Sleep(500);
}

Modbus RTU

using NModbus;
using NModbus.Serial;
using System.IO.Ports;
class Program
{
    static void Main(string[] args)
    {
        using (SerialPort port = new SerialPort("COM10")) {
            // 配置
            port.BaudRate = 9600;
            port.DataBits = 8;
            port.Parity = Parity.None;
            port.StopBits = StopBits.One;
            port.ReadTimeout = 1000;
            port.WriteTimeout = 1000;
            port.Open();

            var factory = new ModbusFactory();
            IModbusMaster master = factory.CreateRtuMaster(port);

            byte slaveId = 1;
            ushort startAddress = 0;
            ushort[] registers = new ushort[] { 1, 2, 3 };

            // 操作寄存器
            //master.WriteMultipleRegisters(slaveId, startAddress, registers);
            ushort[] reg = master.ReadHoldingRegisters(slaveId, startAddress, 3);
            Console.WriteLine(string.Join(" ", reg));
        }

    }
}

做从站

Modbus TCP

using NModbus;
using NModbus.Data;
using System;
using System.Net;
using System.Net.Sockets;
 
namespace ModbusSlaveTest
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //设置从站ID和端口
            byte slaveId = 1;
            int port = 502;
            //创建并配置监听TCP客户端的TcpListener
            var listener = new TcpListener(new IPAddress(new byte[] { 127, 0, 0, 1 }), port);
            listener.Start();
            //创建从站并开启监听
            var factory = new ModbusFactory();
            var modbusTcpSlaveNetwork = factory.CreateSlaveNetwork(listener);
            // 也可以绑定事件
            SlaveDataStore slaveDataStore = new SlaveDataStore();
            slaveDataStore.HoldingRegisters.BeforeWrite += (sender, arg) => {
                Console.WriteLine($"StartAddress:{arg.StartAddress}  data:{string.Join(" ", arg.Points)}");
            };
            var slave = factory.CreateSlave(slaveId, slaveDataStore);
            modbusTcpSlaveNetwork.AddSlave(slave);
            modbusTcpSlaveNetwork.ListenAsync();
            //输入寄存器
            slave.DataStore.InputRegisters.WritePoints(0, new ushort[] { 1, 2, 3 });
            //保持寄存器
            slave.DataStore.HoldingRegisters.WritePoints(0, new ushort[] { 3, 2, 1 });
            //线圈
            slave.DataStore.CoilDiscretes.WritePoints(0, new bool[] { true, false, true });
            //离散输入
            slave.DataStore.CoilInputs.WritePoints(0, new bool[] { true, true, true });

            Console.ReadKey();
        }

    }
}

Modbus RTU

using (SerialPort slavePort = new SerialPort(PrimarySerialPortName))
    {
        // configure serial port
        slavePort.BaudRate = 9600;
        slavePort.DataBits = 8;
        slavePort.Parity = Parity.None;
        slavePort.StopBits = StopBits.One;
        slavePort.Open();

        var factory = new ModbusFactory();
        var slaveNetwork = factory.CreateRtuSlaveNetwork(slavePort);

        IModbusSlave slave1 = factory.CreateSlave(1);
        IModbusSlave slave2 = factory.CreateSlave(2);

        slaveNetwork.AddSlave(slave1);
        slaveNetwork.AddSlave(slave2);

        slaveNetwork.ListenAsync().GetAwaiter().GetResult();
    }
}

Modbus 数据解析类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ModbusTest;
/// <summary>
/// 因为电脑的字节序是小端,而 Modbus 协议规定的是大端,所以需要进行字节顺序转换
/// </summary>
public static class ModbusDataParser
{
    // 解析 uint16
    public static ushort UInt16(ushort[] data, int index)
    {
        return data[index];
    }

    // 解析 int16
    public static short Int16(ushort[] data, int index)
    {
        return (short)data[index];
    }

    // 解析 uint32
    public static uint UInt32(ushort[] data, int index, string order = "ABCD")
    {
        byte[] bytes = GetBytesFromUshorts(data, index, 2);
        return BitConverter.ToUInt32(RearrangeBytes(bytes, order), 0);
    }

    // 解析 int32
    public static int Int32(ushort[] data, int index, string order = "ABCD")
    {
        byte[] bytes = GetBytesFromUshorts(data, index, 2);
        return BitConverter.ToInt32(RearrangeBytes(bytes, order), 0);
    }

    // 解析 uint64
    public static ulong UInt64(ushort[] data, int index, string order = "ABCD")
    {
        byte[] bytes = GetBytesFromUshorts(data, index, 4);
        return BitConverter.ToUInt64(RearrangeBytes(bytes, order), 0);
    }

    // 解析 int64
    public static long Int64(ushort[] data, int index, string order = "ABCD")
    {
        byte[] bytes = GetBytesFromUshorts(data, index, 4);
        return BitConverter.ToInt64(RearrangeBytes(bytes, order), 0);
    }

    // 解析 float32(单精度浮点数)
    public static float Float32(ushort[] data, int index, string order = "ABCD")
    {
        byte[] bytes = GetBytesFromUshorts(data, index, 2);
        return BitConverter.ToSingle(RearrangeBytes(bytes, order), 0);
    }

    // 解析 float64(双精度浮点数)
    public static double Float64(ushort[] data, int index, string order = "ABCD")
    {
        byte[] bytes = GetBytesFromUshorts(data, index, 4);
        return BitConverter.ToDouble(RearrangeBytes(bytes, order), 0);
    }

    // **逆向方法**:转换数值为 ushort[]
    public static ushort[] UInt16ToUshorts(ushort value)
    {
        return new ushort[] { value };
    }

    public static ushort[] Int16ToUshorts(short value)
    {
        return new ushort[] { (ushort)value };
    }

    public static ushort[] UInt32ToUshorts(uint value, string order = "ABCD")
    {
        byte[] bytes = BitConverter.GetBytes(value);
        return GetUshortsFromBytes(RearrangeBytes(bytes, order));
    }

    public static ushort[] Int32ToUshorts(int value, string order = "ABCD")
    {
        byte[] bytes = BitConverter.GetBytes(value);
        return GetUshortsFromBytes(RearrangeBytes(bytes, order));
    }

    public static ushort[] UInt64ToUshorts(ulong value, string order = "ABCD")
    {
        byte[] bytes = BitConverter.GetBytes(value);
        return GetUshortsFromBytes(RearrangeBytes(bytes, order));
    }

    public static ushort[] Int64ToUshorts(long value, string order = "ABCD")
    {
        byte[] bytes = BitConverter.GetBytes(value);
        return GetUshortsFromBytes(RearrangeBytes(bytes, order));
    }

    public static ushort[] Float32ToUshorts(float value, string order = "ABCD")
    {
        byte[] bytes = BitConverter.GetBytes(value);
        return GetUshortsFromBytes(RearrangeBytes(bytes, order));
    }

    public static ushort[] Float64ToUshorts(double value, string order = "ABCD")
    {
        byte[] bytes = BitConverter.GetBytes(value);
        return GetUshortsFromBytes(RearrangeBytes(bytes, order));
    }

    // 辅助方法:获取字节数组
    private static byte[] GetBytesFromUshorts(ushort[] data, int index, int count)
    {
        ushort[] tempData = new ushort[data.Length];
        Array.Copy(data, tempData, data.Length);
        Array.Reverse(tempData); // 修正字节顺序
        byte[] bytes = new byte[count * 2];
        for (int i = 0; i < count; i++) {
            byte[] temp = BitConverter.GetBytes(tempData[index + i]);
            Buffer.BlockCopy(temp, 0, bytes, i * 2, 2);
        }
        return bytes;
    }

    // 辅助方法:调整字节序
    private static byte[] RearrangeBytes(byte[] bytes, string order)
    {
        byte[] result = new byte[bytes.Length];

        switch (order.ToUpper()) {
            case "ABCD": // 大端
                return bytes; // ABCD 本身就是大端,直接返回
            case "DCBA": // 小端
                Array.Reverse(bytes); // 反转字节数组,得到小端序
                return bytes;
            case "BADC": // 2字节颠倒
                SwapBytes(bytes, 0, 1); // 交换字节顺序
                return bytes;
            case "CDAB": // 另外的字节顺序(视具体需求而定)
                SwapBytes(bytes, 1, 2); // 根据需要交换字节顺序
                return bytes;
            default:
                throw new ArgumentException("Unsupported byte order");
        }
    }

    // 辅助方法:交换字节
    private static void SwapBytes(byte[] bytes, int i, int j)
    {
        byte temp = bytes[i];
        bytes[i] = bytes[j];
        bytes[j] = temp;
    }

    // 辅助方法:从字节数组获取 ushort[]
    private static ushort[] GetUshortsFromBytes(byte[] bytes)
    {
        ushort[] result = new ushort[bytes.Length / 2];
        for (int i = 0; i < result.Length; i++) {
            result[i] = BitConverter.ToUInt16(bytes, i * 2);
        }
        Array.Reverse(result); // 修正字节顺序
        return result;
    }
}
回复