记录一下基本用法
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;
}
}
本文由 jxxxy 创作,采用 知识共享署名4.0 国际许可协议进行许可。
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。