目录

前言

开发环境

工具准备

具体实现

下载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(写寄存器请求)

01Addr10Cmd00 00Reg Addr00 06Number0C不知道作用,是 Number*200 01data1:100 0A (data2:1000 64data3:10003 E8data4:100027 10data5:10000FF FFdata6:655353F A8CRC

000003-Tx(应答信号)

01Addr10Cmd00 00Reg Addr00 06Number40 0BCRC

初探 ModBus4j -简单使用指南相关推荐

  1. django初探-创建简单的博客系统(一)

    django第一步 1. django安装 pip install django print(django.get_version()) 查看django版本 2. 创建项目 打开cmd,进入指定目录 ...

  2. C及C++中typedef的简单使用指南

    C及C++中typedef的简单使用指南 又是在学数据结构的时候,发现了之前学习的知识遗忘很多,在发现对C/C++中关键字typedef的理解还是没有到位后,我翻阅了学C++用到的课本,又问了度娘,也 ...

  3. linux环境下的c编程指南,C语言Socket简单编程指南PDF

    1.介绍 Socket 编程让你沮丧吗?从man pages中很难得到有用的信息吗?你想跟上时代去编Internet相关的程序,但是为你在调用 connect() 前的bind() 的结构而不知所措? ...

  4. AD620的简单使用指南

    AD620的简单使用指南 AD620是一个经常使用的运算放大器,我们先来看看它的引脚: 2,3引脚分别为反向放大电路输入端,正向放大电路输入端:6为输出端:7,4分别接正负电压:1,8之间接电阻,用来 ...

  5. 如何利用计算机漏洞,担心电脑被人利用“CPU漏洞”攻击?这里有一份简单的指南...

    IT之家1月5日消息,近日Intel被曝出此前20年内的CPU都存在漏洞,可以被漏洞攻击者利用来窃取数据,对此全世界的PC用户都变得不淡定起来了,因为现在几乎搭载Intel CPU的设备都在这个范围之 ...

  6. Spring Security OAuth2 Opaque 令牌的简单使用指南

    Spring Security OAuth2 Opaque 令牌的简单使用指南 概述 JWT 是一种以广泛接受的 JSON 格式安全传输敏感信息的方法.包含的信息可能是关于用户的,也可能是关于令牌本身 ...

  7. 小狼毫Rime输入法简单配置指南

    目录 为什么选择它?----Rime 一.下载 二.安装 1.官方安装包下载 2.安装选择 3.安装完成 三.配置小狼毫 语言栏设置 1.简体设置 2.输入框配置 3.字符配置 4.英文输入库配置 为 ...

  8. Flask-SocketIO 简单使用指南

    Flask-SocketIO 使 Flask 应用程序能够访问客户端和服务器之间的低延迟双向通信.客户端应用程序可以使用 Javascript,C ++,Java 和 Swift 中的任何 Socke ...

  9. JavaScript正则表达式快速简单的指南

    Interested in learning JavaScript? Get my ebook at jshandbook.com 有兴趣学习JavaScript吗? 在jshandbook.com上 ...

最新文章

  1. 如何卸载iPhone模拟器中的自己创建的程序
  2. 也许MVC不该重写Url格式?
  3. mfc实现秒表小项目
  4. 为什么现在小学生都学计算机编程了,为何要在小学阶段开展编程教育?
  5. 第15章 SpringBoot集成logging日志
  6. Python 字典dict操作定义
  7. django中的admin组件
  8. 如何构建 HBase 集群监控系统?
  9. Go(4 [Map])
  10. mysql备份脚本 shell_MySQL数据库备份Shell脚本
  11. SpringBoot2.0整合jsp
  12. 攻防世界 --> funny_video --> 最完整和正确的解答
  13. Misra-Gries 算法
  14. favicon.ico显示,favicon显示,favicon图标显示
  15. Linux实战之ssl自签名证书
  16. 选择信号的采样频率和信号长度的技巧
  17. 囚徒健身(中文完整版)(保罗·威德)
  18. [导读]7 Steps to Mastering Machine Learning With Python
  19. ffmpeg截取一段视频中一段视频
  20. 江苏学计算机软件哪个专科大学好,江苏 我学软件技术可以报考哪些学校

热门文章

  1. 基于keras的mnist手写体识别程序
  2. 2022-iOS个人开发者账号申请流程
  3. USB3.0接口防静电及lay out设计
  4. 小程序轮播图:点击放大长按保存。
  5. 手机控制NodeMCU实现远程电脑开关机
  6. 问题:设计一个大学教师和学生管理程序, 教师包括 编号、姓名、职称和教研室 数据的输入输出; 大学生包括编号、姓名、性别、班号、英语、高等数学和数据结构三门课程成绩的输入输出和计算平均分; 研究生包
  7. HotFix移动热修复详解
  8. JS结合PHP瀑布流,JavaScript_原生JS实现响应式瀑布流布局,原生JS实现的瀑布流布局,代 - phpStudy...
  9. iOS 提取图片中的文字
  10. Python-random.seed()的作用