一、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;
}