web串口通讯 navigator.serial【电子秤】

650 阅读6分钟

虚拟串口工具

开发过程中如果没有硬件设备,可以使用虚拟串口软件模拟串口,实现数据的收发通信。
如下使用 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 软件安装目录下进行替换即可

image.png

串口调试工具

‌串口通信通常是一对一的通信方式,在串口通信中,发送方和接收方通常是一对一的配置,即一个发送设备对应一个接收设备。这种通信方式适用于点对点的数据传输,确保了数据传输的准确性和可靠性‌。
以下使用 NK版串口调试软件 进行串口调试。

软件安装

① 下载 NK版串口调试软件
链接:www.jrxwxm.com/soft/15896.…
② 在图灵时代下载站下载NK版任意波特率串口调试软件工具助手官方版这款软件的压缩包,并对其进行解压

image.png

③ 得到图示exe文件,双击

image.png

④ NK版任意波特率串口调试软件工具助手官方版软件为绿色版,直接进入主界面,就可以使用

image.png

NK版任意波特率串口调试软件工具助手使用方法

如何更改数据保存路径?
① 运行软件
② 点击“更改路径”

image.png

③ 在电脑中选择自己想要保存的路径,并点击确定

image.png

④ 更改成功

image.png

使用步骤

1、模拟串口

① 安装好 VSPD 后,首先选择你要虚拟的端口号,点击“添加端口”。不要与现有端口冲突。添加的时候是成对添加的,因为一个是接收,一个是发送。

image.png

② 虚拟好端口后,左侧能看到新虚拟出的 COM1 和 COM2,此时两个端口都没有被占用,处于停用状态。

image.png

2、配置串口

打开串口调试助手两个窗口,设置 COM1 和 COM2 的波特率、奇偶校验位、数据位、停止位等参数,然后点击开始,即可在左侧看到已配置好的串口参数。

image.png

image.png

image.png

image.png


image.png

案例

<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