初探 ModBus4j -简单使用指南
目录
前言
开发环境
工具准备
具体实现
下载Modbus4j
解决空指针异常
解决数组越界
测试
测试环境准备
正式测试
前言
之前提到过 由于项目需求,需要封装 ModBus协议,ModBus协议较早,网上开源开源库也不少,可参见 Modbus 史上最全实例资料汇总。安卓上支持ModBus-RTU的库包较为稀缺,毕竟一般安卓手机不会带个串口。所幸运 Android 是一个大的框架,因而我想到了两种思路:
从底层出发,使用 C/C++ 或 Python 的开源库,通过 JNI 为应用层提供调用。
在应用层移植现有ModBus-java协议库,再通过修改协议的传输层将串行通讯修改为 BLE无线传输。
本文采用的是第二种方法,使用的库是 ModBus4j(点击跳转至下载地址),在 Java 平台调试后再移植到Android,随和修改数据传输方式,在此之前会梳理开源库包的实现,如有必要对自行搭轮子也有不小的帮助。
开发环境
- VSCode1.39.2
- JDK1.8.0_221
- JRE1.8.0_221
工具准备
工欲善其事必先利其器。 --- 不是我说的
使用 Modbus4j 前我们需要准备以下工具以便调试
Modbus Poll(模拟ModBus主站)&& Modbus Slave(模拟ModBus从站)
下载地址
Virtual Serial Port Driver Pro(虚拟串口)
下载地址
安装好工具,我一般会先 玩会 测试一下,使用方法见:
modbus slave 和 modbus poll 使用说明
Modbus 测试工具 ModbusPoll 与 Modbus Slave 使用方法
具体实现
下载Modbus4j
下载 ModBus4j ,并用VSCode 打开
运行 MasterTest.java (这里修改了一下,故而贴出)
package com.serotonin.modbus4j.test;import java.util.Arrays;import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ModbusTransportException;
//import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.msg.ReadCoilsRequest;
import com.serotonin.modbus4j.msg.ReadCoilsResponse;
import com.serotonin.modbus4j.msg.ReadDiscreteInputsRequest;
import com.serotonin.modbus4j.msg.ReadDiscreteInputsResponse;
import com.serotonin.modbus4j.msg.ReadExceptionStatusRequest;
import com.serotonin.modbus4j.msg.ReadExceptionStatusResponse;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersRequest;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersResponse;
import com.serotonin.modbus4j.msg.ReadInputRegistersRequest;
import com.serotonin.modbus4j.msg.ReadInputRegistersResponse;
import com.serotonin.modbus4j.msg.ReportSlaveIdRequest;
import com.serotonin.modbus4j.msg.ReportSlaveIdResponse;
import com.serotonin.modbus4j.msg.WriteCoilRequest;
import com.serotonin.modbus4j.msg.WriteCoilResponse;
import com.serotonin.modbus4j.msg.WriteCoilsRequest;
import com.serotonin.modbus4j.msg.WriteCoilsResponse;
import com.serotonin.modbus4j.msg.WriteMaskRegisterRequest;
import com.serotonin.modbus4j.msg.WriteMaskRegisterResponse;
import com.serotonin.modbus4j.msg.WriteRegisterRequest;
import com.serotonin.modbus4j.msg.WriteRegisterResponse;
import com.serotonin.modbus4j.msg.WriteRegistersRequest;
import com.serotonin.modbus4j.msg.WriteRegistersResponse;public class MasterTest {public static void main(String[] args) throws Exception {String commPortId = "COM1";int baudRate = 9600;int flowControlIn = 0;int flowControlOut = 0; int dataBits = 8;int stopBits = 1;int parity = 0;TestSerialPortWrapper wrapper = new TestSerialPortWrapper(commPortId, baudRate, flowControlIn, flowControlOut, dataBits, stopBits, parity);//IpParameters ipParameters = new IpParameters();//ipParameters.setHost("localhost");ModbusFactory modbusFactory = new ModbusFactory();ModbusMaster master = modbusFactory.createRtuMaster(wrapper);// ModbusMaster master = modbusFactory.createAsciiMaster(wrapper);//ModbusMaster master = modbusFactory.createTcpMaster(ipParameters, false);// ModbusMaster master = modbusFactory.createUdpMaster(ipParameters);try {master.init();int slaveId = 1;// readCoilTest(master, slaveId, 0, 10);// readCoilTest(master, slaveId, 99, 200);// readDiscreteInputTest(master, slaveId, 1, 10);// readDiscreteInputTest(master, slaveId, 449, 72);/*//This is Success//读取保持寄存器readHoldingRegistersTest(master, slaveId, 9, 125);*/// readHoldingRegistersTest(master, slaveId, 9, 120);// readInputRegistersTest(master, slaveId, 0, 1);// readInputRegistersTest(master, slaveId, 14, 8);// writeCoilTest(master, slaveId, 1, true);// writeCoilTest(master, slaveId, 110, true);/*//This is Success//写单个寄存器writeRegisterTest(master, slaveId, 0, 1);*/// writeRegisterTest(master, slaveId, 14, 12345);// readExceptionStatusTest(master, slaveId);// reportSlaveIdTest(master, slaveId);// writeCoilsTest(master, slaveId, 50, new boolean[] {true, false, false, true, false});// writeCoilsTest(master, slaveId, 115, new boolean[] {true, false, false, true, false});//This is Success//写多个寄存器writeRegistersTest(master, slaveId, 300, new short[] {1, 10, 100, 1000, 10000, (short)65535});// writeRegistersTest(master, slaveId, 21, new short[] {1, 10, 100, 1000, 10000, (short)65535});//This is Success// writeMaskRegisterTest(master, slaveId, 26, 0xf2, 0x25);// readCoilTest(master, slaveId, 9, 5);// readCoilTest(master, slaveId, 10, 5);// readDiscreteInputTest(master, slaveId, 10, 6);// readDiscreteInputTest(master, slaveId, 10, 5);// readHoldingRegistersTest(master, slaveId, 9, 7);// readHoldingRegistersTest(master, slaveId, 10, 5);// readInputRegistersTest(master, slaveId, 0, 1);// readInputRegistersTest(master, slaveId, 10, 5);// writeCoilTest(master, slaveId, 8, true);// writeCoilTest(master, slaveId, 11, true);// writeRegisterTest(master, slaveId, 1, 1);// writeRegisterTest(master, slaveId, 14, 12345);// readExceptionStatusTest(master, slaveId);// reportSlaveIdTest(master, slaveId);// writeCoilsTest(master, slaveId, 11, new boolean[] {false, true, false, false, true});// writeCoilsTest(master, slaveId, 10, new boolean[] {false, true, false, false, true});// writeRegistersTest(master, slaveId, 11, new short[] {(short)65535, 1000, 100, 10, 1});// writeRegistersTest(master, slaveId, 10, new short[] {(short)65535, 1000, 100, 10, 1});// writeMaskRegisterTest(master, slaveId, 9, 0xf2, 0x25);// writeMaskRegisterTest(master, slaveId, 10, 0xf2, 0x25);// Automatic WriteMaskRegister failover test// ModbusLocator locator = new ModbusLocator(slaveId, RegisterRange.HOLDING_REGISTER, 15, (byte)2);// System.out.println(master.getValue(locator));// master.setValue(locator, true);// System.out.println(master.getValue(locator));// master.setValue(locator, false);// System.out.println(master.getValue(locator));// BatchRead<String> batch = new BatchRead<String>();// batch.addLocator("hr1", new ModbusLocator(31, RegisterRange.HOLDING_REGISTER, 80,// DataType.TWO_BYTE_BCD));// batch.addLocator("hr2", new ModbusLocator(31, RegisterRange.HOLDING_REGISTER, 81,// DataType.FOUR_BYTE_BCD));// BatchResults<String> results = master.send(batch);// System.out.println(results.getValue("hr1"));// System.out.println(results.getValue("hr2"));// This's Successful Way to set Reg Data // BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, 10, DataType.EIGHT_BYTE_INT_UNSIGNED);// master.setValue(locator, 10000000);// System.out.println(master.getValue(locator));}finally {master.destroy();}}public static void readCoilTest(ModbusMaster master, int slaveId, int start, int len) {try {ReadCoilsRequest request = new ReadCoilsRequest(slaveId, start, len);ReadCoilsResponse response = (ReadCoilsResponse) master.send(request);if (response.isException())System.out.println("Exception response: message=" + response.getExceptionMessage());elseSystem.out.println(Arrays.toString(response.getBooleanData()));}catch (ModbusTransportException e) {e.printStackTrace();}}public static void readDiscreteInputTest(ModbusMaster master, int slaveId, int start, int len) {try {ReadDiscreteInputsRequest request = new ReadDiscreteInputsRequest(slaveId, start, len);ReadDiscreteInputsResponse response = (ReadDiscreteInputsResponse) master.send(request);if (response.isException())System.out.println("Exception response: message=" + response.getExceptionMessage());elseSystem.out.println(Arrays.toString(response.getBooleanData()));}catch (ModbusTransportException e) {e.printStackTrace();}}public static void readHoldingRegistersTest(ModbusMaster master, int slaveId, int start, int len) {try {ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(slaveId, start, len);ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) master.send(request);if (response.isException())System.out.println("Exception response: message=" + response.getExceptionMessage());elseSystem.out.println(Arrays.toString(response.getShortData()));}catch (ModbusTransportException e) {e.printStackTrace();}}public static void readInputRegistersTest(ModbusMaster master, int slaveId, int start, int len) {try {ReadInputRegistersRequest request = new ReadInputRegistersRequest(slaveId, start, len);ReadInputRegistersResponse response = (ReadInputRegistersResponse) master.send(request);if (response.isException())System.out.println("Exception response: message=" + response.getExceptionMessage());elseSystem.out.println(Arrays.toString(response.getShortData()));}catch (ModbusTransportException e) {e.printStackTrace();}}public static void writeCoilTest(ModbusMaster master, int slaveId, int offset, boolean value) {try {WriteCoilRequest request = new WriteCoilRequest(slaveId, offset, value);WriteCoilResponse response = (WriteCoilResponse) master.send(request);if (response.isException())System.out.println("Exception response: message=" + response.getExceptionMessage());elseSystem.out.println("Success");}catch (ModbusTransportException e) {e.printStackTrace();}}public static void writeRegisterTest(ModbusMaster master, int slaveId, int offset, int value) {try {WriteRegisterRequest request = new WriteRegisterRequest(slaveId, offset, value);WriteRegisterResponse response = (WriteRegisterResponse) master.send(request);if (response.isException())System.out.println("Exception response: message=" + response.getExceptionMessage());elseSystem.out.println("Success");}catch (ModbusTransportException e) {e.printStackTrace();}}public static void readExceptionStatusTest(ModbusMaster master, int slaveId) {try {ReadExceptionStatusRequest request = new ReadExceptionStatusRequest(slaveId);ReadExceptionStatusResponse response = (ReadExceptionStatusResponse) master.send(request);if (response.isException())System.out.println("Exception response: message=" + response.getExceptionMessage());elseSystem.out.println(response.getExceptionStatus());}catch (ModbusTransportException e) {e.printStackTrace();}}public static void reportSlaveIdTest(ModbusMaster master, int slaveId) {try {ReportSlaveIdRequest request = new ReportSlaveIdRequest(slaveId);ReportSlaveIdResponse response = (ReportSlaveIdResponse) master.send(request);if (response.isException())System.out.println("Exception response: message=" + response.getExceptionMessage());elseSystem.out.println(Arrays.toString(response.getData()));}catch (ModbusTransportException e) {e.printStackTrace();}}public static void writeCoilsTest(ModbusMaster master, int slaveId, int start, boolean[] values) {try {WriteCoilsRequest request = new WriteCoilsRequest(slaveId, start, values);WriteCoilsResponse response = (WriteCoilsResponse) master.send(request);if (response.isException())System.out.println("Exception response: message=" + response.getExceptionMessage());elseSystem.out.println("Success");}catch (ModbusTransportException e) {e.printStackTrace();}}public static void writeRegistersTest(ModbusMaster master, int slaveId, int start, short[] values) {try {WriteRegistersRequest request = new WriteRegistersRequest(slaveId, start, values);WriteRegistersResponse response = (WriteRegistersResponse) master.send(request);if (response.isException())System.out.println("Exception response: message=" + response.getExceptionMessage());elseSystem.out.println("Success");}catch (ModbusTransportException e) {e.printStackTrace();}}public static void writeMaskRegisterTest(ModbusMaster master, int slaveId, int offset, int and, int or) {try {WriteMaskRegisterRequest request = new WriteMaskRegisterRequest(slaveId, offset, and, or);WriteMaskRegisterResponse response = (WriteMaskRegisterResponse) master.send(request);if (response.isException())System.out.println("Exception response: message=" + response.getExceptionMessage());elseSystem.out.println("Success");}catch (ModbusTransportException e) {e.printStackTrace();}}
}
运行结果:空指针异常
解决空指针异常
这是由于 ModBus4J 并没有给我们提供底层串口驱动
落后的解决方法:
- 使用 Sum 基本放弃的 javacomm
最常见的解决方法:
- 使用 RXTXcomm.jar
受到前辈启发:
modbus4j初次使用总结(该解决方案主要参照了 Freedomotic Open IoT Framework 开源框架)
- 使用 Jssc.jar
如果你不会导入库包,请前往 Visual Studio Code 手动导入 jar 包
导入库包后,我们去实现 TestSerialPortWrapper.java
/*** Copyright (C) 2015 Infinite Automation Software. All rights reserved.* @author Terry Packer*/
package com.serotonin.modbus4j.test;import com.serotonin.modbus4j.serial.SerialPortWrapper;
import jssc.SerialPort;import java.io.InputStream;
import java.io.OutputStream;import jssc.SerialPortException;
//The project cannot be built until build path errors are resolved
/*** * This class is not finished* * @author Terry Packer**/
public class TestSerialPortWrapper implements SerialPortWrapper{private SerialPort port;private String commPortId;private int baudRate;private int flowControlIn;private int flowControlOut;private int dataBits;private int stopBits;private int parity;public TestSerialPortWrapper(String commPortId, int baudRate, int flowControlIn,int flowControlOut, int dataBits, int stopBits, int parity){this.commPortId = commPortId;this.baudRate = baudRate;this.flowControlIn = flowControlIn;this.flowControlOut = flowControlOut;this.dataBits = dataBits;this.stopBits = stopBits;this.parity = parity;port = new SerialPort(this.commPortId);}/* (non-Javadoc)* @see com.serotonin.modbus4j.serial.SerialPortWrapper#close()*/@Overridepublic void close() throws Exception {port.closePort();// TODO Auto-generated method stub}/* (non-Javadoc)* @see com.serotonin.modbus4j.serial.SerialPortWrapper#open()*/@Overridepublic void open() throws Exception {try {port.openPort();port.setParams(this.getBaudRate(), this.getDataBits(), this.getStopBits(), this.getParity());port.setFlowControlMode(this.getFlowControlIn() | this.getFlowControlOut());//listeners.forEach(PortConnectionListener::opened);//LOG.debug("Serial port {} opened", port.getPortName());} catch (SerialPortException ex) {//LOG.error("Error opening port : {} for {} ", port.getPortName(), ex);}// TODO Auto-generated method stub}/* (non-Javadoc)* @see com.serotonin.modbus4j.serial.SerialPortWrapper#getInputStream()*/@Overridepublic InputStream getInputStream() {// TODO Auto-generated method stubreturn new SerialInputStream(port);}/* (non-Javadoc)* @see com.serotonin.modbus4j.serial.SerialPortWrapper#getOutputStream()*/@Overridepublic OutputStream getOutputStream() {// TODO Auto-generated method stubreturn new SerialOutputStream(port);}/* (non-Javadoc)* @see com.serotonin.modbus4j.serial.SerialPortWrapper#getBaudRate()*/@Overridepublic int getBaudRate() {// TODO Auto-generated method stubreturn baudRate;}public int getFlowControlIn() {return flowControlIn;//return SerialPort.FLOWCONTROL_NONE;}public int getFlowControlOut() {return flowControlOut;//return SerialPort.FLOWCONTROL_NONE;}/* (non-Javadoc)* @see com.serotonin.modbus4j.serial.SerialPortWrapper#getStopBits()*/@Overridepublic int getStopBits() {// TODO Auto-generated method stubreturn stopBits;}/* (non-Javadoc)* @see com.serotonin.modbus4j.serial.SerialPortWrapper#getParity()*/@Overridepublic int getParity() {// TODO Auto-generated method stubreturn parity;}/* (non-Javadoc)* @see com.serotonin.modbus4j.serial.SerialPortWrapper#getDataBits()*/@Overridepublic int getDataBits() {// TODO Auto-generated method stubreturn dataBits;}}
此外我们还需要将下图两个文件添加到我们的项目内
这时再运行程序还会出现错误:数组越界错误
解决数组越界
修改 SerialInputStream.java 中的 read( )
@Overridepublic int read(byte[] buf, int offset, int length) throws IOException {if (buf.length < offset + length) {length = buf.length - offset;}int available = this.available();if (available > length) {available = length;}try {byte[] readBuf = serialPort.readBytes(available);System.arraycopy(readBuf, 0, buf, offset, readBuf.length);return readBuf.length;} catch (Exception e) {throw new IOException(e);}}
修改 SerialOutputStream.java 中的 write( )
@Overridepublic void write(byte[] b, int off, int len) throws IOException {byte[] buffer = new byte[len];System.arraycopy(b, off, buffer, 0, b.length);try {serialPort.writeBytes(buffer);} catch (SerialPortException e) {throw new IOException(e);}}
测试
测试环境准备
虚拟串口:建立 COM1 和 COM2 虚拟连接
添加完成后可在设备管理器中查看
ModBus Slave
F8 配置
F3配置
正式测试
读多个保持寄存器
写多个保持寄存器
注意到,最后一个寄存器写 65535 却显示 -1
这是由于显示格式导致的,我们可在 Display 中设置为其他显示格式,例如十六进制Hex
若想查看详细的数据,可在Modbus Slave 中点击工具栏的 Display--Communication Traffic 查看详细的读写信息
以文中写多个寄存器为例
000002-Rx(写寄存器请求)
01(Addr)10(Cmd) 00 00 (Reg Addr)00 06(Number) 0C(不知道作用,是 Number*2 ) 00 01(data1:1) 00 0A (data2:10)00 64 (data3:100)03 E8(data4:1000) 27 10(data5:10000) FF FF (data6:65535)3F A8 (CRC)
000003-Tx(应答信号)
01 (Addr)10 (Cmd)00 00(Reg Addr) 00 06(Number) 40 0B (CRC)
初探 ModBus4j -简单使用指南相关推荐
- django初探-创建简单的博客系统(一)
django第一步 1. django安装 pip install django print(django.get_version()) 查看django版本 2. 创建项目 打开cmd,进入指定目录 ...
- C及C++中typedef的简单使用指南
C及C++中typedef的简单使用指南 又是在学数据结构的时候,发现了之前学习的知识遗忘很多,在发现对C/C++中关键字typedef的理解还是没有到位后,我翻阅了学C++用到的课本,又问了度娘,也 ...
- linux环境下的c编程指南,C语言Socket简单编程指南PDF
1.介绍 Socket 编程让你沮丧吗?从man pages中很难得到有用的信息吗?你想跟上时代去编Internet相关的程序,但是为你在调用 connect() 前的bind() 的结构而不知所措? ...
- AD620的简单使用指南
AD620的简单使用指南 AD620是一个经常使用的运算放大器,我们先来看看它的引脚: 2,3引脚分别为反向放大电路输入端,正向放大电路输入端:6为输出端:7,4分别接正负电压:1,8之间接电阻,用来 ...
- 如何利用计算机漏洞,担心电脑被人利用“CPU漏洞”攻击?这里有一份简单的指南...
IT之家1月5日消息,近日Intel被曝出此前20年内的CPU都存在漏洞,可以被漏洞攻击者利用来窃取数据,对此全世界的PC用户都变得不淡定起来了,因为现在几乎搭载Intel CPU的设备都在这个范围之 ...
- Spring Security OAuth2 Opaque 令牌的简单使用指南
Spring Security OAuth2 Opaque 令牌的简单使用指南 概述 JWT 是一种以广泛接受的 JSON 格式安全传输敏感信息的方法.包含的信息可能是关于用户的,也可能是关于令牌本身 ...
- 小狼毫Rime输入法简单配置指南
目录 为什么选择它?----Rime 一.下载 二.安装 1.官方安装包下载 2.安装选择 3.安装完成 三.配置小狼毫 语言栏设置 1.简体设置 2.输入框配置 3.字符配置 4.英文输入库配置 为 ...
- Flask-SocketIO 简单使用指南
Flask-SocketIO 使 Flask 应用程序能够访问客户端和服务器之间的低延迟双向通信.客户端应用程序可以使用 Javascript,C ++,Java 和 Swift 中的任何 Socke ...
- JavaScript正则表达式快速简单的指南
Interested in learning JavaScript? Get my ebook at jshandbook.com 有兴趣学习JavaScript吗? 在jshandbook.com上 ...
最新文章
- 如何卸载iPhone模拟器中的自己创建的程序
- 也许MVC不该重写Url格式?
- mfc实现秒表小项目
- 为什么现在小学生都学计算机编程了,为何要在小学阶段开展编程教育?
- 第15章 SpringBoot集成logging日志
- Python 字典dict操作定义
- django中的admin组件
- 如何构建 HBase 集群监控系统?
- Go(4 [Map])
- mysql备份脚本 shell_MySQL数据库备份Shell脚本
- SpringBoot2.0整合jsp
- 攻防世界 --> funny_video --> 最完整和正确的解答
- Misra-Gries 算法
- favicon.ico显示,favicon显示,favicon图标显示
- Linux实战之ssl自签名证书
- 选择信号的采样频率和信号长度的技巧
- 囚徒健身(中文完整版)(保罗·威德)
- [导读]7 Steps to Mastering Machine Learning With Python
- ffmpeg截取一段视频中一段视频
- 江苏学计算机软件哪个专科大学好,江苏 我学软件技术可以报考哪些学校
热门文章
- 基于keras的mnist手写体识别程序
- 2022-iOS个人开发者账号申请流程
- USB3.0接口防静电及lay out设计
- 小程序轮播图:点击放大长按保存。
- 手机控制NodeMCU实现远程电脑开关机
- 问题:设计一个大学教师和学生管理程序, 教师包括 编号、姓名、职称和教研室 数据的输入输出; 大学生包括编号、姓名、性别、班号、英语、高等数学和数据结构三门课程成绩的输入输出和计算平均分; 研究生包
- HotFix移动热修复详解
- JS结合PHP瀑布流,JavaScript_原生JS实现响应式瀑布流布局,原生JS实现的瀑布流布局,代 - phpStudy...
- iOS 提取图片中的文字
- Python-random.seed()的作用