[Modbus] 如何 用 C# 開發 Modbus Master Protocol - (02) 瞭解通訊協定

[Modbus] 如何 用 C# 開發 Modbus Master Protocol - (02) 瞭解通訊協定

一般資料框架:

比如RTU/ASCII,硬體通訊介面是RS232/RS485 etc..

ADU:就是完整通訊協定。

Additional address:通常是指設備ID

PDU:通訊協定層,依不同的功能則會有不同的變化。

Error Check :Additional address + PDU 的資料,透過 CRC.LRC 得到的資料

image

 

下圖表示RTU

image

 

下圖表示ASCII

image

 


TCP/IP 資料框架:

硬體通訊介面是RJ45 etc..

ADU:就是完整通訊協定。

MBAP Header:TCP/IP 通訊協定的標頭

PDU:通訊協定層,依不同的功能則會有不同的變化。

image

 

MBAP Header 的定義

image


採用Big-Endian:

請參考:

[C#.NET] 處理通訊協定的事前準備

http://www.prudentman.idv.tw/2007/11/big-endianlittle-endian.html

image


資料模組定義:

帶有 Input 關鍵字的表示唯讀

image


Exception Code 的定義

當Response回傳0x80以上,表示發生例外

image


ADU定義:

以Function 0x01 (Read Coils)為例

image

 

我將所有完整的通訊協定用 Excel 整理起來,下圖以 Modbus TCP為例,你也可以整理出這樣的東西讓自己更清楚。

image

 

工作流程:

若我們需要開發Slave(也就是Server)的程式這工作流程很重要。

image


依慣例,一定要來一點程式碼



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

Image result for microsoft+mvp+logo