原因
说到要写这串口内容的博客原因,当然是有需求开发才会去接触这一方面的内容。
需求
系统上需要使用到条形码扫描枪,根据已获得的条形码,通过扫描枪获取条形码的内容,再进而触发下一个功能逻辑。
方案
遇到问题,肯定是先百度(毕竟这一方面的功能开发我是头一次),所以兜兜转转…最终确定使用Web Serial API
Web Serial API为网站提供了一种使用JavaScript对串行设备进行读写的方法。串行设备可以通过用户系统上的串行端口连接,也可以通过模拟串行端口的可移动USB和蓝牙设备连接。
这里是我找到的参考文档:
链接1
链接2
确定好要使用哪一个方案后,就要写例子来验证一下这个API是否可行。
步骤说明
要验证API,我这里先写了一个HTML文件做简单实现。
test2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>串口调试</title>
</head>
<body>
<button onclick="serial()">启动串口调试</button>
<h2>hamon</h2>
</body>
</html>
<script type="text/javascript">
async function serial(){
// 浏览器支持serial
if ('serial' in navigator) {
console.log('当前浏览器支持serial')
const port = await navigator.serial.requestPort()
await port.open({ baudRate: 9600})
console.log("port的内容是:", port);
const reader = port.readable.getReader();
try{
while(true){
const {value,done} = await reader.read();
if (done) {
reader.releaseLock();
break;
}
console.log("测试value的内容是", value);
}
}catch (error) {
console.error(error)
}finally {
reader.releaseLock();
console.log('允许稍后关闭串口。');
await readableStreamClosed.catch(() => { /* Ignore the error */ });
await port.close();
}
}
}
</script>
测试代码写完了,那要创造什么环境进行验证呢?
因为我们当时还没有拿到设备,所以说先是本地虚拟出串口进行测试。
创建一对虚拟串口:
串口是一个双向通信接口,允许字节发送和接收数据。上述的COM1和COM2就相当于传输线的两端。
有了串口,就要构建一发一收,web端当然是用来接收数据的,还有一端用来发数据。
这里我们使用串口调试助手,这个工具是在微软store里面直接下载的(免费的)。
打开串口调试助手,连接虚拟串口的另一端
回到web项目这里,运行test2.html文件
根据弹出窗口显示,浏览器可以获取到我们虚拟出来的两个串口,然后我们浏览器连接COM1,串口调试助手连接COM2,并发送一串数据:
点击发送后,回到浏览器端,F12打开调试窗口:
可以看到接收到COM2传过来的值,但是不是我们想要的效果,这是因为:
SerialPort对象的readable和writable属性返回一个ReadableStream和一个WritableStream。这些将用于从串行设备接收数据并将数据发送到串行设备。两者都使用Uint8Array
实例进行数据传输。
所以我们还需要将传过来的Uint8Array数组进行转换。
在test2.html中加上一个方法:
function Uint8ArrayToString(serialData){
var out, i, len, c;
var char2, char3;
out = "";
len = serialData.length;
i = 0;
while(i < len) {
c = serialData[i++];
switch(c >> 4)
{
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
// 0xxxxxxx
out += String.fromCharCode(c);
break;
case 12: case 13:
// 110x xxxx 10xx xxxx
char2 = serialData[i++];
out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
break;
case 14:
// 1110 xxxx 10xx xxxx 10xx xxxx
char2 = serialData[i++];
char3 = serialData[i++];
out += String.fromCharCode(((c & 0x0F) << 12) |
((char2 & 0x3F) << 6) |
((char3 & 0x3F) << 0));
break;
}
}
return out;
}
这时候我们再发送一次数据,看看效果:
可以看到传过来的数据已经拿到了,而且通过转码得到我们想要的效果,所以这个方案是可行的。
后续补充
你以为到这里就结束了?没错,最开始我也是这样认为的。
但是后面换一个串口调试工具的时候,发送的数据却出现了中文乱码!!
就是在用Commix的时候,也是同样的字符串:
但是得到的效果是不一样的
中文部分出现了乱码。
后来查明了是commix这个软件支持HEX和ASC编码方式,但是之前那个是用UTF-8的编码方式,而且拿到物理设备后,中文也是出现了乱码,而且和commix这个工具出现的乱码一模一样。
出现这种问题,当然要找出原因和解决的方法,然后就在网上找了很多种编码转换的方法,Java的和js的,都试过了,还是不生效;甚至是转十六进制也不行。
不过最后还是找到了一个解决的方法:
在tes2.html中的while循环体内加入
let utf8decoder = new TextDecoder('GBK');
let u8arr = new Uint8Array(value);
var code = utf8decoder.decode(u8arr);
console.log('value的值是:', code);
然后再用Commix这个工具进行验证
发现乱码的问题解决了。
注意
后来发现,虽然刚开始知道是编码不对,以为设备和Commix工具一样,都是用ASC去编码,但是并不是这样,我们拿到设备的编码方式是GBK,所以很多种转码方式都不合适。
代码分享
上述的例子只是用html来做简单验证而已,实际的功能代码是用vue来写的。后面我会把我写了html例子、vue格式和用到的调试串口工具安装包整合在一起。可以点击链接获取。
2022-04-24
资源包已经整理出来了,可点击上面的链接进行下载。