使用 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

在这里插入图片描述

使用说明

使用下面方法可以侦测电脑上串口设备插入与拔出:

// 全局串口设备插入事件
navigator.serial.onconnect = (event) => {console.log("Serial connected: ", event.target);
};// 全局串口设备拔出事件
navigator.serial.ondisconnect = (event) => {console.log("Serial disconnected: ", event.target);
};// 也可以对单个的串口设备设置插入与拔出事件

使用下面方法可以显示电脑上的串口设备选择授权,或者显示已授权的串口设备列表:

// requestPort方法将显示一个包含已连接设备列表的对话框,用户选择可以并授予其中一个设备访问权限
// 对于USB虚拟串口而言该方法还可以传入一个过滤器,指定PID&VID的串口
const port = await navigator.serial.requestPort();
// port.forget(); // 取消授权
// port.getInfo() // 获取PID&VID (对于蓝牙串口好像是显示服务号)// getDevices方法可以返回已连接的授权过的设备列表
const ports = await navigator.serial.getPorts();

使用 open 方法打开选中的串口设备后就可以进行数据交互了:

// open时可以传入串口参数
await port.open({baudRate: 115200,// bufferSize: 255,   // 读写缓存,默认255// dataBits: 8,       // 数据位,默认8// flowControl: none, // 流控制,默认无// parity: none,      // 校验,默认无// stopBits: 1,       // 停止位,默认1
});

打开后就可以发送数据了:

const encoder = new TextEncoder();
// const data= new Uint8Array(length);
const writer = port.writable.getWriter();
await writer.write(encoder.encode("PING"));
// await writer.write(data);
writer.releaseLock();

同样可以设置数据接收:

while (port.readable) {const reader = port.readable.getReader();try {while (true) {const { value, done } = await reader.read();if (done) {// |reader| has been canceled.break;}// Do something with |value|…}} catch (error) {// Handle |error|…} finally {reader.releaseLock();}
}

数据接收本身很简单,但需要注意的是在关闭串口前需要释放 reader 对象。

下面是关闭串口操作:

// 使用 await port.close(); 即可关闭串口,如果正在读写数据,需要先释放相关资源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) {// |reader| has been canceled.break;}// Do something with |value|...}} catch (error) {// Handle |error|...} finally {reader.releaseLock();}}await port.close();
}const closed = readUntilClosed();// Sometime later...
keepReading = false;
reader.cancel();
await closed;

除了上面内容外还可以使用 setSignals 和 getSignals 来设置和获取流控制情况。

代码与演示

<!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) {// alert("Your browser support Web Serial API."); // 浏览器不支持 Web Serial API} 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);// await port.forget(); // 取消授权// console.log(port.getInfo()); // 打印PID&VID (对于蓝牙串口好像是显示服务号)} catch (e) {console.log(e); // The prompt has been dismissed without selecting a device.}};function updateInputData(data) {let array = new Uint8Array(data); // event.data.buffer就是接收到的inputreport包数据了let hexstr = "";for (const data of array) {hexstr += (Array(2).join(0) + data.toString(16).toUpperCase()).slice(-2) + " "; // 将字节数据转换成(XX )形式字符串}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) {// |reader| has been canceled.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,// bufferSize: 255,   // 读写缓存,默认255// dataBits: 8,       // 数据位,默认8// flowControl: none, // 流控制,默认无// parity: none,      // 校验,默认无// stopBits: 1,       // 停止位,默认1});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 访问串口非常方便,目前来说唯一的问题是这还是实验性质的功能,可能之后接口还会变动,需要根据实际情况进行调整。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/29741.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

消息队列-分布式消息队列技术选型

Kafka Kafka 是 LinkedIn 开源的一个分布式流式处理平台&#xff0c;已经成为 Apache 顶级项目&#xff0c;早期被用来用于处理海量的日志&#xff0c;后面才慢慢发展成了一款功能全面的高性能消息队列。 流式处理平台具有三个关键功能&#xff1a; 消息队列&#xff1a;发布和…

智慧校园综合解决方案:提供全方位的学校管理支持

在当今数字化时代&#xff0c;学校管理面临着越来越多的挑战&#xff0c;包括学生管理、教职员工管理、校园安全等。为了应对这些挑战&#xff0c;智慧校园综合解决方案应运而生。智慧校园综合解决方案融入了先进的信息技术&#xff0c;为学校带来了一场管理与教育模式的革新。…

SAP ABAP开发:如何读取物料主数据中的长文本?

在SAP ERP系统中&#xff0c;物料的基本描述可存储40个字符&#xff0c;见下图&#xff1a; 但长文本信息如何从系统中读取呢&#xff1f; 在SAP ABAP开发中&#xff0c;读取物料主数据&#xff08;Material Master Data&#xff09;中的基本视图&#xff08;Basic View&#…

DLS平台:惠誉全球经济展望——今年调增至2.6%,明年调减!

摘要 尽管全球货币政策逐渐转向宽松&#xff0c;惠誉国际评级&#xff08;Fitch Ratings&#xff09;在最新的《全球经济展望》中对2024年全球经济增长进行了上调。然而&#xff0c;由于美国经济增速放缓和其他因素的影响&#xff0c;2025年的全球经济增长预期则被下调。这篇文…

保姆级小白就业人工智能(视频+源码+笔记)

&#x1f345;我是小宋&#xff0c; Java学习AI&#xff0c;记录学习之旅。关注我&#xff0c;带你轻松过面试。提升简历亮点&#xff08;14个demo&#xff09; &#x1f345;我的java面试合集已有12W 浏览量。&#x1f30f;号&#xff1a;tutou123com。拉你进专属群。 ⭐⭐你的…

LVGL欢乐桌球游戏(LVGL+2D物理引擎学习案例)

LVGL欢乐桌球游戏&#xff08;LVGL2D物理引擎学习案例&#xff09; 视频效果&#xff1a; https://www.bilibili.com/video/BV1if421X7DL

最新Prompt预设词分享,DALL-E3文生图+文档分析

使用指南 直接复制使用 可以前往已经添加好Prompt预设的AI系统测试使用&#xff08;可自定义添加使用&#xff09; 支持GPTs SparkAi SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。支持GPT-4o…

微服务开发与实战Day11 - 微服务面试篇

一、分布式事务 1. CAP定理 1998年&#xff0c;加州大学的计算机科学及Eric Brewer提出&#xff0c;分布式系统有三个指标&#xff1a; Consistency&#xff08;一致性&#xff09;Availability&#xff08;可用性&#xff09;Partition tolerance&#xff08;分区容错性&am…

使用Minikube部署Kubernetes环境

使用Minikube部署Kubernetes环境 1. Minikube简介 Minikube是一个轻量级的Kubernetes实现&#xff0c;它在本地运行一个Kubernetes集群&#xff0c;可以是单节点或者集群环境&#xff0c;主要用于开发和测试。Minikube支持Kubernetes的所有主要功能&#xff0c;包括Dashboard…

经典文献阅读之--OrienterNet(自动驾驶中基于网格的交通场景感知)

dia 0. 简介 使用神经网络来匹配2D公开地图的做法是一个很有趣的方法&#xff0c;人们可以使用简单的2D地图在3D环境中指明自己所处的位置&#xff0c;而大部分视觉定位算法则依赖于昂贵的、难以构建和维护的3D点云地图。为了弥合这一差距《OrienterNet: Visual Localization…

cesium按照参数绘制不同形状的船舶

俺们公司之前有个自创的所谓前端GIS框架&#xff0c;是用Cesium搞的。我对该框架不熟悉&#xff0c;用它在地图上作画&#xff0c;画船舶符号&#xff0c;看以前的代码&#xff0c;感觉十分艰深晦涩&#xff0c;什么材质、纹理&#xff0c;令人头大如斗。我4年前用过一阵Cesium…

ClickHouse 高性能的列式数据库管理系统

ClickHouse是一个高性能的列式数据库管理系统&#xff08;DBMS&#xff09;&#xff0c;主要用于在线分析处理查询&#xff08;OLAP&#xff09;。以下是对ClickHouse的详细介绍&#xff1a; 基本信息&#xff1a; 来源&#xff1a;由俄罗斯的Yandex公司于2016年开源。全称&…

成都晨持绪科技:2024年抖音网店做起来难吗

随着抖音平台的日益火爆&#xff0c;越来越多的商家和个人开始关注并尝试开设自己的抖音网店。然而&#xff0c;面对激烈的市场竞争和不断变化的平台规则&#xff0c;许多人都在问&#xff1a;2024年抖音网店做起来难吗? 要回答这个问题&#xff0c;我们首先需要了解抖音网店的…

【机器学习】第3章 K-近邻算法

一、概念 1.K-近邻算法&#xff1a;也叫KNN 分类 算法&#xff0c;其中的N是 邻近邻居NearestNeighbor的首字母。 &#xff08;1&#xff09;其中K是特征值&#xff0c;就是选择离某个预测的值&#xff08;例如预测的是苹果&#xff0c;就找个苹果&#xff09;最近的几个值&am…

Javaweb07-JavaBean技术和Jsp开发模式

JavaBean技术和Jsp开发模式 一.JavaBean技术 1.JavaBean的基本概念 **JavaBean组件&#xff1a;**与html分离且使用Java代码封装类 **JavaBean分类&#xff1a;**可视化JavaBean&#xff1a;swing 非可视化JavaBean&#xff1a;用于封装实体和业务逻辑 JavaBean特点&#x…

把Vue文件转至树莓派上遇到的问题和解决方案

把整个文件夹复制进树莓派后&#xff0c;运行 npm run dev ,报错sh: 1: vite: Permission denied 解决方案&#xff1a;删除项目里的 node_modules 重新 npm install 再运行即可 rm -rf node_modules/ npm install 在安装过程中&#xff0c;遇到下图问题&#xff0c;vulnerabi…

【热门开源项目推荐】满足不同程序员的需求与关注点

目录 前言一、热门开源项目介绍二、使用开源热门项目的优势&#xff08;一&#xff09;经济方面&#xff08;二&#xff09;技术方面&#xff08;三&#xff09;社区支持及协作方面 三、程序员选择项目模型建议&#xff08;一&#xff09;关键步骤&#xff08;二&#xff09;示…

掌握rpc、grpc并探究内在本质

文章目录 rpc是什么&#xff1f;又如何实现服务通信&#xff1f;理解rpcRPC的通信过程通信协议的选择小结RPC VS Restful net_rpc实践案例net/rpc包介绍创建服务端创建client 看看net_rpc的通信调度实现的内部原理明确目标基于自己实现的角度分析我会怎么做代码分析 grpc介绍与…

c++实现二叉搜索树(下)

好久不见啊&#xff0c;baby们&#xff0c;小吉我又回归了&#xff0c;发完这一篇小吉将会有两周时间不会更新blog了&#xff08;sorry&#xff09;&#xff0c;在小吉没有发blog的日子里大家也要好好学习数据结构与算法哦&#xff0c;还有就是别忘了小吉我❤️  这篇博客是二…

充电学习—7、BC1.2 PD协议

BC1.2&#xff08;battery charging&#xff09;充电端口识别机制&#xff1a; SDP、CDP、DCP 1、VBUS detect&#xff1a;vbus检测 PD&#xff08;portable device&#xff0c;便携式设备&#xff09;中有个检测VBUS是否有效的电路&#xff0c;电路有个参考值&#xff0c;高…