基于 Web HID API 的HID透传测试工具(纯前端)

前言

最近在搞HID透传 《STM32 USB使用记录:HID类设备(后篇)》 。

市面上的各种测试工具都或多或少存在问题,所以就自己写一个工具进行测试。目前来说纯前端方案编写这个工具应该是最方便的,这里放上相关代码。

项目地址与代码示例

项目地址:https://github.com/NaisuXu/HID_Passthrough_Tool

在这里插入图片描述

下面代码保存到 index.html 文件,双击打开文件即可使用:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>HID Passthrough Tool</title><style>* {margin: 0;padding: 0;}html,body {height: 100vh;background-color: #f7f7ff;}div {height: calc(100% - 4rem);padding: 2rem;display: grid;grid-template-columns: 1fr 1fr 1fr;grid-template-rows: 2rem 1fr;row-gap: 1rem;column-gap: 2rem;}textarea {resize: none;overflow-y: scroll;overflow-x: hidden;padding: 1rem;}</style><script>if ("hid" in navigator) {// 浏览器支持hid} else {alert("Browser is not supported Web HID API.");}</script></head><body><div><button id="btnOpen">open</button><button id="btnSend">send</button><button id="btnClear">clear</button><textarea id="iptLog" readonly></textarea><textarea id="iptOutput">D0 D1 D2 D3 D4 D5 D6 D7</textarea><textarea id="iptInput" readonly></textarea></div><script>const btnOpen = document.querySelector("#btnOpen");const btnSend = document.querySelector("#btnSend");const btnClear = document.querySelector("#btnClear");const iptLog = document.querySelector("#iptLog");const iptOutput = document.querySelector("#iptOutput");const iptInput = document.querySelector("#iptInput");iptLog.value += "HID Passthrough Tool\n\n";iptLog.value += "This is an HID Passthrough device read/write Tool.\n\n";iptLog.value += "Device must have one collection with one input and one output.\n\n";iptLog.value += "For more detail see below:\n\n";iptLog.value += "https://github.com/NaisuXu/HID_Passthrough_Tool\n\n";iptLog.value += "《STM32 USB使用记录:HID类设备(后篇)》\nhttps://blog.csdn.net/Naisu_kun/article/details/131880999\n\n";iptLog.value += "《使用 Web HID API 在浏览器中进行HID设备交互(纯前端)》\nhttps://blog.csdn.net/Naisu_kun/article/details/132539918\n\n";let device; // 需要连接或已连接的设备let inputDataLength; // 发送数据包长度let outputDataLength; // 发送数据包长度// 打开设备相关操作btnOpen.onclick = async () => {try {// requestDevice方法将显示一个包含已连接设备列表的对话框,用户选择可以并授予其中一个设备访问权限const devices = await navigator.hid.requestDevice({ filters: [] });// const devices = await navigator.hid.requestDevice({//     filters: [{//         vendorId: 0xabcd,  // 根据VID进行过滤//         productId: 0x1234, // 根据PID进行过滤//         usagePage: 0x0c,   // 根据usagePage进行过滤//         usage: 0x01,       // 根据usage进行过滤//     },],// });// let devices = await navigator.hid.getDevices(); // getDevices方法可以返回已连接的授权过的设备列表if (devices.length == 0) {iptLog.value += "No device selected\n\n";iptLog.scrollTop = iptLog.scrollHeight; // 滚动到底部return;}device = devices[0]; // 选择列表中第一个设备if (!device.opened) {// 检查设备是否打开await device.open(); // 打开设备// 下面几行代码和我的自定义的透传的HID设备有关// 我的设备中有一个collection,其中有一个input、一个output// inputReports和outputReports数据是Array,reportSize是8// reportCount表示一包数据的字节数,USB-FS 和 USB-HS 设置的reportCount最大值不同if (device.collections[0].inputReports[0].items[0].isArray && device.collections[0].inputReports[0].items[0].reportSize === 8) {// 发送数据包长度必须和报告描述符中描述的一致inputDataLength = device.collections[0].inputReports[0].items[0].reportCount ?? 0;}if (device.collections[0].outputReports[0].items[0].isArray && device.collections[0].outputReports[0].items[0].reportSize === 8) {// 发送数据包长度必须和报告描述符中描述的一致outputDataLength = device.collections[0].outputReports[0].items[0].reportCount ?? 0;}iptLog.value += `Open device: \n${device.productName}\nPID-${device.productId} VID-${device.vendorId}\ninputDataLength-${inputDataLength} outputDataLength-${outputDataLength}\n\n`;iptLog.scrollTop = iptLog.scrollHeight; // 滚动到底部}// await device.close(); // 关闭设备// await device.forget() // 遗忘设备// 电脑接收到来自设备的消息回调device.oninputreport = (event) => {console.log(event); // event中包含device、reportId、data等内容let array = new Uint8Array(event.data.buffer); // 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; // 滚动到底部iptLog.value += `Received ${event.data.byteLength} bytes\n\n`;iptLog.scrollTop = iptLog.scrollHeight; // 滚动到底部};} catch (error) {iptLog.value += `${error}\n\n`;iptLog.scrollTop = iptLog.scrollHeight; // 滚动到底部}};// 发送数据相关操作btnSend.onclick = async () => {try {if (!device?.opened) {throw "Device not opened";}const outputData = new Uint8Array(outputDataLength); // 要发送的数据包let outputDatastr = iptOutput.value.replace(/\s+/g, ""); // 去除所有空白字符if (outputDatastr.length % 2 == 0 && /^[0-9a-fA-F]+$/.test(outputDatastr)) {// 检查长度和字符是否正确// 一包长度不能大于报告描述符中规定的长度const byteLength = outputDatastr.length / 2 > outputDataLength ? outputDataLength : outputDatastr.length / 2;// 将字符串转成字节数组数据for (let i = 0; i < byteLength; i++) {outputData[i] = parseInt(outputDatastr.substr(i * 2, 2), 16);}} else {throw "Data is not even or 0-9、a-f、A-F";}await device.sendReport(0, outputData); // 发送数据,第一个参数为reportId,填0表示不使用reportIdiptLog.value += `Send ${outputData.length} bytes\n\n`;iptLog.scrollTop = iptLog.scrollHeight; // 滚动到底部} catch (error) {iptLog.value += `${error}\n\n`;iptLog.scrollTop = iptLog.scrollHeight; // 滚动到底部}};// 全局HID设备插入事件navigator.hid.onconnect = (event) => {console.log("HID connected: ", event.device); // device 的 collections 可以看到设备报告描述符相关信息iptLog.value += `HID connected:\n${event.device.productName}\nPID ${event.device.productId} VID ${event.device.vendorId}\n\n`;iptLog.scrollTop = iptLog.scrollHeight; // 滚动到底部};// 全局HID设备拔出事件navigator.hid.ondisconnect = (event) => {device = null; // 释放当前设备iptLog.value += `HID disconnected:\n${event.device.productName}\nPID ${event.device.productId} VID ${event.device.vendorId}\n\n`;iptLog.scrollTop = iptLog.scrollHeight; // 滚动到底部};// 清空数据接收窗口btnClear.onclick = () => {iptInput.value = "";};</script></body></html>

注意事项

Web HID API 目前还处于实验性质,只有电脑上的Chrome、Edge、Opera等浏览器支持:
在这里插入图片描述

另外还需要注意的是从网页操作设备是比较容易产生安全风险的,所以这个API只支持本地调用或者是HTTPS方式调用。

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

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

相关文章

动静态库生成使用

&#x1f525;&#x1f525; 欢迎来到小林的博客&#xff01;&#xff01;       &#x1f6f0;️博客主页&#xff1a;✈️林 子       &#x1f6f0;️博客专栏&#xff1a;✈️ Linux       &#x1f6f0;️社区 :✈️ 进步学堂       &#x1f6f0…

星际争霸之小霸王之小蜜蜂(十二)--猫有九条命

系列文章目录 星际争霸之小霸王之小蜜蜂&#xff08;十一&#xff09;--杀杀杀 星际争霸之小霸王之小蜜蜂&#xff08;十&#xff09;--鼠道 星际争霸之小霸王之小蜜蜂&#xff08;九&#xff09;--狂鼠之灾 星际争霸之小霸王之小蜜蜂&#xff08;八&#xff09;--蓝皮鼠和大…

Java:Springboot和React中枚举值(数据字典)的使用

目录 1、开发中的需求2、实现效果3、后端代码4、前端代码5、接口数据6、完整代码7、参考文章 1、开发中的需求 开发和使用过程中&#xff0c;通常会涉及四个角色&#xff1a;数据库管理员、后端开发人员、前端开发人员、浏览者 数据库使用int类型的数值进行存储&#xff08;e…

蓝桥杯打卡Day6

文章目录 N的阶乘基本算术整数查询 一、N的阶乘OI链接 本题思路&#xff1a;本题是关于高精度的模板题。 #pragma GCC optimize(3) #include <bits/stdc.h>constexpr int N1010;std::vector<int> a; std::vector<int> f[N];std::vector<int> mul(in…

JavaWeb_LeadNews_Day11-KafkaStream实现实时计算文章分数

JavaWeb_LeadNews_Day11-KafkaStream实现实时计算文章分数 KafkaStream概述案例-统计单词个数SpringBoot集成 实时计算文章分值来源Gitee KafkaStream 概述 Kafka Stream: 提供了对存储与Kafka内的数据进行流式处理和分析的功能特点: Kafka Stream提供了一个非常简单而轻量的…

AggregateFunction结合自定义触发器实现点击率计算

背景&#xff1a; 接上一篇文章&#xff0c;ProcessWindowFunction 结合自定义触发器会有状态过大的问题&#xff0c;本文就使用AggregateFunction结合自定义触发器来实现&#xff0c;这样就不会导致状态过大的问题了 AggregateFunction结合自定义触发器实现 flink对于每个窗…

如何实现MongoDB数据的快速迁移?

作为一种Schema Free文档数据库&#xff0c;MongoDB因其灵活的数据模型&#xff0c;支撑业务快速迭代研发&#xff0c;广受开发者欢迎并被广泛使用。在企业使用MongoDB承载应用的过程中&#xff0c;会因为业务上云/跨云/下云/跨机房迁移/跨地域迁移、或数据库版本升级、数据库整…

【DockerCE】Docker-CE 24.0.6正式版发布

官网下载地址&#xff08;For RHEL/CentOS 7.9&#xff09;&#xff1a; https://download.docker.com/linux/centos/7/x86_64/stable/Packages/ 相对于24.0.5版本&#xff0c;本次24.0.6版本更新的rpm包有 5 个&#xff0c;使用目录对比软件对比的结果如下&#xff1a; 在Lin…

(Note)中文EI检索期刊目录

ei和sci、ssci一样是国际知名的期刊数据库&#xff0c;ei不仅收录国际知名的刊物&#xff0c;也收录了一些国内期刊&#xff0c;为方便投稿选刊&#xff0c;Elsevier官网更新了的EI Compendex期刊目录&#xff0c;那么 国内ei期刊有哪些? 经查询共有250余种期刊&#xff0c;新…

什么是Docker和Docker-Compose?

Docker的构成 Docker仓库&#xff1a;https://hub.docker.com Docker自身组件 Docker Client&#xff1a;Docker的客户端 Docker Server&#xff1a;Docker daemon的主要组成部分&#xff0c;接受用户通过Docker Client发出的请求&#xff0c;并按照相应的路由规则实现路由分发…

Qt包含文件不存在问题解决 QNetworkAccessManager

这里用到了Qt的网络模块&#xff0c;在.pro中添加了 QT network 但是添加 #include <QNetworkAccessManager> 会报错说找不到&#xff0c;可以通过在项目上右键执行qmake后&#xff0c;直接#include <QNetworkAccessManager>就不会报错了&#xff1a;

20230911 Shell指令数组以及函数值传递,值返回

实现一个对数组求和的函数&#xff0c;数组通过实参传递给函数 #!/bin/bashfunction fun() {sum0for ((i0;i<$var;i))do(( sumarr[i] ))doneecho $sum } read -p "输入该数组个数: " var for((j0;j<$var;j)) doread -p "输入数组第$j个值: " arr[j] …