golang + modbus tcp

基本原理

Modbus是PLC常用的通讯协议,经常用于与HMI通信。通过对此协议的分析,可以如同三菱MC协议一样,利用来与PC结合,发挥更大的作用。

Modbus 是一个应用层的通讯协议,位于 OSI 的第七层,在总线或者网络上的不同设备之间的,通过 客户端/服务端 的方式通讯,默认使用502端口进行通讯。

通讯示例:

// Remember: Big-endian values!

var responseADU = []byte{

	// MBAP HEADER:
	0x00, 0xFF, // Tx ID #255, typically an incremental value
	0x00, 0x00, // Protocol ID, always 0
	0x00, 0x0B, // Length of the following data (8+3=11)
	0x00, // Unit identifier, 0 unless a Gateway is used

	// PROTOCOL DATA UNIT:
	0x03,       // Function Code 03: Read Holding Registers
	0x08,       // Byte count of the following data (4*2=8)
	0x00, 0x0A, // First register; value 10
	0x0A, 0x00, // Second register; value 2560
	0xFF, 0xFF, // Third register; value 65535 or -1 when signed.
	0x00, 0x01, // Fourth register; value 1
}

功能码说明

代码 中文名称 位操作/字操作 操作数量
01h 读线圈状态 位操作 单个或多个
02h 读离散输入状态(只能读到0或1) 位操作 单个或多个
03h 读保持寄存器(保持寄存器可以通过06h功能写入) 字操作 单个或多个
04h 读输入寄存器(输入寄存器只能读取,不能通过06h功能写入) 字操作 单个或多个
05h 写单个线圈(线圈表示用来控制输出IO控制) 位操作 单个
06h 写单个保持寄存器 字操作 单个
0Fh 写多个线圈 位操作 多个
10h 写多个保持寄存器 字操作 多个

调试工具

Modbus Slave version 6.0.2

官方工具,不太好用,检测地址长度不能超过125,无法正确显示高低位转换的数据
链接: https://pan.baidu.com/s/19tEx6KzM0bjbCv342cA3zg
提取码: z44q

Modbus/TCP Master

优点:可以同时显示多种类型的数据比较方便
缺点:检测地址长度不能超过125;无法正确显示高低位转换的数据;无法自动刷新

ModScan 32 version 8.A00-10

应该是最好用的工具了,长度没有限制,可以自动刷新,可以自定义数据的类型,并且可以处理高低位转换的场景
选择 connection -> connect

录入 modbus tcp server 的地址和端口号

setup -> Display Options -> floating point -> 选择高位优先,还是低位优先

ngrok

在车间连接外网的机器上,把502端口映射到外网,方便远程调试。
官网 (https://dashboard.ngrok.com/get-started/setup) 注册账号,下载客户端

# 进入 rgrok 命令行工具所在目录
# 配置token
ngrok config add-authtoken {ngrok token} 
# 映射modbus tcp server 到外网
ngrok tcp {modbus tcp server ip}:502

高低位的问题

在plc中,有的是低位优先,也有的是高位优先,比如:
如果一个Int类型数组,占用4个字节,4个字节顺序为ABCD,那么采用big-endian大端字节顺序,那么在内存中即为ABCD,如果采用small-endian小端字节顺序,那么在内存中存储即为DCBA,但是在实际应用中,还有可能出现BADC或者CDAB的情况,因此我们在大小端的基础上做了一下扩展,定义了4种不同字节顺序,采用枚举类型表示,代码如下所示:

            ABCD = 0, // 大端形式
            BADC = 1, // 单字反转
            CDAB = 2, // 双字反转
            DCBA = 3, // 小端形式

示例程序 golang

import (
    "bytes"
    "encoding/binary"
    "fmt"
    "testing"
    "time"

    "github.com/simonvetter/modbus"
)

func Test_Modbus(t *testing.T) {
    var client *modbus.ModbusClient
    var err error

    // for a TCP endpoint
    // (see examples/tls_client.go for TLS usage and options)
    client, err = modbus.NewClient(&modbus.ClientConfiguration{
        URL:     "tcp://ip:port", // 可以直接使用ngrok映射到外网的地址
        Timeout: 1 * time.Second,
    })
    // note: use udp:// for modbus TCP over UDP
    if err != nil {
        fmt.Println(err.Error())
    }

    err = client.Open()
    if err != nil {
        fmt.Println(err.Error())
    }

    // uint
    fmt.Println("uint read and write")
    for i := 0; i <= 7; i++ {
        address := uint16(i)
        value := uint16(1)
        v, err := client.ReadRegister(address, modbus.HOLDING_REGISTER)
        if err != nil {
            fmt.Println(err.Error())
        }
        fmt.Println("address:", address, ",old value:", v)
        err = client.WriteRegister(address, value)
        if err != nil {
            fmt.Println(err.Error())
        }
        v, err = client.ReadRegister(address, modbus.HOLDING_REGISTER)
        if err != nil {
            fmt.Println(err.Error())
        }
        fmt.Println("address:", address, ",new value:", v)
    }

    // float
    fmt.Println("float read and write")
    bytes, err := client.ReadBytes(6, 4, modbus.HOLDING_REGISTER)
    if err != nil {
        fmt.Println(err.Error())
    }
    f, _ := bytesToFloat32(bytes)
    fmt.Println("old value:", f)
    err = client.WriteBytes(6, float32ToBytes(123.456))
    if err != nil {
        fmt.Println(err.Error())
    }
    bytes, err = client.ReadBytes(6, 4, modbus.HOLDING_REGISTER)
    if err != nil {
        fmt.Println(err.Error())
    }
    f, _ = bytesToFloat32(bytes)
    fmt.Println("new value:", f)

}

参考资料

modbus tcp: https://www.ad.siemens.com.cn/productportal/Prods/published/Comm/Comm_9.1/Comm_9.1.html
golang modbus: https://github.com/goburrow/modbus
modbus server:https://blog.csdn.net/weixin_42330983/article/details/124860023
Modscan32 https://www.sohu.com/a/443539826_651846
https://github.com/ffffffff0x/1earn/blob/master/1earn/Security/ICS/实验/Modbus仿真环境搭建.md
读写:https://github.com/simonvetter/modbus

posted @ 2022-11-09 17:17  远方V3  阅读(3197)  评论(0编辑  收藏  举报