虚拟串口工具
开发过程中如果没有硬件设备,可以使用虚拟串口软件模拟串口,实现数据的收发通信。
如下使用 VSPD 虚拟串口软件模拟。
软件安装
Configure Virtual Serial Port Driver(VSPD)
① 首先下载 Configure Virtual Serial Port Driver(VSPD) 软件
链接:pan.baidu.com/s/11aGc2aHG…
提取码:rmd7
② 安装时注意将路径选在 D 盘里,其他步骤都选择 next 即可。
③ 破解:将 Cracked 目录下的 vspdconfig.exe 和 vspdctl.dll 拷贝到 VSPD 软件安装目录下进行替换即可
串口调试工具
串口通信通常是一对一的通信方式,在串口通信中,发送方和接收方通常是一对一的配置,即一个发送设备对应一个接收设备。这种通信方式适用于点对点的数据传输,确保了数据传输的准确性和可靠性。
以下使用 NK版串口调试软件 进行串口调试。
软件安装
① 下载 NK版串口调试软件
链接:www.jrxwxm.com/soft/15896.…
② 在图灵时代下载站下载NK版任意波特率串口调试软件工具助手官方版这款软件的压缩包,并对其进行解压
③ 得到图示exe文件,双击
④ NK版任意波特率串口调试软件工具助手官方版软件为绿色版,直接进入主界面,就可以使用
NK版任意波特率串口调试软件工具助手使用方法
如何更改数据保存路径?
① 运行软件
② 点击“更改路径”
③ 在电脑中选择自己想要保存的路径,并点击确定
④ 更改成功
使用步骤
1、模拟串口
① 安装好 VSPD 后,首先选择你要虚拟的端口号,点击“添加端口”。不要与现有端口冲突。添加的时候是成对添加的,因为一个是接收,一个是发送。
② 虚拟好端口后,左侧能看到新虚拟出的 COM1 和 COM2,此时两个端口都没有被占用,处于停用状态。
2、配置串口
打开串口调试助手两个窗口,设置 COM1 和 COM2 的波特率、奇偶校验位、数据位、停止位等参数,然后点击开始,即可在左侧看到已配置好的串口参数。
案例
<template>
<div class="serial-port">测试串口</div>
<el-button type="primary" @click="connectToSerialPort">连接串口</el-button>
<el-input v-model="inputData" maxlength="50" placeholder="输入发送数据内容" show-word-limit type="textarea" />
<el-button type="success" @click="sendData">发送数据</el-button>
<el-button type="default" @click="readData">读取数据</el-button>
<el-button type="danger" @click="cutPort">断开串口</el-button>
<!-- <el-input v-model="receiveStr" maxlength="50" placeholder="接收数据内容" show-word-limit type="textarea" /> -->
<p>重量为:{{ weight }}</p>
<div v-html="receiveStr" style="max-height: 300px; overflow-y: scroll"></div>
<!-- <el-input v-model="resolveReceiveStr" maxlength="50" placeholder="解析接收数据内容" show-word-limit type="textarea" /> -->
<div v-html="resolveReceiveStr" style="max-height: 300px; overflow-y: scroll"></div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
import { msgError, msgSuccess } from '@/utils/element-plus';
const port = ref('');
const ports = ref([]);
const reader = ref('');
let receiveStr = ref('原始数据:');
let resolveReceiveStr = ref('解析后数据:');
const connectToSerialPort = async () => {
try {
if (!('serial' in navigator)) {
return msgError({ msg: '你的浏览器不支持串口连接!' });
}
if (port.value) {
return ElMessage({
message: '串口已处于连接状态!',
type: 'warning',
});
}
// 提示用户选择一个串口
port.value = await navigator.serial.requestPort();
// 获取用户之前授予该网站访问权限的所有串口。
ports.value = await navigator.serial.getPorts();
console.log(port.value, ports.value);
console.log(port.value);
// 等待串口打开
await port.value.open({
path: 'COM1',
baudRate: 9600,
dataBits: 8, // 数据位;
stopBits: 1, // 停止位;
parity: 'none', // 校验;
flowControl: 'none', // 流控制;
});
// console.log(typeof port.value);
msgSuccess({ msg: '成功连接串口!' });
// readData(port.value);
readData();
} catch (error) {
// 处理连接串口出错的情况
console.log('Error connecting to serial port:', error);
}
};
const readData = async () => {
console.log('读取数据');
reader.value = port.value.readable.getReader();
console.log(reader);
// 监听来自串口的数据
while (true) {
const { value, done } = await reader.value.read();
console.log('读取数据value', done, value);
if (done) {
// 允许稍后关闭串口
reader.value.releaseLock();
break;
}
receiveStr.value = `${receiveStr.value}<br>${value}`;
// 获取发送的数据
const serialData = new TextDecoder('utf-8').decode(value);
// const serialData = new TextDecoder().decode(value);
resolveReceiveStr.value = `${resolveReceiveStr.value}<br>${serialData}`;
// resolveReceiveStr.value = serialData;
console.log(serialData);
// value 是一个 Uint8Array
console.log(value);
resolveData(value);
}
};
const inputData = ref('');
const sendData = async () => {
// if (port.value && port.value.isOpen) {
if (port.value) {
if (inputData.value) {
const writer = port.value.writable.getWriter();
console.log('发送数据');
await writer.write(new TextEncoder().encode(inputData.value));
await writer.close();
} else {
return ElMessage({
message: '输入需要发送的数据内容',
type: 'warning',
showClose: true,
grouping: true,
duration: 2000,
});
}
} else {
ElMessage({
message: '串口未连接或未打开!',
type: 'warning',
showClose: true,
grouping: true,
duration: 2000,
});
// console.error("串口未连接或未打开!");
}
};
/**
* 断开接口;
*/
const cutPort = async () => {
if (port.value !== '') {
await reader.value.cancel();
await port.value.close();
port.value = '';
console.log('断开串口连接');
ElMessage({
message: '已成功断开串口连接',
type: 'success',
});
} else {
ElMessage({
message: '请先连接或打开串口',
type: 'warning',
showClose: true,
grouping: true,
duration: 2000,
});
// console.error("串口未连接或未打开!");
}
};
let initUint8Arr = new Uint8Array([]); // 初始化Uint8Array数据;
let newUint8Arr = ref(); // 新的Uint8Array数据;
let newHexStr = ref(); // 新的16进制数据;
let weight = ref(); // 重量值;
newUint8Arr.value = initUint8Arr;
/**
* 解析接收的数据;
*/
const resolveData = (Uint8ArrData: Uint8Array) => {
const mergedUint8Arr = joinUint8Array(newUint8Arr.value, Uint8ArrData).mergedArray;
newUint8Arr.value = mergedUint8Arr;
const hexStr = uint8ArrayToHexStr(newUint8Arr.value);
console.log('hexStr:', hexStr);
const hexStrArr = hexStr.split('0a0d').filter((item) => item !== '');
console.log('hexStrArr:', hexStrArr);
const count = countCharacter(hexStr, '0a0d');
let newHexStrs = (hexStrArr.length && hexStrArr[hexStrArr.length - 1]) || '';
if (count == hexStrArr.length) {
newHexStr.value = newHexStrs + '0a0d';
} else {
newHexStr.value = newHexStrs;
}
console.log(123, newHexStr.value);
newUint8Arr.value = hexStrToUint8Array(newHexStr.value);
const str = new TextDecoder().decode(newUint8Arr.value);
console.log('str', str);
weight.value = str.trim().split(' ')[0];
console.log('重量:', weight.value);
orderInforFields[2].value = weight.value;
// console.log(1234, orderInforFields);
};
/**
* 解析Uint8Array转16进制;
*/
const uint8ArrayToHexStr = (uint8Array: any[]) => {
let hexStr = '';
uint8Array.forEach(function (byte) {
hexStr += byte.toString(16).padStart(2, '0');
});
return hexStr;
};
/**
* 16进制转Uint8Array;
*/
const hexStrToUint8Array = (hexString: string) => {
return new Uint8Array(
hexString.match(/[\da-f]{2}/gi).map(function (h) {
return parseInt(h, 16);
}),
);
};
/**
* Uint8Array拼接;
*/
const joinUint8Array = (unit8Arr1: any, unit8Arr2: any) => {
let mergedArray = new Uint8Array(unit8Arr1.length + unit8Arr2.length);
mergedArray.set(unit8Arr1, 0);
mergedArray.set(unit8Arr2, unit8Arr1.length);
return { unit8Arr1, unit8Arr2, mergedArray };
};
/**
* 计算字符串中包含指定字符个数;
*/
const countCharacter = (str: string, charToCount: string) => {
return str.split(charToCount).length - 1;
};
</script>
该案例使用的协议
数据构成:
① 通信协议:
9600 波特率, 8位数字位, 1 位停止位, 没校验位
② 数据构成:XX2EXX XX2EXX XXXX2EXX 0A0D………
(0A0D为数据流的协议尾,2E为小数点)
净重:5 位, 单价: 5位, 总价: 7位;
显示器上不亮的数码管用20代替
③ 数据举例:(重量0.70、单价0.00、总价0.00)
计价秤收到的17位数据格式(16进制):
连续模式:20 20 30 2E 37 30 20 20 30 2E 30 30 20 20 20 20 30 2E 30 30 0A 0D
净重:20 20 30 2E 37 30(0.70)
单价:20 20 30 2E 30 30(0.00)
金额:20 20 20 20 30 2E 30 30(0.00)
协议尾0A 0D
拓展
web 串口通讯navigator.serial
Vue中使用Web Serial API连接串口,实现通信交互
Vue使用Serial连接串口
VSPD虚拟串口软件安装及使用
NK版任意波特率串口调试软件工具助手 1.3B