前面已经讲解了有关ModbusTCPIP协议的有关内容,相关链接:https://blog.csdn.net/sgmcumt/article/details/87435191
下面我们具体用一个C#的例程来说明一下ModbusTCP报文的数据组成和传输方法。
使用的调试工具是Modbus Slave和Modbus Poll。工具的使用可参照:https://blog.csdn.net/byxdaz/article/details/77979114
1 客户端概述
1.1 设置Modbus slave
我们使用Modbus slave作为服务器,Modbus slave的IP地址根据自己的电脑IP来确定,端口号默认为502。
1.2 客户端预览
客户端的设计界面如下图所示:
为了保证程序的完整性,下面是整个程序的代码
using System;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;namespace ModbusDemo
{public partial class FrmChildren : Form{private Socket tcpSocket = null;/// <summary>/// 是否已与服务器建立连接/// </summary>public bool Connected { get { return tcpSocket.Connected; } }public FrmChildren(){InitializeComponent();}private void btnLink_Click(object sender, EventArgs e){IPAddress plcIPAddress = IPAddress.Parse(txtPLCIP.Text.ToString());int plcPort = int.Parse(txtPLCPort.Text.ToString());IPEndPoint pcIpport = new IPEndPoint(IPAddress.Parse(txtPCIP.Text.ToString()), int.Parse(txtPCPort.Text.ToString()));tcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//tcpSocket.ServerConnectedAsync += TcpSocket_ServerConnected;if (!tcpSocket.Connected)// 开始异步连接操作tcpSocket.BeginConnect(plcIPAddress, plcPort, HandleTcpServerConnected, pcIpport);}/// <summary>/// 异步连接执行后处理程序/// </summary>/// <param name="ar"></param>private void HandleTcpServerConnected(IAsyncResult ar){try{tcpSocket.EndConnect(ar);RaiseServerConnected();}catch (Exception ex){// 错误信息写入日志}}private void RaiseServerConnected(){//如果连接成功,textbox背景色为蓝色,否则为红色if (Connected){txtPCIP.Invoke(new Action(() =>{txtPCIP.BackColor = Color.Blue;txtPCPort.BackColor = Color.Blue;}));}else{txtPCIP.Invoke(new Action(() =>{txtPCIP.BackColor = Color.Red;txtPCPort.BackColor = Color.Red;}));}}private void btnSend_Click(object sender, EventArgs e){byte[] sendMessage = ConvertBytes(txtSendMessage.Text.ToString().Trim());tcpSocket.Send(sendMessage);byte[] reciveMessage = new byte[256];int receiveLength=Receive(ref reciveMessage, reciveMessage.Length);if (receiveLength != 0){txtReceiveMessage.Text = ConvertString(reciveMessage);}}/// <summary>/// 接收从PLC发回来的数据/// </summary>/// <param name="response">存放接收数据的数组</param>/// <param name="respLength">接收的长度</param>/// <returns></returns>private int Receive(ref Byte[] response, int respLength){int await = 3;if (!this.Connected){throw new Exception("Socket is not connected.");}//如果服务器(PLC)没有数据传送过来,等待300ms;while (this.tcpSocket.Available == 0 && await > 0){await--;Thread.Sleep(100);}if (this.tcpSocket.Available == 0) return 0; //等待300ms后仍没有数据,则退出int recvLength = this.tcpSocket.Receive(response, respLength, SocketFlags.None);return recvLength;}private byte[] ConvertBytes(string sourceStr){string[] tmpSrt = sourceStr.Trim().Split(' ');byte[] destinationByte = new byte[tmpSrt.Count()];for (int i = 0; i < tmpSrt.Count(); i++){destinationByte[i] = Convert.ToByte(Convert.ToInt32( tmpSrt[i],16));}return destinationByte;}private string ConvertString(byte[] sourceBytes){string byteStr = string.Empty;for (int i = 0; i <sourceBytes.Length; i++){byteStr += string.Format("{0:D2}", sourceBytes[i]) + " ";}return byteStr;}}
}
2 案例讲解
2.1 参数设置
Modbus slave连接成功后,选择setup–>slave definition,Function选择“03 Holding Register”,其它设置如图所示:
设置完成后,在主界面的MBslave1页面设置地址1到10的数值为1到10,格式为十六进制,如图所示:
2.2 读保持寄存器数据
我们的目的是请求读保持寄存器1-10的数据,根据上节1.1,1.2和3.3,可以很容易得出需要发送的byte[]数组:
01 01 00 00 00 06 FF 03 00 00 00 0A
接收的数据为:
01 01 00 00 00 23 255 03 20 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00 09 00 10
注:接收的数据是十进制,所以FF为255。
程序界面如下图所示:
2.3 写单个保持寄存器数据
我们的目的是请求将十六进制 00 0A 写入寄存器2,根据上节1.1,1.2和3.6,可以很容易得出需要发送的byte[]数组:
02 01 00 00 00 06 FF 06 00 01 00 0A
接收的数据为:
02 01 00 00 00 06 255 06 00 01 00 10
程序界面如下图所示:
Mbslave1数据更改后的界面如下图所示: