Rapid Development of Modbus Communication with Xinjie PLC Based on Nmodbus Library

System Learning

Life is like a marathon; sometimes you need to pause and enjoy the scenery to go further.

Last time, I worked on a modbus_tcp communication protocol. I wonder if everyone has seen it. This time, I will present the implementation of the Modbus protocol using C# WinForms based on the Nmodbus4 library, and how to communicate with Xinjie PLC.

First, let’s look at the results of this session.

Project Setup

Step 1Open VS2022, create a project, and select Windows Forms Application, or .NET Framework. Choose .NET 8.Rapid Development of Modbus Communication with Xinjie PLC Based on Nmodbus Library

Step 2

Right-click on the project name and select Package Management.

Rapid Development of Modbus Communication with Xinjie PLC Based on Nmodbus Library

Step 3

Add the Nmodbus4.netCore package.

Rapid Development of Modbus Communication with Xinjie PLC Based on Nmodbus Library

Step 4

Return to the program and import the namespace.

using Modbus.Device;using System.Net.Sockets;

Program Development

The interface consists of simple button controls and text boxes, nothing more.Rapid Development of Modbus Communication with Xinjie PLC Based on Nmodbus Library Inpublic partial class Form1 : Form, add global variables.

TcpClient client = new TcpClient(); // Create a new connectionIModbusMaster master = null; // Library nameprivate bool[] M = new bool[101]; // Store the status of the switch Mprivate ushort[] D = new ushort[101]; // Store D status

1. Establishing ConnectionHere, I only use button1 to connect. If the connection is valid, it will succeed; otherwise, a prompt box will pop up.

      private void button1_Click(object sender, EventArgs e)      {          try          {              client.Connect("192.168.6.6", 502);              if (client.Connected) // If connected successfully              {                  master = ModbusIpMaster.CreateIp(client); // Initialize class fields                  MessageBox.Show("Connection successful!"); // Show connected prompt                  button1.Text = "Connected";                  button1.BackColor = Color.Green;              }          }          catch (Exception ex)          {              MessageBox.Show($"Connection failed: {ex.Message}"); // Show error message          }      }

2. Data ReadingI used a timer for automatic reading, so I created two functions for the function codes.For reading function code 01, I store all the read function codes in the custom variable M array.

private void Read01(){    bool[] date = master.ReadCoils(1, 0, 10);  // Read 10 starting from 0    for (int i = 0; i < date.Length; i++)    {        M[i] = date[i];   // Populate M array    }}

For reading function code 03,I store all the read function codes in the custom variable D array.

private void Read03(){    ushort[] date = master.ReadHoldingRegisters(1, 0, 10);// Read 10 starting from 0    for (int i = 0; i < date.Length; i++)    {        D[i] = date[i];   // Populate D array    }}

Finally, I put it into a timer to read once per second.

private void timer1_Tick(object sender, EventArgs e){    try    {        if (client.Connected) // If connected successfully           {            Read01();  // Function code 01            Read03(); // Function code 03            b_Color();// Change color of switches            Date_show();// Display values        }    }    catch (Exception ex)    {        MessageBox.Show($"Connection failed: {ex.Message}"); // Show error message    }}

3. Data Application

You can see that when I click the button, it changes color. The method implemented here is by reading the M array and checking if it is true; if so, it turns green; if not, it remains default. Regarding case0–case3, these are the names of my buttons, and since there are multiple, I used an array for the loop instead of writing them one by one.

private void b_Color(){    // Switch controls    System.Windows.Forms.Button[] cases = new System.Windows.Forms.Button[] { case0, case1, case2, case3 };    for (int i = 0; i < cases.Length; i++)    {        if (i < M.Length && M[i])        {            cases[i].BackColor = Color.Green;        }        else        {            cases[i].BackColor = SystemColors.Control; // Restore default color        }    }}

Also, for data display, I used the same method for data application.

private void Date_show(){    System.Windows.Forms.TextBox[] Axis = new System.Windows.Forms.TextBox[] { Axis0, Axis1, Axis2, Axis3 };    for (int i = 0; i < Axis.Length; i++)    {        Axis[i].Text = D[i].ToString();    }}

Finally, adding these two functions to the timer will enable data display.

4. Data Writing

For writing switch values, I used the WriteSingleCoil method, which is provided by the library.

The main operation is to check if the current value is 1 when the communication button is pressed; if so, set it to 0, otherwise, it is an inversion operation.

 private void case1_Click(object sender, EventArgs e) {     ushort date = 1;     if (client.Connected) // If connected successfully       {         if (M[date]) master.WriteSingleCoil(1, date, false);         else master.WriteSingleCoil(1, date, true);     } }

For writing analog values, I used the value change event of the text box to enter data writing.

 private void setDate0_TextChanged(object sender, EventArgs e) {     string input = setDate0.Text; // Get input string from textBox                 if (ushort.TryParse(input, out ushort date)) // Try to convert string to ushort     {         // If connected successfully // Write to register         if (client.Connected) master.WriteSingleRegister(1, 0, date);     }     else MessageBox.Show("Please enter a valid number."); // Handle conversion failure, e.g., show error message }

5. Closing Connection

Here, I implemented a close operation when the window closes, but it can also be done with a button.

// Ensure resources are released when the form closesprivate void Form1_FormClosing(object sender, FormClosingEventArgs e){    if (client != null && client.Connected)    {        client.Close();    }}

Nmodbus Library Explanation

Method Name

Method Name

Function

Required Parameters

Return Value

Corresponding Function Code

ReadCoils

Read the status of DO

Slave Address (8 bits)

byte slaveAddress

Starting Address (16 bits)

ushort startAddress

Number of Points to Read (16 bits)

ushort numberOfPoints

bool[]

01

ReadInputs

Read the status of DI

Slave Address (8 bits)

byte slaveAddress

Starting Address (16 bits)

ushort startAddress

Number of Points to Read (16 bits)

ushort numberOfPoints

bool[]

02

ReadHoldingRegisters

Read the value of AO

Slave Address (8 bits)

byte slaveAddress

Starting Address (16 bits)

ushort startAddress

Number of Points to Read (16 bits)

ushort numberOfPoints

ushort[]

03

ReadInputRegisters

Read the value of AI

Slave Address (8 bits)

byte slaveAddress

Starting Address (16 bits)

ushort startAddress

Number of Points to Read (16 bits)

ushort numberOfPoints

ushort[]

04

WriteSingleCoil

Write value to DO

Slave Address (8 bits)

byte slaveAddress

Coil Address (16 bits)

ushort coilAddress

Value to Write (Boolean)

bool value

No return value

05

WriteSingleRegister

Write value to AO

Slave Address (8 bits)

byte slaveAddress

Register Address (16 bits)

ushort registerAddress

Value to Write (16-bit)

ushort value

No return value

06

WriteMultipleCoils

Write multiple coil registers

Slave Address (8 bits)

byte slaveAddress

Starting Address (16 bits)

ushort startAddress

Value to Write (Boolean array)

bool[] data

No return value

15

WriteMultipleRegisters

Write multiple holding registers

Slave Address (8 bits)

byte slaveAddress

Starting Address (16 bits)

ushort startAddress,

Register Values (16-bit integer array)

ushort[] data

No return value

16

ReadWriteMultipleRegisters

Read and write multiple holding registers

Slave Address (8 bits)

byte slaveAddress

Read Starting Address (16 bits)

ushort startReadAddress

Number of Points to Read (16 bits)

ushort numberOfPointsToRead,

Write Starting Address (16 bits)

ushort startWriteAddress,

Write Values (16-bit integer array)

ushort[] writeData

ushort[]

23

Conclusion

This is just a simple application. For real projects, threading is needed for connections. If interested, you can refer to the source code.Link: https://pan.quark.cn/s/f192bb0861c2Teaching someone to fish is better than giving them fish.

Rapid Development of Modbus Communication with Xinjie PLC Based on Nmodbus Library

Leave a Comment