前端javascript实现与外接设备之间的串口通信
文章目录前言实现的功能前言最近在项目中需要实现通过串口与外接电流表之间的通信,拿到电流表的实时测试电流同步显示在页面上,由于公司人员不多,这个重任自然而然落到我的头上,起初我也是不敢相信javascript有这么强大,还能通过串口操纵外接硬件设备,但我冷静之后百度了一下果然百度出来serialport.js这么个东西。开发环境:electron-vue+node+serialport.js先上效果
·
前言
最近在项目中需要实现通过串口与外接电流表之间的通信,拿到电流表的实时测试电流同步显示在页面上,由于公司人员不多,这个重任自然而然落到我的头上,起初我也是不敢相信javascript有这么强大,还能通过串口操纵外接硬件设备,但我冷静之后百度了一下果然百度出来serialport.js这么个东西。
开发环境:electron-vue+node+serialport.js
先上效果图:
实现的功能
- 页面加载默获取所有串口列表遍历出需要打开的串口,将串口打开
- 点击开始测试,每一秒钟向电流表写入一次返回电流值得指令,从而达到每秒中更新一次页面上的电流值,实现页面电流测试值与电流表显示一致
- 点击停止测试,中止电流测试,停止向电流表写入指令
- 点击打开串口,将串口打开。再次点击关闭串口
安装serialport.js
安装这个东西有点麻烦,大家自行百度安装方法,且安装之后会导致各种报错,比如我就经历了只要 npm i 其他包就会导致serialport.js包报错,这时候只要一开始就把node_modules中的serialport备份,报错时删除替换即可解决了。
实现步骤
- 在conponent创建电流测试组件
- 定义视图层,一个按钮,一个dialog弹窗
<template>
<div>
<el-button
style="width: 74%;"
class="m-t-15 f-r m-r-15"
type="primary"
@click="openElectricity"
>电流测试</el-button
>
<el-dialog
:title="'电流测试 串口号-' + portName"
center
:visible.sync="dialogVisible"
width="40%"
@close="close"
@opened="opened"
>
<div class="content">
<div
class="mytext"
:class="{
green: ElectricityData <= standard,
red: ElectricityData > standard,
}"
>
{{ ElectricityData }} mA
</div>
<el-progress
:stroke-width="20"
:percentage="percentage"
:format="format"
></el-progress>
<div class="result">
电流测试结果:
<span :class="{ red: ElectricityData > standard }">{{
resultVal
}}</span>
</div>
<div class="testTime">
测试时间
<el-input
@keyup.enter.native="setTime"
v-model.number="testTime"
style="width: 80px; margin: 0 15px"
></el-input
>秒
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="startTest">开始测试</el-button>
<el-button @click="endTest">停止测试</el-button>
<el-button @click="closePort" type="success">{{
isOpen ? "关闭串口" : "打开串口"
}}</el-button>
</span>
</el-dialog>
</div>
</template>
- 组件script标签内定义一个prot变量 methods中定义openPort打开串口方法
<script>
var port;
export default {
async mounted() {
await this.openPort();
},
methods: {
// 打开串口方法
async openPort() {
// 串口操作--------获取串口信息
var serialport = require("serialport");
// 获取串口列表
const res = await serialport.list();
let portName = "";
res.forEach((val) => {
if (val.manufacturer === "FTDI") {
portName = val.path;
this.portName = val.path;
}
});
if (portName === "") return this.$message.error("串口未找到!");
// 设置打开串口参数
port = new serialport(
portName,
{
baudRate: 9600,
dataBits: 8,
autoOpen: false,
},
false
);
// 打开串口并提示
port.open((error) => {
if (error) {
this.$message.error("串口打开失败!");
this.isOpen = false;
console.log("打开端口" + "错误:" + error);
} else {
this.$message.success("串口开启!");
console.log("打开端口成功,正在监听数据中");
this.isOpen = true;
}
});
// 设置接收值为hex类型
port.setEncoding("hex");
// 监听数据 并将返回的16进制转为浮点数 利用引入HexToSingle 方法
port.on("data", (data) => {
if (data.length === 16) {
let str = data.slice(6, 14);
let str2 = HexToSingle(str);
str2 = str2 == 0 ? 0 : str2.toFixed(4);
this.ElectricityData = str2;
console.log(this.ElectricityData);
} else {
this.hexData += data;
let reg = /.{4}/g;
let rs = this.hexData.match(reg);
if (this.hexData.length === 16) {
let str3 = this.hexData.slice(6, 14);
let str4 = HexToSingle(str3);
str4 = str4 == 0 ? 0 : str4.toFixed(4);
this.ElectricityData = str4;
console.log(this.ElectricityData);
}
}
});
// 监听数据
port.on("error", () => {
console.log("hiulhgiolgui");
});
},
}
}
}
</script>
- methods中定义一个向串口写入数据的方法,点击开始测试定时想串口写入数据,设备响应的定时返回数据,页面数据也响应的更新,这里设置每秒写入一次。同时开始测试前判断串口是否打开,未打开则提示打开。用户点击打开串口遍历所有串口找到需要打开的那个。
var port;
export default {
data() {
return {
dialogVisible: false, // 测试弹窗显隐
writeData: "88AE0011", // 写入的数据
ElectricityData: 0, // 电流测试值
hexData: "", // 16进制转浮点数的结果
portName: "", //端口名称
stertId: null, // 计时器id
percentage: 0, // 进度条的值
resultVal: "待测试", // 测试结果提示
standard: 0.05, // 电流测试标准值
isOpen: false, // 串口是否打开
testTime: 25, // 测试时间
add: 4, // 进度条步长
};
},
async mounted() {
await this.openPort();
},
methods: {
format(percentage) {
let str = "";
if (percentage === 0) {
str = `等待测试`;
} else if (percentage > 0 && percentage < 100) {
str = "正在测试...";
} else if (percentage > 99) {
str = "测试完成";
}
return str;
},
// 打开串口方法
async openPort() {
// 串口操作--------获取串口信息
var serialport = require("serialport");
// 获取串口列表
const res = await serialport.list();
let portName = "";
res.forEach((val) => {
if (val.manufacturer === "FTDI") {
portName = val.path;
this.portName = val.path;
}
});
if (portName === "") return this.$message.error("串口未找到!");
// 设置打开串口参数
port = new serialport(
portName,
{
baudRate: 9600,
dataBits: 8,
autoOpen: false,
},
false
);
// 打开串口并提示
port.open((error) => {
if (error) {
this.$message.error("串口打开失败!");
this.isOpen = false;
console.log("打开端口" + "错误:" + error);
} else {
this.$message.success("串口开启!");
console.log("打开端口成功,正在监听数据中");
this.isOpen = true;
}
});
// 设置接收值为hex类型
port.setEncoding("hex");
// 监听数据
port.on("data", (data) => {
if (data.length === 16) {
let str = data.slice(6, 14);
let str2 = HexToSingle(str);
str2 = str2 == 0 ? 0 : str2.toFixed(4);
this.ElectricityData = str2;
console.log(this.ElectricityData);
} else {
this.hexData += data;
let reg = /.{4}/g;
let rs = this.hexData.match(reg);
if (this.hexData.length === 16) {
let str3 = this.hexData.slice(6, 14);
let str4 = HexToSingle(str3);
str4 = str4 == 0 ? 0 : str4.toFixed(4);
this.ElectricityData = str4;
console.log(this.ElectricityData);
}
}
});
},
// 写入数据方法
writeport() {
this.hexData = "";
port.write(this.writeData, "hex", function (err, a) {
if (err) {
console.log(err);
return console.log("Error on write: ", err.message);
}
});
},
// 电流测试按钮点击
openElectricity() {
this.dialogVisible = true;
},
// 点击开始测试
startTest() {
clearInterval(this.stertId);
this.percentage = 0;
if (!this.isOpen) return this.$message.error("请先打开串口!");
this.$message.success("电流测试开始!");
this.stertId = setInterval(() => {
this.percentage += this.add;
this.writeport();
if (this.percentage > 100) this.endTest();
}, 1000);
this.resultVal = "待测试";
},
// 点击停止测试
endTest() {
this.$message.success("电流测试停止!");
clearInterval(this.stertId);
this.stertId = null;
this.percentage = 0;
// 进行合格判断显示
setTimeout(() => {
if (this.ElectricityData <= this.standard) {
this.resultVal = "测试合格";
} else if (this.ElectricityData > this.standard) {
this.resultVal = "测试不合格";
}
}, 1000);
},
// 弹窗关闭
close() {
this.ElectricityData = 0;
this.endTest();
},
opened() {
this.startTest();
},
// 关闭串口
closePort() {
if (this.isOpen) {
this.endTest();
port.close((err) => {
console.log(err);
if (!err) {
this.$message.success("串口关闭成功!");
this.isOpen = false;
} else {
this.$message.success("串口已经关闭!");
this.isOpen = false;
}
});
} else {
this.openPort();
}
},
// 测试时长设置
setTime() {
if (this.testTime < 25) {
this.testTime = 25;
return this.$message.error("测试时间过低!");
}
this.$message.success("测试时间设置成功!");
this.add = 100 / this.testTime;
this.startTest();
},
},
};
- 就到这里为止页面引入组件即可,这里附上电流测试串口通信组件完整代码。
<template>
<div>
<el-button
style="width: 74%;"
class="m-t-15 f-r m-r-15"
type="primary"
@click="openElectricity"
>电流测试</el-button
>
<el-dialog
:title="'电流测试 串口号-' + portName"
center
:visible.sync="dialogVisible"
width="40%"
@close="close"
@opened="opened"
>
<div class="content">
<div
class="mytext"
:class="{
green: ElectricityData <= standard,
red: ElectricityData > standard,
}"
>
{{ ElectricityData }} mA
</div>
<el-progress
:stroke-width="20"
:percentage="percentage"
:format="format"
></el-progress>
<div class="result">
电流测试结果:
<span :class="{ red: ElectricityData > standard }">{{
resultVal
}}</span>
</div>
<div class="testTime">
测试时间
<el-input
@keyup.enter.native="setTime"
v-model.number="testTime"
style="width: 80px; margin: 0 15px"
></el-input
>秒
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="startTest">开始测试</el-button>
<el-button @click="endTest">停止测试</el-button>
<el-button @click="closePort" type="success">{{
isOpen ? "关闭串口" : "打开串口"
}}</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { HexToSingle } from "../utils/hextosingle";
var port;
export default {
data() {
return {
dialogVisible: false, // 测试弹窗显隐
writeData: "88AE0011", // 写入的数据
ElectricityData: 0, // 电流测试值
hexData: "", // 16进制转浮点数的结果
portName: "", //端口名称
stertId: null, // 计时器id
percentage: 0, // 进度条的值
resultVal: "待测试", // 测试结果提示
standard: 0.05, // 电流测试标准值
isOpen: false, // 串口是否打开
testTime: 25, // 测试时间
add: 4, // 进度条步长
};
},
async mounted() {
await this.openPort();
},
methods: {
format(percentage) {
let str = "";
if (percentage === 0) {
str = `等待测试`;
} else if (percentage > 0 && percentage < 100) {
str = "正在测试...";
} else if (percentage > 99) {
str = "测试完成";
}
return str;
},
// 打开串口方法
async openPort() {
// 串口操作--------获取串口信息
var serialport = require("serialport");
// 获取串口列表
const res = await serialport.list();
let portName = "";
res.forEach((val) => {
if (val.manufacturer === "FTDI") {
portName = val.path;
this.portName = val.path;
}
});
if (portName === "") return this.$message.error("串口未找到!");
// 设置打开串口参数
port = new serialport(
portName,
{
baudRate: 9600,
dataBits: 8,
autoOpen: false,
},
false
);
// 打开串口并提示
port.open((error) => {
if (error) {
this.$message.error("串口打开失败!");
this.isOpen = false;
console.log("打开端口" + "错误:" + error);
} else {
this.$message.success("串口开启!");
console.log("打开端口成功,正在监听数据中");
this.isOpen = true;
}
});
// 设置接收值为hex类型
port.setEncoding("hex");
// 监听数据
port.on("data", (data) => {
if (data.length === 16) {
let str = data.slice(6, 14);
let str2 = HexToSingle(str);
str2 = str2 == 0 ? 0 : str2.toFixed(4);
this.ElectricityData = str2;
console.log(this.ElectricityData);
} else {
this.hexData += data;
let reg = /.{4}/g;
let rs = this.hexData.match(reg);
if (this.hexData.length === 16) {
let str3 = this.hexData.slice(6, 14);
let str4 = HexToSingle(str3);
str4 = str4 == 0 ? 0 : str4.toFixed(4);
this.ElectricityData = str4;
console.log(this.ElectricityData);
}
}
});
},
// 写入数据方法
writeport() {
this.hexData = "";
port.write(this.writeData, "hex", function (err, a) {
if (err) {
console.log(err);
return console.log("Error on write: ", err.message);
}
});
},
// 电流测试按钮点击
openElectricity() {
this.dialogVisible = true;
},
// 点击开始测试
startTest() {
clearInterval(this.stertId);
this.percentage = 0;
if (!this.isOpen) return this.$message.error("请先打开串口!");
this.$message.success("电流测试开始!");
this.stertId = setInterval(() => {
this.percentage += this.add;
this.writeport();
if (this.percentage > 100) this.endTest();
}, 1000);
this.resultVal = "待测试";
},
// 点击停止测试
endTest() {
this.$message.success("电流测试停止!");
clearInterval(this.stertId);
this.stertId = null;
this.percentage = 0;
// 进行合格判断显示
setTimeout(() => {
if (this.ElectricityData <= this.standard) {
this.resultVal = "测试合格";
} else if (this.ElectricityData > this.standard) {
this.resultVal = "测试不合格";
}
}, 1000);
},
// 弹窗关闭
close() {
this.ElectricityData = 0;
this.endTest();
},
opened() {
this.startTest();
},
// 关闭串口
closePort() {
if (this.isOpen) {
this.endTest();
port.close((err) => {
console.log(err);
if (!err) {
this.$message.success("串口关闭成功!");
this.isOpen = false;
} else {
this.$message.success("串口已经关闭!");
this.isOpen = false;
}
});
} else {
this.openPort();
}
},
// 测试时长设置
setTime() {
if (this.testTime < 25) {
this.testTime = 25;
return this.$message.error("测试时间过低!");
}
this.$message.success("测试时间设置成功!");
this.add = 100 / this.testTime;
this.startTest();
},
},
};
</script>
<style scoped>
.content {
text-align: center;
}
.mytext {
text-align: center;
height: 150px;
line-height: 150px;
font-size: 50px;
font-weight: 700;
border: 5px solid #111;
width: 300px;
margin: 0 auto;
background-color: #555;
color: #eee;
}
.el-progress {
width: 400px;
margin: 40px auto;
}
.green {
color: greenyellow;
}
.red {
color: red;
}
.result {
margin-top: 40px;
font-size: 26px;
font-weight: 700;
}
.testTime {
margin-top: 20px;
font-size: 24px;
}
</style>
- 这里是16进制转浮点数方法代码,因为设备返回的是16进制数据,所以要转为浮点数渲染到页面,utils文件夹新建hextosingle.js文件 电流测试组件中引入使用。
// 16进制转浮点数
function InsertString(t, c, n) {
var r = new Array();
for (var i = 0; i * 2 < t.length; i++) {
r.push(t.substr(i * 2, n));
}
return r.join(c);
}
//需要用到的函数
function FillString(t, c, n, b) {
if ((t == "") || (c.length != 1) || (n <= t.length)) {
return t;
}
var l = t.length;
for (var i = 0; i < n - l; i++) {
if (b == true) {
t = c + t;
}
else {
t += c;
}
}
return t;
}
/**
*
* @param {String|Number} t 16进制数据
* @returns {String} 返回16进制转化后的浮点数
*/
//16进制转浮点数
export function HexToSingle(t) {
let jiang = t
t = jiang.replace(/\s+/g, "");
if (t == "") {
return "";
}
if (t == "00000000") {
return "0";
}
if ((t.length > 8) || (isNaN(parseInt(t, 16)))) {
return "Error";
}
if (t.length < 8) {
t = FillString(t, "0", 8, true);
}
t = parseInt(t, 16).toString(2);
t = FillString(t, "0", 32, true);
var s = t.substring(0, 1);
var e = t.substring(1, 9);
var m = t.substring(9);
e = parseInt(e, 2) - 127;
m = "1" + m;
if (e >= 0) {
m = m.substr(0, e + 1) + "." + m.substring(e + 1)
}
else {
m = "0." + FillString(m, "0", m.length - e - 1, true)
}
if (m.indexOf(".") == -1) {
m = m + ".0";
}
var a = m.split(".");
var mi = parseInt(a[0], 2);
var mf = 0;
for (var i = 0; i < a[1].length; i++) {
mf += parseFloat(a[1].charAt(i)) * Math.pow(2, -(i + 1));
}
m = parseInt(mi) + parseFloat(mf);
if (s == 1) {
m = 0 - m;
}
return m;
}
ok 跑路!
更多推荐
已为社区贡献2条内容
所有评论(0)