Web Serial API读写数据实战

通过 Web Serial API读写数据中,我通过翻译一篇技术文档,学习了和串口交互的基本方法。

在本篇笔记中,想学以致用。有两个目标,第一个是将基础的读写方法封装一下,以求可以简单实用,第二个呢就是能在已封装的基础上,实际使用一下。

首先展示封装的代码,如果有需要,直接复制粘贴拿去用,代码里面有看不明的,欢迎翻看一下通过 Web Serial API读写数据,本篇内容都是基于它而来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
export default class HandleSerial {
constructor (usbProductId, usbVendorId) {
if (!usbProductId || !usbVendorId) return;
this.usbProductId = usbProductId;
this.usbVendorId = usbVendorId;
this.support = false;

// 打开后的serilPort
this.port = '';


this.checkSupport()
}
test () {
console.log(this.usbProductId, this.usbVendorId)
console.log(this.support, 'ssss')
}
checkSupport () {
if ('serial' in navigator) {
this.support = true
} else {
this.support = true
}
}
listenOnConnectDevice (cb) {
navigator.serial.addEventListener('connect', () => {
if (cb) {
cb()
}
})
}
listenOnDisconnectDevice (cb) {
navigator.serial.addEventListener('disconnect', (event) => {
const {usbProductId, usbVendorId} = event.target.getInfo();
if (Number(usbProductId) === Number(this.usbProductId) && Number(usbVendorId) === Number(this.usbVendorId)) {
this.port = ''
if (cb) {
cb()
}
}
})
}
async connectSerial (openData) {
console.log(navigator, 'navigator.serial')
const hadAccessPortList = await navigator.serial.getPorts();
this._getPortByList(hadAccessPortList)
if (!this.port) {
const filters = [{
usbProductId: this.usbProductId,
usbVendorId: this.usbVendorId
}]
this.port = await navigator.serial.requestPort({
filters
})
}
if (this.port) {
// openData = {baudRate: 4} ,传参参考https://developer.mozilla.org/en-US/docs/Web/API/SerialPort/open
await this.port.open(openData)
}
return this.port
}
_getPortByList (portList) {
for (let i = 0; i < portList.length; i++) {
const eachPort = portList[i].getInfo()
if (Number(eachPort.usbProductId) === Number(this.usbProductId) && Number(eachPort.usbVendorId) === Number(this.usbVendorId)) {
this.port = portList[i];
break;
}
}
return
}
async writeData (data) {
if (!this.port) return;

const textEncoder = new TextEncoderStream();
const writableStreamClosed = textEncoder.readable.pipeTo(this.port.writable);
const writer = textEncoder.writable.getWriter()

await writer.write(data);
writer.close();
await writableStreamClosed;
await writer.releaseLock();
}
async readData () {
if (!this.port) return;

const textDecoder = new TextDecoderStream();
const readableStreamClosed = this.port.readable.pipeTo(textDecoder.writable);
const reader = textDecoder.readable.getReader();

const {value} = await reader.read();
reader.cancel();
await readableStreamClosed.catch(() => {})
await reader.releaseLock();
return value;
}
}

在使用时,首先引入代码(假设引入代码和HandleSerial.js在同一目录下),如下:

import HandleSerial from './HandleSerial.js'

如果需要在html文件的script标签中通过src引入,可以对上面文件做如下改造

原代码

1
2
3
export default class HandleSerial {
......
}

改造后的代码
1
2
3
class HandleSerial {
......
}

然后直接做如下引用:
<script src="./HandleSerial.js"></script>

引入之后,就可以开始使用,首先实例话,不管用哪种方式引入,接下来的用法都是一样的,usbProductId, usbVendorId必选,不知道的话可以直接找厂商去要

1
const handleSerial = new HandleSerial(usbProductId, usbVendorId)

实例话后首先检查当前浏览器是否支持串口设备的连接,如果支持,再继续连接或者写自己的逻辑

1
2
3
4
5
if (handleSerial.support) {
......
} else {
alert('当前浏览器不支持串口设备,请升级后再尝试')
}

如果当前浏览器不支持串口设备,哪只能提醒用户去升级,不然我们没办法服务了。

浏览器支持串口设备的情况下,我们就可以来做接下来的工作。

首先,将设备的断开连接检测起来,以处理相应的逻辑

  • 监听串口设备断开 listenOnDisconnectDevice
1
2
3
4
function onSerialDisconnect() {
// 串口设备断开后的业务处理逻辑
}
handleSerial.listenOnDisconnectDevice(onSerialDisconnect)
  • 监听串口设备连接 listenOnConnectDevice
    如果没有监听串口设备,监听到串口设备,有可能是之前断开过,所以每次初始化重连是必要的

    1
    2
    3
    4
    5
    6
    7
    8
    function onSerialConnect() {
    if (handleSerial.port) {
    handleSerial.port = ''
    // 重连
    }
    // 串口设备连接时的业务逻辑
    }
    handleSerial.listenOnConnectDevice(onSerialConnect)

    提前做好监听功能后,就可以正式开始和串口交互了

  • 连接 connectSerial,需要传入openData参数,openData的具体参数参考SerialPort.open()或者翻看这篇博客

基本参数如下:

  • baudRate :每秒发送数据的速度
    • dataBits: 每帧发送的数据量(7或者8)
    • parity: parity模式,可选值有noneevenodd
    • bufferSize: 读写的buffer数据大小,不能超过16MB
    • flowControl:流控制模式,none或者hardware
1
2
const openData = {baudRate: 4}
handleSerial.connectSerial(openData)
  • 写入数据到串口设备 writeData

连接成功后,就可以给串口设备写入数据,具体数据,不同的设备可能要求不同,要根据设备要求来写,比如下面我们写入一个1.

1
2
const writeData = 1
handleSerial.writeData(writeData)
  • 读取串口设备的数据 readData

    如果我们的函数是在一个async/awiat函数中,我们可以这样做

    1
    2
    const data = await handleSerial.readData()
    // 处理逻辑

    如果不是,也可以这样

1
2
3
handleSerial.readData().then((data) => {
// 处理逻辑
})

是不是感觉比直接写简单了很多。希望能对看到本文的用户有所帮助。