一文搞懂物联网Modbus通讯协议
简介: 一般来说,常见的物联网通讯协议众多,如蓝牙、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]
参考开源项目:
原文链接
本文为阿里云原创内容,未经允许不得转载。