簡介: 一般來說,常見的物聯網通訊協議眾多,如藍牙、Zigbee、WiFi、ModBus、PROFINET、EtherCAT、蜂窩等。而在眾多的物聯網通訊協議中,Modbus是當前非常流行的一種通訊協議。它一種串行通信協議,是Modicon公司於1979年為使用可編程邏輯控制器(PLC)通信而制定的,可以說,它已經成為工業領域通信協議的業界標准。
1 概述
隨着IT技術的快速發展,當前已經步入了智能化時代,其中的物聯網技術將在未來占據越來越重要的地位。根據百度百科的定義,物聯網(Internet of things,簡稱IOT )即“萬物相連的互聯網”,是互聯網基礎上的延伸和擴展的網絡,物聯網將各種信息有機的結合起來,實現任何時間、任何地點,人、機、物的互聯互通。物聯網從技術上來說,很重要的核心是通訊協議,即如何按約定的通訊協議,把機、物和人與互聯網相連接,進行信息通信,以實現對人、機和物的智能化識別、定位、跟蹤、監控和管理的一種網絡。
一般來說,常見的物聯網通訊協議眾多,如藍牙、Zigbee、WiFi、ModBus、PROFINET、EtherCAT、蜂窩等。而在眾多的物聯網通訊協議中,Modbus是當前非常流行的一種通訊協議。它一種串行通信協議,是Modicon公司於1979年為使用可編程邏輯控制器(PLC)通信而制定的,可以說,它已經成為工業領域通信協議的業界標准。其優勢如下:
- 免費無版稅限制
- 容易部署
- 靈活限制少
2 ModBus協議概述
Modbus通訊協議使用請求-應答機制在主(Master)(客戶端Client)和從(Slave)(服務器Server)之間交換信息。Client-Server原理是通信協議的模型,其中一個主設備控制多個從設備。這里需要注意的是:Modbus通訊協議當中的Master對應Client,而Slave對應Server。Modbus通訊協議的官網為www.modbus.org。目前官網組織已經建議將Master-Slave替換為Client-Server。從協議類型上可以分為:Modbus-RTU(ASCII)、Modbus-TCP和Modbus-Plus。本文主要介紹Modbus-RTU(ASCII)的通訊協議原理。標准的Modbus協議物理層接口有RS232、RS422、RS485和以太網接口。
通訊示意圖如下:
- 一次只有一個主機(Master)連接到網絡
- 只有主設備(Master)可以啟動通信並向從設備(Slave)發送請求
- 主設備(Master)可以使用其特定地址單獨尋址每個從設備(Slave),也可以使用地址0(廣播)同時尋址所有從設備(Slave)
- 從設備(Slave)只能向主設備(Master)發送回復
- 從設備(Slave)無法啟動與主設備(Master)或其他從設備(Slave)的通信
Modbus協議可使用2種通信模式交換信息:
- 單播模式
- 廣播模式
不管是請求報文還是答復報文,數據結構如下:
- Modbus Poll(Master)
- Modbus Slave
具體的安裝過程這里不再贅述。首先這里需要模擬一個物聯網傳感器設備,這里用Modbus Slave來定義,首先打開此軟件,並定義一個ID為1的設備:
成功建立通訊后,通信的報文格式如下:
3 ModBus Java實現
下面介紹一下如何用Java來實現一個Modbus TCP通信。這里Java框架采用Spring Boot,首先需要引入Modbus4j庫。Maven依賴庫的pom.xml定義如下:
<dependency> <groupId>com.infiniteautomation</groupId> <artifactId>modbus4j</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>org.rxtx</groupId> <artifactId>rxtx</artifactId> <version>2.1.7</version> </dependency>
其中的modbus4j庫可能在Maven中無法正常下載,可以手動下載后放於項目中,並添加到項目庫中。如下圖所示:
訪問
For a JDK installation: Copy RXTXcomm.jar ---> <JAVA_HOME>\jre\lib\ext Copy rxtxSerial.dll ---> <JAVA_HOME>\jre\bin Copy rxtxParallel.dll ---> <JAVA_HOME>\jre\bin
另外,需要注意,這里還需要串口支持,這里可以用虛擬串口軟件來解決。
public static SerialPort open(String portName, Integer baudRate, Integer dataBits, Integer stopBits, Integer parity) { SerialPort result = null; try { CommPortIdentifier identifier = CommPortIdentifier.getPortIdentifier(portName); // 打開端口 CommPort commPort = identifier.open(portName, 2000); // 判斷是不是串口 if (commPort instanceof SerialPort) { result = (SerialPort) commPort; // 設置串口的參數 result.setSerialPortParams(baudRate, dataBits, stopBits, parity); log.info("打開串口{}成功", portName); }else{ log.info("{}不是串口", portName); } } catch (Exception e) { log.error("打開串口{}錯誤", portName, e); } return result; }
首先需要啟動Modbus RTU Slave程序,核心代碼片段如下:
public static void createRtuSlave(){ // 串口是COM3,波特率是9600 SerialPortWrapperImpl wrapper = new SerialPortWrapperImpl("COM3", 9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE, 0, 0); ModbusFactory modbusFactory = new ModbusFactory(); 創建RTU Slave final ModbusSlaveSet slave = modbusFactory.createRtuSlave(wrapper); // 寄存器里可以設置線圈狀態、離散輸入狀態、保持寄存器和輸入寄存器 //從站設備ID是1 BasicProcessImage processImage = new BasicProcessImage(1); processImage.setInvalidAddressValue(Short.MIN_VALUE); slave.addProcessImage(processImage); // 添加監聽器,監聽slave線圈狀態和保持寄存器的寫入 processImage.addListener(new MyProcessImageListener()); //設置數據 setCoil(processImage); setInput(processImage); setHoldingRegister(processImage); setInputRegister(processImage); // 開啟線程啟動 new Thread(() -> { try { slave.start(); } catch (ModbusInitException e) { e.printStackTrace(); } }).start(); }
而Modbus RTU Master程序,核心代碼片段如下:
private static void createRtuMaster() throws Exception{ //串口是COM4,波特率是9600 SerialPortWrapperImpl wrapper = new SerialPortWrapperImpl("COM4", 9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE, 0, 0); ModbusFactory modbusFactory = new ModbusFactory(); //RTU Master ModbusMaster master = modbusFactory.createRtuMaster(wrapper); master.init(); // 從站設備ID是1 int slaveId = 1; // 讀取保持寄存器 readHoldingRegisters(master, slaveId, 0, 3); // 將地址為0的保持寄存器數據修改為0 writeRegister(master, slaveId, 0, 0); // 再讀取保持寄存器 readHoldingRegisters(master, slaveId, 0, 3); }
啟動后輸出如下所示:
//Slave [Thread-1] INFO cn.wu.demo.modbus4j.util.SerialPortUtils - 打開串口COM3成功 保持寄存器地址=0,舊值=8,新值=0 //Master ////////////////////////////////////////////////////////////////////// [main] INFO cn.wu.demo.modbus4j.util.SerialPortUtils - 打開串口COM4成功 讀取保持寄存器=[8, 56, 0] 寫保持寄存器成功 讀取保持寄存器=[0, 56, 0]
參考開源項目:
原文鏈接
本文為阿里雲原創內容,未經允許不得轉載。