天天看點

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;


    }