-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathindex.html
198 lines (169 loc) · 8.04 KB
/
index.html
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
191
192
193
194
195
196
197
198
<!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("Your browser is not support 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表示不使用reportId
iptLog.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>