在安卓中使用 Modbus
經(jīng)過上面的介紹,相信大家已經(jīng)對(duì)于 Modbus 有了一個(gè)大致的了解。
那么,如何在安卓中使用 Modbus 呢?如果你理解了 Modbus 的基礎(chǔ),并且前面的兩篇文章也大致理解了,那么這就不是問題了。
核心思路就是通過上篇文章介紹的使用 android-serialport-api 或使用 USB Host 的方法打開串口,并獲取到輸入輸出流,然后在發(fā)送和接收數(shù)據(jù)時(shí)按照 Modbus 協(xié)議標(biāo)準(zhǔn)封裝或解析即可。
其中如何打開串口以及獲取輸入輸出流已經(jīng)在上篇文章介紹,因此現(xiàn)在需要解決的是如何封裝/解析數(shù)據(jù)。
當(dāng)然,你可以按照 Modbus 標(biāo)準(zhǔn)文檔自己動(dòng)手寫一個(gè)。
或者,你也可以不用重復(fù)造輪子,直接使用現(xiàn)成的第三方庫。
這里我們可以使用 modbus4j,但是,從它的名字就可以看出來,這是一個(gè) java 庫,好在我們只需要使用它的解析和封裝的功能,所以在安卓中依舊可以使用。
modbus4j
老規(guī)矩,使用 modbus4j 前需要先引入依賴:
// 添加倉庫地址
repositories {
...
maven { url 'https://jitpack.io' }
}
……
// 添加依賴
implementation 'com.github.MangoAutomation:modbus4j:3.1.0'
然后在正式使用之前,我們需要新建一個(gè)類繼承自 SerialPortWrapper
,用于實(shí)現(xiàn)在安卓上的串口功能:
class AndroidWrapper : SerialPortWrapper {
// 關(guān)閉串口
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")
}
// 獲取數(shù)據(jù)位
override fun getDataBits(): Int {
TODO("Not yet implemented")
}
// 獲取停止位
override fun getStopBits(): Int {
TODO("Not yet implemented")
}
// 獲取校驗(yàn)位
override fun getParity(): Int {
TODO("Not yet implemented")
}
}
在我們新建的這個(gè)類中重寫上述幾個(gè)方法,用于提供串口通信所需要的幾個(gè)參數(shù)即可。
然后,初始化 modbus4j 并發(fā)送消息:
val modbusFactory = ModbusFactory()
val wrapper: SerialPortWrapper = AndroidWrapper()
// 創(chuàng)建管理對(duì)象
val master = modbusFactory.createRtuMaster(wrapper)
// 發(fā)送消息
val request = ……
val response = master.send(request) // requst 為要發(fā)送的數(shù)據(jù),response 為接收到的響應(yīng)數(shù)據(jù)
上面就是 modbus4j 的簡(jiǎn)單使用方法,如果同學(xué)們甚至都不想自己去完成串口通信的話,還可以用這個(gè)庫 Modbus4Android ,這個(gè)庫基于 android-serialport-api 和 上面的 modbus4j 封裝了一個(gè)安卓上到手即用的 Modbus 庫。
不過它使用的是 android-serialport-api 實(shí)現(xiàn)串口通信,如果需要使用 USB Host 的話可能還是需要自己去封裝一個(gè)庫了。(等我找到合適的測(cè)試設(shè)備后抽空我也封裝一個(gè))
并且,這個(gè)庫使用了 RxJava 如果不喜歡 RxJava 的話也得自己封裝一個(gè)了,其實(shí)封裝起來也不算難,完全可以基于這個(gè)庫自己改一改就好了。
Modbus4Android
使用這個(gè)庫的第一步,依舊是導(dǎo)入依賴:
// 添加遠(yuǎn)程倉庫
repositories {
maven { url 'https://jitpack.io' }
}
……
// 添加依賴
dependencies {
implementation 'com.github.licheedev:Modbus4Android:2.0.2'
}
接下來,為了方便使用,同時(shí)為了避免重復(fù)初始化,我們可以創(chuàng)建一個(gè)全局單例實(shí)例 ModbusManager
:
class ModbusManager : ModbusWorker() {
/**
* 釋放整個(gè)ModbusManager,單例會(huì)被置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!!
}
}
}
復(fù)制代碼
然后初始化串口連接:
private fun initConnect(): Boolean {
Log.i(TAG, "initConnect: 開始初始化連接 Modbus\\nconfig=$config")
val param = SerialParam
.create(config.serialPath, config.serialRate) // 串口地址和波特率
.setDataBits(config.serialDataBits) // 數(shù)據(jù)位
.setParity(config.serialParity) // 校驗(yàn)位
.setStopBits(config.serialStopBits) // 停止位
.setTimeout(config.serialTimeout) //超時(shí)時(shí)間
.setRetries(config.serialRetries) // 重試次數(shù)
try {
// 初始化前先關(guān)閉,避免串口已經(jīng)被打開過
ModbusManager.getInstance().closeModbusMaster()
val modbusMaster = ModbusManager.getInstance().syncInit(param)
return true
// 初始化(打開串口)成功
} catch (e: ModbusInitException) {
Log.e(TAG, "initConnect: 初始化modbus出錯(cuò)!", e)
} catch (e: InterruptedException) {
Log.e(TAG, "initConnect: 初始化modbus出錯(cuò)!", e)
} catch (e: ExecutionException) {
Log.e(TAG, "initConnect: 初始化modbus出錯(cuò)!", e)
} catch (e: ModbusTransportException) {
Log.e(TAG, "initConnect: 初始化modbus出錯(cuò)!", e)
} catch (e: ModbusRespException) {
Log.e(TAG, "initConnect: 初始化modbus出錯(cuò)!", e)
}
return false
}
完成上述步驟后,我們就可以開始發(fā)送請(qǐng)求并接收數(shù)據(jù)了。
這里依舊以讀取線圈數(shù)據(jù)為例,我們可以使用同步請(qǐng)求:
val slaveId = 1 // 從站地址
val start = 00001 // 讀取的起始位置
val len = 1 // 需要讀取的長(zhǎng)度
val response = ModbusManager.getInstance().syncReadCoil(slaveId, start, len)
其中的 response
即為響應(yīng)數(shù)據(jù)信息。
另外,我們也可以使用異步讀取的方式:
ModbusManager.getInstance().readCoil(slaveId, start, len, object : ModbusCallback
該庫支持的所有讀取方法如下:
所有寫數(shù)據(jù)方法如下:
總結(jié)
我們?cè)谶@篇文章中介紹了在安卓中使用串口通信時(shí)大概率會(huì)接觸到的一種應(yīng)用層協(xié)議 -- Modbus,并講解了如何在安卓中使用 Modbus ,另外介紹了幾個(gè)個(gè)人認(rèn)為比較好用的第三方庫。
-
MODBUS
+關(guān)注
關(guān)注
28文章
1825瀏覽量
77358 -
串口通信
+關(guān)注
關(guān)注
34文章
1628瀏覽量
55757 -
安卓
+關(guān)注
關(guān)注
5文章
2137瀏覽量
57665
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
為何選擇智炫安卓胎壓監(jiān)測(cè)?
安卓與ESP8266串口WIFI模塊的通信實(shí)現(xiàn)相關(guān)資料推薦
安卓設(shè)備如何通過USB與多串設(shè)備通信?
QNX操作系統(tǒng)下的Modbus串口通信設(shè)計(jì)
基于QNX操作系統(tǒng)下的Modbus串口通信設(shè)計(jì)
![基于QNX操作系統(tǒng)<b class='flag-5'>下</b>的<b class='flag-5'>Modbus</b><b class='flag-5'>串口</b><b class='flag-5'>通信</b>設(shè)計(jì)](https://file.elecfans.com/web2/M00/49/39/poYBAGKhwJCAevTyAAA7u0_HYnw044.png)
用單片機(jī)串口和modbus poll 進(jìn)行通信
![用單片機(jī)<b class='flag-5'>串口</b>和<b class='flag-5'>modbus</b> poll 進(jìn)行<b class='flag-5'>通信</b>](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
安卓與串口通信入門之modbus(上)
![<b class='flag-5'>安</b><b class='flag-5'>卓</b>與<b class='flag-5'>串口</b><b class='flag-5'>通信</b><b class='flag-5'>入門</b><b class='flag-5'>之</b><b class='flag-5'>modbus</b>(上)](https://file1.elecfans.com/web2/M00/82/28/wKgZomRE_GiAfBgtAABFExTeiRc484.jpg)
評(píng)論