[Modbus] 如何 用 C# 開發 Modbus Master Protocol - (02) 瞭解通訊協定
一般資料框架:
比如RTU/ASCII,硬體通訊介面是RS232/RS485 etc..
ADU:就是完整通訊協定。
Additional address:通常是指設備ID
PDU:通訊協定層,依不同的功能則會有不同的變化。
Error Check :Additional address + PDU 的資料,透過 CRC.LRC 得到的資料
下圖表示RTU
下圖表示ASCII
TCP/IP 資料框架:
硬體通訊介面是RJ45 etc..
ADU:就是完整通訊協定。
MBAP Header:TCP/IP 通訊協定的標頭
PDU:通訊協定層,依不同的功能則會有不同的變化。
MBAP Header 的定義
採用Big-Endian:
請參考:
http://www.prudentman.idv.tw/2007/11/big-endianlittle-endian.html
資料模組定義:
帶有 Input 關鍵字的表示唯讀
Exception Code 的定義
當Response回傳0x80以上,表示發生例外
ADU定義:
以Function 0x01 (Read Coils)為例
我將所有完整的通訊協定用 Excel 整理起來,下圖以 Modbus TCP為例,你也可以整理出這樣的東西讓自己更清楚。
工作流程:
若我們需要開發Slave(也就是Server)的程式這工作流程很重要。
依慣例,一定要來一點程式碼
private void button1_Click(object sender, EventArgs e)
{
var ipAddress = IPAddress.Parse("127.0.0.1");
var iPEndPoint = new IPEndPoint(ipAddress, 502);
//Create a TCP/IP socket.
if (this._socketCliect == null)
{
this._socketCliect = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
EndPoint endPoint = (EndPoint)iPEndPoint;
this._socketCliect.Connect(endPoint);
if (this._socketCliect.Connected)
{
var request = CreateReadHeader(1, 1, 0, 10);
this._socketCliect.Send(request);
var result = Receive();
}
}
private byte[] CreateReadHeader(ushort id, byte function, ushort startAddress, ushort length)
{
byte[] data = new byte[12];
data[0] = 0; // Transaction Hi
data[1] = 1; // Transaction Lo
data[2] = 0; // Modbus Protocol Hi
data[3] = 0; // Modbus Protocol Lo
data[4] = 0; // Length Hi
data[5] = 6; // Length Lo
byte[] _id = BitConverter.GetBytes((short)id);
data[6] = 255; // Slave ID
data[7] = function; // Function code
byte[] _adr = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)startAddress));
data[8] = _adr[0]; // Start address Hi
data[9] = _adr[1]; // Start address Lo
byte[] _length = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)length));
data[10] = _length[0]; // Number of data to read Hi
data[11] = _length[1]; // Number of data to read Lo
return data;
}
這裡有一點點的偷懶,應該是要轉成Hex字串才對,但我偷懶轉成ASCII
{
if (!this._socketCliect.Connected)
{
return null;
}
var tempTimeOut = this._socketCliect.ReceiveTimeout;
this._socketCliect.ReceiveTimeout = 1000;
var sb = new StringBuilder();
var buffer = new byte[1024];
while (true)
{
var socketError = new SocketError();
var receiveCount = this._socketCliect.Receive(buffer, 0, buffer.Length, SocketFlags.None, out socketError);
if (receiveCount == 0 || socketError != SocketError.Success)
{
break;
}
else
{
var receive = Encoding.ASCII.GetString(buffer, 0, receiveCount);
sb.Append(receive);
if (this._socketCliect.Available == 0)
{
break;
}
}
}
this._socketCliect.ReceiveTimeout = tempTimeOut;
var result = sb.ToString();
Console.WriteLine(result);
return result;
}
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET