Modbus over TCP is nothing but the Modbus protocol over a TCP/IP interface running on Ethernet. Therefore all device addressing and routing is done through the standard IP addresses while the application protocol remains the same.
In case of Modbus protocol, only one device in the network usually acts as a master and others act as slaves. That mean, only the master device can send out commands to the slave devices. Sending out commands means reading from or writing to the registers or a sort of variable reference in the slave device. Also the slave device can read its own variable references and can act accordingly. Thus the data exchange takes place.
In this post we will learn how to use a .Net library to communicate b/w two Ethernet devices that could be PCs or PLCs. We will be developing both a Modbus master as will as a Modbus client and will try to enable communication b/w the two.
However, before starting up our development, lets just have a look at the protocol itself. In case of Modbus protocol, devices have registers that can be called variable references and they are of the following 4 types:
Discrete Outputs or Coils: These are read/write registers/references that hold only a single bit each.
Discrete Inputs: These are read only registers/references and these too hold only a single bit each. Since they are read only, master devices can read data from these references but can’t change them.
Input Registers (Input Data): These are similar to Discrete Input in that these are read only but can hold up to 16 bits each.
Holding Registers (Output Data): These are similar to Discrete output in that these are read/write but can hold up to 16 bits each.
Now that we understand the protocol, let’s fire up our Visual Studio. We will we using the NModbus4 nuget package. There are several other libraries as well that can be used. However the basic idea remains the same.

Below is the sample code for the master device and the slave device
Master
class Program { static void Main(string[] args) { Console.WriteLine("Master device running!"); string ipAddr = "127.0.0.1"; // IP Address of the device to connect to int tcpPort = 502; // Port to connect to // Create a tcp client and connect to the device TcpClient tcpClient = new TcpClient(); tcpClient.BeginConnect(ipAddr, tcpPort, null, null); // Create modbus master device on the tcp client ModbusIpMaster master = ModbusIpMaster.CreateIp(tcpClient); ushort startRef, noOfRefs; // Discrete Outputs or Coils: Read/write single bit references // Read startRef = 0; // Discrete output to start reading from noOfRefs = 5; // Number of registers to read bool[] dOutputs = master.ReadCoils(startRef, noOfRefs); // dOutputs contain all the bits stored at the address // Write master.WriteMultipleCoils(startRef, new bool[] { true, false, true, false, true}); // Discrete Inputs: Read only single bit references // Read bool[] dInputs = master.ReadInputs(startRef, 5); string inputStr = String.Join(" | ", dInputs); Console.WriteLine("Discrete Input -- " + inputStr); // Holding Registers (Output Data): Read/write only single 16 bit references // Read ushort[] outputRegisterData = master.ReadHoldingRegisters(startRef, noOfRefs); // Write master.WriteMultipleRegisters(startRef, new ushort[] {5, 10, 15, 12, 8}); // Input Registers (Input Data): Read only single 16 bit references // Read ushort[] inputRegisterData = master.ReadInputRegisters(startRef, noOfRefs); string registerStr = String.Join(" | ", inputRegisterData); Console.WriteLine("Input Registers -- " + registerStr); Console.WriteLine("Press enter to exit!"); Console.ReadLine(); } }
Slave
class Program { static void Main(string[] args) { Console.WriteLine("Slave device running!"); string ipAddr = "127.0.0.1"; // IP Address of the slave int tcpPort = 502; // Communication port // Create a TCP listener for the ip and port TcpListener listener = new TcpListener(System.Net.IPAddress.Parse(ipAddr), tcpPort); // Create a slave device for the TCP listener ModbusTcpSlave slave = ModbusTcpSlave.CreateTcp(1, listener); // Assign an event handler for the slave request received event // Will be fired whenever a new read/write request is made to the slave device slave.ModbusSlaveRequestReceived += Slave_ModbusSlaveRequestReceived; // Start listening slave.Listen(); // setting discrete inputs (read only for master) slave.DataStore.InputDiscretes[1] = true; slave.DataStore.InputDiscretes[2] = true; // setting input registers (read only for master) slave.DataStore.InputRegisters[1] = 12; slave.DataStore.InputRegisters[3] = 7; Console.WriteLine("Press enter to exit!"); Console.ReadLine(); } private static void Slave_ModbusSlaveRequestReceived(object sender, ModbusSlaveRequestEventArgs e) { ModbusTcpSlave slave = sender as ModbusTcpSlave; Console.WriteLine("Request received!! " + e.Message.FunctionCode); // Discrete Outputs IEnumerable<bool> dOutput = slave.DataStore.CoilDiscretes.Skip(1).Take(5); string outputStr = String.Join(" | ", dOutput); Console.WriteLine("Discrete output -- " + outputStr); // Holding Registers IEnumerable<ushort> holdingRegisters = slave.DataStore.HoldingRegisters.Skip(1).Take(5); string registerStr = String.Join(" | ", holdingRegisters); Console.WriteLine("Holding registers -- " + registerStr); Console.WriteLine("\n"); } }
In order to test the above samples, run the slave device program first. And then run the master device program. As soon the master runs and sends commands to the slave device, the request event handler on the slave runs and prints the device registers/references. The following is the expected output.
Slave:

Master:

For the above sample to work, make sure that the communication port is open.
0 Comments