使用 Web Serial API 在浏览器中实现串口通讯(纯前端) 目的 串口是非常常用的一种电脑与设备交互的接口。目前在浏览器上直接使用电脑上的串口设备了,这篇文章将介绍相关内容。
相关资料 Web Serial API 相关内容参考如下:
https://developer.mozilla.org/en-US/docs/Web/API/Serial https://developer.mozilla.org/en-US/docs/Web/API/SerialPort https://wicg.github.io/serial/
这个API目前还处于实验性质,只有电脑上的Chrome、Edge、Opera等浏览器支持:
另外还需要注意的是从网页操作设备是比较容易产生安全风险的,所以这个API只支持本地调用或者是HTTPS方式调用。
对于这个API谷歌有提供示例工程:
在线使用:https://googlechromelabs.github.io/serial-terminal/ 项目地址:https://github.com/GoogleChromeLabs/serial-terminal
下面这个项目做的挺不错的,直接拿来用也很好:
在线使用:https://itldg.github.io/web-serial-debug/ 项目地址:https://gitee.com/itldg/web-serial-debug or https://github.com/itldg/web-serial-debug
使用说明 使用下面方法可以侦测电脑上串口设备插入与拔出:
1 2 3 4 5 6 7 8 9 10 11 12 navigator.serial.onconnect = (event ) => { console .log("Serial connected: " , event.target); }; navigator.serial.ondisconnect = (event ) => { console .log("Serial disconnected: " , event.target); };
使用下面方法可以显示电脑上的串口设备选择授权,或者显示已授权的串口设备列表:
1 2 3 4 5 6 7 8 9 const port = await navigator.serial.requestPort();const ports = await navigator.serial.getPorts();
使用 open 方法打开选中的串口设备后就可以进行数据交互了:
1 2 3 4 5 6 7 8 9 10 await port.open({ baudRate: 115200 , });
打开后就可以发送数据了:
1 2 3 4 5 6 7 const encoder = new TextEncoder();const writer = port.writable.getWriter();await writer.write(encoder.encode("PING" ));writer.releaseLock();
同样可以设置数据接收:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 while (port.readable) { const reader = port.readable.getReader(); try { while (true ) { const { value, done } = await reader.read(); if (done) { break ; } } } catch (error) { } finally { reader.releaseLock(); } }
数据接收本身很简单,但需要注意的是在关闭串口前需要释放 reader 对象。
下面是关闭串口操作:
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 let keepReading = true ;let reader;async function readUntilClosed ( ) { while (port.readable && keepReading) { reader = port.readable.getReader(); try { while (true ) { const { value, done } = await reader.read(); if (done) { break ; } } } catch (error) { } finally { reader.releaseLock(); } } await port.close(); } const closed = readUntilClosed();keepReading = false ; reader.cancel(); await closed;
除了上面内容外还可以使用 setSignals 和 getSignals 来设置和获取流控制情况。
代码与演示 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 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Web Serial API Test</title > <style > * { margin : 0 ; padding : 0 ; } button ,textarea { margin : 1rem ; margin-bottom : 0 ; padding : 0.5rem ; width : 20rem ; } textarea { resize : none; overflow-y : scroll; overflow-x : hidden; height : 5rem ; } </style > <script > if ("serial" in navigator) { } else { alert("Your browser is not support Web Serial API." ); } navigator.serial.onconnect = (event ) => { console .log("Serial port connected: " , event.target); }; navigator.serial.ondisconnect = (event ) => { console .log("Serial port disconnected: " , event.target); }; </script > </head > <body > <button id ="btnSelect" > select</button > <br > <button id ="btnOpen" > open</button > <br > <button id ="btnClose" > close</button > <br > <button id ="btnSend" > send</button > <br > <textarea id ="iptOutput" > D0 D1 D2 D3 D4 D5 D6 D7</textarea > <br > <textarea id ="iptInput" readonly > </textarea > <script > const btnSelect = document .querySelector("#btnSelect" ); const btnOpen = document .querySelector("#btnOpen" ); const btnClose = document .querySelector("#btnClose" ); const btnSend = document .querySelector("#btnSend" ); const iptOutput = document .querySelector("#iptOutput" ); const iptInput = document .querySelector("#iptInput" ); let port = null ; let reader = null ; let reading = false ; btnSelect.onclick = async () => { try { port = await navigator.serial.requestPort(); let ports = await navigator.serial.getPorts(); console .log(ports); } catch (e) { console .log(e); } }; function updateInputData (data ) { let array = new Uint8Array (data); let hexstr = "" ; for (const data of array) { hexstr += (Array (2 ).join(0 ) + data.toString(16 ).toUpperCase()).slice(-2 ) + " " ; } iptInput.value += hexstr; iptInput.scrollTop = iptInput.scrollHeight; } async function listenReceived ( ) { if (reading) { console .log("On reading." ); return ; } reading = true ; while (port.readable && reading) { reader = port.readable.getReader(); try { while (true ) { const { value, done } = await reader.read(); if (done) { break ; } updateInputData(value); } } catch (e) { console .log(e); } finally { reader.releaseLock(); } } await port.close(); port = null ; console .log("Port closed." ); } btnOpen.onclick = async () => { if (port === null ) { console .log("Not selected." ); return ; } await port.open({ baudRate: 115200, }); listenReceived(); console .log("Port opened." ); } btnClose.onclick = async () => { if ((port === null ) || (!port.writable)) { console .log("Not opened." ); return ; } if (reading) { reading = false ; reader?.cancel(); } } function getOutputData ( ) { let outputDatastr = iptOutput.value.replace(/\s+/g , "" ); if (outputDatastr.length % 2 == 0 && /^[0-9a-fA-F]+$/ .test(outputDatastr)) { const byteLength = outputDatastr.length / 2 ; const outputData = new Uint8Array (byteLength); for (let i = 0 ; i < byteLength; i++) { outputData[i] = parseInt (outputDatastr.substr(i * 2 , 2 ), 16 ); } return outputData; } else { throw "Data is not even or 0-9、a-f、A-F" ; } } btnSend.onclick = async () => { if ((port === null ) || (!port.writable)) { console .log("Not opened." ); return ; } const writer = port.writable.getWriter(); await writer.write(getOutputData()); writer.releaseLock(); } </script > </body > </html >
下面测试时我将串口的TX/RT短接在一起,发送什么数据就会收到什么数据:
总结 使用 Web Serial API 访问串口非常方便,目前来说唯一的问题是这还是实验性质的功能,可能之后接口还会变动,需要根据实际情况进行调整。
相关链接(侵删)
使用 Web Serial API 在浏览器中实现串口通讯(纯前端)
=================我是分割线=================
欢迎到公众号来唠嗑: