[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