Installing NModbus Library
Search for NModbus in NuGet Package Manager and install the package.
Creating a ModbusTCP Wrapper Class
public class ModbusTcpClient
{
private ModbusFactory factory;
private IModbusMaster modbusMaster;
private TcpClient tcpConnection;
public string HostAddress { get; set; }
public int ConnectionPort { get; set; }
public bool IsConnected
{
get => tcpConnection.Connected;
}
public ModbusTcpClient(string ipAddress, int portNumber)
{
HostAddress = ipAddress;
ConnectionPort = portNumber;
factory = new ModbusFactory();
tcpConnection = new TcpClient(HostAddress, ConnectionPort);
modbusMaster = factory.CreateMaster(tcpConnection);
modbusMaster.Transport.ReadTimeout = 2000;
modbusMaster.Transport.Retries = 10;
}
public bool[] ReadDiscreteOutputs(byte deviceAddress, ushort startingAddress, ushort quantity)
{
return modbusMaster.ReadCoils(deviceAddress, startingAddress, quantity);
}
public bool[] ReadDiscreteInputs(byte deviceAddress, ushort startingAddress, ushort quantity)
{
return modbusMaster.ReadInputs(deviceAddress, startingAddress, quantity);
}
public ushort[] ReadOutputRegisters(byte deviceAddress, ushort startingAddress, ushort quantity)
{
return modbusMaster.ReadHoldingRegisters(deviceAddress, startingAddress, quantity);
}
public ushort[] ReadInputRegisters(byte deviceAddress, ushort startingAddress, ushort quantity)
{
return modbusMaster.ReadInputRegisters(deviceAddress, startingAddress, quantity);
}
public void WriteSingleCoil(byte deviceAddress, ushort address, bool data)
{
modbusMaster.WriteSingleCoil(deviceAddress, address, data);
}
public void WriteSingleRegister(byte deviceAddress, ushort address, ushort data)
{
modbusMaster.WriteSingleRegister(deviceAddress, address, data);
}
public void WriteMultipleCoils(byte deviceAddress, ushort startingAddress, bool[] values)
{
modbusMaster.WriteMultipleCoils(deviceAddress, startingAddress, values);
}
public void WriteMultipleRegisters(byte deviceAddress, ushort startingAddress, ushort[] values)
{
modbusMaster.WriteMultipleRegisters(deviceAddress, startingAddress, values);
}
}
Connection Testing Setup
Download Modbus Slave simulator software Link: https://pan.baidu.com/s/1ydX_9KElkOTA7h-E3SIadwPassword: 7fh5 Configure the settings accordingly.
Establishing Connection
private ModbusTcpClient modbusClient;
private void EstablishConnection()
{
try
{
modbusClient = new ModbusTcpClient(ServerIP, ServerPort);
pollTimer.Start();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Data Writing Implementation
Since NModbus only provides ushort type register operations, conversion utilities are needed. Create conversion helper methods:
public class ModbusDataConverter
{
public static void AssignString(ushort[] buffer, int offset, string text)
{
byte[] byteData = Encoding.UTF8.GetBytes(text);
ushort[] convertedData = ConvertBytesToUshorts(byteData);
Array.Copy(convertedData, 0, buffer, offset, convertedData.Length);
}
public static string ExtractString(ushort[] buffer, int offset, int length)
{
ushort[] tempBuffer = new ushort[length];
Array.Copy(buffer, offset, tempBuffer, 0, length);
byte[] byteData = ConvertUshortsToBytes(tempBuffer);
string result = Encoding.UTF8.GetString(byteData).Trim('\0');
return result;
}
public static void AssignFloat(ushort[] buffer, int offset, float value)
{
byte[] floatBytes = BitConverter.GetBytes(value);
ushort[] convertedValues = ConvertBytesToUshorts(floatBytes);
Array.Copy(convertedValues, 0, buffer, offset, convertedValues.Length);
}
public static float ExtractFloat(ushort[] buffer, int offset)
{
ushort[] tempBuffer = new ushort[2];
Array.Copy(buffer, offset, tempBuffer, 0, 2);
byte[] byteData = ConvertUshortsToBytes(tempBuffer);
float result = BitConverter.ToSingle(byteData, 0);
return result;
}
public static void AssignInt16(ushort[] buffer, int offset, short value)
{
byte[] shortBytes = BitConverter.GetBytes(value);
ushort[] convertedValues = ConvertBytesToUshorts(shortBytes);
Array.Copy(convertedValues, 0, buffer, offset, convertedValues.Length);
}
public static short ExtractInt16(ushort[] buffer, int offset)
{
ushort[] tempBuffer = new ushort[1];
tempBuffer[0] = buffer[offset];
byte[] byteData = ConvertUshortsToBytes(tempBuffer);
short result = BitConverter.ToInt16(byteData, 0);
return result;
}
public static bool[] ExtractBooleans(ushort[] buffer, int offset, int count)
{
ushort[] tempBuffer = new ushort[count];
Array.Copy(buffer, offset, tempBuffer, 0, count);
byte[] byteData = ConvertUshortsToBytes(tempBuffer);
bool[] result = ConvertBytesToBooleans(byteData);
return result;
}
private static bool[] ConvertBytesToBooleans(byte[] input)
{
bool[] output = new bool[input.Length * 8];
for (int i = 0; i < input.Length; i++)
{
for (int j = 0; j < 8; j++)
{
output[i * 8 + j] = (input[i] & (1 << j)) != 0;
}
}
return output;
}
private static byte ConvertBooleansToByte(bool[] input)
{
if (input != null && input.Length > 0)
{
byte result = 0;
for (int i = 0; i < Math.Min(input.Length, 8); i++)
{
if (input[i])
{
result |= (byte)(1 << i);
}
}
return result;
}
return 0;
}
private static ushort[] ConvertBytesToUshorts(byte[] input, bool swapOrder = false)
{
int byteCount = input.Length;
int ushortCount = (byteCount + 1) / 2;
ushort[] output = new ushort[ushortCount];
for (int i = 0; i < ushortCount; i++)
{
int lowIndex = i * 2;
int highIndex = Math.Min(lowIndex + 1, byteCount - 1);
if (swapOrder)
{
output[i] = (ushort)((input[lowIndex] << 8) | input[highIndex]);
}
else
{
output[i] = (ushort)(input[lowIndex] | (input[highIndex] << 8));
}
}
return output;
}
private static byte[] ConvertUshortsToBytes(ushort[] input, bool swapOrder = false)
{
byte[] output = new byte[input.Length * 2];
for (int i = 0; i < input.Length; i++)
{
if (swapOrder)
{
output[i * 2] = (byte)(input[i] >> 8);
output[i * 2 + 1] = (byte)(input[i] & 0xFF);
}
else
{
output[i * 2] = (byte)(input[i] & 0xFF);
output[i * 2 + 1] = (byte)(input[i] >> 8);
}
}
return output;
}
}
Writing data based on selected data type:
private void PerformWriteOperation()
{
try
{
if (DataType == "float")
{
ushort[] writeBuffer = new ushort[2];
float numericValue = float.Parse(InputValue);
ModbusDataConverter.AssignFloat(writeBuffer, 0, numericValue);
modbusClient.WriteMultipleRegisters(DeviceId, WriteRegisterAddress, writeBuffer);
}
else if (DataType == "text")
{
ushort[] writeBuffer = new ushort[10];
ModbusDataConverter.AssignString(writeBuffer, 0, InputValue);
modbusClient.WriteMultipleRegisters(DeviceId, WriteRegisterAddress, writeBuffer);
}
else if (DataType == "int16")
{
ushort[] writeBuffer = new ushort[1];
short numericValue = short.Parse(InputValue);
ModbusDataConverter.AssignInt16(writeBuffer, 0, numericValue);
modbusClient.WriteMultipleRegisters(DeviceId, WriteRegisterAddress, writeBuffer);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
As observed in the Modbus Slave simulator, floating point data occupies two registers (4 bytes). After proper configuration, successful writing can be verified.
Data Reading Implementation
private void PerformReadOperation()
{
try
{
if (DataType == "float")
{
ushort[] readBuffer = modbusClient.ReadOutputRegisters(DeviceId, ReadRegisterAddress, 2);
float extractedValue = ModbusDataConverter.ExtractFloat(readBuffer, 0);
DisplayValue = extractedValue.ToString();
}
else if (DataType == "text")
{
ushort[] readBuffer = modbusClient.ReadOutputRegisters(DeviceId, ReadRegisterAddress, 10);
DisplayValue = ModbusDataConverter.ExtractString(readBuffer, 0, 10);
}
else if (DataType == "int16")
{
ushort[] readBuffer = modbusClient.ReadOutputRegisters(DeviceId, ReadRegisterAddress, 1);
short extractedValue = ModbusDataConverter.ExtractInt16(readBuffer, 0);
DisplayValue = extractedValue.ToString();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
String Operations
Refer to the DataType=="text" implementation sections for string handling operations.