Uniapp实现串口调试(安卓原生插件)
安卓原生开发uniapp插件,实现安卓设备的串口调试
基于插件源码开发
此部分官方教程建议后期再独立创建,主要是几处配置需要修改,如果博主后续玩了再写文章给大家看~
不看就继续看下去
下载安卓插件开发包
官方最新源码解压获取 UniPlugin-Hello-AS 目录使用 Android studio 打开
示例中本身集成了 uniplugin_module ,直接使用(先偷懒啦~)
引入串口操作相关包
module下build.gradle文件增加第三方串口包引入
感谢开源源码dependencies {
compileOnly 'com.gitee.sscl:SerialPortLibrary:1.0.7',
//......
}
切记修改后,gradle同步一下
开发串口相关函数
module下TestModule类修改增加串口相关封装函数
package io.dcloud.uniplugin;
import android.util.Log;
import com.alibaba.fastjson.JSONObject;
import io.dcloud.feature.uniapp.annotation.UniJSMethod;
import io.dcloud.feature.uniapp.bridge.UniJSCallback;
import io.dcloud.feature.uniapp.common.UniModule;
import com.jackiepenghe.serialportlibrary.OnSerialPortDataChangedListener;
import com.jackiepenghe.serialportlibrary.SerialPortManager;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class TestModule extends UniModule {
String TAG = "TestModule";
public static int REQUEST_CODE = 1000;
public static byte[] hexStrToBinaryStr(String hexString) {
if (hexString == null|| hexString.isEmpty()) {
return null;
}
hexString = hexString.replaceAll(" ", "");
int len = hexString.length();
int index = 0;
byte[] bytes = new byte[len / 2];
while (index < len) {
String sub = hexString.substring(index, index + 2);
bytes[index/2] = (byte)Integer.parseInt(sub,16);
index += 2;
}
return bytes;
}
/**
* 获取设备信息
*
* @param callback
*/
@UniJSMethod(uiThread = true)
public void getDevices(UniJSCallback callback) {
Log.i(TAG, "getDevices--");
String[] allDevices = SerialPortManager.getAllDevices();
String[] allDevicesPath = SerialPortManager.getAllDevicesPath();
Log.i(TAG, "getDevices--" + Arrays.toString(allDevices));
Log.i(TAG, "getDevices--" + Arrays.toString(allDevicesPath));
if (callback != null) {
JSONObject data = new JSONObject();
data.put("allDevices", allDevices);
data.put("allDevicesPath", allDevicesPath);
callback.invoke(data);
// callback.invokeAndKeepAlive(data);
}
}
/**
* 打开串口
*
* @param callback
*/
@UniJSMethod(uiThread = true)
public void openDevice(JSONObject options, UniJSCallback callback) {
Log.i(TAG, "openDevice = " + options);
if (SerialPortManager.isOpened()) {
JSONObject data = new JSONObject();
data.put("error", "串口已打开");
callback.invoke(data);
}
String serialPortPath = options.getString("serialPortPath");
int baudRate = options.getInteger("baudRate");
Log.i(TAG, "serialPortPath = " + serialPortPath);
Log.i(TAG, "baudRate = " + baudRate);
boolean open = SerialPortManager.openSerialPort(serialPortPath, baudRate);
if (open) {
JSONObject data = new JSONObject();
data.put("msg", "开启成功");
//监听 获取串口消息
SerialPortManager.setOnSerialPortDataChangedListener(new OnSerialPortDataChangedListener() {
@Override
public void serialPortDataReceived(byte[] data, int size) {
Log.i(TAG, "事件监听 = " + Arrays.toString(data));
StringBuilder sb = new StringBuilder();
for (byte b : data) {
sb.append(String.format("%02X", b));
}
Map<String, Object> params = new HashMap<>();
params.put("value", sb.toString());
mWXSDKInstance.fireGlobalEventCallback("serialPortData", params);
}
});
callback.invoke(data);
} else {
JSONObject data = new JSONObject();
data.put("error", "开启失败");
callback.invoke(data);
}
}
/**
* 关闭串口
*
* @param callback
*/
@UniJSMethod(uiThread = true)
public void closeDevice(UniJSCallback callback) {
Log.i(TAG, "closeDevice = ");
SerialPortManager.closeSerialPort();
JSONObject data = new JSONObject();
data.put("msg", "串口关闭成功");
callback.invoke(data);
}
/**
* 发送消息
*
* @param callback
*/
@UniJSMethod(uiThread = true)
public void sendMsg(JSONObject options, UniJSCallback callback) {
int type = options.getInteger("type");
Log.i(TAG, "type = " + type);
boolean succeed = false;
if (type == 0) {
SerialPortManager.writeData(hexStrToBinaryStr("AE0000B8"));
} else if (type == 1) {
SerialPortManager.writeData(hexStrToBinaryStr("AE0101B8"));
} else if (type == 2) {
SerialPortManager.writeData(hexStrToBinaryStr("AE0102B8"));
}
if (succeed) {
JSONObject data = new JSONObject();
data.put("msg", "发送成功");
callback.invoke(data);
} else {
JSONObject data = new JSONObject();
data.put("error", "发送失败");
callback.invoke(data);
}
}
}
配置cloud相关参数(Hbuilder离线打包必要)
此部分官方教程不看就继续看下去
cmd 生成 keystore 文件(Java环境自带的 keytool)
keytool -genkey -alias 证书名称 -keyalg RSA -keysize 2048 -validity 36500 -keystore 证书文件名.keystore
#后续会让输入信息可以直接回车默认空,密码需要设置
在项目里 app 下 build.gradle 文件中配置(证书文件名.keystore 放在文件可以读取的路径即可)
查看生成信息(证书文件名.keystore 路径下 cmd 执行)
keytool -list -v -keystore 证书文件名.keystore
拷贝必要信息 SHA1 、 SHA256 到 Dcloud开发者中心 上进行配置
Dcloud开发者中心创建应用 > 各平台信息 > 配置 包名、SHA1 、 SHA256
注意包名和本地的配置包名一致, app 下 build.gradle 文件中 applicationId
配置cloud相关参数(Hbuilder离线打包必要)
Uniapp页面编写
图表插件地址为了展示数据去安装了图表插件,看了 30秒的广告 ,害!
不需要展示就去掉我代码里的一部分(问就是我懒得删掉)
下拉框选择器插件地址新建项目 index.vue 页面(丑?因为博主只关心技术难点)
<template>
<uni-data-select v-model="selDevice" :localdata="devices"></uni-data-select>
<uni-data-select v-model="selDevicePath" :localdata="devicesPath"></uni-data-select>
<button class="btn" type="button" @click="getDevices()">点击获取串口信息</button>
<button class="btn" type="button" @click="openDevice()">打开串口</button>
<button class="btn" type="button" @click="closeDevice()">关闭串口</button>
<input type="text" v-model="sendMessage" />
<button class="btn" type="button" @click="sendMsg(0)">停止采集</button>
<button class="btn" type="button" @click="sendMsg(1)">点动采集</button>
<button class="btn" type="button" @click="sendMsg(2)">持续采集</button>
<button class="btn" type="button" @click="showECharts()">显示图表(以当前数据)</button>
<button class="btn" type="button" @click="clearData()">清除历史数据</button>
获取消息:
<p>
{{resultMsg}}
</p>
<view class="charts-box">
<qiun-data-charts type="line" :chartData="chartData" />
</view>
</template>
<script>
var serialPortManagerModule = uni.requireNativePlugin("SerialPortManagerModule")
export default {
data() {
return {
selDevice: "",
selDevicePath: "",
devices: [],
devicesPath: [],
resultMsg: "",
sendMessage: "",
log: "",
datas: [1, 3, 6, 5, 5],
categoriesIndex:0,
categories: [1, 2, 3, 4, 5],
chartData: {},
}
},
onLoad() {
},
methods: {
getDevices() {
var _this = this
uni.showToast({
title: "开始调用" + this.devices.length
})
_this.devices = []
// 注意调用的方法名和原生中定义的方法名一致,
//参数中获的为num1和num2,所以这里也传入这两个值
serialPortManagerModule.getDevices((res) => {
let allDevices = res["allDevices"]
for (var i = 0; i < allDevices.length; i++) {
let device = {
text: allDevices[i],
value: allDevices[i],
}
_this.devices.push(device)
}
let allDevicesPath = res["allDevicesPath"]
for (var i = 0; i < allDevicesPath.length; i++) {
let devicePath = {
text: allDevicesPath[i],
value: allDevicesPath[i],
}
_this.devicesPath.push(devicePath)
}
})
uni.showToast({
title: "调用结束"
})
},
openDevice() {
var _this = this
uni.showToast({
title: this.selDevicePath + "开启"
})
serialPortManagerModule.openDevice({
serialPortPath: _this.selDevicePath,
baudRate: 115200
}, (res) => {
if (!res["error"]) {
plus.globalEvent.addEventListener('serialPortData', function(e) {
_this.resultMsg += e['value'] + " "
let hexString = e['value']
let highHex = hexString.substring(6, 8);
let lowHex = hexString.substring(8, 10);
let data = (parseInt(highHex, 16) << 4) | parseInt(lowHex, 16);
_this.datas.push(data)
const now = new Date();
const minutes = now.getMinutes();
const seconds = now.getSeconds();
_this.categoriesIndex += 1;
_this.categories.push(_this.categoriesIndex)
});
uni.showToast({
title: res["msg"]
})
} else {
uni.showToast({
title: res["error"]
})
}
})
},
closeDevice() {
var _this = this
uni.showToast({
title: this.selDevice + "关闭串口"
})
serialPortManagerModule.closeDevice((res) => {
if (!res["error"]) {
uni.showToast({
title: res["msg"]
})
} else {
uni.showToast({
title: res["error"]
})
}
})
},
sendMsg(type) {
var _this = this
uni.showToast({
title: "发送消息"
})
serialPortManagerModule.sendMsg({
type: type
}, (res) => {
if (!res["error"]) {
uni.showToast({
title: res["msg"]
})
} else {
uni.showToast({
title: res["error"]
})
}
})
},
clearData() {
var _this = this
uni.showToast({
title: "清除历史数据"
})
_this.resultMsg = ""
_this.datas = []
_this.categories = []
_this.categoriesIndex = 0
},
showECharts() {
var _this = this
let res = {
categories: _this.categories,
series: [{
name: "采集数据",
lineType: "dash",
data: _this.datas
}]
};
this.chartData = JSON.parse(JSON.stringify(res));
}
}
}
</script>
<style scoped>
.charts-box {
width: 100%;
height: 300px;
}
</style>
伪打包(自定义基座运行,模拟器上导出的APK即可到处安装)
自定义基座离线打包官方文档此处只考虑到处 apk 文件在平板上运行,所以非完整上架打包
uniapp项目 本地打包
① 本地打包
② 结束后去 resources 下复制打包目录
Android Studio 修改配置,粘贴打包目录
① 如图所示,增加 app 标签配置上打包目录名称(见 uniapp 项目中的 manifest.json 文件的 AppId)
② 粘贴打包目录到 apps 目录下
Android打包
① 打开侧边 Gradle
② 在 module 下进行 assembleRelease 打包(如果没有 assembleRelease 选项,请看下图设置里勾上它)
③ 打开 build 下进行 Generate APKs
Uniapp 运行自定义基座
① 复制 APK 到 uniapp 项目 unpackage/debug 目录下
② 打开运行 > 运行到手机或模拟器 > 运行到 Android App 基座
③ 选择自定义基座,然后开始运行即可
模拟器测试和导出
模拟器里打开 apk 简单测试下,再导出就好


到此,你拥有了一款可以调试串口的uniapp开发的工具软件,可以使用前端开发了,单纯想调试安卓设备的串口,百度搜索
安卓串口调试助手
就好了