通过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