天天看点

Java接口开发及Modbus Slave仿真使用 Modbus TCP协议

一、ModbusTcp简介

什么是ModbusTcp?

/1、Modbus rtu和Modbus tcp两个协议的本质都是MODBUS协议,都是靠MODBUS寄存器地址来交换数据;

/2、但所用的硬件接口不一样,Modbus RTU一般采用串口RS232C或RS485/422,而Modbus TCP一般采用以太网口。

/3、现在市场上有很多协议转换器,可以轻松的将这些不同的协议相互转换 如:Intesisbox可以把modbus rtu转换成Modbus tcp实际上Modbus协议包括ASCII、RTU、TCP。

/4、标准的Modicon控制器使用RS232C实现串行的Modbus。Modbus的ASCII、RTU协议规定了消息、数据的结构、命令和就答的方式,数据通讯采用Maser/Slave方式。 

/5、Modbus协议需要对数据进行校验,串行协议中除有奇偶校验外,ASCII模式采用LRC校验,RTU模式采用16位CRC校验.

/6、ModbusTCP模式没有额外规定校验,因为TCP协议是一个面向连接的可靠协议。

/7、TCP和RTU协议非常类似,只要把RTU协议的两个字节的校验码去掉,然后在RTU协议的开始加上5个0和一个6并通过TCP/IP网络协议发送出去即可。

二、报文解析

本报文以电力逆变器设备的报文做解析

1、查询指令

(发送)00 00 00 00 00 06[k1]  01[k2]  03[k3]  00[k4]  00[k5]  00[k6]  00[k7] 

[k1]起始字符组,长度,代表后面还有6个字节

[k2]设备地址

[k3]读指令

[k4]寄存器地址高8位

[k5]寄存器地址低8位

[k6]寄存器数量高8位

[k7]寄存器数量低8位

(返回)00 00 00 00 00 25[k1]  01[k2]  03[k3]  22[k4] (自定义功能码字节区)

[k1]起始字符组,长度,代表后面还有25个字节

[k2]设备地址

[k3]读指令

[k4]表示接下来数据字节的长度

2、控制指令(启动/停止)

(发送)00 00 00 00 00 06[k1]  01[k2]  06[k3]  00 12 00 01[k4]

[k1]起始字符组,长度,代表后面还有6个字节

[k2]设备地址

[k3]写指令

[k4]00=停止/01=启动

(返回)同上

3、逆变功率设定指令

(发送)00 00 00 00 00 06[k1]  01[k2]  06[k3]  00 13 00[k4]  00[k5] 

[k1]起始字符组,长度,代表后面还有6个字节

[k2]设备地址

[k3]写指令

[k4]功率设定高字节

[k5]功率设定低字节

(返回)同上

请求报文案例:

主站向从站发送请求报文:01 03 00 01 00 02 95 CB

01代表设备地址 

03代表功能码(读取保存寄存器的值) 

00 01代表采集点对应的寄存器号

00 02代表读取两个连续寄存器的值

95 CB代表01 03 00 01 00 02计算多得的CRC校验值 

从站向主站放回的数据报文:01 03 04 00 00 00 00 FA 33 

01代表设备地址 

03代表功能码(读取保存寄存器的值)

04代表设备返回的数据个数(单位为字节)

00 00 00 00代表为数据返回的连续两个寄电器的数据

FA 33代表01 03 04 00 00 00 00计算所得的CRC校验码

1、ModbusTCP仿真
  ModbusSlave(软件官方网站地址)是一个从站设备仿真软件,它用于接收主设备的命令包,并回送数据包;可用于测试和调试Modbus主站设备,便于观察Modbus通信过程中的各种报文。ModbusPoll及ModbusSlave支持ModbusRTU, ASCII,TCP/IP等协议。

  首先,了解MODBUS支持的部分功能代码,以十进制表示,如下表所示。

代码    中文名称    英文名称    位操作/字操作    操作数量
01    读线圈状态    READ COIL STATUS    位操作    单个或多个
02    读离散输入状态    READ INPUT STATUS    位操作    单个或多个
03    读保持寄存器    READ HOLDING REGISTER    字操作    单个或多个
04    读输入寄存器    READ INPUT REGISTER    字操作    单个或多个
05    写线圈状态    WRITE SINGLE COIL    位操作    单个
06    写单个保持寄存器    WRITE SINGLE REGISTER    字操作    单个
15    写多个线圈    WRITE MULTIPLE COIL    位操作    多个
16    写多个保持寄存器    WRITE MULTIPLE REGISTER    字操作    多个
  参数设置: 
  点击菜单“Setup”中“Slave Definition.. F2”进行参数设置,会弹出如下图对话框



  其中: 
  (1)Slave ID:设备ID; 
  (2)Function:对应上表所对应的Modbus功能,例如本文所选用的“03 Holding Register…”,与下文Java代码“见类ReadHoldingRegistersResponse ”所对应。 
  (3)Address:寄存器地址; 
  (4)Quantity:数量。

  打开ModbusSlave软件,为方便起见,本文采用默认的地址(localhost,与下文第二段代码对应“见类ClientForTests ”),功能码,寄存器数量,单击Connection->connect,在弹出的窗口设置connection为TCP/IP,端口Port设置为30502,点击OK,如下图所示,从端配置完毕。



  注意: 
  (1)本文连接Connection采用Modbus TCP/IP协议; 
  (2)网络地址为本地地址,127.0.0.1; 
  (3)端口与下文第二段代码“见类ClientForTests”中的地址和端口设置为“30502”; 
  (4)选择“Ignore Unit ID”,如果不选择,测试程序返回空值。
--------------------- 


 /**
     * 读保持寄存器上的内容
     *
     * @param ip        从站IP
     * @param port      modbus端口
     * @param start     起始地址偏移量
     * @param readLenth 待读寄存器个数
     * @return
     */
    public static String modbusTCP(String ip, int port, int start, int readLenth) {
        ModbusFactory modbusFactory = new ModbusFactory();
        // 设备ModbusTCP的Ip与端口,如果不设定端口则默认为502
        IpParameters params = new IpParameters();
        params.setHost("116.62.210.27");
//        设置端口,默认502
        if (502 != 15835) {
            params.setPort(15835);
        }
        ModbusMaster tcpMaster = null;
        tcpMaster = modbusFactory.createTcpMaster(params, true);
        try {
            tcpMaster.init();
            System.out.println("========初始化成功=======");
        } catch (Exception e) {
            return null;
        }
        ModbusRequest modbusRequest = null;
        try {
            //功能码03   读取保持寄存器的值
            modbusRequest = new ReadHoldingRegistersRequest(1, start, readLenth);
        } catch (Exception e) {
            e.printStackTrace();
        }
        ModbusResponse modbusResponse = null;
        try {
            modbusResponse = tcpMaster.send(modbusRequest);
        } catch (Exception e) {
            e.printStackTrace();
        }
        ByteQueue byteQueue = new ByteQueue(1024);
        modbusResponse.write(byteQueue);
        System.out.println("功能码:" + modbusRequest.getFunctionCode());
        System.out.println("从站地址:" + modbusRequest.getSlaveId());
        System.out.println("收到的响应信息大小:" + byteQueue.size());
        System.out.println("收到的响应信息值:" + byteQueue);
        //数据解析
        Float fl = toBytes(String.valueOf(byteQueue));
        return fl.toString();
    }

//----------------------------------------------解析16进制数据
    public static Float toBytes(String s) {
        if (s.startsWith("["))
            s = s.substring(1);
        if (s.endsWith("]"))
            s = s.substring(0, s.length() - 1);
        String parts = s.replaceAll("[`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~!@#¥%……& amp;*()——+|{}【】‘;:”“’。,、?|-]", "");
        Float value = Float.intBitsToFloat(Integer.valueOf(parts.trim(), 16));
        System.out.println("value=================" + value);
        return value;


    }