电子说
经过上面的介绍,相信大家已经对于 Modbus 有了一个大致的了解。
那么,如何在安卓中使用 Modbus 呢?如果你理解了 Modbus 的基础,并且前面的两篇文章也大致理解了,那么这就不是问题了。
核心思路就是通过上篇文章介绍的使用 android-serialport-api 或使用 USB Host 的方法打开串口,并获取到输入输出流,然后在发送和接收数据时按照 Modbus 协议标准封装或解析即可。
其中如何打开串口以及获取输入输出流已经在上篇文章介绍,因此现在需要解决的是如何封装/解析数据。
当然,你可以按照 Modbus 标准文档自己动手写一个。
或者,你也可以不用重复造轮子,直接使用现成的第三方库。
这里我们可以使用 modbus4j,但是,从它的名字就可以看出来,这是一个 java 库,好在我们只需要使用它的解析和封装的功能,所以在安卓中依旧可以使用。
老规矩,使用 modbus4j 前需要先引入依赖:
// 添加仓库地址
repositories {
...
maven { url 'https://jitpack.io' }
}
……
// 添加依赖
implementation 'com.github.MangoAutomation:modbus4j:3.1.0'
然后在正式使用之前,我们需要新建一个类继承自 SerialPortWrapper
,用于实现在安卓上的串口功能:
class AndroidWrapper : SerialPortWrapper {
// 关闭串口
override fun close() {
TODO("Not yet implemented")
}
// 打开串口
override fun open() {
TODO("Not yet implemented")
}
// 获取输入流
override fun getInputStream(): InputStream {
TODO("Not yet implemented")
}
// 获取输出流
override fun getOutputStream(): OutputStream {
TODO("Not yet implemented")
}
// 获取波特率
override fun getBaudRate(): Int {
TODO("Not yet implemented")
}
// 获取数据位
override fun getDataBits(): Int {
TODO("Not yet implemented")
}
// 获取停止位
override fun getStopBits(): Int {
TODO("Not yet implemented")
}
// 获取校验位
override fun getParity(): Int {
TODO("Not yet implemented")
}
}
在我们新建的这个类中重写上述几个方法,用于提供串口通信所需要的几个参数即可。
然后,初始化 modbus4j 并发送消息:
val modbusFactory = ModbusFactory()
val wrapper: SerialPortWrapper = AndroidWrapper()
// 创建管理对象
val master = modbusFactory.createRtuMaster(wrapper)
// 发送消息
val request = ……
val response = master.send(request) // requst 为要发送的数据,response 为接收到的响应数据
上面就是 modbus4j 的简单使用方法,如果同学们甚至都不想自己去完成串口通信的话,还可以用这个库 Modbus4Android ,这个库基于 android-serialport-api 和 上面的 modbus4j 封装了一个安卓上到手即用的 Modbus 库。
不过它使用的是 android-serialport-api 实现串口通信,如果需要使用 USB Host 的话可能还是需要自己去封装一个库了。(等我找到合适的测试设备后抽空我也封装一个)
并且,这个库使用了 RxJava 如果不喜欢 RxJava 的话也得自己封装一个了,其实封装起来也不算难,完全可以基于这个库自己改一改就好了。
使用这个库的第一步,依旧是导入依赖:
// 添加远程仓库
repositories {
maven { url 'https://jitpack.io' }
}
……
// 添加依赖
dependencies {
implementation 'com.github.licheedev:Modbus4Android:2.0.2'
}
接下来,为了方便使用,同时为了避免重复初始化,我们可以创建一个全局单例实例 ModbusManager
:
class ModbusManager : ModbusWorker() {
/**
* 释放整个ModbusManager,单例会被置null
*/
@Synchronized
override fun release() {
super.release()
sInstance = null
}
companion object {
@Volatile
private var sInstance: ModbusManager? = null
fun getInstance(): ModbusManager {
var manager = sInstance
if (manager == null) {
synchronized(ModbusManager::class.java) {
manager = sInstance
if (manager == null) {
manager = ModbusManager()
sInstance = manager
}
}
}
return manager!!
}
}
}
复制代码
然后初始化串口连接:
private fun initConnect(): Boolean {
Log.i(TAG, "initConnect: 开始初始化连接 Modbus\\nconfig=$config")
val param = SerialParam
.create(config.serialPath, config.serialRate) // 串口地址和波特率
.setDataBits(config.serialDataBits) // 数据位
.setParity(config.serialParity) // 校验位
.setStopBits(config.serialStopBits) // 停止位
.setTimeout(config.serialTimeout) //超时时间
.setRetries(config.serialRetries) // 重试次数
try {
// 初始化前先关闭,避免串口已经被打开过
ModbusManager.getInstance().closeModbusMaster()
val modbusMaster = ModbusManager.getInstance().syncInit(param)
return true
// 初始化(打开串口)成功
} catch (e: ModbusInitException) {
Log.e(TAG, "initConnect: 初始化modbus出错!", e)
} catch (e: InterruptedException) {
Log.e(TAG, "initConnect: 初始化modbus出错!", e)
} catch (e: ExecutionException) {
Log.e(TAG, "initConnect: 初始化modbus出错!", e)
} catch (e: ModbusTransportException) {
Log.e(TAG, "initConnect: 初始化modbus出错!", e)
} catch (e: ModbusRespException) {
Log.e(TAG, "initConnect: 初始化modbus出错!", e)
}
return false
}
完成上述步骤后,我们就可以开始发送请求并接收数据了。
这里依旧以读取线圈数据为例,我们可以使用同步请求:
val slaveId = 1 // 从站地址
val start = 00001 // 读取的起始位置
val len = 1 // 需要读取的长度
val response = ModbusManager.getInstance().syncReadCoil(slaveId, start, len)
其中的 response
即为响应数据信息。
另外,我们也可以使用异步读取的方式:
ModbusManager.getInstance().readCoil(slaveId, start, len, object : ModbusCallback
该库支持的所有读取方法如下:
所有写数据方法如下:
我们在这篇文章中介绍了在安卓中使用串口通信时大概率会接触到的一种应用层协议 -- Modbus,并讲解了如何在安卓中使用 Modbus ,另外介绍了几个个人认为比较好用的第三方库。
全部0条评论
快来发表一下你的评论吧 !