通過MC協議讀寫三菱PLC寄存器


一. 背景

     在做大型工業設備數據采集 監控的時候常遇到 PLC控制器,常見的三菱 台達 歐姆龍等,本文以三菱q系列為例,通過go語言編寫MC協議客戶端程序 實現數據寄存器的讀寫

 

二 . MC協議介紹

   三菱官網有詳細文檔(參考q16 ,配備網絡模塊),此處不再贅述,文檔下載鏈接

上位機可以通過網絡調試助手測試通訊端口的配置狀況,二進制與ASCII碼兩種通訊方式協議內容有所不同,具體可以查詢三菱的通訊協議資料。我這邊主要使用的是如下這份,主要是其中的第3章 通過 QnA 兼容 3E/3C/4C 幀、4E 幀進行通信時 
下載地址:https://pan.baidu.com/s/1jQs8l2M7IZHvMKFqrgP0qw

 

三. PLC 配置

3.1 自帶網口的CPU

GX Works 軟件打開【參數】-->>【PLC參數】-->>【內置以太網端口設置】配置IP,協議格式等參數。

然后進【打開設置】,如下是按TCP協議開放了兩個供上位機MC協議的端口。

 

這樣PLC端的配置就結束了。

 

3.2 使用網絡模塊QJ71E71-100通訊(界面與上述的內置網口PLC有所不同)

GX Works 軟件打開【參數】-->>【網絡參數】-->>【以太網***】配置IP,協議格式等參數。下圖中的初始時間設置,允許RUN中寫入一定要勾對!!!

 

 

 然后進【打開設置】,如下是按TCP協議開放了一個供上位機MC協議的端口3210。

 

 

 

三 . 讀寫實例

讀寫方式有兩種,一種是用ASSIC 方式,另外一直是十六進制

1. 讀寫D7000 寄存器為例子

7000 的十六進制表示方式為 001B58,分配了三個字節,需要倒敘轉換581B00

如下指令為讀取D7000指令

   發送:50 00 00 FF FF 03 00 0C 00 10 00 01 04 00 00 58 1B 00 A8 01 00
   接收:D0 00 00 FF FF 03 00 04 00 00 00 0C 00

各個指令說明

副頭部 :5000 指令為5000,響應為D000
網絡編號:00
PLC編號:FF
IO編號:FF03
模塊站號:00
請求數據長度:0C00 請求數據長度計算為之后的所有數據
時鍾 :0100 表示等待PLC響應的timeout時間
高低位互換,實際為0001 即最大等待時間250ms*1=0.25秒
指令:0104 實際為0401,即為批量讀取 (后面單獨列出指令)
子指令:0000 值是0表示按字讀取(1個字=16位),如果值是1就按位讀取
首地址:58 1B 00  實際為001B58 十進制為7000
軟元件:表示讀取PLC寄存器的類型 A8 對應D點(后面有詳細對應)
長度:01
結束代碼:00
示例回復:
成功:D0 00 00 FF FF 03 00 04 00 00 00 0C 00(D7000寄存器數據為13)

副頭部:D000 網絡編號:00 PLC編號:FF
IO編號:FF03 模塊站號:00
應答數據長度:0400 實際為0004 即為4
異常代碼:0000 如果正常的話,就是0000
應答數據:0C00 實際為000C 即為13

 

2.寫入

如下指令為向D7000寫入H000C

   發送:50 00 00 FF FF 03 00 OE 00 10 00 01 14 00 00 58 1B 00 A8 01 00 0C 00
   接收:D0 00 00 FF FF 03 00 02 00 00 00

 

 

四. go 程序實現

可以用如下兩種方式實現

1. 采用modelbus tcp協議,引用go的net包

import (
    "encoding/csv"
    "fmt"
    "net"
    "os"
    "log"
    "time"
)
// 當前給D7000 寄存器寫值為 14
func writeTest(c net.Conn){
    buf := make([]byte, 64)
    // 寫入指令      50    00    00    FF    FF    03     00    0E   00     10    00     01  14    00    00    58     1B    00    A8    01    00   0E    00
    input := []byte{0x50, 0x00, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0x0E, 0x00, 0x10, 0x00, 0x01, 0x14, 0x00, 0x00, 0x58, 0x1B, 0x00, 0xA8, 0x01, 0x00, 0x0E, 0x00}
        //客戶端請求數據寫入 conn,並傳輸
    c.Write([]byte(input))
    //服務器端返回的數據寫入空buf
    cnt, err := c.Read(buf)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println("寫入成功",cnt)
}
 
func ClientSocket() net.Conn {
    conn, err := net.Dial("tcp", "192.168.18.44:5002")
    if err != nil {
        fmt.Println("客戶端建立連接失敗")
        log.Printf("客戶端建立連接失敗:%s", err)
        return nil
    }
    fmt.Println("客戶端建立成功")
    log.Printf("客戶端建立連接成功:%s", "11")
    return conn
}

 

2. 引用github.com/future-architect/go-mcprotocol/mcp包

代碼如下:

package main

import (
    // "encoding/hex"
    // "os"
    // "strconv"
    "strings"
    "github.com/future-architect/go-mcprotocol/mcp"
    "fmt"
    "time"
    "log"
    "encoding/hex"
)

func main() {
    //opts.Host, opts.Port
    //192.168.18.34   2000
    fmt.Println("start connect")
    log.Printf("tufei :%s", "start connect")
    // 43 139,44 5002
    client, err := mcp.New3EClient("192.168.18.44", 5002, mcp.NewLocalStation())
    fmt.Println("end connect")
    log.Printf("tufei :%s", "end connect")
    if err != nil {
        fmt.Println(err)
        //log.Printf("New3EClient err :%s", err)
    }
    fmt.Println("connect success!")

    fmt.Println("start read !")

    // 1 device
    resp1, err := client.Read("D", 7000, 1)
    if err != nil {
        log.Printf("unexpected mcp read err: %v", err)
    }

    if len(resp1) != 13 {
        log.Printf("expected %v but actual is %v", 13, len(resp1))
    }
    if hex.EncodeToString(resp1) != strings.ReplaceAll("d000 00 ff ff03 0004 0000 0000 00", " ", "") {
        log.Printf("expected %v but actual is %X", "d00000ffff0300040000000000", hex.EncodeToString(resp1))
    }

    fmt.Println(resp1)


    time.Sleep(100 * time.Second)
}

兩種方式都已驗證 確認ok

 

五.參考資料

https://www.cnblogs.com/haozhanggy/p/12213159.html

https://blog.csdn.net/cmwanysys/article/details/106681255

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM