基于 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.mzph.cn/news/76100.shtml

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

相关文章

数据库实现学生管理系统

1.QT将数据库分为三个层次&#xff1a; 1> 数据库驱动层&#xff1a;QSqlDriver、QSqlDriverCreator、QSqlDriverCreatorBase、QSqlDriverPlugin 2> sql接口层&#xff1a;QSqlDatabase、QSqlQuery、QSqlRecord、QSqlError 3> 用户接口层&#xff1a;提供一些模型QSql…

linux非root安装特定版本的cuda

由于一些代码实现&#xff08;cuda写的外部扩展包&#xff09;对cuda版本要求比较高&#xff0c;因此&#xff0c;我在实验室linux系统下默认的cuda版本上&#xff0c;没办法编译扩展包。需要重新安装特定版本的cuda。 一. 首先&#xff0c;需要查看系统版本&#xff1a; lsb…

LabVIEW利用人工神经网络辅助进行结冰检测

LabVIEW利用人工神经网络辅助进行结冰检测 结冰对各个领域构成重大威胁&#xff0c;包括但不限于航空航天和风力涡轮机行业。在起飞过程中&#xff0c;飞机机翼上轻微积冰会导致升力降低25%。研究报告称&#xff0c;涡轮叶片上的冰堆积可在19个月的运行时间内造成29MWh的功率损…

【微服务部署】四、Jenkins一键打包部署NodeJS(Vue)前端项目步骤详解

本文介绍使用Jenkins一键将NodeJS&#xff08;Vue&#xff09;前端项目打包并上传到生产环境服务器&#xff0c;这里使用的是直接打包静态页面&#xff0c;发送到远程服务器Nginx配置目录的方式&#xff0c;首先确保服务器环境配置好&#xff0c;安装Nginx&#xff0c;运行目录…

解决Ubuntu无法安装pycairo和PyGObject

环境&#xff1a;虚拟机Ubuntu20.04&#xff0c;vscode无法安装pycairo和PyGObject 虚拟机Ubuntu20.04&#xff0c;vscode中运行Anaconda搭建的vens 的Python3.8.10 首先在vscode中点击ctrlshiftp&#xff0c;选择Python3.8.10的环境&#xff0c;自动激活Python 最近在搞无人…

第 362 场 LeetCode 周赛题解

A 与车相交的点 数据范围小直接暴力枚举 class Solution { public:int numberOfPoints(vector <vector<int>> &nums) {unordered_set<int> vis;for (auto &p: nums)for (int i p[0]; i < p[1]; i)vis.insert(i);return vis.size();} };B 判断能否…

普中 51 单片机点亮LED灯

普中 51 单片机 &#xff08;STC89C52RC&#xff09; LED / IO 将LED1进行闪烁操作 为啥要进行延时操作&#xff1f;依据人的肉眼余晖效应&#xff0c; 延时时间不能太短&#xff0c;否则就无法观察到 LED 闪烁 #include "reg52.h" typedef unsigned int u16; //对…

React中父子组件参数传递讲解

文章目录 结合案例&#xff1a;github搜索案例1.父容器代码2.搜索Search子模块代码3.展示Lisi子模块代码 父子参数传递分析1.子(Search)传父(App)2.父(App)传子(List) 结合案例&#xff1a;github搜索案例 案例结果展示如下图 1.父容器代码 import React, { Component } fr…

【GO语言基础】前言

系列文章目录 【Go语言学习】ide安装与配置 【GO语言基础】前言 【GO语言基础】变量常量 【GO语言基础】数据类型 文章目录 系列文章目录一、基础知识包和函数函数声明语法简洁性 括号成对出现GO常用DOS命令命名规则项目目录结构注释 总结 一、基础知识 包和函数 //声明本代…

Python中使用item()方法遍历字典的例子

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 这篇文章主要介绍了Python中使用item()方法遍历字典的例子, for…in这种是Python中最常用的遍历字典的方法了,需要的朋友可以参考下 Python字典的遍历方法有好几种&#xff0c;其中一种是for…in&#xff0c;这个我就…

记录docker 部署nessus

1、开启容器 docker run -itd --nameramisec_nessus -p 8834:8834 ramisec/nessus 2、登录 &#xff1a;注意是https https://ip8843 3、修改admin密码 #进入容器 docker exec -it ramisec_nessus /bin/bash#列出用户名 /opt/nessus/sbin/nessuscli lsuser#修改密码&a…

sqlserver union和union all 的区别

1.首先在数据库编辑1-40数字&#xff1b; 2.查询Num<30的数据&#xff0c;查询Num>20 and Num<40的数据&#xff0c;使用union all合并&#xff1b; 发现30-20的数字重复了&#xff0c;可见union all 不去重&#xff1b; 3.查询Num<30的数据&#xff0c;查询Num…

Promise 解决 Vue 中父子组件的加载问题!

前言 关于Promie我这里就不多解释了&#xff0c;不懂得可以看看官方文档。下面文章重点介绍项目中遇到的问题解决方法。 需求 组件b初始化某个用到的库&#xff0c;只有在初始化完成后才能调用其API&#xff0c;不然会报错。a页面负责调用。 // a.vue <template><d…

动态表单设计

动态表单设计 背景方案讨论基于上面分析&#xff0c;对比调研&#xff0c;自定义动态表单数据模型表单详解&#xff08;一&#xff09; 表单模板&#xff1a;jim_dynamic_form&#xff08;二&#xff09;表单数据类型&#xff1a;jim_form_data_type&#xff08;三&#xff09;…

【Linux】编辑器 vim

1、vim的基本概念 vi/vim【一款文本编辑器】vim【一款多模式编辑器】vi/vim 的区别简单点来说&#xff0c;它们都是多模式编辑器&#xff0c;不同的是 vim 是 vi 的升级版本&#xff0c;它不仅兼容vi的所有指令&#xff0c;而且还有一些新的特性在里面。例如语法加亮&#xff0…

分享一个python实验室设备预约管理系统 实验室设备维修系统源码 lw 调试

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人七年开发经验&#xff0c;擅长Java、Python、PHP、.NET、微信小程序、爬虫、大数据等&#xff0c;大家有这一块的问题可以一起交流&#xff01; &#x1f495;&…

DAY08_MyBatisPlus——入门案例标准数据层开发CRUD-Lombok-分页功能DQL编程控制DML编程控制乐观锁快速开发-代码生成器

目录 一 MyBatisPlus简介1. 入门案例问题导入1.1 SpringBoot整合MyBatisPlus入门程序①&#xff1a;创建新模块&#xff0c;选择Spring初始化&#xff0c;并配置模块相关基础信息②&#xff1a;选择当前模块需要使用的技术集&#xff08;仅保留JDBC&#xff09;③&#xff1a;手…

《VulnHub》DarkHole:1

VulnHub 1&#xff1a;靶场信息2&#xff1a;打靶2.1&#xff1a;情报收集&威胁建模2.2&#xff1a;漏洞分析&渗透攻击 3&#xff1a;总结3.1&#xff1a;命令&工具3.1.1&#xff1a;Nmap 3.2&#xff1a;关键技术 VulnHub 打靶记录。官网&#xff1a;https://www.…

Android -BLE 蓝牙模块开发

Android-Ble蓝牙开发Demo示例–扫描,连接,发送和接收数据,分包解包(附源码) - 简书前言 万物互联的物联网时代的已经来临&#xff0c;ble蓝牙开发在其中扮演着举重若轻的角色。最近刚好闲一点&#xff0c;抽时间梳理下这块的知识点。 涉及ble蓝牙通讯的客户端(开启、扫描、连接…

Web framework-Gin(二)

目录 一、Gin 1、Ajax 2、文件上传 2.1、form表单中文件上传(单个文件) 2.2、form表单中文件上传(多个文件) 2.3、ajax上传单个文件 2.4、ajax上传多个文件 3、模板语法 4、数据绑定 5、路由组 6、中间件 一、Gin 1、Ajax AJAX 即“Asynchronous Javascript And XM…